Mantid
Loading...
Searching...
No Matches
MDNormBase.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 +
7
15
16#include <tuple>
17
18namespace Mantid::MDAlgorithms {
19
20using namespace Mantid::DataObjects;
21using namespace Mantid::API;
22using namespace Mantid::Kernel;
23
24namespace {
26// function to compare two intersections (h,k,l,Momentum) by Momentum
27bool compareMomentum(const std::array<double, 4> &v1, const std::array<double, 4> &v2) { return (v1[3] < v2[3]); }
28// k=sqrt(energyToK * E)
29constexpr double energyToK = 8.0 * M_PI * M_PI * PhysicalConstants::NeutronMass * PhysicalConstants::meV * 1e-20 /
31const std::string LOG_CHARGE_NAME("proton_charge");
32} // namespace
33
35 : m_hmin(0.0f), m_hmax(0.0f), m_kmin(0.0f), m_kmax(0.0f), m_lmin(0.0f), m_lmax(0.0f), m_dEmin(0.f), m_dEmax(0.f),
36 m_Ei(0.), m_ki(0.), m_kfmin(0.), m_kfmax(0.), m_hIntegrated(true), m_kIntegrated(true), m_lIntegrated(true),
37 m_dEIntegrated(true), m_UB(3, 3, true), m_W(3, 3, true), m_hIdx(-1), m_kIdx(-1), m_lIdx(-1), m_eIdx(-1),
38 m_samplePos(), m_beamDir(), m_numExptInfos(0), m_diffraction(true), m_accumulate(false) {}
39
44std::string MDNormBase::inputEnergyMode() const {
45 const auto &history = m_inputWS->getHistory();
46 const size_t nalgs = history.size();
47 const auto &lastAlgorithm = history.getAlgorithmHistory(nalgs - 1);
48
49 std::string emode;
50 if (lastAlgorithm->name() == "ConvertToMD") {
51 // get dEAnalysisMode
52 emode = lastAlgorithm->getPropertyValue("dEAnalysisMode");
53 } else {
54 if ((lastAlgorithm->name() == "Load" || lastAlgorithm->name() == "LoadMD") && nalgs > 1) {
55 const auto &penultimateAlgorithm = history.getAlgorithmHistory(nalgs - 2);
56 if (penultimateAlgorithm->name() == "ConvertToMD") {
57 return penultimateAlgorithm->getPropertyValue("dEAnalysisMode");
58 }
59 }
60 throw std::invalid_argument("The last algorithm in the history of the "
61 "input workspace is not ConvertToMD");
62 }
63 return emode;
64}
65
72 const auto &props = getProperties();
73 auto binMD = createChildAlgorithm("BinMD", 0.0, 0.3);
74 binMD->setPropertyValue("AxisAligned", "1");
75 for (auto prop : props) {
76 const auto &propName = prop->name();
77 if (propName != "FluxWorkspace" && propName != "SolidAngleWorkspace" &&
78 propName != "TemporaryNormalizationWorkspace" && propName != "OutputNormalizationWorkspace" &&
79 propName != "SkipSafetyCheck") {
80 binMD->setPropertyValue(propName, prop->value());
81 }
82 }
83 binMD->executeAsChildAlg();
84 Workspace_sptr outputWS = binMD->getProperty("OutputWorkspace");
85 return std::dynamic_pointer_cast<MDHistoWorkspace>(outputWS);
86}
87
93 // Copy the MDHisto workspace, and change signals and errors to 0.
94 std::shared_ptr<IMDHistoWorkspace> tmp = this->getProperty("TemporaryNormalizationWorkspace");
95 m_normWS = std::dynamic_pointer_cast<MDHistoWorkspace>(tmp);
96 if (!m_normWS) {
97 m_normWS = dataWS.clone();
98 m_normWS->setTo(0., 0., 0.);
99 } else {
100 // Temp is given. Accumulation mode is on
101 m_accumulate = true;
102 }
103}
104
113std::vector<coord_t> MDNormBase::getValuesFromOtherDimensions(bool &skipNormalization, uint16_t expInfoIndex) const {
114 const auto &currentRun = m_inputWS->getExperimentInfo(expInfoIndex)->run();
115
116 std::vector<coord_t> otherDimValues;
117 size_t lastCol = m_diffraction ? 3 : 4;
118 for (size_t i = lastCol; i < m_inputWS->getNumDims(); i++) {
119 const auto dimension = m_inputWS->getDimension(i);
120 auto dimMin = static_cast<float>(dimension->getMinimum());
121 auto dimMax = static_cast<float>(dimension->getMaximum());
122 auto *dimProp = dynamic_cast<Kernel::TimeSeriesProperty<double> *>(currentRun.getProperty(dimension->getName()));
123 if (dimProp) {
124 auto value = static_cast<coord_t>(dimProp->firstValue());
125 otherDimValues.emplace_back(value);
126 // in the original MD data no time was spent measuring between dimMin and
127 // dimMax
128 if (value < dimMin || value > dimMax) {
129 skipNormalization = true;
130 }
131 }
132 }
133 return otherDimValues;
134}
135
144void MDNormBase::findIntegratedDimensions(const std::vector<coord_t> &otherDimValues, bool &skipNormalization) {
145 // Get indices of the original dimensions in the output workspace,
146 // and if not found, the corresponding dimension is integrated
147 m_transformation = m_normWS->getTransformFromOriginal(0)->makeAffineMatrix();
148
149 const size_t nrm1 = m_transformation.numRows() - 1;
150 const size_t ncm1 = m_transformation.numCols() - 1;
151 const size_t lastCol = m_diffraction ? 3 : 4;
152 for (size_t row = 0; row < nrm1; row++) // affine matrix, ignore last row
153 {
154 const auto dimen = m_normWS->getDimension(row);
155 const auto dimMin(dimen->getMinimum()), dimMax(dimen->getMaximum());
156 if (m_transformation[row][0] == 1.) {
157 m_hIntegrated = false;
158 m_hIdx = row;
159 m_hmin = std::max(m_hmin, dimMin);
160 m_hmax = std::min(m_hmax, dimMax);
161 if (m_hmin > dimMax || m_hmax < dimMin) {
162 skipNormalization = true;
163 }
164 }
165 if (m_transformation[row][1] == 1.) {
166 m_kIntegrated = false;
167 m_kIdx = row;
168 m_kmin = std::max(m_kmin, dimMin);
169 m_kmax = std::min(m_kmax, dimMax);
170 if (m_kmin > dimMax || m_kmax < dimMin) {
171 skipNormalization = true;
172 }
173 }
174 if (m_transformation[row][2] == 1.) {
175 m_lIntegrated = false;
176 m_lIdx = row;
177 m_lmin = std::max(m_lmin, dimMin);
178 m_lmax = std::min(m_lmax, dimMax);
179 if (m_lmin > dimMax || m_lmax < dimMin) {
180 skipNormalization = true;
181 }
182 }
183
184 if (!m_diffraction && m_transformation[row][3] == 1.) {
185 m_dEIntegrated = false;
186 m_eIdx = row;
187 m_dEmin = std::max(m_dEmin, dimMin);
188 m_dEmax = std::min(m_dEmax, dimMax);
189 if (m_dEmin > dimMax || m_dEmax < dimMin) {
190 skipNormalization = true;
191 }
192 }
193 for (size_t col = lastCol; col < ncm1; col++) // affine matrix, ignore last column
194 {
195 if (m_transformation[row][col] == 1.) {
196 double val = otherDimValues.at(col - lastCol);
197 if (val > dimMax || val < dimMin) {
198 skipNormalization = true;
199 }
200 }
201 }
202 }
203}
204
210 if (!m_hIntegrated) {
211 auto &hDim = *m_normWS->getDimension(m_hIdx);
212 m_hX = std::vector<double>(hDim.getNBoundaries());
213 for (size_t i = 0; i < m_hX.size(); ++i) {
214 m_hX[i] = hDim.getX(i);
215 }
216 }
217 if (!m_kIntegrated) {
218 auto &kDim = *m_normWS->getDimension(m_kIdx);
219 m_kX = std::vector<double>(kDim.getNBoundaries());
220 for (size_t i = 0; i < m_kX.size(); ++i) {
221 m_kX[i] = kDim.getX(i);
222 }
223 }
224 if (!m_lIntegrated) {
225 auto &lDim = *m_normWS->getDimension(m_lIdx);
226 m_lX = std::vector<double>(lDim.getNBoundaries());
227 for (size_t i = 0; i < m_lX.size(); ++i) {
228 m_lX[i] = lDim.getX(i);
229 }
230 }
231 if ((!m_diffraction) && (!m_dEIntegrated)) {
232 // NOTE: store k final instead
233 auto &eDim = *m_normWS->getDimension(m_eIdx);
234 m_eX = std::vector<double>(eDim.getNBoundaries());
235 for (size_t i = 0; i < m_eX.size(); ++i) {
236 double temp = m_Ei - eDim.getX(i);
237 temp = std::max(temp, 0.);
238 m_eX[i] = std::sqrt(energyToK * temp);
239 }
240 }
241}
242
251 bool doInvert) {
252 // Make it to a method!
253 DblMatrix soMatrix(3, 3);
254 auto v = so.transformHKL(V3D(1, 0, 0));
255 soMatrix.setColumn(0, v);
256 v = so.transformHKL(V3D(0, 1, 0));
257 soMatrix.setColumn(1, v);
258 v = so.transformHKL(V3D(0, 0, 1));
259 soMatrix.setColumn(2, v);
260 soMatrix.Invert();
261 DblMatrix Qtransform = R * m_UB * soMatrix * m_W;
262 if (doInvert)
263 Qtransform.Invert();
264
265 return Qtransform;
266}
267
274void MDNormBase::calculateNormalization(const std::vector<coord_t> &otherValues, uint16_t expInfoIndex) {
275 const auto &currentExptInfo = *(m_inputWS->getExperimentInfo(expInfoIndex));
276 const auto &spectrumInfo = currentExptInfo.spectrumInfo();
277 Kernel::DblMatrix Qtransform(getLogValues<VectorDoubleProperty>(
278 currentExptInfo, "RUBW_MATRIX")); // includes the 2*pi factor but not goniometer for now :)
279 Qtransform = currentExptInfo.run().getGoniometerMatrix() * Qtransform;
280 Qtransform.Invert();
281 const double protonCharge = currentExptInfo.run().getProtonCharge();
282
283 calculateNormInner(spectrumInfo, otherValues, protonCharge, 0., Qtransform);
284}
285
293void MDNormBase::calculateNormalization(const std::vector<coord_t> &otherValues, const Geometry::SymmetryOperation &so,
294 uint16_t expInfoIndex) {
295 const auto &currentExptInfo = *(m_inputWS->getExperimentInfo(expInfoIndex));
296 std::vector<double> lowValues = getLogValues<VectorDoubleProperty>(currentExptInfo, "MDNorm_low");
297 std::vector<double> highValues = getLogValues<VectorDoubleProperty>(currentExptInfo, "MDNorm_high");
298 // calculate Q transformation matrix (R * UB * SymmetryOperation * m_W)^-1
299 // in order to calculate intersections
300 Kernel::DblMatrix Qtransform = calQTransform(currentExptInfo.run().getGoniometerMatrix(), so);
301
302 // get proton charges
303 const double protonCharge = currentExptInfo.run().getProtonCharge();
304 const double protonChargeBkgd =
305 (m_backgroundWS != nullptr) ? m_backgroundWS->getExperimentInfo(0)->run().getProtonCharge() : 0;
306
307 const auto &spectrumInfo = currentExptInfo.spectrumInfo();
308
309 calculateNormInner(spectrumInfo, otherValues, protonCharge, protonChargeBkgd, Qtransform, lowValues, highValues);
310}
311
318void MDNormBase::calculateNormContinuous(const std::vector<coord_t> &otherValues, uint16_t expInfoIndex,
319 const Geometry::SymmetryOperation *so) {
320 const auto &currentExptInfo = *(m_inputWS->getExperimentInfo(expInfoIndex));
321 const auto &spectrumInfo = currentExptInfo.spectrumInfo();
322 bool isMDNorm = false;
323 std::vector<double> lowValues, highValues;
324 DblMatrix UBWSymm; // Symmetrised matrix (UB * symm * W) converting from lab to crystal frame
325 if (so == nullptr) {
326 UBWSymm = getLogValues<VectorDoubleProperty>(
327 currentExptInfo, "RUBW_MATRIX"); // includes the 2*pi factor but not goniometer for now :)
328 } else {
329 if (m_backgroundWS != nullptr) {
330 throw std::runtime_error("Continuous rotation and background workspace not yet implemented.");
331 }
332 isMDNorm = true;
333 UBWSymm = calQTransform(DblMatrix(3, 3, true), *so, false);
334 lowValues = getLogValues<VectorDoubleProperty>(currentExptInfo, "MDNorm_low");
335 highValues = getLogValues<VectorDoubleProperty>(currentExptInfo, "MDNorm_high");
336 if ((lowValues.size() != spectrumInfo.size()) || (highValues.size() != spectrumInfo.size())) {
337 g_log.warning() << "Incompatible detector and spectrum size. Using original data limits.\n";
338 const auto &binBoundaries = currentExptInfo.run().getBinBoundaries();
339 const double runEmin = binBoundaries.front();
340 const double runEmax = binBoundaries.back();
341 lowValues.assign(spectrumInfo.size(), runEmin);
342 highValues.assign(spectrumInfo.size(), runEmax);
343 }
344 }
345 // MDEventWS was created with the "useLogTimes" option: should be only a single expInfo, but
346 // gonios vary with time - we now coarse-bin it to compute the normalisation.
347 const Run &run = currentExptInfo.run();
348 if (!run.hasProperty(LOG_CHARGE_NAME)) {
349 throw std::runtime_error("Wokspace does not contain the proton charge log. Cannot continue.");
350 }
351
352 double progressStart = 0.3 + 0.7 * expInfoIndex / m_numExptInfos;
353 double progressEnd = 0.3 + 0.7 * (expInfoIndex + 1) / m_numExptInfos;
354 double normfac = 1.0;
355 if (auto *factor = run.hasProperty("NormalizationFactor")
356 ? dynamic_cast<Kernel::PropertyWithValue<double> *>(run.getProperty("NormalizationFactor"))
357 : nullptr) {
358 normfac = (*factor)();
359 }
360 std::istringstream tosplit;
361 if (auto *logTimesStr = dynamic_cast<PropertyWithValue<std::string> *>(run.getProperty("useLogTimes"));
362 run.hasProperty("useLogTimes") && logTimesStr) {
363 tosplit.str((*logTimesStr)());
364 } else {
365 throw std::runtime_error("useLogTimes property needs to be defined for Continuous Normalization");
366 }
367 std::vector<TimeSeriesProperty<double> *> logs;
368 std::vector<size_t> movingGonioIndex;
369 const TimeSeriesProperty<double> *protonlog = run.getTimeSeriesProperty<double>(LOG_CHARGE_NAME);
370 std::vector<double> protonCharge = protonlog->valuesAsVector();
371 std::vector<Types::Core::DateAndTime> protonTimes = protonlog->timesAsVector();
373
374 for (std::string name; std::getline(tosplit, name, ',');) {
375 auto *log = run.getTimeSeriesProperty<double>(name);
376 logs.push_back(log);
377 if ((log->maxValue() - log->minValue()) > STATIONARYANGLIM) { // Assume gonio logs in degrees
378 movingGonioIndex.push_back(logs.size() - 1);
379 }
380 }
381
382 // Convert from picoCoulomb to uA.hr for SNS data
383 if (protonlog->units().find("picoCoulomb") != std::string::npos) {
384 normfac *= 3600.e6;
385 }
386
387 if (movingGonioIndex.size() == 1) {
388 // If we only have a single moving gonio, bin all its values to GONIOBINSTEP degree bins and
389 // run inner loop on each binned angle
390 const TimeROI &timeroi = run.getTimeROI();
391 const auto &gonioAxLog = logs[movingGonioIndex[0]];
392 std::vector<double> filteredVals = gonioAxLog->filteredValuesAsVector(&timeroi);
393 const auto &[min, max] = std::minmax_element(filteredVals.begin(), filteredVals.end());
394 std::vector<double> gonioCharge(static_cast<int>((*max - *min) / GONIOBINSTEP) + 1, 0.0);
395 for (size_t n = 0; n < protonCharge.size(); n++) {
396 double logval = gonioAxLog->getSingleValue(protonTimes[n]);
397 if (std::isnan(logval) || logval > *max || logval < *min) {
398 continue;
399 }
400 auto idx = static_cast<size_t>(floor((logval - *min) / GONIOBINSTEP));
401 gonioCharge[idx] += protonCharge[n];
402 }
403 m_progress->resetNumSteps(static_cast<int64_t>(gonioCharge.size()), progressStart, progressEnd);
404 for (size_t n = 0; n < gonioCharge.size(); n++) {
405 if (gonioCharge[n] < MINPROTONCHARGE) {
406 continue;
407 }
408 auto nn = static_cast<double>(n);
409 gonio.setRotationAngle(movingGonioIndex[0], nn * GONIOBINSTEP + *min);
410 DblMatrix Qtransform = gonio.getR() * UBWSymm;
411 Qtransform.Invert();
412 if (isMDNorm) {
413 calculateNormInner(spectrumInfo, otherValues, gonioCharge[n] / normfac, 0., Qtransform, lowValues, highValues);
414 } else {
415 calculateNormInner(spectrumInfo, otherValues, gonioCharge[n] / normfac, 0., Qtransform);
416 }
417 m_progress->report();
418 }
419 } else {
420 // Otherwise run inner loop over small bins of proton charge in time
421 double chargeSum = 0.0;
422 size_t i0 = 0;
423 bool skipIter = false;
424 m_progress->resetNumSteps(static_cast<int64_t>(protonCharge.size()), progressStart, progressEnd);
425 for (size_t n = 0; n < protonCharge.size(); n++) {
426 chargeSum += protonCharge[n];
427 if (chargeSum > CHARGEBINSIZE) {
428 size_t index = static_cast<int>(floor(static_cast<double>(n - i0) / 2.)) + i0;
429 skipIter = false;
430 for (size_t gAx = 0; gAx < gonio.getNumberAxes(); gAx++) {
431 double logval = logs[gAx]->getSingleValue(protonTimes[index]);
432 if (std::isnan(logval)) {
433 skipIter = true;
434 continue;
435 }
436 gonio.setRotationAngle(gAx, logval);
437 }
438 if (!skipIter) {
439 DblMatrix Qtrans = gonio.getR() * UBWSymm;
440 Qtrans.Invert();
441 if (isMDNorm) {
442 calculateNormInner(spectrumInfo, otherValues, chargeSum / normfac, 0., Qtrans, lowValues, highValues);
443 } else {
444 calculateNormInner(spectrumInfo, otherValues, chargeSum / normfac, 0., Qtrans);
445 }
446 }
447 chargeSum = 0;
448 i0 = n;
449 }
450 m_progress->report();
451 }
452 }
453 if (m_numExptInfos > 1) {
454 m_progress->resetNumSteps(m_numExptInfos - expInfoIndex, progressStart, 1.0);
455 }
456}
457
458void MDNormBase::calculateNormInner(const API::SpectrumInfo &spectrumInfo, const std::vector<coord_t> &otherValues,
459 const double protonCharge, const double protonChargeBkgd,
460 const Kernel::DblMatrix &Qtransform, const std::vector<double> &lowValues,
461 const std::vector<double> &highValues) {
462 // Mapping
463 const auto ndets = static_cast<int64_t>(spectrumInfo.size());
464 bool haveSA = false;
465 API::MatrixWorkspace_const_sptr integrFlux, solidAngleWS = getProperty("SolidAngleWorkspace");
466 detid2index_map fluxDetToIdx, solidAngDetToIdx;
467 bool thread_safe = true;
468 if (m_diffraction) {
469 integrFlux = getProperty("FluxWorkspace"); // FluxWorkspace is mandatory for diffraction
470 std::tie(m_kfmin, m_kfmax) = integrFlux->getXMinMax();
471 fluxDetToIdx = integrFlux->getDetectorIDToWorkspaceIndexMap();
472 thread_safe = Kernel::threadSafe(*integrFlux);
473 }
474 if (solidAngleWS != nullptr) {
475 haveSA = true;
476 solidAngDetToIdx = solidAngleWS->getDetectorIDToWorkspaceIndexMap();
477 }
478
479 const size_t vmdDims = m_diffraction ? 3 : 4;
480 size_t numNPoints = (m_backgroundWS) ? m_bkgdNormWS->getNPoints() : 0;
481 if (m_backgroundWS && numNPoints != m_normWS->getNPoints()) {
482 throw std::runtime_error("N points are different");
483 }
484
485 bool isMDNorm = !lowValues.empty();
486 if (isMDNorm) {
487 m_hmin = static_cast<coord_t>(m_hX[0]);
488 m_kmin = static_cast<coord_t>(m_kX[0]);
489 m_lmin = static_cast<coord_t>(m_lX[0]);
490 m_hmax = static_cast<coord_t>(m_hX.back());
491 m_kmax = static_cast<coord_t>(m_kX.back());
492 m_lmax = static_cast<coord_t>(m_lX.back());
493 }
494
495 PRAGMA_OMP(parallel for if(thread_safe))
496 for (int64_t i = 0; i < ndets; i++) {
498
499 if (!spectrumInfo.hasDetectors(i) || spectrumInfo.isMonitor(i) || spectrumInfo.isMasked(i)) {
500 continue;
501 }
502 double theta = spectrumInfo.twoTheta(i, m_samplePos, m_beamDir);
503 double phi = spectrumInfo.phi(i);
504 // If the detector is a group, this should be the ID of the first detector
505 const auto detID = spectrumInfo.firstDetectorID(i);
506
507 // Get the flux spectrum spectrum number for diffraction
508 size_t wsIdx = 0;
509 if (m_diffraction) {
510 if (auto index = fluxDetToIdx.find(detID); index != fluxDetToIdx.end()) {
511 wsIdx = index->second;
512 } else { // masked detector in flux, but not in input workspace
513 continue;
514 }
515 }
516
517 // Intersections
518 std::vector<std::array<double, 4>> intersections;
519 std::vector<coord_t> pos, posNew;
520 if (isMDNorm) {
521 this->calculateIntersections(intersections, theta, phi, Qtransform, lowValues[i], highValues[i]);
522 } else {
523 this->calculateIntersections(intersections, theta, phi, Qtransform);
524 }
525 // No need to do normalization calculation if there is no intersection
526 if (intersections.empty())
527 continue;
528
529 // Get solid angle for this contribution
530 double solid = protonCharge;
531 double bkgdSolid = protonChargeBkgd;
532 if (haveSA) {
533 double solid_angle_factor = solidAngleWS->y(solidAngDetToIdx.at(detID))[0];
534 solid = solid_angle_factor * protonCharge;
535 bkgdSolid = solid_angle_factor * protonChargeBkgd;
536 }
537
538 // -- calculate integrals for the intersection --
539 // momentum values at intersections
540 std::vector<double> yValues;
541 if (m_diffraction) {
542 // copy momenta to xValues
543 std::vector<double> xValues(intersections.size());
544 yValues.resize(intersections.size());
545 auto x = xValues.begin();
546 for (auto it = intersections.begin(); it != intersections.end(); ++it, ++x) {
547 *x = (*it)[3];
548 }
549 // calculate integrals at momenta from xValues by interpolating between
550 // points in spectrum sp
551 // of workspace integrFlux. The result is stored in yValues
552 calcIntegralsForIntersections(xValues, *integrFlux, wsIdx, yValues);
553 }
554
555 // Compute final position in HKL
556 // pre-allocate for efficiency and copy non-hkl dim values into place
557 pos.resize(vmdDims + otherValues.size() + 1);
558 std::copy(otherValues.begin(), otherValues.end(), pos.begin() + vmdDims);
559 pos.emplace_back(1.f);
560 for (auto it = intersections.begin() + 1; it != intersections.end(); ++it) {
561 const auto &curIntSec = *it;
562 const auto &prevIntSec = *(it - 1);
563 // the full vector isn't used so compute only what is necessary
564 double delta = m_diffraction ? (curIntSec[3] - prevIntSec[3]) / 1000. // tolerance is 1e-7
565 : (curIntSec[3] * curIntSec[3] - prevIntSec[3] * prevIntSec[3]) / energyToK;
566 if (delta < 1e-10)
567 continue; // Assume zero contribution if difference is small
568
569 // Average between two intersections for final position
570 std::transform(curIntSec.data(), curIntSec.data() + vmdDims, prevIntSec.data(), pos.begin(),
571 [](const double rhs, const double lhs) { return static_cast<coord_t>(0.5 * (rhs + lhs)); });
572
573 // transform kf to energy transfer
574 if (!m_diffraction) {
575 pos[3] = static_cast<coord_t>(m_Ei - pos[3] * pos[3] / energyToK);
576 }
577 m_transformation.multiplyPoint(pos, posNew);
578 size_t linIndex = m_normWS->getLinearIndexAtCoord(posNew.data());
579 if (linIndex == static_cast<size_t>(-1))
580 continue;
581
582 if (m_diffraction) {
583 // index of the current intersection
584 auto k = static_cast<size_t>(std::distance(intersections.begin(), it));
585 delta = (yValues[k] - yValues[k - 1]);
586 }
587 // signal = delta * solid = integral between two consecutive intersections * solid angle
588 Mantid::Kernel::AtomicOp(m_signalArray[linIndex], delta * solid, std::plus<signal_t>());
589 if (m_backgroundWS) {
590 Mantid::Kernel::AtomicOp(m_bkgdSignalArray[linIndex], delta * bkgdSolid, std::plus<signal_t>());
591 }
592 }
594 }
596}
597
606void MDNormBase::calcIntegralsForIntersections(const std::vector<double> &xValues,
607 const API::MatrixWorkspace &integrFlux, size_t sp,
608 std::vector<double> &yValues) const {
609 assert(xValues.size() == yValues.size());
610
611 // the x-data from the workspace
612 const auto &xData = integrFlux.x(sp);
613 const double xStart = xData.front();
614 const double xEnd = xData.back();
615
616 // the values in integrFlux are expected to be integrals of a non-negative
617 // function
618 // ie they must make a non-decreasing function
619 const auto &yData = integrFlux.y(sp);
620 size_t spSize = yData.size();
621
622 const double yMin = 0.0;
623 const double yMax = yData.back();
624
625 size_t nData = xValues.size();
626 // all integrals below xStart must be 0
627 if (xValues[nData - 1] < xStart) {
628 std::fill(yValues.begin(), yValues.end(), yMin);
629 return;
630 }
631
632 // all integrals above xEnd must be equal tp yMax
633 if (xValues[0] > xEnd) {
634 std::fill(yValues.begin(), yValues.end(), yMax);
635 return;
636 }
637
638 size_t i = 0;
639 // integrals below xStart must be 0
640 while (i < nData - 1 && xValues[i] < xStart) {
641 yValues[i] = yMin;
642 i++;
643 }
644 size_t j = 0;
645 for (; i < nData; i++) {
646 // integrals above xEnd must be equal tp yMax
647 if (j >= spSize - 1) {
648 yValues[i] = yMax;
649 } else {
650 double xi = xValues[i];
651 while (j < spSize - 1 && xi > xData[j])
652 j++;
653 // if x falls onto an interpolation point return the corresponding y
654 if (xi == xData[j]) {
655 yValues[i] = yData[j];
656 } else if (j == spSize - 1) {
657 // if we get above xEnd it's yMax
658 yValues[i] = yMax;
659 } else if (j > 0) {
660 // interpolate between the consecutive points
661 double x0 = xData[j - 1];
662 double x1 = xData[j];
663 double y0 = yData[j - 1];
664 double y1 = yData[j];
665 yValues[i] = y0 + (y1 - y0) * (xi - x0) / (x1 - x0);
666 } else // j == 0
667 {
668 yValues[i] = yMin;
669 }
670 }
671 }
672}
673
685void MDNormBase::calculateIntersections(std::vector<std::array<double, 4>> &intersections, const double theta,
686 const double phi, const Kernel::DblMatrix &transform, double lowvalue,
687 double highvalue) {
688 V3D qin, qout;
689 double kfmin, kfmax, kimin, kimax;
690 bool isMDNorm = !std::isnan(lowvalue);
691 if (isMDNorm) {
692 qout = V3D(sin(theta) * cos(phi), sin(theta) * sin(phi), cos(theta));
693 qin = V3D(0., 0., 1.);
694 if (m_diffraction) {
695 kimin = kfmin = lowvalue;
696 kimax = kfmax = highvalue;
697 } else {
698 kimin = kimax = std::sqrt(energyToK * m_Ei);
699 kfmin = std::sqrt(energyToK * (m_Ei - highvalue));
700 kfmax = std::sqrt(energyToK * (m_Ei - lowvalue));
701 }
702 } else {
703 if (m_diffraction) {
704 qin = qout = V3D(-sin(theta) * cos(phi), -sin(theta) * sin(phi), 1. - cos(theta));
705 kimin = kfmin = m_kfmin;
706 kimax = kfmax = m_kfmax;
707 } else {
708 qout = V3D(sin(theta) * cos(phi), sin(theta) * sin(phi), cos(theta));
709 qin = V3D(0., 0., 1.);
710 kimin = kimax = m_ki;
711 kfmin = m_kfmin;
712 kfmax = m_kfmax;
713 }
714 }
715
716 qout = transform * qout;
717 qin = transform * qin;
718 if (m_convention == "Crystallography") {
719 qout *= -1;
720 qin *= -1;
721 }
722 double hStart = qin.X() * kimin - qout.X() * kfmin, hEnd = qin.X() * kimax - qout.X() * kfmax;
723 double kStart = qin.Y() * kimin - qout.Y() * kfmin, kEnd = qin.Y() * kimax - qout.Y() * kfmax;
724 double lStart = qin.Z() * kimin - qout.Z() * kfmin, lEnd = qin.Z() * kimax - qout.Z() * kfmax;
725 double eps = 1e-10;
726 auto hNBins = m_hX.size();
727 auto kNBins = m_kX.size();
728 auto lNBins = m_lX.size();
729 auto eNBins = m_eX.size();
730 intersections.clear();
731 intersections.reserve(hNBins + kNBins + lNBins + eNBins + 8); // 8 is 3*(min,max for each Q component)+kfmin+kfmax
732
733 // calculate intersections with planes perpendicular to h
734 if (fabs(hStart - hEnd) > eps) {
735 double fmom = (kfmax - kfmin) / (hEnd - hStart);
736 double fk = (kEnd - kStart) / (hEnd - hStart);
737 double fl = (lEnd - lStart) / (hEnd - hStart);
738 if (!m_hIntegrated) {
739 for (size_t i = 0; i < hNBins; i++) {
740 double hi = m_hX[i];
741 if ((hi >= m_hmin) && (hi <= m_hmax) && ((hStart - hi) * (hEnd - hi) < 0)) {
742 // if hi is between hStart and hEnd, then ki and li will be between
743 // kStart, kEnd and lStart, lEnd and momi will be between kfmin and kfmax
744 double ki = fk * (hi - hStart) + kStart;
745 double li = fl * (hi - hStart) + lStart;
746 if ((ki >= m_kmin) && (ki <= m_kmax) && (li >= m_lmin) && (li <= m_lmax)) {
747 double momi = fmom * (hi - hStart) + kfmin;
748 intersections.push_back({{hi, ki, li, momi}});
749 }
750 }
751 }
752 }
753 if (!isMDNorm) {
754 double momhMin = fmom * (m_hmin - hStart) + kfmin;
755 if ((momhMin - kfmin) * (momhMin - kfmax) < 0) // kfmin>kfmax
756 {
757 // khmin and lhmin
758 double khmin = fk * (m_hmin - hStart) + kStart;
759 double lhmin = fl * (m_hmin - hStart) + lStart;
760 if ((khmin >= m_kmin) && (khmin <= m_kmax) && (lhmin >= m_lmin) && (lhmin <= m_lmax)) {
761 intersections.push_back({{m_hmin, khmin, lhmin, momhMin}});
762 }
763 }
764 double momhMax = fmom * (m_hmax - hStart) + kfmin;
765 if ((momhMax - kfmin) * (momhMax - kfmax) <= 0) {
766 // khmax and lhmax
767 double khmax = fk * (m_hmax - hStart) + kStart;
768 double lhmax = fl * (m_hmax - hStart) + lStart;
769 if ((khmax >= m_kmin) && (khmax <= m_kmax) && (lhmax >= m_lmin) && (lhmax <= m_lmax)) {
770 intersections.push_back({{m_hmax, khmax, lhmax, momhMax}});
771 }
772 }
773 }
774 }
775
776 // calculate intersections with planes perpendicular to k
777 if (fabs(kStart - kEnd) > eps) {
778 double fmom = (kfmax - kfmin) / (kEnd - kStart);
779 double fh = (hEnd - hStart) / (kEnd - kStart);
780 double fl = (lEnd - lStart) / (kEnd - kStart);
781 if (!m_kIntegrated) {
782 for (size_t i = 0; i < kNBins; i++) {
783 double ki = m_kX[i];
784 if ((ki >= m_kmin) && (ki <= m_kmax) && ((kStart - ki) * (kEnd - ki) < 0)) {
785 // if ki is between kStart and kEnd, then hi and li will be between
786 // hStart, hEnd and lStart, lEnd and momi will be between kfmin and kfmax
787 double hi = fh * (ki - kStart) + hStart;
788 double li = fl * (ki - kStart) + lStart;
789 if ((hi >= m_hmin) && (hi <= m_hmax) && (li >= m_lmin) && (li <= m_lmax)) {
790 double momi = fmom * (ki - kStart) + kfmin;
791 intersections.push_back({{hi, ki, li, momi}});
792 }
793 }
794 }
795 }
796 if (!isMDNorm) {
797 double momkMin = fmom * (m_kmin - kStart) + kfmin;
798 if ((momkMin - kfmin) * (momkMin - kfmax) < 0) {
799 // hkmin and lkmin
800 double hkmin = fh * (m_kmin - kStart) + hStart;
801 double lkmin = fl * (m_kmin - kStart) + lStart;
802 if ((hkmin >= m_hmin) && (hkmin <= m_hmax) && (lkmin >= m_lmin) && (lkmin <= m_lmax)) {
803 intersections.push_back({{hkmin, m_kmin, lkmin, momkMin}});
804 }
805 }
806 double momkMax = fmom * (m_kmax - kStart) + kfmin;
807 if ((momkMax - kfmin) * (momkMax - kfmax) <= 0) {
808 // hkmax and lkmax
809 double hkmax = fh * (m_kmax - kStart) + hStart;
810 double lkmax = fl * (m_kmax - kStart) + lStart;
811 if ((hkmax >= m_hmin) && (hkmax <= m_hmax) && (lkmax >= m_lmin) && (lkmax <= m_lmax)) {
812 intersections.push_back({{hkmax, m_kmax, lkmax, momkMax}});
813 }
814 }
815 }
816 }
817
818 // calculate intersections with planes perpendicular to l
819 if (fabs(lStart - lEnd) > eps) {
820 double fmom = (kfmax - kfmin) / (lEnd - lStart);
821 double fh = (hEnd - hStart) / (lEnd - lStart);
822 double fk = (kEnd - kStart) / (lEnd - lStart);
823 if (!m_lIntegrated) {
824 for (size_t i = 0; i < lNBins; i++) {
825 double li = m_lX[i];
826 if ((li >= m_lmin) && (li <= m_lmax) && ((lStart - li) * (lEnd - li) < 0)) {
827 // if li is between lStart and lEnd, then hi and ki will be between
828 // hStart, hEnd and kStart, kEnd
829 double hi = fh * (li - lStart) + hStart;
830 double ki = fk * (li - lStart) + kStart;
831 if ((hi >= m_hmin) && (hi <= m_hmax) && (ki >= m_kmin) && (ki <= m_kmax)) {
832 double momi = fmom * (li - lStart) + kfmin;
833 intersections.push_back({{hi, ki, li, momi}});
834 }
835 }
836 }
837 }
838 if (!isMDNorm) {
839 double momlMin = fmom * (m_lmin - lStart) + kfmin;
840 if ((momlMin - kfmin) * (momlMin - kfmax) <= 0) {
841 // hlmin and klmin
842 double hlmin = fh * (m_lmin - lStart) + hStart;
843 double klmin = fk * (m_lmin - lStart) + kStart;
844 if ((hlmin >= m_hmin) && (hlmin <= m_hmax) && (klmin >= m_kmin) && (klmin <= m_kmax)) {
845 intersections.push_back({{hlmin, klmin, m_lmin, momlMin}});
846 }
847 }
848 double momlMax = fmom * (m_lmax - lStart) + kfmin;
849 if ((momlMax - kfmin) * (momlMax - kfmax) < 0) {
850 // hlmax and klmax
851 double hlmax = fh * (m_lmax - lStart) + hStart;
852 double klmax = fk * (m_lmax - lStart) + kStart;
853 if ((hlmax >= m_hmin) && (hlmax <= m_hmax) && (klmax >= m_kmin) && (klmax <= m_kmax)) {
854 intersections.push_back({{hlmax, klmax, m_lmax, momlMax}});
855 }
856 }
857 }
858 }
859
860 // intersections with dE
861 if (!m_dEIntegrated) {
862 for (size_t i = 0; i < eNBins; i++) {
863 double kfi = m_eX[i];
864 if ((kfi - kfmin) * (kfi - kfmax) <= 0) {
865 double h = qin.X() * kimin - qout.X() * kfi;
866 double k = qin.Y() * kimin - qout.Y() * kfi;
867 double l = qin.Z() * kimin - qout.Z() * kfi;
868 if ((h >= m_hmin) && (h <= m_hmax) && (k >= m_kmin) && (k <= m_kmax) && (l >= m_lmin) && (l <= m_lmax)) {
869 intersections.push_back({{h, k, l, kfi}});
870 }
871 }
872 }
873 }
874
875 // endpoints
876 if ((hStart >= m_hmin) && (hStart <= m_hmax) && (kStart >= m_kmin) && (kStart <= m_kmax) && (lStart >= m_lmin) &&
877 (lStart <= m_lmax)) {
878 intersections.push_back({{hStart, kStart, lStart, kfmin}});
879 }
880 if ((hEnd >= m_hmin) && (hEnd <= m_hmax) && (kEnd >= m_kmin) && (kEnd <= m_kmax) && (lEnd >= m_lmin) &&
881 (lEnd <= m_lmax)) {
882 intersections.push_back({{hEnd, kEnd, lEnd, kfmax}});
883 }
884
885 // sort intersections by final momentum
886 std::stable_sort(intersections.begin(), intersections.end(), compareMomentum);
887}
888
889} // namespace Mantid::MDAlgorithms
gsl_vector * tmp
const std::vector< double > & rhs
double value
The value of the point.
Definition FitMW.cpp:51
std::map< DeltaEMode::Type, std::string > index
#define fabs(x)
Definition Matrix.cpp:22
#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_END_INTERRUPT_REGION
Ends a block to skip processing is the algorithm has been interupted Note the start of the block if n...
#define PRAGMA_OMP(expression)
#define PARALLEL_CHECK_INTERRUPT_REGION
Adds a check after a Parallel region to see if it was interupted.
std::vector< history_type > history
history information
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:426
const std::vector< Kernel::Property * > & getProperties() const override
Get the list of managed properties.
const std::string name() const override=0
function to return a name of the algorithm, must be overridden in all algorithms
bool hasProperty(const std::string &name) const
Does the property exist on the object.
const Kernel::TimeROI & getTimeROI() const
Kernel::Property * getProperty(const std::string &name) const
Returns the named property as a pointer.
Kernel::TimeSeriesProperty< T > * getTimeSeriesProperty(const std::string &name) const
Returns a property as a time series property.
Base MatrixWorkspace Abstract Class.
const HistogramData::HistogramX & x(const size_t index) const
const HistogramData::HistogramY & y(const size_t index) const
This class stores information regarding an experimental run as a series of log entries.
Definition Run.h:36
const Geometry::Goniometer & getGoniometer() const
Return reference to the first const Goniometer object for this run.
Definition Run.cpp:525
API::SpectrumInfo is an intermediate step towards a SpectrumInfo that is part of Instrument-2....
bool isMonitor(const size_t index) const
Returns true if the detector(s) associated with the spectrum are monitors.
bool hasDetectors(const size_t index) const
Returns true if the spectrum is associated with detectors in the instrument.
detid_t firstDetectorID(const size_t index) const
Returns the detector ID of the first detector of the spectrum with given index.
double twoTheta(const size_t index) const
Returns the scattering angle 2 theta in radians (angle w.r.t.
bool isMasked(const size_t index) const
Returns true if the detector(s) associated with the spectrum are masked.
double phi(const size_t index) const
Returns the angle of the spectrum position about the lab-frame z axis, atan2(y, x),...
size_t size() const
Returns the size of the SpectrumInfo, i.e., the number of spectra.
std::unique_ptr< MDHistoWorkspace > clone() const
Returns a clone of the workspace.
Class to represent a particular goniometer setting, which is described by the rotation matrix.
Definition Goniometer.h:55
void setRotationAngle(const std::string &name, double value)
Set rotation angle for an axis using motor name.
const Kernel::DblMatrix & getR() const
Return global rotation matrix.
Crystallographic symmetry operations are composed of a rotational component, which is represented by ...
Kernel::V3D transformHKL(const Kernel::V3D &hkl) const
Transforms an index triplet hkl.
void warning(const std::string &msg)
Logs at warning level.
Definition Logger.cpp:117
T Invert()
LU inversion routine.
Definition Matrix.cpp:924
void multiplyPoint(const std::vector< T > &in, std::vector< T > &out) const
Multiply M*Vec.
Definition Matrix.cpp:375
size_t numRows() const
Return the number of rows in the matrix.
Definition Matrix.h:143
void setColumn(const size_t nCol, const std::vector< T > &newCol)
Definition Matrix.cpp:675
size_t numCols() const
Return the number of columns in the matrix.
Definition Matrix.h:146
The concrete, templated class for properties.
virtual const std::string & units() const
Returns the units of the property, if any, as a string.
Definition Property.cpp:194
TimeROI : Object that holds information about when the time measurement was active.
Definition TimeROI.h:18
A specialised Property class for holding a series of time-value pairs.
std::vector< TYPE > valuesAsVector() const
Return the time series's values (unfiltered) as a vector<TYPE>
std::vector< Types::Core::DateAndTime > timesAsVector() const override
Return the time series's times as a vector<DateAndTime>
Class for 3D vectors.
Definition V3D.h:35
constexpr double X() const noexcept
Get x.
Definition V3D.h:239
constexpr double Y() const noexcept
Get y.
Definition V3D.h:240
constexpr double Z() const noexcept
Get z.
Definition V3D.h:241
std::vector< std::atomic< signal_t > > m_bkgdSignalArray
Definition MDNormBase.h:107
API::IMDEventWorkspace_sptr m_inputWS
Input workspace.
Definition MDNormBase.h:69
bool m_hIntegrated
flag for integrated h,k,l, dE dimensions
Definition MDNormBase.h:80
void createNormalizationWS(const DataObjects::MDHistoWorkspace &dataWS)
Create & cached the normalization workspace.
bool m_diffraction
Flag indicating if the input workspace is from diffraction.
Definition MDNormBase.h:100
API::IMDEventWorkspace_sptr m_backgroundWS
Input background workspace.
Definition MDNormBase.h:74
static constexpr double CHARGEBINSIZE
Definition MDNormBase.h:39
void calcIntegralsForIntersections(const std::vector< double > &xValues, const API::MatrixWorkspace &integrFlux, size_t sp, std::vector< double > &yValues) const
Linearly interpolate between the points in integrFlux at xValues and save the results in yValues.
void cacheDimensionXValues()
Stores the X values from each H,K,L,E dimension as member variables Energy dimension is transformed t...
Mantid::Kernel::DblMatrix m_W
W matrix.
Definition MDNormBase.h:84
void findIntegratedDimensions(const std::vector< coord_t > &otherDimValues, bool &skipNormalization)
Checks the normalization workspace against the indices of the original dimensions.
void calculateNormalization(const std::vector< coord_t > &otherValues, uint16_t expInfoIndex)
Computed the normalization for the input workspace (for MDNormSCD/MDNormDirectSC).
DataObjects::MDHistoWorkspace_sptr m_normWS
Normalization workspace.
Definition MDNormBase.h:71
Kernel::V3D m_samplePos
Sample position.
Definition MDNormBase.h:92
std::vector< coord_t > getValuesFromOtherDimensions(bool &skipNormalization, uint16_t expInfoIndex=0) const
Retrieve logged values from non-HKL dimensions.
void calculateNormInner(const API::SpectrumInfo &spectrumInfo, const std::vector< coord_t > &otherValues, const double protonCharge, const double protonChargeBkgd, const DblMatrix &Qtransform, const std::vector< double > &lowValues=std::vector< double >(), const std::vector< double > &highValues=std::vector< double >())
std::vector< std::atomic< signal_t > > m_signalArray
internal array to accumulate signals to avoid copying (serial) each loop
Definition MDNormBase.h:106
bool m_accumulate
Flag to accumulate normalization.
Definition MDNormBase.h:102
std::vector< double > m_eX
Definition MDNormBase.h:90
DataObjects::MDHistoWorkspace_sptr binInputWS()
Runs the BinMD algorithm on the input to provide the output workspace All slicing algorithm propertie...
void calculateIntersections(std::vector< std::array< double, 4 > > &intersections, const double theta, const double phi, const DblMatrix &transform, double lowvalue=std::nan(""), double highvalue=0)
Calculate the points of intersection for the given detector with cuboid surrounding the detector posi...
uint16_t m_numExptInfos
number of experiment infos
Definition MDNormBase.h:98
DataObjects::MDHistoWorkspace_sptr m_bkgdNormWS
Definition MDNormBase.h:72
Mantid::Kernel::Matrix< coord_t > m_transformation
matrix for transforming from intersections to positions in the normalization workspace
Definition MDNormBase.h:86
Kernel::V3D m_beamDir
Beam direction.
Definition MDNormBase.h:94
Mantid::Kernel::DblMatrix m_UB
UB matrix.
Definition MDNormBase.h:82
coord_t m_hmin
limits for h,k,l, dE dimensions
Definition MDNormBase.h:76
size_t m_hIdx
index of h,k,l, dE dimensions in the output workspaces
Definition MDNormBase.h:88
std::vector< double > m_kX
Definition MDNormBase.h:90
std::vector< double > m_hX
cached X values along dimensions h,k,l. dE
Definition MDNormBase.h:90
std::unique_ptr< API::Progress > m_progress
Progress bar.
Definition MDNormBase.h:104
Mantid::Kernel::DblMatrix calQTransform(const Kernel::DblMatrix &R, const Mantid::Geometry::SymmetryOperation &so, bool doInvert=true)
Calculate QTransform = (R * UB * SymmetryOperation * m_W)^-1.
void calculateNormContinuous(const std::vector< coord_t > &otherValues, uint16_t expInfoIndex, const Geometry::SymmetryOperation *so=nullptr)
Computes the normalization for the input workspace for the case of a continous rotation.
static constexpr double STATIONARYANGLIM
Definition MDNormBase.h:42
std::vector< double > m_lX
Definition MDNormBase.h:90
static constexpr double MINPROTONCHARGE
Definition MDNormBase.h:41
std::string inputEnergyMode() const
Currently looks for the ConvertToMD algorithm in the history.
static constexpr double GONIOBINSTEP
Definition MDNormBase.h:40
std::string m_convention
ki-kf for Inelastic convention; kf-ki for Crystallography convention
Definition MDNormBase.h:96
double m_Ei
cached values for incident energy and momentum, final momentum min/max
Definition MDNormBase.h:78
std::shared_ptr< Workspace > Workspace_sptr
shared pointer to Mantid::API::Workspace
std::shared_ptr< const MatrixWorkspace > MatrixWorkspace_const_sptr
shared pointer to the matrix workspace base class (const version)
std::shared_ptr< MDHistoWorkspace > MDHistoWorkspace_sptr
A shared pointer to a MDHistoWorkspace.
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.
void AtomicOp(std::atomic< T > &f, T d, BinaryOp op)
Uses std::compare_exchange_weak to update the atomic value f = op(f, d) Used to improve parallel scal...
Mantid::Kernel::Matrix< double > DblMatrix
Definition Matrix.h:205
Kernel::PropertyWithValue< std::vector< double > > VectorDoubleProperty
static constexpr double NeutronMass
Mass of the neutron in kg.
static constexpr double h
Planck constant in J*s.
static constexpr double meV
1 meV in Joules.
float coord_t
Typedef for the data type to use for coordinate axes in MD objects such as MDBox, MDEventWorkspace,...
Definition MDTypes.h:27
std::unordered_map< detid_t, size_t > detid2index_map
Map with key = detector ID, value = workspace index.