Mantid
Loading...
Searching...
No Matches
CalculatePlaczekSelfScattering.cpp
Go to the documentation of this file.
1// Mantid Repository : https://github.com/mantidproject/mantid
2//
3// Copyright © 2019 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 +
8#include "MantidAPI/Axis.h"
9#include "MantidAPI/Sample.h"
15#include "MantidKernel/Atom.h"
19#include "MantidKernel/Unit.h"
20
21#include <utility>
22
23namespace Mantid::Algorithms {
24
25namespace { // anonymous namespace
26
27// calculate summation term w/ neutron mass over molecular mass ratio
28double calculateSummationTerm(const Kernel::Material &material) {
29 // add together the weighted sum
30 const auto &formula = material.chemicalFormula();
31 auto sumLambda = [](double sum, const auto &formula_unit) {
32 return sum + formula_unit.multiplicity * formula_unit.atom->neutron.tot_scatt_xs / formula_unit.atom->mass;
33 };
34 const double unnormalizedTerm = std::accumulate(formula.begin(), formula.end(), 0.0, sumLambda);
35
36 // neutron mass converted to atomic mass comes out of the sum
38 // normalizing by totalStoich (number of atoms) comes out of the sum
39 const double totalStoich = material.totalAtoms();
40 // converting scattering cross section to scattering length square comes out of the sum
41 return neutronMass * unnormalizedTerm / (4. * M_PI * totalStoich);
42}
43
44} // anonymous namespace
45
46DECLARE_ALGORITHM(CalculatePlaczekSelfScattering)
47
49 declareProperty(
50 std::make_unique<API::WorkspaceProperty<API::MatrixWorkspace>>("InputWorkspace", "", Kernel::Direction::Input),
51 "Raw diffraction data workspace for associated correction to be "
52 "calculated for. Workspace must have instrument and sample data.");
53 auto inspValidator = std::make_shared<Mantid::Kernel::CompositeValidator>();
54 inspValidator->add<Mantid::API::WorkspaceUnitValidator>("Wavelength");
55 declareProperty(std::make_unique<API::WorkspaceProperty<API::MatrixWorkspace>>(
56 "IncidentSpectra", "", Kernel::Direction::Input, inspValidator),
57 "Workspace of fitted incident spectrum with it's first derivative. Must be in units of Wavelength.");
58 declareProperty(
59 std::make_unique<API::WorkspaceProperty<API::MatrixWorkspace>>("OutputWorkspace", "", Kernel::Direction::Output),
60 "Workspace with the Self scattering correction, in the same unit as the InputWorkspace.");
61 declareProperty("CrystalDensity", EMPTY_DBL(), "The crystalographic density of the sample material.");
62 declareProperty("ScaleByPackingFraction", true, "Scale the correction value by packing fraction.");
63}
64//----------------------------------------------------------------------------------------------
67std::map<std::string, std::string> CalculatePlaczekSelfScattering::validateInputs() {
68 std::map<std::string, std::string> issues;
69 const API::MatrixWorkspace_sptr inWS = getProperty("InputWorkspace");
70 const API::SpectrumInfo specInfo = inWS->spectrumInfo();
71 if (specInfo.size() == 0) {
72 issues["InputWorkspace"] = "Input workspace does not have detector information";
73 }
74 Kernel::Material::ChemicalFormula formula = inWS->sample().getMaterial().chemicalFormula();
75 if (formula.size() == 0) {
76 issues["InputWorkspace"] = "Input workspace does not have a valid sample";
77 }
78 return issues;
79}
80
81//----------------------------------------------------------------------------------------------
83 // get a handle to the material
84 const auto &material = ws->sample().getMaterial();
85
86 // default value is packing fraction
87 double packingFraction = material.packingFraction();
88
89 // see if the user thinks the material wasn't setup right
90 const double crystalDensity = getProperty("CrystalDensity");
91 if (crystalDensity > 0. && !isEmpty(crystalDensity)) {
92 // assume that the number density set in the Material is the effective number density
93 packingFraction = material.numberDensity() / crystalDensity;
94 }
95
96 return packingFraction;
97}
98
99//----------------------------------------------------------------------------------------------
103 const API::MatrixWorkspace_sptr inWS = getProperty("InputWorkspace");
104 const API::MatrixWorkspace_sptr incidentWS = getProperty("IncidentSpectra");
105 const bool scaleByPackingFraction = getProperty("ScaleByPackingFraction");
106 auto inputUnit = inWS->getAxis(0)->unit();
107
108 // calculate summation term w/ neutron mass over molecular mass ratio
109 const double summationTerm = calculateSummationTerm(inWS->sample().getMaterial());
110 const double packingFraction = getPackingFraction(inWS);
111
112 // get incident spectrum and 1st derivative
113 const MantidVec xLambda = incidentWS->readX(0);
114 const MantidVec incident = incidentWS->readY(0);
115 const MantidVec incidentPrime = incidentWS->readY(1);
116 const double dx = (xLambda[1] - xLambda[0]) / 2.0; // assume constant bin width
117 std::vector<double> phi1;
118 for (size_t i = 0; i < xLambda.size() - 1; i++) {
119 phi1.emplace_back((xLambda[i] + dx) * incidentPrime[i] / incident[i]);
120 }
121 // set detector law term (eps1)
122 std::vector<double> eps1;
123 constexpr auto LambdaD = 1.44;
124 for (size_t i = 0; i < xLambda.size() - 1; i++) {
125 const auto xTerm = -(xLambda[i] + dx) / LambdaD;
126 eps1.emplace_back(xTerm * exp(xTerm) / (1.0 - exp(xTerm)));
127 }
128 /* Placzek
129 Original Placzek inelastic correction Ref (for constant wavelength, reactor
130 source): Placzek, Phys. Rev v86, (1952), pp. 377-388 First Placzek
131 correction for time-of-flight, pulsed source (also shows reactor eqs.):
132 Powles, Mol. Phys., v6 (1973), pp.1325-1350
133 Nomenclature and calculation for this program follows Ref:
134 Howe, McGreevy, and Howells, J. Phys.: Condens. Matter v1, (1989), pp.
135 3433-3451 NOTE: Powles's Equation for inelastic self-scattering is equal to
136 Howe's Equation for P(theta) by adding the elastic self-scattering
137 */
138
139 const auto &specInfo = inWS->spectrumInfo();
140 // prep the output workspace
141 // - use instrument information from InputWorkspace
142 // - use the bin Edges from the incident flux
144 DataObjects::create<API::HistoWorkspace>(*inWS, incidentWS->getSpectrum(0).binEdges());
145 // Set outputWS unit to Wavelength
146 outputWS->getAxis(0)->unit() = incidentWS->getAxis(0)->unit();
147 // The algorithm computes the signal values at bin centres so they should
148 // be treated as a distribution
149 outputWS->setDistribution(true);
150 outputWS->setYUnit("");
151 for (size_t specIndex = 0; specIndex < specInfo.size(); specIndex++) {
152 auto &y = outputWS->mutableY(specIndex);
153 double l1 = specInfo.l1();
154 double l2 = specInfo.l2(specIndex);
155
156 if (!specInfo.isMonitor(specIndex) && !(specInfo.l2(specIndex) == 0.0)) {
157 double twoTheta = specInfo.twoTheta(specIndex);
158 const double sinThetaBy2 = sin(twoTheta / 2.0);
159 const double f = l1 / (l1 + l2);
160 for (size_t xIndex = 0; xIndex < xLambda.size() - 1; xIndex++) {
161 const double term1 = (f - 1.0) * phi1[xIndex];
162 const double term2 = f * (1.0 - eps1[xIndex]);
163 const double inelasticPlaczekSelfCorrection =
164 2.0 * (term1 + term2 - 3) * sinThetaBy2 * sinThetaBy2 * summationTerm;
165 y[xIndex] =
166 scaleByPackingFraction ? inelasticPlaczekSelfCorrection * packingFraction : inelasticPlaczekSelfCorrection;
167 }
168 } else {
169 for (size_t xIndex = 0; xIndex < xLambda.size() - 1; xIndex++) {
170 y[xIndex] = 0;
171 }
172 }
173 }
174
175 auto cvtalg = createChildAlgorithm("ConvertUnits");
176 cvtalg->setProperty("InputWorkspace", outputWS);
177 cvtalg->setProperty("OutputWorkspace", outputWS);
178 cvtalg->setProperty("Target", inputUnit->unitID());
179 cvtalg->execute();
180 outputWS = cvtalg->getProperty("OutputWorkspace");
181
182 setProperty("OutputWorkspace", outputWS);
183}
184
185} // namespace Mantid::Algorithms
#define DECLARE_ALGORITHM(classname)
Definition: Algorithm.h:576
TypedValue getProperty(const std::string &name) const override
Get the value of a property.
Definition: Algorithm.cpp:2076
virtual 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)
Create a Child Algorithm.
Definition: Algorithm.cpp:842
static bool isEmpty(const NumT toCheck)
checks that the value was not set by users, uses the value in empty double/int.
API::SpectrumInfo is an intermediate step towards a SpectrumInfo that is part of Instrument-2....
Definition: SpectrumInfo.h:53
size_t size() const
Returns the size of the SpectrumInfo, i.e., the number of spectra.
A property class for workspaces.
A validator which checks that the unit of the workspace referred to by a WorkspaceProperty is the exp...
CalculatePlaczekSelfScattering : This algorithm calculates a correction for an incident spectrum defr...
double getPackingFraction(const API::MatrixWorkspace_const_sptr &ws)
std::map< std::string, std::string > validateInputs() override
Validate inputs.
IPropertyManager * setProperty(const std::string &name, const T &value)
Templated method to set the value of a PropertyWithValue.
std::vector< FormulaUnit > ChemicalFormula
Definition: Material.h:60
std::shared_ptr< const MatrixWorkspace > MatrixWorkspace_const_sptr
shared pointer to the matrix workspace base class (const version)
std::shared_ptr< MatrixWorkspace > MatrixWorkspace_sptr
shared pointer to the matrix workspace base class
static constexpr double NeutronMass
Mass of the neutron in kg.
static constexpr double AtomicMassUnit
AMU in kg.
std::vector< double > MantidVec
typedef for the data storage used in Mantid matrix workspaces
Definition: cow_ptr.h:172
constexpr double EMPTY_DBL() noexcept
Returns what we consider an "empty" double within a property.
Definition: EmptyValues.h:43
@ Input
An input workspace.
Definition: Property.h:53
@ Output
An output workspace.
Definition: Property.h:54