Mantid
Loading...
Searching...
No Matches
ConvertSpectrumAxis2.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"
18#include "MantidHistogramData/Histogram.h"
19#include "MantidHistogramData/HistogramBuilder.h"
23#include "MantidKernel/Unit.h"
26#include "MantidTypes/SpectrumDefinition.h"
27
28#include <cfloat>
29
30constexpr double rad2deg = 180.0 / M_PI;
31
32namespace Mantid::Algorithms {
33// Register the algorithm into the AlgorithmFactory
34DECLARE_ALGORITHM(ConvertSpectrumAxis2)
35using namespace Kernel;
36using namespace API;
37using namespace Geometry;
38using namespace DataObjects;
39using namespace HistogramData;
40
42 // Validator for Input Workspace
43 auto wsVal = std::make_shared<CompositeValidator>();
44 wsVal->add<SpectraAxisValidator>();
45 wsVal->add<InstrumentValidator>();
46 declareProperty(std::make_unique<WorkspaceProperty<>>("InputWorkspace", "", Direction::Input, wsVal),
47 "The name of the input workspace.");
48 declareProperty(std::make_unique<WorkspaceProperty<>>("OutputWorkspace", "", Direction::Output),
49 "The name to use for the output workspace.");
50 std::vector<std::string> targetOptions{
51 "Theta", "SignedTheta", "InPlaneTwoTheta", "SignedInPlaneTwoTheta", "ElasticQ",
52 "ElasticQSquared", "theta", "signed_theta", "ElasticDSpacing"};
53 declareProperty("Target", "", std::make_shared<StringListValidator>(targetOptions),
54 "The unit to which spectrum axis is converted to - \"theta\" (for the "
55 "angle in degrees), Q or Q^2, where elastic Q is evaluated at EFixed. "
56 "InPlaneTwoTheta and SignedInPlaneTwoTheta are the angle when each point "
57 "is projected on the horizontal plane."
58 "Note that 'theta' and 'signed_theta' are there for compatibility "
59 "purposes; they are the same as 'Theta' and 'SignedTheta' respectively");
60 std::vector<std::string> eModeOptions;
61 eModeOptions.emplace_back("Direct");
62 eModeOptions.emplace_back("Indirect");
63 declareProperty("EMode", "Direct", std::make_shared<StringListValidator>(eModeOptions),
64 "Some unit conversions require this value to be set "
65 "(\"Direct\" or \"Indirect\")");
66 auto mustBePositive = std::make_shared<BoundedValidator<double>>();
67 mustBePositive->setLower(0.0);
68 declareProperty("EFixed", EMPTY_DBL(), mustBePositive,
69 "Value of fixed energy in meV : EI (EMode=Direct) or EF "
70 "(EMode=Indirect))");
71
72 declareProperty("OrderAxis", true,
73 "Whether or not to sort the resulting"
74 " spectrum axis.");
75}
76
78 // Get the input workspace.
79 API::MatrixWorkspace_sptr inputWS = getProperty("InputWorkspace");
80 // Assign value to the member variable storing the number of histograms.
81 const size_t nHist = inputWS->getNumberHistograms();
82
83 // The unit to convert to.
84 const std::string unitTarget = getProperty("Target");
85
86 // Whether needs to be ordered
87 m_toOrder = getProperty("OrderAxis");
88 size_t nProgress = nHist;
89 if (m_toOrder) {
90 // we will need to loop twice, once to build the indexMap,
91 // once to copy over the spectra and set the output
92 nProgress *= 2;
93 } else {
94 m_axis.reserve(nHist);
95 }
96
97 Progress progress(this, 0.0, 1.0, nProgress);
98 // Call the functions to convert to the different forms of theta or Q.
99 if (unitTarget == "theta" || unitTarget == "Theta" || unitTarget == "signed_theta" || unitTarget == "SignedTheta" ||
100 unitTarget == "InPlaneTwoTheta" || unitTarget == "SignedInPlaneTwoTheta") {
101 createThetaMap(progress, unitTarget, inputWS);
102 } else if (unitTarget == "ElasticQ" || unitTarget == "ElasticQSquared" || unitTarget == "ElasticDSpacing") {
103 createElasticQMap(progress, unitTarget, inputWS);
104 }
105
106 // Create an output workspace and set the property for it.
107 MatrixWorkspace_sptr outputWS = createOutputWorkspace(progress, unitTarget, inputWS);
108 setProperty("OutputWorkspace", outputWS);
109}
110
116void ConvertSpectrumAxis2::createThetaMap(API::Progress &progress, const std::string &targetUnit,
117 API::MatrixWorkspace_sptr &inputWS) {
118 // Not sure about default, previously there was a call to a null function?
119 enum thetaTypes { theta, signedTheta, inPlaneTheta, signedInPlaneTheta };
120 thetaTypes thetaType = theta;
121 if (targetUnit == "signed_theta" || targetUnit == "SignedTheta") {
122 thetaType = signedTheta;
123 } else if (targetUnit == "theta" || targetUnit == "Theta") {
124 thetaType = theta;
125 } else if (targetUnit == "InPlaneTwoTheta") {
126 thetaType = inPlaneTheta;
127 } else if (targetUnit == "SignedInPlaneTwoTheta") {
128 thetaType = signedInPlaneTheta;
129 }
130 bool warningGiven = false;
131
132 const auto &spectrumInfo = inputWS->spectrumInfo();
133 for (size_t i = 0; i < spectrumInfo.size(); ++i) {
134 if (!spectrumInfo.hasDetectors(i)) {
135 if (!warningGiven)
136 g_log.warning("The instrument definition is incomplete - spectra "
137 "dropped from output");
138 warningGiven = true;
139 continue;
140 }
141 if (!spectrumInfo.isMonitor(i)) {
142 switch (thetaType) {
143 case signedTheta:
144 emplaceIndexMap(spectrumInfo.signedTwoTheta(i) * rad2deg, i);
145 break;
146 case theta:
147 emplaceIndexMap(spectrumInfo.twoTheta(i) * rad2deg, i);
148 break;
149 case inPlaneTheta:
150 emplaceIndexMap(inPlaneTwoTheta(i, inputWS) * rad2deg, i);
151 break;
152 case signedInPlaneTheta:
154 break;
155 }
156 } else {
157 emplaceIndexMap(0.0, i);
158 }
159
160 progress.report("Converting to theta...");
161 }
162}
163
172 const auto spectrumInfo = inputWS->spectrumInfo();
173 const auto refFrame = inputWS->getInstrument()->getReferenceFrame();
174 const V3D position = spectrumInfo.position(index) - spectrumInfo.samplePosition();
175
176 double angle =
177 std::atan2(std::abs(position[refFrame->pointingHorizontal()]), position[refFrame->pointingAlongBeam()]);
178 return angle;
179}
180
189 const auto spectrumInfo = inputWS->spectrumInfo();
190 const auto refFrame = inputWS->getInstrument()->getReferenceFrame();
191
192 const auto samplePos = spectrumInfo.samplePosition();
193 const auto beamLine = samplePos - spectrumInfo.sourcePosition();
194
195 if (beamLine.nullVector()) {
196 throw Kernel::Exception::InstrumentDefinitionError("Source and sample are at same position!");
197 }
198
199 const V3D sampleDetVec = spectrumInfo.position(index) - samplePos;
200
201 return std::atan2(sampleDetVec[refFrame->pointingHorizontal()], sampleDetVec[refFrame->pointingAlongBeam()]);
202}
203
209void ConvertSpectrumAxis2::createElasticQMap(API::Progress &progress, const std::string &targetUnit,
210 API::MatrixWorkspace_sptr &inputWS) {
211
212 const std::string emodeStr = getProperty("EMode");
213 int emode = 0;
214 if (emodeStr == "Direct")
215 emode = 1;
216 else if (emodeStr == "Indirect")
217 emode = 2;
218
219 const auto &spectrumInfo = inputWS->spectrumInfo();
220 const auto &detectorInfo = inputWS->detectorInfo();
221 const size_t nHist = spectrumInfo.size();
222 for (size_t i = 0; i < nHist; i++) {
223 double theta(0.0), efixed(0.0);
224 if (!spectrumInfo.isMonitor(i)) {
225 theta = 0.5 * spectrumInfo.twoTheta(i);
226 /*
227 * Two assumptions made in the following code.
228 * 1. Getting the detector index of the first detector in the spectrum
229 * definition is enough (this should be completely safe).
230 * 2. That the time index is not important (first element of pair only
231 * accessed). i.e we are not performing scanning. Step scanning is not
232 * supported at the time of writing.
233 */
234 const auto detectorIndex = spectrumInfo.spectrumDefinition(i)[0].first;
235 efixed = getEfixed(detectorIndex, detectorInfo, *inputWS,
236 emode); // get efixed
237 } else {
238 theta = DBL_MIN;
239 efixed = DBL_MIN;
240 }
241
242 // Convert to MomentumTransfer
243 double elasticQInAngstroms = Kernel::UnitConversion::convertToElasticQ(theta, efixed);
244
245 if (targetUnit == "ElasticQ") {
246 emplaceIndexMap(elasticQInAngstroms, i);
247 } else if (targetUnit == "ElasticQSquared") {
248 // The QSquared value.
249 double elasticQSquaredInAngstroms = elasticQInAngstroms * elasticQInAngstroms;
250
251 emplaceIndexMap(elasticQSquaredInAngstroms, i);
252 } else if (targetUnit == "ElasticDSpacing") {
253 double elasticDSpacing = 2 * M_PI / elasticQInAngstroms;
254 emplaceIndexMap(elasticDSpacing, i);
255 }
256
257 progress.report("Converting to " + targetUnit);
258 }
259}
260
269 API::MatrixWorkspace_sptr &inputWS) {
270
271 MatrixWorkspace_sptr outputWorkspace = nullptr;
272 std::unique_ptr<NumericAxis> newAxis = nullptr;
273 EventWorkspace_sptr eventWS = std::dynamic_pointer_cast<EventWorkspace>(inputWS);
274 if (m_toOrder) {
275 // Can not re-use the input one because the spectra are re-ordered.
276 const Histogram hist = eventWS ? Histogram(inputWS->binEdges(0)) : inputWS->histogram(0);
277 outputWorkspace = create<MatrixWorkspace>(*inputWS, m_indexMap.size(), hist);
278 std::vector<double> axis;
279 axis.reserve(m_indexMap.size());
280 std::transform(m_indexMap.begin(), m_indexMap.end(), std::back_inserter(axis),
281 [](const auto &it) { return it.first; });
282
283 newAxis = std::make_unique<NumericAxis>(std::move(axis));
284 } else {
285 // If there is no reordering we can simply clone.
286 outputWorkspace = inputWS->clone();
287 newAxis = std::make_unique<NumericAxis>(m_axis);
288 }
289
290 // Set the units of the axis.
291 if (targetUnit == "theta" || targetUnit == "Theta" || targetUnit == "signed_theta" || targetUnit == "SignedTheta" ||
292 targetUnit == "InPlaneTwoTheta" || targetUnit == "SignedInPlaneTwoTheta") {
293 newAxis->unit() = std::make_shared<Units::Degrees>();
294 } else if (targetUnit == "ElasticQ") {
295 newAxis->unit() = UnitFactory::Instance().create("MomentumTransfer");
296 } else if (targetUnit == "ElasticQSquared") {
297 newAxis->unit() = UnitFactory::Instance().create("QSquared");
298 } else if (targetUnit == "ElasticDSpacing") {
299 newAxis->unit() = UnitFactory::Instance().create("dSpacing");
300 }
301 outputWorkspace->replaceAxis(1, std::move(newAxis));
302 // Note that this is needed only for ordered case
303 if (m_toOrder) {
304 size_t currentIndex = 0;
305 std::multimap<double, size_t>::const_iterator it;
306 for (it = m_indexMap.begin(); it != m_indexMap.end(); ++it) {
307 // Copy over the data.
308 outputWorkspace->getSpectrum(currentIndex).copyDataFrom(inputWS->getSpectrum(it->second));
309 // We can keep the spectrum numbers etc.
310 outputWorkspace->getSpectrum(currentIndex).copyInfoFrom(inputWS->getSpectrum(it->second));
311 ++currentIndex;
312 progress.report("Setting output spectrum #" + std::to_string(currentIndex));
313 }
314 }
315
316 return outputWorkspace;
317}
318
320 const Mantid::API::MatrixWorkspace &inputWS, const int emode) const {
321 double efixed(0);
322 double efixedProp = getProperty("Efixed");
323 Mantid::detid_t detectorID = detectorInfo.detectorIDs()[detectorIndex];
324 if (efixedProp != EMPTY_DBL()) {
325 efixed = efixedProp;
326 g_log.debug() << "Detector: " << detectorID << " Efixed: " << efixed << "\n";
327 } else {
328 if (emode == 1) {
329 if (inputWS.run().hasProperty("Ei")) {
330 efixed = inputWS.run().getLogAsSingleValue("Ei");
331 } else {
332 throw std::invalid_argument("Could not retrieve Efixed from the "
333 "workspace. Please provide a value.");
334 }
335 } else if (emode == 2) {
336
337 const auto &detectorSingle = detectorInfo.detector(detectorIndex);
338
339 std::vector<double> efixedVec = detectorSingle.getNumberParameter("Efixed");
340
341 if (!efixedVec.empty()) {
342 efixed = efixedVec.at(0);
343 g_log.debug() << "Detector: " << detectorID << " EFixed: " << efixed << "\n";
344 } else {
345 g_log.warning() << "Efixed could not be found for detector " << detectorID << ", please provide a value\n";
346 throw std::invalid_argument("Could not retrieve Efixed from the "
347 "detector. Please provide a value.");
348 }
349 }
350 }
351 return efixed;
352}
353
358void ConvertSpectrumAxis2::emplaceIndexMap(double value, size_t wsIndex) {
359 if (m_toOrder) {
360 m_indexMap.emplace(value, wsIndex);
361 } else {
362 m_axis.emplace_back(value);
363 }
364}
365
366} // namespace Mantid::Algorithms
#define DECLARE_ALGORITHM(classname)
Definition Algorithm.h:538
constexpr double rad2deg
double value
The value of the point.
Definition FitMW.cpp:51
double position
Definition GetAllEi.cpp:154
std::map< DeltaEMode::Type, std::string > index
IntArray detectorIndex
void declareProperty(std::unique_ptr< Kernel::Property > p, const std::string &doc="") override
Add a property to the list of managed properties.
TypedValue getProperty(const std::string &name) const override
Get the value of a property.
Kernel::Logger & g_log
Definition Algorithm.h:422
void progress(double p, const std::string &msg="", double estimatedTime=0.0, int progressPrecision=0)
Sends ProgressNotification.
const Run & run() const
Run details object access.
A validator which checks that a workspace has a valid instrument.
bool hasProperty(const std::string &name) const
Does the property exist on the object.
double getLogAsSingleValue(const std::string &name, Kernel::Math::StatisticType statistic=Kernel::Math::Mean) const
Definition LogManager.h:161
Base MatrixWorkspace Abstract Class.
Helper class for reporting progress from algorithms.
Definition Progress.h:25
A validator which checks whether the input workspace has the Spectra number in the axis.
A property class for workspaces.
std::vector< double > m_axis
Vector of axis in case ordering is not asked.
double getEfixed(const size_t detectorIndex, const Mantid::Geometry::DetectorInfo &detectorInfo, const API::MatrixWorkspace &inputWS, const int emode) const
Getting Efixed.
bool m_toOrder
Flag whether ordering is needed.
double signedInPlaneTwoTheta(const size_t index, const API::MatrixWorkspace_sptr &inputWS) const
Compute signed in plane two theta.
void createElasticQMap(API::Progress &progress, const std::string &targetUnit, API::MatrixWorkspace_sptr &inputWS)
Converting to Q and QSquared.
double inPlaneTwoTheta(const size_t index, const API::MatrixWorkspace_sptr &inputWS) const
Compute inPlaneTwoTheta.
void emplaceIndexMap(double value, size_t wsIndex)
Emplaces to value and the index pair into the map.
API::MatrixWorkspace_sptr createOutputWorkspace(API::Progress &progress, const std::string &targetUnit, API::MatrixWorkspace_sptr &inputWS)
Creates an output workspace.
std::multimap< double, size_t > m_indexMap
Map to which the conversion to the unit is stored.
void createThetaMap(API::Progress &progress, const std::string &targetUnit, API::MatrixWorkspace_sptr &inputWS)
Converting to theta.
void init() override
Initialisation code.
Geometry::DetectorInfo is an intermediate step towards a DetectorInfo that is part of Instrument-2....
const std::vector< detid_t > & detectorIDs() const
Returns a sorted vector of all detector IDs.
const Geometry::IDetector & detector(const size_t index) const
Return a const reference to the detector with given index.
virtual std::vector< double > getNumberParameter(const std::string &pname, bool recursive=true) const =0
Get a parameter defined as a double.
Exception for errors associated with the instrument definition.
Definition Exception.h:220
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:145
void warning(const std::string &msg)
Logs at warning level.
Definition Logger.cpp:117
static double convertToElasticQ(const double theta, const double efixed)
Convert to ElasticQ from Energy.
Class for 3D vectors.
Definition V3D.h:34
std::shared_ptr< MatrixWorkspace > MatrixWorkspace_sptr
shared pointer to the matrix workspace base class
std::shared_ptr< EventWorkspace > EventWorkspace_sptr
shared pointer to the EventWorkspace class
constexpr double rad2deg
Radians to degrees conversion factor.
Definition AngleUnits.h:23
int32_t detid_t
Typedef for a detector ID.
constexpr double EMPTY_DBL() noexcept
Returns what we consider an "empty" double within a property.
Definition EmptyValues.h:42
std::string to_string(const wide_integer< Bits, Signed > &n)
@ Input
An input workspace.
Definition Property.h:53
@ Output
An output workspace.
Definition Property.h:54