Mantid
Loading...
Searching...
No Matches
PolarizationCorrectionsHelpers.h
Go to the documentation of this file.
1// Mantid Repository : https://github.com/mantidproject/mantid
2//
3// Copyright © 2024 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 +
7
8#pragma once
9
13#include "MantidAlgorithms/DllConfig.h"
15#include <Eigen/Dense>
16#include <algorithm>
17#include <array>
18#include <cmath>
19#include <concepts>
20#include <optional>
21#include <tuple>
22#include <type_traits>
23#include <unsupported/Eigen/AutoDiff>
24#include <utility>
25
26namespace Mantid::Algorithms {
27namespace PolarizationCorrectionsHelpers {
29 const std::string &spinStateOrder,
30 const std::string &targetSpinState);
31} // namespace PolarizationCorrectionsHelpers
32
33namespace FlipperConfigurations {
34static const std::string OFF_ON = "01";
35static const std::string ON_OFF = "10";
36static const std::string OFF_OFF = "00";
37static const std::string ON_ON = "11";
38static const std::string OFF = "0";
39static const std::string ON = "1";
40} // namespace FlipperConfigurations
41
42namespace SpinStateConfigurationsFredrikze {
43static const std::string PARA_ANTI = "pa";
44static const std::string ANTI_PARA = "ap";
45static const std::string PARA_PARA = "pp";
46static const std::string ANTI_ANTI = "aa";
47static const std::string PARA = "p";
48static const std::string ANTI = "a";
49} // namespace SpinStateConfigurationsFredrikze
50
51namespace SpinStateConfigurationsWildes {
52static const std::string MINUS_PLUS = "-+";
53static const std::string PLUS_MINUS = "+-";
54static const std::string MINUS_MINUS = "--";
55static const std::string PLUS_PLUS = "++";
56static const std::string MINUS = "-";
57static const std::string PLUS = "+";
58} // namespace SpinStateConfigurationsWildes
59
60namespace SpinStatesORSO {
61/*
62 * Polarization constants and helper methods to support the Reflectometry ORSO file format
63 */
64static const std::string PP = "pp";
65static const std::string PM = "pm";
66static const std::string MP = "mp";
67static const std::string MM = "mm";
68static const std::string PO = "po";
69static const std::string MO = "mo";
70
71static const std::string LOG_NAME = "spin_state_ORSO";
72
73MANTID_ALGORITHMS_DLL const std::string &getORSONotationForSpinState(const std::string &spinState);
74MANTID_ALGORITHMS_DLL void addORSOLogForSpinState(const Mantid::API::MatrixWorkspace_sptr &ws,
75 const std::string &spinState);
76} // namespace SpinStatesORSO
77
78namespace Arithmetic {
79
80template <typename Provider, typename InputArray, typename CovarianceMatrix>
82 std::invocable<Provider, const InputArray &, const InputArray &> &&
83 std::convertible_to<std::invoke_result_t<Provider, const InputArray &, const InputArray &>, CovarianceMatrix>;
84
85template <size_t N> class ErrorTypeHelper {
86public:
87 using DerType = Eigen::Matrix<double, N, 1>;
89 using ADScalar = Eigen::AutoDiffScalar<DerType>;
90 using CovarianceMatrix = Eigen::Matrix<double, N, N>;
91};
92
93template <size_t IndependentVars, size_t DependentVars, typename... DependentFuncs> class CovarianceMatrixProvider {
94public:
95 static_assert(sizeof...(DependentFuncs) == DependentVars, "Number of dependent functions must match DependentVars");
96
97 static constexpr size_t TotalVars = IndependentVars + DependentVars;
100 using InputArray = typename Types::InputArray;
101 using CovarianceMatrix = typename Types::CovarianceMatrix;
102 using ADScalar = typename IndependentTypes::ADScalar;
103 using IndependentDerType = typename IndependentTypes::DerType;
104 using FunctionInput = std::array<ADScalar, TotalVars>;
105
106 explicit CovarianceMatrixProvider(DependentFuncs... dependentFuncs)
107 : m_dependentFuncs(std::move(dependentFuncs)...) {}
108
109 CovarianceMatrix operator()(const InputArray &inputValues, const InputArray &inputErrors) const {
110 CovarianceMatrix covariance = inputErrors.array().square().matrix().asDiagonal();
111 const auto functionInputs = makeFunctionInput(inputValues);
112 const auto derivatives = dependentDerivatives(functionInputs, std::make_index_sequence<DependentVars>{});
113
114 for (size_t dep = 0; dep < DependentVars; ++dep) {
115 const size_t depIndex = IndependentVars + dep;
116 for (size_t independent = 0; independent < IndependentVars; ++independent) {
117 const double covarianceWithIndependent =
118 derivatives[dep][independent] * inputErrors[independent] * inputErrors[independent];
119 covariance(independent, depIndex) = covarianceWithIndependent;
120 covariance(depIndex, independent) = covarianceWithIndependent;
121 }
122 }
123
124 // Preserve supplied dependent variances on the diagonal. Only dependent-dependent off-diagonal terms are inferred.
125 for (size_t depA = 0; depA < DependentVars; ++depA) {
126 const size_t depAIndex = IndependentVars + depA;
127 for (size_t depB = depA + 1; depB < DependentVars; ++depB) {
128 const size_t depBIndex = IndependentVars + depB;
129 double covarianceBetweenDependents = 0.0;
130 for (size_t independent = 0; independent < IndependentVars; ++independent) {
131 covarianceBetweenDependents += derivatives[depA][independent] * derivatives[depB][independent] *
132 inputErrors[independent] * inputErrors[independent];
133 }
134 covariance(depAIndex, depBIndex) = covarianceBetweenDependents;
135 covariance(depBIndex, depAIndex) = covarianceBetweenDependents;
136 }
137 }
138
139 return covariance;
140 }
141
142private:
143 FunctionInput makeFunctionInput(const InputArray &inputValues) const {
144 FunctionInput functionInputs;
145 for (size_t i = 0; i < IndependentVars; ++i) {
146 functionInputs[i] = ADScalar(inputValues[i], IndependentDerType::Unit(IndependentVars, i));
147 }
148 for (size_t i = IndependentVars; i < TotalVars; ++i) {
149 functionInputs[i] = ADScalar(inputValues[i], IndependentDerType::Zero());
150 }
151 return functionInputs;
152 }
153
154 template <typename Func>
155 ADScalar evaluateDependentFunction(Func const &func, const FunctionInput &functionInputs) const {
156 static_assert(std::invocable<Func, const FunctionInput &>,
157 "Dependent functions must accept the covariance provider input values");
158 return func(functionInputs);
159 }
160
161 template <size_t... Indices>
162 std::array<IndependentDerType, DependentVars> dependentDerivatives(const FunctionInput &functionInputs,
163 std::index_sequence<Indices...>) const {
164 return {evaluateDependentFunction(std::get<Indices>(m_dependentFuncs), functionInputs).derivatives()...};
165 }
166
167 std::tuple<DependentFuncs...> m_dependentFuncs;
168};
169
170// The first IndependentVars inputs are treated as independent. The following DependentVars inputs are treated as
171// quantities derived from the independent variables. If dependent variable d_a has derivatives J_ai = dd_a/dx_i, then
172// Cov(x_i, d_a) = J_ai Var(x_i) and Cov(d_a, d_b) = sum_i J_ai J_bi Var(x_i). Dependent functions must express each
173// dependency directly in terms of the independent variables; if one dependent quantity depends on another, inline that
174// dependency into the function. Use the input values' .value() when a fixed bin value is needed.
175template <size_t IndependentVars, size_t DependentVars, typename... DependentFuncs>
176auto makeCovarianceMatrixProvider(DependentFuncs &&...dependentFuncs) {
178 std::forward<DependentFuncs>(dependentFuncs)...);
179}
180
181template <size_t N, typename Func> class ErrorPropagation {
182public:
184 using DerType = Types::DerType;
185 using ADScalar = Types::ADScalar;
186 using InputArray = Types::InputArray;
187 using CovarianceMatrix = Types::CovarianceMatrix;
188 ErrorPropagation(Func func) : computeFunc(std::move(func)) {}
189
191 double value;
192 double error;
193 Eigen::Array<double, N, 1> derivatives;
194 };
195
196 AutoDevResult evaluate(const InputArray &values, const InputArray &errors) const {
198 }
199
200 AutoDevResult evaluateWithCovariance(const InputArray &values, const CovarianceMatrix &covariance) const {
201 std::array<ADScalar, N> x;
202 for (size_t i = 0; i < N; ++i) {
203 x[i] = ADScalar(values[i], DerType::Unit(N, i));
204 }
205 const ADScalar y = computeFunc(x);
206 const auto &derivatives = y.derivatives();
207 // First-order Taylor propagation with correlated inputs:
208 // Var(f) = J C J^T, where J is the row vector of derivatives df/dx_i and C is the covariance matrix.
209 const double variance = derivatives.dot(covariance * derivatives);
210 return {y.value(), std::sqrt(std::max(variance, 0.0)), derivatives};
211 }
212
213 template <std::same_as<API::MatrixWorkspace_sptr>... Ts>
214 API::MatrixWorkspace_sptr evaluateWorkspaces(const bool outputWorkspaceDistribution, Ts... args) const {
215 return evaluateWorkspacesImpl(outputWorkspaceDistribution, independentCovarianceMatrixProvider,
216 std::forward<Ts>(args)...);
217 }
218
219 template <std::same_as<API::MatrixWorkspace_sptr>... Ts>
221 return evaluateWorkspacesImpl(std::nullopt, independentCovarianceMatrixProvider, std::forward<Ts>(args)...);
222 }
223
224 template <typename Provider, std::same_as<API::MatrixWorkspace_sptr>... Ts>
226 API::MatrixWorkspace_sptr evaluateWorkspacesWithCovariance(const bool outputWorkspaceDistribution,
227 Provider covarianceMatrixProvider, Ts... args) const {
228 return evaluateWorkspacesImpl(outputWorkspaceDistribution, covarianceMatrixProvider, std::forward<Ts>(args)...);
229 }
230
231 template <typename Provider, std::same_as<API::MatrixWorkspace_sptr>... Ts>
233 API::MatrixWorkspace_sptr evaluateWorkspacesWithCovariance(Provider covarianceMatrixProvider, Ts... args) const {
234 return evaluateWorkspacesImpl(std::nullopt, covarianceMatrixProvider, std::forward<Ts>(args)...);
235 }
236
238 // Independent inputs have no off-diagonal covariance terms, so their covariance matrix is diagonal with
239 // C_ii = Var(x_i) = sigma_i^2.
240 return errors.array().square().matrix().asDiagonal();
241 }
242
243private:
245
249
250 template <typename Provider, std::same_as<API::MatrixWorkspace_sptr>... Ts>
252 API::MatrixWorkspace_sptr evaluateWorkspacesImpl(std::optional<bool> outputWorkspaceDistribution,
253 Provider covarianceMatrixProvider, Ts... args) const {
254 const auto firstWs = std::get<0>(std::forward_as_tuple(args...));
255 API::MatrixWorkspace_sptr outWs = firstWs->clone();
256
257 if (outWs->id() == "EventWorkspace") {
258 outWs = convertToWorkspace2D(outWs);
259 }
260
261 const size_t numSpec = outWs->getNumberHistograms();
262 const size_t specSize = outWs->blocksize();
263
264 // cppcheck-suppress unreadVariable
265 const bool isThreadSafe = Kernel::threadSafe((*args)..., *outWs);
266 // cppcheck-suppress unreadVariable
267 const bool specOverBins = numSpec > specSize;
268
269 PARALLEL_FOR_IF(isThreadSafe && specOverBins)
270 for (int64_t i = 0; i < static_cast<int64_t>(numSpec); i++) {
271 auto &yOut = outWs->mutableY(i);
272 auto &eOut = outWs->mutableE(i);
273
274 PARALLEL_FOR_IF(isThreadSafe && !specOverBins)
275 for (int64_t j = 0; j < static_cast<int64_t>(specSize); ++j) {
276 const InputArray values{args->y(i)[j]...};
277 const InputArray errors(args->e(i)[j]...);
278 const CovarianceMatrix covariance = covarianceMatrixProvider(values, errors);
279 const auto result = evaluateWithCovariance(values, covariance);
280 yOut[j] = result.value;
281 eOut[j] = result.error;
282 }
283 }
284
285 if (outputWorkspaceDistribution.has_value()) {
286 outWs->setDistribution(outputWorkspaceDistribution.value());
287 }
288 return outWs;
289 }
290
292 const std::string &algName) const {
293 auto conversionAlg = API::AlgorithmManager::Instance().create(algName);
294 conversionAlg->initialize();
295 conversionAlg->setChild(true);
296 conversionAlg->setProperty("InputWorkspace", workspace);
297 conversionAlg->setProperty("OutputWorkspace", workspace->getName());
298 conversionAlg->execute();
299 return conversionAlg->getProperty("OutputWorkspace");
300 }
301
306};
307
308template <size_t N, typename Func> auto makeErrorPropagation(Func &&func) {
309 return ErrorPropagation<N, std::decay_t<Func>>(std::forward<Func>(func));
310}
311
312} // namespace Arithmetic
313} // namespace Mantid::Algorithms
IPeaksWorkspace_sptr workspace
#define PARALLEL_FOR_IF(condition)
Empty definitions - to enable set your complier to enable openMP.
FunctionInput makeFunctionInput(const InputArray &inputValues) const
CovarianceMatrix operator()(const InputArray &inputValues, const InputArray &inputErrors) const
ADScalar evaluateDependentFunction(Func const &func, const FunctionInput &functionInputs) const
std::array< IndependentDerType, DependentVars > dependentDerivatives(const FunctionInput &functionInputs, std::index_sequence< Indices... >) const
API::MatrixWorkspace_sptr evaluateWorkspacesWithCovariance(const bool outputWorkspaceDistribution, Provider covarianceMatrixProvider, Ts... args) const
AutoDevResult evaluate(const InputArray &values, const InputArray &errors) const
API::MatrixWorkspace_sptr runWorkspaceConversionAlg(const API::MatrixWorkspace_sptr &workspace, const std::string &algName) const
AutoDevResult evaluateWithCovariance(const InputArray &values, const CovarianceMatrix &covariance) const
API::MatrixWorkspace_sptr evaluateWorkspaces(Ts... args) const
API::MatrixWorkspace_sptr evaluateWorkspacesWithCovariance(Provider covarianceMatrixProvider, Ts... args) const
API::MatrixWorkspace_sptr evaluateWorkspaces(const bool outputWorkspaceDistribution, Ts... args) const
API::MatrixWorkspace_sptr evaluateWorkspacesImpl(std::optional< bool > outputWorkspaceDistribution, Provider covarianceMatrixProvider, Ts... args) const
static CovarianceMatrix independentCovarianceMatrixProvider(const InputArray &, const InputArray &errors)
static CovarianceMatrix covarianceMatrixFromErrors(const InputArray &errors)
API::MatrixWorkspace_sptr convertToWorkspace2D(const API::MatrixWorkspace_sptr &workspace) const
std::shared_ptr< WorkspaceGroup > WorkspaceGroup_sptr
shared pointer to Mantid::API::WorkspaceGroup
std::shared_ptr< MatrixWorkspace > MatrixWorkspace_sptr
shared pointer to the matrix workspace base class
auto makeCovarianceMatrixProvider(DependentFuncs &&...dependentFuncs)
MANTID_ALGORITHMS_DLL API::MatrixWorkspace_sptr workspaceForSpinState(const API::WorkspaceGroup_sptr &group, const std::string &spinStateOrder, const std::string &targetSpinState)
Returns the workspace in the group associated with the given targetSpinState according to the order d...
MANTID_ALGORITHMS_DLL void addORSOLogForSpinState(const Mantid::API::MatrixWorkspace_sptr &ws, const std::string &spinState)
MANTID_ALGORITHMS_DLL const std::string & getORSONotationForSpinState(const std::string &spinState)
std::enable_if< std::is_pointer< Arg >::value, bool >::type threadSafe(Arg workspace)
Thread-safety check Checks the workspace to ensure it is suitable for multithreaded access.
STL namespace.