2 * This file is part of the GROMACS molecular simulation package.
4 * Copyright (c) 2012,2013,2014,2015,2016,2017,2018, 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
44 #include "datastorage.h"
52 #include "gromacs/analysisdata/abstractdata.h"
53 #include "gromacs/analysisdata/dataframe.h"
54 #include "gromacs/analysisdata/datamodulemanager.h"
55 #include "gromacs/analysisdata/paralleloptions.h"
56 #include "gromacs/compat/make_unique.h"
57 #include "gromacs/utility/exceptions.h"
58 #include "gromacs/utility/gmxassert.h"
63 /********************************************************************
64 * AnalysisDataParallelOptions
67 AnalysisDataParallelOptions::AnalysisDataParallelOptions()
68 : parallelizationFactor_(1)
73 AnalysisDataParallelOptions::AnalysisDataParallelOptions(int parallelizationFactor)
74 : parallelizationFactor_(parallelizationFactor)
76 GMX_RELEASE_ASSERT(parallelizationFactor >= 1,
77 "Invalid parallelization factor");
81 /********************************************************************
82 * AnalysisDataStorageImpl declaration
88 //! Smart pointer type for managing a storage frame builder.
89 typedef std::unique_ptr<AnalysisDataStorageFrame>
90 AnalysisDataFrameBuilderPointer;
93 * Private implementation class for AnalysisDataStorage.
95 * \ingroup module_analysisdata
97 class AnalysisDataStorageImpl
100 //! Smart pointer type for managing a stored frame.
101 typedef std::unique_ptr<AnalysisDataStorageFrameData> FramePointer;
103 //! Shorthand for a list of data frames that are currently stored.
104 typedef std::vector<FramePointer> FrameList;
105 //! Shorthand for a list of currently unused storage frame builders.
106 typedef std::vector<AnalysisDataFrameBuilderPointer> FrameBuilderList;
108 AnalysisDataStorageImpl();
110 //! Returns whether the storage is set to use multipoint data.
111 bool isMultipoint() const;
113 * Whether storage of all frames has been requested.
115 * Storage of all frames also works as expected if \a storageLimit_ is
116 * used in comparisons directly, but this method should be used to
117 * check how to manage \a frames_.
119 bool storeAll() const
121 return storageLimit_ == std::numeric_limits<int>::max();
123 //! Returns the index of the oldest frame that may be currently stored.
124 int firstStoredIndex() const;
125 //! Returns the index of the first frame that is not fully notified.
126 int firstUnnotifiedIndex() const { return firstUnnotifiedIndex_; }
128 * Computes index into \a frames_ for accessing frame \p index.
130 * \param[in] index Zero-based frame index.
131 * \retval -1 if \p index is not available in \a frames_.
135 int computeStorageLocation(int index) const;
138 * Computes an index into \a frames_ that is one past the last frame
143 size_t endStorageLocation() const;
146 * Extends \a frames_ to a new size.
148 * \throws std::bad_alloc if out of memory.
150 void extendBuffer(size_t newSize);
152 * Remove oldest frame from the storage to make space for a new one.
154 * Increments \a firstFrameLocation_ and reinitializes the frame that
155 * was made unavailable by this operation.
164 * Returns a frame builder object for use with a new frame.
166 * \throws std::bad_alloc if out of memory.
168 AnalysisDataFrameBuilderPointer getFrameBuilder();
171 * Returns whether notifications should be immediately fired.
173 * This is used to optimize multipoint handling for non-parallel cases,
174 * where it is not necessary to store even a single frame.
178 bool shouldNotifyImmediately() const
180 return isMultipoint() && storageLimit_ == 0 && pendingLimit_ == 1;
183 * Returns whether data needs to be stored at all.
185 * This is used to optimize multipoint handling for parallel cases
186 * (where shouldNotifyImmediately() returns false),
187 * where it is not necessary to store even a single frame.
190 * This could be extended to non-multipoint data as well.
194 bool needStorage() const
196 return storageLimit_ > 0 || (pendingLimit_ > 1 && modules_->hasSerialModules());
198 //! Implementation for AnalysisDataStorage::finishFrame().
199 void finishFrame(int index);
201 * Implementation for AnalysisDataStorage::finishFrameSerial().
203 void finishFrameSerial(int index);
206 //! Parent data object to access data dimensionality etc.
207 const AbstractAnalysisData *data_;
208 //! Manager to use for notification calls.
209 AnalysisDataModuleManager *modules_;
211 * Number of past frames that need to be stored.
213 * Always non-negative. If storage of all frames has been requested,
214 * this is set to a large number.
218 * Number of future frames that may need to be started.
220 * Should always be at least one.
223 * Get rid of this alltogether, as it is no longer used much.
225 * \see AnalysisDataStorage::startFrame()
229 * Data frames that are currently stored.
231 * If storage of all frames has been requested, this is simply a vector
232 * of frames up to the latest frame that has been started.
233 * In this case, \a firstFrameLocation_ is always zero.
235 * If storage of all frames is not requested, this is a ring buffer of
236 * frames of size \c n=storageLimit_+pendingLimit_+1. If a frame with
237 * index \c index is currently stored, its location is
238 * \c index%frames_.size().
239 * When at most \a storageLimit_ first frames have been finished,
240 * this contains storage for the first \c n-1 frames.
241 * When more than \a storageLimit_ first frames have been finished,
242 * the oldest stored frame is stored in the location
243 * \a firstFrameLocation_, and \a storageLimit_ frames starting from
244 * this location are the last finished frames. \a pendingLimit_ frames
245 * follow, and some of these may be in progress or finished.
246 * There is always one unused frame in the buffer, which is initialized
247 * such that when \a firstFrameLocation_ is incremented, it becomes
248 * valid. This makes it easier to rotate the buffer in concurrent
249 * access scenarions (which are not yet otherwise implemented).
252 //! Location of oldest frame in \a frames_.
253 size_t firstFrameLocation_;
254 //! Index of the first frame that is not fully notified.
255 int firstUnnotifiedIndex_;
257 * Currently unused frame builders.
259 * The builders are cached to avoid repeatedly allocating memory for
260 * them. Typically, there are as many builders as there are concurrent
261 * users of the storage object. Whenever a frame is started, a builder
262 * is pulled from this pool by getFrameBuilder() (a new one is created
263 * if none are available), and assigned for that frame. When that
264 * frame is finished, the builder is returned to this pool.
266 FrameBuilderList builders_;
268 * Index of next frame that will be added to \a frames_.
270 * If all frames are not stored, this will be the index of the unused
271 * frame (see \a frames_).
276 /********************************************************************
277 * AnalysisDataStorageFrameImpl declaration
282 * Internal representation for a single stored frame.
284 * It is implemented such that the frame header is always valid, i.e.,
285 * header().isValid() returns always true.
287 * Methods in this class do not throw unless otherwise indicated.
289 * \ingroup module_analysisdata
291 class AnalysisDataStorageFrameData
294 //! Indicates what operations have been performed on a frame.
297 eMissing, //!< Frame has not yet been started.
298 eStarted, //!< startFrame() has been called.
299 eFinished, //!< finishFrame() has been called.
300 eNotified //!< Appropriate notifications have been sent.
304 * Create a new storage frame.
306 * \param storageImpl Storage object this frame belongs to.
307 * \param[in] index Zero-based index for the frame.
309 AnalysisDataStorageFrameData(AnalysisDataStorageImpl *storageImpl,
312 //! Whether the frame has been started with startFrame().
313 bool isStarted() const { return status_ >= eStarted; }
314 //! Whether the frame has been finished with finishFrame().
315 bool isFinished() const { return status_ >= eFinished; }
316 //! Whether all notifications have been sent.
317 bool isNotified() const { return status_ >= eNotified; }
318 //! Whether the frame is ready to be available outside the storage.
319 bool isAvailable() const { return status_ >= eFinished; }
321 //! Marks the frame as notified.
322 void markNotified() { status_ = eNotified; }
324 //! Returns the storage implementation object.
325 AnalysisDataStorageImpl &storageImpl() const { return storageImpl_; }
326 //! Returns the underlying data object (for data dimensionalities etc.).
327 const AbstractAnalysisData &baseData() const { return *storageImpl().data_; }
329 //! Returns header for the frame.
330 const AnalysisDataFrameHeader &header() const { return header_; }
331 //! Returns zero-based index of the frame.
332 int frameIndex() const { return header().index(); }
333 //! Returns the number of point sets for the frame.
334 int pointSetCount() const { return pointSets_.size(); }
336 //! Clears the frame for reusing as a new frame.
337 void clearFrame(int newIndex);
339 * Initializes the frame during AnalysisDataStorage::startFrame().
341 * \param[in] header Header to use for the new frame.
342 * \param[in] builder Builder object to use.
344 void startFrame(const AnalysisDataFrameHeader &header,
345 AnalysisDataFrameBuilderPointer builder);
346 //! Returns the builder for this frame.
347 AnalysisDataStorageFrame &builder() const
349 GMX_ASSERT(builder_, "Accessing builder for not-in-progress frame");
353 * Adds a new point set to this frame.
355 void addPointSet(int dataSetIndex, int firstColumn,
356 ArrayRef<const AnalysisDataValue> v);
358 * Finalizes the frame during AnalysisDataStorage::finishFrame().
360 * \returns The builder object used by the frame, for reusing it for
363 AnalysisDataFrameBuilderPointer finishFrame(bool bMultipoint);
365 //! Returns frame reference to this frame.
366 AnalysisDataFrameRef frameReference() const
368 return AnalysisDataFrameRef(header_, values_, pointSets_);
370 //! Returns point set reference to a given point set.
371 AnalysisDataPointSetRef pointSet(int index) const;
374 //! Storage object that contains this frame.
375 AnalysisDataStorageImpl &storageImpl_;
376 //! Header for the frame.
377 AnalysisDataFrameHeader header_;
378 //! Values for the frame.
379 std::vector<AnalysisDataValue> values_;
380 //! Information about each point set in the frame.
381 std::vector<AnalysisDataPointSetInfo> pointSets_;
383 * Builder object for the frame.
385 * Non-NULL when the frame is in progress, i.e., has been started but
388 AnalysisDataFrameBuilderPointer builder_;
389 //! In what state the frame currently is.
392 GMX_DISALLOW_COPY_AND_ASSIGN(AnalysisDataStorageFrameData);
395 /********************************************************************
396 * AnalysisDataStorageImpl implementation
399 AnalysisDataStorageImpl::AnalysisDataStorageImpl()
400 : data_(nullptr), modules_(nullptr),
401 storageLimit_(0), pendingLimit_(1),
402 firstFrameLocation_(0), firstUnnotifiedIndex_(0), nextIndex_(0)
408 AnalysisDataStorageImpl::isMultipoint() const
410 GMX_ASSERT(data_ != nullptr, "isMultipoint() called too early");
411 return data_->isMultipoint();
416 AnalysisDataStorageImpl::firstStoredIndex() const
418 return frames_[firstFrameLocation_]->frameIndex();
423 AnalysisDataStorageImpl::computeStorageLocation(int index) const
425 if (index < firstStoredIndex() || index >= nextIndex_)
429 return index % frames_.size();
434 AnalysisDataStorageImpl::endStorageLocation() const
438 return frames_.size();
440 if (frames_[0]->frameIndex() == 0 || firstFrameLocation_ == 0)
442 return frames_.size() - 1;
444 return firstFrameLocation_ - 1;
449 AnalysisDataStorageImpl::extendBuffer(size_t newSize)
451 frames_.reserve(newSize);
452 while (frames_.size() < newSize)
454 frames_.push_back(compat::make_unique<AnalysisDataStorageFrameData>(this, nextIndex_));
457 // The unused frame should not be included in the count.
466 AnalysisDataStorageImpl::rotateBuffer()
468 GMX_ASSERT(!storeAll(),
469 "No need to rotate internal buffer if everything is stored");
470 size_t prevFirst = firstFrameLocation_;
471 size_t nextFirst = prevFirst + 1;
472 if (nextFirst == frames_.size())
476 firstFrameLocation_ = nextFirst;
477 frames_[prevFirst]->clearFrame(nextIndex_ + 1);
482 AnalysisDataFrameBuilderPointer
483 AnalysisDataStorageImpl::getFrameBuilder()
485 if (builders_.empty())
487 return AnalysisDataFrameBuilderPointer(new AnalysisDataStorageFrame(*data_));
489 AnalysisDataFrameBuilderPointer builder(std::move(builders_.back()));
490 builders_.pop_back();
496 AnalysisDataStorageImpl::finishFrame(int index)
498 const int storageIndex = computeStorageLocation(index);
499 GMX_RELEASE_ASSERT(storageIndex >= 0, "Out of bounds frame index");
501 AnalysisDataStorageFrameData &storedFrame = *frames_[storageIndex];
502 GMX_RELEASE_ASSERT(storedFrame.isStarted(),
503 "finishFrame() called for frame before startFrame()");
504 GMX_RELEASE_ASSERT(!storedFrame.isFinished(),
505 "finishFrame() called twice for the same frame");
506 GMX_RELEASE_ASSERT(storedFrame.frameIndex() == index,
507 "Inconsistent internal frame indexing");
508 builders_.push_back(storedFrame.finishFrame(isMultipoint()));
509 modules_->notifyParallelFrameFinish(storedFrame.header());
510 if (pendingLimit_ == 1)
512 finishFrameSerial(index);
518 AnalysisDataStorageImpl::finishFrameSerial(int index)
520 GMX_RELEASE_ASSERT(index == firstUnnotifiedIndex_,
521 "Out of order finisFrameSerial() calls");
522 const int storageIndex = computeStorageLocation(index);
523 GMX_RELEASE_ASSERT(storageIndex >= 0, "Out of bounds frame index");
525 AnalysisDataStorageFrameData &storedFrame = *frames_[storageIndex];
526 GMX_RELEASE_ASSERT(storedFrame.frameIndex() == index,
527 "Inconsistent internal frame indexing");
528 GMX_RELEASE_ASSERT(storedFrame.isFinished(),
529 "finishFrameSerial() called before finishFrame()");
530 GMX_RELEASE_ASSERT(!storedFrame.isNotified(),
531 "finishFrameSerial() called twice for the same frame");
532 // Increment before the notifications to make the frame available
533 // in the module callbacks.
534 ++firstUnnotifiedIndex_;
535 if (shouldNotifyImmediately())
537 modules_->notifyFrameFinish(storedFrame.header());
541 modules_->notifyFrameStart(storedFrame.header());
542 for (int j = 0; j < storedFrame.pointSetCount(); ++j)
544 modules_->notifyPointsAdd(storedFrame.pointSet(j));
546 modules_->notifyFrameFinish(storedFrame.header());
548 storedFrame.markNotified();
549 if (storedFrame.frameIndex() >= storageLimit_)
556 /********************************************************************
557 * AnalysisDataStorageFrame implementation
560 AnalysisDataStorageFrameData::AnalysisDataStorageFrameData(
561 AnalysisDataStorageImpl *storageImpl,
563 : storageImpl_(*storageImpl), header_(index, 0.0, 0.0), status_(eMissing)
565 GMX_RELEASE_ASSERT(storageImpl->data_ != nullptr,
566 "Storage frame constructed before data started");
567 // With non-multipoint data, the point set structure is static,
568 // so initialize it only once here.
569 if (!baseData().isMultipoint())
572 for (int i = 0; i < baseData().dataSetCount(); ++i)
574 int columnCount = baseData().columnCount(i);
575 pointSets_.emplace_back(offset, columnCount, i, 0);
576 offset += columnCount;
583 AnalysisDataStorageFrameData::clearFrame(int newIndex)
585 GMX_RELEASE_ASSERT(!builder_, "Should not clear an in-progress frame");
587 header_ = AnalysisDataFrameHeader(newIndex, 0.0, 0.0);
589 if (baseData().isMultipoint())
597 AnalysisDataStorageFrameData::startFrame(
598 const AnalysisDataFrameHeader &header,
599 AnalysisDataFrameBuilderPointer builder)
603 builder_ = std::move(builder);
604 builder_->data_ = this;
605 builder_->selectDataSet(0);
610 AnalysisDataStorageFrameData::addPointSet(int dataSetIndex, int firstColumn,
611 ArrayRef<const AnalysisDataValue> v)
613 const int valueCount = v.size();
614 AnalysisDataPointSetInfo pointSetInfo(0, valueCount,
615 dataSetIndex, firstColumn);
616 AnalysisDataPointSetRef pointSet(header(), pointSetInfo, v);
617 storageImpl().modules_->notifyParallelPointsAdd(pointSet);
618 if (storageImpl().shouldNotifyImmediately())
620 storageImpl().modules_->notifyPointsAdd(pointSet);
622 else if (storageImpl().needStorage())
624 pointSets_.emplace_back(values_.size(), valueCount,
625 dataSetIndex, firstColumn);
626 std::copy(v.begin(), v.end(), std::back_inserter(values_));
631 AnalysisDataFrameBuilderPointer
632 AnalysisDataStorageFrameData::finishFrame(bool bMultipoint)
637 GMX_RELEASE_ASSERT(static_cast<int>(pointSets_.size()) == baseData().dataSetCount(),
638 "Point sets created for non-multipoint data");
639 values_ = builder_->values_;
640 builder_->clearValues();
641 for (int i = 0; i < pointSetCount(); ++i)
643 storageImpl().modules_->notifyParallelPointsAdd(pointSet(i));
648 GMX_RELEASE_ASSERT(!builder_->bPointSetInProgress_,
649 "Unfinished point set");
651 AnalysisDataFrameBuilderPointer builder(std::move(builder_));
657 AnalysisDataPointSetRef
658 AnalysisDataStorageFrameData::pointSet(int index) const
660 GMX_ASSERT(index >= 0 && index < pointSetCount(),
661 "Invalid point set index");
662 return AnalysisDataPointSetRef(
663 header_, pointSets_[index], values_);
666 } // namespace internal
668 /********************************************************************
669 * AnalysisDataStorageFrame
672 AnalysisDataStorageFrame::AnalysisDataStorageFrame(
673 const AbstractAnalysisData &data)
674 : data_(nullptr), currentDataSet_(0), currentOffset_(0),
675 columnCount_(data.columnCount(0)), bPointSetInProgress_(false)
677 int totalColumnCount = 0;
678 for (int i = 0; i < data.dataSetCount(); ++i)
680 totalColumnCount += data.columnCount(i);
682 values_.resize(totalColumnCount);
686 AnalysisDataStorageFrame::~AnalysisDataStorageFrame()
692 AnalysisDataStorageFrame::clearValues()
694 if (bPointSetInProgress_)
696 std::vector<AnalysisDataValue>::iterator i;
697 for (i = values_.begin(); i != values_.end(); ++i)
702 bPointSetInProgress_ = false;
707 AnalysisDataStorageFrame::selectDataSet(int index)
709 GMX_RELEASE_ASSERT(data_ != nullptr, "Invalid frame accessed");
710 const AbstractAnalysisData &baseData = data_->baseData();
711 GMX_RELEASE_ASSERT(index >= 0 && index < baseData.dataSetCount(),
712 "Out of range data set index");
713 GMX_RELEASE_ASSERT(!baseData.isMultipoint() || !bPointSetInProgress_,
714 "Point sets in multipoint data cannot span data sets");
715 currentDataSet_ = index;
717 // TODO: Consider precalculating.
718 for (int i = 0; i < index; ++i)
720 currentOffset_ += baseData.columnCount(i);
722 columnCount_ = baseData.columnCount(index);
727 AnalysisDataStorageFrame::finishPointSet()
729 GMX_RELEASE_ASSERT(data_ != nullptr, "Invalid frame accessed");
730 GMX_RELEASE_ASSERT(data_->baseData().isMultipoint(),
731 "Should not be called for non-multipoint data");
732 if (bPointSetInProgress_)
734 size_t begin = currentOffset_;
735 size_t end = begin + columnCount_;
737 while (begin != end && !values_[begin].isSet())
742 while (end != begin && !values_[end-1].isSet())
750 data_->addPointSet(currentDataSet_, firstColumn,
751 makeConstArrayRef(values_).
752 subArray(begin, end-begin));
759 AnalysisDataStorageFrame::finishFrame()
761 GMX_RELEASE_ASSERT(data_ != nullptr, "Invalid frame accessed");
762 data_->storageImpl().finishFrame(data_->frameIndex());
766 /********************************************************************
767 * AnalysisDataStorage
770 AnalysisDataStorage::AnalysisDataStorage()
776 AnalysisDataStorage::~AnalysisDataStorage()
782 AnalysisDataStorage::frameCount() const
784 return impl_->firstUnnotifiedIndex();
789 AnalysisDataStorage::tryGetDataFrame(int index) const
791 int storageIndex = impl_->computeStorageLocation(index);
792 if (storageIndex == -1)
794 return AnalysisDataFrameRef();
796 const internal::AnalysisDataStorageFrameData &storedFrame
797 = *impl_->frames_[storageIndex];
798 if (!storedFrame.isAvailable())
800 return AnalysisDataFrameRef();
802 return storedFrame.frameReference();
807 AnalysisDataStorage::requestStorage(int nframes)
809 // Handle the case when everything needs to be stored.
812 impl_->storageLimit_ = std::numeric_limits<int>::max();
815 // Check whether an earlier call has requested more storage.
816 if (nframes < impl_->storageLimit_)
820 impl_->storageLimit_ = nframes;
826 AnalysisDataStorage::startDataStorage(AbstractAnalysisData *data,
827 AnalysisDataModuleManager *modules)
829 modules->notifyDataStart(data);
830 // Data needs to be set before calling extendBuffer()
832 impl_->modules_ = modules;
833 if (!impl_->storeAll())
835 // 2 = pending limit (1) + 1
836 impl_->extendBuffer(impl_->storageLimit_ + 2);
842 AnalysisDataStorage::startParallelDataStorage(
843 AbstractAnalysisData *data,
844 AnalysisDataModuleManager *modules,
845 const AnalysisDataParallelOptions &options)
847 const int pendingLimit = options.parallelizationFactor();
848 impl_->pendingLimit_ = pendingLimit;
849 modules->notifyParallelDataStart(data, options);
850 // Data needs to be set before calling extendBuffer()
852 impl_->modules_ = modules;
853 if (!impl_->storeAll())
855 impl_->extendBuffer(impl_->storageLimit_ + pendingLimit + 1);
860 AnalysisDataStorageFrame &
861 AnalysisDataStorage::startFrame(const AnalysisDataFrameHeader &header)
863 GMX_ASSERT(header.isValid(), "Invalid header");
864 internal::AnalysisDataStorageFrameData *storedFrame;
865 if (impl_->storeAll())
867 size_t size = header.index() + 1;
868 if (impl_->frames_.size() < size)
870 impl_->extendBuffer(size);
872 storedFrame = impl_->frames_[header.index()].get();
876 int storageIndex = impl_->computeStorageLocation(header.index());
877 if (storageIndex == -1)
879 GMX_THROW(APIError("Out of bounds frame index"));
881 storedFrame = impl_->frames_[storageIndex].get();
883 GMX_RELEASE_ASSERT(!storedFrame->isStarted(),
884 "startFrame() called twice for the same frame");
885 GMX_RELEASE_ASSERT(storedFrame->frameIndex() == header.index(),
886 "Inconsistent internal frame indexing");
887 storedFrame->startFrame(header, impl_->getFrameBuilder());
888 impl_->modules_->notifyParallelFrameStart(header);
889 if (impl_->shouldNotifyImmediately())
891 impl_->modules_->notifyFrameStart(header);
893 return storedFrame->builder();
897 AnalysisDataStorageFrame &
898 AnalysisDataStorage::startFrame(int index, real x, real dx)
900 return startFrame(AnalysisDataFrameHeader(index, x, dx));
904 AnalysisDataStorageFrame &
905 AnalysisDataStorage::currentFrame(int index)
907 const int storageIndex = impl_->computeStorageLocation(index);
908 GMX_RELEASE_ASSERT(storageIndex >= 0, "Out of bounds frame index");
910 internal::AnalysisDataStorageFrameData &storedFrame = *impl_->frames_[storageIndex];
911 GMX_RELEASE_ASSERT(storedFrame.isStarted(),
912 "currentFrame() called for frame before startFrame()");
913 GMX_RELEASE_ASSERT(!storedFrame.isFinished(),
914 "currentFrame() called for frame after finishFrame()");
915 GMX_RELEASE_ASSERT(storedFrame.frameIndex() == index,
916 "Inconsistent internal frame indexing");
917 return storedFrame.builder();
922 AnalysisDataStorage::finishFrame(int index)
924 impl_->finishFrame(index);
928 AnalysisDataStorage::finishFrameSerial(int index)
930 if (impl_->pendingLimit_ > 1)
932 impl_->finishFrameSerial(index);
937 AnalysisDataStorage::finishDataStorage()
939 // TODO: Check that all frames have been finished etc.
940 impl_->builders_.clear();
941 impl_->modules_->notifyDataFinish();