Merge "Merge fatalerror into utility."
[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/utility/exceptions.h"
41 #include "gromacs/utility/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         // TODO: Check all frames before doing anything for slightly better
77         // exception behavior.
78         if (bCheckMissing && !frame.allPresent())
79         {
80             GMX_THROW(APIError("Missing data not supported by a module"));
81         }
82         module->frameStarted(frame.header());
83         module->pointsAdded(frame.points());
84         module->frameFinished(frame.header());
85     }
86     if (!_bInData)
87     {
88         module->dataFinished();
89     }
90 }
91
92
93 /********************************************************************
94  * AbstractAnalysisData
95  */
96 /*! \cond libapi */
97 AbstractAnalysisData::AbstractAnalysisData()
98     : _impl(new Impl()), _ncol(0), _bMultiPoint(false)
99 {
100 }
101 //! \endcond
102
103 AbstractAnalysisData::~AbstractAnalysisData()
104 {
105 }
106
107
108 int
109 AbstractAnalysisData::frameCount() const
110 {
111     return _impl->_nframes;
112 }
113
114
115 AnalysisDataFrameRef
116 AbstractAnalysisData::tryGetDataFrame(int index) const
117 {
118     if (index < 0 || index >= frameCount())
119     {
120         return AnalysisDataFrameRef();
121     }
122     return tryGetDataFrameInternal(index);
123 }
124
125
126 AnalysisDataFrameRef
127 AbstractAnalysisData::getDataFrame(int index) const
128 {
129     AnalysisDataFrameRef frame = tryGetDataFrame(index);
130     if (!frame.isValid())
131     {
132         GMX_THROW(APIError("Invalid frame accessed"));
133     }
134     return frame;
135 }
136
137
138 bool
139 AbstractAnalysisData::requestStorage(int nframes)
140 {
141     GMX_RELEASE_ASSERT(nframes >= -1, "Invalid number of frames requested");
142     if (nframes == 0)
143     {
144         return true;
145     }
146     return requestStorageInternal(nframes);
147 }
148
149
150 void
151 AbstractAnalysisData::addModule(AnalysisDataModulePointer module)
152 {
153     if ((columnCount() > 1 && !(module->flags() & AnalysisDataModuleInterface::efAllowMulticolumn))
154         || (isMultipoint() && !(module->flags() & AnalysisDataModuleInterface::efAllowMultipoint))
155         || (!isMultipoint() && (module->flags() & AnalysisDataModuleInterface::efOnlyMultipoint)))
156     {
157         GMX_THROW(APIError("Data module not compatible with data object properties"));
158     }
159
160     if (_impl->_bDataStart)
161     {
162         GMX_RELEASE_ASSERT(!_impl->_bInFrame,
163                            "Cannot add data modules in mid-frame");
164         _impl->presentData(this, module.get());
165     }
166     if (!(module->flags() & AnalysisDataModuleInterface::efAllowMissing))
167     {
168         _impl->_bAllowMissing = false;
169     }
170     _impl->_modules.push_back(move(module));
171 }
172
173
174 void
175 AbstractAnalysisData::addColumnModule(int col, int span,
176                                       AnalysisDataModulePointer module)
177 {
178     GMX_RELEASE_ASSERT(col >= 0 && span >= 1 && col + span <= _ncol,
179                        "Invalid columns specified for a column module");
180     if (_impl->_bDataStart)
181     {
182         GMX_THROW(NotImplementedError("Cannot add column modules after data"));
183     }
184
185     boost::shared_ptr<AnalysisDataProxy> proxy(
186             new AnalysisDataProxy(col, span, this));
187     proxy->addModule(module);
188     addModule(proxy);
189 }
190
191
192 void
193 AbstractAnalysisData::applyModule(AnalysisDataModuleInterface *module)
194 {
195     if ((columnCount() > 1 && !(module->flags() & AnalysisDataModuleInterface::efAllowMulticolumn))
196         || (isMultipoint() && !(module->flags() & AnalysisDataModuleInterface::efAllowMultipoint))
197         || (!isMultipoint() && (module->flags() & AnalysisDataModuleInterface::efOnlyMultipoint)))
198     {
199         GMX_THROW(APIError("Data module not compatible with data object properties"));
200     }
201     GMX_RELEASE_ASSERT(_impl->_bDataStart && !_impl->_bInData,
202                        "Data module can only be applied to ready data");
203
204     _impl->presentData(this, module);
205 }
206
207 /*! \cond libapi */
208 void
209 AbstractAnalysisData::setColumnCount(int ncol)
210 {
211     GMX_RELEASE_ASSERT(ncol > 0, "Invalid data column count");
212     GMX_RELEASE_ASSERT(_ncol == 0 || _impl->_modules.empty(),
213                        "Data column count cannot be changed after modules are added");
214     GMX_RELEASE_ASSERT(!_impl->_bDataStart,
215                        "Data column count cannot be changed after data has been added");
216     _ncol = ncol;
217 }
218
219
220 void
221 AbstractAnalysisData::setMultipoint(bool multipoint)
222 {
223     GMX_RELEASE_ASSERT(_impl->_modules.empty(),
224                        "Data type cannot be changed after modules are added");
225     GMX_RELEASE_ASSERT(!_impl->_bDataStart,
226                        "Data type cannot be changed after data has been added");
227     _bMultiPoint = multipoint;
228 }
229
230
231 /*! \internal
232  * This method is not const because the dataStarted() methods of the attached
233  * modules can request storage of the data.
234  */
235 void
236 AbstractAnalysisData::notifyDataStart()
237 {
238     GMX_RELEASE_ASSERT(!_impl->_bDataStart,
239                        "notifyDataStart() called more than once");
240     GMX_RELEASE_ASSERT(_ncol > 0, "Data column count is not set");
241     _impl->_bDataStart = _impl->_bInData = true;
242
243     Impl::ModuleList::const_iterator i;
244     for (i = _impl->_modules.begin(); i != _impl->_modules.end(); ++i)
245     {
246         if (_ncol > 1 && !((*i)->flags() & AnalysisDataModuleInterface::efAllowMulticolumn))
247         {
248             GMX_THROW(APIError("Data module not compatible with data object properties"));
249         }
250         (*i)->dataStarted(this);
251     }
252 }
253
254
255 void
256 AbstractAnalysisData::notifyFrameStart(const AnalysisDataFrameHeader &header) const
257 {
258     GMX_ASSERT(_impl->_bInData, "notifyDataStart() not called");
259     GMX_ASSERT(!_impl->_bInFrame,
260                "notifyFrameStart() called while inside a frame");
261     GMX_ASSERT(header.index() == _impl->_nframes,
262                "Out of order frames");
263     _impl->_bInFrame = true;
264     _impl->_currIndex = header.index();
265
266     Impl::ModuleList::const_iterator i;
267     for (i = _impl->_modules.begin(); i != _impl->_modules.end(); ++i)
268     {
269         (*i)->frameStarted(header);
270     }
271 }
272
273
274 void
275 AbstractAnalysisData::notifyPointsAdd(const AnalysisDataPointSetRef &points) const
276 {
277     GMX_ASSERT(_impl->_bInData, "notifyDataStart() not called");
278     GMX_ASSERT(_impl->_bInFrame, "notifyFrameStart() not called");
279     GMX_ASSERT(points.lastColumn() < columnCount(), "Invalid columns");
280     GMX_ASSERT(points.frameIndex() == _impl->_currIndex,
281                "Points do not correspond to current frame");
282     if (!_impl->_bAllowMissing && !points.allPresent())
283     {
284         GMX_THROW(APIError("Missing data not supported by a module"));
285     }
286
287     Impl::ModuleList::const_iterator i;
288     for (i = _impl->_modules.begin(); i != _impl->_modules.end(); ++i)
289     {
290         (*i)->pointsAdded(points);
291     }
292 }
293
294
295 void
296 AbstractAnalysisData::notifyFrameFinish(const AnalysisDataFrameHeader &header)
297 {
298     GMX_ASSERT(_impl->_bInData, "notifyDataStart() not called");
299     GMX_ASSERT(_impl->_bInFrame, "notifyFrameStart() not called");
300     GMX_ASSERT(header.index() == _impl->_currIndex,
301                "Header does not correspond to current frame");
302     _impl->_bInFrame = false;
303     _impl->_currIndex = -1;
304
305     // Increment the counter before notifications to allow frame access from
306     // modules.
307     ++_impl->_nframes;
308
309     Impl::ModuleList::const_iterator i;
310     for (i = _impl->_modules.begin(); i != _impl->_modules.end(); ++i)
311     {
312         (*i)->frameFinished(header);
313     }
314 }
315
316
317 void
318 AbstractAnalysisData::notifyDataFinish() const
319 {
320     GMX_RELEASE_ASSERT(_impl->_bInData, "notifyDataStart() not called");
321     GMX_RELEASE_ASSERT(!_impl->_bInFrame,
322                        "notifyDataFinish() called while inside a frame");
323     _impl->_bInData = false;
324
325     Impl::ModuleList::const_iterator i;
326     for (i = _impl->_modules.begin(); i != _impl->_modules.end(); ++i)
327     {
328         (*i)->dataFinished();
329     }
330 }
331 //! \endcond
332
333 } // namespace gmx