// 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 & CSNS, Institute of High Energy Physics, CAS // SPDX - License - Identifier: GPL - 3.0 + //---------------------------------------------------------------------- // Includes //---------------------------------------------------------------------- #include "MantidAPI/IPowderDiffPeakFunction.h" #include "MantidAPI/Jacobian.h" #include "MantidKernel/ConfigService.h" #include "MantidKernel/Exception.h" #include <boost/lexical_cast.hpp> #include <cmath> const double IGNOREDCHANGE = 1.0E-9; namespace Mantid { namespace API { int IPowderDiffPeakFunction::s_peakRadius = 5; //---------------------------------------------------------------------------------------------- /** Constructor. Sets peak radius to the value of curvefitting.peakRadius * property */ IPowderDiffPeakFunction::IPowderDiffPeakFunction() : m_centre(0.), m_dcentre(0.), m_fwhm(0.), m_hasNewParameterValue(false), m_cellParamValueChanged(false), m_sortedProfileParameterNames(), m_unitCell(), m_unitCellSize(0.), m_parameterValid(false), mH(0), mK(0), mL(0), mHKLSet(false), LATTICEINDEX(9999), HEIGHTINDEX(9999) { // Set peak's radius from configuration auto peakRadius = Kernel::ConfigService::Instance().getValue<int>( "curvefitting.peakRadius"); if (peakRadius.is_initialized() && peakRadius.get() != s_peakRadius) { setPeakRadius(peakRadius.get()); } } //---------------------------------------------------------------------------------------------- /** Override setting parameter by parameter index * @param i :: parameter index in function; * @param value :: parameter name * @param explicitlySet :: */ void IPowderDiffPeakFunction::setParameter(size_t i, const double &value, bool explicitlySet) { double origparamvalue = getParameter(i); if (fabs(origparamvalue - value) > IGNOREDCHANGE) { m_hasNewParameterValue = true; } ParamFunction::setParameter(i, value, explicitlySet); } //---------------------------------------------------------------------------------------------- /** Overriding setting parameter by parameter name * @param name :: name of the parameter to set * @param value :: parameter name * @param explicitlySet :: */ void IPowderDiffPeakFunction::setParameter(const std::string &name, const double &value, bool explicitlySet) { double origparamvalue = getParameter(name); if (fabs(origparamvalue - value) > IGNOREDCHANGE) { m_hasNewParameterValue = true; } ParamFunction::setParameter(name, value, explicitlySet); } //---------------------------------------------------------------------------------------------- /** Get peak centre */ double IPowderDiffPeakFunction::centre() const { // Re-calcualte peak parameters if required if (m_hasNewParameterValue) calculateParameters(false); return m_centre; } //---------------------------------------------------------------------------------------------- /** Get peak height double IPowderDiffPeakFunction::height() const { return m_intensity; } */ //---------------------------------------------------------------------------------------------- /** Set peak height (intensity indeed) void IPowderDiffPeakFunction::setHeight(const double h) { m_intensity = h; return; } */ //---------------------------------------------------------------------------------------------- /** Set peak height */ void IPowderDiffPeakFunction::setHeight(const double h) { setParameter(HEIGHTINDEX, h); } //---------------------------------------------------------------------------------------------- /** Get peak's height */ double IPowderDiffPeakFunction::height() const { double height = this->getParameter(HEIGHTINDEX); return height; } //---------------------------------------------------------------------------------------------- /** Get peak's FWHM */ double IPowderDiffPeakFunction::fwhm() const { if (m_hasNewParameterValue) calculateParameters(false); return m_fwhm; } //---------------------------------------------------------------------------------------------- /** Get maximum value on a given set of data points */ double IPowderDiffPeakFunction::getMaximumValue(const std::vector<double> &xValues, size_t &indexmax) const { // Calculate function with given data points std::vector<double> out(xValues.size(), 0.); function(out, xValues); // Find out the maximum value double max = -DBL_MAX; indexmax = 0; for (size_t i = 0; i < out.size(); ++i) { if (out[i] > max) { max = out[i]; indexmax = i; } } return max; } //---------------------------------------------------------------------------------------------- /** Set Miller Indices for this peak */ void IPowderDiffPeakFunction::setMillerIndex(int h, int k, int l) { // Check validity and set flag if (mHKLSet) { // Throw exception if tried to reset the miller index std::stringstream errss; errss << "Profile function " << name() << "cannot have (HKL) reset."; throw std::runtime_error(errss.str()); } else { // Set flag mHKLSet = true; } // Set value mH = static_cast<int>(h); mK = static_cast<int>(k); mL = static_cast<int>(l); // Check value valid or not if (mH * mH + mK * mK + mL * mL < 1.0E-8) { std::stringstream errmsg; errmsg << "H = K = L = 0 is not allowed"; throw std::invalid_argument(errmsg.str()); } } //---------------------------------------------------------------------------------------------- /** Get Miller Index from this peak */ void IPowderDiffPeakFunction::getMillerIndex(int &h, int &k, int &l) { h = static_cast<int>(mH); k = static_cast<int>(mK); l = static_cast<int>(mL); } //---------------------------------------------------------------------------------------------- /** Set peak radius * @param r :: radius */ void IPowderDiffPeakFunction::setPeakRadius(const int &r) { if (r > 0) { s_peakRadius = r; // std::string setting = boost::lexical_cast<std::string>(r); // Kernel::ConfigService::Instance().setString("curvefitting.peakRadius",setting); } } //---------------------------------------------------------------------------------------------- /** Check whether a parameter is a profile parameter */ bool IPowderDiffPeakFunction::hasProfileParameter(std::string paramname) { auto candname = lower_bound(m_sortedProfileParameterNames.begin(), m_sortedProfileParameterNames.end(), paramname); if (candname == m_sortedProfileParameterNames.end()) return false; return *candname != paramname; } //------------------------- External Functions //--------------------------------------------------- // FIXME : This is the same function used for ThermalNeutron... ... /** Implementation of complex integral E_1 */ std::complex<double> E1(std::complex<double> z) { std::complex<double> exp_e1; double rz = real(z); double az = abs(z); if (fabs(az) < 1.0E-8) { // If z = 0, then the result is infinity... diverge! std::complex<double> r(1.0E300, 0.0); exp_e1 = r; } else if (az <= 10.0 || (rz < 0.0 && az < 20.0)) { // Some interesting region, equal to integrate to infinity, converged // cout << "[DB] Type 1\n"; std::complex<double> r(1.0, 0.0); exp_e1 = r; std::complex<double> cr = r; for (size_t k = 1; k <= 150; ++k) { auto dk = double(k); cr = -cr * dk * z / ((dk + 1.0) * (dk + 1.0)); exp_e1 += cr; if (abs(cr) < abs(exp_e1) * 1.0E-15) { // cr is converged to zero break; } } // ENDFOR k const double el = 0.5772156649015328; exp_e1 = -el - log(z) + (z * exp_e1); } else { // Rest of the region std::complex<double> ct0(0.0, 0.0); for (int k = 120; k > 0; --k) { std::complex<double> dk(double(k), 0.0); ct0 = dk / (10.0 + dk / (z + ct0)); } // ENDFOR k exp_e1 = 1.0 / (z + ct0); exp_e1 = exp_e1 * exp(-z); if (rz < 0.0 && fabs(imag(z)) < 1.0E-10) { std::complex<double> u(0.0, 1.0); exp_e1 = exp_e1 - (M_PI * u); } } return exp_e1; } } // namespace API } // namespace Mantid