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