Mantid
Loading...
Searching...
No Matches
SofQWCentre.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 +
16
17namespace Mantid::Algorithms {
18
19// Register the algorithm into the AlgorithmFactory
20DECLARE_ALGORITHM(SofQWCentre)
21
22using namespace Kernel;
23using namespace API;
24
29
31 using namespace Geometry;
33
34 MatrixWorkspace_const_sptr inputWorkspace = getProperty("InputWorkspace");
35
36 m_EmodeProperties.initCachedValues(*inputWorkspace, this);
37 const int emode = m_EmodeProperties.m_emode;
38
39 std::vector<double> verticalAxis;
40 MatrixWorkspace_sptr outputWorkspace = SofQW::setUpOutputWorkspace<DataObjects::Workspace2D>(
41 *inputWorkspace, getProperty("QAxisBinning"), verticalAxis, getProperty("EAxisBinning"), m_EmodeProperties);
42 setProperty("OutputWorkspace", outputWorkspace);
43 const auto &xAxis = outputWorkspace->binEdges(0).rawData();
44
45 // Holds the spectrum-detector mapping
46 std::vector<specnum_t> specNumberMapping;
47 std::vector<detid_t> detIDMapping;
48
49 const auto &detectorInfo = inputWorkspace->detectorInfo();
50 const auto &spectrumInfo = inputWorkspace->spectrumInfo();
51 const V3D beamDir = normalize(detectorInfo.samplePosition() - detectorInfo.sourcePosition());
52 const double l1 = detectorInfo.l1();
53 g_log.debug() << "Source-sample distance: " << l1 << '\n';
54
55 // Loop over input workspace bins, reassigning data to correct bin in output
56 // qw workspace
57 const size_t numHists = inputWorkspace->getNumberHistograms();
58 const size_t numBins = inputWorkspace->blocksize();
59 Progress prog(this, 0.0, 1.0, numHists);
60 for (int64_t i = 0; i < int64_t(numHists); ++i) {
61 if (!spectrumInfo.hasDetectors(i) || spectrumInfo.isMonitor(i))
62 continue;
63
64 const auto &spectrumDet = spectrumInfo.detector(i);
65 const double efixed = m_EmodeProperties.getEFixed(spectrumDet);
66
67 // For inelastic scattering the simple relationship q=4*pi*sinTheta/lambda
68 // does not hold. In order to
69 // be completely general we must calculate the momentum transfer by
70 // calculating the incident and final
71 // wave vectors and then use |q| = sqrt[(ki - kf)*(ki - kf)]
72
73 const auto &detIDs = inputWorkspace->getSpectrum(i).getDetectorIDs();
74 auto numDets_d = static_cast<double>(detIDs.size());
75 const auto &Y = inputWorkspace->y(i);
76 const auto &E = inputWorkspace->e(i);
77 const auto &X = inputWorkspace->x(i);
78
79 // Loop over the detectors and for each bin calculate Q
80 for (const auto detID : detIDs) {
81 try {
82 size_t idet = detectorInfo.indexOf(detID);
83 // Calculate kf vector direction and then Q for each energy bin
84 const V3D scatterDir = normalize(detectorInfo.position(idet) - detectorInfo.samplePosition());
85 for (size_t j = 0; j < numBins; ++j) {
86 if (X[j] < xAxis.front() || X[j + 1] > xAxis.back())
87 continue;
88
89 const double deltaE = 0.5 * (X[j] + X[j + 1]);
90 // Compute ki and kf wave vectors and therefore q = ki - kf
91 double ei(0.0), ef(0.0);
92 if (emode == 1) {
93 ei = efixed;
94 ef = efixed - deltaE;
95 if (ef < 0) {
96 std::string mess = "Energy transfer requested in Direct mode exceeds incident "
97 "energy.\n Found for det ID: " +
98 std::to_string(idet) + " bin No " + std::to_string(j) +
99 " with Ei=" + boost::lexical_cast<std::string>(efixed) +
100 " and energy transfer: " + boost::lexical_cast<std::string>(deltaE);
101 throw std::runtime_error(mess);
102 }
103 } else {
104 ei = efixed + deltaE;
105 ef = efixed;
106 if (ef < 0) {
107 std::string mess = "Incident energy of a neutron is negative. Are you trying to "
108 "process Direct data in Indirect mode?\n Found for det ID: " +
109 std::to_string(idet) + " bin No " + std::to_string(j) +
110 " with efied=" + boost::lexical_cast<std::string>(efixed) +
111 " and energy transfer: " + boost::lexical_cast<std::string>(deltaE);
112 throw std::runtime_error(mess);
113 }
114 }
115
116 if (ei < 0)
117 throw std::runtime_error("Negative incident energy. Check binning.");
118
119 const V3D ki = beamDir * sqrt(ei / E_mev_toNeutronWavenumberSq);
120 const V3D kf = scatterDir * sqrt(ef / E_mev_toNeutronWavenumberSq);
121 const double q = (ki - kf).norm();
122
123 // Test whether it's in range of the Q axis
124 if (q < verticalAxis.front() || q > verticalAxis.back())
125 continue;
126 // Find which q bin this point lies in
127 const MantidVec::difference_type qIndex =
128 std::upper_bound(verticalAxis.begin(), verticalAxis.end(), q) - verticalAxis.begin() - 1;
129 // Find which e bin this point lies in
130 const MantidVec::difference_type eIndex =
131 std::upper_bound(xAxis.begin(), xAxis.end(), deltaE) - xAxis.begin() - 1;
132
133 // Add this spectra-detector pair to the mapping
134 specNumberMapping.emplace_back(outputWorkspace->getSpectrum(qIndex).getSpectrumNo());
135 detIDMapping.emplace_back(detID);
136
137 // And add the data and it's error to that bin, taking into account
138 // the number of detectors contributing to this bin
139 outputWorkspace->mutableY(qIndex)[eIndex] += Y[j] / numDets_d;
140 // Standard error on the average
141 outputWorkspace->mutableE(qIndex)[eIndex] =
142 sqrt((pow(outputWorkspace->e(qIndex)[eIndex], 2) + pow(E[j], 2)) / numDets_d);
143 }
144 } catch (std::out_of_range &) {
145 // Skip invalid detector IDs
146 numDets_d -= 1.0;
147 continue;
148 }
149 }
150 prog.report();
151 }
152
153 // If the input workspace was a distribution, need to divide by q bin width
154 if (inputWorkspace->isDistribution())
155 this->makeDistribution(*outputWorkspace, verticalAxis);
156
157 // Set the output spectrum-detector mapping
158 SpectrumDetectorMapping outputDetectorMap(specNumberMapping, detIDMapping);
159 outputWorkspace->updateSpectraUsing(outputDetectorMap);
160
161 // Replace any NaNs in outputWorkspace with zeroes
162 if (this->getProperty("ReplaceNaNs")) {
163 auto replaceNans = this->createChildAlgorithm("ReplaceSpecialValues");
164 replaceNans->setChild(true);
165 replaceNans->initialize();
166 replaceNans->setProperty("InputWorkspace", outputWorkspace);
167 replaceNans->setProperty("OutputWorkspace", outputWorkspace);
168 replaceNans->setProperty("NaNValue", 0.0);
169 replaceNans->setProperty("InfinityValue", 0.0);
170 replaceNans->setProperty("BigNumberThreshold", DBL_MAX);
171 replaceNans->execute();
172 }
173}
174
179void SofQWCentre::makeDistribution(API::MatrixWorkspace &outputWS, const std::vector<double> &qAxis) {
180 std::vector<double> widths(qAxis.size());
181 std::adjacent_difference(qAxis.begin(), qAxis.end(), widths.begin());
182
183 const size_t numQBins = outputWS.getNumberHistograms();
184 for (size_t i = 0; i < numQBins; ++i) {
185 auto &Y = outputWS.mutableY(i);
186 auto &E = outputWS.mutableE(i);
187 using std::placeholders::_1;
188 std::transform(Y.begin(), Y.end(), Y.begin(), std::bind(std::divides<double>(), _1, widths[i + 1]));
189 std::transform(E.begin(), E.end(), E.begin(), std::bind(std::divides<double>(), _1, widths[i + 1]));
190 }
191}
192
193} // 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
Kernel::Logger & g_log
Definition: Algorithm.h:451
Base MatrixWorkspace Abstract Class.
virtual std::size_t getNumberHistograms() const =0
Returns the number of histograms in the workspace.
HistogramData::HistogramE & mutableE(const size_t index) &
HistogramData::HistogramY & mutableY(const size_t index) &
Helper class for reporting progress from algorithms.
Definition: Progress.h:25
A minimal class to hold the mapping between the spectrum number and its related detector ID numbers f...
void exec() override
Execution code.
Definition: SofQWCentre.cpp:30
static void makeDistribution(API::MatrixWorkspace &outputWS, const std::vector< double > &qAxis)
Convert the workspace to a distribution.
void init() override
Initialization code.
Definition: SofQWCentre.cpp:28
static void createCommonInputProperties(API::Algorithm &alg)
Create the input properties on the given algorithm object.
Definition: SofQW.cpp:69
IPropertyManager * setProperty(const std::string &name, const T &value)
Templated method to set the value of a PropertyWithValue.
void debug(const std::string &msg)
Logs at debug level.
Definition: Logger.cpp:114
void report()
Increments the loop counter by 1, then sends the progress notification on behalf of its algorithm.
Definition: ProgressBase.h:51
Class for 3D vectors.
Definition: V3D.h:34
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
MANTID_KERNEL_DLL V3D normalize(V3D v)
Normalizes a V3D.
Definition: V3D.h:341
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)
double getEFixed(const Geometry::IDetector &det) const
Get the efixed value for the given detector.
Definition: SofQCommon.cpp:72
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 ...
Definition: SofQCommon.cpp:25