Mantid
Loading...
Searching...
No Matches
WorkspaceHistory.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 +
14#include "MantidTypes/Core/DateAndTime.h"
15
16#include <boost/algorithm/string/classification.hpp>
17#include <boost/algorithm/string/split.hpp>
18#include <boost/functional/hash.hpp>
19#if BOOST_VERSION == 106900
20#ifndef BOOST_PENDING_INTEGER_LOG2_HPP
21#define BOOST_PENDING_INTEGER_LOG2_HPP
22#include <boost/integer/integer_log2.hpp>
23#endif /* BOOST_PENDING_INTEGER_LOG2_HPP */
24#endif /* BOOST_VERSION */
25#include <boost/uuid/uuid.hpp>
26#include <boost/uuid/uuid_generators.hpp>
27#include <boost/uuid/uuid_io.hpp>
28
29#include "Poco/DateTime.h"
30#include <Poco/DateTimeParser.h>
31
32using boost::algorithm::split;
34
35namespace Mantid::API {
36namespace {
38Kernel::Logger g_log("WorkspaceHistory");
39struct AlgorithmHistorySearch {
40 bool operator()(const AlgorithmHistory_sptr &lhs, const AlgorithmHistory_sptr &rhs) { return (*lhs) < (*rhs); }
41};
42struct AlgorithmHistoryHasher {
43 size_t operator()(const AlgorithmHistory_sptr &x) const {
44 std::size_t nameAsSeed = std::hash<std::string>{}(x->name());
45 boost::hash_combine(nameAsSeed, x->executionDate().totalNanoseconds());
46 return nameAsSeed;
47 }
48};
49struct AlgorithmHistoryComparator {
50 bool operator()(const AlgorithmHistory_sptr &a, const AlgorithmHistory_sptr &b) const {
51 return a->uuid() == b->uuid();
52 }
53};
54} // namespace
55
58
63
66 // Don't copy one's own history onto oneself
67 if (this == &otherHistory) {
68 return;
69 }
70
71 // Merge the histories
72 const AlgorithmHistories &otherAlgorithms = otherHistory.getAlgorithmHistories();
73
74 for (const auto &algHistory : otherAlgorithms) {
75 this->addHistory(algHistory);
76 }
77
78 using UniqueAlgorithmHistories =
79 std::unordered_set<AlgorithmHistory_sptr, AlgorithmHistoryHasher, AlgorithmHistoryComparator>;
80 // It is faster to default construct a set/unordered_set and insert the
81 // elements than use the range-based constructor directly.
82 // See https://stackoverflow.com/a/24477023:
83 // "the constructor actually construct a new node for every element, before
84 // checking its value to determine if it should actually be inserted."
85 UniqueAlgorithmHistories uniqueHistories;
86 for (const auto &algorithmHistory : m_algorithms) {
87 uniqueHistories.insert(algorithmHistory);
88 }
89 m_algorithms.assign(std::begin(uniqueHistories), std::end(uniqueHistories));
90 std::sort(std::begin(m_algorithms), std::end(m_algorithms), AlgorithmHistorySearch());
91}
92
95 // Assume it is always sorted as algorithm history should only be inserted in
96 // the correct order
97 m_algorithms.emplace_back(std::move(algHistory));
98}
99
100/*
101 Return the history length
102 */
103size_t WorkspaceHistory::size() const { return m_algorithms.size(); }
104
109bool WorkspaceHistory::empty() const { return m_algorithms.empty(); }
110
115
123 if (index >= this->size()) {
124 throw std::out_of_range("WorkspaceHistory::getAlgorithmHistory() - Index out of range");
125 }
126 return *std::next(m_algorithms.cbegin(), index);
127}
128
137}
138
144std::shared_ptr<IAlgorithm> WorkspaceHistory::getAlgorithm(const size_t index) const {
145 return Algorithm::fromHistory(*(this->getAlgorithmHistory(index)));
146}
147
152std::shared_ptr<IAlgorithm> WorkspaceHistory::lastAlgorithm() const {
153 if (m_algorithms.empty()) {
154 throw std::out_of_range("WorkspaceHistory::lastAlgorithm() - History contains no algorithms.");
155 }
156 return this->getAlgorithm(this->size() - 1);
157}
158
164void WorkspaceHistory::printSelf(std::ostream &os, const int indent) const {
165 os << std::string(indent, ' ') << m_environment << '\n';
166 os << std::string(indent, ' ') << "Histories:\n";
167 for (const auto &algorithm : m_algorithms) {
168 os << '\n';
169 algorithm->printSelf(os, indent + 2);
170 }
171}
172
173//------------------------------------------------------------------------------------------------
180void WorkspaceHistory::saveNexus(::NeXus::File *file) const {
181 file->makeGroup("process", "NXprocess", true);
182 std::stringstream output;
183
184 // Environment history
185 EnvironmentHistory envHist;
186 output << envHist;
187 char buffer[25];
188 time_t now;
189 time(&now);
190 strftime(buffer, 25, "%Y-%b-%d %H:%M:%S", localtime(&now));
191 file->makeGroup("MantidEnvironment", "NXnote", true);
192 file->writeData("author", "mantid");
193 file->openData("author");
194 file->putAttr("date", std::string(buffer));
195 file->closeData();
196 file->writeData("description", "Mantid Environment data");
197 file->writeData("data", output.str());
198 file->closeGroup();
199
200 // Algorithm History
201 int algCount = 0;
202 for (const auto &algorithm : m_algorithms) {
203 algorithm->saveNexus(file, algCount);
204 }
205
206 // close process group
207 file->closeGroup();
208}
209
210//-------------------------------------------------------------------------------------------------
220void getWordsInString(const std::string &words3, std::string &w1, std::string &w2, std::string &w3) {
222 if (data.count() != 3)
223 throw std::out_of_range("Algorithm list line " + words3 + " is not of the correct format\n");
224
225 w1 = data[0];
226 w2 = data[1];
227 w3 = data[2];
228}
229
230//-------------------------------------------------------------------------------------------------
241void getWordsInString(const std::string &words4, std::string &w1, std::string &w2, std::string &w3, std::string &w4) {
243 if (data.count() != 4)
244 throw std::out_of_range("Algorithm list line " + words4 + " is not of the correct format\n");
245
246 w1 = data[0];
247 w2 = data[1];
248 w3 = data[2];
249 w4 = data[3];
250}
251
252//------------------------------------------------------------------------------------------------
258void WorkspaceHistory::loadNexus(::NeXus::File *file) {
259 // Warn but continue if the group does not exist.
260 try {
261 file->openGroup("process", "NXprocess");
262 } catch (std::exception &) {
263 g_log.warning() << "Error opening the algorithm history field 'process'. "
264 "Workspace will have no history."
265 << "\n";
266 return;
267 }
268
269 loadNestedHistory(file);
270 file->closeGroup();
271}
272
282void WorkspaceHistory::loadNestedHistory(::NeXus::File *file, const AlgorithmHistory_sptr &parent) {
283 // historyNumbers should be sorted by number
284 std::set<int> historyNumbers = findHistoryEntries(file);
285 for (auto historyNumber : historyNumbers) {
286 std::string entryName = "MantidAlgorithm_" + Kernel::Strings::toString(historyNumber);
287 std::string rawData;
288 file->openGroup(entryName, "NXnote");
289 file->readData("data", rawData);
290
291 try {
294 if (parent) {
295 parent->addChildHistory(history);
296 } else {
297 // if not parent point is supplied, assume we're at the top
298 // and attach the history to the workspace
299 this->addHistory(history);
300 }
301 } catch (std::runtime_error &e) {
302 // just log the exception as a warning and continue parsing history
303 g_log.warning() << e.what() << "\n";
304 }
305
306 file->closeGroup();
307 }
308}
309
314std::set<int> WorkspaceHistory::findHistoryEntries(::NeXus::File *file) {
315 std::set<int> historyNumbers;
316 std::map<std::string, std::string> entries;
317 file->getEntries(entries);
318
319 // Histories are numbered MantidAlgorithm_0, ..., MantidAlgorithm_10, etc.
320 // Find all the unique numbers
321 for (auto &entry : entries) {
322 std::string entryName = entry.first;
323 if (entryName.find("MantidAlgorithm_") != std::string::npos) {
324 // Just get the number
325 entryName = entryName.substr(16, entryName.size() - 16);
326 int num = -1;
327 if (Kernel::Strings::convert(entryName, num))
328 historyNumbers.insert(num);
329 }
330 }
331
332 return historyNumbers;
333}
334
342 enum AlgorithmHist {
343 NAME = 0, //< algorithms name
344 EXEC_TIME = 1, //< when the algorithm was run
345 EXEC_DUR = 2, //< execution time for the algorithm
346 UUID = 3, //< the universal unique id of the algorithm
347 PARAMS = 4 //< the algorithm's parameters
348 };
349
350 std::vector<std::string> info;
351 boost::split(info, rawData, boost::is_any_of("\n"));
352
353 const size_t nlines = info.size();
354 if (nlines < 4) { // ignore badly formed history entries still at 4 so that
355 // legacy files can be loaded, 5 is ideal for newer files
356 throw std::runtime_error("Malformed history record: Incorrect record size.");
357 }
358
359 std::string algName, dummy, temp;
360 // get the name and version of the algorithm
361 getWordsInString(info[NAME], dummy, algName, temp);
362
363 // Chop of the v from the version string
364 size_t numStart = temp.find('v');
365 // this doesn't abort if the version string doesn't contain a v
366 numStart = numStart != 1 ? 1 : 0;
367 temp = std::string(temp.begin() + numStart, temp.end());
368 const auto version = boost::lexical_cast<int>(temp);
369
370 // Get the execution date/time
371 std::string date, time;
372 getWordsInString(info[EXEC_TIME], dummy, dummy, date, time);
373 Mantid::Types::Core::DateAndTime utc_start;
374 // If not legacy version construct normally else Parse in the legacy data
375 if (std::isdigit(date[6])) {
376 Mantid::Types::Core::DateAndTime timeConstruction(date + "T" + time);
377 utc_start = timeConstruction;
378 } else {
379 Poco::DateTime start_timedate;
380 // This is needed by the Poco parsing function
381 int tzdiff(-1);
382 if (!Poco::DateTimeParser::tryParse("%Y-%b-%d %H:%M:%S", date + " " + time, start_timedate, tzdiff)) {
383 g_log.warning() << "Error parsing start time in algorithm history entry."
384 << "\n";
385 utc_start = Types::Core::DateAndTime::defaultTime();
386 }
387 utc_start.set_from_time_t(start_timedate.timestamp().epochTime());
388 }
389
390 // Get the duration
391 getWordsInString(info[EXEC_DUR], dummy, dummy, temp, dummy);
392 auto dur = boost::lexical_cast<double>(temp);
393 if (dur < -1.0) {
394 g_log.warning() << "Error parsing duration in algorithm history entry."
395 << "\n";
396 dur = -1.0;
397 }
398
401 std::string uuid;
402 size_t paramNum;
403 if (info[3] != "Parameters:") {
404 uuid = info[UUID];
405 uuid.erase(uuid.find("UUID: "), 6);
406 paramNum = PARAMS;
407 } else {
408 uuid = boost::uuids::to_string(boost::uuids::random_generator()());
409 paramNum = 3;
410 }
411
412 // Create the algorithm history
413 API::AlgorithmHistory alg_hist(algName, version, uuid, utc_start, dur, Algorithm::g_execCount);
414 // Simulate running an algorithm
416
417 // Add property information
418 for (size_t index = static_cast<size_t>(paramNum) + 1; index < nlines; ++index) {
419 const std::string line = info[index];
420 std::string::size_type colon = line.find(':');
421 std::string::size_type comma = line.find(',');
422 // Each colon has a space after it
423 std::string prop_name = line.substr(colon + 2, comma - colon - 2);
424 colon = line.find(':', comma);
425 comma = line.find(", Default?", colon);
426 std::string prop_value = line.substr(colon + 2, comma - colon - 2);
427 colon = line.find(':', comma);
428 comma = line.find(", Direction", colon);
429 std::string is_def = line.substr(colon + 2, comma - colon - 2);
430 colon = line.find(':', comma);
431 comma = line.find(',', colon);
432 std::string direction = line.substr(colon + 2, comma - colon - 2);
433 unsigned int direc(Mantid::Kernel::Direction::asEnum(direction));
434 alg_hist.addProperty(prop_name, prop_value, (is_def[0] == 'Y'), direc);
435 }
436
437 AlgorithmHistory_sptr history = std::make_shared<AlgorithmHistory>(alg_hist);
438 return history;
439}
440
441//-------------------------------------------------------------------------------------------------
444std::shared_ptr<HistoryView> WorkspaceHistory::createView() const { return std::make_shared<HistoryView>(*this); }
445
446//------------------------------------------------------------------------------------------------
452std::ostream &operator<<(std::ostream &os, const WorkspaceHistory &WH) {
453 WH.printSelf(os);
454 return os;
455}
456
457bool WorkspaceHistory::operator==(const WorkspaceHistory &otherHistory) const {
458 return m_algorithms == otherHistory.m_algorithms;
459}
460
461} // namespace Mantid::API
const std::vector< double > & rhs
std::map< DeltaEMode::Type, std::string > index
Definition: DeltaEMode.cpp:19
std::vector< history_type > history
history information
This class stores information about the Command History used by algorithms on a workspace.
void addProperty(const std::string &name, const std::string &value, bool isdefault, const unsigned int &direction=99)
Add a property to the history.
static size_t g_execCount
Counter to keep track of algorithm execution order.
Definition: Algorithm.h:439
static IAlgorithm_sptr fromHistory(const AlgorithmHistory &history)
Construct an object from a history entry.
Definition: Algorithm.cpp:929
This class stores information about the Workspace History used by algorithms on a workspace and the e...
std::set< int > findHistoryEntries(::NeXus::File *file)
Find the history entries at this level in the file.
void loadNestedHistory(::NeXus::File *file, const AlgorithmHistory_sptr &parent=std::shared_ptr< AlgorithmHistory >())
Recursive function to load the algorithm history tree from file.
std::shared_ptr< IAlgorithm > getAlgorithm(const size_t index) const
Create an algorithm from a history record at a given index.
AlgorithmHistory_const_sptr getAlgorithmHistory(const size_t index) const
Retrieve an algorithm history by index.
AlgorithmHistory_const_sptr operator[](const size_t index) const
Add operator[] access.
Mantid::API::AlgorithmHistories m_algorithms
The algorithms which have been called on the workspace.
bool operator==(const WorkspaceHistory &otherHistory) const
Add an operator== that compares algorithm historys.
WorkspaceHistory()
Default constructor.
std::shared_ptr< HistoryView > createView() const
Create a flat view of the workspaces algorithm history.
void printSelf(std::ostream &, const int indent=0) const
Pretty print the entire history.
void saveNexus(::NeXus::File *file) const
Save the workspace history to a nexus file.
void addHistory(const WorkspaceHistory &otherHistory)
Append an workspace history to this one.
std::shared_ptr< IAlgorithm > lastAlgorithm() const
Convenience function for retrieving the last algorithm.
const Kernel::EnvironmentHistory & getEnvironmentHistory() const
Retrieve the environment history.
bool empty() const
Is the history empty.
const AlgorithmHistories & getAlgorithmHistories() const
Retrieve the algorithm history list.
AlgorithmHistory_sptr parseAlgorithmHistory(const std::string &rawData)
Parse an algorithm history string loaded from file.
size_t size() const
How many entries are there.
void clearHistory()
remove all algorithm history objects from the workspace history
const Kernel::EnvironmentHistory m_environment
The environment of the workspace.
void loadNexus(::NeXus::File *file)
Load the workspace history from a nexus file.
This class stores information about the Environment of the computer used by the framework.
void printSelf(std::ostream &, const int indent=0) const
print contents of object
void warning(const std::string &msg)
Logs at warning level.
Definition: Logger.cpp:86
@ TOK_TRIM
remove leading and trailing whitespace from tokens
std::size_t count() const
Get the total number of tokens.
std::vector< AlgorithmHistory_sptr > AlgorithmHistories
void getWordsInString(const std::string &words3, std::string &w1, std::string &w2, std::string &w3)
If the first string contains exactly three words separated by spaces these words will be copied into ...
MANTID_API_DLL std::ostream & operator<<(std::ostream &, const AlgorithmHistory &)
Prints a text representation.
std::shared_ptr< const AlgorithmHistory > AlgorithmHistory_const_sptr
Kernel::Logger g_log("ExperimentInfo")
static logger object
std::shared_ptr< AlgorithmHistory > AlgorithmHistory_sptr
int convert(const std::string &A, T &out)
Convert a string into a number.
Definition: Strings.cpp:665
std::string toString(const T &value)
Convert a number to a string.
Definition: Strings.cpp:703
static int asEnum(const std::string &direction)
Returns an enum representation of the input Direction string.
Definition: Property.h:74