Newer
Older
#include "MantidMuon/MuonAlgorithmHelper.h"
#include "MantidAPI/AlgorithmManager.h"
#include "MantidAPI/MatrixWorkspace.h"
#include "MantidAPI/WorkspaceGroup.h"
#include "MantidKernel/InstrumentInfo.h"
#include "MantidKernel/StringTokenizer.h"
#include "MantidKernel/Strings.h"
#include <fstream>
#include <string>
#include <vector>
namespace Mantid {
namespace MuonAlgorithmHelper {
using namespace Mantid::Kernel;
using namespace Mantid::API;
/**
* Return a first period MatrixWorkspace in a run workspace. If the run
* workspace has one period only - it is returned.
* @param ws :: Run workspace
*/
MatrixWorkspace_sptr firstPeriod(Workspace_sptr ws) {
if (auto group = boost::dynamic_pointer_cast<WorkspaceGroup>(ws)) {
return boost::dynamic_pointer_cast<MatrixWorkspace>(group->getItem(0));
} else {
return boost::dynamic_pointer_cast<MatrixWorkspace>(ws);
}
}
* Get a run label for a single workspace.
* @param ws :: [input] workspace pointer
* @return :: run label
*/
std::string getRunLabel(Mantid::API::Workspace_sptr ws) {
const std::vector<Mantid::API::Workspace_sptr> wsList{ws};
return getRunLabel(wsList);
}
/**
* Get a run label for a list of workspaces.
* E.g. for MUSR data of runs 15189, 15190, 15191 it will look like
* MUSR00015189-91.
* (Assumes all runs have the same instrument)
* @param wsList :: [input] Vector of workspace pointers
* @return :: run label
* @throws std::invalid_argument if empty list given
*/
std::string getRunLabel(const std::vector<Workspace_sptr> &wsList) {
if (wsList.empty())
throw std::invalid_argument("Unable to run on an empty list");
const std::string instrument =
firstPeriod(wsList.front())->getInstrument()->getName();
// Extract the run numbers
std::vector<int> runNumbers;
runNumbers.reserve(wsList.size());
for (auto &&workspace : wsList) {
int runNumber = firstPeriod(workspace)->getRunNumber();
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
runNumbers.push_back(runNumber);
}
return getRunLabel(instrument, runNumbers);
}
/**
* Get a run label for a given instrument and list of runs.
* E.g. for MUSR data of runs 15189, 15190, 15191 it will look like
* MUSR00015189-91.
* (Assumes all runs have the same instrument)
* @param instrument :: [input] instrument name
* @param runNumbers :: [input] List of run numbers
* @return :: run label
* @throws std::invalid_argument if empty run list given
*/
std::string getRunLabel(const std::string &instrument,
const std::vector<int> &runNumbers) {
if (runNumbers.empty()) {
throw std::invalid_argument("Cannot run on an empty list");
}
// Find ranges of consecutive runs
auto ranges = findConsecutiveRuns(runNumbers);
// Zero-padding for the first run
int zeroPadding;
try {
zeroPadding = ConfigService::Instance()
.getInstrument(instrument)
.zeroPadding(ranges.begin()->first);
} catch (const Mantid::Kernel::Exception::NotFoundError &) {
// Old muon instrument without an IDF - default to 3 zeros
zeroPadding = 3;
}
std::ostringstream label;
label << instrument;
for (auto range : ranges) {
label << createStringFromRange(range, zeroPadding);
// Only pad the first set
zeroPadding = 0;
if (range != ranges.back()) {
label << ", ";
}
}
return label.str();
}
/**
* Create a string from a range
* @param range :: [input] a pair of integers representing the ends of the range
* (may be the same)
* @param zeroPadding :: [input] pad the lower element of range with zeros up to
* length zeroPadding.
* @return :: range in the form "1234-45" removing common digits from the upper
* end of the range.
*/
std::string createStringFromRange(const std::pair<int, int> &range,
const int &zeroPadding) {
std::string firstRun;
std::string lastRun;
if (range.second > range.first) {
firstRun = std::to_string(range.first);
lastRun = std::to_string(range.second);
} else {
firstRun = std::to_string(range.second);
lastRun = std::to_string(range.first);
}
// Begin string output with full label of first run
std::ostringstream paddedLabel;
paddedLabel << std::setw(zeroPadding) << std::setfill('0') << std::right;
paddedLabel << firstRun;
if (range.second != range.first) {
// Remove the common part of the first and last run, so we get e.g.
// "12345-56" instead of "12345-12356"
for (size_t i = 0; i < firstRun.size() && i < lastRun.size(); ++i) {
if (firstRun[i] != lastRun[i]) {
lastRun.erase(0, i);
break;
}
}
paddedLabel << "-" << lastRun;
return paddedLabel.str();
/**
* Given a vector of run numbers, returns the consecutive ranges of runs.
* e.g. 1,2,3,5,6,8 -> (1,3), (5,6), (8,8)
* @param runs :: [input] Vector of run numbers - need not be sorted
* @returns Vector of pairs of (start, end) of consecutive runs
*/
std::vector<std::pair<int, int>>
findConsecutiveRuns(const std::vector<int> &runs) {
// Groups to output
std::vector<std::pair<int, int>> ranges;
// Sort the vector to begin with
std::vector<int> runNumbers(runs); // local copy
std::sort(runNumbers.begin(), runNumbers.end());
// Iterate through vector looking for consecutive groups
auto a = runNumbers.begin();
auto start = a;
auto b = a + 1;
while (b != runNumbers.end()) {
if (*b - 1 == *a) { // Still consecutive
a++;
b++;
} else { // Reached end of consecutive group
ranges.emplace_back(*start, *a);
start = b++;
a = start;
}
}
// Reached end of last consecutive group
return ranges;
}
/**
* Makes sure the specified workspaces are in specified group. If group exists
* already - missing workspaces are added to it, otherwise new group is created.
* If ws exists in ADS under groupName, and it is not a group - it's
* overwritten.
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
220
221
222
223
224
225
226
227
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
260
261
262
263
264
265
266
267
268
269
270
271
272
* @param groupName :: Name of the group workspaces should be in
* @param inputWorkspaces :: Names of the workspaces to group
*/
void groupWorkspaces(const std::string &groupName,
const std::vector<std::string> &inputWorkspaces) {
auto &ads = AnalysisDataService::Instance();
WorkspaceGroup_sptr group;
if (ads.doesExist(groupName)) {
group = ads.retrieveWS<WorkspaceGroup>(groupName);
}
if (group) {
// Exists and is a group -> add missing workspaces to it
for (auto it = inputWorkspaces.begin(); it != inputWorkspaces.end(); ++it) {
if (!group->contains(*it)) {
group->add(*it);
}
}
} else {
// Doesn't exist or isn't a group -> create/overwrite
IAlgorithm_sptr groupingAlg =
AlgorithmManager::Instance().create("GroupWorkspaces");
groupingAlg->setProperty("InputWorkspaces", inputWorkspaces);
groupingAlg->setPropertyValue("OutputWorkspace", groupName);
groupingAlg->execute();
}
}
/**
* Generate a workspace name from the given parameters
* Format: "INST00012345; Pair; long; Asym;[ 1;] #1"
* @param params :: [input] Struct containing dataset parameters
* @returns :: Name for analysis workspace
*/
std::string generateWorkspaceName(const Muon::DatasetParams ¶ms) {
std::ostringstream workspaceName;
const static std::string sep("; ");
// Instrument and run number
if (params.label.empty()) {
workspaceName << getRunLabel(params.instrument, params.runs) << sep;
} else {
workspaceName << params.label << sep;
}
// Pair/group and name of pair/group
if (params.itemType == Muon::ItemType::Pair) {
workspaceName << "Pair" << sep;
} else if (params.itemType == Muon::ItemType::Group) {
workspaceName << "Group" << sep;
}
workspaceName << params.itemName << sep;
// Type of plot
switch (params.plotType) {
case Muon::PlotType::Asymmetry:
workspaceName << "Asym";
break;
case Muon::PlotType::Counts:
workspaceName << "Counts";
break;
case Muon::PlotType::Logarithm:
workspaceName << "Logs";
break;
}
// Period(s)
const auto periods = params.periods;
if (!periods.empty()) {
workspaceName << sep << periods;
}
// Version - always "#1" if overwrite is on, otherwise increment
workspaceName << sep << "#" << params.version;
return workspaceName.str();
}
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
/**
* Find all the detector IDs contained inside a workspace (either matrix or
* group) and return as an ordered set.
*/
std::set<Mantid::detid_t>
getAllDetectorIDsFromWorkspace(Mantid::API::Workspace_sptr ws) {
std::set<Mantid::detid_t> detectorIDs;
if (auto workspace = boost::dynamic_pointer_cast<MatrixWorkspace>(ws)) {
detectorIDs = getAllDetectorIDsFromMatrixWorkspace(workspace);
} else if (auto workspace = boost::dynamic_pointer_cast<WorkspaceGroup>(ws)) {
detectorIDs = getAllDetectorIDsFromGroupWorkspace(workspace);
}
return detectorIDs;
}
/**
* Find all the detector IDs contained inside a matrix workspace
*/
std::set<Mantid::detid_t>
getAllDetectorIDsFromMatrixWorkspace(Mantid::API::MatrixWorkspace_sptr ws) {
std::set<Mantid::detid_t> detectorIDs;
std::set<Mantid::detid_t> spectrumIDs;
auto numSpectra = ws->getNumberHistograms();
for (size_t i = 0; i < numSpectra; i++) {
spectrumIDs = ws->getSpectrum(i).getDetectorIDs();
detectorIDs.insert(spectrumIDs.begin(), spectrumIDs.end());
}
return detectorIDs;
}
/**
* Find all the detector IDs contained inside a group workspace
*/
std::set<Mantid::detid_t>
getAllDetectorIDsFromGroupWorkspace(Mantid::API::WorkspaceGroup_sptr ws) {
std::set<Mantid::detid_t> detectorIDs;
std::set<Mantid::detid_t> detectorIDsSingleWorkspace;
MatrixWorkspace_sptr matrixWS;
std::vector<Workspace_sptr> workspaces = ws->getAllItems();
for (size_t i = 0; i < workspaces.size(); i++) {
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
matrixWS = boost::dynamic_pointer_cast<MatrixWorkspace>(workspaces[i]);
detectorIDsSingleWorkspace = getAllDetectorIDsFromMatrixWorkspace(matrixWS);
detectorIDs.insert(detectorIDsSingleWorkspace.begin(),
detectorIDsSingleWorkspace.end());
}
return detectorIDs;
}
/**
* Find all the detector IDs contained inside a grouping object and return as a
* vector of ints
*/
std::vector<int> getAllDetectorIDsFromGroup(const Grouping &grouping) {
std::vector<int> groupDetectors;
for (auto group : grouping.groups) {
std::vector<int> groupDetectorIDs =
Mantid::Kernel::Strings::parseRange(group);
groupDetectors.insert(groupDetectors.end(), groupDetectorIDs.begin(),
groupDetectorIDs.end());
}
return groupDetectors;
}
// Checks if all the detectors in the groups in a Grouping are in the workspace.
// Workspace can be matrix or group type.
bool checkGroupDetectorsInWorkspace(const Grouping &grouping,
Workspace_sptr ws) {
std::set<int> detectorIDs = getAllDetectorIDsFromWorkspace(ws);
std::vector<int> groupDetectorIDs = getAllDetectorIDsFromGroup(grouping);
return checkItemsInSet(groupDetectorIDs, detectorIDs);
}
// Checks that all of the entries of a vector are contained in a set, returns
// true/false
bool checkItemsInSet(const std::vector<int> &items, const std::set<int> &set) {
for (const auto item : items) {
if (set.find(item) == set.end()) {
return false;
}
}
return true;
* Parse a workspace name into dataset parameters
* Format: "INST00012345; Pair; long; Asym;[ 1;] #1"
* @param wsName :: [input] Name of workspace
* @returns :: Struct containing dataset parameters
*/
Muon::DatasetParams parseWorkspaceName(const std::string &wsName) {
Muon::DatasetParams params;
Mantid::Kernel::StringTokenizer tokenizer(
wsName, ";", Mantid::Kernel::StringTokenizer::TOK_TRIM);
const size_t numTokens = tokenizer.count();
// Name contains min of 5 ";" separated values and max 6.
if (numTokens < 5 || numTokens > 6) {
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
throw std::invalid_argument("Could not parse workspace name: " + wsName);
}
params.label = tokenizer[0];
parseRunLabel(params.label, params.instrument, params.runs);
const std::string itemType = tokenizer[1];
params.itemType =
(itemType == "Group") ? Muon::ItemType::Group : Muon::ItemType::Pair;
params.itemName = tokenizer[2];
const std::string plotType = tokenizer[3];
if (plotType == "Asym") {
params.plotType = Muon::PlotType::Asymmetry;
} else if (plotType == "Counts") {
params.plotType = Muon::PlotType::Counts;
} else {
params.plotType = Muon::PlotType::Logarithm;
}
std::string versionString;
if (numTokens > 5) { // periods included
params.periods = tokenizer[4];
versionString = tokenizer[5];
} else {
versionString = tokenizer[4];
}
// Remove the # from the version string
versionString.erase(
std::remove(versionString.begin(), versionString.end(), '#'),
versionString.end());
try {
params.version = boost::lexical_cast<size_t>(versionString);
} catch (const boost::bad_lexical_cast &) {
params.version = 1; // Set to 1 and ignore the error
}
return params;
}
/**
* Parse a run label e.g. "MUSR00015189-91, 15193" into instrument
* ("MUSR") and set of runs (15189, 15190, 15191, 15193).
* Assumes instrument name doesn't contain a digit (true for muon instruments).
* @param label :: [input] Label to parse
* @param instrument :: [output] Name of instrument
* @param runNumbers :: [output] Vector to fill with run numbers
* @throws std::invalid_argument if input cannot be parsed
*/
void parseRunLabel(const std::string &label, std::string &instrument,
std::vector<int> &runNumbers) {
const size_t instPos = label.find_first_of("0123456789");
instrument = label.substr(0, instPos);
const size_t numPos = label.find_first_not_of('0', instPos);
if (numPos != std::string::npos) {
std::string runString = label.substr(numPos, label.size());
// sets of continuous ranges
Mantid::Kernel::StringTokenizer rangeTokenizer(
runString, ",", Mantid::Kernel::StringTokenizer::TOK_TRIM);
for (const auto &range : rangeTokenizer.asVector()) {
Mantid::Kernel::StringTokenizer pairTokenizer(
range, "-", Mantid::Kernel::StringTokenizer::TOK_TRIM);
try {
if (pairTokenizer.count() == 2) {
// Range of run numbers
// Deal with common part of string: "151" in "15189-91"
const size_t diff =
pairTokenizer[0].length() - pairTokenizer[1].length();
const std::string endRun =
pairTokenizer[0].substr(0, diff) + pairTokenizer[1];
const int start = boost::lexical_cast<int>(pairTokenizer[0]);
const int end = boost::lexical_cast<int>(endRun);
for (int run = start; run < end + 1; run++) {
runNumbers.push_back(run);
}
} else if (pairTokenizer.count() == 1) {
// Single run
runNumbers.push_back(boost::lexical_cast<int>(pairTokenizer[0]));
} else {
throw std::invalid_argument("Failed to parse run label: " + label +
" too many tokens ");
}
} catch (const boost::bad_lexical_cast &) {
throw std::invalid_argument("Failed to parse run label: " + label +
" not a good run number");
} catch (...) {
throw std::invalid_argument("Failed to parse run label: " + label);
}
}
} else {
// The string was "INST000" or similar...
runNumbers = {0};
}
}
// Validate the input group workspaces using their names.
bool checkValidPair(const std::string &WSname1, const std::string &WSname2) {
Muon::DatasetParams group1, group2;
try {
group1 = parseWorkspaceName(WSname1);
group2 = parseWorkspaceName(WSname2);
} catch (std::invalid_argument) {
throw std::invalid_argument(
"Ensure workspaces have the correctly formatted name (see "
"documentation).");
}
if (group1.instrument != group2.instrument) {
throw std::invalid_argument(
"Group workspaces named with different instruments.");
}
throw std::invalid_argument(
"Groups used for pairing must have differnt names.");
if (group1.itemType != Muon::ItemType::Group ||
group2.itemType != Muon::ItemType::Group) {
throw std::invalid_argument("Workspaces must be of group type (not pair)");
}
if (group1.plotType != Muon::PlotType::Counts ||
group2.plotType != Muon::PlotType::Counts) {
}
return true;
}
/// Check whether a group or pair name is valid
bool checkValidGroupPairName(const std::string &name) {
if (name.empty()) {
return false;
}
if (!std::all_of(std::begin(name), std::end(name), isalnum)) {
return false;
}
if (name == "Group" || name == "Pair") {
bool is_alphanumerical_or_underscore(char character) {
return (isalpha(character) || isdigit(character) || (character == '_'));
}
} // namespace MuonAlgorithmHelper
} // namespace Mantid