// clang-format off /* ---------------------------------------------------------------------- LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator https://www.lammps.org/ Sandia National Laboratories LAMMPS development team: developers@lammps.org Copyright (2003) Sandia Corporation. Under the terms of Contract DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains certain rights in this software. This software is distributed under the GNU General Public License. See the README file in the top-level LAMMPS directory. ------------------------------------------------------------------------- */ /* ---------------------------------------------------------------------- Contributing authors: Ngoc Cuong Nguyen (MIT) ------------------------------------------------------------------------- */ // LAMMPS header files #include "comm.h" #include "error.h" #include "math_const.h" #include "math_special.h" #include "memory.h" #include "tokenizer.h" #include // header file. Moved down here to avoid polluting other headers with its defines #include "eapod.h" using namespace LAMMPS_NS; using MathConst::MY_PI; using MathSpecial::cube; using MathSpecial::powint; static constexpr int MAXLINE=1024; // constructor EAPOD::EAPOD(LAMMPS *_lmp, const std::string &pod_file, const std::string &coeff_file) : Pointers(_lmp), elemindex(nullptr), Phi(nullptr), Lambda(nullptr), coeff(nullptr), tmpmem(nullptr), Proj(nullptr), Centroids(nullptr), bd(nullptr), bdd(nullptr), pd(nullptr), pdd(nullptr), pn3(nullptr), pq3(nullptr), pc3(nullptr), pq4(nullptr), pa4(nullptr), pb4(nullptr), pc4(nullptr), tmpint(nullptr), ind23(nullptr), ind32(nullptr), ind33(nullptr), ind34(nullptr), ind43(nullptr), ind44(nullptr), ind33l(nullptr), ind33r(nullptr), ind34l(nullptr), ind34r(nullptr), ind44l(nullptr), ind44r(nullptr) { rin = 0.5; rcut = 5.0; nClusters = 1; nComponents = 1; nelements = 1; onebody = 1; besseldegree = 4; inversedegree = 8; nbesselpars = 3; true4BodyDesc = 1; ns = nbesselpars*besseldegree + inversedegree; Njmax = 100; nrbf2 = 8; nrbf3 = 6; nrbf4 = 4; nabf3 = 5; nabf4 = 3; nrbf23 = 0; nabf23 = 0; nrbf33 = 0; nabf33 = 0; nrbf34 = 0; nabf34 = 0; nabf43 = 0; nrbf44 = 0; nabf44 = 0; P3 = 4; P4 = 3; P23 = 0; P33 = 0; P34 = 0; P44 = 0; pdegree[0] = besseldegree; pdegree[1] = inversedegree; pbc[0] = 1; pbc[1] = 1; pbc[2] = 1; besselparams[0] = 1e-3; besselparams[1] = 2.0; besselparams[2] = 4.0; // read pod input file to podstruct read_pod_file(pod_file); if (coeff_file != "") { read_model_coeff_file(coeff_file); } } // destructor EAPOD::~EAPOD() { memory->destroy(elemindex); memory->destroy(Phi); memory->destroy(Lambda); memory->destroy(Proj); memory->destroy(Centroids); memory->destroy(bd); memory->destroy(bdd); memory->destroy(pd); memory->destroy(pdd); memory->destroy(coeff); memory->destroy(tmpmem); memory->destroy(tmpint); memory->destroy(pn3); memory->destroy(pq3); memory->destroy(pc3); memory->destroy(pa4); memory->destroy(pb4); memory->destroy(pc4); memory->destroy(pq4); memory->destroy(ind23); memory->destroy(ind32); memory->destroy(ind33); memory->destroy(ind34); memory->destroy(ind43); memory->destroy(ind44); memory->destroy(ind33l); memory->destroy(ind34l); memory->destroy(ind44l); memory->destroy(ind33r); memory->destroy(ind34r); memory->destroy(ind44r); } void EAPOD::read_pod_file(std::string pod_file) { std::string podfilename = pod_file; FILE *fppod; if (comm->me == 0) { fppod = utils::open_potential(podfilename,lmp,nullptr); if (fppod == nullptr) error->one(FLERR,"Cannot open POD coefficient file {}: ", podfilename, utils::getsyserror()); } // loop through lines of POD file and parse keywords char line[MAXLINE],*ptr; int eof = 0; while (true) { if (comm->me == 0) { ptr = fgets(line,MAXLINE,fppod); if (ptr == nullptr) { eof = 1; fclose(fppod); } } MPI_Bcast(&eof,1,MPI_INT,0,world); if (eof) break; MPI_Bcast(line,MAXLINE,MPI_CHAR,0,world); // words = ptrs to all words in line // strip single and double quotes from words std::vector words; try { words = Tokenizer(utils::trim_comment(line),"\"' \t\n\r\f").as_vector(); } catch (TokenizerException &) { // ignore } if (words.size() == 0) continue; auto keywd = words[0]; if (keywd == "species") { nelements = words.size()-1; for (int ielem = 1; ielem <= nelements; ielem++) { species.push_back(words[ielem]); } } if (keywd == "pbc") { if (words.size() != 4) error->one(FLERR,"Improper POD file.", utils::getsyserror()); pbc[0] = utils::inumeric(FLERR,words[1],false,lmp); pbc[1] = utils::inumeric(FLERR,words[2],false,lmp); pbc[2] = utils::inumeric(FLERR,words[3],false,lmp); } if ((keywd != "#") && (keywd != "species") && (keywd != "pbc")) { if (words.size() != 2) error->one(FLERR,"Improper POD file.", utils::getsyserror()); if (keywd == "rin") rin = utils::numeric(FLERR,words[1],false,lmp); if (keywd == "rcut") rcut = utils::numeric(FLERR,words[1],false,lmp); if (keywd == "number_of_environment_clusters") nClusters = utils::inumeric(FLERR,words[1],false,lmp); if (keywd == "number_of_principal_components") nComponents = utils::inumeric(FLERR,words[1],false,lmp); if (keywd == "bessel_polynomial_degree") besseldegree = utils::inumeric(FLERR,words[1],false,lmp); if (keywd == "inverse_polynomial_degree") inversedegree = utils::inumeric(FLERR,words[1],false,lmp); if (keywd == "onebody") onebody = utils::inumeric(FLERR,words[1],false,lmp); if (keywd == "twobody_number_radial_basis_functions") nrbf2 = utils::inumeric(FLERR,words[1],false,lmp); if (keywd == "threebody_number_radial_basis_functions") nrbf3 = utils::inumeric(FLERR,words[1],false,lmp); if (keywd == "threebody_angular_degree") P3 = utils::inumeric(FLERR,words[1],false,lmp); if (keywd == "fourbody_number_radial_basis_functions") nrbf4 = utils::inumeric(FLERR,words[1],false,lmp); if (keywd == "fourbody_angular_degree") P4 = utils::inumeric(FLERR,words[1],false,lmp); if (keywd == "fivebody_number_radial_basis_functions") nrbf33 = utils::inumeric(FLERR,words[1],false,lmp); if (keywd == "fivebody_angular_degree") P33 = utils::inumeric(FLERR,words[1],false,lmp); if (keywd == "sixbody_number_radial_basis_functions") nrbf34 = utils::inumeric(FLERR,words[1],false,lmp); if (keywd == "sixbody_angular_degree") P34 = utils::inumeric(FLERR,words[1],false,lmp); if (keywd == "sevenbody_number_radial_basis_functions") nrbf44 = utils::inumeric(FLERR,words[1],false,lmp); if (keywd == "sevenbody_angular_degree") P44 = utils::inumeric(FLERR,words[1],false,lmp); } } if (nrbf3 < nrbf4) error->all(FLERR,"number of four-body radial basis functions must be equal or less than number of three-body radial basis functions"); if (nrbf4 < nrbf33) error->all(FLERR,"number of five-body radial basis functions must be equal or less than number of four-body radial basis functions"); if (nrbf4 < nrbf34) error->all(FLERR,"number of six-body radial basis functions must be equal or less than number of four-body radial basis functions"); if (nrbf4 < nrbf44) error->all(FLERR,"number of seven-body radial basis functions must be equal or less than number of four-body radial basis functions"); nrbfmax = (nrbf2 < nrbf3) ? nrbf3 : nrbf2; nrbfmax = (nrbfmax < nrbf4) ? nrbf4 : nrbfmax; nrbfmax = (nrbfmax < nrbf33) ? nrbf33 : nrbfmax; nrbfmax = (nrbfmax < nrbf34) ? nrbf34 : nrbfmax; nrbfmax = (nrbfmax < nrbf44) ? nrbf44 : nrbfmax; if (P3 < P4) error->all(FLERR,"four-body angular degree must be equal or less than three-body angular degree"); if (P4 < P33) error->all(FLERR,"five-body angular degree must be equal or less than four-body angular degree"); if (P4 < P34) error->all(FLERR,"six-body angular degree must be equal or less than four-body angular degree"); if (P4 < P44) error->all(FLERR,"seven-body angular degree must be equal or less than four-body angular degree"); if (P3 > 12) error->all(FLERR,"three-body angular degree must be equal or less than 12"); if (P4 > 6) error->all(FLERR,"four-body angular degree must be equal or less than 6"); int Ne = nelements; memory->create(elemindex, Ne*Ne, "elemindex"); int k = 0; for (int i1 = 0; i1create(ind23, n23, "ind23"); memory->create(ind32, n32, "ind32"); memory->create(ind33, n33, "ind33"); memory->create(ind34, n34, "ind34"); memory->create(ind43, n43, "ind43"); memory->create(ind44, n44, "ind44"); indexmap3(ind23, 1, nrbf23, Ne, 1, nrbf2); indexmap3(ind32, nabf23, nrbf23, Ne*(Ne+1)/2, nabf3, nrbf3); indexmap3(ind33, nabf33, nrbf33, Ne*(Ne+1)/2, nabf3, nrbf3); indexmap3(ind34, nabf34, nrbf34, Ne*(Ne+1)/2, nabf3, nrbf3); indexmap3(ind43, nabf43, nrbf34, Ne*(Ne+1)*(Ne+2)/6, nabf4, nrbf4); indexmap3(ind44, nabf44, nrbf44, Ne*(Ne+1)*(Ne+2)/6, nabf4, nrbf4); nld33 = 0; nld34 = 0; nld44 = 0; int nebf3 = Ne*(Ne+1)/2; int nebf4 = Ne*(Ne+1)*(Ne+2)/6; int dabf3[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12}; int dabf4[] = {0, 1, 2, 2, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 5, 6, 6, 6, 6, 6, 6, 6}; if (nrbf33>0) { nld33 = crossindices(dabf3, nabf3, nrbf3, nebf3, dabf3, nabf3, nrbf3, nebf3, P33, nrbf33); memory->create(ind33l, nld33, "ind33l"); memory->create(ind33r, nld33, "ind33r"); crossindices(ind33l, ind33r, dabf3, nabf3, nrbf3, nebf3, dabf3, nabf3, nrbf3, nebf3, P33, nrbf33); } if (nrbf34>0) { nld34 = crossindices(dabf3, nabf3, nrbf3, nebf3, dabf4, nabf4, nrbf4, nebf4, P34, nrbf34); memory->create(ind34l, nld34, "ind34l"); memory->create(ind34r, nld34, "ind34r"); crossindices(ind34l, ind34r, dabf3, nabf3, nrbf3, nebf3, dabf4, nabf4, nrbf4, nebf4, P34, nrbf34); } if (nrbf44>0) { nld44 = crossindices(dabf4, nabf4, nrbf4, nebf4, dabf4, nabf4, nrbf4, nebf4, P44, nrbf44); memory->create(ind44l, nld44, "ind44l"); memory->create(ind44r, nld44, "ind44r"); crossindices(ind44l, ind44r, dabf4, nabf4, nrbf4, nebf4, dabf4, nabf4, nrbf4, nebf4, P44, nrbf44); } ngd33 = nld33*Ne; ngd34 = nld34*Ne; ngd44 = nld44*Ne; nl33 = nld33; nl34 = nld34; nl44 = nld44; nd33 = ngd33; nd34 = ngd34; nd44 = ngd44; Mdesc = nl2 + nl3 + nl4 + nl23 + nl33 + nl34 + nl44; nl = nl1 + nl2 + nl3 + nl4 + nl23 + nl33 + nl34 + nl44; nd = nd1 + nd2 + nd3 + nd4 + nd23 + nd33 + nd34 + nd44; nCoeffPerElement = nl1 + Mdesc*nClusters; nCoeffAll = nCoeffPerElement*nelements; allocate_temp_memory(Njmax); if (comm->me == 0) { utils::logmesg(lmp, "**************** Begin of POD Potentials ****************\n"); utils::logmesg(lmp, "species: "); for (int i=0; ime == 0) { fpcoeff = utils::open_potential(coefffilename,lmp,nullptr); if (fpcoeff == nullptr) error->one(FLERR,"Cannot open model coefficient file {}: ", coefffilename, utils::getsyserror()); } // check format for first line of file char line[MAXLINE],*ptr; int eof = 0; int nwords = 0; while (nwords == 0) { if (comm->me == 0) { ptr = fgets(line,MAXLINE,fpcoeff); if (ptr == nullptr) { eof = 1; fclose(fpcoeff); } } MPI_Bcast(&eof,1,MPI_INT,0,world); if (eof) break; MPI_Bcast(line,MAXLINE,MPI_CHAR,0,world); // strip comment, skip line if blank nwords = utils::count_words(utils::trim_comment(line)); } if (nwords != 4) error->all(FLERR,"Incorrect format in POD coefficient file"); // strip single and double quotes from words int ncoeffall, nprojall, ncentall; std::string tmp_str; try { ValueTokenizer words(utils::trim_comment(line),"\"' \t\n\r\f"); tmp_str = words.next_string(); ncoeffall = words.next_int(); nprojall = words.next_int(); ncentall = words.next_int(); } catch (TokenizerException &e) { error->all(FLERR,"Incorrect format in POD coefficient file: {}", e.what()); } // loop over single block of coefficients and insert values in coeff memory->create(coeff, ncoeffall, "pod:pod_coeff"); for (int icoeff = 0; icoeff < ncoeffall; icoeff++) { if (comm->me == 0) { ptr = fgets(line,MAXLINE,fpcoeff); if (ptr == nullptr) { eof = 1; fclose(fpcoeff); } } MPI_Bcast(&eof,1,MPI_INT,0,world); if (eof) error->all(FLERR,"Incorrect format in model coefficient file"); MPI_Bcast(line,MAXLINE,MPI_CHAR,0,world); try { ValueTokenizer cff(utils::trim_comment(line)); if (cff.count() != 1) error->all(FLERR,"Incorrect format in model coefficient file"); coeff[icoeff] = cff.next_double(); } catch (TokenizerException &e) { error->all(FLERR,"Incorrect format in model coefficient file: {}", e.what()); } } memory->create(Proj, nprojall, "pod:pca_proj"); for (int iproj = 0; iproj < nprojall; iproj++) { if (comm->me == 0) { ptr = fgets(line,MAXLINE,fpcoeff); if (ptr == nullptr) { eof = 1; fclose(fpcoeff); } } MPI_Bcast(&eof,1,MPI_INT,0,world); if (eof) error->all(FLERR,"Incorrect format in model coefficient file"); MPI_Bcast(line,MAXLINE,MPI_CHAR,0,world); try { ValueTokenizer cff(utils::trim_comment(line)); if (cff.count() != 1) error->all(FLERR,"Incorrect format in model coefficient file"); Proj[iproj] = cff.next_double(); } catch (TokenizerException &e) { error->all(FLERR,"Incorrect format in model coefficient file: {}", e.what()); } } memory->create(Centroids, ncentall, "pod:pca_cent"); for (int icent = 0; icent < ncentall; icent++) { if (comm->me == 0) { ptr = fgets(line,MAXLINE,fpcoeff); if (ptr == nullptr) { eof = 1; fclose(fpcoeff); } } MPI_Bcast(&eof,1,MPI_INT,0,world); if (eof) error->all(FLERR,"Incorrect format in model coefficient file"); MPI_Bcast(line,MAXLINE,MPI_CHAR,0,world); try { ValueTokenizer cff(utils::trim_comment(line)); if (cff.count() != 1) error->all(FLERR,"Incorrect format in model coefficient file"); Centroids[icent] = cff.next_double(); } catch (TokenizerException &e) { error->all(FLERR,"Incorrect format in model coefficient file: {}", e.what()); } } if (comm->me == 0) { if (!eof) fclose(fpcoeff); } if (ncoeffall != nCoeffAll) error->all(FLERR,"number of coefficients in the coefficient file is not correct"); if (nClusters > 1) { if (nprojall != nComponents*Mdesc*nelements) error->all(FLERR,"number of coefficients in the projection file is not correct"); if (ncentall != nComponents*nClusters*nelements) error->all(FLERR,"number of coefficients in the projection file is not correct"); } if (comm->me == 0) { utils::logmesg(lmp, "**************** Begin of Model Coefficients ****************\n"); utils::logmesg(lmp, "total number of coefficients for POD potential: {}\n", ncoeffall); utils::logmesg(lmp, "total number of elements for PCA projection matrix: {}\n", nprojall); utils::logmesg(lmp, "total number of elements for PCA centroids: {}\n", ncentall); utils::logmesg(lmp, "**************** End of Model Coefficients ****************\n\n"); } } int EAPOD::read_coeff_file(std::string coeff_file) { std::string coefffilename = coeff_file; FILE *fpcoeff; if (comm->me == 0) { fpcoeff = utils::open_potential(coefffilename,lmp,nullptr); if (fpcoeff == nullptr) error->one(FLERR,"Cannot open POD coefficient file {}: ", coefffilename, utils::getsyserror()); } // check format for first line of file char line[MAXLINE],*ptr; int eof = 0; int nwords = 0; while (nwords == 0) { if (comm->me == 0) { ptr = fgets(line,MAXLINE,fpcoeff); if (ptr == nullptr) { eof = 1; fclose(fpcoeff); } } MPI_Bcast(&eof,1,MPI_INT,0,world); if (eof) break; MPI_Bcast(line,MAXLINE,MPI_CHAR,0,world); // strip comment, skip line if blank nwords = utils::count_words(utils::trim_comment(line)); } if (nwords != 2) error->all(FLERR,"Incorrect format in POD coefficient file"); // strip single and double quotes from words int ncoeffall; std::string tmp_str; try { ValueTokenizer words(utils::trim_comment(line),"\"' \t\n\r\f"); tmp_str = words.next_string(); ncoeffall = words.next_int(); } catch (TokenizerException &e) { error->all(FLERR,"Incorrect format in POD coefficient file: {}", e.what()); } // loop over single block of coefficients and insert values in coeff memory->create(coeff, ncoeffall, "pod:pod_coeff"); for (int icoeff = 0; icoeff < ncoeffall; icoeff++) { if (comm->me == 0) { ptr = fgets(line,MAXLINE,fpcoeff); if (ptr == nullptr) { eof = 1; fclose(fpcoeff); } } MPI_Bcast(&eof,1,MPI_INT,0,world); if (eof) error->all(FLERR,"Incorrect format in POD coefficient file"); MPI_Bcast(line,MAXLINE,MPI_CHAR,0,world); try { ValueTokenizer cff(utils::trim_comment(line)); if (cff.count() != 1) error->all(FLERR,"Incorrect format in POD coefficient file"); coeff[icoeff] = cff.next_double(); } catch (TokenizerException &e) { error->all(FLERR,"Incorrect format in POD coefficient file: {}", e.what()); } } if (comm->me == 0) { if (!eof) fclose(fpcoeff); } if (comm->me == 0) { utils::logmesg(lmp, "**************** Begin of POD Coefficients ****************\n"); utils::logmesg(lmp, "total number of coefficients for POD potential: {}\n", ncoeffall); utils::logmesg(lmp, "**************** End of POD Coefficients ****************\n\n"); } return ncoeffall; } // funcion to read the projection matrix from file. int EAPOD::read_projection_matrix(std::string proj_file) { std::string projfilename = proj_file; FILE *fpproj; if (comm->me == 0) { fpproj = utils::open_potential(projfilename,lmp,nullptr); if (fpproj == nullptr) error->one(FLERR,"Cannot open PCA projection matrix file {}: ", projfilename, utils::getsyserror()); } // check format for first line of file char line[MAXLINE],*ptr; int eof = 0; int nwords = 0; while (nwords == 0) { if (comm->me == 0) { ptr = fgets(line,MAXLINE,fpproj); if (ptr == nullptr) { eof = 1; fclose(fpproj); } } MPI_Bcast(&eof,1,MPI_INT,0,world); if (eof) break; MPI_Bcast(line,MAXLINE,MPI_CHAR,0,world); // strip comment, skip line if blank nwords = utils::count_words(utils::trim_comment(line)); } if (nwords != 2) error->all(FLERR,"Incorrect format in PCA projection matrix file"); // strip single and double quotes from words int nprojall; std::string tmp_str; try { ValueTokenizer words(utils::trim_comment(line),"\"' \t\n\r\f"); tmp_str = words.next_string(); nprojall = words.next_int(); } catch (TokenizerException &e) { error->all(FLERR,"Incorrect format in PCA projection matrix file: {}", e.what()); } // loop over single block of coefficients and insert values in coeff memory->create(Proj, nprojall, "pod:pca_proj"); for (int iproj = 0; iproj < nprojall; iproj++) { if (comm->me == 0) { ptr = fgets(line,MAXLINE,fpproj); if (ptr == nullptr) { eof = 1; fclose(fpproj); } } MPI_Bcast(&eof,1,MPI_INT,0,world); if (eof) error->all(FLERR,"Incorrect format in PCA projection matrix file"); MPI_Bcast(line,MAXLINE,MPI_CHAR,0,world); try { ValueTokenizer cff(utils::trim_comment(line)); if (cff.count() != 1) error->all(FLERR,"Incorrect format in PCA projection matrix file"); Proj[iproj] = cff.next_double(); } catch (TokenizerException &e) { error->all(FLERR,"Incorrect format in PCA projection matrix file: {}", e.what()); } } if (comm->me == 0) { if (!eof) fclose(fpproj); } if (comm->me == 0) { utils::logmesg(lmp, "**************** Begin of PCA projection matrix ****************\n"); utils::logmesg(lmp, "total number of elements for PCA projection matrix: {}\n", nprojall); utils::logmesg(lmp, "**************** End of PCA projection matrix ****************\n\n"); } return nprojall; } // read Centroids from file int EAPOD::read_centroids(std::string centroids_file) { std::string centfilename = centroids_file; FILE *fpcent; if (comm->me == 0) { fpcent = utils::open_potential(centfilename,lmp,nullptr); if (fpcent == nullptr) error->one(FLERR,"Cannot open PCA centroids file {}: ", centfilename, utils::getsyserror()); } // check format for first line of file char line[MAXLINE],*ptr; int eof = 0; int nwords = 0; while (nwords == 0) { if (comm->me == 0) { ptr = fgets(line,MAXLINE,fpcent); if (ptr == nullptr) { eof = 1; fclose(fpcent); } } MPI_Bcast(&eof,1,MPI_INT,0,world); if (eof) break; MPI_Bcast(line,MAXLINE,MPI_CHAR,0,world); // strip comment, skip line if blank nwords = utils::count_words(utils::trim_comment(line)); } if (nwords != 2) error->all(FLERR,"Incorrect format in PCA centroids file"); // strip single and double quotes from words int ncentall; std::string tmp_str; try { ValueTokenizer words(utils::trim_comment(line),"\"' \t\n\r\f"); tmp_str = words.next_string(); ncentall = words.next_int(); } catch (TokenizerException &e) { error->all(FLERR,"Incorrect format in PCA centroids file: {}", e.what()); } // loop over single block of coefficients and insert values in coeff memory->create(Centroids, ncentall, "pod:pca_cent"); for (int icent = 0; icent < ncentall; icent++) { if (comm->me == 0) { ptr = fgets(line,MAXLINE,fpcent); if (ptr == nullptr) { eof = 1; fclose(fpcent); } } MPI_Bcast(&eof,1,MPI_INT,0,world); if (eof) error->all(FLERR,"Incorrect format in PCA centroids file"); MPI_Bcast(line,MAXLINE,MPI_CHAR,0,world); try { ValueTokenizer cff(utils::trim_comment(line)); if (cff.count() != 1) error->all(FLERR,"Incorrect format in PCA centroids file"); Centroids[icent] = cff.next_double(); } catch (TokenizerException &e) { error->all(FLERR,"Incorrect format in PCA centroids file: {}", e.what()); } } if (comm->me == 0) { if (!eof) fclose(fpcent); } if (comm->me == 0) { utils::logmesg(lmp, "**************** Begin of PCA centroids ****************\n"); utils::logmesg(lmp, "total number of elements for PCA centroids: {}\n", ncentall); utils::logmesg(lmp, "**************** End of PCA centroids ****************\n\n"); } return ncentall; } void EAPOD::peratombase_descriptors(double *bd1, double *bdd1, double *rij, double *temp, int *tj, int Nj) { for (int i=0; i0) && (Nj>0)) { twobodydescderiv(d2, dd2, rbf, rbfx, rbfy, rbfz, tj, Nj); } if ((nl3 > 0) && (Nj>1)) { double *abf = &temp[4*n1 + n5 + 4*n2]; // Nj*K3 double *abfx = &temp[4*n1 + n5 + 4*n2 + n4]; // Nj*K3 double *abfy = &temp[4*n1 + n5 + 4*n2 + 2*n4]; // Nj*K3 double *abfz = &temp[4*n1 + n5 + 4*n2 + 3*n4]; // Nj*K3 double *tm = &temp[4*n1 + n5 + 4*n2 + 4*n4]; // 4*K3 angularbasis(abf, abfx, abfy, abfz, rij, tm, pq3, Nj, K3); radialangularbasis(sumU, U, Ux, Uy, Uz, rbf, rbfx, rbfy, rbfz, abf, abfx, abfy, abfz, tj, Nj, K3, nrbf3, nelements); threebodydesc(d3, sumU); threebodydescderiv(dd3, sumU, Ux, Uy, Uz, tj, Nj); if ((nl23>0) && (Nj>2)) { fourbodydesc23(d23, d2, d3); fourbodydescderiv23(dd23, d2, d3, dd2, dd3, 3*Nj); } if ((nl33>0) && (Nj>3)) { crossdesc(d33, d3, d3, ind33l, ind33r, nl33); crossdescderiv(dd33, d3, d3, dd3, dd3, ind33l, ind33r, nl33, 3*Nj); } if ((nl4 > 0) && (Nj>2)) { if (K4 < K3) { for (int m=0; m0) && (Nj>4)) { crossdesc(d34, d3, d4, ind34l, ind34r, nl34); crossdescderiv(dd34, d3, d4, dd3, dd4, ind34l, ind34r, nl34, 3*Nj); } if ((nl44>0) && (Nj>5)) { crossdesc(d44, d4, d4, ind44l, ind44r, nl44); crossdescderiv(dd44, d4, d4, dd4, dd4, ind44l, ind44r, nl44, 3*Nj); } } } } double EAPOD::peratombase_coefficients(double *cb, double *bd, int *ti) { int nc = nCoeffPerElement*(ti[0]-1); double ei = coeff[0 + nc]; for (int m=0; m0) && (Nj>0)) { twobodydesc(d2, rbf, tj, Nj); } if ((nl3 > 0) && (Nj>1)) { double *abf = &temp[4*n1 + n5 + 4*n2]; // Nj*K3 double *abfx = &temp[4*n1 + n5 + 4*n2 + n4]; // Nj*K3 double *abfy = &temp[4*n1 + n5 + 4*n2 + 2*n4]; // Nj*K3 double *abfz = &temp[4*n1 + n5 + 4*n2 + 3*n4]; // Nj*K3 double *tm = &temp[4*n1 + n5 + 4*n2 + 4*n4]; // 4*K3 angularbasis(abf, abfx, abfy, abfz, rij, tm, pq3, Nj, K3); radialangularbasis(sumU, U, Ux, Uy, Uz, rbf, rbfx, rbfy, rbfz, abf, abfx, abfy, abfz, tj, Nj, K3, nrbf3, nelements); threebodydesc(d3, sumU); if ((nl23>0) && (Nj>2)) { fourbodydesc23(d23, d2, d3); } if ((nl33>0) && (Nj>3)) { crossdesc(d33, d3, d3, ind33l, ind33r, nl33); } if ((nl4 > 0) && (Nj>2)) { fourbodydesc(d4, sumU); if ((nl34>0) && (Nj>4)) { crossdesc(d34, d3, d4, ind34l, ind34r, nl34); } if ((nl44>0) && (Nj>5)) { crossdesc(d44, d4, d4, ind44l, ind44r, nl44); } } } double *cb = &bdd[0]; if (nClusters > 1) { e += peratom_environment_descriptors(cb, bd, &temp[4*n1 + n5 + 4*n2], ti); } else { e += peratombase_coefficients(cb, bd, ti); } double *cb2 = &cb[0]; // nl3 double *cb3 = &cb[nl2]; // nl3 double *cb4 = &cb[(nl2 + nl3)]; // nl4 double *cb33 = &cb[(nl2 + nl3 + nl4)]; // nl33 double *cb34 = &cb[(nl2 + nl3 + nl4 + nl33)]; // nl34 double *cb44 = &cb[(nl2 + nl3 + nl4 + nl33 + nl34)]; // nl44 if ((nl33>0) && (Nj>3)) { crossdesc_reduction(cb3, cb3, cb33, d3, d3, ind33l, ind33r, nl33); } if ((nl34>0) && (Nj>4)) { crossdesc_reduction(cb3, cb4, cb34, d3, d4, ind34l, ind34r, nl34); } if ((nl44>0) && (Nj>5)) { crossdesc_reduction(cb4, cb4, cb44, d4, d4, ind44l, ind44r, nl44); } if ((nl2 > 0) && (Nj>0)) twobody_forces(fij, cb2, rbfx, rbfy, rbfz, tj, Nj); // Initialize forcecoeff to zero double *forcecoeff = &cb[(nl2 + nl3 + nl4)]; // nl33 std::fill(forcecoeff, forcecoeff + nelements * K3 * nrbf3, 0.0); if ((nl3 > 0) && (Nj>1)) threebody_forcecoeff(forcecoeff, cb3, sumU); if ((nl4 > 0) && (Nj>2)) fourbody_forcecoeff(forcecoeff, cb4, sumU); if ((nl3 > 0) && (Nj>1)) allbody_forces(fij, forcecoeff, Ux, Uy, Uz, tj, Nj); return e; } double EAPOD::peratomenergyforce(double *fij, double *rij, double *temp, int *ti, int *tj, int Nj) { if (Nj==0) { return coeff[nCoeffPerElement*(ti[0]-1)]; } int N = 3*Nj; for (int n=0; n 1) { // multi-environment descriptors // calculate multi-environment descriptors and their derivatives with respect to atom coordinates peratomenvironment_descriptors(pd, pdd, bd, bdd, temp, ti[0] - 1, Nj); for (int j = 0; jNjmax) { Njmax = Nj; free_temp_memory(); allocate_temp_memory(Njmax); } double *rij = &tmpmem[0]; // 3*Nj double *fij = &tmpmem[3*Nj]; // 3*Nj int *ai = &tmpint[0]; // Nj int *aj = &tmpint[Nj]; // Nj int *ti = &tmpint[2*Nj]; // Nj int *tj = &tmpint[3*Nj]; // Nj myneighbors(rij, x, ai, aj, ti, tj, jlist, pairnumsum, atomtype, alist, i); etot += peratomenergyforce(fij, rij, &tmpmem[6*Nj], ti, tj, Nj); tallyforce(force, fij, ai, aj, Nj); } } return etot; } void EAPOD::base_descriptors(double *basedesc, double *x, int *atomtype, int *alist, int *jlist, int *pairnumsum, int natom) { for (int i=0; i0) { // reallocate temporary memory if (Nj>Njmax) { Njmax = Nj; free_temp_memory(); allocate_temp_memory(Njmax); if (comm->me == 0) utils::logmesg(lmp, "reallocate temporary memory with Njmax = %d ...\n", Njmax); } double *rij = &tmpmem[0]; // 3*Nj int *ai = &tmpint[0]; // Nj int *aj = &tmpint[Nj]; // Nj int *ti = &tmpint[2*Nj]; // Nj int *tj = &tmpint[3*Nj]; // Nj myneighbors(rij, x, ai, aj, ti, tj, jlist, pairnumsum, atomtype, alist, i); // many-body descriptors peratombase_descriptors(bd, bdd, rij, &tmpmem[3*Nj], tj, Nj); for (int m=0; m0) { gd[nCoeffPerElement*(atomtype[i]-1)] += 1.0; } if (Nj>0) { // reallocate temporary memory if (Nj>Njmax) { Njmax = Nj; free_temp_memory(); allocate_temp_memory(Njmax); if (comm->me == 0) utils::logmesg(lmp, "reallocate temporary memory with Njmax = %d ...\n", Njmax); } double *rij = &tmpmem[0]; // 3*Nj int *ai = &tmpint[0]; // Nj int *aj = &tmpint[Nj]; // Nj int *ti = &tmpint[2*Nj]; // Nj int *tj = &tmpint[3*Nj]; // Nj myneighbors(rij, x, ai, aj, ti, tj, jlist, pairnumsum, atomtype, alist, i); // many-body descriptors peratombase_descriptors(bd, bdd, rij, &tmpmem[3*Nj], tj, Nj); for (int m=0; m0) { gd[nCoeffPerElement*(atomtype[i]-1)] += 1.0; } if (Nj>0) { // reallocate temporary memory if (Nj>Njmax) { Njmax = Nj; free_temp_memory(); allocate_temp_memory(Njmax); if (comm->me == 0) utils::logmesg(lmp, "reallocate temporary memory with Njmax = %d ...\n", Njmax); } double *rij = &tmpmem[0]; // 3*Nj int *ai = &tmpint[0]; // Nj int *aj = &tmpint[Nj]; // Nj int *ti = &tmpint[2*Nj]; // Nj int *tj = &tmpint[3*Nj]; // Nj myneighbors(rij, x, ai, aj, ti, tj, jlist, pairnumsum, atomtype, alist, i); // many-body descriptors peratombase_descriptors(bd, bdd, rij, &tmpmem[3*Nj], tj, Nj); // calculate multi-environment descriptors and their derivatives with respect to atom coordinates peratomenvironment_descriptors(pd, pdd, bd, bdd, tmpmem, ti[0] - 1, Nj); for (int j = 0; j < nClusters; j++) { probdesc[i + natom*(j)] = pd[j]; for (int m=0; mcreate(coeff, nc, "coeff"); // Copy the coefficients for (int n=0; ncreate(xij, N, "eapod:xij"); memory->create(S, N*ns, "eapod:S"); memory->create(Q, N*ns, "eapod:Q"); memory->create(A, ns*ns, "eapod:A"); memory->create(work, ns*ns, "eapod:work"); memory->create(b, ns, "eapod:ns"); // Generate the xij array for (int i=0; i= max(1,3*N-1) int info = 1; // = 0: successful exit //double work[ns*ns]; char chv = 'V'; char chu = 'U'; DSYEV(&chv, &chu, &ns, A, &ns, b, work, &lwork, &info); // Order eigenvalues and eigenvectors from largest to smallest for (int j=0; jdestroy(xij); memory->destroy(S); memory->destroy(A); memory->destroy(work); memory->destroy(b); memory->destroy(Q); } /** * @brief Initialize the two-body coefficients. * * @param None */ void EAPOD::init2body() { // Set the degree of the Bessel function and the inverse distance function pdegree[0] = besseldegree; pdegree[1] = inversedegree; // Compute the total number of snapshots ns = nbesselpars * pdegree[0] + pdegree[1]; // Allocate memory for the eigenvectors and eigenvalues memory->create(Phi, ns * ns, "Phi"); memory->create(Lambda, ns, "Lambda"); // Perform eigenvalue decomposition of the snapshots matrix S and store the eigenvectors and eigenvalues eigenvaluedecomposition(Phi, Lambda, 2000); } /** * @brief Initialize arrays for the three-body descriptors. * * @param Pa3 The degree of the angular basis functions of the three-body descriptors. */ void EAPOD::init3body(int Pa3) { // Define the number of monomials for each degree int npa[] = {0, 1, 4, 10, 20, 35, 56, 84, 120, 165, 220, 286, 364, 455}; // Set the number of coefficients, the number of basis functions, and the degree of the Bessel function nabf3 = Pa3+1; // Number of angular basis functions K3 = npa[nabf3]; // number of monimials P3 = nabf3-1; // the degree of angular basis functions of the three-body descriptors // Allocate memory for the coefficients, the basis functions, and the cutoff function memory->create(pn3, nabf3+1, "pn3"); // array stores the number of monomials for each degree memory->create(pq3, K3*2, "pq3"); // array needed for the recursive computation of the angular basis functions memory->create(pc3, K3, "pc3"); // array needed for the computation of the three-body descriptors // Initialize the arrays init3bodyarray(pn3, pq3, pc3, nabf3-1); } /** * @brief Initialize arrays for the four-body descriptors. * * @param Pa4 The degree of the angular basis functions of the four-body descriptors. */ void EAPOD::init4body(int Pa4) { // Define the number of monomials for each degree int npa[] = {0, 1, 4, 10, 20, 35, 56, 84, 120, 165, 220, 286, 364, 455}; // Define the number of angular basis functions for each degree int nb[] = {1, 2, 4, 7, 11, 16, 23}; // Define the number of terms needed to compute angular basis functions int ns[] = {0, 1, 4, 10, 19, 29, 47, 74, 89, 119, 155, 209, 230, 275, 335, 425, 533, 561, 624, 714, 849, 949, 1129, 1345}; // Set the degree of the angular basis functions of the four-body descriptors P4 = Pa4; // Set the number of monomials for the angular basis functions of the four-body descriptors K4 = npa[Pa4+1]; // Allocate memory for the output arrays int *pn4, *tm4; memory->create(pn4, Pa4+2, "pn4"); // array stores the number of monomials for each degree memory->create(pq4, K4*2, "pq4"); // array needed for the recursive computation of the angular basis functions memory->create(tm4, K4, "tm4"); // Initialize the arrays init3bodyarray(pn4, pq4, tm4, Pa4); // Set the number of angular basis functions for the four-body descriptors nabf4 = nb[Pa4]; // the size of the array pc4 Q4 = ns[nabf4]; // Allocate memory for the coefficients, the basis functions, and the cutoff function memory->create(pa4, nabf4+1, "pa4"); // this array is a subset of the array ns memory->create(pb4, Q4*3, "pb4"); // array stores the indices of the monomials needed for the computation of the angular basis functions memory->create(pc4, Q4, "pc4"); // array of monomial coefficients needed for the computation of the four-body descriptors // Initialize the arrays init4bodyarray(pa4, pb4, pc4, Pa4); // Deallocate memory memory->destroy(pn4); memory->destroy(tm4); } /** * @brief Estimate the amount of memory needed for the computation. * * @param Nj Number of neighboring atoms. * @return int The estimated amount of memory needed. */ int EAPOD::estimate_temp_memory(int Nj) { // Determine the maximum number of radial basis functions and angular basis functions int Kmax = (K3 > K4) ? K3 : K4; int nrbf34 = (nrbf3 > nrbf4) ? nrbf3 : nrbf4; int nrbfmax = (nrbf2 > nrbf34) ? nrbf2 : nrbf34; int Knrbf34 = (K3*nrbf3 > K4*nrbf4) ? K3*nrbf3 : K4*nrbf4; // Determine the maximum number of local descriptors int nld = (nl23 > nl33) ? nl23 : nl33; nld = (nld > nl34) ? nld : nl34; nld = (nld > nl44) ? nld : nl44; // rij, fij, and d2, dd2, d3, dd3, d4, dd4 int nmax1 = 6*Nj + nl2 + 3*Nj*nl2 + nl3 + 3*Nj*nl3 + nl4 + 3*Nj*nl4 + nld + 3*Nj*nld; // U, Ux, Uy, Uz int nmax2 = 4*Nj*Knrbf34; // sumU and cU int nmax3 = 2*nelements*Knrbf34; // rbf, rbfx, rbfy, rbfz int nmax4 = 4*Nj*nrbfmax; // rbft, rbfxt, rbfyt, rbfzt int nmax5 = 4*Nj*ns; // abf, abfx, abfy, abfz int nmax6 = 4*(Nj+1)*Kmax; // Determine the maximum amount of memory needed for U, Ux, Uy, Uz, sumU, cU, rbf, rbfx, rbfy, rbfz, abf, abfx, abfy, abfz int nmax7 = (nmax5 > nmax6) ? nmax5 : nmax6; int nmax8 = nmax2 + nmax3 + nmax4 + nmax7; // Determine the total amount of memory needed for all double memory ndblmem = (nmax1 + nmax8); int nmax9 = 6*Nj + nComponents + nClusters + nClusters*nComponents + 2*nClusters*Mdesc + nClusters*nClusters; if (ndblmem < nmax9) ndblmem = nmax9; // Determine the total amount of memory needed for all integer memory nintmem = 4*Nj; // Return the estimated amount of memory needed return ndblmem; } void EAPOD::allocate_temp_memory(int Nj) { estimate_temp_memory(Nj); memory->create(tmpmem, ndblmem, "tmpmem"); memory->create(tmpint, nintmem, "tmpint"); memory->create(bd, Mdesc, "bdd"); memory->create(bdd, 3*Nj*Mdesc, "bdd"); memory->create(pd, nClusters, "bdd"); memory->create(pdd, 3*Nj*nClusters, "bdd"); } void EAPOD::free_temp_memory() { memory->destroy(tmpmem); memory->destroy(tmpint); memory->destroy(bd); memory->destroy(bdd); memory->destroy(pd); memory->destroy(pdd); } /** * @brief Map a 3D index to a 1D index. * * @param indx The 1D index array. * @param n1 The size of the first dimension. * @param n2 The size of the second dimension. * @param n3 The size of the third dimension. * @param N1 The stride of the first dimension. * @param N2 The stride of the second dimension. * @return int The total number of elements in the 1D index array. */ int EAPOD::indexmap3(int *indx, int n1, int n2, int n3, int N1, int N2) { int k = 0; for (int i3=0; i3= m1) && (i2 >= i1) && (a1 + a2 <= dabf12) && (j1+j2 < nrbf12)) { n += 1; } } } return n; } /** * @brief Calculate the number of cross descriptors between two sets of descriptors and store the indices in two arrays. * * @param ind1 Pointer to the array of indices of the first set of descriptors. * @param ind2 Pointer to the array of indices of the second set of descriptors. * @param dabf1 Pointer to the array of degrees of angular basis functions of the first set of descriptors. * @param nabf1 Number of angular basis functions in the first set of descriptors. * @param nrbf1 Number of radial basis functions in the first set of descriptors. * @param nebf1 Number of element interactions in the first set of descriptors. * @param dabf2 Pointer to the array of degrees of angular basis functions of the second set of descriptors. * @param nabf2 Number of angular basis functions in the second set of descriptors. * @param nrbf2 Number of radial basis functions in the second set of descriptors. * @param nebf2 Number of element interactions in the second set descriptors. * @param dabf12 degree of angular basis functions for the cross descriptors * @param nrbf12 number of radial basis functions for the cross descriptors * @return int The number of cross descriptors between two sets of descriptors. */ int EAPOD::crossindices(int *ind1, int *ind2, int *dabf1, int nabf1, int nrbf1, int nebf1, int *dabf2, int nabf2, int nrbf2, int nebf2, int dabf12, int nrbf12) { int n = 0; // Loop over the first set of descriptors for (int i1=0; i1= m1) && (i2 >= i1) && (a1 + a2 <= dabf12) && (j1+j2 < nrbf12)) { ind1[n] = n1; ind2[n] = n2; n += 1; } } } return n; } void EAPOD::scalarproduct(double *d, double c, int N) { for (int n=0; n