#include "Stress.h" #include "CauchyBorn.h" #include "CBLattice.h" #include "CbLjCut.h" #include "CbLjSmoothLinear.h" #include "CbEam.h" #include "ATC_Error.h" #include "LammpsInterface.h" #include "VoigtOperations.h" #include using ATC_Utility::command_line; using ATC_Utility::str2dbl; using voigt3::voigt_idx1; using voigt3::voigt_idx2; using voigt3::to_voigt_unsymmetric; using voigt3::from_voigt_unsymmetric; using voigt3::to_voigt; using voigt3::from_voigt; using std::stringstream; using std::vector; using std::string; using std::fstream; namespace ATC { //============================================================================= // extracts a stress at an integration point // Note: Utility function: not in header //============================================================================= DENS_MAT extract_stress(const DENS_MAT_VEC &sigma, INDEX ip=0) { DENS_MAT s(3,3,false); for (int j=0; j<3; j++) for (int i=0; i<3; i++) s(i,j) = sigma[i](ip,j); return s; } //============================================================================= // computes the pressure from the stress at the first quadrature point (in atm) // Note: Utility function: not in header //============================================================================= double compute_pressure(const DENS_MAT_VEC &sigma, const DENS_MAT &F) { // pressure in units (mass-velocity^2)/Volume (LAMMPS real) double p = (sigma[0](0,0) + sigma[1](0,1) + sigma[2](0,2)) * (1.0/3.0); p *= 1.0e14/6.0221415; // convert from units real to Pa p *= 1.0/101235.0; // convert from Pa to ATM return p * pow(det(F), -1.0/3.0); // convert from PK2 to Cauchy stress } //============================================================================= // extracts the deformation gradient at a quadrature point, q // Note: Utility function: not in header //============================================================================= void deformation_gradient(const DENS_MAT_VEC &du, INDEX q, MATRIX &F) { F.reset(du.size(), du.size(), false); for (INDEX j=0; jsecond)[0]).nRows(); energy.reset(nRows,1); ATC::LammpsInterface::instance()->print_msg("WARNING: returning dummy elastic energy"); } //============================================================================= // isotropic linear elastic //============================================================================= StressLinearElastic::StressLinearElastic(fstream &fileId) : StressCubicElastic(), E_(0), nu_(0), mu_(0), lambda_(0) { if (!fileId.is_open()) throw ATC_Error("cannot open material file"); vector line; while(fileId.good()) { command_line(fileId, line); if (line[0] == "end") { mu_ = E_/(2.0+2.0*nu_); lambda_ = mu_*nu_ / (0.5 - nu_); StressCubicElastic::c11_ = E_*(1-nu_)/(1+nu_)/(1-2*nu_); StressCubicElastic::c12_ = E_*nu_ /(1+nu_)/(1-2*nu_); StressCubicElastic::c44_ = E_/(1+nu_)/2; if (nu_ < 0.0 || nu_ > 1.0) throw ATC_Error("bad linear elastic constants"); if (lambda_ < 0.0 || mu_ < 0.0) throw ATC_Error("bad continuum material parameter"); return; } else if (line[0]=="modulus") E_ = str2dbl(line[1]); else if (line[0]=="possions_ratio") nu_ = str2dbl(line[1]); else throw ATC_Error( "unrecognized material function"); } } //============================================================================= // compute the stress at N integration points from the displacement gradient // T_{ij} = 1/2*C_{ijkl}* (u_{k,l} + u_{l,k}) //============================================================================= void StressLinearElastic::stress(const FIELD_MATS &fields, const GRAD_FIELD_MATS &gradFields, DENS_MAT_VEC &sigma) { GRAD_FIELD_MATS::const_iterator du_itr = gradFields.find(DISPLACEMENT); const DENS_MAT_VEC &du = du_itr->second; CLON_VEC uxx(du[0],CLONE_COL,0); CLON_VEC uxy(du[1],CLONE_COL,0); CLON_VEC uxz(du[2],CLONE_COL,0); CLON_VEC uyx(du[0],CLONE_COL,1); CLON_VEC uyy(du[1],CLONE_COL,1); CLON_VEC uyz(du[2],CLONE_COL,1); CLON_VEC uzx(du[0],CLONE_COL,2); CLON_VEC uzy(du[1],CLONE_COL,2); CLON_VEC uzz(du[2],CLONE_COL,2); const INDEX N = uxx.size(); // # of integration pts sigma.assign(3, DENS_MAT(N,3)); // precompute the pressure and copy to the diagonal column(sigma[0],0) = (uxx + uyy + uzz)*(-lambda_); column(sigma[1],1) = column(sigma[0],0); column(sigma[2],2) = column(sigma[0],0); column(sigma[0],0) -= 2.0*mu_*uxx; column(sigma[0],1) = (uxy + uyx)*(-mu_); column(sigma[0],2) = (uxz + uzx)*(-mu_); column(sigma[1],0) = column(sigma[0],1); column(sigma[1],1) -= 2.0*mu_*uyy; column(sigma[1],2) = (uyz + uzy)*(-mu_); column(sigma[2],0) = column(sigma[0],2); column(sigma[2],1) = column(sigma[1],2); column(sigma[2],2) -= 2.0*mu_*uzz; } //============================================================================= // cubic elastic //============================================================================= StressCubicElastic::StressCubicElastic(fstream &fileId) : c11_(0), c12_(0), c44_(0) { if (!fileId.is_open()) throw ATC_Error("cannot open material file"); vector line; while(fileId.good()) { command_line(fileId, line); if (line[0]=="end") return; else if (line[0]=="c11") c11_ = str2dbl(line[1]); else if (line[0]=="c12") c12_ = str2dbl(line[1]); else if (line[0]=="c44") c44_ = str2dbl(line[1]); else throw ATC_Error( "unrecognized material function"); } } //--------------------------------------------------------------------------- // compute the stress at N integration points from the displacement gradient // T_{ij} = 1/2*C_{ijkl}*(u_{k,l} + u_{l,k}) //--------------------------------------------------------------------------- void StressCubicElastic::stress(const FIELD_MATS &fields, const GRAD_FIELD_MATS &gradFields, DENS_MAT_VEC &sigma) { GRAD_FIELD_MATS::const_iterator du_itr = gradFields.find(DISPLACEMENT); const DENS_MAT_VEC &du = du_itr->second; CLON_VEC uxx(du[0],CLONE_COL,0); CLON_VEC uxy(du[1],CLONE_COL,0); CLON_VEC uxz(du[2],CLONE_COL,0); CLON_VEC uyx(du[0],CLONE_COL,1); CLON_VEC uyy(du[1],CLONE_COL,1); CLON_VEC uyz(du[2],CLONE_COL,1); CLON_VEC uzx(du[0],CLONE_COL,2); CLON_VEC uzy(du[1],CLONE_COL,2); CLON_VEC uzz(du[2],CLONE_COL,2); const INDEX N = uxx.size(); // # of integration pts sigma.assign(3, DENS_MAT(N,3)); const double c12 = c12_; const double c11 = c11_; const double c44 = c44_; // scaling: stress must return (-) stress column(sigma[0],0) = -c11*uxx - c12*(uyy+uzz); column(sigma[1],1) = -c11*uyy - c12*(uxx+uzz); column(sigma[2],2) = -c11*uzz - c12*(uxx+uyy); column(sigma[0],1) = -c44*(uxy+uyx); column(sigma[1],0) = column(sigma[0],1); column(sigma[0],2) = -c44*(uxz+uzx); column(sigma[2],0) = column(sigma[0],2); column(sigma[1],2) = -c44*(uyz+uzy); column(sigma[2],1) = column(sigma[1],2); } //--------------------------------------------------------------------------- // compute the elastic energy at N integration points from displacement gradient // E = 1/8*C_{ijkl}* (u_{k,l} + u_{l,k})* (u_{i,j} + u_{j,i})*rho ? // = 1/2 (4 c44 (u12^2 + u13^2 + u23^2) + 2 c12 (u11 u22 + u11 u33 + u22 u33) // + c11 (u11^2 + u22^2 + u33^2)) //--------------------------------------------------------------------------- void StressCubicElastic::elastic_energy(const FIELD_MATS &fields, const GRAD_FIELD_MATS &gradFields, DENS_MAT &energy) const { GRAD_FIELD_MATS::const_iterator du_itr = gradFields.find(DISPLACEMENT); const DENS_MAT_VEC &du = du_itr->second; CLON_VEC uxx(du[0],CLONE_COL,0); CLON_VEC uxy(du[1],CLONE_COL,0); CLON_VEC uxz(du[2],CLONE_COL,0); CLON_VEC uyx(du[0],CLONE_COL,1); CLON_VEC uyy(du[1],CLONE_COL,1); CLON_VEC uyz(du[2],CLONE_COL,1); CLON_VEC uzx(du[0],CLONE_COL,2); CLON_VEC uzy(du[1],CLONE_COL,2); CLON_VEC uzz(du[2],CLONE_COL,2); CLON_VEC E(energy,CLONE_COL,0); const double c12 = c12_; const double c11 = c11_; const double c44 = c44_; //double scale = (ATC::LammpsInterface::instance()->mvv2e()); for (INDEX gp=0; gp line; while(fileId.good()) { command_line(fileId, line); if (line[0]=="end") return; else if (line[0]=="c11") StressCubicElastic::c11_ = str2dbl(line[1]); else if (line[0]=="c12") StressCubicElastic::c12_ = str2dbl(line[1]); else if (line[0]=="c44") StressCubicElastic::c44_ = str2dbl(line[1]); else if (line[0]=="gamma") gamma_ = str2dbl(line[1]); else throw ATC_Error( "unrecognized material function"); } } //--------------------------------------------------------------------------- // compute the stress at N integration points //--------------------------------------------------------------------------- void StressCubicElasticDamped::stress(const FIELD_MATS &fields, const GRAD_FIELD_MATS &gradFields, DENS_MAT_VEC &sigma) { StressCubicElastic::stress(fields,gradFields,sigma); GRAD_FIELD_MATS::const_iterator dv_itr = gradFields.find(VELOCITY); const DENS_MAT_VEC &dv = dv_itr->second; CLON_VEC vxx(dv[0],CLONE_COL,0); CLON_VEC vxy(dv[1],CLONE_COL,0); CLON_VEC vxz(dv[2],CLONE_COL,0); CLON_VEC vyx(dv[0],CLONE_COL,1); CLON_VEC vyy(dv[1],CLONE_COL,1); CLON_VEC vyz(dv[2],CLONE_COL,1); CLON_VEC vzx(dv[0],CLONE_COL,2); CLON_VEC vzy(dv[1],CLONE_COL,2); CLON_VEC vzz(dv[2],CLONE_COL,2); // scaling: stress must return (-) stress column(sigma[0],0) += -gamma_*vxx; column(sigma[1],1) += -gamma_*vyy; column(sigma[2],2) += -gamma_*vzz; column(sigma[0],1) += -0.5*gamma_*(vxy+vyx); column(sigma[1],0) += column(sigma[0],1); column(sigma[0],2) += -0.5*gamma_*(vxz+vzx); column(sigma[2],0) += column(sigma[0],2); column(sigma[1],2) += -0.5*gamma_*(vyz+vzy); column(sigma[2],1) += column(sigma[1],2); } //============================================================================== // cauchy born model //============================================================================== StressCauchyBorn::StressCauchyBorn(fstream &fileId, CbData &cb) : cblattice_(NULL), potential_(NULL), makeLinear_(false), cubicMat_(NULL), initialized_(false), fixed_temperature_(0.), cbdata_(cb) { if (!fileId.is_open()) throw ATC_Error("cannot open material file"); while(fileId.good()) { // reads a line from the material file vector line; command_line(fileId, line); if (line.empty()) continue; // skip blank lines else if (line[0]=="end") { delete cblattice_; if (!potential_) throw ATC_Error( "no potential defined"); cblattice_ = new CBLattice(cbdata_.cell_vectors, cbdata_.basis_vectors); return; } else if (line[0] == "pair_style") { if (line[1] == "lj/cut") { // Lennard-Jones w/ cutoff radius if (line.size()<3) throw(ATC_Error("no lj/cut cutoff radius")); const double rc = str2dbl(line[2]); while (!fileId.eof()) { // find next pair_coeff command command_line(fileId, line); if (line.size() && line[0]=="pair_coeff") break; } if (line[0] != "pair_coeff" || line.size() != 3) { throw(ATC_Error("lj/cut needs 2 coefficents")); } delete potential_; potential_ = new CbLjCut(str2dbl(line[1]), str2dbl(line[2]), rc); } else if (line[1] == "lj/smooth/linear") { // Lennard-Jones w/ cutoff radius and smoothed if (line.size()<3) throw(ATC_Error("no lj/smooth/linear cutoff radius")); const double rc = str2dbl(line[2]); while (!fileId.eof()) { // find next pair_coeff command command_line(fileId, line); if (line.size() && line[0]=="pair_coeff") break; } if (line[0] != "pair_coeff" || line.size() != 3) { throw(ATC_Error("lj/smooth/linear needs 2 coefficents")); } delete potential_; potential_ = new CbLjSmoothLinear(str2dbl(line[1]), str2dbl(line[2]), rc); } else if (line[1] == "eam") { // Embedded atom method potential delete potential_; potential_ = new CbEam(); } else throw (ATC_Error("Invalid pair style")); } else if (line[0] == "linear") makeLinear_ = true; else if (line[0] == "temperature" && line.size() == 2) { fixed_temperature_ = str2dbl(line[1]); } else if (line[0]=="material" || line[0]=="stress") /* ignore this */; else throw ATC_Error( "Unrecognized Cauchy-Born parameter: "+line[0]+"."); } } //============================================================================== //* default destructor - delete potential and lattice //============================================================================== StressCauchyBorn::~StressCauchyBorn() { if (potential_) delete potential_; if (cblattice_) delete cblattice_; if (cubicMat_) delete cubicMat_; } //============================================================================== // initialize //============================================================================== void StressCauchyBorn::initialize(void) { if (!initialized_) { if (makeLinear_) linearize(); stringstream ss; double k = stiffness()*cbdata_.e2mvv; double m = cbdata_.atom_mass; double w0 = sqrt(k*m); ss << "CB stiffness: " << stiffness() << " Einstein freq: " << w0; ATC::LammpsInterface::instance()->print_msg_once(ss.str()); initialized_ = true; } } //============================================================================== // compute the bond stiffness consistent with the einstein freq //============================================================================== double StressCauchyBorn::stiffness(void) const { AtomCluster vac; cblattice_->atom_cluster(eye(3,3), potential_->cutoff_radius(), vac); DENS_MAT k = vac.force_constants(0,potential_); return k(0,0); } //============================================================================== // compute the stress at N integration points from the displacement gradient //============================================================================== void StressCauchyBorn::stress(const FIELD_MATS &fields, const GRAD_FIELD_MATS &gradFields, DENS_MAT_VEC &sigma) { if (cubicMat_) { cubicMat_->stress(fields, gradFields, sigma); return; } FIELD_MATS::const_iterator temp = fields.find(TEMPERATURE); GRAD_FIELD_MATS::const_iterator disp_gradient = gradFields.find(DISPLACEMENT); // Scaling factor - scale by atomic volume and energy conversion. // negative because stress must return (-) stress const double fact = -cbdata_.inv_atom_volume * cbdata_.e2mvv; const DENS_MAT_VEC &du(disp_gradient->second); const INDEX num_integration_pts = du.front().nRows(); const INDEX nsd = du.size(); DENS_MAT F(nsd,nsd); // displacement gradient bool temp_varies = (temp != fields.end()); sigma.assign(nsd, DENS_MAT(num_integration_pts, nsd)); StressAtIP S(sigma); // wrapper for quadrature points. AtomCluster vac; for (INDEX gp=0; gpatom_cluster(F, potential_->cutoff_radius(), vac); // Get temperature (assume 0K if no temperature field is present). const double T = (temp_varies ? temp->second[gp] : fixed_temperature_); // Computes the cauchy-born stresses. const StressArgs args(vac, potential_, cbdata_.boltzmann, cbdata_.hbar, T); cb_stress(args, S); // copy symmetric part of stress and scale by V0 for (INDEX i=0; i0)/potential(T=0) energy density. [mvv/L^3] //============================================================================== void StressCauchyBorn::elastic_energy(const FIELD_MATS &fields, const GRAD_FIELD_MATS &gradFields, DENS_MAT &energy) const { if (cubicMat_) { cubicMat_->elastic_energy(fields, gradFields, energy); return; } FIELD_MATS::const_iterator temp = fields.find(TEMPERATURE); GRAD_FIELD_MATS::const_iterator disp_gradient = gradFields.find(DISPLACEMENT); const DENS_MAT_VEC &du(disp_gradient->second); DENS_MAT F(du.size(),du.size()); AtomCluster vac; for (INDEX gp=0; gpatom_cluster(F, potential_->cutoff_radius(), vac); double T = (temp!=fields.end() ? temp->second[gp] : fixed_temperature_); energy[gp] = cb_energy(StressArgs(vac, potential_, cbdata_.boltzmann, cbdata_.hbar, T)); } // Scaling factor - scale by atomic volume and energy conversion. energy *= cbdata_.inv_atom_volume * cbdata_.e2mvv; } //============================================================================== // Computes entropic energy density. [mvv/L^3] //============================================================================== void StressCauchyBorn::entropic_energy(const FIELD_MATS &fields, const GRAD_FIELD_MATS &gradFields, DENS_MAT &energy) const { FIELD_MATS::const_iterator temp = fields.find(TEMPERATURE); GRAD_FIELD_MATS::const_iterator disp_gradient = gradFields.find(DISPLACEMENT); const DENS_MAT_VEC &du(disp_gradient->second); DENS_MAT F(du.size(),du.size()); AtomCluster vac; for (INDEX gp=0; gpatom_cluster(F, potential_->cutoff_radius(), vac); double T = (temp!=fields.end() ? temp->second[gp] : fixed_temperature_); energy[gp] = cb_entropic_energy(StressArgs(vac, potential_, cbdata_.boltzmann, cbdata_.hbar, T)); } // Scaling factor - scale by atomic volume and energy conversion. energy *= cbdata_.inv_atom_volume * cbdata_.e2mvv; } //============================================================================== // creates a linearization for a deformation gradient //============================================================================== void StressCauchyBorn::linearize(MATRIX *F) { if (cubicMat_) delete cubicMat_; DENS_MAT C; if (F) tangent(*F, C); else tangent(eye(3,3), C); cubicMat_ = new StressCubicElastic(C(0,0), C(0,1), C(3,3)); stringstream ss; double c11 = C(0,0)/cbdata_.e2mvv; double c12 = C(0,1)/cbdata_.e2mvv; double c44 = C(3,3)/cbdata_.e2mvv; ss << "created cubic stress function:" << "\n lammps ATC units" << "\n c11=" << c11 << " " << C(0,0) << "\n c12=" << c12 << " " << C(0,1) << "\n c44=" << c44 << " " << C(3,3); ATC::LammpsInterface::instance()->print_msg_once(ss.str()); } //============================================================================== // sets C as the material tangent modulus, given deformation gradient F //============================================================================== // Note: C is dS/dC which is 1/2 dS/dF_sym void StressCauchyBorn::tangent(const MATRIX &F, MATRIX &C) const { if (cubicMat_) { cubicMat_->tangent(F,C); return; } elasticity_tensor(F,C); } //============================================================================== // 1st elasticity tensor : B = dP/dF = C F F + S I ( 9 x 9 in Voigt notation) // 2nd elasticity tensor : C = dS/dE ( 6 x 6 in Voigt notation) //============================================================================== DENS_VEC StressCauchyBorn::elasticity_tensor(const VECTOR &Fv, MATRIX &C, const ElasticityTensorType type) const { DENS_MAT F; if (Fv.nRows()==9) { F = from_voigt_unsymmetric(Fv); } else { F = from_voigt(Fv); } return elasticity_tensor(F, C,type); } DENS_VEC StressCauchyBorn::elasticity_tensor(const MATRIX &F, MATRIX &C, const ElasticityTensorType type) const { double T = 0; AtomCluster vac; cblattice_->atom_cluster(F, potential_->cutoff_radius(), vac); if (vac.size() < 4) throw ATC_Error("StressCauchyBorn::second_elasticity_tensor cluster does not have sufficient atoms"); const StressArgs args(vac, potential_, cbdata_.boltzmann, cbdata_.hbar, T); // if using EAM potential, calculate embedding function and derivatives bool hasEAM = potential_->terms.embedding; double embed_p = 0; double embed_pp = 0; if (hasEAM) { double e_density = cb_electron_density(args); embed_p = potential_->F_p(e_density); // "F" in usual EAM symbology embed_pp = potential_->F_pp(e_density); } int size = 6; if (type == FIRST_ELASTICITY_TENSOR) { size = 9; } DENS_VEC Z(size), S(size), Zfp(size); Zfp = 0; C.reset(size,size); for (INDEX a=0; aphi_r(d); // computes phi' double phi_rr = potential_->phi_rr(d); // computes phi'' double fact1 = 0.5*phi_r*rinv; // 1/2 see Philips double fact2 = 0.5*(phi_rr - phi_r*rinv) * rinv*rinv; if (hasEAM) { double rho_r = potential_->rho_r(d); // computes rho' double rho_rr = potential_->rho_rr(d); // computes rho'' fact1 += embed_p*rho_r*rinv; fact2 += embed_p*(rho_rr - rho_r*rinv) * rinv*rinv; Zfp += Z*(rho_r*rinv); } for (INDEX i=0; i