void MinusMD::doMinus(typename MDEventWorkspace<MDE, nd>::sptr ws) { typename MDEventWorkspace<MDE, nd>::sptr ws1 = ws; typename MDEventWorkspace<MDE, nd>::sptr ws2 = boost::dynamic_pointer_cast<MDEventWorkspace<MDE, nd> >(m_operand_event); if (!ws1 || !ws2) throw std::runtime_error("Incompatible workspace types passed to MinusMD."); MDBoxBase<MDE,nd> * box1 = ws1->getBox(); MDBoxBase<MDE,nd> * box2 = ws2->getBox(); Progress prog(this, 0.0, 0.4, box2->getBoxController()->getTotalNumMDBoxes()); // How many events you started with size_t initial_numEvents = ws1->getNPoints(); // Make a leaf-only iterator through all boxes with events in the RHS workspace MDBoxIterator<MDE,nd> it2(box2, 1000, true); do { MDBox<MDE,nd> * box = dynamic_cast<MDBox<MDE,nd> *>(it2.getBox()); if (box) { // Copy the events from WS2 and add them into WS1 const std::vector<MDE> & events = box->getConstEvents(); // Perform a copy while flipping the signal std::vector<MDE> eventsCopy; eventsCopy.reserve(events.size()); for (auto it = events.begin(); it != events.end(); it++) { MDE eventCopy(*it); eventCopy.setSignal( -eventCopy.getSignal()); eventsCopy.push_back(eventCopy); } // Add events, with bounds checking box1->addEvents(eventsCopy); box->releaseEvents(); } prog.report("Adding Events"); } while (it2.next()); this->progress(0.41, "Splitting Boxes"); Progress * prog2 = new Progress(this, 0.4, 0.9, 100); ThreadScheduler * ts = new ThreadSchedulerFIFO(); ThreadPool tp(ts, 0, prog2); ws1->splitAllIfNeeded(ts); prog2->resetNumSteps( ts->size(), 0.4, 0.6); tp.joinAll(); this->progress(0.95, "Refreshing cache"); ws1->refreshCache(); // Set a marker that the file-back-end needs updating if the # of events changed. if (ws1->getNPoints() != initial_numEvents) ws1->setFileNeedsUpdating(true); }
/** 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 PlusMD::doPlus(typename MDEventWorkspace<MDE, nd>::sptr ws) { typename MDEventWorkspace<MDE, nd>::sptr ws1 = ws; typename MDEventWorkspace<MDE, nd>::sptr ws2 = boost::dynamic_pointer_cast<MDEventWorkspace<MDE, nd> >(m_operand_event); if (!ws1 || !ws2) throw std::runtime_error("Incompatible workspace types passed to PlusMD."); MDBoxBase<MDE,nd> * box1 = ws1->getBox(); MDBoxBase<MDE,nd> * box2 = ws2->getBox(); Progress prog(this, 0.0, 0.4, box2->getBoxController()->getTotalNumMDBoxes()); // How many events you started with size_t initial_numEvents = ws1->getNPoints(); // Make a leaf-only iterator through all boxes with events in the RHS workspace MDBoxIterator<MDE,nd> it2(box2, 1000, true); do { MDBox<MDE,nd> * box = dynamic_cast<MDBox<MDE,nd> *>(it2.getBox()); if (box) { // Copy the events from WS2 and add them into WS1 const std::vector<MDE> & events = box->getConstEvents(); // Add events, with bounds checking box1->addEvents(events); box->releaseEvents(); } prog.report("Adding Events"); } while (it2.next()); this->progress(0.41, "Splitting Boxes"); Progress * prog2 = new Progress(this, 0.4, 0.9, 100); ThreadScheduler * ts = new ThreadSchedulerFIFO(); ThreadPool tp(ts, 0, prog2); ws1->splitAllIfNeeded(ts); prog2->resetNumSteps( ts->size(), 0.4, 0.6); tp.joinAll(); // // Now we need to save all the data that was not saved before. // if (ws1->isFileBacked()) // { // // Flush anything else in the to-write buffer // BoxController_sptr bc = ws1->getBoxController(); // // prog.resetNumSteps(bc->getTotalNumMDBoxes(), 0.6, 1.0); // MDBoxIterator<MDE,nd> it1(box1, 1000, true); // while (true) // { // MDBox<MDE,nd> * box = dynamic_cast<MDBox<MDE,nd> *>(it1.getBox()); // if (box) // { // // Something was maybe added to this box // if (box->getEventVectorSize() > 0) // { // // By getting the events, this will merge the newly added and the cached events. // box->getEvents(); // // The MRU to-write cache will optimize writes by reducing seek times // box->releaseEvents(); // } // } // prog.report("Saving"); // if (!it1.next()) break; // } // //bc->getDiskBuffer().flushCache(); // // Flush the data writes to disk. // box1->flushData(); // } this->progress(0.95, "Refreshing cache"); ws1->refreshCache(); // Set a marker that the file-back-end needs updating if the # of events changed. if (ws1->getNPoints() != initial_numEvents) ws1->setFileNeedsUpdating(true); }