More precise analysisdata histogram accumulation
[alexxy/gromacs.git] / src / gromacs / analysisdata / datastorage.h
index 9ba187be106775e62ad11ba335a1de804e5b48b9..4e9fd2534127aa686175851136d49766b7d737a7 100644 (file)
@@ -1,10 +1,10 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2012, by the GROMACS development team, led by
- * David van der Spoel, Berk Hess, Erik Lindahl, and including many
- * others, as listed in the AUTHORS file in the top-level source
- * directory and at http://www.gromacs.org.
+ * Copyright (c) 2012,2013,2014, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
  *
  * GROMACS is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public License
 
 #include <vector>
 
-#include "../legacyheaders/types/simple.h"
-
-#include "../utility/common.h"
-#include "../utility/gmxassert.h"
-
-#include "dataframe.h"
+#include "gromacs/analysisdata/dataframe.h"
+#include "gromacs/utility/common.h"
+#include "gromacs/utility/gmxassert.h"
+#include "gromacs/utility/real.h"
 
 namespace gmx
 {
@@ -58,17 +56,24 @@ namespace gmx
 class AbstractAnalysisData;
 class AnalysisDataFrameHeader;
 class AnalysisDataFrameRef;
+class AnalysisDataModuleManager;
 class AnalysisDataParallelOptions;
 
 class AnalysisDataStorage;
 
+namespace internal
+{
+class AnalysisDataStorageImpl;
+class AnalysisDataStorageFrameData;
+}   // namespace internal
+
 /*! \libinternal \brief
- * Stores a single data frame for AnalysisDataStorage.
- *
- * It also provides access to the frame outside AnalysisDataStorage.
+ * Allows assigning values for a data frame in AnalysisDataStorage.
  *
- * It is implemented such that the frame header is always valid, i.e.,
- * header().isValid() returns always true.
+ * This class implements the necessary methods to add new data into the
+ * storage.  AnalysisDataStorage::startFrame() returns an object of this type,
+ * which can be used to add one or more point sets to that data frame.
+ * When all data has been added, finishFrame() needs to be called.
  *
  * \inlibraryapi
  * \ingroup module_analysisdata
@@ -82,23 +87,24 @@ class AnalysisDataStorageFrame
          */
         ~AnalysisDataStorageFrame();
 
-        //! 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 values_.size(); }
-
         /*! \brief
-         * Returns point set reference to currently set values.
+         * Select data set that all other methods operate on.
+         *
+         * \param[in] index  Zero-based data set index to select.
+         *
+         * With multipoint data, a single point set can only contain values in
+         * a single data set.
+         * With non-multipoint data, arbitrary sequences of selectDataSet() and
+         * setValue() are supported.  The full frame is notified to the modules
+         * once it is finished.
          *
          * Does not throw.
          */
-        AnalysisDataPointSetRef currentPoints() const;
+        void selectDataSet(int index);
+
+        //! Returns number of columns for the frame.
+        int columnCount() const { return columnCount_; }
+
         /*! \brief
          * Sets value for a column.
          *
@@ -115,7 +121,8 @@ class AnalysisDataStorageFrame
         {
             GMX_ASSERT(column >= 0 && column < columnCount(),
                        "Invalid column index");
-            values_[column].setValue(value, bPresent);
+            values_[currentOffset_ + column].setValue(value, bPresent);
+            bPointSetInProgress_ = true;
         }
         /*! \brief
          * Sets value for a column.
@@ -134,7 +141,8 @@ class AnalysisDataStorageFrame
         {
             GMX_ASSERT(column >= 0 && column < columnCount(),
                        "Invalid column index");
-            values_[column].setValue(value, error, bPresent);
+            values_[currentOffset_ + column].setValue(value, error, bPresent);
+            bPointSetInProgress_ = true;
         }
         /*! \brief
          * Access value for a column.
@@ -151,7 +159,7 @@ class AnalysisDataStorageFrame
         {
             GMX_ASSERT(column >= 0 && column < columnCount(),
                        "Invalid column index");
-            return values_[column].value();
+            return values_[currentOffset_ + column].value();
         }
         /*! \brief
          * Access value for a column.
@@ -167,7 +175,7 @@ class AnalysisDataStorageFrame
         {
             GMX_ASSERT(column >= 0 && column < columnCount(),
                        "Invalid column index");
-            return values_[column].value();
+            return values_[currentOffset_ + column].value();
         }
         /*! \brief
          * Mark point set as finished for multipoint data.
@@ -179,37 +187,55 @@ class AnalysisDataStorageFrame
          *
          * After this method has been called, all values appear as not set.
          *
-         * Calls AbstractAnalysisData::notifyPointsAdd(), and throws any
-         * exception this method throws.
+         * May call AnalysisDataModuleManager::notifyPointsAdd() and
+         * AnalysisDataModuleManager::notifyParallelPointsAdd(), and may throw
+         * any exception these methods throw.
          */
         void finishPointSet();
+        /*! \brief
+         * Finish storing a frame.
+         *
+         * Must be called exactly once for each frame returned by startFrame(),
+         * after the corresponding call.
+         * The frame object must not be accessed after the call.
+         *
+         * Calls notification methods in AnalysisDataModuleManager, and may
+         * throw any exceptions these methods throw.
+         */
+        void finishFrame();
 
     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.
+         * \param[in] data  Data object for which the frame is for
+         *      (used for data set and column counts).
          */
-        AnalysisDataStorageFrame(AnalysisDataStorage *storage, int columnCount,
-                                 int index);
+        explicit AnalysisDataStorageFrame(const AbstractAnalysisData &data);
 
         //! Clear all column values from the frame.
         void clearValues();
 
-        //! Storage object that contains this frame.
-        AnalysisDataStorage           &storage_;
-        //! Header for the frame.
-        AnalysisDataFrameHeader        header_;
-        //! Values for the frame.
-        std::vector<AnalysisDataValue> values_;
+        //! Implementation data.
+        internal::AnalysisDataStorageFrameData *data_;
+        //! Values for the currently in-progress point set.
+        std::vector<AnalysisDataValue>          values_;
 
-        /*! \brief
-         * Needed for full write access to the data and for access to
-         * constructor/destructor.
-         */
-        friend class AnalysisDataStorage;
+        //! Index of the currently active dataset.
+        int                                     currentDataSet_;
+        //! Offset of the first value in \a values_ for the current data set.
+        int                                     currentOffset_;
+        //! Number of columns in the current data set.
+        int                                     columnCount_;
+
+        //! Whether any values have been set in the current point set.
+        bool                                    bPointSetInProgress_;
+
+        //! Needed for access to the constructor.
+        friend class internal::AnalysisDataStorageImpl;
+        //! Needed for managing the frame the object points to.
+        friend class internal::AnalysisDataStorageFrameData;
 
         GMX_DISALLOW_COPY_AND_ASSIGN(AnalysisDataStorageFrame);
 };
