ddfem 0.9.0__py3-none-any.whl → 1.0.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.
- ddfem/__init__.py +0 -4
- ddfem/base_model.py +200 -0
- ddfem/boundary.py +3 -12
- ddfem/geometry/__init__.py +3 -3
- ddfem/geometry/arc.py +8 -10
- ddfem/geometry/box.py +8 -7
- ddfem/geometry/circle.py +39 -0
- ddfem/geometry/domain.py +4 -16
- ddfem/geometry/domain_dune.py +0 -16
- ddfem/geometry/helpers.py +12 -19
- ddfem/geometry/pie.py +7 -7
- ddfem/geometry/primitive_base.py +70 -129
- ddfem/geometry/vesica.py +6 -7
- ddfem/model2ufl.py +4 -10
- ddfem/transformers/DDM1.py +68 -10
- ddfem/transformers/Fitted.py +12 -22
- ddfem/transformers/Mix0.py +64 -10
- ddfem/transformers/NNS.py +72 -11
- ddfem/transformers/NS.py +79 -14
- ddfem/transformers/__init__.py +0 -1
- ddfem/transformers/transformer_base.py +15 -102
- {ddfem-0.9.0.dist-info → ddfem-1.0.0.dist-info}/METADATA +1 -2
- ddfem-1.0.0.dist-info/RECORD +27 -0
- ddfem/examples/__init__.py +0 -0
- ddfem/examples/advection_diffusion.py +0 -74
- ddfem/examples/beam.py +0 -147
- ddfem/examples/cahn_hilliard.py +0 -67
- ddfem/examples/chemical_reaction.py +0 -88
- ddfem/examples/constant.py +0 -46
- ddfem/examples/five_circle_flat.py +0 -197
- ddfem/examples/forchheimer.py +0 -48
- ddfem/examples/hyperelasticity.py +0 -88
- ddfem/examples/linear_elasticity.py +0 -45
- ddfem/examples/plaplace.py +0 -29
- ddfem/examples/single_circle.py +0 -135
- ddfem/examples/triple_circle.py +0 -217
- ddfem/examples/triple_circle_beam.py +0 -208
- ddfem/geometry/ball.py +0 -24
- ddfem/geometry/plane.py +0 -20
- ddfem-0.9.0.dist-info/RECORD +0 -41
- {ddfem-0.9.0.dist-info → ddfem-1.0.0.dist-info}/WHEEL +0 -0
- {ddfem-0.9.0.dist-info → ddfem-1.0.0.dist-info}/licenses/LICENSE +0 -0
- {ddfem-0.9.0.dist-info → ddfem-1.0.0.dist-info}/top_level.txt +0 -0
ddfem/__init__.py
CHANGED
ddfem/base_model.py
ADDED
@@ -0,0 +1,200 @@
|
|
1
|
+
import inspect
|
2
|
+
|
3
|
+
import ufl
|
4
|
+
from ufl import as_matrix, as_vector, diff, dot, inner, outer, variable, zero
|
5
|
+
from ufl.algorithms.ad import expand_derivatives
|
6
|
+
from ufl.algorithms.apply_derivatives import apply_derivatives
|
7
|
+
|
8
|
+
|
9
|
+
class BaseModel:
|
10
|
+
boundary = {}
|
11
|
+
|
12
|
+
@staticmethod
|
13
|
+
def function(self, *args, **kwargs):
|
14
|
+
uh = self._function(*args, **kwargs)
|
15
|
+
uh.boundary = self.boundary
|
16
|
+
return uh
|
17
|
+
|
18
|
+
# U_t + div[F_c(x,t,U) - F_v(x,t,U,grad[U]) ] = S(x,t,U, grad[U]).
|
19
|
+
|
20
|
+
@classmethod
|
21
|
+
def F_c_lin(cls, t, x, U):
|
22
|
+
U = variable(U)
|
23
|
+
d = diff(cls.F_c(t, x, U), U)
|
24
|
+
d = apply_derivatives(expand_derivatives(d))
|
25
|
+
return d
|
26
|
+
|
27
|
+
# U.ufl_shape == (1,)
|
28
|
+
# F_c(U).ufl_shape == (1, 2,)
|
29
|
+
# diff(F_c(U), U).ufl_shape == (1, 2, 1)
|
30
|
+
# n.ufl_shape == (2,)
|
31
|
+
#
|
32
|
+
# s, t = F_c(U).ufl_shape
|
33
|
+
# f_c = as_matrix([[dot(d[i, j, :], U) for j in range(t)] for i in range(s)])
|
34
|
+
#
|
35
|
+
# w, = U.ufl_shape
|
36
|
+
# convec = as_vector([dot([f_c[w, :], n) for i in range(w)]) # f_c * n
|
37
|
+
#
|
38
|
+
# switch order
|
39
|
+
|
40
|
+
@classmethod
|
41
|
+
def F_c_lin_mult(cls, t, x, U, n):
|
42
|
+
G = cls.F_c_lin(t, x, U)
|
43
|
+
# try:
|
44
|
+
# d = dot(G, n)
|
45
|
+
# print("F_c dot")
|
46
|
+
# return d
|
47
|
+
# except:
|
48
|
+
m, d, m_ = G.ufl_shape
|
49
|
+
return as_matrix([[dot(G[i, :, k], n) for k in range(m_)] for i in range(m)])
|
50
|
+
|
51
|
+
@classmethod
|
52
|
+
def F_v_lin(cls, t, x, U, DU):
|
53
|
+
DU = variable(DU)
|
54
|
+
d = diff(cls.F_v(t, x, U, DU), DU)
|
55
|
+
d = apply_derivatives(expand_derivatives(d))
|
56
|
+
return d
|
57
|
+
|
58
|
+
@classmethod
|
59
|
+
def F_v_lin_mult(cls, t, x, U, DU, v):
|
60
|
+
G = cls.F_v_lin(t, x, U, DU)
|
61
|
+
# try:
|
62
|
+
# d = dot(G, v)
|
63
|
+
# print("F_v dot")
|
64
|
+
# return d
|
65
|
+
# except:
|
66
|
+
m, d = v.ufl_shape
|
67
|
+
return as_matrix(
|
68
|
+
[[inner(G[i, k, :, :], v) for k in range(d)] for i in range(m)]
|
69
|
+
)
|
70
|
+
|
71
|
+
# avoid issue with variable capturing in lambdas in the for loop below
|
72
|
+
# https://www.geeksforgeeks.org/why-do-python-lambda-defined-in-a-loop-with-different-values-all-return-the-same-result/
|
73
|
+
def _createV(v, U=None):
|
74
|
+
if U is None:
|
75
|
+
return lambda t, x, u: v # classify
|
76
|
+
return lambda t, x: v(t, x, U) # jumpV
|
77
|
+
|
78
|
+
def _createF(v, U=None, DU=None, N=None):
|
79
|
+
if U is None and DU is None and N is None:
|
80
|
+
return lambda t, x, u, _, n: v(t, x, u, n) # classify
|
81
|
+
elif U and N and DU is None:
|
82
|
+
return lambda t, x: v(t, x, U, N) # jumpAF
|
83
|
+
elif U and N and DU:
|
84
|
+
return lambda t, x: v(t, x, U, DU, N) # jumpDF
|
85
|
+
|
86
|
+
@staticmethod
|
87
|
+
def classify_boundary(Model):
|
88
|
+
boundaryDict = getattr(Model, "boundary_d", {})
|
89
|
+
|
90
|
+
boundaryAFlux = {} # Fluxes for the advection term
|
91
|
+
boundaryDFlux = {} # Fluxes for the diffusion term
|
92
|
+
boundaryValue = {} # Boundary values for Dirichlet
|
93
|
+
|
94
|
+
hasAdvFlux = hasattr(Model, "F_c")
|
95
|
+
hasDiffFlux = hasattr(Model, "F_v")
|
96
|
+
|
97
|
+
for k, f in boundaryDict.items():
|
98
|
+
if isinstance(f, (tuple, list)):
|
99
|
+
assert (
|
100
|
+
hasAdvFlux and hasDiffFlux
|
101
|
+
), "two boundary fluxes provided but only one bulk flux given"
|
102
|
+
boundaryAFlux[k], boundaryDFlux[k] = f
|
103
|
+
# (f[0](t, x, u, n), f[1](t, x, u, grad(u), n))
|
104
|
+
|
105
|
+
elif isinstance(f, ufl.core.expr.Expr):
|
106
|
+
boundaryValue[k] = BaseModel._createV(f)
|
107
|
+
|
108
|
+
else:
|
109
|
+
num_args = len(inspect.signature(f).parameters)
|
110
|
+
|
111
|
+
if num_args == 3:
|
112
|
+
boundaryValue[k] = f # f(t, x, u)
|
113
|
+
|
114
|
+
elif num_args == 4:
|
115
|
+
if hasAdvFlux and not hasDiffFlux:
|
116
|
+
boundaryAFlux[k] = f # f(t, x, u, n)
|
117
|
+
elif not hasAdvFlux and hasDiffFlux:
|
118
|
+
boundaryDFlux[k] = BaseModel._createF(f)
|
119
|
+
else:
|
120
|
+
assert not (
|
121
|
+
hasAdvFlux and hasDiffFlux
|
122
|
+
), "one boundary fluxes provided but two bulk fluxes given"
|
123
|
+
|
124
|
+
else:
|
125
|
+
raise NotImplementedError(f"boundary function {num_args} arguments")
|
126
|
+
|
127
|
+
return boundaryAFlux, boundaryDFlux, boundaryValue
|
128
|
+
|
129
|
+
@staticmethod
|
130
|
+
def boundaryTerms(Model, domain):
|
131
|
+
boundaryAFlux, boundaryDFlux, boundaryValue = BaseModel.classify_boundary(Model)
|
132
|
+
bd_weight = []
|
133
|
+
bN_weight = []
|
134
|
+
|
135
|
+
for model_key in boundaryValue.keys():
|
136
|
+
phi_i_proj = domain.bndProjSDFs(model_key)
|
137
|
+
bd_weight.append(phi_i_proj)
|
138
|
+
|
139
|
+
for model_key in {*boundaryAFlux.keys(), *boundaryDFlux.keys()}:
|
140
|
+
phi_i_proj = domain.bndProjSDFs(model_key)
|
141
|
+
bN_weight.append(phi_i_proj)
|
142
|
+
|
143
|
+
def total_weight(t, x):
|
144
|
+
weight = 1e-10 # tol
|
145
|
+
for w in bd_weight + bN_weight:
|
146
|
+
weight += w(t, x)
|
147
|
+
return weight
|
148
|
+
|
149
|
+
# perhaps switch methods around so that gExt is setup and then
|
150
|
+
# jumpD does sum(w)*U - gExt. But then the exception needs to be caught...
|
151
|
+
def jumpV(t, x, U, U1=None):
|
152
|
+
jdv = zero(U.ufl_shape)
|
153
|
+
|
154
|
+
if U1 is None:
|
155
|
+
U1 = U
|
156
|
+
|
157
|
+
for g, w in zip(boundaryValue.values(), bd_weight):
|
158
|
+
g_tmp = BaseModel._createV(v=g, U=U)
|
159
|
+
g_ext = domain.omega.boundary_extend(g_tmp)
|
160
|
+
|
161
|
+
jdv += w(t, x) * (U1 - g_ext(t, x))
|
162
|
+
|
163
|
+
return jdv / total_weight(t, x)
|
164
|
+
|
165
|
+
if len(boundaryValue) == 0:
|
166
|
+
gExt = None
|
167
|
+
else:
|
168
|
+
|
169
|
+
def gExt(t, x, U):
|
170
|
+
z = zero(U.ufl_shape)
|
171
|
+
return -jumpV(t, x, U, z)
|
172
|
+
|
173
|
+
# the models expect to be provided with a unit normal in the boundary fluxes
|
174
|
+
def jumpDF(t, x, U, DU, Fv):
|
175
|
+
# (sigma.n-gN)*ds(N) = - wN ( sigma.Dphi + gN|Dphi| )
|
176
|
+
# = wN ( (-sigma.Dphi) - gN(t,x,-Dphi/|Dphi|)|Dphi| )
|
177
|
+
# = wN ( sigma.sn - gN(t,x,sn) ) with sn = -Dphi
|
178
|
+
jdf = zero(U.ufl_shape)
|
179
|
+
|
180
|
+
fv_scaled = Fv * domain.scaledNormal(x)
|
181
|
+
for g, w in zip(boundaryDFlux.values(), bN_weight):
|
182
|
+
g_tmp = BaseModel._createF(v=g, U=U, DU=DU, N=domain.scaledNormal(x))
|
183
|
+
g_ext = domain.omega.boundary_extend(g_tmp)
|
184
|
+
|
185
|
+
jdf += w(t, x) * (fv_scaled - g_ext(t, x))
|
186
|
+
|
187
|
+
return jdf / total_weight(t, x)
|
188
|
+
|
189
|
+
def jumpAF(t, x, U):
|
190
|
+
jda = zero(U.ufl_shape)
|
191
|
+
|
192
|
+
for g, w in zip(boundaryAFlux.values(), bN_weight):
|
193
|
+
g_tmp = BaseModel._createF(v=g, U=U, N=domain.scaledNormal(x))
|
194
|
+
g_ext = domain.omega.boundary_extend(g_tmp)
|
195
|
+
|
196
|
+
jda += -w(t, x) * g_ext(t, x)
|
197
|
+
|
198
|
+
return jda / total_weight(t, x)
|
199
|
+
|
200
|
+
return jumpV, gExt, jumpDF, jumpAF
|
ddfem/boundary.py
CHANGED
@@ -6,7 +6,6 @@ from ufl.algorithms.ad import expand_derivatives
|
|
6
6
|
from ufl.core.expr import Expr
|
7
7
|
|
8
8
|
from .geometry.domain import Domain
|
9
|
-
from .geometry.primitive_base import SDF
|
10
9
|
|
11
10
|
|
12
11
|
class BoundaryCondition:
|
@@ -23,9 +22,7 @@ class BndValue(BoundaryCondition):
|
|
23
22
|
super().__init__(lambda t, x, u: value)
|
24
23
|
else:
|
25
24
|
num_args = len(inspect.signature(value).parameters)
|
26
|
-
if num_args ==
|
27
|
-
super().__init__(lambda t, x, u: value(x))
|
28
|
-
elif num_args == 2:
|
25
|
+
if num_args == 2:
|
29
26
|
super().__init__(lambda t, x, u: value(t, x))
|
30
27
|
elif num_args == 3:
|
31
28
|
super().__init__(value)
|
@@ -132,7 +129,7 @@ class BoundaryTerms:
|
|
132
129
|
else:
|
133
130
|
self.domain = Domain(domainDescription)
|
134
131
|
|
135
|
-
condition = lambda k: isinstance(k, str)
|
132
|
+
condition = lambda k: isinstance(k, str)
|
136
133
|
self.diffuse = {k: v for k, v in Model.boundary.items() if condition(k)}
|
137
134
|
self.physical = {k: v for k, v in Model.boundary.items() if not condition(k)}
|
138
135
|
|
@@ -157,11 +154,6 @@ class BoundaryTerms:
|
|
157
154
|
if not self.boundary_flux_vs:
|
158
155
|
self.BndFlux_vExt = None
|
159
156
|
|
160
|
-
def _dbc_total_weight(self, x):
|
161
|
-
weight = 1e-10
|
162
|
-
for w_func in self.bV_weight:
|
163
|
-
weight += w_func(x)
|
164
|
-
return weight
|
165
157
|
def _total_weight(self, x):
|
166
158
|
weight = 1e-10 # tol
|
167
159
|
for w_func in self.bV_weight + self.bF_weight:
|
@@ -170,7 +162,7 @@ class BoundaryTerms:
|
|
170
162
|
|
171
163
|
def _boundary_extend(self, g, x):
|
172
164
|
g_tmp = expand_derivatives(g)
|
173
|
-
return replace(g_tmp, {x: self.domain.boundary_projection(x)})
|
165
|
+
return replace(g_tmp, {x: self.domain.omega.boundary_projection(x)})
|
174
166
|
|
175
167
|
# perhaps switch methods around so that BndValueExt is setup and then
|
176
168
|
# jumpD does sum(w)*U - BndValueExt. But then the exception needs to be caught...
|
@@ -205,7 +197,6 @@ class BoundaryTerms:
|
|
205
197
|
jdf += w_func(x) * (fv_scaled - g_ext * self.domain.surface_delta(x))
|
206
198
|
|
207
199
|
return jdf / self._total_weight(x)
|
208
|
-
# return jdf * (self._dbc_total_weight(x) / self._total_weight(x))
|
209
200
|
|
210
201
|
def BndFlux_vExt(self, t, x, U, DU):
|
211
202
|
# called if self.BndFlux_vExt was not set to None in __init__
|
ddfem/geometry/__init__.py
CHANGED
@@ -1,12 +1,13 @@
|
|
1
1
|
from .arc import Arc
|
2
|
-
from .ball import Ball
|
3
2
|
from .box import Box
|
3
|
+
from .circle import Circle, Sphere
|
4
4
|
from .pie import Pie
|
5
|
-
from .
|
5
|
+
from .vesica import Vesica
|
6
6
|
from .primitive_base import (
|
7
7
|
SDF,
|
8
8
|
Intersection,
|
9
9
|
Invert,
|
10
|
+
Round,
|
10
11
|
Rotate,
|
11
12
|
Round,
|
12
13
|
Scale,
|
@@ -15,4 +16,3 @@ from .primitive_base import (
|
|
15
16
|
Union,
|
16
17
|
Xor,
|
17
18
|
)
|
18
|
-
from .vesica import Vesica
|
ddfem/geometry/arc.py
CHANGED
@@ -7,34 +7,32 @@ from .primitive_base import ORIGIN, SDF
|
|
7
7
|
class Arc(SDF):
|
8
8
|
"""SDF for an arc:
|
9
9
|
|
10
|
-
|
11
|
-
|
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
12
|
"""
|
13
|
-
|
14
13
|
def __init__(self, radius, angle, width, center=ORIGIN, *args, **kwargs):
|
14
|
+
super().__init__(*args, **kwargs)
|
15
15
|
self.radius = radius
|
16
16
|
self.angle = angle # angle of opening
|
17
17
|
self.width = width
|
18
18
|
|
19
|
+
assert len(center) == 2
|
19
20
|
if isinstance(center, (list, tuple)):
|
20
21
|
center = as_vector(center)
|
21
22
|
self.center = center
|
22
23
|
|
23
|
-
super().__init__(*args, **kwargs)
|
24
|
-
|
25
24
|
def __repr__(self):
|
26
|
-
return f"Arc({self.radius}, {self.angle}, {self.width}, {self.center}
|
25
|
+
return f"Arc({self.radius}, {self.angle}, {self.width}, {self.center})"
|
27
26
|
|
28
27
|
def sdf(self, x):
|
29
|
-
# Note: Ignores z
|
30
28
|
y0_abs = abs(x[1])
|
31
|
-
coords = as_vector(
|
29
|
+
coords = as_vector((x[0], y0_abs))
|
32
30
|
|
33
31
|
center_radius = self.radius - self.width / 2
|
34
32
|
|
35
|
-
distance = ufl_length(
|
33
|
+
distance = ufl_length(x) - center_radius
|
36
34
|
|
37
|
-
edge_point = center_radius * as_vector(
|
35
|
+
edge_point = center_radius * as_vector((cos(self.angle), sin(self.angle)))
|
38
36
|
|
39
37
|
left_coords = coords - edge_point
|
40
38
|
|
ddfem/geometry/box.py
CHANGED
@@ -1,4 +1,6 @@
|
|
1
|
-
from ufl import as_vector, conditional
|
1
|
+
from ufl import as_vector, conditional
|
2
|
+
from ufl import max_value as Max
|
3
|
+
from ufl import min_value as Min
|
2
4
|
|
3
5
|
from .helpers import ufl_length
|
4
6
|
from .primitive_base import ORIGIN, SDF
|
@@ -6,28 +8,27 @@ from .primitive_base import ORIGIN, SDF
|
|
6
8
|
|
7
9
|
class Box(SDF):
|
8
10
|
def __init__(self, width, height, center=ORIGIN, *args, **kwargs):
|
11
|
+
super().__init__(*args, **kwargs)
|
9
12
|
self.width = width
|
10
13
|
self.height = height
|
11
14
|
|
15
|
+
assert len(center) == 2
|
12
16
|
if isinstance(center, (list, tuple)):
|
13
17
|
center = as_vector(center)
|
14
18
|
self.center = center
|
15
19
|
|
16
|
-
super().__init__(*args, **kwargs)
|
17
|
-
|
18
20
|
def __repr__(self):
|
19
|
-
return f"Box({self.width}, {self.height}, {self.center}
|
21
|
+
return f"Box({self.width}, {self.height}, {self.center})"
|
20
22
|
|
21
23
|
def sdf(self, x):
|
22
|
-
# Note: Ignores z
|
23
24
|
# shift x
|
24
25
|
center_x = x - self.center
|
25
26
|
# aux functions
|
26
27
|
w0 = abs(center_x[0]) - self.width / 2
|
27
28
|
w1 = abs(center_x[1]) - self.height / 2
|
28
29
|
|
29
|
-
g =
|
30
|
+
g = Max(w0, w1)
|
30
31
|
|
31
|
-
q = as_vector(
|
32
|
+
q = as_vector((Max(w0, 0), Max(w1, 0)))
|
32
33
|
|
33
34
|
return conditional(g > 0, ufl_length(q), g)
|
ddfem/geometry/circle.py
ADDED
@@ -0,0 +1,39 @@
|
|
1
|
+
from ufl import as_vector
|
2
|
+
|
3
|
+
from .helpers import ufl_length
|
4
|
+
from .primitive_base import ORIGIN, SDF
|
5
|
+
|
6
|
+
|
7
|
+
class Circle(SDF):
|
8
|
+
def __init__(self, radius, center=ORIGIN, *args, **kwargs):
|
9
|
+
super().__init__(*args, **kwargs)
|
10
|
+
self.radius = radius
|
11
|
+
|
12
|
+
assert len(center) == 2
|
13
|
+
if isinstance(center, (list, tuple)):
|
14
|
+
center = as_vector(center)
|
15
|
+
self.center = center
|
16
|
+
|
17
|
+
def __repr__(self):
|
18
|
+
return f"Circle({self.radius}, {self.center})"
|
19
|
+
|
20
|
+
def sdf(self, x):
|
21
|
+
center_x = x - self.center
|
22
|
+
return ufl_length(center_x) - self.radius
|
23
|
+
|
24
|
+
class Sphere(SDF):
|
25
|
+
def __init__(self, radius, center=ORIGIN, *args, **kwargs):
|
26
|
+
super().__init__(*args, **kwargs)
|
27
|
+
self.radius = radius
|
28
|
+
|
29
|
+
if isinstance(center, (list, tuple)):
|
30
|
+
center = as_vector(center)
|
31
|
+
self.center = center
|
32
|
+
|
33
|
+
def __repr__(self):
|
34
|
+
return f"Circle({self.radius}, {self.center})"
|
35
|
+
|
36
|
+
def sdf(self, x):
|
37
|
+
center_x = x - self.center
|
38
|
+
return ufl_length(center_x) - self.radius
|
39
|
+
|
ddfem/geometry/domain.py
CHANGED
@@ -1,7 +1,5 @@
|
|
1
1
|
from ufl import conditional, dot, grad, sqrt
|
2
2
|
|
3
|
-
from .primitive_base import SDF
|
4
|
-
|
5
3
|
|
6
4
|
class Domain:
|
7
5
|
def __init__(self, omega):
|
@@ -11,9 +9,6 @@ class Domain:
|
|
11
9
|
tol = 1e-10
|
12
10
|
return (1 - tol) * self.omega.phi(x) + tol
|
13
11
|
|
14
|
-
def chi(self, x):
|
15
|
-
return self.omega.chi(x)
|
16
|
-
|
17
12
|
def scaled_normal(self, x):
|
18
13
|
return -grad(self.phi(x))
|
19
14
|
|
@@ -25,25 +20,18 @@ class Domain:
|
|
25
20
|
sd = conditional(self.surface_delta(x) > tol, self.surface_delta(x), tol)
|
26
21
|
return self.scaled_normal(x) / sd # grad(self.omega(x))
|
27
22
|
|
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
23
|
def bndSDFs(self, SDFname):
|
35
|
-
if isinstance(SDFname, SDF):
|
36
|
-
SDFname = SDFname.name
|
37
|
-
|
38
24
|
sdf = self.omega.search(SDFname)
|
39
25
|
if sdf is None:
|
40
26
|
raise ValueError(f"No SDF with name {SDFname}")
|
41
27
|
return sdf
|
42
28
|
|
43
29
|
def bndProjSDFs(self, SDFname):
|
44
|
-
sdf = self.
|
30
|
+
sdf = self.omega.search(SDFname)
|
31
|
+
if sdf is None:
|
32
|
+
raise ValueError(f"No SDF with name {SDFname}")
|
45
33
|
return self.generate_projSDF(sdf)
|
46
34
|
|
47
35
|
def generate_projSDF(self, sdf):
|
48
36
|
w = lambda x: sdf.phi(x) * (1 - sdf.phi(x))
|
49
|
-
return lambda x: w(self.boundary_projection(x))
|
37
|
+
return lambda x: w(self.omega.boundary_projection(x))
|
ddfem/geometry/domain_dune.py
CHANGED
@@ -13,8 +13,6 @@ class DomainDune(Domain):
|
|
13
13
|
self.gridView = gridView
|
14
14
|
|
15
15
|
self._phi = None
|
16
|
-
self._bndProj = None
|
17
|
-
self._extProj = None
|
18
16
|
self._bndProjSDFs = {}
|
19
17
|
|
20
18
|
self.fullSDF = self.gridFunction(self.omega(self.x), name="full-sdf")
|
@@ -32,20 +30,6 @@ class DomainDune(Domain):
|
|
32
30
|
|
33
31
|
return self._phi
|
34
32
|
|
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
33
|
def generate_projSDF(self, sdf):
|
50
34
|
projSDF = self._bndProjSDFs.get(sdf.name)
|
51
35
|
if projSDF is None:
|
ddfem/geometry/helpers.py
CHANGED
@@ -1,19 +1,12 @@
|
|
1
|
-
from ufl import conditional, dot,
|
2
|
-
|
3
|
-
|
4
|
-
# https://en.wikipedia.org/wiki/Smooth_maximum, https://en.wikipedia.org/wiki/LogSumExp
|
5
|
-
def smax_value(a, b, s):
|
6
|
-
return 1 / s * ln(exp(s * a) + exp(s * b))
|
7
|
-
|
8
|
-
|
9
|
-
def smin_value(a, b, s):
|
10
|
-
return smax_value(a, b, -s)
|
1
|
+
from ufl import conditional, dot, sqrt
|
2
|
+
from ufl import max_value as Max
|
3
|
+
from ufl import min_value as Min
|
11
4
|
|
12
5
|
|
13
6
|
def ufl_length(p):
|
14
|
-
return sqrt(dot(p, p) + 1e-10)
|
15
|
-
# return max_value(sqrt(dot(p, p)), 1e-10)
|
16
7
|
# return sqrt(dot(p, p))
|
8
|
+
# return ufl_max(sqrt(dot(p, p) + 1e-10), 1e-10)
|
9
|
+
return sqrt(dot(p, p))
|
17
10
|
|
18
11
|
|
19
12
|
def ufl_sign(p):
|
@@ -27,15 +20,15 @@ def ufl_clamp(p, minimum, maximum):
|
|
27
20
|
if isinstance(p, (float, int)):
|
28
21
|
return min(max(p, minimum), maximum)
|
29
22
|
|
30
|
-
|
31
|
-
|
23
|
+
def ufl_max(p1, p2):
|
24
|
+
return conditional(p2 < p1, p1, p2)
|
32
25
|
|
33
|
-
|
34
|
-
|
26
|
+
def ufl_min(p1, p2):
|
27
|
+
return conditional(p1 < p2, p1, p2)
|
35
28
|
|
36
|
-
|
37
|
-
#
|
38
|
-
return
|
29
|
+
return ufl_min(ufl_max(p, minimum), maximum)
|
30
|
+
# using Min/Max, seems to break shape Pie?
|
31
|
+
return Min(Max(p, minimum), maximum)
|
39
32
|
|
40
33
|
|
41
34
|
def ufl_cross(p1, p2):
|
ddfem/geometry/pie.py
CHANGED
@@ -1,4 +1,6 @@
|
|
1
|
-
from ufl import as_vector, cos, dot,
|
1
|
+
from ufl import as_vector, cos, dot, sin
|
2
|
+
from ufl import max_value as Max
|
3
|
+
from ufl import min_value as Min
|
2
4
|
|
3
5
|
from .helpers import ufl_clamp, ufl_cross, ufl_length, ufl_sign
|
4
6
|
from .primitive_base import ORIGIN, SDF
|
@@ -6,18 +8,16 @@ from .primitive_base import ORIGIN, SDF
|
|
6
8
|
|
7
9
|
class Pie(SDF):
|
8
10
|
def __init__(self, radius, angle, *args, **kwargs):
|
11
|
+
super().__init__(*args, **kwargs)
|
9
12
|
self.radius = radius
|
10
13
|
self.angle = angle # angle of cicle (not opening)
|
11
14
|
|
12
|
-
super().__init__(*args, **kwargs)
|
13
|
-
|
14
15
|
def __repr__(self):
|
15
|
-
return f"Pie({self.radius}, {self.angle}
|
16
|
+
return f"Pie({self.radius}, {self.angle})"
|
16
17
|
|
17
18
|
def sdf(self, x):
|
18
|
-
# Note: Ignores z
|
19
19
|
x0_abs = abs(x[0])
|
20
|
-
coords = as_vector(
|
20
|
+
coords = as_vector((x0_abs, x[1]))
|
21
21
|
|
22
22
|
circle_dist = ufl_length(coords) - self.radius
|
23
23
|
|
@@ -28,4 +28,4 @@ class Pie(SDF):
|
|
28
28
|
rejc = coords - proj
|
29
29
|
edge_dist = ufl_length(rejc) * ufl_sign(ufl_cross(coords, trig))
|
30
30
|
|
31
|
-
return
|
31
|
+
return Max(circle_dist, edge_dist)
|