/*
* 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
{
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
*/
~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.
*
{
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.
{
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.
{
GMX_ASSERT(column >= 0 && column < columnCount(),
"Invalid column index");
- return values_[column].value();
+ return values_[currentOffset_ + column].value();
}
/*! \brief
* Access value for a column.
{
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.
*
* 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);
};
* 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.
~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.
*
* 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.
+ * \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.
*
* 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
*/
AnalysisDataStorageFrame ¤tFrame(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