2 * This file is part of the GROMACS molecular simulation package.
4 * Copyright (c) 2012,2013,2014,2015,2016 by the GROMACS development team.
5 * Copyright (c) 2017,2018,2019,2020,2021, by the GROMACS development team, led by
6 * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
7 * and including many others, as listed in the AUTHORS file in the
8 * top-level source directory and at http://www.gromacs.org.
10 * GROMACS is free software; you can redistribute it and/or
11 * modify it under the terms of the GNU Lesser General Public License
12 * as published by the Free Software Foundation; either version 2.1
13 * of the License, or (at your option) any later version.
15 * GROMACS is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 * Lesser General Public License for more details.
20 * You should have received a copy of the GNU Lesser General Public
21 * License along with GROMACS; if not, see
22 * http://www.gnu.org/licenses, or write to the Free Software Foundation,
23 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
25 * If you want to redistribute modifications to GROMACS, please
26 * consider that scientific software is very special. Version
27 * control is crucial - bugs must be traceable. We will be happy to
28 * consider code for inclusion in the official distribution, but
29 * derived work must not be called official GROMACS. Details are found
30 * in the README & COPYING files - if they are missing, get the
31 * official version at http://www.gromacs.org.
33 * To help us fund GROMACS development, we humbly ask that you cite
34 * the research papers on the package. Check out http://www.gromacs.org.
38 * Implements classes in datastorage.h and paralleloptions.h.
40 * \author Teemu Murtola <teemu.murtola@gmail.com>
41 * \ingroup module_analysisdata
45 #include "datastorage.h"
53 #include "gromacs/analysisdata/abstractdata.h"
54 #include "gromacs/analysisdata/dataframe.h"
55 #include "gromacs/analysisdata/datamodulemanager.h"
56 #include "gromacs/analysisdata/paralleloptions.h"
57 #include "gromacs/utility/classhelpers.h"
58 #include "gromacs/utility/exceptions.h"
59 #include "gromacs/utility/gmxassert.h"
64 /********************************************************************
65 * AnalysisDataParallelOptions
68 AnalysisDataParallelOptions::AnalysisDataParallelOptions() : parallelizationFactor_(1) {}
71 AnalysisDataParallelOptions::AnalysisDataParallelOptions(int parallelizationFactor) :
72 parallelizationFactor_(parallelizationFactor)
74 GMX_RELEASE_ASSERT(parallelizationFactor >= 1, "Invalid parallelization factor");
78 /********************************************************************
79 * AnalysisDataStorageImpl declaration
85 //! Smart pointer type for managing a storage frame builder.
86 typedef std::unique_ptr<AnalysisDataStorageFrame> AnalysisDataFrameBuilderPointer;
89 * Private implementation class for AnalysisDataStorage.
91 * \ingroup module_analysisdata
93 class AnalysisDataStorageImpl
96 //! Smart pointer type for managing a stored frame.
97 typedef std::unique_ptr<AnalysisDataStorageFrameData> FramePointer;
99 //! Shorthand for a list of data frames that are currently stored.
100 typedef std::vector<FramePointer> FrameList;
101 //! Shorthand for a list of currently unused storage frame builders.
102 typedef std::vector<AnalysisDataFrameBuilderPointer> FrameBuilderList;
104 AnalysisDataStorageImpl();
106 //! Returns whether the storage is set to use multipoint data.
107 bool isMultipoint() const;
109 * Whether storage of all frames has been requested.
111 * Storage of all frames also works as expected if \a storageLimit_ is
112 * used in comparisons directly, but this method should be used to
113 * check how to manage \a frames_.
115 bool storeAll() const { return storageLimit_ == std::numeric_limits<int>::max(); }
116 //! Returns the index of the oldest frame that may be currently stored.
117 int firstStoredIndex() const;
118 //! Returns the index of the first frame that is not fully notified.
119 int firstUnnotifiedIndex() const { return firstUnnotifiedIndex_; }
121 * Computes index into \a frames_ for accessing frame \p index.
123 * \param[in] index Zero-based frame index.
124 * \retval -1 if \p index is not available in \a frames_.
128 int computeStorageLocation(int index) const;
131 * Computes an index into \a frames_ that is one past the last frame
136 size_t endStorageLocation() const;
139 * Extends \a frames_ to a new size.
141 * \throws std::bad_alloc if out of memory.
143 void extendBuffer(size_t newSize);
145 * Remove oldest frame from the storage to make space for a new one.
147 * Increments \a firstFrameLocation_ and reinitializes the frame that
148 * was made unavailable by this operation.
157 * Returns a frame builder object for use with a new frame.
159 * \throws std::bad_alloc if out of memory.
161 AnalysisDataFrameBuilderPointer getFrameBuilder();
164 * Returns whether notifications should be immediately fired.
166 * This is used to optimize multipoint handling for non-parallel cases,
167 * where it is not necessary to store even a single frame.
171 bool shouldNotifyImmediately() const
173 return isMultipoint() && storageLimit_ == 0 && pendingLimit_ == 1;
176 * Returns whether data needs to be stored at all.
178 * This is used to optimize multipoint handling for parallel cases
179 * (where shouldNotifyImmediately() returns false),
180 * where it is not necessary to store even a single frame.
183 * This could be extended to non-multipoint data as well.
187 bool needStorage() const
189 return storageLimit_ > 0 || (pendingLimit_ > 1 && modules_->hasSerialModules());
191 //! Implementation for AnalysisDataStorage::finishFrame().
192 void finishFrame(int index);
194 * Implementation for AnalysisDataStorage::finishFrameSerial().
196 void finishFrameSerial(int index);
199 //! Parent data object to access data dimensionality etc.
200 const AbstractAnalysisData* data_;
201 //! Manager to use for notification calls.
202 AnalysisDataModuleManager* modules_;
204 * Number of past frames that need to be stored.
206 * Always non-negative. If storage of all frames has been requested,
207 * this is set to a large number.
211 * Number of future frames that may need to be started.
213 * Should always be at least one.
216 * Get rid of this alltogether, as it is no longer used much.
218 * \see AnalysisDataStorage::startFrame()
222 * Data frames that are currently stored.
224 * If storage of all frames has been requested, this is simply a vector
225 * of frames up to the latest frame that has been started.
226 * In this case, \a firstFrameLocation_ is always zero.
228 * If storage of all frames is not requested, this is a ring buffer of
229 * frames of size \c n=storageLimit_+pendingLimit_+1. If a frame with
230 * index \c index is currently stored, its location is
231 * \c index%frames_.size().
232 * When at most \a storageLimit_ first frames have been finished,
233 * this contains storage for the first \c n-1 frames.
234 * When more than \a storageLimit_ first frames have been finished,
235 * the oldest stored frame is stored in the location
236 * \a firstFrameLocation_, and \a storageLimit_ frames starting from
237 * this location are the last finished frames. \a pendingLimit_ frames
238 * follow, and some of these may be in progress or finished.
239 * There is always one unused frame in the buffer, which is initialized
240 * such that when \a firstFrameLocation_ is incremented, it becomes
241 * valid. This makes it easier to rotate the buffer in concurrent
242 * access scenarions (which are not yet otherwise implemented).
245 //! Location of oldest frame in \a frames_.
246 size_t firstFrameLocation_;
247 //! Index of the first frame that is not fully notified.
248 int firstUnnotifiedIndex_;
250 * Currently unused frame builders.
252 * The builders are cached to avoid repeatedly allocating memory for
253 * them. Typically, there are as many builders as there are concurrent
254 * users of the storage object. Whenever a frame is started, a builder
255 * is pulled from this pool by getFrameBuilder() (a new one is created
256 * if none are available), and assigned for that frame. When that
257 * frame is finished, the builder is returned to this pool.
259 FrameBuilderList builders_;
261 * Index of next frame that will be added to \a frames_.
263 * If all frames are not stored, this will be the index of the unused
264 * frame (see \a frames_).
269 /********************************************************************
270 * AnalysisDataStorageFrameImpl declaration
275 * Internal representation for a single stored frame.
277 * It is implemented such that the frame header is always valid, i.e.,
278 * header().isValid() returns always true.
280 * Methods in this class do not throw unless otherwise indicated.
282 * \ingroup module_analysisdata
284 class AnalysisDataStorageFrameData
287 //! Indicates what operations have been performed on a frame.
290 eMissing, //!< Frame has not yet been started.
291 eStarted, //!< startFrame() has been called.
292 eFinished, //!< finishFrame() has been called.
293 eNotified //!< Appropriate notifications have been sent.
297 * Create a new storage frame.
299 * \param storageImpl Storage object this frame belongs to.
300 * \param[in] index Zero-based index for the frame.
302 AnalysisDataStorageFrameData(AnalysisDataStorageImpl* storageImpl, int index);
304 //! Whether the frame has been started with startFrame().
305 bool isStarted() const { return status_ >= eStarted; }
306 //! Whether the frame has been finished with finishFrame().
307 bool isFinished() const { return status_ >= eFinished; }
308 //! Whether all notifications have been sent.
309 bool isNotified() const { return status_ >= eNotified; }
310 //! Whether the frame is ready to be available outside the storage.
311 bool isAvailable() const { return status_ >= eFinished; }
313 //! Marks the frame as notified.
314 void markNotified() { status_ = eNotified; }
316 //! Returns the storage implementation object.
317 AnalysisDataStorageImpl& storageImpl() const { return storageImpl_; }
318 //! Returns the underlying data object (for data dimensionalities etc.).
319 const AbstractAnalysisData& baseData() const { return *storageImpl().data_; }
321 //! Returns header for the frame.
322 const AnalysisDataFrameHeader& header() const { return header_; }
323 //! Returns zero-based index of the frame.
324 int frameIndex() const { return header().index(); }
325 //! Returns the number of point sets for the frame.
326 int pointSetCount() const { return pointSets_.size(); }
328 //! Clears the frame for reusing as a new frame.
329 void clearFrame(int newIndex);
331 * Initializes the frame during AnalysisDataStorage::startFrame().
333 * \param[in] header Header to use for the new frame.
334 * \param[in] builder Builder object to use.
336 void startFrame(const AnalysisDataFrameHeader& header, AnalysisDataFrameBuilderPointer builder);
337 //! Returns the builder for this frame.
338 AnalysisDataStorageFrame& builder() const
340 GMX_ASSERT(builder_, "Accessing builder for not-in-progress frame");
344 * Adds a new point set to this frame.
346 void addPointSet(int dataSetIndex, int firstColumn, ArrayRef<const AnalysisDataValue> v);
348 * Finalizes the frame during AnalysisDataStorage::finishFrame().
350 * \returns The builder object used by the frame, for reusing it for
353 AnalysisDataFrameBuilderPointer finishFrame(bool bMultipoint);
355 //! Returns frame reference to this frame.
356 AnalysisDataFrameRef frameReference() const
358 return AnalysisDataFrameRef(header_, values_, pointSets_);
360 //! Returns point set reference to a given point set.
361 AnalysisDataPointSetRef pointSet(int index) const;
364 //! Storage object that contains this frame.
365 AnalysisDataStorageImpl& storageImpl_;
366 //! Header for the frame.
367 AnalysisDataFrameHeader header_;
368 //! Values for the frame.
369 std::vector<AnalysisDataValue> values_;
370 //! Information about each point set in the frame.
371 std::vector<AnalysisDataPointSetInfo> pointSets_;
373 * Builder object for the frame.
375 * Non-NULL when the frame is in progress, i.e., has been started but
378 AnalysisDataFrameBuilderPointer builder_;
379 //! In what state the frame currently is.
382 GMX_DISALLOW_COPY_AND_ASSIGN(AnalysisDataStorageFrameData);
385 /********************************************************************
386 * AnalysisDataStorageImpl implementation
389 AnalysisDataStorageImpl::AnalysisDataStorageImpl() :
394 firstFrameLocation_(0),
395 firstUnnotifiedIndex_(0),
401 bool AnalysisDataStorageImpl::isMultipoint() const
403 GMX_ASSERT(data_ != nullptr, "isMultipoint() called too early");
404 return data_->isMultipoint();
408 int AnalysisDataStorageImpl::firstStoredIndex() const
410 return frames_[firstFrameLocation_]->frameIndex();
414 int AnalysisDataStorageImpl::computeStorageLocation(int index) const
416 if (index < firstStoredIndex() || index >= nextIndex_)
420 return index % frames_.size();
424 size_t AnalysisDataStorageImpl::endStorageLocation() const
428 return frames_.size();
430 if (frames_[0]->frameIndex() == 0 || firstFrameLocation_ == 0)
432 return frames_.size() - 1;
434 return firstFrameLocation_ - 1;
438 void AnalysisDataStorageImpl::extendBuffer(size_t newSize)
440 frames_.reserve(newSize);
441 while (frames_.size() < newSize)
443 frames_.push_back(std::make_unique<AnalysisDataStorageFrameData>(this, nextIndex_));
446 // The unused frame should not be included in the count.
454 void AnalysisDataStorageImpl::rotateBuffer()
456 GMX_ASSERT(!storeAll(), "No need to rotate internal buffer if everything is stored");
457 size_t prevFirst = firstFrameLocation_;
458 size_t nextFirst = prevFirst + 1;
459 if (nextFirst == frames_.size())
463 firstFrameLocation_ = nextFirst;
464 frames_[prevFirst]->clearFrame(nextIndex_ + 1);
469 AnalysisDataFrameBuilderPointer AnalysisDataStorageImpl::getFrameBuilder()
471 if (builders_.empty())
473 return AnalysisDataFrameBuilderPointer(new AnalysisDataStorageFrame(*data_));
475 AnalysisDataFrameBuilderPointer builder(std::move(builders_.back()));
476 builders_.pop_back();
481 void AnalysisDataStorageImpl::finishFrame(int index)
483 const int storageIndex = computeStorageLocation(index);
484 GMX_RELEASE_ASSERT(storageIndex >= 0, "Out of bounds frame index");
486 AnalysisDataStorageFrameData& storedFrame = *frames_[storageIndex];
487 GMX_RELEASE_ASSERT(storedFrame.isStarted(),
488 "finishFrame() called for frame before startFrame()");
489 GMX_RELEASE_ASSERT(!storedFrame.isFinished(), "finishFrame() called twice for the same frame");
490 GMX_RELEASE_ASSERT(storedFrame.frameIndex() == index, "Inconsistent internal frame indexing");
491 builders_.push_back(storedFrame.finishFrame(isMultipoint()));
492 modules_->notifyParallelFrameFinish(storedFrame.header());
493 if (pendingLimit_ == 1)
495 finishFrameSerial(index);
500 void AnalysisDataStorageImpl::finishFrameSerial(int index)
502 GMX_RELEASE_ASSERT(index == firstUnnotifiedIndex_, "Out of order finisFrameSerial() calls");
503 const int storageIndex = computeStorageLocation(index);
504 GMX_RELEASE_ASSERT(storageIndex >= 0, "Out of bounds frame index");
506 AnalysisDataStorageFrameData& storedFrame = *frames_[storageIndex];
507 GMX_RELEASE_ASSERT(storedFrame.frameIndex() == index, "Inconsistent internal frame indexing");
508 GMX_RELEASE_ASSERT(storedFrame.isFinished(), "finishFrameSerial() called before finishFrame()");
509 GMX_RELEASE_ASSERT(!storedFrame.isNotified(),
510 "finishFrameSerial() called twice for the same frame");
511 // Increment before the notifications to make the frame available
512 // in the module callbacks.
513 ++firstUnnotifiedIndex_;
514 if (shouldNotifyImmediately())
516 modules_->notifyFrameFinish(storedFrame.header());
520 modules_->notifyFrameStart(storedFrame.header());
521 for (int j = 0; j < storedFrame.pointSetCount(); ++j)
523 modules_->notifyPointsAdd(storedFrame.pointSet(j));
525 modules_->notifyFrameFinish(storedFrame.header());
527 storedFrame.markNotified();
528 if (storedFrame.frameIndex() >= storageLimit_)
535 /********************************************************************
536 * AnalysisDataStorageFrame implementation
539 AnalysisDataStorageFrameData::AnalysisDataStorageFrameData(AnalysisDataStorageImpl* storageImpl, int index) :
540 storageImpl_(*storageImpl),
541 header_(index, 0.0, 0.0),
544 GMX_RELEASE_ASSERT(storageImpl->data_ != nullptr,
545 "Storage frame constructed before data started");
546 // With non-multipoint data, the point set structure is static,
547 // so initialize it only once here.
548 if (!baseData().isMultipoint())
551 for (int i = 0; i < baseData().dataSetCount(); ++i)
553 int columnCount = baseData().columnCount(i);
554 pointSets_.emplace_back(offset, columnCount, i, 0);
555 offset += columnCount;
561 void AnalysisDataStorageFrameData::clearFrame(int newIndex)
563 GMX_RELEASE_ASSERT(!builder_, "Should not clear an in-progress frame");
565 header_ = AnalysisDataFrameHeader(newIndex, 0.0, 0.0);
567 if (baseData().isMultipoint())
574 void AnalysisDataStorageFrameData::startFrame(const AnalysisDataFrameHeader& header,
575 AnalysisDataFrameBuilderPointer builder)
579 builder_ = std::move(builder);
580 builder_->data_ = this;
581 builder_->selectDataSet(0);
585 void AnalysisDataStorageFrameData::addPointSet(int dataSetIndex,
587 ArrayRef<const AnalysisDataValue> v)
589 const int valueCount = v.size();
590 AnalysisDataPointSetInfo pointSetInfo(0, valueCount, dataSetIndex, firstColumn);
591 AnalysisDataPointSetRef pointSet(header(), pointSetInfo, v);
592 storageImpl().modules_->notifyParallelPointsAdd(pointSet);
593 if (storageImpl().shouldNotifyImmediately())
595 storageImpl().modules_->notifyPointsAdd(pointSet);
597 else if (storageImpl().needStorage())
599 pointSets_.emplace_back(values_.size(), valueCount, dataSetIndex, firstColumn);
600 std::copy(v.begin(), v.end(), std::back_inserter(values_));
605 AnalysisDataFrameBuilderPointer AnalysisDataStorageFrameData::finishFrame(bool bMultipoint)
610 GMX_RELEASE_ASSERT(ssize(pointSets_) == baseData().dataSetCount(),
611 "Point sets created for non-multipoint data");
612 values_ = builder_->values_;
613 builder_->clearValues();
614 for (int i = 0; i < pointSetCount(); ++i)
616 storageImpl().modules_->notifyParallelPointsAdd(pointSet(i));
621 GMX_RELEASE_ASSERT(!builder_->bPointSetInProgress_, "Unfinished point set");
623 AnalysisDataFrameBuilderPointer builder(std::move(builder_));
629 AnalysisDataPointSetRef AnalysisDataStorageFrameData::pointSet(int index) const
631 GMX_ASSERT(index >= 0 && index < pointSetCount(), "Invalid point set index");
632 return AnalysisDataPointSetRef(header_, pointSets_[index], values_);
635 } // namespace internal
637 /********************************************************************
638 * AnalysisDataStorageFrame
641 AnalysisDataStorageFrame::AnalysisDataStorageFrame(const AbstractAnalysisData& data) :
645 columnCount_(data.columnCount(0)),
646 bPointSetInProgress_(false)
648 int totalColumnCount = 0;
649 for (int i = 0; i < data.dataSetCount(); ++i)
651 totalColumnCount += data.columnCount(i);
653 values_.resize(totalColumnCount);
657 AnalysisDataStorageFrame::~AnalysisDataStorageFrame() {}
660 void AnalysisDataStorageFrame::clearValues()
662 if (bPointSetInProgress_)
664 std::vector<AnalysisDataValue>::iterator i;
665 for (i = values_.begin(); i != values_.end(); ++i)
670 bPointSetInProgress_ = false;
674 void AnalysisDataStorageFrame::selectDataSet(int index)
676 GMX_RELEASE_ASSERT(data_ != nullptr, "Invalid frame accessed");
677 const AbstractAnalysisData& baseData = data_->baseData();
678 GMX_RELEASE_ASSERT(index >= 0 && index < baseData.dataSetCount(), "Out of range data set index");
679 GMX_RELEASE_ASSERT(!baseData.isMultipoint() || !bPointSetInProgress_,
680 "Point sets in multipoint data cannot span data sets");
681 currentDataSet_ = index;
683 // TODO: Consider precalculating.
684 for (int i = 0; i < index; ++i)
686 currentOffset_ += baseData.columnCount(i);
688 columnCount_ = baseData.columnCount(index);
692 void AnalysisDataStorageFrame::finishPointSet()
694 GMX_RELEASE_ASSERT(data_ != nullptr, "Invalid frame accessed");
695 GMX_RELEASE_ASSERT(data_->baseData().isMultipoint(),
696 "Should not be called for non-multipoint data");
697 if (bPointSetInProgress_)
699 size_t begin = currentOffset_;
700 size_t end = begin + columnCount_;
702 while (begin != end && !values_[begin].isSet())
707 while (end != begin && !values_[end - 1].isSet())
716 currentDataSet_, firstColumn, makeConstArrayRef(values_).subArray(begin, end - begin));
722 void AnalysisDataStorageFrame::finishFrame()
724 GMX_RELEASE_ASSERT(data_ != nullptr, "Invalid frame accessed");
725 data_->storageImpl().finishFrame(data_->frameIndex());
729 /********************************************************************
730 * AnalysisDataStorage
733 AnalysisDataStorage::AnalysisDataStorage() : impl_(new Impl()) {}
736 AnalysisDataStorage::~AnalysisDataStorage() {}
739 int AnalysisDataStorage::frameCount() const
741 return impl_->firstUnnotifiedIndex();
745 AnalysisDataFrameRef AnalysisDataStorage::tryGetDataFrame(int index) const
747 int storageIndex = impl_->computeStorageLocation(index);
748 if (storageIndex == -1)
750 return AnalysisDataFrameRef();
752 const internal::AnalysisDataStorageFrameData& storedFrame = *impl_->frames_[storageIndex];
753 if (!storedFrame.isAvailable())
755 return AnalysisDataFrameRef();
757 return storedFrame.frameReference();
761 bool AnalysisDataStorage::requestStorage(int nframes)
763 // Handle the case when everything needs to be stored.
766 impl_->storageLimit_ = std::numeric_limits<int>::max();
769 // Check whether an earlier call has requested more storage.
770 if (nframes < impl_->storageLimit_)
774 impl_->storageLimit_ = nframes;
779 void AnalysisDataStorage::startDataStorage(AbstractAnalysisData* data, AnalysisDataModuleManager* modules)
781 modules->notifyDataStart(data);
782 // Data needs to be set before calling extendBuffer()
784 impl_->modules_ = modules;
785 if (!impl_->storeAll())
787 // 2 = pending limit (1) + 1
788 impl_->extendBuffer(impl_->storageLimit_ + 2);
793 void AnalysisDataStorage::startParallelDataStorage(AbstractAnalysisData* data,
794 AnalysisDataModuleManager* modules,
795 const AnalysisDataParallelOptions& options)
797 const int pendingLimit = options.parallelizationFactor();
798 impl_->pendingLimit_ = pendingLimit;
799 modules->notifyParallelDataStart(data, options);
800 // Data needs to be set before calling extendBuffer()
802 impl_->modules_ = modules;
803 if (!impl_->storeAll())
805 impl_->extendBuffer(impl_->storageLimit_ + pendingLimit + 1);
810 AnalysisDataStorageFrame& AnalysisDataStorage::startFrame(const AnalysisDataFrameHeader& header)
812 GMX_ASSERT(header.isValid(), "Invalid header");
813 internal::AnalysisDataStorageFrameData* storedFrame = nullptr;
814 if (impl_->storeAll())
816 size_t size = header.index() + 1;
817 if (impl_->frames_.size() < size)
819 impl_->extendBuffer(size);
821 storedFrame = impl_->frames_[header.index()].get();
825 int storageIndex = impl_->computeStorageLocation(header.index());
826 if (storageIndex == -1)
828 GMX_THROW(APIError("Out of bounds frame index"));
830 storedFrame = impl_->frames_[storageIndex].get();
832 GMX_RELEASE_ASSERT(!storedFrame->isStarted(), "startFrame() called twice for the same frame");
833 GMX_RELEASE_ASSERT(storedFrame->frameIndex() == header.index(),
834 "Inconsistent internal frame indexing");
835 storedFrame->startFrame(header, impl_->getFrameBuilder());
836 impl_->modules_->notifyParallelFrameStart(header);
837 if (impl_->shouldNotifyImmediately())
839 impl_->modules_->notifyFrameStart(header);
841 return storedFrame->builder();
845 AnalysisDataStorageFrame& AnalysisDataStorage::startFrame(int index, real x, real dx)
847 return startFrame(AnalysisDataFrameHeader(index, x, dx));
851 AnalysisDataStorageFrame& AnalysisDataStorage::currentFrame(int index)
853 const int storageIndex = impl_->computeStorageLocation(index);
854 GMX_RELEASE_ASSERT(storageIndex >= 0, "Out of bounds frame index");
856 internal::AnalysisDataStorageFrameData& storedFrame = *impl_->frames_[storageIndex];
857 GMX_RELEASE_ASSERT(storedFrame.isStarted(),
858 "currentFrame() called for frame before startFrame()");
859 GMX_RELEASE_ASSERT(!storedFrame.isFinished(),
860 "currentFrame() called for frame after finishFrame()");
861 GMX_RELEASE_ASSERT(storedFrame.frameIndex() == index, "Inconsistent internal frame indexing");
862 return storedFrame.builder();
866 void AnalysisDataStorage::finishFrame(int index)
868 impl_->finishFrame(index);
871 void AnalysisDataStorage::finishFrameSerial(int index)
873 if (impl_->pendingLimit_ > 1)
875 impl_->finishFrameSerial(index);
879 void AnalysisDataStorage::finishDataStorage()
881 // TODO: Check that all frames have been finished etc.
882 impl_->builders_.clear();
883 impl_->modules_->notifyDataFinish();