Use separate class for data storage.
authorTeemu Murtola <teemu.murtola@gmail.com>
Fri, 6 Jan 2012 17:56:41 +0000 (19:56 +0200)
committerTeemu Murtola <teemu.murtola@gmail.com>
Wed, 22 Feb 2012 05:38:09 +0000 (07:38 +0200)
Moved storage implementation from AbstractAnalysisDataStored and
parallel storage implementation from AnalysisData to a common
AnalysisDataStorage helper class that is used through containment
and delegation instead of inheritance.

Makes the code more reusable in eventual implementation of
parallel-enabled analysis modules, moves implementation details away
from the public interface (AnalysisDataStorage now only needs to be
forward-declared in public headers), and clarifies responsibilities in
the code.

Support for multipoint data is still not very good (but not worse than
it was), but should now be easier to improve.  In the future, it could
be considered whether to split the unit tests such that storage tests
test AnalysisDataStorage directly instead of through the AnalysisData
interface, but currently, these classes are quite tightly bound
together, so there is not much to gain from separate testing.

Related to issue #827.

Change-Id: I8c34a618893c770bff6d8216500d1061455bee89

25 files changed:
share/template/template.cpp
src/gromacs/analysisdata/abstractdata-impl.h
src/gromacs/analysisdata/abstractdata.cpp
src/gromacs/analysisdata/abstractdata.h
src/gromacs/analysisdata/analysisdata-impl.h
src/gromacs/analysisdata/analysisdata.cpp
src/gromacs/analysisdata/analysisdata.h
src/gromacs/analysisdata/datastorage-impl.h [new file with mode: 0644]
src/gromacs/analysisdata/datastorage.cpp [new file with mode: 0644]
src/gromacs/analysisdata/datastorage.h [new file with mode: 0644]
src/gromacs/analysisdata/modules/histogram-impl.h
src/gromacs/analysisdata/modules/histogram.cpp
src/gromacs/analysisdata/modules/histogram.h
src/gromacs/analysisdata/paralleloptions.h [new file with mode: 0644]
src/gromacs/analysisdata/tests/analysisdata.cpp
src/gromacs/analysisdata/tests/datatest.cpp
src/gromacs/analysisdata/tests/datatest.h
src/gromacs/trajectoryanalysis/analysismodule-impl.h
src/gromacs/trajectoryanalysis/analysismodule.cpp
src/gromacs/trajectoryanalysis/analysismodule.h
src/gromacs/trajectoryanalysis/cmdlinerunner.cpp
src/gromacs/trajectoryanalysis/modules/angle.cpp
src/gromacs/trajectoryanalysis/modules/distance.cpp
src/gromacs/trajectoryanalysis/modules/select.cpp
src/gromacs/trajectoryanalysis/modules/select.h

index 5a7b5c7bf41aa855379dacfc91769c7351c0bdf8..e42db5930086524f5e158ee3f3db0d43560527ce 100644 (file)
@@ -49,7 +49,7 @@ class AnalysisTemplate : public TrajectoryAnalysisModule
                                   const TopologyInformation &top);
 
         virtual TrajectoryAnalysisModuleData *startFrames(
-                    AnalysisDataParallelOptions opt,
+                    const AnalysisDataParallelOptions &opt,
                     const SelectionCollection &selections);
         virtual void analyzeFrame(int frnr, const t_trxframe &fr, t_pbc *pbc,
                                   TrajectoryAnalysisModuleData *pdata);
@@ -76,7 +76,7 @@ class AnalysisTemplate::ModuleData : public TrajectoryAnalysisModuleData
 {
     public:
         ModuleData(TrajectoryAnalysisModule *module,
-                   AnalysisDataParallelOptions opt,
+                   const AnalysisDataParallelOptions &opt,
                    const SelectionCollection &selections)
             : TrajectoryAnalysisModuleData(module, opt, selections),
               _nb(NULL)
@@ -171,7 +171,7 @@ AnalysisTemplate::initAnalysis(const TrajectoryAnalysisSettings &settings,
 
 
 TrajectoryAnalysisModuleData *
-AnalysisTemplate::startFrames(AnalysisDataParallelOptions opt,
+AnalysisTemplate::startFrames(const AnalysisDataParallelOptions &opt,
                               const SelectionCollection &selections)
 {
     std::auto_ptr<ModuleData> pdata(new ModuleData(this, opt, selections));
@@ -199,7 +199,7 @@ AnalysisTemplate::analyzeFrame(int frnr, const t_trxframe &fr, t_pbc *pbc,
             frave += nb->minimumDistance(sel->x(i));
         }
         frave /= nr;
-        dh->addPoint(g, frave);
+        dh->setPoint(g, frave);
     }
     dh->finishFrame();
 }
index 939e3469504fec11247797a595affd4b11f3e78d..9471424321a3bb46f6f483e114dfce9f37865568 100644 (file)
@@ -30,8 +30,7 @@
  */
 /*! \internal \file
  * \brief
- * Declares internal implementation classes for gmx::AbstractAnalysisData and
- * gmx::AbstractAnalysisDataStored.
+ * Declares internal implementation class for gmx::AbstractAnalysisData.
  *
  * \author Teemu Murtola <teemu.murtola@cbr.su.se>
  * \ingroup module_analysisdata
@@ -103,78 +102,6 @@ class AbstractAnalysisData::Impl
         int                     _nframes;
 };
 
-/*! \internal \brief
- * Internal implementation class for storing a single data frame.
- *
- * \ingroup module_analysisdata
- */
-class AnalysisDataFrame
-{
-    public:
-        explicit AnalysisDataFrame(int columnCount);
-        ~AnalysisDataFrame();
-
-        //! Zero-based global index of the frame.
-        int                     _index;
-        //! x value of the frame.
-        real                    _x;
-        //! Error of x for the frame.
-        real                    _dx;
-        //! Number of columns in the frame.
-        int                     _columnCount;
-        //! Array of column values for the frame.
-        real                   *_y;
-        //! Array of column error values for the frame.
-        real                   *_dy;
-        //! Array of flags that tell whether a value is present.
-        bool                   *_present;
-};
-
-/*! \internal \brief
- * Private implementation class for AbstractAnalysisDataStored.
- *
- * \ingroup module_analysisdata
- */
-class AbstractAnalysisDataStored::Impl
-{
-    public:
-        //! Shorthand for a list of data frames that are currently stored.
-        typedef std::vector<AnalysisDataFrame *> FrameList;
-
-        Impl();
-        ~Impl();
-
-        /*! \brief
-         * Calculates the index of a frame in the storage vector.
-         *
-         * \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, int nframes) const;
-
-        /*! \brief
-         * Number of elements in \a _store.
-         *
-         * Also holds the number of frames that should be stored, even before
-         * \a _store has been allocated.
-         */
-        int                     _nalloc;
-        //! Whether all frames should be stored.
-        bool                    _bStoreAll;
-        //! List of data frames that are currently stored.
-        FrameList               _store;
-        /*! \brief
-         * Index in \a _store where the next frame will be stored.
-         *
-         * This counter is incremented after notifyPointsAdd() has been called
-         * for the frame.
-         */
-        int                     _nextind;
-};
-
 } // namespace gmx
 
 #endif
index 249f58e2637e41a5b3ee8e1b8f748b0a2a90cc82..fc0fd9d1e66ce1dcc3e97de097b49dd2fad85fe9 100644 (file)
@@ -30,7 +30,7 @@
  */
 /*! \internal \file
  * \brief
- * Implements gmx::AbstractAnalysisData and gmx::AbstractAnalysisDataStored.
+ * Implements gmx::AbstractAnalysisData.
  *
  * \author Teemu Murtola <teemu.murtola@cbr.su.se>
  * \ingroup module_analysisdata
@@ -39,9 +39,6 @@
 
 #include <memory>
 
-// Legacy header.
-#include "smalloc.h"
-
 #include "gromacs/fatalerror/exceptions.h"
 #include "gromacs/fatalerror/gmxassert.h"
 
@@ -304,16 +301,6 @@ AbstractAnalysisData::notifyPointsAdd(const AnalysisDataPointSetRef &points) con
 }
 
 
-void
-AbstractAnalysisData::notifyPointsAdd(int firstcol, int n,
-                                      const real *y, const real *dy,
-                                      const bool *present) const
-{
-    notifyPointsAdd(AnalysisDataPointSetRef(
-            _impl->_currHeader, firstcol, n, y, dy, present));
-}
-
-
 void
 AbstractAnalysisData::notifyFrameFinish(const AnalysisDataFrameHeader &header) const
 {
@@ -335,13 +322,6 @@ AbstractAnalysisData::notifyFrameFinish(const AnalysisDataFrameHeader &header) c
 }
 
 
-void
-AbstractAnalysisData::notifyFrameFinish() const
-{
-    notifyFrameFinish(_impl->_currHeader);
-}
-
-
 void
 AbstractAnalysisData::notifyDataFinish() const
 {
@@ -357,232 +337,4 @@ AbstractAnalysisData::notifyDataFinish() const
     }
 }
 
-
-/********************************************************************
- * AnalysisDataFrame
- */
-
-AnalysisDataFrame::AnalysisDataFrame(int columnCount)
-    : _index(-1), _x(0.0), _dx(0.0), _columnCount(columnCount),
-      _y(NULL), _dy(NULL), _present(NULL)
-{
-    snew(_y, columnCount);
-    snew(_dy, columnCount);
-    snew(_present, columnCount);
-}
-
-
-AnalysisDataFrame::~AnalysisDataFrame()
-{
-    sfree(_y);
-    sfree(_dy);
-    sfree(_present);
-}
-
-
-/********************************************************************
- * AbstractAnalysisDataStored::Impl
- */
-
-AbstractAnalysisDataStored::Impl::Impl()
-    : _nalloc(0), _bStoreAll(false), _nextind(-1)
-{
-}
-
-
-AbstractAnalysisDataStored::Impl::~Impl()
-{
-    FrameList::const_iterator i;
-    for (i = _store.begin(); i != _store.end(); ++i)
-    {
-        delete *i;
-    }
-}
-
-
-int
-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))
-    {
-        return -1;
-    }
-    // Calculate the index into the storage array.
-    if (index < 0)
-    {
-        index = _nextind + index;
-        if (index < 0)
-        {
-            index += _nalloc;
-        }
-    }
-    else if (nframes > _nalloc)
-    {
-        index %= _nalloc;
-    }
-    return index;
-}
-
-
-/********************************************************************
- * AbstractAnalysisDataStored
- */
-
-AbstractAnalysisDataStored::AbstractAnalysisDataStored()
-    : _impl(new Impl())
-{
-}
-
-
-AbstractAnalysisDataStored::~AbstractAnalysisDataStored()
-{
-    delete _impl;
-}
-
-
-AnalysisDataFrameRef
-AbstractAnalysisDataStored::tryGetDataFrameInternal(int index) const
-{
-    if (!_impl->_bStoreAll && index < frameCount() - _impl->_nalloc)
-    {
-        return AnalysisDataFrameRef();
-    }
-    AnalysisDataFrame *fr = _impl->_store[index % _impl->_nalloc];
-    return AnalysisDataFrameRef(fr->_index, fr->_x, fr->_dx, fr->_columnCount,
-                                fr->_y, fr->_dy, fr->_present);
-}
-
-
-bool
-AbstractAnalysisDataStored::requestStorageInternal(int nframes)
-{
-    GMX_RELEASE_ASSERT(!isMultipoint(), "Storage of multipoint data not supported");
-
-    // Handle the case when everything needs to be stored.
-    if (nframes == -1)
-    {
-        _impl->_bStoreAll = true;
-        _impl->_nalloc = 1;
-        return true;
-    }
-    // Check whether an earier call has requested more storage.
-    if (_impl->_bStoreAll || nframes < _impl->_nalloc)
-    {
-        return true;
-    }
-    // nframes previous frames plus the current one
-    _impl->_nalloc = nframes + 1;
-    return true;
-}
-
-
-void
-AbstractAnalysisDataStored::setMultipoint(bool multipoint)
-{
-    GMX_RELEASE_ASSERT(_impl->_nalloc == 0 || !multipoint,
-                       "Storage of multipoint data not supported");
-    AbstractAnalysisData::setMultipoint(multipoint);
-}
-
-
-void
-AbstractAnalysisDataStored::startDataStore()
-{
-    // We first notify any attached modules, because they also might request
-    // some storage.
-    notifyDataStart();
-
-    // If any storage has been requested, preallocate it.
-    if (_impl->_nalloc > 0)
-    {
-        _impl->_store.resize(_impl->_nalloc);
-        for (int i = 0; i < _impl->_nalloc; ++i)
-        {
-            _impl->_store[i] = new AnalysisDataFrame(columnCount());
-        }
-        _impl->_nextind = 0;
-    }
-}
-
-
-void
-AbstractAnalysisDataStored::startNextFrame(const AnalysisDataFrameHeader &header)
-{
-    // Start storing the frame if needed.
-    if (_impl->_nalloc > 0)
-    {
-        if (_impl->_nextind >= _impl->_nalloc)
-        {
-            if (_impl->_bStoreAll)
-            {
-                _impl->_nalloc = _impl->_nextind + 1;
-                _impl->_store.resize(_impl->_nalloc);
-                for (int i = _impl->_nextind; i < _impl->_nalloc; ++i)
-                {
-                    _impl->_store[i] = new AnalysisDataFrame(columnCount());
-                }
-            }
-            else
-            {
-                _impl->_nextind = 0;
-            }
-        }
-
-        _impl->_store[_impl->_nextind]->_index = header.index();
-        _impl->_store[_impl->_nextind]->_x     = header.x();
-        _impl->_store[_impl->_nextind]->_dx    = header.dx();
-    }
-
-    // Notify any modules.
-    notifyFrameStart(header);
-}
-
-
-void
-AbstractAnalysisDataStored::storeThisFrame(const real *y, const real *dy,
-                                           const bool *present)
-{
-    int ncol = columnCount();
-
-    // Store the values if required.
-    if (_impl->_nextind >= 0)
-    {
-        AnalysisDataFrame *fr = _impl->_store[_impl->_nextind];
-
-        for (int i = 0; i < ncol; ++i)
-        {
-            fr->_y[i] = y[i];
-            if (dy)
-            {
-                fr->_dy[i] = dy[i];
-            }
-            if (present)
-            {
-                fr->_present[i] = present[i];
-            }
-        }
-    }
-
-    // Notify modules of new data.
-    notifyPointsAdd(0, ncol, y, dy, present);
-    // The index needs to be incremented after the notifications to allow
-    // the modules to use getDataFrame() properly.
-    if (_impl->_nextind >= 0)
-    {
-        ++_impl->_nextind;
-    }
-    notifyFrameFinish();
-}
-
-
-void
-AbstractAnalysisDataStored::storeNextFrame(real x, real dx, const real *y,
-                                           const real *dy, const bool *present)
-{
-    startNextFrame(AnalysisDataFrameHeader(frameCount(), x, dx));
-    storeThisFrame(y, dy, present);
-}
-
 } // namespace gmx
