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,197 @@
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,
33
+ version,
34
+ adaptLevels=0,
35
+ epsFactor=4.5,
36
+ smoothing=None,
37
+ *args,
38
+ **kwargs,
39
+ ):
40
+
41
+ gm.SDF.smoothing = smoothing
42
+ shiftx, shifty = sqrt(2) * 1e-6, sqrt(3) * 1e-6
43
+ domain_range = [[-1.7 + shiftx, -1.3 + shifty], [1.7 + shiftx, 1.3 + shifty]]
44
+ initial_gridsize = [170 * 2**initialRefine, 130 * 2**initialRefine]
45
+ h = sqrt(
46
+ ((domain_range[1][0] - domain_range[0][0]) / initial_gridsize[0]) ** 2
47
+ + ((domain_range[1][1] - domain_range[0][1]) / initial_gridsize[1]) ** 2
48
+ )
49
+
50
+ def get_eps(h):
51
+ return Constant(epsFactor * h * 0.5 ** (adaptLevels / 2), "epsilon")
52
+
53
+ balls = [
54
+ [1, [0, 0], "Center"],
55
+ [0.5, [0, 0.8], "TopCut"],
56
+ [0.5, [0, -0.8], "BotCut"],
57
+ [0.5, [1, 0], "RightAdd"],
58
+ [0.5, [-1, 0], "LeftAdd"],
59
+ [1.4, [0, 0], "Cutoff"],
60
+ ]
61
+
62
+ b = [gm.Ball(c[0], c[1], name=c[2]) for c in balls]
63
+ # omega = (b[0] - b[1] - b[2] | b[3] | b[4]) & b[5]
64
+
65
+ bulk = b[0] - b[1] - b[2] | b[3] | b[4]
66
+ bulk.name = "bulk"
67
+
68
+ left_cutoff = b[5] | gm.Plane([-1, 0], -0.4)
69
+ left_cutoff.name = "left"
70
+ right_cutoff = b[5] | gm.Plane([1, 0], -0.4)
71
+ right_cutoff.name = "right"
72
+
73
+ cutoff = left_cutoff & right_cutoff # b[5]
74
+
75
+ omega = bulk & cutoff
76
+ omega.name = "full"
77
+
78
+ h_max = h * 3
79
+ h_min = h / 2
80
+ radius = 5
81
+
82
+ x = SpatialCoordinate(Space(2))
83
+ sdf = omega(x)
84
+
85
+ def spacing(x, y, epsilon):
86
+ r_min = epsilon.value
87
+ r_max = radius * epsilon.value
88
+ dist = np.abs(sdf((x, y)))
89
+ if dist <= r_min:
90
+ return geom.characteristic_length_min
91
+ elif dist >= r_max:
92
+ return geom.characteristic_length_max
93
+ else:
94
+ # Linear
95
+ m = (geom.characteristic_length_max - geom.characteristic_length_min) / (
96
+ r_max - r_min
97
+ )
98
+ return m * (dist - r_min) + geom.characteristic_length_min
99
+
100
+ if version == "cartesian":
101
+ domain = cartesianDomain(*domain_range, initial_gridsize)
102
+ epsilon = get_eps(h)
103
+
104
+ elif version == "fitted":
105
+ if pygmsh is None:
106
+ raise AttributeError("'fitted' requires install pygmsh")
107
+ with pygmsh.occ.Geometry() as geom:
108
+ geom.characteristic_length_max = h_max
109
+ geom.characteristic_length_min = h_min
110
+ epsilon = get_eps(h_min)
111
+
112
+ disks = [geom.add_disk([c[1][0], c[1][1], 0.0], c[0]) for c in balls]
113
+
114
+ disk_removed1 = geom.boolean_difference(disks[0], disks[1])
115
+ disk_removed2 = geom.boolean_difference(disk_removed1, disks[2])
116
+
117
+ disk_add = geom.boolean_union([disk_removed2, disks[3], disks[4]])
118
+ shape = geom.boolean_intersection([disk_add, disks[5]])
119
+
120
+ geom.set_mesh_size_callback(
121
+ lambda dim, tag, x, y, z, lc: spacing(x, y, epsilon),
122
+ ignore_other_mesh_sizes=True,
123
+ )
124
+ mesh = geom.generate_mesh()
125
+ points, cells = mesh.points, mesh.cells_dict
126
+ domain = {
127
+ "vertices": points[:, :2].astype(float),
128
+ "simplices": cells["triangle"].astype(int),
129
+ }
130
+
131
+ elif version == "dune_adaptive":
132
+ gridsize = [int(j * h / h_max) for j in initial_gridsize]
133
+ domain = cartesianDomain(*domain_range, gridsize)
134
+
135
+ elif version == "gmsh_adaptive":
136
+ if pygmsh is None:
137
+ raise AttributeError("'gmsh_adaptive' requires install pygmsh")
138
+ with pygmsh.occ.Geometry() as geom:
139
+ geom.characteristic_length_max = h_max
140
+ geom.characteristic_length_min = h_min
141
+ epsilon = get_eps(h_min)
142
+
143
+ geom.add_rectangle(
144
+ [domain_range[0][0], domain_range[0][1], 0.0],
145
+ domain_range[1][0] - domain_range[0][0],
146
+ domain_range[1][1] - domain_range[0][1],
147
+ )
148
+
149
+ geom.set_mesh_size_callback(
150
+ lambda dim, tag, x, y, z, lc: spacing(x, y, epsilon),
151
+ ignore_other_mesh_sizes=True,
152
+ )
153
+ mesh = geom.generate_mesh()
154
+ points, cells = mesh.points, mesh.cells_dict
155
+ domain = {
156
+ "vertices": points[:, :2].astype(float),
157
+ "simplices": cells["triangle"].astype(int),
158
+ }
159
+ else:
160
+ raise ValueError("invalid mesh type")
161
+
162
+ gridView = adaptiveLeafGridView(leafGridView(domain))
163
+
164
+ if version == "dune_adaptive":
165
+ omega.epsilon = get_eps(h_min)
166
+ omega.epsilon.value *= radius
167
+ epsilon_value = omega.epsilon.value
168
+
169
+ marker = mark
170
+
171
+ refinements = int(2 * np.log2(h_max / h_min))
172
+
173
+ region = gridFunction(
174
+ omega.phi(x) * (1 - omega.phi(x)), gridView=gridView
175
+ ) # interface
176
+
177
+ for j in range(1, refinements + 1):
178
+
179
+ omega.epsilon.value = epsilon_value * j / refinements
180
+ marker(region, 0.00247262315663, maxLevel=refinements) # 1 epsilon
181
+
182
+ adapt(gridView.hierarchicalGrid)
183
+
184
+ h_min = h_max * 0.5 ** (j / 2)
185
+ epsilon = get_eps(h_min)
186
+
187
+ omega.propagate_epsilon(epsilon)
188
+ domain = omega
189
+
190
+ domain = DomainDune(omega, x, gridView)
191
+ domain.adapt(level=adaptLevels)
192
+
193
+ print(
194
+ f"h_max={h_max}, h_min={h_min * 0.5 ** (adaptLevels / 2)}, epsilon={epsilon.value}"
195
+ )
196
+
197
+ return gridView, domain
@@ -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