/** Set up an Event workspace * @param numentries :: number of log entries to output * @param times :: vector of Kernel::DateAndTime * @param values :: vector of log value in double */ void ExportTimeSeriesLog::setupEventWorkspace(int numentries, vector<DateAndTime> ×, vector<double> values) { Kernel::DateAndTime runstart( m_dataWS->run().getProperty("run_start")->value()); // Get some stuff from the input workspace const size_t numberOfSpectra = 1; const int YLength = static_cast<int>(m_dataWS->blocksize()); // Make a brand new EventWorkspace EventWorkspace_sptr outEventWS = boost::dynamic_pointer_cast<EventWorkspace>( API::WorkspaceFactory::Instance().create( "EventWorkspace", numberOfSpectra, YLength + 1, YLength)); // Copy geometry over. API::WorkspaceFactory::Instance().initializeFromParent(m_dataWS, outEventWS, false); m_outWS = boost::dynamic_pointer_cast<MatrixWorkspace>(outEventWS); if (!m_outWS) throw runtime_error( "Output workspace cannot be casted to a MatrixWorkspace."); g_log.debug("[DBx336] An output workspace is generated.!"); // Create the output event list (empty) EventList &outEL = outEventWS->getOrAddEventList(0); outEL.switchTo(WEIGHTED_NOTIME); // Allocate all the required memory outEL.reserve(numentries); outEL.clearDetectorIDs(); for (size_t i = 0; i < static_cast<size_t>(numentries); i++) { Kernel::DateAndTime tnow = times[i]; int64_t dt = tnow.totalNanoseconds() - runstart.totalNanoseconds(); // convert to microseconds double dtmsec = static_cast<double>(dt) / 1000.0; outEL.addEventQuickly(WeightedEventNoTime(dtmsec, values[i], values[i])); } // Ensure thread-safety outEventWS->sortAll(TOF_SORT, NULL); // Now, create a default X-vector for histogramming, with just 2 bins. Kernel::cow_ptr<MantidVec> axis; MantidVec &xRef = axis.access(); xRef.resize(2); std::vector<WeightedEventNoTime> &events = outEL.getWeightedEventsNoTime(); xRef[0] = events.begin()->tof(); xRef[1] = events.rbegin()->tof(); // Set the binning axis using this. outEventWS->setX(0, axis); return; }
/** Creates the output workspace, setting the X vector to the bins boundaries in * Qx. * @return A pointer to the newly-created workspace */ API::MatrixWorkspace_sptr Qxy::setUpOutputWorkspace(API::MatrixWorkspace_const_sptr inputWorkspace) { const double max = getProperty("MaxQxy"); const double delta = getProperty("DeltaQ"); int bins = static_cast<int>(max / delta); if (bins * delta != max) ++bins; // Stop at first boundary past MaxQxy if max is not a multiple of // delta const double startVal = -1.0 * delta * bins; bins *= 2; // go from -max to +max bins += 1; // Add 1 - this is a histogram // Create an output workspace with the same meta-data as the input MatrixWorkspace_sptr outputWorkspace = WorkspaceFactory::Instance().create( inputWorkspace, bins - 1, bins, bins - 1); // ... but clear the masking from the parameter map as we don't want to carry // that over since this is essentially // a 2D rebin ParameterMap &pmap = outputWorkspace->instrumentParameters(); pmap.clearParametersByName("masked"); // Create a numeric axis to replace the vertical one Axis *verticalAxis = new BinEdgeAxis(bins); outputWorkspace->replaceAxis(1, verticalAxis); // Build up the X values Kernel::cow_ptr<MantidVec> axis; MantidVec &horizontalAxisRef = axis.access(); horizontalAxisRef.resize(bins); for (int i = 0; i < bins; ++i) { const double currentVal = startVal + i * delta; // Set the X value horizontalAxisRef[i] = currentVal; // Set the Y value on the axis verticalAxis->setValue(i, currentVal); } // Fill the X vectors in the output workspace for (int i = 0; i < bins - 1; ++i) { outputWorkspace->setX(i, axis); for (int j = 0; j < bins - j; ++j) { outputWorkspace->dataY(i)[j] = std::numeric_limits<double>::quiet_NaN(); outputWorkspace->dataE(i)[j] = std::numeric_limits<double>::quiet_NaN(); } } // Set the axis units outputWorkspace->getAxis(1)->unit() = outputWorkspace->getAxis(0)->unit() = UnitFactory::Instance().create("MomentumTransfer"); // Set the 'Y' unit (gets confusing here...this is probably a Z axis in this // case) outputWorkspace->setYUnitLabel("Cross Section (1/cm)"); setProperty("OutputWorkspace", outputWorkspace); return outputWorkspace; }
/** * Get shortest and longest tof from the Parameters file and sets the time * axis * Properties must be present in the Parameters file: shortest-tof, * longest-tof */ void LoadSwans::setTimeAxis() { const unsigned int shortest_tof = static_cast<unsigned int>( m_ws->getInstrument()->getNumberParameter("shortest-tof")[0]); const unsigned int longest_tof = static_cast<unsigned int>( m_ws->getInstrument()->getNumberParameter("longest-tof")[0]); // Now, create a default X-vector for histogramming, with just 2 bins. Kernel::cow_ptr<MantidVec> axis; MantidVec &xRef = axis.access(); xRef = {static_cast<double>(shortest_tof), static_cast<double>(longest_tof)}; // Set the binning axis using this. m_ws->setAllX(axis); }
/** * Execute the algorithm. */ void LoadBBY::exec() { // Delete the output workspace name if it existed std::string outName = getPropertyValue("OutputWorkspace"); if (API::AnalysisDataService::Instance().doesExist(outName)) API::AnalysisDataService::Instance().remove(outName); // Get the name of the data file. std::string filename = getPropertyValue(FilenameStr); ANSTO::Tar::File tarFile(filename); if (!tarFile.good()) throw std::invalid_argument("invalid BBY file"); // region of intreset std::vector<bool> roi = createRoiVector(getPropertyValue(MaskStr)); double tofMinBoundary = getProperty(FilterByTofMinStr); double tofMaxBoundary = getProperty(FilterByTofMaxStr); double timeMinBoundary = getProperty(FilterByTimeStartStr); double timeMaxBoundary = getProperty(FilterByTimeStopStr); if (isEmpty(tofMaxBoundary)) tofMaxBoundary = std::numeric_limits<double>::infinity(); if (isEmpty(timeMaxBoundary)) timeMaxBoundary = std::numeric_limits<double>::infinity(); API::Progress prog(this, 0.0, 1.0, Progress_Total); prog.doReport("creating instrument"); // create workspace DataObjects::EventWorkspace_sptr eventWS = boost::make_shared<DataObjects::EventWorkspace>(); eventWS->initialize(HISTO_BINS_Y * HISTO_BINS_X, 2, // number of TOF bin boundaries 1); // set the units eventWS->getAxis(0)->unit() = Kernel::UnitFactory::Instance().create("TOF"); eventWS->setYUnit("Counts"); // set title const std::vector<std::string> &subFiles = tarFile.files(); for (const auto &subFile : subFiles) if (subFile.compare(0, 3, "BBY") == 0) { std::string title = subFile; if (title.rfind(".hdf") == title.length() - 4) title.resize(title.length() - 4); if (title.rfind(".nx") == title.length() - 3) title.resize(title.length() - 3); eventWS->setTitle(title); break; } // create instrument InstrumentInfo instrumentInfo; // Geometry::Instrument_sptr instrument = createInstrument(tarFile, /* ref */ instrumentInfo); // eventWS->setInstrument(instrument); // load events size_t numberHistograms = eventWS->getNumberHistograms(); std::vector<EventVector_pt> eventVectors(numberHistograms, nullptr); std::vector<size_t> eventCounts(numberHistograms, 0); // phase correction Kernel::Property *periodMasterProperty = getPointerToProperty(PeriodMasterStr); Kernel::Property *periodSlaveProperty = getPointerToProperty(PeriodSlaveStr); Kernel::Property *phaseSlaveProperty = getPointerToProperty(PhaseSlaveStr); double periodMaster; double periodSlave; double phaseSlave; if (periodMasterProperty->isDefault() || periodSlaveProperty->isDefault() || phaseSlaveProperty->isDefault()) { if (!periodMasterProperty->isDefault() || !periodSlaveProperty->isDefault() || !phaseSlaveProperty->isDefault()) { throw std::invalid_argument("Please specify PeriodMaster, PeriodSlave " "and PhaseSlave or none of them."); } // if values have not been specified in loader then use values from hdf file periodMaster = instrumentInfo.period_master; periodSlave = instrumentInfo.period_slave; phaseSlave = instrumentInfo.phase_slave; } else { periodMaster = getProperty(PeriodMasterStr); periodSlave = getProperty(PeriodSlaveStr); phaseSlave = getProperty(PhaseSlaveStr); if ((periodMaster < 0.0) || (periodSlave < 0.0)) throw std::invalid_argument( "Please specify a positive value for PeriodMaster and PeriodSlave."); } double period = periodSlave; double shift = -1.0 / 6.0 * periodMaster - periodSlave * phaseSlave / 360.0; // count total events per pixel to reserve necessary memory ANSTO::EventCounter eventCounter( roi, HISTO_BINS_Y, period, shift, tofMinBoundary, tofMaxBoundary, timeMinBoundary, timeMaxBoundary, eventCounts); loadEvents(prog, "loading neutron counts", tarFile, eventCounter); // prepare event storage ANSTO::ProgressTracker progTracker(prog, "creating neutron event lists", numberHistograms, Progress_ReserveMemory); for (size_t i = 0; i != numberHistograms; ++i) { DataObjects::EventList &eventList = eventWS->getEventList(i); eventList.setSortOrder(DataObjects::PULSETIME_SORT); eventList.reserve(eventCounts[i]); eventList.setDetectorID(static_cast<detid_t>(i)); eventList.setSpectrumNo(static_cast<detid_t>(i)); DataObjects::getEventsFrom(eventList, eventVectors[i]); progTracker.update(i); } progTracker.complete(); ANSTO::EventAssigner eventAssigner( roi, HISTO_BINS_Y, period, shift, tofMinBoundary, tofMaxBoundary, timeMinBoundary, timeMaxBoundary, eventVectors); loadEvents(prog, "loading neutron events", tarFile, eventAssigner); Kernel::cow_ptr<MantidVec> axis; MantidVec &xRef = axis.access(); xRef.resize(2, 0.0); xRef[0] = std::max( 0.0, floor(eventCounter.tofMin())); // just to make sure the bins hold it all xRef[1] = eventCounter.tofMax() + 1; eventWS->setAllX(axis); // count total number of masked bins size_t maskedBins = 0; for (size_t i = 0; i != roi.size(); i++) if (!roi[i]) maskedBins++; if (maskedBins > 0) { // create list of masked bins std::vector<size_t> maskIndexList(maskedBins); size_t maskIndex = 0; for (size_t i = 0; i != roi.size(); i++) if (!roi[i]) maskIndexList[maskIndex++] = i; API::IAlgorithm_sptr maskingAlg = createChildAlgorithm("MaskDetectors"); maskingAlg->setProperty("Workspace", eventWS); maskingAlg->setProperty("WorkspaceIndexList", maskIndexList); maskingAlg->executeAsChildAlg(); } // set log values API::LogManager &logManager = eventWS->mutableRun(); logManager.addProperty("filename", filename); logManager.addProperty("att_pos", static_cast<int>(instrumentInfo.att_pos)); logManager.addProperty("frame_count", static_cast<int>(eventCounter.numFrames())); logManager.addProperty("period", period); // currently beam monitor counts are not available, instead number of frames // times period is used logManager.addProperty( "bm_counts", static_cast<double>(eventCounter.numFrames()) * period / 1.0e6); // static_cast<double>(instrumentInfo.bm_counts) // currently Kernel::time_duration duration = boost::posix_time::microseconds(static_cast<boost::int64_t>( static_cast<double>(eventCounter.numFrames()) * period)); Kernel::DateAndTime start_time("2000-01-01T00:00:00"); Kernel::DateAndTime end_time(start_time + duration); logManager.addProperty("start_time", start_time.toISO8601String()); logManager.addProperty("end_time", end_time.toISO8601String()); std::string time_str = start_time.toISO8601String(); AddSinglePointTimeSeriesProperty(logManager, time_str, "L1_chopper_value", instrumentInfo.L1_chopper_value); AddSinglePointTimeSeriesProperty(logManager, time_str, "L2_det_value", instrumentInfo.L2_det_value); AddSinglePointTimeSeriesProperty(logManager, time_str, "L2_curtainl_value", instrumentInfo.L2_curtainl_value); AddSinglePointTimeSeriesProperty(logManager, time_str, "L2_curtainr_value", instrumentInfo.L2_curtainr_value); AddSinglePointTimeSeriesProperty(logManager, time_str, "L2_curtainu_value", instrumentInfo.L2_curtainu_value); AddSinglePointTimeSeriesProperty(logManager, time_str, "L2_curtaind_value", instrumentInfo.L2_curtaind_value); AddSinglePointTimeSeriesProperty(logManager, time_str, "D_det_value", instrumentInfo.D_det_value); AddSinglePointTimeSeriesProperty(logManager, time_str, "D_curtainl_value", instrumentInfo.D_curtainl_value); AddSinglePointTimeSeriesProperty(logManager, time_str, "D_curtainr_value", instrumentInfo.D_curtainr_value); AddSinglePointTimeSeriesProperty(logManager, time_str, "D_curtainu_value", instrumentInfo.D_curtainu_value); AddSinglePointTimeSeriesProperty(logManager, time_str, "D_curtaind_value", instrumentInfo.D_curtaind_value); AddSinglePointTimeSeriesProperty(logManager, time_str, "curtain_rotation", 10.0); API::IAlgorithm_sptr loadInstrumentAlg = createChildAlgorithm("LoadInstrument"); loadInstrumentAlg->setProperty("Workspace", eventWS); loadInstrumentAlg->setPropertyValue("InstrumentName", "BILBY"); loadInstrumentAlg->setProperty("RewriteSpectraMap", Mantid::Kernel::OptionalBool(false)); loadInstrumentAlg->executeAsChildAlg(); setProperty("OutputWorkspace", eventWS); }
/** Process the event file properly. * @param workspace :: EventWorkspace to write to. */ void LoadEventPreNexus::procEvents(DataObjects::EventWorkspace_sptr &workspace) { this->num_error_events = 0; this->num_good_events = 0; this->num_ignored_events = 0; // Default values in the case of no parallel size_t loadBlockSize = Mantid::Kernel::DEFAULT_BLOCK_SIZE * 2; shortest_tof = static_cast<double>(MAX_TOF_UINT32) * TOF_CONVERSION; longest_tof = 0.; // Initialize progress reporting. size_t numBlocks = (max_events + loadBlockSize - 1) / loadBlockSize; // We want to pad out empty pixels. detid2det_map detector_map; workspace->getInstrument()->getDetectors(detector_map); // -------------- Determine processing mode std::string procMode = getProperty("UseParallelProcessing"); if (procMode == "Serial") parallelProcessing = false; else if (procMode == "Parallel") parallelProcessing = true; else { // Automatic determination. Loading serially (for me) is about 3 million // events per second, // (which is sped up by ~ x 3 with parallel processing, say 10 million per // second, e.g. 7 million events more per seconds). // compared to a setup time/merging time of about 10 seconds per million // detectors. double setUpTime = double(detector_map.size()) * 10e-6; parallelProcessing = ((double(max_events) / 7e6) > setUpTime); g_log.debug() << (parallelProcessing ? "Using" : "Not using") << " parallel processing." << std::endl; } // determine maximum pixel id detid2det_map::iterator it; detid_max = 0; // seems like a safe lower bound for (it = detector_map.begin(); it != detector_map.end(); it++) if (it->first > detid_max) detid_max = it->first; // Pad all the pixels prog->report("Padding Pixels"); this->pixel_to_wkspindex.reserve( detid_max + 1); // starting at zero up to and including detid_max // Set to zero this->pixel_to_wkspindex.assign(detid_max + 1, 0); size_t workspaceIndex = 0; for (it = detector_map.begin(); it != detector_map.end(); it++) { if (!it->second->isMonitor()) { this->pixel_to_wkspindex[it->first] = workspaceIndex; EventList &spec = workspace->getOrAddEventList(workspaceIndex); spec.addDetectorID(it->first); // Start the spectrum number at 1 spec.setSpectrumNo(specid_t(workspaceIndex + 1)); workspaceIndex += 1; } } // For slight speed up loadOnlySomeSpectra = (this->spectra_list.size() > 0); // Turn the spectra list into a map, for speed of access for (std::vector<int64_t>::iterator it = spectra_list.begin(); it != spectra_list.end(); it++) spectraLoadMap[*it] = true; CPUTimer tim; // --------------- Create the partial workspaces // ------------------------------------------ // Vector of partial workspaces, for parallel processing. std::vector<EventWorkspace_sptr> partWorkspaces; std::vector<DasEvent *> buffers; /// Pointer to the vector of events typedef std::vector<TofEvent> *EventVector_pt; /// Bare array of arrays of pointers to the EventVectors EventVector_pt **eventVectors; /// How many threads will we use? size_t numThreads = 1; if (parallelProcessing) numThreads = size_t(PARALLEL_GET_MAX_THREADS); partWorkspaces.resize(numThreads); buffers.resize(numThreads); eventVectors = new EventVector_pt *[numThreads]; // cppcheck-suppress syntaxError PRAGMA_OMP( parallel for if (parallelProcessing) ) for (int i = 0; i < int(numThreads); i++) { // This is the partial workspace we are about to create (if in parallel) EventWorkspace_sptr partWS; if (parallelProcessing) { prog->report("Creating Partial Workspace"); // Create a partial workspace partWS = EventWorkspace_sptr(new EventWorkspace()); // Make sure to initialize. partWS->initialize(1, 1, 1); // Copy all the spectra numbers and stuff (no actual events to copy // though). partWS->copyDataFrom(*workspace); // Push it in the array partWorkspaces[i] = partWS; } else partWS = workspace; // Allocate the buffers buffers[i] = new DasEvent[loadBlockSize]; // For each partial workspace, make an array where index = detector ID and // value = pointer to the events vector eventVectors[i] = new EventVector_pt[detid_max + 1]; EventVector_pt *theseEventVectors = eventVectors[i]; for (detid_t j = 0; j < detid_max + 1; j++) { size_t wi = pixel_to_wkspindex[j]; // Save a POINTER to the vector<tofEvent> theseEventVectors[j] = &partWS->getEventList(wi).getEvents(); } } g_log.debug() << tim << " to create " << partWorkspaces.size() << " workspaces for parallel loading." << std::endl; prog->resetNumSteps(numBlocks, 0.1, 0.8); // ---------------------------------- LOAD THE DATA -------------------------- PRAGMA_OMP( parallel for schedule(dynamic, 1) if (parallelProcessing) ) for (int blockNum = 0; blockNum < int(numBlocks); blockNum++) { PARALLEL_START_INTERUPT_REGION // Find the workspace for this particular thread EventWorkspace_sptr ws; size_t threadNum = 0; if (parallelProcessing) { threadNum = PARALLEL_THREAD_NUMBER; ws = partWorkspaces[threadNum]; } else ws = workspace; // Get the buffer (for this thread) DasEvent *event_buffer = buffers[threadNum]; // Get the speeding-up array of vector<tofEvent> where index = detid. EventVector_pt *theseEventVectors = eventVectors[threadNum]; // Where to start in the file? size_t fileOffset = first_event + (loadBlockSize * blockNum); // May need to reduce size of last (or only) block size_t current_event_buffer_size = (blockNum == int(numBlocks - 1)) ? (max_events - (numBlocks - 1) * loadBlockSize) : loadBlockSize; // Load this chunk of event data (critical block) PARALLEL_CRITICAL(LoadEventPreNexus_fileAccess) { current_event_buffer_size = eventfile->loadBlockAt( event_buffer, fileOffset, current_event_buffer_size); } // This processes the events. Can be done in parallel! procEventsLinear(ws, theseEventVectors, event_buffer, current_event_buffer_size, fileOffset); // Report progress prog->report("Load Event PreNeXus"); PARALLEL_END_INTERUPT_REGION } PARALLEL_CHECK_INTERUPT_REGION g_log.debug() << tim << " to load the data." << std::endl; // ---------------------------------- MERGE WORKSPACES BACK TOGETHER // -------------------------- if (parallelProcessing) { PARALLEL_START_INTERUPT_REGION prog->resetNumSteps(workspace->getNumberHistograms(), 0.8, 0.95); size_t memoryCleared = 0; MemoryManager::Instance().releaseFreeMemory(); // Merge all workspaces, index by index. PARALLEL_FOR_NO_WSP_CHECK() for (int iwi = 0; iwi < int(workspace->getNumberHistograms()); iwi++) { size_t wi = size_t(iwi); // The output event list. EventList &el = workspace->getEventList(wi); el.clear(false); // How many events will it have? size_t numEvents = 0; for (size_t i = 0; i < numThreads; i++) numEvents += partWorkspaces[i]->getEventList(wi).getNumberEvents(); // This will avoid too much copying. el.reserve(numEvents); // Now merge the event lists for (size_t i = 0; i < numThreads; i++) { EventList &partEl = partWorkspaces[i]->getEventList(wi); el += partEl.getEvents(); // Free up memory as you go along. partEl.clear(false); } // With TCMalloc, release memory when you accumulate enough to make sense PARALLEL_CRITICAL(LoadEventPreNexus_trackMemory) { memoryCleared += numEvents; if (memoryCleared > 10000000) // ten million events = about 160 MB { MemoryManager::Instance().releaseFreeMemory(); memoryCleared = 0; } } prog->report("Merging Workspaces"); } // Final memory release MemoryManager::Instance().releaseFreeMemory(); g_log.debug() << tim << " to merge workspaces together." << std::endl; PARALLEL_END_INTERUPT_REGION } PARALLEL_CHECK_INTERUPT_REGION // Delete the buffers for each thread. for (size_t i = 0; i < numThreads; i++) { delete[] buffers[i]; delete[] eventVectors[i]; } delete[] eventVectors; // delete [] pulsetimes; prog->resetNumSteps(3, 0.94, 1.00); // finalize loading prog->report("Deleting Empty Lists"); if (loadOnlySomeSpectra) workspace->deleteEmptyLists(); prog->report("Setting proton charge"); this->setProtonCharge(workspace); g_log.debug() << tim << " to set the proton charge log." << std::endl; // Make sure the MRU is cleared workspace->clearMRU(); // Now, create a default X-vector for histogramming, with just 2 bins. Kernel::cow_ptr<MantidVec> axis; MantidVec &xRef = axis.access(); xRef.resize(2); xRef[0] = shortest_tof - 1; // Just to make sure the bins hold it all xRef[1] = longest_tof + 1; workspace->setAllX(axis); this->pixel_to_wkspindex.clear(); g_log.information() << "Read " << this->num_good_events << " events + " << this->num_error_events << " errors" << ". Shortest TOF: " << shortest_tof << " microsec; longest TOF: " << longest_tof << " microsec." << std::endl; }
/** * 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); }