index 34be73652e5c7d84972cec4aae2674fb8db28bea..0c793cb8e83b644dc19c189e429c96b6c65438e3 100644 (file)
@@ -30,7 +30,7 @@
  */
 /*! \file
  * \brief
- * Declares gmx::AbstractAnalysisData and gmx::AbstractAnalysisDataStored.
+ * Declares gmx::AbstractAnalysisData.
  *
  * \author Teemu Murtola <teemu.murtola@cbr.su.se>
  * \inlibraryapi
@@ -48,6 +48,7 @@ class AnalysisDataModuleInterface;
 class AnalysisDataFrameHeader;
 class AnalysisDataFrameRef;
 class AnalysisDataPointSetRef;
+class AnalysisDataStorage;
 
 /*! \brief
  * Abstract base class for all objects that provide data.
@@ -322,15 +323,6 @@ class AbstractAnalysisData
          * for each column separately.
          */
         void notifyPointsAdd(const AnalysisDataPointSetRef &points) const;
-        /*! \brief
-         * Deprecated convenience method for not needing to construct a
-         * AnalysisDataPointSetRef object.
-         *
-         * Will be removed as part of future work.
-         */
-        void notifyPointsAdd(int firstcol, int n,
-                             const real *y, const real *dy,
-                             const bool *present) const;
         /*! \brief
          * Notifies attached modules of the end of a frame.
          *
@@ -338,12 +330,6 @@ class AbstractAnalysisData
          * notifyPointsAdd() calls for the frame.
          */
         void notifyFrameFinish(const AnalysisDataFrameHeader &header) const;
-        /*! \brief
-         * Deprecated convenience method for not needing a frame header.
-         *
-         * Will be removed as part of future work.
-         */
-        void notifyFrameFinish() const;
         /*! \brief
          * Notifies attached modules of the end of data.
          *
@@ -359,72 +345,16 @@ class AbstractAnalysisData
         int                     _ncol;
         bool                    _bMultiPoint;
 
+        /*! \brief
+         * Needed to provide access to notification methods.
+         */
+        friend class AnalysisDataStorage;
+
         // Disallow copy and assign.
         AbstractAnalysisData(const AbstractAnalysisData &);
         void operator =(const AbstractAnalysisData &);
 };
 
-
-/*! \brief
- * Abstract class that implements storage of data.
- *
- * \if libapi
- * This class implements a standard way of storing data, to avoid implementing
- * storage in each derived class separately. All the pure virtual methods of
- * AbstractData are implemented, and protected methods are provided to add data
- * to the storage. These protected methods automatically call notifyDataStart(),
- * notifyFrameStart(), notifyPointsAdd() and notifyFrameFinish()
- * functions in AbstractAnalysisData, but the derived class should still
- * call notifyDataFinish().
- *
- * Additional protected functions could be implemented to allow optimization:
- * in the current interface, some data copying is unavoidable.
- * Some changes could make it possible to obtain a pointer to the
- * storage, allowing the calculated values to be stored there directly
- * instead of a temporary array.
- * \endif
- *
- * \inlibraryapi
- * \ingroup module_analysisdata
- */
-class AbstractAnalysisDataStored : public AbstractAnalysisData
-{
-    public:
-        virtual ~AbstractAnalysisDataStored();
-
-    protected:
-        /*! \cond libapi */
-        AbstractAnalysisDataStored();
-
-        /*! \copydoc AbstractAnalysisData::setMultipoint()
-         *
-         * The overridden method also asserts if
-         * storage has been requested and \p multipoint is \c true.
-         */
-        void setMultipoint(bool multipoint);
-
-        //! Start storing data.
-        void startDataStore();
-        //! Starts storing a next frame.
-        void startNextFrame(const AnalysisDataFrameHeader &header);
-        //! Stores the whole frame in a single call after start_next_frame().
-        void storeThisFrame(const real *y, const real *dy, const bool *present);
-        //! Convenience function for storing a whole frame in a single call.
-        void storeNextFrame(real x, real dx, const real *y, const real *dy,
-                            const bool *present);
-        //! \endcond
-
-    private:
-        virtual AnalysisDataFrameRef tryGetDataFrameInternal(int index) const;
-        virtual bool requestStorageInternal(int nframes);
-
-        class Impl;
-
-        Impl                   *_impl;
-
-        // Copy and assign disallowed by base class.
-};
-
 } // namespace gmx
 
 #endif
index 7af2b58035b273d68ee610811be4e2ef8f84cc3a..b8bbbbd01556092c5578bb85f2ab2cc245764eab 100644 (file)
 #ifndef GMX_ANALYSISDATA_ANALYSISDATA_IMPL_H
 #define GMX_ANALYSISDATA_ANALYSISDATA_IMPL_H
 
-#include <memory>
 #include <vector>
 
 #include "analysisdata.h"
-
-#include "abstractdata-impl.h"
+#include "datastorage.h"
 
 namespace gmx
 {
@@ -58,42 +56,13 @@ class AnalysisData::Impl
     public:
         //! Shorthand for a list of data handles.
         typedef std::vector<AnalysisDataHandle *> HandleList;
-        //! Shorthand for a list of frames.
-        typedef std::vector<AnalysisDataFrame *>  FrameList;
 
-        //! Creates an implementation class associated with the given object.
-        explicit Impl(AnalysisData *data);
+        Impl();
         ~Impl();
 
-        /*! \brief
-         * Handles a new frame.
-         *
-         * If all earlier frames are ready, the data is directly passed to
-         * AbstractStoredData.  Otherwise, it is put into the correct location
-         * in \a _pending.  Calls processPendingFrame() after processing \p fr.
-         */
-        void addPendingFrame(AnalysisDataFrame *fr);
-        /*! \brief
-         * Passes pending frames to base class if all earlier frames are ready.
-         */
-        void processPendingFrames();
-        //! Increments \a _pstart.
-        void incrementPStart();
-
-        //! The data object that uses this implementation class.
-        AnalysisData           &_data;
+        AnalysisDataStorage     storage_;
         //! List of handles for this data object.
-        HandleList              _handles;
-        /*! \brief
-         * Index into \a _pending that points to the location where the current
-         * frame would go.
-         */
-        size_t                  _pstart;
-        /*! \brief
-         * Circular buffer for frames that are ready but waiting for earlier
-         * frames.
-         */
-        FrameList               _pending;
+        HandleList              handles_;
 };
 
 /*! \internal \brief
@@ -106,13 +75,11 @@ class AnalysisDataHandle::Impl
     public:
         //! Creates a handle associated with the given data object.
         explicit Impl(AnalysisData *data);
-        ~Impl();
 
         //! The data object that this handle belongs to.
-        AnalysisData            &_data;
-        //! Frame object where the current frame is being accumulated.
-        // Could be scoped_ptr
-        std::auto_ptr<AnalysisDataFrame> _frame;
+        AnalysisData             &data_;
+        //! Current storage frame object, or NULL if no current frame.
+        AnalysisDataStorageFrame *currentFrame_;
 };
 
 } // namespace gmx
index 0734bb48787f8e91d02721bf6507d1736eac4f59..4914a0c0eae4e3f6a5e5fb244dd906ce88ea6d96 100644 (file)
 #include <memory>
 
 #include "gromacs/analysisdata/dataframe.h"
+#include "gromacs/analysisdata/datastorage.h"
+#include "gromacs/analysisdata/paralleloptions.h"
 #include "gromacs/fatalerror/exceptions.h"
 #include "gromacs/fatalerror/gmxassert.h"
 
-#include "abstractdata-impl.h"
 #include "analysisdata-impl.h"
 
 namespace gmx
@@ -52,17 +53,9 @@ namespace gmx
 
 /********************************************************************
  * AnalysisData::Impl
- ********************************************************************/
-
-static bool
-frame_index_gtr(AnalysisDataFrame *a, AnalysisDataFrame *b)
-{
-    return a->_index > b->_index;
-}
-
+ */
 
-AnalysisData::Impl::Impl(AnalysisData *data)
-    : _data(*data), _pstart(0)
+AnalysisData::Impl::Impl()
 {
 }
 
@@ -70,84 +63,10 @@ AnalysisData::Impl::Impl(AnalysisData *data)
 AnalysisData::Impl::~Impl()
 {
     HandleList::const_iterator i;
-    for (i = _handles.begin(); i != _handles.end(); ++i)
+    for (i = handles_.begin(); i != handles_.end(); ++i)
     {
         delete *i;
     }
-
-    FrameList::const_iterator j;
-    for (j = _pending.begin(); j != _pending.end(); ++j)
-    {
-        delete *j;
-    }
-}
-
-
-void
-AnalysisData::Impl::addPendingFrame(AnalysisDataFrame *fr)
-{
-    GMX_ASSERT(fr->_index >= _data.frameCount(),
-               "addPendingFrame() called for too old frame");
-    size_t pindex = fr->_index - _data.frameCount();
-    if (pindex == 0)
-    {
-        // Just store our frame if it is the next one.
-        _data.storeNextFrame(fr->_x, fr->_dx, fr->_y, fr->_dy, fr->_present);
-        incrementPStart();
-    }
-    else
-    {
-        if (pindex >= _pending.size())
-        {
-            // TODO: We need to wait until earlier frames are ready...
-        }
-        // TODO: This is not thread-safe.
-        pindex += _pstart;
-        if (pindex > _pending.size())
-        {
-            pindex -= _pending.size();
-        }
-
-        int ncol = _data.columnCount();
-        _pending[pindex]->_x     = fr->_x;
-        _pending[pindex]->_dx    = fr->_dx;
-        for (int i = 0; i < ncol; ++i)
-        {
-            _pending[pindex]->_y[i]       = fr->_y[i];
-            _pending[pindex]->_dy[i]      = fr->_dy[i];
-            _pending[pindex]->_present[i] = fr->_present[i];
-        }
-        _pending[pindex]->_index = fr->_index;
-    }
-    processPendingFrames();
-}
-
-
-void
-AnalysisData::Impl::processPendingFrames()
-{
-    while (_pending[_pstart]->_index != -1)
-    {
-        AnalysisDataFrame *fr = _pending[_pstart];
-
-        _data.storeNextFrame(fr->_x, fr->_dx, fr->_y, fr->_dy, fr->_present);
-        fr->_index = -1;
-        incrementPStart();
-    }
-}
-
-
-void
-AnalysisData::Impl::incrementPStart()
-{
-    size_t val = _pstart;
-
-    ++val;
-    if (val >= _pending.size())
-    {
-        val -= _pending.size();
-    }
-    _pstart = val;
 }
 
 
