const TopologyInformation &top);
virtual TrajectoryAnalysisModuleData *startFrames(
- AnalysisDataParallelOptions opt,
+ const AnalysisDataParallelOptions &opt,
const SelectionCollection &selections);
virtual void analyzeFrame(int frnr, const t_trxframe &fr, t_pbc *pbc,
TrajectoryAnalysisModuleData *pdata);
{
public:
ModuleData(TrajectoryAnalysisModule *module,
- AnalysisDataParallelOptions opt,
+ const AnalysisDataParallelOptions &opt,
const SelectionCollection &selections)
: TrajectoryAnalysisModuleData(module, opt, selections),
_nb(NULL)
TrajectoryAnalysisModuleData *
-AnalysisTemplate::startFrames(AnalysisDataParallelOptions opt,
+AnalysisTemplate::startFrames(const AnalysisDataParallelOptions &opt,
const SelectionCollection &selections)
{
std::auto_ptr<ModuleData> pdata(new ModuleData(this, opt, selections));
frave += nb->minimumDistance(sel->x(i));
}
frave /= nr;
- dh->addPoint(g, frave);
+ dh->setPoint(g, frave);
}
dh->finishFrame();
}
*/
/*! \internal \file
* \brief
- * Declares internal implementation classes for gmx::AbstractAnalysisData and
- * gmx::AbstractAnalysisDataStored.
+ * Declares internal implementation class for gmx::AbstractAnalysisData.
*
* \author Teemu Murtola <teemu.murtola@cbr.su.se>
* \ingroup module_analysisdata
int _nframes;
};
-/*! \internal \brief
- * Internal implementation class for storing a single data frame.
- *
- * \ingroup module_analysisdata
- */
-class AnalysisDataFrame
-{
- public:
- explicit AnalysisDataFrame(int columnCount);
- ~AnalysisDataFrame();
-
- //! Zero-based global index of the frame.
- int _index;
- //! x value of the frame.
- real _x;
- //! Error of x for the frame.
- real _dx;
- //! Number of columns in the frame.
- int _columnCount;
- //! Array of column values for the frame.
- real *_y;
- //! Array of column error values for the frame.
- real *_dy;
- //! Array of flags that tell whether a value is present.
- bool *_present;
-};
-
-/*! \internal \brief
- * Private implementation class for AbstractAnalysisDataStored.
- *
- * \ingroup module_analysisdata
- */
-class AbstractAnalysisDataStored::Impl
-{
- public:
- //! Shorthand for a list of data frames that are currently stored.
- typedef std::vector<AnalysisDataFrame *> FrameList;
-
- Impl();
- ~Impl();
-
- /*! \brief
- * Calculates the index of a frame in the storage vector.
- *
- * \param[in] index Zero-based index for the frame to query.
- * Negative value counts backwards from the current frame.
- * \param[in] nframes Total number of frames in the data.
- * \returns Index in \a _store corresponding to \p index,
- * or -1 if not available.
- */
- int getStoreIndex(int index, int nframes) const;
-
- /*! \brief
- * Number of elements in \a _store.
- *
- * Also holds the number of frames that should be stored, even before
- * \a _store has been allocated.
- */
- int _nalloc;
- //! Whether all frames should be stored.
- bool _bStoreAll;
- //! List of data frames that are currently stored.
- FrameList _store;
- /*! \brief
- * Index in \a _store where the next frame will be stored.
- *
- * This counter is incremented after notifyPointsAdd() has been called
- * for the frame.
- */
- int _nextind;
-};
-
} // namespace gmx
#endif
*/
/*! \internal \file
* \brief
- * Implements gmx::AbstractAnalysisData and gmx::AbstractAnalysisDataStored.
+ * Implements gmx::AbstractAnalysisData.
*
* \author Teemu Murtola <teemu.murtola@cbr.su.se>
* \ingroup module_analysisdata
#include <memory>
-// Legacy header.
-#include "smalloc.h"
-
#include "gromacs/fatalerror/exceptions.h"
#include "gromacs/fatalerror/gmxassert.h"
}
-void
-AbstractAnalysisData::notifyPointsAdd(int firstcol, int n,
- const real *y, const real *dy,
- const bool *present) const
-{
- notifyPointsAdd(AnalysisDataPointSetRef(
- _impl->_currHeader, firstcol, n, y, dy, present));
-}
-
-
void
AbstractAnalysisData::notifyFrameFinish(const AnalysisDataFrameHeader &header) const
{
}
-void
-AbstractAnalysisData::notifyFrameFinish() const
-{
- notifyFrameFinish(_impl->_currHeader);
-}
-
-
void
AbstractAnalysisData::notifyDataFinish() const
{
}
}
-
-/********************************************************************
- * AnalysisDataFrame
- */
-
-AnalysisDataFrame::AnalysisDataFrame(int columnCount)
- : _index(-1), _x(0.0), _dx(0.0), _columnCount(columnCount),
- _y(NULL), _dy(NULL), _present(NULL)
-{
- snew(_y, columnCount);
- snew(_dy, columnCount);
- snew(_present, columnCount);
-}
-
-
-AnalysisDataFrame::~AnalysisDataFrame()
-{
- sfree(_y);
- sfree(_dy);
- sfree(_present);
-}
-
-
-/********************************************************************
- * AbstractAnalysisDataStored::Impl
- */
-
-AbstractAnalysisDataStored::Impl::Impl()
- : _nalloc(0), _bStoreAll(false), _nextind(-1)
-{
-}
-
-
-AbstractAnalysisDataStored::Impl::~Impl()
-{
- FrameList::const_iterator i;
- for (i = _store.begin(); i != _store.end(); ++i)
- {
- delete *i;
- }
-}
-
-
-int
-AbstractAnalysisDataStored::Impl::getStoreIndex(int index, int nframes) const
-{
- // Check that the requested index is available.
- if ((index < 0 && (-index > _nalloc || -index > nframes))
- || index >= nframes || (index >= 0 && index < nframes - _nalloc))
- {
- return -1;
- }
- // Calculate the index into the storage array.
- if (index < 0)
- {
- index = _nextind + index;
- if (index < 0)
- {
- index += _nalloc;
- }
- }
- else if (nframes > _nalloc)
- {
- index %= _nalloc;
- }
- return index;
-}
-
-
-/********************************************************************
- * AbstractAnalysisDataStored
- */
-
-AbstractAnalysisDataStored::AbstractAnalysisDataStored()
- : _impl(new Impl())
-{
-}
-
-
-AbstractAnalysisDataStored::~AbstractAnalysisDataStored()
-{
- delete _impl;
-}
-
-
-AnalysisDataFrameRef
-AbstractAnalysisDataStored::tryGetDataFrameInternal(int index) const
-{
- if (!_impl->_bStoreAll && index < frameCount() - _impl->_nalloc)
- {
- return AnalysisDataFrameRef();
- }
- AnalysisDataFrame *fr = _impl->_store[index % _impl->_nalloc];
- return AnalysisDataFrameRef(fr->_index, fr->_x, fr->_dx, fr->_columnCount,
- fr->_y, fr->_dy, fr->_present);
-}
-
-
-bool
-AbstractAnalysisDataStored::requestStorageInternal(int nframes)
-{
- GMX_RELEASE_ASSERT(!isMultipoint(), "Storage of multipoint data not supported");
-
- // Handle the case when everything needs to be stored.
- if (nframes == -1)
- {
- _impl->_bStoreAll = true;
- _impl->_nalloc = 1;
- return true;
- }
- // Check whether an earier call has requested more storage.
- if (_impl->_bStoreAll || nframes < _impl->_nalloc)
- {
- return true;
- }
- // nframes previous frames plus the current one
- _impl->_nalloc = nframes + 1;
- return true;
-}
-
-
-void
-AbstractAnalysisDataStored::setMultipoint(bool multipoint)
-{
- GMX_RELEASE_ASSERT(_impl->_nalloc == 0 || !multipoint,
- "Storage of multipoint data not supported");
- AbstractAnalysisData::setMultipoint(multipoint);
-}
-
-
-void
-AbstractAnalysisDataStored::startDataStore()
-{
- // We first notify any attached modules, because they also might request
- // some storage.
- notifyDataStart();
-
- // If any storage has been requested, preallocate it.
- if (_impl->_nalloc > 0)
- {
- _impl->_store.resize(_impl->_nalloc);
- for (int i = 0; i < _impl->_nalloc; ++i)
- {
- _impl->_store[i] = new AnalysisDataFrame(columnCount());
- }
- _impl->_nextind = 0;
- }
-}
-
-
-void
-AbstractAnalysisDataStored::startNextFrame(const AnalysisDataFrameHeader &header)
-{
- // Start storing the frame if needed.
- if (_impl->_nalloc > 0)
- {
- if (_impl->_nextind >= _impl->_nalloc)
- {
- if (_impl->_bStoreAll)
- {
- _impl->_nalloc = _impl->_nextind + 1;
- _impl->_store.resize(_impl->_nalloc);
- for (int i = _impl->_nextind; i < _impl->_nalloc; ++i)
- {
- _impl->_store[i] = new AnalysisDataFrame(columnCount());
- }
- }
- else
- {
- _impl->_nextind = 0;
- }
- }
-
- _impl->_store[_impl->_nextind]->_index = header.index();
- _impl->_store[_impl->_nextind]->_x = header.x();
- _impl->_store[_impl->_nextind]->_dx = header.dx();
- }
-
- // Notify any modules.
- notifyFrameStart(header);
-}
-
-
-void
-AbstractAnalysisDataStored::storeThisFrame(const real *y, const real *dy,
- const bool *present)
-{
- int ncol = columnCount();
-
- // Store the values if required.
- if (_impl->_nextind >= 0)
- {
- AnalysisDataFrame *fr = _impl->_store[_impl->_nextind];
-
- for (int i = 0; i < ncol; ++i)
- {
- fr->_y[i] = y[i];
- if (dy)
- {
- fr->_dy[i] = dy[i];
- }
- if (present)
- {
- fr->_present[i] = present[i];
- }
- }
- }
-
- // Notify modules of new data.
- notifyPointsAdd(0, ncol, y, dy, present);
- // The index needs to be incremented after the notifications to allow
- // the modules to use getDataFrame() properly.
- if (_impl->_nextind >= 0)
- {
- ++_impl->_nextind;
- }
- notifyFrameFinish();
-}
-
-
-void
-AbstractAnalysisDataStored::storeNextFrame(real x, real dx, const real *y,
- const real *dy, const bool *present)
-{
- startNextFrame(AnalysisDataFrameHeader(frameCount(), x, dx));
- storeThisFrame(y, dy, present);
-}
-
} // namespace gmx
*/
/*! \file
* \brief
- * Declares gmx::AbstractAnalysisData and gmx::AbstractAnalysisDataStored.
+ * Declares gmx::AbstractAnalysisData.
*
* \author Teemu Murtola <teemu.murtola@cbr.su.se>
* \inlibraryapi
class AnalysisDataFrameHeader;
class AnalysisDataFrameRef;
class AnalysisDataPointSetRef;
+class AnalysisDataStorage;
/*! \brief
* Abstract base class for all objects that provide data.
* for each column separately.
*/
void notifyPointsAdd(const AnalysisDataPointSetRef &points) const;
- /*! \brief
- * Deprecated convenience method for not needing to construct a
- * AnalysisDataPointSetRef object.
- *
- * Will be removed as part of future work.
- */
- void notifyPointsAdd(int firstcol, int n,
- const real *y, const real *dy,
- const bool *present) const;
/*! \brief
* Notifies attached modules of the end of a frame.
*
* notifyPointsAdd() calls for the frame.
*/
void notifyFrameFinish(const AnalysisDataFrameHeader &header) const;
- /*! \brief
- * Deprecated convenience method for not needing a frame header.
- *
- * Will be removed as part of future work.
- */
- void notifyFrameFinish() const;
/*! \brief
* Notifies attached modules of the end of data.
*
int _ncol;
bool _bMultiPoint;
+ /*! \brief
+ * Needed to provide access to notification methods.
+ */
+ friend class AnalysisDataStorage;
+
// Disallow copy and assign.
AbstractAnalysisData(const AbstractAnalysisData &);
void operator =(const AbstractAnalysisData &);
};
-
-/*! \brief
- * Abstract class that implements storage of data.
- *
- * \if libapi
- * This class implements a standard way of storing data, to avoid implementing
- * storage in each derived class separately. All the pure virtual methods of
- * AbstractData are implemented, and protected methods are provided to add data
- * to the storage. These protected methods automatically call notifyDataStart(),
- * notifyFrameStart(), notifyPointsAdd() and notifyFrameFinish()
- * functions in AbstractAnalysisData, but the derived class should still
- * call notifyDataFinish().
- *
- * Additional protected functions could be implemented to allow optimization:
- * in the current interface, some data copying is unavoidable.
- * Some changes could make it possible to obtain a pointer to the
- * storage, allowing the calculated values to be stored there directly
- * instead of a temporary array.
- * \endif
- *
- * \inlibraryapi
- * \ingroup module_analysisdata
- */
-class AbstractAnalysisDataStored : public AbstractAnalysisData
-{
- public:
- virtual ~AbstractAnalysisDataStored();
-
- protected:
- /*! \cond libapi */
- AbstractAnalysisDataStored();
-
- /*! \copydoc AbstractAnalysisData::setMultipoint()
- *
- * The overridden method also asserts if
- * storage has been requested and \p multipoint is \c true.
- */
- void setMultipoint(bool multipoint);
-
- //! Start storing data.
- void startDataStore();
- //! Starts storing a next frame.
- void startNextFrame(const AnalysisDataFrameHeader &header);
- //! Stores the whole frame in a single call after start_next_frame().
- void storeThisFrame(const real *y, const real *dy, const bool *present);
- //! Convenience function for storing a whole frame in a single call.
- void storeNextFrame(real x, real dx, const real *y, const real *dy,
- const bool *present);
- //! \endcond
-
- private:
- virtual AnalysisDataFrameRef tryGetDataFrameInternal(int index) const;
- virtual bool requestStorageInternal(int nframes);
-
- class Impl;
-
- Impl *_impl;
-
- // Copy and assign disallowed by base class.
-};
-
} // namespace gmx
#endif
#ifndef GMX_ANALYSISDATA_ANALYSISDATA_IMPL_H
#define GMX_ANALYSISDATA_ANALYSISDATA_IMPL_H
-#include <memory>
#include <vector>
#include "analysisdata.h"
-
-#include "abstractdata-impl.h"
+#include "datastorage.h"
namespace gmx
{
public:
//! Shorthand for a list of data handles.
typedef std::vector<AnalysisDataHandle *> HandleList;
- //! Shorthand for a list of frames.
- typedef std::vector<AnalysisDataFrame *> FrameList;
- //! Creates an implementation class associated with the given object.
- explicit Impl(AnalysisData *data);
+ Impl();
~Impl();
- /*! \brief
- * Handles a new frame.
- *
- * If all earlier frames are ready, the data is directly passed to
- * AbstractStoredData. Otherwise, it is put into the correct location
- * in \a _pending. Calls processPendingFrame() after processing \p fr.
- */
- void addPendingFrame(AnalysisDataFrame *fr);
- /*! \brief
- * Passes pending frames to base class if all earlier frames are ready.
- */
- void processPendingFrames();
- //! Increments \a _pstart.
- void incrementPStart();
-
- //! The data object that uses this implementation class.
- AnalysisData &_data;
+ AnalysisDataStorage storage_;
//! List of handles for this data object.
- HandleList _handles;
- /*! \brief
- * Index into \a _pending that points to the location where the current
- * frame would go.
- */
- size_t _pstart;
- /*! \brief
- * Circular buffer for frames that are ready but waiting for earlier
- * frames.
- */
- FrameList _pending;
+ HandleList handles_;
};
/*! \internal \brief
public:
//! Creates a handle associated with the given data object.
explicit Impl(AnalysisData *data);
- ~Impl();
//! The data object that this handle belongs to.
- AnalysisData &_data;
- //! Frame object where the current frame is being accumulated.
- // Could be scoped_ptr
- std::auto_ptr<AnalysisDataFrame> _frame;
+ AnalysisData &data_;
+ //! Current storage frame object, or NULL if no current frame.
+ AnalysisDataStorageFrame *currentFrame_;
};
} // namespace gmx
#include <memory>
#include "gromacs/analysisdata/dataframe.h"
+#include "gromacs/analysisdata/datastorage.h"
+#include "gromacs/analysisdata/paralleloptions.h"
#include "gromacs/fatalerror/exceptions.h"
#include "gromacs/fatalerror/gmxassert.h"
-#include "abstractdata-impl.h"
#include "analysisdata-impl.h"
namespace gmx
/********************************************************************
* AnalysisData::Impl
- ********************************************************************/
-
-static bool
-frame_index_gtr(AnalysisDataFrame *a, AnalysisDataFrame *b)
-{
- return a->_index > b->_index;
-}
-
+ */
-AnalysisData::Impl::Impl(AnalysisData *data)
- : _data(*data), _pstart(0)
+AnalysisData::Impl::Impl()
{
}
AnalysisData::Impl::~Impl()
{
HandleList::const_iterator i;
- for (i = _handles.begin(); i != _handles.end(); ++i)
+ for (i = handles_.begin(); i != handles_.end(); ++i)
{
delete *i;
}
-
- FrameList::const_iterator j;
- for (j = _pending.begin(); j != _pending.end(); ++j)
- {
- delete *j;
- }
-}
-
-
-void
-AnalysisData::Impl::addPendingFrame(AnalysisDataFrame *fr)
-{
- GMX_ASSERT(fr->_index >= _data.frameCount(),
- "addPendingFrame() called for too old frame");
- size_t pindex = fr->_index - _data.frameCount();
- if (pindex == 0)
- {
- // Just store our frame if it is the next one.
- _data.storeNextFrame(fr->_x, fr->_dx, fr->_y, fr->_dy, fr->_present);
- incrementPStart();
- }
- else
- {
- if (pindex >= _pending.size())
- {
- // TODO: We need to wait until earlier frames are ready...
- }
- // TODO: This is not thread-safe.
- pindex += _pstart;
- if (pindex > _pending.size())
- {
- pindex -= _pending.size();
- }
-
- int ncol = _data.columnCount();
- _pending[pindex]->_x = fr->_x;
- _pending[pindex]->_dx = fr->_dx;
- for (int i = 0; i < ncol; ++i)
- {
- _pending[pindex]->_y[i] = fr->_y[i];
- _pending[pindex]->_dy[i] = fr->_dy[i];
- _pending[pindex]->_present[i] = fr->_present[i];
- }
- _pending[pindex]->_index = fr->_index;
- }
- processPendingFrames();
-}
-
-
-void
-AnalysisData::Impl::processPendingFrames()
-{
- while (_pending[_pstart]->_index != -1)
- {
- AnalysisDataFrame *fr = _pending[_pstart];
-
- _data.storeNextFrame(fr->_x, fr->_dx, fr->_y, fr->_dy, fr->_present);
- fr->_index = -1;
- incrementPStart();
- }
-}
-
-
-void
-AnalysisData::Impl::incrementPStart()
-{
- size_t val = _pstart;
-
- ++val;
- if (val >= _pending.size())
- {
- val -= _pending.size();
- }
- _pstart = val;
}
*/
AnalysisData::AnalysisData()
- : _impl(new Impl(this))
+ : impl_(new Impl)
{
}
AnalysisData::~AnalysisData()
{
- delete _impl;
+ delete impl_;
}
AnalysisData::setColumns(int ncol, bool multipoint)
{
GMX_RELEASE_ASSERT(ncol > 0, "Number of columns must be positive");
- GMX_RELEASE_ASSERT(_impl->_handles.empty(),
+ GMX_RELEASE_ASSERT(impl_->handles_.empty(),
"Cannot change data dimensionality after creating handles");
setColumnCount(ncol);
setMultipoint(multipoint);
+ impl_->storage_.setMultipoint(multipoint);
}
AnalysisDataHandle *
-AnalysisData::startData(AnalysisDataParallelOptions opt)
+AnalysisData::startData(const AnalysisDataParallelOptions &opt)
{
- if (_impl->_handles.empty())
+ GMX_RELEASE_ASSERT(impl_->handles_.size() < static_cast<unsigned>(opt.parallelizationFactor()),
+ "Too many calls to startData() compared to provided options");
+ if (impl_->handles_.empty())
{
- startDataStore();
+ notifyDataStart();
+ impl_->storage_.setParallelOptions(opt);
+ impl_->storage_.startDataStorage(this);
}
else if (isMultipoint())
{
}
std::auto_ptr<AnalysisDataHandle> handle(new AnalysisDataHandle(this));
- _impl->_handles.push_back(handle.get());
-
- size_t oldSize = _impl->_pending.size();
- _impl->_pending.resize(2 * _impl->_handles.size() - 1);
- Impl::FrameList::iterator i;
- for (i = _impl->_pending.begin() + oldSize; i != _impl->_pending.end(); ++i)
- {
- *i = new AnalysisDataFrame(columnCount());
- (*i)->_index = -1;
- }
-
+ impl_->handles_.push_back(handle.get());
return handle.release();
}
{
Impl::HandleList::iterator i;
- i = std::find(_impl->_handles.begin(), _impl->_handles.end(), handle);
- GMX_RELEASE_ASSERT(i != _impl->_handles.end(),
+ i = std::find(impl_->handles_.begin(), impl_->handles_.end(), handle);
+ GMX_RELEASE_ASSERT(i != impl_->handles_.end(),
"finishData() called for an unknown handle");
- _impl->_handles.erase(i);
+ impl_->handles_.erase(i);
delete handle;
- if (_impl->_handles.empty())
+ if (impl_->handles_.empty())
{
notifyDataFinish();
}
}
-/********************************************************************
- * AnalysisDataHandle::Impl
- */
+AnalysisDataFrameRef
+AnalysisData::tryGetDataFrameInternal(int index) const
+{
+ return impl_->storage_.tryGetDataFrame(index);
+}
-AnalysisDataHandle::Impl::Impl(AnalysisData *data)
- : _data(*data)
+
+bool
+AnalysisData::requestStorageInternal(int nframes)
{
- if (!_data.isMultipoint())
- {
- _frame.reset(new AnalysisDataFrame(_data.columnCount()));
- }
+ return impl_->storage_.requestStorage(nframes);
}
-AnalysisDataHandle::Impl::~Impl()
+/********************************************************************
+ * AnalysisDataHandle::Impl
+ */
+
+AnalysisDataHandle::Impl::Impl(AnalysisData *data)
+ : data_(*data), currentFrame_(NULL)
{
}
*/
AnalysisDataHandle::AnalysisDataHandle(AnalysisData *data)
- : _impl(new Impl(data))
+ : impl_(new Impl(data))
{
}
AnalysisDataHandle::~AnalysisDataHandle()
{
- delete _impl;
+ delete impl_;
}
void
AnalysisDataHandle::startFrame(int index, real x, real dx)
{
- if (_impl->_data.isMultipoint())
- {
- _impl->_data.notifyFrameStart(AnalysisDataFrameHeader(index, x, dx));
- }
- else
- {
- _impl->_frame->_index = index;
- _impl->_frame->_x = x;
- _impl->_frame->_dx = dx;
- for (int i = 0; i < _impl->_data.columnCount(); ++i)
- {
- _impl->_frame->_y[i] = 0.0;
- _impl->_frame->_dy[i] = 0.0;
- _impl->_frame->_present[i] = false;
- }
- }
-}
-
-
-void
-AnalysisDataHandle::addPoint(int col, real y, real dy, bool present)
-{
- if (_impl->_data.isMultipoint())
- {
- _impl->_data.notifyPointsAdd(col, 1, &y, &dy, &present);
- }
- else
- {
- GMX_ASSERT(!_impl->_frame->_present[col],
- "Data for a column set multiple times");
- _impl->_frame->_y[col] = y;
- _impl->_frame->_dy[col] = dy;
- _impl->_frame->_present[col] = present;
- }
+ GMX_RELEASE_ASSERT(impl_->currentFrame_ == NULL,
+ "startFrame() called twice without calling finishFrame()");
+ impl_->currentFrame_ =
+ &impl_->data_.impl_->storage_.startFrame(index, x, dx);
}
void
-AnalysisDataHandle::addPoints(int firstcol, int n,
- const real *y, const real *dy,
- const bool *present)
+AnalysisDataHandle::setPoint(int col, real y, real dy, bool present)
{
- if (_impl->_data.isMultipoint())
- {
- _impl->_data.notifyPointsAdd(firstcol, n, y, dy, present);
- }
- else
- {
- for (int i = 0; i < n; ++i)
- {
- addPoint(firstcol + i, y[i], dy ? dy[i] : 0.0,
- present ? present[i] : true);
- }
- }
+ GMX_RELEASE_ASSERT(impl_->currentFrame_ != NULL,
+ "setPoint() called without calling startFrame()");
+ impl_->currentFrame_->setValue(col, y, dy, present);
}
void
-AnalysisDataHandle::finishFrame()
+AnalysisDataHandle::setPoints(int firstcol, int n, const real *y)
{
- if (_impl->_data.isMultipoint())
- {
- _impl->_data.notifyFrameFinish();
- }
- else
+ GMX_RELEASE_ASSERT(impl_->currentFrame_ != NULL,
+ "setPoints() called without calling startFrame()");
+ for (int i = 0; i < n; ++i)
{
- _impl->_data._impl->addPendingFrame(_impl->_frame.get());
+ impl_->currentFrame_->setValue(firstcol + i, y[i]);
}
}
void
-AnalysisDataHandle::addFrame(int index, real x, const real *y, const real *dy,
- const bool *present)
+AnalysisDataHandle::finishPointSet()
{
- addFrame(index, x, 0.0, y, dy, present);
+ GMX_RELEASE_ASSERT(impl_->data_.isMultipoint(),
+ "finishPointSet() called for non-multipoint data");
+ GMX_RELEASE_ASSERT(impl_->currentFrame_ != NULL,
+ "finishPointSet() called without calling startFrame()");
+ impl_->currentFrame_->finishPointSet();
}
void
-AnalysisDataHandle::addFrame(int index, real x, real dx,
- const real *y, const real *dy,
- const bool *present)
+AnalysisDataHandle::finishFrame()
{
- startFrame(index, x, dx);
- addPoints(0, _impl->_data.columnCount(), y, dy, present);
- finishFrame();
+ GMX_RELEASE_ASSERT(impl_->currentFrame_ != NULL,
+ "finishFrame() called without calling startFrame()");
+ int index = impl_->currentFrame_->frameIndex();
+ impl_->currentFrame_ = NULL;
+ impl_->data_.impl_->storage_.finishFrame(index);
}
AnalysisDataHandle::finishData()
{
// Calls delete this
- _impl->_data.finishData(this);
+ impl_->data_.finishData(this);
}
} // namespace gmx
{
class AnalysisDataHandle;
-
-//! Placeholder type for parallelization options.
-typedef void *AnalysisDataParallelOptions;
+class AnalysisDataParallelOptions;
/*! \brief
* Parallelizable data container for raw data.
*
- * This is the only data object (in addition to the tightly coupled
- * \c AnalysisDataHandle object) that needs explicit parallelization.
- *
+ * \if internal
* Special note for MPI implementation: assuming that the initialization of
* data objects is identical in all processes, associating the data objects
* in different MPI processes should be possible without changes in the
* Alternative, more robust implementation could get a unique ID as parameter
* to the constructor or a separate function, but would require all tools to
* provide it.
+ * \endif
*
* \inpublicapi
* \ingroup module_analysisdata
*/
-class AnalysisData : public AbstractAnalysisDataStored
+class AnalysisData : public AbstractAnalysisData
{
public:
//! Creates an empty analysis data object.
* used.
* \returns The created handle.
*/
- AnalysisDataHandle *startData(AnalysisDataParallelOptions opt);
+ AnalysisDataHandle *startData(const AnalysisDataParallelOptions &opt);
/*! \brief
* Destroy a handle after all data has been added.
*
void finishData(AnalysisDataHandle *handle);
private:
+ virtual AnalysisDataFrameRef tryGetDataFrameInternal(int index) const;
+ virtual bool requestStorageInternal(int nframes);
+
class Impl;
- Impl *_impl;
+ Impl *impl_;
- friend class Impl;
friend class AnalysisDataHandle;
// Copy and assign disallowed by base class.
//! Start data for a new frame.
void startFrame(int index, real x, real dx = 0.0);
- //! Add a data point for in single column for the current frame.
- void addPoint(int col, real y, real dy = 0.0, bool present = true);
- //! Add multiple data points in neighboring columns for the current frame.
- void addPoints(int firstcol, int n,
- const real *y, const real *dy = 0,
- const bool *present = 0);
+ //! Set a value for a single column for the current frame.
+ void setPoint(int col, real y, real dy = 0.0, bool present = true);
+ //! Set values for consecutive columns for the current frame.
+ void setPoints(int firstcol, int n, const real *y);
+ //! Finish data for the current point set.
+ void finishPointSet();
//! Finish data for the current frame.
void finishFrame();
- //! Convenience function for adding a complete frame.
- void addFrame(int index, real x, const real *y, const real *dy = 0,
- const bool *present = 0);
- //! Convenience function for adding a complete frame.
- void addFrame(int index, real x, real dx,
- const real *y, const real *dy = 0,
- const bool *present = 0);
//! Calls AnalysisData::finishData() for this handle.
void finishData();
class Impl;
- Impl *_impl;
+ Impl *impl_;
friend class AnalysisData;
--- /dev/null
+/*
+ *
+ * This source code is part of
+ *
+ * G R O M A C S
+ *
+ * GROningen MAchine for Chemical Simulations
+ *
+ * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2009, The GROMACS development team,
+ * check out http://www.gromacs.org for more information.
+
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * If you want to redistribute modifications, please consider that
+ * scientific software is very special. Version control is crucial -
+ * bugs must be traceable. We will be happy to consider code for
+ * inclusion in the official distribution, but derived work must not
+ * be called official GROMACS. Details are found in the README & COPYING
+ * files - if they are missing, get the official version at www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the papers on the package - you can find them in the top README file.
+ *
+ * For more info, check our website at http://www.gromacs.org
+ */
+/*! \internal \file
+ * \brief
+ * Declares internal implementation classes for gmx::AnalysisDataStorage.
+ *
+ * \author Teemu Murtola <teemu.murtola@cbr.su.se>
+ * \ingroup module_analysisdata
+ */
+#ifndef GMX_ANALYSISDATA_DATASTORAGE_IMPL_H
+#define GMX_ANALYSISDATA_DATASTORAGE_IMPL_H
+
+#include <limits>
+#include <vector>
+
+#include "datastorage.h"
+
+namespace gmx
+{
+
+class AbstractAnalysisData;
+class AnalysisDataPointSetRef;
+class AnalysisDataStorageFrame;
+
+/*! \internal \brief
+ * Private implementation class for AnalysisDataStorage.
+ *
+ * \ingroup module_analysisdata
+ */
+class AnalysisDataStorage::Impl
+{
+ public:
+ /*! \brief
+ * Stored information about a single stored frame.
+ */
+ struct StoredFrame
+ {
+ enum Status
+ {
+ eMissing, //!< Frame has not yet been started.
+ eStarted, //!< startFrame() has been called.
+ eFinished, //!< finishFrame() has been called.
+ eNotified //!< Appropriate notifications have been sent.
+ };
+
+ StoredFrame() : frame(NULL), status(eMissing) {}
+ explicit StoredFrame(AnalysisDataStorageFrame *frame)
+ : frame(frame), status(eMissing)
+ {
+ }
+
+ bool isStarted() const { return status >= eStarted; }
+ bool isFinished() const { return status >= eFinished; }
+ bool isNotified() const { return status >= eNotified; }
+ bool isAvailable() const { return status >= eFinished; }
+
+ /*! \brief
+ * Actual frame data.
+ *
+ * Always allocated. Memory is managed by the parent Impl object
+ * to make the StoredFrame type STL-container-friendly.
+ */
+ AnalysisDataStorageFrame *frame;
+ //! In what state the frame currently is.
+ Status status;
+ };
+
+ //! Shorthand for a list of data frames that are currently stored.
+ typedef std::vector<StoredFrame> FrameList;
+
+ Impl();
+ ~Impl();
+
+ //! Returns the number of columns in the attached data.
+ int columnCount() const;
+ //! Returns whether the storage is set to use multipoint data.
+ bool isMultipoint() const;
+ /*! \brief
+ * Whether storage of all frames has been requested.
+ *
+ * Storage of all frames also works as expected if \a storageLimit_ is
+ * used in comparisons directly, but this method should be used to
+ * check how to manage \a frames_.
+ */
+ bool storeAll() const
+ {
+ return storageLimit_ == std::numeric_limits<int>::max();
+ }
+ //! Returns the index of the oldest frame that may be currently stored.
+ int firstStoredIndex() const;
+ /*! \brief
+ * Computes index into \a frames_ for accessing frame \p index.
+ *
+ * \param[in] index Zero-based frame index.
+ * \retval -1 if \p index is not available in \a frames_.
+ */
+ int computeStorageLocation(int index) const;
+
+ /*! \brief
+ * Computes an index into \a frames_ that is one past the last frame
+ * stored.
+ */
+ size_t endStorageLocation() const;
+
+ /*! \brief
+ * Extends \a frames_ to a new size.
+ */
+ void extendBuffer(AnalysisDataStorage *storage, size_t newSize);
+ /*! \brief
+ * Remove oldest frame from the storage to make space for a new one.
+ *
+ * Increments \a firstFrameLocation_ and reinitializes the frame that
+ * was made unavailable by this operation.
+ *
+ * \see frames_
+ */
+ void rotateBuffer();
+
+ /*! \brief
+ * Calls notification method in \a data_.
+ */
+ void notifyPointSet(const AnalysisDataPointSetRef &points);
+ /*! \brief
+ * Calls notification methods for new frames.
+ *
+ * \param[in] firstLocation First frame to consider.
+ *
+ * Notifies \a data_ of new frames (from \p firstLocation and after
+ * that) if all previous frames have already been notified.
+ * Also rotates the \a frames_ buffer as necessary.
+ */
+ void notifyNextFrames(size_t firstLocation);
+
+ //! Data object to use for notification calls.
+ AbstractAnalysisData *data_;
+ /*! \brief
+ * Whether the storage has been set to allow multipoint.
+ *
+ * Should be possible to remove once full support for multipoint data
+ * has been implemented; isMultipoint() can simply return
+ * \c data_->isMultipoint() in that case.
+ */
+ bool bMultipoint_;
+ /*! \brief
+ * Number of past frames that need to be stored.
+ *
+ * Always non-negative. If storage of all frames has been requested,
+ * this is set to a large number.
+ */
+ int storageLimit_;
+ /*! \brief
+ * Number of future frames that may need to be started.
+ *
+ * Should always be at least one.
+ *
+ * \see AnalysisDataStorage::startFrame()
+ */
+ int pendingLimit_;
+ /*! \brief
+ * Data frames that are currently stored.
+ *
+ * If storage of all frames has been requested, this is simply a vector
+ * of frames up to the latest frame that has been started.
+ * In this case, \a firstFrameLocation_ is always zero.
+ *
+ * If storage of all frames is not requested, this is a ring buffer of
+ * frames of size \c n=storageLimit_+pendingLimit_+1. If a frame with
+ * index \c index is currently stored, its location is
+ * \c index%frames_.size().
+ * When at most \a storageLimit_ first frames have been finished,
+ * this contains storage for the first \c n-1 frames.
+ * When more than \a storageLimit_ first frames have been finished,
+ * the oldest stored frame is stored in the location
+ * \a firstFrameLocation_, and \a storageLimit_ frames starting from
+ * this location are the last finished frames. \a pendingLimit_ frames
+ * follow, and some of these may be in progress or finished.
+ * There is always one unused frame in the buffer, which is initialized
+ * such that when \a firstFrameLocation_ is incremented, it becomes
+ * valid. This makes it easier to rotate the buffer in concurrent
+ * access scenarions (which are not yet otherwise implemented).
+ */
+ FrameList frames_;
+ //! Location of oldest frame in \a frames_.
+ size_t firstFrameLocation_;
+ /*! \brief
+ * Index of next frame that will be added to \a frames_.
+ *
+ * If all frames are not stored, this will be the index of the unused
+ * frame (see \a frames_).
+ */
+ int nextIndex_;
+};
+
+} // namespace gmx
+
+#endif
--- /dev/null
+/*
+ *
+ * This source code is part of
+ *
+ * G R O M A C S
+ *
+ * GROningen MAchine for Chemical Simulations
+ *
+ * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2009, The GROMACS development team,
+ * check out http://www.gromacs.org for more information.
+
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * If you want to redistribute modifications, please consider that
+ * scientific software is very special. Version control is crucial -
+ * bugs must be traceable. We will be happy to consider code for
+ * inclusion in the official distribution, but derived work must not
+ * be called official GROMACS. Details are found in the README & COPYING
+ * files - if they are missing, get the official version at www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the papers on the package - you can find them in the top README file.
+ *
+ * For more info, check our website at http://www.gromacs.org
+ */
+/*! \internal \file
+ * \brief
+ * Implements classes in datastorage.h and paralleloptions.h.
+ *
+ * \author Teemu Murtola <teemu.murtola@cbr.su.se>
+ * \ingroup module_analysisdata
+ */
+#include "datastorage.h"
+
+#include <limits>
+
+#include "smalloc.h"
+
+#include "gromacs/analysisdata/abstractdata.h"
+#include "gromacs/analysisdata/dataframe.h"
+#include "gromacs/analysisdata/paralleloptions.h"
+#include "gromacs/fatalerror/exceptions.h"
+#include "gromacs/fatalerror/gmxassert.h"
+
+#include "datastorage-impl.h"
+
+namespace gmx
+{
+
+/********************************************************************
+ * AnalysisDataParallelOptions
+ */
+
+AnalysisDataParallelOptions::AnalysisDataParallelOptions()
+ : parallelizationFactor_(1)
+{
+}
+
+
+AnalysisDataParallelOptions::AnalysisDataParallelOptions(int parallelizationFactor)
+ : parallelizationFactor_(parallelizationFactor)
+{
+ GMX_RELEASE_ASSERT(parallelizationFactor >= 1,
+ "Invalid parallelization factor");
+}
+
+
+/********************************************************************
+ * AnalysisDataStorageFrame
+ */
+
+AnalysisDataStorageFrame::AnalysisDataStorageFrame(AnalysisDataStorage *storage,
+ int columnCount, int index)
+ : storage_(storage), header_(index, 0.0, 0.0), columnCount_(columnCount),
+ y_(NULL), dy_(NULL), present_(NULL)
+{
+ snew(y_, columnCount);
+ snew(dy_, columnCount);
+ snew(present_, columnCount);
+}
+
+
+AnalysisDataStorageFrame::~AnalysisDataStorageFrame()
+{
+ sfree(y_);
+ sfree(dy_);
+ sfree(present_);
+}
+
+
+AnalysisDataPointSetRef
+AnalysisDataStorageFrame::currentPoints() const
+{
+ // TODO: Deduce first column and column count for multipoint data.
+ // Requires additional information to be stored.
+ return AnalysisDataPointSetRef(header_, 0, columnCount_, y_, dy_, present_);
+}
+
+
+void
+AnalysisDataStorageFrame::clearValues()
+{
+ for (int i = 0; i < columnCount(); ++i)
+ {
+ y_[i] = 0.0;
+ dy_[i] = 0.0;
+ present_[i] = false;
+ }
+}
+
+
+void
+AnalysisDataStorageFrame::finishPointSet()
+{
+ storage_->impl_->notifyPointSet(currentPoints());
+ clearValues();
+}
+
+
+/********************************************************************
+ * AnalysisDataStorage::Impl
+ */
+
+AnalysisDataStorage::Impl::Impl()
+ : data_(NULL), bMultipoint_(false),
+ storageLimit_(0), pendingLimit_(1), firstFrameLocation_(0), nextIndex_(0)
+{
+}
+
+
+AnalysisDataStorage::Impl::~Impl()
+{
+ FrameList::const_iterator i;
+ for (i = frames_.begin(); i != frames_.end(); ++i)
+ {
+ delete i->frame;
+ }
+}
+
+
+int
+AnalysisDataStorage::Impl::columnCount() const
+{
+ GMX_ASSERT(data_ != NULL, "columnCount() called too early");
+ return data_->columnCount();
+}
+
+
+bool
+AnalysisDataStorage::Impl::isMultipoint() const
+{
+ return bMultipoint_;
+}
+
+
+int
+AnalysisDataStorage::Impl::firstStoredIndex() const
+{
+ return frames_[firstFrameLocation_].frame->frameIndex();
+}
+
+
+int
+AnalysisDataStorage::Impl::computeStorageLocation(int index) const
+{
+ if (index < firstStoredIndex() || index >= nextIndex_)
+ {
+ return -1;
+ }
+ return index % frames_.size();
+}
+
+
+size_t
+AnalysisDataStorage::Impl::endStorageLocation() const
+{
+ if (storeAll())
+ {
+ return frames_.size();
+ }
+ if (frames_[0].frame->frameIndex() == 0 || firstFrameLocation_ == 0)
+ {
+ return frames_.size() - 1;
+ }
+ return firstFrameLocation_ - 1;
+}
+
+
+void
+AnalysisDataStorage::Impl::extendBuffer(AnalysisDataStorage *storage,
+ size_t newSize)
+{
+ frames_.reserve(newSize);
+ while (frames_.size() < newSize)
+ {
+ AnalysisDataStorageFrame *frame =
+ new AnalysisDataStorageFrame(storage, columnCount(), nextIndex_);
+ frames_.push_back(StoredFrame(frame));
+ ++nextIndex_;
+ }
+ // The unused frame should not be included in the count.
+ if (!storeAll())
+ {
+ --nextIndex_;
+ }
+}
+
+
+void
+AnalysisDataStorage::Impl::rotateBuffer()
+{
+ GMX_ASSERT(!storeAll(),
+ "No need to rotate internal buffer if everything is stored");
+ size_t prevFirst = firstFrameLocation_;
+ size_t nextFirst = prevFirst + 1;
+ if (nextFirst == frames_.size())
+ {
+ nextFirst = 0;
+ }
+ firstFrameLocation_ = nextFirst;
+ StoredFrame &prevFrame = frames_[prevFirst];
+ prevFrame.status = StoredFrame::eMissing;
+ prevFrame.frame->header_ = AnalysisDataFrameHeader(nextIndex_ + 1, 0.0, 0.0);
+ prevFrame.frame->clearValues();
+ ++nextIndex_;
+}
+
+
+void
+AnalysisDataStorage::Impl::notifyPointSet(const AnalysisDataPointSetRef &points)
+{
+ data_->notifyPointsAdd(points);
+}
+
+
+void
+AnalysisDataStorage::Impl::notifyNextFrames(size_t firstLocation)
+{
+ if (firstLocation != firstFrameLocation_)
+ {
+ // firstLocation can only be zero here if !storeAll() because
+ // firstFrameLocation_ is always zero for storeAll()
+ int prevIndex =
+ (firstLocation == 0 ? frames_.size() - 1 : firstLocation - 1);
+ if (!frames_[prevIndex].isNotified())
+ {
+ return;
+ }
+ }
+ size_t i = firstLocation;
+ size_t end = endStorageLocation();
+ while (i != end)
+ {
+ Impl::StoredFrame &storedFrame = frames_[i];
+ if (!storedFrame.isFinished())
+ {
+ break;
+ }
+ if (storedFrame.status == StoredFrame::eFinished)
+ {
+ data_->notifyFrameStart(storedFrame.frame->header());
+ data_->notifyPointsAdd(storedFrame.frame->currentPoints());
+ data_->notifyFrameFinish(storedFrame.frame->header());
+ storedFrame.status = StoredFrame::eNotified;
+ if (storedFrame.frame->frameIndex() >= storageLimit_)
+ {
+ rotateBuffer();
+ }
+ }
+ ++i;
+ if (!storeAll() && i >= frames_.size())
+ {
+ i = 0;
+ }
+ }
+}
+
+
+/********************************************************************
+ * AnalysisDataStorage
+ */
+
+AnalysisDataStorage::AnalysisDataStorage()
+ : impl_(new Impl())
+{
+}
+
+
+AnalysisDataStorage::~AnalysisDataStorage()
+{
+ delete impl_;
+}
+
+
+void
+AnalysisDataStorage::setMultipoint(bool bMultipoint)
+{
+ if (bMultipoint && impl_->storageLimit_ > 0)
+ {
+ GMX_THROW(APIError("Storage of multipoint data not supported"));
+ }
+ impl_->bMultipoint_ = bMultipoint;
+}
+
+
+void
+AnalysisDataStorage::setParallelOptions(const AnalysisDataParallelOptions &opt)
+{
+ impl_->pendingLimit_ = 2 * opt.parallelizationFactor() - 1;
+}
+
+
+AnalysisDataFrameRef
+AnalysisDataStorage::tryGetDataFrame(int index) const
+{
+ if (impl_->isMultipoint())
+ {
+ return AnalysisDataFrameRef();
+ }
+ int storageIndex = impl_->computeStorageLocation(index);
+ if (storageIndex == -1)
+ {
+ return AnalysisDataFrameRef();
+ }
+ const Impl::StoredFrame &storedFrame = impl_->frames_[storageIndex];
+ if (!storedFrame.isAvailable())
+ {
+ return AnalysisDataFrameRef();
+ }
+ const AnalysisDataStorageFrame *frame = storedFrame.frame;
+ return AnalysisDataFrameRef(frame->header(), frame->columnCount(),
+ frame->y_, frame->dy_, frame->present_);
+}
+
+
+bool
+AnalysisDataStorage::requestStorage(int nframes)
+{
+ if (impl_->isMultipoint())
+ {
+ return false;
+ }
+
+ // Handle the case when everything needs to be stored.
+ if (nframes == -1)
+ {
+ impl_->storageLimit_ = std::numeric_limits<int>::max();
+ return true;
+ }
+ // Check whether an earlier call has requested more storage.
+ if (nframes < impl_->storageLimit_)
+ {
+ return true;
+ }
+ impl_->storageLimit_ = nframes;
+ return true;
+}
+
+
+void
+AnalysisDataStorage::startDataStorage(AbstractAnalysisData *data)
+{
+ // Data needs to be set before calling extendBuffer()
+ impl_->data_ = data;
+ setMultipoint(data->isMultipoint());
+ if (!impl_->storeAll())
+ {
+ impl_->extendBuffer(this, impl_->storageLimit_ + impl_->pendingLimit_ + 1);
+ }
+}
+
+
+AnalysisDataStorageFrame &
+AnalysisDataStorage::startFrame(const AnalysisDataFrameHeader &header)
+{
+ GMX_ASSERT(header.isValid(), "Invalid header");
+ Impl::StoredFrame *storedFrame;
+ if (impl_->storeAll())
+ {
+ size_t size = header.index() + 1;
+ if (impl_->frames_.size() < size)
+ {
+ impl_->extendBuffer(this, size);
+ }
+ storedFrame = &impl_->frames_[header.index()];
+ }
+ else
+ {
+ int storageIndex = impl_->computeStorageLocation(header.index());
+ if (storageIndex == -1)
+ {
+ GMX_THROW(APIError("Out of bounds frame index"));
+ }
+ storedFrame = &impl_->frames_[storageIndex];
+ }
+ GMX_RELEASE_ASSERT(!storedFrame->isStarted(),
+ "startFrame() called twice for the same frame");
+ GMX_RELEASE_ASSERT(storedFrame->frame->frameIndex() == header.index(),
+ "Inconsistent internal frame indexing");
+ storedFrame->status = Impl::StoredFrame::eStarted;
+ storedFrame->frame->header_ = header;
+ if (impl_->isMultipoint())
+ {
+ impl_->data_->notifyFrameStart(header);
+ }
+ return *storedFrame->frame;
+}
+
+
+AnalysisDataStorageFrame &
+AnalysisDataStorage::startFrame(int index, real x, real dx)
+{
+ return startFrame(AnalysisDataFrameHeader(index, x, dx));
+}
+
+
+AnalysisDataStorageFrame &
+AnalysisDataStorage::currentFrame(int index)
+{
+ int storageIndex = impl_->computeStorageLocation(index);
+ GMX_RELEASE_ASSERT(storageIndex >= 0, "Out of bounds frame index");
+ Impl::StoredFrame &storedFrame = impl_->frames_[storageIndex];
+ GMX_RELEASE_ASSERT(storedFrame.isStarted(),
+ "currentFrame() called for frame before startFrame()");
+ GMX_RELEASE_ASSERT(!storedFrame.isFinished(),
+ "currentFrame() called for frame after finishFrame()");
+ GMX_RELEASE_ASSERT(storedFrame.frame->frameIndex() == index,
+ "Inconsistent internal frame indexing");
+ return *storedFrame.frame;
+}
+
+
+void
+AnalysisDataStorage::finishFrame(int index)
+{
+ int storageIndex = impl_->computeStorageLocation(index);
+ GMX_RELEASE_ASSERT(storageIndex >= 0, "Out of bounds frame index");
+ Impl::StoredFrame &storedFrame = impl_->frames_[storageIndex];
+ GMX_RELEASE_ASSERT(storedFrame.isStarted(),
+ "finishFrame() called for frame before startFrame()");
+ GMX_RELEASE_ASSERT(!storedFrame.isFinished(),
+ "finishFrame() called twice for the same frame");
+ GMX_RELEASE_ASSERT(storedFrame.frame->frameIndex() == index,
+ "Inconsistent internal frame indexing");
+ storedFrame.status = Impl::StoredFrame::eFinished;
+ if (impl_->isMultipoint())
+ {
+ // TODO: Check that the last point set has been finished
+ impl_->data_->notifyFrameFinish(storedFrame.frame->header());
+ if (storedFrame.frame->frameIndex() >= impl_->storageLimit_)
+ {
+ impl_->rotateBuffer();
+ }
+ }
+ else
+ {
+ impl_->notifyNextFrames(storageIndex);
+ }
+}
+
+
+void
+AnalysisDataStorage::finishFrame(const AnalysisDataStorageFrame &frame)
+{
+ finishFrame(frame.frameIndex());
+}
+
+} // namespace gmx
--- /dev/null
+/*
+ *
+ * This source code is part of
+ *
+ * G R O M A C S
+ *
+ * GROningen MAchine for Chemical Simulations
+ *
+ * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2009, The GROMACS development team,
+ * check out http://www.gromacs.org for more information.
+
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * If you want to redistribute modifications, please consider that
+ * scientific software is very special. Version control is crucial -
+ * bugs must be traceable. We will be happy to consider code for
+ * inclusion in the official distribution, but derived work must not
+ * be called official GROMACS. Details are found in the README & COPYING
+ * files - if they are missing, get the official version at www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the papers on the package - you can find them in the top README file.
+ *
+ * For more info, check our website at http://www.gromacs.org
+ */
+/*! \libinternal \file
+ * \brief
+ * Declares gmx::AnalysisDataStorage.
+ *
+ * \author Teemu Murtola <teemu.murtola@cbr.su.se>
+ * \inlibraryapi
+ * \ingroup module_analysisdata
+ */
+#ifndef GMX_ANALYSISDATA_DATASTORAGE_H
+#define GMX_ANALYSISDATA_DATASTORAGE_H
+
+#include "../legacyheaders/types/simple.h"
+
+#include "../fatalerror/gmxassert.h"
+
+#include "dataframe.h"
+
+namespace gmx
+{
+
+class AbstractAnalysisData;
+class AnalysisDataFrameHeader;
+class AnalysisDataFrameRef;
+class AnalysisDataParallelOptions;
+
+class AnalysisDataStorage;
+
+/*! \libinternal \brief
+ * Stores a single data frame for AnalysisDataStorage.
+ *
+ * It also provides access to the frame outside AnalysisDataStorage.
+ *
+ * It is implemented such that the frame header is always valid, i.e.,
+ * header().isValid() returns always true.
+ *
+ * \inlibraryapi
+ * \ingroup module_analysisdata
+ */
+class AnalysisDataStorageFrame
+{
+ public:
+ //! Returns header for the frame.
+ const AnalysisDataFrameHeader &header() const { return header_; }
+ //! Returns zero-based index of the frame.
+ int frameIndex() const { return header().index(); }
+ //! Returns x coordinate for the frame.
+ real x() const { return header().x(); }
+ //! Returns error in x coordinate for the frame if applicable.
+ real dx() const { return header().dx(); }
+ //! Returns number of columns for the frame.
+ int columnCount() const { return columnCount_; }
+
+ /*! \brief
+ * Returns point set reference to currently set values.
+ *
+ * Does not throw.
+ */
+ AnalysisDataPointSetRef currentPoints() const;
+ /*! \brief
+ * Sets value for a column.
+ *
+ * \param[in] column Zero-based column index.
+ * \param[in] value Value to set for the column.
+ * \param[in] error Error estimate to set for the column.
+ * \param[in] present Present flag to set for the column.
+ *
+ * If called multiple times for a column (within one point set for
+ * multipoint data), old values are overwritten.
+ *
+ * Does not throw.
+ */
+ void setValue(int column, real value, real error = 0.0,
+ bool present = true)
+ {
+ GMX_ASSERT(column >= 0 && column < columnCount(),
+ "Invalid column index");
+ y_[column] = value;
+ dy_[column] = error;
+ present_[column] = present;
+ }
+ /*! \brief
+ * Access value for a column.
+ *
+ * \param[in] column Zero-based column index.
+ *
+ * Should only be called after the column value has been set using
+ * setValue(); assigning a value to \c value(i) does not mark the
+ * column as set.
+ *
+ * Does not throw.
+ */
+ real &value(int column)
+ {
+ GMX_ASSERT(column >= 0 && column < columnCount(),
+ "Invalid column index");
+ return y_[column];
+ }
+ /*! \brief
+ * Access value for a column.
+ *
+ * \param[in] column Zero-based column index.
+ *
+ * Should only be called after the column value has been set using
+ * setValue().
+ *
+ * Does not throw.
+ */
+ real value(int column) const
+ {
+ GMX_ASSERT(column >= 0 && column < columnCount(),
+ "Invalid column index");
+ return y_[column];
+ }
+ /*! \brief
+ * Mark point set as finished for multipoint data.
+ *
+ * Must be called after each point set for multipoint data, including
+ * the last (i.e., no values must be set between the last call to this
+ * method and AnalysisDataStorage::finishFrame()).
+ * Must not be called for non-multipoint data.
+ *
+ * After this method has been called, all values appear as not set.
+ *
+ * Calls AbstractAnalysisData::notifyPointsAdd(), and throws any
+ * exception this method throws.
+ */
+ void finishPointSet();
+
+ private:
+ /*! \brief
+ * Create a new storage frame.
+ *
+ * \param storage Storage object this frame belongs to.
+ * \param[in] columnCount Number of columns for the frame.
+ * \param[in] index Zero-based index for the frame.
+ */
+ AnalysisDataStorageFrame(AnalysisDataStorage *storage, int columnCount,
+ int index);
+ ~AnalysisDataStorageFrame();
+
+ //! Clear all column values from the frame.
+ void clearValues();
+
+ //! Storage object that contains this frame.
+ AnalysisDataStorage *storage_;
+ //! Header for the frame.
+ AnalysisDataFrameHeader header_;
+ //! Number of columns in the frame.
+ int columnCount_;
+ //! Array of column values for the frame.
+ real *y_;
+ //! Array of column error values for the frame.
+ real *dy_;
+ //! Array of flags that tell whether a value is present.
+ bool *present_;
+
+ /*! \brief
+ * Needed for full write access to the data and for access to
+ * constructor/destructor.
+ */
+ friend class AnalysisDataStorage;
+
+ // Disallow copy and assign.
+ AnalysisDataStorageFrame(const AnalysisDataStorageFrame &);
+ void operator =(const AnalysisDataStorageFrame &);
+};
+
+/*! \libinternal \brief
+ * Helper class that implements storage of data.
+ *
+ * This class implements a standard way of storing data to avoid implementing
+ * storage in each class derived from AbstractAnalysisData separately.
+ * To use this class in a class derived from AbstractAnalysisData, a member
+ * variable of this type should be declared and the data storage methods
+ * forwarded to tryGetDataFrame() and requestStorage() in that object.
+ * Storage properties should be set up, and then startDataStorage() called
+ * after calling AbstractAnalysisData::notifyDataStart().
+ * New frames can then be added using startFrame(), currentFrame() and
+ * finishFrame() methods. These methods (and
+ * AnalysisDataStorageFrame::finishPointSet()) take the responsibility of
+ * calling AbstractAnalysisData::notifyFrameStart(),
+ * AbstractAnalysisData::notifyPointsAdd() and
+ * AbstractAnalysisData::notifyFrameFinish() appropriately.
+ *
+ * \todo
+ * Full support for multipoint data.
+ * Currently, multipoint data is only supported in serial pass-through mode
+ * without any storage.
+ *
+ * \todo
+ * Proper multi-threaded implementation.
+ *
+ * \inlibraryapi
+ * \ingroup module_analysisdata
+ */
+class AnalysisDataStorage
+{
+ public:
+ //! Constructs a storage object.
+ AnalysisDataStorage();
+ ~AnalysisDataStorage();
+
+ /*! \brief
+ * Sets whether the data will be multipoint.
+ *
+ * \exception APIError if storage has been requested.
+ *
+ * It is not mandatory to call this method (the multipoint property is
+ * automatically detected in startDataStorage()), but currently calling
+ * it will produce better diagnostic messages.
+ * When full support for multipoint data has been implemented, this
+ * method can be removed.
+ */
+ void setMultipoint(bool bMultipoint);
+ /*! \brief
+ * Set parallelization options for the storage.
+ *
+ * \param[in] opt Parallization options to use.
+ *
+ * If this method is not called, the storage is set up for serial
+ * storage only.
+ *
+ * Does not throw.
+ */
+ void setParallelOptions(const AnalysisDataParallelOptions &opt);
+
+ /*! \brief
+ * Implements access to data frames.
+ *
+ * This method is designed such that calls to
+ * AbstractAnalysisData::tryGetDataFrameInternal() can be directly
+ * forwarded to this method. See that method for more documentation.
+ *
+ * A valid reference for a frame will be returned after finishFrame()
+ * has been called for that frame.
+ *
+ * \see AbstractAnalysisData::tryGetDataFrameInternal()
+ */
+ AnalysisDataFrameRef tryGetDataFrame(int index) const;
+ /*! \brief
+ * Implements storage requests.
+ *
+ * This method is designed such that calls to
+ * AbstractAnalysisData::requestStorageInternal() can be directly
+ * forwarded to this method. See that method for more documentation.
+ *
+ * Currently, multipoint data cannot be stored, but all other storage
+ * request will be honored.
+ *
+ * \see AbstractAnalysisData::requestStorageInternal()
+ */
+ bool requestStorage(int nframes);
+
+ /*! \brief
+ * Start storing data.
+ *
+ * \param data AbstractAnalysisData object containing this storage.
+ * \exception std::bad_alloc if storage allocation fails.
+ * \exception APIError if storage has been requested and \p data is
+ * multipoint.
+ *
+ * Lifetime of \p data must exceed the lifetime of the storage object
+ * (typically, the storage object will be a member in \p data).
+ * The storage object will take responsibility of calling
+ * AbstractAnalysisData::notifyFrameStart(),
+ * AbstractAnalysisData::notifyPointsAdd() and
+ * AbstractAnalysisData::notifyFrameFinish() for \p data appropriately.
+ *
+ * AbstractAnalysisData::notifyDataStart() must have been called for
+ * \p data, because that may trigger storage requests from attached
+ * modules.
+ */
+ void startDataStorage(AbstractAnalysisData *data);
+ /*! \brief
+ * Starts storing a new frame.
+ *
+ * \param[in] header Header for the new frame.
+ * \retval Frame object corresponding to the started frame.
+ * \exception std::bad_alloc if storage reallocation fails
+ * (only possible if storage of all frames has been requested).
+ * \exception APIError if frame is too far in the future.
+ *
+ * The returned object will be valid until the corresponding
+ * finishFrame() call.
+ *
+ * Must be called exactly once for each frame index.
+ *
+ * Currently, the implementation only works if the new frame is not too
+ * far in the future:
+ * If \c i is the index of the last frame such that all frames from
+ * 0, ..., \c i have been finished, then \p header().index() should be
+ * at most \c 2*parallelizationFactor-1 larger than \c i, where
+ * parallelizationFactor is the parallelization factor passed to
+ * setParallelOptions().
+ * Throws APIError if this constraint is violated.
+ *
+ * Calls AbstractAnalysisData::notifyDataStarted() in certain cases,
+ * and throws any exceptions this method throws.
+ */
+ AnalysisDataStorageFrame &startFrame(const AnalysisDataFrameHeader &header);
+ /*! \brief
+ * Convenience method to start storing a new frame.
+ *
+ * Identical to \c startFrame(AnalysisDataFrameHeader(index, x, dx));
+ */
+ AnalysisDataStorageFrame &startFrame(int index, real x, real dx);
+ /*! \brief
+ * Obtain a frame object for an in-progress frame.
+ *
+ * \param[in] index Frame index.
+ * \retval Frame object corresponding to \p index.
+ *
+ * startFrame() should have been called for the frame with index
+ * \p index, and finishFrame() should not yet have been called.
+ * Returns the same object as returned by the original startFrame()
+ * call for the same index.
+ *
+ * Does not throw.
+ */
+ AnalysisDataStorageFrame ¤tFrame(int index);
+ /*! \brief
+ * Finish storing a frame.
+ *
+ * \param[in] index Frame index.
+ *
+ * Must be called exactly once for each startFrame(), after the
+ * corresponding call.
+ *
+ * Calls notification methods in AbstractAnalysisData, and throws any
+ * exceptions these methods throw.
+ */
+ void finishFrame(int index);
+ /*! \brief
+ * Convenience method for finishing a data frame.
+ *
+ * \param[in] frame Frame object to finish.
+ *
+ * \p frame should have been previously obtained from startFrame() or
+ * currentFrame(). The \p frame reference is no longer valid after the
+ * call.
+ *
+ * Identical to \c finishframe(frame.frameIndex());
+ */
+ void finishFrame(const AnalysisDataStorageFrame &frame);
+
+ private:
+ class Impl;
+
+ Impl *impl_;
+
+ /*! \brief
+ * Needed because the frame object needs to trigger notifications.
+ */
+ friend void AnalysisDataStorageFrame::finishPointSet();
+
+ // Disallow copy and assign.
+ AnalysisDataStorage(const AnalysisDataStorage &);
+ void operator =(const AnalysisDataStorage &);
+};
+
+} // namespace gmx
+
+#endif
#include "histogram.h"
+#include "../datastorage.h"
+
namespace gmx
{
* not exist.
*/
void ensureAveragerExists(AbstractAnalysisData *data);
+ /*! \brief
+ * Initializes data storage frame when a new frame starts.
+ */
+ void initFrame(AnalysisDataStorageFrame *frame);
+ //! Storage implementation object.
+ AnalysisDataStorage storage_;
//! Settings for the histogram object.
AnalysisHistogramSettings settings_;
- //! Histogram data accumulated for a frame so far.
- std::vector<real> hist_;
//! Averager module, or NULL if not yet allocated.
BasicAverageHistogramModule *averager_;
};
#include <cmath>
-#include <algorithm>
#include <limits>
#include <memory>
#include "gromacs/basicmath.h"
#include "gromacs/analysisdata/dataframe.h"
+#include "gromacs/analysisdata/datastorage.h"
#include "gromacs/fatalerror/exceptions.h"
#include "gromacs/fatalerror/gmxassert.h"
}
}
+
+void
+BasicHistogramImpl::initFrame(AnalysisDataStorageFrame *frame)
+{
+ for (int i = 0; i < frame->columnCount(); ++i)
+ {
+ frame->setValue(i, 0.0);
+ }
+}
+
} // namespace internal
AnalysisDataSimpleHistogramModule::dataStarted(AbstractAnalysisData *data)
{
impl_->ensureAveragerExists(this);
- impl_->hist_.resize(settings().binCount());
setColumnCount(settings().binCount());
- startDataStore();
+ notifyDataStart();
+ impl_->storage_.startDataStorage(this);
}
void
AnalysisDataSimpleHistogramModule::frameStarted(const AnalysisDataFrameHeader &header)
{
- std::fill(impl_->hist_.begin(), impl_->hist_.end(), 0.0);
- startNextFrame(header);
+ AnalysisDataStorageFrame &frame = impl_->storage_.startFrame(header);
+ impl_->initFrame(&frame);
}
void
AnalysisDataSimpleHistogramModule::pointsAdded(const AnalysisDataPointSetRef &points)
{
+ AnalysisDataStorageFrame &frame =
+ impl_->storage_.currentFrame(points.frameIndex());
for (int i = 0; i < points.columnCount(); ++i)
{
int bin = settings().findBin(points.y(i));
if (bin != -1)
{
- impl_->hist_[bin] += 1;
+ frame.value(bin) += 1;
}
}
}
void
-AnalysisDataSimpleHistogramModule::frameFinished(const AnalysisDataFrameHeader & /*header*/)
+AnalysisDataSimpleHistogramModule::frameFinished(const AnalysisDataFrameHeader &header)
{
- storeThisFrame(&impl_->hist_[0], NULL, NULL);
+ impl_->storage_.finishFrame(header.index());
}
}
+AnalysisDataFrameRef
+AnalysisDataSimpleHistogramModule::tryGetDataFrameInternal(int index) const
+{
+ return impl_->storage_.tryGetDataFrame(index);
+}
+
+
+bool
+AnalysisDataSimpleHistogramModule::requestStorageInternal(int nframes)
+{
+ return impl_->storage_.requestStorage(nframes);
+}
+
+
/********************************************************************
* AnalysisDataWeightedHistogramModule
*/
AnalysisDataWeightedHistogramModule::dataStarted(AbstractAnalysisData *data)
{
impl_->ensureAveragerExists(this);
- impl_->hist_.resize(settings().binCount());
setColumnCount(settings().binCount());
- startDataStore();
+ notifyDataStart();
+ impl_->storage_.startDataStorage(this);
}
void
AnalysisDataWeightedHistogramModule::frameStarted(const AnalysisDataFrameHeader &header)
{
- std::fill(impl_->hist_.begin(), impl_->hist_.end(), 0.0);
- startNextFrame(header);
+ AnalysisDataStorageFrame &frame = impl_->storage_.startFrame(header);
+ impl_->initFrame(&frame);
}
int bin = settings().findBin(points.y(0));
if (bin != -1)
{
+ AnalysisDataStorageFrame &frame =
+ impl_->storage_.currentFrame(points.frameIndex());
for (int i = 1; i < points.columnCount(); ++i)
{
- impl_->hist_[bin] += points.y(i);
+ frame.value(bin) += points.y(i);
}
}
}
void
-AnalysisDataWeightedHistogramModule::frameFinished(const AnalysisDataFrameHeader & /*header*/)
+AnalysisDataWeightedHistogramModule::frameFinished(const AnalysisDataFrameHeader &header)
{
- storeThisFrame(&impl_->hist_[0], NULL, NULL);
+ impl_->storage_.finishFrame(header.index());
}
}
+AnalysisDataFrameRef
+AnalysisDataWeightedHistogramModule::tryGetDataFrameInternal(int index) const
+{
+ return impl_->storage_.tryGetDataFrame(index);
+}
+
+
+bool
+AnalysisDataWeightedHistogramModule::requestStorageInternal(int nframes)
+{
+ return impl_->storage_.requestStorage(nframes);
+}
+
+
/********************************************************************
* AnalysisDataBinAverageModule
*/
* \inpublicapi
* \ingroup module_analysisdata
*/
-class AnalysisDataSimpleHistogramModule : public AbstractAnalysisDataStored,
+class AnalysisDataSimpleHistogramModule : public AbstractAnalysisData,
public AnalysisDataModuleInterface
{
public:
virtual void dataFinished();
private:
+ virtual AnalysisDataFrameRef tryGetDataFrameInternal(int index) const;
+ virtual bool requestStorageInternal(int nframes);
+
internal::BasicHistogramImpl *impl_;
// Copy and assign disallowed by base.
* \inpublicapi
* \ingroup module_analysisdata
*/
-class AnalysisDataWeightedHistogramModule : public AbstractAnalysisDataStored,
+class AnalysisDataWeightedHistogramModule : public AbstractAnalysisData,
public AnalysisDataModuleInterface
{
public:
virtual void dataFinished();
private:
+ virtual AnalysisDataFrameRef tryGetDataFrameInternal(int index) const;
+ virtual bool requestStorageInternal(int nframes);
+
internal::BasicHistogramImpl *impl_;
// Copy and assign disallowed by base.
--- /dev/null
+/*
+ *
+ * This source code is part of
+ *
+ * G R O M A C S
+ *
+ * GROningen MAchine for Chemical Simulations
+ *
+ * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2009, The GROMACS development team,
+ * check out http://www.gromacs.org for more information.
+
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * If you want to redistribute modifications, please consider that
+ * scientific software is very special. Version control is crucial -
+ * bugs must be traceable. We will be happy to consider code for
+ * inclusion in the official distribution, but derived work must not
+ * be called official GROMACS. Details are found in the README & COPYING
+ * files - if they are missing, get the official version at www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the papers on the package - you can find them in the top README file.
+ *
+ * For more info, check our website at http://www.gromacs.org
+ */
+/*! \libinternal file
+ * \brief
+ * Declares gmx::AnalysisDataParallelOptions.
+ *
+ * \if internal
+ * Implementation of this class is currently in datastorage.cpp.
+ * \endif
+ *
+ * \author Teemu Murtola <teemu.murtola@cbr.su.se>
+ * \inlibraryapi
+ * \ingroup module_analysisdata
+ */
+#ifndef GMX_ANALYSISDATA_PARALLELOPTIONS_H
+#define GMX_ANALYSISDATA_PARALLELOPTIONS_H
+
+namespace gmx
+{
+
+/*! \brief
+ * Parallelization options for analysis data objects.
+ *
+ * Methods in this class do not throw.
+ *
+ * \inlibraryapi
+ */
+class AnalysisDataParallelOptions
+{
+ public:
+ //! Constructs options for serial execution.
+ AnalysisDataParallelOptions();
+ /*! \brief
+ * Constructs options for parallel execution with given number of
+ * concurrent frames.
+ *
+ * \param[in] parallelizationFactor
+ * Number of frames that may be constructed concurrently.
+ * Must be >= 1.
+ */
+ explicit AnalysisDataParallelOptions(int parallelizationFactor);
+
+ //! Returns the number of frames that may be constructed concurrently.
+ int parallelizationFactor() const { return parallelizationFactor_; }
+
+ private:
+ int parallelizationFactor_;
+};
+
+} // namespace gmx
+
+#endif
* \brief
* Tests for analysis data functionality.
*
- * These tests check the functionality of gmx::AnalysisData, as well as its
- * base classes gmx::AbstractAnalysisData and gmx::AbstractAnalysisDataStored.
+ * These tests check the functionality of gmx::AnalysisData, as well as classes
+ * used in its implementation: gmx::AbstractAnalysisData and
+ * gmx::AnalysisDataStorage.
* Most checking is done using gmx::test::AnalysisDataTestFixture and mock
* modules that implement gmx::AnalysisDataModuleInterface.
*
#include <gtest/gtest.h>
#include "gromacs/analysisdata/analysisdata.h"
+#include "gromacs/analysisdata/paralleloptions.h"
#include "gromacs/fatalerror/exceptions.h"
#include "datatest.h"
ASSERT_NO_THROW(presentAllData(input, &data));
}
-/*
- * Tests that data is forwarded correctly to modules when the data is added as
- * individual points and not as full frames.
- */
-TEST_F(AnalysisDataTest, CallsModuleCorrectlyWithIndividualPoints)
-{
- gmx::test::AnalysisDataTestInput input(inputdata);
- gmx::AnalysisData data;
- data.setColumns(input.columnCount());
-
- ASSERT_NO_THROW(addStaticCheckerModule(input, &data));
- ASSERT_NO_THROW(addStaticColumnCheckerModule(input, 1, 2, &data));
- gmx::AnalysisDataHandle *handle = NULL;
- ASSERT_NO_THROW(handle = data.startData(NULL));
- for (int row = 0; row < input.frameCount(); ++row)
- {
- const gmx::test::AnalysisDataTestInputFrame &frame = input.frame(row);
- const gmx::test::AnalysisDataTestInputPointSet &points = frame.points();
- ASSERT_NO_THROW(handle->startFrame(row, frame.x(), frame.dx()));
- for (int column = 0; column < input.columnCount(); ++column)
- {
- ASSERT_NO_THROW(handle->addPoint(column, points.y(column)));
- }
- ASSERT_NO_THROW(handle->finishFrame());
- EXPECT_EQ(row + 1, data.frameCount());
- }
- ASSERT_NO_THROW(handle->finishData());
-}
-
/*
* Tests that data is forwarded correctly (in frame order) to modules when the
* data is added through multiple handles in non-increasing order.
ASSERT_NO_THROW(addStaticColumnCheckerModule(input, 1, 2, &data));
gmx::AnalysisDataHandle *handle1 = NULL;
gmx::AnalysisDataHandle *handle2 = NULL;
- ASSERT_NO_THROW(handle1 = data.startData(NULL));
- ASSERT_NO_THROW(handle2 = data.startData(NULL));
+ gmx::AnalysisDataParallelOptions options(2);
+ ASSERT_NO_THROW(handle1 = data.startData(options));
+ ASSERT_NO_THROW(handle2 = data.startData(options));
ASSERT_NO_THROW(presentDataFrame(input, 1, handle1));
ASSERT_NO_THROW(presentDataFrame(input, 0, handle2));
ASSERT_NO_THROW(presentDataFrame(input, 2, handle1));
#include <gtest/gtest.h>
#include "gromacs/analysisdata/analysisdata.h"
+#include "gromacs/analysisdata/paralleloptions.h"
#include "gromacs/fatalerror/gmxassert.h"
#include "gromacs/utility/format.h"
void AnalysisDataTestFixture::presentAllData(const AnalysisDataTestInput &input,
AnalysisData *data)
{
- gmx::AnalysisDataHandle *handle = NULL;
- handle = data->startData(NULL);
+ gmx::AnalysisDataParallelOptions options;
+ gmx::AnalysisDataHandle *handle = data->startData(options);
for (int row = 0; row < input.frameCount(); ++row)
{
presentDataFrame(input, row, handle);
for (int i = 0; i < frame.pointSetCount(); ++i)
{
const AnalysisDataTestInputPointSet &points = frame.points(i);
- handle->addPoints(0, points.size(), points.yptr(), points.dyptr(),
- points.presentptr());
+ for (int j = 0; j < points.size(); ++j)
+ {
+ handle->setPoint(j, points.y(j), points.dy(j), points.present(j));
+ }
+ if (input.isMultipoint())
+ {
+ handle->finishPointSet();
+ }
}
handle->finishFrame();
}
*
* If the data is not multipoint, each frame contains exactly one set of
* points. If there is more than one set of points, each of these sets is
- * passed separately to AnalysisDataHandle::addPoints().
+ * passed separately and AnalysisDataHandle::finishPointSet() called in
+ * between.
*
* \ingroup module_analysisdata
*/
int size() const { return y_.size(); }
real y(int i) const { return y_[i]; }
+ real dy(int i) const { return 0.0; }
+ real present(int i) const { return true; }
const std::vector<real> &yvector() const { return y_; }
- const real *yptr() const { return &y_[0]; }
- const real *dyptr() const { return NULL; }
- const bool *presentptr() const { return NULL; }
private:
std::vector<real> y_;
typedef std::map<std::string, AnalysisDataHandle *> HandleContainer;
Impl(TrajectoryAnalysisModule *module,
- /*AnalysisDataParallelOptions*/ void* opt,
+ const AnalysisDataParallelOptions &opt,
const SelectionCollection &selections);
~Impl();
{
public:
TrajectoryAnalysisModuleDataBasic(TrajectoryAnalysisModule *module,
- /*AnalysisDataParallelOptions*/ void* opt,
+ const AnalysisDataParallelOptions &opt,
const SelectionCollection &selections);
virtual void finish();
TrajectoryAnalysisModuleData::Impl::Impl(
TrajectoryAnalysisModule *module,
- AnalysisDataParallelOptions opt,
+ const AnalysisDataParallelOptions &opt,
const SelectionCollection &selections)
: _selections(selections)
{
TrajectoryAnalysisModuleData::TrajectoryAnalysisModuleData(
TrajectoryAnalysisModule *module,
- AnalysisDataParallelOptions opt,
+ const AnalysisDataParallelOptions &opt,
const SelectionCollection &selections)
: _impl(new Impl(module, opt, selections))
{
*/
TrajectoryAnalysisModuleDataBasic::TrajectoryAnalysisModuleDataBasic(
TrajectoryAnalysisModule *module,
- /*AnalysisDataParallelOptions*/ void* opt,
+ const AnalysisDataParallelOptions &opt,
const SelectionCollection &selections)
: TrajectoryAnalysisModuleData(module, opt, selections)
{
TrajectoryAnalysisModuleData *
-TrajectoryAnalysisModule::startFrames(AnalysisDataParallelOptions opt,
+TrajectoryAnalysisModule::startFrames(const AnalysisDataParallelOptions &opt,
const SelectionCollection &selections)
{
return new TrajectoryAnalysisModuleDataBasic(this, opt, selections);
class AbstractAnalysisData;
class AnalysisData;
class AnalysisDataHandle;
+class AnalysisDataParallelOptions;
class Options;
class Selection;
class SelectionCollection;
* The handles are accessible through dataHandle().
*/
TrajectoryAnalysisModuleData(TrajectoryAnalysisModule *module,
- /*AnalysisDataParallelOptions*/ void* opt,
+ const AnalysisDataParallelOptions &opt,
const SelectionCollection &selections);
/*! \brief
* \see TrajectoryAnalysisModuleData
*/
virtual TrajectoryAnalysisModuleData *startFrames(
- /*AnalysisDataParallelOptions*/ void* opt,
+ const AnalysisDataParallelOptions &opt,
const SelectionCollection &selections);
/*! \brief
* Analyzes a single frame.
#include <rmpbc.h>
#include <statutil.h>
+#include "gromacs/analysisdata/paralleloptions.h"
#include "gromacs/fatalerror/exceptions.h"
#include "gromacs/fatalerror/gmxassert.h"
#include "gromacs/options/asciihelpwriter.h"
t_pbc *ppbc = settings.hasPBC() ? &pbc : NULL;
int nframes = 0;
+ AnalysisDataParallelOptions dataOptions;
std::auto_ptr<TrajectoryAnalysisModuleData>
- pdata(module->startFrames(NULL, selections));
+ pdata(module->startFrames(dataOptions, selections));
do
{
common.initFrame();
}
if (_bAll)
{
- dh->addPoint(n + 1, angle);
+ dh->setPoint(n + 1, angle);
}
ave += angle;
++n;
{
ave /= n;
}
- dh->addPoint(g, ave);
+ dh->setPoint(g, ave);
}
dh->finishFrame();
}
}
r = norm(dx);
dh->startFrame(frnr, fr.time);
- dh->addPoint(0, r);
- dh->addPoints(1, 3, dx);
+ dh->setPoint(0, r);
+ dh->setPoints(1, 3, dx);
dh->finishFrame();
}
{
public:
ModuleData(TrajectoryAnalysisModule *module,
- AnalysisDataParallelOptions opt,
+ const AnalysisDataParallelOptions &opt,
const SelectionCollection &selections)
: TrajectoryAnalysisModuleData(module, opt, selections),
_mmap(NULL)
TrajectoryAnalysisModuleData *
-Select::startFrames(AnalysisDataParallelOptions opt,
+Select::startFrames(const AnalysisDataParallelOptions &opt,
const SelectionCollection &selections)
{
ModuleData *pdata = new ModuleData(this, opt, selections);
{
real normfac = _bFracNorm ? 1.0 / sel[g]->coveredFraction() : 1.0;
normfac /= _totsize[g];
- sdh->addPoint(g, sel[g]->posCount() * normfac);
+ sdh->setPoint(g, sel[g]->posCount() * normfac);
}
sdh->finishFrame();
}
cdh->startFrame(frnr, fr.time);
for (size_t g = 0; g < sel.size(); ++g)
{
- cdh->addPoint(g, sel[g]->coveredFraction());
+ cdh->setPoint(g, sel[g]->coveredFraction());
}
cdh->finishFrame();
}
idh->startFrame(frnr, fr.time);
for (size_t g = 0; g < sel.size(); ++g)
{
- idh->addPoint(0, sel[g]->posCount());
+ idh->setPoint(0, sel[g]->posCount());
+ idh->finishPointSet();
for (int i = 0; i < sel[g]->posCount(); ++i)
{
if (sel[g]->type() == INDEX_RES && !_bResInd)
{
- idh->addPoint(1, _top->atoms.resinfo[sel[g]->mapId(i)].nr);
+ idh->setPoint(1, _top->atoms.resinfo[sel[g]->mapId(i)].nr);
}
else
{
- idh->addPoint(1, sel[g]->mapId(i) + 1);
+ idh->setPoint(1, sel[g]->mapId(i) + 1);
}
+ idh->finishPointSet();
}
}
idh->finishFrame();
mdh->startFrame(frnr, fr.time);
for (int b = 0; b < d->_mmap->nr; ++b)
{
- mdh->addPoint(b, d->_mmap->refid[b] == -1 ? 0 : 1);
+ mdh->setPoint(b, d->_mmap->refid[b] == -1 ? 0 : 1);
}
mdh->finishFrame();
}
const TopologyInformation &top);
virtual TrajectoryAnalysisModuleData *startFrames(
- AnalysisDataParallelOptions opt,
+ const AnalysisDataParallelOptions &opt,
const SelectionCollection &selections);
virtual void analyzeFrame(int frnr, const t_trxframe &fr, t_pbc *pbc,
TrajectoryAnalysisModuleData *pdata);