import math

class Orthotropic(object):

    def __init__(self,
    matID = None,
    E1 = None,
    E2 = None,
    G12 = None,
    G13 = None,
    G23 = None,
    v12 = None,
    rho = 0.0,
    alpha1 = 0.0,
    alpha2 = 0.0,
    beta1 = 0.0,
    beta2 = 0.0,
    eps11_tension_allowable = 0.0,
    eps11_compression_allowable = 0.0,
    eps22_tension_allowable = 0.0,
    eps22_compression_allowable = 0.0,
    gamma12_allowable = 0.0,
    gamma23_allowable = 0.0,
    gamma13_allowable = 0.0,
    sig11_tension_allowable = 0.0,
    sig11_compression_allowable = 0.0,
    sig22_tension_allowable = 0.0,
    sig22_compression_allowable = 0.0,
    tau12_allowable = 0.0,
    tau23_allowable = 0.0,
    tau13_allowable = 0.0):
        """Initialize the Orthotropic instance.
        
        Parameters
        ----------
        matID : str

            Orthotropic material id (required).

        E1 : float

            Elastic modulus in direction 1 (required).

        E2 : float

            Elastic modulus in direction 2 (required).

        G12 : float

            Shear modulus in 12 plane (required).

        G13 : float

            Transverse shear modulus in 13 plane (required).

        G23 : float

            Transverse shear modulus in 23 plane (required).

        v12 : float

            Poisson's ratio in 21 plane (required).

        rho : float

            Density (default = 0.0) (optional)

        alpha1 : float

            Coefficient of thermal expansion in direction 1 (default = 0.0) (optional)

        alpha2 : float

            Coefficient of thermal expansion in direction 2 (default = 0.0) (optional)

        beta1 : float

            Coefficient of hygral expansion in direction 1 (default = 0.0) (optional)

        beta2 : float

            Coefficient of hygral expansion in direction 2 (default = 0.0) (optional)
        
        eps11_tension_allowable: float

            Allowable tensile strain in direction 1 (default = 0.0) (optional)
        
        eps11_compression_allowable: float

            Allowable compressive strain in direction 1 (default = 0.0) (optional)
        
        eps22_tension_allowable: float

            Allowable tensile strain in direction 2 (default = 0.0) (optional)
        
        eps22_compression_allowable: float

            Allowable compressive strain in direction 2 (default = 0.0) (optional)
        
        gamma12_allowable: float

            Allowable shear strain in 12 plane (default = 0.0) (optional)
        
        gamma23_allowable: float

            Allowable shear strain in 23 plane (default = 0.0) (optional)
        
        gamma13_allowable: float

            Allowable shear strain in 13 plane (default = 0.0) (optional)
        
        sig11_tension_allowable: float

            Allowable tensile stress in direction 1 (default = 0.0) (optional)
        
        sig11_compression_allowable: float

            Allowable compressive stress in direction 1 (default = 0.0) (optional)
        
        sig22_tension_allowable: float

            Allowable tensile stress in direction 2 (default = 0.0) (optional)
        
        sig22_compression_allowable: float

            Allowable compressive stress in direction 2 (default = 0.0) (optional)
        
        tau12_allowable: float

            Allowable shear stress in 12 plane (default = 0.0) (optional)
        
        tau23_allowable: float

            Allowable shear stress in 23 plane (default = 0.0) (optional)
        
        tau13_allowable: float

            Allowable shear stress in 13 plane (default = 0.0) (optional)
        
        """

        self.matID = str(matID)
        self.E1 = float(E1)
        self.E2 = float(E2)
        self.G12 = float(G12)
        self.G13 = float(G13)
        self.G23 = float(G23)
        self.v12 = float(v12)
        self.rho = float(rho)
        self.alpha1 = float(alpha1)
        self.alpha2 = float(alpha2)
        self.beta1 = float(beta1)
        self.beta2 = float(beta2)

        self.eps11_tension_allowable = float(eps11_tension_allowable)
        self.eps11_compression_allowable = float(eps11_compression_allowable)
        self.eps22_tension_allowable = float(eps22_tension_allowable)
        self.eps22_compression_allowable = float(eps22_compression_allowable)
        self.gamma12_allowable = float(gamma12_allowable)
        self.gamma23_allowable = float(gamma23_allowable)
        self.gamma13_allowable = float(gamma13_allowable)

        self.sig11_tension_allowable = float(sig11_tension_allowable)
        self.sig11_compression_allowable = float(sig11_compression_allowable)
        self.sig22_tension_allowable = float(sig22_tension_allowable)
        self.sig22_compression_allowable = float(sig22_compression_allowable)
        self.tau12_allowable = float(tau12_allowable)
        self.tau23_allowable = float(tau23_allowable)
        self.tau13_allowable = float(tau13_allowable)

        self._invalidate()

    @classmethod
    def fromDict(cls, **kwargs):
        """Initialize Orthotropic using kwargs dict.
        
        Parameters
        ----------
        
        kwargs : dict

            {"matID": str, "E1": float, "E2": float, "G12": float, "G13": float,
            "G23": float, "v12": float, "rho": float, "alpha1": float,
            "alpha2": float, "beta1": float, "beta2": float}
            
        """
        matID = kwargs.get("matID")
        E1 = kwargs.get("E1")
        E2 = kwargs.get("E2")
        G12 = kwargs.get("G12")
        G13 = kwargs.get("G13")
        G23 = kwargs.get("G23")
        v12 = kwargs.get("v12")
        rho = kwargs.get("rho", 0.0)
        alpha1 = kwargs.get("alpha1", 0.0)
        alpha2 = kwargs.get("alpha2", 0.0)
        beta1 = kwargs.get("beta1", 0.0)
        beta2 = kwargs.get("beta2", 0.0)
        eps11_tension_allowable = kwargs.get("eps11_tension_allowable", 0.0)
        eps11_compression_allowable = kwargs.get("eps11_compression_allowable", 0.0)
        eps22_tension_allowable = kwargs.get("eps22_tension_allowable", 0.0)
        eps22_compression_allowable = kwargs.get("eps22_compression_allowable", 0.0)
        gamma12_allowable = kwargs.get("gamma12_allowable", 0.0)
        gamma23_allowable = kwargs.get("gamma23_allowable", 0.0)
        gamma13_allowable = kwargs.get("gamma13_allowable", 0.0)
        sig11_tension_allowable = kwargs.get("sig11_tension_allowable", 0.0)
        sig11_compression_allowable = kwargs.get("sig11_compression_allowable", 0.0)
        sig22_tension_allowable = kwargs.get("sig22_tension_allowable", 0.0)
        sig22_compression_allowable = kwargs.get("sig22_compression_allowable", 0.0)
        tau12_allowable = kwargs.get("tau12_allowable", 0.0)
        tau23_allowable = kwargs.get("tau23_allowable", 0.0)
        tau13_allowable = kwargs.get("tau13_allowable", 0.0)
        
        return cls(matID, E1, E2, G12, G13, G23, v12, rho,
        alpha1, alpha2, beta1, beta2, eps11_tension_allowable,
        eps11_compression_allowable, eps22_tension_allowable,
        eps22_compression_allowable, gamma12_allowable,
        gamma23_allowable, gamma13_allowable,
        sig11_tension_allowable, sig11_compression_allowable,
        sig22_tension_allowable, sig22_compression_allowable,
        tau12_allowable, tau23_allowable, tau13_allowable)

    def _invalidate(self):
        """Invalidates calculated properties for fresh calculation.
        
        """

        self._v21 = None
        self._F1 = None
        self._F2 = None
        self._F11 = None
        self._F22 = None
        self._F12 = None

    @property
    def v21(self):
        """Poissons ratio in 21 plane."""

        if self._v21 is None:
            self._v21 = self.v12*self.E2/self.E1
        
        return self._v21

    @property
    def F1(self):
        """Tsai-Wu failure theory parameters.
        
        """

        if self._F1 is not None:
            return self._F1
        
        self._F1 = (1.0/self.sig11_tension_allowable) + (1.0/self.sig11_compression_allowable)

        return self._F1

    @property
    def F2(self):
        """Tsai-Wu failure theory parameters.
        
        """

        if self._F2 is not None:
            return self._F2
        
        self._F2 = (1.0/self.sig22_tension_allowable) + (1.0/self.sig22_compression_allowable)

        return self._F2

    @property
    def F11(self):
        """Tsai-Wu failure theory parameters.
        
        """

        if self._F11 is not None:
            return self._F11

        self._F11 = -1.0/(self.sig11_tension_allowable*self.sig11_compression_allowable)

        return self._F11

    @property
    def F22(self):
        """Tsai-Wu failure theory parameters.
        
        """

        if self._F22 is not None:
            return self._F22

        self._F22 = -1.0/(self.sig22_tension_allowable*self.sig22_compression_allowable)

        return self._F22

    @property
    def F12(self):
        """Tsai-Wu failure theory parameters.
        
        """

        if self._F12 is not None:
            return self._F12

        self._F12 = -0.5*math.sqrt(self.F11*self.F22)