2 * This file is part of the GROMACS molecular simulation package.
4 * Copyright (c) 2010,2011,2012, by the GROMACS development team, led by
5 * David van der Spoel, Berk Hess, Erik Lindahl, and including many
6 * others, as listed in the AUTHORS file in the top-level source
7 * directory and at http://www.gromacs.org.
9 * GROMACS is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU Lesser General Public License
11 * as published by the Free Software Foundation; either version 2.1
12 * of the License, or (at your option) any later version.
14 * GROMACS is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 * Lesser General Public License for more details.
19 * You should have received a copy of the GNU Lesser General Public
20 * License along with GROMACS; if not, see
21 * http://www.gnu.org/licenses, or write to the Free Software Foundation,
22 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
24 * If you want to redistribute modifications to GROMACS, please
25 * consider that scientific software is very special. Version
26 * control is crucial - bugs must be traceable. We will be happy to
27 * consider code for inclusion in the official distribution, but
28 * derived work must not be called official GROMACS. Details are found
29 * in the README & COPYING files - if they are missing, get the
30 * official version at http://www.gromacs.org.
32 * To help us fund GROMACS development, we humbly ask that you cite
33 * the research papers on the package. Check out http://www.gromacs.org.
37 * Implements gmx::AbstractAnalysisData.
39 * \author Teemu Murtola <teemu.murtola@gmail.com>
40 * \ingroup module_analysisdata
42 #include "gromacs/analysisdata/abstractdata.h"
46 #include "gromacs/analysisdata/datamodule.h"
47 #include "gromacs/utility/exceptions.h"
48 #include "gromacs/utility/gmxassert.h"
49 #include "gromacs/utility/uniqueptr.h"
51 #include "dataframe.h"
52 #include "dataproxy.h"
57 /********************************************************************
58 * AbstractAnalysisData::Impl
62 * Private implementation class for AbstractAnalysisData.
64 * \ingroup module_analysisdata
66 class AbstractAnalysisData::Impl
69 //! Shorthand for list of modules added to the data.
70 typedef std::vector<AnalysisDataModulePointer> ModuleList;
75 * Present data already added to the data object to a module.
77 * \param[in] data Data object to read data from.
78 * \param[in] module Module to present the data to.
79 * \throws APIError if \p module is not compatible with the data
81 * \throws APIError if all data is not available through
83 * \throws unspecified Any exception thrown by \p module in its data
84 * notification methods.
86 * Uses getDataFrame() in \p data to access all data in the object, and
87 * calls the notification functions in \p module as if the module had
88 * been registered to the data object when the data was added.
90 void presentData(AbstractAnalysisData *data,
91 AnalysisDataModuleInterface *module);
93 //! List of modules added to the data.
95 //! true if all modules support missing data.
97 //! Whether notifyDataStart() has been called.
98 mutable bool bDataStart_;
99 //! Whether new data is being added.
100 mutable bool bInData_;
101 //! Whether data for a frame is being added.
102 mutable bool bInFrame_;
103 //! Index of the currently active frame.
104 mutable int currIndex_;
106 * Total number of frames in the data.
108 * The counter is incremented in notifyFrameFinish().
113 AbstractAnalysisData::Impl::Impl()
114 : bAllowMissing_(true), bDataStart_(false), bInData_(false), bInFrame_(false),
115 currIndex_(-1), nframes_(0)
120 AbstractAnalysisData::Impl::presentData(AbstractAnalysisData *data,
121 AnalysisDataModuleInterface *module)
123 module->dataStarted(data);
124 bool bCheckMissing = bAllowMissing_
125 && !(module->flags() & AnalysisDataModuleInterface::efAllowMissing);
126 for (int i = 0; i < data->frameCount(); ++i)
128 AnalysisDataFrameRef frame = data->getDataFrame(i);
129 GMX_RELEASE_ASSERT(frame.isValid(), "Invalid data frame returned");
130 // TODO: Check all frames before doing anything for slightly better
131 // exception behavior.
132 if (bCheckMissing && !frame.allPresent())
134 GMX_THROW(APIError("Missing data not supported by a module"));
136 module->frameStarted(frame.header());
137 module->pointsAdded(frame.points());
138 module->frameFinished(frame.header());
142 module->dataFinished();
147 /********************************************************************
148 * AbstractAnalysisData
151 AbstractAnalysisData::AbstractAnalysisData()
152 : impl_(new Impl()), columnCount_(0), bMultiPoint_(false)
157 AbstractAnalysisData::~AbstractAnalysisData()
163 AbstractAnalysisData::frameCount() const
165 return impl_->nframes_;
170 AbstractAnalysisData::tryGetDataFrame(int index) const
172 if (index < 0 || index >= frameCount())
174 return AnalysisDataFrameRef();
176 return tryGetDataFrameInternal(index);
181 AbstractAnalysisData::getDataFrame(int index) const
183 AnalysisDataFrameRef frame = tryGetDataFrame(index);
184 if (!frame.isValid())
186 GMX_THROW(APIError("Invalid frame accessed"));
193 AbstractAnalysisData::requestStorage(int nframes)
195 GMX_RELEASE_ASSERT(nframes >= -1, "Invalid number of frames requested");
200 return requestStorageInternal(nframes);
205 AbstractAnalysisData::addModule(AnalysisDataModulePointer module)
207 if ((columnCount() > 1 && !(module->flags() & AnalysisDataModuleInterface::efAllowMulticolumn))
208 || (isMultipoint() && !(module->flags() & AnalysisDataModuleInterface::efAllowMultipoint))
209 || (!isMultipoint() && (module->flags() & AnalysisDataModuleInterface::efOnlyMultipoint)))
211 GMX_THROW(APIError("Data module not compatible with data object properties"));
214 if (impl_->bDataStart_)
216 GMX_RELEASE_ASSERT(!impl_->bInFrame_,
217 "Cannot add data modules in mid-frame");
218 impl_->presentData(this, module.get());
220 if (!(module->flags() & AnalysisDataModuleInterface::efAllowMissing))
222 impl_->bAllowMissing_ = false;
224 impl_->modules_.push_back(module);
229 AbstractAnalysisData::addColumnModule(int col, int span,
230 AnalysisDataModulePointer module)
232 GMX_RELEASE_ASSERT(col >= 0 && span >= 1 && col + span <= columnCount_,
233 "Invalid columns specified for a column module");
234 if (impl_->bDataStart_)
236 GMX_THROW(NotImplementedError("Cannot add column modules after data"));
239 boost::shared_ptr<AnalysisDataProxy> proxy(
240 new AnalysisDataProxy(col, span, this));
241 proxy->addModule(module);
247 AbstractAnalysisData::applyModule(AnalysisDataModuleInterface *module)
249 if ((columnCount() > 1 && !(module->flags() & AnalysisDataModuleInterface::efAllowMulticolumn))
250 || (isMultipoint() && !(module->flags() & AnalysisDataModuleInterface::efAllowMultipoint))
251 || (!isMultipoint() && (module->flags() & AnalysisDataModuleInterface::efOnlyMultipoint)))
253 GMX_THROW(APIError("Data module not compatible with data object properties"));
255 GMX_RELEASE_ASSERT(impl_->bDataStart_ && !impl_->bInData_,
256 "Data module can only be applied to ready data");
258 impl_->presentData(this, module);
263 AbstractAnalysisData::setColumnCount(int columnCount)
265 GMX_RELEASE_ASSERT(columnCount > 0, "Invalid data column count");
266 GMX_RELEASE_ASSERT(columnCount_ == 0 || impl_->modules_.empty(),
267 "Data column count cannot be changed after modules are added");
268 GMX_RELEASE_ASSERT(!impl_->bDataStart_,
269 "Data column count cannot be changed after data has been added");
270 columnCount_ = columnCount;
275 AbstractAnalysisData::setMultipoint(bool multipoint)
277 GMX_RELEASE_ASSERT(impl_->modules_.empty(),
278 "Data type cannot be changed after modules are added");
279 GMX_RELEASE_ASSERT(!impl_->bDataStart_,
280 "Data type cannot be changed after data has been added");
281 bMultiPoint_ = multipoint;
286 * This method is not const because the dataStarted() methods of the attached
287 * modules can request storage of the data.
290 AbstractAnalysisData::notifyDataStart()
292 GMX_RELEASE_ASSERT(!impl_->bDataStart_,
293 "notifyDataStart() called more than once");
294 GMX_RELEASE_ASSERT(columnCount_ > 0, "Data column count is not set");
295 impl_->bDataStart_ = impl_->bInData_ = true;
297 Impl::ModuleList::const_iterator i;
298 for (i = impl_->modules_.begin(); i != impl_->modules_.end(); ++i)
300 if (columnCount_ > 1 && !((*i)->flags() & AnalysisDataModuleInterface::efAllowMulticolumn))
302 GMX_THROW(APIError("Data module not compatible with data object properties"));
304 (*i)->dataStarted(this);
310 AbstractAnalysisData::notifyFrameStart(const AnalysisDataFrameHeader &header) const
312 GMX_ASSERT(impl_->bInData_, "notifyDataStart() not called");
313 GMX_ASSERT(!impl_->bInFrame_,
314 "notifyFrameStart() called while inside a frame");
315 GMX_ASSERT(header.index() == impl_->nframes_,
316 "Out of order frames");
317 impl_->bInFrame_ = true;
318 impl_->currIndex_ = header.index();
320 Impl::ModuleList::const_iterator i;
321 for (i = impl_->modules_.begin(); i != impl_->modules_.end(); ++i)
323 (*i)->frameStarted(header);
329 AbstractAnalysisData::notifyPointsAdd(const AnalysisDataPointSetRef &points) const
331 GMX_ASSERT(impl_->bInData_, "notifyDataStart() not called");
332 GMX_ASSERT(impl_->bInFrame_, "notifyFrameStart() not called");
333 GMX_ASSERT(points.lastColumn() < columnCount(), "Invalid columns");
334 GMX_ASSERT(points.frameIndex() == impl_->currIndex_,
335 "Points do not correspond to current frame");
336 if (!impl_->bAllowMissing_ && !points.allPresent())
338 GMX_THROW(APIError("Missing data not supported by a module"));
341 Impl::ModuleList::const_iterator i;
342 for (i = impl_->modules_.begin(); i != impl_->modules_.end(); ++i)
344 (*i)->pointsAdded(points);
350 AbstractAnalysisData::notifyFrameFinish(const AnalysisDataFrameHeader &header)
352 GMX_ASSERT(impl_->bInData_, "notifyDataStart() not called");
353 GMX_ASSERT(impl_->bInFrame_, "notifyFrameStart() not called");
354 GMX_ASSERT(header.index() == impl_->currIndex_,
355 "Header does not correspond to current frame");
356 impl_->bInFrame_ = false;
357 impl_->currIndex_ = -1;
359 // Increment the counter before notifications to allow frame access from
363 Impl::ModuleList::const_iterator i;
364 for (i = impl_->modules_.begin(); i != impl_->modules_.end(); ++i)
366 (*i)->frameFinished(header);
372 AbstractAnalysisData::notifyDataFinish() const
374 GMX_RELEASE_ASSERT(impl_->bInData_, "notifyDataStart() not called");
375 GMX_RELEASE_ASSERT(!impl_->bInFrame_,
376 "notifyDataFinish() called while inside a frame");
377 impl_->bInData_ = false;
379 Impl::ModuleList::const_iterator i;
380 for (i = impl_->modules_.begin(); i != impl_->modules_.end(); ++i)
382 (*i)->dataFinished();