Newer
Older
Peterson, Peter
committed
#include "MantidAlgorithms/AddSampleLog.h"
Federico Montesino Pouzols
committed
#include "MantidAPI/ExperimentInfo.h"
#include "MantidAPI/Run.h"
#include "MantidAPI/Workspace.h"
#include "MantidAPI/MatrixWorkspace.h"
Steve Williams
committed
#include "MantidKernel/Exception.h"
Janik Zikovsky
committed
#include "MantidKernel/ListValidator.h"
#include "MantidKernel/MandatoryValidator.h"
Janik Zikovsky
committed
#include "MantidKernel/Strings.h"
#include "MantidKernel/TimeSeriesProperty.h"
#include "MantidKernel/PropertyWithValue.h"
Federico Montesino Pouzols
committed
Steve Williams
committed
#include <string>
namespace {
static const std::string intTypeOption = "Int";
static const std::string doubleTypeOption = "Double";
static const std::string autoTypeOption = "AutoDetect";
static const std::string stringLogOption = "String";
static const std::string numberLogOption = "Number";
static const std::string numberSeriesLogOption = "Number Series";
}
namespace Mantid {
namespace Algorithms {
Steve Williams
committed
// Register the algorithm into the AlgorithmFactory
DECLARE_ALGORITHM(AddSampleLog)
using namespace Kernel;
using namespace API;
declareProperty(Kernel::make_unique<WorkspaceProperty<Workspace>>(
"Workspace", "", Direction::InOut),
"Workspace to add the log entry to");
boost::make_shared<MandatoryValidator<std::string>>(),
"The name that will identify the log entry");
Janik Zikovsky
committed
declareProperty("LogText", "", "The content of the log");
Janik Zikovsky
committed
std::vector<std::string> propOptions;
propOptions.push_back(stringLogOption);
propOptions.push_back(numberLogOption);
propOptions.push_back(numberSeriesLogOption);
declareProperty("LogType", stringLogOption,
boost::make_shared<StringListValidator>(propOptions),
"The type that the log data will be.");
declareProperty("LogUnit", "", "The units of the log");
std::vector<std::string> typeOptions;
typeOptions.push_back(intTypeOption);
typeOptions.push_back(doubleTypeOption);
typeOptions.push_back(autoTypeOption);
declareProperty("NumberType", autoTypeOption,
boost::make_shared<StringListValidator>(typeOptions),
"Force LogText to be interpreted as a number of type 'int' "
"or 'double'.");
// add optional workspace which contains the data of the TimeSeriesProperty
declareProperty(
Kernel::make_unique<WorkspaceProperty<API::MatrixWorkspace>>(
"TimeSeriesWorkspace", "", Direction::Input, PropertyMode::Optional),
"Optional workspace contain the data");
declareProperty(
"WorkspaceIndex", 0,
"The workspace index of the TimeSeriesWorkspace to be imported.");
declareProperty("AutoMetaData", false,
"If it is specified as true, then all the meta data "
"information will be retrieved from the input workspace. It "
"will be used with algorithm ExportTimeSeriesProperty.");
std::vector<std::string> time_units{"Second", "Nanosecond"};
declareProperty("TimeUnit", "Second",
boost::make_shared<Kernel::StringListValidator>(time_units),
"The unit of the time of the input workspace");
declareProperty("RelativeTime", true, "If specified as True, then then the "
"time stamps are relative to the run "
"start time of the target workspace.");
Steve Williams
committed
}
/**
* @brief AddSampleLog::exec
*/
Steve Williams
committed
// A pointer to the workspace to add a log to
Workspace_sptr target_workspace = getProperty("Workspace");
ExperimentInfo_sptr expinfo_ws =
boost::dynamic_pointer_cast<ExperimentInfo>(target_workspace);
// we're going to edit the workspaces run details so get a non-const reference
// to it
Run &theRun = expinfo_ws->mutableRun();
Steve Williams
committed
// get the data that the user wants to add
std::string propName = getProperty("LogName");
std::string propValue = getProperty("LogText");
std::string propUnit = getProperty("LogUnit");
Janik Zikovsky
committed
std::string propType = getPropertyValue("LogType");
std::string propNumberType = getPropertyValue("NumberType");
if ((propNumberType != autoTypeOption) &&
((propType != numberLogOption) && (propType != numberSeriesLogOption))) {
throw std::invalid_argument(
"You may only use NumberType 'Int' or 'Double' options if "
"LogType is 'Number' or 'Number Series'");
Janik Zikovsky
committed
// Remove any existing log
Janik Zikovsky
committed
theRun.removeLogData(propName);
Steve Williams
committed
// add sample log!
if (propType == stringLogOption) {
// add string log value and return
addStringLog(theRun, propName, propValue, propUnit);
} else if (propType == numberSeriesLogOption) {
// add a TimeSeriesProperty
// TODO: Need to re-define the behavior on the default propNumberType for Series.
// If propValue is given, then use this value to determine whether the type is
// integer or double; Otherwise, the series should be double
addTimeSeriesProperty(theRun, propName, propValue, propUnit,
propNumberType);
} else {
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
// // TODO: Refactor this section to a method!
// // add a single value property, integer or double
// bool value_is_int(false);
// if (propNumberType != autoTypeOption) {
// value_is_int = (propNumberType == intTypeOption);
// } else {
// int intVal;
// if (Strings::convert(propValue, intVal)) {
// value_is_int = true;
// }
// }
// // set value
// if (value_is_int) {
// // convert to integer
// int intVal;
// bool convert_to_int = Strings::convert(propValue, intVal);
// if (!convert_to_int) {
// // spit out error message and set to default value
// g_log.error() << "Error interpreting string '" << propValue
// << "' as NumberType Int.";
// throw std::runtime_error("Invalie integer input");
// // intVal = 0;
// }
// theRun.addLogData(new PropertyWithValue<int>(propName, intVal));
// } else {
// // convert to double
// double dblVal;
// bool convert_to_dbl = Strings::convert(propValue, dblVal);
// if (!convert_to_dbl) {
// g_log.error() << "Error interpreting string '" << propValue
// << "' as NumberType Double.";
// throw std::runtime_error("Invalid double input.");
// // dblVal = 0.;
// }
// theRun.addLogData(new PropertyWithValue<double>(propName, dblVal));
// g_log.warning() << "added property " << propName << " with value "
// << dblVal << "\n";
// }
// // add unit
// theRun.getProperty(propName)->setUnits(propUnit);
addSingleValueProperty(theRun, propName, propValue, propUnit, propNumberType);
}
return;
}
void AddSampleLog::addSingleValueProperty(Run &theRun, const std::string &propName,
const std::string &propValue,
const std::string &propUnit,
const std::string &propNumberType)
{
// TODO: Refactor this section to a method!
// add a single value property, integer or double
bool value_is_int(false);
if (propNumberType != autoTypeOption) {
value_is_int = (propNumberType == intTypeOption);
} else {
int intVal;
if (Strings::convert(propValue, intVal)) {
value_is_int = true;
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
// set value
if (value_is_int) {
// convert to integer
int intVal;
bool convert_to_int = Strings::convert(propValue, intVal);
if (!convert_to_int) {
// spit out error message and set to default value
g_log.error() << "Error interpreting string '" << propValue
<< "' as NumberType Int.";
throw std::runtime_error("Invalie integer input");
// intVal = 0;
}
theRun.addLogData(new PropertyWithValue<int>(propName, intVal));
} else {
// convert to double
double dblVal;
bool convert_to_dbl = Strings::convert(propValue, dblVal);
if (!convert_to_dbl) {
g_log.error() << "Error interpreting string '" << propValue
<< "' as NumberType Double.";
throw std::runtime_error("Invalid double input.");
// dblVal = 0.;
}
theRun.addLogData(new PropertyWithValue<double>(propName, dblVal));
g_log.warning() << "added property " << propName << " with value "
<< dblVal << "\n";
Janik Zikovsky
committed
}
// add unit
theRun.getProperty(propName)->setUnits(propUnit);
/** Add a sample log (property) with value as string
* @brief AddSampleLog::addStringLog
* @param theRun
* @param propName
* @param propValue
* @param propUnit
*/
void AddSampleLog::addStringLog(Run &theRun, const std::string &propName,
const std::string &propValue,
const std::string &propUnit) {
theRun.addLogData(new PropertyWithValue<std::string>(propName, propValue));
theRun.getProperty(propName)->setUnits(propUnit);
/** Add a sample log as a TimeSeriesProperty
* @brief AddSampleLog::addTimeSeriesProperty
* @param run_obj
* @param prop_name
* @param prop_value
* @param prop_unit
* @param prop_number_type
*/
void AddSampleLog::addTimeSeriesProperty(Run &run_obj,
const std::string &prop_name,
const std::string &prop_value,
const std::string &prop_unit,
const std::string &prop_number_type) {
// set up the number type right
bool is_int_series(false);
if (prop_number_type.compare(intTypeOption) == 0) {
// integer type
is_int_series = true;
} else if (prop_number_type.compare(autoTypeOption) == 0) {
// auto type. by default
g_log.warning("For sample log in TimeSeriesProperty the default data type "
"is double.");
} else if (prop_number_type.compare(doubleTypeOption) != 0) {
// unsupported type
g_log.error() << "TimeSeriesProperty with data type " << prop_number_type
<< " is not supported.\n";
throw std::runtime_error("Unsupported TimeSeriesProperty type.");
// check using workspace or some specified start value
std::string tsp_ws_name = getPropertyValue("TimeSeriesWorkspace");
bool use_ws = tsp_ws_name.size() > 0;
bool use_single_value = prop_value.size() > 0;
if (use_ws && use_single_value) {
throw std::runtime_error("Both TimeSeries workspace and sing value are "
"specified. It is not allowed.");
} else if (!use_ws && !use_single_value) {
throw std::runtime_error("Neither TimeSeries workspace or sing value are "
"specified. It is not allowed.");
}
// create workspace
// get run start
Kernel::DateAndTime startTime = getRunStart(run_obj);
// initialze the TimeSeriesProperty and add unit
if (is_int_series) {
auto tsp = new TimeSeriesProperty<int>(prop_name);
if (Strings::convert(prop_value, intVal)) {
tsp->addValue(startTime, intVal);
} else {
throw std::invalid_argument(
"Input value cannot be converted to an integer value.");
} else {
auto tsp = new TimeSeriesProperty<double>(prop_name);
if (Strings::convert(prop_value, dblVal)) {
tsp->addValue(startTime, dblVal);
} else {
throw std::invalid_argument(
"Input value cannot be converted to a double number.");
}
// add unit
run_obj.getProperty(prop_name)->setUnits(prop_unit);
if (use_ws)
setTimeSeriesData(run_obj, prop_name, is_int_series);
/** Set value of a TimeSeriesProperty from input workspace
* @brief AddSampleLog::setTimeSeriesData
* @param run_obj
* @param property_name
* @param value_is_int
*/
void AddSampleLog::setTimeSeriesData(Run &run_obj,
const std::string &property_name,
bool value_is_int) {
MatrixWorkspace_sptr data_ws = getProperty("TimeSeriesWorkspace");
int ws_index = getProperty("WorkspaceIndex");
if (ws_index < 0 ||
ws_index > static_cast<int>(data_ws->getNumberHistograms()))
throw std::runtime_error("Input workspace index is out of range");
// get meta data
bool epochtime(false);
std::string timeunit;
getMetaData(data_ws, epochtime, timeunit);
bool is_second = timeunit.compare("Second") == 0;
// convert the data in workspace to time series property value
std::vector<DateAndTime> time_vec =
getTimes(data_ws, ws_index, epochtime, is_second, run_obj);
if (value_is_int) {
TimeSeriesProperty<int> *int_prop = dynamic_cast<TimeSeriesProperty<int> *>(
run_obj.getProperty(property_name));
std::vector<int> value_vec = getIntValues(data_ws, ws_index);
int_prop->addValues(time_vec, value_vec);
TimeSeriesProperty<double> *int_prop =
dynamic_cast<TimeSeriesProperty<double> *>(
run_obj.getProperty(property_name));
std::vector<double> value_vec = getDblValues(data_ws, ws_index);
int_prop->addValues(time_vec, value_vec);
}
return;
}
/** Get the time vector for the TimeSeriesProperty to which the entries is to
* set
* @brief AddSampleLog::getTimes
* @param dataws
* @param workspace_index
* @param is_epoch
* @param is_second
* @param run_obj
* @return
*/
std::vector<Kernel::DateAndTime>
AddSampleLog::getTimes(API::MatrixWorkspace_const_sptr dataws,
int workspace_index, bool is_epoch, bool is_second,
API::Run &run_obj) {
// get run start time
int64_t timeshift(0);
// get the run start time
Kernel::DateAndTime run_start_time = getRunStart(run_obj);
timeshift = run_start_time.totalNanoseconds();
}
// set up the time vector
std::vector<Kernel::DateAndTime> timevec;
size_t vecsize = dataws->readX(workspace_index).size();
double timedbl = dataws->readX(workspace_index)[i];
if (is_second)
timedbl *= 1.E9;
int64_t entry_i64 = static_cast<int64_t>(timedbl);
Kernel::DateAndTime entry(timeshift + entry_i64);
timevec.push_back(entry);
}
return timevec;
}
/** Get run start time from the target workspace to add the property
* @brief AddSampleLog::getRunStart
* @param run_obj
* @return
*/
Kernel::DateAndTime AddSampleLog::getRunStart(API::Run &run_obj) {
// TODO/ISSUE/NOW - data ws should be the target workspace with run_start or
// proton_charge property!
Kernel::DateAndTime runstart(0);
try {
runstart = run_obj.startTime();
} catch (std::runtime_error &) {
// Swallow the error - startTime will just be 0
}
return runstart;
}
/** Get the values (in double) of the TimeSeriesProperty's entries from input
* workspace
* @brief AddSampleLog::getDblValues
* @param dataws
* @param workspace_index
* @return
*/
std::vector<double>
AddSampleLog::getDblValues(API::MatrixWorkspace_const_sptr dataws,
int workspace_index) {
std::vector<double> valuevec;
size_t vecsize = dataws->readY(workspace_index).size();
for (size_t i = 0; i < vecsize; ++i)
valuevec.push_back(dataws->readY(workspace_index)[i]);
return valuevec;
}
/** Get the values (in integer) of the TimeSeriesProperty's entries from input
* workspace
* @brief AddSampleLog::getIntValues
* @param dataws
* @param workspace_index
* @return
*/
std::vector<int>
AddSampleLog::getIntValues(API::MatrixWorkspace_const_sptr dataws,
int workspace_index) {
std::vector<int> valuevec;
size_t vecsize = dataws->readY(workspace_index).size();
for (size_t i = 0; i < vecsize; ++i)
valuevec.push_back(static_cast<int>(dataws->readY(workspace_index)[i]));
return valuevec;
}
/** Get Meta data from input data workspace's properties
* @brief AddSampleLog::getMetaData
* @param dataws
* @param epochtime
* @param timeunit
*/
void AddSampleLog::getMetaData(API::MatrixWorkspace_const_sptr dataws,
bool &epochtime, std::string &timeunit) {
bool auto_meta = getProperty("AutoMetaData");
// get the meta data from the input workspace
std::string epochtimestr =
dataws->run().getProperty("IsEpochTime")->value();
epochtime = epochtimestr.compare("true") == 0;
timeunit = dataws->run().getProperty("TimeUnit")->value();
// get the meta data from input
epochtime = !getProperty("RelativeTime");
timeunit = getPropertyValue("TimeUnit");
Steve Williams
committed
}
} // namespace Algorithms
} // namespace Mantid