Mantid
Loading...
Searching...
No Matches
MDNorm.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 +
11#include "MantidAPI/Run.h"
12#include "MantidAPI/Sample.h"
32
33#include <algorithm>
34#include <boost/lexical_cast.hpp>
35
36namespace Mantid::MDAlgorithms {
37
38using namespace Mantid::Kernel;
39using namespace Mantid::API;
40using namespace Mantid::Geometry;
41using namespace Mantid::DataObjects;
42
43namespace {
45// function to compare two intersections (h,k,l,Momentum) by Momentum
46bool compareMomentum(const std::array<double, 4> &v1, const std::array<double, 4> &v2) { return (v1[3] < v2[3]); }
47
48// k=sqrt(energyToK * E)
49constexpr double energyToK = 8.0 * M_PI * M_PI * PhysicalConstants::NeutronMass * PhysicalConstants::meV * 1e-20 /
51
52// compare absolute values of doubles
53static bool abs_compare(double a, double b) { return (std::fabs(a) < std::fabs(b)); }
54} // namespace
55
56// Register the algorithm into the AlgorithmFactory
58
59//----------------------------------------------------------------------------------------------
64 : m_normWS(), m_inputWS(), m_isRLU(false), m_UB(3, 3, true), m_W(3, 3, true), m_transformation(), m_hX(), m_kX(),
65 m_lX(), m_eX(), m_hIdx(-1), m_kIdx(-1), m_lIdx(-1), m_eIdx(-1), m_numExptInfos(0), m_Ei(0.0), m_diffraction(true),
66 m_monochromatic(false), m_accumulate(false), m_dEIntegrated(true), m_samplePos(), m_beamDir(), convention("") {}
67
69const std::string MDNorm::name() const { return "MDNorm"; }
70
72int MDNorm::version() const { return 1; }
73
75const std::string MDNorm::category() const { return "MDAlgorithms\\Normalisation"; }
76
78const std::string MDNorm::summary() const {
79 return "Bins multidimensional data and calculate the normalization on the "
80 "same grid";
81}
82
83//----------------------------------------------------------------------------------------------
88 std::make_unique<WorkspaceProperty<API::IMDEventWorkspace>>("InputWorkspace", "", Kernel::Direction::Input),
89 "An input MDEventWorkspace. Must be in Q_sample frame.");
90
92 "BackgroundWorkspace", "", Kernel::Direction::Input, PropertyMode::Optional),
93 "An (optional) input MDEventWorkspace for background. Must be in Q_lab frame.");
94
95 // RLU and settings
96 declareProperty("RLU", true, "Use reciprocal lattice units. If false, use Q_sample");
97 setPropertyGroup("RLU", "Q projections RLU");
98
99 auto mustBe3D = std::make_shared<Kernel::ArrayLengthValidator<double>>(3);
100 std::vector<double> Q0(3, 0.), Q1(3, 0), Q2(3, 0);
101 Q0[0] = 1.;
102 Q1[1] = 1.;
103 Q2[2] = 1.;
104
105 declareProperty(std::make_unique<ArrayProperty<double>>("QDimension0", Q0, mustBe3D),
106 "The first Q projection axis - Default is (1,0,0)");
107 setPropertySettings("QDimension0", std::make_unique<Kernel::VisibleWhenProperty>("RLU", IS_EQUAL_TO, "1"));
108 setPropertyGroup("QDimension0", "Q projections RLU");
109
110 declareProperty(std::make_unique<ArrayProperty<double>>("QDimension1", Q1, mustBe3D),
111 "The second Q projection axis - Default is (0,1,0)");
112 setPropertySettings("QDimension1", std::make_unique<Kernel::VisibleWhenProperty>("RLU", IS_EQUAL_TO, "1"));
113 setPropertyGroup("QDimension1", "Q projections RLU");
114
115 declareProperty(std::make_unique<ArrayProperty<double>>("QDimension2", Q2, mustBe3D),
116 "The thirdtCalculateCover Q projection axis - Default is (0,0,1)");
117 setPropertySettings("QDimension2", std::make_unique<Kernel::VisibleWhenProperty>("RLU", IS_EQUAL_TO, "1"));
118 setPropertyGroup("QDimension2", "Q projections RLU");
119
120 // vanadium
121 auto fluxValidator = std::make_shared<CompositeValidator>();
122 fluxValidator->add<InstrumentValidator>();
123 fluxValidator->add<CommonBinsValidator>();
124 auto solidAngleValidator = fluxValidator->clone();
125 declareProperty(std::make_unique<WorkspaceProperty<>>("SolidAngleWorkspace", "", Direction::Input,
126 API::PropertyMode::Optional, solidAngleValidator),
127 "An input workspace containing integrated vanadium "
128 "(a measure of the solid angle).\n"
129 "Mandatory for diffraction, optional for direct geometry inelastic");
130 declareProperty(std::make_unique<WorkspaceProperty<>>("FluxWorkspace", "", Direction::Input,
131 API::PropertyMode::Optional, fluxValidator),
132 "An input workspace containing momentum dependent flux.\n"
133 "Mandatory for diffraction. No effect on direct geometry inelastic");
134 setPropertyGroup("SolidAngleWorkspace", "Vanadium normalization");
135 setPropertyGroup("FluxWorkspace", "Vanadium normalization");
136
137 // monochromatic single crystal diffraction (WAND, DEMAND)
138 declareProperty(std::make_unique<WorkspaceProperty<API::IMDEventWorkspace>>("MonoSCDNormalizationWorkspace", "",
140 "An (optional) input MDEventWorkspace containing a pre-computed normalization "
141 "for monochromatic single crystal diffraction (e.g. produced by "
142 "ConvertHFIRSCDtoMDE). Must be in Q_sample frame with the same number of "
143 "dimensions as InputWorkspace. Cannot be used together with "
144 "SolidAngleWorkspace/FluxWorkspace or BackgroundWorkspace.");
145 setPropertyGroup("MonoSCDNormalizationWorkspace", "monochromatic-SCD");
146
147 // Define slicing
148 for (std::size_t i = 0; i < 6; i++) {
149 std::string propName = "Dimension" + Strings::toString(i) + "Name";
150 std::string propBinning = "Dimension" + Strings::toString(i) + "Binning";
151 std::string defaultName = "";
152 if (i < 3) {
153 defaultName = "QDimension" + Strings::toString(i);
154 }
155 declareProperty(std::make_unique<PropertyWithValue<std::string>>(propName, defaultName, Direction::Input),
156 "Name for the " + Strings::toString(i) + "th dimension. Leave blank for NONE.");
157 auto atMost3 = std::make_shared<ArrayLengthValidator<double>>(0, 3);
158 std::vector<double> temp;
159 declareProperty(std::make_unique<ArrayProperty<double>>(propBinning, temp, atMost3),
160 "Binning for the " + Strings::toString(i) + "th dimension.\n" +
161 "- Leave blank for complete integration\n" +
162 "- One value is interpreted as step\n"
163 "- Two values are interpreted integration interval\n" +
164 "- Three values are interpreted as min, step, max");
165 setPropertyGroup(propName, "Binning");
166 setPropertyGroup(propBinning, "Binning");
167 }
168
169 // symmetry operations
170 declareProperty(std::make_unique<PropertyWithValue<std::string>>("SymmetryOperations", "", Direction::Input),
171 "If specified the symmetry will be applied, "
172 "can be space group name, point group name, or list "
173 "individual symmetries.");
174
175 // temporary workspaces
176 declareProperty(std::make_unique<WorkspaceProperty<IMDHistoWorkspace>>("TemporaryDataWorkspace", "", Direction::Input,
178 "An (optional) input MDHistoWorkspace used to accumulate data from "
179 "multiple MDEventWorkspaces. If unspecified a blank "
180 "MDHistoWorkspace will be created.");
181 declareProperty(std::make_unique<WorkspaceProperty<IMDHistoWorkspace>>("TemporaryNormalizationWorkspace", "",
183 "An (optional) input MDHistoWorkspace used to accumulate normalization "
184 "from multiple MDEventWorkspaces. If unspecified a blank "
185 "MDHistoWorkspace will be created.");
186
187 // temporary background workspace
188 declareProperty(std::make_unique<WorkspaceProperty<IMDHistoWorkspace>>("TemporaryBackgroundDataWorkspace", "",
190 "An (optional) input MDHistoWorkspace used to accumulate background from "
191 "multiple background MDEventWorkspaces. If unspecified but "
192 "BackgroundWorkspace is specified, a blank "
193 "MDHistoWorkspace will be created.");
194 declareProperty(std::make_unique<WorkspaceProperty<IMDHistoWorkspace>>("TemporaryBackgroundNormalizationWorkspace",
196 "An (optional) input MDHistoWorkspace used to accumulate background normalization "
197 "from multiple background MDEventWorkspaces. If unspecified but "
198 "BackgroundWorkspace is specified, a blank "
199 "MDHistoWorkspace will be created.");
200
201 setPropertyGroup("TemporaryDataWorkspace", "Temporary workspaces");
202 setPropertyGroup("TemporaryNormalizationWorkspace", "Temporary workspaces");
203 setPropertyGroup("TemporaryBackgroundDataWorkspace", "Temporary workspaces");
204 setPropertyGroup("TemporaryBackgroundNormalizationWorkspace", "Temporary workspaces");
205
206 declareProperty(std::make_unique<WorkspaceProperty<API::Workspace>>("OutputWorkspace", "", Kernel::Direction::Output),
207 "A name for the normalized output MDHistoWorkspace.");
209 std::make_unique<WorkspaceProperty<API::Workspace>>("OutputDataWorkspace", "", Kernel::Direction::Output),
210 "A name for the output data MDHistoWorkspace.");
211 declareProperty(std::make_unique<WorkspaceProperty<Workspace>>("OutputNormalizationWorkspace", "", Direction::Output),
212 "A name for the output normalization MDHistoWorkspace.");
214 "OutputBackgroundDataWorkspace", "", Kernel::Direction::Output, PropertyMode::Optional),
215 "A name for the optional output background data MDHistoWorkspace.");
216 declareProperty(std::make_unique<WorkspaceProperty<Workspace>>("OutputBackgroundNormalizationWorkspace", "",
218 "A name for the optional output background normalization MDHistoWorkspace.");
219}
220
221//----------------------------------------------------------------------------------------------
223std::map<std::string, std::string> MDNorm::validateInputs() {
224 std::map<std::string, std::string> errorMessage;
225
226 // Check for input workspace frame
227 Mantid::API::IMDEventWorkspace_sptr inputWS = this->getProperty("InputWorkspace");
228 if (inputWS->getNumDims() < 3) {
229 errorMessage.emplace("InputWorkspace", "The input workspace must be at least 3D");
230 } else {
231 for (size_t i = 0; i < 3; i++) {
232 if (inputWS->getDimension(i)->getMDFrame().name() != Mantid::Geometry::QSample::QSampleName) {
233 errorMessage.emplace("InputWorkspace", "The input workspace must be in Q_sample");
234 }
235 }
236 }
237
238 // Optional background input IMDE
239 Mantid::API::IMDEventWorkspace_sptr bkgdWS = this->getProperty("BackgroundWorkspace");
240 if (bkgdWS) {
241 if (bkgdWS->getNumDims() < 3) {
242 // must have at least 3 dimensions
243 errorMessage.emplace("BackgroundWorkspace", "The input background workspace must be at least 3D");
244 } else {
245 // Check first 3 dimension for Q lab,
246 for (size_t i = 0; i < 3; i++) {
247 if (bkgdWS->getDimension(i)->getMDFrame().name() != Mantid::Geometry::QLab::QLabName) {
248 errorMessage.emplace("BackgroundWorkspace", "The input backgound workspace must be in Q_lab");
249 }
250 }
251
252 // Check 4th dimension if input workspace is elastic
253 if (inputWS->getNumDims() > 3) {
254 if (bkgdWS->getNumDims() <= 3) {
255 errorMessage.emplace("BackgroundWorkspace", "The input background workspace must have at 4 dimensions when "
256 "input workspace has more than 4 dimensions (inelastic case).");
257 } else if (bkgdWS->getDimension(3)->getName() != inputWS->getDimension(3)->getName()) {
258 errorMessage.emplace("BackgroundWorkspace", "The input background workspace 4th dimension must be DeltaE "
259 "for inelastic case.");
260 }
261 }
262 }
263 }
264
265 // Check if the vanadium is available for diffraction
266 bool diffraction = true;
267 if ((inputWS->getNumDims() > 3) && (inputWS->getDimension(3)->getName() == "DeltaE")) {
268 diffraction = false;
269 }
270
271 // Optional pre-computed normalization workspace for monochromatic single crystal diffraction
272 // (e.g. WAND, DEMAND). This is an alternative to SolidAngleWorkspace/FluxWorkspace.
273 Mantid::API::IMDEventWorkspace_sptr monoNormWS = this->getProperty("MonoSCDNormalizationWorkspace");
274 bool monochromatic = bool(monoNormWS);
275 API::MatrixWorkspace_const_sptr solidAngleWS = getProperty("SolidAngleWorkspace");
276 API::MatrixWorkspace_const_sptr fluxWS = getProperty("FluxWorkspace");
277
278 if (monochromatic) {
279 if (!diffraction) {
280 errorMessage.emplace("MonoSCDNormalizationWorkspace", "MonoSCDNormalizationWorkspace can only be used for "
281 "diffraction (InputWorkspace must not have a DeltaE "
282 "dimension)");
283 }
284 if (solidAngleWS || fluxWS) {
285 errorMessage.emplace("MonoSCDNormalizationWorkspace", "MonoSCDNormalizationWorkspace cannot be used together "
286 "with SolidAngleWorkspace/FluxWorkspace");
287 }
288 if (bkgdWS) {
289 errorMessage.emplace("MonoSCDNormalizationWorkspace", "MonoSCDNormalizationWorkspace cannot currently be used "
290 "together with BackgroundWorkspace");
291 }
292 if (monoNormWS->getNumDims() < 3) {
293 errorMessage.emplace("MonoSCDNormalizationWorkspace", "MonoSCDNormalizationWorkspace must be at least 3D");
294 } else {
295 if (monoNormWS->getNumDims() != inputWS->getNumDims()) {
296 errorMessage.emplace("MonoSCDNormalizationWorkspace", "MonoSCDNormalizationWorkspace must have the same "
297 "number of dimensions as InputWorkspace");
298 }
299 for (size_t i = 0; i < 3; i++) {
300 if (monoNormWS->getDimension(i)->getMDFrame().name() != Mantid::Geometry::QSample::QSampleName) {
301 errorMessage.emplace("MonoSCDNormalizationWorkspace", "MonoSCDNormalizationWorkspace must be in Q_sample");
302 }
303 }
304 }
305 } else if (diffraction) {
306 if (solidAngleWS == nullptr) {
307 errorMessage.emplace("SolidAngleWorkspace", "SolidAngleWorkspace is required for diffraction");
308 }
309 if (fluxWS == nullptr) {
310 errorMessage.emplace("FluxWorkspace", "FluxWorkspace is required for diffraction");
311 }
312 }
313 // Check for property MDNorm_low and MDNorm_high (TOF only), or, for monochromatic
314 // input, that a wavelength log is present (set by e.g. ConvertHFIRSCDtoMDE)
315 size_t nExperimentInfos = inputWS->getNumExperimentInfo();
316 if (nExperimentInfos == 0) {
317 errorMessage.emplace("InputWorkspace", "There must be at least one experiment info");
318 } else {
319 for (size_t iExpInfo = 0; iExpInfo < nExperimentInfos; iExpInfo++) {
320 auto &currentExptInfo = *(inputWS->getExperimentInfo(static_cast<uint16_t>(iExpInfo)));
321 if (monochromatic) {
322 if (!currentExptInfo.run().hasProperty("wavelength")) {
323 errorMessage.emplace("InputWorkspace", "Missing wavelength log. InputWorkspace does not look like it was "
324 "produced by ConvertHFIRSCDtoMDE, as expected when "
325 "MonoSCDNormalizationWorkspace is provided");
326 }
327 } else {
328 if (!currentExptInfo.run().hasProperty("MDNorm_low")) {
329 errorMessage.emplace("InputWorkspace", "Missing MDNorm_low log. Please "
330 "use CropWorkspaceForMDNorm "
331 "before converting to MD");
332 }
333 if (!currentExptInfo.run().hasProperty("MDNorm_high")) {
334 errorMessage.emplace("InputWorkspace", "Missing MDNorm_high log. Please use "
335 "CropWorkspaceForMDNorm before converting to MD");
336 }
337 }
338 }
339 }
340 // check projections and UB
341 if (getProperty("RLU")) {
342 DblMatrix W = DblMatrix(3, 3);
343 std::vector<double> Q0Basis = getProperty("QDimension0");
344 std::vector<double> Q1Basis = getProperty("QDimension1");
345 std::vector<double> Q2Basis = getProperty("QDimension2");
346 W.setColumn(0, Q0Basis);
347 W.setColumn(1, Q1Basis);
348 W.setColumn(2, Q2Basis);
349 if (fabs(W.determinant()) < 1e-5) {
350 errorMessage.emplace("QDimension0", "The projection dimensions are coplanar or zero");
351 errorMessage.emplace("QDimension1", "The projection dimensions are coplanar or zero");
352 errorMessage.emplace("QDimension2", "The projection dimensions are coplanar or zero");
353 }
354 if (!inputWS->getExperimentInfo(0)->sample().hasOrientedLattice()) {
355 errorMessage.emplace("InputWorkspace", "There is no oriented lattice "
356 "associated with the input workspace. "
357 "Use SetUB algorithm");
358 }
359 }
360 // check dimension names
361 std::vector<std::string> originalDimensionNames;
362 for (size_t i = 3; i < inputWS->getNumDims(); i++) {
363 originalDimensionNames.emplace_back(inputWS->getDimension(i)->getName());
364 }
365 originalDimensionNames.emplace_back("QDimension0");
366 originalDimensionNames.emplace_back("QDimension1");
367 originalDimensionNames.emplace_back("QDimension2");
368 std::vector<std::string> selectedDimensions;
369 for (std::size_t i = 0; i < 6; i++) {
370 std::string propName = "Dimension" + Strings::toString(i) + "Name";
371 std::string dimName = getProperty(propName);
372 std::string binningName = "Dimension" + Strings::toString(i) + "Binning";
373 std::vector<double> binning = getProperty(binningName);
374 if (!dimName.empty()) {
375 auto it = std::find(originalDimensionNames.begin(), originalDimensionNames.end(), dimName);
376 if (it == originalDimensionNames.end()) {
377 errorMessage.emplace(propName, "Name '" + dimName +
378 "' is not one of the "
379 "original workspace names or a directional dimension");
380 } else {
381 // make sure dimension is unique
382 auto itSel = std::find(selectedDimensions.begin(), selectedDimensions.end(), dimName);
383 if (itSel == selectedDimensions.end()) {
384 selectedDimensions.emplace_back(dimName);
385 } else {
386 errorMessage.emplace(propName, "Name '" + dimName + "' was already selected");
387 }
388 }
389 } else {
390 if (!binning.empty()) {
391 errorMessage.emplace(binningName, "There should be no binning if the dimension name is empty");
392 }
393 }
394 }
395 // since Q dimensions can be non - orthogonal, all must be present
396 if ((std::find(selectedDimensions.begin(), selectedDimensions.end(), "QDimension0") == selectedDimensions.end()) ||
397 (std::find(selectedDimensions.begin(), selectedDimensions.end(), "QDimension1") == selectedDimensions.end()) ||
398 (std::find(selectedDimensions.begin(), selectedDimensions.end(), "QDimension2") == selectedDimensions.end())) {
399 for (std::size_t i = 0; i < 6; i++) {
400 std::string propName = "Dimension" + Strings::toString(i) + "Name";
401 errorMessage.emplace(propName, "All of QDimension0, QDimension1, QDimension2 must be present");
402 }
403 }
404 // symmetry operations
405 std::string symOps = this->getProperty("SymmetryOperations");
406 if (!symOps.empty()) {
407 bool isSpaceGroup = Geometry::SpaceGroupFactory::Instance().isSubscribed(symOps);
408 bool isPointGroup = Geometry::PointGroupFactory::Instance().isSubscribed(symOps);
409 if (!isSpaceGroup && !isPointGroup) {
410 try {
411 Geometry::SymmetryOperationFactory::Instance().createSymOps(symOps);
412 } catch (const Mantid::Kernel::Exception::ParseError &) {
413 errorMessage.emplace("SymmetryOperations", "The input is not a space group, a point group, "
414 "or a list of symmetry operations");
415 }
416 }
417 }
418 // validate accumulation workspaces, if provided
419 std::shared_ptr<IMDHistoWorkspace> tempNormWS = this->getProperty("TemporaryNormalizationWorkspace");
420 Mantid::API::IMDHistoWorkspace_sptr tempDataWS = this->getProperty("TemporaryDataWorkspace");
421
422 // check that either both or neuther accumulation workspaces are provied
423 if ((tempNormWS && !tempDataWS) || (!tempNormWS && tempDataWS)) {
424 errorMessage.emplace("TemporaryDataWorkspace", "Must provide either no accumulation workspaces or,"
425 "both TemporaryNormalizationWorkspaces and TemporaryDataWorkspace");
426 }
427 // check that both accumulation workspaces are on the same grid
428 if (tempNormWS && tempDataWS) {
429 size_t numNormDims = tempNormWS->getNumDims();
430 size_t numDataDims = tempDataWS->getNumDims();
431 if (numNormDims == numDataDims) {
432 for (size_t i = 0; i < numNormDims; i++) {
433 const auto dim1 = tempNormWS->getDimension(i);
434 const auto dim2 = tempDataWS->getDimension(i);
435 if ((dim1->getMinimum() != dim2->getMinimum()) || (dim1->getMaximum() != dim2->getMaximum()) ||
436 (dim1->getNBins() != dim2->getNBins()) || (dim1->getName() != dim2->getName())) {
437 errorMessage.emplace("TemporaryDataWorkspace", "Binning for TemporaryNormalizationWorkspaces "
438 "and TemporaryDataWorkspace must be the same.");
439 break;
440 }
441 }
442 } else { // accumulation workspaces have different number of dimensions
443 errorMessage.emplace("TemporaryDataWorkspace", "TemporaryNormalizationWorkspace and TemporaryDataWorkspace "
444 "do not have the same number of dimensions");
445 }
446 }
447
448 // validate accumulated background workspaces
449 Mantid::API::IMDHistoWorkspace_sptr tempBkgdDataWS = this->getProperty("TemporaryBackgroundDataWorkspace");
450 Mantid::API::IMDHistoWorkspace_sptr tempBkgdNormWS = this->getProperty("TemporaryBackgroundNormalizationWorkspace");
451 // check existing criteria: Background, TempBackgroundData and
452 // TempBackgroundNormalization must be specified
453 if (tempBkgdDataWS && (!bkgdWS || !tempDataWS || !tempBkgdNormWS)) {
454 errorMessage.emplace("TemporaryBackgroundDataWorkspace", "TemporaryBackgroundDataWorkspace is specified but at "
455 "least one of these is not.");
456 } else if (tempBkgdNormWS && (!bkgdWS || !tempNormWS || !tempBkgdDataWS)) {
457 errorMessage.emplace("TemporaryBackgroundNormalizationWorkspace", "TemporaryBackgroundNormalizationWorkspace is "
458 "specified but at least one of these is not.");
459 } else if (bkgdWS && tempDataWS && !tempBkgdDataWS) {
460 errorMessage.emplace("TemporaryDataWorkspace",
461 "With Background is specifed and TemporaryDataWorkspace is specifed, "
462 "TemporaryBackgroundDataWorkspace must be specified.");
463 } else if (tempBkgdDataWS && tempNormWS) {
464 // check when they both exist
465 size_t numBkgdDataDims = tempBkgdDataWS->getNumDims();
466 size_t numBkgdNormDims = tempBkgdNormWS->getNumDims();
467 size_t numDataDims = tempDataWS->getNumDims();
468 if (numBkgdDataDims == numBkgdNormDims && numBkgdDataDims == numDataDims) {
469 // On each dimension, compare min, max, NBins and name
470 for (size_t idim = 0; idim < numBkgdDataDims; ++idim) {
471 const auto dimB = tempBkgdDataWS->getDimension(idim);
472 const auto dimN = tempBkgdNormWS->getDimension(idim);
473 const auto dimD = tempDataWS->getDimension(idim);
474 if ((dimB->getMinimum() != dimN->getMinimum()) || (dimB->getMinimum() != dimD->getMinimum()) ||
475 (dimB->getMaximum() != dimN->getMaximum()) || (dimB->getMaximum() != dimD->getMaximum()) ||
476 (dimB->getNBins() != dimN->getNBins()) || (dimB->getNBins() != dimD->getNBins()) ||
477 (dimB->getName() != dimN->getName()) || (dimB->getName() != dimD->getName())) {
478 errorMessage.emplace("TemporaryBackgroundDataWorkspace",
479 "TemporaryBackgroundDataWorkspace, "
480 "TemporaryBackgroundNormalizationWorkspace and "
481 "TemporaryDataWorkspace "
482 "must have same minimum, maximum, number of bins and name.");
483 break;
484 }
485 }
486 } else {
487 errorMessage.emplace("TemporaryBackgroundDataWorkspace", "TemporaryBackgroundDataWorkspace, "
488 "TemporaryBackgroundNormalizationWorkspace and "
489 "TemporaryDataWorkspace must have same dimensions");
490 }
491 }
492
493 return errorMessage;
494}
495
496//----------------------------------------------------------------------------------------------
500 convention = Kernel::ConfigService::Instance().getString("Q.convention");
501 // symmetry operations
502 std::string symOps = this->getProperty("SymmetryOperations");
503 std::vector<Geometry::SymmetryOperation> symmetryOps;
504 if (symOps.empty()) {
505 symOps = "x,y,z";
506 }
507 if (Geometry::SpaceGroupFactory::Instance().isSubscribed(symOps)) {
508 auto spaceGroup = Geometry::SpaceGroupFactory::Instance().createSpaceGroup(symOps);
509 auto pointGroup = spaceGroup->getPointGroup();
510 symmetryOps = pointGroup->getSymmetryOperations();
511 } else if (Geometry::PointGroupFactory::Instance().isSubscribed(symOps)) {
512 auto pointGroup = Geometry::PointGroupFactory::Instance().createPointGroup(symOps);
513 symmetryOps = pointGroup->getSymmetryOperations();
514 } else {
515 symmetryOps = Geometry::SymmetryOperationFactory::Instance().createSymOps(symOps);
516 }
517 g_log.debug() << "Symmetry operations\n";
518 for (const auto &so : symmetryOps) {
519 g_log.debug() << so.identifier() << "\n";
520 }
521 m_numSymmOps = symmetryOps.size();
522
523 m_isRLU = getProperty("RLU");
524 // get the workspaces
525 m_inputWS = this->getProperty("InputWorkspace");
526 Mantid::API::IMDEventWorkspace_sptr monoNormInputWS = this->getProperty("MonoSCDNormalizationWorkspace");
527 m_monochromatic = bool(monoNormInputWS);
528 const auto &exptInfoZero = *(m_inputWS->getExperimentInfo(0));
529 auto source = exptInfoZero.getInstrument()->getSource();
530 auto sample = exptInfoZero.getInstrument()->getSample();
531 if (source == nullptr || sample == nullptr) {
533 "Instrument not sufficiently defined: failed to get source and/or "
534 "sample");
535 }
536 m_samplePos = sample->getPos();
537 m_beamDir = normalize(m_samplePos - source->getPos());
538 if ((m_inputWS->getNumDims() > 3) && (m_inputWS->getDimension(3)->getName() == "DeltaE")) {
539 // DeltaE in input MDE: it cannot be diffraction!
540 m_diffraction = false;
541 if (exptInfoZero.run().hasProperty("Ei")) {
542 Kernel::Property *eiprop = exptInfoZero.run().getProperty("Ei");
543 m_Ei = boost::lexical_cast<double>(eiprop->value());
544 if (m_Ei <= 0) {
545 throw std::invalid_argument("Ei stored in the workspace is not positive");
546 }
547 } else {
548 throw std::invalid_argument("Could not find Ei value in the workspace.");
549 }
550 }
551
552 // Calculate (BinMD) input sample MDE to MDH and create noramlization MDH from
553 // it
554 auto outputDataWS = binInputWS(symmetryOps);
555 if (m_monochromatic) {
556 // Monochromatic single crystal diffraction (WAND, DEMAND): the normalization is a
557 // pre-computed MDEventWorkspace, binned identically to the data instead of being
558 // calculated from solid angle/flux trajectories.
559 m_normWS = binMonoSCDNormalizationWS(symmetryOps);
560 } else {
561 createNormalizationWS(*outputDataWS);
562 }
563 this->setProperty("OutputNormalizationWorkspace", m_normWS);
564 this->setProperty("OutputDataWorkspace", outputDataWS);
565
566 // Background
567 m_backgroundWS = this->getProperty("BackgroundWorkspace");
568 DataObjects::MDHistoWorkspace_sptr outputBackgroundDataWS(nullptr);
569 // Outputs for background related
570 if (m_backgroundWS) {
571 outputBackgroundDataWS = binBackgroundWS(symmetryOps);
572 createBackgroundNormalizationWS(*outputBackgroundDataWS);
573 this->setProperty("OutputBackgroundNormalizationWorkspace", m_bkgdNormWS);
574 this->setProperty("OutputBackgroundDataWorkspace", outputBackgroundDataWS);
575 }
576
577 m_numExptInfos = outputDataWS->getNumExperimentInfo();
578 if (!m_monochromatic) {
579 // loop over all experiment infos, computing the normalization from solid angle/flux
580 // trajectories (TOF only; for monochromatic input, m_normWS was already binned above)
581 for (uint16_t expInfoIndex = 0; expInfoIndex < m_numExptInfos; expInfoIndex++) {
582 // Check for other dimensions if we could measure anything in the original
583 // data
584 bool skipNormalization = false;
585 const std::vector<coord_t> otherValues = getValuesFromOtherDimensions(skipNormalization, expInfoIndex);
586
588
589 if (!skipNormalization) {
590 size_t symmOpsIndex = 0;
591 for (const auto &so : symmetryOps) {
592 calculateNormalization(otherValues, so, expInfoIndex, symmOpsIndex);
593 symmOpsIndex++;
594 }
595
596 } else {
597 g_log.warning("Binning limits are outside the limits of the MDWorkspace. "
598 "Not applying normalization.");
599 }
600 // if more than one experiment info, keep accumulating
601 m_accumulate = true;
602 }
603 }
604
605 API::IMDWorkspace_sptr out(nullptr);
606
607 if (m_backgroundWS) {
608 // Normalize binned (BinMD) sample workspace with background
609 out = divideMD(outputDataWS, m_normWS, getPropertyValue("OutputWorkspace"), 0.97, 0.98);
610
611 // Normalize background
612 const std::string normedBkgdWSName("_normedBkgd");
613 API::IMDWorkspace_sptr outbkgd = divideMD(outputBackgroundDataWS, m_bkgdNormWS, normedBkgdWSName, 0.98, 0.99);
614
615 // Clean workspace
616 auto minusMD = createChildAlgorithm("MinusMD", 0.99, 1.00);
617 // set up
618 minusMD->setProperty("LHSWorkspace", out);
619 minusMD->setProperty("RHSWorkspace", outbkgd);
620 minusMD->setPropertyValue("OutputWorkspace", getPropertyValue("OutputWorkspace"));
621 // run and return
622 minusMD->executeAsChildAlg();
623 out = minusMD->getProperty("OutputWorkspace");
624
625 } else {
626 // Normalize binned (BinMD) sample workspace without background
627 out = divideMD(outputDataWS, m_normWS, getPropertyValue("OutputWorkspace"), 0.97, 1.);
628 }
629
630 // Set output workspace
631 this->setProperty("OutputWorkspace", out);
632}
633
635 const API::IMDHistoWorkspace_sptr &rhs, const std::string &outputwsname,
636 const double &startProgress, const double &endProgress) {
637 auto divideMD = createChildAlgorithm("DivideMD", startProgress, endProgress);
638 divideMD->setProperty("LHSWorkspace", lhs);
639 divideMD->setProperty("RHSWorkspace", rhs);
640 divideMD->setPropertyValue("OutputWorkspace", outputwsname);
641 divideMD->executeAsChildAlg();
642 // API::IMDWorkspace_sptr
643 API::IMDWorkspace_sptr out = divideMD->getProperty("OutputWorkspace");
644
645 return out;
646}
647
654 if (i == 0)
655 return std::string("Q_sample_x");
656 else if (i == 1)
657 return std::string("Q_sample_y");
658 else if (i == 2)
659 return std::string("Q_sample_z");
660 else
661 throw std::invalid_argument("Index must be 0, 1, or 2 for QDimensionNameQSample");
662}
669std::string MDNorm::QDimensionName(std::vector<double> projection) {
670 std::vector<double>::iterator result;
671 result = std::max_element(projection.begin(), projection.end(), abs_compare);
672 std::vector<char> symbol{'H', 'K', 'L'};
673 char character = symbol[std::distance(projection.begin(), result)];
674 std::stringstream name;
675 name << "[";
676 for (size_t i = 0; i < 3; i++) {
677 if (projection[i] == 0) {
678 name << "0";
679 } else if (projection[i] == 1) {
680 name << character;
681 } else if (projection[i] == -1) {
682 name << "-" << character;
683 } else {
684 name << std::defaultfloat << std::setprecision(3) << projection[i] << character;
685 }
686 if (i != 2) {
687 name << ",";
688 }
689 }
690 name << "]";
691 return name.str();
692}
693
698std::map<std::string, std::string> MDNorm::getBinParameters() {
699 std::map<std::string, std::string> parameters;
700 std::stringstream extents;
701 std::stringstream bins;
702 std::vector<std::string> originalDimensionNames;
703 originalDimensionNames.emplace_back("QDimension0");
704 originalDimensionNames.emplace_back("QDimension1");
705 originalDimensionNames.emplace_back("QDimension2");
706 for (size_t i = 3; i < m_inputWS->getNumDims(); i++) {
707 originalDimensionNames.emplace_back(m_inputWS->getDimension(i)->getName());
708 }
709
710 if (m_isRLU) {
711 m_Q0Basis = getProperty("QDimension0");
712 m_Q1Basis = getProperty("QDimension1");
713 m_Q2Basis = getProperty("QDimension2");
714 m_UB = m_inputWS->getExperimentInfo(0)->sample().getOrientedLattice().getUB() * 2 * M_PI;
715 }
716
717 std::vector<double> W(m_Q0Basis);
718 W.insert(W.end(), m_Q1Basis.begin(), m_Q1Basis.end());
719 W.insert(W.end(), m_Q2Basis.begin(), m_Q2Basis.end());
720 m_W = DblMatrix(W);
721 m_W.Transpose();
722
723 // Find maximum Q, an isotropic bound used below to set the default HKL bin extents
724 // (only consumed when a Q dimension's binning is left automatic/step-only, see the
725 // m_isRLU branch below). For TOF input this comes from the physical wavelength/TOF
726 // window each detector reaches (MDNorm_low/MDNorm_high logs, set by
727 // CropWorkspaceForMDNorm). Monochromatic input has no such trajectory -- each event
728 // is a single measured Q_sample point -- so maxQ is instead estimated from the
729 // workspace's own data-occupied extents: the box-tree's tight bounding box (not the
730 // nominal/arbitrary MinValues/MaxValues used at MD-conversion time), converted to an
731 // isotropic bound by taking the modulus of its farthest corner from the origin.
732 double maxQ = 0.;
733 if (!m_monochromatic) {
734 auto &exptInfo0 = *(m_inputWS->getExperimentInfo(static_cast<uint16_t>(0)));
735 auto upperLimitsVector =
736 (*(dynamic_cast<Kernel::PropertyWithValue<std::vector<double>> *>(exptInfo0.getLog("MDNorm_high"))))();
737 if (m_diffraction) {
738 maxQ = 2. * (*std::max_element(upperLimitsVector.begin(), upperLimitsVector.end()));
739 } else {
740 double Ei;
741 double maxDE = *std::max_element(upperLimitsVector.begin(), upperLimitsVector.end());
742 auto loweLimitsVector =
743 (*(dynamic_cast<Kernel::PropertyWithValue<std::vector<double>> *>(exptInfo0.getLog("MDNorm_low"))))();
744 double minDE = *std::min_element(loweLimitsVector.begin(), loweLimitsVector.end());
745 if (exptInfo0.run().hasProperty("Ei")) {
746 Kernel::Property *eiprop = exptInfo0.run().getProperty("Ei");
747 Ei = boost::lexical_cast<double>(eiprop->value());
748 if (Ei <= 0) {
749 throw std::invalid_argument("Ei stored in the workspace is not positive");
750 }
751 } else {
752 throw std::invalid_argument("Could not find Ei value in the workspace.");
753 }
754 const double energyToK = 8.0 * M_PI * M_PI * PhysicalConstants::NeutronMass * PhysicalConstants::meV * 1e-20 /
756 double ki = std::sqrt(energyToK * Ei);
757 double kfmin = std::sqrt(energyToK * (Ei - minDE));
758 double kfmax = std::sqrt(energyToK * (Ei - maxDE));
759
760 maxQ = ki + std::max(kfmin, kfmax);
761 }
762 } else {
763 // getMinimumExtents() walks the box tree to the given depth and unions the extents of
764 // populated boxes found there; its default depth of 2 can be much looser than the box
765 // tree's actual depth for sparser inputs (measured up to ~3.7x tighter per-axis bounds by
766 // depth 4 on real WAND/DEMAND data), while costing at most tens of milliseconds even for
767 // O(1e7)-event workspaces, so a fixed depth of 4 is used here instead of the default.
768 auto dataExtents = m_inputWS->getMinimumExtents(4);
769 double qx = std::max(std::fabs(dataExtents[0].getMin()), std::fabs(dataExtents[0].getMax()));
770 double qy = std::max(std::fabs(dataExtents[1].getMin()), std::fabs(dataExtents[1].getMax()));
771 double qz = std::max(std::fabs(dataExtents[2].getMin()), std::fabs(dataExtents[2].getMax()));
772 maxQ = std::sqrt(qx * qx + qy * qy + qz * qz);
773 }
774 size_t basisVectorIndex = 0;
775 std::vector<coord_t> transformation;
776 for (std::size_t i = 0; i < 6; i++) {
777 std::string propName = "Dimension" + Strings::toString(i) + "Name";
778 std::string binningName = "Dimension" + Strings::toString(i) + "Binning";
779 std::string dimName = getProperty(propName);
780 std::vector<double> binning = getProperty(binningName);
781 std::string bv = "BasisVector";
782 if (!dimName.empty()) {
783 std::string property = bv + Strings::toString(basisVectorIndex);
784 std::stringstream propertyValue;
785 propertyValue << dimName;
786 // get the index in the original workspace
787 auto dimIndex = std::distance(originalDimensionNames.begin(),
788 std::find(originalDimensionNames.begin(), originalDimensionNames.end(), dimName));
789 auto dimension = m_inputWS->getDimension(dimIndex);
790 propertyValue << "," << dimension->getMDUnits().getUnitLabel().ascii();
791 for (size_t j = 0; j < originalDimensionNames.size(); j++) {
792 if (j == static_cast<size_t>(dimIndex)) {
793 propertyValue << ",1";
794 transformation.emplace_back(1.f);
795 } else {
796 propertyValue << ",0";
797 transformation.emplace_back(0.f);
798 }
799 }
800 parameters.emplace(property, propertyValue.str());
801 // get the extents an number of bins
802 coord_t dimMax = dimension->getMaximum();
803 coord_t dimMin = dimension->getMinimum();
804 if (m_isRLU) {
806 ol.setUB(m_UB * m_W); // note that this is already multiplied by 2Pi
807 if (dimIndex == 0) {
808 dimMax = static_cast<coord_t>(ol.a() * maxQ);
809 dimMin = -dimMax;
810 } else if (dimIndex == 1) {
811 dimMax = static_cast<coord_t>(ol.b() * maxQ);
812 dimMin = -dimMax;
813 } else if (dimIndex == 2) {
814 dimMax = static_cast<coord_t>(ol.c() * maxQ);
815 dimMin = -dimMax;
816 }
817 }
818 if (binning.size() == 0) {
819 // only one bin, integrating from min to max
820 extents << dimMin << "," << dimMax << ",";
821 bins << 1 << ",";
822 } else if (binning.size() == 2) {
823 // only one bin, integrating from min to max
824 extents << binning[0] << "," << binning[1] << ",";
825 bins << 1 << ",";
826 } else if (binning.size() == 1) {
827 auto step = binning[0];
828 double nsteps = (dimMax - dimMin) / step;
829 if (nsteps + 1 - std::ceil(nsteps) >= 1e-4) {
830 nsteps = std::ceil(nsteps);
831 } else {
832 nsteps = std::floor(nsteps);
833 }
834 bins << static_cast<int>(nsteps) << ",";
835 extents << dimMin << "," << dimMin + nsteps * step << ",";
836 } else if (binning.size() == 3) {
837 dimMin = static_cast<coord_t>(binning[0]);
838 auto step = binning[1];
839 dimMax = static_cast<coord_t>(binning[2]);
840 double nsteps = (dimMax - dimMin) / step;
841 if (nsteps + 1 - std::ceil(nsteps) >= 1e-4) {
842 nsteps = std::ceil(nsteps);
843 } else {
844 nsteps = std::floor(nsteps);
845 }
846 bins << static_cast<int>(nsteps) << ",";
847 extents << dimMin << "," << dimMin + nsteps * step << ",";
848 }
849 basisVectorIndex++;
850 }
851 }
852 parameters.emplace("OutputExtents", extents.str());
853 parameters.emplace("OutputBins", bins.str());
855 transformation, static_cast<size_t>((transformation.size()) / m_inputWS->getNumDims()), m_inputWS->getNumDims());
856 return parameters;
857}
858
864 // Copy the MDHisto workspace, and change signals and errors to 0.
865 std::shared_ptr<IMDHistoWorkspace> tmp = this->getProperty("TemporaryNormalizationWorkspace");
866 m_normWS = std::dynamic_pointer_cast<MDHistoWorkspace>(tmp);
867 if (!m_normWS) {
868 m_normWS = dataWS.clone();
869 m_normWS->setTo(0., 0., 0.);
870 } else {
871 // Temp is given. Accumulation mode is on
872 m_accumulate = true;
873 }
874}
875
877
878 // requiring background workspace is specified
879 if (!m_backgroundWS) {
880 return;
881 }
882
883 // Copy the MDHisto workspace, and change signals and errors to 0.
884 std::shared_ptr<IMDHistoWorkspace> tmp = this->getProperty("TemporaryBackgroundNormalizationWorkspace");
885 m_bkgdNormWS = std::dynamic_pointer_cast<MDHistoWorkspace>(tmp);
886 if (!m_bkgdNormWS) {
887 m_bkgdNormWS = bkgdDataWS.clone();
888 m_bkgdNormWS->setTo(0., 0., 0.);
889 }
890}
891
898void MDNorm::validateBinningForTemporaryDataWorkspace(const std::map<std::string, std::string> &parameters,
899 const Mantid::API::IMDHistoWorkspace_sptr &tempDataWS) {
900
901 // parse the paramters map and get extents from tempDataWS
902 const std::string numBinsStr = parameters.at("OutputBins");
903 const std::string extentsStr = parameters.at("OutputExtents");
904 const std::vector<size_t> numBins = VectorHelper::splitStringIntoVector<size_t>(numBinsStr);
905 const std::vector<double> extents = VectorHelper::splitStringIntoVector<double>(extentsStr);
906
907 // make sure the number of dimensions is the same for both workspaces
908 size_t numDimsTemp = tempDataWS->getNumDims();
909 if ((numBins.size() != numDimsTemp) || (extents.size() != numDimsTemp * 2)) {
910 std::stringstream errorMessage;
911 errorMessage << "The number of dimensions in the output and ";
912 errorMessage << "TemporaryDataWorkspace are not the same.";
913 throw(std::invalid_argument(errorMessage.str()));
914 }
915
916 // compare the extents and number of bins
917 for (size_t i = 0; i < numDimsTemp; i++) {
918 auto ax = tempDataWS->getDimension(i);
919 if (numBins[i] != ax->getNBins()) {
920 std::stringstream errorMessage;
921 errorMessage << "The number of bins output and number of bins in ";
922 errorMessage << "TemporaryDataWorkspace are not the same along ";
923 errorMessage << "dimension " << i;
924 throw(std::invalid_argument(errorMessage.str()));
925 }
926 if (std::abs(extents[2 * i] - ax->getMinimum()) > 1.e-5) {
927 std::stringstream errorMessage;
928 errorMessage << "The minimum binning value for the output and ";
929 errorMessage << "TemporaryDataWorkspace are not the same along ";
930 errorMessage << "dimension " << i;
931 throw(std::invalid_argument(errorMessage.str()));
932 }
933 if (std::abs(extents[2 * i + 1] - ax->getMaximum()) > 1.e-5) {
934 std::stringstream errorMessage;
935 errorMessage << "The maximum binning value for the output and ";
936 errorMessage << "TemporaryDataWorkspace are not the same along ";
937 errorMessage << "dimension " << i;
938 throw(std::invalid_argument(errorMessage.str()));
939 }
940 }
941
942 // sort out which axes are dimensional and check names
943 size_t parametersIndex = 0;
944 std::vector<size_t> dimensionIndex(numDimsTemp + 1, 3); // stores h, k, l or Qx, Qy, Qz dimensions
945 for (const auto &p : parameters) {
946 auto key = p.first;
947 auto value = p.second;
948 // value starts with QDimension0, then other stuff
949 // do not use ==
950 if (value.find("QDimension0") != std::string::npos) {
951 dimensionIndex[0] = parametersIndex;
952 const std::string dimXName = tempDataWS->getDimension(parametersIndex)->getName();
953 if (m_isRLU) { // hkl
954 if (dimXName != QDimensionName(m_Q0Basis)) {
955 std::stringstream errorMessage;
956 std::stringstream debugMessage;
957 errorMessage << "TemporaryDataWorkspace does not have the ";
958 errorMessage << "correct name for dimension " << parametersIndex;
959 debugMessage << "QDimension0 Names: Output will be: " << QDimensionName(m_Q0Basis);
960 debugMessage << " TemporaryDataWorkspace: " << dimXName;
961 g_log.warning(debugMessage.str());
962 throw(std::invalid_argument(errorMessage.str()));
963 }
964 } else {
965 if (dimXName != QDimensionNameQSample(0)) {
966 std::stringstream errorMessage;
967 std::stringstream debugMessage;
968 errorMessage << "TemporaryDataWorkspace does not have the ";
969 errorMessage << "correct name for dimension " << parametersIndex;
970 debugMessage << "QDimension0 Names: Output will be: " << QDimensionNameQSample(0);
971 debugMessage << " TemporaryDataWorkspace: " << dimXName;
972 g_log.warning(debugMessage.str());
973 throw(std::invalid_argument(errorMessage.str()));
974 }
975 }
976 } else if (value.find("QDimension1") != std::string::npos) {
977 dimensionIndex[1] = parametersIndex;
978 const std::string dimYName = tempDataWS->getDimension(parametersIndex)->getName();
979 if (m_isRLU) { // hkl
980 if (dimYName != QDimensionName(m_Q1Basis)) {
981 std::stringstream errorMessage;
982 std::stringstream debugMessage;
983 errorMessage << "TemporaryDataWorkspace does not have the ";
984 errorMessage << "correct name for dimension " << parametersIndex;
985 debugMessage << "QDimension1 Names: Output will be: " << QDimensionName(m_Q1Basis);
986 debugMessage << " TemporaryDataWorkspace: " << dimYName;
987 g_log.warning(debugMessage.str());
988 throw(std::invalid_argument(errorMessage.str()));
989 }
990 } else {
991 if (dimYName != QDimensionNameQSample(1)) {
992 std::stringstream errorMessage;
993 std::stringstream debugMessage;
994 errorMessage << "TemporaryDataWorkspace does not have the ";
995 errorMessage << "correct name for dimension " << parametersIndex;
996 debugMessage << "QDimension1 Names: Output will be: " << QDimensionNameQSample(1);
997 debugMessage << " TemporaryDataWorkspace: " << dimYName;
998 g_log.warning(debugMessage.str());
999 throw(std::invalid_argument(errorMessage.str()));
1000 }
1001 }
1002 } else if (value.find("QDimension2") != std::string::npos) {
1003 dimensionIndex[2] = parametersIndex;
1004 const std::string dimZName = tempDataWS->getDimension(parametersIndex)->getName();
1005 if (m_isRLU) { // hkl
1006 if (dimZName != QDimensionName(m_Q2Basis)) {
1007 std::stringstream errorMessage;
1008 std::stringstream debugMessage;
1009 errorMessage << "TemporaryDataWorkspace does not have the ";
1010 errorMessage << "correct name for dimension " << parametersIndex;
1011 debugMessage << "QDimension2 Names: Output will be: " << QDimensionName(m_Q2Basis);
1012 debugMessage << " TemporaryDataWorkspace: " << dimZName;
1013 g_log.warning(debugMessage.str());
1014 throw(std::invalid_argument(errorMessage.str()));
1015 }
1016 } else {
1017 if (dimZName != QDimensionNameQSample(2)) {
1018 std::stringstream errorMessage;
1019 std::stringstream debugMessage;
1020 errorMessage << "TemporaryDataWorkspace does not have the ";
1021 errorMessage << "correct name for dimension " << parametersIndex;
1022 debugMessage << "QDimension2 Names: Output will be: " << QDimensionNameQSample(2);
1023 debugMessage << " TemporaryDataWorkspace: " << dimZName;
1024 g_log.warning(debugMessage.str());
1025 throw(std::invalid_argument(errorMessage.str()));
1026 }
1027 }
1028
1029 } else if ((key != "OutputBins") && (key != "OutputExtents")) {
1030 // make sure the names of non-directional dimensions are the same
1031 const std::string nameData = tempDataWS->getDimension(parametersIndex)->getName();
1032 if (value.find(nameData) != 0) {
1033 g_log.error() << "Dimension " << nameData
1034 << " from the temporary workspace"
1035 " is not one of the binning dimensions, "
1036 " or dimensions are in the wrong order."
1037 << std::endl;
1038 throw(std::invalid_argument("Beside the Q dimensions, "
1039 "TemporaryDataWorkspace does not have the "
1040 "same dimension names as OutputWorkspace."));
1041 }
1042 }
1043 parametersIndex++;
1044 }
1045 const auto it = std::find_if(dimensionIndex.cbegin(), dimensionIndex.cend(),
1046 [numDimsTemp](const auto &idx) { return idx > numDimsTemp; });
1047 if (it != dimensionIndex.cend())
1048 throw(std::invalid_argument("Cannot find at least one of QDimension0, "
1049 "QDimension1, or QDimension2"));
1050}
1051
1058 // calculate dimensions for binning
1059 DblMatrix soMatrix(3, 3);
1060 auto v = so.transformHKL(V3D(1, 0, 0));
1061 soMatrix.setColumn(0, v);
1062 v = so.transformHKL(V3D(0, 1, 0));
1063 soMatrix.setColumn(1, v);
1064 v = so.transformHKL(V3D(0, 0, 1));
1065 soMatrix.setColumn(2, v);
1066 soMatrix.Invert();
1067
1068 return soMatrix;
1069}
1070
1071// projection: input/output
1072// requiring: m_hIdx, m_kIndex, m_lIdx, meidx, m_dEintegrated, m_Q0Basis,
1073// mQ1Basis,
1083inline void MDNorm::determineBasisVector(const size_t &qindex, const std::string &value,
1084 const Mantid::Kernel::DblMatrix &Qtransform, std::vector<double> &projection,
1085 std::stringstream &basisVector, std::vector<size_t> &qDimensionIndices) {
1086 if (value.find("QDimension0") != std::string::npos) {
1087 m_hIdx = qindex;
1088 if (!m_isRLU) {
1089 projection[0] = 1.;
1090 basisVector << QDimensionNameQSample(0) << ",A^{-1}";
1091 } else {
1092 qDimensionIndices.emplace_back(qindex);
1093 projection[0] = Qtransform[0][0];
1094 projection[1] = Qtransform[1][0];
1095 projection[2] = Qtransform[2][0];
1096 basisVector << QDimensionName(m_Q0Basis) << ", r.l.u.";
1097 }
1098 } else if (value.find("QDimension1") != std::string::npos) {
1099 m_kIdx = qindex;
1100 if (!m_isRLU) {
1101 projection[1] = 1.;
1102 basisVector << QDimensionNameQSample(1) << ",A^{-1}";
1103 } else {
1104 qDimensionIndices.emplace_back(qindex);
1105 projection[0] = Qtransform[0][1];
1106 projection[1] = Qtransform[1][1];
1107 projection[2] = Qtransform[2][1];
1108 basisVector << QDimensionName(m_Q1Basis) << ", r.l.u.";
1109 }
1110 } else if (value.find("QDimension2") != std::string::npos) {
1111 m_lIdx = qindex;
1112 if (!m_isRLU) {
1113 projection[2] = 1.;
1114 basisVector << QDimensionNameQSample(2) << ",A^{-1}";
1115 } else {
1116 qDimensionIndices.emplace_back(qindex);
1117 projection[0] = Qtransform[0][2];
1118 projection[1] = Qtransform[1][2];
1119 projection[2] = Qtransform[2][2];
1120 basisVector << QDimensionName(m_Q2Basis) << ", r.l.u.";
1121 }
1122 } else if (value.find("DeltaE") != std::string::npos) {
1123 m_eIdx = qindex;
1124 m_dEIntegrated = false;
1125 }
1126}
1127
1133inline void MDNorm::setQUnit(const std::vector<size_t> &qDimensionIndices,
1134 const Mantid::DataObjects::MDHistoWorkspace_sptr &outputMDHWS) {
1136 auto mdFrameFactory = Mantid::Geometry::makeMDFrameFactoryChain();
1137 Mantid::Geometry::MDFrame_uptr hklFrame = mdFrameFactory->create(argument);
1138 for (size_t i : qDimensionIndices) {
1139 auto mdHistoDimension = std::const_pointer_cast<Mantid::Geometry::MDHistoDimension>(
1140 std::dynamic_pointer_cast<const Mantid::Geometry::MDHistoDimension>(outputMDHWS->getDimension(i)));
1141 mdHistoDimension->setMDFrame(*hklFrame);
1142 }
1143 // add W_matrix
1144 auto ei = outputMDHWS->getExperimentInfo(0);
1145 ei->mutableRun().addProperty("W_MATRIX", m_W.getVector(), true);
1146}
1147
1154MDNorm::binBackgroundWS(const std::vector<Geometry::SymmetryOperation> &symmetryOps) {
1155 // Create output background data histogram MD workspace
1156 // Either from TemporaryBackgroundDataWorkspace
1157 // Or from scratch
1158 Mantid::API::IMDHistoWorkspace_sptr tempBkgdDataWS = this->getProperty("TemporaryBackgroundDataWorkspace");
1160
1161 // check that our input matches the temporary workspaces
1162 std::map<std::string, std::string> parameters = getBinParameters();
1163 if (tempBkgdDataWS) {
1164 validateBinningForTemporaryDataWorkspace(parameters, tempBkgdDataWS);
1165 }
1166
1167 // For each symmetry operation, do binning MD once
1168 std::vector<size_t> qDimensionIndices;
1169 uint16_t numexpinfo = static_cast<uint16_t>(m_inputWS->getNumExperimentInfo());
1170 if (m_numSymmOps != symmetryOps.size())
1171 throw std::runtime_error("Symmetry operation number m_umSymops is wrong!");
1172
1173 for (uint16_t i_expinfo = 0; i_expinfo < numexpinfo; ++i_expinfo) {
1174
1175 auto rotMatrix = m_inputWS->getExperimentInfo(i_expinfo)->run().getGoniometerMatrix();
1176
1177 // Reset symmetry operation index
1178 double soIndex = 0;
1179
1180 for (const auto &so : symmetryOps) {
1181 // Q transformation matrix: From Q_lab to HKL or Q_sample
1182 // Building symmetric operation matrix
1183 DblMatrix soMatrix = buildSymmetryMatrix(so);
1184 // Calculate Q transform matrix
1185 DblMatrix Qtransform;
1186 if (m_isRLU) {
1187 Qtransform = rotMatrix * m_UB * soMatrix * m_W;
1188 } else {
1189 Qtransform = rotMatrix * soMatrix * m_W;
1190 }
1191
1192 // Set up BinMD for this symmetry opeation
1193 double progress_fraction = 1. / static_cast<double>(symmetryOps.size() * numexpinfo);
1194 auto binMD =
1195 createChildAlgorithm("BinMD", soIndex * 0.3 * progress_fraction, (soIndex + 1) * 0.3 * progress_fraction);
1196
1197 binMD->setPropertyValue("AxisAligned", "0");
1198 binMD->setProperty("InputWorkspace", m_backgroundWS);
1199 binMD->setProperty("TemporaryDataWorkspace", tempBkgdDataWS);
1200 binMD->setPropertyValue("NormalizeBasisVectors", "0");
1201 // Set the output Workspace directly to Algorithm's
1202 // OutputBackgroundDataWorkspace
1203 binMD->setPropertyValue("OutputWorkspace", getPropertyValue("OutputBackgroundDataWorkspace"));
1204 // set binning properties
1205 size_t qindex = 0;
1206 for (const auto &p : parameters) {
1207 auto key = p.first;
1208 auto value = p.second;
1209 std::stringstream basisVector;
1210 std::vector<double> projection(m_inputWS->getNumDims(), 0.);
1211 // value is a string that can start with QDimension0, etc, but contain
1212 // other stuff. Do not use ==
1213 determineBasisVector(qindex, value, Qtransform, projection, basisVector, qDimensionIndices);
1214
1215 if (!basisVector.str().empty()) {
1216 // reconstruct from calculated basis vector
1217 for (auto proji : projection) {
1218 proji = std::abs(proji) > 1e-10 ? proji : 0.0;
1219 basisVector << "," << proji;
1220 }
1221 value = basisVector.str();
1222 }
1223
1224 binMD->setPropertyValue(key, value);
1225 qindex++;
1226 }
1227 // execute algorithm
1228 binMD->executeAsChildAlg();
1229
1230 // set the temporary workspace to be the output workspace, so it keeps
1231 // adding different symmetries AND
1232 // FIXME in future another ExpInfo
1233 outputWS = binMD->getProperty("OutputWorkspace");
1234 tempBkgdDataWS = std::dynamic_pointer_cast<MDHistoWorkspace>(outputWS);
1235 tempBkgdDataWS->clearOriginalWorkspaces();
1236 tempBkgdDataWS->clearTransforms();
1237 }
1238 }
1239 auto outputMDHWS = std::dynamic_pointer_cast<MDHistoWorkspace>(outputWS);
1240 // set MDUnits for Q dimensions
1241 if (m_isRLU) {
1242 setQUnit(qDimensionIndices, outputMDHWS);
1243 }
1244
1245 outputMDHWS->setDisplayNormalization(Mantid::API::NoNormalization);
1246 return outputMDHWS;
1247}
1248
1254DataObjects::MDHistoWorkspace_sptr MDNorm::binInputWS(const std::vector<Geometry::SymmetryOperation> &symmetryOps) {
1255 std::map<std::string, std::string> parameters = getBinParameters();
1256 return binMDEventWorkspace(m_inputWS, "TemporaryDataWorkspace", "OutputDataWorkspace", symmetryOps, parameters);
1257}
1258
1267MDNorm::binMonoSCDNormalizationWS(const std::vector<Geometry::SymmetryOperation> &symmetryOps) {
1268 std::map<std::string, std::string> parameters = getBinParameters();
1269 Mantid::API::IMDEventWorkspace_sptr monoNormInputWS = this->getProperty("MonoSCDNormalizationWorkspace");
1270 return binMDEventWorkspace(monoNormInputWS, "TemporaryNormalizationWorkspace", "OutputNormalizationWorkspace",
1271 symmetryOps, parameters);
1272}
1273
1289MDNorm::binMDEventWorkspace(const API::IMDEventWorkspace_sptr &ws, const std::string &temporaryWSPropertyName,
1290 const std::string &outputWSPropertyName,
1291 const std::vector<Geometry::SymmetryOperation> &symmetryOps,
1292 const std::map<std::string, std::string> &parameters) {
1293 Mantid::API::IMDHistoWorkspace_sptr tempWS = this->getProperty(temporaryWSPropertyName);
1295
1296 // check that our input matches the temporary workspace
1297 if (tempWS)
1298 validateBinningForTemporaryDataWorkspace(parameters, tempWS);
1299
1300 double soIndex = 0;
1301 std::vector<size_t> qDimensionIndices;
1302 for (const auto &so : symmetryOps) {
1303 // calculate dimensions for binning
1304 DblMatrix soMatrix = buildSymmetryMatrix(so);
1305 DblMatrix Qtransform;
1306
1307 if (m_isRLU) {
1308 Qtransform = m_UB * soMatrix * m_W;
1309 } else {
1310 Qtransform = soMatrix * m_W;
1311 }
1312
1313 // bin the data
1314 double fraction = 1. / static_cast<double>(symmetryOps.size());
1315 auto binMD = createChildAlgorithm("BinMD", soIndex * 0.3 * fraction, (soIndex + 1) * 0.3 * fraction);
1316 binMD->setPropertyValue("AxisAligned", "0");
1317 binMD->setProperty("InputWorkspace", ws);
1318 binMD->setProperty("TemporaryDataWorkspace", tempWS);
1319 binMD->setPropertyValue("NormalizeBasisVectors", "0");
1320 binMD->setPropertyValue("OutputWorkspace", getPropertyValue(outputWSPropertyName));
1321 // set binning properties
1322 size_t qindex = 0;
1323 for (const auto &p : parameters) {
1324 auto value = p.second;
1325 std::stringstream basisVector;
1326 std::vector<double> projection(ws->getNumDims(), 0.);
1327 // value is a string that can start with QDimension0, etc, but contain
1328 // other stuff. Do not use ==
1329 determineBasisVector(qindex, value, Qtransform, projection, basisVector, qDimensionIndices);
1330
1331 if (!basisVector.str().empty()) {
1332 // reconstruct from calculated basis vector
1333 for (auto proji : projection) {
1334 proji = std::abs(proji) > 1e-10 ? proji : 0.0;
1335 basisVector << "," << proji;
1336 }
1337 value = basisVector.str();
1338 }
1339
1340 binMD->setPropertyValue(p.first, value);
1341 qindex++;
1342 }
1343 // execute algorithm
1344 binMD->executeAsChildAlg();
1345 outputWS = binMD->getProperty("OutputWorkspace");
1346
1347 // set the temporary workspace to be the output workspace, so it keeps
1348 // adding different symmetries
1349 tempWS = std::dynamic_pointer_cast<MDHistoWorkspace>(outputWS);
1350 tempWS->clearOriginalWorkspaces();
1351 tempWS->clearTransforms();
1352 soIndex += 1;
1353 }
1354
1355 auto outputMDHWS = std::dynamic_pointer_cast<MDHistoWorkspace>(outputWS);
1356 // set MDUnits for Q dimensions
1357 if (m_isRLU) {
1358 setQUnit(qDimensionIndices, outputMDHWS);
1359 }
1360
1361 outputMDHWS->setDisplayNormalization(Mantid::API::NoNormalization);
1362 return outputMDHWS;
1363}
1364
1373std::vector<coord_t> MDNorm::getValuesFromOtherDimensions(bool &skipNormalization, uint16_t expInfoIndex) const {
1374 const auto &currentRun = m_inputWS->getExperimentInfo(expInfoIndex)->run();
1375
1376 std::vector<coord_t> otherDimValues;
1377 for (size_t i = 3; i < m_inputWS->getNumDims(); i++) {
1378 const auto dimension = m_inputWS->getDimension(i);
1379 auto inputDimMin = static_cast<float>(dimension->getMinimum());
1380 auto inputDimMax = static_cast<float>(dimension->getMaximum());
1381 coord_t outputDimMin(0), outputDimMax(0);
1382 bool isIntegrated = true;
1383
1384 for (size_t j = 0; j < m_transformation.numRows(); j++) {
1385 if (m_transformation[j][i] == 1) {
1386 isIntegrated = false;
1387 outputDimMin = m_normWS->getDimension(j)->getMinimum();
1388 outputDimMax = m_normWS->getDimension(j)->getMaximum();
1389 }
1390 }
1391 if (dimension->getName() == "DeltaE") {
1392 if ((inputDimMax < outputDimMin) || (inputDimMin > outputDimMax)) {
1393 skipNormalization = true;
1394 }
1395 } else {
1396 coord_t value = static_cast<coord_t>(
1397 currentRun.getLogAsSingleValue(dimension->getName(), Mantid::Kernel::Math::TimeAveragedMean));
1398 otherDimValues.emplace_back(value);
1399 if (value < inputDimMin || value > inputDimMax) {
1400 skipNormalization = true;
1401 }
1402 if ((!isIntegrated) && (value < outputDimMin || value > outputDimMax)) {
1403 skipNormalization = true;
1404 }
1405 }
1406 }
1407 return otherDimValues;
1408}
1409
1415 auto &hDim = *m_normWS->getDimension(m_hIdx);
1416 m_hX.resize(hDim.getNBoundaries());
1417 for (size_t i = 0; i < m_hX.size(); ++i) {
1418 m_hX[i] = hDim.getX(i);
1419 }
1420 auto &kDim = *m_normWS->getDimension(m_kIdx);
1421 m_kX.resize(kDim.getNBoundaries());
1422 for (size_t i = 0; i < m_kX.size(); ++i) {
1423 m_kX[i] = kDim.getX(i);
1424 }
1425
1426 auto &lDim = *m_normWS->getDimension(m_lIdx);
1427 m_lX.resize(lDim.getNBoundaries());
1428 for (size_t i = 0; i < m_lX.size(); ++i) {
1429 m_lX[i] = lDim.getX(i);
1430 }
1431
1432 if ((!m_diffraction) && (!m_dEIntegrated)) {
1433 // NOTE: store k final instead
1434 auto &eDim = *m_normWS->getDimension(m_eIdx);
1435 m_eX.resize(eDim.getNBoundaries());
1436 for (size_t i = 0; i < m_eX.size(); ++i) {
1437 double temp = m_Ei - eDim.getX(i);
1438 temp = std::max(temp, 0.);
1439 m_eX[i] = std::sqrt(energyToK * temp);
1440 }
1441 }
1442}
1443
1451 const Geometry::SymmetryOperation &so) {
1452 // Make it to a method!
1453 DblMatrix R = currentExpInfo.run().getGoniometerMatrix();
1454 DblMatrix soMatrix(3, 3);
1455 auto v = so.transformHKL(V3D(1, 0, 0));
1456 soMatrix.setColumn(0, v);
1457 v = so.transformHKL(V3D(0, 1, 0));
1458 soMatrix.setColumn(1, v);
1459 v = so.transformHKL(V3D(0, 0, 1));
1460 soMatrix.setColumn(2, v);
1461 soMatrix.Invert();
1462 DblMatrix Qtransform = R * m_UB * soMatrix * m_W;
1463 Qtransform.Invert();
1464
1465 return Qtransform;
1466}
1467
1477inline void MDNorm::calcDiffractionIntersectionIntegral(std::vector<std::array<double, 4>> &intersections,
1478 std::vector<double> &xValues, std::vector<double> &yValues,
1479 const API::MatrixWorkspace &integrFlux, const size_t &wsIdx) {
1480 // -- calculate integrals for the intersection --
1481 // momentum values at intersections
1482 auto intersectionsBegin = intersections.begin();
1483 // copy momenta to xValues
1484 xValues.resize(intersections.size());
1485 yValues.resize(intersections.size());
1486 auto x = xValues.begin();
1487 for (auto it = intersectionsBegin; it != intersections.end(); ++it, ++x) {
1488 *x = (*it)[3];
1489 }
1490 // calculate integrals at momenta from xValues by interpolating between
1491 // points in spectrum sp
1492 // of workspace integrFlux. The result is stored in yValues
1493 calcIntegralsForIntersections(xValues, integrFlux, wsIdx, yValues);
1494}
1495
1509inline void MDNorm::calcSingleDetectorNorm(const std::vector<std::array<double, 4>> &intersections, const double &solid,
1510 std::vector<double> &yValues, const size_t &vmdDims,
1511 std::vector<coord_t> &pos, std::vector<coord_t> &posNew,
1512 std::vector<std::atomic<signal_t>> &signalArray, const double &solidBkgd,
1513 std::vector<std::atomic<signal_t>> &bkgdSignalArray) {
1514
1515 auto intersectionsBegin = intersections.begin();
1516 for (auto it = intersectionsBegin + 1; it != intersections.end(); ++it) {
1517
1518 const auto &curIntSec = *it;
1519 const auto &prevIntSec = *(it - 1);
1520
1521 // The full vector isn't used so compute only what is necessary
1522 // If the difference between 2 adjacent intersection is trivial, no
1523 // intersection normalization is to be calculated
1524 double delta, eps;
1525 if (m_diffraction) {
1526 // diffraction
1527 delta = curIntSec[3] - prevIntSec[3];
1528 eps = 1e-7;
1529 } else {
1530 // inelastic
1531 delta = (curIntSec[3] * curIntSec[3] - prevIntSec[3] * prevIntSec[3]) / energyToK;
1532 eps = 1e-10;
1533 }
1534 if (delta < eps)
1535 continue; // Assume zero contribution if difference is small
1536
1537 // Average between two intersections for final position
1538 // [Task 89] Sample and background have same 'pos[]'
1539 std::transform(curIntSec.data(), curIntSec.data() + vmdDims, prevIntSec.data(), pos.begin(),
1540 [](const double rhs, const double lhs) { return static_cast<coord_t>(0.5 * (rhs + lhs)); });
1541 signal_t signal;
1542 signal_t bkgdSignal(0.);
1543 if (m_diffraction) {
1544 // Diffraction
1545 // index of the current intersection
1546 auto k = static_cast<size_t>(std::distance(intersectionsBegin, it));
1547 // signal = integral between two consecutive intersections
1548 signal = (yValues[k] - yValues[k - 1]) * solid;
1549 if (m_backgroundWS)
1550 bkgdSignal = (yValues[k] - yValues[k - 1]) * solidBkgd;
1551
1552 } else {
1553 // Inelastic
1554 // transform kf to energy transfer
1555 pos[3] = static_cast<coord_t>(m_Ei - pos[3] * pos[3] / energyToK);
1556 // signal = energy distance between two consecutive intersections *solid
1557 // angle *PC
1558 signal = solid * delta;
1559 if (m_backgroundWS)
1560 bkgdSignal = solidBkgd * delta;
1561 }
1562
1563 // Find the coordiate of the new position after transformation
1564 m_transformation.multiplyPoint(pos, posNew);
1565 // [Task 89] Is linIndex common to both sample and background?
1566 size_t linIndex = m_normWS->getLinearIndexAtCoord(posNew.data());
1567 if (linIndex == size_t(-1))
1568 continue; // not found
1569
1570 // Set to output
1571 // set the calculated signal to
1572 Mantid::Kernel::AtomicOp(signalArray[linIndex], signal, std::plus<signal_t>());
1573 // [Task 89]
1574 if (m_backgroundWS)
1575 Mantid::Kernel::AtomicOp(bkgdSignalArray[linIndex], bkgdSignal, std::plus<signal_t>());
1576 }
1577 return;
1578}
1579
1588void MDNorm::calculateNormalization(const std::vector<coord_t> &otherValues, const Geometry::SymmetryOperation &so,
1589 uint16_t expInfoIndex, size_t soIndex) {
1590 const auto &currentExptInfo = *(m_inputWS->getExperimentInfo(expInfoIndex));
1591 std::vector<double> lowValues, highValues;
1592 auto *lowValuesLog = dynamic_cast<VectorDoubleProperty *>(currentExptInfo.getLog("MDNorm_low"));
1593 lowValues = (*lowValuesLog)();
1594 auto *highValuesLog = dynamic_cast<VectorDoubleProperty *>(currentExptInfo.getLog("MDNorm_high"));
1595 highValues = (*highValuesLog)();
1596
1597 // calculate Q transformation matrix (R * UB * SymmetryOperation * m_W)^-1
1598 // in order to calculate intersections
1599 DblMatrix Qtransform = calQTransform(currentExptInfo, so);
1600
1601 // get proton charges
1602 const double protonCharge = currentExptInfo.run().getProtonCharge();
1603 // [Task 89]
1604 const double protonChargeBkgd =
1605 (m_backgroundWS != nullptr) ? m_backgroundWS->getExperimentInfo(0)->run().getProtonCharge() : 0;
1606
1607 const auto &spectrumInfo = currentExptInfo.spectrumInfo();
1608
1609 // Mappings: solid angle and flux workspaces' detector to ws_index map
1610 const auto ndets = static_cast<int64_t>(spectrumInfo.size());
1611 bool haveSA = false;
1612 API::MatrixWorkspace_const_sptr solidAngleWS = getProperty("SolidAngleWorkspace");
1613 if (solidAngleWS != nullptr) {
1614 haveSA = true;
1615 }
1616 API::MatrixWorkspace_const_sptr integrFlux = getProperty("FluxWorkspace");
1617 const detid2index_map solidAngDetToIdx =
1618 (haveSA) ? solidAngleWS->getDetectorIDToWorkspaceIndexMap() : detid2index_map();
1619 const detid2index_map fluxDetToIdx =
1620 (m_diffraction) ? integrFlux->getDetectorIDToWorkspaceIndexMap() : detid2index_map();
1621
1622 // Define dimension, signal array
1623 const size_t vmdDims = (m_diffraction) ? 3 : 4;
1624 std::vector<std::atomic<signal_t>> signalArray(m_normWS->getNPoints());
1625
1626 size_t numNPoints = (m_backgroundWS) ? m_bkgdNormWS->getNPoints() : 0;
1627 if (m_backgroundWS && numNPoints != m_normWS->getNPoints()) {
1628 throw std::runtime_error("N points are different");
1629 }
1630 std::vector<std::atomic<signal_t>> bkgdSignalArray(numNPoints);
1631
1632 std::vector<std::array<double, 4>> intersections;
1633 std::vector<double> xValues, yValues;
1634 std::vector<coord_t> pos, posNew;
1635
1636 // Progress report
1637 double progStep = 0.7 / static_cast<double>(m_numExptInfos * m_numSymmOps);
1638 auto progIndex = static_cast<double>(soIndex + expInfoIndex * m_numSymmOps);
1639 auto prog =
1640 std::make_unique<API::Progress>(this, 0.3 + progStep * progIndex, 0.3 + progStep * (1. + progIndex), ndets);
1641 // muliple threading
1642 bool safe = m_diffraction ? Kernel::threadSafe(*integrFlux) : true;
1643
1644PRAGMA_OMP(parallel for private(intersections, xValues, yValues, pos, posNew) if (safe))
1645for (int64_t i = 0; i < ndets; i++) {
1647
1648 // Skip: non-existing detector, monitor and masked detector
1649 if (!spectrumInfo.hasDetectors(i) || spectrumInfo.isMonitor(i) || spectrumInfo.isMasked(i)) {
1650 continue;
1651 }
1652
1653 const auto &detector = spectrumInfo.detector(i);
1654 double theta = detector.getTwoTheta(m_samplePos, m_beamDir);
1655 double phi = detector.getPhi();
1656 // If the dtefctor is a group, this should be the ID of the first detector
1657 const auto detID = detector.getID();
1658
1659 // get the flux spectrum number: this is for diffraction only!
1660 size_t wsIdx = 0;
1661 if (m_diffraction) {
1662 auto index = fluxDetToIdx.find(detID);
1663 if (index != fluxDetToIdx.end()) {
1664 wsIdx = index->second;
1665 } else { // masked detector in flux, but not in input workspace
1666 continue;
1667 }
1668 }
1669
1670 // Intersections for sample and background if present
1671 this->calculateIntersections(intersections, theta, phi, Qtransform, lowValues[i], highValues[i]);
1672
1673 // No need to do normalization calculation if there is no intersection
1674 if (intersections.empty())
1675 continue;
1676
1677 // Get solid angle for this contribution
1678 double solid = protonCharge;
1679 // [Task 89]
1680 double bkgdSolid = protonChargeBkgd;
1681 if (haveSA) {
1682 double solid_angle_factor = solidAngleWS->y(solidAngDetToIdx.find(detID)->second)[0];
1683 // solidAngleWS->y(solidAngDetToIdx.find(detID)->second)[0]
1684 solid = solid_angle_factor * protonCharge;
1685 // [Task 89]
1686 bkgdSolid = solid_angle_factor * protonChargeBkgd;
1687 }
1688
1689 if (m_diffraction) {
1690 // -- calculate integrals for the intersection --
1691 calcDiffractionIntersectionIntegral(intersections, xValues, yValues, *integrFlux, wsIdx);
1692 }
1693
1694 // Compute final position in HKL
1695 // pre-allocate for efficiency and copy non-hkl dim values into place
1696 pos.resize(vmdDims + otherValues.size());
1697 std::copy(otherValues.begin(), otherValues.end(), pos.begin() + vmdDims);
1698
1699 calcSingleDetectorNorm(intersections, solid, yValues, vmdDims, pos, posNew, signalArray, bkgdSolid,
1700 bkgdSignalArray); // [Task 89] ADD solidBkgd, bkgdYValues, bkgdSignalArray
1701
1702 prog->report();
1703
1705}
1707if (m_accumulate) {
1708 std::transform(signalArray.cbegin(), signalArray.cend(), m_normWS->getSignalArray(), m_normWS->mutableSignalArray(),
1709 [](const std::atomic<signal_t> &a, const signal_t &b) { return a + b; });
1710 // [Task 89] Process background
1711 if (m_backgroundWS)
1712 std::transform(bkgdSignalArray.cbegin(), bkgdSignalArray.cend(), m_bkgdNormWS->getSignalArray(),
1713 m_bkgdNormWS->mutableSignalArray(),
1714 [](const std::atomic<signal_t> &a, const signal_t &b) { return a + b; });
1715
1716} else {
1717 // First time, init
1718 std::copy(signalArray.cbegin(), signalArray.cend(), m_normWS->mutableSignalArray());
1719 // [Task 89]
1720 if (m_backgroundWS)
1721 std::copy(bkgdSignalArray.cbegin(), bkgdSignalArray.cend(), m_bkgdNormWS->mutableSignalArray());
1722}
1723m_accumulate = true;
1724}
1725
1736void MDNorm::calculateIntersections(std::vector<std::array<double, 4>> &intersections, const double theta,
1737 const double phi, const Kernel::DblMatrix &transform, double lowvalue,
1738 double highvalue) {
1739 V3D qout(sin(theta) * cos(phi), sin(theta) * sin(phi), cos(theta)), qin(0., 0., 1);
1740
1741 qout = transform * qout;
1742 qin = transform * qin;
1743 if (convention == "Crystallography") {
1744 qout *= -1;
1745 qin *= -1;
1746 }
1747 double kfmin, kfmax, kimin, kimax;
1748 if (m_diffraction) {
1749 kimin = lowvalue;
1750 kimax = highvalue;
1751 kfmin = kimin;
1752 kfmax = kimax;
1753 } else {
1754 kimin = std::sqrt(energyToK * m_Ei);
1755 kimax = kimin;
1756 kfmin = std::sqrt(energyToK * (m_Ei - highvalue));
1757 kfmax = std::sqrt(energyToK * (m_Ei - lowvalue));
1758 }
1759
1760 double hStart = qin.X() * kimin - qout.X() * kfmin, hEnd = qin.X() * kimax - qout.X() * kfmax;
1761 double kStart = qin.Y() * kimin - qout.Y() * kfmin, kEnd = qin.Y() * kimax - qout.Y() * kfmax;
1762 double lStart = qin.Z() * kimin - qout.Z() * kfmin, lEnd = qin.Z() * kimax - qout.Z() * kfmax;
1763
1764 double eps = 1e-10;
1765 auto hNBins = m_hX.size();
1766 auto kNBins = m_kX.size();
1767 auto lNBins = m_lX.size();
1768 auto eNBins = m_eX.size();
1769 intersections.clear();
1770 intersections.reserve(hNBins + kNBins + lNBins + eNBins + 2);
1771
1772 // calculate intersections with planes perpendicular to h
1773 if (fabs(hStart - hEnd) > eps) {
1774 double fmom = (kfmax - kfmin) / (hEnd - hStart);
1775 double fk = (kEnd - kStart) / (hEnd - hStart);
1776 double fl = (lEnd - lStart) / (hEnd - hStart);
1777 for (size_t i = 0; i < hNBins; i++) {
1778 double hi = m_hX[i];
1779 if (((hStart - hi) * (hEnd - hi) < 0)) {
1780 // if hi is between hStart and hEnd, then ki and li will be between
1781 // kStart, kEnd and lStart, lEnd and momi will be between kfmin and
1782 // kfmax
1783 double ki = fk * (hi - hStart) + kStart;
1784 double li = fl * (hi - hStart) + lStart;
1785 if ((ki >= m_kX[0]) && (ki <= m_kX[kNBins - 1]) && (li >= m_lX[0]) && (li <= m_lX[lNBins - 1])) {
1786 double momi = fmom * (hi - hStart) + kfmin;
1787 intersections.push_back({{hi, ki, li, momi}});
1788 }
1789 }
1790 }
1791 }
1792 // calculate intersections with planes perpendicular to k
1793 if (fabs(kStart - kEnd) > eps) {
1794 double fmom = (kfmax - kfmin) / (kEnd - kStart);
1795 double fh = (hEnd - hStart) / (kEnd - kStart);
1796 double fl = (lEnd - lStart) / (kEnd - kStart);
1797 for (size_t i = 0; i < kNBins; i++) {
1798 double ki = m_kX[i];
1799 if (((kStart - ki) * (kEnd - ki) < 0)) {
1800 // if ki is between kStart and kEnd, then hi and li will be between
1801 // hStart, hEnd and lStart, lEnd and momi will be between kfmin and
1802 // kfmax
1803 double hi = fh * (ki - kStart) + hStart;
1804 double li = fl * (ki - kStart) + lStart;
1805 if ((hi >= m_hX[0]) && (hi <= m_hX[hNBins - 1]) && (li >= m_lX[0]) && (li <= m_lX[lNBins - 1])) {
1806 double momi = fmom * (ki - kStart) + kfmin;
1807 intersections.push_back({{hi, ki, li, momi}});
1808 }
1809 }
1810 }
1811 }
1812
1813 // calculate intersections with planes perpendicular to l
1814 if (fabs(lStart - lEnd) > eps) {
1815 double fmom = (kfmax - kfmin) / (lEnd - lStart);
1816 double fh = (hEnd - hStart) / (lEnd - lStart);
1817 double fk = (kEnd - kStart) / (lEnd - lStart);
1818
1819 for (size_t i = 0; i < lNBins; i++) {
1820 double li = m_lX[i];
1821 if (((lStart - li) * (lEnd - li) < 0)) {
1822 double hi = fh * (li - lStart) + hStart;
1823 double ki = fk * (li - lStart) + kStart;
1824 if ((hi >= m_hX[0]) && (hi <= m_hX[hNBins - 1]) && (ki >= m_kX[0]) && (ki <= m_kX[kNBins - 1])) {
1825 double momi = fmom * (li - lStart) + kfmin;
1826 intersections.push_back({{hi, ki, li, momi}});
1827 }
1828 }
1829 }
1830 }
1831 // intersections with dE
1832 if (!m_dEIntegrated) {
1833 for (size_t i = 0; i < eNBins; i++) {
1834 double kfi = m_eX[i];
1835 if ((kfi - kfmin) * (kfi - kfmax) <= 0) {
1836 double h = qin.X() * kimin - qout.X() * kfi;
1837 double k = qin.Y() * kimin - qout.Y() * kfi;
1838 double l = qin.Z() * kimin - qout.Z() * kfi;
1839 if ((h >= m_hX[0]) && (h <= m_hX[hNBins - 1]) && (k >= m_kX[0]) && (k <= m_kX[kNBins - 1]) && (l >= m_lX[0]) &&
1840 (l <= m_lX[lNBins - 1])) {
1841 intersections.push_back({{h, k, l, kfi}});
1842 }
1843 }
1844 }
1845 }
1846
1847 // endpoints
1848 if ((hStart >= m_hX[0]) && (hStart <= m_hX[hNBins - 1]) && (kStart >= m_kX[0]) && (kStart <= m_kX[kNBins - 1]) &&
1849 (lStart >= m_lX[0]) && (lStart <= m_lX[lNBins - 1])) {
1850 intersections.push_back({{hStart, kStart, lStart, kfmin}});
1851 }
1852 if ((hEnd >= m_hX[0]) && (hEnd <= m_hX[hNBins - 1]) && (kEnd >= m_kX[0]) && (kEnd <= m_kX[kNBins - 1]) &&
1853 (lEnd >= m_lX[0]) && (lEnd <= m_lX[lNBins - 1])) {
1854 intersections.push_back({{hEnd, kEnd, lEnd, kfmax}});
1855 }
1856
1857 // sort intersections by final momentum
1858 std::stable_sort(intersections.begin(), intersections.end(), compareMomentum);
1859}
1860
1869void MDNorm::calcIntegralsForIntersections(const std::vector<double> &xValues, const API::MatrixWorkspace &integrFlux,
1870 size_t sp, std::vector<double> &yValues) {
1871 assert(xValues.size() == yValues.size());
1872
1873 // the x-data from the workspace
1874 const auto &xData = integrFlux.x(sp);
1875 const double xStart = xData.front();
1876 const double xEnd = xData.back();
1877
1878 // the values in integrFlux are expected to be integrals of a non-negative
1879 // function
1880 // ie they must make a non-decreasing function
1881 const auto &yData = integrFlux.y(sp);
1882 size_t spSize = yData.size();
1883
1884 const double yMin = 0.0;
1885 const double yMax = yData.back();
1886
1887 size_t nData = xValues.size();
1888 // all integrals below xStart must be 0
1889 if (xValues[nData - 1] < xStart) {
1890 std::fill(yValues.begin(), yValues.end(), yMin);
1891 return;
1892 }
1893
1894 // all integrals above xEnd must be equal tp yMax
1895 if (xValues[0] > xEnd) {
1896 std::fill(yValues.begin(), yValues.end(), yMax);
1897 return;
1898 }
1899
1900 size_t i = 0;
1901 // integrals below xStart must be 0
1902 while (i < nData - 1 && xValues[i] < xStart) {
1903 yValues[i] = yMin;
1904 i++;
1905 }
1906 size_t j = 0;
1907 for (; i < nData; i++) {
1908 // integrals above xEnd must be equal tp yMax
1909 if (j >= spSize - 1) {
1910 yValues[i] = yMax;
1911 } else {
1912 double xi = xValues[i];
1913 while (j < spSize - 1 && xi > xData[j])
1914 j++;
1915 // if x falls onto an interpolation point return the corresponding y
1916 if (xi == xData[j]) {
1917 yValues[i] = yData[j];
1918 } else if (j == spSize - 1) {
1919 // if we get above xEnd it's yMax
1920 yValues[i] = yMax;
1921 } else if (j > 0) {
1922 // interpolate between the consecutive points
1923 double x0 = xData[j - 1];
1924 double x1 = xData[j];
1925 double y0 = yData[j - 1];
1926 double y1 = yData[j];
1927 yValues[i] = y0 + (y1 - y0) * (xi - x0) / (x1 - x0);
1928 } else // j == 0
1929 {
1930 yValues[i] = yMin;
1931 }
1932 }
1933 }
1934}
1935
1936} // 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
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.
void declareProperty(std::unique_ptr< Kernel::Property > p, const std::string &doc="") override
Add a property to the list of managed properties.
std::string getPropertyValue(const std::string &name) const override
Get the value of a property as a string.
TypedValue getProperty(const std::string &name) const override
Get the value of a property.
virtual std::shared_ptr< Algorithm > createChildAlgorithm(const std::string &name, const double startProgress=-1., const double endProgress=-1., const bool enableLogging=true, const int &version=-1)
Create a Child Algorithm.
Kernel::Logger & g_log
Definition Algorithm.h:423
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.
This class is shared by a few Workspace types and holds information related to a particular experimen...
const Run & run() const
Run details object access.
A validator which checks that a workspace has a valid instrument.
Base MatrixWorkspace Abstract Class.
const HistogramData::HistogramX & x(const size_t index) const
const HistogramData::HistogramY & y(const size_t index) const
const Kernel::Matrix< double > & getGoniometerMatrix() const
Retrieve the first goniometer rotation matrix.
Definition Run.cpp:570
A property class for workspaces.
std::unique_ptr< MDHistoWorkspace > clone() const
Returns a clone of the workspace.
static const std::string HKLName
Definition HKL.h:26
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
Definition QLab.h:34
static const std::string QSampleName
Definition QSample.h:22
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)
Definition UnitCell.cpp:94
double c() const
Get lattice parameter.
Definition UnitCell.cpp:128
double b() const
Get lattice parameter.
Definition UnitCell.cpp:123
Support for a property that holds an array of values.
Exception for errors associated with the instrument definition.
Definition Exception.h:220
Records the filename, the description of failure and the line on which it happened.
Definition Exception.h:115
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.
Definition Logger.cpp:145
void error(const std::string &msg)
Logs at error level.
Definition Logger.cpp:108
void warning(const std::string &msg)
Logs at warning level.
Definition Logger.cpp:117
T determinant() const
Calculate the determinant.
Definition Matrix.cpp:1048
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
std::vector< T > getVector() const
Definition Matrix.cpp:77
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
Matrix< T > & Transpose()
Transpose the matrix.
Definition Matrix.cpp:793
The concrete, templated class for properties.
Base class for properties.
Definition Property.h:94
virtual std::string value() const =0
Returns the value of the property as a string.
static const UnitLabel RLU
Reciprocal lattice units.
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
MDNormalization : Bin single crystal diffraction or direct geometry inelastic data and calculate the ...
Definition MDNorm.h:21
size_t m_numSymmOps
number of symmetry operations
Definition MDNorm.h:127
bool m_monochromatic
Flag indicating a pre-computed MonoSCDNormalizationWorkspace was provided (monochromatic single cryst...
Definition MDNorm.h:134
Mantid::Kernel::DblMatrix m_W
W matrix.
Definition MDNorm.h:116
void calcSingleDetectorNorm(const std::vector< std::array< double, 4 > > &intersections, const double &solid, std::vector< double > &yValues, const size_t &vmdDims, std::vector< coord_t > &pos, std::vector< coord_t > &posNew, std::vector< std::atomic< signal_t > > &signalArray, const double &solidBkgd, std::vector< std::atomic< signal_t > > &bkgdSignalArray)
Calculate the normalization among intersections on a single detector in 1 specific SpectrumInfo/Exper...
Definition MDNorm.cpp:1509
Mantid::Kernel::Matrix< coord_t > m_transformation
matrix for transforming from intersections to positions in the normalization workspace
Definition MDNorm.h:119
bool m_dEIntegrated
Flag to indicate that the energy dimension is integrated.
Definition MDNorm.h:138
DataObjects::MDHistoWorkspace_sptr binBackgroundWS(const std::vector< Geometry::SymmetryOperation > &symmetryOps)
Bin(MD) input Background workspace.
Definition MDNorm.cpp:1154
std::string QDimensionName(std::vector< double > projection)
Get the dimension name when using reciprocal lattice units.
Definition MDNorm.cpp:669
bool m_diffraction
Flag indicating if the input workspace is from diffraction.
Definition MDNorm.h:131
void createNormalizationWS(const DataObjects::MDHistoWorkspace &dataWS)
Create & cached the normalization workspace.
Definition MDNorm.cpp:863
void exec() override
Execute the algorithm.
Definition MDNorm.cpp:499
bool m_accumulate
Flag to accumulate normalization.
Definition MDNorm.h:136
void calcIntegralsForIntersections(const std::vector< double > &xValues, const API::MatrixWorkspace &integrFlux, size_t sp, std::vector< double > &yValues)
Linearly interpolate between the points in integrFlux at xValues and save the results in yValues.
Definition MDNorm.cpp:1869
std::vector< double > m_Q0Basis
The projection vectors.
Definition MDNorm.h:112
DataObjects::MDHistoWorkspace_sptr m_normWS
Normalization workspace.
Definition MDNorm.h:102
const std::string summary() const override
Algorithm's summary for use in the GUI and help.
Definition MDNorm.cpp:78
std::map< std::string, std::string > getBinParameters()
Calculate binning parameters.
Definition MDNorm.cpp:698
API::IMDWorkspace_sptr divideMD(const API::IMDHistoWorkspace_sptr &lhs, const API::IMDHistoWorkspace_sptr &rhs, const std::string &outputwsname, const double &startProgress, const double &endProgress)
Definition MDNorm.cpp:634
const std::string category() const override
Algorithm's category for identification.
Definition MDNorm.cpp:75
std::vector< double > m_lX
Definition MDNorm.h:121
std::vector< double > m_kX
Definition MDNorm.h:121
std::vector< coord_t > getValuesFromOtherDimensions(bool &skipNormalization, uint16_t expInfoIndex=0) const
Retrieve logged values from non-HKL dimensions.
Definition MDNorm.cpp:1373
void setQUnit(const std::vector< size_t > &qDimensionIndices, const Mantid::DataObjects::MDHistoWorkspace_sptr &outputMDHWS)
Set the output Frame to HKL.
Definition MDNorm.cpp:1133
DataObjects::MDHistoWorkspace_sptr m_bkgdNormWS
Definition MDNorm.h:103
void calculateIntersections(std::vector< std::array< double, 4 > > &intersections, const double theta, const double phi, const Kernel::DblMatrix &transform, double lowvalue, double highvalue)
Calculate the points of intersection for the given detector with cuboid surrounding the detector posi...
Definition MDNorm.cpp:1736
API::IMDEventWorkspace_sptr m_inputWS
Input workspace.
Definition MDNorm.h:105
double m_Ei
Cached value of incident energy dor direct geometry.
Definition MDNorm.h:129
size_t m_hIdx
index of h,k,l, dE dimensions in the output workspaces
Definition MDNorm.h:123
Mantid::Kernel::DblMatrix m_UB
UB matrix.
Definition MDNorm.h:114
std::vector< double > m_Q1Basis
Definition MDNorm.h:112
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.
Definition MDNorm.cpp:898
Mantid::Kernel::DblMatrix buildSymmetryMatrix(const Geometry::SymmetryOperation &so)
build symmetry matrix
Definition MDNorm.cpp:1057
Kernel::V3D m_beamDir
Beam direction.
Definition MDNorm.h:142
size_t m_numExptInfos
number of experimentInfo objects
Definition MDNorm.h:125
void calcDiffractionIntersectionIntegral(std::vector< std::array< double, 4 > > &intersections, std::vector< double > &xValues, std::vector< double > &yValues, const API::MatrixWorkspace &integrFlux, const size_t &wsIdx)
Calculate the diffraction MDE's intersection integral of a certain detector/spectru.
Definition MDNorm.cpp:1477
DataObjects::MDHistoWorkspace_sptr binMonoSCDNormalizationWS(const std::vector< Geometry::SymmetryOperation > &symmetryOps)
Bin(MD) MonoSCDNormalizationWorkspace (monochromatic single crystal diffraction)
Definition MDNorm.cpp:1267
std::string QDimensionNameQSample(int i)
Get the dimension name when not using reciprocal lattice units.
Definition MDNorm.cpp:653
void cacheDimensionXValues()
Stores the X values from each H,K,L, and optionally DeltaE dimension as member variables.
Definition MDNorm.cpp:1414
std::vector< double > m_hX
cached X values along dimensions h,k,l. dE
Definition MDNorm.h:121
const std::string name() const override
Algorithms name for identification.
Definition MDNorm.cpp:69
std::vector< double > m_Q2Basis
Definition MDNorm.h:112
bool m_isRLU
flag for reciprocal lattice units
Definition MDNorm.h:110
DataObjects::MDHistoWorkspace_sptr binInputWS(const std::vector< Geometry::SymmetryOperation > &symmetryOps)
Bin(MD) input MDE workspace.
Definition MDNorm.cpp:1254
void calculateNormalization(const std::vector< coord_t > &otherValues, const Geometry::SymmetryOperation &so, uint16_t expInfoIndex, size_t soIndex)
Computed the normalization for the input workspace.
Definition MDNorm.cpp:1588
std::string convention
ki-kf for Inelastic convention; kf-ki for Crystallography convention
Definition MDNorm.h:144
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 > &parameters)
Bin(MD), per symmetry operation, an MDEventWorkspace using pre-computed bin parameters.
Definition MDNorm.cpp:1289
Mantid::Kernel::DblMatrix calQTransform(const Mantid::API::ExperimentInfo &currentExpInfo, const Geometry::SymmetryOperation &so)
Calculate QTransform = (R * UB * SymmetryOperation * m_W)^-1.
Definition MDNorm.cpp:1450
std::map< std::string, std::string > validateInputs() override final
Validate the input workspace.
Definition MDNorm.cpp:223
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.
Definition MDNorm.cpp:1083
int version() const override
Algorithm's version for identification.
Definition MDNorm.cpp:72
void init() override
Initialize the algorithm's properties.
Definition MDNorm.cpp:86
API::IMDEventWorkspace_sptr m_backgroundWS
Input background workspace.
Definition MDNorm.h:107
std::vector< double > m_eX
Definition MDNorm.h:121
void createBackgroundNormalizationWS(const DataObjects::MDHistoWorkspace &dataWS)
Definition MDNorm.cpp:876
Kernel::V3D m_samplePos
Sample position.
Definition MDNorm.h:140
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.
Definition IMDIterator.h:27
std::shared_ptr< MDHistoWorkspace > MDHistoWorkspace_sptr
A shared pointer to a MDHistoWorkspace.
std::unique_ptr< MDFrame > MDFrame_uptr
Definition MDFrame.h:36
MDFrameFactory_uptr MANTID_GEOMETRY_DLL makeMDFrameFactoryChain()
Make a complete factory chain.
std::string toString(const T &value)
Convert a number to a string.
Definition Strings.cpp:734
template DLLExport std::vector< size_t > splitStringIntoVector< size_t >(std::string listString, const std::string &separator)
template DLLExport std::vector< double > splitStringIntoVector< double >(std::string listString, const std::string &separator)
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.
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...
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.
double signal_t
Typedef for the signal recorded in a MDBox, etc.
Definition MDTypes.h:36
@ Input
An input workspace.
Definition Property.h:53
@ Output
An output workspace.
Definition Property.h:54