/* * Project: MoleCuilder * Description: creates and alters molecular systems * Copyright (C) 2012 University of Bonn. All rights reserved. * Please see the COPYING file or "Copyright notice" in builder.cpp for details. * * * This file is part of MoleCuilder. * * MoleCuilder is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) any later version. * * MoleCuilder 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with MoleCuilder. If not, see . */ /* * ManyBodyPotential_Tersoff.cpp * * Created on: Sep 26, 2012 * Author: heber */ // include config.h #ifdef HAVE_CONFIG_H #include #endif #include "CodePatterns/MemDebug.hpp" #include "ManyBodyPotential_Tersoff.hpp" #include #include #include "CodePatterns/Assert.hpp" //#include "CodePatterns/Info.hpp" #include "CodePatterns/Log.hpp" #include "Potentials/helpers.hpp" ManyBodyPotential_Tersoff::ManyBodyPotential_Tersoff( boost::function< std::vector(const argument_t &, const double)> &_triplefunction ) : params(parameters_t(MAXPARAMS, 0.)), R(3.2), S(3.5), lambda3(0.), alpha(0.), chi(1.), omega(1.), triplefunction(_triplefunction) {} ManyBodyPotential_Tersoff::ManyBodyPotential_Tersoff( const double &_R, const double &_S, const double &_A, const double &_B, const double &_lambda, const double &_mu, const double &_lambda3, const double &_alpha, const double &_beta, const double &_chi, const double &_omega, const double &_n, const double &_c, const double &_d, const double &_h, const double &_offset, boost::function< std::vector(const argument_t &, const double)> &_triplefunction) : params(parameters_t(MAXPARAMS, 0.)), R(_R), S(_S), lambda3(_lambda3), alpha(_alpha), chi(_chi), omega(_mu), triplefunction(_triplefunction) { // Info info(__func__); // R = _R; // S = _S; params[A] = _A; params[B] = _B; params[lambda] = _lambda; params[mu] = _mu; // lambda3 = _lambda3; // alpha = _alpha; params[beta] = _beta; // chi = _chi; // omega = _omega; params[n] = _n; params[c] = _c; params[d] = _d; params[h] = _h; params[offset] = _offset; } void ManyBodyPotential_Tersoff::setParameters(const parameters_t &_params) { const size_t paramsDim = _params.size(); ASSERT( paramsDim <= getParameterDimension(), "ManyBodyPotential_Tersoff::setParameters() - we need not more than " +toString(getParameterDimension())+" parameters."); for (size_t i=0; i< paramsDim; ++i) params[i] = _params[i]; #ifndef NDEBUG parameters_t check_params(getParameters()); check_params.resize(paramsDim); // truncate to same size ASSERT( check_params == _params, "ManyBodyPotential_Tersoff::setParameters() - failed, mismatch in to be set " +toString(_params)+" and set "+toString(check_params)+" params."); #endif } ManyBodyPotential_Tersoff::results_t ManyBodyPotential_Tersoff::operator()( const arguments_t &arguments ) const { // Info info(__func__); double result = 0.; for(arguments_t::const_iterator argiter = arguments.begin(); argiter != arguments.end(); ++argiter) { const argument_t &r_ij = *argiter; const double cutoff = function_cutoff(r_ij.distance); const double temp = (cutoff == 0.) ? 0. : cutoff * ( function_prefactor( alpha, function_eta(r_ij)) * function_smoother( params[A], params[lambda], r_ij.distance) + function_prefactor( params[beta], function_zeta(r_ij)) * function_smoother( -params[B], params[mu], r_ij.distance) ); result += temp; } // LOG(2, "DEBUG: operator()(" << r_ij.distance << ") = " << result); return std::vector(1, params[offset]+result); } ManyBodyPotential_Tersoff::derivative_components_t ManyBodyPotential_Tersoff::derivative( const arguments_t &arguments ) const { // Info info(__func__); return ManyBodyPotential_Tersoff::derivative_components_t(); } ManyBodyPotential_Tersoff::results_t ManyBodyPotential_Tersoff::parameter_derivative( const arguments_t &arguments, const size_t index ) const { // Info info(__func__); // ASSERT( arguments.size() == 1, // "ManyBodyPotential_Tersoff::parameter_derivative() - requires exactly one argument."); if (index == offset) return results_t(1, 1.); double result = 0.; for(arguments_t::const_iterator argiter = arguments.begin(); argiter != arguments.end(); ++argiter) { const argument_t &r_ij = *argiter; switch (index) { // case R: // { // result += 0.; // break; // } // case S: // { // result += 0.; // break; // } case A: { const double cutoff = function_cutoff(r_ij.distance); result += (cutoff == 0.) ? 0. : cutoff * function_prefactor( alpha, function_eta(r_ij)) * function_smoother( 1., params[lambda], r_ij.distance); // cutoff * function_prefactor( // alpha, // function_eta(r_ij)) // * function_smoother( // 1., // params[mu], // r_ij.distance); break; } case B: { const double cutoff = function_cutoff(r_ij.distance); result += (cutoff == 0.) ? 0. : cutoff * function_prefactor( params[beta], function_zeta(r_ij)) * function_smoother( -1., params[mu], r_ij.distance); // cutoff * function_prefactor( // beta, // function_zeta(r_ij)) // * function_smoother( // -params[B], // params[mu], // r_ij.distance)/params[B]; break; } case lambda: { const double cutoff = function_cutoff(r_ij.distance); result += (cutoff == 0.) ? 0. : -r_ij.distance * cutoff * function_prefactor( alpha, function_eta(r_ij)) * function_smoother( params[A], params[lambda], r_ij.distance); break; } case mu: { const double cutoff = function_cutoff(r_ij.distance); result += (cutoff == 0.) ? 0. : -r_ij.distance * cutoff *( function_prefactor( params[beta], function_zeta(r_ij)) * function_smoother( -params[B], params[mu], r_ij.distance) ); break; } // case lambda3: // { // result += 0.; // break; // } // case alpha: // { // const double temp = // pow(alpha*function_eta(r_ij), params[n]); // const double cutoff = function_cutoff(r_ij.distance); // result += (cutoff == 0.) || (alpha == 0. )? // 0. : // function_smoother( // params[A], // params[lambda], // r_ij.distance) // * (-.5) * alpha * (temp/alpha) // / (1. + temp) // ; // break; // } // case chi: // { // result += 0.; // break; // } // case omega: // { // result += 0.; // break; // } case beta: { const double temp = pow(params[beta]*function_zeta(r_ij), params[n]); const double cutoff = function_cutoff(r_ij.distance); result += (cutoff == 0.) || (params[beta] == 0. )? 0. : cutoff * function_smoother( -params[B], params[mu], r_ij.distance) * (-.5) * function_prefactor( params[beta], function_zeta(r_ij)) * (temp/params[beta]) / (1. + temp) ; break; } case n: { const double zeta = function_zeta(r_ij); const double temp = pow( params[beta]*zeta , params[n]); const double cutoff = function_cutoff(r_ij.distance); const double tempres = ((cutoff == 0.) || (zeta == 0.)) ? // zeta must be caught if zero due to log 0. : .5 * cutoff * function_smoother( -params[B], params[mu], r_ij.distance) * function_prefactor( params[beta], function_zeta(r_ij)) * ( log(1.+temp)/(params[n]*params[n]) - temp * (log(function_zeta(r_ij)) + log(params[beta])) /(params[n]*(1.+temp))) ; // if (tempres != tempres) // LOG(2, "DEBUG: tempres is NaN."); // LOG(2, "DEBUG: Adding " << tempres << " for p.d. w.r.t n, temp=" << temp << ", cutoff=" << cutoff); result += tempres; break; } case c: { const double zeta = function_zeta(r_ij); if (zeta == 0.) break; const double temp = pow(zeta, params[n]-1.) * pow(params[beta],params[n]); const double cutoff = function_cutoff(r_ij.distance); const double tempres = (cutoff == 0.) ? 0. : cutoff * function_smoother( -params[B], params[mu], r_ij.distance) * function_prefactor( params[beta], zeta) * (-1.) * temp / (1.+temp*zeta); double factor = function_derivative_c(r_ij); result += tempres*factor; if (result != result) ELOG(1, "result is NaN, zeta=" << zeta << ", temp=" << temp << ", cutoff=" << cutoff << ", tempres=" << tempres << ", factor=" << factor); break; } case d: { const double zeta = function_zeta(r_ij); const double temp = pow(zeta, params[n]-1.) * pow(params[beta],params[n]); const double cutoff = function_cutoff(r_ij.distance); const double tempres = (cutoff == 0.) ? 0. : cutoff * function_smoother( -params[B], params[mu], r_ij.distance) * function_prefactor( params[beta], zeta) * (-1.) * temp / (1.+temp*zeta); double factor = function_derivative_d(r_ij); result += tempres*factor; if (result != result) ELOG(1, "result is NaN, zeta=" << zeta << ", temp=" << temp << ", cutoff=" << cutoff << ", tempres=" << tempres << ", factor=" << factor); break; } case h: { const double zeta = function_zeta(r_ij); const double temp = pow(zeta, params[n]-1.) * pow(params[beta],params[n]); const double cutoff = function_cutoff(r_ij.distance); const double tempres = (cutoff == 0.) ? 0. : cutoff * function_smoother( -params[B], params[mu], r_ij.distance) * function_prefactor( params[beta], zeta) * (-1.) * temp / (1.+temp*zeta); double factor = function_derivative_h(r_ij); result += tempres*factor; if (result != result) ELOG(1, "result is NaN, zeta=" << zeta << ", temp=" << temp << ", cutoff=" << cutoff << ", tempres=" << tempres << ", factor=" << factor); break; } case offset: result += 1.; break; default: break; } if (result != result) ELOG(1, "result is NaN."); } return results_t(1,-result); } ManyBodyPotential_Tersoff::result_t ManyBodyPotential_Tersoff::function_derivative_c( const argument_t &r_ij ) const { double result = 0.; std::vector triples = triplefunction(r_ij, S); for (std::vector::const_iterator iter = triples.begin(); iter != triples.end(); ++iter) { ASSERT( iter->size() == 2, "ManyBodyPotential_Tersoff::function_derivative_c() - the triples result must contain exactly two distances."); const argument_t &r_ik = (*iter)[0]; const argument_t &r_jk = (*iter)[1]; const double tempangle = params[h] - function_theta(r_ij.distance, r_ik.distance, r_jk.distance); const double cutoff = function_cutoff(r_ik.distance); result += (cutoff == 0.) ? 0. : cutoff * omega * exp( Helpers::pow(lambda3 * (r_ij.distance - r_ik.distance) ,3)) * ( params[c]/Helpers::pow(params[d],2) - params[c] / ( Helpers::pow(params[d],2) + Helpers::pow(tempangle,2) ) ); } return result; } ManyBodyPotential_Tersoff::result_t ManyBodyPotential_Tersoff::function_derivative_d( const argument_t &r_ij ) const { double result = 0.; std::vector triples = triplefunction(r_ij, S); for (std::vector::const_iterator iter = triples.begin(); iter != triples.end(); ++iter) { ASSERT( iter->size() == 2, "ManyBodyPotential_Tersoff::function_derivative_d() - the triples result must contain exactly two distances."); const argument_t &r_ik = (*iter)[0]; const argument_t &r_jk = (*iter)[1]; const double tempangle = params[h] - function_theta(r_ij.distance, r_ik.distance, r_jk.distance); const double cutoff = function_cutoff(r_ik.distance); result += (cutoff == 0.) ? 0. : cutoff * omega * exp( Helpers::pow(lambda3 * (r_ij.distance - r_ik.distance) ,3)) * ( - Helpers::pow(params[c],2)/Helpers::pow(params[d],3) + Helpers::pow(params[c],2) * params[d] / Helpers::pow(Helpers::pow(params[d],2) + Helpers::pow(tempangle,2),2) ); } return result; } ManyBodyPotential_Tersoff::result_t ManyBodyPotential_Tersoff::function_derivative_h( const argument_t &r_ij ) const { double result = 0.; std::vector triples = triplefunction(r_ij, S); for (std::vector::const_iterator iter = triples.begin(); iter != triples.end(); ++iter) { ASSERT( iter->size() == 2, "ManyBodyPotential_Tersoff::function_derivative_h() - the triples result must contain exactly two distances."); const argument_t &r_ik = (*iter)[0]; const argument_t &r_jk = (*iter)[1]; const double tempangle = params[h] - function_theta(r_ij.distance, r_ik.distance, r_jk.distance); const double cutoff = function_cutoff(r_ik.distance); result += (cutoff == 0.) ? 0. : cutoff * omega * exp( Helpers::pow(lambda3 * (r_ij.distance - r_ik.distance) ,3)) * ( ( Helpers::pow(params[c],2)*tempangle ) / Helpers::pow(Helpers::pow(params[d],2) + Helpers::pow(tempangle,2),2) ); } return result; } ManyBodyPotential_Tersoff::result_t ManyBodyPotential_Tersoff::function_cutoff( const double &distance ) const { // Info info(__func__); double result = 0.; if (distance < R) result = 1.; else if (distance > S) result = 0.; else { result = (0.5 + 0.5 * cos( M_PI * (distance - R)/(S-R))); } // LOG(2, "DEBUG: function_cutoff(" << distance << ") = " << result); return result; } ManyBodyPotential_Tersoff::result_t ManyBodyPotential_Tersoff::function_prefactor( const double &alpha, const double &eta ) const { // Info info(__func__); const double result = chi * pow( (1. + pow(alpha * eta, params[n])), -1./(2.*params[n])); // LOG(2, "DEBUG: function_prefactor(" << alpha << "," << eta << ") = " << result); return result; } ManyBodyPotential_Tersoff::result_t ManyBodyPotential_Tersoff::function_smoother( const double &prefactor, const double &lambda, const double &distance ) const { // Info info(__func__); const double result = prefactor * exp(-lambda * distance); // LOG(2, "DEBUG: function_smoother(" << prefactor << "," << lambda << "," << distance << ") = " << result); return result; } ManyBodyPotential_Tersoff::result_t ManyBodyPotential_Tersoff::function_eta( const argument_t &r_ij ) const { // Info info(__func__); result_t result = 0.; // get all triples within the cutoff std::vector triples = triplefunction(r_ij, S); for (std::vector::const_iterator iter = triples.begin(); iter != triples.end(); ++iter) { ASSERT( iter->size() == 2, "ManyBodyPotential_Tersoff::function_zeta() - the triples result must contain of exactly two distances."); const argument_t &r_ik = (*iter)[0]; result += function_cutoff(r_ik.distance) * exp( Helpers::pow(lambda3 * (r_ij.distance - r_ik.distance) ,3)); } // LOG(2, "DEBUG: function_eta(" << r_ij.distance << ") = " << result); return result; } ManyBodyPotential_Tersoff::result_t ManyBodyPotential_Tersoff::function_zeta( const argument_t &r_ij ) const { // Info info(__func__); result_t result = 0.; // get all triples within the cutoff std::vector triples = triplefunction(r_ij, S); for (std::vector::const_iterator iter = triples.begin(); iter != triples.end(); ++iter) { ASSERT( iter->size() == 2, "ManyBodyPotential_Tersoff::function_zeta() - the triples result must contain exactly two distances."); const argument_t &r_ik = (*iter)[0]; const argument_t &r_jk = (*iter)[1]; result += function_cutoff(r_ik.distance) * omega * function_angle(r_ij.distance, r_ik.distance, r_jk.distance) * exp( Helpers::pow(lambda3 * (r_ij.distance - r_ik.distance) ,3)); } // LOG(2, "DEBUG: function_zeta(" << r_ij.distance << ") = " << result); return result; } ManyBodyPotential_Tersoff::result_t ManyBodyPotential_Tersoff::function_theta( const double &r_ij, const double &r_ik, const double &r_jk ) const { const double angle = Helpers::pow(r_ij,2) + Helpers::pow(r_ik,2) - Helpers::pow(r_jk,2); const double divisor = 2.* r_ij * r_ik; if (divisor != 0.) { LOG(2, "DEBUG: cos(theta)= " << angle/divisor); return angle/divisor; } else return 0.; } ManyBodyPotential_Tersoff::result_t ManyBodyPotential_Tersoff::function_angle( const double &r_ij, const double &r_ik, const double &r_jk ) const { // Info info(__func__); const double result = 1. + (Helpers::pow(params[c]/params[d], 2)) - Helpers::pow(params[c], 2)/(Helpers::pow(params[d], 2) + Helpers::pow(params[h] - function_theta(r_ij, r_ik, r_jk),2)); // LOG(2, "DEBUG: function_angle(" << r_ij << "," << r_ik << "," << r_jk << ") = " << result); return result; }