Skip to content

Commit 1614420

Browse files
committed
introduce mixed mesh
1 parent eabdb00 commit 1614420

17 files changed

+504
-135
lines changed

test/test_external_operator.py

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -205,10 +205,10 @@ def test_differentiation_procedure_action(V1, V2):
205205
def test_extractions(domain_2d, V1):
206206
from ufl.algorithms.analysis import (
207207
extract_arguments,
208-
extract_arguments_and_coefficients,
209208
extract_base_form_operators,
210209
extract_coefficients,
211210
extract_constants,
211+
extract_terminals_with_domain,
212212
)
213213

214214
u = Coefficient(V1)
@@ -219,15 +219,15 @@ def test_extractions(domain_2d, V1):
219219

220220
assert extract_coefficients(e) == [u]
221221
assert extract_arguments(e) == [vstar_e]
222-
assert extract_arguments_and_coefficients(e) == ([vstar_e], [u])
222+
assert extract_terminals_with_domain(e) == ([vstar_e], [u], [])
223223
assert extract_constants(e) == [c]
224224
assert extract_base_form_operators(e) == [e]
225225

226226
F = e * dx
227227

228228
assert extract_coefficients(F) == [u]
229229
assert extract_arguments(e) == [vstar_e]
230-
assert extract_arguments_and_coefficients(e) == ([vstar_e], [u])
230+
assert extract_terminals_with_domain(e) == ([vstar_e], [u], [])
231231
assert extract_constants(F) == [c]
232232
assert F.base_form_operators() == (e,)
233233

@@ -236,14 +236,14 @@ def test_extractions(domain_2d, V1):
236236

237237
assert extract_coefficients(e) == [u]
238238
assert extract_arguments(e) == [vstar_e, u_hat]
239-
assert extract_arguments_and_coefficients(e) == ([vstar_e, u_hat], [u])
239+
assert extract_terminals_with_domain(e) == ([vstar_e, u_hat], [u], [])
240240
assert extract_base_form_operators(e) == [e]
241241

242242
F = e * dx
243243

244244
assert extract_coefficients(F) == [u]
245245
assert extract_arguments(e) == [vstar_e, u_hat]
246-
assert extract_arguments_and_coefficients(e) == ([vstar_e, u_hat], [u])
246+
assert extract_terminals_with_domain(e) == ([vstar_e, u_hat], [u], [])
247247
assert F.base_form_operators() == (e,)
248248

249249
w = Coefficient(V1)
@@ -252,14 +252,14 @@ def test_extractions(domain_2d, V1):
252252

253253
assert extract_coefficients(e2) == [u, w]
254254
assert extract_arguments(e2) == [vstar_e2, u_hat]
255-
assert extract_arguments_and_coefficients(e2) == ([vstar_e2, u_hat], [u, w])
255+
assert extract_terminals_with_domain(e2) == ([vstar_e2, u_hat], [u, w], [])
256256
assert extract_base_form_operators(e2) == [e, e2]
257257

258258
F = e2 * dx
259259

260260
assert extract_coefficients(e2) == [u, w]
261261
assert extract_arguments(e2) == [vstar_e2, u_hat]
262-
assert extract_arguments_and_coefficients(e2) == ([vstar_e2, u_hat], [u, w])
262+
assert extract_terminals_with_domain(e2) == ([vstar_e2, u_hat], [u, w], [])
263263
assert F.base_form_operators() == (e, e2)
264264

265265

