/** 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; }