"""
This module contains all common functions used in variational form. 
All operators are defined with respect to the reference coordiantes.
"""
import fenics as fn

__version__ = "0.1"
__author__ = "Reidmen Arostica"
__date__ = "25/06/2020"

def F_(coordinates: fn.Function) -> fn.grad:
    """
    Computes the deformation gradient operator (dx/dX).
    """
    return fn.grad(coordinates)

def inv_F_(coordinates: fn.Function) -> fn.inv:
    """
    Computes the inverse of the deformation gradient.
    """
    return fn.inv(F_(coordinates))

def J_(coordinates: fn.Function) -> fn.det:
    """
    Computes the determinant of the deformation gradient.
    """
    return fn.det(F_(coordinates))

def grad_(u: fn.Function, coordinates: fn.Function) -> fn.Function:
    """
    Computes the grad operator on the reference configuration.
    """
    inv_F = fn.inv(F_(coordinates))
    return fn.dot(fn.grad(u), inv_F)

def nabla_grad_(u: fn.Function, coordinates: fn.Function) -> fn.Function:
    """
    Computes the nabla_grad on the reference configuration.
    """
    return grad_(u, coordinates).T

def sym_grad_(u: fn.Function, coordinates: fn.Function) -> fn.Function:
    """
    Computes the symmetric gradient operator w.r.t. the reference configuration.
    """
    return 0.5*(grad_(u, coordinates) + grad_(u, coordinates).T)

def div_(u: fn.Function, coordinates: fn.Function) -> fn.tr:
    """
    Computes the Piola div operator w.r.t. the reference configuration.
    """
    J = J_(coordinates)
    inv_F = inv_F_(coordinates)
    return fn.div(J*fn.dot(inv_F, u))
