├── .gitignore ├── mechpy ├── units.bat ├── __init__.py ├── design.py ├── dynamics.py ├── units.py ├── math.py ├── statics.py ├── fem.py └── composites.py ├── setup.py ├── LICENSE.txt └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | .ipynb_checkpoints 2 | __pycache__ 3 | mechpy/__pycache__ 4 | *.pyc -------------------------------------------------------------------------------- /mechpy/units.bat: -------------------------------------------------------------------------------- 1 | :: set mypath=%cd% 2 | :: set file1=\units.py 3 | :: set "filepath=%mypath%%file1%" 4 | @python.exe %~dp0\units.py %* -------------------------------------------------------------------------------- /mechpy/__init__.py: -------------------------------------------------------------------------------- 1 | # mechpy: A Python Package for Mechanical Engineers 2 | 3 | __author__ = 'Neal Gordon' 4 | __version__ = '0.01' 5 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | from distutils.core import setup 2 | 3 | setup( 4 | name='mechpy', 5 | version='0.01', 6 | description="A Python package for mechanical engineers", 7 | author='Neal Gordon', 8 | author_email='nealagordon@gmail.com', 9 | packages=['mechpy'], 10 | license="The MIT License (MIT)", 11 | long_description=open('README.md').read(), 12 | url='https://github.com/nagordon/mechpy', 13 | keywords = ['composites', 'mechanics', 'statics', 'materials'], 14 | classifiers = [ 15 | "Programming Language :: Python", 16 | "Programming Language :: Python :: 3.4", 17 | "License :: The MIT License (MIT)", 18 | "Operating System :: OS Independent", 19 | "Intended Audience :: Science/Research", 20 | "Topic :: Scientific/Engineering", 21 | "Development Status :: 2 - Pre-Alpha" 22 | ], 23 | install_requires=['numpy', 'matplotlib', 'scipy','sympy','pint','python-quantities'], 24 | ) -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Neal Gordon 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /mechpy/design.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | ''' 3 | scripts and boilerplate code to use for mechanical engineering design tasks 4 | ''' 5 | 6 | import pandas as pd 7 | import numpy as np 8 | from numpy import pi, array 9 | import matplotlib.pyplot as plt 10 | 11 | def gear(): 12 | ''' 13 | Plotting of the Involute function applied to gear 14 | design (adapated from http://www.arc.id.au/GearDrawing.html) 15 | Transcendental Parametric function describing the contour of the gear face 16 | ''' 17 | 18 | th1 = np.pi/4 19 | th2 = np.pi/3 20 | thcir = np.linspace(-np.pi,np.pi,100) 21 | th = np.linspace(th1,th2,100) 22 | Rb = 0.5 23 | x = Rb*(np.sin(th)+np.cos(th)) 24 | y = Rb*(np.sin(th)-np.cos(th)) 25 | xcir = np.sin(thcir) 26 | ycir = np.cos(thcir) 27 | 28 | ofst = 0.05 29 | y = max(ycir)+y 30 | x = x-min(x)+ofst 31 | 32 | plt.plot(x,y) 33 | plt.plot(-x,y) 34 | plt.plot([-ofst , ofst],[max(y) , max(y)] ) 35 | 36 | plt.plot(xcir,ycir,'--') 37 | plt.show() 38 | 39 | 40 | def fastened_joint(fx, fy, P, l): 41 | '''computes stressed in fastened joints with bolts or rivets 42 | INCOMPLETE 43 | 44 | # fastener location 45 | fx = array([0,1,2,3,0,1,2,3]) 46 | fy = array([0,0,0,0,1,1,1,1]) 47 | # Force(x,y) 48 | P = array([-300,-500]) 49 | l = [2,1] 50 | 51 | ''' 52 | 53 | fn = range(len(fx)) 54 | 55 | df = pd.DataFrame() 56 | 57 | Pnorm = P/np.max(np.abs(P)) # for plotting 58 | # Location of Force P, x,y 59 | 60 | d = array(5/16*np.ones(len(fn))) 61 | 62 | A = array([pi*d1**2/4 for d1 in d]) 63 | 64 | fn = range(len(fx)) 65 | 66 | df = pd.DataFrame({ 'Fastener' : fn, 67 | 'x' : fx, 68 | 'y' : fy}, index=fn) 69 | 70 | df['x^2'] = df.x**2 71 | df['y^2'] = df.y**2 72 | df['xbar'] = np.sum(A*fx)/np.sum(A) 73 | df['ybar'] = np.sum(A*fy)/np.sum(A) 74 | return df 75 | 76 | 77 | def mohr(s): 78 | pass 79 | 80 | 81 | 82 | if __name__=='__main__': 83 | shear_bending() -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | mechpy - a mechanical engineer's python toolbox 2 | 3 | Tutorials - see the [nbviewer for mechpy](http://nbviewer.jupyter.org/github/nagordon/mechpy/blob/master/mechpy.ipynb) 4 | 5 | Mechpy was created for two reasons. 6 | * To provide the practicing engineer with applications to quickly replicate and solve real-world systems common in mechanical engineering 7 | * To give the engineering student a code baes from which to suppliment learning through hand-calculations and an easy way to check work. 8 | 9 | There are many different tools available to engineers. Hand-calcsulations, spreadsheets, and code are all great ways to perform calculations or visualize data or concepts. MATLAB is the defacto tool to solve many engineering calulations, but is just too expensive to be a practical tool. Octave, Scilab, or Freelab are great alternatives, but is limited in scope to calculation. I began using python for calculations and visualzations and have found it to be a very powerful tool with many existing modules for mathematics and plotting, in addition to the thousands of other libraries for general computing. 10 | 11 | Check out the dependencies of mechpy for specific examples and documentation 12 | * Scipy 13 | * numpy 14 | * sympy 15 | * matplotlib 16 | * pandas 17 | * pyndamics 18 | 19 | Other neat engineering python modules 20 | * [pyndamics]() with [example](http://nbviewer.ipython.org/gist/bblais/7321928) 21 | * [control-systems](https://github.com/python-control/python-control) 22 | * [grid solvers](http://pyamg.org/) with [example)[https://code.google.com/p/pyamg/wiki/Examples] 23 | * [python dynamics](http://www.pydy.org/),(https://pypi.python.org/pypi/pydy/), [examples](#http://nbviewer.jupyter.org/github/pydy/pydy-tutorial-human-standing/tree/online-read/notebooks/) 24 | * [sympy classical mechanics](http://docs.sympy.org/latest/modules/physics/mechanics/index.html) 25 | 26 | http://docs.sympy.org/latest/modules/physics/mechanics/index.html 27 | https://github.com/cdsousa/sympybotics 28 | https://pypi.python.org/pypi/Hamilton 29 | https://pypi.python.org/pypi/arboris 30 | https://pypi.python.org/pypi/PyODE 31 | https://pypi.python.org/pypi/odeViz 32 | https://pypi.python.org/pypi/ARS 33 | https://pypi.python.org/pypi/pymunk 34 | http://scipy.github.io/old-wiki/pages/NumPy_for_Matlab_Users.html 35 | 36 | https://docs.scipy.org/doc/numpy-dev/user/numpy-for-matlab-users.html 37 | 38 | TODO 39 | - [ ] Add OOP model to generate Free Body Diagrams 40 | - [ ] add shear-bending diagrams 41 | -------------------------------------------------------------------------------- /mechpy/dynamics.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | ''' 3 | 4 | http://nbviewer.jupyter.org/github/pydy/pydy/blob/master/examples/mass_spring_damper/mass_spring_damper.ipynb 5 | 6 | ''' 7 | 8 | def double_pendulumn(): 9 | 10 | # Double pendulum formula translated from the C code at 11 | # http://www.physics.usyd.edu.au/~wheat/dpend_html/solve_dpend.c 12 | 13 | from numpy import sin, cos, pi, array 14 | import numpy as np 15 | import matplotlib.pyplot as plt 16 | import scipy.integrate as integrate 17 | import matplotlib.animation as animation 18 | 19 | G = 9.8 # acceleration due to gravity, in m/s^2 20 | L1 = 1.0 # length of pendulum 1 in m 21 | L2 = 1.0 # length of pendulum 2 in m 22 | M1 = 1.0 # mass of pendulum 1 in kg 23 | M2 = 1.0 # mass of pendulum 2 in kg 24 | 25 | 26 | def derivs(state, t): 27 | 28 | dydx = np.zeros_like(state) 29 | dydx[0] = state[1] 30 | 31 | del_ = state[2]-state[0] 32 | den1 = (M1+M2)*L1 - M2*L1*cos(del_)*cos(del_) 33 | dydx[1] = (M2*L1*state[1]*state[1]*sin(del_)*cos(del_) 34 | + M2*G*sin(state[2])*cos(del_) + M2*L2*state[3]*state[3]*sin(del_) 35 | - (M1+M2)*G*sin(state[0]))/den1 36 | 37 | dydx[2] = state[3] 38 | 39 | den2 = (L2/L1)*den1 40 | dydx[3] = (-M2*L2*state[3]*state[3]*sin(del_)*cos(del_) 41 | + (M1+M2)*G*sin(state[0])*cos(del_) 42 | - (M1+M2)*L1*state[1]*state[1]*sin(del_) 43 | - (M1+M2)*G*sin(state[2]))/den2 44 | 45 | return dydx 46 | 47 | # create a time array from 0..100 sampled at 0.05 second steps 48 | dt = 0.05 49 | t = np.arange(0.0, 20, dt) 50 | 51 | # th1 and th2 are the initial angles (degrees) 52 | # w10 and w20 are the initial angular velocities (degrees per second) 53 | th1 = 120.0 54 | w1 = 0.0 55 | th2 = -10.0 56 | w2 = 0.0 57 | 58 | rad = pi/180 59 | 60 | # initial state 61 | state = np.array([th1, w1, th2, w2])*pi/180. 62 | 63 | # integrate your ODE using scipy.integrate. 64 | y = integrate.odeint(derivs, state, t) 65 | 66 | x1 = L1*sin(y[:,0]) 67 | y1 = -L1*cos(y[:,0]) 68 | 69 | x2 = L2*sin(y[:,2]) + x1 70 | y2 = -L2*cos(y[:,2]) + y1 71 | 72 | fig = plt.figure() 73 | ax = fig.add_subplot(111, autoscale_on=False, xlim=(-2, 2), ylim=(-2, 2)) 74 | ax.grid() 75 | 76 | line, = ax.plot([], [], 'o-', lw=2) 77 | time_template = 'time = %.1fs' 78 | time_text = ax.text(0.05, 0.9, '', transform=ax.transAxes) 79 | 80 | def init(): 81 | line.set_data([], []) 82 | time_text.set_text('') 83 | return line, time_text 84 | 85 | def animate(i): 86 | thisx = [0, x1[i], x2[i]] 87 | thisy = [0, y1[i], y2[i]] 88 | 89 | line.set_data(thisx, thisy) 90 | time_text.set_text(time_template%(i*dt)) 91 | return line, time_text 92 | 93 | ani = animation.FuncAnimation(fig, animate, np.arange(1, len(y)), 94 | interval=25, blit=True, init_func=init) 95 | 96 | #ani.save('double_pendulum.mp4', fps=15) 97 | plt.show() 98 | 99 | if __name__ == '__main__': 100 | double_pendulumn() 101 | -------------------------------------------------------------------------------- /mechpy/units.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | ''' 3 | Created on Tue Aug 12 13:03:59 2014 4 | author: Neal Gordon 5 | 6 | Typical use of unitconvert are to find the equivalent of a unit at the command line 7 | without the need to be online 8 | 9 | scripts for use with engineering calculations 10 | 11 | NOTE - if you want to use this in a windows command line anywhere 12 | in the system, 13 | follow these instrcutions 14 | 15 | Copy the units.py file to a static location. If using Anaconda 16 | this is a great place because it will already have been added to the user path 17 | C:/Users/Neal/Anaconda3/Scripts 18 | 19 | ready to go! 20 | > units 1 inch foot 21 | 1.00 foot = 12.00 inch 22 | ''' 23 | 24 | def uc1(numin,frm,to): 25 | '''sympy 26 | uc1 - unit convert 1 27 | http://docs.sympy.org/dev/modules/physics/units.html 28 | # uses sympy module for unit conversion(uc) 29 | # converts number 'num' from 'frm' units to 'to' units 30 | 31 | # examples 32 | uc1(1.0,'pascal','psi') 33 | uc1(1.0,'psi','kPa') 34 | uc1(1.0,'atm','psi') 35 | uc1(1.0,'inch','mm') 36 | uc1(1.0,'kilometer','mile') 37 | uc1(1.0,'mile','kilometer') 38 | uc1(1.0,'newton','pound') 39 | uc1(1.0,'pound','newton') 40 | uc1(1.0,'joule','calorie') 41 | uc1(1.0, 'radians','degrees') 42 | 43 | 44 | ''' 45 | from sympy.physics import units 46 | 47 | try: 48 | eval('units.'+frm) 49 | except: 50 | print('no unit %s found, did you mean...\n' % frm) 51 | print(eval("units.find_unit('"+frm+"')")) 52 | return 53 | 54 | try: 55 | eval('units.'+to) 56 | except: 57 | print('no unit %s found, did you mean...\n' % to) 58 | print(eval("units.find_unit('"+to+"')")) 59 | return 60 | 61 | strin = 'numin * units.'+frm+'/units.'+to 62 | numout = float(eval(strin)) 63 | #print(numin , frm , '=' , numout , to) 64 | print('%.2f %s = %.2f %s '%(numin, frm, numout, to )) 65 | #return numout 66 | 67 | 68 | 69 | def uc2(num,frm,to): 70 | ''' Pint 71 | uc2 - unit convert 2 72 | Pint is used to manipulate physical quanities 73 | https://pint.readthedocs.org/en/0.6/tutorial.html 74 | 75 | # uses pint module for unit conversion 76 | # converts number 'num' from 'frm' units to 'to' units 77 | uc2(17.5,'lbf','newton') 78 | uc2(1,'lbf','newton') 79 | uc2(300,'pascal','psi') 80 | uc2(1,'inch','mm') 81 | uc2(1,'kilometer','mile') 82 | uc2(1,'mile','kilometer') 83 | ''' 84 | try: 85 | from pint import UnitRegistry 86 | except: 87 | print('pint is missing, install with $ pip install pint') 88 | ureg = UnitRegistry() 89 | strin = 'num * ureg.'+frm+'.to(ureg.'+to+')' 90 | numout = eval(strin) 91 | print(num , frm , '=' ,numout ) 92 | return numout 93 | 94 | def uc3(numin,frm,to): 95 | '''quantities 96 | uc3 - unit convert 3 97 | https://github.com/python-quantities/python-quantities 98 | https://pythonhosted.org/quantities/user/tutorial.html 99 | 100 | c3(1,'inch','ft') 101 | 102 | inch, ft, m, mil, mile, fathom, light_year, mm, 103 | Celsius, Fahrenheit, kelvin 104 | pascal, psi 105 | calorie, joule 106 | watt, horsepower 107 | 108 | ''' 109 | try: 110 | import quantities as pq 111 | except: 112 | print('python-quantities not found, downloading and installing from github now...') 113 | import os 114 | os.system('pip install git+https://github.com/python-quantities/python-quantities.git') 115 | 116 | try: 117 | eval('pq.'+frm) 118 | eval('pq.'+to) 119 | except: 120 | print('no unit found') 121 | return 122 | 123 | numout = eval(str(numin)+"* pq."+frm) 124 | numout.units = to 125 | numout = float(numout) 126 | 127 | #print(numin , frm , '=' , numout , to) 128 | print('%.2f %s = %.2f %s '%(numin, frm, numout, to )) 129 | #return numout 130 | 131 | 132 | def in_mm(n=16): 133 | # %pylab inline # command in ipython that imports sci modulues 134 | import sympy as sym 135 | sym.init_printing() 136 | for k in range(n+1): 137 | n = float(n) 138 | print(' %5s in - %.6f in - %.6f mm ' % (sym.Rational(k,n) , k/n, 25.4*k/n ) ) 139 | 140 | def nas(n=16): 141 | # %pylab inline # command in ipython that imports sci modulues 142 | import sympy as sym 143 | sym.init_printing() 144 | for k in range(n+1): 145 | n = float(n) 146 | print('NAS63%02d = %s' % ( k, sym.Rational(k,n) ) ) 147 | 148 | def hst(n=16): 149 | # %pylab inline # command in ipython that imports sci modulues 150 | import sympy as sym 151 | sym.init_printing() 152 | for k in range(n+1): 153 | n = float(n) 154 | print('HST63%02d = %s' % ( k, sym.Rational(k,n*2) ) ) 155 | 156 | 157 | 158 | 159 | if __name__ == '__main__': 160 | # executed when script is run alone 161 | import sys 162 | 163 | if sys.argv[1] == 'in_mm': 164 | in_mm() 165 | else: 166 | numin = float(sys.argv[1]) 167 | frm = sys.argv[2] 168 | to = sys.argv[3] 169 | 170 | uc3(numin,frm,to) 171 | 172 | -------------------------------------------------------------------------------- /mechpy/math.py: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | 3 | ''' 4 | Module to be used for mathematical tools for mechanical engineering stuff 5 | ''' 6 | 7 | 8 | import sympy as sp 9 | import numpy as np 10 | import matplotlib.pyplot as mp 11 | from pprint import pprint 12 | 13 | #np.set_printoptions(edgeitems=3,linewidth=75, precision=5, suppress=False, threshold=1000) 14 | #get_ipython().magic('matplotlib inline') 15 | 16 | def T2rot(th): 17 | return np.array([[np.cos(th), -np.sin(th)],[np.sin(th), np.cos(th)]]) 18 | 19 | # %load T3r.py 20 | def T3rot(th): 21 | from numpy import matrix, pi, cos, sin 22 | # rotation only about the z axis 23 | th *= pi/180 # change degrees to radians 24 | T3 = matrix([ [ cos(th), sin(th), 0], 25 | [-sin(th), cos(th), 0], 26 | [ 0, 0 , 1]]) 27 | return T3 28 | 29 | 30 | def T6rot(thx=0, thy=0, thz=0): 31 | ''' 32 | aij = [1 0 0;0 1 0; 0 0 1] 33 | derivation of the voight notation transformation matrix 34 | from barberom FEA of composites with abaqus, pg 14a 35 | accepts a as cosine direction matrix, aij, i new axis, and j is old axisa 36 | C = 6x6 stiffness matrix 37 | stress = C*strain 38 | S = inv(C) 39 | Cp = T*C*T' 40 | C = Tbar'Cp*Tbar 41 | Cp = T*C*T' 42 | Tbar' = inv(T) 43 | setup for transformation about axis 3 only 44 | thx = 45 # rot about x 45 | thy = 45 # rot about y 46 | thz = 45 # rot about z 47 | ''' 48 | 49 | from numpy import pi, cos, sin, matrix, eye, zeros 50 | #from numpy.linalg import inv 51 | ## Method 2 rotation matrices 52 | thx = thx*pi/180 # convert to radians 53 | thy = thy*pi/180 # convert to radians 54 | thz = thz*pi/180 # convert to radians 55 | # transformation about axis 1 56 | T1 = matrix([[1, 0, 0], 57 | [0, cos(thx), sin(thx)], 58 | [0, -sin(thx), cos(thx)]]) 59 | 60 | # transformation about axis 2 61 | T2 = matrix([[cos(thy), 0, -sin(thy)], 62 | [0, 1, 0], 63 | [sin(thy), 0, cos(thy)]]) 64 | 65 | # transformation about axis 3 66 | T3 = matrix([[cos(thz), sin(thz), 0], 67 | [-sin(thz), cos(thz), 0], 68 | [0, 0, 1]]) 69 | aij = T1*T2*T3 70 | # reuter matrix 71 | R = matrix(eye(6,6)) 72 | R[3,3]=2 73 | R[4,4]=2 74 | R[5,5]=2 75 | T = matrix(zeros((6,6))) 76 | for i in [1,2,3]: 77 | for j in [1,2,3]: 78 | alph = j if i==j else 9-i-j 79 | for p in [1,2,3]: 80 | for q in [1,2,3]: 81 | beta = p if p==q else 9-p-q 82 | if alph <= 3 and beta <= 3: 83 | T[alph-1,beta-1] = aij[i-1,p-1]*aij[i-1,p-1] 84 | elif alph > 3 and beta <= 3: 85 | T[alph-1,beta-1] = aij[i-1,p-1]*aij[j-1,p-1] 86 | elif alph <= 3 and beta > 3: 87 | T[alph-1,beta-1] = aij[i-1,q-1]*aij[i-1,p-1]+aij[i-1,p-1]*aij[i-1,q-1] 88 | elif alph > 3 and beta > 3: 89 | T[alph-1,beta-1] = aij[i-1,p-1]*aij[j-1,q-1] + aij[i-1,q-1]*aij[j-1,p-1] 90 | else: 91 | T[alph-1,beta-1] = 0 92 | 93 | Tbar = R*T*(R.I) # ==R*T*inv(R) 94 | #print(Tbar) 95 | return Tbar 96 | 97 | 98 | 99 | def ode1(): 100 | """ 101 | First order ode from Learning Scipy for Numerical and Scientific computing 102 | """ 103 | import numpy 104 | from scipy.integrate import ode 105 | 106 | f = lambda t,y: -20*y 107 | 108 | actual_solution = lambda t:numpy.exp(-20*t) 109 | dt = 0.1 110 | 111 | solver = ode(f).set_integrator('dop853') 112 | solver.set_initial_value(1,0) 113 | 114 | while solver.successful() and solver.t <= 1+dt: 115 | print(solver.t, solver.y, actual_solution(solver.t)) 116 | solver.integrate(solver.t+dt) 117 | 118 | 119 | def fft_example(): 120 | from scipy import fft 121 | from numpy import arange, cos, pi, random 122 | from matplotlib.pyplot import subplot, plot, ylabel, xlabel, title, grid, xlim, show 123 | 124 | N = 2**9 125 | F = 25 126 | t = arange(N)/float(N) 127 | x = cos(2*pi*t*F) + random.rand(len(t))*3 128 | subplot(2,1,1) 129 | plot(t,x) 130 | ylabel('x []') 131 | xlabel('t [seconds]') 132 | title('A cosine wave') 133 | grid() 134 | 135 | subplot(2,1,2) 136 | f = t*N 137 | xf = fft(x) 138 | xf = xf[:N/2] 139 | f = f[:N/2] 140 | plot(f,abs(xf)) 141 | title('Fourier transform of a cosine wave') 142 | xlabel('xf []') 143 | ylabel('xf []') 144 | xlim([0,N/2]) 145 | grid() 146 | show() 147 | 148 | def fft_example2(): 149 | from numpy import sin, linspace, pi 150 | from pylab import plot, show, title, xlabel, ylabel, subplot 151 | from scipy import fft, arange 152 | 153 | Fs = 150.0; # sampling rate 154 | Ts = 1.0/Fs; # sampling interval 155 | t = arange(0,1,Ts) # time vector 156 | 157 | ff = 5; # frequency of the signal 158 | y = sin(2*pi*ff*t) 159 | n = len(y) # length of the signal 160 | k = arange(n) 161 | T = n/Fs 162 | frq = k/T # two sides frequency range 163 | frq = frq[range(n//2)] # one side frequency range 164 | Y = fft(y)/n # fft computing and normalization 165 | Y = Y[range(n//2)] 166 | 167 | subplot(2,1,1) 168 | plot(t,y) 169 | xlabel('Time') 170 | ylabel('Amplitude') 171 | subplot(2,1,2) 172 | plot(frq,abs(Y),'r') # plotting the spectrum 173 | xlabel('Freq (Hz)') 174 | ylabel('|Y(freq)|') 175 | 176 | show() 177 | 178 | def fft(y,Fs): 179 | from numpy import sin, linspace, pi 180 | from pylab import plot, show, title, xlabel, ylabel, subplot 181 | from scipy import fft, arange 182 | 183 | Ts = 1.0/Fs; # sampling interval 184 | t = arange(0,1,Ts) # time vector 185 | n = len(y) # length of the signal 186 | k = arange(n) 187 | T = n/Fs 188 | frq = k/T # two sides frequency range 189 | frq = frq[range(n//2)] # one side frequency range 190 | Y = fft(y)/n # fft computing and normalization 191 | Y = Y[range(n//2)] 192 | 193 | subplot(2,1,1) 194 | plot(t,y) 195 | xlabel('Time') 196 | ylabel('Amplitude') 197 | subplot(2,1,2) 198 | plot(frq,abs(Y),'r') # plotting the spectrum 199 | xlabel('Freq (Hz)') 200 | ylabel('|Y(freq)|') 201 | 202 | show() 203 | 204 | def cart2pol(x, y): 205 | rho = np.sqrt(x**2 + y**2) 206 | phi = np.arctan2(y, x) 207 | return(rho, phi) 208 | 209 | def pol2cart(rho, phi): 210 | x = rho * np.cos(phi) 211 | y = rho * np.sin(phi) 212 | return(x, y) 213 | 214 | if __name__ == '__main__': 215 | 216 | 217 | T3rot(45) 218 | 219 | T6rot(45,45,45) 220 | 221 | qbar_transformtion() -------------------------------------------------------------------------------- /mechpy/statics.py: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | 3 | ''' 4 | Module to be used for static analysis 5 | ''' 6 | import numpy as np 7 | import sympy as sp 8 | import scipy 9 | import matplotlib.pyplot as plt 10 | from matplotlib import patches 11 | from mpl_toolkits.mplot3d import Axes3D 12 | 13 | def simple_support(): 14 | L = 15 15 | P = 5 16 | Ploc = 5 17 | 18 | plt.rcParams['figure.figsize'] = (10, 8) # (width, height) 19 | 20 | fig1 = plt.figure() 21 | ax1 = fig1.add_subplot(311)#, aspect='equal') 22 | 23 | def add_beam(): 24 | #plt.subplot(3,1,1) 25 | #ax = plt.gca() 26 | 27 | plt.xlim([-1, L+1]) 28 | plt.ylim([-1, P*2]) 29 | 30 | # add rigid ground 31 | rectangle = plt.Rectangle((-1, -2), L+2, 2, hatch='//', fill=False) 32 | ax1.add_patch(rectangle) 33 | 34 | # add rigid rollers 35 | #circle = plt.Circle((0, 5), radius=1, fc='g') 36 | #ax.add_patch(circle) 37 | e1 = patches.Ellipse((0, 2), L/20, 4, angle=0, linewidth=2, fill=False, zorder=2) 38 | ax1.add_patch(e1) 39 | 40 | # add triangle 41 | points = [[L, 4], [L-L/40, 0], [L+L/40,0]] 42 | polygon = plt.Polygon(points, fill=False) 43 | ax1.add_patch(polygon) 44 | 45 | # add beam 46 | rectangle = plt.Rectangle((0, 4), L, 4, fill=False) 47 | ax1.add_patch(rectangle) 48 | 49 | def point_load(): 50 | # point load shear 51 | x = np.linspace(0,L,100) 52 | y = np.ones(len(x))*P/2 53 | y[x>Ploc] = y[x>Ploc]-P 54 | x[0]=0 55 | x[-1]=0 56 | 57 | plt.subplot(3,1,2) 58 | plt.ylabel('Shear, V') 59 | plt.title('Shear Diagram') 60 | plt.fill(x, y, 'b', alpha=0.25) 61 | plt.grid(True) 62 | plt.xlim([-1, L+1]) 63 | 64 | # point load bending 65 | x = np.linspace(-L/2,L/2,100) 66 | y = -(x**2)+(np.max(x**2)) 67 | x = np.linspace(0,L,100) 68 | plt.subplot(3,1,3) 69 | plt.title('Bending Diagram') 70 | plt.ylabel('Moment, M') 71 | plt.fill(x, y, 'b', alpha=0.25) 72 | plt.grid(True) 73 | plt.xlim([-1, L+1]) 74 | 75 | # add point load 76 | plt.subplot(3,1,1) 77 | plt.annotate('P=%i'%P, ha = 'center', va = 'bottom', 78 | xytext = (Ploc, 15), xy = (Ploc,7.5), 79 | arrowprops = { 'facecolor' : 'black', 'shrink' : 0.05 }) 80 | plt.title('Free Body Diagram') 81 | plt.axis('off') # removes axis and labels 82 | 83 | # # add point load 84 | # ax1.arrow(3, 11+L/10, 0, -3, head_width=L*0.02, head_length=L*0.1, fc='k', ec='k') 85 | # plt.title('Free Body Diagram') 86 | # plt.axis('off') # removes axis and labels 87 | # #ax1.set_yticklabels('') 88 | 89 | def dist_load(): 90 | 91 | # add distributed load 92 | plt.subplot(3,1,1) 93 | for k in np.linspace(0,L,20): 94 | ax1.arrow(k, 11+L/10, 0, -3, head_width=L*0.01, head_length=L*0.1, fc='k', ec='k') 95 | plt.title('Free Body Diagram') 96 | plt.axis('off') # removes axis and labels 97 | #ax1.set_yticklabels('') 98 | 99 | # dist load shear 100 | x = [0,0,L,L] 101 | y = [0,5,-5,0] 102 | plt.subplot(3,1,2) 103 | plt.ylabel('Shear, V') 104 | plt.title('Shear Diagram') 105 | plt.fill(x, y, 'b', alpha=0.25) 106 | plt.grid(True) 107 | plt.xlim([-1, L+1]) 108 | 109 | # dist load bending 110 | x = np.linspace(-L/2,L/2,100) 111 | y = -(x**2)+(np.max(x**2)) 112 | x = np.linspace(0,L,100) 113 | plt.subplot(3,1,3) 114 | plt.title('Bending Diagram') 115 | plt.ylabel('Moment, M') 116 | plt.fill(x, y, 'b', alpha=0.25) 117 | plt.grid(True) 118 | plt.xlim([-1, L+1]) 119 | 120 | add_beam() 121 | dist_load() 122 | #point_load() 123 | plt.tight_layout() 124 | plt.show() 125 | 126 | def moment_calc(): 127 | 128 | fig = plt.figure() 129 | 130 | ax = plt.axes(projection='3d') 131 | 132 | # bar 133 | x=[0,0,4,4] 134 | y=[0,5,5,5] 135 | z=[0,0,0,-2] 136 | 137 | # Applied Forces 138 | X=[0,0,4] 139 | Y=[5,5,5] 140 | Z=[0,0,-2] 141 | U=[-60,0 ,80] 142 | V=[40 ,50,40] 143 | W=[20 ,0 ,-30] 144 | 145 | ax.plot(x, y, z, '-b', linewidth=5) 146 | ax.view_init(45, 45) 147 | 148 | ax.set_xlabel('X') 149 | ax.set_ylabel('Y') 150 | ax.set_zlabel('Z') 151 | ax.set_title('Hibbler pg 129 example') 152 | ax.set_xlim([min(X)-2, max(X) + 2]) 153 | ax.set_ylim([min(Y)-5, max(Y) + 2]) 154 | ax.set_zlim([min(Z)-2, max(Z) + 2]) 155 | 156 | #plt.tight_layout() 157 | 158 | ax.quiver3D(X, Y, Z, U, V, W, pivot='tail'); 159 | 160 | rA = np.array([0,5,0]) # start of F1 and F2 161 | rB = np.array([4,5,-2]) # start of F3 162 | F1 = np.array([-60,40,20]) 163 | F2 = np.array([0,50,0]) 164 | F3 = np.array([80,40,-30]) 165 | M = np.cross(rA,F1) + np.cross(rA,F2) + np.cross(rB,F3) 166 | print('Total Moment vector') 167 | print(M) 168 | 169 | print('Total Force Vector about point O') 170 | print(sum([F1,F2,F3])) 171 | 172 | print('unit vector of the moment') 173 | u = M/np.linalg.norm(M) 174 | print(u) 175 | 176 | print('angles at which the moments react') 177 | print(np.rad2deg(np.arccos(u))) 178 | 179 | def point_ss_shear_bending(L,Pin,ain): 180 | ''' 181 | Shear Bending plot of point loads of a simply supported beam 182 | L = 4 # total length of beam 183 | Pin = [5] # point load 184 | ain = [2] # location of point load 185 | 186 | # or more multiple points 187 | L = 10 188 | Pin = [3,15] 189 | ain = [2,6] 190 | ''' 191 | 192 | import numpy as np 193 | import matplotlib.pyplot as plt 194 | 195 | x = np.arange(0,L,L*0.02) 196 | V = np.zeros(len(x)) 197 | M = np.zeros(len(x)) 198 | 199 | for a, P in zip(ain, Pin): 200 | V[x<=a] += P*(1-a/L) 201 | V[x>a] += -P*a/L 202 | M[x<=a] += P*(1-a/L)*x[x<=a] 203 | M[x>a] += -P*a*(x[x>a]/L-1) 204 | 205 | plt.subplot(2,1,1) 206 | plt.stem(x,V) 207 | plt.ylabel('V,shear') 208 | plt.subplot(2,1,2) 209 | plt.stem(x,M) 210 | plt.ylabel('M,moment') 211 | 212 | def moment_ss_shear_bending(L,Pin,ain): 213 | ''' 214 | Shear Bending plot of moment loads of a simply supported beam 215 | L = 4 # total length of beam 216 | Pin = [5] # point moment load 217 | ain = [2] # location of point load 218 | 219 | # or more multiple point moments 220 | L = 10 221 | Pin = [3,-15] 222 | ain = [2,6] 223 | ''' 224 | 225 | import numpy as np 226 | import matplotlib.pyplot as plt 227 | 228 | x = np.arange(0,L,L*0.02) 229 | V = np.zeros(len(x)) 230 | M = np.zeros(len(x)) 231 | 232 | for a, P in zip(ain, Pin): 233 | V += -P/L 234 | M[x<=a] += -P*x[x<=a]/L 235 | M[x>a] += P*(1-x[x>a]/L) 236 | 237 | plt.figure() 238 | plt.title('Point Moment Loads') 239 | plt.subplot(2,1,1) 240 | plt.stem(x,V) 241 | plt.ylabel('V,shear') 242 | plt.subplot(2,1,2) 243 | plt.stem(x,M) 244 | plt.ylabel('M,moment') 245 | 246 | def dist_ss_shear_bending(L, win, ain): 247 | ''' 248 | Shear Bending plot of distributed loads of a simply supported beam 249 | L = 10 # total length of beam 250 | win = [5] # distributed load 251 | ain = [[3,4]] # location of point load 252 | 253 | # or more multiple point moments 254 | L = 10 255 | win = [3,6] 256 | ain = [[0,3],[4,6]] 257 | ''' 258 | 259 | import numpy as np 260 | import matplotlib.pyplot as plt 261 | 262 | 263 | x = np.arange(0,L,L*0.02) 264 | V = np.zeros(len(x)) 265 | M = np.zeros(len(x)) 266 | 267 | for a, w in zip(ain, win): 268 | #a = ain[0] 269 | P = w*(a[1]-a[0]) # convert distributed load to point load 270 | l = (a[1]+a[0])/2 271 | 272 | i = [xa[1]] 277 | V[i] += -P*l/L 278 | M[i] += x[i]*-P*l/L + P*l 279 | 280 | i = [ (a[0]<=x) & (x<=a[1]) ] 281 | V[i] += P*(1-l/L) - w*(x[i]-a[0]) 282 | M[i] += (P*(1-l/L) - w*(x[i]-a[0]))*x[i] + w*(x[i]-a[0])*(a[0]+x[i])/2 283 | #V[i] += P*(1-l/L)-P*x[i] 284 | #M[i] += P/2*(L*x[i] - x[i]**2) 285 | #M[i] += x[i]*P*(1-l/L) - (P*x[i]**2)/2 286 | 287 | 288 | 289 | plt.figure() 290 | plt.title('Point Moment Loads') 291 | plt.subplot(2,1,1) 292 | plt.stem(x,V) 293 | plt.ylabel('V,shear') 294 | plt.subplot(2,1,2) 295 | plt.stem(x,M) 296 | plt.ylabel('M,moment') 297 | 298 | if __name__ == '__main__': 299 | # executed when script is run alone 300 | #moment_calc() 301 | dist_ss_shear_bending() -------------------------------------------------------------------------------- /mechpy/fem.py: -------------------------------------------------------------------------------- 1 | 2 | from numpy import array, matrix, zeros, linspace, arange 3 | #from matplotlib.pyplot import * 4 | import scipy.linalg 5 | import matplotlib.pyplot as plt 6 | from matplotlib.pyplot import plot, figure, xlim, ylim, title, xlabel, ylabel, show 7 | import numpy as np 8 | plt.rcParams['figure.figsize'] = (10, 8) # (width, height) 9 | 10 | def cst_fem(structure='9node'): 11 | ''' 12 | Gusset plate problem using 8 CST elemetnts. Uniform load across top edge 13 | is modeled with 2 concentrated forces 14 | structure = ['truss','4node', '9node'] 15 | ''' 16 | 17 | ## define variables 18 | E = 10e6 # modulus of elasticity 19 | L = 20 # length of sketch (see drawing) 20 | Q = 1000 # pounds/inch load 21 | plotfactor = 1e2 # displacement factor for plot 22 | poisson = 0.3 # poisson ratio 23 | ## Set nodal coordinates and element destination entries. 24 | # u1 u2 u3 u4 u5 u6 u7 u8 25 | 26 | #============================================================================== 27 | if structure == '4node': 28 | nodexy = array([0, 0, 10, 10, 20, 20, 0, 20]) # global node coordinets (arbitrary) 29 | nodeBC = array([1, 1, 0, 0, 0, 0, 1, 1]) # boundary conditions, 1 if u=0 30 | 31 | nodex = list(nodexy[0::2]) 32 | nodey = list(nodexy[1::2]) 33 | ####nodexyplot = [nodex, nodey] 34 | nodexyT = list(zip(nodex, nodey)) 35 | ### list(zip(nodexplot, nodeyplot)) 36 | 37 | #### node 0 1 2 3 38 | adj = array([[0, 1, 0, 1], 39 | [0, 0, 1, 1], 40 | [0, 0, 0, 1], 41 | [0, 0, 0, 0]]) 42 | 43 | #### x y x y x y 44 | #### u1 u2 u# u# u# u# 45 | elnodes = array([[0, 1, 2, 3, 6, 7], 46 | [6, 7, 2, 3, 4, 5]]) # 1 element per row, dofs labled CCW (arbitrary) 47 | #============================================================================== 48 | elif structure == '9node': 49 | # 9 nodes 50 | nodexy = array([0, 0, L/4, L/4, L/2, L/2,3*L/4, 3*L/4, L, 51 | L, L/2, L, L/4, 3*L/4, 0, L, 0, L/2]) # global node coordinets (arbitrary) 52 | # u1 u2 u3 u4 u5 u6 u7 u8 u9 u10 u11 u12 53 | nodeBC = array([1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1]) # is dof fixed? 54 | # x y x y x y 55 | # u1 u2 u# u# u# u# 56 | elnodes = array([[ 0, 1, 2, 3, 16, 17], 57 | [ 2, 3, 4, 5, 16, 17], 58 | [ 4, 5, 12, 13, 16, 17], 59 | [ 4, 5, 10, 11, 12, 13], 60 | [ 4, 5, 6, 7, 10, 11], 61 | [ 6, 7, 8, 9, 10, 11], 62 | [12, 13, 10, 11, 14, 15], 63 | [16, 17, 12, 13, 14, 15]]) # 1 element per row, dofs labled CCW (arbitrary) 64 | 65 | adj = array([[0,1,0,0,0,0,0,0,1], 66 | [0,0,1,0,0,0,0,0,1], 67 | [0,0,0,1,0,1,1,0,1], 68 | [0,0,0,0,1,1,0,0,0], 69 | [0,0,0,0,0,1,0,0,0], 70 | [0,0,0,0,0,0,1,1,0], 71 | [0,0,0,0,0,0,0,1,1], 72 | [0,0,0,0,0,0,0,0,1], 73 | [0,0,0,0,0,0,0,0,0]]) 74 | #============================================================================== 75 | elif structure == 'truss': 76 | nodexy = array([0,0,1,0,2,0,3,0,4,0,5,0,5,1,4,1,3,1,2,1,1,1,0,1] ) 77 | nodeBC = array([1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1] ) 78 | 79 | elnodes = array([ [ 0, 1, 2, 3, 22, 23], 80 | [ 2, 3, 4, 5, 20, 21], 81 | [ 4, 5, 6, 7, 18, 19], 82 | [ 6, 7, 8, 9, 16, 17], 83 | [ 8, 9, 10, 11, 14, 15], 84 | [10, 11, 12, 13, 14, 15], 85 | [ 8, 9, 14, 15, 16, 17], 86 | [ 6, 7, 16, 17, 18, 19], 87 | [ 4, 5, 18, 19, 20, 21], 88 | [ 2, 3, 20, 21, 22, 23]]) 89 | 90 | nodes = int(len(nodexy)//2) 91 | adj = np.zeros((nodes,nodes)) 92 | conmat = array([[0,0, 1, 1,1,2,2,2,3,3,3,4,4,4,5,5,6,7, 8, 9, 10], 93 | [1,11,2,10,11,3,9,10,4,8,9,5,7,8,6,7, 7,8, 9, 10, 11]]) 94 | conmat = np.transpose(conmat) 95 | for i in range(len(conmat)): 96 | adj[conmat[i,0],conmat[i,1]] = 1 97 | 98 | 99 | #### Begin calculations 100 | nodex = list(nodexy[0::2]) 101 | nodey = list(nodexy[1::2]) 102 | ####nodexyplot = [nodex, nodey] 103 | nodexyT = list(zip(nodex, nodey)) 104 | ### list(zip(nodexplot, nodeyplot)) 105 | 106 | elements = int(len(elnodes)) # Number of elements 107 | nodes = int(len(nodexy)//2) # number of nodes 108 | doftotal = int(nodes*2) # number of total degrees of freedom 109 | nodexyplot = zeros((nodes,2)) # global coordinates of nodes for plotting 110 | nodeplotload = zeros((nodes,2)) # global coordiantes for deflected nodes for plotting 111 | P = zeros((doftotal,1)) # total load vector 112 | U = zeros((doftotal,1)) # displacements 113 | Ue = zeros((6,1)) # displacements per element, 6 for CST 114 | Ke = zeros((6,6)) # stiffness per element 115 | K = zeros((doftotal,doftotal)) # totral structure stiffness 116 | B = zeros((3,6)) # dN/dx , strain = B*u, correct for CST 117 | D = zeros((3,3)) # Elasticity Matrix (D), correct for CST 118 | strain = zeros((elements,3)) # Element(row) strain per node (column) 119 | stress = zeros((elements,3)) # Element(row) stress per node (column) 120 | pstress = 1 # pstress >0 plane stress pstress = 0 plane strain 121 | ## Load Vector 122 | P[9] = -2e6 # 10 kips load 123 | #P[3] = -20000/2 # 10 kips load 124 | ## Elasticity Matrix 125 | D[1,0] = poisson 126 | D[0,1] = poisson 127 | 128 | if pstress == 1: 129 | print('plane stress condition') 130 | D[0,0] = 1 131 | D[1,1] = 1 132 | D[2,2] = 0.5*(1-poisson) 133 | D = D*E/(1-poisson*poisson) 134 | else: 135 | print('plane strain condition') 136 | D[0,0] = 1-poisson 137 | D[1,1] = 1-poisson 138 | D[2,2] = 0.5*(1-2*poisson) 139 | D = D*E/((1-2*poisson)*(1+poisson)) 140 | 141 | 142 | 143 | ## loop over each element, build element [B] matrix then element matrix. 144 | # Assemble element stiffness into structure stiffness by building B matrix 145 | # B = dN/dx 146 | # x,y are the local nodal coordinates 147 | for i in range(elements): # looping through each element and building shape function 148 | x1 = nodexy[elnodes[i,0]] 149 | x2 = nodexy[elnodes[i,2]] 150 | x3 = nodexy[elnodes[i,4]] 151 | y1 = nodexy[elnodes[i,1]] 152 | y2 = nodexy[elnodes[i,3]] 153 | y3 = nodexy[elnodes[i,5]] 154 | x13 = x1-x3 155 | x21 = x2-x1 156 | x32 = x3-x2 157 | y12 = y1-y2 158 | y23 = y2-y3 159 | y31 = y3-y1 160 | B[0,0] = y23 161 | B[2,0] = x32 162 | B[1,1] = x32 163 | B[2,1] = y23 164 | B[0,2] = y31 165 | B[2,2] = x13 166 | B[1,3] = x13 167 | B[2,3] = y31 168 | B[0,4] = y12 169 | B[2,4] = x21 170 | B[1,5] = x21 171 | B[2,5] = y12 172 | A = 0.5*(x1*y23 + x2*y31 + x3*y12) 173 | B = B/(2*A) 174 | Ke = B.T @ D @ B * A # matrix multiplcation 175 | 176 | # assemble elements into structure stiffness 177 | for kk in range(6): # 6 u dof in CST 178 | ii = elnodes[i,kk] # u(j) in element i 179 | for j in range(6): # 6 v dof in CST 180 | jj = elnodes[i,j] # vj in element i 181 | K[ii,jj] += Ke[kk,j] # add element to total structure 182 | 183 | ## Apply Boundary Conditions via partition matrix method for 0 displacement only 184 | Ksol = np.copy(K) 185 | Psol = np.copy(P) 186 | for i in range(doftotal): 187 | if nodeBC[i] == 1: 188 | Ksol[i,:] = 0 189 | Ksol[:,i] = 0 190 | Ksol[i,i] = 1 191 | Psol[i] = 0 192 | 193 | ## Solve displacements 194 | #U = Ksol\Psol 195 | U = scipy.linalg.solve(Ksol,Psol) 196 | 197 | ## retrieve kru of total structure stiffness matrix and get reactions 198 | R = K @ U 199 | ## loop over each element and form [B], get strains then stresses 200 | for i in range(elements): 201 | Ue = zeros((6,1)) 202 | x1 = nodexy[elnodes[i,0]] 203 | x2 = nodexy[elnodes[i,2]] 204 | x3 = nodexy[elnodes[i,4]] 205 | y1 = nodexy[elnodes[i,1]] 206 | y2 = nodexy[elnodes[i,3]] 207 | y3 = nodexy[elnodes[i,5]] 208 | x13 = x1-x3 209 | x21 = x2-x1 210 | x32 = x3-x2 211 | y12 = y1-y2 212 | y23 = y2-y3 213 | y31 = y3-y1 214 | B[0,0] = y23 215 | B[2,0] = x32 216 | B[1,1] = x32 217 | B[2,1] = y23 218 | B[0,2] = y31 219 | B[2,2] = x13 220 | B[1,3] = x13 221 | B[2,3] = y31 222 | B[0,4] = y12 223 | B[2,4] = x21 224 | B[1,5] = x21 225 | B[2,5] = y12 226 | A = 0.5*(x1*y23 + x2*y31 + x3*y12) 227 | B = B*0.5/A 228 | for j in range(6): 229 | ii = elnodes[i,j] 230 | Ue[j] = U[ii] 231 | 232 | strain[i,:] = np.transpose(B @ Ue) 233 | stress[i,:] = np.transpose(D @ B @ Ue) 234 | 235 | # plot shape function 236 | x = linspace(0,20,100) 237 | y = linspace(0,20,100) 238 | N1 = (1/(2*A))*( x2*y3-y2*x3 + (y2-y3)*x +(x3-x2)*y ) 239 | N2 = (1/(2*A))*( x3*y1-y3*x1 + (y3-y1)*x +(x1-x3)*y ) 240 | N3 = (1/(2*A))*( x1*y2-y1*x2 + (y1-y2)*x +(x2-x1)*y ) 241 | Nsum = N1 + N2 + N3 242 | fig1 = figure() 243 | plot(N1,'-.') 244 | plot(N2,'--') 245 | plot(N3,'.') 246 | plot(Nsum) 247 | title('CST shape functions') 248 | show() 249 | 250 | ## plotting of FEA structure for visual confirmation of results 251 | fig2 = figure() 252 | # transforms global node coordinates nodexy from 1xc vector to nx2 253 | # x y 254 | 255 | #adj += adj.T 256 | 257 | # nodexy = array([ 0, 0, 10, 10, 20, 20, 0, 20]) 258 | plotmag = 1 259 | nodexyu = nodexy+U.T[0]*plotmag 260 | nodexu = list(nodexyu[0::2]) 261 | nodeyu = list(nodexyu[1::2]) 262 | 263 | nodexyuT = list(zip(nodexu, nodeyu)) 264 | 265 | rlen, clen = adj.shape 266 | x = [] ; y = [] 267 | for r in range(rlen): 268 | for c in range(clen): 269 | if adj[r,c] == 1: 270 | 271 | x = [nodexyT[r][0], nodexyT[c][0]] 272 | y = [nodexyT[r][1], nodexyT[c][1]] 273 | plt.plot(x,y,'b') 274 | x = [nodexyuT[r][0], nodexyuT[c][0]] 275 | y = [nodexyuT[r][1], nodexyuT[c][1]] 276 | plt.plot(x,y,'b--') 277 | 278 | plt.xlim([np.min(nodex)-3, np.max(nodex)+3]) 279 | plt.ylim([np.min(nodey)-3, np.max(nodey)+3]) 280 | 281 | for i in range(nodes): 282 | plt.text(nodex[i]+0.5, nodey[i], '$n_%i$' % (i+1),fontsize=20) 283 | 284 | for i in range(elements): 285 | xtemp = nodexy[elnodes[i][0::2]] 286 | ytemp = nodexy[elnodes[i][1::2]] 287 | elnodesx = (max(xtemp) + min(xtemp)) / 2 288 | elnodesy = (max(ytemp) + min(ytemp)) / 2 289 | plt.text(elnodesx, elnodesy, '$e_%i$' % (i+1),fontsize=20) 290 | plt.plot(nodex, nodey, 'o') 291 | plt.plot(nodexu, nodeyu, '*') 292 | 293 | # plot stress 294 | elnodeslist = [] 295 | for e in elnodes: 296 | tmpnodex = list(nodexy[e[0::2]]) 297 | tmpnodey = list(nodexy[e[1::2]]) 298 | elnodeslist.append(list(zip(tmpnodex, tmpnodey))) 299 | plot_contour(elnodeslist,stress, '$\sigma$') 300 | #plot_contour(elnodeslist,strain,'$\epsilon$') 301 | 302 | def gplot(adj,xy): 303 | ''' 304 | # for a 2 element triangle 305 | xy = [(0, 0), (10, 10), (20, 20), (0, 20)] 306 | adj = [[0, 1, 0, 1], 307 | [0, 0, 1, 1], 308 | [0, 0, 0, 1], 309 | [0, 0, 0, 0]] 310 | gplot(adj,xy) 311 | 312 | # simple truss 313 | xy =[(0, 0), 314 | (1, 0), 315 | (2, 0), 316 | (3, 0), 317 | (4, 0), 318 | (5, 0), 319 | (5, 1), 320 | (4, 1), 321 | (3, 1), 322 | (2, 1), 323 | (1, 1), 324 | (0, 1)] 325 | adj = [[ 0., 1., 0., 0., 0., 0., 0., 0., 0., 0., 0., 1.], 326 | [ 0., 0., 1., 0., 0., 0., 0., 0., 0., 0., 1., 1.], 327 | [ 0., 0., 0., 1., 0., 0., 0., 0., 0., 1., 1., 0.], 328 | [ 0., 0., 0., 0., 1., 0., 0., 0., 1., 1., 0., 0.], 329 | [ 0., 0., 0., 0., 0., 1., 0., 1., 1., 0., 0., 0.], 330 | [ 0., 0., 0., 0., 0., 0., 1., 1., 0., 0., 0., 0.], 331 | [ 0., 0., 0., 0., 0., 0., 0., 1., 0., 0., 0., 0.], 332 | [ 0., 0., 0., 0., 0., 0., 0., 0., 1., 0., 0., 0.], 333 | [ 0., 0., 0., 0., 0., 0., 0., 0., 0., 1., 0., 0.], 334 | [ 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 1., 0.], 335 | [ 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 1.], 336 | [ 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.]] 337 | gplot(adj,xy) 338 | ''' 339 | 340 | adj = np.array(adj) 341 | rlen, clen = adj.shape 342 | nodex = [k[0] for k in xy] 343 | nodey = [k[1] for k in xy] 344 | 345 | xtemp = [] ; ytemp = [] 346 | for r in range(rlen): 347 | for c in range(clen): 348 | if adj[r,c] == 1: 349 | x = [xy[r][0], xy[c][0]] 350 | y = [xy[r][1], xy[c][1]] 351 | plt.plot(x,y,'b') 352 | #plt.plot(x,y,'b--') 353 | xlim([np.min(nodex)-3, np.max(nodex)+3]) 354 | ylim([np.min(nodey)-3, np.max(nodey)+3]) 355 | plt.plot(nodex, nodey, 'o') 356 | 357 | def plot_contour(xynodes, stress, mytitle='contour plot'): 358 | """ 359 | Simple demo of the fill function. 360 | xynodes = [[(0, 0), (10, 10), (0, 20)], [(10, 10), (20, 20), (0, 20)]] 361 | stress = [[-300,-100,-700],[300,350,-350]] 362 | plot_stress(xynodes, stress) 363 | """ 364 | 365 | 366 | stress = np.array(stress) 367 | fig2 = plt.figure() 368 | 369 | fig2 = plt.figure() 370 | sigma1 = stress[:,0] 371 | sigma2 = stress[:,1] 372 | sigma12 = stress[:,2] 373 | vonmises = np.sqrt(sigma1**2 - sigma1*sigma2+sigma2**2+3*sigma12**2) 374 | prins1 = (sigma1 + sigma2) / 2 + np.sqrt( ((sigma1-sigma2)/2)**2 + sigma12**2 ) 375 | prins2 = (sigma1 + sigma2) / 2 - np.sqrt( ((sigma1-sigma2)/2)**2 + sigma12**2 ) 376 | 377 | sigma1norm = sigma1/np.max(np.abs(sigma1)) 378 | sigma2norm = sigma2/np.max(np.abs(sigma2)) 379 | sigma12norm = sigma12/np.max(np.abs(sigma12)) 380 | vonmisesnorm = vonmises/np.max(np.abs(vonmises)) 381 | prins1norm = prins1/np.max(np.abs(prins1)) 382 | prins2norm = prins2/np.max(np.abs(prins2)) 383 | 384 | pltsigma = sigma1norm 385 | for i, xy in enumerate(xynodes): 386 | x = [k[0] for k in xy] 387 | y = [k[1] for k in xy] 388 | # red for tension, blue for compression 389 | pltcolor = 'r' if pltsigma[i] >= 0 else 'b' 390 | sigmavalnorm = pltsigma[i] if pltsigma[i] > 0 else pltsigma[i]*-1 391 | plt.fill(x,y,pltcolor, alpha=sigmavalnorm) 392 | plt.plot(x,y,'k') 393 | 394 | title(mytitle) 395 | tmpx = [x[0] for k in xynodes for x in k] 396 | tmpy = [x[1] for k in xynodes for x in k] 397 | plt.xlim([np.min(tmpx)-3, np.max(tmpx)+3]) 398 | plt.ylim([np.min(tmpy)-3, np.max(tmpy)+3]) 399 | plt.show() 400 | 401 | 402 | 403 | 404 | if __name__ == '__main__': 405 | 406 | # 4node, 9node, truss 407 | cst_fem('9node') 408 | #plot_stress() 409 | -------------------------------------------------------------------------------- /mechpy/composites.py: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | 3 | ''' 4 | Module to be used for composite material analysis 5 | ''' 6 | from numpy import arange, pi, sin, cos, zeros, matrix 7 | from numpy.linalg import solve 8 | import matplotlib.pyplot as plt 9 | 10 | from numpy import linspace, pi, sin, cos, matrix, array 11 | 12 | import numpy as np 13 | import matplotlib.pyplot as mp 14 | 15 | from numpy import matrix 16 | from matplotlib.pyplot import * 17 | 18 | def failure_envelope(): 19 | # failure envelopes 20 | 21 | # max stress criteria 22 | # 1 direction in first row 23 | # 2 direction in second row 24 | 25 | # failure strength in compression 26 | #Fc = matrix([[-1250.0, -600.0], 27 | # [-200.0, -120.0]]) # Mpa 28 | # 29 | ##failure strength in tension 30 | #Ft = matrix([[1500, 1000] 31 | # [50, 30]]) # Mpa 32 | # 33 | ##Failure strength in shear 34 | #Fs = matrix( [100, 70] ) # Shear 35 | 36 | Fc1 = [-1250, -600] # Compression 1 direction 37 | Fc2 = [-200, -120] # Compression 2 direction 38 | Ft1 = [1500, 1000] # Tension 1 direction 39 | Ft2 = [50, 30] # Tension 2 direction 40 | Fs = [100, 70] # Shear 41 | 42 | # F1 = Ft(1); 43 | # F2 = Ft(1); 44 | # F6 = Fs(1); 45 | 46 | for c in range(2):# mattype 47 | factor = 1.9 48 | # right 49 | plot( [Ft1[c], Ft1[c]] , [Fc2[c], Ft2[c]]) 50 | 51 | # left 52 | plot( [Fc1[c], Fc1[c]] , [Fc2[c], Ft2[c]]) 53 | # top 54 | plot( [Fc1[c], Ft1[c]] , [Ft2[c], Ft2[c]]) 55 | # bottom 56 | plot( [Fc1[c], Ft1[c]] , [Fc2[c], Fc2[c]]) 57 | # center horizontal 58 | plot( [Fc1[c], Ft1[c]] , [0, 0]) 59 | # center vertical 60 | plot( [0, 0] , [Fc2[c], Ft2[c]]) 61 | 62 | #xlim([min(Fc1) max(Ft1)]*factor) 63 | #ylim([min(Fc2) max(Ft2)]*factor) 64 | xlabel('$\sigma_1,MPa$') 65 | ylabel('$\sigma_2,MPa$') 66 | title('failure envelope with Max-Stress Criteria') 67 | 68 | def vary_ply_direction_plot(): 69 | ''' 70 | composites calculations 71 | ''' 72 | 73 | 74 | th = arange(0, 180.1, 0.1) * (pi/180) 75 | deg = th*180/pi 76 | 77 | Exbar = zeros(len(th)) 78 | Eybar = zeros(len(th)) 79 | Gxybar = zeros(len(th)) 80 | 81 | for i,theta in enumerate(th): 82 | 83 | # lamina thickness 84 | h = 1 85 | ## Initial lamina Properties 86 | #E1 = 147.0 # GPa 87 | #E2 = 10.3 # GPa 88 | #12 = 7.0 # GPa 89 | #u12 = 0.27 90 | #u21 = E2*Nu12/E1 91 | 92 | E1 = 5730000 93 | E2 = 5730000 94 | G12 = 440000 95 | Nu12 = 0.07 96 | Nu21 = E2*Nu12/E1 97 | 98 | ## Calculate stiffness 99 | Q = matrix(zeros((3,3))) 100 | Q[0,0] = E1 / (1-Nu12*Nu21) 101 | Q[1,1] = E2 / (1-Nu12*Nu21) 102 | Q[2,2] = G12 103 | Q[0,1] = E2*Nu12 / (1-Nu12*Nu21) 104 | Q[1,0] = E2*Nu12 / (1-Nu12*Nu21) 105 | n = sin(theta) 106 | m = cos(theta) 107 | R = matrix([[m**2, n**2, m*n], [n**2, m**2, -m*n], [-2*m*n, 2*m*n, (m**2-n**2)]]) 108 | T = matrix([[m**2, n**2, 2*m*n],[n**2, m**2, -2*m*n],[-m*n, m*n, (m**2-n**2)]]) 109 | Qbar = solve(T,Q)*R 110 | 111 | Aij = Qbar*h 112 | # laminate Stiffness 113 | # | Exbar Eybar Gxybar | 114 | # A = | vxybar vyxbar etasxbar | 115 | # | etaxsbar etaysbar etasybar | 116 | 117 | # laminate Comnpliance 118 | aij = Aij.I 119 | # material properties for whole laminate (Daniel, pg183) 120 | Exbar[i] = 1 / (h*aij[0,0]) 121 | Eybar[i] = 1 / (h*aij[1,1]) 122 | Gxybar[i] = 1 / (h*aij[2,2]) 123 | 124 | 125 | fig1 = plt.figure(1, figsize=(12,8), frameon=False) 126 | plt.plot(deg, Exbar, label = r"Modulus: $E_x$") 127 | plt.plot(deg, Eybar, label = r"Modulus: $E_y$") 128 | plt.plot(deg, Gxybar, label = r"Modulus: $G_{xy}$") 129 | plt.title("Constitutive Properties in various angles") 130 | plt.xlabel("deg") 131 | plt.ylabel("modulus, GPa") 132 | plt.legend(loc='best') 133 | plt.show() 134 | 135 | 136 | def qbar_transformtion(): 137 | 138 | # -*- coding: utf-8 -*- 139 | """ 140 | Created on Fri Oct 16 08:45:22 2015 141 | 142 | @author: ngordon 143 | 144 | """ 145 | 146 | 147 | 148 | theta = np.linspace(-90,90,100) * pi/180 149 | 150 | s_xy = matrix([[100], 151 | [10], 152 | [5]]) 153 | 154 | s_12 = matrix(np.zeros((3,100))) 155 | 156 | for i,th in enumerate(theta): 157 | n = sin(th) 158 | m = cos(th) 159 | T = matrix([[m**2, n**2, 2*m*n], 160 | [n**2, m**2, -2*m*n], 161 | [-m*n, m*n, (m**2-n**2)]]) 162 | s_12[:,i] = T*s_xy 163 | 164 | fig = mp.figure(1,figsize=(8,8)) 165 | mp.plot(theta, array(s_12[0,:])[0], label = '$\sigma_{11},MPa$' ) 166 | mp.plot(theta, array(s_12[1,:])[0], label = '$\sigma_{22},MPa$' ) 167 | mp.plot(theta, array(s_12[2,:])[0], label = '$\sigma_{12},MPa$' ) 168 | mp.legend(loc='lower left') 169 | mp.xlabel("$/theta, rad$") ; mp.ylabel("Stress, MPa") 170 | mp.show() 171 | 172 | Ex = 150e9 173 | Ey = 150e9 174 | vxy = 0.248 175 | Gxy = 4.4e9 176 | vy = 0.458 177 | Gyx = Ey / (2*(1+vy)) 178 | vyx = Ey*vxy/Ex 179 | 180 | Q = matrix(np.zeros((3,3))) 181 | Qbar = np.zeros((3,3,len(theta))) 182 | 183 | Q[0,0] = Ex / (1-vxy*vyx) 184 | Q[1,1] = Ey / (1-vxy*vyx) 185 | Q[2,2] = Gyx 186 | Q[0,1] = Ey*vxy / (1-vxy*vyx) 187 | Q[1,0] = Q[0,1] 188 | 189 | for i in range(len(theta)): 190 | n = sin(theta[i]) 191 | m = cos(theta[i]) 192 | R = matrix([ [m**2, n**2, m*n] , [n**2, m**2, -m*n] , [-2*m*n, 2*m*n, (m**2-n**2)] ]) 193 | T = matrix([ [m**2, n**2, 2*m*n] , [n**2, m**2, -2*m*n] , [-m*n, m*n, (m**2-n**2)] ]) 194 | Qbar[:,:,i] = np.linalg.solve(T, Q)*R 195 | 196 | Qbar11 = Qbar[0,0,:] 197 | Qbar22 = Qbar[1,1,:] 198 | Qbar66 = Qbar[2,2,:] 199 | Qbar12 = Qbar[0,1,:] 200 | Qbar16 = Qbar[0,2,:] 201 | Qbar26 = Qbar[1,2,:] 202 | 203 | # plot theta as a function of time 204 | fig = mp.figure(2,figsize=(8,8)) 205 | ax1 = fig.add_subplot(211) 206 | ax1.plot(theta,Qbar11, label = "Qbar11") 207 | ax1.plot(theta,Qbar22, label = "Qbar22") 208 | ax1.plot(theta,Qbar66, label = "Qbar66") 209 | mp.legend(loc='lower left') 210 | ax1.set_xlabel('theta') 211 | ax1.set_ylabel('Q') 212 | fig.show() 213 | 214 | 215 | # plot theta as a function of time 216 | fig = mp.figure(3,figsize=(8,8)) 217 | ax2 = fig.add_subplot(212) 218 | ax2.plot(theta,Qbar12, label = "Qbar12") 219 | ax2.plot(theta,Qbar16, label = "Qbar16") 220 | ax2.plot(theta,Qbar26, label = "Qbar26") 221 | mp.legend(loc='lower left') 222 | ax2.set_xlabel('theta') 223 | ax2.set_ylabel('Q') 224 | fig.show() 225 | 226 | 227 | def laminate_gen(lamthk=1.5, symang = [45,0,90], plyratio=2.0, matrixlayers=False, nonsym=False ): 228 | ''' 229 | ## function created to quickly create laminates based on given parameters 230 | lamthk=1.5 # total #thickness of laminate 231 | symang = [45,0,90, 30] #symmertic ply angle 232 | plyratio=2.0 # lamina/matrix 233 | matrixlayers=False # add matrix layers between lamina plys 234 | nonsym=False # symmetric 235 | 236 | #ply ratio can be used to vary the ratio of thickness between a matrix ply 237 | and lamina ply. if the same thickness is desired, plyratio = 1, 238 | if lamina is 2x as thick as matrix plyratio = 2 239 | ''' 240 | 241 | import numpy as np 242 | if matrixlayers: 243 | nl = (len(symang)*2+1)*2 244 | nm = nl-len(symang)*2 245 | nf = len(symang)*2 246 | tm = lamthk / (plyratio*nf + nm) 247 | tf = tm*plyratio 248 | ang = np.zeros(nl/2) 249 | mat = 2*np.ones(nl/2) # orthotropic fiber and matrix = 1, isotropic matrix=2, 250 | mat[1:-1:2] = 1 # [2 if x%2 else 1 for x in range(nl//2) ] 251 | ang[1:-1:2] = symang[:] 252 | thk = tm*np.ones(1,nl/2) 253 | thk[2:2:-1] = tf 254 | lamang = list(symang) + list(symang[::-1]) 255 | ang = list(ang) + list(ang[::-1]) 256 | mat = list(mat) + list(mat[::-1]) 257 | thk = list(thk) + list(thk[::-1]) 258 | else: # no matrix layers, ignore ratio 259 | if nonsym: 260 | nl = len(symang) 261 | mat =[1]*nl 262 | thk = list(lamthk/nl*np.ones(nl)) 263 | lamang = symang[:] 264 | ang = symang[:] 265 | else: 266 | nl = len(symang)*2 267 | mat = list(3*np.ones(nl)) 268 | thk = list(lamthk/nl*np.ones(nl)) 269 | lamang = list(symang) + list(symang[::-1]) 270 | ang = list(symang) + list(symang[::-1]) 271 | 272 | return thk,ang,mat,lamang 273 | 274 | def laminate_calcs(): 275 | 276 | ''' 277 | code to compute composite properties 278 | 279 | ''' 280 | import numpy as np 281 | # suppress scientific notation 282 | #np.set_printoptions(suppress=False,precision=2) 283 | np.set_printoptions(precision=3, linewidth=200) 284 | 285 | from numpy import pi, linspace, zeros, matrix, sin, cos 286 | import matplotlib.pyplot as plt 287 | from scipy import linalg 288 | 289 | import matplotlib 290 | matplotlib.rcParams['figure.figsize'] = 10,8 291 | 292 | plt.close('all') 293 | 294 | # pg 125 Voyiadjis 295 | wid = 10 # plate width 296 | H = 0.8 # plate thickness 297 | area = wid*H 298 | ang = [0, 45, 90]# [0, 90, 90, 0] # [0 90 90 0]# [90 0 90 0 0 90 0 90] # [30 -30 0 0 -30 30] # 299 | thk = zeros(len(ang)) + H/len(ang) #meters 300 | nlay = len(thk) 301 | z = linspace(-H/2,H/2,nlay+1) 302 | 303 | # Engineering contants for a single ply 304 | E1 = 8.49e6 305 | E2 = 8.49e6 306 | E3 = 8.49e6 307 | nu12 = 0.06 308 | nu13 = 0.06 309 | nu23 = 0.06 310 | G12 = 1.076e6 311 | G13 = 1.076e6 312 | G23 = 1.076e6 313 | dT = 0 # change in temp 314 | alpha1 = 0 # coefficient of thermal expansion in 1 315 | alpha2 = 0 # coefficient of thermal expansion in 1 316 | 317 | 318 | # # Reduced compliance matrix 319 | # S11 = 1/E1 320 | # S22 = 1/E2 321 | # S66= 1/G12 322 | # S12 = -nu12/E1 323 | # S = [S11 S12 0 324 | # S12 S22 0 325 | # 0 0 S66] 326 | # compliance matrix 327 | S6 = zeros((6,6)) 328 | S6[0,0] = 1/E1 329 | S6[1,1] = 1/E2 330 | S6[2,2] = 1/E3 331 | S6[3,3] = 1/G23 332 | S6[4,4] = 1/G13 333 | S6[5,5] = 1/G12 334 | S6[0,1] = -nu12/E1 335 | S6[1,0] = S6[0,1] 336 | S6[0,2] = -nu13/E1 337 | S6[2,0] = S6[0,2] 338 | S6[1,2] = -nu23/E2 339 | S6[2,1] = S6[1,2] 340 | S6 341 | C6 = linalg.inv(S6) 342 | 343 | # # reduced stiffness matrix 344 | # Q11 = S22 / (S11*S22-S12**2) 345 | # Q12 = -S12 / (S11*S22 - S12**2) 346 | # Q22 = S11 / (S11*S22 - S12**2) 347 | # Q66 = 1/S66 348 | # # local coordinates 349 | # Q = [Q11 Q12 0Q12 Q22 00 0 Q66] 350 | # # inv(S) == Q 351 | 352 | Q = matrix(zeros((3,3))) 353 | Q[0,0] = C6[0,0] - C6[0,2]**2/C6[2,2] 354 | Q[0,1] = C6[0,1] - C6[0,2]*C6[1,2]/C6[2,2] 355 | Q[1,1] = C6[1,1] - C6[1,2]**2/C6[2,2] 356 | Q[2,2] = C6[5,5] # actually Q66 in many texts 357 | Q[1,0] = Q[0,1] 358 | 359 | 360 | A = matrix(zeros((3,3))) 361 | B = matrix(zeros((3,3))) 362 | D = matrix(zeros((3,3))) 363 | 364 | # strain with curvature and thermal 365 | 366 | # calcualte strain at each interface 367 | kap1 = 0 # curvature coefficient 368 | kap2 = 0 # curvature coefficient 369 | kap12 = 0 #curvature coefficient 370 | eps1app = 1e-3 # aplpied strain 371 | eps2app = 0 # applied strain in 2 372 | eps12app = 0 373 | strain6 = matrix([ [eps1app], [eps2app], [eps12app], [kap1], [kap2], [kap12] ]) 374 | 375 | strain = matrix(zeros((3,len(z)))) 376 | strainx0 = eps1app +kap1*z - alpha1*dT 377 | strainy0 = eps2app + kap2*z- alpha2*dT 378 | strainxy0 = 0 379 | strain[0,:] = strainx0 380 | strain[1,:] = strainy0 381 | strain[2,:] = strainxy0 382 | 383 | sigma = matrix(zeros((3,2*nlay))) 384 | zplot = zeros(2*nlay) 385 | 386 | for n,k in enumerate(range(0,2*nlay,2)): 387 | s = sin(ang[n]*pi/180) 388 | c = cos(ang[n]*pi/180) 389 | T = matrix( [[c**2, s**2, 2*c*s], [s**2, c**2, -2*c*s,], [-c*s, c*s, (c**2-s**2)]] ) 390 | R = matrix( [[c**2, s**2, c*s], [s**2, c**2, -c*s,], [-2*c*s, 2*c*s, (c**2-s**2)]] ) 391 | Qbar = T.I*Q*R 392 | A = A + Qbar * (z[n+1]-z[n]) 393 | # coupling stiffness 394 | B = B + 0.5*Qbar * (z[n+1]**2-z[n]**2) 395 | # bending or flexural laminate stiffness relating moments to curvatures 396 | D = D + (1/3)*Qbar * (z[n+1]**3-z[n]**3) 397 | # stress is calcuated at top and bottom of each ply 398 | sigma[:,k] = Qbar*strain[:,n] 399 | sigma[:,k+1] = Qbar*strain[:,n+1] 400 | zplot[k] = z[n] 401 | zplot[k+1] = z[n+1] 402 | 403 | 404 | f, axarr = plt.subplots(2, 3)#, sharey=True) 405 | plt.tight_layout() 406 | axarr[0,0].plot(strain[0,:].tolist()[0] , z) 407 | axarr[0,0].set_xlabel('strain 1') 408 | axarr[0,1].plot(strain[1,:].tolist()[0] , z) 409 | axarr[0,1].set_xlabel('strain 2') 410 | axarr[0,2].plot(strain[2,:].tolist()[0] , z) 411 | axarr[0,2].set_xlabel('strain 3') 412 | 413 | axarr[1,0].plot(sigma[0,:].tolist()[0] , zplot) 414 | axarr[1,0].set_xlabel('stress 1') 415 | xlim([np.min(sigma), np.max(sigma)]) 416 | axarr[1,1].plot(sigma[1,:].tolist()[0] , zplot) 417 | axarr[1,1].set_xlabel('stress 2') 418 | xlim([np.min(sigma), np.max(sigma)]) 419 | axarr[1,2].plot(sigma[2,:].tolist()[0] , zplot) 420 | axarr[1,2].set_xlabel('stress 3') 421 | xlim([np.min(sigma), np.max(sigma)]) 422 | plt.tight_layout() 423 | 424 | 425 | # laminate stiffness matrix 426 | G = matrix(zeros((6,6))) 427 | G[0:3,0:3] = A 428 | G[0:3,3:6] = B 429 | G[3:6,0:3] = B 430 | G[3:6,3:6] = D 431 | 432 | print(G) 433 | # laminatee compliance 434 | g = G.I 435 | 436 | NM = G*strain6 437 | Nx = NM[0] 438 | 439 | Exbar = 1 / (H*g[0,0]) 440 | Eybar = 1 / (H*g[1,1]) 441 | Gxybar = 1 / (H*g[2,2]) 442 | nuxybar = -g[0,1]/g[0,0] 443 | nuyxbar = -g[0,1]/g[1,1] 444 | 445 | print('Ex=%f'%Exbar) 446 | print('Ey=%f'%Eybar) 447 | print('Gxy=%f'%Gxybar) 448 | 449 | 450 | 451 | #### Needs validation, Currently incorrect 452 | def composite_plate(): 453 | # -*- coding: utf-8 -*- 454 | 455 | import numpy as np 456 | import numpy.matlib # needed to create a zeros matrix 457 | from matplotlib import cm 458 | import matplotlib.pyplot as plt 459 | np.set_printoptions(linewidth=300) 460 | from mpl_toolkits.mplot3d import axes3d 461 | 462 | #from IPython import get_ipython 463 | #ipython = get_ipython() 464 | #ipython.magic('matplotlib') 465 | 466 | plt.rcParams['figure.figsize'] = (10, 8) 467 | plt.rcParams['font.size'] = 12 468 | plt.rcParams['legend.fontsize'] = 8 469 | #plt.rcParams['title.fontsize'] = 12 470 | 471 | """ 472 | Code to anaylze composite plates using classical laminate plate theory 473 | 474 | clpt = classical lamianted plate theory 475 | 476 | First written by Neal Gordon, January, 2014 477 | This program was developed to determine the linear-elastic properties 478 | of laminate composites 479 | 480 | suffix bar refers to global coordinate system(CS), while unspecified refers to 481 | material CS. Laminate model with plane stress assumption. sigma_3 = tau_23 = tau_13 = 0 482 | ----References---- 483 | Daniel, I. M., & Ishai, O. (2006). Engineering Mechanics of Composite Materials (2nd ed.). Oxford University Press. 484 | Hyer, M. W. (1998). Stress Analysis of Fiber-Reinforced Composite Materials. 485 | Reddy, J. N. (2004). Mechanics of Laminated Composite PLates and Shells: Theory and Analysis (2nd ed.). 486 | Herakovich, C. T. (n.d.). Mechanics of Fibrous Composites. 487 | """ 488 | 489 | 490 | ######################## Laminate properties ############################# 491 | 492 | # The custom function laminate_gen generates the properties of a laminate 493 | # ith the following parameters. 494 | 495 | epsxapp = 0 #10e-4 #10e-4 496 | Nxapp = 1 497 | 498 | # properties per ply 499 | #ang = [90, 0, 90, 0, 0, 90, 0, 90] # degrees 500 | #ang = [90, 0, 90, 0, 0, 90, 0, 90] 501 | ang = [45, 0, 0, 45, 0 ,45, 45, 45, 45, 45, 0, 45, 0, 0, 45] 502 | lamang = ang 503 | 504 | 505 | thk = [2]*len(ang) # mm 506 | mat = [0]*len(ang) # material index, starting with 0 507 | 508 | W = 1.283 # laminate width 509 | L = 3.8 # laminate length 510 | nl = len(mat) # number of layers 511 | H = np.sum(thk) # laminate thickness 512 | area = W*H # total cross-sectioal area of laminate 513 | 514 | ################### Mechanical and thermal loading ####################### 515 | 516 | Ti = 0 # initial temperature (C) 517 | Tf = 0 # final temperature (C) 518 | dT = Tf-Ti 519 | # Applied loads 520 | # NMbarapp = Nx*1000/W , Nx is the composite load (kN) 521 | # applied load of laminate. This is used to simulate a load controlled tensile test 522 | # Nx Ny Nxy Mx My Mxy 523 | NMbarapp = np.matrix([Nxapp, 0, 0, 0, 0, 0 ]).T*1000/W # load or moments per unit length (kN/m) 524 | # Applied strains 525 | # kapx = 1/m , units are in mm so 2.22*m**-1 = 2.22e-3 *m**-1 526 | # 5e-4 = 0.05# strain 527 | # epsx epsy epsxy kapx kapy kapxy 528 | # epsxapp = 1e-5 529 | epsilonbarapp = np.matrix([epsxapp, 0, 0, 0, 0, 0, ]).T #' # used to simulate strain controlled tensile test 530 | 531 | ######################## Material properties ############################# 532 | 533 | # Material Properties from Hyer pg58 534 | # # material 1 in column 1, material 2 in colum 2 etc, 535 | # # graphite-polymer lamina 536 | # multiple lamina properties can be given by adding columns 537 | # # material 1 in column 1, material 2 in colum 2 etc 538 | E1 = [8.49e6 , 400e3] 539 | E2 = [8.49e6, 400e3] 540 | E3 = [8.49e6, 400e3] 541 | G12 = [1.076e6 , 170e3] 542 | G13 = [1.076e6 , 170e3] 543 | G23 = [1.076e6 , 170e3] 544 | nu12 = [1.076e6 , 0.17] 545 | nu13 = [0.06, 0.17] 546 | nu23 = [0.06, 0.17] 547 | alpha1 = [0, -0.3e-6] # coefficient of thermal expansion in 1 548 | alpha2 = [0, -0.3e-6] # coefficient of thermal expansion in 1 549 | alpha12 = [0, 0] # coefficient of thermal expansion in 12 550 | 551 | alpha = [np.matrix([alpha1[k], alpha2[k], alpha12[k]]).T for k in range(len(alpha1))] 552 | 553 | ######################## Material properties ############################# 554 | 555 | # lamina bound coordinates 556 | z = [0.0]*(nl+1) # lamina coordinates 557 | zmid = [0.0]*(nl) 558 | z[0] = -H/2 559 | 560 | for k in range(nl): 561 | z[k+1] = z[k] + thk[k] 562 | zmid[k] = z[k] + thk[k]/2 563 | 564 | # calculate Compliance and reduced stiffness matrix 565 | matname = [int(k) for k in set(mat)] 566 | # create a 3 dimension matrix access values with Q[3rd Dim, row, column] , Q[0,2,2] 567 | Q = [np.matrix(np.zeros((3,3))) for k in set(mat)] 568 | 569 | for k in range(len(matname)): 570 | # compliance matrix 571 | S6 = np.matrix(np.zeros((6,6))) 572 | 573 | S6[0,0] = 1/E1[k] 574 | S6[1,1] = 1/E2[k] 575 | S6[2,2] = 1/E3[k] 576 | S6[3,3] = 1/G23[k] 577 | S6[4,4] = 1/G13[k] 578 | S6[5,5] = 1/G12[k] 579 | S6[0,1] = -nu12[k]/E1[k] 580 | S6[1,0] = S6[0,1] 581 | S6[0,2] = -nu13[k]/E1[k] 582 | S6[2,0] = S6[0,2] 583 | S6[1,2] = -nu23[k]/E2[k] 584 | S6[2,1] = S6[1,2] 585 | # reduced stiffness matrix 586 | Q[k][0,0] = S6[1,1] / ( S6[0,0]*S6[1,1]-S6[0,1]**2) 587 | Q[k][0,1] = -S6[0,1] / ( S6[0,0]*S6[1,1]-S6[0,1]**2) 588 | Q[k][1,1] = S6[0,0] / ( S6[0,0]*S6[1,1]-S6[0,1]**2) 589 | Q[k][2,2] = 1/S6[5,5] # actually Q66 in many texts 590 | Q[k][1,0] = Q[k][0,1] 591 | # calcluating Q from the Compliance matrix may cause cancellation errors 592 | 593 | # mechanical property calc 594 | A = np.matlib.zeros((3,3)) #extensional stiffness, in-plane laminate moduli 595 | B = np.matlib.zeros((3,3)) # coupling stiffness 596 | D = np.matlib.zeros((3,3)) # flexural or bending stiffness 597 | Nhatth = np.matlib.zeros((3,1)) # unit thermal force in global CS 598 | Mhatth = np.matlib.zeros((3,1)) # unit thermal moment in global CS 599 | NMhatth = np.matlib.zeros((6,1)) 600 | alphabar = [np.matlib.zeros((3,1)) for k in range(nl)] 601 | # transformation reminders - see Herakovich for details 602 | # sig1 = T*sigx 603 | # sigx = inv(T)*sig1 604 | # eps1 = R*epsx 605 | # epsx = inv(R)*epsx 606 | # sigx = inv(T)*Q*R*epsx 607 | # Qbar = inv(T)*Q*R 608 | # Sbar = inv(R)*inv(Q)*R 609 | # determine mechanical properties 610 | 611 | for k in range(nl): # loop through each ply to calculate the properties 612 | n = np.sin(np.deg2rad(ang[k])) 613 | m = np.cos(np.deg2rad(ang[k])) 614 | T = np.matrix([[m**2, n**2, 2*m*n], [n**2, m**2, -2*m*n], [-m*n, m*n, (m**2-n**2)]]) 615 | R = np.matrix([[m**2, n**2, m*n ], [n**2, m**2, -m*n], [-2*m*n, 2*m*n, (m**2-n**2)]]) 616 | Qbar = T.I*Q[mat[k]]*R # Convert to global CS 617 | A += Qbar * (z[k+1]-z[k]) # coupling stiffness 618 | B += 0.5*Qbar * (z[k+1]**2-z[k]**2) # bending or flexural laminate stiffness relating moments to curvatures 619 | D += (1/3)*Qbar * (z[k+1]**3-z[k]**3) 620 | alphabar[k] = R.I*alpha[mat[k]] # Convert to global CS 621 | Nhatth += Qbar*alphabar[k]*(z[k+1] - z[k]) # Hyer method for calculating thermal unit loads[] 622 | Mhatth += 0.5*Qbar*alphabar[k]*(z[k+1]**2-z[k]**2) 623 | 624 | NMhatth[:3] = Nhatth 625 | NMhatth[3:] = Mhatth 626 | 627 | NMbarth = NMhatth*dT # resultant thermal loads 628 | # laminate stiffness matrix 629 | ABD = np.matlib.zeros((6,6)) 630 | ABD[0:3, 3:6] = B 631 | ABD[3:6, 0:3] = B 632 | ABD[0:3, 0:3] = A 633 | ABD[3:6, 3:6] = D 634 | 635 | 636 | # laminatee compliance 637 | # abd = [a bc d] 638 | abcd = ABD.I 639 | a = abcd[0:3,0:3] 640 | b = abcd[0:3,3:7] 641 | c = abcd[3:7,0:3] 642 | d = abcd[3:7,3:7] 643 | # Average composite properties, pg 183 daniel ishai, pg 325 hyer 644 | # coefficients of thermal epansion for entire composite 645 | alpha_composite = a*Nhatth # laminate CTE 646 | # effective laminate moduli 647 | Exbar = 1 / (H*a[0,0]) 648 | Eybar = 1 / (H*a[1,1]) 649 | # effective shear modulus 650 | Gxybar = 1 / (H*a[2,2]) 651 | # effective poisson ratio 652 | nuxybar = -a[0,1]/a[0,0] 653 | nuyxbar = -a[0,1]/a[1,1] 654 | # effective laminate shear coupling coefficients 655 | etasxbar = a[0,2]/a[2,2] 656 | etasybar = a[1,2]/a[2,2] 657 | etaxsbar = a[2,0]/a[0,0] 658 | etaysbar = a[2,1]/a[1,1] 659 | # Laminate compliance matrix 660 | 661 | LamComp = np.matrix([[1/Exbar, -nuyxbar/Eybar, etasxbar/Gxybar], 662 | [-nuxybar/Exbar, 1/Eybar, etasybar/Gxybar], 663 | [ etaxsbar/Exbar, etaysbar/Eybar, 1/Gxybar]]) 664 | LamMod = LamComp.I # Laminate moduli 665 | # combines applied loads and applied strains 666 | NMbarapptotal = NMbarapp + ABD*epsilonbarapp 667 | # Composite respone from applied mechanical loads and strains. Average 668 | # properties only. Used to compare results from tensile test. 669 | epsilon_composite = abcd*NMbarapptotal 670 | sigma_composite = ABD*epsilon_composite/H 671 | ## determine thermal load and applied loads or strains Hyer pg 435,452 672 | Nx = NMbarapptotal[0]*W/1000 # units kiloNewtons, total load as would be applied in a tensile test 673 | Ny = NMbarapptotal[1]*L/1000 # units kN 674 | epsilonbarth = abcd*NMbarth 675 | epsilonbarapptotal = epsilonbarapp + abcd*NMbarapp # includes applied loads and strains 676 | # Note, epsilonbarapptotal == abcd*NMbarapptotal 677 | 678 | ''' 679 | Plots are incorrect, need to check 680 | ###################### prepare data for plotting########################## 681 | 682 | 683 | zmid = [0.0]*8 684 | 685 | 686 | 687 | # create a 3 dimension matrix access values with Q[3rd Dim, row, column] , Q[0,2,2] 688 | 689 | epsilonbar = np.matlib.zeros((3,len(z))) 690 | sigmabar = np.matlib.zeros((3,len(z))) 691 | epsilon = np.matlib.zeros((3,len(z))) 692 | sigma = np.matlib.zeros((3,len(z))) 693 | epsilonbar_app = np.matlib.zeros((3,len(z))) 694 | sigmabar_app = np.matlib.zeros((3,len(z))) 695 | epsilon_app = np.matlib.zeros((3,len(z))) 696 | sigma_app = np.matlib.zeros((3,len(z))) 697 | epsilonbar_th = np.matlib.zeros((3,len(z))) 698 | sigmabar_th = np.matlib.zeros((3,len(z))) 699 | epsilon_th = np.matlib.zeros((3,len(z))) 700 | sigma_th = np.matlib.zeros((3,len(z))) 701 | 702 | epsilon_app_plot = np.matlib.zeros((3,2*nl)) 703 | epsilonbar_app_plot = np.matlib.zeros((3,2*nl)) 704 | sigma_app_plot = np.matlib.zeros((3,2*nl)) 705 | sigmabar_app_plot = np.matlib.zeros((3,2*nl)) 706 | epsilon_th_plot = np.matlib.zeros((3,2*nl)) 707 | epsilonbar_th_plot = np.matlib.zeros((3,2*nl)) 708 | sigma_th_plot = np.matlib.zeros((3,2*nl)) 709 | sigmabar_th_plot = np.matlib.zeros((3,2*nl)) 710 | epsilonplot = np.matlib.zeros((3,2*nl)) 711 | epsilonbarplot = np.matlib.zeros((3,2*nl)) 712 | sigmaplot = np.matlib.zeros((3,2*nl)) 713 | sigmabarplot = np.matlib.zeros((3,2*nl)) 714 | zplot = np.zeros((2*nl)) 715 | 716 | for k in range(nl): 717 | n = np.sin(np.deg2rad(ang[k])) 718 | m = np.cos(np.deg2rad(ang[k])) 719 | T = np.matrix([[m**2, n**2, 2*m*n], [n**2, m**2, -2*m*n], [-m*n, m*n, (m**2-n**2)]]) 720 | R = np.matrix([[m**2, n**2, m*n ], [n**2, m**2, -m*n], [-2*m*n, 2*m*n, (m**2-n**2)]]) 721 | 722 | zplot[2*k:2*(k+1)] = z[k:(k+2)] 723 | 724 | Qbar = T.I*Q[mat[k]]*R # Convert to global CS 725 | 726 | # Global stresses and strains, applied load only 727 | epsbar1 = epsilonbarapptotal[0:3] + z[k]*epsilonbarapptotal[3:7] 728 | epsbar2 = epsilonbarapptotal[0:3] + z[k+1]*epsilonbarapptotal[3:7] 729 | sigbar1 = Qbar*epsbar1 730 | sigbar2 = Qbar*epsbar2 731 | epsilonbar_app[:,k:k+2] = np.column_stack((epsbar1,epsbar2)) 732 | sigmabar_app[:,k:k+2] = np.column_stack((sigbar1,sigbar2)) 733 | 734 | # Local stresses and strains, appplied load only 735 | eps1 = R*epsbar1 736 | eps2 = R*epsbar2 737 | sig1 = Q[mat[k]]*eps1 738 | sig2 = Q[mat[k]]*eps2 739 | epsilon_app[:,k:k+2] = np.column_stack((eps1,eps2)) 740 | sigma_app[:,k:k+2] = np.column_stack((sig1,sig2)) 741 | 742 | epsilon_app_plot[:, 2*k:2*(k+1)] = np.column_stack((eps1,eps2)) 743 | epsilonbar_app_plot[:, 2*k:2*(k+1)] = np.column_stack((epsbar1,epsbar2)) 744 | sigma_app_plot[:, 2*k:2*(k+1)] = np.column_stack((sig1,sig2)) 745 | sigmabar_app_plot[:, 2*k:2*(k+1)] = np.column_stack((sigbar1,sigbar2)) 746 | 747 | # Global stress and strains, thermal loading only 748 | epsbar1 = (alpha_composite - alphabar[k])*dT 749 | sigbar1 = Qbar*epsbar1 750 | epsilonbar_th[:,k:k+2] = np.column_stack((epsbar1,epsbar1)) 751 | sigmabar_th[:,k:k+2] = np.column_stack((sigbar1,sigbar1)) 752 | 753 | # Local stress and strains, thermal loading only 754 | eps1 = R*epsbar1 755 | sig1 = Q[mat[k]]*eps1 756 | epsilon_th[:,k:k+2] = np.column_stack((eps1,eps1)) 757 | sigma_th[:,k:k+2] = np.column_stack((sig1,sig1)) 758 | 759 | # Create array for plotting purposes only 760 | epsilon_th_plot[:, 2*k:2*(k+1)] = np.column_stack((eps1,eps1)) 761 | epsilonbar_th_plot[:, 2*k:2*(k+1)] = np.column_stack((epsbar1,epsbar1)) 762 | sigma_th_plot[:, 2*k:2*(k+1)] = np.column_stack((sig1,sig1)) 763 | sigmabar_th_plot[:, 2*k:2*(k+1)] = np.column_stack((sigbar1,sigbar1)) 764 | 765 | # global stresses and strains, bar ,xy coord including both applied 766 | # loads and thermal loads. NET, or relaized stress-strain 767 | epsbar1 = epsilonbarapptotal[0:3] + z[k]*epsilonbarapptotal[3:7] + (alpha_composite - alphabar[k])*dT 768 | epsbar2 = epsilonbarapptotal[0:3] + z[k+1]*epsilonbarapptotal[3:7] + (alpha_composite - alphabar[k])*dT 769 | sigbar1 = Qbar*epsbar1 770 | sigbar2 = Qbar*epsbar2 771 | epsilonbar[:,k:k+2] = np.column_stack((epsbar1,epsbar2)) 772 | sigmabar[:,k:k+2] = np.column_stack((sigbar1,sigbar2)) 773 | 774 | # local stresses and strains , 12 coord, includes both applied loads 775 | # and thermal load. NET , or realized stress and strain 776 | eps1 = R*epsbar1 777 | eps2 = R*epsbar2 778 | sig1 = Q[mat[k]]*eps1 779 | sig2 = Q[mat[k]]*eps2 780 | epsilon[:,k:k+2] = np.column_stack((eps1,eps2)) 781 | sigma[:,k:k+2] = np.column_stack((sig1,sig2)) 782 | 783 | # Create array for plotting purposes only 784 | epsilonplot[:, 2*k:2*(k+1)] = np.column_stack((eps1,eps2)) 785 | epsilonbarplot[:, 2*k:2*(k+1)] = np.column_stack((epsbar1,epsbar2)) 786 | sigmaplot[:, 2*k:2*(k+1)] = np.column_stack((sig1,sig2)) 787 | sigmabarplot[:, 2*k:2*(k+1)] = np.column_stack((sigbar1,sigbar2)) 788 | 789 | ############################ Plotting ##################################### 790 | legendlab = ['composite','total','thermal','applied'] 791 | 792 | # global stresses and strains 793 | ## initialize the figure 794 | fig = plt.figure() 795 | fig.canvas.set_window_title('global-stresses-strains') 796 | mylw = 1.5 #linewidth 797 | stresslabel = ['$\sigma_x,\ MPa$','$\sigma_y,\ MPa$','$\\tau_{xy},\ MPa$'] 798 | strainlabel = ['$\epsilon_x$','$\epsilon_y$','$\gamma_{xy}$'] 799 | for k in range(3): 800 | ## the top axes 801 | ax1 = fig.add_subplot(2,3,k+1) 802 | ax1.set_ylabel('thickness,z') 803 | ax1.set_xlabel(strainlabel[k]) 804 | ax1.set_title(' Ply Strain at $\epsilon=%f$' % (epsxapp*100)) 805 | ax1.ticklabel_format(axis='x', style='sci', scilimits=(1,4)) # scilimits=(-2,2)) 806 | 807 | line1 = ax1.plot(np.array(epsilonbarplot[k,:])[0], zplot, color='blue', lw=mylw) 808 | line2 = ax1.plot(np.array(epsilonbar_th_plot[k,:])[0], zplot, color='red', lw=mylw) 809 | line3 = ax1.plot(np.array(epsilonbar_app_plot[k,:])[0], zplot, color='green', lw=mylw) 810 | line4 = ax1.plot([np.array(epsilon_composite[k])[0], np.array(epsilon_composite[k])[0]],\ 811 | [np.min(z) , np.max(z)], color='black', lw=mylw) 812 | 813 | for k in range(3): 814 | ## the top axes 815 | ax1 = fig.add_subplot(2,3,k+4) 816 | ax1.set_ylabel('thickness,z') 817 | ax1.set_xlabel(stresslabel[k]) 818 | ax1.set_title(' Ply Stress at $\sigma=%f$' % (epsxapp*100)) 819 | ax1.ticklabel_format(axis='x', style='sci', scilimits=(-3,3)) # scilimits=(-2,2)) 820 | 821 | line1 = ax1.plot(np.array(sigmabarplot[k,:])[0], zplot, color='blue', lw=mylw) 822 | line2 = ax1.plot(np.array(sigmabar_th_plot[k,:])[0], zplot, color='red', lw=mylw) 823 | line3 = ax1.plot(np.array(sigmabar_app_plot[k,:])[0], zplot, color='green', lw=mylw) 824 | line4 = ax1.plot([np.array(sigma_composite[k])[0], np.array(sigma_composite[k])[0]],\ 825 | [np.min(z) , np.max(z)], color='black', lw=mylw) 826 | 827 | #plt.savefig('global-stresses-strains.png') 828 | # local stresses and strains 829 | ## initialize the figure 830 | fig = plt.figure() 831 | fig.canvas.set_window_title('local-stresses-strains') 832 | mylw = 1.5 #linewidth 833 | stresslabel = ['$\sigma_1,\ MPa$','$\sigma_2,\ MPa$','$\\tau_{12},\ MPa$'] 834 | strainlabel = ['$\epsilon_1$','$\epsilon_2$','$\gamma_{12}$'] 835 | for k in range(3): 836 | ## the top axes 837 | ax1 = fig.add_subplot(2,3,k+1) 838 | ax1.set_ylabel('thickness,z') 839 | ax1.set_xlabel(strainlabel[k]) 840 | ax1.set_title(' Ply Strain at $\epsilon=%f$' % (epsxapp*100)) 841 | ax1.ticklabel_format(axis='x', style='sci', scilimits=(1,4)) # scilimits=(-2,2)) 842 | 843 | line1 = ax1.plot(np.array(epsilonplot[k,:])[0], zplot, color='blue', lw=mylw) 844 | line2 = ax1.plot(np.array(epsilon_th_plot[k,:])[0], zplot, color='red', lw=mylw) 845 | line3 = ax1.plot(np.array(epsilon_app_plot[k,:])[0], zplot, color='green', lw=mylw) 846 | line4 = ax1.plot([np.array(epsilon_composite[k])[0], np.array(epsilon_composite[k])[0]],\ 847 | [np.min(z) , np.max(z)], color='black', lw=mylw) 848 | 849 | for k in range(3): 850 | ## the top axes 851 | ax1 = fig.add_subplot(2,3,k+4) 852 | ax1.set_ylabel('thickness,z') 853 | ax1.set_xlabel(stresslabel[k]) 854 | ax1.set_title(' Ply Stress at $\sigma=%f$' % (epsxapp*100)) 855 | ax1.ticklabel_format(axis='x', style='sci', scilimits=(-3,3)) # scilimits=(-2,2)) 856 | 857 | line1 = ax1.plot(np.array(sigmaplot[k,:])[0], zplot, color='blue', lw=mylw) 858 | line2 = ax1.plot(np.array(sigma_th_plot[k,:])[0], zplot, color='red', lw=mylw) 859 | line3 = ax1.plot(np.array(sigma_app_plot[k,:])[0], zplot, color='green', lw=mylw) 860 | line4 = ax1.plot([np.array(sigma_composite[k])[0], np.array(sigma_composite[k])[0]],\ 861 | [np.min(z) , np.max(z)], color='black', lw=mylw) 862 | 863 | #plt.savefig('local-stresses-strains.png') 864 | 865 | ### warpage 866 | res = 250 867 | Xplt,Yplt = np.meshgrid(np.linspace(-W/2,W/2,res), np.linspace(-L/2,L/2,res)) 868 | epsx = epsilon_composite[0,0] 869 | epsy = epsilon_composite[1,0] 870 | epsxy = epsilon_composite[2,0] 871 | kapx = epsilon_composite[3,0] 872 | kapy = epsilon_composite[4,0] 873 | kapxy = epsilon_composite[5,0] 874 | ### dispalcement 875 | w = -0.5*(kapx*Xplt**2 + kapy*Yplt**2 + kapxy*Xplt*Yplt) 876 | u = epsx*Xplt # pg 451 hyer 877 | fig = plt.figure('plate-warpage') 878 | ax = fig.gca(projection='3d') 879 | ax.plot_surface(Xplt, Yplt, w+zmid[0], cmap=cm.jet, alpha=0.3) 880 | ###ax.auto_scale_xyz([-(W/2)*1.1, (W/2)*1.1], [(L/2)*1.1, (L/2)*1.1], [-1e10, 1e10]) 881 | ax.set_xlabel('plate width,y-direction,mm') 882 | ax.set_ylabel('plate length,x-direction, mm') 883 | ax.set_zlabel('warpage,mm') 884 | plt.show() 885 | #plt.savefig('plate-warpage') 886 | 887 | ########################### DATA display ################################## 888 | 889 | print('---------------Stress analysis of fibers----------') 890 | ## OUTPUT 891 | 892 | print(Exbar) 893 | print(Eybar) 894 | print(Gxybar) 895 | print(A) 896 | print(alpha_composite) 897 | print(nuxybar) 898 | ''' 899 | 900 | 901 | 902 | 903 | 904 | if __name__=='__main__': 905 | laminate_calcs() --------------------------------------------------------------------------------