ddfem 0.0.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/base_model.py +200 -0
- ddfem/boundary.py +214 -0
- ddfem/dune.py +3 -0
- ddfem/geometry/__init__.py +18 -0
- ddfem/geometry/arc.py +46 -0
- ddfem/geometry/box.py +34 -0
- ddfem/geometry/circle.py +39 -0
- ddfem/geometry/domain.py +37 -0
- ddfem/geometry/domain_dune.py +66 -0
- ddfem/geometry/helpers.py +35 -0
- ddfem/geometry/pie.py +31 -0
- ddfem/geometry/primitive_base.py +279 -0
- ddfem/geometry/vesica.py +48 -0
- ddfem/model2ufl.py +145 -0
- ddfem/transformers/DDM1.py +94 -0
- ddfem/transformers/Fitted.py +67 -0
- ddfem/transformers/Mix0.py +161 -0
- ddfem/transformers/NNS.py +143 -0
- ddfem/transformers/NS.py +151 -0
- ddfem/transformers/__init__.py +5 -0
- ddfem/transformers/transformer_base.py +126 -0
- ddfem-1.0.0.dist-info/METADATA +25 -0
- ddfem-1.0.0.dist-info/RECORD +27 -0
- {ddfem-0.0.0.dist-info → ddfem-1.0.0.dist-info}/WHEEL +1 -1
- ddfem-1.0.0.dist-info/licenses/LICENSE +19 -0
- ddfem-0.0.0.dist-info/METADATA +0 -5
- ddfem-0.0.0.dist-info/RECORD +0 -5
- {ddfem-0.0.0.dist-info → ddfem-1.0.0.dist-info}/top_level.txt +0 -0
ddfem/geometry/pie.py
ADDED
@@ -0,0 +1,31 @@
|
|
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
|
4
|
+
|
5
|
+
from .helpers import ufl_clamp, ufl_cross, ufl_length, ufl_sign
|
6
|
+
from .primitive_base import ORIGIN, SDF
|
7
|
+
|
8
|
+
|
9
|
+
class Pie(SDF):
|
10
|
+
def __init__(self, radius, angle, *args, **kwargs):
|
11
|
+
super().__init__(*args, **kwargs)
|
12
|
+
self.radius = radius
|
13
|
+
self.angle = angle # angle of cicle (not opening)
|
14
|
+
|
15
|
+
def __repr__(self):
|
16
|
+
return f"Pie({self.radius}, {self.angle})"
|
17
|
+
|
18
|
+
def sdf(self, x):
|
19
|
+
x0_abs = abs(x[0])
|
20
|
+
coords = as_vector((x0_abs, x[1]))
|
21
|
+
|
22
|
+
circle_dist = ufl_length(coords) - self.radius
|
23
|
+
|
24
|
+
trig = as_vector([sin(self.angle / 2), cos(self.angle / 2)])
|
25
|
+
|
26
|
+
# projection of coords on to trig, clamped to within circle
|
27
|
+
proj = ufl_clamp(dot(coords, trig), 0, self.radius) * trig
|
28
|
+
rejc = coords - proj
|
29
|
+
edge_dist = ufl_length(rejc) * ufl_sign(ufl_cross(coords, trig))
|
30
|
+
|
31
|
+
return Max(circle_dist, edge_dist)
|
@@ -0,0 +1,279 @@
|
|
1
|
+
from ufl import (
|
2
|
+
as_matrix,
|
3
|
+
as_vector,
|
4
|
+
conditional,
|
5
|
+
cos,
|
6
|
+
grad,
|
7
|
+
pi,
|
8
|
+
replace,
|
9
|
+
sin,
|
10
|
+
tanh,
|
11
|
+
)
|
12
|
+
from ufl import max_value as Max
|
13
|
+
from ufl import min_value as Min
|
14
|
+
|
15
|
+
from ufl.algorithms import expand_indices
|
16
|
+
from ufl.algorithms.apply_algebra_lowering import apply_algebra_lowering
|
17
|
+
from ufl.algorithms.apply_derivatives import apply_derivatives
|
18
|
+
|
19
|
+
ORIGIN = as_vector([0, 0])
|
20
|
+
|
21
|
+
|
22
|
+
class SDF:
|
23
|
+
def __init__(self, epsilon=None, name=None, children=None):
|
24
|
+
self.name = name
|
25
|
+
self.epsilon = epsilon
|
26
|
+
self.child_sdf = children if children else []
|
27
|
+
|
28
|
+
def sdf(self, x):
|
29
|
+
raise NotImplementedError
|
30
|
+
|
31
|
+
def __call__(self, x):
|
32
|
+
return self.sdf(x)
|
33
|
+
|
34
|
+
def search(self, child_name):
|
35
|
+
if self.name == child_name:
|
36
|
+
return self
|
37
|
+
|
38
|
+
queue = self.child_sdf.copy()
|
39
|
+
|
40
|
+
while queue:
|
41
|
+
current_child = queue.pop(0)
|
42
|
+
|
43
|
+
if current_child.name == child_name:
|
44
|
+
return current_child
|
45
|
+
|
46
|
+
for child in current_child.child_sdf:
|
47
|
+
queue.append(child)
|
48
|
+
|
49
|
+
return None
|
50
|
+
|
51
|
+
def propgate_epsilon(self, epsilon):
|
52
|
+
self.epsilon = epsilon
|
53
|
+
for child in self.child_sdf:
|
54
|
+
child.propgate_epsilon(epsilon)
|
55
|
+
|
56
|
+
def phi(self, x, epsilon=None):
|
57
|
+
if not epsilon:
|
58
|
+
epsilon = self.epsilon
|
59
|
+
assert self.epsilon, "Must define epsilon"
|
60
|
+
return 0.5 * (1 - tanh((3 * self.sdf(x) / epsilon)))
|
61
|
+
|
62
|
+
def chi(self, x):
|
63
|
+
return conditional(self.sdf(x) <= 0, 1, 0)
|
64
|
+
|
65
|
+
def projection(self, x):
|
66
|
+
return -grad(self.sdf(x)) * self.sdf(x)
|
67
|
+
|
68
|
+
def boundary_projection(self, x):
|
69
|
+
return x + self.projection(x)
|
70
|
+
|
71
|
+
def external_projection(self, x):
|
72
|
+
# return self.chi(x) * x + self.boundary_projection(x) * (1 - self.chi(x))
|
73
|
+
return x + self.projection(x) * (1 - self.chi(x))
|
74
|
+
|
75
|
+
def union(self, other):
|
76
|
+
return Union(self, other)
|
77
|
+
|
78
|
+
def subtraction(self, other):
|
79
|
+
return Subtraction(self, other)
|
80
|
+
|
81
|
+
def intersection(self, other):
|
82
|
+
return Intersection(self, other)
|
83
|
+
|
84
|
+
def xor(self, other):
|
85
|
+
return Xor(self, other)
|
86
|
+
|
87
|
+
def scale(self, sc):
|
88
|
+
return Scale(self, sc)
|
89
|
+
|
90
|
+
def invert(self):
|
91
|
+
return Invert(self)
|
92
|
+
|
93
|
+
def rotate(self, angle, radians=True):
|
94
|
+
return Rotate(self, angle, radians)
|
95
|
+
|
96
|
+
def translate(self, vector):
|
97
|
+
return Translate(self, vector)
|
98
|
+
|
99
|
+
def round(self, sc):
|
100
|
+
return Round(self, sc)
|
101
|
+
|
102
|
+
def __or__(self, other):
|
103
|
+
return self.union(other)
|
104
|
+
|
105
|
+
def __and__(self, other):
|
106
|
+
return self.intersection(other)
|
107
|
+
|
108
|
+
def __sub__(self, other):
|
109
|
+
return self.subtraction(other)
|
110
|
+
|
111
|
+
def __xor__(self, other):
|
112
|
+
return self.xor(other)
|
113
|
+
|
114
|
+
def __mul__(self, other):
|
115
|
+
if isinstance(other, (int, float)):
|
116
|
+
return self.scale(other)
|
117
|
+
raise TypeError(f"Cannot multiply a SDF with {type(other)}")
|
118
|
+
|
119
|
+
def __rmul__(self, other):
|
120
|
+
if isinstance(other, (int, float)):
|
121
|
+
return self.scale(other)
|
122
|
+
raise TypeError(f"Cannot multiply a SDF with {type(other)}")
|
123
|
+
|
124
|
+
class BaseOperator(SDF):
|
125
|
+
def __init__(self, epsilon, children, *args, **kwargs):
|
126
|
+
|
127
|
+
if not epsilon and all(child.epsilon for child in children):
|
128
|
+
if len(children) == 1:
|
129
|
+
epsilon = children[0].epsilon
|
130
|
+
else:
|
131
|
+
epsilon = Min(*[child.epsilon for child in children])
|
132
|
+
|
133
|
+
super().__init__(children=children, epsilon=epsilon, *args, **kwargs)
|
134
|
+
|
135
|
+
def __repr__(self):
|
136
|
+
return f"{self.__class__.__name__}({', '.join(map(repr, self.child_sdf))})"
|
137
|
+
|
138
|
+
def __getitem__(self, key):
|
139
|
+
return self.child_sdf[key]
|
140
|
+
|
141
|
+
|
142
|
+
class Union(BaseOperator):
|
143
|
+
"""Union of two SDFs (OR) - not perfect(negative)"""
|
144
|
+
|
145
|
+
def __init__(self, sdf1, sdf2, epsilon=None, name=None, *args, **kwargs):
|
146
|
+
super().__init__(epsilon=epsilon, children=[sdf1, sdf2], *args, **kwargs)
|
147
|
+
if self.name is None:
|
148
|
+
self.name = f"({sdf1.name}|{sdf2.name})"
|
149
|
+
|
150
|
+
def sdf(self, x):
|
151
|
+
return Min(self.child_sdf[0].sdf(x), self.child_sdf[1].sdf(x))
|
152
|
+
|
153
|
+
|
154
|
+
class Subtraction(BaseOperator):
|
155
|
+
"""Subtraction of two SDFs (difference) - not perfect"""
|
156
|
+
|
157
|
+
def __init__(self, sdf1, sdf2, epsilon=None, name=None, *args, **kwargs):
|
158
|
+
super().__init__(epsilon=epsilon, children=[sdf1, sdf2], *args, **kwargs)
|
159
|
+
if self.name is None:
|
160
|
+
self.name = f"({sdf1.name}-{sdf2.name})"
|
161
|
+
|
162
|
+
def sdf(self, x):
|
163
|
+
return Max(self.child_sdf[0].sdf(x), -self.child_sdf[1].sdf(x))
|
164
|
+
|
165
|
+
|
166
|
+
class Intersection(BaseOperator):
|
167
|
+
"""Intersection of two SDFs (AND) - not perfect"""
|
168
|
+
|
169
|
+
def __init__(self, sdf1, sdf2, epsilon=None, name=None, *args, **kwargs):
|
170
|
+
super().__init__(epsilon=epsilon, children=[sdf1, sdf2], *args, **kwargs)
|
171
|
+
if self.name is None:
|
172
|
+
self.name = f"({sdf1.name}&{sdf2.name})"
|
173
|
+
|
174
|
+
def sdf(self, x):
|
175
|
+
return Max(self.child_sdf[0].sdf(x), self.child_sdf[1].sdf(x))
|
176
|
+
|
177
|
+
|
178
|
+
class Xor(BaseOperator):
|
179
|
+
"""Xor of two SDFs (AND) - perfect"""
|
180
|
+
|
181
|
+
def __init__(self, sdf1, sdf2, epsilon=None, name=None, *args, **kwargs):
|
182
|
+
super().__init__(epsilon=epsilon, children=[sdf1, sdf2], *args, **kwargs)
|
183
|
+
if self.name is None:
|
184
|
+
self.name = f"({sdf1.name}^{sdf2.name})"
|
185
|
+
|
186
|
+
def sdf(self, x):
|
187
|
+
a_x = self.child_sdf[0].sdf(x)
|
188
|
+
b_x = self.child_sdf[1].sdf(x)
|
189
|
+
return Max(Min(a_x, b_x), -Max(a_x, b_x))
|
190
|
+
|
191
|
+
|
192
|
+
class Invert(BaseOperator):
|
193
|
+
"""Inverts SDF"""
|
194
|
+
|
195
|
+
def __init__(self, sdf1, epsilon=None, name=None, *args, **kwargs):
|
196
|
+
super().__init__(epsilon=epsilon, children=[sdf1], *args, **kwargs)
|
197
|
+
if self.name is None:
|
198
|
+
self.name = f"(-{sdf1.name})"
|
199
|
+
|
200
|
+
def sdf(self, x):
|
201
|
+
return -self.child_sdf[0].sdf(x)
|
202
|
+
|
203
|
+
|
204
|
+
class Scale(BaseOperator):
|
205
|
+
"""Scales SDF"""
|
206
|
+
|
207
|
+
def __init__(self, sdf1, scale, epsilon=None, name=None, *args, **kwargs):
|
208
|
+
super().__init__(epsilon=epsilon, children=[sdf1], *args, **kwargs)
|
209
|
+
self.scale = scale
|
210
|
+
if self.name is None:
|
211
|
+
self.name = f"({scale}*{sdf1.name})"
|
212
|
+
|
213
|
+
def sdf(self, x):
|
214
|
+
return self.child_sdf[0].sdf(x / self.scale) * self.scale
|
215
|
+
|
216
|
+
def __repr__(self):
|
217
|
+
return f"Scale({repr(self.child_sdf[0])}, {self.scale})"
|
218
|
+
|
219
|
+
|
220
|
+
class Rotate(BaseOperator):
|
221
|
+
"""Rotates SDF, counterclockwise of orgin"""
|
222
|
+
|
223
|
+
def __init__(
|
224
|
+
self, sdf1, angle, radians=True, epsilon=None, name=None, *args, **kwargs
|
225
|
+
):
|
226
|
+
super().__init__(epsilon=epsilon, children=[sdf1], *args, **kwargs)
|
227
|
+
if self.name is None:
|
228
|
+
self.name = f"({angle}@{sdf1.name})"
|
229
|
+
|
230
|
+
if not radians:
|
231
|
+
angle *= pi / 180
|
232
|
+
self.angle = angle
|
233
|
+
|
234
|
+
def sdf(self, x):
|
235
|
+
c = cos(self.angle)
|
236
|
+
s = sin(self.angle)
|
237
|
+
|
238
|
+
r = as_matrix(((c, -s), (s, c)))
|
239
|
+
return self.child_sdf[0].sdf(r.T * x)
|
240
|
+
|
241
|
+
def __repr__(self):
|
242
|
+
return f"Rotate({repr(self.child_sdf[0])}, {self.angle})"
|
243
|
+
|
244
|
+
|
245
|
+
class Translate(BaseOperator):
|
246
|
+
"""Translates SDF"""
|
247
|
+
|
248
|
+
def __init__(self, sdf1, vec, epsilon=None, name=None, *args, **kwargs):
|
249
|
+
super().__init__(epsilon=epsilon, children=[sdf1], *args, **kwargs)
|
250
|
+
if self.name is None:
|
251
|
+
self.name = f"({vec}+{sdf1.name})"
|
252
|
+
|
253
|
+
if isinstance(vec, (list, tuple)):
|
254
|
+
vec = as_vector(vec)
|
255
|
+
self.vec = vec
|
256
|
+
|
257
|
+
def sdf(self, x):
|
258
|
+
return self.child_sdf[0].sdf(x - self.vec)
|
259
|
+
|
260
|
+
def __repr__(self):
|
261
|
+
return f"Translate({repr(self.child_sdf[0])}, {self.vec})"
|
262
|
+
|
263
|
+
|
264
|
+
class Round(BaseOperator):
|
265
|
+
"""Rounds SDF"""
|
266
|
+
|
267
|
+
def __init__(self, sdf1, scale, epsilon=None, name=None, *args, **kwargs):
|
268
|
+
super().__init__(epsilon=epsilon, children=[sdf1], *args, **kwargs)
|
269
|
+
if self.name is None:
|
270
|
+
self.name = f"({scale}~{sdf1.name})"
|
271
|
+
|
272
|
+
assert scale > 0
|
273
|
+
self._scale = scale # careful not to overwrite SDF.scale here
|
274
|
+
|
275
|
+
def sdf(self, x):
|
276
|
+
return self.child_sdf[0].sdf(x) - self._scale
|
277
|
+
|
278
|
+
def __repr__(self):
|
279
|
+
return f"Round({repr(self.child_sdf[0])}, {self.scale})"
|
ddfem/geometry/vesica.py
ADDED
@@ -0,0 +1,48 @@
|
|
1
|
+
from ufl import as_vector, conditional, sqrt
|
2
|
+
|
3
|
+
from .helpers import ufl_length, ufl_sign
|
4
|
+
from .primitive_base import ORIGIN, SDF
|
5
|
+
|
6
|
+
|
7
|
+
class Vesica(SDF):
|
8
|
+
def __init__(self, radius, distance, smooth_radius, *args, **kwargs):
|
9
|
+
"""Generates sign distance function and domain coordinates for a Vesica.
|
10
|
+
i.e. Union of two circles.
|
11
|
+
Centred at (0,0)
|
12
|
+
|
13
|
+
Args:
|
14
|
+
radius (float,): Radius of each circle.
|
15
|
+
distance (float): Distance of circle center from y-axis.
|
16
|
+
smooth_radius (float): Smoothing of domain,so no sharp corner when circles connection.
|
17
|
+
"""
|
18
|
+
super().__init__(*args, **kwargs)
|
19
|
+
|
20
|
+
assert distance != 0, "This is a circle, use Circle class"
|
21
|
+
assert distance < radius, "No shape exists, circles cancel each other out"
|
22
|
+
assert (
|
23
|
+
smooth_radius * distance < 0
|
24
|
+
), "For a smooth edge, smooth_radius needs to be opposite sign of distance"
|
25
|
+
|
26
|
+
self.radius = radius
|
27
|
+
self.distance = distance
|
28
|
+
self.smooth_radius = smooth_radius
|
29
|
+
|
30
|
+
def __repr__(self):
|
31
|
+
return f"Vesica({self.radius}, {self.distance}, {self.smooth_radius})"
|
32
|
+
|
33
|
+
def sdf(self, x):
|
34
|
+
x0_abs = abs(x[0])
|
35
|
+
x1_abs = abs(x[1])
|
36
|
+
|
37
|
+
b = sqrt((self.radius + self.smooth_radius) ** 2 - self.distance**2)
|
38
|
+
|
39
|
+
circle_coords = as_vector((x0_abs + self.distance, x[1]))
|
40
|
+
|
41
|
+
return (
|
42
|
+
conditional(
|
43
|
+
(x1_abs - b) * self.distance > x0_abs * b,
|
44
|
+
ufl_length(as_vector((x0_abs, x1_abs - b))) * ufl_sign(self.distance),
|
45
|
+
ufl_length(circle_coords) - self.radius - self.smooth_radius,
|
46
|
+
)
|
47
|
+
+ self.smooth_radius
|
48
|
+
)
|
ddfem/model2ufl.py
ADDED
@@ -0,0 +1,145 @@
|
|
1
|
+
from ufl import (
|
2
|
+
FacetNormal,
|
3
|
+
SpatialCoordinate,
|
4
|
+
TestFunction,
|
5
|
+
TrialFunction,
|
6
|
+
as_vector,
|
7
|
+
ds,
|
8
|
+
dx,
|
9
|
+
grad,
|
10
|
+
inner,
|
11
|
+
)
|
12
|
+
|
13
|
+
from .boundary import boundary_validation
|
14
|
+
|
15
|
+
|
16
|
+
def boundaries_ufl(Model, space, t):
|
17
|
+
boundary_flux_cs, boundary_flux_vs, boundary_values = boundary_validation(Model)
|
18
|
+
|
19
|
+
u = TrialFunction(space)
|
20
|
+
n = FacetNormal(space.cell())
|
21
|
+
x = SpatialCoordinate(space.cell())
|
22
|
+
|
23
|
+
boundary_flux_cs = {
|
24
|
+
(k(x) if callable(k) else k): f(t, x, u, n) for k, f in boundary_flux_cs.items()
|
25
|
+
}
|
26
|
+
boundary_flux_vs = {
|
27
|
+
(k(x) if callable(k) else k): f(t, x, u, grad(u), n)
|
28
|
+
for k, f in boundary_flux_vs.items()
|
29
|
+
}
|
30
|
+
boundary_values = {
|
31
|
+
(k(x) if callable(k) else k): f(t, x, u) for k, f in boundary_values.items()
|
32
|
+
}
|
33
|
+
hasBoundaryValue = {k: True for k in boundary_values.keys()}
|
34
|
+
|
35
|
+
return (
|
36
|
+
boundary_flux_cs,
|
37
|
+
boundary_flux_vs,
|
38
|
+
boundary_values,
|
39
|
+
hasBoundaryValue,
|
40
|
+
)
|
41
|
+
|
42
|
+
|
43
|
+
class DirichletBC:
|
44
|
+
def __init__(self, space, value, domain=None):
|
45
|
+
self.space = space
|
46
|
+
self.value = value
|
47
|
+
self.domain = domain
|
48
|
+
|
49
|
+
def __str__(self):
|
50
|
+
return str(self.value) + str(self.domain)
|
51
|
+
|
52
|
+
|
53
|
+
def model_ufl(Model, space, initialTime=0, DirichletBC=DirichletBC):
|
54
|
+
u = TrialFunction(space)
|
55
|
+
v = TestFunction(space)
|
56
|
+
x = SpatialCoordinate(space.cell())
|
57
|
+
t = initialTime
|
58
|
+
|
59
|
+
f_c_model = None
|
60
|
+
if hasattr(Model, "F_c"):
|
61
|
+
f_c_model = inner(Model.F_c(t, x, u), grad(v)) * dx # -div F_c v
|
62
|
+
if hasattr(Model, "S_e"):
|
63
|
+
se = (
|
64
|
+
inner(as_vector(Model.S_e(t, x, u, grad(u))), v) * dx
|
65
|
+
) # (-div F_c + S_e) * v
|
66
|
+
if f_c_model is not None:
|
67
|
+
f_c_model += se
|
68
|
+
else:
|
69
|
+
f_c_model = se
|
70
|
+
|
71
|
+
f_v_model = None
|
72
|
+
if hasattr(Model, "F_v"):
|
73
|
+
f_v_model = inner(Model.F_v(t, x, u, grad(u)), grad(v)) * dx # -div F_v v
|
74
|
+
|
75
|
+
if hasattr(Model, "S_i"):
|
76
|
+
si = inner(as_vector(Model.S_i(t, x, u, grad(u))), v) * dx # (-div F_v + S_i) v
|
77
|
+
if f_v_model is not None:
|
78
|
+
f_v_model += si
|
79
|
+
else:
|
80
|
+
f_v_model = si
|
81
|
+
|
82
|
+
# need to extract boundary information from Model
|
83
|
+
(
|
84
|
+
boundary_flux_cs,
|
85
|
+
boundary_flux_vs,
|
86
|
+
boundary_values,
|
87
|
+
hasBoundaryValue,
|
88
|
+
) = boundaries_ufl(Model, space, t)
|
89
|
+
|
90
|
+
dirichletBCs = [
|
91
|
+
DirichletBC(space, item[1], item[0]) for item in boundary_values.items()
|
92
|
+
]
|
93
|
+
boundary_flux_vs = -sum(
|
94
|
+
[inner(item[1], v) * ds(item[0]) for item in boundary_flux_vs.items()]
|
95
|
+
) # keep all forms on left hand side
|
96
|
+
boundary_flux_cs = -sum(
|
97
|
+
[inner(item[1], v) * ds(item[0]) for item in boundary_flux_cs.items()]
|
98
|
+
) # keep all forms on left hand side
|
99
|
+
|
100
|
+
return (
|
101
|
+
f_c_model,
|
102
|
+
f_v_model,
|
103
|
+
{
|
104
|
+
"dirichletBCs": dirichletBCs,
|
105
|
+
"boundary_flux_cs": boundary_flux_cs,
|
106
|
+
"boundary_flux_vs": boundary_flux_vs,
|
107
|
+
"hasBoundaryValue": hasBoundaryValue,
|
108
|
+
},
|
109
|
+
)
|
110
|
+
|
111
|
+
|
112
|
+
def model2ufl(
|
113
|
+
Model, space, initialTime=0, DirichletBC=DirichletBC, *, returnFull=False
|
114
|
+
):
|
115
|
+
class M(Model):
|
116
|
+
if hasattr(Model, "S_e"):
|
117
|
+
|
118
|
+
def S_e(t, x, U, DU):
|
119
|
+
return -Model.S_e(t, x, U, DU)
|
120
|
+
|
121
|
+
if hasattr(Model, "S_i"):
|
122
|
+
|
123
|
+
def S_i(t, x, U, DU):
|
124
|
+
return -Model.S_i(t, x, U, DU)
|
125
|
+
|
126
|
+
if hasattr(Model, "F_c"):
|
127
|
+
|
128
|
+
def F_c(t, x, U):
|
129
|
+
return -Model.F_c(t, x, U)
|
130
|
+
|
131
|
+
f_c_model, f_v_model, boundary_model = model_ufl(M, space, initialTime, DirichletBC)
|
132
|
+
boundary_model["boundary_flux_cs"] = -boundary_model["boundary_flux_cs"]
|
133
|
+
form = boundary_model["boundary_flux_cs"] + boundary_model["boundary_flux_vs"]
|
134
|
+
if f_c_model is not None:
|
135
|
+
form += f_c_model
|
136
|
+
if f_v_model is not None:
|
137
|
+
form += f_v_model
|
138
|
+
|
139
|
+
if not returnFull:
|
140
|
+
return [form == 0, *boundary_model["dirichletBCs"]]
|
141
|
+
else:
|
142
|
+
boundary_model["f_c_model"] = f_c_model
|
143
|
+
boundary_model["f_v_model"] = f_v_model
|
144
|
+
boundary_model["form"] = form
|
145
|
+
return boundary_model
|
@@ -0,0 +1,94 @@
|
|
1
|
+
from ufl import grad, zero
|
2
|
+
|
3
|
+
from .transformer_base import transformer_base
|
4
|
+
|
5
|
+
|
6
|
+
def DDM1(OriginalModel, domainDescription):
|
7
|
+
Model = transformer_base(OriginalModel, domainDescription)
|
8
|
+
|
9
|
+
class DDModel(Model):
|
10
|
+
def sigma(t, x, U, DU=None):
|
11
|
+
if DU:
|
12
|
+
return DU
|
13
|
+
return grad(U)
|
14
|
+
|
15
|
+
def S_e_source(t, x, U, DU):
|
16
|
+
return DDModel.phi(x) * Model.S_e(t, x, U, DDModel.sigma(t, x, U, DU))
|
17
|
+
|
18
|
+
def S_e_convection(t, x, U, DU):
|
19
|
+
return DDModel.BT.BndFlux_cExt(t, x, U)
|
20
|
+
|
21
|
+
if hasattr(Model, "S_e") and hasattr(Model, "F_c"):
|
22
|
+
print("DDM1: S_e and F_c")
|
23
|
+
|
24
|
+
def S_e(t, x, U, DU):
|
25
|
+
return DDModel.S_e_source(t, x, U, DU) + DDModel.S_e_convection(
|
26
|
+
t, x, U, DU
|
27
|
+
)
|
28
|
+
|
29
|
+
elif hasattr(Model, "S_e"):
|
30
|
+
print("DDM1: S_e")
|
31
|
+
|
32
|
+
def S_e(t, x, U, DU):
|
33
|
+
return DDModel.S_e_source(t, x, U, DU)
|
34
|
+
|
35
|
+
elif hasattr(Model, "F_c"):
|
36
|
+
print("DDM1: F_c")
|
37
|
+
|
38
|
+
def S_e(t, x, U, DU):
|
39
|
+
return DDModel.S_e_convection(t, x, U, DU)
|
40
|
+
|
41
|
+
def S_i_stability(t, x, U, DU):
|
42
|
+
return -Model.stabFactor * (
|
43
|
+
DDModel.BT.jumpV(t, x, U) * (1 - DDModel.phi(x)) / (DDModel.epsilon**3)
|
44
|
+
)
|
45
|
+
|
46
|
+
def S_i_source(t, x, U, DU):
|
47
|
+
return DDModel.phi(x) * Model.S_i(t, x, U, DDModel.sigma(t, x, U, DU))
|
48
|
+
|
49
|
+
def S_i_diffusion(t, x, U, DU):
|
50
|
+
if DDModel.BT.BndFlux_vExt is not None:
|
51
|
+
diffusion = DDModel.BT.BndFlux_vExt(t, x, U, DU)
|
52
|
+
else:
|
53
|
+
diffusion = zero(U.ufl_shape)
|
54
|
+
return diffusion
|
55
|
+
|
56
|
+
if hasattr(Model, "S_i") and hasattr(Model, "F_v"):
|
57
|
+
print("DDM1: S_i and F_v")
|
58
|
+
|
59
|
+
def S_i(t, x, U, DU):
|
60
|
+
return (
|
61
|
+
DDModel.S_i_stability(t, x, U, DU)
|
62
|
+
+ DDModel.S_i_source(t, x, U, DU)
|
63
|
+
+ DDModel.S_i_diffusion(t, x, U, DU)
|
64
|
+
)
|
65
|
+
|
66
|
+
elif hasattr(Model, "F_v"):
|
67
|
+
print("DDM1: F_v")
|
68
|
+
|
69
|
+
def S_i(t, x, U, DU):
|
70
|
+
return DDModel.S_i_stability(t, x, U, DU) + DDModel.S_i_diffusion(
|
71
|
+
t, x, U, DU
|
72
|
+
)
|
73
|
+
|
74
|
+
elif hasattr(Model, "S_i"):
|
75
|
+
print("DDM1: S_i")
|
76
|
+
|
77
|
+
def S_i(t, x, U, DU):
|
78
|
+
return DDModel.S_i_stability(t, x, U, DU) + DDModel.S_i_source(
|
79
|
+
t, x, U, DU
|
80
|
+
)
|
81
|
+
|
82
|
+
if hasattr(Model, "F_c"):
|
83
|
+
print("DDM1: F_c")
|
84
|
+
|
85
|
+
def F_c(t, x, U):
|
86
|
+
return DDModel.phi(x) * Model.F_c(t, x, U)
|
87
|
+
|
88
|
+
if hasattr(Model, "F_v"):
|
89
|
+
print("DDM1: F_v")
|
90
|
+
|
91
|
+
def F_v(t, x, U, DU):
|
92
|
+
return DDModel.phi(x) * Model.F_v(t, x, U, DDModel.sigma(t, x, U, DU))
|
93
|
+
|
94
|
+
return DDModel
|
@@ -0,0 +1,67 @@
|
|
1
|
+
from functools import reduce
|
2
|
+
|
3
|
+
from ufl import Min, conditional, eq, grad
|
4
|
+
|
5
|
+
from ..boundary import BndFlux_c, BndFlux_v, BndValue, boundary_validation
|
6
|
+
from .transformer_base import transformer_base
|
7
|
+
|
8
|
+
|
9
|
+
def Fitted(OriginalModel, domainDescription):
|
10
|
+
Model = transformer_base(OriginalModel, domainDescription)
|
11
|
+
|
12
|
+
class Fitted(Model):
|
13
|
+
def sigma(t, x, U, DU=None):
|
14
|
+
if DU:
|
15
|
+
return DU
|
16
|
+
return grad(U)
|
17
|
+
|
18
|
+
boundary = Model.BT.physical
|
19
|
+
bndSDFs = {k: Model.domain.bndSDFs(k) for k in Model.BT.diffuse.keys()}
|
20
|
+
|
21
|
+
def make_boundary_function(key, mv, bndSDFs=bndSDFs):
|
22
|
+
sdf = bndSDFs[key]
|
23
|
+
closest_sdf = lambda x: reduce(
|
24
|
+
Min,
|
25
|
+
([abs(v(x)) for b, v in bndSDFs.items()]),
|
26
|
+
)
|
27
|
+
|
28
|
+
boundary_map = lambda x: conditional(eq(closest_sdf(x), abs(sdf(x))), 1, 0)
|
29
|
+
|
30
|
+
if isinstance(mv, BndFlux_v):
|
31
|
+
return BndFlux_v(
|
32
|
+
lambda t, x, u, DU, n: boundary_map(x) * mv(t, x, u, DU, n),
|
33
|
+
)
|
34
|
+
|
35
|
+
elif isinstance(mv, BndFlux_c):
|
36
|
+
return BndFlux_c(
|
37
|
+
lambda t, x, u, n: boundary_map(x) * mv(t, x, u, n),
|
38
|
+
)
|
39
|
+
|
40
|
+
boundary_flux_cs, boundary_flux_vs, boundary_values = boundary_validation(
|
41
|
+
Model, override_boundary_dict=Model.BT.diffuse
|
42
|
+
)
|
43
|
+
|
44
|
+
def make_boundary_conditional(key, bndSDFs=bndSDFs, tol=0.01):
|
45
|
+
sdf = bndSDFs[key]
|
46
|
+
return lambda x: abs(sdf(x)) < tol
|
47
|
+
|
48
|
+
for bc_key, bc_value in boundary_values.items():
|
49
|
+
boundary[make_boundary_conditional(bc_key)] = bc_value
|
50
|
+
|
51
|
+
for bc_key in boundary_flux_cs.keys() | boundary_flux_vs.keys():
|
52
|
+
if bc_key in boundary_flux_cs and bc_key in boundary_flux_vs:
|
53
|
+
af = make_boundary_function(bc_key, boundary_flux_cs[bc_key])
|
54
|
+
df = make_boundary_function(bc_key, boundary_flux_vs[bc_key])
|
55
|
+
boundary[make_boundary_conditional(bc_key)] = [af, df]
|
56
|
+
|
57
|
+
elif bc_key in boundary_flux_cs and bc_key not in boundary_flux_vs:
|
58
|
+
af = make_boundary_function(bc_key, boundary_flux_cs[bc_key])
|
59
|
+
boundary[make_boundary_conditional(bc_key)] = af
|
60
|
+
|
61
|
+
elif bc_key not in boundary_flux_cs and bc_key in boundary_flux_vs:
|
62
|
+
df = make_boundary_function(bc_key, boundary_flux_vs[bc_key])
|
63
|
+
boundary[make_boundary_conditional(bc_key)] = df
|
64
|
+
else:
|
65
|
+
raise ValueError()
|
66
|
+
|
67
|
+
return Fitted
|