Loading...
Searching...
No Matches
BondBoost.cpp
Go to the documentation of this file.
1/*
2** This file is part of eOn.
3**
4** SPDX-License-Identifier: BSD-3-Clause
5**
6** Copyright (c) 2010--present, eOn Development Team
7** All rights reserved.
8**
9** Repo:
10** https://github.com/TheochemUI/eOn
11*/
12
13#include "eon/BondBoost.h"
14#include "eon/EonLogger.h"
15#include "eon/HelperFunctions.h"
16
17#include <algorithm>
18#include <cctype>
19#include <cmath>
20#include <ranges>
21#include <stdexcept>
22#include <string>
23#include <unordered_set>
24#include <vector>
25
26namespace eonc {
27
28const char Hyperdynamics::NONE[] = "none";
29const char Hyperdynamics::BOND_BOOST[] = "bond_boost";
30
32 : matter{matt},
33 parameters{params} {
34 if (!matter) {
35 throw std::invalid_argument("BondBoost: null Matter");
36 }
37 nAtoms = matter->numberOfAtoms();
38}
39
40BondBoost::~BondBoost() = default;
41
43 nBBs = 0;
44 nReg = 1;
45
46 const std::string &balString =
47 parameters.hyperdynamics_options().boost_atom_list;
48 std::string lowered = balString;
49 std::ranges::transform(lowered, lowered.begin(), [](unsigned char c) {
50 return static_cast<char>(std::tolower(c));
51 });
52
53 BAList.clear();
54 if (lowered.empty() || lowered == "all") {
55 for (long i = 0; i < nAtoms; ++i) {
56 if (!matter->getFixed(i)) {
57 BAList.push_back(i);
58 }
59 }
60 } else {
61 const auto atoms = eonc::helpers::split_string_int(balString, ",");
62 if (atoms.empty()) {
63 throw std::invalid_argument(
64 "hyperdynamics.boost_atom_list must be 'all' or a comma list of "
65 "CON file-order indices");
66 }
67 std::unordered_set<long> seen;
68 for (int raw : atoms) {
69 const long row = matter->mapFileRow(static_cast<long>(raw));
70 if (row < 0 || row >= nAtoms) {
71 throw std::out_of_range(
72 "hyperdynamics.boost_atom_list index out of range");
73 }
74 if (matter->getFixed(row)) {
75 continue;
76 }
77 if (seen.insert(row).second) {
78 BAList.push_back(row);
79 }
80 }
81 }
82 if (BAList.empty()) {
83 throw std::runtime_error("BondBoost: no boostable atoms");
84 }
85
86 nBAs = static_cast<long>(BAList.size());
87 const std::unordered_set<long> boosted(BAList.begin(), BAList.end());
88 RAList.clear();
89 RAList.reserve(static_cast<size_t>(nAtoms - nBAs));
90 for (long i = 0; i < nAtoms; ++i) {
91 if (boosted.find(i) == boosted.end()) {
92 RAList.push_back(i);
93 }
94 }
95 nRAs = static_cast<long>(RAList.size());
96
97 nTABs = nBAs * (nBAs - 1) / 2 + nBAs * nRAs;
98 TABAList.assign(static_cast<size_t>(2 * std::max(nTABs, 0L)), 0);
99 TABLList.setZero(nTABs, 1);
100 QUILL_LOG_DEBUG(log, "BondBoost: {} boost atoms, {} rest, {} tagged bonds",
101 nBAs, nRAs, nTABs);
102}
103
105 const double dt = parameters.dynamics_options().time_step;
106 if (!(dt > 0.0)) {
107 return 0;
108 }
109 return static_cast<long>(parameters.hyperdynamics_options().rmd_time / dt);
110}
111
113 const long RMDS = rmdSteps();
114 // nReg starts at 1, so a zero sample count never enters the average below
115 // and never divides by RMDS. TABLList would stay the zeros from
116 // initialize(), and BondSelect would keep every tagged pair at length 0.
117 if (RMDS <= 0) {
118 if (nBBs == 0) {
119 TABLList = Rmdsteps();
120 nBBs = BondSelect();
121 }
122 nReg++;
123 return;
124 }
125 if (nReg <= RMDS) {
126 // Equilibration: sample bond lengths once per MD step.
127 Matrix<double, Eigen::Dynamic, 1> TABL_tmp = Rmdsteps();
128 TABLList = TABLList + (1.0 / RMDS) * TABL_tmp;
129 nReg++;
130 if (nReg == RMDS + 1) {
131 nBBs = BondSelect();
132 }
133 return;
134 }
135 if (nReg == RMDS + 1) {
136 nBBs = BondSelect();
137 }
138 nReg++;
139}
140
142 const long RMDS = rmdSteps();
143 if (RMDS > 0 && nReg <= RMDS) {
144 return 0.0;
145 }
146 // Callers that never advance() still select on the first evaluation.
147 // With no equilibration samples that selection has to measure lengths
148 // first; SafeHyper and ParallelReplica advance() before they get here.
149 if (nBBs == 0) {
150 if (RMDS <= 0) {
151 TABLList = Rmdsteps();
152 }
153 nBBs = BondSelect();
154 }
155 Epsr_Q.resize(nBBs);
156 CBBLList.setZero(nBBs, 1);
157 const double biasPot = Booststeps();
158 Epsr_Q.clear();
159 return biasPot;
160}
161
165 const double QRR = parameters.hyperdynamics_options().qrr;
166 const double PRR = parameters.hyperdynamics_options().prr;
167 const double DVMAX = parameters.hyperdynamics_options().dvmax;
168 if (nBBs <= 0 || !(QRR > 0.0)) {
169 return 0.0;
170 }
171 const double nBBsD = static_cast<double>(nBBs);
172
173 AtomMatrix addForces(nBBs, 3);
174 AtomMatrix TADF(nAtoms, 3);
175 addForces.setZero();
176 TADF.setZero();
177
178 // Measure current bond lengths
179 for (long i = 0; i < nBBs; i++) {
180 CBBLList(i, 0) = matter->distance(BBAList[2 * i], BBAList[2 * i + 1]);
181 }
182
183 // Compute strain parameters and find maximum
184 double epsrMax = 0.0;
185 for (long i = 0; i < nBBs; i++) {
186 const double eq = EBBLList(i, 0);
187 if (!(eq > 0.0)) {
188 Epsr_Q[i] = 0.0;
189 continue;
190 }
191 Epsr_Q[i] = (CBBLList(i, 0) - eq) / eq / QRR;
192 if (std::abs(Epsr_Q[i]) >= epsrMax) {
193 epsrMax = std::abs(Epsr_Q[i]);
194 }
195 }
196
197 // Envelope function A(eps_max)
198 double A_eps = (1.0 - epsrMax * epsrMax) * (1.0 - epsrMax * epsrMax) /
199 (1.0 - PRR * PRR * epsrMax * epsrMax);
200
201 // Sum of individual bias potentials
202 double sumV = 0.0;
203 if (epsrMax < 1.0) {
204 for (long i = 0; i < nBBs; i++) {
205 sumV += DVMAX * (1.0 - Epsr_Q[i] * Epsr_Q[i]) / nBBsD;
206 }
207 } else {
208 A_eps = 0.0;
209 }
210
211 double boostFact = A_eps * sumV;
212
213 // Compute bias forces per bond, accumulate on atoms
214 for (long i = 0; i < nBBs; i++) {
215 double dforce = 0.0;
216 double fact1 =
217 2.0 * A_eps * DVMAX * Epsr_Q[i] / QRR / EBBLList(i, 0) / nBBsD;
218
219 if (std::abs(Epsr_Q[i]) < epsrMax) {
220 dforce = fact1;
221 } else {
222 // Bond at maximum strain: additional envelope derivative
223 double fTmp1 = 1.0 - PRR * PRR * Epsr_Q[i] * Epsr_Q[i];
224 double fTmp2 = 1.0 - Epsr_Q[i] * Epsr_Q[i];
225 double fact2 = 2.0 * fTmp2 * Epsr_Q[i] *
226 (2.0 * fTmp1 - PRR * PRR * fTmp2) / QRR / EBBLList(i, 0) /
227 fTmp1 / fTmp1;
228 dforce = fact1 + sumV * fact2;
229 }
230
231 long a1 = BBAList[2 * i];
232 long a2 = BBAList[2 * i + 1];
233 if (!(EBBLList(i, 0) > 0.0)) {
234 continue;
235 }
236 // distance() minimum-images the whole bond. pdistance minimum-images one
237 // Cartesian axis after zeroing the other two, which is not that vector
238 // when the cell is non-orthogonal.
239 AtomMatrix delta(1, 3);
240 delta.row(0) =
241 matter->getPositions().row(a1) - matter->getPositions().row(a2);
242 delta = matter->pbc(delta);
243 const double R = delta.norm();
244 if (!(R > 0.0)) {
245 continue;
246 }
247
248 for (int j = 0; j < 3; j++) {
249 const double rij = delta(0, j);
250 const double fij = rij / R * dforce;
251 addForces(i, j) = fij;
252 TADF(a1, j) += fij;
253 TADF(a2, j) -= fij;
254 }
255 }
256
257 // Apply free-atom mask and set bias forces
258 AtomMatrix biasForces = TADF.array() * matter->getFree().array();
259 matter->setBiasForces(biasForces);
260 return boostFact;
261}
262
264Matrix<double, Eigen::Dynamic, 1> BondBoost::Rmdsteps() {
265 Matrix<double, Eigen::Dynamic, 1> bondLengths(nTABs, 1);
266 long count = 0;
267
268 // Boost-atom pairs
269 for (long i = 0; i < nBAs; i++) {
270 for (long j = i + 1; j < nBAs; j++) {
271 bondLengths(count, 0) = matter->distance(BAList[i], BAList[j]);
272 TABAList[2 * count] = BAList[i];
273 TABAList[2 * count + 1] = BAList[j];
274 count++;
275 }
276 }
277
278 // Boost-atom to rest-atom pairs
279 for (long i = 0; i < nBAs; i++) {
280 for (long j = 0; j < nRAs; j++) {
281 bondLengths(count, 0) = matter->distance(BAList[i], RAList[j]);
282 TABAList[2 * count] = BAList[i];
283 TABAList[2 * count + 1] = RAList[j];
284 count++;
285 }
286 }
287
288 if (count != nTABs) {
289 QUILL_LOG_DEBUG(log, "Total involved bond count does not match expected\n");
290 }
291 return bondLengths;
292}
293
296 const double qCutoff = parameters.hyperdynamics_options().qcut;
297
298 // Count bonds within cutoff
299 long nSelected = 0;
300 for (long i = 0; i < nTABs; i++) {
301 if (TABLList(i, 0) <= qCutoff) {
302 nSelected++;
303 }
304 }
305
306 EBBLList.setZero(nSelected, 1);
307 BBAList.resize(2 * nSelected);
308 long count = 0;
309 for (long i = 0; i < nTABs; i++) {
310 if (TABLList(i, 0) <= qCutoff) {
311 EBBLList(count, 0) = TABLList(i, 0);
312 BBAList[2 * count] = TABAList[2 * i];
313 BBAList[2 * count + 1] = TABAList[2 * i + 1];
314 count++;
315 }
316 }
317 return nSelected;
318}
319
320} // namespace eonc
Eigen::Matrix< double, Eigen::Dynamic, 3, eOnStorageOrder > AtomMatrix
Definition Eigen.h:37
double boost()
Evaluate the current bias potential and write bias forces.
const Parameters & parameters
Reference to a structure outside the scope of the class containing runtime parameters.
Definition BondBoost.h:53
Matter * matter
Pointer to atom object outside the scope of the class.
Definition BondBoost.h:51
long nAtoms
Number of free coordinates.
Definition BondBoost.h:50
double Booststeps()
Compute bias potential and forces for bond-boost hyperdynamics.
long rmdSteps() const
long BondSelect()
Select bonds within cutoff distance for boosting.
~BondBoost()
Destructor.
Matrix< double, Eigen::Dynamic, 1 > EBBLList
Definition BondBoost.h:62
eonc::log::Scoped log
Definition BondBoost.h:69
std::vector< long > BBAList
Definition BondBoost.h:58
Matrix< double, Eigen::Dynamic, 1 > TABLList
Definition BondBoost.h:61
std::vector< double > Epsr_Q
Definition BondBoost.h:59
BondBoost(Matter *matt, const Parameters &params)
Constructor to be used when a structure is minimized.
Definition BondBoost.cpp:31
std::vector< long > TABAList
Definition BondBoost.h:57
std::vector< long > BAList
Definition BondBoost.h:55
Matrix< double, Eigen::Dynamic, 1 > CBBLList
Definition BondBoost.h:63
void advance()
Advance the equilibration / boost schedule by one MD step.
Matrix< double, Eigen::Dynamic, 1 > Rmdsteps()
Measure all tagged-atom bond lengths for the equilibration phase.
std::vector< long > RAList
Definition BondBoost.h:56
static const char BOND_BOOST[]
Definition BondBoost.h:75
static const char NONE[]
Definition BondBoost.h:74
std::vector< int > split_string_int(std::string s, std::string delim)
RAII resource manager for the ARTn C library with global synchronization.