/** * Load an NX log entry a group type that has value and time entries. * @param file :: A reference to the NeXus file handle opened at the parent * group * @param entry_name :: The name of the log entry * @param entry_class :: The type of the entry * @param workspace :: A pointer to the workspace to store the logs */ void LoadNexusLogs::loadNXLog( ::NeXus::File &file, const std::string &entry_name, const std::string &entry_class, boost::shared_ptr<API::MatrixWorkspace> workspace) const { g_log.debug() << "processing " << entry_name << ":" << entry_class << "\n"; file.openGroup(entry_name, entry_class); // Validate the NX log class. std::map<std::string, std::string> entries = file.getEntries(); if ((entries.find("value") == entries.end()) || (entries.find("time") == entries.end())) { g_log.warning() << "Invalid NXlog entry " << entry_name << " found. Did not contain 'value' and 'time'.\n"; file.closeGroup(); return; } // whether or not to overwrite logs on workspace bool overwritelogs = this->getProperty("OverwriteLogs"); try { if (overwritelogs || !(workspace->run().hasProperty(entry_name))) { Kernel::Property *logValue = createTimeSeries(file, entry_name); workspace->mutableRun().addProperty(logValue, overwritelogs); } } catch (::NeXus::Exception &e) { g_log.warning() << "NXlog entry " << entry_name << " gave an error when loading:'" << e.what() << "'.\n"; } file.closeGroup(); }
/** * Open the first NXentry of the supplied nexus file. * * @param handle Object to work on. */ void MuonNexusReader::openFirstNXentry(NeXus::File &handle) { std::map<string, string> entries = handle.getEntries(); const auto entry = std::find_if(entries.cbegin(), entries.cend(), [](const auto entry) { return entry.second == NXENTRY; }); if (entry == entries.cend()) throw std::runtime_error("Failed to find NXentry"); handle.openGroup(entry->first, NXENTRY); }
/** * Open the first NXentry of the supplied nexus file. * * @param handle Object to work on. */ void MuonNexusReader::openFirstNXentry(NeXus::File &handle) { std::map<string, string> entries = handle.getEntries(); bool found = false; for (auto &entrie : entries) { if (entrie.second == NXENTRY) { handle.openGroup(entrie.first, NXENTRY); found = true; break; } } if (!found) throw std::runtime_error("Failed to find NXentry"); }
/** * Load log entries from the given group * @param file :: A reference to the NeXus file handle opened such that the * next call can be to open the named group * @param entry_name :: The name of the log entry * @param entry_class :: The class type of the log entry * @param workspace :: A pointer to the workspace to store the logs */ void LoadNexusLogs::loadLogs( ::NeXus::File &file, const std::string &entry_name, const std::string &entry_class, boost::shared_ptr<API::MatrixWorkspace> workspace) const { file.openGroup(entry_name, entry_class); std::map<std::string, std::string> entries = file.getEntries(); std::map<std::string, std::string>::const_iterator iend = entries.end(); for (std::map<std::string, std::string>::const_iterator itr = entries.begin(); itr != iend; ++itr) { std::string log_class = itr->second; if (log_class == "NXlog" || log_class == "NXpositioner") { loadNXLog(file, itr->first, log_class, workspace); } else if (log_class == "IXseblock") { loadSELog(file, itr->first, workspace); } else if (log_class == "NXcollection") { int jj = 0; ++jj; } } loadVetoPulses(file, workspace); file.closeGroup(); }
/** Loads an entry from a previously-open NXS file as a log entry * in the workspace's run. * * @param file: NXS file handle. MUST BE PASSED BY REFERENCE otherwise there * occurs a segfault. * @param entry_name, entry_class: name and class of NXlog to open. */ void LoadLogsFromSNSNexus::loadSampleLog(::NeXus::File& file, std::string entry_name, std::string entry_class) { // whether or not to overwrite logs on workspace bool overwritelogs = this->getProperty("OverwriteLogs"); file.openGroup(entry_name, entry_class); // Validate the NX log class. map<string, string> entries = file.getEntries(); if ((entries.find("value") == entries.end()) || (entries.find("time") == entries.end()) ) { g_log.warning() << "Invalid NXlog entry " << entry_name << " found. Did not contain 'value' and 'time'.\n"; file.closeGroup(); return; } ::NeXus::Info info; //Two possible types of properties: vector<double> values; vector<int> values_int; bool isTimeSeries = false; bool isInt = false; file.openData("value"); //Get the units of the property std::string units(""); try { file.getAttr("units", units); } catch (::NeXus::Exception &) { //Ignore missing units field. units = ""; } //If there is more than one entry, it is a timeseries info = file.getInfo(); //isTimeSeries = (info.dims[0] > 1); isTimeSeries = true; Timer timer1; try { //Get the data (convert types if necessary) if (file.isDataInt()) { isInt = true; file.getDataCoerce(values_int); // if (values_int.size() == 1) // { // WS->mutableRun().addProperty(entry_name, values_int[0], units); // } } else { //Try to get as doubles. file.getDataCoerce(values); // if (values.size() == 1) // { // WS->mutableRun().addProperty(entry_name, values[0], units); // } } } catch (::NeXus::Exception &e) { g_log.warning() << "NXlog entry " << entry_name << " gave an error when loading 'value' data:'" << e.what() << "'.\n"; file.closeData(); file.closeGroup(); return; } if (VERBOSE) std::cout << "getDataCoerce took " << timer1.elapsed() << " sec.\n"; file.closeData(); if (isTimeSeries) { // --- Time series property --- //Get the times vector<double> time_double; vector<DateAndTime> times; try { file.openData("time"); } catch (::NeXus::Exception &e) { g_log.warning() << "NXlog entry " << entry_name << " gave an error when opening the time field '" << e.what() << "'.\n"; file.closeGroup(); return; } //----- Start time is an ISO8601 string date and time. ------ std::string start; try { file.getAttr("start", start); } catch (::NeXus::Exception &) { //Some logs have "offset" instead of start try { file.getAttr("offset", start); } catch (::NeXus::Exception &) { g_log.warning() << "NXlog entry " << entry_name << " has no start time indicated.\n"; file.closeData(); file.closeGroup(); return; } } //Convert to date and time Kernel::DateAndTime start_time = Kernel::DateAndTime(start); std::string time_units; file.getAttr("units", time_units); if (time_units != "second") { g_log.warning() << "NXlog entry " << entry_name << " has time units of '" << time_units << "', which are unsupported. 'second' is the only supported time unit.\n"; file.closeData(); file.closeGroup(); return; } Timer timer2; //--- Load the seconds into a double array --- try { file.getDataCoerce(time_double); } catch (::NeXus::Exception &e) { g_log.warning() << "NXlog entry " << entry_name << "'s time field could not be loaded: '" << e.what() << "'.\n"; file.closeData(); file.closeGroup(); return; } file.closeData(); if (VERBOSE) std::cout << "getDataCoerce for the seconds field took " << timer2.elapsed() << " sec.\n"; if (isInt) { //Make an int TSP TimeSeriesProperty<int> * tsp = new TimeSeriesProperty<int>(entry_name); tsp->create(start_time, time_double, values_int); tsp->setUnits( units ); WS->mutableRun().addProperty( tsp, overwritelogs ); } else { //Make a double TSP TimeSeriesProperty<double> * tsp = new TimeSeriesProperty<double>(entry_name); Timer timer3; tsp->create(start_time, time_double, values); if (VERBOSE) std::cout << "creating a TSP took " << timer3.elapsed() << " sec.\n"; tsp->setUnits( units ); WS->mutableRun().addProperty( tsp, overwritelogs ); // Trick to free memory? std::vector<double>().swap(time_double); std::vector<double>().swap(values); } } file.closeGroup(); }
/** * Read histogram data * @param histogramEntries map of the file entries that have histogram * @param outputGroup pointer to the workspace group * @param nxFile Reads data from inside first first top entry */ void LoadMcStas::readHistogramData( const std::map<std::string, std::string> &histogramEntries, WorkspaceGroup_sptr &outputGroup, ::NeXus::File &nxFile) { std::string nameAttrValueYLABEL; for (const auto &histogramEntry : histogramEntries) { const std::string &dataName = histogramEntry.first; const std::string &dataType = histogramEntry.second; // open second level entry nxFile.openGroup(dataName, dataType); // grap title to use to e.g. create workspace name std::string nameAttrValueTITLE; nxFile.getAttr("filename", nameAttrValueTITLE); if (nxFile.hasAttr("ylabel")) { nxFile.getAttr("ylabel", nameAttrValueYLABEL); } // Find the axis names auto nxdataEntries = nxFile.getEntries(); std::string axis1Name, axis2Name; for (auto &nxdataEntry : nxdataEntries) { if (nxdataEntry.second == "NXparameters") continue; if (nxdataEntry.first == "ncount") continue; nxFile.openData(nxdataEntry.first); if (nxFile.hasAttr("axis")) { int axisNo(0); nxFile.getAttr("axis", axisNo); if (axisNo == 1) axis1Name = nxdataEntry.first; else if (axisNo == 2) axis2Name = nxdataEntry.first; else throw std::invalid_argument("Unknown axis number"); } nxFile.closeData(); } std::vector<double> axis1Values, axis2Values; nxFile.readData<double>(axis1Name, axis1Values); if (axis2Name.length() == 0) { axis2Name = nameAttrValueYLABEL; axis2Values.push_back(0.0); } else { nxFile.readData<double>(axis2Name, axis2Values); } const size_t axis1Length = axis1Values.size(); const size_t axis2Length = axis2Values.size(); g_log.debug() << "Axis lengths=" << axis1Length << " " << axis2Length << '\n'; // Require "data" field std::vector<double> data; nxFile.readData<double>("data", data); // Optional errors field std::vector<double> errors; try { nxFile.readData<double>("errors", errors); } catch (::NeXus::Exception &) { g_log.information() << "Field " << dataName << " contains no error information.\n"; } // close second level entry nxFile.closeGroup(); MatrixWorkspace_sptr ws = WorkspaceFactory::Instance().create( "Workspace2D", axis2Length, axis1Length, axis1Length); Axis *axis1 = ws->getAxis(0); axis1->title() = axis1Name; // Set caption auto lblUnit = boost::make_shared<Units::Label>(); lblUnit->setLabel(axis1Name, ""); axis1->unit() = lblUnit; Axis *axis2 = new NumericAxis(axis2Length); axis2->title() = axis2Name; // Set caption lblUnit = boost::make_shared<Units::Label>(); lblUnit->setLabel(axis2Name, ""); axis2->unit() = lblUnit; ws->setYUnit(axis2Name); ws->replaceAxis(1, axis2); for (size_t wsIndex = 0; wsIndex < axis2Length; ++wsIndex) { auto &dataY = ws->dataY(wsIndex); auto &dataE = ws->dataE(wsIndex); auto &dataX = ws->dataX(wsIndex); for (size_t j = 0; j < axis1Length; ++j) { // Data is stored in column-major order so we are translating to // row major for Mantid const size_t fileDataIndex = j * axis2Length + wsIndex; dataY[j] = data[fileDataIndex]; dataX[j] = axis1Values[j]; if (!errors.empty()) dataE[j] = errors[fileDataIndex]; } axis2->setValue(wsIndex, axis2Values[wsIndex]); } // set the workspace title ws->setTitle(nameAttrValueTITLE); // use the workspace title to create the workspace name std::replace(nameAttrValueTITLE.begin(), nameAttrValueTITLE.end(), ' ', '_'); // ensure that specified name is given to workspace (eventWS) when added to // outputGroup std::string nameOfGroupWS = getProperty("OutputWorkspace"); std::string nameUserSee = nameAttrValueTITLE + "_" + nameOfGroupWS; std::string extraProperty = "Outputworkspace_dummy_" + std::to_string(m_countNumWorkspaceAdded); declareProperty(Kernel::make_unique<WorkspaceProperty<Workspace>>( extraProperty, nameUserSee, Direction::Output)); setProperty(extraProperty, boost::static_pointer_cast<Workspace>(ws)); m_countNumWorkspaceAdded++; // need to increment to ensure extraProperty are // unique // Make Mantid store the workspace in the group outputGroup->addWorkspace(ws); } nxFile.closeGroup(); } // finish
/** * Read Event Data * @param eventEntries map of the file entries that have events * @param nxFile Reads data from inside first top entry * @return Names of workspaces to include in the output group */ std::vector<std::string> LoadMcStas::readEventData( const std::map<std::string, std::string> &eventEntries, ::NeXus::File &nxFile) { // vector to store output workspaces std::vector<std::string> scatteringWSNames; std::string filename = getPropertyValue("Filename"); auto entries = nxFile.getEntries(); const bool errorBarsSetTo1 = getProperty("ErrorBarsSetTo1"); // will assume that each top level entry contain one mcstas // generated IDF and any event data entries within this top level // entry are data collected for that instrument // This code for loading the instrument is for now adjusted code from // ExperimentalInfo. // Close data folder and go back to top level. Then read and close the // Instrument folder. nxFile.closeGroup(); Geometry::Instrument_sptr instrument; // Initialize progress reporting int reports = 2; const double progressFractionInitial = 0.1; Progress progInitial(this, 0.0, progressFractionInitial, reports); std::string instrumentXML; progInitial.report("Loading instrument"); try { nxFile.openGroup("instrument", "NXinstrument"); nxFile.openGroup("instrument_xml", "NXnote"); nxFile.readData("data", instrumentXML); nxFile.closeGroup(); nxFile.closeGroup(); } catch (...) { g_log.warning() << "\nCould not find the instrument description in the Nexus file:" << filename << " Ignore eventdata from the Nexus file\n"; return scatteringWSNames; ; } try { std::string instrumentName = "McStas"; Geometry::InstrumentDefinitionParser parser(filename, instrumentName, instrumentXML); std::string instrumentNameMangled = parser.getMangledName(); // Check whether the instrument is already in the InstrumentDataService if (InstrumentDataService::Instance().doesExist(instrumentNameMangled)) { // If it does, just use the one from the one stored there instrument = InstrumentDataService::Instance().retrieve(instrumentNameMangled); } else { // Really create the instrument instrument = parser.parseXML(nullptr); // Add to data service for later retrieval InstrumentDataService::Instance().add(instrumentNameMangled, instrument); } } catch (Exception::InstrumentDefinitionError &e) { g_log.warning() << "When trying to read the instrument description in the Nexus file: " << filename << " the following error is reported: " << e.what() << " Ignore eventdata from the Nexus file\n"; return scatteringWSNames; ; } catch (...) { g_log.warning() << "Could not parse instrument description in the Nexus file: " << filename << " Ignore eventdata from the Nexus file\n"; return scatteringWSNames; ; } // Finished reading Instrument. Then open new data folder again nxFile.openGroup("data", "NXdetector"); // create and prepare an event workspace ready to receive the mcstas events progInitial.report("Set up EventWorkspace"); EventWorkspace_sptr eventWS(new EventWorkspace()); // initialize, where create up front number of eventlists = number of // detectors eventWS->initialize(instrument->getNumberDetectors(), 1, 1); // Set the units eventWS->getAxis(0)->unit() = UnitFactory::Instance().create("TOF"); eventWS->setYUnit("Counts"); // set the instrument eventWS->setInstrument(instrument); // assign detector ID to eventlists std::vector<detid_t> detIDs = instrument->getDetectorIDs(); for (size_t i = 0; i < instrument->getNumberDetectors(); i++) { eventWS->getSpectrum(i).addDetectorID(detIDs[i]); // spectrum number are treated as equal to detector IDs for McStas data eventWS->getSpectrum(i).setSpectrumNo(detIDs[i]); } // the one is here for the moment for backward compatibility eventWS->rebuildSpectraMapping(true); bool isAnyNeutrons = false; // to store shortest and longest recorded TOF double shortestTOF(0.0); double longestTOF(0.0); // create vector container all the event output workspaces needed const size_t numEventEntries = eventEntries.size(); std::string nameOfGroupWS = getProperty("OutputWorkspace"); const auto eventDataTotalName = "EventData_" + nameOfGroupWS; std::vector<std::pair<EventWorkspace_sptr, std::string>> allEventWS = { {eventWS, eventDataTotalName}}; // if numEventEntries > 1 also create separate event workspaces const bool onlySummedEventWorkspace = getProperty("OutputOnlySummedEventWorkspace"); if (!onlySummedEventWorkspace && numEventEntries > 1) { for (const auto &eventEntry : eventEntries) { const std::string &dataName = eventEntry.first; // create container to hold partial event data // plus the name users will see for it const auto ws_name = dataName + "_" + nameOfGroupWS; allEventWS.emplace_back(eventWS->clone(), ws_name); } } Progress progEntries(this, progressFractionInitial, 1.0, numEventEntries * 2); // Refer to entry in allEventWS. The first non-summed workspace index is 1 auto eventWSIndex = 1u; // Loop over McStas event data components for (const auto &eventEntry : eventEntries) { const std::string &dataName = eventEntry.first; const std::string &dataType = eventEntry.second; // open second level entry nxFile.openGroup(dataName, dataType); std::vector<double> data; nxFile.openData("events"); progEntries.report("read event data from nexus"); // Need to take into account that the nexus readData method reads a // multi-column data entry // into a vector // The number of data column for each neutron is here hardcoded to (p, x, // y, n, id, t) // Thus we have // column 0 : p neutron wight // column 1 : x x coordinate // column 2 : y y coordinate // column 3 : n accumulated number of neutrons // column 4 : id pixel id // column 5 : t time // get info about event data ::NeXus::Info id_info = nxFile.getInfo(); if (id_info.dims.size() != 2) { g_log.error() << "Event data in McStas nexus file not loaded. Expected " "event data block to be two dimensional\n"; return scatteringWSNames; ; } int64_t nNeutrons = id_info.dims[0]; int64_t numberOfDataColumn = id_info.dims[1]; if (nNeutrons && numberOfDataColumn != 6) { g_log.error() << "Event data in McStas nexus file expecting 6 columns\n"; return scatteringWSNames; ; } if (!isAnyNeutrons && nNeutrons > 0) isAnyNeutrons = true; std::vector<int64_t> start(2); std::vector<int64_t> step(2); // read the event data in blocks. 1 million event is 1000000*6*8 doubles // about 50Mb int64_t nNeutronsInBlock = 1000000; int64_t nOfFullBlocks = nNeutrons / nNeutronsInBlock; int64_t nRemainingNeutrons = nNeutrons - nOfFullBlocks * nNeutronsInBlock; // sum over number of blocks + 1 to cover the remainder for (int64_t iBlock = 0; iBlock < nOfFullBlocks + 1; iBlock++) { if (iBlock == nOfFullBlocks) { // read remaining neutrons start[0] = nOfFullBlocks * nNeutronsInBlock; start[1] = 0; step[0] = nRemainingNeutrons; step[1] = numberOfDataColumn; } else { // read neutrons in a full block start[0] = iBlock * nNeutronsInBlock; start[1] = 0; step[0] = nNeutronsInBlock; step[1] = numberOfDataColumn; } const int64_t nNeutronsForthisBlock = step[0]; // number of neutrons read for this block data.resize(nNeutronsForthisBlock * numberOfDataColumn); // Check that the type is what it is supposed to be if (id_info.type == ::NeXus::FLOAT64) { nxFile.getSlab(&data[0], start, step); } else { g_log.warning() << "Entry event field is not FLOAT64! It will be skipped.\n"; continue; } // populate workspace with McStas events const detid2index_map detIDtoWSindex_map = allEventWS[0].first->getDetectorIDToWorkspaceIndexMap(true); progEntries.report("read event data into workspace"); for (int64_t in = 0; in < nNeutronsForthisBlock; in++) { const int detectorID = static_cast<int>(data[4 + numberOfDataColumn * in]); const double detector_time = data[5 + numberOfDataColumn * in] * 1.0e6; // convert to microseconds if (in == 0 && iBlock == 0) { shortestTOF = detector_time; longestTOF = detector_time; } else { if (detector_time < shortestTOF) shortestTOF = detector_time; if (detector_time > longestTOF) longestTOF = detector_time; } const size_t workspaceIndex = detIDtoWSindex_map.find(detectorID)->second; int64_t pulse_time = 0; auto weightedEvent = WeightedEvent(); if (errorBarsSetTo1) { weightedEvent = WeightedEvent(detector_time, pulse_time, data[numberOfDataColumn * in], 1.0); } else { weightedEvent = WeightedEvent( detector_time, pulse_time, data[numberOfDataColumn * in], data[numberOfDataColumn * in] * data[numberOfDataColumn * in]); } allEventWS[0].first->getSpectrum(workspaceIndex) += weightedEvent; if (!onlySummedEventWorkspace && numEventEntries > 1) { allEventWS[eventWSIndex].first->getSpectrum(workspaceIndex) += weightedEvent; } } eventWSIndex++; } // end reading over number of blocks of an event dataset nxFile.closeData(); nxFile.closeGroup(); } // end reading over number of event datasets // Create a default TOF-vector for histogramming, for now just 2 bins // 2 bins is the standard. However for McStas simulation data it may make // sense to // increase this number for better initial visual effect auto axis = HistogramData::BinEdges{shortestTOF - 1, longestTOF + 1}; // ensure that specified name is given to workspace (eventWS) when added to // outputGroup for (auto eventWS : allEventWS) { const auto ws = eventWS.first; ws->setAllX(axis); AnalysisDataService::Instance().addOrReplace(eventWS.second, ws); scatteringWSNames.emplace_back(eventWS.second); } return scatteringWSNames; }
/** * Load an SE log entry * @param file :: A reference to the NeXus file handle opened at the parent * group * @param entry_name :: The name of the log entry * @param workspace :: A pointer to the workspace to store the logs */ void LoadNexusLogs::loadSELog( ::NeXus::File &file, const std::string &entry_name, boost::shared_ptr<API::MatrixWorkspace> workspace) const { // Open the entry file.openGroup(entry_name, "IXseblock"); std::string propName = entry_name; if (workspace->run().hasProperty(propName)) { propName = "selog_" + propName; } // There are two possible entries: // value_log - A time series entry. This can contain a corrupt value entry // so if it does use the value one // value - A single value float entry Kernel::Property *logValue(nullptr); std::map<std::string, std::string> entries = file.getEntries(); if (entries.find("value_log") != entries.end()) { try { try { file.openGroup("value_log", "NXlog"); } catch (::NeXus::Exception &) { file.closeGroup(); throw; } logValue = createTimeSeries(file, propName); file.closeGroup(); } catch (::NeXus::Exception &e) { g_log.warning() << "IXseblock entry '" << entry_name << "' gave an error when loading " << "a time series:'" << e.what() << "'. Skipping entry\n"; file.closeGroup(); // value_log file.closeGroup(); // entry_name return; } } else if (entries.find("value") != entries.end()) { try { // This may have a larger dimension than 1 bit it has no time field so // take the first entry file.openData("value"); ::NeXus::Info info = file.getInfo(); if (info.type == ::NeXus::FLOAT32) { boost::scoped_array<float> value(new float[info.dims[0]]); file.getData(value.get()); file.closeData(); logValue = new Kernel::PropertyWithValue<double>( propName, static_cast<double>(value[0]), true); } else { file.closeGroup(); return; } } catch (::NeXus::Exception &e) { g_log.warning() << "IXseblock entry " << entry_name << " gave an error when loading " << "a single value:'" << e.what() << "'.\n"; file.closeData(); file.closeGroup(); return; } } else { g_log.warning() << "IXseblock entry " << entry_name << " cannot be read, skipping entry.\n"; file.closeGroup(); return; } workspace->mutableRun().addProperty(logValue); file.closeGroup(); }
/** * Return the confidence with with this algorithm can load the file * @param eventEntries map of the file entries that have events * @param outputGroup pointer to the workspace group * @param nxFile Reads data from inside first first top entry */ void LoadMcStas::readEventData( const std::map<std::string, std::string> &eventEntries, WorkspaceGroup_sptr &outputGroup, ::NeXus::File &nxFile) { std::string filename = getPropertyValue("Filename"); auto entries = nxFile.getEntries(); // will assume that each top level entry contain one mcstas // generated IDF and any event data entries within this top level // entry are data collected for that instrument // This code for loading the instrument is for now adjusted code from // ExperimentalInfo. // Close data folder and go back to top level. Then read and close the // Instrument folder. nxFile.closeGroup(); Geometry::Instrument_sptr instrument; // Initialize progress reporting int reports = 2; const double progressFractionInitial = 0.1; Progress progInitial(this, 0.0, progressFractionInitial, reports); try { nxFile.openGroup("instrument", "NXinstrument"); std::string instrumentXML; nxFile.openGroup("instrument_xml", "NXnote"); nxFile.readData("data", instrumentXML); nxFile.closeGroup(); nxFile.closeGroup(); progInitial.report("Loading instrument"); Geometry::InstrumentDefinitionParser parser; std::string instrumentName = "McStas"; parser.initialize(filename, instrumentName, instrumentXML); std::string instrumentNameMangled = parser.getMangledName(); // Check whether the instrument is already in the InstrumentDataService if (InstrumentDataService::Instance().doesExist(instrumentNameMangled)) { // If it does, just use the one from the one stored there instrument = InstrumentDataService::Instance().retrieve(instrumentNameMangled); } else { // Really create the instrument instrument = parser.parseXML(NULL); // Add to data service for later retrieval InstrumentDataService::Instance().add(instrumentNameMangled, instrument); } } catch (...) { // Loader should not stop if there is no IDF.xml g_log.warning() << "\nCould not find the instrument description in the Nexus file:" << filename << " Ignore evntdata from data file" << std::endl; return; } // Finished reading Instrument. Then open new data folder again nxFile.openGroup("data", "NXdetector"); // create and prepare an event workspace ready to receive the mcstas events progInitial.report("Set up EventWorkspace"); EventWorkspace_sptr eventWS(new EventWorkspace()); // initialize, where create up front number of eventlists = number of // detectors eventWS->initialize(instrument->getNumberDetectors(), 1, 1); // Set the units eventWS->getAxis(0)->unit() = UnitFactory::Instance().create("TOF"); eventWS->setYUnit("Counts"); // set the instrument eventWS->setInstrument(instrument); // assign detector ID to eventlists std::vector<detid_t> detIDs = instrument->getDetectorIDs(); for (size_t i = 0; i < instrument->getNumberDetectors(); i++) { eventWS->getEventList(i).addDetectorID(detIDs[i]); // spectrum number are treated as equal to detector IDs for McStas data eventWS->getEventList(i).setSpectrumNo(detIDs[i]); } // the one is here for the moment for backward compatibility eventWS->rebuildSpectraMapping(true); bool isAnyNeutrons = false; // to store shortest and longest recorded TOF double shortestTOF(0.0); double longestTOF(0.0); const size_t numEventEntries = eventEntries.size(); Progress progEntries(this, progressFractionInitial, 1.0, numEventEntries * 2); for (auto eit = eventEntries.begin(); eit != eventEntries.end(); ++eit) { std::string dataName = eit->first; std::string dataType = eit->second; // open second level entry nxFile.openGroup(dataName, dataType); std::vector<double> data; nxFile.openData("events"); progEntries.report("read event data from nexus"); // Need to take into account that the nexus readData method reads a // multi-column data entry // into a vector // The number of data column for each neutron is here hardcoded to (p, x, // y, n, id, t) // Thus we have // column 0 : p neutron wight // column 1 : x x coordinate // column 2 : y y coordinate // column 3 : n accumulated number of neutrons // column 4 : id pixel id // column 5 : t time // get info about event data ::NeXus::Info id_info = nxFile.getInfo(); if (id_info.dims.size() != 2) { g_log.error() << "Event data in McStas nexus file not loaded. Expected " "event data block to be two dimensional" << std::endl; return; } int64_t nNeutrons = id_info.dims[0]; int64_t numberOfDataColumn = id_info.dims[1]; if (nNeutrons && numberOfDataColumn != 6) { g_log.error() << "Event data in McStas nexus file expecting 6 columns" << std::endl; return; } if (isAnyNeutrons == false && nNeutrons > 0) isAnyNeutrons = true; std::vector<int64_t> start(2); std::vector<int64_t> step(2); // read the event data in blocks. 1 million event is 1000000*6*8 doubles // about 50Mb int64_t nNeutronsInBlock = 1000000; int64_t nOfFullBlocks = nNeutrons / nNeutronsInBlock; int64_t nRemainingNeutrons = nNeutrons - nOfFullBlocks * nNeutronsInBlock; // sum over number of blocks + 1 to cover the remainder for (int64_t iBlock = 0; iBlock < nOfFullBlocks + 1; iBlock++) { if (iBlock == nOfFullBlocks) { // read remaining neutrons start[0] = nOfFullBlocks * nNeutronsInBlock; start[1] = 0; step[0] = nRemainingNeutrons; step[1] = numberOfDataColumn; } else { // read neutrons in a full block start[0] = iBlock * nNeutronsInBlock; start[1] = 0; step[0] = nNeutronsInBlock; step[1] = numberOfDataColumn; } const int64_t nNeutronsForthisBlock = step[0]; // number of neutrons read for this block data.resize(nNeutronsForthisBlock * numberOfDataColumn); // Check that the type is what it is supposed to be if (id_info.type == ::NeXus::FLOAT64) { nxFile.getSlab(&data[0], start, step); } else { g_log.warning() << "Entry event field is not FLOAT64! It will be skipped.\n"; continue; } // populate workspace with McStas events const detid2index_map detIDtoWSindex_map = eventWS->getDetectorIDToWorkspaceIndexMap(true); progEntries.report("read event data into workspace"); for (int64_t in = 0; in < nNeutronsForthisBlock; in++) { const int detectorID = static_cast<int>(data[4 + numberOfDataColumn * in]); const double detector_time = data[5 + numberOfDataColumn * in] * 1.0e6; // convert to microseconds if (in == 0 && iBlock == 0) { shortestTOF = detector_time; longestTOF = detector_time; } else { if (detector_time < shortestTOF) shortestTOF = detector_time; if (detector_time > longestTOF) longestTOF = detector_time; } const size_t workspaceIndex = detIDtoWSindex_map.find(detectorID)->second; int64_t pulse_time = 0; // eventWS->getEventList(workspaceIndex) += // TofEvent(detector_time,pulse_time); // eventWS->getEventList(workspaceIndex) += TofEvent(detector_time); eventWS->getEventList(workspaceIndex) += WeightedEvent( detector_time, pulse_time, data[numberOfDataColumn * in], 1.0); } } // end reading over number of blocks of an event dataset // nxFile.getData(data); nxFile.closeData(); nxFile.closeGroup(); } // end reading over number of event datasets // Create a default TOF-vector for histogramming, for now just 2 bins // 2 bins is the standard. However for McStas simulation data it may make // sense to // increase this number for better initial visual effect Kernel::cow_ptr<MantidVec> axis; MantidVec &xRef = axis.access(); xRef.resize(2, 0.0); // if ( nNeutrons > 0) if (isAnyNeutrons) { xRef[0] = shortestTOF - 1; // Just to make sure the bins hold it all xRef[1] = longestTOF + 1; } // Set the binning axis eventWS->setAllX(axis); // ensure that specified name is given to workspace (eventWS) when added to // outputGroup std::string nameOfGroupWS = getProperty("OutputWorkspace"); std::string nameUserSee = std::string("EventData_") + nameOfGroupWS; std::string extraProperty = "Outputworkspace_dummy_" + boost::lexical_cast<std::string>(m_countNumWorkspaceAdded); declareProperty(new WorkspaceProperty<Workspace>(extraProperty, nameUserSee, Direction::Output)); setProperty(extraProperty, boost::static_pointer_cast<Workspace>(eventWS)); m_countNumWorkspaceAdded++; // need to increment to ensure extraProperty are // unique outputGroup->addWorkspace(eventWS); }