eOn client
Long-timescale dynamics: aKMC, NEB, parallel replica
☾
Toggle main menu visibility
Loading...
Searching...
No Matches
Hessian.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/Hessian.h
"
13
#include "
eon/EonLogger.h
"
14
#include "
eon/HelperFunctions.h
"
15
#include "
eon/SafeMath.h
"
16
17
#include <cmath>
18
#include <fstream>
19
#include <sstream>
20
#include <string>
21
22
namespace
{
23
24
// phva_atoms entries are *mobile / displaced* atoms for FD (hybrid/PHVA-class
25
// active set). Intersect with non-fixed atoms in HessianJob.
26
27
bool
isCentralScheme(
const
std::string &scheme) {
28
return
scheme ==
"central"
|| scheme ==
"CENTRAL"
|| scheme ==
"Central"
;
29
}
30
31
// Checkpoint: first line "eon_hess_ckpt <size> <next_col>", then size*size
32
// doubles in row-major order matching MatrixXd storage.
33
bool
loadColumnCheckpoint(
const
std::string &path,
int
size,
int
&nextCol,
34
MatrixXd
&H) {
35
std::ifstream in(path);
36
if
(!in) {
37
return
false
;
38
}
39
std::string tag;
40
int
fileSize = 0;
41
in >> tag >> fileSize >> nextCol;
42
if
(!in || tag !=
"eon_hess_ckpt"
|| fileSize != size || nextCol < 0 ||
43
nextCol > size) {
44
return
false
;
45
}
46
H.resize(size, size);
47
for
(
int
i = 0; i < size; ++i) {
48
for
(
int
j = 0; j < size; ++j) {
49
double
v = 0.0;
50
in >> v;
51
if
(!in) {
52
return
false
;
53
}
54
H(i, j) = v;
55
}
56
}
57
return
true
;
58
}
59
60
bool
saveColumnCheckpoint(
const
std::string &path,
int
size,
int
nextCol,
61
const
MatrixXd
&H) {
62
std::ofstream out(path);
63
if
(!out) {
64
return
false
;
65
}
66
out <<
"eon_hess_ckpt "
<< size <<
" "
<< nextCol <<
"\n"
;
67
out.precision(17);
68
for
(
int
i = 0; i < size; ++i) {
69
for
(
int
j = 0; j < size; ++j) {
70
out << H(i, j) << (j + 1 == size ?
'\n'
:
' '
);
71
}
72
}
73
return
static_cast<
bool
>
(out);
74
}
75
76
}
// namespace
77
78
Hessian::Hessian
(
const
Parameters
¶ms,
Matter
*
matter
)
79
:
matter
{
matter
},
80
parameters
{params} {
81
hessian
.resize(0, 0);
82
freqs
.resize(0);
83
}
84
85
MatrixXd
Hessian::getHessian
(
Matter
*matterIn,
const
VectorXi &atomsIn) {
86
if
((
matter
!= matterIn) || (
atoms
.size() != atomsIn.size()) ||
87
(
atoms
!= atomsIn) || (
hessian
.rows() == 0)) {
88
hessian
.resize(0, 0);
89
matter
= matterIn;
90
atoms
= atomsIn;
91
92
if
(!
calculate
()) {
93
hessian
.resize(0, 0);
94
}
95
}
96
return
hessian
;
97
}
98
99
VectorXd
Hessian::getFreqs
(
Matter
*matterIn,
const
VectorXi &atomsIn) {
100
if
((
matter
!= matterIn) || (
atoms
.size() != atomsIn.size()) ||
101
(
atoms
!= atomsIn) || (
hessian
.rows() == 0)) {
102
hessian
.resize(0, 0);
103
matter
= matterIn;
104
atoms
= atomsIn;
105
106
if
(!
calculate
()) {
107
freqs
.resize(0);
108
}
109
}
110
return
freqs
;
111
}
112
113
bool
Hessian::calculate
() {
114
int
nAtoms =
matter
->numberOfAtoms();
115
116
int
size =
static_cast<
int
>
(
atoms
.rows()) * 3;
117
QUILL_LOG_DEBUG(
log
,
"[Hessian] Hessian size: {}\n"
, size);
118
if
(size == 0) {
119
return
false
;
120
}
121
122
// Mobile-atom polarity: indices in `atoms` are FD-displaced DOF owners.
123
for
(
int
a = 0; a <
atoms
.rows(); ++a) {
124
const
long
idx =
atoms
(a);
125
if
(idx < 0 || idx >= nAtoms) {
126
QUILL_LOG_ERROR(
log
,
127
"[Hessian] atom index {} out of range [0, {}) at list "
128
"entry {}; aborting FD Hessian"
,
129
idx, nAtoms, a);
130
return
false
;
131
}
132
}
133
134
Matter
matterTemp(*
matter
);
135
double
dr =
parameters
.main_options.finiteDifference;
136
if
(!(dr > 0.0) || !std::isfinite(dr)) {
137
QUILL_LOG_ERROR(
log
,
"[Hessian] invalid finiteDifference dr={}\n"
, dr);
138
return
false
;
139
}
140
141
const
bool
useCentral = isCentralScheme(
parameters
.hessian_options.fd_scheme);
142
const
std::string &ckptPath =
parameters
.hessian_options.checkpoint_path;
143
const
bool
wantResume =
144
parameters
.hessian_options.resume && !ckptPath.empty();
145
146
AtomMatrix
pos =
matter
->getPositions();
147
AtomMatrix
posDisplace(nAtoms, 3);
148
AtomMatrix
posTemp(nAtoms, 3);
149
AtomMatrix
force0(nAtoms, 3);
150
AtomMatrix
forcePlus(nAtoms, 3);
151
AtomMatrix
forceMinus(nAtoms, 3);
152
153
hessian
.resize(size, size);
154
hessian
.setZero();
155
156
int
startCol = 0;
157
if
(wantResume && loadColumnCheckpoint(ckptPath, size, startCol,
hessian
)) {
158
QUILL_LOG_DEBUG(
log
,
"[Hessian] resume from column {} / {}\n"
, startCol,
159
size);
160
}
else
{
161
startCol = 0;
162
hessian
.setZero();
163
}
164
165
force0 = matterTemp.
getForces
();
166
if
(!force0.allFinite()) {
167
QUILL_LOG_ERROR(
log
,
"[Hessian] non-finite forces at undisplaced geometry; "
168
"aborting FD Hessian"
);
169
return
false
;
170
}
171
172
for
(
int
i = startCol; i < size; i++) {
173
posDisplace.setZero();
174
posDisplace(
atoms
(i / 3), i % 3) = dr;
175
176
posTemp = pos + posDisplace;
177
matterTemp.
setPositions
(posTemp);
178
forcePlus = matterTemp.
getForces
();
179
if
(!forcePlus.allFinite()) {
180
QUILL_LOG_ERROR(
log
,
181
"[Hessian] non-finite forces for FD column {} (+); "
182
"aborting FD Hessian"
,
183
i);
184
return
false
;
185
}
186
187
if
(useCentral) {
188
posTemp = pos - posDisplace;
189
matterTemp.
setPositions
(posTemp);
190
forceMinus = matterTemp.
getForces
();
191
if
(!forceMinus.allFinite()) {
192
QUILL_LOG_ERROR(
log
,
193
"[Hessian] non-finite forces for FD column {} (-); "
194
"aborting FD Hessian"
,
195
i);
196
return
false
;
197
}
198
// Central: H_ij ≈ -(F+(xj) - F-(xj)) / (2 dr), mass-weighted
199
for
(
int
j = 0; j < size; j++) {
200
const
double
dF =
201
forcePlus(
atoms
(j / 3), j % 3) - forceMinus(
atoms
(j / 3), j % 3);
202
hessian
(i, j) = -dF / (2.0 * dr);
203
const
double
effMass = std::sqrt(
matter
->getMass(
atoms
(j / 3)) *
204
matter
->getMass(
atoms
(i / 3)));
205
hessian
(i, j) =
eonc::safemath::safe_div
(
hessian
(i, j), effMass, 0.0);
206
}
207
}
else
{
208
// One-sided (forward): H_ij ≈ -(F+(xj) - F0(xj)) / dr [default; cheaper]
209
for
(
int
j = 0; j < size; j++) {
210
const
double
dF =
211
forcePlus(
atoms
(j / 3), j % 3) - force0(
atoms
(j / 3), j % 3);
212
hessian
(i, j) = -dF / dr;
213
const
double
effMass = std::sqrt(
matter
->getMass(
atoms
(j / 3)) *
214
matter
->getMass(
atoms
(i / 3)));
215
hessian
(i, j) =
eonc::safemath::safe_div
(
hessian
(i, j), effMass, 0.0);
216
}
217
}
218
219
if
(!ckptPath.empty()) {
220
// next column to compute after a clean interrupt
221
saveColumnCheckpoint(ckptPath, size, i + 1,
hessian
);
222
}
223
}
224
225
// Symmetrize (FD noise breaks H=H^T; required for vib analysis)
226
for
(
int
i = 0; i < size; i++) {
227
for
(
int
j = 0; j < i; j++) {
228
hessian
(i, j) = (
hessian
(i, j) +
hessian
(j, i)) / 2;
229
hessian
(j, i) =
hessian
(i, j);
230
}
231
}
232
233
if
(!
hessian
.allFinite()) {
234
QUILL_LOG_ERROR(
log
,
"[Hessian] non-finite entries after FD assembly; "
235
"aborting eigen solve"
);
236
return
false
;
237
}
238
239
if
(!
parameters
.main_options.quiet) {
240
QUILL_LOG_DEBUG(
log
,
"[Hessian] writing hessian\n"
);
241
std::ofstream hessfile;
242
hessfile.open(
"hessian.dat"
);
243
hessfile <<
hessian
;
244
hessfile.close();
245
}
246
247
// Completed run: remove checkpoint so a later job does not resume stale cols
248
if
(!ckptPath.empty()) {
249
std::remove(ckptPath.c_str());
250
}
251
252
double
t0, t1;
253
eonc::helpers::getTime
(&t0,
nullptr
,
nullptr
);
254
QUILL_LOG_DEBUG(
log
,
"[Hessian] calculating eigen values of the hessian\n"
);
255
// ColMajor copy for SelfAdjointEigenSolver (eOn MatrixXd is RowMajor)
256
using
ColMajorXd =
257
Eigen::Matrix<double, Eigen::Dynamic, Eigen::Dynamic, Eigen::ColMajor>;
258
ColMajorXd hessianCol =
hessian
;
259
Eigen::SelfAdjointEigenSolver<ColMajorXd> es(hessianCol,
260
Eigen::EigenvaluesOnly);
261
eonc::helpers::getTime
(&t1,
nullptr
,
nullptr
);
262
QUILL_LOG_DEBUG(
log
,
"[Hessian] eigenvalue problem took {:.4e} seconds\n"
,
263
t1 - t0);
264
if
(es.info() != Eigen::Success) {
265
QUILL_LOG_ERROR(
log
,
266
"[Hessian] SelfAdjointEigenSolver failed (info={}); "
267
"aborting"
,
268
static_cast<
int
>
(es.info()));
269
return
false
;
270
}
271
freqs
= es.eigenvalues();
272
if
(!
freqs
.allFinite()) {
273
QUILL_LOG_ERROR(
log
,
"[Hessian] non-finite eigenvalues; aborting"
);
274
return
false
;
275
}
276
277
return
true
;
278
}
279
280
VectorXd
Hessian::removeZeroFreqs
(
const
VectorXd &
freqs
) {
281
QUILL_LOG_DEBUG(
log
,
"[Hessian] removing zero frequency modes"
);
282
int
size =
freqs
.size();
283
if
(size != 3 *
matter
->numberOfAtoms()) {
284
return
freqs
;
285
}
286
VectorXd newfreqs;
287
newfreqs.resize(size);
288
int
nremoved = 0;
289
for
(
int
i = 0; i < size; i++) {
290
if
(std::abs(
freqs
(i)) >
parameters
.hessian_options.zero_freq_value) {
291
newfreqs(i - nremoved) =
freqs
(i);
292
}
else
{
293
nremoved++;
294
}
295
}
296
297
if
(nremoved != 6) {
298
QUILL_LOG_ERROR(
299
log
,
"[Hessian] [error] Found {} trivial eigenmodes instead of 6"
,
300
nremoved);
301
}
302
return
newfreqs.head(size - nremoved);
303
}
MatrixXd
Eigen::Matrix< double, Eigen::Dynamic, Eigen::Dynamic, eOnStorageOrder > MatrixXd
Definition
Eigen.h:33
AtomMatrix
Eigen::Matrix< double, Eigen::Dynamic, 3, eOnStorageOrder > AtomMatrix
Definition
Eigen.h:37
EonLogger.h
HelperFunctions.h
Hessian.h
SafeMath.h
Hessian::Hessian
Hessian(const Parameters ¶ms, Matter *matter)
Definition
Hessian.cpp:78
eonc::Hessian::calculate
bool calculate()
Definition
Hessian.cpp:113
eonc::Hessian::log
eonc::log::Scoped log
Definition
Hessian.h:40
eonc::Hessian::matter
Matter * matter
Definition
Hessian.h:32
eonc::Hessian::freqs
VectorXd freqs
Definition
Hessian.h:36
eonc::Hessian::removeZeroFreqs
VectorXd removeZeroFreqs(const VectorXd &freqs)
Definition
Hessian.cpp:280
eonc::Hessian::atoms
VectorXi atoms
Definition
Hessian.h:38
eonc::Hessian::getHessian
MatrixXd getHessian(Matter *matterIn, const VectorXi &atomsIn)
Definition
Hessian.cpp:85
eonc::Hessian::getFreqs
VectorXd getFreqs(Matter *matterIn, const VectorXi &atomsIn)
Definition
Hessian.cpp:99
eonc::Hessian::parameters
const Parameters & parameters
Definition
Hessian.h:33
eonc::Hessian::hessian
MatrixXd hessian
Definition
Hessian.h:35
eonc::Matter
Definition
Matter.h:92
eonc::Matter::getForces
const AtomMatrix & getForces() const
Definition
Matter.cpp:324
eonc::Matter::setPositions
void setPositions(const AtomMatrix &pos)
Definition
Matter.cpp:273
eonc::Parameters
Definition
Parameters.h:28
eonc::helpers::getTime
void getTime(double *real, double *user, double *sys)
Definition
HelperFunctions.cpp:45
eonc::safemath::safe_div
constexpr double safe_div(double num, double denom, double fallback=0.0)
Definition
SafeMath.h:21
client
Hessian.cpp
Generated by
1.17.0
Generated by
Doxygen 1.17.0
Analytics by
Antics
provided by
TurtleTech ehf