* Provides functionality for handling and processing output data from
* analysis.
*
+ * <H3>Overview</H3>
+ *
+ * This module provides functionality to do common processing for tabular data
+ * in analysis tools. In addition to providing this common functionality, one
+ * major driver for this module is to make it simple to write analysis tools
+ * that process frames in parallel: the functionality in this module takes care
+ * of necessary synchronization and communication such that output from the
+ * frames is collected and output in the correct order.
+ *
+ * This module consists of two main parts. The first is formed by the
+ * AbstractAnalysisData class and classes that derive from it:
+ * AnalysisData and AnalysisArrayData. These classes are used to process and
+ * store raw data as produced by the analysis tool. They also provide an
+ * interface to attach data modules that implement AnalysisDataModuleInterface.
+ * Modules that implement this interface form the second part of the module,
+ * and they provide functionality to do processing operations on the data.
+ * These modules can also derive from AbstractAnalysisData, allowing other
+ * modules to be attached to them to form a processing chain that best suits
+ * the analysis tool. Typically, such a processing chain ends in a plotting
+ * module that writes the data into a file, but the final module can also
+ * provide direct access to the processed data, allowing the analysis tool to
+ * do custom postprocessing outside the module framework.
+ *
+ * <H3>Using Data Objects and Modules</H3>
+ *
+ * To use the functionality in this module, you typically declare one or more
+ * AnalysisData objects and set its properties. You then create some module
+ * objects and set their properties (see the list of classes that implement
+ * AnalysisDataModuleInterface) and attach them to the data objects or to one
+ * another using AbstractAnalysisData::addModule(). Then you add the actual
+ * data values to the AnalysisData object, which automatically passes it on to
+ * the modules. After all data is added, you may optionally access some
+ * results directly from the module objects. However, in many cases it is
+ * sufficient to initially add a plotting module to the processing chain, which
+ * will then automatically write the results into a file.
+ *
+ * For simple processing needs with a small amount of data, an
+ * AnalysisArrayData class is also provided, which keeps all the data in an
+ * in-memory array and allows you to manipulate the data as you wish before you
+ * pass the data to the attached modules.
+ *
+ * \if libapi
+ * <H3>Writing New Data and Module Objects</H3>
+ *
+ * New data modules can be implemented to perform custom operations that are
+ * not supported by the modules provided in this module. This is done by
+ * creating a new class that implements AnalysisDataModuleInterface.
+ * If the new module computes values that can be used as input for other
+ * modules, the new class should also derive from AbstractAnalysisData, and
+ * preferably use AnalysisDataStorage internally to implement storage of
+ * values. See the documentation of the mentioned classes for more details on
+ * how to implement custom modules.
+ * When implementing a new module, it should be considered whether it can be of
+ * more general use, and if so, it should be added to this module.
+ *
+ * It is also possible to implement new data source objects by deriving a class
+ * from AbstractAnalysisData. This should not normally be necessary, since
+ * this module provides general data source objects for most typical uses.
+ * If the classes in this module are not suitable for some specific use, it
+ * should be considered whether a new generic class could be added (or an
+ * existing extended) instead of implementing a local custom solution.
+ * \endif
+ *
* \author Teemu Murtola <teemu.murtola@cbr.su.se>
*/
/*! \file
*
* \param[in] data Data object to read data from.
* \param[in] module Module to present the data to.
- * \exception APIError if \p module is not compatible with the
- * data object.
- * \exception APIError if all data is not available
- * through getDataFrame().
- *
- * Other exceptions may also be thrown if methods called in \p module
- * throw them.
+ * \throws APIError if \p module is not compatible with the data
+ * object.
+ * \throws APIError if all data is not available through
+ * getDataFrame().
+ * \throws unspecified Any exception thrown by \p module in its data
+ * notification methods.
*
* Uses getDataFrame() in \p data to access all data in the object, and
* calls the notification functions in \p module as if the module had
{
AnalysisDataFrameRef frame = data->getDataFrame(i);
GMX_RELEASE_ASSERT(frame.isValid(), "Invalid data frame returned");
+ // TODO: Check all frames before doing anything for slightly better
+ // exception behavior.
if (bCheckMissing && !frame.allPresent())
{
GMX_THROW(APIError("Missing data not supported by a module"));
/*! \brief
* Abstract base class for all objects that provide data.
*
- * The public interface includes functions for querying the data
- * (isMultipoint(), columnCount(), frameCount(), tryGetDataFrame(),
- * getDataFrame(), requestStorage()) and functions for using modules for
- * processing the data (addModule(), addColumnModule(), applyModule()).
+ * The public interface includes methods for querying the data (isMultipoint(),
+ * columnCount(), frameCount(), tryGetDataFrame(), getDataFrame(),
+ * requestStorage()) and methods for using modules for processing the data
+ * (addModule(), addColumnModule(), applyModule()).
*
- * \if libapi
- * There are also protected functions for use in derived classes:
- * setting the properties returned by isMultipoint() and columnCount()
- * through setMultipoint() and setColumnCount(), and notifying attached
- * modules of added data.
- * \endif
- *
- * Notice that even for non-const objects, the interface does not provide
- * any means of altering the data. It is only possible to add modules,
- * making it relatively safe to return a non-const pointer of this type
- * pointing to an internal data structure without worrying about possible
- * modifications of the data.
+ * Notice that even for non-const objects, the interface does not provide any
+ * means of altering the data. It is only possible to add modules, making it
+ * relatively safe to return a non-const pointer of this type pointing to an
+ * internal data structure without worrying about possible modifications of the
+ * data.
*
* \if libapi
- * It is up to subclasses to ensure that the protected functions are called
- * in a correct sequence (the functions will assert in most incorrect use
- * cases), and that the data provided through the public interface matches
- * that passed to the modules with the notify methods.
+ * This class also provides protected methods for use in derived classes.
+ * The properties returned by isMultipoint() and columnCount() must be set using
+ * setMultipoint() and setColumnCount(), and notify*() methods must be used to
+ * report when data becomes available for modules to process it.
+ * There are also two protected pure virtual methods that need to be
+ * implemented to provide access to stored data: requestStorageInternal() and
+ * tryGetDataFrameInternal().
+ *
+ * It is up to subclasses to ensure that the protected methods are called in a
+ * correct sequence (the methods will assert in most incorrect use cases), and
+ * that the data provided through the public interface matches that passed to
+ * the modules with the notify methods.
+ * Helper class AnalysisDataStorage provides a default implementation for
+ * storing data (calls to the pure virtual methods can simply be forwarded to
+ * appropriate methods in the helper class), and takes care of correctly
+ * calling the notification methods when new data is added to the storage.
+ * In most cases, it should be used to implement the derived classes.
* \endif
*
* Currently, it is not possible to continue using the data object if an
* doesn't support storing such data, but this should rarely be
* necessary.
*
+ * The returned value does not change after modules have been notified
+ * of data start.
* \if libapi
- * Derived classes can change the type by calling setMultipoint().
+ * Derived classes can change the type by calling setMultipoint()
+ * subject to the above restriction.
* If this is not done, the function always returns false.
* \endif
+ *
+ * Does not throw.
*/
bool isMultipoint() const { return _bMultiPoint; }
/*! \brief
* \returns The number of columns in the data.
*
* If the number of columns is yet known, returns 0.
+ * The returned value does not change after modules have been notified
+ * of data start, but may change multiple times before that, depending
+ * on the actual data class.
* \if libapi
* Derived classes should set the number of columns with
- * setColumnCount().
+ * setColumnCount(), within the above limitations.
* \endif
+ *
+ * Does not throw.
*/
int columnCount() const { return _ncol; }
/*! \brief
* \returns The total number of frames in the data.
*
* This function returns the number of frames that the object has
- * produced. If requestStorage() has been successfully called,
- * getDataFrame() can be used to access some or all of these frames.
+ * produced. If requestStorage() has been successfully called,
+ * tryGetDataframe() or getDataFrame() can be used to access some or
+ * all of these frames.
+ *
+ * Does not throw.
*/
int frameCount() const;
/*! \brief
* Access stored data.
*
* \param[in] index Zero-based frame index to access.
- * \retval Frame reference to frame \p index, or an invalid
+ * \returns Frame reference to frame \p index, or an invalid
* reference if no such frame is available.
*
* Does not throw. Failure to access a frame with the given index is
* Access stored data.
*
* \param[in] index Zero-based frame index to access.
- * \retval Frame reference to frame \p index.
- * \exception APIError if the requested frame is accessible.
+ * \returns Frame reference to frame \p index.
+ * \throws APIError if the requested frame is not accessible.
*
* If the data is not certainly available, use tryGetDataFrame().
*
*
* \param[in] nframes Request storing at least \c nframes previous
* frames (-1 = request storing all). Must be >= -1.
- * \retval true if the request could be satisfied.
+ * \returns true if the request could be satisfied.
*
* If called multiple times, the largest request is honored.
*
/*! \brief
* Adds a module to process the data.
*
- * \param module Module to add.
- * \exception APIError if
+ * \param module Module to add.
+ * \throws APIError if
* - \p module is not compatible with the data object
* - data has already been added to the data object and everything
* is not available through getDataFrame().
* immediately processes all existing data. APIError is thrown
* if all data is not available through getDataFrame().
*
- * By default, the data object takes ownership of the module, and
- * automatically destructs it when the data object itself is destroyed.
- * See keepOwnership() for a way to keep the ownership with the caller.
+ * The caller can keep a copy of the module pointer if it requires
+ * later access to the module.
+ *
+ * If the method throws, the state of the data object is not changed.
+ * The state of the data module is indeterminate.
*/
void addModule(AnalysisDataModulePointer module);
/*! \brief
* \param[in] col First column.
* \param[in] span Number of columns.
* \param module Module to add.
+ * \throws APIError in same situations as addModule().
*
* \see addModule()
*/
/*! \brief
* Applies a module to process data that is ready.
*
- * \param module Module to apply.
+ * \param module Module to apply.
+ * \throws APIError in same situations as addModule().
*
- * This function works as addModule(), except that it does not take
- * ownership of \p module. Also, it can only be called after the data
- * is ready, and only if getDataFrame() gives access to all of the data.
+ * This function works as addModule(), except that it does not keep a
+ * reference to \p module within the data object after it returns.
+ * Also, it can only be called after the data is ready, and only if
+ * getDataFrame() gives access to all of the data.
* It is provided for additional flexibility in postprocessing
* in-memory data.
+ *
+ * \todo
+ * Currently, this method may not work correctly if \p module requests
+ * storage (addModule() has the same problem if called after data is
+ * started).
*/
void applyModule(AnalysisDataModuleInterface *module);
protected:
/*! \cond libapi */
+ /*! \brief
+ * Initializes a new analysis data object.
+ *
+ * \throws std::bad_alloc if out of memory.
+ */
AbstractAnalysisData();
/*! \brief
* addModule() has been called, otherwise asserts (a single call
* can occur after addModule() if no calls have been made earlier).
*
+ * Does not throw, but this may change with the below todo item.
+ *
+ * \todo
+ * Consider whether the semantics with respect to addModule() and
+ * notifyDataStart(), and the performed checks, are suitable for all
+ * purposes.
+ *
* \see columnCount()
*/
void setColumnCount(int ncol);
* Can be called only before addModule() or notifyDataStart(),
* otherwise asserts.
*
+ * Does not throw, but this may change with the todo item in
+ * setColumnCount().
+ *
* \see isMultipoint()
*/
void setMultipoint(bool multipoint);
* Implements access to data frames.
*
* \param[in] index Zero-based frame index to access.
- * \retval Frame reference to frame \p index, or an invalid
+ * \returns Frame reference to frame \p index, or an invalid
* reference if no such frame is available.
*
* Must not throw. Failure to access a frame with the given index is
*
* This method is called internally by tryGetDataFrame() and
* getDataFrame().
+ *
+ * \see AnalysisDataStorage
*/
virtual AnalysisDataFrameRef tryGetDataFrameInternal(int index) const = 0;
/*! \brief
*
* \param[in] nframes Request storing at least \c nframes previous
* frames (-1 = request storing all). Will be either -1 or >0.
- * \retval true if the request could be satisfied.
+ * \returns true if the request could be satisfied.
*
* Must not throw. Failure to access a frame with the given index is
* indicated through the return value.
* method before notifyDataStart() is called (and during that call).
*
* This method is called internally by requestStorage().
+ *
+ * \see AnalysisDataStorage
*/
virtual bool requestStorageInternal(int nframes) = 0;
/*! \brief
* Notifies attached modules of the start of data.
*
+ * \throws APIError if any attached data module is not compatible.
+ * \throws unspecified Any exception thrown by attached data modules
+ * in AnalysisDataModuleInterface::dataStarted().
+ *
* Should be called once, after data properties have been set with
* setColumnCount() and isMultipoint(), and before any of the
- * notification functions. The derived class should prepare for
+ * notification functions. The derived class should prepare for
* requestStorage() calls from the attached modules.
*/
void notifyDataStart();
/*! \brief
* Notifies attached modules of the start of a frame.
*
+ * \param[in] header Header information for the frame that is starting.
+ * \throws unspecified Any exception thrown by attached data modules
+ * in AnalysisDataModuleInterface::frameStarted().
+ *
* Should be called once for each frame, before notifyPointsAdd() calls
* for that frame.
*/
* Notifies attached modules of the addition of points to the
* current frame.
*
+ * \param[in] points Set of points added (also provides access to
+ * frame-level data).
+ * \throws APIError if any attached data module is not compatible.
+ * \throws unspecified Any exception thrown by attached data modules
+ * in AnalysisDataModuleInterface::pointsAdded().
+ *
* Can be called zero or more times for each frame.
* The caller should ensure that any column occurs at most once in the
* calls, unless the data is multipoint.
/*! \brief
* Notifies attached modules of the end of a frame.
*
+ * \param[in] header Header information for the frame that is ending.
+ * \throws unspecified Any exception thrown by attached data modules
+ * in AnalysisDataModuleInterface::frameFinished().
+ *
* Should be called once for each call of notifyFrameStart(), after any
* notifyPointsAdd() calls for the frame.
+ * \p header should be identical to that used in the corresponding
+ * notifyFrameStart() call.
*/
void notifyFrameFinish(const AnalysisDataFrameHeader &header);
/*! \brief
* Notifies attached modules of the end of data.
*
+ * \throws unspecified Any exception thrown by attached data modules
+ * in AnalysisDataModuleInterface::dataFinished().
+ *
* Should be called once, after all the other notification calls.
*/
void notifyDataFinish() const;
class AnalysisData::Impl
{
public:
- //! Shorthand for a smart pointer to a data handle.
+ //! Smart pointer type to manage a data handle implementation.
typedef gmx_unique_ptr<internal::AnalysisDataHandleImpl>::type
HandlePointer;
//! Shorthand for a list of data handles.
Impl();
~Impl();
- //! Storage implementation
+ //! Storage implementation.
AnalysisDataStorage storage_;
- //! \brief List of handles for this data object.
- //! Caution: AnalysisDataHandle stores a bare pointer to a HandlePointer
+ /*! \brief
+ * List of handles for this data object.
+ *
+ * Note that AnalysisDataHandle objects also contain (raw) pointers
+ * to these objects.
+ */
HandleList handles_;
};
void
-AnalysisDataHandle::setPoint(int col, real y, real dy, bool present)
+AnalysisDataHandle::setPoint(int column, real value, bool bPresent)
{
GMX_RELEASE_ASSERT(impl_ != NULL, "Invalid data handle used");
GMX_RELEASE_ASSERT(impl_->currentFrame_ != NULL,
"setPoint() called without calling startFrame()");
- impl_->currentFrame_->setValue(col, y, dy, present);
+ impl_->currentFrame_->setValue(column, value, bPresent);
}
void
-AnalysisDataHandle::setPoints(int firstcol, int n, const real *y)
+AnalysisDataHandle::setPoint(int column, real value, real error, bool bPresent)
+{
+ GMX_RELEASE_ASSERT(impl_ != NULL, "Invalid data handle used");
+ GMX_RELEASE_ASSERT(impl_->currentFrame_ != NULL,
+ "setPoint() called without calling startFrame()");
+ impl_->currentFrame_->setValue(column, value, error, bPresent);
+}
+
+
+void
+AnalysisDataHandle::setPoints(int firstColumn, int count, const real *values)
{
GMX_RELEASE_ASSERT(impl_ != NULL, "Invalid data handle used");
GMX_RELEASE_ASSERT(impl_->currentFrame_ != NULL,
"setPoints() called without calling startFrame()");
- for (int i = 0; i < n; ++i)
+ for (int i = 0; i < count; ++i)
{
- impl_->currentFrame_->setValue(firstcol + i, y[i]);
+ impl_->currentFrame_->setValue(firstColumn + i, values[i]);
}
}
*/
/*! \file
* \brief
- * Declares gmx::AnalysisData and related classes.
+ * Declares gmx::AnalysisData and gmx::AnalysisDataHandle.
*
* \author Teemu Murtola <teemu.murtola@cbr.su.se>
* \inpublicapi
/*! \brief
* Parallelizable data container for raw data.
*
+ * This is the main class used to implement parallelizable data processing in
+ * analysis tools. It is used by first creating an object and setting its
+ * properties using setColumnCount() and setMultipoint(), and attaching
+ * necessary modules using addModule() etc. Then one or more
+ * AnalysisDataHandle objects can be created using startData(). Each data
+ * handle can then be independently used to provide data frames (each frame
+ * must be provided by a single handle, but different frames can be freely
+ * mixed between the handles). When all data has been provided, the handles
+ * are destroyed using finishData() (or AnalysisDataHandle::finishData()).
+ * The AnalysisData object takes care of internally sorting the frames and
+ * passing them to the attached modules in the order in which the modules
+ * expect them.
+ *
+ * \todo
+ * Currently, multiple handles with multipoint data are not implemented.
+ *
+ * \todo
+ * Parallel implementation is not complete.
+ *
* \if internal
* Special note for MPI implementation: assuming that the initialization of
* data objects is identical in all processes, associating the data objects
* interface.
* 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.
+ * provide it. With the current registration mechanism in
+ * TrajectoryAnalysisModule, this should be straightforward.
* \endif
*
* \inpublicapi
class AnalysisData : public AbstractAnalysisData
{
public:
- //! Creates an empty analysis data object.
+ /*! \brief
+ * Creates an empty analysis data object.
+ *
+ * \throws std::bad_alloc if out of memory.
+ */
AnalysisData();
virtual ~AnalysisData();
/*! \brief
* Sets the number of columns in the data.
+ *
+ * \param[in] ncol Number of columns in the data (must be > 0).
+ *
+ * Must be called before startData(), and can be called multiple times
+ * before modules are added.
+ * Must not be called after startData() has been called.
+ *
+ * Does not currently throw, but this may change for the case that
+ * modules have already been added.
*/
void setColumnCount(int ncol);
/*! \brief
* Sets whether the data contains multiple points per column per frame.
*
+ * \param[in] bMultipoint Whether the data will allow multiple points
+ * per column within a single frame.
+ *
+ * If this method is not called, the data is not multipoint.
+ *
+ * Must not be called after modules have been added or startData() has
+ * been called.
+ *
+ * Does not currently throw, but this may change for the case that
+ * modules have already been added.
+ *
* \see isMultipoint()
*/
void setMultipoint(bool bMultipoint);
* \param[in] opt Options for setting how this handle will be
* used.
* \returns The created handle.
+ * \throws std::bad_alloc if out of memory.
+ * \throws APIError if any attached data module is not compatible.
+ * \throws unspecified Any exception thrown by attached data modules
+ * in AnalysisDataModuleInterface::dataStarted().
*
* The caller should retain the returned handle (or a copy of it), and
* pass it to finishData() after successfully adding all data.
* The caller should discard the returned handle if an error occurs;
* memory allocated for the handle will be freed when the AnalysisData
* object is destroyed.
+ *
+ * The \p opt options should be the same for all calls to this method,
+ * and the number of calls should match the parallelization factor
+ * defined in \p opt.
*/
AnalysisDataHandle startData(const AnalysisDataParallelOptions &opt);
/*! \brief
* Destroy a handle after all data has been added.
*
* \param[in] handle Handle to destroy.
+ * \throws unspecified Any exception thrown by attached data modules
+ * in AnalysisDataModuleInterface::dataFinished().
+ *
+ * \p handle must have been obtained from startData() of this object.
+ * The order of the calls with respect to the corresponding startData()
+ * calls is not important.
*
* The \p handle (and any copies) are invalid after the call.
*/
/*! \brief
* Handle for inserting data into AnalysisData.
*
+ * This class provides an interface for adding data frames into an AnalysisData
+ * object. After a handle is obtained from AnalysisData::startData(), new
+ * frames can be added using startFrame(). Then values for that frame are set
+ * using provided methods (see below), and finishFrame() is called. After all
+ * frames have been added, finishData() (or AnalysisData::finishData()) must be
+ * called.
+ *
+ * For simple (non-multipoint) data, within a frame values can be set using
+ * setPoint() and setPoints(). Setting the same column multiple times
+ * overrides previously set values. When the frame is finished, attached
+ * modules are notified.
+ *
+ * Multipoint data works otherwise similarly, but requires finishPointSet() to
+ * be called for each set of points for which the modules need to be notified.
+ * Each point set starts empty (after startFrame() or finishPointSet()), and
+ * values can be set using setPoint()/setPoints(). finishPointSet() must also
+ * be called for the last point set just before finishFrame().
+ *
* This class works like a pointer type: copying and assignment is lightweight,
* and all copies work interchangeably, accessing the same internal handle.
- *
- * Several handles can exist concurrently.
+ * However, normally you should only keep one copy of a handle, i.e., treat
+ * this type as movable.
+ * Several handles created from the same AnalysisData object can exist
+ * concurrently, but must currently operate on separate frames.
*
* \inpublicapi
* \ingroup module_analysisdata
* assigning a value to it. Any attempt to call methods without first
* assigning a value from AnalysisData::startData() to the handle
* causes an assert.
+ *
+ * Does not throw.
*/
AnalysisDataHandle();
- //! Start data for a new frame.
+ /*! \brief
+ * Start data for a new frame.
+ *
+ * \param[in] index Zero-based index for the frame to start.
+ * \param[in] x x value for the frame.
+ * \param[in] dx Error in x for the frame if applicable.
+ *
+ * \throws unspecified Any exception thrown by attached data
+ * modules in AnalysisDataModuleInterface::frameStarted().
+ *
+ * Each \p index value 0, 1, ..., N (where N is the total number of
+ * frames) should be started exactly once by exactly one handle of an
+ * AnalysisData object. The frames may be started out of order, but
+ * currently the implementation places some limitations on how far
+ * the index can be in the future (as counted from the first frame that
+ * is not finished).
+ */
void startFrame(int index, real x, real dx = 0.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.
+ /*! \brief
+ * Set a value for a single column for the current frame.
+ *
+ * \param[in] column Zero-based column index.
+ * \param[in] value Value to set for the column.
+ * \param[in] bPresent 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 setPoint(int column, real value, bool bPresent = true);
+ /*! \brief
+ * Set a value and its error estimate for a single column for the
+ * current frame.
+ *
+ * \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] bPresent 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 setPoint(int column, real value, real error, bool bPresent = true);
+ /*! \brief
+ * Set values for consecutive columns for the current frame.
+ *
+ * \param[in] firstColumn Zero-based column index.
+ * \param[in] count Number of columns to set.
+ * \param[in] values Value array of \p column items.
+ *
+ * Equivalent to calling setPoint(firstColumn + i, values[i]) for
+ * i from 0 to count.
+ *
+ * Does not throw.
+ */
+ void setPoints(int firstColumn, int count, const real *values);
+ /*! \brief
+ * Finish data for the current point set.
+ *
+ * \throws APIError if any attached data module is not compatible.
+ * \throws unspecified Any exception thrown by attached data
+ * modules in AnalysisDataModuleInterface::pointsAdded().
+ *
+ * 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.
+ */
void finishPointSet();
- //! Finish data for the current frame.
+ /*! \brief
+ * Finish data for the current frame.
+ *
+ * \throws APIError if any attached data module is not compatible.
+ * \throws unspecified Any exception thrown by attached data
+ * modules in frame notification methods.
+ */
void finishFrame();
//! Calls AnalysisData::finishData() for this handle.
void finishData();
*
* The constructor is private because data handles should only be
* constructed through AnalysisData::startData().
+ *
+ * Does not throw.
*/
explicit AnalysisDataHandle(internal::AnalysisDataHandleImpl *impl);
*/
internal::AnalysisDataHandleImpl *impl_;
+ /*! \brief
+ * Needed to access the non-public implementation.
+ */
friend class AnalysisData;
};
* This class implements a subclass of AbstractAnalysisData that presents an
* in-memory array through the AbstractAnalysisData interface. Subclasses
* should initialize the in-memory array through the provided protected member
- * functions.
+ * functions. This class provides public accessor methods for read access to
+ * the data.
+ *
+ * Public accessor methods in this class do not throw, but assert if data is
+ * accessed before it is available.
*
* \todo
* Add methods to take full advantage of AnalysisDataValue features.
}
protected:
+ /*! \brief
+ * Initializes an empty array data object.
+ *
+ * \throws std::bad_alloc if out of memory.
+ */
AbstractAnalysisArrayData();
/*! \brief
* Sets the number of columns in the data array.
*
+ * \param[in] ncols Number of columns in the data.
+ *
* Cannot be called after allocateValues().
+ *
+ * See AbstractAnalysisData::setColumnCount() for exception behavior.
*/
void setColumnCount(int ncols);
/*! \brief
* Sets the number of rows in the data array.
*
+ * \param[in] nrows Number of rows in the data.
+ *
* Cannot be called after allocateValues().
+ *
+ * Does not throw.
*/
void setRowCount(int nrows);
/*! \brief
* Allocates memory for the values.
*
+ * \throws std::bad_alloc if memory allocation fails.
+ *
* setColumnCount() and setRowCount() must have been called.
+ *
+ * Strong exception safety guarantee.
*/
void allocateValues();
/*! \brief
* Sets the values reported as x values for frames.
+ *
+ * \param[in] start x value for the first frame.
+ * \param[in] step Step between x values of successive frames.
+ *
+ * Must not be called after valuesReady().
+ *
+ * Does not throw.
*/
void setXAxis(real start, real step);
//! Returns a reference to a given array element.
}
/*! \brief
* Sets the value of an element in the array.
+ *
+ * \param[in] row Zero-based row index for the value.
+ * \param[in] col Zero-based column index for the value.
+ * \param[in] val Value to set in the given location.
+ *
+ * Does not throw.
*/
void setValue(int row, int col, real val)
{
/*! \brief
* Notifies modules of the data.
*
+ * \throws unspecified Any exception thrown by attached data modules
+ * in data notification methods.
+ *
* This function should be called once the values in the array
- * have been initialized. The values should not be changed after this
+ * have been initialized. The values should not be changed after this
* function has been called.
*/
void valuesReady();
*
* \param[in] src Object to copy data from.
* \param[in,out] dest Empty array data object to copy data to.
+ * \throws std::bad_alloc if memory allocation for \p dest fails.
*
* \p dest should not have previous contents.
*/
/*! \brief
* Simple in-memory data array.
*
- * This class simply exposes the protected functions of
- * AbstractAnalysisArrayData to allow construction of simple in-memory data
- * arrays for input into data modules.
+ * This class is a simple alternative to AnalysisData for in-memory data arrays
+ * that are constructed in-place.
+ *
+ * Public accessor methods in this class do not throw, but assert if data is
+ * accessed before it is available.
+ *
+ * \if libapi
+ * This class exposes the protected functions of AbstractAnalysisArrayData for
+ * users.
+ * \endif
*
* \inpublicapi
* \ingroup module_analysisdata
class AnalysisArrayData : public AbstractAnalysisArrayData
{
public:
+ /*! \brief
+ * Initializes an empty array data object.
+ *
+ * \throws std::bad_alloc if out of memory.
+ */
AnalysisArrayData() {}
// TODO: These statements cause Doxygen to generate confusing
*
* Methods in this class do not throw.
*
+ * Non-const methods are provided for use within the library only; currently
+ * it is not possible to access a non-const AnalysisDataValue through the
+ * public interface.
+ *
* \inpublicapi
* \ingroup module_analysisdata
*/
* Methods in this class do not throw, but may contain asserts for incorrect
* usage.
*
+ * Note that it is not possible to change the contents of an initialized
+ * object, except by assigning a new object to replace it completely.
+ *
* \inpublicapi
* \ingroup module_analysisdata
*/
* The design of the interfaces is such that all objects of this type should be
* valid, i.e., header().isValid() should always return true.
*
+ * Note that it is not possible to change the contents of an initialized
+ * object, except by assigning a new object to replace it completely.
+ *
* \inpublicapi
* \ingroup module_analysisdata
*/
* Methods in this class do not throw, but may contain asserts for incorrect
* usage.
*
+ * Note that it is not possible to change the contents of an initialized
+ * object, except by assigning a new object to replace it completely.
+ *
* \todo
* Support for multipoint data.
*
* Interface for a module that gets notified whenever data is added.
*
* The interface provides one method (flags()) that describes features of
- * data objects the module supports. Only most common features are included
+ * data objects the module supports. Only most common features are included
* in the flags; custom checks can be implemented in the dataStarted() method
* (see below).
* All other methods in the interface are callbacks that are called by the
* data object to which the module is attached to describe the data.
*
+ * The frames are presented to the module always in the order of increasing
+ * indices, even if they become ready in a different order in the attached
+ * data.
+ *
+ * Currently, if the module throws an exception, it requires the analysis tool
+ * to terminate, since AbstractAnalysisData will be left in a state where it
+ * is not possible to continue processing. See a related todo item in
+ * AbstractAnalysisData.
+ *
* \inlibraryapi
* \ingroup module_analysisdata
*/
* for data compatibility in the classes that implement the interface.
* Instead, AbstractAnalysisData performs these checks based on the
* flags provided.
+ *
+ * Does not throw.
*/
virtual int flags() const = 0;
/*! \brief
* Called (once) when the data has been set up properly.
*
+ * \param[in] data Data object to which the module is added.
+ * \throws APIError if the provided data is not compatible.
+ * \throws unspecified Can throw any exception required by the
+ * implementing class to report errors.
+ *
* The data to which the module is attached is passed as an argument
* to provide access to properties of the data for initialization
- * and/or validation.
+ * and/or validation. The module can also call
+ * AbstractAnalysisData::requestStorage() if needed.
+ *
* This is the only place where the module gets access to the data;
* if properties of the data are required later, the module should
- * store them internally. It is guaranteed that the data properties
+ * store them internally. It is guaranteed that the data properties
* (column count, whether it's multipoint) do not change once this
* method has been called.
+ *
+ * Notice that \p data will be a proxy object if the module is added as
+ * a column module, not the data object for which
+ * AbstractAnalysisData::addColumnModule() was called.
*/
virtual void dataStarted(AbstractAnalysisData *data) = 0;
/*! \brief
* Called at the start of each data frame.
+ *
+ * \param[in] frame Header information for the frame that is starting.
+ * \throws unspecified Can throw any exception required by the
+ * implementing class to report errors.
*/
virtual void frameStarted(const AnalysisDataFrameHeader &frame) = 0;
/*! \brief
* Called one or more times during each data frame.
+ *
+ * \param[in] points Set of points added (also provides access to
+ * frame-level data).
+ * \throws APIError if the provided data is not compatible.
+ * \throws unspecified Can throw any exception required by the
+ * implementing class to report errors.
+ *
+ * Can be called once or multiple times for a frame. For all data
+ * objects currently implemented in the library (and all objects that
+ * will use AnalysisDataStorage for internal implementation), it is
+ * called exactly once for each frame if the data is not multipoint,
+ * but currently this restriction is not enforced.
*/
virtual void pointsAdded(const AnalysisDataPointSetRef &points) = 0;
/*! \brief
* Called when a data frame is finished.
+ *
+ * \param[in] header Header information for the frame that is ending.
+ * \throws unspecified Can throw any exception required by the
+ * implementing class to report errors.
*/
virtual void frameFinished(const AnalysisDataFrameHeader &header) = 0;
/*! \brief
* Called (once) when no more data is available.
+ *
+ * \throws unspecified Can throw any exception required by the
+ * implementing class to report errors.
*/
virtual void dataFinished() = 0;
* \brief
* Declares gmx::AnalysisDataProxy.
*
- * This header is only meant for internal use of the gmx::AbstractAnalysisData
- * class to implement modules that handle only a subset of columns.
+ * This header is only meant for internal use to implement
+ * gmx::AbstractAnalysisData::setColumnModule().
*
* \author Teemu Murtola <teemu.murtola@cbr.su.se>
* \ingroup module_analysisdata
/*! \internal \brief
* Internal implementation class used to implement column modules.
*
+ * This class serves as a proxy between AbstractAnalysisData and the attached
+ * AnalysisDataModuleInterface object. For each notification that
+ * AbstractAnalysisData sends, it maps it such that only the relevant columns
+ * are visible to the AnalysisDataModuleInterface. Similarly, it implements
+ * the frame access methods of AbstractAnalysisData such that only the relevant
+ * columns are returned.
+ *
* \ingroup module_analysisdata
*/
class AnalysisDataProxy : public AbstractAnalysisData,
* \param[in] col First column to present.
* \param[in] span Number of columns to present.
* \param[in] data Data object that should be wrapped.
+ *
+ * Does not throw.
*/
AnalysisDataProxy(int col, int span, AbstractAnalysisData *data);
/*! \brief
* Stored information about a single stored frame.
+ *
+ * Methods in this class do not throw.
*/
struct StoredFrame
{
*
* \param[in] index Zero-based frame index.
* \retval -1 if \p index is not available in \a frames_.
+ *
+ * Does not throw.
*/
int computeStorageLocation(int index) const;
/*! \brief
* Computes an index into \a frames_ that is one past the last frame
* stored.
+ *
+ * Does not throw.
*/
size_t endStorageLocation() const;
/*! \brief
* Extends \a frames_ to a new size.
+ *
+ * \throws std::bad_alloc if out of memory.
*/
void extendBuffer(AnalysisDataStorage *storage, size_t newSize);
/*! \brief
* Increments \a firstFrameLocation_ and reinitializes the frame that
* was made unavailable by this operation.
*
+ * Does not throw.
+ *
* \see frames_
*/
void rotateBuffer();
/*! \brief
* Calls notification method in \a data_.
+ *
+ * \throws unspecified Any exception thrown by
+ * AbstractAnalysisData::notifyPointsAdd().
*/
void notifyPointSet(const AnalysisDataPointSetRef &points);
/*! \brief
* Calls notification methods for new frames.
*
* \param[in] firstLocation First frame to consider.
+ * \throws unspecified Any exception thrown by frame notification
+ * methods in AbstractAnalysisData.
*
* Notifies \a data_ of new frames (from \p firstLocation and after
* that) if all previous frames have already been notified.
frames_.reserve(newSize);
while (frames_.size() < newSize)
{
- AnalysisDataStorageFrame *frame =
- new AnalysisDataStorageFrame(storage, columnCount(), nextIndex_);
- frames_.push_back(StoredFrame(frame));
+ frames_.push_back(StoredFrame(
+ new AnalysisDataStorageFrame(storage, columnCount(), nextIndex_)));
++nextIndex_;
}
// The unused frame should not be included in the count.
* \see histogramFromBins()
* \see histogramFromRange()
*
+ * Methods in this class do not throw.
+ *
* \inpublicapi
* \ingroup module_analysisdata
*/
/*! \brief
* Initializes a histogram using a range and a bin width.
*
+ * Does not throw.
+ *
* \inpublicapi
*/
inline AnalysisHistogramSettingsInitializer
/*! \brief
* Initializes a histogram using bin width and the number of bins.
*
+ * Does not throw.
+ *
* \inpublicapi
*/
inline AnalysisHistogramSettingsInitializer
/*! \brief
* Contains parameters that specify histogram bin locations.
*
+ * Methods in this class do not throw.
+ *
* \inpublicapi
* \ingroup module_analysisdata
*/
* Base class for representing histograms averaged over frames.
*
* The averaging module for a per-frame histogram is always created by the
- * AbstractHistogramModule class, and can be accessed using
- * AbstractHistogramModule::averager().
+ * histogram module class (e.g., AnalysisDataSimpleHistogramModule), and can be
+ * accessed using, e.g., AnalysisDataSimpleHistogramModule::averager().
* The user can alter some properties of the average histogram directly, but
* the main use of the object is to postprocess the histogram once the
* calculation is finished.
/*! \brief
* Creates a copy of the histogram with double the bin width.
*
+ * \throws std::bad_alloc if out of memory.
+ *
* The caller is responsible of deleting the returned object.
*/
AverageHistogramPointer resampleDoubleBinWidth(bool bIntegerBins) const;
/*! \brief
* Creates a deep copy of the histogram.
*
+ * \throws std::bad_alloc if out of memory.
+ *
* The returned histogram is not necessarily of the same dynamic type
* as the original object, but contains the same data from the point of
* view of the AbstractAverageHistogram interface.
namespace gmx
{
-/*! \brief
+/*! \libinternal \brief
* Parallelization options for analysis data objects.
*
* Methods in this class do not throw.
*
* \inlibraryapi
+ * \ingroup module_analysisdata
*/
class AnalysisDataParallelOptions
{