Loading...
Searching...
No Matches
Prefactor.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#include "eon/Prefactor.h"
13#include "eon/HelperFunctions.h"
14#include "eon/Hessian.h"
15
16#include "eon/EonLogger.h"
17#include <algorithm>
18#include <cmath>
19#include <format>
20#include <fstream>
21
23 Matter *saddle, Matter *min2, double &pref1,
24 double &pref2) {
25 VectorXd min1Freqs, saddleFreqs, min2Freqs;
26
27 // determine which atoms moved in the process
28 VectorXi atoms;
29
30 if (parameters.prefactor_options.filter_scheme ==
32 atoms = movedAtomsPct(parameters, min1, saddle, min2);
33 } else {
34 atoms = movedAtoms(parameters, min1, saddle, min2);
35 }
36
37 assert(atoms.rows() > 0);
38
39 // calculate min1 frequencies
40 Hessian hessian(parameters, min1);
41 min1Freqs = hessian.getFreqs(min1, atoms);
42 if (min1Freqs.size() == 0) {
43 EONC_LOG_ERROR("[Prefactor] Bad hessian: min1");
44 return -1;
45 }
46 // remove zero modes
48 min1Freqs = hessian.removeZeroFreqs(min1Freqs);
49 }
50
51 // calculate saddle frequencies
52 saddleFreqs = hessian.getFreqs(saddle, atoms);
53 if (saddleFreqs.size() == 0) {
54 EONC_LOG_ERROR("[Prefactor] Bad hessian: saddle");
55 return -1;
56 }
57 // remove zero modes
59 saddleFreqs = hessian.removeZeroFreqs(saddleFreqs);
60 }
61
62 // calculate min2 frequencies
63 min2Freqs = hessian.getFreqs(min2, atoms);
64 if (min2Freqs.size() == 0) {
65 if (!parameters.main_options.quiet) {
66 EONC_LOG_ERROR("[Prefactor] Bad hessian: min2");
67 }
68 return -1;
69 }
70 // remove zero modes
72 min2Freqs = hessian.removeZeroFreqs(min2Freqs);
73 }
74
75 // check Hessian sizes
76 // BUG FIX: original checked min1 vs saddle twice, never min2
77 if ((min1Freqs.size() != saddleFreqs.size()) ||
78 (min2Freqs.size() != saddleFreqs.size())) {
79 if (!parameters.main_options.quiet) {
80 EONC_LOG_ERROR("[Prefactor] Bad prefactor: Hessian sizes do not match");
81 }
82 return -1;
83 }
84
85 logFreqs(min1Freqs, "minimum 1");
86 logFreqs(saddleFreqs, "saddle");
87 logFreqs(min2Freqs, "minimum 2");
88
89 // check for correct number of negative modes
90 // Bound each loop by its own vector: removeZeroFreqs returns a shorter
91 // vector than the 3N it was handed, and each of the three is shrunk by a
92 // separate call.
93 int numNegFreq = 0;
94 for (int i = 0; i < min1Freqs.size(); i++) {
95 if (min1Freqs(i) < 0) {
96 EONC_LOG_DEBUG("[Prefactor] min1 had negative mode of {}", min1Freqs(i));
97 numNegFreq++;
98 }
99 }
100 if (numNegFreq != 0) {
101 EONC_LOG_DEBUG("[Prefactor] Error: {} negative modes at min1", numNegFreq);
102 return -1;
103 }
104
105 numNegFreq = 0;
106 for (int i = 0; i < saddleFreqs.size(); i++) {
107 if (saddleFreqs(i) < 0) {
108 numNegFreq++;
109 }
110 }
111 if (numNegFreq != 1) {
112 EONC_LOG_DEBUG("Error: {} negative modes at saddle", numNegFreq);
113 return -1;
114 }
115
116 numNegFreq = 0;
117 for (int i = 0; i < min2Freqs.size(); i++) {
118 if (min2Freqs(i) < 0) {
119 numNegFreq++;
120 }
121 }
122 if (numNegFreq != 0) {
123 EONC_LOG_DEBUG("Error: {} negative modes at min2", numNegFreq);
124 return -1;
125 }
126
127 // calculate the prefactors
128 pref1 = 1.0;
129 pref2 = 1.0;
130
132
133 // products are calculated this way in order to avoid overflow
134 for (int i = 0; i < saddleFreqs.size(); i++) {
135 pref1 *= min1Freqs[i];
136 pref2 *= min2Freqs[i];
137 if (saddleFreqs[i] > 0) {
138 pref1 /= saddleFreqs[i];
139 pref2 /= saddleFreqs[i];
140 }
141 }
142 pref1 = std::sqrt(pref1) / (2 * eonc::helpers::pi * 10.18e-15);
143 pref2 = std::sqrt(pref2) / (2 * eonc::helpers::pi * 10.18e-15);
144 } else if (parameters.prefactor_options.rate ==
146 double kB_T = parameters.main_options.temperature * 8.617332e-5; // eV
147 double h_bar = 6.582119e-16; // eV*s
148 double h = 4.135667e-15; // eV*s
149 double temp = (h_bar / (2.0 * kB_T));
150
151 for (int i = 0; i < min1Freqs.size(); i++) {
152 pref1 = pref1 * (std::sinh(temp * (std::sqrt(min1Freqs[i]) / 10.18e-15)));
153 pref2 = pref2 * (std::sinh(temp * (std::sqrt(min2Freqs[i]) / 10.18e-15)));
154
155 if (saddleFreqs[i] > 0) {
156 pref1 =
157 pref1 / (std::sinh(temp * (std::sqrt(saddleFreqs[i]) / 10.18e-15)));
158 pref2 =
159 pref2 / (std::sinh(temp * (std::sqrt(saddleFreqs[i]) / 10.18e-15)));
160 }
161 }
162 pref1 = 2. * kB_T / (h)*pref1;
163 pref2 = 2. * kB_T / (h)*pref2;
164 }
165 EONC_LOG_DEBUG("[Prefactor] reactant to product prefactor: {:.3e}", pref1);
166 EONC_LOG_DEBUG("[Prefactor] product to reactant prefactor: {:.3e}", pref2);
167 return 0;
168}
169
170void eonc::Prefactor::logFreqs(const VectorXd &freqs, const char *name) {
171 std::ofstream outFile("freqs.dat", std::ios::app);
172 if (!outFile.is_open())
173 return;
174
175 outFile << std::format("[Prefactor] Frequencies at {}\n", name);
176 for (int i = 0; i < freqs.size(); i++) {
177 outFile << "\n" << std::format("{:10.6f} ", freqs(i));
178 if ((i + 1) % 5 == 0) {
179 outFile << "\n";
180 }
181 }
182 outFile << "\n";
183}
184
185VectorXi eonc::Prefactor::movedAtoms(const Parameters &parameters, Matter *min1,
186 Matter *saddle, Matter *min2) {
187 long nAtoms = saddle->numberOfAtoms();
188
189 VectorXi moved(nAtoms);
190 moved.setConstant(-1);
191
192 AtomMatrix diffMin1 =
193 saddle->pbc(saddle->getPositions() - min1->getPositions());
194 AtomMatrix diffMin2 =
195 saddle->pbc(saddle->getPositions() - min2->getPositions());
196
197 diffMin1.array() *= saddle->getFree().array();
198 diffMin2.array() *= saddle->getFree().array();
199
200 int nMoved = 0;
201 for (int i = 0; i < nAtoms; i++) {
202 if ((diffMin1.row(i).norm() >
204 (diffMin2.row(i).norm() >
206 // Avoid Eigen Array == scalar in boolean context (C++20 / Eigen 3.3
207 // manylinux toolchains reject Array comparison as bool).
208 if (std::find(moved.data(), moved.data() + nMoved, i) ==
209 moved.data() + nMoved) {
210 moved[nMoved] = i;
211 nMoved++;
212 }
213 for (int j = 0; j < nAtoms; j++) {
214 double diffRSaddle = saddle->distance(i, j);
215
216 if (diffRSaddle < parameters.prefactor_options.within_radius &&
217 (!saddle->getFixed(j))) {
218 if (std::find(moved.data(), moved.data() + nMoved, j) ==
219 moved.data() + nMoved) {
220 moved[nMoved] = j;
221 nMoved++;
222 }
223 }
224 }
225 }
226 }
227 return moved.head(nMoved);
228}
229
231 Matter *min1, Matter *saddle,
232 Matter *min2) {
233 long nAtoms = saddle->numberOfAtoms();
234 long nFree = saddle->numberOfFreeAtoms();
235
236 VectorXi moved(nAtoms);
237 moved.setConstant(-1);
238
239 AtomMatrix diffMin1 =
240 saddle->pbc(saddle->getPositions() - min1->getPositions());
241 AtomMatrix diffMin2 =
242 saddle->pbc(saddle->getPositions() - min2->getPositions());
243
244 diffMin1.array() *= saddle->getFree().array();
245 diffMin2.array() *= saddle->getFree().array();
246
247 VectorXd diff(nAtoms);
248 diff.setConstant(0.0);
249
250 QUILL_LOG_DEBUG(
252 "[Prefactor] including all atoms that make up {:.3f}% of the motion",
253 parameters.prefactor_options.filter_fraction * 100);
254 double sum = 0.0;
255 int mini = 0;
256 for (int i = 0; i < nAtoms; i++) {
257 diff[i] = std::max(diffMin1.row(i).norm(), diffMin2.row(i).norm());
258 sum += diff[i];
259 if (diff[i] <= diff[mini]) {
260 mini = i;
261 }
262 }
263
264 EONC_LOG_DEBUG("[Prefactor] sum of atom distances moved {:.4f}", sum);
265 EONC_LOG_DEBUG("[Prefactor] max moved atom distance: {:.4f}",
266 diff.maxCoeff());
267
268 int nMoved = 0;
269 double d = 0.0;
270 while (d / sum <= parameters.prefactor_options.filter_fraction &&
271 nMoved < nFree) {
272 int maxi = mini;
273 for (int i = 0; i < nAtoms; i++) {
274 if (diff[i] >= diff[maxi]) {
275 if (std::find(moved.data(), moved.data() + nMoved, i) ==
276 moved.data() + nMoved) {
277 maxi = i;
278 }
279 }
280 }
281 moved[nMoved] = maxi;
282 nMoved++;
283 d += diff[maxi];
284 }
285
286 int totalAtoms = nMoved;
287 for (int i = 0; i < nMoved; i++) {
288 for (int j = 0; j < nAtoms; j++) {
289 if (moved[i] == j)
290 continue;
291
292 double diffRSaddle = saddle->distance(moved[i], j);
293
294 if (diffRSaddle < parameters.prefactor_options.within_radius &&
295 (!saddle->getFixed(j))) {
296
297 if (std::find(moved.data(), moved.data() + totalAtoms, j) ==
298 moved.data() + totalAtoms) {
299 moved[totalAtoms++] = j;
300 }
301 }
302 }
303 }
304 EONC_LOG_DEBUG("[Prefactor] including {} atoms in the hessian ({} moved + {} "
305 "neighbors)",
306 totalAtoms, nMoved, totalAtoms - nMoved);
307 return moved.head(totalAtoms);
308}
309
311 long nAtoms = matter->numberOfAtoms();
312
313 VectorXi moved(nAtoms);
314 moved.setConstant(-1);
315
316 int nMoved = 0;
317 for (int i = 0; i < nAtoms; i++) {
318 if (!matter->getFixed(i)) {
319 moved[nMoved] = i;
320 nMoved++;
321 }
322 }
323 return moved.head(nMoved);
324}
Eigen::Matrix< double, Eigen::Dynamic, 3, eOnStorageOrder > AtomMatrix
Definition Eigen.h:37
#define EONC_LOG_DEBUG(...)
Definition EonLogger.h:244
#define EONC_LOG_ERROR(...)
Definition EonLogger.h:262
VectorXd removeZeroFreqs(const VectorXd &freqs)
Definition Hessian.cpp:280
VectorXd getFreqs(Matter *matterIn, const VectorXi &atomsIn)
Definition Hessian.cpp:99
double distance(long index1, long index2) const
Definition Matter.cpp:360
AtomMatrix getFree() const
Definition Matter.cpp:580
AtomMatrix pbc(const AtomMatrix &diff) const
Definition Matter.h:163
long int numberOfAtoms() const
Definition Matter.cpp:209
long int numberOfFreeAtoms() const
Definition Matter.cpp:475
const AtomMatrix & getPositions() const
Definition Matter.cpp:236
int getFixed(long int atom) const
1 if every Cartesian axis of the atom is fixed, else 0.
Definition Matter.cpp:402
struct eonc::Parameters::structure_comparison_options_t structure_comparison_options
struct eonc::Parameters::prefactor_options_t prefactor_options
struct eonc::Parameters::main_options_t main_options
void logFreqs(const VectorXd &freqs, const char *name)
VectorXi allFreeAtoms(Matter *matter)
VectorXi movedAtomsPct(const Parameters &parameters, Matter *min1, Matter *saddle, Matter *min2)
VectorXi movedAtoms(const Parameters &parameters, Matter *min1, Matter *saddle, Matter *min2)
int getPrefactors(const Parameters &parameters, Matter *min1, Matter *saddle, Matter *min2, double &pref1, double &pref2)
Definition Prefactor.cpp:22
const char RATE_HTST[]
Definition Prefactor.h:22
const char RATE_QQHTST[]
Definition Prefactor.h:23
const char FILTER_FRACTION[]
Definition Prefactor.h:25
constexpr double pi
quill::Logger * get() noexcept
Get or create the default "combi" logger.
Definition EonLogger.h:44