Mantid
Loading...
Searching...
No Matches
MDBoxFlatTree.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 +
13#include "MantidKernel/Logger.h"
15
16#include <algorithm>
17#include <filesystem>
18#include <utility>
19
20using file_holder_type = std::unique_ptr<Mantid::Nexus::File>;
21
22namespace Mantid::DataObjects {
23namespace {
25Kernel::Logger g_log("MDBoxFlatTree");
26
27void EmplaceExperimentBlockNum(const std::string &name, std::list<uint16_t> &experimentBlockNum) {
28 if (name.starts_with("experiment")) {
29 try {
30 auto num = boost::lexical_cast<uint16_t>(name.substr(10, name.size() - 10));
31 if (num < std::numeric_limits<uint16_t>::max() - 1) {
32 // dublicated experiment info names are impossible due to the
33 // structure of the nexus file but missing -- can be found.
34 experimentBlockNum.emplace_back(num);
35 }
36 } catch (boost::bad_lexical_cast &) { /* ignore */
37 }
38 }
39}
40
41// check if all subsequent experiment infos numbers are present
42void CheckExperimentBlockNum(const std::list<uint16_t> &experimentBlockNum) {
43
44 auto itr = experimentBlockNum.begin();
45 size_t ic = 0;
46 for (; itr != experimentBlockNum.end(); itr++) {
47 if (*itr != ic) {
48 for (size_t i = ic + 1; i < *itr; i++) {
49 const std::string groupName = "experiment" + Kernel::Strings::toString(i);
50 g_log.warning() << "NXS file is missing a ExperimentInfo block " << groupName
51 << ". Workspace will be missing ExperimentInfo.\n";
52 }
53 }
54 ic++;
55 }
56}
57
58} // namespace
59
61
70void MDBoxFlatTree::initFlatStructure(const API::IMDEventWorkspace_sptr &pws, const std::string &fileName) {
71 m_bcXMLDescr = pws->getBoxController()->toXMLString();
72 m_FileName = fileName;
73
74 m_nDim = int(pws->getNumDims());
75 // flatten the box structure
76 pws->getBoxes(m_Boxes, 1000, false);
77
79
80 size_t maxBoxes = m_Boxes.size();
81 // Box type (0=None, 1=MDBox, 2=MDGridBox
82 m_BoxType.assign(maxBoxes, 0);
83 // Recursion depth
84 m_Depth.assign(maxBoxes, -1);
85 // Start/end indices into the list of events
86 m_BoxEventIndex.assign(maxBoxes * 2, 0);
87 // Min/Max extents in each dimension
88 m_Extents.assign(maxBoxes * m_nDim * 2, 0);
89 // Inverse of the volume of the cell
90 m_InverseVolume.assign(maxBoxes, 0);
91 // Box cached signal/error squared
92 m_BoxSignalErrorsquared.assign(maxBoxes * 2, 0);
93 // Start/end children IDs
94 m_BoxChildren.assign(maxBoxes * 2, 0);
95
96 API::IMDNode *Box;
97 size_t ic(0);
98 bool filePositionDefined(true);
99 for (size_t i = 0; i < maxBoxes; i++) {
100 Box = m_Boxes[i];
101 // currently ID is the number of the box, but it may change in a future.
102 // TODO: uint64_t
103 size_t id = Box->getID();
104 size_t numChildren = Box->getNumChildren();
105 if (numChildren > 0) // MDGridBox have children
106 {
107 // DEBUG:
110 // size_t lastId = Box->getChild(0)->getId();
111 // for (size_t i = 1; i < numChildren; i++)
112 //{
113 // if (Box->getChild(i)->getId() != lastId+1)
114 // throw std::runtime_error("Non-sequential child ID encountered!");
115 // lastId = Box->getChild(i)->getId();
116 //}
117 // TODO! id != ic
118 m_BoxType[ic] = 2;
119 m_BoxChildren[ic * 2] = int(Box->getChild(0)->getID());
120 m_BoxChildren[ic * 2 + 1] = int(Box->getChild(numChildren - 1)->getID());
121
122 // no events but index defined -- TODO -- The proper file has to have
123 // consequent indexes for all boxes too.
124 m_BoxEventIndex[ic * 2] = 0;
125 m_BoxEventIndex[ic * 2 + 1] = 0;
126 } else {
127 m_BoxType[ic] = 1;
128 m_BoxChildren[ic * 2] = 0;
129 m_BoxChildren[ic * 2 + 1] = 0;
130
131 // MDBox<MDE,nd> * mdBox = dynamic_cast<MDBox<MDE,nd> *>(Box);
132 // if(!mdBox) throw std::runtime_error("found unfamiliar type of box");
133 // Store the index
134
135 uint64_t nPoints = Box->getNPoints();
136 Kernel::ISaveable *pSaver = Box->getISaveable();
137 if (pSaver)
138 m_BoxEventIndex[ic * 2] = pSaver->getFilePosition();
139 else
140 filePositionDefined = false;
141
142 m_BoxEventIndex[ic * 2 + 1] = nPoints;
143 }
144
145 // Various bits of data about the box
146 m_Depth[ic] = int(Box->getDepth());
147 m_BoxSignalErrorsquared[ic * 2] = double(Box->getSignal());
148 m_BoxSignalErrorsquared[ic * 2 + 1] = double(Box->getErrorSquared());
149 m_InverseVolume[ic] = Box->getInverseVolume();
150 for (int d = 0; d < m_nDim; d++) {
151 size_t newIndex = id * size_t(m_nDim * 2) + d * 2;
152 m_Extents[newIndex] = Box->getExtents(d).getMin();
153 m_Extents[newIndex + 1] = Box->getExtents(d).getMax();
154 }
155 ic++;
156 }
157 // file position have to be calculated afresh
158 if (!filePositionDefined) {
159 uint64_t boxPosition(0);
160 for (size_t i = 0; i < maxBoxes; i++) {
161 if (m_BoxType[i] == 1) {
162 m_BoxEventIndex[2 * i] = boxPosition;
163 boxPosition += m_BoxEventIndex[2 * i + 1];
164 }
165 }
166 }
167}
168/*** this function tries to set file positions of the boxes to
169 make data physically located close to each other to be as close as possible
170 on the HDD
171 @param setFileBacked -- initiate the boxes to be fileBacked. The boxes
172 assumed not to be saved before.
173*/
174void MDBoxFlatTree::setBoxesFilePositions(bool setFileBacked) {
175 // this will preserve file-backed workspace and information in it as we are
176 // not loading old box data and not?
177 // this would be right for binary access but questionable for Nexus --TODO:
178 // needs testing
179 // Done in INIT--> need check if ID and index in the tree are always the same.
180 // Kernel::ISaveable::sortObjByFilePos(m_Boxes);
181 // calculate the box positions in the resulting file and save it on place
182 uint64_t eventsStart = 0;
183 for (auto mdBox : m_Boxes) {
184 size_t ID = mdBox->getID();
185
186 // avoid grid boxes;
187 if (m_BoxType[ID] == 2)
188 continue;
189
190 size_t nEvents = mdBox->getTotalDataSize();
191 m_BoxEventIndex[ID * 2] = eventsStart;
192 m_BoxEventIndex[ID * 2 + 1] = nEvents;
193 if (setFileBacked)
194 mdBox->setFileBacked(eventsStart, nEvents, false);
195
196 eventsStart += nEvents;
197 }
198}
199
200void MDBoxFlatTree::saveBoxStructure(const std::string &fileName) {
201 m_FileName = fileName;
202 bool old_group;
203 auto hFile = file_holder_type(createOrOpenMDWSgroup(fileName, m_nDim, m_Boxes[0]->getEventType(), false, old_group));
204
205 // Save box structure;
206 this->saveBoxStructure(hFile.get());
207 // close workspace group
208 hFile->closeGroup();
209 // close file
210 hFile->close();
211}
212
213void MDBoxFlatTree::saveBoxStructure(Mantid::Nexus::File *hFile) {
214 size_t maxBoxes = this->getNBoxes();
215 if (maxBoxes == 0)
216 return;
217
218 std::map<std::string, std::string> groupEntries;
219 hFile->getEntries(groupEntries);
220
221 bool create(false);
222 if (groupEntries.find("box_structure") == groupEntries.end()) // dimensions dataset exist
223 create = true;
224
225 // Start the box data group
226 if (create) {
227 hFile->makeGroup("box_structure", "NXdata", true);
228 hFile->putAttr("version", "1.0");
229 // Add box controller info to this group
230 hFile->putAttr("box_controller_xml", m_bcXMLDescr);
231
232 } else {
233 hFile->openGroup("box_structure", "NXdata");
234 // update box controller information
235 hFile->putAttr("box_controller_xml", m_bcXMLDescr);
236 }
237
238 Nexus::DimVector exents_dims(2, 0);
239 exents_dims[0] = (Nexus::dimsize_t(maxBoxes));
240 exents_dims[1] = (m_nDim * 2);
241 Nexus::DimVector exents_chunk(2, 0);
242 exents_chunk[0] = Nexus::dimsize_t(16384);
243 exents_chunk[1] = (m_nDim * 2);
244
245 Nexus::DimVector box_2_dims(2, 0);
246 box_2_dims[0] = Nexus::dimsize_t(maxBoxes);
247 box_2_dims[1] = (2);
248 Nexus::DimVector box_2_chunk(2, 0);
249 box_2_chunk[0] = Nexus::dimsize_t(16384);
250 box_2_chunk[1] = (2);
251
252 if (create) {
253 // Write it for the first time
254 hFile->writeExtendibleData("box_type", m_BoxType);
255 hFile->writeExtendibleData("depth", m_Depth);
256 hFile->writeExtendibleData("inverse_volume", m_InverseVolume);
257 hFile->writeExtendibleData("extents", m_Extents, exents_dims, exents_chunk);
258 hFile->writeExtendibleData("box_children", m_BoxChildren, box_2_dims, box_2_chunk);
259 hFile->writeExtendibleData("box_signal_errorsquared", m_BoxSignalErrorsquared, box_2_dims, box_2_chunk);
260 hFile->writeExtendibleData("box_event_index", m_BoxEventIndex, box_2_dims, box_2_chunk);
261 } else {
262 // Update the expendable data sets
263 hFile->writeUpdatedData("box_type", m_BoxType);
264 hFile->writeUpdatedData("depth", m_Depth);
265 hFile->writeUpdatedData("inverse_volume", m_InverseVolume);
266 hFile->writeUpdatedData("extents", m_Extents, exents_dims);
267 hFile->writeUpdatedData("box_children", m_BoxChildren, box_2_dims);
268 hFile->writeUpdatedData("box_signal_errorsquared", m_BoxSignalErrorsquared, box_2_dims);
269 hFile->writeUpdatedData("box_event_index", m_BoxEventIndex, box_2_dims);
270 }
271 // close the box group.
272 hFile->closeGroup();
273}
274
289void MDBoxFlatTree::loadBoxStructure(const std::string &fileName, int &nDim, const std::string &EventType,
290 bool onlyEventInfo, bool restoreExperimentInfo) {
291
292 m_FileName = fileName;
293 m_nDim = nDim;
295
296 bool old_group;
297 // open the file and the MD workspace group.
298 auto hFile = file_holder_type(createOrOpenMDWSgroup(fileName, nDim, m_eventType, true, old_group));
299 if (!old_group)
300 throw Kernel::Exception::FileError("MD workspace box structure data are not present in the file", fileName);
301
302 m_nDim = nDim;
303
304 this->loadBoxStructure(hFile.get(), onlyEventInfo);
305
306 if (restoreExperimentInfo) {
307 if (!m_mEI)
308 m_mEI = std::make_shared<Mantid::API::MultipleExperimentInfos>(Mantid::API::MultipleExperimentInfos());
309
310 loadExperimentInfos(hFile.get(), fileName, m_mEI);
311 }
312
313 // close workspace group
314 hFile->closeGroup();
315
316 // close the NeXus file
317 hFile->close();
318}
319void MDBoxFlatTree::loadBoxStructure(Mantid::Nexus::File *hFile, bool onlyEventInfo) {
320 // ----------------------------------------- Box Structure
321 // ------------------------------
322 hFile->openGroup("box_structure", "NXdata");
323
324 if (onlyEventInfo) {
325 // Load the box controller description
326 hFile->getAttr("box_controller_xml", m_bcXMLDescr);
327 hFile->readData("box_type", m_BoxType);
328 hFile->readData("box_event_index", m_BoxEventIndex);
329 return;
330 }
331 // Load the box controller description
332 hFile->getAttr("box_controller_xml", m_bcXMLDescr);
333
334 // Read all the data blocks
335 hFile->readData("box_type", m_BoxType);
336 size_t numBoxes = m_BoxType.size();
337 if (numBoxes == 0)
338 throw std::runtime_error("Zero boxes found. There must have been an error "
339 "reading or writing the file.");
340
341 hFile->readData("depth", m_Depth);
342 hFile->readData("inverse_volume", m_InverseVolume);
343 hFile->readData("extents", m_Extents);
344
345 m_nDim = int(m_Extents.size() / (numBoxes * 2));
346 hFile->readData("box_children", m_BoxChildren);
347 hFile->readData("box_signal_errorsquared", m_BoxSignalErrorsquared);
348 hFile->readData("box_event_index", m_BoxEventIndex);
349
350 // Check all vector lengths match
351 if (m_Depth.size() != numBoxes)
352 throw std::runtime_error("Incompatible size for data: depth.");
353 if (m_InverseVolume.size() != numBoxes)
354 throw std::runtime_error("Incompatible size for data: inverse_volume.");
355 // if (boxType.size() != numBoxes) throw std::runtime_error("Incompatible size
356 // for data: boxType.");
357 // if (m_Extents.size() != numBoxes*m_nDim*2) throw
358 // std::runtime_error("Incompatible size for data: extents.");
359 if (m_BoxChildren.size() != numBoxes * 2)
360 throw std::runtime_error("Incompatible size for data: box_children.");
361 if (m_BoxEventIndex.size() != numBoxes * 2)
362 throw std::runtime_error("Incompatible size for data: box_event_index.");
363 if (m_BoxSignalErrorsquared.size() != numBoxes * 2)
364 throw std::runtime_error("Incompatible size for data: box_signal_errorsquared.");
365
366 hFile->closeGroup();
367}
368
375void MDBoxFlatTree::saveExperimentInfos(Mantid::Nexus::File *const file, const API::IMDEventWorkspace_const_sptr &ws) {
376
377 std::map<std::string, std::string> entries;
378 file->getEntries(entries);
379 for (uint16_t i = 0; i < ws->getNumExperimentInfo(); i++) {
380 API::ExperimentInfo_const_sptr ei = ws->getExperimentInfo(i);
381 std::string groupName = "experiment" + Kernel::Strings::toString(i);
382 if (entries.find(groupName) == entries.end()) {
383 // Can't overwrite entries. Just add the new ones
384 file->makeGroup(groupName, "NXgroup", true);
385 file->putAttr("version", 1);
386 ei->saveExperimentInfoNexus(file);
387 file->closeGroup();
388
389 // Warning for high detector IDs.
390 // The routine in MDEvent::saveVectorToNexusSlab() converts detector IDs
391 // to single-precision floats
392 // Floats only have 24 bits of int precision = 16777216 as the max,
393 // precise detector ID
394 detid_t min = 0;
395 detid_t max = 0;
396 try {
397 ei->getInstrument()->getMinMaxDetectorIDs(min, max);
398 } catch (std::runtime_error &) { /* Ignore error. Min/max will be 0 */
399 }
400
401 if (max > 16777216) {
402 g_log.warning() << "This instrument (" << ei->getInstrument()->getName()
403 << ") has detector IDs that are higher than can be "
404 "saved in the .NXS file as single-precision floats."
405 << '\n';
406 g_log.warning() << "Detector IDs above 16777216 will not be precise. "
407 "Please contact the developers.\n";
408 }
409 }
410 }
411}
412
413void MDBoxFlatTree::loadExperimentInfos(Mantid::Nexus::File *const file, const std::string &filename,
414 std::shared_ptr<Mantid::API::MultipleExperimentInfos> mei,
415 const std::string &currentGroup, bool lazy) {
416
417 // First, find how many experimentX blocks there are
418 std::set<std::string> nxGroupEntries = file->getEntriesByClass("NXgroup");
419
420 std::list<uint16_t> experimentBlockNum;
421 for (const std::string &entry : nxGroupEntries) {
422 if (std::count(entry.begin(), entry.end(), '/') != 2) {
423 continue;
424 }
425 if (!entry.starts_with("/" + currentGroup + "/experiment")) {
426 continue;
427 }
428
429 const std::string name = entry.substr(entry.find("experiment"));
430 EmplaceExperimentBlockNum(name, experimentBlockNum);
431 }
432
433 experimentBlockNum.sort();
434 CheckExperimentBlockNum(experimentBlockNum);
435
436 // Now go through in order, loading and adding
437 auto itr = experimentBlockNum.begin();
438 for (; itr != experimentBlockNum.end(); itr++) {
439 std::string groupName = "experiment" + Kernel::Strings::toString(*itr);
440 if (lazy) {
441 auto ei = std::make_shared<API::FileBackedExperimentInfo>(filename, file->getAddress() + "/" + groupName);
442 // And add it to the mutliple experiment info.
443 mei->addExperimentInfo(ei);
444 } else {
445 auto ei = std::make_shared<API::ExperimentInfo>();
446 file->openGroup(groupName, "NXgroup");
447 std::string parameterStr;
448 try {
449 // Get the sample, logs, instrument
450 ei->loadExperimentInfoNexus(filename, file, parameterStr, file->getAddress());
451 // Now do the parameter map
452 if (parameterStr.empty()) {
453 ei->populateInstrumentParameters();
454 } else {
455 ei->readParameterMap(parameterStr);
456 }
457 // And add it to the mutliple experiment info.
458 mei->addExperimentInfo(ei);
459 } catch (std::exception &e) {
460 g_log.information("Error loading section '" + groupName + "' of nxs file.");
461 g_log.information(e.what());
462 }
463 file->closeGroup();
464 }
465 }
466}
467
468//----------------------------------------------------------------------------------------------
480void MDBoxFlatTree::loadExperimentInfos(Mantid::Nexus::File *const file, const std::string &filename,
481 const std::shared_ptr<Mantid::API::MultipleExperimentInfos> &mei, bool lazy) {
482 // First, find how many experimentX blocks there are
483 std::map<std::string, std::string> entries;
484 file->getEntries(entries);
485
486 std::list<uint16_t> experimentBlockNum;
487 for (auto &entry : entries) {
488 const std::string &name = entry.first;
489 EmplaceExperimentBlockNum(name, experimentBlockNum);
490 }
491
492 experimentBlockNum.sort();
493 CheckExperimentBlockNum(experimentBlockNum);
494 // Now go through in order, loading and adding
495 auto itr = experimentBlockNum.begin();
496 for (; itr != experimentBlockNum.end(); itr++) {
497 std::string groupName = "experiment" + Kernel::Strings::toString(*itr);
498 if (lazy) {
499 auto ei = std::make_shared<API::FileBackedExperimentInfo>(filename, file->getAddress() + "/" + groupName);
500 // And add it to the mutliple experiment info.
501 mei->addExperimentInfo(ei);
502 } else {
503 auto ei = std::make_shared<API::ExperimentInfo>();
504 file->openGroup(groupName, "NXgroup");
505 std::string parameterStr;
506 try {
507 // Get the sample, logs, instrument
508 ei->loadExperimentInfoNexus(filename, file, parameterStr);
509 // Now do the parameter map
510 if (parameterStr.empty()) {
511 ei->populateInstrumentParameters();
512 } else {
513 ei->readParameterMap(parameterStr);
514 }
515 // And add it to the mutliple experiment info.
516 mei->addExperimentInfo(ei);
517 } catch (std::exception &e) {
518 g_log.information("Error loading section '" + groupName + "' of nxs file.");
519 g_log.information(e.what());
520 }
521 file->closeGroup();
522 }
523 }
524}
528 // copy experiment infos
529 targetWS->copyExperimentInfos(*m_mEI);
530 // free this Experiment info as it has been already exported
531 m_mEI.reset();
532}
533
551uint64_t MDBoxFlatTree::restoreBoxTree(std::vector<API::IMDNode *> &Boxes, API::BoxController_sptr &bc,
552 bool FileBackEnd, bool BoxStructureOnly) {
553
554 size_t numBoxes = this->getNBoxes();
555 Boxes.assign(numBoxes, nullptr);
556
557 uint64_t totalNumEvents(0);
558 m_nDim = static_cast<int>(bc->getNDims());
559 auto maxNdim = int(MDEventFactory::getMaxNumDim());
560 if (m_nDim <= 0 || m_nDim > maxNdim)
561 throw std::runtime_error("Workspace dimesnions are not defined properly in the box controller");
562
563 int iEventType(0);
564 if (m_eventType == "MDLeanEvent")
565 iEventType = 0;
566 else if (m_eventType == "MDEvent")
567 iEventType = 2;
568 else
569 throw std::invalid_argument(" Unknown event type provided for MDBoxFlatTree::restoreBoxTree");
570
571 for (size_t i = 0; i < numBoxes; i++) {
572
573 size_t box_type = m_BoxType[i];
574 if (box_type == 0)
575 continue;
576
577 API::IMDNode *ibox = nullptr;
578
579 // Extents of the box, as a vector
580 std::vector<Mantid::Geometry::MDDimensionExtents<coord_t>> extentsVector(m_nDim);
581 for (size_t d = 0; d < size_t(m_nDim); d++)
582 extentsVector[d].setExtents(static_cast<double>(m_Extents[i * m_nDim * 2 + d * 2]),
583 static_cast<double>(m_Extents[i * m_nDim * 2 + d * 2 + 1]));
584
585 // retrieve initial and file location and the numner of the events which
586 // belong to this box stored on the HDD
587 uint64_t indexStart = m_BoxEventIndex[i * 2];
588 uint64_t numEvents = m_BoxEventIndex[i * 2 + 1];
589
590 totalNumEvents += numEvents;
591 if (box_type == 1) {
592 // --- Make a MDBox -----
593 if (BoxStructureOnly) { // create box with undefined numer of events --
594 // differs from 0 number of events by not calling
595 // reserve(0) on underlying vectors.
596 ibox = MDEventFactory::createBox(size_t(m_nDim), MDEventFactory::BoxType(iEventType), bc, extentsVector,
597 m_Depth[i]);
598 } else // !BoxStructureOnly)
599 {
600
601 if (FileBackEnd) {
602 ibox = MDEventFactory::createBox(size_t(m_nDim), MDEventFactory::BoxType(iEventType), bc, extentsVector,
603 m_Depth[i]);
604 // Mark the box as file backed and indicate that the box was saved
605 ibox->setFileBacked(indexStart, numEvents, true);
606 } else {
607 ibox = MDEventFactory::createBox(size_t(m_nDim), MDEventFactory::BoxType(iEventType), bc, extentsVector,
608 m_Depth[i], numEvents);
609 }
610 } // ifBoxStructureOnly
611 } else if (box_type == 2) {
612 // --- Make a MDGridBox -----
613 ibox = MDEventFactory::createBox(size_t(m_nDim), MDEventFactory::BoxType(iEventType + 1), bc, extentsVector,
614 m_Depth[i]);
615 } else
616 continue;
617 // Force correct ID
618 ibox->setID(i);
619 // calculate volume from extents;
620 ibox->calcVolume();
621 double vol = m_InverseVolume[i];
622 if (vol <= FLT_EPSILON)
623 vol = 1;
624 if (std::fabs((ibox->getInverseVolume() - vol) / vol) > 1.e-5) {
625 g_log.debug() << " Accuracy warning for box N " << i << " as stored inverse volume is : " << m_InverseVolume[i]
626 << " and calculated from extents: " << ibox->getInverseVolume() << '\n';
628 }
629
630 // Set the cached values
633
634 // Save the box at its index in the vector.
635 Boxes[i] = ibox;
636
637 } // end Box loop
638
639 // Go again, giving the children to the parents
640 for (size_t i = 0; i < numBoxes; i++) {
641 if (m_BoxType[i] == 2) {
642 size_t indexStart = m_BoxChildren[i * 2];
643 size_t indexEnd = m_BoxChildren[i * 2 + 1] + 1;
644 Boxes[i]->setChildren(Boxes, indexStart, indexEnd);
645 }
646 }
647 bc->setMaxId(numBoxes);
648 return totalNumEvents;
649}
666Mantid::Nexus::File *MDBoxFlatTree::createOrOpenMDWSgroup(const std::string &fileName, int &nDims,
667 const std::string &WSEventType, bool readOnly,
668 bool &alreadyExists) {
669 alreadyExists = false;
670 std::filesystem::path oldFile(fileName);
671 bool fileExists = std::filesystem::exists(oldFile);
672 if (!fileExists && readOnly)
673 throw Kernel::Exception::FileError("Attempt to open non-existing file in read-only mode", fileName);
674
675 NXaccess access(NXaccess::RDWR);
676 if (readOnly)
677 access = NXaccess::READ;
678
679 file_holder_type hFile;
680 try {
681 if (fileExists)
682 hFile = file_holder_type(new Mantid::Nexus::File(fileName, access));
683 else
684 hFile = file_holder_type(new Mantid::Nexus::File(fileName, NXaccess::CREATE5));
685 } catch (...) {
686 throw Kernel::Exception::FileError("Can not open NeXus file", fileName);
687 }
688
689 std::map<std::string, std::string> groupEntries;
690
691 hFile->getEntries(groupEntries);
692 if (groupEntries.find("MDEventWorkspace") != groupEntries.end()) // WS group exist
693 {
694 // Open and check ws group
695 // -------------------------------------------------------------------------------->>>
696 hFile->openGroup("MDEventWorkspace", "NXentry");
697 alreadyExists = true;
698
699 std::string eventType;
700 if (hFile->hasAttr("event_type")) {
701 hFile->getAttr("event_type", eventType);
702
703 if (eventType != WSEventType)
704 throw Kernel::Exception::FileError("Trying to open MDWorkspace nexus file with the events: " + eventType +
705 "\n different from workspace type: " + WSEventType,
706 fileName);
707 } else // it is possible that workspace group has been created by somebody
708 // else and there are no this kind of attribute attached to it.
709 {
710 if (readOnly)
711 throw Kernel::Exception::FileError("The NXdata group: MDEventWorkspace opened in read-only mode but \n"
712 " does not have necessary attribute describing the event type used",
713 fileName);
714 hFile->putAttr("event_type", WSEventType);
715 }
716 // check dimensions dataset
717 bool dimDatasetExist(false);
718 hFile->getEntries(groupEntries);
719 if (groupEntries.find("dimensions") != groupEntries.end()) // dimensions dataset exist
720 dimDatasetExist = true;
721
722 if (dimDatasetExist) {
723 int32_t nFileDims;
724 hFile->readData<int32_t>("dimensions", nFileDims);
725 if (nDims != 0) // check against dimensions provided
726 {
727 if (nFileDims != static_cast<int32_t>(nDims))
728 throw Kernel::Exception::FileError("The NXdata group: MDEventWorkspace initiated for different "
729 "number of dimensions then requested ",
730 fileName);
731 } else // read what is already there
732 {
733 nDims = static_cast<int>(nFileDims);
734 }
735 } else {
736 auto nFileDim = static_cast<int32_t>(nDims);
737 if (nFileDim <= 0)
738 throw std::invalid_argument("MDBoxFlatTree::createOrOpenMDWSgrou: "
739 "Invalid number of workspace dimensions "
740 "provided to save into file ");
741
742 // Write out # of dimensions
743 hFile->writeData("dimensions", nFileDim);
744 }
745 // END Open and check ws group
746 // --------------------------------------------------------------------------------<<<<
747 } else {
748 // create new WS group
749 // ------------------------------------------------------------------------------->>>>>
750 if (readOnly)
751 throw Kernel::Exception::FileError("The NXdata group: MDEventWorkspace "
752 "does not exist in the read-only file",
753 fileName);
754
755 try {
756 alreadyExists = false;
757 hFile->makeGroup("MDEventWorkspace", "NXentry", true);
758 hFile->putAttr("event_type", WSEventType);
759
760 auto nDim = int32_t(nDims);
761 // Write out # of dimensions
762 hFile->writeData("dimensions", nDim);
763 } catch (...) {
764 throw Kernel::Exception::FileError("Can not create new NXdata group: MDEventWorkspace", fileName);
765 }
766 // END create new WS group
767 // -------------------------------------------------------------------------------<<<
768 }
769 return hFile.release();
770}
771
774void MDBoxFlatTree::saveWSGenericInfo(Mantid::Nexus::File *const file, const API::IMDWorkspace_const_sptr &ws) {
775 // Write out the coordinate system
776 file->writeData("coordinate_system", static_cast<uint32_t>(ws->getSpecialCoordinateSystem()));
777
778 // Write out the Qconvention
779 // ki-kf for Inelastic convention; kf-ki for Crystallography convention
780 std::string m_QConvention = ws->getConvention();
781 file->putAttr("QConvention", m_QConvention);
782
783 // Write out the set display normalization
784 file->writeData("visual_normalization", static_cast<uint32_t>(ws->displayNormalization()));
785
786 // Write out the set display normalization carried for spawned histo
787 // workspaces.
788 file->writeData("visual_normalization_histo", static_cast<uint32_t>(ws->displayNormalizationHisto()));
789
790 // Save the algorithm history under "process"
791 ws->getHistory().saveNexus(file);
792
793 // Write out the affine matrices
794 saveAffineTransformMatricies(file, std::dynamic_pointer_cast<const API::IMDWorkspace>(ws));
795
796 // Save some info as attributes. (Note: need to use attributes, not data sets
797 // because those cannot be resized).
798 file->putAttr("definition", ws->id());
799 file->putAttr("title", ws->getTitle());
800 // Save each dimension, as their XML representation
801 size_t nDim = ws->getNumDims();
802 for (size_t d = 0; d < nDim; d++) {
803 std::ostringstream mess;
804 mess << "dimension" << d;
805 file->putAttr(mess.str(), ws->getDimension(d)->toXMLString());
806 }
807}
808
815void MDBoxFlatTree::saveAffineTransformMatricies(Mantid::Nexus::File *const file,
817 try {
818 saveAffineTransformMatrix(file, ws->getTransformToOriginal(), "transform_to_orig");
819 } catch (std::runtime_error &) {
820 // Do nothing
821 }
822 try {
823 saveAffineTransformMatrix(file, ws->getTransformFromOriginal(), "transform_from_orig");
824 } catch (std::runtime_error &) {
825 // Do nothing
826 }
827}
834void MDBoxFlatTree::saveAffineTransformMatrix(Mantid::Nexus::File *const file, API::CoordTransform const *transform,
835 const std::string &entry_name) {
836 if (!transform)
837 return;
838 Kernel::Matrix<coord_t> matrix = transform->makeAffineMatrix();
839 g_log.debug() << "TRFM: " << matrix.str() << '\n';
840 saveMatrix<coord_t>(file, entry_name, matrix, NXnumtype::FLOAT32, transform->id());
841}
842
851template <typename T>
852void saveMatrix(Mantid::Nexus::File *const file, const std::string &name, Kernel::Matrix<T> &m, NXnumtype type,
853 const std::string &tag) {
854 std::vector<T> v = m.getVector();
855 // Number of data points
856 auto nPoints = static_cast<int>(v.size());
857
858 file->makeData(name, type, nPoints, true);
859 // Need a pointer
860 file->putData(&v[0]);
861 if (!tag.empty()) {
862 file->putAttr("type", tag);
863 file->putAttr("rows", static_cast<int>(m.numRows()));
864 file->putAttr("columns", static_cast<int>(m.numCols()));
865 }
866 file->closeData();
867}
868} // namespace Mantid::DataObjects
std::string name
Definition Run.cpp:60
std::unique_ptr< Mantid::Nexus::File > file_holder_type
NXaccess
Nexus file access codes.
Unique SingleValueParameter Declaration for InputNDimensions.
virtual std::string id() const =0
virtual Mantid::Kernel::Matrix< coord_t > makeAffineMatrix() const
virtual void setSignal(const signal_t)=0
virtual coord_t getInverseVolume() const =0
virtual void calcVolume()=0
virtual void setErrorSquared(const signal_t)=0
virtual void setChildren(const std::vector< IMDNode * > &boxes, const size_t indexStart, const size_t indexEnd)=0
Sets the children from a vector of children.
virtual void setID(const size_t &newID)=0
sets the special id, which specify the position of this node in the chain linearly ordered nodes
virtual void setFileBacked(const uint64_t, const size_t, const bool)=0
initiate the structure responsible for swapping the box on HDD if out of memory.
static void sortObjByID(std::vector< IMDNode * > &boxes)
Static method for sorting a list of MDBoxBase pointers by their file position, ascending.
Definition IMDNode.h:292
virtual void setInverseVolume(const coord_t)=0
Small class that allows a MDEventWorkspace or a MDHistoWorkspace to hold several ExperimentInfo class...
uint64_t restoreBoxTree(std::vector< API::IMDNode * > &Boxes, API::BoxController_sptr &bc, bool FileBackEnd, bool BoxStructureOnly=false)
Method recovers the interconnected box structure from the plain tree into box tree,...
std::vector< int > m_BoxChildren
Start/end children IDs.
std::vector< uint64_t > m_BoxEventIndex
Start/end indices into the list of events; 2*i – filePosition, 2*i+1 number of events in the block.
std::vector< double > m_InverseVolume
Inverse of the volume of the cell.
std::string m_eventType
name of the event type
void exportExperiment(Mantid::API::IMDEventWorkspace_sptr &targetWS)
Export existing experiment info defined in the box structure to target workspace (or other experiment...
void loadBoxStructure(const std::string &fileName, int &nDim, const std::string &EventType, bool onlyEventInfo=false, bool restoreExperimentInfo=false)
load box structure from the file, defined by file name
std::vector< double > m_BoxSignalErrorsquared
Box cached signal/error squared.
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 saveAffineTransformMatrix(Mantid::Nexus::File *const file, API::CoordTransform const *transform, const std::string &entry_name)
Extract and save the requested affine matrix.
static void saveAffineTransformMatricies(Mantid::Nexus::File *const file, const API::IMDWorkspace_const_sptr &ws)
Save the affine matrices to both directional conversions to the data.
std::vector< API::IMDNode * > m_Boxes
linear vector of boxes;
std::vector< double > m_Extents
Min/Max extents in each dimension.
std::string m_bcXMLDescr
XML representation of the box controller.
std::vector< int > m_Depth
Recursion depth.
MDBoxFlatTree()
The constructor of the flat box tree
static void saveWSGenericInfo(Mantid::Nexus::File *const file, const API::IMDWorkspace_const_sptr &ws)
Save workspace generic info like dimension structure, history, title dimensions etc.
std::shared_ptr< API::MultipleExperimentInfos > m_mEI
shared pointer to multiple experiment info stored within the workspace
void saveBoxStructure(const std::string &fileName)
Save flat box structure into a file, defined by the file name.
void setBoxesFilePositions(bool setFileBacked)
static Mantid::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...
static void loadExperimentInfos(Mantid::Nexus::File *const file, const std::string &filename, std::shared_ptr< API::MultipleExperimentInfos > mei, const std::string &currentGroup, bool lazy=false)
std::vector< int > m_BoxType
Box type (0=None, 1=MDBox, 2=MDGridBox.
static void saveExperimentInfos(Mantid::Nexus::File *const file, const API::IMDEventWorkspace_const_sptr &ws)
Save each NEW ExperimentInfo to a spot in the file.
BoxType
enum defines fifferent box types generated by createBox factory We will use typecast from integer to ...
static size_t getMaxNumDim()
Returns max number of MD dimensions allowed by current Mantid version.
static API::IMDNode * createBox(size_t nDimensions, BoxType Type, API::BoxController_sptr &splitter, const std::vector< Mantid::Geometry::MDDimensionExtents< coord_t > > &extentsVector=std::vector< Mantid::Geometry::MDDimensionExtents< coord_t > >(), const uint32_t depth=0, const size_t nBoxEvents=UNDEF_SIZET, const size_t boxID=UNDEF_SIZET)
Create a MDBox or MDGridBoxof the given type.
Records the filename and the description of failure.
Definition Exception.h:98
An interface for objects that can be cached or saved to disk.
Definition ISaveable.h:28
virtual uint64_t getFilePosition() const
Definition ISaveable.h:36
void debug(const std::string &msg)
Logs at debug level.
Definition Logger.cpp:145
void warning(const std::string &msg)
Logs at warning level.
Definition Logger.cpp:117
void information(const std::string &msg)
Logs at information level.
Definition Logger.cpp:136
Numerical Matrix class.
Definition Matrix.h:42
std::string str() const
Convert the matrix into a simple linear string expression.
Definition Matrix.cpp:1562
The primitive types published by this API.
static unsigned short constexpr FLOAT32
std::shared_ptr< IMDEventWorkspace > IMDEventWorkspace_sptr
Shared pointer to Mantid::API::IMDEventWorkspace.
std::shared_ptr< const IMDEventWorkspace > IMDEventWorkspace_const_sptr
Shared pointer to Mantid::API::IMDEventWorkspace (const version)
std::shared_ptr< const ExperimentInfo > ExperimentInfo_const_sptr
Shared pointer to const ExperimentInfo.
Kernel::Logger g_log("ExperimentInfo")
static logger object
std::shared_ptr< const IMDWorkspace > IMDWorkspace_const_sptr
Shared pointer to the IMDWorkspace base class (const version)
std::shared_ptr< BoxController > BoxController_sptr
Shared ptr to BoxController.
EventType
What kind of event list is being stored.
Definition IEventList.h:18
std::size_t numEvents(Nexus::File &file, bool &hasTotalCounts, bool &oldNeXusFileNames, const std::string &prefix)
Get the number of events in the currently opened group.
void saveMatrix(Mantid::Nexus::File *const file, const std::string &name, Kernel::Matrix< T > &m, NXnumtype type, const std::string &tag)
Save routine for a generic matrix.
std::unique_ptr< T > create(const P &parent, const IndexArg &indexArg, const HistArg &histArg)
This is the create() method that all the other create() methods call.
std::string toString(const T &value)
Convert a number to a string.
Definition Strings.cpp:734
std::vector< dimsize_t > DimVector
float coord_t
Typedef for the data type to use for coordinate axes in MD objects such as MDBox, MDEventWorkspace,...
Definition MDTypes.h:27
int32_t detid_t
Typedef for a detector ID.