Mantid
Loading...
Searching...
No Matches
SaveNXTomo.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 +
8
12#include "MantidAPI/Run.h"
20#include "MantidNexus/NexusFile.h"
21
22namespace Mantid::DataHandling {
23// Register the algorithm into the algorithm factory
24DECLARE_ALGORITHM(SaveNXTomo)
25
26using namespace Kernel;
27using namespace API;
28using namespace DataObjects;
29
30const std::string SaveNXTomo::NXTOMO_VER = "2.0";
31
33 : API::Algorithm(), m_includeError(false), m_overwriteFile(false), m_spectraCount(0), m_filename(""),
34 m_nxFile(nullptr) {}
35
40 auto wsValidator = std::make_shared<CompositeValidator>();
41 // Note: this would be better, but it is too restrictive in
42 // practice when saving image workspaces loaded from different
43 // formats than FITS or not so standard FITS.
44 // wsValidator->add<API::CommonBinsValidator>();
45 wsValidator->add<API::HistogramValidator>();
46
47 declareProperty(std::make_unique<WorkspaceProperty<>>("InputWorkspaces", "", Direction::Input, wsValidator),
48 "The name of the workspace(s) to save - this can be the name of a single "
49 "Workspace2D or the name of a WorkspaceGroup in which case all the "
50 "Workspace2Ds included in the group will be saved.");
51
53 std::make_unique<API::FileProperty>("Filename", "", FileProperty::Save, std::vector<std::string>(1, ".nxs")),
54 "The name of the NXTomo file to write, as a full or relative path");
55
56 declareProperty(std::make_unique<PropertyWithValue<bool>>("OverwriteFile", false, Kernel::Direction::Input),
57 "Replace any existing file of the same name instead of appending data?");
58
59 declareProperty(std::make_unique<PropertyWithValue<bool>>("IncludeError", false, Kernel::Direction::Input),
60 "Write the error values to NXTomo file?");
61}
62
68 try {
69 MatrixWorkspace_sptr m = getProperty("InputWorkspaces");
70 m_workspaces.emplace_back(std::dynamic_pointer_cast<Workspace2D>(m));
71 } catch (...) {
72 }
73
74 if (!m_workspaces.empty())
75 processAll();
76}
77
82 try {
83 std::string wsName = getPropertyValue("InputWorkspaces");
84 WorkspaceGroup_sptr groupWS = AnalysisDataService::Instance().retrieveWS<WorkspaceGroup>(wsName);
85
86 for (int i = 0; i < groupWS->getNumberOfEntries(); ++i) {
87 m_workspaces.emplace_back(std::dynamic_pointer_cast<Workspace2D>(groupWS->getItem(i)));
88 }
89 } catch (...) {
90 }
91
92 if (!m_workspaces.empty())
93 processAll();
94
95 return true;
96}
97
105 m_includeError = getProperty("IncludeError");
106 m_overwriteFile = getProperty("OverwriteFile");
107
108 for (auto &workspace : m_workspaces) {
109 const std::string workspaceID = workspace->id();
110
111 if ((workspaceID.find("Workspace2D") == std::string::npos) &&
112 (workspaceID.find("RebinnedOutput") == std::string::npos))
113 throw Exception::NotImplementedError("SaveNXTomo passed invalid workspaces. Must be Workspace2D");
114 }
115
116 // Retrieve the filename from the properties
117 this->m_filename = getPropertyValue("Filename");
118
119 // Populate the dimension array - assume all are the same
120 m_dimensions.emplace_back(m_workspaces.size());
121 m_dimensions.emplace_back(boost::lexical_cast<int64_t>(m_workspaces[0]->mutableRun().getLogData("Axis1")->value()));
122 m_dimensions.emplace_back(boost::lexical_cast<int64_t>(m_workspaces[0]->mutableRun().getLogData("Axis2")->value()));
123
125
126 // Define
129
130 // What size slabs are we going to write
131 m_slabSize.emplace_back(1);
132 m_slabSize.emplace_back(m_dimensions[1]);
133 m_slabSize.emplace_back(m_dimensions[2]);
134
135 // Init start to first row
136 m_slabStart.emplace_back(0);
137 m_slabStart.emplace_back(0);
138 m_slabStart.emplace_back(0);
139
140 setupFile();
141
142 // Create a progress reporting object
143 Progress progress(this, 0.0, 1.0, m_workspaces.size());
144
145 for (auto const &workspace : m_workspaces) {
147 progress.report();
148 }
149
150 m_nxFile->close();
151}
152
159 // if this pointer already points to a file, make sure it is closed
160 if (m_nxFile) {
161 m_nxFile->close();
162 m_nxFile = nullptr;
163 }
164
165 // if we can overwrite, try to open the file
166 if (!m_overwriteFile) {
167 try {
168 m_nxFile = std::make_unique<Nexus::File>(m_filename, NXaccess::RDWR);
169 return;
170 } catch (Nexus::Exception const &) {
171 }
172 }
173
174 // If not overwriting, or no existing file found above, create new file
175 if (!m_nxFile) {
176 m_nxFile = std::make_unique<Nexus::File>(m_filename, NXaccess::CREATE5);
177 }
178 // Make the top level entry (and open it)
179 m_nxFile->makeGroup("entry1", "NXentry", true);
180
181 // Make an entry to store log values from the original files.
182 m_nxFile->makeGroup("log_info", "NXsubentry", false);
183
184 // Make a sub-group for the entry to work with DAWN software (and open it)
185 m_nxFile->makeGroup("tomo_entry", "NXsubentry", true);
186
187 // Title
188 m_nxFile->writeData("title", this->m_filename);
189
190 // Definition name and version
191 m_nxFile->writeData("definition", "NXtomo");
192 m_nxFile->openData("definition");
193 m_nxFile->putAttr("version", NXTOMO_VER);
194 m_nxFile->closeData();
195
196 // Originating program name and version
197 m_nxFile->writeData("program_name", "mantid");
198 m_nxFile->openData("program_name");
200 m_nxFile->closeData();
201
202 // ******************************************
203 // NXinstrument
204 m_nxFile->makeGroup("instrument", "NXinstrument", true);
205 // Write the instrument name | could add short_name attribute to name
206 m_nxFile->writeData("name", m_workspaces[0]->getInstrument()->getName());
207
208 // detector group - diamond example file contains
209 // {data,distance,image_key,x_pixel_size,y_pixel_size}
210 m_nxFile->makeGroup("detector", "NXdetector", true);
211
212 m_nxFile->makeData("image_key", NXnumtype::FLOAT64, NX_UNLIMITED, false);
213 m_nxFile->closeGroup(); // detector
214
215 // source group // from diamond file contains {current,energy,name,probe,type}
216 // - probe = [neutron | x-ray | electron]
217
218 m_nxFile->closeGroup(); // NXinstrument
219
220 // ******************************************
221 // NXsample
222 m_nxFile->makeGroup("sample", "NXsample", true);
223
224 m_nxFile->makeData("rotation_angle", NXnumtype::FLOAT64, NX_UNLIMITED, true);
225 // Create a link object for rotation_angle to use later
226 NXlink rotationLink = m_nxFile->getDataID();
227 m_nxFile->closeData();
228 m_nxFile->closeGroup(); // NXsample
229
230 // ******************************************
231 // Make the NXmonitor group - Holds base beam intensity for each image
232
233 m_nxFile->makeGroup("control", "NXmonitor", true);
234 m_nxFile->makeData("data", NXnumtype::FLOAT64, NX_UNLIMITED, false);
235 m_nxFile->closeGroup(); // NXmonitor
236
237 m_nxFile->makeGroup("data", "NXdata", true);
238 m_nxFile->putAttr<int>("NumFiles", 0);
239
240 m_nxFile->makeLink(rotationLink);
241
242 m_nxFile->makeData("data", NXnumtype::FLOAT64, m_infDimensions, true);
243 // Create a link object for the data
244 NXlink dataLink = m_nxFile->getDataID();
245 m_nxFile->closeData();
246
247 if (m_includeError)
248 m_nxFile->makeData("error", NXnumtype::FLOAT64, m_infDimensions, false);
249
250 m_nxFile->closeGroup(); // Close Data group
251
252 // Put a link to the data in instrument/detector
253 m_nxFile->openGroup("instrument", "NXinstrument");
254 m_nxFile->openGroup("detector", "NXdetector");
255 m_nxFile->makeLink(dataLink);
256 m_nxFile->closeGroup();
257 m_nxFile->closeGroup();
258
259 m_nxFile->closeGroup(); // tomo_entry sub-group
260 m_nxFile->closeGroup(); // Top level NXentry
261}
262
268 try {
269 m_nxFile->openAddress("/entry1/tomo_entry/data");
270 } catch (...) {
271 throw std::runtime_error("Unable to create a valid NXTomo file");
272 }
273
274 Nexus::dimsize_t numFiles = 0;
275 m_nxFile->getAttr("NumFiles", numFiles);
276
277 // Change slab start to after last data position
278 m_slabStart[0] = numFiles;
279 m_slabSize[0] = 1;
280
281 // Set the rotation value for this WS
282 std::vector<double> rotValue;
283 rotValue.emplace_back(0);
284
285 if (workspace->run().hasProperty("Rotation")) {
286 std::string tmpVal = workspace->run().getLogData("Rotation")->value();
287 try {
288 rotValue[0] = boost::lexical_cast<double>(tmpVal);
289 } catch (...) {
290 }
291 // Invalid Cast is handled below
292 }
293
294 m_nxFile->openData("rotation_angle");
295 m_nxFile->putSlab(rotValue, numFiles, 1);
296 m_nxFile->closeData();
297
298 // Copy data out, remake data with dimension of old size plus new elements.
299 // Insert previous data.
300 m_nxFile->openData("data");
301
302 auto dataArr = new double[m_spectraCount];
303
304 // images can be as one-spectrum-per-pixel, or one-spectrum-per-row
305 bool spectrumPerPixel = (1 == workspace->y(0).size());
306 for (Nexus::dimsize_t i = 0; i < m_dimensions[1]; ++i) {
307 const auto &Y = workspace->y(i);
308 for (Nexus::dimsize_t j = 0; j < m_dimensions[2]; ++j) {
309 if (spectrumPerPixel) {
310 dataArr[i * m_dimensions[1] + j] = workspace->y(i * m_dimensions[1] + j)[0];
311 } else {
312 dataArr[i * m_dimensions[1] + j] = Y[j];
313 }
314 }
315 }
316
317 m_nxFile->putSlab(dataArr, m_slabStart, m_slabSize);
318
319 m_nxFile->closeData();
320
321 m_nxFile->putAttr("NumFiles", numFiles + 1);
322
323 m_nxFile->closeGroup();
324
325 // Write additional log information, intensity and image key
326 writeLogValues(workspace, numFiles);
328 writeImageKeyValue(workspace, numFiles);
329 delete[] dataArr;
330}
331
333 // Add ImageKey to instrument/image_key if present, use 0 if not
334 try {
335 m_nxFile->openAddress("/entry1/tomo_entry/instrument/detector");
336 } catch (...) {
337 throw std::runtime_error("Unable to create a valid NXTomo file");
338 }
339
340 // Set the default key value for this WS
341 std::vector<double> keyValue(1, 0);
342
343 if (workspace->run().hasProperty("ImageKey")) {
344 std::string tmpVal = workspace->run().getLogData("ImageKey")->value();
345 try {
346 keyValue[0] = boost::lexical_cast<double>(tmpVal);
347 } catch (...) {
348 }
349 // Invalid Cast is handled below
350 }
351
352 m_nxFile->openData("image_key");
353 m_nxFile->putSlab(keyValue, thisFileInd, 1);
354 m_nxFile->closeData();
355
356 m_nxFile->closeGroup();
357}
358
360 // Add Log information (minus special values - Rotation, ImageKey, Intensity)
361 // Unable to add multidimensional string data, storing strings as
362 // multidimensional data set of uint8 values
363 try {
364 m_nxFile->openAddress("/entry1/log_info");
365 } catch (...) {
366 throw std::runtime_error("Unable to create a valid NXTomo file");
367 }
368
369 // Loop through all log values, create it if it doesn't exist. Then append
370 // value
371 const auto &logVals = workspace->run().getLogData();
372
373 for (const auto &prop : logVals) {
374 if (prop->name() != "ImageKey" && prop->name() != "Rotation" && prop->name() != "Intensity" &&
375 prop->name() != "Axis1" && prop->name() != "Axis2") {
376 try {
377 m_nxFile->openData(prop->name());
378 } catch (Nexus::Exception const &) {
379 // Create the data entry if it doesn't exist yet, and open.
381 m_nxFile->makeData(prop->name(), NXnumtype::UINT8, infDim, true);
382 }
383 auto valueAsStr = prop->value();
384 size_t strSize = valueAsStr.length();
385 // If log value is from FITS file as it should be,
386 // it won't be greater than this. Otherwise Shorten it
387 if (strSize > 80)
388 strSize = 80;
389 const Nexus::DimVector start{thisFileInd, 0};
390 const Nexus::DimVector size{1, strSize};
391 // single item
392 m_nxFile->putSlab(valueAsStr.data(), start, size);
393
394 m_nxFile->closeData();
395 }
396 }
397}
398
400 // Add Intensity to control if present, use 1 if not
401 try {
402 m_nxFile->openAddress("/entry1/tomo_entry/control");
403 } catch (...) {
404 throw std::runtime_error("Unable to create a valid NXTomo file");
405 }
406
407 std::vector<double> intensityValue;
408 intensityValue.emplace_back(1);
409
410 if (workspace->run().hasProperty("Intensity")) {
411 std::string tmpVal = workspace->run().getLogData("Intensity")->value();
412 try {
413 intensityValue[0] = boost::lexical_cast<double>(tmpVal);
414 } catch (...) {
415 }
416 // Invalid Cast is handled below
417 }
418
419 m_nxFile->openData("data");
420 m_nxFile->putSlab(intensityValue, thisFileInd, 1);
421 m_nxFile->closeData();
422}
423
424} // namespace Mantid::DataHandling
#define DECLARE_ALGORITHM(classname)
Definition Algorithm.h:538
double value
The value of the point.
Definition FitMW.cpp:51
IPeaksWorkspace_sptr workspace
constexpr Mantid::Nexus::dimsize_t NX_UNLIMITED(-1)
std::string getName(const IMDDimension &self)
Base class from which all concrete algorithm classes should be derived.
Definition Algorithm.h:76
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.
void progress(double p, const std::string &msg="", double estimatedTime=0.0, int progressPrecision=0)
Sends ProgressNotification.
@ Save
to specify a file to write to, the file may or may not exist
A validator which checks that a workspace contains histogram data (the default) or point data as requ...
Helper class for reporting progress from algorithms.
Definition Progress.h:25
Class to hold a set of workspaces.
A property class for workspaces.
static const std::string NXTOMO_VER
file format version
Definition SaveNXTomo.h:115
bool processGroups() override
Alternative execution code when operating on a WorkspaceGroup.
void processAll()
Main exec routine, called for group or individual workspace processing.
std::vector< DataObjects::Workspace2D_sptr > m_workspaces
Definition SaveNXTomo.h:117
void writeImageKeyValue(const DataObjects::Workspace2D_sptr &workspace, std::size_t thisFileInd)
void init() override
Initialisation code.
void writeLogValues(const DataObjects::Workspace2D_sptr &workspace, std::size_t thisFileInd)
Write various pieces of data from the workspace log with checks on the structure of the nexus file.
void setupFile()
Creates the format for the output file if it doesn't exist.
void exec() override
Execution code : Single workspace.
void writeSingleWorkspace(const DataObjects::Workspace2D_sptr &workspace)
Writes a single workspace into the file.
void writeIntensityValue(const DataObjects::Workspace2D_sptr &workspace, std::size_t thisFileInd)
std::unique_ptr< Nexus::File > m_nxFile
Definition SaveNXTomo.h:120
std::string m_filename
The filename of the output file.
Definition SaveNXTomo.h:108
Marks code as not implemented yet.
Definition Exception.h:138
static const char * version()
The full version number.
The concrete, templated class for properties.
Class that provides for a standard Nexus exception.
static unsigned short constexpr UINT8
static unsigned short constexpr FLOAT64
std::shared_ptr< WorkspaceGroup > WorkspaceGroup_sptr
shared pointer to Mantid::API::WorkspaceGroup
std::shared_ptr< MatrixWorkspace > MatrixWorkspace_sptr
shared pointer to the matrix workspace base class
std::shared_ptr< Workspace2D > Workspace2D_sptr
shared pointer to Mantid::DataObjects::Workspace2D
std::vector< dimsize_t > DimVector
@ Input
An input workspace.
Definition Property.h:53