20#include "MantidHistogramData/Interpolate.h"
35using namespace Geometry;
36using HistogramData::interpolateLinearInplace;
37using namespace Kernel;
38using Kernel::DeltaEMode;
45constexpr size_t MAX_INTEGRATION_LENGTH{1000};
47const std::string CALC_SAMPLE =
"Sample";
48const std::string CALC_CONTAINER =
"Container";
49const std::string CALC_ENVIRONMENT =
"Environment";
51inline size_t findMiddle(
const size_t start,
const size_t stop) {
52 auto half =
static_cast<size_t>(floor(.5 * (
static_cast<double>(stop - start))));
57double energyToWavelength(
const double energyFixed) {
58 return 2. * M_PI * std::sqrt(E_mev_toNeutronWavenumberSq / energyFixed);
64 :
API::
Algorithm(), m_inputWS(), m_sampleObject(nullptr), m_L1s(), m_elementVolumes(), m_elementPositions(),
65 m_numVolumeElements(0), m_sampleVolume(0.0), m_linearCoefTotScatt(0), m_num_lambda(0), m_xStep(0),
66 m_emode(Kernel::
DeltaEMode::Undefined), m_lambdaFixed(0.), EXPONENTIAL() {}
71 auto wsValidator = std::make_shared<CompositeValidator>();
76 "The X values for the input workspace must be in units of wavelength");
78 "Output workspace name");
81 std::vector<std::string> scatter_options{CALC_SAMPLE, CALC_CONTAINER, CALC_ENVIRONMENT};
82 declareProperty(
"ScatterFrom", CALC_SAMPLE, std::make_shared<StringListValidator>(std::move(scatter_options)),
83 "The component to calculate the absorption for (default: Sample)");
85 auto mustBePositive = std::make_shared<BoundedValidator<double>>();
86 mustBePositive->setLower(0.0);
88 "The ABSORPTION cross-section, at 1.8 Angstroms, for the "
89 "sample material in barns. Column 8 of a table generated "
90 "from http://www.ncnr.nist.gov/resources/n-lengths/.");
92 "The (coherent + incoherent) scattering cross-section for "
93 "the sample material in barns. Column 7 of a table generated "
94 "from http://www.ncnr.nist.gov/resources/n-lengths/.");
96 "The number density of the sample in number of atoms per "
97 "cubic angstrom if not set with SetSampleMaterial");
99 auto positiveInt = std::make_shared<BoundedValidator<int64_t>>();
100 positiveInt->setLower(1);
102 "The number of wavelength points for which the numerical integral is\n"
103 "calculated (default: all points)");
105 std::vector<std::string> exp_options{
"Normal",
"FastApprox"};
106 declareProperty(
"ExpMethod",
"Normal", std::make_shared<StringListValidator>(exp_options),
107 "Select the method to use to calculate exponentials, normal or a\n"
108 "fast approximation (default: Normal)");
110 std::vector<std::string> propOptions{
"Elastic",
"Direct",
"Indirect"};
111 declareProperty(
"EMode",
"Elastic", std::make_shared<StringListValidator>(propOptions),
112 "The energy mode (default: elastic)");
114 "The value of the initial or final energy, as appropriate, in meV.\n"
115 "Will be taken from the instrument definition file, if available.");
123 std::map<std::string, std::string> result;
126 const std::string scatterFrom =
getProperty(
"ScatterFrom");
127 if (scatterFrom != CALC_SAMPLE) {
129 const auto &sample = wksp->sample();
130 if (sample.hasEnvironment()) {
131 const auto numComponents = sample.getEnvironment().nelements();
133 if (scatterFrom == CALC_CONTAINER && numComponents == 0) {
134 result[
"ScatterFrom"] =
"Sample does not have a container defined";
135 }
else if (scatterFrom == CALC_ENVIRONMENT) {
136 if (numComponents < 2) {
137 result[
"ScatterFrom"] =
"Sample does not have an environment defined";
138 }
else if (numComponents > 2) {
139 std::stringstream msg;
140 msg <<
"Do not know how to calculate absorption from multiple "
141 "component sample environment. Encountered "
142 << numComponents <<
" components";
143 result[
"ScatterFrom"] = msg.str();
147 if (scatterFrom == CALC_CONTAINER)
148 result[
"ScatterFrom"] =
"Sample does not have a container defined";
149 else if (scatterFrom == CALC_ENVIRONMENT)
150 result[
"ScatterFrom"] =
"Sample does not have an environment defined";
163 const auto &componentInfo =
m_inputWS->componentInfo();
170 correctionFactors->setDistribution(
true);
171 correctionFactors->setYUnit(
"");
172 correctionFactors->setYUnitLabel(
"Attenuation factor");
176 const auto numHists =
static_cast<int64_t
>(
m_inputWS->getNumberHistograms());
177 const auto specSize =
static_cast<int64_t
>(
m_inputWS->blocksize());
187 std::ostringstream message;
188 message <<
"Numerical integration performed every " <<
m_xStep <<
" wavelength points";
195 throw std::runtime_error(
"Failed to define any initial scattering gauge volume for geometry");
198 const auto &spectrumInfo =
m_inputWS->spectrumInfo();
199 Progress prog(
this, 0.0, 1.0, numHists);
202 for (int64_t i = 0; i < int64_t(numHists); ++i) {
205 correctionFactors->setSharedX(i,
m_inputWS->sharedX(i));
207 if (!spectrumInfo.hasDetectors(i)) {
208 g_log.
information() <<
"Spectrum " << i <<
" does not have a detector defined for it\n";
211 if (spectrumInfo.isMasked(i)) {
221 if (spectrumInfo.hasUniqueDetector(i)) {
223 const auto eFixed = componentInfo.getNumberParameter(spectrumInfo.firstDetectorIndex(i),
"Efixed",
false);
224 if (!eFixed.empty()) {
225 lambdaFixed = energyToWavelength(eFixed.front());
227 }
catch (std::runtime_error &) {
234 const auto wavelengths =
m_inputWS->points(i);
239 auto &
Y = correctionFactors->mutableY(i);
242 for (int64_t j = 0; j < specSize; j = j +
m_xStep) {
244 Y[j] = this->
doIntegration(-linearCoefAbs[j], L2s, 0, L2s.size());
246 Y[j] = this->
doIntegration(linearCoefAbsFixed, -linearCoefAbs[j], L2s, 0, L2s.size());
248 Y[j] = this->
doIntegration(-linearCoefAbs[j], linearCoefAbsFixed, L2s, 0, L2s.size());
250 throw std::runtime_error(
"AbsorptionCorrection doesn't have a known DeltaEMode defined");
255 if (
m_xStep > 1 && j +
m_xStep >= specSize && j + 1 != specSize) {
263 auto histnew = correctionFactors->histogram(i);
264 interpolateLinearInplace(histnew,
m_xStep);
265 correctionFactors->setHistogram(i, histnew);
285 double sigma_atten =
getProperty(
"AttenuationXSection");
286 double sigma_s =
getProperty(
"ScatteringXSection");
288 const std::string scatterFrom =
getProperty(
"ScatterFrom");
292 const auto &sampleObj =
m_inputWS->sample();
293 if (scatterFrom == CALC_SAMPLE) {
295 }
else if (scatterFrom == CALC_CONTAINER) {
296 m_material = sampleObj.getEnvironment().getContainer().material();
297 }
else if (scatterFrom == CALC_ENVIRONMENT) {
298 m_material = sampleObj.getEnvironment().getComponent(1).material();
301 if (createMaterial) {
311 NeutronAtom neutron(0, 0, 0.0, 0.0, sigma_s, 0.0, sigma_s, sigma_atten);
314 auto shape = std::shared_ptr<IObject>(
315 m_inputWS->sample().getShape().cloneWithMaterial(
Material(
"SetInAbsorptionCorrection", neutron,
rho)));
316 m_inputWS->mutableSample().setShape(shape);
329 if (exp_string ==
"Normal")
331 else if (exp_string ==
"FastApprox")
337 if (emodeStr ==
"Direct")
339 else if (emodeStr ==
"Indirect")
341 else if (emodeStr ==
"Elastic")
344 std::stringstream msg;
345 msg <<
"Unknown EMode \"" << emodeStr <<
"\"";
346 throw std::runtime_error(msg.str());
361 const std::string xmlstring =
sampleXML();
362 const std::string scatterFrom =
getProperty(
"ScatterFrom");
363 if (xmlstring.empty()) {
365 if (scatterFrom == CALC_SAMPLE)
367 else if (scatterFrom == CALC_CONTAINER)
369 else if (scatterFrom == CALC_ENVIRONMENT)
372 throw std::runtime_error(
"Somebody forgot to fill in an if/else tree");
376 const std::string mess(
"No shape has been defined for the sample in the input workspace");
378 throw std::invalid_argument(mess);
380 }
else if (scatterFrom != CALC_SAMPLE) {
381 std::stringstream msg;
382 msg <<
"Cannot use geometry xml for ScatterFrom=" << scatterFrom;
383 throw std::runtime_error(msg.str());
399 std::vector<double> &L2s)
const {
405 spectrumInfo.
phi(
index) * 180.0 / M_PI);
425 const size_t startIndex,
const size_t endIndex)
const {
429 if (endIndex - startIndex > MAX_INTEGRATION_LENGTH) {
430 size_t middle = findMiddle(startIndex, endIndex);
434 double integral = 0.0;
437 for (
size_t i = startIndex; i < endIndex; ++i) {
449 const std::vector<double> &L2s,
const size_t startIndex,
450 const size_t endIndex)
const {
457 if (endIndex - startIndex > MAX_INTEGRATION_LENGTH) {
458 size_t middle = findMiddle(startIndex, endIndex);
460 return doIntegration(linearCoefAbsL1, linearCoefAbsL2, L2s, startIndex, middle) +
461 doIntegration(linearCoefAbsL1, linearCoefAbsL2, L2s, middle, endIndex);
463 double integral = 0.0;
466 for (
size_t i = startIndex; i < endIndex; ++i) {
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.
Base class from which all concrete algorithm classes should be derived.
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.
static bool isEmpty(const NumT toCheck)
checks that the value was not set by users, uses the value in empty double/int.
A validator which checks that a workspace has a valid instrument.
Helper class for reporting progress from algorithms.
This class stores information about the sample used in particular run.
void setShape(const Geometry::IObject_sptr &shape)
Update the shape of the object.
const Geometry::IObject & getShape() const
Return the sample shape.
const Geometry::SampleEnvironment & getEnvironment() const
Get a reference to the sample's environment.
API::SpectrumInfo is an intermediate step towards a SpectrumInfo that is part of Instrument-2....
double twoTheta(const size_t index) const
Returns the scattering angle 2 theta in radians (angle w.r.t.
Kernel::V3D position(const size_t index) const
Returns the position of the spectrum with given index.
const SpectrumDefinition & spectrumDefinition(const size_t index) const
Returns a const reference to the SpectrumDefinition of the spectrum.
double phi(const size_t index) const
Returns the angle of the spectrum position about the lab-frame z axis, atan2(y, x),...
A property class for workspaces.
A validator which checks that the unit of the workspace referred to by a WorkspaceProperty is the exp...
void calculateDistances(const API::SpectrumInfo &spectrumInfo, const size_t index, std::vector< double > &L2s) const
Calculate the distances traversed by the neutrons within the sample.
std::vector< double > m_elementVolumes
Cached element volumes.
std::map< std::string, std::string > validateInputs() override
Method checking errors on ALL the inputs, before execution.
void retrieveBaseProperties()
Fetch the properties and set the appropriate member variables.
int64_t m_xStep
interpolated linearly
virtual void retrieveProperties()
A virtual function in which additional properties should be retrieved into member variables.
void exec() override
Execution code.
virtual void defineProperties()
A virtual function in which additional properties of an algorithm should be declared.
double m_linearCoefTotScatt
The total scattering cross-section in 1/m.
double m_lambdaFixed
The wavelength corresponding to the fixed energy,.
Kernel::V3D m_beamDirection
The direction of the beam.
expfunction EXPONENTIAL
Pointer to exponential function.
double m_sampleVolume
The total volume of the sample.
double doIntegration(const double linearCoefAbs, const std::vector< double > &L2s, const size_t startIndex, const size_t endIndex) const
Carries out the numerical integration over the sample for elastic instruments.
API::MatrixWorkspace_sptr m_inputWS
A pointer to the input workspace.
AbsorptionCorrection()
(Empty) Constructor
const Geometry::IObject * m_sampleObject
Local cache of sample object.
Kernel::DeltaEMode::Type m_emode
size_t m_numVolumeElements
The number of volume elements.
std::vector< Kernel::V3D > m_elementPositions
Cached element positions.
std::vector< double > m_L1s
Cached L1 distances.
void constructSample(API::Sample &sample)
Create the sample object using the Geometry classes, or use the existing one.
void init() override
Initialisation code.
virtual std::string sampleXML()=0
Returns the XML string describing the sample, which can be used by the ShapeFactory.
Kernel::Material m_material
int64_t m_num_lambda
The number of points in wavelength, the rest is.
virtual void initialiseCachedDistances()=0
Calculate the distances for L1 and element size for each element in the sample.
virtual int interceptSurface(Geometry::Track &) const =0
virtual bool hasValidShape() const =0
const Container & getContainer() const
const IObject & getComponent(const size_t index) const
Returns the requested IObject.
Class originally intended to be used with the DataHandling 'LoadInstrument' algorithm.
std::shared_ptr< CSGObject > createShape(Poco::XML::Element *pElem)
Creates a geometric object from a DOM-element-node pointing to an element whose child nodes contain t...
Defines a track as a start point and a direction.
double totalDistInsideObject() const
Returns the sum of all the links distInsideObject in the track.
IPropertyManager * setProperty(const std::string &name, const T &value)
Templated method to set the value of a PropertyWithValue.
void error(const std::string &msg)
Logs at error level.
void information(const std::string &msg)
Logs at information level.
A material is defined as being composed of a given element, defined as a PhysicalConstants::NeutronAt...
double absorbXSection(const double lambda=PhysicalConstants::NeutronAtom::ReferenceLambda) const
Get the absorption cross section at a given wavelength in barns.
double numberDensityEffective() const
Get the effective number density.
double linearAbsorpCoef(const double lambda=PhysicalConstants::NeutronAtom::ReferenceLambda) const
Returns the linear coefficient of absorption for the material in units of cm^-1 this should match the...
double totalScatterXSection() const
Return the total scattering cross section for a given wavelength in barns.
void report()
Increments the loop counter by 1, then sends the progress notification on behalf of its algorithm.
void spherical(const double R, const double theta, const double phi) noexcept
Sets the vector position based on spherical coordinates.
double norm() const noexcept
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
double fast_exp(double y)
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.
A namespace containing physical constants that are required by algorithms and unit routines.
static constexpr double E_mev_toNeutronWavenumberSq
Transformation coefficient to transform neutron energy into neutron wavevector: K-neutron[m^-10] = sq...
constexpr int EMPTY_INT() noexcept
Returns what we consider an "empty" integer within a property.
constexpr double EMPTY_DBL() noexcept
Returns what we consider an "empty" double within a property.
Defines the possible energy transfer modes:
@ Input
An input workspace.
@ Output
An output workspace.
Structure to store neutronic scattering information for the various elements.
static const double ReferenceLambda
The reference wavelength value for absorption cross sections.