void MultiPlasticityDebugger::fddflowPotential_dintnl(const RankTwoTensor & stress, const std::vector<Real> & intnl, std::vector<RankTwoTensor> & dr_dintnl) { dr_dintnl.resize(_num_surfaces); std::vector<bool> act; act.assign(_num_surfaces, true); std::vector<RankTwoTensor> origr; flowPotential(stress, intnl, act, origr); std::vector<Real> intnlep; intnlep.resize(_num_models); for (unsigned model = 0; model < _num_models; ++model) intnlep[model] = intnl[model]; Real ep; std::vector<RankTwoTensor> rep; unsigned int model; for (unsigned surface = 0; surface < _num_surfaces; ++surface) { model = modelNumber(surface); ep = _fspb_debug_intnl_change[model]; intnlep[model] += ep; flowPotential(stress, intnlep, act, rep); dr_dintnl[surface] = (rep[surface] - origr[surface]) / ep; intnlep[model] -= ep; } }
void FiniteStrainPlasticBase::fddflowPotential_dintnl(const RankTwoTensor & stress, const std::vector<Real> & intnl, std::vector<std::vector<RankTwoTensor> > & dr_dintnl) { dr_dintnl.resize(numberOfYieldFunctions()); for (unsigned alpha = 0 ; alpha < numberOfYieldFunctions() ; ++alpha) dr_dintnl[alpha].assign(numberOfInternalParameters(), RankTwoTensor()); std::vector<RankTwoTensor> origr; flowPotential(stress, intnl, origr); std::vector<Real> intnlep; intnlep.resize(numberOfInternalParameters()); for (unsigned a = 0 ; a < numberOfInternalParameters() ; ++a) intnlep[a] = intnl[a]; Real ep; std::vector<RankTwoTensor> rep; for (unsigned a = 0 ; a < numberOfInternalParameters() ; ++a) { ep = _fspb_debug_intnl_change[a]; intnlep[a] += ep; flowPotential(stress, intnlep, rep); for (unsigned alpha = 0 ; alpha < numberOfYieldFunctions() ; ++alpha) dr_dintnl[alpha][a] = (rep[alpha] - origr[alpha])/ep; intnlep[a] -= ep; } }
void MultiPlasticityDebugger::fddflowPotential_dstress(const RankTwoTensor & stress, const std::vector<Real> & intnl, std::vector<RankFourTensor> & dr_dstress) { dr_dstress.assign(_num_surfaces, RankFourTensor()); std::vector<bool> act; act.assign(_num_surfaces, true); Real ep = _fspb_debug_stress_change; RankTwoTensor stressep; std::vector<RankTwoTensor> rep, rep_minus; for (unsigned i = 0; i < 3; ++i) for (unsigned j = 0; j < 3; ++j) { stressep = stress; // do a central difference stressep(i, j) += ep / 2.0; flowPotential(stressep, intnl, act, rep); stressep(i, j) -= ep; flowPotential(stressep, intnl, act, rep_minus); for (unsigned surface = 0; surface < _num_surfaces; ++surface) for (unsigned k = 0; k < 3; ++k) for (unsigned l = 0; l < 3; ++l) dr_dstress[surface](k, l, i, j) = (rep[surface](k, l) - rep_minus[surface](k, l)) / ep; } }
// 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; }
void FiniteStrainPlasticBase::calculateConstraints(const RankTwoTensor & stress, const std::vector<Real> & intnl_old, const std::vector<Real> & intnl, const std::vector<Real> & pm, const RankTwoTensor & delta_dp, std::vector<Real> & f, RankTwoTensor & epp, std::vector<Real> & ic) { // yield functions yieldFunction(stress, intnl, f); // flow direction std::vector<RankTwoTensor> r; flowPotential(stress, intnl, r); epp = RankTwoTensor(); for (unsigned alpha = 0 ; alpha < numberOfYieldFunctions() ; ++alpha) epp += pm[alpha]*r[alpha]; epp -= delta_dp; // internal constraints std::vector<std::vector<Real> > h; hardPotential(stress, intnl, h); ic.resize(numberOfInternalParameters()); for (unsigned a = 0 ; a < numberOfInternalParameters() ; ++a) { ic[a] = intnl[a] - intnl_old[a]; for (unsigned alpha = 0 ; alpha < numberOfYieldFunctions() ; ++alpha) ic[a] += pm[alpha]*h[a][alpha]; } }
void TensorMechanicsPlasticModel::flowPotentialV(const RankTwoTensor & stress, Real intnl, std::vector<RankTwoTensor> & r) const { return r.assign(1, flowPotential(stress, intnl)); }
void MultiPlasticityLinearSystem::calculateConstraints(const RankTwoTensor & stress, const std::vector<Real> & intnl_old, const std::vector<Real> & intnl, const std::vector<Real> & pm, const RankTwoTensor & delta_dp, std::vector<Real> & f, std::vector<RankTwoTensor> & r, RankTwoTensor & epp, std::vector<Real> & ic, const std::vector<bool> & active) { // see comments at the start of .h file mooseAssert(intnl_old.size() == _num_models, "Size of intnl_old is " << intnl_old.size() << " which is incorrect in calculateConstraints"); mooseAssert(intnl.size() == _num_models, "Size of intnl is " << intnl.size() << " which is incorrect in calculateConstraints"); mooseAssert(pm.size() == _num_surfaces, "Size of pm is " << pm.size() << " which is incorrect in calculateConstraints"); mooseAssert(active.size() == _num_surfaces, "Size of active is " << active.size() << " which is incorrect in calculateConstraints"); // yield functions yieldFunction(stress, intnl, active, f); // flow directions and "epp" flowPotential(stress, intnl, active, r); epp = RankTwoTensor(); unsigned ind = 0; for (unsigned surface = 0; surface < _num_surfaces; ++surface) if (active[surface]) epp += pm[surface] * r[ind++]; // note, even the deactivated_due_to_ld must get added in epp -= delta_dp; // internal constraints std::vector<Real> h; hardPotential(stress, intnl, active, h); ic.resize(0); ind = 0; std::vector<unsigned int> active_surfaces; std::vector<unsigned int>::iterator active_surface; for (unsigned model = 0; model < _num_models; ++model) { activeSurfaces(model, active, active_surfaces); if (active_surfaces.size() > 0) { // some surfaces are active in this model, so must form an internal constraint ic.push_back(intnl[model] - intnl_old[model]); for (active_surface = active_surfaces.begin(); active_surface != active_surfaces.end(); ++active_surface) ic[ic.size() - 1] += pm[*active_surface] * h[ind++]; // we know the correct one is h[ind] // since it was constructed in the same // manner } } }
void FiniteStrainPlasticBase::fddflowPotential_dstress(const RankTwoTensor & stress, const std::vector<Real> & intnl, std::vector<RankFourTensor> & dr_dstress) { dr_dstress.assign(numberOfYieldFunctions(), RankFourTensor()); Real ep = _fspb_debug_stress_change; RankTwoTensor stressep; std::vector<RankTwoTensor> rep, rep_minus; for (unsigned i = 0 ; i < 3 ; ++i) for (unsigned j = 0 ; j < 3 ; ++j) { stressep = stress; stressep(i, j) += ep/2.0; flowPotential(stressep, intnl, rep); stressep(i, j) -= ep; flowPotential(stressep, intnl, rep_minus); for (unsigned alpha = 0 ; alpha < numberOfYieldFunctions() ; ++alpha) for (unsigned k = 0 ; k < 3 ; ++k) for (unsigned l = 0 ; l < 3 ; ++l) dr_dstress[alpha](k, l, i, j) = (rep[alpha](k, l) - rep_minus[alpha](k, l))/ep; } }
void MultiPlasticityLinearSystem::calculateJacobian(const RankTwoTensor & stress, const std::vector<Real> & intnl, const std::vector<Real> & pm, const RankFourTensor & E_inv, const std::vector<bool> & active, const std::vector<bool> & deactivated_due_to_ld, std::vector<std::vector<Real> > & jac) { // see comments at the start of .h file mooseAssert(intnl.size() == _num_models, "Size of intnl is " << intnl.size() << " which is incorrect in calculateJacobian"); mooseAssert(pm.size() == _num_surfaces, "Size of pm is " << pm.size() << " which is incorrect in calculateJacobian"); mooseAssert(active.size() == _num_surfaces, "Size of active is " << active.size() << " which is incorrect in calculateJacobian"); mooseAssert(deactivated_due_to_ld.size() == _num_surfaces, "Size of deactivated_due_to_ld is " << deactivated_due_to_ld.size() << " which is incorrect in calculateJacobian"); unsigned ind = 0; unsigned active_surface_ind = 0; std::vector<bool> active_surface(_num_surfaces); // active and not deactivated_due_to_ld for (unsigned surface = 0 ; surface < _num_surfaces ; ++surface) active_surface[surface] = (active[surface] && !deactivated_due_to_ld[surface]); unsigned num_active_surface = 0; for (unsigned surface = 0 ; surface < _num_surfaces ; ++surface) if (active_surface[surface]) num_active_surface++; std::vector<bool> active_model(_num_models); // whether a model has surfaces that are active and not deactivated_due_to_ld for (unsigned model = 0 ; model < _num_models ; ++model) active_model[model] = anyActiveSurfaces(model, active_surface); unsigned num_active_model = 0; for (unsigned model = 0 ; model < _num_models ; ++model) if (active_model[model]) num_active_model++; ind = 0; std::vector<unsigned int> active_model_index(_num_models); for (unsigned model = 0 ; model < _num_models ; ++model) if (active_model[model]) active_model_index[model] = ind++; else active_model_index[model] = _num_models+1; // just a dummy, that will probably cause a crash if something goes wrong std::vector<RankTwoTensor> df_dstress; dyieldFunction_dstress(stress, intnl, active_surface, df_dstress); std::vector<Real> df_dintnl; dyieldFunction_dintnl(stress, intnl, active_surface, df_dintnl); std::vector<RankTwoTensor> r; flowPotential(stress, intnl, active, r); std::vector<RankFourTensor> dr_dstress; dflowPotential_dstress(stress, intnl, active, dr_dstress); std::vector<RankTwoTensor> dr_dintnl; dflowPotential_dintnl(stress, intnl, active, dr_dintnl); std::vector<Real> h; hardPotential(stress, intnl, active, h); std::vector<RankTwoTensor> dh_dstress; dhardPotential_dstress(stress, intnl, active, dh_dstress); std::vector<Real> dh_dintnl; dhardPotential_dintnl(stress, intnl, active, dh_dintnl); // d(epp)/dstress = sum_{active alpha} pm[alpha]*dr_dstress RankFourTensor depp_dstress; ind = 0; for (unsigned surface = 0 ; surface < _num_surfaces ; ++surface) if (active[surface]) // includes deactivated_due_to_ld depp_dstress += pm[surface]*dr_dstress[ind++]; depp_dstress += E_inv; // d(epp)/dpm_{active_surface_index} = r_{active_surface_index} std::vector<RankTwoTensor> depp_dpm; depp_dpm.resize(num_active_surface); ind = 0; active_surface_ind = 0; for (unsigned surface = 0 ; surface < _num_surfaces ; ++surface) { if (active[surface]) { if (active_surface[surface]) // do not include the deactived_due_to_ld, since their pm are not dofs in the NR depp_dpm[active_surface_ind++] = r[ind]; ind++; } } // d(epp)/dintnl_{active_model_index} = sum(pm[asdf]*dr_dintnl[fdsa]) std::vector<RankTwoTensor> depp_dintnl; depp_dintnl.assign(num_active_model, RankTwoTensor()); ind = 0; for (unsigned surface = 0 ; surface < _num_surfaces ; ++surface) { if (active[surface]) { unsigned int model_num = modelNumber(surface); if (active_model[model_num]) // only include models with surfaces which are still active after deactivated_due_to_ld depp_dintnl[active_model_index[model_num]] += pm[surface]*dr_dintnl[ind]; ind++; } } // df_dstress has been calculated above // df_dpm is always zero // df_dintnl has been calculated above, but only the active_surface+active_model stuff needs to be included in Jacobian: see below std::vector<RankTwoTensor> dic_dstress; dic_dstress.assign(num_active_model, RankTwoTensor()); ind = 0; for (unsigned surface = 0 ; surface < _num_surfaces ; ++surface) { if (active[surface]) { unsigned int model_num = modelNumber(surface); if (active_model[model_num]) // only include ic for models with active_surface (ie, if model only contains deactivated_due_to_ld don't include it) dic_dstress[active_model_index[model_num]] += pm[surface]*dh_dstress[ind]; ind++; } } std::vector<std::vector<Real> > dic_dpm; dic_dpm.resize(num_active_model); ind = 0; active_surface_ind = 0; for (unsigned model = 0 ; model < num_active_model ; ++model) dic_dpm[model].assign(num_active_surface, 0); for (unsigned surface = 0 ; surface < _num_surfaces ; ++surface) { if (active[surface]) { if (active_surface[surface]) // only take derivs wrt active-but-not-deactivated_due_to_ld pm { unsigned int model_num = modelNumber(surface); // if (active_model[model_num]) // do not need this check as if the surface has active_surface, the model must be deemed active! dic_dpm[active_model_index[model_num]][active_surface_ind] = h[ind]; active_surface_ind++; } ind++; } } std::vector<std::vector<Real> > dic_dintnl; dic_dintnl.resize(num_active_model); for (unsigned model = 0 ; model < num_active_model ; ++model) { dic_dintnl[model].assign(num_active_model, 0); dic_dintnl[model][model] = 1; // deriv wrt internal parameter } ind = 0; for (unsigned surface = 0 ; surface < _num_surfaces ; ++surface) { if (active[surface]) { unsigned int model_num = modelNumber(surface); if (active_model[model_num]) // only the models that contain surfaces that are still active after deactivation_due_to_ld dic_dintnl[active_model_index[model_num]][active_model_index[model_num]] += pm[surface]*dh_dintnl[ind]; ind++; } } unsigned int dim = 3; unsigned int system_size = 6 + num_active_surface + num_active_model; // "6" comes from symmeterizing epp jac.resize(system_size); for (unsigned i = 0 ; i < system_size ; ++i) jac[i].assign(system_size, 0); unsigned int row_num = 0; unsigned int col_num = 0; for (unsigned i = 0 ; i < dim ; ++i) for (unsigned j = 0 ; j <= i ; ++j) { for (unsigned k = 0 ; k < dim ; ++k) for (unsigned l = 0 ; l <= k ; ++l) jac[col_num][row_num++] = depp_dstress(i, j, k, l) + (k != l ? depp_dstress(i, j, l, k) : 0); // extra part is needed because i assume dstress(i, j) = dstress(j, i) for (unsigned surface = 0 ; surface < num_active_surface ; ++surface) jac[col_num][row_num++] = depp_dpm[surface](i, j); for (unsigned a = 0 ; a < num_active_model ; ++a) jac[col_num][row_num++] = depp_dintnl[a](i, j); row_num = 0; col_num++; } ind = 0; for (unsigned surface = 0 ; surface < _num_surfaces ; ++surface) if (active_surface[surface]) { for (unsigned k = 0 ; k < dim ; ++k) for (unsigned l = 0 ; l <= k ; ++l) jac[col_num][row_num++] = df_dstress[ind](k, l) + (k != l ? df_dstress[ind](l, k) : 0); // extra part is needed because i assume dstress(i, j) = dstress(j, i) for (unsigned beta = 0 ; beta < num_active_surface ; ++beta) jac[col_num][row_num++] = 0; // df_dpm for (unsigned model = 0 ; model < _num_models ; ++model) if (active_model[model]) // only use df_dintnl for models in active_model { if (modelNumber(surface) == model) jac[col_num][row_num++] = df_dintnl[ind]; else jac[col_num][row_num++] = 0; } ind++; row_num = 0; col_num++; } for (unsigned a = 0 ; a < num_active_model ; ++a) { for (unsigned k = 0 ; k < dim ; ++k) for (unsigned l = 0 ; l <= k ; ++l) jac[col_num][row_num++] = dic_dstress[a](k, l) + (k != l ? dic_dstress[a](l, k) : 0); // extra part is needed because i assume dstress(i, j) = dstress(j, i) for (unsigned alpha = 0 ; alpha < num_active_surface ; ++alpha) jac[col_num][row_num++] = dic_dpm[a][alpha]; for (unsigned b = 0 ; b < num_active_model ; ++b) jac[col_num][row_num++] = dic_dintnl[a][b]; row_num = 0; col_num++; } mooseAssert(col_num == system_size, "Incorrect filling of cols in Jacobian"); }
/** * 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; } }
void FiniteStrainPlasticBase::calculateJacobian(const RankTwoTensor & stress, const std::vector<Real> & intnl, const std::vector<Real> & pm, const RankFourTensor & E_inv, std::vector<std::vector<Real> > & jac) { // construct quantities used in the Newton-Raphson linear system // These should have been defined by the derived classes, if // they are different from the default functions given elsewhere // in this class std::vector<RankTwoTensor> df_dstress; dyieldFunction_dstress(stress, intnl, df_dstress); std::vector<std::vector<Real> > df_dintnl; dyieldFunction_dintnl(stress, intnl, df_dintnl); std::vector<RankTwoTensor> r; flowPotential(stress, intnl, r); std::vector<RankFourTensor> dr_dstress; dflowPotential_dstress(stress, intnl, dr_dstress); std::vector<std::vector<RankTwoTensor> > dr_dintnl; dflowPotential_dintnl(stress, intnl, dr_dintnl); std::vector<std::vector<Real> > h; hardPotential(stress, intnl, h); std::vector<std::vector<RankTwoTensor> > dh_dstress; dhardPotential_dstress(stress, intnl, dh_dstress); std::vector<std::vector<std::vector<Real> > > dh_dintnl; dhardPotential_dintnl(stress, intnl, dh_dintnl); // construct matrix entries // In the following // epp = pm*r - E_inv*(trial_stress - stress) = pm*r - delta_dp // f = yield function // ic = intnl - intnl_old + pm*h RankFourTensor depp_dstress; for (unsigned alpha = 0 ; alpha < numberOfYieldFunctions() ; ++alpha) depp_dstress += pm[alpha]*dr_dstress[alpha]; depp_dstress += E_inv; std::vector<RankTwoTensor> depp_dpm; depp_dpm.resize(numberOfYieldFunctions()); for (unsigned alpha = 0; alpha < numberOfYieldFunctions() ; ++alpha) depp_dpm[alpha] = r[alpha]; std::vector<RankTwoTensor> depp_dintnl; depp_dintnl.assign(numberOfInternalParameters(), RankTwoTensor()); for (unsigned a = 0; a < numberOfInternalParameters() ; ++a) for (unsigned alpha = 0 ; alpha < numberOfYieldFunctions() ; ++alpha) depp_dintnl[a] += pm[alpha]*dr_dintnl[alpha][a]; // df_dstress has been calculated above // df_dpm is always zero // df_dintnl has been calculated above std::vector<RankTwoTensor> dic_dstress; dic_dstress.assign(numberOfInternalParameters(), RankTwoTensor()); for (unsigned a = 0 ; a < numberOfInternalParameters() ; ++a) for (unsigned alpha = 0 ; alpha < numberOfYieldFunctions() ; ++alpha) dic_dstress[a] += pm[alpha]*dh_dstress[a][alpha]; std::vector<std::vector<Real> > dic_dpm; dic_dpm.resize(numberOfInternalParameters()); for (unsigned a = 0 ; a < numberOfInternalParameters() ; ++a) { dic_dpm[a].resize(numberOfYieldFunctions()); for (unsigned alpha = 0 ; alpha < numberOfYieldFunctions() ; ++alpha) dic_dpm[a][alpha] = h[a][alpha]; } std::vector<std::vector<Real> > dic_dintnl; dic_dintnl.resize(numberOfInternalParameters()); for (unsigned a = 0 ; a < numberOfInternalParameters() ; ++a) { dic_dintnl[a].assign(numberOfInternalParameters(), 0); for (unsigned b = 0 ; b < numberOfInternalParameters() ; ++b) for (unsigned alpha = 0 ; alpha < numberOfYieldFunctions() ; ++alpha) dic_dintnl[a][b] += pm[alpha]*dh_dintnl[a][alpha][b]; dic_dintnl[a][a] += 1; } /** * now construct the Jacobian * It is: * ( depp_dstress depp_dpm depp_dintnl ) * ( df_dstress 0 df_dintnl ) * ( dic_dstress dic_dpm dic_dintnl ) * For the "epp" terms, only the i>=j components are kept in the RHS, so only these terms are kept here too */ unsigned int dim = 3; unsigned int system_size = 6 + numberOfYieldFunctions() + numberOfInternalParameters(); // "6" comes from symmeterizing epp jac.resize(system_size); for (unsigned i = 0 ; i < system_size ; ++i) jac[i].resize(system_size); unsigned int row_num = 0; unsigned int col_num = 0; for (unsigned i = 0 ; i < dim ; ++i) for (unsigned j = 0 ; j <= i ; ++j) { for (unsigned k = 0 ; k < dim ; ++k) for (unsigned l = 0 ; l <= k ; ++l) jac[col_num][row_num++] = depp_dstress(i, j, k, l) + (k != l ? depp_dstress(i, j, l, k) : 0); // extra part is needed because i assume dstress(i, j) = dstress(j, i) for (unsigned alpha = 0 ; alpha < numberOfYieldFunctions() ; ++alpha) jac[col_num][row_num++] = depp_dpm[alpha](i, j); for (unsigned a = 0 ; a < numberOfInternalParameters() ; ++a) jac[col_num][row_num++] = depp_dintnl[a](i, j); row_num = 0; col_num++; } for (unsigned alpha = 0 ; alpha < numberOfYieldFunctions() ; ++alpha) { for (unsigned k = 0 ; k < dim ; ++k) for (unsigned l = 0 ; l <= k ; ++l) jac[col_num][row_num++] = df_dstress[alpha](k, l) + (k != l ? df_dstress[alpha](l, k) : 0); // extra part is needed because i assume dstress(i, j) = dstress(j, i) for (unsigned beta = 0 ; beta < numberOfYieldFunctions() ; ++beta) jac[col_num][row_num++] = 0; for (unsigned a = 0 ; a < numberOfInternalParameters() ; ++a) jac[col_num][row_num++] = df_dintnl[alpha][a]; row_num = 0; col_num++; } for (unsigned a = 0 ; a < numberOfInternalParameters() ; ++a) { for (unsigned k = 0 ; k < dim ; ++k) for (unsigned l = 0 ; l <= k ; ++l) jac[col_num][row_num++] = dic_dstress[a](k, l) + (k != l ? dic_dstress[a](l, k) : 0); // extra part is needed because i assume dstress(i, j) = dstress(j, i) for (unsigned alpha = 0 ; alpha < numberOfYieldFunctions() ; ++alpha) jac[col_num][row_num++] = dic_dpm[a][alpha]; for (unsigned b = 0 ; b < numberOfInternalParameters() ; ++b) jac[col_num][row_num++] = dic_dintnl[a][b]; row_num = 0; col_num++; } }