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/v1.7.4/examples/PhaseField/results/PlateWithHole/Test/Elas_Isot_Miehe_AT2_DP_optimMesh 2 : 0.000 μm, [0.00e+00; 0.00e+00], 1:1.004 s, tol=0.00e+00
3 : 0.800 μm, [0.00e+00; 0.00e+00], 1:0.959 s, tol=1.00e+00 3.20 % -> 58.03 s
4 : 1.600 μm, [1.03e-07; 3.70e-02], 1:0.964 s, tol=8.04e-01 6.40 % -> 42.31 s
5 : 2.400 μm, [3.74e-05; 3.70e-02], 1:0.956 s, tol=5.54e-01 9.60 % -> 36.02 s
6 : 3.200 μm, [8.41e-05; 3.70e-02], 1:0.965 s, tol=4.37e-01 12.80 % -> 32.87 s
7 : 4.000 μm, [1.50e-04; 3.70e-02], 1:0.958 s, tol=3.60e-01 16.00 % -> 30.19 s
8 : 4.800 μm, [2.34e-04; 3.70e-02], 1:0.963 s, tol=3.05e-01 19.20 % -> 28.36 s
9 : 5.600 μm, [3.37e-04; 3.70e-02], 1:0.956 s, tol=2.65e-01 22.40 % -> 26.49 s
10 : 6.400 μm, [4.60e-04; 3.71e-02], 1:0.963 s, tol=2.34e-01 25.60 % -> 25.19 s
11 : 7.200 μm, [6.01e-04; 4.57e-02], 1:0.957 s, tol=2.10e-01 28.80 % -> 23.66 s
12 : 8.000 μm, [7.63e-04; 5.81e-02], 1:0.963 s, tol=1.90e-01 32.00 % -> 22.51 s
13 : 8.800 μm, [9.46e-04; 7.21e-02], 1:0.960 s, tol=1.73e-01 35.20 % -> 21.20 s
14 : 9.600 μm, [1.15e-03; 8.78e-02], 1:0.962 s, tol=1.59e-01 38.40 % -> 20.05 s
15 : 10.400 μm, [1.38e-03; 1.05e-01], 1:0.955 s, tol=1.47e-01 41.60 % -> 18.78 s
16 : 11.200 μm, [1.62e-03; 1.25e-01], 1:0.961 s, tol=1.37e-01 44.80 % -> 17.76 s
17 : 12.000 μm, [1.89e-03; 1.46e-01], 1:0.957 s, tol=1.28e-01 48.00 % -> 16.58 s
18 : 12.800 μm, [2.19e-03; 1.70e-01], 1:0.963 s, tol=1.21e-01 51.20 % -> 15.60 s
19 : 13.600 μm, [2.50e-03; 1.97e-01], 1:0.956 s, tol=1.14e-01 54.40 % -> 14.42 s
20 : 14.400 μm, [2.85e-03; 2.29e-01], 1:0.964 s, tol=1.07e-01 57.60 % -> 13.48 s
21 : 15.200 μm, [3.21e-03; 2.65e-01], 1:0.956 s, tol=1.02e-01 60.80 % -> 12.33 s
22 : 16.000 μm, [3.61e-03; 3.04e-01], 1:0.968 s, tol=9.68e-02 64.00 % -> 11.43 s
23 : 16.800 μm, [4.03e-03; 3.48e-01], 1:0.956 s, tol=9.23e-02 67.20 % -> 10.27 s
24 : 17.600 μm, [4.47e-03; 3.96e-01], 1:0.965 s, tol=8.82e-02 70.40 % -> 9.33 s
25 : 18.400 μm, [4.95e-03; 4.49e-01], 1:0.962 s, tol=8.44e-02 73.60 % -> 8.28 s
26 : 19.200 μm, [5.45e-03; 5.05e-01], 1:0.961 s, tol=8.10e-02 76.80 % -> 7.26 s
27 : 20.000 μm, [5.98e-03; 5.61e-01], 1:0.956 s, tol=7.80e-02 80.00 % -> 6.21 s
28 : 20.800 μm, [6.53e-03; 6.38e-01], 1:0.963 s, tol=7.53e-02 83.20 % -> 5.25 s
29 : 21.000 μm, [7.11e-03; 7.36e-01], 1:0.959 s, tol=2.04e-02 84.00 % -> 5.11 s
30 : 21.200 μm, [7.30e-03; 8.35e-01], 1:0.964 s, tol=2.10e-02 84.80 % -> 5.01 s
31 : 21.400 μm, [7.46e-03; 9.20e-01], 1:0.961 s, tol=2.34e-02 85.60 % -> 4.85 s
32 : 21.600 μm, [7.64e-03; 9.75e-01], 1:0.970 s, tol=2.99e-02 86.40 % -> 4.73 s
33 : 21.800 μm, [7.87e-03; 9.98e-01], 1:0.962 s, tol=4.11e-02 87.20 % -> 4.52 s
34 : 22.000 μm, [8.18e-03; 1.00e+00], 1:0.963 s, tol=5.57e-02 88.00 % -> 4.34 s
35 : 22.200 μm, [8.57e-03; 1.00e+00], 1:0.961 s, tol=6.35e-02 88.80 % -> 4.12 s
36 : 22.400 μm, [8.97e-03; 1.00e+00], 1:0.966 s, tol=8.48e-02 89.60 % -> 3.92 s
37 : 22.600 μm, [9.40e-03; 1.00e+00], 1:0.967 s, tol=8.07e-02 90.40 % -> 3.70 s
38 : 22.800 μm, [9.82e-03; 1.00e+00], 1:0.970 s, tol=7.54e-02 91.20 % -> 3.46 s
39 : 23.000 μm, [1.01e-02; 1.00e+00], 1:0.965 s, tol=7.34e-02 92.00 % -> 3.19 s
40 : 23.200 μm, [1.04e-02; 1.00e+00], 1:0.968 s, tol=7.26e-02 92.80 % -> 2.93 s
41 : 23.400 μm, [1.06e-02; 1.00e+00], 1:0.968 s, tol=7.46e-02 93.60 % -> 2.65 s
42 : 23.600 μm, [1.08e-02; 1.00e+00], 1:0.969 s, tol=7.51e-02 94.40 % -> 2.36 s
43 : 23.800 μm, [1.10e-02; 1.00e+00], 1:0.970 s, tol=7.56e-02 95.20 % -> 2.05 s
44 : 24.000 μm, [1.11e-02; 1.00e+00], 1:0.968 s, tol=7.53e-02 96.00 % -> 1.73 s
45 : 24.200 μm, [1.12e-02; 1.00e+00], 1:0.966 s, tol=7.52e-02 96.80 % -> 1.40 s
46 : 24.400 μm, [1.14e-02; 1.00e+00], 1:0.979 s, tol=7.46e-02 97.60 % -> 1.08 s
47 : 24.600 μm, [1.15e-02; 1.00e+00], 1:0.968 s, tol=7.42e-02 98.40 % -> 723.82 ms
48 : 24.800 μm, [1.16e-02; 1.00e+00], 1:0.968 s, tol=7.27e-02 99.20 % -> 366.99 ms
49 : 25.000 μm, [1.17e-02; 1.00e+00], 1:0.969 s, tol=7.13e-02 100.00 % -> -0.00 µs
Saved:
/home/docs/checkouts/readthedocs.org/user_builds/easyfea/checkouts/v1.7.4/examples/PhaseField/results/PlateWithHole/Test/Elas_Isot_Miehe_AT2_DP_optimMesh/force-displacement.pickle
Saved:
/home/docs/checkouts/readthedocs.org/user_builds/easyfea/checkouts/v1.7.4/examples/PhaseField/results/PlateWithHole/Test/Elas_Isot_Miehe_AT2_DP_optimMesh/simulation.pickle
Saved:
/home/docs/checkouts/readthedocs.org/user_builds/easyfea/checkouts/v1.7.4/examples/PhaseField/results/PlateWithHole/Test/Elas_Isot_Miehe_AT2_DP_optimMesh/summary.txt
Generate movie 01/24 (4.17 %) 3.23 s
Generate movie 02/24 (8.33 %) 3.00 s
Generate movie 03/24 (12.50 %) 2.88 s
Generate movie 04/24 (16.67 %) 2.73 s
Generate movie 05/24 (20.83 %) 2.57 s
Generate movie 06/24 (25.00 %) 2.43 s
Generate movie 07/24 (29.17 %) 2.34 s
Generate movie 08/24 (33.33 %) 2.19 s
Generate movie 09/24 (37.50 %) 2.07 s
Generate movie 10/24 (41.67 %) 1.92 s
Generate movie 11/24 (45.83 %) 1.79 s
Generate movie 12/24 (50.00 %) 1.63 s
Generate movie 13/24 (54.17 %) 1.49 s
Generate movie 14/24 (58.33 %) 1.38 s
Generate movie 15/24 (62.50 %) 1.22 s
Generate movie 16/24 (66.67 %) 1.10 s
Generate movie 17/24 (70.83 %) 973.95 ms
Generate movie 18/24 (75.00 %) 819.51 ms
Generate movie 19/24 (79.17 %) 680.29 ms
Generate movie 20/24 (83.33 %) 558.76 ms
Generate movie 21/24 (87.50 %) 409.19 ms
Generate movie 22/24 (91.67 %) 277.83 ms
Generate movie 23/24 (95.83 %) 140.17 ms
Generate movie 24/24 (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 Mesh,
23 Simulations,
24 PyVista,
25 Paraview,
26 )
27 from EasyFEA.Geoms import Domain, Circle
28
29 import multiprocessing
30
31 # Display.Clear()
32
33
34 # ----------------------------------------------
35 # Configuration
36 # ----------------------------------------------
37
38 # simu options
39 doSimu = True
40 meshTest = True
41 optimMesh = True
42 useParallel = False
43 nProcs = 4 # number of processes in parallel
44
45 # outputs
46 folder = Folder.Results_Dir()
47 plotMesh = False
48 plotIter = False
49 plotResult = True
50 plotEnergy = False
51 showFig = True
52
53 saveParaview = False
54 makeMovie = True
55
56 # models
57
58 # splits = ["Bourdin","Amor","Miehe","Stress"] # Splits Isotropes
59 # splits = ["He","AnisotStrain","AnisotStress","Zhang"] # Splits Anisotropes
60 # splits = ["Bourdin","Amor","Miehe","Stress","He","AnisotStrain","AnisotStress","Zhang"]
61 # splits = ["Zhang"]
62 # splits = ["AnisotStrain","AnisotStress","Zhang"]
63 splits = ["Miehe"]
64
65 regus = ["AT2"] # ["AT1", "AT2"]
66 # regus = ["AT1", "AT2"]
67
68 l0 = 0.12e-3
69
70 # convergence
71 solver = (
72 Models.PhaseField.SolverType.History
73 ) # ["History", "HistoryDamage", "BoundConstrain"]
74 maxIter = 1000
75 tolConv = 1e-0
76
77 # ----------------------------------------------
78 # Mesh
79 # ----------------------------------------------
80
81
82 def DoMesh(
83 L: float, h: float, diam: float, thickness: float, l0: float, split: str
84 ) -> Mesh:
85 clC = l0 * 2 if meshTest else l0 / 2
86 if optimMesh:
87 clD = l0 * 4
88 refineZone = diam * 1.5 / 2
89 if split in ["Bourdin", "Amor"]:
90 refineGeom = Domain((0, h / 2 - refineZone), (L, h / 2 + refineZone), clC)
91 else:
92 refineGeom = Domain((L / 2 - refineZone, 0), (L / 2 + refineZone, h), clC)
93 else:
94 clD = l0 if meshTest else l0 / 2
95 refineGeom = None
96
97 domain = Domain((0, 0), (L, h), clD)
98 circle = Circle((L / 2, h / 2), diam, clD, isHollow=True)
99
100 # ax = Display.Init_Axes()
101 # domain.Plot(ax, color="k", plotPoints=False)
102 # circle.Plot(ax, color="k", plotPoints=False)
103 # # if refineGeom != None:
104 # # refineGeom.Plot(ax, color='k', plotPoints=False)
105 # # ax.scatter(((L+diam)/2, L/2), (h/2, (h+diam)/2), c='k')
106 # ax.axis("off")
107 # Display.Save_fig(folder, "sample", True)
108
109 mesh = domain.Mesh_2D([circle], ElemType.TRI3, refineGeoms=[refineGeom])
110
111 # ax = Display.Plot_Mesh(mesh, lw=0.3, facecolors="white")
112 # ax.axis("off")
113 # ax.set_title("")
114 # Display.Save_fig(folder, "mesh", transparent=True)
115
116 return mesh
117
118
119 # ----------------------------------------------
120 # Do Simu
121 # ----------------------------------------------
122 def DoSimu(split: str, regu: str) -> str:
123
124 folder_save = Simulations.PhaseField.Folder(
125 folder, "Elas_Isot", split, regu, "DP", tolConv, solver, meshTest, optimMesh
126 )
127
128 Display.MyPrint(folder_save, "green")
129
130 # ----------------------------------------------
131 # Geom
132 # ----------------------------------------------
133
134 L = 15e-3
135 h = 30e-3
136 thickness = 1
137 diam = 6e-3
138
139 # load units
140 unitU = "μm"
141 unitF = "kN/mm"
142 unit = 1e6
143
144 # ----------------------------------------------
145 # Mesh
146 # ----------------------------------------------
147
148 if doSimu:
149 mesh = DoMesh(L, h, diam, thickness, l0, split)
150
151 # Get Nodes
152 nodes_lower = mesh.Nodes_Conditions(lambda x, y, z: y == 0)
153 nodes_upper = mesh.Nodes_Conditions(lambda x, y, z: y == h)
154 nodes_x0y0 = mesh.Nodes_Conditions(lambda x, y, z: (x == 0) & (y == 0))
155 nodes_edges = mesh.Nodes_Tags(["L0", "L1", "L2", "L3"])
156 nodes_upper = mesh.Nodes_Conditions(lambda x, y, z: y == h)
157
158 # ----------------------------------------------
159 # Boundary conditions
160 # ----------------------------------------------
161
162 threshold = 0.6
163 u_max = 25e-6
164 # u_max = 35e-6
165
166 uinc0 = 8e-7 if meshTest else 8e-8
167 uinc1 = 2e-7 if meshTest else 2e-8
168
169 config = f"""
170 while ud <= u_max:
171
172 ud += uinc0 if simu.damage.max() < threshold else uinc1
173
174 u_max = {u_max}
175 uinc0 = {uinc0:.1e} (simu.damage.max() < {threshold})
176 uinc1 = {uinc1:.1e}
177
178 simu.add_dirichlet(nodes_lower, [0], ["y"])
179 simu.add_dirichlet(nodes_x0y0, [0], ["x"])
180 simu.add_dirichlet(nodes_upper, [-ud], ["y"])
181 if dim == 3:
182 simu.add_dirichlet(nodes_y0z0, [0], ["z"])
183 """
184
185 # ----------------------------------------------
186 # Material
187 # ----------------------------------------------
188 E = 12e9
189 v = 0.3
190 planeStress = False
191 material = Models.Elastic.Isotropic(2, E, v, planeStress, thickness)
192
193 gc = 1.4
194 pfm = Models.PhaseField(material, split, regu, gc, l0, solver=solver)
195
196 # ----------------------------------------------
197 # Simulation
198 # ----------------------------------------------
199 simu = Simulations.PhaseField(mesh, pfm, verbosity=False)
200
201 simu.Results_Set_Bc_Summary(config)
202
203 dofsY_upper = simu.Bc_dofs_nodes(nodes_upper, ["y"])
204
205 def Apply_BC(ud: float):
206 simu.Bc_Init()
207 simu.add_dirichlet(nodes_lower, [0], ["y"])
208 simu.add_dirichlet(nodes_x0y0, [0], ["x"])
209 simu.add_dirichlet(nodes_upper, [-ud], ["y"])
210
211 # INIT
212 displacement = []
213 force = []
214 ud = -uinc0
215 iter = 0
216 nDetect = 0
217
218 if plotIter:
219 axIter = Display.Plot_Result(simu, "damage", nodeValues=True)
220
221 force = np.asarray(force)
222 displacement = np.asarray(displacement)
223 _, axLoad = Display.Plot_Force_Displacement(
224 force / unit, displacement * unit, f"ud [{unitU}]", f"f [{unitF}]"
225 )
226
227 while ud <= u_max:
228 iter += 1
229 ud += uinc0 if simu.damage.max() < threshold else uinc1
230
231 Apply_BC(ud)
232
233 u, d, Ku, convergence = simu.Solve(tolConv, maxIter)
234 simu.Save_Iter()
235
236 # stop if the simulation does not converge
237 if not convergence:
238 break
239
240 f = np.sum(Ku[dofsY_upper, :] @ u)
241
242 simu.Results_Set_Iteration_Summary(iter, ud * unit, unitU, ud / u_max, True)
243
244 # Detect damaged edges
245 if np.any(d[nodes_edges] >= 1):
246 nDetect += 1
247 if nDetect == 10:
248 break
249
250 displacement = np.concatenate((displacement, [ud]))
251 force = np.concatenate((force, [f]))
252
253 if plotIter:
254 Display.Plot_Result(simu, "damage", nodeValues=True, ax=axIter)
255 plt.figure(axIter.figure)
256 plt.pause(1e-12)
257
258 force = np.asarray(force)
259 displacement = np.asarray(displacement)
260 Display.Plot_Force_Displacement(
261 force / unit,
262 displacement * unit,
263 f"ud [{unitU}]",
264 f"f [{unitF}]",
265 ax=axLoad,
266 )[1]
267 plt.figure(axLoad.figure)
268 plt.pause(1e-12)
269
270 # ----------------------------------------------
271 # Saving
272 # ----------------------------------------------
273 force = np.asarray(force)
274 displacement = np.asarray(displacement)
275 print()
276 Simulations.Save_pickle(
277 (force, displacement), folder_save, "force-displacement"
278 )
279 simu.Save(folder_save)
280
281 else:
282 # ----------------------------------------------
283 # Load
284 # ----------------------------------------------
285 simu: Simulations.PhaseField = Simulations.Load_Simu(folder_save)
286 force, displacement = Simulations.Load_pickle(folder_save, "force-displacement")
287
288 # ----------------------------------------------
289 # Results
290 # ---------------------------------------------
291 if plotEnergy:
292 Display.Plot_Energy(simu, force, displacement, N=400, folder=folder_save)
293
294 if plotResult:
295 Display.Plot_Result(
296 simu,
297 "damage",
298 nodeValues=True,
299 colorbarIsClose=True,
300 folder=folder_save,
301 filename="damage",
302 )
303 Display.Plot_Mesh(simu)
304 Display.Plot_BoundaryConditions(simu)
305 Display.Plot_Iter_Summary(simu, folder_save, None, None)
306 Display.Plot_Force_Displacement(
307 force / unit,
308 displacement * unit,
309 f"ud [{unitU}]",
310 f"f [{unitF}]",
311 folder_save,
312 )
313
314 if plotMesh:
315 Display.Plot_Mesh(mesh)
316
317 if saveParaview:
318 Paraview.Save_simu(simu, folder_save)
319
320 if makeMovie:
321 simu.Set_Iter(-1)
322 depMax = simu.Result("displacement_norm").max()
323 deformFactor = L * 0.05 / depMax
324
325 iterations = np.arange(0, simu.Niter, simu.Niter // 20)
326
327 def Func(plotter, iter):
328 simu.Set_Iter(iterations[iter])
329
330 grid = PyVista._pvMesh(simu, "damage", deformFactor)
331
332 tresh = grid.threshold((0, 0.8))
333
334 PyVista.Plot(
335 tresh,
336 "damage",
337 deformFactor,
338 plotMesh=True,
339 plotter=plotter,
340 clim=(0, 1),
341 )
342
343 PyVista.Movie_func(Func, iterations.size, folder_save, "damage.gif")
344
345 if doSimu:
346 Tic.Plot_History(folder_save, details=False)
347
348 if showFig:
349 plt.show()
350
351 # Display.plt.close("all")
352 Tic.Clear()
353
354 return folder_save
355
356
357 if __name__ == "__main__":
358 # generates configs
359 Splits = []
360 Regus = []
361 for split in splits.copy():
362 for regu in regus.copy():
363 Splits.append(split)
364 Regus.append(regu)
365
366 list_folder: list[str] = []
367 if useParallel:
368 items = [(split, regu) for split, regu in zip(Splits, Regus)]
369 with multiprocessing.Pool(nProcs) as pool:
370 for result in pool.starmap(DoSimu, items):
371 list_folder.append(result)
372 else:
373 for split, regu in zip(Splits, Regus):
374 list_folder.append(DoSimu(split, regu))
Total running time of the script: (0 minutes 52.419 seconds)





