2 * This file is part of the GROMACS molecular simulation package.
4 * Copyright (c) 2012,2013, by the GROMACS development team, led by
5 * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
6 * and including many others, as listed in the AUTHORS file in the
7 * top-level source directory and at http://www.gromacs.org.
9 * GROMACS is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU Lesser General Public License
11 * as published by the Free Software Foundation; either version 2.1
12 * of the License, or (at your option) any later version.
14 * GROMACS is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 * Lesser General Public License for more details.
19 * You should have received a copy of the GNU Lesser General Public
20 * License along with GROMACS; if not, see
21 * http://www.gnu.org/licenses, or write to the Free Software Foundation,
22 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
24 * If you want to redistribute modifications to GROMACS, please
25 * consider that scientific software is very special. Version
26 * control is crucial - bugs must be traceable. We will be happy to
27 * consider code for inclusion in the official distribution, but
28 * derived work must not be called official GROMACS. Details are found
29 * in the README & COPYING files - if they are missing, get the
30 * official version at http://www.gromacs.org.
32 * To help us fund GROMACS development, we humbly ask that you cite
33 * the research papers on the package. Check out http://www.gromacs.org.
37 * Implements classes in datastorage.h and paralleloptions.h.
39 * \author Teemu Murtola <teemu.murtola@gmail.com>
40 * \ingroup module_analysisdata
42 #include "datastorage.h"
49 #include "gromacs/analysisdata/abstractdata.h"
50 #include "gromacs/analysisdata/dataframe.h"
51 #include "gromacs/analysisdata/datamodulemanager.h"
52 #include "gromacs/analysisdata/paralleloptions.h"
53 #include "gromacs/utility/exceptions.h"
54 #include "gromacs/utility/gmxassert.h"
55 #include "gromacs/utility/uniqueptr.h"
60 /********************************************************************
61 * AnalysisDataParallelOptions
64 AnalysisDataParallelOptions::AnalysisDataParallelOptions()
65 : parallelizationFactor_(1)
70 AnalysisDataParallelOptions::AnalysisDataParallelOptions(int parallelizationFactor)
71 : parallelizationFactor_(parallelizationFactor)
73 GMX_RELEASE_ASSERT(parallelizationFactor >= 1,
74 "Invalid parallelization factor");
78 /********************************************************************
79 * AnalysisDataStorageImpl declaration
85 //! Smart pointer type for managing a storage frame builder.
86 typedef gmx_unique_ptr<AnalysisDataStorageFrame>::type
87 AnalysisDataFrameBuilderPointer;
90 * Private implementation class for AnalysisDataStorage.
92 * \ingroup module_analysisdata
94 class AnalysisDataStorageImpl
97 //! Smart pointer type for managing a stored frame.
98 typedef gmx_unique_ptr<AnalysisDataStorageFrameData>::type FramePointer;
100 //! Shorthand for a list of data frames that are currently stored.
101 typedef std::vector<FramePointer> FrameList;
102 //! Shorthand for a list of currently unused storage frame builders.
103 typedef std::vector<AnalysisDataFrameBuilderPointer> FrameBuilderList;
105 AnalysisDataStorageImpl();
107 //! Returns whether the storage is set to use multipoint data.
108 bool isMultipoint() const;
110 * Whether storage of all frames has been requested.
112 * Storage of all frames also works as expected if \a storageLimit_ is
113 * used in comparisons directly, but this method should be used to
114 * check how to manage \a frames_.
116 bool storeAll() const
118 return storageLimit_ == std::numeric_limits<int>::max();
120 //! Returns the index of the oldest frame that may be currently stored.
121 int firstStoredIndex() const;
122 //! Returns the index of the first frame that is not fully notified.
123 int firstUnnotifiedIndex() const { return firstUnnotifiedIndex_; }
125 * Computes index into \a frames_ for accessing frame \p index.
127 * \param[in] index Zero-based frame index.
128 * \retval -1 if \p index is not available in \a frames_.
132 int computeStorageLocation(int index) const;
135 * Computes an index into \a frames_ that is one past the last frame
140 size_t endStorageLocation() const;
143 * Extends \a frames_ to a new size.
145 * \throws std::bad_alloc if out of memory.
147 void extendBuffer(size_t newSize);
149 * Remove oldest frame from the storage to make space for a new one.
151 * Increments \a firstFrameLocation_ and reinitializes the frame that
152 * was made unavailable by this operation.
161 * Returns a frame builder object for use with a new frame.
163 * \throws std::bad_alloc if out of memory.
165 AnalysisDataFrameBuilderPointer getFrameBuilder();
168 * Returns whether notifications should be immediately fired.
170 * This is used to optimize multipoint handling for non-parallel cases,
171 * where it is not necessary to store even a single frame.
175 bool shouldNotifyImmediately() const
177 return isMultipoint() && storageLimit_ == 0 && pendingLimit_ == 1;
180 * Returns whether data needs to be stored at all.
182 * This is used to optimize multipoint handling for parallel cases
183 * (where shouldNotifyImmediately() returns false),
184 * where it is not necessary to store even a single frame.
187 * This could be extended to non-multipoint data as well.
191 bool needStorage() const
193 return storageLimit_ > 0 || (pendingLimit_ > 1 && modules_->hasSerialModules());
196 * Calls notification methods for new frames.
198 * \param[in] firstLocation First frame to consider.
199 * \throws unspecified Any exception thrown by frame notification
200 * methods in AbstractAnalysisData.
202 * Notifies \a data_ of new frames (from \p firstLocation and after
203 * that) if all previous frames have already been notified.
204 * Also rotates the \a frames_ buffer as necessary.
206 void notifyNextFrames(size_t firstLocation);
207 //! Implementation for AnalysisDataStorage::finishFrame().
208 void finishFrame(int index);
211 //! Parent data object to access data dimensionality etc.
212 const AbstractAnalysisData *data_;
213 //! Manager to use for notification calls.
214 AnalysisDataModuleManager *modules_;
216 * Number of past frames that need to be stored.
218 * Always non-negative. If storage of all frames has been requested,
219 * this is set to a large number.
223 * Number of future frames that may need to be started.
225 * Should always be at least one.
228 * Get rid of this alltogether, as it is no longer used much.
230 * \see AnalysisDataStorage::startFrame()
234 * Data frames that are currently stored.
236 * If storage of all frames has been requested, this is simply a vector
237 * of frames up to the latest frame that has been started.
238 * In this case, \a firstFrameLocation_ is always zero.
240 * If storage of all frames is not requested, this is a ring buffer of
241 * frames of size \c n=storageLimit_+pendingLimit_+1. If a frame with
242 * index \c index is currently stored, its location is
243 * \c index%frames_.size().
244 * When at most \a storageLimit_ first frames have been finished,
245 * this contains storage for the first \c n-1 frames.
246 * When more than \a storageLimit_ first frames have been finished,
247 * the oldest stored frame is stored in the location
248 * \a firstFrameLocation_, and \a storageLimit_ frames starting from
249 * this location are the last finished frames. \a pendingLimit_ frames
250 * follow, and some of these may be in progress or finished.
251 * There is always one unused frame in the buffer, which is initialized
252 * such that when \a firstFrameLocation_ is incremented, it becomes
253 * valid. This makes it easier to rotate the buffer in concurrent
254 * access scenarions (which are not yet otherwise implemented).
257 //! Location of oldest frame in \a frames_.
258 size_t firstFrameLocation_;
259 //! Index of the first frame that is not fully notified.
260 int firstUnnotifiedIndex_;
262 * Currently unused frame builders.
264 * The builders are cached to avoid repeatedly allocating memory for
265 * them. Typically, there are as many builders as there are concurrent
266 * users of the storage object. Whenever a frame is started, a builder
267 * is pulled from this pool by getFrameBuilder() (a new one is created
268 * if none are available), and assigned for that frame. When that
269 * frame is finished, the builder is returned to this pool.
271 FrameBuilderList builders_;
273 * Index of next frame that will be added to \a frames_.
275 * If all frames are not stored, this will be the index of the unused
276 * frame (see \a frames_).
281 /********************************************************************
282 * AnalysisDataStorageFrameImpl declaration
287 * Internal representation for a single stored frame.
289 * It is implemented such that the frame header is always valid, i.e.,
290 * header().isValid() returns always true.
292 * Methods in this class do not throw unless otherwise indicated.
294 * \ingroup module_analysisdata
296 class AnalysisDataStorageFrameData
299 //! Shorthand for a iterator into storage value containers.
300 typedef std::vector<AnalysisDataValue>::const_iterator ValueIterator;
302 //! Indicates what operations have been performed on a frame.
305 eMissing, //!< Frame has not yet been started.
306 eStarted, //!< startFrame() has been called.
307 eFinished, //!< finishFrame() has been called.
308 eNotified //!< Appropriate notifications have been sent.
312 * Create a new storage frame.
314 * \param storageImpl Storage object this frame belongs to.
315 * \param[in] index Zero-based index for the frame.
317 AnalysisDataStorageFrameData(AnalysisDataStorageImpl *storageImpl,
320 //! Whether the frame has been started with startFrame().
321 bool isStarted() const { return status_ >= eStarted; }
322 //! Whether the frame has been finished with finishFrame().
323 bool isFinished() const { return status_ >= eFinished; }
324 //! Whether all notifications have been sent.
325 bool isNotified() const { return status_ >= eNotified; }
326 //! Whether the frame is ready to be available outside the storage.
327 bool isAvailable() const { return status_ >= eFinished; }
329 //! Marks the frame as notified.
330 void markNotified() { status_ = eNotified; }
332 //! Returns the storage implementation object.
333 AnalysisDataStorageImpl &storageImpl() const { return storageImpl_; }
334 //! Returns the underlying data object (for data dimensionalities etc.).
335 const AbstractAnalysisData &baseData() const { return *storageImpl().data_; }
337 //! Returns header for the frame.
338 const AnalysisDataFrameHeader &header() const { return header_; }
339 //! Returns zero-based index of the frame.
340 int frameIndex() const { return header().index(); }
341 //! Returns the number of point sets for the frame.
342 int pointSetCount() const { return pointSets_.size(); }
344 //! Clears the frame for reusing as a new frame.
345 void clearFrame(int newIndex);
347 * Initializes the frame during AnalysisDataStorage::startFrame().
349 * \param[in] header Header to use for the new frame.
350 * \param[in] builder Builder object to use.
352 void startFrame(const AnalysisDataFrameHeader &header,
353 AnalysisDataFrameBuilderPointer builder);
354 //! Returns the builder for this frame.
355 AnalysisDataStorageFrame &builder() const
357 GMX_ASSERT(builder_, "Accessing builder for not-in-progress frame");
361 * Adds a new point set to this frame.
363 void addPointSet(int dataSetIndex, int firstColumn,
364 ValueIterator begin, ValueIterator end);
366 * Finalizes the frame during AnalysisDataStorage::finishFrame().
368 * \returns The builder object used by the frame, for reusing it for
371 AnalysisDataFrameBuilderPointer finishFrame(bool bMultipoint);
373 //! Returns frame reference to this frame.
374 AnalysisDataFrameRef frameReference() const
376 return AnalysisDataFrameRef(header_, values_, pointSets_);
378 //! Returns point set reference to a given point set.
379 AnalysisDataPointSetRef pointSet(int index) const;
382 //! Storage object that contains this frame.
383 AnalysisDataStorageImpl &storageImpl_;
384 //! Header for the frame.
385 AnalysisDataFrameHeader header_;
386 //! Values for the frame.
387 std::vector<AnalysisDataValue> values_;
388 //! Information about each point set in the frame.
389 std::vector<AnalysisDataPointSetInfo> pointSets_;
391 * Builder object for the frame.
393 * Non-NULL when the frame is in progress, i.e., has been started but
396 AnalysisDataFrameBuilderPointer builder_;
397 //! In what state the frame currently is.
400 GMX_DISALLOW_COPY_AND_ASSIGN(AnalysisDataStorageFrameData);
403 /********************************************************************
404 * AnalysisDataStorageImpl implementation
407 AnalysisDataStorageImpl::AnalysisDataStorageImpl()
408 : data_(NULL), modules_(NULL),
409 storageLimit_(0), pendingLimit_(1),
410 firstFrameLocation_(0), firstUnnotifiedIndex_(0), nextIndex_(0)
416 AnalysisDataStorageImpl::isMultipoint() const
418 GMX_ASSERT(data_ != NULL, "isMultipoint() called too early");
419 return data_->isMultipoint();
424 AnalysisDataStorageImpl::firstStoredIndex() const
426 return frames_[firstFrameLocation_]->frameIndex();
431 AnalysisDataStorageImpl::computeStorageLocation(int index) const
433 if (index < firstStoredIndex() || index >= nextIndex_)
437 return index % frames_.size();
442 AnalysisDataStorageImpl::endStorageLocation() const
446 return frames_.size();
448 if (frames_[0]->frameIndex() == 0 || firstFrameLocation_ == 0)
450 return frames_.size() - 1;
452 return firstFrameLocation_ - 1;
457 AnalysisDataStorageImpl::extendBuffer(size_t newSize)
459 frames_.reserve(newSize);
460 while (frames_.size() < newSize)
462 frames_.push_back(FramePointer(new AnalysisDataStorageFrameData(this, nextIndex_)));
465 // The unused frame should not be included in the count.
474 AnalysisDataStorageImpl::rotateBuffer()
476 GMX_ASSERT(!storeAll(),
477 "No need to rotate internal buffer if everything is stored");
478 size_t prevFirst = firstFrameLocation_;
479 size_t nextFirst = prevFirst + 1;
480 if (nextFirst == frames_.size())
484 firstFrameLocation_ = nextFirst;
485 frames_[prevFirst]->clearFrame(nextIndex_ + 1);
490 AnalysisDataFrameBuilderPointer
491 AnalysisDataStorageImpl::getFrameBuilder()
493 if (builders_.empty())
495 return AnalysisDataFrameBuilderPointer(new AnalysisDataStorageFrame(*data_));
497 AnalysisDataFrameBuilderPointer builder(move(builders_.back()));
498 builders_.pop_back();
499 return move(builder);
504 AnalysisDataStorageImpl::notifyNextFrames(size_t firstLocation)
506 if (frames_[firstLocation]->frameIndex() != firstUnnotifiedIndex_)
510 size_t i = firstLocation;
511 size_t end = endStorageLocation();
514 AnalysisDataStorageFrameData &storedFrame = *frames_[i];
515 if (!storedFrame.isFinished())
519 if (!storedFrame.isNotified())
521 // Increment before the notifications to make the frame available
522 // in the module callbacks.
523 ++firstUnnotifiedIndex_;
524 modules_->notifyFrameStart(storedFrame.header());
525 for (int j = 0; j < storedFrame.pointSetCount(); ++j)
527 modules_->notifyPointsAdd(storedFrame.pointSet(j));
529 modules_->notifyFrameFinish(storedFrame.header());
530 storedFrame.markNotified();
531 if (storedFrame.frameIndex() >= storageLimit_)
537 if (!storeAll() && i >= frames_.size())
546 AnalysisDataStorageImpl::finishFrame(int index)
548 const int storageIndex = computeStorageLocation(index);
549 GMX_RELEASE_ASSERT(storageIndex >= 0, "Out of bounds frame index");
551 AnalysisDataStorageFrameData &storedFrame = *frames_[storageIndex];
552 GMX_RELEASE_ASSERT(storedFrame.isStarted(),
553 "finishFrame() called for frame before startFrame()");
554 GMX_RELEASE_ASSERT(!storedFrame.isFinished(),
555 "finishFrame() called twice for the same frame");
556 GMX_RELEASE_ASSERT(storedFrame.frameIndex() == index,
557 "Inconsistent internal frame indexing");
558 builders_.push_back(storedFrame.finishFrame(isMultipoint()));
559 modules_->notifyParallelFrameFinish(storedFrame.header());
560 if (shouldNotifyImmediately())
562 ++firstUnnotifiedIndex_;
563 modules_->notifyFrameFinish(storedFrame.header());
564 if (storedFrame.frameIndex() >= storageLimit_)
571 notifyNextFrames(storageIndex);
576 /********************************************************************
577 * AnalysisDataStorageFrame implementation
580 AnalysisDataStorageFrameData::AnalysisDataStorageFrameData(
581 AnalysisDataStorageImpl *storageImpl,
583 : storageImpl_(*storageImpl), header_(index, 0.0, 0.0), status_(eMissing)
585 GMX_RELEASE_ASSERT(storageImpl->data_ != NULL,
586 "Storage frame constructed before data started");
587 // With non-multipoint data, the point set structure is static,
588 // so initialize it only once here.
589 if (!baseData().isMultipoint())
592 for (int i = 0; i < baseData().dataSetCount(); ++i)
594 int columnCount = baseData().columnCount(i);
595 pointSets_.push_back(
596 AnalysisDataPointSetInfo(offset, columnCount, i, 0));
597 offset += columnCount;
604 AnalysisDataStorageFrameData::clearFrame(int newIndex)
606 GMX_RELEASE_ASSERT(!builder_, "Should not clear an in-progress frame");
608 header_ = AnalysisDataFrameHeader(newIndex, 0.0, 0.0);
610 if (baseData().isMultipoint())
618 AnalysisDataStorageFrameData::startFrame(
619 const AnalysisDataFrameHeader &header,
620 AnalysisDataFrameBuilderPointer builder)
624 builder_ = move(builder);
625 builder_->data_ = this;
626 builder_->selectDataSet(0);
631 AnalysisDataStorageFrameData::addPointSet(int dataSetIndex, int firstColumn,
632 ValueIterator begin, ValueIterator end)
634 const int valueCount = end - begin;
635 AnalysisDataPointSetInfo pointSetInfo(0, valueCount,
636 dataSetIndex, firstColumn);
637 AnalysisDataPointSetRef pointSet(header(), pointSetInfo,
638 AnalysisDataValuesRef(begin, end));
639 storageImpl().modules_->notifyParallelPointsAdd(pointSet);
640 if (storageImpl().shouldNotifyImmediately())
642 storageImpl().modules_->notifyPointsAdd(pointSet);
644 else if (storageImpl().needStorage())
646 pointSets_.push_back(
647 AnalysisDataPointSetInfo(values_.size(), valueCount,
648 dataSetIndex, firstColumn));
649 std::copy(begin, end, std::back_inserter(values_));
654 AnalysisDataFrameBuilderPointer
655 AnalysisDataStorageFrameData::finishFrame(bool bMultipoint)
660 GMX_RELEASE_ASSERT(static_cast<int>(pointSets_.size()) == baseData().dataSetCount(),
661 "Point sets created for non-multipoint data");
662 values_ = builder_->values_;
663 builder_->clearValues();
664 for (int i = 0; i < pointSetCount(); ++i)
666 storageImpl().modules_->notifyParallelPointsAdd(pointSet(i));
671 GMX_RELEASE_ASSERT(!builder_->bPointSetInProgress_,
672 "Unfinished point set");
674 AnalysisDataFrameBuilderPointer builder(move(builder_));
676 return move(builder);
680 AnalysisDataPointSetRef
681 AnalysisDataStorageFrameData::pointSet(int index) const
683 GMX_ASSERT(index >= 0 && index < pointSetCount(),
684 "Invalid point set index");
685 return AnalysisDataPointSetRef(
686 header_, pointSets_[index],
687 AnalysisDataValuesRef(values_.begin(), values_.end()));
690 } // namespace internal
692 /********************************************************************
693 * AnalysisDataStorageFrame
696 AnalysisDataStorageFrame::AnalysisDataStorageFrame(
697 const AbstractAnalysisData &data)
698 : data_(NULL), currentDataSet_(0), currentOffset_(0),
699 columnCount_(data.columnCount(0)), bPointSetInProgress_(false)
701 int totalColumnCount = 0;
702 for (int i = 0; i < data.dataSetCount(); ++i)
704 totalColumnCount += data.columnCount(i);
706 values_.resize(totalColumnCount);
710 AnalysisDataStorageFrame::~AnalysisDataStorageFrame()
716 AnalysisDataStorageFrame::clearValues()
718 if (bPointSetInProgress_)
720 std::vector<AnalysisDataValue>::iterator i;
721 for (i = values_.begin(); i != values_.end(); ++i)
726 bPointSetInProgress_ = false;
731 AnalysisDataStorageFrame::selectDataSet(int index)
733 GMX_RELEASE_ASSERT(data_ != NULL, "Invalid frame accessed");
734 const AbstractAnalysisData &baseData = data_->baseData();
735 GMX_RELEASE_ASSERT(index >= 0 && index < baseData.dataSetCount(),
736 "Out of range data set index");
737 GMX_RELEASE_ASSERT(!baseData.isMultipoint() || !bPointSetInProgress_,
738 "Point sets in multipoint data cannot span data sets");
739 currentDataSet_ = index;
741 // TODO: Consider precalculating.
742 for (int i = 0; i < index; ++i)
744 currentOffset_ += baseData.columnCount(i);
746 columnCount_ = baseData.columnCount(index);
751 AnalysisDataStorageFrame::finishPointSet()
753 GMX_RELEASE_ASSERT(data_ != NULL, "Invalid frame accessed");
754 GMX_RELEASE_ASSERT(data_->baseData().isMultipoint(),
755 "Should not be called for non-multipoint data");
756 if (bPointSetInProgress_)
758 std::vector<AnalysisDataValue>::const_iterator begin
759 = values_.begin() + currentOffset_;
760 std::vector<AnalysisDataValue>::const_iterator end
761 = begin + columnCount_;
763 while (begin != end && !begin->isSet())
768 while (end != begin && !(end-1)->isSet())
776 data_->addPointSet(currentDataSet_, firstColumn, begin, end);
783 AnalysisDataStorageFrame::finishFrame()
785 GMX_RELEASE_ASSERT(data_ != NULL, "Invalid frame accessed");
786 data_->storageImpl().finishFrame(data_->frameIndex());
790 /********************************************************************
791 * AnalysisDataStorage
794 AnalysisDataStorage::AnalysisDataStorage()
800 AnalysisDataStorage::~AnalysisDataStorage()
806 AnalysisDataStorage::frameCount() const
808 return impl_->firstUnnotifiedIndex();
813 AnalysisDataStorage::tryGetDataFrame(int index) const
815 int storageIndex = impl_->computeStorageLocation(index);
816 if (storageIndex == -1)
818 return AnalysisDataFrameRef();
820 const internal::AnalysisDataStorageFrameData &storedFrame
821 = *impl_->frames_[storageIndex];
822 if (!storedFrame.isAvailable())
824 return AnalysisDataFrameRef();
826 return storedFrame.frameReference();
831 AnalysisDataStorage::requestStorage(int nframes)
833 // Handle the case when everything needs to be stored.
836 impl_->storageLimit_ = std::numeric_limits<int>::max();
839 // Check whether an earlier call has requested more storage.
840 if (nframes < impl_->storageLimit_)
844 impl_->storageLimit_ = nframes;
850 AnalysisDataStorage::startDataStorage(AbstractAnalysisData *data,
851 AnalysisDataModuleManager *modules)
853 modules->notifyDataStart(data);
854 // Data needs to be set before calling extendBuffer()
856 impl_->modules_ = modules;
857 if (!impl_->storeAll())
859 // 2 = pending limit (1) + 1
860 impl_->extendBuffer(impl_->storageLimit_ + 2);
866 AnalysisDataStorage::startParallelDataStorage(
867 AbstractAnalysisData *data,
868 AnalysisDataModuleManager *modules,
869 const AnalysisDataParallelOptions &options)
871 const int pendingLimit = 2 * options.parallelizationFactor() - 1;
872 impl_->pendingLimit_ = pendingLimit;
873 modules->notifyParallelDataStart(data, options);
874 // Data needs to be set before calling extendBuffer()
876 impl_->modules_ = modules;
877 if (!impl_->storeAll())
879 impl_->extendBuffer(impl_->storageLimit_ + pendingLimit + 1);
884 AnalysisDataStorageFrame &
885 AnalysisDataStorage::startFrame(const AnalysisDataFrameHeader &header)
887 GMX_ASSERT(header.isValid(), "Invalid header");
888 internal::AnalysisDataStorageFrameData *storedFrame;
889 if (impl_->storeAll())
891 size_t size = header.index() + 1;
892 if (impl_->frames_.size() < size)
894 impl_->extendBuffer(size);
896 storedFrame = impl_->frames_[header.index()].get();
900 int storageIndex = impl_->computeStorageLocation(header.index());
901 if (storageIndex == -1)
903 GMX_THROW(APIError("Out of bounds frame index"));
905 storedFrame = impl_->frames_[storageIndex].get();
907 GMX_RELEASE_ASSERT(!storedFrame->isStarted(),
908 "startFrame() called twice for the same frame");
909 GMX_RELEASE_ASSERT(storedFrame->frameIndex() == header.index(),
910 "Inconsistent internal frame indexing");
911 storedFrame->startFrame(header, impl_->getFrameBuilder());
912 impl_->modules_->notifyParallelFrameStart(header);
913 if (impl_->shouldNotifyImmediately())
915 impl_->modules_->notifyFrameStart(header);
917 return storedFrame->builder();
921 AnalysisDataStorageFrame &
922 AnalysisDataStorage::startFrame(int index, real x, real dx)
924 return startFrame(AnalysisDataFrameHeader(index, x, dx));
928 AnalysisDataStorageFrame &
929 AnalysisDataStorage::currentFrame(int index)
931 const int storageIndex = impl_->computeStorageLocation(index);
932 GMX_RELEASE_ASSERT(storageIndex >= 0, "Out of bounds frame index");
934 internal::AnalysisDataStorageFrameData &storedFrame = *impl_->frames_[storageIndex];
935 GMX_RELEASE_ASSERT(storedFrame.isStarted(),
936 "currentFrame() called for frame before startFrame()");
937 GMX_RELEASE_ASSERT(!storedFrame.isFinished(),
938 "currentFrame() called for frame after finishFrame()");
939 GMX_RELEASE_ASSERT(storedFrame.frameIndex() == index,
940 "Inconsistent internal frame indexing");
941 return storedFrame.builder();
946 AnalysisDataStorage::finishFrame(int index)
948 impl_->finishFrame(index);
952 AnalysisDataStorage::finishDataStorage()
954 // TODO: Check that all frames have been finished etc.
955 impl_->builders_.clear();
956 impl_->modules_->notifyDataFinish();