// 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_pair.h" #include "atom.h" #include "error.h" #include "force.h" #include "fix.h" #include "memory.h" #include "pair.h" #include "update.h" #include using namespace LAMMPS_NS; using namespace FixConst; /* ---------------------------------------------------------------------- */ FixPair::FixPair(LAMMPS *lmp, int narg, char **arg) : Fix(lmp, narg, arg) { if (narg < 7) utils::missing_cmd_args(FLERR, "fix pair", error); nevery = utils::inumeric(FLERR,arg[3],false,lmp); if (nevery < 1) error->all(FLERR,"Illegal fix pair every value: {}", nevery); pairname = utils::strdup(arg[4]); query_pstyle(lmp); if (pstyle == nullptr) error->all(FLERR,"Pair style {} for fix pair not found", pairname); nfield = (narg-5) / 2; fieldname = new char*[nfield]; trigger = new int[nfield]; nfield = 0; int iarg = 5; while (iarg < narg) { if (iarg+2 > narg) utils::missing_cmd_args(FLERR, fmt::format("fix pair {}", arg[iarg]), error); fieldname[nfield] = utils::strdup(arg[iarg]); int flag = utils::inumeric(FLERR,arg[iarg+1],true,lmp); if (flag == 0) trigger[nfield] = 0; else if (flag == 1) trigger[nfield] = 1; else error->all(FLERR,"Illegal fix pair {} command flag: {}", arg[iarg], arg[iarg+1]); nfield++; iarg += 2; } // set trigger names = fieldname + "_flag" triggername = new char*[nfield]; for (int ifield = 0; ifield < nfield; ifield++) { if (trigger[ifield] == 0) triggername[ifield] = nullptr; else triggername[ifield] = utils::strdup(fmt::format("{}_flag", fieldname[ifield])); } // extract all fields just to get number of per-atom values // returned data ptr may be NULL, if pair style has not allocated field yet // check for recognized field cannot be done until post_force() // also check if triggername can be extracted as a scalar value triggerptr = new int*[nfield]; ncols = 0; for (int ifield = 0; ifield < nfield; ifield++) { int columns = 0; // set in case fieldname not recognized by pstyle pstyle->extract_peratom(fieldname[ifield],columns); if (columns) ncols += columns; else ncols++; if (trigger[ifield]) { int dim; triggerptr[ifield] = (int *) pstyle->extract(triggername[ifield],dim); if (!triggerptr[ifield]) error->all(FLERR,"Fix pair pair style cannot extract {}", triggername[ifield]); if (dim) error->all(FLERR,"Fix pair pair style {} trigger {} is not a scalar", pairname, triggername[ifield]); } } // if set peratom_freq = Nevery, then cannot access the per-atom // values as part of thermo output during minimiziation // at different frequency or on last step of minimization // instead set peratom_freq = 1 // ok, since vector/array always have values // but requires the vector/array be persisted between Nevery steps // since it may be accessed peratom_flag = 1; if (ncols == 1) size_peratom_cols = 0; else size_peratom_cols = ncols; peratom_freq = 1; // perform initial allocation of atom-based array // register with Atom class vector = nullptr; array = nullptr; grow_arrays(atom->nmax); atom->add_callback(Atom::GROW); // zero the vector/array since dump may access it on timestep 0 // zero the vector/array since a variable may access it before first ru // initialize lasttime so step 0 will trigger/extract int nlocal = atom->nlocal; if (ncols == 1) { for (int i = 0; i < nlocal; i++) vector[i] = 0.0; } else { for (int i = 0; i < nlocal; i++) for (int m = 0; m < ncols; m++) array[i][m] = 0.0; } lasttime = -1; } /* ---------------------------------------------------------------------- */ void FixPair::query_pstyle(LAMMPS *lmp) { char *cptr=nullptr; int nsub = 0; if ((cptr = strchr(pairname, ':'))) { *cptr = '\0'; nsub = utils::inumeric(FLERR,cptr+1,false,lmp); } pstyle = nullptr; if (lmp->suffix_enable) { if (lmp->suffix) { pstyle = force->pair_match(fmt::format("{}/{}", pairname, lmp->suffix), 1, nsub); if (pstyle == nullptr && (lmp->suffix2)) { pstyle = force->pair_match(fmt::format("{}/{}", pairname, lmp->suffix2), 1, nsub); } } } if (pstyle == nullptr) pstyle = force->pair_match(pairname, 1, nsub); } /* ---------------------------------------------------------------------- */ FixPair::~FixPair() { // unregister callbacks to this fix from Atom class atom->delete_callback(id,Atom::GROW); delete[] pairname; for (int ifield = 0; ifield < nfield; ifield++) { delete[] fieldname[ifield]; delete[] triggername[ifield]; } delete[] fieldname; delete[] trigger; delete[] triggername; delete[] triggerptr; if (ncols == 1) memory->destroy(vector); else memory->destroy(array); } /* ---------------------------------------------------------------------- */ int FixPair::setmask() { int mask = 0; mask |= PRE_FORCE; mask |= MIN_PRE_FORCE; mask |= POST_FORCE; mask |= MIN_POST_FORCE; return mask; } /* ---------------------------------------------------------------------- */ void FixPair::init() { // ensure pair style still exists query_pstyle(lmp); if (pstyle == nullptr) error->all(FLERR,"Pair style {} for fix pair not found", pairname); } /* ---------------------------------------------------------------------- */ void FixPair::setup(int vflag) { post_force(vflag); } /* ---------------------------------------------------------------------- */ void FixPair::min_setup(int vflag) { setup(vflag); } /* ---------------------------------------------------------------------- */ void FixPair::setup_pre_force(int vflag) { pre_force(vflag); } /* ---------------------------------------------------------------------- trigger pair style computation on steps which are multiples of Nevery lasttime prevents mulitiple triggers by min linesearch on same iteration ------------------------------------------------------------------------- */ void FixPair::pre_force(int /*vflag*/) { if (update->ntimestep % nevery) return; if (update->ntimestep == lasttime) return; // set pair style triggers for (int ifield = 0; ifield < nfield; ifield++) if (trigger[ifield]) *(triggerptr[ifield]) = 1; } /* ---------------------------------------------------------------------- */ void FixPair::min_pre_force(int vflag) { pre_force(vflag); } /* ---------------------------------------------------------------------- extract results from pair style on steps which are multiples of Nevery lasttime prevents mulitiple extracts by min linesearch on same iteration ------------------------------------------------------------------------- */ void FixPair::post_force(int /*vflag*/) { if (update->ntimestep % nevery) return; if (update->ntimestep == lasttime) return; lasttime = update->ntimestep; // extract pair style fields one by one // store their values in this fix const int nlocal = atom->nlocal; int icol = 0; int columns; for (int ifield = 0; ifield < nfield; ifield++) { void *pvoid = pstyle->extract_peratom(fieldname[ifield],columns); // Pair::extract_peratom() may return a null pointer if there are no atoms the sub-domain // so returning null is only an error if there are local atoms. if ((pvoid == nullptr) && (nlocal > 0)) error->one(FLERR, "Fix pair cannot extract property {} from pair style", fieldname[ifield]); if (columns == 0) { double *pvector = (double *) pvoid; if (ncols == 1) { for (int i = 0; i < nlocal; i++) vector[i] = pvector[i]; } else { for (int i = 0; i < nlocal; i++) array[i][icol] = pvector[i]; } icol++; } else { double **parray = (double **) pvoid; int icoltmp = icol; for (int i = 0; i < nlocal; i++) { icol = icoltmp; for (int m = 0; m < columns; m++) { array[i][icol] = parray[i][m]; icol++; } } } } // unset pair style triggers for (int ifield = 0; ifield < nfield; ifield++) if (trigger[ifield]) *(triggerptr[ifield]) = 0; } /* ---------------------------------------------------------------------- */ void FixPair::min_post_force(int vflag) { post_force(vflag); } /* ---------------------------------------------------------------------- allocate atom-based vector or array ------------------------------------------------------------------------- */ void FixPair::grow_arrays(int nmax) { if (ncols == 1) { memory->grow(vector,nmax,"store/state:vector"); vector_atom = vector; } else { memory->grow(array,nmax,ncols,"store/state:array"); array_atom = array; } } /* ---------------------------------------------------------------------- copy values within local atom-based array ------------------------------------------------------------------------- */ void FixPair::copy_arrays(int i, int j, int /*delflag*/) { if (ncols == 1) { vector[j] = vector[i]; } else { for (int m = 0; m < ncols; m++) array[j][m] = array[i][m]; } } /* ---------------------------------------------------------------------- pack values in local atom-based array for exchange with another proc ------------------------------------------------------------------------- */ int FixPair::pack_exchange(int i, double *buf) { if (ncols == 1) { buf[0] = vector[i]; } else { for (int m = 0; m < ncols; m++) buf[m] = array[i][m]; } return ncols; } /* ---------------------------------------------------------------------- unpack values in local atom-based array from exchange with another proc ------------------------------------------------------------------------- */ int FixPair::unpack_exchange(int nlocal, double *buf) { if (ncols == 1) { vector[nlocal] = buf[0]; } else { for (int m = 0; m < ncols; m++) array[nlocal][m] = buf[m]; } return ncols; } /* ---------------------------------------------------------------------- memory usage of local atom-based vector or array ------------------------------------------------------------------------- */ double FixPair::memory_usage() { double bytes = 0.0; if (ncols == 1) bytes += (double)atom->nmax * sizeof(double); else bytes += (double)atom->nmax*ncols * sizeof(double); return bytes; }