#include "CauchyBorn.h"
#include "VoigtOperations.h"
#include "CBLattice.h"
#include "CbPotential.h"
using voigt3::to_voigt;
namespace ATC {
double cb_electron_density(const StressArgs &args )
{
double e_density = 0.0;
for (INDEX a=0; a<args.vac.size(); a++) {
PairParam pair(args.vac.R(a), args.vac.bond_length(a));
e_density += args.potential->rho(pair.d);
}
return e_density;
}
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; DENS_MAT_VEC dDdF; 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 (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 (args.potential->terms.embedding) {
for (INDEX a=0; a<args.vac.size(); a++) {
PairParam pair(args.vac.R(a), args.vac.bond_length(a));
e_density += args.potential->rho(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); 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));
}
}
for (INDEX a=0; a<args.vac.size(); a++) {
PairParam pair(args.vac.R(a), args.vac.bond_length(a));
if (args.potential->terms.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) { 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 (finite_temp) {
const DENS_MAT &F = args.vac.deformation_gradient();
thermal_end(dDdF, D, F, T, args.boltzmann_constant, s);
}
}
double cb_energy(const StressArgs &args)
{
const double &T = args.temperature;
bool finite_temp = (T > 0.0);
DENS_MAT D; double e_density,embed,embed_p(0.),embed_pp(0.),embed_ppp(0.);
DENS_VEC l0;
DENS_MAT L0;
DENS_MAT_VEC M0;
if (finite_temp) {
D.reset(3,3);
l0.reset(3);
}
double F = 0.0;
if (args.potential->terms.embedding) {
e_density = 0.0;
for (INDEX a=0; a<args.vac.size(); a++) {
PairParam pair(args.vac.R(a), args.vac.bond_length(a));
e_density += args.potential->rho(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; a<args.vac.size(); a++) {
PairParam pair(args.vac.R(a), args.vac.bond_length(a));
if (args.potential->terms.pairwise) {
F += 0.5*args.potential->phi(pair.d);
}
if (finite_temp) { 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);
}
}
}
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)));
}
return F;
}
double cb_entropic_energy(const StressArgs &args)
{
const double &T = args.temperature;
DENS_MAT D(3,3); double e_density,embed_p(0.),embed_pp(0.),embed_ppp(0.);
DENS_VEC l0(3);
DENS_MAT L0;
DENS_MAT_VEC M0;
if (args.potential->terms.embedding) {
e_density = 0.0;
for (INDEX a=0; a<args.vac.size(); a++) {
PairParam pair(args.vac.R(a), args.vac.bond_length(a));
e_density += args.potential->rho(pair.d);
pair.r = args.vac.r(a);
l0 += pair.r*pair.di*pair.rho_r;
}
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);
}
for (INDEX a=0; a<args.vac.size(); a++) {
PairParam pair(args.vac.R(a), args.vac.bond_length(a));
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);
}
}
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;
}
inline void pairwise_stress(const PairParam &p, StressAtIP &s)
{
for (INDEX i=0; i<p.R.size(); i++)
for (INDEX j=i; j<p.R.size(); j++)
s(i,j) += 0.5*p.di * p.phi_r * p.R(i) * p.R(j);
}
inline void embedding_stress(const PairParam &p, StressAtIP &s)
{
for (INDEX i=0; i<p.R.size(); i++)
for (INDEX j=i; j<p.R.size(); j++)
s(i,j) += p.di * p.F_p * p.rho_r * p.R(i) * p.R(j);
}
void pairwise_thermal(const PairParam &p, DENS_MAT &D, DENS_MAT_VEC *dDdF)
{
const double di2 = p.di*p.di;
const double g = p.di*p.phi_r;
const double g_d = p.di*p.phi_rr - p.di*g; const double f = di2 * (p.phi_rr - g); const double f_d = di2*(p.phi_rrr-g_d) - 2.0*p.di*f;
const DENS_MAT rr = tensor_product(p.r, p.r);
D.add_scaled(rr, f);
diagonal(D) += g;
if (!dDdF) return; const double gp_r = g_d*p.di;
const double fp_r = f_d*p.di;
const double fr[] = {f*p.r(0), f*p.r(1), f*p.r(2)};
const DENS_MAT rR = tensor_product(p.r, p.R);
DENS_MAT_VEC &dD = *dDdF;
dD[0].add_scaled(rR, fp_r*rr(0,0) + gp_r);
dD[1].add_scaled(rR, fp_r*rr(1,1) + gp_r);
dD[2].add_scaled(rR, fp_r*rr(2,2) + gp_r);
dD[3].add_scaled(rR, fp_r*rr(1,2));
dD[4].add_scaled(rR, fp_r*rr(0,2));
dD[5].add_scaled(rR, fp_r*rr(0,1));
for (INDEX L=0; L<p.R.size(); L++) {
dD[0](0,L) += p.R[L] * 2.0*fr[0];
dD[1](1,L) += p.R[L] * 2.0*fr[1];
dD[2](2,L) += p.R[L] * 2.0*fr[2];
dD[3](1,L) += p.R[L] * fr[2];
dD[3](2,L) += p.R[L] * fr[1];
dD[4](0,L) += p.R[L] * fr[2];
dD[4](2,L) += p.R[L] * fr[0];
dD[5](0,L) += p.R[L] * fr[1];
dD[5](1,L) += p.R[L] * fr[0];
}
}
void embedding_thermal(const PairParam &p, DENS_MAT &D, DENS_MAT &L0, DENS_MAT_VEC *dDdF)
{
const double di = p.di;
const double di2 = p.di*p.di;
const double di3 = p.di*p.di*p.di;
const double x = p.F_pp*p.rho_r*p.rho_r + 2*p.F_p*p.rho_rr;
const double z = di*(2*p.F_p*p.rho_r);
const double y = di2*(x-z);
const DENS_MAT rr = tensor_product(p.r, p.r);
D.add_scaled(rr, y);
diagonal(D) += z;
if (!dDdF) return; DENS_MAT_VEC &dD = *dDdF;
const DENS_MAT rR = tensor_product(p.r, p.R);
double rho_term1 = p.rho_rr - di*p.rho_r;
double rho_term2 = p.rho_r*rho_term1;
double rho_term3 = p.rho_rrr - 3*di*p.rho_rr + 3*di2*p.rho_r;
const double a = di2*2*p.F_p*rho_term1;
const double b = di2*(p.F_ppp*p.rho_r*p.rho_r + 2*p.F_pp*rho_term1);
const double c = di3*(2*p.F_pp*rho_term2 + 2*p.F_p*rho_term3);
const double w = di2*p.F_pp*p.rho_r*p.rho_r;
dD[0].add_scaled(rR, a + c*rr(0,0));
dD[1].add_scaled(rR, a + c*rr(1,1));
dD[2].add_scaled(rR, a + c*rr(2,2));
dD[3].add_scaled(rR, c*rr(1,2));
dD[4].add_scaled(rR, c*rr(0,2));
dD[5].add_scaled(rR, c*rr(0,1));
dD[0].add_scaled(L0, di*2*p.F_pp*p.rho_r + b*rr(0,0));
dD[1].add_scaled(L0, di*2*p.F_pp*p.rho_r + b*rr(1,1));
dD[2].add_scaled(L0, di*2*p.F_pp*p.rho_r + b*rr(2,2));
dD[3].add_scaled(L0, b*rr(1,2));
dD[4].add_scaled(L0, b*rr(0,2));
dD[5].add_scaled(L0, b*rr(0,1));
const double aw = a + w;
const double awr[] = {aw*p.r(0), aw*p.r(1), aw*p.r(2)};
for (INDEX L=0; L<p.R.size(); L++) {
dD[0](0,L) += 2*awr[0]*p.R[L];
dD[1](1,L) += 2*awr[1]*p.R[L];
dD[2](2,L) += 2*awr[2]*p.R[L];
dD[3](2,L) += awr[1]*p.R[L];
dD[3](1,L) += awr[2]*p.R[L];
dD[4](2,L) += awr[0]*p.R[L];
dD[4](0,L) += awr[2]*p.R[L];
dD[5](1,L) += awr[0]*p.R[L];
dD[5](0,L) += awr[1]*p.R[L];
}
}
inline void thermal_end(const DENS_MAT_VEC &DF, const DENS_MAT &D, const DENS_MAT &F, const double &T, const double &kb, StressAtIP &s, double* F_w) {
DENS_MAT c = adjugate(D), dd(3,3);
dd.add_scaled(DF[0], c(0,0));
dd.add_scaled(DF[1], c(1,1));
dd.add_scaled(DF[2], c(2,2));
dd.add_scaled(DF[3], c(1,2) + c(2,1));
dd.add_scaled(DF[4], c(0,2) + c(2,0));
dd.add_scaled(DF[5], c(0,1) + c(1,0));
const double detD = det(D);
const double factor = 0.5*kb*T/detD;
dd = inv(F)*dd;
for (INDEX i=0; i<3; i++)
for (INDEX j=i; j<3; j++)
s(i,j) += factor * dd(i,j);
if (F_w) *F_w += 0.5*kb*T*log(detD);
}
void stretch_tensor_derivative(const DENS_VEC &C, DENS_VEC &U, DENS_MAT &dU)
{
const DENS_VEC C2(voigt3::dsymm(C,C));
const double Ic = voigt3::tr(C);
const double IIc = 0.5*(Ic*Ic - voigt3::tr(C2));
const double IIIc = voigt3::det(C);
const DENS_VEC I = voigt3::eye(3);
DENS_VEC dIc ( I );
DENS_VEC dIIc ( Ic*dIc - C );
DENS_VEC dIIIc ( voigt3::inv(C) * IIIc );
for (INDEX i=3; i<6; i++) {
dIIc(i) *= 2.0;
dIIIc(i) *= 2.0;
}
const double k = Ic*Ic - 3.0*IIc;
const DENS_VEC dk (2.0*Ic*dIc - 3.0*dIIc);
if (k < 1e-8) {
const double lambda = sqrt((1.0/3.0)*Ic);
const double dlambda = 0.5/(3.0*lambda);
U = I*lambda;
dU = tensor_product(dIc*dlambda, dIc); return;
}
const double L = Ic*(Ic*Ic - 4.5*IIc) + 13.5*IIIc;
DENS_VEC dL( (3.0*Ic*Ic-4.5*IIc)*dIc );
dL.add_scaled(dIIc, -4.5*Ic);
dL.add_scaled(dIIIc, 13.5);
const double kpow = pow(k,-1.5);
const double dkpow = -1.5*kpow/k;
const double phi = acos(L*kpow);
const double d1 = -1.0/sqrt(1.0-L*L*kpow*kpow);
const double d2 = d1*kpow;
const double d3 = d1*L*dkpow;
const DENS_VEC dphi (d2*dL + d3*dk);
const double sqrt_k=sqrt(k), cos_p3i=cos((1.0/3.0)*phi);
const double lam2 = (1.0/3.0)*(Ic + 2.0*sqrt_k*cos_p3i);
DENS_VEC dlam2 = (1.0/3.0)*dIc;
dlam2.add_scaled(dk, (1.0/3.0)*cos_p3i/sqrt_k);
dlam2.add_scaled(dphi, (-2.0/9.0)*sqrt_k*sin((1.0/3.0)*phi));
const double lambda = sqrt(lam2);
const DENS_VEC dlambda = (0.5/lambda)*dlam2;
const double IIIu = sqrt(IIIc);
const DENS_VEC dIIIu (0.5/IIIu*dIIIc);
const double Iu = lambda + sqrt(-lam2 + Ic + 2.0*IIIu/lambda);
const double invrt = 1.0/(Iu-lambda);
DENS_VEC dIu(dlambda); dIu *= 1.0 + invrt*(-lambda - IIIu/lam2);
dIu.add_scaled(dIc, 0.5*invrt);
dIu.add_scaled(dIIIu, invrt/lambda);
const double IIu = 0.5*(Iu*Iu - Ic);
const DENS_VEC dIIu ( Iu*dIu - 0.5*dIc );
const double fct = 1.0/(Iu*IIu-IIIu);
DENS_VEC dfct = dIu; dfct *= IIu;
dfct.add_scaled(dIIu, Iu);
dfct -= dIIIu;
dfct *= -fct*fct;
U = voigt3::eye(3, Iu*IIIu);
U.add_scaled(C, Iu*Iu-IIu);
U -= C2;
DENS_MAT da = tensor_product(I, dIu); da *= IIIu;
da.add_scaled(tensor_product(I, dIIIu), Iu);
da += tensor_product(C, 2.0*Iu*dIu-dIIu);
da.add_scaled(eye<double>(6,6),Iu*Iu-IIu);
da -= voigt3::derivative_of_square(C);
dU = tensor_product(U, dfct);
dU.add_scaled(da, fct);
U *= fct;
}
DENS_MAT compute_dynamical_matrix(const StressArgs &args)
{
DENS_MAT D(3,3);
for (INDEX a=0; a<args.vac.size(); a++) {
PairParam pair(args.vac.R(a), args.vac.r(a).norm());
pair.phi_r = args.potential->phi_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;
}
double compute_detD(const StressArgs &args)
{
return det(compute_dynamical_matrix(args));
}
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++) {
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;
}
}