Mantid
Loading...
Searching...
No Matches
CalculateEfficiency.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 +
20#include <vector>
21
22namespace Mantid::Algorithms {
23
24// Register the class into the algorithm factory
25DECLARE_ALGORITHM(CalculateEfficiency)
26
27using namespace Kernel;
28using namespace API;
29using namespace Geometry;
30using namespace DataObjects;
31
36 declareProperty(std::make_unique<WorkspaceProperty<>>("InputWorkspace", "", Direction::Input),
37 "The workspace containing the flood data");
38 declareProperty(std::make_unique<WorkspaceProperty<>>("OutputWorkspace", "", Direction::Output),
39 "The name of the workspace to be created as the output of the algorithm");
40
41 auto positiveDouble = std::make_shared<BoundedValidator<double>>();
42 positiveDouble->setLower(0);
43 declareProperty("MinEfficiency", EMPTY_DBL(), positiveDouble,
44 "Minimum efficiency for a pixel to be considered (default: no minimum).");
45 declareProperty("MaxEfficiency", EMPTY_DBL(), positiveDouble,
46 "Maximum efficiency for a pixel to be considered (default: no maximum).");
47 declareProperty("MaskedFullComponent", "", "Component Name to fully mask according to the IDF file.");
48 declareProperty(std::make_unique<ArrayProperty<int>>("MaskedEdges"),
49 "Number of pixels to mask on the edges: X-low, X-high, Y-low, Y-high");
50 declareProperty("MaskedComponent", "", "Component Name to mask the edges according to the IDF file.");
51}
52
57
58 // Minimum efficiency. Pixels with lower efficiency will be masked
59 double min_eff = getProperty("MinEfficiency");
60 // Maximum efficiency. Pixels with higher efficiency will be masked
61 double max_eff = getProperty("MaxEfficiency");
62
63 // Get the input workspace
64 MatrixWorkspace_sptr inputWS = getProperty("InputWorkspace");
65 MatrixWorkspace_sptr rebinnedWS; // = inputWS;
66
67 // BioSANS has 2 detectors and reduces one at the time: one is masked!
68 // We must use that masked detector
69 const std::string maskedFullComponent = getPropertyValue("MaskedFullComponent");
70 if (!maskedFullComponent.empty()) {
71 g_log.debug() << "CalculateEfficiency: Masking Full Component: " << maskedFullComponent << "\n";
72 maskComponent(*inputWS, maskedFullComponent);
73 }
74
75 // BioSANS has 2 detectors and the front masks the back!!!!
76 // We must mask the shaded part to calculate efficency
77 std::vector<int> maskedEdges = getProperty("MaskedEdges");
78 if (!maskedEdges.empty() && (maskedEdges[0] > 0 || maskedEdges[1] > 0 || maskedEdges[2] > 0 || maskedEdges[3] > 0)) {
79 g_log.debug() << "CalculateEfficiency: Masking edges length = " << maskedEdges.size() << ")"
80 << " of the component " << maskedFullComponent << "\n";
81 const std::string maskedComponent = getPropertyValue("MaskedComponent");
82 maskEdges(inputWS, maskedEdges[0], maskedEdges[1], maskedEdges[2], maskedEdges[3], maskedComponent);
83 }
84 // Now create the output workspace
85 MatrixWorkspace_sptr outputWS; // = getProperty("OutputWorkspace");
86
87 // DataObjects::EventWorkspace_const_sptr inputEventWS =
88 // std::dynamic_pointer_cast<const EventWorkspace>(inputWS);
89
90 // Sum up all the wavelength bins
91 auto childAlg = createChildAlgorithm("Integration", 0.0, 0.2);
92 childAlg->setProperty<MatrixWorkspace_sptr>("InputWorkspace", inputWS);
93 childAlg->executeAsChildAlg();
94 rebinnedWS = childAlg->getProperty("OutputWorkspace");
95
96 outputWS = WorkspaceFactory::Instance().create(rebinnedWS);
97 WorkspaceFactory::Instance().initializeFromParent(*inputWS, *outputWS, false);
98 for (int i = 0; i < static_cast<int>(rebinnedWS->getNumberHistograms()); i++) {
99 outputWS->setSharedX(i, rebinnedWS->sharedX(i));
100 }
101 setProperty("OutputWorkspace", outputWS);
102
103 double sum = 0.0;
104 double err = 0.0;
105 int npixels = 0;
106
107 // Loop over spectra and sum all the counts to get normalization
108 // Skip monitors and masked detectors
109 sumUnmaskedDetectors(rebinnedWS, sum, err, npixels);
110
111 // Normalize each detector pixel by the sum we just found to get the
112 // relative efficiency. If the minimum and maximum efficiencies are
113 // provided, the pixels falling outside this range will be marked
114 // as 'masked' in both the input and output workspace.
115 // We mask detectors in the input workspace so that we can resum the
116 // counts to find a new normalization factor that takes into account
117 // the newly masked detectors.
118 normalizeDetectors(rebinnedWS, outputWS, sum, err, npixels, min_eff, max_eff);
119
120 if (!isEmpty(min_eff) || !isEmpty(max_eff)) {
121 // Recompute the normalization, excluding the pixels that were outside
122 // the acceptable efficiency range.
123 sumUnmaskedDetectors(rebinnedWS, sum, err, npixels);
124
125 // Now that we have a normalization factor that excludes bad pixels,
126 // recompute the relative efficiency.
127 // We pass EMPTY_DBL() to avoid masking pixels that might end up high or low
128 // after the new normalization.
129 normalizeDetectors(rebinnedWS, outputWS, sum, err, npixels, EMPTY_DBL(), EMPTY_DBL());
130 }
131}
132
133/*
134 * Sum up all the unmasked detector pixels.
135 *
136 * @param rebinnedWS: workspace where all the wavelength bins have been grouped
137 *together
138 * @param sum: sum of all the unmasked detector pixels (counts)
139 * @param error: error on sum (counts)
140 * @param nPixels: number of unmasked detector pixels that contributed to sum
141 */
142void CalculateEfficiency::sumUnmaskedDetectors(const MatrixWorkspace_sptr &rebinnedWS, double &sum, double &error,
143 int &nPixels) {
144 // Number of spectra
145 const auto numberOfSpectra = static_cast<int>(rebinnedWS->getNumberHistograms());
146 sum = 0.0;
147 error = 0.0;
148 nPixels = 0;
149
150 const auto &spectrumInfo = rebinnedWS->spectrumInfo();
151 for (int i = 0; i < numberOfSpectra; i++) {
152 progress(0.2 + 0.2 * i / numberOfSpectra, "Computing sensitivity");
153 // Skip if we have a monitor or if the detector is masked.
154 if (spectrumInfo.isMonitor(i) || spectrumInfo.isMasked(i))
155 continue;
156
157 // Retrieve the spectrum into a vector
158 auto &YValues = rebinnedWS->y(i);
159 auto &YErrors = rebinnedWS->e(i);
160
161 sum += YValues[0];
162 error += YErrors[0] * YErrors[0];
163 nPixels++;
164 }
165
166 g_log.debug() << "sumUnmaskedDetectors: unmasked pixels = " << nPixels << " from a total of " << numberOfSpectra
167 << "\n";
168
169 error = std::sqrt(error);
170}
171
172/*
173 * Normalize each detector to produce the relative detector efficiency.
174 * Pixels that fall outside those efficiency limits will be masked in both
175 * the input and output workspaces.
176 *
177 * @param rebinnedWS: input workspace
178 * @param outputWS: output workspace containing the relative efficiency
179 * @param sum: sum of all the unmasked detector pixels (counts)
180 * @param error: error on sum (counts)
181 * @param nPixels: number of unmasked detector pixels that contributed to sum
182 */
183
185 const MatrixWorkspace_sptr &outputWS, double sum, double error,
186 int nPixels, double min_eff, double max_eff) {
187 // Number of spectra
188 const size_t numberOfSpectra = rebinnedWS->getNumberHistograms();
189
190 // Empty vector to store the pixels that outside the acceptable efficiency
191 // range
192 std::vector<size_t> dets_to_mask;
193
194 const auto &spectrumInfo = rebinnedWS->spectrumInfo();
195 for (size_t i = 0; i < numberOfSpectra; i++) {
196 const double currProgress = 0.4 + 0.2 * (static_cast<double>(i) / static_cast<double>(numberOfSpectra));
197 progress(currProgress, "Computing sensitivity");
198 // If this spectrum is masked, skip to the next one
199 if (spectrumInfo.isMasked(i))
200 continue;
201
202 // Retrieve the spectrum into a vector
203 auto &YIn = rebinnedWS->y(i);
204 auto &EIn = rebinnedWS->e(i);
205 auto &YOut = outputWS->mutableY(i);
206 auto &EOut = outputWS->mutableE(i);
207 // If this detector is a monitor, skip to the next one
208 if (spectrumInfo.isMonitor(i)) {
209 YOut[0] = 1.0;
210 EOut[0] = 0.0;
211 continue;
212 }
213
214 // Normalize counts to get relative efficiency
215 YOut[0] = nPixels / sum * YIn[0];
216 const double err_sum = YIn[0] / sum * error;
217 EOut[0] = nPixels / std::abs(sum) * std::sqrt(EIn[0] * EIn[0] + err_sum * err_sum);
218
219 // Mask this detector if the signal is outside the acceptable band
220 if (!isEmpty(min_eff) && YOut[0] < min_eff)
221 dets_to_mask.emplace_back(i);
222 if (!isEmpty(max_eff) && YOut[0] > max_eff)
223 dets_to_mask.emplace_back(i);
224 }
225
226 g_log.debug() << "normalizeDetectors: Masked pixels outside the acceptable "
227 "efficiency range ["
228 << min_eff << "," << max_eff << "] = " << dets_to_mask.size()
229 << " from a total of non masked = " << nPixels
230 << " (from a total number of spectra in the ws = " << numberOfSpectra << ")\n";
231
232 // If we identified pixels to be masked, mask them now
233 if (!dets_to_mask.empty()) {
234 // Mask detectors that were found to be outside the acceptable efficiency
235 // band
236 try {
237 auto mask = createChildAlgorithm("MaskDetectors", 0.8, 0.9);
238 // First we mask detectors in the output workspace
239 mask->setProperty<MatrixWorkspace_sptr>("Workspace", outputWS);
240 mask->setProperty<std::vector<size_t>>("WorkspaceIndexList", dets_to_mask);
241 mask->execute();
242
243 mask = createChildAlgorithm("MaskDetectors", 0.9, 1.0);
244 // Then we mask the same detectors in the input workspace
245 mask->setProperty<MatrixWorkspace_sptr>("Workspace", rebinnedWS);
246 mask->setProperty<std::vector<size_t>>("WorkspaceIndexList", dets_to_mask);
247 mask->execute();
248 } catch (std::invalid_argument &err) {
249 std::stringstream e;
250 e << "Invalid argument to MaskDetectors Child Algorithm: " << err.what();
251 g_log.error(e.str());
252 } catch (std::runtime_error &err) {
253 std::stringstream e;
254 e << "Unable to successfully run MaskDetectors Child Algorithm: " << err.what();
255 g_log.error(e.str());
256 }
257 }
258}
259
265void CalculateEfficiency::maskComponent(MatrixWorkspace &ws, const std::string &componentName) {
266 auto instrument = ws.getInstrument();
267 try {
268 std::shared_ptr<const Geometry::ICompAssembly> component =
269 std::dynamic_pointer_cast<const Geometry::ICompAssembly>(instrument->getComponentByName(componentName));
270 if (!component) {
271 g_log.warning("Component " + componentName + " expected to be a CompAssembly, e.g., a bank. Component " +
272 componentName + " not masked!");
273 return;
274 }
275 std::vector<detid_t> detectorList;
276 for (int x = 0; x < component->nelements(); x++) {
277 std::shared_ptr<Geometry::ICompAssembly> xColumn =
278 std::dynamic_pointer_cast<Geometry::ICompAssembly>((*component)[x]);
279 for (int y = 0; y < xColumn->nelements(); y++) {
280 std::shared_ptr<Geometry::Detector> detector = std::dynamic_pointer_cast<Geometry::Detector>((*xColumn)[y]);
281 if (detector) {
282 auto detID = detector->getID();
283 detectorList.emplace_back(detID);
284 }
285 }
286 }
287 auto indexList = ws.getIndicesFromDetectorIDs(detectorList);
288 auto &spectrumInfo = ws.mutableSpectrumInfo();
289 for (const auto &idx : indexList) {
290 ws.getSpectrum(idx).clearData();
291 spectrumInfo.setMasked(idx, true);
292 }
293 } catch (std::exception &) {
294 g_log.warning("Expecting the component " + componentName +
295 " to be a CompAssembly, e.g., a bank. Component not masked!");
296 }
297}
298
308void CalculateEfficiency::maskEdges(const MatrixWorkspace_sptr &ws, int left, int right, int high, int low,
309 const std::string &componentName) {
310
311 auto instrument = ws->getInstrument();
312
313 std::shared_ptr<Mantid::Geometry::RectangularDetector> component;
314 try {
315 component = std::const_pointer_cast<Mantid::Geometry::RectangularDetector>(
316 std::dynamic_pointer_cast<const Mantid::Geometry::RectangularDetector>(
317 instrument->getComponentByName(componentName)));
318 } catch (std::exception &) {
319 g_log.warning("Expecting the component " + componentName + " to be a RectangularDetector. maskEdges not executed.");
320 return;
321 }
322 if (!component) {
323 g_log.warning("Component " + componentName + " is not a RectangularDetector. MaskEdges not executed.");
324 return;
325 }
326
327 std::vector<int> IDs;
328 int i = 0;
329
330 while (i < left * component->idstep()) {
331 IDs.emplace_back(component->idstart() + i);
332 i += 1;
333 }
334 // right
335 i = component->maxDetectorID() - right * component->idstep();
336 while (i < component->maxDetectorID()) {
337 IDs.emplace_back(i);
338 i += 1;
339 }
340 // low: 0,256,512,768,..,1,257,513
341 for (int row = 0; row < low; row++) {
342 i = row + component->idstart();
343 while (i < component->nelements() * component->idstep() - component->idstep() + low + component->idstart()) {
344 IDs.emplace_back(i);
345 i += component->idstep();
346 }
347 }
348 // high # 255, 511, 767..
349 for (int row = 0; row < high; row++) {
350 i = component->idstep() + component->idstart() - row - 1;
351 while (i < component->nelements() * component->idstep() + component->idstart()) {
352 IDs.emplace_back(i);
353 i += component->idstep();
354 }
355 }
356
357 g_log.debug() << "CalculateEfficiency::maskEdges Detector Ids to Mask:" << std::endl;
358 for (auto id : IDs) {
359 g_log.debug() << id << " ";
360 }
361 g_log.debug() << std::endl;
362
363 auto maskAlg = createChildAlgorithm("MaskDetectors");
364 maskAlg->setChild(true);
365 maskAlg->setProperty("Workspace", ws);
366 maskAlg->setProperty("DetectorList", IDs);
367 maskAlg->execute();
368}
369
370} // namespace Mantid::Algorithms
#define DECLARE_ALGORITHM(classname)
Definition: Algorithm.h:576
double error
Definition: IndexPeaks.cpp:133
double left
Definition: LineProfile.cpp:80
double right
Definition: LineProfile.cpp:81
IntArray detectorList
void declareProperty(std::unique_ptr< Kernel::Property > p, const std::string &doc="") override
Add a property to the list of managed properties.
Definition: Algorithm.cpp:1913
std::string getPropertyValue(const std::string &name) const override
Get the value of a property as a string.
Definition: Algorithm.cpp:2026
TypedValue getProperty(const std::string &name) const override
Get the value of a property.
Definition: Algorithm.cpp:2076
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.
Definition: Algorithm.cpp:842
Kernel::Logger & g_log
Definition: Algorithm.h:451
void progress(double p, const std::string &msg="", double estimatedTime=0.0, int progressPrecision=0)
Sends ProgressNotification.
Definition: Algorithm.cpp:231
static bool isEmpty(const NumT toCheck)
checks that the value was not set by users, uses the value in empty double/int.
Geometry::Instrument_const_sptr getInstrument() const
Returns the parameterized instrument.
SpectrumInfo & mutableSpectrumInfo()
Return a non-const reference to the SpectrumInfo object.
virtual void clearData()=0
Base MatrixWorkspace Abstract Class.
virtual ISpectrum & getSpectrum(const size_t index)=0
Return the underlying ISpectrum ptr at the given workspace index.
std::vector< size_t > getIndicesFromDetectorIDs(const std::vector< detid_t > &detIdList) const
Converts a list of detector IDs to the corresponding workspace indices.
A property class for workspaces.
void exec() override
Executes the algorithm.
void normalizeDetectors(const API::MatrixWorkspace_sptr &rebinnedWS, const API::MatrixWorkspace_sptr &outputWS, double sum, double error, int nPixels, double min_eff, double max_eff)
Normalize all detectors to get the relative efficiency.
void sumUnmaskedDetectors(const API::MatrixWorkspace_sptr &rebinnedWS, double &sum, double &error, int &nPixels)
Sum all detectors, excluding monitors and masked detectors.
void maskComponent(API::MatrixWorkspace &ws, const std::string &componentName)
Fully masks one component named componentName.
void init() override
Initialization method.
void maskEdges(const API::MatrixWorkspace_sptr &ws, int left, int right, int high, int low, const std::string &componentName)
Mask edges of a RectangularDetector.
Support for a property that holds an array of values.
Definition: ArrayProperty.h:28
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:114
void error(const std::string &msg)
Logs at error level.
Definition: Logger.cpp:77
void warning(const std::string &msg)
Logs at warning level.
Definition: Logger.cpp:86
static T & Instance()
Return a reference to the Singleton instance, creating it if it does not already exist Creation is do...
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:43
@ Input
An input workspace.
Definition: Property.h:53
@ Output
An output workspace.
Definition: Property.h:54