@@ -156,14 +75,14 @@ AnalysisData::Impl::incrementPStart()
  */
 
 AnalysisData::AnalysisData()
-    : _impl(new Impl(this))
+    : impl_(new Impl)
 {
 }
 
 
 AnalysisData::~AnalysisData()
 {
-    delete _impl;
+    delete impl_;
 }
 
 
@@ -171,19 +90,24 @@ void
 AnalysisData::setColumns(int ncol, bool multipoint)
 {
     GMX_RELEASE_ASSERT(ncol > 0, "Number of columns must be positive");
-    GMX_RELEASE_ASSERT(_impl->_handles.empty(),
+    GMX_RELEASE_ASSERT(impl_->handles_.empty(),
                        "Cannot change data dimensionality after creating handles");
     setColumnCount(ncol);
     setMultipoint(multipoint);
+    impl_->storage_.setMultipoint(multipoint);
 }
 
 
 AnalysisDataHandle *
-AnalysisData::startData(AnalysisDataParallelOptions opt)
+AnalysisData::startData(const AnalysisDataParallelOptions &opt)
 {
-    if (_impl->_handles.empty())
+    GMX_RELEASE_ASSERT(impl_->handles_.size() < static_cast<unsigned>(opt.parallelizationFactor()),
+                       "Too many calls to startData() compared to provided options");
+    if (impl_->handles_.empty())
     {
-        startDataStore();
+        notifyDataStart();
+        impl_->storage_.setParallelOptions(opt);
+        impl_->storage_.startDataStorage(this);
     }
     else if (isMultipoint())
     {
@@ -191,17 +115,7 @@ AnalysisData::startData(AnalysisDataParallelOptions opt)
     }
 
     std::auto_ptr<AnalysisDataHandle> handle(new AnalysisDataHandle(this));
-    _impl->_handles.push_back(handle.get());
-
-    size_t oldSize = _impl->_pending.size();
-    _impl->_pending.resize(2 * _impl->_handles.size() - 1);
-    Impl::FrameList::iterator i;
-    for (i = _impl->_pending.begin() + oldSize; i != _impl->_pending.end(); ++i)
-    {
-        *i = new AnalysisDataFrame(columnCount());
-        (*i)->_index = -1;
-    }
-
+    impl_->handles_.push_back(handle.get());
     return handle.release();
 }
 
@@ -211,35 +125,40 @@ AnalysisData::finishData(AnalysisDataHandle *handle)
 {
     Impl::HandleList::iterator i;
 
-    i = std::find(_impl->_handles.begin(), _impl->_handles.end(), handle);
-    GMX_RELEASE_ASSERT(i != _impl->_handles.end(),
+    i = std::find(impl_->handles_.begin(), impl_->handles_.end(), handle);
+    GMX_RELEASE_ASSERT(i != impl_->handles_.end(),
                        "finishData() called for an unknown handle");
 
-    _impl->_handles.erase(i);
+    impl_->handles_.erase(i);
     delete handle;
 
-    if (_impl->_handles.empty())
+    if (impl_->handles_.empty())
     {
         notifyDataFinish();
     }
 }
 
 
-/********************************************************************
- * AnalysisDataHandle::Impl
- */
+AnalysisDataFrameRef
+AnalysisData::tryGetDataFrameInternal(int index) const
+{
+    return impl_->storage_.tryGetDataFrame(index);
+}
 
-AnalysisDataHandle::Impl::Impl(AnalysisData *data)
-    : _data(*data)
+
+bool
+AnalysisData::requestStorageInternal(int nframes)
 {
-    if (!_data.isMultipoint())
-    {
-        _frame.reset(new AnalysisDataFrame(_data.columnCount()));
-    }
+    return impl_->storage_.requestStorage(nframes);
 }
 
 
-AnalysisDataHandle::Impl::~Impl()
+/********************************************************************
+ * AnalysisDataHandle::Impl
+ */
+
+AnalysisDataHandle::Impl::Impl(AnalysisData *data)
+    : data_(*data), currentFrame_(NULL)
 {
 }
 
@@ -249,107 +168,67 @@ AnalysisDataHandle::Impl::~Impl()
  */
 
 AnalysisDataHandle::AnalysisDataHandle(AnalysisData *data)
-    : _impl(new Impl(data))
+    : impl_(new Impl(data))
 {
 }
 
 
 AnalysisDataHandle::~AnalysisDataHandle()
 {
-    delete _impl;
+    delete impl_;
 }
 
 
 void
 AnalysisDataHandle::startFrame(int index, real x, real dx)
 {
-    if (_impl->_data.isMultipoint())
-    {
-        _impl->_data.notifyFrameStart(AnalysisDataFrameHeader(index, x, dx));
-    }
-    else
-    {
-        _impl->_frame->_index = index;
-        _impl->_frame->_x  = x;
-        _impl->_frame->_dx = dx;
-        for (int i = 0; i < _impl->_data.columnCount(); ++i)
-        {
-            _impl->_frame->_y[i]  = 0.0;
-            _impl->_frame->_dy[i] = 0.0;
-            _impl->_frame->_present[i] = false;
-        }
-    }
-}
-
-
-void
-AnalysisDataHandle::addPoint(int col, real y, real dy, bool present)
-{
-    if (_impl->_data.isMultipoint())
-    {
-        _impl->_data.notifyPointsAdd(col, 1, &y, &dy, &present);
-    }
-    else
-    {
-        GMX_ASSERT(!_impl->_frame->_present[col],
-                   "Data for a column set multiple times");
-        _impl->_frame->_y[col] = y;
-        _impl->_frame->_dy[col] = dy;
-        _impl->_frame->_present[col] = present;
-    }
+    GMX_RELEASE_ASSERT(impl_->currentFrame_ == NULL,
+                       "startFrame() called twice without calling finishFrame()");
+    impl_->currentFrame_ =
+        &impl_->data_.impl_->storage_.startFrame(index, x, dx);
 }
 
 
 void
-AnalysisDataHandle::addPoints(int firstcol, int n,
-                              const real *y, const real *dy,
-                              const bool *present)
+AnalysisDataHandle::setPoint(int col, real y, real dy, bool present)
 {
-    if (_impl->_data.isMultipoint())
-    {
-        _impl->_data.notifyPointsAdd(firstcol, n, y, dy, present);
-    }
-    else
-    {
-        for (int i = 0; i < n; ++i)
-        {
-            addPoint(firstcol + i, y[i], dy ? dy[i] : 0.0,
-                     present ? present[i] : true);
-        }
-    }
+    GMX_RELEASE_ASSERT(impl_->currentFrame_ != NULL,
+                       "setPoint() called without calling startFrame()");
+    impl_->currentFrame_->setValue(col, y, dy, present);
 }
 
 
 void
-AnalysisDataHandle::finishFrame()
+AnalysisDataHandle::setPoints(int firstcol, int n, const real *y)
 {
-    if (_impl->_data.isMultipoint())
-    {
-        _impl->_data.notifyFrameFinish();
-    }
-    else
+    GMX_RELEASE_ASSERT(impl_->currentFrame_ != NULL,
+                       "setPoints() called without calling startFrame()");
+    for (int i = 0; i < n; ++i)
     {
-        _impl->_data._impl->addPendingFrame(_impl->_frame.get());
+        impl_->currentFrame_->setValue(firstcol + i, y[i]);
     }
 }
 
 
 void
-AnalysisDataHandle::addFrame(int index, real x, const real *y, const real *dy,
-                             const bool *present)
+AnalysisDataHandle::finishPointSet()
 {
-    addFrame(index, x, 0.0, y, dy, present);
+    GMX_RELEASE_ASSERT(impl_->data_.isMultipoint(),
+                       "finishPointSet() called for non-multipoint data");
+    GMX_RELEASE_ASSERT(impl_->currentFrame_ != NULL,
+                       "finishPointSet() called without calling startFrame()");
+    impl_->currentFrame_->finishPointSet();
 }
 
 
 void
-AnalysisDataHandle::addFrame(int index, real x, real dx,
-                             const real *y, const real *dy,
-                             const bool *present)
+AnalysisDataHandle::finishFrame()
 {
-    startFrame(index, x, dx);
-    addPoints(0, _impl->_data.columnCount(), y, dy, present);
-    finishFrame();
+    GMX_RELEASE_ASSERT(impl_->currentFrame_ != NULL,
+                       "finishFrame() called without calling startFrame()");
+    int index = impl_->currentFrame_->frameIndex();
+    impl_->currentFrame_ = NULL;
+    impl_->data_.impl_->storage_.finishFrame(index);
 }
 
 
@@ -357,7 +236,7 @@ void
 AnalysisDataHandle::finishData()
 {
     // Calls delete this
-    _impl->_data.finishData(this);
+    impl_->data_.finishData(this);
 }
 
 } // namespace gmx
index 61f81c7eef85dbd7cd26d574f29456546abc01a1..8522616c3d256096bdaa537b4aae21c5c88e307a 100644 (file)
@@ -45,16 +45,12 @@ namespace gmx
 {
 
 class AnalysisDataHandle;
-
-//! Placeholder type for parallelization options.
-typedef void *AnalysisDataParallelOptions;
+class AnalysisDataParallelOptions;
 
 /*! \brief
  * Parallelizable data container for raw data.
  *
- * This is the only data object (in addition to the tightly coupled
- * \c AnalysisDataHandle object) that needs explicit parallelization.
- *
+ * \if internal
  * Special note for MPI implementation: assuming that the initialization of
  * data objects is identical in all processes, associating the data objects
  * in different MPI processes should be possible without changes in the
@@ -62,11 +58,12 @@ typedef void *AnalysisDataParallelOptions;
  * Alternative, more robust implementation could get a unique ID as parameter
  * to the constructor or a separate function, but would require all tools to
  * provide it.
+ * \endif
  *
  * \inpublicapi
  * \ingroup module_analysisdata
  */
-class AnalysisData : public AbstractAnalysisDataStored
+class AnalysisData : public AbstractAnalysisData
 {
     public:
         //! Creates an empty analysis data object.
@@ -87,7 +84,7 @@ class AnalysisData : public AbstractAnalysisDataStored
          *     used.
          * \returns The created handle.
          */
-        AnalysisDataHandle *startData(AnalysisDataParallelOptions opt);
+        AnalysisDataHandle *startData(const AnalysisDataParallelOptions &opt);
         /*! \brief
          * Destroy a handle after all data has been added.
          *
@@ -98,11 +95,13 @@ class AnalysisData : public AbstractAnalysisDataStored
         void finishData(AnalysisDataHandle *handle);
 
     private:
+        virtual AnalysisDataFrameRef tryGetDataFrameInternal(int index) const;
+        virtual bool requestStorageInternal(int nframes);
+
         class Impl;
 
-        Impl                *_impl;
+        Impl                *impl_;
 
-        friend class Impl;
         friend class AnalysisDataHandle;
 
         // Copy and assign disallowed by base class.
@@ -130,21 +129,14 @@ class AnalysisDataHandle
 
         //! Start data for a new frame.
         void startFrame(int index, real x, real dx = 0.0);
-        //! Add a data point for in single column for the current frame.
-        void addPoint(int col, real y, real dy = 0.0, bool present = true);
-        //! Add multiple data points in neighboring columns for the current frame.
-        void addPoints(int firstcol, int n,
-                       const real *y, const real *dy = 0,
-                       const bool *present = 0);
+        //! Set a value for a single column for the current frame.
+        void setPoint(int col, real y, real dy = 0.0, bool present = true);
+        //! Set values for consecutive columns for the current frame.
+        void setPoints(int firstcol, int n, const real *y);
+        //! Finish data for the current point set.
+        void finishPointSet();
         //! Finish data for the current frame.
         void finishFrame();
-        //! Convenience function for adding a complete frame.
-        void addFrame(int index, real x, const real *y, const real *dy = 0,
-                      const bool *present = 0);
-        //! Convenience function for adding a complete frame.
-        void addFrame(int index, real x, real dx,
-                      const real *y, const real *dy = 0,
-                      const bool *present = 0);
         //! Calls AnalysisData::finishData() for this handle.
         void finishData();
 
@@ -161,7 +153,7 @@ class AnalysisDataHandle
 
         class Impl;
 
-        Impl                *_impl;
+        Impl                   *impl_;
 
         friend class AnalysisData;
 
diff --git a/src/gromacs/analysisdata/datastorage-impl.h b/src/gromacs/analysisdata/datastorage-impl.h
new file mode 100644 (file)
index 0000000..4f12c98
--- /dev/null
@@ -0,0 +1,224 @@
+/*
+ *
+ *                This source code is part of
+ *
+ *                 G   R   O   M   A   C   S
+ *
+ *          GROningen MAchine for Chemical Simulations
+ *
+ * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2009, The GROMACS development team,
+ * check out http://www.gromacs.org for more information.
+
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * If you want to redistribute modifications, please consider that
+ * scientific software is very special. Version control is crucial -
+ * bugs must be traceable. We will be happy to consider code for
+ * inclusion in the official distribution, but derived work must not
+ * be called official GROMACS. Details are found in the README & COPYING
+ * files - if they are missing, get the official version at www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the papers on the package - you can find them in the top README file.
+ *
+ * For more info, check our website at http://www.gromacs.org
+ */
+/*! \internal \file
+ * \brief
+ * Declares internal implementation classes for gmx::AnalysisDataStorage.
+ *
+ * \author Teemu Murtola <teemu.murtola@cbr.su.se>
+ * \ingroup module_analysisdata
+ */
+#ifndef GMX_ANALYSISDATA_DATASTORAGE_IMPL_H
+#define GMX_ANALYSISDATA_DATASTORAGE_IMPL_H
+
+#include <limits>
+#include <vector>
+
+#include "datastorage.h"
+
+namespace gmx
+{
+
+class AbstractAnalysisData;
+class AnalysisDataPointSetRef;
+class AnalysisDataStorageFrame;
+
+/*! \internal \brief
+ * Private implementation class for AnalysisDataStorage.
+ *
+ * \ingroup module_analysisdata
+ */
+class AnalysisDataStorage::Impl
+{
+    public:
+        /*! \brief
+         * Stored information about a single stored frame.
+         */
+        struct StoredFrame
+        {
+            enum Status
+            {
+                eMissing,  //!< Frame has not yet been started.
+                eStarted,  //!< startFrame() has been called.
+                eFinished, //!< finishFrame() has been called.
+                eNotified  //!< Appropriate notifications have been sent.
+            };
+
+            StoredFrame() : frame(NULL), status(eMissing) {}
+            explicit StoredFrame(AnalysisDataStorageFrame *frame)
+                : frame(frame), status(eMissing)
+            {
+            }
+
+            bool isStarted() const { return status >= eStarted; }
+            bool isFinished() const { return status >= eFinished; }
+            bool isNotified() const { return status >= eNotified; }
+            bool isAvailable() const { return status >= eFinished; }
+
+            /*! \brief
+             * Actual frame data.
+             *
+             * Always allocated.  Memory is managed by the parent Impl object
+             * to make the StoredFrame type STL-container-friendly.
+             */
+            AnalysisDataStorageFrame *frame;
+            //! In what state the frame currently is.
+            Status                    status;
+        };
+
+        //! Shorthand for a list of data frames that are currently stored.
+        typedef std::vector<StoredFrame> FrameList;
+
+        Impl();
+        ~Impl();
+
+        //! Returns the number of columns in the attached data.
+        int columnCount() const;
+        //! Returns whether the storage is set to use multipoint data.
+        bool isMultipoint() const;
+        /*! \brief
+         * Whether storage of all frames has been requested.
+         *
+         * Storage of all frames also works as expected if \a storageLimit_ is
+         * used in comparisons directly, but this method should be used to
+         * check how to manage \a frames_.
+         */
+        bool storeAll() const
+        {
+            return storageLimit_ == std::numeric_limits<int>::max();
+        }
+        //! Returns the index of the oldest frame that may be currently stored.
+        int firstStoredIndex() const;
+        /*! \brief
+         * Computes index into \a frames_ for accessing frame \p index.
+         *
+         * \param[in]  index  Zero-based frame index.
+         * \retval  -1 if \p index is not available in \a frames_.
+         */
+        int computeStorageLocation(int index) const;
+
+        /*! \brief
+         * Computes an index into \a frames_ that is one past the last frame
+         * stored.
+         */
+        size_t endStorageLocation() const;
+
+        /*! \brief
+         * Extends \a frames_ to a new size.
+         */
+        void extendBuffer(AnalysisDataStorage *storage, size_t newSize);
+        /*! \brief
+         * Remove oldest frame from the storage to make space for a new one.
+         *
+         * Increments \a firstFrameLocation_ and reinitializes the frame that
+         * was made unavailable by this operation.
+         *
+         * \see frames_
+         */
+        void rotateBuffer();
+
+        /*! \brief
+         * Calls notification method in \a data_.
+         */
+        void notifyPointSet(const AnalysisDataPointSetRef &points);
+        /*! \brief
+         * Calls notification methods for new frames.
+         *
+         * \param[in] firstLocation  First frame to consider.
+         *
+         * Notifies \a data_ of new frames (from \p firstLocation and after
+         * that) if all previous frames have already been notified.
+         * Also rotates the \a frames_ buffer as necessary.
+         */
+        void notifyNextFrames(size_t firstLocation);
+
+        //! Data object to use for notification calls.
+        AbstractAnalysisData   *data_;
+        /*! \brief
+         * Whether the storage has been set to allow multipoint.
+         *
+         * Should be possible to remove once full support for multipoint data
+         * has been implemented;  isMultipoint() can simply return
+         * \c data_->isMultipoint() in that case.
+         */
+        bool                    bMultipoint_;
+        /*! \brief
+         * Number of past frames that need to be stored.
+         *
+         * Always non-negative.  If storage of all frames has been requested,
+         * this is set to a large number.
+         */
+        int                     storageLimit_;
+        /*! \brief
+         * Number of future frames that may need to be started.
+         *
+         * Should always be at least one.
+         *
+         * \see AnalysisDataStorage::startFrame()
+         */
+        int                     pendingLimit_;
+        /*! \brief
+         * Data frames that are currently stored.
+         *
+         * If storage of all frames has been requested, this is simply a vector
+         * of frames up to the latest frame that has been started.
+         * In this case, \a firstFrameLocation_ is always zero.
+         *
+         * If storage of all frames is not requested, this is a ring buffer of
+         * frames of size \c n=storageLimit_+pendingLimit_+1.  If a frame with
+         * index \c index is currently stored, its location is
+         * \c index%frames_.size().
+         * When at most \a storageLimit_ first frames have been finished,
+         * this contains storage for the first \c n-1 frames.
+         * When more than \a storageLimit_ first frames have been finished,
+         * the oldest stored frame is stored in the location
+         * \a firstFrameLocation_, and \a storageLimit_ frames starting from
+         * this location are the last finished frames.  \a pendingLimit_ frames
+         * follow, and some of these may be in progress or finished.
+         * There is always one unused frame in the buffer, which is initialized
+         * such that when \a firstFrameLocation_ is incremented, it becomes
+         * valid.  This makes it easier to rotate the buffer in concurrent
+         * access scenarions (which are not yet otherwise implemented).
+         */
+        FrameList               frames_;
+        //! Location of oldest frame in \a frames_.
+        size_t                  firstFrameLocation_;
+        /*! \brief
+         * Index of next frame that will be added to \a frames_.
+         *
+         * If all frames are not stored, this will be the index of the unused
+         * frame (see \a frames_).
+         */
+        int                     nextIndex_;
+};
+
+} // namespace gmx
+
+#endif
diff --git a/src/gromacs/analysisdata/datastorage.cpp b/src/gromacs/analysisdata/datastorage.cpp
new file mode 100644 (file)
index 0000000..93e716f
--- /dev/null
@@ -0,0 +1,473 @@
+/*
+ *
+ *                This source code is part of
+ *
+ *                 G   R   O   M   A   C   S
+ *
+ *          GROningen MAchine for Chemical Simulations
+ *
+ * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2009, The GROMACS development team,
+ * check out http://www.gromacs.org for more information.
+
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * If you want to redistribute modifications, please consider that
+ * scientific software is very special. Version control is crucial -
+ * bugs must be traceable. We will be happy to consider code for
+ * inclusion in the official distribution, but derived work must not
+ * be called official GROMACS. Details are found in the README & COPYING
+ * files - if they are missing, get the official version at www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the papers on the package - you can find them in the top README file.
+ *
+ * For more info, check our website at http://www.gromacs.org
+ */
+/*! \internal \file
+ * \brief
+ * Implements classes in datastorage.h and paralleloptions.h.
+ *
+ * \author Teemu Murtola <teemu.murtola@cbr.su.se>
+ * \ingroup module_analysisdata
+ */
+#include "datastorage.h"
+
+#include <limits>
+
+#include "smalloc.h"
+
+#include "gromacs/analysisdata/abstractdata.h"
+#include "gromacs/analysisdata/dataframe.h"
+#include "gromacs/analysisdata/paralleloptions.h"
+#include "gromacs/fatalerror/exceptions.h"
+#include "gromacs/fatalerror/gmxassert.h"
+
+#include "datastorage-impl.h"
+
+namespace gmx
+{
+
+/********************************************************************
+ * AnalysisDataParallelOptions
+ */
+
+AnalysisDataParallelOptions::AnalysisDataParallelOptions()
+    : parallelizationFactor_(1)
+{
+}
+
+
+AnalysisDataParallelOptions::AnalysisDataParallelOptions(int parallelizationFactor)
+    : parallelizationFactor_(parallelizationFactor)
+{
+    GMX_RELEASE_ASSERT(parallelizationFactor >= 1,
+                       "Invalid parallelization factor");
+}
+
+
+/********************************************************************
+ * AnalysisDataStorageFrame
+ */
+
+AnalysisDataStorageFrame::AnalysisDataStorageFrame(AnalysisDataStorage *storage,
+                                                   int columnCount, int index)
+    : storage_(storage), header_(index, 0.0, 0.0), columnCount_(columnCount),
+      y_(NULL), dy_(NULL), present_(NULL)
+{
+    snew(y_, columnCount);
+    snew(dy_, columnCount);
+    snew(present_, columnCount);
+}
+
+
+AnalysisDataStorageFrame::~AnalysisDataStorageFrame()
+{
+    sfree(y_);
+    sfree(dy_);
+    sfree(present_);
+}
+
+
+AnalysisDataPointSetRef
+AnalysisDataStorageFrame::currentPoints() const
+{
+    // TODO: Deduce first column and column count for multipoint data.
+    // Requires additional information to be stored.
+    return AnalysisDataPointSetRef(header_, 0, columnCount_, y_, dy_, present_);
+}
+
+
+void
+AnalysisDataStorageFrame::clearValues()
+{
+    for (int i = 0; i < columnCount(); ++i)
+    {
+        y_[i] = 0.0;
+        dy_[i] = 0.0;
+        present_[i] = false;
+    }
+}
+
+
+void
+AnalysisDataStorageFrame::finishPointSet()
+{
+    storage_->impl_->notifyPointSet(currentPoints());
+    clearValues();
+}
+
+
+/********************************************************************
+ * AnalysisDataStorage::Impl
+ */
+
+AnalysisDataStorage::Impl::Impl()
+    : data_(NULL), bMultipoint_(false),
+      storageLimit_(0), pendingLimit_(1), firstFrameLocation_(0), nextIndex_(0)
+{
+}
+
+
+AnalysisDataStorage::Impl::~Impl()
+{
+    FrameList::const_iterator i;
+    for (i = frames_.begin(); i != frames_.end(); ++i)
+    {
+        delete i->frame;
+    }
+}
+
+
+int
+AnalysisDataStorage::Impl::columnCount() const
+{
+    GMX_ASSERT(data_ != NULL, "columnCount() called too early");
+    return data_->columnCount();
+}
+
+
+bool
+AnalysisDataStorage::Impl::isMultipoint() const
+{
+    return bMultipoint_;
+}
+
+
+int
+AnalysisDataStorage::Impl::firstStoredIndex() const
+{
+    return frames_[firstFrameLocation_].frame->frameIndex();
+}
+
+
+int
+AnalysisDataStorage::Impl::computeStorageLocation(int index) const
+{
+    if (index < firstStoredIndex() || index >= nextIndex_)
+    {
+        return -1;
+    }
+    return index % frames_.size();
+}
+
+
+size_t
+AnalysisDataStorage::Impl::endStorageLocation() const
+{
+    if (storeAll())
+    {
+        return frames_.size();
+    }
+    if (frames_[0].frame->frameIndex() == 0 || firstFrameLocation_ == 0)
+    {
+        return frames_.size() - 1;
+    }
+    return firstFrameLocation_ - 1;
+}
+
+
+void
+AnalysisDataStorage::Impl::extendBuffer(AnalysisDataStorage *storage,
+                                        size_t newSize)
+{
+    frames_.reserve(newSize);
+    while (frames_.size() < newSize)
+    {
+        AnalysisDataStorageFrame *frame =
+            new AnalysisDataStorageFrame(storage, columnCount(), nextIndex_);
+        frames_.push_back(StoredFrame(frame));
+        ++nextIndex_;
+    }
+    // The unused frame should not be included in the count.
+    if (!storeAll())
+    {
+        --nextIndex_;
+    }
+}
+
+
+void
+AnalysisDataStorage::Impl::rotateBuffer()
+{
+    GMX_ASSERT(!storeAll(),
+               "No need to rotate internal buffer if everything is stored");
+    size_t prevFirst = firstFrameLocation_;
+    size_t nextFirst = prevFirst + 1;
+    if (nextFirst == frames_.size())
+    {
+        nextFirst = 0;
+    }
+    firstFrameLocation_ = nextFirst;
+    StoredFrame &prevFrame = frames_[prevFirst];
+    prevFrame.status = StoredFrame::eMissing;
+    prevFrame.frame->header_ = AnalysisDataFrameHeader(nextIndex_ + 1, 0.0, 0.0);
+    prevFrame.frame->clearValues();
+    ++nextIndex_;
+}
+
+
+void
+AnalysisDataStorage::Impl::notifyPointSet(const AnalysisDataPointSetRef &points)
+{
+    data_->notifyPointsAdd(points);
+}
+
+
+void
+AnalysisDataStorage::Impl::notifyNextFrames(size_t firstLocation)
+{
+    if (firstLocation != firstFrameLocation_)
+    {
+        // firstLocation can only be zero here if !storeAll() because
+        // firstFrameLocation_ is always zero for storeAll()
+        int prevIndex =
+            (firstLocation == 0 ? frames_.size() - 1 : firstLocation - 1);
+        if (!frames_[prevIndex].isNotified())
+        {
+            return;
+        }
+    }
+    size_t i = firstLocation;
+    size_t end = endStorageLocation();
+    while (i != end)
+    {
+        Impl::StoredFrame &storedFrame = frames_[i];
+        if (!storedFrame.isFinished())
+        {
+            break;
+        }
+        if (storedFrame.status == StoredFrame::eFinished)
+        {
+            data_->notifyFrameStart(storedFrame.frame->header());
+            data_->notifyPointsAdd(storedFrame.frame->currentPoints());
+            data_->notifyFrameFinish(storedFrame.frame->header());
+            storedFrame.status = StoredFrame::eNotified;
+            if (storedFrame.frame->frameIndex() >= storageLimit_)
+            {
+                rotateBuffer();
+            }
+        }
+        ++i;
+        if (!storeAll() && i >= frames_.size())
+        {
+            i = 0;
+        }
+    }
+}
+
+
+/********************************************************************
+ * AnalysisDataStorage
+ */
+
+AnalysisDataStorage::AnalysisDataStorage()
+    : impl_(new Impl())
+{
+}
+
+
+AnalysisDataStorage::~AnalysisDataStorage()
+{
+    delete impl_;
+}
+
+
+void
+AnalysisDataStorage::setMultipoint(bool bMultipoint)
+{
+    if (bMultipoint && impl_->storageLimit_ > 0)
+    {
+        GMX_THROW(APIError("Storage of multipoint data not supported"));
+    }
+    impl_->bMultipoint_ = bMultipoint;
+}
+
+
+void
+AnalysisDataStorage::setParallelOptions(const AnalysisDataParallelOptions &opt)
+{
+    impl_->pendingLimit_ = 2 * opt.parallelizationFactor() - 1;
+}
+
+
+AnalysisDataFrameRef
+AnalysisDataStorage::tryGetDataFrame(int index) const
+{
+    if (impl_->isMultipoint())
+    {
+        return AnalysisDataFrameRef();
+    }
+    int storageIndex = impl_->computeStorageLocation(index);
+    if (storageIndex == -1)
+    {
+        return AnalysisDataFrameRef();
+    }
+    const Impl::StoredFrame &storedFrame = impl_->frames_[storageIndex];
+    if (!storedFrame.isAvailable())
+    {
+        return AnalysisDataFrameRef();
+    }
+    const AnalysisDataStorageFrame *frame = storedFrame.frame;
+    return AnalysisDataFrameRef(frame->header(), frame->columnCount(),
+                                frame->y_, frame->dy_, frame->present_);
+}
+
+
+bool
+AnalysisDataStorage::requestStorage(int nframes)
+{
+    if (impl_->isMultipoint())
+    {
+        return false;
+    }
+
+    // Handle the case when everything needs to be stored.
+    if (nframes == -1)
+    {
+        impl_->storageLimit_ = std::numeric_limits<int>::max();
+        return true;
+    }
+    // Check whether an earlier call has requested more storage.
+    if (nframes < impl_->storageLimit_)
+    {
+        return true;
+    }
+    impl_->storageLimit_ = nframes;
+    return true;
+}
+
+
+void
+AnalysisDataStorage::startDataStorage(AbstractAnalysisData *data)
+{
+    // Data needs to be set before calling extendBuffer()
+    impl_->data_ = data;
+    setMultipoint(data->isMultipoint());
+    if (!impl_->storeAll())
+    {
+        impl_->extendBuffer(this, impl_->storageLimit_ + impl_->pendingLimit_ + 1);
+    }
+}
+
+
+AnalysisDataStorageFrame &
+AnalysisDataStorage::startFrame(const AnalysisDataFrameHeader &header)
+{
+    GMX_ASSERT(header.isValid(), "Invalid header");
+    Impl::StoredFrame *storedFrame;
+    if (impl_->storeAll())
+    {
+        size_t size = header.index() + 1;
+        if (impl_->frames_.size() < size)
+        {
+            impl_->extendBuffer(this, size);
+        }
+        storedFrame = &impl_->frames_[header.index()];
+    }
+    else
+    {
+        int storageIndex = impl_->computeStorageLocation(header.index());
+        if (storageIndex == -1)
+        {
+            GMX_THROW(APIError("Out of bounds frame index"));
+        }
+        storedFrame = &impl_->frames_[storageIndex];
+    }
+    GMX_RELEASE_ASSERT(!storedFrame->isStarted(),
+                       "startFrame() called twice for the same frame");
+    GMX_RELEASE_ASSERT(storedFrame->frame->frameIndex() == header.index(),
+                       "Inconsistent internal frame indexing");
+    storedFrame->status = Impl::StoredFrame::eStarted;
+    storedFrame->frame->header_ = header;
+    if (impl_->isMultipoint())
+    {
+        impl_->data_->notifyFrameStart(header);
+    }
+    return *storedFrame->frame;
+}
+
+
+AnalysisDataStorageFrame &
+AnalysisDataStorage::startFrame(int index, real x, real dx)
+{
+    return startFrame(AnalysisDataFrameHeader(index, x, dx));
+}
+
+
+AnalysisDataStorageFrame &
+AnalysisDataStorage::currentFrame(int index)
+{
+    int storageIndex = impl_->computeStorageLocation(index);
+    GMX_RELEASE_ASSERT(storageIndex >= 0, "Out of bounds frame index");
+    Impl::StoredFrame &storedFrame = impl_->frames_[storageIndex];
+    GMX_RELEASE_ASSERT(storedFrame.isStarted(),
+                       "currentFrame() called for frame before startFrame()");
+    GMX_RELEASE_ASSERT(!storedFrame.isFinished(),
+                       "currentFrame() called for frame after finishFrame()");
+    GMX_RELEASE_ASSERT(storedFrame.frame->frameIndex() == index,
+                       "Inconsistent internal frame indexing");
+    return *storedFrame.frame;
+}
+
+
+void
+AnalysisDataStorage::finishFrame(int index)
+{
+    int storageIndex = impl_->computeStorageLocation(index);
+    GMX_RELEASE_ASSERT(storageIndex >= 0, "Out of bounds frame index");
+    Impl::StoredFrame &storedFrame = impl_->frames_[storageIndex];
+    GMX_RELEASE_ASSERT(storedFrame.isStarted(),
+                       "finishFrame() called for frame before startFrame()");
+    GMX_RELEASE_ASSERT(!storedFrame.isFinished(),
+                       "finishFrame() called twice for the same frame");
+    GMX_RELEASE_ASSERT(storedFrame.frame->frameIndex() == index,
+                       "Inconsistent internal frame indexing");
+    storedFrame.status = Impl::StoredFrame::eFinished;
+    if (impl_->isMultipoint())
+    {
+        // TODO: Check that the last point set has been finished
+        impl_->data_->notifyFrameFinish(storedFrame.frame->header());
+        if (storedFrame.frame->frameIndex() >= impl_->storageLimit_)
+        {
+            impl_->rotateBuffer();
+        }
+    }
+    else
+    {
+        impl_->notifyNextFrames(storageIndex);
+    }
+}
+
+
+void
+AnalysisDataStorage::finishFrame(const AnalysisDataStorageFrame &frame)
+{
+    finishFrame(frame.frameIndex());
+}
+
+} // namespace gmx
diff --git a/src/gromacs/analysisdata/datastorage.h b/src/gromacs/analysisdata/datastorage.h
new file mode 100644 (file)
index 0000000..fce3e3e
--- /dev/null
@@ -0,0 +1,393 @@
+/*
+ *
+ *                This source code is part of
+ *
+ *                 G   R   O   M   A   C   S
+ *
+ *          GROningen MAchine for Chemical Simulations
+ *
+ * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2009, The GROMACS development team,
+ * check out http://www.gromacs.org for more information.
+
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * If you want to redistribute modifications, please consider that
+ * scientific software is very special. Version control is crucial -
+ * bugs must be traceable. We will be happy to consider code for
+ * inclusion in the official distribution, but derived work must not
+ * be called official GROMACS. Details are found in the README & COPYING
+ * files - if they are missing, get the official version at www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the papers on the package - you can find them in the top README file.
+ *
+ * For more info, check our website at http://www.gromacs.org
+ */
+/*! \libinternal \file
+ * \brief
+ * Declares gmx::AnalysisDataStorage.
+ *
+ * \author Teemu Murtola <teemu.murtola@cbr.su.se>
+ * \inlibraryapi
+ * \ingroup module_analysisdata
+ */
+#ifndef GMX_ANALYSISDATA_DATASTORAGE_H
+#define GMX_ANALYSISDATA_DATASTORAGE_H
+
+#include "../legacyheaders/types/simple.h"
+
+#include "../fatalerror/gmxassert.h"
+
+#include "dataframe.h"
+
+namespace gmx
+{
+
+class AbstractAnalysisData;
+class AnalysisDataFrameHeader;
+class AnalysisDataFrameRef;
+class AnalysisDataParallelOptions;
+
+class AnalysisDataStorage;
+
+/*! \libinternal \brief
+ * Stores a single data frame for AnalysisDataStorage.
+ *
+ * It also provides access to the frame outside AnalysisDataStorage.
+ *
+ * It is implemented such that the frame header is always valid, i.e.,
+ * header().isValid() returns always true.
+ *
+ * \inlibraryapi
+ * \ingroup module_analysisdata
+ */
+class AnalysisDataStorageFrame
+{
+    public:
+        //! Returns header for the frame.
+        const AnalysisDataFrameHeader &header() const { return header_; }
+        //! Returns zero-based index of the frame.
+        int frameIndex() const { return header().index(); }
+        //! Returns x coordinate for the frame.
+        real x() const { return header().x(); }
+        //! Returns error in x coordinate for the frame if applicable.
+        real dx() const { return header().dx(); }
+        //! Returns number of columns for the frame.
+        int columnCount() const { return columnCount_; }
+
+        /*! \brief
+         * Returns point set reference to currently set values.
+         *
+         * Does not throw.
+         */
+        AnalysisDataPointSetRef currentPoints() const;
+        /*! \brief
+         * Sets value for a column.
+         *
+         * \param[in] column  Zero-based column index.
+         * \param[in] value   Value to set for the column.
+         * \param[in] error   Error estimate to set for the column.
+         * \param[in] present Present flag to set for the column.
+         *
+         * If called multiple times for a column (within one point set for
+         * multipoint data), old values are overwritten.
+         *
+         * Does not throw.
+         */
+        void setValue(int column, real value, real error = 0.0,
+                      bool present = true)
+        {
+            GMX_ASSERT(column >= 0 && column < columnCount(),
+                       "Invalid column index");
+            y_[column] = value;
+            dy_[column] = error;
+            present_[column] = present;
+        }
+        /*! \brief
+         * Access value for a column.
+         *
+         * \param[in] column  Zero-based column index.
+         *
+         * Should only be called after the column value has been set using
+         * setValue(); assigning a value to \c value(i) does not mark the
+         * column as set.
+         *
+         * Does not throw.
+         */
+        real &value(int column)
+        {
+            GMX_ASSERT(column >= 0 && column < columnCount(),
+                       "Invalid column index");
+            return y_[column];
+        }
+        /*! \brief
+         * Access value for a column.
+         *
+         * \param[in] column  Zero-based column index.
+         *
+         * Should only be called after the column value has been set using
+         * setValue().
+         *
+         * Does not throw.
+         */
+        real value(int column) const
+        {
+            GMX_ASSERT(column >= 0 && column < columnCount(),
+                       "Invalid column index");
+            return y_[column];
+        }
+        /*! \brief
+         * Mark point set as finished for multipoint data.
+         *
+         * Must be called after each point set for multipoint data, including
+         * the last (i.e., no values must be set between the last call to this
+         * method and AnalysisDataStorage::finishFrame()).
+         * Must not be called for non-multipoint data.
+         *
+         * After this method has been called, all values appear as not set.
+         *
+         * Calls AbstractAnalysisData::notifyPointsAdd(), and throws any
+         * exception this method throws.
+         */
+        void finishPointSet();
+
+    private:
+        /*! \brief
+         * Create a new storage frame.
+         *
+         * \param     storage      Storage object this frame belongs to.
+         * \param[in] columnCount  Number of columns for the frame.
+         * \param[in] index        Zero-based index for the frame.
+         */
+        AnalysisDataStorageFrame(AnalysisDataStorage *storage, int columnCount,
+                                 int index);
+        ~AnalysisDataStorageFrame();
+
+        //! Clear all column values from the frame.
+        void clearValues();
+
+        //! Storage object that contains this frame.
+        AnalysisDataStorage    *storage_;
+        //! Header for the frame.
+        AnalysisDataFrameHeader header_;
+        //! Number of columns in the frame.
+        int                     columnCount_;
+        //! Array of column values for the frame.
+        real                   *y_;
+        //! Array of column error values for the frame.
+        real                   *dy_;
+        //! Array of flags that tell whether a value is present.
+        bool                   *present_;
+
+        /*! \brief
+         * Needed for full write access to the data and for access to
+         * constructor/destructor.
+         */
+        friend class AnalysisDataStorage;
+
+        // Disallow copy and assign.
+        AnalysisDataStorageFrame(const AnalysisDataStorageFrame &);
+        void operator =(const AnalysisDataStorageFrame &);
+};
+
+/*! \libinternal \brief
+ * Helper class that implements storage of data.
+ *
+ * This class implements a standard way of storing data to avoid implementing
+ * storage in each class derived from AbstractAnalysisData separately.
+ * To use this class in a class derived from AbstractAnalysisData, a member
+ * variable of this type should be declared and the data storage methods
+ * forwarded to tryGetDataFrame() and requestStorage() in that object.
+ * Storage properties should be set up, and then startDataStorage() called
+ * after calling AbstractAnalysisData::notifyDataStart().
+ * New frames can then be added using startFrame(), currentFrame() and
+ * finishFrame() methods.  These methods (and
+ * AnalysisDataStorageFrame::finishPointSet()) take the responsibility of
+ * calling AbstractAnalysisData::notifyFrameStart(),
+ * AbstractAnalysisData::notifyPointsAdd() and
+ * AbstractAnalysisData::notifyFrameFinish() appropriately.
+ *
+ * \todo
+ * Full support for multipoint data.
+ * Currently, multipoint data is only supported in serial pass-through mode
+ * without any storage.
+ *
+ * \todo
+ * Proper multi-threaded implementation.
+ *
+ * \inlibraryapi
+ * \ingroup module_analysisdata
+ */
+class AnalysisDataStorage
+{
+    public:
+        //! Constructs a storage object.
+        AnalysisDataStorage();
+        ~AnalysisDataStorage();
+
+        /*! \brief
+         * Sets whether the data will be multipoint.
+         *
+         * \exception APIError if storage has been requested.
+         *
+         * It is not mandatory to call this method (the multipoint property is
+         * automatically detected in startDataStorage()), but currently calling
+         * it will produce better diagnostic messages.
+         * When full support for multipoint data has been implemented, this
+         * method can be removed.
+         */
+        void setMultipoint(bool bMultipoint);
+        /*! \brief
+         * Set parallelization options for the storage.
+         *
+         * \param[in] opt  Parallization options to use.
+         *
+         * If this method is not called, the storage is set up for serial
+         * storage only.
+         *
+         * Does not throw.
+         */
+        void setParallelOptions(const AnalysisDataParallelOptions &opt);
+
+        /*! \brief
+         * Implements access to data frames.
+         *
+         * This method is designed such that calls to
+         * AbstractAnalysisData::tryGetDataFrameInternal() can be directly
+         * forwarded to this method.  See that method for more documentation.
+         *
+         * A valid reference for a frame will be returned after finishFrame()
+         * has been called for that frame.
+         *
+         * \see AbstractAnalysisData::tryGetDataFrameInternal()
+         */
+        AnalysisDataFrameRef tryGetDataFrame(int index) const;
+        /*! \brief
+         * Implements storage requests.
+         *
+         * This method is designed such that calls to
+         * AbstractAnalysisData::requestStorageInternal() can be directly
+         * forwarded to this method.  See that method for more documentation.
+         *
+         * Currently, multipoint data cannot be stored, but all other storage
+         * request will be honored.
+         *
+         * \see AbstractAnalysisData::requestStorageInternal()
+         */
+        bool requestStorage(int nframes);
+
+        /*! \brief
+         * Start storing data.
+         *
+         * \param  data  AbstractAnalysisData object containing this storage.
+         * \exception std::bad_alloc if storage allocation fails.
+         * \exception APIError if storage has been requested and \p data is
+         *      multipoint.
+         *
+         * Lifetime of \p data must exceed the lifetime of the storage object
+         * (typically, the storage object will be a member in \p data).
+         * The storage object will take responsibility of calling
+         * AbstractAnalysisData::notifyFrameStart(),
+         * AbstractAnalysisData::notifyPointsAdd() and
+         * AbstractAnalysisData::notifyFrameFinish() for \p data appropriately.
+         *
+         * AbstractAnalysisData::notifyDataStart() must have been called for
+         * \p data, because that may trigger storage requests from attached
+         * modules.
+         */
+        void startDataStorage(AbstractAnalysisData *data);
+        /*! \brief
+         * Starts storing a new frame.
+         *
+         * \param[in] header  Header for the new frame.
+         * \retval  Frame object corresponding to the started frame.
+         * \exception std::bad_alloc if storage reallocation fails
+         *      (only possible if storage of all frames has been requested).
+         * \exception APIError if frame is too far in the future.
+         *
+         * The returned object will be valid until the corresponding
+         * finishFrame() call.
+         *
+         * Must be called exactly once for each frame index.
+         *
+         * Currently, the implementation only works if the new frame is not too
+         * far in the future:
+         * If \c i is the index of the last frame such that all frames from
+         * 0, ..., \c i have been finished, then \p header().index() should be
+         * at most \c 2*parallelizationFactor-1 larger than \c i, where
+         * parallelizationFactor is the parallelization factor passed to
+         * setParallelOptions().
+         * Throws APIError if this constraint is violated.
+         *
+         * Calls AbstractAnalysisData::notifyDataStarted() in certain cases,
+         * and throws any exceptions this method throws.
+         */
+        AnalysisDataStorageFrame &startFrame(const AnalysisDataFrameHeader &header);
+        /*! \brief
+         * Convenience method to start storing a new frame.
+         *
+         * Identical to \c startFrame(AnalysisDataFrameHeader(index, x, dx));
+         */
+        AnalysisDataStorageFrame &startFrame(int index, real x, real dx);
+        /*! \brief
+         * Obtain a frame object for an in-progress frame.
+         *
+         * \param[in] index  Frame index.
+         * \retval  Frame object corresponding to \p index.
+         *
+         * startFrame() should have been called for the frame with index
+         * \p index, and finishFrame() should not yet have been called.
+         * Returns the same object as returned by the original startFrame()
+         * call for the same index.
+         *
+         * Does not throw.
+         */
+        AnalysisDataStorageFrame &currentFrame(int index);
+        /*! \brief
+         * Finish storing a frame.
+         *
+         * \param[in] index  Frame index.
+         *
+         * Must be called exactly once for each startFrame(), after the
+         * corresponding call.
+         *
+         * Calls notification methods in AbstractAnalysisData, and throws any
+         * exceptions these methods throw.
+         */
+        void finishFrame(int index);
+        /*! \brief
+         * Convenience method for finishing a data frame.
+         *
+         * \param[in] frame  Frame object to finish.
+         *
+         * \p frame should have been previously obtained from startFrame() or
+         * currentFrame().  The \p frame reference is no longer valid after the
+         * call.
+         *
+         * Identical to \c finishframe(frame.frameIndex());
+         */
+        void finishFrame(const AnalysisDataStorageFrame &frame);
+
+    private:
+        class Impl;
+
+        Impl                   *impl_;
+
+        /*! \brief
+         * Needed because the frame object needs to trigger notifications.
+         */
+        friend void AnalysisDataStorageFrame::finishPointSet();
+
+        // Disallow copy and assign.
+        AnalysisDataStorage(const AnalysisDataStorage &);
+        void operator =(const AnalysisDataStorage &);
+};
+
+} // namespace gmx
+
+#endif
index 83d3a464300915ea044d3d04abfb0b24d57d95ae..b1362e87cc91bc1579089db032875b302553f75d 100644 (file)
@@ -42,6 +42,8 @@
 
 #include "histogram.h"
 
+#include "../datastorage.h"
+
 namespace gmx
 {
 
@@ -123,11 +125,15 @@ class BasicHistogramImpl
          * not exist.
          */
         void ensureAveragerExists(AbstractAnalysisData *data);
+        /*! \brief
+         * Initializes data storage frame when a new frame starts.
+         */
+        void initFrame(AnalysisDataStorageFrame *frame);
 
+        //! Storage implementation object.
+        AnalysisDataStorage             storage_;
         //! Settings for the histogram object.
         AnalysisHistogramSettings       settings_;
-        //! Histogram data accumulated for a frame so far.
-        std::vector<real>               hist_;
         //! Averager module, or NULL if not yet allocated.
         BasicAverageHistogramModule    *averager_;
 };
index 09984cda6caaca4b0f51cfe8204bb5db7684d471..cb7febac4b76eaa058424dff7674490a0f7c2aec 100644 (file)
 
 #include <cmath>
 
-#include <algorithm>
 #include <limits>
 #include <memory>
 
 #include "gromacs/basicmath.h"
 #include "gromacs/analysisdata/dataframe.h"
+#include "gromacs/analysisdata/datastorage.h"
 #include "gromacs/fatalerror/exceptions.h"
 #include "gromacs/fatalerror/gmxassert.h"
 
@@ -448,6 +448,16 @@ BasicHistogramImpl::ensureAveragerExists(AbstractAnalysisData *data)
     }
 }
 
+
+void
+BasicHistogramImpl::initFrame(AnalysisDataStorageFrame *frame)
+{
+    for (int i = 0; i < frame->columnCount(); ++i)
+    {
+        frame->setValue(i, 0.0);
+    }
+}
+
 } // namespace internal
 
 
