Add replacements for pbc enumerations
[gromacs.git] / src / gromacs / analysisdata / datamodulemanager.cpp
blobc8e34606da7e74be5b641036155978e241852d43
1 /*
2 * This file is part of the GROMACS molecular simulation package.
4 * Copyright (c) 2010,2011,2012,2013,2014,2015,2016,2018, 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.
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.
35 /*! \internal \file
36 * \brief
37 * Implements gmx::AnalysisDataModuleManager.
39 * \author Teemu Murtola <teemu.murtola@gmail.com>
40 * \ingroup module_analysisdata
42 #include "gmxpre.h"
44 #include "datamodulemanager.h"
46 #include <utility>
47 #include <vector>
49 #include "gromacs/analysisdata/abstractdata.h"
50 #include "gromacs/analysisdata/dataframe.h"
51 #include "gromacs/analysisdata/datamodule.h"
52 #include "gromacs/analysisdata/paralleloptions.h"
53 #include "gromacs/utility/exceptions.h"
54 #include "gromacs/utility/gmxassert.h"
56 namespace gmx
59 /********************************************************************
60 * AnalysisDataModuleManager::Impl
63 /*! \internal \brief
64 * Private implementation class for AnalysisDataModuleManager.
66 * \ingroup module_analysisdata
68 class AnalysisDataModuleManager::Impl
70 public:
71 //! Stores information about an attached module.
72 struct ModuleInfo
74 //! Initializes the module information.
75 explicit ModuleInfo(AnalysisDataModulePointer module)
76 : module(std::move(module)), bParallel(false)
80 //! Pointer to the actual module.
81 AnalysisDataModulePointer module;
82 //! Whether the module supports parallel processing.
83 bool bParallel;
86 //! Shorthand for list of modules added to the data.
87 typedef std::vector<ModuleInfo> ModuleList;
89 //! Describes the current state of the notification methods.
90 enum State
92 eNotStarted, //!< Initial state (nothing called).
93 eInData, //!< notifyDataStart() called, no frame in progress.
94 eInFrame, //!< notifyFrameStart() called, but notifyFrameFinish() not.
95 eFinished //!< notifyDataFinish() called.
98 Impl();
100 /*! \brief
101 * Checks whether a module is compatible with a given data property.
103 * \param[in] module Module to check.
104 * \param[in] property Property to check.
105 * \param[in] bSet Value of the property to check against.
106 * \throws APIError if \p module is not compatible with the data.
108 void checkModuleProperty(const IAnalysisDataModule &module,
109 DataProperty property, bool bSet) const;
110 /*! \brief
111 * Checks whether a module is compatible with the data properties.
113 * \param[in] module Module to check.
114 * \throws APIError if \p module is not compatible with the data.
116 * Does not currently check the actual data (e.g., missing values), but
117 * only the dimensionality and other preset properties of the data.
119 void checkModuleProperties(const IAnalysisDataModule &module) const;
121 /*! \brief
122 * Present data already added to the data object to a module.
124 * \param[in] data Data object to read data from.
125 * \param[in] module Module to present the data to.
126 * \throws APIError if \p module is not compatible with the data.
127 * \throws APIError if all data is not available through
128 * getDataFrame().
129 * \throws unspecified Any exception thrown by \p module in its data
130 * notification methods.
132 * Uses getDataFrame() in \p data to access all data in the object, and
133 * calls the notification functions in \p module as if the module had
134 * been registered to the data object when the data was added.
136 void presentData(AbstractAnalysisData *data,
137 IAnalysisDataModule *module);
139 //! List of modules added to the data.
140 ModuleList modules_;
141 //! Properties of the owning data for module checking.
142 bool bDataProperty_[eDataPropertyNR];
143 //! true if all modules support missing data.
144 bool bAllowMissing_;
145 //! true if there are modules that do not support parallel processing.
146 bool bSerialModules_;
147 //! true if there are modules that support parallel processing.
148 bool bParallelModules_;
150 /*! \brief
151 * Current state of the notification methods.
153 * This is used together with \a currIndex_ for sanity checks on the
154 * input data; invalid call sequences trigger asserts.
155 * The state of these variables does not otherwise affect the behavior
156 * of this class; this is the reason they can be changed in const
157 * methods.
159 //! Whether notifyDataStart() has been called.
160 mutable State state_;
161 //! Index of currently active frame or the next frame if not in frame.
162 mutable int currIndex_;
165 AnalysisDataModuleManager::Impl::Impl()
166 : bDataProperty_(), // This must be in sync with how AbstractAnalysisData
167 // is actually initialized.
168 bAllowMissing_(true), bSerialModules_(false), bParallelModules_(false),
169 state_(eNotStarted), currIndex_(0)
173 void
174 AnalysisDataModuleManager::Impl::checkModuleProperty(
175 const IAnalysisDataModule &module,
176 DataProperty property, bool bSet) const
178 bool bOk = true;
179 const int flags = module.flags();
180 switch (property)
182 case eMultipleDataSets:
183 if (bSet && !(flags & IAnalysisDataModule::efAllowMultipleDataSets))
185 bOk = false;
187 break;
188 case eMultipleColumns:
189 if (bSet && !(flags & IAnalysisDataModule::efAllowMulticolumn))
191 bOk = false;
193 break;
194 case eMultipoint:
195 if ((bSet && !(flags & IAnalysisDataModule::efAllowMultipoint))
196 || (!bSet && (flags & IAnalysisDataModule::efOnlyMultipoint)))
198 bOk = false;
200 break;
201 default:
202 GMX_RELEASE_ASSERT(false, "Invalid data property enumeration");
204 if (!bOk)
206 GMX_THROW(APIError("Data module not compatible with data object properties"));
210 void
211 AnalysisDataModuleManager::Impl::checkModuleProperties(
212 const IAnalysisDataModule &module) const
214 for (int i = 0; i < eDataPropertyNR; ++i)
216 checkModuleProperty(module, static_cast<DataProperty>(i), bDataProperty_[i]);
220 void
221 AnalysisDataModuleManager::Impl::presentData(AbstractAnalysisData *data,
222 IAnalysisDataModule *module)
224 if (state_ == eNotStarted)
226 return;
228 GMX_RELEASE_ASSERT(state_ != eInFrame,
229 "Cannot apply a modules in mid-frame");
230 module->dataStarted(data);
231 const bool bCheckMissing = bAllowMissing_
232 && ((module->flags() & IAnalysisDataModule::efAllowMissing) == 0);
233 for (int i = 0; i < data->frameCount(); ++i)
235 AnalysisDataFrameRef frame = data->getDataFrame(i);
236 GMX_RELEASE_ASSERT(frame.isValid(), "Invalid data frame returned");
237 // TODO: Check all frames before doing anything for slightly better
238 // exception behavior.
239 if (bCheckMissing && !frame.allPresent())
241 GMX_THROW(APIError("Missing data not supported by a module"));
243 module->frameStarted(frame.header());
244 for (int j = 0; j < frame.pointSetCount(); ++j)
246 module->pointsAdded(frame.pointSet(j));
248 module->frameFinished(frame.header());
249 module->frameFinishedSerial(frame.header().index());
251 if (state_ == eFinished)
253 module->dataFinished();
257 /********************************************************************
258 * AnalysisDataModuleManager
261 AnalysisDataModuleManager::AnalysisDataModuleManager()
262 : impl_(new Impl())
266 AnalysisDataModuleManager::~AnalysisDataModuleManager()
270 void
271 AnalysisDataModuleManager::dataPropertyAboutToChange(DataProperty property, bool bSet)
273 GMX_RELEASE_ASSERT(impl_->state_ == Impl::eNotStarted,
274 "Cannot change data properties after data has been started");
275 if (impl_->bDataProperty_[property] != bSet)
277 Impl::ModuleList::const_iterator i;
278 for (i = impl_->modules_.begin(); i != impl_->modules_.end(); ++i)
280 impl_->checkModuleProperty(*i->module, property, bSet);
282 impl_->bDataProperty_[property] = bSet;
286 void
287 AnalysisDataModuleManager::addModule(AbstractAnalysisData *data,
288 AnalysisDataModulePointer module)
290 impl_->checkModuleProperties(*module);
291 // TODO: Ensure that the system does not end up in an inconsistent state by
292 // adding a module in mid-data during parallel processing (probably best to
293 // prevent alltogether).
294 GMX_RELEASE_ASSERT(impl_->state_ != Impl::eInFrame,
295 "Cannot add a data module in mid-frame");
296 impl_->presentData(data, module.get());
298 if (!(module->flags() & IAnalysisDataModule::efAllowMissing))
300 impl_->bAllowMissing_ = false;
302 impl_->modules_.emplace_back(module);
305 void
306 AnalysisDataModuleManager::applyModule(AbstractAnalysisData *data,
307 IAnalysisDataModule *module)
309 impl_->checkModuleProperties(*module);
310 GMX_RELEASE_ASSERT(impl_->state_ == Impl::eFinished,
311 "Data module can only be applied to ready data");
312 impl_->presentData(data, module);
316 bool
317 AnalysisDataModuleManager::hasSerialModules() const
319 GMX_ASSERT(impl_->state_ != Impl::eNotStarted,
320 "Module state not accessible before data is started");
321 return impl_->bSerialModules_;
325 void
326 AnalysisDataModuleManager::notifyDataStart(AbstractAnalysisData *data)
328 GMX_RELEASE_ASSERT(impl_->state_ == Impl::eNotStarted,
329 "notifyDataStart() called more than once");
330 for (int d = 0; d < data->dataSetCount(); ++d)
332 GMX_RELEASE_ASSERT(data->columnCount(d) > 0,
333 "Data column count is not set");
335 impl_->state_ = Impl::eInData;
336 impl_->bSerialModules_ = !impl_->modules_.empty();
337 impl_->bParallelModules_ = false;
339 Impl::ModuleList::const_iterator i;
340 for (i = impl_->modules_.begin(); i != impl_->modules_.end(); ++i)
342 // This should not fail, since addModule() and
343 // dataPropertyAboutToChange() already do the checks, but kept here to
344 // catch potential bugs (perhaps it would be best to assert on failure).
345 impl_->checkModuleProperties(*i->module);
346 i->module->dataStarted(data);
351 void
352 AnalysisDataModuleManager::notifyParallelDataStart(
353 AbstractAnalysisData *data,
354 const AnalysisDataParallelOptions &options)
356 GMX_RELEASE_ASSERT(impl_->state_ == Impl::eNotStarted,
357 "notifyDataStart() called more than once");
358 for (int d = 0; d < data->dataSetCount(); ++d)
360 GMX_RELEASE_ASSERT(data->columnCount(d) > 0,
361 "Data column count is not set");
363 impl_->state_ = Impl::eInData;
364 impl_->bSerialModules_ = false;
365 impl_->bParallelModules_ = false;
367 Impl::ModuleList::iterator i;
368 for (i = impl_->modules_.begin(); i != impl_->modules_.end(); ++i)
370 // This should not fail, since addModule() and
371 // dataPropertyAboutToChange() already do the checks, but kept here to
372 // catch potential bugs (perhaps it would be best to assert on failure).
373 impl_->checkModuleProperties(*i->module);
374 i->bParallel = i->module->parallelDataStarted(data, options);
375 if (i->bParallel)
377 impl_->bParallelModules_ = true;
379 else
381 impl_->bSerialModules_ = true;
387 void
388 AnalysisDataModuleManager::notifyFrameStart(const AnalysisDataFrameHeader &header) const
390 GMX_ASSERT(impl_->state_ == Impl::eInData, "Invalid call sequence");
391 GMX_ASSERT(header.index() == impl_->currIndex_, "Out of order frames");
392 impl_->state_ = Impl::eInFrame;
394 if (impl_->bSerialModules_)
396 Impl::ModuleList::const_iterator i;
397 for (i = impl_->modules_.begin(); i != impl_->modules_.end(); ++i)
399 if (!i->bParallel)
401 i->module->frameStarted(header);
407 void
408 AnalysisDataModuleManager::notifyParallelFrameStart(
409 const AnalysisDataFrameHeader &header) const
411 if (impl_->bParallelModules_)
413 Impl::ModuleList::const_iterator i;
414 for (i = impl_->modules_.begin(); i != impl_->modules_.end(); ++i)
416 if (i->bParallel)
418 i->module->frameStarted(header);
425 void
426 AnalysisDataModuleManager::notifyPointsAdd(const AnalysisDataPointSetRef &points) const
428 GMX_ASSERT(impl_->state_ == Impl::eInFrame, "notifyFrameStart() not called");
429 // TODO: Add checks for column spans (requires passing the information
430 // about the column counts from somewhere).
431 //GMX_ASSERT(points.lastColumn() < columnCount(points.dataSetIndex()),
432 // "Invalid columns");
433 GMX_ASSERT(points.frameIndex() == impl_->currIndex_,
434 "Points do not correspond to current frame");
435 if (impl_->bSerialModules_)
437 if (!impl_->bAllowMissing_ && !points.allPresent())
439 GMX_THROW(APIError("Missing data not supported by a module"));
442 Impl::ModuleList::const_iterator i;
443 for (i = impl_->modules_.begin(); i != impl_->modules_.end(); ++i)
445 if (!i->bParallel)
447 i->module->pointsAdded(points);
454 void
455 AnalysisDataModuleManager::notifyParallelPointsAdd(
456 const AnalysisDataPointSetRef &points) const
458 // TODO: Add checks for column spans (requires passing the information
459 // about the column counts from somewhere).
460 //GMX_ASSERT(points.lastColumn() < columnCount(points.dataSetIndex()),
461 // "Invalid columns");
462 if (impl_->bParallelModules_)
464 if (!impl_->bAllowMissing_ && !points.allPresent())
466 GMX_THROW(APIError("Missing data not supported by a module"));
469 Impl::ModuleList::const_iterator i;
470 for (i = impl_->modules_.begin(); i != impl_->modules_.end(); ++i)
472 if (i->bParallel)
474 i->module->pointsAdded(points);
481 void
482 AnalysisDataModuleManager::notifyFrameFinish(const AnalysisDataFrameHeader &header) const
484 GMX_ASSERT(impl_->state_ == Impl::eInFrame, "notifyFrameStart() not called");
485 GMX_ASSERT(header.index() == impl_->currIndex_,
486 "Header does not correspond to current frame");
487 // TODO: Add a check for the frame count in the source data including this
488 // frame.
489 impl_->state_ = Impl::eInData;
490 ++impl_->currIndex_;
492 if (impl_->bSerialModules_)
494 Impl::ModuleList::const_iterator i;
495 for (i = impl_->modules_.begin(); i != impl_->modules_.end(); ++i)
497 if (!i->bParallel)
499 i->module->frameFinished(header);
503 Impl::ModuleList::const_iterator i;
504 for (i = impl_->modules_.begin(); i != impl_->modules_.end(); ++i)
506 i->module->frameFinishedSerial(header.index());
511 void
512 AnalysisDataModuleManager::notifyParallelFrameFinish(
513 const AnalysisDataFrameHeader &header) const
515 if (impl_->bParallelModules_)
517 Impl::ModuleList::const_iterator i;
518 for (i = impl_->modules_.begin(); i != impl_->modules_.end(); ++i)
520 if (i->bParallel)
522 i->module->frameFinished(header);
529 void
530 AnalysisDataModuleManager::notifyDataFinish() const
532 GMX_RELEASE_ASSERT(impl_->state_ == Impl::eInData, "Invalid call sequence");
533 impl_->state_ = Impl::eFinished;
535 Impl::ModuleList::const_iterator i;
536 for (i = impl_->modules_.begin(); i != impl_->modules_.end(); ++i)
538 i->module->dataFinished();
542 } // namespace gmx