Update my e-mail address on author lines.
[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@gmail.com>
36  * \ingroup module_analysisdata
37  */
38 #include "gromacs/analysisdata/abstractdata.h"
39
40 #include <vector>
41
42 #include "gromacs/analysisdata/datamodule.h"
43 #include "gromacs/utility/exceptions.h"
44 #include "gromacs/utility/gmxassert.h"
45 #include "gromacs/utility/uniqueptr.h"
46
47 #include "dataframe.h"
48 #include "dataproxy.h"
49
50 namespace gmx
51 {
52
53 /********************************************************************
54  * AbstractAnalysisData::Impl
55  */
56
57 /*! \internal \brief
58  * Private implementation class for AbstractAnalysisData.
59  *
60  * \ingroup module_analysisdata
61  */
62 class AbstractAnalysisData::Impl
63 {
64     public:
65         //! Shorthand for list of modules added to the data.
66         typedef std::vector<AnalysisDataModulePointer> ModuleList;
67
68         Impl();
69
70         /*! \brief
71          * Present data already added to the data object to a module.
72          *
73          * \param[in] data   Data object to read data from.
74          * \param[in] module Module to present the data to.
75          * \throws    APIError if \p module is not compatible with the data
76          *      object.
77          * \throws    APIError if all data is not available through
78          *      getDataFrame().
79          * \throws    unspecified Any exception thrown by \p module in its data
80          *      notification methods.
81          *
82          * Uses getDataFrame() in \p data to access all data in the object, and
83          * calls the notification functions in \p module as if the module had
84          * been registered to the data object when the data was added.
85          */
86         void presentData(AbstractAnalysisData        *data,
87                          AnalysisDataModuleInterface *module);
88
89         //! List of modules added to the data.
90         ModuleList              modules_;
91         //! true if all modules support missing data.
92         bool                    bAllowMissing_;
93         //! Whether notifyDataStart() has been called.
94         mutable bool            bDataStart_;
95         //! Whether new data is being added.
96         mutable bool            bInData_;
97         //! Whether data for a frame is being added.
98         mutable bool            bInFrame_;
99         //! Index of the currently active frame.
100         mutable int             currIndex_;
101         /*! \brief
102          * Total number of frames in the data.
103          *
104          * The counter is incremented in notifyFrameFinish().
105          */
106         int                     nframes_;
107 };
108
109 AbstractAnalysisData::Impl::Impl()
110     : bAllowMissing_(true), bDataStart_(false), bInData_(false), bInFrame_(false),
111       currIndex_(-1), nframes_(0)
112 {
113 }
114
115 void
116 AbstractAnalysisData::Impl::presentData(AbstractAnalysisData        *data,
117                                         AnalysisDataModuleInterface *module)
118 {
119     module->dataStarted(data);
120     bool bCheckMissing = bAllowMissing_
121         && !(module->flags() & AnalysisDataModuleInterface::efAllowMissing);
122     for (int i = 0; i < data->frameCount(); ++i)
123     {
124         AnalysisDataFrameRef frame = data->getDataFrame(i);
125         GMX_RELEASE_ASSERT(frame.isValid(), "Invalid data frame returned");
126         // TODO: Check all frames before doing anything for slightly better
127         // exception behavior.
128         if (bCheckMissing && !frame.allPresent())
129         {
130             GMX_THROW(APIError("Missing data not supported by a module"));
131         }
132         module->frameStarted(frame.header());
133         module->pointsAdded(frame.points());
134         module->frameFinished(frame.header());
135     }
136     if (!bInData_)
137     {
138         module->dataFinished();
139     }
140 }
141
142
143 /********************************************************************
144  * AbstractAnalysisData
145  */
146 /*! \cond libapi */
147 AbstractAnalysisData::AbstractAnalysisData()
148     : impl_(new Impl()), columnCount_(0), bMultiPoint_(false)
149 {
150 }
151 //! \endcond
152
153 AbstractAnalysisData::~AbstractAnalysisData()
154 {
155 }
156
157
158 int
159 AbstractAnalysisData::frameCount() const
160 {
161     return impl_->nframes_;
162 }
163
164
165 AnalysisDataFrameRef
166 AbstractAnalysisData::tryGetDataFrame(int index) const
167 {
168     if (index < 0 || index >= frameCount())
169     {
170         return AnalysisDataFrameRef();
171     }
172     return tryGetDataFrameInternal(index);
173 }
174
175
176 AnalysisDataFrameRef
177 AbstractAnalysisData::getDataFrame(int index) const
178 {
179     AnalysisDataFrameRef frame = tryGetDataFrame(index);
180     if (!frame.isValid())
181     {
182         GMX_THROW(APIError("Invalid frame accessed"));
183     }
184     return frame;
185 }
186
187
188 bool
189 AbstractAnalysisData::requestStorage(int nframes)
190 {
191     GMX_RELEASE_ASSERT(nframes >= -1, "Invalid number of frames requested");
192     if (nframes == 0)
193     {
194         return true;
195     }
196     return requestStorageInternal(nframes);
197 }
198
199
200 void
201 AbstractAnalysisData::addModule(AnalysisDataModulePointer module)
202 {
203     if ((columnCount() > 1 && !(module->flags() & AnalysisDataModuleInterface::efAllowMulticolumn))
204         || (isMultipoint() && !(module->flags() & AnalysisDataModuleInterface::efAllowMultipoint))
205         || (!isMultipoint() && (module->flags() & AnalysisDataModuleInterface::efOnlyMultipoint)))
206     {
207         GMX_THROW(APIError("Data module not compatible with data object properties"));
208     }
209
210     if (impl_->bDataStart_)
211     {
212         GMX_RELEASE_ASSERT(!impl_->bInFrame_,
213                            "Cannot add data modules in mid-frame");
214         impl_->presentData(this, module.get());
215     }
216     if (!(module->flags() & AnalysisDataModuleInterface::efAllowMissing))
217     {
218         impl_->bAllowMissing_ = false;
219     }
220     impl_->modules_.push_back(module);
221 }
222
223
224 void
225 AbstractAnalysisData::addColumnModule(int col, int span,
226                                       AnalysisDataModulePointer module)
227 {
228     GMX_RELEASE_ASSERT(col >= 0 && span >= 1 && col + span <= columnCount_,
229                        "Invalid columns specified for a column module");
230     if (impl_->bDataStart_)
231     {
232         GMX_THROW(NotImplementedError("Cannot add column modules after data"));
233     }
234
235     boost::shared_ptr<AnalysisDataProxy> proxy(
236             new AnalysisDataProxy(col, span, this));
237     proxy->addModule(module);
238     addModule(proxy);
239 }
240
241
242 void
243 AbstractAnalysisData::applyModule(AnalysisDataModuleInterface *module)
244 {
245     if ((columnCount() > 1 && !(module->flags() & AnalysisDataModuleInterface::efAllowMulticolumn))
246         || (isMultipoint() && !(module->flags() & AnalysisDataModuleInterface::efAllowMultipoint))
247         || (!isMultipoint() && (module->flags() & AnalysisDataModuleInterface::efOnlyMultipoint)))
248     {
249         GMX_THROW(APIError("Data module not compatible with data object properties"));
250     }
251     GMX_RELEASE_ASSERT(impl_->bDataStart_ && !impl_->bInData_,
252                        "Data module can only be applied to ready data");
253
254     impl_->presentData(this, module);
255 }
256
257 /*! \cond libapi */
258 void
259 AbstractAnalysisData::setColumnCount(int columnCount)
260 {
261     GMX_RELEASE_ASSERT(columnCount > 0, "Invalid data column count");
262     GMX_RELEASE_ASSERT(columnCount_ == 0 || impl_->modules_.empty(),
263                        "Data column count cannot be changed after modules are added");
264     GMX_RELEASE_ASSERT(!impl_->bDataStart_,
265                        "Data column count cannot be changed after data has been added");
266     columnCount_ = columnCount;
267 }
268
269
270 void
271 AbstractAnalysisData::setMultipoint(bool multipoint)
272 {
273     GMX_RELEASE_ASSERT(impl_->modules_.empty(),
274                        "Data type cannot be changed after modules are added");
275     GMX_RELEASE_ASSERT(!impl_->bDataStart_,
276                        "Data type cannot be changed after data has been added");
277     bMultiPoint_ = multipoint;
278 }
279
280
281 /*! \internal
282  * This method is not const because the dataStarted() methods of the attached
283  * modules can request storage of the data.
284  */
285 void
286 AbstractAnalysisData::notifyDataStart()
287 {
288     GMX_RELEASE_ASSERT(!impl_->bDataStart_,
289                        "notifyDataStart() called more than once");
290     GMX_RELEASE_ASSERT(columnCount_ > 0, "Data column count is not set");
291     impl_->bDataStart_ = impl_->bInData_ = true;
292
293     Impl::ModuleList::const_iterator i;
294     for (i = impl_->modules_.begin(); i != impl_->modules_.end(); ++i)
295     {
296         if (columnCount_ > 1 && !((*i)->flags() & AnalysisDataModuleInterface::efAllowMulticolumn))
297         {
298             GMX_THROW(APIError("Data module not compatible with data object properties"));
299         }
300         (*i)->dataStarted(this);
301     }
302 }
303
304
305 void
306 AbstractAnalysisData::notifyFrameStart(const AnalysisDataFrameHeader &header) const
307 {
308     GMX_ASSERT(impl_->bInData_, "notifyDataStart() not called");
309     GMX_ASSERT(!impl_->bInFrame_,
310                "notifyFrameStart() called while inside a frame");
311     GMX_ASSERT(header.index() == impl_->nframes_,
312                "Out of order frames");
313     impl_->bInFrame_  = true;
314     impl_->currIndex_ = header.index();
315
316     Impl::ModuleList::const_iterator i;
317     for (i = impl_->modules_.begin(); i != impl_->modules_.end(); ++i)
318     {
319         (*i)->frameStarted(header);
320     }
321 }
322
323
324 void
325 AbstractAnalysisData::notifyPointsAdd(const AnalysisDataPointSetRef &points) const
326 {
327     GMX_ASSERT(impl_->bInData_, "notifyDataStart() not called");
328     GMX_ASSERT(impl_->bInFrame_, "notifyFrameStart() not called");
329     GMX_ASSERT(points.lastColumn() < columnCount(), "Invalid columns");
330     GMX_ASSERT(points.frameIndex() == impl_->currIndex_,
331                "Points do not correspond to current frame");
332     if (!impl_->bAllowMissing_ && !points.allPresent())
333     {
334         GMX_THROW(APIError("Missing data not supported by a module"));
335     }
336
337     Impl::ModuleList::const_iterator i;
338     for (i = impl_->modules_.begin(); i != impl_->modules_.end(); ++i)
339     {
340         (*i)->pointsAdded(points);
341     }
342 }
343
344
345 void
346 AbstractAnalysisData::notifyFrameFinish(const AnalysisDataFrameHeader &header)
347 {
348     GMX_ASSERT(impl_->bInData_, "notifyDataStart() not called");
349     GMX_ASSERT(impl_->bInFrame_, "notifyFrameStart() not called");
350     GMX_ASSERT(header.index() == impl_->currIndex_,
351                "Header does not correspond to current frame");
352     impl_->bInFrame_  = false;
353     impl_->currIndex_ = -1;
354
355     // Increment the counter before notifications to allow frame access from
356     // modules.
357     ++impl_->nframes_;
358
359     Impl::ModuleList::const_iterator i;
360     for (i = impl_->modules_.begin(); i != impl_->modules_.end(); ++i)
361     {
362         (*i)->frameFinished(header);
363     }
364 }
365
366
367 void
368 AbstractAnalysisData::notifyDataFinish() const
369 {
370     GMX_RELEASE_ASSERT(impl_->bInData_, "notifyDataStart() not called");
371     GMX_RELEASE_ASSERT(!impl_->bInFrame_,
372                        "notifyDataFinish() called while inside a frame");
373     impl_->bInData_ = false;
374
375     Impl::ModuleList::const_iterator i;
376     for (i = impl_->modules_.begin(); i != impl_->modules_.end(); ++i)
377     {
378         (*i)->dataFinished();
379     }
380 }
381 //! \endcond
382
383 } // namespace gmx