OpenMD 3.2
Molecular Dynamics in the Open
Loading...
Searching...
No Matches
TetrahedralityHBMatrix.cpp
1/*
2 * Copyright (c) 2004-present, The University of Notre Dame. All rights
3 * reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 *
8 * 1. Redistributions of source code must retain the above copyright notice,
9 * this list of conditions and the following disclaimer.
10 *
11 * 2. Redistributions in binary form must reproduce the above copyright notice,
12 * this list of conditions and the following disclaimer in the documentation
13 * and/or other materials provided with the distribution.
14 *
15 * 3. Neither the name of the copyright holder nor the names of its
16 * contributors may be used to endorse or promote products derived from
17 * this software without specific prior written permission.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
20 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
23 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 * POSSIBILITY OF SUCH DAMAGE.
30 *
31 * SUPPORT OPEN SCIENCE! If you use OpenMD or its source code in your
32 * research, please cite the following paper when you publish your work:
33 *
34 * [1] Drisko et al., J. Open Source Softw. 9, 7004 (2024).
35 *
36 * Good starting points for code and simulation methodology are:
37 *
38 * [2] Meineke, et al., J. Comp. Chem. 26, 252-271 (2005).
39 * [3] Fennell & Gezelter, J. Chem. Phys. 124, 234104 (2006).
40 * [4] Sun, Lin & Gezelter, J. Chem. Phys. 128, 234107 (2008).
41 * [5] Vardeman, Stocker & Gezelter, J. Chem. Theory Comput. 7, 834 (2011).
42 * [6] Kuang & Gezelter, Mol. Phys., 110, 691-701 (2012).
43 * [7] Lamichhane, Gezelter & Newman, J. Chem. Phys. 141, 134109 (2014).
44 * [8] Bhattarai, Newman & Gezelter, Phys. Rev. B 99, 094106 (2019).
45 * [9] Drisko & Gezelter, J. Chem. Theory Comput. 20, 4986-4997 (2024).
46 */
47
48#include "applications/staticProps/TetrahedralityHBMatrix.hpp"
49
50#include <vector>
51
52#include "io/DumpReader.hpp"
54#include "utils/Constants.hpp"
55#include "utils/Revision.hpp"
56#include "utils/simError.h"
57
58namespace OpenMD {
59
60 TetrahedralityHBMatrix::TetrahedralityHBMatrix(
61 SimInfo* info, const std::string& filename, const std::string& sele,
62 double rCut, double OOcut, double thetaCut, double OHcut, int nbins) :
63 StaticAnalyser(info, filename, nbins),
64 selectionScript_(sele), seleMan_(info), evaluator_(info), rCut_(rCut),
65 OOCut_(OOcut), thetaCut_(thetaCut), OHCut_(OHcut) {
66 setAnalysisType("Tetrahedrality HBond Matrix");
67 setOutputName(getPrefix(filename) + ".hbq");
68
69 evaluator_.loadScriptString(sele);
70 if (!evaluator_.isDynamic()) {
71 seleMan_.setSelectionSet(evaluator_.evaluate());
72 }
73
74 Q_histogram_.resize(nBins_);
75
76 for (unsigned int i = 0; i < nBins_; i++) {
77 Q_histogram_[i].resize(nBins_);
78 }
79
80 // Q can take values from 0 to 1
81
82 MinQ_ = 0.0;
83 MaxQ_ = 1.1;
84 deltaQ_ = (MaxQ_ - MinQ_) / nBins_;
85
86 std::stringstream params;
87 params << " rCut = " << rCut_ << ", OOcut = " << OOCut_
88 << ", thetacut = " << thetaCut_ << ", OHcut = " << OHCut_
89 << ", nbins = " << nBins_ << ", deltaQ = " << deltaQ_;
90 const std::string paramString = params.str();
91 setParameterString(paramString);
92 }
93
94 void TetrahedralityHBMatrix::initializeHistogram() {
95 for (unsigned int i = 0; i < nBins_; i++) {
96 std::fill(Q_histogram_[i].begin(), Q_histogram_[i].end(), 0);
97 }
98 }
99
100 void TetrahedralityHBMatrix::process() {
101 Molecule* mol1;
102 Molecule* mol2;
103 Molecule* moli;
104 Molecule* molj;
105 SimInfo::MoleculeIterator mi;
106 Vector3d vec;
107 Vector3d ri, rj, rk, rik, rkj, dposition, tposition;
108 RealType r, cospsi;
109 std::vector<Molecule::HBondDonor*>::iterator hbdi;
110 Molecule::HBondDonor* hbd;
111 std::vector<Atom*>::iterator hbai;
112 Atom* hba;
113 Vector3d dPos;
114 Vector3d aPos;
115 Vector3d hPos;
116 Vector3d DH;
117 Vector3d DA;
118 Vector3d HA;
119 Vector3d uDA;
120 RealType DAdist, DHdist, HAdist, theta, ctheta, Qk, q1, q2;
121 std::vector<std::pair<RealType, Molecule*>> myNeighbors;
122 int myIndex, ii, jj, index1, index2;
123
124 bool usePeriodicBoundaryConditions_ =
125 info_->getSimParams()->getUsePeriodicBoundaryConditions();
126
127 DumpReader reader(info_, dumpFilename_);
128 int nFrames = reader.getNFrames();
129
130 for (int istep = 0; istep < nFrames; istep += step_) {
131 reader.readFrame(istep);
132 currentSnapshot_ = info_->getSnapshotManager()->getCurrentSnapshot();
133
134 Q_.clear();
135 Q_.resize(info_->getNGlobalMolecules(), 0.0);
136
137 if (evaluator_.isDynamic()) {
138 seleMan_.setSelectionSet(evaluator_.evaluate());
139 }
140
141 // outer loop is over the selected Molecules:
142
143 for (mol1 = seleMan_.beginSelectedMolecule(ii); mol1 != NULL;
144 mol1 = seleMan_.nextSelectedMolecule(ii)) {
145 myIndex = mol1->getGlobalIndex();
146
147 Qk = 1.0;
148
149 myNeighbors.clear();
150
151 // inner loop is over all Molecules in the system:
152
153 for (mol2 = info_->beginMolecule(mi); mol2 != NULL;
154 mol2 = info_->nextMolecule(mi)) {
155 if (mol2->getGlobalIndex() != myIndex) {
156 vec = mol1->getCom() - mol2->getCom();
157
158 if (usePeriodicBoundaryConditions_)
159 currentSnapshot_->wrapVector(vec);
160
161 r = vec.length();
162
163 // Check to see if neighbor is in bond cutoff
164
165 if (r < rCut_) { myNeighbors.push_back(std::make_pair(r, mol2)); }
166 }
167 }
168
169 // Sort the vector using predicate and std::sort
170 std::sort(myNeighbors.begin(), myNeighbors.end());
171
172 // Use only the 4 closest neighbors to do the rest of the work:
173 // int nbors = myNeighbors.size()> 4 ? 4 : myNeighbors.size();
174 int nbors = myNeighbors.size();
175 int nang = int(0.5 * (nbors * (nbors - 1)));
176
177 rk = mol1->getCom();
178
179 for (int i = 0; i < nbors - 1; i++) {
180 moli = myNeighbors[i].second;
181 ri = moli->getCom();
182 rik = rk - ri;
183 if (usePeriodicBoundaryConditions_) currentSnapshot_->wrapVector(rik);
184
185 rik.normalize();
186
187 for (int j = i + 1; j < nbors; j++) {
188 molj = myNeighbors[j].second;
189 rj = molj->getCom();
190 rkj = rk - rj;
191 if (usePeriodicBoundaryConditions_)
192 currentSnapshot_->wrapVector(rkj);
193 rkj.normalize();
194
195 cospsi = dot(rik, rkj);
196
197 // Calculates scaled Qk for each molecule using calculated
198 // angles from the actual number of nearest neighbors.
199 Qk = Qk - (pow(cospsi + 1.0 / 3.0, 2) * 9.0 / (4.0 * nang));
200 }
201 }
202
203 if (nang > 0) { Q_[myIndex] = Qk; }
204 }
205 // Now to scan for histogram:
206
207 for (mol1 = seleMan_.beginSelectedMolecule(ii); mol1 != NULL;
208 mol1 = seleMan_.nextSelectedMolecule(ii)) {
209 for (mol2 = seleMan_.beginSelectedMolecule(jj); mol2 != NULL;
210 mol2 = seleMan_.nextSelectedMolecule(jj)) {
211 // loop over the possible donors in molecule 1:
212 for (hbd = mol1->beginHBondDonor(hbdi); hbd != NULL;
213 hbd = mol1->nextHBondDonor(hbdi)) {
214 dPos = hbd->donorAtom->getPos();
215 hPos = hbd->donatedHydrogen->getPos();
216 DH = hPos - dPos;
217 currentSnapshot_->wrapVector(DH);
218 DHdist = DH.length();
219
220 // loop over the possible acceptors in molecule 2:
221 for (hba = mol2->beginHBondAcceptor(hbai); hba != NULL;
222 hba = mol2->nextHBondAcceptor(hbai)) {
223 aPos = hba->getPos();
224 DA = aPos - dPos;
225 currentSnapshot_->wrapVector(DA);
226 DAdist = DA.length();
227
228 // Distance criteria: are the donor and acceptor atoms
229 // close enough?
230 if (DAdist < OOCut_) {
231 HA = aPos - hPos;
232 currentSnapshot_->wrapVector(HA);
233 HAdist = HA.length();
234
235 ctheta = dot(DH, DA) / (DHdist * DAdist);
236 theta = acos(ctheta) * 180.0 / Constants::PI;
237
238 // Angle criteria: are the D-H and D-A and vectors close?
239 if (theta < thetaCut_ && HAdist < OHCut_) {
240 // We have a hydrogen bond!
241 index1 = mol1->getGlobalIndex();
242 index2 = mol2->getGlobalIndex();
243 q1 = Q_[index1];
244 q2 = Q_[index2];
245 collectHistogram(q1, q2);
246 }
247 }
248 }
249 }
250 // now loop over the possible acceptors in molecule 1:
251 for (hba = mol1->beginHBondAcceptor(hbai); hba != NULL;
252 hba = mol1->nextHBondAcceptor(hbai)) {
253 aPos = hba->getPos();
254
255 // loop over the possible donors in molecule 2:
256 for (hbd = mol2->beginHBondDonor(hbdi); hbd != NULL;
257 hbd = mol2->nextHBondDonor(hbdi)) {
258 dPos = hbd->donorAtom->getPos();
259
260 DA = aPos - dPos;
261 currentSnapshot_->wrapVector(DA);
262 DAdist = DA.length();
263
264 // Distance criteria: are the donor and acceptor atoms
265 // close enough?
266 if (DAdist < OOCut_) {
267 hPos = hbd->donatedHydrogen->getPos();
268 HA = aPos - hPos;
269 currentSnapshot_->wrapVector(HA);
270 HAdist = HA.length();
271
272 DH = hPos - dPos;
273 currentSnapshot_->wrapVector(DH);
274 DHdist = DH.length();
275 ctheta = dot(DH, DA) / (DHdist * DAdist);
276 theta = acos(ctheta) * 180.0 / Constants::PI;
277 // Angle criteria: are the D-H and D-A and vectors close?
278 if (theta < thetaCut_ && HAdist < OHCut_) {
279 // We have a hydrogen bond!
280 // keep these indexed by donor then acceptor:
281 index1 = mol2->getGlobalIndex();
282 index2 = mol1->getGlobalIndex();
283 q1 = Q_[index1];
284 q2 = Q_[index2];
285 collectHistogram(q1, q2);
286 }
287 }
288 }
289 }
290 }
291 }
292 }
293 writeOutput();
294 }
295
296 void TetrahedralityHBMatrix::collectHistogram(RealType q1, RealType q2) {
297 if (q1 > MinQ_ && q1 < MaxQ_) {
298 int bin1 = int((q1 - MinQ_) / deltaQ_);
299 if (q2 > MinQ_ && q2 < MaxQ_) {
300 int bin2 = int((q2 - MinQ_) / deltaQ_);
301 Q_histogram_[bin1][bin2] += 1;
302 count_++;
303 } else {
304 // cerr << "q2 = " << q2 << "\n";
305 }
306 } else {
307 // cerr << "q1 = " << q1 << "\n";
308 }
309 }
310
311 void TetrahedralityHBMatrix::writeOutput() {
312 std::ofstream ofs(outputFilename_.c_str());
313 if (ofs.is_open()) {
314 Revision r;
315 ofs << "# " << getAnalysisType() << "\n";
316 ofs << "# OpenMD " << r.getFullRevision() << "\n";
317 ofs << "# " << r.getBuildDate() << "\n";
318 ofs << "# selection script: \"" << selectionScript_ << "\"\n";
319
320 if (!paramString_.empty())
321 ofs << "# parameters: " << paramString_ << "\n";
322
323 ofs << "# Hydrogen Bond count: " << count_ << "\n";
324
325 for (unsigned int i = 0; i < Q_histogram_.size(); ++i) {
326 for (unsigned int j = 0; j < Q_histogram_[i].size(); ++j) {
327 ofs << RealType(Q_histogram_[i][j]) / RealType(count_) << "\t";
328 }
329 ofs << "\n";
330 }
331
332 } else {
333 snprintf(painCave.errMsg, MAX_SIM_ERROR_MSG_LENGTH,
334 "TetrahedralityHBMatrix: unable to open %s\n",
335 outputFilename_.c_str());
336 painCave.isFatal = 1;
337 simError();
338 }
339
340 ofs.close();
341 }
342} // namespace OpenMD
One of the heavy-weight classes of OpenMD, SimInfo maintains objects and variables relating to the cu...
Definition SimInfo.hpp:96
This basic Periodic Table class was originally taken from the data.cpp file in OpenBabel.
Real dot(const DynamicVector< Real > &v1, const DynamicVector< Real > &v2)
Returns the dot product of two DynamicVectors.
std::string getPrefix(const std::string &str)