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;
74 //! Returns whether any data set has more than one column.
75 bool isMultiColumn() const;
78 * Checks whether a module is compatible with the data properties.
80 * \param[in] module Module to check.
81 * \throws APIError if \p module is not compatible with the data.
83 * Does not check the actual data (e.g., missing values), but only the
84 * dimensionality and other preset properties of the data.
86 void checkModuleProperties(const AnalysisDataModuleInterface &module) const;
89 * Present data already added to the data object to a module.
91 * \param[in] data Data object to read data from.
92 * \param[in] module Module to present the data to.
93 * \throws APIError if \p module is not compatible with the data.
94 * \throws APIError if all data is not available through
96 * \throws unspecified Any exception thrown by \p module in its data
97 * notification methods.
99 * Uses getDataFrame() in \p data to access all data in the object, and
100 * calls the notification functions in \p module as if the module had
101 * been registered to the data object when the data was added.
103 void presentData(AbstractAnalysisData *data,
104 AnalysisDataModuleInterface *module);
106 //! Column counts for each data set in the data.
107 std::vector<int> columnCounts_;
108 //! Whether the data is multipoint.
110 //! List of modules added to the data.
112 //! true if all modules support missing data.
114 //! Whether notifyDataStart() has been called.
115 mutable bool bDataStart_;
116 //! Whether new data is being added.
117 mutable bool bInData_;
118 //! Whether data for a frame is being added.
119 mutable bool bInFrame_;
120 //! Index of the currently active frame.
121 mutable int currIndex_;
123 * Total number of frames in the data.
125 * The counter is incremented in notifyFrameFinish().
130 AbstractAnalysisData::Impl::Impl()
131 : bMultipoint_(false), bAllowMissing_(true),
132 bDataStart_(false), bInData_(false), bInFrame_(false),
133 currIndex_(-1), nframes_(0)
135 columnCounts_.push_back(0);
139 AbstractAnalysisData::Impl::isMultiColumn() const
141 std::vector<int>::const_iterator i;
142 for (i = columnCounts_.begin(); i != columnCounts_.end(); ++i)
152 //! Helper macro for testing module flags.
153 #define TEST_MODULE_FLAG(flags, flagname) \
154 ((flags) & AnalysisDataModuleInterface::flagname)
156 AbstractAnalysisData::Impl::checkModuleProperties(
157 const AnalysisDataModuleInterface &module) const
159 const int flags = module.flags();
160 if ((!TEST_MODULE_FLAG(flags, efAllowMulticolumn) && isMultiColumn()) ||
161 (!TEST_MODULE_FLAG(flags, efAllowMultipoint) && bMultipoint_) ||
162 ( TEST_MODULE_FLAG(flags, efOnlyMultipoint) && !bMultipoint_) ||
163 (!TEST_MODULE_FLAG(flags, efAllowMultipleDataSets)
164 && columnCounts_.size() > 1U))
166 GMX_THROW(APIError("Data module not compatible with data object properties"));
169 #undef TEST_MODULE_FLAGS
172 AbstractAnalysisData::Impl::presentData(AbstractAnalysisData *data,
173 AnalysisDataModuleInterface *module)
175 module->dataStarted(data);
176 bool bCheckMissing = bAllowMissing_
177 && !(module->flags() & AnalysisDataModuleInterface::efAllowMissing);
178 for (int i = 0; i < data->frameCount(); ++i)
180 AnalysisDataFrameRef frame = data->getDataFrame(i);
181 GMX_RELEASE_ASSERT(frame.isValid(), "Invalid data frame returned");
182 // TODO: Check all frames before doing anything for slightly better
183 // exception behavior.
184 if (bCheckMissing && !frame.allPresent())
186 GMX_THROW(APIError("Missing data not supported by a module"));
188 module->frameStarted(frame.header());
189 for (int j = 0; j < frame.pointSetCount(); ++j)
191 module->pointsAdded(frame.pointSet(j));
193 module->frameFinished(frame.header());
197 module->dataFinished();
202 /********************************************************************
203 * AbstractAnalysisData
206 AbstractAnalysisData::AbstractAnalysisData()
212 AbstractAnalysisData::~AbstractAnalysisData()
217 AbstractAnalysisData::isMultipoint() const
219 return impl_->bMultipoint_;
223 AbstractAnalysisData::dataSetCount() const
225 return impl_->columnCounts_.size();
229 AbstractAnalysisData::columnCount(int dataSet) const
231 GMX_ASSERT(dataSet >= 0 && dataSet < dataSetCount(),
232 "Out of range data set index");
233 return impl_->columnCounts_[dataSet];
237 AbstractAnalysisData::columnCount() const
239 GMX_ASSERT(dataSetCount() == 1,
240 "Convenience method not available for multiple data sets");
241 return columnCount(0);
245 AbstractAnalysisData::frameCount() const
247 return impl_->nframes_;
252 AbstractAnalysisData::tryGetDataFrame(int index) const
254 if (index < 0 || index >= frameCount())
256 return AnalysisDataFrameRef();
258 return tryGetDataFrameInternal(index);
263 AbstractAnalysisData::getDataFrame(int index) const
265 AnalysisDataFrameRef frame = tryGetDataFrame(index);
266 if (!frame.isValid())
268 GMX_THROW(APIError("Invalid frame accessed"));
275 AbstractAnalysisData::requestStorage(int nframes)
277 GMX_RELEASE_ASSERT(nframes >= -1, "Invalid number of frames requested");
282 return requestStorageInternal(nframes);
287 AbstractAnalysisData::addModule(AnalysisDataModulePointer module)
289 impl_->checkModuleProperties(*module);
291 if (impl_->bDataStart_)
293 GMX_RELEASE_ASSERT(!impl_->bInFrame_,
294 "Cannot add data modules in mid-frame");
295 impl_->presentData(this, module.get());
297 if (!(module->flags() & AnalysisDataModuleInterface::efAllowMissing))
299 impl_->bAllowMissing_ = false;
301 impl_->modules_.push_back(module);
306 AbstractAnalysisData::addColumnModule(int col, int span,
307 AnalysisDataModulePointer module)
309 GMX_RELEASE_ASSERT(col >= 0 && span >= 1,
310 "Invalid columns specified for a column module");
311 if (impl_->bDataStart_)
313 GMX_THROW(NotImplementedError("Cannot add column modules after data"));
316 boost::shared_ptr<AnalysisDataProxy> proxy(
317 new AnalysisDataProxy(col, span, this));
318 proxy->addModule(module);
324 AbstractAnalysisData::applyModule(AnalysisDataModuleInterface *module)
326 impl_->checkModuleProperties(*module);
327 GMX_RELEASE_ASSERT(impl_->bDataStart_ && !impl_->bInData_,
328 "Data module can only be applied to ready data");
330 impl_->presentData(this, module);
335 AbstractAnalysisData::setDataSetCount(int dataSetCount)
337 GMX_RELEASE_ASSERT(dataSetCount > 0, "Invalid data column count");
338 GMX_RELEASE_ASSERT(!impl_->bDataStart_,
339 "Data set count cannot be changed after data has been added");
340 impl_->columnCounts_.resize(dataSetCount);
344 AbstractAnalysisData::setColumnCount(int dataSet, int columnCount)
346 GMX_RELEASE_ASSERT(dataSet >= 0 && dataSet < dataSetCount(),
347 "Out of range data set index");
348 GMX_RELEASE_ASSERT(columnCount > 0, "Invalid data column count");
349 GMX_RELEASE_ASSERT(!impl_->bDataStart_,
350 "Data column count cannot be changed after data has been added");
351 impl_->columnCounts_[dataSet] = columnCount;
355 AbstractAnalysisData::setMultipoint(bool multipoint)
357 GMX_RELEASE_ASSERT(!impl_->bDataStart_,
358 "Data type cannot be changed after data has been added");
359 impl_->bMultipoint_ = multipoint;
364 * This method is not const because the dataStarted() methods of the attached
365 * modules can request storage of the data.
368 AbstractAnalysisData::notifyDataStart()
370 GMX_RELEASE_ASSERT(!impl_->bDataStart_,
371 "notifyDataStart() called more than once");
372 for (int d = 0; d < dataSetCount(); ++d)
374 GMX_RELEASE_ASSERT(columnCount(d) > 0,
375 "Data column count is not set");
377 impl_->bDataStart_ = impl_->bInData_ = true;
379 Impl::ModuleList::const_iterator i;
380 for (i = impl_->modules_.begin(); i != impl_->modules_.end(); ++i)
382 impl_->checkModuleProperties(**i);
383 (*i)->dataStarted(this);
389 AbstractAnalysisData::notifyFrameStart(const AnalysisDataFrameHeader &header) const
391 GMX_ASSERT(impl_->bInData_, "notifyDataStart() not called");
392 GMX_ASSERT(!impl_->bInFrame_,
393 "notifyFrameStart() called while inside a frame");
394 GMX_ASSERT(header.index() == impl_->nframes_,
395 "Out of order frames");
396 impl_->bInFrame_ = true;
397 impl_->currIndex_ = header.index();
399 Impl::ModuleList::const_iterator i;
400 for (i = impl_->modules_.begin(); i != impl_->modules_.end(); ++i)
402 (*i)->frameStarted(header);
408 AbstractAnalysisData::notifyPointsAdd(const AnalysisDataPointSetRef &points) const
410 GMX_ASSERT(impl_->bInData_, "notifyDataStart() not called");
411 GMX_ASSERT(impl_->bInFrame_, "notifyFrameStart() not called");
412 GMX_ASSERT(points.lastColumn() < columnCount(points.dataSetIndex()),
414 GMX_ASSERT(points.frameIndex() == impl_->currIndex_,
415 "Points do not correspond to current frame");
416 if (!impl_->bAllowMissing_ && !points.allPresent())
418 GMX_THROW(APIError("Missing data not supported by a module"));
421 Impl::ModuleList::const_iterator i;
422 for (i = impl_->modules_.begin(); i != impl_->modules_.end(); ++i)
424 (*i)->pointsAdded(points);
430 AbstractAnalysisData::notifyFrameFinish(const AnalysisDataFrameHeader &header)
432 GMX_ASSERT(impl_->bInData_, "notifyDataStart() not called");
433 GMX_ASSERT(impl_->bInFrame_, "notifyFrameStart() not called");
434 GMX_ASSERT(header.index() == impl_->currIndex_,
435 "Header does not correspond to current frame");
436 impl_->bInFrame_ = false;
437 impl_->currIndex_ = -1;
439 // Increment the counter before notifications to allow frame access from
443 Impl::ModuleList::const_iterator i;
444 for (i = impl_->modules_.begin(); i != impl_->modules_.end(); ++i)
446 (*i)->frameFinished(header);
452 AbstractAnalysisData::notifyDataFinish() const
454 GMX_RELEASE_ASSERT(impl_->bInData_, "notifyDataStart() not called");
455 GMX_RELEASE_ASSERT(!impl_->bInFrame_,
456 "notifyDataFinish() called while inside a frame");
457 impl_->bInData_ = false;
459 Impl::ModuleList::const_iterator i;
460 for (i = impl_->modules_.begin(); i != impl_->modules_.end(); ++i)
462 (*i)->dataFinished();