Skip to content

Commit dcad2e4

Browse files
authored
Merge pull request #60 from firedrakeproject/pbrubeck/merge-upstream
Merge upstream
2 parents 5da0ab9 + 07034ea commit dcad2e4

7 files changed

Lines changed: 67 additions & 25 deletions

File tree

.github/workflows/fenicsx-tests.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,8 @@ jobs:
5353
pip install .[ci]
5454
- name: Run FFCx unit tests
5555
run: python3 -m pytest -n auto ffcx/test
56+
- name: Run FFCx demos
57+
run: python3 -m pytest -n auto ffcx/demo/test_demos.py
5658

5759
dolfinx-tests:
5860
name: Run DOLFINx tests

test/test_evaluate.py

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33

44
import math
55

6+
import numpy as np
7+
68
from ufl import (
79
Argument,
810
Coefficient,
@@ -33,12 +35,28 @@
3335
tr,
3436
triangle,
3537
)
36-
from ufl.constantvalue import as_ufl
38+
from ufl.constantvalue import ConstantValue, as_ufl
3739
from ufl.finiteelement import FiniteElement
3840
from ufl.pullback import identity_pullback
3941
from ufl.sobolevspace import H1
4042

4143

44+
class CustomConstant(ConstantValue):
45+
def __init__(self, value):
46+
super().__init__()
47+
self._value = value
48+
49+
@property
50+
def ufl_shape(self):
51+
return ()
52+
53+
def evaluate(self, x, mapping, component, index_values):
54+
return self._value
55+
56+
def __repr__(self):
57+
return f"CustomConstant({self._value})"
58+
59+
4260
def testScalars():
4361
s = as_ufl(123)
4462
e = s((5, 7))
@@ -132,6 +150,21 @@ def testAlgebra():
132150
assert e == v
133151

134152

153+
def testConstant():
154+
"""Test that constant division doesn't discard the complex type in the case the value is
155+
a numpy complex type, not a native python complex type.
156+
"""
157+
_a = np.complex128(1 + 1j)
158+
_b = np.complex128(-3 + 2j)
159+
a = CustomConstant(_a)
160+
b = CustomConstant(_b)
161+
expr = a / b
162+
e = expr(())
163+
164+
expected = complex(_a) / complex(_b)
165+
assert e == expected
166+
167+
135168
def testIndexSum():
136169
cell = triangle
137170
domain = Mesh(FiniteElement("Lagrange", cell, 1, (2,), identity_pullback, H1))

test/test_interpolate.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
Adjoint,
1111
Argument,
1212
Coefficient,
13+
Cofunction,
1314
FunctionSpace,
1415
Mesh,
1516
TestFunction,
@@ -69,6 +70,20 @@ def test_symbolic(V1, V2):
6970
assert Iu.ufl_operands == (u,)
7071

7172

73+
def test_symbolic_adjoint(V1, V2):
74+
# Set dual of V2
75+
V2_dual = V2.dual()
76+
77+
u = Argument(V1, 1)
78+
vstar = Cofunction(V2_dual)
79+
Iu = Interpolate(u, vstar)
80+
81+
assert Iu.ufl_function_space() == V2_dual
82+
assert Iu.argument_slots() == (vstar, u)
83+
assert Iu.arguments() == (u,)
84+
assert Iu.ufl_operands == (u,)
85+
86+
7287
def test_action_adjoint(V1, V2):
7388
# Set dual of V2
7489
V2_dual = V2.dual()

ufl/algebra.py

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -253,12 +253,7 @@ def evaluate(self, x, mapping, component, index_values):
253253
a, b = self.ufl_operands
254254
a = a.evaluate(x, mapping, component, index_values)
255255
b = b.evaluate(x, mapping, component, index_values)
256-
# Avoiding integer division by casting to float
257-
try:
258-
e = float(a) / float(b)
259-
except TypeError:
260-
e = complex(a) / complex(b)
261-
return e
256+
return a / b
262257

263258
def __str__(self):
264259
"""Format as a string."""

ufl/core/base_form_operator.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
from collections import OrderedDict
1717

