26#include <boost/algorithm/string/join.hpp>
27#include <boost/lexical_cast.hpp>
32using namespace Kernel;
39const detid_t LOQ_TRANSMISSION_MONITOR_UDET = 3;
50size_t getIndexFromDetectorID(
const MatrixWorkspace &ws, detid_t detid) {
51 const std::vector<detid_t> input = {detid};
52 std::vector<size_t> result = ws.getIndicesFromDetectorIDs(input);
54 throw std::invalid_argument(
"Could not find the spectra corresponding to detector ID " +
std::to_string(detid));
64 auto wsValidator = std::make_shared<CompositeValidator>();
70 "The workspace containing the sample transmission run. Must "
71 "have common binning and be in units of wavelength.");
73 "The workspace containing the direct beam (no sample) "
74 "transmission run. The units and binning must match those of "
75 "the SampleRunWorkspace.");
77 "The name of the workspace in which to store the fitted transmission "
80 auto zeroOrMore = std::make_shared<BoundedValidator<int>>();
81 zeroOrMore->setLower(0);
83 declareProperty(
"IncidentBeamMonitor",
EMPTY_INT(), zeroOrMore,
"The UDET of the incident beam monitor");
84 declareProperty(
"TransmissionMonitor",
EMPTY_INT(), zeroOrMore,
"The UDET of the transmission monitor");
87 "A comma separated list of first bin boundary, width, last "
88 "bin boundary. Optionally\n"
89 "this can be followed by a comma and more widths and last "
91 "Negative width values indicate logarithmic binning.");
93 std::vector<std::string> options(3);
94 options[0] =
"Linear";
96 options[2] =
"Polynomial";
98 declareProperty(
"FitMethod",
"Log", std::make_shared<StringListValidator>(options),
99 "Whether to fit directly to the transmission curve using "
100 "Linear, Log or Polynomial.");
101 auto twoOrMore = std::make_shared<BoundedValidator<int>>();
102 twoOrMore->setLower(2);
103 declareProperty(
"PolynomialOrder", 2, twoOrMore,
104 "Order of the polynomial to "
105 "fit. It is considered only "
106 "for FitMethod=Polynomial");
108 declareProperty(
"OutputUnfittedData",
false,
109 "If True, will output an additional workspace called "
110 "[OutputWorkspace]_unfitted containing the unfitted "
111 "transmission correction.");
114 "An optional ArrayProperty containing a list of detector "
115 "ID's. These specify a region of interest "
116 "which is to be summed and then used instead of a "
117 "transmission monitor. This allows for a \"beam stop "
118 "out\" method of transmission calculation.");
128 const std::vector<detid_t> transDetList =
getProperty(
"TransmissionROI");
130 const bool usingSameInstrument = sampleWS->getInstrument()->getName() == directWS->getInstrument()->getName();
131 if (!usingSameInstrument)
132 throw std::invalid_argument(
"The input workspaces do not come from the same instrument.");
134 throw std::invalid_argument(
"The input workspaces do not have matching bins.");
136 bool usingMonitor = !
isEmpty(transMonitorID);
137 const bool usingROI = !transDetList.empty();
138 if (usingMonitor && usingROI)
139 throw std::invalid_argument(
"Unable to use both a monitor and a region of "
140 "interest in transmission calculation.");
141 if (!usingMonitor && !usingROI) {
142 transMonitorID = LOQ_TRANSMISSION_MONITOR_UDET;
153 std::vector<size_t> transmissionIndices;
155 const size_t transmissionMonitorIndex = getIndexFromDetectorID(*sampleWS, transMonitorID);
156 transmissionIndices.emplace_back(transmissionMonitorIndex);
158 }
else if (usingROI) {
159 transmissionIndices = sampleWS->getIndicesFromDetectorIDs(transDetList);
163 const std::string transPropName = usingMonitor ?
"TransmissionMonitor" :
"TransmissionROI";
165 if (transmissionIndices.empty())
166 throw std::invalid_argument(
"The UDET(s) passed to " + transPropName +
167 " do not correspond to spectra in the workspace.");
171 const bool normaliseToMonitor = !
isEmpty(beamMonitorID);
172 size_t beamMonitorIndex = 0;
173 if (normaliseToMonitor) {
174 beamMonitorIndex = getIndexFromDetectorID(*sampleWS, beamMonitorID);
177 const auto transmissionIndex = std::find(transmissionIndices.begin(), transmissionIndices.end(), beamMonitorIndex);
178 if (transmissionIndex != transmissionIndices.end())
179 throw std::invalid_argument(
"The IncidentBeamMonitor UDET (" +
std::to_string(*transmissionIndex) +
180 ") matches a UDET given in " + transPropName +
".");
184 if (normaliseToMonitor)
185 sampleInc = this->
extractSpectra(sampleWS, std::vector<size_t>(1, beamMonitorIndex));
189 if (normaliseToMonitor)
190 directInc = this->
extractSpectra(directWS, std::vector<size_t>(1, beamMonitorIndex));
195 progress.report(
"CalculateTransmission: Dividing transmission by incident");
199 if (normaliseToMonitor)
200 transmission = transmission * (directInc / sampleInc);
203 progress.report(
"CalculateTransmission: Dividing transmission by incident");
206 const bool outputRaw =
getProperty(
"OutputUnfittedData");
210 childAlg->setProperty<
double>(
"NaNValue", 0.0);
211 childAlg->setProperty<
double>(
"NaNError", 0.0);
212 childAlg->setProperty<
double>(
"InfinityValue", 0.0);
213 childAlg->setProperty<
double>(
"InfinityError", 0.0);
214 childAlg->executeAsChildAlg();
215 transmission = childAlg->getProperty(
"OutputWorkspace");
217 outputWSName +=
"_unfitted";
224 if (transmission->y(0).size() > 1) {
242 const std::vector<size_t> &indices) {
244 std::vector<std::string> indexStrings(indices.size());
249 using from_size_t = std::string (*)(
const size_t &);
251 std::transform(indices.begin(), indices.end(), indexStrings.begin(),
252 static_cast<from_size_t
>(boost::lexical_cast<std::string, size_t>));
253 const std::string commaIndexList = boost::algorithm::join(indexStrings,
",");
258 childAlg->setPropertyValue(
"ListOfWorkspaceIndices", commaIndexList);
259 childAlg->executeAsChildAlg();
262 return childAlg->getProperty(
"OutputWorkspace");
276 const std::vector<double> &rebinParams,
277 const std::string &fitMethod) {
281 progress.report(
"CalculateTransmission: Performing fit");
284 double grad(0.0), offset(0.0);
285 std::vector<double> coeficients;
286 const bool logFit = (fitMethod ==
"Log");
288 g_log.
debug(
"Fitting to the logarithm of the transmission");
290 auto &
Y = output->mutableY(0);
291 auto &E = output->mutableE(0);
294 for (
size_t i = 0; i <
Y.size(); ++i) {
297 E[i] = std::abs(E[i] /
Y[i]);
298 Y[i] = std::log10(
Y[i]);
299 progress.report(
"Fitting to the logarithm of the transmission");
303 output =
fitData(output, grad, offset);
305 else if (fitMethod ==
"Linear") {
306 g_log.
debug(
"Fitting directly to the data (i.e. linearly)");
307 output =
fitData(output, grad, offset);
310 std::stringstream info;
311 info <<
"Fitting the transmission to polynomial order=" << order;
316 progress.report(
"CalculateTransmission: Performing fit");
320 if (!rebinParams.empty()) {
321 output =
rebin(rebinParams, output);
323 progress.report(
"CalculateTransmission: Performing fit");
328 if ((!rebinParams.empty()) || logFit) {
329 auto &
X = output->x(0);
330 auto &
Y = output->mutableY(0);
333 const double m(std::pow(10, grad));
334 const double factor(std::pow(10, offset));
336 auto &E = output->mutableE(0);
337 for (
size_t i = 0; i <
Y.size(); ++i) {
341 Y[i] = factor * (std::pow(
m, 0.5 * (
X[i] +
X[i + 1])));
342 E[i] = std::abs(E[i] *
Y[i]);
346 else if (fitMethod ==
"Linear") {
348 for (
size_t i = 0; i <
Y.size(); ++i) {
349 Y[i] = (grad * 0.5 * (
X[i] +
X[i + 1])) + offset;
352 for (
size_t i = 0; i <
Y.size(); ++i) {
354 double x_v = 0.5 * (
X[i] +
X[i + 1]);
356 for (
int j = 0; j < static_cast<int>(coeficients.size()); ++j) {
357 aux += coeficients[j] * std::pow(x_v, j);
363 progress.report(
"CalculateTransmission: Performing fit");
381 childAlg->setProperty(
"Function", linearBack);
383 childAlg->setProperty(
"Minimizer",
"Levenberg-MarquardtMD");
384 childAlg->setProperty(
"CreateOutput",
true);
385 childAlg->setProperty(
"IgnoreInvalidData",
true);
386 childAlg->executeAsChildAlg();
388 std::string fitStatus = childAlg->getProperty(
"OutputStatus");
389 if (fitStatus !=
"success") {
390 g_log.
error(
"Unable to successfully fit the data: " + fitStatus);
391 throw std::runtime_error(
"Unable to successfully fit the data");
395 offset = linearBack->getParameter(0);
396 grad = linearBack->getParameter(1);
397 return this->
extractSpectra(childAlg->getProperty(
"OutputWorkspace"), std::vector<size_t>(1, 1));
406 std::vector<double> &coeficients) {
407 g_log.
notice(
"Fitting the experimental transmission curve fitpolyno");
411 polyfit->setAttributeValue(
"n", order);
412 polyfit->initialize();
413 childAlg->setProperty(
"Function", polyfit);
415 childAlg->setProperty(
"Minimizer",
"Levenberg-MarquardtMD");
416 childAlg->setProperty(
"CreateOutput",
true);
417 childAlg->setProperty(
"IgnoreInvalidData",
true);
418 childAlg->executeAsChildAlg();
419 std::string fitStatus = childAlg->getProperty(
"OutputStatus");
420 if (fitStatus !=
"success") {
421 g_log.
error(
"Unable to successfully fit the data: " + fitStatus);
422 throw std::runtime_error(
"Unable to successfully fit the data");
426 coeficients.resize(order + 1);
427 for (
int i = 0; i <= order; i++) {
428 coeficients[i] = polyfit->getParameter(i);
430 return this->
extractSpectra(childAlg->getProperty(
"OutputWorkspace"), std::vector<size_t>(1, 1));
444 childAlg->setProperty<std::vector<double>>(
"Params", binParams);
445 childAlg->executeAsChildAlg();
448 return childAlg->getProperty(
"OutputWorkspace");
461 const std::string message =
"The detector at index " +
std::to_string(
index) +
" is not a monitor in the ";
462 if (!sampleWS->spectrumInfo().isMonitor(
index))
464 if (!directWS->spectrumInfo().isMonitor(
index))
#define DECLARE_ALGORITHM(classname)
std::map< DeltaEMode::Type, std::string > index
void declareProperty(std::unique_ptr< Kernel::Property > p, const std::string &doc="") override
Add a property to the list of managed properties.
std::string getPropertyValue(const std::string &name) const override
Get the value of a property as a string.
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.
void progress(double p, const std::string &msg="", double estimatedTime=0.0, int progressPrecision=0)
Sends ProgressNotification.
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 provides a TENTATIVE check that a workspace contains common bins in each spectrum.
A validator which checks that a workspace contains histogram data (the default) or point data as requ...
Helper class for reporting progress from algorithms.
A property class for workspaces.
A validator which checks that the unit of the workspace referred to by a WorkspaceProperty is the exp...
Calculates the transmission correction, as a function of wavelength, for a SANS instrument.
API::MatrixWorkspace_sptr extractSpectra(const API::MatrixWorkspace_sptr &ws, const std::vector< size_t > &indices)
Pull out a single spectrum from a 2D workspace.
API::MatrixWorkspace_sptr fit(const API::MatrixWorkspace_sptr &raw, const std::vector< double > &rebinParams, const std::string &fitMethod)
Returns a workspace with the evaulation of the fit to the calculated transmission fraction.
API::MatrixWorkspace_sptr fitData(const API::MatrixWorkspace_sptr &WS, double &grad, double &offset)
Call the Linear fitting algorithm as a child algorithm.
API::MatrixWorkspace_sptr rebin(const std::vector< double > &binParams, const API::MatrixWorkspace_sptr &ws)
Calls the rebin algorithm.
double m_done
stores an estimate of the progress so far as a proportion (starts at zero goes to 1....
void exec() override
Execution code.
API::MatrixWorkspace_sptr fitPolynomial(const API::MatrixWorkspace_sptr &WS, int order, std::vector< double > &coeficients)
Call the Polynomial fitting algorithm as a child algorithm.
void logIfNotMonitor(const API::MatrixWorkspace_sptr &sampleWS, const API::MatrixWorkspace_sptr &directWS, size_t index)
Outpus message to log if the detector at the given index is not a monitor in both input workspaces.
Support for a property that holds an array of values.
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.
void notice(const std::string &msg)
Logs at notice level.
void error(const std::string &msg)
Logs at error level.
void information(const std::string &msg)
Logs at information level.
static T & Instance()
Return a reference to the Singleton instance, creating it if it does not already exist Creation is do...
std::shared_ptr< MatrixWorkspace > MatrixWorkspace_sptr
shared pointer to the matrix workspace base class
constexpr int EMPTY_INT() noexcept
Returns what we consider an "empty" integer within a property.
int32_t detid_t
Typedef for a detector ID.
std::string to_string(const wide_integer< Bits, Signed > &n)
static bool matchingBins(const MatrixWorkspace &ws1, const MatrixWorkspace &ws2, const bool firstOnly=false)
Checks whether the bins (X values) of two workspace are the same.
@ Input
An input workspace.
@ Output
An output workspace.