Mantid
Loading...
Searching...
No Matches
MDNormDirectSC.cpp
Go to the documentation of this file.
1// Mantid Repository : https://github.com/mantidproject/mantid
2//
3// Copyright © 2018 ISIS Rutherford Appleton Laboratory UKRI,
4// NScD Oak Ridge National Laboratory, European Spallation Source,
5// Institut Laue - Langevin & CSNS, Institute of High Energy Physics, CAS
6// SPDX - License - Identifier: GPL - 3.0 +
8
11#include "MantidAPI/Run.h"
25
26namespace Mantid::MDAlgorithms {
27
30using namespace Mantid::DataObjects;
31using namespace Mantid::API;
32using namespace Mantid::Kernel;
33
34namespace {
35// function to compare two intersections (h,k,l,Momentum) by Momentum
36bool compareMomentum(const std::array<double, 4> &v1, const std::array<double, 4> &v2) { return (v1[3] < v2[3]); }
37const std::string LOG_CHARGE_NAME("proton_charge");
38} // namespace
39
40// Register the algorithm into the AlgorithmFactory
41DECLARE_ALGORITHM(MDNormDirectSC)
42
43
47 : m_normWS(), m_inputWS(), m_hmin(0.0f), m_hmax(0.0f), m_kmin(0.0f), m_kmax(0.0f), m_lmin(0.0f), m_lmax(0.0f),
48 m_dEmin(0.f), m_dEmax(0.f), m_Ei(0.), m_ki(0.), m_kfmin(0.), m_kfmax(0.), m_hIntegrated(true),
49 m_kIntegrated(true), m_lIntegrated(true), m_dEIntegrated(true), m_rubw(3, 3), m_hIdx(-1), m_kIdx(-1), m_lIdx(-1),
50 m_eIdx(-1), m_hX(), m_kX(), m_lX(), m_eX(), m_samplePos(), m_beamDir() {}
51
53int MDNormDirectSC::version() const { return 1; }
54
56const std::string MDNormDirectSC::category() const { return "MDAlgorithms\\Normalisation"; }
57
59const std::string MDNormDirectSC::summary() const {
60 return "Calculate normalization for an MDEvent workspace for single crystal "
61 "direct geometry inelastic measurement.";
62}
63
65const std::string MDNormDirectSC::name() const { return "MDNormDirectSC"; }
66
71 declareProperty(std::make_unique<WorkspaceProperty<IMDEventWorkspace>>("InputWorkspace", "", Direction::Input),
72 "An input MDWorkspace.");
73
74 std::string dimChars = getDimensionChars();
75 // --------------- Axis-aligned properties
76 // ---------------------------------------
77 for (size_t i = 0; i < dimChars.size(); i++) {
78 std::string dim(" ");
79 dim[0] = dimChars[i];
80 std::string propName = "AlignedDim" + dim;
82 "Binning parameters for the " + Strings::toString(i) +
83 "th dimension.\n"
84 "Enter it as a comma-separated list of values with the format: "
85 "'name,minimum,maximum,number_of_bins'. Leave blank for NONE.");
86 }
87
88 auto solidAngleValidator = std::make_shared<CompositeValidator>();
89 solidAngleValidator->add<InstrumentValidator>();
90 solidAngleValidator->add<CommonBinsValidator>();
91
92 m_progress = std::make_unique<API::Progress>(this, 0, 1, 1);
93
94 declareProperty(std::make_unique<WorkspaceProperty<>>("SolidAngleWorkspace", "", Direction::Input,
95 PropertyMode::Optional, solidAngleValidator),
96 "An input workspace containing integrated vanadium (a measure of the "
97 "solid angle).");
98
99 declareProperty(std::make_unique<PropertyWithValue<bool>>("SkipSafetyCheck", false, Direction::Input),
100 "If set to true, the algorithm does "
101 "not check history if the workspace was modified since the"
102 "ConvertToMD algorithm was run, and assume that the direct "
103 "geometry inelastic mode is used.");
104
105 declareProperty(std::make_unique<WorkspaceProperty<IMDHistoWorkspace>>("TemporaryNormalizationWorkspace", "",
107 "An input MDHistoWorkspace used to accumulate normalization "
108 "from multiple MDEventWorkspaces. If unspecified a blank "
109 "MDHistoWorkspace will be created.");
110
111 declareProperty(std::make_unique<WorkspaceProperty<IMDHistoWorkspace>>("TemporaryDataWorkspace", "", Direction::Input,
113 "An input MDHistoWorkspace used to accumulate data from "
114 "multiple MDEventWorkspaces. If unspecified a blank "
115 "MDHistoWorkspace will be created.");
116
117 declareProperty(std::make_unique<WorkspaceProperty<Workspace>>("OutputWorkspace", "", Direction::Output),
118 "A name for the output data MDHistoWorkspace.");
119 declareProperty(std::make_unique<WorkspaceProperty<Workspace>>("OutputNormalizationWorkspace", "", Direction::Output),
120 "A name for the output normalization MDHistoWorkspace.");
121}
122
123//----------------------------------------------------------------------------------------------
128 cacheInputs();
129 auto outputWS = binInputWS();
130 convention = Kernel::ConfigService::Instance().getString("Q.convention");
131 outputWS->setDisplayNormalization(Mantid::API::NoNormalization);
132 setProperty<Workspace_sptr>("OutputWorkspace", outputWS);
133 createNormalizationWS(*outputWS);
134 m_normWS->setDisplayNormalization(Mantid::API::NoNormalization);
135 setProperty("OutputNormalizationWorkspace", m_normWS);
136
137 m_numExptInfos = outputWS->getNumExperimentInfo();
138 m_signalArray = std::vector<std::atomic<signal_t>>(m_normWS->getNPoints());
139 // loop over all experiment infos
140 m_progress->resetNumSteps(m_numExptInfos, 0.3, 1.0);
141 for (uint16_t expInfoIndex = 0; expInfoIndex < m_numExptInfos; expInfoIndex++) {
142 const auto &currentExptInfo = *(m_inputWS->getExperimentInfo(expInfoIndex));
143 if (!currentExptInfo.run().hasProperty("RUBW_MATRIX")) {
144 throw std::runtime_error("Wokspace does not contain a log entry for the RUBW matrix."
145 "Cannot continue.");
146 }
147 // Check for other dimensions if we could measure anything in the original
148 // data
149 bool skipNormalization = false;
150 const std::vector<coord_t> otherValues = getValuesFromOtherDimensions(skipNormalization, expInfoIndex);
151 const auto affineTrans = findIntergratedDimensions(otherValues, skipNormalization);
153
154 if (!skipNormalization) {
155 if (currentExptInfo.run().hasProperty("useLogTimes")) {
156 calculateNormContinuous(otherValues, affineTrans, expInfoIndex);
157 } else {
158 calculateNormalization(otherValues, affineTrans, expInfoIndex);
159 }
160 } else {
161 g_log.warning("Binning limits are outside the limits of the MDWorkspace. "
162 "Not applying normalization.");
163 }
164 m_progress->report();
165 }
166 std::copy(m_signalArray.cbegin(), m_signalArray.cend(), m_normWS->mutableSignalArray());
167
168 // Set the display normalization based on the input workspace
169 outputWS->setDisplayNormalization(m_inputWS->displayNormalizationHisto());
170}
171
176 m_inputWS = getProperty("InputWorkspace");
177 bool skipCheck = getProperty("SkipSafetyCheck");
178 if (!skipCheck && (inputEnergyMode() != "Direct")) {
179 throw std::invalid_argument("Invalid energy transfer mode. Algorithm only "
180 "supports direct geometry spectrometers.");
181 }
182 // Min/max dimension values
183 const auto hdim(m_inputWS->getDimension(0)), kdim(m_inputWS->getDimension(1)), ldim(m_inputWS->getDimension(2)),
184 edim(m_inputWS->getDimension(3));
185 m_hmin = hdim->getMinimum();
186 m_kmin = kdim->getMinimum();
187 m_lmin = ldim->getMinimum();
188 m_dEmin = edim->getMinimum();
189 m_hmax = hdim->getMaximum();
190 m_kmax = kdim->getMaximum();
191 m_lmax = ldim->getMaximum();
192 m_dEmax = edim->getMaximum();
193
194 const auto &exptInfoZero = *(m_inputWS->getExperimentInfo(0));
195 auto source = exptInfoZero.getInstrument()->getSource();
196 auto sample = exptInfoZero.getInstrument()->getSample();
197 if (source == nullptr || sample == nullptr) {
199 "Instrument not sufficiently defined: failed to get source and/or "
200 "sample");
201 }
202 m_samplePos = sample->getPos();
203 m_beamDir = normalize(m_samplePos - source->getPos());
204
205 double originaldEmin = exptInfoZero.run().getBinBoundaries().front();
206 double originaldEmax = exptInfoZero.run().getBinBoundaries().back();
207 if (exptInfoZero.run().hasProperty("Ei")) {
208 m_Ei = exptInfoZero.run().getPropertyValueAsType<double>("Ei");
209 if (m_Ei <= 0) {
210 throw std::invalid_argument("Ei stored in the workspace is not positive");
211 }
212 } else {
213 throw std::invalid_argument("Could not find Ei value in the workspace.");
214 }
215 double eps = 1e-7;
216 if (m_Ei - originaldEmin < eps) {
217 originaldEmin = m_Ei - eps;
218 }
219 if (m_Ei - originaldEmax < eps) {
220 originaldEmax = m_Ei - 1e-7;
221 }
222 if (originaldEmin == originaldEmax) {
223 throw std::runtime_error("The limits of the original workspace used in "
224 "ConvertToMD are incorrect");
225 }
226 const double energyToK = 8.0 * M_PI * M_PI * PhysicalConstants::NeutronMass * PhysicalConstants::meV * 1e-20 /
228 m_ki = std::sqrt(energyToK * m_Ei);
229 m_kfmin = std::sqrt(energyToK * (m_Ei - originaldEmin));
230 m_kfmax = std::sqrt(energyToK * (m_Ei - originaldEmax));
231}
232
238 const auto &hist = m_inputWS->getHistory();
239 const size_t nalgs = hist.size();
240 const auto &lastAlgHist = hist.getAlgorithmHistory(nalgs - 1);
241
242 std::string emode;
243 if (lastAlgHist->name() == "ConvertToMD") {
244 // get dEAnalysisMode
245 emode = lastAlgHist->getPropertyValue("dEAnalysisMode");
246 } else {
247 if ((lastAlgHist->name() == "Load" || lastAlgHist->name() == "LoadMD") && nalgs > 1) {
248 const auto &penultimateAlgHist = hist.getAlgorithmHistory(nalgs - 2);
249 if (penultimateAlgHist->name() == "ConvertToMD") {
250 return penultimateAlgHist->getPropertyValue("dEAnalysisMode");
251 }
252 }
253 throw std::invalid_argument("The last algorithm in the history of the "
254 "input workspace is not ConvertToMD");
255 }
256 return emode;
257}
258
265 const auto &props = getProperties();
266 auto binMD = createChildAlgorithm("BinMD", 0.0, 0.3);
267 binMD->setPropertyValue("AxisAligned", "1");
268 for (auto prop : props) {
269 const auto &propName = prop->name();
270 if (propName != "SolidAngleWorkspace" && propName != "TemporaryNormalizationWorkspace" &&
271 propName != "OutputNormalizationWorkspace" && propName != "SkipSafetyCheck") {
272 binMD->setPropertyValue(propName, prop->value());
273 }
274 }
275 binMD->executeAsChildAlg();
276 Workspace_sptr outputWS = binMD->getProperty("OutputWorkspace");
277 return std::dynamic_pointer_cast<MDHistoWorkspace>(outputWS);
278}
279
285 // Copy the MDHisto workspace, and change signals and errors to 0.
286 std::shared_ptr<IMDHistoWorkspace> tmp = this->getProperty("TemporaryNormalizationWorkspace");
287 m_normWS = std::dynamic_pointer_cast<MDHistoWorkspace>(tmp);
288 if (!m_normWS) {
289 m_normWS = dataWS.clone();
290 m_normWS->setTo(0., 0., 0.);
291 }
292}
293
302std::vector<coord_t> MDNormDirectSC::getValuesFromOtherDimensions(bool &skipNormalization,
303 uint16_t expInfoIndex) const {
304 const auto &currentRun = m_inputWS->getExperimentInfo(expInfoIndex)->run();
305
306 std::vector<coord_t> otherDimValues;
307 for (size_t i = 4; i < m_inputWS->getNumDims(); i++) {
308 const auto dimension = m_inputWS->getDimension(i);
309 auto dimMin = static_cast<float>(dimension->getMinimum());
310 auto dimMax = static_cast<float>(dimension->getMaximum());
311 auto *dimProp = dynamic_cast<Kernel::TimeSeriesProperty<double> *>(currentRun.getProperty(dimension->getName()));
312 if (dimProp) {
313 auto value = static_cast<coord_t>(dimProp->firstValue());
314 otherDimValues.emplace_back(value);
315 // in the original MD data no time was spent measuring between dimMin and
316 // dimMax
317 if (value < dimMin || value > dimMax) {
318 skipNormalization = true;
319 }
320 }
321 }
322 return otherDimValues;
323}
324
335 bool &skipNormalization) {
336 // Get indices of the original dimensions in the output workspace,
337 // and if not found, the corresponding dimension is integrated
338 Kernel::Matrix<coord_t> affineMat = m_normWS->getTransformFromOriginal(0)->makeAffineMatrix();
339
340 const size_t nrm1 = affineMat.numRows() - 1;
341 const size_t ncm1 = affineMat.numCols() - 1;
342 for (size_t row = 0; row < nrm1; row++) // affine matrix, ignore last row
343 {
344 const auto dimen = m_normWS->getDimension(row);
345 const auto dimMin(dimen->getMinimum()), dimMax(dimen->getMaximum());
346 if (affineMat[row][0] == 1.) {
347 m_hIntegrated = false;
348 m_hIdx = row;
349 m_hmin = std::max(m_hmin, dimMin);
350 m_hmax = std::min(m_hmax, dimMax);
351 if (m_hmin > dimMax || m_hmax < dimMin) {
352 skipNormalization = true;
353 }
354 }
355 if (affineMat[row][1] == 1.) {
356 m_kIntegrated = false;
357 m_kIdx = row;
358 m_kmin = std::max(m_kmin, dimMin);
359 m_kmax = std::min(m_kmax, dimMax);
360 if (m_kmin > dimMax || m_kmax < dimMin) {
361 skipNormalization = true;
362 }
363 }
364 if (affineMat[row][2] == 1.) {
365 m_lIntegrated = false;
366 m_lIdx = row;
367 m_lmin = std::max(m_lmin, dimMin);
368 m_lmax = std::min(m_lmax, dimMax);
369 if (m_lmin > dimMax || m_lmax < dimMin) {
370 skipNormalization = true;
371 }
372 }
373
374 if (affineMat[row][3] == 1.) {
375 m_dEIntegrated = false;
376 m_eIdx = row;
377 m_dEmin = std::max(m_dEmin, dimMin);
378 m_dEmax = std::min(m_dEmax, dimMax);
379 if (m_dEmin > dimMax || m_dEmax < dimMin) {
380 skipNormalization = true;
381 }
382 }
383 for (size_t col = 4; col < ncm1; col++) // affine matrix, ignore last column
384 {
385 if (affineMat[row][col] == 1.) {
386 double val = otherDimValues.at(col - 3);
387 if (val > dimMax || val < dimMin) {
388 skipNormalization = true;
389 }
390 }
391 }
392 }
393
394 return affineMat;
395}
396
402 constexpr double energyToK = 8.0 * M_PI * M_PI * PhysicalConstants::NeutronMass * PhysicalConstants::meV * 1e-20 /
404 if (!m_hIntegrated) {
405 auto &hDim = *m_normWS->getDimension(m_hIdx);
406 m_hX.resize(hDim.getNBoundaries());
407 for (size_t i = 0; i < m_hX.size(); ++i) {
408 m_hX[i] = hDim.getX(i);
409 }
410 }
411 if (!m_kIntegrated) {
412 auto &kDim = *m_normWS->getDimension(m_kIdx);
413 m_kX.resize(kDim.getNBoundaries());
414 for (size_t i = 0; i < m_kX.size(); ++i) {
415 m_kX[i] = kDim.getX(i);
416 }
417 }
418 if (!m_lIntegrated) {
419 auto &lDim = *m_normWS->getDimension(m_lIdx);
420 m_lX.resize(lDim.getNBoundaries());
421 for (size_t i = 0; i < m_lX.size(); ++i) {
422 m_lX[i] = lDim.getX(i);
423 }
424 }
425 if (!m_dEIntegrated) {
426 // NOTE: store k final instead
427 auto &eDim = *m_normWS->getDimension(m_eIdx);
428 m_eX.resize(eDim.getNBoundaries());
429 for (size_t i = 0; i < m_eX.size(); ++i) {
430 double temp = m_Ei - eDim.getX(i);
431 temp = std::max(temp, 0.);
432 m_eX[i] = std::sqrt(energyToK * temp);
433 }
434 }
435}
436
444void MDNormDirectSC::calculateNormalization(const std::vector<coord_t> &otherValues,
445 const Kernel::Matrix<coord_t> &affineTrans, uint16_t expInfoIndex) {
447 const auto &currentExptInfo = *(m_inputWS->getExperimentInfo(expInfoIndex));
448 const auto &spectrumInfo = currentExptInfo.spectrumInfo();
449 auto *rubwLog = dynamic_cast<VectorDoubleProperty *>(currentExptInfo.getLog("RUBW_MATRIX"));
450 Kernel::DblMatrix rubwValue((*rubwLog)()); // includes the 2*pi factor but not goniometer for now :)
451 m_rubw = currentExptInfo.run().getGoniometerMatrix() * rubwValue;
452 m_rubw.Invert();
453 const double protonCharge = currentExptInfo.run().getProtonCharge();
454
455 calculateNormInner(spectrumInfo, protonCharge, otherValues, affineTrans);
456}
457
464void MDNormDirectSC::calculateNormContinuous(const std::vector<coord_t> &otherValues,
465 const Kernel::Matrix<coord_t> &affineTrans, uint16_t expInfoIndex) {
467 const auto &currentExptInfo = *(m_inputWS->getExperimentInfo(expInfoIndex));
468 const auto &spectrumInfo = currentExptInfo.spectrumInfo();
469 auto *rubwLog = dynamic_cast<VectorDoubleProperty *>(currentExptInfo.getLog("RUBW_MATRIX"));
470 Kernel::DblMatrix rubwValue((*rubwLog)()); // includes the 2*pi factor but not goniometer for now :)
471
472 // MDEventWS was created with the "useLogTimes" option: should be only a single expInfo, but
473 // gonios vary with time - we now coarse-bin it to compute the normalisation.
474 const Run &run = currentExptInfo.run();
475 if (!run.hasProperty(LOG_CHARGE_NAME)) {
476 throw std::runtime_error("Wokspace does not contain the proton charge log. Cannot continue.");
477 }
478
479 double progressStart = 0.3 + 0.7 * expInfoIndex / m_numExptInfos;
480 double progressEnd = 0.3 + 0.7 * (expInfoIndex + 1) / m_numExptInfos;
481 double normfac = run.hasProperty("NormalizationFactor")
482 ? (*dynamic_cast<Kernel::PropertyWithValue<double> *>(run.getProperty("NormalizationFactor")))()
483 : 1.0;
484 std::istringstream tosplit;
485 tosplit.str((*dynamic_cast<PropertyWithValue<std::string> *>(run.getProperty("useLogTimes")))());
486 std::vector<TimeSeriesProperty<double> *> logs;
487 std::vector<size_t> movingGonioIndex;
488 const TimeSeriesProperty<double> *protonlog = run.getTimeSeriesProperty<double>(LOG_CHARGE_NAME);
489 std::vector<double> protonCharge = protonlog->valuesAsVector();
490 std::vector<Types::Core::DateAndTime> protonTimes = protonlog->timesAsVector();
492
493 for (std::string name; std::getline(tosplit, name, ',');) {
494 auto *log = run.getTimeSeriesProperty<double>(name);
495 logs.push_back(log);
496 if ((log->maxValue() - log->minValue()) > STATIONARYANGLIM) { // Assume gonio logs in degrees
497 movingGonioIndex.push_back(logs.size() - 1);
498 }
499 }
500
501 // Convert from picoCoulomb to uA.hr for SNS data
502 if (protonlog->units().find("picoCoulomb") != std::string::npos) {
503 normfac *= 3600.e6;
504 }
505
506 if (movingGonioIndex.size() == 1) {
507 // If we only have a single moving gonio, bin all its values to GONIOBINSTEP degree bins and
508 // run inner loop on each binned angle
509 const TimeROI &timeroi = run.getTimeROI();
510 const auto &gonioAxLog = logs[movingGonioIndex[0]];
511 std::vector<double> filteredVals = gonioAxLog->filteredValuesAsVector(&timeroi);
512 const auto &[min, max] = std::minmax_element(filteredVals.begin(), filteredVals.end());
513 std::vector<double> gonioCharge(static_cast<int>((*max - *min) / GONIOBINSTEP) + 1, 0.0);
514 for (size_t n = 0; n < protonCharge.size(); n++) {
515 double logval = gonioAxLog->getSingleValue(protonTimes[n]);
516 if (std::isnan(logval) || logval > *max || logval < *min) {
517 continue;
518 }
519 auto idx = static_cast<size_t>(floor((logval - *min) / GONIOBINSTEP));
520 gonioCharge[idx] += protonCharge[n];
521 }
522 m_progress->resetNumSteps(static_cast<int64_t>(gonioCharge.size()), progressStart, progressEnd);
523 for (size_t n = 0; n < gonioCharge.size(); n++) {
524 if (gonioCharge[n] < MINPROTONCHARGE) {
525 continue;
526 }
527 auto nn = static_cast<double>(n);
528 gonio.setRotationAngle(movingGonioIndex[0], nn * GONIOBINSTEP + *min);
529 m_rubw = gonio.getR() * rubwValue;
530 m_rubw.Invert();
531 calculateNormInner(spectrumInfo, gonioCharge[n] / normfac, otherValues, affineTrans);
532 m_progress->report();
533 }
534 } else {
535 // Otherwise run inner loop over small bins of proton charge in time
536 double chargeSum = 0.0;
537 size_t i0 = 0;
538 bool skipIter = false;
539 m_progress->resetNumSteps(static_cast<int64_t>(protonCharge.size()), progressStart, progressEnd);
540 for (size_t n = 0; n < protonCharge.size(); n++) {
541 chargeSum += protonCharge[n];
542 if (chargeSum > CHARGEBINSIZE) {
543 size_t mid = static_cast<int>(floor(static_cast<double>(n - i0) / 2.));
544 skipIter = false;
545 for (size_t gAx = 0; gAx < gonio.getNumberAxes(); gAx++) {
546 double logval = logs[gAx]->getSingleValue(protonTimes[mid]);
547 if (std::isnan(logval)) {
548 skipIter = true;
549 continue;
550 }
551 gonio.setRotationAngle(gAx, logval);
552 }
553 if (!skipIter) {
554 m_rubw = gonio.getR() * rubwValue;
555 m_rubw.Invert();
556 calculateNormInner(spectrumInfo, chargeSum / normfac, otherValues, affineTrans);
557 }
558 chargeSum = 0;
559 i0 = n;
560 }
561 m_progress->report();
562 }
563 }
564 if (m_numExptInfos > 1) {
565 m_progress->resetNumSteps(m_numExptInfos - expInfoIndex, progressStart, 1.0);
566 }
567}
568
569void MDNormDirectSC::calculateNormInner(const API::SpectrumInfo &spectrumInfo, const double protonCharge,
570 const std::vector<coord_t> &otherValues,
571 const Kernel::Matrix<coord_t> &affineTrans) {
572 constexpr double energyToK = 8.0 * M_PI * M_PI * PhysicalConstants::NeutronMass * PhysicalConstants::meV * 1e-20 /
574 // Mapping
575 const auto ndets = static_cast<int64_t>(spectrumInfo.size());
576 bool haveSA = false;
577 API::MatrixWorkspace_const_sptr solidAngleWS = getProperty("SolidAngleWorkspace");
578 detid2index_map solidAngDetToIdx;
579 if (solidAngleWS != nullptr) {
580 haveSA = true;
581 solidAngDetToIdx = solidAngleWS->getDetectorIDToWorkspaceIndexMap();
582 }
583
584 const size_t vmdDims = 4;
585
586 PRAGMA_OMP(parallel for)
587 for (int64_t i = 0; i < ndets; i++) {
589
590 if (!spectrumInfo.hasDetectors(i) || spectrumInfo.isMonitor(i) || spectrumInfo.isMasked(i)) {
591 continue;
592 }
593 const auto &detector = spectrumInfo.detector(i);
594 double theta = detector.getTwoTheta(m_samplePos, m_beamDir);
595 double phi = detector.getPhi();
596 // If the detector is a group, this should be the ID of the first detector
597 const auto detID = detector.getID();
598
599 // Intersections
600 std::vector<std::array<double, 4>> intersections;
601 std::vector<coord_t> pos, posNew;
602 this->calculateIntersections(intersections, theta, phi);
603 if (intersections.empty())
604 continue;
605
606 // Get solid angle for this contribution
607 double solid = protonCharge;
608 if (haveSA) {
609 solid = solidAngleWS->y(solidAngDetToIdx.find(detID)->second)[0] * protonCharge;
610 }
611 // Compute final position in HKL
612 // pre-allocate for efficiency and copy non-hkl dim values into place
613 pos.resize(vmdDims + otherValues.size() + 1);
614 std::copy(otherValues.begin(), otherValues.end(), pos.begin() + vmdDims);
615 pos.emplace_back(1.f);
616 for (auto it = intersections.begin() + 1; it != intersections.end(); ++it) {
617 const auto &curIntSec = *it;
618 const auto &prevIntSec = *(it - 1);
619 // the full vector isn't used so compute only what is necessary
620 double delta = (curIntSec[3] * curIntSec[3] - prevIntSec[3] * prevIntSec[3]) / energyToK;
621 if (delta < 1e-10)
622 continue; // Assume zero contribution if difference is small
623
624 // Average between two intersections for final position
625 std::transform(curIntSec.data(), curIntSec.data() + vmdDims, prevIntSec.data(), pos.begin(),
626 [](const double rhs, const double lhs) { return static_cast<coord_t>(0.5 * (rhs + lhs)); });
627
628 // transform kf to energy transfer
629 pos[3] = static_cast<coord_t>(m_Ei - pos[3] * pos[3] / energyToK);
630 affineTrans.multiplyPoint(pos, posNew);
631 size_t linIndex = m_normWS->getLinearIndexAtCoord(posNew.data());
632 if (linIndex == static_cast<size_t>(-1))
633 continue;
634
635 // signal = integral between two consecutive intersections *solid angle
636 // *PC
637 double signal = solid * delta;
638 Mantid::Kernel::AtomicOp(m_signalArray[linIndex], signal, std::plus<signal_t>());
639 }
641 }
643}
644
653void MDNormDirectSC::calculateIntersections(std::vector<std::array<double, 4>> &intersections, const double theta,
654 const double phi) {
655 V3D qout(sin(theta) * cos(phi), sin(theta) * sin(phi), cos(theta)), qin(0., 0., m_ki);
656
657 qout = m_rubw * qout;
658 qin = m_rubw * qin;
659 if (convention == "Crystallography") {
660 qout *= -1;
661 qin *= -1;
662 }
663 double hStart = qin.X() - qout.X() * m_kfmin, hEnd = qin.X() - qout.X() * m_kfmax;
664 double kStart = qin.Y() - qout.Y() * m_kfmin, kEnd = qin.Y() - qout.Y() * m_kfmax;
665 double lStart = qin.Z() - qout.Z() * m_kfmin, lEnd = qin.Z() - qout.Z() * m_kfmax;
666 double eps = 1e-10;
667 auto hNBins = m_hX.size();
668 auto kNBins = m_kX.size();
669 auto lNBins = m_lX.size();
670 auto eNBins = m_eX.size();
671 intersections.clear();
672 intersections.reserve(hNBins + kNBins + lNBins + eNBins + 8); // 8 is 3*(min,max for each Q component)+kfmin+kfmax
673
674 // calculate intersections with planes perpendicular to h
675 if (fabs(hStart - hEnd) > eps) {
676 double fmom = (m_kfmax - m_kfmin) / (hEnd - hStart);
677 double fk = (kEnd - kStart) / (hEnd - hStart);
678 double fl = (lEnd - lStart) / (hEnd - hStart);
679 if (!m_hIntegrated) {
680 for (size_t i = 0; i < hNBins; i++) {
681 double hi = m_hX[i];
682 if ((hi >= m_hmin) && (hi <= m_hmax) && ((hStart - hi) * (hEnd - hi) < 0)) {
683 // if hi is between hStart and hEnd, then ki and li will be between
684 // kStart, kEnd and lStart, lEnd and momi will be between m_kfmin and
685 // m_kfmax
686 double ki = fk * (hi - hStart) + kStart;
687 double li = fl * (hi - hStart) + lStart;
688 if ((ki >= m_kmin) && (ki <= m_kmax) && (li >= m_lmin) && (li <= m_lmax)) {
689 double momi = fmom * (hi - hStart) + m_kfmin;
690 intersections.push_back({{hi, ki, li, momi}});
691 }
692 }
693 }
694 }
695 double momhMin = fmom * (m_hmin - hStart) + m_kfmin;
696 if ((momhMin - m_kfmin) * (momhMin - m_kfmax) < 0) // m_kfmin>m_kfmax
697 {
698 // khmin and lhmin
699 double khmin = fk * (m_hmin - hStart) + kStart;
700 double lhmin = fl * (m_hmin - hStart) + lStart;
701 if ((khmin >= m_kmin) && (khmin <= m_kmax) && (lhmin >= m_lmin) && (lhmin <= m_lmax)) {
702 intersections.push_back({{m_hmin, khmin, lhmin, momhMin}});
703 }
704 }
705 double momhMax = fmom * (m_hmax - hStart) + m_kfmin;
706 if ((momhMax - m_kfmin) * (momhMax - m_kfmax) <= 0) {
707 // khmax and lhmax
708 double khmax = fk * (m_hmax - hStart) + kStart;
709 double lhmax = fl * (m_hmax - hStart) + lStart;
710 if ((khmax >= m_kmin) && (khmax <= m_kmax) && (lhmax >= m_lmin) && (lhmax <= m_lmax)) {
711 intersections.push_back({{m_hmax, khmax, lhmax, momhMax}});
712 }
713 }
714 }
715
716 // calculate intersections with planes perpendicular to k
717 if (fabs(kStart - kEnd) > eps) {
718 double fmom = (m_kfmax - m_kfmin) / (kEnd - kStart);
719 double fh = (hEnd - hStart) / (kEnd - kStart);
720 double fl = (lEnd - lStart) / (kEnd - kStart);
721 if (!m_kIntegrated) {
722 for (size_t i = 0; i < kNBins; i++) {
723 double ki = m_kX[i];
724 if ((ki >= m_kmin) && (ki <= m_kmax) && ((kStart - ki) * (kEnd - ki) < 0)) {
725 // if ki is between kStart and kEnd, then hi and li will be between
726 // hStart, hEnd and lStart, lEnd and momi will be between m_kfmin and
727 // m_kfmax
728 double hi = fh * (ki - kStart) + hStart;
729 double li = fl * (ki - kStart) + lStart;
730 if ((hi >= m_hmin) && (hi <= m_hmax) && (li >= m_lmin) && (li <= m_lmax)) {
731 double momi = fmom * (ki - kStart) + m_kfmin;
732 intersections.push_back({{hi, ki, li, momi}});
733 }
734 }
735 }
736 }
737 double momkMin = fmom * (m_kmin - kStart) + m_kfmin;
738 if ((momkMin - m_kfmin) * (momkMin - m_kfmax) < 0) {
739 // hkmin and lkmin
740 double hkmin = fh * (m_kmin - kStart) + hStart;
741 double lkmin = fl * (m_kmin - kStart) + lStart;
742 if ((hkmin >= m_hmin) && (hkmin <= m_hmax) && (lkmin >= m_lmin) && (lkmin <= m_lmax)) {
743 intersections.push_back({{hkmin, m_kmin, lkmin, momkMin}});
744 }
745 }
746 double momkMax = fmom * (m_kmax - kStart) + m_kfmin;
747 if ((momkMax - m_kfmin) * (momkMax - m_kfmax) <= 0) {
748 // hkmax and lkmax
749 double hkmax = fh * (m_kmax - kStart) + hStart;
750 double lkmax = fl * (m_kmax - kStart) + lStart;
751 if ((hkmax >= m_hmin) && (hkmax <= m_hmax) && (lkmax >= m_lmin) && (lkmax <= m_lmax)) {
752 intersections.push_back({{hkmax, m_kmax, lkmax, momkMax}});
753 }
754 }
755 }
756
757 // calculate intersections with planes perpendicular to l
758 if (fabs(lStart - lEnd) > eps) {
759 double fmom = (m_kfmax - m_kfmin) / (lEnd - lStart);
760 double fh = (hEnd - hStart) / (lEnd - lStart);
761 double fk = (kEnd - kStart) / (lEnd - lStart);
762 if (!m_lIntegrated) {
763 for (size_t i = 0; i < lNBins; i++) {
764 double li = m_lX[i];
765 if ((li >= m_lmin) && (li <= m_lmax) && ((lStart - li) * (lEnd - li) < 0)) {
766 double hi = fh * (li - lStart) + hStart;
767 double ki = fk * (li - lStart) + kStart;
768 if ((hi >= m_hmin) && (hi <= m_hmax) && (ki >= m_kmin) && (ki <= m_kmax)) {
769 double momi = fmom * (li - lStart) + m_kfmin;
770 intersections.push_back({{hi, ki, li, momi}});
771 }
772 }
773 }
774 }
775 double momlMin = fmom * (m_lmin - lStart) + m_kfmin;
776 if ((momlMin - m_kfmin) * (momlMin - m_kfmax) <= 0) {
777 // hlmin and klmin
778 double hlmin = fh * (m_lmin - lStart) + hStart;
779 double klmin = fk * (m_lmin - lStart) + kStart;
780 if ((hlmin >= m_hmin) && (hlmin <= m_hmax) && (klmin >= m_kmin) && (klmin <= m_kmax)) {
781 intersections.push_back({{hlmin, klmin, m_lmin, momlMin}});
782 }
783 }
784 double momlMax = fmom * (m_lmax - lStart) + m_kfmin;
785 if ((momlMax - m_kfmin) * (momlMax - m_kfmax) < 0) {
786 // hlmax and klmax
787 double hlmax = fh * (m_lmax - lStart) + hStart;
788 double klmax = fk * (m_lmax - lStart) + kStart;
789 if ((hlmax >= m_hmin) && (hlmax <= m_hmax) && (klmax >= m_kmin) && (klmax <= m_kmax)) {
790 intersections.push_back({{hlmax, klmax, m_lmax, momlMax}});
791 }
792 }
793 }
794
795 // intersections with dE
796 if (!m_dEIntegrated) {
797 for (size_t i = 0; i < eNBins; i++) {
798 double kfi = m_eX[i];
799 if ((kfi - m_kfmin) * (kfi - m_kfmax) <= 0) {
800 double h = qin.X() - qout.X() * kfi;
801 double k = qin.Y() - qout.Y() * kfi;
802 double l = qin.Z() - qout.Z() * kfi;
803 if ((h >= m_hmin) && (h <= m_hmax) && (k >= m_kmin) && (k <= m_kmax) && (l >= m_lmin) && (l <= m_lmax)) {
804 intersections.push_back({{h, k, l, kfi}});
805 }
806 }
807 }
808 }
809
810 // endpoints
811 if ((hStart >= m_hmin) && (hStart <= m_hmax) && (kStart >= m_kmin) && (kStart <= m_kmax) && (lStart >= m_lmin) &&
812 (lStart <= m_lmax)) {
813 intersections.push_back({{hStart, kStart, lStart, m_kfmin}});
814 }
815 if ((hEnd >= m_hmin) && (hEnd <= m_hmax) && (kEnd >= m_kmin) && (kEnd <= m_kmax) && (lEnd >= m_lmin) &&
816 (lEnd <= m_lmax)) {
817 intersections.push_back({{hEnd, kEnd, lEnd, m_kfmax}});
818 }
819
820 // sort intersections by final momentum
821 std::stable_sort(intersections.begin(), intersections.end(), compareMomentum);
822}
823
824} // namespace Mantid::MDAlgorithms
#define DECLARE_ALGORITHM(classname)
Definition Algorithm.h:542
gsl_vector * tmp
const std::vector< double > & rhs
double value
The value of the point.
Definition FitMW.cpp:51
#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.
void declareProperty(std::unique_ptr< Kernel::Property > p, const std::string &doc="") override
Add a property to the list of managed properties.
TypedValue getProperty(const std::string &name) const override
Get the value of a property.
virtual std::shared_ptr< Algorithm > createChildAlgorithm(const std::string &name, const double startProgress=-1., const double endProgress=-1., const bool enableLogging=true, const int &version=-1)
Create a Child Algorithm.
Kernel::Logger & g_log
Definition Algorithm.h:423
const std::vector< Kernel::Property * > & getProperties() const override
Get the list of managed properties.
A validator which provides a TENTATIVE check that a workspace contains common bins in each spectrum.
A validator which checks that a workspace has a valid instrument.
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.
This class stores information regarding an experimental run as a series of log entries.
Definition Run.h:35
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.
A property class for workspaces.
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.
Exception for errors associated with the instrument definition.
Definition Exception.h:220
IPropertyManager * setProperty(const std::string &name, const T &value)
Templated method to set the value of a PropertyWithValue.
void warning(const std::string &msg)
Logs at warning level.
Definition Logger.cpp:117
Numerical Matrix class.
Definition Matrix.h:42
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
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
MDNormSCD : Generate MD normalization for single crystal diffraction.
DataObjects::MDHistoWorkspace_sptr binInputWS()
Runs the BinMD algorithm on the input to provide the output workspace All slicing algorithm propertie...
Mantid::Kernel::DblMatrix m_rubw
(2*PiRUBW)^-1
static constexpr double CHARGEBINSIZE
API::IMDEventWorkspace_sptr m_inputWS
Input workspace.
void calculateNormInner(const API::SpectrumInfo &spectrumInfo, const double protonCharge, const std::vector< coord_t > &otherValues, const Kernel::Matrix< coord_t > &affineTrans)
double m_Ei
cached values for incident energy and momentum, final momentum min/max
int version() const override
Algorithm's version for identification.
std::vector< std::atomic< signal_t > > m_signalArray
internal array to accumulate signals to avoid copying (serial) each loop
Kernel::V3D m_samplePos
Sample position.
void calculateNormContinuous(const std::vector< coord_t > &otherValues, const Kernel::Matrix< coord_t > &affineTrans, uint16_t expInfoIndex)
Computes the normalization for the input workspace for the case of a continous rotation.
Kernel::V3D m_beamDir
Beam direction.
void calculateNormalization(const std::vector< coord_t > &otherValues, const Kernel::Matrix< coord_t > &affineTrans, uint16_t expInfoIndex)
Computed the normalization for the input workspace.
static constexpr double GONIOBINSTEP
void createNormalizationWS(const DataObjects::MDHistoWorkspace &dataWS)
Create & cached the normalization workspace.
DataObjects::MDHistoWorkspace_sptr m_normWS
Normalization workspace.
bool m_hIntegrated
flag for integrated h,k,l, dE dimensions
Kernel::Matrix< coord_t > findIntergratedDimensions(const std::vector< coord_t > &otherDimValues, bool &skipNormalization)
Checks the normalization workspace against the indices of the original dimensions.
const std::string name() const override
Algorithm's name for use in the GUI and help.
coord_t m_hmin
limits for h,k,l, dE dimensions
static constexpr double STATIONARYANGLIM
std::vector< coord_t > getValuesFromOtherDimensions(bool &skipNormalization, uint16_t expInfoIndex=0) const
Retrieve logged values from non-HKL dimensions.
void calculateIntersections(std::vector< std::array< double, 4 > > &intersections, const double theta, const double phi)
Calculate the points of intersection for the given detector with cuboid surrounding the detector posi...
void cacheInputs()
Set up starting values for cached variables.
std::unique_ptr< API::Progress > m_progress
Progress bar.
void init() override
Initialize the algorithm's properties.
const std::string summary() const override
Algorithm's summary for use in the GUI and help.
uint16_t m_numExptInfos
number of experiment infos
std::vector< double > m_hX
cached X values along dimensions h,k,l. dE
const std::string category() const override
Algorithm's category for identification.
size_t m_hIdx
index of h,k,l, dE dimensions in the output workspaces
std::string convention
ki-kf for Inelastic convention; kf-ki for Crystallography convention
void cacheDimensionXValues()
Stores the X values from each H,K,L,E dimension as member variables Energy dimension is transformed t...
static constexpr double MINPROTONCHARGE
std::string inputEnergyMode() const
Currently looks for the ConvertToMD algorithm in the history.
void exec() override
Execute the algorithm.
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)
@ NoNormalization
Don't normalize = return raw counts.
Definition IMDIterator.h:27
std::shared_ptr< MDHistoWorkspace > MDHistoWorkspace_sptr
A shared pointer to a MDHistoWorkspace.
std::string toString(const T &value)
Convert a number to a string.
Definition Strings.cpp:734
MANTID_KERNEL_DLL V3D normalize(V3D v)
Normalizes a V3D.
Definition V3D.h:352
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...
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.
Describes the direction (within an algorithm) of a Property.
Definition Property.h:50
@ Input
An input workspace.
Definition Property.h:53
@ Output
An output workspace.
Definition Property.h:54