/* ---------------------------------------------------------------------- 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. ------------------------------------------------------------------------- */ /* ---------------------------------------------------------------------- Contributing author: Germain Clavier (TUe) ------------------------------------------------------------------------- */ #include "fix_press_langevin.h" #include "atom.h" #include "comm.h" #include "compute.h" #include "domain.h" #include "error.h" #include "fix_deform.h" #include "force.h" #include "irregular.h" #include "kspace.h" #include "modify.h" #include "random_mars.h" #include "update.h" #include #include using namespace LAMMPS_NS; using namespace FixConst; static constexpr double DELTAFLIP = 0.1; static constexpr double TILTMAX = 1.5; enum { NONE, XYZ, XY, YZ, XZ }; enum { ISO, ANISO, TRICLINIC }; /* ---------------------------------------------------------------------- */ FixPressLangevin::FixPressLangevin(LAMMPS *lmp, int narg, char **arg) : Fix(lmp, narg, arg), id_temp(nullptr), id_press(nullptr), temperature(nullptr), pressure(nullptr), irregular(nullptr), random(nullptr) { if (narg < 5) utils::missing_cmd_args(FLERR, "fix press/langevin", error); // Langevin barostat applied every step // For details on the equations of motion see: // Gronbech-Jensen & Farago J. Chem. Phys. 141 194108 (2014) nevery = 1; // default values pcouple = NONE; allremap = 1; pre_exchange_flag = 0; flipflag = 1; seed = 111111; pflag = 0; kspace_flag = 0; p_ltime = 0.0; // target temperature t_start = t_stop = t_target = 0.0; for (int i = 0; i < 6; i++) { // pressure and pistons period p_start[i] = p_stop[i] = p_period[i] = 0.0; p_flag[i] = 0; p_alpha[i] = 0; p_mass[i] = 0.; // pistons coordinates derivative V p_deriv[i] = 0.0; // a and b values for each piston gjfa[i] = 0.0; gjfb[i] = 0.0; // random value for each piston fran[i] = 0.0; f_piston[i] = 0.0; dilation[i] = 0.0; } // process keywords dimension = domain->dimension; int iarg = 3; while (iarg < narg) { if (strcmp(arg[iarg], "iso") == 0) { if (iarg + 4 > narg) utils::missing_cmd_args(FLERR, "fix press/langevin iso", error); pcouple = XYZ; p_start[0] = p_start[1] = p_start[2] = utils::numeric(FLERR, arg[iarg + 1], false, lmp); p_stop[0] = p_stop[1] = p_stop[2] = utils::numeric(FLERR, arg[iarg + 2], false, lmp); p_period[0] = p_period[1] = p_period[2] = utils::numeric(FLERR, arg[iarg + 3], false, lmp); p_flag[0] = p_flag[1] = p_flag[2] = 1; if (dimension == 2) { p_start[2] = p_stop[2] = p_period[2] = 0.0; p_flag[2] = 0; } iarg += 4; } else if (strcmp(arg[iarg], "aniso") == 0) { if (iarg + 4 > narg) utils::missing_cmd_args(FLERR, "fix press/langevin aniso", error); pcouple = NONE; p_start[0] = p_start[1] = p_start[2] = utils::numeric(FLERR, arg[iarg + 1], false, lmp); p_stop[0] = p_stop[1] = p_stop[2] = utils::numeric(FLERR, arg[iarg + 2], false, lmp); p_period[0] = p_period[1] = p_period[2] = utils::numeric(FLERR, arg[iarg + 3], false, lmp); p_flag[0] = p_flag[1] = p_flag[2] = 1; if (dimension == 2) { p_start[2] = p_stop[2] = p_period[2] = 0.0; p_flag[2] = 0; } iarg += 4; } else if (strcmp(arg[iarg], "tri") == 0) { if (iarg + 4 > narg) utils::missing_cmd_args(FLERR, "fix press/langevin tri", error); pcouple = NONE; p_start[0] = p_start[1] = p_start[2] = utils::numeric(FLERR, arg[iarg + 1], false, lmp); p_stop[0] = p_stop[1] = p_stop[2] = utils::numeric(FLERR, arg[iarg + 2], false, lmp); p_period[0] = p_period[1] = p_period[2] = utils::numeric(FLERR, arg[iarg + 3], false, lmp); p_flag[0] = p_flag[1] = p_flag[2] = 1; p_start[3] = p_start[4] = p_start[5] = 0.0; p_stop[3] = p_stop[4] = p_stop[5] = 0.0; p_period[3] = p_period[4] = p_period[5] = utils::numeric(FLERR, arg[iarg + 3], false, lmp); p_flag[3] = p_flag[4] = p_flag[5] = 1; if (dimension == 2) { p_start[2] = p_stop[2] = p_period[2] = 0.0; p_flag[2] = 0; p_start[3] = p_stop[3] = p_period[3] = 0.0; p_flag[3] = 0; p_start[4] = p_stop[4] = p_period[4] = 0.0; p_flag[4] = 0; } iarg += 4; } else if (strcmp(arg[iarg], "x") == 0) { if (iarg + 4 > narg) utils::missing_cmd_args(FLERR, "fix press/langevin tri", error); if (iarg + 4 > narg) error->all(FLERR, "Illegal fix press/langevin command"); p_start[0] = utils::numeric(FLERR, arg[iarg + 1], false, lmp); p_stop[0] = utils::numeric(FLERR, arg[iarg + 2], false, lmp); p_period[0] = utils::numeric(FLERR, arg[iarg + 3], false, lmp); p_flag[0] = 1; iarg += 4; } else if (strcmp(arg[iarg], "y") == 0) { if (iarg + 4 > narg) utils::missing_cmd_args(FLERR, "fix press/langevin y", error); p_start[1] = utils::numeric(FLERR, arg[iarg + 1], false, lmp); p_stop[1] = utils::numeric(FLERR, arg[iarg + 2], false, lmp); p_period[1] = utils::numeric(FLERR, arg[iarg + 3], false, lmp); p_flag[1] = 1; iarg += 4; } else if (strcmp(arg[iarg], "z") == 0) { if (iarg + 4 > narg) utils::missing_cmd_args(FLERR, "fix press/langevin z", error); p_start[2] = utils::numeric(FLERR, arg[iarg + 1], false, lmp); p_stop[2] = utils::numeric(FLERR, arg[iarg + 2], false, lmp); p_period[2] = utils::numeric(FLERR, arg[iarg + 3], false, lmp); p_flag[2] = 1; iarg += 4; if (dimension == 2) error->all(FLERR, "Fix press/langevin z option not allowed for a 2d simulation"); } else if (strcmp(arg[iarg], "xy") == 0) { if (iarg + 4 > narg) utils::missing_cmd_args(FLERR, "fix press/langevin yz", error); p_start[3] = utils::numeric(FLERR, arg[iarg + 1], false, lmp); p_stop[3] = utils::numeric(FLERR, arg[iarg + 2], false, lmp); p_period[3] = utils::numeric(FLERR, arg[iarg + 3], false, lmp); p_flag[3] = 1; iarg += 4; if (dimension == 2) error->all(FLERR, "Fix press/langevin yz option not allowed for a 2d simulation"); } else if (strcmp(arg[iarg], "xz") == 0) { if (iarg + 4 > narg) utils::missing_cmd_args(FLERR, "fix press/langevin xz", error); p_start[4] = utils::numeric(FLERR, arg[iarg + 1], false, lmp); p_stop[4] = utils::numeric(FLERR, arg[iarg + 2], false, lmp); p_period[4] = utils::numeric(FLERR, arg[iarg + 3], false, lmp); p_flag[4] = 1; iarg += 4; if (dimension == 2) error->all(FLERR, "Fix press/langevin zz option not allowed for a 2d simulation"); } else if (strcmp(arg[iarg], "yz") == 0) { if (iarg + 4 > narg) utils::missing_cmd_args(FLERR, "fix press/langevin xy", error); p_start[5] = utils::numeric(FLERR, arg[iarg + 1], false, lmp); p_stop[5] = utils::numeric(FLERR, arg[iarg + 2], false, lmp); p_period[5] = utils::numeric(FLERR, arg[iarg + 3], false, lmp); p_flag[5] = 1; iarg += 4; if (dimension == 2) error->all(FLERR, "Invalid fix {} command for a 2d simulation", style); } else if (strcmp(arg[iarg], "flip") == 0) { if (iarg + 2 > narg) utils::missing_cmd_args(FLERR, "fix press/langevin 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 press/langevin 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 = NONE; else error->all(FLERR, "Unknown fix press/langevin couple option: {}", arg[iarg + 1]); iarg += 2; } else if (strcmp(arg[iarg], "friction") == 0) { if (iarg + 2 > narg) utils::missing_cmd_args(FLERR, "fix press/langevin friction", error); p_ltime = utils::numeric(FLERR, arg[iarg + 1], false, lmp); if (p_ltime <= 0.0) error->all(FLERR, "Fix press/langevin friction value must be > 0"); iarg += 2; } else if (strcmp(arg[iarg], "dilate") == 0) { if (iarg + 2 > narg) utils::missing_cmd_args(FLERR, "fix press/langevin dilate", error); if (strcmp(arg[iarg + 1], "all") == 0) allremap = 1; else if (strcmp(arg[iarg + 1], "partial") == 0) allremap = 0; else error->all(FLERR, "Unknown fix press/langevin dilate option: {}", arg[iarg + 1]); iarg += 2; } else if (strcmp(arg[iarg], "temp") == 0) { if (iarg + 4 > narg) utils::missing_cmd_args(FLERR, "fix press/langevin temp", error); t_start = utils::numeric(FLERR, arg[iarg + 1], false, lmp); t_stop = utils::numeric(FLERR, arg[iarg + 2], false, lmp); seed = utils::numeric(FLERR, arg[iarg + 3], false, lmp); if (seed <= 0) error->all(FLERR, "Fix press/langevin temp seed must be > 0"); iarg += 4; } else error->all(FLERR, "Unknown fix press/langevin keyword: {}", arg[iarg]); } if (allremap == 0) restart_pbc = 1; random = new RanMars(lmp, seed); // error checks if (dimension == 2 && p_flag[2]) error->all(FLERR, "Invalid fix press/langevin for a 2d simulation"); if (dimension == 2 && (pcouple == YZ || pcouple == XZ)) error->all(FLERR, "Invalid fix press/langevin for a 2d simulation"); if (pcouple == XYZ && (p_flag[0] == 0 || p_flag[1] == 0)) error->all(FLERR, "Invalid fix press/langevin pressure settings"); if (pcouple == XYZ && dimension == 3 && p_flag[2] == 0) error->all(FLERR, "Invalid fix press/langevin pressure settings"); if (pcouple == XY && (p_flag[0] == 0 || p_flag[1] == 0)) error->all(FLERR, "Invalid fix press/langevin pressure settings"); if (pcouple == YZ && (p_flag[1] == 0 || p_flag[2] == 0)) error->all(FLERR, "Invalid fix press/langevin pressure settings"); if (pcouple == XZ && (p_flag[0] == 0 || p_flag[2] == 0)) error->all(FLERR, "Invalid fix press/langevin pressure settings"); if (p_flag[0] && domain->xperiodic == 0) error->all(FLERR, "Cannot use fix press/langevin on a non-periodic dimension"); if (p_flag[1] && domain->yperiodic == 0) error->all(FLERR, "Cannot use fix press/langevin on a non-periodic dimension"); if (p_flag[2] && domain->zperiodic == 0) error->all(FLERR, "Cannot use fix press/langevin on a non-periodic dimension"); // require periodicity in 2nd dim of off-diagonal tilt component if (p_flag[3] && domain->zperiodic == 0) error->all(FLERR, "Cannot use fix {} on a 2nd non-periodic dimension", style); if (p_flag[4] && domain->zperiodic == 0) error->all(FLERR, "Cannot use fix {} on a 2nd non-periodic dimension", style); if (p_flag[5] && domain->yperiodic == 0) error->all(FLERR, "Cannot use fix {} on a 2nd non-periodic dimension", style); if (!domain->triclinic && (p_flag[3] || p_flag[4] || p_flag[5])) error->all(FLERR, "Can not specify Pxy/Pxz/Pyz in fix {} with non-triclinic box", style); if (pcouple == XYZ && dimension == 3 && (p_start[0] != p_start[1] || p_start[0] != p_start[2] || p_stop[0] != p_stop[1] || p_stop[0] != p_stop[2] || p_period[0] != p_period[1] || p_period[0] != p_period[2])) error->all(FLERR, "Invalid fix press/langevin pressure settings"); if (pcouple == XYZ && dimension == 2 && (p_start[0] != p_start[1] || p_stop[0] != p_stop[1] || p_period[0] != p_period[1])) error->all(FLERR, "Invalid fix press/langevin pressure settings"); if (pcouple == XY && (p_start[0] != p_start[1] || p_stop[0] != p_stop[1] || p_period[0] != p_period[1])) error->all(FLERR, "Invalid fix press/langevin pressure settings"); if (pcouple == YZ && (p_start[1] != p_start[2] || p_stop[1] != p_stop[2] || p_period[1] != p_period[2])) error->all(FLERR, "Invalid fix press/langevin pressure settings"); if (pcouple == XZ && (p_start[0] != p_start[2] || p_stop[0] != p_stop[2] || p_period[0] != p_period[2])) error->all(FLERR, "Invalid fix press/langevin pressure settings"); if (t_start < 0.0 || t_stop < 0.0) error->all(FLERR, "Fix press/langevin temperature parameters must be >= 0.0"); if ((p_flag[0] && p_period[0] <= 0.0) || (p_flag[1] && p_period[1] <= 0.0) || (p_flag[2] && p_period[2] <= 0.0) || (p_flag[3] && p_period[3] <= 0.0) || (p_flag[4] && p_period[4] <= 0.0) || (p_flag[5] && p_period[5] <= 0.0)) error->all(FLERR, "Fix press/langevin damping parameters must be > 0.0"); if (p_flag[0]) box_change |= BOX_CHANGE_X; if (p_flag[1]) box_change |= BOX_CHANGE_Y; if (p_flag[2]) box_change |= BOX_CHANGE_Z; if (p_flag[3]) box_change |= BOX_CHANGE_YZ; if (p_flag[4]) box_change |= BOX_CHANGE_XZ; if (p_flag[5]) box_change |= BOX_CHANGE_XY; // pstyle = ISO if XYZ coupling or XY coupling in 2d -> 1 dof // else pstyle = ANISO -> 3 dof if (p_flag[3] || p_flag[4] || p_flag[5]) pstyle = TRICLINIC; else if (pcouple == XYZ || (dimension == 2 && pcouple == XY)) pstyle = ISO; else pstyle = ANISO; // pre_exchange only required if flips can occur due to shape changes if (flipflag && (p_flag[3] || p_flag[4] || p_flag[5])) pre_exchange_flag = pre_exchange_migrate = 1; if (flipflag && (domain->yz != 0.0 || domain->xz != 0.0 || domain->xy != 0.0)) pre_exchange_flag = pre_exchange_migrate = 1; if (pre_exchange_flag) irregular = new Irregular(lmp); else irregular = nullptr; // Langevin GJF dynamics does NOT need a temperature compute // This is stated explicitely in their paper. // The temperature used for the pressure is NkT/V on purpose. // For this reason, the compute must use the virial pressure // Kinetic contribution will be added by the fix style id_press = utils::strdup(std::string(id) + "_press"); pressure = modify->add_compute(fmt::format("{} all pressure NULL virial", id_press)); pflag = 1; // p_fric is alpha coeff from GJF // with alpha = Q/p_period // similar to fix_langevin formalism double kt = force->boltz * t_start; double nkt = (atom->natoms + 1) * kt; for (int i = 0; i < 6; i++) { if (p_ltime > 0.0) p_fric[i] = p_ltime; else p_fric[i] = p_period[i]; } for (int i = 0; i < 6; i++) { p_mass[i] = nkt * p_period[i] * p_period[i]; p_alpha[i] = p_mass[i] * p_fric[i]; gjfa[i] = (1.0 - p_alpha[i] * update->dt / 2.0 / p_mass[i]) / (1.0 + p_alpha[i] * update->dt / 2.0 / p_mass[i]); gjfb[i] = 1. / (1.0 + p_alpha[i] * update->dt / 2.0 / p_mass[i]); } } /* ---------------------------------------------------------------------- */ FixPressLangevin::~FixPressLangevin() { delete random; delete irregular; // delete temperature and pressure if fix created them if (pflag) modify->delete_compute(id_press); delete[] id_press; } /* ---------------------------------------------------------------------- */ int FixPressLangevin::setmask() { int mask = 0; mask |= INITIAL_INTEGRATE; mask |= POST_INTEGRATE; mask |= POST_FORCE; mask |= END_OF_STEP; if (pre_exchange_flag) mask |= PRE_EXCHANGE; return mask; } /* ---------------------------------------------------------------------- */ void FixPressLangevin::init() { // ensure no conflict with fix deform for (const auto &ifix : modify->get_fix_by_style("^deform")) { int *dimflag = static_cast(ifix)->dimflag; if (!dimflag) continue; if ((p_flag[0] && dimflag[0]) || (p_flag[1] && dimflag[1]) || (p_flag[2] && dimflag[2]) || (p_flag[3] && dimflag[3]) || (p_flag[4] && dimflag[4]) || (p_flag[5] && dimflag[5])) error->all(FLERR, "Cannot use fix press/langevin and fix deform on same component of stress tensor"); } // set pressure ptr pressure = modify->get_compute_by_id(id_press); if (!pressure) error->all(FLERR, "Pressure compute ID {} for fix press/langevin does not exist", id_press); // Kspace setting if (force->kspace) kspace_flag = 1; else kspace_flag = 0; // detect if any rigid fixes exist so rigid bodies move when box is remapped rfix.clear(); for (auto &ifix : modify->get_fix_list()) if (ifix->rigid_flag) rfix.push_back(ifix); // Nullifies piston derivatives and forces so that it is not integrated at // the start of a second run. for (int i = 0; i < 6; i++) { p_deriv[i] = 0.0; dilation[i] = 0.0; } } /* ---------------------------------------------------------------------- compute T,P before integrator starts ------------------------------------------------------------------------- */ void FixPressLangevin::setup(int /*vflag*/) { // trigger virial computation on next timestep pressure->addstep(update->ntimestep + 1); } /* ---------------------------------------------------------------------- */ void FixPressLangevin::initial_integrate(int /* vflag */) { // compute new V double dt; double dl; double displacement; double delta = update->ntimestep - update->beginstep; // compute new random term on pistons dynamics if (delta != 0.0) delta /= update->endstep - update->beginstep; t_target = t_start + delta * (t_stop - t_start); couple_beta(); dt = update->dt; for (int i = 0; i < 6; i++) { if (p_flag[i]) { // See equation 13 displacement = dt * p_deriv[i] * gjfb[i]; displacement += 0.5 * dt * dt * f_piston[i] * gjfb[i] / p_mass[i]; displacement += 0.5 * dt * fran[i] * gjfb[i] / p_mass[i]; if (i < 3) { dl = domain->boxhi[i] - domain->boxlo[i]; dilation[i] = (dl + displacement) / dl; } else { dilation[i] = displacement; } } } } void FixPressLangevin::post_integrate() { // remap simulation box and atoms // redo KSpace coeffs since volume has changed remap(); if (kspace_flag) force->kspace->setup(); } /* ---------------------------------------------------------------------- */ void FixPressLangevin::post_force(int /*vflag*/) { // compute new forces on pistons after internal virial computation double delta = update->ntimestep - update->beginstep; if (delta != 0.0) delta /= update->endstep - update->beginstep; // compute current pressure tensor and add kinetic term if (pstyle == ISO) { pressure->compute_scalar(); } else { pressure->compute_vector(); } couple_pressure(); couple_kinetic(); for (int i = 0; i < 6; i++) { if (p_flag[i]) { f_old_piston[i] = f_piston[i]; p_target[i] = p_start[i] + delta * (p_stop[i] - p_start[i]); f_piston[i] = p_current[i] - p_target[i]; } } // trigger virial computation on next timestep pressure->addstep(update->ntimestep + 1); } /* ---------------------------------------------------------------------- */ void FixPressLangevin::end_of_step() { // compute pistons velocity double dt; dt = update->dt; for (int i = 0; i < 6; i++) { if (p_flag[i]) { p_deriv[i] *= gjfa[i]; p_deriv[i] += 0.5 * dt * (gjfa[i] * f_old_piston[i] + f_piston[i]) / p_mass[i]; p_deriv[i] += fran[i] * gjfb[i] / p_mass[i]; } } } /* ---------------------------------------------------------------------- */ void FixPressLangevin::couple_pressure() { double *tensor = pressure->vector; if (pstyle == ISO) p_current[0] = p_current[1] = p_current[2] = pressure->scalar; else if (pcouple == XYZ) { double ave = 1.0 / 3.0 * (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 { p_current[0] = tensor[0]; p_current[1] = tensor[1]; p_current[2] = tensor[2]; } p_current[3] = tensor[3]; p_current[4] = tensor[4]; p_current[5] = tensor[5]; } /* ---------------------------------------------------------------------- */ void FixPressLangevin::couple_kinetic() { double pk, volume; // kinetic part if (dimension == 3) volume = domain->xprd * domain->yprd * domain->zprd; else volume = domain->xprd * domain->yprd; pk = atom->natoms * force->boltz * t_target / volume; pk *= force->nktv2p; p_current[0] += pk; p_current[1] += pk; if (dimension == 3) p_current[2] += pk; } /* ---------------------------------------------------------------------- */ void FixPressLangevin::couple_beta() { double gamma[6]; int me = comm->me; for (int i = 0; i < 6; i++) gamma[i] = sqrt(2.0 * p_fric[i] * force->boltz * update->dt * t_target); fran[0] = fran[1] = fran[2] = 0.0; fran[3] = fran[4] = fran[5] = 0.0; if (me == 0) { if (pstyle == ISO) fran[0] = fran[1] = fran[2] = gamma[0] * random->gaussian(); else if (pcouple == XYZ) { fran[0] = fran[1] = fran[2] = gamma[0] * random->gaussian(); } else if (pcouple == XY) { fran[0] = fran[1] = gamma[0] * random->gaussian(); fran[2] = gamma[2] * random->gaussian(); } else if (pcouple == YZ) { fran[1] = fran[2] = gamma[1] * random->gaussian(); fran[0] = gamma[0] * random->gaussian(); } else if (pcouple == XZ) { fran[0] = fran[2] = gamma[0] * random->gaussian(); fran[1] = gamma[1] * random->gaussian(); } else { fran[0] = gamma[0] * random->gaussian(); fran[1] = gamma[1] * random->gaussian(); fran[2] = gamma[2] * random->gaussian(); } fran[3] = gamma[3] * random->gaussian(); fran[4] = gamma[4] * random->gaussian(); fran[5] = gamma[5] * random->gaussian(); } MPI_Bcast(&fran, 6, MPI_DOUBLE, 0, world); } /* ---------------------------------------------------------------------- change box size remap all atoms or fix group atoms depending on allremap flag if rigid bodies exist, scale rigid body centers-of-mass ------------------------------------------------------------------------- */ void FixPressLangevin::remap() { int i; double oldlo, oldhi, ctr; double **x = atom->x; int *mask = atom->mask; int nlocal = atom->nlocal; // convert pertinent atoms and rigid bodies to lamda coords if (allremap) domain->x2lamda(nlocal); else { for (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 for (i = 0; i < 3; i++) { if (p_flag[i]) { oldlo = domain->boxlo[i]; oldhi = domain->boxhi[i]; ctr = 0.5 * (oldlo + oldhi); domain->boxlo[i] = (oldlo - ctr) * dilation[i] + ctr; domain->boxhi[i] = (oldhi - ctr) * dilation[i] + ctr; } } if (p_flag[3]) domain->xy += dilation[3]; if (p_flag[4]) domain->xz += dilation[4]; if (p_flag[5]) domain->yz += dilation[5]; if (domain->yz < -TILTMAX * domain->yprd || domain->yz > TILTMAX * domain->yprd || domain->xz < -TILTMAX * domain->xprd || domain->xz > TILTMAX * domain->xprd || domain->xy < -TILTMAX * domain->xprd || domain->xy > TILTMAX * domain->xprd) error->all(FLERR, "Fix {} has tilted box too far in one step - " "periodic cell is too far from equilibrium state", style); domain->set_global_box(); domain->set_local_box(); // convert pertinent atoms and rigid bodies back to box coords if (allremap) domain->lamda2x(nlocal); else { for (i = 0; i < nlocal; i++) if (mask[i] & groupbit) domain->lamda2x(x[i], x[i]); } for (auto &ifix : rfix) ifix->deform(1); } /* ---------------------------------------------------------------------- if any tilt ratios exceed limits, 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 if any flip occurs, 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 FixPressLangevin::pre_exchange() { double xprd = domain->xprd; double yprd = domain->yprd; // flip is only triggered when tilt exceeds 0.5 by DELTAFLIP // this avoids immediate re-flipping due to tilt oscillations double xtiltmax = (0.5 + DELTAFLIP) * xprd; double ytiltmax = (0.5 + DELTAFLIP) * yprd; int flipxy, flipxz, flipyz; flipxy = flipxz = flipyz = 0; if (domain->yperiodic) { if (domain->yz < -ytiltmax) { domain->yz += yprd; domain->xz += domain->xy; flipyz = 1; } else if (domain->yz >= ytiltmax) { domain->yz -= yprd; domain->xz -= domain->xy; flipyz = -1; } } if (domain->xperiodic) { if (domain->xz < -xtiltmax) { domain->xz += xprd; flipxz = 1; } else if (domain->xz >= xtiltmax) { domain->xz -= xprd; flipxz = -1; } if (domain->xy < -xtiltmax) { domain->xy += xprd; flipxy = 1; } else if (domain->xy >= xtiltmax) { domain->xy -= xprd; flipxy = -1; } } int flip = 0; if (flipxy || flipxz || flipyz) flip = 1; if (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); } } /* ---------------------------------------------------------------------- */ int FixPressLangevin::modify_param(int narg, char **arg) { if (strcmp(arg[0], "press") == 0) { if (narg < 2) utils::missing_cmd_args(FLERR, "fix_modify press", error); 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; } /* ---------------------------------------------------------------------- */ void FixPressLangevin::reset_dt() { for (int i = 0; i < 6; i++) { gjfa[i] = (1.0 - p_alpha[i] * update->dt / 2.0 / p_mass[i]) / (1.0 + p_alpha[i] * update->dt / 2.0 / p_mass[i]); gjfb[i] = 1. / (1.0 + p_alpha[i] * update->dt / 2.0 / p_mass[i]); } }