votca 2026-dev
Loading...
Searching...
No Matches
topology.cc
Go to the documentation of this file.
1/*
2 * Copyright 2009-2023 The VOTCA Development Team
3 * (http://www.votca.org)
4 *
5 * Licensed under the Apache License, Version 2.0 (the "License")
6 *
7 * You may not use this file except in compliance with the License.
8 * You may obtain a copy of the License at
9 *
10 * http://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing, software
13 * distributed under the License is distributed on an "AS IS" BASIS,
14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 * See the License for the specific language governing permissions and
16 * limitations under the License.
17 *
18 */
19
20// Standard includes
21#include <map>
22#include <queue>
23#include <set>
24
25// Third party includes
26#include <boost/lexical_cast.hpp>
27
28// VOTCA includes
29#include <votca/csg/pdbwriter.h>
30#include <votca/tools/globals.h>
31#include <votca/tools/version.h>
32
33// Local VOTCA includes
34#include "votca/xtp/atom.h"
36#include "votca/xtp/segment.h"
37#include "votca/xtp/topology.h"
38
39namespace votca {
40namespace xtp {
41
43 segments_ = top.segments_;
44 time_ = top.time_;
45 step_ = top.step_;
46 this->setBox(top.getBox());
47 for (const QMPair *pair : top.NBList()) {
48 const Segment &seg1 = segments_[pair->Seg1()->getId()];
49 const Segment &seg2 = segments_[pair->Seg2()->getId()];
50 nblist_.Add(seg1, seg2, pair->R());
51 }
52}
53
55 if (&top != this) {
56 segments_ = top.segments_;
57 time_ = top.time_;
58 step_ = top.step_;
59 this->setBox(top.getBox());
60 nblist_.Cleanup();
61 for (const QMPair *pair : top.NBList()) {
62 const Segment &seg1 = segments_[pair->Seg1()->getId()];
63 const Segment &seg2 = segments_[pair->Seg2()->getId()];
64 nblist_.Add(seg1, seg2, pair->R());
65 }
66 }
67 return *this;
68}
69
70Segment &Topology::AddSegment(std::string segment_name) {
71 Index segment_id = Index(segments_.size());
72 segments_.push_back(Segment(segment_name, segment_id));
73 return segments_.back();
74}
75
76// +++++++++++++++++ //
77// Periodic Boundary //
78// +++++++++++++++++ //
79void Topology::setBox(const Eigen::Matrix3d &box,
81
82 // Determine box type automatically in case boxtype == typeAuto
83 if (boxtype == csg::BoundaryCondition::typeAuto) {
84 boxtype = AutoDetectBoxType(box);
85 }
86
87 if (bc_ != nullptr) {
88 if (Log::verbose()) {
89 std::cout << "Removing periodic box. Creating new... " << std::endl;
90 }
91 }
92 bc_.reset(nullptr);
93 switch (boxtype) {
95 bc_.reset(new csg::TriclinicBox());
96 break;
98 bc_.reset(new csg::OrthorhombicBox());
99 break;
100 default:
101 bc_.reset(new csg::OpenBox());
102 break;
103 }
104
105 bc_->setBox(box);
106}
107
109 const Eigen::Matrix3d &box) {
110
111 // Set box type to OpenBox in case "box" is the zero matrix,
112 // to OrthorhombicBox in case "box" is a diagonal matrix,
113 // or to TriclinicBox otherwise
114
115 if (box.isApproxToConstant(0)) {
116 std::cout << "WARNING: No box vectors specified in trajectory."
117 "Using open-box boundary conditions. "
118 << std::endl;
120 }
121
122 else if ((box - Eigen::Matrix3d(box.diagonal().asDiagonal()))
123 .isApproxToConstant(0)) {
125 }
126
127 else {
129 }
130
132}
133
134Eigen::Vector3d Topology::PbShortestConnect(const Eigen::Vector3d &r1,
135 const Eigen::Vector3d &r2) const {
136 return bc_->BCShortestConnection(r1, r2);
137}
138
140 const Segment &seg2) const {
141 double R2 = std::numeric_limits<double>::max();
142 for (const Atom &atom1 : seg1) {
143 for (const Atom &atom2 : seg2) {
144 double R2_test =
145 PbShortestConnect(atom1.getPos(), atom2.getPos()).squaredNorm();
146 if (R2_test < R2) {
147 R2 = R2_test;
148 }
149 }
150 }
151 return std::sqrt(R2);
152}
153
154std::vector<const Segment *> Topology::FindAllSegmentsOnMolecule(
155 const Segment &seg1, const Segment &seg2) const {
156 const std::vector<Index> &ids1 = seg1.getMoleculeIds();
157 const std::vector<Index> &ids2 = seg2.getMoleculeIds();
158 std::vector<Index> common_elements;
159 std::set_intersection(ids1.begin(), ids1.end(), ids2.begin(), ids2.end(),
160 std::back_inserter(common_elements));
161 std::vector<const Segment *> results;
162 if (common_elements.empty() || common_elements.size() > 1) {
163 return results;
164 }
165 Index molid = common_elements[0];
166
167 for (const Segment &seg : segments_) {
168 if (std::find(seg.getMoleculeIds().begin(), seg.getMoleculeIds().end(),
169 molid) != seg.getMoleculeIds().end()) {
170 results.push_back(&seg);
171 }
172 }
173 return results;
174}
175
176std::vector<const Segment *> Topology::FindLinkingSegments(
177 const Segment &seg1, const Segment &seg2) const {
178 std::vector<const Segment *> results;
179
180 // Real, direct segment-id adjacency graph -- built once, here, from
181 // every atom's own, already-known, real external-bond partner
182 // segment (Atom::getExternalBondPartnerSegmentId(), built and
183 // tested earlier this session). Deliberately built fresh, on every
184 // call, rather than cached anywhere on Topology itself -- this is
185 // not expected to be called often enough (once per genuinely
186 // linker-including pair, not once per ordinary pair at all) for
187 // this to matter, and avoids any risk of a stale cache if segments_
188 // itself is ever mutated after Topology is first constructed.
189 std::map<Index, std::vector<Index>> segment_adjacency;
190 for (const Segment &seg : segments_) {
191 for (const Atom &atom : seg) {
192 if (!atom.hasExternalBond()) {
193 continue;
194 }
195 Index partner_segment_id = atom.getExternalBondPartnerSegmentId();
196 if (partner_segment_id == -1) {
197 continue;
198 }
199 segment_adjacency[seg.getId()].push_back(partner_segment_id);
200 }
201 }
202
203 // Real, direct, standard breadth-first search -- guarantees the
204 // SHORTEST real bond path is found, if any exists at all (not just
205 // any path). predecessor tracks, for every segment id visited, the
206 // segment id it was reached from, to reconstruct the actual path
207 // afterward.
208 std::map<Index, Index> predecessor;
209 std::set<Index> visited;
210 std::queue<Index> to_visit;
211 visited.insert(seg1.getId());
212 to_visit.push(seg1.getId());
213 bool found = false;
214 while (!to_visit.empty() && !found) {
215 Index current = to_visit.front();
216 to_visit.pop();
217 for (Index neighbor : segment_adjacency[current]) {
218 if (visited.count(neighbor) > 0) {
219 continue;
220 }
221 visited.insert(neighbor);
222 predecessor[neighbor] = current;
223 if (neighbor == seg2.getId()) {
224 found = true;
225 break;
226 }
227 to_visit.push(neighbor);
228 }
229 }
230
231 if (!found) {
232 // Either genuinely unconnected (e.g. different molecules
233 // entirely), or connected but not via a real, direct bond path at
234 // all -- either way, matches FindAllSegmentsOnMolecule's own,
235 // established "empty means none" convention.
236 return results;
237 }
238
239 // Reconstruct the real path, walking backward from seg2 to seg1 via
240 // predecessor -- excludes seg2 itself (the starting point of this
241 // walk) and, via the loop condition below, seg1 itself too (the
242 // walk stops once it reaches seg1, without ever pushing it). This
243 // walk itself produces the path in REVERSE order (closest to seg2
244 // first) -- reversed below, so the final result is in genuine
245 // seg1-to-seg2 chain order instead, which the still-open,
246 // planned PBC-correction design (walking the chain one covalent
247 // bond at a time, from seg1 toward seg2) will genuinely need.
248 std::vector<Index> path_segment_ids;
249 Index current = predecessor.at(seg2.getId());
250 while (current != seg1.getId()) {
251 path_segment_ids.push_back(current);
252 current = predecessor.at(current);
253 }
254 std::reverse(path_segment_ids.begin(), path_segment_ids.end());
255
256 for (Index segment_id : path_segment_ids) {
257 results.push_back(&getSegment(segment_id));
258 }
259 return results;
260}
261
262void Topology::WriteToPdb(std::string filename) const {
263
264 csg::PDBWriter writer;
265 writer.Open(filename, false);
266 writer.WriteHeader("Frame:" + std::to_string(this->getStep()));
267 writer.WriteBox(this->getBox() * tools::conv::bohr2ang);
268 for (const Segment &seg : segments_) {
269 writer.WriteContainer(seg);
270 }
271 writer.Close();
272}
273
275 w(votca::tools::ToolsVersionStr(), "XTPVersion");
276 w(topology_version(), "version");
277 w(time_, "time");
278 w(step_, "step");
279 w(this->getBox(), "box");
280 CheckpointWriter ww = w.openChild("segments");
281 for (const Segment &seg : segments_) {
282 CheckpointWriter u = ww.openChild("segment" + std::to_string(seg.getId()));
283 seg.WriteToCpt(u);
284 }
285 CheckpointWriter www = w.openChild("neighborlist");
286 nblist_.WriteToCpt(www);
287}
288
290 r(time_, "time");
291 r(step_, "step");
292 Eigen::Matrix3d box;
293 r(box, "box");
294 setBox(box);
295 CheckpointReader v = r.openChild("segments");
296 segments_.clear();
297 Index count = v.getNumDataSets();
298 segments_.reserve(count);
299 for (Index i = 0; i < count; i++) {
300 CheckpointReader w = v.openChild("segment" + std::to_string(i));
301 segments_.push_back(Segment(w));
302 }
303 CheckpointReader rr = r.openChild("neighborlist");
304 nblist_.ReadFromCpt(rr, segments_);
305}
306
307} // namespace xtp
308} // namespace votca
void WriteHeader(std::string header)
Definition pdbwriter.cc:41
void WriteBox(const Eigen::Matrix3d &box)
Definition pdbwriter.cc:62
void Close() override
Definition pdbwriter.cc:51
void WriteContainer(T &container)
Definition pdbwriter.h:102
void Open(std::string file, bool bAppend=false) override
Definition pdbwriter.cc:33
CheckpointReader openChild(const std::string &childName) const
CheckpointWriter openChild(const std::string &childName) const
const std::vector< Index > & getMoleculeIds() const
Definition segment.h:93
void WriteToCpt(CheckpointWriter &w) const
Definition topology.cc:274
Index getStep() const
Definition topology.h:75
std::unique_ptr< csg::BoundaryCondition > bc_
Definition topology.h:122
Segment & AddSegment(std::string segment_name)
Definition topology.cc:70
std::vector< Segment > segments_
Definition topology.h:120
Eigen::Vector3d PbShortestConnect(const Eigen::Vector3d &r1, const Eigen::Vector3d &r2) const
Definition topology.cc:134
void setBox(const Eigen::Matrix3d &box, csg::BoundaryCondition::eBoxtype boxtype=csg::BoundaryCondition::typeAuto)
Definition topology.cc:79
std::vector< const Segment * > FindAllSegmentsOnMolecule(const Segment &seg1, const Segment &seg2) const
Definition topology.cc:154
csg::BoundaryCondition::eBoxtype AutoDetectBoxType(const Eigen::Matrix3d &box)
Definition topology.cc:108
const Eigen::Matrix3d & getBox() const
Definition topology.h:64
std::vector< const Segment * > FindLinkingSegments(const Segment &seg1, const Segment &seg2) const
Definition topology.cc:176
void ReadFromCpt(CheckpointReader &r)
Definition topology.cc:289
void WriteToPdb(std::string filename) const
Definition topology.cc:262
QMNBList & NBList()
Definition topology.h:70
Topology & operator=(const Topology &top)
Definition topology.cc:54
double GetShortestDist(const Segment &seg1, const Segment &seg2) const
Definition topology.cc:139
Segment & getSegment(Index id)
Definition topology.h:55
static constexpr int topology_version()
Definition topology.h:131
const double bohr2ang
Definition constants.h:49
const std::string & ToolsVersionStr()
Definition version.cc:33
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