1818
from ufl.argument import Argument, Coargument
19+
from ufl.coefficient import BaseCoefficient
1920
from ufl.constantvalue import as_ufl
2021
from ufl.core.operator import Operator
2122
from ufl.core.ufl_type import ufl_type
@@ -134,20 +135,19 @@ def count(self):
134135
def ufl_shape(self):
135136
"""Return the UFL shape of the coefficient.produced by the operator."""
136137
arg, *_ = self.argument_slots()
137-
if isinstance(arg, BaseForm):
138+
if not isinstance(arg, BaseCoefficient) and isinstance(arg, (BaseForm, Coargument)):
138139
arg, *_ = arg.arguments()
139140
return arg._ufl_shape
140141

141142
def ufl_function_space(self):
142143
"""Return the function space associated to the operator.
143144
144-
I.e. return the dual of the base form operator's Coargument.
145+
I.e. return the dual of the base form operator's Coargument space.
145146
"""
146147
arg, *_ = self.argument_slots()
147-
if isinstance(arg, BaseForm):
148+
if not isinstance(arg, BaseCoefficient) and isinstance(arg, (BaseForm, Coargument)):
148149
arg, *_ = arg.arguments()
149-
return arg._ufl_function_space
150-
return arg._ufl_function_space.dual()
150+
return arg.ufl_function_space()
151151

152152
def _ufl_expr_reconstruct_(
153153
self, *operands, function_space=None, derivatives=None, argument_slots=None

ufl/core/interpolate.py

Lines changed: 6 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -8,14 +8,12 @@
88
#
99
# Modified by Nacime Bouziani, 2021-2022
1010

11-
from ufl.action import Action
1211
from ufl.argument import Argument, Coargument
13-
from ufl.coefficient import Cofunction
1412
from ufl.constantvalue import as_ufl
1513
from ufl.core.base_form_operator import BaseFormOperator
1614
from ufl.core.ufl_type import ufl_type
1715
from ufl.duals import is_dual
18-
from ufl.form import BaseForm, Form
16+
from ufl.form import BaseForm
1917
from ufl.functionspace import AbstractFunctionSpace
2018

2119

@@ -35,16 +33,15 @@ def __init__(self, expr, v):
3533
v: the FunctionSpace to interpolate into or the Coargument
3634
defined on the dual of the FunctionSpace to interpolate into.
3735
"""
38-
# This check could be more rigorous.
39-
dual_args = (Coargument, Cofunction, Form, Action, BaseFormOperator)
36+
dual_args = (Coargument, BaseForm)
4037

4138
if isinstance(v, AbstractFunctionSpace):
4239
if is_dual(v):
4340
raise ValueError("Expecting a primal function space.")
4441
v = Argument(v.dual(), 0)
4542
elif not isinstance(v, dual_args):
4643
raise ValueError(
47-
"Expecting the second argument to be FunctionSpace, FiniteElement or dual."
44+
"Expecting the second argument to be FunctionSpace, Coargument, or BaseForm."
4845
)
4946

5047
expr = as_ufl(expr)
@@ -54,11 +51,9 @@ def __init__(self, expr, v):
5451
# Reversed order convention
5552
argument_slots = (v, expr)
5653
# Get the primal space (V** = V)
57-
if isinstance(v, BaseForm):
58-
arg, *_ = v.arguments()
59-
function_space = arg.ufl_function_space()
60-
else:
61-
function_space = v.ufl_function_space().dual()
54+
arg, *_ = v.arguments()
55+
function_space = arg.ufl_function_space()
56+
6257
# Set the operand as `expr` for DAG traversal purpose.
6358
operand = expr
6459
BaseFormOperator.__init__(

ufl/tensors.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,6 @@ def sub(e, *indices):
7474
return sub(e0, 0) if j == () else sub(e0, 0)[(*j, slice(None))]
7575
except ValueError:
7676
pass
77-
7877
# Simplify [v[0,:], v[1,:], ..., v[k,:]] -> v
7978
if (
8079
all(
@@ -85,7 +84,10 @@ def sub(e, *indices):
8584
and all(sub(e, 0, 0) == sub(e0, 0, 0) for e in expressions[1:])
8685
):
8786
indices = [sub(e, 0, 1).indices() for e in expressions]
88-
if all(i[0] == k for k, i in enumerate(indices)):
87+
if all(
88+
i[0] == k and all(isinstance(subindex, Index) for subindex in i[1:])
89+
for k, i in enumerate(indices)
90+
):
8991
return sub(e0, 0, 0)
9092

9193
# Construct a new instance to be initialised

0 commit comments

Comments
 (0)