Newer
Older
#include "MantidAlgorithms/CalcCountRate.h"
#include "MantidKernel/PropertyWithValue.h"
#include "MantidKernel/BoundedValidator.h"
#include "MantidKernel/TimeSeriesProperty.h"
#include "MantidKernel/ListValidator.h"
#include "MantidKernel/UnitFactory.h"
#include "MantidKernel/MandatoryValidator.h"
#include "MantidAPI/Axis.h"
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
#include "MantidDataObjects/Workspace2D.h"
namespace Mantid {
namespace Algorithms {
// Register the algorithm into the AlgorithmFactory
DECLARE_ALGORITHM(CalcCountRate)
//----------------------------------------------------------------------------------------------
/// Algorithms name for identification. @see Algorithm::name
const std::string CalcCountRate::name() const { return "CalcCountRate"; }
/// Algorithm's version for identification. @see Algorithm::version
int CalcCountRate::version() const { return 1; }
/// Algorithm's category for identification. @see Algorithm::category
const std::string CalcCountRate::category() const {
return "Inelastic\\Utility";
}
/// Algorithm's summary for use in the GUI and help. @see Algorithm::summary
const std::string CalcCountRate::summary() const {
return "Calculates instrument count rate as the function of the "
"experiment time and adds CountRate log to the source workspace.";
}
//----------------------------------------------------------------------------------------------
/** Declare the algorithm's properties.
*/
void CalcCountRate::init() {
declareProperty(
Kernel::make_unique<API::WorkspaceProperty<DataObjects::EventWorkspace>>(
"Workspace", "", Kernel::Direction::InOut),
"Name of the event workspace to calculate counting rate for.");
declareProperty(Kernel::make_unique<Kernel::PropertyWithValue<double>>(
"XMin", EMPTY_DBL(), Kernel::Direction::Input),
"Minimal value of X-range for the rate calculations. If left "
"to default, Workspace X-axis minimal value is used.");
declareProperty(Kernel::make_unique<Kernel::PropertyWithValue<double>>(
"XMax", EMPTY_DBL(), Kernel::Direction::Input),
"Maximal value of X-range for the rate calculations. If left "
"to default, Workspace X-axis maximal value is used.");
declareProperty(
Kernel::make_unique<Kernel::PropertyWithValue<std::string>>(
"RangeUnits", "Energy",
boost::make_shared<Kernel::StringListValidator>(
Kernel::UnitFactory::Instance().getKeys()),
Kernel::Direction::Input),
"The units from Mantid Unit factory for calculating the "
"counting rate and XMin-XMax ranges are in. If the "
"X-axis of the input workspace is not expressed"
"in this units, unit conversion will be performed, so the "
"workspace should contain all necessary information for this "
"conversion. E.g. if *RangeUnits* is *EnergyTransfer*, Ei "
"log containing incident energy value should be attached to the "
"input workspace.");
std::vector<std::string> propOptions{"Elastic", "Direct", "Indirect"};
declareProperty("EMode", "Elastic",
boost::make_shared<Kernel::StringListValidator>(propOptions),
"The range units above conversion mode (default: elastic)");
// Used logs group
std::string used_logs_mode("Used normalization logs");
declareProperty(
"NormalizeTheRate", true,
"Usually you want to normalize counting rate to some "
"rate related to the source beam intensity. Change this to "
"'false' if appropriate time series log is broken || not attached to "
"the input workspace.");
declareProperty("UseLogDerivative", false, "If the normalization log gives "
"cumulative counting, derivative "
"of this log is necessary to get "
"correct normalization values.");
declareProperty(
"NormalizationLogName", "proton_charge",
"The name of the log, used in the counting rate normalization. ");
declareProperty(
"UseNormLogGranularity", true,
"If true, the calculated log will have the normalization log "
"accuracy; If false, the 'NumTimeSteps' in the visualization "
"workspace below will be used for the target log granularity too.");
setPropertyGroup("NormalizeTheRate", used_logs_mode);
setPropertyGroup("UseLogDerivative", used_logs_mode);
setPropertyGroup("NormalizationLogName", used_logs_mode);
setPropertyGroup("UseNormLogGranularity", used_logs_mode);
declareProperty(
"CountRateLogName", "block_count_rate",
boost::make_shared<Kernel::MandatoryValidator<std::string>>(),
"The name of the processed time series log with count rate to add"
" to the source workspace");
// visualisation group
std::string spur_vis_mode("Spurion visualisation");
declareProperty(
Kernel::make_unique<API::WorkspaceProperty<DataObjects::Workspace2D>>(
"VisualizationWs", "", Kernel::Direction::Output,
API::PropertyMode::Optional),
"Optional name to build 2D matrix workspace for spurion visualization. "
"If name is provided, a 2D workspace with this name will be created "
"containing data to visualize counting rate as function of time in the "
"ranges "
auto mustBeReasonable = boost::make_shared<Kernel::BoundedValidator<int>>();
mustBeReasonable->setLower(2);
declareProperty(
Kernel::make_unique<Kernel::PropertyWithValue<int>>(
"NumTimeSteps", 200, mustBeReasonable, Kernel::Direction::Input),
"Number of time steps (time accuracy) the visualization workspace has. "
"Also number of steps in 'CountRateLogName' log if "
"'UseNormLogGranularity' is set to false");
declareProperty(
Kernel::make_unique<Kernel::PropertyWithValue<int>>(
"XResolution", 100, mustBeReasonable, Kernel::Direction::Input),
"Number of steps (accuracy) of the visualization workspace has along "
"X-axis. ");
setPropertyGroup("VisualizationWs", spur_vis_mode);
setPropertyGroup("NumTimeSteps", spur_vis_mode);
setPropertyGroup("XResolution", spur_vis_mode);
}
//----------------------------------------------------------------------------------------------
/** Execute the algorithm.
*/
void CalcCountRate::exec() {
DataObjects::EventWorkspace_sptr sourceWS = getProperty("Workspace");
// Identity correct way to treat input logs and general properties of output
// log
this->setOutLogParameters(sourceWS);
// identify ranges for count rate calculations and initiate source workspace
this->setSourceWSandXRanges(m_workingWS);
// create results log and add it to the source workspace
std::string logname = getProperty("CountRateLogName");
auto newlog = new Kernel::TimeSeriesProperty<double>(logname);
sourceWS->mutableRun().addProperty(newlog, true);
// calculate averages requested and modify results log
// clear up log derivative and existing log pointer (if any)
// to avoid incorrect usage
// at subsequent calls to the same algorithm.
m_tmpLogHolder.release();
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
191
192
193
/*
auto newlog = new TimeSeriesProperty<double>(logname);
newlog->setUnits(oldlog->units());
int size = oldlog->realSize();
vector<double> values = oldlog->valuesAsVector();
vector<DateAndTime> times = oldlog->timesAsVector();
for (int i = 0; i < size; i++) {
newlog->addValue(times[i] + offset, values[i]);
}
// Just overwrite if the change is in place
MatrixWorkspace_sptr outputWS = getProperty("OutputWorkspace");
if (outputWS != inputWS) {
IAlgorithm_sptr duplicate = createChildAlgorithm("CloneWorkspace");
duplicate->initialize();
duplicate->setProperty<Workspace_sptr>(
"InputWorkspace", boost::dynamic_pointer_cast<Workspace>(inputWS));
duplicate->execute();
Workspace_sptr temp = duplicate->getProperty("OutputWorkspace");
outputWS = boost::dynamic_pointer_cast<MatrixWorkspace>(temp);
setProperty("OutputWorkspace", outputWS);
}
outputWS->mutableRun().addProperty(newlog, true);
*/
}
void CalcCountRate::calcRateLog(
DataObjects::EventWorkspace_sptr &InputWorkspace,
Kernel::TimeSeriesProperty<double> *const targLog) {
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
MantidVec countRate(m_numLogSteps);
auto nHist = InputWorkspace->getNumberHistograms();
// Initialize progress reporting.
API::Progress prog(this, 0.0, 1.0, nHist);
double dTRangeMin = static_cast<double>(m_TRangeMin.totalNanoseconds());
double dTRangeMax = static_cast<double>(m_TRangeMax.totalNanoseconds());
#pragma omp parallel
{
auto nThreads = omp_get_num_threads();
std::vector<MantidVec> Buff(nThreads);
// initialize thread's histogram buffer
#pragma omp critical
for (size_t i = 0; i < nThreads; i++) {
Buff[i].assign(m_numLogSteps, 0);
}
#pragma omp for
for (int i = 0; i < nHist; ++i) {
auto nThread = omp_get_thread_num();
PARALLEL_START_INTERUPT_REGION
// Get a const event list reference. eventInputWS->dataY() doesn't work.
const DataObjects::EventList &el = InputWorkspace->getSpectrum(i);
el.generateCountsHistogramPulseTime(dTRangeMin, dTRangeMax, Buff[nThread],
m_XRangeMin, m_XRangeMax);
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
// Report progress
prog.report(name());
PARALLEL_END_INTERUPT_REGION
}
PARALLEL_CHECK_INTERUPT_REGION
std::vector<double> countNormalization;
if (m_pNormalizationLog) {
#pragma omp critical
countNormalization = m_pNormalizationLog->valuesAsVector();
}
// calculate final sums
#pragma omp single
for (size_t j = 0; j < m_numLogSteps; j++) {
for (size_t i = 0; i < nThreads; j++) {
countRate[j] += Buff[i][j];
}
// normalize if requested
if (!countNormalization.empty()) {
countRate[j] /= countNormalization[j];
}
}
}
// recover histogram timing, but start assuming run starts at
std::vector<Kernel::DateAndTime> times(m_numLogSteps);
double dt = (dTRangeMax - dTRangeMin) / m_numLogSteps;
auto t0 = m_TRangeMin.totalNanoseconds();
for (size_t i = 0; i < m_numLogSteps; i++) {
times[i] = Kernel::DateAndTime(t0 + static_cast<int64_t>((0.5 + i) * dt));
}
// store calculated values within the target log.
targLog->replaceValues(times, countRate);
/*Analyse input log parameters and logs, attached to the workspace and identify
* the parameters of the target log, including experiment time.
*
@param InputWorkspace -- input workspace to analyse logs
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
void CalcCountRate::setOutLogParameters(
const DataObjects::EventWorkspace_sptr &InputWorkspace) {
std::string NormLogName = getProperty("NormalizationLogName");
bool normalizeResult = getProperty("NormalizeTheRate");
bool useLogDerivative = getProperty("UseLogDerivative");
bool useLogAccuracy = getProperty("UseNormLogGranularity");
bool logPresent = InputWorkspace->run().hasProperty(NormLogName);
if (!logPresent) {
if (normalizeResult) {
g_log.warning() << "Normalization by log " << NormLogName
<< " values requested but the log is not attached to the "
"workspace. Normalization disabled\n";
normalizeResult = false;
}
if (useLogDerivative) {
g_log.warning() << "Normalization by log " << NormLogName
<< " derivative requested but the log is not attached to "
"the workspace. Normalization disabled\n";
useLogDerivative = false;
}
if (useLogAccuracy) {
g_log.warning() << "Using accuracy of the log " << NormLogName
<< " is requested but the log is not attached to the "
"'NumTimeSteps' property value\n";
useLogAccuracy = false;
}
} else {
m_pNormalizationLog =
InputWorkspace->run().getTimeSeriesProperty<double>(NormLogName);
// Analyse properties interactions
// if property derivative is specified.
if (useLogDerivative) {
m_tmpLogHolder = m_pNormalizationLog->getDerivative();
m_pNormalizationLog = m_tmpLogHolder.get();
m_useLogDerivative = true;
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
if (normalizeResult) {
if (!useLogAccuracy) {
g_log.warning() << "Change of the counting log accuracy while "
"normalizing by log values is not implemented. Will "
"use log accuracy.";
useLogAccuracy = true;
}
} //---------------------------------------------------------------------
// find target log ranges and identify what normalization should be used
Kernel::DateAndTime runTMin, runTMax;
InputWorkspace->getPulseTimeMinMax(runTMin, runTMax);
//
if (useLogAccuracy) { // extract log times located inside the run time
Kernel::DateAndTime tLogMin, tLogMax;
if (m_useLogDerivative) { // derivative moves events to center, but we need
// initial range
auto pSource =
InputWorkspace->run().getTimeSeriesProperty<double>(NormLogName);
tLogMin = pSource->firstTime();
tLogMax = pSource->lastTime();
} else {
tLogMin = m_pNormalizationLog->firstTime();
tLogMax = m_pNormalizationLog->lastTime();
}
//
if (tLogMin < runTMin || tLogMax > runTMax) {
if (!m_tmpLogHolder) {
m_tmpLogHolder = std::make_unique<Kernel::TimeSeriesProperty<double>>(
*m_pNormalizationLog->clone());
}
m_tmpLogHolder->filterByTime(runTMin, runTMax);
m_pNormalizationLog = m_tmpLogHolder.get();
m_numLogSteps = m_pNormalizationLog->realSize();
} else {
if (tLogMin > runTMin || tLogMax < runTMax) {
g_log.warning() << "Normalization log " << m_pNormalizationLog->name()
<< " values do not cover the whole experiment time. "
"Log normalization impossible ";
m_pNormalizationLog = nullptr;
useLogAccuracy = false;
}
}
}
if (!useLogAccuracy) {
m_TRangeMin = runTMin;
m_TRangeMax = runTMax;
}
/* Retrieve and define data search ranges from input workspace parameters and
*@param InputWorkspace -- event workspace to process. Also retrieves algorithm
*@return -- the input workspace cropped according to XMin-XMax ranges in units,
void CalcCountRate::setSourceWSandXRanges(
DataObjects::EventWorkspace_sptr &InputWorkspace) {
m_XRangeMin = getProperty("XMin");
m_XRangeMax = getProperty("XMax");
if (m_XRangeMin == EMPTY_DBL() && m_XRangeMax == EMPTY_DBL()) {
m_rangeExplicit = true;
}
if (!m_rangeExplicit) {
InputWorkspace->getEventXMinMax(m_XRangeMin, m_XRangeMax);
std::string RangeUnits = getProperty("RangeUnits");
const auto unit = axis->unit();
API::MatrixWorkspace_sptr wst;
std::string wsName = InputWorkspace->name();
if (wsName.size() == 0) {
}
if (unit->unitID() != RangeUnits) {
auto conv = createChildAlgorithm("ConvertUnits", 0, 1);
conv->setProperty("InputWorkspace", InputWorkspace);
conv->setPropertyValue("OutputWorkspace", wsName);
std::string Emode = getProperty("Emode");
conv->setProperty("Emode", Emode);
conv->execute();
wst = conv->getProperty("OutputWorkspace");
} else {
wst = InputWorkspace;
InputWorkspace =
boost::dynamic_pointer_cast<DataObjects::EventWorkspace>(wst);
if (!InputWorkspace) {
throw std::runtime_error("SetWSDataRanges:Can not retrieve EventWorkspace "
"after converting units");
}
// here the ranges have been changed so both will newer remain defaults
InputWorkspace->getEventXMinMax(realMin, realMax);
if (m_XRangeMin == EMPTY_DBL()) {
}
if (m_XRangeMax == EMPTY_DBL()) {
m_XRangeMax = realMax;
}
if (m_XRangeMin < realMin) {
g_log.debug() << "Workspace constrain min range changed from: "
<< m_XRangeMin << " To: " << realMin << std::endl;
m_XRangeMin = realMin;
g_log.debug() << "Workspace constrain max range changed from: "
<< m_XRangeMax << " To: " << realMax << std::endl;
m_XRangeMax = realMax;
/** Helper function, mainly for testing
* @return true if count rate should be normalized and false
* otherwise */
bool CalcCountRate::notmalizeCountRate() const {
return (m_pNormalizationLog != nullptr);
}
/** Helper function, mainly for testing
* @return true if log derivative is used instead of log itself */
bool CalcCountRate::useLogDerivative() const { return m_useLogDerivative; }
} // namespace Algorithms
} // namespace Mantid