Newer
Older
#include "MantidAlgorithms/AnnularRingAbsorption.h"
#include "MantidAPI/InstrumentValidator.h"
#include "MantidAPI/WorkspaceUnitValidator.h"
Federico Montesino Pouzols
committed
#include "MantidGeometry/Instrument.h"
#include "MantidGeometry/Instrument/ObjComponent.h"
#include "MantidGeometry/Instrument/ReferenceFrame.h"
#include "MantidGeometry/Instrument/SampleEnvironment.h"
#include "MantidGeometry/Objects/CSGObject.h"
#include "MantidGeometry/Objects/ShapeFactory.h"
#include "MantidKernel/Atom.h"
#include "MantidKernel/BoundedValidator.h"
#include "MantidKernel/CompositeValidator.h"
#include "MantidKernel/Material.h"
#include "MantidKernel/MandatoryValidator.h"
#include "MantidKernel/NeutronAtom.h"
#include "MantidKernel/V3D.h"
#include <boost/format.hpp>
#include <boost/shared_ptr.hpp>
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
namespace Mantid {
namespace Algorithms {
using namespace Mantid::API;
using namespace Mantid::Kernel;
// Register the algorithm into the AlgorithmFactory
DECLARE_ALGORITHM(AnnularRingAbsorption)
//----------------------------------------------------------------------------------------------
/// Algorithm's name for identification. @see Algorithm::version
const std::string AnnularRingAbsorption::name() const {
return "AnnularRingAbsorption";
}
/// Algorithm's version for identification. @see Algorithm::version
int AnnularRingAbsorption::version() const { return 1; }
/// Algorithm's category for identification. @see Algorithm::category
const std::string AnnularRingAbsorption::category() const {
return "CorrectionFunctions\\AbsorptionCorrections";
}
/// Algorithm's summary for use in the GUI and help. @see Algorithm::summary
const std::string AnnularRingAbsorption::summary() const {
return "Calculates bin-by-bin correction factors for attenuation due to "
"absorption "
"in a cylindrical sample in the wall of a hollow can";
}
//----------------------------------------------------------------------------------------------
/**
* Initialize the algorithm's properties.
*/
void AnnularRingAbsorption::init() {
// The input workspace must have an instrument and units of wavelength
auto wsValidator = boost::make_shared<CompositeValidator>();
wsValidator->add<WorkspaceUnitValidator>("Wavelength");
wsValidator->add<InstrumentValidator>();
declareProperty(make_unique<WorkspaceProperty<>>(
"InputWorkspace", "", Direction::Input, wsValidator),
"The input workspace in units of wavelength.");
declareProperty(make_unique<WorkspaceProperty<>>("OutputWorkspace", "",
Direction::Output),
"The name to use for the output workspace.");
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
// -- can properties --
auto mustBePositive = boost::make_shared<BoundedValidator<double>>();
mustBePositive->setLower(0.0);
declareProperty("CanOuterRadius", -1.0, mustBePositive,
"The outer radius of the can in centimetres");
declareProperty("CanInnerRadius", -1.0, mustBePositive,
"The inner radius of the can in centimetres");
// -- sample properties --
declareProperty("SampleHeight", -1.0, mustBePositive,
"The height of the sample in centimetres");
declareProperty("SampleThickness", -1.0, mustBePositive,
"The thickness of the sample in centimetres");
auto nonEmptyString = boost::make_shared<MandatoryValidator<std::string>>();
declareProperty("SampleChemicalFormula", "",
"Chemical composition of the sample material",
nonEmptyString);
declareProperty("SampleNumberDensity", -1.0, mustBePositive,
"The number density of the sample in number of formulas per "
"cubic angstrom");
// -- Monte Carlo properties --
auto positiveInt = boost::make_shared<Kernel::BoundedValidator<int>>();
positiveInt->setLower(1);
declareProperty("NumberOfWavelengthPoints", EMPTY_INT(), positiveInt,
"The number of wavelength points for which a simulation is "
"atttempted (default: all points)");
declareProperty(
"EventsPerPoint", 300, positiveInt,
"The number of \"neutron\" events to generate per simulated point");
declareProperty("SeedValue", 123456789, positiveInt,
"Seed the random number generator with this value");
}
//----------------------------------------------------------------------------------------------
/**
* Execute the algorithm.
*/
void AnnularRingAbsorption::exec() {
MatrixWorkspace_sptr inputWS = getProperty("InputWorkspace");
// We neglect any absorption in the can so the actual shape defined is a
// hollow cylinder
// where the sample is in the centre of the can wall
attachSample(inputWS);
MatrixWorkspace_sptr factors = runMonteCarloAbsorptionCorrection(inputWS);
setProperty("OutputWorkspace", factors);
}
//---------------------------------------------------------------------------------------------
// Private members
//---------------------------------------------------------------------------------------------
/**
* @param workspace The workspace where the environment should be attached
*/
void AnnularRingAbsorption::attachSample(MatrixWorkspace_sptr &workspace) {
runCreateSampleShape(workspace);
runSetSampleMaterial(workspace);
}
/**
* @return Creates a new shape object for the sample
*/
void AnnularRingAbsorption::runCreateSampleShape(
API::MatrixWorkspace_sptr &workspace) {
auto inst = workspace->getInstrument();
auto refFrame = inst->getReferenceFrame();
bool childLog = g_log.is(Logger::Priority::PRIO_DEBUG);
auto alg = this->createChildAlgorithm("CreateSampleShape", -1, -1, childLog);
alg->setProperty("InputWorkspace", workspace);
alg->setPropertyValue("ShapeXML",
createSampleShapeXML(refFrame->vecPointingUp()));
try {
alg->executeAsChildAlg();
} catch (std::exception &exc) {
throw std::invalid_argument(
std::string("Unable to create sample shape: '") + exc.what() + "'");
}
}
/**
* Create the XML that defines a hollow cylinder with dimensions provided by the
* user
* The shape is a hollow cylinder where the inner/outer radius is defined by
* \f[
* r_{\pm} = r_l + \frac{r_u-r_l}{2} \pm \frac{t}{2}
* \f]
* where \f$r_{l,u}\f$ are the inner & outer can radii respectively and \f$t\f$
* is the sample
* thickness
* @param upAxis A vector pointing up
* @returns A string containing the shape XML
*/
std::string
AnnularRingAbsorption::createSampleShapeXML(const V3D &upAxis) const {
// User input
const double canLowRadiusCM = getProperty("CanInnerRadius");
const double canUppRadiusCM = getProperty("CanOuterRadius");
const double sampleHeightCM = getProperty("SampleHeight");
const double sampleThickCM = getProperty("SampleThickness");
// Sample dimensions for approximation (converted to metres)
const double wallMidPtCM =
canLowRadiusCM + 0.5 * (canUppRadiusCM - canLowRadiusCM);
const double lowRadiusMtr = (wallMidPtCM - 0.5 * sampleThickCM) / 100.;
const double uppRadiusMtr = (wallMidPtCM + 0.5 * sampleThickCM) / 100.;
// Cylinders oriented along Y, with origin at the centre as expected by
// the MonteCarloAbsorption algorithm.
const V3D bottomCentre{0.0, -sampleHeightCM / 2.0 / 100.0, 0.0}; // in metres.
const std::string innerCylID = std::string("inner-cyl");
const std::string innerCyl = cylinderXML(
innerCylID, bottomCentre, lowRadiusMtr, upAxis, sampleHeightCM / 100.0);
const std::string outerCylID = std::string("outer-cyl");
const std::string outerCyl = cylinderXML(
outerCylID, bottomCentre, uppRadiusMtr, upAxis, sampleHeightCM / 100.0);
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
// Combine shapes
boost::format algebra("<algebra val=\"(%1% (# %2%))\" />");
algebra % outerCylID % innerCylID;
auto xml = outerCyl + "\n" + innerCyl + "\n" + algebra.str();
g_log.debug() << "Sample shape XML:\n" << xml << "\n";
return xml;
}
/**
* @param id String id of object
* @param bottomCentre Point of centre of bottom base
* @param radius Radius of cylinder
* @param axis Cylinder will point along this axis
* @param height The height of the cylinder
* @return A string defining the XML
*/
const std::string
AnnularRingAbsorption::cylinderXML(const std::string &id,
const V3D &bottomCentre, const double radius,
const V3D &axis, const double height) const {
// The newline characters are not necessary for the XML but they make it
// easier to read for debugging
static const char *CYL_TEMPLATE =
"<cylinder id=\"%1%\">\n"
"<centre-of-bottom-base x=\"%2%\" y=\"%3%\" z=\"%4%\" />\n"
" <axis x=\"%5%\" y=\"%6%\" z=\"%7%\" />\n"
" <radius val=\"%8%\" />\n"
" <height val=\"%9%\" />\n"
"</cylinder>";
boost::format xml(CYL_TEMPLATE);
xml % id % bottomCentre.X() % bottomCentre.Y() % bottomCentre.Z() % axis.X() %
axis.Y() % axis.Z() % radius % height;
return xml.str();
}
/**
* @return Attaches a new Material object to the sample
*/
void AnnularRingAbsorption::runSetSampleMaterial(
API::MatrixWorkspace_sptr &workspace) {
bool childLog = g_log.is(Logger::Priority::PRIO_DEBUG);
auto alg = this->createChildAlgorithm("SetSampleMaterial", -1, -1, childLog);
alg->setProperty("InputWorkspace", workspace);
alg->setProperty("ChemicalFormula",
getPropertyValue("SampleChemicalFormula"));
alg->setProperty<double>("SampleNumberDensity",
getProperty("SampleNumberDensity"));
try {
alg->executeAsChildAlg();
} catch (std::exception &exc) {
throw std::invalid_argument(
std::string("Unable to set sample material: '") + exc.what() + "'");
}
}
/**
* Run the MonteCarloAbsorption algorithm on the given workspace and return the
* calculated factors
* @param workspace The input workspace that has the sample and can defined
* @return A 2D workspace of attenuation factors
*/
MatrixWorkspace_sptr AnnularRingAbsorption::runMonteCarloAbsorptionCorrection(
const MatrixWorkspace_sptr &workspace) {
bool childLog = g_log.is(Logger::Priority::PRIO_DEBUG);
auto alg =
this->createChildAlgorithm("MonteCarloAbsorption", 0.1, 1.0, childLog);
alg->setProperty("InputWorkspace", workspace);
alg->setProperty<int>("NumberOfWavelengthPoints",
getProperty("NumberOfWavelengthPoints"));
alg->setProperty<int>("EventsPerPoint", getProperty("EventsPerPoint"));
alg->setProperty<int>("SeedValue", getProperty("SeedValue"));
try {
alg->executeAsChildAlg();
} catch (std::exception &exc) {
throw std::invalid_argument(
std::string("Error running absorption correction: '") + exc.what() +
"'");
}
return alg->getProperty("OutputWorkspace");
}
} // namespace Algorithms