ComputeCosseratElasticityTensor::ComputeCosseratElasticityTensor(const InputParameters & parameters)
  : ComputeElasticityTensorBase(parameters),
    _Eijkl(getParam<std::vector<Real>>("E_ijkl"),
           (RankFourTensor::FillMethod)(int)getParam<MooseEnum>("fill_method")),
    _Bijkl(getParam<std::vector<Real>>("B_ijkl"),
           (RankFourTensor::FillMethod)(int)getParam<MooseEnum>("fill_method_bending")),
    _elastic_flexural_rigidity_tensor(
        declareProperty<RankFourTensor>("elastic_flexural_rigidity_tensor"))
{
  if (!isParamValid("elasticity_tensor_prefactor"))
    issueGuarantee(_elasticity_tensor_name, Guarantee::CONSTANT_IN_TIME);
}
ComputeIsotropicElasticityTensor::ComputeIsotropicElasticityTensor(
    const InputParameters & parameters)
  : ComputeElasticityTensorBase(parameters),
    _bulk_modulus_set(parameters.isParamValid("bulk_modulus")),
    _lambda_set(parameters.isParamValid("lambda")),
    _poissons_ratio_set(parameters.isParamValid("poissons_ratio")),
    _shear_modulus_set(parameters.isParamValid("shear_modulus")),
    _youngs_modulus_set(parameters.isParamValid("youngs_modulus")),
    _bulk_modulus(_bulk_modulus_set ? getParam<Real>("bulk_modulus") : -1),
    _lambda(_lambda_set ? getParam<Real>("lambda") : -1),
    _poissons_ratio(_poissons_ratio_set ? getParam<Real>("poissons_ratio") : -1),
    _shear_modulus(_shear_modulus_set ? getParam<Real>("shear_modulus") : -1),
    _youngs_modulus(_youngs_modulus_set ? getParam<Real>("youngs_modulus") : -1)
{
  unsigned int num_elastic_constants = _bulk_modulus_set + _lambda_set + _poissons_ratio_set +
                                       _shear_modulus_set + _youngs_modulus_set;
  if (num_elastic_constants != 2)
    mooseError("Exactly two elastic constants must be defined for material '" + name() + "'.");

  // all tensors created by this class are always isotropic
  issueGuarantee(_elasticity_tensor_name, Guarantee::ISOTROPIC);
  if (!isParamValid("elasticity_tensor_prefactor"))
    issueGuarantee(_elasticity_tensor_name, Guarantee::CONSTANT_IN_TIME);

  if (_bulk_modulus_set && _bulk_modulus <= 0.0)
    mooseError("Bulk modulus must be positive in material '" + name() + "'.");

  if (_poissons_ratio_set && (_poissons_ratio <= -1.0 || _poissons_ratio >= 0.5))
    mooseError("Poissons ratio must be greater than -1 and less than 0.5 in "
               "material '" +
               name() + "'.");

  if (_shear_modulus_set && _shear_modulus < 0.0)
    mooseError("Shear modulus must not be negative in material '" + name() + "'.");

  if (_youngs_modulus_set && _youngs_modulus <= 0.0)
    mooseError("Youngs modulus must be positive in material '" + name() + "'.");

  if (_youngs_modulus_set && _poissons_ratio_set)
  {
    _Cijkl.fillSymmetricIsotropicEandNu(_youngs_modulus, _poissons_ratio);
    return;
  }

  std::vector<Real> iso_const(2);

  if (_lambda_set && _shear_modulus_set)
  {
    iso_const[0] = _lambda;
    iso_const[1] = _shear_modulus;
  }
  else if (_shear_modulus_set && _bulk_modulus_set)
  {
    iso_const[0] = _bulk_modulus - 2.0 / 3.0 * _shear_modulus;
    iso_const[1] = _shear_modulus;
  }
  else if (_poissons_ratio_set && _bulk_modulus_set)
  {
    iso_const[0] = 3.0 * _bulk_modulus * _poissons_ratio / (1.0 + _poissons_ratio);
    iso_const[1] =
        3.0 * _bulk_modulus * (1.0 - 2.0 * _poissons_ratio) / (2.0 * (1.0 + _poissons_ratio));
  }
  else if (_lambda_set && _bulk_modulus_set)
  {
    iso_const[0] = _lambda;
    iso_const[1] = 3.0 * (_bulk_modulus - _lambda) / 2.0;
  }
  else if (_shear_modulus_set && _youngs_modulus_set)
  {
    iso_const[0] = _shear_modulus * (_youngs_modulus - 2.0 * _shear_modulus) /
                   (3.0 * _shear_modulus - _youngs_modulus);
    iso_const[1] = _shear_modulus;
  }
  else if (_shear_modulus_set && _poissons_ratio_set)
  {
    iso_const[0] = 2.0 * _shear_modulus * _poissons_ratio / (1.0 - 2.0 * _poissons_ratio);
    iso_const[1] = _shear_modulus;
  }
  else if (_youngs_modulus_set && _bulk_modulus_set)
  {
    iso_const[0] = 3.0 * _bulk_modulus * (3.0 * _bulk_modulus - _youngs_modulus) /
                   (9.0 * _bulk_modulus - _youngs_modulus);
    iso_const[1] = 3.0 * _bulk_modulus * _youngs_modulus / (9.0 * _bulk_modulus - _youngs_modulus);
  }
  else if (_lambda_set && _poissons_ratio_set)
  {
    iso_const[0] = _lambda;
    iso_const[1] = _lambda * (1.0 - 2.0 * _poissons_ratio) / (2.0 * _poissons_ratio);
  }
  else if (_lambda_set && _youngs_modulus_set)
  {
    iso_const[0] = _lambda;
    iso_const[1] = (_youngs_modulus - 3.0 * _lambda +
                    std::sqrt(_youngs_modulus * _youngs_modulus + 9.0 * _lambda * _lambda +
                              2.0 * _youngs_modulus * _lambda)) /
                   4.0;
  }
  else
    mooseError("Incorrect combination of elastic properties in ComputeIsotropicElasticityTensor.");

  // Fill elasticity tensor
  _Cijkl.fillFromInputVector(iso_const, RankFourTensor::symmetric_isotropic);
}
ComputeLayeredCosseratElasticityTensor::ComputeLayeredCosseratElasticityTensor(
    const InputParameters & parameters)
  : ComputeElasticityTensorBase(parameters),
    _Eijkl(RankFourTensor()),
    _Bijkl(RankFourTensor()),
    _Cijkl(RankFourTensor()),
    _elastic_flexural_rigidity_tensor(
        declareProperty<RankFourTensor>("elastic_flexural_rigidity_tensor")),
    _compliance(declareProperty<RankFourTensor>(_base_name + "compliance_tensor"))
{
  // all tensors created by this class are always constant in time
  issueGuarantee(_elasticity_tensor_name, Guarantee::CONSTANT_IN_TIME);

  const Real E = getParam<Real>("young");
  const Real nu = getParam<Real>("poisson");
  const Real b = getParam<Real>("layer_thickness");
  const Real kn = getParam<Real>("joint_normal_stiffness");
  const Real ks = getParam<Real>("joint_shear_stiffness");

  // shear modulus of solid
  const Real G = 0.5 * E / (1.0 + nu);
  // shear modulus of jointed material
  const Real Gprime = G * b * ks / (b * ks + G);

  const Real a0000 =
      (b * kn > 0.0)
          ? E / (1.0 - nu * nu - Utility::pow<2>(nu * (1.0 + nu)) / (1.0 - nu * nu + E / b / kn))
          : E / (1.0 - nu * nu);
  const Real a0011 = nu * a0000 / (1.0 - nu);
  const Real a2222 =
      (b * kn > 0.0) ? 1.0 / ((1.0 + nu) * (1.0 - 2.0 * nu) / E / (1.0 - nu) + 1.0 / b / kn) : 0.0;
  const Real a0022 = nu * a2222 / (1.0 - nu);
  const Real a0101 = G;
  const Real a66 = Gprime;
  const Real a77 = 0.5 * (G + Gprime);

  // Eijkl does not obey the usual symmetries, viz Eijkl != Ejikl, so must fill manually
  _Eijkl(0, 0, 0, 0) = _Eijkl(1, 1, 1, 1) = a0000;
  _Eijkl(0, 0, 1, 1) = _Eijkl(1, 1, 0, 0) = a0011;
  _Eijkl(2, 2, 2, 2) = a2222;
  _Eijkl(0, 0, 2, 2) = _Eijkl(1, 1, 2, 2) = _Eijkl(2, 2, 0, 0) = _Eijkl(2, 2, 1, 1) = a0022;
  _Eijkl(0, 1, 0, 1) = _Eijkl(0, 1, 1, 0) = _Eijkl(1, 0, 0, 1) = _Eijkl(1, 0, 1, 0) = a0101;
  _Eijkl(0, 2, 0, 2) = _Eijkl(0, 2, 2, 0) = _Eijkl(2, 0, 0, 2) = _Eijkl(1, 2, 1, 2) =
      _Eijkl(1, 2, 2, 1) = _Eijkl(2, 1, 1, 2) = a66;
  _Eijkl(2, 0, 2, 0) = _Eijkl(2, 1, 2, 1) = a77;

  // most of Bijkl is zero since the only nonzero moment stresses are m01 and m10.
  // It also does not have the usual symmetries.
  const Real D0 = E * Utility::pow<3>(b) / 12.0 / (1.0 - nu * nu); // bending rigidity of a layer
  const Real b0101 = D0 / b * G / (2.0 * b * ks + G);
  const Real b0110 = -nu * b0101;
  _Bijkl(0, 1, 0, 1) = _Bijkl(1, 0, 1, 0) = b0101;
  _Bijkl(0, 1, 1, 0) = _Bijkl(1, 0, 0, 1) = b0110;

  // The compliance tensor also does not obey the usual symmetries, and
  // this is the main reason it is calculated here, since we can't use
  // _Eijkl.invSymm()
  const Real pre = (nu - 1.0) / (a0000 * (nu - 1.0) + 2.0 * a2222 * Utility::pow<2>(nu));
  const Real cp0000 =
      ((a2222 - a0000) * nu * nu + 2.0 * a0000 * nu - a0000) / (2.0 * a0000 * nu - a0000);
  const Real cp0011 = -((a0000 + a2222) * nu * nu - a0000 * nu) / (2.0 * a0000 * nu - a0000);
  _Cijkl(0, 0, 0, 0) = _Cijkl(1, 1, 1, 1) = pre * cp0000;
  _Cijkl(0, 0, 1, 1) = _Cijkl(1, 1, 0, 0) = pre * cp0011;
  _Cijkl(2, 2, 2, 2) = pre * a0000 / a2222;
  _Cijkl(0, 0, 2, 2) = _Cijkl(1, 1, 2, 2) = _Cijkl(2, 2, 0, 0) = _Cijkl(2, 2, 1, 1) = -nu * pre;
  _Cijkl(0, 1, 0, 1) = _Cijkl(0, 1, 1, 0) = _Cijkl(1, 0, 0, 1) = _Cijkl(1, 0, 1, 0) = 0.25 / a0101;
  const Real slip = 2.0 / (G - Gprime);
  _Cijkl(0, 2, 2, 0) = _Cijkl(2, 0, 0, 2) = _Cijkl(1, 2, 2, 1) = _Cijkl(2, 1, 1, 2) = -slip;
  _Cijkl(0, 2, 0, 2) = _Cijkl(1, 2, 1, 2) = (G + Gprime) * slip / 2.0 / Gprime;
  _Cijkl(2, 0, 2, 0) = _Cijkl(2, 1, 2, 1) = slip;
}