Mantid
Loading...
Searching...
No Matches
SolidAngle.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
21#include <atomic>
22
23namespace Mantid::Algorithms {
24
25// Register with the algorithm factory
26DECLARE_ALGORITHM(SolidAngle)
27
28namespace SolidAngleMethods {
29static const std::string GENERIC_SHAPE = "GenericShape";
30static const std::string RECTANGLE = "Rectangle";
31static const std::string VERTICAL_TUBE = "VerticalTube";
32static const std::string HORIZONTAL_TUBE = "HorizontalTube";
33static const std::string VERTICAL_WING = "VerticalWing";
34static const std::string HORIZONTAL_WING = "HorizontalWing";
35} // namespace SolidAngleMethods
36
37using namespace Kernel;
38using namespace API;
39using namespace Geometry;
40using namespace SolidAngleMethods;
41
42namespace SolidAngleHelpers {
43
44constexpr double MM_TO_METERS = 1. / 1000.;
45
54 : m_detectorInfo(detectorInfo), m_samplePos(detectorInfo.samplePosition()) {}
55 double getAlpha(size_t index) const {
56 const auto sampleDetVec = m_detectorInfo.position(index) - m_samplePos;
57 auto inPlane = sampleDetVec;
58 project(inPlane);
59 return sampleDetVec.cosAngle(inPlane);
60 }
61 virtual void project(V3D &v) const = 0;
62 virtual ~AlphaAngleCalculator() = default;
63
64private:
67};
68
71 void project(V3D &v) const override { v.setY(0.0); }
72};
73
76 void project(V3D &v) const override { v.setX(0.0); }
77};
78
83 SolidAngleCalculator(const ComponentInfo &componentInfo, const DetectorInfo &detectorInfo, const std::string &method,
84 const double pixelArea)
85 : m_componentInfo(componentInfo), m_detectorInfo(detectorInfo), m_pixelArea(pixelArea),
86 m_samplePos(detectorInfo.samplePosition()), m_beamLine(m_samplePos - detectorInfo.sourcePosition()) {
87 if (method.find("Vertical") != std::string::npos) {
88 m_alphaAngleCalculator = std::make_unique<AlphaAngleVertical>(detectorInfo);
89 } else if (method.find("Horizontal") != std::string::npos) {
90 m_alphaAngleCalculator = std::make_unique<AlphaAngleHorizontal>(detectorInfo);
91 }
92 }
93 virtual double solidAngle(size_t index) const = 0;
94 virtual ~SolidAngleCalculator() = default;
95
96protected:
99 const double m_pixelArea;
102 std::unique_ptr<const AlphaAngleCalculator> m_alphaAngleCalculator;
103};
104
107 GenericShape(const ComponentInfo &componentInfo, const DetectorInfo &detectorInfo, const std::string &method,
108 const double pixelArea, const int numberOfCylinderSlices)
109 : SolidAngleCalculator(componentInfo, detectorInfo, method, pixelArea),
110 m_numberOfCylinderSlices(numberOfCylinderSlices) {}
114
115private:
117};
118
121 double solidAngle(size_t index) const override {
122 const V3D sampleDetVec = m_detectorInfo.position(index) - m_samplePos;
123 const double cosTheta = sampleDetVec.cosAngle(m_beamLine);
124 const double l2 = m_detectorInfo.l2(index);
125 const V3D scaleFactor = m_componentInfo.scaleFactor(index);
126 const double scaledPixelArea = m_pixelArea * scaleFactor[0] * scaleFactor[1];
127 return scaledPixelArea * cosTheta / (l2 * l2);
128 }
129};
130
131struct Tube : public SolidAngleCalculator {
133 double solidAngle(size_t index) const override {
134 const double cosAlpha = m_alphaAngleCalculator->getAlpha(index);
135 const double l2 = m_detectorInfo.l2(index);
136 const V3D scaleFactor = m_componentInfo.scaleFactor(index);
137 const double scaledPixelArea = m_pixelArea * scaleFactor[0] * scaleFactor[1];
138 return scaledPixelArea * cosAlpha / (l2 * l2);
139 }
140};
141
142struct Wing : public SolidAngleCalculator {
144 double solidAngle(size_t index) const override {
145 const V3D sampleDetVec = m_detectorInfo.position(index) - m_samplePos;
146 const double cosTheta = sampleDetVec.cosAngle(m_beamLine);
147 const double cosAlpha = m_alphaAngleCalculator->getAlpha(index);
148 const double l2 = m_detectorInfo.l2(index);
149 const V3D scaleFactor = m_componentInfo.scaleFactor(index);
150 const double scaledPixelArea = m_pixelArea * scaleFactor[0] * scaleFactor[1];
151 return scaledPixelArea * cosAlpha * cosAlpha * cosAlpha / (l2 * l2 * cosTheta * cosTheta);
152 }
153};
154
155} // namespace SolidAngleHelpers
156
159 declareProperty(std::make_unique<WorkspaceProperty<API::MatrixWorkspace>>("InputWorkspace", "", Direction::Input,
160 std::make_shared<InstrumentValidator>()),
161 "This workspace is used to identify the instrument to use "
162 "and also which\n"
163 "spectra to create a solid angle for. If the Max and Min "
164 "spectra values are\n"
165 "not provided one solid angle will be created for each "
166 "spectra in the input\n"
167 "workspace");
168 declareProperty(std::make_unique<WorkspaceProperty<API::MatrixWorkspace>>("OutputWorkspace", "", Direction::Output),
169 "The name of the workspace to be created as the output of "
170 "the algorithm. A workspace of this name will be created "
171 "and stored in the Analysis Data Service.");
172
173 auto mustBePositive = std::make_shared<BoundedValidator<int>>();
174 mustBePositive->setLower(0);
175 declareProperty("StartWorkspaceIndex", 0, mustBePositive,
176 "The index number of the first spectrum for which to find "
177 "the solid angle\n"
178 "(default: 0)");
179 declareProperty("EndWorkspaceIndex", EMPTY_INT(), mustBePositive,
180 "The index of the last spectrum whose solid angle is to be "
181 "found (default: the\n"
182 "last spectrum in the workspace)");
183
184 const std::vector<std::string> methods{GENERIC_SHAPE, RECTANGLE, VERTICAL_TUBE,
186 declareProperty("Method", GENERIC_SHAPE, std::make_shared<StringListValidator>(methods),
187 "Select the method to calculate the Solid Angle.");
188
189 auto greaterThanTwo = std::make_shared<BoundedValidator<int>>();
190 greaterThanTwo->setLower(3);
191 declareProperty("NumberOfCylinderSlices", 10, greaterThanTwo,
192 "The number of angular slices used when triangulating a cylinder in order to calculate the solid "
193 "angle of a tube detector.");
194}
195
199 // Get the workspaces
200 MatrixWorkspace_const_sptr inputWS = getProperty("InputWorkspace");
201 int m_MinSpec = getProperty("StartWorkspaceIndex");
202 int m_MaxSpec = getProperty("EndWorkspaceIndex");
203
204 const auto numberOfSpectra = static_cast<int>(inputWS->getNumberHistograms());
205
206 // Check 'StartSpectrum' is in range 0-numberOfSpectra
207 if (m_MinSpec > numberOfSpectra) {
208 g_log.warning("StartWorkspaceIndex out of range! Set to 0.");
209 m_MinSpec = 0;
210 }
211 if (isEmpty(m_MaxSpec))
212 m_MaxSpec = numberOfSpectra - 1;
213 if (m_MaxSpec > numberOfSpectra - 1 || m_MaxSpec < m_MinSpec) {
214 g_log.warning("EndWorkspaceIndex out of range! Set to max detector number");
215 m_MaxSpec = numberOfSpectra - 1;
216 }
217
218 MatrixWorkspace_sptr outputWS = WorkspaceFactory::Instance().create(inputWS, numberOfSpectra, 2, 1);
219 // The result of this will be a distribution
220 outputWS->setDistribution(true);
221 outputWS->setYUnit("");
222 outputWS->setYUnitLabel("Steradian");
223 setProperty("OutputWorkspace", outputWS);
224
225 const auto &spectrumInfo = inputWS->spectrumInfo();
226 const auto &detectorInfo = inputWS->detectorInfo();
227 const auto &componentInfo = inputWS->componentInfo();
228
229 // this is the pixel area that is supposed to be constant for the whole
230 // instrument this is used only if Method != GenericShape
231 double pixelArea = 0.;
232 const std::string method = getProperty("Method");
233
234 using namespace SolidAngleHelpers;
235 if (method != GENERIC_SHAPE) {
236 const auto instrument = inputWS->getInstrument();
237 if (instrument->hasParameter("x-pixel-size") && instrument->hasParameter("y-pixel-size")) {
238 const double pixelSizeX = instrument->getNumberParameter("x-pixel-size")[0] * MM_TO_METERS;
239 const double pixelSizeY = instrument->getNumberParameter("y-pixel-size")[0] * MM_TO_METERS;
240 pixelArea = pixelSizeX * pixelSizeY; // l2 is retrieved per pixel
241 } else {
242 // TODO: try to get the pixel sizes from bounding box
243 throw std::runtime_error("Missing necessary instrument parameters for non generic shape: "
244 "x-pixel-size and y-pixel-size [in mm].");
245 }
246 }
247
248 int numberOfCylinderSlices = getProperty("NumberOfCylinderSlices");
249 std::unique_ptr<SolidAngleCalculator> solidAngleCalculator;
250 if (method == GENERIC_SHAPE) {
251 solidAngleCalculator =
252 std::make_unique<GenericShape>(componentInfo, detectorInfo, method, pixelArea, numberOfCylinderSlices);
253 } else if (method == RECTANGLE) {
254 solidAngleCalculator = std::make_unique<Rectangle>(componentInfo, detectorInfo, method, pixelArea);
255 } else if (method == VERTICAL_TUBE || method == HORIZONTAL_TUBE) {
256 solidAngleCalculator = std::make_unique<Tube>(componentInfo, detectorInfo, method, pixelArea);
257 } else if (method == VERTICAL_WING || method == HORIZONTAL_WING) {
258 solidAngleCalculator = std::make_unique<Wing>(componentInfo, detectorInfo, method, pixelArea);
259 }
260
261 std::atomic<size_t> failCount{0};
262 Progress prog(this, 0.0, 1.0, numberOfSpectra);
263 // Loop over the histograms (detector spectra)
264 PARALLEL_FOR_IF(Kernel::threadSafe(*outputWS, *inputWS))
265 for (int j = m_MinSpec; j <= m_MaxSpec; ++j) {
267 initSpectrum(*inputWS, *outputWS, j);
268 if (spectrumInfo.hasDetectors(j)) {
269 double solidAngle = 0.0;
270 for (const auto detID : inputWS->getSpectrum(j).getDetectorIDs()) {
271 const auto index = detectorInfo.indexOf(detID);
272 if (!detectorInfo.isMasked(index) && !detectorInfo.isMonitor(index)) {
273 solidAngle += solidAngleCalculator->solidAngle(index);
274 }
275 }
276 outputWS->mutableY(j)[0] = solidAngle;
277 } else {
278 ++failCount;
279 }
280 prog.report();
282 } // loop over spectra
284 g_log.warning() << "min max total" << m_MinSpec << " " << m_MaxSpec << " " << numberOfSpectra;
285
286 auto &outputSpectrumInfo = outputWS->mutableSpectrumInfo();
287 // Loop over the histograms (detector spectra)
288 for (int j = 0; j < m_MinSpec; ++j) {
289 initSpectrum(*inputWS, *outputWS, j);
290 // SpectrumInfo::setMasked is NOT threadsafe.
291 outputSpectrumInfo.setMasked(j, true);
292 prog.report();
293 } // loop over spectra
294
295 // Loop over the histograms (detector spectra)
296 for (int j = m_MaxSpec + 1; j < numberOfSpectra; ++j) {
297 initSpectrum(*inputWS, *outputWS, j);
298 // SpectrumInfo::setMasked is NOT threadsafe.
299 outputSpectrumInfo.setMasked(j, true);
300 prog.report();
301 } // loop over spectra
302
303 if (failCount != 0) {
304 g_log.information() << "Unable to calculate solid angle for " << failCount
305 << " spectra. The solid angle will be set to zero for "
306 "those detectors.\n";
307 }
308}
309
314void SolidAngle::initSpectrum(const MatrixWorkspace &inputWS, MatrixWorkspace &outputWS, const size_t wsIndex) {
315 outputWS.mutableX(wsIndex)[0] = inputWS.x(wsIndex).front();
316 outputWS.mutableX(wsIndex)[1] = inputWS.x(wsIndex).back();
317 outputWS.mutableE(wsIndex) = 0.;
318 outputWS.mutableY(wsIndex) = 0.; // default value for not calculated
319}
320
321} // namespace Mantid::Algorithms
#define DECLARE_ALGORITHM(classname)
Definition Algorithm.h:538
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_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.
void declareProperty(std::unique_ptr< Kernel::Property > p, const std::string &doc="") override
Add a property to the list of managed properties.
TypedValue getProperty(const std::string &name) const override
Get the value of a property.
Kernel::Logger & g_log
Definition Algorithm.h:422
static bool isEmpty(const NumT toCheck)
checks that the value was not set by users, uses the value in empty double/int.
Base MatrixWorkspace Abstract Class.
HistogramData::HistogramX & mutableX(const size_t index) &
const HistogramData::HistogramX & x(const size_t index) const
HistogramData::HistogramE & mutableE(const size_t index) &
HistogramData::HistogramY & mutableY(const size_t index) &
Helper class for reporting progress from algorithms.
Definition Progress.h:25
A property class for workspaces.
void initSpectrum(const API::MatrixWorkspace &input, API::MatrixWorkspace &output, const size_t j)
SolidAngle::initSpectrum Sets the default value for the spectra for which solid angle is not calculat...
void exec() override
Executes the algorithm.
void init() override
Initialisation method.
ComponentInfo : Provides a component centric view on to the instrument.
Kernel::V3D scaleFactor(const size_t componentIndex) const
Geometry::DetectorInfo is an intermediate step towards a DetectorInfo that is part of Instrument-2....
double l2(const size_t index) const
Returns L2 (distance from sample to spectrum).
Kernel::V3D position(const size_t index) const
Returns the position of the detector with given index.
const Geometry::IDetector & detector(const size_t index) const
Return a const reference to the detector with given index.
virtual double solidAngle(const Geometry::SolidAngleParams &params) const =0
Finds the approximate solid angle covered by the component when viewed from the point given.
IPropertyManager * setProperty(const std::string &name, const T &value)
Templated method to set the value of a PropertyWithValue.
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
void report()
Increments the loop counter by 1, then sends the progress notification on behalf of its algorithm.
static T & Instance()
Return a reference to the Singleton instance, creating it if it does not already exist Creation is do...
Class for 3D vectors.
Definition V3D.h:34
void setX(const double xx) noexcept
Set is x position.
Definition V3D.h:224
double cosAngle(const V3D &) const
cos(Angle) between this and another vector
Definition V3D.cpp:177
void setY(const double yy) noexcept
Set is y position.
Definition V3D.h:230
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
static const std::string HORIZONTAL_WING
static const std::string VERTICAL_TUBE
static const std::string GENERIC_SHAPE
static const std::string HORIZONTAL_TUBE
static const std::string RECTANGLE
static const std::string VERTICAL_WING
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.
constexpr int EMPTY_INT() noexcept
Returns what we consider an "empty" integer within a property.
Definition EmptyValues.h:24
Returns the angle between the sample-to-pixel vector and its projection on the X-Z (vertical tube) or...
double solidAngle(size_t index) const override
GenericShape(const ComponentInfo &componentInfo, const DetectorInfo &detectorInfo, const std::string &method, const double pixelArea, const int numberOfCylinderSlices)
double solidAngle(size_t index) const override
Creates the solid angle calculator based on the selected method.
std::unique_ptr< const AlphaAngleCalculator > m_alphaAngleCalculator
virtual double solidAngle(size_t index) const =0
SolidAngleCalculator(const ComponentInfo &componentInfo, const DetectorInfo &detectorInfo, const std::string &method, const double pixelArea)
double solidAngle(size_t index) const override
double solidAngle(size_t index) const override
@ Input
An input workspace.
Definition Property.h:53
@ Output
An output workspace.
Definition Property.h:54