Mantid
Loading...
Searching...
No Matches
GenerateFlatCellWorkspaceLOQ.cpp
Go to the documentation of this file.
1// Mantid Repository : https://github.com/mantidproject/mantid
2//
3// Copyright © 2026 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 +
14
15#include <fstream>
16#include <limits>
17#include <span>
18#include <sstream>
19
20namespace Mantid::Algorithms {
21
22// Register the algorithm into the AlgorithmFactory
23DECLARE_ALGORITHM(GenerateFlatCellWorkspaceLOQ)
24
25using namespace Kernel;
26using namespace API;
27using namespace Mantid::DataObjects;
28
29struct LoqMeta {
30 static constexpr int histograms() { return 17776; }
31 static constexpr int monitors() { return 8; }
32 static constexpr int LABIndexStart() { return 0; }
33 static constexpr int LABTotalBanks() { return 16384; }
34 static constexpr int HABTotalBanks() { return 1392; }
35 static constexpr int HABIndividualBanks() { return 348; };
36 static constexpr int HABIndexStart() { return 16384; };
37 static constexpr int HAB1IndexStop() { return 16732; };
38 static constexpr int HAB2IndexStop() { return 17080; };
39 static constexpr int HAB3IndexStop() { return 17428; };
40};
41
43 declareProperty(std::make_unique<WorkspaceProperty<EventWorkspace>>("InputWorkspace", "", Direction::Input),
44 "An input event workspace.");
45 declareProperty(std::make_unique<WorkspaceProperty<MatrixWorkspace>>("OutputWorkspace", "", Direction::Output),
46 "An output event workspace.");
47 declareProperty("CreateMaskWorkspace", true, "Determines if masked workspace needs to be created.");
48 declareProperty("LABThresholdMultiplier", 1.0,
49 "The parameter that is used to scale the standard deviation in order to set the masking threshold of "
50 "the low angle bank. The masking threshold is calculated by the equation "
51 "maskingThreshold = 1.0 + (LABThresholdMultiplier x standard deviation of the normalized LAB data);");
52 declareProperty("HABThresholdMultiplier", 0.5,
53 "The parameter that is used to scale the standard deviation in order to set the masking threshold of "
54 "the high angle bank. The masking threshold is calculated by the equation "
55 "maskingThreshold = 1.0 + (HABThresholdMultiplier x standard deviation of the normalized HAB data);");
56 declareProperty("ApplyMaskDirectlyToWorkspace", false, "Determines if mask is directly applied to workspace.");
57 declareProperty("OutputMaskFilePath", "",
58 "Path to the detector mask XML file. It must be provided for the algorithm to create the .xml file.");
59 declareProperty("OutputFlatCellFilePath", "",
60 "Path to the RKH file. It must be provided for the algorithm to create the .out file.");
61}
62
65double GenerateFlatCellWorkspaceLOQ::mean(std::span<const double> values) const {
66 return std::accumulate(values.begin(), values.end(), 0.0) / static_cast<double>(values.size());
67}
68
71double GenerateFlatCellWorkspaceLOQ::stddev(std::span<double> values) const {
72 double m = mean(values);
73 double accum = std::accumulate(values.begin(), values.end(), 0.0, [m](double total, double x) {
74 const double diff = x - m;
75 return total + diff * diff;
76 });
77 return std::sqrt(accum / static_cast<double>(values.size()));
78}
79
82void GenerateFlatCellWorkspaceLOQ::scale(std::span<double> values, double factor) const {
83 std::transform(values.begin(), values.end(), values.begin(), [factor](double x) { return x * factor; });
84}
85
86std::map<std::string, std::string> GenerateFlatCellWorkspaceLOQ::validateInputs() {
87 std::map<std::string, std::string> result;
88
89 EventWorkspace_sptr inputWS = getProperty("InputWorkspace");
90 const size_t nHist = inputWS->getNumberHistograms();
91 if (nHist != LoqMeta::histograms()) {
92 result["InputWorkspace"] = "Expected an EventWorkspace with exactly 17776 histograms "
93 "for SANS ISIS reduction of LOQ.";
94 }
95 return result;
96}
97
101
102 // Get the input WS
103 EventWorkspace_sptr inputWS = getProperty("InputWorkspace");
104
105 // Create output workspace
106 MatrixWorkspace_sptr outputWS = WorkspaceFactory::Instance().create(inputWS, LoqMeta::histograms(), 1, 1);
107 setProperty("OutputWorkspace", outputWS);
108
109 // Integrate spectrums in input events workspace
110 auto processedWS = integrateInput(inputWS);
111
112 // Extract the spectrums into a vector
113 auto values = extractIntegratedValues(processedWS);
114 std::span<double> valuesSpan(values);
115
116 // Normalize the banks
117 FlatCellStats stats = normalizeBanks(valuesSpan);
118
119 // Save the Y data into the output WS
120 for (size_t i = 0; i < LoqMeta::histograms(); ++i) {
121 auto &Y = outputWS->mutableY(i);
122 Y[0] = valuesSpan[i];
123 }
124
125 createAndSaveMaskWorkspace(outputWS, stats.normStdLAB, stats.normStdHAB);
126}
127
129GenerateFlatCellWorkspaceLOQ::normalizeBanks(std::span<double> values) const {
130 // Save the Low and High Angle Bank values into spans
131 std::span<double> LAB = values.subspan(LoqMeta::LABIndexStart(), LoqMeta::LABTotalBanks());
132 std::span<double> HAB = values.subspan(LoqMeta::HABIndexStart(), LoqMeta::HABTotalBanks());
133
134 // Normalize the values in the LAB and HAB vectors
135 scale(LAB, 1.0 / mean(LAB));
136 scale(HAB, 1.0 / mean(HAB));
137
138 // Calculate the normalized std of the LAB and HAB
139 const double normStdLAB = stddev(LAB);
140 const double normStdHAB = stddev(HAB);
141
142 // Save the individual High Angle Bank values into spans
143 std::span<double> HAB1 = values.subspan(LoqMeta::HABIndexStart(), LoqMeta::HABIndividualBanks());
144 std::span<double> HAB2 = values.subspan(LoqMeta::HAB1IndexStop(), LoqMeta::HABIndividualBanks());
145 std::span<double> HAB3 = values.subspan(LoqMeta::HAB2IndexStop(), LoqMeta::HABIndividualBanks());
146 std::span<double> HAB4 = values.subspan(LoqMeta::HAB3IndexStop(), LoqMeta::HABIndividualBanks());
147
148 // Rescale the values in the HAB vectors
149 scale(HAB1, 1.0 / mean(HAB1));
150 scale(HAB2, 1.0 / mean(HAB2));
151 scale(HAB3, 1.0 / mean(HAB3));
152 scale(HAB4, 1.0 / mean(HAB4));
153
154 return {normStdLAB, normStdHAB};
155}
156
158 const size_t nHist = ws->getNumberHistograms();
159 std::vector<double> values;
160 values.reserve(nHist);
161 for (size_t i = 0; i < nHist; ++i) {
162 const auto &Y = ws->readY(i);
163 values.insert(values.end(), Y.cbegin(), Y.cend());
164 }
165 return values;
166}
167
169 auto integration = createChildAlgorithm("Integration");
170 integration->initialize();
171 integration->setProperty("InputWorkspace", ws);
172 integration->setProperty("OutputWorkspace", "processedWS");
173 integration->setProperty("StartWorkspaceIndex", LoqMeta::LABIndexStart());
174 integration->setProperty("EndWorkspaceIndex", LoqMeta::histograms() - 1);
175 integration->execute();
176 return integration->getProperty("OutputWorkspace");
177}
178
180 double normStdHAB) {
181 MatrixWorkspace_sptr directMaskWS = ws->clone();
182
183 // Calculate the thresholds
184 double labThresholdMultiplier = getProperty("LABThresholdMultiplier");
185 double habThresholdMultiplier = getProperty("HABThresholdMultiplier");
186 const double maskingThresholdLAB = 1.0 + (labThresholdMultiplier * normStdLAB);
187 const double maskingThresholdHAB = 1.0 + (habThresholdMultiplier * normStdHAB);
188
189 // Mask the values of the low angle bank
190 auto maskDetectorsLAB = createChildAlgorithm("MaskDetectorsIf");
191 maskDetectorsLAB->initialize();
192 maskDetectorsLAB->setProperty("InputWorkspace", directMaskWS);
193 maskDetectorsLAB->setProperty("OutputWorkspace", directMaskWS);
194 maskDetectorsLAB->setProperty("StartWorkspaceIndex", LoqMeta::LABIndexStart());
195 maskDetectorsLAB->setProperty("EndWorkspaceIndex", LoqMeta::LABTotalBanks() - 1);
196 maskDetectorsLAB->setProperty("Operator", "GreaterEqual");
197 maskDetectorsLAB->setProperty("Value", maskingThresholdLAB);
198 maskDetectorsLAB->execute();
199
200 // Mask the values of the high angle bank
201 auto maskDetectorsHAB = createChildAlgorithm("MaskDetectorsIf");
202 maskDetectorsHAB->initialize();
203 maskDetectorsHAB->setProperty("InputWorkspace", directMaskWS);
204 maskDetectorsHAB->setProperty("OutputWorkspace", directMaskWS);
205 maskDetectorsHAB->setProperty("StartWorkspaceIndex", LoqMeta::HABIndexStart());
206 maskDetectorsHAB->setProperty("Operator", "GreaterEqual");
207 maskDetectorsHAB->setProperty("Value", maskingThresholdHAB);
208 maskDetectorsHAB->execute();
209
210 // Extract Mask
211 const std::string maskName = getPropertyValue("OutputWorkspace") + "_MASK";
212 auto extractMask = createChildAlgorithm("ExtractMask");
213 extractMask->initialize();
214 extractMask->setProperty("InputWorkspace", directMaskWS);
215 extractMask->setProperty("OutputWorkspace", maskName);
216 extractMask->execute();
217 MatrixWorkspace_sptr extractedmaskWS = extractMask->getProperty("OutputWorkspace");
218
219 // Save Mask
220 if (!isDefault("OutputMaskFilePath")) {
221 std::string outputMaskFilePath = getProperty("OutputMaskFilePath");
222 auto saveMask = createChildAlgorithm("SaveMask");
223 saveMask->initialize();
224 saveMask->setProperty("InputWorkspace", extractedmaskWS);
225 saveMask->setProperty("OutputFile", outputMaskFilePath);
226 saveMask->execute();
227 }
228
229 bool createMaskWorkspace = getProperty("CreateMaskWorkspace");
230 if (createMaskWorkspace) {
231 createDetectorMaskWorkspace(extractedmaskWS);
232 }
233 createFlatcellWorkspace(directMaskWS);
234 if (!isDefault("OutputFlatCellFilePath")) {
235 saveRKH();
236 }
237}
238
240 // Create the monitor workspace
241 MatrixWorkspace_sptr monitor_ws = WorkspaceFactory::Instance().create(ws, LoqMeta::monitors(), 1, 1);
242
243 // Prepend the mask with the monitors
244 auto conjoin = createChildAlgorithm("ConjoinWorkspaces");
245 conjoin->initialize();
246 conjoin->setProperty("InputWorkspace1", monitor_ws);
247 conjoin->setProperty("InputWorkspace2", ws);
248 conjoin->setProperty("CheckOverlapping", false);
249 conjoin->execute();
250 MatrixWorkspace_sptr conjoinedWS = conjoin->getProperty("InputWorkspace1");
251
252 // Save the conjoined mask workspace
253 const std::string maskName = getPropertyValue("OutputWorkspace") + "_MASK";
254 AnalysisDataService::Instance().addOrReplace(maskName, conjoinedWS);
255}
256
258 // Retrieve the properties
259 MatrixWorkspace_sptr outputWS = getProperty("OutputWorkspace");
260 bool applyMaskDirectlyToWorkspace = getProperty("ApplyMaskDirectlyToWorkspace");
261
262 // Create the monitor workspace
263 MatrixWorkspace_sptr monitor_ws = WorkspaceFactory::Instance().create(ws, LoqMeta::monitors(), 1, 1);
264
265 // Prepend the mask with the monitors
266 auto conjoin = createChildAlgorithm("ConjoinWorkspaces");
267 conjoin->initialize();
268 conjoin->setProperty("InputWorkspace1", monitor_ws);
269 if (applyMaskDirectlyToWorkspace) {
270 conjoin->setProperty("InputWorkspace2", ws);
271 } else {
272 conjoin->setProperty("InputWorkspace2", outputWS);
273 }
274 conjoin->setProperty("CheckOverlapping", false);
275 conjoin->execute();
276 MatrixWorkspace_sptr conjoinedWS = conjoin->getProperty("InputWorkspace1");
277
278 // Set the flatcell workspace as the OutputWorkspace
279 setProperty("OutputWorkspace", conjoinedWS);
280}
281
283 // Retrieve the properties
284 MatrixWorkspace_sptr outputWS = getProperty("OutputWorkspace");
285 std::string outputFlatCellFilePath = getProperty("OutputFlatCellFilePath");
286
287 // Save the RKH file
288 auto saveRKH = createChildAlgorithm("SaveRKH");
289 saveRKH->initialize();
290 saveRKH->setProperty("InputWorkspace", outputWS);
291 saveRKH->setProperty("Filename", outputFlatCellFilePath);
292 saveRKH->setProperty("Append", false);
293 saveRKH->execute();
294}
295
299
300} // namespace Mantid::Algorithms
#define DECLARE_ALGORITHM(classname)
Definition Algorithm.h:538
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
Method checking errors on ALL the inputs, before execution.
API::MatrixWorkspace_sptr integrateInput(const API::Workspace_sptr &ws)
std::vector< double > extractIntegratedValues(const API::MatrixWorkspace_sptr &ws) const
void createDetectorMaskWorkspace(const API::MatrixWorkspace_sptr &ws)
void createFlatcellWorkspace(const API::MatrixWorkspace_sptr &ws)
double stddev(std::span< double > values) const
Computes the standard deviation of the input span.
double mean(std::span< const double > values) const
Computes the mean of the input span.
FlatCellStats normalizeBanks(std::span< double > values) const
void scale(std::span< double > values, double factor) const
Scales each of the elements of the input vector.
void createAndSaveMaskWorkspace(const API::MatrixWorkspace_sptr &ws, double normStdLAB, double normStdHAB)
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< Workspace > Workspace_sptr
shared pointer to Mantid::API::Workspace
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
@ Input
An input workspace.
Definition Property.h:53
@ Output
An output workspace.
Definition Property.h:54