// // meam_implementation.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) 2020--2021, Regents of the University of Minnesota. // All rights reserved. // // Contributors: // Yaser Afshar // #include "meam_implementation.hpp" #include #include #include #include #include #include #include #include #include #include #include #include #include "KIM_LogMacros.hpp" #include "KIM_ModelDriverHeaders.hpp" #include "helper.hpp" #include "meam.hpp" #include "meam_c.hpp" #include "meam_spline.hpp" #include "meam_sw_spline.hpp" #include "special.hpp" #include "two_body.hpp" #include "utility.hpp" namespace { /*! * \brief The various parameters in the MEAM formulas are listed in three files. * * 1 - MEAM element names between library & parameter file * 2 - MEAM library file in case of `meam/c` or a potential file in case of * `meam/spline` or `meam/sw/spline` potentials. * 3 - MEAM parameter file in case of `meam/c` potential. * */ static constexpr int kMaxNumberParameterFiles = 3; static constexpr int kForce = 2; static constexpr int kParticleEnergy = 2; static constexpr int kVirial = 2; static constexpr int kParticleVirial = 2; static constexpr int kKnotsOnRegularGrid = 2; static constexpr int kMaxLineSize = 1024; // 2**10 static constexpr double kOne = 1.0; // 1 static constexpr double kThird = 0.33333333333333333333; // 1/3 static constexpr double kTwoThird = 0.66666666666666666666; // 2/3 /*! Voight notation index maps for 2D. x-y-z to Voigt-like index */ static constexpr int const kVoight2dIndex[9] = {0, 1, 2, 1, 3, 4, 2, 4, 5}; /*! Voight notation index maps for 3D. x-y-z to Voigt-like index */ static constexpr int const kVoight3dIndex[27] = {0, 1, 2, 1, 3, 4, 2, 4, 5, 1, 3, 4, 3, 6, 7, 4, 7, 8, 2, 4, 5, 4, 7, 8, 5, 8, 9}; /*! * \brief Array of factors to apply for Voight notation. * multiplicity of Voigt index (i.e. [1] -> xy+yx = 2) */ static constexpr int const kVoight2dFactor[6] = {1, 2, 2, 1, 2, 1}; /*! * \brief Array of factors to apply for Voight notation. * (multiplicity of Voigt index) */ static constexpr int const kVoight3dFactor[10] = {1, 3, 3, 3, 6, 3, 1, 3, 3, 1}; } // namespace // public member functions #ifdef KIM_LOGGER_OBJECT_NAME #undef KIM_LOGGER_OBJECT_NAME #endif #define KIM_LOGGER_OBJECT_NAME model_driver_create MEAMImplementation::MEAMImplementation( KIM::ModelDriverCreate *const model_driver_create, KIM::LengthUnit const requested_length_unit, KIM::EnergyUnit const requested_energy_unit, KIM::ChargeUnit const requested_charge_unit, KIM::TemperatureUnit const requested_temperature_unit, KIM::TimeUnit const requested_time_unit, int *const ier) : meam_c_(nullptr), meam_spline_(nullptr), meam_sw_spline_(nullptr) { // Everything is good *ier = false; if (!model_driver_create) { HELPER_LOG_ERROR("The model_driver_create pointer is not assigned.\n"); *ier = true; return; } { std::FILE *parameter_file_pointers[kMaxNumberParameterFiles]; int number_parameter_files(0); // Getting the number of parameter files, open the files and process them model_driver_create->GetNumberOfParameterFiles(&number_parameter_files); if (number_parameter_files > kMaxNumberParameterFiles) { LOG_ERROR("Too many input parameter files!\n"); *ier = true; return; } if (!number_parameter_files) { LOG_ERROR("There is no parameter file!\n"); *ier = true; return; } *ier = OpenParameterFiles(model_driver_create, number_parameter_files, parameter_file_pointers); if (*ier) { return; } *ier = ProcessParameterFiles(model_driver_create, number_parameter_files, parameter_file_pointers); CloseParameterFiles(number_parameter_files, parameter_file_pointers); if (*ier) { return; } } *ier = ConvertUnits(model_driver_create, requested_length_unit, requested_energy_unit, requested_charge_unit, requested_temperature_unit, requested_time_unit); if (*ier) { return; } *ier = SetRefreshMutableValues(model_driver_create); if (*ier) { return; } *ier = RegisterKIMModelSettings(model_driver_create); if (*ier) { return; } *ier = RegisterKIMParameters(model_driver_create); if (*ier) { return; } *ier = RegisterKIMFunctions(model_driver_create); if (*ier) { return; } } #undef KIM_LOGGER_OBJECT_NAME // Private member functions #define KIM_LOGGER_OBJECT_NAME model_driver_create int MEAMImplementation::OpenParameterFiles( KIM::ModelDriverCreate *const model_driver_create, int const number_parameter_files, std::FILE **parameter_file_pointers) { std::string const *parameter_file_directory_name; model_driver_create->GetParameterFileDirectoryName( ¶meter_file_directory_name); for (int i = 0; i < number_parameter_files; ++i) { std::string const *parameter_file_basename; if (model_driver_create->GetParameterFileBasename( i, ¶meter_file_basename)) { LOG_ERROR("Unable to get the parameter file base name\n"); return true; } std::string const parameter_file_name = *parameter_file_directory_name + "/" + *parameter_file_basename; parameter_file_pointers[i] = std::fopen(parameter_file_name.c_str(), "r"); if (!parameter_file_pointers[i]) { std::string msg = "The parameter file (" + *parameter_file_basename; msg += ") can not be opened.\n"; HELPER_LOG_ERROR(msg); for (int j = i - 1; i <= 0; --i) { std::fclose(parameter_file_pointers[j]); } return true; } } // everything is good return false; } #undef KIM_LOGGER_OBJECT_NAME #define KIM_LOGGER_OBJECT_NAME model_driver_create int MEAMImplementation::ProcessParameterFiles( KIM::ModelDriverCreate *const model_driver_create, int const number_parameter_files, std::FILE *const *parameter_file_pointers) { char next_line[kMaxLineSize]; // Read the MEAM element file // create a utility object Utility ut; // read the first line, strip comment, skip line if blank if (ut.GetNextLine(parameter_file_pointers[0], next_line, kMaxLineSize)) { HELPER_LOG_ERROR("End of file while reading the MEAM element file.\n"); return true; } // MEAM element names // element_name_ = list of unique element names element_name_ = ut.GetWordsInLine(next_line); // number_of_elements_ = # of MEAM element_name_ number_of_elements_ = static_cast(element_name_.size()); if (number_of_elements_ < 1) { HELPER_LOG_ERROR("Incorrect MEAM element file.\nNo element is provided.\n"); return true; } // keep track of known species std::map species_map; for (int ielem = 0; ielem < number_of_elements_; ++ielem) { KIM::SpeciesName const species_name(element_name_[ielem]); if (species_name.ToString() == "unknown") { std::string msg = "Incorrect format in the MEAM element file.\n"; msg += "The species name '" + element_name_[ielem] + "' is unknown.\n"; HELPER_LOG_ERROR(msg); return true; } // check for new species auto iter = species_map.find(species_name); if (iter == species_map.end()) { int ier = model_driver_create->SetSpeciesCode(species_name, ielem); if (ier) { LOG_ERROR("SetSpeciesCode failed to set the new species.\n"); return ier; } species_map[species_name] = ielem; } else { std::string msg = "Incorrect format in the MEAM element file.\n"; msg += "The Species '" + element_name_[ielem]; msg += "' is already defined and exist.\n"; HELPER_LOG_ERROR(msg); return true; } } // Detect the MEAM type // Any of the `meam/c` or `meam/spline` or `meam/sw/spline` do { if (number_parameter_files == 3) { // only meam/c might have three parameter files is_meam_c_ = 1; break; } // Skip the first line. The first line is always a comment if (ut.GetFirstLine(parameter_file_pointers[1], next_line, kMaxLineSize)) { std::string msg = "End of file while reading the first line "; msg += "from the input library/potential file.\n"; HELPER_LOG_ERROR(msg); return true; } if (ut.GetNextLine(parameter_file_pointers[1], next_line, kMaxLineSize)) { std::string msg = "End of file while reading a line "; msg += "from the input library/potential file.\n"; HELPER_LOG_ERROR(msg); return true; } char *word = std::strtok(next_line, " \t\n\r\f"); if (std::strcmp(word, "meam/spline") == 0) { // the first word in the meam/spline new format // parameter file is the `meam/spline` is_meam_spline_ = 1; break; } // if it is not a meam/spline new format file, it can be: // // - an old format meam/spline : first word is the number of knots (int) // - a meam/sw/spline : first word is the number of knots (int) // - a meam/c : first word is the species name (string) int number_knots = std::atoi(word); // if no conversion can be performed, ​0​ is returned. if (number_knots == 0) { is_meam_c_ = 1; break; } // it is not a meam/c, it can be: // - an old format meam/spline file // - a meam/sw/spline file // both files contain only a single species if (number_of_elements_ > 1) { std::string msg = "Unable to get the input file format.\n"; msg += "At this point, input should be an old format "; msg += "`meam/spline` file, or a `meam/sw/spline` file. "; msg += "Either one supports only a single species.\nThere "; msg += "are more species defined in the element file.\n"; HELPER_LOG_ERROR(msg); return true; } std::rewind(parameter_file_pointers[1]); // an old format `meam/spline` file, contains 5 sets of data, // while the `meam/sw/spline` file has 7. if (ut.GetFirstLine(parameter_file_pointers[1], next_line, kMaxLineSize)) { std::string msg = "End of file while reading the first line "; msg += "from the input library/potential file.\n"; HELPER_LOG_ERROR(msg); return true; } int nset = 0; while (true) { if (ut.GetNextLine(parameter_file_pointers[1], next_line, kMaxLineSize)) { break; } word = std::strtok(next_line, " \t\n\r\f"); number_knots = std::atoi(word); if (number_knots < 2) { std::string msg = "Invalid number of spline knots (n = "; msg += std::to_string(number_knots); msg += ") in the MEAM potential file.\n"; HELPER_LOG_ERROR(msg); return true; } if (ut.GetNextLine(parameter_file_pointers[1], next_line, kMaxLineSize)) { std::string msg = "End of file while reading a line from "; msg += "a potential file to detect MEAM type.\n"; HELPER_LOG_ERROR(msg); return true; } if (ut.GetNextLine(parameter_file_pointers[1], next_line, kMaxLineSize)) { std::string msg = "End of file while reading a line from "; msg += "a potential file to detect MEAM type.\n"; HELPER_LOG_ERROR(msg); return true; } for (int i = 0; i < number_knots; ++i) { if (ut.GetNextLine(parameter_file_pointers[1], next_line, kMaxLineSize)) { std::string msg = "End of file while reading a line from "; msg += "a potential file to detect MEAM type.\n"; HELPER_LOG_ERROR(msg); return true; } } ++nset; } // error if the potential file is empty if (nset == 0) { std::string msg = "The potential file is empty.\n"; HELPER_LOG_ERROR(msg); return true; } else if (nset == 5) { is_meam_spline_ = 1; break; } else if (nset == 7) { is_meam_sw_spline_ = 1; break; } HELPER_LOG_ERROR("Unable to detect the MEAM type from the input files.\n"); return true; } while (false); // Read MEAM library/potential and/or parameter files. /*! \note In the current implementation all the input * file comments should be started with \c # */ // meam/c if (is_meam_c_) { meam_c_.reset(new MEAMC); // Read MEAM library file. @parameter_file_pointers[1] if (meam_c_->ProcessLibraryFile(parameter_file_pointers[1], kMaxLineSize, element_name_)) { HELPER_LOG_ERROR("Failed to read and parse the `meam/c` library file.\n"); return true; } // done if there is no user paramater file if (number_parameter_files == 2) { // everything is good return false; } // Read MEAM parameter file. @parameter_file_pointers[2] if (meam_c_->ProcessParameterFile(parameter_file_pointers[2], kMaxLineSize)) { std::string msg = "Failed to read and parse the "; msg += "`meam/c` parameter file.\n"; HELPER_LOG_ERROR(msg); return true; } } // meam/spline else if (is_meam_spline_) { meam_spline_.reset(new MEAMSpline); // Read meam/spline potential file. @parameter_file_pointers[1] if (meam_spline_->ProcessPotentialFile(parameter_file_pointers[1], kMaxLineSize, element_name_)) { std::string msg = "Failed to read and parse the "; msg += "`meam/spline` potential file.\n"; HELPER_LOG_ERROR(msg); return true; } } // meam/sw/spline else if (is_meam_sw_spline_) { meam_sw_spline_.reset(new MEAMSWSpline); // Read meam/spline potential file. @parameter_file_pointers[1] if (meam_sw_spline_->ProcessPotentialFile(parameter_file_pointers[1], kMaxLineSize)) { std::string msg = "Failed to read and parse the "; msg += "`meam/sw/spline` potential file.\n"; HELPER_LOG_ERROR(msg); return true; } } // everything is good return false; } #undef KIM_LOGGER_OBJECT_NAME void MEAMImplementation::CloseParameterFiles( int const number_parameter_files, std::FILE *const *parameter_file_pointers) { for (int i = 0; i < number_parameter_files; ++i) { std::fclose(parameter_file_pointers[i]); } } #define KIM_LOGGER_OBJECT_NAME model_driver_create int MEAMImplementation::ConvertUnits( KIM::ModelDriverCreate *const model_driver_create, KIM::LengthUnit const &requested_length_unit, KIM::EnergyUnit const &requested_energy_unit, KIM::ChargeUnit const &requested_charge_unit, KIM::TemperatureUnit const &requested_temperature_unit, KIM::TimeUnit const &requested_time_unit) { // 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 convert_length = kOne; int ier = model_driver_create->ConvertUnit( fromLength, fromEnergy, fromCharge, fromTemperature, fromTime, requested_length_unit, requested_energy_unit, requested_charge_unit, requested_temperature_unit, requested_time_unit, kOne, 0.0, 0.0, 0.0, 0.0, &convert_length); if (ier) { LOG_ERROR("Unable to convert length unit"); return ier; } // Changing units of energy double convert_energy = kOne; ier = model_driver_create->ConvertUnit( fromLength, fromEnergy, fromCharge, fromTemperature, fromTime, requested_length_unit, requested_energy_unit, requested_charge_unit, requested_temperature_unit, requested_time_unit, 0.0, kOne, 0.0, 0.0, 0.0, &convert_energy); if (ier) { LOG_ERROR("Unable to convert energy unit"); return ier; } // Convert to active units if (special::IsNotOne(convert_length) || special::IsNotOne(convert_energy)) { if (is_meam_c_) { meam_c_->ConvertUnit(convert_length, convert_energy); } else if (is_meam_spline_) { meam_spline_->ConvertUnit(convert_length, convert_energy); } else if (is_meam_sw_spline_) { meam_sw_spline_->ConvertUnit(convert_length, convert_energy); } } // Register units ier = model_driver_create->SetUnits( requested_length_unit, requested_energy_unit, KIM::CHARGE_UNIT::unused, KIM::TEMPERATURE_UNIT::unused, KIM::TIME_UNIT::unused); if (ier) { LOG_ERROR("Unable to set units to the requested values"); return ier; } // Everything is good return false; } #undef KIM_LOGGER_OBJECT_NAME template int MEAMImplementation::SetRefreshMutableValues(ModelObj *const model_obj) { if (is_meam_c_) { meam_c_->CompleteSetup(&max_cutoff_); } else if (is_meam_spline_) { if (meam_spline_->CompleteSetup(&max_cutoff_)) { HELPER_LOG_ERROR("Failed to complete the setup.\n"); return true; } } else if (is_meam_sw_spline_) { if (meam_sw_spline_->CompleteSetup(&max_cutoff_)) { HELPER_LOG_ERROR("Failed to complete the setup.\n"); return true; } } influence_distance_ = max_cutoff_; max_cutoff_squared_ = max_cutoff_ * max_cutoff_; // Update the maximum cutoff or influence distance value in KIM API object model_obj->SetInfluenceDistancePointer(&influence_distance_); // In this model we do not need to request neighbors from non-contributing // particles model_will_not_request_neighbors_of_non_contributing_particles_ // = 1 Update the cutoff value in KIM API object model_obj->SetNeighborListPointers( 1, &influence_distance_, &model_will_not_request_neighbors_of_non_contributing_particles_); // Everything is good return false; } int MEAMImplementation::RegisterKIMModelSettings( KIM::ModelDriverCreate *const model_driver_create) const { // Register numbering return model_driver_create->SetModelNumbering(KIM::NUMBERING::zeroBased); } #define KIM_LOGGER_OBJECT_NAME model_driver_create int MEAMImplementation::RegisterKIMParameters( KIM::ModelDriverCreate *const model_driver_create) { // Publish parameters int ier; if (is_meam_c_) { ier = model_driver_create->SetParameterPointer( 1, &meam_c_->cutoff_radius_, "rc", "cutoff radius for cutoff function"); if (ier) { LOG_ERROR("SetParameterPointer failed to set the pointer for 'rc'"); return ier; } ier = model_driver_create->SetParameterPointer( 1, &meam_c_->delr_, "delr", "length of smoothing distance for cutoff function"); if (ier) { LOG_ERROR("SetParameterPointer failed to set the pointer for 'delr'"); return ier; } ier = model_driver_create->SetParameterPointer( number_of_elements_, meam_c_->element_rho0_.data(), "rho0", "relative density for element I"); if (ier) { LOG_ERROR("SetParameterPointer failed to set the pointer for 'rho0'"); return ier; } ier = model_driver_create->SetParameterPointer( number_of_elements_ * number_of_elements_, meam_c_->element_Ec_.data(), "Ec", "cohesive energy of reference structure for I-J mixture.\n" "(matrix of size N x N = " + std::to_string(number_of_elements_) + " x " + std::to_string(number_of_elements_) + ") " + "in row-major storage.\n" "For example, to find the cohesive energy of " "reference structure for I-J mixture use:\n" "index = ((I - 1) * number_of_elements + J); 'Ec(I,J)'"); if (ier) { LOG_ERROR("SetParameterPointer failed to set the pointer for 'Ec'"); return ier; } ier = model_driver_create->SetParameterPointer( number_of_elements_ * number_of_elements_, meam_c_->element_delta_.data(), "delta", "heat of formation for I-J alloy. (if Ec_IJ is input as zero, then " "sets Ec_IJ = (Ec_II + Ec_JJ)/2 - delta_IJ.\n" "(matrix of size N x N = " + std::to_string(number_of_elements_) + " x " + std::to_string(number_of_elements_) + ") " + "in row-major storage.\n" "For example, to find the heat of formation for I-J alloy use:\n" "index = ((I - 1) * number_of_elements + J); 'delta(I,J)'"); if (ier) { LOG_ERROR("SetParameterPointer failed to set the pointer for 'delta'"); return ier; } ier = model_driver_create->SetParameterPointer( number_of_elements_ * number_of_elements_, meam_c_->element_alpha_.data(), "alpha", "alpha parameter for pair potential between I and J. " "(can be computed from bulk modulus of reference structure.)\n" "(matrix of size N x N = " + std::to_string(number_of_elements_) + " x " + std::to_string(number_of_elements_) + ") " + "in row-major storage.\n" "For example, to find the heat alpha parameter between I and J " "use:\nindex = ((I - 1) * number_of_elements + J); 'alpha(I,J)'"); if (ier) { LOG_ERROR("SetParameterPointer failed to set the pointer for 'alpha'"); return ier; } ier = model_driver_create->SetParameterPointer( number_of_elements_ * number_of_elements_, meam_c_->element_re_.data(), "re", "equilibrium distance between I and J in the reference structure.\n" "(matrix of size N x N = " + std::to_string(number_of_elements_) + " x " + std::to_string(number_of_elements_) + ") " + "in row-major storage.\n" "For example, to find the equilibrium distance between I and J " "use:\nindex = ((I - 1) * number_of_elements + J); 're(I,J)'"); if (ier) { LOG_ERROR("SetParameterPointer failed to set the pointer for 're'"); return ier; } ier = model_driver_create->SetParameterPointer( number_of_elements_ * number_of_elements_ * number_of_elements_, meam_c_->element_Cmax_.data(), "Cmax", "Cmax screening parameter when I-J pair is screened by K (I<=J).\n" "(Tensor of size N x N x N= " + std::to_string(number_of_elements_) + " x " + std::to_string(number_of_elements_) + " x " + std::to_string(number_of_elements_) + ") " + "in row-major storage.\n" "For example, to find the Cmax screening parameter when I-J pair " "screened by K use:\n" "index = (((I - 1) * number_of_elements + (J - 1)) * " "number_of_elements + k); 'Cmax(I,J,K)'"); if (ier) { LOG_ERROR("SetParameterPointer failed to set the pointer for 'Cmax'"); return ier; } ier = model_driver_create->SetParameterPointer( number_of_elements_ * number_of_elements_ * number_of_elements_, meam_c_->element_Cmin_.data(), "Cmin", "Cmin screening parameter when I-J pair is screened by K (I<=J).\n" "(Tensor of size N x N x N= " + std::to_string(number_of_elements_) + " x " + std::to_string(number_of_elements_) + " x " + std::to_string(number_of_elements_) + ") " + "in row-major storage.\n" "For example, to find the Cmin screening parameter when I-J pair " "screened by K use:\n" "index = (((I - 1) * number_of_elements + (J - 1)) * " "number_of_elements + k); 'Cmin(I,J,K)'"); if (ier) { LOG_ERROR("SetParameterPointer failed to set the pointer for 'Cmin'"); return ier; } ier = model_driver_create->SetParameterPointer( number_of_elements_ * number_of_elements_, reinterpret_cast(meam_c_->element_lattice_.data()), "lattce", "lattice structure of I-J reference structure.\n" "(matrix of size N x N = " + std::to_string(number_of_elements_) + " x " + std::to_string(number_of_elements_) + ") " + "in row-major storage.\n" "For example, to find the lattice structure of I-J reference " "structure use:\nindex = ((I - 1) * number_of_elements + J); " "'lattce(I,J)'\n" " 0; fcc = face centered cubic\n" " 1; bcc = body centered cubic\n" " 2; hcp = hexagonal close-packed\n" " 3; dim = dimer\n" " 4; dia = diamond (interlaced fcc for alloy)\n" " 5; dia3= diamond structure with primary 1NN and secondary 3NN " "interaction\n" " 6; b1 = rock salt (NaCl structure)\n" " 7; c11 = MoSi2 structure\n" " 8; l12 = Cu3Au structure (lower case L, followed by 12)\n" " 9; b2 = CsCl structure (interpenetrating simple cubic)\n" "10; ch4 = methane-like structure, only for binary system\n" "11; lin = linear structure (180 degree angle)\n" "12; zig = zigzag structure with a uniform angle\n" "13; tri = H2O-like structure that has an angle \n"); if (ier) { LOG_ERROR("SetParameterPointer failed to set the pointer for 'lattce'"); return ier; } ier = model_driver_create->SetParameterPointer( number_of_elements_ * number_of_elements_, meam_c_->element_nn2_.data(), "nn2", "turn on second-nearest neighbor MEAM formulation for I-J pair.\n" "(matrix of size N x N = " + std::to_string(number_of_elements_) + " x " + std::to_string(number_of_elements_) + ") " + "in row-major storage.\n" "For example, to find for I-J pair use:\n" "index = ((I - 1) * number_of_elements + J); 'nn2(I,J)'"); if (ier) { LOG_ERROR("SetParameterPointer failed to set the pointer for 'nn2'"); return ier; } ier = model_driver_create->SetParameterPointer( number_of_elements_ * number_of_elements_, meam_c_->element_attrac_.data(), "attrac", "additional cubic attraction term in Rose energy I-J pair potential.\n" "(matrix of size N x N = " + std::to_string(number_of_elements_) + " x " + std::to_string(number_of_elements_) + ") " + "in row-major storage.\n" "For example, to find the attraction term between I and J use:\n" "index = ((I - 1) * number_of_elements + J); 'attrac(I,J)'"); if (ier) { LOG_ERROR("SetParameterPointer failed to set the pointer for 'attrac'"); return ier; } ier = model_driver_create->SetParameterPointer( number_of_elements_ * number_of_elements_, meam_c_->element_repuls_.data(), "repuls", "additional cubic repulsive term in Rose energy I-J pair potential.\n" "(matrix of size N x N = " + std::to_string(number_of_elements_) + " x " + std::to_string(number_of_elements_) + ") " + "in row-major storage.\n" "For example, to find the repulsive term between I and J use:\n" "index = ((I - 1) * number_of_elements + J); 'repuls(I,J)'"); if (ier) { LOG_ERROR("SetParameterPointer failed to set the pointer for 'repuls'"); return ier; } ier = model_driver_create->SetParameterPointer( number_of_elements_ * number_of_elements_, meam_c_->element_zbl_.data(), "zbl", "blend the MEAM I-J pair potential with the ZBL potential for small " "atom separations.\n(matrix of size N x N = " + std::to_string(number_of_elements_) + " x " + std::to_string(number_of_elements_) + ") " + "in row-major storage.\n" "For example, to find the term between I and J use:\n" "index = ((I - 1) * number_of_elements + J); 'zbl(I,J)'"); if (ier) { LOG_ERROR("SetParameterPointer failed to set the pointer for 'zbl'"); return ier; } ier = model_driver_create->SetParameterPointer( number_of_elements_ * number_of_elements_, meam_c_->element_theta_.data(), "theta", "angle between three atoms in line, zigzag, and trimer reference " "structures in degrees.\n(matrix of size N x N = " + std::to_string(number_of_elements_) + " x " + std::to_string(number_of_elements_) + ") " + "in row-major storage.\n" "For example, to find the term between I and J use:\n" "index = ((I - 1) * number_of_elements + J); 'theta(I,J)'"); if (ier) { LOG_ERROR("SetParameterPointer failed to set the pointer for 'theta'"); return ier; } ier = model_driver_create->SetParameterPointer( 1, &meam_c_->gsmooth_factor_, "gsmooth_factor", "factor determining the length of the G-function smoothing region; " "only significant for ibar=0 or ibar=4.\n" "99.0 = short smoothing region, sharp step\n" "0.5 = long smoothing region, smooth step"); if (ier) { LOG_ERROR( "SetParameterPointer failed to set the pointer for 'gsmooth_factor'"); return ier; } ier = model_driver_create->SetParameterPointer( 1, &meam_c_->augment_t1_, "augt1", "integer flag for whether to augment t1 parameter by 3/5*t3 to account " "for old vs new meam formulations;\n0 = don't augment t1\n" "1 = augment t1"); if (ier) { LOG_ERROR("SetParameterPointer failed to set the pointer for 'augt1'"); return ier; } ier = model_driver_create->SetParameterPointer( 1, &meam_c_->ialloy_, "ialloy", "integer flag to use alternative averaging rule for t parameters, " "for comparison with the DYNAMO MEAM code;\n" "0 = standard averaging (matches ialloy=0 in DYNAMO)\n" "1 = alternative averaging (matches ialloy=1 in DYNAMO)\n" "2 = no averaging of t (use single-element values)"); if (ier) { LOG_ERROR("SetParameterPointer failed to set the pointer for 'ialloy'"); return ier; } ier = model_driver_create->SetParameterPointer( 1, &meam_c_->mixing_rule_compute_t_, "mixture_ref_t", "integer flag to use mixture average of t to compute the background " "reference density for alloys, instead of the single-element values\n" "0 = do not use mixture averaging for t in the reference density\n" "1 = use mixture averaging for t in the reference density"); if (ier) { LOG_ERROR( "SetParameterPointer failed to set the pointer for 'mixture_ref_t'"); return ier; } ier = model_driver_create->SetParameterPointer( 1, &meam_c_->rose_function_form_, "erose_form", "integer value to select the form of the Rose energy function\n" "0: erose = -Ec*(1+astar+a3*(astar**3)/(r/re))*exp(-astar)\n" "1: erose = -Ec*(1+astar+(-attrac+repuls/r)*(astar**3))*exp(-astar)\n" "2: erose = -Ec*(1 +astar + a3*(astar**3))*exp(-astar)"); if (ier) { LOG_ERROR( "SetParameterPointer failed to set the pointer for 'erose_form'"); return ier; } ier = model_driver_create->SetParameterPointer( 1, &meam_c_->fcut_function_form_, "fcut_form", "integer value to select the form of the MEAM cut-off function\n" "0: has the (1-x) term to the fourth power\n" "1: has the (1-x) term to the sixth power"); if (ier) { LOG_ERROR( "SetParameterPointer failed to set the pointer for 'fcut_form'"); return ier; } ier = model_driver_create->SetParameterPointer( 1, &meam_c_->use_rhobar_linear_embedding_function_, "emb_lin_neg", "integer value to select embedding function for negative densities\n" "0 = F(rho)=0\n" "1 = F(rho) = -asub*esub*rho (linear in rho, matches DYNAMO)"); if (ier) { LOG_ERROR( "SetParameterPointer failed to set the pointer for 'emb_lin_neg'"); return ier; } ier = model_driver_create->SetParameterPointer( 1, &meam_c_->dynamo_reference_density_, "bkgd_dyn", "integer value to select background density formula\n" "0 = rho_bkgd = rho_ref_meam(a) (as in the reference structure)\n" "1 = rho_bkgd = rho0_meam(a)*Z_meam(a) (matches DYNAMO)"); if (ier) { LOG_ERROR("SetParameterPointer failed to set the pointer for 'bkgd_dyn'"); return ier; } } // is_meam_c_ else if (is_meam_spline_) { for (int i = 0; i < number_of_elements_; ++i) { int const _ind = i * number_of_elements_ - i * (i + 1) / 2; for (int j = i; j < number_of_elements_; ++j) { int const ind = _ind + j; std::string name = "Phi_x_" + ((i == j) ? element_name_[i] : element_name_[i] + "_" + element_name_[j]); ier = model_driver_create->SetParameterPointer( meam_spline_->e_phi_[ind].GetNumberOfKnots(), meam_spline_->e_phi_[ind].x_data(), name, "Positions of spline knots"); if (ier) { LOG_ERROR("SetParameterPointer failed to set the pointer for '" + name + "'"); return ier; } name = "Phi_" + ((i == j) ? element_name_[i] : element_name_[i] + "_" + element_name_[j]); ier = model_driver_create->SetParameterPointer( meam_spline_->e_phi_[ind].GetNumberOfKnots(), meam_spline_->e_phi_[ind].value_data(), name, "The pair energy function tabulated over values of atomic " "separation distance"); if (ier) { LOG_ERROR("SetParameterPointer failed to set the pointer for '" + name + "'"); return ier; } name = "Phi_d0_" + ((i == j) ? element_name_[i] : element_name_[i] + "_" + element_name_[j]); ier = model_driver_create->SetParameterPointer( 1, meam_spline_->e_phi_[ind].derivative_0_data(), name, "The first derivative of the pair energy function at the beginning " "of the spline"); if (ier) { LOG_ERROR("SetParameterPointer failed to set the pointer for '" + name + "'"); return ier; } name = "Phi_dN_" + ((i == j) ? element_name_[i] : element_name_[i] + "_" + element_name_[j]); ier = model_driver_create->SetParameterPointer( 1, meam_spline_->e_phi_[ind].derivative_n_data(), name, "The first derivative of the pair energy function at the end of " "the spline"); if (ier) { LOG_ERROR("SetParameterPointer failed to set the pointer for '" + name + "'"); return ier; } } // Loop over j } // Loop over i for (int i = 0; i < number_of_elements_; ++i) { std::string name = "U_x_" + element_name_[i]; ier = model_driver_create->SetParameterPointer( meam_spline_->e_u_[i].GetNumberOfKnots(), meam_spline_->e_u_[i].x_data(), name, "Positions of spline knots"); if (ier) { LOG_ERROR("SetParameterPointer failed to set the pointer for '" + name + "'"); return ier; } name = "U_" + element_name_[i]; ier = model_driver_create->SetParameterPointer( meam_spline_->e_u_[i].GetNumberOfKnots(), meam_spline_->e_u_[i].value_data(), name, "The embedding energy functional tabulated over values of the MEAM " "density"); if (ier) { LOG_ERROR("SetParameterPointer failed to set the pointer for '" + name + "'"); return ier; } name = "U_d0_" + element_name_[i]; ier = model_driver_create->SetParameterPointer( 1, meam_spline_->e_u_[i].derivative_0_data(), name, "The first derivative of the embedding energy functional at the " "beginning of the spline"); if (ier) { LOG_ERROR("SetParameterPointer failed to set the pointer for '" + name + "'"); return ier; } name = "U_dN_" + element_name_[i]; ier = model_driver_create->SetParameterPointer( 1, meam_spline_->e_u_[i].derivative_n_data(), name, "The first derivative of the embedding energy functional at the end " "of the spline"); if (ier) { LOG_ERROR("SetParameterPointer failed to set the pointer for '" + name + "'"); return ier; } } // Loop over i for (int i = 0; i < number_of_elements_; ++i) { std::string name = "rho_x_" + element_name_[i]; ier = model_driver_create->SetParameterPointer( meam_spline_->rho_r_[i].GetNumberOfKnots(), meam_spline_->rho_r_[i].x_data(), name, "Positions of spline knots"); if (ier) { LOG_ERROR("SetParameterPointer failed to set the pointer for '" + name + "'"); return ier; } name = "rho_" + element_name_[i]; ier = model_driver_create->SetParameterPointer( meam_spline_->rho_r_[i].GetNumberOfKnots(), meam_spline_->rho_r_[i].value_data(), name, "The rho function in computing the MEAM density tabulated over " "values of the atomic separation distance"); if (ier) { LOG_ERROR("SetParameterPointer failed to set the pointer for '" + name + "'"); return ier; } name = "rho_d0_" + element_name_[i]; ier = model_driver_create->SetParameterPointer( 1, meam_spline_->rho_r_[i].derivative_0_data(), name, "The first derivative of the rho function in computing the MEAM " "density at the beginning of the spline"); if (ier) { LOG_ERROR("SetParameterPointer failed to set the pointer for '" + name + "'"); return ier; } name = "rho_dN_" + element_name_[i]; ier = model_driver_create->SetParameterPointer( 1, meam_spline_->rho_r_[i].derivative_n_data(), name, "The first derivative of the rho function in computing the MEAM " "density at the end of the spline"); if (ier) { LOG_ERROR("SetParameterPointer failed to set the pointer for '" + name + "'"); return ier; } } // Loop over i for (int i = 0; i < number_of_elements_; ++i) { std::string name = "f_x_" + element_name_[i]; ier = model_driver_create->SetParameterPointer( meam_spline_->rho_f_[i].GetNumberOfKnots(), meam_spline_->rho_f_[i].x_data(), name, "Positions of spline knots"); if (ier) { LOG_ERROR("SetParameterPointer failed to set the pointer for '" + name + "'"); return ier; } name = "f_" + element_name_[i]; ier = model_driver_create->SetParameterPointer( meam_spline_->rho_f_[i].GetNumberOfKnots(), meam_spline_->rho_f_[i].value_data(), name, "The f function in computing the MEAM density tabulated over values " "of the atomic separation distance. It comprises the three-body " "contributions to the density"); if (ier) { LOG_ERROR("SetParameterPointer failed to set the pointer for '" + name + "'"); return ier; } name = "f_d0_" + element_name_[i]; ier = model_driver_create->SetParameterPointer( 1, meam_spline_->rho_f_[i].derivative_0_data(), name, "The first derivative of the f function in computing the MEAM " "density at the beginning of the spline"); if (ier) { LOG_ERROR("SetParameterPointer failed to set the pointer for '" + name + "'"); return ier; } name = "f_dN_" + element_name_[i]; ier = model_driver_create->SetParameterPointer( 1, meam_spline_->rho_f_[i].derivative_n_data(), name, "The first derivative of the f function in computing the MEAM " "density at the end of the spline"); if (ier) { LOG_ERROR("SetParameterPointer failed to set the pointer for '" + name + "'"); return ier; } } // Loop over i for (int i = 0; i < number_of_elements_; ++i) { int const _ind = i * number_of_elements_ - i * (i + 1) / 2; for (int j = i; j < number_of_elements_; ++j) { int const ind = _ind + j; std::string name = "g_x_" + ((i == j) ? element_name_[i] : element_name_[i] + "_" + element_name_[j]); ier = model_driver_create->SetParameterPointer( meam_spline_->rho_g_[ind].GetNumberOfKnots(), meam_spline_->rho_g_[ind].x_data(), name, "Positions of spline knots"); if (ier) { LOG_ERROR("SetParameterPointer failed to set the pointer for '" + name + "'"); return ier; } name = "g_" + ((i == j) ? element_name_[i] : element_name_[i] + "_" + element_name_[j]); ier = model_driver_create->SetParameterPointer( meam_spline_->rho_g_[ind].GetNumberOfKnots(), meam_spline_->rho_g_[ind].value_data(), name, "The g function in computing the MEAM density tabulated over " "values of the bond angle between atoms j, i, and k, centered " "on i. It comprises the three-body contributions to the density"); if (ier) { LOG_ERROR("SetParameterPointer failed to set the pointer for '" + name + "'"); return ier; } name = "g_d0_" + ((i == j) ? element_name_[i] : element_name_[i] + "_" + element_name_[j]); ier = model_driver_create->SetParameterPointer( 1, meam_spline_->rho_g_[ind].derivative_0_data(), name, "The first derivative of the g function in computing the MEAM " "density at the beginning of the spline"); if (ier) { LOG_ERROR("SetParameterPointer failed to set the pointer for '" + name + "'"); return ier; } name = "g_dN_" + ((i == j) ? element_name_[i] : element_name_[i] + "_" + element_name_[j]); ier = model_driver_create->SetParameterPointer( 1, meam_spline_->rho_g_[ind].derivative_n_data(), name, "The first derivative of the g function in computing the MEAM " "density at the end of the spline"); if (ier) { LOG_ERROR("SetParameterPointer failed to set the pointer for '" + name + "'"); return ier; } } // Loop over j } // Loop over i } // is_meam_spline_ else if (is_meam_sw_spline_) { ier = model_driver_create->SetParameterPointer( meam_sw_spline_->e_phi_.GetNumberOfKnots(), meam_sw_spline_->e_phi_.x_data(), "Phi_x", "Positions of spline knots"); if (ier) { LOG_ERROR("SetParameterPointer failed to set the pointer for 'Phi'"); return ier; } ier = model_driver_create->SetParameterPointer( meam_sw_spline_->e_phi_.GetNumberOfKnots(), meam_sw_spline_->e_phi_.value_data(), "Phi", "The pair energy function tabulated over values of atomic " "separation distance"); if (ier) { LOG_ERROR("SetParameterPointer failed to set the pointer for 'Phi'"); return ier; } ier = model_driver_create->SetParameterPointer( 1, meam_sw_spline_->e_phi_.derivative_0_data(), "Phi_d0", "The first derivative of the pair energy function at the beginning of " "the spline"); if (ier) { LOG_ERROR("SetParameterPointer failed to set the pointer for 'Phi_d0'"); return ier; } ier = model_driver_create->SetParameterPointer( 1, meam_sw_spline_->e_phi_.derivative_n_data(), "Phi_dN", "The first derivative of the pair energy function at the end of the " "spline"); if (ier) { LOG_ERROR("SetParameterPointer failed to set the pointer for 'Phi_dN'"); return ier; } ier = model_driver_create->SetParameterPointer( meam_sw_spline_->e_u_.GetNumberOfKnots(), meam_sw_spline_->e_u_.x_data(), "U_x", "Positions of spline knots"); if (ier) { LOG_ERROR("SetParameterPointer failed to set the pointer for 'U'"); return ier; } ier = model_driver_create->SetParameterPointer( meam_sw_spline_->e_u_.GetNumberOfKnots(), meam_sw_spline_->e_u_.value_data(), "U", "The embedding energy functional tabulated over values of the MEAM " "density"); if (ier) { LOG_ERROR("SetParameterPointer failed to set the pointer for 'U'"); return ier; } ier = model_driver_create->SetParameterPointer( 1, meam_sw_spline_->e_u_.derivative_0_data(), "U_d0", "The first derivative of the embedding energy functional at the " "beginning of the spline"); if (ier) { LOG_ERROR("SetParameterPointer failed to set the pointer for 'U_d0'"); return ier; } ier = model_driver_create->SetParameterPointer( 1, meam_sw_spline_->e_u_.derivative_n_data(), "U_dN", "The first derivative of the embedding energy functional at the end of " "the spline"); if (ier) { LOG_ERROR("SetParameterPointer failed to set the pointer for 'U_dN'"); return ier; } ier = model_driver_create->SetParameterPointer( meam_sw_spline_->rho_r_.GetNumberOfKnots(), meam_sw_spline_->rho_r_.x_data(), "rho_x", "Positions of spline knots"); if (ier) { LOG_ERROR("SetParameterPointer failed to set the pointer for 'rho'"); return ier; } ier = model_driver_create->SetParameterPointer( meam_sw_spline_->rho_r_.GetNumberOfKnots(), meam_sw_spline_->rho_r_.value_data(), "rho", "The rho function in computing the MEAM density tabulated over " "values of the atomic separation distance"); if (ier) { LOG_ERROR("SetParameterPointer failed to set the pointer for 'rho'"); return ier; } ier = model_driver_create->SetParameterPointer( 1, meam_sw_spline_->rho_r_.derivative_0_data(), "rho_d0", "The first derivative of the rho function in computing the MEAM " "density at the beginning of the spline"); if (ier) { LOG_ERROR("SetParameterPointer failed to set the pointer for 'rho_d0'"); return ier; } ier = model_driver_create->SetParameterPointer( 1, meam_sw_spline_->rho_r_.derivative_n_data(), "rho_dN", "The first derivative of the rho function in computing the MEAM " "density at the end of the spline"); if (ier) { LOG_ERROR("SetParameterPointer failed to set the pointer for 'rho_dN'"); return ier; } ier = model_driver_create->SetParameterPointer( meam_sw_spline_->rho_f_.GetNumberOfKnots(), meam_sw_spline_->rho_f_.x_data(), "f_x", "Positions of spline knots"); if (ier) { LOG_ERROR("SetParameterPointer failed to set the pointer for 'f'"); return ier; } ier = model_driver_create->SetParameterPointer( meam_sw_spline_->rho_f_.GetNumberOfKnots(), meam_sw_spline_->rho_f_.value_data(), "f", "The f function in computing the MEAM density tabulated over values " "of the atomic separation distance. It comprises the three-body " "contributions to the density"); if (ier) { LOG_ERROR("SetParameterPointer failed to set the pointer for 'f'"); return ier; } ier = model_driver_create->SetParameterPointer( 1, meam_sw_spline_->rho_f_.derivative_0_data(), "f_d0", "The first derivative of the f function in computing the MEAM " "density at the beginning of the spline"); if (ier) { LOG_ERROR("SetParameterPointer failed to set the pointer for 'f_d0'"); return ier; } ier = model_driver_create->SetParameterPointer( 1, meam_sw_spline_->rho_f_.derivative_n_data(), "f_dN", "The first derivative of the f function in computing the MEAM " "density at the end of the spline"); if (ier) { LOG_ERROR("SetParameterPointer failed to set the pointer for 'f_dN'"); return ier; } ier = model_driver_create->SetParameterPointer( meam_sw_spline_->rho_g_.GetNumberOfKnots(), meam_sw_spline_->rho_g_.x_data(), "g_x", "Positions of spline knots"); if (ier) { LOG_ERROR("SetParameterPointer failed to set the pointer for 'g'"); return ier; } ier = model_driver_create->SetParameterPointer( meam_sw_spline_->rho_g_.GetNumberOfKnots(), meam_sw_spline_->rho_g_.value_data(), "g", "The g function in computing the MEAM density tabulated over " "values of the bond angle between atoms j, i, and k, centered " "on i. It comprises the three-body contributions to the density"); if (ier) { LOG_ERROR("SetParameterPointer failed to set the pointer for 'g'"); return ier; } ier = model_driver_create->SetParameterPointer( 1, meam_sw_spline_->rho_g_.derivative_0_data(), "g_d0", "The first derivative of the g function in computing the MEAM " "density at the beginning of the spline"); if (ier) { LOG_ERROR("SetParameterPointer failed to set the pointer for 'g_d0'"); return ier; } ier = model_driver_create->SetParameterPointer( 1, meam_sw_spline_->rho_g_.derivative_n_data(), "g_dN", "The first derivative of the g function in computing the MEAM " "density at the end of the spline"); if (ier) { LOG_ERROR("SetParameterPointer failed to set the pointer for 'g_dN'"); return ier; } ier = model_driver_create->SetParameterPointer( meam_sw_spline_->esw_f_.GetNumberOfKnots(), meam_sw_spline_->esw_f_.x_data(), "F_SW_x", "Positions of spline knots"); if (ier) { LOG_ERROR("SetParameterPointer failed to set the pointer for 'F_SW'"); return ier; } ier = model_driver_create->SetParameterPointer( meam_sw_spline_->esw_f_.GetNumberOfKnots(), meam_sw_spline_->esw_f_.value_data(), "F_SW", "The F_SW function in computing the MEAM energy tabulated over values " "of the atomic separation distance. It comprises the SW three-body " "contributions to the energy"); if (ier) { LOG_ERROR("SetParameterPointer failed to set the pointer for 'F_SW'"); return ier; } ier = model_driver_create->SetParameterPointer( 1, meam_sw_spline_->esw_f_.derivative_0_data(), "F_SW_d0", "The first derivative of the F_SW function in computing the MEAM " "energy at the beginning of the spline"); if (ier) { LOG_ERROR("SetParameterPointer failed to set the pointer for 'F_SW_d0'"); return ier; } ier = model_driver_create->SetParameterPointer( 1, meam_sw_spline_->esw_f_.derivative_n_data(), "F_SW_dN", "The first derivative of the F_SW function in computing the MEAM " "energy at the end of the spline"); if (ier) { LOG_ERROR("SetParameterPointer failed to set the pointer for 'F_SW_dN'"); return ier; } ier = model_driver_create->SetParameterPointer( meam_sw_spline_->esw_g_.GetNumberOfKnots(), meam_sw_spline_->esw_g_.x_data(), "G_SW_x", "Positions of spline knots"); if (ier) { LOG_ERROR("SetParameterPointer failed to set the pointer for 'G_SW'"); return ier; } ier = model_driver_create->SetParameterPointer( meam_sw_spline_->esw_g_.GetNumberOfKnots(), meam_sw_spline_->esw_g_.value_data(), "G_SW", "The G_SW function in computing the MEAM energy tabulated over " "values of the bond angle between atoms j, i, and k, centered " "on i. It comprises the three-body contributions to the energy"); if (ier) { LOG_ERROR("SetParameterPointer failed to set the pointer for 'G_SW'"); return ier; } ier = model_driver_create->SetParameterPointer( 1, meam_sw_spline_->esw_g_.derivative_0_data(), "G_SW_d0", "The first derivative of the G_SW function in computing the MEAM " "energy at the beginning of the spline"); if (ier) { LOG_ERROR("SetParameterPointer failed to set the pointer for 'G_SW_d0'"); return ier; } ier = model_driver_create->SetParameterPointer( 1, meam_sw_spline_->esw_g_.derivative_n_data(), "G_SW_dN", "The first derivative of the G_SW function in computing the MEAM " "energy at the end of the spline"); if (ier) { LOG_ERROR("SetParameterPointer failed to set the pointer for 'G_SW_dN'"); return ier; } } // is_meam_sw_spline_ // Everything is good return false; } #undef KIM_LOGGER_OBJECT_NAME int MEAMImplementation::RegisterKIMFunctions( KIM::ModelDriverCreate *const model_driver_create) const { // Register functions int ier = model_driver_create->SetRoutinePointer( KIM::MODEL_ROUTINE_NAME::Destroy, KIM::LANGUAGE_NAME::cpp, true, reinterpret_cast(MEAM::Destroy)) || model_driver_create->SetRoutinePointer( KIM::MODEL_ROUTINE_NAME::Refresh, KIM::LANGUAGE_NAME::cpp, true, reinterpret_cast(MEAM::Refresh)) || model_driver_create->SetRoutinePointer( KIM::MODEL_ROUTINE_NAME::WriteParameterizedModel, KIM::LANGUAGE_NAME::cpp, false, reinterpret_cast(MEAM::WriteParameterizedModel)) || model_driver_create->SetRoutinePointer( KIM::MODEL_ROUTINE_NAME::Compute, KIM::LANGUAGE_NAME::cpp, true, reinterpret_cast(MEAM::Compute)) || model_driver_create->SetRoutinePointer( KIM::MODEL_ROUTINE_NAME::ComputeArgumentsCreate, KIM::LANGUAGE_NAME::cpp, true, reinterpret_cast(MEAM::ComputeArgumentsCreate)) || model_driver_create->SetRoutinePointer( KIM::MODEL_ROUTINE_NAME::ComputeArgumentsDestroy, KIM::LANGUAGE_NAME::cpp, true, reinterpret_cast(MEAM::ComputeArgumentsDestroy)); return ier; } #define KIM_LOGGER_OBJECT_NAME model_compute_arguments int MEAMImplementation::SetComputeMutableValues( KIM::ModelComputeArguments const *const model_compute_arguments, bool &is_compute_energy, bool &is_compute_forces, bool &is_compute_particle_energy, bool &is_compute_virial, bool &is_compute_particle_virial, int const *&particle_species_codes, int const *&particle_contributing, VectorOfSizeDIM const *&coordinates, double *&energy, VectorOfSizeDIM *&forces, double *&particle_energy, VectorOfSizeSix *&virial, VectorOfSizeSix *&particle_virial) { // get compute flags int const *number_of_particles = nullptr; int ier = model_compute_arguments->GetArgumentPointer( KIM::COMPUTE_ARGUMENT_NAME::numberOfParticles, &number_of_particles) || model_compute_arguments->GetArgumentPointer( KIM::COMPUTE_ARGUMENT_NAME::particleSpeciesCodes, &particle_species_codes) || model_compute_arguments->GetArgumentPointer( KIM::COMPUTE_ARGUMENT_NAME::particleContributing, &particle_contributing) || model_compute_arguments->GetArgumentPointer( KIM::COMPUTE_ARGUMENT_NAME::coordinates, (double const **)&coordinates) || model_compute_arguments->GetArgumentPointer( KIM::COMPUTE_ARGUMENT_NAME::partialEnergy, &energy) || model_compute_arguments->GetArgumentPointer( KIM::COMPUTE_ARGUMENT_NAME::partialForces, (double const **)&forces) || model_compute_arguments->GetArgumentPointer( KIM::COMPUTE_ARGUMENT_NAME::partialParticleEnergy, &particle_energy) || model_compute_arguments->GetArgumentPointer( KIM::COMPUTE_ARGUMENT_NAME::partialVirial, (double const **)&virial) || model_compute_arguments->GetArgumentPointer( KIM::COMPUTE_ARGUMENT_NAME::partialParticleVirial, (double const **)&particle_virial); if (ier) { LOG_ERROR("GetArgumentPointer return an error"); return true; } is_compute_energy = (energy); is_compute_forces = (forces); is_compute_particle_energy = (particle_energy); is_compute_virial = (virial); is_compute_particle_virial = (particle_virial); // Update value cached_number_of_particles_ = *number_of_particles; // everything is good return false; } #undef KIM_LOGGER_OBJECT_NAME int MEAMImplementation::GetComputeIndex( bool const is_compute_energy, bool const is_compute_forces, bool const is_compute_particle_energy, bool const is_compute_virial, bool const is_compute_particle_virial) const { int const is_compute_energy_int = static_cast(is_compute_energy); int const is_compute_forces_int = static_cast(is_compute_forces); int const is_compute_particle_energy_int = static_cast(is_compute_particle_energy); int const is_compute_virial_int = static_cast(is_compute_virial); int const is_compute_particle_virial_int = static_cast(is_compute_particle_virial); // energy int index = is_compute_energy_int * kForce * kParticleEnergy * kVirial * kParticleVirial; // force index += is_compute_forces_int * kParticleEnergy * kVirial * kParticleVirial; // particle_energy index += is_compute_particle_energy_int * kVirial * kParticleVirial; // virial index += is_compute_virial_int * kParticleVirial; // particle_virial index += is_compute_particle_virial_int; return index; } int MEAMImplementation::GetComputeIndex( bool const is_compute_energy, bool const is_compute_forces, bool const is_compute_particle_energy, bool const is_compute_virial, bool const is_compute_particle_virial, bool const are_knots_on_regular_grid) const { int const is_compute_energy_int = static_cast(is_compute_energy); int const is_compute_forces_int = static_cast(is_compute_forces); int const is_compute_particle_energy_int = static_cast(is_compute_particle_energy); int const is_compute_virial_int = static_cast(is_compute_virial); int const is_compute_particle_virial_int = static_cast(is_compute_particle_virial); int const are_knots_on_regular_grid_int = static_cast(are_knots_on_regular_grid); // energy int index = is_compute_energy_int * kForce * kParticleEnergy * kVirial * kParticleVirial * kKnotsOnRegularGrid; // force index += is_compute_forces_int * kParticleEnergy * kVirial * kParticleVirial * kKnotsOnRegularGrid; // particle_energy index += is_compute_particle_energy_int * kVirial * kParticleVirial * kKnotsOnRegularGrid; // virial index += is_compute_virial_int * kParticleVirial * kKnotsOnRegularGrid; // particle_virial index += is_compute_particle_virial_int * kKnotsOnRegularGrid; // knots_are_on_regular_grid index += are_knots_on_regular_grid_int; return index; } #define KIM_LOGGER_OBJECT_NAME model_compute_arguments_create int MEAMImplementation::RegisterKIMComputeArgumentsSettings( KIM::ModelComputeArgumentsCreate *const model_compute_arguments_create) const { // register arguments LOG_INFORMATION("Register argument support status"); int ier = model_compute_arguments_create->SetArgumentSupportStatus( KIM::COMPUTE_ARGUMENT_NAME::partialEnergy, KIM::SUPPORT_STATUS::optional) || model_compute_arguments_create->SetArgumentSupportStatus( KIM::COMPUTE_ARGUMENT_NAME::partialForces, KIM::SUPPORT_STATUS::optional) || model_compute_arguments_create->SetArgumentSupportStatus( KIM::COMPUTE_ARGUMENT_NAME::partialParticleEnergy, KIM::SUPPORT_STATUS::optional) || model_compute_arguments_create->SetArgumentSupportStatus( KIM::COMPUTE_ARGUMENT_NAME::partialVirial, KIM::SUPPORT_STATUS::optional) || model_compute_arguments_create->SetArgumentSupportStatus( KIM::COMPUTE_ARGUMENT_NAME::partialParticleVirial, KIM::SUPPORT_STATUS::optional); return ier; } #undef KIM_LOGGER_OBJECT_NAME // public method int MEAMImplementation::Refresh(KIM::ModelRefresh *const model_refresh) { return SetRefreshMutableValues(model_refresh); } int MEAMImplementation::WriteParameterizedModel( KIM::ModelWriteParameterizedModel const *const model_write_parameterized_model) const { if (!model_write_parameterized_model) { std::string msg = "The model_write_parameterized_model "; msg += "pointer is not assigned.\n"; HELPER_LOG_ERROR(msg); return true; } std::string const *path = nullptr; std::string const *model_name = nullptr; model_write_parameterized_model->GetPath(&path); model_write_parameterized_model->GetModelName(&model_name); // MEAM elements file { std::string elements_file(*model_name); elements_file += ".elements.meam"; // Set the file name for the next parameter file. model_write_parameterized_model->SetParameterFileName(elements_file); // MEAM elements file std::string buffer = *path + "/" + elements_file; std::fstream fs; fs.open(buffer.c_str(), std::fstream::out); if (!fs.is_open()) { HELPER_LOG_ERROR("Unable to open the elements file for writing.\n"); return true; } else { if (fs.fail()) { std::string msg = "An error has occurred from opening the "; msg += "file on the associated stream!\n"; HELPER_LOG_ERROR(msg); return true; } fs << "# MEAM elements "; for (int ielem = 0; ielem < number_of_elements_; ++ielem) { fs << element_name_[ielem] << " "; } fs << "\n\n\n\n#\n# MEAM elements file\n#\n"; fs.close(); } } if (is_meam_c_) { std::string parameter_file; for (auto i : element_name_) { parameter_file += i; } parameter_file += ".parameter.meam"; // Set the file name for the next parameter file. model_write_parameterized_model->SetParameterFileName(parameter_file); // MEAM elements file std::string buffer = *path + "/" + parameter_file; std::fstream fs; fs.open(buffer.c_str(), std::fstream::out); if (!fs.is_open()) { HELPER_LOG_ERROR("Unable to open the parameter file for writing.\n"); return true; } else { if (fs.fail()) { std::string msg = "An error has occurred from opening the "; msg += "file on the associated stream!\n"; HELPER_LOG_ERROR(msg); return true; } fs << "# MEAM parameter file "; if (!meam_c_->augment_t1_) { fs << "augt1 = 0"; } if (meam_c_->ialloy_) { fs << "ialloy = " << meam_c_->ialloy_; } if (meam_c_->mixing_rule_compute_t_) { fs << "mixture_ref_t = 1"; } if (meam_c_->rose_function_form_) { fs << "erose_form = " << meam_c_->rose_function_form_; } if (meam_c_->fcut_function_form_) { fs << "fcut_form = " << meam_c_->fcut_function_form_; } if (meam_c_->use_rhobar_linear_embedding_function_) { fs << "emb_lin_neg = " << meam_c_->use_rhobar_linear_embedding_function_; } if (meam_c_->dynamo_reference_density_) { fs << "bkgd_dyn = " << meam_c_->dynamo_reference_density_; } if (special::IsNotZero(meam_c_->cutoff_radius_ - 4.0)) { fs << "rc = " << meam_c_->cutoff_radius_; } if (special::IsNotZero(meam_c_->delr_ - 0.1)) { fs << "delr = " << meam_c_->delr_; } if (special::IsNotZero(meam_c_->gsmooth_factor_ - 99.0)) { fs << "gsmooth_factor = " << meam_c_->gsmooth_factor_; } for (int i = 0; i < number_of_elements_; ++i) { fs << "rho0(" << i + 1 << ") = " << meam_c_->element_rho0_[i]; } for (int i = 0; i < number_of_elements_; ++i) { for (int j = i; j < number_of_elements_; ++j) { if (meam_c_->element_lattice_(i, j) != Lattice::FCC) { fs << "lattce(" << i + 1 << "," << j + 1 << ") = '" << meam_c_->LatticeToString(meam_c_->element_lattice_(i, j)) << "'"; } } } for (int i = 0; i < number_of_elements_; ++i) { for (int j = i; j < number_of_elements_; ++j) { if (meam_c_->element_nn2_(i, j)) { fs << "nn2(" << i + 1 << "," << j + 1 << ") = 1"; } } } for (int i = 0; i < number_of_elements_; ++i) { for (int j = i; j < number_of_elements_; ++j) { if (!meam_c_->element_zbl_(i, j)) { fs << "zbl(" << i + 1 << "," << j + 1 << ") = 0"; } } } for (int i = 0; i < number_of_elements_; ++i) { for (int j = i; j < number_of_elements_; ++j) { if (special::IsNotZero(meam_c_->element_alpha_(i, j))) { fs << "alpha(" << i + 1 << "," << j + 1 << ") = " << meam_c_->element_alpha_(i, j); } } } for (int i = 0; i < number_of_elements_; ++i) { for (int j = i; j < number_of_elements_; ++j) { if (special::IsNotZero(meam_c_->element_re_(i, j))) { fs << "re(" << i + 1 << "," << j + 1 << ") = " << meam_c_->element_re_(i, j); } } } for (int i = 0; i < number_of_elements_; ++i) { for (int j = i; j < number_of_elements_; ++j) { if (special::IsNotZero(meam_c_->element_Ec_(i, j))) { fs << "Ec(" << i + 1 << "," << j + 1 << ") = " << meam_c_->element_Ec_(i, j); } } } for (int i = 0; i < number_of_elements_; ++i) { for (int j = i; j < number_of_elements_; ++j) { if (special::IsNotZero(meam_c_->element_delta_(i, j))) { fs << "delta(" << i + 1 << "," << j + 1 << ") = " << meam_c_->element_delta_(i, j); } } } for (int i = 0; i < number_of_elements_; ++i) { for (int j = i; j < number_of_elements_; ++j) { if (special::IsNotZero(meam_c_->element_attrac_(i, j))) { fs << "attrac(" << i + 1 << "," << j + 1 << ") = " << meam_c_->element_attrac_(i, j); } } } for (int i = 0; i < number_of_elements_; ++i) { for (int j = i; j < number_of_elements_; ++j) { if (special::IsNotZero(meam_c_->element_repuls_(i, j))) { fs << "repuls(" << i + 1 << "," << j + 1 << ") = " << meam_c_->element_repuls_(i, j); } } } for (int i = 0; i < number_of_elements_; ++i) { for (int j = i; j < number_of_elements_; ++j) { if (special::IsNotZero(meam_c_->element_theta_(i, j) - 180.0)) { fs << "theta(" << i + 1 << "," << j + 1 << ") = " << meam_c_->element_theta_(i, j); } } } for (int i = 0; i < number_of_elements_; ++i) { for (int j = i; j < number_of_elements_; ++j) { for (int k = 0; k < number_of_elements_; ++k) { if (special::IsNotZero(meam_c_->element_Cmin_(i, j, k) - 2.0)) { fs << "Cmin(" << i + 1 << "," << j + 1 << "," << k + 1 << ") = " << meam_c_->element_Cmin_(i, j, k); } } } } for (int i = 0; i < number_of_elements_; ++i) { for (int j = i; j < number_of_elements_; ++j) { for (int k = 0; k < number_of_elements_; ++k) { if (special::IsNotZero(meam_c_->element_Cmax_(i, j, k) - 2.8)) { fs << "Cmax(" << i + 1 << "," << j + 1 << "," << k + 1 << ") = " << meam_c_->element_Cmax_(i, j, k); } } } } fs << "\n\n\n\n#\n# MEAM parameter file\n#\n"; fs.close(); } } else if (is_meam_spline_) { std::string potential_file; for (auto i : element_name_) { potential_file += i; } potential_file += ".potential.meam.spline"; // Set the file name for the next parameter file. model_write_parameterized_model->SetParameterFileName(potential_file); // MEAM elements file std::string buffer = *path + "/" + potential_file; std::fstream fs; fs.open(buffer.c_str(), std::fstream::out); if (!fs.is_open()) { HELPER_LOG_ERROR("Unable to open the potential file for writing.\n"); return true; } else { if (fs.fail()) { std::string msg = "An error has occurred from opening the "; msg += "file on the associated stream!\n"; HELPER_LOG_ERROR(msg); return true; } fs << "# MEAM SPLINE potential "; { std::string unique_names; for (auto i : element_name_) { unique_names += " " + i; } fs << "meam/spline " << number_of_elements_ << unique_names; } for (std::size_t i = 0; i < meam_spline_->e_phi_.size(); ++i) { fs << "spline3eq"; int const number_of_knots = meam_spline_->e_phi_[i].GetNumberOfKnots(); fs << number_of_knots; fs << *meam_spline_->e_phi_[i].derivative_0_data() << " " << *meam_spline_->e_phi_[i].derivative_n_data(); auto x = meam_spline_->e_phi_[i].x_data(); auto v = meam_spline_->e_phi_[i].value_data(); auto d = meam_spline_->e_phi_[i].second_derivative_value_data(); for (int n = 0; n < number_of_knots; ++n) { fs << x[n] << " " << v[n] << " " << d[n]; } } for (std::size_t i = 0; i < meam_spline_->rho_r_.size(); ++i) { fs << "spline3eq"; int const number_of_knots = meam_spline_->rho_r_[i].GetNumberOfKnots(); fs << number_of_knots; fs << *meam_spline_->rho_r_[i].derivative_0_data() << " " << *meam_spline_->rho_r_[i].derivative_n_data(); auto x = meam_spline_->rho_r_[i].x_data(); auto v = meam_spline_->rho_r_[i].value_data(); auto d = meam_spline_->rho_r_[i].second_derivative_value_data(); for (int n = 0; n < number_of_knots; ++n) { fs << x[n] << " " << v[n] << " " << d[n]; } } for (std::size_t i = 0; i < meam_spline_->e_u_.size(); ++i) { fs << "spline3eq"; int const number_of_knots = meam_spline_->e_u_[i].GetNumberOfKnots(); fs << number_of_knots; fs << *meam_spline_->e_u_[i].derivative_0_data() << " " << *meam_spline_->e_u_[i].derivative_n_data(); auto x = meam_spline_->e_u_[i].x_data(); auto v = meam_spline_->e_u_[i].value_data(); auto d = meam_spline_->e_u_[i].second_derivative_value_data(); for (int n = 0; n < number_of_knots; ++n) { fs << x[n] << " " << v[n] << " " << d[n]; } } for (std::size_t i = 0; i < meam_spline_->rho_f_.size(); ++i) { fs << "spline3eq"; int const number_of_knots = meam_spline_->rho_f_[i].GetNumberOfKnots(); fs << number_of_knots; fs << *meam_spline_->rho_f_[i].derivative_0_data() << " " << *meam_spline_->rho_f_[i].derivative_n_data(); auto x = meam_spline_->rho_f_[i].x_data(); auto v = meam_spline_->rho_f_[i].value_data(); auto d = meam_spline_->rho_f_[i].second_derivative_value_data(); for (int n = 0; n < number_of_knots; ++n) { fs << x[n] << " " << v[n] << " " << d[n]; } } for (std::size_t i = 0; i < meam_spline_->rho_g_.size(); ++i) { fs << "spline3eq"; int const number_of_knots = meam_spline_->rho_g_[i].GetNumberOfKnots(); fs << number_of_knots; fs << *meam_spline_->rho_g_[i].derivative_0_data() << " " << *meam_spline_->rho_g_[i].derivative_n_data(); auto x = meam_spline_->rho_g_[i].x_data(); auto v = meam_spline_->rho_g_[i].value_data(); auto d = meam_spline_->rho_g_[i].second_derivative_value_data(); for (int n = 0; n < number_of_knots; ++n) { fs << x[n] << " " << v[n] << " " << d[n]; } } fs << "\n\n\n\n#\n# MEAM SPLINE potential file\n#\n"; fs.close(); } } else if (is_meam_sw_spline_) { std::string potential_file; for (auto i : element_name_) { potential_file += i; } potential_file += ".potential.meam.sw.spline"; // Set the file name for the next parameter file. model_write_parameterized_model->SetParameterFileName(potential_file); // MEAM elements file std::string buffer = *path + "/" + potential_file; std::fstream fs; fs.open(buffer.c_str(), std::fstream::out); if (!fs.is_open()) { HELPER_LOG_ERROR("Unable to open the potential file for writing.\n"); return true; } else { if (fs.fail()) { std::string msg = "An error has occurred from opening the "; msg += "file on the associated stream!\n"; HELPER_LOG_ERROR(msg); return true; } fs << "# MEAM SW SPLINE potential "; // e_phi_ { int const number_of_knots = meam_sw_spline_->e_phi_.GetNumberOfKnots(); fs << number_of_knots; fs << *meam_sw_spline_->e_phi_.derivative_0_data() << " " << *meam_sw_spline_->e_phi_.derivative_n_data(); fs << meam_sw_spline_->e_phi_.OldFormatString(); auto x = meam_sw_spline_->e_phi_.x_data(); auto v = meam_sw_spline_->e_phi_.value_data(); auto d = meam_sw_spline_->e_phi_.second_derivative_value_data(); for (int n = 0; n < number_of_knots; ++n) { fs << x[n] << " " << v[n] << " " << d[n]; } } // esw_f_ { int const number_of_knots = meam_sw_spline_->esw_f_.GetNumberOfKnots(); fs << number_of_knots; fs << *meam_sw_spline_->esw_f_.derivative_0_data() << " " << *meam_sw_spline_->esw_f_.derivative_n_data(); fs << meam_sw_spline_->esw_f_.OldFormatString(); auto x = meam_sw_spline_->esw_f_.x_data(); auto v = meam_sw_spline_->esw_f_.value_data(); auto d = meam_sw_spline_->esw_f_.second_derivative_value_data(); for (int n = 0; n < number_of_knots; ++n) { fs << x[n] << " " << v[n] << " " << d[n]; } } // esw_g_ { int const number_of_knots = meam_sw_spline_->esw_g_.GetNumberOfKnots(); fs << number_of_knots; fs << *meam_sw_spline_->esw_g_.derivative_0_data() << " " << *meam_sw_spline_->esw_g_.derivative_n_data(); fs << meam_sw_spline_->esw_g_.OldFormatString(); auto x = meam_sw_spline_->esw_g_.x_data(); auto v = meam_sw_spline_->esw_g_.value_data(); auto d = meam_sw_spline_->esw_g_.second_derivative_value_data(); for (int n = 0; n < number_of_knots; ++n) { fs << x[n] << " " << v[n] << " " << d[n]; } } // rho_r_ { int const number_of_knots = meam_sw_spline_->rho_r_.GetNumberOfKnots(); fs << number_of_knots; fs << *meam_sw_spline_->rho_r_.derivative_0_data() << " " << *meam_sw_spline_->rho_r_.derivative_n_data(); fs << meam_sw_spline_->rho_r_.OldFormatString(); auto x = meam_sw_spline_->rho_r_.x_data(); auto v = meam_sw_spline_->rho_r_.value_data(); auto d = meam_sw_spline_->rho_r_.second_derivative_value_data(); for (int n = 0; n < number_of_knots; ++n) { fs << x[n] << " " << v[n] << " " << d[n]; } } // e_u_ { int const number_of_knots = meam_sw_spline_->e_u_.GetNumberOfKnots(); fs << number_of_knots; fs << *meam_sw_spline_->e_u_.derivative_0_data() << " " << *meam_sw_spline_->e_u_.derivative_n_data(); fs << meam_sw_spline_->e_u_.OldFormatString(); auto x = meam_sw_spline_->e_u_.x_data(); auto v = meam_sw_spline_->e_u_.value_data(); auto d = meam_sw_spline_->e_u_.second_derivative_value_data(); for (int n = 0; n < number_of_knots; ++n) { fs << x[n] << " " << v[n] << " " << d[n]; } } // rho_f_ { int const number_of_knots = meam_sw_spline_->rho_f_.GetNumberOfKnots(); fs << number_of_knots; fs << *meam_sw_spline_->rho_f_.derivative_0_data() << " " << *meam_sw_spline_->rho_f_.derivative_n_data(); fs << meam_sw_spline_->rho_f_.OldFormatString(); auto x = meam_sw_spline_->rho_f_.x_data(); auto v = meam_sw_spline_->rho_f_.value_data(); auto d = meam_sw_spline_->rho_f_.second_derivative_value_data(); for (int n = 0; n < number_of_knots; ++n) { fs << x[n] << " " << v[n] << " " << d[n]; } } // rho_g_ { int const number_of_knots = meam_sw_spline_->rho_g_.GetNumberOfKnots(); fs << number_of_knots; fs << *meam_sw_spline_->rho_g_.derivative_0_data() << " " << *meam_sw_spline_->rho_g_.derivative_n_data(); fs << meam_sw_spline_->rho_g_.OldFormatString(); auto x = meam_sw_spline_->rho_g_.x_data(); auto v = meam_sw_spline_->rho_g_.value_data(); auto d = meam_sw_spline_->rho_g_.second_derivative_value_data(); for (int n = 0; n < number_of_knots; ++n) { fs << x[n] << " " << v[n] << " " << d[n]; } } fs << "\n\n\n\n#\n# MEAM SW SPLINE potential file\n#\n"; fs.close(); } } return false; } int MEAMImplementation::Compute( KIM::ModelCompute const *const model_compute, KIM::ModelComputeArguments const *const model_compute_arguments) { // KIM API Model Output compute flags bool is_compute_energy = false; bool is_compute_forces = false; bool is_compute_particle_energy = false; bool is_compute_virial = false; bool is_compute_particle_virial = false; // KIM API Model Input int const *particle_species_codes = nullptr; int const *particle_contributing = nullptr; VectorOfSizeDIM const *coordinates = nullptr; // KIM API Model Output double *energy = nullptr; VectorOfSizeDIM *forces = nullptr; double *particle_energy = nullptr; VectorOfSizeSix *virial = nullptr; VectorOfSizeSix *particle_virial = nullptr; int ier = SetComputeMutableValues( model_compute_arguments, is_compute_energy, is_compute_forces, is_compute_particle_energy, is_compute_virial, is_compute_particle_virial, particle_species_codes, particle_contributing, coordinates, energy, forces, particle_energy, virial, particle_virial); if (ier) { HELPER_LOG_ERROR("SetComputeMutableValues fails.\n"); return true; } #include "meam_implementation_compute_dispatch.cpp" return ier; } int MEAMImplementation::ComputeArgumentsCreate( KIM::ModelComputeArgumentsCreate *const model_compute_arguments_create) const { return RegisterKIMComputeArgumentsSettings(model_compute_arguments_create); } int MEAMImplementation::ComputeArgumentsDestroy( KIM::ModelComputeArgumentsDestroy *const) const { // Nothing to do for this case // Everything is good return false; } std::size_t MEAMImplementation::TotalNumberOfNeighbors( KIM::ModelComputeArguments const *const model_compute_arguments, int const *const particle_contributing) const { int const *neighbors_of_particle; std::size_t total_number_of_neighbors = 0; for (int i = 0, number_of_neighbors; i < cached_number_of_particles_; ++i) { if (!particle_contributing[i]) { continue; } // Get the neighbors model_compute_arguments->GetNeighborList(0, i, &number_of_neighbors, &neighbors_of_particle); // Setup loop over neighbors of current particle for (int jn = 0; jn < number_of_neighbors; ++jn) { int const j = neighbors_of_particle[jn]; if (!(particle_contributing[j] && j < i)) { ++total_number_of_neighbors; } // Effective half list } // Loop over j } // Loop over i return total_number_of_neighbors; } int MEAMImplementation::MaxNumberOfNeighbors( KIM::ModelComputeArguments const *const model_compute_arguments, int const *const particle_contributing) const { int const *neighbors_of_particle; int max_number_of_neighbors = 0; for (int i = 0, number_of_neighbors; i < cached_number_of_particles_; ++i) { if (!particle_contributing[i]) { continue; } // Get the neighbors model_compute_arguments->GetNeighborList(0, i, &number_of_neighbors, &neighbors_of_particle); if (number_of_neighbors > max_number_of_neighbors) { max_number_of_neighbors = number_of_neighbors; } } // Loop over i return max_number_of_neighbors; } // Main compute function #define KIM_LOGGER_OBJECT_NAME model_compute_arguments template int MEAMImplementation::MeamCCompute( KIM::ModelCompute const *const, KIM::ModelComputeArguments const *const model_compute_arguments, int const *const particle_species_codes, int const *const particle_contributing, const VectorOfSizeDIM *const coordinates, double *const energy, VectorOfSizeDIM *const forces, double *const particle_energy, VectorOfSizeSix virial, VectorOfSizeSix *const particle_virial) const { // Everything is good int ier = false; // Nothing to compute if (!IsComputeEnergy && !IsComputeForces && !IsComputeParticleEnergy && !IsComputeVirial && !IsComputeParticleVirial) { // Everything is good return ier; } // Initialize energy if (IsComputeEnergy) { *energy = 0.0; } // Initialize forces if (IsComputeForces) { for (int i = 0; i < cached_number_of_particles_; ++i) { forces[i][0] = 0.0; forces[i][1] = 0.0; forces[i][2] = 0.0; } } // Initialize particle energy if (IsComputeParticleEnergy) { for (int i = 0; i < cached_number_of_particles_; ++i) { particle_energy[i] = 0.0; } } // Initialize virial if (IsComputeVirial) { virial[0] = 0.0; virial[1] = 0.0; virial[2] = 0.0; virial[3] = 0.0; virial[4] = 0.0; virial[5] = 0.0; } // Initialize particle virial if (IsComputeParticleVirial) { for (int i = 0; i < cached_number_of_particles_; ++i) { particle_virial[i][0] = 0.0; particle_virial[i][1] = 0.0; particle_virial[i][2] = 0.0; particle_virial[i][3] = 0.0; particle_virial[i][4] = 0.0; particle_virial[i][5] = 0.0; } } // Set the density arrays, and initialize them if necessary meam_c_->ResizeDenistyArrays(cached_number_of_particles_); // Grow local arrays scrfcn_, and dscrfcn_ if necessary // Compute the total number of neighbors of all contributing atoms std::size_t const total_number_of_neighbors = TotalNumberOfNeighbors(model_compute_arguments, particle_contributing); meam_c_->ResizeScreeningArrays(total_number_of_neighbors); int number_of_neighbors; int const *neighbors_of_particle = nullptr; // Initialize the density calculation for (int i = 0, offset = 0; i < cached_number_of_particles_; ++i) { if (!particle_contributing[i]) { continue; } // Get the neighbors model_compute_arguments->GetNeighborList(0, i, &number_of_neighbors, &neighbors_of_particle); meam_c_->InitializeDensityCalculation( i, number_of_neighbors, neighbors_of_particle, offset, coordinates, particle_species_codes, particle_contributing); } // Loop over i // Finalize the density calculation for (int i = 0; i < cached_number_of_particles_; ++i) { if (!particle_contributing[i]) { continue; } int const species_i = particle_species_codes[i]; double embedding_energy; meam_c_->FinalizeDensityCalculation(i, species_i, embedding_energy); if (IsComputeEnergy) { *energy += embedding_energy; } if (IsComputeParticleEnergy) { particle_energy[i] += embedding_energy; } } // Loop over i // Temporary sum double ts; for (int i = 0, offset = 0; i < cached_number_of_particles_; ++i) { if (!particle_contributing[i]) { continue; } // Get the neighbors model_compute_arguments->GetNeighborList(0, i, &number_of_neighbors, &neighbors_of_particle); double const *const scrfcn = &meam_c_->scrfcn_[offset]; double const *const dscrfcn = &meam_c_->dscrfcn_[offset]; int const species_i = particle_species_codes[i]; double const xi = coordinates[i][0]; double const yi = coordinates[i][1]; double const zi = coordinates[i][2]; int nth_neighbor_of_i = -1; // Setup loop over neighbors of current particle for (int jn = 0; jn < number_of_neighbors; ++jn) { int const j = neighbors_of_particle[jn]; int const contributing_j = particle_contributing[j]; // Effective half list if (!(contributing_j && j < i)) { ++nth_neighbor_of_i; // Screening function double const sij = scrfcn[nth_neighbor_of_i]; if (special::IsNotZero(sij)) { int const species_j = particle_species_codes[j]; double const xj = coordinates[j][0]; double const yj = coordinates[j][1]; double const zj = coordinates[j][2]; double const dx = xj - xi; double const dy = yj - yi; double const dz = zj - zi; double const rij2 = dx * dx + dy * dy + dz * dz; if (rij2 < max_cutoff_squared_) { double const rij = std::sqrt(rij2); double const rij_inv = 1.0 / rij; // Compute pair densities and derivatives double rhoa0_i, drhoa0_i; double rhoa1_i, drhoa1_i; double rhoa2_i, drhoa2_i; double rhoa3_i, drhoa3_i; double rhoa0_j, drhoa0_j; double rhoa1_j, drhoa1_j; double rhoa2_j, drhoa2_j; double rhoa3_j, drhoa3_j; // Eq.4.8 meam_c_->ComputeAtomicElectronDensities( species_i, species_j, rij, rhoa0_i, drhoa0_i, rhoa1_i, drhoa1_i, rhoa2_i, drhoa2_i, rhoa3_i, drhoa3_i, rhoa0_j, drhoa0_j, rhoa1_j, drhoa1_j, rhoa2_j, drhoa2_j, rhoa3_j, drhoa3_j); double const dxdxv = dx * dx * kVoight2dFactor[0]; double const dxdyv = dx * dy * kVoight2dFactor[1]; double const dxdzv = dx * dz * kVoight2dFactor[2]; double const dydyv = dy * dy * kVoight2dFactor[3]; double const dydzz = dy * dz * kVoight2dFactor[4]; double const dzdzv = dz * dz * kVoight2dFactor[5]; double const dxdxdxv = dx * dx * dx * kVoight3dFactor[0]; double const dxdxdyv = dx * dx * dy * kVoight3dFactor[1]; double const dxdxdzv = dx * dx * dz * kVoight3dFactor[2]; double const dxdydyv = dx * dy * dy * kVoight3dFactor[3]; double const dxdydzv = dx * dy * dz * kVoight3dFactor[4]; double const dxdzdzv = dx * dz * dz * kVoight3dFactor[5]; double const dydydyv = dy * dy * dy * kVoight3dFactor[6]; double const dydydzv = dy * dy * dz * kVoight3dFactor[7]; double const dydzdzv = dy * dz * dz * kVoight3dFactor[8]; double const dzdzdzv = dz * dz * dz * kVoight3dFactor[9]; double const *const arho1_1d_i = &meam_c_->arho1_(i, 0); // In Eq.4.30a & 4.30b double const y1r_i = arho1_1d_i[0] * dx + arho1_1d_i[1] * dy + arho1_1d_i[2] * dz; double const *const arho2_1d_i = &meam_c_->arho2_(i, 0); // In Eq.4.30d & 4.30e ts = arho2_1d_i[0] * dxdxv; ts += arho2_1d_i[1] * dxdyv; ts += arho2_1d_i[2] * dxdzv; ts += arho2_1d_i[3] * dydyv; ts += arho2_1d_i[4] * dydzz; ts += arho2_1d_i[5] * dzdzv; double const y2rr_i = ts; double const *const arho3_1d_i = &meam_c_->arho3_(i, 0); // In Eq.4.30g & 4.30h ts = arho3_1d_i[0] * dxdxdxv; ts += arho3_1d_i[1] * dxdxdyv; ts += arho3_1d_i[2] * dxdxdzv; ts += arho3_1d_i[3] * dxdydyv; ts += arho3_1d_i[4] * dxdydzv; ts += arho3_1d_i[5] * dxdzdzv; ts += arho3_1d_i[6] * dydydyv; ts += arho3_1d_i[7] * dydydzv; ts += arho3_1d_i[8] * dydzdzv; ts += arho3_1d_i[9] * dzdzdzv; double const y3rrr_i = ts; double const *const arho3b_1d_i = &meam_c_->arho3b_(i, 0); // In Eq.4.30g & 4.30h double const w3r_i = arho3b_1d_i[0] * dx + arho3b_1d_i[1] * dy + arho3b_1d_i[2] * dz; // rho0_ terms Eq.4.26a double const drho0dr_i = drhoa0_j * sij; // rho0_ terms Eq.4.26a double const drho0dr_j = drhoa0_i * sij; // In Eq.4.30a double const c2sr = 2.0 * sij * rij_inv; // Eq.4.30a double const drho1dr_i = c2sr * (drhoa1_j - rhoa1_j * rij_inv) * y1r_i; // Eq.4.30c double const drho1drm_i0 = c2sr * rhoa1_j * arho1_1d_i[0]; double const drho1drm_i1 = c2sr * rhoa1_j * arho1_1d_i[1]; double const drho1drm_i2 = c2sr * rhoa1_j * arho1_1d_i[2]; // In Eq.4.30d double const c2sr2 = 2.0 * sij / rij2; // Eq.4.30d double const drho2dr_i = c2sr2 * (drhoa2_j - 2 * rhoa2_j * rij_inv) * y2rr_i - kTwoThird * meam_c_->arho2b_[i] * drhoa2_j * sij; // In Eq.4.30f double const c4sr2 = 4 * sij / rij2; // Eq.4.30f ts = arho2_1d_i[kVoight2dIndex[0]] * dx; // [0][0] ts += arho2_1d_i[kVoight2dIndex[1]] * dy; // [0][1] ts += arho2_1d_i[kVoight2dIndex[2]] * dz; // [0][2] double const drho2drm_i0 = ts * c4sr2 * rhoa2_j; ts = arho2_1d_i[kVoight2dIndex[3]] * dx; // [1][0] ts += arho2_1d_i[kVoight2dIndex[4]] * dy; // [1][1] ts += arho2_1d_i[kVoight2dIndex[5]] * dz; // [1][2] double const drho2drm_i1 = ts * c4sr2 * rhoa2_j; ts = arho2_1d_i[kVoight2dIndex[6]] * dx; // [2][0] ts += arho2_1d_i[kVoight2dIndex[7]] * dy; // [2][1] ts += arho2_1d_i[kVoight2dIndex[8]] * dz; // [2][2] double const drho2drm_i2 = ts * c4sr2 * rhoa2_j; // In Eq.4.30g double const rij3 = rij * rij2; double const c2sr3 = 2 * sij / rij3; double const c65sr = 6.0 / 5.0 * sij * rij_inv; // Eq.4.30g double const drho3dr_i = c2sr3 * (drhoa3_j - 3 * rhoa3_j * rij_inv) * y3rrr_i - c65sr * (drhoa3_j - rhoa3_j * rij_inv) * w3r_i; // In Eq.4.30i double const c6sr3 = 6 * sij / rij3; // Eq.4.30i ts = arho3_1d_i[kVoight3dIndex[0]] * dxdxv; // [0][0][0] ts += arho3_1d_i[kVoight3dIndex[1]] * dxdyv; // [0][0][1] ts += arho3_1d_i[kVoight3dIndex[2]] * dxdzv; // [0][0][2] ts += arho3_1d_i[kVoight3dIndex[4]] * dydyv; // [0][1][1] ts += arho3_1d_i[kVoight3dIndex[5]] * dydzz; // [0][1][2] ts += arho3_1d_i[kVoight3dIndex[8]] * dzdzv; // [0][2][2] double const drho3drm_i0 = (c6sr3 * ts - c65sr * arho3b_1d_i[0]) * rhoa3_j; ts = arho3_1d_i[kVoight3dIndex[9]] * dxdxv; // [1][0][0] ts += arho3_1d_i[kVoight3dIndex[10]] * dxdyv; // [1][0][1] ts += arho3_1d_i[kVoight3dIndex[11]] * dxdzv; // [1][0][2] ts += arho3_1d_i[kVoight3dIndex[13]] * dydyv; // [1][1][1] ts += arho3_1d_i[kVoight3dIndex[14]] * dydzz; // [1][1][2] ts += arho3_1d_i[kVoight3dIndex[17]] * dzdzv; // [1][2][2] double const drho3drm_i1 = (c6sr3 * ts - c65sr * arho3b_1d_i[1]) * rhoa3_j; ts = arho3_1d_i[kVoight3dIndex[18]] * dxdxv; // [2][0][0] ts += arho3_1d_i[kVoight3dIndex[19]] * dxdyv; // [2][0][1] ts += arho3_1d_i[kVoight3dIndex[20]] * dxdzv; // [2][0][2] ts += arho3_1d_i[kVoight3dIndex[22]] * dydyv; // [2][1][1] ts += arho3_1d_i[kVoight3dIndex[23]] * dydzz; // [2][1][2] ts += arho3_1d_i[kVoight3dIndex[26]] * dzdzv; // [2][2][2] double const drho3drm_i2 = (c6sr3 * ts - c65sr * arho3b_1d_i[2]) * rhoa3_j; double y1r_j; double y2rr_j; double y3rrr_j; double w3r_j; double drho1dr_j; double drho1drm_j0; double drho1drm_j1; double drho1drm_j2; double drho2dr_j; double drho2drm_j0; double drho2drm_j1; double drho2drm_j2; double drho3dr_j; double drho3drm_j0; double drho3drm_j1; double drho3drm_j2; if (contributing_j) { double const *const arho1_1d_j = &meam_c_->arho1_(j, 0); // In Eq.4.30a & 4.30b y1r_j = -arho1_1d_j[0] * dx; y1r_j -= arho1_1d_j[1] * dy; y1r_j -= arho1_1d_j[2] * dz; double const *const arho2_1d_j = &meam_c_->arho2_(j, 0); // In Eq.4.30d & 4.30e y2rr_j = arho2_1d_j[0] * dxdxv; y2rr_j += arho2_1d_j[1] * dxdyv; y2rr_j += arho2_1d_j[2] * dxdzv; y2rr_j += arho2_1d_j[3] * dydyv; y2rr_j += arho2_1d_j[4] * dydzz; y2rr_j += arho2_1d_j[5] * dzdzv; double const *const arho3_1d_j = &meam_c_->arho3_(j, 0); // In Eq.4.30g & 4.30h y3rrr_j = -arho3_1d_j[0] * dxdxdxv; y3rrr_j -= arho3_1d_j[1] * dxdxdyv; y3rrr_j -= arho3_1d_j[2] * dxdxdzv; y3rrr_j -= arho3_1d_j[3] * dxdydyv; y3rrr_j -= arho3_1d_j[4] * dxdydzv; y3rrr_j -= arho3_1d_j[5] * dxdzdzv; y3rrr_j -= arho3_1d_j[6] * dydydyv; y3rrr_j -= arho3_1d_j[7] * dydydzv; y3rrr_j -= arho3_1d_j[8] * dydzdzv; y3rrr_j -= arho3_1d_j[9] * dzdzdzv; double const *const arho3b_1d_j = &meam_c_->arho3b_(j, 0); // In Eq.4.30g & 4.30h w3r_j = -arho3b_1d_j[0] * dx; w3r_j -= arho3b_1d_j[1] * dy; w3r_j -= arho3b_1d_j[2] * dz; // Eq.4.30a drho1dr_j = c2sr * (drhoa1_i - rhoa1_i * rij_inv) * y1r_j; // Eq.4.30c drho1drm_j0 = -c2sr * rhoa1_i * arho1_1d_j[0]; drho1drm_j1 = -c2sr * rhoa1_i * arho1_1d_j[1]; drho1drm_j2 = -c2sr * rhoa1_i * arho1_1d_j[2]; // Eq.4.30d drho2dr_j = c2sr2 * (drhoa2_i - 2 * rhoa2_i * rij_inv) * y2rr_j - kTwoThird * meam_c_->arho2b_[j] * drhoa2_i * sij; // Eq.4.30f ts = -arho2_1d_j[kVoight2dIndex[0]] * dx; // [0][0] ts -= arho2_1d_j[kVoight2dIndex[1]] * dy; // [0][1] ts -= arho2_1d_j[kVoight2dIndex[2]] * dz; // [0][2] drho2drm_j0 = ts * (-c4sr2 * rhoa2_i); ts = -arho2_1d_j[kVoight2dIndex[3]] * dx; // [1][0] ts -= arho2_1d_j[kVoight2dIndex[4]] * dy; // [1][1] ts -= arho2_1d_j[kVoight2dIndex[5]] * dz; // [1][2] drho2drm_j1 = ts * (-c4sr2 * rhoa2_i); ts = -arho2_1d_j[kVoight2dIndex[6]] * dx; // [2][0] ts -= arho2_1d_j[kVoight2dIndex[7]] * dy; // [2][1] ts -= arho2_1d_j[kVoight2dIndex[8]] * dz; // [2][2] drho2drm_j2 = ts * (-c4sr2 * rhoa2_i); // Eq.4.30g drho3dr_j = c2sr3 * (drhoa3_i - 3 * rhoa3_i * rij_inv) * y3rrr_j - c65sr * (drhoa3_i - rhoa3_i * rij_inv) * w3r_j; // Eq.4.30i ts = arho3_1d_j[kVoight3dIndex[0]] * dxdxv; // [0][0][0] ts += arho3_1d_j[kVoight3dIndex[1]] * dxdyv; // [0][0][1] ts += arho3_1d_j[kVoight3dIndex[2]] * dxdzv; // [0][0][2] ts += arho3_1d_j[kVoight3dIndex[4]] * dydyv; // [0][1][1] ts += arho3_1d_j[kVoight3dIndex[5]] * dydzz; // [0][1][2] ts += arho3_1d_j[kVoight3dIndex[8]] * dzdzv; // [0][2][2] drho3drm_j0 = (-c6sr3 * ts + c65sr * arho3b_1d_j[0]) * rhoa3_i; ts = arho3_1d_j[kVoight3dIndex[9]] * dxdxv; // [1][0][0] ts += arho3_1d_j[kVoight3dIndex[10]] * dxdyv; // [1][0][1] ts += arho3_1d_j[kVoight3dIndex[11]] * dxdzv; // [1][0][2] ts += arho3_1d_j[kVoight3dIndex[13]] * dydyv; // [1][1][1] ts += arho3_1d_j[kVoight3dIndex[14]] * dydzz; // [1][1][2] ts += arho3_1d_j[kVoight3dIndex[17]] * dzdzv; // [1][2][2] drho3drm_j1 = (-c6sr3 * ts + c65sr * arho3b_1d_j[1]) * rhoa3_i; ts = arho3_1d_j[kVoight3dIndex[18]] * dxdxv; // [2][0][0] ts += arho3_1d_j[kVoight3dIndex[19]] * dxdyv; // [2][0][1] ts += arho3_1d_j[kVoight3dIndex[20]] * dxdzv; // [2][0][2] ts += arho3_1d_j[kVoight3dIndex[22]] * dydyv; // [2][1][1] ts += arho3_1d_j[kVoight3dIndex[23]] * dydzz; // [2][1][2] ts += arho3_1d_j[kVoight3dIndex[26]] * dzdzv; // [2][2][2] drho3drm_j2 = (-c6sr3 * ts + c65sr * arho3b_1d_j[2]) * rhoa3_i; } // particle_contributing // Compute derivatives of weighting functions t wrt rij double const t1m_i = meam_c_->element_t1_[species_i]; double const t1m_j = meam_c_->element_t1_[species_j]; double const t2m_i = meam_c_->element_t2_[species_i]; double const t2m_j = meam_c_->element_t2_[species_j]; double const t3m_i = meam_c_->element_t3_[species_i]; double const t3m_j = meam_c_->element_t3_[species_j]; double const t1_i = meam_c_->t_ave_(i, 0); double const t1_j = meam_c_->t_ave_(j, 0); double const t2_i = meam_c_->t_ave_(i, 1); double const t2_j = meam_c_->t_ave_(j, 1); double const t3_i = meam_c_->t_ave_(i, 2); double const t3_j = meam_c_->t_ave_(j, 2); double dt1dr_i; double dt2dr_i; double dt3dr_i; if (meam_c_->ialloy_ == 1) { double const *const tsq_ave_1d_i = &meam_c_->tsq_ave_(i, 0); double const a1i = special::FloatDivZero(drhoa0_j * sij, tsq_ave_1d_i[0]); double const a2i = special::FloatDivZero(drhoa0_j * sij, tsq_ave_1d_i[1]); double const a3i = special::FloatDivZero(drhoa0_j * sij, tsq_ave_1d_i[2]); dt1dr_i = a1i * (t1m_j - t1_i * special::Square(t1m_j)); dt2dr_i = a2i * (t2m_j - t2_i * special::Square(t2m_j)); dt3dr_i = a3i * (t3m_j - t3_i * special::Square(t3m_j)); } else if (meam_c_->ialloy_ == 2) { dt1dr_i = 0.0; dt2dr_i = 0.0; dt3dr_i = 0.0; } else { double const ai = special::FloatDivZero(drhoa0_j * sij, meam_c_->rho0_[i]); dt1dr_i = ai * (t1m_j - t1_i); dt2dr_i = ai * (t2m_j - t2_i); dt3dr_i = ai * (t3m_j - t3_i); } // Compute derivatives of total density wrt rij, sij and rij(3) VectorOfSizeDIM shape_factors_i; meam_c_->GetShapeFactors( meam_c_->element_lattice_(species_i, species_i), meam_c_->element_stheta_(species_i, species_i), meam_c_->element_ctheta_(species_i, species_i), shape_factors_i); // Eq.4.36a double const drhodr_i = meam_c_->dgamma1_[i] * drho0dr_i + meam_c_->dgamma2_[i] * (dt1dr_i * meam_c_->rho1_[i] + t1_i * drho1dr_i + dt2dr_i * meam_c_->rho2_[i] + t2_i * drho2dr_i + dt3dr_i * meam_c_->rho3_[i] + t3_i * drho3dr_i) - meam_c_->dgamma3_[i] * (shape_factors_i[0] * dt1dr_i + shape_factors_i[1] * dt2dr_i + shape_factors_i[2] * dt3dr_i); // Eq.4.36c double const drhodrm_i0 = meam_c_->dgamma2_[i] * (t1_i * drho1drm_i0 + t2_i * drho2drm_i0 + t3_i * drho3drm_i0); double const drhodrm_i1 = meam_c_->dgamma2_[i] * (t1_i * drho1drm_i1 + t2_i * drho2drm_i1 + t3_i * drho3drm_i1); double const drhodrm_i2 = meam_c_->dgamma2_[i] * (t1_i * drho1drm_i2 + t2_i * drho2drm_i2 + t3_i * drho3drm_i2); VectorOfSizeDIM shape_factors_j; double drhodr_j{}; double drhodrm_j0{}; double drhodrm_j1{}; double drhodrm_j2{}; if (contributing_j) { double dt1dr_j; double dt2dr_j; double dt3dr_j; if (meam_c_->ialloy_ == 1) { double const *const tsq_ave_1d_j = &meam_c_->tsq_ave_(j, 0); double const a1j = special::FloatDivZero(drhoa0_i * sij, tsq_ave_1d_j[0]); double const a2j = special::FloatDivZero(drhoa0_i * sij, tsq_ave_1d_j[1]); double const a3j = special::FloatDivZero(drhoa0_i * sij, tsq_ave_1d_j[2]); dt1dr_j = a1j * t1m_i * (1.0 - t1_j * t1m_i); dt2dr_j = a2j * t2m_i * (1.0 - t2_j * t2m_i); dt3dr_j = a3j * t3m_i * (1.0 - t3_j * t3m_i); } else if (meam_c_->ialloy_ == 2) { dt1dr_j = 0.0; dt2dr_j = 0.0; dt3dr_j = 0.0; } else { double const aj = special::FloatDivZero(drhoa0_i * sij, meam_c_->rho0_[j]); dt1dr_j = aj * (t1m_i - t1_j); dt2dr_j = aj * (t2m_i - t2_j); dt3dr_j = aj * (t3m_i - t3_j); } meam_c_->GetShapeFactors( meam_c_->element_lattice_(species_j, species_j), meam_c_->element_stheta_(species_i, species_i), meam_c_->element_ctheta_(species_i, species_i), shape_factors_j); // Eq.4.36a drhodr_j = meam_c_->dgamma1_[j] * drho0dr_j + meam_c_->dgamma2_[j] * (dt1dr_j * meam_c_->rho1_[j] + t1_j * drho1dr_j + dt2dr_j * meam_c_->rho2_[j] + t2_j * drho2dr_j + dt3dr_j * meam_c_->rho3_[j] + t3_j * drho3dr_j) - meam_c_->dgamma3_[j] * (shape_factors_j[0] * dt1dr_j + shape_factors_j[1] * dt2dr_j + shape_factors_j[2] * dt3dr_j); // Eq.4.36c drhodrm_j0 = meam_c_->dgamma2_[j] * (t1_j * drho1drm_j0 + t2_j * drho2drm_j0 + t3_j * drho3drm_j0); drhodrm_j1 = meam_c_->dgamma2_[j] * (t1_j * drho1drm_j1 + t2_j * drho2drm_j1 + t3_j * drho3drm_j1); drhodrm_j2 = meam_c_->dgamma2_[j] * (t1_j * drho1drm_j2 + t2_j * drho2drm_j2 + t3_j * drho3drm_j2); } // particle_contributing double dphi; double const phi = meam_c_->GetPhiAndDerivative(species_i, species_j, rij, dphi); if (contributing_j) { // Contribute pair term to Energy if (IsComputeEnergy) { *energy += phi * sij; } // Contribute pair term to Particle Energy if (IsComputeParticleEnergy) { particle_energy[i] += 0.5 * phi * sij; particle_energy[j] += 0.5 * phi * sij; } } // particle j is not contributing else { // Contribute pair term to Energy if (IsComputeEnergy) { *energy += 0.5 * phi * sij; } // Contribute pair term to Particle Energy if (IsComputeParticleEnergy) { particle_energy[i] += 0.5 * phi * sij; } } // Derivative of the screening function double const dsij = dscrfcn[nth_neighbor_of_i]; double dEdsij = 0.0; // Compute derivatives wrt sij, but only if necessary if (special::IsNotZero(dsij)) { // Eq.4.26b double const drho0ds_i = rhoa0_j; // Eq.4.26b double const drho0ds_j = rhoa0_i; // in Eq.4.30b double const c2r = 2.0 * rij_inv; // Eq.4.30b double const drho1ds_i = c2r * rhoa1_j * y1r_i; // in Eq.4.30e double const c2r2 = 2.0 / rij2; // Eq.4.30e double const drho2ds_i = c2r2 * rhoa2_j * y2rr_i - kTwoThird * meam_c_->arho2b_[i] * rhoa2_j; // in Eq.4.30h double const c2r3 = 2.0 / rij3; double const c65r = 6.0 / (5.0 * rij); // Eq.4.30h double const drho3ds_i = c2r3 * rhoa3_j * y3rrr_i - c65r * rhoa3_j * w3r_i; double dt1ds_i; double dt2ds_i; double dt3ds_i; if (meam_c_->ialloy_ == 1) { double const *const tsq_ave_1d_i = &meam_c_->tsq_ave_(i, 0); double const a1i = special::FloatDivZero(rhoa0_j, tsq_ave_1d_i[0]); double const a2i = special::FloatDivZero(rhoa0_j, tsq_ave_1d_i[1]); double const a3i = special::FloatDivZero(rhoa0_j, tsq_ave_1d_i[2]); dt1ds_i = a1i * t1m_j * (1.0 - t1_i * t1m_j); dt2ds_i = a2i * t2m_j * (1.0 - t2_i * t2m_j); dt3ds_i = a3i * t3m_j * (1.0 - t3_i * t3m_j); } else if (meam_c_->ialloy_ == 2) { dt1ds_i = 0.0; dt2ds_i = 0.0; dt3ds_i = 0.0; } else { double const ai = special::FloatDivZero(rhoa0_j, meam_c_->rho0_[i]); dt1ds_i = ai * (t1m_j - t1_i); dt2ds_i = ai * (t2m_j - t2_i); dt3ds_i = ai * (t3m_j - t3_i); } // Eq.4.36b double const drhods_i = meam_c_->dgamma1_[i] * drho0ds_i + meam_c_->dgamma2_[i] * (dt1ds_i * meam_c_->rho1_[i] + t1_i * drho1ds_i + dt2ds_i * meam_c_->rho2_[i] + t2_i * drho2ds_i + dt3ds_i * meam_c_->rho3_[i] + t3_i * drho3ds_i) - meam_c_->dgamma3_[i] * (shape_factors_i[0] * dt1ds_i + shape_factors_i[1] * dt2ds_i + shape_factors_i[2] * dt3ds_i); if (contributing_j) { // Eq.4.30b double const drho1ds_j = c2r * rhoa1_i * y1r_j; // Eq.4.30e double const drho2ds_j = c2r2 * rhoa2_i * y2rr_j - kTwoThird * meam_c_->arho2b_[j] * rhoa2_i; // Eq.4.30h double const drho3ds_j = c2r3 * rhoa3_i * y3rrr_j - c65r * rhoa3_i * w3r_j; double dt1ds_j; double dt2ds_j; double dt3ds_j; if (meam_c_->ialloy_ == 1) { double const *const tsq_ave_1d_j = &meam_c_->tsq_ave_(j, 0); double const a1j = special::FloatDivZero(rhoa0_i, tsq_ave_1d_j[0]); double const a2j = special::FloatDivZero(rhoa0_i, tsq_ave_1d_j[1]); double const a3j = special::FloatDivZero(rhoa0_i, tsq_ave_1d_j[2]); dt1ds_j = a1j * t1m_i * (1.0 - t1_j * t1m_i); dt2ds_j = a2j * t2m_i * (1.0 - t2_j * t2m_i); dt3ds_j = a3j * t3m_i * (1.0 - t3_j * t3m_i); } else if (meam_c_->ialloy_ == 2) { dt1ds_j = 0.0; dt2ds_j = 0.0; dt3ds_j = 0.0; } else { double const aj = special::FloatDivZero(rhoa0_i, meam_c_->rho0_[j]); dt1ds_j = aj * (t1m_i - t1_j); dt2ds_j = aj * (t2m_i - t2_j); dt3ds_j = aj * (t3m_i - t3_j); } // Eq.4.36b double const drhods_j = meam_c_->dgamma1_[j] * drho0ds_j + meam_c_->dgamma2_[j] * (dt1ds_j * meam_c_->rho1_[j] + t1_j * drho1ds_j + dt2ds_j * meam_c_->rho2_[j] + t2_j * drho2ds_j + dt3ds_j * meam_c_->rho3_[j] + t3_j * drho3ds_j) - meam_c_->dgamma3_[j] * (shape_factors_j[0] * dt1ds_j + shape_factors_j[1] * dt2ds_j + shape_factors_j[2] * dt3ds_j); // Eq.4.41b dEdsij = phi + meam_c_->frhop_[i] * drhods_i + meam_c_->frhop_[j] * drhods_j; } // particle j is not contributing else { // Eq.4.41b dEdsij = 0.5 * phi + meam_c_->frhop_[i] * drhods_i; } // particle_contributing } // dscrfcn is not zero if (IsComputeForces || IsComputeVirial || IsComputeParticleVirial) { // Compute derivatives of energy wrt rij, sij and rij[3] // Eq.4.41a double dEdrij; // Eq.4.41d double dEdrijm0; double dEdrijm1; double dEdrijm2; if (contributing_j) { // Eq.4.41a dEdrij = dphi * sij + meam_c_->frhop_[i] * drhodr_i + meam_c_->frhop_[j] * drhodr_j; // Eq.4.41d dEdrijm0 = meam_c_->frhop_[i] * drhodrm_i0 + meam_c_->frhop_[j] * drhodrm_j0; dEdrijm1 = meam_c_->frhop_[i] * drhodrm_i1 + meam_c_->frhop_[j] * drhodrm_j1; dEdrijm2 = meam_c_->frhop_[i] * drhodrm_i2 + meam_c_->frhop_[j] * drhodrm_j2; } // particle j is not contributing else { // Eq.4.41a dEdrij = 0.5 * dphi * sij + meam_c_->frhop_[i] * drhodr_i; // Eq.4.41d dEdrijm0 = meam_c_->frhop_[i] * drhodrm_i0; dEdrijm1 = meam_c_->frhop_[i] * drhodrm_i1; dEdrijm2 = meam_c_->frhop_[i] * drhodrm_i2; } // Add the part of the force due to dEdrij and dEdsij double const force = dEdrij * rij_inv + dEdsij * dsij; double const fi0 = dx * force + dEdrijm0; double const fi1 = dy * force + dEdrijm1; double const fi2 = dz * force + dEdrijm2; if (IsComputeForces) { forces[i][0] += fi0; forces[i][1] += fi1; forces[i][2] += fi2; forces[j][0] -= fi0; forces[j][1] -= fi1; forces[j][2] -= fi2; } 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] = dx * fi0; v[1] = dy * fi1; v[2] = dz * fi2; v[3] = 0.5 * (dy * fi2 + dz * fi1); v[4] = 0.5 * (dx * fi2 + dz * fi0); v[5] = 0.5 * (dx * fi1 + dy * fi0); 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; particle_virial[i][0] += v[0]; particle_virial[i][1] += v[1]; particle_virial[i][2] += v[2]; particle_virial[i][3] += v[3]; particle_virial[i][4] += v[4]; particle_virial[i][5] += v[5]; particle_virial[j][0] += v[0]; particle_virial[j][1] += v[1]; particle_virial[j][2] += v[2]; particle_virial[j][3] += v[3]; particle_virial[j][4] += v[4]; particle_virial[j][5] += v[5]; } // IsComputeParticleVirial } // IsComputeVirial || IsComputeParticleVirial // Now compute forces on other atoms k due to the change in sij if (special::IsZero(sij) || special::IsOne(sij)) { // Loop over j continue; } double dx_jk; double dy_jk; double dz_jk; double dx_ik; double dy_ik; double dz_ik; // Setup loop over neighbors of current particle for (int kn = 0; kn < number_of_neighbors; ++kn) { int const k = neighbors_of_particle[kn]; if (k == j) { continue; } double dsij1 = 0.0; double dsij2 = 0.0; if (special::IsNotZero(sij) && special::IsNotOne(sij)) { double const rbound = meam_c_->element_ebound_(species_i, species_j) * rij2; double const xk = coordinates[k][0]; double const yk = coordinates[k][1]; double const zk = coordinates[k][2]; dx_jk = xk - xj; dy_jk = yk - yj; dz_jk = zk - zj; double const rjk2 = dx_jk * dx_jk + dy_jk * dy_jk + dz_jk * dz_jk; // lies within the screening function ellipse, // so we need to calculate its effects if (rjk2 <= rbound) { dx_ik = xk - xi; dy_ik = yk - yi; dz_ik = zk - zi; double const rik2 = dx_ik * dx_ik + dy_ik * dy_ik + dz_ik * dz_ik; // lies within the screening function ellipse, // so we need to calculate its effects if (rik2 <= rbound) { double const xik = rik2 / rij2; double const xjk = rjk2 / rij2; double a = 1 - (xik - xjk) * (xik - xjk); if (special::IsNotZero(a)) { double cijk = (2.0 * (xik + xjk) + a - 2.0) / a; int const species_k = particle_species_codes[k]; double const cmax = meam_c_->element_Cmax_( species_i, species_j, species_k); double const cmin = meam_c_->element_Cmin_( species_i, species_j, species_k); if (cijk >= cmin && cijk <= cmax) { double const delc = cmax - cmin; cijk = (cijk - cmin) / delc; double dfc_drij; double const sijk = meam_c_->RadialCutoff( cijk, dfc_drij, meam_c_->fcut_function_form_); // Eq.4.17b double dcijk_drik; // Eq.4.17c double dcijk_drjk; meam_c_->DCijkDRikDRjk(rij2, rik2, rjk2, dcijk_drik, dcijk_drjk); a = sij / delc * dfc_drij / sijk; // Eq.4.22b dsij1 = a * dcijk_drik; // Eq.4.22c dsij2 = a * dcijk_drjk; } // cijk >= cmin && cijk <= cmax } // IsNotZero(a) } // rik2 <= rbound } // rjk2 <= rbound } // IsNotZero(sij) && IsNotOne(sij) if (special::IsNotZero(dsij1) || special::IsNotZero(dsij2)) { // In Eq.4.40 double const force1 = dEdsij * dsij1; double const fi0 = force1 * dx_ik; double const fi1 = force1 * dy_ik; double const fi2 = force1 * dz_ik; // In Eq.4.40 double const force2 = dEdsij * dsij2; double const fj0 = force2 * dx_jk; double const fj1 = force2 * dy_jk; double const fj2 = force2 * dz_jk; if (IsComputeForces) { forces[i][0] += fi0; forces[i][1] += fi1; forces[i][2] += fi2; forces[j][0] += fj0; forces[j][1] += fj1; forces[j][2] += fj2; forces[k][0] -= (fi0 + fj0); forces[k][1] -= (fi1 + fj1); forces[k][2] -= (fi2 + fj2); } if (IsComputeVirial || IsComputeParticleVirial) { // Tabulate per-atom virial as symmetrized stress // tensor // 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] = kTwoThird * (dx_ik * fi0 + dx_jk * fj0); v[1] = kTwoThird * (dy_ik * fi1 + dy_jk * fj1); v[2] = kTwoThird * (dz_ik * fi2 + dz_jk * fj2); v[3] = kThird * (dy_ik * fi2 + dy_jk * fj2 + dz_ik * fi1 + dz_jk * fj1); v[4] = kThird * (dx_ik * fi2 + dx_jk * fj2 + dz_ik * fi0 + dz_jk * fj0); v[5] = kThird * (dx_ik * fi1 + dx_jk * fj1 + dy_ik * fi0 + dy_jk * fj0); 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]; } // IsComputeVirial 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; particle_virial[i][0] += v[0]; particle_virial[i][1] += v[1]; particle_virial[i][2] += v[2]; particle_virial[i][3] += v[3]; particle_virial[i][4] += v[4]; particle_virial[i][5] += v[5]; particle_virial[j][0] += v[0]; particle_virial[j][1] += v[1]; particle_virial[j][2] += v[2]; particle_virial[j][3] += v[3]; particle_virial[j][4] += v[4]; particle_virial[j][5] += v[5]; particle_virial[k][0] += v[0]; particle_virial[k][1] += v[1]; particle_virial[k][2] += v[2]; particle_virial[k][3] += v[3]; particle_virial[k][4] += v[4]; particle_virial[k][5] += v[5]; } // IsComputeParticleVirial } // IsComputeVirial || IsComputeParticleVirial } // IsNotZero(dsij1) || IsNotZero(dsij2) } // Loop over k } // IsComputeForces || IsComputeVirial || // IsComputeParticleVirial } // rij2 < max_cutoff_squared_ } // scrfcn is not zero } // Effective half list } // Loop over j if (nth_neighbor_of_i > -1) { offset += nth_neighbor_of_i + 1; } } // Loop over i // everything is good return false; } #define KIM_LOGGER_OBJECT_NAME model_compute_arguments template int MEAMImplementation::MeamSplineCompute( KIM::ModelCompute const *const, KIM::ModelComputeArguments const *const model_compute_arguments, int const *const particle_species_codes, int const *const particle_contributing, const VectorOfSizeDIM *const coordinates, double *const energy, VectorOfSizeDIM *const forces, double *const particle_energy, VectorOfSizeSix virial, VectorOfSizeSix *const particle_virial) const { // Everything is good int ier = false; // Nothing to compute if (!IsComputeEnergy && !IsComputeForces && !IsComputeParticleEnergy && !IsComputeVirial && !IsComputeParticleVirial) { // Everything is good return ier; } // Initialize energy if (IsComputeEnergy) { *energy = 0.0; } // Initialize forces if (IsComputeForces) { for (int i = 0; i < cached_number_of_particles_; ++i) { forces[i][0] = 0.0; forces[i][1] = 0.0; forces[i][2] = 0.0; } } // Initialize particle energy if (IsComputeParticleEnergy) { for (int i = 0; i < cached_number_of_particles_; ++i) { particle_energy[i] = 0.0; } } // Initialize virial if (IsComputeVirial) { virial[0] = 0.0; virial[1] = 0.0; virial[2] = 0.0; virial[3] = 0.0; virial[4] = 0.0; virial[5] = 0.0; } // Initialize particle virial if (IsComputeParticleVirial) { for (int i = 0; i < cached_number_of_particles_; ++i) { particle_virial[i][0] = 0.0; particle_virial[i][1] = 0.0; particle_virial[i][2] = 0.0; particle_virial[i][3] = 0.0; particle_virial[i][4] = 0.0; particle_virial[i][5] = 0.0; } } // Grow per-atom array if necessary meam_spline_->ResizeLocalArray(cached_number_of_particles_); // Determine the maximum number of neighbors a single atom has int const max_number_of_neighbors = MaxNumberOfNeighbors(model_compute_arguments, particle_contributing); // Temporary array std::vector two_body(max_number_of_neighbors); int number_of_neighbors; int const *neighbors_of_particle; // Temporary variable double prime; // Sum three-body contributions to charge density and // the embedding energy for (int i = 0; i < cached_number_of_particles_; ++i) { if (!particle_contributing[i]) { continue; } // Get the neighbors model_compute_arguments->GetNeighborList(0, i, &number_of_neighbors, &neighbors_of_particle); int const species_i = particle_species_codes[i]; double const xi = coordinates[i][0]; double const yi = coordinates[i][1]; double const zi = coordinates[i][2]; // Compute number_bonds & charge density int number_bonds = 0; double rho_value = 0.0; // Setup loop over neighbors of the current particle auto bond_j = two_body.begin(); for (int jn = 0; jn < number_of_neighbors; ++jn) { int const j = neighbors_of_particle[jn]; double const dx = coordinates[j][0] - xi; double const dy = coordinates[j][1] - yi; double const dz = coordinates[j][2] - zi; double const rij2 = dx * dx + dy * dy + dz * dz; if (rij2 < max_cutoff_squared_) { double const rij = std::sqrt(rij2); int const species_j = particle_species_codes[j]; bond_j->index = j; bond_j->species = species_j; bond_j->r = rij; bond_j->f = meam_spline_->rho_f_[species_j].Eval( rij, prime); bond_j->f_prime = prime; bond_j->dx = dx / rij; bond_j->dy = dy / rij; bond_j->dz = dz / rij; int const species_ind = species_j * number_of_elements_ - species_j * (species_j + 1) / 2; double partial_sum = 0.0; auto bond_k = two_body.begin(); for (int kb = 0; kb < number_bonds; ++kb, ++bond_k) { int const ind = bond_k->species + species_ind; double const cos_theta = bond_j->dx * bond_k->dx + bond_j->dy * bond_k->dy + bond_j->dz * bond_k->dz; double const rho_g = meam_spline_->rho_g_[ind].Eval(cos_theta); partial_sum += bond_k->f * rho_g; } rho_value += bond_j->f * partial_sum; double const rho_r = meam_spline_->rho_r_[species_j].Eval(rij); rho_value += rho_r; ++number_bonds; ++bond_j; } // rij2 < max_cutoff_squared_ } // Loop over j // Compute embedding energy and its derivative double e_u_prime; double const e_u = meam_spline_->e_u_[species_i].Eval( rho_value, e_u_prime) - meam_spline_->zero_atom_energies_[species_i]; meam_spline_->e_u_prime_[i] = e_u_prime; if (IsComputeEnergy) { *energy += e_u; } if (IsComputeParticleEnergy) { particle_energy[i] += e_u; } // Compute three-body contributions to force VectorOfSizeDIM f_i = {}; bond_j = two_body.begin(); for (int jb = 0; jb < number_bonds; ++jb, ++bond_j) { int const j = bond_j->index; int const species_j = bond_j->species; double const r_ij = bond_j->r; double const f_ij = bond_j->f; double const f_prime_ij = bond_j->f_prime; int const species_ind = species_j * number_of_elements_ - species_j * (species_j + 1) / 2; VectorOfSizeDIM f_j = {}; auto bond_k = two_body.begin(); for (int kb = 0; kb < jb; ++kb, ++bond_k) { int const k = bond_k->index; double const r_ik = bond_k->r; double const f_ik = bond_k->f; double const f_prime_ik = bond_k->f_prime; int const ind = bond_k->species + species_ind; double const cos_theta = bond_j->dx * bond_k->dx + bond_j->dy * bond_k->dy + bond_j->dz * bond_k->dz; double const rho_g = meam_spline_->rho_g_[ind].Eval(cos_theta, prime); double const prefactor = e_u_prime * f_ij * prime * f_ik; double const prefactor_ij = prefactor / r_ij; double const prefactor_ik = prefactor / r_ik; double const fij = -e_u_prime * rho_g * f_prime_ij * f_ik + prefactor_ij * cos_theta; double const fj0 = bond_j->dx * fij - bond_k->dx * prefactor_ij; double const fj1 = bond_j->dy * fij - bond_k->dy * prefactor_ij; double const fj2 = bond_j->dz * fij - bond_k->dz * prefactor_ij; f_j[0] += fj0; f_j[1] += fj1; f_j[2] += fj2; double const fik = -e_u_prime * rho_g * f_prime_ik * f_ij + prefactor_ik * cos_theta; double const fk0 = bond_k->dx * fik - bond_j->dx * prefactor_ik; double const fk1 = bond_k->dy * fik - bond_j->dy * prefactor_ik; double const fk2 = bond_k->dz * fik - bond_j->dz * prefactor_ik; f_i[0] -= fk0; f_i[1] -= fk1; f_i[2] -= fk2; if (IsComputeForces) { forces[k][0] += fk0; forces[k][1] += fk1; forces[k][2] += fk2; } if (IsComputeVirial || IsComputeParticleVirial) { // Tabulate per-atom virial as symmetrized stress // tensor // Virial has 6 components and is stored as a // 6 - element vector in the following order: // xx, yy, zz, yz, xz, xy. double const dx_ij = bond_j->dx * r_ij; double const dy_ij = bond_j->dy * r_ij; double const dz_ij = bond_j->dz * r_ij; double const dx_ik = bond_k->dx * r_ik; double const dy_ik = bond_k->dy * r_ik; double const dz_ik = bond_k->dz * r_ik; VectorOfSizeSix v; v[0] = dx_ij * fj0 + dx_ik * fk0; v[1] = dy_ij * fj1 + dy_ik * fk1; v[2] = dz_ij * fj2 + dz_ik * fk2; v[3] = dy_ij * fj2 + dy_ik * fk2; v[4] = dx_ij * fj2 + dx_ik * fk2; v[5] = dx_ij * fj1 + dx_ik * fk1; 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]; } // IsComputeVirial if (IsComputeParticleVirial) { v[0] *= kThird; v[1] *= kThird; v[2] *= kThird; v[3] *= kThird; v[4] *= kThird; v[5] *= kThird; particle_virial[i][0] += v[0]; particle_virial[i][1] += v[1]; particle_virial[i][2] += v[2]; particle_virial[i][3] += v[3]; particle_virial[i][4] += v[4]; particle_virial[i][5] += v[5]; particle_virial[j][0] += v[0]; particle_virial[j][1] += v[1]; particle_virial[j][2] += v[2]; particle_virial[j][3] += v[3]; particle_virial[j][4] += v[4]; particle_virial[j][5] += v[5]; particle_virial[k][0] += v[0]; particle_virial[k][1] += v[1]; particle_virial[k][2] += v[2]; particle_virial[k][3] += v[3]; particle_virial[k][4] += v[4]; particle_virial[k][5] += v[5]; } // IsComputeParticleVirial } // IsComputeVirial || IsComputeParticleVirial } // Loop over k bonds if (IsComputeForces) { forces[i][0] -= f_j[0]; forces[i][1] -= f_j[1]; forces[i][2] -= f_j[2]; forces[j][0] += f_j[0]; forces[j][1] += f_j[1]; forces[j][2] += f_j[2]; } } // Loop over j bonds if (IsComputeForces) { forces[i][0] += f_i[0]; forces[i][1] += f_i[1]; forces[i][2] += f_i[2]; } } // Loop over i // Compute two-body pair interactions for (int i = 0; i < cached_number_of_particles_; ++i) { if (!particle_contributing[i]) { continue; } // Get the neighbors model_compute_arguments->GetNeighborList(0, i, &number_of_neighbors, &neighbors_of_particle); int const species_i = particle_species_codes[i]; int const species_ind = species_i * number_of_elements_ - species_i * (species_i + 1) / 2; double const xi = coordinates[i][0]; double const yi = coordinates[i][1]; double const zi = coordinates[i][2]; // Setup loop over neighbors of current particle for (int jn = 0; jn < number_of_neighbors; ++jn) { int const j = neighbors_of_particle[jn]; int const contributing_j = particle_contributing[j]; // Effective half list if (!(contributing_j && j < i)) { double const dx = coordinates[j][0] - xi; double const dy = coordinates[j][1] - yi; double const dz = coordinates[j][2] - zi; double const rij2 = dx * dx + dy * dy + dz * dz; if (rij2 < max_cutoff_squared_) { double const rij = std::sqrt(rij2); int const species_j = particle_species_codes[j]; int const ind = species_j + species_ind; double fij; double const phi = meam_spline_->e_phi_[ind].Eval(rij, fij); meam_spline_->rho_r_[species_j].Eval(rij, prime); if (contributing_j) { fij += prime * meam_spline_->e_u_prime_[i]; if (species_i != species_j) { meam_spline_->rho_r_[species_i].Eval(rij, prime); } fij += prime * meam_spline_->e_u_prime_[j]; // Contribute pair term to Energy if (IsComputeEnergy) { *energy += phi; } // Contribute pair term to Particle Energy if (IsComputeParticleEnergy) { particle_energy[i] += 0.5 * phi; particle_energy[j] += 0.5 * phi; } } // particle j is not contributing else { fij = 0.5 * fij + prime * meam_spline_->e_u_prime_[i]; // Contribute pair term to Energy if (IsComputeEnergy) { *energy += 0.5 * phi; } // Contribute pair term to Particle Energy if (IsComputeParticleEnergy) { particle_energy[i] += 0.5 * phi; } } // Divide by r_ij to get forces from gradient fij /= rij; if (IsComputeForces) { forces[i][0] += dx * fij; forces[i][1] += dy * fij; forces[i][2] += dz * fij; forces[j][0] -= dx * fij; forces[j][1] -= dy * fij; forces[j][2] -= dz * fij; } // IsComputeForces if (IsComputeVirial || IsComputeParticleVirial) { // Tabulate per-atom virial as symmetrized stress // tensor // 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] = dx * dx * fij; v[1] = dy * dy * fij; v[2] = dz * dz * fij; v[3] = dy * dz * fij; v[4] = dx * dz * fij; v[5] = dx * dy * fij; 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]; } // IsComputeVirial 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; particle_virial[i][0] += v[0]; particle_virial[i][1] += v[1]; particle_virial[i][2] += v[2]; particle_virial[i][3] += v[3]; particle_virial[i][4] += v[4]; particle_virial[i][5] += v[5]; particle_virial[j][0] += v[0]; particle_virial[j][1] += v[1]; particle_virial[j][2] += v[2]; particle_virial[j][3] += v[3]; particle_virial[j][4] += v[4]; particle_virial[j][5] += v[5]; } // IsComputeParticleVirial } // IsComputeVirial || IsComputeParticleVirial } // rij2 < max_cutoff_squared_ } // Effective half list } // Loop over j } // Loop over i // everything is good return false; } #define KIM_LOGGER_OBJECT_NAME model_compute_arguments template int MEAMImplementation::MeamSWSplineCompute( KIM::ModelCompute const *const, KIM::ModelComputeArguments const *const model_compute_arguments, int const *const, int const *const particle_contributing, const VectorOfSizeDIM *const coordinates, double *const energy, VectorOfSizeDIM *const forces, double *const particle_energy, VectorOfSizeSix virial, VectorOfSizeSix *const particle_virial) const { // Everything is good int ier = false; // Nothing to compute if (!IsComputeEnergy && !IsComputeForces && !IsComputeParticleEnergy && !IsComputeVirial && !IsComputeParticleVirial) { // Everything is good return ier; } // Initialize energy if (IsComputeEnergy) { *energy = 0.0; } // Initialize forces if (IsComputeForces) { for (int i = 0; i < cached_number_of_particles_; ++i) { forces[i][0] = 0.0; forces[i][1] = 0.0; forces[i][2] = 0.0; } } // Initialize particle energy if (IsComputeParticleEnergy) { for (int i = 0; i < cached_number_of_particles_; ++i) { particle_energy[i] = 0.0; } } // Initialize virial if (IsComputeVirial) { virial[0] = 0.0; virial[1] = 0.0; virial[2] = 0.0; virial[3] = 0.0; virial[4] = 0.0; virial[5] = 0.0; } // Initialize particle virial if (IsComputeParticleVirial) { for (int i = 0; i < cached_number_of_particles_; ++i) { particle_virial[i][0] = 0.0; particle_virial[i][1] = 0.0; particle_virial[i][2] = 0.0; particle_virial[i][3] = 0.0; particle_virial[i][4] = 0.0; particle_virial[i][5] = 0.0; } } // Grow per-atom array if necessary meam_sw_spline_->ResizeLocalArray(cached_number_of_particles_); // Determine the maximum number of neighbors a single atom has int const max_number_of_neighbors = MaxNumberOfNeighbors(model_compute_arguments, particle_contributing); // Temporary array std::vector two_body(max_number_of_neighbors); int number_of_neighbors; int const *neighbors_of_particle; // Temporary variable double prime; // Sum three-body contributions to charge density and // the embedding energy for (int i = 0; i < cached_number_of_particles_; ++i) { if (!particle_contributing[i]) { continue; } // Get the neighbors model_compute_arguments->GetNeighborList(0, i, &number_of_neighbors, &neighbors_of_particle); double const xi = coordinates[i][0]; double const yi = coordinates[i][1]; double const zi = coordinates[i][2]; // Compute number_bonds & charge density int number_bonds = 0; double rho_value = 0.0; double rho_sw_value = 0.0; // Setup loop over neighbors of the current particle auto bond_j = two_body.begin(); for (int jn = 0; jn < number_of_neighbors; ++jn) { int const j = neighbors_of_particle[jn]; double const dx = coordinates[j][0] - xi; double const dy = coordinates[j][1] - yi; double const dz = coordinates[j][2] - zi; double const rij2 = dx * dx + dy * dy + dz * dz; if (rij2 < max_cutoff_squared_) { double const rij = std::sqrt(rij2); bond_j->index = j; bond_j->r = rij; bond_j->f = meam_sw_spline_->rho_f_.Eval(rij, prime); bond_j->f_prime = prime; bond_j->sw_f = meam_sw_spline_->esw_f_.Eval(rij, prime); bond_j->sw_f_prime = prime; bond_j->dx = dx / rij; bond_j->dy = dy / rij; bond_j->dz = dz / rij; double partial_sum = 0.0; double partial_sum2 = 0.0; auto bond_k = two_body.begin(); for (int kb = 0; kb < number_bonds; ++kb, ++bond_k) { double const cos_theta = (bond_j->dx * bond_k->dx + bond_j->dy * bond_k->dy + bond_j->dz * bond_k->dz); double const rho_g = meam_sw_spline_->rho_g_.Eval(cos_theta); partial_sum += bond_k->f * rho_g; double const sw_g = meam_sw_spline_->esw_g_.Eval(cos_theta); partial_sum2 += bond_k->sw_f * sw_g; } rho_value += bond_j->f * partial_sum; rho_sw_value += bond_j->sw_f * partial_sum2; double const rho_r = meam_sw_spline_->rho_r_.Eval(rij); rho_value += rho_r; ++number_bonds; ++bond_j; } // rij2 < max_cutoff_squared_ } // Loop over j // Compute embedding energy and its derivative double e_u_prime; double const e_u = meam_sw_spline_->e_u_.Eval(rho_value, e_u_prime) - meam_sw_spline_->zero_atom_energy_; meam_sw_spline_->e_u_prime_[i] = e_u_prime; double const e_sw_prime = 1.0; double const e_sw = rho_sw_value; if (IsComputeEnergy) { *energy += e_u + e_sw; } if (IsComputeParticleEnergy) { particle_energy[i] += e_u + e_sw; } // Compute three-body contributions to force VectorOfSizeDIM f_i = {}; bond_j = two_body.begin(); for (int jb = 0; jb < number_bonds; ++jb, ++bond_j) { int const j = bond_j->index; double const r_ij = bond_j->r; double const f_ij = bond_j->f; double const f_prime_ij = bond_j->f_prime; double const sw_f_ij = bond_j->sw_f; double const sw_f_prime_ij = bond_j->sw_f_prime; VectorOfSizeDIM f_j = {}; auto bond_k = two_body.begin(); for (int kb = 0; kb < jb; ++kb, ++bond_k) { int const k = bond_k->index; double const r_ik = bond_k->r; double const f_ik = bond_k->f; double const f_prime_ik = bond_k->f_prime; double const sw_f_ik = bond_k->sw_f; double const sw_f_prime_ik = bond_k->sw_f_prime; double const cos_theta = (bond_j->dx * bond_k->dx + bond_j->dy * bond_k->dy + bond_j->dz * bond_k->dz); double const rho_g = meam_sw_spline_->rho_g_.Eval( cos_theta, prime); double const prefactor = e_u_prime * f_ij * prime * f_ik; double const prefactor_ij = prefactor / r_ij; double const prefactor_ik = prefactor / r_ik; double const fij = -e_u_prime * rho_g * f_prime_ij * f_ik + prefactor_ij * cos_theta; double const fik = -e_u_prime * rho_g * f_prime_ik * f_ij + prefactor_ik * cos_theta; double const sw_g = meam_sw_spline_->esw_g_.Eval( cos_theta, prime); double const prefactor2 = e_sw_prime * sw_f_ij * prime * sw_f_ik; double const prefactor2_ij = prefactor2 / r_ij; double const prefactor2_ik = prefactor2 / r_ik; double const sw_fij = -e_sw_prime * sw_g * sw_f_prime_ij * sw_f_ik + prefactor2_ij * cos_theta; double const sw_fik = -e_sw_prime * sw_g * sw_f_prime_ik * sw_f_ij + prefactor2_ik * cos_theta; VectorOfSizeDIM fj; fj[0] = bond_j->dx * fij - bond_k->dx * prefactor_ij; fj[1] = bond_j->dy * fij - bond_k->dy * prefactor_ij; fj[2] = bond_j->dz * fij - bond_k->dz * prefactor_ij; fj[0] += bond_j->dx * sw_fij - bond_k->dx * prefactor2_ij; fj[1] += bond_j->dy * sw_fij - bond_k->dy * prefactor2_ij; fj[2] += bond_j->dz * sw_fij - bond_k->dz * prefactor2_ij; f_j[0] += fj[0]; f_j[1] += fj[1]; f_j[2] += fj[2]; VectorOfSizeDIM fk; fk[0] = bond_k->dx * fik - bond_j->dx * prefactor_ik; fk[1] = bond_k->dy * fik - bond_j->dy * prefactor_ik; fk[2] = bond_k->dz * fik - bond_j->dz * prefactor_ik; fk[0] += bond_k->dx * sw_fik - bond_j->dx * prefactor2_ik; fk[1] += bond_k->dy * sw_fik - bond_j->dy * prefactor2_ik; fk[2] += bond_k->dz * sw_fik - bond_j->dz * prefactor2_ik; f_i[0] -= fk[0]; f_i[1] -= fk[1]; f_i[2] -= fk[2]; if (IsComputeForces) { forces[k][0] += fk[0]; forces[k][1] += fk[1]; forces[k][2] += fk[2]; } if (IsComputeVirial || IsComputeParticleVirial) { // Tabulate per-atom virial as symmetrized stress // tensor // Virial has 6 components and is stored as a // 6 - element vector in the following order: // xx, yy, zz, yz, xz, xy. double const dx_ij = bond_j->dx * r_ij; double const dy_ij = bond_j->dy * r_ij; double const dz_ij = bond_j->dz * r_ij; double const dx_ik = bond_k->dx * r_ik; double const dy_ik = bond_k->dy * r_ik; double const dz_ik = bond_k->dz * r_ik; VectorOfSizeSix v; v[0] = dx_ij * fj[0] + dx_ik * fk[0]; v[1] = dy_ij * fj[1] + dy_ik * fk[1]; v[2] = dz_ij * fj[2] + dz_ik * fk[2]; v[3] = dy_ij * fj[2] + dy_ik * fk[2]; v[4] = dx_ij * fj[2] + dx_ik * fk[2]; v[5] = dx_ij * fj[1] + dx_ik * fk[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]; } // IsComputeVirial if (IsComputeParticleVirial) { v[0] *= kThird; v[1] *= kThird; v[2] *= kThird; v[3] *= kThird; v[4] *= kThird; v[5] *= kThird; particle_virial[i][0] += v[0]; particle_virial[i][1] += v[1]; particle_virial[i][2] += v[2]; particle_virial[i][3] += v[3]; particle_virial[i][4] += v[4]; particle_virial[i][5] += v[5]; particle_virial[j][0] += v[0]; particle_virial[j][1] += v[1]; particle_virial[j][2] += v[2]; particle_virial[j][3] += v[3]; particle_virial[j][4] += v[4]; particle_virial[j][5] += v[5]; particle_virial[k][0] += v[0]; particle_virial[k][1] += v[1]; particle_virial[k][2] += v[2]; particle_virial[k][3] += v[3]; particle_virial[k][4] += v[4]; particle_virial[k][5] += v[5]; } // IsComputeParticleVirial } // IsComputeVirial || IsComputeParticleVirial } // Loop over k bonds if (IsComputeForces) { forces[i][0] -= f_j[0]; forces[i][1] -= f_j[1]; forces[i][2] -= f_j[2]; forces[j][0] += f_j[0]; forces[j][1] += f_j[1]; forces[j][2] += f_j[2]; } } // Loop over j bonds if (IsComputeForces) { forces[i][0] += f_i[0]; forces[i][1] += f_i[1]; forces[i][2] += f_i[2]; } } // Loop over i // Compute two-body pair interactions for (int i = 0; i < cached_number_of_particles_; ++i) { if (!particle_contributing[i]) { continue; } // Get the neighbors model_compute_arguments->GetNeighborList(0, i, &number_of_neighbors, &neighbors_of_particle); double const xi = coordinates[i][0]; double const yi = coordinates[i][1]; double const zi = coordinates[i][2]; // Setup loop over neighbors of current particle for (int jn = 0; jn < number_of_neighbors; ++jn) { int const j = neighbors_of_particle[jn]; int const contributing_j = particle_contributing[j]; // Effective half list if (!(contributing_j && j < i)) { double const dx = coordinates[j][0] - xi; double const dy = coordinates[j][1] - yi; double const dz = coordinates[j][2] - zi; double const rij2 = dx * dx + dy * dy + dz * dz; if (rij2 < max_cutoff_squared_) { double const rij = std::sqrt(rij2); double fij; double const phi = meam_sw_spline_->e_phi_.Eval(rij, fij); meam_sw_spline_->rho_r_.Eval(rij, prime); if (contributing_j) { prime *= (meam_sw_spline_->e_u_prime_[i] + meam_sw_spline_->e_u_prime_[j]); fij += prime; // Contribute pair term to Energy if (IsComputeEnergy) { *energy += phi; } // Contribute pair term to Particle Energy if (IsComputeParticleEnergy) { particle_energy[i] += 0.5 * phi; particle_energy[j] += 0.5 * phi; } } // particle j is not contributing else { prime *= meam_sw_spline_->e_u_prime_[i]; fij = 0.5 * fij + prime; // Contribute pair term to Energy if (IsComputeEnergy) { *energy += 0.5 * phi; } // Contribute pair term to Particle Energy if (IsComputeParticleEnergy) { particle_energy[i] += 0.5 * phi; } } // Divide by r_ij to get forces from gradient fij /= rij; if (IsComputeForces) { forces[i][0] += dx * fij; forces[i][1] += dy * fij; forces[i][2] += dz * fij; forces[j][0] -= dx * fij; forces[j][1] -= dy * fij; forces[j][2] -= dz * fij; } // IsComputeForces if (IsComputeVirial || IsComputeParticleVirial) { // Tabulate per-atom virial as symmetrized stress // tensor // 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] = dx * dx * fij; v[1] = dy * dy * fij; v[2] = dz * dz * fij; v[3] = dy * dz * fij; v[4] = dx * dz * fij; v[5] = dx * dy * fij; 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]; } // IsComputeVirial 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; particle_virial[i][0] += v[0]; particle_virial[i][1] += v[1]; particle_virial[i][2] += v[2]; particle_virial[i][3] += v[3]; particle_virial[i][4] += v[4]; particle_virial[i][5] += v[5]; particle_virial[j][0] += v[0]; particle_virial[j][1] += v[1]; particle_virial[j][2] += v[2]; particle_virial[j][3] += v[3]; particle_virial[j][4] += v[4]; particle_virial[j][5] += v[5]; } // IsComputeParticleVirial } // IsComputeVirial || IsComputeParticleVirial } // rij2 < max_cutoff_squared_ } // Effective half list } // Loop over j } // Loop over i // everything is good return false; } #undef KIM_LOGGER_OBJECT_NAME