Apply clang-format to source tree
[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,2014,2015,2019, 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 <memory>
47
48 #include "gromacs/utility/classhelpers.h"
49
50 namespace gmx
51 {
52
53 class AnalysisDataModuleManager;
54 class AnalysisDataFrameHeader;
55 class AnalysisDataFrameRef;
56 class AnalysisDataPointSetRef;
57 class IAnalysisDataModule;
58
59 //! Smart pointer for managing a generic analysis data module.
60 typedef std::shared_ptr<IAnalysisDataModule> AnalysisDataModulePointer;
61
62 /*! \brief
63  * Abstract base class for all objects that provide data.
64  *
65  * The public interface includes methods for querying the data (isMultipoint(),
66  * dataSetCount(), columnCount(), frameCount(), tryGetDataFrame(),
67  * getDataFrame(), requestStorage()) and methods for using modules for
68  * processing the data (addModule(), addColumnModule(), applyModule()).
69  *
70  * Notice that even for non-const objects, the interface does not provide any
71  * means of altering the data.  It is only possible to add modules, making it
72  * relatively safe to return a non-const pointer of this type pointing to an
73  * internal data structure without worrying about possible modifications of the
74  * data.
75  *
76  * \if libapi
77  * This class also provides protected methods for use in derived classes.
78  * The properties returned by isMultipoint(), dataSetCount(), and columnCount()
79  * must be set using setMultipoint(), setDataSetCount(), and setColumnCount().
80  * notify*() methods in the AnalysisDataModuleManager returned by
81  * moduleManager() must be used to report when data becomes available for
82  * modules to process it.
83  * There are also three pure virtual methods that need to be implemented to
84  * provide access to stored data: one public (frameCount()) and two protected
85  * ones (requestStorageInternal() and tryGetDataFrameInternal()).
86  *
87  * It is up to subclasses to ensure that the virtual methods and the
88  * notifications in AnalysisDataModuleManager 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      * \if libapi
201      * Derived classes should implement this to return the number of
202      * frames.  The frame count should not be incremented before
203      * tryGetDataFrameInternal() can return the new frame.
204      * The frame count must be incremented before
205      * AnalysisDataModuleManager::notifyFrameFinish() is called.
206      * \endif
207      */
208     virtual int frameCount() const = 0;
209     /*! \brief
210      * Access stored data.
211      *
212      * \param[in] index  Zero-based frame index to access.
213      * \returns   Frame reference to frame \p index, or an invalid
214      *      reference if no such frame is available.
215      *
216      * Does not throw.  Failure to access a frame with the given index is
217      * indicated through the return value.  Negative \p index is allowed,
218      * and will always result in an invalid reference being returned.
219      *
220      * \see requestStorage()
221      * \see getDataFrame()
222      */
223     AnalysisDataFrameRef tryGetDataFrame(int index) const;
224     /*! \brief
225      * Access stored data.
226      *
227      * \param[in] index  Zero-based frame index to access.
228      * \returns   Frame reference to frame \p index.
229      * \throws    APIError if the requested frame is not accessible.
230      *
231      * If the data is not certainly available, use tryGetDataFrame().
232      *
233      * \see requestStorage()
234      * \see tryGetDataFrame()
235      */
236     AnalysisDataFrameRef getDataFrame(int index) const;
237     /*! \brief
238      * Request storage of frames.
239      *
240      * \param[in] nframes  Request storing at least \c nframes previous
241      *     frames (-1 = request storing all). Must be >= -1.
242      * \returns true if the request could be satisfied.
243      *
244      * If called multiple times, the largest request is honored.
245      *
246      * Does not throw.  Failure to honor the request is indicated through
247      * the return value.
248      *
249      * \see getDataFrame()
250      * \see tryGetDataFrame()
251      */
252     bool requestStorage(int nframes);
253
254     /*! \brief
255      * Adds a module to process the data.
256      *
257      * \param     module  Module to add.
258      * \throws    std::bad_alloc if out of memory.
259      * \throws    APIError if
260      *      - \p module is not compatible with the data object
261      *      - data has already been added to the data object and everything
262      *        is not available through getDataFrame().
263      * \throws    unspecified Any exception thrown by \p module in its
264      *      notification methods (if data has been added).
265      *
266      * If data has already been added to the data, the new module
267      * immediately processes all existing data.  APIError is thrown
268      * if all data is not available through getDataFrame().
269      *
270      * The caller can keep a copy of the module pointer if it requires
271      * later access to the module.
272      *
273      * If the method throws, the state of the data object is not changed.
274      * The state of the data module is indeterminate.
275      */
276     void addModule(const AnalysisDataModulePointer& module);
277     /*! \brief
278      * Adds a module that processes only a subset of the columns.
279      *
280      * \param[in] col     First column.
281      * \param[in] span    Number of columns.
282      * \param     module  Module to add.
283      *
284      * Throws in the same situations as addModule().
285      *
286      * Currently, all data sets are filtered using the same column mask.
287      *
288      * \todo
289      * This method doesn't currently work in all cases with multipoint
290      * data or with multiple data sets.  In particular, if the added module
291      * requests storage and uses getDataFrame(), it will behave
292      * unpredictably (most likely asserts).
293      *
294      * \todo
295      * Generalize this method to multiple data sets (e.g., for adding
296      * modules that only process a single data set).
297      *
298      * \see addModule()
299      */
300     void addColumnModule(int col, int span, const AnalysisDataModulePointer& module);
301     /*! \brief
302      * Applies a module to process data that is ready.
303      *
304      * \param     module  Module to apply.
305      * \throws    APIError in same situations as addModule().
306      * \throws    unspecified Any exception thrown by \p module in its
307      *      notification methods.
308      *
309      * This function works as addModule(), except that it does not keep a
310      * reference to \p module within the data object after it returns.
311      * Also, it can only be called after the data is ready, and only if
312      * getDataFrame() gives access to all of the data.
313      * It is provided for additional flexibility in postprocessing
314      * in-memory data.
315      *
316      * \todo
317      * Currently, this method may not work correctly if \p module requests
318      * storage (addModule() has the same problem if called after data is
319      * started).
320      */
321     void applyModule(IAnalysisDataModule* module);
322
323 protected:
324     /*! \cond libapi */
325     /*! \brief
326      * Initializes a new analysis data object.
327      *
328      * \throws std::bad_alloc if out of memory.
329      */
330     AbstractAnalysisData();
331
332     /*! \brief
333      * Sets the number of data sets.
334      *
335      * \param[in] dataSetCount  Number of data sets (must be > 0).
336      * \throws    std::bad_alloc if out of memory.
337      * \throws    APIError if modules have been added that are not
338      *      compatible with the new data set count.
339      *
340      * It not called, the data object has a single data set.  Can be called
341      * only before AnalysisDataModuleManager::notifyDataStart().
342      * Multiple calls are allowed before that point; the last call takes
343      * effect.
344      *
345      * Strong exception safety.
346      *
347      * \see dataSetCount()
348      */
349     void setDataSetCount(int dataSetCount);
350     /*! \brief
351      * Sets the number of columns for a data set.
352      *
353      * \param[in] dataSet      Zero-based index of the data set.
354      * \param[in] columnCount  Number of columns in \p dataSet (must be > 0).
355      * \throws    APIError if modules have been added that are not
356      *      compatible with the new column count.
357      *
358      * Must be called at least once for each data set before
359      * AnalysisDataModuleManager::notifyDataStart().  Can be called only
360      * before AnalysisDataModuleManager::notifyDataStart().
361      * Multiple calls are allowed before that point; the last call takes
362      * effect.
363      *
364      * Strong exception safety.
365      *
366      * \see columnCount()
367      */
368     void setColumnCount(int dataSet, int columnCount);
369     /*! \brief
370      * Sets whether the data has multiple points per column in a frame.
371      *
372      * \param[in] bMultipoint  Whether multiple points per column are
373      *     possible.
374      * \throws    APIError if modules have been added that are not
375      *      compatible with the new setting.
376      *
377      * If not called, only a single point per column is allowed.  Can be
378      * called only before AnalysisDataModuleManager::notifyDataStart().
379      * Multiple calls are allowed before that point; the last call takes
380      * effect.
381      *
382      * Strong exception safety.
383      *
384      * \see isMultipoint()
385      */
386     void setMultipoint(bool bMultipoint);
387
388     /*! \brief
389      * Implements access to data frames.
390      *
391      * \param[in] index  Zero-based frame index to access.
392      * \returns   Frame reference to frame \p index, or an invalid
393      *      reference if no such frame is available.
394      *
395      * Must not throw.  Failure to access a frame with the given index is
396      * indicated through the return value.
397      *
398      * Code in derived classes can assume that \p index is non-negative and
399      * less than frameCount().
400      *
401      * Derived classes can choose to return an invalid reference if
402      * requestStorageInternal() has not been called at all, or if the frame
403      * is too old (compared to the value given to requestStorageInternal()).
404      *
405      * This method is called internally by tryGetDataFrame() and
406      * getDataFrame().
407      *
408      * \see AnalysisDataStorage
409      */
410     virtual AnalysisDataFrameRef tryGetDataFrameInternal(int index) const = 0;
411     /*! \brief
412      * Implements storage requests.
413      *
414      * \param[in] nframes  Request storing at least \c nframes previous
415      *     frames (-1 = request storing all). Will be either -1 or >0.
416      * \returns   true if the request could be satisfied.
417      *
418      * Must not throw.  Failure to access a frame with the given index is
419      * indicated through the return value.
420      *
421      * Derived classes should be prepared for any number of calls to this
422      * method before notifyDataStart() is called (and during that call).
423      *
424      * This method is called internally by requestStorage().
425      *
426      * \see AnalysisDataStorage
427      */
428     virtual bool requestStorageInternal(int nframes) = 0;
429
430     //! Returns the module manager to use for calling notification methods.
431     AnalysisDataModuleManager& moduleManager();
432     //! Returns the module manager to use for calling notification methods.
433     const AnalysisDataModuleManager& moduleManager() const;
434     //! \endcond
435
436 private:
437     class Impl;
438
439     PrivateImplPointer<Impl> impl_;
440 };
441
442 } // namespace gmx
443
444 #endif