Real TensorMechanicsPlasticMeanCapTC::dyieldFunction_dintnl(const RankTwoTensor & stress, Real intnl) const { const Real tr = stress.trace(); const Real t_str = tensile_strength(intnl); if (tr >= t_str) return -dtensile_strength(intnl); const Real c_str = compressive_strength(intnl); if (tr <= c_str) return dcompressive_strength(intnl); const Real dt = dtensile_strength(intnl); const Real dc = dcompressive_strength(intnl); return (dc - dt) / M_PI * std::sin(M_PI * (tr - c_str) / (t_str - c_str)) + 1.0 / (t_str - c_str) * std::cos(M_PI * (tr - c_str) / (t_str - c_str)) * ((tr - c_str) * dt - (tr - t_str) * dc ); }
RankFourTensor TensorMechanicsPlasticMeanCapTC::consistentTangentOperator(const RankTwoTensor & trial_stress, Real intnl_old, const RankTwoTensor & stress, Real intnl, const RankFourTensor & E_ijkl, const std::vector<Real> & cumulative_pm) const { if (!_use_custom_cto) return TensorMechanicsPlasticModel::consistentTangentOperator(trial_stress, intnl_old, stress, intnl, E_ijkl, cumulative_pm); Real df_dq; Real alpha; if (trial_stress.trace() >= tensile_strength(intnl_old)) { df_dq = -dtensile_strength(intnl); alpha = 1.0; } else { df_dq = dcompressive_strength(intnl); alpha = -1.0; } RankTwoTensor elas; for (unsigned int i = 0; i < 3; ++i) for (unsigned int j = 0; j < 3; ++j) for (unsigned int k = 0; k < 3; ++k) elas(i, j) += E_ijkl(i, j, k, k); const Real hw = -df_dq + alpha * elas.trace(); return E_ijkl - alpha / hw * elas.outerProduct(elas); }
Real TensorMechanicsPlasticMeanCapTC::dhardPotential_dintnl(const RankTwoTensor & stress, Real intnl) const { const Real tr = stress.trace(); const Real t_str = tensile_strength(intnl); if (tr >= t_str) return 0.0; const Real c_str = compressive_strength(intnl); if (tr <= c_str) return 0.0; const Real dt = dtensile_strength(intnl); const Real dc = dcompressive_strength(intnl); return - std::sin(M_PI * (tr - c_str) / (t_str - c_str)) * M_PI / std::pow(t_str - c_str, 2) * ((tr - t_str) * dc - (tr - c_str) * dt); }
bool TensorMechanicsPlasticTensileMulti::returnPlane(const std::vector<Real> & eigvals, const std::vector<std::vector<Real> > & n, std::vector<Real> & dpm, RankTwoTensor & returned_stress, const Real & intnl_old, const Real & initial_guess) const { // the returned point, "a", is defined by f2=0 and // a = p - dpm[2]*n2. // This is a vector equation in // principal stress space, and dpm[2] is the third // plasticity multiplier (dpm[0]=0=dpm[1] for return // to the plane) and "p" is the starting // position (p=eigvals). // (Kuhn-Tucker demands that dpm[2]>=0, but we leave checking // that condition for later.) // Since f2=0, we must have a[2]=tensile_strength, // so we can just look at the [2] component of the // equation, which yields // n[2][2]*dpm[2] - eigvals[2] + str = 0 // For hardening, str=tensile_strength(intnl_old+dpm[2]), // and we want to solve for dpm[2]. // Use Newton-Raphson with initial guess dpm[2] = initial_guess dpm[2] = initial_guess; Real residual = n[2][2]*dpm[2] - eigvals[2] + tensile_strength(intnl_old + dpm[2]); Real jacobian; unsigned int iter = 0; do { jacobian = n[2][2] + dtensile_strength(intnl_old + dpm[2]); dpm[2] += -residual/jacobian; if (iter > _max_iters) // not converging return false; residual = n[2][2]*dpm[2] - eigvals[2] + tensile_strength(intnl_old + dpm[2]); iter ++; } while (residual*residual > _f_tol*_f_tol); dpm[0] = 0; dpm[1] = 0; returned_stress(0, 0) = eigvals[0] - dpm[2]*n[2][0]; returned_stress(1, 1) = eigvals[1] - dpm[2]*n[2][1]; returned_stress(2, 2) = eigvals[2] - dpm[2]*n[2][2]; return true; }
Real TensorMechanicsPlasticTensile::dyieldFunction_dintnl(const RankTwoTensor & /*stress*/, Real intnl) const { return -dtensile_strength(intnl); }
void TensorMechanicsPlasticTensileMulti::dyieldFunction_dintnlV(const RankTwoTensor & /*stress*/, const Real & intnl, std::vector<Real> & df_dintnl) const { df_dintnl.assign(3, -dtensile_strength(intnl)); }
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; }
bool TensorMechanicsPlasticTensileMulti::returnEdge(const std::vector<Real> & eigvals, const std::vector<std::vector<Real> > & n, std::vector<Real> & dpm, RankTwoTensor & returned_stress, const Real & intnl_old, const Real & initial_guess) const { // work out the point to which we would return, "a". It is defined by // f1 = 0 = f2, and the normality condition: // (eigvals - a).(n1 x n2) = 0, // where eigvals is the starting position // (it is a vector in principal stress space). // To get f1=0=f2, we need a = (a0, str, str), and a0 is found // by expanding the normality condition to yield: // a0 = (-str*n1xn2[1] - str*n1xn2[2] + edotn1xn2)/n1xn2[0]; // where edotn1xn2 = eigvals.(n1 x n2) // // We need to find the plastic multipliers, dpm, defined by // eigvals - a = dpm[1]*n1 + dpm[2]*n2 // For the isotropic case, and defining eminusa = eigvals - a, // the solution is easy: // dpm[0] = 0; // dpm[1] = (eminusa[1] - eminusa[0])/(n[1][1] - n[1][0]); // dpm[2] = (eminusa[2] - eminusa[0])/(n[2][2] - n[2][0]); // // Now specialise to the isotropic case. Define // x = dpm[1] + dpm[2] = (eigvals[1] + eigvals[2] - 2*str)/(n[0][0] + n[0][1]) // Notice that the RHS is a function of x, so we solve using // Newton-Raphson starting with x=initial_guess Real x = initial_guess; Real denom = n[0][0] + n[0][1]; Real str = tensile_strength(intnl_old + x); if (_strength.modelName().compare("Constant") != 0) { // Finding x is expensive. Therefore // although x!=0 for Constant Hardening, solution // for dpm above demonstrates that we don't // actually need to know x to find the dpm for // Constant Hardening. // // However, for nontrivial Hardening, the following // is necessary Real eig = eigvals[1] + eigvals[2]; Real residual = denom*x - eig + 2*str; Real jacobian; unsigned int iter = 0; do { jacobian = denom + 2*dtensile_strength(intnl_old + x); x += -residual/jacobian; if (iter > _max_iters) // not converging return false; str = tensile_strength(intnl_old + x); residual = denom*x - eig + 2*str; iter ++; } while (residual*residual > _f_tol*_f_tol); } dpm[0] = 0; dpm[1] = ((eigvals[1]*n[0][0] - eigvals[2]*n[0][1])/(n[0][0] - n[0][1]) - str)/denom; dpm[2] = ((eigvals[2]*n[0][0] - eigvals[1]*n[0][1])/(n[0][0] - n[0][1]) - str)/denom; returned_stress(0, 0) = eigvals[0] - n[0][1]*(dpm[1] + dpm[2]); returned_stress(1, 1) = returned_stress(2, 2) = str; return true; }
bool TensorMechanicsPlasticTensileMulti::returnTip(const std::vector<Real> & eigvals, const std::vector<std::vector<Real> > & n, std::vector<Real> & dpm, RankTwoTensor & returned_stress, const Real & intnl_old, const Real & initial_guess) const { // The returned point is defined by f0=f1=f2=0. // that is, returned_stress = diag(str, str, str), where // str = tensile_strength(intnl), // where intnl = intnl_old + dpm[0] + dpm[1] + dpm[2] // The 3 plastic multipliers, dpm, are defiend by the normality condition // eigvals - str = dpm[0]*n[0] + dpm[1]*n[1] + dpm[2]*n[2] // (Kuhn-Tucker demands that all dpm are non-negative, but we leave // that checking for later.) // This is a vector equation with solution (A): // dpm[0] = triple(eigvals - str, n[1], n[2])/trip; // dpm[1] = triple(eigvals - str, n[2], n[0])/trip; // dpm[2] = triple(eigvals - str, n[0], n[1])/trip; // where trip = triple(n[0], n[1], n[2]). // By adding the three components of that solution together // we can get an equation for x = dpm[0] + dpm[1] + dpm[2], // and then our Newton-Raphson only involves one variable (x). // In the following, i specialise to the isotropic situation. Real x = initial_guess; Real denom = (n[0][0] - n[0][1])*(n[0][0] + 2*n[0][1]); Real str = tensile_strength(intnl_old + x); if (_strength.modelName().compare("Constant") != 0) { // Finding x is expensive. Therefore // although x!=0 for Constant Hardening, solution (A) // demonstrates that we don't // actually need to know x to find the dpm for // Constant Hardening. // // However, for nontrivial Hardening, the following // is necessary Real eig = eigvals[0] + eigvals[1] + eigvals[2]; Real bul = (n[0][0] + 2*n[0][1]); // and finally, the equation we want to solve is: // bul*x - eig + 3*str = 0 // where str=tensile_strength(intnl_old + x) // and x = dpm[0] + dpm[1] + dpm[2] // (Note this has units of stress, so using _f_tol as a convergence check is reasonable.) // Use Netwon-Raphson with initial guess x = 0 Real residual = bul*x - eig + 3*str; Real jacobian; unsigned int iter = 0; do { jacobian = bul + 3*dtensile_strength(intnl_old + x); x += -residual/jacobian; if (iter > _max_iters) // not converging return false; str = tensile_strength(intnl_old + x); residual = bul*x - eig + 3*str; iter ++; } while (residual*residual > _f_tol*_f_tol); } // The following is the solution (A) written above // (dpm[0] = triple(eigvals - str, n[1], n[2])/trip, etc) // in the isotropic situation dpm[0] = (n[0][0]*(eigvals[0] - str) + n[0][1]*(eigvals[0] - eigvals[1] - eigvals[2] + str))/denom; dpm[1] = (n[0][0]*(eigvals[1] - str) + n[0][1]*(eigvals[1] - eigvals[2] - eigvals[0] + str))/denom; dpm[2] = (n[0][0]*(eigvals[2] - str) + n[0][1]*(eigvals[2] - eigvals[0] - eigvals[1] + str))/denom; returned_stress(0, 0) = returned_stress(1, 1) = returned_stress(2, 2) = str; return true; }
bool TensorMechanicsPlasticMeanCapTC::returnMap(const RankTwoTensor & trial_stress, Real intnl_old, const RankFourTensor & E_ijkl, Real ep_plastic_tolerance, RankTwoTensor & returned_stress, Real & returned_intnl, std::vector<Real> & dpm, RankTwoTensor & delta_dp, std::vector<Real> & yf, bool & trial_stress_inadmissible) const { if (!(_use_custom_returnMap)) return TensorMechanicsPlasticModel::returnMap(trial_stress, intnl_old, E_ijkl, ep_plastic_tolerance, returned_stress, returned_intnl, dpm, delta_dp, yf, trial_stress_inadmissible); yf.resize(1); Real yf_orig = yieldFunction(trial_stress, intnl_old); yf[0] = yf_orig; if (yf_orig < _f_tol) { // the trial_stress is admissible trial_stress_inadmissible = false; return true; } trial_stress_inadmissible = true; // In the following we want to solve // trial_stress - stress = E_ijkl * dpm * r ...... (1) // and either // stress.trace() = tensile_strength(intnl) ...... (2a) // intnl = intnl_old + dpm ...... (3a) // or // stress.trace() = compressive_strength(intnl) ... (2b) // intnl = intnl_old - dpm ...... (3b) // The former (2a and 3a) are chosen if // trial_stress.trace() > tensile_strength(intnl_old) // while the latter (2b and 3b) are chosen if // trial_stress.trace() < compressive_strength(intnl_old) // The variables we want to solve for are stress, dpm // and intnl. We do this using a Newton approach, starting // with stress=trial_stress and intnl=intnl_old and dpm=0 const bool tensile_failure = (trial_stress.trace() >= tensile_strength(intnl_old)); const Real dirn = (tensile_failure ? 1.0 : -1.0); RankTwoTensor n; // flow direction, which is E_ijkl * r for (unsigned i = 0; i < 3; ++i) for (unsigned j = 0; j < 3; ++j) for (unsigned k = 0; k < 3; ++k) n(i, j) += dirn * E_ijkl(i, j, k, k); const Real n_trace = n.trace(); // Perform a Newton-Raphson to find dpm when // residual = trial_stress.trace() - tensile_strength(intnl) - dpm * n.trace() [for tensile_failure=true] // or // residual = trial_stress.trace() - compressive_strength(intnl) - dpm * n.trace() [for tensile_failure=false] Real trial_trace = trial_stress.trace(); Real residual; Real jac; dpm[0] = 0; unsigned int iter = 0; do { if (tensile_failure) { residual = trial_trace - tensile_strength(intnl_old + dpm[0]) - dpm[0] * n_trace; jac = -dtensile_strength(intnl_old + dpm[0]) - n_trace; } else { residual = trial_trace - compressive_strength(intnl_old - dpm[0]) - dpm[0] * n_trace; jac = -dcompressive_strength(intnl_old - dpm[0]) - n_trace; } dpm[0] += -residual/jac; if (iter > _max_iters) // not converging return false; iter++; } while (residual*residual > _f_tol*_f_tol); // set the returned values yf[0] = 0; returned_intnl = intnl_old + dirn * dpm[0]; returned_stress = trial_stress - dpm[0] * n; delta_dp = dpm[0] * dirn * returned_stress.dtrace(); return true; }
Real TensorMechanicsPlasticWeakPlaneTensileN::dyieldFunction_dintnl(const RankTwoTensor & /*stress*/, const Real & intnl) const { return -dtensile_strength(intnl); }