void FindPeaksMD::findPeaks(typename MDEventWorkspace<MDE, nd>::sptr ws) { if (nd < 3) throw std::invalid_argument("Workspace must have at least 3 dimensions."); progress(0.01, "Refreshing Centroids"); // TODO: This might be slow, progress report? // Make sure all centroids are fresh ws->getBox()->refreshCentroid(); typedef IMDBox<MDE,nd>* boxPtr; if (ws->getNumExperimentInfo() == 0) throw std::runtime_error("No instrument was found in the MDEventWorkspace. Cannot find peaks."); // TODO: Do we need to pick a different instrument info? ExperimentInfo_sptr ei = ws->getExperimentInfo(0); // Instrument associated with workspace Geometry::Instrument_const_sptr inst = ei->getInstrument(); // Find the run number int runNumber = ei->getRunNumber(); // Check that the workspace dimensions are in Q-sample-frame or Q-lab-frame. eDimensionType dimType; std::string dim0 = ws->getDimension(0)->getName(); if (dim0 == "H") { dimType = HKL; throw std::runtime_error("Cannot find peaks in a workspace that is already in HKL space."); } else if (dim0 == "Q_lab_x") { dimType = QLAB; } else if (dim0 == "Q_sample_x") dimType = QSAMPLE; else throw std::runtime_error("Unexpected dimensions: need either Q_lab_x or Q_sample_x."); // Find the goniometer rotation matrix Mantid::Kernel::Matrix<double> goniometer(3,3, true); // Default IDENTITY matrix try { goniometer = ei->mutableRun().getGoniometerMatrix(); } catch (std::exception & e) { g_log.warning() << "Error finding goniometer matrix. It will not be set in the peaks found." << std::endl; g_log.warning() << e.what() << std::endl; } /// Arbitrary scaling factor for density to make more manageable numbers, especially for older file formats. signal_t densityScalingFactor = 1e-6; // Calculate a threshold below which a box is too diffuse to be considered a peak. signal_t thresholdDensity = 0.0; thresholdDensity = ws->getBox()->getSignalNormalized() * DensityThresholdFactor * densityScalingFactor; g_log.notice() << "Threshold signal density: " << thresholdDensity << std::endl; // We will fill this vector with pointers to all the boxes (up to a given depth) typename std::vector<boxPtr> boxes; // Get all the MDboxes progress(0.10, "Getting Boxes"); ws->getBox()->getBoxes(boxes, 1000, true); // TODO: Here keep only the boxes > e.g. 3 * mean. typedef std::pair<double, boxPtr> dens_box; // Map that will sort the boxes by increasing density. The key = density; value = box *. typename std::multimap<double, boxPtr> sortedBoxes; progress(0.20, "Sorting Boxes by Density"); typename std::vector<boxPtr>::iterator it1; typename std::vector<boxPtr>::iterator it1_end = boxes.end(); for (it1 = boxes.begin(); it1 != it1_end; it1++) { boxPtr box = *it1; double density = box->getSignalNormalized() * densityScalingFactor; // Skip any boxes with too small a signal density. if (density > thresholdDensity) sortedBoxes.insert(dens_box(density,box)); } // List of chosen possible peak boxes. std::vector<boxPtr> peakBoxes; prog = new Progress(this, 0.30, 0.95, MaxPeaks); int64_t numBoxesFound = 0; // Now we go (backwards) through the map // e.g. from highest density down to lowest density. typename std::multimap<double, boxPtr>::reverse_iterator it2; typename std::multimap<double, boxPtr>::reverse_iterator it2_end = sortedBoxes.rend(); for (it2 = sortedBoxes.rbegin(); it2 != it2_end; it2++) { signal_t density = it2->first; boxPtr box = it2->second; #ifndef MDBOX_TRACK_CENTROID coord_t boxCenter[nd]; box->calculateCentroid(boxCenter); #else const coord_t * boxCenter = box->getCentroid(); #endif // Compare to all boxes already picked. bool badBox = false; for (typename std::vector<boxPtr>::iterator it3=peakBoxes.begin(); it3 != peakBoxes.end(); it3++) { #ifndef MDBOX_TRACK_CENTROID coord_t otherCenter[nd]; (*it3)->calculateCentroid(otherCenter); #else const coord_t * otherCenter = (*it3)->getCentroid(); #endif // Distance between this box and a box we already put in. coord_t distSquared = 0.0; for (size_t d=0; d<nd; d++) { coord_t dist = otherCenter[d] - boxCenter[d]; distSquared += (dist * dist); } // Reject this box if it is too close to another previously found box. if (distSquared < peakRadiusSquared) { badBox = true; break; } } // The box was not rejected for another reason. if (!badBox) { if (numBoxesFound++ >= MaxPeaks) { g_log.notice() << "Number of peaks found exceeded the limit of " << MaxPeaks << ". Stopping peak finding." << std::endl; break; } peakBoxes.push_back(box); g_log.information() << "Found box at "; for (size_t d=0; d<nd; d++) g_log.information() << (d>0?",":"") << boxCenter[d]; g_log.information() << "; Density = " << density << std::endl; // Report progres for each box found. prog->report("Finding Peaks"); } } prog->resetNumSteps(numBoxesFound, 0.95, 1.0); // Copy the instrument, sample, run to the peaks workspace. peakWS->copyExperimentInfoFrom(ei.get()); // --- Convert the "boxes" to peaks ---- for (typename std::vector<boxPtr>::iterator it3=peakBoxes.begin(); it3 != peakBoxes.end(); it3++) { // The center of the box = Q in the lab frame boxPtr box = *it3; #ifndef MDBOX_TRACK_CENTROID coord_t boxCenter[nd]; box->calculateCentroid(boxCenter); #else const coord_t * boxCenter = box->getCentroid(); #endif V3D Q(boxCenter[0], boxCenter[1], boxCenter[2]); // Create a peak and add it // Empty starting peak. Peak p; try { if (dimType == QLAB) { // Build using the Q-lab-frame constructor p = Peak(inst, Q); // Save gonio matrix for later p.setGoniometerMatrix(goniometer); } else if (dimType == QSAMPLE) { // Build using the Q-sample-frame constructor p = Peak(inst, Q, goniometer); } } catch (std::exception &e) { g_log.notice() << "Error creating peak at " << Q << " because of '" << e.what() << "'. Peak will be skipped." << std::endl; continue; } try { // Look for a detector p.findDetector(); } catch (...) { /* Ignore errors in ray-tracer TODO: Handle for WISH data later */ } // The "bin count" used will be the box density. p.setBinCount( box->getSignalNormalized() * densityScalingFactor); // Save the run number found before. p.setRunNumber(runNumber); peakWS->addPeak(p); // Report progres for each box found. prog->report("Adding Peaks"); } // for each box found }