Improve AbstractAnalysisData data access interface.
[alexxy/gromacs.git] / src / gromacs / analysisdata / analysisdata.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 analysisdata.h.
34  *
35  * \author Teemu Murtola <teemu.murtola@cbr.su.se>
36  * \ingroup module_analysisdata
37  */
38 #include "gromacs/analysisdata/analysisdata.h"
39
40 #include <algorithm>
41 #include <memory>
42
43 #include "gromacs/analysisdata/dataframe.h"
44 #include "gromacs/fatalerror/exceptions.h"
45 #include "gromacs/fatalerror/gmxassert.h"
46
47 #include "abstractdata-impl.h"
48 #include "analysisdata-impl.h"
49
50 namespace gmx
51 {
52
53 /********************************************************************
54  * AnalysisData::Impl
55  ********************************************************************/
56
57 static bool
58 frame_index_gtr(AnalysisDataFrame *a, AnalysisDataFrame *b)
59 {
60     return a->_index > b->_index;
61 }
62
63
64 AnalysisData::Impl::Impl(AnalysisData *data)
65     : _data(*data), _pstart(0)
66 {
67 }
68
69
70 AnalysisData::Impl::~Impl()
71 {
72     HandleList::const_iterator i;
73     for (i = _handles.begin(); i != _handles.end(); ++i)
74     {
75         delete *i;
76     }
77
78     FrameList::const_iterator j;
79     for (j = _pending.begin(); j != _pending.end(); ++j)
80     {
81         delete *j;
82     }
83 }
84
85
86 void
87 AnalysisData::Impl::addPendingFrame(AnalysisDataFrame *fr)
88 {
89     GMX_ASSERT(fr->_index >= _data.frameCount(),
90                "addPendingFrame() called for too old frame");
91     size_t pindex = fr->_index - _data.frameCount();
92     if (pindex == 0)
93     {
94         // Just store our frame if it is the next one.
95         _data.storeNextFrame(fr->_x, fr->_dx, fr->_y, fr->_dy, fr->_present);
96         incrementPStart();
97     }
98     else
99     {
100         if (pindex >= _pending.size())
101         {
102             // TODO: We need to wait until earlier frames are ready...
103         }
104         // TODO: This is not thread-safe.
105         pindex += _pstart;
106         if (pindex > _pending.size())
107         {
108             pindex -= _pending.size();
109         }
110
111         int ncol = _data.columnCount();
112         _pending[pindex]->_x     = fr->_x;
113         _pending[pindex]->_dx    = fr->_dx;
114         for (int i = 0; i < ncol; ++i)
115         {
116             _pending[pindex]->_y[i]       = fr->_y[i];
117             _pending[pindex]->_dy[i]      = fr->_dy[i];
118             _pending[pindex]->_present[i] = fr->_present[i];
119         }
120         _pending[pindex]->_index = fr->_index;
121     }
122     processPendingFrames();
123 }
124
125
126 void
127 AnalysisData::Impl::processPendingFrames()
128 {
129     while (_pending[_pstart]->_index != -1)
130     {
131         AnalysisDataFrame *fr = _pending[_pstart];
132
133         _data.storeNextFrame(fr->_x, fr->_dx, fr->_y, fr->_dy, fr->_present);
134         fr->_index = -1;
135         incrementPStart();
136     }
137 }
138
139
140 void
141 AnalysisData::Impl::incrementPStart()
142 {
143     size_t val = _pstart;
144
145     ++val;
146     if (val >= _pending.size())
147     {
148         val -= _pending.size();
149     }
150     _pstart = val;
151 }
152
153
154 /********************************************************************
155  * AnalysisData
156  */
157
158 AnalysisData::AnalysisData()
159     : _impl(new Impl(this))
160 {
161 }
162
163
164 AnalysisData::~AnalysisData()
165 {
166     delete _impl;
167 }
168
169
170 void
171 AnalysisData::setColumns(int ncol, bool multipoint)
172 {
173     GMX_RELEASE_ASSERT(ncol > 0, "Number of columns must be positive");
174     GMX_RELEASE_ASSERT(_impl->_handles.empty(),
175                        "Cannot change data dimensionality after creating handles");
176     setColumnCount(ncol);
177     setMultipoint(multipoint);
178 }
179
180
181 AnalysisDataHandle *
182 AnalysisData::startData(AnalysisDataParallelOptions opt)
183 {
184     if (_impl->_handles.empty())
185     {
186         startDataStore();
187     }
188     else if (isMultipoint())
189     {
190         GMX_THROW(NotImplementedError("Parallelism not supported for multipoint data"));
191     }
192
193     std::auto_ptr<AnalysisDataHandle> handle(new AnalysisDataHandle(this));
194     _impl->_handles.push_back(handle.get());
195
196     size_t oldSize = _impl->_pending.size();
197     _impl->_pending.resize(2 * _impl->_handles.size() - 1);
198     Impl::FrameList::iterator i;
199     for (i = _impl->_pending.begin() + oldSize; i != _impl->_pending.end(); ++i)
200     {
201         *i = new AnalysisDataFrame(columnCount());
202         (*i)->_index = -1;
203     }
204
205     return handle.release();
206 }
207
208
209 void
210 AnalysisData::finishData(AnalysisDataHandle *handle)
211 {
212     Impl::HandleList::iterator i;
213
214     i = std::find(_impl->_handles.begin(), _impl->_handles.end(), handle);
215     GMX_RELEASE_ASSERT(i != _impl->_handles.end(),
216                        "finishData() called for an unknown handle");
217
218     _impl->_handles.erase(i);
219     delete handle;
220
221     if (_impl->_handles.empty())
222     {
223         notifyDataFinish();
224     }
225 }
226
227
228 /********************************************************************
229  * AnalysisDataHandle::Impl
230  */
231
232 AnalysisDataHandle::Impl::Impl(AnalysisData *data)
233     : _data(*data)
234 {
235     if (!_data.isMultipoint())
236     {
237         _frame.reset(new AnalysisDataFrame(_data.columnCount()));
238     }
239 }
240
241
242 AnalysisDataHandle::Impl::~Impl()
243 {
244 }
245
246
247 /********************************************************************
248  * AnalysisDataHandle
249  */
250
251 AnalysisDataHandle::AnalysisDataHandle(AnalysisData *data)
252     : _impl(new Impl(data))
253 {
254 }
255
256
257 AnalysisDataHandle::~AnalysisDataHandle()
258 {
259     delete _impl;
260 }
261
262
263 void
264 AnalysisDataHandle::startFrame(int index, real x, real dx)
265 {
266     if (_impl->_data.isMultipoint())
267     {
268         _impl->_data.notifyFrameStart(AnalysisDataFrameHeader(index, x, dx));
269     }
270     else
271     {
272         _impl->_frame->_index = index;
273         _impl->_frame->_x  = x;
274         _impl->_frame->_dx = dx;
275         for (int i = 0; i < _impl->_data.columnCount(); ++i)
276         {
277             _impl->_frame->_y[i]  = 0.0;
278             _impl->_frame->_dy[i] = 0.0;
279             _impl->_frame->_present[i] = false;
280         }
281     }
282 }
283
284
285 void
286 AnalysisDataHandle::addPoint(int col, real y, real dy, bool present)
287 {
288     if (_impl->_data.isMultipoint())
289     {
290         _impl->_data.notifyPointsAdd(col, 1, &y, &dy, &present);
291     }
292     else
293     {
294         GMX_ASSERT(!_impl->_frame->_present[col],
295                    "Data for a column set multiple times");
296         _impl->_frame->_y[col] = y;
297         _impl->_frame->_dy[col] = dy;
298         _impl->_frame->_present[col] = present;
299     }
300 }
301
302
303 void
304 AnalysisDataHandle::addPoints(int firstcol, int n,
305                               const real *y, const real *dy,
306                               const bool *present)
307 {
308     if (_impl->_data.isMultipoint())
309     {
310         _impl->_data.notifyPointsAdd(firstcol, n, y, dy, present);
311     }
312     else
313     {
314         for (int i = 0; i < n; ++i)
315         {
316             addPoint(firstcol + i, y[i], dy ? dy[i] : 0.0,
317                      present ? present[i] : true);
318         }
319     }
320 }
321
322
323 void
324 AnalysisDataHandle::finishFrame()
325 {
326     if (_impl->_data.isMultipoint())
327     {
328         _impl->_data.notifyFrameFinish();
329     }
330     else
331     {
332         _impl->_data._impl->addPendingFrame(_impl->_frame.get());
333     }
334 }
335
336
337 void
338 AnalysisDataHandle::addFrame(int index, real x, const real *y, const real *dy,
339                              const bool *present)
340 {
341     addFrame(index, x, 0.0, y, dy, present);
342 }
343
344
345 void
346 AnalysisDataHandle::addFrame(int index, real x, real dx,
347                              const real *y, const real *dy,
348                              const bool *present)
349 {
350     startFrame(index, x, dx);
351     addPoints(0, _impl->_data.columnCount(), y, dy, present);
352     finishFrame();
353 }
354
355
356 void
357 AnalysisDataHandle::finishData()
358 {
359     // Calls delete this
360     _impl->_data.finishData(this);
361 }
362
363 } // namespace gmx