34#include <boost/lexical_cast.hpp>
51static bool abs_compare(
double a,
double b) {
return (std::fabs(a) < std::fabs(b)); }
55 if (
workspace.getNumExperimentInfo() == 0)
57 const std::string instrument =
workspace.getExperimentInfo(0)->getInstrumentName();
58 return instrument ==
"WAND" || instrument ==
"HB2C" || instrument ==
"HB3A" || instrument ==
"DEMAND";
82 return "Bins multidimensional data and calculate the normalization on the "
92 "An input MDEventWorkspace. Must be in Q_sample frame.");
96 "An (optional) input MDEventWorkspace for background. Must be in Q_lab frame.");
99 declareProperty(
"RLU",
true,
"Use reciprocal lattice units. If false, use Q_sample");
102 auto mustBe3D = std::make_shared<Kernel::ArrayLengthValidator<double>>(3);
103 std::vector<double> Q0(3, 0.), Q1(3, 0), Q2(3, 0);
109 "The first Q projection axis - Default is (1,0,0)");
114 "The second Q projection axis - Default is (0,1,0)");
119 "The thirdtCalculateCover Q projection axis - Default is (0,0,1)");
124 auto fluxValidator = std::make_shared<CompositeValidator>();
127 auto solidAngleValidator = fluxValidator->
clone();
130 "An input workspace containing integrated vanadium "
131 "(a measure of the solid angle).\n"
132 "Mandatory for diffraction, optional for direct geometry inelastic");
135 "An input workspace containing momentum dependent flux.\n"
136 "Mandatory for diffraction. No effect on direct geometry inelastic");
143 "An (optional) input MDEventWorkspace containing a pre-computed normalization "
144 "for monochromatic single crystal diffraction (e.g. produced by "
145 "ConvertHFIRSCDtoMDE). Must be in Q_sample frame with the same number of "
146 "dimensions as InputWorkspace. Cannot be used together with "
147 "SolidAngleWorkspace/FluxWorkspace or BackgroundWorkspace.");
151 for (std::size_t i = 0; i < 6; i++) {
154 std::string defaultName =
"";
160 auto atMost3 = std::make_shared<ArrayLengthValidator<double>>(0, 3);
161 std::vector<double> temp;
164 "- Leave blank for complete integration\n" +
165 "- One value is interpreted as step\n"
166 "- Two values are interpreted integration interval\n" +
167 "- Three values are interpreted as min, step, max");
174 "If specified the symmetry will be applied, "
175 "can be space group name, point group name, or list "
176 "individual symmetries.");
181 "An (optional) input MDHistoWorkspace used to accumulate data from "
182 "multiple MDEventWorkspaces. If unspecified a blank "
183 "MDHistoWorkspace will be created.");
186 "An (optional) input MDHistoWorkspace used to accumulate normalization "
187 "from multiple MDEventWorkspaces. If unspecified a blank "
188 "MDHistoWorkspace will be created.");
193 "An (optional) input MDHistoWorkspace used to accumulate background from "
194 "multiple background MDEventWorkspaces. If unspecified but "
195 "BackgroundWorkspace is specified, a blank "
196 "MDHistoWorkspace will be created.");
199 "An (optional) input MDHistoWorkspace used to accumulate background normalization "
200 "from multiple background MDEventWorkspaces. If unspecified but "
201 "BackgroundWorkspace is specified, a blank "
202 "MDHistoWorkspace will be created.");
205 setPropertyGroup(
"TemporaryNormalizationWorkspace",
"Temporary workspaces");
206 setPropertyGroup(
"TemporaryBackgroundDataWorkspace",
"Temporary workspaces");
207 setPropertyGroup(
"TemporaryBackgroundNormalizationWorkspace",
"Temporary workspaces");
210 "A name for the normalized output MDHistoWorkspace.");
213 "A name for the output data MDHistoWorkspace.");
215 "A name for the output normalization MDHistoWorkspace.");
218 "A name for the optional output background data MDHistoWorkspace.");
221 "A name for the optional output background normalization MDHistoWorkspace.");
223 m_progress = std::make_unique<API::Progress>(
this, 0, 1, 1);
229 std::map<std::string, std::string> errorMessage;
233 if (inputWS->getNumDims() < 3) {
234 errorMessage.emplace(
"InputWorkspace",
"The input workspace must be at least 3D");
236 for (
size_t i = 0; i < 3; i++) {
238 errorMessage.emplace(
"InputWorkspace",
"The input workspace must be in Q_sample");
246 if (bkgdWS->getNumDims() < 3) {
248 errorMessage.emplace(
"BackgroundWorkspace",
"The input background workspace must be at least 3D");
251 for (
size_t i = 0; i < 3; i++) {
253 errorMessage.emplace(
"BackgroundWorkspace",
"The input backgound workspace must be in Q_lab");
258 if (inputWS->getNumDims() > 3) {
259 if (bkgdWS->getNumDims() <= 3) {
260 errorMessage.emplace(
"BackgroundWorkspace",
"The input background workspace must have at 4 dimensions when "
261 "input workspace has more than 4 dimensions (inelastic case).");
262 }
else if (bkgdWS->getDimension(3)->getName() != inputWS->getDimension(3)->getName()) {
263 errorMessage.emplace(
"BackgroundWorkspace",
"The input background workspace 4th dimension must be DeltaE "
264 "for inelastic case.");
271 bool diffraction =
true;
272 if ((inputWS->getNumDims() > 3) && (inputWS->getDimension(3)->getName() ==
"DeltaE")) {
279 bool monochromatic = bool(monoNormWS);
285 if (bkgdWS && (monochromatic || isMonochromaticSCDInstrument(*inputWS))) {
286 errorMessage[
"BackgroundWorkspace"] =
"MDNorm does not support a background for monochromatic single crystal "
287 "diffraction. Subtract the background in detector space with "
288 "HFIRSCDSubtractBackground before ConvertHFIRSCDtoMDE.";
293 errorMessage.emplace(
"MonoSCDNormalizationWorkspace",
"MonoSCDNormalizationWorkspace can only be used for "
294 "diffraction (InputWorkspace must not have a DeltaE "
297 if (solidAngleWS || fluxWS) {
298 errorMessage.emplace(
"MonoSCDNormalizationWorkspace",
"MonoSCDNormalizationWorkspace cannot be used together "
299 "with SolidAngleWorkspace/FluxWorkspace");
301 if (monoNormWS->getNumDims() < 3) {
302 errorMessage.emplace(
"MonoSCDNormalizationWorkspace",
"MonoSCDNormalizationWorkspace must be at least 3D");
304 if (monoNormWS->getNumDims() != inputWS->getNumDims()) {
305 errorMessage.emplace(
"MonoSCDNormalizationWorkspace",
"MonoSCDNormalizationWorkspace must have the same "
306 "number of dimensions as InputWorkspace");
308 for (
size_t i = 0; i < 3; i++) {
310 errorMessage.emplace(
"MonoSCDNormalizationWorkspace",
"MonoSCDNormalizationWorkspace must be in Q_sample");
314 }
else if (diffraction) {
315 if (solidAngleWS ==
nullptr) {
316 errorMessage.emplace(
"SolidAngleWorkspace",
"SolidAngleWorkspace is required for diffraction");
318 if (fluxWS ==
nullptr) {
319 errorMessage.emplace(
"FluxWorkspace",
"FluxWorkspace is required for diffraction");
324 size_t nExperimentInfos = inputWS->getNumExperimentInfo();
325 if (nExperimentInfos == 0) {
326 errorMessage.emplace(
"InputWorkspace",
"There must be at least one experiment info");
328 for (
size_t iExpInfo = 0; iExpInfo < nExperimentInfos; iExpInfo++) {
329 auto ¤tExptInfo = *(inputWS->getExperimentInfo(
static_cast<uint16_t
>(iExpInfo)));
331 if (!currentExptInfo.run().hasProperty(
"wavelength")) {
332 errorMessage.emplace(
"InputWorkspace",
"Missing wavelength log. InputWorkspace does not look like it was "
333 "produced by ConvertHFIRSCDtoMDE, as expected when "
334 "MonoSCDNormalizationWorkspace is provided");
337 if (!currentExptInfo.run().hasProperty(
"MDNorm_low")) {
338 errorMessage.emplace(
"InputWorkspace",
"Missing MDNorm_low log. Please "
339 "use CropWorkspaceForMDNorm "
340 "before converting to MD");
342 if (!currentExptInfo.run().hasProperty(
"MDNorm_high")) {
343 errorMessage.emplace(
"InputWorkspace",
"Missing MDNorm_high log. Please use "
344 "CropWorkspaceForMDNorm before converting to MD");
352 std::vector<double> Q0Basis =
getProperty(
"QDimension0");
353 std::vector<double> Q1Basis =
getProperty(
"QDimension1");
354 std::vector<double> Q2Basis =
getProperty(
"QDimension2");
359 errorMessage.emplace(
"QDimension0",
"The projection dimensions are coplanar or zero");
360 errorMessage.emplace(
"QDimension1",
"The projection dimensions are coplanar or zero");
361 errorMessage.emplace(
"QDimension2",
"The projection dimensions are coplanar or zero");
363 if (!inputWS->getExperimentInfo(0)->sample().hasOrientedLattice()) {
364 errorMessage.emplace(
"InputWorkspace",
"There is no oriented lattice "
365 "associated with the input workspace. "
366 "Use SetUB algorithm");
370 std::vector<std::string> originalDimensionNames;
371 for (
size_t i = 3; i < inputWS->getNumDims(); i++) {
372 originalDimensionNames.emplace_back(inputWS->getDimension(i)->getName());
374 originalDimensionNames.emplace_back(
"QDimension0");
375 originalDimensionNames.emplace_back(
"QDimension1");
376 originalDimensionNames.emplace_back(
"QDimension2");
377 std::vector<std::string> selectedDimensions;
378 for (std::size_t i = 0; i < 6; i++) {
382 std::vector<double> binning =
getProperty(binningName);
383 if (!dimName.empty()) {
384 auto it = std::find(originalDimensionNames.begin(), originalDimensionNames.end(), dimName);
385 if (it == originalDimensionNames.end()) {
386 errorMessage.emplace(propName,
"Name '" + dimName +
387 "' is not one of the "
388 "original workspace names or a directional dimension");
391 auto itSel = std::find(selectedDimensions.begin(), selectedDimensions.end(), dimName);
392 if (itSel == selectedDimensions.end()) {
393 selectedDimensions.emplace_back(dimName);
395 errorMessage.emplace(propName,
"Name '" + dimName +
"' was already selected");
399 if (!binning.empty()) {
400 errorMessage.emplace(binningName,
"There should be no binning if the dimension name is empty");
405 if ((std::find(selectedDimensions.begin(), selectedDimensions.end(),
"QDimension0") == selectedDimensions.end()) ||
406 (std::find(selectedDimensions.begin(), selectedDimensions.end(),
"QDimension1") == selectedDimensions.end()) ||
407 (std::find(selectedDimensions.begin(), selectedDimensions.end(),
"QDimension2") == selectedDimensions.end())) {
408 for (std::size_t i = 0; i < 6; i++) {
410 errorMessage.emplace(propName,
"All of QDimension0, QDimension1, QDimension2 must be present");
414 std::string symOps = this->
getProperty(
"SymmetryOperations");
415 if (!symOps.empty()) {
416 bool isSpaceGroup = Geometry::SpaceGroupFactory::Instance().isSubscribed(symOps);
417 bool isPointGroup = Geometry::PointGroupFactory::Instance().isSubscribed(symOps);
418 if (!isSpaceGroup && !isPointGroup) {
420 Geometry::SymmetryOperationFactory::Instance().createSymOps(symOps);
422 errorMessage.emplace(
"SymmetryOperations",
"The input is not a space group, a point group, "
423 "or a list of symmetry operations");
428 std::shared_ptr<IMDHistoWorkspace> tempNormWS = this->
getProperty(
"TemporaryNormalizationWorkspace");
432 if ((tempNormWS && !tempDataWS) || (!tempNormWS && tempDataWS)) {
433 errorMessage.emplace(
"TemporaryDataWorkspace",
"Must provide either no accumulation workspaces or,"
434 "both TemporaryNormalizationWorkspaces and TemporaryDataWorkspace");
437 if (tempNormWS && tempDataWS) {
438 size_t numNormDims = tempNormWS->getNumDims();
439 size_t numDataDims = tempDataWS->getNumDims();
440 if (numNormDims == numDataDims) {
441 for (
size_t i = 0; i < numNormDims; i++) {
442 const auto dim1 = tempNormWS->getDimension(i);
443 const auto dim2 = tempDataWS->getDimension(i);
444 if ((dim1->getMinimum() != dim2->getMinimum()) || (dim1->getMaximum() != dim2->getMaximum()) ||
445 (dim1->getNBins() != dim2->getNBins()) || (dim1->getName() != dim2->getName())) {
446 errorMessage.emplace(
"TemporaryDataWorkspace",
"Binning for TemporaryNormalizationWorkspaces "
447 "and TemporaryDataWorkspace must be the same.");
452 errorMessage.emplace(
"TemporaryDataWorkspace",
"TemporaryNormalizationWorkspace and TemporaryDataWorkspace "
453 "do not have the same number of dimensions");
462 if (tempBkgdDataWS && (!bkgdWS || !tempDataWS || !tempBkgdNormWS)) {
463 errorMessage.emplace(
"TemporaryBackgroundDataWorkspace",
"TemporaryBackgroundDataWorkspace is specified but at "
464 "least one of these is not.");
465 }
else if (tempBkgdNormWS && (!bkgdWS || !tempNormWS || !tempBkgdDataWS)) {
466 errorMessage.emplace(
"TemporaryBackgroundNormalizationWorkspace",
"TemporaryBackgroundNormalizationWorkspace is "
467 "specified but at least one of these is not.");
468 }
else if (bkgdWS && tempDataWS && !tempBkgdDataWS) {
469 errorMessage.emplace(
"TemporaryDataWorkspace",
470 "With Background is specifed and TemporaryDataWorkspace is specifed, "
471 "TemporaryBackgroundDataWorkspace must be specified.");
472 }
else if (tempBkgdDataWS && tempNormWS) {
474 size_t numBkgdDataDims = tempBkgdDataWS->getNumDims();
475 size_t numBkgdNormDims = tempBkgdNormWS->getNumDims();
476 size_t numDataDims = tempDataWS->getNumDims();
477 if (numBkgdDataDims == numBkgdNormDims && numBkgdDataDims == numDataDims) {
479 for (
size_t idim = 0; idim < numBkgdDataDims; ++idim) {
480 const auto dimB = tempBkgdDataWS->getDimension(idim);
481 const auto dimN = tempBkgdNormWS->getDimension(idim);
482 const auto dimD = tempDataWS->getDimension(idim);
483 if ((dimB->getMinimum() != dimN->getMinimum()) || (dimB->getMinimum() != dimD->getMinimum()) ||
484 (dimB->getMaximum() != dimN->getMaximum()) || (dimB->getMaximum() != dimD->getMaximum()) ||
485 (dimB->getNBins() != dimN->getNBins()) || (dimB->getNBins() != dimD->getNBins()) ||
486 (dimB->getName() != dimN->getName()) || (dimB->getName() != dimD->getName())) {
487 errorMessage.emplace(
"TemporaryBackgroundDataWorkspace",
488 "TemporaryBackgroundDataWorkspace, "
489 "TemporaryBackgroundNormalizationWorkspace and "
490 "TemporaryDataWorkspace "
491 "must have same minimum, maximum, number of bins and name.");
496 errorMessage.emplace(
"TemporaryBackgroundDataWorkspace",
"TemporaryBackgroundDataWorkspace, "
497 "TemporaryBackgroundNormalizationWorkspace and "
498 "TemporaryDataWorkspace must have same dimensions");
509 m_convention = Kernel::ConfigService::Instance().getString(
"Q.convention");
512 std::string symOps = this->
getProperty(
"SymmetryOperations");
513 std::vector<Geometry::SymmetryOperation> symmetryOps;
514 if (symOps.empty()) {
517 if (Geometry::SpaceGroupFactory::Instance().isSubscribed(symOps)) {
518 auto spaceGroup = Geometry::SpaceGroupFactory::Instance().createSpaceGroup(symOps);
519 auto pointGroup = spaceGroup->getPointGroup();
520 symmetryOps = pointGroup->getSymmetryOperations();
521 }
else if (Geometry::PointGroupFactory::Instance().isSubscribed(symOps)) {
522 auto pointGroup = Geometry::PointGroupFactory::Instance().createPointGroup(symOps);
523 symmetryOps = pointGroup->getSymmetryOperations();
525 symmetryOps = Geometry::SymmetryOperationFactory::Instance().createSymOps(symOps);
528 for (
const auto &so : symmetryOps) {
538 const auto &exptInfoZero = *(
m_inputWS->getExperimentInfo(0));
539 auto source = exptInfoZero.getInstrument()->getSource();
540 auto sample = exptInfoZero.getInstrument()->getSample();
541 if (source ==
nullptr || sample ==
nullptr) {
543 "Instrument not sufficiently defined: failed to get source and/or "
548 if ((
m_inputWS->getNumDims() > 3) && (
m_inputWS->getDimension(3)->getName() ==
"DeltaE")) {
551 if (exptInfoZero.run().hasProperty(
"Ei")) {
553 m_Ei = boost::lexical_cast<double>(eiprop->
value());
555 throw std::invalid_argument(
"Ei stored in the workspace is not positive");
558 throw std::invalid_argument(
"Could not find Ei value in the workspace.");
574 this->
setProperty(
"OutputDataWorkspace", outputDataWS);
584 this->
setProperty(
"OutputBackgroundDataWorkspace", outputBackgroundDataWS);
595 for (uint16_t expInfoIndex = 0; expInfoIndex <
m_numExptInfos; expInfoIndex++) {
596 const auto ¤tExptInfo = *(
m_inputWS->getExperimentInfo(expInfoIndex));
599 bool skipNormalization =
false;
604 if (!skipNormalization) {
605 for (
const auto &so : symmetryOps) {
606 if (currentExptInfo.run().hasProperty(
"useLogTimes")) {
614 g_log.
warning(
"Binning limits are outside the limits of the MDWorkspace. "
615 "Not applying normalization.");
621 [](
const std::atomic<signal_t> &a,
const signal_t &b) { return a + b; });
625 [](
const std::atomic<signal_t> &a,
const signal_t &b) { return a + b; });
640 const std::string normedBkgdWSName(
"_normedBkgd");
646 minusMD->setProperty(
"LHSWorkspace", out);
647 minusMD->setProperty(
"RHSWorkspace", outbkgd);
648 minusMD->setPropertyValue(
"OutputWorkspace",
getPropertyValue(
"OutputWorkspace"));
650 minusMD->executeAsChildAlg();
651 out = minusMD->getProperty(
"OutputWorkspace");
664 const double &startProgress,
const double &endProgress) {
666 divideMD->setProperty(
"LHSWorkspace", lhs);
668 divideMD->setPropertyValue(
"OutputWorkspace", outputwsname);
683 return std::string(
"Q_sample_x");
685 return std::string(
"Q_sample_y");
687 return std::string(
"Q_sample_z");
689 throw std::invalid_argument(
"Index must be 0, 1, or 2 for QDimensionNameQSample");
698 std::vector<double>::iterator result;
699 result = std::max_element(projection.begin(), projection.end(), abs_compare);
700 std::vector<char> symbol{
'H',
'K',
'L'};
701 char character = symbol[std::distance(projection.begin(), result)];
702 std::stringstream
name;
704 for (
size_t i = 0; i < 3; i++) {
705 if (projection[i] == 0) {
707 }
else if (projection[i] == 1) {
709 }
else if (projection[i] == -1) {
710 name <<
"-" << character;
712 name << std::defaultfloat << std::setprecision(3) << projection[i] << character;
727 std::map<std::string, std::string> parameters;
728 std::stringstream extents;
729 std::stringstream bins;
730 std::vector<std::string> originalDimensionNames;
731 originalDimensionNames.emplace_back(
"QDimension0");
732 originalDimensionNames.emplace_back(
"QDimension1");
733 originalDimensionNames.emplace_back(
"QDimension2");
734 for (
size_t i = 3; i <
m_inputWS->getNumDims(); i++) {
735 originalDimensionNames.emplace_back(
m_inputWS->getDimension(i)->getName());
742 m_UB =
m_inputWS->getExperimentInfo(0)->sample().getOrientedLattice().getUB() * 2 * M_PI;
762 auto &exptInfo0 = *(
m_inputWS->getExperimentInfo(
static_cast<uint16_t
>(0)));
763 auto upperLimitsVector =
766 maxQ = 2. * (*std::max_element(upperLimitsVector.begin(), upperLimitsVector.end()));
769 double maxDE = *std::max_element(upperLimitsVector.begin(), upperLimitsVector.end());
770 auto loweLimitsVector =
772 double minDE = *std::min_element(loweLimitsVector.begin(), loweLimitsVector.end());
773 if (exptInfo0.run().hasProperty(
"Ei")) {
775 Ei = boost::lexical_cast<double>(eiprop->
value());
777 throw std::invalid_argument(
"Ei stored in the workspace is not positive");
780 throw std::invalid_argument(
"Could not find Ei value in the workspace.");
782 double ki = std::sqrt(energyToK * Ei);
783 double kfmin = std::sqrt(energyToK * (Ei - minDE));
784 double kfmax = std::sqrt(energyToK * (Ei - maxDE));
786 maxQ = ki + std::max(kfmin, kfmax);
794 auto dataExtents =
m_inputWS->getMinimumExtents(4);
795 double qx = std::max(std::fabs(dataExtents[0].getMin()), std::fabs(dataExtents[0].getMax()));
796 double qy = std::max(std::fabs(dataExtents[1].getMin()), std::fabs(dataExtents[1].getMax()));
797 double qz = std::max(std::fabs(dataExtents[2].getMin()), std::fabs(dataExtents[2].getMax()));
798 maxQ = std::sqrt(qx * qx + qy * qy + qz * qz);
800 size_t basisVectorIndex = 0;
801 std::vector<coord_t> transformation;
802 for (std::size_t i = 0; i < 6; i++) {
806 std::vector<double> binning =
getProperty(binningName);
807 std::string bv =
"BasisVector";
808 if (!dimName.empty()) {
810 std::stringstream propertyValue;
811 propertyValue << dimName;
813 auto dimIndex = std::distance(originalDimensionNames.begin(),
814 std::find(originalDimensionNames.begin(), originalDimensionNames.end(), dimName));
815 auto dimension =
m_inputWS->getDimension(dimIndex);
816 propertyValue <<
"," << dimension->getMDUnits().getUnitLabel().ascii();
817 for (
size_t j = 0; j < originalDimensionNames.size(); j++) {
818 if (j ==
static_cast<size_t>(dimIndex)) {
819 propertyValue <<
",1";
820 transformation.emplace_back(1.f);
822 propertyValue <<
",0";
823 transformation.emplace_back(0.f);
826 parameters.emplace(property, propertyValue.str());
828 coord_t dimMax = dimension->getMaximum();
829 coord_t dimMin = dimension->getMinimum();
834 dimMax =
static_cast<coord_t>(ol.
a() * maxQ);
836 }
else if (dimIndex == 1) {
837 dimMax =
static_cast<coord_t>(ol.
b() * maxQ);
839 }
else if (dimIndex == 2) {
840 dimMax =
static_cast<coord_t>(ol.
c() * maxQ);
844 if (binning.size() == 0) {
846 extents << dimMin <<
"," << dimMax <<
",";
848 }
else if (binning.size() == 2) {
850 extents << binning[0] <<
"," << binning[1] <<
",";
852 }
else if (binning.size() == 1) {
853 auto step = binning[0];
854 double nsteps = (dimMax - dimMin) / step;
855 if (nsteps + 1 - std::ceil(nsteps) >= 1e-4) {
856 nsteps = std::ceil(nsteps);
858 nsteps = std::floor(nsteps);
860 bins << static_cast<int>(nsteps) <<
",";
861 extents << dimMin <<
"," << dimMin + nsteps * step <<
",";
862 }
else if (binning.size() == 3) {
863 dimMin =
static_cast<coord_t>(binning[0]);
864 auto step = binning[1];
865 dimMax =
static_cast<coord_t>(binning[2]);
866 double nsteps = (dimMax - dimMin) / step;
867 if (nsteps + 1 - std::ceil(nsteps) >= 1e-4) {
868 nsteps = std::ceil(nsteps);
870 nsteps = std::floor(nsteps);
872 bins << static_cast<int>(nsteps) <<
",";
873 extents << dimMin <<
"," << dimMin + nsteps * step <<
",";
878 parameters.emplace(
"OutputExtents", extents.str());
879 parameters.emplace(
"OutputBins", bins.str());
881 transformation,
static_cast<size_t>((transformation.size()) /
m_inputWS->getNumDims()),
m_inputWS->getNumDims());
893 std::shared_ptr<IMDHistoWorkspace>
tmp = this->
getProperty(
"TemporaryBackgroundNormalizationWorkspace");
911 const std::string numBinsStr = parameters.at(
"OutputBins");
912 const std::string extentsStr = parameters.at(
"OutputExtents");
917 size_t numDimsTemp = tempDataWS->getNumDims();
918 if ((numBins.size() != numDimsTemp) || (extents.size() != numDimsTemp * 2)) {
919 std::stringstream errorMessage;
920 errorMessage <<
"The number of dimensions in the output and ";
921 errorMessage <<
"TemporaryDataWorkspace are not the same.";
922 throw(std::invalid_argument(errorMessage.str()));
926 for (
size_t i = 0; i < numDimsTemp; i++) {
927 auto ax = tempDataWS->getDimension(i);
928 if (numBins[i] != ax->getNBins()) {
929 std::stringstream errorMessage;
930 errorMessage <<
"The number of bins output and number of bins in ";
931 errorMessage <<
"TemporaryDataWorkspace are not the same along ";
932 errorMessage <<
"dimension " << i;
933 throw(std::invalid_argument(errorMessage.str()));
935 if (std::abs(extents[2 * i] - ax->getMinimum()) > 1.e-5) {
936 std::stringstream errorMessage;
937 errorMessage <<
"The minimum binning value for the output and ";
938 errorMessage <<
"TemporaryDataWorkspace are not the same along ";
939 errorMessage <<
"dimension " << i;
940 throw(std::invalid_argument(errorMessage.str()));
942 if (std::abs(extents[2 * i + 1] - ax->getMaximum()) > 1.e-5) {
943 std::stringstream errorMessage;
944 errorMessage <<
"The maximum binning value for the output and ";
945 errorMessage <<
"TemporaryDataWorkspace are not the same along ";
946 errorMessage <<
"dimension " << i;
947 throw(std::invalid_argument(errorMessage.str()));
952 size_t parametersIndex = 0;
953 std::vector<size_t> dimensionIndex(numDimsTemp + 1, 3);
954 for (
const auto &p : parameters) {
956 auto value = p.second;
959 if (
value.find(
"QDimension0") != std::string::npos) {
960 dimensionIndex[0] = parametersIndex;
961 const std::string dimXName = tempDataWS->getDimension(parametersIndex)->getName();
964 std::stringstream errorMessage;
965 std::stringstream debugMessage;
966 errorMessage <<
"TemporaryDataWorkspace does not have the ";
967 errorMessage <<
"correct name for dimension " << parametersIndex;
969 debugMessage <<
" TemporaryDataWorkspace: " << dimXName;
971 throw(std::invalid_argument(errorMessage.str()));
975 std::stringstream errorMessage;
976 std::stringstream debugMessage;
977 errorMessage <<
"TemporaryDataWorkspace does not have the ";
978 errorMessage <<
"correct name for dimension " << parametersIndex;
980 debugMessage <<
" TemporaryDataWorkspace: " << dimXName;
982 throw(std::invalid_argument(errorMessage.str()));
985 }
else if (
value.find(
"QDimension1") != std::string::npos) {
986 dimensionIndex[1] = parametersIndex;
987 const std::string dimYName = tempDataWS->getDimension(parametersIndex)->getName();
990 std::stringstream errorMessage;
991 std::stringstream debugMessage;
992 errorMessage <<
"TemporaryDataWorkspace does not have the ";
993 errorMessage <<
"correct name for dimension " << parametersIndex;
995 debugMessage <<
" TemporaryDataWorkspace: " << dimYName;
997 throw(std::invalid_argument(errorMessage.str()));
1001 std::stringstream errorMessage;
1002 std::stringstream debugMessage;
1003 errorMessage <<
"TemporaryDataWorkspace does not have the ";
1004 errorMessage <<
"correct name for dimension " << parametersIndex;
1006 debugMessage <<
" TemporaryDataWorkspace: " << dimYName;
1008 throw(std::invalid_argument(errorMessage.str()));
1011 }
else if (
value.find(
"QDimension2") != std::string::npos) {
1012 dimensionIndex[2] = parametersIndex;
1013 const std::string dimZName = tempDataWS->getDimension(parametersIndex)->getName();
1016 std::stringstream errorMessage;
1017 std::stringstream debugMessage;
1018 errorMessage <<
"TemporaryDataWorkspace does not have the ";
1019 errorMessage <<
"correct name for dimension " << parametersIndex;
1021 debugMessage <<
" TemporaryDataWorkspace: " << dimZName;
1023 throw(std::invalid_argument(errorMessage.str()));
1027 std::stringstream errorMessage;
1028 std::stringstream debugMessage;
1029 errorMessage <<
"TemporaryDataWorkspace does not have the ";
1030 errorMessage <<
"correct name for dimension " << parametersIndex;
1032 debugMessage <<
" TemporaryDataWorkspace: " << dimZName;
1034 throw(std::invalid_argument(errorMessage.str()));
1038 }
else if ((key !=
"OutputBins") && (key !=
"OutputExtents")) {
1040 const std::string nameData = tempDataWS->getDimension(parametersIndex)->getName();
1041 if (
value.find(nameData) != 0) {
1043 <<
" from the temporary workspace"
1044 " is not one of the binning dimensions, "
1045 " or dimensions are in the wrong order."
1047 throw(std::invalid_argument(
"Beside the Q dimensions, "
1048 "TemporaryDataWorkspace does not have the "
1049 "same dimension names as OutputWorkspace."));
1054 const auto it = std::find_if(dimensionIndex.cbegin(), dimensionIndex.cend(),
1055 [numDimsTemp](
const auto &idx) { return idx > numDimsTemp; });
1056 if (it != dimensionIndex.cend())
1057 throw(std::invalid_argument(
"Cannot find at least one of QDimension0, "
1058 "QDimension1, or QDimension2"));
1094 std::stringstream &basisVector, std::vector<size_t> &qDimensionIndices) {
1095 if (
value.find(
"QDimension0") != std::string::npos) {
1101 qDimensionIndices.emplace_back(qindex);
1102 projection[0] = Qtransform[0][0];
1103 projection[1] = Qtransform[1][0];
1104 projection[2] = Qtransform[2][0];
1107 }
else if (
value.find(
"QDimension1") != std::string::npos) {
1113 qDimensionIndices.emplace_back(qindex);
1114 projection[0] = Qtransform[0][1];
1115 projection[1] = Qtransform[1][1];
1116 projection[2] = Qtransform[2][1];
1119 }
else if (
value.find(
"QDimension2") != std::string::npos) {
1125 qDimensionIndices.emplace_back(qindex);
1126 projection[0] = Qtransform[0][2];
1127 projection[1] = Qtransform[1][2];
1128 projection[2] = Qtransform[2][2];
1131 }
else if (
value.find(
"DeltaE") != std::string::npos) {
1147 for (
size_t i : qDimensionIndices) {
1148 auto mdHistoDimension = std::const_pointer_cast<Mantid::Geometry::MDHistoDimension>(
1149 std::dynamic_pointer_cast<const Mantid::Geometry::MDHistoDimension>(outputMDHWS->getDimension(i)));
1150 mdHistoDimension->setMDFrame(*hklFrame);
1153 auto ei = outputMDHWS->getExperimentInfo(0);
1154 ei->mutableRun().addProperty(
"W_MATRIX",
m_W.
getVector(),
true);
1172 if (tempBkgdDataWS) {
1177 std::vector<size_t> qDimensionIndices;
1178 uint16_t numexpinfo =
static_cast<uint16_t
>(
m_inputWS->getNumExperimentInfo());
1180 throw std::runtime_error(
"Symmetry operation number m_umSymops is wrong!");
1182 for (uint16_t i_expinfo = 0; i_expinfo < numexpinfo; ++i_expinfo) {
1184 auto rotMatrix =
m_inputWS->getExperimentInfo(i_expinfo)->run().getGoniometerMatrix();
1189 for (
const auto &so : symmetryOps) {
1196 Qtransform = rotMatrix *
m_UB * soMatrix *
m_W;
1198 Qtransform = rotMatrix * soMatrix *
m_W;
1202 double progress_fraction = 1. /
static_cast<double>(symmetryOps.size() * numexpinfo);
1204 createChildAlgorithm(
"BinMD", soIndex * 0.3 * progress_fraction, (soIndex + 1) * 0.3 * progress_fraction);
1206 binMD->setPropertyValue(
"AxisAligned",
"0");
1208 binMD->setProperty(
"TemporaryDataWorkspace", tempBkgdDataWS);
1209 binMD->setPropertyValue(
"NormalizeBasisVectors",
"0");
1212 binMD->setPropertyValue(
"OutputWorkspace",
getPropertyValue(
"OutputBackgroundDataWorkspace"));
1215 for (
const auto &p : parameters) {
1217 auto value = p.second;
1218 std::stringstream basisVector;
1219 std::vector<double> projection(
m_inputWS->getNumDims(), 0.);
1224 if (!basisVector.str().empty()) {
1226 for (
auto proji : projection) {
1227 proji = std::abs(proji) > 1e-10 ? proji : 0.0;
1228 basisVector <<
"," << proji;
1230 value = basisVector.str();
1233 binMD->setPropertyValue(key,
value);
1237 binMD->executeAsChildAlg();
1242 outputWS = binMD->getProperty(
"OutputWorkspace");
1243 tempBkgdDataWS = std::dynamic_pointer_cast<MDHistoWorkspace>(outputWS);
1244 tempBkgdDataWS->clearOriginalWorkspaces();
1245 tempBkgdDataWS->clearTransforms();
1248 auto outputMDHWS = std::dynamic_pointer_cast<MDHistoWorkspace>(outputWS);
1251 setQUnit(qDimensionIndices, outputMDHWS);
1279 return binMDEventWorkspace(monoNormInputWS,
"TemporaryNormalizationWorkspace",
"OutputNormalizationWorkspace",
1280 symmetryOps, parameters);
1299 const std::string &outputWSPropertyName,
1300 const std::vector<Geometry::SymmetryOperation> &symmetryOps,
1301 const std::map<std::string, std::string> ¶meters) {
1310 std::vector<size_t> qDimensionIndices;
1311 for (
const auto &so : symmetryOps) {
1317 Qtransform =
m_UB * soMatrix *
m_W;
1319 Qtransform = soMatrix *
m_W;
1323 double fraction = 1. /
static_cast<double>(symmetryOps.size());
1324 auto binMD =
createChildAlgorithm(
"BinMD", soIndex * 0.3 * fraction, (soIndex + 1) * 0.3 * fraction);
1325 binMD->setPropertyValue(
"AxisAligned",
"0");
1326 binMD->setProperty(
"InputWorkspace", ws);
1327 binMD->setProperty(
"TemporaryDataWorkspace", tempWS);
1328 binMD->setPropertyValue(
"NormalizeBasisVectors",
"0");
1329 binMD->setPropertyValue(
"OutputWorkspace",
getPropertyValue(outputWSPropertyName));
1332 for (
const auto &p : parameters) {
1333 auto value = p.second;
1334 std::stringstream basisVector;
1335 std::vector<double> projection(ws->getNumDims(), 0.);
1340 if (!basisVector.str().empty()) {
1342 for (
auto proji : projection) {
1343 proji = std::abs(proji) > 1e-10 ? proji : 0.0;
1344 basisVector <<
"," << proji;
1346 value = basisVector.str();
1349 binMD->setPropertyValue(p.first,
value);
1353 binMD->executeAsChildAlg();
1354 outputWS = binMD->getProperty(
"OutputWorkspace");
1358 tempWS = std::dynamic_pointer_cast<MDHistoWorkspace>(outputWS);
1359 tempWS->clearOriginalWorkspaces();
1360 tempWS->clearTransforms();
1364 auto outputMDHWS = std::dynamic_pointer_cast<MDHistoWorkspace>(outputWS);
1367 setQUnit(qDimensionIndices, outputMDHWS);
1383 const auto ¤tRun =
m_inputWS->getExperimentInfo(expInfoIndex)->run();
1385 std::vector<coord_t> otherDimValues;
1386 for (
size_t i = 3; i <
m_inputWS->getNumDims(); i++) {
1387 const auto dimension =
m_inputWS->getDimension(i);
1388 auto inputDimMin =
static_cast<float>(dimension->getMinimum());
1389 auto inputDimMax =
static_cast<float>(dimension->getMaximum());
1390 coord_t outputDimMin(0), outputDimMax(0);
1391 bool isIntegrated =
true;
1395 isIntegrated =
false;
1396 outputDimMin =
m_normWS->getDimension(j)->getMinimum();
1397 outputDimMax =
m_normWS->getDimension(j)->getMaximum();
1400 if (dimension->getName() ==
"DeltaE") {
1401 if ((inputDimMax < outputDimMin) || (inputDimMin > outputDimMax)) {
1402 skipNormalization =
true;
1407 otherDimValues.emplace_back(
value);
1408 if (value < inputDimMin || value > inputDimMax) {
1409 skipNormalization =
true;
1411 if ((!isIntegrated) && (value < outputDimMin || value > outputDimMax)) {
1412 skipNormalization =
true;
1416 return otherDimValues;
#define DECLARE_ALGORITHM(classname)
const std::vector< double > & rhs
double value
The value of the point.
IPeaksWorkspace_sptr workspace
void declareProperty(std::unique_ptr< Kernel::Property > p, const std::string &doc="") override
Add a property to the list of managed properties.
std::string getPropertyValue(const std::string &name) const override
Get the value of a property as a string.
TypedValue getProperty(const std::string &name) const override
Get the value of a property.
virtual std::shared_ptr< Algorithm > createChildAlgorithm(const std::string &name, const double startProgress=-1., const double endProgress=-1., const bool enableLogging=true, const int &version=-1)
Create a Child Algorithm.
A validator which provides a TENTATIVE check that a workspace contains common bins in each spectrum.
Kernel::IValidator_sptr clone() const override
Clone the current state.
Abstract base class for multi-dimension event workspaces (MDEventWorkspace).
A validator which checks that a workspace has a valid instrument.
A property class for workspaces.
std::unique_ptr< MDHistoWorkspace > clone() const
Returns a clone of the workspace.
static const std::string HKLName
Input argument type for MDFrameFactory chainable factory.
Class to implement UB matrix.
void setUB(const Kernel::DblMatrix &newUB)
Sets the UB matrix and recalculates lattice parameters.
static const std::string QLabName
static const std::string QSampleName
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.
double a(int nd) const
Get lattice parameter a1-a3 as function of index (0-2)
double c() const
Get lattice parameter.
double b() const
Get lattice parameter.
Support for a property that holds an array of values.
Exception for errors associated with the instrument definition.
Records the filename, the description of failure and the line on which it happened.
IPropertyManager * setProperty(const std::string &name, const T &value)
Templated method to set the value of a PropertyWithValue.
void setPropertySettings(const std::string &name, std::unique_ptr< IPropertySettings const > settings)
Add a PropertySettings instance to the chain of settings for a given property.
void setPropertyGroup(const std::string &name, const std::string &group)
Set the group for a given property.
void debug(const std::string &msg)
Logs at debug level.
void error(const std::string &msg)
Logs at error level.
void warning(const std::string &msg)
Logs at warning level.
T determinant() const
Calculate the determinant.
T Invert()
LU inversion routine.
std::vector< T > getVector() const
size_t numRows() const
Return the number of rows in the matrix.
void setColumn(const size_t nCol, const std::vector< T > &newCol)
Matrix< T > & Transpose()
Transpose the matrix.
The concrete, templated class for properties.
Base class for properties.
virtual std::string value() const =0
Returns the value of the property as a string.
static const UnitLabel RLU
Reciprocal lattice units.
std::vector< std::atomic< signal_t > > m_bkgdSignalArray
API::IMDEventWorkspace_sptr m_inputWS
Input workspace.
bool m_hIntegrated
flag for integrated h,k,l, dE dimensions
void createNormalizationWS(const DataObjects::MDHistoWorkspace &dataWS)
Create & cached the normalization workspace.
bool m_diffraction
Flag indicating if the input workspace is from diffraction.
API::IMDEventWorkspace_sptr m_backgroundWS
Input background workspace.
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.
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.
Kernel::V3D m_samplePos
Sample position.
std::vector< std::atomic< signal_t > > m_signalArray
internal array to accumulate signals to avoid copying (serial) each loop
bool m_accumulate
Flag to accumulate normalization.
DataObjects::MDHistoWorkspace_sptr binInputWS()
Runs the BinMD algorithm on the input to provide the output workspace All slicing algorithm propertie...
uint16_t m_numExptInfos
number of experiment infos
DataObjects::MDHistoWorkspace_sptr m_bkgdNormWS
Mantid::Kernel::Matrix< coord_t > m_transformation
matrix for transforming from intersections to positions in the normalization workspace
Kernel::V3D m_beamDir
Beam direction.
Mantid::Kernel::DblMatrix m_UB
UB matrix.
size_t m_hIdx
index of h,k,l, dE dimensions in the output workspaces
std::unique_ptr< API::Progress > m_progress
Progress bar.
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.
std::string m_convention
ki-kf for Inelastic convention; kf-ki for Crystallography convention
double m_Ei
cached values for incident energy and momentum, final momentum min/max
MDNormalization : Bin single crystal diffraction or direct geometry inelastic data and calculate the ...
size_t m_numSymmOps
number of symmetry operations
bool m_monochromatic
Flag indicating a pre-computed MonoSCDNormalizationWorkspace was provided (monochromatic single cryst...
DataObjects::MDHistoWorkspace_sptr binBackgroundWS(const std::vector< Geometry::SymmetryOperation > &symmetryOps)
Bin(MD) input Background workspace.
std::string QDimensionName(std::vector< double > projection)
Get the dimension name when using reciprocal lattice units.
void exec() override
Execute the algorithm.
std::vector< double > m_Q0Basis
The projection vectors.
const std::string summary() const override
Algorithm's summary for use in the GUI and help.
std::map< std::string, std::string > getBinParameters()
Calculate binning parameters.
API::IMDWorkspace_sptr divideMD(const API::IMDHistoWorkspace_sptr &lhs, const API::IMDHistoWorkspace_sptr &rhs, const std::string &outputwsname, const double &startProgress, const double &endProgress)
const std::string category() const override
Algorithm's category for identification.
std::vector< coord_t > getValuesFromOtherDimensions(bool &skipNormalization, uint16_t expInfoIndex=0) const
Retrieve logged values from non-HKL dimensions.
void setQUnit(const std::vector< size_t > &qDimensionIndices, const Mantid::DataObjects::MDHistoWorkspace_sptr &outputMDHWS)
Set the output Frame to HKL.
std::vector< double > m_Q1Basis
void validateBinningForTemporaryDataWorkspace(const std::map< std::string, std::string > &, const Mantid::API::IMDHistoWorkspace_sptr &)
Validates the TemporaryDataWorkspace has the same binning as the input binning parameters.
Mantid::Kernel::DblMatrix buildSymmetryMatrix(const Geometry::SymmetryOperation &so)
build symmetry matrix
DataObjects::MDHistoWorkspace_sptr binMonoSCDNormalizationWS(const std::vector< Geometry::SymmetryOperation > &symmetryOps)
Bin(MD) MonoSCDNormalizationWorkspace (monochromatic single crystal diffraction)
std::string QDimensionNameQSample(int i)
Get the dimension name when not using reciprocal lattice units.
const std::string name() const override
Algorithms name for identification.
std::vector< double > m_Q2Basis
bool m_isRLU
flag for reciprocal lattice units
DataObjects::MDHistoWorkspace_sptr binMDEventWorkspace(const API::IMDEventWorkspace_sptr &ws, const std::string &temporaryWSPropertyName, const std::string &outputWSPropertyName, const std::vector< Geometry::SymmetryOperation > &symmetryOps, const std::map< std::string, std::string > ¶meters)
Bin(MD), per symmetry operation, an MDEventWorkspace using pre-computed bin parameters.
std::map< std::string, std::string > validateInputs() override final
Validate the input workspace.
void determineBasisVector(const size_t &qindex, const std::string &value, const Kernel::DblMatrix &Qtransform, std::vector< double > &projection, std::stringstream &basisVector, std::vector< size_t > &qDimensionIndices)
MDNorm::determineBasisVector.
int version() const override
Algorithm's version for identification.
void init() override
Initialize the algorithm's properties.
void createBackgroundNormalizationWS(const DataObjects::MDHistoWorkspace &dataWS)
std::shared_ptr< IMDEventWorkspace > IMDEventWorkspace_sptr
Shared pointer to Mantid::API::IMDEventWorkspace.
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< IMDHistoWorkspace > IMDHistoWorkspace_sptr
shared pointer to Mantid::API::IMDHistoWorkspace
std::shared_ptr< IMDWorkspace > IMDWorkspace_sptr
Shared pointer to the IMDWorkspace base class.
@ NoNormalization
Don't normalize = return raw counts.
std::shared_ptr< MDHistoWorkspace > MDHistoWorkspace_sptr
A shared pointer to a MDHistoWorkspace.
std::unique_ptr< MDFrame > MDFrame_uptr
MDFrameFactory_uptr MANTID_GEOMETRY_DLL makeMDFrameFactoryChain()
Make a complete factory chain.
std::string toString(const T &value)
Convert a number to a string.
template MANTID_KERNEL_DLL std::vector< double > splitStringIntoVector< double >(std::string listString, const std::string &separator)
template MANTID_KERNEL_DLL std::vector< size_t > splitStringIntoVector< size_t >(std::string listString, const std::string &separator)
MANTID_KERNEL_DLL V3D normalize(V3D v)
Normalizes a V3D.
Mantid::Kernel::Matrix< double > DblMatrix
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,...
double signal_t
Typedef for the signal recorded in a MDBox, etc.
@ Input
An input workspace.
@ Output
An output workspace.