Files
lammps/src/fix_deform.cpp
2023-07-18 09:25:22 -06:00

1572 lines
58 KiB
C++

// clang-format off
/* ----------------------------------------------------------------------
LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator
https://www.lammps.org/, Sandia National Laboratories
Steve Plimpton, sjplimp@sandia.gov
Copyright (2003) Sandia Corporation. Under the terms of Contract
DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains
certain rights in this software. This software is distributed under
the GNU General Public License.
See the README file in the top-level LAMMPS directory.
------------------------------------------------------------------------- */
/* ----------------------------------------------------------------------
Contributing author: Pieter in 't Veld (SNL), Joel Clemmer (SNL)
------------------------------------------------------------------------- */
#include "fix_deform.h"
#include "atom.h"
#include "comm.h"
#include "compute.h"
#include "domain.h"
#include "error.h"
#include "force.h"
#include "group.h"
#include "input.h"
#include "irregular.h"
#include "kspace.h"
#include "lattice.h"
#include "math_const.h"
#include "modify.h"
#include "update.h"
#include "variable.h"
#include <cmath>
#include <cstring>
using namespace LAMMPS_NS;
using namespace FixConst;
using namespace MathConst;
enum{NONE=0,FINAL,DELTA,SCALE,VEL,ERATE,TRATE,VOLUME,WIGGLE,VARIABLE,PRESSURE,PMEAN};
enum{ONE_FROM_ONE,ONE_FROM_TWO,TWO_FROM_ONE};
enum{NOCOUPLE=0,XYZ,XY,YZ,XZ};
/* ---------------------------------------------------------------------- */
FixDeform::FixDeform(LAMMPS *lmp, int narg, char **arg) : Fix(lmp, narg, arg),
irregular(nullptr), set(nullptr), id_temp(nullptr), id_press(nullptr)
{
if (narg < 4) error->all(FLERR,"Illegal fix deform command");
no_change_box = 1;
restart_global = 1;
pre_exchange_migrate = 1;
pcouple = NOCOUPLE;
dimension = domain->dimension;
max_h_rate = 0.0;
vol_balance_flag = 0;
normalize_pressure_flag = 0;
nevery = utils::inumeric(FLERR,arg[3],false,lmp);
if (nevery <= 0) error->all(FLERR,"Illegal fix deform command");
// set defaults
set = new Set[7];
memset(set,0,7*sizeof(Set));
// parse arguments
triclinic = domain->triclinic;
int index;
int iarg = 4;
while (iarg < narg) {
if (strcmp(arg[iarg],"x") == 0 ||
strcmp(arg[iarg],"y") == 0 ||
strcmp(arg[iarg],"z") == 0) {
if (strcmp(arg[iarg],"x") == 0) index = 0;
else if (strcmp(arg[iarg],"y") == 0) index = 1;
else if (strcmp(arg[iarg],"z") == 0) index = 2;
if (iarg+2 > narg) utils::missing_cmd_args(FLERR, "fix deform", error);
if (strcmp(arg[iarg+1],"final") == 0) {
if (iarg+4 > narg) utils::missing_cmd_args(FLERR, "fix deform final", error);
set[index].style = FINAL;
set[index].flo = utils::numeric(FLERR,arg[iarg+2],false,lmp);
set[index].fhi = utils::numeric(FLERR,arg[iarg+3],false,lmp);
iarg += 4;
} else if (strcmp(arg[iarg+1],"delta") == 0) {
if (iarg+4 > narg) utils::missing_cmd_args(FLERR, "fix deform delta", error);
set[index].style = DELTA;
set[index].dlo = utils::numeric(FLERR,arg[iarg+2],false,lmp);
set[index].dhi = utils::numeric(FLERR,arg[iarg+3],false,lmp);
iarg += 4;
} else if (strcmp(arg[iarg+1],"scale") == 0) {
if (iarg+3 > narg) utils::missing_cmd_args(FLERR, "fix deform scale", error);
set[index].style = SCALE;
set[index].scale = utils::numeric(FLERR,arg[iarg+2],false,lmp);
iarg += 3;
} else if (strcmp(arg[iarg+1],"vel") == 0) {
if (iarg+3 > narg) utils::missing_cmd_args(FLERR, "fix deform vel", error);
set[index].style = VEL;
set[index].vel = utils::numeric(FLERR,arg[iarg+2],false,lmp);
iarg += 3;
} else if (strcmp(arg[iarg+1],"erate") == 0) {
if (iarg+3 > narg) utils::missing_cmd_args(FLERR, "fix deform erate", error);
set[index].style = ERATE;
set[index].rate = utils::numeric(FLERR,arg[iarg+2],false,lmp);
iarg += 3;
} else if (strcmp(arg[iarg+1],"trate") == 0) {
if (iarg+3 > narg) utils::missing_cmd_args(FLERR, "fix deform trate", error);
set[index].style = TRATE;
set[index].rate = utils::numeric(FLERR,arg[iarg+2],false,lmp);
iarg += 3;
} else if (strcmp(arg[iarg+1],"volume") == 0) {
set[index].style = VOLUME;
iarg += 2;
} else if (strcmp(arg[iarg+1],"wiggle") == 0) {
if (iarg+4 > narg) utils::missing_cmd_args(FLERR, "fix deform wiggle", error);
set[index].style = WIGGLE;
set[index].amplitude = utils::numeric(FLERR,arg[iarg+2],false,lmp);
set[index].tperiod = utils::numeric(FLERR,arg[iarg+3],false,lmp);
if (set[index].tperiod <= 0.0)
error->all(FLERR,"Illegal fix deform wiggle period, must be positive");
iarg += 4;
} else if (strcmp(arg[iarg+1],"variable") == 0) {
if (iarg+4 > narg) utils::missing_cmd_args(FLERR, "fix deform variable", error);
set[index].style = VARIABLE;
if (strstr(arg[iarg+2],"v_") != arg[iarg+2])
error->all(FLERR,"Illegal fix deform variable name {}", arg[iarg+2]);
if (strstr(arg[iarg+3],"v_") != arg[iarg+3])
error->all(FLERR,"Illegal fix deform variable name {}", arg[iarg+3]);
delete[] set[index].hstr;
delete[] set[index].hratestr;
set[index].hstr = utils::strdup(&arg[iarg+2][2]);
set[index].hratestr = utils::strdup(&arg[iarg+3][2]);
iarg += 4;
} else if (strcmp(arg[iarg+1],"pressure") == 0) {
if (iarg+4 > narg) utils::missing_cmd_args(FLERR, "fix deform pressure", error);
set[index].style = PRESSURE;
if (strstr(arg[iarg+2],"v_") != arg[iarg+2]) {
set[index].ptarget = utils::numeric(FLERR,arg[iarg+2],false,lmp);
} else {
set[index].pstr = utils::strdup(&arg[iarg+2][2]);
set[index].pvar_flag = 1;
}
set[index].pgain = utils::numeric(FLERR,arg[iarg+3],false,lmp);
if (set[index].pgain <= 0.0)
error->all(FLERR,"Illegal fix deform pressure gain, must be positive");
iarg += 4;
} else if (strcmp(arg[iarg+1],"pressure/mean") == 0) {
if (iarg+4 > narg) utils::missing_cmd_args(FLERR, "fix deform pressure/mean", error);
set[index].style = PMEAN;
if (strstr(arg[iarg+2],"v_") != arg[iarg+2]) {
set[index].ptarget = utils::numeric(FLERR,arg[iarg+2],false,lmp);
} else {
set[index].pstr = utils::strdup(&arg[iarg+2][2]);
set[index].pvar_flag = 1;
}
set[index].pgain = utils::numeric(FLERR,arg[iarg+3],false,lmp);
if (set[index].pgain <= 0.0)
error->all(FLERR,"Illegal fix deform pressure gain, must be positive");
iarg += 4;
} else error->all(FLERR,"Illegal fix deform command argument: {}", arg[iarg+1]);
} else if (strcmp(arg[iarg],"xy") == 0 ||
strcmp(arg[iarg],"xz") == 0 ||
strcmp(arg[iarg],"yz") == 0) {
if (triclinic == 0)
error->all(FLERR,"Fix deform tilt factors require triclinic box");
if (strcmp(arg[iarg],"xy") == 0) index = 5;
else if (strcmp(arg[iarg],"xz") == 0) index = 4;
else if (strcmp(arg[iarg],"yz") == 0) index = 3;
if (iarg+2 > narg) utils::missing_cmd_args(FLERR, "fix deform", error);
if (strcmp(arg[iarg+1],"final") == 0) {
if (iarg+3 > narg) utils::missing_cmd_args(FLERR, "fix deform final", error);
set[index].style = FINAL;
set[index].ftilt = utils::numeric(FLERR,arg[iarg+2],false,lmp);
iarg += 3;
} else if (strcmp(arg[iarg+1],"delta") == 0) {
if (iarg+3 > narg) utils::missing_cmd_args(FLERR, "fix deform delta", error);
set[index].style = DELTA;
set[index].dtilt = utils::numeric(FLERR,arg[iarg+2],false,lmp);
iarg += 3;
} else if (strcmp(arg[iarg+1],"vel") == 0) {
if (iarg+3 > narg) utils::missing_cmd_args(FLERR, "fix deform vel", error);
set[index].style = VEL;
set[index].vel = utils::numeric(FLERR,arg[iarg+2],false,lmp);
iarg += 3;
} else if (strcmp(arg[iarg+1],"erate") == 0) {
if (iarg+3 > narg) utils::missing_cmd_args(FLERR, "fix deform erate", error);
set[index].style = ERATE;
set[index].rate = utils::numeric(FLERR,arg[iarg+2],false,lmp);
iarg += 3;
} else if (strcmp(arg[iarg+1],"trate") == 0) {
if (iarg+3 > narg) utils::missing_cmd_args(FLERR, "fix deform trate", error);
set[index].style = TRATE;
set[index].rate = utils::numeric(FLERR,arg[iarg+2],false,lmp);
iarg += 3;
} else if (strcmp(arg[iarg+1],"wiggle") == 0) {
if (iarg+4 > narg) utils::missing_cmd_args(FLERR, "fix deform wiggle", error);
set[index].style = WIGGLE;
set[index].amplitude = utils::numeric(FLERR,arg[iarg+2],false,lmp);
set[index].tperiod = utils::numeric(FLERR,arg[iarg+3],false,lmp);
if (set[index].tperiod <= 0.0)
error->all(FLERR,"Illegal fix deform wiggle period, must be positive");
iarg += 4;
} else if (strcmp(arg[iarg+1],"variable") == 0) {
if (iarg+4 > narg) utils::missing_cmd_args(FLERR, "fix deform variable", error);
set[index].style = VARIABLE;
if (strstr(arg[iarg+2],"v_") != arg[iarg+2])
error->all(FLERR,"Illegal fix deform variable name {}", arg[iarg+2]);
if (strstr(arg[iarg+3],"v_") != arg[iarg+3])
error->all(FLERR,"Illegal fix deform variable name {}", arg[iarg+3]);
delete[] set[index].hstr;
delete[] set[index].hratestr;
set[index].hstr = utils::strdup(&arg[iarg+2][2]);
set[index].hratestr = utils::strdup(&arg[iarg+3][2]);
iarg += 4;
} else if (strcmp(arg[iarg+1],"pressure") == 0) {
if (iarg+4 > narg) utils::missing_cmd_args(FLERR, "fix deform pressure", error);
set[index].style = PRESSURE;
if (strstr(arg[iarg+2],"v_") != arg[iarg+2]) {
set[index].ptarget = utils::numeric(FLERR,arg[iarg+2],false,lmp);
} else {
set[index].pstr = utils::strdup(&arg[iarg+2][2]);
set[index].pvar_flag = 1;
}
set[index].pgain = utils::numeric(FLERR,arg[iarg+3],false,lmp);
if (set[index].pgain <= 0.0)
error->all(FLERR,"Illegal fix deform pressure gain, must be positive");
iarg += 4;
} else error->all(FLERR,"Illegal fix deform command: {}", arg[iarg+1]);
} else if (strcmp(arg[iarg],"iso") == 0) {
index = 6;
if (strcmp(arg[iarg+1],"volume") == 0) {
set[index].style = VOLUME;
iarg += 2;
} else if (strcmp(arg[iarg+1],"pressure") == 0) {
if (iarg+4 > narg) utils::missing_cmd_args(FLERR, "fix deform pressure", error);
set[index].style = PRESSURE;
if (strstr(arg[iarg+2],"v_") != arg[iarg+2]) {
set[index].ptarget = utils::numeric(FLERR,arg[iarg+2],false,lmp);
} else {
set[index].pstr = utils::strdup(&arg[iarg+2][2]);
set[index].pvar_flag = 1;
}
set[index].pgain = utils::numeric(FLERR,arg[iarg+3],false,lmp);
if (set[index].pgain <= 0.0)
error->all(FLERR,"Illegal fix deform pressure gain, must be positive");
iarg += 4;
}
} else break;
}
// read options from end of input line
// no x remap effectively moves atoms within box, so set restart_pbc
options(narg-iarg,&arg[iarg]);
if (remapflag != Domain::X_REMAP) restart_pbc = 1;
// populate coupled pressure controls
if (pcouple != NOCOUPLE) {
int coupled_indices[3] = {0};
int j = -1;
double couple_gain, coupled_pressure;
char *couple_str;
if (pcouple == XYZ || pcouple == XY || pcouple == XZ)
coupled_indices[0] = 1;
if (pcouple == XYZ || pcouple == XY || pcouple == YZ)
coupled_indices[1] = 1;
if (pcouple == XYZ || pcouple == XZ || pcouple == YZ)
coupled_indices[2] = 1;
// Check coupled styles and find reference
for (int i = 0; i < 3; i++) {
if (coupled_indices[i]) {
set[i].coupled_flag = 1;
if (set[i].style != PRESSURE && set[i].style != NONE)
error->all(FLERR, "Cannot couple non-pressure-controlled dimensions");
if (set[i].style == PRESSURE)
j = i;
}
}
if (j == -1)
error->all(FLERR, "Must specify deformation style for at least one coupled dimension");
// Copy or compare data for each coupled dimension
for (int i = 0; i < 3; i++) {
if (coupled_indices[i]) {
// Copy coupling information if dimension style is undefined
if (set[i].style == NONE) {
set[i].style = PRESSURE;
set[i].pgain = set[j].pgain;
if (set[j].pvar_flag) {
set[i].pstr = set[j].pstr;
set[i].pvar_flag = 1;
} else {
set[i].ptarget = set[j].ptarget;
}
} else {
// Check for incompatibilities in style
if (set[j].style != set[i].style && set[i].style != NONE)
error->all(FLERR, "Cannot couple dimensions with different control options");
if (set[j].style != PRESSURE) continue;
// If pressure controlled, check for incompatibilities in parameters
if (set[i].pgain != set[j].pgain || set[i].pvar_flag != set[j].pvar_flag ||
set[i].ptarget != set[j].ptarget)
error->all(FLERR, "Coupled dimensions must have identical gain parameters");
if (set[j].pvar_flag)
if (strcmp(set[i].pstr, set[j].pstr) != 0)
error->all(FLERR, "Coupled dimensions must have the same target pressure");
}
}
}
}
// setup dimflags used by other classes to check for volume-change conflicts
for (int i = 0; i < 6; i++)
if (set[i].style == NONE) dimflag[i] = 0;
else dimflag[i] = 1;
if (set[6].style != NONE) {
dimflag[0] = 1;
dimflag[1] = 1;
dimflag[2] = 1;
}
if (dimflag[0]) box_change |= BOX_CHANGE_X;
if (dimflag[1]) box_change |= BOX_CHANGE_Y;
if (dimflag[2]) box_change |= BOX_CHANGE_Z;
if (dimflag[3]) box_change |= BOX_CHANGE_YZ;
if (dimflag[4]) box_change |= BOX_CHANGE_XZ;
if (dimflag[5]) box_change |= BOX_CHANGE_XY;
// no tensile deformation on shrink-wrapped dims
// b/c shrink wrap will change box-length
for (int i = 0; i < 3; i++)
if ((set[i].style || set[6].style) && (domain->boundary[i][0] >= 2 || domain->boundary[i][1] >= 2))
error->all(FLERR,"Cannot use fix deform on a shrink-wrapped boundary");
// no tilt deformation on shrink-wrapped 2nd dim
// b/c shrink wrap will change tilt factor in domain::reset_box()
if (set[3].style && (domain->boundary[2][0] >= 2 || domain->boundary[2][1] >= 2))
error->all(FLERR,"Cannot use fix deform tilt on a shrink-wrapped 2nd dim");
if (set[4].style && (domain->boundary[2][0] >= 2 || domain->boundary[2][1] >= 2))
error->all(FLERR,"Cannot use fix deform tilt on a shrink-wrapped 2nd dim");
if (set[5].style && (domain->boundary[1][0] >= 2 || domain->boundary[1][1] >= 2))
error->all(FLERR,"Cannot use fix deform tilt on a shrink-wrapped 2nd dim");
// apply scaling to FINAL,DELTA,VEL,WIGGLE since they have dist/vel units
int flag = 0;
for (int i = 0; i < 6; i++)
if (set[i].style == FINAL || set[i].style == DELTA ||
set[i].style == VEL || set[i].style == WIGGLE) flag = 1;
double xscale,yscale,zscale;
if (flag && scaleflag) {
xscale = domain->lattice->xlattice;
yscale = domain->lattice->ylattice;
zscale = domain->lattice->zlattice;
}
else xscale = yscale = zscale = 1.0;
// for 3,4,5: scaling is in 1st dimension, e.g. x for xz
double map[6];
map[0] = xscale; map[1] = yscale; map[2] = zscale;
map[3] = yscale; map[4] = xscale; map[5] = xscale;
for (int i = 0; i < 3; i++) {
if (set[i].style == FINAL) {
set[i].flo *= map[i];
set[i].fhi *= map[i];
} else if (set[i].style == DELTA) {
set[i].dlo *= map[i];
set[i].dhi *= map[i];
} else if (set[i].style == VEL) {
set[i].vel *= map[i];
} else if (set[i].style == WIGGLE) {
set[i].amplitude *= map[i];
}
}
for (int i = 3; i < 6; i++) {
if (set[i].style == FINAL) set[i].ftilt *= map[i];
else if (set[i].style == DELTA) set[i].dtilt *= map[i];
else if (set[i].style == VEL) set[i].vel *= map[i];
else if (set[i].style == WIGGLE) set[i].amplitude *= map[i];
}
// for VOLUME, setup links to other dims
// fixed, dynamic1, dynamic2
volume_flag = 0;
for (int i = 0; i < 3; i++) {
if (set[i].style != VOLUME) continue;
volume_flag = 1;
int other1 = (i+1) % 3;
int other2 = (i+2) % 3;
// Cannot use VOLUME option without at least one deformed dimension
if (set[other1].style == NONE || set[other1].style == VOLUME)
if (set[other2].style == NONE || set[other2].style == VOLUME)
error->all(FLERR,"Fix deform volume setting is invalid");
if (set[other1].style == NONE) {
set[i].substyle = ONE_FROM_ONE;
set[i].fixed = other1;
set[i].dynamic1 = other2;
} else if (set[other2].style == NONE) {
set[i].substyle = ONE_FROM_ONE;
set[i].fixed = other2;
set[i].dynamic1 = other1;
} else if (set[other1].style == VOLUME) {
set[i].substyle = TWO_FROM_ONE;
set[i].fixed = other1;
set[i].dynamic1 = other2;
} else if (set[other2].style == VOLUME) {
set[i].substyle = TWO_FROM_ONE;
set[i].fixed = other2;
set[i].dynamic1 = other1;
} else {
set[i].substyle = ONE_FROM_TWO;
set[i].dynamic1 = other1;
set[i].dynamic2 = other2;
}
if (vol_balance_flag && set[i].substyle != TWO_FROM_ONE)
error->all(FLERR, "Two dimensions must maintain constant volume to use the vol/balance/p option");
}
// set strain_flag
strain_flag = 0;
for (int i = 0; i < 6; i++)
if (set[i].style != NONE && set[i].style != VOLUME &&
set[i].style != PRESSURE && set[i].style != PMEAN)
strain_flag = 1;
// set varflag
varflag = 0;
for (int i = 0; i < 7; i++) {
if (set[i].style == VARIABLE) varflag = 1;
if (set[i].pvar_flag) varflag = 1;
}
// set pressure_flag
pressure_flag = 0;
for (int i = 0; i < 7; i++) {
if (set[i].style == PRESSURE || set[i].style == PMEAN) pressure_flag = 1;
if (set[i].coupled_flag) pressure_flag = 1;
}
if (vol_balance_flag) pressure_flag = 1;
// check conflict between constant volume/pressure
if (volume_flag)
for (int i = 0; i < 6; i++)
if (set[i].style == PMEAN)
error->all(FLERR, "Cannot use fix deform to assign constant volume and pressure");
// check conflicts between x,y,z styles and iso
if (set[6].style)
for (int i = 0; i < 3; i++) {
if (set[i].style == FINAL || set[i].style == DELTA || set[i].style == SCALE || set[i].style == PMEAN || set[i].style == VARIABLE)
error->all(FLERR, "Cannot use fix deform iso parameter with x, y, or z styles other than vel, erate, trate, pressure, and wiggle");
}
// check pressure used for max rate and normalize error flag
if (!pressure_flag && max_h_rate != 0)
error->all(FLERR, "Can only assign a maximum strain rate using pressure-controlled dimensions");
if (!pressure_flag && normalize_pressure_flag)
error->all(FLERR, "Can only normalize error using pressure-controlled dimensions");
// set initial values at time fix deform is issued
for (int i = 0; i < 3; i++) {
set[i].lo_initial = domain->boxlo[i];
set[i].hi_initial = domain->boxhi[i];
set[i].vol_initial = domain->xprd * domain->yprd * domain->zprd;
}
set[3].tilt_initial = domain->yz;
set[4].tilt_initial = domain->xz;
set[5].tilt_initial = domain->xy;
set[6].vol_initial = domain->xprd * domain->yprd * domain->zprd;
// reneighboring only forced if flips can occur due to shape changes
if (flipflag && (set[3].style || set[4].style || set[5].style))
force_reneighbor = 1;
next_reneighbor = -1;
flip = 0;
if (force_reneighbor) irregular = new Irregular(lmp);
else irregular = nullptr;
// Create pressure compute, if needed
pflag = 0;
tflag = 0;
if (pressure_flag) {
// create a new compute temp style
// id = fix-ID + temp
// compute group = all since pressure is always global (group all)
// and thus its KE/temperature contribution should use group all
id_temp = utils::strdup(std::string(id) + "_temp");
modify->add_compute(fmt::format("{} all temp",id_temp));
tflag = 1;
// create a new compute pressure style
// id = fix-ID + press, compute group = all
// pass id_temp as 4th arg to pressure constructor
id_press = utils::strdup(std::string(id) + "_press");
modify->add_compute(fmt::format("{} all pressure {}",id_press, id_temp));
pflag = 1;
}
// initialize all rates to 0.0 in constructor instead of init so values persist
// across run statements and ghosts have correct velocities until the destructor
h_rate = domain->h_rate;
h_ratelo = domain->h_ratelo;
for (int i = 0; i < 3; i++)
h_rate[i] = h_ratelo[i] = 0.0;
for (int i = 3; i < 6; i++)
h_rate[i] = 0.0;
}
/* ---------------------------------------------------------------------- */
FixDeform::~FixDeform()
{
if (set) {
for (int i = 0; i < 7; i++) {
delete[] set[i].hstr;
delete[] set[i].hratestr;
delete[] set[i].pstr;
}
}
delete[] set;
delete irregular;
// reset domain's h_rate = 0.0, since this fix may have made it non-zero
double *h_rate = domain->h_rate;
double *h_ratelo = domain->h_ratelo;
h_rate[0] = h_rate[1] = h_rate[2] =
h_rate[3] = h_rate[4] = h_rate[5] = 0.0;
h_ratelo[0] = h_ratelo[1] = h_ratelo[2] = 0.0;
// delete temperature and pressure if fix created them
if (tflag) modify->delete_compute(id_temp);
if (pflag) modify->delete_compute(id_press);
delete [] id_temp;
delete [] id_press;
}
/* ---------------------------------------------------------------------- */
int FixDeform::setmask()
{
int mask = 0;
if (force_reneighbor) mask |= PRE_EXCHANGE;
mask |= END_OF_STEP;
return mask;
}
/* ---------------------------------------------------------------------- */
void FixDeform::init()
{
// error if more than one fix deform
// domain, fix nvt/sllod, compute temp/deform only work on single h_rate
if (modify->get_fix_by_style("deform").size() > 1)
error->all(FLERR,"More than one fix deform");
// Kspace setting
if (force->kspace) kspace_flag = 1;
else kspace_flag = 0;
// elapsed time for entire simulation, including multiple runs if defined
double delt = (update->endstep - update->beginstep) * update->dt;
// check variables for VARIABLE style
for (int i = 0; i < 7; i++) {
if (set[i].style != VARIABLE) continue;
set[i].hvar = input->variable->find(set[i].hstr);
if (set[i].hvar < 0)
error->all(FLERR,"Variable name {} for fix deform does not exist", set[i].hstr);
if (!input->variable->equalstyle(set[i].hvar))
error->all(FLERR,"Variable {} for fix deform is invalid style", set[i].hstr);
set[i].hratevar = input->variable->find(set[i].hratestr);
if (set[i].hratevar < 0)
error->all(FLERR,"Variable name {} for fix deform does not exist", set[i].hratestr);
if (!input->variable->equalstyle(set[i].hratevar))
error->all(FLERR,"Variable {} for fix deform is invalid style", set[i].hratestr);
}
// check optional variables for PRESSURE or PMEAN style
for (int i = 0; i < 7; i++) {
if (!set[i].pvar_flag) continue;
set[i].pvar = input->variable->find(set[i].pstr);
if (set[i].pvar < 0)
error->all(FLERR,"Variable name {} for fix deform does not exist", set[i].pstr);
if (!input->variable->equalstyle(set[i].pvar))
error->all(FLERR,"Variable {} for fix deform is invalid style", set[i].pstr);
}
// set start/stop values for box size and shape
// if single run, start is current values
// if multiple runs enabled via run start/stop settings,
// start is value when fix deform was issued
// if VARIABLE or NONE, no need to set
for (int i = 0; i < 3; i++) {
if (update->firststep == update->beginstep) {
set[i].lo_start = domain->boxlo[i];
set[i].hi_start = domain->boxhi[i];
set[i].vol_start = domain->xprd * domain->yprd * domain->zprd;
} else {
set[i].lo_start = set[i].lo_initial;
set[i].hi_start = set[i].hi_initial;
set[i].vol_start = set[i].vol_initial;
}
if (set[i].style == FINAL) {
set[i].lo_stop = set[i].flo;
set[i].hi_stop = set[i].fhi;
} else if (set[i].style == DELTA) {
set[i].lo_stop = set[i].lo_start + set[i].dlo;
set[i].hi_stop = set[i].hi_start + set[i].dhi;
} else if (set[i].style == SCALE) {
double shift = 0.5 * set[i].scale * (set[i].hi_start - set[i].lo_start);
set[i].lo_stop = 0.5 * (set[i].lo_start + set[i].hi_start) - shift;
set[i].hi_stop = 0.5 * (set[i].lo_start + set[i].hi_start) + shift;
} else if (set[i].style == VEL) {
set[i].lo_stop = set[i].lo_start - 0.5 * delt * set[i].vel;
set[i].hi_stop = set[i].hi_start + 0.5 * delt * set[i].vel;
} else if (set[i].style == ERATE) {
double shift = 0.5 * delt * set[i].rate * (set[i].hi_start - set[i].lo_start);
set[i].lo_stop = set[i].lo_start - shift;
set[i].hi_stop = set[i].hi_start + shift;
if (set[i].hi_stop <= set[i].lo_stop)
error->all(FLERR,"Final box dimension due to fix deform is < 0.0");
} else if (set[i].style == TRATE) {
double shift = 0.5 * ((set[i].hi_start - set[i].lo_start) * exp(set[i].rate * delt));
set[i].lo_stop = 0.5 * (set[i].lo_start + set[i].hi_start) - shift;
set[i].hi_stop = 0.5 * (set[i].lo_start + set[i].hi_start) + shift;
} else if (set[i].style == WIGGLE) {
double shift = 0.5 * set[i].amplitude * sin(MY_2PI * delt / set[i].tperiod);
set[i].lo_stop = set[i].lo_start - shift;
set[i].hi_stop = set[i].hi_start + shift;
}
}
for (int i = 3; i < 6; i++) {
if (update->firststep == update->beginstep) {
if (i == 5) set[i].tilt_start = domain->xy;
else if (i == 4) set[i].tilt_start = domain->xz;
else if (i == 3) set[i].tilt_start = domain->yz;
} else set[i].tilt_start = set[i].tilt_initial;
if (set[i].style == FINAL) {
set[i].tilt_stop = set[i].ftilt;
} else if (set[i].style == DELTA) {
set[i].tilt_stop = set[i].tilt_start + set[i].dtilt;
} else if (set[i].style == VEL) {
set[i].tilt_stop = set[i].tilt_start + delt * set[i].vel;
} else if (set[i].style == ERATE) {
if (i == 3) set[i].tilt_stop = set[i].tilt_start +
delt * set[i].rate * (set[2].hi_start - set[2].lo_start);
if (i == 4) set[i].tilt_stop = set[i].tilt_start +
delt * set[i].rate * (set[2].hi_start - set[2].lo_start);
if (i == 5) set[i].tilt_stop = set[i].tilt_start +
delt * set[i].rate * (set[1].hi_start - set[1].lo_start);
} else if (set[i].style == TRATE) {
set[i].tilt_stop = set[i].tilt_start * exp(set[i].rate * delt);
} else if (set[i].style == WIGGLE) {
double shift = set[i].amplitude * sin(MY_2PI * delt / set[i].tperiod);
set[i].tilt_stop = set[i].tilt_start + shift;
// compute min/max for WIGGLE = extrema tilt factor will ever reach
if (set[i].amplitude >= 0.0) {
if (delt < 0.25 * set[i].tperiod) {
set[i].tilt_min = set[i].tilt_start;
set[i].tilt_max = set[i].tilt_start + shift;
} else if (delt < 0.5 * set[i].tperiod) {
set[i].tilt_min = set[i].tilt_start;
set[i].tilt_max = set[i].tilt_start + set[i].amplitude;
} else if (delt < 0.75 * set[i].tperiod) {
set[i].tilt_min = set[i].tilt_start - shift;
set[i].tilt_max = set[i].tilt_start + set[i].amplitude;
} else {
set[i].tilt_min = set[i].tilt_start - set[i].amplitude;
set[i].tilt_max = set[i].tilt_start + set[i].amplitude;
}
} else {
if (delt < 0.25 * set[i].tperiod) {
set[i].tilt_min = set[i].tilt_start - shift;
set[i].tilt_max = set[i].tilt_start;
} else if (delt < 0.5 * set[i].tperiod) {
set[i].tilt_min = set[i].tilt_start - set[i].amplitude;
set[i].tilt_max = set[i].tilt_start;
} else if (delt < 0.75 * set[i].tperiod) {
set[i].tilt_min = set[i].tilt_start - set[i].amplitude;
set[i].tilt_max = set[i].tilt_start + shift;
} else {
set[i].tilt_min = set[i].tilt_start - set[i].amplitude;
set[i].tilt_max = set[i].tilt_start + set[i].amplitude;
}
}
}
}
set[6].vol_start = domain->xprd * domain->yprd * domain->zprd;
// if using tilt TRATE, then initial tilt must be non-zero
for (int i = 3; i < 6; i++)
if (set[i].style == TRATE && set[i].tilt_start == 0.0)
error->all(FLERR,"Cannot use fix deform trate on a box with zero tilt");
// if yz changes and will cause box flip, then xy cannot be changing
// yz = [3], xy = [5]
// this is b/c the flips would induce continuous changes in xz
// in order to keep the edge vectors of the flipped shape matrix
// an integer combination of the edge vectors of the unflipped shape matrix
// VARIABLE or PRESSURE for yz is error, since no way to calculate if box flip occurs
// WIGGLE lo/hi flip test is on min/max oscillation limit, not tilt_stop
// only trigger actual errors if flipflag is set
if (set[3].style && set[5].style) {
int flag = 0;
double lo,hi;
if (flipflag && (set[3].style == VARIABLE || set[3].style == PRESSURE))
error->all(FLERR,"Fix deform cannot use yz variable or pressure with xy");
if (set[3].style == WIGGLE) {
lo = set[3].tilt_min;
hi = set[3].tilt_max;
} else lo = hi = set[3].tilt_stop;
if (flipflag) {
if (lo / (set[1].hi_start - set[1].lo_start) < -0.5 ||
hi / (set[1].hi_start - set[1].lo_start) > 0.5) flag = 1;
if (set[1].style) {
if (lo / (set[1].hi_stop - set[1].lo_stop) < -0.5 ||
hi / (set[1].hi_stop - set[1].lo_stop) > 0.5) flag = 1;
}
if (flag)
error->all(FLERR,"Fix deform is changing yz too much with xy");
}
}
// set domain->h_rate values for use by domain and other fixes/computes
// cannot set here for TRATE,VOLUME,WIGGLE,VARIABLE,PRESSURE since not constant
// if iso style is used, these will also not be constant
for (int i = 0; i < 3; i++) {
if (set[i].style == FINAL || set[i].style == DELTA ||
set[i].style == SCALE || set[i].style == VEL ||
set[i].style == ERATE) {
double dlo_dt,dhi_dt;
if (delt != 0.0) {
dlo_dt = (set[i].lo_stop - set[i].lo_start) / delt;
dhi_dt = (set[i].hi_stop - set[i].hi_start) / delt;
} else dlo_dt = dhi_dt = 0.0;
h_rate[i] = dhi_dt - dlo_dt;
h_ratelo[i] = dlo_dt;
}
}
for (int i = 3; i < 6; i++) {
if (set[i].style == FINAL || set[i].style == DELTA ||
set[i].style == VEL || set[i].style == ERATE) {
if (delt != 0.0)
h_rate[i] = (set[i].tilt_stop - set[i].tilt_start) / delt;
else h_rate[i] = 0.0;
}
}
// detect if any rigid fixes exist so rigid bodies can be rescaled
// rfix[] = vector with pointers to each fix rigid
rfix.clear();
for (auto ifix : modify->get_fix_list())
if (ifix->rigid_flag) rfix.push_back(ifix);
// Find pressure/temp computes if needed
if (pressure_flag) {
int icompute = modify->find_compute(id_temp);
if (icompute < 0) error->all(FLERR,"Temperature ID for fix deform does not exist");
temperature = modify->compute[icompute];
icompute = modify->find_compute(id_press);
if (icompute < 0) error->all(FLERR,"Pressure ID for fix deform does not exist");
pressure = modify->compute[icompute];
}
}
/* ----------------------------------------------------------------------
compute T,P if needed before integrator starts
------------------------------------------------------------------------- */
void FixDeform::setup(int /*vflag*/)
{
// trigger virial computation on next timestep
if (pressure_flag) pressure->addstep(update->ntimestep+1);
}
/* ----------------------------------------------------------------------
box flipped on previous step
reset box tilts for flipped config and create new box in domain
image_flip() adjusts image flags due to box shape change induced by flip
remap() puts atoms outside the new box back into the new box
perform irregular on atoms in lamda coords to migrate atoms to new procs
important that image_flip comes before remap, since remap may change
image flags to new values, making eqs in doc of Domain:image_flip incorrect
------------------------------------------------------------------------- */
void FixDeform::pre_exchange()
{
if (flip == 0) return;
domain->yz = set[3].tilt_target = set[3].tilt_flip;
domain->xz = set[4].tilt_target = set[4].tilt_flip;
domain->xy = set[5].tilt_target = set[5].tilt_flip;
domain->set_global_box();
domain->set_local_box();
domain->image_flip(flipxy,flipxz,flipyz);
double **x = atom->x;
imageint *image = atom->image;
int nlocal = atom->nlocal;
for (int i = 0; i < nlocal; i++) domain->remap(x[i],image[i]);
domain->x2lamda(atom->nlocal);
irregular->migrate_atoms();
domain->lamda2x(atom->nlocal);
flip = 0;
}
/* ---------------------------------------------------------------------- */
void FixDeform::end_of_step()
{
// wrap variable evaluations with clear/add
if (varflag) modify->clearstep_compute();
// set new box size for strain-based dims
set_strain();
// set new box size for pressure-based dims
if (pressure_flag) {
temperature->compute_vector();
pressure->compute_vector();
pressure->compute_scalar();
for (int i = 0; i < 3; i++) {
if (!set[i].saved) {
set[i].saved = 1;
set[i].prior_rate = 0.0;
set[i].prior_pressure = pressure->vector[i];
}
}
set_pressure();
}
// set new box size for VOLUME dims that are linked to other dims
// NOTE: still need to set h_rate for these dims
if (volume_flag) set_volume();
// apply any final isotropic scalings
if (set[6].style) set_iso();
// Save pressure/strain rate if required
if (pressure_flag) {
for (int i = 0; i < 3; i++) {
set[i].prior_pressure = pressure->vector[i];
set[i].prior_rate = ((set[i].hi_target - set[i].lo_target) /
(domain->boxhi[i] - domain->boxlo[i]) - 1.0) / update->dt;
}
}
if (varflag) modify->addstep_compute(update->ntimestep + nevery);
// tilt_target can be large positive or large negative value
// add/subtract box lengths until tilt_target is closest to current value
if (triclinic) {
double *h = domain->h;
for (int i = 3; i < 6; i++) {
int idenom = 0;
if (i == 5) idenom = 0;
else if (i == 4) idenom = 0;
else if (i == 3) idenom = 1;
double denom = set[idenom].hi_target - set[idenom].lo_target;
double current = h[i] / h[idenom];
while (set[i].tilt_target / denom - current > 0.0)
set[i].tilt_target -= denom;
while (set[i].tilt_target / denom - current < 0.0)
set[i].tilt_target += denom;
if (fabs(set[i].tilt_target / denom - 1.0 - current) <
fabs(set[i].tilt_target / denom - current))
set[i].tilt_target -= denom;
}
}
// if any tilt ratios exceed 0.5, set flip = 1 and compute new tilt values
// do not flip in x or y if non-periodic (can tilt but not flip)
// this is b/c the box length would be changed (dramatically) by flip
// if yz tilt exceeded, adjust C vector by one B vector
// if xz tilt exceeded, adjust C vector by one A vector
// if xy tilt exceeded, adjust B vector by one A vector
// check yz first since it may change xz, then xz check comes after
// flip is performed on next timestep, before reneighboring in pre-exchange()
if (triclinic && flipflag) {
double xprd = set[0].hi_target - set[0].lo_target;
double yprd = set[1].hi_target - set[1].lo_target;
double xprdinv = 1.0 / xprd;
double yprdinv = 1.0 / yprd;
if (set[3].tilt_target * yprdinv < -0.5 ||
set[3].tilt_target * yprdinv > 0.5 ||
set[4].tilt_target * xprdinv < -0.5 ||
set[4].tilt_target * xprdinv > 0.5 ||
set[5].tilt_target * xprdinv < -0.5 ||
set[5].tilt_target * xprdinv > 0.5) {
set[3].tilt_flip = set[3].tilt_target;
set[4].tilt_flip = set[4].tilt_target;
set[5].tilt_flip = set[5].tilt_target;
flipxy = flipxz = flipyz = 0;
if (domain->yperiodic) {
if (set[3].tilt_flip * yprdinv < -0.5) {
set[3].tilt_flip += yprd;
set[4].tilt_flip += set[5].tilt_flip;
flipyz = 1;
} else if (set[3].tilt_flip * yprdinv > 0.5) {
set[3].tilt_flip -= yprd;
set[4].tilt_flip -= set[5].tilt_flip;
flipyz = -1;
}
}
if (domain->xperiodic) {
if (set[4].tilt_flip * xprdinv < -0.5) {
set[4].tilt_flip += xprd;
flipxz = 1;
}
if (set[4].tilt_flip * xprdinv > 0.5) {
set[4].tilt_flip -= xprd;
flipxz = -1;
}
if (set[5].tilt_flip * xprdinv < -0.5) {
set[5].tilt_flip += xprd;
flipxy = 1;
}
if (set[5].tilt_flip * xprdinv > 0.5) {
set[5].tilt_flip -= xprd;
flipxy = -1;
}
}
flip = 0;
if (flipxy || flipxz || flipyz) flip = 1;
if (flip) next_reneighbor = update->ntimestep + 1;
}
}
// convert atoms and rigid bodies to lamda coords
if (remapflag == Domain::X_REMAP) {
double **x = atom->x;
int *mask = atom->mask;
int nlocal = atom->nlocal;
for (int i = 0; i < nlocal; i++)
if (mask[i] & groupbit)
domain->x2lamda(x[i],x[i]);
for (auto ifix : rfix)
ifix->deform(0);
}
// reset global and local box to new size/shape
// only if deform fix is controlling the dimension
if (set[0].style || set[6].style) {
domain->boxlo[0] = set[0].lo_target;
domain->boxhi[0] = set[0].hi_target;
}
if (set[1].style || set[6].style) {
domain->boxlo[1] = set[1].lo_target;
domain->boxhi[1] = set[1].hi_target;
}
if (set[2].style || set[6].style) {
domain->boxlo[2] = set[2].lo_target;
domain->boxhi[2] = set[2].hi_target;
}
if (triclinic) {
if (set[3].style) domain->yz = set[3].tilt_target;
if (set[4].style) domain->xz = set[4].tilt_target;
if (set[5].style) domain->xy = set[5].tilt_target;
}
domain->set_global_box();
domain->set_local_box();
// convert atoms and rigid bodies back to box coords
if (remapflag == Domain::X_REMAP) {
double **x = atom->x;
int *mask = atom->mask;
int nlocal = atom->nlocal;
for (int i = 0; i < nlocal; i++)
if (mask[i] & groupbit)
domain->lamda2x(x[i],x[i]);
for (auto ifix : rfix)
ifix->deform(1);
}
// redo KSpace coeffs since box has changed
if (kspace_flag) force->kspace->setup();
// trigger virial computation, if needed, on next timestep
if (pressure_flag)
pressure->addstep(update->ntimestep+1);
}
/* ----------------------------------------------------------------------
set box size for strain-based dimensions
------------------------------------------------------------------------- */
void FixDeform::set_strain()
{
// for NONE, target is current box size
// for TRATE, set target directly based on current time, also set h_rate
// for WIGGLE, set target directly based on current time, also set h_rate
// for VARIABLE, set target directly via variable eval, also set h_rate
// for others except VOLUME, target is linear value between start and stop
double delta = update->ntimestep - update->beginstep;
if (delta != 0.0) delta /= update->endstep - update->beginstep;
for (int i = 0; i < 3; i++) {
if (set[i].style == NONE) {
set[i].lo_target = domain->boxlo[i];
set[i].hi_target = domain->boxhi[i];
} else if (set[i].style == TRATE) {
double delt = (update->ntimestep - update->beginstep) * update->dt;
double shift = 0.5 * ((set[i].hi_start - set[i].lo_start) * exp(set[i].rate * delt));
set[i].lo_target = 0.5 * (set[i].lo_start + set[i].hi_start) - shift;
set[i].hi_target = 0.5 * (set[i].lo_start + set[i].hi_start) + shift;
h_rate[i] = set[i].rate * domain->h[i];
h_ratelo[i] = -0.5 * h_rate[i];
} else if (set[i].style == WIGGLE) {
double delt = (update->ntimestep - update->beginstep) * update->dt;
double shift = 0.5 * set[i].amplitude * sin(MY_2PI * delt / set[i].tperiod);
set[i].lo_target = set[i].lo_start - shift;
set[i].hi_target = set[i].hi_start + shift;
h_rate[i] = MY_2PI / set[i].tperiod * set[i].amplitude *
cos(MY_2PI * delt / set[i].tperiod);
h_ratelo[i] = -0.5 * h_rate[i];
} else if (set[i].style == VARIABLE) {
double del = input->variable->compute_equal(set[i].hvar);
set[i].lo_target = set[i].lo_start - 0.5 * del;
set[i].hi_target = set[i].hi_start + 0.5 * del;
h_rate[i] = input->variable->compute_equal(set[i].hratevar);
h_ratelo[i] = -0.5 * h_rate[i];
} else if (set[i].style == FINAL || set[i].style == DELTA || set[i].style == SCALE ||
set[i].style == VEL || set[i].style == ERATE) {
set[i].lo_target = set[i].lo_start + delta * (set[i].lo_stop - set[i].lo_start);
set[i].hi_target = set[i].hi_start + delta * (set[i].hi_stop - set[i].hi_start);
}
}
// for triclinic, set new box shape
// for NONE, target is current tilt
// for TRATE, set target directly based on current time. also set h_rate
// for WIGGLE, set target directly based on current time. also set h_rate
// for VARIABLE, set target directly via variable eval. also set h_rate
// for other styles, target is linear value between start and stop values
if (triclinic) {
double *h = domain->h;
for (int i = 3; i < 6; i++) {
if (set[i].style == NONE) {
if (i == 5) set[i].tilt_target = domain->xy;
else if (i == 4) set[i].tilt_target = domain->xz;
else if (i == 3) set[i].tilt_target = domain->yz;
} else if (set[i].style == TRATE) {
double delt = (update->ntimestep - update->beginstep) * update->dt;
set[i].tilt_target = set[i].tilt_start * exp(set[i].rate * delt);
h_rate[i] = set[i].rate * domain->h[i];
} else if (set[i].style == WIGGLE) {
double delt = (update->ntimestep - update->beginstep) * update->dt;
set[i].tilt_target = set[i].tilt_start +
set[i].amplitude * sin(MY_2PI * delt / set[i].tperiod);
h_rate[i] = MY_2PI / set[i].tperiod * set[i].amplitude *
cos(MY_2PI * delt / set[i].tperiod);
} else if (set[i].style == VARIABLE) {
double delta_tilt = input->variable->compute_equal(set[i].hvar);
set[i].tilt_target = set[i].tilt_start + delta_tilt;
h_rate[i] = input->variable->compute_equal(set[i].hratevar);
} else {
set[i].tilt_target = set[i].tilt_start + delta * (set[i].tilt_stop - set[i].tilt_start);
}
}
}
}
/* ----------------------------------------------------------------------
set box size for pressure-based dimensions
------------------------------------------------------------------------- */
void FixDeform::set_pressure()
{
// If variable pressure, calculate current target
for (int i = 0; i < 6; i++)
if (set[i].style == PRESSURE)
if (set[i].pvar_flag)
set[i].ptarget = input->variable->compute_equal(set[i].pvar);
// Find current (possibly coupled/hydrostatic) pressure for X, Y, Z
double *tensor = pressure->vector;
double scalar = pressure->scalar;
double p_current[3];
if (pcouple == XYZ) {
double ave = THIRD * (tensor[0] + tensor[1] + tensor[2]);
p_current[0] = p_current[1] = p_current[2] = ave;
} else if (pcouple == XY) {
double ave = 0.5 * (tensor[0] + tensor[1]);
p_current[0] = p_current[1] = ave;
p_current[2] = tensor[2];
} else if (pcouple == YZ) {
double ave = 0.5 * (tensor[1] + tensor[2]);
p_current[1] = p_current[2] = ave;
p_current[0] = tensor[0];
} else if (pcouple == XZ) {
double ave = 0.5 * (tensor[0] + tensor[2]);
p_current[0] = p_current[2] = ave;
p_current[1] = tensor[1];
} else {
if (set[0].style == PRESSURE) p_current[0] = tensor[0];
else if (set[0].style == PMEAN) p_current[0] = scalar;
if (set[1].style == PRESSURE) p_current[1] = tensor[1];
else if (set[1].style == PMEAN) p_current[1] = scalar;
if (set[2].style == PRESSURE) p_current[2] = tensor[2];
else if (set[2].style == PMEAN) p_current[2] = scalar;
}
for (int i = 0; i < 3; i++) {
if (set[i].style != PRESSURE && set[i].style != PMEAN) continue;
h_rate[i] = set[i].pgain * (p_current[i] - set[i].ptarget);
if (normalize_pressure_flag) {
if (set[i].ptarget == 0) {
if (max_h_rate == 0) {
error->all(FLERR, "Cannot normalize error for zero pressure without defining a max rate");
} else h_rate[i] = max_h_rate * h_rate[i] / fabs(h_rate[i]);
} else h_rate[i] /= fabs(set[i].ptarget);
}
if (max_h_rate != 0)
if (fabs(h_rate[i]) > max_h_rate)
h_rate[i] = max_h_rate * h_rate[i] / fabs(h_rate[i]);
h_ratelo[i] = -0.5 * h_rate[i];
double offset = 0.5 * (domain->boxhi[i] - domain->boxlo[i]) * (1.0 + update->dt * h_rate[i]);
set[i].lo_target = 0.5 * (set[i].lo_start + set[i].hi_start) - offset;
set[i].hi_target = 0.5 * (set[i].lo_start + set[i].hi_start) + offset;
}
for (int i = 3; i < 6; i++) {
if (set[i].style != PRESSURE) continue;
double L, tilt, pcurrent;
if (i == 3) {
L = domain->zprd;
tilt = domain->yz;
pcurrent = tensor[5];
} else if (i == 4) {
L = domain->zprd;
tilt = domain->xz + update->dt;
pcurrent = tensor[4];
} else {
L = domain->yprd;
tilt = domain->xy;
pcurrent = tensor[3];
}
h_rate[i] = L * set[i].pgain * (pcurrent - set[i].ptarget);
if (normalize_pressure_flag) {
if (set[i].ptarget == 0) {
if (max_h_rate == 0) {
error->all(FLERR, "Cannot normalize error for zero pressure without defining a max rate");
} else h_rate[i] = max_h_rate * h_rate[i] / fabs(h_rate[i]);
} else h_rate[i] /= fabs(set[i].ptarget);
}
if (max_h_rate != 0)
if (fabs(h_rate[i]) > max_h_rate)
h_rate[i] = max_h_rate * h_rate[i] / fabs(h_rate[i]);
set[i].tilt_target = tilt + update->dt * h_rate[i];
}
}
/* ----------------------------------------------------------------------
set box size for VOLUME dimensions
------------------------------------------------------------------------- */
void FixDeform::set_volume()
{
double e1, e2;
int linked_pressure = 0;
for (int i = 0; i < 3; i++) {
if (set[i].style != VOLUME) continue;
int dynamic1 = set[i].dynamic1;
int dynamic2 = set[i].dynamic2;
int fixed = set[i].fixed;
double v0 = set[i].vol_start;
double shift;
if (set[i].substyle == ONE_FROM_ONE) {
shift = 0.5 * (v0 / (set[dynamic1].hi_target - set[dynamic1].lo_target) /
(set[fixed].hi_start-set[fixed].lo_start));
} else if (set[i].substyle == ONE_FROM_TWO) {
shift = 0.5 * (v0 / (set[dynamic1].hi_target - set[dynamic1].lo_target) /
(set[dynamic2].hi_target - set[dynamic2].lo_target));
} else if (set[i].substyle == TWO_FROM_ONE) {
if (!vol_balance_flag) {
shift = 0.5 * sqrt(v0 * (set[i].hi_start - set[i].lo_start) /
(set[dynamic1].hi_target - set[dynamic1].lo_target) /
(set[fixed].hi_start - set[fixed].lo_start));
} else {
double dt = update->dt;
double e1i = set[i].prior_rate;
double e2i = set[fixed].prior_rate;
double L1i = domain->boxhi[i] - domain->boxlo[i];
double L2i = domain->boxhi[fixed] - domain->boxlo[fixed];
double L3i = domain->boxhi[dynamic1] - domain->boxlo[dynamic1];
double L3 = (set[dynamic1].hi_target - set[dynamic1].lo_target);
double e3 = (L3 / L3i - 1.0) / dt;
double p1 = pressure->vector[i];
double p2 = pressure->vector[fixed];
double p1i = set[i].prior_pressure;
double p2i = set[fixed].prior_pressure;
if (e3 == 0) {
e1 = 0.0;
e2 = 0.0;
shift = 0.5 * L1i;
} else if (e1i == 0 || e2i == 0 || (p2 == p2i && p1 == p1i)) {
// If no prior strain or no change in pressure (initial step) just scale shift by relative box lengths
shift = 0.5 * sqrt(v0 * L1i / L3 / L2i);
} else {
if (!linked_pressure) {
// Calculate first strain rate by expanding stress to linear order, p1(t+dt) = p2(t+dt)
// Calculate second strain rate to preserve volume
e1 = -e3 / (1 + e3 * dt) * (p2 - p2i) - e2i * (p1 - p2) / (p2 - p2i + (p1 - p1i) / e1i * e2i);
e2 = (1.0 - (1 + e3 * dt) * (1 + e1 * dt)) / ((1 + e3 * dt) * (1 + e1 * dt) * dt);
shift = 0.5 * L1i * (1.0 + e1 * dt);
linked_pressure = 1;
} else {
// Already calculated value of e2
shift = 0.5 * L1i * (1.0 + e2 * dt);
}
}
}
}
h_rate[i] = (2.0 * shift / (domain->boxhi[i] - domain->boxlo[i]) - 1.0) / update->dt;
h_ratelo[i] = -0.5 * h_rate[i];
set[i].lo_target = 0.5 * (set[i].lo_start + set[i].hi_start) - shift;
set[i].hi_target = 0.5 * (set[i].lo_start + set[i].hi_start) + shift;
}
}
/* ----------------------------------------------------------------------
apply isotropic controls
------------------------------------------------------------------------- */
void FixDeform::set_iso()
{
int i;
double scale, shift;
double v_rate;
if (set[6].style == VOLUME) {
double v0 = set[6].vol_start;
double v = 1.0;
for (i = 0; i < 3; i++)
v *= (set[i].hi_target - set[i].lo_target);
scale = std::pow(v0 / v, THIRD);
for (i = 0; i < 3; i++) {
shift = 0.5 * (set[i].hi_target - set[i].lo_target) * scale;
set[i].lo_target = 0.5 * (set[i].lo_start + set[i].hi_start) - shift;
set[i].hi_target = 0.5 * (set[i].lo_start + set[i].hi_start) + shift;
// Recalculate h_rate
h_rate[i] = (set[i].hi_target - set[i].lo_target) / (domain->boxhi[i] - domain->boxlo[i]) - 1.0;
h_rate[i] /= update->dt;
h_ratelo[i] = -0.5 * h_rate[i];
}
} else if (set[6].style == PRESSURE) {
// If variable pressure, calculate current target
if (set[6].pvar_flag)
set[6].ptarget = input->variable->compute_equal(set[6].pvar);
v_rate = set[6].pgain * (pressure->scalar- set[6].ptarget);
if (normalize_pressure_flag) {
if (set[6].ptarget == 0) {
if (max_h_rate == 0) {
error->all(FLERR, "Cannot normalize error for zero pressure without defining a max rate");
} else v_rate = max_h_rate * v_rate / fabs(v_rate);
} else v_rate /= fabs(set[6].ptarget);
}
if (max_h_rate != 0)
if (fabs(v_rate) > max_h_rate)
v_rate = max_h_rate * v_rate / fabs(v_rate);
set[6].cumulative_strain += update->dt * v_rate;
scale = (1.0 + set[6].cumulative_strain);
for (i = 0; i < 3; i++) {
shift = 0.5 * (set[i].hi_target - set[i].lo_target) * scale;
set[i].lo_target = 0.5 * (set[i].lo_start + set[i].hi_start) - shift;
set[i].hi_target = 0.5 * (set[i].lo_start + set[i].hi_start) + shift;
// Recalculate h_rate
h_rate[i] = (set[i].hi_target - set[i].lo_target) / (domain->boxhi[i] - domain->boxlo[i]) - 1.0;
h_rate[i] /= update->dt;
h_ratelo[i] = -0.5 * h_rate[i];
}
}
}
/* ----------------------------------------------------------------------
write Set data to restart file
------------------------------------------------------------------------- */
void FixDeform::write_restart(FILE *fp)
{
if (comm->me == 0) {
int size = 9 * sizeof(double) + 7 * sizeof(Set);
fwrite(&size,sizeof(int),1,fp);
fwrite(h_rate,sizeof(double),6,fp);
fwrite(h_ratelo,sizeof(double),3,fp);
fwrite(set,sizeof(Set),7,fp);
}
}
/* ----------------------------------------------------------------------
use selected state info from restart file to restart the Fix
------------------------------------------------------------------------- */
void FixDeform::restart(char *buf)
{
int n = 0;
auto list = (double *) buf;
for (int i = 0; i < 6; i++)
h_rate[i] = list[n++];
for (int i = 0; i < 3; i++)
h_ratelo[i] = list[n++];
int samestyle = 1;
Set *set_restart = (Set *) &buf[n * sizeof(double)];
for (int i = 0; i < 7; ++i) {
// restore data from initial state
set[i].lo_initial = set_restart[i].lo_initial;
set[i].hi_initial = set_restart[i].hi_initial;
set[i].vol_initial = set_restart[i].vol_initial;
set[i].tilt_initial = set_restart[i].tilt_initial;
set[i].saved = set_restart[i].saved;
set[i].prior_rate = set_restart[i].prior_rate;
set[i].prior_pressure = set_restart[i].prior_pressure;
set[i].cumulative_strain = set_restart[i].cumulative_strain;
// check if style settings are consistent (should do the whole set?)
if (set[i].style != set_restart[i].style)
samestyle = 0;
if (set[i].substyle != set_restart[i].substyle)
samestyle = 0;
}
if (!samestyle)
error->all(FLERR,"Fix deform settings not consistent with restart");
}
/* ---------------------------------------------------------------------- */
void FixDeform::options(int narg, char **arg)
{
if (narg < 0) error->all(FLERR,"Illegal fix deform command");
remapflag = Domain::X_REMAP;
scaleflag = 1;
flipflag = 1;
int iarg = 0;
while (iarg < narg) {
if (strcmp(arg[iarg],"remap") == 0) {
if (iarg+2 > narg) utils::missing_cmd_args(FLERR, "fix deform remap", error);
if (strcmp(arg[iarg+1],"x") == 0) remapflag = Domain::X_REMAP;
else if (strcmp(arg[iarg+1],"v") == 0) remapflag = Domain::V_REMAP;
else if (strcmp(arg[iarg+1],"none") == 0) remapflag = Domain::NO_REMAP;
else error->all(FLERR,"Illegal fix deform remap command: {}", arg[iarg+1]);
iarg += 2;
} else if (strcmp(arg[iarg],"units") == 0) {
if (iarg+2 > narg) utils::missing_cmd_args(FLERR, "fix deform units", error);
if (strcmp(arg[iarg+1],"box") == 0) scaleflag = 0;
else if (strcmp(arg[iarg+1],"lattice") == 0) scaleflag = 1;
else error->all(FLERR,"Illegal fix deform units command: {}", arg[iarg+1]);
iarg += 2;
} else if (strcmp(arg[iarg],"flip") == 0) {
if (iarg+2 > narg) utils::missing_cmd_args(FLERR, "fix deform flip", error);
flipflag = utils::logical(FLERR,arg[iarg+1],false,lmp);
iarg += 2;
} else if (strcmp(arg[iarg],"couple") == 0) {
if (iarg+2 > narg) utils::missing_cmd_args(FLERR, "fix deform couple", error);
if (strcmp(arg[iarg+1],"xyz") == 0) pcouple = XYZ;
else if (strcmp(arg[iarg+1],"xy") == 0) pcouple = XY;
else if (strcmp(arg[iarg+1],"yz") == 0) pcouple = YZ;
else if (strcmp(arg[iarg+1],"xz") == 0) pcouple = XZ;
else if (strcmp(arg[iarg+1],"none") == 0) pcouple = NOCOUPLE;
else error->all(FLERR,"Illegal fix fix deform couple command: {}", arg[iarg+1]);
iarg += 2;
} else if (strcmp(arg[iarg],"max/rate") == 0) {
if (iarg+2 > narg) utils::missing_cmd_args(FLERR, "fix deform max/rate", error);
max_h_rate = utils::numeric(FLERR,arg[iarg+1],false,lmp);
if (max_h_rate <= 0.0)
error->all(FLERR,"Maximum strain rate must be a positive, non-zero value");
iarg += 2;
} else if (strcmp(arg[iarg],"normalize/pressure") == 0) {
if (iarg+2 > narg) utils::missing_cmd_args(FLERR, "fix deform normalize/pressure", error);
normalize_pressure_flag = utils::logical(FLERR,arg[iarg+1],false,lmp);
iarg += 2;
} else if (strcmp(arg[iarg],"vol/balance/p") == 0) {
if (iarg+2 > narg) utils::missing_cmd_args(FLERR, "fix deform vol/balance/p", error);
vol_balance_flag = utils::logical(FLERR,arg[iarg+1],false,lmp);
iarg += 2;
} else error->all(FLERR,"Illegal fix deform command: {}", arg[iarg]);
}
if (dimension == 2)
if (pcouple == XYZ || pcouple == XZ || pcouple == YZ)
error->all(FLERR, "Cannot couple Z dimension in fix deform in 2D");
}
/* ----------------------------------------------------------------------
memory usage of Irregular
------------------------------------------------------------------------- */
double FixDeform::memory_usage()
{
double bytes = 0.0;
if (irregular) bytes += irregular->memory_usage();
return bytes;
}
/* ---------------------------------------------------------------------- */
int FixDeform::modify_param(int narg, char **arg)
{
if (strcmp(arg[0],"temp") == 0) {
if (narg < 2) error->all(FLERR,"Illegal fix_modify command");
if (tflag) {
modify->delete_compute(id_temp);
tflag = 0;
}
delete[] id_temp;
id_temp = utils::strdup(arg[1]);
temperature = modify->get_compute_by_id(arg[1]);
if (!temperature)
error->all(FLERR,"Could not find fix_modify temperature compute ID: ", arg[1]);
if (temperature->tempflag == 0)
error->all(FLERR,"Fix_modify temperature compute {} does not compute temperature", arg[1]);
if (temperature->igroup != 0 && comm->me == 0)
error->warning(FLERR,"Temperature compute {} for fix {} is not for group all: {}",
arg[1], style, group->names[temperature->igroup]);
// reset id_temp of pressure to new temperature ID
auto icompute = modify->get_compute_by_id(id_press);
if (!icompute)
error->all(FLERR,"Pressure compute ID {} for fix {} does not exist", id_press, style);
icompute->reset_extra_compute_fix(id_temp);
return 2;
} else if (strcmp(arg[0],"press") == 0) {
if (narg < 2) error->all(FLERR,"Illegal fix_modify command");
if (pflag) {
modify->delete_compute(id_press);
pflag = 0;
}
delete[] id_press;
id_press = utils::strdup(arg[1]);
pressure = modify->get_compute_by_id(arg[1]);
if (!pressure) error->all(FLERR,"Could not find fix_modify pressure compute ID: {}", arg[1]);
if (pressure->pressflag == 0)
error->all(FLERR,"Fix_modify pressure compute {} does not compute pressure", arg[1]);
return 2;
}
return 0;
}