2 * This file is part of the GROMACS molecular simulation package.
4 * Copyright (c) 2012,2013,2014,2015,2016,2017,2018,2019, 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/utility/exceptions.h"
57 #include "gromacs/utility/gmxassert.h"
62 /********************************************************************
63 * AnalysisDataParallelOptions
66 AnalysisDataParallelOptions::AnalysisDataParallelOptions()
67 : parallelizationFactor_(1)
72 AnalysisDataParallelOptions::AnalysisDataParallelOptions(int parallelizationFactor
)
73 : parallelizationFactor_(parallelizationFactor
)
75 GMX_RELEASE_ASSERT(parallelizationFactor
>= 1,
76 "Invalid parallelization factor");
80 /********************************************************************
81 * AnalysisDataStorageImpl declaration
87 //! Smart pointer type for managing a storage frame builder.
88 typedef std::unique_ptr
<AnalysisDataStorageFrame
>
89 AnalysisDataFrameBuilderPointer
;
92 * Private implementation class for AnalysisDataStorage.
94 * \ingroup module_analysisdata
96 class AnalysisDataStorageImpl
99 //! Smart pointer type for managing a stored frame.
100 typedef std::unique_ptr
<AnalysisDataStorageFrameData
> FramePointer
;
102 //! Shorthand for a list of data frames that are currently stored.
103 typedef std::vector
<FramePointer
> FrameList
;
104 //! Shorthand for a list of currently unused storage frame builders.
105 typedef std::vector
<AnalysisDataFrameBuilderPointer
> FrameBuilderList
;
107 AnalysisDataStorageImpl();
109 //! Returns whether the storage is set to use multipoint data.
110 bool isMultipoint() const;
112 * Whether storage of all frames has been requested.
114 * Storage of all frames also works as expected if \a storageLimit_ is
115 * used in comparisons directly, but this method should be used to
116 * check how to manage \a frames_.
118 bool storeAll() const
120 return storageLimit_
== std::numeric_limits
<int>::max();
122 //! Returns the index of the oldest frame that may be currently stored.
123 int firstStoredIndex() const;
124 //! Returns the index of the first frame that is not fully notified.
125 int firstUnnotifiedIndex() const { return firstUnnotifiedIndex_
; }
127 * Computes index into \a frames_ for accessing frame \p index.
129 * \param[in] index Zero-based frame index.
130 * \retval -1 if \p index is not available in \a frames_.
134 int computeStorageLocation(int index
) const;
137 * Computes an index into \a frames_ that is one past the last frame
142 size_t endStorageLocation() const;
145 * Extends \a frames_ to a new size.
147 * \throws std::bad_alloc if out of memory.
149 void extendBuffer(size_t newSize
);
151 * Remove oldest frame from the storage to make space for a new one.
153 * Increments \a firstFrameLocation_ and reinitializes the frame that
154 * was made unavailable by this operation.
163 * Returns a frame builder object for use with a new frame.
165 * \throws std::bad_alloc if out of memory.
167 AnalysisDataFrameBuilderPointer
getFrameBuilder();
170 * Returns whether notifications should be immediately fired.
172 * This is used to optimize multipoint handling for non-parallel cases,
173 * where it is not necessary to store even a single frame.
177 bool shouldNotifyImmediately() const
179 return isMultipoint() && storageLimit_
== 0 && pendingLimit_
== 1;
182 * Returns whether data needs to be stored at all.
184 * This is used to optimize multipoint handling for parallel cases
185 * (where shouldNotifyImmediately() returns false),
186 * where it is not necessary to store even a single frame.
189 * This could be extended to non-multipoint data as well.
193 bool needStorage() const
195 return storageLimit_
> 0 || (pendingLimit_
> 1 && modules_
->hasSerialModules());
197 //! Implementation for AnalysisDataStorage::finishFrame().
198 void finishFrame(int index
);
200 * Implementation for AnalysisDataStorage::finishFrameSerial().
202 void finishFrameSerial(int index
);
205 //! Parent data object to access data dimensionality etc.
206 const AbstractAnalysisData
*data_
;
207 //! Manager to use for notification calls.
208 AnalysisDataModuleManager
*modules_
;
210 * Number of past frames that need to be stored.
212 * Always non-negative. If storage of all frames has been requested,
213 * this is set to a large number.
217 * Number of future frames that may need to be started.
219 * Should always be at least one.
222 * Get rid of this alltogether, as it is no longer used much.
224 * \see AnalysisDataStorage::startFrame()
228 * Data frames that are currently stored.
230 * If storage of all frames has been requested, this is simply a vector
231 * of frames up to the latest frame that has been started.
232 * In this case, \a firstFrameLocation_ is always zero.
234 * If storage of all frames is not requested, this is a ring buffer of
235 * frames of size \c n=storageLimit_+pendingLimit_+1. If a frame with
236 * index \c index is currently stored, its location is
237 * \c index%frames_.size().
238 * When at most \a storageLimit_ first frames have been finished,
239 * this contains storage for the first \c n-1 frames.
240 * When more than \a storageLimit_ first frames have been finished,
241 * the oldest stored frame is stored in the location
242 * \a firstFrameLocation_, and \a storageLimit_ frames starting from
243 * this location are the last finished frames. \a pendingLimit_ frames
244 * follow, and some of these may be in progress or finished.
245 * There is always one unused frame in the buffer, which is initialized
246 * such that when \a firstFrameLocation_ is incremented, it becomes
247 * valid. This makes it easier to rotate the buffer in concurrent
248 * access scenarions (which are not yet otherwise implemented).
251 //! Location of oldest frame in \a frames_.
252 size_t firstFrameLocation_
;
253 //! Index of the first frame that is not fully notified.
254 int firstUnnotifiedIndex_
;
256 * Currently unused frame builders.
258 * The builders are cached to avoid repeatedly allocating memory for
259 * them. Typically, there are as many builders as there are concurrent
260 * users of the storage object. Whenever a frame is started, a builder
261 * is pulled from this pool by getFrameBuilder() (a new one is created
262 * if none are available), and assigned for that frame. When that
263 * frame is finished, the builder is returned to this pool.
265 FrameBuilderList builders_
;
267 * Index of next frame that will be added to \a frames_.
269 * If all frames are not stored, this will be the index of the unused
270 * frame (see \a frames_).
275 /********************************************************************
276 * AnalysisDataStorageFrameImpl declaration
281 * Internal representation for a single stored frame.
283 * It is implemented such that the frame header is always valid, i.e.,
284 * header().isValid() returns always true.
286 * Methods in this class do not throw unless otherwise indicated.
288 * \ingroup module_analysisdata
290 class AnalysisDataStorageFrameData
293 //! Indicates what operations have been performed on a frame.
296 eMissing
, //!< Frame has not yet been started.
297 eStarted
, //!< startFrame() has been called.
298 eFinished
, //!< finishFrame() has been called.
299 eNotified
//!< Appropriate notifications have been sent.
303 * Create a new storage frame.
305 * \param storageImpl Storage object this frame belongs to.
306 * \param[in] index Zero-based index for the frame.
308 AnalysisDataStorageFrameData(AnalysisDataStorageImpl
*storageImpl
,
311 //! Whether the frame has been started with startFrame().
312 bool isStarted() const { return status_
>= eStarted
; }
313 //! Whether the frame has been finished with finishFrame().
314 bool isFinished() const { return status_
>= eFinished
; }
315 //! Whether all notifications have been sent.
316 bool isNotified() const { return status_
>= eNotified
; }
317 //! Whether the frame is ready to be available outside the storage.
318 bool isAvailable() const { return status_
>= eFinished
; }
320 //! Marks the frame as notified.
321 void markNotified() { status_
= eNotified
; }
323 //! Returns the storage implementation object.
324 AnalysisDataStorageImpl
&storageImpl() const { return storageImpl_
; }
325 //! Returns the underlying data object (for data dimensionalities etc.).
326 const AbstractAnalysisData
&baseData() const { return *storageImpl().data_
; }
328 //! Returns header for the frame.
329 const AnalysisDataFrameHeader
&header() const { return header_
; }
330 //! Returns zero-based index of the frame.
331 int frameIndex() const { return header().index(); }
332 //! Returns the number of point sets for the frame.
333 int pointSetCount() const { return pointSets_
.size(); }
335 //! Clears the frame for reusing as a new frame.
336 void clearFrame(int newIndex
);
338 * Initializes the frame during AnalysisDataStorage::startFrame().
340 * \param[in] header Header to use for the new frame.
341 * \param[in] builder Builder object to use.
343 void startFrame(const AnalysisDataFrameHeader
&header
,
344 AnalysisDataFrameBuilderPointer builder
);
345 //! Returns the builder for this frame.
346 AnalysisDataStorageFrame
&builder() const
348 GMX_ASSERT(builder_
, "Accessing builder for not-in-progress frame");
352 * Adds a new point set to this frame.
354 void addPointSet(int dataSetIndex
, int firstColumn
,
355 ArrayRef
<const AnalysisDataValue
> v
);
357 * Finalizes the frame during AnalysisDataStorage::finishFrame().
359 * \returns The builder object used by the frame, for reusing it for
362 AnalysisDataFrameBuilderPointer
finishFrame(bool bMultipoint
);
364 //! Returns frame reference to this frame.
365 AnalysisDataFrameRef
frameReference() const
367 return AnalysisDataFrameRef(header_
, values_
, pointSets_
);
369 //! Returns point set reference to a given point set.
370 AnalysisDataPointSetRef
pointSet(int index
) const;
373 //! Storage object that contains this frame.
374 AnalysisDataStorageImpl
&storageImpl_
;
375 //! Header for the frame.
376 AnalysisDataFrameHeader header_
;
377 //! Values for the frame.
378 std::vector
<AnalysisDataValue
> values_
;
379 //! Information about each point set in the frame.
380 std::vector
<AnalysisDataPointSetInfo
> pointSets_
;
382 * Builder object for the frame.
384 * Non-NULL when the frame is in progress, i.e., has been started but
387 AnalysisDataFrameBuilderPointer builder_
;
388 //! In what state the frame currently is.
391 GMX_DISALLOW_COPY_AND_ASSIGN(AnalysisDataStorageFrameData
);
394 /********************************************************************
395 * AnalysisDataStorageImpl implementation
398 AnalysisDataStorageImpl::AnalysisDataStorageImpl()
399 : data_(nullptr), modules_(nullptr),
400 storageLimit_(0), pendingLimit_(1),
401 firstFrameLocation_(0), firstUnnotifiedIndex_(0), nextIndex_(0)
407 AnalysisDataStorageImpl::isMultipoint() const
409 GMX_ASSERT(data_
!= nullptr, "isMultipoint() called too early");
410 return data_
->isMultipoint();
415 AnalysisDataStorageImpl::firstStoredIndex() const
417 return frames_
[firstFrameLocation_
]->frameIndex();
422 AnalysisDataStorageImpl::computeStorageLocation(int index
) const
424 if (index
< firstStoredIndex() || index
>= nextIndex_
)
428 return index
% frames_
.size();
433 AnalysisDataStorageImpl::endStorageLocation() const
437 return frames_
.size();
439 if (frames_
[0]->frameIndex() == 0 || firstFrameLocation_
== 0)
441 return frames_
.size() - 1;
443 return firstFrameLocation_
- 1;
448 AnalysisDataStorageImpl::extendBuffer(size_t newSize
)
450 frames_
.reserve(newSize
);
451 while (frames_
.size() < newSize
)
453 frames_
.push_back(std::make_unique
<AnalysisDataStorageFrameData
>(this, nextIndex_
));
456 // The unused frame should not be included in the count.
465 AnalysisDataStorageImpl::rotateBuffer()
467 GMX_ASSERT(!storeAll(),
468 "No need to rotate internal buffer if everything is stored");
469 size_t prevFirst
= firstFrameLocation_
;
470 size_t nextFirst
= prevFirst
+ 1;
471 if (nextFirst
== frames_
.size())
475 firstFrameLocation_
= nextFirst
;
476 frames_
[prevFirst
]->clearFrame(nextIndex_
+ 1);
481 AnalysisDataFrameBuilderPointer
482 AnalysisDataStorageImpl::getFrameBuilder()
484 if (builders_
.empty())
486 return AnalysisDataFrameBuilderPointer(new AnalysisDataStorageFrame(*data_
));
488 AnalysisDataFrameBuilderPointer
builder(std::move(builders_
.back()));
489 builders_
.pop_back();
495 AnalysisDataStorageImpl::finishFrame(int index
)
497 const int storageIndex
= computeStorageLocation(index
);
498 GMX_RELEASE_ASSERT(storageIndex
>= 0, "Out of bounds frame index");
500 AnalysisDataStorageFrameData
&storedFrame
= *frames_
[storageIndex
];
501 GMX_RELEASE_ASSERT(storedFrame
.isStarted(),
502 "finishFrame() called for frame before startFrame()");
503 GMX_RELEASE_ASSERT(!storedFrame
.isFinished(),
504 "finishFrame() called twice for the same frame");
505 GMX_RELEASE_ASSERT(storedFrame
.frameIndex() == index
,
506 "Inconsistent internal frame indexing");
507 builders_
.push_back(storedFrame
.finishFrame(isMultipoint()));
508 modules_
->notifyParallelFrameFinish(storedFrame
.header());
509 if (pendingLimit_
== 1)
511 finishFrameSerial(index
);
517 AnalysisDataStorageImpl::finishFrameSerial(int index
)
519 GMX_RELEASE_ASSERT(index
== firstUnnotifiedIndex_
,
520 "Out of order finisFrameSerial() calls");
521 const int storageIndex
= computeStorageLocation(index
);
522 GMX_RELEASE_ASSERT(storageIndex
>= 0, "Out of bounds frame index");
524 AnalysisDataStorageFrameData
&storedFrame
= *frames_
[storageIndex
];
525 GMX_RELEASE_ASSERT(storedFrame
.frameIndex() == index
,
526 "Inconsistent internal frame indexing");
527 GMX_RELEASE_ASSERT(storedFrame
.isFinished(),
528 "finishFrameSerial() called before finishFrame()");
529 GMX_RELEASE_ASSERT(!storedFrame
.isNotified(),
530 "finishFrameSerial() called twice for the same frame");
531 // Increment before the notifications to make the frame available
532 // in the module callbacks.
533 ++firstUnnotifiedIndex_
;
534 if (shouldNotifyImmediately())
536 modules_
->notifyFrameFinish(storedFrame
.header());
540 modules_
->notifyFrameStart(storedFrame
.header());
541 for (int j
= 0; j
< storedFrame
.pointSetCount(); ++j
)
543 modules_
->notifyPointsAdd(storedFrame
.pointSet(j
));
545 modules_
->notifyFrameFinish(storedFrame
.header());
547 storedFrame
.markNotified();
548 if (storedFrame
.frameIndex() >= storageLimit_
)
555 /********************************************************************
556 * AnalysisDataStorageFrame implementation
559 AnalysisDataStorageFrameData::AnalysisDataStorageFrameData(
560 AnalysisDataStorageImpl
*storageImpl
,
562 : storageImpl_(*storageImpl
), header_(index
, 0.0, 0.0), status_(eMissing
)
564 GMX_RELEASE_ASSERT(storageImpl
->data_
!= nullptr,
565 "Storage frame constructed before data started");
566 // With non-multipoint data, the point set structure is static,
567 // so initialize it only once here.
568 if (!baseData().isMultipoint())
571 for (int i
= 0; i
< baseData().dataSetCount(); ++i
)
573 int columnCount
= baseData().columnCount(i
);
574 pointSets_
.emplace_back(offset
, columnCount
, i
, 0);
575 offset
+= columnCount
;
582 AnalysisDataStorageFrameData::clearFrame(int newIndex
)
584 GMX_RELEASE_ASSERT(!builder_
, "Should not clear an in-progress frame");
586 header_
= AnalysisDataFrameHeader(newIndex
, 0.0, 0.0);
588 if (baseData().isMultipoint())
596 AnalysisDataStorageFrameData::startFrame(
597 const AnalysisDataFrameHeader
&header
,
598 AnalysisDataFrameBuilderPointer builder
)
602 builder_
= std::move(builder
);
603 builder_
->data_
= this;
604 builder_
->selectDataSet(0);
609 AnalysisDataStorageFrameData::addPointSet(int dataSetIndex
, int firstColumn
,
610 ArrayRef
<const AnalysisDataValue
> v
)
612 const int valueCount
= v
.size();
613 AnalysisDataPointSetInfo
pointSetInfo(0, valueCount
,
614 dataSetIndex
, firstColumn
);
615 AnalysisDataPointSetRef
pointSet(header(), pointSetInfo
, v
);
616 storageImpl().modules_
->notifyParallelPointsAdd(pointSet
);
617 if (storageImpl().shouldNotifyImmediately())
619 storageImpl().modules_
->notifyPointsAdd(pointSet
);
621 else if (storageImpl().needStorage())
623 pointSets_
.emplace_back(values_
.size(), valueCount
,
624 dataSetIndex
, firstColumn
);
625 std::copy(v
.begin(), v
.end(), std::back_inserter(values_
));
630 AnalysisDataFrameBuilderPointer
631 AnalysisDataStorageFrameData::finishFrame(bool bMultipoint
)
636 GMX_RELEASE_ASSERT(ssize(pointSets_
) == baseData().dataSetCount(),
637 "Point sets created for non-multipoint data");
638 values_
= builder_
->values_
;
639 builder_
->clearValues();
640 for (int i
= 0; i
< pointSetCount(); ++i
)
642 storageImpl().modules_
->notifyParallelPointsAdd(pointSet(i
));
647 GMX_RELEASE_ASSERT(!builder_
->bPointSetInProgress_
,
648 "Unfinished point set");
650 AnalysisDataFrameBuilderPointer
builder(std::move(builder_
));
656 AnalysisDataPointSetRef
657 AnalysisDataStorageFrameData::pointSet(int index
) const
659 GMX_ASSERT(index
>= 0 && index
< pointSetCount(),
660 "Invalid point set index");
661 return AnalysisDataPointSetRef(
662 header_
, pointSets_
[index
], values_
);
665 } // namespace internal
667 /********************************************************************
668 * AnalysisDataStorageFrame
671 AnalysisDataStorageFrame::AnalysisDataStorageFrame(
672 const AbstractAnalysisData
&data
)
673 : data_(nullptr), currentDataSet_(0), currentOffset_(0),
674 columnCount_(data
.columnCount(0)), bPointSetInProgress_(false)
676 int totalColumnCount
= 0;
677 for (int i
= 0; i
< data
.dataSetCount(); ++i
)
679 totalColumnCount
+= data
.columnCount(i
);
681 values_
.resize(totalColumnCount
);
685 AnalysisDataStorageFrame::~AnalysisDataStorageFrame()
691 AnalysisDataStorageFrame::clearValues()
693 if (bPointSetInProgress_
)
695 std::vector
<AnalysisDataValue
>::iterator i
;
696 for (i
= values_
.begin(); i
!= values_
.end(); ++i
)
701 bPointSetInProgress_
= false;
706 AnalysisDataStorageFrame::selectDataSet(int index
)
708 GMX_RELEASE_ASSERT(data_
!= nullptr, "Invalid frame accessed");
709 const AbstractAnalysisData
&baseData
= data_
->baseData();
710 GMX_RELEASE_ASSERT(index
>= 0 && index
< baseData
.dataSetCount(),
711 "Out of range data set index");
712 GMX_RELEASE_ASSERT(!baseData
.isMultipoint() || !bPointSetInProgress_
,
713 "Point sets in multipoint data cannot span data sets");
714 currentDataSet_
= index
;
716 // TODO: Consider precalculating.
717 for (int i
= 0; i
< index
; ++i
)
719 currentOffset_
+= baseData
.columnCount(i
);
721 columnCount_
= baseData
.columnCount(index
);
726 AnalysisDataStorageFrame::finishPointSet()
728 GMX_RELEASE_ASSERT(data_
!= nullptr, "Invalid frame accessed");
729 GMX_RELEASE_ASSERT(data_
->baseData().isMultipoint(),
730 "Should not be called for non-multipoint data");
731 if (bPointSetInProgress_
)
733 size_t begin
= currentOffset_
;
734 size_t end
= begin
+ columnCount_
;
736 while (begin
!= end
&& !values_
[begin
].isSet())
741 while (end
!= begin
&& !values_
[end
-1].isSet())
749 data_
->addPointSet(currentDataSet_
, firstColumn
,
750 makeConstArrayRef(values_
).
751 subArray(begin
, end
-begin
));
758 AnalysisDataStorageFrame::finishFrame()
760 GMX_RELEASE_ASSERT(data_
!= nullptr, "Invalid frame accessed");
761 data_
->storageImpl().finishFrame(data_
->frameIndex());
765 /********************************************************************
766 * AnalysisDataStorage
769 AnalysisDataStorage::AnalysisDataStorage()
775 AnalysisDataStorage::~AnalysisDataStorage()
781 AnalysisDataStorage::frameCount() const
783 return impl_
->firstUnnotifiedIndex();
788 AnalysisDataStorage::tryGetDataFrame(int index
) const
790 int storageIndex
= impl_
->computeStorageLocation(index
);
791 if (storageIndex
== -1)
793 return AnalysisDataFrameRef();
795 const internal::AnalysisDataStorageFrameData
&storedFrame
796 = *impl_
->frames_
[storageIndex
];
797 if (!storedFrame
.isAvailable())
799 return AnalysisDataFrameRef();
801 return storedFrame
.frameReference();
806 AnalysisDataStorage::requestStorage(int nframes
)
808 // Handle the case when everything needs to be stored.
811 impl_
->storageLimit_
= std::numeric_limits
<int>::max();
814 // Check whether an earlier call has requested more storage.
815 if (nframes
< impl_
->storageLimit_
)
819 impl_
->storageLimit_
= nframes
;
825 AnalysisDataStorage::startDataStorage(AbstractAnalysisData
*data
,
826 AnalysisDataModuleManager
*modules
)
828 modules
->notifyDataStart(data
);
829 // Data needs to be set before calling extendBuffer()
831 impl_
->modules_
= modules
;
832 if (!impl_
->storeAll())
834 // 2 = pending limit (1) + 1
835 impl_
->extendBuffer(impl_
->storageLimit_
+ 2);
841 AnalysisDataStorage::startParallelDataStorage(
842 AbstractAnalysisData
*data
,
843 AnalysisDataModuleManager
*modules
,
844 const AnalysisDataParallelOptions
&options
)
846 const int pendingLimit
= options
.parallelizationFactor();
847 impl_
->pendingLimit_
= pendingLimit
;
848 modules
->notifyParallelDataStart(data
, options
);
849 // Data needs to be set before calling extendBuffer()
851 impl_
->modules_
= modules
;
852 if (!impl_
->storeAll())
854 impl_
->extendBuffer(impl_
->storageLimit_
+ pendingLimit
+ 1);
859 AnalysisDataStorageFrame
&
860 AnalysisDataStorage::startFrame(const AnalysisDataFrameHeader
&header
)
862 GMX_ASSERT(header
.isValid(), "Invalid header");
863 internal::AnalysisDataStorageFrameData
*storedFrame
;
864 if (impl_
->storeAll())
866 size_t size
= header
.index() + 1;
867 if (impl_
->frames_
.size() < size
)
869 impl_
->extendBuffer(size
);
871 storedFrame
= impl_
->frames_
[header
.index()].get();
875 int storageIndex
= impl_
->computeStorageLocation(header
.index());
876 if (storageIndex
== -1)
878 GMX_THROW(APIError("Out of bounds frame index"));
880 storedFrame
= impl_
->frames_
[storageIndex
].get();
882 GMX_RELEASE_ASSERT(!storedFrame
->isStarted(),
883 "startFrame() called twice for the same frame");
884 GMX_RELEASE_ASSERT(storedFrame
->frameIndex() == header
.index(),
885 "Inconsistent internal frame indexing");
886 storedFrame
->startFrame(header
, impl_
->getFrameBuilder());
887 impl_
->modules_
->notifyParallelFrameStart(header
);
888 if (impl_
->shouldNotifyImmediately())
890 impl_
->modules_
->notifyFrameStart(header
);
892 return storedFrame
->builder();
896 AnalysisDataStorageFrame
&
897 AnalysisDataStorage::startFrame(int index
, real x
, real dx
)
899 return startFrame(AnalysisDataFrameHeader(index
, x
, dx
));
903 AnalysisDataStorageFrame
&
904 AnalysisDataStorage::currentFrame(int index
)
906 const int storageIndex
= impl_
->computeStorageLocation(index
);
907 GMX_RELEASE_ASSERT(storageIndex
>= 0, "Out of bounds frame index");
909 internal::AnalysisDataStorageFrameData
&storedFrame
= *impl_
->frames_
[storageIndex
];
910 GMX_RELEASE_ASSERT(storedFrame
.isStarted(),
911 "currentFrame() called for frame before startFrame()");
912 GMX_RELEASE_ASSERT(!storedFrame
.isFinished(),
913 "currentFrame() called for frame after finishFrame()");
914 GMX_RELEASE_ASSERT(storedFrame
.frameIndex() == index
,
915 "Inconsistent internal frame indexing");
916 return storedFrame
.builder();
921 AnalysisDataStorage::finishFrame(int index
)
923 impl_
->finishFrame(index
);
927 AnalysisDataStorage::finishFrameSerial(int index
)
929 if (impl_
->pendingLimit_
> 1)
931 impl_
->finishFrameSerial(index
);
936 AnalysisDataStorage::finishDataStorage()
938 // TODO: Check that all frames have been finished etc.
939 impl_
->builders_
.clear();
940 impl_
->modules_
->notifyDataFinish();