3 * This source code is part of
7 * GROningen MAchine for Chemical Simulations
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.
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.
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.
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.
29 * For more info, check our website at http://www.gromacs.org
33 * Implements gmx::AbstractAnalysisData.
35 * \author Teemu Murtola <teemu.murtola@cbr.su.se>
36 * \ingroup module_analysisdata
38 #include "gromacs/analysisdata/abstractdata.h"
42 #include "gromacs/analysisdata/datamodule.h"
43 #include "gromacs/utility/exceptions.h"
44 #include "gromacs/utility/gmxassert.h"
45 #include "gromacs/utility/uniqueptr.h"
47 #include "dataframe.h"
48 #include "dataproxy.h"
53 /********************************************************************
54 * AbstractAnalysisData::Impl
58 * Private implementation class for AbstractAnalysisData.
60 * \ingroup module_analysisdata
62 class AbstractAnalysisData::Impl
65 //! Shorthand for list of modules added to the data.
66 typedef std::vector<AnalysisDataModulePointer> ModuleList;
71 * Present data already added to the data object to a module.
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
77 * \throws APIError if all data is not available through
79 * \throws unspecified Any exception thrown by \p module in its data
80 * notification methods.
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.
86 void presentData(AbstractAnalysisData *data,
87 AnalysisDataModuleInterface *module);
89 //! List of modules added to the data.
91 //! true if all modules support missing data.
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;
102 * Total number of frames in the data.
104 * The counter is incremented in notifyFrameFinish().
109 AbstractAnalysisData::Impl::Impl()
110 : _bAllowMissing(true), _bDataStart(false), _bInData(false), _bInFrame(false),
111 _currIndex(-1), _nframes(0)
116 AbstractAnalysisData::Impl::presentData(AbstractAnalysisData *data,
117 AnalysisDataModuleInterface *module)
119 module->dataStarted(data);
120 bool bCheckMissing = _bAllowMissing
121 && !(module->flags() & AnalysisDataModuleInterface::efAllowMissing);
122 for (int i = 0; i < data->frameCount(); ++i)
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())
130 GMX_THROW(APIError("Missing data not supported by a module"));
132 module->frameStarted(frame.header());
133 module->pointsAdded(frame.points());
134 module->frameFinished(frame.header());
138 module->dataFinished();
143 /********************************************************************
144 * AbstractAnalysisData
147 AbstractAnalysisData::AbstractAnalysisData()
148 : _impl(new Impl()), _ncol(0), _bMultiPoint(false)
153 AbstractAnalysisData::~AbstractAnalysisData()
159 AbstractAnalysisData::frameCount() const
161 return _impl->_nframes;
166 AbstractAnalysisData::tryGetDataFrame(int index) const
168 if (index < 0 || index >= frameCount())
170 return AnalysisDataFrameRef();
172 return tryGetDataFrameInternal(index);
177 AbstractAnalysisData::getDataFrame(int index) const
179 AnalysisDataFrameRef frame = tryGetDataFrame(index);
180 if (!frame.isValid())
182 GMX_THROW(APIError("Invalid frame accessed"));
189 AbstractAnalysisData::requestStorage(int nframes)
191 GMX_RELEASE_ASSERT(nframes >= -1, "Invalid number of frames requested");
196 return requestStorageInternal(nframes);
201 AbstractAnalysisData::addModule(AnalysisDataModulePointer module)
203 if ((columnCount() > 1 && !(module->flags() & AnalysisDataModuleInterface::efAllowMulticolumn))
204 || (isMultipoint() && !(module->flags() & AnalysisDataModuleInterface::efAllowMultipoint))
205 || (!isMultipoint() && (module->flags() & AnalysisDataModuleInterface::efOnlyMultipoint)))
207 GMX_THROW(APIError("Data module not compatible with data object properties"));
210 if (_impl->_bDataStart)
212 GMX_RELEASE_ASSERT(!_impl->_bInFrame,
213 "Cannot add data modules in mid-frame");
214 _impl->presentData(this, module.get());
216 if (!(module->flags() & AnalysisDataModuleInterface::efAllowMissing))
218 _impl->_bAllowMissing = false;
220 _impl->_modules.push_back(move(module));
225 AbstractAnalysisData::addColumnModule(int col, int span,
226 AnalysisDataModulePointer module)
228 GMX_RELEASE_ASSERT(col >= 0 && span >= 1 && col + span <= _ncol,
229 "Invalid columns specified for a column module");
230 if (_impl->_bDataStart)
232 GMX_THROW(NotImplementedError("Cannot add column modules after data"));
235 boost::shared_ptr<AnalysisDataProxy> proxy(
236 new AnalysisDataProxy(col, span, this));
237 proxy->addModule(module);
243 AbstractAnalysisData::applyModule(AnalysisDataModuleInterface *module)
245 if ((columnCount() > 1 && !(module->flags() & AnalysisDataModuleInterface::efAllowMulticolumn))
246 || (isMultipoint() && !(module->flags() & AnalysisDataModuleInterface::efAllowMultipoint))
247 || (!isMultipoint() && (module->flags() & AnalysisDataModuleInterface::efOnlyMultipoint)))
249 GMX_THROW(APIError("Data module not compatible with data object properties"));
251 GMX_RELEASE_ASSERT(_impl->_bDataStart && !_impl->_bInData,
252 "Data module can only be applied to ready data");
254 _impl->presentData(this, module);
259 AbstractAnalysisData::setColumnCount(int ncol)
261 GMX_RELEASE_ASSERT(ncol > 0, "Invalid data column count");
262 GMX_RELEASE_ASSERT(_ncol == 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");
271 AbstractAnalysisData::setMultipoint(bool multipoint)
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;
282 * This method is not const because the dataStarted() methods of the attached
283 * modules can request storage of the data.
286 AbstractAnalysisData::notifyDataStart()
288 GMX_RELEASE_ASSERT(!_impl->_bDataStart,
289 "notifyDataStart() called more than once");
290 GMX_RELEASE_ASSERT(_ncol > 0, "Data column count is not set");
291 _impl->_bDataStart = _impl->_bInData = true;
293 Impl::ModuleList::const_iterator i;
294 for (i = _impl->_modules.begin(); i != _impl->_modules.end(); ++i)
296 if (_ncol > 1 && !((*i)->flags() & AnalysisDataModuleInterface::efAllowMulticolumn))
298 GMX_THROW(APIError("Data module not compatible with data object properties"));
300 (*i)->dataStarted(this);
306 AbstractAnalysisData::notifyFrameStart(const AnalysisDataFrameHeader &header) const
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();
316 Impl::ModuleList::const_iterator i;
317 for (i = _impl->_modules.begin(); i != _impl->_modules.end(); ++i)
319 (*i)->frameStarted(header);
325 AbstractAnalysisData::notifyPointsAdd(const AnalysisDataPointSetRef &points) const
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())
334 GMX_THROW(APIError("Missing data not supported by a module"));
337 Impl::ModuleList::const_iterator i;
338 for (i = _impl->_modules.begin(); i != _impl->_modules.end(); ++i)
340 (*i)->pointsAdded(points);
346 AbstractAnalysisData::notifyFrameFinish(const AnalysisDataFrameHeader &header)
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;
355 // Increment the counter before notifications to allow frame access from
359 Impl::ModuleList::const_iterator i;
360 for (i = _impl->_modules.begin(); i != _impl->_modules.end(); ++i)
362 (*i)->frameFinished(header);
368 AbstractAnalysisData::notifyDataFinish() const
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;
375 Impl::ModuleList::const_iterator i;
376 for (i = _impl->_modules.begin(); i != _impl->_modules.end(); ++i)
378 (*i)->dataFinished();