/* ---------------------------------------------------------------------- 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. ------------------------------------------------------------------------- */ /* Copyright 2021 Yury Lysogorskiy^1, Cas van der Oord^2, Anton Bochkarev^1, Sarath Menon^1, Matteo Rinaldi^1, Thomas Hammerschmidt^1, Matous Mrovec^1, Aidan Thompson^3, Gabor Csanyi^2, Christoph Ortner^4, Ralf Drautz^1 ^1: Ruhr-University Bochum, Bochum, Germany ^2: University of Cambridge, Cambridge, United Kingdom ^3: Sandia National Laboratories, Albuquerque, New Mexico, USA ^4: University of British Columbia, Vancouver, BC, Canada */ // // Created by Lysogorskiy Yury on 27.02.20. // #include "pair_pace.h" #include "atom.h" #include "comm.h" #include "error.h" #include "force.h" #include "math_const.h" #include "memory.h" #include "neigh_list.h" #include "neighbor.h" #include "update.h" #include #include #include "ace_c_basis.h" #include "ace_evaluator.h" #include "ace_recursive.h" #include "ace_version.h" namespace LAMMPS_NS { struct ACEImpl { ACEImpl() : basis_set(nullptr), ace(nullptr) {} ~ACEImpl() { delete basis_set; delete ace; } ACECTildeBasisSet *basis_set; ACERecursiveEvaluator *ace; }; } // namespace LAMMPS_NS using namespace LAMMPS_NS; using namespace MathConst; static char const *const elements_pace[] = { "X", "H", "He", "Li", "Be", "B", "C", "N", "O", "F", "Ne", "Na", "Mg", "Al", "Si", "P", "S", "Cl", "Ar", "K", "Ca", "Sc", "Ti", "V", "Cr", "Mn", "Fe", "Co", "Ni", "Cu", "Zn", "Ga", "Ge", "As", "Se", "Br", "Kr", "Rb", "Sr", "Y", "Zr", "Nb", "Mo", "Tc", "Ru", "Rh", "Pd", "Ag", "Cd", "In", "Sn", "Sb", "Te", "I", "Xe", "Cs", "Ba", "La", "Ce", "Pr", "Nd", "Pm", "Sm", "Eu", "Gd", "Tb", "Dy", "Ho", "Er", "Tm", "Yb", "Lu", "Hf", "Ta", "W", "Re", "Os", "Ir", "Pt", "Au", "Hg", "Tl", "Pb", "Bi", "Po", "At", "Rn", "Fr", "Ra", "Ac", "Th", "Pa", "U", "Np", "Pu", "Am", "Cm", "Bk", "Cf", "Es", "Fm", "Md", "No", "Lr"}; static constexpr int elements_num_pace = sizeof(elements_pace) / sizeof(const char *); static int AtomicNumberByName_pace(char *elname) { for (int i = 1; i < elements_num_pace; i++) if (strcmp(elname, elements_pace[i]) == 0) return i; return -1; } /* ---------------------------------------------------------------------- */ PairPACE::PairPACE(LAMMPS *lmp) : Pair(lmp) { single_enable = 0; restartinfo = 0; one_coeff = 1; manybody_flag = 1; aceimpl = new ACEImpl; recursive = false; scale = nullptr; chunksize = 4096; } /* ---------------------------------------------------------------------- check if allocated, since class can be destructed when incomplete ------------------------------------------------------------------------- */ PairPACE::~PairPACE() { if (copymode) return; delete aceimpl; if (allocated) { memory->destroy(setflag); memory->destroy(cutsq); memory->destroy(scale); } } /* ---------------------------------------------------------------------- */ void PairPACE::compute(int eflag, int vflag) { int i, j, ii, jj, inum, jnum; double delx, dely, delz, evdwl; double fij[3]; int *ilist, *jlist, *numneigh, **firstneigh; ev_init(eflag, vflag); // downwards modified by YL double **x = atom->x; double **f = atom->f; int *type = atom->type; // number of atoms in cell int nlocal = atom->nlocal; int newton_pair = force->newton_pair; // inum: length of the neighborlists list inum = list->inum; // ilist: list of "i" atoms for which neighbor lists exist ilist = list->ilist; //numneigh: the length of each these neigbor list numneigh = list->numneigh; // the pointer to the list of neighbors of "i" firstneigh = list->firstneigh; //determine the maximum number of neighbours int max_jnum = 0; int nei = 0; for (ii = 0; ii < list->inum; ii++) { i = ilist[ii]; jnum = numneigh[i]; nei = nei + jnum; if (jnum > max_jnum) max_jnum = jnum; } aceimpl->ace->resize_neighbours_cache(max_jnum); //loop over atoms for (ii = 0; ii < list->inum; ii++) { i = list->ilist[ii]; const int itype = type[i]; const double xtmp = x[i][0]; const double ytmp = x[i][1]; const double ztmp = x[i][2]; jlist = firstneigh[i]; jnum = numneigh[i]; // checking if neighbours are actually within cutoff range is done inside compute_atom // mapping from LAMMPS atom types ('type' array) to ACE species is done inside compute_atom // by using 'aceimpl->ace->element_type_mapping' array // x: [r0 ,r1, r2, ..., r100] // i = 0 ,1 // jnum(0) = 50 // jlist(neigh ind of 0-atom) = [1,2,10,7,99,25, .. 50 element in total] try { aceimpl->ace->compute_atom(i, x, type, jnum, jlist); } catch (std::exception &e) { error->one(FLERR, e.what()); } // 'compute_atom' will update the `aceimpl->ace->e_atom` and `aceimpl->ace->neighbours_forces(jj, alpha)` arrays for (jj = 0; jj < jnum; jj++) { j = jlist[jj]; j &= NEIGHMASK; delx = x[j][0] - xtmp; dely = x[j][1] - ytmp; delz = x[j][2] - ztmp; fij[0] = scale[itype][itype] * aceimpl->ace->neighbours_forces(jj, 0); fij[1] = scale[itype][itype] * aceimpl->ace->neighbours_forces(jj, 1); fij[2] = scale[itype][itype] * aceimpl->ace->neighbours_forces(jj, 2); f[i][0] += fij[0]; f[i][1] += fij[1]; f[i][2] += fij[2]; f[j][0] -= fij[0]; f[j][1] -= fij[1]; f[j][2] -= fij[2]; // tally per-atom virial contribution if (vflag) ev_tally_xyz(i, j, nlocal, newton_pair, 0.0, 0.0, fij[0], fij[1], fij[2], -delx, -dely, -delz); } // tally energy contribution if (eflag) { // evdwl = energy of atom I evdwl = scale[itype][itype] * aceimpl->ace->e_atom; ev_tally_full(i, 2.0 * evdwl, 0.0, 0.0, 0.0, 0.0, 0.0); } } if (vflag_fdotr) virial_fdotr_compute(); // end modifications YL } /* ---------------------------------------------------------------------- */ void PairPACE::allocate() { allocated = 1; int n = atom->ntypes + 1; memory->create(setflag, n, n, "pair:setflag"); memory->create(cutsq, n, n, "pair:cutsq"); memory->create(scale, n, n, "pair:scale"); map = new int[n]; } /* ---------------------------------------------------------------------- global settings ------------------------------------------------------------------------- */ void PairPACE::settings(int narg, char **arg) { if (narg > 3) error->all(FLERR, "Illegal pair_style command."); // ACE potentials are parameterized in metal units if (strcmp("metal", update->unit_style) != 0) error->all(FLERR, "ACE potentials require 'metal' units"); recursive = true; // default evaluator style: RECURSIVE int iarg = 0; while (iarg < narg) { if (strcmp(arg[iarg], "recursive") == 0) { recursive = true; iarg += 1; } else if (strcmp(arg[iarg], "product") == 0) { recursive = false; iarg += 1; } else if (strcmp(arg[iarg], "chunksize") == 0) { chunksize = utils::inumeric(FLERR, arg[iarg + 1], false, lmp); iarg += 2; } else error->all(FLERR, "Illegal pair_style command"); } if (comm->me == 0) { utils::logmesg(lmp, "ACE version: {}.{}.{}\n", VERSION_YEAR, VERSION_MONTH, VERSION_DAY); if (recursive) utils::logmesg(lmp, "Recursive evaluator is used\n"); else utils::logmesg(lmp, "Product evaluator is used\n"); } } /* ---------------------------------------------------------------------- set coeffs for one or more type pairs ------------------------------------------------------------------------- */ void PairPACE::coeff(int narg, char **arg) { if (!allocated) allocate(); map_element2type(narg - 3, arg + 3); auto potential_file_name = utils::get_potential_file_path(arg[2]); char **elemtypes = &arg[3]; //load potential file delete aceimpl->basis_set; if (comm->me == 0) utils::logmesg(lmp, "Loading {}\n", potential_file_name); aceimpl->basis_set = new ACECTildeBasisSet(potential_file_name); if (comm->me == 0) { utils::logmesg(lmp, "Total number of basis functions\n"); for (SPECIES_TYPE mu = 0; mu < aceimpl->basis_set->nelements; mu++) { int n_r1 = aceimpl->basis_set->total_basis_size_rank1[mu]; int n = aceimpl->basis_set->total_basis_size[mu]; utils::logmesg(lmp, "\t{}: {} (r=1) {} (r>1)\n", aceimpl->basis_set->elements_name[mu], n_r1, n); } } // read args that map atom types to pACE elements // map[i] = which element the Ith atom type is, -1 if not mapped // map[0] is not used delete aceimpl->ace; aceimpl->ace = new ACERecursiveEvaluator(); aceimpl->ace->set_recursive(recursive); aceimpl->ace->element_type_mapping.init(atom->ntypes + 1); const int n = atom->ntypes; for (int i = 1; i <= n; i++) { char *elemname = arg[2 + i]; if (strcmp(elemname, "NULL") == 0) { // species_type=-1 value will not reach ACE Evaluator::compute_atom, // but if it will ,then error will be thrown there aceimpl->ace->element_type_mapping(i) = -1; map[i] = -1; if (comm->me == 0) utils::logmesg(lmp, "Skipping LAMMPS atom type #{}(NULL)\n", i); } else { int atomic_number = AtomicNumberByName_pace(elemname); if (atomic_number == -1) error->all(FLERR, "'{}' is not a valid element\n", elemname); SPECIES_TYPE mu = aceimpl->basis_set->get_species_index_by_name(elemname); if (mu != -1) { if (comm->me == 0) utils::logmesg(lmp, "Mapping LAMMPS atom type #{}({}) -> ACE species type #{}\n", i, elemname, mu); map[i] = mu; // set up LAMMPS atom type to ACE species mapping for ace evaluator aceimpl->ace->element_type_mapping(i) = mu; } else { error->all(FLERR, "Element {} is not supported by ACE-potential from file {}", elemname, potential_file_name); } } } // initialize scale factor for (int i = 1; i <= n; i++) { for (int j = i; j <= n; j++) scale[i][j] = 1.0; } aceimpl->ace->set_basis(*aceimpl->basis_set, 1); } /* ---------------------------------------------------------------------- init specific to this pair style ------------------------------------------------------------------------- */ void PairPACE::init_style() { if (atom->tag_enable == 0) error->all(FLERR, "Pair style pACE requires atom IDs"); if (force->newton_pair == 0) error->all(FLERR, "Pair style pACE requires newton pair on"); // request a full neighbor list neighbor->add_request(this, NeighConst::REQ_FULL); } /* ---------------------------------------------------------------------- init for one type pair i,j and corresponding j,i ------------------------------------------------------------------------- */ double PairPACE::init_one(int i, int j) { if (setflag[i][j] == 0) error->all(FLERR, "All pair coeffs are not set"); //cutoff from the basis set's radial functions settings scale[j][i] = scale[i][j]; return aceimpl->basis_set->radial_functions->cut(map[i], map[j]); } /* ---------------------------------------------------------------------- extract method for extracting value of scale variable ---------------------------------------------------------------------- */ void *PairPACE::extract(const char *str, int &dim) { dim = 2; if (strcmp(str, "scale") == 0) return (void *) scale; return nullptr; }