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