real _currx;
//! dx value for the current frame.
real _currdx;
+ /*! \brief
+ * Total number of frames in the data.
+ *
+ * The counter is incremented in notifyFrameStart().
+ */
+ int _nframes;
};
/*! \internal \brief
*
* \param[in] index Zero-based index for the frame to query.
* Negative value counts backwards from the current frame.
+ * \param[in] nframes Total number of frames in the data.
* \returns Index in \a _store corresponding to \p index,
* or -1 if not available.
*/
- int getStoreIndex(int index) const;
+ int getStoreIndex(int index, int nframes) const;
- /*! \brief
- * Total number of complete frames in the data.
- */
- int _nframes;
/*! \brief
* Number of elements in \a _store.
*
AbstractAnalysisData::Impl::Impl()
: _bDataStart(false), _bInData(false), _bInFrame(false),
- _bAllowMissing(true)
+ _bAllowMissing(true), _nframes(0)
{
}
}
+int
+AbstractAnalysisData::frameCount() const
+{
+ return _impl->_nframes;
+}
+
+
bool
AbstractAnalysisData::getData(int index, real *x, const real **y,
const bool **missing) const
_impl->_bInFrame = true;
_impl->_currx = x;
_impl->_currdx = dx;
+ ++_impl->_nframes;
Impl::ModuleList::const_iterator i;
for (i = _impl->_modules.begin(); i != _impl->_modules.end(); ++i)
*/
AbstractAnalysisDataStored::Impl::Impl()
- : _nframes(0), _nalloc(0), _bStoreAll(false), _nextind(-1)
+ : _nalloc(0), _bStoreAll(false), _nextind(-1)
{
}
int
-AbstractAnalysisDataStored::Impl::getStoreIndex(int index) const
+AbstractAnalysisDataStored::Impl::getStoreIndex(int index, int nframes) const
{
// Check that the requested index is available.
- if ((index < 0 && (-index > _nalloc || -index > _nframes))
- || index >= _nframes || (index >= 0 && index < _nframes - _nalloc))
+ if ((index < 0 && (-index > _nalloc || -index > nframes))
+ || index >= nframes || (index >= 0 && index < nframes - _nalloc))
{
return -1;
}
index += _nalloc;
}
}
- else if (_nframes > _nalloc)
+ else if (nframes > _nalloc)
{
index %= _nalloc;
}
}
-int
-AbstractAnalysisDataStored::frameCount() const
-{
- return _impl->_nframes;
-}
-
-
bool
AbstractAnalysisDataStored::getDataWErr(int index, real *x, real *dx,
const real **y, const real **dy,
const bool **present) const
{
- index = _impl->getStoreIndex(index);
+ index = _impl->getStoreIndex(index, frameCount());
if (index < 0)
{
return false;
}
}
}
- ++_impl->_nframes;
// Notify modules of new data.
notifyPointsAdd(0, ncol, y, dy, present);
* produced. If requestStorage() has been successfully called,
* getData() can be used to access some or all of these frames.
*/
- virtual int frameCount() const = 0;
+ int frameCount() const;
/*! \brief
* Access stored data.
*
public:
virtual ~AbstractAnalysisDataStored();
- virtual int frameCount() const;
virtual bool getDataWErr(int index, real *x, real *dx,
const real **y, const real **dy,
const bool **present = 0) const;
}
-int
-AbstractAnalysisArrayData::frameCount() const
-{
- return _bReady ? _nrows : 0;
-}
-
-
bool
AbstractAnalysisArrayData::getDataWErr(int index, real *x, real *dx,
const real **y, const real **dy,
public:
virtual ~AbstractAnalysisArrayData();
- virtual int frameCount() const;
virtual bool getDataWErr(int index, real *x, real *dx,
const real **y, const real **dy,
const bool **present = 0) const;
}
-int
-AnalysisDataDisplacementModule::frameCount() const
-{
- return _impl->nstored > 1 ? _impl->nstored - 1 : 0;
-}
-
-
bool
AnalysisDataDisplacementModule::getDataWErr(int index, real *x, real *dx,
const real **y, const real **dy,
*/
void setMSDHistogram(AnalysisDataBinAverageModule *histm);
- virtual int frameCount() const;
virtual bool getDataWErr(int index, real *x, real *dx,
const real **y, const real **dy,
const bool **present = 0) const;
// Input data for the tests below.
using gmx::test::END_OF_DATA;
using gmx::test::END_OF_FRAME;
+using gmx::test::MPSTOP;
static const real inputdata[] = {
1.0, 0.0, 1.0, 2.0, END_OF_FRAME,
2.0, 1.0, 1.0, 1.0, END_OF_FRAME,
for (int row = 0; row < input.frameCount(); ++row)
{
const gmx::test::AnalysisDataTestInputFrame &frame = input.frame(row);
+ const gmx::test::AnalysisDataTestInputPointSet &points = frame.points();
ASSERT_NO_THROW(handle->startFrame(row, frame.x(), frame.dx()));
for (int column = 0; column < input.columnCount(); ++column)
{
- ASSERT_NO_THROW(handle->addPoint(column, frame.yptr()[column]));
+ ASSERT_NO_THROW(handle->addPoint(column, points.y(column)));
}
ASSERT_NO_THROW(handle->finishFrame());
EXPECT_EQ(row + 1, data.frameCount());
ASSERT_NO_THROW(presentAllData(input, &data));
}
+// Input data for the tests below.
+static const real multipointinputdata[] = {
+ 1.0, 0.0, 1.0, 2.0, MPSTOP, 1.1, 2.1, 1.1, MPSTOP, 2.2, 1.2, 0.2, END_OF_FRAME,
+ 2.0, 1.0, 1.0, 1.0, MPSTOP, 2.1, 1.1, 0.1, MPSTOP, 1.2, 0.2, 1.2, END_OF_FRAME,
+ 3.0, 2.0, 0.0, 0.0, MPSTOP, 3.1, 2.1, 1.1, MPSTOP, 0.2, 2.2, 1.2, END_OF_FRAME,
+ END_OF_DATA
+};
+
+/*
+ * Tests that multipoint data is forwarded correctly to modules using two
+ * independent modules.
+ */
+TEST_F(AnalysisDataTest, MultipointCallsModuleCorrectly)
+{
+ gmx::test::AnalysisDataTestInput input(multipointinputdata);
+ gmx::AnalysisData data;
+ data.setColumns(input.columnCount(), true);
+
+ ASSERT_NO_THROW(addStaticCheckerModule(input, &data));
+ ASSERT_NO_THROW(addStaticCheckerModule(input, &data));
+ ASSERT_NO_THROW(presentAllData(input, &data));
+}
+
+/*
+ * Tests that multipoint data is forwarded correctly to modules that are added
+ * using addColumnModule().
+ * Uses two independent modules.
+ */
+TEST_F(AnalysisDataTest, MultipointCallsColumnModuleCorrectly)
+{
+ gmx::test::AnalysisDataTestInput input(multipointinputdata);
+ gmx::AnalysisData data;
+ data.setColumns(input.columnCount(), true);
+
+ ASSERT_NO_THROW(addStaticColumnCheckerModule(input, 0, 2, &data));
+ ASSERT_NO_THROW(addStaticColumnCheckerModule(input, 2, 1, &data));
+ ASSERT_NO_THROW(presentAllData(input, &data));
+}
+
} // namespace
{
/********************************************************************
- * AnalysisDataTestInputFrame
+ * AnalysisDataTestInputPointSet
*/
-AnalysisDataTestInputFrame::AnalysisDataTestInputFrame()
+AnalysisDataTestInputPointSet::AnalysisDataTestInputPointSet()
{
}
/********************************************************************
- * AnalysisDataTestInput
+ * AnalysisDataTestInputFrame
*/
-namespace
+AnalysisDataTestInputFrame::AnalysisDataTestInputFrame()
{
- void checkValidDataItem(real data)
- {
- GMX_RELEASE_ASSERT(data != END_OF_DATA && data != END_OF_FRAME,
- "Inconsistent data array");
- }
}
+
+/********************************************************************
+ * AnalysisDataTestInput
+ */
+
AnalysisDataTestInput::AnalysisDataTestInput(const real *data)
- : columnCount_(0)
+ : columnCount_(0), bMultipoint_(false)
{
- // Count rows and columns.
- int rows = 0, columns = -1;
+ size_t columns = 0;
const real *dataptr = data;
- for (int i = 0; dataptr[i] != END_OF_DATA; ++i)
+
+ while (*dataptr != END_OF_DATA)
{
- if (dataptr[i] == END_OF_FRAME)
+ frames_.push_back(AnalysisDataTestInputFrame());
+ AnalysisDataTestInputFrame &frame = frames_.back();
+ GMX_RELEASE_ASSERT(*dataptr != END_OF_FRAME && *dataptr != MPSTOP,
+ "Empty data frame");
+ frame.index_ = frames_.size() - 1;
+ frame.x_ = *dataptr;
+ while (*dataptr != END_OF_FRAME)
{
- GMX_RELEASE_ASSERT(i > 0, "Empty data frame");
- if (columns == -1)
+ ++dataptr;
+ frame.points_.push_back(AnalysisDataTestInputPointSet());
+ AnalysisDataTestInputPointSet &points = frame.points_.back();
+ while (*dataptr != MPSTOP && *dataptr != END_OF_FRAME)
+ {
+ GMX_RELEASE_ASSERT(*dataptr != END_OF_DATA,
+ "Premature end of data marker");
+ points.y_.push_back(*dataptr);
+ ++dataptr;
+ }
+ size_t frameColumns = points.y_.size();
+ GMX_RELEASE_ASSERT(frameColumns > 0U, "Empty data point set");
+ if (*dataptr == MPSTOP)
{
- columns = i - 1;
+ bMultipoint_ = true;
}
- GMX_RELEASE_ASSERT(columns == i - 1,
+ GMX_RELEASE_ASSERT(!(!bMultipoint_ && columns > 0U && columns != frameColumns),
"Different frames have different number of columns");
- ++rows;
- dataptr += i + 1;
- i = -1;
+ if (columns < frameColumns)
+ {
+ columns = frameColumns;
+ }
}
- }
- GMX_RELEASE_ASSERT(rows > 0, "Empty data");
- columnCount_ = columns;
-
- // Store the data.
- frames_.resize(rows);
- dataptr = data;
- for (int r = 0; r < rows; ++r)
- {
- AnalysisDataTestInputFrame &frame = frames_[r];
- checkValidDataItem(*dataptr);
- frame.index_ = r;
- frame.x_ = *dataptr;
++dataptr;
- frame.y_.resize(columns);
- for (int c = 0; c < columns; ++c)
- {
- checkValidDataItem(dataptr[c]);
- frame.y_[c] = dataptr[c];
- }
- GMX_RELEASE_ASSERT(dataptr[columns] == END_OF_FRAME,
- "Inconsistent data array");
- dataptr += columns + 1;
}
- GMX_RELEASE_ASSERT(*dataptr == END_OF_DATA, "Inconsistent data array");
+ GMX_RELEASE_ASSERT(frames_.size() > 0U, "Empty data");
+ columnCount_ = columns;
}
{
const AnalysisDataTestInputFrame &frame = input.frame(row);
handle->startFrame(row, frame.x(), frame.dx());
- handle->addPoints(0, input.columnCount(), frame.yptr(), frame.dyptr(),
- frame.presentptr());
+ for (int i = 0; i < frame.pointSetCount(); ++i)
+ {
+ const AnalysisDataTestInputPointSet &points = frame.points(i);
+ handle->addPoints(0, points.size(), points.yptr(), points.dyptr(),
+ points.presentptr());
+ }
handle->finishFrame();
}
AbstractAnalysisData *source)
{
std::auto_ptr<MockAnalysisModule> module(
- new MockAnalysisModule(gmx::AnalysisDataModuleInterface::efAllowMulticolumn));
+ new MockAnalysisModule(gmx::AnalysisDataModuleInterface::efAllowMulticolumn |
+ gmx::AnalysisDataModuleInterface::efAllowMultipoint));
module->setupStaticCheck(data, source);
source->addModule(module.release());
}
AbstractAnalysisData *source)
{
std::auto_ptr<MockAnalysisModule> module(
- new MockAnalysisModule(gmx::AnalysisDataModuleInterface::efAllowMulticolumn));
+ new MockAnalysisModule(gmx::AnalysisDataModuleInterface::efAllowMulticolumn |
+ gmx::AnalysisDataModuleInterface::efAllowMultipoint));
module->setupStaticColumnCheck(data, firstcol, n, source);
source->addColumnModule(firstcol, n, module.release());
}
#include <gtest/gtest.h>
#include "gromacs/legacyheaders/types/simple.h"
+#include "gromacs/fatalerror/gmxassert.h"
#include "testutils/refdata.h"
const real END_OF_DATA = std::numeric_limits<real>::max();
//! Constant to use to signify end of one data frame for AnalysisDataTestInput.
const real END_OF_FRAME = std::numeric_limits<real>::min();
+//! Constant to use to signify end of one multipoint set for AnalysisDataTestInput.
+const real MPSTOP = -std::numeric_limits<real>::max();
+
+/*! \libinternal \brief
+ * Represents a single set of points in AnalysisDataTestInputFrame structure.
+ *
+ * If the data is not multipoint, each frame contains exactly one set of
+ * points. If there is more than one set of points, each of these sets is
+ * passed separately to AnalysisDataHandle::addPoints().
+ *
+ * \ingroup module_analysisdata
+ */
+class AnalysisDataTestInputPointSet
+{
+ public:
+ AnalysisDataTestInputPointSet();
+
+ int size() const { return y_.size(); }
+ real y(int i) const { return y_[i]; }
+ const std::vector<real> &yvector() const { return y_; }
+ const real *yptr() const { return &y_[0]; }
+ const real *dyptr() const { return NULL; }
+ const bool *presentptr() const { return NULL; }
+
+ private:
+ std::vector<real> y_;
+
+ friend class AnalysisDataTestInput;
+};
/*! \libinternal \brief
* Represents a single frame in AnalysisDataTestInput structure.
public:
AnalysisDataTestInputFrame();
+ bool isMultipoint() const { return points_.size() > 1; }
+
int index() const { return index_; }
real x() const { return x_; }
real dx() const { return 0.0; }
- real y(int i) const { return y_[i]; }
- const std::vector<real> &yvector() const { return y_; }
- const real *yptr() const { return &y_[0]; }
- const real *dyptr() const { return NULL; }
- const bool *presentptr() const { return NULL; }
+
+ int pointSetCount() const { return points_.size(); }
+ const AnalysisDataTestInputPointSet &points(int index = 0) const
+ {
+ GMX_ASSERT(index >= 0 && static_cast<size_t>(index) < points_.size(),
+ "Point set index out of range");
+ return points_[index];
+ }
private:
int index_;
real x_;
- std::vector<real> y_;
+ std::vector<AnalysisDataTestInputPointSet> points_;
friend class AnalysisDataTestInput;
};
* The input array should consist of a set of frames, separated by a
* END_OF_FRAME marker. The first value for a frame is the X value,
* all following values are Y values.
+ * For multipoint data, one frame can contain several point sets,
+ * separated by MPSTOP markers. There should be no MPSTOP marker after
+ * the last point set, only an END_OF_FRAME marker. All point sets are
+ * assumed to start from column zero, but the sets may contain
+ * different number of columns. For non-multipoint data, all frames
+ * must containt the same number of columns.
* The final element in the array (after the last END_OF_FRAME) should
* be END_OF_DATA.
*/
int frameCount() const { return frames_.size(); }
int columnCount() const { return columnCount_; }
+ bool isMultipoint() const { return bMultipoint_; }
const AnalysisDataTestInputFrame &frame(int index) const;
private:
int columnCount_;
+ bool bMultipoint_;
std::vector<AnalysisDataTestInputFrame> frames_;
};
* checks are done during the these methods, by the added mock modules.
*
* \todo
- * Support for errors and for multipoint data.
+ * Support for errors and for arbitrary multipoint data.
*
* \see AnalysisDataTestInput
*
namespace
{
-void checkFrame(real x, real dx, int firstcol, int n, const real *y,
- const AnalysisDataTestInputFrame &frame)
+void checkFrame(real x, real dx, const AnalysisDataTestInputFrame &frame)
{
EXPECT_FLOAT_EQ(frame.x(), x);
EXPECT_FLOAT_EQ(frame.dx(), dx);
+}
+
+void checkPoints(int firstcol, int n, const real *y,
+ const AnalysisDataTestInputPointSet &points)
+{
for (int i = 0; i < n; ++i)
{
- EXPECT_FLOAT_EQ(frame.y(firstcol + i), y[i]);
+ EXPECT_FLOAT_EQ(points.y(firstcol + i), y[i]);
}
}
+void checkFrame(real x, real dx, int firstcol, int n, const real *y,
+ const AnalysisDataTestInputFrame &frame)
+{
+ checkFrame(x, dx, frame);
+ checkPoints(firstcol, n, y, frame.points());
+}
+
class StaticDataPointsChecker
{
public:
StaticDataPointsChecker(const AnalysisDataTestInputFrame *frame,
+ const AnalysisDataTestInputPointSet *points,
int firstcol, int n)
- : frame_(frame), firstcol_(firstcol), n_(n)
+ : frame_(frame), points_(points), firstcol_(firstcol), n_(n)
{
}
SCOPED_TRACE(formatString("Frame %d", frame_->index()));
EXPECT_EQ(0, firstcol);
EXPECT_EQ(n_, n);
- checkFrame(x, dx, firstcol_ + firstcol, n, y, *frame_);
+ checkFrame(x, dx, *frame_);
+ checkPoints(firstcol_ + firstcol, n, y, *points_);
}
private:
const AnalysisDataTestInputFrame *frame_;
+ const AnalysisDataTestInputPointSet *points_;
int firstcol_;
int n_;
};
{
const AnalysisDataTestInputFrame &frame = data.frame(row);
EXPECT_CALL(*this, frameStarted(frame.x(), frame.dx()));
- EXPECT_CALL(*this, pointsAdded(frame.x(), frame.dx(), 0,
- data.columnCount(), _, _, _))
- .WillOnce(Invoke(StaticDataPointsChecker(&frame, 0, data.columnCount())));
+ for (int ps = 0; ps < frame.pointSetCount(); ++ps)
+ {
+ const AnalysisDataTestInputPointSet &points = frame.points(ps);
+ EXPECT_CALL(*this, pointsAdded(frame.x(), frame.dx(), 0,
+ points.size(), _, _, _))
+ .WillOnce(Invoke(StaticDataPointsChecker(&frame, &points, 0,
+ data.columnCount())));
+ }
EXPECT_CALL(*this, frameFinished());
}
EXPECT_CALL(*this, dataFinished());
{
const AnalysisDataTestInputFrame &frame = data.frame(row);
EXPECT_CALL(*this, frameStarted(frame.x(), frame.dx()));
- EXPECT_CALL(*this, pointsAdded(frame.x(), frame.dx(), 0, n, _, _, _))
- .WillOnce(Invoke(StaticDataPointsChecker(&frame, firstcol, n)));
+ for (int ps = 0; ps < frame.pointSetCount(); ++ps)
+ {
+ const AnalysisDataTestInputPointSet &points = frame.points(ps);
+ EXPECT_CALL(*this, pointsAdded(frame.x(), frame.dx(), 0, n, _, _, _))
+ .WillOnce(Invoke(StaticDataPointsChecker(&frame, &points, firstcol, n)));
+ }
EXPECT_CALL(*this, frameFinished());
}
EXPECT_CALL(*this, dataFinished());
{
GMX_RELEASE_ASSERT(data.columnCount() == source->columnCount(),
"Mismatching data column count");
+ GMX_RELEASE_ASSERT(!data.isMultipoint() && !source->isMultipoint(),
+ "Storage testing not implemented for multipoint data");
::testing::InSequence dummy;
using ::testing::_;