Mantid
Loading...
Searching...
No Matches
PawleyFunction.cpp
Go to the documentation of this file.
1// Mantid Repository : https://github.com/mantidproject/mantid
2//
3// Copyright © 2018 ISIS Rutherford Appleton Laboratory UKRI,
4// NScD Oak Ridge National Laboratory, European Spallation Source,
5// Institut Laue - Langevin & CSNS, Institute of High Energy Physics, CAS
6// SPDX - License - Identifier: GPL - 3.0 +
8
9#include "MantidAPI/Axis.h"
12
14
18
19#include <boost/algorithm/string.hpp>
20#include <memory>
21
23
24using namespace CurveFitting;
25using namespace Constraints;
26
27DECLARE_FUNCTION(PawleyParameterFunction)
28
29using namespace API;
30
31using namespace Geometry;
32
33using namespace Kernel;
34
37 : ParamFunction(), m_latticeSystem(PointGroup::LatticeSystem::Triclinic), m_profileFunctionCenterParameterName() {}
38
48void PawleyParameterFunction::setAttribute(const std::string &attName, const Attribute &attValue) {
49 if (attName == "LatticeSystem") {
50 setLatticeSystem(attValue.asString());
51 } else if (attName == "ProfileFunction") {
52 setProfileFunction(attValue.asString());
53 }
54
55 ParamFunction::setAttribute(attName, attValue);
56}
57
60
63 switch (m_latticeSystem) {
64 case PointGroup::LatticeSystem::Cubic: {
65 double a = getParameter("a");
66 double aErr = getError(0);
67 UnitCell uc(a, a, a);
68 uc.setError(aErr, aErr, aErr, 0.0, 0.0, 0.0);
69 return uc;
70 }
71 case PointGroup::LatticeSystem::Tetragonal: {
72 double a = getParameter("a");
73 double aErr = getError(0);
74 UnitCell uc(a, a, getParameter("c"));
75 uc.setError(aErr, aErr, getError(1), 0.0, 0.0, 0.0);
76 return uc;
77 }
78 case PointGroup::LatticeSystem::Hexagonal: {
79 double a = getParameter("a");
80 double aErr = getError(0);
81 UnitCell uc(a, a, getParameter("c"), 90, 90, 120);
82 uc.setError(aErr, aErr, getError(1), 0.0, 0.0, 0.0);
83 return uc;
84 }
85 case PointGroup::LatticeSystem::Rhombohedral: {
86 double a = getParameter("a");
87 double alpha = getParameter("Alpha");
88 double aErr = getError(0);
89 double alphaErr = getError(1);
90 UnitCell uc(a, a, a, alpha, alpha, alpha);
91 uc.setError(aErr, aErr, aErr, alphaErr, alphaErr, alphaErr);
92 return uc;
93 }
94 case PointGroup::LatticeSystem::Orthorhombic: {
96 uc.setError(getError(0), getError(1), getError(2), 0.0, 0.0, 0.0);
97 return uc;
98 }
99 case PointGroup::LatticeSystem::Monoclinic: {
100 UnitCell uc(getParameter("a"), getParameter("b"), getParameter("c"), 90, getParameter("Beta"), 90);
101 uc.setError(getError(0), getError(1), getError(2), 0.0, getError(3), 0.0);
102 return uc;
103 }
104 case PointGroup::LatticeSystem::Triclinic: {
105 UnitCell uc(getParameter("a"), getParameter("b"), getParameter("c"), getParameter("Alpha"), getParameter("Beta"),
106 getParameter("Gamma"));
107 uc.setError(getError(0), getError(1), getError(2), getError(3), getError(4), getError(5));
108 return uc;
109 }
110 }
111
112 return UnitCell();
113}
114
117 // Parameter "a" exists in all crystal systems.
118 setParameter("a", cell.a());
119
120 try {
121 setParameter("b", cell.b());
122 } catch (const std::invalid_argument &) {
123 // do nothing.
124 }
125
126 try {
127 setParameter("c", cell.c());
128 } catch (const std::invalid_argument &) {
129 // do nothing
130 }
131
132 try {
133 setParameter("Alpha", cell.alpha());
134 } catch (const std::invalid_argument &) {
135 // do nothing.
136 }
137 try {
138 setParameter("Beta", cell.beta());
139 } catch (const std::invalid_argument &) {
140 // do nothing.
141 }
142 try {
143 setParameter("Gamma", cell.gamma());
144 } catch (const std::invalid_argument &) {
145 // do nothing.
146 }
147}
148
151 UNUSED_ARG(domain);
152 UNUSED_ARG(values);
153}
154
157 UNUSED_ARG(domain)
158 UNUSED_ARG(jacobian);
159}
160
163 declareAttribute("LatticeSystem", IFunction::Attribute("Triclinic"));
164 declareAttribute("ProfileFunction", IFunction::Attribute("Gaussian"));
165
166 setLatticeSystem("Triclinic");
167 setProfileFunction("Gaussian");
168}
169
179void PawleyParameterFunction::setProfileFunction(const std::string &profileFunction) {
180 IPeakFunction_sptr peakFunction =
181 std::dynamic_pointer_cast<IPeakFunction>(FunctionFactory::Instance().createFunction(profileFunction));
182
183 if (!peakFunction) {
184 throw std::invalid_argument("PawleyFunction can only use IPeakFunctions to "
185 "calculate peak profiles.");
186 }
187
189}
190
202void PawleyParameterFunction::setLatticeSystem(const std::string &latticeSystem) {
204
206}
207
211
213 switch (latticeSystem) {
214 case PointGroup::LatticeSystem::Cubic:
215 declareParameter("a", 1.0);
217 break;
218
219 case PointGroup::LatticeSystem::Hexagonal:
220 case PointGroup::LatticeSystem::Tetragonal:
221 declareParameter("a", 1.0);
222 declareParameter("c", 1.0);
225 break;
226
227 case PointGroup::LatticeSystem::Orthorhombic:
228 declareParameter("a", 1.0);
229 declareParameter("b", 1.0);
230 declareParameter("c", 1.0);
234 break;
235
236 case PointGroup::LatticeSystem::Monoclinic:
237 declareParameter("a", 1.0);
238 declareParameter("b", 1.0);
239 declareParameter("c", 1.0);
243
244 declareParameter("Beta", 90.0);
245 addAngleConstraint("Beta");
246 break;
247
248 case PointGroup::LatticeSystem::Rhombohedral:
249 declareParameter("a", 1.0);
250 declareParameter("Alpha", 90.0);
252 addAngleConstraint("Alpha");
253 break;
254
255 default:
256 // triclinic
257 declareParameter("a", 1.0);
258 declareParameter("b", 1.0);
259 declareParameter("c", 1.0);
263
264 declareParameter("Alpha", 90.0);
265 declareParameter("Beta", 90.0);
266 declareParameter("Gamma", 90.0);
267 addAngleConstraint("Alpha");
268 addAngleConstraint("Beta");
269 addAngleConstraint("Gamma");
270 break;
271 }
272
273 declareParameter("ZeroShift", 0.0);
274}
275
277void PawleyParameterFunction::addLengthConstraint(const std::string &parameterName) {
278 auto cellEdgeConstraint = std::make_unique<BoundaryConstraint>(this, parameterName, 0.0, true);
279 cellEdgeConstraint->setPenaltyFactor(1e12);
280 addConstraint(std::move(cellEdgeConstraint));
281}
282
284void PawleyParameterFunction::addAngleConstraint(const std::string &parameterName) {
285 auto cellAngleConstraint = std::make_unique<BoundaryConstraint>(this, parameterName, 0.0, 180.0, true);
286 cellAngleConstraint->setPenaltyFactor(1e12);
287 addConstraint(std::move(cellAngleConstraint));
288}
289
293 if (profileFunction) {
294 m_profileFunctionCenterParameterName = profileFunction->getCentreParameterName();
295 }
296}
297
299
300
302 : IPawleyFunction(), m_compositeFunction(), m_pawleyParameterFunction(), m_peakProfileComposite(), m_hkls(),
303 m_dUnit(), m_wsUnit(), m_peakRadius(5) {
304 auto peakRadius = Kernel::ConfigService::Instance().getValue<int>("curvefitting.peakRadius");
305 m_peakRadius = peakRadius.get_value_or(5);
306}
307
308void PawleyFunction::setMatrixWorkspace(std::shared_ptr<const MatrixWorkspace> workspace, size_t wi, double startX,
309 double endX) {
310 if (workspace) {
311 Axis *xAxis = workspace->getAxis(wi);
312 Kernel::Unit_sptr wsUnit = xAxis->unit();
313
314 if (std::dynamic_pointer_cast<Units::Empty>(wsUnit) || std::dynamic_pointer_cast<Units::dSpacing>(wsUnit)) {
316 } else {
317 double factor, power;
318 if (wsUnit->quickConversion(*m_dUnit, factor, power)) {
319 m_wsUnit = wsUnit;
320 } else {
321 throw std::invalid_argument("Can not use quick conversion for unit.");
322 }
323 }
324 }
325
326 m_wrappedFunction->setMatrixWorkspace(workspace, wi, startX, endX);
327}
328
331void PawleyFunction::setLatticeSystem(const std::string &latticeSystem) {
332 m_pawleyParameterFunction->setAttributeValue("LatticeSystem", latticeSystem);
333 m_compositeFunction->checkFunction();
334}
335
338void PawleyFunction::setProfileFunction(const std::string &profileFunction) {
339 m_pawleyParameterFunction->setAttributeValue("ProfileFunction", profileFunction);
340
341 /* At this point PawleyParameterFunction guarantees that it's an IPeakFunction
342 * and all existing profile functions are replaced.
343 */
344 for (size_t i = 0; i < m_peakProfileComposite->nFunctions(); ++i) {
345 IPeakFunction_sptr oldFunction = std::dynamic_pointer_cast<IPeakFunction>(m_peakProfileComposite->getFunction(i));
346
347 IPeakFunction_sptr newFunction = std::dynamic_pointer_cast<IPeakFunction>(
348 FunctionFactory::Instance().createFunction(m_pawleyParameterFunction->getProfileFunctionName()));
349
350 newFunction->setCentre(oldFunction->centre());
351 try {
352 newFunction->setFwhm(oldFunction->fwhm());
353 } catch (...) {
354 // do nothing.
355 }
356 newFunction->setHeight(oldFunction->height());
357
358 m_peakProfileComposite->replaceFunction(i, newFunction);
359 }
360
361 // Update exposed parameters.
362 m_compositeFunction->checkFunction();
363}
364
366void PawleyFunction::setUnitCell(const std::string &unitCellString) {
367 m_pawleyParameterFunction->setParametersFromUnitCell(strToUnitCell(unitCellString));
368}
369
372 if ((m_dUnit && m_wsUnit) && m_dUnit != m_wsUnit) {
374 }
375
376 return d;
377}
378
379void PawleyFunction::setPeakPositions(const std::string &centreName, double zeroShift, const UnitCell &cell) const {
380 for (size_t i = 0; i < m_hkls.size(); ++i) {
381 double centre = getTransformedCenter(cell.d(m_hkls[i]));
382
383 m_peakProfileComposite->getFunction(i)->setParameter(centreName, centre + zeroShift);
384 }
385}
386
388 API::FunctionValues &localValues) const {
389 size_t domainSize = domain.size();
390 const double *domainBegin = domain.getPointerAt(0);
391 const double *domainEnd = domain.getPointerAt(domainSize);
392
393 double centre = peak->centre();
394 double dx = m_peakRadius * peak->fwhm();
395
396 auto lb = std::lower_bound(domainBegin, domainEnd, centre - dx);
397 auto ub = std::upper_bound(lb, domainEnd, centre + dx);
398
399 size_t n = std::distance(lb, ub);
400
401 if (n == 0) {
402 throw std::invalid_argument("Null-domain");
403 }
404
405 FunctionDomain1DView localDomain(lb, n);
406 localValues.reset(localDomain);
407
408 peak->functionLocal(localValues.getPointerToCalculated(0), localDomain.getPointerAt(0), n);
409
410 return std::distance(domainBegin, lb);
411}
412
425void PawleyFunction::function(const FunctionDomain &domain, FunctionValues &values) const {
426 values.zeroCalculated();
427 try {
428 const auto &domain1D = dynamic_cast<const FunctionDomain1D &>(domain);
429
430 UnitCell cell = m_pawleyParameterFunction->getUnitCellFromParameters();
431 double zeroShift = m_pawleyParameterFunction->getParameter("ZeroShift");
432 std::string centreName = m_pawleyParameterFunction->getProfileFunctionCenterParameterName();
433
434 setPeakPositions(centreName, zeroShift, cell);
435
436 FunctionValues localValues;
437
438 for (size_t i = 0; i < m_peakProfileComposite->nFunctions(); ++i) {
439 IPeakFunction_sptr peak = std::dynamic_pointer_cast<IPeakFunction>(m_peakProfileComposite->getFunction(i));
440
441 try {
442 size_t offset = calculateFunctionValues(peak, domain1D, localValues);
443 values.addToCalculated(offset, localValues);
444 } catch (const std::invalid_argument &) {
445 // do nothing
446 }
447 }
448
449 setPeakPositions(centreName, 0.0, cell);
450 } catch (const std::bad_cast &) {
451 // do nothing
452 }
453}
454
458 std::dynamic_pointer_cast<CompositeFunction>(FunctionFactory::Instance().createFunction("CompositeFunction"));
459 m_compositeFunction->replaceFunction(1, m_peakProfileComposite);
460 m_hkls.clear();
461}
462
465void PawleyFunction::setPeaks(const std::vector<Kernel::V3D> &hkls, double fwhm, double height) {
466 clearPeaks();
467
468 for (const auto &hkl : hkls) {
469 addPeak(hkl, fwhm, height);
470 }
471}
472
474void PawleyFunction::addPeak(const Kernel::V3D &hkl, double fwhm, double height) {
475 m_hkls.emplace_back(hkl);
476
477 IPeakFunction_sptr peak = std::dynamic_pointer_cast<IPeakFunction>(
478 FunctionFactory::Instance().createFunction(m_pawleyParameterFunction->getProfileFunctionName()));
479
480 peak->fix(peak->parameterIndex(m_pawleyParameterFunction->getProfileFunctionCenterParameterName()));
481
482 try {
483 peak->setFwhm(fwhm);
484 } catch (...) {
485 // do nothing.
486 }
487
488 peak->setHeight(height);
489
490 m_peakProfileComposite->addFunction(peak);
491
492 m_compositeFunction->checkFunction();
493}
494
496size_t PawleyFunction::getPeakCount() const { return m_hkls.size(); }
497
499 if (i >= m_hkls.size()) {
500 throw std::out_of_range("Peak index out of range.");
501 }
502
503 return std::dynamic_pointer_cast<IPeakFunction>(m_peakProfileComposite->getFunction(i));
504}
505
508 if (i >= m_hkls.size()) {
509 throw std::out_of_range("Peak index out of range.");
510 }
511
512 return m_hkls[i];
513}
514
517
519 setDecoratedFunction("CompositeFunction");
520
521 if (!m_compositeFunction) {
522 throw std::runtime_error("PawleyFunction could not construct internal CompositeFunction.");
523 }
524
525 m_dUnit = UnitFactory::Instance().create("dSpacing");
526}
527
530 CompositeFunction_sptr composite = std::dynamic_pointer_cast<CompositeFunction>(fn);
531
532 if (!composite) {
533 throw std::invalid_argument("PawleyFunction only works with "
534 "CompositeFunction. Selecting another "
535 "decorated function is not possible.");
536 }
537
538 m_compositeFunction = composite;
539
540 if (m_compositeFunction->nFunctions() == 0) {
542 std::dynamic_pointer_cast<CompositeFunction>(FunctionFactory::Instance().createFunction("CompositeFunction"));
543
544 m_pawleyParameterFunction = std::dynamic_pointer_cast<PawleyParameterFunction>(
545 FunctionFactory::Instance().createFunction("PawleyParameterFunction"));
546
549 } else {
550 m_pawleyParameterFunction = std::dynamic_pointer_cast<PawleyParameterFunction>(m_compositeFunction->getFunction(0));
551 m_peakProfileComposite = std::dynamic_pointer_cast<CompositeFunction>(m_compositeFunction->getFunction(1));
552 }
553}
554
555} // namespace Mantid::CurveFitting::Functions
#define DECLARE_FUNCTION(classname)
Macro for declaring a new type of function to be used with the FunctionFactory.
double height
Definition: GetAllEi.cpp:155
IPeaksWorkspace_sptr workspace
Definition: IndexPeaks.cpp:114
#define UNUSED_ARG(x)
Function arguments are sometimes unused in certain implmentations but are required for documentation ...
Definition: System.h:64
Class to represent the axis of a workspace.
Definition: Axis.h:30
const std::shared_ptr< Kernel::Unit > & unit() const
The unit for this axis.
Definition: Axis.cpp:28
1D domain - a wrapper around an array of doubles.
Represent a domain for functions of one real argument.
size_t size() const override
Return the number of arguments in the domain.
const double * getPointerAt(size_t i) const
Get a pointer to i-th value.
Base class that represents the domain of a function.
void setDecoratedFunction(const std::string &wrappedFunctionName)
A class to store values calculated by a function.
void addToCalculated(size_t i, double value)
Add a number to a calculated value.
void zeroCalculated()
Set all calculated values to zero.
double * getPointerToCalculated(size_t i)
Get a pointer to calculated data at index i.
void reset(const FunctionDomain &domain)
Reset the values to match a new domain.
Attribute is a non-fitting parameter.
Definition: IFunction.h:282
std::string asString() const
Returns string value if attribute is a string, throws exception otherwise.
Definition: IFunction.cpp:660
virtual double getParameter(size_t i) const =0
Get i-th parameter.
void declareAttribute(const std::string &name, const API::IFunction::Attribute &defaultValue)
Declare a single attribute.
Definition: IFunction.cpp:1418
virtual void setParameter(size_t, const double &value, bool explicitlySet=true)=0
Set i-th parameter.
virtual std::string parameterName(size_t i) const =0
Returns the name of parameter i.
virtual double getError(size_t i) const =0
Get the fitting error for a parameter.
virtual void addConstraint(std::unique_ptr< IConstraint > ic)
Add a constraint to function.
Definition: IFunction.cpp:376
virtual void declareParameter(const std::string &name, double initValue=0, const std::string &description="")=0
Declare a new parameter.
Represents the Jacobian in IFitFunction::functionDeriv.
Definition: Jacobian.h:22
Implements the part of IFunction interface dealing with parameters.
Definition: ParamFunction.h:33
void clearAllParameters()
Nonvirtual member which removes all declared parameters.
The Pawley approach to obtain lattice parameters from a powder diffractogram works by placing peak pr...
PawleyParameterFunction_sptr m_pawleyParameterFunction
API::CompositeFunction_sptr m_compositeFunction
Kernel::V3D getPeakHKL(size_t i) const override
Return the HKL of the i-th peak.
PawleyParameterFunction_sptr getPawleyParameterFunction() const
Returns the internally stored PawleyParameterFunction.
API::CompositeFunction_sptr m_peakProfileComposite
size_t getPeakCount() const override
Returns the number of peaks that are stored in the function.
API::IPeakFunction_sptr getPeakFunction(size_t i) const override
Returns the profile function stored for the i-th peak.
void function(const API::FunctionDomain &domain, API::FunctionValues &values) const override
Calculates the function values on the supplied domain.
size_t calculateFunctionValues(const API::IPeakFunction_sptr &peak, const API::FunctionDomain1D &domain, API::FunctionValues &localValues) const
void addPeak(const Kernel::V3D &hkl, double fwhm, double height) override
Adds a peak with the supplied FWHM and height.
double getTransformedCenter(double d) const
Transform d value to workspace unit.
void beforeDecoratedFunctionSet(const API::IFunction_sptr &fn) override
Checks that the decorated function has the correct structure.
void clearPeaks() override
Removes all peaks from the function.
void setUnitCell(const std::string &unitCellString) override
Sets the unit cell from a string with either 6 or 3 space-separated numbers.
void setLatticeSystem(const std::string &latticeSystem) override
Sets the crystal system on the internal parameter function and updates the exposed parameters.
void setPeakPositions(const std::string &centreName, double zeroShift, const Geometry::UnitCell &cell) const
void setPeaks(const std::vector< Kernel::V3D > &hkls, double fwhm, double height) override
Clears peaks and adds a peak for each hkl, all with the same FWHM and height.
void setMatrixWorkspace(std::shared_ptr< const API::MatrixWorkspace > workspace, size_t wi, double startX, double endX) override
Set matrix workspace.
void setProfileFunction(const std::string &profileFunction) override
Sets the profile function and replaces already existing functions in the internally stored CompositeF...
void setProfileFunction(const std::string &profileFunction)
Sets the profile function.
void addAngleConstraint(const std::string &parameterName)
Adds a default constraint so cell angles are in the range 0 to 180.
void setAttribute(const std::string &attName, const Attribute &attValue) override
Sets the supplied attribute value.
void setParametersFromUnitCell(const Geometry::UnitCell &cell)
Sets the function's parameters from the supplied UnitCell.
void createLatticeSystemParameters(Geometry::PointGroup::LatticeSystem latticeSystem)
This method clears all parameters and declares parameters according to the supplied crystal system.
Geometry::PointGroup::LatticeSystem getLatticeSystem() const
Returns the crystal system.
Geometry::PointGroup::LatticeSystem m_latticeSystem
Geometry::UnitCell getUnitCellFromParameters() const
Returns a UnitCell object constructed from the function's parameters.
void function(const API::FunctionDomain &domain, API::FunctionValues &values) const override
This method does nothing.
void init() override
Declares attributes and generates parameters based on the defaults.
void setLatticeSystem(const std::string &latticeSystem)
Assigns the lattice system.
void addLengthConstraint(const std::string &parameterName)
Adds a default constraint so that cell edge lengths can not be less than 0.
void functionDeriv(const API::FunctionDomain &domain, API::Jacobian &jacobian) override
This method does nothing.
void setCenterParameterNameFromFunction(const API::IPeakFunction_sptr &profileFunction)
Tries to extract and store the center parameter name from the function.
A class containing the Point Groups for a crystal.
Definition: PointGroup.h:31
Class to implement unit cell of crystals.
Definition: UnitCell.h:44
double alpha() const
Get lattice parameter.
Definition: UnitCell.cpp:133
double a(int nd) const
Get lattice parameter a1-a3 as function of index (0-2)
Definition: UnitCell.cpp:94
double c() const
Get lattice parameter.
Definition: UnitCell.cpp:128
double d(double h, double k, double l) const
Return d-spacing ( ) for a given h,k,l coordinate.
Definition: UnitCell.cpp:700
double beta() const
Get lattice parameter.
Definition: UnitCell.cpp:138
void setError(double _aerr, double _berr, double _cerr, double _alphaerr, double _betaerr, double _gammaerr, const int angleunit=angDegrees)
Set lattice parameter errors.
Definition: UnitCell.cpp:325
double b() const
Get lattice parameter.
Definition: UnitCell.cpp:123
double gamma() const
Get lattice parameter.
Definition: UnitCell.cpp:143
static T & Instance()
Return a reference to the Singleton instance, creating it if it does not already exist Creation is do...
static double run(const std::string &src, const std::string &dest, const double srcValue, const double l1, const double l2, const double theta, const DeltaEMode::Type emode, const double efixed)
Convert a single value between the given units (as strings)
Class for 3D vectors.
Definition: V3D.h:34
std::shared_ptr< IPeakFunction > IPeakFunction_sptr
std::shared_ptr< IFunction > IFunction_sptr
shared pointer to the function base class
Definition: IFunction.h:732
std::shared_ptr< CompositeFunction > CompositeFunction_sptr
shared pointer to the composite function base class
std::shared_ptr< PawleyParameterFunction > PawleyParameterFunction_sptr
MANTID_GEOMETRY_DLL PointGroup::LatticeSystem getLatticeSystemFromString(const std::string &latticeSystem)
Returns the lattice system enum that corresponds to the supplied string or throws an invalid_argument...
Definition: PointGroup.cpp:284
MANTID_GEOMETRY_DLL UnitCell strToUnitCell(const std::string &unitCellString)
Definition: UnitCell.cpp:896
std::shared_ptr< Unit > Unit_sptr
Shared pointer to the Unit base class.
Definition: Unit.h:229