9c32aa8c8ff70a8cde37e3648adcb4dd829f817f
[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,2014, 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 /*! \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 "../utility/common.h"
49 #include "../utility/gmxassert.h"
50 #include "../utility/real.h"
51
52 #include "dataframe.h"
53
54 namespace gmx
55 {
56
57 class AbstractAnalysisData;
58 class AnalysisDataFrameHeader;
59 class AnalysisDataFrameRef;
60 class AnalysisDataModuleManager;
61 class AnalysisDataParallelOptions;
62
63 class AnalysisDataStorage;
64
65 namespace internal
66 {
67 class AnalysisDataStorageImpl;
68 class AnalysisDataStorageFrameData;
69 }   // namespace internal
70
71 /*! \libinternal \brief
72  * Allows assigning values for a data frame in AnalysisDataStorage.
73  *
74  * This class implements the necessary methods to add new data into the
75  * storage.  AnalysisDataStorage::startFrame() returns an object of this type,
76  * which can be used to add one or more point sets to that data frame.
77  * When all data has been added, finishFrame() needs to be called.
78  *
79  * \inlibraryapi
80  * \ingroup module_analysisdata
81  */
82 class AnalysisDataStorageFrame
83 {
84     public:
85         /*! \brief Frees the frame object.
86          *
87          * Should not be called outside AnalysisDataStorage.
88          */
89         ~AnalysisDataStorageFrame();
90
91         /*! \brief
92          * Select data set that all other methods operate on.
93          *
94          * \param[in] index  Zero-based data set index to select.
95          *
96          * With multipoint data, a single point set can only contain values in
97          * a single data set.
98          * With non-multipoint data, arbitrary sequences of selectDataSet() and
99          * setValue() are supported.  The full frame is notified to the modules
100          * once it is finished.
101          *
102          * Does not throw.
103          */
104         void selectDataSet(int index);
105
106         //! Returns number of columns for the frame.
107         int columnCount() const { return columnCount_; }
108
109         /*! \brief
110          * Sets value for a column.
111          *
112          * \param[in] column  Zero-based column index.
113          * \param[in] value   Value to set for the column.
114          * \param[in] bPresent Present flag to set for the column.
115          *
116          * If called multiple times for a column (within one point set for
117          * multipoint data), old values are overwritten.
118          *
119          * Does not throw.
120          */
121         void setValue(int column, real value, bool bPresent = true)
122         {
123             GMX_ASSERT(column >= 0 && column < columnCount(),
124                        "Invalid column index");
125             values_[currentOffset_ + column].setValue(value, bPresent);
126             bPointSetInProgress_ = true;
127         }
128         /*! \brief
129          * Sets value for a column.
130          *
131          * \param[in] column  Zero-based column index.
132          * \param[in] value   Value to set for the column.
133          * \param[in] error   Error estimate to set for the column.
134          * \param[in] bPresent Present flag to set for the column.
135          *
136          * If called multiple times for a column (within one point set for
137          * multipoint data), old values are overwritten.
138          *
139          * Does not throw.
140          */
141         void setValue(int column, real value, real error, bool bPresent = true)
142         {
143             GMX_ASSERT(column >= 0 && column < columnCount(),
144                        "Invalid column index");
145             values_[currentOffset_ + column].setValue(value, error, bPresent);
146             bPointSetInProgress_ = true;
147         }
148         /*! \brief
149          * Access value for a column.
150          *
151          * \param[in] column  Zero-based column index.
152          *
153          * Should only be called after the column value has been set using
154          * setValue(); assigning a value to \c value(i) does not mark the
155          * column as set.
156          *
157          * Does not throw.
158          */
159         real &value(int column)
160         {
161             GMX_ASSERT(column >= 0 && column < columnCount(),
162                        "Invalid column index");
163             return values_[currentOffset_ + column].value();
164         }
165         /*! \brief
166          * Access value for a column.
167          *
168          * \param[in] column  Zero-based column index.
169          *
170          * Should only be called after the column value has been set using
171          * setValue().
172          *
173          * Does not throw.
174          */
175         real value(int column) const
176         {
177             GMX_ASSERT(column >= 0 && column < columnCount(),
178                        "Invalid column index");
179             return values_[currentOffset_ + column].value();
180         }
181         /*! \brief
182          * Mark point set as finished for multipoint data.
183          *
184          * Must be called after each point set for multipoint data, including
185          * the last (i.e., no values must be set between the last call to this
186          * method and AnalysisDataStorage::finishFrame()).
187          * Must not be called for non-multipoint data.
188          *
189          * After this method has been called, all values appear as not set.
190          *
191          * May call AnalysisDataModuleManager::notifyPointsAdd() and
192          * AnalysisDataModuleManager::notifyParallelPointsAdd(), and may throw
193          * any exception these methods throw.
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() or
253  * startParallelDataStorage() called.
254  * New frames can then be added using startFrame(), currentFrame() and
255  * finishFrame() methods.  When all frames are ready, finishDataStorage() must
256  * be called.  These methods (and AnalysisDataStorageFrame::finishPointSet())
257  * take the responsibility of calling all the notification methods in
258  * AnalysisDataModuleManager,
259  *
260  * \todo
261  * Proper multi-threaded implementation.
262  *
263  * \inlibraryapi
264  * \ingroup module_analysisdata
265  */
266 class AnalysisDataStorage
267 {
268     public:
269         //! Constructs a storage object.
270         AnalysisDataStorage();
271         ~AnalysisDataStorage();
272
273         /*! \brief
274          * Returns the number of ready frames.
275          *
276          * This method is designed such that calls to
277          * AbstractAnalysisData::frameCount() can be directly forwarded to this
278          * method.  See that method for more documentation.
279          *
280          * If this method returns N, this means that the first N frames have
281          * all been finished.
282          *
283          * \see AbstractAnalysisData::frameCount()
284          */
285         int frameCount() const;
286         /*! \brief
287          * Implements access to data frames.
288          *
289          * This method is designed such that calls to
290          * AbstractAnalysisData::tryGetDataFrameInternal() can be directly
291          * forwarded to this method.  See that method for more documentation.
292          *
293          * A valid reference for a frame will be returned after finishFrame()
294          * has been called for that frame.
295          *
296          * \see AbstractAnalysisData::tryGetDataFrameInternal()
297          */
298         AnalysisDataFrameRef tryGetDataFrame(int index) const;
299         /*! \brief
300          * Implements storage requests.
301          *
302          * This method is designed such that calls to
303          * AbstractAnalysisData::requestStorageInternal() can be directly
304          * forwarded to this method.  See that method for more documentation.
305          *
306          * \see AbstractAnalysisData::requestStorageInternal()
307          */
308         bool requestStorage(int nframes);
309
310         /*! \brief
311          * Start storing data.
312          *
313          * \param[in] data    AbstractAnalysisData object containing this
314          *      storage.
315          * \param     modules Module manager for \p data.
316          * \exception std::bad_alloc if storage allocation fails.
317          *
318          * Typically called as \c startDataStorage(this, &moduleManager())
319          * from a member of \p data when the data is ready to be started.
320          * The storage object will take responsibility of calling all
321          * module notification methods in AnalysisDataModuleManager using
322          * \p modules.
323          *
324          * Lifetime of \p data and \p modules must exceed the lifetime of the
325          * storage object
326          * (typically, the storage object will be a member in \p data).
327          *
328          * Calls AnalysisDataModuleManager::notifyDataStart(), and throws any
329          * exceptions this method throws.
330          */
331         void startDataStorage(AbstractAnalysisData      *data,
332                               AnalysisDataModuleManager *modules);
333         /*! \brief
334          * Start storing data in parallel.
335          *
336          * \param[in] data    AbstractAnalysisData object containing this
337          *      storage.
338          * \param[in] options Parallelization options to use.
339          * \param     modules Module manager for \p data.
340          * \exception std::bad_alloc if storage allocation fails.
341          *
342          * Should be called instead of startDataStorage() if the data will be
343          * produced in parallel.  Works as startDataStorage(), but additionally
344          * initializes the storage and the attached modules to prepare for
345          * out-of-order data frames.
346          *
347          * Calls AnalysisDataModuleManager::notifyParallelDataStart(), and
348          * throws any exceptions this method throws.
349          */
350         void startParallelDataStorage(
351             AbstractAnalysisData              *data,
352             AnalysisDataModuleManager         *modules,
353             const AnalysisDataParallelOptions &options);
354         /*! \brief
355          * Starts storing a new frame.
356          *
357          * \param[in] header  Header for the new frame.
358          * \retval  Frame object corresponding to the started frame.
359          * \exception std::bad_alloc if storage reallocation fails
360          *      (only possible if storage of all frames has been requested).
361          * \exception APIError if frame is too far in the future.
362          *
363          * The returned object will be valid until the corresponding
364          * finishFrame() call.
365          *
366          * Must be called exactly once for each frame index.
367          *
368          * Currently, the implementation only works if the new frame is not too
369          * far in the future:
370          * If \c i is the index of the last frame such that all frames from
371          * 0, ..., \c i have been finished, then \p header().index() should be
372          * at most \c 2*parallelizationFactor-1 larger than \c i, where
373          * parallelizationFactor is the parallelization factor passed to
374          * setParallelOptions().
375          * Throws APIError if this constraint is violated.
376          *
377          * Calls AnalysisDataModuleManager::notifyFrameStart() (in certain
378          * cases) and AnalysisDataModuleManager::notifyParallelFrameStart(),
379          * and throws any exceptions these methods throw.
380          */
381         AnalysisDataStorageFrame &startFrame(const AnalysisDataFrameHeader &header);
382         /*! \brief
383          * Convenience method to start storing a new frame.
384          *
385          * Identical to \c startFrame(AnalysisDataFrameHeader(index, x, dx));
386          */
387         AnalysisDataStorageFrame &startFrame(int index, real x, real dx);
388         /*! \brief
389          * Obtain a frame object for an in-progress frame.
390          *
391          * \param[in] index  Frame index.
392          * \retval  Frame object corresponding to \p index.
393          *
394          * startFrame() should have been called for the frame with index
395          * \p index, and finishFrame() should not yet have been called.
396          * Returns the same object as returned by the original startFrame()
397          * call for the same index.
398          *
399          * Does not throw.
400          */
401         AnalysisDataStorageFrame &currentFrame(int index);
402         /*! \brief
403          * Convenience method for finishing a data frame.
404          *
405          * \param[in] index  Frame index.
406          *
407          * Identical to \c currentFrame(index).finishFrame().
408          *
409          * \see AnalysisDataStorageFrame::finishFrame()
410          */
411         void finishFrame(int index);
412         /*! \brief
413          * Finishes storing data.
414          *
415          * Calls AnalysisDataModuleManager::notifyDataFinish(), and throws any
416          * exceptions this method throws.
417          */
418         void finishDataStorage();
419
420     private:
421         typedef internal::AnalysisDataStorageImpl Impl;
422
423         PrivateImplPointer<Impl> impl_;
424 };
425
426 } // namespace gmx
427
428 #endif