@@ -506,38 +516,40 @@ void
 AnalysisDataSimpleHistogramModule::dataStarted(AbstractAnalysisData *data)
 {
     impl_->ensureAveragerExists(this);
-    impl_->hist_.resize(settings().binCount());
     setColumnCount(settings().binCount());
-    startDataStore();
+    notifyDataStart();
+    impl_->storage_.startDataStorage(this);
 }
 
 
 void
 AnalysisDataSimpleHistogramModule::frameStarted(const AnalysisDataFrameHeader &header)
 {
-    std::fill(impl_->hist_.begin(), impl_->hist_.end(), 0.0);
-    startNextFrame(header);
+    AnalysisDataStorageFrame &frame = impl_->storage_.startFrame(header);
+    impl_->initFrame(&frame);
 }
 
 
 void
 AnalysisDataSimpleHistogramModule::pointsAdded(const AnalysisDataPointSetRef &points)
 {
+    AnalysisDataStorageFrame &frame =
+        impl_->storage_.currentFrame(points.frameIndex());
     for (int i = 0; i < points.columnCount(); ++i)
     {
         int bin = settings().findBin(points.y(i));
         if (bin != -1)
         {
-            impl_->hist_[bin] += 1;
+            frame.value(bin) += 1;
         }
     }
 }
 
 
 void
