25#include "MantidIndexing/IndexInfo.h"
27#include "MantidTypes/SpectrumDefinition.h"
29#include <boost/math/special_functions/pow.hpp>
31using boost::math::pow;
76 double minTwoTheta{std::numeric_limits<double>::max()};
77 double maxTwoTheta{std::numeric_limits<double>::lowest()};
78 for (
int width = 0; width < 2; ++width) {
79 const auto offset =
right *
static_cast<double>(width);
80 for (
const auto &pointInRing : capRing) {
81 auto point = pointInRing;
85 const auto current = twoThetaFromLocalPoint(detInfo, detInfoIndex, samplePos, beamDir, point);
86 minTwoTheta = std::min(minTwoTheta, current);
87 maxTwoTheta = std::max(maxTwoTheta, current);
90 const auto beltOffset =
right * 0.5;
91 for (
size_t beltIndex = 0; beltIndex < capRing.size(); beltIndex += 2) {
92 const auto point = capRing[beltIndex] + beltOffset;
93 const auto current = twoThetaFromLocalPoint(detInfo, detInfoIndex, samplePos, beamDir, point);
94 minTwoTheta = std::min(minTwoTheta, current);
95 maxTwoTheta = std::max(maxTwoTheta, current);
97 return std::make_pair(minTwoTheta, maxTwoTheta);
116 if (geometry.
axis.
X() != 0. && geometry.
axis.
Z() != 0) {
117 const auto inverseXZSumSq = 1. / (pow<2>(geometry.
axis.
X()) + pow<2>(geometry.
axis.
Z()));
118 basis1.
setX(std::sqrt(1. - pow<2>(geometry.
axis.
X()) * inverseXZSumSq));
119 basis1.setY(geometry.
axis.
X() * std::sqrt(inverseXZSumSq));
122 const std::array<double, 8> angles{
123 {0., 0.25 * M_PI, 0.5 * M_PI, 0.75 * M_PI, M_PI, 1.25 * M_PI, 1.5 * M_PI, 1.75 * M_PI}};
124 double minTwoTheta{std::numeric_limits<double>::max()};
125 double maxTwoTheta{std::numeric_limits<double>::lowest()};
126 for (
const double &angle : angles) {
127 const auto basePoint =
129 for (
int i = 0; i < 3; ++i) {
130 const auto point = basePoint + geometry.
axis * (0.5 * geometry.
height *
static_cast<double>(i));
131 const auto current = twoThetaFromLocalPoint(detInfo, detInfoIndex, samplePos, beamDir, point);
132 minTwoTheta = std::min(minTwoTheta, current);
133 maxTwoTheta = std::max(maxTwoTheta, current);
136 return std::make_pair(minTwoTheta, maxTwoTheta);
154 const auto maxPoint = bbox.
maxPoint();
155 auto side = maxPoint * sideDir;
156 return twoThetaFromLocalPoint(detInfo, detInfoIndex, samplePos, beamDir, side);
173 double minTwoTheta{std::numeric_limits<double>::max()};
174 double maxTwoTheta{std::numeric_limits<double>::lowest()};
175 const std::array<Mantid::Kernel::V3D, 6> dirs{
176 {
Mantid::Kernel::V3D{1., 0., 0.}, {0., 1., 0.}, {0., 0., 1.}, {-1., 0., 0.}, {0., -1., 0.}, {0., 0., -1.}}};
177 for (
const auto &dir : dirs) {
178 const auto current = twoThetaFromBoundingBox(compInfo, detInfo, detInfoIndex, samplePos, beamDir, dir);
179 minTwoTheta = std::min(minTwoTheta, current);
180 maxTwoTheta = std::max(maxTwoTheta, current);
182 return std::make_pair(minTwoTheta, maxTwoTheta);
198 const auto &shape = compInfo.
shape(detInfoIndex);
200 switch (shape.shape()) {
210 return generalTwoThetaRange(compInfo, detInfo, detInfoIndex, samplePos, beamDir);
221std::pair<double, double> twoThetasFromTable(
const Mantid::detid_t detID,
const std::vector<int> &detectorIDs,
222 const std::vector<double> &lowers,
const std::vector<double> &uppers) {
223 const auto range = std::equal_range(detectorIDs.cbegin(), detectorIDs.cend(),
static_cast<int>(detID));
224 if (std::distance(range.first, range.second) > 1) {
225 throw std::invalid_argument(
"Duplicate detector IDs in 'DetectorTwoThetaRanges'.");
227 if (range.first == detectorIDs.cend()) {
228 throw std::invalid_argument(
"No min/max 2thetas found for detector ID " +
std::to_string(detID));
230 const auto index = std::distance(detectorIDs.cbegin(), range.first);
231 const auto minmax = std::make_pair(lowers[
index], uppers[
index]);
232 if (minmax.first <= 0) {
233 throw std::invalid_argument(
"Non-positive min 2theta for detector ID " +
std::to_string(detID));
235 if (minmax.first > M_PI) {
236 throw std::invalid_argument(
"Min 2theta greater than pi for detector ID " +
std::to_string(detID));
238 if (minmax.second > M_PI) {
239 throw std::invalid_argument(
"Max 2theta greater than pi for detector ID" +
std::to_string(detID));
241 if (minmax.first >= minmax.second) {
242 throw std::invalid_argument(
"Min 2theta larger than max for detector ID " +
std::to_string(detID));
270 if (spectrumDefinition.size() == 1 || emodeProperties.
m_emode == 1 || emodeProperties.
m_efixedGiven) {
271 return emodeProperties.
getEFixed(componentInfo, spectrumDefinition[0].first);
273 std::string groupName;
274 for (
const auto &def : spectrumDefinition) {
275 groupName += componentInfo.
name(def.first) +
";";
277 throw std::runtime_error(
"Cannot find EFixed parameter for component \"" + groupName +
278 "\". This is required in indirect mode. Please check the IDF "
279 "contains these values.");
308 std::map<std::string, std::string> result;
311 result[
"InputWorkspace"] =
"InputWorkspace is of Incorrect type. Please "
312 "provide a MatrixWorkspace as the "
318 if (tableWS->columnCount() != 3) {
319 result[
"DetectorTwoThetaRanges"] =
"DetectorTwoThetaRanges requires 3 columns";
322 else if (!tableWS->getColumn(0)->isType<
int>()) {
323 result[
"DetectorTwoThetaRanges"] =
"The first column of DetectorTwoThetaRanges should be of type int";
326 else if (!tableWS->getColumn(1)->isType<
double>()) {
327 result[
"DetectorTwoThetaRanges"] =
"The second column of DetectorTwoThetaRanges should be of type "
331 else if (!tableWS->getColumn(2)->isType<
double>()) {
332 result[
"DetectorTwoThetaRanges"] =
"The third column of DetectorTwoThetaRanges should be of type double";
335 else if (tableWS->rowCount() != inputWS->getNumberHistograms()) {
336 result[
"DetectorTwoThetaRanges"] =
"The table and workspace do not have the same number of detectors";
352 g_log.
debug() <<
"Workspace type: " << outputWS->id() <<
'\n';
354 const size_t nEnergyBins = inputWS->blocksize();
355 const size_t nHistos = inputWS->getNumberHistograms();
358 std::vector<SpectrumDefinition> detIDMapping(outputWS->getNumberHistograms());
361 const size_t nreports(nHistos * nEnergyBins);
362 m_progress = std::make_unique<API::Progress>(
this, 0.0, 1.0, nreports);
369 std::vector<double> par = inputWS->getInstrument()->getNumberParameter(
"detector-neighbour-offset");
373 g_log.
debug() <<
"Offset: " << par[0] <<
'\n';
378 const auto &
X = inputWS->x(0);
380 const auto &inputIndices = inputWS->indexInfo();
381 const auto &spectrumInfo = inputWS->spectrumInfo();
382 const auto &componentInfo = inputWS->componentInfo();
385 for (int64_t i = 0; i < static_cast<int64_t>(nHistos); ++i) {
388 if (!spectrumInfo.hasDetectors(i) || spectrumInfo.isMasked(i) || spectrumInfo.isMonitor(i)) {
396 const auto specNo =
static_cast<specnum_t>(inputIndices.spectrumNumber(i));
397 std::stringstream logStream;
398 for (
size_t j = 0; j < nEnergyBins; ++j) {
399 m_progress->report(
"Computing polygon intersections");
402 const double dE_j =
X[j];
403 const double dE_jp1 =
X[j + 1];
408 const V2D lr(dE_jp1, lrQ);
411 if (
g_log.
is(Logger::Priority::PRIO_DEBUG)) {
412 logStream <<
"Spectrum=" << specNo <<
", lower theta=" << thetaLower *
rad2deg
413 <<
", upper theta=" << thetaUpper *
rad2deg <<
". QE polygon: ll=" << ll <<
", lr=" << lr
414 <<
", ur=" << ur <<
", ul=" << ul <<
"\n";
421 const MantidVec::difference_type qIndex = std::upper_bound(
m_Qout.begin(),
m_Qout.end(), lrQ) -
m_Qout.begin();
422 if (qIndex != 0 && qIndex <
static_cast<int>(
m_Qout.size())) {
428 detIDMapping[qIndex - 1].add(spectrumInfo.spectrumDefinition(i)[0].first);
432 if (
g_log.
is(Logger::Priority::PRIO_DEBUG)) {
441 outputWS->finalize();
445 auto outputIndices = outputWS->indexInfo();
446 outputIndices.setSpectrumDefinitions(std::move(detIDMapping));
447 outputWS->setIndexInfo(outputIndices);
452 replaceNans->setChild(
true);
453 replaceNans->initialize();
454 replaceNans->setProperty(
"InputWorkspace", outputWS);
455 replaceNans->setProperty(
"OutputWorkspace", outputWS);
456 replaceNans->setProperty(
"NaNValue", 0.0);
457 replaceNans->setProperty(
"InfinityValue", 0.0);
458 replaceNans->setProperty(
"BigNumberThreshold", DBL_MAX);
459 replaceNans->execute();
472 const size_t nhist =
workspace.getNumberHistograms();
473 constexpr double skipDetector{-1.};
478 const auto referenceFrame = inst->getReferenceFrame();
479 const auto beamDir = referenceFrame->vecPointingAlongBeam();
481 const auto &detectorInfo =
workspace.detectorInfo();
482 const auto &componentInfo =
workspace.componentInfo();
483 const auto &spectrumInfo =
workspace.spectrumInfo();
484 const auto samplePos = spectrumInfo.samplePosition();
485 for (
size_t i = 0; i < nhist; ++i) {
486 m_progress->report(
"Calculating detector angles");
489 if (!spectrumInfo.hasDetectors(i) || spectrumInfo.isMonitor(i)) {
496 }
catch (std::runtime_error &) {
500 const auto &spectrumDefinition = spectrumInfo.spectrumDefinition(i);
501 double minTwoTheta{spectrumInfo.twoTheta(i)};
502 double maxTwoTheta{minTwoTheta};
503 if (spectrumInfo.hasUniqueDetector(i)) {
504 const auto detInfoIndex = spectrumDefinition[0].first;
505 std::tie(minTwoTheta, maxTwoTheta) =
506 minMaxTwoTheta(componentInfo, detectorInfo, detInfoIndex, samplePos, beamDir);
508 for (
const auto &def : spectrumDefinition) {
509 const auto detInfoIndex = def.first;
510 const auto current = minMaxTwoTheta(componentInfo, detectorInfo, detInfoIndex, samplePos, beamDir);
511 minTwoTheta = std::min(minTwoTheta, current.first);
512 maxTwoTheta = std::max(maxTwoTheta, current.second);
517 if (
g_log.
is(Logger::Priority::PRIO_DEBUG)) {
532 const size_t nHistos =
workspace.getNumberHistograms();
534 bool ignoreMasked =
true;
535 const int numNeighbours = 4;
541 const auto &spectrumInfo =
workspace.spectrumInfo();
543 for (
size_t i = 0; i < nHistos; ++i) {
544 m_progress->report(
"Calculating detector angular widths");
547 if (!spectrumInfo.hasDetectors(i) || spectrumInfo.isMonitor(i)) {
555 double thetaWidth = std::numeric_limits<double>::lowest();
558 const double theta = spectrumInfo.twoTheta(i);
561 const specnum_t deltaMinus1 = inSpec - 1;
565 for (
auto const &neighbour : neighbours) {
567 if (spec == deltaPlus1 || spec == deltaMinus1 || spec == deltaPlusT || spec == deltaMinusT) {
568 const double theta_n = spectrumInfo.twoTheta(spec - 1) * 0.5;
570 const double dTheta = std::abs(theta - theta_n);
571 thetaWidth = std::max(thetaWidth, dTheta);
576 if (
g_log.
is(Logger::Priority::PRIO_DEBUG)) {
577 g_log.
debug() <<
"Detector at spectrum = " << inSpec <<
", width = " << thetaWidth *
rad2deg <<
" degrees\n";
584 constexpr double skipDetector{-1.};
585 const size_t nhist =
workspace.getNumberHistograms();
588 const auto &detIDs = angleTable.
getColVector<
int>(
"Detector ID");
589 const auto &lowers = angleTable.
getColVector<
double>(
"Min two theta");
590 const auto &uppers = angleTable.
getColVector<
double>(
"Max two theta");
591 const auto &spectrumInfo =
workspace.spectrumInfo();
592 const auto &detectorInfo =
workspace.detectorInfo();
593 const auto &componentInfo =
workspace.componentInfo();
594 for (
size_t i = 0; i < nhist; ++i) {
595 m_progress->report(
"Reading detector angles");
596 if (!spectrumInfo.hasDetectors(i) || spectrumInfo.isMonitor(i)) {
601 }
catch (std::runtime_error &) {
605 const auto &spectrumDefinition = spectrumInfo.spectrumDefinition(i);
606 if (spectrumInfo.hasUniqueDetector(i)) {
607 const auto twoThetas = twoThetasFromTable(spectrumInfo.firstDetectorID(i), detIDs, lowers, uppers);
611 double minTwoTheta{spectrumInfo.twoTheta(i)};
612 double maxTwoTheta{minTwoTheta};
613 for (
const auto &def : spectrumDefinition) {
614 const auto twoThetas = twoThetasFromTable(detectorInfo.detid(def.first), detIDs, lowers, uppers);
615 minTwoTheta = std::min(minTwoTheta, twoThetas.first);
616 maxTwoTheta = std::max(maxTwoTheta, twoThetas.second);
621 if (
g_log.
is(Logger::Priority::PRIO_DEBUG)) {
#define DECLARE_ALGORITHM(classname)
IPeaksWorkspace_sptr workspace
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.
Mantid::Kernel::Quat(ComponentInfo::* rotation)(const size_t) const
TypedValue getProperty(const std::string &name) const override
Get the value of a property.
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.
Base MatrixWorkspace Abstract Class.
API::SpectrumInfo is an intermediate step towards a SpectrumInfo that is part of Instrument-2....
const SpectrumDefinition & spectrumDefinition(const size_t index) const
Returns a const reference to the SpectrumDefinition of the spectrum.
WorkspaceNearestNeighbourInfo provides easy access to nearest-neighbour information for a workspace.
std::map< specnum_t, Kernel::V3D > getNeighboursExact(specnum_t spec) const
Queries the WorkspaceNearestNeighbours object for the selected spectrum number.
std::unique_ptr< API::Progress > m_progress
Progress reporter.
Converts a 2D workspace that has axes of energy transfer against spectrum number to one that gives in...
void exec() override
Run the algorithm.
std::vector< double > m_twoThetaLowers
Array for the lower 2theta angles, in radians.
int m_detNeighbourOffset
Offset for finding neighbor in nearest tube.
std::map< std::string, std::string > validateInputs() override
validate the inputs
void initAngularCachesNonPSD(const API::MatrixWorkspace &workspace)
Init the theta index.
std::vector< double > m_twoThetaUppers
Array for the upper 2theta angles, in radians.
void initAngularCachesPSD(const API::MatrixWorkspace &workspace)
Get angles and calculate angular widths.
int version() const override
Algorithm's version for identification.
SofQCommon m_EmodeProperties
void init() override
Initialize the algorithm.
const std::string category() const override
Algorithm's category for identification.
const std::string name() const override
Algorithm's name for identification.
void initAngularCachesTable(const API::MatrixWorkspace &workspace, const DataObjects::TableWorkspace &widthTable)
std::vector< double > m_Qout
Output Q axis.
static void createCommonInputProperties(API::Algorithm &alg)
Create the input properties on the given algorithm object.
TableWorkspace is an implementation of Workspace in which the data are organised in columns of same s...
std::vector< T > & getColVector(size_t index)
get access to column vector for index i.
A simple structure that defines an axis-aligned cuboid shaped bounding box for a geometrical object.
const Kernel::V3D & maxPoint() const
Returns the min point of the box.
Constructive Solid Geometry object.
const detail::ShapeInfo & shapeInfo() const override
ComponentInfo : Provides a component centric view on to the instrument.
const std::string & name(const size_t componentIndex) const
const Geometry::IObject & shape(const size_t componentIndex) const
Geometry::DetectorInfo is an intermediate step towards a DetectorInfo that is part of Instrument-2....
Kernel::Quat rotation(const size_t index) const
Returns the rotation of the detector with given index.
Kernel::V3D position(const size_t index) const
Returns the position of the detector with given index.
virtual const BoundingBox & getBoundingBox() const =0
Return cached value of axis-aligned bounding box.
A ConvexPolygon with only 4 vertices.
CylinderGeometry cylinderGeometry() const
CuboidGeometry cuboidGeometry() const
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.
bool is(int level) const
Returns true if at least the given log level is set.
void rotate(V3D &) const
Rotate a vector.
Implements a 2-dimensional vector embedded in a 3D space, i.e.
constexpr double X() const noexcept
Get x.
constexpr V3D cross_prod(const V3D &v) const noexcept
Cross product (this * argument)
double angle(const V3D &) const
Angle between this and another vector.
void setX(const double xx) noexcept
Set is x position.
constexpr double Z() const noexcept
Get z.
std::shared_ptr< const MatrixWorkspace > MatrixWorkspace_const_sptr
shared pointer to the matrix workspace base class (const version)
Kernel::Logger g_log("DetermineSpinStateOrder")
Geometry::IDetector_const_sptr DetConstPtr
std::map< specnum_t, Mantid::Kernel::V3D > SpectraDistanceMap
MANTID_DATAOBJECTS_DLL void normaliseOutput(const API::MatrixWorkspace_sptr &outputWS, const API::MatrixWorkspace_const_sptr &inputWS, API::Progress *progress=nullptr)
Compute sqrt of errors and put back in bin width division if necessary.
MANTID_DATAOBJECTS_DLL void rebinToFractionalOutput(const Geometry::Quadrilateral &inputQ, const API::MatrixWorkspace_const_sptr &inputWS, const size_t i, const size_t j, DataObjects::RebinnedOutput &outputWS, std::span< double const > verticalAxis, const DataObjects::RebinnedOutput_const_sptr &inputRB=nullptr)
Rebin the input quadrilateral to to output grid.
MANTID_DATAOBJECTS_DLL void finalizeFractionalRebin(DataObjects::RebinnedOutput &outputWS)
Set finalize flag after fractional rebinning loop.
std::shared_ptr< TableWorkspace > TableWorkspace_sptr
shared pointer to Mantid::DataObjects::TableWorkspace
std::shared_ptr< RebinnedOutput > RebinnedOutput_sptr
shared pointer to the RebinnedOutput class
constexpr double rad2deg
Radians to degrees conversion factor.
std::shared_ptr< const Mantid::Geometry::IDetector > IDetector_const_sptr
Shared pointer to IDetector (const version)
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.
int32_t detid_t
Typedef for a detector ID.
int32_t specnum_t
Typedef for a spectrum Number.
std::string to_string(const wide_integer< Bits, Signed > &n)
void initCachedValues(const API::MatrixWorkspace &workspace, API::Algorithm const *hostAlgorithm)
The procedure analyses emode and efixed properties provided to the algorithm and identify the energy ...
bool m_efixedGiven
EFixed has been provided.
double getEFixed(const Geometry::IDetector &det) const
Get the efixed value for the given detector.
double q(const double deltaE, const double twoTheta, const Geometry::IDetector *det) const
Calculate the Q value.
const Kernel::V3D & leftFrontTop
const Kernel::V3D & rightFrontBottom
const Kernel::V3D & leftBackBottom
const Kernel::V3D & leftFrontBottom
const Kernel::V3D & centreOfBottomBase