1443 lines
43 KiB
C++
1443 lines
43 KiB
C++
/* ----------------------------------------------------------------------
|
|
LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator
|
|
http://lammps.sandia.gov, 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 "math.h"
|
|
#include "stdlib.h"
|
|
#include "string.h"
|
|
#include "ctype.h"
|
|
#include "unistd.h"
|
|
#include "variable.h"
|
|
#include "universe.h"
|
|
#include "atom.h"
|
|
#include "update.h"
|
|
#include "group.h"
|
|
#include "domain.h"
|
|
#include "modify.h"
|
|
#include "compute.h"
|
|
#include "fix.h"
|
|
#include "output.h"
|
|
#include "thermo.h"
|
|
#include "memory.h"
|
|
#include "error.h"
|
|
|
|
using namespace LAMMPS_NS;
|
|
|
|
#define VARDELTA 4
|
|
#define MAXLEVEL 4
|
|
|
|
enum{INDEX,LOOP,EQUAL,WORLD,UNIVERSE,ULOOP,ATOM};
|
|
enum{ARG,OP};
|
|
enum{DONE,ADD,SUBTRACT,MULTIPLY,DIVIDE,CARAT,UNARY,
|
|
SQRT,EXP,LN,VALUE,ATOMARRAY,TYPEARRAY};
|
|
|
|
#define INVOKED_SCALAR 1 // same as in computes
|
|
#define INVOKED_VECTOR 2
|
|
#define INVOKED_PERATOM 4
|
|
|
|
/* ---------------------------------------------------------------------- */
|
|
|
|
Variable::Variable(LAMMPS *lmp) : Pointers(lmp)
|
|
{
|
|
MPI_Comm_rank(world,&me);
|
|
|
|
nvar = maxvar = 0;
|
|
names = NULL;
|
|
style = NULL;
|
|
num = NULL;
|
|
index = NULL;
|
|
data = NULL;
|
|
|
|
precedence[DONE] = 0;
|
|
precedence[ADD] = precedence[SUBTRACT] = 1;
|
|
precedence[MULTIPLY] = precedence[DIVIDE] = 2;
|
|
precedence[CARAT] = 3;
|
|
precedence[UNARY] = 4;
|
|
}
|
|
|
|
/* ---------------------------------------------------------------------- */
|
|
|
|
Variable::~Variable()
|
|
{
|
|
for (int i = 0; i < nvar; i++) {
|
|
delete [] names[i];
|
|
for (int j = 0; j < num[i]; j++) delete [] data[i][j];
|
|
delete [] data[i];
|
|
}
|
|
memory->sfree(names);
|
|
memory->sfree(style);
|
|
memory->sfree(num);
|
|
memory->sfree(index);
|
|
memory->sfree(data);
|
|
}
|
|
|
|
/* ----------------------------------------------------------------------
|
|
called by variable command in input script
|
|
------------------------------------------------------------------------- */
|
|
|
|
void Variable::set(int narg, char **arg)
|
|
{
|
|
if (narg < 3) error->all("Illegal variable command");
|
|
|
|
// if var already exists, just skip, except EQUAL and ATOM vars
|
|
|
|
if (find(arg[0]) >= 0 &&
|
|
strcmp(arg[1],"equal") != 0 && strcmp(arg[1],"atom") != 0) return;
|
|
|
|
// make space for new variable
|
|
|
|
if (nvar == maxvar) {
|
|
maxvar += VARDELTA;
|
|
names = (char **)
|
|
memory->srealloc(names,maxvar*sizeof(char *),"var:names");
|
|
style = (int *) memory->srealloc(style,maxvar*sizeof(int),"var:style");
|
|
num = (int *) memory->srealloc(num,maxvar*sizeof(int),"var:num");
|
|
index = (int *) memory->srealloc(index,maxvar*sizeof(int),"var:index");
|
|
data = (char ***)
|
|
memory->srealloc(data,maxvar*sizeof(char **),"var:data");
|
|
}
|
|
|
|
// INDEX
|
|
// num = listed args, index = 1st value, data = copied args
|
|
|
|
if (strcmp(arg[1],"index") == 0) {
|
|
style[nvar] = INDEX;
|
|
num[nvar] = narg - 2;
|
|
index[nvar] = 0;
|
|
data[nvar] = new char*[num[nvar]];
|
|
copy(num[nvar],&arg[2],data[nvar]);
|
|
|
|
// LOOP
|
|
// num = N, index = 1st value, data = list of NULLS since never used
|
|
|
|
} else if (strcmp(arg[1],"loop") == 0) {
|
|
if (narg != 3) error->all("Illegal variable command");
|
|
style[nvar] = LOOP;
|
|
num[nvar] = atoi(arg[2]);
|
|
index[nvar] = 0;
|
|
data[nvar] = new char*[num[nvar]];
|
|
for (int i = 0; i < num[nvar]; i++) data[nvar][i] = NULL;
|
|
|
|
// EQUAL
|
|
// remove pre-existing var if also style EQUAL (allows it to be reset)
|
|
// num = 2, index = 1st value
|
|
// data = 2 values, 1st is string to eval, 2nd is filled on retrieval
|
|
|
|
} else if (strcmp(arg[1],"equal") == 0) {
|
|
if (narg != 3) error->all("Illegal variable command");
|
|
if (find(arg[0]) >= 0) {
|
|
if (style[find(arg[0])] != EQUAL)
|
|
error->all("Cannot redefine variable as a different style");
|
|
remove(find(arg[0]));
|
|
}
|
|
style[nvar] = EQUAL;
|
|
num[nvar] = 2;
|
|
index[nvar] = 0;
|
|
data[nvar] = new char*[num[nvar]];
|
|
copy(1,&arg[2],data[nvar]);
|
|
data[nvar][1] = NULL;
|
|
|
|
// WORLD
|
|
// num = listed args, index = partition this proc is in, data = copied args
|
|
// error check that num = # of worlds in universe
|
|
|
|
} else if (strcmp(arg[1],"world") == 0) {
|
|
style[nvar] = WORLD;
|
|
num[nvar] = narg - 2;
|
|
if (num[nvar] != universe->nworlds)
|
|
error->all("World variable count doesn't match # of partitions");
|
|
index[nvar] = universe->iworld;
|
|
data[nvar] = new char*[num[nvar]];
|
|
copy(num[nvar],&arg[2],data[nvar]);
|
|
|
|
// UNIVERSE and ULOOP
|
|
// for UNIVERSE: num = listed args, data = copied args
|
|
// for ULOOP: num = N, data = list of NULLS since never used
|
|
// index = partition this proc is in
|
|
// universe proc 0 creates lock file
|
|
// error check that all other universe/uloop variables are same length
|
|
|
|
} else if (strcmp(arg[1],"universe") == 0 || strcmp(arg[1],"uloop") == 0) {
|
|
if (strcmp(arg[1],"universe") == 0) {
|
|
style[nvar] = UNIVERSE;
|
|
num[nvar] = narg - 2;
|
|
data[nvar] = new char*[num[nvar]];
|
|
copy(num[nvar],&arg[2],data[nvar]);
|
|
} else {
|
|
if (narg != 3) error->all("Illegal variable command");
|
|
style[nvar] = ULOOP;
|
|
num[nvar] = atoi(arg[2]);
|
|
data[nvar] = new char*[num[nvar]];
|
|
for (int i = 0; i < num[nvar]; i++) data[nvar][i] = NULL;
|
|
}
|
|
|
|
if (num[nvar] < universe->nworlds)
|
|
error->all("Universe/uloop variable count < # of partitions");
|
|
index[nvar] = universe->iworld;
|
|
|
|
if (universe->me == 0) {
|
|
FILE *fp = fopen("tmp.lammps.variable","w");
|
|
fprintf(fp,"%d\n",universe->nworlds);
|
|
fclose(fp);
|
|
}
|
|
|
|
for (int jvar = 0; jvar < nvar; jvar++)
|
|
if (num[jvar] && (style[jvar] == UNIVERSE || style[jvar] == ULOOP) &&
|
|
num[nvar] != num[jvar])
|
|
error->all("All universe/uloop variables must have same # of values");
|
|
|
|
if (me == 0) {
|
|
if (universe->uscreen)
|
|
fprintf(universe->uscreen,
|
|
"Initial ${%s} setting: value %d on partition %d\n",
|
|
arg[0],index[nvar]+1,universe->iworld);
|
|
if (universe->ulogfile)
|
|
fprintf(universe->ulogfile,
|
|
"Initial ${%s} setting: value %d on partition %d\n",
|
|
arg[0],index[nvar]+1,universe->iworld);
|
|
}
|
|
|
|
// ATOM
|
|
// remove pre-existing var if also style ATOM (allows it to be reset)
|
|
// num = 1, index = 1st value
|
|
// data = 1 value, string to eval
|
|
|
|
} else if (strcmp(arg[1],"atom") == 0) {
|
|
if (narg != 3) error->all("Illegal variable command");
|
|
if (find(arg[0]) >= 0) {
|
|
if (style[find(arg[0])] != ATOM)
|
|
error->all("Cannot redefine variable as a different style");
|
|
remove(find(arg[0]));
|
|
}
|
|
style[nvar] = ATOM;
|
|
num[nvar] = 1;
|
|
index[nvar] = 0;
|
|
data[nvar] = new char*[num[nvar]];
|
|
copy(1,&arg[2],data[nvar]);
|
|
|
|
} else error->all("Illegal variable command");
|
|
|
|
// set name of variable
|
|
// must come at end, since EQUAL/ATOM reset may have removed name
|
|
// name must be all alphanumeric chars or underscores
|
|
|
|
int n = strlen(arg[0]) + 1;
|
|
names[nvar] = new char[n];
|
|
strcpy(names[nvar],arg[0]);
|
|
|
|
for (int i = 0; i < n-1; i++)
|
|
if (!isalnum(names[nvar][i]) && names[nvar][i] != '_')
|
|
error->all("Variable name must be alphanumeric or underscore characters");
|
|
|
|
nvar++;
|
|
}
|
|
|
|
/* ----------------------------------------------------------------------
|
|
single-value INDEX variable created by command-line argument
|
|
------------------------------------------------------------------------- */
|
|
|
|
void Variable::set(char *name, char *value)
|
|
{
|
|
char **newarg = new char*[3];
|
|
newarg[0] = name;
|
|
newarg[1] = (char *) "index";
|
|
newarg[2] = value;
|
|
set(3,newarg);
|
|
delete [] newarg;
|
|
}
|
|
|
|
/* ----------------------------------------------------------------------
|
|
increment variable(s)
|
|
return 0 if OK if successfully incremented
|
|
return 1 if any variable is exhausted, free the variable to allow re-use
|
|
------------------------------------------------------------------------- */
|
|
|
|
int Variable::next(int narg, char **arg)
|
|
{
|
|
int ivar;
|
|
|
|
if (narg == 0) error->all("Illegal next command");
|
|
|
|
// check that variables exist and are all the same style
|
|
// exception: UNIVERSE and ULOOP variables can be mixed in same next command
|
|
|
|
for (int iarg = 0; iarg < narg; iarg++) {
|
|
ivar = find(arg[iarg]);
|
|
if (ivar == -1) error->all("Invalid variable in next command");
|
|
if (style[ivar] == ULOOP && style[find(arg[0])] == UNIVERSE) continue;
|
|
else if (style[ivar] == UNIVERSE && style[find(arg[0])] == ULOOP) continue;
|
|
else if (style[ivar] != style[find(arg[0])])
|
|
error->all("All variables in next command must be same style");
|
|
}
|
|
|
|
// invalid styles EQUAL or WORLD or ATOM
|
|
|
|
int istyle = style[find(arg[0])];
|
|
if (istyle == EQUAL || istyle == WORLD || istyle == ATOM)
|
|
error->all("Invalid variable style with next command");
|
|
|
|
// increment all variables in list
|
|
// if any variable is exhausted, set flag = 1 and remove var to allow re-use
|
|
|
|
int flag = 0;
|
|
|
|
if (istyle == INDEX || istyle == LOOP) {
|
|
for (int iarg = 0; iarg < narg; iarg++) {
|
|
ivar = find(arg[iarg]);
|
|
index[ivar]++;
|
|
if (index[ivar] >= num[ivar]) {
|
|
flag = 1;
|
|
remove(ivar);
|
|
}
|
|
}
|
|
|
|
} else if (istyle == UNIVERSE || istyle == ULOOP) {
|
|
|
|
// wait until lock file can be created and owned by proc 0 of this world
|
|
// read next available index and Bcast it within my world
|
|
// set all variables in list to nextindex
|
|
|
|
int nextindex;
|
|
if (me == 0) {
|
|
while (1) {
|
|
if (!rename("tmp.lammps.variable","tmp.lammps.variable.lock")) break;
|
|
usleep(100000);
|
|
}
|
|
FILE *fp = fopen("tmp.lammps.variable.lock","r");
|
|
fscanf(fp,"%d",&nextindex);
|
|
fclose(fp);
|
|
fp = fopen("tmp.lammps.variable.lock","w");
|
|
fprintf(fp,"%d\n",nextindex+1);
|
|
fclose(fp);
|
|
rename("tmp.lammps.variable.lock","tmp.lammps.variable");
|
|
if (universe->uscreen)
|
|
fprintf(universe->uscreen,
|
|
"Increment via next: value %d on partition %d\n",
|
|
nextindex+1,universe->iworld);
|
|
if (universe->ulogfile)
|
|
fprintf(universe->ulogfile,
|
|
"Increment via next: value %d on partition %d\n",
|
|
nextindex+1,universe->iworld);
|
|
}
|
|
MPI_Bcast(&nextindex,1,MPI_INT,0,world);
|
|
|
|
for (int iarg = 0; iarg < narg; iarg++) {
|
|
ivar = find(arg[iarg]);
|
|
index[ivar] = nextindex;
|
|
if (index[ivar] >= num[ivar]) {
|
|
flag = 1;
|
|
remove(ivar);
|
|
}
|
|
}
|
|
}
|
|
|
|
return flag;
|
|
}
|
|
|
|
/* ----------------------------------------------------------------------
|
|
return ptr to the data text associated with a variable
|
|
if EQUAL var, evaluates variable and puts result in str
|
|
return NULL if no variable or index is bad, caller must respond
|
|
------------------------------------------------------------------------- */
|
|
|
|
char *Variable::retrieve(char *name)
|
|
{
|
|
int ivar = find(name);
|
|
if (ivar == -1) return NULL;
|
|
if (index[ivar] >= num[ivar]) return NULL;
|
|
|
|
char *str;
|
|
if (style[ivar] == INDEX || style[ivar] == WORLD ||
|
|
style[ivar] == UNIVERSE) {
|
|
str = data[ivar][index[ivar]];
|
|
} else if (style[ivar] == LOOP || style[ivar] == ULOOP) {
|
|
char *value = new char[16];
|
|
sprintf(value,"%d",index[ivar]+1);
|
|
int n = strlen(value) + 1;
|
|
if (data[ivar][0]) delete [] data[ivar][0];
|
|
data[ivar][0] = new char[n];
|
|
strcpy(data[ivar][0],value);
|
|
delete [] value;
|
|
str = data[ivar][0];
|
|
} else if (style[ivar] == EQUAL) {
|
|
char result[32];
|
|
double answer = evaluate(data[ivar][0],NULL);
|
|
sprintf(result,"%.10g",answer);
|
|
int n = strlen(result) + 1;
|
|
if (data[ivar][1]) delete [] data[ivar][1];
|
|
data[ivar][1] = new char[n];
|
|
strcpy(data[ivar][1],result);
|
|
str = data[ivar][1];
|
|
} else if (style[ivar] == ATOM) return NULL;
|
|
|
|
return str;
|
|
}
|
|
|
|
/* ----------------------------------------------------------------------
|
|
return result of equal-style variable evaluation
|
|
------------------------------------------------------------------------- */
|
|
|
|
double Variable::compute_equal(int ivar)
|
|
{
|
|
return evaluate(data[ivar][0],NULL);
|
|
}
|
|
|
|
/* ----------------------------------------------------------------------
|
|
compute result of atom-style variable evaluation
|
|
stride used since result may not be contiguous memory locs
|
|
if sumflag, add variable values to existing result
|
|
------------------------------------------------------------------------- */
|
|
|
|
void Variable::compute_atom(int ivar, int igroup,
|
|
double *result, int stride, int sumflag)
|
|
{
|
|
Tree *tree;
|
|
double tmp = evaluate(data[ivar][0],&tree);
|
|
|
|
int groupbit = group->bitmask[igroup];
|
|
int *mask = atom->mask;
|
|
int nlocal = atom->nlocal;
|
|
|
|
if (sumflag == 0) {
|
|
int m = 0;
|
|
for (int i = 0; i < nlocal; i++) {
|
|
if (mask[i] && groupbit) result[m] = eval_tree(tree,i);
|
|
else result[m] = 0.0;
|
|
m += stride;
|
|
}
|
|
|
|
} else {
|
|
int m = 0;
|
|
for (int i = 0; i < nlocal; i++) {
|
|
if (mask[i] && groupbit) result[m] += eval_tree(tree,i);
|
|
m += stride;
|
|
}
|
|
}
|
|
|
|
free_tree(tree);
|
|
}
|
|
|
|
/* ----------------------------------------------------------------------
|
|
search for name in list of variables names
|
|
return index or -1 if not found
|
|
------------------------------------------------------------------------- */
|
|
|
|
int Variable::find(char *name)
|
|
{
|
|
for (int i = 0; i < nvar; i++)
|
|
if (strcmp(name,names[i]) == 0) return i;
|
|
return -1;
|
|
}
|
|
|
|
/* ----------------------------------------------------------------------
|
|
return 1 if variable is EQUAL style, 0 if not
|
|
------------------------------------------------------------------------- */
|
|
|
|
int Variable::equalstyle(int ivar)
|
|
{
|
|
if (style[ivar] == EQUAL) return 1;
|
|
return 0;
|
|
}
|
|
|
|
/* ----------------------------------------------------------------------
|
|
return 1 if variable is ATOM style, 0 if not
|
|
------------------------------------------------------------------------- */
|
|
|
|
int Variable::atomstyle(int ivar)
|
|
{
|
|
if (style[ivar] == ATOM) return 1;
|
|
return 0;
|
|
}
|
|
|
|
/* ----------------------------------------------------------------------
|
|
remove Nth variable from list and compact list
|
|
------------------------------------------------------------------------- */
|
|
|
|
void Variable::remove(int n)
|
|
{
|
|
delete [] names[n];
|
|
for (int i = 0; i < num[n]; i++) delete [] data[n][i];
|
|
delete [] data[n];
|
|
|
|
for (int i = n+1; i < nvar; i++) {
|
|
names[i-1] = names[i];
|
|
style[i-1] = style[i];
|
|
num[i-1] = num[i];
|
|
index[i-1] = index[i];
|
|
data[i-1] = data[i];
|
|
}
|
|
nvar--;
|
|
}
|
|
|
|
/* ----------------------------------------------------------------------
|
|
copy narg strings from **from to **to
|
|
------------------------------------------------------------------------- */
|
|
|
|
void Variable::copy(int narg, char **from, char **to)
|
|
{
|
|
int n;
|
|
|
|
for (int i = 0; i < narg; i++) {
|
|
n = strlen(from[i]) + 1;
|
|
to[i] = new char[n];
|
|
strcpy(to[i],from[i]);
|
|
}
|
|
}
|
|
|
|
/* ----------------------------------------------------------------------
|
|
recursive evaluation of a string str
|
|
string is a equal-style or atom-style formula containing one or more items:
|
|
number = 0.0, -5.45, 2.8e-4, ...
|
|
thermo keyword = ke, vol, atoms, ...
|
|
math operation = (),-x,x+y,x-y,x*y,x/y,x^y,sqrt(x),exp(x),ln(x)
|
|
group function = count(group), mass(group), xcm(group,x), ...
|
|
atom value = x[123], y[3], vx[34], ...
|
|
atom vector = x[], y[], vx[], ...
|
|
compute = c_ID, c_ID[2], c_ID[], c_ID[][2], c_ID[N], c_ID[N][2]
|
|
fix = f_ID, f_ID[2], f_ID[], f_ID[][2], f_ID[N], f_ID[N][2]
|
|
variable = v_name, v_name[], v_name[N]
|
|
if tree ptr passed in, return ptr to parse tree created for formula
|
|
if no tree ptr passed in, return value of string as double
|
|
------------------------------------------------------------------------- */
|
|
|
|
double Variable::evaluate(char *str, Tree **tree)
|
|
{
|
|
int op,opprevious;
|
|
double value1,value2;
|
|
char onechar;
|
|
|
|
double argstack[MAXLEVEL];
|
|
Tree *treestack[MAXLEVEL];
|
|
int opstack[MAXLEVEL];
|
|
int nargstack = 0;
|
|
int ntreestack = 0;
|
|
int nopstack = 0;
|
|
|
|
int i = 0;
|
|
int expect = ARG;
|
|
|
|
while (1) {
|
|
onechar = str[i];
|
|
|
|
// whitespace: just skip
|
|
|
|
if (isspace(onechar)) i++;
|
|
|
|
// ----------------
|
|
// parentheses: recursively evaluate contents of parens
|
|
// ----------------
|
|
|
|
else if (onechar == '(') {
|
|
if (expect == OP) error->all("Invalid syntax in variable formula");
|
|
expect = OP;
|
|
|
|
char *contents;
|
|
i = find_matching_paren(str,i,contents);
|
|
i++;
|
|
|
|
// evaluate contents and push on stack
|
|
|
|
if (tree) {
|
|
Tree *newtree;
|
|
double tmp = evaluate(contents,&newtree);
|
|
treestack[ntreestack++] = newtree;
|
|
} else argstack[nargstack++] = evaluate(contents,NULL);
|
|
|
|
delete [] contents;
|
|
|
|
// ----------------
|
|
// number: push value onto stack
|
|
// ----------------
|
|
|
|
} else if (isdigit(onechar) || onechar == '.') {
|
|
if (expect == OP) error->all("Invalid syntax in variable formula");
|
|
expect = OP;
|
|
|
|
// istop = end of number, including scientific notation
|
|
|
|
int istart = i;
|
|
int istop = istart + strspn(&str[i],"0123456789eE.-") - 1;
|
|
while (str[istop] == '-') istop--;
|
|
i = istop + 1;
|
|
|
|
int n = istop - istart + 1;
|
|
char *number = new char[n+1];
|
|
strncpy(number,&str[istart],n);
|
|
number[n] = '\0';
|
|
|
|
if (tree) {
|
|
Tree *newtree = new Tree();
|
|
newtree->type = VALUE;
|
|
newtree->value = atof(number);
|
|
treestack[ntreestack++] = newtree;
|
|
} else argstack[nargstack++] = atof(number);
|
|
|
|
delete [] number;
|
|
|
|
// ----------------
|
|
// letter: c_ID, f_ID, v_name, exp(), xcm(,), x[234], x[], vol
|
|
// ----------------
|
|
|
|
} else if (islower(onechar)) {
|
|
if (expect == OP) error->all("Invalid syntax in variable formula");
|
|
expect = OP;
|
|
|
|
// istop = end of word
|
|
// word = all alphanumeric or underscore
|
|
|
|
int istart = i;
|
|
while (isalnum(str[i]) || str[i] == '_') i++;
|
|
int istop = i-1;
|
|
|
|
int n = istop - istart + 1;
|
|
char *word = new char[n+1];
|
|
strncpy(word,&str[istart],n);
|
|
word[n] = '\0';
|
|
|
|
// ----------------
|
|
// compute
|
|
// ----------------
|
|
|
|
if (strncmp(word,"c_",2) == 0) {
|
|
n = strlen(word) - 2 + 1;
|
|
char *id = new char[n];
|
|
strcpy(id,&word[2]);
|
|
|
|
if (update->first_update == 0)
|
|
error->all("Compute in variable formula before initial run");
|
|
|
|
int icompute = modify->find_compute(id);
|
|
if (icompute < 0) error->all("Invalid compute ID in variable formula");
|
|
Compute *compute = modify->compute[icompute];
|
|
|
|
delete [] id;
|
|
|
|
// parse zero or one or two trailing brackets
|
|
// point i beyond last bracket
|
|
// nbracket = # of bracket pairs
|
|
// index1,index2 = int inside each bracket pair, 0 if empty
|
|
|
|
int nbracket,index1,index2;
|
|
if (str[i] != '[') nbracket = 0;
|
|
else {
|
|
nbracket = 1;
|
|
i = int_between_brackets(str,i,index1,1);
|
|
i++;
|
|
if (str[i] == '[') {
|
|
nbracket = 2;
|
|
i = int_between_brackets(str,i,index2,0);
|
|
i++;
|
|
}
|
|
}
|
|
|
|
// c_ID = global scalar
|
|
|
|
if (nbracket == 0 && compute->scalar_flag) {
|
|
|
|
if (!(compute->invoked & INVOKED_SCALAR))
|
|
value1 = compute->compute_scalar();
|
|
else value1 = compute->scalar;
|
|
if (tree) {
|
|
Tree *newtree = new Tree();
|
|
newtree->type = VALUE;
|
|
newtree->value = value1;
|
|
treestack[ntreestack++] = newtree;
|
|
} else argstack[nargstack++] = value1;
|
|
|
|
// c_ID[2] = global vector
|
|
|
|
} else if (nbracket == 1 && index1 > 0 && compute->vector_flag) {
|
|
|
|
if (index1 > compute->size_vector)
|
|
error->all("Compute vector in variable formula is too small");
|
|
if (!(compute->invoked & INVOKED_VECTOR)) compute->compute_vector();
|
|
value1 = compute->vector[index1-1];
|
|
if (tree) {
|
|
Tree *newtree = new Tree();
|
|
newtree->type = VALUE;
|
|
newtree->value = value1;
|
|
treestack[ntreestack++] = newtree;
|
|
} else argstack[nargstack++] = value1;
|
|
|
|
// c_ID[] = per-atom scalar
|
|
|
|
} else if (nbracket == 1 && index1 == 0 &&
|
|
compute->peratom_flag && compute->size_peratom == 0) {
|
|
|
|
if (tree == NULL)
|
|
error->all("Per-atom compute in equal-style variable formula");
|
|
if (!(compute->invoked & INVOKED_PERATOM))
|
|
compute->compute_peratom();
|
|
Tree *newtree = new Tree();
|
|
newtree->type = ATOMARRAY;
|
|
newtree->array = compute->scalar_atom;
|
|
newtree->nstride = 1;
|
|
treestack[ntreestack++] = newtree;
|
|
|
|
// c_ID[N] = global value from per-atom scalar
|
|
|
|
} else if (nbracket == 1 && index1 > 0 &&
|
|
compute->peratom_flag && compute->size_peratom == 0) {
|
|
|
|
if (!(compute->invoked & INVOKED_PERATOM))
|
|
compute->compute_peratom();
|
|
peratom2global(1,NULL,compute->scalar_atom,1,index1,
|
|
tree,treestack,ntreestack,argstack,nargstack);
|
|
|
|
// c_ID[][2] = per-atom vector
|
|
|
|
} else if (nbracket == 2 && index1 == 0 && index2 > 0 &&
|
|
compute->peratom_flag) {
|
|
|
|
if (tree == NULL)
|
|
error->all("Per-atom compute in equal-style variable formula");
|
|
if (index2 > compute->size_peratom)
|
|
error->all("Compute vector in variable formula is too small");
|
|
if (!(compute->invoked & INVOKED_PERATOM))
|
|
compute->compute_peratom();
|
|
Tree *newtree = new Tree();
|
|
newtree->type = ATOMARRAY;
|
|
newtree->array = &compute->vector_atom[0][index2-1];
|
|
newtree->nstride = compute->size_peratom;
|
|
treestack[ntreestack++] = newtree;
|
|
|
|
// c_ID[N][2] = global value from per-atom vector
|
|
|
|
} else if (nbracket == 2 && index1 > 0 && index2 > 0 &&
|
|
compute->peratom_flag) {
|
|
|
|
if (index2 > compute->size_peratom)
|
|
error->all("Compute vector in variable formula is too small");
|
|
if (!(compute->invoked & INVOKED_PERATOM))
|
|
compute->compute_peratom();
|
|
peratom2global(1,NULL,&compute->vector_atom[0][index2-1],
|
|
compute->size_peratom,index1,
|
|
tree,treestack,ntreestack,argstack,nargstack);
|
|
|
|
} else error->all("Mismatched compute in variable formula");
|
|
|
|
// ----------------
|
|
// fix
|
|
// ----------------
|
|
|
|
} else if (strncmp(word,"f_",2) == 0) {
|
|
n = strlen(word) - 2 + 1;
|
|
char *id = new char[n];
|
|
strcpy(id,&word[2]);
|
|
|
|
if (update->first_update == 0)
|
|
error->all("Fix in variable formula before initial run");
|
|
|
|
int ifix = modify->find_fix(id);
|
|
if (ifix < 0) error->all("Invalid fix ID in variable formula");
|
|
Fix *fix = modify->fix[ifix];
|
|
|
|
delete [] id;
|
|
|
|
// parse zero or one or two trailing brackets
|
|
// point i beyond last bracket
|
|
// nbracket = # of bracket pairs
|
|
// index1,index2 = int inside each bracket pair, 0 if empty
|
|
|
|
int nbracket,index1,index2;
|
|
if (str[i] != '[') nbracket = 0;
|
|
else {
|
|
nbracket = 1;
|
|
i = int_between_brackets(str,i,index1,1);
|
|
i++;
|
|
if (str[i] == '[') {
|
|
nbracket = 2;
|
|
i = int_between_brackets(str,i,index2,0);
|
|
i++;
|
|
}
|
|
}
|
|
|
|
// f_ID = global scalar
|
|
|
|
if (nbracket == 0 && fix->scalar_flag) {
|
|
|
|
if (update->ntimestep % fix->scalar_vector_freq)
|
|
error->all("Fix in variable not computed at compatible time");
|
|
value1 = fix->compute_scalar();
|
|
if (tree) {
|
|
Tree *newtree = new Tree();
|
|
newtree->type = VALUE;
|
|
newtree->value = value1;
|
|
treestack[ntreestack++] = newtree;
|
|
} else argstack[nargstack++] = value1;
|
|
|
|
// f_ID[2] = global vector
|
|
|
|
} else if (nbracket == 1 && index1 > 0 && fix->vector_flag) {
|
|
|
|
if (index1 > fix->size_vector)
|
|
error->all("Fix vector in variable formula is too small");
|
|
if (update->ntimestep % fix->scalar_vector_freq)
|
|
error->all("Fix in variable not computed at compatible time");
|
|
value1 = fix->compute_vector(index1-1);
|
|
if (tree) {
|
|
Tree *newtree = new Tree();
|
|
newtree->type = VALUE;
|
|
newtree->value = value1;
|
|
treestack[ntreestack++] = newtree;
|
|
} else argstack[nargstack++] = value1;
|
|
|
|
// f_ID[] = per-atom scalar
|
|
|
|
} else if (nbracket == 1 && index1 == 0 &&
|
|
fix->peratom_flag && fix->size_peratom == 0) {
|
|
|
|
if (tree == NULL)
|
|
error->all("Per-atom fix in equal-style variable formula");
|
|
if (update->ntimestep % fix->peratom_freq)
|
|
error->all("Fix in variable not computed at compatible time");
|
|
Tree *newtree = new Tree();
|
|
newtree->type = ATOMARRAY;
|
|
newtree->array = fix->scalar_atom;
|
|
newtree->nstride = 1;
|
|
treestack[ntreestack++] = newtree;
|
|
|
|
// f_ID[N] = global value from per-atom scalar
|
|
|
|
} else if (nbracket == 1 && index1 > 0 &&
|
|
fix->peratom_flag && fix->size_peratom == 0) {
|
|
|
|
if (update->ntimestep % fix->peratom_freq)
|
|
error->all("Fix in variable not computed at compatible time");
|
|
peratom2global(1,NULL,fix->scalar_atom,1,index1,
|
|
tree,treestack,ntreestack,argstack,nargstack);
|
|
|
|
// f_ID[][2] = per-atom vector
|
|
|
|
} else if (nbracket == 2 && index1 == 0 && index2 > 0 &&
|
|
fix->peratom_flag) {
|
|
|
|
if (tree == NULL)
|
|
error->all("Per-atom fix in equal-style variable formula");
|
|
if (index2 > fix->size_peratom)
|
|
error->all("Fix vector in variable formula is too small");
|
|
if (update->ntimestep % fix->peratom_freq)
|
|
error->all("Fix in variable not computed at compatible time");
|
|
Tree *newtree = new Tree();
|
|
newtree->type = ATOMARRAY;
|
|
newtree->array = &fix->vector_atom[0][index2-1];
|
|
newtree->nstride = fix->size_peratom;
|
|
treestack[ntreestack++] = newtree;
|
|
|
|
// f_ID[N][2] = global value from per-atom vector
|
|
|
|
} else if (nbracket == 2 && index1 > 0 && index2 > 0 &&
|
|
fix->peratom_flag) {
|
|
|
|
if (index2 > fix->size_peratom)
|
|
error->all("Fix vector in variable formula is too small");
|
|
if (update->ntimestep % fix->peratom_freq)
|
|
error->all("Fix in variable not computed at compatible time");
|
|
peratom2global(1,NULL,&fix->vector_atom[0][index2-1],
|
|
fix->size_peratom,index1,
|
|
tree,treestack,ntreestack,argstack,nargstack);
|
|
|
|
} else error->all("Mismatched fix in variable formula");
|
|
|
|
// ----------------
|
|
// variable
|
|
// ----------------
|
|
|
|
} else if (strncmp(word,"v_",2) == 0) {
|
|
n = strlen(word) - 2 + 1;
|
|
char *id = new char[n];
|
|
strcpy(id,&word[2]);
|
|
|
|
int ivar = find(id);
|
|
if (ivar < 0) error->all("Invalid variable name in variable formula");
|
|
|
|
// parse zero or one trailing brackets
|
|
// point i beyond last bracket
|
|
// nbracket = # of bracket pairs
|
|
// index = int inside bracket, 0 if empty
|
|
|
|
int nbracket,index;
|
|
if (str[i] != '[') nbracket = 0;
|
|
else {
|
|
nbracket = 1;
|
|
i = int_between_brackets(str,i,index,1);
|
|
i++;
|
|
}
|
|
|
|
// v_name = non atom-style variable = global value
|
|
|
|
if (nbracket == 0 && style[ivar] != ATOM) {
|
|
|
|
char *var = retrieve(id);
|
|
if (var == NULL)
|
|
error->all("Invalid variable evaluation in variable formula");
|
|
if (tree) {
|
|
Tree *newtree = new Tree();
|
|
newtree->type = VALUE;
|
|
newtree->value = atof(var);
|
|
treestack[ntreestack++] = newtree;
|
|
} else argstack[nargstack++] = atof(var);
|
|
|
|
// v_name[] = atom-style variable
|
|
|
|
} else if (nbracket && index == 0 && style[ivar] == ATOM) {
|
|
|
|
if (tree == NULL)
|
|
error->all("Atom-style variable in equal-style variable formula");
|
|
Tree *newtree;
|
|
double tmp = evaluate(data[ivar][0],&newtree);
|
|
treestack[ntreestack++] = newtree;
|
|
|
|
// v_name[N] = global value from atom-style variable
|
|
// compute the per-atom variable in result
|
|
// use peratom2global to extract single value from result
|
|
|
|
} else if (nbracket && index > 0 && style[ivar] == ATOM) {
|
|
|
|
double *result = (double *)
|
|
memory->smalloc(atom->nlocal*sizeof(double),"variable:result");
|
|
compute_atom(ivar,0,result,1,0);
|
|
peratom2global(1,NULL,result,1,index,
|
|
tree,treestack,ntreestack,argstack,nargstack);
|
|
memory->sfree(result);
|
|
|
|
} else error->all("Mismatched variable in variable formula");
|
|
|
|
delete [] id;
|
|
|
|
// ----------------
|
|
// math/group function or atom value/vector or thermo keyword
|
|
// ----------------
|
|
|
|
} else {
|
|
|
|
// math or group function
|
|
|
|
if (str[i] == '(') {
|
|
char *contents;
|
|
i = find_matching_paren(str,i,contents);
|
|
i++;
|
|
|
|
if (math_function(word,contents,tree,
|
|
treestack,ntreestack,argstack,nargstack));
|
|
else if (group_function(word,contents,tree,
|
|
treestack,ntreestack,argstack,nargstack));
|
|
else error->all("Invalid math or group function in variable formula");
|
|
|
|
delete [] contents;
|
|
|
|
// ----------------
|
|
// atom value or vector
|
|
// ----------------
|
|
|
|
} else if (str[i] == '[') {
|
|
int id;
|
|
i = int_between_brackets(str,i,id,1);
|
|
i++;
|
|
|
|
// ID between brackets exists: atom value
|
|
// empty brackets: atom vector
|
|
|
|
if (id > 0)
|
|
peratom2global(0,word,NULL,0,id,
|
|
tree,treestack,ntreestack,argstack,nargstack);
|
|
else
|
|
atom_vector(word,tree,treestack,ntreestack);
|
|
|
|
// ----------------
|
|
// thermo keyword
|
|
// ----------------
|
|
|
|
} else {
|
|
if (update->first_update == 0)
|
|
error->all("Thermo keyword in variable formula before initial run");
|
|
int flag = output->thermo->evaluate_keyword(word,&value1);
|
|
if (flag) error->all("Invalid thermo keyword in variable formula");
|
|
if (tree) {
|
|
Tree *newtree = new Tree();
|
|
newtree->type = VALUE;
|
|
newtree->value = value1;
|
|
treestack[ntreestack++] = newtree;
|
|
} else argstack[nargstack++] = value1;
|
|
}
|
|
}
|
|
|
|
delete [] word;
|
|
|
|
// ----------------
|
|
// math operator, including end-of-string
|
|
// ----------------
|
|
|
|
} else if (strchr("+-*/^\0",onechar)) {
|
|
if (onechar == '+') op = ADD;
|
|
else if (onechar == '-') op = SUBTRACT;
|
|
else if (onechar == '*') op = MULTIPLY;
|
|
else if (onechar == '/') op = DIVIDE;
|
|
else if (onechar == '^') op = CARAT;
|
|
else op = DONE;
|
|
i++;
|
|
|
|
if (op == SUBTRACT && expect == ARG) {
|
|
opstack[nopstack++] = UNARY;
|
|
continue;
|
|
}
|
|
|
|
if (expect == ARG) error->all("Invalid syntax in variable formula");
|
|
expect = ARG;
|
|
|
|
// evaluate stack as deep as possible while respecting precedence
|
|
// before pushing current op onto stack
|
|
|
|
while (nopstack && precedence[opstack[nopstack-1]] >= precedence[op]) {
|
|
opprevious = opstack[--nopstack];
|
|
|
|
if (tree) {
|
|
Tree *newtree = new Tree();
|
|
newtree->type = opprevious;
|
|
newtree->right = treestack[--ntreestack];
|
|
if (opprevious == UNARY) newtree->left = newtree->right;
|
|
else newtree->left = treestack[--ntreestack];
|
|
treestack[ntreestack++] = newtree;
|
|
|
|
} else {
|
|
value2 = argstack[--nargstack];
|
|
if (opprevious != UNARY) value1 = argstack[--nargstack];
|
|
|
|
if (opprevious == ADD)
|
|
argstack[nargstack++] = value1 + value2;
|
|
else if (opprevious == SUBTRACT)
|
|
argstack[nargstack++] = value1 - value2;
|
|
else if (opprevious == MULTIPLY)
|
|
argstack[nargstack++] = value1 * value2;
|
|
else if (opprevious == DIVIDE) {
|
|
if (value2 == 0.0) error->all("Divide by 0 in variable formula");
|
|
argstack[nargstack++] = value1 / value2;
|
|
} else if (opprevious == CARAT) {
|
|
if (value2 == 0.0) error->all("Power by 0 in variable formula");
|
|
argstack[nargstack++] = pow(value1,value2);
|
|
} else if (opprevious == UNARY)
|
|
argstack[nargstack++] = -value2;
|
|
}
|
|
}
|
|
|
|
// if end-of-string, break out of entire formula evaluation loop
|
|
|
|
if (op == DONE) break;
|
|
|
|
// push current operation onto stack
|
|
|
|
opstack[nopstack++] = op;
|
|
|
|
} else error->all("Invalid syntax in variable formula");
|
|
}
|
|
|
|
if (nopstack) error->all("Invalid syntax in variable formula");
|
|
|
|
// for atom-style variable, return remaining tree
|
|
// for equal-style variable, return remaining arg
|
|
|
|
if (tree) {
|
|
if (ntreestack != 1) error->all("Invalid syntax in variable formula");
|
|
*tree = treestack[0];
|
|
return 0.0;
|
|
} else {
|
|
if (nargstack != 1) error->all("Invalid syntax in variable formula");
|
|
return argstack[0];
|
|
}
|
|
}
|
|
|
|
/* ---------------------------------------------------------------------- */
|
|
|
|
double Variable::eval_tree(Tree *tree, int i)
|
|
{
|
|
if (tree->type == VALUE) return tree->value;
|
|
if (tree->type == ATOMARRAY) return tree->array[i*tree->nstride];
|
|
if (tree->type == TYPEARRAY) return tree->array[atom->type[i]];
|
|
|
|
if (tree->type == ADD)
|
|
return eval_tree(tree->left,i) + eval_tree(tree->right,i);
|
|
if (tree->type == SUBTRACT)
|
|
return eval_tree(tree->left,i) - eval_tree(tree->right,i);
|
|
if (tree->type == MULTIPLY)
|
|
return eval_tree(tree->left,i) * eval_tree(tree->right,i);
|
|
if (tree->type == DIVIDE) {
|
|
double denom = eval_tree(tree->right,i);
|
|
if (denom == 0.0) error->all("Divide by 0 in variable formula");
|
|
return eval_tree(tree->left,i) / denom;
|
|
}
|
|
if (tree->type == CARAT) {
|
|
double exponent = eval_tree(tree->right,i);
|
|
if (exponent == 0.0) error->all("Power by 0 in variable formula");
|
|
return pow(eval_tree(tree->left,i),exponent);
|
|
}
|
|
if (tree->type == UNARY)
|
|
return -eval_tree(tree->left,i);
|
|
if (tree->type == SQRT) {
|
|
double arg = eval_tree(tree->left,i);
|
|
if (arg < 0.0) error->all("Sqrt of negative in variable formula");
|
|
return sqrt(arg);
|
|
}
|
|
if (tree->type == EXP)
|
|
return exp(eval_tree(tree->left,i));
|
|
if (tree->type == LN) {
|
|
double arg = eval_tree(tree->left,i);
|
|
if (arg <= 0.0) error->all("Log of negative/zero in variable formula");
|
|
return log(arg);
|
|
}
|
|
|
|
return 0.0;
|
|
}
|
|
|
|
/* ---------------------------------------------------------------------- */
|
|
|
|
void Variable::free_tree(Tree *tree)
|
|
{
|
|
if (tree->left) free_tree(tree->left);
|
|
if (tree->right) free_tree(tree->right);
|
|
delete tree;
|
|
}
|
|
|
|
/* ----------------------------------------------------------------------
|
|
find matching parenthesis in str, allocate contents = str between parens
|
|
i = left paren
|
|
return loc or right paren
|
|
------------------------------------------------------------------------- */
|
|
|
|
int Variable::find_matching_paren(char *str, int i,char *&contents)
|
|
{
|
|
// istop = matching ')' at same level, allowing for nested parens
|
|
|
|
int istart = i;
|
|
int ilevel = 0;
|
|
while (1) {
|
|
i++;
|
|
if (!str[i]) break;
|
|
if (str[i] == '(') ilevel++;
|
|
else if (str[i] == ')' && ilevel) ilevel--;
|
|
else if (str[i] == ')') break;
|
|
}
|
|
if (!str[i]) error->all("Invalid syntax in variable formula");
|
|
int istop = i;
|
|
|
|
int n = istop - istart - 1;
|
|
contents = new char[n+1];
|
|
strncpy(contents,&str[istart+1],n);
|
|
contents[n] = '\0';
|
|
|
|
return istop;
|
|
}
|
|
|
|
/* ----------------------------------------------------------------------
|
|
find int between brackets and set index to its value
|
|
if emptyflag, then brackets can be empty (index = 0)
|
|
else if not emptyflag and brackets empty, is an error
|
|
else contents of brackets must be positive int
|
|
i = left bracket
|
|
return loc of right bracket
|
|
------------------------------------------------------------------------- */
|
|
|
|
int Variable::int_between_brackets(char *str, int i, int &index, int emptyflag)
|
|
{
|
|
// istop = matching ']'
|
|
|
|
int istart = i;
|
|
while (!str[i] || str[i] != ']') i++;
|
|
if (!str[i]) error->all("Invalid syntax in variable formula");
|
|
int istop = i;
|
|
|
|
int n = istop - istart - 1;
|
|
char *arg = new char[n+1];
|
|
strncpy(arg,&str[istart+1],n);
|
|
arg[n] = '\0';
|
|
|
|
if (n == 0 && emptyflag) index = 0;
|
|
else if (n == 0) error->all("Empty brackets in variable formula");
|
|
else {
|
|
index = atoi(arg);
|
|
if (index <= 0) error->all("Invalid index in variable formula");
|
|
}
|
|
delete [] arg;
|
|
|
|
return istop;
|
|
}
|
|
|
|
/* ----------------------------------------------------------------------
|
|
process a math function in formula
|
|
push result onto tree or arg stack
|
|
word = math function
|
|
contents = str bewteen parentheses
|
|
return 0 if not a match, 1 if successfully processed
|
|
customize by adding a math function: sqrt(),exp(),log()
|
|
------------------------------------------------------------------------- */
|
|
|
|
int Variable::math_function(char *word, char *contents, Tree **tree,
|
|
Tree **treestack, int &ntreestack,
|
|
double *argstack, int &nargstack)
|
|
{
|
|
// word not a match to any math function
|
|
|
|
if (strcmp(word,"sqrt") && strcmp(word,"exp") && strcmp(word,"ln"))
|
|
return 0;
|
|
|
|
Tree *newtree;
|
|
double value;
|
|
|
|
if (tree) {
|
|
newtree = new Tree();
|
|
Tree *argtree;
|
|
double tmp = evaluate(contents,&argtree);
|
|
newtree->left = argtree;
|
|
treestack[ntreestack++] = newtree;
|
|
} else value = evaluate(contents,NULL);
|
|
|
|
if (strcmp(word,"sqrt") == 0) {
|
|
if (tree) newtree->type = SQRT;
|
|
else {
|
|
if (value < 0.0) error->all("Sqrt of negative in variable formula");
|
|
argstack[nargstack++] = sqrt(value);
|
|
}
|
|
|
|
} else if (strcmp(word,"exp") == 0) {
|
|
if (tree) newtree->type = EXP;
|
|
else argstack[nargstack++] = exp(value);
|
|
|
|
} else if (strcmp(word,"ln") == 0) {
|
|
if (tree) newtree->type = LN;
|
|
else {
|
|
if (value <= 0.0) error->all("Log of zero/negative in variable formula");
|
|
argstack[nargstack++] = log(value);
|
|
}
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
|
|
/* ----------------------------------------------------------------------
|
|
process a group function in formula
|
|
push result onto tree or arg stack
|
|
word = group function
|
|
contents = str bewteen parentheses with one or two args
|
|
return 0 if not a match, 1 if successfully processed
|
|
customize by adding a group function:
|
|
count(group),mass(group),charge(group),
|
|
xcm(group,dim),vcm(group,dim),fcm(group,dim),
|
|
bound(group,xmin),gyration(group)
|
|
------------------------------------------------------------------------- */
|
|
|
|
int Variable::group_function(char *word, char *contents, Tree **tree,
|
|
Tree **treestack, int &ntreestack,
|
|
double *argstack, int &nargstack)
|
|
{
|
|
// parse contents for arg1,arg2 separated by comma
|
|
|
|
char *arg1,*arg2;
|
|
char *ptr = strchr(contents,',');
|
|
if (ptr) *ptr = '\0';
|
|
int n = strlen(contents) + 1;
|
|
arg1 = new char[n];
|
|
strcpy(arg1,contents);
|
|
if (ptr) {
|
|
n = strlen(ptr+1) + 1;
|
|
arg2 = new char[n];
|
|
strcpy(arg2,ptr+1);
|
|
} else arg2 = NULL;
|
|
|
|
int igroup = group->find(arg1);
|
|
if (igroup == -1)
|
|
error->all("Group ID in variable formula does not exist");
|
|
|
|
Tree *newtree;
|
|
double value;
|
|
|
|
if (tree) {
|
|
newtree = new Tree();
|
|
newtree->type = VALUE;
|
|
treestack[ntreestack++] = newtree;
|
|
}
|
|
|
|
// match word to group function
|
|
|
|
if (strcmp(word,"count") == 0) {
|
|
if (arg2)
|
|
error->all("Invalid group function in variable formula");
|
|
value = group->count(igroup);
|
|
|
|
} else if (strcmp(word,"mass") == 0) {
|
|
if (arg2)
|
|
error->all("Invalid group function in variable formula");
|
|
value = group->mass(igroup);
|
|
|
|
} else if (strcmp(word,"charge") == 0) {
|
|
if (arg2)
|
|
error->all("Invalid group function in variable formula");
|
|
value = group->charge(igroup);
|
|
|
|
} else if (strcmp(word,"xcm") == 0) {
|
|
if (!arg2)
|
|
error->all("Invalid group function in variable formula");
|
|
atom->check_mass();
|
|
double masstotal = group->mass(igroup);
|
|
double xcm[3];
|
|
group->xcm(igroup,masstotal,xcm);
|
|
if (strcmp(arg2,"x") == 0) value = xcm[0];
|
|
else if (strcmp(arg2,"y") == 0) value = xcm[1];
|
|
else if (strcmp(arg2,"z") == 0) value = xcm[2];
|
|
else error->all("Invalid group function in variable formula");
|
|
|
|
} else if (strcmp(word,"vcm") == 0) {
|
|
if (!arg2)
|
|
error->all("Invalid group function in variable formula");
|
|
atom->check_mass();
|
|
double masstotal = group->mass(igroup);
|
|
double vcm[3];
|
|
group->vcm(igroup,masstotal,vcm);
|
|
if (strcmp(arg2,"x") == 0) value = vcm[0];
|
|
else if (strcmp(arg2,"y") == 0) value = vcm[1];
|
|
else if (strcmp(arg2,"z") == 0) value = vcm[2];
|
|
else error->all("Invalid group function in variable formula");
|
|
|
|
} else if (strcmp(word,"fcm") == 0) {
|
|
if (!arg2)
|
|
error->all("Invalid group function in variable formula");
|
|
double fcm[3];
|
|
group->fcm(igroup,fcm);
|
|
if (strcmp(arg2,"x") == 0) value = fcm[0];
|
|
else if (strcmp(arg2,"y") == 0) value = fcm[1];
|
|
else if (strcmp(arg2,"z") == 0) value = fcm[2];
|
|
else error->all("Invalid group function in variable formula");
|
|
|
|
} else if (strcmp(word,"bound") == 0) {
|
|
if (!arg2)
|
|
error->all("Invalid group function in variable formula");
|
|
double minmax[6];
|
|
group->bounds(igroup,minmax);
|
|
if (strcmp(arg2,"xmin") == 0) value = minmax[0];
|
|
else if (strcmp(arg2,"xmax") == 0) value = minmax[1];
|
|
else if (strcmp(arg2,"ymin") == 0) value = minmax[2];
|
|
else if (strcmp(arg2,"ymax") == 0) value = minmax[3];
|
|
else if (strcmp(arg2,"zmin") == 0) value = minmax[4];
|
|
else if (strcmp(arg2,"zmax") == 0) value = minmax[5];
|
|
else error->all("Invalid group function in variable formula");
|
|
|
|
} else if (strcmp(word,"gyration") == 0) {
|
|
atom->check_mass();
|
|
double masstotal = group->mass(igroup);
|
|
double xcm[3];
|
|
group->xcm(igroup,masstotal,xcm);
|
|
value = group->gyration(igroup,masstotal,xcm);
|
|
|
|
} else return 0;
|
|
|
|
delete [] arg1;
|
|
delete [] arg2;
|
|
|
|
if (tree) newtree->value= value;
|
|
else argstack[nargstack++] = value;
|
|
|
|
return 1;
|
|
}
|
|
|
|
/* ----------------------------------------------------------------------
|
|
extract a global value from a per-atom quantity in a formula
|
|
flag = 0 -> word is an atom vector
|
|
flag = 1 -> vector is a per-atom compute or fix quantity with nstride
|
|
id = positive global ID of atom, converted to local index
|
|
push result onto tree or arg stack
|
|
customize by adding an atom vector: mass,x,y,z,vx,vy,vz,fx,fy,fz
|
|
------------------------------------------------------------------------- */
|
|
|
|
void Variable::peratom2global(int flag, char *word,
|
|
double *vector, int nstride, int id,
|
|
Tree **tree, Tree **treestack, int &ntreestack,
|
|
double *argstack, int &nargstack)
|
|
{
|
|
if (atom->map_style == 0)
|
|
error->all("Indexed per-atom vector in variable formula without atom map");
|
|
|
|
int index = atom->map(id);
|
|
|
|
double mine;
|
|
if (index >= 0 && index < atom->nlocal) {
|
|
|
|
if (flag == 0) {
|
|
if (strcmp(word,"mass") == 0) {
|
|
if (atom->mass) mine = atom->mass[atom->type[index]];
|
|
else mine = atom->rmass[index];
|
|
}
|
|
else if (strcmp(word,"x") == 0) mine = atom->x[index][0];
|
|
else if (strcmp(word,"y") == 0) mine = atom->x[index][1];
|
|
else if (strcmp(word,"z") == 0) mine = atom->x[index][2];
|
|
else if (strcmp(word,"vx") == 0) mine = atom->v[index][0];
|
|
else if (strcmp(word,"vy") == 0) mine = atom->v[index][1];
|
|
else if (strcmp(word,"vz") == 0) mine = atom->v[index][2];
|
|
else if (strcmp(word,"fx") == 0) mine = atom->f[index][0];
|
|
else if (strcmp(word,"fy") == 0) mine = atom->f[index][1];
|
|
else if (strcmp(word,"fz") == 0) mine = atom->f[index][2];
|
|
|
|
else error->one("Invalid atom vector in variable formula");
|
|
|
|
} else mine = vector[index*nstride];
|
|
|
|
} else mine = 0.0;
|
|
|
|
double value;
|
|
MPI_Allreduce(&mine,&value,1,MPI_DOUBLE,MPI_SUM,world);
|
|
|
|
if (tree) {
|
|
Tree *newtree = new Tree();
|
|
newtree->type = VALUE;
|
|
newtree->value = value;
|
|
treestack[ntreestack++] = newtree;
|
|
} else argstack[nargstack++] = value;
|
|
}
|
|
|
|
/* ----------------------------------------------------------------------
|
|
process an atom vector in formula
|
|
push result onto tree
|
|
word = atom vector
|
|
customize by adding an atom vector: mass,x,y,z,vx,vy,vz,fx,fy,fz
|
|
------------------------------------------------------------------------- */
|
|
|
|
void Variable::atom_vector(char *word, Tree **tree,
|
|
Tree **treestack, int &ntreestack)
|
|
{
|
|
if (tree == NULL)
|
|
error->all("Atom vector in equal-style variable formula");
|
|
|
|
Tree *newtree = new Tree();
|
|
newtree->type = ATOMARRAY;
|
|
newtree->nstride = 3;
|
|
treestack[ntreestack++] = newtree;
|
|
|
|
if (strcmp(word,"mass") == 0) {
|
|
if (atom->mass) {
|
|
newtree->type = TYPEARRAY;
|
|
newtree->array = atom->mass;
|
|
} else {
|
|
newtree->nstride = 1;
|
|
newtree->array = atom->rmass;
|
|
}
|
|
}
|
|
else if (strcmp(word,"x") == 0) newtree->array = &atom->x[0][0];
|
|
else if (strcmp(word,"y") == 0) newtree->array = &atom->x[0][1];
|
|
else if (strcmp(word,"z") == 0) newtree->array = &atom->x[0][2];
|
|
else if (strcmp(word,"vx") == 0) newtree->array = &atom->v[0][0];
|
|
else if (strcmp(word,"vy") == 0) newtree->array = &atom->v[0][1];
|
|
else if (strcmp(word,"vz") == 0) newtree->array = &atom->v[0][2];
|
|
else if (strcmp(word,"fx") == 0) newtree->array = &atom->f[0][0];
|
|
else if (strcmp(word,"fy") == 0) newtree->array = &atom->f[0][1];
|
|
else if (strcmp(word,"fz") == 0) newtree->array = &atom->f[0][2];
|
|
|
|
else error->all("Invalid atom vector in variable formula");
|
|
}
|