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
24using namespace Mantid::API;
25using namespace Mantid::Kernel;
26using namespace Mantid::Geometry;
27
28namespace Prop {
29static const std::string EFFICIENCIES{"Efficiencies"};
30static const std::string INPUT_WORKSPACE{"InputWorkspace"};
31static const std::string OUTPUT_WORKSPACE{"OutputWorkspace"};
32static const std::string INPUT_SPIN_STATES("InputSpinStates");
33static const std::string OUTPUT_SPIN_STATES("OutputSpinStates");
34static const std::string ADD_SPIN_STATE_LOG{"AddSpinStateToLog"};
35
36// Default order for PA anaysis
42// Default order for PNR analysis
46
47} // namespace Prop
48
49namespace {
50
51static const std::string CRHO_LABEL("Rho");
52
53static const std::string CPP_LABEL("Pp");
54
55static const std::string CALPHA_LABEL("Alpha");
56
57static const std::string CAP_LABEL("Ap");
58
59static const std::string PNR_LABEL("PNR");
60
61static const std::string PA_LABEL("PA");
62
63std::vector<std::string> modes() {
64 std::vector<std::string> modes;
65 modes.emplace_back(PA_LABEL);
66 modes.emplace_back(PNR_LABEL);
67 return modes;
68}
69
70Instrument_const_sptr fetchInstrument(WorkspaceGroup const *const groupWS) {
71 if (groupWS->size() == 0) {
72 throw std::invalid_argument("Input group workspace has no children.");
73 }
74
75 Workspace_sptr firstWS = groupWS->getItem(0);
76 MatrixWorkspace_sptr matrixWS = std::dynamic_pointer_cast<MatrixWorkspace>(firstWS);
77 return matrixWS->getInstrument();
78}
79
80// Helper function to check valid spin states
81bool isValidSpinState(const std::vector<std::string> &spinStates, const std::string &analysisMode) {
82 if (analysisMode == PNR_LABEL) {
83 // for PR, spinStates must be "p,a", "a,p", or empty vector
84 return (spinStates.size() == 2 &&
89 spinStates.empty();
90 } else if (analysisMode == PA_LABEL) {
91 // For PA, spinStates size must be 4 or empty
92 return (spinStates.size() == 4 || spinStates.empty());
93 }
94
95 return false;
96}
97
98void validateInputWorkspace(WorkspaceGroup_sptr &ws, const std::string &inputStatesStr,
99 const std::string &outputStatesStr, const std::string &analysisMode) {
101 for (size_t i = 0; i < ws->size(); ++i) {
102
103 Workspace_sptr item = ws->getItem(i);
104
105 if (MatrixWorkspace_sptr ws2d = std::dynamic_pointer_cast<MatrixWorkspace>(item)) {
106
107 // X-units check
108 auto wsUnit = ws2d->getAxis(0)->unit();
109 auto expectedUnit = Units::Wavelength();
110 if (wsUnit->unitID() != expectedUnit.unitID()) {
111 throw std::invalid_argument("Input workspaces must have units of Wavelength");
112 }
113
114 // More detailed checks based on shape.
115 if (lastWS) {
116 if (lastWS->getNumberHistograms() != ws2d->getNumberHistograms()) {
117 throw std::invalid_argument("Not all workspaces in the "
118 "InputWorkspace WorkspaceGroup have the "
119 "same number of spectrum");
120 }
121 if (lastWS->blocksize() != ws2d->blocksize()) {
122 throw std::invalid_argument("Number of bins do not match between all "
123 "workspaces in the InputWorkspace "
124 "WorkspaceGroup");
125 }
126
127 auto &currentX = ws2d->x(0);
128 auto &lastX = lastWS->x(0);
129 auto xMatches = std::equal(lastX.cbegin(), lastX.cend(), currentX.cbegin());
130 if (!xMatches) {
131 throw std::invalid_argument("X-arrays do not match between all "
132 "workspaces in the InputWorkspace "
133 "WorkspaceGroup.");
134 }
135
136 const auto inputStates = SpinStateHelpers::splitSpinStateString(inputStatesStr);
137 const auto outputStates = SpinStateHelpers::splitSpinStateString(outputStatesStr);
138
139 if (!isValidSpinState(inputStates, analysisMode)) {
140 throw std::invalid_argument("Invalid input spin state: " + inputStatesStr + " for " + analysisMode +
141 " The possible values are 'pp,pa,ap,aa' for PA, or 'p,a' for PNR, in any order");
142 }
143
144 if (!isValidSpinState(outputStates, analysisMode)) {
145 throw std::invalid_argument("Invalid output spin state: " + outputStatesStr + " for " + analysisMode +
146 " The possible values are 'pp,pa,ap,aa' for PA, or 'p,a' for PNR, in any order");
147 }
148 }
149
150 lastWS = ws2d; // Cache the last workspace so we can use it for comparison
151 // purposes.
152
153 } else {
154 std::stringstream messageBuffer;
155 messageBuffer << "Item with index: " << i << "in the InputWorkspace is not a MatrixWorkspace";
156 throw std::invalid_argument(messageBuffer.str());
157 }
158 }
159}
160
167std::map<std::string, MatrixWorkspace_sptr> mapSpinStatesToWorkspaces(const WorkspaceGroup_sptr &inWS,
168 const std::vector<std::string> &spinStates) {
169 std::map<std::string, MatrixWorkspace_sptr> workspaceMap;
170
171 for (size_t i = 0; i < spinStates.size(); ++i) {
172 workspaceMap[spinStates[i]] = std::dynamic_pointer_cast<MatrixWorkspace>(inWS->getItem(i));
173 }
174
175 return workspaceMap;
176}
177
183void addSpinStateLogToWs(const Mantid::API::MatrixWorkspace_sptr &ws, const std::string &spinState) {
185}
186
193WorkspaceGroup_sptr mapWorkspacesToSpinStates(const std::map<std::string, MatrixWorkspace_sptr> &workspaces,
194 const std::vector<std::string> &spinStates, const bool addSpinStateLog) {
195
196 auto dataOut = std::make_shared<WorkspaceGroup>();
197
198 std::for_each(spinStates.begin(), spinStates.end(), [&](const auto &spinState) {
199 // Retrieve the workspace corresponding to the current spin state
200 auto workspace = workspaces.at(spinState);
201
202 dataOut->addWorkspace(workspace);
203
204 if (addSpinStateLog) {
205 // Log the spin state into the sample logs of the current workspace
206 addSpinStateLogToWs(workspace, spinState);
207 }
208 });
209
210 return dataOut;
211}
212
213using VecDouble = std::vector<double>;
214
215} // namespace
216
217namespace Mantid::Algorithms {
218
219// Register the algorithm into the AlgorithmFactory
220DECLARE_ALGORITHM(PolarizationCorrectionFredrikze)
221
222//----------------------------------------------------------------------------------------------
224const std::string PolarizationCorrectionFredrikze::name() const { return "PolarizationCorrectionFredrikze"; }
225
228
230const std::string PolarizationCorrectionFredrikze::category() const { return "Reflectometry"; }
231
236 return "Makes corrections for polarization efficiencies of the polarizer and "
237 "analyzer in a reflectometry neutron spectrometer.";
238}
239
247 auto multiply = this->createChildAlgorithm("Multiply");
248 auto rhsWS = std::make_shared<DataObjects::WorkspaceSingleValue>(rhs);
249 multiply->initialize();
250 multiply->setProperty("LHSWorkspace", lhsWS);
251 multiply->setProperty("RHSWorkspace", rhsWS);
252 multiply->execute();
254 return outWS;
255}
256
264 auto plus = this->createChildAlgorithm("Plus");
265 auto rhsWS = std::make_shared<DataObjects::WorkspaceSingleValue>(rhs);
266 plus->initialize();
267 plus->setProperty("LHSWorkspace", lhsWS);
268 plus->setProperty("RHSWorkspace", rhsWS);
269 plus->execute();
270 MatrixWorkspace_sptr outWS = plus->getProperty(Prop::OUTPUT_WORKSPACE);
271 return outWS;
272}
273
274//----------------------------------------------------------------------------------------------
280 "An input workspace to process.");
281
282 auto propOptions = modes();
283 declareProperty("PolarizationAnalysis", "PA", std::make_shared<StringListValidator>(propOptions),
284 "What Polarization mode will be used?\n"
285 "PNR: Polarized Neutron Reflectivity mode\n"
286 "PA: Full Polarization Analysis PNR-PA");
287
290 "A workspace containing the efficiency factors Pp, Ap, Rho and Alpha "
291 "as histograms");
292
295 "An output workspace.");
296
297 const auto spinStateValidator =
298 std::make_shared<SpinStateValidator>(std::unordered_set<int>{2, 4}, true, "p", "a", true);
299
300 declareProperty(Prop::INPUT_SPIN_STATES, "", spinStateValidator,
301 "The order of spin states in the input workspace group. The possible values are 'pp,pa,ap,aa' or "
302 "'p,a', in any order.");
303
304 declareProperty(Prop::OUTPUT_SPIN_STATES, "", spinStateValidator,
305 "The order of spin states in the output workspace group. The possible values are 'pp,pa,ap,aa' or "
306 "'p,a', in any order.");
307
310 "Whether to add the final spin state into the sample log of each child workspace in the output group.");
311}
312
314 const std::vector<std::string> &inputSpinStates,
315 const std::vector<std::string> &outputSpinStates,
316 const bool addSpinStateLog) {
317
318 // If the input spinStates vector is empty, use the default spinStates.
319 const auto &effectiveInputSpinStates = inputSpinStates.empty() ? Prop::defaultSpinStatesForPA : inputSpinStates;
320 // Map the input workspaces according to the specified input spinStates
321 auto inputMap = mapSpinStatesToWorkspaces(inWS, effectiveInputSpinStates);
322
327
328 Ipp->setTitle("Ipp");
329 Iaa->setTitle("Iaa");
330 Ipa->setTitle("Ipa");
331 Iap->setTitle("Iap");
332
333 const auto rho = this->getEfficiencyWorkspace(CRHO_LABEL);
334 const auto pp = this->getEfficiencyWorkspace(CPP_LABEL);
335 const auto alpha = this->getEfficiencyWorkspace(CALPHA_LABEL);
336 const auto ap = this->getEfficiencyWorkspace(CAP_LABEL);
337
338 const auto A0 = (Iaa * pp * ap) + (Ipa * ap * rho * pp) + (Iap * ap * alpha * pp) + (Ipp * ap * alpha * rho * pp);
339 const auto A1 = Iaa * pp;
340 const auto A2 = Iap * pp;
341 const auto A3 = Iaa * ap;
342 const auto A4 = Ipa * ap;
343 const auto A5 = Ipp * ap * alpha;
344 const auto A6 = Iap * ap * alpha;
345 const auto A7 = Ipp * pp * rho;
346 const auto A8 = Ipa * pp * rho;
347
348 const auto D = pp * ap * (rho + alpha + 1.0 + (rho * alpha));
349
350 const auto nIpp = (A0 - A1 + A2 - A3 + A4 + A5 - A6 + A7 - A8 + Ipp + Iaa - Ipa - Iap) / D;
351 const auto nIaa = (A0 + A1 - A2 + A3 - A4 - A5 + A6 - A7 + A8 + Ipp + Iaa - Ipa - Iap) / D;
352 const auto nIap = (A0 - A1 + A2 + A3 - A4 - A5 + A6 + A7 - A8 - Ipp - Iaa + Ipa + Iap) / D;
353 const auto nIpa = (A0 + A1 - A2 - A3 + A4 + A5 - A6 - A7 + A8 - Ipp - Iaa + Ipa + Iap) / D;
354
355 // Map the corrected workspaces to the specified output spinStates
356 std::map<std::string, MatrixWorkspace_sptr> outputMap;
361
362 // If the output spinStates vector is empty, use the default spinStates
363 const auto &effectiveOutputSpinStates = outputSpinStates.empty() ? Prop::defaultSpinStatesForPA : outputSpinStates;
364 auto dataOut = mapWorkspacesToSpinStates(outputMap, effectiveOutputSpinStates, addSpinStateLog);
365
366 size_t totalGroupEntries(dataOut->getNumberOfEntries());
367 for (size_t i = 1; i < totalGroupEntries; i++) {
368 auto alg = this->createChildAlgorithm("ReplaceSpecialValues");
369 alg->setProperty(Prop::INPUT_WORKSPACE, dataOut->getItem(i));
370 alg->setProperty(Prop::OUTPUT_WORKSPACE, "dataOut_" + std::to_string(i));
371 alg->setProperty("NaNValue", 0.0);
372 alg->setProperty("NaNError", 0.0);
373 alg->setProperty("InfinityValue", 0.0);
374 alg->setProperty("InfinityError", 0.0);
375 alg->execute();
376 }
377 // Preserve the history of the inside workspaces
378 nIpp->history().addHistory(Ipp->getHistory());
379 nIaa->history().addHistory(Iaa->getHistory());
380 nIpa->history().addHistory(Ipa->getHistory());
381 nIap->history().addHistory(Iap->getHistory());
382
383 return dataOut;
384}
385
387 const std::vector<std::string> &inputSpinStates,
388 const std::vector<std::string> &outputSpinStates,
389 const bool addSpinStateLog) {
390
391 // If the input spinStates vector is empty, use the default spinStates
392 const auto &effectiveInputOrder = inputSpinStates.empty() ? Prop::defaultSpinStatesForPNR : inputSpinStates;
393 // Map the input workspaces according to the specified input spinStates
394 auto inputMap = mapSpinStatesToWorkspaces(inWS, effectiveInputOrder);
395
398
399 const auto rho = this->getEfficiencyWorkspace(CRHO_LABEL);
400 const auto pp = this->getEfficiencyWorkspace(CPP_LABEL);
401
402 const auto D = pp * (rho + 1);
403
404 const auto nIp = (Ip * (rho * pp + 1.0) + Ia * (pp - 1.0)) / D;
405 const auto nIa = (Ip * (rho * pp - 1.0) + Ia * (pp + 1.0)) / D;
406
407 // Preserve the history of the inside workspaces
408 nIp->history().addHistory(Ip->getHistory());
409 nIa->history().addHistory(Ia->getHistory());
410
411 std::map<std::string, MatrixWorkspace_sptr> outputMap;
414
415 // If the output order vector is empty, use the default spinStates
416 const auto &effectiveOutputSpinStates = outputSpinStates.empty() ? Prop::defaultSpinStatesForPNR : outputSpinStates;
417 auto dataOut = mapWorkspacesToSpinStates(outputMap, effectiveOutputSpinStates, addSpinStateLog);
418
419 return dataOut;
420}
421
426std::shared_ptr<Mantid::API::MatrixWorkspace>
429 const auto &axis = dynamic_cast<TextAxis &>(*efficiencies->getAxis(1));
430 size_t index = axis.length();
431 for (size_t i = 0; i < axis.length(); ++i) {
432 if (axis.label(i) == label) {
433 index = i;
434 break;
435 }
436 }
437
438 if (index == axis.length()) {
439 // Check if we need to fetch polarization parameters from the instrument's
440 // parameters
441 static std::map<std::string, std::string> loadableProperties{
442 {CRHO_LABEL, "crho"}, {CPP_LABEL, "cPp"}, {CAP_LABEL, "cAp"}, {CALPHA_LABEL, "calpha"}};
444 Instrument_const_sptr instrument = fetchInstrument(inWS.get());
445 auto vals = instrument->getStringParameter(loadableProperties[label]);
446 if (vals.empty()) {
447 throw std::invalid_argument("Efficiency property not found: " + label);
448 }
449 auto extract = createChildAlgorithm("CreatePolarizationEfficiencies");
450 extract->initialize();
451 extract->setProperty(Prop::INPUT_WORKSPACE, efficiencies);
452 extract->setProperty(label, vals.front());
453 extract->execute();
454 MatrixWorkspace_sptr outWS = extract->getProperty(Prop::OUTPUT_WORKSPACE);
455 return outWS;
456 } else {
457 auto extract = createChildAlgorithm("ExtractSingleSpectrum");
458 extract->initialize();
459 extract->setProperty(Prop::INPUT_WORKSPACE, efficiencies);
460 extract->setProperty("WorkspaceIndex", static_cast<int>(index));
461 extract->execute();
462 MatrixWorkspace_sptr outWS = extract->getProperty(Prop::OUTPUT_WORKSPACE);
463 return outWS;
464 }
465}
466
467//----------------------------------------------------------------------------------------------
472 const std::string analysisMode = getProperty("PolarizationAnalysis");
473 const size_t nWorkspaces = inWS->size();
474
475 const std::string inputStatesStr = getProperty(Prop::INPUT_SPIN_STATES);
476 const std::string outputStatesStr = getProperty(Prop::OUTPUT_SPIN_STATES);
477 const bool addSpinStateLog = getProperty(Prop::ADD_SPIN_STATE_LOG);
478
479 const auto inputStates = Mantid::Kernel::SpinStateHelpers::splitSpinStateString(inputStatesStr);
480 const auto outputStates = Mantid::Kernel::SpinStateHelpers::splitSpinStateString(outputStatesStr);
481
482 validateInputWorkspace(inWS, inputStatesStr, outputStatesStr, analysisMode);
483
485 if (analysisMode == PA_LABEL) {
486 if (nWorkspaces != 4) {
487 throw std::invalid_argument("For PA analysis, input group must have 4 periods.");
488 }
489 g_log.notice("PA polarization correction");
490 outWS = execPA(inWS, inputStates, outputStates, addSpinStateLog);
491 } else if (analysisMode == PNR_LABEL) {
492 if (nWorkspaces != 2) {
493 throw std::invalid_argument("For PNR analysis, input group must have 2 periods.");
494 }
495 outWS = execPNR(inWS, inputStates, outputStates, addSpinStateLog);
496 g_log.notice("PNR polarization correction");
497 }
498
500}
501
502} // namespace Mantid::Algorithms
std::string name
Definition Run.cpp:60
#define DECLARE_ALGORITHM(classname)
Definition Algorithm.h:538
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:422
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