// // CDDL HEADER START // // The contents of this file are subject to the terms of the Common Development // and Distribution License Version 1.0 (the "License"). // // You can obtain a copy of the license at // http://www.opensource.org/licenses/CDDL-1.0. See the License for the // specific language governing permissions and limitations under the License. // // When distributing Covered Code, include this CDDL HEADER in each file and // include the License file in a prominent location with the name LICENSE.CDDL. // If applicable, add the following below this CDDL HEADER, with the fields // enclosed by brackets "[]" replaced with your own identifying information: // // Portions Copyright (c) [yyyy] [name of copyright owner]. All rights reserved. // // CDDL HEADER END // // // Copyright (c) 2018--2021, Regents of the University of Minnesota. // All rights reserved. // // Contributors: // Mingjian Wen // Yaser Afshar // #include "KIM_ModelDriverHeaders.hpp" #include "StillingerWeberImplementation.hpp" #include #include #include #include #include #include #include #define MAXLINE 1024 //============================================================================== // // Implementation of StillingerWeberImplementation public member functions // //============================================================================== //****************************************************************************** #undef KIM_LOGGER_OBJECT_NAME #define KIM_LOGGER_OBJECT_NAME modelDriverCreate StillingerWeberImplementation::StillingerWeberImplementation( 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) : numberModelSpecies_(0), numberUniqueSpeciesPairs_(0), cutoff_(NULL), A_(NULL), B_(NULL), p_(NULL), q_(NULL), sigma_(NULL), lambda_(NULL), gamma_(NULL), costheta0_(NULL), influenceDistance_(0.0), modelWillNotRequestNeighborsOfNoncontributingParticles_(1), cutoffSq_2D_(NULL), A_2D_(NULL), B_2D_(NULL), p_2D_(NULL), q_2D_(NULL), sigma_2D_(NULL), lambda_2D_(NULL), gamma_2D_(NULL), costheta0_2D_(NULL), cachedNumberOfParticles_(0) { FILE * parameterFilePointers[MAX_PARAMETER_FILES]; int numberParameterFiles; modelDriverCreate->GetNumberOfParameterFiles(&numberParameterFiles); *ier = OpenParameterFiles( modelDriverCreate, numberParameterFiles, parameterFilePointers); if (*ier) { return; } *ier = ProcessParameterFiles(modelDriverCreate, 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; } // everything is good *ier = false; return; } //****************************************************************************** StillingerWeberImplementation::~StillingerWeberImplementation() { // note: it is ok to delete a null pointer and we have ensured that // everything is initialized to null Deallocate1DArray(A_); Deallocate1DArray(B_); Deallocate1DArray(p_); Deallocate1DArray(q_); Deallocate1DArray(sigma_); Deallocate1DArray(lambda_); Deallocate1DArray(gamma_); Deallocate1DArray(costheta0_); Deallocate1DArray(cutoff_); Deallocate2DArray(A_2D_); Deallocate2DArray(B_2D_); Deallocate2DArray(p_2D_); Deallocate2DArray(q_2D_); Deallocate2DArray(sigma_2D_); Deallocate2DArray(lambda_2D_); Deallocate2DArray(gamma_2D_); Deallocate2DArray(costheta0_2D_); Deallocate2DArray(cutoffSq_2D_); } //****************************************************************************** #undef KIM_LOGGER_OBJECT_NAME #define KIM_LOGGER_OBJECT_NAME modelRefresh int StillingerWeberImplementation::Refresh( KIM::ModelRefresh * const modelRefresh) { int ier = SetRefreshMutableValues(modelRefresh); if (ier) { return ier; } // nothing else to do for this case // everything is good ier = false; return ier; } //****************************************************************************** int StillingerWeberImplementation::Compute( KIM::ModelCompute const * const modelCompute, KIM::ModelComputeArguments const * const modelComputeArguments) { int ier; // 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; // // KIM API Model Input int const * particleSpeciesCodes = NULL; int const * particleContributing = NULL; VectorOfSizeDIM const * coordinates = NULL; // // KIM API Model Output double * energy = NULL; double * particleEnergy = NULL; VectorOfSizeDIM * forces = NULL; VectorOfSizeSix * virial = NULL; VectorOfSizeSix * particleVirial = NULL; ier = SetComputeMutableValues(modelComputeArguments, isComputeProcess_dEdr, isComputeProcess_d2Edr2, isComputeEnergy, isComputeForces, isComputeParticleEnergy, isComputeVirial, isComputeParticleVirial, particleSpeciesCodes, particleContributing, coordinates, energy, forces, particleEnergy, virial, particleVirial); if (ier) { return ier; } // Skip this check for efficiency // // ier = CheckParticleSpecies(modelComputeArguments, particleSpeciesCodes); // if (ier) return ier; #include "StillingerWeberImplementationComputeDispatch.cpp" return ier; } //****************************************************************************** int StillingerWeberImplementation::ComputeArgumentsCreate( KIM::ModelComputeArgumentsCreate * const modelComputeArgumentsCreate) const { int ier = RegisterKIMComputeArgumentsSettings(modelComputeArgumentsCreate); if (ier) { return ier; } // nothing else to do for this case // everything is good ier = false; return ier; } //****************************************************************************** int StillingerWeberImplementation::ComputeArgumentsDestroy( KIM::ModelComputeArgumentsDestroy * const /* modelComputeArgumentsDestroy */) const { // nothing to do for this case // everything is good int ier = false; return ier; } //============================================================================== // // Implementation of StillingerWeberImplementation private member functions // //============================================================================== //****************************************************************************** void StillingerWeberImplementation::AllocatePrivateParameterMemory() { // nothing to do for this case } //****************************************************************************** void StillingerWeberImplementation::AllocateParameterMemory() { // allocate memory for data AllocateAndInitialize1DArray(cutoff_, numberUniqueSpeciesPairs_); AllocateAndInitialize1DArray(A_, numberUniqueSpeciesPairs_); AllocateAndInitialize1DArray(B_, numberUniqueSpeciesPairs_); AllocateAndInitialize1DArray(p_, numberUniqueSpeciesPairs_); AllocateAndInitialize1DArray(q_, numberUniqueSpeciesPairs_); AllocateAndInitialize1DArray(sigma_, numberUniqueSpeciesPairs_); AllocateAndInitialize1DArray(lambda_, numberUniqueSpeciesPairs_); AllocateAndInitialize1DArray(gamma_, numberUniqueSpeciesPairs_); AllocateAndInitialize1DArray(costheta0_, numberUniqueSpeciesPairs_); AllocateAndInitialize2DArray( cutoffSq_2D_, numberModelSpecies_, numberModelSpecies_); AllocateAndInitialize2DArray( A_2D_, numberModelSpecies_, numberModelSpecies_); AllocateAndInitialize2DArray( B_2D_, numberModelSpecies_, numberModelSpecies_); AllocateAndInitialize2DArray( p_2D_, numberModelSpecies_, numberModelSpecies_); AllocateAndInitialize2DArray( q_2D_, numberModelSpecies_, numberModelSpecies_); AllocateAndInitialize2DArray( sigma_2D_, numberModelSpecies_, numberModelSpecies_); AllocateAndInitialize2DArray( lambda_2D_, numberModelSpecies_, numberModelSpecies_); AllocateAndInitialize2DArray( gamma_2D_, numberModelSpecies_, numberModelSpecies_); AllocateAndInitialize2DArray( costheta0_2D_, numberModelSpecies_, numberModelSpecies_); } //****************************************************************************** #undef KIM_LOGGER_OBJECT_NAME #define KIM_LOGGER_OBJECT_NAME modelDriverCreate int StillingerWeberImplementation::OpenParameterFiles( KIM::ModelDriverCreate * const modelDriverCreate, int const numberParameterFiles, FILE * parameterFilePointers[MAX_PARAMETER_FILES]) { if (numberParameterFiles > MAX_PARAMETER_FILES) { LOG_ERROR("StillingerWeber given too many parameter files"); return true; } int ier; std::string const * parameterFileDirectoryName; modelDriverCreate->GetParameterFileDirectoryName( ¶meterFileDirectoryName); for (int i = 0; i < numberParameterFiles; ++i) { std::string const * paramFileBasename; ier = modelDriverCreate->GetParameterFileBasename(i, ¶mFileBasename); if (ier) { LOG_ERROR("Unable to get parameter file base name"); return ier; } std::string const paramFileName = *parameterFileDirectoryName + "/" + *paramFileBasename; parameterFilePointers[i] = fopen(paramFileName.c_str(), "r"); if (parameterFilePointers[i] == 0) { char message[MAXLINE]; sprintf(message, "StillingerWeber parameter file number %d cannot be opened", i); LOG_ERROR(message); for (int j = i - 1; i <= 0; --i) { fclose(parameterFilePointers[j]); } return true; } } // everything is good ier = false; return ier; } //****************************************************************************** #undef KIM_LOGGER_OBJECT_NAME #define KIM_LOGGER_OBJECT_NAME modelDriverCreate int StillingerWeberImplementation::ProcessParameterFiles( KIM::ModelDriverCreate * const modelDriverCreate, FILE * const parameterFilePointers[MAX_PARAMETER_FILES]) { int N, ier; int endOfFileFlag = 0; char spec1[MAXLINE], spec2[MAXLINE], nextLine[MAXLINE]; int iIndex, jIndex, indx; double next_A, next_B, next_p, next_q, next_sigma, next_lambda, next_gamma; double next_costheta0, next_cutoff; getNextDataLine(parameterFilePointers[0], nextLine, MAXLINE, &endOfFileFlag); ier = sscanf(nextLine, "%d", &N); if (ier != 1) { LOG_ERROR("unable to read first line of the parameter file"); fclose(parameterFilePointers[0]); return true; } numberModelSpecies_ = N; numberUniqueSpeciesPairs_ = ((numberModelSpecies_ + 1) * numberModelSpecies_) / 2; AllocateParameterMemory(); // set all values of p_ to -1.1e10 for later check that we have read all // params for (int i = 0; i < ((N + 1) * N / 2); i++) { p_[i] = -1.1e10; } // keep track of known species std::map modelSpeciesMap; int index = 0; // species code integer code starting from 0 // Read and process data lines getNextDataLine(parameterFilePointers[0], nextLine, MAXLINE, &endOfFileFlag); while (endOfFileFlag == 0) { ier = sscanf(nextLine, "%s %s %lg %lg %lg %lg %lg %lg %lg %lg %lg", spec1, spec2, &next_A, &next_B, &next_p, &next_q, &next_sigma, &next_lambda, &next_gamma, &next_costheta0, &next_cutoff); if (ier != 11) { LOG_ERROR("error reading lines of the parameter file"); return true; } // convert species strings to proper type instances KIM::SpeciesName const specName1(spec1); KIM::SpeciesName const specName2(spec2); if ((specName1.ToString() == "unknown") || (specName2.ToString() == "unknown")) { LOG_ERROR("error parameter file: get unknown species"); return true; } // check for new species std::map:: const_iterator iIter = modelSpeciesMap.find(specName1); if (iIter == modelSpeciesMap.end()) { modelSpeciesMap[specName1] = index; modelSpeciesCodeList_.push_back(index); modelSpeciesStringList_.push_back(spec1); ier = modelDriverCreate->SetSpeciesCode(specName1, index); if (ier) { return ier; } iIndex = index; index++; } else { iIndex = modelSpeciesMap[specName1]; } std::map:: const_iterator jIter = modelSpeciesMap.find(specName2); if (jIter == modelSpeciesMap.end()) { modelSpeciesMap[specName2] = index; modelSpeciesCodeList_.push_back(index); modelSpeciesStringList_.push_back(spec2); ier = modelDriverCreate->SetSpeciesCode(specName2, index); if (ier) { return ier; } jIndex = index; index++; } else { jIndex = modelSpeciesMap[specName2]; } if (iIndex >= jIndex) { indx = jIndex * N + iIndex - (jIndex * jIndex + jIndex) / 2; } else { indx = iIndex * N + jIndex - (iIndex * iIndex + iIndex) / 2; } A_[indx] = next_A; B_[indx] = next_B; p_[indx] = next_p; q_[indx] = next_q; sigma_[indx] = next_sigma; lambda_[indx] = next_lambda; gamma_[indx] = next_gamma; costheta0_[indx] = next_costheta0; cutoff_[indx] = next_cutoff; getNextDataLine( parameterFilePointers[0], nextLine, MAXLINE, &endOfFileFlag); } // check we have read all parameters for (int i = 0; i < ((N + 1) * N / 2); i++) { if (p_[i] < -1e10) { sprintf(nextLine, "error: not enough parameter data.\n"); sprintf( nextLine, "%d species requires %d data lines.", N, (N + 1) * N / 2); LOG_ERROR(nextLine); return true; } } // everything is good ier = false; return ier; } //****************************************************************************** void StillingerWeberImplementation::getNextDataLine(FILE * const filePtr, char * nextLinePtr, int const maxSize, int * endOfFileFlag) { do { if (fgets(nextLinePtr, maxSize, filePtr) == NULL) { *endOfFileFlag = 1; break; } while ((nextLinePtr[0] == ' ') || (nextLinePtr[0] == '\t') || (nextLinePtr[0] == '\n') || (nextLinePtr[0] == '\r')) { nextLinePtr++; } } while ((strncmp("#", nextLinePtr, 1) == 0) || (strlen(nextLinePtr) == 0)); } //****************************************************************************** void StillingerWeberImplementation::CloseParameterFiles( int const numberParameterFiles, FILE * const parameterFilePointers[MAX_PARAMETER_FILES]) { for (int i = 0; i < numberParameterFiles; ++i) { fclose(parameterFilePointers[i]); } } #undef KIM_LOGGER_OBJECT_NAME #define KIM_LOGGER_OBJECT_NAME modelWriteParameterizedModel //****************************************************************************** int StillingerWeberImplementation::WriteParameterizedModel( KIM::ModelWriteParameterizedModel const * const modelWriteParameterizedModel) const { std::string buffer; std::string const * path; std::string const * modelName; modelWriteParameterizedModel->GetPath(&path); modelWriteParameterizedModel->GetModelName(&modelName); buffer = *modelName + ".params"; modelWriteParameterizedModel->SetParameterFileName(buffer); buffer = *path + "/" + *modelName + ".params"; std::ofstream fp(buffer.c_str()); if (!fp.is_open()) { LOG_ERROR("Unable to open parameter file for writing."); return true; } // number of species fp << numberModelSpecies_ << std::endl; // params int const N = numberModelSpecies_; for (int i = 0; i < N; i++) { for (int j = i; j < N; j++) { int indx = i * N + j - (i * i + i) / 2; fp << modelSpeciesStringList_[i] << " " << modelSpeciesStringList_[j] << " " << std::scientific << std::setprecision(16) << A_[indx] << " " << B_[indx] << " " << p_[indx] << " " << q_[indx] << " " << sigma_[indx] << " " << lambda_[indx] << " " << gamma_[indx] << " " << costheta0_[indx] << " " << cutoff_[indx] << std::endl; } } fp << "\n\n#\n" << "# First line: number of species\n" << "#\n" << "# Then each data line lists two species and 9 parameters for the " "interaction\n" << "# between the two species:\n" << "#\n" << "# species1 species2 A B p q sigma lambda gamma costheta_0 cutoff\n" << "#\n" << "# species1 and species are valid KIM API particle species string\n" << "# A and lambda in [eV]\n" << "# sigma, gamma, and cutoff in [Angstrom]\n" << "# others are unitless\n" << "#\n"; fp.close(); return false; } //****************************************************************************** #undef KIM_LOGGER_OBJECT_NAME #define KIM_LOGGER_OBJECT_NAME modelDriverCreate int StillingerWeberImplementation::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) { int ier; // define default base units KIM::LengthUnit fromLength = KIM::LENGTH_UNIT::A; KIM::EnergyUnit fromEnergy = KIM::ENERGY_UNIT::eV; KIM::ChargeUnit fromCharge = KIM::CHARGE_UNIT::e; KIM::TemperatureUnit fromTemperature = KIM::TEMPERATURE_UNIT::K; KIM::TimeUnit fromTime = KIM::TIME_UNIT::ps; // changing units of sigma, gamma, and cutoff double convertLength = ONE; 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 length unit"); return ier; } // convert to active units if (convertLength != ONE) { for (int i = 0; i < numberUniqueSpeciesPairs_; ++i) { sigma_[i] *= convertLength; gamma_[i] *= convertLength; cutoff_[i] *= convertLength; } } // changing units of A and lambda 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) { for (int i = 0; i < numberUniqueSpeciesPairs_; ++i) { A_[i] *= convertEnergy; lambda_[i] *= convertEnergy; } } // register units ier = modelDriverCreate->SetUnits(requestedLengthUnit, requestedEnergyUnit, KIM::CHARGE_UNIT::unused, KIM::TEMPERATURE_UNIT::unused, KIM::TIME_UNIT::unused); if (ier) { LOG_ERROR("Unable to set units to requested values"); return ier; } // everything is good ier = false; return ier; } //****************************************************************************** int StillingerWeberImplementation::RegisterKIMModelSettings( KIM::ModelDriverCreate * const modelDriverCreate) const { // register numbering int ier = modelDriverCreate->SetModelNumbering(KIM::NUMBERING::zeroBased); return ier; } //****************************************************************************** #undef KIM_LOGGER_OBJECT_NAME #define KIM_LOGGER_OBJECT_NAME modelComputeArgumentsCreate int StillingerWeberImplementation::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; } //****************************************************************************** // helper macro #define SNUM(x) \ static_cast(std::ostringstream() << std::dec << x).str() #undef KIM_LOGGER_OBJECT_NAME #define KIM_LOGGER_OBJECT_NAME modelDriverCreate int StillingerWeberImplementation::RegisterKIMParameters( KIM::ModelDriverCreate * const modelDriverCreate) { int ier = false; // publish parameters (order is important) ier = modelDriverCreate->SetParameterPointer( numberUniqueSpeciesPairs_, A_, "A", "Multiplicative factors on the two-body energy function as a whole " "for each binary species combination. In terms of the original SW " "parameters, each quantity is equal to A*epsilon for the " "corresponding species combination. " "This array corresponds to a lower-triangular matrix (of " "size N=" + SNUM(numberUniqueSpeciesPairs_) + ") in row-major storage. " "Ordering is according to SpeciesCode values. For example, " "to find " "the parameter related to SpeciesCode 'i' and SpeciesCode " "'j' " "(i >= j), use (zero-based) index = (j*N + i - (j*j + j)/2).") || modelDriverCreate->SetParameterPointer( numberUniqueSpeciesPairs_, B_, "B", "Multiplicative factors on the repulsive term in the two-body " "energy " "function for each binary species combination. " "This array corresponds to a lower-triangular matrix (of " "size N=" + SNUM(numberUniqueSpeciesPairs_) + ") in row-major storage. " "Ordering is according to SpeciesCode values. For " "example, to find " "the parameter related to SpeciesCode 'i' and SpeciesCode " "'j' " "(i >= j), use (zero-based) index = (j*N + i - (j*j + " "j)/2).") || modelDriverCreate->SetParameterPointer( numberUniqueSpeciesPairs_, p_, "p", "The exponent of the repulsive term in the two-body energy " "function " "is equal to the negative of this parameter. " "This array corresponds to a lower-triangular matrix (of " "size N=" + SNUM(numberUniqueSpeciesPairs_) + ") in row-major storage. " "Ordering is according to SpeciesCode values. For " "example, to find " "the parameter related to SpeciesCode 'i' and SpeciesCode " "'j' " "(i >= j), use (zero-based) index = (j*N + i - (j*j + " "j)/2).") || modelDriverCreate->SetParameterPointer( numberUniqueSpeciesPairs_, q_, "q", "The exponent of the attractive term in the two-body energy " "function " "is equal to the negative of this parameter. " "This array corresponds to a lower-triangular matrix (of " "size N=" + SNUM(numberUniqueSpeciesPairs_) + ") in row-major storage. " "Ordering is according to SpeciesCode values. For " "example, to find " "the parameter related to SpeciesCode 'i' and SpeciesCode " "'j' " "(i >= j), use (zero-based) index = (j*N + i - (j*j + " "j)/2).") || modelDriverCreate->SetParameterPointer( numberUniqueSpeciesPairs_, sigma_, "sigma", "Length normalization factors used in the two-body energy " "function " "for each binary species combination. " "This array corresponds to a lower-triangular matrix (of " "size N=" + SNUM(numberUniqueSpeciesPairs_) + ") in row-major storage. " "Ordering is according to SpeciesCode values. For " "example, to find " "the parameter related to SpeciesCode 'i' and SpeciesCode " "'j' " "(i >= j), use (zero-based) index = (j*N + i - (j*j + " "j)/2).") || modelDriverCreate->SetParameterPointer( numberUniqueSpeciesPairs_, gamma_, "gamma", "Length normalization factors used in the three-body energy " "function " "for each binary species combination. " "In terms of the original SW parameters, each quantity is equal " "to " "gamma*sigma for the corresponding species combination. " "This array corresponds to a lower-triangular matrix (of " "size N=" + SNUM(numberUniqueSpeciesPairs_) + ") in row-major storage. " "Ordering is according to SpeciesCode values. For " "example, to find " "the parameter related to SpeciesCode 'i' and SpeciesCode " "'j' " "(i >= j), use (zero-based) index = (j*N + i - (j*j + " "j)/2).") || modelDriverCreate->SetParameterPointer( numberUniqueSpeciesPairs_, cutoff_, "cutoff", "Distances used to determine whether two-body interactions for " "a pair " "of atoms occur, as well as to determine whether three-body " "interactions for a triplet of atoms occur." "This array corresponds to a lower-triangular matrix (of " "size N=" + SNUM(numberUniqueSpeciesPairs_) + ") in row-major storage. " "Ordering is according to SpeciesCode values. For " "example, to find " "the parameter related to SpeciesCode 'i' and SpeciesCode " "'j' " "(i >= j), use (zero-based) index = (j*N + i - (j*j + " "j)/2).") || modelDriverCreate->SetParameterPointer( numberUniqueSpeciesPairs_, lambda_, "lambda", "Multiplicative factors on the three-body energy function as a " "whole " "for each binary species combination. In terms of the original " "SW " "parameters, each quantity is equal to lambda*epsilon for the " "corresponding species combination. " "For a vertex atom i with neighbors j and k, the value " "ultimately used " "for the three-body interactions of bonds ij and ik is given by " "lambda_ijk = sqrt(lambda_ij*lambda_ik). " "This array corresponds to a lower-triangular matrix (of " "size N=" + SNUM(numberUniqueSpeciesPairs_) + ") in row-major storage. " "Ordering is according to SpeciesCode values. For " "example, to find " "the parameter related to SpeciesCode 'i' and SpeciesCode " "'j' " "(i >= j), use (zero-based) index = (j*N + i - (j*j + " "j)/2).") || modelDriverCreate->SetParameterPointer( numberUniqueSpeciesPairs_, costheta0_, "costheta0", "Cosine of the energetically preferable angle between bonds " "which share " "a common vertex atom. Formally, this is an array which " "corresponds " "to a lower-triangular matrix (of size N=" + SNUM(numberUniqueSpeciesPairs_) + ") in row-major storage. " "Ordering is according to SpeciesCode values. For " "example, to find " "the parameter related to SpeciesCode 'i' and SpeciesCode " "'j' " "(i >= j), use (zero-based) index = (j*N + i - (j*j + " "j)/2). However, " "the values are still expected to be the same across " "different species " "combinations."); if (ier) { LOG_ERROR("set_parameters"); return ier; } // everything is good ier = false; return ier; } //****************************************************************************** int StillingerWeberImplementation::RegisterKIMFunctions( KIM::ModelDriverCreate * const modelDriverCreate) const { int ier; // register functions ier = modelDriverCreate->SetRoutinePointer( KIM::MODEL_ROUTINE_NAME::Destroy, KIM::LANGUAGE_NAME::cpp, true, reinterpret_cast(StillingerWeber::Destroy)) || modelDriverCreate->SetRoutinePointer( KIM::MODEL_ROUTINE_NAME::Refresh, KIM::LANGUAGE_NAME::cpp, true, reinterpret_cast(StillingerWeber::Refresh)) || modelDriverCreate->SetRoutinePointer( KIM::MODEL_ROUTINE_NAME::Compute, KIM::LANGUAGE_NAME::cpp, true, reinterpret_cast(StillingerWeber::Compute)) || modelDriverCreate->SetRoutinePointer( KIM::MODEL_ROUTINE_NAME::ComputeArgumentsCreate, KIM::LANGUAGE_NAME::cpp, true, reinterpret_cast( StillingerWeber::ComputeArgumentsCreate)) || modelDriverCreate->SetRoutinePointer( KIM::MODEL_ROUTINE_NAME::ComputeArgumentsDestroy, KIM::LANGUAGE_NAME::cpp, true, reinterpret_cast( StillingerWeber::ComputeArgumentsDestroy)) || modelDriverCreate->SetRoutinePointer( KIM::MODEL_ROUTINE_NAME::WriteParameterizedModel, KIM::LANGUAGE_NAME::cpp, false, reinterpret_cast( StillingerWeber::WriteParameterizedModel)); return ier; } //****************************************************************************** template int StillingerWeberImplementation::SetRefreshMutableValues( ModelObj * const modelObj) { // use (possibly) new values of parameters to compute other quantities // NOTE: This function is templated because it's called with both a // modelDriverCreate object during initialization and with a // modelRefresh object when the Model's parameters have been altered int ier; // update parameters for (int i = 0; i < numberModelSpecies_; ++i) { for (int j = 0; j <= i; ++j) { int const index = j * numberModelSpecies_ + i - (j * j + j) / 2; A_2D_[i][j] = A_2D_[j][i] = A_[index]; B_2D_[i][j] = B_2D_[j][i] = B_[index]; p_2D_[i][j] = p_2D_[j][i] = p_[index]; q_2D_[i][j] = q_2D_[j][i] = q_[index]; sigma_2D_[i][j] = sigma_2D_[j][i] = sigma_[index]; lambda_2D_[i][j] = lambda_2D_[j][i] = lambda_[index]; gamma_2D_[i][j] = gamma_2D_[j][i] = gamma_[index]; costheta0_2D_[i][j] = costheta0_2D_[j][i] = costheta0_[index]; cutoffSq_2D_[i][j] = cutoffSq_2D_[j][i] = cutoff_[index] * cutoff_[index]; } } // update cutoff value in KIM API object influenceDistance_ = 0.0; for (int i = 0; i < numberModelSpecies_; i++) { int indexI = modelSpeciesCodeList_[i]; for (int j = 0; j < numberModelSpecies_; j++) { int indexJ = modelSpeciesCodeList_[j]; if (influenceDistance_ < cutoffSq_2D_[indexI][indexJ]) { influenceDistance_ = cutoffSq_2D_[indexI][indexJ]; } } } influenceDistance_ = std::sqrt(influenceDistance_); modelObj->SetInfluenceDistancePointer(&influenceDistance_); modelObj->SetNeighborListPointers( 1, &influenceDistance_, &modelWillNotRequestNeighborsOfNoncontributingParticles_); // everything is good ier = false; return ier; } //****************************************************************************** #undef KIM_LOGGER_OBJECT_NAME #define KIM_LOGGER_OBJECT_NAME modelComputeArguments int StillingerWeberImplementation::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) { int ier = true; // 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; 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 ** const) & coordinates) || modelComputeArguments->GetArgumentPointer( KIM::COMPUTE_ARGUMENT_NAME::partialEnergy, &energy) || modelComputeArguments->GetArgumentPointer( KIM::COMPUTE_ARGUMENT_NAME::partialForces, (double const ** const) & forces) || modelComputeArguments->GetArgumentPointer( KIM::COMPUTE_ARGUMENT_NAME::partialParticleEnergy, &particleEnergy) || modelComputeArguments->GetArgumentPointer( KIM::COMPUTE_ARGUMENT_NAME::partialVirial, (double const ** const) & virial) || modelComputeArguments->GetArgumentPointer( KIM::COMPUTE_ARGUMENT_NAME::partialParticleVirial, (double const ** const) & particleVirial); if (ier) { LOG_ERROR("GetArgumentPointer"); return ier; } isComputeEnergy = (energy != NULL); isComputeForces = (forces != NULL); isComputeParticleEnergy = (particleEnergy != NULL); isComputeVirial = (virial != NULL); isComputeParticleVirial = (particleVirial != NULL); // update values cachedNumberOfParticles_ = *numberOfParticles; // everything is good ier = false; return ier; } //****************************************************************************** // Assume that the particle species interge code starts from 0 #undef KIM_LOGGER_OBJECT_NAME #define KIM_LOGGER_OBJECT_NAME modelCompute int StillingerWeberImplementation::CheckParticleSpeciesCodes( KIM::ModelCompute const * const modelCompute, int const * const particleSpeciesCodes) const { for (int i = 0; i < cachedNumberOfParticles_; ++i) { if ((particleSpeciesCodes[i] < 0) || (particleSpeciesCodes[i] >= numberModelSpecies_)) { LOG_ERROR("unsupported particle species codes detected"); return true; } } // everything is good int ier = false; return ier; } //****************************************************************************** int StillingerWeberImplementation::GetComputeIndex( const bool & isComputeProcess_dEdr, const bool & isComputeProcess_d2Edr2, const bool & isComputeEnergy, const bool & isComputeForces, const bool & isComputeParticleEnergy, const bool & isComputeVirial, const bool & isComputeParticleVirial) const { // const int processdE = 2; const int processd2E = 2; const int energy = 2; const int force = 2; const int particleEnergy = 2; const int virial = 2; const int particleVirial = 2; int index = 0; // processdE index += (int(isComputeProcess_dEdr)) * processd2E * energy * force * particleEnergy * virial * particleVirial; // processd2E index += (int(isComputeProcess_d2Edr2)) * energy * force * particleEnergy * virial * particleVirial; // energy index += (int(isComputeEnergy)) * force * particleEnergy * virial * particleVirial; // force index += (int(isComputeForces)) * particleEnergy * virial * particleVirial; // particleEnergy index += (int(isComputeParticleEnergy)) * virial * particleVirial; // virial index += (int(isComputeVirial)) * particleVirial; // particleVirial index += (int(isComputeParticleVirial)); return index; } //============================================================================== // // Stillinger-Weber functions // //============================================================================== void StillingerWeberImplementation::CalcPhiTwo(int const ispec, int const jspec, double const r_sq, double const r, double & phi) const { double const cutoff_sq = cutoffSq_2D_[ispec][jspec]; if (r_sq >= cutoff_sq) { phi = 0.0; return; } // get parameters double const A = A_2D_[ispec][jspec]; double const B = B_2D_[ispec][jspec]; double const p = p_2D_[ispec][jspec]; double const q = q_2D_[ispec][jspec]; double const sigma = sigma_2D_[ispec][jspec]; double const cutoff = std::sqrt(cutoff_sq); double const r_capinv = sigma / r; double const rp = pow(r_capinv, p); double const rq = pow(r_capinv, q); double const expsrinv = std::exp(sigma / (r - cutoff)); phi = A * (B * rp - rq) * expsrinv; } void StillingerWeberImplementation::CalcPhiDphiTwo(int const ispec, int const jspec, double const r_sq, double const r, double & phi, double & dphi) const { double const cutoff_sq = cutoffSq_2D_[ispec][jspec]; if (r_sq >= cutoff_sq) { phi = 0.0; dphi = 0.0; return; } // get parameters double const A = A_2D_[ispec][jspec]; double const B = B_2D_[ispec][jspec]; double const p = p_2D_[ispec][jspec]; double const q = q_2D_[ispec][jspec]; double const sigma = sigma_2D_[ispec][jspec]; double const cutoff = std::sqrt(cutoff_sq); double const r_capinv = sigma / r; double const rp = pow(r_capinv, p); double const rp1 = rp * r_capinv; double const rq = pow(r_capinv, q); double const rq1 = rq * r_capinv; double const srinv = sigma / (r - cutoff); double const expsrinv = std::exp(srinv); phi = A * (B * rp - rq) * expsrinv; dphi = q * rq1 - p * B * rp1 - (B * rp - rq) * srinv * srinv; dphi *= A * expsrinv / sigma; } void StillingerWeberImplementation::CalcPhiD2phiTwo(int const ispec, int const jspec, double const r_sq, double const r, double & phi, double & dphi, double & d2phi) const { double const cutoff_sq = cutoffSq_2D_[ispec][jspec]; if (r_sq >= cutoff_sq) { phi = 0.0; dphi = 0.0; d2phi = 0.0; return; } // get parameters double const A = A_2D_[ispec][jspec]; double const B = B_2D_[ispec][jspec]; double const p = p_2D_[ispec][jspec]; double const q = q_2D_[ispec][jspec]; double const sigma = sigma_2D_[ispec][jspec]; double const cutoff = std::sqrt(cutoff_sq); double const r_capinv = sigma / r; double const rp = pow(r_capinv, p); double const rp1 = rp * r_capinv; double const rp2 = rp1 * r_capinv; double const rq = pow(r_capinv, q); double const rq1 = rq * r_capinv; double const rq2 = rq1 * r_capinv; double const srinv = sigma / (r - cutoff); double const srinv2 = srinv * srinv; double const srinv3 = srinv2 * srinv; double const expsrinv = std::exp(srinv); phi = A * (B * rp - rq) * expsrinv; dphi = (q * rq1 - p * B * rp1) - (B * rp - rq) * srinv2; dphi *= A * expsrinv / sigma; d2phi = (B * rp - rq) * srinv3 * (srinv + 2) + 2 * (p * B * rp1 - q * rq1) * srinv2 + (p * (p + 1) * B * rp2 - q * (q + 1) * rq2); d2phi *= A * expsrinv / (sigma * sigma); } void StillingerWeberImplementation::CalcPhiThree(int const ispec, int const jspec, int const kspec, double const rij_sq, double const rij, double const rik_sq, double const rik, double const rjk_sq, double const /* rjk */, double & phi) const { double const cutoff_ij_sq = cutoffSq_2D_[ispec][jspec]; double const cutoff_ik_sq = cutoffSq_2D_[ispec][kspec]; if (!(rij_sq < cutoff_ij_sq && rik_sq < cutoff_ik_sq)) { phi = 0.0; return; } // get parameters double const lambda_ij = lambda_2D_[ispec][jspec]; double const lambda_ik = lambda_2D_[ispec][kspec]; double const gamma_ij = gamma_2D_[ispec][jspec]; double const gamma_ik = gamma_2D_[ispec][kspec]; double const cutoff_ij = std::sqrt(cutoff_ij_sq); double const cutoff_ik = std::sqrt(cutoff_ik_sq); double const grinv_ij = gamma_ij / (rij - cutoff_ij); double const grinv_ik = gamma_ik / (rik - cutoff_ik); // mix parameters double const lambda = std::sqrt(std::fabs(lambda_ij) * std::fabs(lambda_ik)); double const costheta0 = costheta0_2D_[ispec][jspec]; double const costhetajik = (rij_sq + rik_sq - rjk_sq) / (2 * rij * rik); double const diff_costhetajik = costhetajik - costheta0; double const exp_ij_ik = std::exp(grinv_ij + grinv_ik); phi = lambda * exp_ij_ik * diff_costhetajik * diff_costhetajik; } void StillingerWeberImplementation::CalcPhiDphiThree(int const ispec, int const jspec, int const kspec, double const rij_sq, double const rij, double const rik_sq, double const rik, double const rjk_sq, double const rjk, double & phi, double * const dphi) const { double const cutoff_ij_sq = cutoffSq_2D_[ispec][jspec]; double const cutoff_ik_sq = cutoffSq_2D_[ispec][kspec]; if (!(rij_sq < cutoff_ij_sq && rik_sq < cutoff_ik_sq)) { phi = 0.0; dphi[0] = dphi[1] = dphi[2] = 0.0; return; } // get parameters double const lambda_ij = lambda_2D_[ispec][jspec]; double const lambda_ik = lambda_2D_[ispec][kspec]; double const gamma_ij = gamma_2D_[ispec][jspec]; double const gamma_ik = gamma_2D_[ispec][kspec]; // mix parameters double const lambda = std::sqrt(std::fabs(lambda_ij) * std::fabs(lambda_ik)); double const costheta0 = costheta0_2D_[ispec][jspec]; // do not mix double const cutoff_ij = std::sqrt(cutoff_ij_sq); double const cutoff_ik = std::sqrt(cutoff_ik_sq); double const r_cut_ij = rij - cutoff_ij; double const r_cut_ik = rik - cutoff_ik; double const grinv_ij = gamma_ij / r_cut_ij; double const grinv_ik = gamma_ik / r_cut_ik; double const costhetajik = (rij_sq + rik_sq - rjk_sq) / (2 * rij * rik); double const diff_costhetajik = costhetajik - costheta0; /* Derivatives of cosines w.r.t rij, rik, rjk */ double const costhetajik_ij = (rij_sq - rik_sq + rjk_sq) / (2 * rij_sq * rik); double const costhetajik_ik = (rik_sq - rij_sq + rjk_sq) / (2 * rij * rik_sq); double const costhetajik_jk = -rjk / (rij * rik); /* Variables for simplifying terms */ double const exp_ij_ik = std::exp(grinv_ij + grinv_ik); double const d_ij = -grinv_ij / r_cut_ij; double const d_ik = -grinv_ik / r_cut_ik; phi = lambda * exp_ij_ik * diff_costhetajik * diff_costhetajik; dphi[0] = lambda * diff_costhetajik * exp_ij_ik * (d_ij * diff_costhetajik + 2 * costhetajik_ij); dphi[1] = lambda * diff_costhetajik * exp_ij_ik * (d_ik * diff_costhetajik + 2 * costhetajik_ik); dphi[2] = lambda * diff_costhetajik * exp_ij_ik * 2 * costhetajik_jk; } // Calculate phi_three(rij, rik, rjk) and its 1st & 2nd derivatives // dphi_three(rij, rik, rjk), d2phi_three(rij, rik, rjk) // // dphi has three components as derivatives of phi w.r.t. rij, rik, rjk // // d2phi as symmetric Hessian matrix of phi has six components: // [0]=(ij,ij), [3]=(ij,ik), [4]=(ij,jk) // [1]=(ik,ik), [5]=(ik,jk) // [2]=(jk,jk) void StillingerWeberImplementation::CalcPhiD2phiThree( int const ispec, int const jspec, int const kspec, double const rij_sq, double const rij, double const rik_sq, double const rik, double const rjk_sq, double const rjk, double & phi, double * const dphi, double * const d2phi) const { double const cutoff_ij_sq = cutoffSq_2D_[ispec][jspec]; double const cutoff_ik_sq = cutoffSq_2D_[ispec][kspec]; if (!(rij_sq < cutoff_ij_sq && rik_sq < cutoff_ik_sq)) { phi = 0.0; dphi[0] = dphi[1] = dphi[2] = 0.0; d2phi[0] = d2phi[1] = d2phi[2] = d2phi[3] = d2phi[4] = d2phi[5] = 0.0; return; } // get parameters double const lambda_ij = lambda_2D_[ispec][jspec]; double const lambda_ik = lambda_2D_[ispec][kspec]; double const gamma_ij = gamma_2D_[ispec][jspec]; double const gamma_ik = gamma_2D_[ispec][kspec]; // mix parameters double const lambda = std::sqrt(std::fabs(lambda_ij) * std::fabs(lambda_ik)); double const costheta0 = costheta0_2D_[ispec][jspec]; // do not mix double const cutoff_ij = std::sqrt(cutoff_ij_sq); double const cutoff_ik = std::sqrt(cutoff_ik_sq); double const r_cut_ij = rij - cutoff_ij; double const r_cut_ik = rik - cutoff_ik; double const grinv_ij = gamma_ij / r_cut_ij; double const grinv_ik = gamma_ik / r_cut_ik; double const costhetajik = (rij_sq + rik_sq - rjk_sq) / (2 * rij * rik); double const diff_costhetajik = costhetajik - costheta0; double const diff_costhetajik_2 = diff_costhetajik * diff_costhetajik; /* Derivatives of cosines w.r.t rij, rik, rjk */ double const costhetajik_ij = (rij_sq - rik_sq + rjk_sq) / (2 * rij_sq * rik); double const costhetajik_ik = (rik_sq - rij_sq + rjk_sq) / (2 * rij * rik_sq); double const costhetajik_jk = -rjk / (rij * rik); /* Hessian matrix of cosine */ double const costhetajik_ij_ij = (rik_sq - rjk_sq) / (rij_sq * rij * rik); double const costhetajik_ik_ik = (rij_sq - rjk_sq) / (rij * rik_sq * rik); double const costhetajik_jk_jk = -1.0 / (rij * rik); double const costhetajik_ij_ik = -(rij_sq + rik_sq + rjk_sq) / (2 * rij_sq * rik_sq); double const costhetajik_ij_jk = rjk / (rij_sq * rik); double const costhetajik_ik_jk = rjk / (rij * rik_sq); /* Variables for simplifying terms */ double const exp_ij_ik = std::exp(grinv_ij + grinv_ik); double const d_ij = -grinv_ij / r_cut_ij; double const d_ik = -grinv_ik / r_cut_ik; double const d_ij_2 = d_ij * d_ij; double const d_ik_2 = d_ik * d_ik; double const dd_ij = 2 * grinv_ij / (r_cut_ij * r_cut_ij); double const dd_ik = 2 * grinv_ik / (r_cut_ik * r_cut_ik); phi = lambda * exp_ij_ik * diff_costhetajik * diff_costhetajik; dphi[0] = lambda * diff_costhetajik * exp_ij_ik * (d_ij * diff_costhetajik + 2 * costhetajik_ij); dphi[1] = lambda * diff_costhetajik * exp_ij_ik * (d_ik * diff_costhetajik + 2 * costhetajik_ik); dphi[2] = lambda * diff_costhetajik * exp_ij_ik * 2 * costhetajik_jk; d2phi[0] = lambda * exp_ij_ik * ( (d_ij_2 + dd_ij) * diff_costhetajik_2 + (4 * d_ij * costhetajik_ij + 2 * costhetajik_ij_ij) * diff_costhetajik + 2 * costhetajik_ij * costhetajik_ij); d2phi[1] = lambda * exp_ij_ik * ( (d_ik_2 + dd_ik) * diff_costhetajik_2 + (4 * d_ik * costhetajik_ik + 2 * costhetajik_ik_ik) * diff_costhetajik + 2 * costhetajik_ik * costhetajik_ik); d2phi[2] = lambda * 2 * exp_ij_ik * ( costhetajik_jk_jk * diff_costhetajik + costhetajik_jk * costhetajik_jk); d2phi[3] = lambda * exp_ij_ik * ( d_ij * d_ik * diff_costhetajik_2 + ( d_ij * costhetajik_ik + d_ik * costhetajik_ij + costhetajik_ij_ik ) * 2 * diff_costhetajik + 2 * costhetajik_ij * costhetajik_ik); d2phi[4] = lambda * exp_ij_ik * ( (d_ij * costhetajik_jk + costhetajik_ij_jk) * 2 * diff_costhetajik + 2 * costhetajik_ij * costhetajik_jk); d2phi[5] = lambda * exp_ij_ik * ( (d_ik * costhetajik_jk + costhetajik_ik_jk) * 2 * diff_costhetajik + 2 * costhetajik_ik * costhetajik_jk); } #undef MAXLINE #undef MAX_PARAMETER_FILES #undef ONE