import numpy as np
import math
from lamfail.CONSTANTS import STRESSMIN, STRAINMIN, TOL

class Lamfail(object):

    def __init__(self, panel = None, loads = None):
        """Initialize the Lamfail instance.
        
        Parameters
        ----------

        panel : panel.Panel (required)

        loads : loads.Loads (required)

        """

        self.panel = panel
        self.loads = loads

        self._invalidate()

    @classmethod
    def fromDict(cls, **kwargs):
        """Initialize Lamfail from kwargs dictionary.
        
        Parameters
        ----------
        
        kwargs : dict

            {"panel": Panel, "loads": Loads}

        Returns
        -------

        cls : Lamfail
        
        """
        panel = kwargs.get("panel")
        loads = kwargs.get("loads")
        return cls(id, panel, loads)

    def _invalidate(self):
        """Invalidate all calculation to force re calculation."""

        self._NT = None
        self._MT = None
        self._NC = None
        self._MC = None
        self._ek0 = None
        self._gT0 = None
        self._tauyz = None
        self._tauxz = None
        self._stress = None
        self._stressM = None
        self._stressT = None
        self._stressH = None
        self._strain = None
        self._strainM = None
        self._strainT = None
        self._strainH = None
        self._stress1 = None
        self._stressM1 = None
        self._stressT1 = None
        self._stressH1 = None
        self._strain1 = None
        self._strainM1 = None
        self._strainT1 = None
        self._strainH1 = None

        self._stress1MS = None
        self._stressM1MS = None
        self._strain1MS = None
        self._strainM1MS = None
        self._tsaiHill1MS = None
        self._tsaiHillM1MS = None
        self._tsaiWu1MS = None
        self._tsaiWuM1MS = None

    @property
    def NT(self):
        """Returns thermal inplane loads on the laminate."""

        if self._NT is not None:
            return self._NT
        
        self._NT = np.zeros(3)
        deltaT = self.loads.deltaT
        z = self.panel.laminate.z
        for i, ply in enumerate(self.panel.laminate.stacking_sequence, start=1):
            self._NT += (ply.Qbar @ ply.alphabar) * deltaT * (z[i]-z[i-1])
        return self._NT

    @property
    def MT(self):
        """Returns thermal inplane moments on the laminate."""

        if self._MT is not None:
            return self._MT
        
        self._MT = np.zeros(3)
        deltaT = self.loads.deltaT
        z = self.panel.laminate.z
        for i, ply in enumerate(self.panel.laminate.stacking_sequence, start=1):
            self._MT += 0.5 * (ply.Qbar @ ply.alphabar) * deltaT * ((z[i]**2)-(z[i-1]**2))
        return self._MT

    @property
    def NC(self):
        """Returns hygral inplane loads on the laminate."""

        if self._NC is not None:
            return self._NC
        
        self._NC = np.zeros(3)
        deltaC = self.loads.deltaC
        z = self.panel.laminate.z
        for i, ply in enumerate(self.panel.laminate.stacking_sequence, start=1):
            self._NC += (ply.Qbar @ ply.betabar) * deltaC * (z[i]-z[i-1])
        return self._NC

    @property
    def MC(self):
        """Returns hygral inplane moments on the laminate."""

        if self._MC is not None:
            return self._MC
        
        self._MC = np.zeros(3)
        deltaC = self.loads.deltaC
        z = self.panel.laminate.z
        for i, ply in enumerate(self.panel.laminate.stacking_sequence, start=1):
            self._MC += 0.5 * (ply.Qbar @ ply.betabar) * deltaC * ((z[i]**2)-(z[i-1]**2))
        return self._MC

    @property
    def ek0(self):
        """Returns mid plane strain and curvatures in the form
        ex0, ey0, gxy0, kx0, ky0, kxy0
        
        Returns
        -------
        
        ek0 : numpy.array
        
            ex0, ey0, gxy0, kx0, ky0, kxy0
            
        """

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

        N = np.array([self.loads.Nxx, self.loads.Nyy, self.loads.Nxy])
        N += self.NT + self.NC

        M = np.array([self.loads.Mxx, self.loads.Myy, self.loads.Mxy])
        M += self.MT + self.MC

        NM = np.concatenate((N, M), axis=None)

        self._ek0 = np.linalg.inv(self.panel.laminate.ABD) @ NM
        return self._ek0

    @property
    def gT0(self):
        """Returns mid plane transverse strain in the form
        gyz0, gxz0
        
        Returns
        
        gT0 : numpy.array
        
            gyz0, gxz0
            
        """

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

        QT = np.array([self.loads.Qyz, self.loads.Qxz])
        AT = np.array([[self.panel.laminate.A44, self.panel.laminate.A45],
        [self.panel.laminate.A45, self.panel.laminate.A55]])
        self._gT0 = np.linalg.inv(AT) @ QT
        return self._gT0

    @property
    def tauyz(self):
        """Returns tau_yz for each ply in a dictionary starting from 
        ply 1 (which is at the bottom of the laminate). The key for each
        ply is its ply number 1, 2, 3, ..., N.
        
        Returns
        -------
        
        tauyz : dict
        
            {"1": {"bottom" : float, "top": float}, ...}
            
        """

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

        D11 = self.panel.laminate.Dmid[0,0]
        D16 = self.panel.laminate.Dmid[0,2]
        D22 = self.panel.laminate.Dmid[1,1]
        D26 = self.panel.laminate.Dmid[1,2]
        D = D11*D22 - D16*D26
        Qyz = self.loads.Qyz
        Qxz = self.loads.Qxz
        t = self.panel.laminate.thickness
        self._tauyz = {}
        for i, ply in enumerate(self.panel.laminate.stacking_sequence, start=1):
            zBottom = self.panel.laminate.zmid[i-1]
            zTop = self.panel.laminate.zmid[i]
            zMid = (zBottom+zTop)/2.0
            d3wdx3 = ((-D22*Qxz)+(D26*Qyz))/D
            d3wdy3 = ((D16*Qxz)-(D11*Qyz))/D
            sigBottom = 0.125*((4.0*zBottom**2)-(t**2))*((ply.Qbar[0,2]*d3wdx3)+(ply.Qbar[1,1]*d3wdy3))
            sigTop = 0.125*((4.0*zTop**2)-(t**2))*((ply.Qbar[0,2]*d3wdx3)+(ply.Qbar[1,1]*d3wdy3))
            sigMid = 0.125*((4.0*zMid**2)-(t**2))*((ply.Qbar[0,2]*d3wdx3)+(ply.Qbar[1,1]*d3wdy3))
            self._tauyz[str(i)] = {"bottom": sigBottom,
                                   "top": sigTop,
                                   "mid": sigMid}
        return self._tauyz

    @property
    def tauxz(self):
        """Returns tau_xz for each ply in a dictionary starting from 
        ply 1 (which is at the bottom of the laminate). The key for each
        ply is its ply number 1, 2, 3, ..., N.
        
        Returns
        -------
        
        tauxz : dict
        
            {"1": {"bottom" : float, "top": float}, ...}
            
        """

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

        D11 = self.panel.laminate.Dmid[0,0]
        D16 = self.panel.laminate.Dmid[0,2]
        D22 = self.panel.laminate.Dmid[1,1]
        D26 = self.panel.laminate.Dmid[1,2]
        D = D11*D22 - D16*D26
        Qyz = self.loads.Qyz
        Qxz = self.loads.Qxz
        t = self.panel.laminate.thickness
        self._tauxz = {}
        for i, ply in enumerate(self.panel.laminate.stacking_sequence, start=1):
            zBottom = self.panel.laminate.zmid[i-1]
            zTop = self.panel.laminate.zmid[i]
            zMid = (zBottom+zTop)/2.0
            d3wdx3 = ((-D22*Qxz)+(D26*Qyz))/D
            d3wdy3 = ((D16*Qxz)-(D11*Qyz))/D
            sigBottom = 0.125*((4.0*zBottom**2)-(t**2))*((ply.Qbar[0,0]*d3wdx3)+(ply.Qbar[1,2]*d3wdy3))
            sigTop = 0.125*((4.0*zTop**2)-(t**2))*((ply.Qbar[0,0]*d3wdx3)+(ply.Qbar[1,2]*d3wdy3))
            sigMid = 0.125*((4.0*zMid**2)-(t**2))*((ply.Qbar[0,0]*d3wdx3)+(ply.Qbar[1,2]*d3wdy3))
            self._tauxz[str(i)] = {"bottom": sigBottom,
                                   "top": sigTop,
                                   "mid": sigMid}
        return self._tauxz


    @property
    def stress(self):
        """Returns stress in reference coordinate axes (x-y-z) for each ply
        in the laminate. The key of the dictionary is the ply number starting
        from the bottom of the laminate labelled as 1, 2, 3, ..., N.
        
        Returns
        -------
        
        stress : dict
        
            {"1": {"bottom" : numpy.array([sigx, sigy, tauxy, tauyz, tauxz]), 
            "top": numpy.array([sigx, sigy, tauxy, tauyz, tauxz]), 
            "mid": numpy.array([sigx, sigy, tauxy, tauyz, tauxz])}, ...}
            
        """

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

        self._calculateStressStrain()
        return self._stress

    @property
    def stressM(self):
        """Returns mechanical or residual stress in reference coordinate axes 
        (x-y-z) for each ply in the laminate. The key of the dictionary is the 
        ply number starting from the bottom of the laminate labelled as 1, 2, 
        3, ..., N.
        
        Returns
        -------
        
        stress : dict
        
            {"1": {"bottom" : numpy.array([sigx, sigy, tauxy, tauyz, tauxz]), 
            "top": numpy.array([sigx, sigy, tauxy, tauyz, tauxz]), 
            "mid": numpy.array([sigx, sigy, tauxy, tauyz, tauxz])}, ...}
            
        """

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

        self._calculateStressStrain()
        return self._stressM

    @property
    def stressT(self):
        """Returns thermal stress in reference coordinate axes (x-y-z) for each 
        ply in the laminate. The key of the dictionary is the ply number starting 
        from the bottom of the laminate labelled as 1, 2, 3, ..., N.
        
        Returns
        -------
        
        stress : dict
        
            {"1": {"bottom" : numpy.array([sigx, sigy, tauxy, tauyz, tauxz]), 
            "top": numpy.array([sigx, sigy, tauxy, tauyz, tauxz]), 
            "mid": numpy.array([sigx, sigy, tauxy, tauyz, tauxz])}, ...}
            
        """

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

        self._calculateStressStrain()
        return self._stressT

    @property
    def stressH(self):
        """Returns hygral stress in reference coordinate axes (x-y-z) for each 
        ply in the laminate. The key of the dictionary is the ply number starting 
        from the bottom of the laminate labelled as 1, 2, 3, ..., N.
        
        Returns
        -------
        
        stress : dict
        
            {"1": {"bottom" : numpy.array([sigx, sigy, tauxy, tauyz, tauxz]), 
            "top": numpy.array([sigx, sigy, tauxy, tauyz, tauxz]), 
            "mid": numpy.array([sigx, sigy, tauxy, tauyz, tauxz])}, ...}
            
        """

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

        self._calculateStressStrain()
        return self._stressH

    @property
    def stress1(self):
        """Returns stress in material coordinate axes (1-2-3) for each ply
        in the laminate. The key of the dictionary is the ply number starting
        from the bottom of the laminate labelled as 1, 2, 3, ..., N.
        
        Returns
        -------
        
        stress1 : dict
        
            {"1": {"bottom" : numpy.array([sig1, sig2, tau12, tau23, tau13]), 
            "top": numpy.array([sig1, sig2, tau12, tau23, tau13]),
            "mid": numpy.array([sig1, sig2, tau12, tau23, tau13])}, ...}
            
        """

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

        self._calculateStressStrain()
        return self._stress1

    @property
    def stressM1(self):
        """Returns mechanical or residual stress in material coordinate axes 
        (1-2-3) for each ply in the laminate. The key of the dictionary is the 
        ply number starting from the bottom of the laminate labelled as 1, 2, 3, 
        ..., N.
        
        Returns
        -------
        
        stress1 : dict
        
            {"1": {"bottom" : numpy.array([sig1, sig2, tau12, tau23, tau13]), 
            "top": numpy.array([sig1, sig2, tau12, tau23, tau13]),
            "mid": numpy.array([sig1, sig2, tau12, tau23, tau13])}, ...}
            
        """

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

        self._calculateStressStrain()
        return self._stressM1

    @property
    def stressT1(self):
        """Returns thermal stress in material coordinate axes (1-2-3) for each 
        ply in the laminate. The key of the dictionary is the ply number starting 
        from the bottom of the laminate labelled as 1, 2, 3, ..., N.
        
        Returns
        -------
        
        stress1 : dict
        
            {"1": {"bottom" : numpy.array([sig1, sig2, tau12, tau23, tau13]), 
            "top": numpy.array([sig1, sig2, tau12, tau23, tau13]),
            "mid": numpy.array([sig1, sig2, tau12, tau23, tau13])}, ...}
            
        """

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

        self._calculateStressStrain()
        return self._stressT1

    @property
    def stressH1(self):
        """Returns hygral stress in material coordinate axes (1-2-3) for each 
        ply in the laminate. The key of the dictionary is the ply number starting 
        from the bottom of the laminate labelled as 1, 2, 3, ..., N.
        
        Returns
        -------
        
        stress1 : dict
        
            {"1": {"bottom" : numpy.array([sig1, sig2, tau12, tau23, tau13]), 
            "top": numpy.array([sig1, sig2, tau12, tau23, tau13]),
            "mid": numpy.array([sig1, sig2, tau12, tau23, tau13])}, ...}
            
        """

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

        self._calculateStressStrain()
        return self._stressH1

    @property
    def strain(self):
        """Returns strains in reference coordinate axes (x-y-z) for each ply
        in the laminate. The key of the dictionary is the ply number starting
        from the bottom of the laminate labelled as 1, 2, 3, ..., N.
        
        Returns
        -------
        
        strain : dict
        
            {"1": {"bottom" : numpy.array([ex, ey, gxy, gyz, gxz]), 
            "top": numpy.array([ex, ey, gxy, gyz, gxz]),
            "mid": numpy.array([ex, ey, gxy, gyz, gxz])}, ...}.
                   
        """

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

        self._calculateStressStrain()
        return self._strain

    @property
    def strainM(self):
        """Returns mechanical or residual strains in reference coordinate axes 
        (x-y-z) for each ply in the laminate. The key of the dictionary is the 
        ply number starting from the bottom of the laminate labelled as 1, 2, 3, 
        ..., N.
        
        Returns
        -------
        
        strain : dict
        
            {"1": {"bottom" : numpy.array([ex, ey, gxy, gyz, gxz]), 
            "top": numpy.array([ex, ey, gxy, gyz, gxz]),
            "mid": numpy.array([ex, ey, gxy, gyz, gxz])}, ...}.
                   
        """

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

        self._calculateStressStrain()
        return self._strainM

    @property
    def strainT(self):
        """Returns thermal strains in reference coordinate axes (x-y-z) for each 
        ply in the laminate. The key of the dictionary is the ply number starting 
        from the bottom of the laminate labelled as 1, 2, 3, ..., N.
        
        Returns
        -------
        
        strain : dict
        
            {"1": {"bottom" : numpy.array([ex, ey, gxy, gyz, gxz]), 
            "top": numpy.array([ex, ey, gxy, gyz, gxz]),
            "mid": numpy.array([ex, ey, gxy, gyz, gxz])}, ...}.
                   
        """

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

        self._calculateStressStrain()
        return self._strainT

    @property
    def strainH(self):
        """Returns hygral strains in reference coordinate axes (x-y-z) for each 
        ply in the laminate. The key of the dictionary is the ply number starting 
        from the bottom of the laminate labelled as 1, 2, 3, ..., N.
        
        Returns
        -------
        
        strain : dict
        
            {"1": {"bottom" : numpy.array([ex, ey, gxy, gyz, gxz]), 
            "top": numpy.array([ex, ey, gxy, gyz, gxz]),
            "mid": numpy.array([ex, ey, gxy, gyz, gxz])}, ...}.
                   
        """

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

        self._calculateStressStrain()
        return self._strainH

    @property
    def strain1(self):
        """Returns strains in material coordinate axes (1-2-3) for each ply
        in the laminate. The key of the dictionary is the ply number starting
        from the bottom of the laminate labelled as 1, 2, 3, ..., N.
        
        Returns
        -------
        
        strain1 : dict
        
            {"1": {"bottom" : numpy.array([e1, e2, g12, g23, g13]), 
            "top": numpy.array([e1, e2, g12, g23, g13]),
            "mid": numpy.array([e1, e2, g12, g23, g13])}, ...}..
            
        """

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

        self._calculateStressStrain()
        return self._strain1

    @property
    def strainM1(self):
        """Returns mechanical or residual strains in material coordinate axes 
        (1-2-3) for each ply in the laminate. The key of the dictionary is the 
        ply number starting from the bottom of the laminate labelled as 1, 2, 3, 
        ..., N.
        
        Returns
        -------
        
        strain1 : dict
        
            {"1": {"bottom" : numpy.array([e1, e2, g12, g23, g13]), 
            "top": numpy.array([e1, e2, g12, g23, g13]),
            "mid": numpy.array([e1, e2, g12, g23, g13])}, ...}..
            
        """

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

        self._calculateStressStrain()
        return self._strainM1

    @property
    def strainT1(self):
        """Returns thermal strains in material coordinate axes (1-2-3) for each 
        ply in the laminate. The key of the dictionary is the ply number starting 
        from the bottom of the laminate labelled as 1, 2, 3, ..., N.
        
        Returns
        -------
        
        strain1 : dict
        
            {"1": {"bottom" : numpy.array([e1, e2, g12, g23, g13]), 
            "top": numpy.array([e1, e2, g12, g23, g13]),
            "mid": numpy.array([e1, e2, g12, g23, g13])}, ...}..
            
        """

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

        self._calculateStressStrain()
        return self._strainT1

    @property
    def strainH1(self):
        """Returns hygral strains in material coordinate axes (1-2-3) for each 
        ply in the laminate. The key of the dictionary is the ply number starting 
        from the bottom of the laminate labelled as 1, 2, 3, ..., N.
        
        Returns
        -------
        
        strain1 : dict
        
            {"1": {"bottom" : numpy.array([e1, e2, g12, g23, g13]), 
            "top": numpy.array([e1, e2, g12, g23, g13]),
            "mid": numpy.array([e1, e2, g12, g23, g13])}, ...}..
            
        """

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

        self._calculateStressStrain()
        return self._strainH1

    @property
    def stress1MS(self):
        """Returns MS using Max Stress failure theory using total stress.
        
        Returns
        -------
        
        stress1MS : dict
        
            {"1": {"bottom": numpy.array([ms11, ms22, ms12]),
            "top": numpy.array([ms11, ms22, ms12]),
            "mid:: numpy.array([ms11, ms22, ms12])}, ...}
            
        """

        if self._stress1MS is not None:
            return self._stress1MS
        
        self._calculateMaxStress()
        return self._stress1MS

    @property
    def stressM1MS(self):
        """Returns MS using Max Stress failure theory using mechanical stress.
        
        Returns
        -------
        
        stressM1MS : dict
        
            {"1": {"bottom": numpy.array([ms11, ms22, ms12]),
            "top": numpy.array([ms11, ms22, ms12]),
            "mid:: numpy.array([ms11, ms22, ms12])}, ...}
            
        """

        if self._stressM1MS is not None:
            return self._stressM1MS
        
        self._calculateMaxStress()
        return self._stressM1MS

    @property
    def strain1MS(self):
        """Returns MS using Max Strain failure theory using total strain.
        
        Returns
        -------
        
        strain1MS : dict
        
            {"1": {"bottom": numpy.array([ms11, ms22, ms12]),
            "top": numpy.array([ms11, ms22, ms12]),
            "mid:: numpy.array([ms11, ms22, ms12])}, ...}
            
        """

        if self._strain1MS is not None:
            return self._strain1MS
        
        self._calculateMaxStrain()
        return self._strain1MS

    @property
    def strainM1MS(self):
        """Returns MS using Max Strain failure theory using mechanical stress.
        
        Returns
        -------
        
        strainM1MS : dict
        
            {"1": {"bottom": numpy.array([ms11, ms22, ms12]),
            "top": numpy.array([ms11, ms22, ms12]),
            "mid:: numpy.array([ms11, ms22, ms12])}, ...}
            
        """

        if self._strainM1MS is not None:
            return self._strainM1MS
        
        self._calculateMaxStrain()
        return self._strainM1MS

    @property
    def tsaiHill1MS(self):
        """Returns MS using Tsai Hill failure theory using total strain.
        
        Returns
        -------
        
        tsaiHill1MS : dict
        
            {"1": {"bottom": float, "top": float, "mid": float}, ...}
            
        """

        if self._tsaiHill1MS is not None:
            return self._tsaiHill1MS
        
        self._calculateTsaiHill()
        return self._tsaiHill1MS

    @property
    def tsaiHillM1MS(self):
        """Returns MS using Tsai Hill failure theory using mechanical strain.
        
        Returns
        -------
        
        tsaiHillM1MS : dict
        
            {"1": {"bottom": float, "top": float, "mid": float}, ...}
            
        """

        if self._tsaiHillM1MS is not None:
            return self._tsaiHillM1MS
        
        self._calculateTsaiHill()
        return self._tsaiHillM1MS

    @property
    def tsaiWu1MS(self):
        """Returns MS using Tsai Wu failure theory using total strain.
        
        Returns
        -------
        
        tsaiWu1MS : dict
        
            {"1": {"bottom": float, "top": float, "mid": float}, ...}
            
        """

        if self._tsaiWu1MS is not None:
            return self._tsaiWu1MS
        
        self._calculateTsaiWu()
        return self._tsaiWu1MS

    @property
    def tsaiWuM1MS(self):
        """Returns MS using Tsai Wu failure theory using mechanical strain.
        
        Returns
        -------
        
        tsaiWuM1MS : dict
        
            {"1": {"bottom": float, "top": float, "mid": float}, ...}
            
        """

        if self._tsaiWuM1MS is not None:
            return self._tsaiWuM1MS
        
        self._calculateTsaiWu()
        return self._tsaiWuM1MS

    def _calculateStressStrain(self):
        """Calculates all Stress and Strain"""

        self._stress = {}
        self._stressM = {}
        self._stressT = {}
        self._stressH = {}

        self._stress1 = {}
        self._stressM1 = {}
        self._stressT1 = {}
        self._stressH1 = {}

        self._strain = {}
        self._strainM = {}
        self._strainT = {}
        self._strainH = {}

        self._strain1 = {}
        self._strainM1 = {}
        self._strainT1 = {}
        self._strainH1 = {}

        e0 = self.ek0[0:3]
        k = self.ek0[3:6]

        for i, ply in enumerate(self.panel.laminate.stacking_sequence, start=1):
            
            zBottom = self.panel.laminate.z[i-1]
            zTop = self.panel.laminate.z[i]
            zMid = (zBottom+zTop)/2.0

            epsBottom = e0 + zBottom*k
            epsTop = e0 + zTop*k
            epsMid = e0 + zMid*k

            sigBottom = ply.Qbar @ epsBottom
            sigBottom = np.concatenate((sigBottom, self.tauyz[str(i)]["bottom"], self.tauxz[str(i)]["bottom"]), axis=None)
            sigTop = ply.Qbar @ epsTop
            sigTop = np.concatenate((sigTop, self.tauyz[str(i)]["top"], self.tauxz[str(i)]["top"]), axis=None)
            sigMid = ply.Qbar @ epsMid
            sigMid = np.concatenate((sigMid, self.tauyz[str(i)]["mid"], self.tauxz[str(i)]["mid"]), axis=None)

            m = math.cos(math.radians(ply.angle))
            n = math.sin(math.radians(ply.angle))
            T = np.array([[m**2, n**2, 2.0*m*n, 0.0, 0.0],
                          [n**2, m**2, -2.0*m*n, 0.0, 0.0],
                          [-m*n, m*n, (m**2)-(n**2), 0.0, 0.0],
                          [0.0, 0.0, 0.0, m, -n],
                          [0.0, 0.0, 0.0, n, m]])
            T1 = np.array([[m**2, n**2, 2.0*m*n],
                          [n**2, m**2, -2.0*m*n],
                          [-m*n, m*n, (m**2)-(n**2)]])
            sigBottom1 = T @ sigBottom
            sigTop1 = T @ sigTop
            sigMid1 = T @ sigMid

            epsBottom = np.concatenate((epsBottom, self.gT0), axis=None)
            epsTop = np.concatenate((epsTop, self.gT0), axis=None)
            epsMid = np.concatenate((epsMid, self.gT0), axis=None)

            Te = np.array([[m**2, n**2, m*n, 0.0, 0.0],
                           [n**2, m**2, -m*n, 0.0, 0.0],
                           [-2.0*m*n, 2.0*m*n, (m**2)-(n**2), 0.0, 0.0],
                           [0.0, 0.0, 0.0, m, -n],
                           [0.0, 0.0, 0.0, n, m]])
            Te1 = np.array([[m**2, n**2, m*n],
                           [n**2, m**2, -m*n],
                           [-2.0*m*n, 2.0*m*n, (m**2)-(n**2)]])

            epsBottom1 = Te @ epsBottom
            epsTop1 = Te @ epsTop
            epsMid1 = Te @ epsMid

            self._stress[str(i)] = {"bottom": sigBottom, "top": sigTop, "mid": sigMid}
            self._stress1[str(i)] = {"bottom": sigBottom1, "top": sigTop1, "mid": sigMid1}

            self._strain[str(i)] = {"bottom": epsBottom, "top": epsTop, "mid": epsMid}
            self._strain1[str(i)] = {"bottom": epsBottom1, "top": epsTop1, "mid": epsMid1}

            epsFT = ply.alphabar*self.loads.deltaT
            epsFC = ply.betabar*self.loads.deltaC

            epsMBottom = e0 + zBottom*k - epsFT - epsFC
            epsMTop = e0 + zTop*k - epsFT - epsFC
            epsMMid = e0 + zMid*k - epsFT - epsFC

            sigMBottom = ply.Qbar @ epsMBottom
            sigMBottom = np.concatenate((sigMBottom, self.tauyz[str(i)]["bottom"], self.tauxz[str(i)]["bottom"]), axis=None)
            sigMTop = ply.Qbar @ epsMTop
            sigMTop = np.concatenate((sigMTop, self.tauyz[str(i)]["top"], self.tauxz[str(i)]["top"]), axis=None)
            sigMMid = ply.Qbar @ epsMMid
            sigMMid = np.concatenate((sigMMid, self.tauyz[str(i)]["mid"], self.tauxz[str(i)]["mid"]), axis=None)

            sigFT = ply.Qbar @ epsFT
            sigFC = ply.Qbar @ epsFC

            sigMBottom1 = T @ sigMBottom
            sigMTop1 = T @ sigMTop
            sigMMid1 = T @ sigMMid

            sigFT1 = T1 @ sigFT
            sigFC1 = T1 @ sigFC

            epsMBottom = np.concatenate((epsMBottom, self.gT0), axis=None)
            epsMTop = np.concatenate((epsMTop, self.gT0), axis=None)
            epsMMid = np.concatenate((epsMMid, self.gT0), axis=None)

            epsMBottom1 = Te @ epsMBottom
            epsMTop1 = Te @ epsMTop
            epsMMid1 = Te @ epsMMid

            epsFT1 = Te1 @ epsFT
            epsFC1 = Te1 @ epsFC

            self._stressM[str(i)] = {"bottom": sigMBottom, "top": sigMTop, "mid": sigMMid}
            self._stressT[str(i)] = {"bottom": sigFT, "top": sigFT, "mid": sigFT}
            self._stressH[str(i)] = {"bottom": sigFC, "top": sigFC, "mid": sigFC}

            self._stressM1[str(i)] = {"bottom": sigMBottom1, "top": sigMTop1, "mid": sigMMid1}
            self._stressT1[str(i)] = {"bottom": sigFT1, "top": sigFT1, "mid": sigFT1}
            self._stressH1[str(i)] = {"bottom": sigFC1, "top": sigFC1, "mid": sigFC1}

            self._strainM[str(i)] = {"bottom": epsMBottom, "top": epsMTop, "mid": epsMMid}
            self._strainT[str(i)] = {"bottom": epsFT, "top": epsFT, "mid": epsFT}
            self._strainH[str(i)] = {"bottom": epsFC, "top": epsFC, "mid": epsFC}

            self._strainM1[str(i)] = {"bottom": epsMBottom1, "top": epsMTop1, "mid": epsMMid1}
            self._strainT1[str(i)] = {"bottom": epsFT1, "top": epsFT1, "mid": epsFT1}
            self._strainH1[str(i)] = {"bottom": epsFC1, "top": epsFC1, "mid": epsFC1}


    def _calculateMaxStress(self):
        """Calculate MS assuming Max Stress failure theory
        
        """

        self._stress1MS = {}
        self._stressM1MS = {}
        for i, ply in enumerate(self.panel.laminate.stacking_sequence, start=1):
            s11T = ply.material.sig11_tension_allowable
            s11C = ply.material.sig11_compression_allowable
            s22T = ply.material.sig22_tension_allowable
            s22C = ply.material.sig22_compression_allowable
            s12 = ply.material.tau12_allowable
            
            sigBottom1 = self.stress1[str(i)]["bottom"]
            sigTop1 = self.stress1[str(i)]["top"]
            sigMid1 = self.stress1[str(i)]["mid"]

            if sigBottom1[0] > STRESSMIN:
                ms11B = (s11T/sigBottom1[0])-1.0
            elif sigBottom1[0] < -STRESSMIN:
                ms11B = (s11C/sigBottom1[0])-1.0
            else:
                ms11B = "NA"
            if sigBottom1[1] > STRESSMIN:
                ms22B = (s22T/sigBottom1[1])-1.0
            elif sigBottom1[1] < -STRESSMIN:
                ms22B = (s22C/sigBottom1[1])-1.0
            else:
                ms22B = "NA"
            if abs(sigBottom1[2]) <= STRESSMIN:
                ms12B = "NA"
            else:
                ms12B = (s12/abs(sigBottom1[2]))-1.0
            msB = [ms11B, ms22B, ms12B]

            if sigTop1[0] > STRESSMIN:
                ms11T = (s11T/sigTop1[0])-1.0
            elif sigTop1[0] < -STRESSMIN:
                ms11T = (s11C/sigTop1[0])-1.0
            else:
                ms11T = "NA"
            if sigTop1[1] > STRESSMIN:
                ms22T = (s22T/sigTop1[1])-1.0
            elif sigTop1[1] < -STRESSMIN:
                ms22T = (s22C/sigTop1[1])-1.0
            else:
                ms22T = "NA"
            if abs(sigTop1[2]) <= STRESSMIN:
                ms12T = "NA"
            else:
                ms12T = (s12/abs(sigTop1[2]))-1.0
            msT = [ms11T, ms22T, ms12T]

            if sigMid1[0] > STRESSMIN:
                ms11M = (s11T/sigMid1[0])-1.0
            elif sigMid1[0] < -STRESSMIN:
                ms11M = (s11C/sigMid1[0])-1.0
            else:
                ms11M = "NA"
            if sigMid1[1] > STRESSMIN:
                ms22M = (s22T/sigMid1[1])-1.0
            elif sigMid1[1] < -STRESSMIN:
                ms22M = (s22C/sigMid1[1])-1.0
            else:
                ms22M = "NA"
            if abs(sigMid1[2]) <= STRESSMIN:
                ms12M = "NA"
            else:
                ms12M = (s12/abs(sigMid1[2]))-1.0
            msM = [ms11M, ms22M, ms12M]

            self._stress1MS[str(i)]={"bottom": msB, "top": msT, "mid": msM}

            sigMBottom1 = self.stressM1[str(i)]["bottom"]
            sigMTop1 = self.stressM1[str(i)]["top"]
            sigMMid1 = self.stressM1[str(i)]["mid"]

            if sigMBottom1[0] > STRESSMIN:
                msM11B = (s11T/sigMBottom1[0])-1.0
            elif sigMBottom1[0] < -STRESSMIN:
                msM11B = (s11C/sigMBottom1[0])-1.0
            else:
                msM11B = "NA"
            if sigMBottom1[1] > STRESSMIN:
                msM22B = (s22T/sigMBottom1[1])-1.0
            elif sigMBottom1[1] < -STRESSMIN:
                msM22B = (s22C/sigMBottom1[1])-1.0
            else:
                msM22B = "NA"
            if abs(sigMBottom1[2]) <= STRESSMIN:
                msM12B = "NA"
            else:
                msM12B = (s12/abs(sigMBottom1[2]))-1.0
            msMB = [msM11B, msM22B, msM12B]

            if sigMTop1[0] > STRESSMIN:
                msM11T = (s11T/sigMTop1[0])-1.0
            elif sigMTop1[0] < -STRESSMIN:
                msM11T = (s11C/sigMTop1[0])-1.0
            else:
                msM11T = "NA"
            if sigMTop1[1] > STRESSMIN:
                msM22T = (s22T/sigMTop1[1])-1.0
            elif sigMTop1[1] < -STRESSMIN:
                msM22T = (s22C/sigMTop1[1])-1.0
            else:
                msM22T = "NA"
            if abs(sigMTop1[2]) <= STRESSMIN:
                msM12T = "NA"
            else:
                msM12T = (s12/abs(sigMTop1[2]))-1.0
            msMT = [msM11T, msM22T, msM12T]

            if sigMMid1[0] > STRESSMIN:
                msM11M = (s11T/sigMMid1[0])-1.0
            elif sigMMid1[0] < -STRESSMIN:
                msM11M = (s11C/sigMMid1[0])-1.0
            else:
                msM11M = "NA"
            if sigMMid1[1] > STRESSMIN:
                msM22M = (s22T/sigMMid1[1])-1.0
            elif sigMMid1[1] < -STRESSMIN:
                msM22M = (s22C/sigMMid1[1])-1.0
            else:
                msM22M = "NA"
            if abs(sigMMid1[2]) <= STRESSMIN:
                msM12M = "NA"
            else:
                msM12M = (s12/abs(sigMMid1[2]))-1.0
            msMM = [msM11M, msM22M, msM12M]
            
            self._stressM1MS[str(i)]={"bottom": msMB, "top": msMT, "mid": msMM}

    def _calculateMaxStrain(self):
        """Calculate MS assuming Max Strain failure theory
        
        """

        self._strain1MS = {}
        self._strainM1MS = {}
        for i, ply in enumerate(self.panel.laminate.stacking_sequence, start=1):
            e11T = ply.material.eps11_tension_allowable
            e11C = ply.material.eps11_compression_allowable
            e22T = ply.material.eps22_tension_allowable
            e22C = ply.material.eps22_compression_allowable
            g12 = ply.material.gamma12_allowable
            
            epsBottom1 = self.strain1[str(i)]["bottom"]
            epsTop1 = self.strain1[str(i)]["top"]
            epsMid1 = self.strain1[str(i)]["mid"]

            if epsBottom1[0] > STRAINMIN:
                ms11B = (e11T/epsBottom1[0])-1.0
            elif epsBottom1[0] < -STRAINMIN:
                ms11B = (e11C/epsBottom1[0])-1.0
            else:
                ms11B = "NA"
            if epsBottom1[1] > STRAINMIN:
                ms22B = (e22T/epsBottom1[1])-1.0
            elif epsBottom1[1] < -STRAINMIN:
                ms22B = (e22C/epsBottom1[1])-1.0
            else:
                ms22B = "NA"
            if abs(epsBottom1[2]) <= STRAINMIN:
                ms12B = "NA"
            else:
                ms12B = (g12/abs(epsBottom1[2]))-1.0
            msB = [ms11B, ms22B, ms12B]

            if epsTop1[0] > STRAINMIN:
                ms11T = (e11T/epsTop1[0])-1.0
            elif epsTop1[0] < -STRAINMIN:
                ms11T = (e11C/epsTop1[0])-1.0
            else:
                ms11T = "NA"
            if epsTop1[1] > STRAINMIN:
                ms22T = (e22T/epsTop1[1])-1.0
            elif epsTop1[1] < -STRAINMIN:
                ms22T = (e22C/epsTop1[1])-1.0
            else:
                ms22T = "NA"
            if abs(epsTop1[2]) <= STRAINMIN:
                ms12T = "NA"
            else:
                ms12T = (g12/abs(epsTop1[2]))-1.0
            msT = [ms11T, ms22T, ms12T]

            if epsMid1[0] > STRAINMIN:
                ms11M = (e11T/epsMid1[0])-1.0
            elif epsMid1[0] < -STRAINMIN:
                ms11M = (e11C/epsMid1[0])-1.0
            else:
                ms11M = "NA"
            if epsMid1[1] > STRAINMIN:
                ms22M = (e22T/epsMid1[1])-1.0
            elif epsMid1[1] < -STRAINMIN:
                ms22M = (e22C/epsMid1[1])-1.0
            else:
                ms22M = "NA"
            if abs(epsMid1[2]) <= STRAINMIN:
                ms12M = "NA"
            else:
                ms12M = (g12/abs(epsMid1[2]))-1.0
            msM = [ms11M, ms22M, ms12M]

            self._strain1MS[str(i)]={"bottom": msB, "top": msT, "mid": msM}

            epsMBottom1 = self.strainM1[str(i)]["bottom"]
            epsMTop1 = self.strainM1[str(i)]["top"]
            epsMMid1 = self.strainM1[str(i)]["mid"]

            if epsMBottom1[0] > STRAINMIN:
                msM11B = (e11T/epsMBottom1[0])-1.0
            elif epsMBottom1[0] < -STRAINMIN:
                msM11B = (e11C/epsMBottom1[0])-1.0
            else:
                msM11B = "NA"
            if epsMBottom1[1] > STRAINMIN:
                msM22B = (e22T/epsMBottom1[1])-1.0
            elif epsMBottom1[1] < -STRAINMIN:
                msM22B = (e22C/epsMBottom1[1])-1.0
            else:
                msM22B = "NA"
            if abs(epsMBottom1[2]) <= STRAINMIN:
                msM12B = "NA"
            else:
                msM12B = (g12/abs(epsMBottom1[2]))-1.0
            msMB = [msM11B, msM22B, msM12B]

            if epsMTop1[0] > STRAINMIN:
                msM11T = (e11T/epsMTop1[0])-1.0
            elif epsMTop1[0] < -STRAINMIN:
                msM11T = (e11C/epsMTop1[0])-1.0
            else:
                msM11T = "NA"
            if epsMTop1[1] > STRAINMIN:
                msM22T = (e22T/epsMTop1[1])-1.0
            elif epsMTop1[1] < -STRAINMIN:
                msM22T = (e22C/epsMTop1[1])-1.0
            else:
                msM22T = "NA"
            if abs(epsMTop1[2]) <= STRAINMIN:
                msM12T = "NA"
            else:
                msM12T = (g12/abs(epsMTop1[2]))-1.0
            msMT = [msM11T, msM22T, msM12T]

            if epsMMid1[0] > STRAINMIN:
                msM11M = (e11T/epsMMid1[0])-1.0
            elif epsMMid1[0] < -STRAINMIN:
                msM11M = (e11C/epsMMid1[0])-1.0
            else:
                msM11M = "NA"
            if epsMMid1[1] > STRAINMIN:
                msM22M = (e22T/epsMMid1[1])-1.0
            elif epsMMid1[1] < -STRAINMIN:
                msM22M = (e22C/epsMMid1[1])-1.0
            else:
                msM22M = "NA"
            if abs(epsMMid1[2]) <= STRAINMIN:
                msM12M = "NA"
            else:
                msM12M = (g12/abs(epsMMid1[2]))-1.0
            msMM = [msM11M, msM22M, msM12M]
            
            self._strainM1MS[str(i)]={"bottom": msMB, "top": msMT, "mid": msMM}

    def _calculateTsaiHill(self):
        """Calculate MS assuming Tsai Hill failure theory
        
        """

        self._tsaiHill1MS = {}
        self._tsaiHillM1MS = {}
        for i, ply in enumerate(self.panel.laminate.stacking_sequence, start=1):
            s11T = ply.material.sig11_tension_allowable
            s22T = ply.material.sig22_tension_allowable
            s12 = ply.material.tau12_allowable
            
            sigBottom1 = self.stress1[str(i)]["bottom"]
            sigTop1 = self.stress1[str(i)]["top"]
            sigMid1 = self.stress1[str(i)]["mid"]

            if abs(sigBottom1[0]) > STRESSMIN:
                ms11B = sigBottom1[0]/s11T
            else:
                ms11B = 0.0
            if abs(sigBottom1[1]) > STRESSMIN:
                ms22B = sigBottom1[1]/s22T
            else:
                ms22B = 0.0
            if abs(sigBottom1[2]) > STRESSMIN:
                ms12B = abs(sigBottom1[2])/s12
            else:
                ms12B = 0.0
            valB = (ms11B**2) + (ms22B**2) - (ms11B*ms22B) + (ms12B**2)
            if valB > TOL:
                msB = (1.0/math.sqrt(valB))-1.0
            else:
                msB = "NA"

            if abs(sigTop1[0]) > STRESSMIN:
                ms11T = sigTop1[0]/s11T
            else:
                ms11T = 0.0
            if abs(sigTop1[1]) > STRESSMIN:
                ms22T = sigTop1[1]/s22T
            else:
                ms22T = 0.0
            if abs(sigTop1[2]) > STRESSMIN:
                ms12T = abs(sigTop1[2])/s12
            else:
                ms12T = 0.0
            valT = (ms11T**2) + (ms22T**2) - (ms11T*ms22T) + (ms12T**2)
            if valT > TOL:
                msT = (1.0/math.sqrt(valT))-1.0
            else:
                msT = "NA"

            if abs(sigMid1[0]) > STRESSMIN:
                ms11M = sigMid1[0]/s11T
            else:
                ms11M = 0.0
            if abs(sigMid1[1]) > STRESSMIN:
                ms22M = sigMid1[1]/s22T
            else:
                ms22M = 0.0
            if abs(sigMid1[2]) > STRESSMIN:
                ms12M = abs(sigMid1[2])/s12
            else:
                ms12M = 0.0
            valM = (ms11M**2) + (ms22M**2) - (ms11M*ms22M) + (ms12M**2)
            if valM > TOL:
                msM = (1.0/math.sqrt(valM))-1.0
            else:
                msM = "NA"

            self._tsaiHill1MS[str(i)]={"bottom": (valB, msB), "top": (valT, msT), "mid": (valM, msM)}

            sigMBottom1 = self.stressM1[str(i)]["bottom"]
            sigMTop1 = self.stressM1[str(i)]["top"]
            sigMMid1 = self.stressM1[str(i)]["mid"]

            if abs(sigMBottom1[0]) > STRESSMIN:
                msM11B = sigMBottom1[0]/s11T
            else:
                msM11B = 0.0
            if abs(sigMBottom1[1]) > STRESSMIN:
                msM22B = sigMBottom1[1]/s22T
            else:
                msM22B = 0.0
            if abs(sigMBottom1[2]) > STRESSMIN:
                msM12B = abs(sigMBottom1[2])/s12
            else:
                msM12B = 0.0
            valMB = (msM11B**2) + (msM22B**2) - (msM11B*msM22B) + (msM12B**2)
            if valMB > TOL:
                msMB = (1.0/math.sqrt(valMB))-1.0
            else:
                msMB = "NA"

            if abs(sigMTop1[0]) > STRESSMIN:
                msM11T = sigMTop1[0]/s11T
            else:
                msM11T = 0.0
            if abs(sigMTop1[1]) > STRESSMIN:
                msM22T = sigMTop1[1]/s22T
            else:
                msM22T = 0.0
            if abs(sigMTop1[2]) > STRESSMIN:
                msM12T = abs(sigMTop1[2])/s12
            else:
                msM12T = 0.0
            valMT = (msM11T**2) + (msM22T**2) - (msM11T*msM22T) + (msM12T**2)
            if valMT > TOL:
                msMT = (1.0/math.sqrt(valMT))-1.0
            else:
                msMT = "NA"

            if abs(sigMMid1[0]) > STRESSMIN:
                msM11M = sigMMid1[0]/s11T
            else:
                msM11M = 0.0
            if abs(sigMMid1[1]) > STRESSMIN:
                msM22M = sigMMid1[1]/s22T
            else:
                msM22M = 0.0
            if abs(sigMMid1[2]) > STRESSMIN:
                msM12M = abs(sigMMid1[2])/s12
            else:
                msM12M = 0.0
            valMM = (msM11M**2) + (msM22M**2) - (msM11M*msM22M) + (msM12M**2)
            if valMM > TOL:
                msMM = (1.0/math.sqrt(valMM))-1.0
            else:
                msMM = "NA"
            
            self._tsaiHillM1MS[str(i)]={"bottom": (valMB, msMB), "top": (valMT, msMT), "mid": (valMM, msMM)}

    def _calculateTsaiWu(self):
        """Calculate MS assuming Tsai Wu failure theory
        
        """

        self._tsaiWu1MS = {}
        self._tsaiWuM1MS = {}
        for i, ply in enumerate(self.panel.laminate.stacking_sequence, start=1):
            s11T = ply.material.sig11_tension_allowable
            s11C = ply.material.sig11_compression_allowable
            s22T = ply.material.sig22_tension_allowable
            s22C = ply.material.sig22_compression_allowable
            s12 = ply.material.tau12_allowable
            F1 = (1.0/s11T)+(1.0/s11C)
            F2 = (1.0/s22T)+(1.0/s22C)
            F11 = -1.0/(s11T*s11C)
            F22 = -1.0/(s22T*s22C)
            F66 = 1.0/(s12**2)
            F12 = -0.5*math.sqrt(F11*F22)
            
            sigBottom1 = self.stress1[str(i)]["bottom"]
            sigTop1 = self.stress1[str(i)]["top"]
            sigMid1 = self.stress1[str(i)]["mid"]

            aB = (F11*sigBottom1[0]**2) + (F22*sigBottom1[1]**2) + (2.0*F12*sigBottom1[0]*sigBottom1[1]) + (F66*sigBottom1[2]**2)
            bB = (F1*sigBottom1[0]) + (F2*sigBottom1[1])
            cB = -1.0
            if ((bB**2)-(4.0*aB*cB)) > 0.0:
                valB1 = (-bB + math.sqrt((bB**2)-(4.0*aB*cB)))/(2*aB)
                valB2 = (-bB - math.sqrt((bB**2)-(4.0*aB*cB)))/(2*aB)
            else:
                valB1 = 0.0
                valB2 = 0.0
            if min(valB1, valB2) < 0.0:
                valB = max(valB1, valB2)
            else:
                valB = min(valB1, valB2)
            if valB > TOL:
                msB = valB-1.0
            else:
                msB = "NA"
            fiB = aB+bB

            aT = (F11*sigTop1[0]**2) + (F22*sigTop1[1]**2) + (2.0*F12*sigTop1[0]*sigTop1[1]) + (F66*sigTop1[2]**2)
            bT = (F1*sigTop1[0]) + (F2*sigTop1[1])
            cT = -1.0
            if ((bT**2)-(4.0*aT*cT)) > 0.0:
                valT1 = (-bT + math.sqrt((bT**2)-(4.0*aT*cT)))/(2*aT)
                valT2 = (-bT - math.sqrt((bT**2)-(4.0*aT*cT)))/(2*aT)
            else:
                valT1 = 0.0
                valT2 = 0.0
            if min(valT1, valT2) < 0.0:
                valT = max(valT1, valT2)
            else:
                valT = min(valT1, valT2)
            if valT > TOL:
                msT = valT-1.0
            else:
                msT = "NA"
            fiT = aT+bT

            aM = (F11*sigMid1[0]**2) + (F22*sigMid1[1]**2) + (2.0*F12*sigMid1[0]*sigMid1[1]) + (F66*sigMid1[2]**2)
            bM = (F1*sigMid1[0]) + (F2*sigMid1[1])
            cM = -1.0
            if ((bM**2)-(4.0*aM*cM)) > 0.0:
                valM1 = (-bM + math.sqrt((bM**2)-(4.0*aM*cM)))/(2*aM)
                valM2 = (-bM - math.sqrt((bM**2)-(4.0*aM*cM)))/(2*aM)
            else:
                valM1 = 0.0
                valM2 = 0.0
            if min(valM1, valM2) < 0.0:
                valM = max(valM1, valM2)
            else:
                valM = min(valM1, valM2)
            if valM > TOL:
                msM = valM-1.0
            else:
                msM = "NA"
            fiM = aM+bM

            self._tsaiWu1MS[str(i)]={"bottom": (fiB, msB), "top": (fiT, msT), "mid": (fiM, msM)}

            sigMBottom1 = self.stressM1[str(i)]["bottom"]
            sigMTop1 = self.stressM1[str(i)]["top"]
            sigMMid1 = self.stressM1[str(i)]["mid"]

            aMB = (F11*sigMBottom1[0]**2) + (F22*sigMBottom1[1]**2) + (2.0*F12*sigMBottom1[0]*sigMBottom1[1]) + (F66*sigMBottom1[2]**2)
            bMB = (F1*sigMBottom1[0]) + (F2*sigMBottom1[1])
            cMB = -1.0
            if ((bMB**2)-(4.0*aMB*cMB)) > 0.0:
                valMB1 = (-bMB + math.sqrt((bMB**2)-(4.0*aMB*cMB)))/(2*aMB)
                valMB2 = (-bMB - math.sqrt((bMB**2)-(4.0*aMB*cMB)))/(2*aMB)
            else:
                valMB1 = 0.0
                valMB2 = 0.0
            if min(valMB1, valMB2) < 0.0:
                valMB = max(valMB1, valMB2)
            else:
                valMB = min(valMB1, valMB2)
            if valMB > TOL:
                msMB = valMB-1.0
            else:
                msMB = "NA"
            fiMB = aMB+bMB

            aMT = (F11*sigMTop1[0]**2) + (F22*sigMTop1[1]**2) + (2.0*F12*sigMTop1[0]*sigMTop1[1]) + (F66*sigMTop1[2]**2)
            bMT = (F1*sigMTop1[0]) + (F2*sigMTop1[1])
            cMT = -1.0
            if ((bMT**2)-(4.0*aMT*cMT)) > 0.0:
                valMT1 = (-bMT + math.sqrt((bMT**2)-(4.0*aMT*cMT)))/(2*aMT)
                valMT2 = (-bMT - math.sqrt((bMT**2)-(4.0*aMT*cMT)))/(2*aMT)
            else:
                valMT1 = 0.0
                valMT2 = 0.0
            if min(valMT1, valMT2) < 0.0:
                valMT = max(valMT1, valMT2)
            else:
                valMT = min(valMT1, valMT2)
            if valMT > TOL:
                msMT = valMT-1.0
            else:
                msMT = "NA"
            fiMT = aMT+bMT

            aMM = (F11*sigMMid1[0]**2) + (F22*sigMMid1[1]**2) + (2.0*F12*sigMMid1[0]*sigMMid1[1]) + (F66*sigMMid1[2]**2)
            bMM = (F1*sigMMid1[0]) + (F2*sigMMid1[1])
            cMM = -1.0
            if ((bMM**2)-(4.0*aMM*cMM)) > 0.0:
                valMM1 = (-bMM + math.sqrt((bMM**2)-(4.0*aMM*cMM)))/(2*aMM)
                valMM2 = (-bMM - math.sqrt((bMM**2)-(4.0*aMM*cMM)))/(2*aMM)
            else:
                valMM1 = 0.0
                valMM2 = 0.0
            if min(valMM1, valMM2) < 0.0:
                valMM = max(valMM1, valMM2)
            else:
                valMM = min(valMM1, valMM2)
            if valMM > TOL:
                msMM = valMM-1.0
            else:
                msMM = "NA"
            fiMM = aMM+bMM
            
            self._tsaiWuM1MS[str(i)]={"bottom": (fiMB, msMB), "top": (fiMT, msMT), "mid": (fiMM, msMM)}
