Mantid
Loading...
Searching...
No Matches
PolarizationCorrectionFredrikze.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 +
8#include "MantidAPI/Axis.h"
10#include "MantidAPI/TextAxis.h"
14
20
21#include <algorithm>
22#include <memory>
23#include <unordered_set>
24
25using namespace Mantid::API;
26using namespace Mantid::Kernel;
27using namespace Mantid::Geometry;
28
29namespace Prop {
30static const std::string EFFICIENCIES{"Efficiencies"};
31static const std::string INPUT_WORKSPACE{"InputWorkspace"};
32static const std::string OUTPUT_WORKSPACE{"OutputWorkspace"};
33static const std::string INPUT_SPIN_STATES("InputSpinStates");
34static const std::string OUTPUT_SPIN_STATES("OutputSpinStates");
35static const std::string ADD_SPIN_STATE_LOG{"AddSpinStateToLog"};
36
37// Default order for PA anaysis
43// Default order for PNR analysis
47
48} // namespace Prop
49
50namespace {
51
52static const std::string CRHO_LABEL("Rho");
53
54static const std::string CPP_LABEL("Pp");
55
56static const std::string CALPHA_LABEL("Alpha");
57
58static const std::string CAP_LABEL("Ap");
59
60static const std::string PNR_LABEL("PNR");
61
62static const std::string PA_LABEL("PA");
63
64std::vector<std::string> modes() {
65 std::vector<std::string> modes;
66 modes.emplace_back(PA_LABEL);
67 modes.emplace_back(PNR_LABEL);
68 return modes;
69}
70
71Instrument_const_sptr fetchInstrument(WorkspaceGroup const *const groupWS) {
72 if (groupWS->size() == 0) {
73 throw std::invalid_argument("Input group workspace has no children.");
74 }
75
76 Workspace_sptr firstWS = groupWS->getItem(0);
77 MatrixWorkspace_sptr matrixWS = std::dynamic_pointer_cast<MatrixWorkspace>(firstWS);
78 return matrixWS->getInstrument();
79}
80
81bool isValidSpinState(const std::vector<std::string> &spinStates, const std::string &analysisMode) {
82 if (spinStates.empty()) {
83 return true;
84 }
85
86 if (analysisMode == PNR_LABEL) {
87 return spinStates.size() == 2 && ((spinStates[0] == Mantid::Algorithms::SpinStateConfigurationsFredrikze::PARA &&
91 }
92 if (analysisMode == PA_LABEL) {
93 const std::unordered_set<std::string> validStates = {
98 const std::unordered_set<std::string> uniqueStates(spinStates.cbegin(), spinStates.cend());
99 return uniqueStates == validStates;
100 }
101
102 return false;
103}
104
105bool isValidFredrikzeSpinStateOrder(const std::string &spinStatesStr) {
106 static const SpinStateValidator validator(std::unordered_set<int>{2, 4}, true, "p", "a", true);
107 return validator.isValid(spinStatesStr).empty();
108}
109
110std::string invalidSpinStateMessage(const std::string &propertyDescription, const std::string &spinStatesStr,
111 const std::string &analysisMode) {
112 return "Invalid " + propertyDescription + " spin state: " + spinStatesStr + " for " + analysisMode +
113 " The possible values are 'pp,pa,ap,aa' for PA, or 'p,a' for PNR, in any order";
114}
115
116std::string spinStateCountMessage(const std::string &propertyDescription, const std::string &spinStatesStr,
117 const std::string &analysisMode) {
118 auto const numberOfSpinStates = std::to_string(SpinStateHelpers::splitSpinStateString(spinStatesStr).size());
119 if (analysisMode == PNR_LABEL) {
120 return "The Fredrikze " + propertyDescription + " spin state order \"" + spinStatesStr + "\" contains " +
121 numberOfSpinStates +
122 " spin states, but PNR polarization correction expects 2 single spin states such as "
123 "\"p,a\".";
124 }
125 return "The Fredrikze " + propertyDescription + " spin state order \"" + spinStatesStr + "\" contains " +
126 numberOfSpinStates +
127 " spin states, but PA polarization correction expects 4 paired spin states such as \"pp,pa,ap,aa\".";
128}
129
130void validateSpinStateOrder(const std::string &propertyDescription, const std::string &spinStatesStr,
131 const std::string &analysisMode) {
132 const auto spinStates = SpinStateHelpers::splitSpinStateString(spinStatesStr);
133 if (!isValidFredrikzeSpinStateOrder(spinStatesStr)) {
134 throw std::invalid_argument(invalidSpinStateMessage(propertyDescription, spinStatesStr, analysisMode));
135 }
136 if (!isValidSpinState(spinStates, analysisMode)) {
137 throw std::invalid_argument(spinStateCountMessage(propertyDescription, spinStatesStr, analysisMode));
138 }
139}
140
141void validateInputWorkspace(WorkspaceGroup_sptr &ws, const std::string &inputStatesStr,
142 const std::string &outputStatesStr, const std::string &analysisMode) {
143 validateSpinStateOrder("input", inputStatesStr, analysisMode);
144 validateSpinStateOrder("output", outputStatesStr, analysisMode);
145
147 for (size_t i = 0; i < ws->size(); ++i) {
148
149 Workspace_sptr item = ws->getItem(i);
150
151 if (MatrixWorkspace_sptr ws2d = std::dynamic_pointer_cast<MatrixWorkspace>(item)) {
152
153 // X-units check
154 auto wsUnit = ws2d->getAxis(0)->unit();
155 auto expectedUnit = Units::Wavelength();
156 if (wsUnit->unitID() != expectedUnit.unitID()) {
157 throw std::invalid_argument("Input workspaces must have units of Wavelength");
158 }
159
160 // More detailed checks based on shape.
161 if (lastWS) {
162 if (lastWS->getNumberHistograms() != ws2d->getNumberHistograms()) {
163 throw std::invalid_argument("Not all workspaces in the "
164 "InputWorkspace WorkspaceGroup have the "
165 "same number of spectrum");
166 }
167 if (lastWS->blocksize() != ws2d->blocksize()) {
168 throw std::invalid_argument("Number of bins do not match between all "
169 "workspaces in the InputWorkspace "
170 "WorkspaceGroup");
171 }
172
173 auto &currentX = ws2d->x(0);
174 auto &lastX = lastWS->x(0);
175 auto xMatches = std::equal(lastX.cbegin(), lastX.cend(), currentX.cbegin());
176 if (!xMatches) {
177 throw std::invalid_argument("X-arrays do not match between all "
178 "workspaces in the InputWorkspace "
179 "WorkspaceGroup.");
180 }
181 }
182
183 lastWS = ws2d; // Cache the last workspace so we can use it for comparison
184 // purposes.
185
186 } else {
187 std::stringstream messageBuffer;
188 messageBuffer << "Item with index: " << i << "in the InputWorkspace is not a MatrixWorkspace";
189 throw std::invalid_argument(messageBuffer.str());
190 }
191 }
192}
193
200std::map<std::string, MatrixWorkspace_sptr> mapSpinStatesToWorkspaces(const WorkspaceGroup_sptr &inWS,
201 const std::vector<std::string> &spinStates) {
202 std::map<std::string, MatrixWorkspace_sptr> workspaceMap;
203
204 for (size_t i = 0; i < spinStates.size(); ++i) {
205 workspaceMap[spinStates[i]] = std::dynamic_pointer_cast<MatrixWorkspace>(inWS->getItem(i));
206 }
207
208 return workspaceMap;
209}
210
216void addSpinStateLogToWs(const Mantid::API::MatrixWorkspace_sptr &ws, const std::string &spinState) {
218}
219
226WorkspaceGroup_sptr mapWorkspacesToSpinStates(const std::map<std::string, MatrixWorkspace_sptr> &workspaces,
227 const std::vector<std::string> &spinStates, const bool addSpinStateLog) {
228
229 auto dataOut = std::make_shared<WorkspaceGroup>();
230
231 std::for_each(spinStates.begin(), spinStates.end(), [&](const auto &spinState) {
232 // Retrieve the workspace corresponding to the current spin state
233 auto workspace = workspaces.at(spinState);
234
235 dataOut->addWorkspace(workspace);
236
237 if (addSpinStateLog) {
238 // Log the spin state into the sample logs of the current workspace
239 addSpinStateLogToWs(workspace, spinState);
240 }
241 });
242
243 return dataOut;
244}
245
246using VecDouble = std::vector<double>;
247
248} // namespace
249
250namespace Mantid::Algorithms {
251
252// Register the algorithm into the AlgorithmFactory
253DECLARE_ALGORITHM(PolarizationCorrectionFredrikze)
254
255//----------------------------------------------------------------------------------------------
257const std::string PolarizationCorrectionFredrikze::name() const { return "PolarizationCorrectionFredrikze"; }
258
261
263const std::string PolarizationCorrectionFredrikze::category() const { return "Reflectometry"; }
264
269 return "Makes corrections for polarization efficiencies of the polarizer and "
270 "analyzer in a reflectometry neutron spectrometer.";
271}
272
280 auto multiply = this->createChildAlgorithm("Multiply");
281 auto rhsWS = std::make_shared<DataObjects::WorkspaceSingleValue>(rhs);
282 multiply->initialize();
283 multiply->setProperty("LHSWorkspace", lhsWS);
284 multiply->setProperty("RHSWorkspace", rhsWS);
285 multiply->execute();
287 return outWS;
288}
289
297 auto plus = this->createChildAlgorithm("Plus");
298 auto rhsWS = std::make_shared<DataObjects::WorkspaceSingleValue>(rhs);
299 plus->initialize();
300 plus->setProperty("LHSWorkspace", lhsWS);
301 plus->setProperty("RHSWorkspace", rhsWS);
302 plus->execute();
303 MatrixWorkspace_sptr outWS = plus->getProperty(Prop::OUTPUT_WORKSPACE);
304 return outWS;
305}
306
307//----------------------------------------------------------------------------------------------
313 "An input workspace to process.");
314
315 auto propOptions = modes();
316 declareProperty("PolarizationAnalysis", "PA", std::make_shared<StringListValidator>(propOptions),
317 "What Polarization mode will be used?\n"
318 "PNR: Polarized Neutron Reflectivity mode\n"
319 "PA: Full Polarization Analysis PNR-PA");
320
323 "A workspace containing the efficiency factors Pp, Ap, Rho and Alpha "
324 "as histograms");
325
328 "An output workspace.");
329
330 const auto spinStateValidator =
331 std::make_shared<SpinStateValidator>(std::unordered_set<int>{2, 4}, true, "p", "a", true);
332
333 declareProperty(Prop::INPUT_SPIN_STATES, "", spinStateValidator,
334 "The order of spin states in the input workspace group. The possible values are 'pp,pa,ap,aa' or "
335 "'p,a', in any order.");
336
337 declareProperty(Prop::OUTPUT_SPIN_STATES, "", spinStateValidator,
338 "The order of spin states in the output workspace group. The possible values are 'pp,pa,ap,aa' or "
339 "'p,a', in any order.");
340
343 "Whether to add the final spin state into the sample log of each child workspace in the output group.");
344}
345
347 const std::vector<std::string> &inputSpinStates,
348 const std::vector<std::string> &outputSpinStates,
349 const bool addSpinStateLog) {
350
351 // If the input spinStates vector is empty, use the default spinStates.
352 const auto &effectiveInputSpinStates = inputSpinStates.empty() ? Prop::defaultSpinStatesForPA : inputSpinStates;
353 // Map the input workspaces according to the specified input spinStates
354 auto inputMap = mapSpinStatesToWorkspaces(inWS, effectiveInputSpinStates);
355
360
361 Ipp->setTitle("Ipp");
362 Iaa->setTitle("Iaa");
363 Ipa->setTitle("Ipa");
364 Iap->setTitle("Iap");
365
366 const auto rho = this->getEfficiencyWorkspace(CRHO_LABEL);
367 const auto pp = this->getEfficiencyWorkspace(CPP_LABEL);
368 const auto alpha = this->getEfficiencyWorkspace(CALPHA_LABEL);
369 const auto ap = this->getEfficiencyWorkspace(CAP_LABEL);
370
371 const auto A0 = (Iaa * pp * ap) + (Ipa * ap * rho * pp) + (Iap * ap * alpha * pp) + (Ipp * ap * alpha * rho * pp);
372 const auto A1 = Iaa * pp;
373 const auto A2 = Iap * pp;
374 const auto A3 = Iaa * ap;
375 const auto A4 = Ipa * ap;
376 const auto A5 = Ipp * ap * alpha;
377 const auto A6 = Iap * ap * alpha;
378 const auto A7 = Ipp * pp * rho;
379 const auto A8 = Ipa * pp * rho;
380
381 const auto D = pp * ap * (rho + alpha + 1.0 + (rho * alpha));
382
383 const auto nIpp = (A0 - A1 + A2 - A3 + A4 + A5 - A6 + A7 - A8 + Ipp + Iaa - Ipa - Iap) / D;
384 const auto nIaa = (A0 + A1 - A2 + A3 - A4 - A5 + A6 - A7 + A8 + Ipp + Iaa - Ipa - Iap) / D;
385 const auto nIap = (A0 - A1 + A2 + A3 - A4 - A5 + A6 + A7 - A8 - Ipp - Iaa + Ipa + Iap) / D;
386 const auto nIpa = (A0 + A1 - A2 - A3 + A4 + A5 - A6 - A7 + A8 - Ipp - Iaa + Ipa + Iap) / D;
387
388 // Map the corrected workspaces to the specified output spinStates
389 std::map<std::string, MatrixWorkspace_sptr> outputMap;
394
395 // If the output spinStates vector is empty, use the default spinStates
396 const auto &effectiveOutputSpinStates = outputSpinStates.empty() ? Prop::defaultSpinStatesForPA : outputSpinStates;
397 auto dataOut = mapWorkspacesToSpinStates(outputMap, effectiveOutputSpinStates, addSpinStateLog);
398
399 size_t totalGroupEntries(dataOut->getNumberOfEntries());
400 for (size_t i = 1; i < totalGroupEntries; i++) {
401 auto alg = this->createChildAlgorithm("ReplaceSpecialValues");
402 alg->setProperty(Prop::INPUT_WORKSPACE, dataOut->getItem(i));
403 alg->setProperty(Prop::OUTPUT_WORKSPACE, "dataOut_" + std::to_string(i));
404 alg->setProperty("NaNValue", 0.0);
405 alg->setProperty("NaNError", 0.0);
406 alg->setProperty("InfinityValue", 0.0);
407 alg->setProperty("InfinityError", 0.0);
408 alg->execute();
409 }
410 // Preserve the history of the inside workspaces
411 nIpp->history().addHistory(Ipp->getHistory());
412 nIaa->history().addHistory(Iaa->getHistory());
413 nIpa->history().addHistory(Ipa->getHistory());
414 nIap->history().addHistory(Iap->getHistory());
415
416 return dataOut;
417}
418
420 const std::vector<std::string> &inputSpinStates,
421 const std::vector<std::string> &outputSpinStates,
422 const bool addSpinStateLog) {
423
424 // If the input spinStates vector is empty, use the default spinStates
425 const auto &effectiveInputOrder = inputSpinStates.empty() ? Prop::defaultSpinStatesForPNR : inputSpinStates;
426 // Map the input workspaces according to the specified input spinStates
427 auto inputMap = mapSpinStatesToWorkspaces(inWS, effectiveInputOrder);
428
431
432 const auto rho = this->getEfficiencyWorkspace(CRHO_LABEL);
433 const auto pp = this->getEfficiencyWorkspace(CPP_LABEL);
434
435 const auto D = pp * (rho + 1);
436
437 const auto nIp = (Ip * (rho * pp + 1.0) + Ia * (pp - 1.0)) / D;
438 const auto nIa = (Ip * (rho * pp - 1.0) + Ia * (pp + 1.0)) / D;
439
440 // Preserve the history of the inside workspaces
441 nIp->history().addHistory(Ip->getHistory());
442 nIa->history().addHistory(Ia->getHistory());
443
444 std::map<std::string, MatrixWorkspace_sptr> outputMap;
447
448 // If the output order vector is empty, use the default spinStates
449 const auto &effectiveOutputSpinStates = outputSpinStates.empty() ? Prop::defaultSpinStatesForPNR : outputSpinStates;
450 auto dataOut = mapWorkspacesToSpinStates(outputMap, effectiveOutputSpinStates, addSpinStateLog);
451
452 return dataOut;
453}
454
459std::shared_ptr<Mantid::API::MatrixWorkspace>
462 const auto &axis = dynamic_cast<TextAxis &>(*efficiencies->getAxis(1));
463 size_t index = axis.length();
464 for (size_t i = 0; i < axis.length(); ++i) {
465 if (axis.label(i) == label) {
466 index = i;
467 break;
468 }
469 }
470
471 if (index == axis.length()) {
472 // Check if we need to fetch polarization parameters from the instrument's
473 // parameters
474 static std::map<std::string, std::string> loadableProperties{
475 {CRHO_LABEL, "crho"}, {CPP_LABEL, "cPp"}, {CAP_LABEL, "cAp"}, {CALPHA_LABEL, "calpha"}};
477 Instrument_const_sptr instrument = fetchInstrument(inWS.get());
478 auto vals = instrument->getStringParameter(loadableProperties[label]);
479 if (vals.empty()) {
480 throw std::invalid_argument("Efficiency property not found: " + label);
481 }
482 auto extract = createChildAlgorithm("CreatePolarizationEfficiencies");
483 extract->initialize();
484 extract->setProperty(Prop::INPUT_WORKSPACE, efficiencies);
485 extract->setProperty(label, vals.front());
486 extract->execute();
487 MatrixWorkspace_sptr outWS = extract->getProperty(Prop::OUTPUT_WORKSPACE);
488 return outWS;
489 } else {
490 auto extract = createChildAlgorithm("ExtractSingleSpectrum");
491 extract->initialize();
492 extract->setProperty(Prop::INPUT_WORKSPACE, efficiencies);
493 extract->setProperty("WorkspaceIndex", static_cast<int>(index));
494 extract->execute();
495 MatrixWorkspace_sptr outWS = extract->getProperty(Prop::OUTPUT_WORKSPACE);
496 return outWS;
497 }
498}
499
500//----------------------------------------------------------------------------------------------
505 const std::string analysisMode = getProperty("PolarizationAnalysis");
506 const size_t nWorkspaces = inWS->size();
507
508 const std::string inputStatesStr = getProperty(Prop::INPUT_SPIN_STATES);
509 const std::string outputStatesStr = getProperty(Prop::OUTPUT_SPIN_STATES);
510 const bool addSpinStateLog = getProperty(Prop::ADD_SPIN_STATE_LOG);
511
512 const auto inputStates = Mantid::Kernel::SpinStateHelpers::splitSpinStateString(inputStatesStr);
513 const auto outputStates = Mantid::Kernel::SpinStateHelpers::splitSpinStateString(outputStatesStr);
514
515 validateInputWorkspace(inWS, inputStatesStr, outputStatesStr, analysisMode);
516
518 if (analysisMode == PA_LABEL) {
519 if (nWorkspaces != 4) {
520 throw std::invalid_argument("For PA analysis, input group must have 4 periods.");
521 }
522 g_log.notice("PA polarization correction");
523 outWS = execPA(inWS, inputStates, outputStates, addSpinStateLog);
524 } else if (analysisMode == PNR_LABEL) {
525 if (nWorkspaces != 2) {
526 throw std::invalid_argument("For PNR analysis, input group must have 2 periods.");
527 }
528 outWS = execPNR(inWS, inputStates, outputStates, addSpinStateLog);
529 g_log.notice("PNR polarization correction");
530 }
531
533}
534
535} // namespace Mantid::Algorithms
std::string name
Definition Run.cpp:60
#define DECLARE_ALGORITHM(classname)
Definition Algorithm.h:542
const std::vector< double > & rhs
std::map< DeltaEMode::Type, std::string > index
std::vector< double > VecDouble
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.
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.
Kernel::Logger & g_log
Definition Algorithm.h:423
Class to represent a text axis of a workspace.
Definition TextAxis.h:36
std::size_t length() const override
Get the length of the axis.
Definition TextAxis.h:41
Class to hold a set of workspaces.
Workspace_sptr getItem(const size_t index) const
Return the ith workspace.
size_t size() const
Return the size of the group, so it is more like a container.
A property class for workspaces.
PolarizationCorrectionFredrikze : Algorithm to perform polarisation corrections on multi-period group...
std::shared_ptr< Mantid::API::WorkspaceGroup > execPA(const std::shared_ptr< Mantid::API::WorkspaceGroup > &inWS, const std::vector< std::string > &inputSpinStates, const std::vector< std::string > &outputSpinStates, const bool addSpinStateLog)
std::shared_ptr< Mantid::API::MatrixWorkspace > add(const std::shared_ptr< Mantid::API::MatrixWorkspace > &lhsWS, const double &rhs)
Add a constant value to a workspace.
void init() override
Initialize the algorithm's properties.
std::shared_ptr< Mantid::API::WorkspaceGroup > execPNR(const std::shared_ptr< Mantid::API::WorkspaceGroup > &inWS, const std::vector< std::string > &inputSpinStates, const std::vector< std::string > &outputSpinStates, const bool addSpinStateLog)
std::shared_ptr< Mantid::API::MatrixWorkspace > getEfficiencyWorkspace(const std::string &label)
Extract a spectrum from the Efficiencies workspace as a 1D workspace.
int version() const override
Algorithm's version for identification.
std::shared_ptr< Mantid::API::MatrixWorkspace > multiply(const std::shared_ptr< Mantid::API::MatrixWorkspace > &lhsWS, const double &rhs)
Multiply a workspace by a constant value.
const std::string category() const override
Algorithm's category for identification.
IPropertyManager * setProperty(const std::string &name, const T &value)
Templated method to set the value of a PropertyWithValue.
void notice(const std::string &msg)
Logs at notice level.
Definition Logger.cpp:126
Wavelength in Angstrom.
Definition Unit.h:267
std::shared_ptr< WorkspaceGroup > WorkspaceGroup_sptr
shared pointer to Mantid::API::WorkspaceGroup
std::shared_ptr< Workspace > Workspace_sptr
shared pointer to Mantid::API::Workspace
std::shared_ptr< MatrixWorkspace > MatrixWorkspace_sptr
shared pointer to the matrix workspace base class
MANTID_ALGORITHMS_DLL void addORSOLogForSpinState(const Mantid::API::MatrixWorkspace_sptr &ws, const std::string &spinState)
std::shared_ptr< const Instrument > Instrument_const_sptr
Shared pointer to an const instrument object.
MANTID_KERNEL_DLL std::vector< std::string > splitSpinStateString(const std::string &spinStates)
String constants for algorithm's properties.
static const std::string ADD_SPIN_STATE_LOG
static const std::string INPUT_WORKSPACE
static const std::string OUTPUT_WORKSPACE
static const std::string EFFICIENCIES
static const std::string OUTPUT_SPIN_STATES("OutputSpinStates")
static const std::vector< std::string > defaultSpinStatesForPNR
static const std::vector< std::string > defaultSpinStatesForPA
static const std::string INPUT_SPIN_STATES("InputSpinStates")
STL namespace.
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