Add support for non-uniform X axis in array data
authorTeemu Murtola <teemu.murtola@gmail.com>
Thu, 27 Feb 2014 05:02:02 +0000 (07:02 +0200)
committerRoland Schulz <roland@rschulz.eu>
Fri, 28 Feb 2014 01:25:46 +0000 (02:25 +0100)
It is now possible to construct AnalysisArrayData objects that have a
non-uniform X axis.  AnalysisDataAverageModule can use this to provide a
more meaningful X axis in the output plot in case the values correspond
to, e.g., a set of atoms.

Change-Id: I578f6944d4ff51c315d068b011b705be2a4be00a

src/gromacs/analysisdata/arraydata.cpp
src/gromacs/analysisdata/arraydata.h
src/gromacs/analysisdata/modules/average.h
src/gromacs/analysisdata/tests/arraydata.cpp
src/gromacs/analysisdata/tests/average.cpp
src/gromacs/analysisdata/tests/refdata/AverageModuleTest_CanCustomizeNonUniformXAxis.xml [new file with mode: 0644]

index 293ed0ad3c532ac35114d2f0292e942086bdcda9..c14fece0aec9b2fb6f5f9cc898f377f83823d622 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2010,2011,2012,2013, by the GROMACS development team, led by
+ * Copyright (c) 2010,2011,2012,2013,2014, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -52,9 +52,10 @@ namespace gmx
 {
 
 AbstractAnalysisArrayData::AbstractAnalysisArrayData()
-    : rowCount_(0), pointSetInfo_(0, 0, 0, 0), xstart_(0.0), xstep_(1.0),
-      bReady_(false)
+    : rowCount_(0), pointSetInfo_(0, 0, 0, 0), xstep_(1.0),
+      bUniformX_(true), bReady_(false)
 {
+    xvalue_.push_back(0);
 }
 
 AbstractAnalysisArrayData::~AbstractAnalysisArrayData()
@@ -101,6 +102,17 @@ AbstractAnalysisArrayData::setRowCount(int rowCount)
     GMX_RELEASE_ASSERT(rowCount > 0, "Invalid number of rows");
     GMX_RELEASE_ASSERT(!isAllocated(),
                        "Cannot change row count after data has been allocated");
+    GMX_RELEASE_ASSERT(bUniformX_ || rowCount_ == 0
+                       || rowCount == static_cast<int>(xvalue_.size()),
+                       "X axis set with setXAxisValue() does not match the row count");
+    xvalue_.resize(rowCount);
+    if (bUniformX_ && rowCount > rowCount_)
+    {
+        for (int i = rowCount_; i < rowCount; ++i)
+        {
+            xvalue_[i] = xvalue_[0] + i * xstep_;
+        }
+    }
     rowCount_ = rowCount;
 }
 
@@ -124,8 +136,31 @@ void
 AbstractAnalysisArrayData::setXAxis(real start, real step)
 {
     GMX_RELEASE_ASSERT(!bReady_, "X axis cannot be set after data is finished");
-    xstart_ = start;
-    xstep_  = step;
+    xvalue_[0] = start;
+    xstep_     = step;
+    bUniformX_ = true;
+    for (int i = 0; i < rowCount_; ++i)
+    {
+        xvalue_[i] = start + i * xstep_;
+    }
+}
+
+
+void
+AbstractAnalysisArrayData::setXAxisValue(int row, real value)
+{
+    GMX_RELEASE_ASSERT(!bReady_, "X axis cannot be set after data is finished");
+    if (rowCount_ > 0)
+    {
+        GMX_RELEASE_ASSERT(row >= 0 && row < rowCount(), "Row index out of range");
+    }
+    else if (row >= static_cast<int>(xvalue_.size()))
+    {
+        xvalue_.resize(row + 1);
+    }
+    bUniformX_   = false;
+    xstep_       = 0.0;
+    xvalue_[row] = value;
 }
 
 
@@ -167,7 +202,9 @@ AbstractAnalysisArrayData::copyContents(const AbstractAnalysisArrayData *src,
     dest->setColumnCount(src->columnCount());
     dest->setRowCount(src->rowCount());
     dest->allocateValues();
-    dest->setXAxis(src->xstart(), src->xstep());
+    dest->xstep_     = src->xstep_;
+    dest->bUniformX_ = src->bUniformX_;
+    std::copy(src->xvalue_.begin(), src->xvalue_.end(), dest->xvalue_.begin());
     std::copy(src->value_.begin(), src->value_.end(), dest->value_.begin());
 }
 
index 5a8c35bf514996542588f0ddd979bdae30d319f8..71efadf6edf385ce8424dce4a53cdc736b50b206 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2010,2011,2012,2013, by the GROMACS development team, led by
+ * Copyright (c) 2010,2011,2012,2013,2014, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -91,14 +91,18 @@ class AbstractAnalysisArrayData : public AbstractAnalysisData
         //! Returns true if values have been allocated.
         bool isAllocated() const { return !value_.empty(); }
         //! Returns the x value of the first frame.
