Mantid
Loading...
Searching...
No Matches
SANSSensitivityCorrection.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 +
13#include "MantidAPI/Run.h"
14#include "MantidAPI/TableRow.h"
23#include "MantidNexus/NexusFile.h"
24#include "Poco/File.h"
25#include "Poco/NumberFormatter.h"
26#include "Poco/Path.h"
27#include "Poco/String.h"
28
30
31// Register the algorithm into the AlgorithmFactory
32DECLARE_ALGORITHM(SANSSensitivityCorrection)
33
34using namespace Kernel;
35using namespace API;
36using namespace Geometry;
37using namespace DataObjects;
38
41 std::make_unique<WorkspaceProperty<>>("InputWorkspace", "", Direction::Input, PropertyMode::Optional));
42 const std::vector<std::string> fileExts{"_event.nxs", ".xml"};
43 declareProperty(std::make_unique<API::FileProperty>("Filename", "", API::FileProperty::Load, fileExts),
44 "Flood field or sensitivity file.");
45 declareProperty("UseSampleDC", true,
46 "If true, the dark current subtracted "
47 "from the sample data will also be "
48 "subtracted from the flood field.");
49 declareProperty(std::make_unique<API::FileProperty>("DarkCurrentFile", "", API::FileProperty::OptionalLoad, fileExts),
50 "The name of the input file to load as dark current.");
51
52 auto positiveDouble = std::make_shared<BoundedValidator<double>>();
53 positiveDouble->setLower(0);
54 declareProperty("MinEfficiency", EMPTY_DBL(), positiveDouble,
55 "Minimum efficiency for a pixel to be considered (default: no minimum).");
56 declareProperty("MaxEfficiency", EMPTY_DBL(), positiveDouble,
57 "Maximum efficiency for a pixel to be considered (default: no maximum).");
58
59 declareProperty("FloodTransmissionValue", EMPTY_DBL(), positiveDouble,
60 "Transmission value for the flood field material "
61 "(default: no transmission).");
62 declareProperty("FloodTransmissionError", 0.0, positiveDouble,
63 "Transmission error for the flood field material "
64 "(default: no transmission).");
65
66 declareProperty("BeamCenterX", EMPTY_DBL(),
67 "Beam position in X pixel coordinates (optional: otherwise "
68 "sample beam center is used)");
69 declareProperty("BeamCenterY", EMPTY_DBL(),
70 "Beam position in Y pixel coordinates (optional: otherwise "
71 "sample beam center is used)");
72 declareProperty("MaskedFullComponent", "", "Component Name to fully mask according to the IDF file.");
73 declareProperty(std::make_unique<ArrayProperty<int>>("MaskedEdges"),
74 "Number of pixels to mask on the edges: X-low, X-high, Y-low, Y-high");
75 declareProperty("MaskedComponent", "", "Component Name to mask the edges according to the IDF file.");
76
78 std::make_unique<WorkspaceProperty<>>("OutputWorkspace", "", Direction::Output, PropertyMode::Optional));
79 declareProperty("ReductionProperties", "__sans_reduction_properties");
80 declareProperty(std::make_unique<WorkspaceProperty<MatrixWorkspace>>("OutputSensitivityWorkspace", "",
82 declareProperty("OutputMessage", "", Direction::Output);
83}
84
89bool SANSSensitivityCorrection::fileCheck(const std::string &filePath) {
90 // Check the file extension
91 Poco::Path path(filePath);
92 const std::string extn = path.getExtension();
93 const std::string nxs("nxs");
94 const std::string nx5("nx5");
95 if (!(Poco::icompare(nxs, extn) == 0 || Poco::icompare(nx5, extn) == 0))
96 return false;
97
98 // open file and make sure it has entries
99 try {
100 Nexus::File handle(filePath, NXaccess::READ);
101 const auto entries = handle.getEntries();
102 if (entries.size() == 0) {
103 g_log.error("Error no entries found in " + filePath);
104 return false;
105 }
106 // ensure the file has an NXentry type named "mantid_workspace_1"
107 auto const iter = entries.find("mantid_workspace_1");
108 if (iter == entries.cend())
109 return false;
110 else
111 return iter->second == "NXentry";
112 } catch (Nexus::Exception const &) {
113 throw Exception::FileError("Unable to read data in File:", filePath);
114 }
115}
116
118 // Output log
119 m_output_message = "";
120
121 Progress progress(this, 0.0, 1.0, 10);
122
123 // Reduction property manager
124 const std::string reductionManagerName = getProperty("ReductionProperties");
125 std::shared_ptr<PropertyManager> reductionManager;
126 if (PropertyManagerDataService::Instance().doesExist(reductionManagerName)) {
127 reductionManager = PropertyManagerDataService::Instance().retrieve(reductionManagerName);
128 } else {
129 reductionManager = std::make_shared<PropertyManager>();
130 PropertyManagerDataService::Instance().addOrReplace(reductionManagerName, reductionManager);
131 }
132
133 if (!reductionManager->existsProperty("SensitivityAlgorithm")) {
134 auto algProp = std::make_unique<AlgorithmProperty>("SensitivityAlgorithm");
135 algProp->setValue(toString());
136 reductionManager->declareProperty(std::move(algProp));
137 }
138
139 progress.report("Loading sensitivity file");
140 const std::string fileName = getPropertyValue("Filename");
141
142 // Look for an entry for the dark current in the reduction table
143 Poco::Path path(fileName);
144 const std::string entryName = "Sensitivity" + path.getBaseName();
145 MatrixWorkspace_sptr floodWS;
146 std::string floodWSName = "__sensitivity_" + path.getBaseName();
147
148 if (reductionManager->existsProperty(entryName)) {
149 std::string wsName = reductionManager->getPropertyValue(entryName);
150 floodWS =
151 std::dynamic_pointer_cast<MatrixWorkspace>(AnalysisDataService::Instance().retrieveWS<MatrixWorkspace>(wsName));
152 m_output_message += " |Using " + wsName + "\n";
153 g_log.debug() << "SANSSensitivityCorrection :: Using sensitivity workspace: " << wsName << "\n";
154 } else {
155 // Load the flood field if we don't have it already
156 // First, try to determine whether we need to load data or a sensitivity
157 // workspace...
158 if (fileCheck(fileName)) {
159 g_log.debug() << "SANSSensitivityCorrection :: Loading sensitivity file: " << fileName << "\n";
160 auto loadAlg = createChildAlgorithm("Load", 0.1, 0.3);
161 loadAlg->setProperty("Filename", fileName);
162 loadAlg->executeAsChildAlg();
163 Workspace_sptr floodWS_ws = loadAlg->getProperty("OutputWorkspace");
164 floodWS = std::dynamic_pointer_cast<MatrixWorkspace>(floodWS_ws);
165
166 // Check that it's really a sensitivity file
167 if (!floodWS->run().hasProperty("is_sensitivity")) {
168 // Reset pointer
169 floodWS.reset();
170 g_log.error() << "A processed Mantid workspace was loaded but it "
171 "wasn't a sensitivity file!\n";
172 }
173 }
174
175 // ... if we don't, just load the data and process it
176 if (!floodWS) {
177 // Read in default beam center
178 double center_x = getProperty("BeamCenterX");
179 double center_y = getProperty("BeamCenterY");
180 if (isEmpty(center_x) || isEmpty(center_y)) {
181 if (reductionManager->existsProperty("LatestBeamCenterX") &&
182 reductionManager->existsProperty("LatestBeamCenterY")) {
183 center_x = reductionManager->getProperty("LatestBeamCenterX");
184 center_y = reductionManager->getProperty("LatestBeamCenterY");
185 m_output_message += " |Setting beam center to [" + Poco::NumberFormatter::format(center_x, 1) + ", " +
186 Poco::NumberFormatter::format(center_y, 1) + "]\n";
187 } else
188 m_output_message += " |No beam center provided: skipping!\n";
189 }
190
191 const std::string rawFloodWSName = "__flood_data_" + path.getBaseName();
192 MatrixWorkspace_sptr rawFloodWS;
193 if (!reductionManager->existsProperty("LoadAlgorithm")) {
194 auto loadAlg = createChildAlgorithm("Load", 0.1, 0.3);
195 loadAlg->setProperty("Filename", fileName);
196 if (!isEmpty(center_x) && loadAlg->existsProperty("BeamCenterX"))
197 loadAlg->setProperty("BeamCenterX", center_x);
198 if (!isEmpty(center_y) && loadAlg->existsProperty("BeamCenterY"))
199 loadAlg->setProperty("BeamCenterY", center_y);
200 loadAlg->setPropertyValue("OutputWorkspace", rawFloodWSName);
201 loadAlg->executeAsChildAlg();
202 Workspace_sptr tmpWS = loadAlg->getProperty("OutputWorkspace");
203 rawFloodWS = std::dynamic_pointer_cast<MatrixWorkspace>(tmpWS);
204 m_output_message += " | Loaded " + fileName + " (Load algorithm)\n";
205 } else {
206 // Get load algorithm as a string so that we can create a completely
207 // new proxy and ensure that we don't overwrite existing properties
208 IAlgorithm_sptr loadAlg0 = reductionManager->getProperty("LoadAlgorithm");
209 const std::string loadString = loadAlg0->toString();
210 auto loadAlg = Algorithm::fromString(loadString);
211 loadAlg->setChild(true);
212 loadAlg->setProperty("Filename", fileName);
213 loadAlg->setPropertyValue("OutputWorkspace", rawFloodWSName);
214 if (!isEmpty(center_x) && loadAlg->existsProperty("BeamCenterX"))
215 loadAlg->setProperty("BeamCenterX", center_x);
216 if (!isEmpty(center_y) && loadAlg->existsProperty("BeamCenterY"))
217 loadAlg->setProperty("BeamCenterY", center_y);
218 loadAlg->execute();
219 rawFloodWS = loadAlg->getProperty("OutputWorkspace");
220 m_output_message += " |Loaded " + fileName + "\n";
221 if (loadAlg->existsProperty("OutputMessage")) {
222 std::string msg = loadAlg->getPropertyValue("OutputMessage");
223 m_output_message += " |" + Poco::replace(msg, "\n", "\n |") + "\n";
224 }
225 }
226
227 // Check whether we just loaded a flood field data set, or the actual
228 // sensitivity
229 if (!rawFloodWS->run().hasProperty("is_sensitivity")) {
230 const std::string darkCurrentFile = getPropertyValue("DarkCurrentFile");
231
232 // Look for a dark current subtraction algorithm
233 std::string dark_result;
234 if (reductionManager->existsProperty("DarkCurrentAlgorithm")) {
235 IAlgorithm_sptr darkAlg = reductionManager->getProperty("DarkCurrentAlgorithm");
236 darkAlg->setChild(true);
237 darkAlg->setProperty("InputWorkspace", rawFloodWS);
238 darkAlg->setProperty("OutputWorkspace", rawFloodWS);
239
240 // Execute as-is if we use the sample dark current, otherwise check
241 // whether a dark current file was provided.
242 // Otherwise do nothing
243 if (getProperty("UseSampleDC")) {
244 darkAlg->execute();
245 if (darkAlg->existsProperty("OutputMessage"))
246 dark_result = darkAlg->getPropertyValue("OutputMessage");
247 } else if (!darkCurrentFile.empty()) {
248 darkAlg->setProperty("Filename", darkCurrentFile);
249 darkAlg->setProperty("PersistentCorrection", false);
250 darkAlg->execute();
251 if (darkAlg->existsProperty("OutputMessage"))
252 dark_result = darkAlg->getPropertyValue("OutputMessage");
253 else
254 dark_result = " Dark current subtracted\n";
255 }
256 } else if (!darkCurrentFile.empty()) {
257 // We need to subtract the dark current for the flood field but no
258 // dark
259 // current subtraction was set for the sample! Use the default dark
260 // current algorithm if we can find it.
261 if (reductionManager->existsProperty("DefaultDarkCurrentAlgorithm")) {
262 IAlgorithm_sptr darkAlg = reductionManager->getProperty("DefaultDarkCurrentAlgorithm");
263 darkAlg->setChild(true);
264 darkAlg->setProperty("InputWorkspace", rawFloodWS);
265 darkAlg->setProperty("OutputWorkspace", rawFloodWS);
266 darkAlg->setProperty("Filename", darkCurrentFile);
267 darkAlg->setProperty("PersistentCorrection", false);
268 darkAlg->execute();
269 if (darkAlg->existsProperty("OutputMessage"))
270 dark_result = darkAlg->getPropertyValue("OutputMessage");
271 } else {
272 // We are running out of options
273 g_log.error() << "No dark current algorithm provided to load [" << getPropertyValue("DarkCurrentFile")
274 << "]: skipped!\n";
275 dark_result = " No dark current algorithm provided: skipped\n";
276 }
277 }
278 m_output_message += " |" + Poco::replace(dark_result, "\n", "\n |") + "\n";
279
280 // Look for solid angle correction algorithm
281 if (reductionManager->existsProperty("SANSSolidAngleCorrection")) {
282 IAlgorithm_sptr solidAlg = reductionManager->getProperty("SANSSolidAngleCorrection");
283 solidAlg->setChild(true);
284 solidAlg->setProperty("InputWorkspace", rawFloodWS);
285 solidAlg->setProperty("OutputWorkspace", rawFloodWS);
286 solidAlg->execute();
287 std::string msg = "Solid angle correction applied\n";
288 if (solidAlg->existsProperty("OutputMessage"))
289 msg = solidAlg->getPropertyValue("OutputMessage");
290 m_output_message += " |" + Poco::replace(msg, "\n", "\n |") + "\n";
291 }
292
293 // Apply transmission correction as needed
294 double floodTransmissionValue = getProperty("FloodTransmissionValue");
295 double floodTransmissionError = getProperty("FloodTransmissionError");
296
297 if (!isEmpty(floodTransmissionValue)) {
298 g_log.debug() << "SANSSensitivityCorrection :: Applying transmission "
299 "to flood field\n";
300 auto transAlg = createChildAlgorithm("ApplyTransmissionCorrection");
301 transAlg->setProperty("InputWorkspace", rawFloodWS);
302 transAlg->setProperty("OutputWorkspace", rawFloodWS);
303 transAlg->setProperty("TransmissionValue", floodTransmissionValue);
304 transAlg->setProperty("TransmissionError", floodTransmissionError);
305 transAlg->setProperty("ThetaDependent", true);
306 transAlg->execute();
307 rawFloodWS = transAlg->getProperty("OutputWorkspace");
308 m_output_message += " |Applied transmission to flood field\n";
309 }
310
311 // Calculate detector sensitivity
312 auto effAlg = createChildAlgorithm("CalculateEfficiency", -1, -1, true, 1);
313 effAlg->setProperty("InputWorkspace", rawFloodWS);
314
315 const double minEff = getProperty("MinEfficiency");
316 const double maxEff = getProperty("MaxEfficiency");
317 const std::string maskFullComponent = getPropertyValue("MaskedFullComponent");
318 const std::string maskEdges = getPropertyValue("MaskedEdges");
319 const std::string maskComponent = getPropertyValue("MaskedComponent");
320
321 effAlg->setProperty("MinEfficiency", minEff);
322 effAlg->setProperty("MaxEfficiency", maxEff);
323 effAlg->setProperty("MaskedFullComponent", maskFullComponent);
324 effAlg->setProperty("MaskedEdges", maskEdges);
325 effAlg->setProperty("MaskedComponent", maskComponent);
326 effAlg->execute();
327 floodWS = effAlg->getProperty("OutputWorkspace");
328 } else {
329 floodWS = rawFloodWS;
330 }
331 // Patch as needed
332 if (reductionManager->existsProperty("SensitivityPatchAlgorithm")) {
333 IAlgorithm_sptr patchAlg = reductionManager->getProperty("SensitivityPatchAlgorithm");
334 patchAlg->setChild(true);
335 patchAlg->setProperty("Workspace", floodWS);
336 patchAlg->execute();
337 m_output_message += " |Sensitivity patch applied\n";
338 }
339
340 floodWS->mutableRun().addProperty("is_sensitivity", 1, "", true);
341 }
342 std::string floodWSOutputName = getPropertyValue("OutputSensitivityWorkspace");
343 if (floodWSOutputName.empty()) {
344 setPropertyValue("OutputSensitivityWorkspace", floodWSName);
345 AnalysisDataService::Instance().addOrReplace(floodWSName, floodWS);
346 reductionManager->declareProperty(
347 std::make_unique<WorkspaceProperty<>>(entryName, floodWSName, Direction::InOut));
348 reductionManager->setPropertyValue(entryName, floodWSName);
349 reductionManager->setProperty(entryName, floodWS);
350 }
351 setProperty("OutputSensitivityWorkspace", floodWS);
352 }
353
354 progress.report(3, "Loaded flood field");
355
356 // Check whether we need to apply the correction to a workspace
357 MatrixWorkspace_sptr inputWS = getProperty("InputWorkspace");
358 if (inputWS) {
359 // Divide sample data by detector efficiency
360 auto divideAlg = createChildAlgorithm("Divide", 0.6, 0.7);
361 divideAlg->setProperty("LHSWorkspace", inputWS);
362 divideAlg->setProperty("RHSWorkspace", floodWS);
363 divideAlg->executeAsChildAlg();
364 MatrixWorkspace_sptr outputWS = divideAlg->getProperty("OutputWorkspace");
365
366 // Copy over the efficiency's masked pixels to the reduced workspace
367 auto maskAlg = createChildAlgorithm("MaskDetectors", 0.75, 0.85);
368 maskAlg->setProperty("Workspace", outputWS);
369 maskAlg->setProperty("MaskedWorkspace", floodWS);
370 maskAlg->executeAsChildAlg();
371
372 setProperty("OutputWorkspace", outputWS);
373 }
374 setProperty("OutputMessage", "Sensitivity correction computed\n" + m_output_message);
375
376 progress.report("Performed sensitivity correction");
377}
378
379} // namespace Mantid::WorkflowAlgorithms
#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.
std::string toString() const override
Serialize an object to a string.
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.
static IAlgorithm_sptr fromString(const std::string &input)
De-serialize an object from a string.
Kernel::Logger & g_log
Definition Algorithm.h:422
void progress(double p, const std::string &msg="", double estimatedTime=0.0, int progressPrecision=0)
Sends ProgressNotification.
void setPropertyValue(const std::string &name, const std::string &value) override
Set the value of a property by string N.B.
static bool isEmpty(const NumT toCheck)
checks that the value was not set by users, uses the value in empty double/int.
@ OptionalLoad
to specify a file to read but the file doesn't have to exist
@ Load
allowed here which will be passed to the algorithm
Helper class for reporting progress from algorithms.
Definition Progress.h:25
A property class for workspaces.
Support for a property that holds an array of values.
Records the filename and the description of failure.
Definition Exception.h:98
IPropertyManager * setProperty(const std::string &name, const T &value)
Templated method to set the value of a PropertyWithValue.
void debug(const std::string &msg)
Logs at debug level.
Definition Logger.cpp:145
void error(const std::string &msg)
Logs at error level.
Definition Logger.cpp:108
Class that provides for a standard Nexus exception.
bool fileCheck(const std::string &filePath)
Check whether we have a processed file of not.
std::shared_ptr< IAlgorithm > IAlgorithm_sptr
shared pointer to Mantid::API::IAlgorithm
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
constexpr double EMPTY_DBL() noexcept
Returns what we consider an "empty" double within a property.
Definition EmptyValues.h:42
@ InOut
Both an input & output workspace.
Definition Property.h:55
@ Input
An input workspace.
Definition Property.h:53
@ Output
An output workspace.
Definition Property.h:54