Mantid
Loading...
Searching...
No Matches
SofQCommon.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/Run.h"
15#include "MantidKernel/Unit.h"
17
18namespace Mantid::Algorithms {
27 // Retrieve the emode & efixed properties
28 const std::string emode = hostAlgorithm->getProperty("EMode");
29 // Convert back to an integer representation
30 m_emode = 0;
31 if (emode == "Direct")
32 m_emode = 1;
33 else if (emode == "Indirect")
34 m_emode = 2;
35 m_efixed = hostAlgorithm->getProperty("EFixed");
36
37 // Check whether they should have supplied an EFixed value
38 if (m_emode == 1) // Direct
39 {
40 // If GetEi was run then it will have been stored in the workspace, if not
41 // the user will need to enter one
42 if (m_efixed == 0.0) {
43 if (workspace.run().hasProperty("Ei")) {
44 Kernel::Property *p = workspace.run().getProperty("Ei");
45 auto *eiProp = dynamic_cast<Kernel::PropertyWithValue<double> *>(p);
46 if (!eiProp)
47 throw std::runtime_error("Input workspace contains Ei but its "
48 "property type is not a double.");
49 m_efixed = (*eiProp)();
50 } else {
51 throw std::invalid_argument("Input workspace does not contain an "
52 "EFixed value. Please provide one or run "
53 "GetEi.");
54 }
55 } else {
56 m_efixedGiven = true;
57 }
58 } else {
59 if (m_efixed != 0.0) {
60 m_efixedGiven = true;
61 }
62 }
63}
64
74 double efixed(0.0);
75 if (m_emode == 1) // Direct
76 {
78 } else // Indirect
79 {
80 if (m_efixedGiven)
81 efixed = m_efixed; // user provided a value
82 else {
83 const std::vector<double> param = det.getNumberParameter("EFixed");
84 if (param.empty())
85 throw std::runtime_error("Cannot find EFixed parameter for component \"" + det.getName() +
86 "\". This is required in indirect mode. Please check the IDF "
87 "contains these values.");
88 efixed = param[0];
89 }
90 }
91 return efixed;
92}
93
102double SofQCommon::getEFixed(const Geometry::ComponentInfo &componentInfo, const size_t componentIndex) const {
103 if (m_emode == 1 || m_efixedGiven) {
104 return m_efixed;
105 }
106 const std::vector<double> param = componentInfo.getNumberParameter(componentIndex, "EFixed");
107 if (param.empty())
108 throw std::runtime_error("Cannot find EFixed parameter for component \"" + componentInfo.name(componentIndex) +
109 "\". This is required in indirect mode. Please check the IDF "
110 "contains these values.");
111 return param[0];
112}
113
122double SofQCommon::q(const double deltaE, const double twoTheta, const Geometry::IDetector *det) const {
123 if (m_emode == 1) {
124 return directQ(deltaE, twoTheta);
125 }
126 return indirectQ(deltaE, twoTheta, det);
127}
128
137double SofQCommon::q(const double deltaE, const double twoTheta, const double efixed) const {
138 if (m_emode == 1) {
139 return directQ(deltaE, twoTheta);
140 }
141 return indirectQFromEFixed(deltaE, twoTheta, efixed);
142}
143
151std::pair<double, double> SofQCommon::qBinHints(const API::MatrixWorkspace &ws, const double minE,
152 const double maxE) const {
153 if (m_emode == 1) {
154 return qBinHintsDirect(ws, minE, maxE);
155 }
156 return qBinHintsIndirect(ws, minE, maxE);
157}
158
165double SofQCommon::directQ(const double deltaE, const double twoTheta) const {
167 const double ki = std::sqrt(m_efixed / E_mev_toNeutronWavenumberSq);
168 const double kf = std::sqrt((m_efixed - deltaE) / E_mev_toNeutronWavenumberSq);
169 return std::sqrt(ki * ki + kf * kf - 2. * ki * kf * std::cos(twoTheta));
170}
171
179double SofQCommon::indirectQ(const double deltaE, const double twoTheta, const Geometry::IDetector *det) const {
180 if (!det) {
181 throw std::runtime_error("indirectQ: det is nullptr.");
182 }
183 return indirectQFromEFixed(deltaE, twoTheta, getEFixed(*det));
184}
185
193double SofQCommon::indirectQFromEFixed(const double deltaE, const double twoTheta, const double efixed) {
195 const double ki = std::sqrt((efixed + deltaE) / E_mev_toNeutronWavenumberSq);
196 const double kf = std::sqrt(efixed / E_mev_toNeutronWavenumberSq);
197 return std::sqrt(ki * ki + kf * kf - 2. * ki * kf * std::cos(twoTheta));
198}
199
208std::pair<double, double> SofQCommon::qBinHintsDirect(const API::MatrixWorkspace &ws, const double minE,
209 const double maxE) const {
210 using namespace Mantid::PhysicalConstants;
211 if (maxE > m_efixed) {
212 throw std::invalid_argument("Cannot compute Q binning range: maximum "
213 "energy transfer is greater than the incident "
214 "energy.");
215 }
216 auto minTwoTheta = std::numeric_limits<double>::max();
217 auto maxTwoTheta = std::numeric_limits<double>::lowest();
218 const auto &spectrumInfo = ws.spectrumInfo();
219 for (size_t i = 0; i < spectrumInfo.size(); ++i) {
220 if (spectrumInfo.isMasked(i) || spectrumInfo.isMonitor(i)) {
221 continue;
222 }
223 const auto twoTheta = spectrumInfo.twoTheta(i);
224 minTwoTheta = std::min(minTwoTheta, twoTheta);
225 maxTwoTheta = std::max(maxTwoTheta, twoTheta);
226 }
227 if (minTwoTheta == std::numeric_limits<double>::max()) {
228 throw std::runtime_error("Could not determine Q binning: workspace does "
229 "not contain usable spectra.");
230 }
231 std::array<double, 4> qArr;
232 qArr[0] = directQ(minE, minTwoTheta);
233 qArr[1] = directQ(minE, maxTwoTheta);
234 qArr[2] = directQ(maxE, minTwoTheta);
235 qArr[3] = directQ(maxE, maxTwoTheta);
236 const auto minmaxQ = std::minmax_element(qArr.cbegin(), qArr.cend());
237 return std::make_pair(*minmaxQ.first, *minmaxQ.second);
238}
239
250std::pair<double, double> SofQCommon::qBinHintsIndirect(const API::MatrixWorkspace &ws, const double minE,
251 const double maxE) const {
252 using namespace Mantid::PhysicalConstants;
253 auto minQ = std::numeric_limits<double>::max();
254 auto maxQ = std::numeric_limits<double>::lowest();
255 const auto &detectorInfo = ws.detectorInfo();
256 const auto &componentInfo = ws.componentInfo();
257 for (size_t i = 0; i < detectorInfo.size(); ++i) {
258 if (detectorInfo.isMasked(i) || detectorInfo.isMonitor(i)) {
259 continue;
260 }
261 const auto twoTheta = detectorInfo.twoTheta(i);
262 const auto efixed = getEFixed(componentInfo, i);
263 const auto Q1 = indirectQFromEFixed(minE, twoTheta, efixed);
264 const auto Q2 = indirectQFromEFixed(maxE, twoTheta, efixed);
265 if (!std::isfinite(Q1) || !std::isfinite(Q2)) {
266 throw std::invalid_argument("Cannot compute Q binning range: non-finite "
267 "Q found for detector ID " +
268 std::to_string(detectorInfo.detid(i)));
269 }
270 const auto minmaxQ = std::minmax(Q1, Q2);
271 minQ = std::min(minQ, minmaxQ.first);
272 maxQ = std::max(maxQ, minmaxQ.second);
273 }
274 if (minQ == std::numeric_limits<double>::max()) {
275 throw std::runtime_error("Could not determine Q binning: workspace does "
276 "not contain usable spectra.");
277 }
278 return std::make_pair(minQ, maxQ);
279}
280} // namespace Mantid::Algorithms
IPeaksWorkspace_sptr workspace
Base class from which all concrete algorithm classes should be derived.
Definition Algorithm.h:76
TypedValue getProperty(const std::string &name) const override
Get the value of a property.
const SpectrumInfo & spectrumInfo() const
Return a reference to the SpectrumInfo object.
const Geometry::DetectorInfo & detectorInfo() const
Return a const reference to the DetectorInfo object.
const Geometry::ComponentInfo & componentInfo() const
Base MatrixWorkspace Abstract Class.
double twoTheta(const size_t index) const
Returns the scattering angle 2 theta in radians (angle w.r.t.
ComponentInfo : Provides a component centric view on to the instrument.
std::vector< double > getNumberParameter(const size_t componentIndex, const std::string &name, bool recursive=true) const
const std::string & name(const size_t componentIndex) const
virtual std::vector< double > getNumberParameter(const std::string &pname, bool recursive=true) const =0
Get a parameter defined as a double.
virtual std::string getName() const =0
Get the IComponent name.
Interface class for detector objects.
Definition IDetector.h:37
The concrete, templated class for properties.
Base class for properties.
Definition Property.h:95
A namespace containing physical constants that are required by algorithms and unit routines.
Definition Atom.h:14
static constexpr double E_mev_toNeutronWavenumberSq
Transformation coefficient to transform neutron energy into neutron wavevector: K-neutron[m^-10] = sq...
std::string to_string(const wide_integer< Bits, Signed > &n)
std::pair< double, double > qBinHintsDirect(const API::MatrixWorkspace &ws, const double minE, const double maxE) const
Return a pair of (minimum Q, maximum Q) for given direct geometry workspace.
double indirectQ(const double deltaE, const double twoTheta, const Geometry::IDetector *det) const
Calculate the Q value for an indirect instrument.
std::pair< double, double > qBinHints(const API::MatrixWorkspace &ws, const double minE, const double maxE) const
Estimate minimum and maximum momentum transfer.
double directQ(const double deltaE, const double twoTheta) const
Calculate the Q value for a direct instrument.
void initCachedValues(const API::MatrixWorkspace &workspace, API::Algorithm const *hostAlgorithm)
The procedure analyses emode and efixed properties provided to the algorithm and identify the energy ...
bool m_efixedGiven
EFixed has been provided.
Definition SofQCommon.h:26
double getEFixed(const Geometry::IDetector &det) const
Get the efixed value for the given detector.
std::pair< double, double > qBinHintsIndirect(const API::MatrixWorkspace &ws, const double minE, const double maxE) const
Return a pair of (minimum Q, maximum Q) for given indirect geometry workspace.
static double indirectQFromEFixed(const double deltaE, const double twoTheta, const double efixed)
Calculate the Q value for an indirect instrument.
double q(const double deltaE, const double twoTheta, const Geometry::IDetector *det) const
Calculate the Q value.