Merge branch release-4-6
[alexxy/gromacs.git] / src / gromacs / trajectoryanalysis / analysismodule.cpp
1 /*
2  * This file is part of the GROMACS molecular simulation package.
3  *
4  * Copyright (c) 2010,2011,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 analysismodule.h.
38  *
39  * \author Teemu Murtola <teemu.murtola@gmail.com>
40  * \ingroup module_trajectoryanalysis
41  */
42 #include "gromacs/trajectoryanalysis/analysismodule.h"
43
44 #include <utility>
45
46 #include "gromacs/analysisdata/analysisdata.h"
47 #include "gromacs/selection/selection.h"
48 #include "gromacs/utility/exceptions.h"
49 #include "gromacs/utility/gmxassert.h"
50
51 namespace gmx
52 {
53
54 /********************************************************************
55  * TrajectoryAnalysisModule::Impl
56  */
57
58 /*! \internal \brief
59  * Private implementation class for TrajectoryAnalysisModule.
60  *
61  * \ingroup module_trajectoryanalysis
62  */
63 class TrajectoryAnalysisModule::Impl
64 {
65     public:
66         //! Container that associates a data set with its name.
67         typedef std::map<std::string, AbstractAnalysisData *> DatasetContainer;
68         //! Container that associates a AnalysisData object with its name.
69         typedef std::map<std::string, AnalysisData *> AnalysisDatasetContainer;
70
71         //! Initializes analysis module data with given name and description.
72         Impl(const char *name, const char *description)
73             : name_(name), description_(description)
74         {
75         }
76
77         //! Name of the module.
78         std::string                     name_;
79         //! Description of the module.
80         std::string                     description_;
81         //! List of registered data set names.
82         std::vector<std::string>        datasetNames_;
83         /*! \brief
84          * Keeps all registered data sets.
85          *
86          * This container also includes datasets from \a analysisDatasets_.
87          */
88         DatasetContainer                datasets_;
89         //! Keeps registered AnalysisData objects.
90         AnalysisDatasetContainer        analysisDatasets_;
91 };
92
93 /********************************************************************
94  * TrajectoryAnalysisModuleData::Impl
95  */
96
97 /*! \internal \brief
98  * Private implementation class for TrajectoryAnalysisModuleData.
99  *
100  * \ingroup module_trajectoryanalysis
101  */
102 class TrajectoryAnalysisModuleData::Impl
103 {
104     public:
105         //! Container that associates a data handle to its AnalysisData object.
106         typedef std::map<const AnalysisData *, AnalysisDataHandle>
107             HandleContainer;
108
109         //! \copydoc TrajectoryAnalysisModuleData::TrajectoryAnalysisModuleData()
110         Impl(TrajectoryAnalysisModule          *module,
111              const AnalysisDataParallelOptions &opt,
112              const SelectionCollection         &selections);
113
114         //! Checks whether the given AnalysisData has been initialized.
115         bool isInitialized(const AnalysisData &data) const;
116
117         //! Keeps a data handle for each AnalysisData object.
118         HandleContainer            handles_;
119         //! Stores thread-local selections.
120         const SelectionCollection &selections_;
121 };
122
123 TrajectoryAnalysisModuleData::Impl::Impl(
124         TrajectoryAnalysisModule          *module,
125         const AnalysisDataParallelOptions &opt,
126         const SelectionCollection         &selections)
127     : selections_(selections)
128 {
129     TrajectoryAnalysisModule::Impl::AnalysisDatasetContainer::const_iterator i;
130     for (i = module->impl_->analysisDatasets_.begin();
131          i != module->impl_->analysisDatasets_.end(); ++i)
132     {
133         AnalysisDataHandle handle;
134         if (isInitialized(*i->second))
135         {
136             handle = i->second->startData(opt);
137         }
138         handles_.insert(std::make_pair(i->second, handle));
139     }
140 }
141
142 bool TrajectoryAnalysisModuleData::Impl::isInitialized(
143         const AnalysisData &data) const
144 {
145     for (int i = 0; i < data.dataSetCount(); ++i)
146     {
147         if (data.columnCount(i) > 0)
148         {
149             // If not all of the column counts are set, startData() in the
150             // constructor asserts, so that does not need to be checked here.
151             return true;
152         }
153     }
154     return false;
155 }
156
157
158 /********************************************************************
159  * TrajectoryAnalysisModuleData
160  */
161
162 TrajectoryAnalysisModuleData::TrajectoryAnalysisModuleData(
163         TrajectoryAnalysisModule          *module,
164         const AnalysisDataParallelOptions &opt,
165         const SelectionCollection         &selections)
166     : impl_(new Impl(module, opt, selections))
167 {
168 }
169
170
171 TrajectoryAnalysisModuleData::~TrajectoryAnalysisModuleData()
172 {
173 }
174
175
176 void TrajectoryAnalysisModuleData::finishDataHandles()
177 {
178     // FIXME: Call finishData() for all handles even if one throws
179     Impl::HandleContainer::iterator i;
180     for (i = impl_->handles_.begin(); i != impl_->handles_.end(); ++i)
181     {
182         if (i->second.isValid())
183         {
184             i->second.finishData();
185         }
186     }
187     impl_->handles_.clear();
188 }
189
190
191 AnalysisDataHandle
192 TrajectoryAnalysisModuleData::dataHandle(const AnalysisData &data)
193 {
194     Impl::HandleContainer::const_iterator i = impl_->handles_.find(&data);
195     GMX_RELEASE_ASSERT(i != impl_->handles_.end(),
196                        "Data handle requested on unknown dataset");
197     return i->second;
198 }
199
200
201 Selection TrajectoryAnalysisModuleData::parallelSelection(const Selection &selection)
202 {
203     // TODO: Implement properly.
204     return selection;
205 }
206
207
208 SelectionList
209 TrajectoryAnalysisModuleData::parallelSelections(const SelectionList &selections)
210 {
211     // TODO: Consider an implementation that does not allocate memory every time.
212     SelectionList                 newSelections;
213     newSelections.reserve(selections.size());
214     SelectionList::const_iterator i = selections.begin();
215     for (; i != selections.end(); ++i)
216     {
217         newSelections.push_back(parallelSelection(*i));
218     }
219     return newSelections;
220 }
221
222
223 /********************************************************************
224  * TrajectoryAnalysisModuleDataBasic
225  */
226
227 namespace
228 {
229
230 /*! \brief
231  * Basic thread-local trajectory analysis data storage class.
232  *
233  * Most simple tools should only require data handles and selections to be
234  * thread-local, so this class implements just that.
235  *
236  * \ingroup module_trajectoryanalysis
237  */
238 class TrajectoryAnalysisModuleDataBasic : public TrajectoryAnalysisModuleData
239 {
240     public:
241         /*! \brief
242          * Initializes thread-local storage for data handles and selections.
243          *
244          * \param[in] module     Analysis module to use for data objects.
245          * \param[in] opt        Data parallelization options.
246          * \param[in] selections Thread-local selection collection.
247          */
248         TrajectoryAnalysisModuleDataBasic(TrajectoryAnalysisModule          *module,
249                                           const AnalysisDataParallelOptions &opt,
250                                           const SelectionCollection         &selections);
251
252         virtual void finish();
253 };
254
255 TrajectoryAnalysisModuleDataBasic::TrajectoryAnalysisModuleDataBasic(
256         TrajectoryAnalysisModule          *module,
257         const AnalysisDataParallelOptions &opt,
258         const SelectionCollection         &selections)
259     : TrajectoryAnalysisModuleData(module, opt, selections)
260 {
261 }
262
263
264 void
265 TrajectoryAnalysisModuleDataBasic::finish()
266 {
267     finishDataHandles();
268 }
269
270 }   // namespace
271
272
273 /********************************************************************
274  * TrajectoryAnalysisModule
275  */
276
277 TrajectoryAnalysisModule::TrajectoryAnalysisModule(const char *name,
278                                                    const char *description)
279     : impl_(new Impl(name, description))
280 {
281 }
282
283
284 TrajectoryAnalysisModule::~TrajectoryAnalysisModule()
285 {
286 }
287
288
289 void TrajectoryAnalysisModule::optionsFinished(
290         Options                    * /*options*/,
291         TrajectoryAnalysisSettings * /*settings*/)
292 {
293 }
294
295
296 void TrajectoryAnalysisModule::initAfterFirstFrame(const t_trxframe & /*fr*/)
297 {
298 }
299
300
301 TrajectoryAnalysisModuleDataPointer
302 TrajectoryAnalysisModule::startFrames(const AnalysisDataParallelOptions &opt,
303                                       const SelectionCollection         &selections)
304 {
305     return TrajectoryAnalysisModuleDataPointer(
306             new TrajectoryAnalysisModuleDataBasic(this, opt, selections));
307 }
308
309
310 void TrajectoryAnalysisModule::finishFrames(TrajectoryAnalysisModuleData * /*pdata*/)
311 {
312 }
313
314
315 const char *TrajectoryAnalysisModule::name() const
316 {
317     return impl_->name_.c_str();
318 }
319
320
321 const char *TrajectoryAnalysisModule::description() const
322 {
323     return impl_->description_.c_str();
324 }
325
326
327 int TrajectoryAnalysisModule::datasetCount() const
328 {
329     return impl_->datasetNames_.size();
330 }
331
332
333 const std::vector<std::string> &TrajectoryAnalysisModule::datasetNames() const
334 {
335     return impl_->datasetNames_;
336 }
337
338
339 AbstractAnalysisData &TrajectoryAnalysisModule::datasetFromIndex(int index) const
340 {
341     if (index < 0 || index >= datasetCount())
342     {
343         GMX_THROW(APIError("Out of range data set index"));
344     }
345     Impl::DatasetContainer::const_iterator item
346         = impl_->datasets_.find(impl_->datasetNames_[index]);
347     GMX_RELEASE_ASSERT(item != impl_->datasets_.end(),
348                        "Inconsistent data set names");
349     return *item->second;
350 }
351
352
353 AbstractAnalysisData &TrajectoryAnalysisModule::datasetFromName(const char *name) const
354 {
355     Impl::DatasetContainer::const_iterator item = impl_->datasets_.find(name);
356     if (item == impl_->datasets_.end())
357     {
358         GMX_THROW(APIError("Unknown data set name"));
359     }
360     return *item->second;
361 }
362
363
364 void TrajectoryAnalysisModule::registerBasicDataset(AbstractAnalysisData *data,
365                                                     const char           *name)
366 {
367     GMX_RELEASE_ASSERT(data != NULL, "Attempting to register NULL data");
368     // TODO: Strong exception safety should be possible to implement.
369     GMX_RELEASE_ASSERT(impl_->datasets_.find(name) == impl_->datasets_.end(),
370                        "Duplicate data set name registered");
371     impl_->datasets_[name] = data;
372     impl_->datasetNames_.push_back(name);
373 }
374
375
376 void TrajectoryAnalysisModule::registerAnalysisDataset(AnalysisData *data,
377                                                        const char   *name)
378 {
379     // TODO: Strong exception safety should be possible to implement.
380     registerBasicDataset(data, name);
381     impl_->analysisDatasets_[name] = data;
382 }
383
384 } // namespace gmx