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
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 if (file->hasGroup("process", "NXprocess")) {
260 file->openGroup("process", "NXprocess");
261 loadNestedHistory(file);
262 file->closeGroup();
263 } else {
264 // Warn but continue if the group does not exist.
265 g_log.warning() << "Error opening the algorithm history field 'process'. "
266 "Workspace will have no history."
267 << "\n";
268 }
269}
270
280void WorkspaceHistory::loadNestedHistory(Nexus::File *file, const AlgorithmHistory_sptr &parent) {
281 // historyNumbers should be sorted by number
282 std::set<int> historyNumbers = findHistoryEntries(file);
283 for (auto historyNumber : historyNumbers) {
284 std::string entryName = "MantidAlgorithm_" + Kernel::Strings::toString(historyNumber);
285 std::string rawData;
286 file->openGroup(entryName, "NXnote");
287 file->readData("data", rawData);
288
289 try {
292 if (parent) {
293 parent->addChildHistory(history);
294 } else {
295 // if not parent point is supplied, assume we're at the top
296 // and attach the history to the workspace
297 this->addHistory(history);
298 }
299 } catch (std::runtime_error &e) {
300 // just log the exception as a warning and continue parsing history
301 g_log.warning() << e.what() << "\n";
302 }
303
304 file->closeGroup();
305 }
306}
307
312std::set<int> WorkspaceHistory::findHistoryEntries(Nexus::File const *file) {
313 std::set<int> historyNumbers;
314 std::map<std::string, std::string> entries;
315 file->getEntries(entries);
316
317 // Histories are numbered MantidAlgorithm_0, ..., MantidAlgorithm_10, etc.
318 // Find all the unique numbers
319 for (auto const &entry : entries) {
320 std::string entryName = entry.first;
321 if (entryName.find("MantidAlgorithm_") != std::string::npos) {
322 // Just get the number
323 entryName = entryName.substr(16, entryName.size() - 16);
324 int num = -1;
325 if (Kernel::Strings::convert(entryName, num))
326 historyNumbers.insert(num);
327 }
328 }
329
330 return historyNumbers;
331}
332
340 enum AlgorithmHist {
341 NAME = 0, //< algorithms name
342 EXEC_TIME = 1, //< when the algorithm was run
343 EXEC_DUR = 2, //< execution time for the algorithm
344 UUID = 3, //< the universal unique id of the algorithm
345 PARAMS = 4 //< the algorithm's parameters
346 };
347
348 // split on lines
349 std::vector<std::string> info;
350 boost::split(info, rawData, boost::is_any_of("\n"));
351
352 const size_t nlines = info.size();
353 if (nlines < 4) { // ignore badly formed history entries still at 4 so that
354 // legacy files can be loaded, 5 is ideal for newer files
355 throw std::runtime_error("Malformed history record: Incorrect record size.");
356 }
357
358 std::string algName, dummy, temp;
359 // get the name and version of the algorithm
360 getWordsInString(info[NAME], dummy, algName, temp);
361
362 // Chop of the v from the version string
363 size_t numStart = temp.find('v');
364 // this doesn't abort if the version string doesn't contain a v
365 numStart = numStart != 1 ? 1 : 0;
366 temp = std::string(temp.begin() + numStart, temp.end());
367 const auto version = boost::lexical_cast<int>(temp);
368
369 // Get the execution date/time
370 std::string date, time;
371 getWordsInString(info[EXEC_TIME], dummy, dummy, date, time);
372 Mantid::Types::Core::DateAndTime utc_start;
373 // If not legacy version construct normally else Parse in the legacy data
374 if (std::isdigit(static_cast<unsigned char>(date[6]))) {
375 Mantid::Types::Core::DateAndTime timeConstruction(date + "T" + time);
376 utc_start = timeConstruction;
377 } else {
378 Poco::DateTime start_timedate;
379 // This is needed by the Poco parsing function
380 int tzdiff(-1);
381 if (!Poco::DateTimeParser::tryParse("%Y-%b-%d %H:%M:%S", date + " " + time, start_timedate, tzdiff)) {
382 g_log.warning() << "Error parsing start time in algorithm history entry."
383 << "\n";
384 utc_start = Types::Core::DateAndTime::defaultTime();
385 }
386 utc_start.set_from_time_t(start_timedate.timestamp().epochTime());
387 }
388
389 // Get the duration
390 getWordsInString(info[EXEC_DUR], dummy, dummy, temp, dummy);
391 auto dur = boost::lexical_cast<double>(temp);
392 if (dur < -1.0) {
393 g_log.warning() << "Error parsing duration in algorithm history entry."
394 << "\n";
395 dur = -1.0;
396 }
397
400 std::string uuid;
401 size_t paramNum;
402 if (info[3] != "Parameters:") {
403 uuid = info[UUID];
404 uuid.erase(uuid.find("UUID: "), 6);
405 paramNum = PARAMS;
406 } else {
407 uuid = boost::uuids::to_string(boost::uuids::random_generator()());
408 paramNum = 3;
409 }
410
411 // Create the algorithm history
412 API::AlgorithmHistory alg_hist(algName, version, uuid, utc_start, dur, Algorithm::g_execCount);
413 // Simulate running an algorithm
415
416 // Add property information
417 for (size_t index = static_cast<size_t>(paramNum) + 1; index < nlines; ++index) {
418 const std::string line = info[index];
419 std::string::size_type colon = line.find(':');
420 std::string::size_type comma = line.find(',');
421 // Each colon has a space after it
422 std::string prop_name = line.substr(colon + 2, comma - colon - 2);
423 colon = line.find(':', comma);
424 comma = line.find(", Default?", colon);
425 std::string prop_value = line.substr(colon + 2, comma - colon - 2);
426 colon = line.find(':', comma);
427 comma = line.find(", Direction", colon);
428 std::string is_def = line.substr(colon + 2, comma - colon - 2);
429 colon = line.find(':', comma);
430 comma = line.find(',', colon);
431 std::string direction = line.substr(colon + 2, comma - colon - 2);
432 unsigned int direc(Mantid::Kernel::Direction::asEnum(direction));
433 alg_hist.addProperty(prop_name, prop_value, (is_def[0] == 'Y'), direc);
434 }
435
436 AlgorithmHistory_sptr history = std::make_shared<AlgorithmHistory>(alg_hist);
437 return history;
438}
439
440//-------------------------------------------------------------------------------------------------
443std::shared_ptr<HistoryView> WorkspaceHistory::createView() const { return std::make_shared<HistoryView>(*this); }
444
445//------------------------------------------------------------------------------------------------
451std::ostream &operator<<(std::ostream &os, const WorkspaceHistory &WH) {
452 WH.printSelf(os);
453 return os;
454}
455
456bool WorkspaceHistory::operator==(const WorkspaceHistory &otherHistory) const {
457 return m_algorithms == otherHistory.m_algorithms;
458}
459
460} // namespace Mantid::API
const std::vector< double > & rhs
std::map< DeltaEMode::Type, std::string > index
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:410
static IAlgorithm_sptr fromHistory(const AlgorithmHistory &history)
Construct an object from a history entry.
This class stores information about the Workspace History used by algorithms on a workspace and the e...
std::shared_ptr< IAlgorithm > getAlgorithm(const size_t index) const
Create an algorithm from a history record at a given index.
void loadNexus(Nexus::File *file)
Load the workspace history from a nexus file.
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.
void saveNexus(Nexus::File *file) const
Save the workspace history to a nexus file.
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 loadNestedHistory(Nexus::File *file, const AlgorithmHistory_sptr &parent=std::shared_ptr< AlgorithmHistory >())
Recursive function to load the algorithm history tree from file.
void printSelf(std::ostream &, const int indent=0) const
Pretty print the entire history.
void addHistory(const WorkspaceHistory &otherHistory)
Append an workspace history to this one.
std::set< int > findHistoryEntries(Nexus::File const *file)
Find the history entries at this level in the file.
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.
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:117
@ 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:696
std::string toString(const T &value)
Convert a number to a string.
Definition Strings.cpp:734
static int asEnum(const std::string &direction)
Returns an enum representation of the input Direction string.
Definition Property.h:74