Files
lammps/src/fix_wall_table.cpp

473 lines
15 KiB
C++

/* ----------------------------------------------------------------------
LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator
https://www.lammps.org/, Sandia National Laboratories
LAMMPS development team: developers@lammps.org
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 "fix_wall_table.h"
#include "atom.h"
#include "comm.h"
#include "error.h"
#include "memory.h"
#include "table_file_reader.h"
#include "tokenizer.h"
#include <cstring>
using namespace LAMMPS_NS;
enum { NONE, LINEAR, SPLINE };
static constexpr double BIGNUM = 1.0e300;
/* ---------------------------------------------------------------------- */
FixWallTable::FixWallTable(LAMMPS *lmp, int narg, char **arg) :
FixWall(lmp, narg, arg), tables(nullptr)
{
dynamic_group_allow = 1;
tabstyle = NONE;
if (strcmp(arg[3], "linear") == 0)
tabstyle = LINEAR;
else if (strcmp(arg[3], "spline") == 0)
tabstyle = SPLINE;
else
error->all(FLERR, "Unknown table style {} in fix {}", arg[3], style);
tablength = utils::inumeric(FLERR, arg[4], false, lmp);
if (tablength < 2) error->all(FLERR, "Illegal number of fix {} table entries", style);
}
/* ---------------------------------------------------------------------- */
void FixWallTable::post_constructor()
{
memory->sfree(tables);
tables = (Table *) memory->smalloc(nwall * sizeof(Table), "wall:tables");
for (int m = 0; m < nwall; ++m) {
Table &tb = tables[m];
null_table(tb);
if (comm->me == 0) read_table(tb, fstr[m], kstr[m]);
bcast_table(tb);
// error check on table parameters
if (tb.ninput <= 1) error->all(FLERR, "Invalid fix {} table length: {}", style, tb.ninput);
tb.lo = tb.rfile[0];
tb.hi = tb.rfile[tb.ninput - 1];
if (tb.lo >= tb.hi) error->all(FLERR, "Fix {} table distance values do not increase", style);
if (cutoff[m] > tb.hi)
error->all(FLERR, "Fix {} wall cutoff {} is larger than table outer cutoff {}", style,
cutoff[m], tb.hi);
// spline read-in data and compute r,e,f arrays within table
spline_table(tb);
compute_table(tb);
}
}
/* ---------------------------------------------------------------------- */
FixWallTable::~FixWallTable()
{
for (int m = 0; m < nwall; m++) free_table(tables[m]);
memory->sfree(tables);
}
/* ----------------------------------------------------------------------
compute the potential energy offset so it can be shifted to zero at the cutoff
------------------------------------------------------------------------- */
void FixWallTable::precompute(int m)
{
double u, mdu;
// always use linear interpolation to determine the offset, since we may be at the
// outer table cutoff and spline interpolation would cause out-of-bounds array accesses
int savedtabstyle = tabstyle;
tabstyle = LINEAR;
uf_lookup(m, cutoff[m], u, mdu);
offset[m] = u;
tabstyle = savedtabstyle;
}
/* ----------------------------------------------------------------------
interaction of all particles in group with a wall
m = index of wall coeffs
which = xlo,xhi,ylo,yhi,zlo,zhi
error if any particle is on or behind wall
------------------------------------------------------------------------- */
void FixWallTable::wall_particle(int m, int which, double coord)
{
double delta, fwall, vn, u, mdu;
double **x = atom->x;
double **f = atom->f;
int *mask = atom->mask;
int nlocal = atom->nlocal;
int dim = which / 2;
int side = which % 2;
if (side == 0) side = -1;
int onflag = 0;
for (int i = 0; i < nlocal; i++)
if (mask[i] & groupbit) {
if (side < 0)
delta = x[i][dim] - coord;
else
delta = coord - x[i][dim];
if (delta >= cutoff[m]) continue;
if (delta <= 0.0) {
onflag = 1;
continue;
}
uf_lookup(m, delta, u, mdu);
fwall = side * mdu;
f[i][dim] -= fwall;
ewall[0] += u - offset[m];
ewall[m + 1] += fwall;
if (evflag) {
if (side < 0)
vn = -fwall * delta;
else
vn = fwall * delta;
v_tally(dim, i, vn);
}
}
if (onflag) error->one(FLERR, "Particle on or inside fix {} surface", style);
}
/* ---------------------------------------------------------------------- */
void FixWallTable::null_table(Table &tb)
{
tb.rfile = tb.efile = tb.ffile = nullptr;
tb.e2file = tb.f2file = nullptr;
tb.r = tb.e = tb.de = nullptr;
tb.f = tb.df = tb.e2 = tb.f2 = nullptr;
}
/* ---------------------------------------------------------------------- */
void FixWallTable::free_table(Table &tb)
{
memory->destroy(tb.rfile);
memory->destroy(tb.efile);
memory->destroy(tb.ffile);
memory->destroy(tb.e2file);
memory->destroy(tb.f2file);
memory->destroy(tb.r);
memory->destroy(tb.e);
memory->destroy(tb.de);
memory->destroy(tb.f);
memory->destroy(tb.df);
memory->destroy(tb.e2);
memory->destroy(tb.f2);
}
/* ---------------------------------------------------------------------- */
void FixWallTable::read_table(Table &tb, const std::string &tabfile, const std::string &keyword)
{
TableFileReader reader(lmp, tabfile, "fix wall/table");
char *line = reader.find_section_start(keyword);
if (!line) error->one(FLERR, "Did not find keyword {} in table file", keyword);
// read args on 2nd line of section
// allocate table arrays for file values
line = reader.next_line();
param_extract(tb, line);
memory->create(tb.rfile, tb.ninput, "wall:rfile");
memory->create(tb.efile, tb.ninput, "wall:efile");
memory->create(tb.ffile, tb.ninput, "wall:ffile");
// read r,e,f table values from file
reader.skip_line();
for (int i = 0; i < tb.ninput; i++) {
line = reader.next_line();
if (!line)
error->one(FLERR, "Data missing when parsing wall table '{}' line {} of {}.", keyword, i + 1,
tb.ninput);
try {
ValueTokenizer values(line);
values.next_int();
tb.rfile[i] = values.next_double();
tb.efile[i] = values.next_double();
tb.ffile[i] = values.next_double();
} catch (TokenizerException &e) {
error->one(FLERR, "Error parsing wall table '{}' line {} of {}. {}\nLine was: {}", keyword,
i + 1, tb.ninput, e.what(), line);
}
}
// warn if force != dE/dr at any point that is not an inflection point
// check via secant approximation to dE/dr
// skip two end points since do not have surrounding secants
// inflection point is where curvature changes sign
double r, e, f, rprev, rnext, eprev, enext, fleft, fright;
int ferror = 0;
for (int i = 1; i < tb.ninput - 1; i++) {
r = tb.rfile[i];
rprev = tb.rfile[i - 1];
rnext = tb.rfile[i + 1];
e = tb.efile[i];
eprev = tb.efile[i - 1];
enext = tb.efile[i + 1];
f = tb.ffile[i];
fleft = -(e - eprev) / (r - rprev);
fright = -(enext - e) / (rnext - r);
if (f < fleft && f < fright) ferror++;
if (f > fleft && f > fright) ferror++;
}
if (ferror)
error->warning(FLERR,
"{} of {} force values in table are inconsistent with -dE/dr.\n"
"WARNING: Should only be flagged at inflection points",
ferror, tb.ninput);
}
/* ----------------------------------------------------------------------
build spline representation of e,f over entire range of read-in table
this function sets these values in e2file,f2file
------------------------------------------------------------------------- */
void FixWallTable::spline_table(Table &tb)
{
memory->create(tb.e2file, tb.ninput, "wall:e2file");
memory->create(tb.f2file, tb.ninput, "wall:f2file");
double ep0 = -tb.ffile[0];
double epn = -tb.ffile[tb.ninput - 1];
spline(tb.rfile, tb.efile, tb.ninput, ep0, epn, tb.e2file);
if (tb.fpflag == 0) {
tb.fplo = (tb.ffile[1] - tb.ffile[0]) / (tb.rfile[1] - tb.rfile[0]);
tb.fphi = (tb.ffile[tb.ninput - 1] - tb.ffile[tb.ninput - 2]) /
(tb.rfile[tb.ninput - 1] - tb.rfile[tb.ninput - 2]);
}
double fp0 = tb.fplo;
double fpn = tb.fphi;
spline(tb.rfile, tb.ffile, tb.ninput, fp0, fpn, tb.f2file);
}
/* ----------------------------------------------------------------------
compute r,e,f vectors from splined values
------------------------------------------------------------------------- */
void FixWallTable::compute_table(Table &tb)
{
// delta = table spacing for N-1 bins
int tlm1 = tablength - 1;
tb.delta = (tb.hi - tb.lo) / tlm1;
tb.invdelta = 1.0 / tb.delta;
tb.deltasq6 = tb.delta * tb.delta / 6.0;
// N-1 evenly spaced bins in r from min to max
// r,e,f = value at lower edge of bin
// de,df values = delta values of e,f
// r,e,f are N in length so de,df arrays can compute difference
memory->create(tb.r, tablength, "wall:r");
memory->create(tb.e, tablength, "wall:e");
memory->create(tb.de, tablength, "wall:de");
memory->create(tb.f, tablength, "wall:f");
memory->create(tb.df, tablength, "wall:df");
memory->create(tb.e2, tablength, "wall:e2");
memory->create(tb.f2, tablength, "wall:f2");
double a;
for (int i = 0; i < tablength; i++) {
a = tb.lo + i * tb.delta;
tb.r[i] = a;
tb.e[i] = splint(tb.rfile, tb.efile, tb.e2file, tb.ninput, a);
tb.f[i] = splint(tb.rfile, tb.ffile, tb.f2file, tb.ninput, a);
}
for (int i = 0; i < tlm1; i++) {
tb.de[i] = tb.e[i + 1] - tb.e[i];
tb.df[i] = tb.f[i + 1] - tb.f[i];
}
// get final elements from linear extrapolation
tb.de[tlm1] = 2.0 * tb.de[tlm1 - 1] - tb.de[tlm1 - 2];
tb.df[tlm1] = 2.0 * tb.df[tlm1 - 1] - tb.df[tlm1 - 2];
double ep0 = -tb.f[0];
double epn = -tb.f[tlm1];
spline(tb.r, tb.e, tablength, ep0, epn, tb.e2);
spline(tb.r, tb.f, tablength, tb.fplo, tb.fphi, tb.f2);
}
/* ----------------------------------------------------------------------
extract attributes from parameter line in table section
format of line: N value FP fplo fphi
N is required, other params are optional
------------------------------------------------------------------------- */
void FixWallTable::param_extract(Table &tb, char *line)
{
tb.ninput = 0;
tb.fpflag = 0;
try {
ValueTokenizer values(line);
while (values.has_next()) {
std::string word = values.next_string();
if (word == "N") {
tb.ninput = values.next_int();
} else if (word == "FP") {
tb.fpflag = 1;
tb.fplo = values.next_double();
tb.fphi = values.next_double();
} else {
error->one(FLERR, "Invalid keyword {} in fix {} table parameters", word, style);
}
}
} catch (TokenizerException &e) {
error->one(FLERR, e.what());
}
if (tb.ninput == 0) error->one(FLERR, "Fix {} parameters did not set N", style);
}
/* ----------------------------------------------------------------------
broadcast read-in table info from proc 0 to other procs
this function communicates these values in Table:
ninput,rfile,efile,ffile,fpflag,fplo,fphi
------------------------------------------------------------------------- */
void FixWallTable::bcast_table(Table &tb)
{
MPI_Bcast(&tb.ninput, 1, MPI_INT, 0, world);
if (comm->me > 0) {
memory->create(tb.rfile, tb.ninput, "wall:rfile");
memory->create(tb.efile, tb.ninput, "wall:efile");
memory->create(tb.ffile, tb.ninput, "wall:ffile");
}
MPI_Bcast(tb.rfile, tb.ninput, MPI_DOUBLE, 0, world);
MPI_Bcast(tb.efile, tb.ninput, MPI_DOUBLE, 0, world);
MPI_Bcast(tb.ffile, tb.ninput, MPI_DOUBLE, 0, world);
MPI_Bcast(&tb.fpflag, 1, MPI_INT, 0, world);
if (tb.fpflag) {
MPI_Bcast(&tb.fplo, 1, MPI_DOUBLE, 0, world);
MPI_Bcast(&tb.fphi, 1, MPI_DOUBLE, 0, world);
}
}
/* ----------------------------------------------------------------------
spline and splint routines modified from Numerical Recipes
------------------------------------------------------------------------- */
void FixWallTable::spline(double *x, double *y, int n, double yp1, double ypn, double *y2)
{
int i, k;
double p, qn, sig, un;
auto u = new double[n];
if (yp1 > BIGNUM)
y2[0] = u[0] = 0.0;
else {
y2[0] = -0.5;
u[0] = (3.0 / (x[1] - x[0])) * ((y[1] - y[0]) / (x[1] - x[0]) - yp1);
}
for (i = 1; i < n - 1; i++) {
sig = (x[i] - x[i - 1]) / (x[i + 1] - x[i - 1]);
p = sig * y2[i - 1] + 2.0;
y2[i] = (sig - 1.0) / p;
u[i] = (y[i + 1] - y[i]) / (x[i + 1] - x[i]) - (y[i] - y[i - 1]) / (x[i] - x[i - 1]);
u[i] = (6.0 * u[i] / (x[i + 1] - x[i - 1]) - sig * u[i - 1]) / p;
}
if (ypn > BIGNUM)
qn = un = 0.0;
else {
qn = 0.5;
un = (3.0 / (x[n - 1] - x[n - 2])) * (ypn - (y[n - 1] - y[n - 2]) / (x[n - 1] - x[n - 2]));
}
y2[n - 1] = (un - qn * u[n - 2]) / (qn * y2[n - 2] + 1.0);
for (k = n - 2; k >= 0; k--) y2[k] = y2[k] * y2[k + 1] + u[k];
delete[] u;
}
/* ---------------------------------------------------------------------- */
double FixWallTable::splint(double *xa, double *ya, double *y2a, int n, double x)
{
int klo, khi, k;
double h, b, a, y;
klo = 0;
khi = n - 1;
while (khi - klo > 1) {
k = (khi + klo) >> 1;
if (xa[k] > x)
khi = k;
else
klo = k;
}
h = xa[khi] - xa[klo];
a = (xa[khi] - x) / h;
b = (x - xa[klo]) / h;
y = a * ya[klo] + b * ya[khi] +
((a * a * a - a) * y2a[klo] + (b * b * b - b) * y2a[khi]) * (h * h) / 6.0;
return y;
}
/* ----------------------------------------------------------------------
calculate potential u and force f at distance x
ensure x is between wall min/max, exit with error if not
------------------------------------------------------------------------- */
void FixWallTable::uf_lookup(int m, double x, double &u, double &f)
{
double fraction, a, b;
const Table &tb = tables[m];
const int itable = static_cast<int>((x - tb.lo) * tb.invdelta);
if (itable < 0)
error->one(FLERR, "Particle / wall distance < table inner cutoff: {:.8}", x);
else if (itable >= tablength)
error->one(FLERR, "Particle / wall distance > table outer cutoff: {:.8}", x);
if (tabstyle == LINEAR) {
fraction = (x - tb.r[itable]) * tb.invdelta;
u = tb.e[itable] + fraction * tb.de[itable];
f = tb.f[itable] + fraction * tb.df[itable];
} else if (tabstyle == SPLINE) {
fraction = (x - tb.r[itable]) * tb.invdelta;
b = (x - tb.r[itable]) * tb.invdelta;
a = 1.0 - b;
u = a * tb.e[itable] + b * tb.e[itable + 1] +
((a * a * a - a) * tb.e2[itable] + (b * b * b - b) * tb.e2[itable + 1]) * tb.deltasq6;
f = a * tb.f[itable] + b * tb.f[itable + 1] +
((a * a * a - a) * tb.f2[itable] + (b * b * b - b) * tb.f2[itable + 1]) * tb.deltasq6;
}
}