// // SNAPImplementation.cpp // // LGPL Version 2.1 HEADER START // // This library is free software; you can redistribute it and/or // modify it under the terms of the GNU Lesser General Public // // License as published by the Free Software Foundation; either // version 2.1 of the License, or (at your option) any later version. // // This library is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU // Lesser General Public License for more details. // // You should have received a copy of the GNU Lesser General Public // License along with this library; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, // MA 02110-1301 USA // // LGPL Version 2.1 HEADER END // // // Copyright (c) 2019, Regents of the University of Minnesota. // All rights reserved. // // Contributors: // Yaser Afshar // Ryan S. Elliott // #include "KIM_ModelDriverHeaders.hpp" #include "KIM_LogMacros.hpp" #include "SNAP.hpp" #include "SNAPImplementation.hpp" #include #include #include #include #include #include #include #include #include #include #include #include #ifdef MAXLINE #undef MAXLINE #endif #define MAXLINE 1024 #ifdef ONE #undef ONE #endif #define ONE 1.0 #ifdef KIM_LOGGER_OBJECT_NAME #undef KIM_LOGGER_OBJECT_NAME #endif // public member functions #define KIM_LOGGER_OBJECT_NAME modelDriverCreate SNAPImplementation::SNAPImplementation( KIM::ModelDriverCreate *const modelDriverCreate, KIM::LengthUnit const requestedLengthUnit, KIM::EnergyUnit const requestedEnergyUnit, KIM::ChargeUnit const requestedChargeUnit, KIM::TemperatureUnit const requestedTemperatureUnit, KIM::TimeUnit const requestedTimeUnit, int *const ier) : cachedNumberOfParticles_(0), numberOfContributingParticles_(0), modelWillNotRequestNeighborsOfNoncontributingParticles_(1), influenceDistance_(0), nelements(0), ncoeffall(0), twojmax(0), ncoeff(0), switchflag(1), // Set defaults for optional keywords bzeroflag(1), // Set defaults for optional keywords quadraticflag(0), // Set defaults for optional keywords beta_max(0), rfac0(0.99363), // Set defaults for optional keywords rmin0(0.0), rcutfac(0.0), snap(nullptr), nAllSpecies(0), nHybridStyleSpecies(0), nzbls(0), // Metal unit as a default base units angstrom(1.0), qqr2e(14.399645354084361), qelectron(1.0), inner(0.0), outer(0.0), zbl(nullptr), ntables(0) { // Everything is good *ier = false; if (!modelDriverCreate) { HELPER_LOG_ERROR("The ModelDriverCreate object pointer is not assigned\n"); *ier = true; return; } { std::FILE *parameterFilePointers[MAX_NUM_PARAMETER_FILES]; int numberParameterFiles(0); // Getting the number of parameter files, open the files and process them modelDriverCreate->GetNumberOfParameterFiles(&numberParameterFiles); if (numberParameterFiles > MAX_NUM_PARAMETER_FILES) { LOG_ERROR("Too many parameter files!\n"); *ier = true; return; } if (!numberParameterFiles) { LOG_ERROR("There is no parameter file!\n"); *ier = true; return; } *ier = OpenParameterFiles(modelDriverCreate, numberParameterFiles, parameterFilePointers); if (*ier) { return; } *ier = ProcessParameterFiles(modelDriverCreate, numberParameterFiles, parameterFilePointers); CloseParameterFiles(numberParameterFiles, parameterFilePointers); if (*ier) { return; } } *ier = ConvertUnits(modelDriverCreate, requestedLengthUnit, requestedEnergyUnit, requestedChargeUnit, requestedTemperatureUnit, requestedTimeUnit); if (*ier) { return; } *ier = setRefreshMutableValues(modelDriverCreate); if (*ier) { return; } *ier = RegisterKIMModelSettings(modelDriverCreate); if (*ier) { return; } *ier = RegisterKIMParameters(modelDriverCreate); if (*ier) { return; } *ier = RegisterKIMFunctions(modelDriverCreate); if (*ier) { return; } } #undef KIM_LOGGER_OBJECT_NAME SNAPImplementation::~SNAPImplementation() { } int SNAPImplementation::Refresh(KIM::ModelRefresh *const modelRefresh) { return setRefreshMutableValues(modelRefresh); } int SNAPImplementation::WriteParameterizedModel(KIM::ModelWriteParameterizedModel const *const modelWriteParameterizedModel) const { if (!modelWriteParameterizedModel) { HELPER_LOG_ERROR("The modelWriteParameterizedModel object pointer is not assigned\n"); return true; } std::string const *path = NULL; std::string const *modelName = NULL; modelWriteParameterizedModel->GetPath(&path); modelWriteParameterizedModel->GetModelName(&modelName); std::string coefficient_file(*modelName); std::string parameter_file(*modelName); std::string hybrid_file(*modelName); coefficient_file += ".snapcoeff"; parameter_file += ".snapparam"; hybrid_file += ".hybridparam"; // Set the file name for the next parameter file. // It must be called once for each parameter file. The order of these calls is important // and determines the order in which the parameter files will be listed in the automatically // generated CMakeLists.txt file. modelWriteParameterizedModel->SetParameterFileName(coefficient_file); modelWriteParameterizedModel->SetParameterFileName(parameter_file); if (nzbls || ntables) { modelWriteParameterizedModel->SetParameterFileName(hybrid_file); } // SNAP coefficient file std::string buffer = *path + "/" + coefficient_file; std::fstream fs; fs.open(buffer.c_str(), std::fstream::out); if (!fs.is_open()) { HELPER_LOG_ERROR("Unable to open the coefficient file for writing."); return true; } else { if (fs.fail()) { HELPER_LOG_ERROR("An error has occurred from opening the file on the associated stream!"); return true; } fs << "# SNAP coefficients for "; for (int ielem = 0; ielem < nelements; ++ielem) { fs << elements[ielem] << " "; } fs << "\n" << std::endl; // nelements & ncoeffall fs << nelements << " " << ncoeffall << std::endl; fs << std::setprecision(std::numeric_limits::digits10 + 1) << std::fixed; // Loop over nelements blocks in the SNAP coefficient file for (int ielem = 0; ielem < nelements; ++ielem) { fs << elements[ielem] << " " << radelem[ielem] << " " << wjelem[ielem] << std::endl; for (int icoeff = 0; icoeff < ncoeffall; ++icoeff) { fs << coeffelem(ielem, icoeff) << std::endl; } } fs << "\n\n" << "#\n" << "# SNAP coefficient file" << "#\n" << "# First line: nelements & ncoeffall\n" << "#\n" << "# This SNAP coefficient file has " << nelements << ((nelements > 1) ? " blocks, where\n" : " block, where\n") << "# First line of each block contains: 'species name' & 'species radius' & 'species weight'\n" << "# followed by " << ncoeffall << " SNAP coefficients.\n" << "#\n"; fs.close(); } // SNAP parameter file buffer = *path + "/" + parameter_file; fs.open(buffer.c_str(), std::fstream::out); if (!fs.is_open()) { HELPER_LOG_ERROR("Unable to open the parameter file for writing."); return true; } else { if (fs.fail()) { HELPER_LOG_ERROR("An error has occurred from opening the file on the associated stream!"); return true; } fs << "# SNAP parameters for "; for (int ielem = 0; ielem < nelements; ++ielem) { fs << elements[ielem] << " "; } fs << "\n"; fs << std::endl; fs << std::setprecision(std::numeric_limits::digits10 + 1) << std::fixed; // rcutfac fs << "rcutfac " << rcutfac << std::endl; // twojmax fs << "twojmax " << twojmax << std::endl; // rfac0 fs << "rfac0 " << rfac0 << std::endl; // rmin0 fs << "rmin0 " << rmin0 << std::endl; // switchflag fs << "switchflag " << switchflag << std::endl; // bzeroflag fs << "bzeroflag " << bzeroflag << std::endl; // quadraticflag fs << "quadraticflag " << quadraticflag << std::endl; fs << "\n\n" << "#\n" << "# SNAP parameter file" << "#\n"; fs.close(); } // HYBRID parameter file if (nzbls || ntables) { buffer = *path + "/" + hybrid_file; fs.open(buffer.c_str(), std::fstream::out); if (!fs.is_open()) { HELPER_LOG_ERROR("Unable to open the parameter file for writing."); return true; } else { if (fs.fail()) { HELPER_LOG_ERROR("An error has occurred from opening the file on the associated stream!"); return true; } fs << "# HYBRID style parameters (extra 'zbl' and two-body pair styles\n" << "# as a table style can be assigned)\n" << "#\n" << "# NOTE:\n" << "# No mixing rule will be used here.\n" << "#\n" << "# Each pair (I,J) or (J,I) can be assigned to one style\n" << "# If you specify the same pair for the second time, it wipes\n" << "# out all the previous assignments of that pair and the\n" << "# second one will be calculated for the two interacting atoms\n" << "# of those types.\n" << "\n\n" << "# Number of elements for the hybrid style\n"; fs << nHybridStyleSpecies; fs << "# Number of elements names, (atom names)\n"; for (int iSpec = 0; iSpec < nHybridStyleSpecies; ++iSpec) { fs << hybridStyleSpeciesNames[iSpec] << " "; } fs << "\n" << std::endl; fs << std::setprecision(std::numeric_limits::digits10 + 1) << std::fixed; if (nzbls) { fs << "# In the ZBL style, the inner and outer cutoff are the\n" << "# same for all pairs of atom types.\n" << "# zbl inner outer\n"; fs << "zbl " << inner << " " << outer << std::endl; fs << "# Element_1 Element_2 zbl Z_1 Z_2\n"; for (int iSpecies = 0; iSpecies < nAllSpecies; ++iSpecies) { for (int jSpecies = iSpecies; jSpecies < nAllSpecies; ++jSpecies) { if (setflag(iSpecies, jSpecies) == HYBRIDSTYLE::ZBL) { fs << allSpeciesNames[iSpecies] << " " << allSpeciesNames[jSpecies] << " zbl " << atomicNumber[iSpecies] << " " << atomicNumber[jSpecies] << std::endl; } } } } if (ntables) { fs << "\n" << "# Style table:\n" << "# table style N\n"; for (auto i : tables_info) { if (i.tableStyle == TABLESTYLE::LOOKUP) { fs << "table lookup " << i.tableLength << std::endl; } else if (i.tableStyle == TABLESTYLE::LINEAR) { fs << "table linear " << i.tableLength << std::endl; } else if (i.tableStyle == TABLESTYLE::SPLINE) { fs << "table spline " << i.tableLength << std::endl; } else if (i.tableStyle == TABLESTYLE::BITMAP) { fs << "table bitmap " << i.tableLength << std::endl; } } fs << "#\n" << "# style = 'lookup' or 'linear' or 'spline' or 'bitmap' =\n" << "# method of interpolation\n" << "# N = use N values in 'lookup', 'linear', 'spline' tables\n" << "# N = use 2^N values in 'bitmap' tables\n" << "#\n" << "#\n" << "# 'table' style can be used multiple times. For example, interactions between\n" << "# I and I atoms use a 'linear' table style and interactions between I and J\n" << "# atoms use a 'spline' table style, then you should list the table style two\n" << "# times. Style indexing starts from '1'. It means, that the fisrt style is\n" << "# numbered '1' and the second '2' and so on so forth.\n" << "# Later in the pair interactions, the 'table' style must be added after the\n" << "# I,J atom names followed by the style number and then followed by the\n" << "# remaining coefficients as of 'filename', and 'keyword', and 'cutoff'.\n" << "#\n" << "#\n" << "# The 'filename' specifies a file containing tabulated energy\n" << "# and force values.\n" << "# The 'keyword' specifies a section of the file.\n" << "# The 'cutoff' is an optional coefficient in distance unit.\n" << "#\n" << "#\n" << "# Example 1, we use 2 styles\n" << "# table linear 1000\n" << "# table spline 10000\n" << "# I I table 1 II.table IIKey 4.0\n" << "# I J table 2 IJ.table IJ\n" << "#\n" << "#\n" << "# Example 2, we use one style\n" << "# table spline 10000\n" << "# I I table 1 II.table IIKey 4.0\n" << "# I J table 1 IJ.table IJ\n" << "#\n" << "#\n" << "# Example 3, we use one style and have one tabulated file\n" << "# table spline 10000\n" << "# I I table 1 tablefile.txt II 4.0\n" << "# I J table 1 tablefile.txt IJ 4.8\n" << "#\n" << "#\n" << "#- Element_1 Element_2 table style_number filename keyword [cutoff]\n"; for (int iSpecies = 0; iSpecies < nAllSpecies; ++iSpecies) { for (int jSpecies = iSpecies; jSpecies < nAllSpecies; ++jSpecies) { if (setflag(iSpecies, jSpecies) == HYBRIDSTYLE::TABLE) { fs << allSpeciesNames[iSpecies] << " " << allSpeciesNames[jSpecies] << " table " << tableNumber(iSpecies, jSpecies) << " filename keyword [cutoff]" << std::endl; } } } } fs << "\n\n" << "#\n" << "# SNAP HYBRID style parameter file" << "#\n"; fs.close(); } } return false; } int SNAPImplementation::Compute(KIM::ModelCompute const *const modelCompute, KIM::ModelComputeArguments const *const modelComputeArguments) { // KIM API Model Input compute flags bool isComputeProcess_dEdr = false; bool isComputeProcess_d2Edr2 = false; // KIM API Model Output compute flags bool isComputeEnergy = false; bool isComputeForces = false; bool isComputeParticleEnergy = false; bool isComputeVirial = false; bool isComputeParticleVirial = false; bool isHybrid = (nzbls || ntables); // KIM API Model Input int const *particleSpeciesCodes = NULL; int const *particleContributing = NULL; VectorOfSizeDIM const *coordinates = NULL; // KIM API Model Output double *energy = NULL; VectorOfSizeDIM *forces = NULL; double *particleEnergy = NULL; VectorOfSizeSix *virial = NULL; VectorOfSizeSix *particleVirial = NULL; int ier = setComputeMutableValues(modelComputeArguments, isComputeProcess_dEdr, isComputeProcess_d2Edr2, isComputeEnergy, isComputeForces, isComputeParticleEnergy, isComputeVirial, isComputeParticleVirial, particleSpeciesCodes, particleContributing, coordinates, energy, forces, particleEnergy, virial, particleVirial); if (ier) { HELPER_LOG_ERROR("setComputeMutableValues fails."); return true; } if (beta_max < numberOfContributingParticles_) { beta_max = numberOfContributingParticles_; beta.resize(beta_max, ncoeff); bispectrum.resize(beta_max, ncoeff); } // Compute the bispectrum component of atom if (quadraticflag || isComputeEnergy || isComputeParticleEnergy) { computeBispectrum(modelComputeArguments, particleSpeciesCodes, particleContributing, coordinates); } // Compute dE_i/dB_i = beta_i for all i in list computeBeta(particleSpeciesCodes, particleContributing); #include "SNAPImplementationComputeDispatch.cpp" return ier; } int SNAPImplementation::ComputeArgumentsCreate(KIM::ModelComputeArgumentsCreate *const modelComputeArgumentsCreate) const { return RegisterKIMComputeArgumentsSettings(modelComputeArgumentsCreate); } int SNAPImplementation::ComputeArgumentsDestroy(KIM::ModelComputeArgumentsDestroy *const /*modelComputeArgumentsDestroy*/) const { // Nothing to do for this case // Everything is good return false; } // Private member functions #define KIM_LOGGER_OBJECT_NAME modelDriverCreate int SNAPImplementation::OpenParameterFiles(KIM::ModelDriverCreate *const modelDriverCreate, int const numberParameterFiles, std::FILE **parameterFilePointers) { for (int i = 0; i < numberParameterFiles; ++i) { std::string const *parameterFileName; if (modelDriverCreate->GetParameterFileName(i, ¶meterFileName)) { LOG_ERROR("Unable to get the parameter file name\n"); return true; } parameterFilePointers[i] = std::fopen(parameterFileName->c_str(), "r"); if (!parameterFilePointers[i]) { HELPER_LOG_ERROR("The parameter file (" + *parameterFileName + ") can not be opened\n"); for (int j = i - 1; i <= 0; --i) { std::fclose(parameterFilePointers[j]); } return true; } } // everything is good return false; } #undef KIM_LOGGER_OBJECT_NAME void SNAPImplementation::GetNextDataLine(std::FILE *const filePtr, char *nextLinePtr, int const maxSize, int *endOfFileFlag) { *endOfFileFlag = 0; do { if (!std::fgets(nextLinePtr, maxSize, filePtr)) { *endOfFileFlag = 1; break; } while (nextLinePtr[0] == ' ' || nextLinePtr[0] == '\t' || nextLinePtr[0] == '\n' || nextLinePtr[0] == '\r') nextLinePtr++; } while ((std::strncmp("#", nextLinePtr, 1) == 0) || (std::strlen(nextLinePtr) == 0)); // remove comments starting with `#' in a line char *pch = std::strchr(nextLinePtr, '#'); if (pch) { *pch = '\0'; } } #define KIM_LOGGER_OBJECT_NAME modelDriverCreate int SNAPImplementation::ProcessParameterFiles(KIM::ModelDriverCreate *const modelDriverCreate, int const numberParameterFiles, std::FILE *const *parameterFilePointers) { char nextLine[MAXLINE]; int endOfFileFlag; // keep track of known species std::map speciesMap; // Read SNAP coefficient file GetNextDataLine(parameterFilePointers[0], nextLine, MAXLINE, &endOfFileFlag); if (endOfFileFlag) { HELPER_LOG_ERROR("End of file in the SNAP coefficient file.\n"); return true; } int ier = std::sscanf(nextLine, "%d %d", &nelements, &ncoeffall); if (ier != 2) { HELPER_LOG_ERROR("Unable to read nelements & ncoeffall from the line:\n" + std::string(nextLine) + "\n"); return true; } if (nelements < 1) { HELPER_LOG_ERROR("Incorrect number of elements in the SNAP coefficient file.\n"); return true; } if (ncoeffall < 1) { HELPER_LOG_ERROR("Incorrect number of coefficients in the SNAP coefficient file.\n"); return true; } // Allocate memory based on the number of elements // Set up memory for the element lists elements.resize(nelements); radelem.resize(nelements); wjelem.resize(nelements); coeffelem.resize(nelements, ncoeffall); // Loop over nelements blocks in the SNAP coefficient file for (int ielem = 0; ielem < nelements; ++ielem) { GetNextDataLine(parameterFilePointers[0], nextLine, MAXLINE, &endOfFileFlag); if (endOfFileFlag) { HELPER_LOG_ERROR("End of file in the SNAP coefficient file.\n"); return true; } char spec[MAXLINE]; ier = std::sscanf(nextLine, "%s %lf %lf", spec, &radelem[ielem], &wjelem[ielem]); if (ier != 3) { HELPER_LOG_ERROR("Incorrect format in the SNAP coefficient file.\n" "Unable to read spec, radius & weight from the line:\n" + std::string(nextLine) + "\n"); return true; } if (radelem[ielem] < 0.0) { HELPER_LOG_ERROR("Incorrect radius in the SNAP coefficient file.\n"); return true; } KIM::SpeciesName const speciesName(spec); if (speciesName.ToString() == "unknown") { HELPER_LOG_ERROR("Incorrect format in the SNAP coefficient file.\n" "The species name '" + std::string(spec) + "' is unknown.\n"); return true; } // check for new species auto iter = speciesMap.find(speciesName); if (iter == speciesMap.end()) { ier = modelDriverCreate->SetSpeciesCode(speciesName, ielem); if (ier) { LOG_ERROR("SetSpeciesCode failed to set the new species.\n"); return ier; } speciesMap[speciesName] = ielem; elements[ielem] = speciesName.ToString(); } else { HELPER_LOG_ERROR("Incorrect format in the SNAP coefficient file.\n" "The Species '" + std::string(spec) + "' is already defined and exist.\n"); return true; } for (int icoeff = 0; icoeff < ncoeffall; ++icoeff) { GetNextDataLine(parameterFilePointers[0], nextLine, MAXLINE, &endOfFileFlag); if (endOfFileFlag) { HELPER_LOG_ERROR("End of file in the SNAP coefficient file.\n"); return true; } double coeff; ier = std::sscanf(nextLine, "%lf", &coeff); if (ier != 1) { HELPER_LOG_ERROR("Incorrect format in the SNAP coefficient file.\n"); return true; } coeffelem(ielem, icoeff) = coeff; } } // set flags for required keywords bool rcutfacflag(false); bool twojmaxflag(false); // Read SNAP parameter file while (1) { GetNextDataLine(parameterFilePointers[1], nextLine, MAXLINE, &endOfFileFlag); if (endOfFileFlag) { break; } // words = ptrs to all words in line // strip single and double quotes from words char *keywd = std::strtok(nextLine, "' \t\n\r\f"); char *keyval = std::strtok(NULL, "' \t\n\r\f"); if (!std::strcmp(keywd, "rcutfac")) { rcutfac = std::atof(keyval); rcutfacflag = true; } else if (!std::strcmp(keywd, "twojmax")) { twojmax = std::atoi(keyval); twojmaxflag = true; } else if (!std::strcmp(keywd, "rfac0")) { rfac0 = std::atof(keyval); } else if (!std::strcmp(keywd, "rmin0")) { rmin0 = std::atof(keyval); } else if (!std::strcmp(keywd, "switchflag")) { switchflag = std::atoi(keyval); if (switchflag != 0 && switchflag != 1) switchflag = 1; } else if (!std::strcmp(keywd, "bzeroflag")) { bzeroflag = std::atoi(keyval); if (bzeroflag != 0 && bzeroflag != 1) bzeroflag = 1; } else if (!std::strcmp(keywd, "quadraticflag")) { quadraticflag = std::atoi(keyval); if (quadraticflag != 0 && quadraticflag != 1) quadraticflag = 1; } else if (!std::strcmp(keywd, "diagonalstyle")) { int const diagonalstyle = std::atoi(keyval); if (diagonalstyle != 3) { HELPER_LOG_ERROR("Incorrect SNAP parameter file.\n" "The 'diagonalstyle' keyword was removed in 2019 " "since all known SNAP potentials use the default " "value of '3'. (This keyword is kept only for " "backward compatibility.)\n" "Incorrect diagonalstyle value of " + std::string(keyval) + " is given.\n"); return true; } } else { HELPER_LOG_ERROR("Incorrect SNAP parameter file.\n" "Incorrect SNAP keywords of " + std::string(keywd) + ", " + std::string(keyval) + "\n"); return true; } } if (!rcutfacflag || !twojmaxflag) { HELPER_LOG_ERROR("Incorrect SNAP parameter file.\n" + std::string(rcutfacflag ? "'twojmaxflag' is not given\n" : "'rcutfacflag' is not given\n")); return true; } // Process Hybrid parameter file if (numberParameterFiles > 2) { // Read the Hybrid parameter file GetNextDataLine(parameterFilePointers[2], nextLine, MAXLINE, &endOfFileFlag); if (endOfFileFlag) { HELPER_LOG_ERROR("End of file in the Hybrid parameter file.\n"); return true; } int ier = std::sscanf(nextLine, "%d", &nHybridStyleSpecies); if (ier != 1) { HELPER_LOG_ERROR("Unable to read the number of unique species from the line:\n" + std::string(nextLine) + "\n"); return true; } if (nHybridStyleSpecies <= 0) { HELPER_LOG_ERROR("Incorrect number of unique species '" + std::to_string(nHybridStyleSpecies) + "' in the Hybrid parameter file.\n"); return true; } // Allocate memory based on the number of species nAllSpecies = nelements; allSpeciesNames.reserve(nelements + nHybridStyleSpecies); for (auto i : elements) { allSpeciesNames.push_back(i); } // Set up memory for the species lists hybridStyleSpeciesNames.resize(nHybridStyleSpecies); GetNextDataLine(parameterFilePointers[2], nextLine, MAXLINE, &endOfFileFlag); if (endOfFileFlag) { HELPER_LOG_ERROR("End of file in the Hybrid parameter file.\n"); return true; } for (int iSpec = 0; iSpec < nHybridStyleSpecies; ++iSpec) { char *spec = std::strtok(iSpec ? NULL : nextLine, "' \t\n\r\f"); if (!spec) { HELPER_LOG_ERROR("Unable to read the name of unique species from the line:\n" + std::string(nextLine) + "\n"); return true; } KIM::SpeciesName const speciesName(spec); if (speciesName.ToString() == "unknown") { HELPER_LOG_ERROR("Incorrect format in the Hybrid parameter file.\n" "The species name '" + std::string(spec) + "' is unknown.\n"); return true; } for (int i = 0; i < iSpec; ++i) { KIM::SpeciesName const tmpName(hybridStyleSpeciesNames[i]); if (speciesName == tmpName) { HELPER_LOG_ERROR("Incorrect format in the Hybrid parameter file.\n" "The Species '" + std::string(spec) + "' is already defined and exist.\n"); return true; } } hybridStyleSpeciesNames[iSpec] = speciesName.ToString(); // check for new species auto iter = speciesMap.find(speciesName); if (iter == speciesMap.end()) { ier = modelDriverCreate->SetSpeciesCode(speciesName, nAllSpecies); if (ier) { LOG_ERROR("SetSpeciesCode failed to set the new species.\n"); return ier; } speciesMap[speciesName] = nAllSpecies++; allSpeciesNames.push_back(speciesName.ToString()); } } // End of loop for the number of species while (1) { GetNextDataLine(parameterFilePointers[2], nextLine, MAXLINE, &endOfFileFlag); if (endOfFileFlag) { break; } char *keywd = std::strtok(nextLine, "' \t\n\r\f"); char *keyval1 = std::strtok(NULL, "' \t\n\r\f"); char *keyval2 = std::strtok(NULL, "' \t\n\r\f"); if (!std::strcmp(keywd, "zbl")) { ++nzbls; if (nzbls >= 2) { HELPER_LOG_ERROR("There is more than one zbl style in the Hybrid parameter file\n"); return true; } if (!keyval1 || !keyval2) { HELPER_LOG_ERROR("Incorrect zbl style in the Hybrid parameter file.\n"); return true; } inner = std::atof(keyval1); outer = std::atof(keyval2); if (inner <= 0.0) { HELPER_LOG_ERROR("Incorrect distance (where switching function begins) for ZBL interaction in the Hybrid parameter file.\n"); return true; } if (inner > outer) { HELPER_LOG_ERROR("Incorrect global cutoff for ZBL interaction in the Hybrid parameter file.\n"); return true; } continue; } // if zbl if (keyval2) { if (!std::strcmp(keyval2, "table")) { ++ntables; continue; } } } // End of while loop if (!nzbls && !ntables) { HELPER_LOG_ERROR("There is no zbl/table style in the Hybrid parameter file.\n"); return true; } // Allocate memory setflag.resize(nAllSpecies, nAllSpecies, HYBRIDSTYLE::NONE); if (nzbls) { atomicNumber.resize(nAllSpecies, 0.0); } if (ntables) { tables_info.reserve(ntables); tables.resize(ntables); tableNumber.resize(nAllSpecies, nAllSpecies, -1); } // Rewind the file if (std::fseek(parameterFilePointers[2], 0, SEEK_SET)) { HELPER_LOG_ERROR("Failed to rewind the Hybrid parameter file.\n"); return true; } int nTableStyles = 0; int nTables = 0; while (1) { GetNextDataLine(parameterFilePointers[2], nextLine, MAXLINE, &endOfFileFlag); if (endOfFileFlag) { break; } char *keywd = std::strtok(nextLine, "' \t\n\r\f"); char *keyval1 = std::strtok(NULL, "' \t\n\r\f"); char *keyval2 = std::strtok(NULL, "' \t\n\r\f"); if (!keyval1 || !keyval2) { continue; } if (!std::strcmp(keywd, "zbl")) { continue; } if (!std::strcmp(keywd, "table")) { if (!keyval1 || !keyval2) { HELPER_LOG_ERROR("Wrong table style in the Hybrid parameter file.\n"); return true; } TABLESTYLE tabstyle; if (!std::strcmp(keyval1, "lookup")) { tabstyle = TABLESTYLE::LOOKUP; } else if (!std::strcmp(keyval1, "linear")) { tabstyle = TABLESTYLE::LINEAR; } else if (!std::strcmp(keyval1, "spline")) { tabstyle = TABLESTYLE::SPLINE; } else if (!std::strcmp(keyval1, "bitmap")) { tabstyle = TABLESTYLE::BITMAP; } else { HELPER_LOG_ERROR("Unknown table style '" + std::string(keyval1) + "' in the Hybrid parameter file.\n"); return true; } int const tablength = std::atoi(keyval2); if (tablength < 2) { HELPER_LOG_ERROR("Illegal number of table entries " + std::string(keyval2) + " < 2\n"); return true; } tables_info.resize(nTableStyles + 1); tables_info[nTableStyles].tableStyle = tabstyle; tables_info[nTableStyles].tableLength = tablength; ++nTableStyles; continue; } if ((!std::strcmp(keyval2, "zbl")) || (!std::strcmp(keyval2, "table"))) { char *keyval3 = std::strtok(NULL, "' \t\n\r\f"); char *keyval4 = std::strtok(NULL, "' \t\n\r\f"); char *keyval5 = std::strtok(NULL, "' \t\n\r\f"); char *keyval6 = std::strtok(NULL, "' \t\n\r\f"); if (!keyval3 || !keyval4) { HELPER_LOG_ERROR("Incorrect zbl/table style in the Hybrid parameter file.\n"); return true; } // convert species strings to proper type instances KIM::SpeciesName const specName1(keywd); KIM::SpeciesName const specName2(keyval1); // Extra check for the species auto iter = speciesMap.find(specName1); if (iter == speciesMap.end()) { HELPER_LOG_ERROR("The species name '" + specName1.ToString() + "' is not defined\n"); return true; } int const iSpecies = speciesMap[specName1]; // Extra check for the species iter = speciesMap.find(specName2); if (iter == speciesMap.end()) { HELPER_LOG_ERROR("The species name '" + specName2.ToString() + "' is not defined\n"); return true; } int const jSpecies = speciesMap[specName2]; if (!std::strcmp(keyval2, "zbl")) { if (keyval5 || keyval6) { HELPER_LOG_ERROR("Incorrect zbl style in the Hybrid parameter file.\n"); return true; } // Get the species atomic numbers double const z_iSpecies = std::atof(keyval3); double const z_jSpecies = std::atof(keyval4); if (iSpecies == jSpecies) { if (z_iSpecies != z_jSpecies) { HELPER_LOG_ERROR("Incorrect ZBL args in the Hybrid parameter file.\n" "When i == j, it is required that Z_i == Z_j.\n"); return true; } } else { setflag(jSpecies, iSpecies) = HYBRIDSTYLE::ZBL; atomicNumber[jSpecies] = z_jSpecies; } setflag(iSpecies, jSpecies) = HYBRIDSTYLE::ZBL; atomicNumber[iSpecies] = z_iSpecies; continue; } // if zbl if (!std::strcmp(keyval2, "table")) { // specName1 specName2 table 1 filename keyword [cutoff] // Get the table style number int const tableStyleNumber = std::stoi(keyval3); if (tableStyleNumber > nTableStyles) { HELPER_LOG_ERROR("Incorrect style number in the Hybrid parameter file.\n" " style number > number of defined table styles.\n"); return true; } // Table keyword if (!keyval5) { HELPER_LOG_ERROR("Incorrect table style in the Hybrid parameter file (No keyword).\n"); return true; } int const sn = tableStyleNumber - 1; int const nt = nTables++; // Construct a table object TABLE tt(tables_info[sn].tableStyle, tables_info[sn].tableLength); tables[nt] = std::move(tt); std::string const *tableFileName; int tableFileNumber = 3; for (; tableFileNumber < numberParameterFiles; ++tableFileNumber) { // if (modelDriverCreate->GetParameterFileName(tableFileNumber, &tableFileName)) // { // LOG_ERROR("Unable to get the table file name.\n"); // return true; // } // A hack to make it work // // KIM-API, resgisters the table file name using a unique random // string which is different from the tabulated file name in the // hybrid parameter file, thus we use the unqiue heywords for // each interaction pair to find the associated file bool findMatch(false); { // Rewind the file in case it is in the middle of the file if (std::fseek(parameterFilePointers[tableFileNumber], 0, SEEK_SET)) { HELPER_LOG_ERROR("Failed to rewind the file.\n"); return true; } char tmpNextLine[MAXLINE]; // Loop until section found with matching keyword while (1) { GetNextDataLine(parameterFilePointers[tableFileNumber], tmpNextLine, MAXLINE, &endOfFileFlag); if (endOfFileFlag) { break; } char *word = std::strtok(tmpNextLine, " \t\n\r"); // matching keyword if (!std::strcmp(word, keyval5)) { findMatch = true; break; } } // End of loop to find the matching keyword } if (findMatch) { break; } // if (!std::strcmp(keyval4, tableFileName->c_str())) // { // break; // } } // End of loop throug the number of registered parameter files if (tableFileNumber >= numberParameterFiles) { HELPER_LOG_ERROR("Incorrect TABLE filename '" + std::string(keyval4) + "' in the Hybrid parameter file.\n" "and/or failed to find the unique keyword '" + std::string(keyval5) + "' in the tabulated filename.\n"); return true; } // Read the table if (tables[nt].read_table(parameterFilePointers[tableFileNumber], keyval5)) { HELPER_LOG_ERROR("Failed to read from the table file '" + std::string(keyval4) + "'.\n"); return true; } // Check the table parameters and insure cutoff is within table // for BITMAP tables, file values can be in non-ascending order if (tables[nt].ninput <= 1) { HELPER_LOG_ERROR("Invalid table length '" + std::to_string(tables[nt].ninput) + "' <= 1 read from the table file '" + std::string(keyval4) + "'.\n"); return true; } // Set the table cutoff if (keyval6) { tables[nt].cut = std::atof(keyval6); if (tables[nt].cut < 0.0) { HELPER_LOG_ERROR("Invalid table cutoff '" + std::to_string(tables[nt].cut) + "' < 0.0 in the Hybrid parameter file.\n"); return true; } } else if (tables[nt].rflag != TABLEDISTANCESTYLE::NONE) { tables[nt].cut = tables[nt].rhi; } else { tables[nt].cut = tables[nt].rfile[tables[nt].ninput - 1]; } double rlo; double rhi; if (tables[nt].rflag == TABLEDISTANCESTYLE::NONE) { rlo = tables[nt].rfile[0]; rhi = tables[nt].rfile[tables[nt].ninput - 1]; } else { rlo = tables[nt].rlo; rhi = tables[nt].rhi; } if (tables[nt].cut <= rlo || tables[nt].cut > rhi) { HELPER_LOG_ERROR("Invalid table cutoff.\n"); return true; } if (rlo <= 0.0) { HELPER_LOG_ERROR("Invalid table rlo <= 0.0.\n"); return true; } // match = 1, if don't need to spline read-in tables this is only // the case if r values needed by final tables exactly match r // values read from the file for tabstyle SPLINE, always need to // build spline tables tables[nt].match = 0; if (tables[nt].tableStyle == TABLESTYLE::LINEAR && tables[nt].ninput == tables[nt].tableLength && tables[nt].rflag == TABLEDISTANCESTYLE::RSQ && tables[nt].rhi == tables[nt].cut) { tables[nt].match = 1; } if (tables[nt].tableStyle == TABLESTYLE::BITMAP && tables[nt].ninput == 1 << tables[nt].tableLength && tables[nt].rflag == TABLEDISTANCESTYLE::BITMAP && tables[nt].rhi == tables[nt].cut) { tables[nt].match = 1; } if (tables[nt].rflag == TABLEDISTANCESTYLE::BITMAP && tables[nt].match == 0) { HELPER_LOG_ERROR("Bitmapped table in the file does not match the requested table.\n"); return true; } if (iSpecies != jSpecies) { setflag(jSpecies, iSpecies) = HYBRIDSTYLE::TABLE; tableNumber(jSpecies, iSpecies) = nt; } setflag(iSpecies, jSpecies) = HYBRIDSTYLE::TABLE; tableNumber(iSpecies, jSpecies) = nt; continue; } // if table } // if zbl or table } // End of while loop } // if numberParameterFiles > 2 // Extra check to set the flag for all species defined to have interactions // in the SNAP potential snapflag.resize(nAllSpecies ? nAllSpecies : nelements, false); for (auto elem : elements) { int const iSpecies = speciesMap[elem]; snapflag[iSpecies] = true; } // everything is good return false; } #undef KIM_LOGGER_OBJECT_NAME void SNAPImplementation::CloseParameterFiles(int const numberParameterFiles, std::FILE *const *parameterFilePointers) { for (int i = 0; i < numberParameterFiles; ++i) { std::fclose(parameterFilePointers[i]); } } #define KIM_LOGGER_OBJECT_NAME modelDriverCreate int SNAPImplementation::ConvertUnits(KIM::ModelDriverCreate *const modelDriverCreate, KIM::LengthUnit const &requestedLengthUnit, KIM::EnergyUnit const &requestedEnergyUnit, KIM::ChargeUnit const &requestedChargeUnit, KIM::TemperatureUnit const &requestedTemperatureUnit, KIM::TimeUnit const &requestedTimeUnit) { // Define metal unit as a default base units KIM::LengthUnit const fromLength = KIM::LENGTH_UNIT::A; KIM::EnergyUnit const fromEnergy = KIM::ENERGY_UNIT::eV; KIM::ChargeUnit const fromCharge = KIM::CHARGE_UNIT::e; KIM::TemperatureUnit const fromTemperature = KIM::TEMPERATURE_UNIT::K; KIM::TimeUnit const fromTime = KIM::TIME_UNIT::ps; // changing units of length double convertLength = ONE; int ier = modelDriverCreate->ConvertUnit(fromLength, fromEnergy, fromCharge, fromTemperature, fromTime, requestedLengthUnit, requestedEnergyUnit, requestedChargeUnit, requestedTemperatureUnit, requestedTimeUnit, ONE, 0.0, 0.0, 0.0, 0.0, &convertLength); if (ier) { LOG_ERROR("Unable to convert unit"); return ier; } // Convert to active units if (convertLength != ONE) { std::for_each(radelem.begin(), radelem.end(), [&](double &rc) { rc *= convertLength; }); rmin0 *= convertLength; if (nzbls) { angstrom *= convertLength; qqr2e *= convertLength; inner *= convertLength; outer *= convertLength; } if (ntables) { for (int nt = 0; nt < ntables; ++nt) { tables[nt].rlo *= convertLength; tables[nt].rhi *= convertLength; tables[nt].fplo /= convertLength; tables[nt].fplo /= convertLength; tables[nt].fphi /= convertLength; tables[nt].fphi /= convertLength; std::for_each(tables[nt].rfile.begin(), tables[nt].rfile.end(), [&](double &r) { r *= convertLength; }); std::for_each(tables[nt].ffile.begin(), tables[nt].ffile.end(), [&](double &f) { f /= convertLength; }); } } } // Changing units of energy double convertEnergy = ONE; ier = modelDriverCreate->ConvertUnit(fromLength, fromEnergy, fromCharge, fromTemperature, fromTime, requestedLengthUnit, requestedEnergyUnit, requestedChargeUnit, requestedTemperatureUnit, requestedTimeUnit, 0.0, ONE, 0.0, 0.0, 0.0, &convertEnergy); if (ier) { LOG_ERROR("Unable to convert energy unit"); return ier; } // Convert to active units if (convertEnergy != ONE) { // Loop over nelements blocks in the SNAP coefficient file for (int ielem = 0; ielem < nelements; ++ielem) { for (int icoeff = 0; icoeff < ncoeffall; ++icoeff) { coeffelem(ielem, icoeff) *= convertEnergy; } } if (nzbls) { qqr2e *= convertEnergy; } if (ntables) { for (int nt = 0; nt < ntables; ++nt) { tables[nt].fplo *= convertEnergy; tables[nt].fphi *= convertEnergy; std::for_each(tables[nt].ffile.begin(), tables[nt].ffile.end(), [&](double &f) { f *= convertEnergy; }); std::for_each(tables[nt].efile.begin(), tables[nt].efile.end(), [&](double &e) { e *= convertEnergy; }); } } } // Changing units of charge double convertCharge = ONE; ier = modelDriverCreate->ConvertUnit(fromLength, fromEnergy, fromCharge, fromTemperature, fromTime, requestedLengthUnit, requestedEnergyUnit, requestedChargeUnit, requestedTemperatureUnit, requestedTimeUnit, 0.0, 0.0, ONE, 0.0, 0.0, &convertCharge); if (ier) { LOG_ERROR("Unable to convert energy unit"); return ier; } // Convert to active units if (convertCharge != ONE) { if (nzbls) { qqr2e /= convertCharge; qqr2e /= convertCharge; qelectron *= convertCharge; } } // Register units ier = modelDriverCreate->SetUnits(requestedLengthUnit, requestedEnergyUnit, requestedChargeUnit, KIM::TEMPERATURE_UNIT::unused, KIM::TIME_UNIT::unused); if (ier) { LOG_ERROR("Unable to set units to requested values"); return ier; } // Everything is good return false; } #undef KIM_LOGGER_OBJECT_NAME template int SNAPImplementation::setRefreshMutableValues(ModelObj *const modelObj) { // Correct the total number of coefficients if (quadraticflag) { ncoeff = std::sqrt(2 * ncoeffall) - 1; int const ntmp = 1 + ncoeff * (ncoeff + 3) / 2; if (ntmp != ncoeffall) { HELPER_LOG_ERROR("Incorrect args.\n" "ncoeffall = " + std::to_string(ncoeffall) + " ntmp = " + std::to_string(ntmp) + " ncoeff = " + std::to_string(ncoeff) + "\n"); return true; } } else { ncoeff = ncoeffall - 1; } // We have to creat the SNAP object and do the extra check on // the number of coefficients // Construct the SNAP object snap.reset(new SNA(rfac0, twojmax, rmin0, switchflag, bzeroflag)); // Extra check if (ncoeff != snap->ncoeff) { HELPER_LOG_ERROR("Wrong number of coefficients.\n" "ncoeff = " + std::to_string(ncoeff) + " & SNAP ncoeff = " + std::to_string(snap->ncoeff) + "\nNumber of coefficients to the model and the one created in SNAP object do not match\n"); return true; } // Set up memory for square of cutoff cutsq.resize(nelements, nelements); // Calculate maximum cutoff for all elements & update parameters double rcutmax(0.0); for (int ielem = 0; ielem < nelements; ++ielem) { double const radi = radelem[ielem]; rcutmax = std::max(2.0 * radi * rcutfac, rcutmax); for (int jelem = 0; jelem <= ielem; ++jelem) { double const cut = (radi + radelem[jelem]) * rcutfac; // Set the square of cutoff for all pairs cutsq(ielem, jelem) = cutsq(jelem, ielem) = cut * cut; } } if (nzbls) { // Construct the ZBL object zbl.reset(new ZBL(inner, outer)); zbl->allocate(nAllSpecies); rcutmax = std::max(rcutmax, zbl->cut_global); for (int iSpecies = 0; iSpecies < nAllSpecies; ++iSpecies) { for (int jSpecies = iSpecies; jSpecies < nAllSpecies; ++jSpecies) { if (setflag(iSpecies, jSpecies) == HYBRIDSTYLE::ZBL) { zbl->set_coeff(iSpecies, jSpecies, atomicNumber[iSpecies], atomicNumber[jSpecies], angstrom, qqr2e, qelectron); } } } } if (ntables) { for (int nt = 0; nt < ntables; ++nt) { rcutmax = std::max(rcutmax, tables[nt].cut); // spline read-in values and compute r,e,f vectors within table if (tables[nt].match == 0) { tables[nt].spline_table(); } tables[nt].compute_table(); } } influenceDistance_ = rcutmax; // Update the maximum cutoff or influence distance value in KIM API object modelObj->SetInfluenceDistancePointer(&influenceDistance_); // In this model we do not need to request neighbors from non-contributing particles // modelWillNotRequestNeighborsOfNoncontributingParticles_ = 1 // Update the cutoff value in KIM API object modelObj->SetNeighborListPointers(1, &influenceDistance_, &modelWillNotRequestNeighborsOfNoncontributingParticles_); // everything is good return false; } int SNAPImplementation::RegisterKIMFunctions(KIM::ModelDriverCreate *const modelDriverCreate) const { // Register functions int ier = modelDriverCreate->SetRoutinePointer(KIM::MODEL_ROUTINE_NAME::Destroy, KIM::LANGUAGE_NAME::cpp, true, reinterpret_cast(SNAP::Destroy)) || modelDriverCreate->SetRoutinePointer(KIM::MODEL_ROUTINE_NAME::Refresh, KIM::LANGUAGE_NAME::cpp, true, reinterpret_cast(SNAP::Refresh)) || modelDriverCreate->SetRoutinePointer(KIM::MODEL_ROUTINE_NAME::WriteParameterizedModel, KIM::LANGUAGE_NAME::cpp, false, reinterpret_cast(SNAP::WriteParameterizedModel)) || modelDriverCreate->SetRoutinePointer(KIM::MODEL_ROUTINE_NAME::Compute, KIM::LANGUAGE_NAME::cpp, true, reinterpret_cast(SNAP::Compute)) || modelDriverCreate->SetRoutinePointer(KIM::MODEL_ROUTINE_NAME::ComputeArgumentsCreate, KIM::LANGUAGE_NAME::cpp, true, reinterpret_cast(SNAP::ComputeArgumentsCreate)) || modelDriverCreate->SetRoutinePointer(KIM::MODEL_ROUTINE_NAME::ComputeArgumentsDestroy, KIM::LANGUAGE_NAME::cpp, true, reinterpret_cast(SNAP::ComputeArgumentsDestroy)); return ier; } #define KIM_LOGGER_OBJECT_NAME modelComputeArguments int SNAPImplementation::setComputeMutableValues(KIM::ModelComputeArguments const *const modelComputeArguments, bool &isComputeProcess_dEdr, bool &isComputeProcess_d2Edr2, bool &isComputeEnergy, bool &isComputeForces, bool &isComputeParticleEnergy, bool &isComputeVirial, bool &isComputeParticleVirial, int const *&particleSpeciesCodes, int const *&particleContributing, VectorOfSizeDIM const *&coordinates, double *&energy, VectorOfSizeDIM *&forces, double *&particleEnergy, VectorOfSizeSix *&virial, VectorOfSizeSix *&particleVirial) { // get compute flags int compProcess_dEdr; int compProcess_d2Edr2; modelComputeArguments->IsCallbackPresent(KIM::COMPUTE_CALLBACK_NAME::ProcessDEDrTerm, &compProcess_dEdr); modelComputeArguments->IsCallbackPresent(KIM::COMPUTE_CALLBACK_NAME::ProcessD2EDr2Term, &compProcess_d2Edr2); isComputeProcess_dEdr = compProcess_dEdr; isComputeProcess_d2Edr2 = compProcess_d2Edr2; int const *numberOfParticles = NULL; int ier = modelComputeArguments->GetArgumentPointer(KIM::COMPUTE_ARGUMENT_NAME::numberOfParticles, &numberOfParticles) || modelComputeArguments->GetArgumentPointer(KIM::COMPUTE_ARGUMENT_NAME::particleSpeciesCodes, &particleSpeciesCodes) || modelComputeArguments->GetArgumentPointer(KIM::COMPUTE_ARGUMENT_NAME::particleContributing, &particleContributing) || modelComputeArguments->GetArgumentPointer(KIM::COMPUTE_ARGUMENT_NAME::coordinates, (double const **)&coordinates) || modelComputeArguments->GetArgumentPointer(KIM::COMPUTE_ARGUMENT_NAME::partialEnergy, &energy) || modelComputeArguments->GetArgumentPointer(KIM::COMPUTE_ARGUMENT_NAME::partialForces, (double const **)&forces) || modelComputeArguments->GetArgumentPointer(KIM::COMPUTE_ARGUMENT_NAME::partialParticleEnergy, &particleEnergy) || modelComputeArguments->GetArgumentPointer(KIM::COMPUTE_ARGUMENT_NAME::partialVirial, (double const **)&virial) || modelComputeArguments->GetArgumentPointer(KIM::COMPUTE_ARGUMENT_NAME::partialParticleVirial, (double const **)&particleVirial); if (ier) { LOG_ERROR("GetArgumentPointer"); return true; } isComputeEnergy = (energy); isComputeForces = (forces); isComputeParticleEnergy = (particleEnergy); isComputeVirial = (virial); isComputeParticleVirial = (particleVirial); // Update values cachedNumberOfParticles_ = *numberOfParticles; // Get the number of contributing particles numberOfContributingParticles_ = std::accumulate(particleContributing, particleContributing + cachedNumberOfParticles_, 0); // everything is good return false; } #undef KIM_LOGGER_OBJECT_NAME int SNAPImplementation::getComputeIndex(bool const isComputeProcess_dEdr, bool const isComputeProcess_d2Edr2, bool const isComputeEnergy, bool const isComputeForces, bool const isComputeParticleEnergy, bool const isComputeVirial, bool const isComputeParticleVirial, bool const isHybrid) const { int const processd2E = 2; int const energy = 2; int const force = 2; int const particleEnergy = 2; int const virial = 2; int const particleVirial = 2; int const hybrid = 2; int const isComputeProcess_dEdr_int = static_cast(isComputeProcess_dEdr); int const isComputeProcess_d2Edr2_int = static_cast(isComputeProcess_d2Edr2); int const isComputeEnergy_int = static_cast(isComputeEnergy); int const isComputeForces_int = static_cast(isComputeForces); int const isComputeParticleEnergy_int = static_cast(isComputeParticleEnergy); int const isComputeVirial_int = static_cast(isComputeVirial); int const isComputeParticleVirial_int = static_cast(isComputeParticleVirial); int const isHybrid_int = static_cast(isHybrid); int index = 0; // processdE index += isComputeProcess_dEdr_int * processd2E * energy * force * particleEnergy * virial * particleVirial * hybrid; // processd2E index += isComputeProcess_d2Edr2_int * energy * force * particleEnergy * virial * particleVirial * hybrid; // energy index += isComputeEnergy_int * force * particleEnergy * virial * particleVirial * hybrid; // force index += isComputeForces_int * particleEnergy * virial * particleVirial * hybrid; // particleEnergy index += isComputeParticleEnergy_int * virial * particleVirial * hybrid; // virial index += isComputeVirial_int * particleVirial * hybrid; // particleVirial index += isComputeParticleVirial_int * hybrid; // hybrid index += isHybrid_int; return index; } int SNAPImplementation::RegisterKIMModelSettings(KIM::ModelDriverCreate *const modelDriverCreate) const { // Register numbering return modelDriverCreate->SetModelNumbering(KIM::NUMBERING::zeroBased); } #define KIM_LOGGER_OBJECT_NAME modelDriverCreate int SNAPImplementation::RegisterKIMParameters(KIM::ModelDriverCreate *const modelDriverCreate) { // Publish parameters int ier; ier = modelDriverCreate->SetParameterPointer(nelements, radelem.data(), "R", "List of cutoff radii, one for each type (distance units) 'radelem'."); if (ier) { LOG_ERROR("SetParameterPointer radelem"); return ier; } ier = modelDriverCreate->SetParameterPointer(nelements, wjelem.data(), "W", "List of neighbor weights, one for each type 'wjelem'."); if (ier) { LOG_ERROR("SetParameterPointer wjelem"); return ier; } ier = modelDriverCreate->SetParameterPointer(nelements * ncoeffall, coeffelem.data(), "coeffs", "Element bispectrum coefficients : " "(matrix of size M x N = " + std::to_string(nelements) + " x " + std::to_string(ncoeffall) + ") " + "in row-major storage. Ordering is according to each element. " "For example, to find the coefficient 'j' related to element 'i', " "use index = ((i - 1) * nelements + j). 'coeffelem'."); if (ier) { LOG_ERROR("SetParameterPointer coeffelem"); return ier; } ier = modelDriverCreate->SetParameterPointer(1, &rcutfac, "rcutfac", "Scale factor applied to all cutoff radii (positive real) 'rcutfac'."); if (ier) { LOG_ERROR("SetParameterPointer rcutfac"); return ier; } ier = modelDriverCreate->SetParameterPointer(1, &twojmax, "twojmax", "Band limit for bispectrum components (non-negative integer) 'twojmax'."); if (ier) { LOG_ERROR("SetParameterPointer twojmax"); return ier; } ier = modelDriverCreate->SetParameterPointer(1, &rfac0, "rfac0", "Parameter in distance to angle conversion (0 < rcutfac < 1) 'rfac0'."); if (ier) { LOG_ERROR("SetParameterPointer rfac0"); return ier; } ier = modelDriverCreate->SetParameterPointer(1, &rmin0, "rmin0", "Parameter in distance to angle conversion (distance units) 'rmin0'."); if (ier) { LOG_ERROR("SetParameterPointer rmin0"); return ier; } ier = modelDriverCreate->SetParameterPointer(1, &switchflag, "switchflag", "0 or 1\n " "0 = do not use switching function,\n " "1 = use switching function, 'switchflag'."); if (ier) { LOG_ERROR("SetParameterPointer switchflag"); return ier; } ier = modelDriverCreate->SetParameterPointer(1, &bzeroflag, "bzeroflag", "0 or 1\n " "0 = do not subtract B0,\n " "1 = subtract B0, 'bzeroflag'."); if (ier) { LOG_ERROR("SetParameterPointer bzeroflag"); return ier; } ier = modelDriverCreate->SetParameterPointer(1, &quadraticflag, "quadraticflag", "0 or 1\n " "0 = do not generate quadratic terms,\n " "1 = generate quadratic terms, 'quadraticflag'."); if (ier) { LOG_ERROR("SetParameterPointer quadraticflag"); return ier; } if (nzbls) { ier = modelDriverCreate->SetParameterPointer(1, &inner, "zbl_inner", "Distance where switching function in the ZBL interaction begins."); if (ier) { LOG_ERROR("SetParameterPointer switchflag"); return ier; } ier = modelDriverCreate->SetParameterPointer(1, &outer, "zbl_outer", "Distance Global cutoff for the ZBL interaction."); if (ier) { LOG_ERROR("SetParameterPointer switchflag"); return ier; } } // Everything is good return false; } #undef KIM_LOGGER_OBJECT_NAME #define KIM_LOGGER_OBJECT_NAME modelComputeArgumentsCreate int SNAPImplementation::RegisterKIMComputeArgumentsSettings(KIM::ModelComputeArgumentsCreate *const modelComputeArgumentsCreate) const { // register arguments LOG_INFORMATION("Register argument supportStatus"); int ier = modelComputeArgumentsCreate->SetArgumentSupportStatus(KIM::COMPUTE_ARGUMENT_NAME::partialEnergy, KIM::SUPPORT_STATUS::optional) || modelComputeArgumentsCreate->SetArgumentSupportStatus(KIM::COMPUTE_ARGUMENT_NAME::partialForces, KIM::SUPPORT_STATUS::optional) || modelComputeArgumentsCreate->SetArgumentSupportStatus(KIM::COMPUTE_ARGUMENT_NAME::partialParticleEnergy, KIM::SUPPORT_STATUS::optional) || modelComputeArgumentsCreate->SetArgumentSupportStatus(KIM::COMPUTE_ARGUMENT_NAME::partialVirial, KIM::SUPPORT_STATUS::optional) || modelComputeArgumentsCreate->SetArgumentSupportStatus(KIM::COMPUTE_ARGUMENT_NAME::partialParticleVirial, KIM::SUPPORT_STATUS::optional); // register callbacks LOG_INFORMATION("Register callback supportStatus"); ier = ier || modelComputeArgumentsCreate->SetCallbackSupportStatus(KIM::COMPUTE_CALLBACK_NAME::ProcessDEDrTerm, KIM::SUPPORT_STATUS::optional) || modelComputeArgumentsCreate->SetCallbackSupportStatus(KIM::COMPUTE_CALLBACK_NAME::ProcessD2EDr2Term, KIM::SUPPORT_STATUS::optional); return ier; } #undef KIM_LOGGER_OBJECT_NAME void SNAPImplementation::computeBispectrum(KIM::ModelComputeArguments const *const modelComputeArguments, int const *particleSpeciesCodes, int const *particleContributing, VectorOfSizeDIM const *coordinates) { // calculate contribution from pair function int numnei = 0; int const *n1atom = NULL; // Setup loop over contributing particles for (int i = 0, contributing_index = 0; i < cachedNumberOfParticles_; ++i) { if (!particleContributing[i]) { continue; } // Get the species index for atom i int const iSpecies = particleSpeciesCodes[i]; if (!snapflag[iSpecies]) { continue; } { // Get neighbors of i modelComputeArguments->GetNeighborList(0, i, &numnei, &n1atom); double const radi = radelem[iSpecies]; double const x = coordinates[i][0]; double const y = coordinates[i][1]; double const z = coordinates[i][2]; // Make sure {rij, inside, wj, and rcutij} arrays are big enough for numnei atoms snap->grow_rij(numnei); // number of neighbors of I within cutoff int ninside = 0; // note Rij sign convention => dU/dRij = dU/dRj = -dU/dRi // Setup loop over neighbors of current particle for (int n = 0; n < numnei; ++n) { int const j = n1atom[n]; int const jSpecies = particleSpeciesCodes[j]; if (snapflag[jSpecies]) { double const dx = coordinates[j][0] - x; double const dy = coordinates[j][1] - y; double const dz = coordinates[j][2] - z; double const rsq = dx * dx + dy * dy + dz * dz; if (rsq < cutsq(iSpecies, jSpecies) && rsq > 1e-20) { snap->rij(ninside, 0) = dx; snap->rij(ninside, 1) = dy; snap->rij(ninside, 2) = dz; snap->inside[ninside] = j; snap->wj[ninside] = wjelem[jSpecies]; snap->rcutij[ninside] = (radi + radelem[jSpecies]) * rcutfac; ++ninside; } } } snap->compute_ui(ninside); snap->compute_zi(); snap->compute_bi(); auto Bi = bispectrum.data_1D(contributing_index++); for (int icoeff = 0; icoeff < ncoeff; ++icoeff) { Bi[icoeff] = snap->blist[icoeff]; } } } } void SNAPImplementation::computeBeta(int const *particleSpeciesCodes, int const *particleContributing) { // Setup loop over contributing particles if (quadraticflag) { for (int i = 0, contributing_index = 0; i < cachedNumberOfParticles_; ++i) { if (!particleContributing[i]) { continue; } // Get the species index for atom i int const iSpecies = particleSpeciesCodes[i]; if (!snapflag[iSpecies]) { continue; } { // Get the 1D view to the 2D coeffelem array at row iSpecies auto coeffi = coeffelem.data_1D(iSpecies); // Get the pointer to the raw data + 1 to avoid extra sum double *Ci = coeffi.data() + 1; // Get the 1D view to the 2D beta array at row i auto bi = beta.data_1D(contributing_index); for (int icoeff = 0; icoeff < ncoeff; ++icoeff) { bi[icoeff] = Ci[icoeff]; } // Get the pointer to the start of coeffi array of data --Ci; // Get the 1D view to the 2D bispectrum array at row i auto Bi = bispectrum.data_1D(contributing_index++); int k = ncoeff + 1; for (int icoeff = 0; icoeff < ncoeff; ++icoeff) { double const bveci = Bi[icoeff]; bi[icoeff] += Ci[k++] * bveci; for (int jcoeff = icoeff + 1; jcoeff < ncoeff; ++jcoeff, ++k) { bi[icoeff] += Ci[k] * Bi[jcoeff]; bi[jcoeff] += Ci[k] * bveci; } } } } } else { for (int i = 0, contributing_index = 0; i < cachedNumberOfParticles_; ++i) { if (!particleContributing[i]) { continue; } // Get the species index for atom i int const iSpecies = particleSpeciesCodes[i]; if (snapflag[iSpecies]) { // Get the 1D view to the 2D coeffelem array at row iSpecies auto coeffi = coeffelem.data_1D(iSpecies); // Get the pointer to the raw data + 1 to avoid extra sum double *Ci = coeffi.data() + 1; // Get the 1D view to the 2D beta array at row i auto bi = beta.data_1D(contributing_index++); for (int icoeff = 0; icoeff < ncoeff; ++icoeff) { bi[icoeff] = Ci[icoeff]; } } } } } #define KIM_LOGGER_OBJECT_NAME modelComputeArguments template int SNAPImplementation::Compute(KIM::ModelCompute const *const /* modelCompute */, KIM::ModelComputeArguments const *const modelComputeArguments, int const *const particleSpeciesCodes, int const *const particleContributing, const VectorOfSizeDIM *const coordinates, double *const energy, VectorOfSizeDIM *const forces, double *const particleEnergy, VectorOfSizeSix virial, VectorOfSizeSix *const particleVirial) const { // Everything is good int ier = false; // Nothing to compute if (!isComputeEnergy && !isComputeForces && !isComputeParticleEnergy && !isComputeVirial && !isComputeParticleVirial && !isComputeProcess_dEdr && !isComputeProcess_d2Edr2 && !isHybrid) { return ier; } // Initialize energy if (isComputeEnergy) { *energy = 0.0; } // Initialize forces if (isComputeForces) { for (int i = 0; i < cachedNumberOfParticles_; ++i) { for (int j = 0; j < 3; ++j) { forces[i][j] = 0.0; } } } // Initialize particle energy if (isComputeParticleEnergy) { for (int i = 0; i < cachedNumberOfParticles_; ++i) { particleEnergy[i] = 0.0; } } // Initialize virial if (isComputeVirial) { for (int i = 0; i < 6; ++i) { virial[i] = 0.0; } } // Initialize particle virial if (isComputeParticleVirial) { for (int i = 0; i < cachedNumberOfParticles_; ++i) { for (int j = 0; j < 6; ++j) { particleVirial[i][j] = 0.0; } } } // If it is a hybrid style if (isHybrid) { double dEidr_ZBL; double dEidrByR_ZBL; double dEidrByR_TABLE; double a; double b; double fraction; int itable; TABLE const *tb; union_int_float_t rsq_lookup; int numnei = 0; int const *n1atom = NULL; // Loop over all the contributing particles for (int i = 0, contributing_index = 0; i < cachedNumberOfParticles_; ++i) { if (!particleContributing[i]) { continue; } // Get the species index for atom i int const iSpecies = particleSpeciesCodes[i]; double const xi = coordinates[i][0]; double const yi = coordinates[i][1]; double const zi = coordinates[i][2]; // Calculate contribution // Get the neighbors modelComputeArguments->GetNeighborList(0, i, &numnei, &n1atom); // Make sure {rij, inside, wj, and rcutij} arrays are big enough for numnei atoms snap->grow_rij(numnei); // number of neighbors of I within cutoff int ninside = 0; if (snapflag[iSpecies]) { // Get the iSpecies cutoff double const radi = radelem[iSpecies]; // noutside, is the number of neighbors of I outside the cutoff // note Rij sign convention => dU/dRij = dU/dRj = -dU/dRi // Setup loop over neighbors of current particle for (int n = 0, noutside = numnei; n < numnei; ++n) { // Index of the neighbor atom int const j = n1atom[n]; // Get the species index for atom j int const jSpecies = particleSpeciesCodes[j]; double const dx = coordinates[j][0] - xi; double const dy = coordinates[j][1] - yi; double const dz = coordinates[j][2] - zi; if (snapflag[jSpecies]) { double const rsq = dx * dx + dy * dy + dz * dz; if (rsq < cutsq(iSpecies, jSpecies) && rsq > 1e-20) { snap->rij(ninside, 0) = dx; snap->rij(ninside, 1) = dy; snap->rij(ninside, 2) = dz; snap->inside[ninside] = j; snap->wj[ninside] = wjelem[jSpecies]; // Get the pair of iSpecies & jSpecies cutoff snap->rcutij[ninside] = (radi + radelem[jSpecies]) * rcutfac; ++ninside; } else { --noutside; snap->rij(noutside, 0) = dx; snap->rij(noutside, 1) = dy; snap->rij(noutside, 2) = dz; snap->inside[noutside] = j; } } else { --noutside; snap->rij(noutside, 0) = dx; snap->rij(noutside, 1) = dy; snap->rij(noutside, 2) = dz; snap->inside[noutside] = j; } } // compute Ui, Yi for atom i snap->compute_ui(ninside); // Get the 1D view to the 2D beta array at row i auto betai = beta.data_1D(contributing_index); // Get the pointer to the beta array of data for atom i double const *const bi = const_cast(betai.data()); snap->compute_yi(bi); } else { // Setup loop over neighbors of current particle for (int n = 0; n < numnei; ++n) { // Index of the neighbor atom int const j = n1atom[n]; double const dx = coordinates[j][0] - xi; double const dy = coordinates[j][1] - yi; double const dz = coordinates[j][2] - zi; snap->rij(n, 0) = dx; snap->rij(n, 1) = dy; snap->rij(n, 2) = dz; snap->inside[n] = j; } } // Compute contribution to force, etc. // For the neighbors of particle i within the cutoff: // Compute deidrj = dEi/dRj = -dEi/dRi add to Fi, subtract from Fj VectorOfSizeDIM deidrj; // Setup loop over neighbors of particle i for (int n = 0; n < numnei; ++n) { // Index of the neighbor atom int const j = snap->inside[n]; // Get the species index for atom j int const jSpecies = particleSpeciesCodes[j]; // If particle j is contributing or not int const jContrib = particleContributing[j]; bool const lsnap = n < ninside; bool const lhalf = !(jContrib && (j < i)); if (!lsnap) { if (!lhalf) { continue; } if (setflag(iSpecies, jSpecies) == HYBRIDSTYLE::NONE) { continue; } } bool lzbl = lhalf ? (setflag(iSpecies, jSpecies) == HYBRIDSTYLE::ZBL) : false; bool ltable = lhalf ? (setflag(iSpecies, jSpecies) == HYBRIDSTYLE::TABLE) : false; if (ltable) { tb = &tables[tableNumber(iSpecies, jSpecies)]; } // Get the 1D view to the 2D snap->rij array at row n auto rij = snap->rij.data_1D(n); // Get the pointer to the rij_const array of data double const *const rij_const = const_cast(rij.data()); double const rsq = rij_const[0] * rij_const[0] + rij_const[1] * rij_const[1] + rij_const[2] * rij_const[2]; double const rrsq = std::sqrt(rsq); if (lzbl) { lzbl = (rsq < zbl->cut_globalsq); } if (ltable) { ltable = (rrsq < tb->cut) ? (rsq > tb->innersq) : false; } if (lsnap) { snap->compute_duidrj(rij_const, snap->wj[n], snap->rcutij[n], n); snap->compute_deidrj(deidrj); } if (lzbl) { // Compute dphi if (isComputeForces || isComputeProcess_dEdr || isComputeVirial || isComputeParticleVirial) { dEidr_ZBL = zbl->dzbldr(rrsq, iSpecies, jSpecies); if (rsq > zbl->cut_innersq) { double const t = rrsq - zbl->cut_inner; double const fswitch = t * t * (zbl->sw1(iSpecies, jSpecies) + zbl->sw2(iSpecies, jSpecies) * t); dEidr_ZBL += fswitch; } if (!jContrib) { dEidr_ZBL *= 0.5; } dEidrByR_ZBL = dEidr_ZBL / rrsq; } else { dEidr_ZBL = 0.0; dEidrByR_ZBL = 0.0; } } if (ltable) { int const tlm1 = tb->tableLength - 1; if (tb->tableStyle == TABLESTYLE::LOOKUP) { itable = static_cast((rsq - tb->innersq) * tb->invdelta); if (itable >= tlm1) { LOG_ERROR("Pair distance (" + std::to_string(rrsq) + ") > table outer cutoff between types (" + hybridStyleSpeciesNames[iSpecies] + ", " + hybridStyleSpeciesNames[jSpecies] + ")"); return true; } dEidrByR_TABLE = tb->f[itable]; } else if (tb->tableStyle == TABLESTYLE::LINEAR) { itable = static_cast((rsq - tb->innersq) * tb->invdelta); if (itable >= tlm1) { LOG_ERROR("Pair distance (" + std::to_string(rrsq) + ") > table outer cutoff between types (" + hybridStyleSpeciesNames[iSpecies] + ", " + hybridStyleSpeciesNames[jSpecies] + ")"); return true; } fraction = (rsq - tb->rsq[itable]) * tb->invdelta; dEidrByR_TABLE = tb->f[itable] + fraction * tb->df[itable]; } else if (tb->tableStyle == TABLESTYLE::SPLINE) { itable = static_cast((rsq - tb->innersq) * tb->invdelta); if (itable >= tlm1) { LOG_ERROR("Pair distance (" + std::to_string(rrsq) + ") > table outer cutoff between types (" + hybridStyleSpeciesNames[iSpecies] + ", " + hybridStyleSpeciesNames[jSpecies] + ")"); return true; } b = (rsq - tb->rsq[itable]) * tb->invdelta; a = 1.0 - b; dEidrByR_TABLE = a * tb->f[itable] + b * tb->f[itable + 1] + ((a * a * a - a) * tb->f2[itable] + (b * b * b - b) * tb->f2[itable + 1]) * tb->deltasq6; } else if (tb->tableStyle == TABLESTYLE::BITMAP) { rsq_lookup.f = rsq; itable = rsq_lookup.i & tb->nmask; itable >>= tb->nshiftbits; fraction = (rsq_lookup.f - tb->rsq[itable]) * tb->drsq[itable]; dEidrByR_TABLE = tb->f[itable] + fraction * tb->df[itable]; } dEidrByR_TABLE *= jContrib ? -1.0 : -0.5; } // Contribution to forces if (isComputeForces) { if (lsnap) { forces[i][0] += deidrj[0]; forces[i][1] += deidrj[1]; forces[i][2] += deidrj[2]; forces[j][0] -= deidrj[0]; forces[j][1] -= deidrj[1]; forces[j][2] -= deidrj[2]; } if (lzbl) { forces[i][0] += dEidrByR_ZBL * rij_const[0]; forces[i][1] += dEidrByR_ZBL * rij_const[1]; forces[i][2] += dEidrByR_ZBL * rij_const[2]; forces[j][0] -= dEidrByR_ZBL * rij_const[0]; forces[j][1] -= dEidrByR_ZBL * rij_const[1]; forces[j][2] -= dEidrByR_ZBL * rij_const[2]; } if (ltable) { forces[i][0] += dEidrByR_TABLE * rij_const[0]; forces[i][1] += dEidrByR_TABLE * rij_const[1]; forces[i][2] += dEidrByR_TABLE * rij_const[2]; forces[j][0] -= dEidrByR_TABLE * rij_const[0]; forces[j][1] -= dEidrByR_TABLE * rij_const[1]; forces[j][2] -= dEidrByR_TABLE * rij_const[2]; } } if (isComputeEnergy || isComputeParticleEnergy) { if (lzbl) { double phi = zbl->e_zbl(rrsq, iSpecies, jSpecies); phi += zbl->sw5(iSpecies, jSpecies); if (rsq > zbl->cut_innersq) { double const t = rrsq - zbl->cut_inner; double const eswitch = t * t * t * (zbl->sw3(iSpecies, jSpecies) + zbl->sw4(iSpecies, jSpecies) * t); phi += eswitch; } if (isComputeEnergy) { *energy += jContrib ? phi : 0.5 * phi; } if (isComputeParticleEnergy) { particleEnergy[i] += 0.5 * phi; if (jContrib) { particleEnergy[j] += 0.5 * phi; } } } if (ltable) { double phi; if (tb->tableStyle == TABLESTYLE::LOOKUP) { phi = tb->e[itable]; } else if (tb->tableStyle == TABLESTYLE::LINEAR) { phi = tb->e[itable] + fraction * tb->de[itable]; } else if (tb->tableStyle == TABLESTYLE::SPLINE) { phi = a * tb->e[itable] + b * tb->e[itable + 1] + ((a * a * a - a) * tb->e2[itable] + (b * b * b - b) * tb->e2[itable + 1]) * tb->deltasq6; } else if (tb->tableStyle == TABLESTYLE::BITMAP) { phi = tb->e[itable] + fraction * tb->de[itable]; } if (isComputeEnergy) { *energy += jContrib ? phi : 0.5 * phi; } if (isComputeParticleEnergy) { particleEnergy[i] += 0.5 * phi; if (jContrib) { particleEnergy[j] += 0.5 * phi; } } } } if (isComputeProcess_dEdr) { double dedr = lsnap ? std::sqrt(deidrj[0] * deidrj[0] + deidrj[1] * deidrj[1] + deidrj[2] * deidrj[2]) : 0.0; if (lzbl) { dedr += dEidr_ZBL; } if (ltable) { dedr += dEidrByR_TABLE * rrsq; } ier = modelComputeArguments->ProcessDEDrTerm(dedr, rrsq, rij_const, i, j); if (ier) { LOG_ERROR("ProcessDEDrTerm"); return ier; } } if (isComputeVirial || isComputeParticleVirial) { // Virial has 6 components and is stored as a 6-element // vector in the following order: xx, yy, zz, yz, xz, xy. VectorOfSizeSix v; if (lsnap) { v[0] = rij_const[0] * deidrj[0]; v[1] = rij_const[1] * deidrj[1]; v[2] = rij_const[2] * deidrj[2]; v[3] = rij_const[1] * deidrj[2]; v[4] = rij_const[0] * deidrj[2]; v[5] = rij_const[0] * deidrj[1]; } else { v[0] = 0.0; v[1] = 0.0; v[2] = 0.0; v[3] = 0.0; v[4] = 0.0; v[5] = 0.0; } if (lzbl) { v[0] += rij_const[0] * rij_const[0] * dEidrByR_ZBL; v[1] += rij_const[1] * rij_const[1] * dEidrByR_ZBL; v[2] += rij_const[2] * rij_const[2] * dEidrByR_ZBL; v[3] += rij_const[1] * rij_const[2] * dEidrByR_ZBL; v[4] += rij_const[0] * rij_const[2] * dEidrByR_ZBL; v[5] += rij_const[0] * rij_const[1] * dEidrByR_ZBL; } if (ltable) { v[0] += rij_const[0] * rij_const[0] * dEidrByR_TABLE; v[1] += rij_const[1] * rij_const[1] * dEidrByR_TABLE; v[2] += rij_const[2] * rij_const[2] * dEidrByR_TABLE; v[3] += rij_const[1] * rij_const[2] * dEidrByR_TABLE; v[4] += rij_const[0] * rij_const[2] * dEidrByR_TABLE; v[5] += rij_const[0] * rij_const[1] * dEidrByR_TABLE; } if (isComputeVirial) { virial[0] += v[0]; virial[1] += v[1]; virial[2] += v[2]; virial[3] += v[3]; virial[4] += v[4]; virial[5] += v[5]; } if (isComputeParticleVirial) { v[0] *= 0.5; v[1] *= 0.5; v[2] *= 0.5; v[3] *= 0.5; v[4] *= 0.5; v[5] *= 0.5; particleVirial[i][0] += v[0]; particleVirial[i][1] += v[1]; particleVirial[i][2] += v[2]; particleVirial[i][3] += v[3]; particleVirial[i][4] += v[4]; particleVirial[i][5] += v[5]; particleVirial[j][0] += v[0]; particleVirial[j][1] += v[1]; particleVirial[j][2] += v[2]; particleVirial[j][3] += v[3]; particleVirial[j][4] += v[4]; particleVirial[j][5] += v[5]; } // isComputeParticleVirial } // isComputeVirial || isComputeParticleVirial } // End of loop over neighbors of particle i if (snapflag[iSpecies]) { // Energy contribution if (isComputeEnergy || isComputeParticleEnergy) { // Compute contribution to energy. // Energy of particle i, sum over coeffs_k * Bi_k // Get the 1D view to the 2D coeffelem array at row iSpecies auto coeffi = coeffelem.data_1D(iSpecies); // Compute phi double phi = coeffi[0]; // Get the pointer to the raw data + 1 to avoid extra summation double *Ci = coeffi.data() + 1; // Get the bispectrum of particle i // Get the 1D view to the 2D bispectrum array at row i auto Bi = bispectrum.data_1D(contributing_index); // E = beta.B + 0.5*B^t.alpha.B // Linear contributions for (int icoeff = 0; icoeff < ncoeff; ++icoeff) { phi += Ci[icoeff] * Bi[icoeff]; } // Quadratic contributions if (quadraticflag) { // Get the pointer to the start of coeffi array of data --Ci; int k = ncoeff + 1; for (int icoeff = 0; icoeff < ncoeff; ++icoeff) { double const bveci = Bi[icoeff]; phi += 0.5 * Ci[k++] * bveci * bveci; for (int jcoeff = icoeff + 1; jcoeff < ncoeff; ++jcoeff) { double const bvecj = Bi[jcoeff]; phi += Ci[k++] * bveci * bvecj; } } } // quadraticflag // Contribution to energy if (isComputeEnergy) { *energy += phi; } // Contribution to particleEnergy if (isComputeParticleEnergy) { particleEnergy[i] += phi; } } // isComputeEnergy || isComputeParticleEnergy ++contributing_index; } // snapflag[iSpecies] } // End of loop over contributing particles } // If it is a hybrid style else // If it is not a hybrid style { int numnei = 0; int const *n1atom = NULL; // Loop over all the contributing particles for (int i = 0, contributing_index = 0; i < cachedNumberOfParticles_; ++i) { if (!particleContributing[i]) { continue; } // Get the species index for atom i int const iSpecies = particleSpeciesCodes[i]; // Get the iSpecies cutoff double const radi = radelem[iSpecies]; double const xi = coordinates[i][0]; double const yi = coordinates[i][1]; double const zi = coordinates[i][2]; // Calculate contribution // Get the neighbors modelComputeArguments->GetNeighborList(0, i, &numnei, &n1atom); // Make sure {rij, inside, wj, and rcutij} arrays are big enough for numnei atoms snap->grow_rij(numnei); // number of neighbors of I within cutoff int ninside = 0; // note Rij sign convention => dU/dRij = dU/dRj = -dU/dRi // Setup loop over neighbors of current particle for (int n = 0; n < numnei; ++n) { // Index of the neighbor atom int const j = n1atom[n]; // Get the species index for atom j int const jSpecies = particleSpeciesCodes[j]; double const dx = coordinates[j][0] - xi; double const dy = coordinates[j][1] - yi; double const dz = coordinates[j][2] - zi; double const rsq = dx * dx + dy * dy + dz * dz; if (rsq < cutsq(iSpecies, jSpecies) && rsq > 1e-20) { snap->rij(ninside, 0) = dx; snap->rij(ninside, 1) = dy; snap->rij(ninside, 2) = dz; snap->inside[ninside] = j; snap->wj[ninside] = wjelem[jSpecies]; // Get the pair of iSpecies & jSpecies cutoff snap->rcutij[ninside] = (radi + radelem[jSpecies]) * rcutfac; ++ninside; } } // compute Ui, Yi for atom i { snap->compute_ui(ninside); // Get the 1D view to the 2D beta array at row i auto betai = beta.data_1D(contributing_index); // Get the pointer to the beta array of data for atom i double const *const bi = const_cast(betai.data()); snap->compute_yi(bi); } // Compute contribution to force, etc. // For the neighbors of particle i within the cutoff: // Compute deidrj = dEi/dRj = -dEi/dRi add to Fi, subtract from Fj VectorOfSizeDIM deidrj; // Setup loop over neighbors of particle i for (int n = 0; n < ninside; ++n) { // Get the 1D view to the 2D snap->rij array at row n auto rij = snap->rij.data_1D(n); // Get the pointer to the rij_const array of data double const *const rij_const = const_cast(rij.data()); snap->compute_duidrj(rij_const, snap->wj[n], snap->rcutij[n], n); snap->compute_deidrj(deidrj); // Index of the neighbor atom int const j = snap->inside[n]; // Contribution to forces if (isComputeForces) { forces[i][0] += deidrj[0]; forces[i][1] += deidrj[1]; forces[i][2] += deidrj[2]; forces[j][0] -= deidrj[0]; forces[j][1] -= deidrj[1]; forces[j][2] -= deidrj[2]; } if (isComputeProcess_dEdr) { double const rrsq = std::sqrt(rij_const[0] * rij_const[0] + rij_const[1] * rij_const[1] + rij_const[2] * rij_const[2]); double const dedr = std::sqrt(deidrj[0] * deidrj[0] + deidrj[1] * deidrj[1] + deidrj[2] * deidrj[2]); ier = modelComputeArguments->ProcessDEDrTerm(dedr, rrsq, rij_const, i, j); if (ier) { LOG_ERROR("ProcessDEDrTerm"); return ier; } } if (isComputeVirial || isComputeParticleVirial) { // Virial has 6 components and is stored as a 6-element // vector in the following order: xx, yy, zz, yz, xz, xy. VectorOfSizeSix v; v[0] = rij_const[0] * deidrj[0]; v[1] = rij_const[1] * deidrj[1]; v[2] = rij_const[2] * deidrj[2]; v[3] = rij_const[1] * deidrj[2]; v[4] = rij_const[0] * deidrj[2]; v[5] = rij_const[0] * deidrj[1]; if (isComputeVirial) { virial[0] += v[0]; virial[1] += v[1]; virial[2] += v[2]; virial[3] += v[3]; virial[4] += v[4]; virial[5] += v[5]; } if (isComputeParticleVirial) { v[0] *= 0.5; v[1] *= 0.5; v[2] *= 0.5; v[3] *= 0.5; v[4] *= 0.5; v[5] *= 0.5; particleVirial[i][0] += v[0]; particleVirial[i][1] += v[1]; particleVirial[i][2] += v[2]; particleVirial[i][3] += v[3]; particleVirial[i][4] += v[4]; particleVirial[i][5] += v[5]; particleVirial[j][0] += v[0]; particleVirial[j][1] += v[1]; particleVirial[j][2] += v[2]; particleVirial[j][3] += v[3]; particleVirial[j][4] += v[4]; particleVirial[j][5] += v[5]; } // isComputeParticleVirial } // isComputeVirial || isComputeParticleVirial } // End of loop over neighbors of particle i // Energy contribution if (isComputeEnergy || isComputeParticleEnergy) { // Compute contribution to energy. // Energy of particle i, sum over coeffs_k * Bi_k // Get the 1D view to the 2D coeffelem array at row iSpecies auto coeffi = coeffelem.data_1D(iSpecies); // Compute phi double phi = coeffi[0]; // Get the pointer to the raw data + 1 to avoid extra summation double *Ci = coeffi.data() + 1; // Get the bispectrum of particle i // Get the 1D view to the 2D bispectrum array at row i auto Bi = bispectrum.data_1D(contributing_index); // E = beta.B + 0.5*B^t.alpha.B // Linear contributions for (int icoeff = 0; icoeff < ncoeff; ++icoeff) { phi += Ci[icoeff] * Bi[icoeff]; } // Quadratic contributions if (quadraticflag) { // Get the pointer to the start of coeffi array of data --Ci; int k = ncoeff + 1; for (int icoeff = 0; icoeff < ncoeff; ++icoeff) { double const bveci = Bi[icoeff]; phi += 0.5 * Ci[k++] * bveci * bveci; for (int jcoeff = icoeff + 1; jcoeff < ncoeff; ++jcoeff) { double const bvecj = Bi[jcoeff]; phi += Ci[k++] * bveci * bvecj; } } } // quadraticflag // Contribution to energy if (isComputeEnergy) { *energy += phi; } // Contribution to particleEnergy if (isComputeParticleEnergy) { particleEnergy[i] += phi; } } // isComputeEnergy || isComputeParticleEnergy ++contributing_index; } // End of loop over contributing particles } // If it is not a hybrid style // everything is good return false; } #undef KIM_LOGGER_OBJECT_NAME #undef ONE #undef MAXLINE #undef HELPER_LOG_ERROR