Newer
Older
//----------------------------------------------------------------------
// Includes
//----------------------------------------------------------------------
#include "MantidAlgorithms/CalculateMuonAsymmetry.h"
#include "MantidAlgorithms/MuonAsymmetryHelper.h"
#include "MantidAPI/FuncMinimizerFactory.h"
#include "MantidAPI/IFuncMinimizer.h"
#include "MantidAPI/IFunction.h"
#include "MantidAPI/MatrixWorkspace.h"
#include "MantidAPI/Run.h"
#include "MantidAPI/Workspace_fwd.h"
#include "MantidAPI/WorkspaceFactory.h"
#include "MantidKernel/ArrayProperty.h"
#include "MantidKernel/BoundedValidator.h"
#include "MantidKernel/ListValidator.h"
#include "MantidKernel/PhysicalConstants.h"
#include "MantidKernel/StartsWithValidator.h"
#include <cmath>
#include <numeric>
#include <vector>
namespace Mantid {
namespace Algorithms {
using namespace Kernel;
using API::Progress;
using std::size_t;
// Register the class into the algorithm factory
/** Initialisation method. Declares properties to be used in algorithm.
*
*/
declareProperty(make_unique<API::WorkspaceProperty<API::MatrixWorkspace>>(
"InputWorkspace", "", Direction::Input),
"The name of the input 2D workspace.");
declareProperty(make_unique<API::WorkspaceProperty<API::MatrixWorkspace>>(
"OutputWorkspace", "", Direction::Output),
"The name of the output 2D workspace.");
std::vector<int> empty;
declareProperty(
Kernel::make_unique<Kernel::ArrayProperty<int>>("Spectra", empty),
"The workspace indices to remove the exponential decay from.");
"The lower limit for calculating the asymmetry (an X value).");
declareProperty(
"The upper limit for calculating the asymmetry (an X value).");
declareProperty(
"name = GausOsc, A = 10.0, Sigma = 0.2, Frequency = 1.0, Phi = 0.0",
"The additional fitting functions to be used.");
declareProperty("InputDataType", "counts",
boost::make_shared<Mantid::Kernel::StringListValidator>(
std::vector<std::string>{"counts", "asymmetry"}),
"If the data is raw counts or asymmetry");
Kernel::IValidator_sptr minimizerValidator =
boost::make_shared<Kernel::StartsWithValidator>(minimizerOptions);
declareProperty("Minimizer", "Levenberg-MarquardtMD", minimizerValidator,
auto mustBePositive = boost::make_shared<Kernel::BoundedValidator<int>>();
mustBePositive->setLower(0);
declareProperty(
"MaxIterations", 500, mustBePositive->clone(),
"Stop after this number of iterations if a good fit is not found");
declareProperty(Kernel::make_unique<Kernel::ArrayProperty<double>>(
"NormalizationConstant", Direction::Output));
std::vector<double> emptyDoubles;
declareProperty(Kernel::make_unique<Kernel::ArrayProperty<double>>(
"PreviousNormalizationConstant", emptyDoubles),
"Normalization constant used"
" to estimate asymmetry");
* @returns map with keys corresponding to properties with errors and values
std::map<std::string, std::string> CalculateMuonAsymmetry::validateInputs() {
// create the map
std::map<std::string, std::string> validationOutput;
// check start and end times
double startX = getProperty("StartX");
double endX = getProperty("EndX");
if (startX > endX) {
validationOutput["StartX"] = "Start time is after the end time.";
} else if (startX == endX) {
validationOutput["StartX"] = "Start and end times are equal, there is no "
"data to apply the algorithm to.";
}
std::string dataType = getProperty("InputDataType");
std::vector<double> oldNorm = getProperty("PreviousNormalizationConstant");
std::vector<int> spectra = getProperty("Spectra");
if (dataType == "asymmetry" && oldNorm.empty()) {
validationOutput["PreviousNormalizationConstant"] =
"Asymmetry data has been provided but"
" no normalization constants have been provided.";
if (dataType == "asymmetry" && oldNorm.size() != spectra.size()) {
validationOutput["PreviousNormalizationConstant"] =
"Number of spectra and the list"
" of normalization constants are inconsistant.";
/** Executes the algorithm
*
*/
std::vector<int> spectra = getProperty("Spectra");
std::vector<double> oldNorm = getProperty("PreviousNormalizationConstant");
// Get original workspace
API::MatrixWorkspace_const_sptr inputWS = getProperty("InputWorkspace");
auto numSpectra = inputWS->getNumberHistograms();
// Create output workspace with same dimensions as input
API::MatrixWorkspace_sptr outputWS = getProperty("OutputWorkspace");
if (inputWS != outputWS) {
outputWS = API::WorkspaceFactory::Instance().create(inputWS);
}
double startX = getProperty("StartX");
double endX = getProperty("EndX");
auto dataType = getPropertyValue("InputDataType");
const Mantid::API::Run &run = inputWS->run();
const double numGoodFrames = std::stod(run.getProperty("goodfrm")->value());
g_log.warning("Assuming that the spectra and normalization constants are in "
"the same order");
// Share the X values
for (size_t i = 0; i < static_cast<size_t>(numSpectra); ++i) {
outputWS->setSharedX(i, inputWS->sharedX(i));
}
// No spectra specified = process all spectra
if (spectra.empty()) {
spectra = std::vector<int>(numSpectra);
std::iota(spectra.begin(), spectra.end(), 0);
}
Progress prog(this, 0.0, 1.0, numSpectra + spectra.size());
if (inputWS != outputWS) {
// Copy all the Y and E data
PARALLEL_FOR_IF(Kernel::threadSafe(*inputWS, *outputWS))
for (int64_t i = 0; i < int64_t(numSpectra); ++i) {
PARALLEL_START_INTERUPT_REGION
const auto index = static_cast<size_t>(i);
outputWS->setSharedY(index, inputWS->sharedY(index));
outputWS->setSharedE(index, inputWS->sharedE(index));
prog.report();
PARALLEL_END_INTERUPT_REGION
}
PARALLEL_CHECK_INTERUPT_REGION
}
// Do the specified spectra only
int specLength = static_cast<int>(spectra.size());
PARALLEL_FOR_IF(Kernel::threadSafe(*inputWS, *outputWS))
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
for (int i = 0; i < specLength; ++i) {
PARALLEL_START_INTERUPT_REGION
const auto specNum = static_cast<size_t>(spectra[i]);
if (spectra[i] > static_cast<int>(numSpectra)) {
g_log.error("The spectral index " + std::to_string(spectra[i]) +
" is greater than the number of spectra!");
throw std::invalid_argument("The spectral index " +
std::to_string(spectra[i]) +
" is greater than the number of spectra!");
}
double estNormConst;
if (dataType == "counts") {
// inital estimate of N0
estNormConst = estimateNormalisationConst(inputWS->histogram(specNum),
numGoodFrames, startX, endX);
// Calculate the normalised counts
outputWS->setHistogram(
specNum, normaliseCounts(inputWS->histogram(specNum), numGoodFrames));
} else {
estNormConst = oldNorm[i];
outputWS->setHistogram(specNum, inputWS->histogram(specNum));
// convert the data back to normalised counts
outputWS->mutableY(specNum) += 1.0;
outputWS->mutableY(specNum) *= estNormConst;
outputWS->mutableE(specNum) *= estNormConst;
}
// get the normalisation constant
const double normConst =
getNormConstant(outputWS, spectra[i], estNormConst, startX, endX);
// calculate the asymmetry
outputWS->mutableY(specNum) /= normConst;
outputWS->mutableY(specNum) -= 1.0;
outputWS->mutableE(specNum) /= normConst;
norm[i] = normConst;
prog.report();
PARALLEL_END_INTERUPT_REGION
}
PARALLEL_CHECK_INTERUPT_REGION
// Update Y axis units
outputWS->setYUnit("Asymmetry");
setProperty("OutputWorkspace", outputWS);
}
* Calculate normalisation constant after the exponential decay has been removed
* to a linear fitting function
* @param ws :: workspace
* @param wsIndex :: workspace index
* @param estNormConstant :: estimate of normalisation constant
* @param startX :: the smallest x value for the fit
* @param endX :: the largest x value for the fit
* @return normalisation constant
*/
double CalculateMuonAsymmetry::getNormConstant(API::MatrixWorkspace_sptr ws,
int wsIndex,
const double estNormConstant,
const double startX,
const double endX) {
int maxIterations = getProperty("MaxIterations");
auto minimizer = getProperty("Minimizer");
API::IAlgorithm_sptr fit;
fit = createChildAlgorithm("Fit", -1, -1, true);
std::string tmpString = getProperty("FittingFunction");
std::string function;
function = "composite=ProductFunction;name=FlatBackground,A0=" +
std::to_string(estNormConstant);
function += ";(name=FlatBackground,A0=1.0,ties=(A0=1.0);";
function += tmpString + ")";
fit->setProperty("MaxIterations", maxIterations);
fit->setPropertyValue("Function", function);
fit->setProperty("InputWorkspace", ws);
fit->setProperty("WorkspaceIndex", wsIndex);
fit->setPropertyValue("Minimizer", minimizer);
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
fit->setProperty("StartX", startX);
fit->setProperty("EndX", endX);
fit->execute();
std::string fitStatus = fit->getProperty("OutputStatus");
API::IFunction_sptr result = fit->getProperty("Function");
const double A0 = result->getParameter(0);
if (fitStatus == "success") { // to be explicit
if (A0 < 0) {
g_log.warning() << "When trying to fit Asymmetry normalisation constant "
"this constant comes out negative. For workspace "
<< wsIndex << "."
<< "To proceed Asym norm constant set to 1.0\n";
retVal = 1.0;
} else {
retVal = A0;
}
} else {
g_log.warning() << "Fit falled. Status = " << fitStatus
<< "\nFor workspace index " << wsIndex
<< "\nAsym norm constant set to 1.0\n";
retVal = 1.0;
}
return retVal;
}
} // namespace Algorithm
} // namespace Mantid