1136 lines
33 KiB
C++
1136 lines
33 KiB
C++
// clang-format off
|
|
/* ----------------------------------------------------------------------
|
|
LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator
|
|
https://www.lammps.org/ Sandia National Laboratories
|
|
Steve Plimpton, sjplimp@sandia.gov
|
|
|
|
Copyright (2003) Sandia Corporation. Under the terms of Contract
|
|
DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains
|
|
certain rights in this software. This software is distributed under
|
|
the GNU General Public License.
|
|
|
|
See the README file in the top-level LAMMPS directory.
|
|
------------------------------------------------------------------------- */
|
|
|
|
#include "pair_amoeba.h"
|
|
|
|
#include "atom.h"
|
|
#include "comm.h"
|
|
#include "domain.h"
|
|
#include "error.h"
|
|
#include "fix_store_peratom.h"
|
|
#include "neigh_list.h"
|
|
|
|
#include <cmath>
|
|
|
|
using namespace LAMMPS_NS;
|
|
|
|
enum{NOFRAME,ZONLY,ZTHENX,BISECTOR,ZBISECT,THREEFOLD};
|
|
|
|
// ----------------------------------------------------------------------
|
|
// utility methods used by different parts of the AMOEBA force field
|
|
// ----------------------------------------------------------------------
|
|
|
|
/* ----------------------------------------------------------------------
|
|
kmpole performs one-time assignment of
|
|
xyzaxis multipole neighbors to each owned atom
|
|
any of the values can be 0 if not used
|
|
yaxis can later be negative due to chkpole()
|
|
also sets polaxe and pole[13] multipole for each owned atom
|
|
------------------------------------------------------------------------- */
|
|
|
|
void PairAmoeba::kmpole()
|
|
{
|
|
bool path;
|
|
int i,j,k,m,j12,k12,m12,k13,m13,flag;
|
|
int iframe,nframe;
|
|
int itype,jtype,ktype,mtype,xtype,ytype,ztype;
|
|
tagint jneigh,kneigh,mneigh;
|
|
|
|
// DEBUG vectors
|
|
|
|
tagint bondneigh[12];
|
|
tagint angleneigh[36];
|
|
|
|
amtype = atom->ivector[index_amtype];
|
|
int *polaxe = atom->ivector[index_polaxe];
|
|
double **xyzaxis = atom->darray[index_xyzaxis];
|
|
double **pole = fixpole->astore;
|
|
|
|
int **nspecial = atom->nspecial;
|
|
tagint **special = atom->special;
|
|
int nlocal = atom->nlocal;
|
|
|
|
int nmissing = 0;
|
|
|
|
for (i = 0; i < nlocal; i++) {
|
|
itype = amtype[i];
|
|
nframe = nmultiframe[itype];
|
|
|
|
// flag is used to prevent matching multiple times
|
|
// only first match is used
|
|
|
|
flag = 0;
|
|
|
|
// create a sorted version of bond/angle neighs from special[][]
|
|
// NOTE: this is to try and do it identically to Tinker
|
|
// b/c I think in Tinker, which case is seen first can depend on atom order
|
|
|
|
for (j = 0; j < nspecial[i][0]; j++)
|
|
bondneigh[j] = special[i][j];
|
|
for (m = 0; m < nspecial[i][0]; m++) {
|
|
tagint smallest = MAXTAGINT;
|
|
for (j = m; j < nspecial[i][0]; j++) {
|
|
if (bondneigh[j] < smallest) {
|
|
smallest = bondneigh[j];
|
|
k = j;
|
|
bondneigh[k] = bondneigh[m];
|
|
bondneigh[m] = smallest;
|
|
}
|
|
}
|
|
}
|
|
|
|
for (j = nspecial[i][0]; j < nspecial[i][1]; j++)
|
|
angleneigh[j] = special[i][j];
|
|
for (m = nspecial[i][0]; m < nspecial[i][1]; m++) {
|
|
tagint smallest = MAXTAGINT;
|
|
for (j = m; j < nspecial[i][1]; j++) {
|
|
if (angleneigh[j] < smallest) {
|
|
smallest = angleneigh[j];
|
|
k = j;
|
|
}
|
|
angleneigh[k] = angleneigh[m];
|
|
angleneigh[m] = smallest;
|
|
}
|
|
}
|
|
|
|
// assign xyz axis and fpole via only 1-2 connected atoms
|
|
|
|
for (iframe = 0; iframe < nframe; iframe++) {
|
|
xtype = xpole[itype][iframe];
|
|
ytype = ypole[itype][iframe];
|
|
ztype = zpole[itype][iframe];
|
|
for (j12 = 0; j12 < nspecial[i][0]; j12++) {
|
|
jneigh = bondneigh[j12];
|
|
j = atom->map(jneigh);
|
|
if (j < 0)
|
|
error->one(FLERR,"AMOEBA kmpole() could not find bond partner");
|
|
jtype = amtype[j];
|
|
if (jtype == ztype) {
|
|
for (k12 = 0; k12 < nspecial[i][0]; k12++) {
|
|
if (k12 == j12) continue;
|
|
kneigh = bondneigh[k12];
|
|
k = atom->map(kneigh);
|
|
if (k < 0)
|
|
error->one(FLERR,"AMOEBA kmpole() could not find bond partner");
|
|
ktype = amtype[k];
|
|
if (ktype == xtype) {
|
|
if (ytype == 0 && !flag) {
|
|
flag = 1;
|
|
xyzaxis[i][2] = ubuf(jneigh).d;
|
|
xyzaxis[i][0] = ubuf(kneigh).d;
|
|
xyzaxis[i][1] = 0.0;
|
|
polaxe[i] = mpaxis[itype][iframe];
|
|
for (j = 0; j < 13; j++)
|
|
pole[i][j] = fpole[itype][iframe][j];
|
|
} else {
|
|
for (m12 = 0; m12 < nspecial[i][0]; m12++) {
|
|
if (m12 == j12 || m12 == k12) continue;
|
|
mneigh = bondneigh[m12];
|
|
m = atom->map(mneigh);
|
|
if (m < 0)
|
|
error->one(FLERR,"AMOEBA kmpole() could not find bond partner");
|
|
mtype = amtype[m];
|
|
if (mtype == ytype && !flag) {
|
|
flag = 1;
|
|
xyzaxis[i][2] = ubuf(jneigh).d;
|
|
xyzaxis[i][0] = ubuf(kneigh).d;
|
|
xyzaxis[i][1] = ubuf(mneigh).d;
|
|
polaxe[i] = mpaxis[itype][iframe];
|
|
for (j = 0; j < 13; j++)
|
|
pole[i][j] = fpole[itype][iframe][j];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (flag) continue;
|
|
|
|
// assign xyz axis via 1-2 and 1-3 connected atoms
|
|
|
|
for (iframe = 0; iframe < nframe; iframe++) {
|
|
xtype = xpole[itype][iframe];
|
|
ytype = ypole[itype][iframe];
|
|
ztype = zpole[itype][iframe];
|
|
for (j12 = 0; j12 < nspecial[i][0]; j12++) {
|
|
jneigh = bondneigh[j12];
|
|
j = atom->map(jneigh);
|
|
if (j < 0) error->one(FLERR,"AMOEBA kmpole() could not find bond partner");
|
|
jtype = amtype[j];
|
|
if (jtype == ztype) {
|
|
for (k13 = nspecial[i][0]; k13 < nspecial[i][1]; k13++) {
|
|
kneigh = angleneigh[k13];
|
|
k = atom->map(kneigh);
|
|
if (k < 0) error->one(FLERR,"AMOEBA kmpole() could not find bond partner");
|
|
ktype = amtype[k];
|
|
path = false;
|
|
for (m12 = 0; m12 < nspecial[k][0]; m12++)
|
|
if (special[k][m12] == jneigh) path = true;
|
|
if (!path) continue;
|
|
|
|
if (ktype == xtype) {
|
|
if (ytype == 0 && !flag) {
|
|
flag = 1;
|
|
xyzaxis[i][2] = ubuf(jneigh).d;
|
|
xyzaxis[i][0] = ubuf(kneigh).d;
|
|
xyzaxis[i][1] = 0.0;
|
|
polaxe[i] = mpaxis[itype][iframe];
|
|
for (j = 0; j < 13; j++)
|
|
pole[i][j] = fpole[itype][iframe][j];
|
|
} else {
|
|
for (m13 = nspecial[i][0]; m13 < nspecial[i][1]; m13++) {
|
|
if (m13 == k13) continue;
|
|
mneigh = angleneigh[m13];
|
|
m = atom->map(mneigh);
|
|
if (m < 0)
|
|
error->one(FLERR,"AMOEBA kmpole() could not find bond partner");
|
|
mtype = amtype[m];
|
|
path = false;
|
|
for (m12 = 0; m12 < nspecial[m][0]; m12++)
|
|
if (special[m][m12] == jneigh) path = true;
|
|
if (!path) continue;
|
|
if (mtype == ytype && !flag) {
|
|
flag = 1;
|
|
xyzaxis[i][2] = ubuf(jneigh).d;
|
|
xyzaxis[i][0] = ubuf(kneigh).d;
|
|
xyzaxis[i][1] = ubuf(mneigh).d;
|
|
polaxe[i] = mpaxis[itype][iframe];
|
|
for (j = 0; j < 13; j++)
|
|
pole[i][j] = fpole[itype][iframe][j];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (flag) continue;
|
|
|
|
// assign xyz axis via only a z-defining atom
|
|
|
|
for (iframe = 0; iframe < nframe; iframe++) {
|
|
xtype = xpole[itype][iframe];
|
|
ytype = ypole[itype][iframe];
|
|
ztype = zpole[itype][iframe];
|
|
for (j12 = 0; j12 < nspecial[i][0]; j12++) {
|
|
jneigh = bondneigh[j12];
|
|
j = atom->map(jneigh);
|
|
if (j < 0) error->one(FLERR,"AMOEBA kmpole() could not find bond partner");
|
|
jtype = amtype[j];
|
|
if (jtype == ztype) {
|
|
if (xtype == 0 && !flag) {
|
|
flag = 1;
|
|
xyzaxis[i][2] = ubuf(jneigh).d;
|
|
xyzaxis[i][0] = xyzaxis[i][1] = 0.0;
|
|
polaxe[i] = mpaxis[itype][iframe];
|
|
for (j = 0; j < 13; j++)
|
|
pole[i][j] = fpole[itype][iframe][j];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (flag) continue;
|
|
|
|
// assign xyz axis via no connected atoms
|
|
|
|
for (iframe = 0; iframe < nframe; iframe++) {
|
|
xtype = xpole[itype][iframe];
|
|
ytype = ypole[itype][iframe];
|
|
ztype = zpole[itype][iframe];
|
|
if (ztype == 0 && !flag) {
|
|
flag = 1;
|
|
xyzaxis[i][2] = xyzaxis[i][1] = xyzaxis[i][0] = 0.0;
|
|
polaxe[i] = mpaxis[itype][iframe];
|
|
for (j = 0; j < 13; j++)
|
|
pole[i][j] = fpole[itype][iframe][j];
|
|
}
|
|
}
|
|
|
|
if (flag) continue;
|
|
|
|
// flag error if could not assign xyz axis
|
|
|
|
nmissing++;
|
|
}
|
|
|
|
// error check on missing settings
|
|
|
|
int nmissing_all;
|
|
MPI_Allreduce(&nmissing,&nmissing_all,1,MPI_INT,MPI_SUM,world);
|
|
if (nmissing_all)
|
|
error->all(FLERR, "Pair amoeba: {} multipole settings missing\n", nmissing_all);
|
|
}
|
|
|
|
/* ----------------------------------------------------------------------
|
|
chkpole inverts atomic multipole moments as necessary
|
|
at sites with chiral local reference frame definitions
|
|
called every timestep for each atom I
|
|
------------------------------------------------------------------------- */
|
|
|
|
void PairAmoeba::chkpole(int i)
|
|
{
|
|
bool check;
|
|
int ib,ic,id;
|
|
double xad,yad,zad;
|
|
double xbd,ybd,zbd;
|
|
double xcd,ycd,zcd;
|
|
double c1,c2,c3,vol;
|
|
|
|
double **pole = fixpole->astore;
|
|
|
|
int *polaxe = atom->ivector[index_polaxe];
|
|
double **xyzaxis = atom->darray[index_xyzaxis];
|
|
tagint yaxisID = (tagint) ubuf(xyzaxis[i][1]).i;
|
|
|
|
// test for chirality inversion
|
|
// if not, return
|
|
|
|
check = true;
|
|
if (polaxe[i] != ZTHENX) check = false;
|
|
if (yaxisID == 0) check = false;
|
|
if (!check) return;
|
|
|
|
ib = zaxis2local[i];
|
|
ic = xaxis2local[i];
|
|
id = yaxis2local[i];
|
|
|
|
// compute the signed parallelpiped volume at chiral site
|
|
|
|
double **x = atom->x;
|
|
|
|
xad = x[i][0] - x[id][0];
|
|
yad = x[i][1] - x[id][1];
|
|
zad = x[i][2] - x[id][2];
|
|
xbd = x[ib][0] - x[id][0];
|
|
ybd = x[ib][1] - x[id][1];
|
|
zbd = x[ib][2] - x[id][2];
|
|
xcd = x[ic][0] - x[id][0];
|
|
ycd = x[ic][1] - x[id][1];
|
|
zcd = x[ic][2] - x[id][2];
|
|
c1 = ybd*zcd - zbd*ycd;
|
|
c2 = ycd*zad - zcd*yad;
|
|
c3 = yad*zbd - zad*ybd;
|
|
vol = xad*c1 + xbd*c2 + xcd*c3;
|
|
|
|
// invert atomic multipole components involving the y-axis
|
|
// flip sign in permanent yaxis, not yaxis2local
|
|
|
|
if ((yaxisID < 0 && vol > 0.0) || (yaxisID > 0 && vol < 0.0)) {
|
|
xyzaxis[i][1] = -ubuf(yaxisID).d;
|
|
pole[i][2] = -pole[i][2];
|
|
pole[i][5] = -pole[i][5];
|
|
pole[i][7] = -pole[i][7];
|
|
pole[i][9] = -pole[i][9];
|
|
pole[i][11] = -pole[i][11];
|
|
}
|
|
}
|
|
|
|
/* ----------------------------------------------------------------------
|
|
rotmat finds the rotation matrix that rotates the local
|
|
coordinate system into the global frame at a multipole site
|
|
called every timestep for each atom I
|
|
------------------------------------------------------------------------- */
|
|
|
|
void PairAmoeba::rotmat(int i)
|
|
{
|
|
int ix,iy,iz;
|
|
double r,dot;
|
|
double xi,yi,zi;
|
|
double dx,dy,dz;
|
|
double dx1,dy1,dz1;
|
|
double dx2,dy2,dz2;
|
|
double dx3,dy3,dz3;
|
|
|
|
int *polaxe = atom->ivector[index_polaxe];
|
|
|
|
// get coordinates and frame definition for the multipole site
|
|
|
|
double **x = atom->x;
|
|
|
|
xi = x[i][0];
|
|
yi = x[i][1];
|
|
zi = x[i][2];
|
|
|
|
iz = zaxis2local[i];
|
|
ix = xaxis2local[i];
|
|
iy = yaxis2local[i];
|
|
|
|
// use the identity matrix as the default rotation matrix
|
|
|
|
rotate[0][0] = 1.0;
|
|
rotate[0][1] = 0.0;
|
|
rotate[0][2] = 0.0;
|
|
rotate[2][0] = 0.0;
|
|
rotate[2][1] = 0.0;
|
|
rotate[2][2] = 1.0;
|
|
|
|
// z-only frame rotation matrix elements for z-axis only
|
|
|
|
if (polaxe[i] == ZONLY) {
|
|
dx = x[iz][0] - xi;
|
|
dy = x[iz][1] - yi;
|
|
dz = x[iz][2] - zi;
|
|
r = sqrt(dx*dx + dy*dy + dz*dz);
|
|
rotate[2][0] = dx / r;
|
|
rotate[2][1] = dy / r;
|
|
rotate[2][2] = dz / r;
|
|
dx = 1.0;
|
|
dy = 0.0;
|
|
dz = 0.0;
|
|
dot = rotate[2][0];
|
|
if (fabs(dot) > 0.866) {
|
|
dx = 0.0;
|
|
dy = 1.0;
|
|
dot = rotate[2][1];
|
|
}
|
|
dx = dx - dot*rotate[2][0];
|
|
dy = dy - dot*rotate[2][1];
|
|
dz = dz - dot*rotate[2][2];
|
|
r = sqrt(dx*dx + dy*dy + dz*dz);
|
|
rotate[0][0] = dx / r;
|
|
rotate[0][1] = dy / r;
|
|
rotate[0][2] = dz / r;
|
|
|
|
// z-then-x frame rotation matrix elements for z- and x-axes
|
|
|
|
} else if (polaxe[i] == ZTHENX) {
|
|
dx = x[iz][0] - xi;
|
|
dy = x[iz][1] - yi;
|
|
dz = x[iz][2] - zi;
|
|
r = sqrt(dx*dx + dy*dy + dz*dz);
|
|
rotate[2][0] = dx / r;
|
|
rotate[2][1] = dy / r;
|
|
rotate[2][2] = dz / r;
|
|
dx = x[ix][0] - xi;
|
|
dy = x[ix][1] - yi;
|
|
dz = x[ix][2] - zi;
|
|
dot = dx*rotate[2][0] + dy*rotate[2][1] + dz*rotate[2][2];
|
|
dx = dx - dot*rotate[2][0];
|
|
dy = dy - dot*rotate[2][1];
|
|
dz = dz - dot*rotate[2][2];
|
|
r = sqrt(dx*dx + dy*dy + dz*dz);
|
|
rotate[0][0] = dx / r;
|
|
rotate[0][1] = dy / r;
|
|
rotate[0][2] = dz / r;
|
|
|
|
// bisector frame rotation matrix elements for z- and x-axes
|
|
|
|
} else if (polaxe[i] == BISECTOR) {
|
|
dx = x[iz][0] - xi;
|
|
dy = x[iz][1] - yi;
|
|
dz = x[iz][2] - zi;
|
|
r = sqrt(dx*dx + dy*dy + dz*dz);
|
|
dx1 = dx / r;
|
|
dy1 = dy / r;
|
|
dz1 = dz / r;
|
|
dx = x[ix][0] - xi;
|
|
dy = x[ix][1] - yi;
|
|
dz = x[ix][2] - zi;
|
|
r = sqrt(dx*dx + dy*dy + dz*dz);
|
|
dx2 = dx / r;
|
|
dy2 = dy / r;
|
|
dz2 = dz / r;
|
|
dx = dx1 + dx2;
|
|
dy = dy1 + dy2;
|
|
dz = dz1 + dz2;
|
|
r = sqrt(dx*dx + dy*dy + dz*dz);
|
|
rotate[2][0] = dx / r;
|
|
rotate[2][1] = dy / r;
|
|
rotate[2][2] = dz / r;
|
|
dot = dx2*rotate[2][0] + dy2*rotate[2][1] + dz2*rotate[2][2];
|
|
dx = dx2 - dot*rotate[2][0];
|
|
dy = dy2 - dot*rotate[2][1];
|
|
dz = dz2 - dot*rotate[2][2];
|
|
r = sqrt(dx*dx + dy*dy + dz*dz);
|
|
rotate[0][0] = dx / r;
|
|
rotate[0][1] = dy / r;
|
|
rotate[0][2] = dz / r;
|
|
|
|
// z-bisect frame rotation matrix elements for z- and x-axes
|
|
|
|
} else if (polaxe[i] == ZBISECT) {
|
|
dx = x[iz][0] - xi;
|
|
dy = x[iz][1] - yi;
|
|
dz = x[iz][2] - zi;
|
|
r = sqrt(dx*dx + dy*dy + dz*dz);
|
|
rotate[2][0] = dx / r;
|
|
rotate[2][1] = dy / r;
|
|
rotate[2][2] = dz / r;
|
|
dx = x[ix][0] - xi;
|
|
dy = x[ix][1] - yi;
|
|
dz = x[ix][2] - zi;
|
|
r = sqrt(dx*dx + dy*dy + dz*dz);
|
|
dx1 = dx / r;
|
|
dy1 = dy / r;
|
|
dz1 = dz / r;
|
|
dx = x[iy][0] - xi;
|
|
dy = x[iy][1] - yi;
|
|
dz = x[iy][2] - zi;
|
|
r = sqrt(dx*dx + dy*dy + dz*dz);
|
|
dx2 = dx / r;
|
|
dy2 = dy / r;
|
|
dz2 = dz / r;
|
|
dx = dx1 + dx2;
|
|
dy = dy1 + dy2;
|
|
dz = dz1 + dz2;
|
|
r = sqrt(dx*dx + dy*dy + dz*dz);
|
|
dx = dx / r;
|
|
dy = dy / r;
|
|
dz = dz / r;
|
|
dot = dx*rotate[2][0] + dy*rotate[2][1] + dz*rotate[2][2];
|
|
dx = dx - dot*rotate[2][0];
|
|
dy = dy - dot*rotate[2][1];
|
|
dz = dz - dot*rotate[2][2];
|
|
r = sqrt(dx*dx + dy*dy + dz*dz);
|
|
rotate[0][0] = dx / r;
|
|
rotate[0][1] = dy / r;
|
|
rotate[0][2] = dz / r;
|
|
|
|
// 3-fold frame rotation matrix elements for z- and x-axes
|
|
|
|
} else if (polaxe[i] == THREEFOLD) {
|
|
dx = x[iz][0] - xi;
|
|
dy = x[iz][1] - yi;
|
|
dz = x[iz][2] - zi;
|
|
r = sqrt(dx*dx + dy*dy + dz*dz);
|
|
dx1 = dx / r;
|
|
dy1 = dy / r;
|
|
dz1 = dz / r;
|
|
dx = x[ix][0] - xi;
|
|
dy = x[ix][1] - yi;
|
|
dz = x[ix][2] - zi;
|
|
r = sqrt(dx*dx + dy*dy + dz*dz);
|
|
dx2 = dx / r;
|
|
dy2 = dy / r;
|
|
dz2 = dz / r;
|
|
dx = x[iy][0] - xi;
|
|
dy = x[iy][1] - yi;
|
|
dz = x[iy][2] - zi;
|
|
r = sqrt(dx*dx + dy*dy + dz*dz);
|
|
dx3 = dx / r;
|
|
dy3 = dy / r;
|
|
dz3 = dz / r;
|
|
dx = dx1 + dx2 + dx3;
|
|
dy = dy1 + dy2 + dy3;
|
|
dz = dz1 + dz2 + dz3;
|
|
r = sqrt(dx*dx + dy*dy + dz*dz);
|
|
rotate[2][0] = dx / r;
|
|
rotate[2][1] = dy / r;
|
|
rotate[2][2] = dz / r;
|
|
dot = dx2*rotate[2][0] + dy2*rotate[2][1] + dz2*rotate[2][2];
|
|
dx = dx2 - dot*rotate[2][0];
|
|
dy = dy2 - dot*rotate[2][1];
|
|
dz = dz2 - dot*rotate[2][2];
|
|
r = sqrt(dx*dx + dy*dy + dz*dz);
|
|
rotate[0][0] = dx / r;
|
|
rotate[0][1] = dy / r;
|
|
rotate[0][2] = dz / r;
|
|
}
|
|
|
|
// finally, find rotation matrix elements for the y-axis
|
|
|
|
rotate[1][0] = rotate[0][2]*rotate[2][1] - rotate[0][1]*rotate[2][2];
|
|
rotate[1][1] = rotate[0][0]*rotate[2][2] - rotate[0][2]*rotate[2][0];
|
|
rotate[1][2] = rotate[0][1]*rotate[2][0] - rotate[0][0]*rotate[2][1];
|
|
}
|
|
|
|
/* ----------------------------------------------------------------------
|
|
rotsite rotates the local frame atomic multipoles at a
|
|
specified site into the global coordinate frame by applying a rotation matrix
|
|
called every timestep for each atom Isite
|
|
------------------------------------------------------------------------- */
|
|
|
|
void PairAmoeba::rotsite(int isite)
|
|
{
|
|
int i,j,k,m;
|
|
double mp[3][3];
|
|
double rp[3][3];
|
|
|
|
double **pole = fixpole->astore;
|
|
|
|
// monopoles have the same value in any coordinate frame
|
|
|
|
rpole[isite][0] = pole[isite][0];
|
|
|
|
// rotate the dipoles to the global coordinate frame
|
|
|
|
for (i = 1; i <= 3; i++) {
|
|
rpole[isite][i] = 0.0;
|
|
for (j = 1; j <= 3; j++)
|
|
rpole[isite][i] += pole[isite][j] * rotate[j-1][i-1];
|
|
}
|
|
|
|
// rotate the quadrupoles to the global coordinate frame
|
|
|
|
k = 4;
|
|
for (i = 0; i < 3; i++) {
|
|
for (j = 0; j < 3; j++) {
|
|
mp[j][i] = pole[isite][k];
|
|
rp[j][i] = 0.0;
|
|
k++;
|
|
}
|
|
}
|
|
|
|
for (i = 0; i < 3; i++) {
|
|
for (j = 0; j < 3; j++) {
|
|
if (j < i) rp[j][i] = rp[i][j];
|
|
else {
|
|
for (k = 0; k < 3; k++)
|
|
for (m = 0; m < 3; m++)
|
|
rp[j][i] += rotate[k][i]*rotate[m][j] * mp[m][k];
|
|
}
|
|
}
|
|
}
|
|
|
|
k = 4;
|
|
for (i = 0; i < 3; i++) {
|
|
for (j = 0; j < 3; j++) {
|
|
rpole[isite][k] = rp[j][i];
|
|
k++;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* ----------------------------------------------------------------------
|
|
scan standard neighbor list and make it compatible with 1-5 neighbors
|
|
if IJ entry is a 1-2,1-3,1-4 neighbor then adjust offset to SBBITS15
|
|
else scan special15 to see if a 1-5 neighbor and adjust offset to SBBITS15
|
|
else do nothing to IJ entry
|
|
------------------------------------------------------------------------- */
|
|
|
|
void PairAmoeba::add_onefive_neighbors()
|
|
{
|
|
int i,j,k,ii,jj,which,inum,jnum,n15;
|
|
tagint jtag;
|
|
int *ilist,*jlist,*numneigh,**firstneigh;
|
|
tagint *list15;
|
|
|
|
// test for overflow of reduced allowed size of neighbor list
|
|
|
|
if (atom->nlocal + atom->nghost > NEIGHMASK15)
|
|
error->one(FLERR,"Pair amoeba neighbor list overflow");
|
|
|
|
// neigh list
|
|
|
|
inum = list->inum;
|
|
ilist = list->ilist;
|
|
numneigh = list->numneigh;
|
|
firstneigh = list->firstneigh;
|
|
|
|
// reset special neighbor flags to include 1-5 neighbors
|
|
|
|
tagint *tag = atom->tag;
|
|
int *nspecial15 = atom->nspecial15;
|
|
tagint **special15 = atom->special15;
|
|
|
|
for (ii = 0; ii < inum; ii++) {
|
|
i = ilist[ii];
|
|
n15 = nspecial15[i];
|
|
list15 = special15[i];
|
|
jlist = firstneigh[i];
|
|
jnum = numneigh[i];
|
|
|
|
for (jj = 0; jj < jnum; jj++) {
|
|
j = jlist[jj];
|
|
which = j >> SBBITS & 3;
|
|
j &= NEIGHMASK;
|
|
jtag = tag[j];
|
|
|
|
if (!which) {
|
|
for (k = 0; k < n15; k++) {
|
|
if (list15[k] == jtag) {
|
|
which = 4;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (which) jlist[jj] = j ^ (which << SBBITS15);
|
|
}
|
|
}
|
|
}
|
|
|
|
/* ----------------------------------------------------------------------
|
|
update local indices of hydrogen neighbors for owned and ghost atoms
|
|
red2local = used for offset of hydrogen positions in Vdwl term
|
|
called on reneighboring steps, only for AMOEBA
|
|
------------------------------------------------------------------------- */
|
|
|
|
void PairAmoeba::find_hydrogen_neighbors()
|
|
{
|
|
int index;
|
|
tagint id;
|
|
|
|
// grab current ptr for redID
|
|
// redID[i] = atom ID that atom I is bonded to
|
|
// red2local[i] = local index of that atom
|
|
// for furthest away ghost atoms, bond partner can be missing
|
|
// in that case red2local = -1, but only an error if accessed in hal()
|
|
|
|
double *redID = atom->dvector[index_redID];
|
|
|
|
int nlocal = atom->nlocal;
|
|
int nall = nlocal + atom->nghost;
|
|
|
|
for (int i = 0; i < nall; i++) {
|
|
if (redID[i] == 0.0) red2local[i] = i;
|
|
else {
|
|
id = (tagint) ubuf(redID[i]).i;
|
|
index = atom->map(id);
|
|
if (index >= 0) index = domain->closest_image(i,index);
|
|
red2local[i] = index;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* ----------------------------------------------------------------------
|
|
update local indices of bond topology neighbors for owned atoms
|
|
xyz axis2local = used for multipole orientation
|
|
called on reneighboring steps
|
|
------------------------------------------------------------------------- */
|
|
|
|
void PairAmoeba::find_multipole_neighbors()
|
|
{
|
|
int index;
|
|
tagint xaxisID,yaxisID,zaxisID;
|
|
|
|
// grab current pts for xaxis,yaxis,zaxis
|
|
// xyzaxis[i] = atom IDs that atom I uses for its multipole orientation
|
|
// can be zero if not used, in which case set local index to self
|
|
// yaxis can be negative, in which case use absolute value
|
|
|
|
double **xyzaxis = atom->darray[index_xyzaxis];
|
|
|
|
int nlocal = atom->nlocal;
|
|
int nmissing = 0;
|
|
|
|
for (int i = 0; i < nlocal; i++) {
|
|
xaxisID = (tagint) ubuf(xyzaxis[i][0]).i;
|
|
yaxisID = (tagint) ubuf(xyzaxis[i][1]).i;
|
|
zaxisID = (tagint) ubuf(xyzaxis[i][2]).i;
|
|
|
|
if (xaxisID) {
|
|
index = atom->map(xaxisID);
|
|
if (index == -1) nmissing++;
|
|
else {
|
|
index = domain->closest_image(i,index);
|
|
xaxis2local[i] = index;
|
|
}
|
|
} else xaxis2local[i] = i;
|
|
|
|
if (yaxisID) {
|
|
if (xyzaxis[i][1] < 0) yaxisID = -yaxisID;
|
|
index = atom->map(yaxisID);
|
|
if (index == -1) nmissing++;
|
|
else {
|
|
index = domain->closest_image(i,index);
|
|
yaxis2local[i] = index;
|
|
}
|
|
} else yaxis2local[i] = i;
|
|
|
|
if (zaxisID) {
|
|
index = atom->map(zaxisID);
|
|
if (index == -1) nmissing++;
|
|
else {
|
|
index = domain->closest_image(i,index);
|
|
zaxis2local[i] = index;
|
|
}
|
|
} else zaxis2local[i] = i;
|
|
}
|
|
|
|
// error check on missing neighbors
|
|
|
|
int nmissing_all;
|
|
MPI_Allreduce(&nmissing,&nmissing_all,1,MPI_INT,MPI_SUM,world);
|
|
if (nmissing_all)
|
|
error->all(FLERR, "Pair amoeba: {} multipole neighbors missing\n", nmissing_all);
|
|
}
|
|
|
|
/* ----------------------------------------------------------------------
|
|
torque2force takes the torque values on a single site defined by
|
|
a local coordinate frame and converts to Cartesian forces on
|
|
the original site and sites specifying the local frame, also
|
|
gives the x,y,z-force components needed for virial computation
|
|
|
|
force distribution for the 3-fold local frame by Chao Lu,
|
|
Ponder Lab, Washington University, July 2016
|
|
|
|
literature reference:
|
|
|
|
P. L. Popelier and A. J. Stone, "Formulae for the First and
|
|
Second Derivatives of Anisotropic Potentials with Respect to
|
|
Geometrical Parameters", Molecular Physics, 82, 411-425 (1994)
|
|
|
|
C. Segui, L. G. Pedersen and T. A. Darden, "Towards an Accurate
|
|
Representation of Electrostatics in Classical Force Fields:
|
|
Efficient Implementation of Multipolar Interactions in
|
|
Biomolecular Simulations", Journal of Chemical Physics, 120,
|
|
73-87 (2004)
|
|
|
|
i = single site = local atom index
|
|
trq = torque on that atom
|
|
frc xyz = returned xyz force components
|
|
f = force vector for local atoms, multiple atoms will be updated
|
|
------------------------------------------------------------------------- */
|
|
|
|
void PairAmoeba::torque2force(int i, double *trq,
|
|
double *frcx, double *frcy, double *frcz,
|
|
double **f)
|
|
{
|
|
int j;
|
|
int ia,ib,ic,id;
|
|
int axetyp;
|
|
double du,dv,dw,dot;
|
|
double usiz,vsiz,wsiz;
|
|
double psiz,rsiz,ssiz;
|
|
double t1siz,t2siz;
|
|
double uvsiz,uwsiz,vwsiz;
|
|
double ursiz,ussiz;
|
|
double vssiz,wssiz;
|
|
double delsiz,dphiddel;
|
|
double uvcos,urcos;
|
|
double vscos,wscos;
|
|
double upcos,vpcos,wpcos;
|
|
double rwcos,rucos,rvcos;
|
|
double ut1cos,ut2cos;
|
|
double uvsin,ursin;
|
|
double vssin,wssin;
|
|
double rwsin,rusin,rvsin;
|
|
double ut1sin,ut2sin;
|
|
double dphidu,dphidv,dphidw;
|
|
double dphidr,dphids;
|
|
double u[3],v[3],w[3];
|
|
double p[3],r[3],s[3];
|
|
double t1[3],t2[3];
|
|
double uv[3],uw[3],vw[3];
|
|
double ur[3],us[3];
|
|
double vs[3],ws[3];
|
|
double del[3],eps[3];
|
|
|
|
double **x = atom->x;
|
|
|
|
// zero out force components on local frame-defining atoms
|
|
|
|
for (j = 0; j < 3; j++) {
|
|
frcz[j] = 0.0;
|
|
frcx[j] = 0.0;
|
|
frcy[j] = 0.0;
|
|
}
|
|
|
|
// get the local frame type and the frame-defining atoms
|
|
|
|
int *polaxe = atom->ivector[index_polaxe];
|
|
axetyp = polaxe[i];
|
|
if (axetyp == NOFRAME) return;
|
|
|
|
ia = zaxis2local[i];
|
|
ib = i;
|
|
ic = xaxis2local[i];
|
|
id = yaxis2local[i];
|
|
|
|
// construct the three rotation axes for the local frame
|
|
|
|
u[0] = x[ia][0] - x[ib][0];
|
|
u[1] = x[ia][1] - x[ib][1];
|
|
u[2] = x[ia][2] - x[ib][2];
|
|
usiz = sqrt(u[0]*u[0] + u[1]*u[1] + u[2]*u[2]);
|
|
|
|
if (axetyp != ZONLY) {
|
|
v[0] = x[ic][0] - x[ib][0];
|
|
v[1] = x[ic][1] - x[ib][1];
|
|
v[2] = x[ic][2] - x[ib][2];
|
|
vsiz = sqrt(v[0]*v[0] + v[1]*v[1] + v[2]*v[2]);
|
|
} else {
|
|
v[0] = 1.0;
|
|
v[1] = 0.0;
|
|
v[2] = 0.0;
|
|
vsiz = 1.0;
|
|
dot = u[0] / usiz;
|
|
if (fabs(dot) > 0.866) {
|
|
v[0] = 0.0;
|
|
v[1] = 1.0;
|
|
}
|
|
}
|
|
|
|
if (axetyp == ZBISECT || axetyp == THREEFOLD) {
|
|
w[0] = x[id][0] - x[ib][0];
|
|
w[1] = x[id][1] - x[ib][1];
|
|
w[2] = x[id][2] - x[ib][2];
|
|
} else {
|
|
w[0] = u[1]*v[2] - u[2]*v[1];
|
|
w[1] = u[2]*v[0] - u[0]*v[2];
|
|
w[2] = u[0]*v[1] - u[1]*v[0];
|
|
}
|
|
|
|
wsiz = sqrt(w[0]*w[0] + w[1]*w[1] + w[2]*w[2]);
|
|
|
|
for (j = 0; j < 3; j++) {
|
|
u[j] /= usiz;
|
|
v[j] /= vsiz;
|
|
w[j] /= wsiz;
|
|
}
|
|
|
|
// build some additional axes needed for the Z-Bisect method
|
|
|
|
if (axetyp == ZBISECT) {
|
|
r[0] = v[0] + w[0];
|
|
r[1] = v[1] + w[1];
|
|
r[2] = v[2] + w[2];
|
|
rsiz = sqrt(r[0]*r[0] + r[1]*r[1] + r[2]*r[2]);
|
|
s[0] = u[1]*r[2] - u[2]*r[1];
|
|
s[1] = u[2]*r[0] - u[0]*r[2];
|
|
s[2] = u[0]*r[1] - u[1]*r[0];
|
|
ssiz = sqrt(s[0]*s[0] + s[1]*s[1] + s[2]*s[2]);
|
|
for (j = 0; j < 3; j++) {
|
|
r[j] /= rsiz;
|
|
s[j] /= ssiz;
|
|
}
|
|
}
|
|
|
|
// find the perpendicular and angle for each pair of axes
|
|
|
|
uv[0] = v[1]*u[2] - v[2]*u[1];
|
|
uv[1] = v[2]*u[0] - v[0]*u[2];
|
|
uv[2] = v[0]*u[1] - v[1]*u[0];
|
|
uvsiz = sqrt(uv[0]*uv[0] + uv[1]*uv[1] + uv[2]*uv[2]);
|
|
uw[0] = w[1]*u[2] - w[2]*u[1];
|
|
uw[1] = w[2]*u[0] - w[0]*u[2];
|
|
uw[2] = w[0]*u[1] - w[1]*u[0];
|
|
uwsiz = sqrt(uw[0]*uw[0] + uw[1]*uw[1] + uw[2]*uw[2]);
|
|
vw[0] = w[1]*v[2] - w[2]*v[1];
|
|
vw[1] = w[2]*v[0] - w[0]*v[2];
|
|
vw[2] = w[0]*v[1] - w[1]*v[0];
|
|
vwsiz = sqrt(vw[0]*vw[0] + vw[1]*vw[1] + vw[2]*vw[2]);
|
|
for (j = 0; j < 3; j++) {
|
|
uv[j] /= uvsiz;
|
|
uw[j] /= uwsiz;
|
|
vw[j] /= vwsiz;
|
|
}
|
|
|
|
if (axetyp == ZBISECT) {
|
|
ur[0] = r[1]*u[2] - r[2]*u[1];
|
|
ur[1] = r[2]*u[0] - r[0]*u[2];
|
|
ur[2] = r[0]*u[1] - r[1]*u[0];
|
|
ursiz = sqrt(ur[0]*ur[0] + ur[1]*ur[1] + ur[2]*ur[2]);
|
|
us[0] = s[1]*u[2] - s[2]*u[1];
|
|
us[1] = s[2]*u[0] - s[0]*u[2];
|
|
us[2] = s[0]*u[1] - s[1]*u[0];
|
|
ussiz = sqrt(us[0]*us[0] + us[1]*us[1] + us[2]*us[2]);
|
|
vs[0] = s[1]*v[2] - s[2]*v[1];
|
|
vs[1] = s[2]*v[0] - s[0]*v[2];
|
|
vs[2] = s[0]*v[1] - s[1]*v[0];
|
|
vssiz = sqrt(vs[0]*vs[0] + vs[1]*vs[1] + vs[2]*vs[2]);
|
|
ws[0] = s[1]*w[2] - s[2]*w[1];
|
|
ws[1] = s[2]*w[0] - s[0]*w[2];
|
|
ws[2] = s[0]*w[1] - s[1]*w[0];
|
|
wssiz = sqrt(ws[0]*ws[0] + ws[1]*ws[1] + ws[2]*ws[2]);
|
|
for (j = 0; j < 3; j++) {
|
|
ur[j] /= ursiz;
|
|
us[j] /= ussiz;
|
|
vs[j] /= vssiz;
|
|
ws[j] /= wssiz;
|
|
}
|
|
}
|
|
|
|
// get sine and cosine of angles between the rotation axes
|
|
|
|
uvcos = u[0]*v[0] + u[1]*v[1] + u[2]*v[2];
|
|
uvsin = sqrt(1.0 - uvcos*uvcos);
|
|
if (axetyp == ZBISECT) {
|
|
urcos = u[0]*r[0] + u[1]*r[1] + u[2]*r[2];
|
|
ursin = sqrt(1.0 - urcos*urcos);
|
|
vscos = v[0]*s[0] + v[1]*s[1] + v[2]*s[2];
|
|
vssin = sqrt(1.0 - vscos*vscos);
|
|
wscos = w[0]*s[0] + w[1]*s[1] + w[2]*s[2];
|
|
wssin = sqrt(1.0 - wscos*wscos);
|
|
}
|
|
|
|
// compute the projection of v and w onto the ru-plane
|
|
|
|
if (axetyp == ZBISECT) {
|
|
for (j = 0; j < 3; j++) {
|
|
t1[j] = v[j] - s[j]*vscos;
|
|
t2[j] = w[j] - s[j]*wscos;
|
|
}
|
|
t1siz = sqrt(t1[0]*t1[0] + t1[1]*t1[1] + t1[2]*t1[2]);
|
|
t2siz = sqrt(t2[0]*t2[0] + t2[1]*t2[1] + t2[2]*t2[2]);
|
|
for (j = 0; j < 3; j++) {
|
|
t1[j] /= t1siz;
|
|
t2[j] /= t2siz;
|
|
}
|
|
ut1cos = u[0]*t1[0] + u[1]*t1[1] + u[2]*t1[2];
|
|
ut1sin = sqrt(1.0 - ut1cos*ut1cos);
|
|
ut2cos = u[0]*t2[0] + u[1]*t2[1] + u[2]*t2[2];
|
|
ut2sin = sqrt(1.0 - ut2cos*ut2cos);
|
|
}
|
|
|
|
// negative of dot product of torque with unit vectors gives
|
|
// result of infinitesimal rotation along these vectors
|
|
|
|
dphidu = -trq[0]*u[0] - trq[1]*u[1] - trq[2]*u[2];
|
|
dphidv = -trq[0]*v[0] - trq[1]*v[1] - trq[2]*v[2];
|
|
dphidw = -trq[0]*w[0] - trq[1]*w[1] - trq[2]*w[2];
|
|
if (axetyp == ZBISECT) {
|
|
dphidr = -trq[0]*r[0] - trq[1]*r[1] - trq[2]*r[2];
|
|
dphids = -trq[0]*s[0] - trq[1]*s[1] - trq[2]*s[2];
|
|
}
|
|
|
|
// force distribution for the Z-Only local coordinate method
|
|
|
|
if (axetyp == ZONLY) {
|
|
for (j = 0; j < 3; j++) {
|
|
du = uv[j]*dphidv/(usiz*uvsin) + uw[j]*dphidw/usiz;
|
|
f[ia][j] -= du;
|
|
f[ib][j] += du;
|
|
frcz[j] += du;
|
|
}
|
|
|
|
// force distribution for the Z-then-X local coordinate method
|
|
|
|
} else if (axetyp == ZTHENX) {
|
|
for (j = 0; j < 3; j++) {
|
|
du = uv[j]*dphidv/(usiz*uvsin) + uw[j]*dphidw/usiz;
|
|
dv = -uv[j]*dphidu/(vsiz*uvsin);
|
|
f[ia][j] -= du;
|
|
f[ic][j] -= dv;
|
|
f[ib][j] += du + dv;
|
|
frcz[j] += du;
|
|
frcx[j] += dv;
|
|
}
|
|
|
|
// force distribution for the Bisector local coordinate method
|
|
|
|
} else if (axetyp == BISECTOR) {
|
|
for (j = 0; j < 3; j++) {
|
|
du = uv[j]*dphidv/(usiz*uvsin) + 0.5*uw[j]*dphidw/usiz;
|
|
dv = -uv[j]*dphidu/(vsiz*uvsin) + 0.5*vw[j]*dphidw/vsiz;
|
|
f[ia][j] -= du;
|
|
f[ic][j] -= dv;
|
|
f[ib][j] += du + dv;
|
|
frcz[j] += du;
|
|
frcx[j] += dv;
|
|
}
|
|
|
|
// force distribution for the Z-Bisect local coordinate method
|
|
|
|
} else if (axetyp == ZBISECT) {
|
|
for (j = 0; j < 3; j++) {
|
|
du = ur[j]*dphidr/(usiz*ursin) + us[j]*dphids/usiz;
|
|
dv = (vssin*s[j]-vscos*t1[j])*dphidu / (vsiz*(ut1sin+ut2sin));
|
|
dw = (wssin*s[j]-wscos*t2[j])*dphidu / (wsiz*(ut1sin+ut2sin));
|
|
f[ia][j] -= du;
|
|
f[ic][j] -= dv;
|
|
f[id][j] -= dw;
|
|
f[ib][j] += du + dv + dw;
|
|
frcz[j] += du;
|
|
frcx[j] += dv;
|
|
frcy[j] += dw;
|
|
}
|
|
|
|
// force distribution for the 3-Fold local coordinate method
|
|
|
|
} else if (axetyp == THREEFOLD) {
|
|
p[0] = u[0] + v[0] + w[0];
|
|
p[1] = u[1] + v[1] + w[1];
|
|
p[2] = u[2] + v[2] + w[2];
|
|
psiz = sqrt(p[0]*p[0] + p[1]*p[1] + p[2]*p[2]) ;
|
|
for (j = 0; j < 3; j++) p[j] /= psiz;
|
|
|
|
wpcos = w[0]*p[0] + w[1]*p[1] + w[2]*p[2];
|
|
upcos = u[0]*p[0] + u[1]*p[1] + u[2]*p[2];
|
|
vpcos = v[0]*p[0] + v[1]*p[1] + v[2]*p[2];
|
|
r[0] = u[0] + v[0];
|
|
r[1] = u[1] + v[1];
|
|
r[2] = u[2] + v[2];
|
|
rsiz = sqrt(r[0]*r[0] + r[1]*r[1] + r[2]*r[2]);
|
|
for (j = 0; j < 3; j++) r[j] /= rsiz;
|
|
|
|
rwcos = r[0]*w[0] + r[1]*w[1] + r[2]*w[2];
|
|
rwsin = sqrt(1.0 - rwcos*rwcos);
|
|
dphidr = -trq[0]*r[0] - trq[1]*r[1] - trq[2]*r[2];
|
|
del[0] = r[1]*w[2] - r[2]*w[1];
|
|
del[1] = r[2]*w[0] - r[0]*w[2] ;
|
|
del[2] = r[0]*w[1] - r[1]*w[0];
|
|
delsiz = sqrt(del[0]*del[0] + del[1]*del[1] + del[2]*del[2]);
|
|
for (j = 0; j < 3; j++) del[j] /= delsiz;
|
|
|
|
dphiddel = -trq[0]*del[0] - trq[1]*del[1] - trq[2]*del[2];
|
|
eps[0] = del[1]*w[2] - del[2]*w[1];
|
|
eps[1] = del[2]*w[0] - del[0]*w[2];
|
|
eps[2] = del[0]*w[1] - del[1]*w[0];
|
|
for (j = 0; j < 3; j++) {
|
|
dw = del[j]*dphidr/(wsiz*rwsin) + eps[j]*dphiddel*wpcos/(wsiz*psiz);
|
|
f[id][j] -= dw;
|
|
f[ib][j] += dw;
|
|
frcy[j] += dw;
|
|
}
|
|
r[0] = v[0] + w[0];
|
|
r[1] = v[1] + w[1];
|
|
r[2] = v[2] + w[2];
|
|
rsiz = sqrt(r[0]*r[0] + r[1]*r[1] + r[2]*r[2]);
|
|
for (j = 0; j < 3; j++) r[j] /= rsiz;
|
|
|
|
rucos = r[0]*u[0] + r[1]*u[1] + r[2]*u[2];
|
|
rusin = sqrt(1.0 - rucos*rucos) ;
|
|
dphidr = -trq[0]*r[0] - trq[1]*r[1] - trq[2]*r[2];
|
|
del[0] = r[1]*u[2] - r[2]*u[1];
|
|
del[1] = r[2]*u[0] - r[0]*u[2];
|
|
del[2] = r[0]*u[1] - r[1]*u[0];
|
|
delsiz = sqrt(del[0]*del[0] + del[1]*del[1] + del[2]*del[2]);
|
|
for (j = 0; j < 3; j++) del[j] /= delsiz;
|
|
|
|
dphiddel = -trq[0]*del[0] - trq[1]*del[1] - trq[2]*del[2];
|
|
eps[0] = del[1]*u[2] - del[2]*u[1];
|
|
eps[1] = del[2]*u[0] - del[0]*u[2];
|
|
eps[2] = del[0]*u[1] - del[1]*u[0];
|
|
for (j = 0; j < 3; j++) {
|
|
du = del[j]*dphidr/(usiz*rusin) + eps[j]*dphiddel*upcos/(usiz*psiz);
|
|
f[ia][j] -= du;
|
|
f[ib][j] += du;
|
|
frcz[j] += du;
|
|
}
|
|
r[0] = u[0] + w[0];
|
|
r[1] = u[1] + w[1];
|
|
r[2] = u[2] + w[2];
|
|
rsiz = sqrt(r[0]*r[0] + r[1]*r[1] + r[2]*r[2]);
|
|
for (j = 0; j < 3; j++) r[j] /= rsiz;
|
|
|
|
rvcos = r[0]*v[0] + r[1]*v[1] + r[2]*v[2] ;
|
|
rvsin = sqrt(1.0 - rvcos*rvcos);
|
|
dphidr = -trq[0]*r[0] - trq[1]*r[1] - trq[2]*r[2];
|
|
del[0] = r[1]*v[2] - r[2]*v[1];
|
|
del[1] = r[2]*v[0] - r[0]*v[2];
|
|
del[2] = r[0]*v[1] - r[1]*v[0];
|
|
delsiz = sqrt(del[0]*del[0] + del[1]*del[1] + del[2]*del[2]);
|
|
for (j = 0; j < 3; j++) del[j] /= delsiz;
|
|
|
|
dphiddel = -trq[0]*del[0] - trq[1]*del[1] - trq[2]*del[2];
|
|
eps[0] = del[1]*v[2] - del[2]*v[1];
|
|
eps[1] = del[2]*v[0] - del[0]*v[2];
|
|
eps[2] = del[0]*v[1] - del[1]*v[0];
|
|
for (j = 0; j < 3; j++) {
|
|
dv = del[j]*dphidr/(vsiz*rvsin) + eps[j]*dphiddel*vpcos/(vsiz*psiz);
|
|
f[ic][j] -= dv;
|
|
f[ib][j] += dv;
|
|
frcx[j] += dv;
|
|
}
|
|
}
|
|
}
|