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