diff --git a/Code/Mantid/Framework/API/inc/MantidAPI/IMDHistoWorkspace.h b/Code/Mantid/Framework/API/inc/MantidAPI/IMDHistoWorkspace.h index e58fa0a91b9c87bc7f9bf5890d0cb114e8e4e71d..5da50570e195a3d5e866a35b85f2470a02a5ecc6 100644 --- a/Code/Mantid/Framework/API/inc/MantidAPI/IMDHistoWorkspace.h +++ b/Code/Mantid/Framework/API/inc/MantidAPI/IMDHistoWorkspace.h @@ -78,6 +78,8 @@ namespace API virtual double & operator [](const size_t & index) = 0; virtual void setCoordinateSystem(const Mantid::API::SpecialCoordinateSystem coordinateSystem) = 0; + + boost::shared_ptr<IMDHistoWorkspace> cloneShape() const = 0; protected: virtual const std::string toString() const; }; diff --git a/Code/Mantid/Framework/Crystal/inc/MantidCrystal/ConnectedComponentLabeling.h b/Code/Mantid/Framework/Crystal/inc/MantidCrystal/ConnectedComponentLabeling.h index c483331e55e52cb0c3c68e0906c991a79e6116c1..39d11cf2bbb8457e1dfb6953ba081fdedf7a7fa2 100644 --- a/Code/Mantid/Framework/Crystal/inc/MantidCrystal/ConnectedComponentLabeling.h +++ b/Code/Mantid/Framework/Crystal/inc/MantidCrystal/ConnectedComponentLabeling.h @@ -11,6 +11,10 @@ namespace Mantid { + namespace Kernel + { + class Logger; + } namespace API { @@ -101,6 +105,8 @@ namespace Mantid /// Run multithreaded const boost::optional<int> m_nThreads; + Mantid::Kernel::Logger& m_logger; + }; } // namespace Crystal diff --git a/Code/Mantid/Framework/Crystal/inc/MantidCrystal/IntegratePeaksUsingClusters.h b/Code/Mantid/Framework/Crystal/inc/MantidCrystal/IntegratePeaksUsingClusters.h index 87f970edd3c91ce532f6ef93f9c344f5fbe555ba..139fd5637ace28ac8eb13bb61a6b537bad67ce62 100644 --- a/Code/Mantid/Framework/Crystal/inc/MantidCrystal/IntegratePeaksUsingClusters.h +++ b/Code/Mantid/Framework/Crystal/inc/MantidCrystal/IntegratePeaksUsingClusters.h @@ -3,6 +3,7 @@ #include "MantidKernel/System.h" #include "MantidAPI/Algorithm.h" +#include "MantidAPI/IMDWorkspace.h" namespace Mantid { @@ -42,6 +43,7 @@ namespace Crystal virtual const std::string category() const; private: + Mantid::API::MDNormalization getNormalization(); virtual void initDocs(); void init(); void exec(); @@ -53,4 +55,4 @@ namespace Crystal } // namespace Crystal } // namespace Mantid -#endif /* MANTID_CRYSTAL_INTEGRATEPEAKSUSINGCLUSTERS_H_ */ \ No newline at end of file +#endif /* MANTID_CRYSTAL_INTEGRATEPEAKSUSINGCLUSTERS_H_ */ diff --git a/Code/Mantid/Framework/Crystal/src/ConnectedComponentLabeling.cpp b/Code/Mantid/Framework/Crystal/src/ConnectedComponentLabeling.cpp index 7d3bd25e0548ebc3bf0e302d375e576fa94bcf28..abca8d2386923e5b2b21b74778324b904df0901d 100644 --- a/Code/Mantid/Framework/Crystal/src/ConnectedComponentLabeling.cpp +++ b/Code/Mantid/Framework/Crystal/src/ConnectedComponentLabeling.cpp @@ -1,4 +1,5 @@ #include "MantidKernel/MultiThreaded.h" +#include "MantidKernel/Logger.h" #include "MantidKernel/V3D.h" #include "MantidAPI/AlgorithmManager.h" #include "MantidAPI/FrameworkManager.h" @@ -45,6 +46,27 @@ namespace Mantid return maxNeighbours; } + /** + * Get the maximum number of clusters possible in an MD space. For use as an offset. + * @param ws : Workspace to interogate + * @param nIterators : number of equally sized iterators working on the workspace + * @return Maximum number of clusters. + */ + size_t calculateMaxClusters(IMDHistoWorkspace const * const ws, size_t nIterators) + { + size_t maxClusters = 1; + for(size_t i = 0; i < ws->getNumDims(); ++i) + { + maxClusters *= ws->getDimension(i)->getNBins()/2; + } + maxClusters /= nIterators; + if(maxClusters == 0) + { + maxClusters = ws->getNPoints(); + } + return maxClusters; + } + /** * Helper non-member to clone the input workspace * @param inWS: To clone @@ -113,6 +135,18 @@ namespace Mantid typedef std::set<DisjointPair> SetDisjointPair; typedef std::vector<EdgeIndexPair> VecEdgeIndexPair; + /** + * Free function performing the CCL implementation over a range defined by the iterator. + * + * @param iterator : Iterator giving access the the image + * @param strategy : Strategy for identifying background + * @param neighbourElements : Grid of DisjointElements the same size as the image + * @param progress : Progress object to update + * @param maxNeighbours : Maximum number of neighbours each element may have. Determined by dimensionality. + * @param startLabelId : Start label index to increment + * @param edgeIndexVec : Vector of edge index pairs. To identify elements across iterator boundaries to resolve later. + * @return + */ size_t doConnectedComponentLabeling( IMDIterator* iterator, BackgroundStrategy * const strategy, @@ -123,6 +157,7 @@ namespace Mantid ) { size_t currentLabelCount = startLabelId; + strategy->configureIterator(iterator); // Set up such things as desired Normalization. do { if(!strategy->isBackground(iterator)) @@ -205,7 +240,7 @@ namespace Mantid * @param nThreads : Optional argument of number of threads to use. */ ConnectedComponentLabeling::ConnectedComponentLabeling(const size_t& startId, const boost::optional<int> nThreads) - : m_startId(startId), m_nThreads(nThreads) + : m_startId(startId), m_nThreads(nThreads), m_logger(Kernel::Logger::get("ConnectedComponentLabeling")) { if(m_nThreads.is_initialized() && m_nThreads.get() < 0) { @@ -274,30 +309,34 @@ namespace Mantid VecElements neighbourElements(ws->getNPoints()); const size_t maxNeighbours = calculateMaxNeighbours(ws.get()); + progress.doReport("Identifying clusters"); size_t frequency = reportEvery<size_t>(10000, ws->getNPoints()); - progress.resetNumSteps(frequency, 0.0, 0.5); + progress.resetNumSteps(frequency, 0.0, 0.8); // For each process maintains pair of index from within process bounds to index outside process bounds const int nThreadsToUse = getNThreads(); + if(nThreadsToUse > 1) { std::vector<API::IMDIterator*> iterators = ws->createIterators(nThreadsToUse); - const int nthreads = getNThreads(); - std::vector<VecEdgeIndexPair> parallelEdgeVec(nthreads); + + const size_t maxClustersPossible = calculateMaxClusters(ws.get(), nThreadsToUse); + + std::vector<VecEdgeIndexPair> parallelEdgeVec(nThreadsToUse); - std::vector<std::map<size_t, boost::shared_ptr<Cluster> > > parallelClusterMapVec(nthreads); + std::vector<std::map<size_t, boost::shared_ptr<Cluster> > > parallelClusterMapVec(nThreadsToUse); // ------------- Stage One. Local CCL in parallel. + m_logger.debug("Parallel solve local CCL"); PARALLEL_FOR_NO_WSP_CHECK() - for(int i = 0; i < nthreads; ++i) + for(int i = 0; i < nThreadsToUse; ++i) { API::IMDIterator* iterator = iterators[i]; - iterator->setNormalization(NoNormalization); boost::scoped_ptr<BackgroundStrategy> strategy(baseStrategy->clone()); // local strategy VecEdgeIndexPair& edgeVec = parallelEdgeVec[i]; // local edge indexes - const size_t startLabel = m_startId + (i * ws->getNPoints()); // Ensure that label ids are totally unique within each parallel unit. + const size_t startLabel = m_startId + (i * maxClustersPossible); // Ensure that label ids are totally unique within each parallel unit. const size_t endLabel = doConnectedComponentLabeling(iterator, strategy.get(), neighbourElements, progress, maxNeighbours, startLabel, edgeVec); // Create clusters from labels. @@ -332,6 +371,7 @@ namespace Mantid } // Percolate minimum label across boundaries for indexes where there is ambiguity. + m_logger.debug("Percolate minimum label across boundaries"); std::vector<boost::shared_ptr<Cluster> > incompleteClusterVec; incompleteClusterVec.reserve(1000); std::set<size_t> usedLabels; @@ -369,7 +409,8 @@ namespace Mantid // ------------- Stage 3 In parallel, process each incomplete cluster. progress.doReport("Merging clusters across processors"); - progress.resetNumSteps(incompleteClusterVec.size(), 0.5, 0.75); + m_logger.debug("Merging clusters across processors"); + progress.resetNumSteps(incompleteClusterVec.size(), 0.8, 0.9); PARALLEL_FOR_NO_WSP_CHECK() for(int i = 0; i < static_cast<int>(incompleteClusterVec.size()); ++i) { @@ -377,13 +418,16 @@ namespace Mantid cluster->toUniformMinimum(neighbourElements); progress.report(); } - - // Now combine clusters and add the resolved clusters to the clustermap. + m_logger.debug("Remove duplicates"); + m_logger.debug() << incompleteClusterVec.size() << " clusters to reconstruct" << std::endl; + // Now combine clusters and add the resolved clusters to the clusterMap. + SetIds usedOwningClusterIds; for(size_t i = 0; i < incompleteClusterVec.size(); ++i) { const size_t label = incompleteClusterVec[i]->getLabel(); - if(!does_contain_key(clusterMap, label)) + if(usedOwningClusterIds.find(label) == usedOwningClusterIds.end()) { + usedOwningClusterIds.insert(label); clusterMap.insert(std::make_pair(label, incompleteClusterVec[i])); } else @@ -409,7 +453,6 @@ namespace Mantid // Associate the member DisjointElements with a cluster. Involves looping back over iterator. iterator->jumpTo(0); // Reset - iterator->setNormalization(NoNormalization); // TODO: check that this is a valid assumption. std::set<size_t> labelIds; do { diff --git a/Code/Mantid/Framework/Crystal/src/IntegratePeaksUsingClusters.cpp b/Code/Mantid/Framework/Crystal/src/IntegratePeaksUsingClusters.cpp index 09b701642ee91d4c8d490880eb1b9d0cf2738b23..0936f5ff036b3c86b1113e3194038fd4d489f25a 100644 --- a/Code/Mantid/Framework/Crystal/src/IntegratePeaksUsingClusters.cpp +++ b/Code/Mantid/Framework/Crystal/src/IntegratePeaksUsingClusters.cpp @@ -140,6 +140,16 @@ namespace Mantid declareProperty( new PropertyWithValue<double>("Threshold", 0, compositeValidator, Direction::Input), "Threshold signal above which to consider peaks"); + + std::vector<std::string> normalizations(3); + normalizations[0] = "NoNormalization"; + normalizations[1] = "VolumeNormalization"; + normalizations[2] = "NumEventsNormalization"; + + declareProperty("Normalization",normalizations[1], + Kernel::IValidator_sptr(new Kernel::ListValidator<std::string>(normalizations)), + "Normalization to use with Threshold. Defaults to VolumeNormalization to account for different binning."); + declareProperty(new WorkspaceProperty<IPeaksWorkspace>("OutputWorkspace", "", Direction::Output), "An output integrated peaks workspace."); declareProperty( @@ -147,6 +157,30 @@ namespace Mantid "MDHistoWorkspace containing the labeled clusters used by the algorithm."); } + + /** + * Get the normalization. For use with iterators + background strategies. + * @return Chosen normalization + */ + MDNormalization IntegratePeaksUsingClusters::getNormalization() + { + std::string normProp = getPropertyValue("Normalization"); + Mantid::API::MDNormalization normalization; + if (normProp == "NoNormalization") + { + normalization = NoNormalization; + } + else if (normProp == "VolumeNormalization") + { + normalization = VolumeNormalization; + } + else + { + normalization = NumEventsNormalization; + } + return normalization; + } + //---------------------------------------------------------------------------------------------- /** Execute the algorithm. */ @@ -178,12 +212,12 @@ namespace Mantid const double threshold = getProperty("Threshold"); // Make a background strategy for the CCL analysis to use. - HardThresholdBackground backgroundStrategy(threshold,NoNormalization); + HardThresholdBackground backgroundStrategy(threshold, this->getNormalization()); // CCL. Multi-processor version. ConnectedComponentLabeling analysis; Progress progress(this, 0, 1, 1); - // Peform CCL. + // Perform CCL. ClusterTuple clusters = analysis.executeAndFetchClusters(mdWS, &backgroundStrategy, progress); // Extract the clusters ConnectedComponentMappingTypes::ClusterMap& clusterMap = clusters.get<1>(); @@ -195,14 +229,15 @@ namespace Mantid std::map<size_t, size_t> labelsTakenByPeaks; progress.doReport("Performing Peak Integration"); - progress.resetNumSteps(peakWS->getNumberPeaks(), 0.75, 1); + g_log.information("Starting Integration"); + progress.resetNumSteps(peakWS->getNumberPeaks(), 0.9, 1); PARALLEL_FOR1(peakWS) for(int i = 0; i < peakWS->getNumberPeaks(); ++i) { PARALLEL_START_INTERUPT_REGION IPeak& peak = peakWS->getPeak(i); const V3D& peakCenterInMDFrame = peakTransform->transformPeak(peak); - const Mantid::signal_t signalValue = outHistoWS->getSignalAtVMD(peakCenterInMDFrame, NoNormalization); + const Mantid::signal_t signalValue = outHistoWS->getSignalAtVMD(peakCenterInMDFrame, NoNormalization); // No normalization when extracting label ids! if(boost::math::isnan(signalValue)) { g_log.warning() << "Warning: image for integration is off edge of detector for peak " << i << std::endl; diff --git a/Code/Mantid/Framework/Crystal/test/ConnectedComponentLabelingTest.h b/Code/Mantid/Framework/Crystal/test/ConnectedComponentLabelingTest.h index 516972f75e36b9f5001cf3697894d1626c98844b..d3ee6c2a55f215b1bf5e9757f5fcae7321d6c806 100644 --- a/Code/Mantid/Framework/Crystal/test/ConnectedComponentLabelingTest.h +++ b/Code/Mantid/Framework/Crystal/test/ConnectedComponentLabelingTest.h @@ -106,7 +106,7 @@ public: MockBackgroundStrategy mockStrategy; EXPECT_CALL(mockStrategy, isBackground(_)).Times(static_cast<int>(inWS->getNPoints())*2).WillRepeatedly(Return(false));// A filter that passes everything. - + EXPECT_CALL(mockStrategy, configureIterator(_)).Times(1); size_t labelingId = 1; int multiThreaded = 1; ConnectedComponentLabeling ccl(labelingId, multiThreaded); @@ -128,7 +128,7 @@ public: MockBackgroundStrategy mockStrategy; EXPECT_CALL(mockStrategy, isBackground(_)).Times(static_cast<int>(inWS->getNPoints())*2).WillRepeatedly(Return(false));// A filter that passes everything. - + EXPECT_CALL(mockStrategy, configureIterator(_)).Times(1); size_t labelingId = 2; int multiThreaded = 1; ConnectedComponentLabeling ccl(labelingId, multiThreaded); @@ -151,7 +151,7 @@ public: IMDHistoWorkspace_sptr inWS = MDEventsTestHelper::makeFakeMDHistoWorkspace(1, 1, 6); // Makes a 1 by 6 md ws with identical signal values. MockBackgroundStrategy mockStrategy; - + EXPECT_CALL(mockStrategy, configureIterator(_)).Times(1); /* * We use the is background strategy to set up two disconected blocks for us. * */ @@ -192,7 +192,7 @@ public: IMDHistoWorkspace_sptr inWS = MDEventsTestHelper::makeFakeMDHistoWorkspace(1, 1, 5); // Makes a 1 by 5 md ws with identical signal values. MockBackgroundStrategy mockStrategy; - + EXPECT_CALL(mockStrategy, configureIterator(_)).Times(1); /* * We use the is background strategy to set up three disconected blocks for us. * */ @@ -230,6 +230,7 @@ public: IMDHistoWorkspace_sptr inWS = MDEventsTestHelper::makeFakeMDHistoWorkspace(1, 2, 4); // Makes a 4 by 4 grid. MockBackgroundStrategy mockStrategy; + EXPECT_CALL(mockStrategy, configureIterator(_)).Times(1); EXPECT_CALL(mockStrategy, isBackground(_)).WillRepeatedly(Return(false));// Nothing is treated as background size_t labelingId = 1; @@ -249,6 +250,7 @@ public: IMDHistoWorkspace_sptr inWS = MDEventsTestHelper::makeFakeMDHistoWorkspace(1, 2, 3); // Makes a 3 by 3 grid. MockBackgroundStrategy mockStrategy; + EXPECT_CALL(mockStrategy, configureIterator(_)).Times(1); /* * We treat alternate cells as background, which actually should result in a single object. Think of a chequered flag. @@ -292,6 +294,7 @@ public: IMDHistoWorkspace_sptr inWS = MDEventsTestHelper::makeFakeMDHistoWorkspace(1, 3, 3); // Makes a 3 by 3 by 3 grid. All populated with a single value. MockBackgroundStrategy mockStrategy; + EXPECT_CALL(mockStrategy, configureIterator(_)).Times(1); /* * We treat alternate cells as background, which actually should result in a single object. Think of a chequered flag. diff --git a/Code/Mantid/Framework/MDEvents/inc/MantidMDEvents/MDHistoWorkspace.h b/Code/Mantid/Framework/MDEvents/inc/MantidMDEvents/MDHistoWorkspace.h index 2406ab0d3e25b01f26d40d6548ade6245969f238..d0cd3edb8679160e7ddc3a0b19f96f87ceba0d8c 100644 --- a/Code/Mantid/Framework/MDEvents/inc/MantidMDEvents/MDHistoWorkspace.h +++ b/Code/Mantid/Framework/MDEvents/inc/MantidMDEvents/MDHistoWorkspace.h @@ -400,6 +400,10 @@ namespace MDEvents /// Get the size of an element in the HistoWorkspace. static size_t sizeOfElement(); + + /// Virtual constructor. + boost::shared_ptr<IMDHistoWorkspace> cloneShape() const; + private: void initVertexesArray(); diff --git a/Code/Mantid/Framework/MDEvents/src/MDHistoWorkspace.cpp b/Code/Mantid/Framework/MDEvents/src/MDHistoWorkspace.cpp index 8fe513c57d6cf62e49ac856032027c1f84709aab..669866466d960a47b8034087c6932a127831dae4 100644 --- a/Code/Mantid/Framework/MDEvents/src/MDHistoWorkspace.cpp +++ b/Code/Mantid/Framework/MDEvents/src/MDHistoWorkspace.cpp @@ -1308,6 +1308,11 @@ namespace MDEvents return (3 * sizeof(signal_t) ) + sizeof(bool); } + boost::shared_ptr<IMDHistoWorkspace> MDHistoWorkspace::clone() const; + { + return boost::make_ + } + } // namespace Mantid } // namespace MDEvents