Mantid
Loading...
Searching...
No Matches
CalculateCarpenterSampleCorrection.cpp
Go to the documentation of this file.
1// Mantid Repository : https://github.com/mantidproject/mantid
2//
3// Copyright © 2018 ISIS Rutherford Appleton Laboratory UKRI,
4// NScD Oak Ridge National Laboratory, European Spallation Source,
5// Institut Laue - Langevin & CSNS, Institute of High Energy Physics, CAS
6// SPDX - License - Identifier: GPL - 3.0 +
10#include "MantidAPI/Sample.h"
20
21#include <stdexcept>
22
23namespace Mantid::Algorithms {
24DECLARE_ALGORITHM(CalculateCarpenterSampleCorrection) // Register the class
25 // into the algorithm
26 // factory
27using namespace DataObjects;
28using namespace Kernel;
29using namespace API;
32using Mantid::HistogramData::HistogramY;
33using Mantid::HistogramData::Points;
34using std::vector;
35using namespace Mantid::PhysicalConstants;
36using namespace Geometry;
37
38// Constants required internally only, so make them static. These are
39// Chebyshev expansion coefficients copied directly from Carpenter 1969 Table 1
40namespace { // anonymous
41static const double CHEBYSHEV[] = {
42 // l= 0 1 2 3 4 5 // (m,n)
43 0.730284, -0.249987, 0.019448, -0.000006, 0.000249, -0.000004, // (1,1)
44 0.848859, -0.452690, 0.056557, -0.000009, 0.000000, -0.000006, // (1,2)
45 1.133129, -0.749962, 0.118245, -0.000018, -0.001345, -0.000012, // (1,3)
46 1.641112, -1.241639, 0.226247, -0.000045, -0.004821, -0.000030, // (1,4)
47 0.848859, -0.452690, 0.056557, -0.000009, 0.000000, -0.000006, // (2,1)
48 1.000006, -0.821100, 0.166645, -0.012096, 0.000008, -0.000126, // (2,2)
49 1.358113, -1.358076, 0.348199, -0.038817, 0.000022, -0.000021, // (2,3)
50 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, // (2,4)
51 1.133129, -0.749962, 0.118245, -0.000018, -0.001345, -0.000012, // (3,1)
52 1.358113, -1.358076, 0.348199, -0.038817, 0.000022, -0.000021, // (3,2)
53 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, // (3,3)
54 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, // (3,4)
55 1.641112, -1.241639, 0.226247, -0.000045, -0.004821, -0.000030, // (4,1)
56 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, // (4,2)
57 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, // (4,3)
58 0.0, 0.0, 0.0, 0.0, 0.0, 0.0 // (4,4)
59};
60
61static const int Z_size = 36; // Caution, this must be updated if the
62 // algorithm is changed to use a different
63 // size Z array.
64static const double Z_initial[] = {
65 1.0, 0.8488263632, 1.0, 1.358122181, 2.0, 3.104279270, 0.8488263632, 0.0, 0.0, 0.0, 0.0, 0.0,
66 1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.358122181, 0.0, 0.0, 0.0, 0.0, 0.0,
67 2.0, 0.0, 0.0, 0.0, 0.0, 0.0, 3.104279270, 0.0, 0.0, 0.0, 0.0, 0.0};
68
69static const double LAMBDA_REF = 1.81;
70// Badly named constants, no explanation of the origin of these
71// values. They appear to be used when calculating the multiple
72// scattering correction factor.
73static const double COEFF4 = 1.1967;
74static const double COEFF5 = -0.8667;
75} // namespace
76
77const std::string CalculateCarpenterSampleCorrection::name() const { return "CalculateCarpenterSampleCorrection"; }
78
80
82 return "CorrectionFunctions\\AbsorptionCorrections";
83}
84
89 // The input workspace must have an instrument and units of wavelength
90 auto wsValidator = std::make_shared<CompositeValidator>();
91 wsValidator->add<WorkspaceUnitValidator>("Wavelength");
92 wsValidator->add<InstrumentValidator>();
93
94 declareProperty(
95 std::make_unique<WorkspaceProperty<MatrixWorkspace>>("InputWorkspace", "", Direction::Input, wsValidator),
96 "The name of the input workspace.");
97 declareProperty(
98 std::make_unique<WorkspaceProperty<API::WorkspaceGroup>>("OutputWorkspaceBaseName", "", Direction::Output),
99 "Basename of the output workspace group for corrections."
100 "Absorption suffix = '_abs'. "
101 "Multiple Scattering suffix = '_ms'. ");
102 declareProperty("AttenuationXSection", 2.8,
103 "Coefficient 1, absorption cross "
104 "section / 1.81 if not set with "
105 "SetSampleMaterial");
106 declareProperty("ScatteringXSection", 5.1,
107 "Coefficient 3, total scattering "
108 "cross section if not set with "
109 "SetSampleMaterial");
110 declareProperty("SampleNumberDensity", 0.0721, "Coefficient 2, density if not set with SetSampleMaterial");
111 declareProperty("CylinderSampleRadius", 0.3175, "Sample radius, in cm");
112 declareProperty("Absorption", true, "If True then calculates the absorption correction.", Direction::Input);
113 declareProperty("MultipleScattering", true, "If True then calculates the multiple scattering correction.",
115}
116
121 // common information
122 MatrixWorkspace_sptr inputWksp = getProperty("InputWorkspace");
123 double radius = getProperty("CylinderSampleRadius");
124 double coeff1 = getProperty("AttenuationXSection");
125 double coeff2 = getProperty("SampleNumberDensity");
126 double coeff3 = getProperty("ScatteringXSection");
127 const bool absOn = getProperty("Absorption");
128 const bool msOn = getProperty("MultipleScattering");
129 const Material &sampleMaterial = inputWksp->sample().getMaterial();
130 if (sampleMaterial.totalScatterXSection() != 0.0) {
131 g_log.information() << "Using material \"" << sampleMaterial.name() << "\" from workspace\n";
132 if (Kernel::equals(coeff1, 2.8))
133 coeff1 = sampleMaterial.absorbXSection(LAMBDA_REF) / LAMBDA_REF;
134 if (Kernel::equals(coeff2, 0.0721) && !isEmpty(sampleMaterial.numberDensity()))
135 coeff2 = sampleMaterial.numberDensity();
136 if (Kernel::equals(coeff3, 5.1))
137 coeff3 = sampleMaterial.totalScatterXSection();
138 } else // Save input in Sample with wrong atomic number and name
139 {
140 NeutronAtom neutron(0, 0, 0.0, 0.0, coeff3, 0.0, coeff3, coeff1);
141 auto shape = std::shared_ptr<IObject>(
142 inputWksp->sample().getShape().cloneWithMaterial(Material("SetInMultipleScattering", neutron, coeff2)));
143 inputWksp->mutableSample().setShape(shape);
144 }
145 g_log.debug() << "radius=" << radius << " coeff1=" << coeff1 << " coeff2=" << coeff2 << " coeff3=" << coeff3 << "\n";
146
147 // geometry stuff
148 const auto NUM_HIST = static_cast<int64_t>(inputWksp->getNumberHistograms());
149 Instrument_const_sptr instrument = inputWksp->getInstrument();
150 if (instrument == nullptr)
151 throw std::runtime_error("Failed to find instrument attached to InputWorkspace");
152 IComponent_const_sptr source = instrument->getSource();
153 IComponent_const_sptr sample = instrument->getSample();
154 if (source == nullptr)
155 throw std::runtime_error("Failed to find source in the instrument for InputWorkspace");
156 if (sample == nullptr)
157 throw std::runtime_error("Failed to find sample in the instrument for InputWorkspace");
158
159 // Initialize progress reporting.
160 Progress prog(this, 0.0, 1.0, NUM_HIST);
161
162 // Create the new correction workspaces
163 MatrixWorkspace_sptr absWksp = createOutputWorkspace(inputWksp, "Attenuation factor");
164 MatrixWorkspace_sptr msWksp = createOutputWorkspace(inputWksp, "Multiple scattering factor");
165
166 // now do the correction
167 const auto &spectrumInfo = inputWksp->spectrumInfo();
168 PARALLEL_FOR_IF(Kernel::threadSafe(*absWksp, *msWksp))
169 for (int64_t index = 0; index < NUM_HIST; ++index) {
171 if (!spectrumInfo.hasDetectors(index))
172 throw std::runtime_error("Failed to find detector");
173 if (spectrumInfo.isMasked(index))
174 continue;
175 const double tth_rad = spectrumInfo.twoTheta(index);
176
177 // absorption
178 if (absOn) {
179 absWksp->setSharedX(index, inputWksp->sharedX(index));
180 const auto lambdas = inputWksp->points(index);
181 auto &y = absWksp->mutableY(index);
182 calculate_abs_correction(tth_rad, radius, coeff1, coeff2, coeff3, lambdas, y);
183 }
184
185 // multiple scattering
186 if (msOn) {
187 msWksp->setSharedX(index, inputWksp->sharedX(index));
188 const auto lambdas = inputWksp->points(index);
189 auto &y = msWksp->mutableY(index);
190 calculate_ms_correction(tth_rad, radius, coeff1, coeff2, coeff3, lambdas, y);
191 }
192
193 prog.report();
195 }
197
198 absWksp->setDistribution(true);
199 absWksp->setYUnit("");
200 absWksp->setYUnitLabel("Attenuation factor");
201
202 msWksp->setDistribution(true);
203 msWksp->setYUnit("");
204 msWksp->setYUnitLabel("Multiple scattering factor");
205
206 // Group and output workspaces we calculated
207 const std::string group_prefix = getPropertyValue("OutputWorkspaceBaseName");
208 auto outputGroup = std::make_shared<API::WorkspaceGroup>();
209 if (absOn) {
210 absWksp = setUncertainties(absWksp);
211 std::string ws_name = group_prefix + std::string("_abs");
212 AnalysisDataService::Instance().addOrReplace(ws_name, absWksp);
213 outputGroup->addWorkspace(absWksp);
214 } else {
215 deleteWorkspace(absWksp);
216 }
217
218 if (msOn) {
219 msWksp = setUncertainties(msWksp);
220 std::string ws_name = group_prefix + std::string("_ms");
221 AnalysisDataService::Instance().addOrReplace(ws_name, msWksp);
222 outputGroup->addWorkspace(msWksp);
223 } else {
224 deleteWorkspace(msWksp);
225 }
226
227 setProperty("OutputWorkspaceBaseName", outputGroup);
228}
229
230namespace { // anonymous namespace
231// Set up the Z table for the specified two theta angle (in degrees).
232vector<double> createZ(const double angle_rad) {
233 vector<double> Z(Z_initial, Z_initial + Z_size);
234
235 const double theta_rad = angle_rad * .5;
236 int l, J;
237 double sum;
238
239 for (int i = 1; i <= 4; i++) {
240 for (int j = 1; j <= 4; j++) {
241 int iplusj = i + j;
242 if (iplusj <= 5) {
243 l = 0;
244 J = 1 + l + 6 * (i - 1) + 6 * 4 * (j - 1);
245 sum = CHEBYSHEV[J - 1];
246
247 for (l = 1; l <= 5; l++) {
248 J = 1 + l + 6 * (i - 1) + 6 * 4 * (j - 1);
249 sum = sum + CHEBYSHEV[J - 1] * cos(l * theta_rad);
250 }
251 J = 1 + i + 6 * j;
252 Z[J - 1] = sum;
253 }
254 }
255 }
256 return Z;
257}
258
262double AttFac(const double sigir, const double sigsr, const vector<double> &Z) {
263 double facti = 1.0;
264 double att = 0.0;
265
266 for (size_t i = 0; i <= 5; i++) {
267 double facts = 1.0;
268 for (size_t j = 0; j <= 5; j++) {
269 if (i + j <= 5) {
270 size_t J = 1 + i + 6 * j; // TODO J defined in terms of j?
271 att = att + Z[J - 1] * facts * facti;
272 facts = -facts * sigsr / static_cast<double>(j + 1);
273 }
274 }
275 facti = -facti * sigir / static_cast<double>(i + 1);
276 }
277 return att;
278}
279
280double calculate_abs_factor(const double radius, const double Q2, const double sigsct, const vector<double> &Z,
281 const double wavelength) {
282
283 const double sigabs = Q2 * wavelength;
284 const double sigir = (sigabs + sigsct) * radius;
290 const double sigsr = sigir;
291
292 return AttFac(sigir, sigsr, Z);
293}
294
295double calculate_ms_factor(const double radius, const double Q2, const double sigsct, const vector<double> &Z,
296 const double wavelength) {
297
298 const double sigabs = Q2 * wavelength;
299 const double sigir = (sigabs + sigsct) * radius;
305 const double sigsr = sigir;
306
307 const double delta = COEFF4 * sigir + COEFF5 * sigir * sigir;
308 const double deltp = (delta * sigsct) / (sigsct + sigabs);
309
310 double temp = AttFac(sigir, sigsr, Z);
311 return (deltp / temp);
312}
313
314} // namespace
315
330void CalculateCarpenterSampleCorrection::calculate_abs_correction(const double angle_deg, const double radius,
331 const double coeff1, const double coeff2,
332 const double coeff3, const Points &wavelength,
333 HistogramY &y_val) {
334
335 const size_t NUM_Y = y_val.size();
336 bool is_histogram = false;
337 if (wavelength.size() == NUM_Y + 1)
338 is_histogram = true;
339 else if (wavelength.size() == NUM_Y)
340 is_histogram = false;
341 else
342 throw std::runtime_error("Data is neither historgram or density");
343
344 // initialize Z array for this angle
345 vector<double> Z = createZ(angle_deg);
346
347 const double Q2 = coeff1 * coeff2;
348 const double sigsct = coeff2 * coeff3;
349
350 for (size_t j = 0; j < NUM_Y; j++) {
351 double wl_val = wavelength[j];
352 if (is_histogram) // average with next value
353 wl_val = .5 * (wl_val + wavelength[j + 1]);
354
355 y_val[j] = calculate_abs_factor(radius, Q2, sigsct, Z, wl_val);
356 }
357}
358
359void CalculateCarpenterSampleCorrection::calculate_ms_correction(const double angle_deg, const double radius,
360 const double coeff1, const double coeff2,
361 const double coeff3, const Points &wavelength,
362 HistogramY &y_val) {
363
364 const size_t NUM_Y = y_val.size();
365 bool is_histogram = false;
366 if (wavelength.size() == NUM_Y + 1)
367 is_histogram = true;
368 else if (wavelength.size() == NUM_Y)
369 is_histogram = false;
370 else
371 throw std::runtime_error("Data is neither historgram or density");
372
373 // initialize Z array for this angle
374 vector<double> Z = createZ(angle_deg);
375
376 const double Q2 = coeff1 * coeff2;
377 const double sigsct = coeff2 * coeff3;
378
379 for (size_t j = 0; j < NUM_Y; j++) {
380 double wl_val = wavelength[j];
381 if (is_histogram) // average with next value
382 wl_val = .5 * (wl_val + wavelength[j + 1]);
383
384 y_val[j] = calculate_ms_factor(radius, Q2, sigsct, Z, wl_val);
385 }
386}
387
389 const std::string &ylabel) const {
390 MatrixWorkspace_sptr outputWS = create<HistoWorkspace>(*inputWksp);
391 // The algorithm computes the signal values at bin centres so they should
392 // be treated as a distribution
393 outputWS->setDistribution(true);
394 outputWS->setYUnit("");
395 outputWS->setYUnitLabel(ylabel);
396 return outputWS;
397}
398
400 auto alg = this->createChildAlgorithm("SetUncertainties");
401 alg->initialize();
402 alg->setProperty("InputWorkspace", workspace);
403 alg->execute();
404 return alg->getProperty("OutputWorkspace");
405}
406
408 auto alg = this->createChildAlgorithm("DeleteWorkspace");
409 alg->initialize();
410 alg->setChild(true);
411 alg->setLogging(false);
412 alg->setProperty("Workspace", workspace);
413 alg->execute();
414}
415
416} // namespace Mantid::Algorithms
#define DECLARE_ALGORITHM(classname)
Definition Algorithm.h:538
IPeaksWorkspace_sptr workspace
std::map< DeltaEMode::Type, std::string > index
#define PARALLEL_START_INTERRUPT_REGION
Begins a block to skip processing is the algorithm has been interupted Note the end of the block if n...
#define PARALLEL_END_INTERRUPT_REGION
Ends a block to skip processing is the algorithm has been interupted Note the start of the block if n...
#define PARALLEL_FOR_IF(condition)
Empty definitions - to enable set your complier to enable openMP.
#define PARALLEL_CHECK_INTERRUPT_REGION
Adds a check after a Parallel region to see if it was interupted.
std::shared_ptr< Algorithm > createChildAlgorithm(const std::string &name, const double startProgress=-1., const double endProgress=-1., const bool enableLogging=true, const int &version=-1) override
Create a Child Algorithm.
Kernel::IPropertyManager::TypedValue getProperty(const std::string &name) const override
Get the property held by this object.
std::string getPropertyValue(const std::string &name) const override
Get the property held by this object.
A validator which checks that a workspace has a valid instrument.
Helper class for reporting progress from algorithms.
Definition Progress.h:25
A property class for workspaces.
A validator which checks that the unit of the workspace referred to by a WorkspaceProperty is the exp...
int version() const override
Algorithm's version for identification overriding a virtual method.
const std::string name() const override
Algorithm's name for identification overriding a virtual method.
void calculate_abs_correction(const double angle_deg, const double radius, const double coeff1, const double coeff2, const double coeff3, const HistogramData::Points &wavelength, HistogramData::HistogramY &y_val)
CalculateCarpenterSampleCorrection correction calculation.
void calculate_ms_correction(const double angle_deg, const double radius, const double coeff1, const double coeff2, const double coeff3, const HistogramData::Points &wavelength, HistogramData::HistogramY &y_val)
const std::string category() const override
Algorithm's category for identification overriding a virtual method.
void init() override
Initialize the properties to default values.
API::MatrixWorkspace_sptr createOutputWorkspace(const API::MatrixWorkspace_sptr &inputWS, const std::string &) const
API::MatrixWorkspace_sptr setUncertainties(const API::MatrixWorkspace_sptr &workspace)
This class is intended to fulfill the design specified in <https://github.com/mantidproject/documents...
void debug(const std::string &msg)
Logs at debug level.
Definition Logger.cpp:145
void information(const std::string &msg)
Logs at information level.
Definition Logger.cpp:136
A material is defined as being composed of a given element, defined as a PhysicalConstants::NeutronAt...
Definition Material.h:50
double numberDensity() const
Get the number density.
Definition Material.cpp:189
double absorbXSection(const double lambda=PhysicalConstants::NeutronAtom::ReferenceLambda) const
Get the absorption cross section at a given wavelength in barns.
Definition Material.cpp:260
const std::string & name() const
Returns the name of the material.
Definition Material.cpp:181
double totalScatterXSection() const
Return the total scattering cross section for a given wavelength in barns.
Definition Material.cpp:252
void report()
Increments the loop counter by 1, then sends the progress notification on behalf of its algorithm.
std::shared_ptr< MatrixWorkspace > MatrixWorkspace_sptr
shared pointer to the matrix workspace base class
Kernel::Logger g_log("DetermineSpinStateOrder")
std::shared_ptr< EventWorkspace > EventWorkspace_sptr
shared pointer to the EventWorkspace class
std::shared_ptr< const IComponent > IComponent_const_sptr
Typdef of a shared pointer to a const IComponent.
Definition IComponent.h:167
std::shared_ptr< const Instrument > Instrument_const_sptr
Shared pointer to an const instrument object.
MANTID_KERNEL_DLL bool equals(T const x, T const y)
Test for equality of doubles using compiler-defined precision.
std::enable_if< std::is_pointer< Arg >::value, bool >::type threadSafe(Arg workspace)
Thread-safety check Checks the workspace to ensure it is suitable for multithreaded access.
A namespace containing physical constants that are required by algorithms and unit routines.
Definition Atom.h:14
@ Input
An input workspace.
Definition Property.h:53
@ Output
An output workspace.
Definition Property.h:54
Structure to store neutronic scattering information for the various elements.
Definition NeutronAtom.h:22