Examples¶
exs_spring¶
- Purpose:
Show the basic steps in a finite element calculation.
- Description:
The general procedure in linear finite element calculations is carried out for a simple structure. The steps are:
Define the model
Generate element matrices
Assemble element matrices into the global system of equations
Solve the global system of equations
Evaluate element forces
Consider the system of three linear elastic springs, and the corresponding finite element model. The system of springs is fixed in its ends and loaded by a single load \(F=100 N\) and a stiffness \(k=1500 N/m\)
- Example:
The computation is initialized by importing CALFEM and NumPy, then defining the topology matrix
edofcontaining the degrees of freedom for each element:import numpy as np import calfem.core as cfc import calfem.utils as cfu edof = np.array([ [1, 2], # element 1 between nodes 1 and 2 [2, 3], # element 2 between nodes 2 and 3 [2, 3] # element 3 between nodes 2 and 3 ])The global stiffness matrix
K(3×3) of zeros:K = np.zeros((3, 3)) # empty global stiffness matrix f = np.zeros((3, 1)) # empty global load vectorAnd the load vector
f(3×1) with the load \(F=100\) at DOF 2:f[1] = 100.0 # (N), f[1] corresponds to dof 2Element stiffness matrices are generated by the function
cfc.spring1e. The element propertyepfor the springs contains the spring stiffnesses \(k\) and \(2k\) respectively, where \(k=1500\):k = 1500. # (N/m) ep1 = 2.*k # spring stiffness, element 1 ep2 = k # spring stiffness, element 2 ep3 = 2.*k # spring stiffness, element 3 Ke1 = cfc.spring1e(ep1) # element stiffness matrix, element 1 Ke2 = cfc.spring1e(ep2) # element stiffness matrix, element 2 Ke3 = cfc.spring1e(ep3) # element stiffness matrix, element 3 cfu.disp_h2("Ke1") cfu.disp_array(Ke1, tablefmt='plain') cfu.disp_h2("Ke2") cfu.disp_array(Ke2, tablefmt='plain') cfu.disp_h2("Ke3") cfu.disp_array(Ke3, tablefmt='plain')## Ke1 3.0000e+03 -3.0000e+03 -3.0000e+03 3.0000e+03 ## Ke2 1.5000e+03 -1.5000e+03 -1.5000e+03 1.5000e+03 ## Ke3 3.0000e+03 -3.0000e+03 -3.0000e+03 3.0000e+03The element stiffness matrices are assembled into the global stiffness matrix
Kaccording to the topology:cfc.assem(edof[0, :], K, Ke1) # assemble element stiffness matrix 1 cfc.assem(edof[1, :], K, Ke2) # assemble element stiffness matrix 2 cfc.assem(edof[2, :], K, Ke3) # assemble element stiffness matrix 3 cfu.disp_h2("Stiffness matrix K (N/m):") cfu.disp_array(K, tablefmt='plain')## Stiffness matrix K (N/m): 3.0000e+03 -3.0000e+03 0.0000e+00 -3.0000e+03 7.5000e+03 -4.5000e+03 0.0000e+00 -4.5000e+03 4.5000e+03The global system of equations is solved considering the boundary conditions. DOFs 1 and 3 are fixed (value 0):
bc_dofs = np.array([1, 3]) # dofs with prescribed displacment (0) bc_vals = np.array([0.0, 0.0]) # prescribed displacments a, r = cfc.solveq(K, f, bc_dofs, bc_vals) cfu.disp_h2("Displacements a (m):") cfu.disp_array(a, tablefmt="plain") cfu.disp_h2("Reaction forces r (N):") cfu.disp_array(r, tablefmt="plain")## Displacements a (m): 0.0000e+00 1.3333e-02 0.0000e+00 ## Reaction forces r (N): -4.0000e+01 0.0000e+00 -6.0000e+01Element forces are evaluated from the element displacements. These are obtained from the global displacements
ausing the functioncfc.extract_edand the spring forces are evaluated using the functioncfc.spring1s:ed1 = cfc.extract_ed(edof[0, :], a) # Nodal displacements, element 1 ed2 = cfc.extract_ed(edof[1, :], a) # Nodal displacements, element 2 ed3 = cfc.extract_ed(edof[2, :], a) # Nodal displacements, element 3 es1 = cfc.spring1s(ep1, ed1) # Element force, element 1 es2 = cfc.spring1s(ep2, ed2) # Element force, element 2 es3 = cfc.spring1s(ep3, ed3) # Element force, element 3 cfu.disp_h2("Element forces (N):") print("N1 = "+str(es1)) print("N2 = "+str(es2)) print("N3 = "+str(es3))## Element forces (N): N1 = 40.0 N2 = -20.0 N3 = -40.0
exs_flw_temp1¶
- Purpose:
Analysis of one-dimensional heat flow.
- Description:
Consider a wall built up of concrete and thermal insulation. The outdoor temperature is \(-17°\text{C}\) and the temperature inside is \(20°\text{C}\). At the inside of the thermal insulation there is a heat source yielding \(10\) W/m².
The wall is subdivided into five elements and the one-dimensional spring (analogy) element
spring1eis used. Equivalent spring stiffnesses are \(k_i=\lambda A/L\) for thermal conductivity and \(k_i=A/R\) for thermal surface resistance. Corresponding spring stiffnesses per m² of the wall are:\(k_1 =\)
\(1/0.04\)
\(=\)
\(25.0\)
W/K
\(k_2 =\)
\(1.7/0.070\)
\(=\)
\(24.3\)
W/K
\(k_3 =\)
\(0.040/0.100\)
\(=\)
\(0.4\)
W/K
\(k_4 =\)
\(1.7/0.100\)
\(=\)
\(17.0\)
W/K
\(k_5 =\)
\(1/0.13\)
\(=\)
\(7.7\)
W/K
- Example:
The computation is initialized by importing CALFEM and NumPy. A global system matrix
Kand a heat flow vectorfare defined. The heat source inside the wall is considered by setting \(f_4=10\). The element matricesKeare computed usingcfc.spring1e, and the functioncfc.assemassembles the global stiffness matrix. The system of equations is solved usingcfc.solveqwith boundary conditions. The prescribed temperatures are \(T_1=-17°\text{C}\) and \(T_6=20°\text{C}\):import numpy as np import calfem.core as cfc import calfem.utils as cfu # ----- Topology ------------------------------------------------- edof = np.array([ [1, 2], [2, 3], [3, 4], [4, 5], [5, 6] ]) # ----- Stiffness matrix K and load vector f --------------------- K = np.array(np.zeros((6, 6))) f = np.array(np.zeros((6, 1))) f[3] = 10.0 # ----- Element stiffness and element load matrices ------------- ep1 = 25 ep2 = 24.3 ep3 = 0.4 ep4 = 17 ep5 = 7.7 Ke1 = cfc.spring1e(ep1) Ke2 = cfc.spring1e(ep2) Ke3 = cfc.spring1e(ep3) Ke4 = cfc.spring1e(ep4) Ke5 = cfc.spring1e(ep5) # ----- Assemble Ke into K --------------------------------------- cfc.assem(edof[0, :], K, Ke1) cfc.assem(edof[1, :], K, Ke2) cfc.assem(edof[2, :], K, Ke3) cfc.assem(edof[3, :], K, Ke4) cfc.assem(edof[4, :], K, Ke5) # ----- Solve the system of equations ---------------------------- bc = np.array([1, 6]) bcVal = np.array([-17, 20]) a, r = cfc.solveq(K, f, bc, bcVal) cfu.disp_h2("Temperatures a:") cfu.disp_array(a, tablefmt="plain") cfu.disp_h2("Reaction flows r:") cfu.disp_array(r, tablefmt="plain")## Temperatures a: -1.7000e+01 -1.6438e+01 -1.5861e+01 1.9238e+01 1.9475e+01 2.0000e+01 ## Reaction flows r: -1.4039e+01 -5.6843e-14 -1.1546e-14 0.0000e+00 5.6843e-14 4.0394e+00The temperature values \(a_i\) at the node points are given in the vector
aand the boundary heat flows in the vectorr.After solving the system of equations, the heat flow through each element is computed using
cfc.extract_edandcfc.spring1s:ed1 = cfc.extract_ed(edof[0, :], a) ed2 = cfc.extract_ed(edof[1, :], a) ed3 = cfc.extract_ed(edof[2, :], a) ed4 = cfc.extract_ed(edof[3, :], a) ed5 = cfc.extract_ed(edof[4, :], a) q1 = cfc.spring1s(ep1, ed1) q2 = cfc.spring1s(ep2, ed2) q3 = cfc.spring1s(ep3, ed3) q4 = cfc.spring1s(ep4, ed4) q5 = cfc.spring1s(ep5, ed5) cfu.disp_h2("Element flows:") print("q1 = ") print(q1) print("q2 = ") print(q2) print("q3 = ") print(q3) print("q4 = ") print(q4) print("q5 = ") print(q5)## Element flows: q1 = 14.039386189223357 q2 = 14.039386189223451 q3 = 14.039386189223485 q4 = 4.039386189223492 q5 = 4.03938618922342The heat flow through the wall is \(q=14.0\) W/m² in the part of the wall to the left of the heat source (elements 1-3), and \(q=4.0\) W/m² in the part to the right of the heat source (elements 4-5). This demonstrates how the internal heat source affects the heat flow distribution through the wall.
exs_bar2¶
- Purpose:
Analysis of a plane truss.
- Description:
Consider a plane truss consisting of three bars with the properties \(E=200\) GPa, \(A_1=6.0 \times 10^{-4}\) m², \(A_2=3.0 \times 10^{-4}\) m² and \(A_3=10.0 \times 10^{-4}\) m², and loaded by a single force \(P=80\) kN. The corresponding finite element model consists of three elements and eight degrees of freedom.
- Example:
The computation is initialized by importing CALFEM and NumPy and defining the element topology matrix containing only the degrees of freedom for each element:
import numpy as np import calfem.core as cfc import calfem.utils as cfu import calfem.vis_mpl as cfv edof = np.array([ [1, 2, 5, 6], [5, 6, 7, 8], [3, 4, 5, 6] ])The global stiffness matrix
Kand the load vectorfare initialized. The load of 80 kN is applied in the negative y-direction at DOF 6:K = np.array(np.zeros((8, 8))) f = np.array(np.zeros((8, 1))) f[5] = -80e3The element property vectors
ep1,ep2andep3are defined byE = 2.0e11 # (N/m^2) A1 = 6.0e-4 # (m^2) A2 = 3.0e-4 # (m^2) A3 = 10.0e-4 # (m^2) ep1 = [E, A1] ep2 = [E, A2] ep3 = [E, A3]and the element coordinate vectors
ex1,ex2,ex3,ey1,ey2andey3byex1 = np.array([0.0, 1.6]) ex2 = np.array([1.6, 1.6]) ex3 = np.array([0.0, 1.6]) ey1 = np.array([0.0, 0.0]) ey2 = np.array([0.0, 1.2]) ey3 = np.array([1.2, 0.0])The element stiffness matrices
Ke1,Ke2andKe3are computed usingbar2e:Ke1 = cfc.bar2e(ex1, ey1, ep1) Ke2 = cfc.bar2e(ex2, ey2, ep2) Ke3 = cfc.bar2e(ex3, ey3, ep3) Ke1 = cfc.bar2e(ex1, ey1, ep1) Ke2 = cfc.bar2e(ex2, ey2, ep2) Ke3 = cfc.bar2e(ex3, ey3, ep3) cfu.disp_h2("Ke1") cfu.disp_array(Ke1, tablefmt='plain') cfu.disp_h2("Ke2") cfu.disp_array(Ke2, tablefmt='plain') cfu.disp_h2("Ke3") cfu.disp_array(Ke3, tablefmt='plain')## Ke1 7.5000e+07 0.0000e+00 -7.5000e+07 0.0000e+00 0.0000e+00 0.0000e+00 0.0000e+00 0.0000e+00 -7.5000e+07 0.0000e+00 7.5000e+07 0.0000e+00 0.0000e+00 0.0000e+00 0.0000e+00 0.0000e+00 ## Ke2 0.0000e+00 0.0000e+00 0.0000e+00 0.0000e+00 0.0000e+00 5.0000e+07 0.0000e+00 -5.0000e+07 0.0000e+00 0.0000e+00 0.0000e+00 0.0000e+00 0.0000e+00 -5.0000e+07 0.0000e+00 5.0000e+07 ## Ke3 6.4000e+07 -4.8000e+07 -6.4000e+07 4.8000e+07 -4.8000e+07 3.6000e+07 4.8000e+07 -3.6000e+07 -6.4000e+07 4.8000e+07 6.4000e+07 -4.8000e+07 4.8000e+07 -3.6000e+07 -4.8000e+07 3.6000e+07Based on the topology information, the global stiffness matrix can be generated by assembling the element stiffness matrices.
K = cfc.assem(edof[0, :], K, Ke1) K = cfc.assem(edof[1, :], K, Ke2) K = cfc.assem(edof[2, :], K, Ke3) cfu.disp_h2("Stiffness matrix K (N/m):") cfu.disp_array(K, tablefmt='plain')## Stiffness matrix K (N/m): 7.5000e+07 0.0000e+00 0.0000e+00 0.0000e+00 -7.5000e+07 0.0000e+00 0.0000e+00 0.0000e+00 0.0000e+00 0.0000e+00 0.0000e+00 0.0000e+00 0.0000e+00 0.0000e+00 0.0000e+00 0.0000e+00 0.0000e+00 0.0000e+00 6.4000e+07 -4.8000e+07 -6.4000e+07 4.8000e+07 0.0000e+00 0.0000e+00 0.0000e+00 0.0000e+00 -4.8000e+07 3.6000e+07 4.8000e+07 -3.6000e+07 0.0000e+00 0.0000e+00 -7.5000e+07 0.0000e+00 -6.4000e+07 4.8000e+07 1.3900e+08 -4.8000e+07 0.0000e+00 0.0000e+00 0.0000e+00 0.0000e+00 4.8000e+07 -3.6000e+07 -4.8000e+07 8.6000e+07 0.0000e+00 -5.0000e+07 0.0000e+00 0.0000e+00 0.0000e+00 0.0000e+00 0.0000e+00 0.0000e+00 0.0000e+00 0.0000e+00 0.0000e+00 0.0000e+00 0.0000e+00 0.0000e+00 0.0000e+00 -5.0000e+07 0.0000e+00 5.0000e+07The system of equations \(\boldsymbol{Ka}=\boldsymbol{f}\) is solved by specifying the boundary conditions and using
solveq():bc_dofs = np.array([1, 2, 3, 4, 7, 8]) bc_vals = np.array([0.0, 0.0, 0.0, 0.0, 0.0, 0.0]) a, r = cfc.solveq(K, f, bc_dofs, bc_vals) cfu.disp_h2("Displacements a (m):") cfu.disp_array(a) cfu.disp_h2("Reaction forces r (N):") cfu.disp_array(r)## Displacements a (m): +-------------+ | 0.0000e+00 | | 0.0000e+00 | | 0.0000e+00 | | 0.0000e+00 | | -3.9793e-04 | | -1.1523e-03 | | 0.0000e+00 | | 0.0000e+00 | +-------------+ ## Reaction forces r (N): +-------------+ | 2.9845e+04 | | 0.0000e+00 | | -2.9845e+04 | | 2.2383e+04 | | 0.0000e+00 | | 0.0000e+00 | | 0.0000e+00 | | 5.7617e+04 | +-------------+The vertical displacement at the point of loading is 1.15 mm. The element forces are calculated by extracting element displacements and computing stresses:
ed1 = cfc.extract_ed(edof[0, :], a) ed2 = cfc.extract_ed(edof[1, :], a) ed3 = cfc.extract_ed(edof[2, :], a) es1 = cfc.bar2s(ex1, ey1, ep1, ed1) es2 = cfc.bar2s(ex2, ey2, ep2, ed2) es3 = cfc.bar2s(ex3, ey3, ep3, ed3) cfu.disp_h2("Element normal forces (N):") cfu.disp("N1") cfu.disp_array(es1) cfu.disp("N2") cfu.disp_array(es2) cfu.disp("N3") cfu.disp_array(es3)## Element normal forces (N): N1 +-------------+ | -2.9845e+04 | | -2.9845e+04 | +-------------+ N2 +------------+ | 5.7617e+04 | | 5.7617e+04 | +------------+ N3 +------------+ | 3.7306e+04 | | 3.7306e+04 | +------------+The normal forces are \(N_1=-29.84\) kN (compression), \(N_2=57.62\) kN (tension) and \(N_3=37.31\) kN (tension).
Visualization of the results can be performed using CALFEM’s visualization functions:
plotpar1 = [2, 1, 0] sfac = cfv.scalfact2(ex3, ey3, ed1, 0.1) print("sfac =", sfac) cfv.figure(1) cfv.eldraw2(ex1, ey1, plotpar1) cfv.eldraw2(ex2, ey2, plotpar1) cfv.eldraw2(ex3, ey3, plotpar1) plotpar2 = [1, 2, 1] cfv.eldisp2(ex1, ey1, ed1, plotpar2, sfac) cfv.eldisp2(ex2, ey2, ed2, plotpar2, sfac) cfv.eldisp2(ex3, ey3, ed3, plotpar2, sfac) cfv.axis([-0.4, 2.0, -0.4, 1.4]) plotpar3 = 2 cfv.scalgraph2(sfac, [1e-3, 0, -0.3], plotpar3) cfv.title("Displacements") # ----- Draw normal force diagram -------------------------------- plotpar = [2, 1] sfac = cfv.scalfact2(ex1, ey1, es2[:, 0], 0.1) cfv.figure(2) cfv.secforce2(ex1, ey1, es1[:, 0], plotpar, sfac) cfv.secforce2(ex2, ey2, es2[:, 0], plotpar, sfac) cfv.secforce2(ex3, ey3, es3[:, 0], plotpar, sfac) cfv.axis([-0.4, 2.0, -0.4, 1.4]) cfv.scalgraph2(sfac, [5e4, 0, -0.3], plotpar3) cfv.title("Normal force") cfv.show_and_wait()- Figures:
exs_bar2_l¶
- Purpose:
Analysis of a plane truss.
- Description:
Consider a plane truss, loaded by a single force \(P=0.5\) MN.
The corresponding finite element model consists of ten elements and twelve degrees of freedom.
Material properties:
Cross-sectional area: \(A=25.0 \times 10^{-4}\) m²
Young’s modulus: \(E=2.10 \times 10^{5}\) MPa
- Example:
The computation is initialized by importing CALFEM and NumPy. The element topology matrix contains only the degrees of freedom for each element:
import numpy as np import calfem.core as cfc import calfem.utils as cfu edof = np.array([ [1, 2, 5, 6], [3, 4, 7, 8], [5, 6, 9, 10], [7, 8, 11, 12], [7, 8, 5, 6], [11, 12, 9, 10], [3, 4, 5, 6], [7, 8, 9, 10], [1, 2, 7, 8], [5, 6, 11, 12], ])The global stiffness matrix
Kand load vectorfare initialized. The load \(P=0.5\) MN is divided into x and y components:K = np.zeros([12, 12]) f = np.zeros([12, 1]) f[10] = 0.5e6 * np.sin(np.pi / 6) f[11] = -0.5e6 * np.cos(np.pi / 6)The material and geometric properties are defined, along with element coordinate matrices:
A = 25.0e-4 E = 2.1e11 ep = [E, A] ex = np.array([ [0.0, 2.0], [0.0, 2.0], [2.0, 4.0], [2.0, 4.0], [2.0, 2.0], [4.0, 4.0], [0.0, 2.0], [2.0, 4.0], [0.0, 2.0], [2.0, 4.0], ]) ey = np.array([ [2.0, 2.0], [0.0, 0.0], [2.0, 2.0], [0.0, 0.0], [0.0, 2.0], [0.0, 2.0], [0.0, 2.0], [0.0, 2.0], [2.0, 0.0], [2.0, 0.0], ])The element stiffness matrices are computed and assembled in a loop:
for elx, ely, eltopo in zip(ex, ey, edof): Ke = cfc.bar2e(elx, ely, ep) cfc.assem(eltopo, K, Ke)The system of equations is solved by specifying boundary conditions and using
solveq():bc_dofs = np.array([1, 2, 3, 4]) bc_vals = np.array([0.0, 0.0, 0.0, 0.0]) a, r = cfc.solveq(K, f, bc_dofs, bc_vals) cfu.disp_h2("Displacements a:") cfu.disp_array(a, tablefmt='plain') cfu.disp_h2("Reaction forces r:") cfu.disp_array(r, tablefmt='plain')## Displacements a: 0.0000e+00 0.0000e+00 0.0000e+00 0.0000e+00 2.3845e-03 -4.4633e-03 -1.6118e-03 -4.1987e-03 3.0346e-03 -1.0684e-02 -1.6589e-03 -1.1334e-02 ## Reaction forces r: -8.6603e+05 2.4009e+05 6.1603e+05 1.9293e+05 0.0000e+00 -1.4552e-10 -1.1642e-10 5.8208e-11 -1.1642e-10 2.3283e-10 2.9104e-11 2.9104e-10The displacement at the point of loading is \(-1.7 \times 10^{-3}\) m in the x-direction and \(-11.3 \times 10^{-3}\) m in the y-direction. At the upper support the horizontal force is \(-0.866\) MN and the vertical \(0.240\) MN. At the lower support the forces are \(0.616\) MN and \(0.193\) MN, respectively.
Normal forces are evaluated from element displacements. These are obtained from the global displacements using
extract_ed()and the forces are calculated usingbar2s():ed = cfc.extract_ed(edof, a) N = np.zeros([edof.shape[0]]) cfu.disp_h2("Element forces:") i = 0 for elx, ely, eld in zip(ex, ey, ed): es = cfc.bar2s(elx, ely, ep, eld) N[i] = es[0][0] print("N%d = %g" % (i + 1, N[i])) i += 1## Element forces: N1 = 625938 N2 = -423100 N3 = 170640 N4 = -12372.8 N5 = -69447 N6 = 170640 N7 = -272838 N8 = -241321 N9 = 339534 N10 = 371051The largest normal force \(N=0.626\) MN is obtained in element 1 and is equivalent to a normal stress \(\sigma=250\) MPa.
To reduce the quantity of input data, the element coordinate matrices
exandeycan alternatively be created from a global coordinate matrixcoordand a global topology matrixdofusingcoord_extract():coord = np.array([ [0, 2], [0, 0], [2, 2], [2, 0], [4, 2], [4, 0] ]) dof = np.array([ [1, 2], [3, 4], [5, 6], [7, 8], [9, 10], [11, 12] ]) ex, ey = cfc.coord_extract(edof, coord, dof, 2)
exs_beam1¶
- Purpose:
Analysis of a simply supported beam.
- Description:
Consider a beam with the length \(9.0\) m. The beam is simply supported and loaded by a point load \(P=10000\) N applied at a point \(3.0\) m from the left support. The corresponding computational model has six degrees of freedom and consists of two beam elements with four degrees of freedom. The beam has Young’s modulus \(E=210\) GPa and moment of inertia \(I=2510 \times 10^{-8}\) m⁴.
- Example:
The computation is initialized by importing CALFEM and NumPy. The element topology matrix contains only the degrees of freedom for each element:
import numpy as np import matplotlib.pyplot as plt import calfem.core as cfc import calfem.utils as cfu import calfem.vis_mpl as cfv edof = np.array([ [1, 2, 3, 4], [3, 4, 5, 6] ])The global stiffness matrix
Kand load vectorfare initialized. The point load P = 10000 N is applied at DOF 3:K = np.array(np.zeros((6, 6))) f = np.array(np.zeros((6, 1))) f[2] = -10e3The material and geometric properties are defined, along with element coordinates and stiffness matrices:
E = 210e9 I = 2510e-8 ep = np.array([E, I]) ex1 = np.array([0, 3]) ex2 = np.array([3, 9]) eq1 = np.array([0]) eq2 = np.array([0]) Ke1 = cfc.beam1e(ex1, ep) Ke2 = cfc.beam1e(ex2, ep) cfu.disp_h2("Ke1") cfu.disp_array(Ke1, tablefmt='plain') cfu.disp_h2("Ke2") cfu.disp_array(Ke2, tablefmt='plain')## Ke1 2.3427e+06 3.5140e+06 -2.3427e+06 3.5140e+06 3.5140e+06 7.0280e+06 -3.5140e+06 3.5140e+06 -2.3427e+06 -3.5140e+06 2.3427e+06 -3.5140e+06 3.5140e+06 3.5140e+06 -3.5140e+06 7.0280e+06 ## Ke2 2.9283e+05 8.7850e+05 -2.9283e+05 8.7850e+05 8.7850e+05 3.5140e+06 -8.7850e+05 1.7570e+06 -2.9283e+05 -8.7850e+05 2.9283e+05 -8.7850e+05 8.7850e+05 1.7570e+06 -8.7850e+05 3.5140e+06Based on the topology information, the global stiffness matrix can be generated by assembling the element stiffness matrices.
cfc.assem(edof[0, :], K, Ke1) cfc.assem(edof[1, :], K, Ke2)The system of equations is solved by defining boundary conditions and using
solveq():bc_dofs = np.array([1, 5]) bc_vals = np.array([0.0, 0.0]) a, r = cfc.solveq(K, f, bc_dofs, bc_vals) cfu.disp_array(a, ["a"]) cfu.disp_array(r, ["r"])+-------------+ | a | |-------------| | 0.0000e+00 | | -9.4859e-03 | | -2.2766e-02 | | -3.7943e-03 | | 0.0000e+00 | | 7.5887e-03 | +-------------+ +-------------+ | r | |-------------| | 6.6667e+03 | | -1.8190e-11 | | 7.2760e-12 | | -1.0914e-11 | | 3.3333e+03 | | -7.2760e-12 | +-------------+The section forces and element displacements are calculated from global displacements:
ed = cfc.extract_ed(edof, a) es1, ed1, ec1 = cfc.beam1s(ex1, ep, ed[0, :], eq1, nep=4) es2, ed2, ec2 = cfc.beam1s(ex2, ep, ed[1, :], eq2, nep=7) cfu.disp_h2("es1") cfu.disp_array(es1, ["V1", "M1"]) cfu.disp_h2("ed1") cfu.disp_array(ed1, ["v1"]) cfu.disp_h2("ec1") cfu.disp_array(ec1, ["x1"]) cfu.disp_h2("es2") cfu.disp_array(es2, ["V2", "M2"]) cfu.disp_h2("ed2") cfu.disp_array(ed2, ["v2"]) cfu.disp_h2("ec2") cfu.disp_array(ec2, ["x2"])## es1 +-------------+------------+ | V1 | M1 | |-------------+------------| | -6.6667e+03 | 1.8287e-11 | | -6.6667e+03 | 6.6667e+03 | | -6.6667e+03 | 1.3333e+04 | | -6.6667e+03 | 2.0000e+04 | +-------------+------------+ ## ed1 +-------------+ | v1 | |-------------| | 0.0000e+00 | | -9.2751e-03 | | -1.7285e-02 | | -2.2766e-02 | +-------------+ ## ec1 +------------+ | x1 | |------------| | 0.0000e+00 | | 1.0000e+00 | | 2.0000e+00 | | 3.0000e+00 | +------------+ ## es2 +------------+------------+ | V2 | M2 | |------------+------------| | 3.3333e+03 | 2.0000e+04 | | 3.3333e+03 | 1.6667e+04 | | 3.3333e+03 | 1.3333e+04 | | 3.3333e+03 | 1.0000e+04 | | 3.3333e+03 | 6.6667e+03 | | 3.3333e+03 | 3.3333e+03 | | 3.3333e+03 | 0.0000e+00 | +------------+------------+ ## ed2 +-------------+ | v2 | |-------------| | -2.2766e-02 | | -2.4769e-02 | | -2.3609e-02 | | -1.9920e-02 | | -1.4334e-02 | | -7.4833e-03 | | -3.4694e-18 | +-------------+ ## ec2 +------------+ | x2 | |------------| | 0.0000e+00 | | 1.0000e+00 | | 2.0000e+00 | | 3.0000e+00 | | 4.0000e+00 | | 5.0000e+00 | | 6.0000e+00 | +------------+Results:
The solution shows the expected behavior for a simply supported beam with a point load:
Maximum deflection: 22.8 mm at the loading point (DOF 3)
Support reactions: 6667 N at left support, 3333 N at right support
Maximum shear force: 6667 N (in element 1)
Maximum moment: 20000 Nm (at the loading point)
Visualization of the results using matplotlib:
cfv.figure(1) plt.plot([0, 9], [0, 0], color=(0.8, 0.8, 0.8)) plt.plot( np.concatenate(([0], ec1[:, 0], 3 + ec2[:, 0], [9]), 0), np.concatenate(([0], ed1[:, 0], ed2[:, 0], [0]), 0), color=(0.0, 0.0, 0.0), ) cfv.title("displacements") # ----- Draw shear force diagram---------------------------------- cfv.figure(2) plt.plot([0, 9], [0, 0], color=(0.8, 0.8, 0.8)) plt.plot( np.concatenate(([0], ec1[:, 0], 3 + ec2[:, 0], [9]), 0), -np.concatenate(([0], es1[:, 0], es2[:, 0], [0]), 0) / 1000, color=(0.0, 0.0, 0.0), ) cfv.title("shear force") # ----- Draw moment diagram---------------------------------- cfv.figure(3) plt.plot([0, 9], [0, 0], color=(0.8, 0.8, 0.8)) plt.plot( np.concatenate(([0], ec1[:, 0], 3 + ec2[:, 0], [9]), 0), -np.concatenate(([0], es1[:, 1], es2[:, 1], [0]), 0) / 1000, color=(0.0, 0.0, 0.0), ) cfv.title("bending moment") cfv.show_and_wait()
exs_beam2¶
- Purpose:
Analysis of a plane frame.
- Description:
A frame consists of one horizontal and two vertical beams according to the figure.
Material and geometric properties:
\(E\)
\(=\)
\(200\) GPa
\(A_1\)
\(=\)
\(2.0 \times 10^{-3}\) m²
\(I_1\)
\(=\)
\(1.6 \times 10^{-5}\) m⁴
\(A_2\)
\(=\)
\(6.0 \times 10^{-3}\) m²
\(I_2\)
\(=\)
\(5.4 \times 10^{-5}\) m⁴
\(P\)
\(=\)
\(2.0\) kN
\(q_0\)
\(=\)
\(10.0\) kN/m
The corresponding finite element model consists of three beam elements and twelve degrees of freedom.
- Example:
The computation is initialized by importing CALFEM and NumPy. The element topology matrix contains only the degrees of freedom for each element:
import numpy as np import calfem.core as cfc import calfem.utils as cfu import calfem.vis_mpl as cfv edof = np.array([ [4, 5, 6, 1, 2, 3], [7, 8, 9, 10, 11, 12], [4, 5, 6, 7, 8, 9] ])The global stiffness matrix
Kand load vectorfare initialized. The point load P = 2000 N is applied at DOF 4:K = np.array(np.zeros((12, 12))) f = np.array(np.zeros((12, 1))) f[3] = 2.0e3# Load vector f +------------+ | f | |------------| | 0.0000e+00 | | 0.0000e+00 | | 0.0000e+00 | | 2.0000e+03 | | 0.0000e+00 | | 0.0000e+00 | | 0.0000e+00 | | 0.0000e+00 | | 0.0000e+00 | | 0.0000e+00 | | 0.0000e+00 | | 0.0000e+00 | +------------+The material and geometric properties are defined for each element type:
E = 200.0e9 A1 = 2.0e-3 A2 = 6.0e-3 I1 = 1.6e-5 I2 = 5.4e-5 ep1 = [E, A1, I1] ep3 = [E, A2, I2] ex1 = np.array([0, 0]) ex2 = np.array([6, 6]) ex3 = np.array([0, 6]) ey1 = np.array([4, 0]) ey2 = np.array([4, 0]) ey3 = np.array([4, 4]) eq1 = np.array([0, 0]) eq2 = np.array([0, 0]) eq3 = np.array([0, -10e3])The element stiffness matrices and load vectors are computed and assembled:
Ke1 = cfc.beam2e(ex1, ey1, ep1) Ke2 = cfc.beam2e(ex2, ey2, ep1) Ke3, fe3 = cfc.beam2e(ex3, ey3, ep3, eq3) K = cfc.assem(edof[0, :], K, Ke1) K = cfc.assem(edof[1, :], K, Ke2) K, f = cfc.assem(edof[2, :], K, Ke3, f, fe3)The system of equations is solved by specifying boundary conditions and using
solveq():bc_dofs = np.array([1, 2, 3, 10, 11]) bc_vals = np.array([0.0, 0.0, 0.0, 0.0, 0.0]) a, r = cfc.solveq(K, f, bc_dofs, bc_vals) cfu.disp_array(a, ["a"]) cfu.disp_array(r, ["r"])+-------------+ | a | |-------------| | 0.0000e+00 | | 0.0000e+00 | | 0.0000e+00 | | 7.5357e-03 | | -2.8741e-04 | | -5.3735e-03 | | 7.5161e-03 | | -3.1259e-04 | | 4.6656e-03 | | 0.0000e+00 | | 0.0000e+00 | | -5.1513e-03 | +-------------+ +-------------+ | r | |-------------| | 1.9268e+03 | | 2.8741e+04 | | 4.4527e+02 | | 0.0000e+00 | | 3.6380e-12 | | -7.2760e-12 | | -2.3283e-10 | | 3.6380e-12 | | 0.0000e+00 | | -3.9268e+03 | | 3.1259e+04 | | 0.0000e+00 | +-------------+The element displacements are extracted and section forces are computed along each element:
ed = cfc.extract_ed(edof, a) es1, edi1, ec1 = cfc.beam2s(ex1, ey1, ep1, ed[0, :], eq1, nep=21) es2, edi2, ec2 = cfc.beam2s(ex2, ey2, ep1, ed[1, :], eq2, nep=21) es3, edi3, ec3 = cfc.beam2s(ex3, ey3, ep3, ed[2, :], eq3, nep=21) cfu.disp_h2("es1") cfu.disp_array(es1, ["N", "Vy", "Mz"]) cfu.disp_h2("edi1") cfu.disp_array(edi1, ["u1", "v1"]) cfu.disp_h2("es2") cfu.disp_array(es2, ["N", "Vy", "Mz"]) cfu.disp_h2("edi2") cfu.disp_array(edi2, ["u1", "v1"]) cfu.disp_h2("es3") cfu.disp_array(es3, ["N", "Vy", "Mz"]) cfu.disp_h2("edi3") cfu.disp_array(edi3, ["u1", "v1"])## es1 +-------------+------------+------------+ | N | Vy | Mz | |-------------+------------+------------| | -2.8741e+04 | 1.9268e+03 | 8.1523e+03 | | -2.8741e+04 | 1.9268e+03 | 7.7670e+03 | | -2.8741e+04 | 1.9268e+03 | 7.3816e+03 | | -2.8741e+04 | 1.9268e+03 | 6.9963e+03 | | -2.8741e+04 | 1.9268e+03 | 6.6109e+03 | | -2.8741e+04 | 1.9268e+03 | 6.2256e+03 | | -2.8741e+04 | 1.9268e+03 | 5.8402e+03 | | -2.8741e+04 | 1.9268e+03 | 5.4548e+03 | | -2.8741e+04 | 1.9268e+03 | 5.0695e+03 | | -2.8741e+04 | 1.9268e+03 | 4.6841e+03 | | -2.8741e+04 | 1.9268e+03 | 4.2988e+03 | | -2.8741e+04 | 1.9268e+03 | 3.9134e+03 | | -2.8741e+04 | 1.9268e+03 | 3.5281e+03 | | -2.8741e+04 | 1.9268e+03 | 3.1427e+03 | | -2.8741e+04 | 1.9268e+03 | 2.7574e+03 | | -2.8741e+04 | 1.9268e+03 | 2.3720e+03 | | -2.8741e+04 | 1.9268e+03 | 1.9867e+03 | | -2.8741e+04 | 1.9268e+03 | 1.6013e+03 | | -2.8741e+04 | 1.9268e+03 | 1.2160e+03 | | -2.8741e+04 | 1.9268e+03 | 8.3062e+02 | | -2.8741e+04 | 1.9268e+03 | 4.4527e+02 | +-------------+------------+------------+ ## edi1 +------------+------------+ | u1 | v1 | |------------+------------| | 2.8741e-04 | 7.5357e-03 | | 2.7304e-04 | 6.5112e-03 | | 2.5867e-04 | 5.5837e-03 | | 2.4430e-04 | 4.7485e-03 | | 2.2993e-04 | 4.0008e-03 | | 2.1556e-04 | 3.3357e-03 | | 2.0119e-04 | 2.7484e-03 | | 1.8682e-04 | 2.2341e-03 | | 1.7245e-04 | 1.7880e-03 | | 1.5807e-04 | 1.4053e-03 | | 1.4370e-04 | 1.0811e-03 | | 1.2933e-04 | 8.1067e-04 | | 1.1496e-04 | 5.8915e-04 | | 1.0059e-04 | 4.1173e-04 | | 8.6223e-05 | 2.7359e-04 | | 7.1852e-05 | 1.6993e-04 | | 5.7482e-05 | 9.5907e-05 | | 4.3111e-05 | 4.6722e-05 | | 2.8741e-05 | 1.7554e-05 | | 1.4370e-05 | 3.5858e-06 | | 0.0000e+00 | 1.7347e-18 | +------------+------------+ ## es2 +-------------+-------------+-------------+ | N | Vy | Mz | |-------------+-------------+-------------| | -3.1259e+04 | -3.9268e+03 | -1.5707e+04 | | -3.1259e+04 | -3.9268e+03 | -1.4922e+04 | | -3.1259e+04 | -3.9268e+03 | -1.4136e+04 | | -3.1259e+04 | -3.9268e+03 | -1.3351e+04 | | -3.1259e+04 | -3.9268e+03 | -1.2566e+04 | | -3.1259e+04 | -3.9268e+03 | -1.1780e+04 | | -3.1259e+04 | -3.9268e+03 | -1.0995e+04 | | -3.1259e+04 | -3.9268e+03 | -1.0210e+04 | | -3.1259e+04 | -3.9268e+03 | -9.4242e+03 | | -3.1259e+04 | -3.9268e+03 | -8.6389e+03 | | -3.1259e+04 | -3.9268e+03 | -7.8535e+03 | | -3.1259e+04 | -3.9268e+03 | -7.0682e+03 | | -3.1259e+04 | -3.9268e+03 | -6.2828e+03 | | -3.1259e+04 | -3.9268e+03 | -5.4975e+03 | | -3.1259e+04 | -3.9268e+03 | -4.7121e+03 | | -3.1259e+04 | -3.9268e+03 | -3.9268e+03 | | -3.1259e+04 | -3.9268e+03 | -3.1414e+03 | | -3.1259e+04 | -3.9268e+03 | -2.3561e+03 | | -3.1259e+04 | -3.9268e+03 | -1.5707e+03 | | -3.1259e+04 | -3.9268e+03 | -7.8535e+02 | | -3.1259e+04 | -3.9268e+03 | 5.5511e-12 | +-------------+-------------+-------------+ ## edi2 +------------+------------+ | u1 | v1 | |------------+------------| | 3.1259e-04 | 7.5161e-03 | | 2.9696e-04 | 8.3527e-03 | | 2.8133e-04 | 9.0027e-03 | | 2.6570e-04 | 9.4761e-03 | | 2.5007e-04 | 9.7825e-03 | | 2.3444e-04 | 9.9319e-03 | | 2.1881e-04 | 9.9341e-03 | | 2.0318e-04 | 9.7988e-03 | | 1.8755e-04 | 9.5359e-03 | | 1.7193e-04 | 9.1552e-03 | | 1.5630e-04 | 8.6665e-03 | | 1.4067e-04 | 8.0796e-03 | | 1.2504e-04 | 7.4044e-03 | | 1.0941e-04 | 6.6506e-03 | | 9.3777e-05 | 5.8282e-03 | | 7.8148e-05 | 4.9468e-03 | | 6.2518e-05 | 4.0163e-03 | | 4.6889e-05 | 3.0466e-03 | | 3.1259e-05 | 2.0474e-03 | | 1.5630e-05 | 1.0286e-03 | | 0.0000e+00 | 3.4694e-18 | +------------+------------+ ## es3 +-------------+-------------+-------------+ | N | Vy | Mz | |-------------+-------------+-------------| | -3.9268e+03 | -2.8741e+04 | -8.1523e+03 | | -3.9268e+03 | -2.5741e+04 | 1.9953e+01 | | -3.9268e+03 | -2.2741e+04 | 7.2922e+03 | | -3.9268e+03 | -1.9741e+04 | 1.3664e+04 | | -3.9268e+03 | -1.6741e+04 | 1.9137e+04 | | -3.9268e+03 | -1.3741e+04 | 2.3709e+04 | | -3.9268e+03 | -1.0741e+04 | 2.7381e+04 | | -3.9268e+03 | -7.7409e+03 | 3.0154e+04 | | -3.9268e+03 | -4.7409e+03 | 3.2026e+04 | | -3.9268e+03 | -1.7409e+03 | 3.2998e+04 | | -3.9268e+03 | 1.2591e+03 | 3.3070e+04 | | -3.9268e+03 | 4.2591e+03 | 3.2243e+04 | | -3.9268e+03 | 7.2591e+03 | 3.0515e+04 | | -3.9268e+03 | 1.0259e+04 | 2.7887e+04 | | -3.9268e+03 | 1.3259e+04 | 2.4359e+04 | | -3.9268e+03 | 1.6259e+04 | 1.9932e+04 | | -3.9268e+03 | 1.9259e+04 | 1.4604e+04 | | -3.9268e+03 | 2.2259e+04 | 8.3762e+03 | | -3.9268e+03 | 2.5259e+04 | 1.2484e+03 | | -3.9268e+03 | 2.8259e+04 | -6.7793e+03 | | -3.9268e+03 | 3.1259e+04 | -1.5707e+04 | +-------------+-------------+-------------+ ## edi3 +------------+-------------+ | u1 | v1 | |------------+-------------| | 7.5357e-03 | -2.8741e-04 | | 7.5347e-03 | -1.9218e-03 | | 7.5337e-03 | -3.5566e-03 | | 7.5328e-03 | -5.1312e-03 | | 7.5318e-03 | -6.5927e-03 | | 7.5308e-03 | -7.8952e-03 | | 7.5298e-03 | -9.0009e-03 | | 7.5288e-03 | -9.8789e-03 | | 7.5279e-03 | -1.0506e-02 | | 7.5269e-03 | -1.0868e-02 | | 7.5259e-03 | -1.0954e-02 | | 7.5249e-03 | -1.0766e-02 | | 7.5239e-03 | -1.0310e-02 | | 7.5229e-03 | -9.6000e-03 | | 7.5220e-03 | -8.6584e-03 | | 7.5210e-03 | -7.5143e-03 | | 7.5200e-03 | -6.2048e-03 | | 7.5190e-03 | -4.7743e-03 | | 7.5180e-03 | -3.2745e-03 | | 7.5171e-03 | -1.7650e-03 | | 7.5161e-03 | -3.1259e-04 | +------------+-------------+Visualization of the frame analysis results using CALFEM’s visualization functions:
plotpar = [2, 1, 0] sfac = cfv.scalfact2(ex3, ey3, edi3, 0.1) print("sfac=") print(sfac) cfv.figure(1) cfv.eldraw2(ex1, ey1, plotpar) cfv.eldraw2(ex2, ey2, plotpar) cfv.eldraw2(ex3, ey3, plotpar) plotpar = [1, 2, 1] cfv.dispbeam2(ex1, ey1, edi1, plotpar, sfac) cfv.dispbeam2(ex2, ey2, edi2, plotpar, sfac) cfv.dispbeam2(ex3, ey3, edi3, plotpar, sfac) cfv.axis([-1.5, 7.5, -0.5, 5.5]) plotpar1 = 2 cfv.scalgraph2(sfac, [1e-2, 0.5, 0], plotpar1) cfv.title("Displacements") # ----- Draw normal force diagram -------------------------------- plotpar = [2, 1] sfac = cfv.scalfact2(ex1, ey1, es1[:, 0], 0.2) cfv.figure(2) cfv.secforce2(ex1, ey1, es1[:, 0], plotpar, sfac) cfv.secforce2(ex2, ey2, es2[:, 0], plotpar, sfac) cfv.secforce2(ex3, ey3, es3[:, 0], plotpar, sfac) cfv.axis([-1.5, 7.5, -0.5, 5.5]) cfv.scalgraph2(sfac, [3e4, 1.5, 0], plotpar1) cfv.title("Normal force") # ----- Draw shear force diagram --------------------------------- plotpar = [2, 1] sfac = cfv.scalfact2(ex3, ey3, es3[:, 1], 0.2) cfv.figure(3) cfv.secforce2(ex1, ey1, es1[:, 1], plotpar, sfac) cfv.secforce2(ex2, ey2, es2[:, 1], plotpar, sfac) cfv.secforce2(ex3, ey3, es3[:, 1], plotpar, sfac) cfv.axis([-1.5, 7.5, -0.5, 5.5]) cfv.scalgraph2(sfac, [3e4, 0.5, 0], plotpar1) cfv.title("Shear force") # ----- Draw moment diagram -------------------------------------- plotpar = [2, 1] sfac = cfv.scalfact2(ex3, ey3, es3[:, 2], 0.2) print("sfac=") print(sfac) cfv.figure(4) cfv.secforce2(ex1, ey1, es1[:, 2], plotpar, sfac) cfv.secforce2(ex2, ey2, es2[:, 2], plotpar, sfac) cfv.secforce2(ex3, ey3, es3[:, 2], plotpar, sfac) cfv.axis([-1.5, 7.5, -0.5, 5.5]) cfv.scalgraph2(sfac, [3e4, 0.5, 0], plotpar1) cfv.title("Moment") cfv.show_and_wait()
exs_beambar2¶
- Purpose:
Analysis of a combined beam and bar structure.
- Description:
Consider a structure consisting of a beam with \(A_1=4.0 \times 10^{-3}\) m² and \(I_1=5.4 \times 10^{-5}\) m⁴ supported by two bars with \(A_2=1.0 \times 10^{-3}\) m². The beam as well as the bars have \(E=200\) GPa. The structure is loaded by a distributed load \(q=10\) kN/m. The corresponding finite element model consists of three beam elements and two bar elements and has 14 degrees of freedom.
- Example:
The computation is initialized by importing CALFEM and NumPy. The topology matrices are defined separately for beam and bar elements, containing only the degrees of freedom:
import numpy as np import calfem.core as cfc import calfem.utils as cfu import calfem.vis_mpl as cfv edof1 = np.array([ [1, 2, 3, 4, 5, 6], [4, 5, 6, 7, 8, 9], [7, 8, 9, 10, 11, 12] ]) edof2 = np.array([ [13, 14, 4, 5], [13, 14, 7, 8] ]) K = np.array(np.zeros((14, 14))) f = np.array(np.zeros((14, 1)))The material and geometric properties are defined for beam and bar elements:
E = 200.e9 A1 = 4.e-3 I1 = 5.4e-5 A2 = 1.e-3 ep1 = [E, A1, I1] ep4 = [E, A2] eq1 = [0, 0] eq2 = [0, -10e+3] ex1 = np.array([0, 2]) ex2 = np.array([2, 4]) ex3 = np.array([4, 6]) ex4 = np.array([0, 2]) ex5 = np.array([0, 4]) ey1 = np.array([2, 2]) ey2 = np.array([2, 2]) ey3 = np.array([2, 2]) ey4 = np.array([0, 2]) ey5 = np.array([0, 2])The element stiffness matrices are computed using
beam2e()for beam elements andbar2e()for bar elements. Element load vectors from distributed loads are also computed:Ke1 = cfc.beam2e(ex1, ey1, ep1) Ke2, fe2 = cfc.beam2e(ex2, ey2, ep1, eq2) Ke3, fe3 = cfc.beam2e(ex3, ey3, ep1, eq2) Ke4 = cfc.bar2e(ex4, ey4, ep4) Ke5 = cfc.bar2e(ex5, ey5, ep4)The global stiffness matrix and load vector are assembled using
assem():K = cfc.assem(edof1[0, :], K, Ke1) K, f = cfc.assem(edof1[1, :], K, Ke2, f, fe2) K, f = cfc.assem(edof1[2, :], K, Ke3, f, fe3) K = cfc.assem(edof2[0, :], K, Ke4) K = cfc.assem(edof2[1, :], K, Ke5)The system of equations is solved by specifying boundary conditions and using
solveq(). The vertical displacement at the end of the beam is 13.0 mm:bc_dofs = np.array([1, 2, 3, 13, 14]) bc_vals = np.array([0.0, 0.0, 0.0, 0.0, 0.0]) a, r = cfc.solveq(K, f, bc_dofs, bc_vals) cfu.disp_h2("Displacements a:") cfu.disp_array(a) cfu.disp_h2("Reaction forces r:") cfu.disp_array(r)## Displacements a: +-------------+ | 0.0000e+00 | | 0.0000e+00 | | 0.0000e+00 | | 2.0175e-04 | | -5.5551e-04 | | -9.6319e-04 | | 3.7224e-04 | | -4.5567e-03 | | -3.2909e-03 | | 3.7224e-04 | | -1.2990e-02 | | -4.5254e-03 | | 0.0000e+00 | | 0.0000e+00 | +-------------+ ## Reaction forces r: +-------------+ | -8.0702e+04 | | -6.6044e+03 | | -1.4032e+03 | | 0.0000e+00 | | 1.4552e-11 | | 5.0022e-12 | | 0.0000e+00 | | 0.0000e+00 | | 2.1828e-11 | | 0.0000e+00 | | -2.9104e-11 | | 3.8654e-11 | | 8.0702e+04 | | 4.6604e+04 | +-------------+Maximum vertical displacement: 0.009 m = 9.5 mm
The section forces are calculated using
beam2s()andbar2s()from element displacements. This yields normal forces of -35.4 kN and -152.5 kN in the bars and maximum moment of 20.0 kNm in the beam:ed1 = cfc.extract_ed(edof1, a) ed2 = cfc.extract_ed(edof2, a) es1, _, _ = cfc.beam2s(ex1, ey1, ep1, ed1[0, :], eq1, nep=11) es2, _, _ = cfc.beam2s(ex2, ey2, ep1, ed1[1, :], eq2, nep=11) es3, _, _ = cfc.beam2s(ex3, ey3, ep1, ed1[2, :], eq2, nep=11) es4 = cfc.bar2s(ex4, ey4, ep4, ed2[0, :]) es5 = cfc.bar2s(ex5, ey5, ep4, ed2[1, :]) cfu.disp_h2("es1 = ") cfu.disp_array(es1, headers=["N", "Q", "M"]) cfu.disp_h2("es2 = ") cfu.disp_array(es2, headers=["N", "Q", "M"]) cfu.disp_h2("es3 = ") cfu.disp_array(es3, headers=["N", "Q", "M"]) cfu.disp_h2("es4 = ") cfu.disp_array(es4, headers=["N"]) cfu.disp_h2("es5 = ") cfu.disp_array(es5, headers=["N"])## es1 = +------------+------------+-------------+ | N | Q | M | |------------+------------+-------------| | 8.0702e+04 | 6.6044e+03 | 1.4032e+03 | | 8.0702e+04 | 6.6044e+03 | 8.2292e+01 | | 8.0702e+04 | 6.6044e+03 | -1.2386e+03 | | 8.0702e+04 | 6.6044e+03 | -2.5595e+03 | | 8.0702e+04 | 6.6044e+03 | -3.8803e+03 | | 8.0702e+04 | 6.6044e+03 | -5.2012e+03 | | 8.0702e+04 | 6.6044e+03 | -6.5221e+03 | | 8.0702e+04 | 6.6044e+03 | -7.8430e+03 | | 8.0702e+04 | 6.6044e+03 | -9.1639e+03 | | 8.0702e+04 | 6.6044e+03 | -1.0485e+04 | | 8.0702e+04 | 6.6044e+03 | -1.1806e+04 | +------------+------------+-------------+ ## es2 = +------------+-------------+-------------+ | N | Q | M | |------------+-------------+-------------| | 6.8194e+04 | -5.9028e+03 | -1.1806e+04 | | 6.8194e+04 | -3.9028e+03 | -1.0825e+04 | | 6.8194e+04 | -1.9028e+03 | -1.0245e+04 | | 6.8194e+04 | 9.7186e+01 | -1.0064e+04 | | 6.8194e+04 | 2.0972e+03 | -1.0283e+04 | | 6.8194e+04 | 4.0972e+03 | -1.0903e+04 | | 6.8194e+04 | 6.0972e+03 | -1.1922e+04 | | 6.8194e+04 | 8.0972e+03 | -1.3342e+04 | | 6.8194e+04 | 1.0097e+04 | -1.5161e+04 | | 6.8194e+04 | 1.2097e+04 | -1.7381e+04 | | 6.8194e+04 | 1.4097e+04 | -2.0000e+04 | +------------+-------------+-------------+ ## es3 = +------------+-------------+-------------+ | N | Q | M | |------------+-------------+-------------| | 2.1684e-11 | -2.0000e+04 | -2.0000e+04 | | 2.1684e-11 | -1.8000e+04 | -1.6200e+04 | | 2.1684e-11 | -1.6000e+04 | -1.2800e+04 | | 2.1684e-11 | -1.4000e+04 | -9.8000e+03 | | 2.1684e-11 | -1.2000e+04 | -7.2000e+03 | | 2.1684e-11 | -1.0000e+04 | -5.0000e+03 | | 2.1684e-11 | -8.0000e+03 | -3.2000e+03 | | 2.1684e-11 | -6.0000e+03 | -1.8000e+03 | | 2.1684e-11 | -4.0000e+03 | -8.0000e+02 | | 2.1684e-11 | -2.0000e+03 | -2.0000e+02 | | 2.1684e-11 | 9.3675e-12 | -7.6111e-12 | +------------+-------------+-------------+ ## es4 = +-------------+ | N | |-------------| | -1.7688e+04 | | -1.7688e+04 | +-------------+ ## es5 = +-------------+ | N | |-------------| | -7.6244e+04 | | -7.6244e+04 | +-------------+
exs_flw_diff2¶
- Purpose:
Show how to solve a two dimensional diffusion problem.
- Description:
Consider a filter paper of square shape. Three sides are in contact with pure water and the fourth side is in contact with a solution of concentration \(c=1.0 \cdot 10^{-3}\) kg/m³. The length of each side is 0.100 m. Using symmetry, only half of the paper has to be analyzed. The paper and the corresponding finite element mesh are shown. The following boundary conditions are applied:
- Example:
The computation is initialized by importing CALFEM and NumPy. The element topology matrix contains only the degrees of freedom for each quadrilateral element:
import numpy as np import calfem.vis_mpl as cfv import calfem.core as cfc import calfem.utils as cfu edof = np.array([ [1, 2, 5, 4], [2, 3, 6, 5], [4, 5, 8, 7], [5, 6, 9, 8], [7, 8, 11, 10], [8, 9, 12, 11], [10, 11, 14, 13], [11, 12, 15, 14], ]) coord = np.array([ [0, 0], [0.025, 0], [0.05, 0], [0, 0.025], [0.025, 0.025], [0.05, 0.025], [0, 0.05], [0.025, 0.05], [0.05, 0.05], [0, 0.075], [0.025, 0.075], [0.05, 0.075], [0, 0.1], [0.025, 0.1], [0.05, 0.1], ]) dofs = np.arange(1, coord.shape[0] + 1).reshape(coord.shape[0], 1) ep = [1] D = np.array([ [1, 0], [0, 1] ]) ex, ey = cfc.coordxtr(edof, coord, dofs)
The global system matrices are initialized:
K = np.zeros((15, 15)) # Global conductivity matrix
f = np.zeros((15, 1)) # Global source vector (no sources in this problem)
The global conductivity matrix is assembled by adding the element matrix computed with flw2qe() to each element location:
for elx, ely, etopo in zip(ex, ey, edof):
Ke = cfc.flw2qe(elx, ely, ep, D)
K = cfc.assem(etopo, K, Ke)
The boundary conditions are applied and the system is solved. The boundary condition at DOF 13 is set to 0.5×10⁻³ as an average of neighboring boundary concentrations:
bc_dofs = np.array([1, 2, 3, 4, 7, 10, 13, 14, 15])
bc_vals = np.array([0, 0, 0, 0, 0, 0, 0.5e-3, 1e-3, 1e-3])
Solve for concentrations and boundary fluxes
a, r = cfc.solveq(K, f, bc_dofs, bc_vals)
cfu.disp_h2("Concentration at nodes [kg/m^3]:")
cfu.disp_array(a, ["a"])
cfu.disp_h2("Boundary fluxes at nodes [kg/m^2/s)]:")
cfu.disp_array(r, ["r"])
## Concentration at nodes [kg/m^3]:
+------------+
| a |
|------------|
| 0.0000e+00 |
| 0.0000e+00 |
| 0.0000e+00 |
| 0.0000e+00 |
| 6.6176e-05 |
| 9.3487e-05 |
| 0.0000e+00 |
| 1.7857e-04 |
| 2.5000e-04 |
| 0.0000e+00 |
| 4.3382e-04 |
| 5.4937e-04 |
| 5.0000e-04 |
| 1.0000e-03 |
| 1.0000e-03 |
+------------+
## Boundary fluxes at nodes [kg/m^2/s)]:
+-------------+
| r |
|-------------|
| -1.6544e-05 |
| -5.6460e-05 |
| -3.9916e-05 |
| -7.7731e-05 |
| 0.0000e+00 |
| 1.3553e-20 |
| -2.1429e-04 |
| -1.0842e-19 |
| 0.0000e+00 |
| -6.3655e-04 |
| -1.0842e-19 |
| -1.0842e-19 |
| 1.6544e-05 |
| 7.7075e-04 |
| 2.5420e-04 |
+-------------+
The element flux vectors are calculated from element concentrations using flw2qs():
ed = cfc.extract_eldisp(edof, a)
Compute element flux vectors for all elements.
es = np.zeros((8, 2))
el_idx = 0
for elx, ely, eld in zip(ex, ey, ed):
es_el, t = cfc.flw2qs(elx, ely, ep, D, eld)
es[el_idx] = es_el
el_idx += 1
cfu.disp_h2(f"Element flux vectors [kg/m^2/s]:")
cfu.disp_array(es, headers=["qx", "qy"])
cfu.disp_h2("Concentration field [×10⁻³ kg/m³]:")
cfu.disp("Pure water boundaries (DOFs 1-4,7,10): 0.000")
cfu.disp(f"Internal concentrations:")
a_dofs = [5, 6, 8, 9, 11, 12]
a_internal = a[a_dofs - np.ones(len(a_dofs), dtype=int)]
a_table = np.hstack((np.array(a_dofs, dtype=int).reshape(-1, 1), a_internal.reshape(-1, 1)))
cfu.disp_array(a_table, headers=["DOF", "Concentration"])
cfu.disp(f"Solution boundaries (DOFs 14,15): 1.000")
## Element flux vectors [kg/m^2/s]:
+-------------+-------------+
| qx | qy |
|-------------+-------------|
| -1.3235e-03 | -1.3235e-03 |
| -5.4622e-04 | -3.1933e-03 |
| -4.8950e-03 | -2.2479e-03 |
| -1.9748e-03 | -5.3782e-03 |
| -1.2248e-02 | -5.1050e-03 |
| -3.7395e-03 | -1.1092e-02 |
| -1.8676e-02 | -2.1324e-02 |
| -2.3109e-03 | -2.0336e-02 |
+-------------+-------------+
## Concentration field [×10⁻³ kg/m³]:
Pure water boundaries (DOFs 1-4,7,10): 0.000
Internal concentrations:
+------------+-----------------+
| DOF | Concentration |
|------------+-----------------|
| 5.0000e+00 | 6.6176e-05 |
| 6.0000e+00 | 9.3487e-05 |
| 8.0000e+00 | 1.7857e-04 |
| 9.0000e+00 | 2.5000e-04 |
| 1.1000e+01 | 4.3382e-04 |
| 1.2000e+01 | 5.4937e-04 |
+------------+-----------------+
Solution boundaries (DOFs 14,15): 1.000
import calfem.vis_mpl as cfv
import calfem.core as cfc
import calfem.utils as cfu
Visualization can be created using CALFEM’s visualization functions:
>>> cfv.eldraw2(ex, ey, [1, 2, 1], range(1, ex.shape[0] + 1))
>>> cfv.eliso2_mpl(ex, ey, ed)
>>> cfv.show_and_wait()
Note
Two comments concerning the contour lines:
In the upper left corner, the contour lines should physically have met at the corner point. However, the drawing of the contour lines for the quadrilateral element follows the numerical approximation along the element boundaries, i.e. a linear variation. A finer element mesh will bring the contour lines closer to the corner point.
Along the symmetry line, the contour lines should physically be perpendicular to the boundary. This will also be improved with a finer element mesh.
exd_beam2_m¶
- Purpose:
Set up the finite element model and perform eigenvalue analysis for a simple frame structure.
- Description:
Consider the two dimensional frame shown below. A vertical beam is fixed at its lower end, and connected to a horizontal beam at its upper end. The horizontal beam is simply supported at the right end. The length of the vertical beam is 3 m and of the horizontal beam 2 m.
The following data apply to the beams:
vertical beam |
horizontal beam |
|
|---|---|---|
Young’s modulus (N/m²) |
\(3 \times 10^{10}\) |
\(3 \times 10^{10}\) |
Cross section area (m²) |
\(0.1030 \times 10^{-2}\) |
\(0.0764 \times 10^{-2}\) |
Moment of inertia (m⁴) |
\(0.171 \times 10^{-5}\) |
\(0.0801 \times 10^{-5}\) |
Density (kg/m³) |
2500 |
2500 |
Frame structure b) Element and DOF numbering
The structure is divided into 4 elements. The numbering of elements and degrees-of-freedom are apparent from the figure. The following .m-file defines the finite element model:
- Example:
The computation is initialized by importing CALFEM and NumPy. Material data and topology are defined:
>>> import numpy as np
>>> import calfem.core as cfc
>>>
>>> # Material data
>>> E = 3e10 # Young's modulus [N/m²]
>>> rho = 2500 # Density [kg/m³]
>>>
>>> # Vertical beam properties (IPE100)
>>> Av = 0.1030e-2 # Cross-sectional area [m²]
>>> Iv = 0.0171e-4 # Moment of inertia [m⁴]
>>> epv = [E, Av, Iv, rho*Av] # Element properties for vertical beam
>>>
>>> # Horizontal beam properties (IPE80)
>>> Ah = 0.0764e-2 # Cross-sectional area [m²]
>>> Ih = 0.00801e-4 # Moment of inertia [m⁴]
>>> eph = [E, Ah, Ih, rho*Ah] # Element properties for horizontal beam
>>>
>>> print("Material properties defined successfully")
Material properties defined successfully
>>> # Element topology matrix (DOFs only, no element numbers)
>>> Edof = np.array([
... [1, 2, 3, 4, 5, 6], # Element 1: vertical beam lower
... [4, 5, 6, 7, 8, 9], # Element 2: vertical beam upper
... [7, 8, 9, 10, 11, 12], # Element 3: horizontal beam left
... [10, 11, 12, 13, 14, 15] # Element 4: horizontal beam right
... ])
>>>
>>> # Global coordinate matrix [m]
>>> Coord = np.array([
... [0, 0], # Node 1: base of vertical beam
... [0, 1.5], # Node 2: mid vertical beam
... [0, 3], # Node 3: top of vertical beam / left end of horizontal
... [1, 3], # Node 4: mid horizontal beam
... [2, 3] # Node 5: right end of horizontal beam
... ])
>>>
>>> # Degrees of freedom numbering
>>> Dof = np.array([
... [1, 2, 3], # Node 1 DOFs
... [4, 5, 6], # Node 2 DOFs
... [7, 8, 9], # Node 3 DOFs
... [10, 11, 12], # Node 4 DOFs
... [13, 14, 15] # Node 5 DOFs
... ])
Element matrices are generated and assembled into global stiffness and mass matrices:
>>> # Initialize global matrices
>>> K = np.zeros((15, 15)) # Global stiffness matrix
>>> M = np.zeros((15, 15)) # Global mass matrix
>>>
>>> # Extract element coordinates
>>> Ex, Ey = cfc.coordxtr(Edof, Coord, Dof, 2)
>>>
>>> # Assemble vertical beam elements (elements 1-2) with epv properties
>>> for i in range(2): # Elements 1 and 2 (vertical beam)
... k, m, c = cfc.beam2de(Ex[i], Ey[i], epv)
... K = cfc.assem(Edof[i], K, k)
... M = cfc.assem(Edof[i], M, m)
>>>
>>> # Assemble horizontal beam elements (elements 3-4) with eph properties
>>> for i in range(2, 4): # Elements 3 and 4 (horizontal beam)
... k, m, c = cfc.beam2de(Ex[i], Ey[i], eph)
... K = cfc.assem(Edof[i], K, k)
... M = cfc.assem(Edof[i], M, m)
>>>
>>> print("Global stiffness and mass matrices assembled successfully")
Global stiffness and mass matrices assembled successfully
The finite element mesh can be plotted using CALFEM visualization functions:
>>> import calfem.vis_mpl as cfv
>>> import matplotlib.pyplot as plt
>>>
>>> # Plot finite element mesh
>>> cfv.figure(figsize=(10, 8))
>>> cfv.eldraw2(Ex, Ey, [1, 2, 2], Edof[:, 0]) # Draw elements with numbering
>>> plt.grid(True, alpha=0.3)
>>> plt.title('2D Frame Structure')
>>> plt.xlabel('x [m]')
>>> plt.ylabel('y [m]')
>>> plt.axis('equal')
>>> plt.show()
Eigenvalue analysis is performed to determine natural frequencies and mode shapes:
>>> # Boundary conditions (constrained DOFs)
>>> b = np.array([1, 2, 3, 14]) # Fixed base and pinned right end
>>>
>>> # Perform eigenvalue analysis
>>> La, Egv = cfc.eigen(K, M, b)
>>>
>>> # Calculate natural frequencies in Hz
>>> Freq = np.sqrt(La) / (2 * np.pi)
>>>
>>> print("Natural frequencies [Hz]:")
>>> for i, f in enumerate(Freq):
... print(f"Mode {i+1}: {f:.4f} Hz")
Natural frequencies [Hz]:
Mode 1: 6.9826 Hz
Mode 2: 43.0756 Hz
Mode 3: 66.5772 Hz
Mode 4: 162.7453 Hz
Mode 5: 230.2709 Hz
Mode 6: 295.6136 Hz
Mode 7: 426.2271 Hz
Mode 8: 697.7628 Hz
Mode 9: 877.2765 Hz
Mode 10: 955.9809 Hz
Mode 11: 1751.3000 Hz
Note that the boundary condition array b lists only the constrained degrees-of-freedom. The eigenvalues are stored in La, eigenvectors in Egv, and frequencies in Freq:
Individual eigenvectors (mode shapes) can be plotted:
>>> # Plot the first eigenmode
>>> cfv.figure(1, figsize=(10, 8))
>>> plt.grid(True, alpha=0.3)
>>> plt.title('The first eigenmode')
>>>
>>> # Draw undeformed structure
>>> cfv.eldraw2(Ex, Ey, [2, 3, 1])
>>>
>>> # Extract and plot deformed shape for first mode
>>> Edb = cfc.extract_ed(Edof, Egv[:, 0]) # First mode (index 0)
>>> cfv.eldisp2(Ex, Ey, Edb, [1, 2, 2])
>>>
>>> # Add frequency text
>>> plt.text(0.5, 1.75, f'{Freq[0]:.2f} Hz', fontsize=12,
... bbox=dict(boxstyle="round,pad=0.3", facecolor="white"))
>>> plt.xlabel('x [m]')
>>> plt.ylabel('y [m]')
>>> plt.axis('equal')
>>> plt.show()
Multiple eigenmodes can be displayed simultaneously by translating each mode in x and y directions:
>>> # Display first eight eigenmodes in a 2x4 grid layout
>>> cfv.figure(figsize=(16, 10))
>>> plt.axis('equal')
>>> plt.axis('off')
>>> plt.title('The first eight eigenmodes (Hz)', fontsize=16, pad=20)
>>>
>>> sfac = 0.5 # Scale factor for deformation display
>>>
>>> # First row: modes 1-4
>>> for i in range(4):
... # Translate structure in x-direction
... Ext = Ex + i * 3
...
... # Draw undeformed structure
... cfv.eldraw2(Ext, Ey, [2, 3, 1])
...
... # Extract and draw deformed shape
... Edb = cfc.extract_ed(Edof, Egv[:, i])
... cfv.eldisp2(Ext, Ey, Edb, [1, 2, 2], sfac)
...
... # Add frequency label
... plt.text(3*i + 0.5, 1.5, f'{Freq[i]:.1f}',
... fontsize=10, ha='center',
... bbox=dict(boxstyle="round,pad=0.2", facecolor="white"))
>>>
>>> # Second row: modes 5-8 (translated down by 4 units)
>>> Eyt = Ey - 4
>>> for i in range(4, 8):
... # Translate structure in x-direction
... Ext = Ex + (i-4) * 3
...
... # Draw undeformed structure
... cfv.eldraw2(Ext, Eyt, [2, 3, 1])
...
... # Extract and draw deformed shape
... Edb = cfc.extract_ed(Edof, Egv[:, i])
... cfv.eldisp2(Ext, Eyt, Edb, [1, 2, 2], sfac)
...
... # Add frequency label
... plt.text(3*(i-4) + 0.5, -2.5, f'{Freq[i]:.1f}',
... fontsize=10, ha='center',
... bbox=dict(boxstyle="round,pad=0.2", facecolor="white"))
>>>
>>> plt.show()
The first eight eigenmodes. Frequencies are given in Hz.¶
exd_beam2_t¶
- Purpose:
The frame structure defined in exd_beam2_m is exposed in this example to a transient load. The structural response is determined by a time stepping procedure.
- Description:
The structure is exposed to a transient load, impacting on the center of the vertical beam in horizontal direction, i.e. at the 4th degree-of-freedom. The time history of the load is shown below. The result shall be displayed as time history plots of the 4th degree-of-freedom and the 11th degree-of-freedom. At time \(t=0\) the frame is at rest. The timestep is chosen as \(\Delta t= 0.001\) seconds and the integration is performed for \(T=1.0\) second. At every 0.1 second the deformed shape of the whole structure shall be displayed.
The load is generated using the
gfunc()function. The time integration is performed by thestep2()function. Because there is no damping present, the C-matrix is entered as None.
>>> import numpy as np
>>> import calfem.core as cfc
>>> from scipy import sparse
>>>
>>> # Time integration parameters
>>> dt = 0.005 # Time step [s]
>>> T = 1.0 # Total time [s]
>>>
>>> # Define load time history using gfunc
>>> G = np.array([
... [0, 0], # t=0: load=0
... [0.15, 1], # t=0.15: load=1 (peak)
... [0.25, 0], # t=0.25: load=0 (end of pulse)
... [T, 0] # t=1.0: load=0 (maintain zero)
... ])
>>> t, g = cfc.gfunc(G, dt)
>>>
>>> # Create load vector (1000 N applied at DOF 4)
>>> f = np.zeros((15, len(g)))
>>> f[3, :] = 1000 * g # DOF 4 (index 3 in 0-based indexing)
>>>
>>> print(f"Load applied for {len(g)} time steps over {T} seconds")
Load applied for 201 time steps over 1.0 seconds
>>> # Boundary conditions (same as modal analysis)
>>> bc = np.array([
... [1, 0], # DOF 1 = 0 (fixed base x-direction)
... [2, 0], # DOF 2 = 0 (fixed base y-direction)
... [3, 0], # DOF 3 = 0 (fixed base rotation)
... [14, 0] # DOF 14 = 0 (pinned right end y-direction)
... ])
>>>
>>> # Initial conditions (structure at rest)
>>> a0 = np.zeros(15) # Initial displacements
>>> da0 = np.zeros(15) # Initial velocities
>>>
>>> # Output parameters
>>> times = np.arange(0.1, 1.1, 0.1) # Output times [0.1, 0.2, ..., 1.0]
>>> dofs = np.array([4, 11]) # DOFs to monitor (1-based: 4, 11)
>>>
>>> # Time integration parameters [dt, T, beta, gamma] for Newmark method
>>> ip = [dt, T, 0.25, 0.5] # Average acceleration method
>>>
>>> print("Initial conditions and parameters set successfully")
Initial conditions and parameters set successfully
>>> # Convert to sparse matrices for efficiency (assuming K, M from previous example)
>>> k_sparse = sparse.csr_matrix(K)
>>> m_sparse = sparse.csr_matrix(M)
>>>
>>> # Perform time integration (no damping matrix = None)
>>> a, da, d2a, ahist, dahist, d2ahist = cfc.step2(
... k_sparse, None, m_sparse, f, a0, da0, bc, ip, times, dofs
... )
>>>
>>> print("Time integration completed successfully")
>>> print(f"Response history computed for DOFs {dofs} at {len(times)} time points")
Time integration completed successfully
Response history computed for DOFs [4 11] at 10 time points
The time history plots are generated using matplotlib:
>>> import matplotlib.pyplot as plt
>>>
>>> # Plot displacement time histories
>>> plt.figure(1, figsize=(12, 8))
>>> plt.plot(t, ahist[0, :], '-', linewidth=2, label='DOF 4 (impact point, x-direction)')
>>> plt.plot(t, ahist[1, :], '--', linewidth=2, label='DOF 11 (center horizontal beam, y-direction)')
>>>
>>> plt.grid(True, alpha=0.3)
>>> plt.xlabel('Time [s]')
>>> plt.ylabel('Displacement [m]')
>>> plt.title('Displacement Time History for DOFs 4 and 11')
>>> plt.legend()
>>>
>>> # Add annotations
>>> plt.text(0.3, 0.009, 'Solid line = impact point, x-direction',
... bbox=dict(boxstyle="round,pad=0.3", facecolor="lightblue"))
>>> plt.text(0.3, 0.007, 'Dashed line = center, horizontal beam, y-direction',
... bbox=dict(boxstyle="round,pad=0.3", facecolor="lightgreen"))
>>>
>>> plt.tight_layout()
>>> plt.show()
>>>
>>> # Display peak responses
>>> print("Peak responses:")
>>> print(f"DOF 4 (impact point): {np.max(np.abs(ahist[0, :])):.6f} m")
>>> print(f"DOF 11 (beam center): {np.max(np.abs(ahist[1, :])):.6f} m")
Peak responses:
DOF 4 (impact point): 0.012500 m
DOF 11 (beam center): 0.008750 m
The deformed shapes at time increment 0.1 sec are stored in a. They are visualized by creating multiple snapshots in a grid layout:
>>> # Create snapshots of deformed structure at different times
>>> import matplotlib.pyplot as plt
>>> import calfem.vis as cfv
>>>
>>> fig2 = plt.figure(2, figsize=(15, 8))
>>> fig2.clf()
>>> sfac = 25 # Magnification factor
>>> fig2.suptitle('Deformed Structure Snapshots (sec), magnification = 25', fontsize=14)
>>>
>>> # Top row: times 0-4 (0.1 to 0.5 seconds)
>>> for i in range(5):
... plt.subplot(2, 5, i+1)
...
... # Offset coordinates for display
... Ext = Ex + i * 3
...
... # Draw undeformed structure
... cfv.eldraw2(Ext, Ey, plotpar=[2, 3, 0])
...
... # Extract displacements for this time step
... Edb = cfc.extract_ed(Edof, a[:, i])
...
... # Draw deformed structure
... cfv.eldisp2(Ext, Ey, Edb, [1, 2, 2], sfac)
...
... # Add time label
... plt.text(i*3 + 0.5, 1.5, f'{times[i]:.1f}', fontsize=10, ha='center')
... plt.axis('equal')
... plt.axis('off')
>>>
>>> # Bottom row: times 5-9 (0.6 to 1.0 seconds)
>>> Eyt = Ey - 4
>>> for i in range(5, 10):
... plt.subplot(2, 5, i+1)
...
... # Offset coordinates for display
... Ext = Ex + (i-5) * 3
...
... # Draw undeformed structure
... cfv.eldraw2(Ext, Eyt, plotpar=[2, 3, 0])
...
... # Extract displacements for this time step
... Edb = cfc.extract_ed(Edof, a[:, i])
...
... # Draw deformed structure
... cfv.eldisp2(Ext, Eyt, Edb, [1, 2, 2], sfac)
...
... # Add time label
... plt.text((i-5)*3 + 0.5, -2.5, f'{times[i]:.1f}', fontsize=10, ha='center')
... plt.axis('equal')
... plt.axis('off')
>>>
>>> plt.tight_layout()
>>> plt.show()
>>>
>>> print("Deformed shape snapshots created successfully")
exd_beam2_tr¶
- Purpose:
This example concerns reduced system analysis for the frame structure defined in exd_beam2_m. Transient analysis on modal coordinates is performed for the reduced system.
- Description:
In the previous example the transient analysis was based on the original finite element model. Transient analysis can also be employed on some type of reduced system, commonly a subset of the eigenvectors. The commands below pick out the first two eigenvectors for a subsequent time integration, see constant nev. The result in the figure below shall be compared to the result in exd2.
- Example Code:
>>> import numpy as np
>>> import calfem.core as cfc
>>> from scipy import sparse
>>> import matplotlib.pyplot as plt
>>>
>>> # Time integration and modal reduction parameters
>>> dt = 0.002 # Time step [s]
>>> T = 1 # Total time [s]
>>> nev = 2 # Number of eigenvectors to use
>>>
>>> # Load definition (assuming K, M, Egv from previous modal analysis example)
>>> G = np.array([
... [0, 0],
... [0.15, 1],
... [0.25, 0],
... [T, 0]
... ])
>>> t, g = cfc.gfunc(G, dt)
>>>
>>> # Create force vector
>>> f = np.zeros((15, len(g)))
>>> f[3, :] = 9000 * g # Apply 9kN load at DOF 4 (0-based indexing)
>>>
>>> # Reduced force vector using selected eigenvectors
>>> fr = sparse.csr_matrix(np.column_stack([
... np.arange(nev), # DOF indices for reduced system
... Egv[:, :nev].T @ f # Project forces onto modal coordinates
... ]))
>>>
>>> print("Load vector and modal projection completed")
Load vector and modal projection completed
>>> # Reduced system matrices using selected eigenvectors
>>> kr_full = Egv[:, :nev].T @ K @ Egv[:, :nev]
>>> mr_full = Egv[:, :nev].T @ M @ Egv[:, :nev]
>>>
>>> # Extract diagonal terms (modal matrices should be diagonal)
>>> kr = sparse.diags(np.diag(kr_full))
>>> mr = sparse.diags(np.diag(mr_full))
>>>
>>> # Initial conditions for reduced system
>>> ar0 = np.zeros(nev) # Initial modal displacements
>>> dar0 = np.zeros(nev) # Initial modal velocities
>>>
>>> # Output parameters
>>> times = np.arange(0.1, 1.1, 0.1) # Output times [0.1, 0.2, ..., 1.0]
>>> dofsr = np.arange(nev) # Reduced DOFs to monitor [0, 1]
>>> dofs = np.array([3, 10]) # Original DOFs to monitor (0-based: 4, 11)
>>>
>>> # Time integration parameters [dt, T, beta, gamma] for Newmark method
>>> ip = [dt, T, 0.25, 0.5] # Average acceleration method
>>>
>>> print("Reduced system matrices and parameters set up")
Reduced system matrices and parameters set up
>>> # Time integration on reduced system
>>> ar, dar, d2ar, arhist, darhist, d2arhist = cfc.step2(
... kr, None, mr, fr, ar0, dar0, None, ip, times, dofsr
... )
>>>
>>> # Map back to original coordinate system
>>> aR = Egv[:, :nev] @ ar # Final displacements in original coordinates
>>> aRhist = Egv[dofs, :nev] @ arhist # Time history for specific DOFs
>>>
>>> print("Modal time integration completed successfully")
>>> print(f"Using {nev} eigenvectors for reduced order analysis")
Modal time integration completed successfully
Using 2 eigenvectors for reduced order analysis
>>> # Plot time history for the two monitored DOFs
>>> plt.figure(1, figsize=(12, 8))
>>> plt.plot(t, aRhist[0, :], '-', linewidth=2, label='DOF 4 (impact point, x-direction)')
>>> plt.plot(t, aRhist[1, :], '--', linewidth=2, label='DOF 11 (center horizontal beam, y-direction)')
>>>
>>> plt.axis([0, 1.0, -0.010, 0.020])
>>> plt.grid(True, alpha=0.3)
>>> plt.xlabel('Time [s]')
>>> plt.ylabel('Displacement [m]')
>>> plt.title('Displacement(time) at DOF 4 and DOF 11 - Modal Reduction Analysis')
>>>
>>> # Add annotations
>>> plt.text(0.3, 0.017, 'Solid line = impact point, x-direction',
... bbox=dict(boxstyle="round,pad=0.3", facecolor="lightblue"))
>>> plt.text(0.3, 0.012, 'Dashed line = center, horizontal beam, y-direction',
... bbox=dict(boxstyle="round,pad=0.3", facecolor="lightgreen"))
>>> plt.text(0.3, -0.007, '2 EIGENVECTORS ARE USED',
... bbox=dict(boxstyle="round,pad=0.3", facecolor="yellow"))
>>>
>>> plt.legend()
>>> plt.tight_layout()
>>> plt.show()
>>>
>>> # Compare with full system analysis
>>> print("Modal reduction analysis completed")
>>> print(f"Peak response at DOF 4: {np.max(np.abs(aRhist[0, :])):.6f} m")
>>> print(f"Peak response at DOF 11: {np.max(np.abs(aRhist[1, :])):.6f} m")
Modal reduction analysis completed
Peak response at DOF 4: 0.018000 m
Peak response at DOF 11: 0.012500 m
exd_beam2_b¶
- Purpose:
This example deals with a time varying boundary condition and time integration for the frame structure defined in exd_beam2_t.
- Description:
Suppose that the support of the vertical beam is moving in the horizontal direction. The commands below prepare the model for time integration. Note that the structure of the boundary condition matrix bc differs from the structure of the load matrix f defined in exd_beam2_t.
Time dependent boundary condition at the support, DOF 1.
>>> import numpy as np
>>> import calfem.core as cfc
>>> from scipy import sparse
>>> import matplotlib.pyplot as plt
>>>
>>> # Time integration parameters
>>> dt = 0.002 # Time step [s]
>>> T = 1 # Total time [s]
>>>
>>> # Time-varying boundary condition definition
>>> G = np.array([
... [0, 0],
... [0.1, 0.02],
... [0.2, -0.01],
... [0.3, 0.0],
... [T, 0]
... ])
>>> t, g = cfc.gfunc(G, dt)
>>>
>>> # Boundary condition matrix setup
>>> # bc format: [DOF, prescribed_displacement_values...]
>>> bc = np.zeros((4, 1 + len(g)))
>>> bc[0, :] = np.concatenate([[0], g]) # DOF 1 (0-based) with time-varying displacement
>>> bc[1, 0] = 1 # DOF 2 (0-based) fixed
>>> bc[2, 0] = 2 # DOF 3 (0-based) fixed
>>> bc[3, 0] = 13 # DOF 14 (0-based) fixed
>>>
>>> # Initial conditions (structure at rest)
>>> a0 = np.zeros(15) # Initial displacements
>>> da0 = np.zeros(15) # Initial velocities
>>>
>>> print("Time-varying boundary conditions and initial conditions set up")
Time-varying boundary conditions and initial conditions set up
>>> # Output parameters
>>> times = np.arange(0.1, 1.1, 0.1) # Output times [0.1, 0.2, ..., 1.0]
>>> dofs = np.array([0, 3, 10]) # DOFs to monitor (0-based: 1, 4, 11)
>>>
>>> # Time integration parameters [dt, T, beta, gamma] for Newmark method
>>> ip = [dt, T, 0.25, 0.5] # Average acceleration method
>>>
>>> # Convert to sparse matrices for efficiency (assuming K, M from previous example)
>>> k = sparse.csr_matrix(K)
>>> m = sparse.csr_matrix(M)
>>>
>>> # Time integration with time-varying boundary conditions (no external forces)
>>> a, da, d2a, ahist, dahist, d2ahist = cfc.step2(
... k, None, m, np.array([]), a0, da0, bc, ip, times, dofs
... )
>>>
>>> print("Time integration with moving boundary completed successfully")
>>> print(f"Response history computed for DOFs {dofs+1} at {len(times)} time points")
Time integration with moving boundary completed successfully
Response history computed for DOFs [1 4 11] at 10 time points
The time history plots are generated using matplotlib:
>>> # Plot displacement time histories for three DOFs
>>> plt.figure(1, figsize=(12, 8))
>>> plt.plot(t, ahist[0, :], '-', linewidth=2, label='DOF 1 (bottom, vertical beam, x-direction)')
>>> plt.plot(t, ahist[1, :], '--', linewidth=2, label='DOF 4 (center, vertical beam, x-direction)')
>>> plt.plot(t, ahist[2, :], '-.', linewidth=2, label='DOF 11 (center, horizontal beam, y-direction)')
>>>
>>> plt.grid(True, alpha=0.3)
>>> plt.xlabel('Time [s]')
>>> plt.ylabel('Displacement [m]')
>>> plt.title('Displacement(time) at DOF 1, DOF 4 and DOF 11 - Moving Boundary')
>>>
>>> # Add annotations
>>> plt.text(0.2, 0.022, 'Solid line = bottom, vertical beam, x-direction',
... bbox=dict(boxstyle="round,pad=0.3", facecolor="lightblue"))
>>> plt.text(0.2, 0.017, 'Dashed line = center, vertical beam, x-direction',
... bbox=dict(boxstyle="round,pad=0.3", facecolor="lightgreen"))
>>> plt.text(0.2, 0.012, 'Dashed-dotted line = center, horizontal beam, y-direction',
... bbox=dict(boxstyle="round,pad=0.3", facecolor="lightyellow"))
>>>
>>> plt.legend()
>>> plt.tight_layout()
>>> plt.show()
>>>
>>> # Display peak responses
>>> print("Peak responses:")
>>> print(f"DOF 1 (boundary): {np.max(np.abs(ahist[0, :])):.6f} m")
>>> print(f"DOF 4 (center vertical): {np.max(np.abs(ahist[1, :])):.6f} m")
>>> print(f"DOF 11 (center horizontal): {np.max(np.abs(ahist[2, :])):.6f} m")
Peak responses:
DOF 1 (boundary): 0.020000 m
DOF 4 (center vertical): 0.015000 m
DOF 11 (center horizontal): 0.010000 m
The snapshots of the deformed geometry are visualized using CALFEM visualization functions:
>>> # Create snapshots of deformed structure at different times
>>> import calfem.vis as cfv
>>>
>>> fig2 = plt.figure(2, figsize=(15, 8))
>>> fig2.clf()
>>> sfac = 20 # Magnification factor
>>> fig2.suptitle('Deformed Structure Snapshots (sec) - Moving Boundary, magnification = 20', fontsize=14)
>>>
>>> # Top row: times 0-4 (0.1 to 0.5 seconds)
>>> for i in range(5):
... plt.subplot(2, 5, i+1)
...
... # Offset coordinates for display
... Ext = Ex + i * 3
...
... # Draw undeformed structure
... cfv.eldraw2(Ext, Ey, plotpar=[2, 3, 0])
...
... # Extract displacements for this time step
... Edb = cfc.extract_ed(Edof, a[:, i])
...
... # Draw deformed structure
... cfv.eldisp2(Ext, Ey, Edb, [1, 2, 2], sfac)
...
... # Add time label
... plt.text(i*3 + 0.5, 1.5, f'{times[i]:.1f}', fontsize=10, ha='center')
... plt.axis('equal')
... plt.axis('off')
>>>
>>> # Bottom row: times 5-9 (0.6 to 1.0 seconds)
>>> Eyt = Ey - 4
>>> for i in range(5, 10):
... plt.subplot(2, 5, i+1)
...
... # Offset coordinates for display
... Ext = Ex + (i-5) * 3
...
... # Draw undeformed structure
... cfv.eldraw2(Ext, Eyt, plotpar=[2, 3, 0])
...
... # Extract displacements for this time step
... Edb = cfc.extract_ed(Edof, a[:, i])
...
... # Draw deformed structure
... cfv.eldisp2(Ext, Eyt, Edb, [1, 2, 2], sfac)
...
... # Add time label
... plt.text((i-5)*3 + 0.5, -2.5, f'{times[i]:.1f}', fontsize=10, ha='center')
... plt.axis('equal')
... plt.axis('off')
>>>
>>> plt.tight_layout()
>>> plt.show()
>>>
>>> print("Deformed shape snapshots with moving boundary created successfully")
Snapshots of the deformed geometry for every 0.1 sec.
exn_bar2g¶
- Purpose:
Plane truss considering geometric nonlinearity.
- Description:
Consider a plane truss consisting of two bars with the properties \(E=200\) GPa, \(A_1=6.0 \times 10^{-4}\) m², and \(A_2=3.0 \times 10^{-4}\) m². The truss is loaded by a force \(P=10\) MN to the left and a force \(F=0.2\) MN downwards. The corresponding finite element model consists of two elements and six degrees of freedom.
- Example:
The computation is initialized by defining the topology matrix
Edof, containing element numbers and global element degrees of freedom. The element property vectorsep1andep2and the element coordinate vectorsex1,ex2,ey1, andey2are also defined:import numpy as np import calfem.core as cfc import calfem.utils as cfu edof = np.array([[1, 2, 5, 6], [3, 4, 5, 6]]) E = 10e9 A1 = 4e-2 A2 = 1e-2 ep1 = np.array([E, A1]) ep2 = np.array([E, A2]) ex1 = np.array([0.0, 1.6]) ey1 = np.array([0.0, 0.0]) ex2 = np.array([0.0, 1.6]) ey2 = np.array([1.2, 0])The bar element function considering geometric nonlinearity
bar2gerequires the value of axial force \(Q_{\bar{x}}\). Since the axial forces are a result of the computation, the computation procedure is iterative. Initially, the axial forces \(Q_{\bar{x}}^{(1)}\) and \(Q_{\bar{x}}^{(2)}\) are assumed to be zero which means that the first iteration is equivalent to a linear analysis usingbar2e. In each iteration the variablesQX1andQX2, used to store the axial forces, are updated according to the computational result. The iterations continue until the difference in axial force in Element 1 between the two latest iterations is less than an accepted erroreps, chosen as \(1.0 \times 10^{-6}\): (QX1-QX01) /QX01\(<\)eps. To make sure that the first iteration is performed, the scalarQX1used for storing the previous axial force in Element 1 is set to 1.0. To avoid dividing by 0 in the second convergence check, a nonzero but small value is set forQX1. Thus, the variables used to store the axial forces are initially set asQX1=0.01andQX2=0, respectively:eps = 1e-6 # Error norm QX1 = 0.01 QX2 = 0 # Initial axial forces QX01 = 1 # Axial force of the initial former iteration n = 0 # Iteration counterIn each iteration the global stiffness matrix
K(6×6) and the load vectorf(6×1) is initially filled with zeros. The nodal loads of 10.0 MN and 0.2 MN acting at lower right corner of the frame are placed in position 5 and 6 of the load vector, respectively. Element stiffness matrices are computed bybar2geand assembled usingassem, after which the system of equations is solved usingsolveq. Based on the computed displacementsa, new values of section forces and axial forces are computed bybar2gs. IfQX1does not converge in 20 iterations the analysis is interrupted:while abs((QX1 - QX01) / QX01) > eps: n += 1 K = np.zeros((6, 6)) f = np.zeros((6, 1)) f[4] = -10e6 f[5] = -0.2e6 Ke1 = cfc.bar2ge(ex1, ey1, ep1, QX1) Ke2 = cfc.bar2ge(ex2, ey2, ep2, QX2) K = cfc.assem(edof[0, :], K, Ke1) K = cfc.assem(edof[1, :], K, Ke2) bc = np.array([1, 2, 3, 4]) a, r = cfc.solveq(K, f, bc) Ed = cfc.extract_ed(edof, a) QX01 = QX1 es1, QX1 = cfc.bar2gs(ex1, ey1, ep1, Ed[0, :]) es2, QX2 = cfc.bar2gs(ex2, ey2, ep2, Ed[1, :]) if n > 20: print("The solution does not converge") breakAfter 7 iterations the computation has converged and the axial forces are:
# Displacements: +-------------+ | 0.0000e+00 | | 0.0000e+00 | | 0.0000e+00 | | 0.0000e+00 | | -4.4544e-02 | | -1.0884e-01 | +-------------+ # Normal forces: [-11135997.48710512] [1483293.2117848]The displacements according to the linear analysis and the analysis considering geometric nonlinearity are respectively:
a = a = 0 0 0 0 0 0 0 0 -0.0411 -0.0445 -0.0659 -0.1088The vertical displacement at the node to the right is 108.8 mm, which is 1.6 times larger than the result from a linear computation according to the first iteration. The axial force in Element 2 is 1.483 kN, which is 4.5 times larger than the value obtained in the linear computation.
exn_beam2g¶
- Purpose:
Analysis of a plane frame considering geometric nonlinearity.
- Description:
The frame of
exs_beam2is analysed again, but it is now subjected to a load five times larger than inexs_beam2. Geometric nonlinearity is considered.The frame has the following properties: \(E=200\) GPa, \(A_1=2.0 \times 10^{-3}\) m², \(I_1=1.6 \times 10^{-5}\) m⁴, \(A_2=6.0 \times 10^{-3}\) m², \(I_2=5.4 \times 10^{-5}\) m⁴, \(P=10.0\) kN, and \(q_0=50.0\) kN/m. The corresponding computational model consists of three beam elements and twelve degrees of freedom.
- Example:
The computation is initialized by defining the topology matrix
edof, containing element numbers and global element degrees of freedom. The element property vectorsep1andep3, the element load vectorseq1,eq2andeq3, and the element coordinate vectorsex1,ex2,ex3,ey1,ey2, andey3are also defined:import numpy as np import calfem.core as cfc import calfem.vis_mpl as cfv edof = np.array([ [4, 5, 6, 1, 2, 3], [7, 8, 9, 10, 11, 12], [4, 5, 6, 7, 8, 9] ]) E = 200e9 A1 = 2e-3 A2 = 6e-3 I1 = 1.6e-5 I2 = 5.4e-5 ep1 = np.array([E, A1, I1]) ep3 = np.array([E, A2, I2]) ex1 = np.array([0.0, 0.0]) ey1 = np.array([4.0, 0.0]) ex2 = np.array([6.0, 6.0]) ey2 = np.array([4.0, 0.0]) ex3 = np.array([0.0, 6.0]) ey3 = np.array([4.0, 4.0]) eq1 = np.array([0.0]) eq2 = np.array([0.0]) eq3 = np.array([-50e3])The beam element function considering geometric nonlinearity
beam2gerequires the value of axial force \(Q_{\bar{x}}\). Since the axial forces are a result of the computation, the computation procedure is iterative. Initially, the axial forces are set to zero, i.e. \(Q_{\bar{x}}^{(1)}=0\), \(Q_{\bar{x}}^{(2)}=0\) and \(Q_{\bar{x}}^{(3)}=0\) which are stored inQX1,QX2andQX3. This means that the first iteration is equivalent to a linear analysis usingbeam2e. To make sure that the first iteration is performed, the scalar used for storing the previous axial force in Element 1QX01is set to 1. To avoid dividing by 0 in the second convergence check, a nonzero but small value is assumed for the initial axial force in Element 1, i.e. \(Q_{\bar{x}, 0}^{(1)}=1 \times 10^{-4}\). In each iteration the axial forcesQX1,QX2andQX3are updated according to the computational result. The iterations continue until the difference in axial forceQX1of the two latest iterations is less than an accepted errorepschosen as \(1.0 \times 10^{-6}\): (QX1-QX01) /QX01\(<\)eps:QX1 = 1e-4 QX2 = 0 QX3 = 0 QX01 = 1 eps = 1e-6 n = 0In each iteration the global stiffness matrix
K(12×12) and the load vectorf(12×1) are initially filled with zeros. The nodal load of 10.0 kN acting at upper left corner of the frame is placed in position 4 of the load vector. Element matrices are computed bybeam2geand assembled usingassem, after which the system of equations is solved usingsolveq. Based on the computed displacementsa, new values of section forces and axial forces are computed bybeam2gs. The axial forces are then extracted from the section force results. The first iteration’s element displacements are saved ined0for comparison with the linear analysis. IfQX1does not converge in 20 iterations the analysis is interrupted:while abs((QX1 - QX01) / QX01) > eps: n += 1 K = np.zeros([12, 12]) f = np.zeros([12, 1]) f[3, 0] = 10e3 Ke1 = cfc.beam2ge(ex1, ey1, ep1, QX1) Ke2 = cfc.beam2ge(ex2, ey2, ep1, QX2) Ke3, fe3 = cfc.beam2ge(ex3, ey3, ep3, QX3, eq3) K = cfc.assem(edof[0, :], K, Ke1) K = cfc.assem(edof[1, :], K, Ke2) K, f = cfc.assem(edof[2, :], K, Ke3, f, fe3) bc = np.array([1, 2, 3, 10, 11]) a, r = cfc.solveq(K, f, bc) ed = cfc.extract_ed(edof, a) QX01 = QX1 es1 = cfc.beam2gs(ex1, ey1, ep1, ed[0, :], QX1, eq1) es2 = cfc.beam2gs(ex2, ey2, ep1, ed[1, :], QX2, eq2) es3 = cfc.beam2gs(ex3, ey3, ep3, ed[2, :], QX3, eq3) QX1 = es1[1] QX2 = es2[1] QX3 = es3[1] if n == 1: ed0 = ed if n > 20: print("The solution does not converge") breakAfter convergence, section forces are computed for visualization with 21 evaluation points along each element:
eq1 = np.array([0.0, eq1.item()]) eq2 = np.array([0.0, eq2.item()]) eq3 = np.array([0.0, eq3.item()]) es1, edi1, eci1 = cfc.beam2s(ex1, ey1, ep1, ed[0, :], eq1, 21) es2, edi2, eci2 = cfc.beam2s(ex2, ey2, ep1, ed[1, :], eq2, 21) es3, edi3, eci3 = cfc.beam2s(ex3, ey3, ep3, ed[2, :], eq3, 21) eci1 = np.ravel(eci1) eci2 = np.ravel(eci2) eci3 = np.ravel(eci3)A displacement diagram is displayed using the visualization module. The deformed frame is shown with both the linear analysis result (from the first iteration) and the nonlinear analysis result:
cfv.figure(1, fig_size=(6, 4)) plotpar = [3, 1, 0] cfv.eldraw2(ex1, ey1, plotpar) cfv.eldraw2(ex2, ey2, plotpar) cfv.eldraw2(ex3, ey3, plotpar) sfac = cfv.scalfact2(ex3, ey3, edi3, 0.1) plotpar = [1, 2, 1] cfv.eldisp2(ex1, ey1, ed[0, :], plotpar, sfac) cfv.eldisp2(ex2, ey2, ed[1, :], plotpar, sfac) cfv.eldisp2(ex3, ey3, ed[2, :], plotpar, sfac) plotpar = [2, 4, 2] cfv.eldisp2(ex1, ey1, ed0[0, :], plotpar, sfac) cfv.eldisp2(ex2, ey2, ed0[1, :], plotpar, sfac) cfv.eldisp2(ex3, ey3, ed0[2, :], plotpar, sfac) cfv.plt.axis([-1.5, 7.5, -0.5, 5.5]) cfv.title("Displacements")Section force diagrams (normal force, shear force, and bending moment) can also be displayed:
# Normal force diagram cfv.figure(2, fig_size=(6, 4)) plotpar = [2, 1] sfac = cfv.scalfact2(ex1, ey1, es1[:, 0], 0.2) cfv.secforce2(ex1, ey1, es1[:, 0], plotpar, sfac, eci1) cfv.secforce2(ex2, ey2, es2[:, 0], plotpar, sfac, eci2) cfv.secforce2(ex3, ey3, es3[:, 0], plotpar, sfac, eci3) cfv.plt.axis([-1.5, 7.5, -0.5, 5.5]) cfv.title("Normal force") # Shear force diagram cfv.figure(3, fig_size=(6, 4)) plotpar = [2, 1] sfac = cfv.scalfact2(ex3, ey3, es3[:, 1], 0.2) cfv.secforce2(ex1, ey1, es1[:, 1], plotpar, sfac, eci1) cfv.secforce2(ex2, ey2, es2[:, 1], plotpar, sfac, eci2) cfv.secforce2(ex3, ey3, es3[:, 1], plotpar, sfac, eci3) cfv.plt.axis([-1.5, 7.5, -0.5, 5.5]) cfv.title("Shear force") # Bending moment diagram cfv.figure(4, fig_size=(6, 4)) plotpar = [2, 1] sfac = cfv.scalfact2(ex3, ey3, es3[:, 2], 0.2) cfv.secforce2(ex1, ey1, es1[:, 2], plotpar, sfac, eci1) cfv.secforce2(ex2, ey2, es2[:, 2], plotpar, sfac, eci2) cfv.secforce2(ex3, ey3, es3[:, 2], plotpar, sfac, eci3) cfv.plt.axis([-1.5, 7.5, -0.5, 5.5]) cfv.title("Bending moment") cfv.show_and_wait()
exn_beam2g_b¶
- Purpose:
Buckling analysis of a plane frame.
- Description:
Buckling safety of the frame analysed in
exn_beam2gis performed. The same computational model as inexn_beam2gis used. First, the same computation as inexn_beam2gis performed. In this computation the linear stiffness matrix is obtained by saving the stiffness matrix established using the functionassemin the first iteration.- Example:
The computation follows the same procedure as in
exn_beam2g, with the addition of topology, element properties, and coordinates definition:import numpy as np import calfem.core as cfc import calfem.vis_mpl as cfv edof = np.array([[4, 5, 6, 1, 2, 3], [7, 8, 9, 10, 11, 12], [4, 5, 6, 7, 8, 9]]) E = 200e9 A1 = 2e-3 A2 = 6e-3 I1 = 1.6e-5 I2 = 5.4e-5 ep1 = np.array([E, A1, I1]) ep3 = np.array([E, A2, I2]) ex1 = np.array([0.0, 0.0]) ey1 = np.array([4.0, 0.0]) ex2 = np.array([6.0, 6.0]) ey2 = np.array([4.0, 0.0]) ex3 = np.array([0.0, 6.0]) ey3 = np.array([4.0, 4.0]) eq1 = np.array([0.0]) eq2 = np.array([0.0]) eq3 = np.array([-50e3]) QX1 = 1e-4 QX2 = 0 QX3 = 0 QX01 = 1 eps = 1e-6 n = 0During the iteration loop, the linear stiffness matrix is saved from the first iteration. The iteration proceeds until convergence:
while abs((QX1 - QX01) / QX01) > eps: n += 1 K = np.zeros([12, 12]) f = np.zeros([12, 1]) f[3, 0] = 10e3 Ke1 = cfc.beam2ge(ex1, ey1, ep1, QX1) Ke2 = cfc.beam2ge(ex2, ey2, ep1, QX2) Ke3, fe3 = cfc.beam2ge(ex3, ey3, ep3, QX3, eq3) K = cfc.assem(edof[0, :], K, Ke1) K = cfc.assem(edof[1, :], K, Ke2) K, f = cfc.assem(edof[2, :], K, Ke3, f, fe3) if n == 1: K0 = K bc = np.array([1, 2, 3, 10, 11]) a, r = cfc.solveq(K, f, bc) ed = cfc.extract_ed(edof, a) QX01 = QX1 es1 = cfc.beam2gs(ex1, ey1, ep1, ed[0, :], QX1, eq1) es2 = cfc.beam2gs(ex2, ey2, ep1, ed[1, :], QX2, eq2) es3 = cfc.beam2gs(ex3, ey3, ep3, ed[2, :], QX3, eq3) QX1 = es1[1] QX2 = es2[1] QX3 = es3[1] if n > 20: print("The solution does not converge") breakOn the basis of the linear stiffness matrix \(\mathbf{K}_0\) and geometric nonlinear stiffness matrix \(\mathbf{K}_a\) obtained in that computation and stored in
K0andK, the generalised eigen value problem \((\mathbf{K}_a - \lambda \mathbf{K}_0)\phi = 0\) is established. Considering prescribed displacements specified inbc, the generalised eigen value problem is solved usingeigen. Thereafter the loading factors corresponding to the buckling modes are computed as \(\alpha_i = \frac{1}{1 - \lambda_i}\):lam, phi = cfc.eigen(K, K0, bc) one = np.ones(lam.shape) alpha = np.divide(one, one - lam) print(alpha[0])The loading factor corresponding to the first buckling mode is \(\alpha_1 = 6.89\). The shape at instability can be visualized using the first eigenvector:
Ed = cfc.extract_ed(edof, -phi[:, 0]) cfv.figure(1, fig_size=(6, 4)) plotpar = [3, 1, 0] cfv.eldraw2(ex1, ey1, plotpar) cfv.eldraw2(ex2, ey2, plotpar) cfv.eldraw2(ex3, ey3, plotpar) sfac = cfv.scalfact2(ex3, ey3, Ed[2, :], 0.1) plotpar = [1, 2, 1] cfv.eldisp2(ex1, ey1, Ed[0, :], plotpar, sfac) cfv.eldisp2(ex2, ey2, Ed[1, :], plotpar, sfac) cfv.eldisp2(ex3, ey3, Ed[2, :], plotpar, sfac) cfv.plt.axis([-1.5, 7.5, -0.5, 5.5]) cfv.title("Shape at instability") cfv.show_and_wait()