Merge "Merge branch release-4-6 into master"
[alexxy/gromacs.git] / src / gromacs / analysisdata / abstractdata.h
1 /*
2  * This file is part of the GROMACS molecular simulation package.
3  *
4  * Copyright (c) 2010,2011,2012,2013, by the GROMACS development team, led by
5  * David van der Spoel, Berk Hess, Erik Lindahl, and including many
6  * others, as listed in the AUTHORS file in the top-level source
7  * directory and at http://www.gromacs.org.
8  *
9  * GROMACS is free software; you can redistribute it and/or
10  * modify it under the terms of the GNU Lesser General Public License
11  * as published by the Free Software Foundation; either version 2.1
12  * of the License, or (at your option) any later version.
13  *
14  * GROMACS is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17  * Lesser General Public License for more details.
18  *
19  * You should have received a copy of the GNU Lesser General Public
20  * License along with GROMACS; if not, see
21  * http://www.gnu.org/licenses, or write to the Free Software Foundation,
22  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
23  *
24  * If you want to redistribute modifications to GROMACS, please
25  * consider that scientific software is very special. Version
26  * control is crucial - bugs must be traceable. We will be happy to
27  * consider code for inclusion in the official distribution, but
28  * derived work must not be called official GROMACS. Details are found
29  * in the README & COPYING files - if they are missing, get the
30  * official version at http://www.gromacs.org.
31  *
32  * To help us fund GROMACS development, we humbly ask that you cite
33  * the research papers on the package. Check out http://www.gromacs.org.
34  */
35 /*! \file
36  * \brief
37  * Declares gmx::AbstractAnalysisData.
38  *
39  * \author Teemu Murtola <teemu.murtola@gmail.com>
40  * \inlibraryapi
41  * \ingroup module_analysisdata
42  */
43 #ifndef GMX_ANALYSISDATA_ABSTRACTDATA_H
44 #define GMX_ANALYSISDATA_ABSTRACTDATA_H
45
46 #include <boost/shared_ptr.hpp>
47
48 #include "../legacyheaders/types/simple.h"
49
50 #include "../utility/common.h"
51
52 namespace gmx
53 {
54
55 class AnalysisDataModuleInterface;
56 class AnalysisDataFrameHeader;
57 class AnalysisDataFrameRef;
58 class AnalysisDataPointSetRef;
59 class AnalysisDataStorage;
60
61 //! Smart pointer for managing a generic analysis data module.
62 typedef boost::shared_ptr<AnalysisDataModuleInterface> AnalysisDataModulePointer;
63
64 /*! \brief
65  * Abstract base class for all objects that provide data.
66  *
67  * The public interface includes methods for querying the data (isMultipoint(),
68  * dataSetCount(), columnCount(), frameCount(), tryGetDataFrame(),
69  * getDataFrame(), requestStorage()) and methods for using modules for
70  * processing the data (addModule(), addColumnModule(), applyModule()).
71  *
72  * Notice that even for non-const objects, the interface does not provide any
73  * means of altering the data.  It is only possible to add modules, making it
74  * relatively safe to return a non-const pointer of this type pointing to an
75  * internal data structure without worrying about possible modifications of the
76  * data.
77  *
78  * \if libapi
79  * This class also provides protected methods for use in derived classes.
80  * The properties returned by isMultipoint(), dataSetCount(), and columnCount()
81  * must be set using setMultipoint(), setDataSetCount(), and setColumnCount(),
82  * and notify*() methods must be used to report when data becomes available for
83  * modules to process it.
84  * There are also two protected pure virtual methods that need to be
85  * implemented to provide access to stored data: requestStorageInternal() and
86  * tryGetDataFrameInternal().
87  *
88  * It is up to subclasses to ensure that the protected methods are called in a
89  * correct sequence (the methods will assert in most incorrect use cases), and
90  * that the data provided through the public interface matches that passed to
91  * the modules with the notify methods.
92  * Helper class AnalysisDataStorage provides a default implementation for
93  * storing data (calls to the pure virtual methods can simply be forwarded to
94  * appropriate methods in the helper class), and takes care of correctly
95  * calling the notification methods when new data is added to the storage.
96  * In most cases, it should be used to implement the derived classes.
97  * \endif
98  *
99  * Currently, it is not possible to continue using the data object if an
100  * attached module throws an exception during data processing; it is only safe
101  * to destroy such data object.
102  *
103  * \todo
104  * Improve the exception-handling semantics.  In most cases, it doesn't make
105  * much sense to continue data processing after one module fails, but having
106  * the alternative would not hurt.
107  *
108  * \inlibraryapi
109  * \ingroup module_analysisdata
110  */
111 class AbstractAnalysisData
112 {
113     public:
114         virtual ~AbstractAnalysisData();
115
116         /*! \brief
117          * Whether the data can have multiple points in the same column
118          * in the same frame.
119          *
120          * \returns \c true if multiple points in the same column are
121          *     allowed within a single frame.
122          *
123          * This kind of data can appear in many histogramming applications
124          * (e.g., RDFs), where each trajectory frame has several data points
125          * (possibly a different number for each frame). The current interface
126          * doesn't support storing such data, but this should rarely be
127          * necessary.
128          *
129          * The returned value does not change after modules have been notified
130          * of data start.
131          * \if libapi
132          * Derived classes can change the type by calling setMultipoint()
133          * subject to the above restriction.
134          * If this is not done, the function always returns false.
135          * \endif
136          *
137          * Does not throw.
138          */
139         bool isMultipoint() const;
140         /*! \brief
141          * Returns the number of data sets in the data object.
142          *
143          * \returns The number of data sets in the data.
144          *
145          * If the number is not yet known, returns 0.
146          * The returned value does not change after modules have been notified
147          * of data start, but may change multiple times before that, depending
148          * on the actual data class.
149          * \if libapi
150          * Derived classes should set the number of columns with
151          * setDataSetCount(), within the above limitations.
152          * \endif
153          *
154          * Does not throw.
155          */
156         int dataSetCount() const;
157         /*! \brief
158          * Returns the number of columns in a data set.
159          *
160          * \param[in] dataSet Zero-based index of the data set to query.
161          * \returns The number of columns in the data.
162          *
163          * If the number of columns is not yet known, returns 0.
164          * The returned value does not change after modules have been notified
165          * of data start, but may change multiple times before that, depending
166          * on the actual data class.
167          * \if libapi
168          * Derived classes should set the number of columns with
169          * setColumnCount(), within the above limitations.
170          * \endif
171          *
172          * Does not throw.
173          */
174         int columnCount(int dataSet) const;
175         /*! \brief
176          * Returns the number of columns in the data.
177          *
178          * \returns The number of columns in the data.
179          *
180          * This is a convenience method for data objects with a single data set.
181          * Can only be called if dataSetCount() == 1.
182          *
183          * Does not throw.
184          *
185          * \see columnCount(int)
186          */
187         int columnCount() const;
188         /*! \brief
189          * Returns the total number of frames in the data.
190          *
191          * \returns The total number of frames in the data.
192          *
193          * This function returns the number of frames that the object has
194          * produced.  If requestStorage() has been successfully called,
195          * tryGetDataframe() or getDataFrame() can be used to access some or
196          * all of these frames.
197          *
198          * Does not throw.
199          */
200         int frameCount() const;
201         /*! \brief
202          * Access stored data.
203          *
204          * \param[in] index  Zero-based frame index to access.
205          * \returns   Frame reference to frame \p index, or an invalid
206          *      reference if no such frame is available.
207          *
208          * Does not throw.  Failure to access a frame with the given index is
209          * indicated through the return value.  Negative \p index is allowed,
210          * and will always result in an invalid reference being returned.
211          *
212          * \see requestStorage()
213          * \see getDataFrame()
214          */
215         AnalysisDataFrameRef tryGetDataFrame(int index) const;
216         /*! \brief
217          * Access stored data.
218          *
219          * \param[in] index  Zero-based frame index to access.
220          * \returns   Frame reference to frame \p index.
221          * \throws    APIError if the requested frame is not accessible.
222          *
223          * If the data is not certainly available, use tryGetDataFrame().
224          *
225          * \see requestStorage()
226          * \see tryGetDataFrame()
227          */
228         AnalysisDataFrameRef getDataFrame(int index) const;
229         /*! \brief
230          * Request storage of frames.
231          *
232          * \param[in] nframes  Request storing at least \c nframes previous
233          *     frames (-1 = request storing all). Must be >= -1.
234          * \returns true if the request could be satisfied.
235          *
236          * If called multiple times, the largest request is honored.
237          *
238          * Does not throw.  Failure to honor the request is indicated through
239          * the return value.
240          *
241          * \see getDataFrame()
242          * \see tryGetDataFrame()
243          */
244         bool requestStorage(int nframes);
245
246         /*! \brief
247          * Adds a module to process the data.
248          *
249          * \param     module  Module to add.
250          * \throws    APIError if
251          *      - \p module is not compatible with the data object
252          *      - data has already been added to the data object and everything
253          *        is not available through getDataFrame().
254          *
255          * If data has already been added to the module, the new module
256          * immediately processes all existing data.  APIError is thrown
257          * if all data is not available through getDataFrame().
258          *
259          * The caller can keep a copy of the module pointer if it requires
260          * later access to the module.
261          *
262          * If the method throws, the state of the data object is not changed.
263          * The state of the data module is indeterminate.
264          */
265         void addModule(AnalysisDataModulePointer module);
266         /*! \brief
267          * Adds a module that processes only a subset of the columns.
268          *
269          * \param[in] col     First column.
270          * \param[in] span    Number of columns.
271          * \param     module  Module to add.
272          * \throws    APIError in same situations as addModule().
273          *
274          * Currently, all data sets are filtered using the same column mask.
275          *
276          * \todo
277          * This method doesn't currently work in all cases with multipoint
278          * data or with multiple data sets.  In particular, if the added module
279          * requests storage and uses getDataFrame(), it will behave
280          * unpredictably (most likely asserts).
281          *
282          * \todo
283          * Generalize this method to multiple data sets (e.g., for adding
284          * modules that only process a single data set).
285          *
286          * \see addModule()
287          */
288         void addColumnModule(int col, int span, AnalysisDataModulePointer module);
289         /*! \brief
290          * Applies a module to process data that is ready.
291          *
292          * \param     module  Module to apply.
293          * \throws    APIError in same situations as addModule().
294          *
295          * This function works as addModule(), except that it does not keep a
296          * reference to \p module within the data object after it returns.
297          * Also, it can only be called after the data is ready, and only if
298          * getDataFrame() gives access to all of the data.
299          * It is provided for additional flexibility in postprocessing
300          * in-memory data.
301          *
302          * \todo
303          * Currently, this method may not work correctly if \p module requests
304          * storage (addModule() has the same problem if called after data is
305          * started).
306          */
307         void applyModule(AnalysisDataModuleInterface *module);
308
309     protected:
310         /*! \cond libapi */
311         /*! \brief
312          * Initializes a new analysis data object.
313          *
314          * \throws std::bad_alloc if out of memory.
315          */
316         AbstractAnalysisData();
317
318         /*! \brief
319          * Sets the number of data sets.
320          *
321          * \param[in] dataSetCount  Number of data sets (must be > 0).
322          *
323          * It not called, the data object has a single data set.
324          * Can be called only before notifyDataStart().
325          * Multiple calls are allowed before that point; the last call takes
326          * effect.
327          *
328          * Does not throw, but this may change with the todo item in
329          * setColumnCount().
330          *
331          * \see dataSetCount()
332          */
333         void setDataSetCount(int dataSetCount);
334         /*! \brief
335          * Sets the number of columns for a data set.
336          *
337          * \param[in] dataSet      Zero-based index of the data set.
338          * \param[in] columnCount  Number of columns in \p dataSet (must be > 0).
339          *
340          * Must be called at least once before notifyDataStart() for each data
341          * set.
342          * Can be called only before notifyDataStart().
343          * Multiple calls are allowed before that point; the last call takes
344          * effect.
345          *
346          * Does not throw, but this may change with the below todo item.
347          *
348          * \todo
349          * Consider whether the call should check the modules that have already
350          * been added (currently it is only done in notifyDataStart()).
351          *
352          * \see columnCount()
353          */
354         void setColumnCount(int dataSet, int columnCount);
355         /*! \brief
356          * Sets whether the data has multiple points per column in a frame.
357          *
358          * \param[in] multipoint  Whether multiple points per column are
359          *     possible.
360          *
361          * If not called, only a single point per column is allowed.
362          * Can be called only before notifyDataStart().
363          * Multiple calls are allowed before that point; the last call takes
364          * effect.
365          *
366          * Does not throw, but this may change with the todo item in
367          * setColumnCount().
368          *
369          * \see isMultipoint()
370          */
371         void setMultipoint(bool multipoint);
372
373         /*! \brief
374          * Implements access to data frames.
375          *
376          * \param[in] index  Zero-based frame index to access.
377          * \returns   Frame reference to frame \p index, or an invalid
378          *      reference if no such frame is available.
379          *
380          * Must not throw.  Failure to access a frame with the given index is
381          * indicated through the return value.
382          *
383          * Code in derived classes can assume that \p index is non-negative and
384          * less than frameCount().
385          *
386          * Derived classes can choose to return an invalid reference if
387          * requestStorageInternal() has not been called at all, or if the frame
388          * is too old (compared to the value given to requestStorageInternal()).
389          *
390          * This method is called internally by tryGetDataFrame() and
391          * getDataFrame().
392          *
393          * \see AnalysisDataStorage
394          */
395         virtual AnalysisDataFrameRef tryGetDataFrameInternal(int index) const = 0;
396         /*! \brief
397          * Implements storage requests.
398          *
399          * \param[in] nframes  Request storing at least \c nframes previous
400          *     frames (-1 = request storing all). Will be either -1 or >0.
401          * \returns   true if the request could be satisfied.
402          *
403          * Must not throw.  Failure to access a frame with the given index is
404          * indicated through the return value.
405          *
406          * Derived classes should be prepared for any number of calls to this
407          * method before notifyDataStart() is called (and during that call).
408          *
409          * This method is called internally by requestStorage().
410          *
411          * \see AnalysisDataStorage
412          */
413         virtual bool requestStorageInternal(int nframes) = 0;
414
415         /*! \brief
416          * Notifies attached modules of the start of data.
417          *
418          * \throws    APIError if any attached data module is not compatible.
419          * \throws    unspecified Any exception thrown by attached data modules
420          *      in AnalysisDataModuleInterface::dataStarted().
421          *
422          * Should be called once, after data properties have been set with
423          * setColumnCount() and isMultipoint(), and before any of the
424          * notification functions.  The derived class should prepare for
425          * requestStorage() calls from the attached modules.
426          */
427         void notifyDataStart();
428         /*! \brief
429          * Notifies attached modules of the start of a frame.
430          *
431          * \param[in] header  Header information for the frame that is starting.
432          * \throws    unspecified Any exception thrown by attached data modules
433          *      in AnalysisDataModuleInterface::frameStarted().
434          *
435          * Should be called once for each frame, before notifyPointsAdd() calls
436          * for that frame.
437          */
438         void notifyFrameStart(const AnalysisDataFrameHeader &header) const;
439         /*! \brief
440          * Notifies attached modules of the addition of points to the
441          * current frame.
442          *
443          * \param[in] points  Set of points added (also provides access to
444          *      frame-level data).
445          * \throws    APIError if any attached data module is not compatible.
446          * \throws    unspecified Any exception thrown by attached data modules
447          *      in AnalysisDataModuleInterface::pointsAdded().
448          *
449          * Can be called zero or more times for each frame.
450          * The caller should ensure that any column occurs at most once in the
451          * calls, unless the data is multipoint.
452          * For efficiency reasons, calls to this method should be aggregated
453          * whenever possible, i.e., it's better to handle multiple columns or
454          * even the whole frame in a single call rather than calling the method
455          * for each column separately.
456          */
457         void notifyPointsAdd(const AnalysisDataPointSetRef &points) const;
458         /*! \brief
459          * Notifies attached modules of the end of a frame.
460          *
461          * \param[in] header  Header information for the frame that is ending.
462          * \throws    unspecified Any exception thrown by attached data modules
463          *      in AnalysisDataModuleInterface::frameFinished().
464          *
465          * Should be called once for each call of notifyFrameStart(), after any
466          * notifyPointsAdd() calls for the frame.
467          * \p header should be identical to that used in the corresponding
468          * notifyFrameStart() call.
469          */
470         void notifyFrameFinish(const AnalysisDataFrameHeader &header);
471         /*! \brief
472          * Notifies attached modules of the end of data.
473          *
474          * \throws    unspecified Any exception thrown by attached data modules
475          *      in AnalysisDataModuleInterface::dataFinished().
476          *
477          * Should be called once, after all the other notification calls.
478          */
479         void notifyDataFinish() const;
480         //! \endcond
481
482     private:
483         class Impl;
484
485         PrivateImplPointer<Impl> impl_;
486
487         /*! \brief
488          * Needed to provide access to notification methods.
489          */
490         friend class AnalysisDataStorage;
491 };
492
493 } // namespace gmx
494
495 #endif