void TensorMechanicsPlasticMohrCoulombMulti::yieldFunctionV(const RankTwoTensor & stress, const Real & intnl, std::vector<Real> & f) const { std::vector<Real> eigvals; stress.symmetricEigenvalues(eigvals); eigvals[0] += _shift; eigvals[2] -= _shift; Real sinphi = std::sin(phi(intnl)); Real cosphi = std::cos(phi(intnl)); Real cohcos = cohesion(intnl)*cosphi; // Naively it seems a shame to have 6 yield functions active instead of just // 3. But 3 won't do. Eg, think of a loading with eigvals[0]=eigvals[1]=eigvals[2] // Then to return to the yield surface would require 2 positive plastic multipliers // and one negative one. Boo hoo. f.resize(6); f[0] = 0.5*(eigvals[0] - eigvals[1]) + 0.5*(eigvals[0] + eigvals[1])*sinphi - cohcos; f[1] = 0.5*(eigvals[1] - eigvals[0]) + 0.5*(eigvals[0] + eigvals[1])*sinphi - cohcos; f[2] = 0.5*(eigvals[0] - eigvals[2]) + 0.5*(eigvals[0] + eigvals[2])*sinphi - cohcos; f[3] = 0.5*(eigvals[2] - eigvals[0]) + 0.5*(eigvals[0] + eigvals[2])*sinphi - cohcos; f[4] = 0.5*(eigvals[1] - eigvals[2]) + 0.5*(eigvals[1] + eigvals[2])*sinphi - cohcos; f[5] = 0.5*(eigvals[2] - eigvals[1]) + 0.5*(eigvals[1] + eigvals[2])*sinphi - cohcos; }
void TensorMechanicsPlasticTensileMulti::yieldFunctionV(const RankTwoTensor & stress, const Real & intnl, std::vector<Real> & f) const { std::vector<Real> eigvals; stress.symmetricEigenvalues(eigvals); Real str = tensile_strength(intnl); f.resize(3); f[0] = eigvals[0] + _shift - str; f[1] = eigvals[1] - str; f[2] = eigvals[2] - _shift - str; }
void TensorMechanicsPlasticMohrCoulombMulti::yieldFunctionV(const RankTwoTensor & stress, Real intnl, std::vector<Real> & f) const { std::vector<Real> eigvals; stress.symmetricEigenvalues(eigvals); eigvals[0] += _shift; eigvals[2] -= _shift; const Real sinphi = std::sin(phi(intnl)); const Real cosphi = std::cos(phi(intnl)); const Real cohcos = cohesion(intnl)*cosphi; yieldFunctionEigvals(eigvals[0], eigvals[1], eigvals[2], sinphi, cohcos, f); }
void TensorMechanicsPlasticMohrCoulombMulti::dyieldFunction_dintnlV(const RankTwoTensor & stress, Real intnl, std::vector<Real> & df_dintnl) const { std::vector<Real> eigvals; stress.symmetricEigenvalues(eigvals); eigvals[0] += _shift; eigvals[2] -= _shift; const Real sin_angle = std::sin(phi(intnl)); const Real cos_angle = std::cos(phi(intnl)); const Real dsin_angle = cos_angle*dphi(intnl); const Real dcos_angle = -sin_angle*dphi(intnl); const Real dcohcos = dcohesion(intnl)*cos_angle + cohesion(intnl)*dcos_angle; df_dintnl.resize(6); df_dintnl[0] = df_dintnl[1] = 0.5*(eigvals[0] + eigvals[1])*dsin_angle - dcohcos; df_dintnl[2] = df_dintnl[3] = 0.5*(eigvals[0] + eigvals[2])*dsin_angle - dcohcos; df_dintnl[4] = df_dintnl[5] = 0.5*(eigvals[1] + eigvals[2])*dsin_angle - dcohcos; }
Real TensorMechanicsPlasticTensile::yieldFunction(const RankTwoTensor & stress, const Real & intnl) const { Real mean_stress = stress.trace()/3.0; Real sin3Lode = stress.sin3Lode(_lode_cutoff, 0); if (sin3Lode <= _sin3tt) { // the non-edge-smoothed version std::vector<Real> eigvals; stress.symmetricEigenvalues(eigvals); return mean_stress + std::sqrt(_small_smoother2 + std::pow(eigvals[2] - mean_stress, 2)) - tensile_strength(intnl); } else { // the edge-smoothed version Real kk = _aaa + _bbb*sin3Lode + _ccc*std::pow(sin3Lode, 2); Real sibar2 = stress.secondInvariant(); return mean_stress + std::sqrt(_small_smoother2 + sibar2*std::pow(kk, 2)) - tensile_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; }
void RankTwoEigenRoutinesTest::dsymmetricEigenvaluesTest() { // this derivative is less trivial than dtrace and dsecondInvariant, // so let's check with a finite-difference approximation Real ep = 1E-5; // small finite-difference parameter std::vector<Real> eigvals; // eigenvalues in ascending order provided by RankTwoTensor std::vector<RankTwoTensor> deriv; // derivatives of these eigenvalues provided by RankTwoTensor RankTwoTensor mep; // the RankTwoTensor with successive entries shifted by ep std::vector<Real> eigvalsep; // eigenvalues of mep in ascending order std::vector<Real> eigvalsep_minus; // for equal-eigenvalue cases, i take a central difference _m2.dsymmetricEigenvalues(eigvals, deriv); mep = _m2; for (unsigned i = 0 ; i < 3 ; ++i) for (unsigned j = 0 ; j < 3 ; ++j) { mep(i, j) += ep; mep.symmetricEigenvalues(eigvalsep); for (unsigned k = 0 ; k < 3 ; ++k) CPPUNIT_ASSERT_DOUBLES_EQUAL((eigvalsep[k] - eigvals[k])/ep, deriv[k](i, j), ep); mep(i, j) -= ep; } _m3.dsymmetricEigenvalues(eigvals, deriv); mep = _m3; for (unsigned i = 0 ; i < 3 ; ++i) for (unsigned j = 0 ; j < 3 ; ++j) { mep(i, j) += ep; mep.symmetricEigenvalues(eigvalsep); for (unsigned k = 0 ; k < 3 ; ++k) CPPUNIT_ASSERT_DOUBLES_EQUAL((eigvalsep[k] - eigvals[k])/ep, deriv[k](i, j), ep); mep(i, j) -= ep; } // the equal-eigenvalue cases follow: _m5.dsymmetricEigenvalues(eigvals, deriv); mep = _m5; for (unsigned i = 0 ; i < 3 ; ++i) for (unsigned j = 0 ; j < 3 ; ++j) { // here i use a central difference to define the // discontinuous derivative mep(i, j) += ep/2.0; mep.symmetricEigenvalues(eigvalsep); mep(i, j) -= ep; mep.symmetricEigenvalues(eigvalsep_minus); for (unsigned k = 0 ; k < 3 ; ++k) CPPUNIT_ASSERT_DOUBLES_EQUAL((eigvalsep[k] - eigvalsep_minus[k])/ep, deriv[k](i, j), ep); mep(i, j) += ep/2.0; } _m6.dsymmetricEigenvalues(eigvals, deriv); mep = _m6; for (unsigned i = 0 ; i < 3 ; ++i) for (unsigned j = 0 ; j < 3 ; ++j) { // here i use a central difference to define the // discontinuous derivative mep(i, j) += ep/2.0; mep.symmetricEigenvalues(eigvalsep); mep(i, j) -= ep; mep.symmetricEigenvalues(eigvalsep_minus); for (unsigned k = 0 ; k < 3 ; ++k) CPPUNIT_ASSERT_DOUBLES_EQUAL((eigvalsep[k] - eigvalsep_minus[k])/ep, deriv[k](i, j), ep); mep(i, j) += ep/2.0; } _m7.dsymmetricEigenvalues(eigvals, deriv); mep = _m7; for (unsigned i = 0 ; i < 3 ; ++i) for (unsigned j = 0 ; j < 3 ; ++j) { // here i use a central difference to define the // discontinuous derivative mep(i, j) += ep/2.0; mep.symmetricEigenvalues(eigvalsep); mep(i, j) -= ep; mep.symmetricEigenvalues(eigvalsep_minus); for (unsigned k = 0 ; k < 3 ; ++k) CPPUNIT_ASSERT_DOUBLES_EQUAL((eigvalsep[k] - eigvalsep_minus[k])/ep, deriv[k](i, j), ep); mep(i, j) += ep/2.0; } _m8.dsymmetricEigenvalues(eigvals, deriv); mep = _m8; for (unsigned i = 0 ; i < 3 ; ++i) for (unsigned j = 0 ; j < 3 ; ++j) { // here i use a central difference to define the // discontinuous derivative mep(i, j) += ep/2.0; mep.symmetricEigenvalues(eigvalsep); mep(i, j) -= ep; mep.symmetricEigenvalues(eigvalsep_minus); for (unsigned k = 0 ; k < 3 ; ++k) CPPUNIT_ASSERT_DOUBLES_EQUAL((eigvalsep[k] - eigvalsep_minus[k])/ep, deriv[k](i, j), ep); mep(i, j) += ep/2.0; } }
void TensorMechanicsPlasticMohrCoulombMulti::activeConstraints(const std::vector<Real> & /*f*/, const RankTwoTensor & stress, const Real & intnl, std::vector<bool> & act) const { act.assign(6, false); std::vector<Real> eigvals; stress.symmetricEigenvalues(eigvals); eigvals[0] += _shift; eigvals[2] -= _shift; Real sinphi = std::sin(phi(intnl)); Real cosphi = std::cos(phi(intnl)); Real coh = cohesion(intnl); Real cohcot = coh*cosphi/sinphi; std::vector<Real> v(3); v[0] = eigvals[0] - cohcot; v[1] = eigvals[1] - cohcot; v[2] = eigvals[2] - cohcot; // naively there are lots of different combinations // to try. Eg, there are 6 yield surfaces, and so // for returning-to-the-tip, there are 6*5*4 possible // combinations of 3 yield functions that we could try. // However, with the constraint v0<=v1<=v2, and symmetry, // the following combinations of act are checked: // 110100 (tip) (about 43%) // 010101 (tip) (about 1%) // 010100 (edge) (about 22%) // 000101 (edge) (about 22%) // 000100 (plane) (about 12%) // Some other "tip" combinations are tried by // FiniteStrainMultiPlasticity, in about 0.001% of cases, // but these are not work checking. // these are the normals to the 6 yield surfaces (flow directions) std::vector<std::vector<Real> > n(6); Real sinpsi = std::sin(psi(intnl)); Real oneminus = 1 - sinpsi; Real oneplus = 1 + sinpsi; n[0].resize(3); n[0][0] = oneplus ; n[0][1] = -oneminus ; n[0][2] = 0; n[1].resize(3); n[1][0] = -oneminus ; n[1][1] = oneplus ; n[1][2] = 0; n[2].resize(3); n[2][0] = oneplus ; n[2][1] = 0 ; n[2][2] = -oneminus; n[3].resize(3); n[3][0] = -oneminus ; n[3][1] = 0 ; n[3][2] = oneplus; n[4].resize(3); n[4][0] = 0 ; n[4][1] = oneplus ; n[4][2] = -oneminus; n[5].resize(3); n[5][0] = 0 ; n[5][1] = -oneminus ; n[5][2] = oneplus; // Check for return to the tip. // For tip-return to satisfy Kuhn-Tucker we need // v = a*n[a] + b*n[b] + c*n[c] // with a, b, and c all being non-negative (they are // the plasticity multipliers) Real denom; // 110100 (tip) denom = triple(n[0], n[1], n[3]); if (triple(v, n[0], n[1])/denom >= 0 && triple(v, n[1], n[3])/denom >= 0 && triple(v, n[3], n[0])/denom >= 0) { act[0] = act[1] = act[3] = true; return; } // 010101 (tip) denom = triple(n[1], n[3], n[5]); if (triple(v, n[1], n[3])/denom >= 0 && triple(v, n[3], n[5])/denom >= 0 && triple(v, n[5], n[1])/denom >= 0) { act[1] = act[3] = act[5] = true; return; } // the following are tangents to the 1, 3, and 5 yield surfaces // used below. std::vector<Real> t1(3); t1[0] = oneplus ; t1[1] = oneminus ; t1[2] = 0; std::vector<Real> t3(3); t3[0] = oneplus ; t3[1] = 0 ; t3[2] = oneminus; std::vector<Real> t5(3); t5[0] = 0 ; t5[1] = oneplus ; t5[2] = oneminus; // 010100 (edge) std::vector<Real> n1xn3(3); n1xn3[0] = oneplus ; n1xn3[1] = oneminus ; n1xn3[2] = oneminus; // work out the point to which we would return, "a". It is defined by // f1 = 0 = f3, and that (p - a).(n1 x n3) = 0, where "p" is the // starting position (p = eigvals). // In the following a = (lam0, lam2, lam2) Real pdotn1xn3 = dot(eigvals, n1xn3); Real lam0 = pdotn1xn3 - 4*coh*cosphi*oneminus/(1 + sinphi); lam0 /= oneplus + 2*oneminus*(1 - sinphi)/(1 + sinphi); Real lam1 = lam0*(1 - sinphi)/(1 + sinphi) + 2*coh*cosphi/(1 + sinphi); std::vector<Real> pminusa(3); pminusa[0] = eigvals[0] - lam0; pminusa[1] = eigvals[1] - lam1; pminusa[2] = eigvals[2] - lam1; if (dot(pminusa, t3)/dot(n[1], t3) >= 0 && dot(pminusa, t1)/dot(n[3], t1) >= 0) { act[1] = act[3] = true; return; } // 000101 (edge) std::vector<Real> n3xn5(3); n3xn5[0] = oneplus ; n3xn5[1] = oneplus ; n3xn5[2] = oneminus; // work out the point to which we would return, "a". It is defined by // f3 = 0 = f5, and that (p - a).(n3 x n5) = 0, where "p" is the // starting position (p = eigvals). // In the following a = (lam0, lam2, lam2) Real pdotn3xn5 = dot(eigvals, n3xn5); Real lam2 = pdotn3xn5 + 4*coh*cosphi*oneplus/(1 - sinphi); lam2 /= oneminus + 2*oneplus*(1 + sinphi)/(1 - sinphi); lam1 = lam2*(1 + sinphi)/(1 - sinphi) - 2*coh*cosphi/(1 - sinphi); pminusa[0] = eigvals[0] - lam1; pminusa[1] = eigvals[1] - lam1; pminusa[2] = eigvals[2] - lam2; if (dot(pminusa, t5)/dot(n[3], t5) >= 0 && dot(pminusa, t3)/dot(n[5], t3) >= 0) { act[3] = act[5] = true; return; } // 000100 (plane) act[3] = true; return; }