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