2 * This file is part of the GROMACS molecular simulation package.
4 * Copyright (c) 2010,2011,2012,2013, 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 for (int j = 0; j < frame.pointSetCount(); ++j)
139 module->pointsAdded(frame.pointSet(j));
141 module->frameFinished(frame.header());
145 module->dataFinished();
150 /********************************************************************
151 * AbstractAnalysisData
154 AbstractAnalysisData::AbstractAnalysisData()
155 : impl_(new Impl()), columnCount_(0), bMultiPoint_(false)
160 AbstractAnalysisData::~AbstractAnalysisData()
166 AbstractAnalysisData::frameCount() const
168 return impl_->nframes_;
173 AbstractAnalysisData::tryGetDataFrame(int index) const
175 if (index < 0 || index >= frameCount())
177 return AnalysisDataFrameRef();
179 return tryGetDataFrameInternal(index);
184 AbstractAnalysisData::getDataFrame(int index) const
186 AnalysisDataFrameRef frame = tryGetDataFrame(index);
187 if (!frame.isValid())
189 GMX_THROW(APIError("Invalid frame accessed"));
196 AbstractAnalysisData::requestStorage(int nframes)
198 GMX_RELEASE_ASSERT(nframes >= -1, "Invalid number of frames requested");
203 return requestStorageInternal(nframes);
208 AbstractAnalysisData::addModule(AnalysisDataModulePointer module)
210 if ((columnCount() > 1 && !(module->flags() & AnalysisDataModuleInterface::efAllowMulticolumn))
211 || (isMultipoint() && !(module->flags() & AnalysisDataModuleInterface::efAllowMultipoint))
212 || (!isMultipoint() && (module->flags() & AnalysisDataModuleInterface::efOnlyMultipoint)))
214 GMX_THROW(APIError("Data module not compatible with data object properties"));
217 if (impl_->bDataStart_)
219 GMX_RELEASE_ASSERT(!impl_->bInFrame_,
220 "Cannot add data modules in mid-frame");
221 impl_->presentData(this, module.get());
223 if (!(module->flags() & AnalysisDataModuleInterface::efAllowMissing))
225 impl_->bAllowMissing_ = false;
227 impl_->modules_.push_back(module);
232 AbstractAnalysisData::addColumnModule(int col, int span,
233 AnalysisDataModulePointer module)
235 GMX_RELEASE_ASSERT(col >= 0 && span >= 1 && col + span <= columnCount_,
236 "Invalid columns specified for a column module");
237 if (impl_->bDataStart_)
239 GMX_THROW(NotImplementedError("Cannot add column modules after data"));
242 boost::shared_ptr<AnalysisDataProxy> proxy(
243 new AnalysisDataProxy(col, span, this));
244 proxy->addModule(module);
250 AbstractAnalysisData::applyModule(AnalysisDataModuleInterface *module)
252 if ((columnCount() > 1 && !(module->flags() & AnalysisDataModuleInterface::efAllowMulticolumn))
253 || (isMultipoint() && !(module->flags() & AnalysisDataModuleInterface::efAllowMultipoint))
254 || (!isMultipoint() && (module->flags() & AnalysisDataModuleInterface::efOnlyMultipoint)))
256 GMX_THROW(APIError("Data module not compatible with data object properties"));
258 GMX_RELEASE_ASSERT(impl_->bDataStart_ && !impl_->bInData_,
259 "Data module can only be applied to ready data");
261 impl_->presentData(this, module);
266 AbstractAnalysisData::setColumnCount(int columnCount)
268 GMX_RELEASE_ASSERT(columnCount > 0, "Invalid data column count");
269 GMX_RELEASE_ASSERT(columnCount_ == 0 || impl_->modules_.empty(),
270 "Data column count cannot be changed after modules are added");
271 GMX_RELEASE_ASSERT(!impl_->bDataStart_,
272 "Data column count cannot be changed after data has been added");
273 columnCount_ = columnCount;
278 AbstractAnalysisData::setMultipoint(bool multipoint)
280 GMX_RELEASE_ASSERT(impl_->modules_.empty(),
281 "Data type cannot be changed after modules are added");
282 GMX_RELEASE_ASSERT(!impl_->bDataStart_,
283 "Data type cannot be changed after data has been added");
284 bMultiPoint_ = multipoint;
289 * This method is not const because the dataStarted() methods of the attached
290 * modules can request storage of the data.
293 AbstractAnalysisData::notifyDataStart()
295 GMX_RELEASE_ASSERT(!impl_->bDataStart_,
296 "notifyDataStart() called more than once");
297 GMX_RELEASE_ASSERT(columnCount_ > 0, "Data column count is not set");
298 impl_->bDataStart_ = impl_->bInData_ = true;
300 Impl::ModuleList::const_iterator i;
301 for (i = impl_->modules_.begin(); i != impl_->modules_.end(); ++i)
303 if (columnCount_ > 1 && !((*i)->flags() & AnalysisDataModuleInterface::efAllowMulticolumn))
305 GMX_THROW(APIError("Data module not compatible with data object properties"));
307 (*i)->dataStarted(this);
313 AbstractAnalysisData::notifyFrameStart(const AnalysisDataFrameHeader &header) const
315 GMX_ASSERT(impl_->bInData_, "notifyDataStart() not called");
316 GMX_ASSERT(!impl_->bInFrame_,
317 "notifyFrameStart() called while inside a frame");
318 GMX_ASSERT(header.index() == impl_->nframes_,
319 "Out of order frames");
320 impl_->bInFrame_ = true;
321 impl_->currIndex_ = header.index();
323 Impl::ModuleList::const_iterator i;
324 for (i = impl_->modules_.begin(); i != impl_->modules_.end(); ++i)
326 (*i)->frameStarted(header);
332 AbstractAnalysisData::notifyPointsAdd(const AnalysisDataPointSetRef &points) const
334 GMX_ASSERT(impl_->bInData_, "notifyDataStart() not called");
335 GMX_ASSERT(impl_->bInFrame_, "notifyFrameStart() not called");
336 GMX_ASSERT(points.lastColumn() < columnCount(), "Invalid columns");
337 GMX_ASSERT(points.frameIndex() == impl_->currIndex_,
338 "Points do not correspond to current frame");
339 if (!impl_->bAllowMissing_ && !points.allPresent())
341 GMX_THROW(APIError("Missing data not supported by a module"));
344 Impl::ModuleList::const_iterator i;
345 for (i = impl_->modules_.begin(); i != impl_->modules_.end(); ++i)
347 (*i)->pointsAdded(points);
353 AbstractAnalysisData::notifyFrameFinish(const AnalysisDataFrameHeader &header)
355 GMX_ASSERT(impl_->bInData_, "notifyDataStart() not called");
356 GMX_ASSERT(impl_->bInFrame_, "notifyFrameStart() not called");
357 GMX_ASSERT(header.index() == impl_->currIndex_,
358 "Header does not correspond to current frame");
359 impl_->bInFrame_ = false;
360 impl_->currIndex_ = -1;
362 // Increment the counter before notifications to allow frame access from
366 Impl::ModuleList::const_iterator i;
367 for (i = impl_->modules_.begin(); i != impl_->modules_.end(); ++i)
369 (*i)->frameFinished(header);
375 AbstractAnalysisData::notifyDataFinish() const
377 GMX_RELEASE_ASSERT(impl_->bInData_, "notifyDataStart() not called");
378 GMX_RELEASE_ASSERT(!impl_->bInFrame_,
379 "notifyDataFinish() called while inside a frame");
380 impl_->bInData_ = false;
382 Impl::ModuleList::const_iterator i;
383 for (i = impl_->modules_.begin(); i != impl_->modules_.end(); ++i)
385 (*i)->dataFinished();