From 55c20c406637f93ba08ddd20bde31ea2b0c13302 Mon Sep 17 00:00:00 2001
From: Gennady Pospelov <g.pospelov@fz-juelich.de>
Date: Tue, 18 Oct 2016 10:30:24 +0200
Subject: [PATCH] IDetector::getIntensityData now returns ROI-clipped map in
 desired units.

---
 Core/Instrument/IDetector2D.cpp               | 69 ++++++++++++++++---
 Core/Instrument/IDetector2D.h                 |  5 ++
 .../UnitTests/Core/3/SphericalDetectorTest.h  | 69 +++++++++++++++++++
 3 files changed, 135 insertions(+), 8 deletions(-)

diff --git a/Core/Instrument/IDetector2D.cpp b/Core/Instrument/IDetector2D.cpp
index 6136d0836da..ebc3aeda49f 100644
--- a/Core/Instrument/IDetector2D.cpp
+++ b/Core/Instrument/IDetector2D.cpp
@@ -117,21 +117,20 @@ OutputData<double> *IDetector2D::getDetectorIntensity(const OutputData<double> &
                                                       const Beam& beam,
                                                       IDetector2D::EAxesUnits units_type) const
 {
-    std::unique_ptr<OutputData<double> > result (data.clone());
+    std::unique_ptr<OutputData<double>> result(data.clone());
     applyDetectorResolution(result.get());
 
-    if(units_type == IDetector2D::DEFAULT) {
+    if(units_type == IDetector2D::DEFAULT && regionOfInterest() == nullptr)
         return result.release();
-    }
 
-    OutputData<double>* detectorMap = createDetectorMap(beam, units_type);
+    std::unique_ptr<OutputData<double>> detectorMap(createDetectorMap(beam, units_type));
     if(!detectorMap)
         throw Exceptions::RuntimeErrorException("Instrument::getDetectorIntensity() -> Error."
                                     "Can't create detector map.");
-    detectorMap->setRawDataVector(result->getRawDataVector());
-    return detectorMap;
 
+    setDataToDetectorMap(*detectorMap.get(), *result.get());
 
+    return detectorMap.release();
 }
 
 OutputData<double>* IDetector2D::createDetectorMap(const Beam& beam, EAxesUnits units) const
