Merge 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  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
6  * and including many others, as listed in the AUTHORS file in the
7  * top-level source 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 AnalysisDataModuleManager;
57 class AnalysisDataFrameHeader;
58 class AnalysisDataFrameRef;
59 class AnalysisDataPointSetRef;
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  * notify*() methods in the AnalysisDataModuleManager returned by
83  * moduleManager() must be used to report when data becomes available for
84  * modules to process it.
85  * There are also three pure virtual methods that need to be implemented to
86  * provide access to stored data: one public (frameCount()) and two protected
87  * ones (requestStorageInternal() and tryGetDataFrameInternal()).
88  *
89  * It is up to subclasses to ensure that the virtual methods and the
90  * notifications in AnalysisDataModuleManager are called in a
91  * correct sequence (the methods will assert in most incorrect use cases), and
92  * that the data provided through the public interface matches that passed to
93  * the modules with the notify methods.
94  * Helper class AnalysisDataStorage provides a default implementation for
95  * storing data (calls to the pure virtual methods can simply be forwarded to
96  * appropriate methods in the helper class), and takes care of correctly
97  * calling the notification methods when new data is added to the storage.
98  * In most cases, it should be used to implement the derived classes.
99  * \endif
100  *
101  * Currently, it is not possible to continue using the data object if an
102  * attached module throws an exception during data processing; it is only safe
103  * to destroy such data object.
104  *
105  * \todo
106  * Improve the exception-handling semantics.  In most cases, it doesn't make
107  * much sense to continue data processing after one module fails, but having
108  * the alternative would not hurt.
109  *
110  * \inlibraryapi
111  * \ingroup module_analysisdata
112  */
113 class AbstractAnalysisData
114 {
115     public:
116         virtual ~AbstractAnalysisData();
117
118         /*! \brief
119          * Whether the data can have multiple points in the same column
120          * in the same frame.
121          *
122          * \returns \c true if multiple points in the same column are
123          *     allowed within a single frame.
124          *
125          * This kind of data can appear in many histogramming applications
126          * (e.g., RDFs), where each trajectory frame has several data points
127          * (possibly a different number for each frame). The current interface
128          * doesn't support storing such data, but this should rarely be
129          * necessary.
130          *
131          * The returned value does not change after modules have been notified
132          * of data start.
133          * \if libapi
134          * Derived classes can change the type by calling setMultipoint()
135          * subject to the above restriction.
136          * If this is not done, the function always returns false.
137          * \endif
138          *
139          * Does not throw.
140          */
141         bool isMultipoint() const;
142         /*! \brief
143          * Returns the number of data sets in the data object.
144          *
145          * \returns The number of data sets in the data.
146          *
147          * If the number is not yet known, returns 0.
148          * The returned value does not change after modules have been notified
149          * of data start, but may change multiple times before that, depending
150          * on the actual data class.
151          * \if libapi
152          * Derived classes should set the number of columns with
153          * setDataSetCount(), within the above limitations.
154          * \endif
155          *
156          * Does not throw.
157          */
158         int dataSetCount() const;
159         /*! \brief
160          * Returns the number of columns in a data set.
161          *
162          * \param[in] dataSet Zero-based index of the data set to query.
163          * \returns The number of columns in the data.
164          *
165          * If the number of columns is not yet known, returns 0.
166          * The returned value does not change after modules have been notified
167          * of data start, but may change multiple times before that, depending
168          * on the actual data class.
169          * \if libapi
170          * Derived classes should set the number of columns with
171          * setColumnCount(), within the above limitations.
172          * \endif
173          *
174          * Does not throw.
175          */
176         int columnCount(int dataSet) const;
177         /*! \brief
178          * Returns the number of columns in the data.
179          *
180          * \returns The number of columns in the data.
181          *
182          * This is a convenience method for data objects with a single data set.
183          * Can only be called if dataSetCount() == 1.
184          *
185          * Does not throw.
186          *
187          * \see columnCount(int)
188          */
189         int columnCount() const;
190         /*! \brief
191          * Returns the total number of frames in the data.
192          *
193          * \returns The total number of frames in the data.
194          *
195          * This function returns the number of frames that the object has
196          * produced.  If requestStorage() has been successfully called,
197          * tryGetDataframe() or getDataFrame() can be used to access some or
198          * all of these frames.
199          *
200          * Does not throw.
201          *
202          * \if libapi
203          * Derived classes should implement this to return the number of
204          * frames.  The frame count should not be incremented before
205          * tryGetDataFrameInternal() can return the new frame.
206          * The frame count must be incremented before
207          * AnalysisDataModuleManager::notifyFrameFinish() is called.
208          * \endif
209          */
210         virtual int frameCount() const = 0;
211         /*! \brief
212          * Access stored data.
213          *
214          * \param[in] index  Zero-based frame index to access.
215          * \returns   Frame reference to frame \p index, or an invalid
216          *      reference if no such frame is available.
217          *
218          * Does not throw.  Failure to access a frame with the given index is
219          * indicated through the return value.  Negative \p index is allowed,
220          * and will always result in an invalid reference being returned.
221          *
222          * \see requestStorage()
223          * \see getDataFrame()
224          */
225         AnalysisDataFrameRef tryGetDataFrame(int index) const;
226         /*! \brief
227          * Access stored data.
228          *
229          * \param[in] index  Zero-based frame index to access.
230          * \returns   Frame reference to frame \p index.
231          * \throws    APIError if the requested frame is not accessible.
232          *
233          * If the data is not certainly available, use tryGetDataFrame().
234          *
235          * \see requestStorage()
236          * \see tryGetDataFrame()
237          */
238         AnalysisDataFrameRef getDataFrame(int index) const;
239         /*! \brief
240          * Request storage of frames.
241          *
242          * \param[in] nframes  Request storing at least \c nframes previous
243          *     frames (-1 = request storing all). Must be >= -1.
244          * \returns true if the request could be satisfied.
245          *
246          * If called multiple times, the largest request is honored.
247          *
248          * Does not throw.  Failure to honor the request is indicated through
249          * the return value.
250          *
251          * \see getDataFrame()
252          * \see tryGetDataFrame()
253          */
254         bool requestStorage(int nframes);
255
256         /*! \brief
257          * Adds a module to process the data.
258          *
259          * \param     module  Module to add.
260          * \throws    std::bad_alloc if out of memory.
261          * \throws    APIError if
262          *      - \p module is not compatible with the data object
263          *      - data has already been added to the data object and everything
264          *        is not available through getDataFrame().
265          * \throws    unspecified Any exception thrown by \p module in its
266          *      notification methods (if data has been added).
267          *
268          * If data has already been added to the data, the new module
269          * immediately processes all existing data.  APIError is thrown
270          * if all data is not available through getDataFrame().
271          *
272          * The caller can keep a copy of the module pointer if it requires
273          * later access to the module.
274          *
275          * If the method throws, the state of the data object is not changed.
276          * The state of the data module is indeterminate.
277          */
278         void addModule(AnalysisDataModulePointer module);
279         /*! \brief
280          * Adds a module that processes only a subset of the columns.
281          *
282          * \param[in] col     First column.
283          * \param[in] span    Number of columns.
284          * \param     module  Module to add.
285          *
286          * Throws in the same situations as addModule().
287          *
288          * Currently, all data sets are filtered using the same column mask.
289          *
290          * \todo
291          * This method doesn't currently work in all cases with multipoint
292          * data or with multiple data sets.  In particular, if the added module
293          * requests storage and uses getDataFrame(), it will behave
294          * unpredictably (most likely asserts).
295          *
296          * \todo
297          * Generalize this method to multiple data sets (e.g., for adding
298          * modules that only process a single data set).
299          *
300          * \see addModule()
301          */
302         void addColumnModule(int col, int span, AnalysisDataModulePointer module);
303         /*! \brief
304          * Applies a module to process data that is ready.
305          *
306          * \param     module  Module to apply.
307          * \throws    APIError in same situations as addModule().
308          * \throws    unspecified Any exception thrown by \p module in its
309          *      notification methods.
310          *
311          * This function works as addModule(), except that it does not keep a
312          * reference to \p module within the data object after it returns.
313          * Also, it can only be called after the data is ready, and only if
314          * getDataFrame() gives access to all of the data.
315          * It is provided for additional flexibility in postprocessing
316          * in-memory data.
317          *
318          * \todo
319          * Currently, this method may not work correctly if \p module requests
320          * storage (addModule() has the same problem if called after data is
321          * started).
322          */
323         void applyModule(AnalysisDataModuleInterface *module);
324
325     protected:
326         /*! \cond libapi */
327         /*! \brief
328          * Initializes a new analysis data object.
329          *
330          * \throws std::bad_alloc if out of memory.
331          */
332         AbstractAnalysisData();
333
334         /*! \brief
335          * Sets the number of data sets.
336          *
337          * \param[in] dataSetCount  Number of data sets (must be > 0).
338          * \throws    std::bad_alloc if out of memory.
339          * \throws    APIError if modules have been added that are not
340          *      compatible with the new data set count.
341          *
342          * It not called, the data object has a single data set.  Can be called
343          * only before AnalysisDataModuleManager::notifyDataStart().
344          * Multiple calls are allowed before that point; the last call takes
345          * effect.
346          *
347          * Strong exception safety.
348          *
349          * \see dataSetCount()
350          */
351         void setDataSetCount(int dataSetCount);
352         /*! \brief
353          * Sets the number of columns for a data set.
354          *
355          * \param[in] dataSet      Zero-based index of the data set.
356          * \param[in] columnCount  Number of columns in \p dataSet (must be > 0).
357          * \throws    APIError if modules have been added that are not
358          *      compatible with the new column count.
359          *
360          * Must be called at least once for each data set before
361          * AnalysisDataModuleManager::notifyDataStart().  Can be called only
362          * before AnalysisDataModuleManager::notifyDataStart().
363          * Multiple calls are allowed before that point; the last call takes
364          * effect.
365          *
366          * Strong exception safety.
367          *
368          * \see columnCount()
369          */
370         void setColumnCount(int dataSet, int columnCount);
371         /*! \brief
372          * Sets whether the data has multiple points per column in a frame.
373          *
374          * \param[in] bMultipoint  Whether multiple points per column are
375          *     possible.
376          * \throws    APIError if modules have been added that are not
377          *      compatible with the new setting.
378          *
379          * If not called, only a single point per column is allowed.  Can be
380          * called only before AnalysisDataModuleManager::notifyDataStart().
381          * Multiple calls are allowed before that point; the last call takes
382          * effect.
383          *
384          * Strong exception safety.
385          *
386          * \see isMultipoint()
387          */
388         void setMultipoint(bool bMultipoint);
389
390         /*! \brief
391          * Implements access to data frames.
392          *
393          * \param[in] index  Zero-based frame index to access.
394          * \returns   Frame reference to frame \p index, or an invalid
395          *      reference if no such frame is available.
396          *
397          * Must not throw.  Failure to access a frame with the given index is
398          * indicated through the return value.
399          *
400          * Code in derived classes can assume that \p index is non-negative and
401          * less than frameCount().
402          *
403          * Derived classes can choose to return an invalid reference if
404          * requestStorageInternal() has not been called at all, or if the frame
405          * is too old (compared to the value given to requestStorageInternal()).
406          *
407          * This method is called internally by tryGetDataFrame() and
408          * getDataFrame().
409          *
410          * \see AnalysisDataStorage
411          */
412         virtual AnalysisDataFrameRef tryGetDataFrameInternal(int index) const = 0;
413         /*! \brief
414          * Implements storage requests.
415          *
416          * \param[in] nframes  Request storing at least \c nframes previous
417          *     frames (-1 = request storing all). Will be either -1 or >0.
418          * \returns   true if the request could be satisfied.
419          *
420          * Must not throw.  Failure to access a frame with the given index is
421          * indicated through the return value.
422          *
423          * Derived classes should be prepared for any number of calls to this
424          * method before notifyDataStart() is called (and during that call).
425          *
426          * This method is called internally by requestStorage().
427          *
428          * \see AnalysisDataStorage
429          */
430         virtual bool requestStorageInternal(int nframes) = 0;
431
432         //! Returns the module manager to use for calling notification methods.
433         AnalysisDataModuleManager       &moduleManager();
434         //! Returns the module manager to use for calling notification methods.
435         const AnalysisDataModuleManager &moduleManager() const;
436         //! \endcond
437
438     private:
439         class Impl;
440
441         PrivateImplPointer<Impl> impl_;
442 };
443
444 } // namespace gmx
445
446 #endif