import numpy as np
import scipy as sp
from composipy.laminate_class import Laminate
from composipy.load_class import Load
[docs]class Strength:
'''
This class creates the Strength object. It's inputs are Laminate and Load objects.
The strength Object returns laminate strenght parameteres, including strain and stress in laminate and load coordinates.
It also returns the Tsai-Wu failure index for all plies in the current load conditions and the First Ply Failure
analysis (based on Tsai-Wu failure criterion).
Parameters
----------
laminate : Laminate
The laminate is an object that contains the layup scheme with its mechanical proprieties.
[(angle_of_ply_1, ply_1), (angle_of_ply_2, ply_2), ... (angle_of_ply_n, ply_n)]
load: Load
Load is and object that contains the in-plane forces and moments in x and y directions.
Load(Nx, Ny, Nxy, Mx, My, Mxy)
Example
-------
>>> from composipy import Ply, Laminate, Load, Strength
>>> ply_1 = Ply(129500, 9370, 0.38, 5240, 0.2)
>>> layup_1 = [(90, ply_1), (0, ply_1), (90, ply_1)]
>>> laminate_1 = Laminate(layup_1)
>>> load_1 = Load(100, 200, 50, 1000, 250, 100)
>>> str_analysis = Strength(laminate_1, load_1)
>>> str_analysis.mid_strain_xy # Returns the mid-plane strain and slope of the laminate in xy coordinates
>>> str_analysis.mid_strain_12 # Returns the mid-plane strain and slope of the laminate in 12 coordinates
>>> str_analysis.stress_xy # Returns the maximum stress for all plies in the xy coordinates
>>> str_analysis.stress_12 # Returns the maximum stress for all plies in the 12 coordinates
>>> str_analysis.TW_i # Returns the Tsai-Wu failure index for all plies
>>> str_anaysis.FPF # Prints the First Ply Failure load, failed plies, TW_i for the laminate and Strength Ratio
References
----------
1 - JONES, M. Robert. Mechanics of Composite Materials. Taylor & Francis: 2nd ed 1999.
2 - Analysis and Design of composite structures. Class notes. ITA 2020.
'''
def __init__(self, laminate, load):
# Checking layup
if not isinstance(laminate, Laminate):
raise ValueError(
'laminate must be a Laminate object. Check {laminate}'
)
if not isinstance(load, Load):
raise ValueError('load must be a Load object. Check {load}')
self.laminate = laminate
self.load = load
self._mid_strain_xy = None
self._mid_strain_12 = None
self._stress_12 = None
self._stress_xy = None
self._TW_i = None
self._FPF = None
#Properties
# TODO we can create methods instead properties. Like get_strain_xy and so on.
# These methods might be documented since they are accessible by the user.
@property
def mid_strain_xy(self):
''' [strain_x, strain_y, strain_xy, slope_x, slope_y, slope_xy] '''
if self._mid_strain_xy is None:
load_array = sp.matrix(
np.vstack((
self.load.Nx,
self.load.Ny,
self.load.Nxy,
self.load.Mx,
self.load.My,
self.load.Mxy)))
self._mid_strain_xy = self.laminate.ABD_p * load_array
return self._mid_strain_xy
@property
def mid_strain_12(self):
''' [strain_1, strain_2, strain_12, slope_1, slope_2, slope_12]'''
if self._mid_strain_12 is None:
mid_strain_12_lam = []
for T_ply in self.laminate.T_layup:
mid_strain_12_ply = T_ply[1] * self.mid_strain_xy[0:3]
mid_strain_12_lam.append(mid_strain_12_ply)
self._mid_strain_12 = mid_strain_12_lam
return self._mid_strain_12
@property
def stress_xy(self):
''' [sigma_x, sigma_y, tau_xy] for each ply'''
if self._stress_xy is None:
ply_num = 0
stress_xy_lam = []
for q_bar in self.laminate.Q_layup:
stress_xy_ply = q_bar \
* self.mid_strain_xy[0:3] \
+ self.laminate.z_position[ply_num] \
* self.mid_strain_xy[3:7]
stress_xy_lam.append(stress_xy_ply)
ply_num += 1
self._stress_xy = stress_xy_lam
return self._stress_xy
@property
def stress_12(self):
''' [sigma_1, sigma_2, tau_12] for each ply'''
if self._stress_12 is None:
stress_12_lam = []
ply_num = 0
for T_ply in self.laminate.T_layup:
stress_12_ply = T_ply[0] * self.stress_xy[ply_num]
stress_12_lam.append(stress_12_ply)
ply_num += 1
self._stress_12 = stress_12_lam
return self._stress_12
@property
def TW_i(self):
''' Tsai-Wu index (F12 was aproximated using Hoffman criterion)'''
if self._TW_i is None:
TW_i_lam = []
ply_num = 0
for X in self.laminate.layup:
F1 = 1/X[1].t1 + 1/X[1].c1
F2 = 1/X[1].t2 + 1/X[1].c2
F11 = -1/(X[1].t1*X[1].c1)
F22 = -1/(X[1].t2*X[1].c2)
F66 = 1/X[1].s**2
F12 = 1/(2*X[1].t1*X[1].c1)
TW_i = float(F1 * self.stress_12[ply_num][0] \
+ F2 * self.stress_12[ply_num][1] \
+ F11 * self.stress_12[ply_num][0]**2 \
+ F22 * self.stress_12[ply_num][1]**2 \
+ F66 * self.stress_12[ply_num][2]**2 \
+ 2 * F12 * self.stress_12[ply_num][0] * self.stress_12[ply_num][1])
TW_i_lam.append(TW_i)
ply_num += 1
self._TW_i = TW_i_lam
return self._TW_i
@property
def FPF(self):
''' First Ply Failure analysis '''
if self._FPF is None:
i = 1
failed_plies = []
while len(failed_plies) == 0:
ABD_p = self.laminate.ABD_p
load_array = i * sp.matrix(np.vstack((self.load.Nx,
self.load.Ny,
self.load.Nxy,
self.load.Mx,
self.load.My,
self.load.Mxy)))
mid_strain_xy = ABD_p * load_array
stress_xy_lam = []
stress_12_lam = []
TW_i_lam = []
for x in range(len(self.laminate.layup)):
stress_xy_ply = self.laminate.Q_layup[x] \
* mid_strain_xy[0:3] \
+ self.laminate.z_position[x] \
* mid_strain_xy[3:7]
stress_xy_lam.append(stress_xy_ply)
stress_12_ply = self.laminate.T_layup[x][0] * stress_xy_ply
stress_12_lam.append(stress_12_ply)
F1 = 1 / self.laminate.layup[x][1].t1 \
+ 1 / self.laminate.layup[x][1].c1
F2 = 1 / self.laminate.layup[x][1].t2 \
+ 1 / self.laminate.layup[x][1].c2
F11 = -1 / (self.laminate.layup[x][1].t1 \
* self.laminate.layup[x][1].c1)
F22 = -1 / (self.laminate.layup[x][1].t2
* self.laminate.layup[x][1].c2)
F66 = 1 / self.laminate.layup[x][1].s**2
F12 = 1 / (2 * self.laminate.layup[x][1].t1 \
* self.laminate.layup[x][1].c1)
TW_i = float(F1*stress_12_ply[0]
+ F2*stress_12_ply[1]
+ F11*stress_12_ply[0]**2
+ F22*stress_12_ply[1]**2
+ F66*stress_12_ply[2]**2
+ 2*F12*stress_12_ply[0] * stress_12_ply[1])
TW_i_lam.append(TW_i)
for j in range(len(TW_i_lam)):
if TW_i_lam[j] > 1:
failed_plies.append(j+1)
i = i * 1.01
if i > 100: break
self._SR = i
print("FPF Load: \n", load_array)
print("Failed Ply(ies): \n", failed_plies)
print("Laminate Tsai-Wu Index in FPF: \n", TW_i_lam)
print("Strength Ratio for FPF: \n", self._SR)
return None
#Representation
def __repr__(self):
representation = ''
for ply_num in range(len(self.laminate.layup)):
representation += f'Ply {ply_num+1} \nStress_12 \n{self.stress_12[ply_num]} \nTsai-Wu Index: {self.TW_i[ply_num]} \n ============== \n'
return representation
#Comparisons
def __eq__(self, other):
if isinstance(other, Strength):
return (self.laminate == other.laminate
and self.load == other.load)
return NotImplemented