Mantid
Loading...
Searching...
No Matches
DetectorEfficiencyCor.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"
11#include "MantidAPI/Run.h"
24#include "MantidTypes/SpectrumDefinition.h"
25
26#include <algorithm>
27#include <cmath>
28#include <functional>
29
30namespace Mantid::Algorithms {
31// Register the class into the algorithm factory
32DECLARE_ALGORITHM(DetectorEfficiencyCor)
33
34using namespace Kernel;
35using namespace API;
36using namespace Geometry;
37using namespace DataObjects;
38
39namespace {
40
41// E = KSquaredToE*K^2 KSquaredToE = (hbar^2)/(2*NeutronMass)
42const double KSquaredToE = 2.07212466; // units of meV Angstrom^-2
43
44const short NUMCOEFS = 25;
45// series expansion coefficients copied from a fortran source code file
46const double c_eff_f[] = {
47 0.7648360390553052, -0.3700950778935237, 0.1582704090813516, -6.0170218669705407E-02,
48 2.0465515957968953E-02, -6.2690181465706840E-03, 1.7408667184745830E-03, -4.4101378999425122E-04,
49 1.0252117967127217E-04, -2.1988904738111659E-05, 4.3729347905629990E-06, -8.0998753944849788E-07,
50 1.4031240949230472E-07, -2.2815971698619819E-08, 3.4943984983382137E-09, -5.0562696807254781E-10,
51 6.9315483353094009E-11, -9.0261598195695569E-12, 1.1192324844699897E-12, -1.3204992654891612E-13,
52 1.4100387524251801E-14, -8.6430862467068437E-16, -1.1129985821867194E-16, -4.5505266221823604E-16,
53 3.8885561437496108E-16};
54
55const double c_eff_g[] = {
56 2.033429926215546, -2.3123407369310212E-02, 7.0671915734894875E-03, -7.5970017538257162E-04,
57 7.4848652541832373E-05, 4.5642679186460588E-05, -2.3097291253000307E-05, 1.9697221715275770E-06,
58 2.4115259271262346E-06, -7.1302220919333692E-07, -2.5124427621592282E-07, 1.3246884875139919E-07,
59 3.4364196805913849E-08, -2.2891359549026546E-08, -6.7281240212491156E-09, 3.8292458615085678E-09,
60 1.6451021034313840E-09, -5.5868962123284405E-10, -4.2052310689211225E-10, 4.3217612266666094E-11,
61 9.9547699528024225E-11, 1.2882834243832519E-11, -1.9103066351000564E-11, -7.6805495297094239E-12,
62 1.8568853399347773E-12};
63
64// constants from the fortran code multiplied together sigref=143.23d0,
65// wref=3.49416d0, atmref=10.0d0 const = 2.0*sigref*wref/atmref
66const double g_helium_prefactor = 2.0 * 143.23 * 3.49416 / 10.0;
67
68// this should be a big number but not so big that there are rounding errors
69const double DIST_TO_UNIVERSE_EDGE = 1e3;
70
71// Name of pressure parameter
72const std::string PRESSURE_PARAM = "TubePressure";
73// Name of wall thickness parameter
74const std::string THICKNESS_PARAM = "TubeThickness";
75} // namespace
76
77// this default constructor calls default constructors and sets other member
78// data to impossible (flag) values
80 : Algorithm(), m_inputWS(), m_outputWS(), m_paraMap(nullptr), m_Ei(-1.0), m_ki(-1.0), m_shapeCache(), m_samplePos(),
81 m_spectraSkipped() {
83}
84
89 auto val = std::make_shared<CompositeValidator>();
90 val->add<WorkspaceUnitValidator>("DeltaE");
91 val->add<HistogramValidator>();
92 val->add<InstrumentValidator>();
93 declareProperty(std::make_unique<WorkspaceProperty<>>("InputWorkspace", "", Direction::Input, val),
94 "The workspace to correct for detector efficiency");
95 declareProperty(std::make_unique<WorkspaceProperty<>>("OutputWorkspace", "", Direction::Output),
96 "The name of the workspace in which to store the result. Each histogram "
97 "from the input workspace maps to a histogram in this workspace that has "
98 "just one value which indicates if there was a bad detector.");
99 auto checkEi = std::make_shared<BoundedValidator<double>>();
100 checkEi->setLower(0.0);
101 declareProperty("IncidentEnergy", EMPTY_DBL(), checkEi,
102 "The energy of neutrons leaving the source as can be "
103 "calculated by :ref:`algm-GetEi`. If this value is provided, "
104 "uses property value, if it is not present, needs Ei log "
105 "value set on the workspace.");
106}
107
115 // gets and checks the values passed to the algorithm
117
118 // wave number that the neutrons originally had
119 m_ki = std::sqrt(m_Ei / KSquaredToE);
120
121 // Store some information about the instrument setup that will not change
122 m_samplePos = m_inputWS->getInstrument()->getSample()->getPos();
123
124 int64_t numHists = m_inputWS->getNumberHistograms();
125 auto numHists_d = static_cast<double>(numHists);
126 const auto progStep = static_cast<int64_t>(ceil(numHists_d / 100.0));
127 auto const &spectrumInfo = m_inputWS->spectrumInfo();
128
130 for (int64_t i = 0; i < numHists; ++i) {
132
133 m_outputWS->setSharedX(i, m_inputWS->sharedX(i));
134 try {
135 correctForEfficiency(i, spectrumInfo);
136 } catch (Exception::NotFoundError &) {
137 // zero the Y data that can't be corrected
138 m_outputWS->mutableY(i) *= 0.0;
139 PARALLEL_CRITICAL(deteff_invalid) {
140 m_spectraSkipped.insert(m_spectraSkipped.end(), m_inputWS->getAxis(1)->spectraNo(i));
141 }
142 }
143 // make regular progress reports and check for canceling the algorithm
144 if (i % progStep == 0) {
145 progress(static_cast<double>(i) / numHists_d);
147 }
148
150 }
152
153 logErrors(numHists);
154 setProperty("OutputWorkspace", m_outputWS);
155}
162 // these first three properties are fully checked by validators
163 m_inputWS = getProperty("InputWorkspace");
164 m_paraMap = &(m_inputWS->constInstrumentParameters());
165
166 m_Ei = getProperty("IncidentEnergy");
167 // If we're not given an Ei, see if one has been set.
168 if (m_Ei == EMPTY_DBL()) {
169 if (m_inputWS->run().hasProperty("Ei")) {
170 m_Ei = m_inputWS->run().getPropertyValueAsType<double>("Ei");
171 g_log.debug() << "Using stored Ei value " << m_Ei << "\n";
172 } else {
173 throw std::invalid_argument("No Ei value has been set or stored within the run information.");
174 }
175 }
176
177 m_outputWS = getProperty("OutputWorkspace");
178 // If input and output workspaces are not the same, create a new workspace for
179 // the output
180 if (m_outputWS != m_inputWS) {
181 m_outputWS = create<MatrixWorkspace>(*m_inputWS);
182 }
183}
184
196void DetectorEfficiencyCor::correctForEfficiency(int64_t spectraIn, const SpectrumInfo &spectrumInfo) {
197 if (!spectrumInfo.hasDetectors(spectraIn))
198 throw Exception::NotFoundError("No detectors found", spectraIn);
199
200 if (spectrumInfo.isMonitor(spectraIn) || spectrumInfo.isMasked(spectraIn)) {
201 return;
202 }
203
204 auto &yout = m_outputWS->mutableY(spectraIn);
205 auto &eout = m_outputWS->mutableE(spectraIn);
206 // Need the original values so this is not a reference
207 const auto yValues = m_inputWS->y(spectraIn);
208 const auto eValues = m_inputWS->e(spectraIn);
209
210 // Storage for the reciprocal wave vectors that are calculated as the
211 // correction proceeds
212 std::vector<double> oneOverWaveVectors(yValues.size());
213 const auto &detectorInfo = m_inputWS->detectorInfo();
214 const auto &spectrumDefinition = spectrumInfo.spectrumDefinition(spectraIn);
215
216 for (const auto &index : spectrumDefinition) {
217 const auto detIndex = index.first;
218 const auto &det_member = detectorInfo.detector(detIndex);
219 Parameter_sptr par = m_paraMap->getRecursive(det_member.getComponentID(), PRESSURE_PARAM);
220 if (!par) {
221 throw Exception::NotFoundError(PRESSURE_PARAM, spectraIn);
222 }
223 const double atms = par->value<double>();
224 par = m_paraMap->getRecursive(det_member.getComponentID(), THICKNESS_PARAM);
225 if (!par) {
226 throw Exception::NotFoundError(THICKNESS_PARAM, spectraIn);
227 }
228 const double wallThickness = par->value<double>();
229 double detRadius(0.0);
230 V3D detAxis;
231 getDetectorGeometry(det_member, detRadius, detAxis);
232
233 // now get the sin of the angle, it's the magnitude of the cross product of
234 // unit vector along the detector tube axis and a unit vector directed from
235 // the sample to the detector centre
236 const V3D vectorFromSample = normalize(det_member.getPos() - m_samplePos);
237 Quat rot = det_member.getRotation();
238 // rotate the original cylinder object axis to get the detector axis in the
239 // actual instrument
240 rot.rotate(detAxis);
241 detAxis.normalize();
242 // Scalar product is quicker than cross product
243 double cosTheta = detAxis.scalar_prod(vectorFromSample);
244 double sinTheta = std::sqrt(1.0 - cosTheta * cosTheta);
245 // Detector constant
246 const double det_const = g_helium_prefactor * (detRadius - wallThickness) * atms / sinTheta;
247
248 auto yinItr = yValues.cbegin();
249 auto einItr = eValues.cbegin();
250 auto youtItr = yout.begin();
251 auto eoutItr = eout.begin();
252 auto xItr = m_inputWS->x(spectraIn).cbegin();
253 auto wavItr = oneOverWaveVectors.begin();
254
255 for (; youtItr != yout.end(); ++youtItr, ++eoutItr) {
256 if (index == spectrumDefinition[0]) {
257 *youtItr = 0.0;
258 *eoutItr = 0.0;
259 *wavItr = calculateOneOverK(*xItr, *(xItr + 1));
260 }
261 const double oneOverWave = *wavItr;
262 const auto nDets(static_cast<double>(spectrumDefinition.size()));
263 const double factor = 1.0 / nDets / detectorEfficiency(det_const * oneOverWave);
264 *youtItr += (*yinItr) * factor;
265 *eoutItr += (*einItr) * factor;
266 ++yinItr;
267 ++einItr;
268 ++xItr;
269 ++wavItr;
270 }
271 }
272}
273
283double DetectorEfficiencyCor::calculateOneOverK(double loBinBound, double uppBinBound) const {
284 double energy = m_Ei - 0.5 * (uppBinBound + loBinBound);
285 double oneOverKSquared = KSquaredToE / energy;
286 return std::sqrt(oneOverKSquared);
287}
288
294void DetectorEfficiencyCor::getDetectorGeometry(const Geometry::IDetector &det, double &detRadius, V3D &detAxis) {
295 std::shared_ptr<const IObject> shape_sptr = det.shape();
296 if (!shape_sptr->hasValidShape()) {
297 throw Exception::NotFoundError("Shape", "Detector has no shape");
298 }
299
300 std::map<const Geometry::IObject *, std::pair<double, Kernel::V3D>>::const_iterator it =
301 m_shapeCache.find(shape_sptr.get());
302 if (it == m_shapeCache.end()) {
303 double xDist = distToSurface(V3D(DIST_TO_UNIVERSE_EDGE, 0, 0), shape_sptr.get());
304 double zDist = distToSurface(V3D(0, 0, DIST_TO_UNIVERSE_EDGE), shape_sptr.get());
305 if (withinAbsoluteDifference(zDist, xDist, 1e-6)) {
306 detRadius = zDist / 2.0;
307 detAxis = V3D(0, 1, 0);
308 // assume radi in z and x and the axis is in the y
309 PARALLEL_CRITICAL(deteff_shapecachea) {
310 m_shapeCache.emplace(shape_sptr.get(), std::make_pair(detRadius, detAxis));
311 }
312 return;
313 }
314 double yDist = distToSurface(V3D(0, DIST_TO_UNIVERSE_EDGE, 0), shape_sptr.get());
315 if (withinAbsoluteDifference(yDist, zDist, 1e-8)) {
316 detRadius = yDist / 2.0;
317 detAxis = V3D(1, 0, 0);
318 // assume that y and z are radi of the cylinder's circular cross-section
319 // and the axis is perpendicular, in the x direction
320 PARALLEL_CRITICAL(deteff_shapecacheb) {
321 m_shapeCache.emplace(shape_sptr.get(), std::make_pair(detRadius, detAxis));
322 }
323 return;
324 }
325
326 if (withinAbsoluteDifference(xDist, yDist, 1e-8)) {
327 detRadius = xDist / 2.0;
328 detAxis = V3D(0, 0, 1);
329 PARALLEL_CRITICAL(deteff_shapecachec) {
330 m_shapeCache.emplace(shape_sptr.get(), std::make_pair(detRadius, detAxis));
331 }
332 return;
333 }
334 } else {
335 std::pair<double, V3D> geometry = it->second;
336 detRadius = geometry.first;
337 detAxis = geometry.second;
338 }
339}
340
352double DetectorEfficiencyCor::distToSurface(const V3D &start, const IObject *shape) const {
353 // get a vector from the point that was passed to the origin
354 const V3D direction = normalize(-start);
355 // put the point and the vector (direction) together to get a line, here
356 // called a track
357 Track track(start, direction);
358 // split the track (line) up into the part that is inside the shape and the
359 // part that is outside
360 shape->interceptSurface(track);
361
362 if (track.count() != 1) { // the track missed the shape, probably the shape is
363 // not centered on the origin
364 throw std::invalid_argument("Fatal error interpreting the shape of a detector");
365 }
366 // the first part of the track will be the part inside the shape, return its
367 // length
368 return track.cbegin()->distInsideObject;
369}
370
377double DetectorEfficiencyCor::detectorEfficiency(const double alpha) const {
378 if (alpha < 9.0) {
379 return 0.25 * M_PI * alpha * chebevApprox(0.0, 10.0, c_eff_f, alpha);
380 }
381 if (alpha > 10.0) {
382 double y = 1.0 - 18.0 / alpha;
383 return 1.0 - chebevApprox(-1.0, 1.0, c_eff_g, y) / (alpha * alpha);
384 }
385 double eff_f = 0.25 * M_PI * alpha * chebevApprox(0.0, 10.0, c_eff_f, alpha);
386 double y = 1.0 - 18.0 / alpha;
387 double eff_g = 1.0 - chebevApprox(-1.0, 1.0, c_eff_g, y) / (alpha * alpha);
388 return (10.0 - alpha) * eff_f + (alpha - 9.0) * eff_g;
389}
390
402double DetectorEfficiencyCor::chebevApprox(double a, double b, const double exspansionCoefs[], double x) const {
403 double d = 0.0;
404 double dd = 0.0;
405 double y = (2.0 * x - a - b) / (b - a);
406 double y2 = 2.0 * y;
407 for (int j = NUMCOEFS - 1; j > 0; j -= 1) {
408 double sv = d;
409 d = y2 * d - dd + exspansionCoefs[j];
410 dd = sv;
411 }
412 return y * d - dd + 0.5 * exspansionCoefs[0];
413}
414
420void DetectorEfficiencyCor::logErrors(size_t totalNDetectors) const {
421 std::vector<int>::size_type nspecs = m_spectraSkipped.size();
422 if (!m_spectraSkipped.empty()) {
423 g_log.warning() << "There were " << nspecs
424 << " spectra that could not be corrected out of total: " << totalNDetectors << '\n';
425 g_log.warning() << "Their spectra were nullified\n";
426 g_log.debug() << " Nullified spectra numbers: ";
427 auto itend = m_spectraSkipped.end();
428 for (auto it = m_spectraSkipped.begin(); it != itend; ++it) {
429 g_log.debug() << *it << " ";
430 }
431 g_log.debug() << "\n";
432 }
433}
434
435} // namespace Mantid::Algorithms
#define DECLARE_ALGORITHM(classname)
Definition Algorithm.h:538
double energy
Definition GetAllEi.cpp:157
const double DIST_TO_UNIVERSE_EDGE
std::map< DeltaEMode::Type, std::string > index
#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 progress(double p, const std::string &msg="", double estimatedTime=0.0, int progressPrecision=0)
Sends ProgressNotification.
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 hasDetectors(const size_t index) const
Returns true if the spectrum is associated with detectors in the instrument.
bool isMasked(const size_t index) const
Returns true if the detector(s) associated with the spectrum are masked.
const SpectrumDefinition & spectrumDefinition(const size_t index) const
Returns a const reference to the SpectrumDefinition of the spectrum.
A property class for workspaces.
A validator which checks that the unit of the workspace referred to by a WorkspaceProperty is the exp...
void getDetectorGeometry(const Geometry::IDetector &det, double &detRadius, Kernel::V3D &detAxis)
Sets the detector geometry cache if necessary.
double calculateOneOverK(double loBinBound, double uppBinBound) const
Calculate one over the wave vector for 2 bin bounds.
double distToSurface(const Kernel::V3D &start, const Geometry::IObject *shape) const
Computes the distance to the given shape from a starting point.
double m_ki
stores the wave number of incidient neutrons, calculated from the energy
void exec() override
Executes the algorithm.
std::list< int64_t > m_spectraSkipped
The spectra numbers that were skipped.
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...
API::MatrixWorkspace_const_sptr m_inputWS
the user selected workspace
API::MatrixWorkspace_sptr m_outputWS
output workspace, maybe the same as the input one
double m_Ei
stores the user selected value for incidient energy of the neutrons
void init() override
Declare algorithm properties.
void retrieveProperties()
Retrieve algorithm properties.
double detectorEfficiency(const double alpha) const
Computes the detector efficiency for a given paramater.
double chebevApprox(double a, double b, const double exspansionCoefs[], double x) const
Computes an approximate expansion of a Chebysev polynomial.
const Geometry::ParameterMap * m_paraMap
points the map that stores additional properties for detectors in that map
void correctForEfficiency(int64_t spectraIn, const API::SpectrumInfo &spectrumInfo)
Correct the given spectra index for efficiency.
void logErrors(size_t totalNDetectors) const
Log any errors with spectra that occurred.
Interface class for detector objects.
Definition IDetector.h:43
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
std::shared_ptr< Parameter > getRecursive(const IComponent *comp, const std::string &name, const std::string &type="") const
Use get() recursively to see if can find param in all parents of comp and given type (std::string ver...
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
Exception for when an item is not found in a collection.
Definition Exception.h:145
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
Class for quaternions.
Definition Quat.h:39
void rotate(V3D &) const
Rotate a vector.
Definition Quat.cpp:397
std::vector< double > getRotation(bool check_normalisation=false, bool throw_on_errors=false) const
returns the rotation matrix defined by this quaternion as an 9-point
Definition Quat.cpp:453
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< Parameter > Parameter_sptr
Typedef for the shared pointer.
Definition Parameter.h:194
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
constexpr double EMPTY_DBL() noexcept
Returns what we consider an "empty" double within a property.
Definition EmptyValues.h:42
@ Input
An input workspace.
Definition Property.h:53
@ Output
An output workspace.
Definition Property.h:54