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