Exemplo n.º 1
0
Real
TensorMechanicsPlasticMeanCapTC::yieldFunction(const RankTwoTensor & stress, Real intnl) const
{
  const Real tr = stress.trace();
  const Real t_str = tensile_strength(intnl);
  if (tr >= t_str)
    return tr - t_str;
  const Real c_str = compressive_strength(intnl);
  if (tr <= c_str)
    return -(tr - c_str);
  // the following is zero at tr = t_str, and at tr = c_str
  // it also has derivative = 1 at tr = t_str, and derivative = -1 at tr = c_str
  // it also has second derivative = 0, at these points.
  // This makes the complete yield function C2 continuous.
  return (c_str - t_str) / M_PI * std::sin(M_PI * (tr - c_str) / (t_str - c_str));
}
Exemplo n.º 2
0
void
TensorMechanicsPlasticMeanCapTC::activeConstraints(const std::vector<Real> & f, const RankTwoTensor & stress, Real intnl, const RankFourTensor & Eijkl, std::vector<bool> & act, RankTwoTensor & returned_stress) const
{
  act.assign(1, false);

  if (f[0] <= _f_tol)
  {
    returned_stress = stress;
    return;
  }

  const Real tr = stress.trace();
  const Real t_str = tensile_strength(intnl);
  Real str;
  Real dirn;
  if (tr >= t_str)
  {
    str = t_str;
    dirn = 1.0;
  }
  else
  {
    str = compressive_strength(intnl);
    dirn = -1.0;
  }

  RankTwoTensor n; // flow direction
  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 * Eijkl(i, j, k, k);

  // returned_stress = stress - gamma*n
  // and taking the trace of this and using
  // Tr(returned_stress) = str, gives
  // gamma = (Tr(stress) - str)/Tr(n)
  Real gamma = (stress.trace() - str) / n.trace();

  for (unsigned i = 0; i < 3; ++i)
    for (unsigned j = 0; j < 3; ++j)
      returned_stress(i, j) = stress(i, j) - gamma * n(i, j);

  act[0] = true;
}
Exemplo n.º 3
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);
  }
}
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
TensorMechanicsPlasticTensileMulti::doReturnMap(const RankTwoTensor & trial_stress, const 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
{
  mooseAssert(dpm.size() == 3, "TensorMechanicsPlasticTensileMulti size of dpm should be 3 but it is " << dpm.size());

  std::vector<Real> eigvals;
  RankTwoTensor eigvecs;
  trial_stress.symmetricEigenvaluesEigenvectors(eigvals, eigvecs);
  eigvals[0] += _shift;
  eigvals[2] -= _shift;

  Real str = tensile_strength(intnl_old);

  yf.resize(3);
  yf[0] = eigvals[0] - str;
  yf[1] = eigvals[1] - str;
  yf[2] = eigvals[2] - str;

  if (yf[0] <= _f_tol && yf[1] <= _f_tol && yf[2] <= _f_tol)
  {
    // purely elastic (trial_stress, intnl_old)
    trial_stress_inadmissible = false;
    return true;
  }

  trial_stress_inadmissible = true;
  delta_dp.zero();
  returned_stress.zero();

  // In the following i often assume that E_ijkl is
  // for an isotropic situation.  This reduces FLOPS
  // substantially which is important since the returnMap
  // is potentially the most compute-intensive function
  // of a simulation.
  // In many comments i write the general expression, and
  // i hope that might guide future coders if they are
  // generalising to a non-istropic E_ijkl

  // n[alpha] = E_ijkl*r[alpha]_kl expressed in principal stress space
  // (alpha = 0, 1, 2, corresponding to the three surfaces)
  // Note that in principal stress space, the flow
  // directions are, expressed in 'vector' form,
  // r[0] = (1,0,0), r[1] = (0,1,0), r[2] = (0,0,1).
  // Similar for _n:
  // so _n[0] = E_ij00*r[0], _n[1] = E_ij11*r[1], _n[2] = E_ij22*r[2]
  // In the following I assume that the E_ijkl is
  // for an isotropic situation.
  // In the anisotropic situation, we couldn't express
  // the flow directions as vectors in the same principal
  // stress space as the stress: they'd be full rank-2 tensors
  std::vector<std::vector<Real> > n(3);
  for (unsigned i = 0 ; i < 3 ; ++i)
    n[i].resize(3);
  n[0][0] = E_ijkl(0,0,0,0);
  n[0][1] = E_ijkl(1,1,0,0);
  n[0][2] = E_ijkl(2,2,0,0);
  n[1][0] = E_ijkl(0,0,1,1);
  n[1][1] = E_ijkl(1,1,1,1);
  n[1][2] = E_ijkl(2,2,1,1);
  n[2][0] = E_ijkl(0,0,2,2);
  n[2][1] = E_ijkl(1,1,2,2);
  n[2][2] = E_ijkl(2,2,2,2);


  // With non-zero Poisson's ratio and hardening
  // it is not computationally cheap to know whether
  // the trial stress will return to the tip, edge,
  // or plane.  The following is correct for zero
  // Poisson's ratio and no hardening, and at least
  // gives a not-completely-stupid guess in the
  // more general case.
  // trial_order[0] = type of return to try first
  // trial_order[1] = type of return to try second
  // trial_order[2] = type of return to try third
  std::vector<int> trial_order(3);
  if (yf[0] > 0) // all the yield functions are positive, since eigvals are ordered eigvals[0] <= eigvals[1] <= eigvals[2]
  {
    trial_order[0] = tip;
    trial_order[1] = edge;
    trial_order[2] = plane;
  }
  else if (yf[1] > 0)  // two yield functions are positive
  {
    trial_order[0] = edge;
    trial_order[1] = tip;
    trial_order[2] = plane;
  }
  else
  {
    trial_order[0] = plane;
    trial_order[1] = edge;
    trial_order[2] = tip;
  }

  unsigned trial;
  bool nr_converged;
  for (trial = 0 ; trial < 3 ; ++trial)
  {
    switch (trial_order[trial])
    {
      case tip:
        nr_converged = returnTip(eigvals, n, dpm, returned_stress, intnl_old, 0);
        break;
      case edge:
        nr_converged = returnEdge(eigvals, n, dpm, returned_stress, intnl_old, 0);
        break;
      case plane:
        nr_converged = returnPlane(eigvals, n, dpm, returned_stress, intnl_old, 0);
        break;
    }
    str = tensile_strength(intnl_old + dpm[0] + dpm[1] + dpm[2]);
    if (nr_converged && KuhnTuckerOK(returned_stress, dpm, str))
      break;
  }

  if (trial == 3)
  {
    Moose::err << "Trial stress = \n";
    trial_stress.print(Moose::err);
    Moose::err << "Internal parameter = " << intnl_old << "\n";
    mooseError("TensorMechanicsPlasticTensileMulti: FAILURE!  You probably need to implement a line search\n");
    // failure - must place yield function values at trial stress into yf
    str = tensile_strength(intnl_old);
    yf[0] = eigvals[0] - str;
    yf[1] = eigvals[1] - str;
    yf[2] = eigvals[2] - str;
    return false;
  }

  // success

  returned_intnl = intnl_old;
  for (unsigned i = 0 ; i < 3 ; ++i)
  {
    yf[i] = returned_stress(i, i) - str;
    delta_dp(i, i) = dpm[i];
    returned_intnl += dpm[i];
  }
  returned_stress = eigvecs*returned_stress*(eigvecs.transpose());
  delta_dp = eigvecs*delta_dp*(eigvecs.transpose());
  return true;
}
Exemplo n.º 7
0
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
TensorMechanicsPlasticWeakPlaneTensile::yieldFunction(const RankTwoTensor & stress, const Real & intnl) const
{
  return stress(2,2) - tensile_strength(intnl);
}
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;
}