ddfem 0.0.0__py3-none-any.whl → 0.9.0__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 +223 -0
  3. ddfem/dune.py +3 -0
  4. ddfem/examples/__init__.py +0 -0
  5. ddfem/examples/advection_diffusion.py +74 -0
  6. ddfem/examples/beam.py +147 -0
  7. ddfem/examples/cahn_hilliard.py +67 -0
  8. ddfem/examples/chemical_reaction.py +88 -0
  9. ddfem/examples/constant.py +46 -0
  10. ddfem/examples/five_circle_flat.py +197 -0
  11. ddfem/examples/forchheimer.py +48 -0
  12. ddfem/examples/hyperelasticity.py +88 -0
  13. ddfem/examples/linear_elasticity.py +45 -0
  14. ddfem/examples/plaplace.py +29 -0
  15. ddfem/examples/single_circle.py +135 -0
  16. ddfem/examples/triple_circle.py +217 -0
  17. ddfem/examples/triple_circle_beam.py +208 -0
  18. ddfem/geometry/__init__.py +18 -0
  19. ddfem/geometry/arc.py +48 -0
  20. ddfem/geometry/ball.py +24 -0
  21. ddfem/geometry/box.py +33 -0
  22. ddfem/geometry/domain.py +49 -0
  23. ddfem/geometry/domain_dune.py +82 -0
  24. ddfem/geometry/helpers.py +42 -0
  25. ddfem/geometry/pie.py +31 -0
  26. ddfem/geometry/plane.py +20 -0
  27. ddfem/geometry/primitive_base.py +338 -0
  28. ddfem/geometry/vesica.py +49 -0
  29. ddfem/model2ufl.py +151 -0
  30. ddfem/transformers/DDM1.py +36 -0
  31. ddfem/transformers/Fitted.py +77 -0
  32. ddfem/transformers/Mix0.py +107 -0
  33. ddfem/transformers/NNS.py +82 -0
  34. ddfem/transformers/NS.py +86 -0
  35. ddfem/transformers/__init__.py +6 -0
  36. ddfem/transformers/transformer_base.py +213 -0
  37. ddfem-0.9.0.dist-info/METADATA +26 -0
  38. ddfem-0.9.0.dist-info/RECORD +41 -0
  39. {ddfem-0.0.0.dist-info → ddfem-0.9.0.dist-info}/WHEEL +1 -1
  40. ddfem-0.9.0.dist-info/licenses/LICENSE +19 -0
  41. ddfem-0.0.0.dist-info/METADATA +0 -5
  42. ddfem-0.0.0.dist-info/RECORD +0 -5
  43. {ddfem-0.0.0.dist-info → ddfem-0.9.0.dist-info}/top_level.txt +0 -0
