diff --git a/src/ML-UF3/pair_uf3.cpp b/src/ML-UF3/pair_uf3.cpp new file mode 100644 index 0000000000..4188c51ac0 --- /dev/null +++ b/src/ML-UF3/pair_uf3.cpp @@ -0,0 +1,1312 @@ +/* ---------------------------------------------------------------------- + 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 authors: Ajinkya Hire (U of Florida), + * Hendrik Kraß (U of Constance), + * Richard Hennig (U of Florida) + * ---------------------------------------------------------------------- */ + +#include "pair_uf3.h" +#include "uf3_pair_bspline.h" +#include "uf3_triplet_bspline.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 "text_file_reader.h" + +#include + +using namespace LAMMPS_NS; +using namespace MathConst; + +PairUF3::PairUF3(LAMMPS *lmp) : Pair(lmp) +{ + single_enable = 1; // 1 if single() routine exists + restartinfo = 0; // 1 if pair style writes restart info + maxshort = 10; + neighshort = nullptr; + centroidstressflag = CENTROID_AVAIL; + manybody_flag = 1; + one_coeff = 0; //if 1 then allow only one coeff call of form 'pair_coeff * *' + //by setting it to 0 we will allow multiple 'pair_coeff' calls + bsplines_created = 0; +} + +PairUF3::~PairUF3() +{ + if (copymode) return; + if (allocated) { + memory->destroy(setflag); + memory->destroy(cutsq); + memory->destroy(cut); + + if (pot_3b) { + memory->destroy(setflag_3b); + memory->destroy(cut_3b); + memory->destroy(cut_3b_list); + memory->destroy(min_cut_3b); + memory->destroy(neighshort); + } + } +} + +/* ---------------------------------------------------------------------- + * global settings + * ---------------------------------------------------------------------- */ + +void PairUF3::settings(int narg, char **arg) +{ + + if (narg != 2) + error->all(FLERR, "UF3: Invalid number of argument in pair settings\n\ + Are you running 2-body or 2 & 3-body UF potential\n\ + Also how many elements?"); + nbody_flag = utils::numeric(FLERR, arg[0], true, lmp); + num_of_elements = utils::numeric(FLERR, arg[1], true, lmp); // atom->ntypes; + if (num_of_elements != atom->ntypes) { + if (comm->me == 0) + utils::logmesg(lmp, "\nUF3: Number of elements provided in the input file and \ +number of elements detected by lammps in the structure are not same\n\ + proceed with caution\n"); + } + if (nbody_flag == 2) { + pot_3b = false; + n2body_pot_files = num_of_elements * (num_of_elements + 1) / 2; + tot_pot_files = n2body_pot_files; + } else if (nbody_flag == 3) { + pot_3b = true; + n2body_pot_files = num_of_elements * (num_of_elements + 1) / 2; + n3body_pot_files = num_of_elements * (num_of_elements * (num_of_elements + 1) / 2); + tot_pot_files = n2body_pot_files + n3body_pot_files; + } else + error->all(FLERR, "UF3: UF3 not yet implemented for {}-body", nbody_flag); +} + +/* ---------------------------------------------------------------------- + * set coeffs for one or more type pairs + * ---------------------------------------------------------------------- */ +void PairUF3::coeff(int narg, char **arg) +{ + if (!allocated) allocate(); + + if (narg != 3 && narg != 5){ + /*error->warning(FLERR, "\nUF3: WARNING!! It seems that you are using the \n\ + older style of specifying UF3 POT files. This style of listing \n\ + all the potential files on a single line will be depcrecated in \n\ + the next version of ML-UF3");*/ + if (narg == tot_pot_files + 2) + error->all(FLERR, "UF3 The old style of listing all the potential files\n\ + on a single line is depcrecated"); + else + error->all(FLERR, "UF3: Invalid number of argument in pair coeff;\n\ + Provide the species number followed by the LAMMPS POT file\n\ + Eg. 'pair_coeff 1 1 POT_FILE' for 2-body and \n\ + 'pair_coeff 3b 1 2 2 POT_FILE' for 3-body."); + } + if (narg == 3 || narg == 5){ + int ilo, ihi, jlo, jhi, klo, khi; + if (narg == 3){ + utils::bounds(FLERR, arg[0], 1, atom->ntypes, ilo, ihi, error); + utils::bounds(FLERR, arg[1], 1, atom->ntypes, jlo, jhi, error); + } + + if (narg == 5){ + utils::bounds(FLERR, arg[1], 1, atom->ntypes, ilo, ihi, error); + utils::bounds(FLERR, arg[2], 1, atom->ntypes, jlo, jhi, error); + utils::bounds(FLERR, arg[3], 1, atom->ntypes, klo, khi, error); + } + + if (narg == 3){ + if (utils::strmatch(arg[0],".*\\*.*") || utils::strmatch(arg[1],".*\\*.*")){ + for (int i = ilo; i <= ihi; i++) { + for (int j = MAX(jlo, i); j <= jhi; j++) { + if (comm->me == 0) + utils::logmesg(lmp, "\nUF3: Opening {} file\n", arg[2]); + uf3_read_pot_file(i,j,arg[2]); + } + } + } + + else{ + int i = utils::inumeric(FLERR, arg[0], true, lmp); + int j = utils::inumeric(FLERR, arg[1], true, lmp); + if (comm->me == 0) + utils::logmesg(lmp, "\nUF3: Opening {} file\n", arg[2]); + uf3_read_pot_file(i,j,arg[2]); + } + } + + if (narg == 5){ + if (!utils::strmatch(arg[0],"3b")) + error->all(FLERR, "UF3: Invalid argument. For 3-body the first argument\n\ + argument to pair_coeff needs to be 3b.\n\ + Example pair_coeff 3b 1 2 2 A_B_B."); + if (utils::strmatch(arg[1],".*\\*.*") || utils::strmatch(arg[2],".*\\*.*") || utils::strmatch(arg[3],".*\\*.*")){ + for (int i = ilo; i <= ihi; i++) { + for (int j = jlo; j <= jhi; j++) { + for (int k = MAX(klo, jlo); k <= khi; k++) { + if (comm->me == 0) + utils::logmesg(lmp, "\nUF3: Opening {} file\n", arg[4]); + uf3_read_pot_file(i,j,k,arg[4]); + } + } + } + } + else{ + if (comm->me == 0) + utils::logmesg(lmp, "\nUF3: Opening {} file\n", arg[4]); + int i = utils::inumeric(FLERR, arg[1], true, lmp); + int j = utils::inumeric(FLERR, arg[2], true, lmp); + int k = utils::inumeric(FLERR, arg[3], true, lmp); + uf3_read_pot_file(i,j,k,arg[4]); + } + } + } + + /*else{ + if (narg != tot_pot_files + 2) + error->all(FLERR,"UF3: Invalid number of argument in pair coeff; \n\ + Number of potential files provided is not correct"); + + error->warning(FLERR, "\nUF3: WARNING!! It seems that you are using the \n\ + older style of specifying UF3 POT files. This style of listing \n\ + all the potential files on a single line will be depcrecated in \n\ + the next version of ML-UF3"); + + // open UF3 potential file on all proc + for (int i = 2; i < narg; i++) { uf3_read_pot_file(arg[i]); } + if (!bsplines_created) create_bsplines(); + + // setflag check needed here + for (int i = 1; i < num_of_elements + 1; i++) { + for (int j = 1; j < num_of_elements + 1; j++) { + if (setflag[i][j] != 1) + error->all(FLERR,"UF3: Not all 2-body UF potentials are set, \n\ + missing potential file for {}-{} interaction",i, j); + } + } + + if (pot_3b) { + for (int i = 1; i < num_of_elements + 1; i++) { + for (int j = 1; j < num_of_elements + 1; j++) { + for (int k = 1; k < num_of_elements + 1; k++) { + if (setflag_3b[i][j][k] != 1) + error->all(FLERR,"UF3: Not all 3-body UF potentials are set, \n\ + missing potential file for {}-{}-{} interaction", i, j, k); + } + } + } + } + for (int i = 1; i < num_of_elements + 1; i++) { + for (int j = i; j < num_of_elements + 1; j++) { + UFBS2b[i][j] = uf3_pair_bspline(lmp, n2b_knot[i][j], n2b_coeff[i][j]); + UFBS2b[j][i] = UFBS2b[i][j]; + } + if (pot_3b) { + for (int j = 1; j < num_of_elements + 1; j++) { + for (int k = j; k < num_of_elements + 1; k++) { + std::string key = std::to_string(i) + std::to_string(j) + std::to_string(k); + UFBS3b[i][j][k] = + uf3_triplet_bspline(lmp, n3b_knot_matrix[i][j][k], n3b_coeff_matrix[key]); + UFBS3b[i][k][j] = UFBS3b[i][j][k]; + } + } + } + } + }*/ +} + +void PairUF3::allocate() +{ + allocated = 1; + + // Contains info about wether UF potential were found for type i and j + memory->create(setflag, num_of_elements + 1, num_of_elements + 1, "pair:setflag"); + + // Contains info about 2-body cutoff distance for type i and j + // cutsq is the global variable + // Even though we are making cutsq don't manually change the default values + // Lammps take care of setting the value + memory->create(cutsq, num_of_elements + 1, num_of_elements + 1, "pair:cutsq"); + // cut is specific to this pair style. We will set the values in cut + memory->create(cut, num_of_elements + 1, num_of_elements + 1, "pair:cut"); + //Contains info about type of knot_spacing--> 0 = uniform knot spacing (default) + //1 = non-uniform knot spacing + memory->create(knot_spacing_type_2b, num_of_elements + 1, num_of_elements + 1, "pair:knot_spacing_2b"); + + // Contains knot_vect of 2-body potential for type i and j + n2b_knot.resize(num_of_elements + 1); + n2b_coeff.resize(num_of_elements + 1); + UFBS2b.resize(num_of_elements + 1); + for (int i = 1; i < num_of_elements + 1; i++) { + n2b_knot[i].resize(num_of_elements + 1); + n2b_coeff[i].resize(num_of_elements + 1); + UFBS2b[i].resize(num_of_elements + 1); + } + if (pot_3b) { + // Contains info about wether UF potential were found for type i, j and k + memory->create(setflag_3b, num_of_elements + 1, num_of_elements + 1, num_of_elements + 1, + "pair:setflag_3b"); + // Contains info about 3-body cutoff distance for type i, j and k + memory->create(cut_3b, num_of_elements + 1, num_of_elements + 1, num_of_elements + 1, + "pair:cut_3b"); + // Contains info about 3-body cutoff distance for type i, j and k + // for constructing 3-body list + memory->create(cut_3b_list, num_of_elements + 1, num_of_elements + 1, "pair:cut_3b_list"); + // Contains info about minimum 3-body cutoff distance for type i, j and k + memory->create(min_cut_3b, num_of_elements + 1, num_of_elements + 1, num_of_elements + 1, 3, + "pair:min_cut_3b"); + //Contains info about type of knot_spacing--> 0 = uniform knot spacing (default) + //1 = non-uniform knot spacing + memory->create(knot_spacing_type_3b, num_of_elements + 1, num_of_elements + 1, + num_of_elements + 1, "pair:knot_spacing_3b"); + + + // setting cut_3b and setflag = 0 + for (int i = 1; i < num_of_elements + 1; i++) { + for (int j = 1; j < num_of_elements + 1; j++) { + cut_3b_list[i][j] = 0; + for (int k = 1; k < num_of_elements + 1; k++) { + cut_3b[i][j][k] = 0; + min_cut_3b[i][j][k][0] = 0; + min_cut_3b[i][j][k][1] = 0; + min_cut_3b[i][j][k][2] = 0; + } + } + } + n3b_knot_matrix.resize(num_of_elements + 1); + UFBS3b.resize(num_of_elements + 1); + for (int i = 1; i < num_of_elements + 1; i++) { + n3b_knot_matrix[i].resize(num_of_elements + 1); + UFBS3b[i].resize(num_of_elements + 1); + for (int j = 1; j < num_of_elements + 1; j++) { + n3b_knot_matrix[i][j].resize(num_of_elements + 1); + UFBS3b[i][j].resize(num_of_elements + 1); + } + } + memory->create(neighshort, maxshort, "pair:neighshort"); + } +} + +void PairUF3::uf3_read_pot_file(int itype, int jtype, char *potf_name) +{ + utils::logmesg(lmp, "UF3: {} file should contain UF3 potential for {} {}\n", \ + potf_name, itype, jtype); + + if (!platform::file_is_readable(potf_name)) + error->all(FLERR, "UF3: {} file is not readable", potf_name); + + FILE *fp; + fp = utils::open_potential(potf_name, lmp, nullptr); + + TextFileReader txtfilereader(fp, "UF3:POTFP"); + txtfilereader.ignore_comments = false; + + std::string temp_line = txtfilereader.next_line(1); + Tokenizer file_header(temp_line); + + if (file_header.count() != 2) + error->all(FLERR, "UF3: Expected only two words on 1st line of {} but found \n\ + {} word/s",potf_name,file_header.count()); + + if (file_header.contains("#UF3 POT") == 0) + error->all(FLERR, "UF3: {} file is not UF3 POT type, 1st line of UF3 POT \n\ + files contain '#UF3 POT'. Found {} in the header",potf_name,temp_line); + + temp_line = txtfilereader.next_line(1); + ValueTokenizer fp2nd_line(temp_line); + + if (fp2nd_line.count() != 4) + error->all(FLERR, "UF3: Expected 4 words on 2nd line =>\n\ + nBody leading_trim trailing_trim type_of_knot_spacing\n\ + Found {}",temp_line); + + std::string nbody_on_file = fp2nd_line.next_string(); + if (utils::strmatch(nbody_on_file,"2B")) + utils::logmesg(lmp, "UF3: File {} contains 2-body UF3 potential\n",potf_name); + else + error->all(FLERR, "UF3: Expected a 2B UF3 file but found {}", + nbody_on_file); + + int leading_trim = fp2nd_line.next_int(); + int trailing_trim = fp2nd_line.next_int(); + if (leading_trim != 0) + error->all(FLERR, "UF3: Current implementation is throughly tested only for\n\ + leading_trim=0\n"); + if (trailing_trim != 3) + error->all(FLERR, "UF3: Current implementation is throughly tested only for\n\ + trailing_trim=3\n"); + + std::string knot_type = fp2nd_line.next_string(); + if (utils::strmatch(knot_type,"uk")){ + utils::logmesg(lmp, "UF3: File {} contains 2-body UF3 potential with uniform\n\ + knot spacing\n",potf_name); + knot_spacing_type_2b[itype][jtype] = 0; + knot_spacing_type_2b[jtype][itype] = 0; + } + else if (utils::strmatch(knot_type,"nk")){ + utils::logmesg(lmp, "UF3: File {} contains 2-body UF3 potential with non-uniform\n\ + knot spacing\n",potf_name); + knot_spacing_type_2b[itype][jtype] = 1; + knot_spacing_type_2b[jtype][itype] = 1; + /*error->all(FLERR, "UF3: Current implementation only works with uniform\n\ + knot spacing");*/ + } + else + error->all(FLERR, "UF3: Expected either 'uk'(uniform-knots) or 'nk'(non-uniform knots)\n\ + Found {} on the 2nd line of {} pot file",knot_type,potf_name); + + temp_line = txtfilereader.next_line(1); + ValueTokenizer fp3rd_line(temp_line); + if (fp3rd_line.count() != 2) + error->all(FLERR, "UF3: Expected only 2 numbers on 3rd line =>\n\ + Rij_CUTOFF NUM_OF_KNOTS\n\ + Found {} number/s",fp3rd_line.count()); + + //cut is used in init_one which is called by pair.cpp at line 267 where the return of init_one is squared + cut[itype][jtype] = fp3rd_line.next_double(); + cut[jtype][itype] = cut[itype][jtype]; + + int num_knots_2b = fp3rd_line.next_int(); + + temp_line = txtfilereader.next_line(num_knots_2b); + ValueTokenizer fp4th_line(temp_line); + + if (fp4th_line.count() != num_knots_2b) + error->all(FLERR, "UF3: Expected {} numbers on 4th line but found {} numbers", + num_knots_2b,fp4th_line.count()); + + n2b_knot[itype][jtype].resize(num_knots_2b); + n2b_knot[jtype][itype].resize(num_knots_2b); + for (int k = 0; k < num_knots_2b; k++) { + n2b_knot[itype][jtype][k] = fp4th_line.next_double(); + n2b_knot[jtype][itype][k] = n2b_knot[itype][jtype][k]; + } + + temp_line = txtfilereader.next_line(1); + ValueTokenizer fp5th_line(temp_line); + int num_of_coeff_2b = fp5th_line.next_int(); + + temp_line = txtfilereader.next_line(num_of_coeff_2b); + ValueTokenizer fp6th_line(temp_line); + + if (fp6th_line.count() != num_of_coeff_2b) + error->all(FLERR, "UF3: Expected {} numbers on 6th line but found {} numbers", + num_of_coeff_2b, fp6th_line.count()); + + n2b_coeff[itype][jtype].resize(num_of_coeff_2b); + n2b_coeff[jtype][itype].resize(num_of_coeff_2b); + for (int k = 0; k < num_of_coeff_2b; k++) { + n2b_coeff[itype][jtype][k] = fp6th_line.next_double(); + n2b_coeff[jtype][itype][k] = n2b_coeff[itype][jtype][k]; + } + + if (n2b_knot[itype][jtype].size() != n2b_coeff[itype][jtype].size() + 4) { + error->all(FLERR, "UF3: {} has incorrect knot and coeff data nknots!=ncoeffs + 3 +1", + potf_name); + } + setflag[itype][jtype] = 1; + setflag[jtype][itype] = 1; +} + + +void PairUF3::uf3_read_pot_file(int itype, int jtype, int ktype, char *potf_name) +{ + utils::logmesg(lmp, "UF3: {} file should contain UF3 potential for {} {} {}\n", + potf_name, itype, jtype, ktype); + + if (!platform::file_is_readable(potf_name)) + error->all(FLERR, "UF3: {} file is not readable", potf_name); + + FILE *fp; + fp = utils::open_potential(potf_name, lmp, nullptr); + + TextFileReader txtfilereader(fp, "UF3:POTFP"); + txtfilereader.ignore_comments = false; + + std::string temp_line = txtfilereader.next_line(1); + Tokenizer file_header(temp_line); + + if (file_header.count() != 2) + error->all(FLERR, "UF3: Expected only two words on 1st line of {} but found \n\ + {} word/s",potf_name,file_header.count()); + + if (file_header.contains("#UF3 POT") == 0) + error->all(FLERR, "UF3: {} file is not UF3 POT type, 1st line of UF3 POT \n\ + files contain '#UF3 POT'. Found {} in the header",potf_name,temp_line); + + temp_line = txtfilereader.next_line(1); + ValueTokenizer fp2nd_line(temp_line); + + if (fp2nd_line.count() != 4) + error->all(FLERR, "UF3: Expected 3 words on 2nd line =>\n\ + nBody leading_trim trailing_trim type_of_knot_spacing\n\ + Found {}",temp_line); + + std::string nbody_on_file = fp2nd_line.next_string(); + + if (utils::strmatch(nbody_on_file,"3B")) + utils::logmesg(lmp, "UF3: File {} contains 3-body UF3 potential\n",potf_name); + else + error->all(FLERR, "UF3: Expected a 3B UF3 file but found {}", + nbody_on_file); + + int leading_trim = fp2nd_line.next_int(); + int trailing_trim = fp2nd_line.next_int(); + if (leading_trim != 0) + error->all(FLERR, "UF3: Current implementation is throughly tested only for\n\ + leading_trim=0\n"); + if (trailing_trim != 3) + error->all(FLERR, "UF3: Current implementation is throughly tested only for\n\ + trailing_trim=3\n"); + + std::string knot_type = fp2nd_line.next_string(); + if (utils::strmatch(knot_type,"uk")){ + utils::logmesg(lmp, "UF3: File {} contains 3-body UF3 potential with uniform\n\ + knot spacing\n",potf_name); + knot_spacing_type_3b[itype][jtype][ktype] = 0; + knot_spacing_type_3b[itype][ktype][jtype] = 0; + } + else if (utils::strmatch(knot_type,"nk")){ + utils::logmesg(lmp, "UF3: File {} contains 3-body UF3 potential with non-uniform\n\ + knot spacing\n",potf_name); + knot_spacing_type_3b[itype][jtype][ktype] = 1; + knot_spacing_type_3b[itype][ktype][jtype] = 1; + /*error->all(FLERR, "UF3: Current implementation only works with uniform\n\ + knot spacing");*/ + } + else + error->all(FLERR, "UF3: Expected either 'uk'(uniform-knots) or 'nk'(non-uniform knots)\n\ + Found {} on the 2nd line of {} pot file",knot_type,potf_name); + + temp_line = txtfilereader.next_line(6); + ValueTokenizer fp3rd_line(temp_line); + + if (fp3rd_line.count() != 6) + error->all(FLERR, "UF3: Expected only 6 numbers on 3rd line =>\n\ + Rjk_CUTOFF Rik_CUTOFF Rij_CUTOFF NUM_OF_KNOTS_JK NUM_OF_KNOTS_IK NUM_OF_KNOTS_IJ\n\ + Found {} number/s",fp3rd_line.count()); + + double cut3b_rjk = fp3rd_line.next_double(); + double cut3b_rij = fp3rd_line.next_double(); + double cut3b_rik = fp3rd_line.next_double(); + + if (cut3b_rij != cut3b_rik) { + error->all(FLERR, "UF3: rij!=rik, Current implementation only works for rij=rik"); + } + + if (2 * cut3b_rik != cut3b_rjk) { + error->all(FLERR, "UF3: 2rij=2rik!=rik, Current implementation only works \n\ + for 2rij=2rik!=rik"); + } + + cut_3b_list[itype][jtype] = std::max(cut3b_rij, cut_3b_list[itype][jtype]); + cut_3b_list[itype][ktype] = std::max(cut_3b_list[itype][ktype], cut3b_rik); + + cut_3b[itype][jtype][ktype] = cut3b_rij; + cut_3b[itype][ktype][jtype] = cut3b_rik; + + int num_knots_3b_jk = fp3rd_line.next_int(); + temp_line = txtfilereader.next_line(num_knots_3b_jk); + ValueTokenizer fp4th_line(temp_line); + + if (fp4th_line.count() != num_knots_3b_jk) + error->all(FLERR, "UF3: Expected {} numbers on 4th line but found {} numbers", + num_knots_3b_jk, fp4th_line.count()); + + + n3b_knot_matrix[itype][jtype][ktype].resize(3); + n3b_knot_matrix[itype][ktype][jtype].resize(3); + + n3b_knot_matrix[itype][jtype][ktype][0].resize(num_knots_3b_jk); + n3b_knot_matrix[itype][ktype][jtype][0].resize(num_knots_3b_jk); + + for (int i = 0; i < num_knots_3b_jk; i++) { + n3b_knot_matrix[itype][jtype][ktype][0][i] = fp4th_line.next_double(); + n3b_knot_matrix[itype][ktype][jtype][0][i] = + n3b_knot_matrix[itype][jtype][ktype][0][i]; + } + + min_cut_3b[itype][jtype][ktype][0] = n3b_knot_matrix[itype][jtype][ktype][0][0]; + //min_cut_3b[itype][jtype][ktype][0] --> cutoff for jk distance + + min_cut_3b[itype][ktype][jtype][0] = n3b_knot_matrix[itype][ktype][jtype][0][0]; + if (comm->me == 0) + utils::logmesg(lmp, "UF3: 3b min cutoff {} {}-{}-{}_jk={} {}-{}-{}_jk={}\n", + potf_name,itype,jtype,ktype,min_cut_3b[itype][jtype][ktype][0], + itype,ktype,jtype,min_cut_3b[itype][ktype][jtype][0]); + + int num_knots_3b_ik = fp3rd_line.next_int(); + temp_line = txtfilereader.next_line(num_knots_3b_ik); + ValueTokenizer fp5th_line(temp_line); + + if (fp5th_line.count() != num_knots_3b_ik) + error->all(FLERR, "UF3: Expected {} numbers on 5th line but found {} numbers", + num_knots_3b_ik, fp5th_line.count()); + + n3b_knot_matrix[itype][jtype][ktype][1].resize(num_knots_3b_ik); + n3b_knot_matrix[itype][ktype][jtype][2].resize(num_knots_3b_ik); + for (int i = 0; i < num_knots_3b_ik; i++) { + n3b_knot_matrix[itype][jtype][ktype][1][i] = fp5th_line.next_double(); + n3b_knot_matrix[itype][ktype][jtype][2][i] = + n3b_knot_matrix[itype][jtype][ktype][1][i]; + } + + min_cut_3b[itype][jtype][ktype][1] = n3b_knot_matrix[itype][jtype][ktype][1][0]; + //min_cut_3b[itype][jtype][ktype][1] --> cutoff for ik distance + + min_cut_3b[itype][ktype][jtype][2] = n3b_knot_matrix[itype][ktype][jtype][2][0]; + if (comm->me == 0) + utils::logmesg(lmp, "UF3: 3b min cutoff {} {}-{}-{}_ik={} {}-{}-{}_ik={}\n", + potf_name,itype,jtype,ktype,min_cut_3b[itype][jtype][ktype][1], + itype,ktype,jtype,min_cut_3b[itype][ktype][jtype][2]); + + int num_knots_3b_ij = fp3rd_line.next_int(); + temp_line = txtfilereader.next_line(num_knots_3b_ij); + ValueTokenizer fp6th_line(temp_line); + + if (fp6th_line.count() != num_knots_3b_ij) + error->all(FLERR, "UF3: Expected {} numbers on 6th line but found {} numbers", + num_knots_3b_ij, fp5th_line.count()); + + n3b_knot_matrix[itype][jtype][ktype][2].resize(num_knots_3b_ij); + n3b_knot_matrix[itype][ktype][jtype][1].resize(num_knots_3b_ij); + for (int i = 0; i < num_knots_3b_ij; i++) { + n3b_knot_matrix[itype][jtype][ktype][2][i] = fp6th_line.next_double(); + n3b_knot_matrix[itype][ktype][jtype][1][i] = + n3b_knot_matrix[itype][jtype][ktype][2][i]; + } + + min_cut_3b[itype][jtype][ktype][2] = n3b_knot_matrix[itype][jtype][ktype][2][0]; + //min_cut_3b[itype][jtype][ktype][2] --> cutoff for ij distance + min_cut_3b[itype][ktype][jtype][1] = n3b_knot_matrix[itype][ktype][jtype][1][0]; + if (comm->me == 0) + utils::logmesg(lmp, "UF3: 3b min cutoff {} {}-{}-{}_ij={} {}-{}-{}_ij={}\n", + potf_name,itype,jtype,ktype,min_cut_3b[itype][jtype][ktype][2], + itype,ktype,jtype,min_cut_3b[itype][ktype][jtype][1]); + + temp_line = txtfilereader.next_line(3); + ValueTokenizer fp7th_line(temp_line); + + if (fp7th_line.count() != 3) + error->all(FLERR, "UF3: Expected 3 numbers on 7th line =>\n\ + SHAPE_OF_COEFF_MATRIX[I][J][K] \n\ + found {} numbers", fp7th_line.count()); + + coeff_matrix_dim1 = fp7th_line.next_int(); + coeff_matrix_dim2 = fp7th_line.next_int(); + coeff_matrix_dim3 = fp7th_line.next_int(); + + if (n3b_knot_matrix[itype][jtype][ktype][0].size() != coeff_matrix_dim3 + 3 + 1) + error->all(FLERR, "UF3: {} has incorrect knot (NUM_OF_KNOTS_JK) and \n\ + coeff (coeff_matrix_dim3) data nknots!=ncoeffs + 3 +1", potf_name); + + if (n3b_knot_matrix[itype][jtype][ktype][1].size() != coeff_matrix_dim2 + 3 + 1) + error->all(FLERR, "UF3: {} has incorrect knot (NUM_OF_KNOTS_IK) and \n\ + coeff (coeff_matrix_dim2) data nknots!=ncoeffs + 3 +1",potf_name); + + if (n3b_knot_matrix[itype][jtype][ktype][2].size() != coeff_matrix_dim1 + 3 + 1) + error->all(FLERR, "UF3: {} has incorrect knot (NUM_OF_KNOTS_IJ) and \n\ + coeff ()coeff_matrix_dim1 data nknots!=ncoeffs + 3 +1",potf_name); + + coeff_matrix_elements_len = coeff_matrix_dim3; + + std::string key = std::to_string(itype) + std::to_string(jtype) + std::to_string(ktype); + n3b_coeff_matrix[key].resize(coeff_matrix_dim1); + + int line_count = 0; + for (int i = 0; i < coeff_matrix_dim1; i++) { + n3b_coeff_matrix[key][i].resize(coeff_matrix_dim2); + for (int j = 0; j < coeff_matrix_dim2; j++) { + temp_line = txtfilereader.next_line(coeff_matrix_elements_len); + ValueTokenizer coeff_line(temp_line); + n3b_coeff_matrix[key][i][j].resize(coeff_matrix_dim3); + + if (coeff_line.count() != coeff_matrix_elements_len) + error->all(FLERR, "UF3: Expected {} numbers on {}th line but found \n\ + {} numbers",coeff_matrix_elements_len, line_count+8, coeff_line.count()); + for (int k = 0; k < coeff_matrix_dim3; k++) { + n3b_coeff_matrix[key][i][j][k] = coeff_line.next_double(); + } + line_count += 1; + } + } + + std::string key2 = std::to_string(itype) + std::to_string(ktype) + std::to_string(jtype); + n3b_coeff_matrix[key2].resize(coeff_matrix_dim2); + for (int j = 0; j < coeff_matrix_dim2; j++) { + n3b_coeff_matrix[key2][j].resize(coeff_matrix_dim1); + for (int i = 0; i < coeff_matrix_dim1; i++) { + n3b_coeff_matrix[key2][j][i].resize(coeff_matrix_dim3); + } + } + + for (int i = 0; i < coeff_matrix_dim1; i++) { + for (int j = 0; j < coeff_matrix_dim2; j++) { + for (int k = 0; k < coeff_matrix_dim3; k++) { + n3b_coeff_matrix[key2][j][i][k] = n3b_coeff_matrix[key][i][j][k]; + } + } + } + + setflag_3b[itype][jtype][ktype] = 1; + setflag_3b[itype][ktype][jtype] = 1; + +} + +void PairUF3::uf3_read_pot_file(char *potf_name) +{ + if (comm->me == 0) utils::logmesg(lmp, "\nUF3: Opening {} file\n", potf_name); + + FILE *fp; + fp = utils::open_potential(potf_name, lmp, nullptr); + // if (fp) error->all(FLERR,"UF3: {} file not found",potf_name); + + TextFileReader txtfilereader(fp, "UF3:POTFP"); + txtfilereader.ignore_comments = false; + + std::string temp_line = txtfilereader.next_line(2); + Tokenizer fp1st_line(temp_line); + + if (fp1st_line.contains("#UF3 POT") == 0) + error->all(FLERR, "UF3: {} file is not UF3 POT type, found type {} {} on the file", potf_name, + fp1st_line.next(), fp1st_line.next()); + + if (comm->me == 0) + utils::logmesg(lmp, "UF3: {} file is of type {} {}\n", potf_name, fp1st_line.next(), + fp1st_line.next()); + + temp_line = txtfilereader.next_line(1); + Tokenizer fp2nd_line(temp_line); + if (fp2nd_line.contains("2B") == 1) { + temp_line = txtfilereader.next_line(4); + ValueTokenizer fp3rd_line(temp_line); + int temp_type1 = fp3rd_line.next_int(); + int temp_type2 = fp3rd_line.next_int(); + if (comm->me == 0) + utils::logmesg(lmp, "UF3: {} file contains 2-body UF3 potential for {} {}\n", potf_name, + temp_type1, temp_type2); + + //cut is used in init_one which is called by pair.cpp at line 267 where the return of init_one is squared + cut[temp_type1][temp_type2] = fp3rd_line.next_double(); + // if(comm->me==0) utils::logmesg(lmp,"UF3: Cutoff {}\n",cutsq[temp_type1][temp_type2]); + cut[temp_type2][temp_type1] = cut[temp_type1][temp_type2]; + + int temp_line_len = fp3rd_line.next_int(); + + temp_line = txtfilereader.next_line(temp_line_len); + ValueTokenizer fp4th_line(temp_line); + + n2b_knot[temp_type1][temp_type2].resize(temp_line_len); + n2b_knot[temp_type2][temp_type1].resize(temp_line_len); + for (int k = 0; k < temp_line_len; k++) { + n2b_knot[temp_type1][temp_type2][k] = fp4th_line.next_double(); + n2b_knot[temp_type2][temp_type1][k] = n2b_knot[temp_type1][temp_type2][k]; + } + + temp_line = txtfilereader.next_line(1); + ValueTokenizer fp5th_line(temp_line); + + temp_line_len = fp5th_line.next_int(); + + temp_line = txtfilereader.next_line(temp_line_len); + // utils::logmesg(lmp,"UF3:11 {}",temp_line); + ValueTokenizer fp6th_line(temp_line); + // if(comm->me==0) utils::logmesg(lmp,"UF3: {}\n",temp_line_len); + n2b_coeff[temp_type1][temp_type2].resize(temp_line_len); + n2b_coeff[temp_type2][temp_type1].resize(temp_line_len); + + for (int k = 0; k < temp_line_len; k++) { + n2b_coeff[temp_type1][temp_type2][k] = fp6th_line.next_double(); + n2b_coeff[temp_type2][temp_type1][k] = n2b_coeff[temp_type1][temp_type2][k]; + // if(comm->me==0) utils::logmesg(lmp,"UF3: {}\n",n2b_coeff[temp_type1][temp_type2][k]); + } + // for(int i=0;ime==0) utils::logmesg(lmp,"UF3: {}\n",n2b_coeff[temp_type1][temp_type2][i]); + if (n2b_knot[temp_type1][temp_type2].size() != n2b_coeff[temp_type1][temp_type2].size() + 4) { + error->all(FLERR, "UF3: {} has incorrect knot and coeff data nknots!=ncoeffs + 3 +1", + potf_name); + } + setflag[temp_type1][temp_type2] = 1; + setflag[temp_type2][temp_type1] = 1; + } else if (fp2nd_line.contains("3B") == 1) { + temp_line = txtfilereader.next_line(9); + ValueTokenizer fp3rd_line(temp_line); + int temp_type1 = fp3rd_line.next_int(); + int temp_type2 = fp3rd_line.next_int(); + int temp_type3 = fp3rd_line.next_int(); + if (comm->me == 0) + utils::logmesg(lmp, "UF3: {} file contains 3-body UF3 potential for {} {} {}\n", potf_name, + temp_type1, temp_type2, temp_type3); + + double cut3b_rjk = fp3rd_line.next_double(); + double cut3b_rij = fp3rd_line.next_double(); + // cut_3b[temp_type1][temp_type2] = std::max(cut3b_rij, + // cut_3b[temp_type1][temp_type2]); + cut_3b_list[temp_type1][temp_type2] = std::max(cut3b_rij, cut_3b_list[temp_type1][temp_type2]); + double cut3b_rik = fp3rd_line.next_double(); + if (cut3b_rij != cut3b_rik) { + error->all(FLERR, "UF3: rij!=rik, Current implementation only works for rij=rik"); + } + if (2 * cut3b_rik != cut3b_rjk) { + error->all(FLERR, + "UF3: 2rij=2rik!=rik, Current implementation only works for 2rij=2rik!=rik"); + } + // cut_3b[temp_type1][temp_type3] = std::max(cut_3b[temp_type1][temp_type3],cut3b_rik); + cut_3b_list[temp_type1][temp_type3] = std::max(cut_3b_list[temp_type1][temp_type3], cut3b_rik); + + cut_3b[temp_type1][temp_type2][temp_type3] = cut3b_rij; + cut_3b[temp_type1][temp_type3][temp_type2] = cut3b_rik; + + int temp_line_len = fp3rd_line.next_int(); + temp_line = txtfilereader.next_line(temp_line_len); + ValueTokenizer fp4th_line(temp_line); + + n3b_knot_matrix[temp_type1][temp_type2][temp_type3].resize(3); + n3b_knot_matrix[temp_type1][temp_type3][temp_type2].resize(3); + + n3b_knot_matrix[temp_type1][temp_type2][temp_type3][0].resize(temp_line_len); + n3b_knot_matrix[temp_type1][temp_type3][temp_type2][0].resize(temp_line_len); + for (int i = 0; i < temp_line_len; i++) { + n3b_knot_matrix[temp_type1][temp_type2][temp_type3][0][i] = fp4th_line.next_double(); + n3b_knot_matrix[temp_type1][temp_type3][temp_type2][0][i] = + n3b_knot_matrix[temp_type1][temp_type2][temp_type3][0][i]; + } + + min_cut_3b[temp_type1][temp_type2][temp_type3][0] = n3b_knot_matrix[temp_type1][temp_type2][temp_type3][0][0]; + min_cut_3b[temp_type1][temp_type3][temp_type2][0] = n3b_knot_matrix[temp_type1][temp_type3][temp_type2][0][0]; + if (comm->me == 0) + utils::logmesg(lmp, "UF3: 3b min cutoff {} {}-{}-{}_0={} {}-{}-{}_0={}\n", + potf_name,temp_type1,temp_type2,temp_type3,min_cut_3b[temp_type1][temp_type2][temp_type3][0], + temp_type1,temp_type3,temp_type2,min_cut_3b[temp_type1][temp_type3][temp_type2][0]); + + temp_line_len = fp3rd_line.next_int(); + temp_line = txtfilereader.next_line(temp_line_len); + ValueTokenizer fp5th_line(temp_line); + n3b_knot_matrix[temp_type1][temp_type2][temp_type3][1].resize(temp_line_len); + n3b_knot_matrix[temp_type1][temp_type3][temp_type2][2].resize(temp_line_len); + for (int i = 0; i < temp_line_len; i++) { + n3b_knot_matrix[temp_type1][temp_type2][temp_type3][1][i] = fp5th_line.next_double(); + n3b_knot_matrix[temp_type1][temp_type3][temp_type2][2][i] = + n3b_knot_matrix[temp_type1][temp_type2][temp_type3][1][i]; + } + + min_cut_3b[temp_type1][temp_type2][temp_type3][1] = n3b_knot_matrix[temp_type1][temp_type2][temp_type3][1][0]; + min_cut_3b[temp_type1][temp_type3][temp_type2][2] = n3b_knot_matrix[temp_type1][temp_type3][temp_type2][2][0]; + if (comm->me == 0) + utils::logmesg(lmp, "UF3: 3b min cutoff {} {}-{}-{}_1={} {}-{}-{}_2={}\n", + potf_name,temp_type1,temp_type2,temp_type3,min_cut_3b[temp_type1][temp_type2][temp_type3][1], + temp_type1,temp_type3,temp_type2,min_cut_3b[temp_type1][temp_type3][temp_type2][2]); + + temp_line_len = fp3rd_line.next_int(); + temp_line = txtfilereader.next_line(temp_line_len); + ValueTokenizer fp6th_line(temp_line); + n3b_knot_matrix[temp_type1][temp_type2][temp_type3][2].resize(temp_line_len); + n3b_knot_matrix[temp_type1][temp_type3][temp_type2][1].resize(temp_line_len); + for (int i = 0; i < temp_line_len; i++) { + n3b_knot_matrix[temp_type1][temp_type2][temp_type3][2][i] = fp6th_line.next_double(); + n3b_knot_matrix[temp_type1][temp_type3][temp_type2][1][i] = + n3b_knot_matrix[temp_type1][temp_type2][temp_type3][2][i]; + } + + min_cut_3b[temp_type1][temp_type2][temp_type3][2] = n3b_knot_matrix[temp_type1][temp_type2][temp_type3][2][0]; + min_cut_3b[temp_type1][temp_type3][temp_type2][1] = n3b_knot_matrix[temp_type1][temp_type3][temp_type2][1][0]; + if (comm->me == 0) + utils::logmesg(lmp, "UF3: 3b min cutoff {} {}-{}-{}_2={} {}-{}-{}_1={}\n", + potf_name,temp_type1,temp_type2,temp_type3,min_cut_3b[temp_type1][temp_type2][temp_type3][2], + temp_type1,temp_type3,temp_type2,min_cut_3b[temp_type1][temp_type3][temp_type2][2]); + + temp_line = txtfilereader.next_line(3); + ValueTokenizer fp7th_line(temp_line); + + coeff_matrix_dim1 = fp7th_line.next_int(); + coeff_matrix_dim2 = fp7th_line.next_int(); + coeff_matrix_dim3 = fp7th_line.next_int(); + if (n3b_knot_matrix[temp_type1][temp_type2][temp_type3][0].size() != + coeff_matrix_dim3 + 3 + 1) { + error->all(FLERR, "UF3: {} has incorrect knot and coeff data nknots!=ncoeffs + 3 +1", + potf_name); + } + if (n3b_knot_matrix[temp_type1][temp_type2][temp_type3][1].size() != + coeff_matrix_dim2 + 3 + 1) { + error->all(FLERR, "UF3: {} has incorrect knot and coeff data nknots!=ncoeffs + 3 +1", + potf_name); + } + if (n3b_knot_matrix[temp_type1][temp_type2][temp_type3][2].size() != + coeff_matrix_dim1 + 3 + 1) { + error->all(FLERR, "UF3: {} has incorrect knot and coeff data nknots!=ncoeffs + 3 +1", + potf_name); + } + + coeff_matrix_elements_len = coeff_matrix_dim3; + + std::string key = + std::to_string(temp_type1) + std::to_string(temp_type2) + std::to_string(temp_type3); + n3b_coeff_matrix[key].resize(coeff_matrix_dim1); + for (int i = 0; i < coeff_matrix_dim1; i++) { + n3b_coeff_matrix[key][i].resize(coeff_matrix_dim2); + for (int j = 0; j < coeff_matrix_dim2; j++) { + temp_line = txtfilereader.next_line(coeff_matrix_elements_len); + ValueTokenizer coeff_line(temp_line); + n3b_coeff_matrix[key][i][j].resize(coeff_matrix_dim3); + for (int k = 0; k < coeff_matrix_dim3; k++) { + n3b_coeff_matrix[key][i][j][k] = coeff_line.next_double(); + } + } + } + + key = std::to_string(temp_type1) + std::to_string(temp_type3) + std::to_string(temp_type2); + n3b_coeff_matrix[key] = + n3b_coeff_matrix[std::to_string(temp_type1) + std::to_string(temp_type2) + + std::to_string(temp_type3)]; + setflag_3b[temp_type1][temp_type2][temp_type3] = 1; + setflag_3b[temp_type1][temp_type3][temp_type2] = 1; + } else + error->all( + FLERR, + "UF3: {} file does not contain right words indicating whether it is 2 or 3 body potential", + potf_name); +} + +/* ---------------------------------------------------------------------- + init specific to this pair style +------------------------------------------------------------------------- */ +void PairUF3::init_style() +{ + if (force->newton_pair == 0) error->all(FLERR, "UF3: Pair style requires newton pair on"); + // request a default neighbor list + neighbor->add_request(this, NeighConst::REQ_FULL); +} + +/* ---------------------------------------------------------------------- + init list sets the pointer to full neighbour list requested in previous function +------------------------------------------------------------------------- */ + +void PairUF3::init_list(int /*id*/, class NeighList *ptr) +{ + list = ptr; +} + +/* ---------------------------------------------------------------------- + init for one type pair i,j and corresponding j,i +------------------------------------------------------------------------- */ +double PairUF3::init_one(int i /*i*/, int /*j*/ j) +{ + + if (!bsplines_created) create_bsplines(); + + //init_one is called by pair.cpp at line 267 where it is squred + //at line 268 + return cut[i][j]; +} + +void PairUF3::create_bsplines() +{ + bsplines_created = 1; + for (int i = 1; i < num_of_elements + 1; i++) { + for (int j = 1; j < num_of_elements + 1; j++) { + if (setflag[i][j] != 1) + error->all(FLERR,"UF3: Not all 2-body UF potentials are set, \n\ + missing potential file for {}-{} interaction",i, j); + } + } + if (pot_3b) { + for (int i = 1; i < num_of_elements + 1; i++) { + for (int j = 1; j < num_of_elements + 1; j++) { + for (int k = 1; k < num_of_elements + 1; k++) { + if (setflag_3b[i][j][k] != 1) + error->all(FLERR,"UF3: Not all 3-body UF potentials are set, \n\ + missing potential file for {}-{}-{} interaction", i, j, k); + } + } + } + } + + for (int i = 1; i < num_of_elements + 1; i++) { + for (int j = i; j < num_of_elements + 1; j++) { + UFBS2b[i][j] = uf3_pair_bspline(lmp, n2b_knot[i][j], n2b_coeff[i][j], + knot_spacing_type_2b[i][j]); + UFBS2b[j][i] = UFBS2b[i][j]; + } + if (pot_3b) { + for (int j = 1; j < num_of_elements + 1; j++) { + for (int k = j; k < num_of_elements + 1; k++) { + std::string key = std::to_string(i) + std::to_string(j) + std::to_string(k); + UFBS3b[i][j][k] = + uf3_triplet_bspline(lmp, n3b_knot_matrix[i][j][k], n3b_coeff_matrix[key], + knot_spacing_type_3b[i][j][k]); + std::string key2 = std::to_string(i) + std::to_string(k) + std::to_string(j); + UFBS3b[i][k][j] = + uf3_triplet_bspline(lmp, n3b_knot_matrix[i][k][j], n3b_coeff_matrix[key2], + knot_spacing_type_3b[i][k][j]); + } + } + } + } +} + +void PairUF3::compute(int eflag, int vflag) +{ + int i, j, k, ii, jj, kk, inum, jnum, itype, jtype, ktype; + double xtmp, ytmp, ztmp, delx, dely, delz, evdwl, fpair, fx, fy, fz; + double del_rji[3], del_rki[3], del_rkj[3]; + double fij[3], fik[3], fjk[3]; + double fji[3], fki[3], fkj[3]; + double Fi[3], Fj[3], Fk[3]; + double rsq, rij, rik, rjk; + int *ilist, *jlist, *numneigh, **firstneigh; + + ev_init(eflag, vflag); + + double **x = atom->x; + double **f = atom->f; + int *type = atom->type; + int nlocal = atom->nlocal; + int newton_pair = force->newton_pair; + + inum = list->inum; + ilist = list->ilist; + numneigh = list->numneigh; + firstneigh = list->firstneigh; + // loop over neighbors of my atoms + for (ii = 0; ii < inum; ii++) { + evdwl = 0; + i = ilist[ii]; + xtmp = x[i][0]; + ytmp = x[i][1]; + ztmp = x[i][2]; + itype = type[i]; + jlist = firstneigh[i]; + jnum = numneigh[i]; + int numshort = 0; + for (jj = 0; jj < jnum; jj++) { + fx = 0; + fy = 0; + fz = 0; + j = jlist[jj]; + j &= NEIGHMASK; + + delx = xtmp - x[j][0]; + dely = ytmp - x[j][1]; + delz = ztmp - x[j][2]; + + rsq = delx * delx + dely * dely + delz * delz; + jtype = type[j]; + if (rsq < cutsq[itype][jtype]) { + rij = sqrt(rsq); + + if (pot_3b) { + if (rij <= cut_3b_list[itype][jtype]) { + neighshort[numshort] = j; + if (numshort >= maxshort - 1) { + maxshort += maxshort / 2; + memory->grow(neighshort, maxshort, "pair:neighshort"); + } + numshort = numshort + 1; + } + } + + double *pair_eval = UFBS2b[itype][jtype].eval(rij); + + fpair = -1 * pair_eval[1] / rij; + + fx = delx * fpair; + fy = dely * fpair; + fz = delz * fpair; + + f[i][0] += fx; + f[i][1] += fy; + f[i][2] += fz; + f[j][0] -= fx; + f[j][1] -= fy; + f[j][2] -= fz; + + if (eflag) evdwl = pair_eval[0]; + + if (evflag) { + ev_tally_xyz(i, j, nlocal, newton_pair, evdwl, 0.0, fx, fy, fz, delx, dely, delz); + + // Centroid Stress + if (vflag_either && cvflag_atom) { + double v[6]; + + v[0] = delx * fx; + v[1] = dely * fy; + v[2] = delz * fz; + v[3] = delx * fy; + v[4] = delx * fz; + v[5] = dely * fz; + + cvatom[i][0] += 0.5 * v[0]; + cvatom[i][1] += 0.5 * v[1]; + cvatom[i][2] += 0.5 * v[2]; + cvatom[i][3] += 0.5 * v[3]; + cvatom[i][4] += 0.5 * v[4]; + cvatom[i][5] += 0.5 * v[5]; + cvatom[i][6] += 0.5 * v[3]; + cvatom[i][7] += 0.5 * v[4]; + cvatom[i][8] += 0.5 * v[5]; + + cvatom[j][0] += 0.5 * v[0]; + cvatom[j][1] += 0.5 * v[1]; + cvatom[j][2] += 0.5 * v[2]; + cvatom[j][3] += 0.5 * v[3]; + cvatom[j][4] += 0.5 * v[4]; + cvatom[j][5] += 0.5 * v[5]; + cvatom[j][6] += 0.5 * v[3]; + cvatom[j][7] += 0.5 * v[4]; + cvatom[j][8] += 0.5 * v[5]; + } + } + } + } + + // 3-body interaction + // jth atom + jnum = numshort - 1; + for (jj = 0; jj < jnum; jj++) { + fij[0] = fji[0] = 0; + fij[1] = fji[1] = 0; + fij[2] = fji[2] = 0; + j = neighshort[jj]; + jtype = type[j]; + del_rji[0] = x[j][0] - xtmp; + del_rji[1] = x[j][1] - ytmp; + del_rji[2] = x[j][2] - ztmp; + rij = + sqrt(((del_rji[0] * del_rji[0]) + (del_rji[1] * del_rji[1]) + (del_rji[2] * del_rji[2]))); + + // kth atom + for (kk = jj + 1; kk < numshort; kk++) { + + fik[0] = fki[0] = 0; + fik[1] = fki[1] = 0; + fik[2] = fki[2] = 0; + + fjk[0] = fkj[0] = 0; + fjk[1] = fkj[1] = 0; + fjk[2] = fkj[2] = 0; + + k = neighshort[kk]; + ktype = type[k]; + del_rki[0] = x[k][0] - xtmp; + del_rki[1] = x[k][1] - ytmp; + del_rki[2] = x[k][2] - ztmp; + rik = sqrt( + ((del_rki[0] * del_rki[0]) + (del_rki[1] * del_rki[1]) + (del_rki[2] * del_rki[2]))); + + if ((rij <= cut_3b[itype][jtype][ktype]) && (rik <= cut_3b[itype][ktype][jtype]) && + (rij >= min_cut_3b[itype][jtype][ktype][2]) && + (rik >= min_cut_3b[itype][jtype][ktype][1])) { + + del_rkj[0] = x[k][0] - x[j][0]; + del_rkj[1] = x[k][1] - x[j][1]; + del_rkj[2] = x[k][2] - x[j][2]; + rjk = sqrt( + ((del_rkj[0] * del_rkj[0]) + (del_rkj[1] * del_rkj[1]) + (del_rkj[2] * del_rkj[2]))); + + if (rjk >= min_cut_3b[itype][jtype][ktype][0]){ + double *triangle_eval = UFBS3b[itype][jtype][ktype].eval(rij, rik, rjk); + + fij[0] = *(triangle_eval + 1) * (del_rji[0] / rij); + fji[0] = -fij[0]; + fik[0] = *(triangle_eval + 2) * (del_rki[0] / rik); + fki[0] = -fik[0]; + fjk[0] = *(triangle_eval + 3) * (del_rkj[0] / rjk); + fkj[0] = -fjk[0]; + + fij[1] = *(triangle_eval + 1) * (del_rji[1] / rij); + fji[1] = -fij[1]; + fik[1] = *(triangle_eval + 2) * (del_rki[1] / rik); + fki[1] = -fik[1]; + fjk[1] = *(triangle_eval + 3) * (del_rkj[1] / rjk); + fkj[1] = -fjk[1]; + + fij[2] = *(triangle_eval + 1) * (del_rji[2] / rij); + fji[2] = -fij[2]; + fik[2] = *(triangle_eval + 2) * (del_rki[2] / rik); + fki[2] = -fik[2]; + fjk[2] = *(triangle_eval + 3) * (del_rkj[2] / rjk); + fkj[2] = -fjk[2]; + + Fi[0] = fij[0] + fik[0]; + Fi[1] = fij[1] + fik[1]; + Fi[2] = fij[2] + fik[2]; + f[i][0] += Fi[0]; + f[i][1] += Fi[1]; + f[i][2] += Fi[2]; + + Fj[0] = fji[0] + fjk[0]; + Fj[1] = fji[1] + fjk[1]; + Fj[2] = fji[2] + fjk[2]; + f[j][0] += Fj[0]; + f[j][1] += Fj[1]; + f[j][2] += Fj[2]; + + Fk[0] = fki[0] + fkj[0]; + Fk[1] = fki[1] + fkj[1]; + Fk[2] = fki[2] + fkj[2]; + f[k][0] += Fk[0]; + f[k][1] += Fk[1]; + f[k][2] += Fk[2]; + + if (eflag) evdwl = *triangle_eval; + + if (evflag) { ev_tally3(i, j, k, evdwl, 0, Fj, Fk, del_rji, del_rki); + // Centroid stress 3-body term + if (vflag_either && cvflag_atom) { + double ric[3]; + ric[0] = THIRD * (-del_rji[0] - del_rki[0]); + ric[1] = THIRD * (-del_rji[1] - del_rki[1]); + ric[2] = THIRD * (-del_rji[2] - del_rki[2]); + + cvatom[i][0] += ric[0] * Fi[0]; + cvatom[i][1] += ric[1] * Fi[1]; + cvatom[i][2] += ric[2] * Fi[2]; + cvatom[i][3] += ric[0] * Fi[1]; + cvatom[i][4] += ric[0] * Fi[2]; + cvatom[i][5] += ric[1] * Fi[2]; + cvatom[i][6] += ric[1] * Fi[0]; + cvatom[i][7] += ric[2] * Fi[0]; + cvatom[i][8] += ric[2] * Fi[1]; + + double rjc[3]; + rjc[0] = THIRD * (del_rji[0] - del_rkj[0]); + rjc[1] = THIRD * (del_rji[1] - del_rkj[1]); + rjc[2] = THIRD * (del_rji[2] - del_rkj[2]); + + cvatom[j][0] += rjc[0] * Fj[0]; + cvatom[j][1] += rjc[1] * Fj[1]; + cvatom[j][2] += rjc[2] * Fj[2]; + cvatom[j][3] += rjc[0] * Fj[1]; + cvatom[j][4] += rjc[0] * Fj[2]; + cvatom[j][5] += rjc[1] * Fj[2]; + cvatom[j][6] += rjc[1] * Fj[0]; + cvatom[j][7] += rjc[2] * Fj[0]; + cvatom[j][8] += rjc[2] * Fj[1]; + + double rkc[3]; + rkc[0] = THIRD * (del_rki[0] + del_rkj[0]); + rkc[1] = THIRD * (del_rki[1] + del_rkj[1]); + rkc[2] = THIRD * (del_rki[2] + del_rkj[2]); + + cvatom[k][0] += rkc[0] * Fk[0]; + cvatom[k][1] += rkc[1] * Fk[1]; + cvatom[k][2] += rkc[2] * Fk[2]; + cvatom[k][3] += rkc[0] * Fk[1]; + cvatom[k][4] += rkc[0] * Fk[2]; + cvatom[k][5] += rkc[1] * Fk[2]; + cvatom[k][6] += rkc[1] * Fk[0]; + cvatom[k][7] += rkc[2] * Fk[0]; + cvatom[k][8] += rkc[2] * Fk[1]; + } + } + } + } + } + } + } + if (vflag_fdotr) virial_fdotr_compute(); +} + +double PairUF3::single(int /*i*/, int /*j*/, int itype, int jtype, double rsq, + double /*factor_coul*/, double factor_lj, double &fforce) +{ + double value = 0.0; + double r = sqrt(rsq); + + if (r < cut[itype][jtype]) { + double *pair_eval = UFBS2b[itype][jtype].eval(r); + value = pair_eval[0]; + fforce = factor_lj * pair_eval[1]; + } + + return factor_lj * value; +} + +double PairUF3::memory_usage() +{ + double bytes = Pair::memory_usage(); + + bytes = 0; + + bytes += (double)5*sizeof(double); //num_of_elements, nbody_flag, + //n2body_pot_files, n3body_pot_files, + //tot_pot_files; + + bytes += (double)5*sizeof(double); //bsplines_created, coeff_matrix_dim1, + //coeff_matrix_dim2, coeff_matrix_dim3, + //coeff_matrix_elements_len + bytes += (double)(num_of_elements+1)*(num_of_elements+1)*\ + (num_of_elements+1)*sizeof(double); //***setflag_3b + + bytes += (double)(num_of_elements+1)*(num_of_elements+1)*sizeof(double); //cut + + bytes += (double)(num_of_elements+1)*(num_of_elements+1)*\ + (num_of_elements+1)*sizeof(double); //***cut_3b + + bytes += (double)(num_of_elements+1)*(num_of_elements+1)*sizeof(double); //cut_3b_list + + bytes += (double)(num_of_elements+1)*(num_of_elements+1)*\ + (num_of_elements+1)*3*sizeof(double); //min_cut_3b + + for (int i=1; i < num_of_elements+1; i++){ + for (int j=i; j < num_of_elements+1; j++){ + bytes += (double)2*n2b_knot[i][j].size()*sizeof(double); //n2b_knot + bytes += (double)2*n2b_coeff[i][j].size()*sizeof(double); //n2b_coeff + } + if (pot_3b){ + for (int j = 1; j < num_of_elements + 1; j++) { + for (int k = j; k < num_of_elements + 1; k++) { + bytes += (double)2*n3b_knot_matrix[i][j][k][0].size()*sizeof(double); + bytes += (double)2*n3b_knot_matrix[i][j][k][1].size()*sizeof(double); + bytes += (double)2*n3b_knot_matrix[i][j][k][2].size()*sizeof(double); + + std::string key = std::to_string(i) + std::to_string(j) + std::to_string(k); + + for (int l=0; l < n3b_coeff_matrix[key].size(); l++){ + for (int m=0; m < n3b_coeff_matrix[key][l].size(); m++){ + bytes += (double)2*n3b_coeff_matrix[key][l][m].size()*sizeof(double); + //key = ijk + //key = ikj + } + } + } + } + } + } + + for (int i = 1; i < num_of_elements + 1; i++) { + for (int j = i; j < num_of_elements + 1; j++){ + bytes += (double)2*UFBS2b[i][j].memory_usage(); //UFBS2b[i][j] UFBS2b[j][1] + } + if (pot_3b) { + for (int j = 1; j < num_of_elements + 1; j++) { + for (int k = j; k < num_of_elements + 1; k++) { + bytes += (double)2*UFBS3b[i][j][k].memory_usage(); + } + } + } + } + + bytes += (double)(maxshort+1)*sizeof(int); //neighshort, maxshort + + return bytes; +} + diff --git a/src/ML-UF3/pair_uf3.h b/src/ML-UF3/pair_uf3.h new file mode 100644 index 0000000000..54f0e7e2e4 --- /dev/null +++ b/src/ML-UF3/pair_uf3.h @@ -0,0 +1,88 @@ +/* -*- c++ -*- ---------------------------------------------------------- + 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 authors: Ajinkya Hire(U of Florida), + * Hendrik Kraß (U of Constance), + * Richard Hennig (U of Florida) + * ---------------------------------------------------------------------- */ + +#ifdef PAIR_CLASS +// clang-format off +PairStyle(uf3,PairUF3); +// clang-format on +#else + +#ifndef LMP_PAIR_UF3_H +#define LMP_PAIR_UF3_H + +#include "uf3_pair_bspline.h" +#include "uf3_triplet_bspline.h" + +#include "pair.h" + +#include +namespace LAMMPS_NS { + +class PairUF3 : public Pair { + public: + PairUF3(class LAMMPS *); + ~PairUF3() override; + void compute(int, int) override; + void settings(int, char **) override; + void coeff(int, char **) override; + void init_style() override; + void init_list(int, class NeighList *) override; // needed for ptr to full neigh list + double init_one(int, int) override; // needed for cutoff radius for neighbour list + double single(int, int, int, int, double, double, double, double &) override; + + double memory_usage() override; + + protected: + void uf3_read_pot_file(char *potf_name); + void uf3_read_pot_file(int i, int j, char *potf_name); + void uf3_read_pot_file(int i, int j, int k, char *potf_name); + int num_of_elements, nbody_flag, n2body_pot_files, n3body_pot_files, tot_pot_files; + int bsplines_created; + int coeff_matrix_dim1, coeff_matrix_dim2, coeff_matrix_dim3, coeff_matrix_elements_len; + bool pot_3b; + int ***setflag_3b, **knot_spacing_type_2b, ***knot_spacing_type_3b; + double **cut, ***cut_3b, **cut_3b_list, ****min_cut_3b; + virtual void allocate(); + void create_bsplines(); + std::vector>> n2b_knot, n2b_coeff; + std::vector>>>> n3b_knot_matrix; + std::unordered_map>>> n3b_coeff_matrix; + std::vector> UFBS2b; + std::vector>> UFBS3b; + int *neighshort, maxshort; // short neighbor list array for 3body interaction +}; + +} // namespace LAMMPS_NS + +#endif +#endif + +/* ERROR/WARNING messages: + +E: Illegal ... command + +Self-explanatory. Check the input script syntax and compare to the +documentation for the command. You can use -echo screen as a +command-line option when running LAMMPS to see the offending line. + +E: Incorrect args for pair coefficients + +Self-explanatory. Check the input script or data file. + +*/ diff --git a/src/ML-UF3/uf3_bspline_basis2.cpp b/src/ML-UF3/uf3_bspline_basis2.cpp new file mode 100644 index 0000000000..8ae1991ce2 --- /dev/null +++ b/src/ML-UF3/uf3_bspline_basis2.cpp @@ -0,0 +1,88 @@ +#include "uf3_bspline_basis2.h" + +#include "utils.h" +#include + +using namespace LAMMPS_NS; + +// Constructor +// Initializes coefficients and knots +// Requires [knots] to have length 4 +uf3_bspline_basis2::uf3_bspline_basis2(LAMMPS *ulmp, const double *knots, double coefficient) +{ + lmp = ulmp; + + double c0, c1, c2; + + c0 = coefficient * + (pow(knots[0], 2) / + (pow(knots[0], 2) - knots[0] * knots[1] - knots[0] * knots[2] + knots[1] * knots[2])); + c1 = coefficient * + (-2 * knots[0] / + (pow(knots[0], 2) - knots[0] * knots[1] - knots[0] * knots[2] + knots[1] * knots[2])); + c2 = coefficient * + (1 / (pow(knots[0], 2) - knots[0] * knots[1] - knots[0] * knots[2] + knots[1] * knots[2])); + constants.push_back(c0); + constants.push_back(c1); + constants.push_back(c2); + c0 = coefficient * + (-knots[1] * knots[3] / + (pow(knots[1], 2) - knots[1] * knots[2] - knots[1] * knots[3] + knots[2] * knots[3]) - + knots[0] * knots[2] / + (knots[0] * knots[1] - knots[0] * knots[2] - knots[1] * knots[2] + pow(knots[2], 2))); + c1 = coefficient * + (knots[1] / + (pow(knots[1], 2) - knots[1] * knots[2] - knots[1] * knots[3] + knots[2] * knots[3]) + + knots[3] / + (pow(knots[1], 2) - knots[1] * knots[2] - knots[1] * knots[3] + knots[2] * knots[3]) + + knots[0] / + (knots[0] * knots[1] - knots[0] * knots[2] - knots[1] * knots[2] + pow(knots[2], 2)) + + knots[2] / + (knots[0] * knots[1] - knots[0] * knots[2] - knots[1] * knots[2] + pow(knots[2], 2))); + c2 = coefficient * + (-1 / (pow(knots[1], 2) - knots[1] * knots[2] - knots[1] * knots[3] + knots[2] * knots[3]) - + 1 / (knots[0] * knots[1] - knots[0] * knots[2] - knots[1] * knots[2] + pow(knots[2], 2))); + constants.push_back(c0); + constants.push_back(c1); + constants.push_back(c2); + c0 = coefficient * + (pow(knots[3], 2) / + (knots[1] * knots[2] - knots[1] * knots[3] - knots[2] * knots[3] + pow(knots[3], 2))); + c1 = coefficient * + (-2 * knots[3] / + (knots[1] * knots[2] - knots[1] * knots[3] - knots[2] * knots[3] + pow(knots[3], 2))); + c2 = coefficient * + (1 / (knots[1] * knots[2] - knots[1] * knots[3] - knots[2] * knots[3] + pow(knots[3], 2))); + constants.push_back(c0); + constants.push_back(c1); + constants.push_back(c2); +} + +uf3_bspline_basis2::~uf3_bspline_basis2() {} + +// Evaluate outer-left part of spline +double uf3_bspline_basis2::eval0(double rsq, double r) +{ + return rsq * constants[2] + r * constants[1] + constants[0]; +} + +// Evaluate center-left part of spline +double uf3_bspline_basis2::eval1(double rsq, double r) +{ + return rsq * constants[5] + r * constants[4] + constants[3]; +} + +// Evaluate center-right part of spline +double uf3_bspline_basis2::eval2(double rsq, double r) +{ + return rsq * constants[8] + r * constants[7] + constants[6]; +} + +double uf3_bspline_basis2::memory_usage() +{ + double bytes = 0; + + bytes += (double)constants.size()*sizeof(double); + + return bytes; +} diff --git a/src/ML-UF3/uf3_bspline_basis2.h b/src/ML-UF3/uf3_bspline_basis2.h new file mode 100644 index 0000000000..8551b097b1 --- /dev/null +++ b/src/ML-UF3/uf3_bspline_basis2.h @@ -0,0 +1,32 @@ +//De Boor's algorithm @ +//https://pages.mtu.edu/~shene/COURSES/cs3621/NOTES/spline/B-spline/de-Boor.html +//For values outside the domain, +//extrapoltaes the left(right) hand side piece of the curve +//Only works for bspline degree upto 3 becuase of definiation of P +// +#include "pointers.h" + +#include + +#ifndef UF3_BSPLINE_BASIS2_H +#define UF3_BSPLINE_BASIS2_H + +namespace LAMMPS_NS { + +class uf3_bspline_basis2 { + private: + LAMMPS *lmp; + std::vector constants; + + public: + uf3_bspline_basis2(LAMMPS *ulmp, const double *knots, double coefficient); + ~uf3_bspline_basis2(); + double eval0(double, double); + double eval1(double, double); + double eval2(double, double); + + double memory_usage(); +}; + +} // namespace LAMMPS_NS +#endif diff --git a/src/ML-UF3/uf3_bspline_basis3.cpp b/src/ML-UF3/uf3_bspline_basis3.cpp new file mode 100644 index 0000000000..f66ac0d1dc --- /dev/null +++ b/src/ML-UF3/uf3_bspline_basis3.cpp @@ -0,0 +1,324 @@ +#include "uf3_bspline_basis3.h" + +#include "utils.h" +#include + +using namespace LAMMPS_NS; + +// Constructor +// Initializes coefficients and knots +// [knots] needs to have length 4 +uf3_bspline_basis3::uf3_bspline_basis3(LAMMPS *ulmp, const double *knots, double coefficient) +{ + lmp = ulmp; + + double c0, c1, c2, c3; + + c0 = coefficient * + (-pow(knots[0], 3) / + (-pow(knots[0], 3) + pow(knots[0], 2) * knots[1] + pow(knots[0], 2) * knots[2] + + pow(knots[0], 2) * knots[3] - knots[0] * knots[1] * knots[2] - + knots[0] * knots[1] * knots[3] - knots[0] * knots[2] * knots[3] + + knots[1] * knots[2] * knots[3])); + c1 = coefficient * + (3 * pow(knots[0], 2) / + (-pow(knots[0], 3) + pow(knots[0], 2) * knots[1] + pow(knots[0], 2) * knots[2] + + pow(knots[0], 2) * knots[3] - knots[0] * knots[1] * knots[2] - + knots[0] * knots[1] * knots[3] - knots[0] * knots[2] * knots[3] + + knots[1] * knots[2] * knots[3])); + c2 = coefficient * + (-3 * knots[0] / + (-pow(knots[0], 3) + pow(knots[0], 2) * knots[1] + pow(knots[0], 2) * knots[2] + + pow(knots[0], 2) * knots[3] - knots[0] * knots[1] * knots[2] - + knots[0] * knots[1] * knots[3] - knots[0] * knots[2] * knots[3] + + knots[1] * knots[2] * knots[3])); + c3 = coefficient * + (1 / + (-pow(knots[0], 3) + pow(knots[0], 2) * knots[1] + pow(knots[0], 2) * knots[2] + + pow(knots[0], 2) * knots[3] - knots[0] * knots[1] * knots[2] - + knots[0] * knots[1] * knots[3] - knots[0] * knots[2] * knots[3] + + knots[1] * knots[2] * knots[3])); + constants.push_back(c0); + constants.push_back(c1); + constants.push_back(c2); + constants.push_back(c3); + c0 = coefficient * + (pow(knots[1], 2) * knots[4] / + (-pow(knots[1], 3) + pow(knots[1], 2) * knots[2] + pow(knots[1], 2) * knots[3] + + pow(knots[1], 2) * knots[4] - knots[1] * knots[2] * knots[3] - + knots[1] * knots[2] * knots[4] - knots[1] * knots[3] * knots[4] + + knots[2] * knots[3] * knots[4]) + + pow(knots[0], 2) * knots[2] / + (-pow(knots[0], 2) * knots[1] + pow(knots[0], 2) * knots[2] + + knots[0] * knots[1] * knots[2] + knots[0] * knots[1] * knots[3] - + knots[0] * pow(knots[2], 2) - knots[0] * knots[2] * knots[3] - + knots[1] * knots[2] * knots[3] + pow(knots[2], 2) * knots[3]) + + knots[0] * knots[1] * knots[3] / + (-knots[0] * pow(knots[1], 2) + knots[0] * knots[1] * knots[2] + + knots[0] * knots[1] * knots[3] - knots[0] * knots[2] * knots[3] + + pow(knots[1], 2) * knots[3] - knots[1] * knots[2] * knots[3] - + knots[1] * pow(knots[3], 2) + knots[2] * pow(knots[3], 2))); + c1 = coefficient * + (-pow(knots[1], 2) / + (-pow(knots[1], 3) + pow(knots[1], 2) * knots[2] + pow(knots[1], 2) * knots[3] + + pow(knots[1], 2) * knots[4] - knots[1] * knots[2] * knots[3] - + knots[1] * knots[2] * knots[4] - knots[1] * knots[3] * knots[4] + + knots[2] * knots[3] * knots[4]) - + 2 * knots[1] * knots[4] / + (-pow(knots[1], 3) + pow(knots[1], 2) * knots[2] + pow(knots[1], 2) * knots[3] + + pow(knots[1], 2) * knots[4] - knots[1] * knots[2] * knots[3] - + knots[1] * knots[2] * knots[4] - knots[1] * knots[3] * knots[4] + + knots[2] * knots[3] * knots[4]) - + pow(knots[0], 2) / + (-pow(knots[0], 2) * knots[1] + pow(knots[0], 2) * knots[2] + + knots[0] * knots[1] * knots[2] + knots[0] * knots[1] * knots[3] - + knots[0] * pow(knots[2], 2) - knots[0] * knots[2] * knots[3] - + knots[1] * knots[2] * knots[3] + pow(knots[2], 2) * knots[3]) - + 2 * knots[0] * knots[2] / + (-pow(knots[0], 2) * knots[1] + pow(knots[0], 2) * knots[2] + + knots[0] * knots[1] * knots[2] + knots[0] * knots[1] * knots[3] - + knots[0] * pow(knots[2], 2) - knots[0] * knots[2] * knots[3] - + knots[1] * knots[2] * knots[3] + pow(knots[2], 2) * knots[3]) - + knots[0] * knots[1] / + (-knots[0] * pow(knots[1], 2) + knots[0] * knots[1] * knots[2] + + knots[0] * knots[1] * knots[3] - knots[0] * knots[2] * knots[3] + + pow(knots[1], 2) * knots[3] - knots[1] * knots[2] * knots[3] - + knots[1] * pow(knots[3], 2) + knots[2] * pow(knots[3], 2)) - + knots[0] * knots[3] / + (-knots[0] * pow(knots[1], 2) + knots[0] * knots[1] * knots[2] + + knots[0] * knots[1] * knots[3] - knots[0] * knots[2] * knots[3] + + pow(knots[1], 2) * knots[3] - knots[1] * knots[2] * knots[3] - + knots[1] * pow(knots[3], 2) + knots[2] * pow(knots[3], 2)) - + knots[1] * knots[3] / + (-knots[0] * pow(knots[1], 2) + knots[0] * knots[1] * knots[2] + + knots[0] * knots[1] * knots[3] - knots[0] * knots[2] * knots[3] + + pow(knots[1], 2) * knots[3] - knots[1] * knots[2] * knots[3] - + knots[1] * pow(knots[3], 2) + knots[2] * pow(knots[3], 2))); + c2 = coefficient * + (2 * knots[1] / + (-pow(knots[1], 3) + pow(knots[1], 2) * knots[2] + pow(knots[1], 2) * knots[3] + + pow(knots[1], 2) * knots[4] - knots[1] * knots[2] * knots[3] - + knots[1] * knots[2] * knots[4] - knots[1] * knots[3] * knots[4] + + knots[2] * knots[3] * knots[4]) + + knots[4] / + (-pow(knots[1], 3) + pow(knots[1], 2) * knots[2] + pow(knots[1], 2) * knots[3] + + pow(knots[1], 2) * knots[4] - knots[1] * knots[2] * knots[3] - + knots[1] * knots[2] * knots[4] - knots[1] * knots[3] * knots[4] + + knots[2] * knots[3] * knots[4]) + + 2 * knots[0] / + (-pow(knots[0], 2) * knots[1] + pow(knots[0], 2) * knots[2] + + knots[0] * knots[1] * knots[2] + knots[0] * knots[1] * knots[3] - + knots[0] * pow(knots[2], 2) - knots[0] * knots[2] * knots[3] - + knots[1] * knots[2] * knots[3] + pow(knots[2], 2) * knots[3]) + + knots[2] / + (-pow(knots[0], 2) * knots[1] + pow(knots[0], 2) * knots[2] + + knots[0] * knots[1] * knots[2] + knots[0] * knots[1] * knots[3] - + knots[0] * pow(knots[2], 2) - knots[0] * knots[2] * knots[3] - + knots[1] * knots[2] * knots[3] + pow(knots[2], 2) * knots[3]) + + knots[0] / + (-knots[0] * pow(knots[1], 2) + knots[0] * knots[1] * knots[2] + + knots[0] * knots[1] * knots[3] - knots[0] * knots[2] * knots[3] + + pow(knots[1], 2) * knots[3] - knots[1] * knots[2] * knots[3] - + knots[1] * pow(knots[3], 2) + knots[2] * pow(knots[3], 2)) + + knots[1] / + (-knots[0] * pow(knots[1], 2) + knots[0] * knots[1] * knots[2] + + knots[0] * knots[1] * knots[3] - knots[0] * knots[2] * knots[3] + + pow(knots[1], 2) * knots[3] - knots[1] * knots[2] * knots[3] - + knots[1] * pow(knots[3], 2) + knots[2] * pow(knots[3], 2)) + + knots[3] / + (-knots[0] * pow(knots[1], 2) + knots[0] * knots[1] * knots[2] + + knots[0] * knots[1] * knots[3] - knots[0] * knots[2] * knots[3] + + pow(knots[1], 2) * knots[3] - knots[1] * knots[2] * knots[3] - + knots[1] * pow(knots[3], 2) + knots[2] * pow(knots[3], 2))); + c3 = coefficient * + (-1 / + (-pow(knots[1], 3) + pow(knots[1], 2) * knots[2] + pow(knots[1], 2) * knots[3] + + pow(knots[1], 2) * knots[4] - knots[1] * knots[2] * knots[3] - + knots[1] * knots[2] * knots[4] - knots[1] * knots[3] * knots[4] + + knots[2] * knots[3] * knots[4]) - + 1 / + (-pow(knots[0], 2) * knots[1] + pow(knots[0], 2) * knots[2] + + knots[0] * knots[1] * knots[2] + knots[0] * knots[1] * knots[3] - + knots[0] * pow(knots[2], 2) - knots[0] * knots[2] * knots[3] - + knots[1] * knots[2] * knots[3] + pow(knots[2], 2) * knots[3]) - + 1 / + (-knots[0] * pow(knots[1], 2) + knots[0] * knots[1] * knots[2] + + knots[0] * knots[1] * knots[3] - knots[0] * knots[2] * knots[3] + + pow(knots[1], 2) * knots[3] - knots[1] * knots[2] * knots[3] - + knots[1] * pow(knots[3], 2) + knots[2] * pow(knots[3], 2))); + constants.push_back(c0); + constants.push_back(c1); + constants.push_back(c2); + constants.push_back(c3); + c0 = coefficient * + (-knots[0] * pow(knots[3], 2) / + (-knots[0] * knots[1] * knots[2] + knots[0] * knots[1] * knots[3] + + knots[0] * knots[2] * knots[3] - knots[0] * pow(knots[3], 2) + + knots[1] * knots[2] * knots[3] - knots[1] * pow(knots[3], 2) - + knots[2] * pow(knots[3], 2) + pow(knots[3], 3)) - + knots[1] * knots[3] * knots[4] / + (-pow(knots[1], 2) * knots[2] + pow(knots[1], 2) * knots[3] + + knots[1] * knots[2] * knots[3] + knots[1] * knots[2] * knots[4] - + knots[1] * pow(knots[3], 2) - knots[1] * knots[3] * knots[4] - + knots[2] * knots[3] * knots[4] + pow(knots[3], 2) * knots[4]) - + knots[2] * pow(knots[4], 2) / + (-knots[1] * pow(knots[2], 2) + knots[1] * knots[2] * knots[3] + + knots[1] * knots[2] * knots[4] - knots[1] * knots[3] * knots[4] + + pow(knots[2], 2) * knots[4] - knots[2] * knots[3] * knots[4] - + knots[2] * pow(knots[4], 2) + knots[3] * pow(knots[4], 2))); + c1 = coefficient * + (2 * knots[0] * knots[3] / + (-knots[0] * knots[1] * knots[2] + knots[0] * knots[1] * knots[3] + + knots[0] * knots[2] * knots[3] - knots[0] * pow(knots[3], 2) + + knots[1] * knots[2] * knots[3] - knots[1] * pow(knots[3], 2) - + knots[2] * pow(knots[3], 2) + pow(knots[3], 3)) + + pow(knots[3], 2) / + (-knots[0] * knots[1] * knots[2] + knots[0] * knots[1] * knots[3] + + knots[0] * knots[2] * knots[3] - knots[0] * pow(knots[3], 2) + + knots[1] * knots[2] * knots[3] - knots[1] * pow(knots[3], 2) - + knots[2] * pow(knots[3], 2) + pow(knots[3], 3)) + + knots[1] * knots[3] / + (-pow(knots[1], 2) * knots[2] + pow(knots[1], 2) * knots[3] + + knots[1] * knots[2] * knots[3] + knots[1] * knots[2] * knots[4] - + knots[1] * pow(knots[3], 2) - knots[1] * knots[3] * knots[4] - + knots[2] * knots[3] * knots[4] + pow(knots[3], 2) * knots[4]) + + knots[1] * knots[4] / + (-pow(knots[1], 2) * knots[2] + pow(knots[1], 2) * knots[3] + + knots[1] * knots[2] * knots[3] + knots[1] * knots[2] * knots[4] - + knots[1] * pow(knots[3], 2) - knots[1] * knots[3] * knots[4] - + knots[2] * knots[3] * knots[4] + pow(knots[3], 2) * knots[4]) + + knots[3] * knots[4] / + (-pow(knots[1], 2) * knots[2] + pow(knots[1], 2) * knots[3] + + knots[1] * knots[2] * knots[3] + knots[1] * knots[2] * knots[4] - + knots[1] * pow(knots[3], 2) - knots[1] * knots[3] * knots[4] - + knots[2] * knots[3] * knots[4] + pow(knots[3], 2) * knots[4]) + + 2 * knots[2] * knots[4] / + (-knots[1] * pow(knots[2], 2) + knots[1] * knots[2] * knots[3] + + knots[1] * knots[2] * knots[4] - knots[1] * knots[3] * knots[4] + + pow(knots[2], 2) * knots[4] - knots[2] * knots[3] * knots[4] - + knots[2] * pow(knots[4], 2) + knots[3] * pow(knots[4], 2)) + + pow(knots[4], 2) / + (-knots[1] * pow(knots[2], 2) + knots[1] * knots[2] * knots[3] + + knots[1] * knots[2] * knots[4] - knots[1] * knots[3] * knots[4] + + pow(knots[2], 2) * knots[4] - knots[2] * knots[3] * knots[4] - + knots[2] * pow(knots[4], 2) + knots[3] * pow(knots[4], 2))); + c2 = coefficient * + (-knots[0] / + (-knots[0] * knots[1] * knots[2] + knots[0] * knots[1] * knots[3] + + knots[0] * knots[2] * knots[3] - knots[0] * pow(knots[3], 2) + + knots[1] * knots[2] * knots[3] - knots[1] * pow(knots[3], 2) - + knots[2] * pow(knots[3], 2) + pow(knots[3], 3)) - + 2 * knots[3] / + (-knots[0] * knots[1] * knots[2] + knots[0] * knots[1] * knots[3] + + knots[0] * knots[2] * knots[3] - knots[0] * pow(knots[3], 2) + + knots[1] * knots[2] * knots[3] - knots[1] * pow(knots[3], 2) - + knots[2] * pow(knots[3], 2) + pow(knots[3], 3)) - + knots[1] / + (-pow(knots[1], 2) * knots[2] + pow(knots[1], 2) * knots[3] + + knots[1] * knots[2] * knots[3] + knots[1] * knots[2] * knots[4] - + knots[1] * pow(knots[3], 2) - knots[1] * knots[3] * knots[4] - + knots[2] * knots[3] * knots[4] + pow(knots[3], 2) * knots[4]) - + knots[3] / + (-pow(knots[1], 2) * knots[2] + pow(knots[1], 2) * knots[3] + + knots[1] * knots[2] * knots[3] + knots[1] * knots[2] * knots[4] - + knots[1] * pow(knots[3], 2) - knots[1] * knots[3] * knots[4] - + knots[2] * knots[3] * knots[4] + pow(knots[3], 2) * knots[4]) - + knots[4] / + (-pow(knots[1], 2) * knots[2] + pow(knots[1], 2) * knots[3] + + knots[1] * knots[2] * knots[3] + knots[1] * knots[2] * knots[4] - + knots[1] * pow(knots[3], 2) - knots[1] * knots[3] * knots[4] - + knots[2] * knots[3] * knots[4] + pow(knots[3], 2) * knots[4]) - + knots[2] / + (-knots[1] * pow(knots[2], 2) + knots[1] * knots[2] * knots[3] + + knots[1] * knots[2] * knots[4] - knots[1] * knots[3] * knots[4] + + pow(knots[2], 2) * knots[4] - knots[2] * knots[3] * knots[4] - + knots[2] * pow(knots[4], 2) + knots[3] * pow(knots[4], 2)) - + 2 * knots[4] / + (-knots[1] * pow(knots[2], 2) + knots[1] * knots[2] * knots[3] + + knots[1] * knots[2] * knots[4] - knots[1] * knots[3] * knots[4] + + pow(knots[2], 2) * knots[4] - knots[2] * knots[3] * knots[4] - + knots[2] * pow(knots[4], 2) + knots[3] * pow(knots[4], 2))); + c3 = coefficient * + (1 / + (-knots[0] * knots[1] * knots[2] + knots[0] * knots[1] * knots[3] + + knots[0] * knots[2] * knots[3] - knots[0] * pow(knots[3], 2) + + knots[1] * knots[2] * knots[3] - knots[1] * pow(knots[3], 2) - + knots[2] * pow(knots[3], 2) + pow(knots[3], 3)) + + 1 / + (-pow(knots[1], 2) * knots[2] + pow(knots[1], 2) * knots[3] + + knots[1] * knots[2] * knots[3] + knots[1] * knots[2] * knots[4] - + knots[1] * pow(knots[3], 2) - knots[1] * knots[3] * knots[4] - + knots[2] * knots[3] * knots[4] + pow(knots[3], 2) * knots[4]) + + 1 / + (-knots[1] * pow(knots[2], 2) + knots[1] * knots[2] * knots[3] + + knots[1] * knots[2] * knots[4] - knots[1] * knots[3] * knots[4] + + pow(knots[2], 2) * knots[4] - knots[2] * knots[3] * knots[4] - + knots[2] * pow(knots[4], 2) + knots[3] * pow(knots[4], 2))); + constants.push_back(c0); + constants.push_back(c1); + constants.push_back(c2); + constants.push_back(c3); + c0 = coefficient * + (pow(knots[4], 3) / + (-knots[1] * knots[2] * knots[3] + knots[1] * knots[2] * knots[4] + + knots[1] * knots[3] * knots[4] - knots[1] * pow(knots[4], 2) + + knots[2] * knots[3] * knots[4] - knots[2] * pow(knots[4], 2) - knots[3] * pow(knots[4], 2) + + pow(knots[4], 3))); + c1 = coefficient * + (-3 * pow(knots[4], 2) / + (-knots[1] * knots[2] * knots[3] + knots[1] * knots[2] * knots[4] + + knots[1] * knots[3] * knots[4] - knots[1] * pow(knots[4], 2) + + knots[2] * knots[3] * knots[4] - knots[2] * pow(knots[4], 2) - knots[3] * pow(knots[4], 2) + + pow(knots[4], 3))); + c2 = coefficient * + (3 * knots[4] / + (-knots[1] * knots[2] * knots[3] + knots[1] * knots[2] * knots[4] + + knots[1] * knots[3] * knots[4] - knots[1] * pow(knots[4], 2) + + knots[2] * knots[3] * knots[4] - knots[2] * pow(knots[4], 2) - knots[3] * pow(knots[4], 2) + + pow(knots[4], 3))); + c3 = coefficient * + (-1 / + (-knots[1] * knots[2] * knots[3] + knots[1] * knots[2] * knots[4] + + knots[1] * knots[3] * knots[4] - knots[1] * pow(knots[4], 2) + + knots[2] * knots[3] * knots[4] - knots[2] * pow(knots[4], 2) - knots[3] * pow(knots[4], 2) + + pow(knots[4], 3))); + constants.push_back(c0); + constants.push_back(c1); + constants.push_back(c2); + constants.push_back(c3); +} + +uf3_bspline_basis3::~uf3_bspline_basis3() {} + +// Evaluate outer-left part of spline +double uf3_bspline_basis3::eval0(double rth, double rsq, double r) +{ + return rth * constants[3] + rsq * constants[2] + r * constants[1] + constants[0]; +} + +// Evaluate center-left part of spline +double uf3_bspline_basis3::eval1(double rth, double rsq, double r) +{ + return rth * constants[7] + rsq * constants[6] + r * constants[5] + constants[4]; +} + +// Evaluate center-right part of spline +double uf3_bspline_basis3::eval2(double rth, double rsq, double r) +{ + return rth * constants[11] + rsq * constants[10] + r * constants[9] + constants[8]; +} + +// Evaluate outer-right part of spline +double uf3_bspline_basis3::eval3(double rth, double rsq, double r) +{ + return rth * constants[15] + rsq * constants[14] + r * constants[13] + constants[12]; +} + +double uf3_bspline_basis3::memory_usage() +{ + double bytes = 0; + + bytes += (double)constants.size()*sizeof(double); + + return bytes; +} diff --git a/src/ML-UF3/uf3_bspline_basis3.h b/src/ML-UF3/uf3_bspline_basis3.h new file mode 100644 index 0000000000..d29d9b08f1 --- /dev/null +++ b/src/ML-UF3/uf3_bspline_basis3.h @@ -0,0 +1,33 @@ +//De Boor's algorithm @ +//https://pages.mtu.edu/~shene/COURSES/cs3621/NOTES/spline/B-spline/de-Boor.html +//For values outside the domain, +//extrapoltaes the left(right) hand side piece of the curve +//Only works for bspline degree upto 3 becuase of definiation of P +// +#include "pointers.h" + +#include + +#ifndef UF3_BSPLINE_BASIS3_H +#define UF3_BSPLINE_BASIS3_H + +namespace LAMMPS_NS { + +class uf3_bspline_basis3 { + private: + LAMMPS *lmp; + std::vector constants; + + public: + uf3_bspline_basis3(LAMMPS *ulmp, const double *knots, double coefficient); + ~uf3_bspline_basis3(); + double eval0(double, double, double); + double eval1(double, double, double); + double eval2(double, double, double); + double eval3(double, double, double); + + double memory_usage(); +}; + +} // namespace LAMMPS_NS +#endif diff --git a/src/ML-UF3/uf3_pair_bspline.cpp b/src/ML-UF3/uf3_pair_bspline.cpp new file mode 100644 index 0000000000..d4c14284f8 --- /dev/null +++ b/src/ML-UF3/uf3_pair_bspline.cpp @@ -0,0 +1,144 @@ +#include "uf3_pair_bspline.h" + +#include "uf3_bspline_basis2.h" +#include "uf3_bspline_basis3.h" + +#include "utils.h" +#include "error.h" +#include + +using namespace LAMMPS_NS; + +// Dummy constructor +uf3_pair_bspline::uf3_pair_bspline() {} + +// Constructor +// Passing vectors by reference +uf3_pair_bspline::uf3_pair_bspline(LAMMPS *ulmp, const std::vector &uknot_vect, + const std::vector &ucoeff_vect, + const int &uknot_spacing_type) +{ + lmp = ulmp; + knot_vect = uknot_vect; + coeff_vect = ucoeff_vect; + + knot_spacing_type = uknot_spacing_type; + if (knot_spacing_type==0){ + knot_spacing = knot_vect[4]-knot_vect[3]; + get_starting_index=&uf3_pair_bspline::get_starting_index_uniform; + } + else if (knot_spacing_type==1){ + knot_spacing = 0; + get_starting_index=&uf3_pair_bspline::get_starting_index_nonuniform; + } + + else + lmp->error->all(FLERR, "UF3: Expected either '0'(uniform-knots) or \n\ + '1'(non-uniform knots)"); + + knot_vect_size = uknot_vect.size(); + coeff_vect_size = ucoeff_vect.size(); + + // Initialize B-Spline Basis Functions + for (int i = 0; i < knot_vect.size() - 4; i++) + bspline_bases.push_back(uf3_bspline_basis3(lmp, &knot_vect[i], coeff_vect[i])); + + // Initialize Coefficients and Knots for Derivatives + // The last coefficient needs to be droped + for (int i = 0; i < coeff_vect_size - 1; i++) { + double dntemp4 = 3 / (knot_vect[i + 4] - knot_vect[i + 1]); + dncoeff_vect.push_back((coeff_vect[i + 1] - coeff_vect[i]) * dntemp4); + } + //What we have is a clamped bspline -->i.e value of the bspline curve at the + //knots with multiplicity equal to the degree of bspline is equal to the coefficient + // + //Therefore for the derivative bspline the very first and last knot needs to be droped + //to change their multiplicity from 4 (necessary condition for clamped cubic bspline) + //to 3 (necessary condition for clamped quadratic bspline) + // + //Also if the coeff vector size of decreases by 1 for the derivative bspline + //knots size needs to go down by 2 as ==> knots = coefficient + degree + 1 + for (int i = 1; i < knot_vect_size - 1; i++) dnknot_vect.push_back(knot_vect[i]); + + // Initialize B-Spline Derivative Basis Functions + for (int i = 0; i < dnknot_vect.size() - 3; i++) + dnbspline_bases.push_back(uf3_bspline_basis2(lmp, &dnknot_vect[i], dncoeff_vect[i])); +} + +uf3_pair_bspline::~uf3_pair_bspline() {} + +int uf3_pair_bspline::get_starting_index_uniform(double r) +{ + return 3+(int)((r-knot_vect[0])/knot_spacing); +} + +int uf3_pair_bspline::get_starting_index_nonuniform(double r) +{ + if (knot_vect.front() <= r && r < knot_vect.back()) { + //Determine the interval for value_rij + for (int i = 3; i < knot_vect_size - 1; ++i) { + if (knot_vect[i] <= r && r < knot_vect[i + 1]) { + return i; + } + } + } +} + +double *uf3_pair_bspline::eval(double r) +{ + + // Find knot starting position + + int start_index=(this->*get_starting_index)(r); + /*if (knot_vect.front() <= r && r < knot_vect.back()) { + //Determine the interval for value_rij + for (int i = 3; i < knot_vect_size - 1; ++i) { + if (knot_vect[i] <= r && r < knot_vect[i + 1]) { + start_index = i; + break; + } + } + }*/ + + int knot_affect_start = start_index - 3; + + double rsq = r * r; + double rth = rsq * r; + + // Calculate energy + + ret_val[0] = bspline_bases[knot_affect_start + 3].eval0(rth, rsq, r); + ret_val[0] += bspline_bases[knot_affect_start + 2].eval1(rth, rsq, r); + ret_val[0] += bspline_bases[knot_affect_start + 1].eval2(rth, rsq, r); + ret_val[0] += bspline_bases[knot_affect_start].eval3(rth, rsq, r); + + // Calculate force + + ret_val[1] = dnbspline_bases[knot_affect_start + 2].eval0(rsq, r); + ret_val[1] += dnbspline_bases[knot_affect_start + 1].eval1(rsq, r); + ret_val[1] += dnbspline_bases[knot_affect_start].eval2(rsq, r); + + return ret_val; +} + +double uf3_pair_bspline::memory_usage() +{ + double bytes = 0; + + bytes += (double)2*sizeof(int); //knot_vect_size, + //coeff_vect_size + bytes += (double)knot_vect.size()*sizeof(double); //knot_vect + bytes += (double)dnknot_vect.size()*sizeof(double); //dnknot_vect + bytes += (double)coeff_vect.size()*sizeof(double); //coeff_vect + bytes += (double)dncoeff_vect.size()*sizeof(double); //dncoeff_vect + + for (int i = 0; i < knot_vect.size() - 4; i++) + bytes += (double)bspline_bases[i].memory_usage(); //bspline_basis3 + + for (int i = 0; i < dnknot_vect.size() - 3; i++) + bytes += (double)dnbspline_bases[i].memory_usage(); //bspline_basis2 + + bytes += (double)2*sizeof(double); //ret_val + + return bytes; +} diff --git a/src/ML-UF3/uf3_pair_bspline.h b/src/ML-UF3/uf3_pair_bspline.h new file mode 100644 index 0000000000..aa3f1e8c40 --- /dev/null +++ b/src/ML-UF3/uf3_pair_bspline.h @@ -0,0 +1,55 @@ +/* ---------------------------------------------------------------------- + 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. +------------------------------------------------------------------------- */ + +// De Boor's algorithm @ +// https://pages.mtu.edu/~shene/COURSES/cs3621/NOTES/spline/B-spline/de-Boor.html +// For values outside the domain, it exhibits undefined behavior. +// Uses fixed B-Spline degree 3. + +#include "pointers.h" + +#include "uf3_bspline_basis2.h" +#include "uf3_bspline_basis3.h" + +#include + +#ifndef UF3_PAIR_BSPLINE_H +#define UF3_PAIR_BSPLINE_H + +namespace LAMMPS_NS { + +class uf3_pair_bspline { + private: + int knot_vect_size, coeff_vect_size; + std::vector knot_vect, dnknot_vect; + std::vector coeff_vect, dncoeff_vect; + std::vector bspline_bases; + std::vector dnbspline_bases; + int get_starting_index_uniform(double), get_starting_index_nonuniform(double); + int (uf3_pair_bspline::*get_starting_index)(double); + //double knot_spacing=0; + LAMMPS *lmp; + + public: + // dummy constructor + uf3_pair_bspline(); + uf3_pair_bspline(LAMMPS *ulmp, const std::vector &uknot_vect, + const std::vector &ucoeff_vect, + const int &uknot_spacing_type); + ~uf3_pair_bspline(); + int knot_spacing_type; + double knot_spacing=0; + double ret_val[2]; + double *eval(double value_rij); + double memory_usage(); +}; +} // namespace LAMMPS_NS +#endif diff --git a/src/ML-UF3/uf3_triplet_bspline.cpp b/src/ML-UF3/uf3_triplet_bspline.cpp new file mode 100644 index 0000000000..6c5a5a19e7 --- /dev/null +++ b/src/ML-UF3/uf3_triplet_bspline.cpp @@ -0,0 +1,347 @@ +#include "uf3_triplet_bspline.h" +#include "error.h" +#include +#include + +using namespace LAMMPS_NS; + +// Dummy constructor +uf3_triplet_bspline::uf3_triplet_bspline(){}; + +// Construct a new 3D B-Spline +uf3_triplet_bspline::uf3_triplet_bspline( + LAMMPS *ulmp, const std::vector> &uknot_matrix, + const std::vector>> &ucoeff_matrix, + const int &uknot_spacing_type) +{ + lmp = ulmp; + knot_matrix = uknot_matrix; + coeff_matrix = ucoeff_matrix; + + knot_spacing_type = uknot_spacing_type; + if (knot_spacing_type==0){ + knot_spacing_ij = knot_matrix[2][4]-knot_matrix[2][3]; + knot_spacing_ik = knot_matrix[1][4]-knot_matrix[1][3]; + knot_spacing_jk = knot_matrix[0][4]-knot_matrix[0][3]; + get_starting_index=&uf3_triplet_bspline::get_starting_index_uniform; + } + else if (knot_spacing_type==1){ + knot_spacing_ij = 0; + knot_spacing_ik = 0; + knot_spacing_jk = 0; + get_starting_index=&uf3_triplet_bspline::get_starting_index_nonuniform; + } + + else + lmp->error->all(FLERR, "UF3: Expected either '0'(uniform-knots) or \n\ + '1'(non-uniform knots)"); + + knot_vect_size_ij = knot_matrix[2].size(); + knot_vect_size_ik = knot_matrix[1].size(); + knot_vect_size_jk = knot_matrix[0].size(); + + int resolution_ij = knot_vect_size_ij - 4; + int resolution_ik = knot_vect_size_ik - 4; + int resolution_jk = knot_vect_size_jk - 4; + + // Cache Spline Basis Functions + for (int l = 0; l < resolution_ij; l++) { + bsplines_ij.push_back(uf3_bspline_basis3(lmp, &knot_matrix[2][l], 1)); + } + + for (int l = 0; l < resolution_ik; l++) { + // Reuse jk Basis if Knots match + if (knot_matrix[1][l] == knot_matrix[2][l] && knot_matrix[1][l + 1] == knot_matrix[2][l + 1] && + knot_matrix[1][l + 2] == knot_matrix[2][l + 2] && + knot_matrix[1][l + 3] == knot_matrix[2][l + 3]) + bsplines_ik.push_back(bsplines_ij[l]); + else + bsplines_ik.push_back(uf3_bspline_basis3(lmp, &knot_matrix[1][l], 1)); + } + + for (int l = 0; l < resolution_jk; l++) { + bsplines_jk.push_back(uf3_bspline_basis3(lmp, &knot_matrix[0][l], 1)); + } + + // Initialize Coefficients for Derivatives + for (int i = 0; i < coeff_matrix.size(); i++) { + std::vector> dncoeff_vect2; + for (int j = 0; j < coeff_matrix[0].size(); j++) { + std::vector dncoeff_vect; + for (int k = 0; k < coeff_matrix[0][0].size() - 1; k++) { + double dntemp4 = 3 / (knot_matrix[0][k + 4] - knot_matrix[0][k + 1]); + dncoeff_vect.push_back((coeff_matrix[i][j][k + 1] - coeff_matrix[i][j][k]) * dntemp4); + } + dncoeff_vect2.push_back(dncoeff_vect); + } + dncoeff_matrix_jk.push_back(dncoeff_vect2); + } + + for (int i = 0; i < coeff_matrix.size(); i++) { + std::vector> dncoeff_vect2; + for (int j = 0; j < coeff_matrix[0].size() - 1; j++) { + double dntemp4 = 3 / (knot_matrix[1][j + 4] - knot_matrix[1][j + 1]); + std::vector dncoeff_vect; + for (int k = 0; k < coeff_matrix[0][0].size(); k++) { + dncoeff_vect.push_back((coeff_matrix[i][j + 1][k] - coeff_matrix[i][j][k]) * dntemp4); + } + dncoeff_vect2.push_back(dncoeff_vect); + } + dncoeff_matrix_ik.push_back(dncoeff_vect2); + } + + for (int i = 0; i < coeff_matrix.size() - 1; i++) { + std::vector> dncoeff_vect2; + double dntemp4 = 3 / (knot_matrix[2][i + 4] - knot_matrix[2][i + 1]); + for (int j = 0; j < coeff_matrix[0].size(); j++) { + std::vector dncoeff_vect; + for (int k = 0; k < coeff_matrix[0][0].size(); k++) { + dncoeff_vect.push_back((coeff_matrix[i + 1][j][k] - coeff_matrix[i][j][k]) * dntemp4); + } + dncoeff_vect2.push_back(dncoeff_vect); + } + dncoeff_matrix_ij.push_back(dncoeff_vect2); + } + + std::vector> dnknot_matrix; + for (int i = 0; i < knot_matrix.size(); i++) { + std::vector dnknot_vect; + for (int j = 1; j < knot_matrix[0].size() - 1; j++) { + dnknot_vect.push_back(knot_matrix[i][j]); + } + dnknot_matrix.push_back(dnknot_vect); + } + + // Cache Derivative Spline Basis Functions + for (int l = 0; l < resolution_ij - 1; l++) { + dnbsplines_ij.push_back(uf3_bspline_basis2(lmp, &dnknot_matrix[2][l], 1)); + } + + for (int l = 0; l < resolution_ik - 1; l++) { + // Reuse jk Basis if Knots match + if (dnknot_matrix[1][l] == dnknot_matrix[2][l] && + dnknot_matrix[1][l + 1] == dnknot_matrix[2][l + 1] && + dnknot_matrix[1][l + 2] == dnknot_matrix[2][l + 2]) + dnbsplines_ik.push_back(dnbsplines_ij[l]); + else + dnbsplines_ik.push_back(uf3_bspline_basis2(lmp, &dnknot_matrix[1][l], 1)); + } + + for (int l = 0; l < resolution_jk - 1; l++) { + dnbsplines_jk.push_back(uf3_bspline_basis2(lmp, &dnknot_matrix[0][l], 1)); + } +} + +// Destructor +uf3_triplet_bspline::~uf3_triplet_bspline() {} + +// Evaluate 3D B-Spline value +double *uf3_triplet_bspline::eval(double value_rij, double value_rik, double value_rjk) +{ + + // Find starting knots + + //int iknot_ij = starting_knot(knot_matrix[2], knot_vect_size_ij, value_rij) - 3; + //int iknot_ik = starting_knot(knot_matrix[1], knot_vect_size_ik, value_rik) - 3; + //int iknot_jk = starting_knot(knot_matrix[0], knot_vect_size_jk, value_rjk) - 3; + int iknot_ij = (this->*get_starting_index)(knot_matrix[2], knot_vect_size_ij, value_rij,knot_spacing_ij) - 3; + int iknot_ik = (this->*get_starting_index)(knot_matrix[1], knot_vect_size_ik, value_rik,knot_spacing_ik) - 3; + int iknot_jk = (this->*get_starting_index)(knot_matrix[0], knot_vect_size_jk, value_rjk,knot_spacing_jk) - 3; + + double rsq_ij = value_rij * value_rij; + double rsq_ik = value_rik * value_rik; + double rsq_jk = value_rjk * value_rjk; + double rth_ij = rsq_ij * value_rij; + double rth_ik = rsq_ik * value_rik; + double rth_jk = rsq_jk * value_rjk; + + // Calculate energies + + double basis_ij[4]; + basis_ij[0] = bsplines_ij[iknot_ij].eval3(rth_ij, rsq_ij, value_rij); + basis_ij[1] = bsplines_ij[iknot_ij + 1].eval2(rth_ij, rsq_ij, value_rij); + basis_ij[2] = bsplines_ij[iknot_ij + 2].eval1(rth_ij, rsq_ij, value_rij); + basis_ij[3] = bsplines_ij[iknot_ij + 3].eval0(rth_ij, rsq_ij, value_rij); + + double basis_ik[4]; + basis_ik[0] = bsplines_ik[iknot_ik].eval3(rth_ik, rsq_ik, value_rik); + basis_ik[1] = bsplines_ik[iknot_ik + 1].eval2(rth_ik, rsq_ik, value_rik); + basis_ik[2] = bsplines_ik[iknot_ik + 2].eval1(rth_ik, rsq_ik, value_rik); + basis_ik[3] = bsplines_ik[iknot_ik + 3].eval0(rth_ik, rsq_ik, value_rik); + + double basis_jk[4]; + basis_jk[0] = bsplines_jk[iknot_jk].eval3(rth_jk, rsq_jk, value_rjk); + basis_jk[1] = bsplines_jk[iknot_jk + 1].eval2(rth_jk, rsq_jk, value_rjk); + basis_jk[2] = bsplines_jk[iknot_jk + 2].eval1(rth_jk, rsq_jk, value_rjk); + basis_jk[3] = bsplines_jk[iknot_jk + 3].eval0(rth_jk, rsq_jk, value_rjk); + + ret_val[0] = 0; + ret_val[1] = 0; + ret_val[2] = 0; + ret_val[3] = 0; + + for (int i = 0; i < 4; i++) { + const double basis_iji = basis_ij[i]; // prevent repeated access of same memory location + for (int j = 0; j < 4; j++) { + const double factor = basis_iji * basis_ik[j]; // prevent repeated access of same memory location + const double* slice = &coeff_matrix[i + iknot_ij][j + iknot_ik][iknot_jk]; // declare a contigues 1D slice of memory + double tmp[4]; // declare tmp array that holds the 4 tmp values so the can be computed simultaniously in 4 separate registeres. + tmp[0] = slice[0] * basis_jk[0]; + tmp[1] = slice[1] * basis_jk[1]; + tmp[2] = slice[2] * basis_jk[2]; + tmp[3] = slice[3] * basis_jk[3]; + double sum = tmp[0] + tmp[1] + tmp[2] + tmp[3]; + ret_val[0] += factor * sum; // use 1 fused multiply-add (FMA) + } + } + + // Calculate forces + + double dnbasis_ij[4]; + dnbasis_ij[0] = dnbsplines_ij[iknot_ij].eval2(rsq_ij, value_rij); + dnbasis_ij[1] = dnbsplines_ij[iknot_ij + 1].eval1(rsq_ij, value_rij); + dnbasis_ij[2] = dnbsplines_ij[iknot_ij + 2].eval0(rsq_ij, value_rij); + dnbasis_ij[3] = 0; + + double dnbasis_ik[4]; + dnbasis_ik[0] = dnbsplines_ik[iknot_ik].eval2(rsq_ik, value_rik); + dnbasis_ik[1] = dnbsplines_ik[iknot_ik + 1].eval1(rsq_ik, value_rik); + dnbasis_ik[2] = dnbsplines_ik[iknot_ik + 2].eval0(rsq_ik, value_rik); + dnbasis_ik[3] = 0; + + double dnbasis_jk[4]; + dnbasis_jk[0] = dnbsplines_jk[iknot_jk].eval2(rsq_jk, value_rjk); + dnbasis_jk[1] = dnbsplines_jk[iknot_jk + 1].eval1(rsq_jk, value_rjk); + dnbasis_jk[2] = dnbsplines_jk[iknot_jk + 2].eval0(rsq_jk, value_rjk); + dnbasis_jk[3] = 0; + + for (int i = 0; i < 3; i++) { + const double dnbasis_iji = dnbasis_ij[i]; + for (int j = 0; j < 4; j++) { + const double factor = dnbasis_iji * basis_ik[j]; + const double* slice = &dncoeff_matrix_ij[iknot_ij + i][iknot_ik + j][iknot_jk]; + double tmp[4]; + tmp[0] = slice[0] * basis_jk[0]; + tmp[1] = slice[1] * basis_jk[1]; + tmp[2] = slice[2] * basis_jk[2]; + tmp[3] = slice[3] * basis_jk[3]; + double sum = tmp[0] + tmp[1] + tmp[2] + tmp[3]; + ret_val[1] += factor * sum; + } + } + + for (int i = 0; i < 4; i++) { + const double basis_iji = basis_ij[i]; + for (int j = 0; j < 3; j++) { + const double factor = basis_iji * dnbasis_ik[j]; + const double* slice = &dncoeff_matrix_ik[iknot_ij + i][iknot_ik + j][iknot_jk]; + double tmp[4]; + tmp[0] = slice[0] * basis_jk[0]; + tmp[1] = slice[1] * basis_jk[1]; + tmp[2] = slice[2] * basis_jk[2]; + tmp[3] = slice[3] * basis_jk[3]; + double sum = tmp[0] + tmp[1] + tmp[2] + tmp[3]; + ret_val[2] += factor * sum; + } + } + + for (int i = 0; i < 4; i++) { + const double basis_iji = basis_ij[i]; + for (int j = 0; j < 4; j++) { + const double factor = basis_iji * basis_ik[j]; + const double* slice = &dncoeff_matrix_jk[iknot_ij + i][iknot_ik + j][iknot_jk]; + double tmp[3]; + tmp[0] = slice[0] * dnbasis_jk[0]; + tmp[1] = slice[1] * dnbasis_jk[1]; + tmp[2] = slice[2] * dnbasis_jk[2]; + double sum = tmp[0] + tmp[1] + tmp[2]; + ret_val[3] += factor * sum; + } + } + + return ret_val; +} + +// Find starting knot for spline evaluation + +int uf3_triplet_bspline::starting_knot(const std::vector knot_vect, int knot_vect_size, + double r) +{ + if (knot_vect.front() <= r && r < knot_vect.back()) { + for (int i = 3; i < knot_vect_size - 1; i++) { + if (knot_vect[i] <= r && r < knot_vect[i + 1]) return i; + } + } + + return 0; +} + +int uf3_triplet_bspline::get_starting_index_uniform(const std::vector knot_vect, int knot_vect_size, + double r, double knot_spacing) +{ + return 3+(int)((r-knot_vect[0])/knot_spacing); +} + +int uf3_triplet_bspline::get_starting_index_nonuniform(const std::vector knot_vect, int knot_vect_size, + double r, double knot_spacing) +{ + if (knot_vect.front() <= r && r < knot_vect.back()) { + //Determine the interval for value_rij + for (int i = 3; i < knot_vect_size - 1; ++i) { + if (knot_vect[i] <= r && r < knot_vect[i + 1]) { + return i; + } + } + } +} + +double uf3_triplet_bspline::memory_usage() +{ + double bytes = 0; + + bytes += (double) 3*sizeof(int); //knot_vect_size_ij, + //knot_vect_size_ik, + //knot_vect_size_jk; + + for (int i=0; i + +#ifndef UF3_TRIPLET_BSPLINE_H +#define UF3_TRIPLET_BSPLINE_H + +namespace LAMMPS_NS { +class uf3_triplet_bspline { + private: + LAMMPS *lmp; + int knot_vect_size_ij, knot_vect_size_ik, knot_vect_size_jk; + std::vector>> coeff_matrix, dncoeff_matrix_ij, dncoeff_matrix_ik, + dncoeff_matrix_jk; + std::vector> knot_matrix; + std::vector bsplines_ij, bsplines_ik, bsplines_jk; + std::vector dnbsplines_ij, dnbsplines_ik, dnbsplines_jk; + int get_starting_index_uniform(const std::vector, int, double, double); + int get_starting_index_nonuniform(const std::vector, int, double, double); + int (uf3_triplet_bspline::*get_starting_index)(const std::vector, int, double, double); + //double knot_spacing_ij=0,knot_spacing_ik=0,knot_spacing_jk=0; + //double _alignvar(, 8) ret_val[4]; // Force memory alignment on 8 byte boundaries + double ret_val[4]; + + int starting_knot(const std::vector, int, double); + + public: + //Dummy Constructor + uf3_triplet_bspline(); + uf3_triplet_bspline(LAMMPS *ulmp, const std::vector> &uknot_matrix, + const std::vector>> &ucoeff_matrix, + const int &uknot_spacing_type); + ~uf3_triplet_bspline(); + int knot_spacing_type; + double knot_spacing_ij=0,knot_spacing_ik=0,knot_spacing_jk=0; + double *eval(double value_rij, double value_rik, double value_rjk); + + double memory_usage(); +}; +} // namespace LAMMPS_NS +#endif