Merge "Fixing some Doxygen warnings"
[alexxy/gromacs.git] / src / gromacs / analysisdata / abstractdata.cpp
1 /*
2  *
3  *                This source code is part of
4  *
5  *                 G   R   O   M   A   C   S
6  *
7  *          GROningen MAchine for Chemical Simulations
8  *
9  * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
10  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
11  * Copyright (c) 2001-2009, The GROMACS development team,
12  * check out http://www.gromacs.org for more information.
13
14  * This program is free software; you can redistribute it and/or
15  * modify it under the terms of the GNU General Public License
16  * as published by the Free Software Foundation; either version 2
17  * of the License, or (at your option) any later version.
18  *
19  * If you want to redistribute modifications, please consider that
20  * scientific software is very special. Version control is crucial -
21  * bugs must be traceable. We will be happy to consider code for
22  * inclusion in the official distribution, but derived work must not
23  * be called official GROMACS. Details are found in the README & COPYING
24  * files - if they are missing, get the official version at www.gromacs.org.
25  *
26  * To help us fund GROMACS development, we humbly ask that you cite
27  * the papers on the package - you can find them in the top README file.
28  *
29  * For more info, check our website at http://www.gromacs.org
30  */
31 /*! \internal \file
32  * \brief
33  * Implements gmx::AbstractAnalysisData.
34  *
35  * \author Teemu Murtola <teemu.murtola@cbr.su.se>
36  * \ingroup module_analysisdata
37  */
38 #include "gromacs/analysisdata/abstractdata.h"
39
40 #include "gromacs/fatalerror/exceptions.h"
41 #include "gromacs/fatalerror/gmxassert.h"
42
43 #include "abstractdata-impl.h"
44 #include "dataframe.h"
45 #include "dataproxy.h"
46
47 namespace gmx
48 {
49
50 /********************************************************************
51  * AbstractAnalysisData::Impl
52  */
53
54 AbstractAnalysisData::Impl::Impl()
55     : _bAllowMissing(true), _bDataStart(false), _bInData(false), _bInFrame(false),
56       _currIndex(-1), _nframes(0)
57 {
58 }
59
60 AbstractAnalysisData::Impl::~Impl()
61 {
62 }
63
64
65 void
66 AbstractAnalysisData::Impl::presentData(AbstractAnalysisData *data,
67                                         AnalysisDataModuleInterface *module)
68 {
69     module->dataStarted(data);
70     bool bCheckMissing = _bAllowMissing
71         && !(module->flags() & AnalysisDataModuleInterface::efAllowMissing);
72     for (int i = 0; i < data->frameCount(); ++i)
73     {
74         AnalysisDataFrameRef frame = data->getDataFrame(i);
75         GMX_RELEASE_ASSERT(frame.isValid(), "Invalid data frame returned");
76         if (bCheckMissing && !frame.allPresent())
77         {
78             GMX_THROW(APIError("Missing data not supported by a module"));
79         }
80         module->frameStarted(frame.header());
81         module->pointsAdded(frame.points());
82         module->frameFinished(frame.header());
83     }
84     if (!_bInData)
85     {
86         module->dataFinished();
87     }
88 }
89
90
91 /********************************************************************
92  * AbstractAnalysisData
93  */
94 /*! \cond libapi */
95 AbstractAnalysisData::AbstractAnalysisData()
96     : _impl(new Impl()), _ncol(0), _bMultiPoint(false)
97 {
98 }
99 //! \endcond
100
101 AbstractAnalysisData::~AbstractAnalysisData()
102 {
103 }
104
105
106 int
107 AbstractAnalysisData::frameCount() const
108 {
109     return _impl->_nframes;
110 }
111
112
113 AnalysisDataFrameRef
114 AbstractAnalysisData::tryGetDataFrame(int index) const
115 {
116     if (index < 0 || index >= frameCount())
117     {
118         return AnalysisDataFrameRef();
119     }
120     return tryGetDataFrameInternal(index);
121 }
122
123
124 AnalysisDataFrameRef
125 AbstractAnalysisData::getDataFrame(int index) const
126 {
127     AnalysisDataFrameRef frame = tryGetDataFrame(index);
128     if (!frame.isValid())
129     {
130         GMX_THROW(APIError("Invalid frame accessed"));
131     }
132     return frame;
133 }
134
135
136 bool
137 AbstractAnalysisData::requestStorage(int nframes)
138 {
139     GMX_RELEASE_ASSERT(nframes >= -1, "Invalid number of frames requested");
140     if (nframes == 0)
141     {
142         return true;
143     }
144     return requestStorageInternal(nframes);
145 }
146
147
148 void
149 AbstractAnalysisData::addModule(AnalysisDataModulePointer module)
150 {
151     if ((columnCount() > 1 && !(module->flags() & AnalysisDataModuleInterface::efAllowMulticolumn))
152         || (isMultipoint() && !(module->flags() & AnalysisDataModuleInterface::efAllowMultipoint))
153         || (!isMultipoint() && (module->flags() & AnalysisDataModuleInterface::efOnlyMultipoint)))
154     {
155         GMX_THROW(APIError("Data module not compatible with data object properties"));
156     }
157
158     if (_impl->_bDataStart)
159     {
160         GMX_RELEASE_ASSERT(!_impl->_bInFrame,
161                            "Cannot add data modules in mid-frame");
162         _impl->presentData(this, module.get());
163     }
164     if (!(module->flags() & AnalysisDataModuleInterface::efAllowMissing))
165     {
166         _impl->_bAllowMissing = false;
167     }
168     _impl->_modules.push_back(move(module));
169 }
170
171
172 void
173 AbstractAnalysisData::addColumnModule(int col, int span,
174                                       AnalysisDataModulePointer module)
175 {
176     GMX_RELEASE_ASSERT(col >= 0 && span >= 1 && col + span <= _ncol,
177                        "Invalid columns specified for a column module");
178     if (_impl->_bDataStart)
179     {
180         GMX_THROW(NotImplementedError("Cannot add column modules after data"));
181     }
182
183     boost::shared_ptr<AnalysisDataProxy> proxy(
184             new AnalysisDataProxy(col, span, this));
185     proxy->addModule(module);
186     addModule(proxy);
187 }
188
189
190 void
191 AbstractAnalysisData::applyModule(AnalysisDataModuleInterface *module)
192 {
193     if ((columnCount() > 1 && !(module->flags() & AnalysisDataModuleInterface::efAllowMulticolumn))
194         || (isMultipoint() && !(module->flags() & AnalysisDataModuleInterface::efAllowMultipoint))
195         || (!isMultipoint() && (module->flags() & AnalysisDataModuleInterface::efOnlyMultipoint)))
196     {
197         GMX_THROW(APIError("Data module not compatible with data object properties"));
198     }
199     GMX_RELEASE_ASSERT(_impl->_bDataStart && !_impl->_bInData,
200                        "Data module can only be applied to ready data");
201
202     _impl->presentData(this, module);
203 }
204
205 /*! \cond libapi */
206 void
207 AbstractAnalysisData::setColumnCount(int ncol)
208 {
209     GMX_RELEASE_ASSERT(ncol > 0, "Invalid data column count");
210     GMX_RELEASE_ASSERT(_ncol == 0 || _impl->_modules.empty(),
211                        "Data column count cannot be changed after modules are added");
212     GMX_RELEASE_ASSERT(!_impl->_bDataStart,
213                        "Data column count cannot be changed after data has been added");
214     _ncol = ncol;
215 }
216
217
218 void
219 AbstractAnalysisData::setMultipoint(bool multipoint)
220 {
221     GMX_RELEASE_ASSERT(_impl->_modules.empty(),
222                        "Data type cannot be changed after modules are added");
223     GMX_RELEASE_ASSERT(!_impl->_bDataStart,
224                        "Data type cannot be changed after data has been added");
225     _bMultiPoint = multipoint;
226 }
227
228
229 /*! \internal
230  * This method is not const because the dataStarted() methods of the attached
231  * modules can request storage of the data.
232  */
233 void
234 AbstractAnalysisData::notifyDataStart()
235 {
236     GMX_RELEASE_ASSERT(!_impl->_bDataStart,
237                        "notifyDataStart() called more than once");
238     GMX_RELEASE_ASSERT(_ncol > 0, "Data column count is not set");
239     _impl->_bDataStart = _impl->_bInData = true;
240
241     Impl::ModuleList::const_iterator i;
242     for (i = _impl->_modules.begin(); i != _impl->_modules.end(); ++i)
243     {
244         if (_ncol > 1 && !((*i)->flags() & AnalysisDataModuleInterface::efAllowMulticolumn))
245         {
246             GMX_THROW(APIError("Data module not compatible with data object properties"));
247         }
248         (*i)->dataStarted(this);
249     }
250 }
251
252
253 void
254 AbstractAnalysisData::notifyFrameStart(const AnalysisDataFrameHeader &header) const
255 {
256     GMX_ASSERT(_impl->_bInData, "notifyDataStart() not called");
257     GMX_ASSERT(!_impl->_bInFrame,
258                "notifyFrameStart() called while inside a frame");
259     GMX_ASSERT(header.index() == _impl->_nframes,
260                "Out of order frames");
261     _impl->_bInFrame = true;
262     _impl->_currIndex = header.index();
263
264     Impl::ModuleList::const_iterator i;
265     for (i = _impl->_modules.begin(); i != _impl->_modules.end(); ++i)
266     {
267         (*i)->frameStarted(header);
268     }
269 }
270
271
272 void
273 AbstractAnalysisData::notifyPointsAdd(const AnalysisDataPointSetRef &points) const
274 {
275     GMX_ASSERT(_impl->_bInData, "notifyDataStart() not called");
276     GMX_ASSERT(_impl->_bInFrame, "notifyFrameStart() not called");
277     GMX_ASSERT(points.lastColumn() < columnCount(), "Invalid columns");
278     GMX_ASSERT(points.frameIndex() == _impl->_currIndex,
279                "Points do not correspond to current frame");
280     if (!_impl->_bAllowMissing && !points.allPresent())
281     {
282         GMX_THROW(APIError("Missing data not supported by a module"));
283     }
284
285     Impl::ModuleList::const_iterator i;
286     for (i = _impl->_modules.begin(); i != _impl->_modules.end(); ++i)
287     {
288         (*i)->pointsAdded(points);
289     }
290 }
291
292
293 void
294 AbstractAnalysisData::notifyFrameFinish(const AnalysisDataFrameHeader &header)
295 {
296     GMX_ASSERT(_impl->_bInData, "notifyDataStart() not called");
297     GMX_ASSERT(_impl->_bInFrame, "notifyFrameStart() not called");
298     GMX_ASSERT(header.index() == _impl->_currIndex,
299                "Header does not correspond to current frame");
300     _impl->_bInFrame = false;
301     _impl->_currIndex = -1;
302
303     // Increment the counter before notifications to allow frame access from
304     // modules.
305     ++_impl->_nframes;
306
307     Impl::ModuleList::const_iterator i;
308     for (i = _impl->_modules.begin(); i != _impl->_modules.end(); ++i)
309     {
310         (*i)->frameFinished(header);
311     }
312 }
313
314
315 void
316 AbstractAnalysisData::notifyDataFinish() const
317 {
318     GMX_RELEASE_ASSERT(_impl->_bInData, "notifyDataStart() not called");
319     GMX_RELEASE_ASSERT(!_impl->_bInFrame,
320                        "notifyDataFinish() called while inside a frame");
321     _impl->_bInData = false;
322
323     Impl::ModuleList::const_iterator i;
324     for (i = _impl->_modules.begin(); i != _impl->_modules.end(); ++i)
325     {
326         (*i)->dataFinished();
327     }
328 }
329 //! \endcond
330
331 } // namespace gmx