Merge "Merge branch release-4-6 into master"
[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 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          * Returns whether notifications should be immediately fired.
170          *
171          * This is used to optimize multipoint handling for non-parallel cases,
172          * where it is not necessary to store even a single frame.
173          *
174          * Does not throw.
175          */
176         bool shouldNotifyImmediately() const
177         {
178             return isMultipoint() && storageLimit_ == 0 && pendingLimit_ == 1;
179         }
180         /*! \brief
181          * Calls notification method in \a data_.
182          *
183          * \throws    unspecified  Any exception thrown by
184          *      AbstractAnalysisData::notifyPointsAdd().
185          */
186         void notifyPointSet(const AnalysisDataPointSetRef &points);
187         /*! \brief
188          * Calls notification methods for new frames.
189          *
190          * \param[in] firstLocation  First frame to consider.
191          * \throws    unspecified  Any exception thrown by frame notification
192          *      methods in AbstractAnalysisData.
193          *
194          * Notifies \a data_ of new frames (from \p firstLocation and after
195          * that) if all previous frames have already been notified.
196          * Also rotates the \a frames_ buffer as necessary.
197          */
198         void notifyNextFrames(size_t firstLocation);
199         //! Implementation for AnalysisDataStorage::finishFrame().
200         void finishFrame(int index);
201
202
203         //! Data object to use for notification calls.
204         AbstractAnalysisData   *data_;
205         /*! \brief
206          * Number of past frames that need to be stored.
207          *
208          * Always non-negative.  If storage of all frames has been requested,
209          * this is set to a large number.
210          */
211         int                     storageLimit_;
212         /*! \brief
213          * Number of future frames that may need to be started.
214          *
215          * Should always be at least one.
216          *
217          * \see AnalysisDataStorage::startFrame()
218          */
219         int                     pendingLimit_;
220         /*! \brief
221          * Data frames that are currently stored.
222          *
223          * If storage of all frames has been requested, this is simply a vector
224          * of frames up to the latest frame that has been started.
225          * In this case, \a firstFrameLocation_ is always zero.
226          *
227          * If storage of all frames is not requested, this is a ring buffer of
228          * frames of size \c n=storageLimit_+pendingLimit_+1.  If a frame with
229          * index \c index is currently stored, its location is
230          * \c index%frames_.size().
231          * When at most \a storageLimit_ first frames have been finished,
232          * this contains storage for the first \c n-1 frames.
233          * When more than \a storageLimit_ first frames have been finished,
234          * the oldest stored frame is stored in the location
235          * \a firstFrameLocation_, and \a storageLimit_ frames starting from
236          * this location are the last finished frames.  \a pendingLimit_ frames
237          * follow, and some of these may be in progress or finished.
238          * There is always one unused frame in the buffer, which is initialized
239          * such that when \a firstFrameLocation_ is incremented, it becomes
240          * valid.  This makes it easier to rotate the buffer in concurrent
241          * access scenarions (which are not yet otherwise implemented).
242          */
243         FrameList               frames_;
244         //! Location of oldest frame in \a frames_.
245         size_t                  firstFrameLocation_;
246         /*! \brief
247          * Currently unused frame builders.
248          *
249          * The builders are cached to avoid repeatedly allocating memory for
250          * them.  Typically, there are as many builders as there are concurrent
251          * users of the storage object.  Whenever a frame is started, a builder
252          * is pulled from this pool by getFrameBuilder() (a new one is created
253          * if none are available), and assigned for that frame.  When that
254          * frame is finished, the builder is returned to this pool.
255          */
256         FrameBuilderList        builders_;
257         /*! \brief
258          * Index of next frame that will be added to \a frames_.
259          *
260          * If all frames are not stored, this will be the index of the unused
261          * frame (see \a frames_).
262          */
263         int                     nextIndex_;
264 };
265
266 /********************************************************************
267  * AnalysisDataStorageFrameImpl declaration
268  */
269
270 namespace internal
271 {
272
273 /*! \internal \brief
274  * Internal representation for a single stored frame.
275  *
276  * It is implemented such that the frame header is always valid, i.e.,
277  * header().isValid() returns always true.
278  *
279  * Methods in this class do not throw unless otherwise indicated.
280  *
281  * \ingroup module_analysisdata
282  */
283 class AnalysisDataStorageFrameData
284 {
285     public:
286         //! Shorthand for a iterator into storage value containers.
287         typedef std::vector<AnalysisDataValue>::const_iterator ValueIterator;
288
289         //! Indicates what operations have been performed on a frame.
290         enum Status
291         {
292             eMissing,  //!< Frame has not yet been started.
293             eStarted,  //!< startFrame() has been called.
294             eFinished, //!< finishFrame() has been called.
295             eNotified  //!< Appropriate notifications have been sent.
296         };
297
298         /*! \brief
299          * Create a new storage frame.
300          *
301          * \param     storageImpl  Storage object this frame belongs to.
302          * \param[in] index        Zero-based index for the frame.
303          */
304         AnalysisDataStorageFrameData(AnalysisDataStorage::Impl *storageImpl,
305                                      int                        index);
306
307         //! Whether the frame has been started with startFrame().
308         bool isStarted() const { return status_ >= eStarted; }
309         //! Whether the frame has been finished with finishFrame().
310         bool isFinished() const { return status_ >= eFinished; }
311         //! Whether all notifications have been sent.
312         bool isNotified() const { return status_ >= eNotified; }
313         //! Whether the frame is ready to be available outside the storage.
314         bool isAvailable() const { return status_ >= eFinished; }
315
316         //! Marks the frame as notified.
317         void markNotified() { status_ = eNotified; }
318
319         //! Returns the storage implementation object.
320         AnalysisDataStorage::Impl &storageImpl() const { return storageImpl_; }
321         //! Returns the underlying data object (for data dimensionalities etc.).
322         const AbstractAnalysisData &baseData() const { return *storageImpl().data_; }
323
324         //! Returns header for the frame.
325         const AnalysisDataFrameHeader &header() const { return header_; }
326         //! Returns zero-based index of the frame.
327         int frameIndex() const { return header().index(); }
328         //! Returns the number of point sets for the frame.
329         int pointSetCount() const { return pointSets_.size(); }
330
331         //! Clears the frame for reusing as a new frame.
332         void clearFrame(int newIndex);
333         /*! \brief
334          * Initializes the frame during AnalysisDataStorage::startFrame().
335          *
336          * \param[in] header  Header to use for the new frame.
337          * \param[in] builder Builder object to use.
338          */
339         void startFrame(const AnalysisDataFrameHeader   &header,
340                         AnalysisDataFrameBuilderPointer  builder);
341         //! Returns the builder for this frame.
342         AnalysisDataStorageFrame &builder() const
343         {
344             GMX_ASSERT(builder_, "Accessing builder for not-in-progress frame");
345             return *builder_;
346         }
347         /*! \brief
348          * Adds a new point set to this frame.
349          */
350         void addPointSet(int dataSetIndex, int firstColumn,
351                          ValueIterator begin, ValueIterator end);
352         /*! \brief
353          * Finalizes the frame during AnalysisDataStorage::finishFrame().
354          *
355          * \returns The builder object used by the frame, for reusing it for
356          *      other frames.
357          */
358         AnalysisDataFrameBuilderPointer finishFrame(bool bMultipoint);
359
360         //! Returns frame reference to this frame.
361         AnalysisDataFrameRef frameReference() const
362         {
363             return AnalysisDataFrameRef(header_, values_, pointSets_);
364         }
365         //! Returns point set reference to a given point set.
366         AnalysisDataPointSetRef pointSet(int index) const;
367
368     private:
369         //! Storage object that contains this frame.
370         AnalysisDataStorage::Impl              &storageImpl_;
371         //! Header for the frame.
372         AnalysisDataFrameHeader                 header_;
373         //! Values for the frame.
374         std::vector<AnalysisDataValue>          values_;
375         //! Information about each point set in the frame.
376         std::vector<AnalysisDataPointSetInfo>   pointSets_;
377         /*! \brief
378          * Builder object for the frame.
379          *
380          * Non-NULL when the frame is in progress, i.e., has been started but
381          * not yet finished.
382          */
383         AnalysisDataFrameBuilderPointer         builder_;
384         //! In what state the frame currently is.
385         Status                                  status_;
386
387         GMX_DISALLOW_COPY_AND_ASSIGN(AnalysisDataStorageFrameData);
388 };
389
390 }   // namespace internal
391
392 /********************************************************************
393  * AnalysisDataStorage::Impl implementation
394  */
395
396 AnalysisDataStorage::Impl::Impl()
397     : data_(NULL),
398       storageLimit_(0), pendingLimit_(1), firstFrameLocation_(0), nextIndex_(0)
399 {
400 }
401
402
403 bool
404 AnalysisDataStorage::Impl::isMultipoint() const
405 {
406     GMX_ASSERT(data_ != NULL, "isMultipoint() called too early");
407     return data_->isMultipoint();
408 }
409
410
411 int
412 AnalysisDataStorage::Impl::firstStoredIndex() const
413 {
414     return frames_[firstFrameLocation_]->frameIndex();
415 }
416
417
418 int
419 AnalysisDataStorage::Impl::computeStorageLocation(int index) const
420 {
421     if (index < firstStoredIndex() || index >= nextIndex_)
422     {
423         return -1;
424     }
425     return index % frames_.size();
426 }
427
428
429 size_t
430 AnalysisDataStorage::Impl::endStorageLocation() const
431 {
432     if (storeAll())
433     {
434         return frames_.size();
435     }
436     if (frames_[0]->frameIndex() == 0 || firstFrameLocation_ == 0)
437     {
438         return frames_.size() - 1;
439     }
440     return firstFrameLocation_ - 1;
441 }
442
443
444 void
445 AnalysisDataStorage::Impl::extendBuffer(size_t newSize)
446 {
447     frames_.reserve(newSize);
448     while (frames_.size() < newSize)
449     {
450         frames_.push_back(FramePointer(new FrameData(this, nextIndex_)));
451         ++nextIndex_;
452     }
453     // The unused frame should not be included in the count.
454     if (!storeAll())
455     {
456         --nextIndex_;
457     }
458 }
459
460
461 void
462 AnalysisDataStorage::Impl::rotateBuffer()
463 {
464     GMX_ASSERT(!storeAll(),
465                "No need to rotate internal buffer if everything is stored");
466     size_t prevFirst = firstFrameLocation_;
467     size_t nextFirst = prevFirst + 1;
468     if (nextFirst == frames_.size())
469     {
470         nextFirst = 0;
471     }
472     firstFrameLocation_ = nextFirst;
473     frames_[prevFirst]->clearFrame(nextIndex_ + 1);
474     ++nextIndex_;
475 }
476
477
478 internal::AnalysisDataFrameBuilderPointer
479 AnalysisDataStorage::Impl::getFrameBuilder()
480 {
481     if (builders_.empty())
482     {
483         return FrameBuilderPointer(new AnalysisDataStorageFrame(*data_));
484     }
485     FrameBuilderPointer builder(move(builders_.back()));
486     builders_.pop_back();
487     return move(builder);
488 }
489
490
491 void
492 AnalysisDataStorage::Impl::notifyPointSet(const AnalysisDataPointSetRef &points)
493 {
494     data_->notifyPointsAdd(points);
495 }
496
497
498 void
499 AnalysisDataStorage::Impl::notifyNextFrames(size_t firstLocation)
500 {
501     if (firstLocation != firstFrameLocation_)
502     {
503         // firstLocation can only be zero here if !storeAll() because
504         // firstFrameLocation_ is always zero for storeAll()
505         int prevIndex =
506             (firstLocation == 0 ? frames_.size() - 1 : firstLocation - 1);
507         if (!frames_[prevIndex]->isNotified())
508         {
509             return;
510         }
511     }
512     size_t i   = firstLocation;
513     size_t end = endStorageLocation();
514     while (i != end)
515     {
516         Impl::FrameData &storedFrame = *frames_[i];
517         if (!storedFrame.isFinished())
518         {
519             break;
520         }
521         if (!storedFrame.isNotified())
522         {
523             data_->notifyFrameStart(storedFrame.header());
524             for (int j = 0; j < storedFrame.pointSetCount(); ++j)
525             {
526                 data_->notifyPointsAdd(storedFrame.pointSet(j));
527             }
528             data_->notifyFrameFinish(storedFrame.header());
529             storedFrame.markNotified();
530             if (storedFrame.frameIndex() >= storageLimit_)
531             {
532                 rotateBuffer();
533             }
534         }
535         ++i;
536         if (!storeAll() && i >= frames_.size())
537         {
538             i = 0;
539         }
540     }
541 }
542
543
544 void
545 AnalysisDataStorage::Impl::finishFrame(int index)
546 {
547     int                storageIndex = computeStorageLocation(index);
548     GMX_RELEASE_ASSERT(storageIndex >= 0, "Out of bounds frame index");
549     Impl::FrameData   &storedFrame = *frames_[storageIndex];
550     GMX_RELEASE_ASSERT(storedFrame.isStarted(),
551                        "finishFrame() called for frame before startFrame()");
552     GMX_RELEASE_ASSERT(!storedFrame.isFinished(),
553                        "finishFrame() called twice for the same frame");
554     GMX_RELEASE_ASSERT(storedFrame.frameIndex() == index,
555                        "Inconsistent internal frame indexing");
556     builders_.push_back(storedFrame.finishFrame(isMultipoint()));
557     if (shouldNotifyImmediately())
558     {
559         data_->notifyFrameFinish(storedFrame.header());
560         if (storedFrame.frameIndex() >= storageLimit_)
561         {
562             rotateBuffer();
563         }
564     }
565     else
566     {
567         notifyNextFrames(storageIndex);
568     }
569 }
570
571
572 /********************************************************************
573  * AnalysisDataStorageFrame implementation
574  */
575
576 namespace internal
577 {
578
579 AnalysisDataStorageFrameData::AnalysisDataStorageFrameData(
580         AnalysisDataStorage::Impl *storageImpl,
581         int                        index)
582     : storageImpl_(*storageImpl), header_(index, 0.0, 0.0), status_(eMissing)
583 {
584     GMX_RELEASE_ASSERT(storageImpl->data_ != NULL,
585                        "Storage frame constructed before data started");
586     // With non-multipoint data, the point set structure is static,
587     // so initialize it only once here.
588     if (!baseData().isMultipoint())
589     {
590         int offset = 0;
591         for (int i = 0; i < baseData().dataSetCount(); ++i)
592         {
593             int columnCount = baseData().columnCount(i);
594             pointSets_.push_back(
595                     AnalysisDataPointSetInfo(offset, columnCount, i, 0));
596             offset += columnCount;
597         }
598     }
599 }
600
601
602 void
603 AnalysisDataStorageFrameData::clearFrame(int newIndex)
604 {
605     GMX_RELEASE_ASSERT(!builder_, "Should not clear an in-progress frame");
606     status_ = eMissing;
607     header_ = AnalysisDataFrameHeader(newIndex, 0.0, 0.0);
608     values_.clear();
609     if (baseData().isMultipoint())
610     {
611         pointSets_.clear();
612     }
613 }
614
615
616 void
617 AnalysisDataStorageFrameData::startFrame(
618         const AnalysisDataFrameHeader   &header,
619         AnalysisDataFrameBuilderPointer  builder)
620 {
621     status_         = eStarted;
622     header_         = header;
623     builder_        = move(builder);
624     builder_->data_ = this;
625     builder_->selectDataSet(0);
626 }
627
628
629 void
630 AnalysisDataStorageFrameData::addPointSet(int dataSetIndex, int firstColumn,
631                                           ValueIterator begin, ValueIterator end)
632 {
633     const int valueCount  = end - begin;
634     if (storageImpl().shouldNotifyImmediately())
635     {
636         AnalysisDataPointSetInfo pointSetInfo(0, valueCount,
637                                               dataSetIndex, firstColumn);
638         storageImpl().notifyPointSet(
639                 AnalysisDataPointSetRef(header(), pointSetInfo,
640                                         AnalysisDataValuesRef(begin, end)));
641     }
642     else
643     {
644         pointSets_.push_back(
645                 AnalysisDataPointSetInfo(values_.size(), valueCount,
646                                          dataSetIndex, 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(static_cast<int>(pointSets_.size()) == baseData().dataSetCount(),
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(
691         const AbstractAnalysisData &data)
692     : data_(NULL), currentDataSet_(0), currentOffset_(0),
693       columnCount_(data.columnCount(0)), bPointSetInProgress_(false)
694 {
695     int totalColumnCount = 0;
696     for (int i = 0; i < data.dataSetCount(); ++i)
697     {
698         totalColumnCount += data.columnCount(i);
699     }
700     values_.resize(totalColumnCount);
701 }
702
703
704 AnalysisDataStorageFrame::~AnalysisDataStorageFrame()
705 {
706 }
707
708
709 void
710 AnalysisDataStorageFrame::clearValues()
711 {
712     if (bPointSetInProgress_)
713     {
714         std::vector<AnalysisDataValue>::iterator i;
715         for (i = values_.begin(); i != values_.end(); ++i)
716         {
717             i->clear();
718         }
719     }
720     bPointSetInProgress_ = false;
721 }
722
723
724 void
725 AnalysisDataStorageFrame::selectDataSet(int index)
726 {
727     GMX_RELEASE_ASSERT(data_ != NULL, "Invalid frame accessed");
728     const AbstractAnalysisData &baseData = data_->baseData();
729     GMX_RELEASE_ASSERT(index >= 0 && index < baseData.dataSetCount(),
730                        "Out of range data set index");
731     GMX_RELEASE_ASSERT(!baseData.isMultipoint() || !bPointSetInProgress_,
732                        "Point sets in multipoint data cannot span data sets");
733     currentDataSet_ = index;
734     currentOffset_  = 0;
735     // TODO: Consider precalculating.
736     for (int i = 0; i < index; ++i)
737     {
738         currentOffset_ += baseData.columnCount(i);
739     }
740     columnCount_    = baseData.columnCount(index);
741 }
742
743
744 void
745 AnalysisDataStorageFrame::finishPointSet()
746 {
747     GMX_RELEASE_ASSERT(data_ != NULL, "Invalid frame accessed");
748     GMX_RELEASE_ASSERT(data_->baseData().isMultipoint(),
749                        "Should not be called for non-multipoint data");
750     if (bPointSetInProgress_)
751     {
752         std::vector<AnalysisDataValue>::const_iterator begin
753             = values_.begin() + currentOffset_;
754         std::vector<AnalysisDataValue>::const_iterator end
755             = begin + columnCount_;
756         int firstColumn = 0;
757         while (begin != end && !begin->isSet())
758         {
759             ++begin;
760             ++firstColumn;
761         }
762         while (end != begin && !(end-1)->isSet())
763         {
764             --end;
765         }
766         if (begin == end)
767         {
768             firstColumn = 0;
769         }
770         data_->addPointSet(currentDataSet_, firstColumn, begin, end);
771     }
772     clearValues();
773 }
774
775
776 void
777 AnalysisDataStorageFrame::finishFrame()
778 {
779     GMX_RELEASE_ASSERT(data_ != NULL, "Invalid frame accessed");
780     data_->storageImpl().finishFrame(data_->frameIndex());
781 }
782
783
784 /********************************************************************
785  * AnalysisDataStorage
786  */
787
788 AnalysisDataStorage::AnalysisDataStorage()
789     : impl_(new Impl())
790 {
791 }
792
793
794 AnalysisDataStorage::~AnalysisDataStorage()
795 {
796 }
797
798
799 void
800 AnalysisDataStorage::setParallelOptions(const AnalysisDataParallelOptions &opt)
801 {
802     impl_->pendingLimit_ = 2 * opt.parallelizationFactor() - 1;
803 }
804
805
806 AnalysisDataFrameRef
807 AnalysisDataStorage::tryGetDataFrame(int index) const
808 {
809     int storageIndex = impl_->computeStorageLocation(index);
810     if (storageIndex == -1)
811     {
812         return AnalysisDataFrameRef();
813     }
814     const Impl::FrameData &storedFrame = *impl_->frames_[storageIndex];
815     if (!storedFrame.isAvailable())
816     {
817         return AnalysisDataFrameRef();
818     }
819     return storedFrame.frameReference();
820 }
821
822
823 bool
824 AnalysisDataStorage::requestStorage(int nframes)
825 {
826     // Handle the case when everything needs to be stored.
827     if (nframes == -1)
828     {
829         impl_->storageLimit_ = std::numeric_limits<int>::max();
830         return true;
831     }
832     // Check whether an earlier call has requested more storage.
833     if (nframes < impl_->storageLimit_)
834     {
835         return true;
836     }
837     impl_->storageLimit_ = nframes;
838     return true;
839 }
840
841
842 void
843 AnalysisDataStorage::startDataStorage(AbstractAnalysisData *data)
844 {
845     // Data needs to be set before calling extendBuffer()
846     impl_->data_ = data;
847     if (!impl_->storeAll())
848     {
849         impl_->extendBuffer(impl_->storageLimit_ + impl_->pendingLimit_ + 1);
850     }
851 }
852
853
854 AnalysisDataStorageFrame &
855 AnalysisDataStorage::startFrame(const AnalysisDataFrameHeader &header)
856 {
857     GMX_ASSERT(header.isValid(), "Invalid header");
858     Impl::FrameData *storedFrame;
859     if (impl_->storeAll())
860     {
861         size_t size = header.index() + 1;
862         if (impl_->frames_.size() < size)
863         {
864             impl_->extendBuffer(size);
865         }
866         storedFrame = impl_->frames_[header.index()].get();
867     }
868     else
869     {
870         int storageIndex = impl_->computeStorageLocation(header.index());
871         if (storageIndex == -1)
872         {
873             GMX_THROW(APIError("Out of bounds frame index"));
874         }
875         storedFrame = impl_->frames_[storageIndex].get();
876     }
877     GMX_RELEASE_ASSERT(!storedFrame->isStarted(),
878                        "startFrame() called twice for the same frame");
879     GMX_RELEASE_ASSERT(storedFrame->frameIndex() == header.index(),
880                        "Inconsistent internal frame indexing");
881     storedFrame->startFrame(header, impl_->getFrameBuilder());
882     if (impl_->shouldNotifyImmediately())
883     {
884         impl_->data_->notifyFrameStart(header);
885     }
886     return storedFrame->builder();
887 }
888
889
890 AnalysisDataStorageFrame &
891 AnalysisDataStorage::startFrame(int index, real x, real dx)
892 {
893     return startFrame(AnalysisDataFrameHeader(index, x, dx));
894 }
895
896
897 AnalysisDataStorageFrame &
898 AnalysisDataStorage::currentFrame(int index)
899 {
900     int                storageIndex = impl_->computeStorageLocation(index);
901     GMX_RELEASE_ASSERT(storageIndex >= 0, "Out of bounds frame index");
902     Impl::FrameData   &storedFrame = *impl_->frames_[storageIndex];
903     GMX_RELEASE_ASSERT(storedFrame.isStarted(),
904                        "currentFrame() called for frame before startFrame()");
905     GMX_RELEASE_ASSERT(!storedFrame.isFinished(),
906                        "currentFrame() called for frame after finishFrame()");
907     GMX_RELEASE_ASSERT(storedFrame.frameIndex() == index,
908                        "Inconsistent internal frame indexing");
909     return storedFrame.builder();
910 }
911
912
913 void
914 AnalysisDataStorage::finishFrame(int index)
915 {
916     impl_->finishFrame(index);
917 }
918
919 } // namespace gmx