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