RankFourTensor TensorMechanicsPlasticOrthotropic::dflowPotential_dstress(const RankTwoTensor & stress, Real /*intnl*/) const { if (_associative) { const RankTwoTensor j2prime = _l1 * stress; const RankTwoTensor j3prime = _l2 * stress; const RankTwoTensor dj2 = dj2_dSkl(j2prime); const RankTwoTensor dj3 = j3prime.ddet(); const Real j2 = -j2prime.generalSecondInvariant(); const Real j3 = j3prime.det(); const RankFourTensor dr = dfj2_dj2(j2, j3) * _l1.innerProductTranspose(dj2).outerProduct(_l1.innerProductTranspose(dj2)) + dfj2_dj3(j2, j3) * _l1.innerProductTranspose(dj2).outerProduct(_l2.innerProductTranspose(dj3)) + dfj3_dj2(j2, j3) * _l2.innerProductTranspose(dj3).outerProduct(_l1.innerProductTranspose(dj2)) + dfj3_dj3(j2, j3) * _l2.innerProductTranspose(dj3).outerProduct(_l2.innerProductTranspose(dj3)); const RankTwoTensor r = _b * dI_sigma() + dphi_dj2(j2, j3) * _l1.innerProductTranspose(dj2_dSkl(j2prime)) + dphi_dj3(j2, j3) * _l2.innerProductTranspose(j3prime.ddet()); const Real norm = r.L2norm(); return dr / norm - (r / Utility::pow<3>(norm)).outerProduct(dr.innerProductTranspose(r)); } else return TensorMechanicsPlasticJ2::dflowPotential_dstress(stress, 0); }
void StressDivergenceTensors::computeFiniteDeformJacobian() { const RankTwoTensor I(RankTwoTensor::initIdentity); const RankFourTensor II_ijkl = I.mixedProductIkJl(I); // Bring back to unrotated config const RankTwoTensor unrotated_stress = (*_rotation_increment)[_qp].transpose() * _stress[_qp] * (*_rotation_increment)[_qp]; // Incremental deformation gradient Fhat const RankTwoTensor Fhat = (*_deformation_gradient)[_qp] * (*_deformation_gradient_old)[_qp].inverse(); const RankTwoTensor Fhatinv = Fhat.inverse(); const RankTwoTensor rot_times_stress = (*_rotation_increment)[_qp] * unrotated_stress; const RankFourTensor dstress_drot = I.mixedProductIkJl(rot_times_stress) + I.mixedProductJkIl(rot_times_stress); const RankFourTensor rot_rank_four = (*_rotation_increment)[_qp].mixedProductIkJl((*_rotation_increment)[_qp]); const RankFourTensor drot_dUhatinv = Fhat.mixedProductIkJl(I); const RankTwoTensor A = I - Fhatinv; // Ctilde = Chat^-1 - I const RankTwoTensor Ctilde = A * A.transpose() - A - A.transpose(); const RankFourTensor dCtilde_dFhatinv = -I.mixedProductIkJl(A) - I.mixedProductJkIl(A) + II_ijkl + I.mixedProductJkIl(I); // Second order approximation of Uhat - consistent with strain increment definition // const RankTwoTensor Uhat = I - 0.5 * Ctilde - 3.0/8.0 * Ctilde * Ctilde; RankFourTensor dUhatinv_dCtilde = 0.5 * II_ijkl - 1.0 / 8.0 * (I.mixedProductIkJl(Ctilde) + Ctilde.mixedProductIkJl(I)); RankFourTensor drot_dFhatinv = drot_dUhatinv * dUhatinv_dCtilde * dCtilde_dFhatinv; drot_dFhatinv -= Fhat.mixedProductIkJl((*_rotation_increment)[_qp].transpose()); _finite_deform_Jacobian_mult[_qp] = dstress_drot * drot_dFhatinv; const RankFourTensor dstrain_increment_dCtilde = -0.5 * II_ijkl + 0.25 * (I.mixedProductIkJl(Ctilde) + Ctilde.mixedProductIkJl(I)); _finite_deform_Jacobian_mult[_qp] += rot_rank_four * _Jacobian_mult[_qp] * dstrain_increment_dCtilde * dCtilde_dFhatinv; _finite_deform_Jacobian_mult[_qp] += Fhat.mixedProductJkIl(_stress[_qp]); const RankFourTensor dFhat_dFhatinv = -Fhat.mixedProductIkJl(Fhat.transpose()); const RankTwoTensor dJ_dFhatinv = dFhat_dFhatinv.innerProductTranspose(Fhat.ddet()); // Component from Jacobian derivative _finite_deform_Jacobian_mult[_qp] += _stress[_qp].outerProduct(dJ_dFhatinv); // Derivative of Fhatinv w.r.t. undisplaced coordinates const RankTwoTensor Finv = (*_deformation_gradient)[_qp].inverse(); const RankFourTensor dFhatinv_dGradu = -Fhatinv.mixedProductIkJl(Finv.transpose()); _finite_deform_Jacobian_mult[_qp] = _finite_deform_Jacobian_mult[_qp] * dFhatinv_dGradu; }
// Jacobian for stress update algorithm void FiniteStrainPlasticMaterial::getJac(const RankTwoTensor & sig, const RankFourTensor & E_ijkl, Real flow_incr, RankFourTensor & dresid_dsig) { RankTwoTensor sig_dev, df_dsig, flow_dirn; RankTwoTensor dfi_dft, dfi_dsig; RankFourTensor dft_dsig, dfd_dft, dfd_dsig; Real sig_eqv; Real f1, f2, f3; RankFourTensor temp; sig_dev = sig.deviatoric(); sig_eqv = getSigEqv(sig); df_dsig = dyieldFunction_dstress(sig); flow_dirn = flowPotential(sig); f1 = 3.0 / (2.0 * sig_eqv); f2 = f1 / 3.0; f3 = 9.0 / (4.0 * Utility::pow<3>(sig_eqv)); dft_dsig = f1 * _deltaMixed - f2 * _deltaOuter - f3 * sig_dev.outerProduct(sig_dev); dfd_dsig = dft_dsig; dresid_dsig = E_ijkl.invSymm() + dfd_dsig * flow_incr; }
//Jacobian for stress update algorithm void FiniteStrainPlasticMaterial::getJac(RankTwoTensor sig, RankFourTensor E_ijkl, Real flow_incr, RankFourTensor* dresid_dsig) { RankTwoTensor sig_dev, flow_tensor, flow_dirn; RankTwoTensor dfi_dft,dfi_dsig; RankFourTensor dft_dsig,dfd_dft,dfd_dsig; Real sig_eqv/*,val*/; Real f1,f2,f3; RankFourTensor temp; sig_dev=getSigDev(sig); sig_eqv=getSigEqv(sig); getFlowTensor(sig,&flow_tensor); flow_dirn=flow_tensor; f1=3.0/(2.0*sig_eqv); f2=f1/3.0; f3=9.0/(4.0*pow(sig_eqv,3.0)); for (int i=0;i<3;i++) for (int j=0;j<3;j++) for (int k=0;k<3;k++) for (int l=0;l<3;l++) dft_dsig(i,j,k,l)=f1*deltaFunc(i,k)*deltaFunc(j,l)-f2*deltaFunc(i,j)*deltaFunc(k,l)-f3*sig_dev(i,j)*sig_dev(k,l); dfd_dsig=dft_dsig; *dresid_dsig=E_ijkl.invSymm()+dfd_dsig*flow_incr; }
//Jacobian for stress update algorithm void FiniteStrainRatePlasticMaterial::getJac(const RankTwoTensor & sig, const RankFourTensor & E_ijkl, Real flow_incr, Real yield_stress, RankFourTensor & dresid_dsig) { unsigned i, j, k ,l; RankTwoTensor sig_dev, flow_tensor, flow_dirn,fij; RankTwoTensor dfi_dft; RankFourTensor dft_dsig, dfd_dft, dfd_dsig, dfi_dsig; Real sig_eqv; Real f1, f2, f3; Real dfi_dseqv; sig_dev = sig.deviatoric(); sig_eqv = getSigEqv(sig); getFlowTensor(sig, yield_stress, flow_tensor); flow_dirn = flow_tensor; dfi_dseqv = _ref_pe_rate * _dt * _exponent * std::pow(macaulayBracket(sig_eqv / yield_stress - 1.0), _exponent - 1.0) / yield_stress; for (i = 0; i < 3; ++i) for (j = 0; j < 3; ++j) for (k = 0; k < 3; ++k) for (l = 0; l < 3; ++l) dfi_dsig(i,j,k,l) = flow_dirn(i,j) * flow_dirn(k,l) * dfi_dseqv; //d_flow_increment/d_sig f1 = 0.0; f2 = 0.0; f3 = 0.0; if (sig_eqv > 1e-8) { f1 = 3.0 / (2.0 * sig_eqv); f2 = f1 / 3.0; f3 = 9.0 / (4.0 * std::pow(sig_eqv, 3.0)); } for (i = 0; i < 3; ++i) for (j = 0; j < 3; ++j) for (k = 0; k < 3; ++k) for (l = 0; l < 3; ++l) dft_dsig(i,j,k,l) = f1 * deltaFunc(i,k) * deltaFunc(j,l) - f2 * deltaFunc(i,j) * deltaFunc(k,l) - f3 * sig_dev(i,j) * sig_dev(k,l); //d_flow_dirn/d_sig - 2nd part dfd_dsig = dft_dsig; //d_flow_dirn/d_sig dresid_dsig = E_ijkl.invSymm() + dfd_dsig * flow_incr + dfi_dsig; //Jacobian }
//Jacobian for stress update algorithm void FiniteStrainRatePlasticMaterial::getJac(const RankTwoTensor & sig, const RankFourTensor & E_ijkl, Real flow_incr, Real yield_stress, RankFourTensor & dresid_dsig) { RankTwoTensor sig_dev, flow_tensor, flow_dirn,fij; RankTwoTensor dfi_dft; RankFourTensor dfd_dft, dfd_dsig, dfi_dsig; Real sig_eqv; Real f1, f2, f3; Real dfi_dseqv; sig_dev = sig.deviatoric(); sig_eqv = getSigEqv(sig); getFlowTensor(sig, yield_stress, flow_tensor); flow_dirn = flow_tensor; dfi_dseqv = _ref_pe_rate * _dt * _exponent * std::pow(macaulayBracket(sig_eqv / yield_stress - 1.0), _exponent - 1.0) / yield_stress; dfi_dsig = flow_dirn.outerProduct(flow_dirn) * dfi_dseqv; //d_flow_increment/d_sig f1 = 0.0; f2 = 0.0; f3 = 0.0; if (sig_eqv > 1e-8) { f1 = 3.0 / (2.0 * sig_eqv); f2 = f1 / 3.0; f3 = 9.0 / (4.0 * std::pow(sig_eqv, 3.0)); } dfd_dsig = f1 * _deltaMixed - f2 * _deltaOuter - f3 * sig_dev.outerProduct(sig_dev); //d_flow_dirn/d_sig - 2nd part //Jacobian dresid_dsig = E_ijkl.invSymm() + dfd_dsig * flow_incr + dfi_dsig; }
void RedbackMechMaterialCC::getJac(const RankTwoTensor & sig, const RankFourTensor & E_ijkl, Real flow_incr, Real sig_eqv, Real pressure, Real p_yield_stress, Real q_yield_stress, Real pc, RankFourTensor & dresid_dsig) { unsigned i, j, k, l; RankTwoTensor sig_dev, flow_dirn_vol, flow_dirn_dev, fij, flow_dirn, flow_tensor; RankTwoTensor dfi_dft; RankFourTensor dfd_dsig, dfi_dsig; Real f1, f2; Real dfi_dseqv_dev, dfi_dseqv_vol, dfi_dseqv; pc *= -1; sig_dev = sig.deviatoric(); dfi_dseqv = getDerivativeFlowIncrement(sig, pressure, sig_eqv, pc, q_yield_stress, p_yield_stress); getFlowTensor(sig, sig_eqv, pressure, pc, flow_dirn); /* The following calculates the tensorial derivative (Jacobian) of the residual with respect to stress, dr_dsig * It consists of two terms: The first is * dr_dsig = (dfi_dseqv_dev*flow_dirn_dev(k,l)) * flow_dirn_dev(i,j) * which is the tensorial product of the flow increment tensor times the flow direction tensor * * The second is the product of the flow increment tensor times the derivative of the flow direction tensor * with respect to the stress tensor. See also REDBACK's documentation * */ // This loop calculates the first term for (i = 0; i < 3; ++i) for (j = 0; j < 3; ++j) for (k = 0; k < 3; ++k) for (l = 0; l < 3; ++l) dfi_dsig(i, j, k, l) = flow_dirn(i, j) * flow_dirn(k, l) * dfi_dseqv; // Real flow_tensor_norm = flow_dirn.L2norm(); // This loop calculates the second term. Read REDBACK's documentation // (same as J2 plasticity case) f1 = 0.0; f2 = 0.0; if (sig_eqv > 1e-8) { f1 = 3.0 / (_slope_yield_surface * _slope_yield_surface); f2 = 2.0 / 9.0 - 1.0 / (_slope_yield_surface * _slope_yield_surface); } for (i = 0; i < 3; ++i) for (j = 0; j < 3; ++j) for (k = 0; k < 3; ++k) for (l = 0; l < 3; ++l) dfd_dsig(i, j, k, l) = f1 * deltaFunc(i, k) * deltaFunc(j, l) - f2 * deltaFunc(i, j) * deltaFunc(k, l); // d_flow_dirn/d_sig - 2nd part (J2 plasticity) // dfd_dsig = dft_dsig1/flow_tensor_norm - 3.0 * dft_dsig2 / // (2*sig_eqv*flow_tensor_norm*flow_tensor_norm*flow_tensor_norm); //d_flow_dirn/d_sig // TODO: check if the previous two lines (i.e normalizing the flow vector) should be activated or not. Currently we // are using the non-unitary flow vector dresid_dsig = E_ijkl.invSymm() + dfd_dsig * flow_incr + dfi_dsig; // Jacobian }
void CappedDruckerPragerCosseratStressUpdate::consistentTangentOperator( const RankTwoTensor & /*stress_trial*/, Real /*p_trial*/, Real /*q_trial*/, const RankTwoTensor & stress, Real /*p*/, Real q, Real gaE, const yieldAndFlow & smoothed_q, const RankFourTensor & Eijkl, bool compute_full_tangent_operator, RankFourTensor & cto) const { if (!compute_full_tangent_operator) { cto = Eijkl; return; } RankFourTensor EAijkl; for (unsigned i = 0; i < _tensor_dimensionality; ++i) for (unsigned j = 0; j < _tensor_dimensionality; ++j) for (unsigned k = 0; k < _tensor_dimensionality; ++k) for (unsigned l = 0; l < _tensor_dimensionality; ++l) { cto(i, j, k, l) = 0.5 * (Eijkl(i, j, k, l) + Eijkl(j, i, k, l)); EAijkl(i, j, k, l) = 0.5 * (Eijkl(i, j, k, l) - Eijkl(j, i, k, l)); } const RankTwoTensor s_over_q = (q == 0.0 ? RankTwoTensor() : (0.5 * (stress + stress.transpose()) - stress.trace() * RankTwoTensor(RankTwoTensor::initIdentity) / 3.0) / q); const RankTwoTensor E_s_over_q = Eijkl.innerProductTranspose(s_over_q); // not symmetric in kl const RankTwoTensor Ekl = RankTwoTensor(RankTwoTensor::initIdentity).initialContraction(Eijkl); // symmetric in kl for (unsigned i = 0; i < _tensor_dimensionality; ++i) for (unsigned j = 0; j < _tensor_dimensionality; ++j) for (unsigned k = 0; k < _tensor_dimensionality; ++k) for (unsigned l = 0; l < _tensor_dimensionality; ++l) { cto(i, j, k, l) -= (i == j) * (1.0 / 3.0) * (Ekl(k, l) * (1.0 - _dp_dpt) + 0.5 * E_s_over_q(k, l) * (-_dp_dqt)); cto(i, j, k, l) -= s_over_q(i, j) * (Ekl(k, l) * (-_dq_dpt) + 0.5 * E_s_over_q(k, l) * (1.0 - _dq_dqt)); } if (smoothed_q.dg[1] != 0.0) { const RankFourTensor Tijab = _Ehost * (gaE / _Epp) * smoothed_q.dg[1] * d2qdstress2(stress); RankFourTensor inv = RankFourTensor(RankFourTensor::initIdentitySymmetricFour) + Tijab; try { inv = inv.transposeMajor().invSymm(); } catch (const MooseException & e) { // Cannot form the inverse, so probably at some degenerate place in stress space. // Just return with the "best estimate" of the cto. mooseWarning("CappedDruckerPragerCosseratStressUpdate: Cannot invert 1+T in consistent " "tangent operator computation at quadpoint ", _qp, " of element ", _current_elem->id()); return; } cto = (cto.transposeMajor() * inv).transposeMajor(); } cto += EAijkl; }
/** * Implements the return-map algorithm via a Newton-Raphson process. * This idea is fully explained in Simo and Hughes "Computational * Inelasticity" Springer 1997, for instance, as well as many other * books on plasticity. * The basic idea is as follows. * Given: sig_old - the stress at the start of the "time step" * plastic_strain_old - the plastic strain at the start of the "time step" * eqvpstrain_old - equivalent plastic strain at the start of the "time step" * (In general we would be given some number of internal * parameters that the yield function depends upon.) * delta_d - the prescribed strain increment for this "time step" * we want to determine the following parameters at the end of this "time step": * sig - the stress * plastic_strain - the plastic strain * eqvpstrain - the equivalent plastic strain (again, in general, we would * have an arbitrary number of internal parameters). * * To determine these parameters, introduce * the "yield function", f * the "consistency parameter", flow_incr * the "flow potential", flow_dirn_ij * the "internal potential", internalPotential (in general there are as many internalPotential * functions as there are internal parameters). * All three of f, flow_dirn_ij, and internalPotential, are functions of * sig and eqvpstrain. * To find sig, plastic_strain and eqvpstrain, we need to solve the following * resid_ij = 0 * f = 0 * rep = 0 * This is done by using Newton-Raphson. * There are 8 equations here: six from resid_ij=0 (more generally there are nine * but in this case resid is symmetric); one from f=0; one from rep=0 (more generally, for N * internal parameters there are N of these equations). * * resid_ij = flow_incr*flow_dirn_ij - (plastic_strain - plastic_strain_old)_ij * = flow_incr*flow_dirn_ij - (E^{-1}(trial_stress - sig))_ij * Here trial_stress = E*(strain - plastic_strain_old) * sig = E*(strain - plastic_strain) * Note: flow_dirn_ij is evaluated at sig and eqvpstrain (not the old values). * * f is the yield function, evaluated at sig and eqvpstrain * * rep = -flow_incr*internalPotential - (eqvpstrain - eqvpstrain_old) * Here internalPotential are evaluated at sig and eqvpstrain (not the old values). * * The Newton-Raphson procedure has sig, flow_incr, and eqvpstrain as its * variables. Therefore we need the derivatives of resid_ij, f, and rep * with respect to these parameters * * In this associative J2 with isotropic hardening, things are a little more specialised. * (1) f = sqrt(3*s_ij*s_ij/2) - K(eqvpstrain) (this is called "isotropic hardening") * (2) associativity means that flow_dirn_ij = df/d(sig_ij) = s_ij*sqrt(3/2/(s_ij*s_ij)), and * this means that flow_dirn_ij*flow_dirn_ij = 3/2, so when resid_ij=0, we get * (plastic_strain_dot)_ij*(plastic_strain_dot)_ij = (3/2)*flow_incr^2, where * plastic_strain_dot = plastic_strain - plastic_strain_old * (3) The definition of equivalent plastic strain is through * eqvpstrain_dot = sqrt(2*plastic_strain_dot_ij*plastic_strain_dot_ij/3), so * using (2), we obtain eqvpstrain_dot = flow_incr, and this yields * internalPotential = -1 in the "rep" equation. */ void FiniteStrainPlasticMaterial::returnMap(const RankTwoTensor & sig_old, const Real eqvpstrain_old, const RankTwoTensor & plastic_strain_old, const RankTwoTensor & delta_d, const RankFourTensor & E_ijkl, RankTwoTensor & sig, Real & eqvpstrain, RankTwoTensor & plastic_strain) { // the yield function, must be non-positive // Newton-Raphson sets this to zero if trial stress enters inadmissible region Real f; // the consistency parameter, must be non-negative // change in plastic strain in this timestep = flow_incr*flow_potential Real flow_incr = 0.0; // direction of flow defined by the potential RankTwoTensor flow_dirn; // Newton-Raphson sets this zero // resid_ij = flow_incr*flow_dirn_ij - (plastic_strain - plastic_strain_old) RankTwoTensor resid; // Newton-Raphson sets this zero // rep = -flow_incr*internalPotential - (eqvpstrain - eqvpstrain_old) Real rep; // change in the stress (sig) in a Newton-Raphson iteration RankTwoTensor ddsig; // change in the consistency parameter in a Newton-Raphson iteration Real dflow_incr = 0.0; // change in equivalent plastic strain in one Newton-Raphson iteration Real deqvpstrain = 0.0; // convenience variable that holds the change in plastic strain incurred during the return // delta_dp = plastic_strain - plastic_strain_old // delta_dp = E^{-1}*(trial_stress - sig), where trial_stress = E*(strain - plastic_strain_old) RankTwoTensor delta_dp; // d(yieldFunction)/d(stress) RankTwoTensor df_dsig; // d(resid_ij)/d(sigma_kl) RankFourTensor dr_dsig; // dr_dsig_inv_ijkl*dr_dsig_klmn = 0.5*(de_ij de_jn + de_ij + de_jm), where de_ij = 1 if i=j, but // zero otherwise RankFourTensor dr_dsig_inv; // d(yieldFunction)/d(eqvpstrain) Real fq; // yield stress at the start of this "time step" (ie, evaluated with // eqvpstrain_old). It is held fixed during the Newton-Raphson return, // even if eqvpstrain != eqvpstrain_old. Real yield_stress; // measures of whether the Newton-Raphson process has converged Real err1, err2, err3; // number of Newton-Raphson iterations performed unsigned int iter = 0; // maximum number of Newton-Raphson iterations allowed unsigned int maxiter = 100; // plastic loading occurs if yieldFunction > toly Real toly = 1.0e-8; // Assume this strain increment does not induce any plasticity // This is the elastic-predictor sig = sig_old + E_ijkl * delta_d; // the trial stress eqvpstrain = eqvpstrain_old; plastic_strain = plastic_strain_old; yield_stress = getYieldStress(eqvpstrain); // yield stress at this equivalent plastic strain if (yieldFunction(sig, yield_stress) > toly) { // the sig just calculated is inadmissable. We must return to the yield surface. // This is done iteratively, using a Newton-Raphson process. delta_dp.zero(); sig = sig_old + E_ijkl * delta_d; // this is the elastic predictor flow_dirn = flowPotential(sig); resid = flow_dirn * flow_incr - delta_dp; // Residual 1 - refer Hughes Simo f = yieldFunction(sig, yield_stress); rep = -eqvpstrain + eqvpstrain_old - flow_incr * internalPotential(); // Residual 3 rep=0 err1 = resid.L2norm(); err2 = std::abs(f); err3 = std::abs(rep); while ((err1 > _rtol || err2 > _ftol || err3 > _eptol) && iter < maxiter) // Stress update iteration (hardness fixed) { iter++; df_dsig = dyieldFunction_dstress(sig); getJac(sig, E_ijkl, flow_incr, dr_dsig); // gets dr_dsig = d(resid_ij)/d(sig_kl) fq = dyieldFunction_dinternal(eqvpstrain); // d(f)/d(eqvpstrain) /** * The linear system is * ( dr_dsig flow_dirn 0 )( ddsig ) ( - resid ) * ( df_dsig 0 fq )( dflow_incr ) = ( - f ) * ( 0 1 -1 )( deqvpstrain ) ( - rep ) * The zeroes are: d(resid_ij)/d(eqvpstrain) = flow_dirn*d(df/d(sig_ij))/d(eqvpstrain) = 0 * and df/d(flow_dirn) = 0 (this is always true, even for general hardening and * non-associative) * and d(rep)/d(sig_ij) = -flow_incr*d(internalPotential)/d(sig_ij) = 0 */ dr_dsig_inv = dr_dsig.invSymm(); /** * Because of the zeroes and ones, the linear system is not impossible to * solve by hand. * NOTE: andy believes there was originally a sign-error in the next line. The * next line is unchanged, however andy's definition of fq is negative of * the original definition of fq. andy can't see any difference in any tests! */ dflow_incr = (f - df_dsig.doubleContraction(dr_dsig_inv * resid) + fq * rep) / (df_dsig.doubleContraction(dr_dsig_inv * flow_dirn) - fq); ddsig = dr_dsig_inv * (-resid - flow_dirn * dflow_incr); // from solving the top row of linear system, given dflow_incr deqvpstrain = rep + dflow_incr; // from solving the bottom row of linear system, given dflow_incr // update the variables flow_incr += dflow_incr; delta_dp -= E_ijkl.invSymm() * ddsig; sig += ddsig; eqvpstrain += deqvpstrain; // evaluate the RHS equations ready for next Newton-Raphson iteration flow_dirn = flowPotential(sig); resid = flow_dirn * flow_incr - delta_dp; f = yieldFunction(sig, yield_stress); rep = -eqvpstrain + eqvpstrain_old - flow_incr * internalPotential(); err1 = resid.L2norm(); err2 = std::abs(f); err3 = std::abs(rep); } if (iter >= maxiter) mooseError("Constitutive failure"); plastic_strain += delta_dp; } }
RankFourTensor TensorMechanicsPlasticTensileMulti::consistentTangentOperator(const RankTwoTensor & trial_stress, const RankTwoTensor & stress, const Real & intnl, const RankFourTensor & E_ijkl, const std::vector<Real> & cumulative_pm) const { if (!_use_custom_cto) return TensorMechanicsPlasticModel::consistentTangentOperator(trial_stress, stress, intnl, E_ijkl, cumulative_pm); mooseAssert(cumulative_pm.size() == 3, "TensorMechanicsPlasticTensileMulti size of cumulative_pm should be 3 but it is " << cumulative_pm.size()); if (cumulative_pm[2] <= 0) // All cumulative_pm are non-positive, so this is admissible return E_ijkl; // Need the eigenvalues at the returned configuration std::vector<Real> eigvals; stress.symmetricEigenvalues(eigvals); // need to rotate to and from principal stress space // using the eigenvectors of the trial configuration // (not the returned configuration). std::vector<Real> trial_eigvals; RankTwoTensor trial_eigvecs; trial_stress.symmetricEigenvaluesEigenvectors(trial_eigvals, trial_eigvecs); // The returnMap will have returned to the Tip, Edge or // Plane. The consistentTangentOperator describes the // change in stress for an arbitrary change in applied // strain. I assume that the change in strain will not // change the type of return (Tip remains Tip, Edge remains // Edge, Plane remains Plane). // I assume isotropic elasticity. // // The consistent tangent operator is a little different // than cases where no rotation to principal stress space // is made during the returnMap. Let S_ij be the stress // in original coordinates, and s_ij be the stress in the // principal stress coordinates, so that // s_ij = diag(eigvals[0], eigvals[1], eigvals[2]) // We want dS_ij under an arbitrary change in strain (ep->ep+dep) // dS = S(ep+dep) - S(ep) // = R(ep+dep) s(ep+dep) R(ep+dep)^T - R(ep) s(ep) R(ep)^T // Here R = the rotation to principal-stress space, ie // R_ij = eigvecs[i][j] = i^th component of j^th eigenvector // Expanding to first order in dep, // dS = R(ep) (s(ep+dep) - s(ep)) R(ep)^T // + dR/dep s(ep) R^T + R(ep) s(ep) dR^T/dep // The first line is all that is usually calculated in the // consistent tangent operator calculation, and is called // cto below. // The second line involves changes in the eigenvectors, and // is called sec below. RankFourTensor cto; Real hard = dtensile_strength(intnl); Real la = E_ijkl(0,0,1,1); Real mu = 0.5*(E_ijkl(0,0,0,0) - la); if (cumulative_pm[1] <= 0) { // only cumulative_pm[2] is positive, so this is return to the Plane Real denom = hard + la + 2*mu; Real al = la*la/denom; Real be = la*(la + 2*mu)/denom; Real ga = hard*(la + 2*mu)/denom; std::vector<Real> comps(9); comps[0] = comps[4] = la + 2*mu - al; comps[1] = comps[3] = la - al; comps[2] = comps[5] = comps[6] = comps[7] = la - be; comps[8] = ga; cto.fillFromInputVector(comps, RankFourTensor::principal); } else if (cumulative_pm[0] <= 0) { // both cumulative_pm[2] and cumulative_pm[1] are positive, so Edge Real denom = 2*hard + 2*la + 2*mu; Real al = hard*2*la/denom; Real be = hard*(2*la + 2*mu)/denom; std::vector<Real> comps(9); comps[0] = la + 2*mu - 2*la*la/denom; comps[1] = comps[2] = al; comps[3] = comps[6] = al; comps[4] = comps[5] = comps[7] = comps[8] = be; cto.fillFromInputVector(comps, RankFourTensor::principal); } else { // all cumulative_pm are positive, so Tip Real denom = 3*hard + 3*la + 2*mu; std::vector<Real> comps(2); comps[0] = hard*(3*la + 2*mu)/denom; comps[1] = 0; cto.fillFromInputVector(comps, RankFourTensor::symmetric_isotropic); } cto.rotate(trial_eigvecs); // drdsig = change in eigenvectors under a small stress change // drdsig(i,j,m,n) = dR(i,j)/dS_mn // The formula below is fairly easily derived: // S R = R s, so taking the variation // dS R + S dR = dR s + R ds, and multiplying by R^T // R^T dS R + R^T S dR = R^T dR s + ds .... (eqn 1) // I demand that RR^T = 1 = R^T R, and also that // (R+dR)(R+dR)^T = 1 = (R+dT)^T (R+dR), which means // that dR = R*c, for some antisymmetric c, so Eqn1 reads // R^T dS R + s c = c s + ds // Grabbing the components of this gives ds/dS (already // in RankTwoTensor), and c, which is: // dR_ik/dS_mn = drdsig(i, k, m, n) = trial_eigvecs(m, b)*trial_eigvecs(n, k)*trial_eigvecs(i, b)/(trial_eigvals[k] - trial_eigvals[b]); // (sum over b!=k). RankFourTensor drdsig; for (unsigned k = 0 ; k < 3 ; ++k) for (unsigned b = 0 ; b < 3 ; ++b) { if (b == k) continue; for (unsigned m = 0 ; m < 3 ; ++m) for (unsigned n = 0 ; n < 3 ; ++n) for (unsigned i = 0 ; i < 3 ; ++i) drdsig(i, k, m, n) += trial_eigvecs(m, b)*trial_eigvecs(n, k)*trial_eigvecs(i, b)/(trial_eigvals[k] - trial_eigvals[b]); } // With diagla = diag(eigvals[0], eigvals[1], digvals[2]) // The following implements // ans(i, j, a, b) += (drdsig(i, k, m, n)*trial_eigvecs(j, l)*diagla(k, l) + trial_eigvecs(i, k)*drdsig(j, l, m, n)*diagla(k, l))*E_ijkl(m, n, a, b); // (sum over k, l, m and n) RankFourTensor ans; for (unsigned i = 0 ; i < 3 ; ++i) for (unsigned j = 0 ; j < 3 ; ++j) for (unsigned a = 0 ; a < 3 ; ++a) for (unsigned k = 0 ; k < 3 ; ++k) for (unsigned m = 0 ; m < 3 ; ++m) ans(i, j, a, a) += (drdsig(i, k, m, m)*trial_eigvecs(j, k) + trial_eigvecs(i, k)*drdsig(j, k, m, m))*eigvals[k]*la; //E_ijkl(m, n, a, b) = la*(m==n)*(a==b); for (unsigned i = 0 ; i < 3 ; ++i) for (unsigned j = 0 ; j < 3 ; ++j) for (unsigned a = 0 ; a < 3 ; ++a) for (unsigned b = 0 ; b < 3 ; ++b) for (unsigned k = 0 ; k < 3 ; ++k) { ans(i, j, a, b) += (drdsig(i, k, a, b)*trial_eigvecs(j, k) + trial_eigvecs(i, k)*drdsig(j, k, a, b))*eigvals[k]*mu; //E_ijkl(m, n, a, b) = mu*(m==a)*(n==b) ans(i, j, a, b) += (drdsig(i, k, b, a)*trial_eigvecs(j, k) + trial_eigvecs(i, k)*drdsig(j, k, b, a))*eigvals[k]*mu; //E_ijkl(m, n, a, b) = mu*(m==b)*(n==a) } return cto + ans; }
/* *Solves for incremental plastic rate of deformation tensor and stress in unrotated frame. *Input: Strain incrment, 4th order elasticity tensor, stress tensor in previous incrmenent and *plastic rate of deformation tensor gradient. */ void FiniteStrainPlasticMaterial::solveStressResid(RankTwoTensor sig_old,RankTwoTensor delta_d,RankFourTensor E_ijkl, RankTwoTensor *dp, RankTwoTensor *sig) { RankTwoTensor sig_new,delta_dp,dpn; RankTwoTensor flow_tensor, flow_dirn; RankTwoTensor resid,ddsig; RankFourTensor dr_dsig,dr_dsig_inv; Real /*sig_eqv,*/flow_incr,f,dflow_incr; Real err1,err2,err3; unsigned int plastic_flag; unsigned int iter,maxiter=100; Real eqvpstrain,eqvpstrain_old,deqvpstrain,fq,rep; Real yield_stress; sig_new=sig_old+E_ijkl*delta_d; eqvpstrain_old=eqvpstrain=_eqv_plastic_strain_old[_qp]; yield_stress=getYieldStress(eqvpstrain); plastic_flag=isPlastic(sig_new,yield_stress);//Check of plasticity for elastic predictor if (plastic_flag==1) { iter=0; dflow_incr=0.0; flow_incr=0.0; delta_dp.zero(); deqvpstrain=0.0; sig_new=sig_old+E_ijkl*delta_d; getFlowTensor(sig_new,&flow_tensor); flow_dirn=flow_tensor; resid=flow_dirn*flow_incr-delta_dp;//Residual 1 - refer Hughes Simo f=getSigEqv(sig_new)-yield_stress;//Residual 2 - f=0 rep=-eqvpstrain+eqvpstrain_old+flow_incr;//Residual 3 rep=0 err1=resid.L2norm(); err2=fabs(f); err3=fabs(rep); while ((err1 > _rtol || err2 > _ftol || err3 > _eptol) && iter < maxiter )//Stress update iteration (hardness fixed) { iter++; getJac(sig_new,E_ijkl,flow_incr,&dr_dsig);//Jacobian dr_dsig_inv=dr_dsig.invSymm(); fq=getdYieldStressdPlasticStrain(eqvpstrain); dflow_incr=(f-flow_tensor.doubleContraction(dr_dsig_inv*resid)+fq*rep)/(flow_tensor.doubleContraction(dr_dsig_inv*flow_dirn)-fq); ddsig=dr_dsig_inv*(-resid-flow_dirn*dflow_incr); flow_incr+=dflow_incr; delta_dp-=E_ijkl.invSymm()*ddsig; sig_new+=ddsig; deqvpstrain=rep+dflow_incr; eqvpstrain+=deqvpstrain; getFlowTensor(sig_new,&flow_tensor); flow_dirn=flow_tensor; resid=flow_dirn*flow_incr-delta_dp; f=getSigEqv(sig_new)-yield_stress; rep=-eqvpstrain+eqvpstrain_old+flow_incr; err1=resid.L2norm(); err2=fabs(f); err3=fabs(rep); } if (iter>=maxiter) { _stress[_qp](2,2)=1e6; printf("Constitutive Error: Too many iterations %f %f %f \n",err1,err2, err3);//Convergence failure return; } dpn=*dp+delta_dp; } *dp=dpn;//Plastic strain in unrotated configuration *sig=sig_new; _eqv_plastic_strain[_qp]=eqvpstrain; }
void RedbackMechMaterialCC::getJac(const RankTwoTensor & sig, const RankFourTensor & E_ijkl, Real flow_incr, Real sig_eqv, Real pressure, Real p_yield_stress, Real q_yield_stress, Real yield_stress, RankFourTensor & dresid_dsig) { unsigned i, j, k, l; RankTwoTensor sig_dev, flow_dirn_vol, flow_dirn_dev, fij, flow_dirn, flow_tensor; RankTwoTensor dfi_dft; RankFourTensor dfd_dsig, dfi_dsig; Real f1, f2, f3; Real dfi_dp, dfi_dseqv; Real pc = -yield_stress; sig_dev = sig.deviatoric(); getDerivativeFlowIncrement(dfi_dp, dfi_dseqv, sig, pressure, sig_eqv, pc, q_yield_stress, p_yield_stress); getFlowTensor(sig, sig_eqv, pressure, q_yield_stress, p_yield_stress, yield_stress, flow_dirn); /* The following calculates the tensorial derivative (Jacobian) of the *residual with respect to stress, dr_dsig * It consists of two terms: The first is * dr_dsig = (dfi_dseqv_dev*flow_dirn_dev(k,l)) * flow_dirn_dev(i,j) * which is the tensorial product of the flow increment tensor times the flow *direction tensor * * The second is the product of the flow increment tensor times the derivative *of the flow direction tensor * with respect to the stress tensor. See also REDBACK's documentation * */ f1 = 0.0; f2 = 0.0; f3 = 0.0; if (sig_eqv > 1e-10) { f1 = 3.0 / (_slope_yield_surface * _slope_yield_surface); f2 = 2.0 / 9.0 - 1.0 / (_slope_yield_surface * _slope_yield_surface); f3 = 3.0 / (2.0 * sig_eqv); } // This loop calculates the first term for (i = 0; i < 3; ++i) for (j = 0; j < 3; ++j) for (k = 0; k < 3; ++k) for (l = 0; l < 3; ++l) dfi_dsig(i, j, k, l) = flow_dirn(i, j) * (f3 * sig_dev(k, l) * dfi_dseqv + dfi_dp * deltaFunc(k, l) / 3.0); // This loop calculates the second term. for (i = 0; i < 3; ++i) for (j = 0; j < 3; ++j) for (k = 0; k < 3; ++k) for (l = 0; l < 3; ++l) dfd_dsig(i, j, k, l) = f1 * deltaFunc(i, k) * deltaFunc(j, l) + f2 * deltaFunc(i, j) * deltaFunc(k, l); dresid_dsig = E_ijkl.invSymm() + dfd_dsig * flow_incr + dfi_dsig; // Jacobian }
void mooseSetToZero<RankFourTensor>(RankFourTensor & v) { v.zero(); }
/* *Solves for incremental plastic rate of deformation tensor and stress in unrotated frame. *Input: Strain incrment, 4th order elasticity tensor, stress tensor in previous incrmenent and *plastic rate of deformation tensor. */ void FiniteStrainRatePlasticMaterial::returnMap(const RankTwoTensor & sig_old, const RankTwoTensor & delta_d, const RankFourTensor & E_ijkl, RankTwoTensor & dp, RankTwoTensor & sig) { RankTwoTensor sig_new, delta_dp, dpn; RankTwoTensor flow_tensor, flow_dirn; RankTwoTensor resid,ddsig; RankFourTensor dr_dsig, dr_dsig_inv; Real flow_incr, flow_incr_tmp; Real err1, err3, tol1, tol3; unsigned int iterisohard, iter, maxiterisohard = 20, maxiter = 50; Real eqvpstrain; Real yield_stress, yield_stress_prev; tol1 = 1e-10; tol3 = 1e-6; iterisohard = 0; eqvpstrain = std::pow(2.0/3.0,0.5) * dp.L2norm(); yield_stress = getYieldStress(eqvpstrain); err3 = 1.1 * tol3; while (err3 > tol3 && iterisohard < maxiterisohard) //Hardness update iteration { iterisohard++; iter = 0; delta_dp.zero(); sig_new = sig_old + E_ijkl * delta_d; flow_incr=_ref_pe_rate*_dt * std::pow(macaulayBracket(getSigEqv(sig_new) / yield_stress - 1.0), _exponent); getFlowTensor(sig_new, yield_stress, flow_tensor); flow_dirn = flow_tensor; resid = flow_dirn * flow_incr - delta_dp; err1 = resid.L2norm(); while (err1 > tol1 && iter < maxiter) //Stress update iteration (hardness fixed) { iter++; getJac(sig_new, E_ijkl, flow_incr, yield_stress, dr_dsig); //Jacobian dr_dsig_inv = dr_dsig.invSymm(); ddsig = -dr_dsig_inv * resid; sig_new += ddsig; //Update stress delta_dp -= E_ijkl.invSymm() * ddsig; //Update plastic rate of deformation tensor flow_incr_tmp = _ref_pe_rate * _dt * std::pow(macaulayBracket(getSigEqv(sig_new) / yield_stress - 1.0), _exponent); if (flow_incr_tmp < 0.0) //negative flow increment not allowed mooseError("Constitutive Error-Negative flow increment: Reduce time increment."); flow_incr = flow_incr_tmp; getFlowTensor(sig_new, yield_stress, flow_tensor); flow_dirn = flow_tensor; resid = flow_dirn * flow_incr - delta_dp; //Residual err1=resid.L2norm(); } if (iter>=maxiter)//Convergence failure mooseError("Constitutive Error-Too many iterations: Reduce time increment.\n"); //Convergence failure dpn = dp + delta_dp; eqvpstrain = std::pow(2.0/3.0, 0.5) * dpn.L2norm(); yield_stress_prev = yield_stress; yield_stress = getYieldStress(eqvpstrain); err3 = std::abs(yield_stress-yield_stress_prev); } if (iterisohard>=maxiterisohard) mooseError("Constitutive Error-Too many iterations in Hardness Update:Reduce time increment.\n"); //Convergence failure dp = dpn; //Plastic rate of deformation tensor in unrotated configuration sig = sig_new; }
bool FiniteStrainPlasticBase::returnMap(const RankTwoTensor & stress_old, const RankTwoTensor & plastic_strain_old, const std::vector<Real> & intnl_old, const RankTwoTensor & delta_d, const RankFourTensor & E_ijkl, RankTwoTensor & stress, RankTwoTensor & plastic_strain, std::vector<Real> & intnl, std::vector<Real> & f, unsigned int & iter) { // Assume this strain increment does not induce any plasticity // This is the elastic-predictor stress = stress_old + E_ijkl * delta_d; // the trial stress plastic_strain = plastic_strain_old; for (unsigned i = 0; i < intnl_old.size() ; ++i) intnl[i] = intnl_old[i]; iter = 0; yieldFunction(stress, intnl, f); Real nr_res2 = 0; for (unsigned i = 0 ; i < f.size() ; ++i) nr_res2 += 0.5*std::pow( std::max(f[i], 0.0)/_f_tol[i], 2); if (nr_res2 < 0.5) // a purely elastic increment. // All output variables have been calculated return true; // So, from here on we know that the trial stress // is inadmissible, and we have to return from that // value to the yield surface. There are three // types of constraints we have to satisfy, listed // below, and calculated in calculateConstraints(...) // Plastic strain constraint, L2 norm must be zero (up to a tolerance) RankTwoTensor epp; // Yield function constraint passed to this function as // std::vector<Real> & f // Each yield function must be <= 0 (up to tolerance) // Internal constraint(s), must be zero (up to a tolerance) std::vector<Real> ic; // During the Newton-Raphson procedure, we'll be // changing the following parameters in order to // (attempt to) satisfy the constraints. RankTwoTensor dstress; // change in stress std::vector<Real> dpm; // change in plasticity multipliers ("consistency parameters") std::vector<Real> dintnl; // change in internal parameters // The following are used in the Newton-Raphson // Inverse of E_ijkl (assuming symmetric) RankFourTensor E_inv = E_ijkl.invSymm(); // convenience variable that holds the change in plastic strain incurred during the return // delta_dp = plastic_strain - plastic_strain_old // delta_dp = E^{-1}*(trial_stress - stress), where trial_stress = E*(strain - plastic_strain_old) RankTwoTensor delta_dp; // The "consistency parameters" (plastic multipliers) // Change in plastic strain in this timestep = pm*flowPotential // Each pm must be non-negative std::vector<Real> pm; pm.assign(numberOfYieldFunctions(), 0.0); // whether line-searching was successful bool ls_success = true; // The Newton-Raphson loops while (nr_res2 > 0.5 && iter < _max_iter && ls_success) { iter++; // calculate dstress, dpm and dintnl for one full Newton-Raphson step nrStep(stress, intnl_old, intnl, pm, E_inv, delta_dp, dstress, dpm, dintnl); // perform a line search // The line-search will exit with updated values ls_success = lineSearch(nr_res2, stress, intnl_old, intnl, pm, E_inv, delta_dp, dstress, dpm, dintnl, f, epp, ic); } if (iter >= _max_iter || !ls_success) { stress = stress_old; for (unsigned i = 0; i < intnl_old.size() ; ++i) intnl[i] = intnl_old[i]; return false; } else { plastic_strain += delta_dp; return true; } }