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