Mantid
Loading...
Searching...
No Matches
CutMD.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 +
12#include "MantidAPI/Run.h"
13#include "MantidAPI/Sample.h"
18#include "MantidKernel/Matrix.h"
19
20#include <boost/algorithm/string.hpp>
21#include <boost/regex.hpp>
22#include <memory>
23
24using namespace Mantid::API;
25using namespace Mantid::Geometry;
26using namespace Mantid::Kernel;
27
28namespace {
29
30// Typedef to simplify function signatures
31using MinMax = std::pair<double, double>;
32
33MinMax getDimensionExtents(const IMDEventWorkspace_sptr &ws, size_t index) {
34 if (!ws)
35 throw std::runtime_error("Invalid workspace passed to getDimensionExtents.");
36 auto dim = ws->getDimension(index);
37 return std::make_pair(dim->getMinimum(), dim->getMaximum());
38}
39
40std::string numToStringWithPrecision(const double num) {
41 std::stringstream s;
42 s.precision(2);
43 s.setf(std::ios::fixed, std::ios::floatfield);
44 s << num;
45 return s.str();
46}
47
48DblMatrix scaleProjection(const DblMatrix &inMatrix, const std::vector<std::string> &inUnits,
49 std::vector<std::string> &outUnits, const IMDEventWorkspace_sptr &inWS) {
50 DblMatrix ret(inMatrix);
51 // Check if we actually need to do anything
52 if (std::equal(inUnits.begin(), inUnits.end(), outUnits.begin()))
53 return ret;
54
55 if (inUnits.size() != outUnits.size())
56 throw std::runtime_error("scaleProjection given different quantity of input and output units");
57
58 assert(inWS->getNumExperimentInfo() > 0);
59 const OrientedLattice &orientedLattice = inWS->getExperimentInfo(0)->sample().getOrientedLattice();
60
61 const size_t numDims = inUnits.size();
62 for (size_t i = 0; i < numDims; ++i) {
63 const double dStar = 2 * M_PI * orientedLattice.dstar(inMatrix[i][0], inMatrix[i][1], inMatrix[i][2]);
64 if (inUnits[i] == outUnits[i])
65 continue;
67 // inv angstroms to rlu
68 outUnits[i] = "in " + numToStringWithPrecision(dStar) + " A^-1";
69 for (size_t j = 0; j < numDims; ++j)
70 ret[i][j] *= dStar;
71 } else {
72 // rlu to inv angstroms
73 for (size_t j = 0; j < numDims; ++j)
74 ret[i][j] /= dStar;
75 }
76 }
77
78 return ret;
79}
80
81std::vector<MinMax> calculateExtents(const DblMatrix &inMatrix, const std::vector<MinMax> &limits) {
82 DblMatrix invMat(inMatrix);
83 invMat.Invert();
84
85 // iterate through min/max of each dimension, calculate dot(vert, inv_mat)
86 // and store min/max value for each dimension
87 std::vector<double> hRange(2), kRange(2), lRange(2);
88
89 hRange[0] = limits[0].first;
90 hRange[1] = limits[0].second;
91 kRange[0] = limits[1].first;
92 kRange[1] = limits[1].second;
93 lRange[0] = limits[2].first;
94 lRange[1] = limits[2].second;
95
96 // Calculate the minimums and maximums of transformed coordinates
97 // Use maxDbl as a "not-yet-set" placeholder
98 const double maxDbl = std::numeric_limits<double>::max();
99 std::vector<MinMax> extents(3, std::make_pair(maxDbl, maxDbl));
100
101 for (double &hIt : hRange) {
102 for (double &kIt : kRange) {
103 for (double &lIt : lRange) {
104 V3D origPos(hIt, kIt, lIt);
105 for (size_t i = 0; i < 3; ++i) {
106 const V3D other(invMat[i][0], invMat[i][1], invMat[i][2]);
107 double val = origPos.scalar_prod(other);
108 // Check if min needs updating
109 if (extents[i].first == maxDbl || extents[i].first > val)
110 extents[i].first = val;
111 // Check if max needs updating
112 if (extents[i].second == maxDbl || extents[i].second < val)
113 extents[i].second = val;
114 }
115 }
116 }
117 }
118
119 return extents;
120}
121
122std::pair<std::vector<MinMax>, std::vector<int>> calculateSteps(const std::vector<MinMax> &inExtents,
123 const std::vector<std::vector<double>> &binning) {
124 std::vector<MinMax> outExtents(inExtents);
125 std::vector<int> outBins;
126
127 for (size_t i = 0; i < inExtents.size(); ++i) {
128 const size_t nArgs = binning[i].size();
129 int outBin = -1;
130
131 if (nArgs == 0) {
132 throw std::runtime_error("Binning parameter cannot be empty");
133
134 } else if (nArgs == 1) {
135 const double dimRange = inExtents[i].second - inExtents[i].first;
136 const double stepSize = binning[i][0] < dimRange ? binning[i][0] : dimRange;
137 outBin = static_cast<int>(dimRange / stepSize);
138 outExtents[i].second = inExtents[i].first + outBin * stepSize;
139
140 } else if (nArgs == 2) {
141 outExtents[i].first = binning[i][0];
142 outExtents[i].second = binning[i][1];
143 outBin = 1;
144
145 } else if (nArgs == 3) {
146 const double dimMin = binning[i][0];
147 const double dimMax = binning[i][2];
148 const double dimRange = dimMax - dimMin;
149 const double stepSize = binning[i][1] < dimRange ? binning[i][1] : dimRange;
150 outBin = static_cast<int>(dimRange / stepSize);
151 outExtents[i].second = dimMin + outBin * stepSize;
152 outExtents[i].first = dimMin;
153
154 } else {
155 throw std::runtime_error("Cannot handle " + std::to_string(nArgs) + " bins.");
156 }
157 if (outBin < 0)
158 throw std::runtime_error("output bin calculated to be less than 0");
159 outBins.emplace_back(outBin);
160 }
161 return std::make_pair(outExtents, outBins);
162}
163
164std::vector<std::string> labelProjection(const DblMatrix &projection) {
165 const std::string replacements[] = {"zeta", "eta", "xi"};
166 std::vector<std::string> ret(3);
167 std::vector<std::string> labels(3);
168
169 for (size_t i = 0; i < 3; ++i) {
170 for (size_t j = 0; j < 3; ++j) {
171 const double in = projection[i][j];
172 if (std::abs(in) == 1)
173 if (in > 0)
174 labels[j] = "'" + replacements[i] + "'";
175 else
176 labels[j] = "'-" + replacements[i] + "'";
177 else if (in == 0)
178 labels[j] = "0";
179 else {
180 labels[j] = "'" + numToStringWithPrecision(in) + replacements[i] + "'";
181 }
182 }
183 ret[i] = "[" + boost::algorithm::join(labels, ", ") + "]";
184 }
185 return ret;
186}
187
188} // anonymous namespace
189
190namespace Mantid::MDAlgorithms {
191
198std::vector<std::string> findOriginalQUnits(const IMDWorkspace_const_sptr &inws, Mantid::Kernel::Logger &logger) {
199 std::vector<std::string> unitMarkers(3);
200 for (size_t i = 0; i < inws->getNumDims() && i < 3; ++i) {
201 auto units = inws->getDimension(i)->getUnits();
202 const boost::regex re("(Angstrom\\^-1)|(A\\^-1)", boost::regex::icase);
203 // Does the unit label look like it's in Angstroms?
204 std::string unitMarker;
205 if (boost::regex_match(units.ascii(), re)) {
207 } else {
209 }
210 unitMarkers[i] = unitMarker;
211 logger.debug() << "In dimension with index " << i << " and units " << units.ascii() << " taken to be of type "
212 << unitMarker << '\n';
213 }
214 return unitMarkers;
215}
216
217// Register the algorithm into the AlgorithmFactory
219
220const std::string CutMD::InvAngstromSymbol = "a";
221const std::string CutMD::RLUSymbol = "r";
222const std::string CutMD::AutoMethod = "Auto";
223const std::string CutMD::RLUMethod = "RLU";
224const std::string CutMD::InvAngstromMethod = "Q in A^-1";
225
226//----------------------------------------------------------------------------------------------
227
229 declareProperty(std::make_unique<WorkspaceProperty<IMDWorkspace>>("InputWorkspace", "", Direction::Input),
230 "MDWorkspace to slice");
231
232 declareProperty(
234 "Projection");
235
236 declareProperty(std::make_unique<ArrayProperty<double>>("P1Bin"), "Projection 1 binning.");
237 declareProperty(std::make_unique<ArrayProperty<double>>("P2Bin"), "Projection 2 binning.");
238 declareProperty(std::make_unique<ArrayProperty<double>>("P3Bin"), "Projection 3 binning.");
239 declareProperty(std::make_unique<ArrayProperty<double>>("P4Bin"), "Projection 4 binning.");
240 declareProperty(std::make_unique<ArrayProperty<double>>("P5Bin"), "Projection 5 binning.");
241
242 declareProperty(std::make_unique<WorkspaceProperty<IMDWorkspace>>("OutputWorkspace", "", Direction::Output),
243 "Output cut workspace");
244 declareProperty("NoPix", false,
245 "If False creates a full MDEventWorkspaces "
246 "as output. True to create an "
247 "MDHistoWorkspace as output. This is DND "
248 "only in Horace terminology.");
249
250 auto mustBePositiveInteger = std::make_shared<BoundedValidator<int>>();
251 mustBePositiveInteger->setLower(0);
252
253 declareProperty("MaxRecursionDepth", 1, mustBePositiveInteger,
254 "Sets the maximum recursion depth to use. Can be used to "
255 "constrain the workspaces internal structure");
256
257 std::vector<std::string> propOptions{AutoMethod, RLUMethod, InvAngstromMethod};
258 char buffer[1024];
259 std::sprintf(buffer,
260 "How will the Q units of the input workspace be interpreted? "
261 "This property will disappear in future versions of Mantid\n"
262 "%s : Figure it out based on the label units\n"
263 "%s : Force them to be rlu\n"
264 "%s : Force them to be inverse angstroms",
265 AutoMethod.c_str(), RLUMethod.c_str(), InvAngstromMethod.c_str());
266
267 std::string help(buffer);
268 boost::algorithm::trim(help);
269 declareProperty("InterpretQDimensionUnits", AutoMethod, std::make_shared<StringListValidator>(propOptions), help);
270}
271
273 g_log.warning("CutMD is in the beta stage of development. Its properties and "
274 "behaviour may change without warning.");
275
276 // Collect input properties
277 const IMDWorkspace_sptr inWS = getProperty("InputWorkspace");
278 const size_t numDims = inWS->getNumDims();
279 const ITableWorkspace_sptr projectionWS = getProperty("Projection");
280 std::vector<std::vector<double>> pbins{getProperty("P1Bin"), getProperty("P2Bin"), getProperty("P3Bin"),
281 getProperty("P4Bin"), getProperty("P5Bin")};
282
283 Workspace_sptr sliceWS; // output workspace
284
285 // Histogram workspaces can be sliced axis-aligned only.
286 if (auto histInWS = std::dynamic_pointer_cast<IMDHistoWorkspace>(inWS)) {
287
288 g_log.information("Integrating using binning parameters only.");
289 auto integrateAlg = this->createChildAlgorithm("IntegrateMDHistoWorkspace", 0, 1);
290 integrateAlg->setProperty("InputWorkspace", histInWS);
291 integrateAlg->setProperty("P1Bin", pbins[0]);
292 integrateAlg->setProperty("P2Bin", pbins[1]);
293 integrateAlg->setProperty("P3Bin", pbins[2]);
294 integrateAlg->setProperty("P4Bin", pbins[3]);
295 integrateAlg->setProperty("P5Bin", pbins[4]);
296 integrateAlg->execute();
297 IMDHistoWorkspace_sptr temp = integrateAlg->getProperty("OutputWorkspace");
298 sliceWS = temp;
299 } else { // We are processing an MDEventWorkspace
300
301 auto eventInWS = std::dynamic_pointer_cast<IMDEventWorkspace>(inWS);
302 const bool noPix = getProperty("NoPix");
303
304 // Check Projection format
305 Projection projection;
306 if (projectionWS)
307 projection = Projection(*projectionWS);
308
309 // Check PBin properties
310 for (size_t i = 0; i < 5; ++i) {
311 if (i < numDims && pbins[i].empty())
312 throw std::runtime_error("P" + std::to_string(i + 1) + "Bin must be set when processing a workspace with " +
313 std::to_string(numDims) + " dimensions.");
314 if (i >= numDims && !pbins[i].empty())
315 throw std::runtime_error("P" + std::to_string(i + 1) + "Bin must NOT be set when processing a workspace with " +
316 std::to_string(numDims) + " dimensions.");
317 }
318
319 // Get extents in projection
320 std::vector<MinMax> extentLimits{getDimensionExtents(eventInWS, 0), getDimensionExtents(eventInWS, 1),
321 getDimensionExtents(eventInWS, 2)};
322
323 // Scale projection
324 DblMatrix projectionMatrix(3, 3);
325 projectionMatrix.setRow(0, projection.U());
326 projectionMatrix.setRow(1, projection.V());
327 projectionMatrix.setRow(2, projection.W());
328
329 std::vector<std::string> targetUnits(3);
330 for (size_t i = 0; i < 3; ++i)
331 targetUnits[i] = projection.getUnit(i) == RLU ? RLUSymbol : InvAngstromSymbol;
332
333 const std::string determineUnitsMethod = this->getProperty("InterpretQDimensionUnits");
334 std::vector<std::string> originUnits;
335 if (determineUnitsMethod == AutoMethod) {
336 originUnits = findOriginalQUnits(inWS, g_log);
337 } else if (determineUnitsMethod == RLUMethod) {
338 originUnits = std::vector<std::string>(3, RLUSymbol);
339 } else {
340 originUnits = std::vector<std::string>(3, InvAngstromSymbol);
341 }
342
343 DblMatrix scaledProjectionMatrix = scaleProjection(projectionMatrix, originUnits, targetUnits, eventInWS);
344
345 // Calculate extents for the first 3 dimensions
346 std::vector<MinMax> scaledExtents = calculateExtents(scaledProjectionMatrix, extentLimits);
347 auto stepPair = calculateSteps(scaledExtents, pbins);
348 std::vector<MinMax> steppedExtents = stepPair.first;
349 std::vector<int> steppedBins = stepPair.second;
350
351 // Calculate extents for additional dimensions
352 for (size_t i = 3; i < numDims; ++i) {
353 const size_t nArgs = pbins[i].size();
354 const MinMax extentLimit = getDimensionExtents(eventInWS, i);
355 const double extentRange = extentLimit.second - extentLimit.first;
356
357 if (nArgs == 1) {
358 steppedExtents.emplace_back(extentLimit);
359 steppedBins.emplace_back(static_cast<int>(extentRange / pbins[i][0]));
360 } else if (nArgs == 2) {
361 steppedExtents.emplace_back(pbins[i][0], pbins[i][1]);
362 steppedBins.emplace_back(1);
363 } else if (nArgs == 3) {
364 const double dimRange = pbins[i][2] - pbins[i][0];
365 const double stepSize = pbins[i][1] < dimRange ? pbins[i][1] : dimRange;
366 steppedExtents.emplace_back(pbins[i][0], pbins[i][2]);
367 steppedBins.emplace_back(static_cast<int>(dimRange / stepSize));
368 }
369
370 // and double targetUnits' length by appending itself to itself
371 const size_t preSize = targetUnits.size();
372 targetUnits.resize(preSize * 2);
373 auto halfEnd = targetUnits.begin() + preSize;
374 std::copy(targetUnits.begin(), halfEnd, halfEnd);
375 }
376
377 // Make labels
378 std::vector<std::string> labels = labelProjection(projectionMatrix);
379
380 // Either run RebinMD or SliceMD
381 const std::string cutAlgName = noPix ? "BinMD" : "SliceMD";
382 auto cutAlg = createChildAlgorithm(cutAlgName, 0.0, 1.0);
383 cutAlg->initialize();
384 cutAlg->setProperty("InputWorkspace", inWS);
385 cutAlg->setProperty("OutputWorkspace", "sliced");
386 cutAlg->setProperty("NormalizeBasisVectors", false);
387 cutAlg->setProperty("AxisAligned", false);
388 if (!noPix) {
389 int recursion_depth = getProperty("MaxRecursionDepth");
390 cutAlg->setProperty("TakeMaxRecursionDepthFromInput", false);
391 cutAlg->setProperty("MaxRecursionDepth", recursion_depth);
392 }
393
394 for (size_t i = 0; i < numDims; ++i) {
395 std::string label;
396 std::string unit;
397 std::string vecStr;
398
399 if (i < 3) {
400 // Slicing algorithms accept name as [x, y, z]
401 label = labels[i];
402 unit = targetUnits[i];
403
404 std::vector<std::string> vec(numDims, "0");
405 for (size_t j = 0; j < 3; ++j)
406 vec[j] = boost::lexical_cast<std::string>(scaledProjectionMatrix[i][j]);
407 vecStr = boost::algorithm::join(vec, ", ");
408 } else {
409 // Always orthogonal
410 auto dim = inWS->getDimension(i);
411 label = dim->getName();
412 unit = dim->getUnits();
413 std::vector<std::string> vec(numDims, "0");
414 vec[i] = "1";
415 vecStr = boost::algorithm::join(vec, ", ");
416 }
417
418 const std::string value = std::string(label).append(", ").append(unit).append(", ").append(vecStr);
419 cutAlg->setProperty("BasisVector" + std::to_string(i), value);
420 }
421
422 // Translate extents into a single vector
423 std::vector<double> outExtents(steppedExtents.size() * 2);
424 for (size_t i = 0; i < steppedExtents.size(); ++i) {
425 outExtents[2 * i] = steppedExtents[i].first;
426 outExtents[2 * i + 1] = steppedExtents[i].second;
427 }
428
429 cutAlg->setProperty("OutputExtents", outExtents);
430 cutAlg->setProperty("OutputBins", steppedBins);
431
432 cutAlg->execute();
433 sliceWS = cutAlg->getProperty("OutputWorkspace");
434
435 MultipleExperimentInfos_sptr sliceInfo = std::dynamic_pointer_cast<MultipleExperimentInfos>(sliceWS);
436
437 if (!sliceInfo)
438 throw std::runtime_error("Could not extract experiment info from child's OutputWorkspace");
439
440 // Attach projection matrix to output
441 if (sliceInfo->getNumExperimentInfo() > 0) {
442 ExperimentInfo_sptr info = sliceInfo->getExperimentInfo(0);
443 info->mutableRun().addProperty("W_MATRIX", projectionMatrix.getVector(), true);
444 }
445 }
446
447 auto geometry = std::dynamic_pointer_cast<Mantid::API::MDGeometry>(sliceWS);
448
449 /* Original workspace and transformation information does not make sense for
450 * self-contained Horace-style
451 * cuts, so clear it out. */
452 geometry->clearTransforms();
453 geometry->clearOriginalWorkspaces();
454
455 setProperty("OutputWorkspace", sliceWS);
456}
457
458} // namespace Mantid::MDAlgorithms
#define DECLARE_ALGORITHM(classname)
Definition Algorithm.h:538
double value
The value of the point.
Definition FitMW.cpp:51
std::map< DeltaEMode::Type, std::string > index
std::vector< T > const * vec
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) override
Create a Child Algorithm.
Kernel::IPropertyManager::TypedValue getProperty(const std::string &name) const override
Get the property held by this object.
ProjectionUnit getUnit(size_t nd)
Retrives the unit of the given dimension.
Kernel::V3D & W()
Definition Projection.h:59
Kernel::V3D & U()
Definition Projection.h:57
Kernel::V3D & V()
Definition Projection.h:58
A property class for workspaces.
Class to implement UB matrix.
double dstar(double h, double k, double l) const
Return d*=1/d ( ) for a given h,k,l coordinate.
Definition UnitCell.cpp:706
Support for a property that holds an array of values.
The Logger class is in charge of the publishing messages from the framework through various channels.
Definition Logger.h:51
void debug(const std::string &msg)
Logs at debug level.
Definition Logger.cpp:145
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
void setRow(const size_t nRow, const std::vector< T > &newRow)
Definition Matrix.cpp:686
std::vector< T > getVector() const
Definition Matrix.cpp:77
Class for 3D vectors.
Definition V3D.h:34
static const std::string InvAngstromSymbol
Definition CutMD.h:38
static const std::string InvAngstromMethod
Definition CutMD.h:42
void exec() override
Definition CutMD.cpp:272
static const std::string RLUSymbol
Definition CutMD.h:39
static const std::string AutoMethod
Definition CutMD.h:40
void init() override
Definition CutMD.cpp:228
static const std::string RLUMethod
Definition CutMD.h:41
std::shared_ptr< IMDEventWorkspace > IMDEventWorkspace_sptr
Shared pointer to Mantid::API::IMDEventWorkspace.
std::shared_ptr< ITableWorkspace > ITableWorkspace_sptr
shared pointer to Mantid::API::ITableWorkspace
std::shared_ptr< Workspace > Workspace_sptr
shared pointer to Mantid::API::Workspace
Kernel::Logger g_log("ExperimentInfo")
static logger object
std::shared_ptr< const IMDWorkspace > IMDWorkspace_const_sptr
Shared pointer to the IMDWorkspace base class (const version)
std::shared_ptr< ExperimentInfo > ExperimentInfo_sptr
Shared pointer to ExperimentInfo.
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.
std::shared_ptr< MultipleExperimentInfos > MultipleExperimentInfos_sptr
std::vector< std::string > DLLExport findOriginalQUnits(const Mantid::API::IMDWorkspace_const_sptr &inws, Mantid::Kernel::Logger &logger)
Determine the original q units.
Definition CutMD.cpp:198
std::string to_string(const wide_integer< Bits, Signed > &n)
@ Input
An input workspace.
Definition Property.h:53
@ Output
An output workspace.
Definition Property.h:54