"git@code.ornl.gov:mantidproject/mantid.git" did not exist on "2b2920c4bba0b6c377c2021ad6c362ffed69d591"
Newer
Older
// Mantid Repository : https://github.com/mantidproject/mantid
//
// Copyright © 2018 ISIS Rutherford Appleton Laboratory UKRI,
// NScD Oak Ridge National Laboratory, European Spallation Source
// & Institut Laue - Langevin
// SPDX - License - Identifier: GPL - 3.0 +
Roman Tolchenov
committed
//----------------------------------------------------------------------
// Includes
//----------------------------------------------------------------------
#include "MantidAPI/IPeakFunction.h"
#include "MantidAPI/FunctionFactory.h"
#include "MantidAPI/FunctionParameterDecorator.h"
#include "MantidAPI/IFunction1D.tcc"
#include "MantidAPI/Jacobian.h"
#include "MantidAPI/PeakFunctionIntegrator.h"
Roman Tolchenov
committed
#include "MantidKernel/Exception.h"
#include <boost/make_shared.hpp>
Roman Tolchenov
committed
#include <cmath>
Roman Tolchenov
committed
namespace Mantid {
namespace API {
Roman Tolchenov
committed
Roman Tolchenov
committed
/** A Jacobian for individual functions
*/
class PartialJacobian1 : public Jacobian {
Jacobian *m_J; ///< pointer to the overall Jacobian
int m_iY0; ///< offset in the overall Jacobian for a particular function
Roman Tolchenov
committed
public:
/** Constructor
Janik Zikovsky
committed
* @param J :: A pointer to the overall Jacobian
* @param iY0 :: The data offset for a particular function
Roman Tolchenov
committed
*/
PartialJacobian1(Jacobian *J, int iY0) : m_J(J), m_iY0(iY0) {}
Roman Tolchenov
committed
/**
* Overridden Jacobian::set(...).
Janik Zikovsky
committed
* @param iY :: The index of the data point
* @param iP :: The parameter index of an individual function.
* @param value :: The derivative value
Roman Tolchenov
committed
*/
void set(size_t iY, size_t iP, double value) override {
Roman Tolchenov
committed
}
Roman Tolchenov
committed
/**
* Overridden Jacobian::get(...).
* @param iY :: The index of the data point
* @param iP :: The parameter index of an individual function.
*/
double get(size_t iY, size_t iP) override { return m_J->get(m_iY0 + iY, iP); }
/** Zero all matrix elements.
void zero() override {
throw Kernel::Exception::NotImplementedError(
"zero() is not implemented for PartialJacobian1");
Roman Tolchenov
committed
};
class TempJacobian : public Jacobian {
public:
TempJacobian(size_t y, size_t p) : m_y(y), m_p(p), m_J(y * p) {}
void set(size_t iY, size_t iP, double value) override {
m_J[iY * m_p + iP] = value;
}
double get(size_t iY, size_t iP) override { return m_J[iY * m_p + iP]; }
size_t maxParam(size_t iY) {
double max = -DBL_MAX;
size_t maxIndex = 0;
for (size_t i = 0; i < m_p; ++i) {
double current = get(iY, i);
if (current > max) {
maxIndex = i;
max = current;
void zero() override { m_J.assign(m_J.size(), 0.0); }
protected:
size_t m_y;
size_t m_p;
std::vector<double> m_J;
};
/// Tolerance for determining the smallest significant value on the peak
const double PEAK_TOLERANCE = 1e-14;
/// "Infinite" value for the peak radius
const int MAX_PEAK_RADIUS = std::numeric_limits<int>::max();
Roman Tolchenov
committed
/**
IPeakFunction::IPeakFunction() : m_peakRadius(MAX_PEAK_RADIUS) {}
Roman Tolchenov
committed
void IPeakFunction::function(const FunctionDomain &domain,
auto peakRadius =
dynamic_cast<const FunctionDomain1D &>(domain).getPeakRadius();
setPeakRadius(peakRadius);
IFunction1D::function(domain, values);
}
/**
* General implementation of the method for all peaks. Limits the peak
* evaluation to
* a certain number of FWHMs around the peak centre. The outside points are set
* to 0.
Roman Tolchenov
committed
* Calls functionLocal() to compute the actual values
Janik Zikovsky
committed
* @param out :: Output function values
* @param xValues :: X values for data points
* @param nData :: Number of data points
Roman Tolchenov
committed
*/
void IPeakFunction::function1D(double *out, const double *xValues,
const size_t nData) const {
Roman Tolchenov
committed
double c = this->centre();
double dx = fabs(m_peakRadius * this->fwhm());
Roman Tolchenov
committed
int i0 = -1;
int n = 0;
for (size_t i = 0; i < nData; ++i) {
if (fabs(xValues[i] - c) < dx) {
if (i0 < 0)
i0 = static_cast<int>(i);
Roman Tolchenov
committed
++n;
out[i] = 0.0;
Roman Tolchenov
committed
}
}
if (i0 < 0 || n == 0)
return;
this->functionLocal(out + i0, xValues + i0, n);
Roman Tolchenov
committed
}
/**
* General implementation of the method for all peaks. Calculates derivatives
* only
* for a range of x values limited to a certain number of FWHM around the peak
* centre.
Roman Tolchenov
committed
* For the points outside the range all derivatives are set to 0.
* Calls functionDerivLocal() to compute the actual values
Janik Zikovsky
committed
* @param out :: Derivatives
* @param xValues :: X values for data points
* @param nData :: Number of data points
Roman Tolchenov
committed
*/
void IPeakFunction::functionDeriv1D(Jacobian *out, const double *xValues,
const size_t nData) {
Roman Tolchenov
committed
double c = this->centre();
double dx = fabs(m_peakRadius * this->fwhm());
Roman Tolchenov
committed
int i0 = -1;
int n = 0;
for (size_t i = 0; i < nData; ++i) {
if (fabs(xValues[i] - c) < dx) {
if (i0 < 0)
i0 = static_cast<int>(i);
Roman Tolchenov
committed
++n;
} else {
for (size_t ip = 0; ip < this->nParams(); ++ip) {
out->set(i, ip, 0.0);
Roman Tolchenov
committed
}
}
}
if (i0 < 0 || n == 0)
return;
PartialJacobian1 J(out, i0);
this->functionDerivLocal(&J, xValues + i0, n);
Roman Tolchenov
committed
}
void IPeakFunction::setPeakRadius(int r) const {
m_peakRadius = r;
} else if (r == 0) {
m_peakRadius = MAX_PEAK_RADIUS;
Roman Tolchenov
committed
}
}
/// Returns the integral intensity of the peak function, using the peak radius
/// to determine integration borders.
double IPeakFunction::intensity() const {
auto interval = getDomainInterval();
PeakFunctionIntegrator integrator;
IntegrationResult result =
integrator.integrate(*this, interval.first, interval.second);
if (!result.success) {
return 0.0;
}
return result.result;
}
/// Sets the integral intensity of the peak by adjusting the height.
void IPeakFunction::setIntensity(const double newIntensity) {
double currentHeight = height();
double currentIntensity = intensity();
if (currentIntensity == 0.0) {
// Try to set a different height first.
setHeight(2.0);
currentHeight = height();
currentIntensity = intensity();
// If the current intensity is still 0, there's nothing left to do.
if (currentIntensity == 0.0) {
throw std::invalid_argument(
"Cannot set new intensity, not enough information available.");
}
}
setHeight(newIntensity / currentIntensity * currentHeight);
std::string IPeakFunction::getCentreParameterName() const {
FunctionParameterDecorator_sptr fn =
boost::dynamic_pointer_cast<FunctionParameterDecorator>(
FunctionFactory::Instance().createFunction("PeakParameterFunction"));
if (!fn) {
throw std::runtime_error(
"PeakParameterFunction could not be created successfully.");
}
fn->setDecoratedFunction(this->name());
FunctionDomain1DVector domain(std::vector<double>(4, 0.0));
TempJacobian jacobian(4, fn->nParams());
fn->functionDeriv(domain, jacobian);
return parameterName(jacobian.maxParam(0));
}
/// Get the interval on which the peak has all its values above a certain
/// level. All values outside the interval are below that level.
/// @param level :: A fraction of the peak height.
/// @return A pair of doubles giving the bounds of the interval.
std::pair<double, double> IPeakFunction::getDomainInterval(double level) const {
if (level < PEAK_TOLERANCE) {
level = PEAK_TOLERANCE;
}
double left = 0.0;
double right = 0.0;
auto h = height();
auto w = fwhm();
auto c = centre();
if (h == 0.0 || w == 0.0 || level >= 1.0) {
return std::make_pair(c, c);
}
auto findBound = [this, c, h, level](double dx) {
double x = c + dx;
double y = 0.0;
this->functionLocal(&y, &x, 1);
if (fabs(y / h) < level) {
return x;
}
dx *= 2;
}
return c + dx;
};
left = findBound(-w);
right = findBound(w);
return std::make_pair(left, right);
}
/**
* Computes the function derivative numerically
* @param jacobian An output Jacobian to receive the calculated values
* @param xValues An input array of X data
* @param nData The number of X values provided
*/
void IPeakFunction::functionDerivLocal(Jacobian *jacobian,
const double *xValues,
const size_t nData) {
auto evalMethod = [this](double *out, const double *xValues,
const size_t nData) {
this->functionLocal(out, xValues, nData);
};
this->calcNumericalDerivative1D(jacobian, std::move(evalMethod), xValues,
nData);
}
Roman Tolchenov
committed
} // namespace API
} // namespace Mantid