#include "CauchyBorn.h" #include "VoigtOperations.h" #include "CBLattice.h" #include "CbPotential.h" using voigt3::to_voigt; namespace ATC { //============================================================================ // Computes the electron density for EAM potentials //============================================================================ double cb_electron_density(const StressArgs &args ) { double e_density = 0.0; for (INDEX a=0; arho(pair.d); } return e_density; } //============================================================================ // Computes the stress at a quadrature point //============================================================================ void cb_stress(const StressArgs &args, StressAtIP &s, double *F) { const double &T = args.temperature; const bool finite_temp = T > 0.0; DENS_MAT D; // dynamical matrix (finite temp) DENS_MAT_VEC dDdF; // derivative of dynamical matrix (finite temp) double e_density(0.),embed(0.),embed_p(0.),embed_pp(0.),embed_ppp(0.); DENS_VEC l0; DENS_MAT L0; DENS_MAT_VEC M0; // If temperature is nonzero then allocate space for // dynamical matrix and its derivative with respect to F. if (finite_temp) { D.reset(3,3); dDdF.assign(6, DENS_MAT(3,3)); M0.assign(3, DENS_MAT(3,3)); L0.reset(3,3); l0.reset(3); } if (F) *F = 0.0; // if using EAM potential, calculate embedding function and derivatives if (args.potential->terms.embedding) { for (INDEX a=0; arho(pair.d); pair.r = args.vac.r(a); pair.rho_r = args.potential->rho_r(pair.d); pair.rho_rr = args.potential->rho_rr(pair.d); if (finite_temp) { l0 += pair.r*pair.di*pair.rho_r; DENS_MAT rR = tensor_product(pair.r, pair.R); L0.add_scaled(rR, pair.di*pair.rho_r); DENS_MAT rr = tensor_product(pair.r, pair.r); rr *= pair.di*pair.di*(pair.rho_rr - pair.di*pair.rho_r); diagonal(rr) += pair.di*pair.rho_r; for (int i = 0; i < 3; i++) { for (int k = 0; k < 3; k++) { for (int L = 0; L < 3; L++) { M0[i](k,L) += rr(i,k)*args.vac.R(a)(L); } } } } } embed = args.potential->F(e_density); // "F" in usual EAM symbology embed_p = args.potential->F_p(e_density); embed_pp = args.potential->F_pp(e_density); embed_ppp = args.potential->F_ppp(e_density); if (F) *F += embed; if (finite_temp) { const DENS_MAT ll = tensor_product(l0, l0); D.add_scaled(ll, embed_pp); const DENS_VEC llvec = to_voigt(ll); for (int v = 0; v < 6; v++) { dDdF[v].add_scaled(L0, embed_ppp*llvec(v)); } dDdF[0].add_scaled(M0[0], 2*embed_pp*l0(0)); dDdF[1].add_scaled(M0[1], 2*embed_pp*l0(1)); dDdF[2].add_scaled(M0[2], 2*embed_pp*l0(2)); dDdF[3].add_scaled(M0[1], embed_pp*l0(2)); dDdF[3].add_scaled(M0[2], embed_pp*l0(1)); dDdF[4].add_scaled(M0[0], embed_pp*l0(2)); dDdF[4].add_scaled(M0[2], embed_pp*l0(0)); dDdF[5].add_scaled(M0[0], embed_pp*l0(1)); dDdF[5].add_scaled(M0[1], embed_pp*l0(0)); } } // Loop on all cluster atoms (origin atom not included). for (INDEX a=0; aterms.pairwise) { if (F) *F += 0.5*args.potential->phi(pair.d); pair.phi_r = args.potential->phi_r(pair.d); pairwise_stress(pair, s); } if (args.potential->terms.embedding) { pair.F_p = embed_p; pair.rho_r = args.potential->rho_r(pair.d); embedding_stress(pair, s); } if (finite_temp) { // Compute finite T terms. pair.r = args.vac.r(a); if (args.potential->terms.pairwise) { pair.phi_rr = args.potential->phi_rr(pair.d); pair.phi_rrr = args.potential->phi_rrr(pair.d); pairwise_thermal(pair, D, &dDdF); } if (args.potential->terms.embedding) { pair.rho_rr = args.potential->rho_rr(pair.d); pair.rho_rrr = args.potential->rho_rrr(pair.d); pair.F_pp = embed_pp; pair.F_ppp = embed_ppp; embedding_thermal(pair,D,L0,&dDdF); } } // if has three-body terms ... TODO compute three-body terms } // Finish finite temperature Cauchy-Born. if (finite_temp) { const DENS_MAT &F = args.vac.deformation_gradient(); thermal_end(dDdF, D, F, T, args.boltzmann_constant, s); } } //=========================================================================== // Computes the elastic energy (free or potential if T=0). //=========================================================================== double cb_energy(const StressArgs &args) { const double &T = args.temperature; bool finite_temp = (T > 0.0); //const bool finite_temp = T > 0.0; DENS_MAT D; // dynamical matrix (finite temp) double e_density,embed,embed_p(0.),embed_pp(0.),embed_ppp(0.); DENS_VEC l0; DENS_MAT L0; DENS_MAT_VEC M0; // If temperature is nonzero then allocate space for dynamical matrix. if (finite_temp) { D.reset(3,3); l0.reset(3); } double F = 0.0; // Do pairwise terms, loop on all cluster atoms (origin atom not included). // if using EAM potential, calculate embedding function and derivatives if (args.potential->terms.embedding) { e_density = 0.0; for (INDEX a=0; arho(pair.d); pair.r = args.vac.r(a); if (finite_temp) { l0 += pair.r*pair.di*pair.rho_r; } } embed = args.potential->F(e_density); embed_p = args.potential->F_p(e_density); embed_pp = args.potential->F_pp(e_density); embed_ppp = args.potential->F_ppp(e_density); F += embed; if (finite_temp) { const DENS_MAT ll = tensor_product(l0, l0); D.add_scaled(ll, embed_pp); } } for (INDEX a=0; aterms.pairwise) { F += 0.5*args.potential->phi(pair.d); } if (finite_temp) { // Compute finite T terms. pair.r = args.vac.r(a); if (args.potential->terms.pairwise) { pair.phi_r = args.potential->phi_r(pair.d); pair.phi_rr = args.potential->phi_rr(pair.d); pair.phi_rrr = args.potential->phi_rrr(pair.d); pairwise_thermal(pair, D); } if (args.potential->terms.embedding) { pair.rho_r = args.potential->rho_r(pair.d); pair.rho_rr = args.potential->rho_rr(pair.d); pair.rho_rrr = args.potential->rho_rrr(pair.d); pair.F_p = embed_p; pair.F_pp = embed_pp; pair.F_ppp = embed_ppp; embedding_thermal(pair,D,L0); } } // if has three-body terms ... TODO compute three-body terms } // Finish finite temperature Cauchy-Born. const double kB = args.boltzmann_constant; const double hbar = args.planck_constant; if (finite_temp) { F += kB*T*log(pow(hbar/(kB*T),3.0)*sqrt(det(D))); } //if (finite_temp) F += 0.5*args.boltzmann_constant*T*log(det(D)); return F; } //=========================================================================== // Computes the entropic energy TS (minus c_v T) //=========================================================================== double cb_entropic_energy(const StressArgs &args) { const double &T = args.temperature; DENS_MAT D(3,3); // dynamical matrix (finite temp) double e_density,embed_p(0.),embed_pp(0.),embed_ppp(0.); DENS_VEC l0(3); DENS_MAT L0; DENS_MAT_VEC M0; // if using EAM potential, calculate embedding function and derivatives if (args.potential->terms.embedding) { e_density = 0.0; for (INDEX a=0; arho(pair.d); pair.r = args.vac.r(a); l0 += pair.r*pair.di*pair.rho_r; //DENS_MAT rR = tensor_product(pair.r, pair.R); //L0.add_scaled(rR, pair.di*args.potential->rho_r(pair.d)); } //embed = args.potential->F(e_density); embed_p = args.potential->F_p(e_density); embed_pp = args.potential->F_pp(e_density); embed_ppp = args.potential->F_ppp(e_density); const DENS_MAT ll = tensor_product(l0, l0); D.add_scaled(ll, embed_pp); } // Compute the dynamical matrix // Loop on all cluster atoms (origin atom not included). for (INDEX a=0; aterms.pairwise) { pair.phi_r = args.potential->phi_r(pair.d); pair.phi_rr = args.potential->phi_rr(pair.d); pair.phi_rrr = args.potential->phi_rrr(pair.d); pairwise_thermal(pair, D); } if (args.potential->terms.embedding) { pair.rho_r = args.potential->rho_r(pair.d); pair.rho_rr = args.potential->rho_rr(pair.d); pair.rho_rrr = args.potential->rho_rrr(pair.d); pair.F_p = embed_p; pair.F_pp = embed_pp; pair.F_ppp = embed_ppp; embedding_thermal(pair,D,L0); } } // Finish finite temperature Cauchy-Born. const double kB = args.boltzmann_constant; const double hbar = args.planck_constant;; double F = kB*T*log(pow(hbar/(kB*T),3.0)*sqrt(det(D))); return F; } //=========================================================================== // Computes the stress contribution given the pairwise parameters. //=========================================================================== inline void pairwise_stress(const PairParam &p, StressAtIP &s) { for (INDEX i=0; i(6,6),Iu*Iu-IIu); da -= voigt3::derivative_of_square(C); dU = tensor_product(U, dfct); dU.add_scaled(da, fct); U *= fct; } //============================================================================ // Computes the dynamical matrix (TESTING FUNCTION) //============================================================================ DENS_MAT compute_dynamical_matrix(const StressArgs &args) { DENS_MAT D(3,3); for (INDEX a=0; aphi_r(pair.d); pair.r = args.vac.r(a); pair.phi_rr = args.potential->phi_rr(pair.d); pair.phi_rrr = args.potential->phi_rrr(pair.d); pairwise_thermal(pair, D); } return D; } //============================================================================ // Computes the determinant of the dynamical matrix (TESTING FUNCTION) //============================================================================ double compute_detD(const StressArgs &args) { return det(compute_dynamical_matrix(args)); } //============================================================================ // Computes the derivative of the dynamical matrix (TESTING FUNCTION) //============================================================================ DENS_MAT_VEC compute_dynamical_derivative(StressArgs &args) { const double EPS = 1.0e-6; DENS_MAT_VEC dDdF(6, DENS_MAT(3,3)); for (INDEX i=0; i<3; i++) { for (INDEX j=0; j<3; j++) { // store original F const double Fij = args.vac.F_(i,j); args.vac.F_(i,j) = Fij + EPS; DENS_MAT Da = compute_dynamical_matrix(args); args.vac.F_(i,j) = Fij - EPS; DENS_MAT Db = compute_dynamical_matrix(args); args.vac.F_(i,j) = Fij; dDdF[0](i,j) = (Da(0,0)-Db(0,0))*(0.5/EPS); dDdF[1](i,j) = (Da(1,1)-Db(1,1))*(0.5/EPS); dDdF[2](i,j) = (Da(2,2)-Db(2,2))*(0.5/EPS); dDdF[3](i,j) = (Da(1,2)-Db(1,2))*(0.5/EPS); dDdF[4](i,j) = (Da(0,2)-Db(0,2))*(0.5/EPS); dDdF[5](i,j) = (Da(0,1)-Db(0,1))*(0.5/EPS); } } return dDdF; } }