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#include <Poco/File.h>
16
17#include <algorithm>
18#include <utility>
19
20using file_holder_type = std::unique_ptr<::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 (boost::starts_with(name, "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(::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 std::vector<int64_t> exents_dims(2, 0);
239 exents_dims[0] = (int64_t(maxBoxes));
240 exents_dims[1] = (m_nDim * 2);
241 std::vector<int64_t> exents_chunk(2, 0);
242 exents_chunk[0] = int64_t(16384);
243 exents_chunk[1] = (m_nDim * 2);
244
245 std::vector<int64_t> box_2_dims(2, 0);
246 box_2_dims[0] = int64_t(maxBoxes);
247 box_2_dims[1] = (2);
248 std::vector<int64_t> box_2_chunk(2, 0);
249 box_2_chunk[0] = int64_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(::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
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(::NeXus::File *const file, const std::string &filename,
414 std::shared_ptr<Mantid::API::MultipleExperimentInfos> mei,
416 const std::string &currentGroup, bool lazy) {
417
418 // First, find how many experimentX blocks there are
419 const auto &allEntries = fileInfo.getAllEntries();
420 auto itNXgroup = allEntries.find("NXgroup");
421 const std::set<std::string> &nxGroupEntries =
422 (itNXgroup != allEntries.end()) ? itNXgroup->second : std::set<std::string>{};
423
424 std::list<uint16_t> experimentBlockNum;
425 for (const std::string &entry : nxGroupEntries) {
426 if (std::count(entry.begin(), entry.end(), '/') != 2) {
427 continue;
428 }
429 if (!boost::starts_with(entry, "/" + currentGroup + "/experiment")) {
430 continue;
431 }
432
433 const std::string name = entry.substr(entry.find("experiment"));
434 EmplaceExperimentBlockNum(name, experimentBlockNum);
435 }
436
437 experimentBlockNum.sort();
438 CheckExperimentBlockNum(experimentBlockNum);
439
440 // Now go through in order, loading and adding
441 auto itr = experimentBlockNum.begin();
442 for (; itr != experimentBlockNum.end(); itr++) {
443 std::string groupName = "experiment" + Kernel::Strings::toString(*itr);
444 if (lazy) {
445 auto ei = std::make_shared<API::FileBackedExperimentInfo>(filename, file->getPath() + "/" + groupName);
446 // And add it to the mutliple experiment info.
447 mei->addExperimentInfo(ei);
448 } else {
449 auto ei = std::make_shared<API::ExperimentInfo>();
450 file->openGroup(groupName, "NXgroup");
451 std::string parameterStr;
452 try {
453 // Get the sample, logs, instrument
454 ei->loadExperimentInfoNexus(filename, file, parameterStr, fileInfo, file->getPath());
455 // Now do the parameter map
456 if (parameterStr.empty()) {
457 ei->populateInstrumentParameters();
458 } else {
459 ei->readParameterMap(parameterStr);
460 }
461 // And add it to the mutliple experiment info.
462 mei->addExperimentInfo(ei);
463 } catch (std::exception &e) {
464 g_log.information("Error loading section '" + groupName + "' of nxs file.");
465 g_log.information(e.what());
466 }
467 file->closeGroup();
468 }
469 }
470}
471
472//----------------------------------------------------------------------------------------------
484void MDBoxFlatTree::loadExperimentInfos(::NeXus::File *const file, const std::string &filename,
485 const std::shared_ptr<Mantid::API::MultipleExperimentInfos> &mei, bool lazy) {
486 // First, find how many experimentX blocks there are
487 std::map<std::string, std::string> entries;
488 file->getEntries(entries);
489
490 std::list<uint16_t> experimentBlockNum;
491 for (auto &entry : entries) {
492 const std::string &name = entry.first;
493 EmplaceExperimentBlockNum(name, experimentBlockNum);
494 }
495
496 experimentBlockNum.sort();
497 CheckExperimentBlockNum(experimentBlockNum);
498 // Now go through in order, loading and adding
499 auto itr = experimentBlockNum.begin();
500 for (; itr != experimentBlockNum.end(); itr++) {
501 std::string groupName = "experiment" + Kernel::Strings::toString(*itr);
502 if (lazy) {
503 auto ei = std::make_shared<API::FileBackedExperimentInfo>(filename, file->getPath() + "/" + groupName);
504 // And add it to the mutliple experiment info.
505 mei->addExperimentInfo(ei);
506 } else {
507 auto ei = std::make_shared<API::ExperimentInfo>();
508 file->openGroup(groupName, "NXgroup");
509 std::string parameterStr;
510 try {
511 // Get the sample, logs, instrument
512 ei->loadExperimentInfoNexus(filename, file, parameterStr);
513 // Now do the parameter map
514 if (parameterStr.empty()) {
515 ei->populateInstrumentParameters();
516 } else {
517 ei->readParameterMap(parameterStr);
518 }
519 // And add it to the mutliple experiment info.
520 mei->addExperimentInfo(ei);
521 } catch (std::exception &e) {
522 g_log.information("Error loading section '" + groupName + "' of nxs file.");
523 g_log.information(e.what());
524 }
525 file->closeGroup();
526 }
527 }
528}
532 // copy experiment infos
533 targetWS->copyExperimentInfos(*m_mEI);
534 // free this Experiment info as it has been already exported
535 m_mEI.reset();
536}
537
555uint64_t MDBoxFlatTree::restoreBoxTree(std::vector<API::IMDNode *> &Boxes, API::BoxController_sptr &bc,
556 bool FileBackEnd, bool BoxStructureOnly) {
557
558 size_t numBoxes = this->getNBoxes();
559 Boxes.assign(numBoxes, nullptr);
560
561 uint64_t totalNumEvents(0);
562 m_nDim = static_cast<int>(bc->getNDims());
563 auto maxNdim = int(MDEventFactory::getMaxNumDim());
564 if (m_nDim <= 0 || m_nDim > maxNdim)
565 throw std::runtime_error("Workspace dimesnions are not defined properly in the box controller");
566
567 int iEventType(0);
568 if (m_eventType == "MDLeanEvent")
569 iEventType = 0;
570 else if (m_eventType == "MDEvent")
571 iEventType = 2;
572 else
573 throw std::invalid_argument(" Unknown event type provided for MDBoxFlatTree::restoreBoxTree");
574
575 for (size_t i = 0; i < numBoxes; i++) {
576
577 size_t box_type = m_BoxType[i];
578 if (box_type == 0)
579 continue;
580
581 API::IMDNode *ibox = nullptr;
582
583 // Extents of the box, as a vector
584 std::vector<Mantid::Geometry::MDDimensionExtents<coord_t>> extentsVector(m_nDim);
585 for (size_t d = 0; d < size_t(m_nDim); d++)
586 extentsVector[d].setExtents(static_cast<double>(m_Extents[i * m_nDim * 2 + d * 2]),
587 static_cast<double>(m_Extents[i * m_nDim * 2 + d * 2 + 1]));
588
589 // retrieve initial and file location and the numner of the events which
590 // belong to this box stored on the HDD
591 uint64_t indexStart = m_BoxEventIndex[i * 2];
592 uint64_t numEvents = m_BoxEventIndex[i * 2 + 1];
593
594 totalNumEvents += numEvents;
595 if (box_type == 1) {
596 // --- Make a MDBox -----
597 if (BoxStructureOnly) { // create box with undefined numer of events --
598 // differs from 0 number of events by not calling
599 // reserve(0) on underlying vectors.
600 ibox = MDEventFactory::createBox(size_t(m_nDim), MDEventFactory::BoxType(iEventType), bc, extentsVector,
601 m_Depth[i]);
602 } else // !BoxStructureOnly)
603 {
604
605 if (FileBackEnd) {
606 ibox = MDEventFactory::createBox(size_t(m_nDim), MDEventFactory::BoxType(iEventType), bc, extentsVector,
607 m_Depth[i]);
608 // Mark the box as file backed and indicate that the box was saved
609 ibox->setFileBacked(indexStart, numEvents, true);
610 } else {
611 ibox = MDEventFactory::createBox(size_t(m_nDim), MDEventFactory::BoxType(iEventType), bc, extentsVector,
612 m_Depth[i], numEvents);
613 }
614 } // ifBoxStructureOnly
615 } else if (box_type == 2) {
616 // --- Make a MDGridBox -----
617 ibox = MDEventFactory::createBox(size_t(m_nDim), MDEventFactory::BoxType(iEventType + 1), bc, extentsVector,
618 m_Depth[i]);
619 } else
620 continue;
621 // Force correct ID
622 ibox->setID(i);
623 // calculate volume from extents;
624 ibox->calcVolume();
625 double vol = m_InverseVolume[i];
626 if (vol <= FLT_EPSILON)
627 vol = 1;
628 if (std::fabs((ibox->getInverseVolume() - vol) / vol) > 1.e-5) {
629 g_log.debug() << " Accuracy warning for box N " << i << " as stored inverse volume is : " << m_InverseVolume[i]
630 << " and calculated from extents: " << ibox->getInverseVolume() << '\n';
632 }
633
634 // Set the cached values
637
638 // Save the box at its index in the vector.
639 Boxes[i] = ibox;
640
641 } // end Box loop
642
643 // Go again, giving the children to the parents
644 for (size_t i = 0; i < numBoxes; i++) {
645 if (m_BoxType[i] == 2) {
646 size_t indexStart = m_BoxChildren[i * 2];
647 size_t indexEnd = m_BoxChildren[i * 2 + 1] + 1;
648 Boxes[i]->setChildren(Boxes, indexStart, indexEnd);
649 }
650 }
651 bc->setMaxId(numBoxes);
652 return totalNumEvents;
653}
670::NeXus::File *MDBoxFlatTree::createOrOpenMDWSgroup(const std::string &fileName, int &nDims,
671 const std::string &WSEventType, bool readOnly,
672 bool &alreadyExists) {
673 alreadyExists = false;
674 Poco::File oldFile(fileName);
675 bool fileExists = oldFile.exists();
676 if (!fileExists && readOnly)
677 throw Kernel::Exception::FileError("Attempt to open non-existing file in read-only mode", fileName);
678
679 NXaccess access(NXACC_RDWR);
680 if (readOnly)
681 access = NXACC_READ;
682
683 file_holder_type hFile;
684 try {
685 if (fileExists)
686 hFile = file_holder_type(new ::NeXus::File(fileName, access));
687 else
688 hFile = file_holder_type(new ::NeXus::File(fileName, NXACC_CREATE5));
689 } catch (...) {
690 throw Kernel::Exception::FileError("Can not open NeXus file", fileName);
691 }
692
693 std::map<std::string, std::string> groupEntries;
694
695 hFile->getEntries(groupEntries);
696 if (groupEntries.find("MDEventWorkspace") != groupEntries.end()) // WS group exist
697 {
698 // Open and check ws group
699 // -------------------------------------------------------------------------------->>>
700 hFile->openGroup("MDEventWorkspace", "NXentry");
701 alreadyExists = true;
702
703 std::string eventType;
704 if (hFile->hasAttr("event_type")) {
705 hFile->getAttr("event_type", eventType);
706
707 if (eventType != WSEventType)
708 throw Kernel::Exception::FileError("Trying to open MDWorkspace nexus file with the the events: " + eventType +
709 "\n different from workspace type: " + WSEventType,
710 fileName);
711 } else // it is possible that workspace group has been created by somebody
712 // else and there are no this kind of attribute attached to it.
713 {
714 if (readOnly)
715 throw Kernel::Exception::FileError("The NXdata group: MDEventWorkspace opened in read-only mode but \n"
716 " does not have necessary attribute describing the event type used",
717 fileName);
718 hFile->putAttr("event_type", WSEventType);
719 }
720 // check dimensions dataset
721 bool dimDatasetExist(false);
722 hFile->getEntries(groupEntries);
723 if (groupEntries.find("dimensions") != groupEntries.end()) // dimensions dataset exist
724 dimDatasetExist = true;
725
726 if (dimDatasetExist) {
727 int32_t nFileDims;
728 hFile->readData<int32_t>("dimensions", nFileDims);
729 if (nDims != 0) // check against dimensions provided
730 {
731 if (nFileDims != static_cast<int32_t>(nDims))
732 throw Kernel::Exception::FileError("The NXdata group: MDEventWorkspace initiated for different "
733 "number of dimensions then requested ",
734 fileName);
735 } else // read what is already there
736 {
737 nDims = static_cast<int>(nFileDims);
738 }
739 } else {
740 auto nFileDim = static_cast<int32_t>(nDims);
741 if (nFileDim <= 0)
742 throw std::invalid_argument("MDBoxFlatTree::createOrOpenMDWSgrou: "
743 "Invalid number of workspace dimensions "
744 "provided to save into file ");
745
746 // Write out # of dimensions
747 hFile->writeData("dimensions", nFileDim);
748 }
749 // END Open and check ws group
750 // --------------------------------------------------------------------------------<<<<
751 } else {
752 // create new WS group
753 // ------------------------------------------------------------------------------->>>>>
754 if (readOnly)
755 throw Kernel::Exception::FileError("The NXdata group: MDEventWorkspace "
756 "does not exist in the read-only file",
757 fileName);
758
759 try {
760 alreadyExists = false;
761 hFile->makeGroup("MDEventWorkspace", "NXentry", true);
762 hFile->putAttr("event_type", WSEventType);
763
764 auto nDim = int32_t(nDims);
765 // Write out # of dimensions
766 hFile->writeData("dimensions", nDim);
767 } catch (...) {
768 throw Kernel::Exception::FileError("Can not create new NXdata group: MDEventWorkspace", fileName);
769 }
770 // END create new WS group
771 // -------------------------------------------------------------------------------<<<
772 }
773 return hFile.release();
774}
775
778void MDBoxFlatTree::saveWSGenericInfo(::NeXus::File *const file, const API::IMDWorkspace_const_sptr &ws) {
779 // Write out the coordinate system
780 file->writeData("coordinate_system", static_cast<uint32_t>(ws->getSpecialCoordinateSystem()));
781
782 // Write out the Qconvention
783 // ki-kf for Inelastic convention; kf-ki for Crystallography convention
784 std::string m_QConvention = ws->getConvention();
785 file->putAttr("QConvention", m_QConvention);
786
787 // Write out the set display normalization
788 file->writeData("visual_normalization", static_cast<uint32_t>(ws->displayNormalization()));
789
790 // Write out the set display normalization carried for spawned histo
791 // workspaces.
792 file->writeData("visual_normalization_histo", static_cast<uint32_t>(ws->displayNormalizationHisto()));
793
794 // Save the algorithm history under "process"
795 ws->getHistory().saveNexus(file);
796
797 // Write out the affine matrices
798 saveAffineTransformMatricies(file, std::dynamic_pointer_cast<const API::IMDWorkspace>(ws));
799
800 // Save some info as attributes. (Note: need to use attributes, not data sets
801 // because those cannot be resized).
802 file->putAttr("definition", ws->id());
803 file->putAttr("title", ws->getTitle());
804 // Save each dimension, as their XML representation
805 size_t nDim = ws->getNumDims();
806 for (size_t d = 0; d < nDim; d++) {
807 std::ostringstream mess;
808 mess << "dimension" << d;
809 file->putAttr(mess.str(), ws->getDimension(d)->toXMLString());
810 }
811}
812
820 try {
821 saveAffineTransformMatrix(file, ws->getTransformToOriginal(), "transform_to_orig");
822 } catch (std::runtime_error &) {
823 // Do nothing
824 }
825 try {
826 saveAffineTransformMatrix(file, ws->getTransformFromOriginal(), "transform_from_orig");
827 } catch (std::runtime_error &) {
828 // Do nothing
829 }
830}
837void MDBoxFlatTree::saveAffineTransformMatrix(::NeXus::File *const file, API::CoordTransform const *transform,
838 const std::string &entry_name) {
839 if (!transform)
840 return;
841 Kernel::Matrix<coord_t> matrix = transform->makeAffineMatrix();
842 g_log.debug() << "TRFM: " << matrix.str() << '\n';
843 saveMatrix<coord_t>(file, entry_name, matrix, ::NeXus::FLOAT32, transform->id());
844}
845
854template <typename T>
855void saveMatrix(::NeXus::File *const file, const std::string &name, Kernel::Matrix<T> &m, ::NeXus::NXnumtype type,
856 const std::string &tag) {
857 std::vector<T> v = m.getVector();
858 // Number of data points
859 auto nPoints = static_cast<int>(v.size());
860
861 file->makeData(name, type, nPoints, true);
862 // Need a pointer
863 file->putData(&v[0]);
864 if (!tag.empty()) {
865 file->putAttr("type", tag);
866 file->putAttr("rows", static_cast<int>(m.numRows()));
867 file->putAttr("columns", static_cast<int>(m.numCols()));
868 }
869 file->closeData();
870}
871} // namespace Mantid::DataObjects
std::unique_ptr<::NeXus::File > file_holder_type
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 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.
Definition: MDBoxFlatTree.h:99
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.
Definition: MDBoxFlatTree.h:91
std::vector< double > m_InverseVolume
Inverse of the volume of the cell.
Definition: MDBoxFlatTree.h:95
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.
Definition: MDBoxFlatTree.h:97
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< API::IMDNode * > m_Boxes
linear vector of boxes;
static void saveAffineTransformMatricies(::NeXus::File *const file, const API::IMDWorkspace_const_sptr &ws)
Save the affine matrices to both directional conversions to the data.
std::vector< double > m_Extents
Min/Max extents in each dimension.
Definition: MDBoxFlatTree.h:93
std::string m_bcXMLDescr
XML representation of the box controller.
std::vector< int > m_Depth
Recursion depth.
Definition: MDBoxFlatTree.h:88
MDBoxFlatTree()
The constructor of the flat box tree
std::shared_ptr< API::MultipleExperimentInfos > m_mEI
shared pointer to multiple experiment info stored within the workspace
static void saveExperimentInfos(::NeXus::File *const file, const API::IMDEventWorkspace_const_sptr &ws)
Save each NEW ExperimentInfo to a spot in the file.
static void loadExperimentInfos(::NeXus::File *const file, const std::string &filename, std::shared_ptr< API::MultipleExperimentInfos > mei, const Mantid::Kernel::NexusHDF5Descriptor &fileInfo, const std::string &currentGroup, bool lazy=false)
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)
static void saveAffineTransformMatrix(::NeXus::File *const file, API::CoordTransform const *transform, const std::string &entry_name)
Extract and save the requested affine matrix.
std::vector< int > m_BoxType
Box type (0=None, 1=MDBox, 2=MDGridBox.
Definition: MDBoxFlatTree.h:86
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:114
void warning(const std::string &msg)
Logs at warning level.
Definition: Logger.cpp:86
void information(const std::string &msg)
Logs at information level.
Definition: Logger.cpp:105
Numerical Matrix class.
Definition: Matrix.h:42
std::string str() const
Convert the matrix into a simple linear string expression.
Definition: Matrix.cpp:1564
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.
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)
Definition: IMDWorkspace.h:148
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, const NexusHDF5Descriptor &descriptor)
Get the number of events in the currently opened group.
void saveMatrix(::NeXus::File *const file, const std::string &name, Kernel::Matrix< T > &m, ::NeXus::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: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
int32_t detid_t
Typedef for a detector ID.
Definition: SpectrumInfo.h:21