-AnalysisDataSimpleHistogramModule::frameFinished(const AnalysisDataFrameHeader & /*header*/)
+AnalysisDataSimpleHistogramModule::frameFinished(const AnalysisDataFrameHeader &header)
 {
-    storeThisFrame(&impl_->hist_[0], NULL, NULL);
+    impl_->storage_.finishFrame(header.index());
 }
 
 
@@ -548,6 +560,20 @@ AnalysisDataSimpleHistogramModule::dataFinished()
 }
 
 
+AnalysisDataFrameRef
+AnalysisDataSimpleHistogramModule::tryGetDataFrameInternal(int index) const
+{
+    return impl_->storage_.tryGetDataFrame(index);
+}
+
+
+bool
+AnalysisDataSimpleHistogramModule::requestStorageInternal(int nframes)
+{
+    return impl_->storage_.requestStorage(nframes);
+}
+
+
 /********************************************************************
  * AnalysisDataWeightedHistogramModule
  */
@@ -603,17 +629,17 @@ void
 AnalysisDataWeightedHistogramModule::dataStarted(AbstractAnalysisData *data)
 {
     impl_->ensureAveragerExists(this);
-    impl_->hist_.resize(settings().binCount());
     setColumnCount(settings().binCount());
-    startDataStore();
+    notifyDataStart();
+    impl_->storage_.startDataStorage(this);
 }
 
 
 void
 AnalysisDataWeightedHistogramModule::frameStarted(const AnalysisDataFrameHeader &header)
 {
-    std::fill(impl_->hist_.begin(), impl_->hist_.end(), 0.0);
-    startNextFrame(header);
+    AnalysisDataStorageFrame &frame = impl_->storage_.startFrame(header);
+    impl_->initFrame(&frame);
 }
 
 
