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