Skip to content

Commit 13d2dbc

Browse files
committed
Fix: Implement numerically safe switching function
replacing function and derivative with order 1 Taylor expansions around indeterminate form 0/0 for original expression fixes #903
1 parent c29f553 commit 13d2dbc

File tree

1 file changed

+20
-16
lines changed

1 file changed

+20
-16
lines changed

src/colvarcomp_coordnums.cpp

Lines changed: 20 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -57,8 +57,16 @@ cvm::real colvar::coordnum::switching_function(cvm::real const &r0,
5757

5858
cvm::real const xn = cvm::integer_power(l2, en2);
5959
cvm::real const xd = cvm::integer_power(l2, ed2);
60-
//The subtraction and division stretches the function back to the range of [0,1] from [pairlist_tol,1]
61-
cvm::real const func_no_pairlist = (1.0-xn)/(1.0-xd);
60+
cvm::real const eps = 1.0e-8;
61+
cvm::real const h = l2 - 1.0;
62+
cvm::real const en2_r = (cvm::real) en2;
63+
cvm::real const ed2_r = (cvm::real) ed2;
64+
65+
// Function value: 1st-order Taylor expansion around l2 = 1
66+
cvm::real const func_no_pairlist = (std::abs(h) < eps) ?
67+
(en2_r / ed2_r) + h * (en2_r * (en2_r - ed2_r) / (2.0 * ed2_r)) :
68+
(1.0 - xn) / (1.0 - xd);
69+
6270
cvm::real func, inv_one_pairlist_tol;
6371
if (flags & ef_use_pairlist) {
6472
inv_one_pairlist_tol = 1 / (1.0-pairlist_tol);
@@ -77,23 +85,19 @@ cvm::real colvar::coordnum::switching_function(cvm::real const &r0,
7785
return 0;
7886

7987
if (flags & ef_gradients) {
80-
//This is the old, completely correct expression for dFdl2:
81-
//cvm::real const dFdl2 = (1.0/(1.0-xd))*(en2*(xn/l2) -
82-
// func*ed2*(xd/l2))*(-1.0);
83-
//This can become:
84-
//cvm::real const dFdl2 = (1.0/(1.0-xd))*(en2*(xn/l2)*(1.0-xn)/(1.0-xn) -
85-
// func*ed2*(xd/l2))*(-1.0);
86-
//Recognizing that func = (1.0-xn)/(1.0-xd), we can group together the "func" and get a version of dFdl2 that is 0
87-
//when func=0, which lets us skip this gradient calculation when func=0.
88-
cvm::real dFdl2;
89-
if (flags & ef_use_pairlist) {
90-
dFdl2 = func_no_pairlist * inv_one_pairlist_tol * ((ed2*xd/((1.0-xd)*l2)) - (en2*xn/((1.0-xn)*l2)));
91-
} else {
92-
dFdl2 = func * ((ed2*xd/((1.0-xd)*l2)) - (en2*xn/((1.0-xn)*l2)));
93-
}
88+
// Logarithmic derivative: 1st-order Taylor expansion around l2 = 1
89+
cvm::real const log_deriv = (std::abs(h) < eps) ?
90+
0.5 * (en2_r - ed2_r) + h * ((en2_r - ed2_r) * (en2_r + ed2_r - 6.0) / 12.0) :
91+
((ed2_r * xd / ((1.0 - xd) * l2)) - (en2_r * xn / ((1.0 - xn) * l2)));
92+
93+
cvm::real const dFdl2 = (flags & ef_use_pairlist) ?
94+
func_no_pairlist * inv_one_pairlist_tol * log_deriv :
95+
func * log_deriv;
96+
9497
cvm::rvector const dl2dx((2.0 * inv_r0sq_vec.x) * diff.x,
9598
(2.0 * inv_r0sq_vec.y) * diff.y,
9699
(2.0 * inv_r0sq_vec.z) * diff.z);
100+
97101
const cvm::rvector G = dFdl2*dl2dx;
98102
g1x += -1.0*G.x;
99103
g1y += -1.0*G.y;

0 commit comments

Comments
 (0)