Split AnalysisDataStorageFrame into two.
[alexxy/gromacs.git] / src / gromacs / analysisdata / datastorage.cpp
1 /*
2  * This file is part of the GROMACS molecular simulation package.
3  *
4  * Copyright (c) 2012,2013, by the GROMACS development team, led by
5  * David van der Spoel, Berk Hess, Erik Lindahl, and including many
6  * others, as listed in the AUTHORS file in the top-level source
7  * directory and at http://www.gromacs.org.
8  *
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.
13  *
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.
18  *
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.
23  *
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.
31  *
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.
34  */
35 /*! \internal \file
36  * \brief
37  * Implements classes in datastorage.h and paralleloptions.h.
38  *
39  * \author Teemu Murtola <teemu.murtola@gmail.com>
40  * \ingroup module_analysisdata
41  */
42 #include "datastorage.h"
43
44 #include <limits>
45 #include <vector>
46
47 #include "gromacs/analysisdata/abstractdata.h"
48 #include "gromacs/analysisdata/dataframe.h"
49 #include "gromacs/analysisdata/paralleloptions.h"
50 #include "gromacs/utility/exceptions.h"
51 #include "gromacs/utility/gmxassert.h"
52 #include "gromacs/utility/uniqueptr.h"
53
54 namespace gmx
55 {
56
57 /********************************************************************
58  * AnalysisDataParallelOptions
59  */
60
61 AnalysisDataParallelOptions::AnalysisDataParallelOptions()
62     : parallelizationFactor_(1)
63 {
64 }
65
66
67 AnalysisDataParallelOptions::AnalysisDataParallelOptions(int parallelizationFactor)
68     : parallelizationFactor_(parallelizationFactor)
69 {
70     GMX_RELEASE_ASSERT(parallelizationFactor >= 1,
71                        "Invalid parallelization factor");
72 }
73
74
75 /********************************************************************
76  * AnalysisDataStorage::Impl declaration
77  */
78
79 namespace internal
80 {
81 //! Smart pointer type for managing a storage frame builder.
82 typedef gmx_unique_ptr<AnalysisDataStorageFrame>::type
83     AnalysisDataFrameBuilderPointer;
84 }   // namespace internal
85
86 /*! \internal \brief
87  * Private implementation class for AnalysisDataStorage.
88  *
89  * \ingroup module_analysisdata
90  */
91 class AnalysisDataStorage::Impl
92 {
93     public:
94         //! Short-hand for the internal frame data type.
95         typedef internal::AnalysisDataStorageFrameData FrameData;
96         //! Smart pointer type for managing a stored frame.
97         typedef gmx_unique_ptr<FrameData>::type FramePointer;
98         //! Short-hand for a smart pointer type to a storage frame builder.
99         typedef internal::AnalysisDataFrameBuilderPointer FrameBuilderPointer;
100
101         //! Shorthand for a list of data frames that are currently stored.
102         typedef std::vector<FramePointer> FrameList;
103         //! Shorthand for a list of currently unused storage frame builders.
104         typedef std::vector<FrameBuilderPointer> FrameBuilderList;
105
106         Impl();
107
108         //! Returns the number of columns in the attached data.
109         int columnCount() const;
110         //! Returns whether the storage is set to use multipoint data.
111         bool isMultipoint() const;
112         /*! \brief
113          * Whether storage of all frames has been requested.
114          *
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_.
118          */
119         bool storeAll() const
120         {
121             return storageLimit_ == std::numeric_limits<int>::max();
122         }
123         //! Returns the index of the oldest frame that may be currently stored.
124         int firstStoredIndex() const;
125         /*! \brief
126          * Computes index into \a frames_ for accessing frame \p index.
127          *
128          * \param[in]  index  Zero-based frame index.
129          * \retval  -1 if \p index is not available in \a frames_.
130          *
131          * Does not throw.
132          */
133         int computeStorageLocation(int index) const;
134
135         /*! \brief
136          * Computes an index into \a frames_ that is one past the last frame
137          * stored.
138          *
139          * Does not throw.
140          */
141         size_t endStorageLocation() const;
142
143         /*! \brief
144          * Extends \a frames_ to a new size.
145          *
146          * \throws std::bad_alloc if out of memory.
147          */
148         void extendBuffer(size_t newSize);
149         /*! \brief
150          * Remove oldest frame from the storage to make space for a new one.
151          *
152          * Increments \a firstFrameLocation_ and reinitializes the frame that
153          * was made unavailable by this operation.
154          *
155          * Does not throw.
156          *
157          * \see frames_
158          */
159         void rotateBuffer();
160
161         /*! \brief
162          * Returns a frame builder object for use with a new frame.
163          *
164          * \throws std::bad_alloc if out of memory.
165          */
166         FrameBuilderPointer getFrameBuilder();
167
168         /*! \brief
169          * Calls notification method in \a data_.
170          *
171          * \throws    unspecified  Any exception thrown by
172          *      AbstractAnalysisData::notifyPointsAdd().
173          */
174         void notifyPointSet(const AnalysisDataPointSetRef &points);
175         /*! \brief
176          * Calls notification methods for new frames.
177          *
178          * \param[in] firstLocation  First frame to consider.
179          * \throws    unspecified  Any exception thrown by frame notification
180          *      methods in AbstractAnalysisData.
181          *
182          * Notifies \a data_ of new frames (from \p firstLocation and after
183          * that) if all previous frames have already been notified.
184          * Also rotates the \a frames_ buffer as necessary.
185          */
186         void notifyNextFrames(size_t firstLocation);
187         //! Implementation for AnalysisDataStorage::finishFrame().
188         void finishFrame(int index);
189
190
191         //! Data object to use for notification calls.
192         AbstractAnalysisData   *data_;
193         /*! \brief
194          * Whether the storage has been set to allow multipoint.
195          *
196          * Should be possible to remove once full support for multipoint data
197          * has been implemented;  isMultipoint() can simply return
198          * \c data_->isMultipoint() in that case.
199          */
200         bool                    bMultipoint_;
201         /*! \brief
202          * Number of past frames that need to be stored.
203          *
204          * Always non-negative.  If storage of all frames has been requested,
205          * this is set to a large number.
206          */
207         int                     storageLimit_;
208         /*! \brief
209          * Number of future frames that may need to be started.
210          *
211          * Should always be at least one.
212          *
213          * \see AnalysisDataStorage::startFrame()
214          */
215         int                     pendingLimit_;
216         /*! \brief
217          * Data frames that are currently stored.
218          *
219          * If storage of all frames has been requested, this is simply a vector
220          * of frames up to the latest frame that has been started.
221          * In this case, \a firstFrameLocation_ is always zero.
222          *
223          * If storage of all frames is not requested, this is a ring buffer of
224          * frames of size \c n=storageLimit_+pendingLimit_+1.  If a frame with
225          * index \c index is currently stored, its location is
226          * \c index%frames_.size().
227          * When at most \a storageLimit_ first frames have been finished,
228          * this contains storage for the first \c n-1 frames.
229          * When more than \a storageLimit_ first frames have been finished,
230          * the oldest stored frame is stored in the location
231          * \a firstFrameLocation_, and \a storageLimit_ frames starting from
232          * this location are the last finished frames.  \a pendingLimit_ frames
233          * follow, and some of these may be in progress or finished.
234          * There is always one unused frame in the buffer, which is initialized
235          * such that when \a firstFrameLocation_ is incremented, it becomes
236          * valid.  This makes it easier to rotate the buffer in concurrent
237          * access scenarions (which are not yet otherwise implemented).
238          */
239         FrameList               frames_;
240         //! Location of oldest frame in \a frames_.
241         size_t                  firstFrameLocation_;
242         /*! \brief
243          * Currently unused frame builders.
244          *
245          * The builders are cached to avoid repeatedly allocating memory for
246          * them.  Typically, there are as many builders as there are concurrent
247          * users of the storage object.  Whenever a frame is started, a builder
248          * is pulled from this pool by getFrameBuilder() (a new one is created
249          * if none are available), and assigned for that frame.  When that
250          * frame is finished, the builder is returned to this pool.
251          */
252         FrameBuilderList        builders_;
253         /*! \brief
254          * Index of next frame that will be added to \a frames_.
255          *
256          * If all frames are not stored, this will be the index of the unused
257          * frame (see \a frames_).
258          */
259         int                     nextIndex_;
260 };
261
262 /********************************************************************
263  * AnalysisDataStorageFrameImpl declaration
264  */
265
266 namespace internal
267 {
268
269 /*! \internal \brief
270  * Internal representation for a single stored frame.
271  *
272  * It is implemented such that the frame header is always valid, i.e.,
273  * header().isValid() returns always true.
274  *
275  * Methods in this class do not throw unless otherwise indicated.
276  *
277  * \ingroup module_analysisdata
278  */
279 class AnalysisDataStorageFrameData
280 {
281     public:
282         //! Shorthand for a iterator into storage value containers.
283         typedef std::vector<AnalysisDataValue>::const_iterator ValueIterator;
284
285         //! Indicates what operations have been performed on a frame.
286         enum Status
287         {
288             eMissing,  //!< Frame has not yet been started.
289             eStarted,  //!< startFrame() has been called.
290             eFinished, //!< finishFrame() has been called.
291             eNotified  //!< Appropriate notifications have been sent.
292         };
293
294         /*! \brief
295          * Create a new storage frame.
296          *
297          * \param     storageImpl  Storage object this frame belongs to.
298          * \param[in] index        Zero-based index for the frame.
299          */
300         AnalysisDataStorageFrameData(AnalysisDataStorage::Impl *storageImpl,
301                                      int                        index);
302
303         //! Whether the frame has been started with startFrame().
304         bool isStarted() const { return status_ >= eStarted; }
305         //! Whether the frame has been finished with finishFrame().
306         bool isFinished() const { return status_ >= eFinished; }
307         //! Whether all notifications have been sent.
308         bool isNotified() const { return status_ >= eNotified; }
309         //! Whether the frame is ready to be available outside the storage.
310         bool isAvailable() const { return status_ >= eFinished; }
311
312         //! Marks the frame as notified.
313         void markNotified() { status_ = eNotified; }
314
315         //! Returns the storage implementation object.
316         AnalysisDataStorage::Impl &storageImpl() const { return storageImpl_; }
317         //! Returns the underlying data object (for data dimensionalities etc.).
318         const AbstractAnalysisData &baseData() const { return *storageImpl().data_; }
319
320         //! Returns header for the frame.
321         const AnalysisDataFrameHeader &header() const { return header_; }
322         //! Returns zero-based index of the frame.
323         int frameIndex() const { return header().index(); }
324
325         //! Clears the frame for reusing as a new frame.
326         void clearFrame(int newIndex);
327         /*! \brief
328          * Initializes the frame during AnalysisDataStorage::startFrame().
329          *
330          * \param[in] header  Header to use for the new frame.
331          * \param[in] builder Builder object to use.
332          */
333         void startFrame(const AnalysisDataFrameHeader   &header,
334                         AnalysisDataFrameBuilderPointer  builder);
335         //! Returns the builder for this frame.
336         AnalysisDataStorageFrame &builder() const
337         {
338             GMX_ASSERT(builder_, "Accessing builder for not-in-progress frame");
339             return *builder_;
340         }
341         /*! \brief
342          * Finalizes the frame during AnalysisDataStorage::finishFrame().
343          *
344          * \returns The builder object used by the frame, for reusing it for
345          *      other frames.
346          */
347         AnalysisDataFrameBuilderPointer finishFrame();
348
349         //! Returns frame reference to this frame.
350         AnalysisDataFrameRef frameReference() const
351         {
352             return AnalysisDataFrameRef(header_, values_);
353         }
354         //! Returns point set reference to currently set values.
355         AnalysisDataPointSetRef currentPoints() const;
356
357     private:
358         //! Storage object that contains this frame.
359         AnalysisDataStorage::Impl              &storageImpl_;
360         //! Header for the frame.
361         AnalysisDataFrameHeader                 header_;
362         //! Values for the frame.
363         std::vector<AnalysisDataValue>          values_;
364         /*! \brief
365          * Builder object for the frame.
366          *
367          * Non-NULL when the frame is in progress, i.e., has been started but
368          * not yet finished.
369          */
370         AnalysisDataFrameBuilderPointer         builder_;
371         //! In what state the frame currently is.
372         Status                                  status_;
373
374         GMX_DISALLOW_COPY_AND_ASSIGN(AnalysisDataStorageFrameData);
375
376         friend class gmx::AnalysisDataStorageFrame;
377 };
378
379 }   // namespace internal
380
381 /********************************************************************
382  * AnalysisDataStorage::Impl implementation
383  */
384
385 AnalysisDataStorage::Impl::Impl()
386     : data_(NULL), bMultipoint_(false),
387       storageLimit_(0), pendingLimit_(1), firstFrameLocation_(0), nextIndex_(0)
388 {
389 }
390
391
392 int
393 AnalysisDataStorage::Impl::columnCount() const
394 {
395     GMX_ASSERT(data_ != NULL, "columnCount() called too early");
396     return data_->columnCount();
397 }
398
399
400 bool
401 AnalysisDataStorage::Impl::isMultipoint() const
402 {
403     return bMultipoint_;
404 }
405
406
407 int
408 AnalysisDataStorage::Impl::firstStoredIndex() const
409 {
410     return frames_[firstFrameLocation_]->frameIndex();
411 }
412
413
414 int
415 AnalysisDataStorage::Impl::computeStorageLocation(int index) const
416 {
417     if (index < firstStoredIndex() || index >= nextIndex_)
418     {
419         return -1;
420     }
421     return index % frames_.size();
422 }
423
424
425 size_t
426 AnalysisDataStorage::Impl::endStorageLocation() const
427 {
428     if (storeAll())
429     {
430         return frames_.size();
431     }
432     if (frames_[0]->frameIndex() == 0 || firstFrameLocation_ == 0)
433     {
434         return frames_.size() - 1;
435     }
436     return firstFrameLocation_ - 1;
437 }
438
439
440 void
441 AnalysisDataStorage::Impl::extendBuffer(size_t newSize)
442 {
443     frames_.reserve(newSize);
444     while (frames_.size() < newSize)
445     {
446         frames_.push_back(FramePointer(new FrameData(this, nextIndex_)));
447         ++nextIndex_;
448     }
449     // The unused frame should not be included in the count.
450     if (!storeAll())
451     {
452         --nextIndex_;
453     }
454 }
455
456
457 void
458 AnalysisDataStorage::Impl::rotateBuffer()
459 {
460     GMX_ASSERT(!storeAll(),
461                "No need to rotate internal buffer if everything is stored");
462     size_t prevFirst = firstFrameLocation_;
463     size_t nextFirst = prevFirst + 1;
464     if (nextFirst == frames_.size())
465     {
466         nextFirst = 0;
467     }
468     firstFrameLocation_ = nextFirst;
469     frames_[prevFirst]->clearFrame(nextIndex_ + 1);
470     ++nextIndex_;
471 }
472
473
474 internal::AnalysisDataFrameBuilderPointer
475 AnalysisDataStorage::Impl::getFrameBuilder()
476 {
477     if (builders_.empty())
478     {
479         return FrameBuilderPointer(new AnalysisDataStorageFrame(columnCount()));
480     }
481     FrameBuilderPointer builder(move(builders_.back()));
482     builders_.pop_back();
483     return move(builder);
484 }
485
486
487 void
488 AnalysisDataStorage::Impl::notifyPointSet(const AnalysisDataPointSetRef &points)
489 {
490     data_->notifyPointsAdd(points);
491 }
492
493
494 void
495 AnalysisDataStorage::Impl::notifyNextFrames(size_t firstLocation)
496 {
497     if (firstLocation != firstFrameLocation_)
498     {
499         // firstLocation can only be zero here if !storeAll() because
500         // firstFrameLocation_ is always zero for storeAll()
501         int prevIndex =
502             (firstLocation == 0 ? frames_.size() - 1 : firstLocation - 1);
503         if (!frames_[prevIndex]->isNotified())
504         {
505             return;
506         }
507     }
508     size_t i   = firstLocation;
509     size_t end = endStorageLocation();
510     while (i != end)
511     {
512         Impl::FrameData &storedFrame = *frames_[i];
513         if (!storedFrame.isFinished())
514         {
515             break;
516         }
517         if (!storedFrame.isNotified())
518         {
519             data_->notifyFrameStart(storedFrame.header());
520             data_->notifyPointsAdd(storedFrame.currentPoints());
521             data_->notifyFrameFinish(storedFrame.header());
522             storedFrame.markNotified();
523             if (storedFrame.frameIndex() >= storageLimit_)
524             {
525                 rotateBuffer();
526             }
527         }
528         ++i;
529         if (!storeAll() && i >= frames_.size())
530         {
531             i = 0;
532         }
533     }
534 }
535
536
537 void
538 AnalysisDataStorage::Impl::finishFrame(int index)
539 {
540     int                storageIndex = computeStorageLocation(index);
541     GMX_RELEASE_ASSERT(storageIndex >= 0, "Out of bounds frame index");
542     Impl::FrameData   &storedFrame = *frames_[storageIndex];
543     GMX_RELEASE_ASSERT(storedFrame.isStarted(),
544                        "finishFrame() called for frame before startFrame()");
545     GMX_RELEASE_ASSERT(!storedFrame.isFinished(),
546                        "finishFrame() called twice for the same frame");
547     GMX_RELEASE_ASSERT(storedFrame.frameIndex() == index,
548                        "Inconsistent internal frame indexing");
549     builders_.push_back(storedFrame.finishFrame());
550     if (isMultipoint())
551     {
552         // TODO: Check that the last point set has been finished
553         data_->notifyFrameFinish(storedFrame.header());
554         if (storedFrame.frameIndex() >= storageLimit_)
555         {
556             rotateBuffer();
557         }
558     }
559     else
560     {
561         notifyNextFrames(storageIndex);
562     }
563 }
564
565
566 /********************************************************************
567  * AnalysisDataStorageFrame implementation
568  */
569
570 namespace internal
571 {
572
573 AnalysisDataStorageFrameData::AnalysisDataStorageFrameData(
574         AnalysisDataStorage::Impl *storageImpl,
575         int                        index)
576     : storageImpl_(*storageImpl), header_(index, 0.0, 0.0), status_(eMissing)
577 {
578 }
579
580
581 void
582 AnalysisDataStorageFrameData::clearFrame(int newIndex)
583 {
584     GMX_RELEASE_ASSERT(!builder_, "Should not clear an in-progress frame");
585     status_ = eMissing;
586     header_ = AnalysisDataFrameHeader(newIndex, 0.0, 0.0);
587     values_.clear();
588 }
589
590
591 void
592 AnalysisDataStorageFrameData::startFrame(
593         const AnalysisDataFrameHeader   &header,
594         AnalysisDataFrameBuilderPointer  builder)
595 {
596     status_         = eStarted;
597     header_         = header;
598     builder_        = move(builder);
599     builder_->data_ = this;
600 }
601
602
603 AnalysisDataFrameBuilderPointer
604 AnalysisDataStorageFrameData::finishFrame()
605 {
606     status_ = eFinished;
607     values_ = builder_->values_;
608     builder_->clearValues();
609     AnalysisDataFrameBuilderPointer builder(move(builder_));
610     builder_.reset();
611     return move(builder);
612 }
613
614
615 AnalysisDataPointSetRef
616 AnalysisDataStorageFrameData::currentPoints() const
617 {
618     std::vector<AnalysisDataValue>::const_iterator begin = values_.begin();
619     std::vector<AnalysisDataValue>::const_iterator end   = values_.end();
620     while (begin != end && !begin->isSet())
621     {
622         ++begin;
623     }
624     while (end != begin && !(end-1)->isSet())
625     {
626         --end;
627     }
628     int firstColumn = (begin != end) ? begin - values_.begin() : 0;
629     return AnalysisDataPointSetRef(header_, firstColumn,
630                                    AnalysisDataValuesRef(begin, end));
631 }
632
633 }   // namespace internal
634
635 /********************************************************************
636  * AnalysisDataStorageFrame
637  */
638
639 AnalysisDataStorageFrame::AnalysisDataStorageFrame(int columnCount)
640     : data_(NULL), values_(columnCount)
641 {
642 }
643
644
645 AnalysisDataStorageFrame::~AnalysisDataStorageFrame()
646 {
647 }
648
649
650 void
651 AnalysisDataStorageFrame::clearValues()
652 {
653     std::vector<AnalysisDataValue>::iterator i;
654     for (i = values_.begin(); i != values_.end(); ++i)
655     {
656         i->clear();
657     }
658 }
659
660
661 void
662 AnalysisDataStorageFrame::finishPointSet()
663 {
664     GMX_RELEASE_ASSERT(data_ != NULL, "Invalid frame accessed");
665     GMX_RELEASE_ASSERT(data_->baseData().isMultipoint(),
666                        "Should not be called for non-multipoint data");
667     data_->values_ = values_;
668     data_->storageImpl().notifyPointSet(data_->currentPoints());
669     clearValues();
670 }
671
672
673 void
674 AnalysisDataStorageFrame::finishFrame()
675 {
676     GMX_RELEASE_ASSERT(data_ != NULL, "Invalid frame accessed");
677     data_->storageImpl().finishFrame(data_->frameIndex());
678 }
679
680
681 /********************************************************************
682  * AnalysisDataStorage
683  */
684
685 AnalysisDataStorage::AnalysisDataStorage()
686     : impl_(new Impl())
687 {
688 }
689
690
691 AnalysisDataStorage::~AnalysisDataStorage()
692 {
693 }
694
695
696 void
697 AnalysisDataStorage::setMultipoint(bool bMultipoint)
698 {
699     if (bMultipoint && impl_->storageLimit_ > 0)
700     {
701         GMX_THROW(APIError("Storage of multipoint data not supported"));
702     }
703     impl_->bMultipoint_ = bMultipoint;
704 }
705
706
707 void
708 AnalysisDataStorage::setParallelOptions(const AnalysisDataParallelOptions &opt)
709 {
710     impl_->pendingLimit_ = 2 * opt.parallelizationFactor() - 1;
711 }
712
713
714 AnalysisDataFrameRef
715 AnalysisDataStorage::tryGetDataFrame(int index) const
716 {
717     if (impl_->isMultipoint())
718     {
719         return AnalysisDataFrameRef();
720     }
721     int storageIndex = impl_->computeStorageLocation(index);
722     if (storageIndex == -1)
723     {
724         return AnalysisDataFrameRef();
725     }
726     const Impl::FrameData &storedFrame = *impl_->frames_[storageIndex];
727     if (!storedFrame.isAvailable())
728     {
729         return AnalysisDataFrameRef();
730     }
731     return storedFrame.frameReference();
732 }
733
734
735 bool
736 AnalysisDataStorage::requestStorage(int nframes)
737 {
738     if (impl_->isMultipoint())
739     {
740         return false;
741     }
742
743     // Handle the case when everything needs to be stored.
744     if (nframes == -1)
745     {
746         impl_->storageLimit_ = std::numeric_limits<int>::max();
747         return true;
748     }
749     // Check whether an earlier call has requested more storage.
750     if (nframes < impl_->storageLimit_)
751     {
752         return true;
753     }
754     impl_->storageLimit_ = nframes;
755     return true;
756 }
757
758
759 void
760 AnalysisDataStorage::startDataStorage(AbstractAnalysisData *data)
761 {
762     // Data needs to be set before calling extendBuffer()
763     impl_->data_ = data;
764     setMultipoint(data->isMultipoint());
765     if (!impl_->storeAll())
766     {
767         impl_->extendBuffer(impl_->storageLimit_ + impl_->pendingLimit_ + 1);
768     }
769 }
770
771
772 AnalysisDataStorageFrame &
773 AnalysisDataStorage::startFrame(const AnalysisDataFrameHeader &header)
774 {
775     GMX_ASSERT(header.isValid(), "Invalid header");
776     Impl::FrameData *storedFrame;
777     if (impl_->storeAll())
778     {
779         size_t size = header.index() + 1;
780         if (impl_->frames_.size() < size)
781         {
782             impl_->extendBuffer(size);
783         }
784         storedFrame = impl_->frames_[header.index()].get();
785     }
786     else
787     {
788         int storageIndex = impl_->computeStorageLocation(header.index());
789         if (storageIndex == -1)
790         {
791             GMX_THROW(APIError("Out of bounds frame index"));
792         }
793         storedFrame = impl_->frames_[storageIndex].get();
794     }
795     GMX_RELEASE_ASSERT(!storedFrame->isStarted(),
796                        "startFrame() called twice for the same frame");
797     GMX_RELEASE_ASSERT(storedFrame->frameIndex() == header.index(),
798                        "Inconsistent internal frame indexing");
799     storedFrame->startFrame(header, impl_->getFrameBuilder());
800     if (impl_->isMultipoint())
801     {
802         impl_->data_->notifyFrameStart(header);
803     }
804     return storedFrame->builder();
805 }
806
807
808 AnalysisDataStorageFrame &
809 AnalysisDataStorage::startFrame(int index, real x, real dx)
810 {
811     return startFrame(AnalysisDataFrameHeader(index, x, dx));
812 }
813
814
815 AnalysisDataStorageFrame &
816 AnalysisDataStorage::currentFrame(int index)
817 {
818     int                storageIndex = impl_->computeStorageLocation(index);
819     GMX_RELEASE_ASSERT(storageIndex >= 0, "Out of bounds frame index");
820     Impl::FrameData   &storedFrame = *impl_->frames_[storageIndex];
821     GMX_RELEASE_ASSERT(storedFrame.isStarted(),
822                        "currentFrame() called for frame before startFrame()");
823     GMX_RELEASE_ASSERT(!storedFrame.isFinished(),
824                        "currentFrame() called for frame after finishFrame()");
825     GMX_RELEASE_ASSERT(storedFrame.frameIndex() == index,
826                        "Inconsistent internal frame indexing");
827     return storedFrame.builder();
828 }
829
830
831 void
832 AnalysisDataStorage::finishFrame(int index)
833 {
834     impl_->finishFrame(index);
835 }
836
837 } // namespace gmx