1 /*
2  * Copyright 2009-2021 The VOTCA Development Team (http://www.votca.org)
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  *
16  */
17 
18 // Standard includes
19 #include <cassert>
20 #include <memory>
21 #include <regex>
22 #include <stdexcept>
23 #include <unordered_set>
24 
25 // Third party includes
26 #include <boost/lexical_cast.hpp>
27 
28 // VOTCA includes
29 #include <votca/tools/rangeparser.h>
30 
31 // Local VOTCA includes
32 #include "votca/csg/boundarycondition.h"
33 #include "votca/csg/interaction.h"
34 #include "votca/csg/molecule.h"
35 #include "votca/csg/openbox.h"
36 #include "votca/csg/topology.h"
37 
38 namespace votca {
39 namespace csg {
40 
41 using namespace std;
42 
is_digits(const std::string & str)43 bool is_digits(const std::string &str) {
44   return str.find_first_not_of("0123456789") == std::string::npos;
45 }
46 
~Topology()47 Topology::~Topology() { Cleanup(); }
48 
Cleanup()49 void Topology::Cleanup() {
50   // cleanup beads
51   beads_.clear();
52 
53   // cleanup molecules
54   molecules_.clear();
55 
56   // cleanup residues
57   residues_.clear();
58 
59   // cleanup interactions
60   {
61     for (InteractionContainer::iterator i = interactions_.begin();
62          i < interactions_.end(); ++i) {
63       delete (*i);
64     }
65     interactions_.clear();
66   }
67   // cleanup  bc_ object
68   bc_ = std::make_unique<OpenBox>();
69 }
70 
71 /// \todo implement checking, only used in xml topology reader
CreateMoleculesByRange(string name,Index first,Index nbeads,Index nmolecules)72 void Topology::CreateMoleculesByRange(string name, Index first, Index nbeads,
73                                       Index nmolecules) {
74   Molecule *mol = CreateMolecule(name);
75   Index beadcount = 0;
76   Index res_offset = 0;
77 
78   for (auto &bead_ : beads_) {
79     // xml numbering starts with 1
80     if (--first > 0) {
81       continue;
82     }
83     // This is not 100% correct, but let's assume for now that the resnr do
84     // increase
85     if (beadcount == 0) {
86       res_offset = bead_.getResnr();
87     }
88     stringstream bname;
89     bname << bead_.getResnr() - res_offset + 1 << ":"
90           << getResidue(bead_.getResnr()).getName() << ":" << bead_.getName();
91     mol->AddBead(&bead_, bname.str());
92     if (++beadcount == nbeads) {
93       if (--nmolecules <= 0) {
94         break;
95       }
96       mol = CreateMolecule(name);
97       beadcount = 0;
98     }
99   }
100 }
101 
CopyTopologyData(Topology * top)102 void Topology::CopyTopologyData(Topology *top) {
103 
104   bc_->setBox(top->getBox());
105   time_ = top->time_;
106   step_ = top->step_;
107 
108   // cleanup old data
109   Cleanup();
110 
111   // copy all residues
112   for (const auto &residue_ : top->residues_) {
113     CreateResidue(residue_.getName());
114   }
115 
116   // create all beads
117   for (const auto &bi : top->beads_) {
118     string type = bi.getType();
119     CreateBead(bi.getSymmetry(), bi.getName(), type, bi.getResnr(),
120                bi.getMass(), bi.getQ());
121   }
122 
123   // copy all molecules
124   for (const auto &molecule_ : top->molecules_) {
125     Molecule *mi = CreateMolecule(molecule_.getName());
126     for (Index i = 0; i < molecule_.BeadCount(); i++) {
127       Index beadid = molecule_.getBead(i)->getId();
128       mi->AddBead(&beads_[beadid], molecule_.getBeadName(i));
129     }
130   }
131 }
132 
getBeadTypeId(string type) const133 Index Topology::getBeadTypeId(string type) const {
134   assert(beadtypes_.count(type));
135   return beadtypes_.at(type);
136 }
137 
RenameMolecules(string range,string name)138 void Topology::RenameMolecules(string range, string name) {
139   tools::RangeParser rp;
140   rp.Parse(range);
141   for (Index i : rp) {
142     if (i > Index(molecules_.size())) {
143       throw runtime_error(
144           string("RenameMolecules: num molecules smaller than"));
145     }
146     getMolecule(i - 1)->setName(name);
147   }
148 }
149 
RenameBeadType(string name,string newname)150 void Topology::RenameBeadType(string name, string newname) {
151 
152   for (auto &bead : beads_) {
153     string type = bead.getType();
154     if (tools::wildcmp(name, type)) {
155       bead.setType(newname);
156     }
157   }
158 }
159 
SetBeadTypeMass(string name,double value)160 void Topology::SetBeadTypeMass(string name, double value) {
161   for (auto &bead : beads_) {
162     string type = bead.getType();
163     if (tools::wildcmp(name, type)) {
164       bead.setMass(value);
165     }
166   }
167 }
168 
CheckMoleculeNaming(void)169 void Topology::CheckMoleculeNaming(void) {
170   map<string, Index> nbeads;
171 
172   for (const auto &mol : molecules_) {
173     map<string, Index>::iterator entry = nbeads.find(mol.getName());
174     if (entry != nbeads.end()) {
175       if (entry->second != mol.BeadCount()) {
176         throw runtime_error(
177             "There are molecules which have the same name but different number "
178             "of bead "
179             "please check the section manual topology handling in the votca "
180             "manual");
181       }
182       continue;
183     }
184     nbeads[mol.getName()] = mol.BeadCount();
185   }
186 }
187 
AddBondedInteraction(Interaction * ic)188 void Topology::AddBondedInteraction(Interaction *ic) {
189   map<string, Index>::iterator iter = interaction_groups_.find(ic->getGroup());
190   if (iter != interaction_groups_.end()) {
191     ic->setGroupId(iter->second);
192   } else {
193     Index i = interaction_groups_.size();
194     interaction_groups_[ic->getGroup()] = i;
195     ic->setGroupId(i);
196   }
197   interactions_.push_back(ic);
198   interactions_by_group_[ic->getGroup()].push_back(ic);
199 }
200 
InteractionsInGroup(const string & group)201 std::vector<Interaction *> Topology::InteractionsInGroup(const string &group) {
202   map<string, vector<Interaction *>>::iterator iter =
203       interactions_by_group_.find(group);
204   if (iter == interactions_by_group_.end()) {
205     return vector<Interaction *>();
206   }
207   return iter->second;
208 }
209 
BeadTypeExist(string type) const210 bool Topology::BeadTypeExist(string type) const {
211   return beadtypes_.count(type);
212 }
213 
RegisterBeadType(string type)214 void Topology::RegisterBeadType(string type) {
215   unordered_set<Index> ids;
216   for (pair<const string, Index> type_and_id : beadtypes_) {
217     ids.insert(type_and_id.second);
218   }
219 
220   Index id = 0;
221   // If the type is also a number use it as the id as well provided it is not
222   // already taken
223   if (is_digits(type)) {
224     id = boost::lexical_cast<Index>(type);
225     assert(!ids.count(id) &&
226            "The type passed in is a number and has already"
227            " been registered. It is likely that you are passing in numbers as "
228            "bead types as well as strings, choose one or the other do not mix "
229            "between using numbers and strings ");
230   }
231 
232   while (ids.count(id)) {
233     ++id;
234   }
235   beadtypes_[type] = id;
236 }
237 
BCShortestConnection(const Eigen::Vector3d & r_i,const Eigen::Vector3d & r_j) const238 Eigen::Vector3d Topology::BCShortestConnection(
239     const Eigen::Vector3d &r_i, const Eigen::Vector3d &r_j) const {
240   return bc_->BCShortestConnection(r_i, r_j);
241 }
242 
getDist(Index bead1,Index bead2) const243 Eigen::Vector3d Topology::getDist(Index bead1, Index bead2) const {
244   return BCShortestConnection(getBead(bead1)->getPos(),
245                               getBead(bead2)->getPos());
246 }
247 
BoxVolume() const248 double Topology::BoxVolume() const { return bc_->BoxVolume(); }
249 
RebuildExclusions()250 void Topology::RebuildExclusions() { exclusions_.CreateExclusions(this); }
251 
autoDetectBoxType(const Eigen::Matrix3d & box) const252 BoundaryCondition::eBoxtype Topology::autoDetectBoxType(
253     const Eigen::Matrix3d &box) const {
254   // set the box type to OpenBox in case "box" is the zero matrix,
255   // to OrthorhombicBox in case "box" is a diagonal matrix,
256   // or to TriclinicBox otherwise
257   if (box.isApproxToConstant(0)) {
258     return BoundaryCondition::typeOpen;
259   } else if ((box - Eigen::Matrix3d(box.diagonal().asDiagonal()))
260                  .isApproxToConstant(0)) {
261     return BoundaryCondition::typeOrthorhombic;
262   } else {
263     return BoundaryCondition::typeTriclinic;
264   }
265   return BoundaryCondition::typeOpen;
266 }
267 
ShortestBoxSize() const268 double Topology::ShortestBoxSize() const {
269   return bc_->getShortestBoxDimension();
270 }
271 
272 }  // namespace csg
273 }  // namespace votca
274