Mantid
Loading...
Searching...
No Matches
LoadEventAsWorkspace2D.cpp
Go to the documentation of this file.
1// Mantid Repository : https://github.com/mantidproject/mantid
2//
3// Copyright © 2024 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 +
7
9#include "MantidAPI/Axis.h"
12#include "MantidAPI/Run.h"
13#include "MantidAPI/Sample.h"
24#include <regex>
25
26namespace Mantid {
27namespace DataHandling {
28using namespace API;
30using Mantid::HistogramData::HistogramX;
36// using Mantid::Kernel::TimeROI;
37// Register the algorithm into the AlgorithmFactory
38DECLARE_ALGORITHM(LoadEventAsWorkspace2D)
39
40//----------------------------------------------------------------------------------------------
41
42
43const std::string LoadEventAsWorkspace2D::name() const { return "LoadEventAsWorkspace2D"; }
44
46int LoadEventAsWorkspace2D::version() const { return 1; }
47
49const std::string LoadEventAsWorkspace2D::category() const { return "DataHandling\\Nexus"; }
50
52const std::string LoadEventAsWorkspace2D::summary() const {
53 return "Load event data, integrating the events during loading. Also set the X-axis based on log data.";
54}
55
56//----------------------------------------------------------------------------------------------
60 const std::vector<std::string> exts{".nxs.h5", ".nxs", "_event.nxs"};
61 this->declareProperty(std::make_unique<FileProperty>("Filename", "", FileProperty::Load, exts),
62 "The name of the Event NeXus file to read, including its full or "
63 "relative path. ");
64 declareProperty(std::make_unique<PropertyWithValue<double>>("FilterByTofMin", EMPTY_DBL(), Direction::Input),
65 "To exclude events that do not fall within a range "
66 "of times-of-flight. "
67 "This is the minimum accepted value in microseconds. Keep "
68 "blank to load all events.");
69 declareProperty(std::make_unique<PropertyWithValue<double>>("FilterByTofMax", EMPTY_DBL(), Direction::Input),
70 "To exclude events that do not fall within a range "
71 "of times-of-flight. "
72 "This is the maximum accepted value in microseconds. Keep "
73 "blank to load all events.");
75 std::make_unique<PropertyWithValue<double>>("FilterByTimeStart", EMPTY_DBL(), Direction::Input),
76 "Optional: To only include events after the provided start "
77 "time, in seconds (relative to the start of the run). If left empty, it will take start time as default.");
78 declareProperty(std::make_unique<PropertyWithValue<double>>("FilterByTimeStop", EMPTY_DBL(), Direction::Input),
79 "Optional: To only include events before the provided stop "
80 "time, in seconds (relative to the start of the run). if left empty, it will take run end time or "
81 "last pulse time as default.");
82 declareProperty(std::make_unique<PropertyWithValue<std::vector<std::string>>>(
83 "LogAllowList", std::vector<std::string>(), Direction::Input),
84 "If specified, only these logs will be loaded from the file (each "
85 "separated by a space).");
86 declareProperty(std::make_unique<PropertyWithValue<std::vector<std::string>>>(
87 "LogBlockList", std::vector<std::string>(), Direction::Input),
88 "If specified, these logs will NOT be loaded from the file (each "
89 "separated by a space).");
90 declareProperty(std::make_unique<PropertyWithValue<std::string>>("XCenterLog", "wavelength", Direction::Input),
91 "Name of log to take to use as the X-bin center");
92 declareProperty(std::make_unique<PropertyWithValue<std::string>>("XWidthLog", "wavelength_spread", Direction::Input),
93 "Name of log to take to use as the X-bin width");
95 "Value to set X-bin center to which overrides XCenterLog");
97 "Value to set X-bin width to which overrides XWidthLog");
98 declareProperty("Units", "Wavelength",
99 std::make_shared<StringListValidator>(UnitFactory::Instance().getConvertibleUnits()),
100 "The name of the units to convert to (must be one of those registered in the Unit Factory)");
101 declareProperty(std::make_unique<WorkspaceProperty<Workspace2D>>("OutputWorkspace", "", Direction::Output),
102 "An output workspace.");
103 declareProperty("LoadNexusInstrumentXML", true,
104 "If true, load the instrument definition file (IDF) from the input NeXus file. "
105 "If false, Mantid will load the most appropriate IDF from the instrument repository.");
106}
107
108std::map<std::string, std::string> LoadEventAsWorkspace2D::validateInputs() {
109 std::map<std::string, std::string> results;
110
111 const std::vector<std::string> allow_list = getProperty("LogAllowList");
112 const std::vector<std::string> block_list = getProperty("LogBlockList");
113
114 if (!allow_list.empty() && !block_list.empty()) {
115 results["LogAllowList"] =
116 "LogBlockList and LogAllowList are mutually exclusive. Please only enter values for one of these fields.";
117 results["LogBlockList"] =
118 "LogBlockList and LogAllowList are mutually exclusive. Please only enter values for one of these fields.";
119 }
120
121 const double tofMin = getProperty("FilterByTofMin");
122 const double tofMax = getProperty("FilterByTofMax");
123
124 if (tofMin != EMPTY_DBL() && tofMax != EMPTY_DBL()) {
125 if (tofMin == EMPTY_DBL() || tofMax == EMPTY_DBL()) {
126 results["FilterByTofMin"] = "You must specify both min & max or neither TOF filters";
127 results["FilterByTofMax"] = "You must specify both min & max or neither TOF filters";
128 } else if (tofMin >= tofMax) {
129 results["FilterByTofMin"] = "FilterByTofMin must be less than FilterByTofMax";
130 results["FilterByTofMax"] = "FilterByTofMax must be greater than FilterByTofMin";
131 }
132 }
133
134 return results;
135}
136
137//----------------------------------------------------------------------------------------------
141 std::string filename = getPropertyValue("Filename");
142
143 auto prog = std::make_unique<Progress>(this, 0.0, 1.0, 6);
144
145 // temporary workspace to load instrument and metadata
146 MatrixWorkspace_sptr WS = WorkspaceFactory::Instance().create("Workspace2D", 1, 1, 1);
147 // Load the logs
148 prog->doReport("Loading logs");
149 int nPeriods = 1; // Unused
150 auto periodLog = std::make_unique<const TimeSeriesProperty<int>>("period_log"); // Unused
151 LoadEventNexus::runLoadNexusLogs<MatrixWorkspace_sptr>(filename, WS, *this, false, nPeriods, periodLog,
152 getProperty("LogAllowList"), getProperty("LogBlockList"));
153
154 if (nPeriods != 1)
155 g_log.warning("This algorithm does not correctly handle period data");
156
157 // set center and width parameters, do it before we try to load the data so if the log doesn't exist we fail fast
158 double center = getProperty("XCenter");
159 if (center == EMPTY_DBL())
160 center = WS->run().getStatistics(getPropertyValue("XCenterLog")).mean;
161
162 double width = getProperty("XWidth");
163 if (width == EMPTY_DBL())
164 width = WS->run().getStatistics(getPropertyValue("XWidthLog")).mean;
165 width *= center;
166
167 if (width == 0.) {
168 std::string errmsg(
169 "Width was calculated to be 0 (XCenter*XWidth). This will result in a invalid bin with zero width");
170 g_log.error(errmsg);
171 throw std::runtime_error(errmsg);
172 }
173
174 const Nexus::NexusDescriptor descriptor(filename);
175
176 // Load the instrument
177 prog->doReport("Loading instrument");
178 LoadEventNexus::loadInstrument<MatrixWorkspace_sptr>(filename, WS, "entry", this, &descriptor);
179
180 // load run metadata
181 prog->doReport("Loading metadata");
182 try {
183 LoadEventNexus::loadEntryMetadata(filename, WS, "entry", descriptor);
184 } catch (std::exception &e) {
185 g_log.warning() << "Error while loading meta data: " << e.what() << '\n';
186 }
187
188 // create IndexInfo
189 prog->doReport("Creating IndexInfo");
190 const std::vector<int32_t> range;
191 LoadEventNexusIndexSetup indexSetup(WS, EMPTY_INT(), EMPTY_INT(), range);
192 auto indexInfo = indexSetup.makeIndexInfo();
193 const size_t numHist = indexInfo.size();
194
195 // make output workspace with correct number of histograms
196 MatrixWorkspace_sptr outWS = WorkspaceFactory::Instance().create(WS, numHist, 2, 1);
197 // set spectrum index information
198 outWS->setIndexInfo(indexInfo);
199
200 // now load the data
201 const auto id_to_wi = outWS->getDetectorIDToWorkspaceIndexMap();
202 detid_t min_detid = std::numeric_limits<detid_t>::max();
203 detid_t max_detid = std::numeric_limits<detid_t>::min();
204
205 for (const auto &entry : id_to_wi) {
206 min_detid = std::min(min_detid, entry.first);
207 max_detid = std::max(max_detid, entry.first);
208 }
209
210 const double tof_min = getProperty("FilterByTofMin");
211 const double tof_max = getProperty("FilterByTofMax");
212 const bool tof_filtering = (tof_min != EMPTY_DBL() && tof_max != EMPTY_DBL());
213
214 double filter_time_start_sec, filter_time_stop_sec;
215 filter_time_start_sec = getProperty("FilterByTimeStart");
216 filter_time_stop_sec = getProperty("FilterByTimeStop");
217
218 // get the start time
219 if (!WS->run().hasProperty("start_time")) {
220 g_log.warning("This NXS file does not have a start time, first pulse time is used instead.");
221 }
222
223 Types::Core::DateAndTime runstart(WS->run().hasProperty("start_time") ? WS->run().getProperty("start_time")->value()
224 : WS->run().getFirstPulseTime());
225
226 // get end time, to account for some older DAS issue where last pulse time can be slightly greater than end time, use
227 // last pulse time in this case and check if WS has "proton_charge" property otherwise last pulse time doesn't exist.
228 Types::Core::DateAndTime endtime;
229 if (WS->run().hasProperty("proton_charge") &&
230 WS->run().getLastPulseTime() > WS->run().getProperty("end_time")->value()) {
231 endtime = WS->run().getLastPulseTime();
232 g_log.warning("Last pulse time is used because the last pulse time is greater than the end time!");
233 } else {
234 endtime = WS->run().getProperty("end_time")->value();
235 }
236
237 Types::Core::DateAndTime filter_time_start;
238 Types::Core::DateAndTime filter_time_stop;
239
240 // vector to stored to integrated counts by detector ID
241 std::vector<uint32_t> Y(max_detid - min_detid + 1, 0);
242
243 Nexus::File h5file(filename);
244
245 h5file.openAddress("/");
246 h5file.openGroup("entry", "NXentry");
247
248 // Now we want to go through all the bankN_event entries
249 const std::map<std::string, std::set<std::string>> &allEntries = descriptor.getAllEntries();
250
251 prog->doReport("Reading and integrating data");
252 auto itClassEntries = allEntries.find("NXevent_data");
253 if (itClassEntries != allEntries.end()) {
254
255 const std::set<std::string> &classEntries = itClassEntries->second;
256 const std::regex classRegex("(/entry/)([^/]*)");
257 std::smatch groups;
258 for (const std::string &classEntry : classEntries) {
259
260 if (std::regex_match(classEntry, groups, classRegex)) {
261 const std::string entry_name(groups[2].str());
262 // skip entries with junk data
263 if (entry_name == "bank_error_events" || entry_name == "bank_unmapped_events")
264 continue;
265 g_log.debug() << "Loading bank " << entry_name << '\n';
266 h5file.openGroup(entry_name, "NXevent_data");
267
268 std::vector<uint32_t> event_ids;
269
270 if (descriptor.isEntry("/entry/" + entry_name + "/event_id", "SDS"))
271 event_ids = Nexus::IOHelper::readNexusVector<uint32_t>(h5file, "event_id");
272 else
273 event_ids = Nexus::IOHelper::readNexusVector<uint32_t>(h5file, "event_pixel_id");
274
275 // closeGroup and skip this bank if there is no event
276 if (event_ids.size() <= 1) {
277 h5file.closeGroup();
278 continue;
279 }
280
281 // Load "h5file" into BankPulseTimes using a shared ptr
282 const auto bankPulseTimes = std::make_shared<BankPulseTimes>(boost::ref(h5file), std::vector<int>());
283
284 // Get event_index from the "h5file"
285 const auto event_index =
286 std::make_shared<std::vector<uint64_t>>(Nexus::IOHelper::readNexusVector<uint64_t>(h5file, "event_index"));
287
288 // if "filterTimeStart" is empty, use run start time as default
289 if (filter_time_start_sec != EMPTY_DBL()) {
290 filter_time_start = runstart + filter_time_start_sec;
291 } else {
292 filter_time_start = runstart;
293 }
294
295 // if "filterTimeStop" is empty, use end time as default
296 if (filter_time_stop_sec != EMPTY_DBL()) {
297 filter_time_stop = runstart + filter_time_stop_sec;
298 } else {
299 filter_time_stop = endtime;
300 }
301
302 // Use filter_time_start time as starting reference in time and create a TimeROI using bankPulseTimes
303 const auto TimeROI = bankPulseTimes->getPulseIndices(filter_time_start, filter_time_stop);
304
305 // set up PulseIndexer and give previous TimeROI to pulseIndexer
306 const PulseIndexer pulseIndexer(event_index, event_index->at(0), event_ids.size(), entry_name, TimeROI);
307
308 std::vector<float> event_times;
309 if (tof_filtering) {
310 if (descriptor.isEntry("/entry/" + entry_name + "/event_time_offset", "SDS"))
311 event_times = Nexus::IOHelper::readNexusVector<float>(h5file, "event_time_offset");
312 else
313 event_times = Nexus::IOHelper::readNexusVector<float>(h5file, "event_time_of_flight");
314 }
315
316 // Nested loop to loop through all the relavent pulses and relavent event_ids.
317 // For someone new to this, every pulse creates an entry in event_index, event_index.size() = # of pulses,
318 // the value of event_index[i] points to the index of event_ids. In short, event_ids[event_index[i]] is the
319 // detector id from the i-th pulse. See NXevent_data description for more details.
320 for (const auto &pulse : pulseIndexer) {
321 for (size_t i = pulse.eventIndexStart; i < pulse.eventIndexStop; i++) {
322 // for (size_t i = 0; i < event_ids.size(); i++) {
323 if (tof_filtering) {
324 const auto tof = event_times[i];
325 if (tof < tof_min || tof > tof_max)
326 continue;
327 }
328 const detid_t det_id = event_ids[i];
329 if (det_id < min_detid || det_id > max_detid)
330 continue;
331 Y[det_id - min_detid]++;
332 }
333 }
334 h5file.closeGroup();
335 }
336 }
337 }
338
339 h5file.closeGroup();
340 h5file.close();
341
342 // determine x values
343 const auto xBins = {center - width / 2, center + width / 2};
344
345 prog->doReport("Setting data to workspace");
346 // set the data on the workspace
347 const auto histX = Mantid::Kernel::make_cow<HistogramX>(xBins);
348 for (detid_t detid = min_detid; detid <= max_detid; detid++) {
349 const auto id_wi = id_to_wi.find(detid);
350 if (id_wi == id_to_wi.end())
351 continue;
352
353 const auto wi = id_wi->second;
354 const auto y = Y[detid - min_detid];
355
356 outWS->mutableY(wi) = y;
357 outWS->mutableE(wi) = sqrt(y);
358 outWS->setSharedX(wi, histX);
359 }
360
361 // set units
362 outWS->getAxis(0)->setUnit(getPropertyValue("Units"));
363 outWS->setYUnit("Counts");
364
365 // add filename
366 outWS->mutableRun().addProperty("Filename", filename);
367 setProperty("OutputWorkspace", outWS);
368}
369
370} // namespace DataHandling
371} // namespace Mantid
std::string name
Definition Run.cpp:60
#define DECLARE_ALGORITHM(classname)
Definition Algorithm.h:538
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.
Kernel::Logger & g_log
Definition Algorithm.h:422
@ Load
allowed here which will be passed to the algorithm
A property class for workspaces.
LoadEventAsWorkspace2D : Load event data, integrating the events during loading.
int version() const override
Algorithm's version for identification.
const std::string category() const override
Algorithm's category for identification.
std::map< std::string, std::string > validateInputs() override
Perform validation of ALL the input properties of the algorithm.
void init() override
Initialize the algorithms properties.
const std::string summary() const override
Algorithm's summary for use in the GUI and help.
Helper for LoadEventNexus dealing with setting up indices (spectrum numbers an detector ID mapping) f...
static void loadEntryMetadata(const std::string &nexusfilename, T WS, const std::string &entry_name, const Nexus::NexusDescriptor &descriptor)
Load the run number and other meta data from the given bank.
PulseIndexer contains information for mapping from pulse index/number to event index.
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.
ListValidator is a validator that requires the value of a property to be one of a defined list of pos...
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 warning(const std::string &msg)
Logs at warning level.
Definition Logger.cpp:117
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...
TimeROI : Object that holds information about when the time measurement was active.
Definition TimeROI.h:18
A specialised Property class for holding a series of time-value pairs.
const std::map< std::string, std::set< std::string > > & getAllEntries() const noexcept
Returns a const reference of the internal map holding all entries in the Nexus HDF5 file.
bool isEntry(const std::string &entryName, const std::string &groupClass) const noexcept
Checks if a full-address entry exists for a particular groupClass in a Nexus dataset.
std::shared_ptr< MatrixWorkspace > MatrixWorkspace_sptr
shared pointer to the matrix workspace base class
SingletonHolder< UnitFactoryImpl > UnitFactory
Definition UnitFactory.h:79
Helper class which provides the Collimation Length for SANS instruments.
constexpr int EMPTY_INT() noexcept
Returns what we consider an "empty" integer within a property.
Definition EmptyValues.h:24
int32_t detid_t
Typedef for a detector ID.
constexpr double EMPTY_DBL() noexcept
Returns what we consider an "empty" double within a property.
Definition EmptyValues.h:42
STL namespace.
Describes the direction (within an algorithm) of a Property.
Definition Property.h:50
@ Input
An input workspace.
Definition Property.h:53
@ Output
An output workspace.
Definition Property.h:54