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