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