c++
|
Makefile,
atom.C,
atom.h,
bond.h,
group.C,
group.h,
main.C,
menu.C,
term.C,
term.h,
|
|
|
/*
* term.c - Energy/force terms, and calculations for them
* Copyright (C) 1997 Will Ware
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include
#include
#include "atom.h"
#include "term.h"
/********************************************************************/
/*
* Physical Units (these are all consistent with one another):
* Distances are in nanometers (10^-9 m)
* Times are in picoseconds (10^-12 sec)
* Masses are in units of 10^-27 kg
* Forces are in piconewtons (10^-12 N)
* Energies are in milli-attojoules (10^-21 J)
* Spring constants are in millinewtons-per-meter or pN/nm
* Charges are in single-proton charges (0.160206*10^-18 coulombs)
*/
/*
* Might make sense to switch to these in the future:
* Distances are in nanometers (10^-9 m)
* Times are in femtoseconds (10^-15 sec)
* Masses are in units of 10^-27 kg
* Forces are in micronewtons (10^-6 N)
* Energies are in femtojoules (10^-15 J)
* Spring constants are in kilonewtons-per-meter or pN/nm
* Indications that this is a good idea: spring constants are now
* huge.
*/
/********************************************************************/
/* Handy little macros for terse vector hacking */
#define COPY(dest,x) dest[0] = x[0]; \
dest[1] = x[1]; \
dest[2] = x[2];
#define ADD(sum,x,y) sum[0] = x[0] + y [0]; \
sum[1] = x[1] + y [1]; \
sum[2] = x[2] + y [2];
#define DIFF(diff,x,y) diff[0] = x[0] - y [0]; \
diff[1] = x[1] - y [1]; \
diff[2] = x[2] - y [2];
#define DOT(x,y) ((x[0] * y [0]) + (x[1] * y [1]) + (x[2] * y [2]))
#define CROSS(z,x,y) \
z[0] = x[1] * y[2] - x[2] * y[1]; \
z[1] = x[2] * y[0] - x[0] * y[2]; \
z[2] = x[0] * y[1] - x[1] * y[0];
#define LEN(x) (sqrt (DOT (x, x)))
#define LEN2(x,y) (sqrt (DOT(x,x) * DOT(y,y))) /* efficiency hack */
#define SCALE(v,m) v[0] *= m; v[1] *= m; v[2] *= m;
#define MUL(v,x,m) v[0] = m * x[0]; v[1] = m * x[1]; v[2] = m * x[2];
#define LIN_COMB(z,a,x,b,y) \
z[0] = a * x[0] + b * y[0]; \
z[1] = a * x[1] + b * y[1]; \
z[2] = a * x[2] + b * y[2];
#define ADD_LIN_COMB(z,a,x,b,y) \
z[0] += a * x[0] + b * y[0]; \
z[1] += a * x[1] + b * y[1]; \
z[2] += a * x[2] + b * y[2];
/********************************************************************/
static double
radial_energy (double (*efunc) (double, double, double, double, double),
double c1, double c2, double c3, double c4,
atom * a1, atom * a2)
{
double diff[3], len;
DIFF (diff, a1->x, a2->x);
len = LEN (diff);
return (*efunc) (len, c1, c2, c3, c4);
}
static void
radial_force (double (*ederiv) (double, double, double, double, double),
double c1, double c2, double c3, double c4,
atom * a1, atom * a2)
{
double diff[3], len, m;
DIFF (diff, a1->x, a2->x);
len = LEN (diff);
m = (*ederiv) (len, c1, c2, c3, c4) / len;
SCALE (diff, m);
DIFF (a1->f, a1->f, diff);
ADD (a2->f, a2->f, diff);
}
/********************************************************************/
static struct
{
enum SPECIES species1, species2;
double ks, r0, De, beta;
}
length_term_coeffs[] =
{
/* ks r0 De beta */
/* pN/nm nm maJ nm^-1 */
{
C_SP3, C_SP3, 440000.0, 0.1523, 545.0, 19.89
}
,
{
C_SP2, C_SP2, 960000.0, 0.1337, 1207.0, 19.94
}
,
{
C_SP, C_SP, 1560000.0, 0.1212, 1614.0, 21.98
}
,
{
C_SP3, H, 460000.0, 0.1113, 671.0, 18.51
}
,
{
C_SP3, O_SP3, 536000.0, 0.1402, 1343.0, 20.05
}
/* just a few for now, add more when it's working */
};
static int num_length_coeffs =
sizeof (length_term_coeffs) / sizeof (length_term_coeffs[0]);
#define KCUBIC 20.0 /* (0.05 nanometer)^-1 */
static double
length_energy_helper (double r, double ks,
double r0, double De, double beta)
{
double rdiff = (r - r0), expr;
if (rdiff < 0.0)
return 0.5 * ks * rdiff * rdiff * (1 - KCUBIC * rdiff);
else
{
expr = 1.0 - exp (-beta * rdiff);
return De * expr * expr;
}
}
static double
length_energy_function (term * t)
{
return radial_energy (length_energy_helper,
t->coeffs[0],
t->coeffs[1],
t->coeffs[2],
t->coeffs[3],
t->atoms[0],
t->atoms[1]);
}
static double
length_energy_deriv (double r, double ks,
double r0, double De, double beta)
{
double rdiff = (r - r0), expr;
if (rdiff < 0.0)
return ks * rdiff * (1.0 - 1.5 * KCUBIC * rdiff);
else
{
expr = exp (-beta * rdiff);
return 2.0 * De * beta * expr * (1.0 - expr);
}
}
static void
length_add_forces (term * t)
{
radial_force (length_energy_deriv,
t->coeffs[0],
t->coeffs[1],
t->coeffs[2],
t->coeffs[3],
t->atoms[0],
t->atoms[1]);
}
term *
length_term (atom * a1, atom * a2)
{
int i, found;
term *t = new term;
t->type = LENGTH;
t->atoms[0] = a1;
t->atoms[1] = a2;
for (i = found = 0; i < num_length_coeffs && !found; i++)
if ((length_term_coeffs[i].species1 == a1->species &&
length_term_coeffs[i].species2 == a2->species) ||
(length_term_coeffs[i].species1 == a2->species &&
length_term_coeffs[i].species2 == a1->species))
{
t->coeffs[0] = length_term_coeffs[i].ks;
t->coeffs[1] = length_term_coeffs[i].r0;
t->coeffs[2] = length_term_coeffs[i].De;
t->coeffs[3] = length_term_coeffs[i].beta;
found = 1;
}
if (!found)
{
t->coeffs[0] = 440000.0;
t->coeffs[1] = 0.1523;
t->coeffs[2] = 545.0;
t->coeffs[3] = 19.89;
}
return t;
}
/********************************************************************/
static struct
{
enum SPECIES species1, species2, species3;
double kth, th0;
}
angle_term_coeffs[] =
{
/* kth th0 */
/* maJ/rad^2 radians */
{
C_SP3, C_SP3, C_SP3, 450.0, 1.911
}
,
{
C_SP3, C_SP3, H, 360.0, 1.909
}
,
{
H, C_SP3, H, 320.0, 1.909
}
,
{
C_SP3, C_SP2, C_SP3, 450.0, 2.046
}
,
{
C_SP2, C_SP3, C_SP2, 450.0, 1.911
}
,
{
C_SP3, C_SP2, C_SP2, 550.0, 2.119
}
,
{
C_SP2, C_SP2, H, 360.0, 2.094
}
,
{
C_SP2, C_SP2, C_SP2, 430.0, 2.094
}
,
{
C_SP3, C_SP, C_SP, 200.0, 3.142
}
,
{
C_SP3, C_SP2, O_SP2, 460.0, 2.138
}
,
{
C_SP3, O_SP3, C_SP3, 770.0, 1.864
}
,
{
C_SP3, N_SP3, C_SP3, 630.0, 1.880
}
,
{
C_SP3, C_SP3, O_SP3, 700.0, 1.876
}
,
{
C_SP3, C_SP3, N_SP3, 570.0, 1.911
}
};
static int num_angle_coeffs =
sizeof (angle_term_coeffs) / sizeof (angle_term_coeffs[0]);
static double
angle_energy_function (term * t)
{
double kth, th0, th;
double adiff[3], bdiff[3];
kth = t->coeffs[0];
th0 = t->coeffs[1];
DIFF (adiff, t->atoms[0]->x, t->atoms[1]->x);
DIFF (bdiff, t->atoms[2]->x, t->atoms[1]->x);
th = DOT (adiff, bdiff) / sqrt (DOT (adiff, adiff) * DOT (bdiff, bdiff));
return 0.5 * kth * (th - th0) * (th - th0);
}
static void
angle_add_forces (term * t)
{
double kth, th0, th, tdif, du_dth;
double adiff[3], bdiff[3], L1[3], L3[3];
double f1[3], f2[3], f3[3];
kth = t->coeffs[0];
th0 = t->coeffs[1];
th = DOT (adiff, bdiff) / sqrt (DOT (adiff, adiff) * DOT (bdiff, bdiff));
tdif = th - th0;
du_dth = kth * (tdif * (1.0 + 1.508 * tdif * tdif));
DIFF (adiff, t->atoms[0]->x, t->atoms[1]->x);
DIFF (bdiff, t->atoms[2]->x, t->atoms[1]->x);
LIN_COMB (L1,
DOT (adiff, adiff), bdiff,
-DOT (adiff, bdiff), adiff);
MUL (f1, L1, (du_dth / LEN2 (L1, adiff)));
LIN_COMB (L3,
DOT (bdiff, bdiff), adiff,
-DOT (adiff, bdiff), bdiff);
MUL (f3, L3, (du_dth / LEN2 (L3, bdiff)));
LIN_COMB (f2, -1.0, f1, -1.0, f3);
ADD (t->atoms[0]->f, t->atoms[0]->f, f1);
ADD (t->atoms[1]->f, t->atoms[1]->f, f2);
ADD (t->atoms[2]->f, t->atoms[2]->f, f3);
}
term *
angle_term (atom * a1, atom * a2, atom * a3)
{
int i, found;
term *t = new term;
t->type = ANGLE;
t->atoms[0] = a1;
t->atoms[1] = a2;
t->atoms[2] = a3;
for (i = found = 0; i < num_angle_coeffs && !found; i++)
if ((angle_term_coeffs[i].species1 == a1->species &&
angle_term_coeffs[i].species2 == a2->species &&
angle_term_coeffs[i].species3 == a3->species) ||
(angle_term_coeffs[i].species1 == a3->species &&
angle_term_coeffs[i].species2 == a2->species &&
angle_term_coeffs[i].species3 == a1->species))
{
t->coeffs[0] = angle_term_coeffs[i].kth;
t->coeffs[1] = angle_term_coeffs[i].th0;
found = 1;
}
if (!found)
{
t->coeffs[0] = 440000.0;
t->coeffs[1] = 0.1523;
}
return t;
}
/********************************************************************/
static struct
{
enum SPECIES species1, species2, species3, species4;
double v1, v2, v3;
}
torsion_term_coeffs[] =
{
/* maJ maJ maJ */
{
C_SP3, C_SP3, C_SP3, C_SP3, 1.39, 1.88, 0.65
}
,
{
C_SP3, C_SP3, C_SP3, H, 0.0, 0.0, 1.85
}
,
{
H, C_SP3, C_SP3, H, 0.0, 0.0, 1.65
}
,
{
C_SP3, C_SP2, C_SP2, C_SP3, -0.69, 69.47, 0.0
}
,
{
C_SP2, C_SP2, C_SP2, C_SP2, -6.46, 55.58, 0.0
}
,
{
H, C_SP2, C_SP2, H, 0.0, 104.21, 0.0
}
};
static int num_torsion_coeffs =
sizeof (torsion_term_coeffs) / sizeof (torsion_term_coeffs[0]);
static double
safe_acos (double x)
{
if (x >= 1.0) return acos (1.0);
if (x <= -1.0) return acos (-1.0);
return acos (x);
}
static double
torsion_energy_function (term * t)
{
double v1, v2, v3, w;
double p[3], q[3], r[3];
v1 = t->coeffs[0];
v2 = t->coeffs[1];
v3 = t->coeffs[2];
DIFF (p, t->atoms[0]->x, t->atoms[1]->x);
DIFF (q, t->atoms[1]->x, t->atoms[2]->x);
DIFF (r, t->atoms[3]->x, t->atoms[2]->x);
LIN_COMB (p,
1.0, p,
-DOT (p, q) / DOT (q, q), q);
MUL (p, p, 1.0 / LEN(p));
LIN_COMB (r,
1.0, r,
-DOT (r, q) / DOT (q, q), q);
MUL (r, r, 1.0 / LEN(r));
w = safe_acos (DOT (p, r));
return 0.5 * (v1 * (1.0 + cos (w)) +
v2 * (1.0 - cos (2 * w)) +
v3 * (1.0 + cos (3 * w)));
}
static void
torsion_add_forces (term * t)
{
double v1, v2, v3, pp, qq, rr, pq, qr, w, du_dw;
double alpha, beta, gamma;
double p[3], q[3], r[3], vm1[3], vq1[3], vm2[3], vq2[3], fm[3], fq[3];
v1 = t->coeffs[0];
v2 = t->coeffs[1];
v3 = t->coeffs[2];
DIFF (p, t->atoms[0]->x, t->atoms[1]->x);
DIFF (q, t->atoms[2]->x, t->atoms[1]->x);
DIFF (r, t->atoms[3]->x, t->atoms[2]->x);
pp = DOT (p, p);
qq = DOT (q, q);
rr = DOT (r, r);
pq = DOT (p, q);
qr = DOT (q, r);
alpha = sqrt (pq * pq / qq);
beta = sqrt (qq * qq);
gamma = sqrt (qr * qr / qq);
CROSS (vm1, q, p);
CROSS (vq1, r, q);
MUL (vm2, vm1, 1.0 / LEN (vm1));
MUL (vq2, vq1, 1.0 / LEN (vq1));
w = safe_acos (DOT (vm2, vq2));
du_dw = -0.5 * (v1 * sin (w) +
-2.0 * v2 * sin (2 * w) +
3.0 * v3 * sin (3 * w));
MUL (fm, vm2, du_dw / sqrt (pp - (pq * pq) / qq));
MUL (fq, vq2, du_dw / sqrt (rr - (qr * qr) / qq));
ADD (t->atoms[0]->f, t->atoms[0]->f, fm);
ADD (t->atoms[3]->f, t->atoms[3]->f, fq);
ADD_LIN_COMB (t->atoms[2]->f,
alpha / beta,
fm,
(beta + gamma) / beta,
fq);
ADD_LIN_COMB (t->atoms[1]->f,
gamma / beta,
fq,
(beta + alpha) / beta,
fm);
}
term *
torsion_term (atom * a1, atom * a2, atom * a3, atom * a4)
{
int i, found;
term *t = new term;
t->type = ANGLE;
t->atoms[0] = a1;
t->atoms[1] = a2;
t->atoms[2] = a3;
t->atoms[3] = a4;
for (i = found = 0; i < num_torsion_coeffs && !found; i++)
if ((torsion_term_coeffs[i].species1 == a1->species &&
torsion_term_coeffs[i].species2 == a2->species &&
torsion_term_coeffs[i].species3 == a3->species &&
torsion_term_coeffs[i].species4 == a4->species) ||
(torsion_term_coeffs[i].species1 == a4->species &&
torsion_term_coeffs[i].species2 == a3->species &&
torsion_term_coeffs[i].species3 == a2->species &&
torsion_term_coeffs[i].species4 == a1->species))
{
t->coeffs[0] = torsion_term_coeffs[i].v1;
t->coeffs[1] = torsion_term_coeffs[i].v2;
t->coeffs[2] = torsion_term_coeffs[i].v3;
found = 1;
}
if (!found)
{
t->coeffs[0] = 440.0;
t->coeffs[1] = 0.1523;
t->coeffs[2] = 0.1523;
}
return t;
}
/********************************************************************/
/* units here are (maJ * nm) / (e * e), where e is charge on a proton */
#define ELECTRO_CONSTANT (8.9876 * 0.160206 * 0.160206 * 1000.0)
static double
electro_energy_helper (double r, double q1,
double q2, double dummy1, double dummy2)
{
return (ELECTRO_CONSTANT * q1 * q2) / r;
}
static double
electrostatic_energy_function (term * t)
{
return radial_energy (electro_energy_helper,
t->coeffs[0],
t->coeffs[1],
0.0,
0.0,
t->atoms[0],
t->atoms[1]);
}
static double
electro_energy_deriv (double r, double q1,
double q2, double dummy1, double dummy2)
{
return (-ELECTRO_CONSTANT * q1 * q2) / (r * r);
}
static void
electrostatic_add_forces (term * t)
{
radial_force (electro_energy_deriv,
t->coeffs[0],
t->coeffs[1],
0.0,
0.0,
t->atoms[0],
t->atoms[1]);
}
term *
electrostatic_term (atom * a1, atom * a2)
{
term *t = new term;
t->type = ELECTROSTATIC;
t->atoms[0] = a1;
t->atoms[1] = a2;
t->coeffs[0] = a1->charge;
t->coeffs[1] = a2->charge;
return t;
}
/********************************************************************/
static double
vdw_energy_helper (double r, double evdw0,
double rvdw0, double dummy1, double dummy2)
{
double ratio = rvdw0 / r, ra3, ra6;
ra3 = ratio * ratio * ratio;
ra6 = ra3 * ra3;
return evdw0 * ra6 * (ra6 - 2.0);
}
static double
vdw_energy_function (term * t)
{
return radial_energy (vdw_energy_helper,
0.5 * (t->atoms[0]->evdw + t->atoms[1]->evdw),
t->atoms[0]->rvdw + t->atoms[1]->rvdw,
0.0,
0.0,
t->atoms[0],
t->atoms[1]);
}
static double
vdw_energy_deriv (double r, double evdw0,
double rvdw0, double dummy1, double dummy2)
{
double ratio = rvdw0 / r, ra3, ra6;
ra3 = ratio * ratio * ratio;
ra6 = ra3 * ra3;
return -12.0 * evdw0 * ratio * ra6 * (ra6 - 1.0);
}
static void
vdw_add_forces (term * t)
{
radial_force (vdw_energy_deriv,
0.5 * (t->atoms[0]->evdw + t->atoms[1]->evdw),
t->atoms[0]->rvdw + t->atoms[1]->rvdw,
0.0,
0.0,
t->atoms[0],
t->atoms[1]);
}
term *
vdw_term (atom * a1, atom * a2)
{
term *t = new term;
t->type = VDW;
t->atoms[0] = a1;
t->atoms[1] = a2;
return t;
}
/********************************************************************/
static double
sprd_energy_helper (double r, double k,
double r0, double dummy1, double dummy2)
{
/* note: the damper makes no contribution to potential energy */
double rdiff = r - r0;
return 0.5 * k * rdiff * rdiff;
}
static double
spring_damper_energy_function (term * t)
{
return radial_energy (sprd_energy_helper,
t->coeffs[0],
t->coeffs[1],
t->coeffs[2],
0.0,
t->atoms[0],
t->atoms[1]);
}
/* Because the damping force depends on velocities, we can't just use the
* normal radial-force routine.
*/
static void
spring_damper_add_forces (term * t)
{
double k, r0, d, diff[3], vdiff[3], r, rel_v, m;
k = t->coeffs[0];
r0 = t->coeffs[1];
d = t->coeffs[2];
DIFF (diff, t->atoms[0]->x, t->atoms[1]->x);
DIFF (vdiff, t->atoms[0]->v, t->atoms[1]->v);
r = LEN (diff);
rel_v = DOT (diff, vdiff) / r;
m = (k * (r - r0) + d * rel_v) / r;
SCALE (diff, m);
DIFF (t->atoms[0]->f, t->atoms[0]->f, diff);
ADD (t->atoms[1]->f, t->atoms[1]->f, diff);
}
term *
spring_damper_term (atom * a1, atom * a2,
double k, double r0, double d)
{
term *t = new term;
t->type = SPRING_DAMPER;
t->atoms[0] = a1;
t->atoms[1] = a2;
t->coeffs[0] = k;
t->coeffs[1] = r0;
t->coeffs[2] = d;
return t;
}
/********************************************************************/
double term::
energy_function (void)
{
switch (type)
{
case INVALID:
cerr << "Bad term!" << endl;
return 0.0;
case LENGTH:
return length_energy_function (this);
case ANGLE:
return angle_energy_function (this);
case TORSION:
return torsion_energy_function (this);
case ELECTROSTATIC:
return electrostatic_energy_function (this);
case VDW:
return vdw_energy_function (this);
case SPRING_DAMPER:
return spring_damper_energy_function (this);
}
return 0.0;
}
void term::
add_forces (void)
{
switch (type)
{
case INVALID:
cerr << "Bad term!" << endl;
break;
case LENGTH:
length_add_forces (this);
break;
case ANGLE:
angle_add_forces (this);
break;
case TORSION:
torsion_add_forces (this);
break;
case ELECTROSTATIC:
electrostatic_add_forces (this);
break;
case VDW:
vdw_add_forces (this);
break;
case SPRING_DAMPER:
spring_damper_add_forces (this);
break;
}
/* if not a known choice, do nothing */
}
|