test/test_interpolate.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -26,9 +26,9 @@
2626
from ufl.algorithms.ad import expand_derivatives
2727
from ufl.algorithms.analysis import (
2828
extract_arguments,
29-
extract_arguments_and_coefficients,
3029
extract_base_form_operators,
3130
extract_coefficients,
31+
extract_terminals_with_domain,
3232
)
3333
from ufl.algorithms.expand_indices import expand_indices
3434
from ufl.core.interpolate import Interpolate
@@ -157,12 +157,12 @@ def test_extract_base_form_operators(V1, V2):
157157
# -- Interpolate(u, V2) -- #
158158
Iu = Interpolate(u, V2)
159159
assert extract_arguments(Iu) == [vstar]
160-
assert extract_arguments_and_coefficients(Iu) == ([vstar], [u])
160+
assert extract_terminals_with_domain(Iu) == ([vstar], [u], [])
161161

162162
F = Iu * dx
163163
# Form composition: Iu * dx <=> Action(v * dx, Iu(u; v*))
164164
assert extract_arguments(F) == []
165-
assert extract_arguments_and_coefficients(F) == ([], [u])
165+
assert extract_terminals_with_domain(F) == ([], [u], [])
166166

167167
for e in [Iu, F]:
168168
assert extract_coefficients(e) == [u]
@@ -171,7 +171,7 @@ def test_extract_base_form_operators(V1, V2):
171171
# -- Interpolate(u, V2) -- #
172172
Iv = Interpolate(uhat, V2)
173173
assert extract_arguments(Iv) == [vstar, uhat]
174-
assert extract_arguments_and_coefficients(Iv) == ([vstar, uhat], [])
174+
assert extract_terminals_with_domain(Iv) == ([vstar, uhat], [], [])
175175
assert extract_coefficients(Iv) == []
176176
assert extract_base_form_operators(Iv) == [Iv]
177177

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
from ufl import (
2+
CellVolume,
3+
Coefficient,
4+
FacetArea,
5+
FacetNormal,
6+
FunctionSpace,
7+
Measure,
8+
Mesh,
9+
MixedMesh,
10+
SpatialCoordinate,
11+
TestFunction,
12+
TrialFunction,
13+
grad,
14+
inner,
15+
split,
16+
triangle,
17+
)
18+
from ufl.algorithms import compute_form_data
19+
from ufl.domain import extract_domains
20+
from ufl.finiteelement import FiniteElement, MixedElement
21+
from ufl.pullback import contravariant_piola, identity_pullback
22+
from ufl.sobolevspace import H1, L2, HDiv
23+
24+
25+
def test_mixed_function_space_with_mixed_mesh_basic():
26+
cell = triangle
27+
elem0 = FiniteElement("Lagrange", cell, 1, (), identity_pullback, H1)
28+
elem1 = FiniteElement("Brezzi-Douglas-Marini", cell, 1, (2,), contravariant_piola, HDiv)
29+
elem2 = FiniteElement("Discontinuous Lagrange", cell, 0, (), identity_pullback, L2)
30+
elem = MixedElement([elem0, elem1, elem2])
31+
mesh0 = Mesh(FiniteElement("Lagrange", cell, 1, (2,), identity_pullback, H1), ufl_id=100)
32+
mesh1 = Mesh(FiniteElement("Lagrange", cell, 1, (2,), identity_pullback, H1), ufl_id=101)
33+
mesh2 = Mesh(FiniteElement("Lagrange", cell, 1, (2,), identity_pullback, H1), ufl_id=102)
34+
domain = MixedMesh([mesh0, mesh1, mesh2])
35+
V = FunctionSpace(domain, elem)
36+
u = TrialFunction(V)
37+
v = TestFunction(V)
38+
f = Coefficient(V, count=1000)
39+
g = Coefficient(V, count=2000)
40+
u0, u1, u2 = split(u)
41+
v0, v1, v2 = split(v)
42+
f0, f1, f2 = split(f)
43+
g0, g1, g2 = split(g)
44+
dx1 = Measure("dx", mesh1)
45+
x = SpatialCoordinate(mesh1)
46+
form = x[1] * f0 * inner(grad(u0), v1) * dx1(999)
47+
fd = compute_form_data(
48+
form,
49+
do_apply_function_pullbacks=True,
50+
do_apply_integral_scaling=True,
51+
do_apply_geometry_lowering=True,
52+
preserve_geometry_types=(CellVolume, FacetArea),
53+
do_apply_restrictions=True,
54+
do_estimate_degrees=True,
55+
complex_mode=False,
56+
)
57+
(id0,) = fd.integral_data
58+
assert fd.preprocessed_form.arguments() == (v, u)
59+
assert fd.reduced_coefficients == [f]
60+
assert form.coefficients()[fd.original_coefficient_positions[0]] is f
61+
assert id0.domain is mesh1
62+
assert id0.integral_type == "cell"
63+
assert id0.subdomain_id == (999,)
64+
assert fd.original_form.domain_numbering()[id0.domain] == 0
65+
assert id0.integral_coefficients == set([f])
66+
assert id0.enabled_coefficients == [True]
67+
68+
69+
def test_mixed_function_space_with_mixed_mesh_signature():
70+
cell = triangle
71+
mesh0 = Mesh(FiniteElement("Lagrange", cell, 1, (2,), identity_pullback, H1), ufl_id=100)
72+
mesh1 = Mesh(FiniteElement("Lagrange", cell, 1, (2,), identity_pullback, H1), ufl_id=101)
73+
dx0 = Measure("dx", mesh0)
74+
dx1 = Measure("dx", mesh1)
75+
n0 = FacetNormal(mesh0)
76+
n1 = FacetNormal(mesh1)
77+
form_a = inner(n1, n1) * dx0(999)
78+
form_b = inner(n0, n0) * dx1(999)
79+
assert form_a.signature() == form_b.signature()
80+
assert extract_domains(form_a) == (mesh0, mesh1)
81+
assert extract_domains(form_b) == (mesh1, mesh0)

