2 * This file is part of the GROMACS molecular simulation package.
4 * Copyright (c) 2010,2011,2012,2013, by the GROMACS development team, led by
5 * David van der Spoel, Berk Hess, Erik Lindahl, and including many
6 * others, as listed in the AUTHORS file in the top-level source
7 * directory and at http://www.gromacs.org.
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.
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.
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.
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.
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.
37 * Declares gmx::AbstractAnalysisData.
39 * \author Teemu Murtola <teemu.murtola@gmail.com>
41 * \ingroup module_analysisdata
43 #ifndef GMX_ANALYSISDATA_ABSTRACTDATA_H
44 #define GMX_ANALYSISDATA_ABSTRACTDATA_H
46 #include <boost/shared_ptr.hpp>
48 #include "../legacyheaders/types/simple.h"
50 #include "../utility/common.h"
55 class AnalysisDataModuleInterface;
56 class AnalysisDataFrameHeader;
57 class AnalysisDataFrameRef;
58 class AnalysisDataPointSetRef;
59 class AnalysisDataStorage;
61 //! Smart pointer for managing a generic analysis data module.
62 typedef boost::shared_ptr<AnalysisDataModuleInterface> AnalysisDataModulePointer;
65 * Abstract base class for all objects that provide data.
67 * The public interface includes methods for querying the data (isMultipoint(),
68 * dataSetCount(), columnCount(), frameCount(), tryGetDataFrame(),
69 * getDataFrame(), requestStorage()) and methods for using modules for
70 * processing the data (addModule(), addColumnModule(), applyModule()).
72 * Notice that even for non-const objects, the interface does not provide any
73 * means of altering the data. It is only possible to add modules, making it
74 * relatively safe to return a non-const pointer of this type pointing to an
75 * internal data structure without worrying about possible modifications of the
79 * This class also provides protected methods for use in derived classes.
80 * The properties returned by isMultipoint(), dataSetCount(), and columnCount()
81 * must be set using setMultipoint(), setDataSetCount(), and setColumnCount(),
82 * and notify*() methods must be used to report when data becomes available for
83 * modules to process it.
84 * There are also two protected pure virtual methods that need to be
85 * implemented to provide access to stored data: requestStorageInternal() and
86 * tryGetDataFrameInternal().
88 * It is up to subclasses to ensure that the protected methods are called in a
89 * correct sequence (the methods will assert in most incorrect use cases), and
90 * that the data provided through the public interface matches that passed to
91 * the modules with the notify methods.
92 * Helper class AnalysisDataStorage provides a default implementation for
93 * storing data (calls to the pure virtual methods can simply be forwarded to
94 * appropriate methods in the helper class), and takes care of correctly
95 * calling the notification methods when new data is added to the storage.
96 * In most cases, it should be used to implement the derived classes.
99 * Currently, it is not possible to continue using the data object if an
100 * attached module throws an exception during data processing; it is only safe
101 * to destroy such data object.
104 * Improve the exception-handling semantics. In most cases, it doesn't make
105 * much sense to continue data processing after one module fails, but having
106 * the alternative would not hurt.
109 * \ingroup module_analysisdata
111 class AbstractAnalysisData
114 virtual ~AbstractAnalysisData();
117 * Whether the data can have multiple points in the same column
120 * \returns \c true if multiple points in the same column are
121 * allowed within a single frame.
123 * This kind of data can appear in many histogramming applications
124 * (e.g., RDFs), where each trajectory frame has several data points
125 * (possibly a different number for each frame). The current interface
126 * doesn't support storing such data, but this should rarely be
129 * The returned value does not change after modules have been notified
132 * Derived classes can change the type by calling setMultipoint()
133 * subject to the above restriction.
134 * If this is not done, the function always returns false.
139 bool isMultipoint() const;
141 * Returns the number of data sets in the data object.
143 * \returns The number of data sets in the data.
145 * If the number is not yet known, returns 0.
146 * The returned value does not change after modules have been notified
147 * of data start, but may change multiple times before that, depending
148 * on the actual data class.
150 * Derived classes should set the number of columns with
151 * setDataSetCount(), within the above limitations.
156 int dataSetCount() const;
158 * Returns the number of columns in a data set.
160 * \param[in] dataSet Zero-based index of the data set to query.
161 * \returns The number of columns in the data.
163 * If the number of columns is not yet known, returns 0.
164 * The returned value does not change after modules have been notified
165 * of data start, but may change multiple times before that, depending
166 * on the actual data class.
168 * Derived classes should set the number of columns with
169 * setColumnCount(), within the above limitations.
174 int columnCount(int dataSet) const;
176 * Returns the number of columns in the data.
178 * \returns The number of columns in the data.
180 * This is a convenience method for data objects with a single data set.
181 * Can only be called if dataSetCount() == 1.
185 * \see columnCount(int)
187 int columnCount() const;
189 * Returns the total number of frames in the data.
191 * \returns The total number of frames in the data.
193 * This function returns the number of frames that the object has
194 * produced. If requestStorage() has been successfully called,
195 * tryGetDataframe() or getDataFrame() can be used to access some or
196 * all of these frames.
200 int frameCount() const;
202 * Access stored data.
204 * \param[in] index Zero-based frame index to access.
205 * \returns Frame reference to frame \p index, or an invalid
206 * reference if no such frame is available.
208 * Does not throw. Failure to access a frame with the given index is
209 * indicated through the return value. Negative \p index is allowed,
210 * and will always result in an invalid reference being returned.
212 * \see requestStorage()
213 * \see getDataFrame()
215 AnalysisDataFrameRef tryGetDataFrame(int index) const;
217 * Access stored data.
219 * \param[in] index Zero-based frame index to access.
220 * \returns Frame reference to frame \p index.
221 * \throws APIError if the requested frame is not accessible.
223 * If the data is not certainly available, use tryGetDataFrame().
225 * \see requestStorage()
226 * \see tryGetDataFrame()
228 AnalysisDataFrameRef getDataFrame(int index) const;
230 * Request storage of frames.
232 * \param[in] nframes Request storing at least \c nframes previous
233 * frames (-1 = request storing all). Must be >= -1.
234 * \returns true if the request could be satisfied.
236 * If called multiple times, the largest request is honored.
238 * Does not throw. Failure to honor the request is indicated through
241 * \see getDataFrame()
242 * \see tryGetDataFrame()
244 bool requestStorage(int nframes);
247 * Adds a module to process the data.
249 * \param module Module to add.
250 * \throws APIError if
251 * - \p module is not compatible with the data object
252 * - data has already been added to the data object and everything
253 * is not available through getDataFrame().
255 * If data has already been added to the module, the new module
256 * immediately processes all existing data. APIError is thrown
257 * if all data is not available through getDataFrame().
259 * The caller can keep a copy of the module pointer if it requires
260 * later access to the module.
262 * If the method throws, the state of the data object is not changed.
263 * The state of the data module is indeterminate.
265 void addModule(AnalysisDataModulePointer module);
267 * Adds a module that processes only a subset of the columns.
269 * \param[in] col First column.
270 * \param[in] span Number of columns.
271 * \param module Module to add.
272 * \throws APIError in same situations as addModule().
274 * Currently, all data sets are filtered using the same column mask.
277 * This method doesn't currently work in all cases with multipoint
278 * data or with multiple data sets. In particular, if the added module
279 * requests storage and uses getDataFrame(), it will behave
280 * unpredictably (most likely asserts).
283 * Generalize this method to multiple data sets (e.g., for adding
284 * modules that only process a single data set).
288 void addColumnModule(int col, int span, AnalysisDataModulePointer module);
290 * Applies a module to process data that is ready.
292 * \param module Module to apply.
293 * \throws APIError in same situations as addModule().
295 * This function works as addModule(), except that it does not keep a
296 * reference to \p module within the data object after it returns.
297 * Also, it can only be called after the data is ready, and only if
298 * getDataFrame() gives access to all of the data.
299 * It is provided for additional flexibility in postprocessing
303 * Currently, this method may not work correctly if \p module requests
304 * storage (addModule() has the same problem if called after data is
307 void applyModule(AnalysisDataModuleInterface *module);
312 * Initializes a new analysis data object.
314 * \throws std::bad_alloc if out of memory.
316 AbstractAnalysisData();
319 * Sets the number of data sets.
321 * \param[in] dataSetCount Number of data sets (must be > 0).
323 * It not called, the data object has a single data set.
324 * Can be called only before notifyDataStart().
325 * Multiple calls are allowed before that point; the last call takes
328 * Does not throw, but this may change with the todo item in
331 * \see dataSetCount()
333 void setDataSetCount(int dataSetCount);
335 * Sets the number of columns for a data set.
337 * \param[in] dataSet Zero-based index of the data set.
338 * \param[in] columnCount Number of columns in \p dataSet (must be > 0).
340 * Must be called at least once before notifyDataStart() for each data
342 * Can be called only before notifyDataStart().
343 * Multiple calls are allowed before that point; the last call takes
346 * Does not throw, but this may change with the below todo item.
349 * Consider whether the call should check the modules that have already
350 * been added (currently it is only done in notifyDataStart()).
354 void setColumnCount(int dataSet, int columnCount);
356 * Sets whether the data has multiple points per column in a frame.
358 * \param[in] multipoint Whether multiple points per column are
361 * If not called, only a single point per column is allowed.
362 * Can be called only before notifyDataStart().
363 * Multiple calls are allowed before that point; the last call takes
366 * Does not throw, but this may change with the todo item in
369 * \see isMultipoint()
371 void setMultipoint(bool multipoint);
374 * Implements access to data frames.
376 * \param[in] index Zero-based frame index to access.
377 * \returns Frame reference to frame \p index, or an invalid
378 * reference if no such frame is available.
380 * Must not throw. Failure to access a frame with the given index is
381 * indicated through the return value.
383 * Code in derived classes can assume that \p index is non-negative and
384 * less than frameCount().
386 * Derived classes can choose to return an invalid reference if
387 * requestStorageInternal() has not been called at all, or if the frame
388 * is too old (compared to the value given to requestStorageInternal()).
390 * This method is called internally by tryGetDataFrame() and
393 * \see AnalysisDataStorage
395 virtual AnalysisDataFrameRef tryGetDataFrameInternal(int index) const = 0;
397 * Implements storage requests.
399 * \param[in] nframes Request storing at least \c nframes previous
400 * frames (-1 = request storing all). Will be either -1 or >0.
401 * \returns true if the request could be satisfied.
403 * Must not throw. Failure to access a frame with the given index is
404 * indicated through the return value.
406 * Derived classes should be prepared for any number of calls to this
407 * method before notifyDataStart() is called (and during that call).
409 * This method is called internally by requestStorage().
411 * \see AnalysisDataStorage
413 virtual bool requestStorageInternal(int nframes) = 0;
416 * Notifies attached modules of the start of data.
418 * \throws APIError if any attached data module is not compatible.
419 * \throws unspecified Any exception thrown by attached data modules
420 * in AnalysisDataModuleInterface::dataStarted().
422 * Should be called once, after data properties have been set with
423 * setColumnCount() and isMultipoint(), and before any of the
424 * notification functions. The derived class should prepare for
425 * requestStorage() calls from the attached modules.
427 void notifyDataStart();
429 * Notifies attached modules of the start of a frame.
431 * \param[in] header Header information for the frame that is starting.
432 * \throws unspecified Any exception thrown by attached data modules
433 * in AnalysisDataModuleInterface::frameStarted().
435 * Should be called once for each frame, before notifyPointsAdd() calls
438 void notifyFrameStart(const AnalysisDataFrameHeader &header) const;
440 * Notifies attached modules of the addition of points to the
443 * \param[in] points Set of points added (also provides access to
445 * \throws APIError if any attached data module is not compatible.
446 * \throws unspecified Any exception thrown by attached data modules
447 * in AnalysisDataModuleInterface::pointsAdded().
449 * Can be called zero or more times for each frame.
450 * The caller should ensure that any column occurs at most once in the
451 * calls, unless the data is multipoint.
452 * For efficiency reasons, calls to this method should be aggregated
453 * whenever possible, i.e., it's better to handle multiple columns or
454 * even the whole frame in a single call rather than calling the method
455 * for each column separately.
457 void notifyPointsAdd(const AnalysisDataPointSetRef &points) const;
459 * Notifies attached modules of the end of a frame.
461 * \param[in] header Header information for the frame that is ending.
462 * \throws unspecified Any exception thrown by attached data modules
463 * in AnalysisDataModuleInterface::frameFinished().
465 * Should be called once for each call of notifyFrameStart(), after any
466 * notifyPointsAdd() calls for the frame.
467 * \p header should be identical to that used in the corresponding
468 * notifyFrameStart() call.
470 void notifyFrameFinish(const AnalysisDataFrameHeader &header);
472 * Notifies attached modules of the end of data.
474 * \throws unspecified Any exception thrown by attached data modules
475 * in AnalysisDataModuleInterface::dataFinished().
477 * Should be called once, after all the other notification calls.
479 void notifyDataFinish() const;
485 PrivateImplPointer<Impl> impl_;
488 * Needed to provide access to notification methods.
490 friend class AnalysisDataStorage;