/*
* This file is part of the GROMACS molecular simulation package.
*
- * Copyright (c) 2010,2011,2012,2013, by the GROMACS development team, led by
- * David van der Spoel, Berk Hess, Erik Lindahl, and including many
- * others, as listed in the AUTHORS file in the top-level source
- * directory and at http://www.gromacs.org.
+ * Copyright (c) 2010,2011,2012,2013,2014, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
*
* GROMACS is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public License
* \author Teemu Murtola <teemu.murtola@gmail.com>
* \ingroup module_analysisdata
*/
-#include "gromacs/analysisdata/abstractdata.h"
+#include "gmxpre.h"
+
+#include "abstractdata.h"
#include <vector>
+#include "gromacs/analysisdata/dataframe.h"
#include "gromacs/analysisdata/datamodule.h"
+#include "gromacs/analysisdata/datamodulemanager.h"
#include "gromacs/utility/exceptions.h"
#include "gromacs/utility/gmxassert.h"
#include "gromacs/utility/uniqueptr.h"
-#include "dataframe.h"
#include "dataproxy.h"
namespace gmx
class AbstractAnalysisData::Impl
{
public:
- //! Shorthand for list of modules added to the data.
- typedef std::vector<AnalysisDataModulePointer> ModuleList;
-
Impl();
- //! Returns whether any data set has more than one column.
- bool isMultiColumn() const;
-
- /*! \brief
- * Checks whether a module is compatible with the data properties.
- *
- * \param[in] module Module to check.
- * \throws APIError if \p module is not compatible with the data.
- *
- * Does not check the actual data (e.g., missing values), but only the
- * dimensionality and other preset properties of the data.
- */
- void checkModuleProperties(const AnalysisDataModuleInterface &module) const;
-
- /*! \brief
- * Present data already added to the data object to a module.
- *
- * \param[in] data Data object to read data from.
- * \param[in] module Module to present the data to.
- * \throws APIError if \p module is not compatible with the data.
- * \throws APIError if all data is not available through
- * getDataFrame().
- * \throws unspecified Any exception thrown by \p module in its data
- * notification methods.
- *
- * Uses getDataFrame() in \p data to access all data in the object, and
- * calls the notification functions in \p module as if the module had
- * been registered to the data object when the data was added.
- */
- void presentData(AbstractAnalysisData *data,
- AnalysisDataModuleInterface *module);
-
//! Column counts for each data set in the data.
- std::vector<int> columnCounts_;
+ std::vector<int> columnCounts_;
//! Whether the data is multipoint.
- bool bMultipoint_;
- //! List of modules added to the data.
- ModuleList modules_;
- //! true if all modules support missing data.
- bool bAllowMissing_;
- //! Whether notifyDataStart() has been called.
- mutable bool bDataStart_;
- //! Whether new data is being added.
- mutable bool bInData_;
- //! Whether data for a frame is being added.
- mutable bool bInFrame_;
- //! Index of the currently active frame.
- mutable int currIndex_;
- /*! \brief
- * Total number of frames in the data.
- *
- * The counter is incremented in notifyFrameFinish().
- */
- int nframes_;
+ bool bMultipoint_;
+ //! Manager for the added modules.
+ AnalysisDataModuleManager modules_;
};
AbstractAnalysisData::Impl::Impl()
- : bMultipoint_(false), bAllowMissing_(true),
- bDataStart_(false), bInData_(false), bInFrame_(false),
- currIndex_(-1), nframes_(0)
+ : bMultipoint_(false)
{
columnCounts_.push_back(0);
}
-bool
-AbstractAnalysisData::Impl::isMultiColumn() const
-{
- std::vector<int>::const_iterator i;
- for (i = columnCounts_.begin(); i != columnCounts_.end(); ++i)
- {
- if (*i > 1)
- {
- return true;
- }
- }
- return false;
-}
-
-//! Helper macro for testing module flags.
-#define TEST_MODULE_FLAG(flags, flagname) \
- ((flags) & AnalysisDataModuleInterface::flagname)
-void
-AbstractAnalysisData::Impl::checkModuleProperties(
- const AnalysisDataModuleInterface &module) const
-{
- const int flags = module.flags();
- if ((!TEST_MODULE_FLAG(flags, efAllowMulticolumn) && isMultiColumn()) ||
- (!TEST_MODULE_FLAG(flags, efAllowMultipoint) && bMultipoint_) ||
- ( TEST_MODULE_FLAG(flags, efOnlyMultipoint) && !bMultipoint_) ||
- (!TEST_MODULE_FLAG(flags, efAllowMultipleDataSets)
- && columnCounts_.size() > 1U))
- {
- GMX_THROW(APIError("Data module not compatible with data object properties"));
- }
-}
-#undef TEST_MODULE_FLAGS
-
-void
-AbstractAnalysisData::Impl::presentData(AbstractAnalysisData *data,
- AnalysisDataModuleInterface *module)
-{
- module->dataStarted(data);
- bool bCheckMissing = bAllowMissing_
- && !(module->flags() & AnalysisDataModuleInterface::efAllowMissing);
- for (int i = 0; i < data->frameCount(); ++i)
- {
- AnalysisDataFrameRef frame = data->getDataFrame(i);
- GMX_RELEASE_ASSERT(frame.isValid(), "Invalid data frame returned");
- // TODO: Check all frames before doing anything for slightly better
- // exception behavior.
- if (bCheckMissing && !frame.allPresent())
- {
- GMX_THROW(APIError("Missing data not supported by a module"));
- }
- module->frameStarted(frame.header());
- for (int j = 0; j < frame.pointSetCount(); ++j)
- {
- module->pointsAdded(frame.pointSet(j));
- }
- module->frameFinished(frame.header());
- }
- if (!bInData_)
- {
- module->dataFinished();
- }
-}
-
/********************************************************************
* AbstractAnalysisData
return columnCount(0);
}
-int
-AbstractAnalysisData::frameCount() const
-{
- return impl_->nframes_;
-}
-
AnalysisDataFrameRef
AbstractAnalysisData::tryGetDataFrame(int index) const
void
AbstractAnalysisData::addModule(AnalysisDataModulePointer module)
{
- impl_->checkModuleProperties(*module);
-
- if (impl_->bDataStart_)
- {
- GMX_RELEASE_ASSERT(!impl_->bInFrame_,
- "Cannot add data modules in mid-frame");
- impl_->presentData(this, module.get());
- }
- if (!(module->flags() & AnalysisDataModuleInterface::efAllowMissing))
- {
- impl_->bAllowMissing_ = false;
- }
- impl_->modules_.push_back(module);
+ impl_->modules_.addModule(this, module);
}
{
GMX_RELEASE_ASSERT(col >= 0 && span >= 1,
"Invalid columns specified for a column module");
- if (impl_->bDataStart_)
- {
- GMX_THROW(NotImplementedError("Cannot add column modules after data"));
- }
-
boost::shared_ptr<AnalysisDataProxy> proxy(
new AnalysisDataProxy(col, span, this));
proxy->addModule(module);
void
AbstractAnalysisData::applyModule(AnalysisDataModuleInterface *module)
{
- impl_->checkModuleProperties(*module);
- GMX_RELEASE_ASSERT(impl_->bDataStart_ && !impl_->bInData_,
- "Data module can only be applied to ready data");
-
- impl_->presentData(this, module);
+ impl_->modules_.applyModule(this, module);
}
/*! \cond libapi */
AbstractAnalysisData::setDataSetCount(int dataSetCount)
{
GMX_RELEASE_ASSERT(dataSetCount > 0, "Invalid data column count");
- GMX_RELEASE_ASSERT(!impl_->bDataStart_,
- "Data set count cannot be changed after data has been added");
+ impl_->modules_.dataPropertyAboutToChange(
+ AnalysisDataModuleManager::eMultipleDataSets, dataSetCount > 1);
impl_->columnCounts_.resize(dataSetCount);
}
GMX_RELEASE_ASSERT(dataSet >= 0 && dataSet < dataSetCount(),
"Out of range data set index");
GMX_RELEASE_ASSERT(columnCount > 0, "Invalid data column count");
- GMX_RELEASE_ASSERT(!impl_->bDataStart_,
- "Data column count cannot be changed after data has been added");
- impl_->columnCounts_[dataSet] = columnCount;
-}
-
-void
-AbstractAnalysisData::setMultipoint(bool multipoint)
-{
- GMX_RELEASE_ASSERT(!impl_->bDataStart_,
- "Data type cannot be changed after data has been added");
- impl_->bMultipoint_ = multipoint;
-}
-
-
-/*! \internal
- * This method is not const because the dataStarted() methods of the attached
- * modules can request storage of the data.
- */
-void
-AbstractAnalysisData::notifyDataStart()
-{
- GMX_RELEASE_ASSERT(!impl_->bDataStart_,
- "notifyDataStart() called more than once");
- for (int d = 0; d < dataSetCount(); ++d)
- {
- GMX_RELEASE_ASSERT(columnCount(d) > 0,
- "Data column count is not set");
- }
- impl_->bDataStart_ = impl_->bInData_ = true;
-
- Impl::ModuleList::const_iterator i;
- for (i = impl_->modules_.begin(); i != impl_->modules_.end(); ++i)
- {
- impl_->checkModuleProperties(**i);
- (*i)->dataStarted(this);
- }
-}
-
-void
-AbstractAnalysisData::notifyFrameStart(const AnalysisDataFrameHeader &header) const
-{
- GMX_ASSERT(impl_->bInData_, "notifyDataStart() not called");
- GMX_ASSERT(!impl_->bInFrame_,
- "notifyFrameStart() called while inside a frame");
- GMX_ASSERT(header.index() == impl_->nframes_,
- "Out of order frames");
- impl_->bInFrame_ = true;
- impl_->currIndex_ = header.index();
-
- Impl::ModuleList::const_iterator i;
- for (i = impl_->modules_.begin(); i != impl_->modules_.end(); ++i)
+ bool bMultipleColumns = columnCount > 1;
+ for (int i = 0; i < dataSetCount() && !bMultipleColumns; ++i)
{
- (*i)->frameStarted(header);
+ if (i != dataSet && this->columnCount(i) > 1)
+ {
+ bMultipleColumns = true;
+ }
}
+ impl_->modules_.dataPropertyAboutToChange(
+ AnalysisDataModuleManager::eMultipleColumns, bMultipleColumns);
+ impl_->columnCounts_[dataSet] = columnCount;
}
-
void
-AbstractAnalysisData::notifyPointsAdd(const AnalysisDataPointSetRef &points) const
+AbstractAnalysisData::setMultipoint(bool bMultipoint)
{
- GMX_ASSERT(impl_->bInData_, "notifyDataStart() not called");
- GMX_ASSERT(impl_->bInFrame_, "notifyFrameStart() not called");
- GMX_ASSERT(points.lastColumn() < columnCount(points.dataSetIndex()),
- "Invalid columns");
- GMX_ASSERT(points.frameIndex() == impl_->currIndex_,
- "Points do not correspond to current frame");
- if (!impl_->bAllowMissing_ && !points.allPresent())
- {
- GMX_THROW(APIError("Missing data not supported by a module"));
- }
-
- Impl::ModuleList::const_iterator i;
- for (i = impl_->modules_.begin(); i != impl_->modules_.end(); ++i)
- {
- (*i)->pointsAdded(points);
- }
+ impl_->modules_.dataPropertyAboutToChange(
+ AnalysisDataModuleManager::eMultipoint, bMultipoint);
+ impl_->bMultipoint_ = bMultipoint;
}
-
-void
-AbstractAnalysisData::notifyFrameFinish(const AnalysisDataFrameHeader &header)
+AnalysisDataModuleManager &AbstractAnalysisData::moduleManager()
{
- GMX_ASSERT(impl_->bInData_, "notifyDataStart() not called");
- GMX_ASSERT(impl_->bInFrame_, "notifyFrameStart() not called");
- GMX_ASSERT(header.index() == impl_->currIndex_,
- "Header does not correspond to current frame");
- impl_->bInFrame_ = false;
- impl_->currIndex_ = -1;
-
- // Increment the counter before notifications to allow frame access from
- // modules.
- ++impl_->nframes_;
-
- Impl::ModuleList::const_iterator i;
- for (i = impl_->modules_.begin(); i != impl_->modules_.end(); ++i)
- {
- (*i)->frameFinished(header);
- }
+ return impl_->modules_;
}
-
-void
-AbstractAnalysisData::notifyDataFinish() const
+const AnalysisDataModuleManager &AbstractAnalysisData::moduleManager() const
{
- GMX_RELEASE_ASSERT(impl_->bInData_, "notifyDataStart() not called");
- GMX_RELEASE_ASSERT(!impl_->bInFrame_,
- "notifyDataFinish() called while inside a frame");
- impl_->bInData_ = false;
-
- Impl::ModuleList::const_iterator i;
- for (i = impl_->modules_.begin(); i != impl_->modules_.end(); ++i)
- {
- (*i)->dataFinished();
- }
+ return impl_->modules_;
}
//! \endcond