Source code for composipy.strength_class


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