AbstractAnalysisData module handling into a separate class.
[alexxy/gromacs.git] / src / gromacs / analysisdata / datastorage.h
1 /*
2  * This file is part of the GROMACS molecular simulation package.
3  *
4  * Copyright (c) 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 /*! \libinternal \file
36  * \brief
37  * Declares gmx::AnalysisDataStorage.
38  *
39  * \author Teemu Murtola <teemu.murtola@gmail.com>
40  * \inlibraryapi
41  * \ingroup module_analysisdata
42  */
43 #ifndef GMX_ANALYSISDATA_DATASTORAGE_H
44 #define GMX_ANALYSISDATA_DATASTORAGE_H
45
46 #include <vector>
47
48 #include "../legacyheaders/types/simple.h"
49
50 #include "../utility/common.h"
51 #include "../utility/gmxassert.h"
52
53 #include "dataframe.h"
54
55 namespace gmx
56 {
57
58 class AbstractAnalysisData;
59 class AnalysisDataFrameHeader;
60 class AnalysisDataFrameRef;
61 class AnalysisDataModuleManager;
62 class AnalysisDataParallelOptions;
63
64 class AnalysisDataStorage;
65
66 namespace internal
67 {
68 class AnalysisDataStorageImpl;
69 class AnalysisDataStorageFrameData;
70 }   // namespace internal
71
72 /*! \libinternal \brief
73  * Allows assigning values for a data frame in AnalysisDataStorage.
74  *
75  * This class implements the necessary methods to add new data into the
76  * storage.  AnalysisDataStorage::startFrame() returns an object of this type,
77  * which can be used to add one or more point sets to that data frame.
78  * When all data has been added, finishFrame() needs to be called.
79  *
80  * \inlibraryapi
81  * \ingroup module_analysisdata
82  */
83 class AnalysisDataStorageFrame
84 {
85     public:
86         /*! \brief Frees the frame object.
87          *
88          * Should not be called outside AnalysisDataStorage.
89          */
90         ~AnalysisDataStorageFrame();
91
92         /*! \brief
93          * Select data set that all other methods operate on.
94          *
95          * \param[in] index  Zero-based data set index to select.
96          *
97          * With multipoint data, a single point set can only contain values in
98          * a single data set.
99          * With non-multipoint data, arbitrary sequences of selectDataSet() and
100          * setValue() are supported.  The full frame is notified to the modules
101          * once it is finished.
102          *
103          * Does not throw.
104          */
105         void selectDataSet(int index);
106
107         //! Returns number of columns for the frame.
108         int columnCount() const { return columnCount_; }
109
110         /*! \brief
111          * Sets value for a column.
112          *
113          * \param[in] column  Zero-based column index.
114          * \param[in] value   Value to set for the column.
115          * \param[in] bPresent Present flag to set for the column.
116          *
117          * If called multiple times for a column (within one point set for
118          * multipoint data), old values are overwritten.
119          *
120          * Does not throw.
121          */
122         void setValue(int column, real value, bool bPresent = true)
123         {
124             GMX_ASSERT(column >= 0 && column < columnCount(),
125                        "Invalid column index");
126             values_[currentOffset_ + column].setValue(value, bPresent);
127             bPointSetInProgress_ = true;
128         }
129         /*! \brief
130          * Sets value for a column.
131          *
132          * \param[in] column  Zero-based column index.
133          * \param[in] value   Value to set for the column.
134          * \param[in] error   Error estimate to set for the column.
135          * \param[in] bPresent Present flag to set for the column.
136          *
137          * If called multiple times for a column (within one point set for
138          * multipoint data), old values are overwritten.
139          *
140          * Does not throw.
141          */
142         void setValue(int column, real value, real error, bool bPresent = true)
143         {
144             GMX_ASSERT(column >= 0 && column < columnCount(),
145                        "Invalid column index");
146             values_[currentOffset_ + column].setValue(value, error, bPresent);
147             bPointSetInProgress_ = true;
148         }
149         /*! \brief
150          * Access value for a column.
151          *
152          * \param[in] column  Zero-based column index.
153          *
154          * Should only be called after the column value has been set using
155          * setValue(); assigning a value to \c value(i) does not mark the
156          * column as set.
157          *
158          * Does not throw.
159          */
160         real &value(int column)
161         {
162             GMX_ASSERT(column >= 0 && column < columnCount(),
163                        "Invalid column index");
164             return values_[currentOffset_ + column].value();
165         }
166         /*! \brief
167          * Access value for a column.
168          *
169          * \param[in] column  Zero-based column index.
170          *
171          * Should only be called after the column value has been set using
172          * setValue().
173          *
174          * Does not throw.
175          */
176         real value(int column) const
177         {
178             GMX_ASSERT(column >= 0 && column < columnCount(),
179                        "Invalid column index");
180             return values_[currentOffset_ + column].value();
181         }
182         /*! \brief
183          * Mark point set as finished for multipoint data.
184          *
185          * Must be called after each point set for multipoint data, including
186          * the last (i.e., no values must be set between the last call to this
187          * method and AnalysisDataStorage::finishFrame()).
188          * Must not be called for non-multipoint data.
189          *
190          * After this method has been called, all values appear as not set.
191          *
192          * May call AnalysisDataModuleManager::notifyPointsAdd(), and may throw
193          * any exception this method throws.
194          */
195         void finishPointSet();
196         /*! \brief
197          * Finish storing a frame.
198          *
199          * Must be called exactly once for each frame returned by startFrame(),
200          * after the corresponding call.
201          * The frame object must not be accessed after the call.
202          *
203          * Calls notification methods in AnalysisDataModuleManager, and may
204          * throw any exceptions these methods throw.
205          */
206         void finishFrame();
207
208     private:
209
210         /*! \brief
211          * Create a new storage frame.
212          *
213          * \param[in] data  Data object for which the frame is for
214          *      (used for data set and column counts).
215          */
216         explicit AnalysisDataStorageFrame(const AbstractAnalysisData &data);
217
218         //! Clear all column values from the frame.
219         void clearValues();
220
221         //! Implementation data.
222         internal::AnalysisDataStorageFrameData *data_;
223         //! Values for the currently in-progress point set.
224         std::vector<AnalysisDataValue>          values_;
225
226         //! Index of the currently active dataset.
227         int                                     currentDataSet_;
228         //! Offset of the first value in \a values_ for the current data set.
229         int                                     currentOffset_;
230         //! Number of columns in the current data set.
231         int                                     columnCount_;
232
233         //! Whether any values have been set in the current point set.
234         bool                                    bPointSetInProgress_;
235
236         //! Needed for access to the constructor.
237         friend class internal::AnalysisDataStorageImpl;
238         //! Needed for managing the frame the object points to.
239         friend class internal::AnalysisDataStorageFrameData;
240
241         GMX_DISALLOW_COPY_AND_ASSIGN(AnalysisDataStorageFrame);
242 };
243
244 /*! \libinternal \brief
245  * Helper class that implements storage of data.
246  *
247  * This class implements a standard way of storing data to avoid implementing
248  * storage in each class derived from AbstractAnalysisData separately.
249  * To use this class in a class derived from AbstractAnalysisData, a member
250  * variable of this type should be declared and the pure virtual methods
251  * forwarded to frameCount(), tryGetDataFrame() and requestStorage().
252  * Storage properties should be set up, and then startDataStorage() called.
253  * New frames can then be added using startFrame(), currentFrame() and
254  * finishFrame() methods.  When all frames are ready, finishDataStorage() must
255  * be called.  These methods (and AnalysisDataStorageFrame::finishPointSet())
256  * take the responsibility of calling all the notification methods in
257  * AnalysisDataModuleManager,
258  *
259  * \todo
260  * Proper multi-threaded implementation.
261  *
262  * \inlibraryapi
263  * \ingroup module_analysisdata
264  */
265 class AnalysisDataStorage
266 {
267     public:
268         //! Constructs a storage object.
269         AnalysisDataStorage();
270         ~AnalysisDataStorage();
271
272         /*! \brief
273          * Set parallelization options for the storage.
274          *
275          * \param[in] opt  Parallization options to use.
276          *
277          * If this method is not called, the storage is set up for serial
278          * storage only.
279          *
280          * Does not throw.
281          */
282         void setParallelOptions(const AnalysisDataParallelOptions &opt);
283
284         /*! \brief
285          * Returns the number of ready frames.
286          *
287          * This method is designed such that calls to
288          * AbstractAnalysisData::frameCount() can be directly forwarded to this
289          * method.  See that method for more documentation.
290          *
291          * If this method returns N, this means that the first N frames have
292          * all been finished.
293          *
294          * \see AbstractAnalysisData::frameCount()
295          */
296         int frameCount() const;
297         /*! \brief
298          * Implements access to data frames.
299          *
300          * This method is designed such that calls to
301          * AbstractAnalysisData::tryGetDataFrameInternal() can be directly
302          * forwarded to this method.  See that method for more documentation.
303          *
304          * A valid reference for a frame will be returned after finishFrame()
305          * has been called for that frame.
306          *
307          * \see AbstractAnalysisData::tryGetDataFrameInternal()
308          */
309         AnalysisDataFrameRef tryGetDataFrame(int index) const;
310         /*! \brief
311          * Implements storage requests.
312          *
313          * This method is designed such that calls to
314          * AbstractAnalysisData::requestStorageInternal() can be directly
315          * forwarded to this method.  See that method for more documentation.
316          *
317          * \see AbstractAnalysisData::requestStorageInternal()
318          */
319         bool requestStorage(int nframes);
320
321         /*! \brief
322          * Start storing data.
323          *
324          * \param[in] data    AbstractAnalysisData object containing this
325          *      storage.
326          * \param     modules Module manager for \p data.
327          * \exception std::bad_alloc if storage allocation fails.
328          *
329          * Typically called as \c startDataStorage(this, &moduleManager())
330          * from a member of \p data when the data is ready to be started.
331          * The storage object will take responsibility of calling all
332          * module notification methods in AnalysisDataModuleManager using
333          * \p modules.
334          *
335          * Lifetime of \p data and \p modules must exceed the lifetime of the
336          * storage object
337          * (typically, the storage object will be a member in \p data).
338          *
339          * Calls AnalysisDataModuleManager::notifyDataStart(), and throws any
340          * exceptions this method throws.
341          */
342         void startDataStorage(AbstractAnalysisData      *data,
343                               AnalysisDataModuleManager *modules);
344         /*! \brief
345          * Starts storing a new frame.
346          *
347          * \param[in] header  Header for the new frame.
348          * \retval  Frame object corresponding to the started frame.
349          * \exception std::bad_alloc if storage reallocation fails
350          *      (only possible if storage of all frames has been requested).
351          * \exception APIError if frame is too far in the future.
352          *
353          * The returned object will be valid until the corresponding
354          * finishFrame() call.
355          *
356          * Must be called exactly once for each frame index.
357          *
358          * Currently, the implementation only works if the new frame is not too
359          * far in the future:
360          * If \c i is the index of the last frame such that all frames from
361          * 0, ..., \c i have been finished, then \p header().index() should be
362          * at most \c 2*parallelizationFactor-1 larger than \c i, where
363          * parallelizationFactor is the parallelization factor passed to
364          * setParallelOptions().
365          * Throws APIError if this constraint is violated.
366          *
367          * Calls AnalysisDataModuleManager::notifyFrameStart() in certain
368          * cases, and throws any exceptions this method throws.
369          */
370         AnalysisDataStorageFrame &startFrame(const AnalysisDataFrameHeader &header);
371         /*! \brief
372          * Convenience method to start storing a new frame.
373          *
374          * Identical to \c startFrame(AnalysisDataFrameHeader(index, x, dx));
375          */
376         AnalysisDataStorageFrame &startFrame(int index, real x, real dx);
377         /*! \brief
378          * Obtain a frame object for an in-progress frame.
379          *
380          * \param[in] index  Frame index.
381          * \retval  Frame object corresponding to \p index.
382          *
383          * startFrame() should have been called for the frame with index
384          * \p index, and finishFrame() should not yet have been called.
385          * Returns the same object as returned by the original startFrame()
386          * call for the same index.
387          *
388          * Does not throw.
389          */
390         AnalysisDataStorageFrame &currentFrame(int index);
391         /*! \brief
392          * Convenience method for finishing a data frame.
393          *
394          * \param[in] index  Frame index.
395          *
396          * Identical to \c currentFrame(index).finishFrame().
397          *
398          * \see AnalysisDataStorageFrame::finishFrame()
399          */
400         void finishFrame(int index);
401         /*! \brief
402          * Finishes storing data.
403          *
404          * Calls AnalysisDataModuleManager::notifyDataFinish(), and throws any
405          * exceptions this method throws.
406          */
407         void finishDataStorage();
408
409     private:
410         typedef internal::AnalysisDataStorageImpl Impl;
411
412         PrivateImplPointer<Impl> impl_;
413 };
414
415 } // namespace gmx
416
417 #endif