@@ -627,18 +653,20 @@ AnalysisDataWeightedHistogramModule::pointsAdded(const AnalysisDataPointSetRef &
     int bin = settings().findBin(points.y(0));
     if (bin != -1)
     {
+        AnalysisDataStorageFrame &frame =
+            impl_->storage_.currentFrame(points.frameIndex());
         for (int i = 1; i < points.columnCount(); ++i)
         {
-            impl_->hist_[bin] += points.y(i);
+            frame.value(bin) += points.y(i);
         }
     }
 }
 
 
 void
-AnalysisDataWeightedHistogramModule::frameFinished(const AnalysisDataFrameHeader & /*header*/)
+AnalysisDataWeightedHistogramModule::frameFinished(const AnalysisDataFrameHeader &header)
 {
-    storeThisFrame(&impl_->hist_[0], NULL, NULL);
+    impl_->storage_.finishFrame(header.index());
 }
 
 
@@ -649,6 +677,20 @@ AnalysisDataWeightedHistogramModule::dataFinished()
 }
 
 
+AnalysisDataFrameRef
+AnalysisDataWeightedHistogramModule::tryGetDataFrameInternal(int index) const
+{
+    return impl_->storage_.tryGetDataFrame(index);
+}
+
+
+bool
+AnalysisDataWeightedHistogramModule::requestStorageInternal(int nframes)
+{
+    return impl_->storage_.requestStorage(nframes);
+}
+
+
 /********************************************************************
  * AnalysisDataBinAverageModule
  */
