Mantid
Loading...
Searching...
No Matches
CreateMDWorkspace.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 +
22#include "MantidKernel/Memory.h"
23#include <boost/algorithm/string.hpp>
24#include <boost/regex.hpp>
25#include <cmath>
26
27namespace Mantid::MDAlgorithms {
28using namespace Mantid::Kernel;
29using namespace Mantid::API;
30using namespace Mantid::Geometry;
31using namespace Mantid::DataObjects;
32using boost::regex;
33
34/*
35 * The list of dimension names often looks like "[H,0,0],[0,K,0]" with "[H,0,0]"
36 * being the first dimension but getProperty returns a vector of
37 * the string split on every comma
38 * This function parses the string, and does not split on commas within brackets
39 */
40std::vector<std::string> parseNames(const std::string &names_string) {
41
42 // This regex has two parts which are separated by the "|" (or)
43 // The first part matches anything which is bounded by square brackets
44 // unless they contain square brackets (so that it only matches inner pairs)
45 // The second part matches anything that doesn't contain a comma
46 // NB, the order of the two parts matters
47
48 regex expression(R"(\[([^\[]*)\]|[^,]+)");
49
50 boost::sregex_token_iterator iter(names_string.begin(), names_string.end(), expression, 0);
51 boost::sregex_token_iterator end;
52
53 std::vector<std::string> names_result(iter, end);
54
55 return names_result;
56}
57
58// Register the algorithm into the AlgorithmFactory
59DECLARE_ALGORITHM(CreateMDWorkspace)
60
61//----------------------------------------------------------------------------------------------
64void CreateMDWorkspace::init() {
65 declareProperty(std::make_unique<PropertyWithValue<int>>("Dimensions", 1, Direction::Input),
66 "Number of dimensions that the workspace will have.");
67
68 std::vector<std::string> propOptions{"MDEvent", "MDLeanEvent"};
69 declareProperty("EventType", "MDLeanEvent", std::make_shared<StringListValidator>(propOptions),
70 "Which underlying data type will event take.");
71
72 declareProperty(std::make_unique<ArrayProperty<double>>("Extents"),
73 "A comma separated list of min, max for each dimension,\n"
74 "specifying the extents of each dimension.");
75
76 declareProperty(std::make_unique<ArrayProperty<std::string>>("Names", Direction::Input),
77 "A comma separated list of the name of each dimension.");
78
79 declareProperty(std::make_unique<ArrayProperty<std::string>>("Units"),
80 "A comma separated list of the units of each dimension.");
81 declareProperty(std::make_unique<ArrayProperty<std::string>>("Frames"),
82 " A comma separated list of the frames of each dimension. "
83 " The frames can be"
84 " **General Frame**: Any frame which is not a Q-based frame."
85 " **QLab**: Wave-vector converted into the lab frame."
86 " **QSample**: Wave-vector converted into the frame of the sample."
87 " **HKL**: Wave-vector converted into the crystal's HKL indices."
88 " Note if nothing is specified then the **General Frame** is being "
89 "selected. Also note that if you select a frame then this might override "
90 "your unit selection if it is not compatible with the frame.");
91 // Set the box controller properties
92 this->initBoxControllerProps("5", 1000, 5);
93
94 declareProperty(std::make_unique<PropertyWithValue<int>>("MinRecursionDepth", 0),
95 "Optional. If specified, then all the boxes will be split to "
96 "this minimum recursion depth. 0 = no splitting, 1 = one "
97 "level of splitting, etc.\n"
98 "Be careful using this since it can quickly create a huge "
99 "number of boxes = (SplitInto ^ (MinRercursionDepth * "
100 "NumDimensions)).");
101 setPropertyGroup("MinRecursionDepth", getBoxSettingsGroupName());
102
103 declareProperty(std::make_unique<WorkspaceProperty<Workspace>>("OutputWorkspace", "", Direction::Output),
104 "Name of the output MDEventWorkspace.");
105 declareProperty(std::make_unique<FileProperty>("Filename", "", FileProperty::OptionalSave, ".nxs"),
106 "Optional: to use a file as the back end, give the path to the file to "
107 "save.");
108
109 declareProperty(std::make_unique<PropertyWithValue<int>>("Memory", -1),
110 "If Filename is specified to use a file back end:\n"
111 " The amount of memory (in MB) to allocate to the in-memory cache.\n"
112 " If not specified, a default of 40% of free physical memory is used.");
113 setPropertySettings("Memory", std::make_unique<EnabledWhenProperty>("Filename", IS_NOT_DEFAULT));
114}
115
120template <typename MDE, size_t nd> void CreateMDWorkspace::finish(typename MDEventWorkspace<MDE, nd>::sptr ws) {
121 // ------------ Set up the box controller ----------------------------------
123 this->setBoxController(bc);
124
125 // Split to level 1
126 ws->splitBox();
127
128 // Do we split more due to MinRecursionDepth?
129 int minDepth = this->getProperty("MinRecursionDepth");
130 if (minDepth < 0)
131 throw std::invalid_argument("MinRecursionDepth must be >= 0.");
132 ws->setMinRecursionDepth(size_t(minDepth));
133}
134
135//----------------------------------------------------------------------------------------------
139 // Get the properties and validate them
140 std::string eventType = getPropertyValue("EventType");
141 int ndims_prop = getProperty("Dimensions");
142 if (ndims_prop <= 0)
143 throw std::invalid_argument("You must specify a number of dimensions >= 1.");
144 int mind = this->getProperty("MinRecursionDepth");
145 int maxd = this->getProperty("MaxRecursionDepth");
146 if (mind > maxd)
147 throw std::invalid_argument("MinRecursionDepth must be <= MaxRecursionDepth.");
148 if (mind < 0 || maxd < 0)
149 throw std::invalid_argument("MinRecursionDepth and MaxRecursionDepth must be positive.");
150
151 auto ndims = static_cast<size_t>(ndims_prop);
152
153 std::vector<double> extents = getProperty("Extents");
154 std::string dimensions_string = getPropertyValue("Names");
155 std::vector<std::string> names = parseNames(dimensions_string);
156
157 std::vector<std::string> units = getProperty("Units");
158 std::vector<std::string> frames = getProperty("Frames");
159
160 if (extents.size() != ndims * 2)
161 throw std::invalid_argument("You must specify twice as many extents "
162 "(min,max) as there are dimensions.");
163 if (names.size() != ndims)
164 throw std::invalid_argument("You must specify as many names as there are dimensions.");
165 if (units.size() != ndims)
166 throw std::invalid_argument("You must specify as many units as there are dimensions.");
167 // If no frames are specified we want to default to the General Frame,
168 // to ensure backward compatibility. But if they are only partly specified,
169 // then we want to throw an error. It should be either used correctly or not
170 // at all
171 if (!frames.empty() && frames.size() != ndims) {
172 throw std::invalid_argument("You must specify as many frames as there are dimensions.");
173 }
174
175 if (frames.empty()) {
176 frames.resize(ndims);
177 std::fill(frames.begin(), frames.end(), GeneralFrame::GeneralFrameName);
178 }
179
180 // Have the factory create it
182
183 // Give all the dimensions
184 for (size_t d = 0; d < ndims; d++) {
185 auto frame = createMDFrame(frames[d], units[d]);
186 MDHistoDimension *dim = new MDHistoDimension(names[d], names[d], *frame, static_cast<coord_t>(extents[d * 2]),
187 static_cast<coord_t>(extents[d * 2 + 1]), 1);
188 out->addDimension(MDHistoDimension_sptr(dim));
189 }
190
191 // Initialize it using the dimension
192 out->initialize();
193
194 // Call the templated function to finish ints
195 CALL_MDEVENT_FUNCTION(this->finish, out);
196
197 // --- File back end ? ----------------
198 std::string filename = getProperty("Filename");
199 if (!filename.empty()) {
200 // First save to the NXS file
201 g_log.notice() << "Running SaveMD\n";
202 auto alg = createChildAlgorithm("SaveMD");
203 alg->setPropertyValue("Filename", filename);
204 alg->setProperty("InputWorkspace", std::dynamic_pointer_cast<IMDWorkspace>(out));
205 alg->executeAsChildAlg();
206 // And now re-load it with this file as the backing.
207 g_log.notice() << "Running LoadMD\n";
208 alg = createChildAlgorithm("LoadMD");
209 alg->setPropertyValue("Filename", filename);
210 alg->setProperty("FileBackEnd", true);
211 alg->setPropertyValue("Memory", getPropertyValue("Memory"));
212 alg->executeAsChildAlg();
213 // Replace the workspace with the loaded, file-backed one
215 temp = alg->getProperty("OutputWorkspace");
216 out = std::dynamic_pointer_cast<IMDEventWorkspace>(temp);
217 }
218
219 // Save it on the output.
220 setProperty("OutputWorkspace", std::dynamic_pointer_cast<Workspace>(out));
221}
222
223MDFrame_uptr CreateMDWorkspace::createMDFrame(const std::string &frame, const std::string &unit) {
224 auto frameFactory = makeMDFrameFactoryChain();
225 MDFrameArgument frameArg(frame, unit);
226 return frameFactory->create(frameArg);
227}
228
229std::map<std::string, std::string> CreateMDWorkspace::validateInputs() {
230 // Check Frame names
231 std::map<std::string, std::string> errors;
232 std::string framePropertyName = "Frames";
233 std::vector<std::string> frames = getProperty(framePropertyName);
234 int ndims_prop = getProperty("Dimensions");
235 auto ndims = static_cast<size_t>(ndims_prop);
236
237 std::vector<std::string> targetFrames{Mantid::Geometry::GeneralFrame::GeneralFrameName,
240
241 auto isValidFrame = true;
242 for (auto &frame : frames) {
243 auto result = checkIfFrameValid(frame, targetFrames);
244 if (!result) {
245 isValidFrame = result;
246 }
247 }
248
249 if (!frames.empty() && frames.size() != ndims) {
250 isValidFrame = false;
251 }
252
253 if (!isValidFrame) {
254 std::string message = "The selected frames can be 'HKL', 'QSample', 'QLab' "
255 "or 'General Frame'. You must specify as many frames "
256 "as there are dimensions.";
257 errors.emplace(framePropertyName, message);
258 }
259 return errors;
260}
261
268bool CreateMDWorkspace::checkIfFrameValid(const std::string &frame, const std::vector<std::string> &targetFrames) {
269 return std::any_of(targetFrames.cbegin(), targetFrames.cend(),
270 [&frame](const auto &targetFrame) { return targetFrame == frame; });
271}
272
273} // namespace Mantid::MDAlgorithms
#define DECLARE_ALGORITHM(classname)
Definition Algorithm.h:538
#define CALL_MDEVENT_FUNCTION(funcname, workspace)
Macro that makes it possible to call a templated method for a MDEventWorkspace using a IMDEventWorksp...
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:422
void setBoxController(const Mantid::API::BoxController_sptr &bc, const Mantid::Geometry::Instrument_const_sptr &instrument)
Set the settings in the given box controller.
@ OptionalSave
to specify a file to write to but an empty string is
A property class for workspaces.
static API::IMDEventWorkspace_sptr CreateMDWorkspace(size_t nd, const std::string &eventType="MDLeanEvent", const Mantid::API::MDNormalization &preferredNormalization=Mantid::API::MDNormalization::VolumeNormalization, const Mantid::API::MDNormalization &preferredNormalizationHisto=Mantid::API::MDNormalization::VolumeNormalization)
Create a MDEventWorkspace of the given type.
std::shared_ptr< MDEventWorkspace< MDE, nd > > sptr
Typedef for a shared pointer of this kind of event workspace.
void setMinRecursionDepth(size_t minDepth) override
Recurse box structure down to a minimum depth.
Mantid::API::BoxController_sptr getBoxController() override
Returns the BoxController used in this workspace.
void splitBox() override
Split the contained MDBox into a MDGridBox or MDSplitBox, if it is not that already.
static const std::string GeneralFrameName
static const std::string HKLName
Definition HKL.h:26
Input argument type for MDFrameFactory chainable factory.
static const std::string QLabName
Definition QLab.h:34
static const std::string QSampleName
Definition QSample.h:22
Support for a property that holds an array of values.
IPropertyManager * setProperty(const std::string &name, const T &value)
Templated method to set the value of a PropertyWithValue.
void notice(const std::string &msg)
Logs at notice level.
Definition Logger.cpp:126
The concrete, templated class for properties.
Mantid::Geometry::MDFrame_uptr createMDFrame(const std::string &frame, const std::string &unit)
std::map< std::string, std::string > validateInputs() override
Perform validation of ALL the input properties of the algorithm.
void finish(typename DataObjects::MDEventWorkspace< MDE, nd >::sptr ws)
Finish initialisation.
void exec() override
Execute the algorithm.
bool checkIfFrameValid(const std::string &frame, const std::vector< std::string > &targetFrames)
Check if the specified frame matches a target frame.
std::shared_ptr< IMDEventWorkspace > IMDEventWorkspace_sptr
Shared pointer to Mantid::API::IMDEventWorkspace.
std::shared_ptr< IMDWorkspace > IMDWorkspace_sptr
Shared pointer to the IMDWorkspace base class.
std::shared_ptr< BoxController > BoxController_sptr
Shared ptr to BoxController.
std::unique_ptr< MDFrame > MDFrame_uptr
Definition MDFrame.h:36
MDFrameFactory_uptr MANTID_GEOMETRY_DLL makeMDFrameFactoryChain()
Make a complete factory chain.
std::shared_ptr< MDHistoDimension > MDHistoDimension_sptr
Shared pointer to a MDHistoDimension.
std::vector< std::string > MANTID_MDALGORITHMS_DLL parseNames(const std::string &names_string)
float coord_t
Typedef for the data type to use for coordinate axes in MD objects such as MDBox, MDEventWorkspace,...
Definition MDTypes.h:27
@ Input
An input workspace.
Definition Property.h:53
@ Output
An output workspace.
Definition Property.h:54