void TensorMechanicsPlasticTensileMulti::activeConstraints(const std::vector<Real> & f, const RankTwoTensor & stress, const Real & intnl, const RankFourTensor & Eijkl, std::vector<bool> & act, RankTwoTensor & returned_stress) const { act.assign(3, false); if (f[0] <= _f_tol && f[1] <= _f_tol && f[2] <= _f_tol) { returned_stress = stress; return; } returned_stress = RankTwoTensor(); std::vector<Real> eigvals; RankTwoTensor eigvecs; stress.symmetricEigenvaluesEigenvectors(eigvals, eigvecs); eigvals[0] += _shift; eigvals[2] -= _shift; Real str = tensile_strength(intnl); std::vector<Real> v(3); v[0] = eigvals[0] - str; v[1] = eigvals[1] - str; v[2] = eigvals[2] - str; // these are the normals to the 3 yield surfaces std::vector<std::vector<Real> > n(3); n[0].resize(3); n[0][0] = 1 ; n[0][1] = 0 ; n[0][2] = 0; n[1].resize(3); n[1][0] = 0 ; n[1][1] = 1 ; n[1][2] = 0; n[2].resize(3); n[2][0] = 0 ; n[2][1] = 0 ; n[2][2] = 1; // the flow directions are these n multiplied by Eijkl. // I re-use the name "n" for the flow directions // In the following I assume that the Eijkl is // for an isotropic situation. This is the most // common when using TensileMulti, and remember // that the returned_stress need not be perfect // anyway. // I divide by E(0,0,0,0) so the n remain of order 1 Real ratio = Eijkl(1,1,0,0)/Eijkl(0,0,0,0); n[0][1] = n[0][2] = ratio; n[1][0] = n[1][2] = ratio; n[2][0] = n[2][1] = ratio; // 111 (tip) // For tip-return to satisfy Kuhn-Tucker, we need // v = alpha*n[0] + beta*n[1] * gamma*n[2] // with alpha, beta, and gamma all being non-negative (they are // the plasticity multipliers) Real denom = triple(n[0], n[1], n[2]); if (triple(v, n[0], n[1])/denom >= 0 && triple(v, n[1], n[2])/denom >= 0 && triple(v, n[2], n[0])/denom >= 0) { act[0] = act[1] = act[2] = true; returned_stress(0, 0) = returned_stress(1, 1) = returned_stress(2, 2) = str; returned_stress = eigvecs*returned_stress*(eigvecs.transpose()); return; } // 011 (edge) std::vector<Real> n1xn2(3); n1xn2[0] = n[1][1]*n[2][2] - n[1][2]*n[2][1]; n1xn2[1] = n[1][2]*n[2][0] - n[1][0]*n[2][2]; n1xn2[2] = n[1][0]*n[2][1] - n[1][1]*n[2][0]; // work out the point to which we would return, "a". It is defined by // f1 = 0 = f2, and that (p - a).(n1 x n2) = 0, where "p" is the // starting position (p = eigvals). // In the following a = (a0, str, str) Real pdotn1xn2 = dot(eigvals, n1xn2); Real a0 = (-str*n1xn2[1] - str*n1xn2[2] + pdotn1xn2)/n1xn2[0]; // we need p - a = alpha*n1 + beta*n2, where alpha and beta are non-negative // for Kuhn-Tucker to be satisfied std::vector<Real> pminusa(3); pminusa[0] = eigvals[0] - a0; pminusa[1] = v[1]; pminusa[2] = v[2]; if ((pminusa[2] - pminusa[0])/(1.0 - ratio) >= 0 && (pminusa[1] - pminusa[0])/(1.0 - ratio) >= 0) { returned_stress(0, 0) = a0; returned_stress(1, 1) = str; returned_stress(2, 2) = str; returned_stress = eigvecs*returned_stress*(eigvecs.transpose()); act[1] = act[2] = true; return; } // 001 (plane) // the returned point, "a", is defined by f2=0 and // a = p - alpha*n2 Real alpha = (eigvals[2] - str)/n[2][2]; act[2] = true; returned_stress(0, 0) = eigvals[0] - alpha*n[2][0]; returned_stress(1, 1) = eigvals[1] - alpha*n[2][1]; returned_stress(2, 2) = str; returned_stress = eigvecs*returned_stress*(eigvecs.transpose()); return; }
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; }