Mantid
Loading...
Searching...
No Matches
IntegratePeaksMD.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 +
9#include "MantidAPI/Column.h"
16#include "MantidAPI/Run.h"
17#include "MantidAPI/TableRow.h"
18#include "MantidAPI/TextAxis.h"
25#include "MantidHistogramData/LinearGenerator.h"
27#include "MantidKernel/Utils.h"
29
30#include <algorithm>
31#include <cmath>
32#include <fstream>
33#include <gsl/gsl_integration.h>
34
35namespace Mantid::MDAlgorithms {
36
37// Register the algorithm into the AlgorithmFactory
38DECLARE_ALGORITHM(IntegratePeaksMD)
39
40using namespace Mantid::Kernel;
41using namespace Mantid::API;
42using namespace Mantid::DataObjects;
43using namespace Mantid::DataObjects;
44using namespace Mantid::Geometry;
45using namespace Mantid::HistogramData;
46
49 useAlgorithm("IntegratePeaksMD", 2);
50 deprecatedDate("2025-05-05");
51}
52
56 declareProperty(std::make_unique<WorkspaceProperty<IMDEventWorkspace>>("InputWorkspace", "", Direction::Input),
57 "An input MDEventWorkspace.");
58
59 std::vector<std::string> propOptions{"Q (lab frame)", "Q (sample frame)", "HKL"};
60 declareProperty("CoordinatesToUse", "Q (lab frame)", std::make_shared<StringListValidator>(propOptions),
61 "Ignored: algorithm uses the InputWorkspace's coordinates.");
62
63 declareProperty(std::make_unique<PropertyWithValue<double>>("PeakRadius", 1.0, Direction::Input),
64 "Fixed radius around each peak position in which to integrate (in the "
65 "same units as the workspace).");
66
67 declareProperty(std::make_unique<PropertyWithValue<double>>("BackgroundInnerRadius", 0.0, Direction::Input),
68 "Inner radius to use to evaluate the background of the peak.\n"
69 "If smaller than PeakRadius, then we assume BackgroundInnerRadius = "
70 "PeakRadius.");
71
72 declareProperty(std::make_unique<PropertyWithValue<double>>("BackgroundOuterRadius", 0.0, Direction::Input),
73 "Outer radius to use to evaluate the background of the peak.\n"
74 "The signal density around the peak (BackgroundInnerRadius < r < "
75 "BackgroundOuterRadius) is used to estimate the background under the "
76 "peak.\n"
77 "If smaller than PeakRadius, no background measurement is done.");
78
79 declareProperty(std::make_unique<WorkspaceProperty<PeaksWorkspace>>("PeaksWorkspace", "", Direction::Input),
80 "A PeaksWorkspace containing the peaks to integrate.");
81
82 declareProperty(std::make_unique<WorkspaceProperty<PeaksWorkspace>>("OutputWorkspace", "", Direction::Output),
83 "The output PeaksWorkspace will be a copy of the input PeaksWorkspace "
84 "with the peaks' integrated intensities.");
85
86 declareProperty("ReplaceIntensity", true,
87 "Always replace intensity in PeaksWorkspacem (default).\n"
88 "If false, then do not replace intensity if calculated value "
89 "is 0 (used for SNSSingleCrystalReduction)");
90
91 declareProperty("IntegrateIfOnEdge", true,
92 "Only warning if all of peak outer radius is not on detector (default).\n"
93 "If false, do not integrate if the outer radius is not on a detector.");
94
95 declareProperty("AdaptiveQRadius", false,
96 "Default is false. If true, all input radii are multiplied "
97 "by the magnitude of Q at the peak center so each peak has a "
98 "different integration radius.");
99
100 declareProperty("Cylinder", false, "Default is sphere. Use next five parameters for cylinder.");
101
102 declareProperty(std::make_unique<PropertyWithValue<double>>("CylinderLength", 0.0, Direction::Input),
103 "Length of cylinder in which to integrate (in the same units as the "
104 "workspace).");
105
106 declareProperty(std::make_unique<PropertyWithValue<double>>("PercentBackground", 0.0, Direction::Input),
107 "Percent of CylinderLength that is background (20 is 20%)");
108
109 std::vector<std::string> peakNames = FunctionFactory::Instance().getFunctionNames<IPeakFunction>();
110 peakNames.emplace_back("NoFit");
111 declareProperty("ProfileFunction", "Gaussian", std::make_shared<StringListValidator>(peakNames),
112 "Fitting function for profile that is used only with "
113 "Cylinder integration.");
114
115 std::vector<std::string> integrationOptions(2);
116 integrationOptions[0] = "Sum";
117 integrationOptions[1] = "GaussianQuadrature";
118 auto integrationvalidator = std::make_shared<StringListValidator>(integrationOptions);
119 declareProperty("IntegrationOption", "GaussianQuadrature", integrationvalidator,
120 "Integration method for calculating intensity "
121 "used only with Cylinder integration.");
122
123 declareProperty(std::make_unique<FileProperty>("ProfilesFile", "", FileProperty::OptionalSave,
124 std::vector<std::string>(1, "profiles")),
125 "Save (Optionally) as Isaw peaks file with profiles included");
126}
127
128//----------------------------------------------------------------------------------------------
133template <typename MDE, size_t nd> void IntegratePeaksMD::integrate(typename MDEventWorkspace<MDE, nd>::sptr ws) {
134 if (nd != 3)
135 throw std::invalid_argument("For now, we expect the input MDEventWorkspace "
136 "to have 3 dimensions only.");
137
139 Mantid::DataObjects::PeaksWorkspace_sptr inPeakWS = getProperty("PeaksWorkspace");
140
142 Mantid::DataObjects::PeaksWorkspace_sptr peakWS = getProperty("OutputWorkspace");
143 if (peakWS != inPeakWS)
144 peakWS = inPeakWS->clone();
145
147 std::string CoordinatesToUseStr = getPropertyValue("CoordinatesToUse");
149 g_log.warning() << " Warning" << CoordinatesToUse << '\n';
150 if (CoordinatesToUse == Kernel::QLab && CoordinatesToUseStr != "Q (lab frame)")
151 g_log.warning() << "Warning: used Q (lab frame) coordinates for MD "
152 "workspace, not CoordinatesToUse from input \n";
153 else if (CoordinatesToUse == Kernel::QSample && CoordinatesToUseStr != "Q (sample frame)")
154 g_log.warning() << "Warning: used Q (sample frame) coordinates for MD "
155 "workspace, not CoordinatesToUse from input \n";
156 else if (CoordinatesToUse == Kernel::HKL && CoordinatesToUseStr != "HKL")
157 g_log.warning() << "Warning: used HKL coordinates for MD workspace, not "
158 "CoordinatesToUse from input \n";
159
161 double PeakRadius = getProperty("PeakRadius");
163 double BackgroundOuterRadius = getProperty("BackgroundOuterRadius");
165 double BackgroundInnerRadius = getProperty("BackgroundInnerRadius");
166 if (BackgroundInnerRadius < PeakRadius)
167 BackgroundInnerRadius = PeakRadius;
169 double cylinderLength = getProperty("CylinderLength");
170 Workspace2D_sptr wsProfile2D, wsFit2D, wsDiff2D;
171 size_t numSteps = 0;
172 bool cylinderBool = getProperty("Cylinder");
173 bool adaptiveQRadius = getProperty("AdaptiveQRadius");
174 std::vector<double> PeakRadiusVector(peakWS->getNumberPeaks(), PeakRadius);
175 std::vector<double> BackgroundInnerRadiusVector(peakWS->getNumberPeaks(), BackgroundInnerRadius);
176 std::vector<double> BackgroundOuterRadiusVector(peakWS->getNumberPeaks(), BackgroundOuterRadius);
177 if (cylinderBool) {
178 numSteps = 100;
179 size_t histogramNumber = peakWS->getNumberPeaks();
180 Workspace_sptr wsProfile = WorkspaceFactory::Instance().create("Workspace2D", histogramNumber, numSteps, numSteps);
181 wsProfile2D = std::dynamic_pointer_cast<Workspace2D>(wsProfile);
182 AnalysisDataService::Instance().addOrReplace("ProfilesData", wsProfile2D);
183 Workspace_sptr wsFit = WorkspaceFactory::Instance().create("Workspace2D", histogramNumber, numSteps, numSteps);
184 wsFit2D = std::dynamic_pointer_cast<Workspace2D>(wsFit);
185 AnalysisDataService::Instance().addOrReplace("ProfilesFit", wsFit2D);
186 Workspace_sptr wsDiff = WorkspaceFactory::Instance().create("Workspace2D", histogramNumber, numSteps, numSteps);
187 wsDiff2D = std::dynamic_pointer_cast<Workspace2D>(wsDiff);
188 AnalysisDataService::Instance().addOrReplace("ProfilesFitDiff", wsDiff2D);
189 auto newAxis1 = std::make_unique<TextAxis>(peakWS->getNumberPeaks());
190 auto newAxis2 = std::make_unique<TextAxis>(peakWS->getNumberPeaks());
191 auto newAxis3 = std::make_unique<TextAxis>(peakWS->getNumberPeaks());
192 auto newAxis1Raw = newAxis1.get();
193 auto newAxis2Raw = newAxis2.get();
194 auto newAxis3Raw = newAxis3.get();
195 wsProfile2D->replaceAxis(1, std::move(newAxis1));
196 wsFit2D->replaceAxis(1, std::move(newAxis2));
197 wsDiff2D->replaceAxis(1, std::move(newAxis3));
198 for (int i = 0; i < peakWS->getNumberPeaks(); ++i) {
199 // Get a direct ref to that peak.
200 IPeak &p = peakWS->getPeak(i);
201 std::ostringstream label;
202 label << Utils::round(p.getH()) << "_" << Utils::round(p.getK()) << "_" << Utils::round(p.getL()) << "_"
203 << p.getRunNumber();
204 newAxis1Raw->setLabel(i, label.str());
205 newAxis2Raw->setLabel(i, label.str());
206 newAxis3Raw->setLabel(i, label.str());
207 }
208 }
209 double percentBackground = getProperty("PercentBackground");
210 size_t peakMin = 0;
211 size_t peakMax = numSteps;
212 double ratio = 0.0;
213 if (cylinderBool) {
214 peakMin = static_cast<size_t>(static_cast<double>(numSteps) * percentBackground / 100.);
215 peakMax = numSteps - peakMin - 1;
216 size_t numPeakCh = peakMax - peakMin + 1; // number of peak channels
217 size_t numBkgCh = numSteps - numPeakCh; // number of background channels
218 ratio = static_cast<double>(numPeakCh) / static_cast<double>(numBkgCh);
219 }
221 bool replaceIntensity = getProperty("ReplaceIntensity");
222 bool integrateEdge = getProperty("IntegrateIfOnEdge");
223
224 std::string profileFunction = getProperty("ProfileFunction");
225 std::string integrationOption = getProperty("IntegrationOption");
226 std::ofstream out;
227 if (cylinderBool && profileFunction != "NoFit") {
228 std::string outFile = getProperty("InputWorkspace");
229 outFile.append(profileFunction);
230 outFile.append(".dat");
231 std::string save_path = ConfigService::Instance().getString("defaultsave.directory");
232 outFile = save_path + outFile;
233 out.open(outFile.c_str(), std::ofstream::out);
234 }
235 //
236 // If the following OMP pragma is included, this algorithm seg faults
237 // sporadically when processing multiple TOPAZ runs in a script, on
238 // Scientific Linux 6.2. Typically, it seg faults after 2 to 6 runs are
239 // processed, though occasionally it will process all 8 requested in the
240 // script without crashing. Since the lower level codes already use OpenMP,
241 // parallelizing at this level is only marginally useful, giving about a
242 // 5-10% speedup. Perhaps is should just be removed permanantly, but for
243 // now it is commented out to avoid the seg faults. Refs #5533
244 // PRAGMA_OMP(parallel for schedule(dynamic, 10) )
245 for (int i = 0; i < peakWS->getNumberPeaks(); ++i) {
246 // Get a direct ref to that peak.
247 IPeak &p = peakWS->getPeak(i);
248
249 // Get the peak center as a position in the dimensions of the workspace
250 V3D pos;
251 if (CoordinatesToUse == Kernel::QLab) //"Q (lab frame)"
252 pos = p.getQLabFrame();
253 else if (CoordinatesToUse == Kernel::QSample) //"Q (sample frame)"
254 pos = p.getQSampleFrame();
255 else if (CoordinatesToUse == Kernel::HKL) //"HKL"
256 pos = p.getHKL();
257
258 // Get the instrument and its detectors
259 inst = peakWS->getInstrument();
260 // Do not integrate if sphere is off edge of detector
261 if (BackgroundOuterRadius > PeakRadius) {
262 if (!detectorQ(p.getQLabFrame(), BackgroundOuterRadius)) {
263 g_log.warning() << "Warning: sphere/cylinder for integration is off "
264 "edge of detector for peak "
265 << i << '\n';
266 if (!integrateEdge)
267 continue;
268 }
269 } else {
270 if (!detectorQ(p.getQLabFrame(), PeakRadius)) {
271 g_log.warning() << "Warning: sphere/cylinder for integration is off "
272 "edge of detector for peak "
273 << i << '\n';
274 if (!integrateEdge)
275 continue;
276 }
277 }
278
279 // Build the sphere transformation
280 bool dimensionsUsed[nd];
281 coord_t center[nd];
282 for (size_t d = 0; d < nd; ++d) {
283 dimensionsUsed[d] = true; // Use all dimensions
284 center[d] = static_cast<coord_t>(pos[d]);
285 }
286 signal_t signal = 0;
287 signal_t errorSquared = 0;
288 signal_t bgSignal = 0;
289 signal_t bgErrorSquared = 0;
290 double background_total = 0.0;
291 if (!cylinderBool) {
292 // modulus of Q
293 coord_t lenQpeak = 1.0;
294 if (adaptiveQRadius) {
295 lenQpeak = 0.0;
296 for (size_t d = 0; d < nd; d++) {
297 lenQpeak += center[d] * center[d];
298 }
299 lenQpeak = std::sqrt(lenQpeak);
300 }
301 PeakRadiusVector[i] = lenQpeak * PeakRadius;
302 BackgroundInnerRadiusVector[i] = lenQpeak * BackgroundInnerRadius;
303 BackgroundOuterRadiusVector[i] = lenQpeak * BackgroundOuterRadius;
304 CoordTransformDistance sphere(nd, center, dimensionsUsed);
305
306 if (auto *shapeablePeak = dynamic_cast<Peak *>(&p)) {
307
308 PeakShape *sphereShape =
309 new PeakShapeSpherical(PeakRadiusVector[i], BackgroundInnerRadiusVector[i], BackgroundOuterRadiusVector[i],
310 CoordinatesToUse, this->name(), this->version());
311 shapeablePeak->setPeakShape(sphereShape);
312 }
313
314 // Perform the integration into whatever box is contained within.
315 ws->getBox()->integrateSphere(sphere, static_cast<coord_t>(lenQpeak * PeakRadius * lenQpeak * PeakRadius), signal,
316 errorSquared);
317
318 // Integrate around the background radius
319
320 if (BackgroundOuterRadius > PeakRadius) {
321 // Get the total signal inside "BackgroundOuterRadius"
322 ws->getBox()->integrateSphere(
323 sphere, static_cast<coord_t>(lenQpeak * BackgroundOuterRadius * lenQpeak * BackgroundOuterRadius), bgSignal,
324 bgErrorSquared);
325
326 // Evaluate the signal inside "BackgroundInnerRadius"
327 signal_t interiorSignal = 0;
328 signal_t interiorErrorSquared = 0;
329
330 // Integrate this 3rd radius, if needed
331 if (BackgroundInnerRadius != PeakRadius) {
332 ws->getBox()->integrateSphere(
333 sphere, static_cast<coord_t>(lenQpeak * BackgroundInnerRadius * lenQpeak * BackgroundInnerRadius),
334 interiorSignal, interiorErrorSquared);
335 } else {
336 // PeakRadius == BackgroundInnerRadius, so use the previous value
337 interiorSignal = signal;
338 interiorErrorSquared = errorSquared;
339 }
340 // Subtract the peak part to get the intensity in the shell
341 // (BackgroundInnerRadius < r < BackgroundOuterRadius)
342 bgSignal -= interiorSignal;
343 // We can subtract the error (instead of adding) because the two values
344 // are 100% dependent; this is the same as integrating a shell.
345 bgErrorSquared -= interiorErrorSquared;
346
347 // Relative volume of peak vs the BackgroundOuterRadius sphere
348 const double radiusRatio = (PeakRadius / BackgroundOuterRadius);
349 const double peakVolume = radiusRatio * radiusRatio * radiusRatio;
350
351 // Relative volume of the interior of the shell vs overall background
352 const double interiorRatio = (BackgroundInnerRadius / BackgroundOuterRadius);
353 // Volume of the bg shell, relative to the volume of the
354 // BackgroundOuterRadius sphere
355 const double bgVolume = 1.0 - interiorRatio * interiorRatio * interiorRatio;
356
357 // Finally, you will multiply the bg intensity by this to get the
358 // estimated background under the peak volume
359 const double scaleFactor = peakVolume / bgVolume;
360 bgSignal *= scaleFactor;
361 bgErrorSquared *= scaleFactor * scaleFactor;
362 }
363 } else {
364 CoordTransformDistance cylinder(nd, center, dimensionsUsed, 2);
365
366 // Perform the integration into whatever box is contained within.
367 Counts signal_fit(numSteps);
368 signal_fit = 0;
369
370 ws->getBox()->integrateCylinder(cylinder, static_cast<coord_t>(PeakRadius), static_cast<coord_t>(cylinderLength),
371 signal, errorSquared, signal_fit.mutableRawData());
372
373 Points points(signal_fit.size(), LinearGenerator(0, 1));
374 wsProfile2D->setHistogram(i, points, signal_fit);
375
376 // Integrate around the background radius
377 if (BackgroundOuterRadius > PeakRadius) {
378 // Get the total signal inside "BackgroundOuterRadius"
379 signal_fit = 0;
380
381 ws->getBox()->integrateCylinder(cylinder, static_cast<coord_t>(BackgroundOuterRadius),
382 static_cast<coord_t>(cylinderLength), bgSignal, bgErrorSquared,
383 signal_fit.mutableRawData());
384
385 wsProfile2D->setHistogram(i, points, signal_fit);
386
387 // Evaluate the signal inside "BackgroundInnerRadius"
388 signal_t interiorSignal = 0;
389 signal_t interiorErrorSquared = 0;
390
391 // Integrate this 3rd radius, if needed
392 if (BackgroundInnerRadius != PeakRadius) {
393 ws->getBox()->integrateCylinder(cylinder, static_cast<coord_t>(BackgroundInnerRadius),
394 static_cast<coord_t>(cylinderLength), interiorSignal, interiorErrorSquared,
395 signal_fit.mutableRawData());
396 } else {
397 // PeakRadius == BackgroundInnerRadius, so use the previous value
398 interiorSignal = signal;
399 interiorErrorSquared = errorSquared;
400 }
401 // Subtract the peak part to get the intensity in the shell
402 // (BackgroundInnerRadius < r < BackgroundOuterRadius)
403 bgSignal -= interiorSignal;
404 // We can subtract the error (instead of adding) because the two values
405 // are 100% dependent; this is the same as integrating a shell.
406 bgErrorSquared -= interiorErrorSquared;
407 // Relative volume of peak vs the BackgroundOuterRadius cylinder
408 const double radiusRatio = (PeakRadius / BackgroundOuterRadius);
409 const double peakVolume = radiusRatio * radiusRatio * cylinderLength;
410
411 // Relative volume of the interior of the shell vs overall background
412 const double interiorRatio = (BackgroundInnerRadius / BackgroundOuterRadius);
413 // Volume of the bg shell, relative to the volume of the
414 // BackgroundOuterRadius cylinder
415 const double bgVolume = 1.0 - interiorRatio * interiorRatio * cylinderLength;
416
417 // Finally, you will multiply the bg intensity by this to get the
418 // estimated background under the peak volume
419 const double scaleFactor = peakVolume / bgVolume;
420 bgSignal *= scaleFactor;
421 bgErrorSquared *= scaleFactor * scaleFactor;
422 } else {
423 wsProfile2D->setHistogram(i, points, signal_fit);
424 }
425
426 if (profileFunction == "NoFit") {
427 auto &y = wsProfile2D->y(i);
428 // sum signal between range
429 signal = y.sum(peakMin, peakMax);
430 // sum background outside of range
431 background_total += y.sum(0, peakMin);
432 background_total += y.sum(peakMax);
433 errorSquared = std::abs(signal);
434 } else {
435 auto findpeaks = createChildAlgorithm("FindPeaks", -1, -1, false);
436 findpeaks->setProperty("InputWorkspace", wsProfile2D);
437 findpeaks->setProperty<int>("FWHM", 7);
438 findpeaks->setProperty<int>("Tolerance", 4);
439 // FindPeaks will do the checking on the validity of WorkspaceIndex
440 findpeaks->setProperty("WorkspaceIndex", static_cast<int>(i));
441
442 // Get the specified peak positions, which is optional
443 findpeaks->setProperty<std::string>("PeakFunction", profileFunction);
444 // FindPeaks will use linear or flat if they are better
445 findpeaks->setProperty<std::string>("BackgroundType", "Quadratic");
446 findpeaks->setProperty<bool>("HighBackground", true);
447 findpeaks->setProperty<bool>("RawPeakParameters", true);
448 std::vector<double> peakPosToFit;
449 peakPosToFit.emplace_back(static_cast<double>(numSteps) / 2.0);
450 findpeaks->setProperty("PeakPositions", peakPosToFit);
451 findpeaks->setProperty<int>("MinGuessedPeakWidth", 4);
452 findpeaks->setProperty<int>("MaxGuessedPeakWidth", 4);
453 try {
454 findpeaks->executeAsChildAlg();
455 } catch (...) {
456 g_log.error("Can't execute FindPeaks algorithm");
457 continue;
458 }
459
460 API::ITableWorkspace_sptr paramws = findpeaks->getProperty("PeaksList");
461 if (paramws->rowCount() < 1)
462 continue;
463 std::ostringstream fun_str;
464 fun_str << "name=" << profileFunction;
465
466 size_t numcols = paramws->columnCount();
467 std::vector<std::string> paramsName = paramws->getColumnNames();
468 std::vector<double> paramsValue;
469 API::TableRow row = paramws->getRow(0);
470 int spectrum;
471 row >> spectrum;
472 for (size_t j = 1; j < numcols; ++j) {
473 double parvalue;
474 row >> parvalue;
475 if (j == numcols - 4)
476 fun_str << ";name=Quadratic";
477 // erase f0. or f1.
478 // if (j > 0 && j < numcols-1) fun_str << "," <<
479 // paramsName[j].erase(0,3) <<"="<<parvalue;
480 if (j > 0 && j < numcols - 1)
481 fun_str << "," << paramsName[j] << "=" << parvalue;
482 paramsValue.emplace_back(parvalue);
483 }
484 if (i == 0) {
485 for (size_t j = 0; j < numcols; ++j)
486 out << std::setw(20) << paramsName[j] << " ";
487 out << "\n";
488 }
489 out << std::setw(20) << i;
490 for (size_t j = 0; j < numcols - 1; ++j)
491 out << std::setw(20) << std::fixed << std::setprecision(10) << paramsValue[j] << " ";
492 out << "\n";
493
494 // Evaluate fit at points
495
496 IFunction_sptr ifun = FunctionFactory::Instance().createInitialized(fun_str.str());
497 std::shared_ptr<const CompositeFunction> fun = std::dynamic_pointer_cast<const CompositeFunction>(ifun);
498 const auto &x = wsProfile2D->x(i);
499 wsFit2D->setSharedX(i, wsProfile2D->sharedX(i));
500 wsDiff2D->setSharedX(i, wsProfile2D->sharedX(i));
501
502 FunctionDomain1DVector domain(x.rawData());
503 FunctionValues yy(domain);
504 fun->function(domain, yy);
505 auto funcValues = yy.toVector();
506
507 wsFit2D->mutableY(i) = funcValues;
508 wsDiff2D->setSharedY(i, wsProfile2D->sharedY(i));
509 wsDiff2D->mutableY(i) -= wsFit2D->y(i);
510
511 // Calculate intensity
512 signal = 0.0;
513 if (integrationOption == "Sum") {
514
515 for (size_t j = peakMin; j <= peakMax; j++)
516 if (std::isfinite(yy[j]))
517 signal += yy[j];
518 } else {
519 gsl_integration_workspace *w = gsl_integration_workspace_alloc(1000);
520
521 double error;
522
523 gsl_function F;
524 F.function = &Mantid::MDAlgorithms::f_eval;
525 F.params = &fun;
526
527 gsl_integration_qags(&F, x[peakMin], x[peakMax], 0, 1e-7, 1000, w, &signal, &error);
528
529 gsl_integration_workspace_free(w);
530 }
531 errorSquared = std::fabs(signal);
532 // Get background counts
533 for (size_t j = 0; j < numSteps; j++) {
534 double background =
535 paramsValue[numcols - 3] * x[j] * x[j] + paramsValue[numcols - 4] * x[j] + paramsValue[numcols - 5];
536 if (j < peakMin || j > peakMax)
537 background_total = background_total + background;
538 }
539 }
540 }
541 // Save it back in the peak object.
542 if (signal != 0. || replaceIntensity) {
543 p.setIntensity(signal - ratio * background_total - bgSignal);
544 p.setSigmaIntensity(sqrt(errorSquared + ratio * ratio * std::fabs(background_total) + bgErrorSquared));
545 }
546
547 g_log.information() << "Peak " << i << " at " << pos << ": signal " << signal << " (sig^2 " << errorSquared
548 << "), with background " << bgSignal + ratio * background_total << " (sig^2 "
549 << bgErrorSquared + ratio * ratio * std::fabs(background_total) << ") subtracted.\n";
550 }
551 // This flag is used by the PeaksWorkspace to evaluate whether it has been
552 // integrated.
553 peakWS->mutableRun().addProperty("PeaksIntegrated", 1, true);
554 // These flags are specific to the algorithm.
555 peakWS->mutableRun().addProperty("PeakRadius", PeakRadiusVector, true);
556 peakWS->mutableRun().addProperty("BackgroundInnerRadius", BackgroundInnerRadiusVector, true);
557 peakWS->mutableRun().addProperty("BackgroundOuterRadius", BackgroundOuterRadiusVector, true);
558
559 // save profiles in peaks file
560 const std::string outfile = getProperty("ProfilesFile");
561 if (outfile.length() > 0) {
562 IAlgorithm_sptr alg;
563 try {
564 alg = createChildAlgorithm("SaveIsawPeaks", -1, -1, false);
565 } catch (Exception::NotFoundError &) {
566 g_log.error("Can't locate SaveIsawPeaks algorithm");
567 throw;
568 }
569 alg->setProperty("InputWorkspace", peakWS);
570 alg->setProperty("ProfileWorkspace", wsProfile2D);
571 alg->setPropertyValue("Filename", outfile);
572 alg->execute();
573 }
574 // Save the output
575 setProperty("OutputWorkspace", peakWS);
576}
577
583bool IntegratePeaksMD::detectorQ(const Mantid::Kernel::V3D &QLabFrame, double r) {
584 bool in = true;
585 const int nAngles = 8;
586 double dAngles = static_cast<coord_t>(nAngles);
587 // check 64 points in theta and phi at outer radius
588 for (int i = 0; i < nAngles; ++i) {
589 double theta = (2 * M_PI) / dAngles * i;
590 for (int j = 0; j < nAngles; ++j) {
591 double phi = (2 * M_PI) / dAngles * j;
592 // Calculate an edge position at this point on the sphere surface.
593 // Spherical coordinates to cartesian.
594 V3D edge = V3D(QLabFrame.X() + r * std::cos(theta) * std::sin(phi),
595 QLabFrame.Y() + r * std::sin(theta) * std::sin(phi), QLabFrame.Z() + r * std::cos(phi));
596 // Create the peak using the Q in the lab frame with all its info:
597 try {
598 Peak p(inst, edge);
599 in = (in && p.findDetector());
600 if (!in) {
601 return false;
602 }
603 } catch (...) {
604 return false;
605 }
606 }
607 }
608 return in;
609}
610
611//----------------------------------------------------------------------------------------------
615 inWS = getProperty("InputWorkspace");
616
618}
619
620double f_eval(double x, void *params) {
621 std::shared_ptr<const API::CompositeFunction> fun =
622 *reinterpret_cast<std::shared_ptr<const API::CompositeFunction> *>(params);
624 FunctionValues yval(domain);
625 fun->function(domain, yval);
626 return yval[0];
627}
628
629} // namespace Mantid::MDAlgorithms
#define DECLARE_ALGORITHM(classname)
Definition Algorithm.h:538
double error
#define CALL_MDEVENT_FUNCTION(funcname, workspace)
Macro that makes it possible to call a templated method for a MDEventWorkspace using a IMDEventWorksp...
void declareProperty(std::unique_ptr< Kernel::Property > p, const std::string &doc="") override
Add a property to the list of managed properties.
std::string getPropertyValue(const std::string &name) const override
Get the value of a property as a string.
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.
Kernel::Logger & g_log
Definition Algorithm.h:422
void deprecatedDate(const std::string &)
The date the algorithm was deprecated on.
void useAlgorithm(const std::string &, const int version=-1)
The algorithm to use instead of this one.
@ OptionalSave
to specify a file to write to but an empty string is
Implements FunctionDomain1D with its own storage in form of a std::vector.
A class to store values calculated by a function.
const std::vector< double > & toVector() const
Return the calculated values as a vector.
An interface to a peak function, which extend the interface of IFunctionWithLocation by adding method...
TableRow represents a row in a TableWorkspace.
Definition TableRow.h:39
A property class for workspaces.
Unique CoordCenterVectorParam type declaration for ndimensional coordinate centers.
void integrateSphere(Mantid::API::CoordTransform &radiusTransform, const coord_t radiusSquared, signal_t &signal, signal_t &errorSquared, const coord_t innerRadiusSquared=0.0, const bool useOnePercentBackgroundCorrection=true) const override=0
Sphere (peak) integration.
void integrateCylinder(Mantid::API::CoordTransform &radiusTransform, const coord_t radius, const coord_t length, signal_t &signal, signal_t &errorSquared, std::vector< signal_t > &signal_fit) const override=0
Cylinder (peak) integration.
std::shared_ptr< MDEventWorkspace< MDE, nd > > sptr
Typedef for a shared pointer of this kind of event workspace.
Kernel::SpecialCoordinateSystem getSpecialCoordinateSystem() const override
Get the coordinate system.
PeakShapeSpherical : PeakShape for a spherical peak.
Structure describing a single-crystal peak.
Definition Peak.h:34
bool findDetector()
After creating a peak using the Q in the lab frame, the detPos is set to the direction of the detecto...
Definition Peak.cpp:623
Structure describing a single-crystal peak.
Definition IPeak.h:26
virtual double getH() const =0
virtual void setIntensity(double m_Intensity)=0
virtual double getK() const =0
virtual int getRunNumber() const =0
virtual Mantid::Kernel::V3D getQSampleFrame() const =0
virtual double getL() const =0
virtual void setSigmaIntensity(double m_SigmaIntensity)=0
virtual Mantid::Kernel::V3D getQLabFrame() const =0
virtual Mantid::Kernel::V3D getHKL() const =0
PeakShape : Abstract type to describes the shape of a peak.
Definition PeakShape.h:20
Exception for when an item is not found in a collection.
Definition Exception.h:145
IPropertyManager * setProperty(const std::string &name, const T &value)
Templated method to set the value of a PropertyWithValue.
void error(const std::string &msg)
Logs at error level.
Definition Logger.cpp:108
void warning(const std::string &msg)
Logs at warning level.
Definition Logger.cpp:117
void information(const std::string &msg)
Logs at information level.
Definition Logger.cpp:136
The concrete, templated class for properties.
static T & Instance()
Return a reference to the Singleton instance, creating it if it does not already exist Creation is do...
Class for 3D vectors.
Definition V3D.h:34
constexpr double X() const noexcept
Get x.
Definition V3D.h:238
constexpr double Y() const noexcept
Get y.
Definition V3D.h:239
constexpr double Z() const noexcept
Get z.
Definition V3D.h:240
void integrate(typename DataObjects::MDEventWorkspace< MDE, nd >::sptr ws)
Integrate the peaks of the workspace using parameters saved in the algorithm class.
int version() const override
Algorithm's version for identification.
bool detectorQ(const Mantid::Kernel::V3D &QLabFrame, double r)
Calculate if this Q is on a detector.
void exec() override
Run the algorithm.
Mantid::API::IMDEventWorkspace_sptr inWS
Input MDEventWorkspace.
void init() override
Initialise the properties.
Geometry::Instrument_const_sptr inst
Instrument reference.
const std::string name() const override
Algorithm's name for identification.
std::shared_ptr< IAlgorithm > IAlgorithm_sptr
shared pointer to Mantid::API::IAlgorithm
std::shared_ptr< ITableWorkspace > ITableWorkspace_sptr
shared pointer to Mantid::API::ITableWorkspace
std::shared_ptr< Workspace > Workspace_sptr
shared pointer to Mantid::API::Workspace
std::shared_ptr< IFunction > IFunction_sptr
shared pointer to the function base class
Definition IFunction.h:743
std::shared_ptr< Workspace2D > Workspace2D_sptr
shared pointer to Mantid::DataObjects::Workspace2D
std::shared_ptr< PeaksWorkspace > PeaksWorkspace_sptr
Typedef for a shared pointer to a peaks workspace.
long round(double x)
Custom rounding method for a double->long because none is portable in C++ (!)
Definition Utils.h:37
SpecialCoordinateSystem
Special coordinate systems for Q3D.
double f_eval(double x, void *params)
float coord_t
Typedef for the data type to use for coordinate axes in MD objects such as MDBox, MDEventWorkspace,...
Definition MDTypes.h:27
double signal_t
Typedef for the signal recorded in a MDBox, etc.
Definition MDTypes.h:36
@ Input
An input workspace.
Definition Property.h:53
@ Output
An output workspace.
Definition Property.h:54