ddfem 1.0.0__py3-none-any.whl → 1.0.1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (43) hide show
  1. ddfem/__init__.py +4 -0
  2. ddfem/boundary.py +12 -3
  3. ddfem/examples/__init__.py +0 -0
  4. ddfem/examples/advection_diffusion.py +74 -0
  5. ddfem/examples/beam.py +147 -0
  6. ddfem/examples/cahn_hilliard.py +67 -0
  7. ddfem/examples/chemical_reaction.py +88 -0
  8. ddfem/examples/constant.py +46 -0
  9. ddfem/examples/five_circle_flat.py +197 -0
  10. ddfem/examples/forchheimer.py +48 -0
  11. ddfem/examples/hyperelasticity.py +88 -0
  12. ddfem/examples/linear_elasticity.py +45 -0
  13. ddfem/examples/plaplace.py +29 -0
  14. ddfem/examples/single_circle.py +135 -0
  15. ddfem/examples/triple_circle.py +217 -0
  16. ddfem/examples/triple_circle_beam.py +208 -0
  17. ddfem/geometry/__init__.py +3 -3
  18. ddfem/geometry/arc.py +10 -8
  19. ddfem/geometry/ball.py +24 -0
  20. ddfem/geometry/box.py +7 -8
  21. ddfem/geometry/domain.py +16 -4
  22. ddfem/geometry/domain_dune.py +16 -0
  23. ddfem/geometry/helpers.py +19 -12
  24. ddfem/geometry/pie.py +7 -7
  25. ddfem/geometry/plane.py +20 -0
  26. ddfem/geometry/primitive_base.py +129 -70
  27. ddfem/geometry/vesica.py +7 -6
  28. ddfem/model2ufl.py +10 -4
  29. ddfem/transformers/DDM1.py +10 -68
  30. ddfem/transformers/Fitted.py +22 -12
  31. ddfem/transformers/Mix0.py +10 -64
  32. ddfem/transformers/NNS.py +11 -72
  33. ddfem/transformers/NS.py +14 -79
  34. ddfem/transformers/__init__.py +1 -0
  35. ddfem/transformers/transformer_base.py +102 -15
  36. {ddfem-1.0.0.dist-info → ddfem-1.0.1.dist-info}/METADATA +2 -6
  37. ddfem-1.0.1.dist-info/RECORD +41 -0
  38. ddfem/base_model.py +0 -200
  39. ddfem/geometry/circle.py +0 -39
  40. ddfem-1.0.0.dist-info/RECORD +0 -27
  41. {ddfem-1.0.0.dist-info → ddfem-1.0.1.dist-info}/WHEEL +0 -0
  42. {ddfem-1.0.0.dist-info → ddfem-1.0.1.dist-info}/licenses/LICENSE +0 -0
  43. {ddfem-1.0.0.dist-info → ddfem-1.0.1.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,48 @@
1
+ from dune.ufl import Constant
2
+ from ufl import as_vector, div, exp, grad, inner, sqrt
3
+
4
+ from ddfem.boundary import BndFlux_v, BndValue
5
+
6
+
7
+ def fhModel(inverted):
8
+ class Model:
9
+ dimRange = 1
10
+ outFactor_i = Constant(1, "outFactor")
11
+
12
+ def initial(x):
13
+ return 1 / 2 * (x[0] ** 2 + x[1] ** 2) - 1 / 3 * (x[0] ** 3 - x[1] ** 3) + 1
14
+
15
+ def exact(t, x):
16
+ return as_vector([exp(-2 * t) * (Model.initial(x) - 1) + 1])
17
+
18
+ def K(U, DU):
19
+ # DU = exp(-2 * t) * (x[0] - x[0]^2, x[1] + x[1]^2)
20
+ return 2 / (1 + sqrt(1 + 4 * sqrt(inner(DU, DU))))
21
+
22
+ def F_v(t, x, U, DU):
23
+ return Model.K(U, DU) * DU
24
+
25
+ def S_i(t, x, U, DU):
26
+ return -div(
27
+ Model.F_v(t, x, Model.exact(t, x), grad(Model.exact(t, x)))
28
+ ) + as_vector([-2 * exp(-2 * t) * (Model.initial(x) - 1)])
29
+
30
+ valD = BndValue(lambda t, x, U: Model.exact(t, x))
31
+
32
+ valN = BndFlux_v(
33
+ lambda t, x, U, DU, n: Model.F_v(
34
+ t, x, Model.exact(t, x), grad(Model.exact(t, x))
35
+ )
36
+ * n
37
+ )
38
+
39
+ boundary = {
40
+ "sides": valD,
41
+ "ends": valN,
42
+ }
43
+
44
+ if inverted:
45
+ for i in range(1, 5):
46
+ boundary[i] = valD
47
+
48
+ return Model
@@ -0,0 +1,88 @@
1
+ from dune.ufl import Constant
2
+ from ufl import (
3
+ Identity,
4
+ as_vector,
5
+ conditional,
6
+ det,
7
+ diff,
8
+ div,
9
+ dot,
10
+ grad,
11
+ ln,
12
+ nabla_div,
13
+ nabla_grad,
14
+ outer,
15
+ replace,
16
+ sym,
17
+ tr,
18
+ variable,
19
+ zero,
20
+ )
21
+ from ufl.algorithms.ad import expand_derivatives
22
+
23
+ from ddfem.boundary import BndFlux_v, BndValue
24
+
25
+
26
+ def hyModel():
27
+ # https://jsdokken.com/dolfinx-tutorial/chapter2/hyperelasticity.html
28
+ class Model:
29
+ dimRange = 2
30
+ rho = Constant(1000, name="density") # kg/m3
31
+ g = Constant(9.8, name="gravity") # m/s2
32
+
33
+ body = Constant([0.0, -rho * g], "body") # N/m3
34
+ vd = Constant([0, 0], "fixedboundary")
35
+ T = Constant([0, 0], "traction") # Pa
36
+
37
+ outFactor_i = Constant(1, "outFactor")
38
+
39
+ # Elasticity parameters
40
+ # https://www.efunda.com/formulae/solid_mechanics/mat_mechanics/calc_elastic_constants.cfm
41
+ E = 1e7 # Young modulus ; Pa
42
+ nu = 0.4 # Poisson ratio ;
43
+ lamb = Constant(E * nu / ((1 + nu) * (1 - 2 * nu)), name="Lame_1") # Pa
44
+ mu = Constant(E / (2 * (1 + nu)), name="Lame_2") # i.e. Shear modulus ; Pa
45
+
46
+ def P(U, DU):
47
+ I = Identity(Model.dimRange)
48
+ # Stress
49
+ # Hyper-elasticity, (compressible neo-Hookean model)
50
+ F = variable(I + grad(U)) # Deformation gradient
51
+ C = F.T * F # Right Cauchy-Green tensor
52
+ J = det(F)
53
+ Ic = tr(C) # First Invariant
54
+ mu = Model.mu
55
+ lamb = Model.lamb
56
+ psi = (mu / 2) * (Ic - 3) - mu * ln(J) + (lamb / 2) * (ln(J)) ** 2
57
+
58
+ p = diff(psi, F)
59
+ p = expand_derivatives(p)
60
+ p = replace(p, {F: I + DU})
61
+ # p = replace(p, {F: I + grad(U)})
62
+
63
+ # Linear-elasticity
64
+ # p = lamb * tr(sym(DU)) * I + 2 * mu * sym(DU)
65
+
66
+ return p
67
+
68
+ def F_v(t, x, U, DU):
69
+ return Model.P(U, DU)
70
+
71
+ def S_i(t, x, U, DU):
72
+ return Model.body
73
+
74
+ valD = lambda t, x, U: Model.vd
75
+
76
+ valN = lambda t, x, U, DU, n: Model.T
77
+
78
+ boundary = {
79
+ "left": BndValue(valD),
80
+ "other": BndFlux_v(valN),
81
+ }
82
+ # boundary = {
83
+ # "left": BndValue(valD),
84
+ # "right": BndFlux_v(valN),
85
+ # "bulk": BndFlux_v(valN),
86
+ # }
87
+
88
+ return Model
@@ -0,0 +1,45 @@
1
+ from dune.ufl import Constant
2
+ from ufl import Identity, as_vector, div, grad, sym, tr, zero
3
+
4
+ from ddfem.boundary import BndFlux_v, BndValue
5
+
6
+
7
+ def leModel():
8
+ class Model:
9
+ dimRange = 2
10
+ lamb = Constant(0.1, name="Lame_1")
11
+ mu = Constant(1, name="Lame_2")
12
+ rho = Constant(1 / 1000, name="density")
13
+ g = Constant(9.8, name="gravity")
14
+
15
+ outFactor_i = Constant(1, "outFactor")
16
+
17
+ vd = Constant([0, 0], "stationary")
18
+ vn = Constant([0, 0], "traction")
19
+
20
+ I = Identity(dimRange)
21
+
22
+ def sigma(U, DU):
23
+ return Model.lamb * tr(sym(DU)) * Model.I + 2 * Model.mu * sym(DU)
24
+
25
+ def F_v(t, x, U, DU):
26
+ return Model.sigma(U, DU)
27
+
28
+ def S_i(t, x, U, DU):
29
+ return as_vector([0.0, -Model.rho * Model.g])
30
+
31
+ valD = lambda t, x, U: Model.vd
32
+
33
+ valN = lambda t, x, U, DU, n: Model.vn
34
+
35
+ # boundary = {
36
+ # "left": BndValue(valD),
37
+ # "other": BndFlux_v(valN),
38
+ # }
39
+ boundary = {
40
+ "left": BndValue(valD),
41
+ "right": BndFlux_v(valN),
42
+ "bulk": BndFlux_v(valN),
43
+ }
44
+
45
+ return Model
@@ -0,0 +1,29 @@
1
+ from dune.ufl import Constant
2
+ from ufl import grad, inner
3
+
4
+ from ddfem.boundary import BndValue
5
+
6
+
7
+ def pModel(power):
8
+ class Model:
9
+ dimRange = 1
10
+ ep = Constant(1e-5, name="ep")
11
+ f = Constant([1], name="f")
12
+ g = Constant([0], name="g")
13
+ p = Constant(power, name="p") # p for p-Laplacian
14
+ assert p.value > 1 and p.value < 2**16
15
+ outFactor_i = Constant(1, "outFactor")
16
+
17
+ def K(u):
18
+ return (Model.ep**2 + inner(grad(u), grad(u))) ** ((Model.p - 2) / 2)
19
+
20
+ def F_v(t, x, U, DU):
21
+ return Model.K(U) * DU
22
+
23
+ def S_i(t, x, U, DU):
24
+ return Model.f - U
25
+
26
+ bc = BndValue(g)
27
+ boundary = {1: bc, 2: bc, 3: bc, 4: bc, "full": bc}
28
+
29
+ return Model
@@ -0,0 +1,135 @@
1
+ import numpy as np
2
+ try:
3
+ import pygmsh
4
+ except ImportError:
5
+ pygmsh = None
6
+
7
+ try:
8
+ import dune
9
+ except ImportError:
10
+ print("""
11
+ Example code requires dune to run. To install run
12
+ pip install dune-fem
13
+ """)
14
+
15
+ from ddfem import geometry as gm
16
+ from ddfem.geometry.domain_dune import DomainDune
17
+
18
+ from dune.alugrid import aluConformGrid as leafGridView
19
+ from dune.fem.view import adaptiveLeafGridView
20
+ from dune.grid import cartesianDomain
21
+ from dune.ufl import Constant, Space
22
+
23
+ from ufl import SpatialCoordinate, sqrt
24
+
25
+ def getDomain(initialRefine, version, inverted, adaptLevels=0, epsFactor=4.5):
26
+
27
+ shiftx, shifty = sqrt(2) * 1e-6, sqrt(3) * 1e-6
28
+ domain_range = [[-0.5 + shiftx, -0.5 + shifty], [0.5 + shiftx, 0.5 + shifty]]
29
+ initial_gridsize = [75 * 2**initialRefine] * 2
30
+ h = sqrt(
31
+ ((domain_range[1][0] - domain_range[0][0]) / initial_gridsize[0]) ** 2
32
+ + ((domain_range[1][1] - domain_range[0][1]) / initial_gridsize[1]) ** 2
33
+ )
34
+ epsilon = Constant(epsFactor * h * 0.5 ** (adaptLevels / 2), "epsilon")
35
+
36
+ print(f"h={h * 0.5 ** (adaptLevels / 2)}, epsilon={epsilon.value}")
37
+
38
+ circles = [
39
+ [0.3, [0.15, 0.15], "b1"],
40
+ [0.3, [-0.15, -0.15], "b2"],
41
+ [0.4, [0, 0], "b3"],
42
+ ]
43
+
44
+ b = [gm.Ball(c[0], c[1], epsilon=epsilon, name=c[2]) for c in circles]
45
+ sdfs = [b[0] | b[1], b[2]]
46
+ sdfs[0].name = "sides"
47
+ sdfs[1].name = "ends"
48
+ omega = b[2] # sdfs[0] & sdfs[1]
49
+ if inverted:
50
+ omega = omega.invert()
51
+ omega.name = "full"
52
+
53
+ x = SpatialCoordinate(Space(2))
54
+ sdf = omega(x)
55
+
56
+ def spacing(x, y):
57
+ r_min = 2*epsilon.value
58
+ r_max = 32 * epsilon.value
59
+ dist = np.abs(sdf((x, y)))
60
+ if dist <= r_min:
61
+ return geom.characteristic_length_min
62
+ elif dist >= r_max:
63
+ return geom.characteristic_length_max
64
+ else:
65
+ # Linear
66
+ m = (geom.characteristic_length_max - geom.characteristic_length_min) / (
67
+ r_max - r_min
68
+ )
69
+ return m * (dist - r_min) + geom.characteristic_length_min
70
+
71
+ if version == "cartesian":
72
+ domain = cartesianDomain(*domain_range, initial_gridsize)
73
+
74
+ elif version == "fitted":
75
+ if pygmsh is None:
76
+ raise AttributeError("'fitted' requires install pygmsh")
77
+ with pygmsh.occ.Geometry() as geom:
78
+ geom.characteristic_length_max = h
79
+ geom.characteristic_length_min = h
80
+
81
+ disks = geom.add_disk([circles[2][1][0], circles[2][1][1], 0.0], circles[2][0])
82
+ # [geom.add_disk([c[1][0], c[1][1], 0.0], c[0]) for c in circles]
83
+
84
+ # ds = geom.boolean_union([disks[0], disks[1]])
85
+ shape = disks # geom.boolean_intersection([ds, disks[2]])
86
+ if inverted:
87
+ rectangle = geom.add_rectangle(
88
+ [domain_range[0][0], domain_range[0][1], 0.0],
89
+ domain_range[1][0] - domain_range[0][0],
90
+ domain_range[1][1] - domain_range[0][1],
91
+ )
92
+ geom.boolean_difference(rectangle, shape)
93
+
94
+ geom.set_mesh_size_callback(
95
+ lambda dim, tag, x, y, z, lc: spacing(x, y),
96
+ ignore_other_mesh_sizes=True,
97
+ )
98
+ mesh = geom.generate_mesh()
99
+ points, cells = mesh.points, mesh.cells_dict
100
+ domain = {
101
+ "vertices": points[:, :2].astype(float),
102
+ "simplices": cells["triangle"].astype(int),
103
+ }
104
+
105
+ elif version == "adaptive":
106
+ if pygmsh is None:
107
+ raise AttributeError("'adaptive' requires install pygmsh")
108
+ with pygmsh.occ.Geometry() as geom:
109
+ geom.characteristic_length_max = h
110
+ geom.characteristic_length_min = h
111
+
112
+ geom.add_rectangle(
113
+ [domain_range[0][0], domain_range[0][1], 0.0],
114
+ domain_range[1][0] - domain_range[0][0],
115
+ domain_range[1][1] - domain_range[0][1],
116
+ )
117
+
118
+ geom.set_mesh_size_callback(
119
+ lambda dim, tag, x, y, z, lc: spacing(x, y),
120
+ ignore_other_mesh_sizes=True,
121
+ )
122
+ mesh = geom.generate_mesh()
123
+ points, cells = mesh.points, mesh.cells_dict
124
+ domain = {
125
+ "vertices": points[:, :2].astype(float),
126
+ "simplices": cells["triangle"].astype(int),
127
+ }
128
+
129
+ gridView = adaptiveLeafGridView(leafGridView(domain))
130
+
131
+ domain = DomainDune(omega, x, gridView)
132
+ domain.adapt(level=adaptLevels)
133
+ gridView.plot()
134
+
135
+ return gridView, domain
@@ -0,0 +1,217 @@
1
+ import numpy as np
2
+
3
+ try:
4
+ import pygmsh
5
+ except ImportError:
6
+ pygmsh = None
7
+
8
+ try:
9
+ import dune
10
+ except ImportError:
11
+ print(
12
+ """
13
+ Example code requires dune to run. To install run
14
+ pip install dune-fem
15
+ """
16
+ )
17
+
18
+ from dune.alugrid import aluConformGrid as leafGridView
19
+ from dune.fem import adapt, mark, markNeighbors
20
+ from dune.fem.function import gridFunction
21
+ from dune.fem.space import lagrange
22
+ from dune.fem.view import adaptiveLeafGridView
23
+ from dune.grid import cartesianDomain
24
+ from dune.ufl import Constant, Space
25
+ from ufl import SpatialCoordinate, sqrt
26
+
27
+ from ddfem import geometry as gm
28
+ from ddfem.geometry.domain_dune import DomainDune
29
+
30
+
31
+ def getDomain(
32
+ initialRefine, version, inverted, adaptLevels=0, epsFactor=4.5, smoothing=None
33
+ ):
34
+
35
+ gm.SDF.smoothing = smoothing
36
+ shiftx, shifty = sqrt(2) * 1e-6, sqrt(3) * 1e-6
37
+ domain_range = [[-0.5 + shiftx, -0.5 + shifty], [0.5 + shiftx, 0.5 + shifty]]
38
+ initial_gridsize = [100 * 2**initialRefine] * 2
39
+ h = sqrt(
40
+ ((domain_range[1][0] - domain_range[0][0]) / initial_gridsize[0]) ** 2
41
+ + ((domain_range[1][1] - domain_range[0][1]) / initial_gridsize[1]) ** 2
42
+ )
43
+
44
+ def get_eps(h):
45
+ return Constant(epsFactor * h * 0.5 ** (adaptLevels / 2), "epsilon")
46
+
47
+ balls = [
48
+ [0.3, [0.15, 0.15], "b1"],
49
+ [0.3, [-0.15, -0.15], "b2"],
50
+ [0.4, [0, 0], "b3"],
51
+ ]
52
+
53
+ b = [gm.Ball(c[0], c[1], name=c[2]) for c in balls]
54
+ sdfs = [b[0] | b[1], b[2]]
55
+ sdfs[0].name = "sides"
56
+ sdfs[1].name = "ends"
57
+ omega = sdfs[0] & sdfs[1]
58
+ if inverted:
59
+ omega = omega.invert()
60
+ omega.name = "full"
61
+
62
+ h_max = h * 3
63
+ h_min = h / 2
64
+ radius = 5
65
+
66
+ x = SpatialCoordinate(Space(2))
67
+ sdf = omega(x)
68
+
69
+ def spacing(x, y, epsilon):
70
+ r_min = epsilon.value
71
+ r_max = radius * epsilon.value
72
+ dist = np.abs(sdf((x, y)))
73
+ if dist <= r_min:
74
+ return geom.characteristic_length_min
75
+ elif dist >= r_max:
76
+ return geom.characteristic_length_max
77
+ else:
78
+ # Linear
79
+ m = (geom.characteristic_length_max - geom.characteristic_length_min) / (
80
+ r_max - r_min
81
+ )
82
+ return m * (dist - r_min) + geom.characteristic_length_min
83
+
84
+ if version == "cartesian":
85
+ domain = cartesianDomain(*domain_range, initial_gridsize)
86
+ epsilon = get_eps(h)
87
+
88
+ elif version == "fitted":
89
+ if pygmsh is None:
90
+ raise AttributeError("'fitted' requires install pygmsh")
91
+ with pygmsh.occ.Geometry() as geom:
92
+ geom.characteristic_length_max = h_max
93
+ geom.characteristic_length_min = h_min
94
+ epsilon = get_eps(h_min)
95
+
96
+ disks = [geom.add_disk([c[1][0], c[1][1], 0.0], c[0]) for c in balls]
97
+
98
+ ds = geom.boolean_union([disks[0], disks[1]])
99
+ shape = geom.boolean_intersection([ds, disks[2]])
100
+ if inverted:
101
+ rectangle = geom.add_rectangle(
102
+ [domain_range[0][0], domain_range[0][1], 0.0],
103
+ domain_range[1][0] - domain_range[0][0],
104
+ domain_range[1][1] - domain_range[0][1],
105
+ )
106
+ geom.boolean_difference(rectangle, shape)
107
+
108
+ geom.set_mesh_size_callback(
109
+ lambda dim, tag, x, y, z, lc: spacing(x, y, epsilon),
110
+ ignore_other_mesh_sizes=True,
111
+ )
112
+ mesh = geom.generate_mesh()
113
+ points, cells = mesh.points, mesh.cells_dict
114
+ domain = {
115
+ "vertices": points[:, :2].astype(float),
116
+ "simplices": cells["triangle"].astype(int),
117
+ }
118
+
119
+ elif version == "dune_adaptive":
120
+ gridsize = [int(j * h / h_max) for j in initial_gridsize]
121
+ domain = cartesianDomain(*domain_range, gridsize)
122
+
123
+ elif version == "gmsh_adaptive":
124
+ if pygmsh is None:
125
+ raise AttributeError("'gmsh_adaptive' requires install pygmsh")
126
+ with pygmsh.occ.Geometry() as geom:
127
+ geom.characteristic_length_max = h_max
128
+ geom.characteristic_length_min = h_min
129
+ epsilon = get_eps(h_min)
130
+
131
+ geom.add_rectangle(
132
+ [domain_range[0][0], domain_range[0][1], 0.0],
133
+ domain_range[1][0] - domain_range[0][0],
134
+ domain_range[1][1] - domain_range[0][1],
135
+ )
136
+
137
+ geom.set_mesh_size_callback(
138
+ lambda dim, tag, x, y, z, lc: spacing(x, y, epsilon),
139
+ ignore_other_mesh_sizes=True,
140
+ )
141
+ mesh = geom.generate_mesh()
142
+ points, cells = mesh.points, mesh.cells_dict
143
+ domain = {
144
+ "vertices": points[:, :2].astype(float),
145
+ "simplices": cells["triangle"].astype(int),
146
+ }
147
+
148
+ elif version == "gmsh_embedded":
149
+ if pygmsh is None:
150
+ raise AttributeError("'fitted' requires install pygmsh")
151
+ with pygmsh.occ.Geometry() as geom:
152
+ geom.characteristic_length_max = h_max
153
+ geom.characteristic_length_min = h_min
154
+ epsilon = get_eps(h_min)
155
+
156
+ disks = [geom.add_disk([c[1][0], c[1][1], 0.0], c[0]) for c in balls]
157
+
158
+ ds = geom.boolean_union([disks[0], disks[1]])
159
+ shape = geom.boolean_intersection([ds, disks[2]])
160
+ rectangle = geom.add_rectangle(
161
+ [domain_range[0][0], domain_range[0][1], 0.0],
162
+ domain_range[1][0] - domain_range[0][0],
163
+ domain_range[1][1] - domain_range[0][1],
164
+ )
165
+
166
+ geom.boolean_fragments(rectangle, shape)
167
+
168
+ geom.set_mesh_size_callback(
169
+ lambda dim, tag, x, y, z, lc: spacing(x, y, epsilon),
170
+ ignore_other_mesh_sizes=True,
171
+ )
172
+ mesh = geom.generate_mesh()
173
+ points, cells = mesh.points, mesh.cells_dict
174
+ domain = {
175
+ "vertices": points[:, :2].astype(float),
176
+ "simplices": cells["triangle"].astype(int),
177
+ }
178
+
179
+ else:
180
+ raise ValueError("invalid mesh type")
181
+
182
+ gridView = adaptiveLeafGridView(leafGridView(domain))
183
+
184
+ if version == "dune_adaptive":
185
+ omega.epsilon = get_eps(h_min)
186
+ omega.epsilon.value *= radius
187
+ epsilon_value = omega.epsilon.value
188
+
189
+ marker = mark
190
+
191
+ refinements = int(2 * np.log2(h_max / h_min))
192
+
193
+ region = gridFunction(
194
+ omega.phi(x) * (1 - omega.phi(x)), gridView=gridView
195
+ ) # interface
196
+
197
+ for j in range(1, refinements + 1):
198
+
199
+ omega.epsilon.value = epsilon_value * j / refinements
200
+ marker(region, 0.00247262315663, maxLevel=refinements) # 1 epsilon
201
+
202
+ adapt(gridView.hierarchicalGrid)
203
+
204
+ h_min = h_max * 0.5 ** (j / 2)
205
+ epsilon = get_eps(h_min)
206
+
207
+ omega.propagate_epsilon(epsilon)
208
+ domain = omega
209
+
210
+ domain = DomainDune(omega, x, gridView)
211
+ domain.adapt(level=adaptLevels)
212
+
213
+ print(
214
+ f"h_max={h_max}, h_min={h_min * 0.5 ** (adaptLevels / 2)}, epsilon={epsilon.value}"
215
+ )
216
+
217
+ return gridView, domain