@@ -296,8 +295,35 @@ std::unique_ptr<IAxis> IDetector2D::constructAxis(size_t axis_index, const Beam
 {
     double amin(0), amax(0);
     calculateAxisRange(axis_index, beam, units, amin, amax);
-    return std::unique_ptr<IAxis>(
-        new FixedBinAxis(getAxisName(axis_index), getAxis(axis_index).getSize(), amin, amax));
+
+    std::unique_ptr<IAxis> result(new FixedBinAxis(getAxisName(axis_index),
+                                                   getAxis(axis_index).getSize(), amin, amax));
+
+    if(m_region_of_interest) {
+        return clipAxisToRoi(axis_index, *result.get());
+    } else {
+        return result;
+    }
+}
+
+std::unique_ptr<IAxis> IDetector2D::clipAxisToRoi(size_t axis_index, const IAxis &axis) const
+{
+    if(!m_region_of_interest)
+        throw Exceptions::RuntimeErrorException("IDetector2D::clipAxisToRoi() -> Error. ROI is "
+                                                "absent");
+
+    size_t nbin1(0), nbin2(axis.getSize()-1);
+    if(axis_index == BornAgain::X_AXIS_INDEX) {
+        nbin1 = getAxis(axis_index).findClosestIndex(m_region_of_interest->getXlow());
+        nbin2 = getAxis(axis_index).findClosestIndex(m_region_of_interest->getXup());
+
+    }else if(axis_index == BornAgain::Y_AXIS_INDEX) {
+        nbin1 = getAxis(axis_index).findClosestIndex(m_region_of_interest->getYlow());
+        nbin2 = getAxis(axis_index).findClosestIndex(m_region_of_interest->getYup());
+    }
+
+    return std::unique_ptr<IAxis>(new FixedBinAxis(axis.getName(), nbin2-nbin1+1,
+                                    axis.getBin(nbin1).m_lower, axis.getBin(nbin2).m_upper));
 }
 
 void IDetector2D::calculateAxisRange(size_t axis_index, const Beam &beam,
@@ -395,6 +421,33 @@ Eigen::Matrix2cd IDetector2D::calculateAnalyzerOperator(
     return result;
 }
 
+void IDetector2D::setDataToDetectorMap(OutputData<double> &detectorMap,
+                                       const OutputData<double> &data) const
+{
+    if(detectorMap.getAllocatedSize() == data.getAllocatedSize()) {
+        detectorMap.setRawDataVector(data.getRawDataVector());
+        return;
+    }
+
+    if(const Geometry::Rectangle *roi = regionOfInterest()) {
+        size_t roi_x = getAxis(BornAgain::X_AXIS_INDEX).findClosestIndex(roi->getXlow());
+        size_t roi_y = getAxis(BornAgain::Y_AXIS_INDEX).findClosestIndex(roi->getYlow());
+        size_t index0 = getGlobalIndex(roi_x, roi_y);
+        const IAxis &yAxisOfDetector = getAxis(BornAgain::Y_AXIS_INDEX);
+
+        const IAxis &xAxisOfMap = *detectorMap.getAxis(BornAgain::X_AXIS_INDEX);
+        const IAxis &yAxisOfMap = *detectorMap.getAxis(BornAgain::Y_AXIS_INDEX);
+        for(size_t ix=0; ix<xAxisOfMap.getSize(); ++ix) {
+            for(size_t iy=0; iy<yAxisOfMap.getSize(); ++iy) {
+                size_t mapIndex = iy + ix*yAxisOfMap.getSize();
+                size_t globalIndex = index0 + iy + ix*yAxisOfDetector.getSize();
+                detectorMap[mapIndex] = data[globalIndex];
+            }
+        }
+    }
+
+}
+
 void IDetector2D::addAxis(const IAxis& axis)
 {
     m_axes.push_back(axis.clone());
diff --git a/Core/Instrument/IDetector2D.h b/Core/Instrument/IDetector2D.h
index 3481c64a3ff..7a99d61f6e7 100644
--- a/Core/Instrument/IDetector2D.h
+++ b/Core/Instrument/IDetector2D.h
@@ -174,6 +174,8 @@ protected:
     std::unique_ptr<IAxis> constructAxis(size_t axis_index, const Beam& beam,
                                          EAxesUnits units) const;
 
+    std::unique_ptr<IAxis> clipAxisToRoi(size_t axis_index, const IAxis &axis) const;
+
     //! Calculates axis range from original detector axes in given units (mm, rad, etc)
     virtual void calculateAxisRange(size_t axis_index, const Beam& beam, EAxesUnits units,
                                     double &amin, double &amax) const;
@@ -216,6 +218,9 @@ private:
         const kvector_t direction, double efficiency, double total_transmission = 1.0) const;
 #endif
 
+    void setDataToDetectorMap(OutputData<double> &detectorMap,
+                              const OutputData<double> &data) const;
+
     std::unique_ptr<Geometry::Rectangle> m_region_of_interest;
 };
 
diff --git a/Tests/UnitTests/Core/3/SphericalDetectorTest.h b/Tests/UnitTests/Core/3/SphericalDetectorTest.h
index 2a827a03685..52dab56ca67 100644
--- a/Tests/UnitTests/Core/3/SphericalDetectorTest.h
+++ b/Tests/UnitTests/Core/3/SphericalDetectorTest.h
@@ -224,6 +224,75 @@ TEST_F(SphericalDetectorTest, regionOfInterestAndData)
     EXPECT_EQ(data.getAxis(1)->getMax(), 4.0);
 }
 
