-
Federico Montesino Pouzols authoredFederico Montesino Pouzols authored
Code owners
Assign users and groups as approvers for specific file changes. Learn more.
IFunction.cpp 35.27 KiB
//----------------------------------------------------------------------
// Includes
//----------------------------------------------------------------------
#include "MantidKernel/Exception.h"
#include "MantidKernel/Logger.h"
#include "MantidAPI/IFunction.h"
#include "MantidAPI/Jacobian.h"
#include "MantidAPI/IConstraint.h"
#include "MantidAPI/ParameterTie.h"
#include "MantidAPI/Expression.h"
#include "MantidAPI/ConstraintFactory.h"
#include "MantidAPI/FunctionFactory.h"
#include "MantidAPI/MatrixWorkspace.h"
#include "MantidAPI/IFunctionWithLocation.h"
#include "MantidGeometry/Instrument/ParameterMap.h"
#include "MantidGeometry/Instrument/Component.h"
#include "MantidGeometry/Instrument/DetectorGroup.h"
#include "MantidGeometry/Instrument/FitParameter.h"
#include "MantidKernel/UnitFactory.h"
#include "MantidKernel/MultiThreaded.h"
#include "MantidKernel/ProgressBase.h"
#include "MantidGeometry/muParser_Silent.h"
#include <boost/lexical_cast.hpp>
#include <Poco/StringTokenizer.h>
#include <limits>
#include <sstream>
#include <algorithm>
namespace Mantid {
namespace API {
using namespace Geometry;
namespace {
/// static logger
Kernel::Logger g_log("IFunction");
}
/**
* Destructor
*/
IFunction::~IFunction() {
m_attrs.clear();
if (m_handler) {
delete m_handler;
m_handler = NULL;
}
}
/**
* Virtual copy constructor
*/
boost::shared_ptr<IFunction> IFunction::clone() const {
return FunctionFactory::Instance().createInitialized(this->asString());
}
/**
* Attach a progress reporter
* @param reporter :: A pointer to a progress reporter that can be called during
* function evaluation
*/
void IFunction::setProgressReporter(Kernel::ProgressBase *reporter) {
m_progReporter = reporter;
m_progReporter->setNotifyStep(0.01);
}
/**
* If a reporter object is set, reports progress with an optional message
* @param msg :: A message to display (default = "")
*/
void IFunction::reportProgress(const std::string &msg) const {
if (m_progReporter) {
const_cast<Kernel::ProgressBase *>(m_progReporter)->report(msg);
}
}
/**
*
* @returns true if a progress reporter is set & evalaution has been requested
*to stop
*/
bool IFunction::cancellationRequestReceived() const {
if (m_progReporter)
return m_progReporter->hasCancellationBeenRequested();
else
return false;
}
/** Base class implementation calculates the derivatives numerically.
* @param domain :: The domain of the function
* @param jacobian :: A Jacobian matrix. It is expected to have dimensions of
* domain.size() by nParams().
*/
void IFunction::functionDeriv(const FunctionDomain &domain,
Jacobian &jacobian) {
calNumericalDeriv(domain, jacobian);
}
/**
* Ties a parameter to other parameters
* @param parName :: The name of the parameter to tie.
* @param expr :: A math expression
* @param isDefault :: Flag to mark as default the value of an object associated
* with this reference: a tie or a constraint.
* @return newly ties parameters
*/
ParameterTie *IFunction::tie(const std::string &parName,
const std::string &expr, bool isDefault) {
ParameterTie *ti = new ParameterTie(this, parName, expr, isDefault);
addTie(ti);
this->fix(getParameterIndex(*ti));
return ti;
}
/**
* Add ties to the function.
* @param ties :: Comma-separated list of name=value pairs where name is a
*parameter name and value
* is a math expression tying the parameter to other parameters or a constant.
* @param isDefault :: Flag to mark as default the value of an object associated
*with this reference: a tie or a constraint.
*
*/
void IFunction::addTies(const std::string &ties, bool isDefault) {
Expression list;
list.parse(ties);
list.toList();
for (auto t = list.begin(); t != list.end(); ++t) {
if (t->name() == "=" && t->size() >= 2) {
size_t n = t->size() - 1;
const std::string value = (*t)[n].str();
for (size_t i = n; i != 0;) {
--i;
this->tie((*t)[i].name(), value, isDefault);
}
}
}
}
/** Removes the tie off a parameter. The parameter becomes active
* This method can be used when constructing and editing the IFunction in a GUI
* @param parName :: The name of the parameter which ties will be removed.
*/
void IFunction::removeTie(const std::string &parName) {
size_t i = parameterIndex(parName);
this->removeTie(i);
}
/**
* Writes a string that can be used in Fit.IFunction to create a copy of this
* IFunction
* @return string representation of the function
*/
std::string IFunction::asString() const {
std::ostringstream ostr;
ostr << "name=" << this->name();
// print the attributes
std::vector<std::string> attr = this->getAttributeNames();
for (size_t i = 0; i < attr.size(); i++) {
std::string attName = attr[i];
std::string attValue = this->getAttribute(attr[i]).value();
if (!attValue.empty() && attValue != "\"\"") {
ostr << ',' << attName << '=' << attValue;
}
}
// print the parameters
for (size_t i = 0; i < nParams(); i++) {
const ParameterTie *tie = getTie(i);
if (!tie || !tie->isDefault()) {
ostr << ',' << parameterName(i) << '=' << getParameter(i);
}
}
// collect non-default constraints
std::string constraints;
for (size_t i = 0; i < nParams(); i++) {
const IConstraint *c = getConstraint(i);
if (c && !c->isDefault()) {
std::string tmp = c->asString();
if (!tmp.empty()) {
if (!constraints.empty()) {
constraints += ",";
}
constraints += tmp;
}
}
}
// print constraints
if (!constraints.empty()) {
ostr << ",constraints=(" << constraints << ")";
}
// collect the non-default ties
std::string ties;
for (size_t i = 0; i < nParams(); i++) {
const ParameterTie *tie = getTie(i);
if (tie && !tie->isDefault()) {
std::string tmp = tie->asString(this);
if (!tmp.empty()) {
if (!ties.empty()) {
ties += ",";
}
ties += tmp;
}
}
}
// print the ties
if (!ties.empty()) {
ostr << ",ties=(" << ties << ")";
}
return ostr.str();
}
/** Add a list of constraints from a string
* @param str :: A comma-separated list of constraint expressions.
* @param isDefault :: Flag to mark as default the value of an object associated
*with this reference.
*
*/
void IFunction::addConstraints(const std::string &str, bool isDefault) {
Expression list;
list.parse(str);
list.toList();
for (auto expr = list.begin(); expr != list.end(); ++expr) {
IConstraint *c =
ConstraintFactory::Instance().createInitialized(this, *expr, isDefault);
this->addConstraint(c);
}
}
/**
* Return a vector with all parameter names.
*/
std::vector<std::string> IFunction::getParameterNames() const {
std::vector<std::string> out;
for (size_t i = 0; i < nParams(); ++i) {
out.push_back(parameterName(i));
}
return out;
}
/** Set a function handler
* @param handler :: A new handler
*/
void IFunction::setHandler(FunctionHandler *handler) {
m_handler = handler;
if (handler && handler->function().get() != this) {
throw std::runtime_error("Function handler points to a different function");
}
m_handler->init();
}
/// Function to return all of the categories that contain this function
const std::vector<std::string> IFunction::categories() const {
std::vector<std::string> res;
Poco::StringTokenizer tokenizer(category(), categorySeparator(),
Poco::StringTokenizer::TOK_TRIM |
Poco::StringTokenizer::TOK_IGNORE_EMPTY);
Poco::StringTokenizer::Iterator h = tokenizer.begin();
for (; h != tokenizer.end(); ++h) {
res.push_back(*h);
}
return res;
}
/**
* Operator <<
* @param ostr :: The output stream
* @param f :: The IFunction
*/
std::ostream &operator<<(std::ostream &ostr, const IFunction &f) {
ostr << f.asString();
return ostr;
}
namespace {
/**
* Const attribute visitor returning the type of the attribute
*/
class AttType : public IFunction::ConstAttributeVisitor<std::string> {
protected:
/// Apply if string
std::string apply(const std::string &) const { return "std::string"; }
/// Apply if int
std::string apply(const int &) const { return "int"; }
/// Apply if double
std::string apply(const double &) const { return "double"; }
/// Apply if bool
std::string apply(const bool &) const { return "bool"; }
/// Apply if vector
std::string apply(const std::vector<double> &) const {
return "std::vector<double>";
}
};
}
std::string IFunction::Attribute::type() const {
AttType tmp;
return apply(tmp);
}
namespace {
/**
* Const attribute visitor returning the value of the attribute as a string
*/
class AttValue : public IFunction::ConstAttributeVisitor<std::string> {
public:
AttValue(bool quoteString = false)
: IFunction::ConstAttributeVisitor<std::string>(),
m_quoteString(quoteString) {}
protected:
/// Apply if string
std::string apply(const std::string &str) const {
return (m_quoteString) ? std::string("\"" + str + "\"") : str;
}
/// Apply if int
std::string apply(const int &i) const {
return boost::lexical_cast<std::string>(i);
}
/// Apply if double
std::string apply(const double &d) const {
return boost::lexical_cast<std::string>(d);
}
/// Apply if bool
std::string apply(const bool &b) const { return b ? "true" : "false"; }
/// Apply if vector
std::string apply(const std::vector<double> &v) const {
std::string res = "(";
if (v.size() > 0) {
for (size_t i = 0; i < v.size() - 1; ++i) {
res += boost::lexical_cast<std::string>(v[i]) + ",";
}
res += boost::lexical_cast<std::string>(v.back());
}
res += ")";
return res;
}
private:
/// Flag to quote a string value returned
bool m_quoteString;
};
}
std::string IFunction::Attribute::value() const {
AttValue tmp(m_quoteValue);
return apply(tmp);
}
/**
* Return the attribute as a string if it is a string.
*/
std::string IFunction::Attribute::asString() const {
if (m_quoteValue)
return asQuotedString();
try {
return boost::get<std::string>(m_data);
} catch (...) {
throw std::runtime_error("Trying to access a " + type() + " attribute "
"as string");
}
}
/**
* Return the attribute as a quoted string if it is a string.
*/
std::string IFunction::Attribute::asQuotedString() const {
std::string attr;
try {
attr = boost::get<std::string>(m_data);
} catch (...) {
throw std::runtime_error("Trying to access a " + type() + " attribute "
"as string");
}
if (attr.empty())
return "\"\"";
std::string quoted(attr);
if (*(attr.begin()) != '\"')
quoted = "\"" + attr;
if (*(quoted.end() - 1) != '\"')
quoted += "\"";
return quoted;
}
/**
* Return the attribute as an unquoted string if it is a string.
*/
std::string IFunction::Attribute::asUnquotedString() const {
std::string attr;
try {
attr = boost::get<std::string>(m_data);
} catch (...) {
throw std::runtime_error("Trying to access a " + type() + " attribute "
"as string");
}
std::string unquoted(attr);
if (attr.empty())
return "";
if (*(attr.begin()) == '\"')
unquoted = std::string(attr.begin() + 1, attr.end() - 1);
if (*(unquoted.end() - 1) == '\"')
unquoted = std::string(unquoted.begin(), unquoted.end() - 1);
return unquoted;
}
/**
* Return the attribute as an int if it is a int.
*/
int IFunction::Attribute::asInt() const {
try {
return boost::get<int>(m_data);
} catch (...) {
throw std::runtime_error("Trying to access a " + type() + " attribute "
"as int");
}
}
/**
* Return the attribute as a double if it is a double.
*/
double IFunction::Attribute::asDouble() const {
try {
return boost::get<double>(m_data);
} catch (...) {
throw std::runtime_error("Trying to access a " + type() + " attribute "
"as double");
}
}
/**
* Return the attribute as a bool if it is a bool.
*/
bool IFunction::Attribute::asBool() const {
try {
return boost::get<bool>(m_data);
} catch (...) {
throw std::runtime_error("Trying to access a " + type() + " attribute "
"as bool");
}
}
/**
* Return the attribute as a bool if it is a vector.
*/
std::vector<double> IFunction::Attribute::asVector() const {
try {
return boost::get<std::vector<double>>(m_data);
} catch (...) {
throw std::runtime_error("Trying to access a " + type() + " attribute "
"as vector");
}
}
/** Sets new value if attribute is a string. If the type is wrong
* throws an exception
* @param str :: The new value
*/
void IFunction::Attribute::setString(const std::string &str) {
try {
boost::get<std::string>(m_data) = str;
} catch (...) {
throw std::runtime_error("Trying to access a " + type() + " attribute "
"as string");
}
}
/** Sets new value if attribute is a double. If the type is wrong
* throws an exception
* @param d :: The new value
*/
void IFunction::Attribute::setDouble(const double &d) {
try {
boost::get<double>(m_data) = d;
} catch (...) {
throw std::runtime_error("Trying to access a " + type() + " attribute "
"as double");
}
}
/** Sets new value if attribute is an int. If the type is wrong
* throws an exception
* @param i :: The new value
*/
void IFunction::Attribute::setInt(const int &i) {
try {
boost::get<int>(m_data) = i;
} catch (...) {
throw std::runtime_error("Trying to access a " + type() + " attribute "
"as int");
}
}
/** Sets new value if attribute is an bool. If the type is wrong
* throws an exception
* @param b :: The new value
*/
void IFunction::Attribute::setBool(const bool &b) {
try {
boost::get<bool>(m_data) = b;
} catch (...) {
throw std::runtime_error("Trying to access a " + type() + " attribute "
"as bool");
}
}
/**
* Sets new value if attribute is a vector. If the type is wrong
* throws an exception
* @param v :: The new value
*/
void IFunction::Attribute::setVector(const std::vector<double> &v) {
try {
auto &value = boost::get<std::vector<double>>(m_data);
value.assign(v.begin(), v.end());
} catch (...) {
throw std::runtime_error("Trying to access a " + type() + " attribute "
"as vector");
}
}
namespace {
/**
* Attribute visitor setting new value to an attribute
*/
class SetValue : public IFunction::AttributeVisitor<> {
public:
/**
* Constructor
* @param value :: The value to set
*/
SetValue(const std::string &value) : m_value(value) {}
protected:
/// Apply if string
void apply(std::string &str) const { str = m_value; }
/// Apply if int
void apply(int &i) const {
std::istringstream istr(m_value + " ");
istr >> i;
if (!istr.good())
throw std::invalid_argument("Failed to set int attribute "
"from string " +
m_value);
}
/// Apply if double
void apply(double &d) const {
std::istringstream istr(m_value + " ");
istr >> d;
if (!istr.good())
throw std::invalid_argument("Failed to set double attribute "
"from string " +
m_value);
}
/// Apply if bool
void apply(bool &b) const {
b = (m_value == "true" || m_value == "TRUE" || m_value == "1");
}
/// Apply if vector
void apply(std::vector<double> &v) const {
if (m_value.empty()) {
v.clear();
return;
}
if (m_value.size() > 2) {
// check if the value is in barckets (...)
if (m_value[0] == '(' && m_value[m_value.size() - 1] == ')') {
m_value.erase(0, 1);
m_value.erase(m_value.size() - 1);
}
}
Poco::StringTokenizer tokenizer(m_value, ",",
Poco::StringTokenizer::TOK_TRIM);
v.resize(tokenizer.count());
for (size_t i = 0; i < v.size(); ++i) {
v[i] = boost::lexical_cast<double>(tokenizer[i]);
}
}
private:
mutable std::string m_value; ///<the value as a string
};
}
/** Set value from a string. Throws exception if the string has wrong format
* @param str :: String representation of the new value
*/
void IFunction::Attribute::fromString(const std::string &str) {
SetValue tmp(str);
apply(tmp);
}
/// Value of i-th active parameter. Override this method to make fitted
/// parameters different from the declared
double IFunction::activeParameter(size_t i) const {
if (!isActive(i)) {
throw std::runtime_error("Attempt to use an inactive parameter");
}
return getParameter(i);
}
/// Set new value of i-th active parameter. Override this method to make fitted
/// parameters different from the declared
void IFunction::setActiveParameter(size_t i, double value) {
if (!isActive(i)) {
throw std::runtime_error("Attempt to use an inactive parameter");
}
setParameter(i, value);
}
/**
* Returns the name of an active parameter.
* @param i :: Index of a parameter. The parameter must be active.
*/
std::string IFunction::nameOfActive(size_t i) const {
if (!isActive(i)) {
throw std::runtime_error("Attempt to use an inactive parameter");
}
return parameterName(i);
}
/**
* Returns the description of an active parameter.
* @param i :: Index of a parameter. The parameter must be active.
*/
std::string IFunction::descriptionOfActive(size_t i) const {
if (!isActive(i)) {
throw std::runtime_error("Attempt to use an inactive parameter");
}
return parameterDescription(i);
}
/** Calculate numerical derivatives.
* @param domain :: The domain of the function
* @param jacobian :: A Jacobian matrix. It is expected to have dimensions of
* domain.size() by nParams().
*/
void IFunction::calNumericalDeriv(const FunctionDomain &domain,
Jacobian &jacobian) {
const double minDouble = std::numeric_limits<double>::min();
const double epsilon = std::numeric_limits<double>::epsilon() * 100;
double stepPercentage = 0.001; // step percentage
double step; // real step
double cutoff = 100.0 * minDouble / stepPercentage;
size_t nParam = nParams();
size_t nData = domain.size();
FunctionValues minusStep(domain);
FunctionValues plusStep(domain);
// PARALLEL_CRITICAL(numeric_deriv)
{
applyTies(); // just in case
function(domain, minusStep);
}
for (size_t iP = 0; iP < nParam; iP++) {
if (isActive(iP)) {
const double val = activeParameter(iP);
if (fabs(val) < cutoff) {
step = epsilon;
} else {
step = val * stepPercentage;
}
double paramPstep = val + step;
// PARALLEL_CRITICAL(numeric_deriv)
{
setActiveParameter(iP, paramPstep);
applyTies();
function(domain, plusStep);
setActiveParameter(iP, val);
}
step = paramPstep - val;
for (size_t i = 0; i < nData; i++) {
jacobian.set(i, iP,
(plusStep.getCalculated(i) - minusStep.getCalculated(i)) /
step);
}
}
}
}
/** Initialize the function providing it the workspace
* @param workspace :: The workspace to set
* @param wi :: The workspace index
* @param startX :: The lower bin index
* @param endX :: The upper bin index
*/
void IFunction::setMatrixWorkspace(
boost::shared_ptr<const API::MatrixWorkspace> workspace, size_t wi,
double startX, double endX) {
UNUSED_ARG(startX);
UNUSED_ARG(endX);
if (!workspace)
return; // unset the workspace
try {
// check if parameter are specified in instrument definition file
const Geometry::ParameterMap ¶mMap = workspace->instrumentParameters();
Geometry::IDetector_const_sptr det;
size_t numDetectors = workspace->getSpectrum(wi)->getDetectorIDs().size();
if (numDetectors > 1) {
// If several detectors are on this workspace index, just use the ID of
// the first detector
// Note JZ oct 2011 - I'm not sure why the code uses the first detector
// and not the group. Ask Roman.
Instrument_const_sptr inst = workspace->getInstrument();
det = inst->getDetector(
*workspace->getSpectrum(wi)->getDetectorIDs().begin());
} else
// Get the detector (single) at this workspace index
det = workspace->getDetector(wi);
;
for (size_t i = 0; i < nParams(); i++) {
if (!isExplicitlySet(i)) {
Geometry::Parameter_sptr param =
paramMap.getRecursive(&(*det), parameterName(i), "fitting");
if (param != Geometry::Parameter_sptr()) {
// get FitParameter
const Geometry::FitParameter &fitParam =
param->value<Geometry::FitParameter>();
// check first if this parameter is actually specified for this
// function
if (name().compare(fitParam.getFunction()) == 0) {
// update value
IFunctionWithLocation *testWithLocation =
dynamic_cast<IFunctionWithLocation *>(this);
if (testWithLocation == NULL ||
(fitParam.getLookUpTable().containData() == false &&
fitParam.getFormula().compare("") == 0)) {
setParameter(i, fitParam.getValue());
} else {
double centreValue = testWithLocation->centre();
Kernel::Unit_sptr centreUnit; // unit of value used in formula or
// to look up value in lookup table
if (fitParam.getFormula().compare("") == 0)
centreUnit = fitParam.getLookUpTable().getXUnit(); // from table
else {
if (!fitParam.getFormulaUnit().empty()) {
try {
centreUnit = Kernel::UnitFactory::Instance().create(
fitParam.getFormulaUnit()); // from formula
} catch (...) {
g_log.warning()
<< fitParam.getFormulaUnit()
<< " Is not an recognised formula unit for parameter "
<< fitParam.getName() << "\n";
}
}
}
// if unit specified convert centre value to unit required by
// formula or look-up-table
if (centreUnit) {
g_log.debug()
<< "For FitParameter " << parameterName(i)
<< " centre of peak before any unit convertion is "
<< centreValue << std::endl;
centreValue =
convertValue(centreValue, centreUnit, workspace, wi);
g_log.debug() << "For FitParameter " << parameterName(i)
<< " centre of peak after any unit convertion is "
<< centreValue << std::endl;
}
double paramValue = fitParam.getValue(centreValue);
// this returned param value by a formula or a look-up-table may
// have
// a unit of its own. If set convert param value
// See section 'Using fitting parameters in
// www.mantidproject.org/IDF
if (fitParam.getFormula().compare("") == 0) {
// so from look up table
Kernel::Unit_sptr resultUnit =
fitParam.getLookUpTable().getYUnit(); // from table
g_log.debug() << "The FitParameter " << parameterName(i)
<< " = " << paramValue
<< " before y-unit convertion" << std::endl;
paramValue /= convertValue(1.0, resultUnit, workspace, wi);
g_log.debug() << "The FitParameter " << parameterName(i)
<< " = " << paramValue
<< " after y-unit convertion" << std::endl;
} else {
// so from formula
std::string resultUnitStr = fitParam.getResultUnit();
if (!resultUnitStr.empty()) {
std::vector<std::string> allUnitStr =
Kernel::UnitFactory::Instance().getKeys();
for (unsigned iUnit = 0; iUnit < allUnitStr.size(); iUnit++) {
size_t found = resultUnitStr.find(allUnitStr[iUnit]);
if (found != std::string::npos) {
size_t len = allUnitStr[iUnit].size();
std::stringstream readDouble;
Kernel::Unit_sptr unt =
Kernel::UnitFactory::Instance().create(
allUnitStr[iUnit]);
readDouble << 1.0 / convertValue(1.0, unt, workspace, wi);
resultUnitStr.replace(found, len, readDouble.str());
}
} // end for
try {
mu::Parser p;
p.SetExpr(resultUnitStr);
g_log.debug() << "The FitParameter " << parameterName(i)
<< " = " << paramValue
<< " before result-unit convertion (using "
<< resultUnitStr << ")" << std::endl;
paramValue *= p.Eval();
g_log.debug() << "The FitParameter " << parameterName(i)
<< " = " << paramValue
<< " after result-unit convertion"
<< std::endl;
} catch (mu::Parser::exception_type &e) {
g_log.error()
<< "Cannot convert formula unit to workspace unit"
<< " Formula unit which cannot be passed is "
<< resultUnitStr
<< ". Muparser error message is: " << e.GetMsg()
<< std::endl;
}
} // end if
} // end trying to convert result-unit from formula or y-unit for
// lookuptable
setParameter(i, paramValue);
} // end of update parameter value
// add tie if specified for this parameter in instrument definition
// file
if (fitParam.getTie().compare("")) {
std::ostringstream str;
str << getParameter(i);
tie(parameterName(i), str.str());
}
// add constraint if specified for this parameter in instrument
// definition file
if (fitParam.getConstraint().compare("")) {
IConstraint *constraint =
ConstraintFactory::Instance().createInitialized(
this, fitParam.getConstraint());
if (fitParam.getConstraintPenaltyFactor().compare("")) {
try {
double penalty =
atof(fitParam.getConstraintPenaltyFactor().c_str());
constraint->setPenaltyFactor(penalty);
} catch (...) {
g_log.warning()
<< "Can't set penalty factor for constraint\n";
}
}
addConstraint(constraint);
}
}
}
}
}
} catch (...) {
}
}
/** Convert a value from unit defined in workspace (ws) to outUnit
*
* @param value :: assumed to be in unit of workspace
* @param outUnit :: unit to convert to
* @param ws :: workspace
* @param wsIndex :: workspace index
* @return converted value
*/
double IFunction::convertValue(double value, Kernel::Unit_sptr &outUnit,
boost::shared_ptr<const MatrixWorkspace> ws,
size_t wsIndex) const {
// only required if formula or look-up-table different from ws unit
const auto &wsUnit = ws->getAxis(0)->unit();
if (outUnit->unitID().compare(wsUnit->unitID()) == 0)
return value;
// first check if it is possible to do a quick conversion and convert
// slight duplication to below to avoid instantiating vector unless necessary
double factor(0.0), power(0.0);
if (wsUnit->quickConversion(*outUnit, factor, power)) {
return factor * std::pow(value, power);
} else {
std::vector<double> singleValue(1, value);
convertValue(singleValue, outUnit, ws, wsIndex);
return singleValue.front();
}
}
/** Convert values from unit defined in workspace (ws) to outUnit
*
* @param values :: As input: assumed to be in unit of workspace.
* As output: in unit of outUnit
* @param outUnit :: unit to convert to
* @param ws :: workspace
* @param wsIndex :: workspace index
*/
void IFunction::convertValue(std::vector<double> &values,
Kernel::Unit_sptr &outUnit,
boost::shared_ptr<const MatrixWorkspace> ws,
size_t wsIndex) const {
// only required if formula or look-up-table different from ws unit
const auto &wsUnit = ws->getAxis(0)->unit();
if (outUnit->unitID().compare(wsUnit->unitID()) == 0)
return;
// first check if it is possible to do a quick conversion convert
double factor, power;
if (wsUnit->quickConversion(*outUnit, factor, power)) {
auto iend = values.end();
for (auto itr = values.begin(); itr != iend; ++itr)
(*itr) = factor * std::pow(*itr, power);
} else {
// Get l1, l2 and theta (see also RemoveBins.calculateDetectorPosition())
Instrument_const_sptr instrument = ws->getInstrument();
Geometry::IComponent_const_sptr sample = instrument->getSample();
if (sample == NULL) {
g_log.error()
<< "No sample defined instrument. Cannot convert units for function\n"
<< "Ignore convertion.";
return;
}
double l1 = instrument->getSource()->getDistance(*sample);
Geometry::IDetector_const_sptr det = ws->getDetector(wsIndex);
double l2(-1.0), twoTheta(0.0);
if (!det->isMonitor()) {
l2 = det->getDistance(*sample);
twoTheta = ws->detectorTwoTheta(det);
} else // If this is a monitor then make l1+l2 = source-detector distance
// and twoTheta=0
{
l2 = det->getDistance(*(instrument->getSource()));
l2 = l2 - l1;
twoTheta = 0.0;
}
int emode = static_cast<int>(ws->getEMode());
double efixed(0.0);
try {
efixed = ws->getEFixed(det);
} catch (std::exception &) {
// assume elastic
efixed = 0.0;
emode = 0;
}
std::vector<double> emptyVec;
wsUnit->toTOF(values, emptyVec, l1, l2, twoTheta, emode, efixed, 0.0);
outUnit->fromTOF(values, emptyVec, l1, l2, twoTheta, emode, efixed, 0.0);
}
}
/**
* Returns the number of attributes associated with the function
*/
size_t IFunction::nAttributes() const { return m_attrs.size(); }
/// Check if attribute named exists
bool IFunction::hasAttribute(const std::string &name) const {
return m_attrs.find(name) != m_attrs.end();
}
/**
* Overload for const char* values.
* @param attName :: Attribute name
* @param value :: New attribute value to set
*/
void IFunction::setAttributeValue(const std::string &attName,
const char *value) {
std::string str(value);
setAttributeValue(attName, str);
}
/**
* Set string attribute by value. Make sure that quoted style doesn't change.
* @param attName :: Attribute name
* @param value :: New attribute value to set
*/
void IFunction::setAttributeValue(const std::string &attName,
const std::string &value) {
Attribute att = getAttribute(attName);
att.setString(value);
setAttribute(attName, att);
}
/// Returns a list of attribute names
std::vector<std::string> IFunction::getAttributeNames() const {
std::vector<std::string> names(nAttributes(), "");
size_t index(0);
for (auto iter = m_attrs.begin(); iter != m_attrs.end(); ++iter) {
names[index] = iter->first;
++index;
}
return names;
}
/**
* Return a value of attribute attName
* @param name :: Returns the named attribute
*/
API::IFunction::Attribute
IFunction::getAttribute(const std::string &name) const {
if (hasAttribute(name)) {
return m_attrs.at(name);
} else {
throw std::invalid_argument(
"ParamFunctionAttributeHolder::getAttribute - Unknown attribute '" +
name + "'");
}
}
/**
* Set a value to a named attribute. Can be overridden in the inheriting class,
* the default
* just stores the value
* @param name :: The name of the attribute
* @param value :: The value of the attribute
*/
void IFunction::setAttribute(const std::string &name,
const API::IFunction::Attribute &value) {
storeAttributeValue(name, value);
}
/**
* Declares a single attribute
* @param name :: The name of the attribute
* @param defaultValue :: A default value
*/
void IFunction::declareAttribute(
const std::string &name, const API::IFunction::Attribute &defaultValue) {
m_attrs.insert(std::make_pair(name, defaultValue));
}
/// Initialize the function. Calls declareAttributes & declareParameters
void IFunction::init() {
declareAttributes();
declareParameters();
}
/**
* Set a value to a named attribute
* @param name :: The name of the attribute
* @param value :: The value of the attribute
*/
void IFunction::storeAttributeValue(const std::string &name,
const API::IFunction::Attribute &value) {
if (hasAttribute(name)) {
m_attrs[name] = value;
} else {
throw std::invalid_argument(
"ParamFunctionAttributeHolder::setAttribute - Unknown attribute '" +
name + "'");
}
}
/**
* Set the covariance matrix. Algorithm Fit sets this matrix to the top-level
* function
* after fitting. If the function is composite the matrix isn't set to its
* members.
* The matrix must be square and its size equal to the number of parameters of
* this function.
* @param covar :: A matrix to set.
*/
void IFunction::setCovarianceMatrix(
boost::shared_ptr<Kernel::Matrix<double>> covar) {
// the matrix shouldn't be empty
if (!covar) {
throw std::invalid_argument(
"IFunction: Cannot set an empty covariance matrix");
}
// the matrix should relate to this function
if (covar->numRows() != nParams() || covar->numCols() != nParams()) {
throw std::invalid_argument(
"IFunction: Covariance matrix has a wrong size");
}
m_covar = covar;
}
} // namespace API
} // namespace Mantid
///\cond TEMPLATE
namespace Mantid {
namespace Kernel {
template <>
MANTID_API_DLL boost::shared_ptr<Mantid::API::IFunction>
IPropertyManager::getValue<boost::shared_ptr<Mantid::API::IFunction>>(
const std::string &name) const {
PropertyWithValue<boost::shared_ptr<Mantid::API::IFunction>> *prop =
dynamic_cast<
PropertyWithValue<boost::shared_ptr<Mantid::API::IFunction>> *>(
getPointerToProperty(name));
if (prop) {
return *prop;
} else {
std::string message = "Attempt to assign property " + name +
" to incorrect type. Expected IFitFunction.";
throw std::runtime_error(message);
}
}
} // namespace Kernel
} // namespace Mantid
///\endcond TEMPLATE