Mantid
Loading...
Searching...
No Matches
DetectorSearcher.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 +
15#include "MantidKernel/Logger.h"
17
18#include <queue>
19#include <tuple>
20
23using namespace Mantid;
24using namespace Mantid::API;
25
26namespace {
27Kernel::Logger g_log("DetectorSearcher");
28
37bool containsOnlyRectDetectors(const Geometry::ComponentInfo &compInfo, const Geometry::DetectorInfo &detInfo) {
38 using Beamline::ComponentType;
39 std::queue<size_t> compQueue;
40 for (auto const child : compInfo.children(compInfo.root()))
41 compQueue.push(child);
42
43 bool foundRect = false;
44 while (!compQueue.empty()) {
45 size_t const index = compQueue.front();
46 compQueue.pop();
47
48 if ((compInfo.hasSource() && index == compInfo.source()) || (compInfo.hasSample() && index == compInfo.sample()))
49 continue;
50 if (compInfo.isDetector(index) && detInfo.isMonitor(index))
51 continue;
52 // skip choppers, slits and supermirrors - HACK!
53 auto const &name = compInfo.name(index);
54 if (name == "chopper-position" || name.substr(0, 4) == "slit" || name == "supermirror")
55 continue;
56
57 auto const type = compInfo.componentType(index);
58 if (type == ComponentType::Rectangular) {
59 foundRect = true;
60 } else if (type == ComponentType::Unstructured || type == ComponentType::Grid ||
61 type == ComponentType::Structured || type == ComponentType::OutlineComposite) {
62 // An assembly, so check its children
63 for (auto const child : compInfo.children(index))
64 compQueue.push(child);
65 } else {
66 // Found a non-rectangular detector or component
67 return false;
68 }
69 }
70 return foundRect;
71}
72} // namespace
73
74double getQSign() {
75 const auto convention = Kernel::ConfigService::Instance().getString("Q.convention");
76 return (convention == "Crystallography") ? -1.0 : 1.0;
77}
78
88 : m_usingFullRayTrace(containsOnlyRectDetectors(compInfo, detInfo)), m_crystallography_convention(getQSign()),
89 m_compInfo(compInfo), m_detInfo(detInfo) {
90
91 /* Choose the search strategy to use
92 * If the instrument uses rectangular detectors (e.g. TOPAZ) then it is faster
93 * to run a full ray trace starting from the top of the instrument. This is
94 * due to the speed up of looking up a single pixel in the rectangular
95 * detector.
96 *
97 * If the instrument does not use rectangular detectors (e.g. WISH, CORELLI)
98 * then it is faster to use a nearest neighbour search to find the closest
99 * pixels, then check them for intersection.
100 * */
101 if (!m_usingFullRayTrace) {
103 } else {
104 m_rayTracer = std::make_unique<InstrumentRayTracer>(compInfo, detInfo);
105 }
106}
107
111 std::vector<Eigen::Vector3d> points;
112 points.reserve(m_detInfo.size());
113 m_indexMap.reserve(m_detInfo.size());
114
115 const auto frame = m_compInfo.referenceFrame();
116 auto beam = frame->vecPointingAlongBeam();
117 auto up = frame->vecPointingUp();
118
119 for (size_t pointNo = 0; pointNo < m_detInfo.size(); ++pointNo) {
120 if (m_detInfo.isMonitor(pointNo) || m_detInfo.isMasked(pointNo))
121 continue; // detector is a monitor or masked so don't use
122
123 // Calculate a unit Q vector for each detector
124 // This follows a method similar to that used in IntegrateEllipsoids
125 const auto pos = normalize(m_detInfo.position(pointNo));
126 auto E1 = (pos - beam) * -m_crystallography_convention;
127 const auto norm = E1.norm();
128 if (norm == 0.) {
130 } else {
131 E1 /= norm;
132 }
133
134 Eigen::Vector3d point(E1[0], E1[1], E1[2]);
135
136 // Ignore nonsensical points
137 if (point.hasNaN() || up.coLinear(beam, pos))
138 continue;
139
140 points.emplace_back(point);
141 m_indexMap.emplace_back(pointNo);
142 }
143
144 // create KDtree of cached detector Q vectors
145 m_detectorCacheSearch = std::make_unique<Kernel::NearestNeighbours<3>>(points);
146}
147
156 // quick check to see if this Q is valid
157 if (q.nullVector())
158 return std::make_tuple(false, 0);
159
160 // search using best strategy for current instrument
163 } else {
165 }
166}
167
177 const auto direction = convertQtoDirection(q);
178 m_rayTracer->traceFromSample(direction);
179 auto const detIndex = m_rayTracer->getDetectorResult();
180
181 if (detIndex == Geometry::ComponentInfo::invalidIndex || m_detInfo.isMasked(detIndex))
182 return std::make_tuple(false, 0);
183
184 return std::make_tuple(true, detIndex);
185}
186
196 const auto detectorDir = convertQtoDirection(q);
197 // find where this Q vector should intersect with "extended" space
198 // NOTE: increase the Neighbors from 11 to 21 to cover a wide extended space
199 const auto neighbours = m_detectorCacheSearch->findNearest(Eigen::Vector3d(q[0], q[1], q[2]), 21);
200
201 // check if neighboring is empty
202 if (neighbours.empty()) {
203 g_log.information() << "empty neighbor list for given Q\n";
204 return std::make_tuple(false, 0);
205 }
206
207 const auto result = checkInteceptWithNeighbours(detectorDir, neighbours);
208 const auto hitDetector = std::get<0>(result);
209 const auto index = std::get<1>(result);
210
211 if (hitDetector) {
212 return std::make_tuple(true, m_indexMap[index]);
213 } else if (m_compInfo.hasParameter(m_compInfo.root(), "tube-gap")) {
214 // Tube Gap Parameter specifically applies to tube instruments
215 return handleTubeGap(detectorDir, neighbours);
216 }
217
218 return std::make_tuple(false, 0);
219}
220
233 std::vector<double> gaps = m_compInfo.getNumberParameter(m_compInfo.root(), "tube-gap", true);
234 if (!gaps.empty()) {
235 const auto gap = static_cast<double>(gaps.front());
236 // try adding and subtracting tube-gap in 3 q dimensions to see if you can
237 // find detectors on each side of tube gap
238 for (int i = 0; i < 3; i++) {
239 auto gapDir = V3D(0., 0., 0.);
240 gapDir[i] = gap;
241
242 auto beam1 = normalize(detectorDir + gapDir);
243 const auto result1 = checkInteceptWithNeighbours(beam1, neighbours);
244 const auto hit1 = std::get<0>(result1);
245
246 const auto beam2 = normalize(detectorDir - gapDir);
247 const auto result2 = checkInteceptWithNeighbours(beam2, neighbours);
248 const auto hit2 = std::get<0>(result2);
249
250 if (hit1 && hit2) {
251 // Set the detector to one of the neighboring pixels
252 return std::make_tuple(true, m_indexMap[std::get<1>(result1)]);
253 }
254 }
255 }
256
257 return std::make_tuple(false, 0);
258}
259
268 const V3D &direction, const Kernel::NearestNeighbours<3>::NearestNeighbourResults &neighbours) const {
269 Geometry::Track track(m_detInfo.samplePosition(), direction);
270 // Find which of the neighbours we actually intersect with
271 for (const auto &neighbour : neighbours) {
272 const auto index = std::get<1>(neighbour);
273 const auto detIndex = m_indexMap[index];
274
276 if (!bb.doesLineIntersect(track))
277 continue;
278
279 if (!m_compInfo.hasValidShape(detIndex))
280 throw Kernel::Exception::NullPointerException("DetectorSearcher::checkInteceptWithNeighbours", "shape");
281
282 // Express the track in the detector shape's frame: remove the detector position and rotation
283 auto unRotate = m_detInfo.rotation(detIndex);
284 unRotate.inverse();
285 auto trackStart = track.startPoint() - m_detInfo.position(detIndex);
286 unRotate.rotate(trackStart);
287 auto trackDirection = track.direction();
288 unRotate.rotate(trackDirection);
289 Geometry::Track probeTrack(trackStart, trackDirection);
290
291 const auto hitDetector = m_compInfo.shape(detIndex).interceptSurface(probeTrack) > 0;
292 if (hitDetector)
293 return std::make_tuple(hitDetector, index);
294 }
295
296 return std::make_tuple(false, 0);
297}
298
305 const auto norm_q = q.norm();
306 const auto refFrame = m_compInfo.referenceFrame();
307 const V3D refBeamDir = refFrame->vecPointingAlongBeam();
308
309 const double qBeam = q.scalar_prod(refBeamDir) * m_crystallography_convention;
310 double one_over_wl = (norm_q * norm_q) / (2.0 * qBeam);
311
312 auto detectorDir = q * -m_crystallography_convention;
313 detectorDir[refFrame->pointingAlongBeam()] = one_over_wl - qBeam;
314 detectorDir.normalize();
315 return detectorDir;
316}
std::string name
Definition Run.cpp:60
double getQSign()
std::map< DeltaEMode::Type, std::string > index
DetectorSearcher(const Geometry::ComponentInfo &compInfo, const Geometry::DetectorInfo &detInfo)
Create a new DetectorSearcher with the given instrument & detectors.
DetectorSearchResult searchUsingNearestNeighbours(const Kernel::V3D &q)
Attempt to find a detector using a nearest neighbours search strategy.
DetectorSearchResult handleTubeGap(const Kernel::V3D &detectorDir, const Kernel::NearestNeighbours< 3 >::NearestNeighbourResults &neighbours)
Helper function to handle the tube gap parameter in tube instruments.
DetectorSearchResult findDetectorIndex(const Kernel::V3D &q)
Find a detector that intsects with the given Qlab vector.
std::tuple< bool, size_t > DetectorSearchResult
Search result type representing whether a detector was found and if so which detector index it was.
const double m_crystallography_convention
flag for whether the crystallography convention is to be used
std::unique_ptr< Geometry::InstrumentRayTracer > m_rayTracer
instrument ray tracer object for searching in rectangular detectors
DetectorSearchResult searchUsingInstrumentRayTracing(const Kernel::V3D &q)
Attempt to find a detector using a full instrument ray tracing strategy.
std::unique_ptr< Kernel::NearestNeighbours< 3 > > m_detectorCacheSearch
Detector search cache for fast look-up of detectors.
std::vector< size_t > m_indexMap
vector of detector indicies used in the search
Kernel::V3D convertQtoDirection(const Kernel::V3D &q) const
Helper function to convert a Qlab vector to a direction in detector space.
const Geometry::ComponentInfo & m_compInfo
component info for the instrument
std::tuple< bool, size_t > checkInteceptWithNeighbours(const Kernel::V3D &direction, const Kernel::NearestNeighbours< 3 >::NearestNeighbourResults &neighbours) const
Check whether the given direction in detector space intercepts with a detector.
const bool m_usingFullRayTrace
flag for whether to use InstrumentRayTracer or NearestNeighbours
const Geometry::DetectorInfo & m_detInfo
detector info for the instrument
void createDetectorCache()
Helper function to build the nearest neighbour tree.
A simple structure that defines an axis-aligned cuboid shaped bounding box for a geometrical object.
Definition BoundingBox.h:33
bool doesLineIntersect(const Track &track) const
Does a specified track intersect the bounding box.
ComponentInfo : Provides a component centric view on to the instrument.
std::shared_ptr< const ReferenceFrame > referenceFrame() const
The instrument's reference frame: which axes point up and along the beam, and its handedness.
bool hasParameter(const size_t componentIndex, const std::string &name, bool recursive=true) const
Returns empty (or false) if the ComponentInfo was built without parameters or the parameter is unset.
bool hasValidShape(const size_t componentIndex) const
std::vector< double > getNumberParameter(const size_t componentIndex, const std::string &name, bool recursive=true) const
bool isDetector(const size_t componentIndex) const
const std::string & name(const size_t componentIndex) const
static constexpr size_t invalidIndex
Returned by indexOfOrInvalid() for a component ID that is not part of this instrument.
const Geometry::IObject & shape(const size_t componentIndex) const
Beamline::ComponentType componentType(const size_t componentIndex) const
Geometry::DetectorInfo is an intermediate step towards a DetectorInfo that is part of Instrument-2....
Kernel::V3D samplePosition() const
Returns the sample position.
Kernel::Quat rotation(const size_t index) const
Returns the rotation of the detector with given index.
bool isMasked(const size_t index) const
Returns true if the detector is masked.
Kernel::V3D position(const size_t index) const
Returns the position of the detector with given index.
size_t size() const
Returns the size of the DetectorInfo, i.e., the number of detectors in the instrument.
bool isMonitor(const size_t index) const
Returns true if the detector is a monitor.
virtual int interceptSurface(Geometry::Track &) const =0
This class is responsible for tracking rays and accumulating a list of objects that are intersected a...
Defines a track as a start point and a direction.
Definition Track.h:165
const Kernel::V3D & startPoint() const
Returns the starting point.
Definition Track.h:191
const Kernel::V3D & direction() const
Returns the direction as a unit vector.
Definition Track.h:193
Exception thrown when an attempt is made to dereference a null pointer.
Definition Exception.h:305
The Logger class is in charge of the publishing messages from the framework through various channels.
Definition Logger.h:51
void information(const std::string &msg)
Logs at information level.
Definition Logger.cpp:136
std::vector< std::tuple< VectorType, size_t, double > > NearestNeighbourResults
void inverse()
Inverse a quaternion (in the sense of rotation inversion)
Definition Quat.cpp:376
Class for 3D vectors.
Definition V3D.h:35
constexpr double scalar_prod(const V3D &v) const noexcept
Calculates the cross product.
Definition V3D.h:281
double normalize()
Make a normalized vector (return norm value)
Definition V3D.cpp:129
void rotate(const Matrix< double > &) noexcept
Rotate a point by a matrix.
Definition V3D.cpp:214
double norm() const noexcept
Definition V3D.h:270
bool nullVector(const double tolerance=1e-3) const noexcept
Determine if the point is null.
Definition V3D.cpp:238
std::complex< double > MANTID_API_DLL E1(std::complex< double > z)
Integral for Gamma.
Kernel::Logger g_log("ExperimentInfo")
static logger object
MANTID_KERNEL_DLL V3D normalize(V3D v)
Normalizes a V3D.
Definition V3D.h:353
Helper class which provides the Collimation Length for SANS instruments.