Mantid
Loading...
Searching...
No Matches
He3TubeEfficiency.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 +
8#include "MantidAPI/Axis.h"
24
25#include <cmath>
26#include <stdexcept>
27
28// this should be a big number but not so big that there are rounding errors
29const double DIST_TO_UNIVERSE_EDGE = 1e3;
30
31// Scalar constant for exponential in units of K / (m Angstroms atm)
32const double EXP_SCALAR_CONST = 2175.486863864;
33
34// Tolerance for diameter/thickness comparison
35const double TOL = 1.0e-8;
36
37namespace Mantid::Algorithms {
38
39using namespace DataObjects;
40using namespace HistogramData;
41// Register the class into the algorithm factory
42DECLARE_ALGORITHM(He3TubeEfficiency)
43
44
46 : Algorithm(), m_inputWS(), m_outputWS(), m_paraMap(nullptr), m_shapeCache(), m_samplePos(), m_spectraSkipped(),
47 m_progress(nullptr) {
48 m_shapeCache.clear();
49}
50
55 using namespace Mantid::Kernel;
56
57 auto wsValidator = std::make_shared<CompositeValidator>();
58 wsValidator->add<API::WorkspaceUnitValidator>("Wavelength");
59 wsValidator->add<API::HistogramValidator>();
60 wsValidator->add<API::InstrumentValidator>();
62 "InputWorkspace", "", Kernel::Direction::Input, wsValidator),
63 "Name of the input workspace");
64 this->declareProperty(
65 std::make_unique<API::WorkspaceProperty<API::MatrixWorkspace>>("OutputWorkspace", "", Kernel::Direction::Output),
66 "Name of the output workspace, can be the same as the input");
67 auto mustBePositive = std::make_shared<Kernel::BoundedValidator<double>>();
68 mustBePositive->setLower(0.0);
69 this->declareProperty(std::make_unique<Kernel::PropertyWithValue<double>>("ScaleFactor", 1.0, mustBePositive),
70 "Constant factor with which to scale the calculated"
71 "detector efficiency. Same factor applies to all efficiencies.");
72
73 auto mustBePosArr = std::make_shared<Kernel::ArrayBoundedValidator<double>>();
74 mustBePosArr->setLower(0.0);
75 this->declareProperty(std::make_unique<Kernel::ArrayProperty<double>>("TubePressure", mustBePosArr),
76 "Provide overriding the default tube pressure. The pressure must "
77 "be specified in atm.");
78 this->declareProperty(std::make_unique<Kernel::ArrayProperty<double>>("TubeThickness", mustBePosArr),
79 "Provide overriding the default tube thickness. The thickness must "
80 "be specified in metres.");
81 this->declareProperty(std::make_unique<Kernel::ArrayProperty<double>>("TubeTemperature", mustBePosArr),
82 "Provide overriding the default tube temperature. The temperature must "
83 "be specified in Kelvin.");
84}
85
90 // Get the workspaces
91 m_inputWS = this->getProperty("InputWorkspace");
92 m_outputWS = this->getProperty("OutputWorkspace");
93
94 if (m_outputWS != m_inputWS) {
95 m_outputWS = create<API::MatrixWorkspace>(*m_inputWS);
96 }
97
98 // Get the detector parameters
99 m_paraMap = &(m_inputWS->constInstrumentParameters());
100
101 // Store some information about the instrument setup that will not change
102 m_samplePos = m_inputWS->getInstrument()->getSample()->getPos();
103
104 // Check if it is an event workspace
106 std::dynamic_pointer_cast<const DataObjects::EventWorkspace>(m_inputWS);
107 if (eventW != nullptr) {
108 this->execEvent();
109 return;
110 }
111
112 std::size_t numHists = m_inputWS->getNumberHistograms();
113 m_progress = std::make_unique<API::Progress>(this, 0.0, 1.0, numHists);
114 const auto &spectrumInfo = m_inputWS->spectrumInfo();
115
117 for (int i = 0; i < static_cast<int>(numHists); ++i) {
119
120 m_outputWS->setSharedX(i, m_inputWS->sharedX(i));
121 try {
122 this->correctForEfficiency(i, spectrumInfo);
123 } catch (std::out_of_range &) {
124 // if we don't have all the data there will be spectra we can't correct,
125 // avoid leaving the workspace part corrected
126 m_outputWS->mutableY(i) = 0;
127 PARALLEL_CRITICAL(deteff_invalid) { m_spectraSkipped.emplace_back(m_inputWS->getAxis(1)->spectraNo(i)); }
128 }
129
130 // make regular progress reports
131 m_progress->report();
132 // check for canceling the algorithm
133 if (i % 1000 == 0) {
135 }
136
138 }
140
141 this->logErrors();
142 this->setProperty("OutputWorkspace", m_outputWS);
143}
144
156void He3TubeEfficiency::correctForEfficiency(std::size_t spectraIndex, const API::SpectrumInfo &spectrumInfo) {
157 if (spectrumInfo.isMonitor(spectraIndex) || spectrumInfo.isMasked(spectraIndex)) {
158 return;
159 }
160
161 const auto &det = spectrumInfo.detector(spectraIndex);
162 const double exp_constant = this->calculateExponential(spectraIndex, det);
163 const double scale = this->getProperty("ScaleFactor");
164
165 const auto &yValues = m_inputWS->y(spectraIndex);
166 auto &yOut = m_outputWS->mutableY(spectraIndex);
167
168 const auto wavelength = m_inputWS->points(spectraIndex);
169
170 // todo is it going to die? returning local var by reference
171 std::vector<double> effCorrection(wavelength.size());
172
173 computeEfficiencyCorrection(effCorrection, wavelength, exp_constant, scale);
174
175 std::transform(yValues.cbegin(), yValues.cend(), effCorrection.cbegin(), yOut.begin(),
176 [&](const double y, const double effcorr) { return y * effcorr; });
177
178 const auto &eValues = m_inputWS->e(spectraIndex);
179 auto &eOut = m_outputWS->mutableE(spectraIndex);
180
181 // reset wavelength iterator
182 std::transform(eValues.cbegin(), eValues.cend(), effCorrection.cbegin(), eOut.begin(),
183 [&](const double e, const double effcorr) { return e * effcorr; });
184}
185
194double He3TubeEfficiency::calculateExponential(std::size_t spectraIndex, const Geometry::IDetector &idet) {
195 // Get the parameters for the current associated tube
196 double pressure = this->getParameter("TubePressure", spectraIndex, "tube_pressure", idet);
197 double tubethickness = this->getParameter("TubeThickness", spectraIndex, "tube_thickness", idet);
198 double temperature = this->getParameter("TubeTemperature", spectraIndex, "tube_temperature", idet);
199
200 double detRadius(0.0);
201 Kernel::V3D detAxis;
202 this->getDetectorGeometry(idet, detRadius, detAxis);
203 double detDiameter = 2.0 * detRadius;
204 double twiceTubeThickness = 2.0 * tubethickness;
205
206 // now get the sin of the angle, it's the magnitude of the cross product of
207 // unit vector along the detector tube axis and a unit vector directed from
208 // the sample to the detector center
209 const Kernel::V3D vectorFromSample = normalize(idet.getPos() - m_samplePos);
210 Kernel::Quat rot = idet.getRotation();
211 // rotate the original cylinder object axis to get the detector axis in the
212 // actual instrument
213 rot.rotate(detAxis);
214 detAxis.normalize();
215 // Scalar product is quicker than cross product
216 double cosTheta = detAxis.scalar_prod(vectorFromSample);
217 double sinTheta = std::sqrt(1.0 - cosTheta * cosTheta);
218
219 const double straight_path = detDiameter - twiceTubeThickness;
220 if (Kernel::withinAbsoluteDifference(straight_path, 0.0, TOL)) {
221 throw std::out_of_range("Twice tube thickness cannot be greater than "
222 "or equal to the tube diameter");
223 }
224
225 const double pathlength = straight_path / sinTheta;
226 return EXP_SCALAR_CONST * (pressure / temperature) * pathlength;
227}
228
235void He3TubeEfficiency::getDetectorGeometry(const Geometry::IDetector &det, double &detRadius, Kernel::V3D &detAxis) {
236 std::shared_ptr<const Geometry::IObject> shape_sptr = det.shape();
237 if (!shape_sptr) {
238 throw std::runtime_error("Detector geometry error: detector with id: " + std::to_string(det.getID()) +
239 " does not have shape. Is this a detectors group?\n"
240 "The algorithm works for instruments with one-to-one "
241 "spectra-to-detector maps only!");
242 }
243 // std::map<const Geometry::Object *, std::pair<double,
244 // Kernel::V3D>>::const_iterator
245 auto it = m_shapeCache.find(shape_sptr.get());
246 if (it == m_shapeCache.end()) {
247 double xDist = distToSurface(Kernel::V3D(DIST_TO_UNIVERSE_EDGE, 0, 0), shape_sptr.get());
248 double zDist = distToSurface(Kernel::V3D(0, 0, DIST_TO_UNIVERSE_EDGE), shape_sptr.get());
249 if (Kernel::withinAbsoluteDifference(zDist, xDist, 1e-8)) {
250 detRadius = zDist / 2.0;
251 detAxis = Kernel::V3D(0, 1, 0);
252 // assume radii in z and x and the axis is in the y
253 PARALLEL_CRITICAL(deteff_shapecachea) { m_shapeCache.insert({shape_sptr.get(), {detRadius, detAxis}}); }
254 return;
255 }
256 double yDist = distToSurface(Kernel::V3D(0, DIST_TO_UNIVERSE_EDGE, 0), shape_sptr.get());
257 if (Kernel::withinAbsoluteDifference(yDist, zDist, 1e-8)) {
258 detRadius = yDist / 2.0;
259 detAxis = Kernel::V3D(1, 0, 0);
260 // assume that y and z are radii of the cylinder's circular cross-section
261 // and the axis is perpendicular, in the x direction
262 PARALLEL_CRITICAL(deteff_shapecacheb) { m_shapeCache.insert({shape_sptr.get(), {detRadius, detAxis}}); }
263 return;
264 }
265
266 if (Kernel::withinAbsoluteDifference(xDist, yDist, 1e-8)) {
267 detRadius = xDist / 2.0;
268 detAxis = Kernel::V3D(0, 0, 1);
269 PARALLEL_CRITICAL(deteff_shapecachec) { m_shapeCache.insert({shape_sptr.get(), {detRadius, detAxis}}); }
270 return;
271 }
272 } else {
273 std::pair<double, Kernel::V3D> geometry = it->second;
274 detRadius = geometry.first;
275 detAxis = geometry.second;
276 }
277}
278
290double He3TubeEfficiency::distToSurface(const Kernel::V3D &start, const Geometry::IObject *shape) const {
291 // get a vector from the point that was passed to the origin
292 const Kernel::V3D direction = normalize(-start);
293 // put the point and the vector (direction) together to get a line,
294 // here called a track
295 Geometry::Track track(start, direction);
296 // split the track (line) up into the part that is inside the shape and the
297 // part that is outside
298 shape->interceptSurface(track);
299
300 if (track.count() != 1) {
301 // the track missed the shape, probably the shape is not centered on
302 // the origin
303 throw std::invalid_argument("Fatal error interpreting the shape of a detector");
304 }
305 // the first part of the track will be the part inside the shape,
306 // return its length
307 return track.cbegin()->distInsideObject;
308}
309
317double He3TubeEfficiency::detectorEfficiency(const double alpha, const double scale_factor) const {
318 return (scale_factor / (1.0 - std::exp(-alpha)));
319}
320
325 std::vector<int>::size_type nspecs = m_spectraSkipped.size();
326 if (nspecs > 0) {
327 this->g_log.warning() << "There were " << nspecs << " spectra that could not be corrected. ";
328 this->g_log.debug() << "Unaffected spectra numbers: ";
329 for (size_t i = 0; i < nspecs; ++i) {
330 this->g_log.debug() << m_spectraSkipped[i] << " ";
331 }
332 this->g_log.debug() << '\n';
333 }
334}
335
345double He3TubeEfficiency::getParameter(const std::string &wsPropName, std::size_t currentIndex,
346 const std::string &detPropName, const Geometry::IDetector &idet) {
347 std::vector<double> wsProp = this->getProperty(wsPropName);
348
349 if (wsProp.empty()) {
350 return idet.getNumberParameter(detPropName).at(0);
351 } else {
352 if (wsProp.size() == 1) {
353 return wsProp.at(0);
354 } else {
355 return wsProp.at(currentIndex);
356 }
357 }
358}
359
364 this->g_log.information("Processing event workspace");
365
366 const API::MatrixWorkspace_const_sptr matrixInputWS = getProperty("InputWorkspace");
367
368 // generate the output workspace pointer
369 API::MatrixWorkspace_sptr matrixOutputWS = getProperty("OutputWorkspace");
370 if (matrixOutputWS != matrixInputWS) {
371 matrixOutputWS = matrixInputWS->clone();
372 setProperty("OutputWorkspace", matrixOutputWS);
373 }
374 auto loc_outputWS = std::dynamic_pointer_cast<DataObjects::EventWorkspace>(matrixOutputWS);
375
376 std::size_t numHistograms = loc_outputWS->getNumberHistograms();
377 auto &spectrumInfo = loc_outputWS->mutableSpectrumInfo();
378 m_progress = std::make_unique<API::Progress>(this, 0.0, 1.0, numHistograms);
379
380 PARALLEL_FOR_IF(Kernel::threadSafe(*loc_outputWS))
381 for (int i = 0; i < static_cast<int>(numHistograms); ++i) {
383
384 const auto &det = spectrumInfo.detector(i);
385 if (spectrumInfo.isMonitor(i) || spectrumInfo.isMasked(i)) {
386 continue;
387 }
388
389 double exp_constant = 0.0;
390 try {
391 exp_constant = this->calculateExponential(i, det);
392 } catch (std::out_of_range &) {
393 // Parameters are bad so skip correction
394 PARALLEL_CRITICAL(deteff_invalid) {
395 m_spectraSkipped.emplace_back(loc_outputWS->getAxis(1)->spectraNo(i));
396 loc_outputWS->getSpectrum(i).clearData();
397 spectrumInfo.setMasked(i, true);
398 }
399 }
400
401 // Do the correction
402 auto &evlist = loc_outputWS->getSpectrum(i);
403 switch (evlist.getEventType()) {
404 case API::TOF:
405 // Switch to weights if needed.
406 evlist.switchTo(API::WEIGHTED);
407 // Fall through
408 case API::WEIGHTED:
409 eventHelper(evlist.getWeightedEvents(), exp_constant);
410 break;
412 eventHelper(evlist.getWeightedEventsNoTime(), exp_constant);
413 break;
414 }
415
416 m_progress->report();
417
418 // check for canceling the algorithm
419 if (i % 1000 == 0) {
421 }
422
424 }
426
427 loc_outputWS->clearMRU();
428
429 this->logErrors();
430}
431
439void He3TubeEfficiency::computeEfficiencyCorrection(std::vector<double> &effCorrection, const Points &xes,
440 const double expConstant, const double scale) const {
441
442 std::transform(xes.cbegin(), xes.cend(), effCorrection.begin(),
443 [&](double wavelen) { return detectorEfficiency(expConstant * wavelen, scale); });
444}
445
451template <class T> void He3TubeEfficiency::eventHelper(std::vector<T> &events, double expval) {
452 const double scale = this->getProperty("ScaleFactor");
453
454 for (auto &event : events) {
455 auto de = static_cast<float>(this->detectorEfficiency(expval * event.tof(), scale));
456 event.m_weight *= de;
457 event.m_errorSquared *= de * de;
458 }
459}
460
461} // namespace Mantid::Algorithms
#define DECLARE_ALGORITHM(classname)
Definition Algorithm.h:538
const double DIST_TO_UNIVERSE_EDGE
const double TOL
const double EXP_SCALAR_CONST
#define PARALLEL_START_INTERRUPT_REGION
Begins a block to skip processing is the algorithm has been interupted Note the end of the block if n...
#define PARALLEL_CRITICAL(name)
#define PARALLEL_END_INTERRUPT_REGION
Ends a block to skip processing is the algorithm has been interupted Note the start of the block if n...
#define PARALLEL_FOR_IF(condition)
Empty definitions - to enable set your complier to enable openMP.
#define PARALLEL_CHECK_INTERRUPT_REGION
Adds a check after a Parallel region to see if it was interupted.
Base class from which all concrete algorithm classes should be derived.
Definition Algorithm.h:76
void declareProperty(std::unique_ptr< Kernel::Property > p, const std::string &doc="") override
Add a property to the list of managed properties.
void clear() override
Clears all properties under management.
TypedValue getProperty(const std::string &name) const override
Get the value of a property.
Kernel::Logger & g_log
Definition Algorithm.h:422
void interruption_point()
This is called during long-running operations, and check if the algorithm has requested that it be ca...
A validator which checks that a workspace contains histogram data (the default) or point data as requ...
A validator which checks that a workspace has a valid instrument.
API::SpectrumInfo is an intermediate step towards a SpectrumInfo that is part of Instrument-2....
bool isMonitor(const size_t index) const
Returns true if the detector(s) associated with the spectrum are monitors.
bool isMasked(const size_t index) const
Returns true if the detector(s) associated with the spectrum are masked.
const Geometry::IDetector & detector(const size_t index) const
Return a const reference to the detector or detector group of the spectrum with given index.
A property class for workspaces.
A validator which checks that the unit of the workspace referred to by a WorkspaceProperty is the exp...
Corrects the input workspace for helium3 tube efficiency based on an exponential parameterization.
double detectorEfficiency(const double alpha, const double scale_factor=1.0) const
Calculate the detector efficiency.
std::map< const Geometry::IObject *, std::pair< double, Kernel::V3D > > m_shapeCache
A lookup of previously seen shape objects used to save calculation time as most detectors have the sa...
double getParameter(const std::string &wsPropName, std::size_t currentIndex, const std::string &detPropName, const Geometry::IDetector &idet)
Retrieve the detector parameters from workspace or detector properties.
Kernel::V3D m_samplePos
Sample position.
void init() override
Declare algorithm properties.
void eventHelper(std::vector< T > &events, double expval)
Helper for event handling.
const Geometry::ParameterMap * m_paraMap
Map that stores additional properties for detectors.
API::MatrixWorkspace_const_sptr m_inputWS
The user selected (input) workspace.
std::vector< specnum_t > m_spectraSkipped
The spectra numbers that were skipped.
void correctForEfficiency(std::size_t spectraIndex, const API::SpectrumInfo &spectrumInfo)
Correct the given spectra index for efficiency.
void getDetectorGeometry(const Geometry::IDetector &det, double &detRadius, Kernel::V3D &detAxis)
Sets the detector geometry cache if necessary.
double calculateExponential(std::size_t spectraIndex, const Geometry::IDetector &idet)
Function to calculate exponential contribution.
double distToSurface(const Kernel::V3D &start, const Geometry::IObject *shape) const
Computes the distance to the given shape from a starting point.
void logErrors() const
Log any errors with spectra that occurred.
void computeEfficiencyCorrection(std::vector< double > &effCorrection, const HistogramData::Points &wavelength, const double expConstant, const double scale) const
Calculates the efficiency correction from the points.
void exec() override
Executes the algorithm.
API::MatrixWorkspace_sptr m_outputWS
The output workspace, maybe the same as the input one.
std::unique_ptr< API::Progress > m_progress
Algorithm progress keeper.
virtual Kernel::V3D getPos() const =0
Get the position of the IComponent. Tree structure is traverse through the.
virtual Kernel::Quat getRotation() const =0
Get the absolute orientation of the IComponent.
virtual std::vector< double > getNumberParameter(const std::string &pname, bool recursive=true) const =0
Get a parameter defined as a double.
Interface class for detector objects.
Definition IDetector.h:43
virtual detid_t getID() const =0
Get the detector ID.
virtual const std::shared_ptr< const IObject > shape() const =0
Returns the shape of the Object.
IObject : Interface for geometry objects.
Definition IObject.h:42
virtual int interceptSurface(Geometry::Track &) const =0
Defines a track as a start point and a direction.
Definition Track.h:165
int count() const
Returns the number of links.
Definition Track.h:219
LType::const_iterator cbegin() const
Returns an interator to the start of the set of links (const version)
Definition Track.h:206
Support for a property that holds an array of values.
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 warning(const std::string &msg)
Logs at warning level.
Definition Logger.cpp:117
void information(const std::string &msg)
Logs at information level.
Definition Logger.cpp:136
The concrete, templated class for properties.
Class for quaternions.
Definition Quat.h:39
void rotate(V3D &) const
Rotate a vector.
Definition Quat.cpp:397
Class for 3D vectors.
Definition V3D.h:34
constexpr double scalar_prod(const V3D &v) const noexcept
Calculates the cross product.
Definition V3D.h:280
double normalize()
Make a normalized vector (return norm value)
Definition V3D.cpp:129
std::shared_ptr< const MatrixWorkspace > MatrixWorkspace_const_sptr
shared pointer to the matrix workspace base class (const version)
std::shared_ptr< MatrixWorkspace > MatrixWorkspace_sptr
shared pointer to the matrix workspace base class
std::shared_ptr< const EventWorkspace > EventWorkspace_const_sptr
shared pointer to a const Workspace2D
MANTID_KERNEL_DLL bool withinAbsoluteDifference(T const x, T const y, S const tolerance)
Test whether x, y are within absolute tolerance tol.
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.
MANTID_KERNEL_DLL V3D normalize(V3D v)
Normalizes a V3D.
Definition V3D.h:352
std::string to_string(const wide_integer< Bits, Signed > &n)
@ Input
An input workspace.
Definition Property.h:53
@ Output
An output workspace.
Definition Property.h:54