-
Notifications
You must be signed in to change notification settings - Fork 122
/
CreateChunkingFromInstrument.cpp
448 lines (391 loc) · 15 KB
/
CreateChunkingFromInstrument.cpp
1
2
3
4
5
6
7
8
9
10
11
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
59
60
61
62
63
64
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
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
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
191
192
193
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
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
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
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
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
#include "MantidKernel/OptionalBool.h"
#include "MantidAPI/FileProperty.h"
#include "MantidAPI/MatrixWorkspace.h"
#include "MantidAPI/ITableWorkspace.h"
#include "MantidAPI/Run.h"
#include "MantidAPI/TableRow.h"
#include "MantidAPI/WorkspaceFactory.h"
#include "MantidDataHandling/CreateChunkingFromInstrument.h"
#include "MantidDataObjects/Workspace2D.h"
#include "MantidGeometry/IDetector.h"
#include "MantidKernel/ListValidator.h"
#include "MantidKernel/StringTokenizer.h"
#include <nexus/NeXusFile.hpp>
#include <nexus/NeXusException.hpp>
namespace Mantid {
namespace DataHandling {
using namespace Mantid::API;
using namespace Mantid::DataObjects;
using namespace Mantid::Geometry;
using namespace Mantid::Kernel;
using namespace std;
typedef Mantid::Kernel::StringTokenizer tokenizer;
// Register the algorithm into the AlgorithmFactory
DECLARE_ALGORITHM(CreateChunkingFromInstrument)
namespace { // anonymous namespace to hide things
/// Input file name
const string PARAM_IN_FILE("Filename");
/// Input workspace parameter name
const string PARAM_IN_WKSP("InputWorkspace");
/// Instrument name parameter name
const string PARAM_INST_NAME("InstrumentName");
/// Instrument file parameter name
const string PARAM_INST_FILE("InstrumentFilename");
/// Explicitly name instrument components
const string PARAM_CHUNK_NAMES("ChunkNames");
/// Canned instrument components names
const string PARAM_CHUNK_BY("ChunkBy");
/// Recursion depth parameter name
const string PARAM_MAX_RECURSE("MaxRecursionDepth");
/// Output workspace parameter name
const string PARAM_OUT_WKSP("OutputWorkspace");
/// Maximum number of banks to look for
const string PARAM_MAX_BANK_NUM("MaxBankNumber");
}
/// Algorithm's name for identification. @see Algorithm::name
const string CreateChunkingFromInstrument::name() const {
return "CreateChunkingFromInstrument";
}
/// Algorithm's version for identification. @see Algorithm::version
int CreateChunkingFromInstrument::version() const { return 1; }
/// Algorithm's category for identification. @see Algorithm::category
const string CreateChunkingFromInstrument::category() const {
return "Workflow\\DataHandling";
}
/// Algorithm's summary for identification. @see Algorithm::summary
const string CreateChunkingFromInstrument::summary() const {
return "Creates chunking at a level of the instrument or instrument "
"components.";
}
/** Initialize the algorithm's properties.
*/
void CreateChunkingFromInstrument::init() {
// instrument selection
string grp1Name("Specify the Instrument");
std::vector<std::string> extensions{"_event.nxs", ".nxs.h5", ".nxs"};
this->declareProperty(
Kernel::make_unique<FileProperty>(PARAM_IN_FILE, "",
FileProperty::OptionalLoad, extensions),
"The name of the event nexus file to read, including its full or "
"relative path.");
this->declareProperty(
Kernel::make_unique<WorkspaceProperty<>>(
PARAM_IN_WKSP, "", Direction::Input, PropertyMode::Optional),
"Optional: An input workspace with the instrument we want to use.");
this->declareProperty(
Kernel::make_unique<PropertyWithValue<string>>(PARAM_INST_NAME, "",
Direction::Input),
"Optional: Name of the instrument to base the ChunkingWorkpace on which "
"to base the GroupingWorkspace.");
this->declareProperty(
Kernel::make_unique<FileProperty>(PARAM_INST_FILE, "",
FileProperty::OptionalLoad, ".xml"),
"Optional: Path to the instrument definition file on which to base the "
"ChunkingWorkpace.");
this->setPropertyGroup(PARAM_IN_FILE, grp1Name);
this->setPropertyGroup(PARAM_IN_WKSP, grp1Name);
this->setPropertyGroup(PARAM_INST_NAME, grp1Name);
this->setPropertyGroup(PARAM_INST_FILE, grp1Name);
// chunking
string grp2Name("Specify Instrument Components");
declareProperty(PARAM_CHUNK_NAMES, "",
"Optional: A string of the instrument component names to use "
"as separate groups. "
"Use / or , to separate multiple groups. "
"If empty, then an empty GroupingWorkspace will be created.");
vector<string> grouping{"", "All", "Group", "Column", "bank"};
declareProperty(
PARAM_CHUNK_BY, "", boost::make_shared<StringListValidator>(grouping),
"Only used if GroupNames is empty: All detectors as one group, Groups "
"(East,West for SNAP), Columns for SNAP, detector banks");
this->setPropertyGroup(PARAM_CHUNK_NAMES, grp2Name);
this->setPropertyGroup(PARAM_CHUNK_BY, grp2Name);
// everything else
declareProperty(PARAM_MAX_RECURSE, 5,
"Number of levels to search into the instrument (default=5)");
declareProperty(PARAM_MAX_BANK_NUM, 300,
"Maximum bank number to search for in the instrument");
declareProperty(Kernel::make_unique<WorkspaceProperty<API::ITableWorkspace>>(
PARAM_OUT_WKSP, "", Direction::Output),
"An output workspace describing the cunking.");
}
/// @copydoc Mantid::API::Algorithm::validateInputs
map<string, string> CreateChunkingFromInstrument::validateInputs() {
map<string, string> result;
// get the input paramters
string filename = getPropertyValue(PARAM_IN_FILE);
string inWSname = getPropertyValue(PARAM_IN_WKSP);
string instName = getPropertyValue(PARAM_INST_NAME);
string instFilename = getPropertyValue(PARAM_INST_FILE);
// count how many ways the input instrument was specified
int numInst = 0;
if (!filename.empty())
numInst++;
if (!inWSname.empty())
numInst++;
if (!instName.empty())
numInst++;
if (!instFilename.empty())
numInst++;
// set the error bits
string msg;
if (numInst == 0) {
msg = "Must specify instrument one way";
} else if (numInst > 1) {
msg = "Can only specify instrument one way";
}
if (!msg.empty()) {
result[PARAM_IN_FILE] = msg;
result[PARAM_IN_WKSP] = msg;
result[PARAM_INST_NAME] = msg;
result[PARAM_INST_FILE] = msg;
}
// get the chunking technology to use
string chunkNames = getPropertyValue(PARAM_CHUNK_NAMES);
string chunkGroups = getPropertyValue(PARAM_CHUNK_BY);
msg = "";
if (chunkNames.empty() && chunkGroups.empty()) {
msg = "Must specify either " + PARAM_CHUNK_NAMES + " or " + PARAM_CHUNK_BY;
} else if ((!chunkNames.empty()) && (!chunkGroups.empty())) {
msg = "Must specify either " + PARAM_CHUNK_NAMES + " or " + PARAM_CHUNK_BY +
" not both";
}
if (!msg.empty()) {
result[PARAM_CHUNK_NAMES] = msg;
result[PARAM_CHUNK_BY] = msg;
}
return result;
}
/**
* Returns true if str starts with prefix.
*
* @param str The string to check.
* @param prefix The prefix to look for.
* @return true if str starts with prefix.
*/
bool startsWith(const string &str, const string &prefix) {
// can't start with if it is shorter than the prefix
if (str.length() < prefix.length())
return false;
return (str.substr(0, prefix.length()).compare(prefix) == 0);
}
/**
* Find the name of the parent of the component that starts with the
* supplied prefix.
*
* @param comp The component to find the parent of.
* @param prefix Prefix of parent names to look for.
* @return The correct parent name. This is an empty string if the name
* isn't found.
*/
string parentName(IComponent_const_sptr comp, const string &prefix) {
// handle the special case of the component has the name
if (startsWith(comp->getName(), prefix))
return comp->getName();
// find the parent with the correct name
IComponent_const_sptr parent = comp->getParent();
if (parent) {
if (startsWith(parent->getName(), prefix))
return parent->getName();
else
return parentName(parent, prefix);
} else {
return "";
}
}
/**
* Find the name of the parent of the component that is in the list of
* parents that are being searched for.
*
* @param comp The component to find the parent of.
* @param names List of parent names to look for.
* @return The correct parent name. This is an empty string if the name
* isn't found.
*/
string parentName(IComponent_const_sptr comp, const vector<string> &names) {
// handle the special case of the component has the name
for (const auto &name : names)
if (name.compare(comp->getName()) == 0)
return name;
// find the parent with the correct name
IComponent_const_sptr parent = comp->getParent();
if (parent) {
// see if this is the parent
for (const auto &name : names)
if (name.compare(parent->getName()) == 0)
return name;
// or recurse
return parentName(parent, names);
} else {
return "";
}
}
/**
* Split a list of instrument components into a vector of strings.
*
* @param names Comma separated list of instrument components
* @return The vector of instrument component names.
*/
vector<string> getGroupNames(const string &names) {
// check that there is something
if (names.empty())
return std::vector<string>();
// do the actual splitting
tokenizer tokens(names, ",", Mantid::Kernel::StringTokenizer::TOK_TRIM);
return tokens.asVector();
}
/**
* Determine the instrument from the various input parameters.
*
* @return The correct instrument.
*/
Instrument_const_sptr CreateChunkingFromInstrument::getInstrument() {
// try the input workspace
MatrixWorkspace_sptr inWS = getProperty(PARAM_IN_WKSP);
if (inWS) {
return inWS->getInstrument();
}
// temporary workspace to hang everything else off of
MatrixWorkspace_sptr tempWS(new Workspace2D());
// name of the instrument
string instName = getPropertyValue(PARAM_INST_NAME);
// see if there is an input file
string filename = getPropertyValue(PARAM_IN_FILE);
if (!filename.empty()) {
string top_entry_name("entry"); // TODO make more flexible
// get the instrument name from the filename
size_t n = filename.rfind('/');
if (n != std::string::npos) {
std::string temp = filename.substr(n + 1, filename.size() - n - 1);
n = temp.find('_');
if (n != std::string::npos && n > 0) {
instName = temp.substr(0, n);
}
}
// read information from the nexus file itself
try {
NeXus::File nxsfile(filename);
// get the run start time
string start_time;
nxsfile.openGroup(top_entry_name, "NXentry");
nxsfile.readData("start_time", start_time);
tempWS->mutableRun().addProperty(
"run_start", DateAndTime(start_time).toISO8601String(), true);
// get the instrument name
nxsfile.openGroup("instrument", "NXinstrument");
nxsfile.readData("name", instName);
nxsfile.closeGroup();
// Test if IDF exists in file, move on quickly if not
nxsfile.openPath("instrument/instrument_xml");
nxsfile.close();
IAlgorithm_sptr loadInst =
createChildAlgorithm("LoadIDFFromNexus", 0.0, 0.2);
// Now execute the Child Algorithm. Catch and log any error, but don't
// stop.
try {
loadInst->setPropertyValue("Filename", filename);
loadInst->setProperty<MatrixWorkspace_sptr>("Workspace", tempWS);
loadInst->setPropertyValue("InstrumentParentPath", top_entry_name);
loadInst->execute();
} catch (std::invalid_argument &) {
g_log.error("Invalid argument to LoadIDFFromNexus Child Algorithm ");
} catch (std::runtime_error &) {
g_log.debug("No instrument definition found in " + filename + " at " +
top_entry_name + "/instrument");
}
if (loadInst->isExecuted())
return tempWS->getInstrument();
else
g_log.information("No IDF loaded from Nexus file.");
} catch (::NeXus::Exception &) {
g_log.information("No instrument definition found in " + filename +
" at " + top_entry_name + "/instrument");
}
}
// run LoadInstrument if other methods have not run
string instFilename = getPropertyValue(PARAM_INST_FILE);
Algorithm_sptr childAlg = createChildAlgorithm("LoadInstrument", 0.0, 0.2);
childAlg->setProperty<MatrixWorkspace_sptr>("Workspace", tempWS);
childAlg->setPropertyValue("Filename", instFilename);
childAlg->setPropertyValue("InstrumentName", instName);
childAlg->setProperty("RewriteSpectraMap",
Mantid::Kernel::OptionalBool(true));
childAlg->executeAsChildAlg();
return tempWS->getInstrument();
}
//----------------------------------------------------------------------------------------------
/** Execute the algorithm.
*/
void CreateChunkingFromInstrument::exec() {
// get the instrument
Instrument_const_sptr inst = this->getInstrument();
// setup the output workspace
ITableWorkspace_sptr strategy =
WorkspaceFactory::Instance().createTable("TableWorkspace");
strategy->addColumn("str", "BankName");
this->setProperty("OutputWorkspace", strategy);
// get the correct level of grouping
string groupLevel = this->getPropertyValue(PARAM_CHUNK_BY);
vector<string> groupNames =
getGroupNames(this->getPropertyValue(PARAM_CHUNK_NAMES));
if (groupLevel.compare("All") == 0) {
return; // nothing to do
} else if (inst->getName().compare("SNAP") == 0 &&
groupLevel.compare("Group") == 0) {
groupNames.clear();
groupNames.emplace_back("East");
groupNames.emplace_back("West");
}
// set up a progress bar with the "correct" number of steps
int maxBankNum = this->getProperty(PARAM_MAX_BANK_NUM);
Progress progress(this, .2, 1., maxBankNum);
// search the instrument for the bank names
int maxRecurseDepth = this->getProperty(PARAM_MAX_RECURSE);
map<string, vector<string>> grouping;
// cppcheck-suppress syntaxError
PRAGMA_OMP(parallel for schedule(dynamic, 1) )
for (int num = 0; num < maxBankNum; ++num) {
PARALLEL_START_INTERUPT_REGION
ostringstream mess;
mess << "bank" << num;
IComponent_const_sptr comp =
inst->getComponentByName(mess.str(), maxRecurseDepth);
PARALLEL_CRITICAL(grouping)
if (comp) {
// get the name of the correct parent
string parent;
if (groupNames.empty()) {
parent = parentName(comp, groupLevel);
} else {
parent = parentName(comp, groupNames);
}
// add it to the correct chunk
if (!parent.empty()) {
if (grouping.count(parent) == 0)
grouping[parent] = vector<string>();
grouping[parent].push_back(comp->getName());
}
}
progress.report();
PARALLEL_END_INTERUPT_REGION
}
PARALLEL_CHECK_INTERUPT_REGION
// check to see that something happened
if (grouping.empty())
throw std::runtime_error("Failed to find any banks in the instrument");
// fill in the table workspace
for (auto &group : grouping) {
stringstream banks;
for (const auto &bank : group.second) {
banks << bank << ",";
}
// remove the trailing comma
string banksStr = banks.str();
banksStr = banksStr.substr(0, banksStr.size() - 1);
// add it to the table
TableRow row = strategy->appendRow();
row << banksStr;
}
}
} // namespace DataHandling
} // namespace Mantid