Mantid
Loading...
Searching...
No Matches
AccumulateMD.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 +
21
22#include <boost/algorithm/string/classification.hpp>
23#include <boost/algorithm/string/split.hpp>
24#include <boost/algorithm/string/trim.hpp>
25#include <filesystem>
26
27using namespace Mantid::Kernel;
28using namespace Mantid::API;
29using namespace Mantid::DataObjects;
30
31namespace Mantid::MDAlgorithms {
32
33/*
34 * Reduce the vector of input data to only data files and workspaces which can
35 * be found
36 * @param input_data :: Vector of input data sources
37 * @param psi :: Vector of goniometer angle psi containing a value for each data
38 * source
39 * @param gl :: Vector of goniometer anglegl containing a value for each data
40 * source
41 * @param gs :: Vector of goniometer angle gs containing a value for each data
42 * source
43 * @param efix :: Vector of data source energy values in meV
44 * @returns names of data sources which cannot be found
45 */
46std::string filterToExistingSources(std::vector<std::string> &input_data, std::vector<double> &psi,
47 std::vector<double> &gl, std::vector<double> &gs, std::vector<double> &efix) {
48 std::ostringstream nonexistent;
49 for (size_t i = input_data.size(); i > 0; i--) {
50 if (!dataExists(input_data[i - 1])) {
51 nonexistent << input_data[i - 1] << ",";
52 input_data.erase(input_data.begin() + i - 1);
53 psi.erase(psi.begin() + i - 1);
54 gl.erase(gl.begin() + i - 1);
55 gs.erase(gs.begin() + i - 1);
56 efix.erase(efix.begin() + i - 1);
57 }
58 }
59 return nonexistent.str();
60}
61
62/*
63 * Return true if dataName is an existing workspace or file
64 * @param data_name :: Workspace name or file name
65 * @returns true if a workspace or file with given name exists
66 */
67bool dataExists(const std::string &data_name) {
68 const std::string filepath = Mantid::API::FileFinder::Instance().getFullPath(data_name);
69 // Calls to the ADS in algorithms like this should ordinarily
70 // be avoided, unfortunately we have little choice in this case.
71 // If we gave FileFinder an absolute path it just returns it (whether or not
72 // the file exists) so we must also check the full path returned with
73 // fileExists()
74 return (AnalysisDataService::Instance().doesExist(data_name) || fileExists(filepath));
75}
76
77/*
78 * Test if a file with this full path exists
79 * @param filename :: full path of a file to test existence of
80 * @returns true if the file exists
81 */
82bool fileExists(const std::string &filename) {
83 if (filename.empty())
84 return false;
85 return std::filesystem::exists(filename);
86}
87
88/*
89 * Remove anything from input_data which is already in current_data
90 * @param input_data :: Vector of input data sources
91 * @param current_data :: Vector of data sources previously appended to
92 * workspace
93 * @param psi :: Vector of goniometer angle psi containing a value for each data
94 * source
95 * @param gl :: Vector of goniometer anglegl containing a value for each data
96 * source
97 * @param gs :: Vector of goniometer angle gs containing a value for each data
98 * source
99 * @param efix :: Vector of data source energy values in meV
100 * @returns data sources which are already in the workspace
101 */
102std::string filterToNew(std::vector<std::string> &input_data, const std::vector<std::string> &current_data,
103 std::vector<double> &psi, std::vector<double> &gl, std::vector<double> &gs,
104 std::vector<double> &efix) {
105 std::ostringstream old_sources;
106 for (size_t i = input_data.size(); i > 0; i--) {
107 if (appearsInCurrentData(input_data[i - 1], current_data)) {
108 old_sources << input_data[i - 1] << ",";
109 input_data.erase(input_data.begin() + i - 1);
110 psi.erase(psi.begin() + i - 1);
111 gl.erase(gl.begin() + i - 1);
112 gs.erase(gs.begin() + i - 1);
113 efix.erase(efix.begin() + i - 1);
114 }
115 }
116 return old_sources.str();
117}
118
119/*
120 * Check if the named data source is in the vector of data currently in the
121 * workspace
122 * @param data_source :: Name of a data source
123 * @param current_data :: Vector of data sources previously appended to
124 * workspace
125 * @returns true if the named data source appears in the vector of current data
126 */
127bool appearsInCurrentData(const std::string &data_source, const std::vector<std::string> &current_data) {
128 return std::any_of(current_data.rbegin(), current_data.rend(),
129 [&data_source](const auto &source) { return data_source == source; });
130}
131
132/*
133 * Return a vector of the names of files and workspaces which have been
134 * previously added to the workspace
135 * @param ws_history :: History of the workspace
136 * @returns a vector of the names of data_sources which have previously been
137 * appended to the workspace
138 */
139std::vector<std::string> getHistoricalDataSources(const WorkspaceHistory &ws_history,
140 const std::string &create_alg_name,
141 const std::string &accumulate_alg_name) {
142 // Using a set so we only insert unique names
143 std::unordered_set<std::string> historical_data_sources;
144
145 // Get previously added data sources from DataSources property of the original
146 // call of CreateMD and any subsequent calls of AccumulateMD
147 auto view = ws_history.createView();
148 view->unrollAll();
149 const std::vector<HistoryItem> history_items = view->getAlgorithmsList();
150 for (const auto &history_item : history_items) {
151 auto alg_history = history_item.getAlgorithmHistory();
152 if (alg_history->name() == create_alg_name || alg_history->name() == accumulate_alg_name) {
153 auto props = alg_history->getProperties();
154 for (const auto &prop : props) {
155 PropertyHistory_const_sptr prop_history = prop;
156 if (prop_history->name() == "DataSources") {
157 insertDataSources(prop_history->value(), historical_data_sources);
158 }
159 }
160 }
161 }
162
163 std::vector<std::string> result(historical_data_sources.begin(), historical_data_sources.end());
164 return result;
165}
166
167/*
168 * Split string of data sources from workspace history and insert them into
169 * complete set of historical data sources
170 * @param data_sources :: string from workspace history containing list of data
171 * sources
172 * @param historical_data_sources :: set of data sources
173 */
174void insertDataSources(const std::string &data_sources, std::unordered_set<std::string> &historical_data_sources) {
175 // Split the property string into a vector of data sources
176 std::vector<std::string> data_split;
177 boost::split(data_split, data_sources, boost::is_any_of(","));
178
179 // Trim any whitespace from ends of each data source string
180 std::for_each(data_split.begin(), data_split.end(),
181 std::bind(boost::algorithm::trim<std::string>, std::placeholders::_1, std::locale()));
182
183 // Insert each data source into our complete set of historical data sources
184 historical_data_sources.insert(data_split.begin(), data_split.end());
185}
186
187// Register the algorithm into the AlgorithmFactory
188DECLARE_ALGORITHM(AccumulateMD)
189
190
191const std::string AccumulateMD::name() const { return "AccumulateMD"; }
192
194int AccumulateMD::version() const { return 1; }
195
197const std::string AccumulateMD::category() const { return "MDAlgorithms"; }
198
200const std::string AccumulateMD::summary() const { return "Add new data to an existing MDHistoWorkspace"; }
201
202/*
203 * Initialize the algorithm's properties.
204 */
206 declareProperty(std::make_unique<WorkspaceProperty<IMDEventWorkspace>>("InputWorkspace", "", Direction::Input),
207 "An input MDEventWorkspace to append data to.");
208
209 declareProperty(std::make_unique<WorkspaceProperty<IMDEventWorkspace>>("OutputWorkspace", "", Direction::Output),
210 "MDEventWorkspace with new data appended.");
211
212 declareProperty(
213 std::make_unique<ArrayProperty<std::string>>(
214 "DataSources", std::make_shared<MandatoryValidator<std::vector<std::string>>>(), Direction::Input),
215 "Input workspaces to process, or filenames to load and process");
216
217 declareProperty(std::make_unique<ArrayProperty<double>>("EFix", Direction::Input), "datasource energy values in meV");
218
219 std::vector<std::string> e_mode_options{"Elastic", "Direct", "Indirect"};
220
221 declareProperty("Emode", "Direct", std::make_shared<StringListValidator>(e_mode_options),
222 "Analysis mode ['Elastic', 'Direct', 'Indirect'].");
223
224 declareProperty(std::make_unique<ArrayProperty<double>>(
225 "Alatt", std::make_shared<MandatoryValidator<std::vector<double>>>(), Direction::Input),
226 "Lattice parameters");
227
228 declareProperty(std::make_unique<ArrayProperty<double>>(
229 "Angdeg", std::make_shared<MandatoryValidator<std::vector<double>>>(), Direction::Input),
230 "Lattice angles");
231
232 declareProperty(std::make_unique<ArrayProperty<double>>(
233 "u", std::make_shared<MandatoryValidator<std::vector<double>>>(), Direction::Input),
234 "Lattice vector parallel to neutron beam");
235
236 declareProperty(std::make_unique<ArrayProperty<double>>(
237 "v", std::make_shared<MandatoryValidator<std::vector<double>>>(), Direction::Input),
238 "Lattice vector perpendicular to neutron beam in the horizontal plane");
239
240 declareProperty(std::make_unique<ArrayProperty<double>>("Psi", Direction::Input),
241 "Psi rotation in degrees. Optional or one entry per run.");
242
243 declareProperty(std::make_unique<ArrayProperty<double>>("Gl", Direction::Input),
244 "gl rotation in degrees. Optional or one entry per run.");
245
246 declareProperty(std::make_unique<ArrayProperty<double>>("Gs", Direction::Input),
247 "gs rotation in degrees. Optional or one entry per run.");
248
249 declareProperty(std::make_unique<PropertyWithValue<bool>>("InPlace", true, Direction::Input),
250 "Execute conversions to MD and Merge in one-step. Less "
251 "memory overhead.");
252
253 declareProperty(std::make_unique<PropertyWithValue<bool>>("Clean", false, Direction::Input),
254 "Create workspace from fresh rather than appending to "
255 "existing workspace data.");
256
257 declareProperty(std::make_unique<FileProperty>("Filename", "", FileProperty::OptionalSave, ".nxs"),
258 "The name of the Nexus file to write, as a full or relative path.\n"
259 "Only used if FileBackEnd is true.");
260 setPropertySettings("Filename", std::make_unique<EnabledWhenProperty>("FileBackEnd", IS_EQUAL_TO, "1"));
261
262 declareProperty("FileBackEnd", false,
263 "If true, Filename must also be specified. The algorithm "
264 "will create the specified file in addition to an output "
265 "workspace. The workspace will load data from the file on "
266 "demand in order to reduce memory use.");
267}
268
269/*
270 * Execute the algorithm.
271 */
273
274 IMDEventWorkspace_sptr input_ws = this->getProperty("InputWorkspace");
275 std::vector<std::string> input_data = this->getProperty("DataSources");
276
277 const std::string out_filename = this->getProperty("Filename");
278 const bool filebackend = this->getProperty("FileBackEnd");
279
280 std::vector<double> psi = this->getProperty("Psi");
281 padParameterVector(psi, input_data.size());
282 std::vector<double> gl = this->getProperty("Gl");
283 padParameterVector(gl, input_data.size());
284 std::vector<double> gs = this->getProperty("Gs");
285 padParameterVector(gs, input_data.size());
286 std::vector<double> efix = this->getProperty("EFix");
287 padParameterVector(efix, input_data.size());
288
289 // Create progress reporting object
290 // Progress prog = Progress(this, 0.0, 1.0, 2);
291 this->progress(0.0);
292
293 const std::string nonexistent = filterToExistingSources(input_data, psi, gl, gs, efix);
294 g_log.notice() << "These data sources were not found: " << nonexistent << '\n';
295
296 // If we can't find any data, we can't do anything
297 if (input_data.empty()) {
298 g_log.warning() << "No data found matching input in " << this->name() << '\n';
299 this->setProperty("OutputWorkspace", input_ws);
300 return; // POSSIBLE EXIT POINT
301 }
302 this->interruption_point();
303
304 // If Clean=True then just call CreateMD to create a fresh workspace and
305 // delete the old one, note this means we don't retain workspace history...
306 bool do_clean = this->getProperty("Clean");
307 if (do_clean) {
308 this->progress(0.5);
309 IMDEventWorkspace_sptr out_ws = createMDWorkspace(input_data, psi, gl, gs, efix, out_filename, filebackend);
310 this->setProperty("OutputWorkspace", out_ws);
311 g_log.notice() << this->name() << " successfully created a clean workspace\n";
312 this->progress(1.0);
313 return; // POSSIBLE EXIT POINT
314 }
315 this->interruption_point();
316
317 // Find what files and workspaces have already been included in the workspace.
318 const WorkspaceHistory ws_history = input_ws->getHistory();
319 // Get name from algorithm like this so that an error is thrown if the
320 // name of the algorithm is changed
321 Algorithm_sptr create_alg = createChildAlgorithm("CreateMD");
322 const std::vector<std::string> current_data = getHistoricalDataSources(ws_history, create_alg->name(), this->name());
323
324 // If there's no new data, we don't have anything to do
325 const std::string old_sources = filterToNew(input_data, current_data, psi, gl, gs, efix);
326 g_log.notice() << "Data from these sources are already in the workspace: " << old_sources << '\n';
327
328 if (input_data.empty()) {
329 g_log.notice() << "No new data to append to workspace in " << this->name() << '\n';
330 this->setProperty("OutputWorkspace", input_ws);
331 return; // POSSIBLE EXIT POINT
332 }
333 this->interruption_point();
334
335 // If we reach here then new data exists to append to the input workspace
336 // Use CreateMD with the new data to make a temp workspace
337 // Merge the temp workspace with the input workspace using MergeMD
338 IMDEventWorkspace_sptr tmp_ws = createMDWorkspace(input_data, psi, gl, gs, efix, "", false);
339 this->interruption_point();
340 this->progress(0.5); // Report as CreateMD is complete
341
342 const std::string temp_ws_name = "TEMP_WORKSPACE_ACCUMULATEMD";
343 // Currently have to use ADS here as list of workspaces can only be passed as
344 // a list of workspace names as a string
345 AnalysisDataService::Instance().add(temp_ws_name, tmp_ws);
346 std::string ws_names_to_merge = input_ws->getName();
347 ws_names_to_merge.append(",");
348 ws_names_to_merge.append(temp_ws_name);
349
350 Algorithm_sptr merge_alg = createChildAlgorithm("MergeMD");
351 merge_alg->setProperty("InputWorkspaces", ws_names_to_merge);
352 merge_alg->executeAsChildAlg();
353
354 API::IMDEventWorkspace_sptr out_ws = merge_alg->getProperty("OutputWorkspace");
355
356 this->setProperty("OutputWorkspace", out_ws);
357 g_log.notice() << this->name() << " successfully appended data\n";
358
359 this->progress(1.0); // Report as MergeMD is complete
360
361 // Clean up temporary workspace
362 AnalysisDataService::Instance().remove(temp_ws_name);
363}
364
365/*
366 * Use the CreateMD algorithm to create an MD workspace
367 * @param data_sources :: Vector of input data sources
368 * @param psi :: Vector of goniometer angle psi containing a value for each data
369 * source
370 * @param gl :: Vector of goniometer anglegl containing a value for each data
371 * source
372 * @param gs :: Vector of goniometer angle gs containing a value for each data
373 * source
374 * @param efix :: Vector of data source energy values in meV
375 * @returns the newly created workspace
376 */
377IMDEventWorkspace_sptr AccumulateMD::createMDWorkspace(const std::vector<std::string> &data_sources,
378 const std::vector<double> &psi, const std::vector<double> &gl,
379 const std::vector<double> &gs, const std::vector<double> &efix,
380 const std::string &filename, const bool filebackend) {
381
382 Algorithm_sptr create_alg = createChildAlgorithm("CreateMD");
383
384 create_alg->setProperty("DataSources", data_sources);
385 create_alg->setProperty("EFix", efix);
386 create_alg->setPropertyValue("EMode", this->getPropertyValue("EMode"));
387 create_alg->setPropertyValue("Alatt", this->getPropertyValue("Alatt"));
388 create_alg->setPropertyValue("Angdeg", this->getPropertyValue("Angdeg"));
389 create_alg->setPropertyValue("u", this->getPropertyValue("u"));
390 create_alg->setPropertyValue("v", this->getPropertyValue("v"));
391 create_alg->setProperty("Psi", psi);
392 create_alg->setProperty("Gl", gl);
393 create_alg->setProperty("Gs", gs);
394 create_alg->setPropertyValue("InPlace", this->getPropertyValue("InPlace"));
395 if (filebackend) {
396 create_alg->setProperty("Filename", filename);
397 create_alg->setProperty("FileBackEnd", filebackend);
398 }
399 create_alg->executeAsChildAlg();
400
401 return create_alg->getProperty("OutputWorkspace");
402}
403
404/*
405 * Validate the input properties
406 * @returns a map of properties names with errors
407 */
408std::map<std::string, std::string> AccumulateMD::validateInputs() {
409 // Create the map
410 std::map<std::string, std::string> validation_output;
411
412 // Get properties to validate
413 const std::vector<std::string> data_sources = this->getProperty("DataSources");
414 const std::vector<double> u = this->getProperty("u");
415 const std::vector<double> v = this->getProperty("v");
416 const std::vector<double> alatt = this->getProperty("Alatt");
417 const std::vector<double> angdeg = this->getProperty("Angdeg");
418 const std::vector<double> psi = this->getProperty("Psi");
419 const std::vector<double> gl = this->getProperty("Gl");
420 const std::vector<double> gs = this->getProperty("Gs");
421 const std::vector<double> efix = this->getProperty("Efix");
422 const std::string filename = this->getProperty("Filename");
423 const bool fileBackEnd = this->getProperty("FileBackEnd");
424
425 if (fileBackEnd && filename.empty()) {
426 validation_output["Filename"] = "Filename must be given if FileBackEnd is required.";
427 }
428
429 const size_t ws_entries = data_sources.size();
430
431 if (u.size() < 3) {
432 validation_output["u"] = "u must have 3 components";
433 }
434 if (v.size() < 3) {
435 validation_output["v"] = "v must have 3 components";
436 }
437 if (alatt.size() < 3) {
438 validation_output["Alatt"] = "Lattice parameters must have 3 components";
439 }
440 if (angdeg.size() < 3) {
441 validation_output["Angdeg"] = "Angle must have 3 components";
442 }
443 if (!psi.empty() && psi.size() != ws_entries) {
444 validation_output["Psi"] = "If Psi is given an entry "
445 "should be provided for "
446 "every input datasource";
447 }
448 if (!gl.empty() && gl.size() != ws_entries) {
449 validation_output["Gl"] = "If Gl is given an entry "
450 "should be provided for "
451 "every input datasource";
452 }
453 if (!gs.empty() && gs.size() != ws_entries) {
454 validation_output["Gs"] = "If Gs is given an entry "
455 "should be provided for "
456 "every input datasource";
457 }
458 if (efix.size() > 1 && efix.size() != ws_entries) {
459 validation_output["EFix"] = "Either specify a single EFix value, or as many "
460 "as there are input datasources";
461 }
462
463 return validation_output;
464}
465
466} // namespace Mantid::MDAlgorithms
std::string name
Definition Run.cpp:60
#define DECLARE_ALGORITHM(classname)
Definition Algorithm.h:538
@ OptionalSave
to specify a file to write to but an empty string is
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.
std::string getPropertyValue(const std::string &name) const override
Get the property held by this object.
This class stores information about the Workspace History used by algorithms on a workspace and the e...
std::shared_ptr< HistoryView > createView() const
Create a flat view of the workspaces algorithm history.
A property class for workspaces.
Support for a property that holds an array of values.
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
Validator to check that a property is not left empty.
The concrete, templated class for properties.
AccumulateMD : Algorithm for appending new data to a MDHistoWorkspace.
Mantid::API::IMDEventWorkspace_sptr createMDWorkspace(const std::vector< std::string > &data_sources, const std::vector< double > &psi, const std::vector< double > &gl, const std::vector< double > &gs, const std::vector< double > &efix, const std::string &filename, const bool filebackend)
Use the CreateMD algorithm to create an MD workspace.
int version() const override
Algorithm's version for identification.
const std::string category() const override
Algorithm's category for identification.
const std::string summary() const override
Algorithm's summary for use in the GUI and help.
std::map< std::string, std::string > validateInputs() override
const std::string name() const override
Algorithms name for identification.
std::shared_ptr< IMDEventWorkspace > IMDEventWorkspace_sptr
Shared pointer to Mantid::API::IMDEventWorkspace.
Kernel::Logger g_log("ExperimentInfo")
static logger object
std::shared_ptr< Algorithm > Algorithm_sptr
Typedef for a shared pointer to an Algorithm.
Definition Algorithm.h:52
std::shared_ptr< const PropertyHistory > PropertyHistory_const_sptr
void MANTID_MDALGORITHMS_DLL insertDataSources(const std::string &data_sources, std::unordered_set< std::string > &historical_data_sources)
Extract names of data sources from workspace history and form a set of historical data sources.
std::string MANTID_MDALGORITHMS_DLL filterToNew(std::vector< std::string > &input_data, const std::vector< std::string > &current_data, std::vector< double > &psi, std::vector< double > &gl, std::vector< double > &gs, std::vector< double > &efix)
Reduce the vector of input data to only data files and workspaces which are not found in the vector o...
void MANTID_MDALGORITHMS_DLL padParameterVector(std::vector< double > &param_vector, const size_t grow_to_size)
Pad vector of parameters to given length.
Definition CreateMD.cpp:40
bool MANTID_MDALGORITHMS_DLL dataExists(const std::string &data_name)
Check if the named data source is an existing workspace or file.
bool fileExists(const std::string &filename)
Test if a file with the given full path name exists.
std::vector< std::string > getHistoricalDataSources(const API::WorkspaceHistory &ws_history, const std::string &create_alg_name, const std::string &accumulate_alg_name)
Return a vector of the names of files and workspaces which have been previously added to the workspac...
std::string MANTID_MDALGORITHMS_DLL filterToExistingSources(std::vector< std::string > &input_data, std::vector< double > &psi, std::vector< double > &gl, std::vector< double > &gs, std::vector< double > &efix)
Reduce the vector of input data to only data files and workspaces which can be found.
bool appearsInCurrentData(const std::string &data_source, const std::vector< std::string > &current_data)
Check if the named data source is in the vector of data currently in the workspace.
STL namespace.
@ Input
An input workspace.
Definition Property.h:53
@ Output
An output workspace.
Definition Property.h:54