+//! Create detector map in the presence of region of interest.
+
+TEST_F(SphericalDetectorTest, regionOfInterestAndDetectorMap)
+{
+    SphericalDetector detector(6, -1.0*Units::deg, 5.0*Units::deg,
+                               4, 0.0*Units::deg, 4.0*Units::deg);
+
+    detector.setRegionOfInterest(0.1*Units::deg, 1.1*Units::deg, 3.0*Units::deg, 2.9*Units::deg);
+    Beam beam;
+    beam.setCentralK(1.0*Units::angstrom, 0.4*Units::deg, 0.0);
+
+    // Creating map in default units, which are radians and checking that axes are clipped
+    // to region of interest.
+    std::unique_ptr<OutputData<double>> data(
+                detector.createDetectorMap(beam, IDetector2D::DEFAULT));
+    EXPECT_EQ(data->getAxis(0)->getSize(), 4);
+    EXPECT_EQ(data->getAxis(0)->getMin(), 0.0*Units::deg);
+    EXPECT_EQ(data->getAxis(0)->getMax(), 4.0*Units::deg);
+    EXPECT_EQ(data->getAxis(1)->getSize(), 2);
+    EXPECT_EQ(data->getAxis(1)->getMin(), 1.0*Units::deg);
+    EXPECT_EQ(data->getAxis(1)->getMax(), 3.0*Units::deg);
+
+    // Creating map with axes in degrees, and checking that it is clipped to the region of interest
+    data.reset(detector.createDetectorMap(beam, IDetector2D::DEGREES));
+    EXPECT_EQ(data->getAxis(0)->getSize(), 4);
+    EXPECT_EQ(data->getAxis(0)->getMin(), 0.0);
+    EXPECT_EQ(data->getAxis(0)->getMax(), 4.0);
+    EXPECT_EQ(data->getAxis(1)->getSize(), 2);
+    EXPECT_EQ(data->getAxis(1)->getMin(), 1.0);
+    EXPECT_EQ(data->getAxis(1)->getMax(), 3.0);
+}
+
+//! Checking IDetector2D::getIntensityData in the presence of region of interest.
+
+TEST_F(SphericalDetectorTest, getIntensityData)
+{
+    SphericalDetector detector(6, -1.0*Units::deg, 5.0*Units::deg,
+                               4, 0.0*Units::deg, 4.0*Units::deg);
+    detector.setRegionOfInterest(0.1*Units::deg, 1.1*Units::deg, 3.0*Units::deg, 2.9*Units::deg);
+    Beam beam;
+    beam.setCentralK(1.0*Units::angstrom, 0.4*Units::deg, 0.0);
+
+    // Initializing data (no region of interest involved yet) and filling with amplitudes
+    OutputData<double> intensityData;
+    detector.initOutputData(intensityData);
+    EXPECT_EQ(intensityData.getAllocatedSize(), 6*4);
+    for(size_t i=0; i<intensityData.getAllocatedSize(); ++i) {
+        intensityData[i] = static_cast<double>(i);
+    }
+    EXPECT_EQ(intensityData[intensityData.getAllocatedSize()-1], 23.0);
+
+    // Getting detectorIntensity and checking that amplitudes are correct and it is clipped to
+    // region of interest.
+
+    std::unique_ptr<OutputData<double>> detectorIntensity(
+                detector.getDetectorIntensity(intensityData, beam, IDetector2D::DEGREES));
+
+    EXPECT_EQ(detectorIntensity->getAllocatedSize(), 8);
+    EXPECT_EQ((*detectorIntensity)[0], 5.0);
+    EXPECT_EQ((*detectorIntensity)[7], 18.0);
+    EXPECT_EQ(detectorIntensity->getAxis(0)->getSize(), 4);
+    EXPECT_EQ(detectorIntensity->getAxis(0)->getMin(), 0.0);
+    EXPECT_EQ(detectorIntensity->getAxis(0)->getMax(), 4.0);
+    EXPECT_EQ(detectorIntensity->getAxis(1)->getSize(), 2);
+    EXPECT_EQ(detectorIntensity->getAxis(1)->getMin(), 1.0);
+    EXPECT_EQ(detectorIntensity->getAxis(1)->getMax(), 3.0);
+}
+
+
 
 TEST_F(SphericalDetectorTest, DetectorCopying)
 {
-- 
GitLab