#!/usr/bin/env python
# -*- coding: utf-8 -*-
#
import logging
import numpy as np
from .elastic_materials import ElasticMaterial
from ..tools import get_T_strain_2d, get_strain_at_z, force_symmetry
# ====================
# Set up module logger
# ====================
logger = logging.getLogger(__name__)
[docs]class FiberReinforcedPlastic(ElasticMaterial):
"""
defines a fiber reinforced material consisting of two constituents, fiber and embedding matrix
both materials need to be of a material type derived from
:class:`ElasticMaterial <material_mechanics.materials.elastic_materials.ElasticMaterial>`
:param fiber_material: fiber material
:param matrix_material: matrix material
:param fiber_volume_fraction: volume fraction of fiber material
:param name: (*optional*) material name. Default is None
:param strength: (*optional*) material strength values of the composite at the provided fiber volume ratio.
Default is None
:type fiber_material: pymat material
:type matrix_material: pymat material
:type fiber_volume_fraction: float
:type name: str
:type strength: dict of floats
Example::
>>> import material_mechanics as mm
>>>
>>> # fiber definition
>>> name = 'Carbon Fiber HT'
>>> stiffness = dict(e1=230000, e2=13000, g12=50000)
>>> poisson = dict(nu12=0.23, nu23=0.3)
>>> fiber = mm.transverse_isotropic_material(name=name, stiffness=stiffness, poisson=poisson, density=1.74)
>>>
>>> # matrix definition
>>> name = 'Epoxy Resin'
>>> matrix = mm.isotropic_material(name=name, stiffness=3200.0, poisson=0.3, density=1.2)
>>>
>>> # fiber volume content
>>> phi = 0.65
>>>
>>> # fiber reinforced material initialization
>>> frp_material = mm.FiberReinforcedPlastic(
>>> fiber_material=fiber, matrix_material=matrix, fiber_volume_fraction=phi
>>> )
>>> print(frp_material)
Name: CarbonFiberHT_EpoxyResin_65, fiber volume fraction: 0.65, Fiber: Carbon Fiber HT, Matrix: Epoxy Resin'
"""
def __init__(self, fiber_material, matrix_material, fiber_volume_fraction, *args, **kwargs):
self._fiber_material = fiber_material
self._matrix_material = matrix_material
self._fiber_volume_fraction = fiber_volume_fraction
self.name = kwargs.get('name', None)
self.symmetry = kwargs.get('symmetry', 'mean')
if self.name is None:
fvc_percent = int(round(self.fiber_volume_fraction * 100, 0))
self.name = f'{self._fiber_material.name}_{self._matrix_material.name}_{fvc_percent}'.replace(" ", "").replace("-", "")
# density
try:
self._density = fiber_material.density * fiber_volume_fraction + matrix_material.density * (
1 - fiber_volume_fraction)
except TypeError:
logger.exception('Density for {name} could not be calculated. Returning None'.format(name=self.name))
self._density = None
e1 = self._get_e1()
e2 = self._get_e2()
g12 = self._get_g12()
nu12 = self._get_nu12()
nu23 = self._get_nu23()
nu21 = e2 / e1 * nu12
g23 = e2 / (2 * (1 + nu23))
stiffnesses = [(e1, g23), (e2, g12), (e2, g12)]
poissons = [(nu23, nu23), (nu12, nu21), (nu12, nu21)]
strength_dict = kwargs.get('strength', None)
if strength_dict is not None:
_strengths = [
(strength_dict.get('r_11_tensile', None), strength_dict.get('r_11_compression', None),
strength_dict.get('r_23', None), strength_dict.get('r_23', None)),
(strength_dict.get('r_22_tensile', None), strength_dict.get('r_22_compression', None),
strength_dict.get('r_12', None), strength_dict.get('r_21', None)),
(strength_dict.get('r_22_tensile', None), strength_dict.get('r_22_compression', None),
strength_dict.get('r_12', None), strength_dict.get('r_21', None)),
]
else:
_strengths = None
super(FiberReinforcedPlastic, self).__init__(
stiffness=stiffnesses, poisson=poissons, strength=_strengths, symmetry=self.symmetry, name=self.name
)
# def __str__(self):
# return self.name
def __eq__(self, other):
if not isinstance(other, FiberReinforcedPlastic):
return False
ret = [
self.fiber_material == other.fiber_material,
self.matrix_material == other.matrix_material,
self.fiber_volume_fraction == other.fiber_volume_fraction
]
return all(ret)
def __str__(self):
ret_string = f'Name: {self.name}, fiber volume fraction: {self.fiber_volume_fraction}, ' \
f'Fiber: {self.fiber_material.name}, Matrix: {self.matrix_material.name}'
return ret_string
@property
def matrix_material(self):
"""
return the matrix material of the fiber reinforced material
:return: matrix material
:rtype: ElasticMaterial or derived class
"""
return self._matrix_material
@property
def fiber_material(self):
"""
return the fiber material of the fiber reinforced material
:return: fiber material
:rtype: ElasticMaterial or derived class
"""
return self._fiber_material
@property
def fiber_volume_fraction(self):
"""
return the fiber volume ratio of the fiber reinforced material
:return: fiber volume ratio
:rtype: float
"""
return self._fiber_volume_fraction
def _get_e1(self):
"""
Return the longitudinal stiffness :math:'E_1' of the fiber reinforced material
:return: Stiffness of the fiber reinforced material in 1-direction
:rtype: float
"""
return self._fiber_material.get_stiffness(1) * self._fiber_volume_fraction + self._matrix_material.get_stiffness() * (1. - self._fiber_volume_fraction)
def _get_e2(self, **kwargs):
"""
Return the perpendicular stiffness :math:'E_2' of the fiber reinforced material
Two methods for the calculation of :math:'E_2' are implemented, a micro mechanical and a semi-empiric
approach, both described by Schürmann [Sch07]_. According to the Literature, the semi empiric approach gives
results closer to experimental test results.
:param method: (*optional*) method to use to calculate :math:`E_2`. Defaults to 'empiric'
Options:
- 'empiric' semi-empiric approach
- 'default': micro-mechanical approach
:param contraction_hindrance: set if contraction hindrance is considered in the calculation. Default is True
:type method: str
:type contraction_hindrance: bool
:return: perpendicular stiffness :math:'E_2' of the composite
:rtype: float
"""
phi = self._fiber_volume_fraction
eMatrix = self._matrix_material.get_stiffness()
nuMatrix = self._matrix_material.get_poisson(12)
e2Fiber = self._fiber_material.get_stiffness(22)
method = kwargs.get('method', 'empiric')
contraction_hinderance = kwargs.get('contraction_hindrance', True)
if method == 'empiric':
term2 = 1. + 0.85 * phi ** 2
exponent = 1.25
else:
term2 = 1.
exponent = 1.0
if contraction_hinderance:
term1 = eMatrix / (1. - nuMatrix ** 2)
else:
term1 = eMatrix
term3 = (1. - phi) ** exponent
term4 = term1 * phi / e2Fiber
e2 = term1 * term2 / (term3 + term4)
return e2
def _get_g12(self, **kwargs):
"""
Return the shear modulus :math:`G_12` of the composite using semi empirical relations introduced by "Förster"
:param method: (*optional*) method to use to calculate :math:`G_12`. Default is 'foerster'
Options:
- 'default' miro-machanical approach
- 'foerster': semi-empiric approach intoduced by 'Förster'
:type method: str
:return: shear modulus :math:`G_21` of the composite
:rtype: float
"""
phi = self._fiber_volume_fraction
gMatrix = self.matrix_material.get_stiffness(12)
g21Fiber = self.fiber_material.get_stiffness(12)
method = kwargs.get('method', 'foerster')
if method == 'foerster':
term1 = 1 + 0.4 * np.power(phi, 0.5)
term2 = np.power((1 - phi), 1.45)
term3 = (gMatrix / g21Fiber) *phi
g21 = gMatrix * term1 / (term2 + term3)
else:
g21 = gMatrix / (1. - phi + phi * gMatrix / g21Fiber)
return g21
def _get_nu12(self):
"""
Return the Poisson Ratio :math:`\nu_{12}` of the fiber reinforced material using the mixing relation
:return: Poisson Ratio :math:`\nu_{12}` of the fiber reinforced material
:rtype: float
"""
phi = self._fiber_volume_fraction
nuMatrix = self._matrix_material.get_poisson(12)
nu12Fiber = self._fiber_material.get_poisson(12)
nu21 = (nu12Fiber * phi + nuMatrix * (1. - phi))
return nu21
def _get_nu23(self):
"""
Return the Poisson Ratio :math:`\nu_{23}` of the fiber reinforced material using the relation introduced by
'Foye'
:return: Poisson Ratio :math:`\nu_{23}` of the fiber reinforced material
:rtype: float
"""
phi = self._fiber_volume_fraction
nuMatrix = self._matrix_material.get_poisson(12)
eMatrix = self._matrix_material.get_stiffness()
nu32Fiber = self._fiber_material.get_poisson(32)
nu21 = self._get_nu12()
e1 = self._get_e1()
term1 = 1. + nuMatrix - nu21 * eMatrix / e1
term2 = 1. - nuMatrix ** 2 + nuMatrix * nu21 * eMatrix / e1
nuMatrixEffektiv = nuMatrix * term1 / term2
nu32 = phi * nu32Fiber + (1 - phi) * nuMatrixEffektiv
return nu32
[docs]class Laminate(object):
"""
Creates a laminate object with an empty stacking
.. note::
a factory class for creating laminates exists and it's use for creating laminates is encouraged
(see :class:`StandardLaminateFactory <material_mechanics.materials.material_factories.StandardLaminateFactory>`)
:param name: (*optional*) name of the laminate, default is None
:param layer_stiffness_symmetry: (*optional*) sets the method of enforcement of symmetry in the layer materials
stiffness matrix. For details on the algorithm for symmetry enforcement please see
:func:`force_symmetry() <material_mechanics.tools.functions.force_symmetry>`. Default is 'upper'.
Options: ('mean', 'upper', 'lower', None)
:type name: str
:type layer_stiffness_symmetry: str or None
Example::
>>> import material_mechanics as mm
>>>
>>> # fiber definition
>>> name = 'Carbon Fiber HT'
>>> stiffness = dict(e1=230000, e2=13000, g12=50000)
>>> poisson = dict(nu12=0.23, nu23=0.3)
>>> fiber = mm.transverse_isotropic_material(name=name, stiffness=stiffness, poisson=poisson, density=1.74)
>>>
>>> # matrix definition
>>> name = 'Epoxy Resin'
>>> matrix = mm.isotropic_material(name=name, stiffness=3200.0, poisson=0.3, density=1.2)
>>>
>>> # fiber volume content
>>> phi = 0.65
>>>
>>> # fiber reinforced material initialization
>>> frp_material = mm.FiberReinforcedPlastic(
>>> fiber_material=fiber, matrix_material=matrix, fiber_volume_fraction=phi
>>> )
>>>
>>> lam_fzb = mm.Laminate()
>>> lam_fzb.add_layer(mm.Layer(material=frp_material, thickness=0.25, orientation=45.))
>>> lam_fzb.add_layer(mm.Layer(material=frp_material, thickness=0.25, orientation=90.))
>>> lam_fzb.add_layer(mm.Layer(material=frp_material, thickness=0.25, orientation=135.))
>>> lam_fzb.add_layer(mm.Layer(material=frp_material, thickness=0.25, orientation=0.))
>>> lam_fzb.add_layer(mm.Layer(material=frp_material, thickness=0.25, orientation=0.))
>>> lam_fzb.add_layer(mm.Layer(material=frp_material, thickness=0.25, orientation=135.))
>>> lam_fzb.add_layer(mm.Layer(material=frp_material, thickness=0.25, orientation=90.))
>>> lam_fzb.add_layer(mm.Layer(material=frp_material, thickness=0.25, orientation=45.))
"""
def __init__(self, *args, **kwargs):
self.name = kwargs.get('name', None)
self._layer_symmetry = kwargs.get('layer_stiffness_symmetry', 'upper')
self.stacking = list()
def __str__(self):
if len(self.stacking) > 0:
return '\n'.join([f'{i+1} - {str(layer)}' for i, layer in enumerate(self.stacking)])
else:
return 'This is an empty laminate'
[docs] def add_layer(self, layer, *args, **kwargs):
"""
add a layer to the laminate
:param layer: layer to add to the end of the laminate
:type layer: Layer
:return: None
Example::
>>> import material_mechanics as mm
>>>
>>> # fiber definition
>>> name = 'Carbon Fiber HT'
>>> stiffness = dict(e1=230000, e2=13000, g12=50000)
>>> poisson = dict(nu12=0.23, nu23=0.3)
>>> fiber = mm.transverse_isotropic_material(name=name, stiffness=stiffness, poisson=poisson, density=1.74)
>>>
>>> # matrix definition
>>> name = 'Epoxy Resin'
>>> matrix = mm.isotropic_material(name=name, stiffness=3200.0, poisson=0.3, density=1.2)
>>>
>>> # fiber volume content
>>> phi = 0.65
>>>
>>> # fiber reinforced material initialization
>>> frp_material = mm.FiberReinforcedPlastic(
>>> fiber_material=fiber, matrix_material=matrix, fiber_volume_fraction=phi
>>> )
>>>
>>> lam_fzb = mm.Laminate()
>>> lam_fzb.add_layer(mm.Layer(name='my_layer', material=frp_material, thickness=0.25, orientation=45.))
>>> print(lam_fzb)
1 - Name: my_layer, Material: CarbonFiberHT_EpoxyResin_65, Thickness: 0.25, Orientation: 45.0'
"""
self.stacking.append(layer)
@property
def density(self):
r"""
return the materials density
:return: density in :math:`\frac{g}{cm^3}`
:rtype: float
"""
cummulative_density = 0.0
for layer_ in self.stacking:
cummulative_density += layer_.material.density * layer_.thickness
return cummulative_density / self.thickness
@property
def thickness(self):
r"""
Return the laminates thickness
:return: laminate thickness in :math:`mm`
:rtype: float
"""
return sum([l.thickness for l in self.stacking])
@property
def area_weight(self):
r"""
return the laminates area weight
:return: laminate area weight in :math:`\frac{g}{cm^2}`
:rtype: float
"""
return sum([l.get_area_weight for l in self.stacking])
@property
def z_values(self):
"""
return the lower and top coordinate of each layer
:return: List of tupels. Each tuple holds the upper an lower coordinate of the layer in reference to the
laminate central plane
:rtype: list of tuples
"""
z_values = list()
for layer in self.stacking:
if len(z_values) == 0:
z_lower = -self.thickness * 0.5
z_upper = z_lower + layer.thickness
else:
z_lower = z_upper
z_upper = z_lower + layer.thickness
z_values.append((z_lower, z_upper))
return z_values
@property
def abd_matrix(self):
"""
return the abd Matrix of the Laminate using classical lamination theory
:return: stiffness matrix of the combined shell and plate element
:rtype: numpy.ndarray
"""
abd = np.concatenate(
(np.concatenate((self.a_matrix, self.b_matrix), axis=1),
np.concatenate((self.b_matrix, self.d_matrix), axis=1)), axis=0)
return abd
@property
def a_matrix(self):
"""
return the shell stiffness matrix of the laminate as defined in the classical lamination theory
:return: shell stiffness matrix
:rtype: numpy.ndarray
"""
matrices = [layer.stiffness_matrix(symmetry=self._layer_symmetry) * layer.thickness for layer in self.stacking]
return np.sum(matrices, axis=0)
@property
def b_matrix(self):
"""
Return the coupling matrix of the laminate as defined in the classical lamination theory
:return: coupling matrix
:rtype: numpy.ndarray
"""
matrices = [layer.stiffness_matrix(symmetry=self._layer_symmetry) * (zVals[1] ** 2 - zVals[0] ** 2) for layer, zVals in zip(self.stacking, self.z_values)]
return 0.5 * np.sum(matrices, axis=0)
@property
def d_matrix(self):
"""
calculates the plate stiffness matrix of the laminate as defined in the classical lamination theory
:return: plate stiffness matrix
:rtype: numpy.ndarray
"""
matrices = [layer.stiffness_matrix(symmetry=self._layer_symmetry) * (zVals[1] ** 3 - zVals[0] ** 3) for layer, zVals in zip(self.stacking, self.z_values)]
return np.sum(matrices, axis=0) / 3.
@property
def layer_count(self):
"""
Return the number of layers in the laminate
:return: number of layers
:rtype: int
"""
return len(self.stacking)
[docs] def get_strains(self, line_loads):
r"""
return the global laminate strains resulting from a planar external loading.
The laminate strains are calculated through the laminates :math:`ABD` matrix. The external load is defined by
the line load vector :math:`[n_x, n_y, n_{xy}, m_x, m_y, m_{xy}]`. The line loads :math:`n` are given in
:math:`\frac{N}{mm}` and the line moments :math:`m` in :math:`N`
:param line_loads: line load vector
:type line_loads: numpy.ndarray
:return: laminate strains
:rtype: numpy.ndarray
Example::
>>> import material_mechanics as mm
>>>
>>> # fiber definition
>>> name = 'Carbon Fiber HT'
>>> stiffness = dict(e1=230000, e2=13000, g12=50000)
>>> poisson = dict(nu12=0.23, nu23=0.3)
>>> fiber = mm.transverse_isotropic_material(name=name, stiffness=stiffness, poisson=poisson, density=1.74)
>>>
>>> # matrix definition
>>> name = 'Epoxy Resin'
>>> matrix = mm.isotropic_material(name=name, stiffness=3200.0, poisson=0.3, density=1.2)
>>>
>>> # fiber volume content
>>> phi = 0.65
>>>
>>> # fiber reinforced material initialization
>>> frp_material = mm.FiberReinforcedPlastic(
>>> fiber_material=fiber, matrix_material=matrix, fiber_volume_fraction=phi
>>> )
>>>
>>> lam_fzb = mm.Laminate()
>>> lam_fzb.add_layer(mm.Layer(material=frp_material, thickness=0.25, orientation=45.))
>>> lam_fzb.add_layer(mm.Layer(material=frp_material, thickness=0.25, orientation=90.))
>>> lam_fzb.add_layer(mm.Layer(material=frp_material, thickness=0.25, orientation=135.))
>>> lam_fzb.add_layer(mm.Layer(material=frp_material, thickness=0.25, orientation=0.))
>>> lam_fzb.add_layer(mm.Layer(material=frp_material, thickness=0.25, orientation=0.))
>>> lam_fzb.add_layer(mm.Layer(material=frp_material, thickness=0.25, orientation=135.))
>>> lam_fzb.add_layer(mm.Layer(material=frp_material, thickness=0.25, orientation=90.))
>>> lam_fzb.add_layer(mm.Layer(material=frp_material, thickness=0.25, orientation=45.))
>>>
>>> line_loads = np.array([100, 50, 10, 20, 5, 3])
>>>
>>> lam_fzb.get_strains(line_loads=line_loads)
array([ 0.00071862, 0.00017637, 0.0002169 , 0.00100021, -0.00015305, -0.0003249 ])
"""
return np.dot(np.linalg.inv(self.abd_matrix), line_loads)
[docs] def get_layer_strains(self, laminate_strains):
r"""
return the strains in the layers of the laminate resulting from an external planar loading of the laminate
The laminate strains are :math:`[\varepsilon_x, \varepsilon_y, \gamma_{xy}, \kappa_x, \kappa_y, \kappa_{xy}]`
and are the resulting global laminate strains from an external loading of the laminate, calculated by the
classical laminate theory (CLT). This step is done in the
:func:`get_strains() <material_mechanics.materials.composites.Laminate.get_strains>`
:param laminate_strains: global laminate strains
:return: strains of the layers at the bottom and the top of each layer
:rtype: list of lists of numpy.ndarray
Example::
>>> import material_mechanics as mm
>>>
>>> # fiber definition
>>> name = 'Carbon Fiber HT'
>>> stiffness = dict(e1=230000, e2=13000, g12=50000)
>>> poisson = dict(nu12=0.23, nu23=0.3)
>>> fiber = mm.transverse_isotropic_material(name=name, stiffness=stiffness, poisson=poisson, density=1.74)
>>>
>>> # matrix definition
>>> name = 'Epoxy Resin'
>>> matrix = mm.isotropic_material(name=name, stiffness=3200.0, poisson=0.3, density=1.2)
>>>
>>> # fiber volume content
>>> phi = 0.65
>>>
>>> # fiber reinforced material initialization
>>> frp_material = mm.FiberReinforcedPlastic(
>>> fiber_material=fiber, matrix_material=matrix, fiber_volume_fraction=phi
>>> )
>>>
>>> lam_fzb = mm.Laminate()
>>> lam_fzb.add_layer(mm.Layer(material=frp_material, thickness=0.25, orientation=45.))
>>> lam_fzb.add_layer(mm.Layer(material=frp_material, thickness=0.25, orientation=90.))
>>> lam_fzb.add_layer(mm.Layer(material=frp_material, thickness=0.25, orientation=135.))
>>> lam_fzb.add_layer(mm.Layer(material=frp_material, thickness=0.25, orientation=0.))
>>> lam_fzb.add_layer(mm.Layer(material=frp_material, thickness=0.25, orientation=0.))
>>> lam_fzb.add_layer(mm.Layer(material=frp_material, thickness=0.25, orientation=135.))
>>> lam_fzb.add_layer(mm.Layer(material=frp_material, thickness=0.25, orientation=90.))
>>> lam_fzb.add_layer(mm.Layer(material=frp_material, thickness=0.25, orientation=45.))
>>>
>>> line_loads = np.array([100, 50, 10, 20, 5, 3])
>>>
>>> laminate_strains = lam_fzb.get_strains(line_loads=line_loads)
>>> layer_strains = fzb_lam.get_layer_strains(laminate_strains=laminate_strains)
>>>
>>> for i, ls in enumerate(layer_strains):
>>> print(f'Layer {i+1}\nBottom strain:{ls[0]}, Top strain:{ls[1]}')
Layer 1
Bottom strain:[ 0.00029482 -0.00024698 0.000611 ], Top strain:[ 0.0003601 -0.00010048 0.00032269]
Layer 2
Bottom strain:[ 2.91153430e-04 -3.15320675e-05 -4.60576048e-04], Top strain:[ 0.00025289 0.00021852 -0.00037935]
Layer 3
Bottom strain:[ 4.60294158e-05 4.25381385e-04 -3.43704880e-05], Top strain:[0.00019254 0.00049066 0.00025394]
Layer 4
Bottom strain:[0.00046857 0.00021463 0.00029813], Top strain:[0.00071862 0.00017637 0.0002169 ]
Layer 5
Bottom strain:[0.00071862 0.00017637 0.0002169 ], Top strain:[0.00096868 0.0001381 0.00013568]
Layer 6
Bottom strain:[0.00048555 0.00062123 0.00083057], Top strain:[0.00063206 0.00068651 0.00111889]
Layer 7
Bottom strain:[ 9.98395044e-05 1.21872905e-03 -5.44556546e-05], Top strain:[6.15767194e-05 1.46878128e-03 2.67684241e-05]
Layer 8
Bottom strain:[ 0.00075179 0.00077856 -0.0014072 ], Top strain:[ 0.00081708 0.00092507 -0.00169552]
"""
layer_vals = list()
for l, z_vals in zip(self.stacking, self.z_values):
layer_val = list()
for z in z_vals:
layer_val.append(np.dot(get_T_strain_2d(np.pi * l.orientation / 180.), get_strain_at_z(laminate_strains, z)))
layer_vals.append(tuple(layer_val))
return layer_vals
[docs]class Layer(object):
"""
Layer class to be used in the Laminate class
:param material: The material of the layer
:param thickness: thickness of the layer in :math:`mm`
:param orientation: orientation of the layer in :math:`degrees`
:param name: (*optional*) name to reference the layer by
:type material: ElasticMaterial or derived class
:type thickness: float
:type orientation: float
:type name: str or None
Example::
>>> import material_mechanics as mm
>>>
>>> # fiber definition
>>> name = 'Carbon Fiber HT'
>>> stiffness = dict(e1=230000, e2=13000, g12=50000)
>>> poisson = dict(nu12=0.23, nu23=0.3)
>>> fiber = mm.transverse_isotropic_material(name=name, stiffness=stiffness, poisson=poisson, density=1.74)
>>>
>>> # matrix definition
>>> name = 'Epoxy Resin'
>>> matrix = mm.isotropic_material(name=name, stiffness=3200.0, poisson=0.3, density=1.2)
>>>
>>> # fiber volume content
>>> phi = 0.65
>>>
>>> # fiber reinforced material initialization
>>> frp_material = mm.FiberReinforcedPlastic(
>>> fiber_material=fiber, matrix_material=matrix, fiber_volume_fraction=phi
>>> )
>>> layer = mm.Layer(name='my_layer',material=frp_material, thickness=0.25, orientation=45.)
>>> print(layer)
Name: my_layer, Material: CarbonFiberHT_EpoxyResin_65, Thickness: 0.25, Orientation: 45.0
"""
def __init__(self, material, thickness, orientation, *args, **kwargs):
self._material = material
self._thickness = thickness
self._orientation = orientation
self._ply_name = kwargs.get('name', None)
def __str__(self):
ret_str = f'Name: {self._ply_name}, Material: {self._material.name}, Thickness: {self._thickness}, ' \
f'Orientation: {self._orientation}'
return ret_str
@property
def material(self):
"""
return layer material
:return: layer material
:rtype: ElasticMaterial or derived class
"""
return self._material
@property
def thickness(self):
"""
return layer thickness
:return: layer thickness in :math:`mm`
:rtype: float
"""
return self._thickness
@property
def orientation(self):
"""
return layer orientation
:return: layer orientation in :math:`degree`
:rtype: float
"""
return self._orientation
@property
def name(self):
"""
return the name of the layer
:return: layer name
:rtype: str
"""
return self._ply_name
@name.setter
def name(self, name):
"""
Set the name of the layer
:param name: new layer name
:type name: str
"""
assert isinstance(name, str)
self._ply_name = name
@property
def area_weight(self):
r"""
return the area weight
:return: area weight of the layer in :math:`\frac{g}{cm^2}`
:rtype: float
"""
return self._material.density * self._thickness * 0.1
[docs] def compliance_matrix(self, *args, **kwargs):
"""
calculate the compliance matrix of the layer in the laminate coordinate system (i.e. in the 0° direction)
:return: layer compliance matrix
:rtype: numpy.ndarray
"""
symmetry = kwargs.get('symmetry', None)
c = np.cos(self._orientation * np.pi / 180.)
s = np.sin(self._orientation * np.pi / 180.)
c2 = np.cos(2. * self._orientation * np.pi / 180.)
s2 = np.sin(2. * self._orientation * np.pi / 180.)
t_sigma_xy12 = np.array([
[c ** 2, s ** 2, s2],
[s ** 2, c ** 2, -s2],
[-.5 * s2, .5 * s2, c2]])
t_epsilon_12xy = np.array([
[c ** 2, s ** 2, -.5 * s2],
[s ** 2, c ** 2, .5 * s2],
[s2, -s2, c2]])
material_compliance_matrix = force_symmetry(self._material.compliance_matrix_2d, symmetry=symmetry)
return np.matmul(t_epsilon_12xy, np.matmul(material_compliance_matrix, t_sigma_xy12))
[docs] def stiffness_matrix(self, *args, **kwargs):
"""
calculates the stiffness matrix of the layer in the laminate coordinate system
:return: layer stiffness matrix
:rtype: numpy.ndarray
"""
symmetry = kwargs.get('symmetry', None)
return np.linalg.inv(self.compliance_matrix(symmetry=symmetry))