Mantid
Loading...
Searching...
No Matches
IntegratePeaksShapeMD.cpp
Go to the documentation of this file.
1// Mantid Repository : https://github.com/mantidproject/mantid
2//
3// Copyright © 2026 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
10#include "MantidAPI/Run.h"
18
23
24#include <boost/math/special_functions/round.hpp>
25#include <cmath>
26#include <string>
27
28using namespace Mantid::API;
29using namespace Mantid::DataObjects;
30using namespace Mantid::Kernel;
31
32namespace Mantid::MDAlgorithms {
33
34// Register the algorithm into the AlgorithmFactory
35DECLARE_ALGORITHM(IntegratePeaksShapeMD)
36
37
38const std::string IntegratePeaksShapeMD::name() const { return "IntegratePeaksShapeMD"; }
39
41int IntegratePeaksShapeMD::version() const { return 1; }
42
48const std::string IntegratePeaksShapeMD::category() const { return "Crystal\\Integration"; }
49
54 auto ws_valid = std::make_shared<CompositeValidator>();
55 ws_valid->add<InstrumentValidator>();
56
57 auto mustBePositive = std::make_shared<BoundedValidator<double>>();
58 mustBePositive->setLower(0.0);
59
61 std::make_unique<WorkspaceProperty<MatrixWorkspace>>("InputWorkspace", "", Direction::Input, ws_valid),
62 "An input MatrixWorkspace with time-of-flight units along "
63 "X-axis and defined instrument with defined sample");
64
65 declareProperty(std::make_unique<WorkspaceProperty<PeaksWorkspace>>("PeaksWorkspace", "", Direction::InOut),
66 "Workspace with peaks to be integrated. Each peak must already have an "
67 "ellipsoidal shape set, e.g. from a previous IntegrateEllipsoids run.");
68
69 declareProperty("RegionRadius", .35, mustBePositive,
70 "Only events at most this distance from a peak will be considered when "
71 "integrating. Must be at least as large as the largest background outer "
72 "radius among the peaks being integrated, or the background shell will be "
73 "truncated.");
74
75 declareProperty("UseOnePercentBackgroundCorrection", true,
76 "If this options is enabled, then the top 1% of the background will be "
77 "removed before the background subtraction.");
78
79 declareProperty("ProfileFit", false,
80 "If true, integrate by maximizing the Poisson log-likelihood of a Gaussian "
81 "peak plus a flat background rate fit against the raw events, instead of "
82 "counting events inside/outside ellipsoidal boundaries. In this mode the "
83 "peak radii are interpreted as the Gaussian's standard deviations (1-sigma), "
84 "and the background radii are unused.");
85
86 declareProperty("AdjustCenter", false,
87 "Only used if ProfileFit is true. If true, also refine each peak's center by "
88 "a bounded Gauss-Newton correction (capped at one standard deviation from the "
89 "peak's stored Q) as part of the profile fit, instead of keeping it fixed at "
90 "the peak's stored Q. The peak's stored Q is not modified; the correction is "
91 "used only for this integration.");
92
93 declareProperty(std::make_unique<WorkspaceProperty<PeaksWorkspace>>("OutputWorkspace", "", Direction::Output),
94 "The output PeaksWorkspace will be a copy of the input PeaksWorkspace "
95 "with the peaks' integrated intensities.");
96}
97
105 PeaksWorkspace_sptr input_peak_ws = getProperty("PeaksWorkspace");
106 MatrixWorkspace_sptr input_ws = getProperty("InputWorkspace");
107 EventWorkspace_sptr eventWS = std::dynamic_pointer_cast<EventWorkspace>(input_ws);
108
109 Workspace2D_sptr histoWS = std::dynamic_pointer_cast<Workspace2D>(input_ws);
110 if (!eventWS && !histoWS) {
111 throw std::runtime_error("IntegratePeaksShapeMD needs either an "
112 "EventWorkspace or Workspace2D as input.");
113 }
114
115 PeaksWorkspace_sptr peak_ws = getProperty("OutputWorkspace");
116 if (peak_ws != input_peak_ws) {
117 peak_ws = input_peak_ws->clone();
118 }
119
120 std::vector<Peak> &peaks = peak_ws->getPeaks();
121 size_t n_peaks = peak_ws->getNumberPeaks();
122
123 // Integrate3DEvents uses UBinv to assign each event to its nearest peak
124 // (by rounding UBinv*Q to the nearest h,k,l), so it must be derived from
125 // the indexed peaks even though integration itself stays in Q-lab.
126 std::vector<V3D> peak_q_list;
127 std::vector<V3D> hkl_vectors;
128 std::vector<std::pair<std::pair<double, double>, V3D>> qList;
129 for (size_t i = 0; i < n_peaks; i++) {
130 const auto *shape = dynamic_cast<const PeakShapeEllipsoid *>(&peaks[i].getPeakShape());
131 if (!shape)
132 throw std::runtime_error("Peak " + std::to_string(i) +
133 " does not have an ellipsoidal shape. Integrate the "
134 "PeaksWorkspace first, e.g. with IntegrateEllipsoids, "
135 "so that every peak has a shape to reuse.");
136 if (shape->frame() != Kernel::QLab)
137 throw std::runtime_error("Peak " + std::to_string(i) + " has an ellipsoidal shape that is not in QLab.");
138
139 V3D hkl(peaks[i].getH(), peaks[i].getK(), peaks[i].getL());
140 if (Geometry::IndexingUtils::ValidIndex(hkl, 1.0)) { // tolerance == 1 just checks for (0,0,0)
141 peak_q_list.emplace_back(peaks[i].getQLabFrame());
142 hkl_vectors.emplace_back(static_cast<double>(boost::math::iround<double>(hkl[0])),
143 static_cast<double>(boost::math::iround<double>(hkl[1])),
144 static_cast<double>(boost::math::iround<double>(hkl[2])));
145 }
146 qList.emplace_back(std::pair<double, double>(1.0, 1.0), V3D(peaks[i].getQLabFrame()));
147 }
148
149 if (peak_q_list.size() < 3)
150 throw std::runtime_error("At least three linearly independent indexed peaks are needed.");
151
152 Matrix<double> UB(3, 3, false);
153 Geometry::IndexingUtils::Optimize_UB(UB, hkl_vectors, peak_q_list);
154 Matrix<double> UBinv(UB);
155 UBinv.Invert();
156 UBinv *= (1.0 / (2.0 * M_PI));
157
158 const bool useOnePercentBackgroundCorrection = getProperty("UseOnePercentBackgroundCorrection");
159 Integrate3DEvents integrator(qList, UBinv, getProperty("RegionRadius"), useOnePercentBackgroundCorrection);
160
161 Progress prog(this, 0.0, 1.0, input_ws->getNumberHistograms());
162 if (eventWS) {
163 qListFromEventWS(integrator, prog, eventWS);
164 } else {
165 qListFromHistoWS(integrator, prog, histoWS);
166 }
167
168 const bool profileFit = getProperty("ProfileFit");
169 const bool adjustCenter = getProperty("AdjustCenter");
170
171 for (size_t i = 0; i < n_peaks; i++) {
172 auto &peak = peaks[i];
173 const auto *shape = dynamic_cast<const PeakShapeEllipsoid *>(&peak.getPeakShape());
174
175 double inti = 0.0;
176 double sigi = 0.0;
177 if (profileFit) {
178 V3D center;
179 integrator.integrateUsingShapeProfileFit(*shape, peak.getQLabFrame(), adjustCenter, center, inti, sigi);
180 // Persist a center correction found by AdjustCenter as the output
181 // shape's translation -- peak.getQLabFrame() itself is left alone,
182 // since it's tied to a specific detector pixel/TOF and isn't free to
183 // move without checking that still corresponds to a valid trajectory.
184 if (adjustCenter && center != shape->translation()) {
185 peak.setPeakShape(new PeakShapeEllipsoid(
186 shape->directions(), shape->abcRadii(), shape->abcRadiiBackgroundInner(), shape->abcRadiiBackgroundOuter(),
187 shape->frame(), shape->algorithmName(), shape->algorithmVersion(), center));
188 }
189 } else {
190 integrator.integrateUsingShape(*shape, peak.getQLabFrame(), inti, sigi);
191 }
192
193 peak.setIntensity(inti);
194 peak.setSigmaIntensity(sigi);
195 }
196
197 // This flag is used by the PeaksWorkspace to evaluate whether it has been
198 // integrated.
199 peak_ws->mutableRun().addProperty("PeaksIntegrated", 1, true);
200 setProperty("OutputWorkspace", peak_ws);
201}
202
204 const std::string ELASTIC("Elastic");
205 const std::string Q3D("Q3D");
206 const std::size_t DIMS(3);
207
208 MDWSDescription m_targWSDescr;
209 m_targWSDescr.setMinMax(std::vector<double>(3, -2000.), std::vector<double>(3, 2000.));
210 m_targWSDescr.buildFromMatrixWS(wksp, Q3D, ELASTIC);
211 m_targWSDescr.setLorentsCorr(false);
212
213 Mantid::API::Algorithm_sptr childAlg = createChildAlgorithm("PreprocessDetectorsToMD", 0., .5);
214 childAlg->setProperty("InputWorkspace", wksp);
215 childAlg->executeAsChildAlg();
216
217 DataObjects::TableWorkspace_sptr table = childAlg->getProperty("OutputWorkspace");
218 if (!table)
219 throw(std::runtime_error("Can not retrieve results of \"PreprocessDetectorsToMD\""));
220
221 m_targWSDescr.m_PreprDetTable = table;
222
223 auto numSpectra = static_cast<int>(wksp->getNumberHistograms());
225 for (int i = 0; i < numSpectra; ++i) {
227
228 UnitsConversionHelper unitConverter;
229 unitConverter.initialize(m_targWSDescr, "Momentum");
230
231 MDTransfQ3D qConverter;
232 qConverter.initialize(m_targWSDescr);
233
234 std::vector<double> buffer(DIMS);
235 EventList events = wksp->getSpectrum(i);
236
238 events.compressEvents(1e-5, &events);
239
240 if (events.empty()) {
241 prog.report();
242 continue;
243 }
244
245 std::vector<Mantid::coord_t> locCoord(DIMS, 0.);
246 unitConverter.updateConversion(i);
247 qConverter.calcYDepCoordinates(locCoord, i);
248
249 double signal(1.);
250 double errorSq(1.);
251 const std::vector<WeightedEventNoTime> &raw_events = events.getWeightedEventsNoTime();
252 std::vector<std::pair<std::pair<double, double>, V3D>> qList;
253 for (const auto &raw_event : raw_events) {
254 double val = unitConverter.convertUnits(raw_event.tof());
255 qConverter.calcMatrixCoord(val, locCoord, signal, errorSq);
256 for (size_t dim = 0; dim < DIMS; ++dim) {
257 buffer[dim] = locCoord[dim];
258 }
259 V3D qVec(buffer[0], buffer[1], buffer[2]);
260 qList.emplace_back(std::pair<double, double>(raw_event.m_weight, raw_event.m_errorSquared), qVec);
261 }
262 PARALLEL_CRITICAL(addEvents) { integrator.addEvents(qList, false); }
263
264 prog.report();
266 }
268}
269
282 const std::string ELASTIC("Elastic");
283 const std::string Q3D("Q3D");
284 const std::size_t DIMS(3);
285
286 MDWSDescription m_targWSDescr;
287 m_targWSDescr.setMinMax(std::vector<double>(3, -2000.), std::vector<double>(3, 2000.));
288 m_targWSDescr.buildFromMatrixWS(wksp, Q3D, ELASTIC);
289 m_targWSDescr.setLorentsCorr(false);
290
291 Mantid::API::Algorithm_sptr childAlg = createChildAlgorithm("PreprocessDetectorsToMD", 0., .5);
292 childAlg->setProperty("InputWorkspace", wksp);
293 childAlg->executeAsChildAlg();
294
295 DataObjects::TableWorkspace_sptr table = childAlg->getProperty("OutputWorkspace");
296 if (!table)
297 throw(std::runtime_error("Can not retrieve results of \"PreprocessDetectorsToMD\""));
298 m_targWSDescr.m_PreprDetTable = table;
299
300 auto numSpectra = static_cast<int>(wksp->getNumberHistograms());
302 for (int i = 0; i < numSpectra; ++i) {
304
305 UnitsConversionHelper unitConverter;
306 unitConverter.initialize(m_targWSDescr, "Momentum");
307
308 MDTransfQ3D qConverter;
309 qConverter.initialize(m_targWSDescr);
310
311 const auto &xVals = wksp->points(i);
312 const auto &yVals = wksp->y(i);
313 const auto &eVals = wksp->e(i);
314
315 std::vector<Mantid::coord_t> locCoord(DIMS, 0.);
316 unitConverter.updateConversion(i);
317 qConverter.calcYDepCoordinates(locCoord, i);
318
319 double signal(1.);
320 double errorSq(1.);
321
322 std::vector<std::pair<std::pair<double, double>, V3D>> qList;
323
324 for (size_t j = 0; j < yVals.size(); ++j) {
325 const double &yVal = yVals[j];
326 const double &esqVal = eVals[j] * eVals[j];
327 if (yVal > 0) {
328 double val = unitConverter.convertUnits(xVals[j]);
329 qConverter.calcMatrixCoord(val, locCoord, signal, errorSq);
330 V3D qVec(locCoord[0], locCoord[1], locCoord[2]);
331
332 if (std::isnan(qVec[0]) || std::isnan(qVec[1]) || std::isnan(qVec[2]))
333 continue;
334 qList.emplace_back(std::pair<double, double>(yVal, esqVal), qVec);
335 }
336 }
337 PARALLEL_CRITICAL(addHisto) { integrator.addEvents(qList, false); }
338 prog.report();
340 }
342}
343} // namespace Mantid::MDAlgorithms
std::string name
Definition Run.cpp:60
#define DECLARE_ALGORITHM(classname)
Definition Algorithm.h:542
#define PARALLEL_START_INTERRUPT_REGION
Begins a block to skip processing is the algorithm has been interupted Note the end of the block if n...
#define PARALLEL_CRITICAL(name)
#define PARALLEL_END_INTERRUPT_REGION
Ends a block to skip processing is the algorithm has been interupted Note the start of the block if n...
#define PARALLEL_FOR_IF(condition)
Empty definitions - to enable set your complier to enable openMP.
#define PARALLEL_CHECK_INTERRUPT_REGION
Adds a check after a Parallel region to see if it was interupted.
void declareProperty(std::unique_ptr< Kernel::Property > p, const std::string &doc="") override
Add a property to the list of managed properties.
TypedValue getProperty(const std::string &name) const override
Get the value of a property.
virtual std::shared_ptr< Algorithm > createChildAlgorithm(const std::string &name, const double startProgress=-1., const double endProgress=-1., const bool enableLogging=true, const int &version=-1)
Create a Child Algorithm.
A validator which checks that a workspace has a valid instrument.
Helper class for reporting progress from algorithms.
Definition Progress.h:25
A property class for workspaces.
A class for holding :
Definition EventList.h:58
void compressEvents(double tolerance, EventList *destination)
Compress the event list by grouping events with the same TOF (within a given tolerance).
std::vector< WeightedEventNoTime > & getWeightedEventsNoTime()
Return the list of WeightedEvent contained.
void switchTo(Mantid::API::EventType newType) override
Switch the EventList to use the given EventType (TOF, WEIGHTED, or WEIGHTED_NOTIME)
bool empty() const
Much like stl containers, returns true if there is nothing in the event list.
PeakShapeEllipsoid : PeakShape representing a 3D ellipsoid.
static double Optimize_UB(Kernel::DblMatrix &UB, const std::vector< Kernel::V3D > &hkl_vectors, const std::vector< Kernel::V3D > &q_vectors, std::vector< double > &sigabc)
Find the UB matrix that most nearly maps hkl to qxyz for 3 or more peaks.
static bool ValidIndex(const Kernel::V3D &hkl, double tolerance)
Check is hkl is within tolerance of integer (h,k,l) non-zero values.
IPropertyManager * setProperty(const std::string &name, const T &value)
Templated method to set the value of a PropertyWithValue.
Numerical Matrix class.
Definition Matrix.h:42
T Invert()
LU inversion routine.
Definition Matrix.cpp:924
void report()
Increments the loop counter by 1, then sends the progress notification on behalf of its algorithm.
Class for 3D vectors.
Definition V3D.h:34
This is a low-level class to construct a map with lists of events near each peak Q-vector,...
void integrateUsingShapeProfileFit(const Mantid::DataObjects::PeakShapeEllipsoid &shape, const Mantid::Kernel::V3D &peak_q, bool adjustCenter, Mantid::Kernel::V3D &center, double &inti, double &sigi)
Integrate a peak using a shape supplied by the caller, by maximizing the Poisson log-likelihood of a ...
void integrateUsingShape(const Mantid::DataObjects::PeakShapeEllipsoid &shape, const Mantid::Kernel::V3D &peak_q, double &inti, double &sigi)
Integrate a peak using a shape supplied by the caller (e.g.
void addEvents(std::vector< std::pair< std::pair< double, double >, Mantid::Kernel::V3D > > const &event_qs, bool hkl_integ)
Add event Q's to lists of events near peaks.
IntegratePeaksShapeMD integrates single crystal Bragg peaks using the ellipsoidal peak shape already ...
int version() const override
Gets the algorithm version.
const std::string category() const override
Get the category of this algorithm.
void init() override
Defines the input, integration, fitting, and output properties for the algorithm.
void exec() override
Integrates peak intensities from an event or histogram workspace using QLab ellipsoidal peak shapes.
void qListFromEventWS(Integrate3DEvents &integrator, API::Progress &prog, DataObjects::EventWorkspace_sptr &wksp)
void qListFromHistoWS(Integrate3DEvents &integrator, API::Progress &prog, DataObjects::Workspace2D_sptr &wksp)
Converts histogram workspace data to Q-space events for integration.
Class responsible for conversion of input workspace data into proper number of output dimensions for ...
Definition MDTransfQ3D.h:28
bool calcMatrixCoord(const double &deltaEOrK0, std::vector< coord_t > &Coord, double &s, double &err) const override
Calculates 3D transformation of the variable coordinates and (if applicable) signal and error dependi...
void initialize(const MDWSDescription &ConvParams) override
function initalizes all variables necessary for converting workspace variables into MD variables in M...
bool calcYDepCoordinates(std::vector< coord_t > &Coord, size_t i) override
Method updates the value of preprocessed detector coordinates in Q-space, used by other functions.
helper class describes the properties of target MD workspace, which should be obtained as the result ...
void setMinMax(const std::vector< double > &minVal, const std::vector< double > &maxVal)
function sets up min-max values to the dimensions, described by the class
void buildFromMatrixWS(const API::MatrixWorkspace_sptr &pWS, const std::string &QMode, const std::string &dEMode, const std::vector< std::string > &dimPropertyNames=std::vector< std::string >())
method builds MD Event ws description from a matrix workspace and the transformations,...
DataObjects::TableWorkspace_const_sptr m_PreprDetTable
void setLorentsCorr(bool On=false)
do we need to perform Lorentz corrections
void initialize(const MDWSDescription &targetWSDescr, const std::string &unitsTo, bool forceViaTOF=false)
Initialize unit conversion helper This method is interface to internal initialize method,...
std::shared_ptr< Algorithm > Algorithm_sptr
Typedef for a shared pointer to an Algorithm.
Definition Algorithm.h:52
std::shared_ptr< MatrixWorkspace > MatrixWorkspace_sptr
shared pointer to the matrix workspace base class
std::shared_ptr< PeaksWorkspace > PeaksWorkspace_sptr
Typedef for a shared pointer to a peaks workspace.
std::shared_ptr< Workspace2D > Workspace2D_sptr
shared pointer to Mantid::DataObjects::Workspace2D
std::shared_ptr< TableWorkspace > TableWorkspace_sptr
shared pointer to Mantid::DataObjects::TableWorkspace
std::shared_ptr< EventWorkspace > EventWorkspace_sptr
shared pointer to the EventWorkspace class
std::enable_if< std::is_pointer< Arg >::value, bool >::type threadSafe(Arg workspace)
Thread-safety check Checks the workspace to ensure it is suitable for multithreaded access.
const std::string Q3D("Q3D")
Only convert to Q-vector.
const std::size_t DIMS(3)
Q-vector is always three dimensional.
const std::string ELASTIC("Elastic")
This only works for diffraction.
STL namespace.
std::string to_string(const wide_integer< Bits, Signed > &n)
@ InOut
Both an input & output workspace.
Definition Property.h:55
@ Input
An input workspace.
Definition Property.h:53
@ Output
An output workspace.
Definition Property.h:54