@@ -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
@@ -0,0 +1,208 @@
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.utility import gridWidth
23
+ from dune.fem.view import adaptiveLeafGridView
24
+ from dune.grid import cartesianDomain
25
+ from dune.ufl import Constant, Space
26
+ from ufl import SpatialCoordinate, as_vector, sqrt
27
+
28
+ from ddfem import geometry as gm
29
+ from ddfem.geometry.domain_dune import DomainDune
30
+
31
+
32
+ def getDomain(initialRefine, version, adaptLevels=0, epsFactor=4.5, dirichlet=True):
33
+
34
+ # gm.SDF.smoothing = 50
35
+
36
+ shiftx, shifty = sqrt(2) * 1e-6, sqrt(3) * 1e-6
37
+
38
+ if dirichlet:
39
+ domain_range = [
40
+ [-0.8 + shiftx, -0.8 + shifty, 0.1], # -0.5
41
+ [0.8 + shiftx, 0.8 + shifty, 3.3], # 3.2
42
+ ]
43
+ initial_gridsize = [60, 60, 60]
44
+ else:
45
+ domain_range = [
46
+ [-0.8 + shiftx, -0.8 + shifty, 0.0 - 0.5],
47
+ [0.8 + shiftx, 0.8 + shifty, 3.2 + 0.5],
48
+ ]
49
+ initial_gridsize = [60, 60, 80]
50
+ initial_gridsize = [i * 2**initialRefine for i in initial_gridsize]
51
+ h = sqrt(
52
+ ((domain_range[1][0] - domain_range[0][0]) / initial_gridsize[0]) ** 2
53
+ + ((domain_range[1][1] - domain_range[0][1]) / initial_gridsize[1]) ** 2
54
+ + ((domain_range[1][2] - domain_range[0][2]) / initial_gridsize[2]) ** 2
55
+ )
56
+
57
+ def get_eps(h):
58
+ return Constant(epsFactor * h * 0.5 ** (adaptLevels / 3), "epsilon")
59
+
60
+ balls = [
61
+ [0.3, [0.15, 0.15], "b1"],
62
+ [0.3, [-0.15, -0.15], "b2"],
63
+ [0.4, [0, 0], "b3"],
64
+ ]
65
+
66
+ b = [gm.Ball(c[0], c[1], name=c[2]) for c in balls]
67
+ sdfs = [b[0] | b[1], b[2]]
68
+ face_2d = sdfs[0] & sdfs[1]
69
+
70
+ face_2d.name = "beam"
71
+
72
+ if dirichlet:
73
+ length = 4
74
+ else:
75
+ length = 3.2
76
+ omega = face_2d.extrude(length, True)
77
+ omega.name = "full"
78
+
79
+ h_max = h * 3
80
+ h_min = h / 2
81
+ radius = 3
82
+
83
+ x = SpatialCoordinate(Space(3))
84
+ sdf = omega(x)
85
+
86
+ def spacing(x, y, z, epsilon):
87
+ r_min = epsilon.value
88
+ r_max = radius * epsilon.value
89
+ dist = np.abs(sdf((x, y, z)))
90
+ if dist <= r_min:
91
+ return geom.characteristic_length_min
92
+ elif dist >= r_max:
93
+ return geom.characteristic_length_max
94
+ else:
95
+ # Linear
96
+ m = (geom.characteristic_length_max - geom.characteristic_length_min) / (
97
+ r_max - r_min
98
+ )
99
+ return m * (dist - r_min) + geom.characteristic_length_min
100
+
101
+ if version == "cartesian":
102
+ domain = cartesianDomain(*domain_range, initial_gridsize)
103
+ epsilon = get_eps(h)
104
+
105
+ elif version == "fitted":
106
+ if pygmsh is None:
107
+ raise AttributeError("'fitted' requires install pygmsh")
108
+ with pygmsh.occ.Geometry() as geom:
109
+ geom.characteristic_length_max = h_max
110
+ geom.characteristic_length_min = h_min
111
+ epsilon = get_eps(h_min)
112
+
113
+ disks = [geom.add_disk([c[1][0], c[1][1], 0.0], c[0]) for c in balls]
114
+
115
+ ds = geom.boolean_union([disks[0], disks[1]])
116
+ shape = geom.boolean_intersection([ds, disks[2]])
117
+ geom.extrude(shape, [0, 0, length])
118
+
119
+ geom.set_mesh_size_callback(
120
+ lambda dim, tag, x, y, z, lc: spacing(x, y, z, epsilon),
121
+ ignore_other_mesh_sizes=True,
122
+ )
123
+ mesh = geom.generate_mesh()
124
+ points, cells = mesh.points, mesh.cells_dict
125
+ domain = {
126
+ "vertices": points[:,].astype(float),
127
+ "simplices": cells["tetra"].astype(int),
128
+ }
129
+
130
+ elif version == "dune_adaptive":
131
+ gridsize = [int(j * h / h_max) for j in initial_gridsize]
132
+ domain = cartesianDomain(*domain_range, gridsize)
133
+
134
+ elif version == "gmsh_adaptive":
135
+ if pygmsh is None:
136
+ raise AttributeError("'gmsh_adaptive' requires install pygmsh")
137
+ with pygmsh.occ.Geometry() as geom:
138
+ geom.characteristic_length_max = h_max
139
+ geom.characteristic_length_min = h_min
140
+ epsilon = get_eps(h_min)
141
+
142
+ geom.add_box(
143
+ [0.0, 0.0, 0.0],
144
+ [
145
+ domain_range[1][0] - domain_range[0][0],
146
+ domain_range[1][1] - domain_range[0][1],
147
+ domain_range[1][2] - domain_range[0][2],
148
+ ],
149
+ )
150
+
151
+ geom.set_mesh_size_callback(
152
+ lambda dim, tag, x, y, z, lc: spacing(x, y, z, epsilon),
153
+ ignore_other_mesh_sizes=True,
154
+ )
155
+ mesh = geom.generate_mesh()
156
+ points, cells = mesh.points, mesh.cells_dict
157
+ domain = {
158
+ "vertices": points[:,].astype(float),
159
+ "simplices": cells["tetra"].astype(int),
160
+ }
161
+ else:
162
+ raise ValueError("invalid mesh type")
163
+
164
+ gridView = adaptiveLeafGridView(leafGridView(domain))
165
+ # return gridView, None
166
+
167
+ if version == "dune_adaptive":
168
+ # omega.epsilon = get_eps(h)
169
+ omega.epsilon = get_eps(h_min)
170
+ omega.epsilon.value *= radius
171
+ epsilon_value = omega.epsilon.value
172
+
173
+ marker = mark
174
+
175
+ refinements = int(3 * np.log2(h_max / h_min))
176
+ region = gridFunction(
177
+ omega.phi(x) * (1 - omega.phi(x)), gridView=gridView
178
+ ) # interface
179
+
180
+ for j in range(1, refinements + 1):
181
+ print("refining:", j, gridView.size(2), flush=True)
182
+ if gridView.size(2) > 5e5:
183
+ assert j == refinements
184
+ j -= 1
185
+ break
186
+ omega.epsilon.value = epsilon_value * j / refinements
187
+ marker(region, 0.00247262315663, maxLevel=refinements) # epsilon
188
+ adapt(gridView.hierarchicalGrid)
189
+
190
+ # markNeighbors(region, 0.1, maxLevel=refinements)
191
+ # marker(region, 0.2, maxLevel=refinements)
192
+ # adapt(gridView.hierarchicalGrid)
193
+ # omega.epsilon.value = omega.epsilon.value * 2**(-1/3)
194
+
195
+ h_min = h_max * 0.5 ** (j / 3)
196
+ epsilon = get_eps(h_min)
197
+
198
+ omega.propagate_epsilon(epsilon)
199
+ domain = omega
200
+
201
+ domain = DomainDune(omega, x, gridView)
202
+ # domain.adapt(level=adaptLevels)
203
+
204
+ print(
205
+ f"h_max={h_max}, h_min={h_min * 0.5 ** (adaptLevels / 3)}, epsilon={epsilon.value}"
206
+ )
207
+
208
+ return gridView, domain
@@ -0,0 +1,18 @@
1
+ from .arc import Arc
2
+ from .ball import Ball
3
+ from .box import Box
4
+ from .pie import Pie
5
+ from .plane import Plane
6
+ from .primitive_base import (
7
+ SDF,
8
+ Intersection,
9
+ Invert,
10
+ Rotate,
11
+ Round,
12
+ Scale,
13
+ Subtraction,
14
+ Translate,
15
+ Union,
16
+ Xor,
17
+ )
18
+ from .vesica import Vesica
ddfem/geometry/arc.py ADDED
@@ -0,0 +1,48 @@
1
+ from ufl import as_vector, conditional, cos, sin
2
+
3
+ from .helpers import ufl_length
4
+ from .primitive_base import ORIGIN, SDF
5
+
6
+
7
+ class Arc(SDF):
8
+ """SDF for an arc:
9
+
10
+ Provides the signed distance function for an arc given the
11
+ radius, angle of opening, the width, and the center (which defaults to the origin).
12
+ """
13
+
14
+ def __init__(self, radius, angle, width, center=ORIGIN, *args, **kwargs):
15
+ self.radius = radius
16
+ self.angle = angle # angle of opening
17
+ self.width = width
18
+
19
+ if isinstance(center, (list, tuple)):
20
+ center = as_vector(center)
21
+ self.center = center
22
+
23
+ super().__init__(*args, **kwargs)
24
+
25
+ def __repr__(self):
26
+ return f"Arc({self.radius}, {self.angle}, {self.width}, {self.center}, {self._repr_core()})"
27
+
28
+ def sdf(self, x):
29
+ # Note: Ignores z
30
+ y0_abs = abs(x[1])
31
+ coords = as_vector([x[0], y0_abs])
32
+
33
+ center_radius = self.radius - self.width / 2
34
+
35
+ distance = ufl_length(coords) - center_radius
36
+
37
+ edge_point = center_radius * as_vector([cos(self.angle), sin(self.angle)])
38
+
39
+ left_coords = coords - edge_point
40
+
41
+ sign_dist = conditional(
42
+ (sin(self.angle) * x[0]) > (cos(self.angle) * y0_abs),
43
+ ufl_length(left_coords),
44
+ abs(distance),
45
+ )
46
+
47
+ sign_dist -= self.width / 2
48
+ return sign_dist
ddfem/geometry/ball.py ADDED
@@ -0,0 +1,24 @@
1
+ from ufl import as_vector
2
+
3
+ from .helpers import ufl_length
4
+ from .primitive_base import ORIGIN, SDF
5
+
6
+
7
+ class Ball(SDF):
8
+ def __init__(self, radius, center=ORIGIN, *args, **kwargs):
9
+ self.radius = radius
10
+
11
+ if isinstance(center, (list, tuple)):
12
+ center = as_vector(center)
13
+ self.center = center
14
+
15
+ super().__init__(*args, **kwargs)
16
+
17
+ def __repr__(self):
18
+ return f"Ball({self.radius}, {self.center}, {self._repr_core()})"
19
+
20
+ def sdf(self, x):
21
+ # Note ignore z, if center 2d
22
+ xx = as_vector([x[i] for i in range(len(self.center))])
23
+ center_x = xx - self.center
24
+ return ufl_length(center_x) - self.radius
ddfem/geometry/box.py ADDED
@@ -0,0 +1,33 @@
1
+ from ufl import as_vector, conditional, max_value, min_value
2
+
3
+ from .helpers import ufl_length
4
+ from .primitive_base import ORIGIN, SDF
5
+
6
+
7
+ class Box(SDF):
8
+ def __init__(self, width, height, center=ORIGIN, *args, **kwargs):
9
+ self.width = width
10
+ self.height = height
11
+
12
+ if isinstance(center, (list, tuple)):
13
+ center = as_vector(center)
14
+ self.center = center
15
+
16
+ super().__init__(*args, **kwargs)
17
+
18
+ def __repr__(self):
19
+ return f"Box({self.width}, {self.height}, {self.center}, {self._repr_core()})"
20
+
21
+ def sdf(self, x):
22
+ # Note: Ignores z
23
+ # shift x
24
+ center_x = x - self.center
25
+ # aux functions
26
+ w0 = abs(center_x[0]) - self.width / 2
27
+ w1 = abs(center_x[1]) - self.height / 2
28
+
29
+ g = max_value(w0, w1)
30
+
31
+ q = as_vector([max_value(w0, 0), max_value(w1, 0)])
32
+
33
+ return conditional(g > 0, ufl_length(q), g)
@@ -0,0 +1,49 @@
1
+ from ufl import conditional, dot, grad, sqrt
2
+
3
+ from .primitive_base import SDF
4
+
5
+
6
+ class Domain:
7
+ def __init__(self, omega):
8
+ self.omega = omega
9
+
10
+ def phi(self, x):
11
+ tol = 1e-10
12
+ return (1 - tol) * self.omega.phi(x) + tol
13
+
14
+ def chi(self, x):
15
+ return self.omega.chi(x)
16
+
17
+ def scaled_normal(self, x):
18
+ return -grad(self.phi(x))
19
+
20
+ def surface_delta(self, x):
21
+ return sqrt(dot(self.scaled_normal(x), self.scaled_normal(x)))
22
+
23
+ def normal(self, x):
24
+ tol = 1e-10
25
+ sd = conditional(self.surface_delta(x) > tol, self.surface_delta(x), tol)
26
+ return self.scaled_normal(x) / sd # grad(self.omega(x))
27
+
28
+ def boundary_projection(self, x):
29
+ return x + self.omega.projection(x)
30
+
31
+ def external_projection(self, x):
32
+ return x + self.omega.projection(x) * (1 - self.chi(x))
33
+
34
+ def bndSDFs(self, SDFname):
35
+ if isinstance(SDFname, SDF):
36
+ SDFname = SDFname.name
37
+
38
+ sdf = self.omega.search(SDFname)
39
+ if sdf is None:
40
+ raise ValueError(f"No SDF with name {SDFname}")
41
+ return sdf
42
+
43
+ def bndProjSDFs(self, SDFname):
44
+ sdf = self.bndSDFs(SDFname)
45
+ return self.generate_projSDF(sdf)
46
+
47
+ def generate_projSDF(self, sdf):
48
+ w = lambda x: sdf.phi(x) * (1 - sdf.phi(x))
49
+ return lambda x: w(self.boundary_projection(x))
@@ -0,0 +1,82 @@
1
+ import time
2
+
3
+ import dune.fem
4
+ import dune.grid
5
+
6
+ from .domain import Domain
7
+
8
+
9
+ class DomainDune(Domain):
10
+ def __init__(self, omega, x, gridView):
11
+ super().__init__(omega)
12
+ self.x = x
13
+ self.gridView = gridView
14
+
15
+ self._phi = None
16
+ self._bndProj = None
17
+ self._extProj = None
18
+ self._bndProjSDFs = {}
19
+
20
+ self.fullSDF = self.gridFunction(self.omega(self.x), name="full-sdf")
21
+
22
+ def gridFunction(self, expr, name):
23
+ start_ = time.time()
24
+ gf = dune.fem.function.gridFunction(expr, name=name, gridView=self.gridView)
25
+ print(f"{name} setup: {time.time() - start_}")
26
+ return gf
27
+
28
+ def phi(self, x):
29
+ if self._phi is None:
30
+ p = super().phi(self.x)
31
+ self._phi = self.gridFunction(p, "phidomain")
32
+
33
+ return self._phi
34
+
35
+ def boundary_projection(self, x):
36
+ if self._bndProj is None:
37
+ p = super().boundary_projection(self.x)
38
+ self._bndProj = self.gridFunction(p, "bndproj")
39
+
40
+ return self._bndProj
41
+
42
+ def external_projection(self, x):
43
+ if self._extProj is None:
44
+ p = super().external_projection(self.x)
45
+ self._extProj = self.gridFunction(p, "extproj")
46
+
47
+ return self._extProj
48
+
49
+ def generate_projSDF(self, sdf):
50
+ projSDF = self._bndProjSDFs.get(sdf.name)
51
+ if projSDF is None:
52
+ projSDF = super().generate_projSDF(sdf)
53
+ gf = self.gridFunction(projSDF(self.x), f"sdfproj{sdf.name}")
54
+ self._bndProjSDFs[sdf.name] = lambda x: gf
55
+ projSDF = self._bndProjSDFs[sdf.name]
56
+ return projSDF
57
+
58
+ def adapt(self, level, lowerTol=-0.1, upperTol=0.1):
59
+ for _ in range(level):
60
+
61
+ def mark(e):
62
+ v = self.fullSDF(e, self.gridView.dimension * [1.0 / 3.0])
63
+ return (
64
+ dune.grid.Marker.refine
65
+ if v > lowerTol and v < upperTol
66
+ else dune.grid.Marker.keep
67
+ )
68
+
69
+ self.gridView.hierarchicalGrid.adapt(mark)
70
+ print(self.gridView.size(0))
71
+ lowerTol /= 2
72
+ upperTol /= 2
73
+
74
+ def _filter(self, tolerance=1):
75
+ sd = self.fullSDF
76
+ tol = tolerance # * self.epsilon.value: issue here with epsilon being a UFL expression due to CSG approach
77
+ phiFilter = (
78
+ lambda e: sd(e, self.gridView.dimension * [1.0 / 3.0]) < tol
79
+ ) # bary needs fixing for squares
80
+ return dune.fem.view.filteredGridView(
81
+ self.gridView, phiFilter, domainId=1, useFilteredIndexSet=True
82
+ )