Introduction and Assumptions
==================
It is fairly easy to add a new plasticity model to TensorMechanics.  If your plasticity model has zero or one internal parameters then you may use a GeneralUserObject to do this, and this is described in more detail below.  Your plasticity model may have more than one surface in it.

If your plasticity model has more than one internal parameter then you must inherit from FiniteStrainPlasticBase, and this procedure is not described in this article.  An example is FiniteStrainMohrCoulomb, which actually only has one internal parameter, but I hope the idea is clear.

If your model has zero internal parameters then it is "perfect", that is, has no hardening or softening.  In this case there will still be a single internal parameter in the code, but no functions will depend upon it.

When you have built your plasticity GeneralUserObject, it must be used in a FiniteStrainMultiPlasticity Material in your MOOSE input file.  There are numerous examples of this in the tensor\_mechanics test suite.

Features of FiniteStrainMultiPlasticity
==================

Sometimes it is tricky to get the derivatives correct in your GeneralUserObject.  The wiki contains [some identities](../identities) that may help.  Also, the FiniteStrainMultiPlasticity Material contains flags that allow you to check your coded derivatives versus finite-difference versions.

Be aware that if running MOOSE in debug mode, you may have to use the --no-trap-fpe command-line option to stop a crash due to the explicit 0/0 and 1/0 calculation checks performed by the PETSc-LAPACK-BLAS routines involved in the singular-value decomposition.

FiniteStrainMultiPlasticity is also nice because it contains all the linear-system building, the Newton-Raphson and line-searching functionality, as well as Kuhn-Tucker condition checking, etc.  So you needn't worry about those hellish details!

Finally, your plasticity GeneralUserObject may be combined with any other plasticity models in FiniteStrainMultiPlasticity without any further work by you.  That is, you automatically get multi-surface plasticity: very nice!

Using TensorMechanicsPlasticModel
==================

The GeneralUserObject that you must inherit from is called TensorMechanicsPlasticModel.  You should typically over-ride 6 functions, listed below.  If your plasticity model contains more than one surface, you need to override the "vector" forms of these functions -- see for example TensorMechanicsPlasticTensileMulti.  In the single-surface case:

/**
* The yield function
* @param stress the stress at which to calculate the yield function
* @param intnl internal parameter
* @return the yield function
*/
virtual Real yieldFunction(const RankTwoTensor & stress, const Real & intnl) const = 0;

/**
* The derivative of yield function with respect to stress
* @param stress the stress at which to calculate the yield function
* @param intnl internal parameter
* @return df_dstress(i, j) = dyieldFunction/dstress(i, j)
*/
virtual RankTwoTensor dyieldFunction_dstress(const RankTwoTensor & stress, const Real & intnl) const;

/**
* The derivative of yield function with respect to the internal parameter
* @param stress the stress at which to calculate the yield function
* @param intnl internal parameter
* @return the derivative
*/
virtual Real dyieldFunction_dintnl(const RankTwoTensor & stress, const Real & intnl) const = 0;

/**
* The flow potential
* @param stress the stress at which to calculate the flow potential
* @param intnl internal parameter
* @return the flow potential
*/
virtual RankTwoTensor flowPotential(const RankTwoTensor & stress, const Real & intnl) const;

/**
* The derivative of the flow potential with respect to stress
* @param stress the stress at which to calculate the flow potential
* @param intnl internal parameter
* @return dr_dstress(i, j, k, l) = dr(i, j)/dstress(k, l)
*/
virtual RankFourTensor dflowPotential_dstress(const RankTwoTensor & stress, const Real & intnl) const;

/**
* The derivative of the flow potential with respect to the internal parameter
* @param stress the stress at which to calculate the flow potential
* @param intnl internal parameter
* @return dr_dintnl(i, j) = dr(i, j)/dintnl
*/
virtual RankTwoTensor dflowPotential_dintnl(const RankTwoTensor & stress, const Real & intnl) const;


You may also choose to over-ride the following 3 functions:

/**
* The hardening potential
* @param stress the stress at which to calculate the hardening potential
* @param intnl internal parameter
* @return the hardening potential
*/
virtual Real hardPotential(const RankTwoTensor & stress, const Real & intnl) const;

/**
* The derivative of the hardening potential with respect to stress
* @param stress the stress at which to calculate the hardening potentials
* @param intnl internal parameter
* @return dh_dstress(i, j) = dh/dstress(i, j)
*/
virtual RankTwoTensor dhardPotential_dstress(const RankTwoTensor & stress, const Real & intnl) const;

/**
* The derivative of the hardening potential with respect to the internal parameter
* @param stress the stress at which to calculate the hardening potentials
* @param intnl internal parameter
* @return the derivative
*/
virtual Real dhardPotential_dintnl(const RankTwoTensor & stress, const Real & intnl) const;


Examples
==================

A very simple example is TensorMechanicsPlasticSimpleTester, which describes associative, perfect plasticity with
$$f = a\sigma_{yy} + b\sigma_{zz} - S$$

A much more complicated example is TensorMechanicsPlasticMohrCoulomb described in the [wiki](../MC).  Although the code is rather lengthy, it is still just over-riding the 6 functions mentioned above.