"""
Composite Laminate Module (:mod:`compmech.composite.laminate`)
==============================================================
.. currentmodule:: compmech.composite.laminate
"""
import numpy as np
from .lamina import Lamina
from .matlamina import read_laminaprop
from compmech.constants import DOUBLE
from compmech.logger import error
[docs]
def read_stack(stack, plyt=None, laminaprop=None, plyts=[], laminaprops=[],
offset=0.):
"""Read a laminate stacking sequence data.
An ``Laminate`` object is returned based on the inputs given.
Parameters
----------
stack : list
Angles of the stacking sequence in degrees.
plyt : float, optional
When all plies have the same thickness, ``plyt`` can be supplied.
laminaprop : tuple, optional
When all plies have the same material properties, ``laminaprop``
can be supplied.
plyts : list, optional
A list of floats with the thickness of each ply.
laminaprops : list, optional
A list of tuples with a laminaprop for each ply.
offset : float, optional
Offset along the normal axis about the mid-surface, which influences
the laminate properties.
Notes
-----
``plyt`` or ``plyts`` must be supplied
``laminaprop`` or ``laminaprops`` must be supplied
For orthotropic plies, the ``laminaprop`` should be::
laminaprop = (E11, E22, nu12, G12, G13, G23)
For isotropic pliey, the ``laminaprop`` should be::
laminaprop = (E, E, nu)
"""
lam = Laminate()
lam.offset = offset
lam.stack = stack
if not plyts:
if not plyt:
error('plyt or plyts must be supplied')
raise ValueError
else:
plyts = [plyt for i in stack]
if not laminaprops:
if not laminaprop:
error('laminaprop or laminaprops must be supplied')
raise ValueError
else:
laminaprops = [laminaprop for i in stack]
lam.plies = []
for plyt, laminaprop, theta in zip(plyts, laminaprops, stack):
laminaprop = laminaprop
ply = Lamina()
ply.theta = float(theta)
ply.t = plyt
ply.matobj = read_laminaprop(laminaprop)
lam.plies.append(ply)
lam.rebuild()
lam.calc_constitutive_matrix()
return lam
[docs]
def read_lamination_parameters(thickness, laminaprop,
xiA1, xiA2, xiA3, xiA4,
xiB1, xiB2, xiB3, xiB4,
xiD1, xiD2, xiD3, xiD4,
xiE1, xiE2, xiE3, xiE4):
r"""Calculates a laminate based on the lamination parameters.
The lamination parameters:
`\xi_{A1} \cdots \xi_{A4}`, `\xi_{B1} \cdots \xi_{B4}`,
`\xi_{C1} \cdots \xi_{C4}`, `\xi_{D1} \cdots \xi_{D4}`,
`\xi_{E1} \cdots \xi_{E4}`
are used to calculate the laminate constitutive matrix.
Parameters
----------
thickness : float
The total thickness of the laminate
laminaprop : tuple
The laminaprop tuple used to define the laminate material.
xiA1 to xiD4 : float
The 16 lamination parameters used to define the laminate.
Returns
-------
lam : Laminate
laminate with the ABD and ABDE matrices already calculated
"""
lam = Laminate()
lam.t = thickness
lam.matobj = read_laminaprop(laminaprop)
lam.xiA = np.array([1, xiA1, xiA2, xiA3, xiA4], dtype=DOUBLE)
lam.xiB = np.array([0, xiB1, xiB2, xiB3, xiB4], dtype=DOUBLE)
lam.xiD = np.array([1, xiD1, xiD2, xiD3, xiD4], dtype=DOUBLE)
lam.xiE = np.array([1, xiE1, xiE2, xiE3, xiE4], dtype=DOUBLE)
lam.calc_ABDE_from_lamination_parameters()
return lam
[docs]
class Laminate(object):
r"""
========= ===========================================================
attribute description
========= ===========================================================
plies list of plies
t total thickness of the laminate
offset offset at the normal direction
e1 equivalent laminate modulus in 1 direction
e2 equivalent laminate modulus in 2 direction
g12 equivalent laminate shear modulus in 12 direction
nu12 equivalent laminate Poisson ratio in 12 direction
nu21 equivalent laminate Poisson ratio in 21 direction
xiA laminate parameters for extensional matrix A
xiB laminate parameters for extension-bending matrix B
xiD laminate parameters for bending matrix D
A laminate extension matrix
B laminate extension-bending matrix
D laminate bending matrix
E laminate transferse shear matrix
ABD laminate ABD matrix
ABDE laminate ABD matrix with transverse shear terms
========= ===========================================================
"""
def __init__(self):
self.plies = []
self.matobj = None
self.t = None
self.offset = 0.
self.e1 = None
self.e2 = None
self.e3 = None
self.nu12 = None
self.g12 = None
self.g13 = None
self.g23 = None
self.xiA = None
self.xiB = None
self.xiD = None
self.A = None
self.B = None
self.D = None
self.E = None
self.ABD = None
self.ABDE = None
def rebuild(self):
lam_thick = 0
for ply in self.plies:
ply.rebuild()
lam_thick += ply.t
self.t = lam_thick
[docs]
def calc_equivalent_modulus(self):
"""Calculates the equivalent laminate properties.
The following attributes are calculated:
e1, e2, g12, nu12, nu21
"""
AI = np.linalg.inv(self.ABD)
a11, a12, a22, a33 = AI[0,0], AI[0,1], AI[1,1], AI[2,2]
self.e1 = 1./(self.t*a11)
self.e2 = 1./(self.t*a22)
self.g12 = 1./(self.t*a33)
self.nu12 = - a12 / a11
self.nu21 = - a12 / a22
[docs]
def calc_lamination_parameters(self):
"""Calculate the lamination parameters.
The following attributes are calculated:
xiA, xiB, xiD, xiE
"""
xiA1, xiA2, xiA3, xiA4 = 0, 0, 0, 0
xiB1, xiB2, xiB3, xiB4 = 0, 0, 0, 0
xiD1, xiD2, xiD3, xiD4 = 0, 0, 0, 0
xiE1, xiE2, xiE3, xiE4 = 0, 0, 0, 0
lam_thick = sum([ply.t for ply in self.plies])
self.t = lam_thick
h0 = -lam_thick/2. + self.offset
for ply in self.plies:
hk_1 = h0
h0 += ply.t
hk = h0
Afac = ply.t / lam_thick
Bfac = (2. / lam_thick**2) * (hk**2 - hk_1**2)
Dfac = (4. / lam_thick**3) * (hk**3 - hk_1**3)
Efac = (1. / lam_thick) * (hk - hk_1)# * (5./6) * (5./6)
cos2t = ply.cos2t
cos4t = ply.cos4t
sin2t = ply.sin2t
sin4t = ply.sin4t
xiA1 += Afac * cos2t
xiA2 += Afac * sin2t
xiA3 += Afac * cos4t
xiA4 += Afac * sin4t
xiB1 += Bfac * cos2t
xiB2 += Bfac * sin2t
xiB3 += Bfac * cos4t
xiB4 += Bfac * sin4t
xiD1 += Dfac * cos2t
xiD2 += Dfac * sin2t
xiD3 += Dfac * cos4t
xiD4 += Dfac * sin4t
xiE1 += Efac * cos2t
xiE2 += Efac * sin2t
xiE3 += Efac * cos4t
xiE4 += Efac * sin4t
self.xiA = np.array([1, xiA1, xiA2, xiA3, xiA4], dtype=DOUBLE)
self.xiB = np.array([0, xiB1, xiB2, xiB3, xiB4], dtype=DOUBLE)
self.xiD = np.array([1, xiD1, xiD2, xiD3, xiD4], dtype=DOUBLE)
self.xiE = np.array([1, xiE1, xiE2, xiE3, xiE4], dtype=DOUBLE)
[docs]
def calc_ABDE_from_lamination_parameters(self):
"""Use the ABDE matrix based on lamination parameters.
Given the lamination parameters ``xiA``, ``xiB``, ``xiC`` and ``xiD``,
the ABD matrix is calculated.
"""
# dummies used to unpack vector results
du1, du2, du3, du4, du5, du6 = 0, 0, 0, 0, 0, 0
# A matrix terms
A11,A22,A12, du1,du2,du3, A66,A16,A26 =\
(self.t ) * np.dot(self.matobj.u, self.xiA)
# B matrix terms
B11,B22,B12, du1,du2,du3, B66,B16,B26 =\
(self.t**2/4. ) * np.dot(self.matobj.u, self.xiB)
# D matrix terms
D11,D22,D12, du1,du2,du3, D66,D16,D26 =\
(self.t**3/12.) * np.dot(self.matobj.u, self.xiD)
# E matrix terms
du1,du2,du3, E44,E55,E45, du4,du5,du6 =\
(self.t ) * np.dot(self.matobj.u, self.xiE)
self.A = np.array([[A11, A12, A16],
[A12, A22, A26],
[A16, A26, A66]], dtype=DOUBLE)
self.B = np.array([[B11, B12, B16],
[B12, B22, B26],
[B16, B26, B66]], dtype=DOUBLE)
self.D = np.array([[D11, D12, D16],
[D12, D22, D26],
[D16, D26, D66]], dtype=DOUBLE)
# printing E acoordingly to Reddy definition for E44, E45 and E55
self.E = np.array([[E55, E45],
[E45, E44]], dtype=DOUBLE)
self.ABD = np.array([[A11, A12, A16, B11, B12, B16],
[A12, A22, A26, B12, B22, B26],
[A16, A26, A66, B16, B26, B66],
[B11, B12, B16, D11, D12, D16],
[B12, B22, B26, D12, D22, D26],
[B16, B26, B66, D16, D26, D66]], dtype=DOUBLE)
# printing ABDE acoordingly to Reddy definition for E44, E45 and E55
self.ABDE = np.array([[A11, A12, A16, B11, B12, B16, 0, 0],
[A12, A22, A26, B12, B22, B26, 0, 0],
[A16, A26, A66, B16, B26, B66, 0, 0],
[B11, B12, B16, D11, D12, D16, 0, 0],
[B12, B22, B26, D12, D22, D26, 0, 0],
[B16, B26, B66, D16, D26, D66, 0, 0],
[0, 0, 0, 0, 0, 0, E55, E45],
[0, 0, 0, 0, 0, 0, E45, E44]],
dtype=DOUBLE)
[docs]
def calc_constitutive_matrix(self):
"""Calculates the laminate constitutive matrix
This is the commonly called ``ABD`` matrix with ``shape=(6, 6)`` when
the classical laminated plate theory is used, or the ``ABDE`` matrix
when the first-order shear deformation theory is used, containing the
transverse shear terms.
"""
self.A_general = np.zeros([5,5], dtype=DOUBLE)
self.B_general = np.zeros([5,5], dtype=DOUBLE)
self.D_general = np.zeros([5,5], dtype=DOUBLE)
lam_thick = sum([ply.t for ply in self.plies])
self.t = lam_thick
h0 = -lam_thick/2 + self.offset
for ply in self.plies:
hk_1 = h0
h0 += ply.t
hk = h0
self.A_general += ply.QL*(hk - hk_1)
self.B_general += 1/2.*ply.QL*(hk**2 - hk_1**2)
self.D_general += 1/3.*ply.QL*(hk**3 - hk_1**3)
self.A = self.A_general[0:3, 0:3]
self.B = self.B_general[0:3, 0:3]
self.D = self.D_general[0:3, 0:3]
self.E = self.A_general[3:5, 3:5]
conc1 = np.concatenate([self.A, self.B], axis=1)
conc2 = np.concatenate([self.B, self.D], axis=1)
self.ABD = np.concatenate([conc1, conc2], axis=0)
self.ABDE = np.zeros((8, 8), dtype=DOUBLE)
self.ABDE[0:6, 0:6] = self.ABD
self.ABDE[6:8, 6:8] = self.E
[docs]
def force_balanced_LP(self):
r"""Force balanced lamination parameters
The lamination parameters `\xi_{A2}` and `\xi_{A4}` are set to null
to force a balanced laminate.
"""
dummy, xiA1, xiA2, xiA3, xiA4 = self.xiA
self.xiA = np.array([1, xiA1, 0, xiA3, 0], dtype=DOUBLE)
self.calc_ABDE_from_lamination_parameters()
[docs]
def force_symmetric_LP(self):
r"""Force symmetric lamination parameters
The lamination parameters `\xi_{Bi}` are set to null
to force a symmetric laminate.
"""
self.xiB = np.zeros(5)
self.calc_ABDE_from_lamination_parameters()
[docs]
def force_orthotropic(self):
r"""Force an orthotropic laminate
The terms
`A_{13}`, `A_{23}`, `A_{31}`, `A_{32}`,
`B_{13}`, `B_{23}`, `B_{31}`, `B_{32}`,
`D_{13}`, `D_{23}`, `D_{31}`, `D_{32}` are set to zero to force an
orthotropic laminate.
"""
if self.offset != 0.:
raise RuntimeError(
'Laminates with offset cannot be forced orthotropic!')
self.A[0, 2] = 0.
self.A[1, 2] = 0.
self.A[2, 0] = 0.
self.A[2, 1] = 0.
self.B[0, 2] = 0.
self.B[1, 2] = 0.
self.B[2, 0] = 0.
self.B[2, 1] = 0.
self.D[0, 2] = 0.
self.D[1, 2] = 0.
self.D[2, 0] = 0.
self.D[2, 1] = 0.
self.ABD[0, 2] = 0. # A16
self.ABD[1, 2] = 0. # A26
self.ABD[2, 0] = 0. # A61
self.ABD[2, 1] = 0. # A62
self.ABD[0, 5] = 0. # B16
self.ABD[5, 0] = 0. # B61
self.ABD[1, 5] = 0. # B26
self.ABD[5, 1] = 0. # B62
self.ABD[3, 2] = 0. # B16
self.ABD[2, 3] = 0. # B61
self.ABD[4, 2] = 0. # B26
self.ABD[2, 4] = 0. # B62
self.ABD[3, 5] = 0. # D16
self.ABD[4, 5] = 0. # D26
self.ABD[5, 3] = 0. # D61
self.ABD[5, 4] = 0. # D62
self.ABDE[0, 2] = 0. # A16
self.ABDE[1, 2] = 0. # A26
self.ABDE[2, 0] = 0. # A61
self.ABDE[2, 1] = 0. # A62
self.ABDE[0, 5] = 0. # B16
self.ABDE[5, 0] = 0. # B61
self.ABDE[1, 5] = 0. # B26
self.ABDE[5, 1] = 0. # B62
self.ABDE[3, 2] = 0. # B16
self.ABDE[2, 3] = 0. # B61
self.ABDE[4, 2] = 0. # B26
self.ABDE[2, 4] = 0. # B62
self.ABDE[3, 5] = 0. # D16
self.ABDE[4, 5] = 0. # D26
self.ABDE[5, 3] = 0. # D61
self.ABDE[5, 4] = 0. # D62
[docs]
def force_symmetric(self):
"""Force a symmetric laminate
The `B` terms of the constitutive matrix are set to zero.
"""
if self.offset != 0.:
raise RuntimeError(
'Laminates with offset cannot be forced symmetric!')
self.B = np.zeros((3,3))
self.ABD[0:3, 3:6] = 0
self.ABD[3:6, 0:3] = 0
self.ABDE[0:3, 3:6] = 0
self.ABDE[3:6, 0:3] = 0