Note
Go to the end to download the full example code.
PlateWithHole#
Damage simulation for a plate with a hole subjected to compression.

/home/docs/checkouts/readthedocs.org/user_builds/easyfea/checkouts/latest/examples/PhaseField/results/PlateWithHole/Test/Miehe_AT1_optimMesh
2: 0.000μm, [0.00e+00;0.00e+00], 1:0.213s, tol=0.00e+00
3: 0.800μm, [0.00e+00;0.00e+00], 1:0.177s, tol=1.00e+00 3.20% -> 10.73s
4: 1.600μm, [0.00e+00;0.00e+00], 1:0.177s, tol=7.50e-01 6.40% -> 7.79s
5: 2.400μm, [0.00e+00;0.00e+00], 1:0.180s, tol=5.56e-01 9.60% -> 6.79s
6: 3.200μm, [0.00e+00;0.00e+00], 1:0.180s, tol=4.38e-01 12.80% -> 6.15s
7: 4.000μm, [0.00e+00;0.00e+00], 1:0.178s, tol=3.60e-01 16.00% -> 5.60s
8: 4.800μm, [0.00e+00;0.00e+00], 1:0.178s, tol=3.06e-01 19.20% -> 5.25s
9: 5.600μm, [0.00e+00;0.00e+00], 1:0.179s, tol=2.65e-01 22.40% -> 4.95s
10: 6.400μm, [0.00e+00;0.00e+00], 1:0.176s, tol=2.34e-01 25.60% -> 4.60s
11: 7.200μm, [0.00e+00;0.00e+00], 1:0.176s, tol=2.10e-01 28.80% -> 4.35s
12: 8.000μm, [0.00e+00;0.00e+00], 1:0.176s, tol=1.90e-01 32.00% -> 4.12s
13: 8.800μm, [0.00e+00;0.00e+00], 1:0.177s, tol=1.74e-01 35.20% -> 3.91s
14: 9.600μm, [0.00e+00;0.00e+00], 1:0.179s, tol=1.60e-01 38.40% -> 3.73s
15: 10.400μm, [0.00e+00;0.00e+00], 1:0.177s, tol=1.48e-01 41.60% -> 3.48s
16: 11.200μm, [0.00e+00;0.00e+00], 1:0.177s, tol=1.38e-01 44.80% -> 3.27s
17: 12.000μm, [0.00e+00;0.00e+00], 1:0.178s, tol=1.29e-01 48.00% -> 3.09s
18: 12.800μm, [0.00e+00;0.00e+00], 1:0.178s, tol=1.21e-01 51.20% -> 2.88s
19: 13.600μm, [0.00e+00;0.00e+00], 1:0.177s, tol=1.14e-01 54.40% -> 2.67s
20: 14.400μm, [0.00e+00;0.00e+00], 1:0.180s, tol=1.08e-01 57.60% -> 2.51s
21: 15.200μm, [0.00e+00;0.00e+00], 1:0.178s, tol=1.02e-01 60.80% -> 2.30s
22: 16.000μm, [0.00e+00;0.00e+00], 1:0.178s, tol=9.75e-02 64.00% -> 2.11s
23: 16.800μm, [9.79e-13;4.17e-03], 1:0.179s, tol=9.30e-02 67.20% -> 1.92s
24: 17.600μm, [1.02e-11;4.36e-02], 1:0.179s, tol=8.88e-02 70.40% -> 1.73s
25: 18.400μm, [1.09e-11;1.18e-01], 1:0.179s, tol=8.49e-02 73.60% -> 1.54s
26: 19.200μm, [7.24e-12;2.14e-01], 1:0.178s, tol=8.14e-02 76.80% -> 1.34s
27: 20.000μm, [4.04e-12;3.31e-01], 1:0.180s, tol=7.82e-02 80.00% -> 1.17s
28: 20.800μm, [2.70e-12;5.03e-01], 1:0.179s, tol=7.50e-02 83.20% -> 976.86ms
29: 21.600μm, [1.48e-12;7.38e-01], 1:0.179s, tol=7.24e-02 86.40% -> 790.12ms
30: 21.800μm, [1.01e-12;9.34e-01], 1:0.178s, tol=2.12e-02 87.20% -> 757.10ms
31: 22.000μm, [1.11e-12;9.98e-01], 1:0.179s, tol=3.65e-02 88.00% -> 732.46ms
32: 22.200μm, [1.22e-12;1.01e+00], 1:0.177s, tol=6.20e-02 88.80% -> 690.57ms
33: 22.400μm, [1.36e-12;1.00e+00], 1:0.177s, tol=8.53e-02 89.60% -> 656.65ms
34: 22.600μm, [1.35e-12;1.01e+00], 1:0.178s, tol=9.21e-02 90.40% -> 623.37ms
35: 22.800μm, [1.44e-12;1.00e+00], 1:0.178s, tol=1.06e-01 91.20% -> 584.65ms
36: 23.000μm, [1.49e-12;1.00e+00], 1:0.179s, tol=1.02e-01 92.00% -> 545.18ms
37: 23.200μm, [1.63e-12;1.00e+00], 1:0.179s, tol=9.02e-02 92.80% -> 498.94ms
38: 23.400μm, [1.67e-12;1.00e+00], 1:0.180s, tol=9.63e-02 93.60% -> 455.41ms
39: 23.600μm, [1.75e-12;1.00e+00], 1:0.181s, tol=1.02e-01 94.40% -> 407.45ms
40: 23.800μm, [1.79e-12;1.00e+00], 1:0.181s, tol=1.03e-01 95.20% -> 356.61ms
41: 24.000μm, [1.68e-12;1.00e+00], 1:0.178s, tol=1.01e-01 96.00% -> 296.23ms
42: 24.200μm, [1.43e-12;1.00e+00], 1:0.178s, tol=9.89e-02 96.80% -> 240.60ms
43: 24.400μm, [1.26e-12;1.00e+00], 1:0.177s, tol=1.01e-01 97.60% -> 182.80ms
44: 24.600μm, [1.10e-12;1.00e+00], 1:0.176s, tol=9.77e-02 98.40% -> 123.34ms
45: 24.800μm, [1.02e-12;1.00e+00], 1:0.177s, tol=9.67e-02 99.20% -> 62.93ms
46: 25.000μm, [9.35e-13;1.00e+00], 1:0.178s, tol=9.35e-02 100.00% -> -0.00µs
Saved mesh data in:
/home/docs/checkouts/readthedocs.org/user_builds/easyfea/checkouts/latest/examples/PhaseField/results/PlateWithHole/Test/Miehe_AT1_optimMesh/Meshes
Saved simulation and summary in:
/home/docs/checkouts/readthedocs.org/user_builds/easyfea/checkouts/latest/examples/PhaseField/results/PlateWithHole/Test/Miehe_AT1_optimMesh
Generate movie 01/23 (4.35 %) 3.78 s
Generate movie 02/23 (8.70 %) 3.36 s
Generate movie 03/23 (13.04 %) 3.23 s
Generate movie 04/23 (17.39 %) 3.14 s
Generate movie 05/23 (21.74 %) 2.94 s
Generate movie 06/23 (26.09 %) 2.78 s
Generate movie 07/23 (30.43 %) 2.64 s
Generate movie 08/23 (34.78 %) 2.47 s
Generate movie 09/23 (39.13 %) 2.34 s
Generate movie 10/23 (43.48 %) 2.19 s
Generate movie 11/23 (47.83 %) 2.00 s
Generate movie 12/23 (52.17 %) 1.84 s
Generate movie 13/23 (56.52 %) 1.68 s
Generate movie 14/23 (60.87 %) 1.54 s
Generate movie 15/23 (65.22 %) 1.35 s
Generate movie 16/23 (69.57 %) 1.18 s
Generate movie 17/23 (73.91 %) 1.01 s
Generate movie 18/23 (78.26 %) 844.04 ms
Generate movie 19/23 (82.61 %) 681.36 ms
Generate movie 20/23 (86.96 %) 511.09 ms
Generate movie 21/23 (91.30 %) 338.72 ms
Generate movie 22/23 (95.65 %) 170.15 ms
Generate movie 23/23 (100.00 %) 0.00 µs
13 import matplotlib.pyplot as plt
14 import numpy as np
15
16 from EasyFEA import (
17 Display,
18 Folder,
19 Models,
20 Tic,
21 ElemType,
22 Simulations,
23 PyVista,
24 Paraview,
25 )
26 from EasyFEA.Geoms import Domain, Circle
27
28 if __name__ == "__main__":
29
30 # ----------------------------------------------
31 # Configuration
32 # ----------------------------------------------
33
34 # simu options
35 doSimu = True
36 meshTest = True
37 optimMesh = True
38
39 # outputs
40 folder = Folder.Results_Dir()
41 plotMesh = False
42 plotIter = False
43 plotEnergy = False
44
45 makeParaview = False
46 makeMovie = True
47
48 # Available splits: Bourdin, Amor, Miehe, Stress (isotropic)
49 # He, AnisotStrain, AnisotStress, Zhang (anisotropic)
50 split = Models.PhaseField.SplitType.Miehe
51
52 # Available regus: AT1, AT2
53 regu = Models.PhaseField.ReguType.AT1
54
55 l0 = 0.12e-3 # m
56
57 # convergence
58 solver = (
59 Models.PhaseField.SolverType.History
60 ) # History, HistoryDamage, BoundConstrain
61 maxIter = 1000
62 tolConv = 1e-0
63
64 # load units
65 unitU = "μm"
66 unitF = "kN/mm"
67 unit = 1e6
68
69 # ----------------------------------------------
70 # Geometry
71 # ----------------------------------------------
72 L = 15e-3 # m
73 h = 30e-3 # m
74 diam = 6e-3 # m
75 thickness = 1
76
77 # ----------------------------------------------
78 # Material
79 # ----------------------------------------------
80 E = 12e9 # Pa
81 v = 0.3
82 Gc = 1.4 # J/m2
83
84 folder_save = Simulations.PhaseField.Folder(
85 folder, "", split, regu, "", tolConv, solver, meshTest, optimMesh
86 )
87 Display.MyPrint(folder_save, "green", end="\n")
88
89 if doSimu:
90 # ----------------------------------------------
91 # Mesh
92 # ----------------------------------------------
93 clC = l0 * 2 if meshTest else l0 / 2
94 if optimMesh:
95 clD = l0 * 4
96 refineZone = diam * 1.5 / 2
97 if split in (
98 Models.PhaseField.SplitType.Bourdin,
99 Models.PhaseField.SplitType.Amor,
100 ):
101 refineGeom = Domain(
102 (0, h / 2 - refineZone), (L, h / 2 + refineZone), clC
103 )
104 else:
105 refineGeom = Domain(
106 (L / 2 - refineZone, 0), (L / 2 + refineZone, h), clC
107 )
108 else:
109 clD = l0 if meshTest else l0 / 2
110 refineGeom = None
111
112 domain = Domain((0, 0), (L, h), clD)
113 circle = Circle((L / 2, h / 2), diam, clD)
114 mesh = domain.Mesh_2D([circle], ElemType.TRI3, refineGeoms=[refineGeom])
115
116 # Nodes
117 nodes_lower = mesh.Nodes_Conditions(lambda x, y, z: y == 0)
118 nodes_upper = mesh.Nodes_Conditions(lambda x, y, z: y == h)
119 nodes_x0y0 = mesh.Nodes_Conditions(lambda x, y, z: (x == 0) & (y == 0))
120 nodes_edges = mesh.Nodes_Tags(["L0", "L1", "L2", "L3"])
121
122 # ----------------------------------------------
123 # Material
124 # ----------------------------------------------
125 material = Models.Elastic.Isotropic(
126 2, E, v, planeStress=False, thickness=thickness
127 )
128 pfm = Models.PhaseField(material, split, regu, Gc, l0, solver=solver)
129
130 # ----------------------------------------------
131 # Boundary conditions
132 # ----------------------------------------------
133 threshold = 0.6
134 u_max = 25e-6
135 uinc0 = 8e-7 if meshTest else 8e-8
136 uinc1 = 2e-7 if meshTest else 2e-8
137
138 config = f"""
139 uinc0 = {uinc0:.1e}; uinc1 = {uinc1:.1e}
140
141 while ud <= u_max = {u_max:.2e}:
142
143 ud += uinc0 if simu.damage.max() < {threshold} else uinc1
144
145 simu.add_dirichlet(nodes_lower, [0], ["y"])
146 simu.add_dirichlet(nodes_x0y0, [0], ["x"])
147 simu.add_dirichlet(nodes_upper, [-ud], ["y"])
148 """
149
150 # ----------------------------------------------
151 # Simulation
152 # ----------------------------------------------
153 simu = Simulations.PhaseField(mesh, pfm, folder=folder_save)
154 simu.Results_Set_Bc_Summary(config)
155
156 dofsY_upper = simu.Bc_dofs_nodes(nodes_upper, ["y"])
157
158 def Apply_BC(ud: float):
159 simu.Bc_Init()
160 simu.add_dirichlet(nodes_lower, [0], ["y"])
161 simu.add_dirichlet(nodes_x0y0, [0], ["x"])
162 simu.add_dirichlet(nodes_upper, [-ud], ["y"])
163
164 list_dep = []
165 list_f = []
166 ud = -uinc0
167 iter = 0
168 nDetect = 0
169
170 if plotIter:
171 axIter = Display.Plot_Result(simu, "damage", nodeValues=True)
172 _, axLoad = plt.subplots()
173 axLoad.set_xlabel(f"ud [{unitU}]")
174 axLoad.set_ylabel(f"f [{unitF}]")
175 axLoad.grid()
176
177 while ud <= u_max:
178 iter += 1
179 ud += uinc0 if simu.damage.max() < threshold else uinc1
180
181 Apply_BC(ud)
182
183 u, d, convergence = simu.Solve(tolConv, maxIter)
184 simu.Save_Iter()
185
186 if not convergence:
187 break
188
189 f = np.sum(simu.Calc_Reaction(dofsY_upper, "elastic"))
190 simu.Results_Set_Iteration_Summary(iter, ud * unit, unitU, ud / u_max, True)
191
192 if simu.Detect_Damage(nodes_edges, 1):
193 nDetect += 1
194 if nDetect == 10:
195 break
196
197 list_dep.append(ud)
198 list_f.append(f)
199
200 if plotIter:
201 Display.Plot_Result(simu, "damage", nodeValues=True, ax=axIter)
202 plt.figure(axIter.figure)
203 plt.pause(1e-12)
204 axLoad.clear()
205 axLoad.plot(np.abs(list_dep * unit), np.abs(list_f / unit), c="blue")
206 axLoad.set_xlabel(f"ud [{unitU}]")
207 axLoad.set_ylabel(f"f [{unitF}]")
208 axLoad.grid()
209 plt.figure(axLoad.figure)
210 plt.pause(1e-12)
211
212 # ----------------------------------------------
213 # Saving
214 # ----------------------------------------------
215 print()
216 Simulations.Save_pickle((list_f, list_dep), folder_save, "force-displacement")
217 simu.Save(folder_save)
218
219 else:
220 simu: Simulations.PhaseField = Simulations.Load_Simu(folder_save)
221 list_f, list_dep = Simulations.Load_pickle(folder_save, "force-displacement")
222
223 # ----------------------------------------------
224 # Results
225 # ----------------------------------------------
226 if plotEnergy:
227 Display.Plot_Energy(simu, list_f, list_dep, N=400, folder=folder_save)
228
229 Display.Plot_Result(
230 simu,
231 "damage",
232 nodeValues=True,
233 colorbarIsClose=True,
234 folder=folder_save,
235 filename="damage",
236 )
237 Display.Plot_Mesh(simu)
238 Display.Plot_BoundaryConditions(simu)
239 Display.Plot_Iter_Summary(simu, folder_save, None, None)
240
241 ax = Display.Init_Axes()
242 ax.plot(np.abs(list_dep) * unit, np.abs(list_f) / unit, c="blue")
243 ax.set_xlabel(f"ud [{unitU}]")
244 ax.set_ylabel(f"f [{unitF}]")
245 ax.grid()
246 Display.Save_fig(folder_save, "force-displacement")
247
248 if plotMesh:
249 Display.Plot_Mesh(simu.mesh)
250
251 if makeParaview:
252 Paraview.Save_simu(simu, folder_save)
253
254 if makeMovie:
255 simu.Set_Iter(-1)
256 deformFactor = L * 0.05 / simu.Result("displacement_norm").max()
257
258 iterations = np.arange(0, simu.Niter, simu.Niter // 20)
259
260 def Func(plotter, iter):
261 simu.Set_Iter(iterations[iter])
262 thresh = PyVista._pvMesh(simu, "damage", deformFactor).threshold((0, 0.8))
263 PyVista.Plot(thresh, "damage", plotMesh=True, plotter=plotter, clim=(0, 1))
264
265 PyVista.Movie_func(Func, iterations.size, folder_save, "damage.gif")
266
267 if doSimu:
268 Tic.Plot_History(folder_save, details=False)
269
270 plt.show()
Total running time of the script: (0 minutes 15.819 seconds)





