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