@@ -220,21 +246,15 @@ class AnalysisDataStorageFrame
  * 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().
+ * variable of this type should be declared and the pure virtual methods
+ * forwarded to frameCount(), tryGetDataFrame() and requestStorage().
+ * Storage properties should be set up, and then startDataStorage() or
+ * startParallelDataStorage() called.
  * 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.
+ * finishFrame() methods.  When all frames are ready, finishDataStorage() must
+ * be called.  These methods (and AnalysisDataStorageFrame::finishPointSet())
+ * take the responsibility of calling all the notification methods in
+ * AnalysisDataModuleManager,
  *
  * \todo
  * Proper multi-threaded implementation.
@@ -250,29 +270,18 @@ class AnalysisDataStorage
         ~AnalysisDataStorage();
 
         /*! \brief
-         * Sets whether the data will be multipoint.
+         * Returns the number of ready frames.
          *
-         * \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.
+         * This method is designed such that calls to
+         * AbstractAnalysisData::frameCount() can be directly forwarded to this
+         * method.  See that method for more documentation.
          *
-         * If this method is not called, the storage is set up for serial
-         * storage only.
+         * If this method returns N, this means that the first N frames have
+         * all been finished.
          *
-         * Does not throw.
+         * \see AbstractAnalysisData::frameCount()
          */
