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
16namespace Mantid::MDAlgorithms {
17
18using namespace Mantid::DataObjects;
19using namespace Mantid::API;
20using namespace Mantid::Kernel;
21
22namespace {
24// function to compare two intersections (h,k,l,Momentum) by Momentum
25bool compareMomentum(const std::array<double, 4> &v1, const std::array<double, 4> &v2) { return (v1[3] < v2[3]); }
26// k=sqrt(energyToK * E)
27constexpr double energyToK = 8.0 * M_PI * M_PI * PhysicalConstants::NeutronMass * PhysicalConstants::meV * 1e-20 /
29const std::string LOG_CHARGE_NAME("proton_charge");
30} // namespace
31
33 : 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),
34 m_Ei(0.), m_ki(0.), m_kfmin(0.), m_kfmax(0.), m_hIntegrated(true), m_kIntegrated(true), m_lIntegrated(true),
35 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),
36 m_samplePos(), m_beamDir(), m_numExptInfos(0), m_diffraction(true), m_accumulate(false) {}
37
42std::string MDNormBase::inputEnergyMode() const {
43 const auto &history = m_inputWS->getHistory();
44 const size_t nalgs = history.size();
45 const auto &lastAlgorithm = history.getAlgorithmHistory(nalgs - 1);
46
47 std::string emode;
48 if (lastAlgorithm->name() == "ConvertToMD") {
49 // get dEAnalysisMode
50 emode = lastAlgorithm->getPropertyValue("dEAnalysisMode");
51 } else {
52 if ((lastAlgorithm->name() == "Load" || lastAlgorithm->name() == "LoadMD") && nalgs > 1) {
53 const auto &penultimateAlgorithm = history.getAlgorithmHistory(nalgs - 2);
54 if (penultimateAlgorithm->name() == "ConvertToMD") {
55 return penultimateAlgorithm->getPropertyValue("dEAnalysisMode");
56 }
57 }
58 throw std::invalid_argument("The last algorithm in the history of the "
59 "input workspace is not ConvertToMD");
60 }
61 return emode;
62}
63
70 const auto &props = getProperties();
71 auto binMD = createChildAlgorithm("BinMD", 0.0, 0.3);
72 binMD->setPropertyValue("AxisAligned", "1");
73 for (auto prop : props) {
74 const auto &propName = prop->name();
75 if (propName != "FluxWorkspace" && propName != "SolidAngleWorkspace" &&
76 propName != "TemporaryNormalizationWorkspace" && propName != "OutputNormalizationWorkspace" &&
77 propName != "SkipSafetyCheck") {
78 binMD->setPropertyValue(propName, prop->value());
79 }
80 }
81 binMD->executeAsChildAlg();
82 Workspace_sptr outputWS = binMD->getProperty("OutputWorkspace");
83 return std::dynamic_pointer_cast<MDHistoWorkspace>(outputWS);
84}
85
91 // Copy the MDHisto workspace, and change signals and errors to 0.
92 std::shared_ptr<IMDHistoWorkspace> tmp = this->getProperty("TemporaryNormalizationWorkspace");
93 m_normWS = std::dynamic_pointer_cast<MDHistoWorkspace>(tmp);
94 if (!m_normWS) {
95 m_normWS = dataWS.clone();
96 m_normWS->setTo(0., 0., 0.);
97 } else {
98 // Temp is given. Accumulation mode is on
99 m_accumulate = true;
100 }
101}
102
111std::vector<coord_t> MDNormBase::getValuesFromOtherDimensions(bool &skipNormalization, uint16_t expInfoIndex) const {
112 const auto &currentRun = m_inputWS->getExperimentInfo(expInfoIndex)->run();
113
114 std::vector<coord_t> otherDimValues;
115 size_t lastCol = m_diffraction ? 3 : 4;
116 for (size_t i = lastCol; i < m_inputWS->getNumDims(); i++) {
117 const auto dimension = m_inputWS->getDimension(i);
118 auto dimMin = static_cast<float>(dimension->getMinimum());
119 auto dimMax = static_cast<float>(dimension->getMaximum());
120 auto *dimProp = dynamic_cast<Kernel::TimeSeriesProperty<double> *>(currentRun.getProperty(dimension->getName()));
121 if (dimProp) {
122 auto value = static_cast<coord_t>(dimProp->firstValue());
123 otherDimValues.emplace_back(value);
124 // in the original MD data no time was spent measuring between dimMin and
125 // dimMax
126 if (value < dimMin || value > dimMax) {
127 skipNormalization = true;
128 }
129 }
130 }
131 return otherDimValues;
132}
133
142void MDNormBase::findIntegratedDimensions(const std::vector<coord_t> &otherDimValues, bool &skipNormalization) {
143 // Get indices of the original dimensions in the output workspace,
144 // and if not found, the corresponding dimension is integrated
145 m_transformation = m_normWS->getTransformFromOriginal(0)->makeAffineMatrix();
146
147 const size_t nrm1 = m_transformation.numRows() - 1;
148 const size_t ncm1 = m_transformation.numCols() - 1;
149 const size_t lastCol = m_diffraction ? 3 : 4;
150 for (size_t row = 0; row < nrm1; row++) // affine matrix, ignore last row
151 {
152 const auto dimen = m_normWS->getDimension(row);
153 const auto dimMin(dimen->getMinimum()), dimMax(dimen->getMaximum());
154 if (m_transformation[row][0] == 1.) {
155 m_hIntegrated = false;
156 m_hIdx = row;
157 m_hmin = std::max(m_hmin, dimMin);
158 m_hmax = std::min(m_hmax, dimMax);
159 if (m_hmin > dimMax || m_hmax < dimMin) {
160 skipNormalization = true;
161 }
162 }
163 if (m_transformation[row][1] == 1.) {
164 m_kIntegrated = false;
165 m_kIdx = row;
166 m_kmin = std::max(m_kmin, dimMin);
167 m_kmax = std::min(m_kmax, dimMax);
168 if (m_kmin > dimMax || m_kmax < dimMin) {
169 skipNormalization = true;
170 }
171 }
172 if (m_transformation[row][2] == 1.) {
173 m_lIntegrated = false;
174 m_lIdx = row;
175 m_lmin = std::max(m_lmin, dimMin);
176 m_lmax = std::min(m_lmax, dimMax);
177 if (m_lmin > dimMax || m_lmax < dimMin) {
178 skipNormalization = true;
179 }
180 }
181
182 if (!m_diffraction && m_transformation[row][3] == 1.) {
183 m_dEIntegrated = false;
184 m_eIdx = row;
185 m_dEmin = std::max(m_dEmin, dimMin);
186 m_dEmax = std::min(m_dEmax, dimMax);
187 if (m_dEmin > dimMax || m_dEmax < dimMin) {
188 skipNormalization = true;
189 }
190 }
191 for (size_t col = lastCol; col < ncm1; col++) // affine matrix, ignore last column
192 {
193 if (m_transformation[row][col] == 1.) {
194 double val = otherDimValues.at(col - lastCol);
195 if (val > dimMax || val < dimMin) {
196 skipNormalization = true;
197 }
198 }
199 }
200 }
201}
202
208 if (!m_hIntegrated) {
209 auto &hDim = *m_normWS->getDimension(m_hIdx);
210 m_hX = std::vector<double>(hDim.getNBoundaries());
211 for (size_t i = 0; i < m_hX.size(); ++i) {
212 m_hX[i] = hDim.getX(i);
213 }
214 }
215 if (!m_kIntegrated) {
216 auto &kDim = *m_normWS->getDimension(m_kIdx);
217 m_kX = std::vector<double>(kDim.getNBoundaries());
218 for (size_t i = 0; i < m_kX.size(); ++i) {
219 m_kX[i] = kDim.getX(i);
220 }
221 }
222 if (!m_lIntegrated) {
223 auto &lDim = *m_normWS->getDimension(m_lIdx);
224 m_lX = std::vector<double>(lDim.getNBoundaries());
225 for (size_t i = 0; i < m_lX.size(); ++i) {
226 m_lX[i] = lDim.getX(i);
227 }
228 }
229 if ((!m_diffraction) && (!m_dEIntegrated)) {
230 // NOTE: store k final instead
231 auto &eDim = *m_normWS->getDimension(m_eIdx);
232 m_eX = std::vector<double>(eDim.getNBoundaries());
233 for (size_t i = 0; i < m_eX.size(); ++i) {
234 double temp = m_Ei - eDim.getX(i);
235 temp = std::max(temp, 0.);
236 m_eX[i] = std::sqrt(energyToK * temp);
237 }
238 }
239}
240
249 bool doInvert) {
250 // Make it to a method!
251 DblMatrix soMatrix(3, 3);
252 auto v = so.transformHKL(V3D(1, 0, 0));
253 soMatrix.setColumn(0, v);
254 v = so.transformHKL(V3D(0, 1, 0));
255 soMatrix.setColumn(1, v);
256 v = so.transformHKL(V3D(0, 0, 1));
257 soMatrix.setColumn(2, v);
258 soMatrix.Invert();
259 DblMatrix Qtransform = R * m_UB * soMatrix * m_W;
260 if (doInvert)
261 Qtransform.Invert();
262
263 return Qtransform;
264}
265
272void MDNormBase::calculateNormalization(const std::vector<coord_t> &otherValues, uint16_t expInfoIndex) {
273 const auto &currentExptInfo = *(m_inputWS->getExperimentInfo(expInfoIndex));
274 const auto &spectrumInfo = currentExptInfo.spectrumInfo();
275 Kernel::DblMatrix Qtransform(getLogValues<VectorDoubleProperty>(
276 currentExptInfo, "RUBW_MATRIX")); // includes the 2*pi factor but not goniometer for now :)
277 Qtransform = currentExptInfo.run().getGoniometerMatrix() * Qtransform;
278 Qtransform.Invert();
279 const double protonCharge = currentExptInfo.run().getProtonCharge();
280
281 calculateNormInner(spectrumInfo, otherValues, protonCharge, 0., Qtransform);
282}
283
291void MDNormBase::calculateNormalization(const std::vector<coord_t> &otherValues, const Geometry::SymmetryOperation &so,
292 uint16_t expInfoIndex) {
293 const auto &currentExptInfo = *(m_inputWS->getExperimentInfo(expInfoIndex));
294 std::vector<double> lowValues = getLogValues<VectorDoubleProperty>(currentExptInfo, "MDNorm_low");
295 std::vector<double> highValues = getLogValues<VectorDoubleProperty>(currentExptInfo, "MDNorm_high");
296 // calculate Q transformation matrix (R * UB * SymmetryOperation * m_W)^-1
297 // in order to calculate intersections
298 Kernel::DblMatrix Qtransform = calQTransform(currentExptInfo.run().getGoniometerMatrix(), so);
299
300 // get proton charges
301 const double protonCharge = currentExptInfo.run().getProtonCharge();
302 const double protonChargeBkgd =
303 (m_backgroundWS != nullptr) ? m_backgroundWS->getExperimentInfo(0)->run().getProtonCharge() : 0;
304
305 const auto &spectrumInfo = currentExptInfo.spectrumInfo();
306
307 calculateNormInner(spectrumInfo, otherValues, protonCharge, protonChargeBkgd, Qtransform, lowValues, highValues);
308}
309
316void MDNormBase::calculateNormContinuous(const std::vector<coord_t> &otherValues, uint16_t expInfoIndex,
317 const Geometry::SymmetryOperation *so) {
318 const auto &currentExptInfo = *(m_inputWS->getExperimentInfo(expInfoIndex));
319 const auto &spectrumInfo = currentExptInfo.spectrumInfo();
320 bool isMDNorm = false;
321 std::vector<double> lowValues, highValues;
322 DblMatrix UBWSymm; // Symmetrised matrix (UB * symm * W) converting from lab to crystal frame
323 if (so == nullptr) {
324 UBWSymm = getLogValues<VectorDoubleProperty>(
325 currentExptInfo, "RUBW_MATRIX"); // includes the 2*pi factor but not goniometer for now :)
326 } else {
327 if (m_backgroundWS != nullptr) {
328 throw std::runtime_error("Continuous rotation and background workspace not yet implemented.");
329 }
330 isMDNorm = true;
331 UBWSymm = calQTransform(DblMatrix(3, 3, true), *so, false);
332 lowValues = getLogValues<VectorDoubleProperty>(currentExptInfo, "MDNorm_low");
333 highValues = getLogValues<VectorDoubleProperty>(currentExptInfo, "MDNorm_high");
334 }
335 // MDEventWS was created with the "useLogTimes" option: should be only a single expInfo, but
336 // gonios vary with time - we now coarse-bin it to compute the normalisation.
337 const Run &run = currentExptInfo.run();
338 if (!run.hasProperty(LOG_CHARGE_NAME)) {
339 throw std::runtime_error("Wokspace does not contain the proton charge log. Cannot continue.");
340 }
341
342 double progressStart = 0.3 + 0.7 * expInfoIndex / m_numExptInfos;
343 double progressEnd = 0.3 + 0.7 * (expInfoIndex + 1) / m_numExptInfos;
344 double normfac = 1.0;
345 if (auto *factor = run.hasProperty("NormalizationFactor")
346 ? dynamic_cast<Kernel::PropertyWithValue<double> *>(run.getProperty("NormalizationFactor"))
347 : nullptr) {
348 normfac = (*factor)();
349 }
350 std::istringstream tosplit;
351 if (auto *logTimesStr = dynamic_cast<PropertyWithValue<std::string> *>(run.getProperty("useLogTimes"));
352 run.hasProperty("useLogTimes") && logTimesStr) {
353 tosplit.str((*logTimesStr)());
354 } else {
355 throw std::runtime_error("useLogTimes property needs to be defined for Continuous Normalization");
356 }
357 std::vector<TimeSeriesProperty<double> *> logs;
358 std::vector<size_t> movingGonioIndex;
359 const TimeSeriesProperty<double> *protonlog = run.getTimeSeriesProperty<double>(LOG_CHARGE_NAME);
360 std::vector<double> protonCharge = protonlog->valuesAsVector();
361 std::vector<Types::Core::DateAndTime> protonTimes = protonlog->timesAsVector();
363
364 for (std::string name; std::getline(tosplit, name, ',');) {
365 auto *log = run.getTimeSeriesProperty<double>(name);
366 logs.push_back(log);
367 if ((log->maxValue() - log->minValue()) > STATIONARYANGLIM) { // Assume gonio logs in degrees
368 movingGonioIndex.push_back(logs.size() - 1);
369 }
370 }
371
372 // Convert from picoCoulomb to uA.hr for SNS data
373 if (protonlog->units().find("picoCoulomb") != std::string::npos) {
374 normfac *= 3600.e6;
375 }
376
377 if (movingGonioIndex.size() == 1) {
378 // If we only have a single moving gonio, bin all its values to GONIOBINSTEP degree bins and
379 // run inner loop on each binned angle
380 const TimeROI &timeroi = run.getTimeROI();
381 const auto &gonioAxLog = logs[movingGonioIndex[0]];
382 std::vector<double> filteredVals = gonioAxLog->filteredValuesAsVector(&timeroi);
383 const auto &[min, max] = std::minmax_element(filteredVals.begin(), filteredVals.end());
384 std::vector<double> gonioCharge(static_cast<int>((*max - *min) / GONIOBINSTEP) + 1, 0.0);
385 for (size_t n = 0; n < protonCharge.size(); n++) {
386 double logval = gonioAxLog->getSingleValue(protonTimes[n]);
387 if (std::isnan(logval) || logval > *max || logval < *min) {
388 continue;
389 }
390 auto idx = static_cast<size_t>(floor((logval - *min) / GONIOBINSTEP));
391 gonioCharge[idx] += protonCharge[n];
392 }
393 m_progress->resetNumSteps(static_cast<int64_t>(gonioCharge.size()), progressStart, progressEnd);
394 for (size_t n = 0; n < gonioCharge.size(); n++) {
395 if (gonioCharge[n] < MINPROTONCHARGE) {
396 continue;
397 }
398 auto nn = static_cast<double>(n);
399 gonio.setRotationAngle(movingGonioIndex[0], nn * GONIOBINSTEP + *min);
400 DblMatrix Qtransform = gonio.getR() * UBWSymm;
401 Qtransform.Invert();
402 if (isMDNorm) {
403 calculateNormInner(spectrumInfo, otherValues, gonioCharge[n] / normfac, 0., Qtransform, lowValues, highValues);
404 } else {
405 calculateNormInner(spectrumInfo, otherValues, gonioCharge[n] / normfac, 0., Qtransform);
406 }
407 m_progress->report();
408 }
409 } else {
410 // Otherwise run inner loop over small bins of proton charge in time
411 double chargeSum = 0.0;
412 size_t i0 = 0;
413 bool skipIter = false;
414 m_progress->resetNumSteps(static_cast<int64_t>(protonCharge.size()), progressStart, progressEnd);
415 for (size_t n = 0; n < protonCharge.size(); n++) {
416 chargeSum += protonCharge[n];
417 if (chargeSum > CHARGEBINSIZE) {
418 size_t index = static_cast<int>(floor(static_cast<double>(n - i0) / 2.)) + i0;
419 skipIter = false;
420 for (size_t gAx = 0; gAx < gonio.getNumberAxes(); gAx++) {
421 double logval = logs[gAx]->getSingleValue(protonTimes[index]);
422 if (std::isnan(logval)) {
423 skipIter = true;
424 continue;
425 }
426 gonio.setRotationAngle(gAx, logval);
427 }
428 if (!skipIter) {
429 DblMatrix Qtrans = gonio.getR() * UBWSymm;
430 Qtrans.Invert();
431 if (isMDNorm) {
432 calculateNormInner(spectrumInfo, otherValues, chargeSum / normfac, 0., Qtrans, lowValues, highValues);
433 } else {
434 calculateNormInner(spectrumInfo, otherValues, chargeSum / normfac, 0., Qtrans);
435 }
436 }
437 chargeSum = 0;
438 i0 = n;
439 }
440 m_progress->report();
441 }
442 }
443 if (m_numExptInfos > 1) {
444 m_progress->resetNumSteps(m_numExptInfos - expInfoIndex, progressStart, 1.0);
445 }
446}
447
448void MDNormBase::calculateNormInner(const API::SpectrumInfo &spectrumInfo, const std::vector<coord_t> &otherValues,
449 const double protonCharge, const double protonChargeBkgd,
450 const Kernel::DblMatrix &Qtransform, const std::vector<double> &lowValues,
451 const std::vector<double> &highValues) {
452 // Mapping
453 const auto ndets = static_cast<int64_t>(spectrumInfo.size());
454 bool haveSA = false;
455 API::MatrixWorkspace_const_sptr integrFlux, solidAngleWS = getProperty("SolidAngleWorkspace");
456 detid2index_map fluxDetToIdx, solidAngDetToIdx;
457 bool thread_safe = true;
458 if (m_diffraction) {
459 integrFlux = getProperty("FluxWorkspace"); // FluxWorkspace is mandatory for diffraction
460 integrFlux->getXMinMax(m_kfmin, m_kfmax);
461 fluxDetToIdx = integrFlux->getDetectorIDToWorkspaceIndexMap();
462 thread_safe = Kernel::threadSafe(*integrFlux);
463 }
464 if (solidAngleWS != nullptr) {
465 haveSA = true;
466 solidAngDetToIdx = solidAngleWS->getDetectorIDToWorkspaceIndexMap();
467 }
468
469 const size_t vmdDims = m_diffraction ? 3 : 4;
470 size_t numNPoints = (m_backgroundWS) ? m_bkgdNormWS->getNPoints() : 0;
471 if (m_backgroundWS && numNPoints != m_normWS->getNPoints()) {
472 throw std::runtime_error("N points are different");
473 }
474
475 bool isMDNorm = !lowValues.empty();
476 if (isMDNorm) {
477 m_hmin = static_cast<coord_t>(m_hX[0]);
478 m_kmin = static_cast<coord_t>(m_kX[0]);
479 m_lmin = static_cast<coord_t>(m_lX[0]);
480 m_hmax = static_cast<coord_t>(m_hX.back());
481 m_kmax = static_cast<coord_t>(m_kX.back());
482 m_lmax = static_cast<coord_t>(m_lX.back());
483 }
484
485 PRAGMA_OMP(parallel for if(thread_safe))
486 for (int64_t i = 0; i < ndets; i++) {
488
489 if (!spectrumInfo.hasDetectors(i) || spectrumInfo.isMonitor(i) || spectrumInfo.isMasked(i)) {
490 continue;
491 }
492 const auto &detector = spectrumInfo.detector(i);
493 double theta = detector.getTwoTheta(m_samplePos, m_beamDir);
494 double phi = detector.getPhi();
495 // If the detector is a group, this should be the ID of the first detector
496 const auto detID = detector.getID();
497
498 // Get the flux spectrum spectrum number for diffraction
499 size_t wsIdx = 0;
500 if (m_diffraction) {
501 if (auto index = fluxDetToIdx.find(detID); index != fluxDetToIdx.end()) {
502 wsIdx = index->second;
503 } else { // masked detector in flux, but not in input workspace
504 continue;
505 }
506 }
507
508 // Intersections
509 std::vector<std::array<double, 4>> intersections;
510 std::vector<coord_t> pos, posNew;
511 if (isMDNorm) {
512 this->calculateIntersections(intersections, theta, phi, Qtransform, lowValues[i], highValues[i]);
513 } else {
514 this->calculateIntersections(intersections, theta, phi, Qtransform);
515 }
516 // No need to do normalization calculation if there is no intersection
517 if (intersections.empty())
518 continue;
519
520 // Get solid angle for this contribution
521 double solid = protonCharge;
522 double bkgdSolid = protonChargeBkgd;
523 if (haveSA) {
524 double solid_angle_factor = solidAngleWS->y(solidAngDetToIdx.at(detID))[0];
525 solid = solid_angle_factor * protonCharge;
526 bkgdSolid = solid_angle_factor * protonChargeBkgd;
527 }
528
529 // -- calculate integrals for the intersection --
530 // momentum values at intersections
531 std::vector<double> yValues;
532 if (m_diffraction) {
533 // copy momenta to xValues
534 std::vector<double> xValues(intersections.size());
535 yValues.resize(intersections.size());
536 auto x = xValues.begin();
537 for (auto it = intersections.begin(); it != intersections.end(); ++it, ++x) {
538 *x = (*it)[3];
539 }
540 // calculate integrals at momenta from xValues by interpolating between
541 // points in spectrum sp
542 // of workspace integrFlux. The result is stored in yValues
543 calcIntegralsForIntersections(xValues, *integrFlux, wsIdx, yValues);
544 }
545
546 // Compute final position in HKL
547 // pre-allocate for efficiency and copy non-hkl dim values into place
548 pos.resize(vmdDims + otherValues.size() + 1);
549 std::copy(otherValues.begin(), otherValues.end(), pos.begin() + vmdDims);
550 pos.emplace_back(1.f);
551 for (auto it = intersections.begin() + 1; it != intersections.end(); ++it) {
552 const auto &curIntSec = *it;
553 const auto &prevIntSec = *(it - 1);
554 // the full vector isn't used so compute only what is necessary
555 double delta = m_diffraction ? (curIntSec[3] - prevIntSec[3]) / 1000. // tolerance is 1e-7
556 : (curIntSec[3] * curIntSec[3] - prevIntSec[3] * prevIntSec[3]) / energyToK;
557 if (delta < 1e-10)
558 continue; // Assume zero contribution if difference is small
559
560 // Average between two intersections for final position
561 std::transform(curIntSec.data(), curIntSec.data() + vmdDims, prevIntSec.data(), pos.begin(),
562 [](const double rhs, const double lhs) { return static_cast<coord_t>(0.5 * (rhs + lhs)); });
563
564 // transform kf to energy transfer
565 if (!m_diffraction) {
566 pos[3] = static_cast<coord_t>(m_Ei - pos[3] * pos[3] / energyToK);
567 }
568 m_transformation.multiplyPoint(pos, posNew);
569 size_t linIndex = m_normWS->getLinearIndexAtCoord(posNew.data());
570 if (linIndex == static_cast<size_t>(-1))
571 continue;
572
573 if (m_diffraction) {
574 // index of the current intersection
575 auto k = static_cast<size_t>(std::distance(intersections.begin(), it));
576 delta = (yValues[k] - yValues[k - 1]);
577 }
578 // signal = delta * solid = integral between two consecutive intersections * solid angle
579 Mantid::Kernel::AtomicOp(m_signalArray[linIndex], delta * solid, std::plus<signal_t>());
580 if (m_backgroundWS) {
581 Mantid::Kernel::AtomicOp(m_bkgdSignalArray[linIndex], delta * bkgdSolid, std::plus<signal_t>());
582 }
583 }
585 }
587}
588
597void MDNormBase::calcIntegralsForIntersections(const std::vector<double> &xValues,
598 const API::MatrixWorkspace &integrFlux, size_t sp,
599 std::vector<double> &yValues) const {
600 assert(xValues.size() == yValues.size());
601
602 // the x-data from the workspace
603 const auto &xData = integrFlux.x(sp);
604 const double xStart = xData.front();
605 const double xEnd = xData.back();
606
607 // the values in integrFlux are expected to be integrals of a non-negative
608 // function
609 // ie they must make a non-decreasing function
610 const auto &yData = integrFlux.y(sp);
611 size_t spSize = yData.size();
612
613 const double yMin = 0.0;
614 const double yMax = yData.back();
615
616 size_t nData = xValues.size();
617 // all integrals below xStart must be 0
618 if (xValues[nData - 1] < xStart) {
619 std::fill(yValues.begin(), yValues.end(), yMin);
620 return;
621 }
622
623 // all integrals above xEnd must be equal tp yMax
624 if (xValues[0] > xEnd) {
625 std::fill(yValues.begin(), yValues.end(), yMax);
626 return;
627 }
628
629 size_t i = 0;
630 // integrals below xStart must be 0
631 while (i < nData - 1 && xValues[i] < xStart) {
632 yValues[i] = yMin;
633 i++;
634 }
635 size_t j = 0;
636 for (; i < nData; i++) {
637 // integrals above xEnd must be equal tp yMax
638 if (j >= spSize - 1) {
639 yValues[i] = yMax;
640 } else {
641 double xi = xValues[i];
642 while (j < spSize - 1 && xi > xData[j])
643 j++;
644 // if x falls onto an interpolation point return the corresponding y
645 if (xi == xData[j]) {
646 yValues[i] = yData[j];
647 } else if (j == spSize - 1) {
648 // if we get above xEnd it's yMax
649 yValues[i] = yMax;
650 } else if (j > 0) {
651 // interpolate between the consecutive points
652 double x0 = xData[j - 1];
653 double x1 = xData[j];
654 double y0 = yData[j - 1];
655 double y1 = yData[j];
656 yValues[i] = y0 + (y1 - y0) * (xi - x0) / (x1 - x0);
657 } else // j == 0
658 {
659 yValues[i] = yMin;
660 }
661 }
662 }
663}
664
676void MDNormBase::calculateIntersections(std::vector<std::array<double, 4>> &intersections, const double theta,
677 const double phi, const Kernel::DblMatrix &transform, double lowvalue,
678 double highvalue) {
679 V3D qin, qout;
680 double kfmin, kfmax, kimin, kimax;
681 bool isMDNorm = !std::isnan(lowvalue);
682 if (isMDNorm) {
683 qout = V3D(sin(theta) * cos(phi), sin(theta) * sin(phi), cos(theta));
684 qin = V3D(0., 0., 1.);
685 if (m_diffraction) {
686 kimin = kfmin = lowvalue;
687 kimax = kfmax = highvalue;
688 } else {
689 kimin = kimax = std::sqrt(energyToK * m_Ei);
690 kfmin = std::sqrt(energyToK * (m_Ei - highvalue));
691 kfmax = std::sqrt(energyToK * (m_Ei - lowvalue));
692 }
693 } else {
694 if (m_diffraction) {
695 qin = qout = V3D(-sin(theta) * cos(phi), -sin(theta) * sin(phi), 1. - cos(theta));
696 kimin = kfmin = m_kfmin;
697 kimax = kfmax = m_kfmax;
698 } else {
699 qout = V3D(sin(theta) * cos(phi), sin(theta) * sin(phi), cos(theta));
700 qin = V3D(0., 0., 1.);
701 kimin = kimax = m_ki;
702 kfmin = m_kfmin;
703 kfmax = m_kfmax;
704 }
705 }
706
707 qout = transform * qout;
708 qin = transform * qin;
709 if (m_convention == "Crystallography") {
710 qout *= -1;
711 qin *= -1;
712 }
713 double hStart = qin.X() * kimin - qout.X() * kfmin, hEnd = qin.X() * kimax - qout.X() * kfmax;
714 double kStart = qin.Y() * kimin - qout.Y() * kfmin, kEnd = qin.Y() * kimax - qout.Y() * kfmax;
715 double lStart = qin.Z() * kimin - qout.Z() * kfmin, lEnd = qin.Z() * kimax - qout.Z() * kfmax;
716 double eps = 1e-10;
717 auto hNBins = m_hX.size();
718 auto kNBins = m_kX.size();
719 auto lNBins = m_lX.size();
720 auto eNBins = m_eX.size();
721 intersections.clear();
722 intersections.reserve(hNBins + kNBins + lNBins + eNBins + 8); // 8 is 3*(min,max for each Q component)+kfmin+kfmax
723
724 // calculate intersections with planes perpendicular to h
725 if (fabs(hStart - hEnd) > eps) {
726 double fmom = (kfmax - kfmin) / (hEnd - hStart);
727 double fk = (kEnd - kStart) / (hEnd - hStart);
728 double fl = (lEnd - lStart) / (hEnd - hStart);
729 if (!m_hIntegrated) {
730 for (size_t i = 0; i < hNBins; i++) {
731 double hi = m_hX[i];
732 if ((hi >= m_hmin) && (hi <= m_hmax) && ((hStart - hi) * (hEnd - hi) < 0)) {
733 // if hi is between hStart and hEnd, then ki and li will be between
734 // kStart, kEnd and lStart, lEnd and momi will be between kfmin and kfmax
735 double ki = fk * (hi - hStart) + kStart;
736 double li = fl * (hi - hStart) + lStart;
737 if ((ki >= m_kmin) && (ki <= m_kmax) && (li >= m_lmin) && (li <= m_lmax)) {
738 double momi = fmom * (hi - hStart) + kfmin;
739 intersections.push_back({{hi, ki, li, momi}});
740 }
741 }
742 }
743 }
744 if (!isMDNorm) {
745 double momhMin = fmom * (m_hmin - hStart) + kfmin;
746 if ((momhMin - kfmin) * (momhMin - kfmax) < 0) // kfmin>kfmax
747 {
748 // khmin and lhmin
749 double khmin = fk * (m_hmin - hStart) + kStart;
750 double lhmin = fl * (m_hmin - hStart) + lStart;
751 if ((khmin >= m_kmin) && (khmin <= m_kmax) && (lhmin >= m_lmin) && (lhmin <= m_lmax)) {
752 intersections.push_back({{m_hmin, khmin, lhmin, momhMin}});
753 }
754 }
755 double momhMax = fmom * (m_hmax - hStart) + kfmin;
756 if ((momhMax - kfmin) * (momhMax - kfmax) <= 0) {
757 // khmax and lhmax
758 double khmax = fk * (m_hmax - hStart) + kStart;
759 double lhmax = fl * (m_hmax - hStart) + lStart;
760 if ((khmax >= m_kmin) && (khmax <= m_kmax) && (lhmax >= m_lmin) && (lhmax <= m_lmax)) {
761 intersections.push_back({{m_hmax, khmax, lhmax, momhMax}});
762 }
763 }
764 }
765 }
766
767 // calculate intersections with planes perpendicular to k
768 if (fabs(kStart - kEnd) > eps) {
769 double fmom = (kfmax - kfmin) / (kEnd - kStart);
770 double fh = (hEnd - hStart) / (kEnd - kStart);
771 double fl = (lEnd - lStart) / (kEnd - kStart);
772 if (!m_kIntegrated) {
773 for (size_t i = 0; i < kNBins; i++) {
774 double ki = m_kX[i];
775 if ((ki >= m_kmin) && (ki <= m_kmax) && ((kStart - ki) * (kEnd - ki) < 0)) {
776 // if ki is between kStart and kEnd, then hi and li will be between
777 // hStart, hEnd and lStart, lEnd and momi will be between kfmin and kfmax
778 double hi = fh * (ki - kStart) + hStart;
779 double li = fl * (ki - kStart) + lStart;
780 if ((hi >= m_hmin) && (hi <= m_hmax) && (li >= m_lmin) && (li <= m_lmax)) {
781 double momi = fmom * (ki - kStart) + kfmin;
782 intersections.push_back({{hi, ki, li, momi}});
783 }
784 }
785 }
786 }
787 if (!isMDNorm) {
788 double momkMin = fmom * (m_kmin - kStart) + kfmin;
789 if ((momkMin - kfmin) * (momkMin - kfmax) < 0) {
790 // hkmin and lkmin
791 double hkmin = fh * (m_kmin - kStart) + hStart;
792 double lkmin = fl * (m_kmin - kStart) + lStart;
793 if ((hkmin >= m_hmin) && (hkmin <= m_hmax) && (lkmin >= m_lmin) && (lkmin <= m_lmax)) {
794 intersections.push_back({{hkmin, m_kmin, lkmin, momkMin}});
795 }
796 }
797 double momkMax = fmom * (m_kmax - kStart) + kfmin;
798 if ((momkMax - kfmin) * (momkMax - kfmax) <= 0) {
799 // hkmax and lkmax
800 double hkmax = fh * (m_kmax - kStart) + hStart;
801 double lkmax = fl * (m_kmax - kStart) + lStart;
802 if ((hkmax >= m_hmin) && (hkmax <= m_hmax) && (lkmax >= m_lmin) && (lkmax <= m_lmax)) {
803 intersections.push_back({{hkmax, m_kmax, lkmax, momkMax}});
804 }
805 }
806 }
807 }
808
809 // calculate intersections with planes perpendicular to l
810 if (fabs(lStart - lEnd) > eps) {
811 double fmom = (kfmax - kfmin) / (lEnd - lStart);
812 double fh = (hEnd - hStart) / (lEnd - lStart);
813 double fk = (kEnd - kStart) / (lEnd - lStart);
814 if (!m_lIntegrated) {
815 for (size_t i = 0; i < lNBins; i++) {
816 double li = m_lX[i];
817 if ((li >= m_lmin) && (li <= m_lmax) && ((lStart - li) * (lEnd - li) < 0)) {
818 // if li is between lStart and lEnd, then hi and ki will be between
819 // hStart, hEnd and kStart, kEnd
820 double hi = fh * (li - lStart) + hStart;
821 double ki = fk * (li - lStart) + kStart;
822 if ((hi >= m_hmin) && (hi <= m_hmax) && (ki >= m_kmin) && (ki <= m_kmax)) {
823 double momi = fmom * (li - lStart) + kfmin;
824 intersections.push_back({{hi, ki, li, momi}});
825 }
826 }
827 }
828 }
829 if (!isMDNorm) {
830 double momlMin = fmom * (m_lmin - lStart) + kfmin;
831 if ((momlMin - kfmin) * (momlMin - kfmax) <= 0) {
832 // hlmin and klmin
833 double hlmin = fh * (m_lmin - lStart) + hStart;
834 double klmin = fk * (m_lmin - lStart) + kStart;
835 if ((hlmin >= m_hmin) && (hlmin <= m_hmax) && (klmin >= m_kmin) && (klmin <= m_kmax)) {
836 intersections.push_back({{hlmin, klmin, m_lmin, momlMin}});
837 }
838 }
839 double momlMax = fmom * (m_lmax - lStart) + kfmin;
840 if ((momlMax - kfmin) * (momlMax - kfmax) < 0) {
841 // hlmax and klmax
842 double hlmax = fh * (m_lmax - lStart) + hStart;
843 double klmax = fk * (m_lmax - lStart) + kStart;
844 if ((hlmax >= m_hmin) && (hlmax <= m_hmax) && (klmax >= m_kmin) && (klmax <= m_kmax)) {
845 intersections.push_back({{hlmax, klmax, m_lmax, momlMax}});
846 }
847 }
848 }
849 }
850
851 // intersections with dE
852 if (!m_dEIntegrated) {
853 for (size_t i = 0; i < eNBins; i++) {
854 double kfi = m_eX[i];
855 if ((kfi - kfmin) * (kfi - kfmax) <= 0) {
856 double h = qin.X() * kimin - qout.X() * kfi;
857 double k = qin.Y() * kimin - qout.Y() * kfi;
858 double l = qin.Z() * kimin - qout.Z() * kfi;
859 if ((h >= m_hmin) && (h <= m_hmax) && (k >= m_kmin) && (k <= m_kmax) && (l >= m_lmin) && (l <= m_lmax)) {
860 intersections.push_back({{h, k, l, kfi}});
861 }
862 }
863 }
864 }
865
866 // endpoints
867 if ((hStart >= m_hmin) && (hStart <= m_hmax) && (kStart >= m_kmin) && (kStart <= m_kmax) && (lStart >= m_lmin) &&
868 (lStart <= m_lmax)) {
869 intersections.push_back({{hStart, kStart, lStart, kfmin}});
870 }
871 if ((hEnd >= m_hmin) && (hEnd <= m_hmax) && (kEnd >= m_kmin) && (kEnd <= m_kmax) && (lEnd >= m_lmin) &&
872 (lEnd <= m_lmax)) {
873 intersections.push_back({{hEnd, kEnd, lEnd, kfmax}});
874 }
875
876 // sort intersections by final momentum
877 std::stable_sort(intersections.begin(), intersections.end(), compareMomentum);
878}
879
880} // 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.
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.
bool isMasked(const size_t index) const
Returns true if the detector(s) associated with the spectrum are masked.
const Geometry::IDetector & detector(const size_t index) const
Return a const reference to the detector or detector group of the spectrum with given index.
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.
virtual double getTwoTheta(const Kernel::V3D &observer, const Kernel::V3D &axis) const =0
Gives the angle of this detector object with respect to an axis.
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.
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:144
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:147
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: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
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:206
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.