Mantid
Loading...
Searching...
No Matches
CreateChunkingFromInstrument.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 +
11#include "MantidAPI/Run.h"
12#include "MantidAPI/TableRow.h"
20#include "MantidNexus/NexusFile.h"
21
22#include <algorithm>
23
24namespace Mantid::DataHandling {
25using namespace Mantid::API;
26using namespace Mantid::DataObjects;
27using namespace Mantid::Geometry;
28using namespace Mantid::Kernel;
29using Types::Core::DateAndTime;
30using namespace std;
31
33
34// Register the algorithm into the AlgorithmFactory
36
37namespace { // anonymous namespace to hide things
39const string PARAM_IN_FILE("Filename");
41const string PARAM_IN_WKSP("InputWorkspace");
43const string PARAM_INST_NAME("InstrumentName");
45const string PARAM_INST_FILE("InstrumentFilename");
47const string PARAM_CHUNK_NAMES("ChunkNames");
49const string PARAM_CHUNK_BY("ChunkBy");
51const string PARAM_MAX_RECURSE("MaxRecursionDepth");
53const string PARAM_OUT_WKSP("OutputWorkspace");
55const string PARAM_MAX_BANK_NUM("MaxBankNumber");
56} // namespace
57
59const string CreateChunkingFromInstrument::name() const { return "CreateChunkingFromInstrument"; }
60
63
65const string CreateChunkingFromInstrument::category() const { return "Workflow\\DataHandling"; }
66
69 return "Creates chunking at a level of the instrument or instrument "
70 "components.";
71}
72
76 // instrument selection
77 string grp1Name("Specify the Instrument");
78
79 std::vector<std::string> extensions{"_event.nxs", ".nxs.h5", ".nxs"};
80 this->declareProperty(std::make_unique<FileProperty>(PARAM_IN_FILE, "", FileProperty::OptionalLoad, extensions),
81 "The name of the event nexus file to read, including its full or "
82 "relative path.");
83
84 this->declareProperty(
85 std::make_unique<WorkspaceProperty<>>(PARAM_IN_WKSP, "", Direction::Input, PropertyMode::Optional),
86 "Optional: An input workspace with the instrument we want to use.");
87
88 this->declareProperty(std::make_unique<PropertyWithValue<string>>(PARAM_INST_NAME, "", Direction::Input),
89 "Optional: Name of the instrument to base the ChunkingWorkpace on which "
90 "to base the GroupingWorkspace.");
91
92 this->declareProperty(std::make_unique<FileProperty>(PARAM_INST_FILE, "", FileProperty::OptionalLoad, ".xml"),
93 "Optional: Path to the instrument definition file on which to base the "
94 "ChunkingWorkpace.");
95
96 this->setPropertyGroup(PARAM_IN_FILE, grp1Name);
97 this->setPropertyGroup(PARAM_IN_WKSP, grp1Name);
98 this->setPropertyGroup(PARAM_INST_NAME, grp1Name);
99 this->setPropertyGroup(PARAM_INST_FILE, grp1Name);
100
101 // chunking
102 string grp2Name("Specify Instrument Components");
103
104 declareProperty(PARAM_CHUNK_NAMES, "",
105 "Optional: A string of the instrument component names to use "
106 "as separate groups. "
107 "Use / or , to separate multiple groups. "
108 "If empty, then an empty GroupingWorkspace will be created.");
109 vector<string> grouping{"", "All", "Group", "Column", "bank"};
110 declareProperty(PARAM_CHUNK_BY, "", std::make_shared<StringListValidator>(grouping),
111 "Only used if GroupNames is empty: All detectors as one group, Groups "
112 "(East,West for SNAP), Columns for SNAP, detector banks");
113
114 this->setPropertyGroup(PARAM_CHUNK_NAMES, grp2Name);
115 this->setPropertyGroup(PARAM_CHUNK_BY, grp2Name);
116
117 // everything else
118 declareProperty(PARAM_MAX_RECURSE, 5, "Number of levels to search into the instrument (default=5)");
119 declareProperty(PARAM_MAX_BANK_NUM, 300, "Maximum bank number to search for in the instrument");
120
122 "An output workspace describing the cunking.");
123}
124
127 map<string, string> result;
128
129 // get the input paramters
130 string filename = getPropertyValue(PARAM_IN_FILE);
131 string inWSname = getPropertyValue(PARAM_IN_WKSP);
132 string instName = getPropertyValue(PARAM_INST_NAME);
133 string instFilename = getPropertyValue(PARAM_INST_FILE);
134
135 // count how many ways the input instrument was specified
136 int numInst = 0;
137 if (!filename.empty())
138 numInst++;
139 if (!inWSname.empty())
140 numInst++;
141 if (!instName.empty())
142 numInst++;
143 if (!instFilename.empty())
144 numInst++;
145
146 // set the error bits
147 string msg;
148 if (numInst == 0) {
149 msg = "Must specify instrument one way";
150 } else if (numInst > 1) {
151 msg = "Can only specify instrument one way";
152 }
153 if (!msg.empty()) {
154 result[PARAM_IN_FILE] = msg;
155 result[PARAM_IN_WKSP] = msg;
156 result[PARAM_INST_NAME] = msg;
157 result[PARAM_INST_FILE] = msg;
158 }
159
160 // get the chunking technology to use
161 string chunkNames = getPropertyValue(PARAM_CHUNK_NAMES);
162 string chunkGroups = getPropertyValue(PARAM_CHUNK_BY);
163 msg = "";
164 if (chunkNames.empty() && chunkGroups.empty()) {
165 msg = "Must specify either " + PARAM_CHUNK_NAMES + " or " + PARAM_CHUNK_BY;
166 } else if ((!chunkNames.empty()) && (!chunkGroups.empty())) {
167 msg = "Must specify either " + PARAM_CHUNK_NAMES + " or " + PARAM_CHUNK_BY + " not both";
168 }
169 if (!msg.empty()) {
170 result[PARAM_CHUNK_NAMES] = msg;
171 result[PARAM_CHUNK_BY] = msg;
172 }
173
174 return result;
175}
176
184bool startsWith(const string &str, const string &prefix) {
185 // can't start with if it is shorter than the prefix
186 if (str.length() < prefix.length())
187 return false;
188
189 return (str.substr(0, prefix.length()) == prefix);
190}
191
201string parentName(const IComponent_const_sptr &comp, const string &prefix) {
202 // handle the special case of the component has the name
203 if (startsWith(comp->getName(), prefix))
204 return comp->getName();
205
206 // find the parent with the correct name
207 IComponent_const_sptr parent = comp->getParent();
208 if (parent) {
209 if (startsWith(parent->getName(), prefix))
210 return parent->getName();
211 else
212 return parentName(parent, prefix);
213 } else {
214 return "";
215 }
216}
217
227string parentName(const IComponent_const_sptr &comp, const vector<string> &names) {
228 // handle the special case of the component has the name
229 const auto compName = comp->getName();
230 auto it = std::find(names.cbegin(), names.cend(), compName);
231 if (it != names.cend())
232 return *it;
233
234 // find the parent with the correct name
235 IComponent_const_sptr parent = comp->getParent();
236 if (parent) {
237 const auto parName = parent->getName();
238 // see if this is the parent
239 it = std::find(names.cbegin(), names.cend(), parName);
240 if (it != names.cend())
241 return *it;
242 // or recurse
243 return parentName(parent, names);
244 } else {
245 return "";
246 }
247}
248
255vector<string> getGroupNames(const string &names) {
256 // check that there is something
257 if (names.empty())
258 return std::vector<string>();
259 // do the actual splitting
261 return tokens.asVector();
262}
263
270 // try the input workspace
271 MatrixWorkspace_sptr inWS = getProperty(PARAM_IN_WKSP);
272 if (inWS) {
273 return inWS->getInstrument();
274 }
275
276 // temporary workspace to hang everything else off of
277 MatrixWorkspace_sptr tempWS(new Workspace2D());
278 // name of the instrument
279 string instName = getPropertyValue(PARAM_INST_NAME);
280
281 // see if there is an input file
282 string filename = getPropertyValue(PARAM_IN_FILE);
283 if (!filename.empty()) {
284 string top_entry_name("entry"); // TODO make more flexible
285
286 // get the instrument name from the filename
287 size_t n = filename.rfind('/');
288 if (n != std::string::npos) {
289 std::string temp = filename.substr(n + 1, filename.size() - n - 1);
290 n = temp.find('_');
291 if (n != std::string::npos && n > 0) {
292 instName = temp.substr(0, n);
293 }
294 }
295
296 // read information from the nexus file itself
297 try {
298 Nexus::File nxsfile(filename);
299
300 // get the run start time
301 string start_time;
302 nxsfile.openGroup(top_entry_name, "NXentry");
303 nxsfile.readData("start_time", start_time);
304 tempWS->mutableRun().addProperty("run_start", DateAndTime(start_time).toISO8601String(), true);
305
306 // get the instrument name
307 nxsfile.openGroup("instrument", "NXinstrument");
308 nxsfile.readData("name", instName);
309 nxsfile.closeGroup();
310
311 // Test if IDF exists in file, move on quickly if not
312 nxsfile.openAddress("instrument/instrument_xml");
313 nxsfile.close();
314 auto loadInst = createChildAlgorithm("LoadIDFFromNexus", 0.0, 0.2);
315 // Now execute the Child Algorithm. Catch and log any error, but don't
316 // stop.
317 try {
318 loadInst->setPropertyValue("Filename", filename);
319 loadInst->setProperty<MatrixWorkspace_sptr>("Workspace", tempWS);
320 loadInst->setPropertyValue("InstrumentParentPath", top_entry_name);
321 loadInst->execute();
322 } catch (std::invalid_argument &) {
323 g_log.error("Invalid argument to LoadIDFFromNexus Child Algorithm ");
324 } catch (std::runtime_error &) {
325 g_log.debug("No instrument definition found in " + filename + " at " + top_entry_name + "/instrument");
326 }
327
328 if (loadInst->isExecuted())
329 return tempWS->getInstrument();
330 else
331 g_log.information("No IDF loaded from Nexus file.");
332
333 } catch (Nexus::Exception const &) {
334 g_log.information("No instrument definition found in " + filename + " at " + top_entry_name + "/instrument");
335 }
336 }
337
338 // run LoadInstrument if other methods have not run
339 string instFilename = getPropertyValue(PARAM_INST_FILE);
340
341 Algorithm_sptr childAlg = createChildAlgorithm("LoadInstrument", 0.0, 0.2);
342 childAlg->setProperty<MatrixWorkspace_sptr>("Workspace", tempWS);
343 childAlg->setPropertyValue("Filename", instFilename);
344 childAlg->setPropertyValue("InstrumentName", instName);
345 childAlg->setProperty("RewriteSpectraMap", Mantid::Kernel::OptionalBool(true));
346 childAlg->executeAsChildAlg();
347 return tempWS->getInstrument();
348}
349
350//----------------------------------------------------------------------------------------------
354 // get the instrument
356
357 // setup the output workspace
358 ITableWorkspace_sptr strategy = WorkspaceFactory::Instance().createTable("TableWorkspace");
359 strategy->addColumn("str", "BankName");
360 this->setProperty("OutputWorkspace", strategy);
361
362 // get the correct level of grouping
363 string groupLevel = this->getPropertyValue(PARAM_CHUNK_BY);
364 vector<string> groupNames = getGroupNames(this->getPropertyValue(PARAM_CHUNK_NAMES));
365 if (groupLevel == "All") {
366 return; // nothing to do
367 } else if (inst->getName() == "SNAP" && groupLevel == "Group") {
368 groupNames.clear();
369 groupNames.emplace_back("East");
370 groupNames.emplace_back("West");
371 }
372
373 // set up a progress bar with the "correct" number of steps
374 int maxBankNum = this->getProperty(PARAM_MAX_BANK_NUM);
375 Progress progress(this, .2, 1., maxBankNum);
376
377 // search the instrument for the bank names
378 int maxRecurseDepth = this->getProperty(PARAM_MAX_RECURSE);
379 map<string, vector<string>> grouping;
380
381 PRAGMA_OMP(parallel for schedule(dynamic, 1) )
382 for (int num = 0; num < maxBankNum; ++num) {
384 ostringstream mess;
385 mess << "bank" << num;
386 IComponent_const_sptr comp = inst->getComponentByName(mess.str(), maxRecurseDepth);
387 PARALLEL_CRITICAL(grouping)
388 if (comp) {
389 // get the name of the correct parent
390 string parent;
391 if (groupNames.empty()) {
392 parent = parentName(comp, groupLevel);
393 } else {
394 parent = parentName(comp, groupNames);
395 }
396
397 // add it to the correct chunk
398 if (!parent.empty()) {
399 grouping.try_emplace(parent, vector<string>());
400 grouping[parent].emplace_back(comp->getName());
401 }
402 }
403 progress.report();
405 }
407
408 // check to see that something happened
409 if (grouping.empty())
410 throw std::runtime_error("Failed to find any banks in the instrument");
411
412 // fill in the table workspace
413 for (const auto &group : grouping) {
414 stringstream banks;
415 for (const auto &bank : group.second) {
416 banks << bank << ",";
417 }
418 // remove the trailing comma
419 string banksStr = banks.str();
420 banksStr.pop_back();
421
422 // add it to the table
423 TableRow row = strategy->appendRow();
424 row << banksStr;
425 }
426}
427
428} // namespace Mantid::DataHandling
#define DECLARE_ALGORITHM(classname)
Definition Algorithm.h:538
#define PARALLEL_START_INTERRUPT_REGION
Begins a block to skip processing is the algorithm has been interupted Note the end of the block if n...
#define PARALLEL_CRITICAL(name)
#define PARALLEL_END_INTERRUPT_REGION
Ends a block to skip processing is the algorithm has been interupted Note the start of the block if n...
#define PRAGMA_OMP(expression)
#define PARALLEL_CHECK_INTERRUPT_REGION
Adds a check after a Parallel region to see if it was interupted.
void declareProperty(std::unique_ptr< Kernel::Property > p, const std::string &doc="") override
Add a property to the list of managed properties.
std::string getPropertyValue(const std::string &name) const override
Get the value of a property as a string.
TypedValue getProperty(const std::string &name) const override
Get the value of a property.
virtual std::shared_ptr< Algorithm > createChildAlgorithm(const std::string &name, const double startProgress=-1., const double endProgress=-1., const bool enableLogging=true, const int &version=-1)
Create a Child Algorithm.
Kernel::Logger & g_log
Definition Algorithm.h:422
void progress(double p, const std::string &msg="", double estimatedTime=0.0, int progressPrecision=0)
Sends ProgressNotification.
@ OptionalLoad
to specify a file to read but the file doesn't have to exist
Helper class for reporting progress from algorithms.
Definition Progress.h:25
TableRow represents a row in a TableWorkspace.
Definition TableRow.h:39
A property class for workspaces.
CreateChunkingFromInstrument : TODO: DESCRIPTION.
Geometry::Instrument_const_sptr getInstrument()
Determine the instrument from the various input parameters.
int version() const override
Algorithm's version for identification.
void init() override
Initialize the algorithm's properties.
std::map< std::string, std::string > validateInputs() override
Perform validation of ALL the input properties of the algorithm.
const std::string summary() const override
Algorithm's summary for identification.
const std::string category() const override
Algorithm's category for identification.
const std::string name() const override
Algorithm's name for identification.
Concrete workspace implementation.
Definition Workspace2D.h:29
IPropertyManager * setProperty(const std::string &name, const T &value)
Templated method to set the value of a PropertyWithValue.
void setPropertyGroup(const std::string &name, const std::string &group)
Set the group for a given property.
void debug(const std::string &msg)
Logs at debug level.
Definition Logger.cpp:145
void error(const std::string &msg)
Logs at error level.
Definition Logger.cpp:108
void information(const std::string &msg)
Logs at information level.
Definition Logger.cpp:136
OptionalBool : Tri-state bool.
The concrete, templated class for properties.
static T & Instance()
Return a reference to the Singleton instance, creating it if it does not already exist Creation is do...
@ TOK_TRIM
remove leading and trailing whitespace from tokens
const TokenVec & asVector()
Returns a vector of tokenized strings.
Class that provides for a standard Nexus exception.
std::shared_ptr< ITableWorkspace > ITableWorkspace_sptr
shared pointer to Mantid::API::ITableWorkspace
std::shared_ptr< Algorithm > Algorithm_sptr
Typedef for a shared pointer to an Algorithm.
Definition Algorithm.h:52
std::shared_ptr< MatrixWorkspace > MatrixWorkspace_sptr
shared pointer to the matrix workspace base class
bool startsWith(const string &str, const string &prefix)
Returns true if str starts with prefix.
vector< string > getGroupNames(const string &names)
Split a list of instrument components into a vector of strings.
string parentName(const IComponent_const_sptr &comp, const string &prefix)
Find the name of the parent of the component that starts with the supplied prefix.
std::shared_ptr< const IComponent > IComponent_const_sptr
Typdef of a shared pointer to a const IComponent.
Definition IComponent.h:167
std::shared_ptr< const Instrument > Instrument_const_sptr
Shared pointer to an const instrument object.
STL namespace.
@ Input
An input workspace.
Definition Property.h:53
@ Output
An output workspace.
Definition Property.h:54