index a4d815d26ca65d3bd731b2afe9e7fdad1abf2aa9..0ebeb24a72b376e814faeb1e7813601ed22decf4 100644 (file)
@@ -316,7 +316,7 @@ class AbstractAverageHistogram : public AbstractAnalysisArrayData
  * \inpublicapi
  * \ingroup module_analysisdata
  */
-class AnalysisDataSimpleHistogramModule : public AbstractAnalysisDataStored,
+class AnalysisDataSimpleHistogramModule : public AbstractAnalysisData,
                                           public AnalysisDataModuleInterface
 {
     public:
@@ -358,6 +358,9 @@ class AnalysisDataSimpleHistogramModule : public AbstractAnalysisDataStored,
         virtual void dataFinished();
 
     private:
+        virtual AnalysisDataFrameRef tryGetDataFrameInternal(int index) const;
+        virtual bool requestStorageInternal(int nframes);
+
         internal::BasicHistogramImpl   *impl_;
 
         // Copy and assign disallowed by base.
@@ -378,7 +381,7 @@ class AnalysisDataSimpleHistogramModule : public AbstractAnalysisDataStored,
  * \inpublicapi
  * \ingroup module_analysisdata
  */
-class AnalysisDataWeightedHistogramModule : public AbstractAnalysisDataStored,
+class AnalysisDataWeightedHistogramModule : public AbstractAnalysisData,
                                             public AnalysisDataModuleInterface
 {
     public:
@@ -406,6 +409,9 @@ class AnalysisDataWeightedHistogramModule : public AbstractAnalysisDataStored,
         virtual void dataFinished();
 
     private:
+        virtual AnalysisDataFrameRef tryGetDataFrameInternal(int index) const;
+        virtual bool requestStorageInternal(int nframes);
+
         internal::BasicHistogramImpl   *impl_;
 
         // Copy and assign disallowed by base.
diff --git a/src/gromacs/analysisdata/paralleloptions.h b/src/gromacs/analysisdata/paralleloptions.h
new file mode 100644 (file)
index 0000000..c268b5e
--- /dev/null
@@ -0,0 +1,80 @@
+/*
+ *
+ *                This source code is part of
+ *
+ *                 G   R   O   M   A   C   S
+ *
+ *          GROningen MAchine for Chemical Simulations
+ *
+ * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2009, The GROMACS development team,
+ * check out http://www.gromacs.org for more information.
+
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * If you want to redistribute modifications, please consider that
+ * scientific software is very special. Version control is crucial -
+ * bugs must be traceable. We will be happy to consider code for
+ * inclusion in the official distribution, but derived work must not
+ * be called official GROMACS. Details are found in the README & COPYING
+ * files - if they are missing, get the official version at www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the papers on the package - you can find them in the top README file.
+ *
+ * For more info, check our website at http://www.gromacs.org
+ */
+/*! \libinternal file
+ * \brief
+ * Declares gmx::AnalysisDataParallelOptions.
+ *
+ * \if internal
+ * Implementation of this class is currently in datastorage.cpp.
+ * \endif
+ *
+ * \author Teemu Murtola <teemu.murtola@cbr.su.se>
+ * \inlibraryapi
+ * \ingroup module_analysisdata
+ */
+#ifndef GMX_ANALYSISDATA_PARALLELOPTIONS_H
+#define GMX_ANALYSISDATA_PARALLELOPTIONS_H
+
+namespace gmx
+{
+
+/*! \brief
+ * Parallelization options for analysis data objects.
+ *
+ * Methods in this class do not throw.
+ *
+ * \inlibraryapi
+ */
+class AnalysisDataParallelOptions
+{
+    public:
+        //! Constructs options for serial execution.
+        AnalysisDataParallelOptions();
+        /*! \brief
+         * Constructs options for parallel execution with given number of
+         * concurrent frames.
+         *
+         * \param[in] parallelizationFactor
+         *      Number of frames that may be constructed concurrently.
+         *      Must be >= 1.
+         */
+        explicit AnalysisDataParallelOptions(int parallelizationFactor);
+
+        //! Returns the number of frames that may be constructed concurrently.
+        int parallelizationFactor() const { return parallelizationFactor_; }
+
+    private:
+        int                     parallelizationFactor_;
+};
+
+} // namespace gmx
+
+#endif
index 2c1bd82bbb0dfdd0b57140c3d2ced7dc071cf164..0505fb6899135cc8a6a77b3c23e2da65fceb26b6 100644 (file)
@@ -32,8 +32,9 @@
  * \brief
  * Tests for analysis data functionality.
  *
- * These tests check the functionality of gmx::AnalysisData, as well as its
- * base classes gmx::AbstractAnalysisData and gmx::AbstractAnalysisDataStored.
+ * These tests check the functionality of gmx::AnalysisData, as well as classes
+ * used in its implementation: gmx::AbstractAnalysisData and
+ * gmx::AnalysisDataStorage.
  * Most checking is done using gmx::test::AnalysisDataTestFixture and mock
  * modules that implement gmx::AnalysisDataModuleInterface.
  *
@@ -46,6 +47,7 @@
 #include <gtest/gtest.h>
 
 #include "gromacs/analysisdata/analysisdata.h"
+#include "gromacs/analysisdata/paralleloptions.h"
 #include "gromacs/fatalerror/exceptions.h"
 
 #include "datatest.h"
@@ -160,35 +162,6 @@ TEST_F(AnalysisDataTest, CallsColumnModuleCorrectly)
     ASSERT_NO_THROW(presentAllData(input, &data));
 }
 
-/*
- * Tests that data is forwarded correctly to modules when the data is added as
- * individual points and not as full frames.
- */
-TEST_F(AnalysisDataTest, CallsModuleCorrectlyWithIndividualPoints)
-{
-    gmx::test::AnalysisDataTestInput input(inputdata);
-    gmx::AnalysisData data;
-    data.setColumns(input.columnCount());
-
-    ASSERT_NO_THROW(addStaticCheckerModule(input, &data));
-    ASSERT_NO_THROW(addStaticColumnCheckerModule(input, 1, 2, &data));
-    gmx::AnalysisDataHandle *handle = NULL;
-    ASSERT_NO_THROW(handle = data.startData(NULL));
-    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, points.y(column)));
-        }
-        ASSERT_NO_THROW(handle->finishFrame());
-        EXPECT_EQ(row + 1, data.frameCount());
-    }
-    ASSERT_NO_THROW(handle->finishData());
-}
-
 /*
  * Tests that data is forwarded correctly (in frame order) to modules when the
  * data is added through multiple handles in non-increasing order.
@@ -203,8 +176,9 @@ TEST_F(AnalysisDataTest, CallsModuleCorrectlyWithOutOfOrderFrames)
     ASSERT_NO_THROW(addStaticColumnCheckerModule(input, 1, 2, &data));
     gmx::AnalysisDataHandle *handle1 = NULL;
     gmx::AnalysisDataHandle *handle2 = NULL;
-    ASSERT_NO_THROW(handle1 = data.startData(NULL));
-    ASSERT_NO_THROW(handle2 = data.startData(NULL));
+    gmx::AnalysisDataParallelOptions options(2);
+    ASSERT_NO_THROW(handle1 = data.startData(options));
+    ASSERT_NO_THROW(handle2 = data.startData(options));
     ASSERT_NO_THROW(presentDataFrame(input, 1, handle1));
     ASSERT_NO_THROW(presentDataFrame(input, 0, handle2));
     ASSERT_NO_THROW(presentDataFrame(input, 2, handle1));
index c057d81571cd8ed1a7cf703d83c1f55ecb3400f5..3c5072db4bd9b68c4ab5c7ead3d77bfe3a7c3aaa 100644 (file)
@@ -43,6 +43,7 @@
 #include <gtest/gtest.h>
 
 #include "gromacs/analysisdata/analysisdata.h"
+#include "gromacs/analysisdata/paralleloptions.h"
 #include "gromacs/fatalerror/gmxassert.h"
 #include "gromacs/utility/format.h"
 
@@ -149,8 +150,8 @@ AnalysisDataTestFixture::AnalysisDataTestFixture()
 void AnalysisDataTestFixture::presentAllData(const AnalysisDataTestInput &input,
                                              AnalysisData *data)
 {
-    gmx::AnalysisDataHandle *handle = NULL;
-    handle = data->startData(NULL);
+    gmx::AnalysisDataParallelOptions options;
+    gmx::AnalysisDataHandle *handle = data->startData(options);
     for (int row = 0; row < input.frameCount(); ++row)
     {
         presentDataFrame(input, row, handle);
@@ -168,8 +169,14 @@ void AnalysisDataTestFixture::presentDataFrame(const AnalysisDataTestInput &inpu
     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());
+        for (int j = 0; j < points.size(); ++j)
+        {
+            handle->setPoint(j, points.y(j), points.dy(j), points.present(j));
+        }
+        if (input.isMultipoint())
+        {
+            handle->finishPointSet();
+        }
     }
     handle->finishFrame();
 }
index e44ddac3511dd50e5e1cef5b5a469b6db9775f8a..a76c1fb6f36e0dcde0729852a69d4d997f58ef0c 100644 (file)
@@ -73,7 +73,8 @@ const real MPSTOP = -std::numeric_limits<real>::max();
  *
  * 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().
+ * passed separately and AnalysisDataHandle::finishPointSet() called in
+ * between.
  *
  * \ingroup module_analysisdata
  */
