Mantid
Loading...
Searching...
No Matches
SaveMD.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/Progress.h"
21#include "MantidKernel/Matrix.h"
23#include "MantidKernel/System.h"
24#include <Poco/File.h>
25
26using file_holder_type = std::unique_ptr<::NeXus::File>;
27
28using namespace Mantid::Kernel;
29using namespace Mantid::API;
30using namespace Mantid::DataObjects;
31
32namespace {
33template <typename MDE, size_t nd>
34void prepareUpdate(MDBoxFlatTree &BoxFlatStruct, BoxController *bc, typename MDEventWorkspace<MDE, nd>::sptr ws,
35 const std::string &filename) {
36 // remove all boxes from the DiskBuffer. DB will calculate boxes positions
37 // on HDD.
38 bc->getFileIO()->flushCache();
39 // flatten the box structure; this will remember boxes file positions in the
40 // box structure
41 BoxFlatStruct.initFlatStructure(ws, filename);
42}
43} // namespace
44
45namespace Mantid::MDAlgorithms {
46
47// Register the algorithm into the AlgorithmFactory
49
50//----------------------------------------------------------------------------------------------
53void SaveMD::init() {
54 declareProperty(std::make_unique<WorkspaceProperty<IMDWorkspace>>("InputWorkspace", "", Direction::Input),
55 "An input MDEventWorkspace or MDHistoWorkspace.");
56
57 declareProperty(std::make_unique<FileProperty>("Filename", "", FileProperty::OptionalSave, ".nxs"),
58 "The name of the Nexus file to write, as a full or relative path.\n"
59 "Optional if UpdateFileBackEnd is checked.");
60 // Filename is NOT used if UpdateFileBackEnd
61 setPropertySettings("Filename", std::make_unique<EnabledWhenProperty>("UpdateFileBackEnd", IS_EQUAL_TO, "0"));
62
63 declareProperty("UpdateFileBackEnd", false,
64 "Only for MDEventWorkspaces with a file back end: check this to update "
65 "the NXS file on disk\n"
66 "to reflect the current data structure. Filename parameter is ignored.");
67 setPropertySettings("UpdateFileBackEnd", std::make_unique<EnabledWhenProperty>("MakeFileBacked", IS_EQUAL_TO, "0"));
68
69 declareProperty("MakeFileBacked", false,
70 "For an MDEventWorkspace that was created in memory:\n"
71 "This saves it to a file AND makes the workspace into a "
72 "file-backed one.");
73 setPropertySettings("MakeFileBacked", std::make_unique<EnabledWhenProperty>("UpdateFileBackEnd", IS_EQUAL_TO, "0"));
74}
75
76//----------------------------------------------------------------------------------------------
83template <typename MDE, size_t nd> void SaveMD::doSaveEvents(typename MDEventWorkspace<MDE, nd>::sptr ws) {
84 bool updateFileBackend = getProperty("UpdateFileBackEnd");
85 bool makeFileBackend = getProperty("MakeFileBacked");
86 if (updateFileBackend && makeFileBackend)
87 throw std::invalid_argument("Please choose either UpdateFileBackEnd or MakeFileBacked, not both.");
88
89 bool wsIsFileBacked = ws->isFileBacked();
90 std::string filename = getPropertyValue("Filename");
92 auto copyFile = wsIsFileBacked && !filename.empty() && filename != bc->getFilename();
93 if (wsIsFileBacked) {
94 if (makeFileBackend) {
95 throw std::runtime_error("MakeFileBacked selected but workspace is already file backed.");
96 }
97 } else {
98 if (updateFileBackend) {
99 throw std::runtime_error("UpdateFileBackEnd selected but workspace is not file backed.");
100 }
101 }
102
103 if (!wsIsFileBacked) {
104 Poco::File oldFile(filename);
105 if (oldFile.exists())
106 oldFile.remove();
107 }
108
109 auto prog = std::make_unique<Progress>(this, 0.0, 0.05, 1);
110 if (updateFileBackend) // workspace has its own file and ignores any changes
111 // to the
112 // algorithm parameters
113 {
114 if (!ws->isFileBacked())
115 throw std::runtime_error(" attempt to update non-file backed workspace");
116 filename = bc->getFileIO()->getFileName();
117 }
118
119 //-----------------------------------------------------------------------------------------------------
120 // create or open WS group and put there additional information about WS and
121 // its dimensions
122 auto nDims = static_cast<int>(nd);
123 bool data_exist;
124 auto file =
125 file_holder_type(MDBoxFlatTree::createOrOpenMDWSgroup(filename, nDims, MDE::getTypeName(), false, data_exist));
126
127 // Save each NEW ExperimentInfo to a spot in the file
129 if (!updateFileBackend || !data_exist) {
130 MDBoxFlatTree::saveWSGenericInfo(file.get(), ws);
131 }
132 file->closeGroup();
133 file->close();
134
135 MDBoxFlatTree BoxFlatStruct;
136 //-----------------------------------------------------------------------------------------------------
137 if (updateFileBackend) // the workspace is already file backed;
138 {
139 prepareUpdate<MDE, nd>(BoxFlatStruct, bc.get(), ws, filename);
140 } else if (copyFile) {
141 // Update the original file
142 if (ws->fileNeedsUpdating()) {
143 prepareUpdate<MDE, nd>(BoxFlatStruct, bc.get(), ws, filename);
144 BoxFlatStruct.saveBoxStructure(filename);
145 }
146 bc->getFileIO()->copyFileTo(filename);
147 } else // not file backed;
148 {
149 // the boxes file positions are unknown and we need to calculate it.
150 BoxFlatStruct.initFlatStructure(ws, filename);
151 // create saver class
152 auto Saver = std::shared_ptr<API::IBoxControllerIO>(new DataObjects::BoxControllerNeXusIO(bc.get()));
153 Saver->setDataType(sizeof(coord_t), MDE::getTypeName());
154 if (makeFileBackend) {
155 // store saver with box controller
156 bc->setFileBacked(Saver, filename);
157 // get access to boxes array
158 std::vector<API::IMDNode *> &boxes = BoxFlatStruct.getBoxes();
159 // calculate the position of the boxes on file, indicating to make them
160 // saveable and that the boxes were not saved.
161 BoxFlatStruct.setBoxesFilePositions(true);
162 prog->resetNumSteps(boxes.size(), 0.06, 0.90);
163 for (auto &boxe : boxes) {
164 auto saveableTag = boxe->getISaveable();
165 if (saveableTag) // only boxes can be saveable
166 {
167 // do not spend time on empty or masked boxes
168 if (boxe->getDataInMemorySize() == 0 || boxe->getIsMasked())
169 continue;
170 // save boxes directly using the boxes file postion, precalculated in
171 // boxFlatStructure.
172 saveableTag->save();
173 // remove boxes data from memory. This will actually correctly set the
174 // tag indicatin that data were not loaded.
175 saveableTag->clearDataFromMemory();
176 // put boxes into write buffer wich will save them when necessary
177 // Saver->toWrite(saveTag);
178 prog->report("Saving Box");
179 }
180 }
181 // remove everything from diskBuffer; (not sure if it really necessary
182 // but just in case , should not make any harm)
183 Saver->flushCache();
184 // drop NeXus on HDD (not sure if it really necessary but just in case )
185 Saver->flushData();
186 } else // just save data, and finish with it
187 {
188 Saver->openFile(filename, "w");
189 BoxFlatStruct.setBoxesFilePositions(false);
190 std::vector<API::IMDNode *> &boxes = BoxFlatStruct.getBoxes();
191 std::vector<uint64_t> &eventIndex = BoxFlatStruct.getEventIndex();
192 prog->resetNumSteps(boxes.size(), 0.06, 0.90);
193 for (size_t i = 0; i < boxes.size(); i++) {
194 if (eventIndex[2 * i + 1] == 0 || boxes[i]->getIsMasked())
195 continue;
196 boxes[i]->saveAt(Saver.get(), eventIndex[2 * i]);
197 prog->report("Saving Box");
198 }
199 Saver->closeFile();
200 }
201 }
202
203 // -------------- Save Box Structure -------------------------------------
204 // OK, we've filled these big arrays of data representing flat box structrre.
205 // Save them.
206 progress(0.91, "Writing Box Data");
207 prog->resetNumSteps(8, 0.92, 1.00);
208
209 // Save box structure;
210 if (!copyFile) {
211 BoxFlatStruct.saveBoxStructure(filename);
212 }
213
214 ws->setFileNeedsUpdating(false);
215}
216
217//----------------------------------------------------------------------------------------------
223 std::string filename = getPropertyValue("Filename");
224
225 // Erase the file if it exists
226 Poco::File oldFile(filename);
227 if (oldFile.exists())
228 oldFile.remove();
229
230 // Create a new file in HDF5 mode.
231 auto file = std::make_unique<::NeXus::File>(filename, NXACC_CREATE5);
232
233 // The base entry. Named so as to distinguish from other workspace types.
234 file->makeGroup("MDHistoWorkspace", "NXentry", true);
235
236 // Write out the coordinate system
237 file->writeData("coordinate_system", static_cast<uint32_t>(ws->getSpecialCoordinateSystem()));
238
239 // Write out the set display normalization
240 file->writeData("visual_normalization", static_cast<uint32_t>(ws->displayNormalization()));
241
242 // Save the algorithm history under "process"
243 ws->getHistory().saveNexus(file.get());
244
245 // Save all the ExperimentInfos
246 for (uint16_t i = 0; i < ws->getNumExperimentInfo(); i++) {
247 ExperimentInfo_sptr ei = ws->getExperimentInfo(i);
248 std::string groupName = "experiment" + Strings::toString(i);
249 if (ei) {
250 // Can't overwrite entries. Just add the new ones
251 file->makeGroup(groupName, "NXgroup", true);
252 file->putAttr("version", 1);
253 ei->saveExperimentInfoNexus(file.get());
254 file->closeGroup();
255 }
256 }
257
258 // Write out some general information like # of dimensions
259 file->writeData("dimensions", int32_t(ws->getNumDims()));
260
261 // Save each dimension, as their XML representation
262 for (size_t d = 0; d < ws->getNumDims(); d++) {
263 std::ostringstream mess;
264 mess << "dimension" << d;
265 file->putAttr(mess.str(), ws->getDimension(d)->toXMLString());
266 }
267
268 // Write out the affine matrices
269 MDBoxFlatTree::saveAffineTransformMatricies(file.get(), std::dynamic_pointer_cast<const IMDWorkspace>(ws));
270
271 // Check that the typedef has not been changed. The NeXus types would need
272 // changing if it does!
273 static_assert(sizeof(signal_t) == sizeof(double), "signal_t using has been changed!");
274
275 // Number of data points
276 auto nPoints = static_cast<int>(ws->getNPoints());
277
278 file->makeData("signal", ::NeXus::FLOAT64, nPoints, true);
279 file->putData(ws->getSignalArray());
280 file->closeData();
281
282 file->makeData("errors_squared", ::NeXus::FLOAT64, nPoints, true);
283 file->putData(ws->getErrorSquaredArray());
284 file->closeData();
285
286 file->makeData("num_events", ::NeXus::FLOAT64, nPoints, true);
287 file->putData(ws->getNumEventsArray());
288 file->closeData();
289
290 file->makeData("mask", ::NeXus::INT8, nPoints, true);
291 file->putData(ws->getMaskArray());
292 file->closeData();
293
294 // TODO: Links to original workspace???
295
296 file->closeGroup();
297 file->close();
298}
299
300//----------------------------------------------------------------------------------------------
304 IMDWorkspace_sptr ws = getProperty("InputWorkspace");
305 IMDEventWorkspace_sptr eventWS = std::dynamic_pointer_cast<IMDEventWorkspace>(ws);
306 MDHistoWorkspace_sptr histoWS = std::dynamic_pointer_cast<MDHistoWorkspace>(ws);
307
308 if (eventWS) {
309 // Wrapper to cast to MDEventWorkspace then call the function
310 CALL_MDEVENT_FUNCTION(this->doSaveEvents, eventWS);
311 } else if (histoWS) {
312 this->doSaveHisto(histoWS);
313 } else
314 throw std::runtime_error("SaveMD can only save MDEventWorkspaces and "
315 "MDHistoWorkspaces.\nPlease use SaveNexus or "
316 "another algorithm appropriate for this workspace "
317 "type.");
318}
319
320} // namespace Mantid::MDAlgorithms
#define DECLARE_ALGORITHM(classname)
Definition: Algorithm.h:576
std::unique_ptr<::NeXus::File > file_holder_type
#define CALL_MDEVENT_FUNCTION(funcname, workspace)
Macro that makes it possible to call a templated method for a MDEventWorkspace using a IMDEventWorksp...
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
void progress(double p, const std::string &msg="", double estimatedTime=0.0, int progressPrecision=0)
Sends ProgressNotification.
Definition: Algorithm.cpp:231
This class is used by MDBox and MDGridBox in order to intelligently determine optimal behavior.
Definition: BoxController.h:33
IBoxControllerIO * getFileIO()
returns the pointer to the class, responsible for fileIO operations;
@ OptionalSave
to specify a file to write to but an empty string is
Definition: FileProperty.h:50
void setFileNeedsUpdating(bool value)
Sets the marker set to true when a file-backed workspace needs its back-end file updated (by calling ...
A property class for workspaces.
The class responsible for saving events into nexus file using generic box controller interface Expect...
The class responsible for saving/loading MD boxes structure to/from HDD and for flattening/restoring ...
Definition: MDBoxFlatTree.h:27
std::vector< API::IMDNode * > & getBoxes()
Definition: MDBoxFlatTree.h:38
void initFlatStructure(const API::IMDEventWorkspace_sptr &pws, const std::string &fileName)
convert MDWS box structure into flat structure used for saving/loading on hdd
static void saveWSGenericInfo(::NeXus::File *const file, const API::IMDWorkspace_const_sptr &ws)
Save workspace generic info like dimension structure, history, title dimensions etc.
std::vector< uint64_t > & getEventIndex()
Definition: MDBoxFlatTree.h:45
static void saveAffineTransformMatricies(::NeXus::File *const file, const API::IMDWorkspace_const_sptr &ws)
Save the affine matrices to both directional conversions to the data.
static void saveExperimentInfos(::NeXus::File *const file, const API::IMDEventWorkspace_const_sptr &ws)
Save each NEW ExperimentInfo to a spot in the file.
void saveBoxStructure(const std::string &fileName)
Save flat box structure into a file, defined by the file name.
::NeXus::File * createOrOpenMDWSgroup(const std::string &fileName, int &nDims, const std::string &WSEventType, bool readOnly, bool &alreadyExists)
The function to create a NeXus MD workspace group with specified events type and number of dimensions...
void setBoxesFilePositions(bool setFileBacked)
std::shared_ptr< MDEventWorkspace< MDE, nd > > sptr
Typedef for a shared pointer of this kind of event workspace.
Mantid::API::BoxController_sptr getBoxController() override
Returns the BoxController used in this workspace.
static std::string getTypeName()
Definition: MDLeanEvent.h:305
void flushCache()
Flush out all the data in the memory; and writes out everything in the to-write cache.
Definition: DiskBuffer.cpp:192
Save a MDEventWorkspace to a .nxs file.
Definition: SaveMD.h:22
void doSaveEvents(typename DataObjects::MDEventWorkspace< MDE, nd >::sptr ws)
Helper method.
Definition: SaveMD.cpp:83
void exec() override
Run the algorithm.
Definition: SaveMD.cpp:303
void doSaveHisto(const Mantid::DataObjects::MDHistoWorkspace_sptr &ws)
Save the MDHistoWorkspace.
Definition: SaveMD.cpp:222
std::shared_ptr< IMDEventWorkspace > IMDEventWorkspace_sptr
Shared pointer to Mantid::API::IMDEventWorkspace.
std::shared_ptr< ExperimentInfo > ExperimentInfo_sptr
Shared pointer to ExperimentInfo.
std::shared_ptr< IMDWorkspace > IMDWorkspace_sptr
Shared pointer to the IMDWorkspace base class.
Definition: IMDWorkspace.h:146
std::shared_ptr< BoxController > BoxController_sptr
Shared ptr to BoxController.
std::shared_ptr< MDHistoWorkspace > MDHistoWorkspace_sptr
A shared pointer to a MDHistoWorkspace.
std::string toString(const T &value)
Convert a number to a string.
Definition: Strings.cpp:703
float coord_t
Typedef for the data type to use for coordinate axes in MD objects such as MDBox, MDEventWorkspace,...
Definition: MDTypes.h:27
double signal_t
Typedef for the signal recorded in a MDBox, etc.
Definition: MDTypes.h:36
@ Input
An input workspace.
Definition: Property.h:53