votca 2026-dev
Loading...
Searching...
No Matches
md2qmengine.cc
Go to the documentation of this file.
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// Local VOTCA includes
20
21namespace votca {
22namespace xtp {
23
25 std::string molkey = "topology.molecules.molecule";
26 std::vector<tools::Property*> molecules = topology_map.Select(molkey);
27 if (SameValueForMultipleEntries<std::string>(molecules, "mdname")) {
28 throw std::runtime_error("Multiple molecules have same mdname");
29 }
30 std::string segkey = "segments.segment";
31 std::vector<tools::Property*> segments_all;
32 for (tools::Property* mol : molecules) {
33 std::vector<tools::Property*> segments = mol->Select(segkey);
34 if (SameValueForMultipleEntries<std::string>(segments, "name")) {
35 throw std::runtime_error("Multiple segments in molecule:" +
36 mol->get("mdname").as<std::string>() +
37 " have same name");
38 }
39 segments_all.insert(segments_all.end(), segments.begin(), segments.end());
40 for (tools::Property* seg : segments) {
41 std::string fragkey = "fragments.fragment";
42 std::vector<tools::Property*> fragments = seg->Select(fragkey);
43 if (SameValueForMultipleEntries<std::string>(fragments, "name")) {
44 throw std::runtime_error(
45 "Multiple fragments have same name in molecule " +
46 mol->get("mdname").as<std::string>() + " segment " +
47 seg->get("name").as<std::string>());
48 }
49
50 std::vector<std::string> atomnames_seg;
51 for (tools::Property* frag : fragments) {
52 std::vector<std::string> atomnames =
53 frag->get("mdatoms").as<std::vector<std::string>>();
54 atomnames_seg.insert(atomnames_seg.end(), atomnames.begin(),
55 atomnames.end());
56 }
57 std::sort(atomnames_seg.begin(), atomnames_seg.end()); // O(N log N)
58 if (adjacent_find(atomnames_seg.begin(), atomnames_seg.end()) !=
59 atomnames_seg.end()) {
60 throw std::runtime_error(
61 "Multiple mdatoms have same identifier in molecule " +
62 mol->get("mdname").as<std::string>() + " segment " +
63 seg->get("name").as<std::string>());
64 }
65 }
66 }
67 if (SameValueForMultipleEntries<std::string>(segments_all, "name")) {
68 throw std::runtime_error("Multiple segments have same name");
69 }
70}
71
73 const csg::Molecule* mol, const std::vector<Index>& atom_ids_map) const {
74 std::vector<Index> IDs;
75 IDs.reserve(mol->BeadCount());
76 for (const csg::Bead* bead : mol->Beads()) {
77 IDs.push_back(bead->getId());
78 }
79 std::cout << "First loop done..." << std::endl;
80 std::sort(IDs.begin(), IDs.end());
81
82 std::cout << IDs[0] << std::endl;
83 std::cout << atom_ids_map[0] << std::endl;
84
85 Index offset = IDs[0] - atom_ids_map[0];
86 std::cout << offset << std::endl;
87 for (Index i = 1; i < Index(IDs.size()); i++) {
88 if (IDs[i] - atom_ids_map[i] != offset) {
89 throw std::runtime_error(
90 "AtomIds offset could not be determined, either our MD trajectory or "
91 "your mapping file have wrong Atom ids");
92 }
93 }
94 std::cout << offset << std::endl;
95 return offset;
96}
97
98bool Md2QmEngine::CheckMolWhole(const Topology& top, const Segment& seg) const {
99 Eigen::Vector3d CoM = seg.getPos();
100 bool whole = true;
101 for (const Atom& a : seg) {
102 Eigen::Vector3d r = a.getPos() - CoM;
103 Eigen::Vector3d r_pbc = top.PbShortestConnect(CoM, a.getPos());
104 Eigen::Vector3d shift = r_pbc - r;
105 if (shift.norm() > 1e-9) {
106 whole = false;
107 break;
108 }
109 }
110 return whole;
111}
112
114 for (Segment& seg : top.Segments()) {
115 seg.calcPos();
116 while (!CheckMolWhole(top, seg)) {
117 Eigen::Vector3d CoM = seg.getPos();
118 for (Atom& a : seg) {
119 Eigen::Vector3d r = a.getPos() - CoM;
120 Eigen::Vector3d r_pbc = top.PbShortestConnect(CoM, a.getPos());
121 Eigen::Vector3d shift = r_pbc - r;
122 if (shift.norm() > 1e-9) {
123 a.Translate(shift);
124 }
125 }
126 seg.calcPos();
127 }
128 }
129}
130
132
133 tools::Property topology_map;
134 topology_map.LoadFromXML(mapfile_);
135 CheckMappingFile(topology_map);
136 Topology xtptop;
137 xtptop.setStep(top.getStep());
138 xtptop.setTime(top.getTime());
139 xtptop.setBox(top.getBox() * tools::conv::nm2bohr, top.getBoxType());
140
141 // which segmentname does an atom belong to molname atomid
142 std::map<std::string, std::map<Index, std::string>> MolToSegMap;
143
144 // which atomids belong to molname
145 std::map<std::string, std::vector<Index>> MolToAtomIds;
146
147 // names of segments in one molecule;
148 std::map<std::string, std::vector<std::string>> SegsinMol;
149
150 std::string molkey = "topology.molecules.molecule";
151 std::vector<tools::Property*> molecules = topology_map.Select(molkey);
152 std::string segkey = "segments.segment";
153
154 for (tools::Property* mol : molecules) {
155 // get the name of this molecule
156 std::string molname = mol->get("mdname").as<std::string>();
157 // get all segment-mapping info
158 std::vector<tools::Property*> segments = mol->Select(segkey);
159 std::vector<std::string> segnames;
160 std::vector<Index> atomids;
161 // now go through all the defined segments
162 for (tools::Property* seg : segments) {
163 // get the name of this segment and add to segnames vector
164 std::string segname = seg->get("name").as<std::string>();
165 segnames.push_back(segname);
166 if (votca::Log::verbose()) {
167 std::cout << "... ... processing mapping information for segment "
168 << segname << std::endl;
169 }
170 std::string fragkey = "fragments.fragment";
171 // get all fragement mapping info
172 std::vector<tools::Property*> fragments = seg->Select(fragkey);
173 // go over all fragments in this segement
174 for (tools::Property* frag : fragments) {
175 // get all mdatom names from this fragment
176 std::vector<std::string> atomnames =
177 frag->get("mdatoms").as<std::vector<std::string>>();
178 // go over all atoms
179 for (const std::string& atomname : atomnames) {
180 // split atom entry at :
181 tools::Tokenizer tok_atom_name(atomname, ":");
182 std::vector<std::string> entries = tok_atom_name.ToVector();
183 if (entries.size() != 3) {
184 throw std::runtime_error("Atom entry " + atomname +
185 " is not well formatted");
186 }
187 // format should be RESNUM:ATOMNAME:ATOMID we do not care about the
188 // first two
189 Index atomid = 0;
190 try {
191 atomid = std::stoi(entries[2]);
192 } catch (std::invalid_argument& e) {
193 throw std::runtime_error("Atom entry " + atomname +
194 " is not well formatted");
195 }
196 if (votca::Log::verbose()) {
197 std::cout << "... ... processing mapping information for atom "
198 << atomname << " with ID " << atomid << std::endl;
199 }
200 atomids.push_back(atomid);
201 MolToSegMap[molname][atomid] = segname;
202 }
203 }
204 }
205 std::sort(atomids.begin(), atomids.end());
206 MolToAtomIds[molname] = atomids;
207 std::cout << "adding " << segnames[0] << std::endl;
208 SegsinMol[molname] = segnames;
209 }
210
211 // Build a direct, one-time "bead ID -> directly-bonded partner bead
212 // IDs" lookup, from the MD-level topology's own real, actual bond
213 // connectivity (csg::Topology::BondedInteractions(), confirmed
214 // directly, by reading csg's own interaction.h/topology.h, to
215 // already exist and be available exactly here -- this is a purely
216 // *geometric* bond list, from the original MD topology itself, NOT
217 // yet aware of segment/fragment membership at all). Only 2-bead
218 // ("B"/IBond-style) interactions are relevant here -- angles (3-bead)
219 // and dihedrals (4-bead) do not represent a direct bond between two
220 // atoms at all, so BeadCount() != 2 entries are skipped.
221 std::map<Index, std::vector<Index>> bead_bonded_partners;
222 for (csg::Interaction* interaction : top.BondedInteractions()) {
223 if (interaction->BeadCount() != 2) {
224 continue;
225 }
226 Index id1 = interaction->getBeadId(0);
227 Index id2 = interaction->getBeadId(1);
228 bead_bonded_partners[id1].push_back(id2);
229 bead_bonded_partners[id2].push_back(id1);
230 }
231
232 // Real, direct, always-visible (not gated behind -v/verbose at all)
233 // warning, worked through directly with the user: if the underlying
234 // MD topology genuinely has no real bond connectivity at all (e.g.
235 // a topology reader that only ever provides atom positions, no real
236 // bond data at all -- this exact situation is exactly what this
237 // whole session's own real, direct debugging arc started from,
238 // csg::GMXTopologyReader itself never having read any real bonds at
239 // all, before that specific fix), external-bond detection itself
240 // (below) can never find anything at all, meaning
241 // IPodCoupling::EvalJob's own H-saturation at cut segment boundaries
242 // (see transport_theory.rst's own "H-Saturation of Cut Segment
243 // Boundaries" section) can never actually fire at all either --
244 // silently producing dangling valences at every cut segment
245 // boundary instead, with no other, direct signal of this at all
246 // until (if ever) a user separately, manually notices something is
247 // wrong much further downstream. Warning here instead, directly, at
248 // the earliest point this is actually knowable at all (right after
249 // bead_bonded_partners is built, genuinely reflecting the real,
250 // complete state of top.BondedInteractions() itself), gives a real,
251 // direct, upfront signal instead.
252 if (bead_bonded_partners.empty()) {
253 std::cout
254 << "\nWARNING: the MD topology being mapped contains no real bond "
255 "connectivity at all (no bonded interactions were found within "
256 "it) -- this topology reader may only provide atom positions, "
257 "not real, actual bond data. Automatic H-saturation of cut "
258 "segment boundaries (used by e.g. the ipodcoupling calculator) "
259 "will not be able to detect any external bonds at all, and will "
260 "silently do nothing at all, rather than saturating anything -- "
261 "check that the real, actual topology file/reader used here "
262 "genuinely provides real bond data, not just atom positions."
263 << std::endl;
264 }
265
266 // go through all molecules in MD topology
267 for (const csg::Molecule& mol : top.Molecules()) {
268
269 // lookup all segment *names* in this molecule
270 const std::vector<std::string> segnames = SegsinMol[mol.getName()];
271 std::cout << segnames.size() << std::endl;
272 for (auto segment : segnames) {
273 std::cout << segment << std::endl;
274 }
275
276 std::vector<Segment>& topology_segments = xtptop.Segments();
277 // std::cout << mol.getName() << " " << MolToAtomIds[mol.getName()] <<
278 // std::endl;
279 Index IdOffset = DetermineAtomNumOffset(&mol, MolToAtomIds[mol.getName()]);
280
281 if (votca::Log::verbose()) {
282 std::cout << "... Mapping molecule " << mol.getId() << ", name "
283 << mol.getName() << ", # of segments " << segnames.size()
284 << ", atomID offset " << IdOffset << std::endl;
285 }
286
287 for (const std::string& segname : segnames) {
288
289 Index segid = topology_segments.size();
290 // construct a segment
291 Segment this_segment = Segment(segname, segid);
292 this_segment.AddMoleculeId(mol.getId());
293
294 // create atomlist
295 for (const csg::Bead* bead : mol.Beads()) {
296 // check if it belongs to this segment, and add it
297 if (segname == MolToSegMap[mol.getName()][bead->getId() - IdOffset]) {
298 Atom atom(bead->getResnr(), bead->getName(), bead->getId(),
299 bead->getPos() * tools::conv::nm2bohr, bead->getType());
300
301 // Check each of this bead's own, real, direct bonded
302 // partners (from the lookup built above): if a partner's
303 // own segment assignment differs from this atom's own
304 // segname (or the partner has no segment assignment at
305 // all -- e.g. an unmapped, non-charge-transport-relevant
306 // spectator atom), the bond crosses the segment boundary --
307 // record the direction toward it directly on this atom, for
308 // later use (H-saturation) once this direction has also
309 // been carried through SegmentMapper's own, later,
310 // rigid-body MD->QM-template transform. Only the first such
311 // partner found is recorded (an atom with more than one
312 // external bond is rare, and not handled specially here).
313 auto it = bead_bonded_partners.find(bead->getId());
314 if (it != bead_bonded_partners.end()) {
315 for (Index partner_id : it->second) {
316 const csg::Bead* partner_bead = top.getBead(partner_id);
317 // MoleculeByIndex() is not const-qualified (confirmed
318 // directly, from a real compile error), and this
319 // function only ever sees top as const -- so the
320 // partner's own parent molecule is found directly here
321 // instead, by matching getMoleculeId() against each
322 // molecule's own getId() (the same, canonical way this
323 // function itself already identifies molecules, per its
324 // own, pre-existing this_segment.AddMoleculeId(mol.getId())
325 // call above), via the const-compatible Molecules()
326 // overload -- this also sidesteps a second, separate
327 // uncertainty MoleculeByIndex() would have carried:
328 // whether its own index parameter expects a molecule ID
329 // or an array position, which are not necessarily the
330 // same thing.
331 const csg::Molecule* partner_mol = nullptr;
332 for (const csg::Molecule& candidate : top.Molecules()) {
333 if (candidate.getId() == partner_bead->getMoleculeId()) {
334 partner_mol = &candidate;
335 break;
336 }
337 }
338 if (partner_mol == nullptr) {
339 continue;
340 }
341 Index partner_offset = DetermineAtomNumOffset(
342 partner_mol, MolToAtomIds[partner_mol->getName()]);
343 std::string partner_segname =
344 MolToSegMap[partner_mol->getName()]
345 [partner_bead->getId() - partner_offset];
346 if (partner_segname != segname) {
347 Eigen::Vector3d direction =
348 (partner_bead->getPos() - bead->getPos()) *
350 atom.setExternalBondDirection(direction, partner_bead->getId());
351 break;
352 }
353 }
354 }
355
356 // Records ALL of this bead's own, real, direct bonded
357 // partners (not just the ones crossing a segment boundary,
358 // unlike the external-bond-direction detection above, which
359 // deliberately stops at the first one found) -- needed for
360 // FragmentSaturator's own, planned OpenBabel-based
361 // relaxation step, which needs a fragment's own, full,
362 // internal connectivity to set up its own force field
363 // correctly at all (see Atom::getBondedPartnerIds's own
364 // header comment for why). These are RAW, MD-level partner
365 // IDs -- not yet translated into QM-level IDs here, matching
366 // the same "raw here, translated later, in SegmentMapper"
367 // split already used for the external-bond direction.
368 if (it != bead_bonded_partners.end()) {
369 for (Index partner_id : it->second) {
370 atom.AddBondedPartner(partner_id);
371 }
372 }
373
374 this_segment.push_back(atom);
375 }
376 }
377 // add segment to topology
378 topology_segments.push_back(this_segment);
379 }
380 }
381
382 // Second pass: resolves each atom's own external-bond partner
383 // (recorded above only as a raw MD-level ATOM id,
384 // getExternalBondPartnerAtomId() -- deliberately transient, see its
385 // own header comment) to the actual SEGMENT that partner atom
386 // belongs to. Cannot be done inline, in the loop above -- at the
387 // point a given atom's own external bond is first detected, later
388 // segments (in molecule/segname iteration order) do not exist yet
389 // at all, so there is no way yet to know which segment a partner
390 // atom that happens to belong to one of them will end up in.
391 //
392 // First builds a direct "MD-level atom id -> segment id" lookup,
393 // spanning every segment in the whole, now-complete xtptop, then
394 // uses it to resolve every atom's own, already-recorded
395 // getExternalBondPartnerAtomId() into the real, actual, persisted
396 // getExternalBondPartnerSegmentId() -- needed downstream (a planned
397 // linking-segment graph, and the decision of whether a given
398 // external bond is already satisfied within an assembled
399 // supermolecule, worked through directly with the user before this
400 // was implemented) to identify not just THAT a given bond crosses a
401 // segment boundary, but SPECIFICALLY WHICH segment it crosses into.
402 std::map<Index, Index> md_atom_id_to_segment_id;
403 for (const Segment& seg : xtptop.Segments()) {
404 for (const Atom& segatom : seg) {
405 md_atom_id_to_segment_id[segatom.getId()] = seg.getId();
406 }
407 }
408 for (Segment& seg : xtptop.Segments()) {
409 for (Atom& segatom : seg) {
410 if (!segatom.hasExternalBond()) {
411 continue;
412 }
413 auto it =
414 md_atom_id_to_segment_id.find(segatom.getExternalBondPartnerAtomId());
415 if (it != md_atom_id_to_segment_id.end()) {
416 segatom.setExternalBondPartnerSegmentId(it->second);
417 }
418 }
419 }
420
421 MakeSegmentsWholePBC(xtptop);
422
423 return xtptop;
424}
425
426template <class T>
428 const std::vector<tools::Property*>& props, std::string valuetag) const {
429 std::vector<T> entries;
430 for (tools::Property* prop : props) {
431 entries.push_back(prop->get(valuetag).as<T>());
432 }
433 std::sort(entries.begin(), entries.end()); // O(N log N)
434 return adjacent_find(entries.begin(), entries.end()) != entries.end();
435}
436
437} // namespace xtp
438} // namespace votca
virtual const Eigen::Vector3d & getPos() const
Definition basebead.h:166
Index getMoleculeId() const noexcept
Get the id of the molecule the bead is a part of, if the molecule id has not been set return topology...
Definition basebead.h:78
Index getId() const noexcept
Gets the id of the bead.
Definition basebead.h:52
information about a bead
Definition bead.h:50
base class for all interactions
Definition interaction.h:40
information about molecules
Definition molecule.h:45
const std::string & getName() const
get the name of the molecule
Definition molecule.h:51
Index BeadCount() const
get the number of beads in the molecule
Definition molecule.h:65
const std::vector< Bead * > & Beads() const
Definition molecule.h:67
topology of the whole system
Definition topology.h:81
double getTime() const
Definition topology.h:317
BoundaryCondition::eBoxtype getBoxType() const
Definition topology.h:394
const Eigen::Matrix3d & getBox() const
Definition topology.h:298
Index getStep() const
Definition topology.h:329
Bead * getBead(const Index i)
Returns a pointer to the bead with index i.
Definition topology.h:227
MoleculeContainer & Molecules()
Definition topology.h:182
InteractionContainer & BondedInteractions()
Definition topology.h:189
class to manage program options with xml serialization functionality
Definition property.h:55
std::vector< Property * > Select(const std::string &filter)
select property based on a filter
Definition property.cc:185
void LoadFromXML(std::string filename)
Definition property.cc:238
break string into words
Definition tokenizer.h:72
std::vector< T > ToVector()
store all words in a vector of type T, does type conversion.
Definition tokenizer.h:109
void push_back(const T &atom)
const Eigen::Vector3d & getPos() const
void AddBondedPartner(Index partner_id)
Definition atom.h:168
void setExternalBondDirection(const Eigen::Vector3d &dir, Index partner_atom_id)
Definition atom.h:120
void CheckMappingFile(tools::Property &topology_map) const
void MakeSegmentsWholePBC(Topology &top) const
Index DetermineAtomNumOffset(const csg::Molecule *mol, const std::vector< Index > &atom_ids_map) const
bool SameValueForMultipleEntries(const std::vector< tools::Property * > &props, std::string tag) const
Topology map(const csg::Topology &top) const
bool CheckMolWhole(const Topology &top, const Segment &seg) const
void AddMoleculeId(Index id)
Definition segment.h:91
Container for segments and box and atoms.
Definition topology.h:41
Eigen::Vector3d PbShortestConnect(const Eigen::Vector3d &r1, const Eigen::Vector3d &r2) const
Definition topology.cc:134
void setTime(double time)
Definition topology.h:78
void setBox(const Eigen::Matrix3d &box, csg::BoundaryCondition::eBoxtype boxtype=csg::BoundaryCondition::typeAuto)
Definition topology.cc:79
std::vector< Segment > & Segments()
Definition topology.h:58
void setStep(Index step)
Definition topology.h:76
const double nm2bohr
Definition constants.h:47
Charge transport classes.
Definition ERIs.h:28
Provides a means for comparing floating point numbers.
Definition basebead.h:33
Eigen::Index Index
Definition types.h:26
static bool verbose()
Definition globals.h:32