1
+ # This example implements homogenization of porous structures undergoing finite strains.
2
+ #
3
+ # largedef_porous_mac.py - problem at (global) macroscopic level
4
+ # largedef_porous_mic.py - local subproblems, homogenized coefficients
5
+ #
6
+ # The mathematical model and numerical results are described in:
7
+ #
8
+ # LUKEŠ V., ROHAN E.
9
+ # Homogenization of large deforming fluid-saturated porous structures
10
+ # https://arxiv.org/abs/2012.03730
11
+ #
12
+ # Run simulation:
13
+ #
14
+ # ./simple.py example_largedef_porous-1/largedef_porous_mac.py
15
+ #
16
+ # The results are stored in `example_largedef_porous-1/results` directory.
17
+ #
18
+
19
+ import numpy as nm
20
+ import six
21
+ import os .path as osp
22
+ from sfepy import data_dir
23
+ from sfepy .base .base import Struct , output , debug
24
+ from sfepy .terms .terms_hyperelastic_ul import HyperElasticULFamilyData
25
+ from sfepy .homogenization .micmac import get_homog_coefs_nonlinear
26
+ import sfepy .linalg as la
27
+ from sfepy .solvers .ts import TimeStepper
28
+ from sfepy .discrete .state import State
29
+
30
+ wdir = osp .dirname (__file__ )
31
+
32
+ hyperelastic_data = {
33
+ 'update_materials' : True ,
34
+ 'state' : {'u' : None , 'du' : None ,
35
+ 'p' : None , 'dp' : None },
36
+ 'mapping0' : None ,
37
+ 'coors0' : None ,
38
+ 'macro_data' : None ,
39
+ }
40
+
41
+ def post_process (out , pb , state , extend = False ):
42
+ ts = hyperelastic_data ['ts' ]
43
+
44
+ if isinstance (state , dict ):
45
+ pass
46
+ else :
47
+ stress = pb .evaluate ('ev_volume_integrate_mat.i.Omega(solid.S, u)' ,
48
+ mode = 'el_avg' )
49
+
50
+ out ['cauchy_stress' ] = Struct (name = 'output_data' ,
51
+ mode = 'cell' ,
52
+ data = stress )
53
+
54
+ ret_stress = pb .evaluate ('ev_volume_integrate_mat.i.Omega(solid.Q, u)' ,
55
+ mode = 'el_avg' )
56
+
57
+ out ['retardation_stress' ] = Struct (name = 'output_data' ,
58
+ mode = 'cell' ,
59
+ data = ret_stress )
60
+
61
+ strain = pb .evaluate ('ev_volume_integrate_mat.i.Omega(solid.E, u)' ,
62
+ mode = 'el_avg' )
63
+
64
+ out ['green_strain' ] = Struct (name = 'output_data' ,
65
+ mode = 'cell' ,
66
+ data = strain )
67
+
68
+ he_state = hyperelastic_data ['state' ]
69
+ for ch in pb .conf .chs :
70
+ plab = 'p%d' % ch
71
+ out ['p0_%d' % ch ] = Struct (name = 'output_data' ,
72
+ mode = 'vertex' ,
73
+ data = he_state [plab ][:, nm .newaxis ])
74
+
75
+ dvel = pb .evaluate ('ev_diffusion_velocity.i.Omega(solid.C%d, %s)' % (ch , plab ),
76
+ mode = 'el_avg' )
77
+ out ['w%d' % ch ] = Struct (name = 'output_data' ,
78
+ mode = 'cell' ,
79
+ data = dvel )
80
+
81
+ out ['u0' ] = Struct (name = 'output_data' ,
82
+ mode = 'vertex' ,
83
+ data = he_state ['u' ])
84
+
85
+ return out
86
+
87
+
88
+ def homog_macro_map (ccoors , macro , nel ):
89
+ nqpe = ccoors .shape [0 ] // nel
90
+ macro_ = {k : nm .sum (v .reshape ((nel , nqpe ) + v .shape [1 :]), axis = 1 ) / nqpe
91
+ for k , v in macro .items ()}
92
+ macro_ ['recovery_idxs' ] = []
93
+ ccoors_ = nm .sum (ccoors .reshape ((nel , nqpe ) + ccoors .shape [1 :]), axis = 1 ) / nqpe
94
+
95
+ return ccoors_ , macro_
96
+
97
+
98
+ def homog_macro_remap (homcf , ncoor ):
99
+ nqpe = ncoor // homcf ['Volume_total' ].shape [0 ]
100
+ homcf_ = {k : nm .repeat (v , nqpe , axis = 0 ) for k , v in homcf .items ()
101
+ if not k == 'Volume_total' }
102
+
103
+ return homcf_
104
+
105
+
106
+ def get_homog_mat (ts , coors , mode , term = None , problem = None , ** kwargs ):
107
+ hyperela = hyperelastic_data
108
+ ts = hyperela ['ts' ]
109
+
110
+ output ('get_homog_mat: mode=%s, update=%s' \
111
+ % (mode , hyperela ['update_materials' ]))
112
+
113
+ if not mode == 'qp' :
114
+ return
115
+
116
+ if not hyperela ['update_materials' ]:
117
+ out = hyperela ['homog_mat' ]
118
+ return {k : nm .array (v ) for k , v in six .iteritems (out )}
119
+
120
+ dim = problem .domain .mesh .dim
121
+ nqp = coors .shape [0 ]
122
+
123
+ state_u = problem .equations .variables ['u' ]
124
+ if len (state_u .field .mappings0 ) == 0 :
125
+ state_u .field .get_mapping (term .region , term .integral ,
126
+ term .integration )
127
+ state_u .field .save_mappings ()
128
+
129
+ state_u .field .clear_mappings ()
130
+ state_u .set_data (hyperela ['state' ]['u' ].ravel ()) # + state_u.data[-1][state_u.indx]
131
+
132
+ mtx_f = problem .evaluate ('ev_def_grad.i.Omega(u)' ,
133
+ mode = 'qp' ).reshape (- 1 , dim , dim )
134
+
135
+ # relative deformation gradient
136
+ if hasattr (problem , 'mtx_f_prev' ):
137
+ rel_mtx_f = la .dot_sequences (mtx_f , nm .linalg .inv (problem .mtx_f_prev ),
138
+ 'AB' )
139
+ else :
140
+ rel_mtx_f = mtx_f
141
+
142
+ problem .mtx_f_prev = mtx_f .copy ()
143
+
144
+ macro_data = {
145
+ 'mtx_e_rel' : rel_mtx_f - nm .eye (dim ), # relative macro strain
146
+ }
147
+
148
+ for ch in problem .conf .chs :
149
+ plab = 'p%d' % ch
150
+ state_p = problem .equations .variables [plab ]
151
+ state_p .set_data (hyperela ['state' ][plab ])
152
+ macro_data ['p%d_0' % ch ] = \
153
+ problem .evaluate ('ev_volume_integrate.i.Omega(p%d)' % ch ,
154
+ mode = 'qp' ).reshape (- 1 , 1 , 1 )
155
+ macro_data ['gp%d_0' % ch ] = \
156
+ problem .evaluate ('ev_grad.i.Omega(p%d)' % ch ,
157
+ mode = 'qp' ).reshape (- 1 , dim , 1 )
158
+
159
+ state_p .set_data (hyperela ['state' ]['d' + plab ])
160
+ macro_data ['dp%d_0' % ch ] = \
161
+ problem .evaluate ('ev_volume_integrate.i.Omega(p%d)' % ch ,
162
+ mode = 'qp' ).reshape (- 1 , 1 , 1 )
163
+ macro_data ['gdp%d_0' % ch ] = \
164
+ problem .evaluate ('ev_grad.i.Omega(p%d)' % ch ,
165
+ mode = 'qp' ).reshape (- 1 , dim , 1 )
166
+
167
+ nel = term .region .entities [- 1 ].shape [0 ]
168
+ ccoors0 , macro_data0 = homog_macro_map (coors , macro_data , nel )
169
+ macro_data0 ['macro_ccoor' ] = ccoors0
170
+ out0 = get_homog_coefs_nonlinear (ts , ccoors0 , mode , macro_data0 ,
171
+ term = term , problem = problem ,
172
+ iteration = ts .step , ** kwargs )
173
+ out0 ['C1' ] += nm .eye (2 ) * 1e-12 # ! auxiliary permeability
174
+ out0 ['C2' ] += nm .eye (2 ) * 1e-12 # ! auxiliary permeability
175
+ out = homog_macro_remap (out0 , nqp )
176
+
177
+ # Green strain
178
+ out ['E' ] = 0.5 * (la .dot_sequences (mtx_f , mtx_f , 'ATB' ) - nm .eye (dim ))
179
+
180
+ for ch in problem .conf .chs :
181
+ out ['B%d' % ch ] = out ['B%d' % ch ].reshape ((nqp , dim , dim ))
182
+ out ['Q' ] = out ['Q' ].reshape ((nqp , dim , dim ))
183
+
184
+ hyperela ['time' ] = ts .step
185
+ hyperela ['homog_mat' ] = \
186
+ {k : nm .array (v ) for k , v in six .iteritems (out )}
187
+ hyperela ['update_materials' ] = False
188
+ hyperela ['macro_data' ] = macro_data
189
+
190
+ return out
191
+
192
+
193
+ def incremental_algorithm (pb ):
194
+ hyperela = hyperelastic_data
195
+ chs = pb .conf .chs
196
+ ts = pb .conf .ts
197
+
198
+ hyperela ['ts' ] = ts
199
+ hyperela ['ofn_trunk' ] = pb .ofn_trunk + '_%03d'
200
+ pb .domain .mesh .coors_act = pb .domain .mesh .coors .copy ()
201
+
202
+ pbvars = pb .get_variables ()
203
+ he_state = hyperela ['state' ]
204
+
205
+ out = []
206
+ out_data = {}
207
+
208
+ coors0 = pbvars ['u' ].field .get_coor ()
209
+
210
+ he_state ['coors0' ] = coors0 .copy ()
211
+ he_state ['u' ] = nm .zeros_like (coors0 )
212
+ he_state ['du' ] = nm .zeros_like (coors0 )
213
+
214
+ for ch in chs :
215
+ plab = 'p%d' % ch
216
+ press0 = pbvars [plab ].field .get_coor ()[:, 0 ].squeeze ()
217
+ he_state [plab ] = nm .zeros_like (press0 )
218
+ he_state ['d' + plab ] = nm .zeros_like (press0 )
219
+
220
+ for step , time in ts :
221
+ print ('>>> step %d (%e):' % (step , time ))
222
+ hyperela ['update_materials' ] = True
223
+
224
+ pb .ofn_trunk = hyperela ['ofn_trunk' ] % step
225
+
226
+ yield pb , out
227
+
228
+ state = out [- 1 ][1 ]
229
+ result = state .get_parts ()
230
+ du = result ['u' ]
231
+
232
+ he_state ['u' ] += du .reshape (he_state ['du' ].shape )
233
+ he_state ['du' ][:] = du .reshape (he_state ['du' ].shape )
234
+ pb .set_mesh_coors (he_state ['u' ] + he_state ['coors0' ],
235
+ update_fields = True , actual = True , clear_all = False )
236
+
237
+ for ch in chs :
238
+ plab = 'p%d' % ch
239
+ dp = result [plab ]
240
+ he_state [plab ] += dp
241
+ he_state ['d' + plab ][:] = dp
242
+
243
+ out_data = post_process (out_data , pb , state , extend = False )
244
+ filename = pb .get_output_name ()
245
+ pb .save_state (filename , out = out_data )
246
+
247
+ yield None
248
+
249
+ print ('<<< step %d finished' % step )
250
+
251
+
252
+ def move (ts , coor , problem = None , ramp = 0.4 , ** kwargs ):
253
+ ts = problem .conf .ts
254
+ nrs = round (ts .n_step * ramp )
255
+ switch = 1 if (ts .step <= nrs ) and (ts .step > 0 ) else 0
256
+ displ = nm .ones ((coor .shape [0 ],)) * problem .conf .move_val / nrs * switch
257
+
258
+ return displ
259
+
260
+
261
+ def define ():
262
+ chs = [1 , 2 ]
263
+
264
+ ts = TimeStepper (0 , 0.15 , n_step = 30 )
265
+
266
+ options = {
267
+ 'output_dir' : osp .join (wdir , 'results' ),
268
+ 'micro_filename' : osp .join (wdir , 'largedef_porous_mic.py' ),
269
+ 'parametric_hook' : 'incremental_algorithm' ,
270
+ }
271
+
272
+ materials = {
273
+ 'solid' : 'get_homog' ,
274
+ }
275
+
276
+ fields = {
277
+ 'displacement' : ('real' , 'vector' , 'Omega' , 1 ),
278
+ 'pressure' : ('real' , 'scalar' , 'Omega' , 1 ),
279
+ }
280
+
281
+ variables = {
282
+ 'u' : ('unknown field' , 'displacement' , 0 ),
283
+ 'v' : ('test field' , 'displacement' , 'u' ),
284
+ 'p1' : ('unknown field' , 'pressure' , 1 ),
285
+ 'q1' : ('test field' , 'pressure' , 'p1' ),
286
+ 'p2' : ('unknown field' , 'pressure' , 2 ),
287
+ 'q2' : ('test field' , 'pressure' , 'p2' ),
288
+ }
289
+
290
+ filename_mesh = osp .join (wdir , 'macro_mesh_3x2.vtk' )
291
+ mesh_d , move_val = 0.24 , - 0.04
292
+
293
+ regions = {
294
+ 'Omega' : 'all' ,
295
+ 'Left' : ('vertices in (x < 0.0001)' , 'facet' ),
296
+ 'Right' : ('vertices in (x > %e)' % (mesh_d * 0.999 ), 'facet' ),
297
+ 'Recovery' : ('cell 1' , 'cell' ),
298
+ }
299
+
300
+ ebcs = {
301
+ 'left_fix_all' : ('Left' , {'u.all' : 0.0 }),
302
+ 'right_fix_x' : ('Right' , {'u.0' : 0.0 }),
303
+ 'right_move_x' : ('Right' , {'u.1' : 'move' }),
304
+ }
305
+
306
+ micro_args = {
307
+ 'eps0' : mesh_d / 3 ,
308
+ 'dt' : ts .dt ,
309
+ }
310
+
311
+ functions = {
312
+ 'move' : (move ,),
313
+ 'get_homog' : (lambda ts , coors , mode , ** kwargs :
314
+ get_homog_mat (ts , coors , mode , define_args = micro_args , ** kwargs ),),
315
+ }
316
+
317
+ integrals = {
318
+ 'i' : 3 ,
319
+ }
320
+
321
+ equations = {
322
+ # eq. (60)
323
+ 'balance_of_forces' : """
324
+ dw_nonsym_elastic.i.Omega(solid.A, v, u)
325
+ - dw_biot.i.Omega(solid.B1, v, p1)
326
+ - dw_biot.i.Omega(solid.B2, v, p2)
327
+ =
328
+ - dw_lin_prestress.i.Omega(solid.S, v)
329
+ - dw_lin_prestress.i.Omega(solid.Q, v)""" ,
330
+ # eq. (61), alpha = 1
331
+ 'mass_conservation_1' : """
332
+ - %e * dw_biot.i.Omega(solid.B1, u, q1)
333
+ - dw_volume_dot.i.Omega(solid.G11, q1, p1)
334
+ - dw_volume_dot.i.Omega(solid.G12, q1, p2)
335
+ - dw_diffusion.i.Omega(solid.C1, q1, p1)
336
+ =
337
+ dw_volume_lvf.i.Omega(solid.Z1, q1)
338
+ """ % (1 / ts .dt ),
339
+ # eq. (61), alpha = 2
340
+ 'mass_conservation_2' : """
341
+ - %e * dw_biot.i.Omega(solid.B2, u, q2)
342
+ - dw_volume_dot.i.Omega(solid.G21, q2, p1)
343
+ - dw_volume_dot.i.Omega(solid.G22, q2, p2)
344
+ - dw_diffusion.i.Omega(solid.C2, q2, p2)
345
+ =
346
+ dw_volume_lvf.i.Omega(solid.Z2, q2)
347
+ """ % (1. / ts .dt ),
348
+ }
349
+
350
+ solvers = {
351
+ 'ls' : ('ls.scipy_direct' , {}),
352
+ 'newton' : ('nls.newton' , {
353
+ 'eps_a' : 1e-3 ,
354
+ 'eps_r' : 1e-3 ,
355
+ 'i_max' : 1 ,
356
+ }),
357
+ }
358
+
359
+ return locals ()
0 commit comments