-        real xstart() const { return xstart_; }
+        real xstart() const { return xvalue_[0]; }
         //! Returns the step between frame x values.
-        real xstep() const { return xstep_; }
+        real xstep() const
+        {
+            GMX_ASSERT(bUniformX_, "Accessing x step for non-uniform data");
+            return xstep_;
+        }
         //! Returns the x value of a row.
         real xvalue(int row) const
         {
             GMX_ASSERT(row >= 0 && row < rowCount(), "Row index out of range");
-            return xstart() + row * xstep();
+            return xvalue_[row];
         }
         //! Returns a given array element.
         const AnalysisDataValue &value(int row, int col) const
@@ -154,10 +158,22 @@ class AbstractAnalysisArrayData : public AbstractAnalysisData
          * \param[in] step   Step between x values of successive frames.
          *
          * Must not be called after valuesReady().
+         * Any values set with setXAxisValue() are overwritten.
          *
          * Does not throw.
          */
         void setXAxis(real start, real step);
+        /*! \brief
+         * Sets a single value reported as x value for frames.
+         *
+         * \param[in] row    Row/frame for which to set the value.
+         * \param[in] value  x value for the frame specified by \p row.
+         *
+         * Must not be called after valuesReady().
+         *
+         * Does not throw.
+         */
+        void setXAxisValue(int row, real value);
         //! Returns a reference to a given array element.
         AnalysisDataValue &value(int row, int col)
         {
@@ -197,8 +213,9 @@ class AbstractAnalysisArrayData : public AbstractAnalysisData
         int                            rowCount_;
         AnalysisDataPointSetInfo       pointSetInfo_;
         std::vector<AnalysisDataValue> value_;
-        real                           xstart_;
+        std::vector<real>              xvalue_;
         real                           xstep_;
+        bool                           bUniformX_;
         bool                           bReady_;
 
         // Copy and assign disallowed by base.
@@ -237,6 +254,7 @@ class AnalysisArrayData : public AbstractAnalysisArrayData
         using AbstractAnalysisArrayData::setRowCount;
         using AbstractAnalysisArrayData::allocateValues;
         using AbstractAnalysisArrayData::setXAxis;
+        using AbstractAnalysisArrayData::setXAxisValue;
         using AbstractAnalysisArrayData::value;
         using AbstractAnalysisArrayData::valuesReady;
 
index 077d5a948d4ebaa9e318d5f463743bf27e7b2d35..3f8f583f06af68de7dbb3bb267cc35cf0d3daa87 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2010,2011,2012,2013, by the GROMACS development team, led by
+ * Copyright (c) 2010,2011,2012,2013,2014, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -87,6 +87,7 @@ class AnalysisDataAverageModule : public AbstractAnalysisArrayData,
         virtual ~AnalysisDataAverageModule();
 
         using AbstractAnalysisArrayData::setXAxis;
+        using AbstractAnalysisArrayData::setXAxisValue;
 
         /*! \brief
          * Sets the averaging to happen over entire data sets.
index 74601b265d0f96e108d2cd1672a472f46195c2f6..37de2b508ba120d3831494a26e22c3f42f37f600 100644 (file)
@@ -109,4 +109,42 @@ TEST_F(AnalysisArrayDataTest, StorageWorks)
     ASSERT_NO_THROW_GMX(data.valuesReady());
 }
 
+TEST_F(AnalysisArrayDataTest, CanSetXAxis)
+{
+    gmx::AnalysisArrayData       data;
+    data.setRowCount(5);
+    data.setXAxis(1.0, 1.0);
+    EXPECT_FLOAT_EQ(1.0, data.xvalue(0));
+    EXPECT_FLOAT_EQ(3.0, data.xvalue(2));
+    EXPECT_FLOAT_EQ(5.0, data.xvalue(4));
+    data.setXAxisValue(0, 3.0);
+    data.setXAxisValue(2, 1.0);
+    EXPECT_FLOAT_EQ(3.0, data.xvalue(0));
+    EXPECT_FLOAT_EQ(2.0, data.xvalue(1));
+    EXPECT_FLOAT_EQ(1.0, data.xvalue(2));
+    EXPECT_FLOAT_EQ(4.0, data.xvalue(3));
+}
+
+TEST_F(AnalysisArrayDataTest, CanSetXAxisBeforeRowCount)
+{
+    {
+        gmx::AnalysisArrayData       data;
+        data.setXAxis(1.0, 1.0);
+        data.setRowCount(5);
+        EXPECT_FLOAT_EQ(1.0, data.xvalue(0));
+        EXPECT_FLOAT_EQ(3.0, data.xvalue(2));
+        EXPECT_FLOAT_EQ(5.0, data.xvalue(4));
+    }
+    {
+        gmx::AnalysisArrayData       data;
+        data.setXAxisValue(0, 2.0);
+        data.setXAxisValue(1, 3.0);
+        data.setXAxisValue(2, 5.0);
+        data.setRowCount(3);
+        EXPECT_FLOAT_EQ(2.0, data.xvalue(0));
+        EXPECT_FLOAT_EQ(3.0, data.xvalue(1));
+        EXPECT_FLOAT_EQ(5.0, data.xvalue(2));
+    }
+}
+
 } // namespace
index f0b540da91776b7104842792bbe51e980a01be34..b3b80b9c24d6b318c2cde0018d1967c687917585 100644 (file)
@@ -232,6 +232,24 @@ TEST_F(AverageModuleTest, CanCustomizeXAxis)
     ASSERT_NO_THROW_GMX(presentAllData(input, &data));
 }
 
+TEST_F(AverageModuleTest, CanCustomizeNonUniformXAxis)
+{
+    const AnalysisDataTestInput &input = SimpleInputData::get();
+    gmx::AnalysisData            data;
+    ASSERT_NO_THROW_GMX(setupDataObject(input, &data));
+
+    gmx::AnalysisDataAverageModulePointer module(new gmx::AnalysisDataAverageModule());
+    data.addModule(module);
+    module->setXAxisValue(0, 2.0);
+    module->setXAxisValue(1, 3.0);
+    module->setXAxisValue(2, 5.0);
+
+    ASSERT_NO_THROW_GMX(addStaticCheckerModule(input, &data));
+    ASSERT_NO_THROW_GMX(addReferenceCheckerModule("InputData", &data));
+    ASSERT_NO_THROW_GMX(addReferenceCheckerModule("Average", module.get()));
+    ASSERT_NO_THROW_GMX(presentAllData(input, &data));
+}
+
 /********************************************************************
  * Tests for gmx::AnalysisDataFrameAverageModule.
  */
diff --git a/src/gromacs/analysisdata/tests/refdata/AverageModuleTest_CanCustomizeNonUniformXAxis.xml b/src/gromacs/analysisdata/tests/refdata/AverageModuleTest_CanCustomizeNonUniformXAxis.xml
new file mode 100644 (file)
index 0000000..61dd93e
--- /dev/null
@@ -0,0 +1,83 @@
+<?xml version="1.0"?>
+<?xml-stylesheet type="text/xsl" href="referencedata.xsl"?>
+<ReferenceData>
+  <AnalysisData Name="InputData">
+    <DataFrame Name="Frame0">
+      <Real Name="X">1</Real>
+      <DataValues>
+        <Int Name="Count">3</Int>
+        <DataValue>
+          <Real Name="Value">0</Real>
+        </DataValue>
+        <DataValue>
+          <Real Name="Value">1</Real>
+        </DataValue>
+        <DataValue>
+          <Real Name="Value">2</Real>
+        </DataValue>
+      </DataValues>
+    </DataFrame>
+    <DataFrame Name="Frame1">
+      <Real Name="X">2</Real>
+      <DataValues>
+        <Int Name="Count">3</Int>
+        <DataValue>
+          <Real Name="Value">1</Real>
+        </DataValue>
+        <DataValue>
+          <Real Name="Value">1</Real>
+        </DataValue>
+        <DataValue>
+          <Real Name="Value">1</Real>
+        </DataValue>
+      </DataValues>
+    </DataFrame>
+    <DataFrame Name="Frame2">
+      <Real Name="X">3</Real>
+      <DataValues>
+        <Int Name="Count">3</Int>
+        <DataValue>
+          <Real Name="Value">2</Real>
+        </DataValue>
+        <DataValue>
+          <Real Name="Value">0</Real>
+        </DataValue>
+        <DataValue>
+          <Real Name="Value">0</Real>
+        </DataValue>
+      </DataValues>
+    </DataFrame>
+  </AnalysisData>
+  <AnalysisData Name="Average">
+    <DataFrame Name="Frame0">
+      <Real Name="X">2</Real>
+      <DataValues>
+        <Int Name="Count">1</Int>
+        <DataValue>
+          <Real Name="Value">1</Real>
+          <Real Name="Error">1</Real>
+        </DataValue>
+      </DataValues>
+    </DataFrame>
+    <DataFrame Name="Frame1">
+      <Real Name="X">3</Real>
+      <DataValues>
+        <Int Name="Count">1</Int>
+        <DataValue>
+          <Real Name="Value">0.66666666666666674</Real>
+          <Real Name="Error">0.57735026918962584</Real>
+        </DataValue>
+      </DataValues>
+    </DataFrame>
+    <DataFrame Name="Frame2">
+      <Real Name="X">5</Real>
+      <DataValues>
+        <Int Name="Count">1</Int>
+        <DataValue>
+          <Real Name="Value">1</Real>
+          <Real Name="Error">1</Real>
+        </DataValue>
+      </DataValues>
+    </DataFrame>
+  </AnalysisData>
+</ReferenceData>