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;
}
Beispiel #5
0
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;
}