Merge release-4-6 into master
[alexxy/gromacs.git] / src / gromacs / analysisdata / datastorage.cpp
1 /*
2  *
3  *                This source code is part of
4  *
5  *                 G   R   O   M   A   C   S
6  *
7  *          GROningen MAchine for Chemical Simulations
8  *
9  * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
10  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
11  * Copyright (c) 2001-2009, The GROMACS development team,
12  * check out http://www.gromacs.org for more information.
13
14  * This program is free software; you can redistribute it and/or
15  * modify it under the terms of the GNU General Public License
16  * as published by the Free Software Foundation; either version 2
17  * of the License, or (at your option) any later version.
18  *
19  * If you want to redistribute modifications, please consider that
20  * scientific software is very special. Version control is crucial -
21  * bugs must be traceable. We will be happy to consider code for
22  * inclusion in the official distribution, but derived work must not
23  * be called official GROMACS. Details are found in the README & COPYING
24  * files - if they are missing, get the official version at www.gromacs.org.
25  *
26  * To help us fund GROMACS development, we humbly ask that you cite
27  * the papers on the package - you can find them in the top README file.
28  *
29  * For more info, check our website at http://www.gromacs.org
30  */
31 /*! \internal \file
32  * \brief
33  * Implements classes in datastorage.h and paralleloptions.h.
34  *
35  * \author Teemu Murtola <teemu.murtola@cbr.su.se>
36  * \ingroup module_analysisdata
37  */
38 #include "datastorage.h"
39
40 #include <limits>
41
42 #include "gromacs/analysisdata/abstractdata.h"
43 #include "gromacs/analysisdata/dataframe.h"
44 #include "gromacs/analysisdata/paralleloptions.h"
45 #include "gromacs/fatalerror/exceptions.h"
46 #include "gromacs/fatalerror/gmxassert.h"
47
48 #include "datastorage-impl.h"
49
50 namespace gmx
51 {
52
53 /********************************************************************
54  * AnalysisDataParallelOptions
55  */
56
57 AnalysisDataParallelOptions::AnalysisDataParallelOptions()
58     : parallelizationFactor_(1)
59 {
60 }
61
62
63 AnalysisDataParallelOptions::AnalysisDataParallelOptions(int parallelizationFactor)
64     : parallelizationFactor_(parallelizationFactor)
65 {
66     GMX_RELEASE_ASSERT(parallelizationFactor >= 1,
67                        "Invalid parallelization factor");
68 }
69
70
71 /********************************************************************
72  * AnalysisDataStorageFrame
73  */
74
75 AnalysisDataStorageFrame::AnalysisDataStorageFrame(AnalysisDataStorage *storage,
76                                                    int columnCount, int index)
77     : storage_(storage), header_(index, 0.0, 0.0), values_(columnCount)
78 {
79 }
80
81
82 AnalysisDataStorageFrame::~AnalysisDataStorageFrame()
83 {
84 }
85
86
87 AnalysisDataPointSetRef
88 AnalysisDataStorageFrame::currentPoints() const
89 {
90     std::vector<AnalysisDataValue>::const_iterator begin = values_.begin();
91     std::vector<AnalysisDataValue>::const_iterator end = values_.end();
92     while (begin != end && !begin->isSet())
93     {
94         ++begin;
95     }
96     while (end != begin && !(end-1)->isSet())
97     {
98         --end;
99     }
100     int firstColumn = (begin != end) ? begin - values_.begin() : 0;
101     return AnalysisDataPointSetRef(header_, firstColumn,
102                 AnalysisDataValuesRef(begin, end));
103 }
104
105
106 void
107 AnalysisDataStorageFrame::clearValues()
108 {
109     std::vector<AnalysisDataValue>::iterator i;
110     for (i = values_.begin(); i != values_.end(); ++i)
111     {
112         i->clear();
113     }
114 }
115
116
117 void
118 AnalysisDataStorageFrame::finishPointSet()
119 {
120     storage_->impl_->notifyPointSet(currentPoints());
121     clearValues();
122 }
123
124
125 /********************************************************************
126  * AnalysisDataStorage::Impl
127  */
128
129 AnalysisDataStorage::Impl::Impl()
130     : data_(NULL), bMultipoint_(false),
131       storageLimit_(0), pendingLimit_(1), firstFrameLocation_(0), nextIndex_(0)
132 {
133 }
134
135
136 AnalysisDataStorage::Impl::~Impl()
137 {
138     FrameList::const_iterator i;
139     for (i = frames_.begin(); i != frames_.end(); ++i)
140     {
141         delete i->frame;
142     }
143 }
144
145
146 int
147 AnalysisDataStorage::Impl::columnCount() const
148 {
149     GMX_ASSERT(data_ != NULL, "columnCount() called too early");
150     return data_->columnCount();
151 }
152
153
154 bool
155 AnalysisDataStorage::Impl::isMultipoint() const
156 {
157     return bMultipoint_;
158 }
159
160
161 int
162 AnalysisDataStorage::Impl::firstStoredIndex() const
163 {
164     return frames_[firstFrameLocation_].frame->frameIndex();
165 }
166
167
168 int
169 AnalysisDataStorage::Impl::computeStorageLocation(int index) const
170 {
171     if (index < firstStoredIndex() || index >= nextIndex_)
172     {
173         return -1;
174     }
175     return index % frames_.size();
176 }
177
178
179 size_t
180 AnalysisDataStorage::Impl::endStorageLocation() const
181 {
182     if (storeAll())
183     {
184         return frames_.size();
185     }
186     if (frames_[0].frame->frameIndex() == 0 || firstFrameLocation_ == 0)
187     {
188         return frames_.size() - 1;
189     }
190     return firstFrameLocation_ - 1;
191 }
192
193
194 void
195 AnalysisDataStorage::Impl::extendBuffer(AnalysisDataStorage *storage,
196                                         size_t newSize)
197 {
198     frames_.reserve(newSize);
199     while (frames_.size() < newSize)
200     {
201         AnalysisDataStorageFrame *frame =
202             new AnalysisDataStorageFrame(storage, columnCount(), nextIndex_);
203         frames_.push_back(StoredFrame(frame));
204         ++nextIndex_;
205     }
206     // The unused frame should not be included in the count.
207     if (!storeAll())
208     {
209         --nextIndex_;
210     }
211 }
212
213
214 void
215 AnalysisDataStorage::Impl::rotateBuffer()
216 {
217     GMX_ASSERT(!storeAll(),
218                "No need to rotate internal buffer if everything is stored");
219     size_t prevFirst = firstFrameLocation_;
220     size_t nextFirst = prevFirst + 1;
221     if (nextFirst == frames_.size())
222     {
223         nextFirst = 0;
224     }
225     firstFrameLocation_ = nextFirst;
226     StoredFrame &prevFrame = frames_[prevFirst];
227     prevFrame.status = StoredFrame::eMissing;
228     prevFrame.frame->header_ = AnalysisDataFrameHeader(nextIndex_ + 1, 0.0, 0.0);
229     prevFrame.frame->clearValues();
230     ++nextIndex_;
231 }
232
233
234 void
235 AnalysisDataStorage::Impl::notifyPointSet(const AnalysisDataPointSetRef &points)
236 {
237     data_->notifyPointsAdd(points);
238 }
239
240
241 void
242 AnalysisDataStorage::Impl::notifyNextFrames(size_t firstLocation)
243 {
244     if (firstLocation != firstFrameLocation_)
245     {
246         // firstLocation can only be zero here if !storeAll() because
247         // firstFrameLocation_ is always zero for storeAll()
248         int prevIndex =
249             (firstLocation == 0 ? frames_.size() - 1 : firstLocation - 1);
250         if (!frames_[prevIndex].isNotified())
251         {
252             return;
253         }
254     }
255     size_t i = firstLocation;
256     size_t end = endStorageLocation();
257     while (i != end)
258     {
259         Impl::StoredFrame &storedFrame = frames_[i];
260         if (!storedFrame.isFinished())
261         {
262             break;
263         }
264         if (storedFrame.status == StoredFrame::eFinished)
265         {
266             data_->notifyFrameStart(storedFrame.frame->header());
267             data_->notifyPointsAdd(storedFrame.frame->currentPoints());
268             data_->notifyFrameFinish(storedFrame.frame->header());
269             storedFrame.status = StoredFrame::eNotified;
270             if (storedFrame.frame->frameIndex() >= storageLimit_)
271             {
272                 rotateBuffer();
273             }
274         }
275         ++i;
276         if (!storeAll() && i >= frames_.size())
277         {
278             i = 0;
279         }
280     }
281 }
282
283
284 /********************************************************************
285  * AnalysisDataStorage
286  */
287
288 AnalysisDataStorage::AnalysisDataStorage()
289     : impl_(new Impl())
290 {
291 }
292
293
294 AnalysisDataStorage::~AnalysisDataStorage()
295 {
296 }
297
298
299 void
300 AnalysisDataStorage::setMultipoint(bool bMultipoint)
301 {
302     if (bMultipoint && impl_->storageLimit_ > 0)
303     {
304         GMX_THROW(APIError("Storage of multipoint data not supported"));
305     }
306     impl_->bMultipoint_ = bMultipoint;
307 }
308
309
310 void
311 AnalysisDataStorage::setParallelOptions(const AnalysisDataParallelOptions &opt)
312 {
313     impl_->pendingLimit_ = 2 * opt.parallelizationFactor() - 1;
314 }
315
316
317 AnalysisDataFrameRef
318 AnalysisDataStorage::tryGetDataFrame(int index) const
319 {
320     if (impl_->isMultipoint())
321     {
322         return AnalysisDataFrameRef();
323     }
324     int storageIndex = impl_->computeStorageLocation(index);
325     if (storageIndex == -1)
326     {
327         return AnalysisDataFrameRef();
328     }
329     const Impl::StoredFrame &storedFrame = impl_->frames_[storageIndex];
330     if (!storedFrame.isAvailable())
331     {
332         return AnalysisDataFrameRef();
333     }
334     const AnalysisDataStorageFrame *frame = storedFrame.frame;
335     return AnalysisDataFrameRef(frame->header(), frame->values_);
336 }
337
338
339 bool
340 AnalysisDataStorage::requestStorage(int nframes)
341 {
342     if (impl_->isMultipoint())
343     {
344         return false;
345     }
346
347     // Handle the case when everything needs to be stored.
348     if (nframes == -1)
349     {
350         impl_->storageLimit_ = std::numeric_limits<int>::max();
351         return true;
352     }
353     // Check whether an earlier call has requested more storage.
354     if (nframes < impl_->storageLimit_)
355     {
356         return true;
357     }
358     impl_->storageLimit_ = nframes;
359     return true;
360 }
361
362
363 void
364 AnalysisDataStorage::startDataStorage(AbstractAnalysisData *data)
365 {
366     // Data needs to be set before calling extendBuffer()
367     impl_->data_ = data;
368     setMultipoint(data->isMultipoint());
369     if (!impl_->storeAll())
370     {
371         impl_->extendBuffer(this, impl_->storageLimit_ + impl_->pendingLimit_ + 1);
372     }
373 }
374
375
376 AnalysisDataStorageFrame &
377 AnalysisDataStorage::startFrame(const AnalysisDataFrameHeader &header)
378 {
379     GMX_ASSERT(header.isValid(), "Invalid header");
380     Impl::StoredFrame *storedFrame;
381     if (impl_->storeAll())
382     {
383         size_t size = header.index() + 1;
384         if (impl_->frames_.size() < size)
385         {
386             impl_->extendBuffer(this, size);
387         }
388         storedFrame = &impl_->frames_[header.index()];
389     }
390     else
391     {
392         int storageIndex = impl_->computeStorageLocation(header.index());
393         if (storageIndex == -1)
394         {
395             GMX_THROW(APIError("Out of bounds frame index"));
396         }
397         storedFrame = &impl_->frames_[storageIndex];
398     }
399     GMX_RELEASE_ASSERT(!storedFrame->isStarted(),
400                        "startFrame() called twice for the same frame");
401     GMX_RELEASE_ASSERT(storedFrame->frame->frameIndex() == header.index(),
402                        "Inconsistent internal frame indexing");
403     storedFrame->status = Impl::StoredFrame::eStarted;
404     storedFrame->frame->header_ = header;
405     if (impl_->isMultipoint())
406     {
407         impl_->data_->notifyFrameStart(header);
408     }
409     return *storedFrame->frame;
410 }
411
412
413 AnalysisDataStorageFrame &
414 AnalysisDataStorage::startFrame(int index, real x, real dx)
415 {
416     return startFrame(AnalysisDataFrameHeader(index, x, dx));
417 }
418
419
420 AnalysisDataStorageFrame &
421 AnalysisDataStorage::currentFrame(int index)
422 {
423     int storageIndex = impl_->computeStorageLocation(index);
424     GMX_RELEASE_ASSERT(storageIndex >= 0, "Out of bounds frame index");
425     Impl::StoredFrame &storedFrame = impl_->frames_[storageIndex];
426     GMX_RELEASE_ASSERT(storedFrame.isStarted(),
427                        "currentFrame() called for frame before startFrame()");
428     GMX_RELEASE_ASSERT(!storedFrame.isFinished(),
429                        "currentFrame() called for frame after finishFrame()");
430     GMX_RELEASE_ASSERT(storedFrame.frame->frameIndex() == index,
431                        "Inconsistent internal frame indexing");
432     return *storedFrame.frame;
433 }
434
435
436 void
437 AnalysisDataStorage::finishFrame(int index)
438 {
439     int storageIndex = impl_->computeStorageLocation(index);
440     GMX_RELEASE_ASSERT(storageIndex >= 0, "Out of bounds frame index");
441     Impl::StoredFrame &storedFrame = impl_->frames_[storageIndex];
442     GMX_RELEASE_ASSERT(storedFrame.isStarted(),
443                        "finishFrame() called for frame before startFrame()");
444     GMX_RELEASE_ASSERT(!storedFrame.isFinished(),
445                        "finishFrame() called twice for the same frame");
446     GMX_RELEASE_ASSERT(storedFrame.frame->frameIndex() == index,
447                        "Inconsistent internal frame indexing");
448     storedFrame.status = Impl::StoredFrame::eFinished;
449     if (impl_->isMultipoint())
450     {
451         // TODO: Check that the last point set has been finished
452         impl_->data_->notifyFrameFinish(storedFrame.frame->header());
453         if (storedFrame.frame->frameIndex() >= impl_->storageLimit_)
454         {
455             impl_->rotateBuffer();
456         }
457     }
458     else
459     {
460         impl_->notifyNextFrames(storageIndex);
461     }
462 }
463
464
465 void
466 AnalysisDataStorage::finishFrame(const AnalysisDataStorageFrame &frame)
467 {
468     finishFrame(frame.frameIndex());
469 }
470
471 } // namespace gmx