Mantid
Loading...
Searching...
No Matches
HeliumAnalyserEfficiencyTime.cpp
Go to the documentation of this file.
1// Mantid Repository : https://github.com/mantidproject/mantid
2//
3// Copyright © 2025 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
10
11#include "MantidAPI/Axis.h"
14#include "MantidAPI/Run.h"
15#include "MantidAPI/Workspace.h"
22#include "MantidKernel/Unit.h"
24#include "MantidTypes/Core/DateAndTime.h"
25
26#include <algorithm>
27#include <vector>
28
29namespace Mantid::Algorithms {
30// Register the algorithm into the algorithm factory
31DECLARE_ALGORITHM(HeliumAnalyserEfficiencyTime)
32
33using namespace Kernel;
34using namespace API;
35
36namespace PropertyNames {
37static const std::string INPUT_WORKSPACE{"InputWorkspace"};
38static const std::string REFERENCE_WORKSPACE{"ReferenceWorkspace"};
39static const std::string REFERENCE_TIMESTAMP{"ReferenceTimeStamp"};
40static const std::string OUTPUT_WORKSPACE{"OutputWorkspace"};
41static const std::string UNPOLARIZED_TRANSMISSION{"UnpolarizedTransmission"};
42static const std::string PXD{"PXD"};
43static const std::string PXD_ERROR{"PXDError"};
44static const std::string LIFETIME{"Lifetime"};
45static const std::string LIFETIME_ERROR{"LifetimeError"};
46static const std::string INITIAL_POL{"InitialPolarization"};
47static const std::string INITIAL_POL_ERROR{"InitialPolarizationError"};
48} // namespace PropertyNames
49
50namespace {
51static const std::string COLUMN_STAMPS = "midtime_stamp";
52static const std::string COLUMN_HOURS = "hours";
53static const std::string COLUMN_HOURS_ERROR = "hours_error";
54
55auto constexpr LAMBDA_CONVERSION_FACTOR = 0.0733;
56
57MatrixWorkspace_sptr createWorkspaceFromVectors(const HistogramData::HistogramX &x, const std::vector<double> &y,
58 const std::vector<double> &e) {
59 const auto ws = WorkspaceFactory::Instance().create("Workspace2D", 1, y.size() + 1, y.size());
60 ws->mutableX(0) = x;
61 ws->mutableY(0) = HistogramData::HistogramY(y);
62 ws->mutableE(0) = HistogramData::HistogramE(e);
63 ws->setDistribution(true);
64 ws->getAxis(0)->unit() = Mantid::Kernel::UnitFactory::Instance().create("Wavelength");
65 return ws;
66}
67
68bool hasUnit(const std::string &unitToCompareWith, const MatrixWorkspace_sptr &ws) {
69 if (ws->axes() == 0) {
70 return false;
71 }
72 const auto unit = ws->getAxis(0)->unit();
73 return (unit && unit->unitID() == unitToCompareWith);
74}
75
76bool hasTimeLogs(const MatrixWorkspace_sptr &ws) {
77 const Run &run = ws->run();
78 const bool hasStart = run.hasProperty("start_time") || run.hasProperty("run_start");
79 const bool hasEnd = run.hasProperty("end_time") || run.hasProperty("run_end");
80 return hasStart && hasEnd;
81}
82
83bool checkValidMatrixWorkspace(const Workspace_sptr &ws) {
84 const auto &ws_input = std::dynamic_pointer_cast<MatrixWorkspace>(ws);
85 return (ws_input && hasUnit("Wavelength", ws_input) && hasTimeLogs(ws_input));
86}
87
88std::string validateWorkspaceWithProperties(const Workspace_sptr &ws) {
89 if (!ws) {
90 return "Workspace has to be a valid workspace";
91 }
92
93 if (ws->isGroup()) {
94 const auto &groupItems = std::dynamic_pointer_cast<WorkspaceGroup>(ws)->getAllItems();
95 if (std::any_of(groupItems.cbegin(), groupItems.cend(),
96 [](const auto &childWs) { return !checkValidMatrixWorkspace(childWs); })) {
97 return "Workspace must have time logs and Wavelength units";
98 }
99 return "";
100 }
101
102 if (!checkValidMatrixWorkspace(ws)) {
103 return "Workspace must have time logs and Wavelength units";
104 }
105 return "";
106}
107} // namespace
108
110 auto wkpsValidator = std::make_shared<LambdaValidator<Workspace_sptr>>(validateWorkspaceWithProperties);
112 wkpsValidator),
113 "Scattering Workspace from which to extract the experiment timestamp");
116 "Reference workspace for which to extract the reference timestamp and wavelength range");
117 declareProperty(PropertyNames::REFERENCE_TIMESTAMP, "", std::make_shared<DateTimeValidator>(true),
118 "An ISO formatted date/time string specifying reference timestamp with respect to the scattering "
119 "workspace start time, e.g 2010-09-14T04:20:12",
121
122 const auto mustBePositive = std::make_shared<BoundedValidator<double>>();
123 mustBePositive->setLower(0);
124 declareProperty(PropertyNames::PXD, 12.0, mustBePositive, "Gas pressure in bar multiplied by cell length in metres");
125 declareProperty(PropertyNames::PXD_ERROR, 0.0, mustBePositive, "Error in pxd");
126 declareProperty(PropertyNames::INITIAL_POL, 0.9, mustBePositive, "Initial Polarization of He Gas in cell");
127 declareProperty(PropertyNames::INITIAL_POL_ERROR, 0.0, mustBePositive, "Error in initial polarization");
128 declareProperty(PropertyNames::LIFETIME, 45.0, mustBePositive,
129 "Lifetime of polarization decay of He gas in cell (in hours)");
130 declareProperty(PropertyNames::LIFETIME_ERROR, 0.0, mustBePositive, "Error in lifetime (in hours)");
132 "Helium analyzer efficiency as a function of wavelength");
135 "Unpolarized beam transmission as a function of wavelength");
136}
137
143std::map<std::string, std::string> HeliumAnalyserEfficiencyTime::validateInputs() {
144 std::map<std::string, std::string> errorList;
147 "Both ReferenceWorkspace and ReferenceTimeStamp properties are empty, at least one of the two has to be "
148 "supplied to execute the Algorithm";
149 }
150
151 return errorList;
152}
153
155 const auto outWs = calculateOutputs();
157 if (outWs.size() > 1) {
159 }
160}
161
166 if (inputWs->isGroup()) {
167 // We get first index of workspace (all members of the group should have the same wavelength range for polarized
168 // run)
169 const auto ws = std::dynamic_pointer_cast<WorkspaceGroup>(inputWs);
170 return std::dynamic_pointer_cast<MatrixWorkspace>(ws->getItem(0));
171 }
172 return std::dynamic_pointer_cast<MatrixWorkspace>(inputWs);
173}
174
175std::vector<MatrixWorkspace_sptr> HeliumAnalyserEfficiencyTime::calculateOutputs() {
176 const bool doUnpolTransmission = !isDefault(PropertyNames::UNPOLARIZED_TRANSMISSION);
177
178 const auto [time, timeError] = getTimeDifference();
179 const double pxd = LAMBDA_CONVERSION_FACTOR * static_cast<double>(getProperty(PropertyNames::PXD));
180 const double pxdError = LAMBDA_CONVERSION_FACTOR * static_cast<double>(getProperty(PropertyNames::PXD_ERROR));
181 const double lifetime = getProperty(PropertyNames::LIFETIME);
182 const double lifetimeError = getProperty(PropertyNames::LIFETIME_ERROR);
183 const double polIni = getProperty(PropertyNames::INITIAL_POL);
184 const double polIniError = getProperty(PropertyNames::INITIAL_POL_ERROR);
185
187 const auto &pointData = inputWs->histogram(0).points();
188 const auto &lambdas = pointData.rawData();
189 const auto &binBoundaries = inputWs->x(0);
190
191 auto efficiency = std::vector<double>(lambdas.size());
192 auto efficiencyErrors = std::vector<double>(lambdas.size());
193
194 std::vector<double> unpolTransmission, unpolTransmissionErrors;
195 if (doUnpolTransmission) {
196 unpolTransmission = std::vector<double>(lambdas.size());
197 unpolTransmissionErrors = std::vector<double>(lambdas.size());
198 }
199
200 for (size_t index = 0; index < lambdas.size(); index++) {
201 const auto lambdaError = binBoundaries[index + 1] - binBoundaries[index];
202 // Efficiency
203 const auto lambda = lambdas.at(index);
204 const auto mu = pxd * lambda;
205 const auto expTerm = std::exp(-time / lifetime);
206 const auto polHe = polIni * expTerm;
207
208 efficiency.at(index) = (1 + std::tanh(mu * polHe)) / 2;
209
210 // Calculate the errors for the efficiency, covariance between variables assumed to be zero
211 const auto muError = pxd * lambdaError + lambda * pxdError;
212 const auto polError = std::sqrt(std::pow(2, expTerm * polIniError) + std::pow(2, polHe * timeError / lifetime) +
213 std::pow(2, polHe * time * lifetimeError / (std::pow(2, lifetime))));
214
215 const auto commonTerm = 0.5 / std::pow(std::cosh(mu * polHe), 2);
216 efficiencyErrors.at(index) =
217 std::sqrt(std::pow(2, commonTerm) * (std::pow(2, mu * polError) + std::pow(2, polHe * muError)));
218
219 if (doUnpolTransmission) {
220 const auto expFactor = std::exp(-mu);
221 const auto coshFactor = std::cosh(mu * polHe);
222 const auto sinhFactor = std::sinh(mu * polHe);
223 unpolTransmission.at(index) = expFactor * coshFactor;
224 unpolTransmissionErrors.at(index) =
225 std::sqrt(std::pow(2, (expFactor * (polHe * sinhFactor - coshFactor) * muError)) +
226 std::pow(2, expFactor * sinhFactor * polError));
227 }
228 }
229
230 const auto outputVec = std::vector({std::move(efficiency), std::move(efficiencyErrors), std::move(unpolTransmission),
231 std::move(unpolTransmissionErrors)});
232 std::vector<MatrixWorkspace_sptr> wsOut;
233 for (size_t index = 0; index < outputVec.size(); index += 2) {
234 if (outputVec.at(index).size() > 0) {
235 wsOut.push_back(createWorkspaceFromVectors(binBoundaries, outputVec.at(index), outputVec.at(index + 1)));
236 }
237 }
238 return wsOut;
239}
240
242 // The reference workspace takes precedence in case a timestamp is also provided.
243 const auto timeDiff = createChildAlgorithm("TimeDifference");
244 timeDiff->initialize();
245 timeDiff->setProperty("InputWorkspaces", getPropertyValue(PropertyNames::INPUT_WORKSPACE));
246
247 std::string refTimeStamp;
249 timeDiff->setProperty("ReferenceWorkspace", getPropertyValue(PropertyNames::REFERENCE_WORKSPACE));
250 } else {
252 }
253
254 timeDiff->execute();
255
256 const ITableWorkspace_sptr table = timeDiff->getProperty(PropertyNames::OUTPUT_WORKSPACE);
257 // This will be always the last row on the table
258 const auto indexRow = table->rowCount() - 1;
259 const auto coltHoursErr = table->getColumn(COLUMN_HOURS_ERROR);
260 const auto tHoursErr = static_cast<double>(coltHoursErr->cell<float>(indexRow));
261
262 double tHours;
263 if (refTimeStamp.empty()) {
264 const auto coltHours = table->getColumn(COLUMN_HOURS);
265 tHours = static_cast<double>(coltHours->cell<float>(indexRow));
266 } else {
267 // Here we can only take the time stamp of the input workspace from the table
268 const auto colTimeStamps = table->getColumn(COLUMN_STAMPS);
269 const auto &expTimeStamp = colTimeStamps->cell<std::string>(indexRow);
270 const auto duration = Types::Core::DateAndTime(expTimeStamp) - Types::Core::DateAndTime(refTimeStamp);
271 tHours = static_cast<double>(duration.total_seconds() / 3600);
272 }
273 return std::make_pair(std::abs(tHours), tHoursErr);
274}
275
276} // namespace Mantid::Algorithms
#define DECLARE_ALGORITHM(classname)
Definition Algorithm.h:538
const std::vector< double > * lambda
std::map< DeltaEMode::Type, std::string > index
void declareProperty(std::unique_ptr< Kernel::Property > p, const std::string &doc="") override
Add a property to the list of managed properties.
std::string getPropertyValue(const std::string &name) const override
Get the value of a property as a string.
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.
bool isDefault(const std::string &name) const
A property class for workspaces.
std::map< std::string, std::string > validateInputs() override
Check that input params are valid.
void exec() override
Virtual method - must be overridden by concrete algorithm.
void init() override
Virtual method - must be overridden by concrete algorithm.
IPropertyManager * setProperty(const std::string &name, const T &value)
Templated method to set the value of a PropertyWithValue.
static T & Instance()
Return a reference to the Singleton instance, creating it if it does not already exist Creation is do...
std::shared_ptr< ITableWorkspace > ITableWorkspace_sptr
shared pointer to Mantid::API::ITableWorkspace
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
static const std::string OUTPUT_WORKSPACE
constexpr double LAMBDA_CONVERSION_FACTOR
@ Input
An input workspace.
Definition Property.h:53
@ Output
An output workspace.
Definition Property.h:54