/* Execute the transformtion. Generates an output IMDEventWorkspace. @return the constructed IMDEventWorkspace following the transformation. @param ws: Input MatrixWorkspace const shared pointer */ IMDEventWorkspace_sptr ReflectometryTransformQxQz::execute(MatrixWorkspace_const_sptr inputWs) const { const size_t nbinsx = 10; const size_t nbinsz = 10; auto ws = boost::make_shared<MDEventWorkspace<MDLeanEvent<2>,2> >(); MDHistoDimension_sptr qxDim = MDHistoDimension_sptr(new MDHistoDimension("Qx","qx","(Ang^-1)", static_cast<Mantid::coord_t>(m_qxMin), static_cast<Mantid::coord_t>(m_qxMax), nbinsx)); MDHistoDimension_sptr qzDim = MDHistoDimension_sptr(new MDHistoDimension("Qz","qz","(Ang^-1)", static_cast<Mantid::coord_t>(m_qzMin), static_cast<Mantid::coord_t>(m_qzMax), nbinsz)); ws->addDimension(qxDim); ws->addDimension(qzDim); // Set some reasonable values for the box controller BoxController_sptr bc = ws->getBoxController(); bc->setSplitInto(2); bc->setSplitThreshold(10); // Initialize the workspace. ws->initialize(); // Start with a MDGridBox. ws->splitBox(); auto spectraAxis = inputWs->getAxis(1); for(size_t index = 0; index < inputWs->getNumberHistograms(); ++index) { auto counts = inputWs->readY(index); auto wavelengths = inputWs->readX(index); auto errors = inputWs->readE(index); const size_t nInputBins = wavelengths.size() -1; const double theta_final = spectraAxis->getValue(index); m_QxCalculation.setThetaFinal(theta_final); m_QzCalculation.setThetaFinal(theta_final); //Loop over all bins in spectra for(size_t binIndex = 0; binIndex < nInputBins; ++binIndex) { const double& wavelength = 0.5*(wavelengths[binIndex] + wavelengths[binIndex+1]); double _qx = m_QxCalculation.execute(wavelength); double _qz = m_QzCalculation.execute(wavelength); double centers[2] = {_qx, _qz}; ws->addEvent(MDLeanEvent<2>(float(counts[binIndex]), float(errors[binIndex]*errors[binIndex]), centers)); } ws->splitAllIfNeeded(NULL); } return ws; }
void CloneMDWorkspace::doClone(const typename MDEventWorkspace<MDE, nd>::sptr ws) { std::string outWSName = getPropertyValue("OutputWorkspace"); Progress prog(this, 0.0, 10.0, 100); BoxController_sptr bc = ws->getBoxController(); if (!bc) throw std::runtime_error("Error with InputWorkspace: no BoxController!"); if (bc->isFileBacked()) { // Generate a new filename to copy to prog.report("Copying File"); std::string originalFile = bc->getFilename(); std::string outFilename = getPropertyValue("Filename"); if (outFilename.empty()) { // Auto-generated name Poco::Path path = Poco::Path(originalFile).absolute(); std::string newName = path.getBaseName() + "_clone." + path.getExtension(); path.setFileName(newName); outFilename = path.toString(); } // Perform the copying g_log.notice() << "Cloned workspace file being copied to: " << outFilename << std::endl; Poco::File(originalFile).copyTo(outFilename); g_log.information() << "File copied successfully." << std::endl; // Now load it back IAlgorithm_sptr alg = createSubAlgorithm("LoadMD", 0.5, 1.0, false); alg->setPropertyValue("Filename", outFilename); alg->setPropertyValue("FileBackEnd", "1"); alg->setPropertyValue("Memory", "0"); //TODO: How much memory? alg->setPropertyValue("OutputWorkspace", outWSName); alg->executeAsSubAlg(); // Set the output workspace to this IMDEventWorkspace_sptr outWS = alg->getProperty("OutputWorkspace"); this->setProperty("OutputWorkspace", outWS); } else { // Perform the clone in memory. boost::shared_ptr<MDEventWorkspace<MDE,nd> > outWS(new MDEventWorkspace<MDE,nd>(*ws)); this->setProperty("OutputWorkspace", boost::dynamic_pointer_cast<IMDEventWorkspace>(outWS) ); } }
/** Set the settings in the given box controller * This should only be called immediately after creating the workspace * * @param bc :: box controller to modify * @param instrument :: instrument to read parameters from. */ void BoxControllerSettingsAlgorithm::setBoxController( BoxController_sptr bc, Mantid::Geometry::Instrument_const_sptr instrument) { size_t nd = bc->getNDims(); takeDefaultsFromInstrument(instrument, nd); setBoxController(bc); }
/** * Creates an MD workspace * @param a : pointer to the first dimension of the MDWorkspace *@param b : pointer to the second dimension of the MDWorkspace * @param boxController : controls how the MDWorkspace will be split */ boost::shared_ptr<MDEventWorkspace2Lean> ReflectometryTransform::createMDWorkspace( Mantid::Geometry::IMDDimension_sptr a, Mantid::Geometry::IMDDimension_sptr b, BoxController_sptr boxController) const { auto ws = boost::make_shared<MDEventWorkspace2Lean>(); ws->addDimension(a); ws->addDimension(b); BoxController_sptr wsbc = ws->getBoxController(); // Get the box controller wsbc->setSplitInto(boxController->getSplitInto(0)); wsbc->setMaxDepth(boxController->getMaxDepth()); wsbc->setSplitThreshold(boxController->getSplitThreshold()); // Initialize the workspace. ws->initialize(); // Start with a MDGridBox. ws->splitBox(); return ws; }
/** Set the settings in the given box controller * This should only be called immediately after creating the workspace * * @param bc :: box controller to modify */ void BoxControllerSettingsAlgorithm::setBoxController(BoxController_sptr bc) { size_t nd = bc->getNDims(); int val; val = this->getProperty("SplitThreshold"); bc->setSplitThreshold(val); val = this->getProperty("MaxRecursionDepth"); bc->setMaxDepth(val); // Build MDGridBox std::vector<int> splits = getProperty("SplitInto"); if (splits.size() == 1) { bc->setSplitInto(splits[0]); } else if (splits.size() == nd) { for (size_t d = 0; d < nd; ++d) bc->setSplitInto(d, splits[d]); } else throw std::invalid_argument("SplitInto parameter has " + Strings::toString(splits.size()) + " arguments. It should have either 1, or the " "same as the number of dimensions."); bc->resetNumBoxes(); }
/** Execute the algorithm. */ void ConvertToDiffractionMDWorkspace::exec() { Timer tim, timtotal; CPUTimer cputim, cputimtotal; // ---------------------- Extract properties // -------------------------------------- ClearInputWorkspace = getProperty("ClearInputWorkspace"); Append = getProperty("Append"); std::string OutputDimensions = getPropertyValue("OutputDimensions"); LorentzCorrection = getProperty("LorentzCorrection"); OneEventPerBin = getProperty("OneEventPerBin"); // -------- Input workspace -> convert to Event // ------------------------------------ m_inWS = getProperty("InputWorkspace"); Workspace2D_sptr m_InWS2D = boost::dynamic_pointer_cast<Workspace2D>(m_inWS); if (LorentzCorrection) { API::Run &run = m_inWS->mutableRun(); if (run.hasProperty("LorentzCorrection")) { Kernel::Property *prop = run.getProperty("LorentzCorrection"); bool lorentzDone = boost::lexical_cast<bool, std::string>(prop->value()); if (lorentzDone) { LorentzCorrection = false; g_log.warning() << "Lorentz Correction was already done for this " "workspace. LorentzCorrection was changed to false." << std::endl; } } } m_inEventWS = boost::dynamic_pointer_cast<EventWorkspace>(m_inWS); // check the input units if (m_inWS->getAxis(0)->unit()->unitID() != "TOF") throw std::invalid_argument( "Input event workspace's X axis must be in TOF units."); // Try to get the output workspace IMDEventWorkspace_sptr i_out = getProperty("OutputWorkspace"); ws = boost::dynamic_pointer_cast< DataObjects::MDEventWorkspace<DataObjects::MDLeanEvent<3>, 3>>(i_out); // Initalize the matrix to 3x3 identity mat = Kernel::Matrix<double>(3, 3, true); // ----------------- Handle the type of output // ------------------------------------- std::string dimensionNames[3] = {"Q_lab_x", "Q_lab_y", "Q_lab_z"}; Mantid::Kernel::SpecialCoordinateSystem coordinateSystem = Mantid::Kernel::QLab; // Setup the MDFrame auto frameFactory = makeMDFrameFactoryChain(); Mantid::Geometry::MDFrame_uptr frame; if (OutputDimensions == "Q (sample frame)") { // Set the matrix based on goniometer angles mat = m_inWS->mutableRun().getGoniometerMatrix(); // But we need to invert it, since we want to get the Q in the sample frame. mat.Invert(); // Names dimensionNames[0] = "Q_sample_x"; dimensionNames[1] = "Q_sample_y"; dimensionNames[2] = "Q_sample_z"; coordinateSystem = Mantid::Kernel::QSample; // Frame MDFrameArgument frameArgQSample(QSample::QSampleName, ""); frame = frameFactory->create(frameArgQSample); } else if (OutputDimensions == "HKL") { // Set the matrix based on UB etc. Kernel::Matrix<double> ub = m_inWS->mutableSample().getOrientedLattice().getUB(); Kernel::Matrix<double> gon = m_inWS->mutableRun().getGoniometerMatrix(); // As per Busing and Levy 1967, q_lab_frame = 2pi * Goniometer * UB * HKL // Therefore, HKL = (2*pi * Goniometer * UB)^-1 * q_lab_frame mat = gon * ub; mat.Invert(); // Divide by 2 PI to account for our new convention, |Q| = 2pi / wl // (December 2011, JZ) mat /= (2 * M_PI); dimensionNames[0] = "H"; dimensionNames[1] = "K"; dimensionNames[2] = "L"; coordinateSystem = Mantid::Kernel::HKL; MDFrameArgument frameArgQLab(HKL::HKLName, Units::Symbol::RLU.ascii()); frame = frameFactory->create(frameArgQLab); } else { MDFrameArgument frameArgQLab(QLab::QLabName, ""); frame = frameFactory->create(frameArgQLab); } // Q in the lab frame is the default, so nothing special to do. if (ws && Append) { // Check that existing workspace dimensions make sense with the desired one // (using the name) if (ws->getDimension(0)->getName() != dimensionNames[0]) throw std::runtime_error("The existing MDEventWorkspace " + ws->getName() + " has different dimensions than were requested! " "Either give a different name for the output, " "or change the OutputDimensions parameter."); } // ------------------- Create the output workspace if needed // ------------------------ if (!ws || !Append) { // Create an output workspace with 3 dimensions. size_t nd = 3; i_out = DataObjects::MDEventFactory::CreateMDWorkspace(nd, "MDLeanEvent"); ws = boost::dynamic_pointer_cast<DataObjects::MDEventWorkspace3Lean>(i_out); // ---------------- Get the extents ------------- std::vector<double> extents = getProperty("Extents"); // Replicate a single min,max into several if (extents.size() == 2) { for (size_t d = 1; d < nd; d++) { extents.push_back(extents[0]); extents.push_back(extents[1]); } } if (extents.size() != nd * 2) throw std::invalid_argument( "You must specify either 2 or 6 extents (min,max)."); // Give all the dimensions for (size_t d = 0; d < nd; d++) { MDHistoDimension *dim = new MDHistoDimension(dimensionNames[d], dimensionNames[d], *frame, static_cast<coord_t>(extents[d * 2]), static_cast<coord_t>(extents[d * 2 + 1]), 10); ws->addDimension(MDHistoDimension_sptr(dim)); } ws->initialize(); // Build up the box controller, using the properties in // BoxControllerSettingsAlgorithm BoxController_sptr bc = ws->getBoxController(); this->setBoxController(bc, m_inWS->getInstrument()); // We always want the box to be split (it will reject bad ones) ws->splitBox(); // Perform minimum recursion depth splitting int minDepth = this->getProperty("MinRecursionDepth"); int maxDepth = this->getProperty("MaxRecursionDepth"); if (minDepth > maxDepth) throw std::invalid_argument( "MinRecursionDepth must be <= MaxRecursionDepth "); ws->setMinRecursionDepth(size_t(minDepth)); } ws->splitBox(); if (!ws) throw std::runtime_error("Error creating a 3D MDEventWorkspace!"); BoxController_sptr bc = ws->getBoxController(); if (!bc) throw std::runtime_error( "Output MDEventWorkspace does not have a BoxController!"); // Cache the extents for speed. m_extentsMin = new coord_t[3]; m_extentsMax = new coord_t[3]; for (size_t d = 0; d < 3; d++) { m_extentsMin[d] = ws->getDimension(d)->getMinimum(); m_extentsMax[d] = ws->getDimension(d)->getMaximum(); } // Copy ExperimentInfo (instrument, run, sample) to the output WS ExperimentInfo_sptr ei(m_inWS->cloneExperimentInfo()); uint16_t runIndex = ws->addExperimentInfo(ei); UNUSED_ARG(runIndex); // ------------------- Cache values that are common for all // --------------------------- // Extract some parameters global to the instrument m_inWS->getInstrument()->getInstrumentParameters(l1, beamline, beamline_norm, samplePos); beamline_norm = beamline.norm(); beamDir = beamline / beamline.norm(); // To get all the detector ID's m_inWS->getInstrument()->getDetectors(allDetectors); // Estimate the number of events in the final workspace size_t totalEvents = m_inWS->size(); if (m_inEventWS && !OneEventPerBin) totalEvents = m_inEventWS->getNumberEvents(); prog = boost::make_shared<Progress>(this, 0, 1.0, totalEvents); // Is the addition of events thread-safe? bool MultiThreadedAdding = m_inWS->threadSafe(); // Create the thread pool that will run all of these. ThreadScheduler *ts = new ThreadSchedulerFIFO(); ThreadPool tp(ts, 0); // To track when to split up boxes this->failedDetectorLookupCount = 0; size_t eventsAdded = 0; size_t approxEventsInOutput = 0; size_t lastNumBoxes = ws->getBoxController()->getTotalNumMDBoxes(); if (DODEBUG) g_log.information() << cputim << ": initial setup. There are " << lastNumBoxes << " MDBoxes.\n"; for (size_t wi = 0; wi < m_inWS->getNumberHistograms(); wi++) { // Get an idea of how many events we'll be adding size_t eventsAdding = m_inWS->blocksize(); if (m_inEventWS && !OneEventPerBin) eventsAdding = m_inEventWS->getEventList(wi).getNumberEvents(); if (MultiThreadedAdding) { // Equivalent to calling "this->convertSpectrum(wi)" boost::function<void()> func = boost::bind(&ConvertToDiffractionMDWorkspace::convertSpectrum, &*this, static_cast<int>(wi)); // Give this task to the scheduler double cost = static_cast<double>(eventsAdding); ts->push(new FunctionTask(func, cost)); } else { // Not thread-safe. Just add right now this->convertSpectrum(static_cast<int>(wi)); } // Keep a running total of how many events we've added eventsAdded += eventsAdding; approxEventsInOutput += eventsAdding; if (bc->shouldSplitBoxes(approxEventsInOutput, eventsAdded, lastNumBoxes)) { if (DODEBUG) g_log.information() << cputim << ": Added tasks worth " << eventsAdded << " events. WorkspaceIndex " << wi << std::endl; // Do all the adding tasks tp.joinAll(); if (DODEBUG) g_log.information() << cputim << ": Performing the addition of these events.\n"; // Now do all the splitting tasks ws->splitAllIfNeeded(ts); if (ts->size() > 0) prog->doReport("Splitting Boxes"); tp.joinAll(); // Count the new # of boxes. lastNumBoxes = ws->getBoxController()->getTotalNumMDBoxes(); if (DODEBUG) g_log.information() << cputim << ": Performing the splitting. There are now " << lastNumBoxes << " boxes.\n"; eventsAdded = 0; } } if (this->failedDetectorLookupCount > 0) { if (this->failedDetectorLookupCount == 1) g_log.warning() << "Unable to find a detector for " << this->failedDetectorLookupCount << " spectrum. It has been skipped." << std::endl; else g_log.warning() << "Unable to find detectors for " << this->failedDetectorLookupCount << " spectra. They have been skipped." << std::endl; } if (DODEBUG) g_log.information() << cputim << ": We've added tasks worth " << eventsAdded << " events.\n"; tp.joinAll(); if (DODEBUG) g_log.information() << cputim << ": Performing the FINAL addition of these events.\n"; // Do a final splitting of everything ws->splitAllIfNeeded(ts); tp.joinAll(); if (DODEBUG) g_log.information() << cputim << ": Performing the FINAL splitting of boxes. There are now " << ws->getBoxController()->getTotalNumMDBoxes() << " boxes\n"; // Recount totals at the end. cputim.reset(); ws->refreshCache(); if (DODEBUG) g_log.information() << cputim << ": Performing the refreshCache().\n"; // TODO: Centroid in parallel, maybe? // ws->getBox()->refreshCentroid(NULL); // if (DODEBUG) g_log.information() << cputim << ": Performing the // refreshCentroid().\n"; if (DODEBUG) { g_log.information() << "Workspace has " << ws->getNPoints() << " events. This took " << cputimtotal << " in total.\n"; std::vector<std::string> stats = ws->getBoxControllerStats(); for (auto &stat : stats) g_log.information() << stat << "\n"; g_log.information() << std::endl; } // Set the special coordinate system. ws->setCoordinateSystem(coordinateSystem); // Save the output setProperty("OutputWorkspace", boost::dynamic_pointer_cast<IMDEventWorkspace>(ws)); // Clean up delete[] m_extentsMin; delete[] m_extentsMax; }
void SliceMD::slice(typename MDEventWorkspace<MDE, nd>::sptr ws) { // Create the ouput workspace typename MDEventWorkspace<OMDE, ond>::sptr outWS( new MDEventWorkspace<OMDE, ond>()); for (size_t od = 0; od < m_binDimensions.size(); od++) { outWS->addDimension(m_binDimensions[od]); } outWS->setCoordinateSystem(ws->getSpecialCoordinateSystem()); outWS->initialize(); // Copy settings from the original box controller BoxController_sptr bc = ws->getBoxController(); // store wrute buffer size for the future // uint64_t writeBufSize = // bc->getFileIO()getDiskBuffer().getWriteBufferSize(); // and disable write buffer (if any) for input MD Events for this algorithm // purposes; // bc->setCacheParameters(1,0); BoxController_sptr obc = outWS->getBoxController(); // Use the "number of bins" as the "split into" parameter for (size_t od = 0; od < m_binDimensions.size(); od++) obc->setSplitInto(od, m_binDimensions[od]->getNBins()); obc->setSplitThreshold(bc->getSplitThreshold()); bool bTakeDepthFromInputWorkspace = getProperty("TakeMaxRecursionDepthFromInput"); int tempDepth = getProperty("MaxRecursionDepth"); size_t maxDepth = bTakeDepthFromInputWorkspace ? bc->getMaxDepth() : size_t(tempDepth); obc->setMaxDepth(maxDepth); // size_t outputSize = writeBufSize; // obc->setCacheParameters(sizeof(OMDE),outputSize); obc->resetNumBoxes(); // Perform the first box splitting outWS->splitBox(); size_t lastNumBoxes = obc->getTotalNumMDBoxes(); // --- File back end ? ---------------- std::string filename = getProperty("OutputFilename"); if (!filename.empty()) { // First save to the NXS file g_log.notice() << "Running SaveMD to create file back-end" << std::endl; IAlgorithm_sptr alg = createChildAlgorithm("SaveMD"); alg->setPropertyValue("Filename", filename); alg->setProperty("InputWorkspace", outWS); alg->setProperty("MakeFileBacked", true); alg->executeAsChildAlg(); if (!obc->isFileBacked()) throw std::runtime_error("SliceMD with file-backed output: Can not set " "up file-backed output workspace "); auto IOptr = obc->getFileIO(); size_t outBufSize = IOptr->getWriteBufferSize(); // the buffer size for resulting workspace; reasonable size is at least 10 // data chunk sizes (nice to verify) if (outBufSize < 10 * IOptr->getDataChunk()) { outBufSize = 10 * IOptr->getDataChunk(); IOptr->setWriteBufferSize(outBufSize); } } // Function defining which events (in the input dimensions) to place in the // output MDImplicitFunction *function = this->getImplicitFunctionForChunk(NULL, NULL); std::vector<API::IMDNode *> boxes; // Leaf-only; no depth limit; with the implicit function passed to it. ws->getBox()->getBoxes(boxes, 1000, true, function); // Sort boxes by file position IF file backed. This reduces seeking time, // hopefully. bool fileBackedWS = bc->isFileBacked(); if (fileBackedWS) API::IMDNode::sortObjByID(boxes); Progress *prog = new Progress(this, 0.0, 1.0, boxes.size()); // The root of the output workspace MDBoxBase<OMDE, ond> *outRootBox = outWS->getBox(); // if target workspace has events, we should count them as added uint64_t totalAdded = outWS->getNEvents(); uint64_t numSinceSplit = 0; // Go through every box for this chunk. // PARALLEL_FOR_IF( !bc->isFileBacked() ) for (int i = 0; i < int(boxes.size()); i++) { MDBox<MDE, nd> *box = dynamic_cast<MDBox<MDE, nd> *>(boxes[i]); // Perform the binning in this separate method. if (box) { // An array to hold the rotated/transformed coordinates coord_t outCenter[ond]; const std::vector<MDE> &events = box->getConstEvents(); typename std::vector<MDE>::const_iterator it = events.begin(); typename std::vector<MDE>::const_iterator it_end = events.end(); for (; it != it_end; it++) { // Cache the center of the event (again for speed) const coord_t *inCenter = it->getCenter(); if (function->isPointContained(inCenter)) { // Now transform to the output dimensions m_transformFromOriginal->apply(inCenter, outCenter); // Create the event OMDE newEvent(it->getSignal(), it->getErrorSquared(), outCenter); // Copy extra data, if any copyEvent(*it, newEvent); // Add it to the workspace outRootBox->addEvent(newEvent); numSinceSplit++; } } box->releaseEvents(); // Ask BC if one needs to split boxes if (obc->shouldSplitBoxes(totalAdded, numSinceSplit, lastNumBoxes)) // if (numSinceSplit > 20000000 || (i == int(boxes.size()-1))) { // This splits up all the boxes according to split thresholds and sizes. Kernel::ThreadScheduler *ts = new ThreadSchedulerFIFO(); ThreadPool tp(ts); outWS->splitAllIfNeeded(ts); tp.joinAll(); // Accumulate stats totalAdded += numSinceSplit; numSinceSplit = 0; lastNumBoxes = obc->getTotalNumMDBoxes(); // Progress reporting if (!fileBackedWS) prog->report(i); } if (fileBackedWS) { if (!(i % 10)) prog->report(i); } } // is box } // for each box in the vector prog->report(); outWS->splitAllIfNeeded(NULL); // Refresh all cache. outWS->refreshCache(); g_log.notice() << totalAdded << " " << OMDE::getTypeName() << "'s added to the output workspace." << std::endl; if (outWS->isFileBacked()) { // Update the file-back-end g_log.notice() << "Running SaveMD" << std::endl; IAlgorithm_sptr alg = createChildAlgorithm("SaveMD"); alg->setProperty("UpdateFileBackEnd", true); alg->setProperty("InputWorkspace", outWS); alg->executeAsChildAlg(); } // return the size of the input workspace write buffer to its initial value // bc->setCacheParameters(sizeof(MDE),writeBufSize); this->setProperty("OutputWorkspace", boost::dynamic_pointer_cast<IMDEventWorkspace>(outWS)); delete prog; }
void BinMD::binByIterating(typename MDEventWorkspace<MDE, nd>::sptr ws) { BoxController_sptr bc = ws->getBoxController(); // store exisiting write buffer size for the future // uint64_t writeBufSize =bc->getDiskBuffer().getWriteBufferSize(); // and disable write buffer (if any) for input MD Events for this algorithm // purposes; // bc->setCacheParameters(1,0); // Cache some data to speed up accessing them a bit indexMultiplier = new size_t[m_outD]; for (size_t d = 0; d < m_outD; d++) { if (d > 0) indexMultiplier[d] = outWS->getIndexMultiplier()[d - 1]; else indexMultiplier[d] = 1; } signals = outWS->getSignalArray(); errors = outWS->getErrorSquaredArray(); numEvents = outWS->getNumEventsArray(); // Start with signal/error/numEvents at 0.0 outWS->setTo(0.0, 0.0, 0.0); // The dimension (in the output workspace) along which we chunk for parallel // processing // TODO: Find the smartest dimension to chunk against size_t chunkDimension = 0; // How many bins (in that dimension) per chunk. // Try to split it so each core will get 2 tasks: int chunkNumBins = int(m_binDimensions[chunkDimension]->getNBins() / (PARALLEL_GET_MAX_THREADS * 2)); if (chunkNumBins < 1) chunkNumBins = 1; // Do we actually do it in parallel? bool doParallel = getProperty("Parallel"); // Not if file-backed! if (bc->isFileBacked()) doParallel = false; if (!doParallel) chunkNumBins = int(m_binDimensions[chunkDimension]->getNBins()); // Total number of steps size_t progNumSteps = 0; if (prog) prog->setNotifyStep(0.1); if (prog) prog->resetNumSteps(100, 0.00, 1.0); // Run the chunks in parallel. There is no overlap in the output workspace so // it is thread safe to write to it.. // cppcheck-suppress syntaxError PRAGMA_OMP( parallel for schedule(dynamic,1) if (doParallel) ) for (int chunk = 0; chunk < int(m_binDimensions[chunkDimension]->getNBins()); chunk += chunkNumBins) { PARALLEL_START_INTERUPT_REGION // Region of interest for this chunk. std::vector<size_t> chunkMin(m_outD); std::vector<size_t> chunkMax(m_outD); for (size_t bd = 0; bd < m_outD; bd++) { // Same limits in the other dimensions chunkMin[bd] = 0; chunkMax[bd] = m_binDimensions[bd]->getNBins(); } // Parcel out a chunk in that single dimension dimension chunkMin[chunkDimension] = size_t(chunk); if (size_t(chunk + chunkNumBins) > m_binDimensions[chunkDimension]->getNBins()) chunkMax[chunkDimension] = m_binDimensions[chunkDimension]->getNBins(); else chunkMax[chunkDimension] = size_t(chunk + chunkNumBins); // Build an implicit function (it needs to be in the space of the // MDEventWorkspace) MDImplicitFunction *function = this->getImplicitFunctionForChunk(chunkMin.data(), chunkMax.data()); // Use getBoxes() to get an array with a pointer to each box std::vector<API::IMDNode *> boxes; // Leaf-only; no depth limit; with the implicit function passed to it. ws->getBox()->getBoxes(boxes, 1000, true, function); // Sort boxes by file position IF file backed. This reduces seeking time, // hopefully. if (bc->isFileBacked()) API::IMDNode::sortObjByID(boxes); // For progress reporting, the # of boxes if (prog) { PARALLEL_CRITICAL(BinMD_progress) { g_log.debug() << "Chunk " << chunk << ": found " << boxes.size() << " boxes within the implicit function.\n"; progNumSteps += boxes.size(); prog->setNumSteps(progNumSteps); } } // Go through every box for this chunk. for (auto &boxe : boxes) { MDBox<MDE, nd> *box = dynamic_cast<MDBox<MDE, nd> *>(boxe); // Perform the binning in this separate method. if (box && !box->getIsMasked()) this->binMDBox(box, chunkMin.data(), chunkMax.data()); // Progress reporting if (prog) prog->report(); // For early cancelling of the loop if (this->m_cancel) break; } // for each box in the vector PARALLEL_END_INTERUPT_REGION } // for each chunk in parallel PARALLEL_CHECK_INTERUPT_REGION // Now the implicit function if (implicitFunction) { if (prog) prog->report("Applying implicit function."); signal_t nan = std::numeric_limits<signal_t>::quiet_NaN(); outWS->applyImplicitFunction(implicitFunction, nan, nan); } // return the size of the input workspace write buffer to its initial value // bc->setCacheParameters(sizeof(MDE),writeBufSize); }
/** Perform the merging, but clone the initial workspace and use the same splitting * as its structure is equivalent to the partial box structures. * * @param ws :: first MDEventWorkspace in the list to merge to. * @param outputFile :: the name of the output file where file-based workspace should be saved */ void MergeMDFiles::doExecByCloning(Mantid::API::IMDEventWorkspace_sptr ws,const std::string &outputFile) { m_OutIWS = ws; m_MDEventType = ws->getEventTypeName(); // Run the tasks in parallel? TODO: enable //bool Parallel = this->getProperty("Parallel"); // Fix the box controller settings in the output workspace so that it splits normally BoxController_sptr bc = ws->getBoxController(); // set up internal variables characterizing the workspace. m_nDims = static_cast<int>(bc->getNDims()); // Fix the max depth to something bigger. bc->setMaxDepth(20); bc->setSplitThreshold(5000); auto saver = boost::shared_ptr<API::IBoxControllerIO>(new MDEvents::BoxControllerNeXusIO(bc.get())); saver->setDataType(sizeof(coord_t),m_MDEventType); if(m_fileBasedTargetWS) { bc->setFileBacked(saver,outputFile); // Complete the file-back-end creation. g_log.notice() << "Setting cache to 400 MB write." << std::endl; bc->getFileIO()->setWriteBufferSize(400000000/m_OutIWS->sizeofEvent()); } /* else { saver->openFile(outputFile,"w"); }*/ // Init box structure used for memory/file space calculations m_BoxStruct.initFlatStructure(ws,outputFile); // First, load all the box data and experiment info and calculate file positions of the target workspace this->loadBoxData(); size_t numBoxes = m_BoxStruct.getNBoxes(); // Progress report based on events processed. this->prog = new Progress(this, 0.1, 0.9, size_t(numBoxes)); prog->setNotifyStep(0.1); // For tracking progress //uint64_t totalEventsInTasks = 0; // Prepare thread pool CPUTimer overallTime; ThreadSchedulerFIFO * ts = new ThreadSchedulerFIFO(); ThreadPool tp(ts); Kernel::DiskBuffer *DiskBuf(NULL); if(m_fileBasedTargetWS) { DiskBuf = bc->getFileIO(); } this->totalLoaded = 0; std::vector<API::IMDNode *> &boxes = m_BoxStruct.getBoxes(); for(size_t ib=0;ib<numBoxes;ib++) { auto box = boxes[ib]; if(!box->isBox())continue; // load all contributed events into current box; this->loadEventsFromSubBoxes(boxes[ib]); if(DiskBuf) { if(box->getDataInMemorySize()>0) { // data position has been already precalculated box->getISaveable()->save(); box->clearDataFromMemory(); //Kernel::ISaveable *Saver = box->getISaveable(); //DiskBuf->toWrite(Saver); } } //else //{ size_t ID = box->getID(); // uint64_t filePosition = targetEventIndexes[2*ID]; // box->saveAt(saver.get(), filePosition); //} // //if (!Parallel) //{ // // Run the task serially only // task->run(); // delete task; //} //else //{ // // Enqueue to run in parallel (at the joinAll() call below). // ts->push(task); //} prog->reportIncrement(ib,"Loading and merging box data"); } if(DiskBuf) { DiskBuf->flushCache(); bc->getFileIO()->flushData(); } //// Run any final tasks //tp.joinAll(); g_log.information() << overallTime << " to do all the adding." << std::endl; // Close any open file handle clearEventLoaders(); // Finish things up this->finalizeOutput(outputFile); }
void LoadMD::doLoad(typename MDEventWorkspace<MDE, nd>::sptr ws) { // Are we using the file back end? bool fileBackEnd = getProperty("FileBackEnd"); if (fileBackEnd && m_BoxStructureAndMethadata) throw std::invalid_argument("Combination of BoxStructureOnly or " "MetaDataOnly were set to TRUE with " "fileBackEnd " ": this is not possible."); CPUTimer tim; auto prog = new Progress(this, 0.0, 1.0, 100); prog->report("Opening file."); std::string title; try { m_file->getAttr("title", title); } catch (std::exception &) { // Leave the title blank if error on loading } ws->setTitle(title); // Load the WorkspaceHistory "process" if (this->getProperty("LoadHistory")) { ws->history().loadNexus(m_file.get()); } this->loadAffineMatricies(boost::dynamic_pointer_cast<IMDWorkspace>(ws)); m_file->closeGroup(); m_file->close(); // Add each of the dimension for (size_t d = 0; d < nd; d++) ws->addDimension(m_dims[d]); // Coordinate system ws->setCoordinateSystem(m_coordSystem); // ----------------------------------------- Box Structure // ------------------------------ prog->report("Reading box structure from HDD."); MDBoxFlatTree FlatBoxTree; int nDims = static_cast<int>(nd); // should be safe FlatBoxTree.loadBoxStructure(m_filename, nDims, MDE::getTypeName()); BoxController_sptr bc = ws->getBoxController(); bc->fromXMLString(FlatBoxTree.getBCXMLdescr()); prog->report("Restoring box structure and connectivity"); std::vector<API::IMDNode *> boxTree; FlatBoxTree.restoreBoxTree(boxTree, bc, fileBackEnd, m_BoxStructureAndMethadata); size_t numBoxes = boxTree.size(); // ---------------------------------------- DEAL WITH BOXES // ------------------------------------ if (fileBackEnd) { // TODO:: call to the file format factory auto loader = boost::shared_ptr<API::IBoxControllerIO>( new DataObjects::BoxControllerNeXusIO(bc.get())); loader->setDataType(sizeof(coord_t), MDE::getTypeName()); bc->setFileBacked(loader, m_filename); // boxes have been already made file-backed when restoring the boxTree; // How much memory for the cache? { // TODO: Clean up, only a write buffer now double mb = getProperty("Memory"); // Defaults have changed, default disk buffer size should be 10 data // chunks TODO: find optimal, 100 may be better. if (mb <= 0) mb = double(10 * loader->getDataChunk() * sizeof(MDE)) / double(1024 * 1024); // Express the cache memory in units of number of events. uint64_t cacheMemory = static_cast<uint64_t>((mb * 1024. * 1024.) / sizeof(MDE)) + 1; // Set these values in the diskMRU bc->getFileIO()->setWriteBufferSize(cacheMemory); g_log.information() << "Setting a DiskBuffer cache size of " << mb << " MB, or " << cacheMemory << " events.\n"; } } // Not file back end else if (!m_BoxStructureAndMethadata) { // ---------------------------------------- READ IN THE BOXES // ------------------------------------ // TODO:: call to the file format factory auto loader = file_holder_type(new DataObjects::BoxControllerNeXusIO(bc.get())); loader->setDataType(sizeof(coord_t), MDE::getTypeName()); loader->openFile(m_filename, "r"); const std::vector<uint64_t> &BoxEventIndex = FlatBoxTree.getEventIndex(); prog->setNumSteps(numBoxes); for (size_t i = 0; i < numBoxes; i++) { prog->report(); MDBox<MDE, nd> *box = dynamic_cast<MDBox<MDE, nd> *>(boxTree[i]); if (!box) continue; if (BoxEventIndex[2 * i + 1] > 0) // Load in memory NOT using the file as the back-end, { boxTree[i]->reserveMemoryForLoad(BoxEventIndex[2 * i + 1]); boxTree[i]->loadAndAddFrom( loader.get(), BoxEventIndex[2 * i], static_cast<size_t>(BoxEventIndex[2 * i + 1])); } } loader->closeFile(); } else // box structure and metadata only { } g_log.debug() << tim << " to create all the boxes and fill them with events.\n"; // Box of ID 0 is the head box. ws->setBox(boxTree[0]); // Make sure the max ID is ok for later ID generation bc->setMaxId(numBoxes); // end-of bMetaDataOnly // Refresh cache // TODO:if(!fileBackEnd)ws->refreshCache(); ws->refreshCache(); g_log.debug() << tim << " to refreshCache(). " << ws->getNPoints() << " points after refresh.\n"; g_log.debug() << tim << " to finish up.\n"; delete prog; }
void SaveMD::doSaveEvents(typename MDEventWorkspace<MDE, nd>::sptr ws) { std::string filename = getPropertyValue("Filename"); bool update = getProperty("UpdateFileBackEnd"); bool MakeFileBacked = getProperty("MakeFileBacked"); bool wsIsFileBacked = ws->isFileBacked(); if (update && MakeFileBacked) throw std::invalid_argument( "Please choose either UpdateFileBackEnd or MakeFileBacked, not both."); if (MakeFileBacked && wsIsFileBacked) throw std::invalid_argument( "You picked MakeFileBacked but the workspace is already file-backed!"); BoxController_sptr bc = ws->getBoxController(); if (!wsIsFileBacked) { // Erase the file if it exists Poco::File oldFile(filename); if (oldFile.exists()) oldFile.remove(); } auto prog = new Progress(this, 0.0, 0.05, 1); if (update) // workspace has its own file and ignores any changes to the // algorithm parameters { if (!ws->isFileBacked()) throw std::runtime_error(" attempt to update non-file backed workspace"); filename = bc->getFileIO()->getFileName(); } //----------------------------------------------------------------------------------------------------- // create or open WS group and put there additional information about WS and // its dimensions int nDims = static_cast<int>(nd); bool data_exist; auto file = file_holder_type(MDBoxFlatTree::createOrOpenMDWSgroup( filename, nDims, MDE::getTypeName(), false, data_exist)); // Save each NEW ExperimentInfo to a spot in the file MDBoxFlatTree::saveExperimentInfos(file.get(), ws); if (!update || !data_exist) { MDBoxFlatTree::saveWSGenericInfo(file.get(), ws); } file->closeGroup(); file->close(); MDBoxFlatTree BoxFlatStruct; //----------------------------------------------------------------------------------------------------- if (update) // the workspace is already file backed; { // remove all boxes from the DiskBuffer. DB will calculate boxes positions // on HDD. bc->getFileIO()->flushCache(); // flatten the box structure; this will remember boxes file positions in the // box structure BoxFlatStruct.initFlatStructure(ws, filename); } else // not file backed; { // the boxes file positions are unknown and we need to calculate it. BoxFlatStruct.initFlatStructure(ws, filename); // create saver class auto Saver = boost::shared_ptr<API::IBoxControllerIO>( new DataObjects::BoxControllerNeXusIO(bc.get())); Saver->setDataType(sizeof(coord_t), MDE::getTypeName()); if (MakeFileBacked) { // store saver with box controller bc->setFileBacked(Saver, filename); // get access to boxes array std::vector<API::IMDNode *> &boxes = BoxFlatStruct.getBoxes(); // calculate the position of the boxes on file, indicating to make them // saveable and that the boxes were not saved. BoxFlatStruct.setBoxesFilePositions(true); prog->resetNumSteps(boxes.size(), 0.06, 0.90); for (auto &boxe : boxes) { auto saveableTag = boxe->getISaveable(); if (saveableTag) // only boxes can be saveable { // do not spend time on empty boxes if (boxe->getDataInMemorySize() == 0) continue; // save boxes directly using the boxes file postion, precalculated in // boxFlatStructure. saveableTag->save(); // remove boxes data from memory. This will actually correctly set the // tag indicatin that data were not loaded. saveableTag->clearDataFromMemory(); // put boxes into write buffer wich will save them when necessary // Saver->toWrite(saveTag); prog->report("Saving Box"); } } // remove everything from diskBuffer; (not sure if it really necessary // but just in case , should not make any harm) Saver->flushCache(); // drop NeXus on HDD (not sure if it really necessary but just in case ) Saver->flushData(); } else // just save data, and finish with it { Saver->openFile(filename, "w"); BoxFlatStruct.setBoxesFilePositions(false); std::vector<API::IMDNode *> &boxes = BoxFlatStruct.getBoxes(); std::vector<uint64_t> &eventIndex = BoxFlatStruct.getEventIndex(); prog->resetNumSteps(boxes.size(), 0.06, 0.90); for (size_t i = 0; i < boxes.size(); i++) { if (eventIndex[2 * i + 1] == 0) continue; boxes[i]->saveAt(Saver.get(), eventIndex[2 * i]); prog->report("Saving Box"); } Saver->closeFile(); } } // -------------- Save Box Structure ------------------------------------- // OK, we've filled these big arrays of data representing flat box structrre. // Save them. progress(0.91, "Writing Box Data"); prog->resetNumSteps(8, 0.92, 1.00); // Save box structure; BoxFlatStruct.saveBoxStructure(filename); delete prog; ws->setFileNeedsUpdating(false); }
/** * Create an output event workspace filled with data simulated with the fitting * function. * @param baseName :: The base name for the workspace * @param inputWorkspace :: The input workspace. * @param values :: The calculated values * @param outputWorkspacePropertyName :: The property name */ boost::shared_ptr<API::Workspace> FitMD::createEventOutputWorkspace( const std::string &baseName, const API::IMDEventWorkspace &inputWorkspace, const API::FunctionValues &values, const std::string &outputWorkspacePropertyName) { auto outputWS = MDEventFactory::CreateMDWorkspace(inputWorkspace.getNumDims(), "MDEvent"); // Add events // TODO: Generalize to ND (the current framework is a bit limiting) auto mdWS = boost::dynamic_pointer_cast< DataObjects::MDEventWorkspace<DataObjects::MDEvent<4>, 4>>(outputWS); if (!mdWS) { return boost::shared_ptr<API::Workspace>(); } // Bins extents and meta data for (size_t i = 0; i < 4; ++i) { boost::shared_ptr<const Geometry::IMDDimension> inputDim = inputWorkspace.getDimension(i); Geometry::MDHistoDimensionBuilder builder; builder.setName(inputDim->getName()); builder.setId(inputDim->getDimensionId()); builder.setUnits(inputDim->getUnits()); builder.setNumBins(inputDim->getNBins()); builder.setMin(inputDim->getMinimum()); builder.setMax(inputDim->getMaximum()); builder.setFrameName(inputDim->getMDFrame().name()); outputWS->addDimension(builder.create()); } // Run information outputWS->copyExperimentInfos(inputWorkspace); // Coordinates outputWS->setCoordinateSystem(inputWorkspace.getSpecialCoordinateSystem()); // Set sensible defaults for splitting behaviour BoxController_sptr bc = outputWS->getBoxController(); bc->setSplitInto(3); bc->setSplitThreshold(3000); outputWS->initialize(); outputWS->splitBox(); auto inputIter = inputWorkspace.createIterator(); size_t resultValueIndex(0); const float errorSq = 0.0; do { const size_t numEvents = inputIter->getNumEvents(); const float signal = static_cast<float>(values.getCalculated(resultValueIndex)); for (size_t i = 0; i < numEvents; ++i) { coord_t centers[4] = { inputIter->getInnerPosition(i, 0), inputIter->getInnerPosition(i, 1), inputIter->getInnerPosition(i, 2), inputIter->getInnerPosition(i, 3)}; mdWS->addEvent(MDEvent<4>(signal, errorSq, inputIter->getInnerRunIndex(i), inputIter->getInnerDetectorID(i), centers)); } ++resultValueIndex; } while (inputIter->next()); delete inputIter; // This splits up all the boxes according to split thresholds and sizes. auto threadScheduler = new Kernel::ThreadSchedulerFIFO(); Kernel::ThreadPool threadPool(threadScheduler); outputWS->splitAllIfNeeded(threadScheduler); threadPool.joinAll(); outputWS->refreshCache(); // Store it if (!outputWorkspacePropertyName.empty()) { declareProperty( new API::WorkspaceProperty<API::IMDEventWorkspace>( outputWorkspacePropertyName, "", Direction::Output), "Name of the output Workspace holding resulting simulated spectrum"); m_manager->setPropertyValue(outputWorkspacePropertyName, baseName + "Workspace"); m_manager->setProperty(outputWorkspacePropertyName, outputWS); } return outputWS; }
void BinToMDHistoWorkspace::binByIterating(typename MDEventWorkspace<MDE, nd>::sptr ws) { BoxController_sptr bc = ws->getBoxController(); // Start with signal at 0.0 outWS->setTo(0.0, 0.0); // Cache some data to speed up accessing them a bit indexMultiplier = new size_t[outD]; for (size_t d=0; d<outD; d++) { if (d > 0) indexMultiplier[d] = outWS->getIndexMultiplier()[d-1]; else indexMultiplier[d] = 1; } signals = outWS->getSignalArray(); errors = outWS->getErrorSquaredArray(); // The dimension (in the output workspace) along which we chunk for parallel processing // TODO: Find the smartest dimension to chunk against size_t chunkDimension = 0; // How many bins (in that dimension) per chunk. // Try to split it so each core will get 2 tasks: int chunkNumBins = int(binDimensions[chunkDimension]->getNBins() / (Mantid::Kernel::ThreadPool::getNumPhysicalCores() * 2)); if (chunkNumBins < 1) chunkNumBins = 1; // Do we actually do it in parallel? bool doParallel = getProperty("Parallel"); // Not if file-backed! if (bc->isFileBacked()) doParallel = false; if (!doParallel) chunkNumBins = int(binDimensions[chunkDimension]->getNBins()); // Total number of steps size_t progNumSteps = 0; if (prog) prog->setNotifyStep(0.1); if (prog) prog->resetNumSteps(100, 0.00, 1.0); // Run the chunks in parallel. There is no overlap in the output workspace so it is // thread safe to write to it.. PRAGMA_OMP( parallel for schedule(dynamic,1) if (doParallel) ) for(int chunk=0; chunk < int(binDimensions[chunkDimension]->getNBins()); chunk += chunkNumBins) { PARALLEL_START_INTERUPT_REGION // Region of interest for this chunk. size_t * chunkMin = new size_t[outD]; size_t * chunkMax = new size_t[outD]; for (size_t bd=0; bd<outD; bd++) { // Same limits in the other dimensions chunkMin[bd] = 0; chunkMax[bd] = binDimensions[bd]->getNBins(); } // Parcel out a chunk in that single dimension dimension chunkMin[chunkDimension] = size_t(chunk); if (size_t(chunk+chunkNumBins) > binDimensions[chunkDimension]->getNBins()) chunkMax[chunkDimension] = binDimensions[chunkDimension]->getNBins(); else chunkMax[chunkDimension] = size_t(chunk+chunkNumBins); // Build an implicit function (it needs to be in the space of the MDEventWorkspace) MDImplicitFunction * function = this->getImplicitFunctionForChunk(chunkMin, chunkMax); // Use getBoxes() to get an array with a pointer to each box std::vector<IMDBox<MDE,nd>*> boxes; // Leaf-only; no depth limit; with the implicit function passed to it. ws->getBox()->getBoxes(boxes, 1000, true, function); // Sort boxes by file position IF file backed. This reduces seeking time, hopefully. if (bc->isFileBacked()) IMDBox<MDE, nd>::sortBoxesByFilePos(boxes); // For progress reporting, the # of boxes if (prog) { PARALLEL_CRITICAL(BinToMDHistoWorkspace_progress) { std::cout << "Chunk " << chunk << ": found " << boxes.size() << " boxes within the implicit function." << std::endl; progNumSteps += boxes.size(); prog->setNumSteps( progNumSteps ); } } // Go through every box for this chunk. for (size_t i=0; i<boxes.size(); i++) { MDBox<MDE,nd> * box = dynamic_cast<MDBox<MDE,nd> *>(boxes[i]); // Perform the binning in this separate method. if (box) this->binMDBox(box, chunkMin, chunkMax); // Progress reporting if (prog) prog->report(); }// for each box in the vector PARALLEL_END_INTERUPT_REGION } // for each chunk in parallel PARALLEL_CHECK_INTERUPT_REGION // Now the implicit function if (implicitFunction) { prog->report("Applying implicit function."); signal_t nan = std::numeric_limits<signal_t>::quiet_NaN(); outWS->applyImplicitFunction(implicitFunction, nan, nan); } }