Laminates exposed to loading beyond failure of individual layers will usually have a significant residual strength. The typical mechanisms involve progressive matrix dominated failure and subsequent redistribution of loads to layers having fibers alligned with the loads.
A simple example introduces the concept:
Assume a [0/90/90/0] laminates subjected to tensile loading $N_x$.
import numpy as np
from laminatelib import laminateStiffnessMatrix, layerResults
from plotlib import plotLayerStresses
import matplotlib.pyplot as plt
%matplotlib inline
m ={'E1':40000, 'E2':10000, 'v12':0.3, 'G12':5000,
'XT':1000, 'XC':800, 'YT':50, 'YC':150, 'S12':75, 'f12':-0.5, 'S23':50}
lup1=[{ 'mat':m , 'ori': 0 , 'thi':1 },
{ 'mat':m , 'ori': 90 , 'thi':1 },
{ 'mat':m , 'ori': 90 , 'thi':1 },
{ 'mat':m , 'ori': 0 , 'thi':1 }]
ABD=laminateStiffnessMatrix(lup1)
abd=np.linalg.inv(ABD)
load=(100,0,0,0,0,0)
defo=np.dot(abd,load)
res=layerResults(lup1,defo)
plotLayerStresses(res)
Using the plane stress Hashin criterion and computing the inter fiber failure (IFF) exposure factors for the layers:
print( res[0]['fail']['HNIFF'])
print( res[1]['fail']['HNIFF'])
print( res[2]['fail']['HNIFF'])
print( res[3]['fail']['HNIFF'])
The 90-layers are obviously most exposed with respect to matrix dominated, or inter fiber failure (0.1956), as illustrated below:
from plotlib import plotHashin
plotHashin(res)
Computing the failure load by dividing the previously applied load by the worst case exposure factor:
Nx_1 = 100/0.19561688311688313
print(Nx_1)
Now we can run a new solution using this value of the $N_x$:
ABD=laminateStiffnessMatrix(lup1)
abd=np.linalg.inv(ABD)
load=(Nx_1,0,0,0,0,0)
defo=np.dot(abd,load)
res=layerResults(lup1,defo)
plotHashin(res)
The midplane strain $\varepsilon_x^0$ at this loading found by:
ex0_1 = defo[0]
print(ex0_1)
The failure of the 90-layers will typically appare as matrix cracking running through the thickness of the layers:
Let us assume that the consequence is a reduction of all the matrix dominated elastic properties of these two layers, including the transverse stiffness $E_2$ and the shear modulus $G_{12}$. We can illustrate that by creating a new material ($m_{90}$) degraded by 99% (that is, only 1% of original stiffness remains).
The concept of a degradation factor $d$ in the range 0...1 is commonly used in this framework as illustrated in the code below.
The case study Elastic degradation of laminates provides an idea of realistic degradation factors based on FEA results.
d=0.99
m90 ={'E1':40000, 'E2':10000*(1-d), 'v12':0.3, 'G12':5000*(1-d),
'XT':1000, 'XC':800, 'YT':50, 'YC':150, 'S12':75, 'f12':-0.5, 'S23':50}
lup1=[{ 'mat':m , 'ori': 0 , 'thi':1 },
{ 'mat':m90, 'ori':90 , 'thi':1 },
{ 'mat':m90, 'ori':90 , 'thi':1 },
{ 'mat':m , 'ori': 0 , 'thi':1 }]
Now we must obtaine a new solution for the degraded laminate using the loading at failure. Just for the record, we may define a new parameter for the loading at this stage, being equal to the failure loading:
Nx_2 = Nx_1
print(Nx_2)
New solution and evaluation of the exposure factors:
ABD=laminateStiffnessMatrix(lup1)
abd=np.linalg.inv(ABD)
load=(Nx_2,0,0,0,0,0)
defo=np.dot(abd,load)
res=layerResults(lup1,defo)
plotHashin(res)
The strain is now:
ex0_2 = defo[0]
print(ex0_2)
Based on the exposure factors at the current loading on the degraded laminate,
print( res[0]['fail']['HNIFF'])
print( res[1]['fail']['HNIFF'])
print( res[2]['fail']['HNIFF'])
print( res[3]['fail']['HNIFF'])
we can make a prediction for the next failure load. At this stage, the 0-layers are most exposed, and the next failure load becomes
Nx_3=Nx_2/0.3052470136503229
print(Nx_3)
Solution for this load is
load=(Nx_3,0,0,0,0,0)
defo=np.dot(abd,load)
res=layerResults(lup1,defo)
plotHashin(res)
where the strain now is
ex0_3 = defo[0]
print(ex0_3)
Now we shall introduce a degradation of the 0-oriented layers as well:
d=0.99
m0 ={'E1':40000, 'E2':10000*(1-d), 'v12':0.3, 'G12':5000*(1-d),
'XT':1000, 'XC':800, 'YT':50, 'YC':150, 'S12':75, 'f12':-0.5, 'S23':50}
lup1=[{ 'mat':m0 , 'ori': 0 , 'thi':1 },
{ 'mat':m90, 'ori':90 , 'thi':1 },
{ 'mat':m90, 'ori':90 , 'thi':1 },
{ 'mat':m0 , 'ori': 0 , 'thi':1 }]
Nx_4 = Nx_3
ABD=laminateStiffnessMatrix(lup1)
abd=np.linalg.inv(ABD)
load=(Nx_4,0,0,0,0,0)
defo=np.dot(abd,load)
res=layerResults(lup1,defo)
plotHashin(res)
The strain is now
ex0_4 = defo[0]
print(ex0_4)
There are still some load capacity since the exposure factors for fiber failure (FF) is less than 1.0. The exact numbers are:
print( res[0]['fail']['HNFF']['bot'])
print( res[1]['fail']['HNFF']['bot'])
print( res[2]['fail']['HNFF']['bot'])
print( res[3]['fail']['HNFF']['bot'])
Hence, the ultimate failure load is
Nx_5 = Nx_4/0.8352728197805782
print(Nx_5)
load=(Nx_5,0,0,0,0,0)
defo=np.dot(abd,load)
res=layerResults(lup1,defo)
plotHashin(res)
The ultimate failure strain is
ex0_5 = defo[0]
print(ex0_5)
the load-strain curve, along with the initial linear realation, is:
ex0= (0, ex0_1, ex0_2, ex0_3, ex0_4, ex0_5)
Nx=(0, Nx_1, Nx_2, Nx_3, Nx_4, Nx_5)
fig,ax = plt.subplots(ncols=1,nrows=1,figsize=(8,6))
ax.grid(True)
ax.plot(ex0,Nx,'-', color='black')
ax.plot( (0, 2000*ex0_1/Nx_1) , (0, 2000), '--', color = 'gray')
ax.set_xlabel('$\epsilon_x^0$',fontsize=14)
ax.set_ylabel('$N_x$', fontsize=14)
plt.tight_layout()
plt.show()
m ={'E1':40000, 'E2':10000, 'v12':0.3, 'G12':5000,
'XT':1000, 'XC':800, 'YT':50, 'YC':150, 'S12':75, 'f12':-0.5, 'S23':50}
lup1=[{ 'mat':m , 'ori': 0 , 'thi':1 },
{ 'mat':m , 'ori': 90 , 'thi':1 },
{ 'mat':m , 'ori': 90 , 'thi':1 },
{ 'mat':m , 'ori': 0 , 'thi':1 }]
from magicdamage import progressiveDamageNx
ex0_a,Nx_a = progressiveDamageNx(layup=lup1,increment=1,degfactor=0.00, maxNx=3000)
ex0_b,Nx_b = progressiveDamageNx(layup=lup1,increment=1,degfactor=0.10, maxNx=3000)
ex0_c,Nx_c = progressiveDamageNx(layup=lup1,increment=1,degfactor=0.99, maxNx=3000)
fig,ax = plt.subplots(nrows=1,ncols=1, figsize = (8,6))
ax.plot(ex0_a,Nx_a,'-',color='black',label='degfact=0.00')
ax.plot(ex0_b,Nx_b,'-',color='blue',label='degfact=0.10')
ax.plot(ex0_c,Nx_c,'-',color='red',label='degfact=0.99')
ax.grid(True)
ax.legend(loc='best',prop={'size': 12})
ax.set_xlabel(r'$\varepsilon_x^0}$',size=14)
ax.set_ylabel('$N_x$',size=14)
ax.set_xlim(0,)
ax.set_ylim(0,)
plt.tight_layout()
plt.show()