-        void setParallelOptions(const AnalysisDataParallelOptions &opt);
-
+        int frameCount() const;
         /*! \brief
          * Implements access to data frames.
          *
@@ -293,9 +302,6 @@ class AnalysisDataStorage
          * 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);
@@ -303,23 +309,47 @@ class AnalysisDataStorage
         /*! \brief
          * Start storing data.
          *
-         * \param  data  AbstractAnalysisData object containing this storage.
+         * \param[in] data    AbstractAnalysisData object containing this
+         *      storage.
+         * \param     modules Module manager for \p data.
          * \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 called as \c startDataStorage(this, &moduleManager())
+         * from a member of \p data when the data is ready to be started.
+         * The storage object will take responsibility of calling all
+         * module notification methods in AnalysisDataModuleManager using
+         * \p modules.
+         *
+         * Lifetime of \p data and \p modules 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.
+         *
+         * Calls AnalysisDataModuleManager::notifyDataStart(), and throws any
+         * exceptions this method throws.
+         */
+        void startDataStorage(AbstractAnalysisData      *data,
+                              AnalysisDataModuleManager *modules);
+        /*! \brief
+         * Start storing data in parallel.
+         *
+         * \param[in] data    AbstractAnalysisData object containing this
+         *      storage.
+         * \param[in] options Parallelization options to use.
+         * \param     modules Module manager for \p data.
+         * \exception std::bad_alloc if storage allocation fails.
+         *
+         * Should be called instead of startDataStorage() if the data will be
+         * produced in parallel.  Works as startDataStorage(), but additionally
+         * initializes the storage and the attached modules to prepare for
+         * out-of-order data frames.
+         *
+         * Calls AnalysisDataModuleManager::notifyParallelDataStart(), and
+         * throws any exceptions this method throws.
          */
-        void startDataStorage(AbstractAnalysisData *data);
+        void startParallelDataStorage(
+            AbstractAnalysisData              *data,
+            AnalysisDataModuleManager         *modules,
+            const AnalysisDataParallelOptions &options);
         /*! \brief
          * Starts storing a new frame.
          *
@@ -338,13 +368,14 @@ class AnalysisDataStorage
          * 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
+         * at most \c parallelizationFactor 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.
+         * Calls AnalysisDataModuleManager::notifyFrameStart() (in certain
+         * cases) and AnalysisDataModuleManager::notifyParallelFrameStart(),
+         * and throws any exceptions these methods throw.
          */
         AnalysisDataStorageFrame &startFrame(const AnalysisDataFrameHeader &header);
         /*! \brief
@@ -368,39 +399,27 @@ class AnalysisDataStorage
          */
         AnalysisDataStorageFrame &currentFrame(int index);
         /*! \brief
-         * Finish storing a frame.
+         * Convenience method for finishing a data frame.
          *
          * \param[in] index  Frame index.
          *
-         * Must be called exactly once for each startFrame(), after the
-         * corresponding call.
+         * Identical to \c currentFrame(index).finishFrame().
          *
-         * Calls notification methods in AbstractAnalysisData, and throws any
-         * exceptions these methods throw.
+         * \see AnalysisDataStorageFrame::finishFrame()
          */
         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.
+         * Finishes storing data.
          *
-         * Identical to \c finishframe(frame.frameIndex());
+         * Calls AnalysisDataModuleManager::notifyDataFinish(), and throws any
+         * exceptions this method throws.
          */
-        void finishFrame(const AnalysisDataStorageFrame &frame);
+        void finishDataStorage();
 
     private:
-        class Impl;
+        typedef internal::AnalysisDataStorageImpl Impl;
 
         PrivateImplPointer<Impl> impl_;
-
-        /*! \brief
-         * Needed because the frame object needs to trigger notifications.
-         */
-        friend void AnalysisDataStorageFrame::finishPointSet();
 };
 
 } // namespace gmx