ufl/__init__.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@
6262
6363
-AbstractDomain
6464
-Mesh
65+
-MixedMesh
6566
-MeshView
6667
6768
* Sobolev spaces::
@@ -265,7 +266,7 @@
265266
from ufl.core.external_operator import ExternalOperator
266267
from ufl.core.interpolate import Interpolate, interpolate
267268
from ufl.core.multiindex import Index, indices
268-
from ufl.domain import AbstractDomain, Mesh, MeshView
269+
from ufl.domain import AbstractDomain, Mesh, MeshView, MixedMesh
269270
from ufl.finiteelement import AbstractFiniteElement
270271
from ufl.form import BaseForm, Form, FormSum, ZeroBaseForm
271272
from ufl.formoperators import (
@@ -488,6 +489,7 @@
488489
"MinCellEdgeLength",
489490
"MinFacetEdgeLength",
490491
"MixedFunctionSpace",
492+
"MixedMesh",
491493
"MixedPullback",
492494
"Not",
493495
"Or",

ufl/action.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -206,9 +206,9 @@ def _get_action_form_arguments(left, right):
206206
elif isinstance(right, CoefficientDerivative):
207207
# Action differentiation pushes differentiation through
208208
# right as a consequence of Leibniz formula.
209-
from ufl.algorithms.analysis import extract_arguments_and_coefficients
209+
from ufl.algorithms.analysis import extract_terminals_with_domain
210210

211-
right_args, right_coeffs = extract_arguments_and_coefficients(right)
211+
right_args, right_coeffs, _ = extract_terminals_with_domain(right)
212212
arguments = left_args + tuple(right_args)
213213
coefficients += tuple(right_coeffs)
214214
elif isinstance(right, (BaseCoefficient, Zero)):

ufl/algorithms/analysis.py

Lines changed: 31 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,9 @@
1818
from ufl.core.base_form_operator import BaseFormOperator
1919
from ufl.core.terminal import Terminal
2020
from ufl.corealg.traversal import traverse_unique_terminals, unique_pre_traversal
21+
from ufl.domain import Mesh
2122
from ufl.form import BaseForm, Form
23+
from ufl.geometry import GeometricQuantity
2224
from ufl.utils.sorting import sorted_by_count, topological_sorting
2325

2426
# TODO: Some of these can possibly be optimised by implementing
@@ -198,19 +200,20 @@ def extract_base_form_operators(a):
198200
return sorted_by_count(extract_type(a, BaseFormOperator))
199201

200202

201-
def extract_arguments_and_coefficients(a):
202-
"""Build two sorted lists of all arguments and coefficients in a.
203+
def extract_terminals_with_domain(a):
204+
"""Build three sorted lists of all arguments, coefficients, and geometric quantities in `a`.
203205
204-
This function is faster than extract_arguments + extract_coefficients
205-
for large forms, and has more validation built in.
206+
This function is faster than extracting each type of terminal
207+
separately for large forms, and has more validation built in.
206208
207209
Args:
208210
a: A BaseForm, Integral or Expr
209211
"""
210-
# Extract lists of all BaseArgument and BaseCoefficient instances
211-
base_coeff_and_args = extract_type(a, (BaseArgument, BaseCoefficient))
212-
arguments = [f for f in base_coeff_and_args if isinstance(f, BaseArgument)]
213-
coefficients = [f for f in base_coeff_and_args if isinstance(f, BaseCoefficient)]
212+
# Extract lists of all BaseArgument, BaseCoefficient, and GeometricQuantity instances
213+
terminals = extract_type(a, (BaseArgument, BaseCoefficient, GeometricQuantity))
214+
arguments = [f for f in terminals if isinstance(f, BaseArgument)]
215+
coefficients = [f for f in terminals if isinstance(f, BaseCoefficient)]
216+
geometric_quantities = [f for f in terminals if isinstance(f, GeometricQuantity)]
214217

215218
# Build number,part: instance mappings, should be one to one
216219
bfnp = dict((f, (f.number(), f.part())) for f in arguments)
@@ -226,20 +229,36 @@ def extract_arguments_and_coefficients(a):
226229
if len(fcounts) != len(set(fcounts.values())):
227230
raise ValueError(
228231
"Found different coefficients with same counts.\n"
229-
"The arguments found are:\n" + "\n".join(f" {c}" for c in coefficients)
232+
"The Coefficients found are:\n" + "\n".join(f" {c}" for c in coefficients)
233+
)
234+
235+
# Build count: instance mappings, should be one to one
236+
gqcounts = {}
237+
for gq in geometric_quantities:
238+
if not isinstance(gq._domain, Mesh):
239+
raise TypeError(f"{gq}._domain must be a Mesh: got {gq._domain}")
240+
gqcounts[gq] = (type(gq).name, gq._domain._ufl_id)
241+
if len(gqcounts) != len(set(gqcounts.values())):
242+
raise ValueError(
243+
"Found different geometric quantities with same (geometric_quantity_type, domain).\n"
244+
"The GeometricQuantities found are:\n"
245+
"\n".join(f" {gq}" for gq in geometric_quantities)
230246
)
231247

232248
# Passed checks, so we can safely sort the instances by count
233249
arguments = _sorted_by_number_and_part(arguments)
234250
coefficients = sorted_by_count(coefficients)
251+
geometric_quantities = list(
252+
sorted(geometric_quantities, key=lambda gq: (type(gq).name, gq._domain._ufl_id))
253+
)
235254

236-
return arguments, coefficients
255+
return arguments, coefficients, geometric_quantities
237256

238257

239258
def extract_elements(form):
240259
"""Build sorted tuple of all elements used in form."""
241-
args = chain(*extract_arguments_and_coefficients(form))
242-
return tuple(f.ufl_element() for f in args)
260+
arguments, coefficients, _ = extract_terminals_with_domain(form)
261+
return tuple(f.ufl_element() for f in arguments + coefficients)
243262

244263

245264
def extract_unique_elements(form):

0 commit comments

Comments
 (0)