import numbers
import numpy as np
import scipy as sp
import sys
sys.path.append('D:/repositories/composipy')
from composipy.ply_class import Ply
[docs]class Laminate:
'''
This class creates laminate object. It needs ply objects and the angle information.
Some formulation characteristics are:
Laminate formulations ares used (see References)
Main reference is the chapter 4 of reference 2.
Parameters
----------
layup : list
The layup instance is composed of a list containing a tuple to each ply.
The tuple must contain the ply angle (float in degrees) with relation to the 1 direciton
and a ply object (of Ply class).
[(angle_of_ply_1, ply_1), (angle_of_ply_2, ply_2), ... (angle_of_ply_n, ply_n)]
Returns
-------
None
Example
-------
>>> from composipy import Ply, Laminate
>>> ply_1 = Ply(129500, 9370, 0.38, 5240, 0.2)
>>> layup_1 = [(90, ply_1), (0, ply_1), (90, ply_1)]
>>> laminate = Laminate(layup_1)
>>> laminate_1.D # retunrs a array containing bending stiffness matrix [D] of the laminate
>>> laminate_1.A # retunrs a array containing stiffness matrix [A] of the laminate
>>> laminate_1.B # retunrs a array containing coupled stiffness matrix [B] of the laminate
>>> laminate_1.print_ABD() # method that prints ABD matrices of the laminate
>>> laminate_1.ABD_p # returns an array containing the ABD prime matricies of the laminate
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, layup):
# Checking layup
if not isinstance(layup, list):
raise ValueError(
'layup must be a list of tuples.\n\
Each tuple must contain a angle value and a Ply object'
)
for ply in layup:
if not isinstance(ply[0], numbers.Real):
raise ValueError(f'the angle must be a real number. Check {ply}')
if not isinstance(ply[1], Ply):
raise ValueError(f'the ply mus be a Ply object. Check {ply}')
self.layup = layup
self._z_position = None
self._Q_layup = None
self._T_layup = None
self._A = None
self._B = None
self._D = None
self._ABD_p = None
#Properties
@property
def z_position(self):
total_thickness = 0
for t in self.layup:
total_thickness += t[1].thickness
current_z = -total_thickness/2
ply_position = [current_z]
for t in self.layup:
current_z += t[1].thickness
ply_position.append(current_z)
return ply_position
@property
def Q_layup(self):
if self._Q_layup is None:
self._Q_layup = []
for theta in self.layup:
c = np.cos(theta[0]*np.pi/180)
s = np.sin(theta[0]*np.pi/180)
T_real = np.array([
[c**2, s**2, 2*c*s],
[s**2, c**2, -2*c*s],
[-c*s, c*s, c**2-s**2]
])
T_engineering = np.array([
[c**2, s**2, c*s],
[s**2, c**2, -c*s],
[-2*c*s, 2*c*s, c**2-s**2]
])
self._Q_layup.append(
(np.linalg.inv(T_real))
@ theta[1].Q_0
@ T_engineering
)
return self._Q_layup
@property
def T_layup(self):
if self._T_layup is None:
self._T_layup = []
for theta in self.layup:
c = np.cos(theta[0]*np.pi/180)
s = np.sin(theta[0]*np.pi/180)
T_real = np.array([
[c**2, s**2, 2*c*s],
[s**2, c**2, -2*c*s],
[-c*s, c*s, c**2-s**2]
])
T_engineering = np.array([
[c**2, s**2, c*s],
[s**2, c**2, -c*s],
[-2*c*s, 2*c*s, c**2-s**2]
])
self._T_layup.append([T_real,T_engineering])
return self._T_layup
@property
def A(self):
'''[A] Matrix as numpy.ndarray '''
if self._A is None:
self._A = np.zeros(9).reshape(3,3)
for i in enumerate(self.Q_layup):
zk1 = self.z_position[i[0]+1]
zk0 = self.z_position[i[0]]
self._A += (zk1-zk0) * i[1]
return self._A
@property
def B(self):
'''[B] Matrix as numpy.ndarray '''
if self._B is None:
self._B = np.zeros(9).reshape(3,3)
for i in enumerate(self.Q_layup):
zk1 = self.z_position[i[0]+1]
zk0 = self.z_position[i[0]]
self._B += (1/2) * (zk1**2-zk0**2) * i[1]
return self._B
@property
def D(self):
'''[D] Matrix as numpy.ndarray '''
if self._D is None:
self._D = np.zeros(9).reshape(3,3)
for i in enumerate(self.Q_layup):
zk1 = self.z_position[i[0]+1]
zk0 = self.z_position[i[0]]
self._D += (1/3) * (zk1**3-zk0**3) * i[1]
return self._D
@property
def ABD_p(self):
''' [A',B',D'], which is inverse of ABD Matrix, as numpy.ndarray '''
if self._ABD_p is None:
A_p = np.linalg.inv(self.A) \
+ (-np.linalg.inv(self.A) * self.B) \
* (np.linalg.inv(self.D-self.B * np.linalg.inv(self.A) * self.B)) \
* (self.B * np.linalg.inv(self.A))
B_p = (-np.linalg.inv(self.A) * self.B) \
* np.linalg.inv(self.D \
- self.B \
* np.linalg.inv(self.A) \
* self.B
)
D_p = np.linalg.inv(
self.D - self.B
* np.linalg.inv(self.A)
* self.B
)
ABD_p = sp.matrix(
np.vstack(
(np.hstack((A_p,B_p)), np.hstack((B_p,D_p))))
)
ABD_p[np.isnan(ABD_p)] = 0
self._ABD_p = ABD_p
return self._ABD_p
@staticmethod
def _pprint(*args):
nStrings = 20
representation = ''
for arg in args:
nSpaces = nStrings - len(str(arg))
if nSpaces < 0:
nSpaces = 1
representation += str(arg) + nSpaces * " "
return representation
#Representation (str not implemented)
def __repr__(self):
representation = ''
for angle, ply in self.layup:
if angle == abs(90):
angle_repr = '|||||'
elif angle == 0:
angle_repr = '====='
elif angle == 45 or angle == -45:
angle_repr = '/////'
else:
angle_repr = '***'
representation += self._pprint(ply.name, angle, angle_repr) + '\n'
return representation
#Comparisons
def __eq__(self, other):
if isinstance(other, Laminate):
return (self.layup == other.layup)
return NotImplemented
#Methods
[docs] def show_ABD(self):
''' This method prints ABD Matrix'''
result = "[A]:\n" + str(self.A) + "\n"
result += "[B]:\n" + str(self.B) + "\n"
result += "[D]:\n" + str(self.D)
print(result)
return None