19#include "MantidHistogramData/Interpolate.h"
34using namespace Geometry;
35using HistogramData::interpolateLinearInplace;
36using namespace Kernel;
37using Kernel::DeltaEMode;
44constexpr size_t MAX_INTEGRATION_LENGTH{1000};
46const std::string CALC_SAMPLE =
"Sample";
47const std::string CALC_CONTAINER =
"Container";
48const std::string CALC_ENVIRONMENT =
"Environment";
50inline size_t findMiddle(
const size_t start,
const size_t stop) {
51 auto half =
static_cast<size_t>(floor(.5 * (
static_cast<double>(stop - start))));
56double energyToWavelength(
const double energyFixed) {
57 return 2. * M_PI * std::sqrt(E_mev_toNeutronWavenumberSq / energyFixed);
63 :
API::
Algorithm(), m_inputWS(), m_sampleObject(nullptr), m_L1s(), m_elementVolumes(), m_elementPositions(),
64 m_numVolumeElements(0), m_sampleVolume(0.0), m_linearCoefTotScatt(0), m_num_lambda(0), m_xStep(0),
65 m_emode(Kernel::
DeltaEMode::Undefined), m_lambdaFixed(0.), EXPONENTIAL() {}
70 auto wsValidator = std::make_shared<CompositeValidator>();
75 "The X values for the input workspace must be in units of wavelength");
77 "Output workspace name");
80 std::vector<std::string> scatter_options{CALC_SAMPLE, CALC_CONTAINER, CALC_ENVIRONMENT};
81 declareProperty(
"ScatterFrom", CALC_SAMPLE, std::make_shared<StringListValidator>(std::move(scatter_options)),
82 "The component to calculate the absorption for (default: Sample)");
84 auto mustBePositive = std::make_shared<BoundedValidator<double>>();
85 mustBePositive->setLower(0.0);
87 "The ABSORPTION cross-section, at 1.8 Angstroms, for the "
88 "sample material in barns. Column 8 of a table generated "
89 "from http://www.ncnr.nist.gov/resources/n-lengths/.");
91 "The (coherent + incoherent) scattering cross-section for "
92 "the sample material in barns. Column 7 of a table generated "
93 "from http://www.ncnr.nist.gov/resources/n-lengths/.");
95 "The number density of the sample in number of atoms per "
96 "cubic angstrom if not set with SetSampleMaterial");
98 auto positiveInt = std::make_shared<BoundedValidator<int64_t>>();
99 positiveInt->setLower(1);
101 "The number of wavelength points for which the numerical integral is\n"
102 "calculated (default: all points)");
104 std::vector<std::string> exp_options{
"Normal",
"FastApprox"};
105 declareProperty(
"ExpMethod",
"Normal", std::make_shared<StringListValidator>(exp_options),
106 "Select the method to use to calculate exponentials, normal or a\n"
107 "fast approximation (default: Normal)");
109 std::vector<std::string> propOptions{
"Elastic",
"Direct",
"Indirect"};
110 declareProperty(
"EMode",
"Elastic", std::make_shared<StringListValidator>(propOptions),
111 "The energy mode (default: elastic)");
113 "The value of the initial or final energy, as appropriate, in meV.\n"
114 "Will be taken from the instrument definition file, if available.");
122 std::map<std::string, std::string> result;
125 const std::string scatterFrom =
getProperty(
"ScatterFrom");
126 if (scatterFrom != CALC_SAMPLE) {
128 const auto &sample = wksp->sample();
129 if (sample.hasEnvironment()) {
130 const auto numComponents = sample.getEnvironment().nelements();
132 if (scatterFrom == CALC_CONTAINER && numComponents == 0) {
133 result[
"ScatterFrom"] =
"Sample does not have a container defined";
134 }
else if (scatterFrom == CALC_ENVIRONMENT) {
135 if (numComponents < 2) {
136 result[
"ScatterFrom"] =
"Sample does not have an environment defined";
137 }
else if (numComponents > 2) {
138 std::stringstream msg;
139 msg <<
"Do not know how to calculate absorption from multiple "
140 "component sample environment. Encountered "
141 << numComponents <<
" components";
142 result[
"ScatterFrom"] = msg.str();
146 if (scatterFrom == CALC_CONTAINER)
147 result[
"ScatterFrom"] =
"Sample does not have a container defined";
148 else if (scatterFrom == CALC_ENVIRONMENT)
149 result[
"ScatterFrom"] =
"Sample does not have an environment defined";
169 correctionFactors->setDistribution(
true);
170 correctionFactors->setYUnit(
"");
171 correctionFactors->setYUnitLabel(
"Attenuation factor");
175 const auto numHists =
static_cast<int64_t
>(
m_inputWS->getNumberHistograms());
176 const auto specSize =
static_cast<int64_t
>(
m_inputWS->blocksize());
186 std::ostringstream message;
187 message <<
"Numerical integration performed every " <<
m_xStep <<
" wavelength points";
194 throw std::runtime_error(
"Failed to define any initial scattering gauge volume for geometry");
197 const auto &spectrumInfo =
m_inputWS->spectrumInfo();
198 Progress prog(
this, 0.0, 1.0, numHists);
201 for (int64_t i = 0; i < int64_t(numHists); ++i) {
204 correctionFactors->setSharedX(i,
m_inputWS->sharedX(i));
206 if (!spectrumInfo.hasDetectors(i)) {
207 g_log.
information() <<
"Spectrum " << i <<
" does not have a detector defined for it\n";
210 if (spectrumInfo.isMasked(i)) {
213 const auto &det = spectrumInfo.detector(i);
224 lambdaFixed = energyToWavelength(par->value<
double>());
226 }
catch (std::runtime_error &) {
233 const auto wavelengths =
m_inputWS->points(i);
238 auto &
Y = correctionFactors->mutableY(i);
241 for (int64_t j = 0; j < specSize; j = j +
m_xStep) {
243 Y[j] = this->
doIntegration(-linearCoefAbs[j], L2s, 0, L2s.size());
245 Y[j] = this->
doIntegration(linearCoefAbsFixed, -linearCoefAbs[j], L2s, 0, L2s.size());
247 Y[j] = this->
doIntegration(-linearCoefAbs[j], linearCoefAbsFixed, L2s, 0, L2s.size());
249 throw std::runtime_error(
"AbsorptionCorrection doesn't have a known DeltaEMode defined");
254 if (
m_xStep > 1 && j +
m_xStep >= specSize && j + 1 != specSize) {
262 auto histnew = correctionFactors->histogram(i);
263 interpolateLinearInplace(histnew,
m_xStep);
264 correctionFactors->setHistogram(i, histnew);
284 double sigma_atten =
getProperty(
"AttenuationXSection");
285 double sigma_s =
getProperty(
"ScatteringXSection");
287 const std::string scatterFrom =
getProperty(
"ScatterFrom");
291 const auto &sampleObj =
m_inputWS->sample();
292 if (scatterFrom == CALC_SAMPLE) {
294 }
else if (scatterFrom == CALC_CONTAINER) {
295 m_material = sampleObj.getEnvironment().getContainer().material();
296 }
else if (scatterFrom == CALC_ENVIRONMENT) {
297 m_material = sampleObj.getEnvironment().getComponent(1).material();
300 if (createMaterial) {
310 NeutronAtom neutron(0, 0, 0.0, 0.0, sigma_s, 0.0, sigma_s, sigma_atten);
313 auto shape = std::shared_ptr<IObject>(
314 m_inputWS->sample().getShape().cloneWithMaterial(
Material(
"SetInAbsorptionCorrection", neutron,
rho)));
315 m_inputWS->mutableSample().setShape(shape);
328 if (exp_string ==
"Normal")
330 else if (exp_string ==
"FastApprox")
336 if (emodeStr ==
"Direct")
338 else if (emodeStr ==
"Indirect")
340 else if (emodeStr ==
"Elastic")
343 std::stringstream msg;
344 msg <<
"Unknown EMode \"" << emodeStr <<
"\"";
345 throw std::runtime_error(msg.str());
360 const std::string xmlstring =
sampleXML();
361 const std::string scatterFrom =
getProperty(
"ScatterFrom");
362 if (xmlstring.empty()) {
364 if (scatterFrom == CALC_SAMPLE)
366 else if (scatterFrom == CALC_CONTAINER)
368 else if (scatterFrom == CALC_ENVIRONMENT)
371 throw std::runtime_error(
"Somebody forgot to fill in an if/else tree");
375 const std::string mess(
"No shape has been defined for the sample in the input workspace");
377 throw std::invalid_argument(mess);
379 }
else if (scatterFrom != CALC_SAMPLE) {
380 std::stringstream msg;
381 msg <<
"Cannot use geometry xml for ScatterFrom=" << scatterFrom;
382 throw std::runtime_error(msg.str());
398 if (detector.
nDets() > 1) {
402 detector.
getPhi() * 180.0 / M_PI);
422 const size_t startIndex,
const size_t endIndex)
const {
426 if (endIndex - startIndex > MAX_INTEGRATION_LENGTH) {
427 size_t middle = findMiddle(startIndex, endIndex);
431 double integral = 0.0;
434 for (
size_t i = startIndex; i < endIndex; ++i) {
446 const std::vector<double> &L2s,
const size_t startIndex,
447 const size_t endIndex)
const {
454 if (endIndex - startIndex > MAX_INTEGRATION_LENGTH) {
455 size_t middle = findMiddle(startIndex, endIndex);
457 return doIntegration(linearCoefAbsL1, linearCoefAbsL2, L2s, startIndex, middle) +
458 doIntegration(linearCoefAbsL1, linearCoefAbsL2, L2s, middle, endIndex);
460 double integral = 0.0;
463 for (
size_t i = startIndex; i < endIndex; ++i) {
#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.
A property class for workspaces.
A validator which checks that the unit of the workspace referred to by a WorkspaceProperty is the exp...
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 calculateDistances(const Geometry::IDetector &detector, std::vector< double > &L2s) const
Calculate the distances traversed by the neutrons within the sample.
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 Kernel::V3D getPos() const =0
Get the position of the IComponent. Tree structure is traverse through the.
Interface class for detector objects.
virtual double getPhi() const =0
Gives the phi of this detector object in radians.
virtual std::size_t nDets() const =0
Get the number of physical detectors this object represents.
virtual double getTwoTheta(const Kernel::V3D &observer, const Kernel::V3D &axis) const =0
Gives the angle of this detector object with respect to an axis.
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
std::shared_ptr< Parameter > Parameter_sptr
Typedef for the shared pointer.
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.
Generate a tableworkspace to store the calibration results.
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.