Files
lammps/src/fix_ave_chunk.cpp

1083 lines
37 KiB
C++

// clang-format off
/* ----------------------------------------------------------------------
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_ave_chunk.h"
#include "arg_info.h"
#include "atom.h"
#include "comm.h"
#include "compute.h"
#include "compute_chunk_atom.h"
#include "domain.h"
#include "error.h"
#include "force.h"
#include "input.h"
#include "memory.h"
#include "modify.h"
#include "update.h"
#include "variable.h"
#include <cstring>
using namespace LAMMPS_NS;
using namespace FixConst;
enum { SCALAR, VECTOR };
enum { SAMPLE, ALL };
enum { NOSCALE, ATOM };
enum { ONE, RUNNING, WINDOW };
/* ---------------------------------------------------------------------- */
FixAveChunk::FixAveChunk(LAMMPS *lmp, int narg, char **arg) :
Fix(lmp, narg, arg), nvalues(0), nrepeat(0), format(nullptr), tstring(nullptr),
sstring(nullptr), id_bias(nullptr), tbias(nullptr), fp(nullptr), chunk_volume_vec(nullptr),
idchunk(nullptr), cchunk(nullptr), varatom(nullptr), count_one(nullptr), count_many(nullptr),
count_sum(nullptr), values_one(nullptr), values_many(nullptr), values_sum(nullptr),
count_total(nullptr), count_list(nullptr), values_total(nullptr), values_list(nullptr)
{
if (narg < 7) utils::missing_cmd_args(FLERR, "fix ave/chunk", error);
nevery = utils::inumeric(FLERR, arg[3], false, lmp);
nrepeat = utils::inumeric(FLERR, arg[4], false, lmp);
nfreq = utils::inumeric(FLERR, arg[5], false, lmp);
idchunk = utils::strdup(arg[6]);
global_freq = nfreq;
no_change_box = 1;
time_depend = 1;
char *group = arg[1];
// expand args if any have wildcard character "*"
const int ioffset = 7;
int expand = 0;
char **earg;
int *amap = nullptr;
int nargnew = utils::expand_args(FLERR, narg - ioffset, &arg[ioffset], 1, earg, lmp, &amap);
if (earg != &arg[ioffset]) expand = 1;
arg = earg;
// parse values until one isn't recognized
densityflag = 0;
int iarg = 0;
values.clear();
while (iarg < nargnew) {
value_t val;
val.id = "";
val.val.c = nullptr;
if (expand) val.iarg = amap[iarg] + ioffset;
else val.iarg = iarg + ioffset;
if (strcmp(arg[iarg],"vx") == 0) {
val.which = ArgInfo::V;
val.argindex = 0;
} else if (strcmp(arg[iarg],"vy") == 0) {
val.which = ArgInfo::V;
val.argindex = 1;
} else if (strcmp(arg[iarg],"vz") == 0) {
val.which = ArgInfo::V;
val.argindex = 2;
} else if (strcmp(arg[iarg],"fx") == 0) {
val.which = ArgInfo::F;
val.argindex = 0;
} else if (strcmp(arg[iarg],"fy") == 0) {
val.which = ArgInfo::F;
val.argindex = 1;
} else if (strcmp(arg[iarg],"fz") == 0) {
val.which = ArgInfo::F;
val.argindex = 2;
} else if (strcmp(arg[iarg],"mass") == 0) {
val.which = ArgInfo::MASS;
val.argindex = 0;
} else if (strcmp(arg[iarg],"density/number") == 0) {
densityflag = 1;
val.which = ArgInfo::DENSITY_NUMBER;
val.argindex = 0;
} else if (strcmp(arg[iarg],"density/mass") == 0) {
densityflag = 1;
val.which = ArgInfo::DENSITY_MASS;
val.argindex = 0;
} else if (strcmp(arg[iarg],"temp") == 0) {
val.which = ArgInfo::TEMPERATURE;
val.argindex = 0;
} else {
ArgInfo argi(arg[iarg]);
if (argi.get_type() == ArgInfo::NONE) break;
if ((argi.get_type() == ArgInfo::UNKNOWN) || (argi.get_dim() > 1))
error->all(FLERR, val.iarg, "Unknown fix ave/chunk data value: {}", arg[iarg]);
val.which = argi.get_type();
val.argindex = argi.get_index1();
val.id = argi.get_name();
}
values.push_back(val);
iarg++;
}
nvalues = values.size();
if (nvalues == 0)
error->all(FLERR, ioffset,
"No values from computes, fixes, or variables used in fix ave/chunk command");
// optional args
normflag = ALL;
scaleflag = ATOM;
ave = ONE;
nwindow = 0;
biasflag = 0;
adof = domain->dimension;
cdof = 0.0;
overwrite = 0;
format = utils::strdup(" %g");
char *title1 = nullptr;
char *title2 = nullptr;
char *title3 = nullptr;
while (iarg < nargnew) {
if (strcmp(arg[iarg],"norm") == 0) {
if (iarg+2 > nargnew) utils::missing_cmd_args(FLERR, "fix ave/chunk norm", error);
if (strcmp(arg[iarg+1],"all") == 0) {
normflag = ALL;
scaleflag = ATOM;
} else if (strcmp(arg[iarg+1],"sample") == 0) {
normflag = SAMPLE;
scaleflag = ATOM;
} else if (strcmp(arg[iarg+1],"none") == 0) {
normflag = SAMPLE;
scaleflag = NOSCALE;
} else error->all(FLERR, iarg + 1, "Unknown fix ave/chunk norm mode: {}", arg[iarg+1]);
iarg += 2;
} else if (strcmp(arg[iarg],"ave") == 0) {
if (iarg+2 > nargnew) utils::missing_cmd_args(FLERR, "fix ave/chunk ave", error);
if (strcmp(arg[iarg+1],"one") == 0) ave = ONE;
else if (strcmp(arg[iarg+1],"running") == 0) ave = RUNNING;
else if (strcmp(arg[iarg+1],"window") == 0) ave = WINDOW;
else error->all(FLERR, iarg + 1, "Unknown fix ave/chunk ave mode: {}", arg[iarg+1]);
if (ave == WINDOW) {
if (iarg+3 > nargnew) utils::missing_cmd_args(FLERR, "fix ave/chunk ave window", error);
nwindow = utils::inumeric(FLERR,arg[iarg+2],false,lmp);
if (nwindow <= 0)
error->all(FLERR, iarg + 2, "Illegal fix ave/chunk number of windows: {}", nwindow);
}
iarg += 2;
if (ave == WINDOW) iarg++;
} else if (strcmp(arg[iarg],"bias") == 0) {
if (iarg+2 > nargnew) utils::missing_cmd_args(FLERR, "fix ave/chunk bias", error);
biasflag = 1;
id_bias = utils::strdup(arg[iarg+1]);
iarg += 2;
} else if (strcmp(arg[iarg],"adof") == 0) {
if (iarg+2 > nargnew) utils::missing_cmd_args(FLERR, "fix ave/chunk adof", error);
adof = utils::numeric(FLERR,arg[iarg+1],false,lmp);
iarg += 2;
} else if (strcmp(arg[iarg],"cdof") == 0) {
if (iarg+2 > nargnew) utils::missing_cmd_args(FLERR, "fix ave/chunk cdof", error);
cdof = utils::numeric(FLERR,arg[iarg+1],false,lmp);
iarg += 2;
} else if ((strcmp(arg[iarg],"file") == 0) || (strcmp(arg[iarg],"append") == 0)) {
if (iarg+2 > nargnew)
utils::missing_cmd_args(FLERR, std::string("fix ave/chunk ")+arg[iarg], error);
if (comm->me == 0) {
if (strcmp(arg[iarg],"file") == 0) fp = fopen(arg[iarg+1],"w");
else fp = fopen(arg[iarg+1],"a");
if (fp == nullptr)
error->one(FLERR, iarg+1, "Cannot open fix ave/chunk file {}: {}", arg[iarg+1],
utils::getsyserror());
}
iarg += 2;
} else if (strcmp(arg[iarg],"overwrite") == 0) {
overwrite = 1;
iarg += 1;
} else if (strcmp(arg[iarg],"format") == 0) {
if (iarg+2 > nargnew) utils::missing_cmd_args(FLERR, "fix ave/chunk format", error);
delete[] format;
format = utils::strdup(arg[iarg+1]);
iarg += 2;
} else if (strcmp(arg[iarg],"title1") == 0) {
if (iarg+2 > nargnew) utils::missing_cmd_args(FLERR, "fix ave/chunk title1", error);
delete[] title1;
title1 = utils::strdup(arg[iarg+1]);
iarg += 2;
} else if (strcmp(arg[iarg],"title2") == 0) {
if (iarg+2 > nargnew) utils::missing_cmd_args(FLERR, "fix ave/chunk title2", error);
delete[] title2;
title2 = utils::strdup(arg[iarg+1]);
iarg += 2;
} else if (strcmp(arg[iarg],"title3") == 0) {
if (iarg+2 > nargnew) utils::missing_cmd_args(FLERR, "fix ave/chunk title3", error);
delete[] title3;
title3 = utils::strdup(arg[iarg+1]);
iarg += 2;
} else error->all(FLERR, iarg, "Unknown fix ave/chunk keyword: {}", arg[iarg]);
}
// setup and error check
if (nevery <= 0) error->all(FLERR, 3, "Illegal fix ave/chunk nevery value: {}", nevery);
if (nrepeat <= 0) error->all(FLERR, 4, "Illegal fix ave/chunk nrepeat value: {}", nrepeat);
if (nfreq <= 0) error->all(FLERR, 5, "Illegal fix ave/chunk nfreq value: {}", nfreq);
if (nfreq % nevery || nrepeat*nevery > nfreq)
error->all(FLERR, Error::NOPOINTER, "Inconsistent fix ave/chunk nevery/nrepeat/nfreq values");
if (ave != RUNNING && overwrite)
error->all(FLERR, Error::NOPOINTER, "Fix ave/chunk overwrite keyword requires ave running setting");
if (biasflag) {
tbias = modify->get_compute_by_id(id_bias);
if (!tbias) error->all(FLERR, 6, "Could not find compute ID {} for temperature bias", id_bias);
if (tbias->tempflag == 0)
error->all(FLERR, 6, "Bias compute {} does not calculate temperature", id_bias);
if (tbias->tempbias == 0)
error->all(FLERR, 6, "Bias compute {} does not calculate a velocity bias", id_bias);
}
for (auto &val : values) {
if (val.which == ArgInfo::COMPUTE) {
val.val.c = modify->get_compute_by_id(val.id);
if (!val.val.c)
error->all(FLERR, val.iarg, "Compute ID {} for fix ave/chunk does not exist",val.id);
if (val.val.c->peratom_flag == 0)
error->all(FLERR, val.iarg, "Fix ave/chunk compute {} does not calculate per-atom values",val.id);
if (val.argindex == 0 && (val.val.c->size_peratom_cols != 0))
error->all(FLERR, val.iarg, "Fix ave/chunk compute {} does not calculate a per-atom vector",val.id);
if (val.argindex && (val.val.c->size_peratom_cols == 0))
error->all(FLERR, val.iarg, "Fix ave/chunk compute {} does not calculate a per-atom array",val.id);
if (val.argindex && (val.argindex > val.val.c->size_peratom_cols))
error->all(FLERR, val.iarg, "Fix ave/chunk compute {} vector is accessed out-of-range",val.id);
} else if (val.which == ArgInfo::FIX) {
val.val.f = modify->get_fix_by_id(val.id);
if (!val.val.f)
error->all(FLERR, val.iarg, "Fix ID {} for fix ave/chunk does not exist",val.id);
if (val.val.f->peratom_flag == 0)
error->all(FLERR, val.iarg, "Fix ave/chunk fix {} does not calculate per-atom values",val.id);
if (val.argindex == 0 && (val.val.f->size_peratom_cols != 0))
error->all(FLERR, val.iarg, "Fix ave/chunk fix {} does not calculate a per-atom vector",val.id);
if (val.argindex && (val.val.f->size_peratom_cols == 0))
error->all(FLERR, val.iarg, "Fix ave/chunk fix {} does not calculate a per-atom array",val.id);
if (val.argindex && val.argindex > val.val.f->size_peratom_cols)
error->all(FLERR, val.iarg, "Fix ave/chunk fix {} vector is accessed out-of-range",val.id);
} else if (val.which == ArgInfo::VARIABLE) {
val.val.v = input->variable->find(val.id.c_str());
if (val.val.v < 0)
error->all(FLERR, val.iarg, "Variable name {} for fix ave/chunk does not exist",val.id);
if (input->variable->atomstyle(val.val.v) == 0)
error->all(FLERR, val.iarg, "Fix ave/chunk variable {} is not atom-style variable",val.id);
}
}
// increment lock counter in compute chunk/atom
// only if nrepeat > 1 or ave = RUNNING/WINDOW,
// so that locking spans multiple timesteps
cchunk = dynamic_cast<ComputeChunkAtom *>(modify->get_compute_by_id(idchunk));
if (!cchunk)
error->all(FLERR, 6, "Chunk/atom compute {} does not exist or is incorrect style for fix ave/chunk",
idchunk);
if ((nrepeat > 1) || (ave == RUNNING) || (ave == WINDOW)) cchunk->lockcount++;
lockforever = 0;
// print file comment lines
if (fp && comm->me == 0) {
clearerr(fp);
if (title1) fprintf(fp,"%s\n",title1);
else fprintf(fp,"# Chunk-averaged data for fix %s and group %s\n", id, group);
if (title2) fprintf(fp,"%s\n",title2);
else fprintf(fp,"# Timestep Number-of-chunks Total-count\n");
if (title3) fprintf(fp,"%s\n",title3);
else {
int compress = cchunk->compress;
int ncoord = cchunk->ncoord;
if (!compress) {
if (ncoord == 0) fprintf(fp,"# Chunk Ncount");
else if (ncoord == 1) fprintf(fp,"# Chunk Coord1 Ncount");
else if (ncoord == 2) fprintf(fp,"# Chunk Coord1 Coord2 Ncount");
else if (ncoord == 3)
fprintf(fp,"# Chunk Coord1 Coord2 Coord3 Ncount");
} else {
if (ncoord == 0) fprintf(fp,"# Chunk OrigID Ncount");
else if (ncoord == 1) fprintf(fp,"# Chunk OrigID Coord1 Ncount");
else if (ncoord == 2) fprintf(fp,"# Chunk OrigID Coord1 Coord2 Ncount");
else if (ncoord == 3)
fprintf(fp,"# Chunk OrigID Coord1 Coord2 Coord3 Ncount");
}
for (int i = 0; i < nvalues; i++) fprintf(fp," %s",earg[i]);
fprintf(fp,"\n");
}
if (ferror(fp))
error->one(FLERR, Error::NOPOINTER, "Error writing file header");
filepos = platform::ftell(fp);
}
delete[] title1;
delete[] title2;
delete[] title3;
// if wildcard expansion occurred, free earg memory from expand_args()
// wait to do this until after file comment lines are printed
if (expand) {
for (int i = 0; i < nargnew; i++) delete[] earg[i];
memory->sfree(earg);
}
// this fix produces a global array
// size_array_rows is variable and set by allocate()
int compress = cchunk->compress;
int ncoord = cchunk->ncoord;
colextra = compress + ncoord;
array_flag = 1;
size_array_cols = colextra + 1 + nvalues;
size_array_rows_variable = 1;
extarray = 0;
// initializations
irepeat = 0;
iwindow = window_limit = 0;
normcount = 0;
maxvar = 0;
varatom = nullptr;
count_one = count_many = count_sum = count_total = nullptr;
count_list = nullptr;
values_one = values_many = values_sum = values_total = nullptr;
values_list = nullptr;
maxchunk = 0;
nchunk = 1;
allocate();
// nvalid = next step on which end_of_step does something
// add nvalid to all computes that store invocation times
// since don't know a priori which are invoked by this fix
// once in end_of_step() can set timestep for ones actually invoked
nvalid_last = -1;
nvalid = nextvalid();
modify->addstep_compute_all(nvalid);
}
/* ---------------------------------------------------------------------- */
FixAveChunk::~FixAveChunk()
{
if (fp && comm->me == 0) fclose(fp);
memory->destroy(varatom);
memory->destroy(count_one);
memory->destroy(count_many);
memory->destroy(count_sum);
memory->destroy(count_total);
memory->destroy(count_list);
memory->destroy(values_one);
memory->destroy(values_many);
memory->destroy(values_sum);
memory->destroy(values_total);
memory->destroy(values_list);
// decrement lock counter in compute chunk/atom, it if still exists
if (nrepeat > 1 || ave == RUNNING || ave == WINDOW) {
cchunk = dynamic_cast<ComputeChunkAtom *>(modify->get_compute_by_id(idchunk));
if (cchunk) {
if (ave == RUNNING || ave == WINDOW) cchunk->unlock(this);
cchunk->lockcount--;
}
}
delete[] idchunk;
delete[] format;
}
/* ---------------------------------------------------------------------- */
int FixAveChunk::setmask()
{
int mask = 0;
mask |= END_OF_STEP;
return mask;
}
/* ---------------------------------------------------------------------- */
void FixAveChunk::init()
{
// set indices and check validity of all computes,fixes,variables
// check that fix frequency is acceptable
cchunk = dynamic_cast<ComputeChunkAtom *>(modify->get_compute_by_id(idchunk));
if (!cchunk)
error->all(FLERR, Error::NOLASTLINE, "Chunk/atom compute {} does not exist or is "
"incorrect style for fix ave/chunk",idchunk);
if (biasflag) {
tbias = modify->get_compute_by_id(id_bias);
if (!tbias)
error->all(FLERR,"Could not find compute ID {} for temperature bias", id_bias);
}
for (auto &val : values) {
if (val.which == ArgInfo::COMPUTE) {
val.val.c = modify->get_compute_by_id(val.id);
if (!val.val.c)
error->all(FLERR, Error::NOLASTLINE, "Compute ID {} for fix ave/chunk does not exist", val.id);
} else if (val.which == ArgInfo::FIX) {
val.val.f = modify->get_fix_by_id(val.id);
if (!val.val.f)
error->all(FLERR, Error::NOLASTLINE, "Fix ID {} for fix ave/chunk does not exist", val.id);
if (nevery % val.val.f->peratom_freq)
error->all(FLERR, Error::NOLASTLINE, "Fix {} for fix ave/chunk not computed at compatible time",
val.id);
} else if (val.which == ArgInfo::VARIABLE) {
val.val.v = input->variable->find(val.id.c_str());
if (val.val.v < 0)
error->all(FLERR, Error::NOLASTLINE, "Variable name {} for fix ave/chunk does not exist"
, val.id);
}
}
// need to reset nvalid if nvalid < ntimestep b/c minimize was performed
if (nvalid < update->ntimestep) {
irepeat = 0;
nvalid = nextvalid();
modify->addstep_compute_all(nvalid);
}
}
/* ----------------------------------------------------------------------
only does averaging if nvalid = current timestep
do not call setup_chunks(), even though fix ave/chunk called setup_bins()
b/c could cause nchunk to change if Nfreq epoch crosses 2 runs
does mean that if change_box is used between runs to change box size,
that nchunk may not track it
------------------------------------------------------------------------- */
void FixAveChunk::setup(int /*vflag*/)
{
end_of_step();
}
/* ---------------------------------------------------------------------- */
void FixAveChunk::end_of_step()
{
int i,j,m,index;
// skip if not step which requires doing something
bigint ntimestep = update->ntimestep;
if (ntimestep != nvalid) return;
nvalid_last = nvalid;
// first sample within single Nfreq epoch
// zero out arrays that accumulate over many samples, but not across epochs
// invoke setup_chunks() to determine current nchunk
// re-allocate per-chunk arrays if needed
// invoke lock() in two cases:
// if nrepeat > 1: so nchunk cannot change until Nfreq epoch is over,
// will be unlocked on last repeat of this Nfreq
// if ave = RUNNING/WINDOW and not yet locked:
// set forever, will be unlocked in fix destructor
// wrap setup_chunks in clearstep/addstep b/c it may invoke computes
// both nevery and nfreq are future steps,
// since call below to cchunk->ichunk()
// does not re-invoke internal cchunk compute on this same step
if (irepeat == 0) {
if (cchunk->computeflag) modify->clearstep_compute();
nchunk = cchunk->setup_chunks();
if (cchunk->computeflag) {
modify->addstep_compute(ntimestep+nevery);
modify->addstep_compute(ntimestep+nfreq);
}
allocate();
if (nrepeat > 1 && ave == ONE)
cchunk->lock(this,ntimestep,ntimestep+((bigint)nrepeat-1)*nevery);
else if ((ave == RUNNING || ave == WINDOW) && !lockforever) {
cchunk->lock(this,update->ntimestep,-1);
lockforever = 1;
}
for (m = 0; m < nchunk; m++) {
count_many[m] = count_sum[m] = 0.0;
for (i = 0; i < nvalues; i++) values_many[m][i] = 0.0;
}
// if any DENSITY requested, invoke setup_chunks() on each sampling step
// nchunk will not change but bin volumes might, e.g. for NPT simulation
} else if (densityflag) {
cchunk->setup_chunks();
}
// zero out arrays for one sample
for (m = 0; m < nchunk; m++) {
count_one[m] = 0.0;
for (i = 0; i < nvalues; i++) values_one[m][i] = 0.0;
}
// compute chunk/atom assigns atoms to chunk IDs
// extract ichunk index vector from compute
// ichunk = 1 to Nchunk for included atoms, 0 for excluded atoms
// wrap compute_ichunk in clearstep/addstep b/c it may invoke computes
if (cchunk->computeflag) modify->clearstep_compute();
cchunk->compute_ichunk();
int *ichunk = cchunk->ichunk;
if (cchunk->computeflag) modify->addstep_compute(ntimestep+nevery);
// perform the computation for one sample
// count # of atoms in each bin
// accumulate results of attributes,computes,fixes,variables to local copy
// sum within each chunk, only include atoms in fix group
// compute/fix/variable may invoke computes so wrap with clear/add
int *mask = atom->mask;
int nlocal = atom->nlocal;
for (i = 0; i < nlocal; i++)
if (mask[i] & groupbit && ichunk[i] > 0)
count_one[ichunk[i]-1]++;
modify->clearstep_compute();
m = 0;
for (auto &val : values) {
j = val.argindex;
// V,F adds velocities,forces to values
if (val.which == ArgInfo::V || val.which == ArgInfo::F) {
double **attribute;
if (val.which == ArgInfo::V) attribute = atom->v;
else attribute = atom->f;
for (i = 0; i < nlocal; i++)
if (mask[i] & groupbit && ichunk[i] > 0) {
index = ichunk[i]-1;
values_one[index][m] += attribute[i][j];
}
// DENSITY_NUMBER adds 1 to values
} else if (val.which == ArgInfo::DENSITY_NUMBER) {
for (i = 0; i < nlocal; i++)
if (mask[i] & groupbit && ichunk[i] > 0) {
index = ichunk[i]-1;
values_one[index][m] += 1.0;
}
// DENSITY_MASS or MASS adds mass to values
} else if ((val.which == ArgInfo::DENSITY_MASS) || (val.which == ArgInfo::MASS)) {
int *type = atom->type;
double *mass = atom->mass;
double *rmass = atom->rmass;
if (rmass) {
for (i = 0; i < nlocal; i++)
if (mask[i] & groupbit && ichunk[i] > 0) {
index = ichunk[i]-1;
values_one[index][m] += rmass[i];
}
} else {
for (i = 0; i < nlocal; i++)
if (mask[i] & groupbit && ichunk[i] > 0) {
index = ichunk[i]-1;
values_one[index][m] += mass[type[i]];
}
}
// TEMPERATURE adds KE to values
// subtract and restore velocity bias if requested
} else if (val.which == ArgInfo::TEMPERATURE) {
if (biasflag) {
if (tbias->invoked_scalar != ntimestep) tbias->compute_scalar();
tbias->remove_bias_all();
}
double **v = atom->v;
int *type = atom->type;
double *mass = atom->mass;
double *rmass = atom->rmass;
if (rmass) {
for (i = 0; i < nlocal; i++)
if (mask[i] & groupbit && ichunk[i] > 0) {
index = ichunk[i]-1;
values_one[index][m] +=
(v[i][0]*v[i][0] + v[i][1]*v[i][1] + v[i][2]*v[i][2]) * rmass[i];
}
} else {
for (i = 0; i < nlocal; i++)
if (mask[i] & groupbit && ichunk[i] > 0) {
index = ichunk[i]-1;
values_one[index][m] +=
(v[i][0]*v[i][0] + v[i][1]*v[i][1] + v[i][2]*v[i][2]) *
mass[type[i]];
}
}
if (biasflag) tbias->restore_bias_all();
// COMPUTE adds its scalar or vector component to values
// invoke compute if not previously invoked
} else if (val.which == ArgInfo::COMPUTE) {
if (!(val.val.c->invoked_flag & Compute::INVOKED_PERATOM)) {
val.val.c->compute_peratom();
val.val.c->invoked_flag |= Compute::INVOKED_PERATOM;
}
double *vector = val.val.c->vector_atom;
double **array = val.val.c->array_atom;
int jm1 = j - 1;
for (i = 0; i < nlocal; i++)
if (mask[i] & groupbit && ichunk[i] > 0) {
index = ichunk[i]-1;
if (j == 0) values_one[index][m] += vector[i];
else values_one[index][m] += array[i][jm1];
}
// FIX adds its scalar or vector component to values
// access fix fields, guaranteed to be ready
} else if (val.which == ArgInfo::FIX) {
double *vector = val.val.f->vector_atom;
double **array = val.val.f->array_atom;
int jm1 = j - 1;
for (i = 0; i < nlocal; i++)
if (mask[i] & groupbit && ichunk[i] > 0) {
index = ichunk[i]-1;
if (j == 0) values_one[index][m] += vector[i];
else values_one[index][m] += array[i][jm1];
}
// VARIABLE adds its per-atom quantities to values
// evaluate atom-style variable
} else if (val.which == ArgInfo::VARIABLE) {
if (atom->nmax > maxvar) {
maxvar = atom->nmax;
memory->destroy(varatom);
memory->create(varatom,maxvar,"ave/chunk:varatom");
}
input->variable->compute_atom(val.val.v,igroup,varatom,1,0);
for (i = 0; i < nlocal; i++)
if (mask[i] & groupbit && ichunk[i] > 0) {
index = ichunk[i]-1;
values_one[index][m] += varatom[i];
}
}
++m;
}
// process the current sample
// if normflag = ALL, accumulate values,count separately to many
// if normflag = SAMPLE, one = value/count, accumulate one to many
// count is MPI summed here, value is MPI summed below across samples
// exception is TEMPERATURE: normalize by DOF
// exception is DENSITY_NUMBER:
// normalize by bin volume, not by atom count
// exception is DENSITY_MASS:
// scale by mv2d, normalize by bin volume, not by atom count
// exception is scaleflag = NOSCALE (norm = NONE):
// no normalize by atom count
// check last so other options can take precedence
double mvv2e = force->mvv2e;
double mv2d = force->mv2d;
double boltz = force->boltz;
if (normflag == ALL) {
for (m = 0; m < nchunk; m++) {
count_many[m] += count_one[m];
for (j = 0; j < nvalues; j++)
values_many[m][j] += values_one[m][j];
}
} else if (normflag == SAMPLE) {
MPI_Allreduce(count_one,count_many,nchunk,MPI_DOUBLE,MPI_SUM,world);
if (cchunk->chunk_volume_vec) {
volflag = VECTOR;
chunk_volume_vec = cchunk->chunk_volume_vec;
} else {
volflag = SCALAR;
chunk_volume_scalar = cchunk->chunk_volume_scalar;
}
for (m = 0; m < nchunk; m++) {
if (count_many[m] > 0.0)
for (j = 0; j < nvalues; j++) {
if (values[j].which == ArgInfo::TEMPERATURE) {
values_many[m][j] += mvv2e*values_one[m][j] /
((cdof + adof*count_many[m]) * boltz);
} else if (values[j].which == ArgInfo::DENSITY_NUMBER) {
if (volflag == SCALAR) values_one[m][j] /= chunk_volume_scalar;
else values_one[m][j] /= chunk_volume_vec[m];
values_many[m][j] += values_one[m][j];
} else if (values[j].which == ArgInfo::DENSITY_MASS) {
if (volflag == SCALAR) values_one[m][j] /= chunk_volume_scalar;
else values_one[m][j] /= chunk_volume_vec[m];
values_many[m][j] += mv2d*values_one[m][j];
} else if (scaleflag == NOSCALE) {
values_many[m][j] += values_one[m][j];
} else {
values_many[m][j] += values_one[m][j]/count_many[m];
}
}
count_sum[m] += count_many[m];
}
}
// done if irepeat < nrepeat
// else reset irepeat and nvalid
irepeat++;
if (irepeat < nrepeat) {
nvalid += nevery;
modify->addstep_compute(nvalid);
return;
}
irepeat = 0;
nvalid = ntimestep+nfreq - ((bigint)nrepeat-1)*nevery;
modify->addstep_compute(nvalid);
// unlock compute chunk/atom at end of Nfreq epoch
// do not unlock if ave = RUNNING or WINDOW
if (nrepeat > 1 && ave == ONE) cchunk->unlock(this);
// time average across samples
// if normflag = ALL, final is total value / total count
// exception is TEMPERATURE: normalize by DOF for total count
// exception is DENSITY_NUMBER:
// normalize by final bin_volume and repeat, not by total count
// exception is DENSITY_MASS:
// scale by mv2d, normalize by bin volume and repeat, not by total count
// exception is scaleflag == NOSCALE:
// normalize by repeat, not by total count
// check last so other options can take precedence
// if normflag = SAMPLE, final is sum of ave / repeat
double repeat = nrepeat;
if (normflag == ALL) {
MPI_Allreduce(count_many,count_sum,nchunk,MPI_DOUBLE,MPI_SUM,world);
MPI_Allreduce(&values_many[0][0],&values_sum[0][0],nchunk*nvalues,
MPI_DOUBLE,MPI_SUM,world);
if (cchunk->chunk_volume_vec) {
volflag = VECTOR;
chunk_volume_vec = cchunk->chunk_volume_vec;
} else {
volflag = SCALAR;
chunk_volume_scalar = cchunk->chunk_volume_scalar;
}
for (m = 0; m < nchunk; m++) {
if (count_sum[m] > 0.0)
for (j = 0; j < nvalues; j++) {
if (values[j].which == ArgInfo::TEMPERATURE) {
values_sum[m][j] *= mvv2e/((repeat*cdof + adof*count_sum[m])*boltz);
} else if (values[j].which == ArgInfo::DENSITY_NUMBER) {
if (volflag == SCALAR) values_sum[m][j] /= chunk_volume_scalar;
else values_sum[m][j] /= chunk_volume_vec[m];
values_sum[m][j] /= repeat;
} else if (values[j].which == ArgInfo::DENSITY_MASS) {
if (volflag == SCALAR) values_sum[m][j] /= chunk_volume_scalar;
else values_sum[m][j] /= chunk_volume_vec[m];
values_sum[m][j] *= mv2d/repeat;
} else if (scaleflag == NOSCALE) {
values_sum[m][j] /= repeat;
} else {
values_sum[m][j] /= count_sum[m];
}
}
count_sum[m] /= repeat;
}
} else if (normflag == SAMPLE) {
MPI_Allreduce(&values_many[0][0],&values_sum[0][0],nchunk*nvalues,
MPI_DOUBLE,MPI_SUM,world);
for (m = 0; m < nchunk; m++) {
for (j = 0; j < nvalues; j++) values_sum[m][j] /= repeat;
count_sum[m] /= repeat;
}
}
// if ave = ONE, only single Nfreq timestep value is needed
// if ave = RUNNING, combine with all previous Nfreq timestep values
// if ave = WINDOW, comine with nwindow most recent Nfreq timestep values
if (ave == ONE) {
for (m = 0; m < nchunk; m++) {
for (i = 0; i < nvalues; i++)
values_total[m][i] = values_sum[m][i];
count_total[m] = count_sum[m];
}
normcount = 1;
} else if (ave == RUNNING) {
for (m = 0; m < nchunk; m++) {
for (i = 0; i < nvalues; i++)
values_total[m][i] += values_sum[m][i];
count_total[m] += count_sum[m];
}
normcount++;
} else if (ave == WINDOW) {
for (m = 0; m < nchunk; m++) {
for (i = 0; i < nvalues; i++) {
values_total[m][i] += values_sum[m][i];
if (window_limit) values_total[m][i] -= values_list[iwindow][m][i];
values_list[iwindow][m][i] = values_sum[m][i];
}
count_total[m] += count_sum[m];
if (window_limit) count_total[m] -= count_list[iwindow][m];
count_list[iwindow][m] = count_sum[m];
}
iwindow++;
if (iwindow == nwindow) {
iwindow = 0;
window_limit = 1;
}
if (window_limit) normcount = nwindow;
else normcount = iwindow;
}
// output result to file
if (fp && comm->me == 0) {
clearerr(fp);
if (overwrite) (void) platform::fseek(fp,filepos);
double count = 0.0;
for (m = 0; m < nchunk; m++) count += count_total[m];
utils::print(fp,"{} {} {}\n",ntimestep,nchunk,count);
int compress = cchunk->compress;
int *chunkID = cchunk->chunkID;
int ncoord = cchunk->ncoord;
double **coord = cchunk->coord;
if (!compress) {
if (ncoord == 0) {
for (m = 0; m < nchunk; m++) {
fprintf(fp," %d %g",m+1,count_total[m]/normcount);
for (i = 0; i < nvalues; i++)
fprintf(fp,format,values_total[m][i]/normcount);
fprintf(fp,"\n");
}
} else if (ncoord == 1) {
for (m = 0; m < nchunk; m++) {
fprintf(fp," %d %g %g",m+1,coord[m][0],
count_total[m]/normcount);
for (i = 0; i < nvalues; i++)
fprintf(fp,format,values_total[m][i]/normcount);
fprintf(fp,"\n");
}
} else if (ncoord == 2) {
for (m = 0; m < nchunk; m++) {
fprintf(fp," %d %g %g %g",m+1,coord[m][0],coord[m][1],
count_total[m]/normcount);
for (i = 0; i < nvalues; i++)
fprintf(fp,format,values_total[m][i]/normcount);
fprintf(fp,"\n");
}
} else if (ncoord == 3) {
for (m = 0; m < nchunk; m++) {
fprintf(fp," %d %g %g %g %g",m+1,
coord[m][0],coord[m][1],coord[m][2],count_total[m]/normcount);
for (i = 0; i < nvalues; i++)
fprintf(fp,format,values_total[m][i]/normcount);
fprintf(fp,"\n");
}
}
} else {
if (ncoord == 0) {
for (m = 0; m < nchunk; m++) {
fprintf(fp," %d %d %g",m+1,chunkID[m],count_total[m]/normcount);
for (i = 0; i < nvalues; i++)
fprintf(fp,format,values_total[m][i]/normcount);
fprintf(fp,"\n");
}
} else if (ncoord == 1) {
for (m = 0; m < nchunk; m++) {
j = chunkID[m];
fprintf(fp," %d %d %g %g",m+1,j,coord[j-1][0],
count_total[m]/normcount);
for (i = 0; i < nvalues; i++)
fprintf(fp,format,values_total[m][i]/normcount);
fprintf(fp,"\n");
}
} else if (ncoord == 2) {
for (m = 0; m < nchunk; m++) {
j = chunkID[m];
fprintf(fp," %d %d %g %g %g",m+1,j,coord[j-1][0],coord[j-1][1],
count_total[m]/normcount);
for (i = 0; i < nvalues; i++)
fprintf(fp,format,values_total[m][i]/normcount);
fprintf(fp,"\n");
}
} else if (ncoord == 3) {
for (m = 0; m < nchunk; m++) {
j = chunkID[m];
fprintf(fp," %d %d %g %g %g %g",m+1,j,coord[j-1][0],
coord[j-1][1],coord[j-1][2],count_total[m]/normcount);
for (i = 0; i < nvalues; i++)
fprintf(fp,format,values_total[m][i]/normcount);
fprintf(fp,"\n");
}
}
}
if (ferror(fp))
error->one(FLERR, Error::NOLASTLINE, "Error writing averaged chunk data");
fflush(fp);
if (overwrite) {
bigint fileend = platform::ftell(fp);
if ((fileend > 0) && (platform::ftruncate(fp,fileend)))
error->warning(FLERR, "Error while tuncating output: {}", utils::getsyserror());
}
}
}
/* ----------------------------------------------------------------------
allocate all per-chunk vectors
------------------------------------------------------------------------- */
void FixAveChunk::allocate()
{
size_array_rows = nchunk;
// reallocate chunk arrays if needed
if (nchunk > maxchunk) {
maxchunk = nchunk;
memory->grow(count_one,nchunk,"ave/chunk:count_one");
memory->grow(count_many,nchunk,"ave/chunk:count_many");
memory->grow(count_sum,nchunk,"ave/chunk:count_sum");
memory->grow(count_total,nchunk,"ave/chunk:count_total");
memory->grow(values_one,nchunk,nvalues,"ave/chunk:values_one");
memory->grow(values_many,nchunk,nvalues,"ave/chunk:values_many");
memory->grow(values_sum,nchunk,nvalues,"ave/chunk:values_sum");
memory->grow(values_total,nchunk,nvalues,"ave/chunk:values_total");
// only allocate count and values list for ave = WINDOW
if (ave == WINDOW) {
memory->create(count_list,nwindow,nchunk,"ave/chunk:count_list");
memory->create(values_list,nwindow,nchunk,nvalues,"ave/chunk:values_list");
}
// reinitialize regrown count/values total since they accumulate
int i,m;
for (m = 0; m < nchunk; m++) {
for (i = 0; i < nvalues; i++) values_total[m][i] = 0.0;
count_total[m] = 0.0;
}
}
}
/* ----------------------------------------------------------------------
return I,J array value
if I exceeds current nchunks, return 0.0 instead of generating an error
columns 1 to colextra = chunkID + ncoord
next column = count, remaining columns = Nvalues
------------------------------------------------------------------------- */
double FixAveChunk::compute_array(int i, int j)
{
if (values_total == nullptr) return 0.0;
if (i >= nchunk) return 0.0;
if (j < colextra) {
if (cchunk->compress) {
if (j == 0) return (double) cchunk->chunkID[i];
return cchunk->coord[i][j-1];
} else return cchunk->coord[i][j];
}
j -= colextra + 1;
if (!normcount) return 0.0;
if (j < 0) return count_total[i]/normcount;
return values_total[i][j]/normcount;
}
/* ----------------------------------------------------------------------
calculate nvalid = next step on which end_of_step does something
can be this timestep if multiple of nfreq and nrepeat = 1
else backup from next multiple of nfreq
------------------------------------------------------------------------- */
bigint FixAveChunk::nextvalid()
{
bigint nvalid = (update->ntimestep/nfreq)*nfreq + nfreq;
if (nvalid-nfreq == update->ntimestep && nrepeat == 1)
nvalid = update->ntimestep;
else
nvalid -= ((bigint)nrepeat-1)*nevery;
if (nvalid < update->ntimestep) nvalid += nfreq;
return nvalid;
}
/* ----------------------------------------------------------------------
memory usage of varatom and bins
------------------------------------------------------------------------- */
double FixAveChunk::memory_usage()
{
double bytes = (double)maxvar * sizeof(double); // varatom
bytes += (double)4*maxchunk * sizeof(double); // count one,many,sum,total
bytes += (double)nvalues*maxchunk * sizeof(double); // values one,many,sum,total
bytes += (double)nwindow*maxchunk * sizeof(double); // count_list
bytes += (double)nwindow*maxchunk*nvalues * sizeof(double); // values_list
return bytes;
}