@@ -84,10 +85,9 @@ class AnalysisDataTestInputPointSet
 
         int size() const { return y_.size(); }
         real y(int i) const { return y_[i]; }
+        real dy(int i) const { return 0.0; }
+        real present(int i) const { return true; }
         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_;
index 0b568a8e56b2b378b8458c312215fd9badf048fc..3d5280868cb894f7b0d9a886538895b35c0f921b 100644 (file)
@@ -58,7 +58,7 @@ class TrajectoryAnalysisModuleData::Impl
         typedef std::map<std::string, AnalysisDataHandle *> HandleContainer;
 
         Impl(TrajectoryAnalysisModule *module,
-             /*AnalysisDataParallelOptions*/ void* opt,
+             const AnalysisDataParallelOptions &opt,
              const SelectionCollection &selections);
         ~Impl();
 
@@ -89,7 +89,7 @@ class TrajectoryAnalysisModuleDataBasic : public TrajectoryAnalysisModuleData
 {
     public:
         TrajectoryAnalysisModuleDataBasic(TrajectoryAnalysisModule *module,
-                                          /*AnalysisDataParallelOptions*/ void* opt,
+                                          const AnalysisDataParallelOptions &opt,
                                           const SelectionCollection &selections);
 
         virtual void finish();
index df5c22d7124a17c3a60451d319fa79c75f636a1a..63292c8f92ee2dbed6a163b3a19152e939410c02 100644 (file)
@@ -51,7 +51,7 @@ namespace gmx
 
 TrajectoryAnalysisModuleData::Impl::Impl(
         TrajectoryAnalysisModule *module,
-        AnalysisDataParallelOptions opt,
+        const AnalysisDataParallelOptions &opt,
         const SelectionCollection &selections)
     : _selections(selections)
 {
@@ -86,7 +86,7 @@ void TrajectoryAnalysisModuleData::Impl::finishHandles()
 
 TrajectoryAnalysisModuleData::TrajectoryAnalysisModuleData(
         TrajectoryAnalysisModule *module,
-        AnalysisDataParallelOptions opt,
+        const AnalysisDataParallelOptions &opt,
         const SelectionCollection &selections)
     : _impl(new Impl(module, opt, selections))
 {
@@ -140,7 +140,7 @@ TrajectoryAnalysisModuleData::parallelSelections(const std::vector<Selection *>
  */
 TrajectoryAnalysisModuleDataBasic::TrajectoryAnalysisModuleDataBasic(
         TrajectoryAnalysisModule *module,
-        /*AnalysisDataParallelOptions*/ void* opt,
+        const AnalysisDataParallelOptions &opt,
         const SelectionCollection &selections)
     : TrajectoryAnalysisModuleData(module, opt, selections)
 {
@@ -181,7 +181,7 @@ void TrajectoryAnalysisModule::initAfterFirstFrame(const t_trxframe &/*fr*/)
 
 
 TrajectoryAnalysisModuleData *
-TrajectoryAnalysisModule::startFrames(AnalysisDataParallelOptions opt,
+TrajectoryAnalysisModule::startFrames(const AnalysisDataParallelOptions &opt,
                                       const SelectionCollection &selections)
 {
     return new TrajectoryAnalysisModuleDataBasic(this, opt, selections);
index e6661ecb988fa527f6082ab6eaccec3a647c31b6..1a25e50dbb01dfa323d6cfb0b992e52995eff668 100644 (file)
@@ -51,6 +51,7 @@ namespace gmx
 class AbstractAnalysisData;
 class AnalysisData;
 class AnalysisDataHandle;
+class AnalysisDataParallelOptions;
 class Options;
 class Selection;
 class SelectionCollection;
@@ -114,7 +115,7 @@ class TrajectoryAnalysisModuleData
          * The handles are accessible through dataHandle().
          */
         TrajectoryAnalysisModuleData(TrajectoryAnalysisModule *module,
-                                     /*AnalysisDataParallelOptions*/ void* opt,
+                                     const AnalysisDataParallelOptions &opt,
                                      const SelectionCollection &selections);
 
         /*! \brief
@@ -228,7 +229,7 @@ class TrajectoryAnalysisModule
          * \see TrajectoryAnalysisModuleData
          */
         virtual TrajectoryAnalysisModuleData *startFrames(
-                /*AnalysisDataParallelOptions*/ void* opt,
+                const AnalysisDataParallelOptions &opt,
                 const SelectionCollection &selections);
         /*! \brief
          * Analyzes a single frame.
index 234001d0137d2fb0b7ddaefbce5a273427344d7a..54a0a0b2971e2945e244d7ab30a0ee3728b35e44 100644 (file)
@@ -46,6 +46,7 @@
 #include <rmpbc.h>
 #include <statutil.h>
 
+#include "gromacs/analysisdata/paralleloptions.h"
 #include "gromacs/fatalerror/exceptions.h"
 #include "gromacs/fatalerror/gmxassert.h"
 #include "gromacs/options/asciihelpwriter.h"
@@ -234,8 +235,9 @@ TrajectoryAnalysisCommandLineRunner::run(int argc, char *argv[])
     t_pbc *ppbc = settings.hasPBC() ? &pbc : NULL;
 
     int nframes = 0;
+    AnalysisDataParallelOptions dataOptions;
     std::auto_ptr<TrajectoryAnalysisModuleData>
-        pdata(module->startFrames(NULL, selections));
+        pdata(module->startFrames(dataOptions, selections));
     do
     {
         common.initFrame();
index c339d253db1adc05ceb360993ae5d2b57f4e80a8..128acea7f46b7cc67119eeec69fac0a1a883f838 100644 (file)
@@ -563,7 +563,7 @@ Angle::analyzeFrame(int frnr, const t_trxframe &fr, t_pbc *pbc,
             }
             if (_bAll)
             {
-                dh->addPoint(n + 1, angle);
+                dh->setPoint(n + 1, angle);
             }
             ave += angle;
             ++n;
@@ -572,7 +572,7 @@ Angle::analyzeFrame(int frnr, const t_trxframe &fr, t_pbc *pbc,
         {
             ave /= n;
         }
-        dh->addPoint(g, ave);
+        dh->setPoint(g, ave);
     }
     dh->finishFrame();
 }
index a12532cead38729d056d039ec95bf6d0b0d47159..a3952496e9372a5b6eb637a3edfcf6fc1b464ce3 100644 (file)
@@ -139,8 +139,8 @@ Distance::analyzeFrame(int frnr, const t_trxframe &fr, t_pbc *pbc,
     }
     r = norm(dx);
     dh->startFrame(frnr, fr.time);
-    dh->addPoint(0, r);
-    dh->addPoints(1, 3, dx);
+    dh->setPoint(0, r);
+    dh->setPoints(1, 3, dx);
     dh->finishFrame();
 }
 
index 8ce4de634276decb1006e9e063f3b64ff02805a7..ff8f0847027f1b614695010e1c223aac058f55f2 100644 (file)
@@ -236,7 +236,7 @@ class Select::ModuleData : public TrajectoryAnalysisModuleData
 {
     public:
         ModuleData(TrajectoryAnalysisModule *module,
-                   AnalysisDataParallelOptions opt,
+                   const AnalysisDataParallelOptions &opt,
                    const SelectionCollection &selections)
             : TrajectoryAnalysisModuleData(module, opt, selections),
               _mmap(NULL)
@@ -469,7 +469,7 @@ Select::initAnalysis(const TrajectoryAnalysisSettings &settings,
 
 
 TrajectoryAnalysisModuleData *
-Select::startFrames(AnalysisDataParallelOptions opt,
+Select::startFrames(const AnalysisDataParallelOptions &opt,
                     const SelectionCollection &selections)
 {
     ModuleData *pdata = new ModuleData(this, opt, selections);
@@ -498,7 +498,7 @@ Select::analyzeFrame(int frnr, const t_trxframe &fr, t_pbc *pbc,
         {
             real normfac = _bFracNorm ? 1.0 / sel[g]->coveredFraction() : 1.0;
             normfac /= _totsize[g];
-            sdh->addPoint(g, sel[g]->posCount() * normfac);
+            sdh->setPoint(g, sel[g]->posCount() * normfac);
         }
         sdh->finishFrame();
     }
@@ -508,7 +508,7 @@ Select::analyzeFrame(int frnr, const t_trxframe &fr, t_pbc *pbc,
         cdh->startFrame(frnr, fr.time);
         for (size_t g = 0; g < sel.size(); ++g)
         {
-            cdh->addPoint(g, sel[g]->coveredFraction());
+            cdh->setPoint(g, sel[g]->coveredFraction());
         }
         cdh->finishFrame();
     }
@@ -518,17 +518,19 @@ Select::analyzeFrame(int frnr, const t_trxframe &fr, t_pbc *pbc,
         idh->startFrame(frnr, fr.time);
         for (size_t g = 0; g < sel.size(); ++g)
         {
-            idh->addPoint(0, sel[g]->posCount());
+            idh->setPoint(0, sel[g]->posCount());
+            idh->finishPointSet();
             for (int i = 0; i < sel[g]->posCount(); ++i)
             {
                 if (sel[g]->type() == INDEX_RES && !_bResInd)
                 {
-                    idh->addPoint(1, _top->atoms.resinfo[sel[g]->mapId(i)].nr);
+                    idh->setPoint(1, _top->atoms.resinfo[sel[g]->mapId(i)].nr);
                 }
                 else
                 {
-                    idh->addPoint(1, sel[g]->mapId(i) + 1);
+                    idh->setPoint(1, sel[g]->mapId(i) + 1);
                 }
+                idh->finishPointSet();
             }
         }
         idh->finishFrame();
@@ -540,7 +542,7 @@ Select::analyzeFrame(int frnr, const t_trxframe &fr, t_pbc *pbc,
         mdh->startFrame(frnr, fr.time);
         for (int b = 0; b < d->_mmap->nr; ++b)
         {
-            mdh->addPoint(b, d->_mmap->refid[b] == -1 ? 0 : 1);
+            mdh->setPoint(b, d->_mmap->refid[b] == -1 ? 0 : 1);
         }
         mdh->finishFrame();
     }
index 2372e9e13397849ef51c2a17411234c15450f9dd..b1c98a93663149d01d3925d16a16f1d8a223d81e 100644 (file)
@@ -67,7 +67,7 @@ class Select : public TrajectoryAnalysisModule
                                   const TopologyInformation &top);
 
         virtual TrajectoryAnalysisModuleData *startFrames(
-                    AnalysisDataParallelOptions opt,
+                    const AnalysisDataParallelOptions &opt,
                     const SelectionCollection &selections);
         virtual void analyzeFrame(int frnr, const t_trxframe &fr, t_pbc *pbc,
                                   TrajectoryAnalysisModuleData *pdata);