Add nblib backend: Part 1 of 2
[alexxy/gromacs.git] / api / nblib / topology.cpp
1 /*
2  * This file is part of the GROMACS molecular simulation package.
3  *
4  * Copyright (c) 2020, 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 nblib Topology and TopologyBuilder
38  *
39  * \author Victor Holanda <victor.holanda@cscs.ch>
40  * \author Joe Jordan <ejjordan@kth.se>
41  * \author Prashanth Kanduri <kanduri@cscs.ch>
42  * \author Sebastian Keller <keller@cscs.ch>
43  * \author Artem Zhmurov <zhmurov@gmail.com>
44  */
45 #include <numeric>
46
47 #include "gromacs/topology/exclusionblocks.h"
48 #include "gromacs/utility/listoflists.h"
49 #include "gromacs/utility/smalloc.h"
50 #include "nblib/exception.h"
51 #include "nblib/particletype.h"
52 #include "nblib/topology.h"
53 #include "nblib/util/internal.h"
54
55 namespace nblib
56 {
57
58 TopologyBuilder::TopologyBuilder() : numParticles_(0) {}
59
60 gmx::ListOfLists<int> TopologyBuilder::createExclusionsListOfLists() const
61 {
62     const auto& moleculesList = molecules_;
63
64     std::vector<gmx::ExclusionBlock> exclusionBlockGlobal;
65     exclusionBlockGlobal.reserve(numParticles_);
66
67     size_t particleNumberOffset = 0;
68     for (const auto& molNumberTuple : moleculesList)
69     {
70         const Molecule& molecule   = std::get<0>(molNumberTuple);
71         size_t          numMols    = std::get<1>(molNumberTuple);
72         const auto&     exclusions = molecule.getExclusions();
73
74         assert((!exclusions.empty()
75                 && std::string("No exclusions found in the " + molecule.name().value() + " molecule.")
76                            .c_str()));
77
78         std::vector<gmx::ExclusionBlock> exclusionBlockPerMolecule =
79                 detail::toGmxExclusionBlock(exclusions);
80
81         // duplicate the exclusionBlockPerMolecule for the number of Molecules of (numMols)
82         for (size_t i = 0; i < numMols; ++i)
83         {
84             auto offsetExclusions =
85                     detail::offsetGmxBlock(exclusionBlockPerMolecule, particleNumberOffset);
86
87             std::copy(std::begin(offsetExclusions), std::end(offsetExclusions),
88                       std::back_inserter(exclusionBlockGlobal));
89
90             particleNumberOffset += molecule.numParticlesInMolecule();
91         }
92     }
93
94     gmx::ListOfLists<int> exclusionsListOfListsGlobal;
95     for (const auto& block : exclusionBlockGlobal)
96     {
97         exclusionsListOfListsGlobal.pushBack(block.atomNumber);
98     }
99
100     return exclusionsListOfListsGlobal;
101 }
102
103 template<typename T, class Extractor>
104 std::vector<T> TopologyBuilder::extractParticleTypeQuantity(Extractor&& extractor)
105 {
106     auto& moleculesList = molecules_;
107
108     // returned object
109     std::vector<T> ret;
110     ret.reserve(numParticles_);
111
112     for (auto& molNumberTuple : moleculesList)
113     {
114         Molecule& molecule = std::get<0>(molNumberTuple);
115         size_t    numMols  = std::get<1>(molNumberTuple);
116
117         for (size_t i = 0; i < numMols; ++i)
118         {
119             for (auto& particleData : molecule.particleData())
120             {
121                 auto particleTypesMap = molecule.particleTypesMap();
122                 ret.push_back(extractor(particleData, particleTypesMap));
123             }
124         }
125     }
126
127     return ret;
128 }
129
130 Topology TopologyBuilder::buildTopology()
131 {
132     topology_.numParticles_ = numParticles_;
133
134     topology_.exclusions_ = createExclusionsListOfLists();
135     topology_.charges_    = extractParticleTypeQuantity<real>([](const auto& data, auto& map) {
136         ignore_unused(map);
137         return data.charge_;
138     });
139
140     // map unique ParticleTypes to IDs
141     std::unordered_map<std::string, int> nameToId;
142     for (auto& name_particleType_tuple : particleTypes_)
143     {
144         topology_.particleTypes_.push_back(name_particleType_tuple.second);
145         nameToId[name_particleType_tuple.first] = nameToId.size();
146     }
147
148     topology_.particleTypeIdOfAllParticles_ =
149             extractParticleTypeQuantity<int>([&nameToId](const auto& data, auto& map) {
150                 ignore_unused(map);
151                 return nameToId[data.particleTypeName_];
152             });
153
154     detail::ParticleSequencer particleSequencer;
155     particleSequencer.build(molecules_);
156     topology_.particleSequencer_ = std::move(particleSequencer);
157
158     topology_.combinationRule_         = particleTypesInteractions_.getCombinationRule();
159     topology_.nonBondedInteractionMap_ = particleTypesInteractions_.generateTable();
160
161     // Check whether there is any missing term in the particleTypesInteractions compared to the
162     // list of particletypes
163     for (const auto& particleType1 : particleTypes_)
164     {
165         for (const auto& particleType2 : particleTypes_)
166         {
167             auto interactionKey = std::make_tuple(ParticleTypeName(particleType1.first),
168                                                   ParticleTypeName(particleType2.first));
169             if (topology_.nonBondedInteractionMap_.count(interactionKey) == 0)
170             {
171                 std::string message =
172                         formatString("Missing nonbonded interaction parameters for pair {} {}",
173                                      particleType1.first, particleType2.first);
174                 throw InputException(message);
175             }
176         }
177     }
178
179     return topology_;
180 }
181
182 TopologyBuilder& TopologyBuilder::addMolecule(const Molecule& molecule, const int nMolecules)
183 {
184     /*
185      * 1. Push-back a tuple of molecule type and nMolecules
186      * 2. Append exclusion list into the data structure
187      */
188
189     molecules_.emplace_back(molecule, nMolecules);
190     numParticles_ += nMolecules * molecule.numParticlesInMolecule();
191
192     auto particleTypesInMolecule = molecule.particleTypesMap();
193
194     for (const auto& name_type_tuple : particleTypesInMolecule)
195     {
196         // If we already have the particleType, we need to make
197         // sure that the type's parameters are actually the same
198         // otherwise we would overwrite them
199         if (particleTypes_.count(name_type_tuple.first) > 0)
200         {
201             if (!(particleTypes_.at(name_type_tuple.first) == name_type_tuple.second))
202             {
203                 throw InputException("Differing ParticleTypes with identical names encountered");
204             }
205         }
206     }
207
208     // Note: insert does nothing if the key already exists
209     particleTypes_.insert(particleTypesInMolecule.begin(), particleTypesInMolecule.end());
210
211     return *this;
212 }
213
214 void TopologyBuilder::addParticleTypesInteractions(const ParticleTypesInteractions& particleTypesInteractions)
215 {
216     particleTypesInteractions_.merge(particleTypesInteractions);
217 }
218
219 int Topology::numParticles() const
220 {
221     return numParticles_;
222 }
223
224 std::vector<real> Topology::getCharges() const
225 {
226     return charges_;
227 }
228
229 std::vector<ParticleType> Topology::getParticleTypes() const
230 {
231     return particleTypes_;
232 }
233
234 std::vector<int> Topology::getParticleTypeIdOfAllParticles() const
235 {
236     return particleTypeIdOfAllParticles_;
237 }
238
239 int Topology::sequenceID(MoleculeName moleculeName, int moleculeNr, ResidueName residueName, ParticleName particleName) const
240 {
241     return particleSequencer_(moleculeName, moleculeNr, residueName, particleName);
242 }
243
244 NonBondedInteractionMap Topology::getNonBondedInteractionMap() const
245 {
246     return nonBondedInteractionMap_;
247 }
248
249 CombinationRule Topology::getCombinationRule() const
250 {
251     return combinationRule_;
252 }
253
254 gmx::ListOfLists<int> Topology::getGmxExclusions() const
255 {
256     return exclusions_;
257 }
258
259 } // namespace nblib