Mantid
Loading...
Searching...
No Matches
SlicingAlgorithm.cpp
Go to the documentation of this file.
1// Mantid Repository : https://github.com/mantidproject/mantid
2//
3// Copyright © 2018 ISIS Rutherford Appleton Laboratory UKRI,
4// NScD Oak Ridge National Laboratory, European Spallation Source,
5// Institut Laue - Langevin & CSNS, Institute of High Energy Physics, CAS
6// SPDX - License - Identifier: GPL - 3.0 +
8#include "MantidAPI/Run.h"
19
20#include <boost/regex.hpp>
21#include <utility>
22
23using namespace Mantid::Kernel;
24using namespace Mantid::API;
25using namespace Mantid::Geometry;
27
28namespace Mantid::MDAlgorithms {
29
33 : m_transform(), m_transformFromOriginal(), m_transformToOriginal(), m_transformFromIntermediate(),
34 m_transformToIntermediate(), m_axisAligned(true), m_outD(0), // unititialized and should be invalid
35 m_NormalizeBasisVectors(false) {}
36
40 std::string dimChars = getDimensionChars();
41
42 // --------------- Axis-aligned properties
43 // ---------------------------------------
44 declareProperty("AxisAligned", true, "Perform binning aligned with the axes of the input MDEventWorkspace?");
45 setPropertyGroup("AxisAligned", "Axis-Aligned Binning");
46 for (size_t i = 0; i < dimChars.size(); i++) {
47 std::string dim(" ");
48 dim[0] = dimChars[i];
49 std::string propName = "AlignedDim" + dim;
51 "Binning parameters for the " + Strings::toString(i) +
52 "th dimension.\n"
53 "Enter it as a comma-separated list of values with the format: "
54 "'name,minimum,maximum,number_of_bins'. Leave blank for NONE.");
55 setPropertySettings(propName, std::make_unique<VisibleWhenProperty>("AxisAligned", IS_EQUAL_TO, "1"));
56 setPropertyGroup(propName, "Axis-Aligned Binning");
57 }
58
59 // --------------- NON-Axis-aligned properties
60 // ---------------------------------------
61 std::string grpName = "Non-Aligned Binning";
62
63 auto ps = [] {
64 std::unique_ptr<IPropertySettings> settings =
65 std::make_unique<VisibleWhenProperty>("AxisAligned", IS_EQUAL_TO, "0");
66 return settings;
67 };
68 for (size_t i = 0; i < dimChars.size(); i++) {
69 std::string dim(" ");
70 dim[0] = dimChars[i];
71 std::string propName = "BasisVector" + dim;
73 "Description of the basis vector of the " + Strings::toString(i) +
74 "th output dimension."
75 "Format: 'name, units, x,y,z,..'.\n"
76 " name : string for the name of the output dimension.\n"
77 " units : string for the units of the output dimension.\n"
78 " x,y,z,...: vector definining the basis in the input dimensions "
79 "space.\n"
80 "Leave blank for NONE.");
81 setPropertySettings(propName, ps());
82 setPropertyGroup(propName, grpName);
83 }
84 declareProperty(std::make_unique<ArrayProperty<double>>("Translation", Direction::Input),
85 "Coordinates in the INPUT workspace that corresponds to "
86 "(0,0,0) in the OUTPUT workspace.\n"
87 "Enter as a comma-separated string.\n"
88 "Default: 0 in all dimensions (no translation).");
89
90 declareProperty(std::make_unique<ArrayProperty<double>>("OutputExtents", Direction::Input),
91 "The minimum, maximum edges of space of each dimension of "
92 "the OUTPUT workspace, as a comma-separated list");
93
94 declareProperty(std::make_unique<ArrayProperty<int>>("OutputBins", Direction::Input),
95 "The number of bins for each dimension of the OUTPUT workspace.");
96
97 declareProperty(std::make_unique<PropertyWithValue<bool>>("NormalizeBasisVectors", true, Direction::Input),
98 "Normalize the given basis vectors to unity. \n"
99 "If true, then a distance of 1 in the INPUT dimensions = 1 "
100 "in the OUTPUT dimensions.\n"
101 "If false, then a distance of norm(basis_vector) in the "
102 "INPUT dimension = 1 in the OUTPUT dimensions.");
103
104 declareProperty(std::make_unique<PropertyWithValue<bool>>("ForceOrthogonal", false, Direction::Input),
105 "Force the input basis vectors to form an orthogonal coordinate system. "
106 "Only works in 3 dimension!");
107
108 // For GUI niceness
109 setPropertyGroup("Translation", grpName);
110 setPropertyGroup("OutputExtents", grpName);
111 setPropertyGroup("OutputBins", grpName);
112 setPropertyGroup("NormalizeBasisVectors", grpName);
113 setPropertyGroup("ForceOrthogonal", grpName);
114 setPropertySettings("Translation", ps());
115 setPropertySettings("OutputExtents", ps());
116 setPropertySettings("OutputBins", ps());
117 setPropertySettings("NormalizeBasisVectors", ps());
118 setPropertySettings("ForceOrthogonal", ps());
119}
120
121//----------------------------------------------------------------------------------------------
135 std::string input = Strings::strip(str);
136 if (input.empty())
137 return;
138 if (input.size() < 3)
139 throw std::invalid_argument("Dimension string is too short to be valid: " + str);
140
141 // The current dimension index.
142 size_t dim = m_binDimensions.size();
143
144 size_t n_first_comma;
145
146 // Special case: accept dimension names [x,y,z]
147 if (input[0] == '[') {
148 // Find the name at the closing []
149 size_t n = input.find_first_of(']', 1);
150 if (n == std::string::npos)
151 throw std::invalid_argument("No closing ] character in the dimension name of : " + str);
152 // Find the comma after the name
153 n_first_comma = input.find_first_of(',', n);
154 if (n_first_comma == std::string::npos)
155 throw std::invalid_argument("No comma after the closing ] character in the dimension string: " + str);
156 } else
157 // Find the comma after the name
158 n_first_comma = input.find_first_of(',');
159
160 if (n_first_comma == std::string::npos)
161 throw std::invalid_argument("No comma in the dimension string: " + str);
162 if (n_first_comma == input.size() - 1)
163 throw std::invalid_argument("Dimension string ends in a comma: " + str);
164
165 // Get the entire name
166 std::string name = Strings::strip(input.substr(0, n_first_comma));
167 if (name.empty())
168 throw std::invalid_argument("name should not be blank.");
169
170 // Now remove the name and comma
171 // And split the rest of it
172 input = input.substr(n_first_comma + 1);
173 std::vector<std::string> strs;
174 boost::split(strs, input, boost::is_any_of(","));
175 if (strs.size() != this->m_inWS->getNumDims() + 1)
176 throw std::invalid_argument("Wrong number of values (expected 2 + # of "
177 "input dimensions) in the dimensions string: " +
178 str);
179
180 // Get the number of bins from
181 int numBins = m_numBins[dim];
182 if (numBins < 1)
183 throw std::invalid_argument("Number of bins for output dimension " + Strings::toString(dim) + " should be >= 1.");
184
185 // Get the min/max extents in this OUTPUT dimension
186 double min = m_minExtents[dim];
187 double max = m_maxExtents[dim];
188 double lengthInOutput = max - min;
189 if (lengthInOutput <= 0)
190 throw std::invalid_argument("The maximum extents for dimension " + Strings::toString(dim) + " should be > 0.");
191
192 // Create the basis vector with the right # of dimensions
193 VMD basis(this->m_inWS->getNumDims());
194 for (size_t d = 0; d < this->m_inWS->getNumDims(); d++)
195 if (!Strings::convert(strs[d + 1], basis[d]))
196 throw std::invalid_argument("Error converting argument '" + strs[d + 1] + "' in the dimensions string '" + str +
197 "' to a number.");
198
199 // If B was binned from A (m_originalWS), and we are binning C from B,
200 // convert the basis vector from B space -> A space
201 if (m_originalWS) {
202 // Turn basis vector into two points
203 VMD basis0(this->m_inWS->getNumDims());
204 VMD basis1 = basis;
205 // Convert the points to the original coordinates (from inWS to originalWS)
206 CoordTransform const *toOrig = m_inWS->getTransformToOriginal();
207 VMD origBasis0 = toOrig->applyVMD(basis0);
208 VMD origBasis1 = toOrig->applyVMD(basis1);
209 // New basis vector, now in the original workspace
210 basis = origBasis1 - origBasis0;
211 }
212
213 // Check on the length of the basis vector
214 double basisLength = basis.norm();
215 if (basisLength <= 0)
216 throw std::invalid_argument("direction should not be 0-length.");
217
218 // Normalize it to unity, if desized
219 double transformScaling = 1.0;
221 // A distance of 1 in the INPUT space = a distance of 1.0 in the OUTPUT
222 // space
223 basis.normalize();
224 transformScaling = 1.0;
225 } else
226 // A distance of |basisVector| in the INPUT space = a distance of 1.0 in the
227 // OUTPUT space
228 transformScaling = (1.0 / basisLength);
229
230 // This is the length of this dimension as measured in the INPUT space
231 double lengthInInput = lengthInOutput / transformScaling;
232
233 // Scaling factor, to convert from units in the INPUT dimensions to the output
234 // BIN number
235 double binningScaling = double(numBins) / (lengthInInput);
236
237 // Extract the arguments
238 std::string units = Strings::strip(strs[0]);
239
240 // Create the appropriate frame
241 auto frame = createMDFrameForNonAxisAligned(units, basis);
242
243 // Create the output dimension
244 auto out = std::make_shared<MDHistoDimension>(name, name, *frame, static_cast<coord_t>(min),
245 static_cast<coord_t>(max), numBins);
246
247 // Put both in the algo for future use
248 m_bases.emplace_back(basis);
249 m_binDimensions.emplace_back(std::move(out));
250 m_binningScaling.emplace_back(binningScaling);
251 m_transformScaling.emplace_back(transformScaling);
252}
253
254//----------------------------------------------------------------------------------------------
261 // Count the number of output dimensions
262 m_outD = 0;
263 std::string dimChars = this->getDimensionChars();
264 for (char dimChar : dimChars) {
265 std::string propName = "BasisVector0";
266 propName[11] = dimChar;
267 if (!Strings::strip(this->getPropertyValue(propName)).empty())
268 m_outD++;
269 }
270
271 std::vector<double> extents = this->getProperty("OutputExtents");
272 if (extents.size() != m_outD * 2)
273 throw std::invalid_argument("The OutputExtents parameter must have " + Strings::toString(m_outD * 2) +
274 " entries "
275 "(2 for each dimension in the OUTPUT workspace).");
276
277 m_minExtents.clear();
278 m_maxExtents.clear();
279 for (size_t d = 0; d < m_outD; d++) {
280 m_minExtents.emplace_back(extents[d * 2]);
281 m_maxExtents.emplace_back(extents[d * 2 + 1]);
282 }
283
284 m_numBins = this->getProperty("OutputBins");
285 if (m_numBins.size() != m_outD)
286 throw std::invalid_argument("The OutputBins parameter must have 1 entry "
287 "for each dimension in the OUTPUT workspace.");
288
289 m_NormalizeBasisVectors = this->getProperty("NormalizeBasisVectors");
290 m_transformScaling.clear();
291
292 // Create the dimensions based on the strings from the user
293 for (char dimChar : dimChars) {
294 std::string propName = "BasisVector0";
295 propName[11] = dimChar;
296 try {
298 } catch (std::exception &e) {
299 throw std::invalid_argument("Error parsing the " + propName + " parameter: " + std::string(e.what()));
300 }
301 }
302
303 // Number of output binning dimensions found
304 m_outD = m_binDimensions.size();
305 if (m_outD == 0)
306 throw std::runtime_error("No output dimensions were found in the MDEventWorkspace. Cannot bin!");
307
308 // Get the Translation parameter
309 std::vector<double> translVector;
310 try {
311 translVector = getProperty("Translation");
312 } catch (std::exception &e) {
313 throw std::invalid_argument("Error parsing the Translation parameter: " + std::string(e.what()));
314 }
315
316 // Default to 0,0,0 when not specified
317 if (translVector.empty())
318 translVector.resize(m_inWS->getNumDims(), 0);
319 m_translation = VMD(translVector);
320
321 if (m_translation.getNumDims() != m_inWS->getNumDims())
322 throw std::invalid_argument("The number of dimensions in the Translation parameter is "
323 "not consistent with the number of dimensions in the input workspace.");
324
325 // Validate
326 if (m_outD > m_inWS->getNumDims())
327 throw std::runtime_error("More output dimensions were specified than input dimensions "
328 "exist in the MDEventWorkspace. Cannot bin!");
329 if (m_binningScaling.size() != m_outD)
330 throw std::runtime_error("Inconsistent number of entries in scaling vector.");
331}
332
333//----------------------------------------------------------------------------------------------
338 // Process all the input properties
340
341 // Number of input dimensions
342 size_t inD = m_inWS->getNumDims();
343
344 // ----- Make the basis vectors orthogonal -------------------------
345 bool ForceOrthogonal = getProperty("ForceOrthogonal");
346 if (ForceOrthogonal && m_bases[0].getNumDims() == 3 && m_bases.size() >= 2) {
347 std::vector<VMD> firstTwo = m_bases;
348 firstTwo.resize(2, VMD(3));
349 std::vector<VMD> ortho = VMD::makeVectorsOrthogonal(firstTwo);
350 // Set the bases back
351 ortho.resize(m_bases.size(), VMD(3));
352 m_bases = ortho;
353 g_log.information() << "Basis vectors forced to be orthogonal: ";
354 for (auto &base : m_bases)
355 g_log.information() << base.toString(",") << "; ";
356 g_log.information() << '\n';
357 }
358
359 // Now, convert the original vector to the coordinates of the ORIGNAL ws, if
360 // any
361 if (m_originalWS) {
362 CoordTransform const *toOrig = m_inWS->getTransformToOriginal();
364 }
365
366 // OK now find the min/max coordinates of the edges in the INPUT workspace
368 for (size_t d = 0; d < m_outD; d++) {
369 // Translate from the outCoords=(0,0,0) to outCoords=(min,min,min)
370 m_inputMinPoint += (m_bases[d] * m_binDimensions[d]->getMinimum());
371 }
372
373 // Create the CoordTransformAffine for BINNING with these basis vectors
374 auto ct = std::make_unique<DataObjects::CoordTransformAffine>(inD, m_outD);
375 // Note: the scaling makes the coordinate correspond to a bin index
376 ct->buildNonOrthogonal(m_inputMinPoint, this->m_bases, VMD(this->m_binningScaling) / VMD(m_transformScaling));
377 this->m_transform = std::move(ct);
378
379 // Transformation original->binned
380 auto ctFrom = std::make_unique<DataObjects::CoordTransformAffine>(inD, m_outD);
381 ctFrom->buildNonOrthogonal(m_translation, this->m_bases, VMD(m_transformScaling) / VMD(m_transformScaling));
382 m_transformFromOriginal = std::move(ctFrom);
383
384 // Validate
385 if (m_transform->getInD() != inD)
386 throw std::invalid_argument("The number of input dimensions in the CoordinateTransform "
387 "object is not consistent with the number of dimensions in the input "
388 "workspace.");
389 if (m_transform->getOutD() != m_outD)
390 throw std::invalid_argument("The number of output dimensions in the CoordinateTransform "
391 "object is not consistent with the number of dimensions specified in "
392 "the OutDimX, etc. properties.");
393
394 // Now the reverse transformation
395 m_transformToOriginal = nullptr;
396 if (m_outD == inD) {
397 // Can't reverse transform if you lost dimensions.
398 auto ctTo = std::make_unique<DataObjects::CoordTransformAffine>(inD, m_outD);
399 auto toMatrix = static_cast<DataObjects::CoordTransformAffine *>(m_transformFromOriginal.get())->getMatrix();
400 // Invert the affine matrix to get the reverse transformation
401 toMatrix.Invert();
402 ctTo->setMatrix(toMatrix);
403 m_transformToOriginal = std::move(ctTo);
404 }
405}
406
407//----------------------------------------------------------------------------------------------
415 if (str.empty()) {
416 throw std::runtime_error("Empty string passed to one of the AlignedDim0 parameters.");
417 } else {
418 // Strip spaces
419 std::string input = Strings::strip(str);
420 if (input.size() < 4)
421 throw std::invalid_argument("Dimensions string is too short to be valid: " + str);
422
423 // Find the 3rd comma from the end
424 size_t n = std::string::npos;
425 for (size_t i = 0; i < 3; i++) {
426 n = input.find_last_of(',', n);
427 if (n == std::string::npos)
428 throw std::invalid_argument("Wrong number of values (4 are expected) "
429 "in the dimensions string: " +
430 str);
431 if (n == 0)
432 throw std::invalid_argument("Dimension string starts with a comma: " + str);
433 n--;
434 }
435 // The name is everything before the 3rd comma from the end
436 std::string name = input.substr(0, n + 1);
438
439 // And split the rest of it
440 input = input.substr(n + 2);
441 std::vector<std::string> strs;
442 boost::split(strs, input, boost::is_any_of(","));
443 if (strs.size() != 3)
444 throw std::invalid_argument("Wrong number of values (3 are expected) after the name "
445 "in the dimensions string: " +
446 str);
447
448 // Extract the arguments
449 coord_t min, max;
450 int numBins = 0;
451 Strings::convert(strs[0], min);
452 Strings::convert(strs[1], max);
453 Strings::convert(strs[2], numBins);
454 if (name.empty())
455 throw std::invalid_argument("Name should not be blank.");
456 if (min >= max)
457 throw std::invalid_argument("Min should be > max.");
458 if (numBins < 1)
459 throw std::invalid_argument("Number of bins should be >= 1.");
460
461 // Find the named axis in the input workspace
462 size_t dim_index = 0;
463 try {
464 dim_index = m_inWS->getDimensionIndexByName(name);
465 } catch (std::runtime_error &) {
466 // The dimension was not found by name. Try using the ID
467 try {
468 dim_index = m_inWS->getDimensionIndexById(name);
469 } catch (std::runtime_error &) {
470 throw std::runtime_error("Dimension " + name +
471 " was not found in the "
472 "MDEventWorkspace! Cannot continue.");
473 }
474 }
475
476 // Copy the dimension name, ID and units
477 IMDDimension_const_sptr inputDim = m_inWS->getDimension(dim_index);
478 const auto &frame = inputDim->getMDFrame();
480 new MDHistoDimension(inputDim->getName(), inputDim->getDimensionId(), frame, min, max, numBins)));
481
482 // Add the index from which we're binning to the vector
483 this->m_dimensionToBinFrom.emplace_back(dim_index);
484 }
485}
486//----------------------------------------------------------------------------------------------
491 std::string dimChars = this->getDimensionChars();
492
493 // Validate inputs
494 bool previousWasEmpty = false;
495 size_t numDims = 0;
496 for (char dimChar : dimChars) {
497 std::string propName = "AlignedDim0";
498 propName[10] = dimChar;
499 std::string prop = Strings::strip(getPropertyValue(propName));
500 if (!prop.empty())
501 numDims++;
502 if (!prop.empty() && previousWasEmpty)
503 throw std::invalid_argument("Please enter the AlignedDim parameters in the order 0,1,2, etc.,"
504 "without skipping any entries.");
505 previousWasEmpty = prop.empty();
506 }
507
508 // Number of input dimension
509 size_t inD = m_inWS->getNumDims();
510 // Validate
511 if (numDims == 0)
512 throw std::runtime_error("No output dimensions specified.");
513 if (numDims > inD)
514 throw std::runtime_error("More output dimensions were specified than input dimensions "
515 "exist in the MDEventWorkspace.");
516
517 // Create the dimensions based on the strings from the user
518 for (size_t i = 0; i < numDims; i++) {
519 std::string propName = "AlignedDim0";
520 propName[10] = dimChars[i];
522 }
523
524 // Number of output binning dimensions found
525 m_outD = m_binDimensions.size();
526
527 // Now we build the coordinate transformation object
528 m_translation = VMD(inD);
529 m_bases.clear();
530 std::vector<coord_t> origin(m_outD), scaling(m_outD);
531 for (size_t d = 0; d < m_outD; d++) {
532 origin[d] = m_binDimensions[d]->getMinimum();
533 scaling[d] = 1.0f / m_binDimensions[d]->getBinWidth();
534 // Origin in the input
536 // Create a unit basis vector that corresponds to this
537 VMD basis(inD);
538 basis[m_dimensionToBinFrom[d]] = 1.0;
539 m_bases.emplace_back(basis);
540 }
541
542 // Transform for binning
543 m_transform = std::make_unique<DataObjects::CoordTransformAligned>(m_inWS->getNumDims(), m_outD, m_dimensionToBinFrom,
544 origin, scaling);
545
546 // Transformation original->binned. There is no offset or scaling!
547 std::vector<coord_t> unitScaling(m_outD, 1.0);
548 std::vector<coord_t> zeroOrigin(m_outD, 0.0);
550 std::make_unique<DataObjects::CoordTransformAligned>(inD, m_outD, m_dimensionToBinFrom, zeroOrigin, unitScaling);
551
552 // Now the reverse transformation.
553 if (m_outD == inD) {
554 // Make the reverse map = if you're in the output dimension "od", what INPUT
555 // dimension index is that?
556 Matrix<coord_t> mat = m_transformFromOriginal->makeAffineMatrix();
557 mat.Invert();
558 auto tmp = std::make_unique<DataObjects::CoordTransformAffine>(inD, m_outD);
559 tmp->setMatrix(mat);
560 m_transformToOriginal = std::move(tmp);
561 } else {
562 // Changed # of dimensions - can't reverse the transform
563 m_transformToOriginal = nullptr;
564 g_log.warning("SlicingAlgorithm: Your slice will cause the output "
565 "workspace to have fewer dimensions than the input. This will "
566 "affect your ability to create subsequent slices.");
567 }
568}
569
570//-----------------------------------------------------------------------------------------------
583 if (!m_inWS)
584 throw std::runtime_error("SlicingAlgorithm::createTransform(): input "
585 "MDWorkspace must be set first!");
586 if (std::dynamic_pointer_cast<MatrixWorkspace>(m_inWS))
587 throw std::runtime_error(this->name() + " cannot be run on a MatrixWorkspace!");
588
589 // Is the transformation aligned with axes?
590 m_axisAligned = getProperty("AxisAligned");
591
592 // Refer to the original workspace. Make sure that is possible
593 if (m_inWS->numOriginalWorkspaces() > 0)
594 m_originalWS = std::dynamic_pointer_cast<IMDWorkspace>(m_inWS->getOriginalWorkspace());
595 if (m_originalWS) {
596 if (m_axisAligned)
597 throw std::runtime_error("Cannot perform axis-aligned binning on a MDHistoWorkspace. "
598 "Please use non-axis aligned binning.");
599
600 if (m_originalWS->getNumDims() != m_inWS->getNumDims())
601 throw std::runtime_error("SlicingAlgorithm::createTransform(): Cannot propagate "
602 "a transformation if the number of dimensions has changed.");
603
604 if (!m_inWS->getTransformToOriginal())
605 throw std::runtime_error("SlicingAlgorithm::createTransform(): Cannot propagate "
606 "a transformation. There is no transformation saved from " +
607 m_inWS->getName() + " back to " + m_originalWS->getName() + ".");
608
609 // Fail if the MDHistoWorkspace was modified by binary operation
610 DataObjects::MDHistoWorkspace_sptr inHisto = std::dynamic_pointer_cast<DataObjects::MDHistoWorkspace>(m_inWS);
611 if (inHisto) {
612 if (inHisto->getNumExperimentInfo() > 0) {
613 const Run &run = inHisto->getExperimentInfo(0)->run();
614 if (run.hasProperty("mdhisto_was_modified")) {
615 Property *prop = run.getProperty("mdhisto_was_modified");
616 if (prop) {
617 if (prop->value() == "1") {
618 throw std::runtime_error("This MDHistoWorkspace was modified by a binary operation "
619 "(e.g. Plus, Minus). "
620 "It is not currently possible to rebin a modified "
621 "MDHistoWorkspace because that requires returning to the "
622 "original "
623 "(unmodified) MDEventWorkspace, and so would give incorrect "
624 "results. "
625 "Instead, you can use SliceMD and perform operations on the "
626 "resulting "
627 "MDEventWorkspaces, which preserve all events. "
628 "You can override this check by removing the "
629 "'mdhisto_was_modified' sample log.");
630 }
631 }
632 }
633 }
634 }
635
636 g_log.notice() << "Performing " << this->name() << " on the original workspace, '" << m_originalWS->getName()
637 << "'\n";
638 }
639
640 // Create the coordinate transformation
641 m_transform = nullptr;
642 if (m_axisAligned)
644 else
646
647 // Finalize, for binnign MDHistoWorkspace
648 if (m_originalWS) {
649 // The intermediate workspace is the MDHistoWorkspace being BINNED
651 CoordTransform const *originalToIntermediate = m_intermediateWS->getTransformFromOriginal();
652 if (originalToIntermediate && (m_originalWS->getNumDims() == m_intermediateWS->getNumDims())) {
653 try {
654 // The transform from the INPUT to the INTERMEDIATE ws
655 // intermediate_coords = [OriginalToIntermediate] * [thisToOriginal] *
656 // these_coords
657 Matrix<coord_t> matToOriginal = m_transformToOriginal->makeAffineMatrix();
658 Matrix<coord_t> matOriginalToIntermediate = originalToIntermediate->makeAffineMatrix();
659 Matrix<coord_t> matToIntermediate = matOriginalToIntermediate * matToOriginal;
660
661 m_transformToIntermediate = std::make_unique<DataObjects::CoordTransformAffine>(m_originalWS->getNumDims(),
662 m_intermediateWS->getNumDims());
663 m_transformToIntermediate->setMatrix(matToIntermediate);
664 // And now the reverse
665 matToIntermediate.Invert();
666 m_transformFromIntermediate = std::make_unique<DataObjects::CoordTransformAffine>(
667 m_intermediateWS->getNumDims(), m_originalWS->getNumDims());
668 m_transformFromIntermediate->setMatrix(matToIntermediate);
669 } catch (std::runtime_error &) {
670 // Ignore error. Have no transform.
671 }
672 }
673
674 // Replace the input workspace with the original MDEventWorkspace
675 // for future binning
677 }
678}
679
680//----------------------------------------------------------------------------------------------
697std::unique_ptr<MDImplicitFunction> SlicingAlgorithm::getGeneralImplicitFunction(const size_t *const chunkMin,
698 const size_t *const chunkMax) {
699 size_t nd = m_inWS->getNumDims();
700
701 // General implicit function
702 auto func = std::make_unique<MDImplicitFunction>();
703
704 // First origin = min of each basis vector
705 VMD o1 = m_translation;
706 // Second origin = max of each basis vector
707 VMD o2 = m_translation;
708 // And this the list of basis vectors. Each vertex is given by o1+bases[i].
709 std::vector<VMD> bases;
710 VMD x;
711
712 for (size_t d = 0; d < m_bases.size(); d++) {
713 double xMin = m_binDimensions[d]->getMinimum();
714 double xMax = m_binDimensions[d]->getMaximum();
715 // Move the position if you're using a chunk
716 if (chunkMin != nullptr)
717 xMin = m_binDimensions[d]->getX(chunkMin[d]);
718 if (chunkMax != nullptr)
719 xMax = m_binDimensions[d]->getX(chunkMax[d]);
720 // Offset the origin by the position along the basis vector
721 o1 += (m_bases[d] * xMin);
722 o2 += (m_bases[d] * xMax);
723
724 VMD thisBase = m_bases[d] * (xMax - xMin);
725 bases.emplace_back(thisBase);
726 if (d == 0)
727 x = thisBase;
728 }
729
730 // Dimensionality of the box
731 size_t boxDim = bases.size();
732
733 // Point that is sure to be inside the volume of interest
734 VMD insidePoint = (o1 + o2) / 2.0;
735
736 if (boxDim == 1) {
737 // 2 planes defined by 1 basis vector
738 // Your normal = the x vector
739 func->addPlane(MDPlane(x, o1));
740 func->addPlane(MDPlane(x * -1.0, o2));
741 } else if (boxDim == nd || boxDim == nd - 1) {
742 // Create a pair of planes for each base supplied. This is general to non-
743 // orthogonal bases. If we have bases (x y z t) then we create the planes
744 //
745 // y z t
746 // x z t
747 // x y t
748 // x y z
749 //
750 // Note: the last plane may or may not be created depending on the number
751 // of bases supplied to the slicing algorithm relative to the number
752 // of dimensions. i.e. if 3 bases were supplied and we have 4 dimensions
753 // then 6 planes are created instead of 8.
754 std::vector<VMD> vectors;
755
756 for (size_t ignoreIndex = 0; ignoreIndex < boxDim; ++ignoreIndex) {
757 vectors.clear();
758 // Create a list of vectors that excludes the "current" basis
759 for (size_t baseIndex = 0; baseIndex < boxDim; ++baseIndex) {
760 if (baseIndex != ignoreIndex)
761 vectors.emplace_back(bases[baseIndex]);
762 }
763
764 // if we have fewer basis vectors than dimensions
765 // create a normal for the final dimension
766 if (boxDim == nd - 1)
767 vectors.emplace_back(VMD::getNormalVector(bases));
768
769 // Add two planes for each set of vectors
770 func->addPlane(MDPlane(vectors, o1, insidePoint));
771 func->addPlane(MDPlane(vectors, o2, insidePoint));
772 }
773 } else {
774 // Last-resort, totally general case
775 // 2*N planes defined by N basis vectors, in any dimensionality workspace.
776 // Assumes orthogonality!
777 g_log.warning("SlicingAlgorithm given " + std::to_string(boxDim) + " bases and " + std::to_string(nd) +
778 " dimensions and " + "therefore will assume orthogonality");
779 for (auto &base : bases) {
780 // For each basis vector, make two planes, perpendicular to it and facing
781 // inwards
782 func->addPlane(MDPlane(base, o1));
783 func->addPlane(MDPlane(base * -1.0, o2));
784 }
785 }
786
787 return func;
788}
789
790//----------------------------------------------------------------------------------------------
803std::unique_ptr<MDImplicitFunction> SlicingAlgorithm::getImplicitFunctionForChunk(const size_t *const chunkMin,
804 const size_t *const chunkMax) {
805 size_t nd = m_inWS->getNumDims();
806 if (m_axisAligned) {
807 std::vector<coord_t> function_min(nd, -1e30f); // default to all space if the dimension is not specified
808 std::vector<coord_t> function_max(nd, +1e30f); // default to all space if the dimension is not specified
809 for (size_t bd = 0; bd < m_outD; bd++) {
810 // Dimension in the MDEventWorkspace
811 size_t d = m_dimensionToBinFrom[bd];
812 if (chunkMin)
813 function_min[d] = m_binDimensions[bd]->getX(chunkMin[bd]);
814 else
815 function_min[d] = m_binDimensions[bd]->getX(0);
816 if (chunkMax)
817 function_max[d] = m_binDimensions[bd]->getX(chunkMax[bd]);
818 else
819 function_max[d] = m_binDimensions[bd]->getX(m_binDimensions[bd]->getNBins());
820 }
821 return std::make_unique<MDBoxImplicitFunction>(function_min, function_max);
822 } else {
823 // General implicit function
824 return getGeneralImplicitFunction(chunkMin, chunkMax);
825 }
826}
827
838 const Mantid::Kernel::VMD &basisVector) const {
839 // Get set of basis vectors
840 auto oldBasis = getOldBasis(m_inWS->getNumDims());
841
842 // Get indices onto which the vector projects
843 auto indicesWithProjection = getIndicesWithProjection(basisVector, oldBasis);
844
845 // Extract MDFrame
846 return extractMDFrameForNonAxisAligned(indicesWithProjection, units);
847}
848
849std::vector<Mantid::Kernel::VMD> SlicingAlgorithm::getOldBasis(size_t dimension) const {
850 std::vector<Mantid::Kernel::VMD> oldBasis;
851 for (size_t i = 0; i < dimension; ++i) {
852 Mantid::Kernel::VMD basisVector(dimension);
853 basisVector[i] = 1.0;
854 oldBasis.emplace_back(basisVector);
855 }
856 return oldBasis;
857}
858
866 const Mantid::Kernel::VMD &basisVector) const {
867 return std::fabs(oldVector.scalar_prod(basisVector)) > 0.0;
868}
869
877 const std::vector<Mantid::Kernel::VMD> &oldBasis) const {
878 std::vector<size_t> indexWithProjection;
879 for (size_t index = 0; index < oldBasis.size(); ++index) {
880 if (isProjectingOnFrame(oldBasis[index], basisVector)) {
881 indexWithProjection.emplace_back(index);
882 }
883 }
884 return indexWithProjection;
885}
886
895SlicingAlgorithm::extractMDFrameForNonAxisAligned(std::vector<size_t> indicesWithProjection,
896 const std::string &units) const {
897 if (indicesWithProjection.empty()) {
898 g_log.warning() << "Slicing Algorithm: Chosen vector does not "
899 "project on any vector of the old basis.";
900 }
901 // Get a reference frame to perform pairwise comparison
902 const auto &referenceMDFrame = m_inWS->getDimension(indicesWithProjection[0])->getMDFrame();
903
904 for (auto &index : indicesWithProjection) {
905 const auto &toCheckMDFrame = m_inWS->getDimension(index)->getMDFrame();
906 if (!referenceMDFrame.isSameType(toCheckMDFrame)) {
907 g_log.warning() << "Slicing Algorithm: New basis vector tries to "
908 "mix un-mixable MDFrame types.";
909 }
910 }
911
912 Mantid::Geometry::MDFrame_uptr mdFrame(referenceMDFrame.clone());
913 setTargetUnits(mdFrame, units);
914
915 return mdFrame;
916}
917
918/*
919 * Set units of the output workspace
920 * @param mdFrame: MDFrame to be added to the output workspace
921 * @param unit: the unit to use in mdFrame
922 */
923void SlicingAlgorithm::setTargetUnits(Mantid::Geometry::MDFrame_uptr &mdFrame, const std::string &unit) const {
924 boost::regex pattern("in.*A.*\\^-1");
925
926 if (boost::regex_match(unit, pattern)) {
927 // RLU with special label
928 auto md_unit = ReciprocalLatticeUnit(unit);
929 mdFrame->setMDUnit(md_unit);
930 } else if (unit == "r") {
931 // RLU
932 auto md_unit = ReciprocalLatticeUnit();
933 mdFrame->setMDUnit(md_unit);
934 } else if (unit == "a") {
935 // Inverse angstroms
936 auto md_unit = InverseAngstromsUnit();
937 mdFrame->setMDUnit(md_unit);
938 }
939 // else leave the unit the same as the input workspace
940}
941
942} // namespace Mantid::MDAlgorithms
gsl_vector * tmp
std::map< DeltaEMode::Type, std::string > index
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.
Kernel::Logger & g_log
Definition Algorithm.h:422
const std::string name() const override=0
function to return a name of the algorithm, must be overridden in all algorithms
Unique SingleValueParameter Declaration for InputNDimensions.
Mantid::Kernel::VMD applyVMD(const Mantid::Kernel::VMD &inputVector) const
Wrapper for VMD.
virtual Mantid::Kernel::Matrix< coord_t > makeAffineMatrix() const
bool hasProperty(const std::string &name) const
Does the property exist on the object.
Kernel::Property * getProperty(const std::string &name) const
Returns the named property as a pointer.
This class stores information regarding an experimental run as a series of log entries.
Definition Run.h:35
Generic class to transform from M input dimensions to N output dimensions.
void setMatrix(const Mantid::Kernel::Matrix< coord_t > &newMatrix)
Directly set the affine matrix to use.
A generalized description of a N-dimensional hyperplane.
Definition MDPlane.h:41
Support for a property that holds an array of values.
void setPropertySettings(const std::string &name, std::unique_ptr< IPropertySettings > settings)
void setPropertyGroup(const std::string &name, const std::string &group)
Set the group for a given property.
Inverse Angstroms unit.
Definition MDUnit.h:51
void notice(const std::string &msg)
Logs at notice level.
Definition Logger.cpp:126
void warning(const std::string &msg)
Logs at warning level.
Definition Logger.cpp:117
void information(const std::string &msg)
Logs at information level.
Definition Logger.cpp:136
Numerical Matrix class.
Definition Matrix.h:42
T Invert()
LU inversion routine.
Definition Matrix.cpp:924
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 std::vector< VMDBase > makeVectorsOrthogonal(std::vector< VMDBase > &vectors)
Make an orthogonal system with 2 input 3D vectors.
Definition VMD.cpp:450
TYPE normalize()
Normalize this vector to unity length.
Definition VMD.cpp:427
TYPE scalar_prod(const VMDBase &v) const
Scalar product of two vectors.
Definition VMD.cpp:391
size_t size() const
Definition VMD.cpp:239
size_t getNumDims() const
Definition VMD.cpp:236
static VMDBase getNormalVector(const std::vector< VMDBase > &vectors)
Given N-1 vectors defining a N-1 dimensional hyperplane in N dimensions, returns a vector that is nor...
Definition VMD.cpp:494
TYPE norm() const
Definition VMD.cpp:420
std::unique_ptr< API::CoordTransform > m_transformFromOriginal
Coordinate transformation to save in the output workspace (original->binned)
Mantid::API::IMDWorkspace_sptr m_intermediateWS
Intermediate original workspace.
std::vector< size_t > m_dimensionToBinFrom
Index of the dimension in the MDEW for the dimension in the output.
std::unique_ptr< Mantid::Geometry::MDImplicitFunction > getImplicitFunctionForChunk(const size_t *const chunkMin, const size_t *const chunkMax)
Create an implicit function for picking boxes, based on the indexes in the output MDHistoWorkspace.
bool isProjectingOnFrame(const Mantid::Kernel::VMD &oldVector, const Mantid::Kernel::VMD &basisVector) const
Check if the two vectors are orthogonal or not.
std::unique_ptr< API::CoordTransform > m_transform
Coordinate transformation to apply.
std::vector< size_t > getIndicesWithProjection(const Mantid::Kernel::VMD &basisVector, const std::vector< Mantid::Kernel::VMD > &oldBasis) const
Get indices which have a projection contribution.
Mantid::API::IMDWorkspace_sptr m_originalWS
Original (MDEventWorkspace) that inWS was based on.
void createGeneralTransform()
Loads the dimensions and create the coordinate transform, using the inputs.
void createTransform()
Read the algorithm properties and creates the appropriate transforms for slicing the MDEventWorkspace...
void createAlignedTransform()
Using the parameters, create a coordinate transformation for aligned cuts.
void setTargetUnits(Mantid::Geometry::MDFrame_uptr &frame, const std::string &units) const
std::vector< Mantid::Kernel::VMD > getOldBasis(size_t dimension) const
Mantid::Kernel::VMD m_inputMinPoint
Coordinates in the INPUT workspace corresponding to the minimum edge in all dimensions.
std::vector< double > m_maxExtents
For non-aligned, the maximum coordinate extents in each OUTPUT dimension.
Mantid::API::IMDWorkspace_sptr m_inWS
Input workspace.
std::unique_ptr< API::CoordTransform > m_transformToOriginal
Coordinate transformation to save in the output workspace (binned->original)
std::unique_ptr< DataObjects::CoordTransformAffine > m_transformFromIntermediate
Coordinate transformation to save in the output WS, from the intermediate WS.
std::vector< double > m_binningScaling
Scaling factor to apply for each basis vector (to map to the bins).
size_t m_outD
Number of dimensions in the output (binned) workspace.
void makeBasisVectorFromString(const std::string &str)
Generate the MDHistoDimension and basis vector for a given string from BasisVector0 etc.
Mantid::Geometry::MDFrame_uptr createMDFrameForNonAxisAligned(const std::string &units, const Mantid::Kernel::VMD &basisVector) const
Create an MDFrame for the Non-Axis-Aligned case.
bool m_NormalizeBasisVectors
The NormalizeBasisVectors option.
void processGeneralTransformProperties()
Reads the various Properties for the general (non-aligned) case and fills in members on the Algorithm...
std::vector< Mantid::Kernel::VMD > m_bases
Basis vectors of the output dimensions, normalized to unity length.
std::unique_ptr< Mantid::Geometry::MDImplicitFunction > getGeneralImplicitFunction(const size_t *const chunkMin, const size_t *const chunkMax)
Create an implicit function for picking boxes, based on the indexes in the output MDHistoWorkspace.
Mantid::Geometry::MDFrame_uptr extractMDFrameForNonAxisAligned(std::vector< size_t > indicesWithProjection, const std::string &units) const
Extract the MDFrame.
bool m_axisAligned
Set to true if the cut is aligned with the axes.
void initSlicingProps()
Initialise the properties.
std::vector< int > m_numBins
For non-aligned, the number of bins in each OUTPUT dimension.
Mantid::Kernel::VMD m_translation
Translation from the OUTPUT to the INPUT workspace i.e.
std::vector< double > m_transformScaling
Scaling factor to apply for each basis vector to transfor to the output dimensions.
std::vector< double > m_minExtents
For non-aligned, the minimum coordinate extents in each OUTPUT dimension.
std::unique_ptr< DataObjects::CoordTransformAffine > m_transformToIntermediate
Coordinate transformation to save in the intermediate WS.
void makeAlignedDimensionFromString(const std::string &str)
Generate a MDHistoDimension_sptr from a comma-sep string (for AlignedDim0, etc.) Must be called in or...
std::vector< Mantid::Geometry::MDHistoDimension_sptr > m_binDimensions
Bin dimensions to actually use.
std::shared_ptr< MDHistoWorkspace > MDHistoWorkspace_sptr
A shared pointer to a MDHistoWorkspace.
std::unique_ptr< MDFrame > MDFrame_uptr
Definition MDFrame.h:36
std::shared_ptr< MDHistoDimension > MDHistoDimension_sptr
Shared pointer to a MDHistoDimension.
std::shared_ptr< const IMDDimension > IMDDimension_const_sptr
Shared Pointer to const IMDDimension.
MANTID_KERNEL_DLL std::string strip(const std::string &A)
strip pre/post spaces
Definition Strings.cpp:419
int convert(const std::string &A, T &out)
Convert a string into a number.
Definition Strings.cpp:696
std::string toString(const T &value)
Convert a number to a string.
Definition Strings.cpp:734
VMDBase< VMD_t > VMD
Define the VMD as using the double or float data type.
Definition VMD.h:86
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::string to_string(const wide_integer< Bits, Signed > &n)
@ Input
An input workspace.
Definition Property.h:53