3 * This source code is part of
7 * GROningen MAchine for Chemical Simulations
9 * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
10 * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
11 * Copyright (c) 2001-2009, The GROMACS development team,
12 * check out http://www.gromacs.org for more information.
14 * This program is free software; you can redistribute it and/or
15 * modify it under the terms of the GNU General Public License
16 * as published by the Free Software Foundation; either version 2
17 * of the License, or (at your option) any later version.
19 * If you want to redistribute modifications, please consider that
20 * scientific software is very special. Version control is crucial -
21 * bugs must be traceable. We will be happy to consider code for
22 * inclusion in the official distribution, but derived work must not
23 * be called official GROMACS. Details are found in the README & COPYING
24 * files - if they are missing, get the official version at www.gromacs.org.
26 * To help us fund GROMACS development, we humbly ask that you cite
27 * the papers on the package - you can find them in the top README file.
29 * For more info, check our website at http://www.gromacs.org
33 * Implements classes in datastorage.h and paralleloptions.h.
35 * \author Teemu Murtola <teemu.murtola@cbr.su.se>
36 * \ingroup module_analysisdata
38 #include "datastorage.h"
43 #include "gromacs/analysisdata/abstractdata.h"
44 #include "gromacs/analysisdata/dataframe.h"
45 #include "gromacs/analysisdata/paralleloptions.h"
46 #include "gromacs/utility/exceptions.h"
47 #include "gromacs/utility/gmxassert.h"
48 #include "gromacs/utility/uniqueptr.h"
53 /********************************************************************
54 * AnalysisDataParallelOptions
57 AnalysisDataParallelOptions::AnalysisDataParallelOptions()
58 : parallelizationFactor_(1)
63 AnalysisDataParallelOptions::AnalysisDataParallelOptions(int parallelizationFactor)
64 : parallelizationFactor_(parallelizationFactor)
66 GMX_RELEASE_ASSERT(parallelizationFactor >= 1,
67 "Invalid parallelization factor");
71 /********************************************************************
72 * AnalysisDataStorage::Impl
76 * Private implementation class for AnalysisDataStorage.
78 * \ingroup module_analysisdata
80 class AnalysisDataStorage::Impl
83 //! Smart pointer type for managing a stored frame.
84 typedef gmx_unique_ptr<AnalysisDataStorageFrame>::type FramePointer;
87 * Stored information about a single stored frame.
89 * Methods in this class do not throw.
93 //! Indicates what operations have been performed on a frame.
96 eMissing, //!< Frame has not yet been started.
97 eStarted, //!< startFrame() has been called.
98 eFinished, //!< finishFrame() has been called.
99 eNotified //!< Appropriate notifications have been sent.
102 //! Constructs an object that manages a given frame object.
103 explicit StoredFrame(AnalysisDataStorageFrame *frame)
104 : frame(frame), status(eMissing)
107 //! Whether the frame has been started with startFrame().
108 bool isStarted() const { return status >= eStarted; }
109 //! Whether the frame has been finished with finishFrame().
110 bool isFinished() const { return status >= eFinished; }
111 //! Whether all notifications have been sent.
112 bool isNotified() const { return status >= eNotified; }
113 //! Whether the frame is ready to be available outside the storage.
114 bool isAvailable() const { return status >= eFinished; }
122 //! In what state the frame currently is.
126 //! Shorthand for a list of data frames that are currently stored.
127 typedef std::vector<StoredFrame> FrameList;
131 //! Returns the number of columns in the attached data.
132 int columnCount() const;
133 //! Returns whether the storage is set to use multipoint data.
134 bool isMultipoint() const;
136 * Whether storage of all frames has been requested.
138 * Storage of all frames also works as expected if \a storageLimit_ is
139 * used in comparisons directly, but this method should be used to
140 * check how to manage \a frames_.
142 bool storeAll() const
144 return storageLimit_ == std::numeric_limits<int>::max();
146 //! Returns the index of the oldest frame that may be currently stored.
147 int firstStoredIndex() const;
149 * Computes index into \a frames_ for accessing frame \p index.
151 * \param[in] index Zero-based frame index.
152 * \retval -1 if \p index is not available in \a frames_.
156 int computeStorageLocation(int index) const;
159 * Computes an index into \a frames_ that is one past the last frame
164 size_t endStorageLocation() const;
167 * Extends \a frames_ to a new size.
169 * \throws std::bad_alloc if out of memory.
171 void extendBuffer(AnalysisDataStorage *storage, size_t newSize);
173 * Remove oldest frame from the storage to make space for a new one.
175 * Increments \a firstFrameLocation_ and reinitializes the frame that
176 * was made unavailable by this operation.
185 * Calls notification method in \a data_.
187 * \throws unspecified Any exception thrown by
188 * AbstractAnalysisData::notifyPointsAdd().
190 void notifyPointSet(const AnalysisDataPointSetRef &points);
192 * Calls notification methods for new frames.
194 * \param[in] firstLocation First frame to consider.
195 * \throws unspecified Any exception thrown by frame notification
196 * methods in AbstractAnalysisData.
198 * Notifies \a data_ of new frames (from \p firstLocation and after
199 * that) if all previous frames have already been notified.
200 * Also rotates the \a frames_ buffer as necessary.
202 void notifyNextFrames(size_t firstLocation);
204 //! Data object to use for notification calls.
205 AbstractAnalysisData *data_;
207 * Whether the storage has been set to allow multipoint.
209 * Should be possible to remove once full support for multipoint data
210 * has been implemented; isMultipoint() can simply return
211 * \c data_->isMultipoint() in that case.
215 * Number of past frames that need to be stored.
217 * Always non-negative. If storage of all frames has been requested,
218 * this is set to a large number.
222 * Number of future frames that may need to be started.
224 * Should always be at least one.
226 * \see AnalysisDataStorage::startFrame()
230 * Data frames that are currently stored.
232 * If storage of all frames has been requested, this is simply a vector
233 * of frames up to the latest frame that has been started.
234 * In this case, \a firstFrameLocation_ is always zero.
236 * If storage of all frames is not requested, this is a ring buffer of
237 * frames of size \c n=storageLimit_+pendingLimit_+1. If a frame with
238 * index \c index is currently stored, its location is
239 * \c index%frames_.size().
240 * When at most \a storageLimit_ first frames have been finished,
241 * this contains storage for the first \c n-1 frames.
242 * When more than \a storageLimit_ first frames have been finished,
243 * the oldest stored frame is stored in the location
244 * \a firstFrameLocation_, and \a storageLimit_ frames starting from
245 * this location are the last finished frames. \a pendingLimit_ frames
246 * follow, and some of these may be in progress or finished.
247 * There is always one unused frame in the buffer, which is initialized
248 * such that when \a firstFrameLocation_ is incremented, it becomes
249 * valid. This makes it easier to rotate the buffer in concurrent
250 * access scenarions (which are not yet otherwise implemented).
253 //! Location of oldest frame in \a frames_.
254 size_t firstFrameLocation_;
256 * Index of next frame that will be added to \a frames_.
258 * If all frames are not stored, this will be the index of the unused
259 * frame (see \a frames_).
264 AnalysisDataStorage::Impl::Impl()
265 : data_(NULL), bMultipoint_(false),
266 storageLimit_(0), pendingLimit_(1), firstFrameLocation_(0), nextIndex_(0)
272 AnalysisDataStorage::Impl::columnCount() const
274 GMX_ASSERT(data_ != NULL, "columnCount() called too early");
275 return data_->columnCount();
280 AnalysisDataStorage::Impl::isMultipoint() const
287 AnalysisDataStorage::Impl::firstStoredIndex() const
289 return frames_[firstFrameLocation_].frame->frameIndex();
294 AnalysisDataStorage::Impl::computeStorageLocation(int index) const
296 if (index < firstStoredIndex() || index >= nextIndex_)
300 return index % frames_.size();
305 AnalysisDataStorage::Impl::endStorageLocation() const
309 return frames_.size();
311 if (frames_[0].frame->frameIndex() == 0 || firstFrameLocation_ == 0)
313 return frames_.size() - 1;
315 return firstFrameLocation_ - 1;
320 AnalysisDataStorage::Impl::extendBuffer(AnalysisDataStorage *storage,
323 frames_.reserve(newSize);
324 while (frames_.size() < newSize)
326 frames_.push_back(StoredFrame(
327 new AnalysisDataStorageFrame(storage, columnCount(), nextIndex_)));
330 // The unused frame should not be included in the count.
339 AnalysisDataStorage::Impl::rotateBuffer()
341 GMX_ASSERT(!storeAll(),
342 "No need to rotate internal buffer if everything is stored");
343 size_t prevFirst = firstFrameLocation_;
344 size_t nextFirst = prevFirst + 1;
345 if (nextFirst == frames_.size())
349 firstFrameLocation_ = nextFirst;
350 StoredFrame &prevFrame = frames_[prevFirst];
351 prevFrame.status = StoredFrame::eMissing;
352 prevFrame.frame->header_ = AnalysisDataFrameHeader(nextIndex_ + 1, 0.0, 0.0);
353 prevFrame.frame->clearValues();
359 AnalysisDataStorage::Impl::notifyPointSet(const AnalysisDataPointSetRef &points)
361 data_->notifyPointsAdd(points);
366 AnalysisDataStorage::Impl::notifyNextFrames(size_t firstLocation)
368 if (firstLocation != firstFrameLocation_)
370 // firstLocation can only be zero here if !storeAll() because
371 // firstFrameLocation_ is always zero for storeAll()
373 (firstLocation == 0 ? frames_.size() - 1 : firstLocation - 1);
374 if (!frames_[prevIndex].isNotified())
379 size_t i = firstLocation;
380 size_t end = endStorageLocation();
383 Impl::StoredFrame &storedFrame = frames_[i];
384 if (!storedFrame.isFinished())
388 if (storedFrame.status == StoredFrame::eFinished)
390 data_->notifyFrameStart(storedFrame.frame->header());
391 data_->notifyPointsAdd(storedFrame.frame->currentPoints());
392 data_->notifyFrameFinish(storedFrame.frame->header());
393 storedFrame.status = StoredFrame::eNotified;
394 if (storedFrame.frame->frameIndex() >= storageLimit_)
400 if (!storeAll() && i >= frames_.size())
408 /********************************************************************
409 * AnalysisDataStorageFrame
412 AnalysisDataStorageFrame::AnalysisDataStorageFrame(AnalysisDataStorage *storage,
413 int columnCount, int index)
414 : storage_(*storage), header_(index, 0.0, 0.0), values_(columnCount)
419 AnalysisDataStorageFrame::~AnalysisDataStorageFrame()
424 AnalysisDataPointSetRef
425 AnalysisDataStorageFrame::currentPoints() const
427 std::vector<AnalysisDataValue>::const_iterator begin = values_.begin();
428 std::vector<AnalysisDataValue>::const_iterator end = values_.end();
429 while (begin != end && !begin->isSet())
433 while (end != begin && !(end-1)->isSet())
437 int firstColumn = (begin != end) ? begin - values_.begin() : 0;
438 return AnalysisDataPointSetRef(header_, firstColumn,
439 AnalysisDataValuesRef(begin, end));
444 AnalysisDataStorageFrame::clearValues()
446 std::vector<AnalysisDataValue>::iterator i;
447 for (i = values_.begin(); i != values_.end(); ++i)
455 AnalysisDataStorageFrame::finishPointSet()
457 storage_.impl_->notifyPointSet(currentPoints());
462 /********************************************************************
463 * AnalysisDataStorage
466 AnalysisDataStorage::AnalysisDataStorage()
472 AnalysisDataStorage::~AnalysisDataStorage()
478 AnalysisDataStorage::setMultipoint(bool bMultipoint)
480 if (bMultipoint && impl_->storageLimit_ > 0)
482 GMX_THROW(APIError("Storage of multipoint data not supported"));
484 impl_->bMultipoint_ = bMultipoint;
489 AnalysisDataStorage::setParallelOptions(const AnalysisDataParallelOptions &opt)
491 impl_->pendingLimit_ = 2 * opt.parallelizationFactor() - 1;
496 AnalysisDataStorage::tryGetDataFrame(int index) const
498 if (impl_->isMultipoint())
500 return AnalysisDataFrameRef();
502 int storageIndex = impl_->computeStorageLocation(index);
503 if (storageIndex == -1)
505 return AnalysisDataFrameRef();
507 const Impl::StoredFrame &storedFrame = impl_->frames_[storageIndex];
508 if (!storedFrame.isAvailable())
510 return AnalysisDataFrameRef();
512 const Impl::FramePointer &frame = storedFrame.frame;
513 return AnalysisDataFrameRef(frame->header(), frame->values_);
518 AnalysisDataStorage::requestStorage(int nframes)
520 if (impl_->isMultipoint())
525 // Handle the case when everything needs to be stored.
528 impl_->storageLimit_ = std::numeric_limits<int>::max();
531 // Check whether an earlier call has requested more storage.
532 if (nframes < impl_->storageLimit_)
536 impl_->storageLimit_ = nframes;
542 AnalysisDataStorage::startDataStorage(AbstractAnalysisData *data)
544 // Data needs to be set before calling extendBuffer()
546 setMultipoint(data->isMultipoint());
547 if (!impl_->storeAll())
549 impl_->extendBuffer(this, impl_->storageLimit_ + impl_->pendingLimit_ + 1);
554 AnalysisDataStorageFrame &
555 AnalysisDataStorage::startFrame(const AnalysisDataFrameHeader &header)
557 GMX_ASSERT(header.isValid(), "Invalid header");
558 Impl::StoredFrame *storedFrame;
559 if (impl_->storeAll())
561 size_t size = header.index() + 1;
562 if (impl_->frames_.size() < size)
564 impl_->extendBuffer(this, size);
566 storedFrame = &impl_->frames_[header.index()];
570 int storageIndex = impl_->computeStorageLocation(header.index());
571 if (storageIndex == -1)
573 GMX_THROW(APIError("Out of bounds frame index"));
575 storedFrame = &impl_->frames_[storageIndex];
577 GMX_RELEASE_ASSERT(!storedFrame->isStarted(),
578 "startFrame() called twice for the same frame");
579 GMX_RELEASE_ASSERT(storedFrame->frame->frameIndex() == header.index(),
580 "Inconsistent internal frame indexing");
581 storedFrame->status = Impl::StoredFrame::eStarted;
582 storedFrame->frame->header_ = header;
583 if (impl_->isMultipoint())
585 impl_->data_->notifyFrameStart(header);
587 return *storedFrame->frame;
591 AnalysisDataStorageFrame &
592 AnalysisDataStorage::startFrame(int index, real x, real dx)
594 return startFrame(AnalysisDataFrameHeader(index, x, dx));
598 AnalysisDataStorageFrame &
599 AnalysisDataStorage::currentFrame(int index)
601 int storageIndex = impl_->computeStorageLocation(index);
602 GMX_RELEASE_ASSERT(storageIndex >= 0, "Out of bounds frame index");
603 Impl::StoredFrame &storedFrame = impl_->frames_[storageIndex];
604 GMX_RELEASE_ASSERT(storedFrame.isStarted(),
605 "currentFrame() called for frame before startFrame()");
606 GMX_RELEASE_ASSERT(!storedFrame.isFinished(),
607 "currentFrame() called for frame after finishFrame()");
608 GMX_RELEASE_ASSERT(storedFrame.frame->frameIndex() == index,
609 "Inconsistent internal frame indexing");
610 return *storedFrame.frame;
615 AnalysisDataStorage::finishFrame(int index)
617 int storageIndex = impl_->computeStorageLocation(index);
618 GMX_RELEASE_ASSERT(storageIndex >= 0, "Out of bounds frame index");
619 Impl::StoredFrame &storedFrame = impl_->frames_[storageIndex];
620 GMX_RELEASE_ASSERT(storedFrame.isStarted(),
621 "finishFrame() called for frame before startFrame()");
622 GMX_RELEASE_ASSERT(!storedFrame.isFinished(),
623 "finishFrame() called twice for the same frame");
624 GMX_RELEASE_ASSERT(storedFrame.frame->frameIndex() == index,
625 "Inconsistent internal frame indexing");
626 storedFrame.status = Impl::StoredFrame::eFinished;
627 if (impl_->isMultipoint())
629 // TODO: Check that the last point set has been finished
630 impl_->data_->notifyFrameFinish(storedFrame.frame->header());
631 if (storedFrame.frame->frameIndex() >= impl_->storageLimit_)
633 impl_->rotateBuffer();
638 impl_->notifyNextFrames(storageIndex);
644 AnalysisDataStorage::finishFrame(const AnalysisDataStorageFrame &frame)
646 finishFrame(frame.frameIndex());