Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
100 commits
Select commit Hold shift + click to select a range
44f3b29
Retire populate_namespace_with_expr_classes
schnellerhase Jun 26, 2025
57dcc30
combine
schnellerhase Jun 26, 2025
c2a8bf2
Remove populate_namespace_with_module_classes
schnellerhase Jun 26, 2025
441a31f
single __all__
schnellerhase Jun 26, 2025
9b51075
Tidy up
schnellerhase Jun 26, 2025
7663e85
Start on mypy
schnellerhase Jun 27, 2025
cfeef15
Merge branch 'main' into schnellerhase/fix_386
schnellerhase Jun 27, 2025
69e65f5
ruff
schnellerhase Jun 27, 2025
3bacb4e
more mypy
schnellerhase Jun 27, 2025
016f2fe
Ignore apply_derivates for now
schnellerhase Jun 27, 2025
894df43
add ignore to aply derivates
schnellerhase Jun 27, 2025
8338db4
ruff
schnellerhase Jun 27, 2025
79d737e
Avoid circular dependecny in typing
schnellerhase Jun 27, 2025
78e2896
All up to objects
schnellerhase Jun 27, 2025
e8331e8
Fix objects
schnellerhase Jun 27, 2025
3c1ff0d
ruff
schnellerhase Jun 27, 2025
c567510
Revert unfixable
schnellerhase Jun 27, 2025
36a9126
Remove coment
schnellerhase Jun 29, 2025
f6358b9
Apply suggestions from code review
schnellerhase Jun 30, 2025
7baa497
Some more
schnellerhase Jul 1, 2025
48f12fa
Move to per line ignore
schnellerhase Jul 1, 2025
38633e7
Revert default type changes
schnellerhase Jul 1, 2025
b285953
Last?
schnellerhase Jul 1, 2025
4a4c278
Merge branch 'main' into schnellerhase/fix_386
schnellerhase Jul 3, 2025
afe157a
Remove duplicates
schnellerhase Jul 3, 2025
4589c6f
Remove changes to UFLObject
schnellerhase Jul 3, 2025
e7052f7
Simplify
schnellerhase Jul 3, 2025
bfbbfc9
Merge branch 'main' into schnellerhase/fix_386
schnellerhase Jul 4, 2025
93cae82
Merge branch 'main' into schnellerhase/fix_386
schnellerhase Jul 10, 2025
4f98312
Merge branch 'main' into schnellerhase/fix_386
schnellerhase Jul 23, 2025
a8efe79
Merge branch 'main' into schnellerhase/fix_386
schnellerhase Aug 7, 2025
05a5003
Start with is_terminal
schnellerhase Jul 2, 2025
629d805
Move _ufl_all_classes_ to new UFLRegistry
schnellerhase Jul 2, 2025
602fdc0
Remove duplicate info _ufl_num_typecods_
schnellerhase Jul 2, 2025
eb97e88
Remove unused _ufl_all_handler_names_
schnellerhase Jul 2, 2025
421be2e
Move profiling info to registry, finalizes global states in UFLType
schnellerhase Jul 2, 2025
6058a4b
Trait list complete
schnellerhase Jul 2, 2025
4d74084
Begin to replace decorator
schnellerhase Jul 2, 2025
8ff603c
Set is_restriction through class hierachy
schnellerhase Jul 2, 2025
8c5c916
Set is_differential through class hierachy
schnellerhase Jul 2, 2025
28d11e4
Set is_evaluation through class hierachy
schnellerhase Jul 2, 2025
ddd0348
Set is_in_reference_frame through class hierachy
schnellerhase Jul 2, 2025
bbfd100
Set is_shaping through class hierachy
schnellerhase Jul 2, 2025
0b32ec6
Set is_literal through class hierachy
schnellerhase Jul 2, 2025
99b0616
Set is_terminal_modifier through class hierachy
schnellerhase Jul 2, 2025
8085003
Remove unused num_ops property from UFLType
schnellerhase Jul 2, 2025
cf23172
Set is_terminal through class hierachy
schnellerhase Jul 2, 2025
ddda292
Remove is_abstract
schnellerhase Jul 2, 2025
ae6a920
Set is_scalar through class hierachy
schnellerhase Jul 2, 2025
b2b363f
Set is_index_free through class hierachy
schnellerhase Jul 2, 2025
0bc8c13
Remove _ufl_terminal_modifiers_
schnellerhase Jul 2, 2025
0fbd1e9
Add custom type test case
schnellerhase Jul 3, 2025
e7634df
Remove (unused) wraps_type
schnellerhase Aug 11, 2025
90c35a0
On PR not main
schnellerhase Aug 11, 2025
664cc07
Remove (unused) unop
schnellerhase Aug 11, 2025
634d031
Remove (unused) binop
schnellerhase Aug 11, 2025
11721bc
Remove (unused) rbinop
schnellerhase Aug 11, 2025
293607e
Remove inherit_shape_from_operand
schnellerhase Aug 11, 2025
d93de2d
Remove inherit_indices_from_operand
schnellerhase Aug 11, 2025
523283f
Remove _ufl_is_scalar_
schnellerhase Aug 11, 2025
e94f6f4
Remove _ufl_is_index_free_
schnellerhase Aug 11, 2025
b9bf6be
one more
schnellerhase Aug 11, 2025
5375569
Simplify
schnellerhase Aug 11, 2025
4f9c5bb
Remove _ufl_class_
schnellerhase Aug 11, 2025
389ab1d
Remove (unused) _ufl_is_shaping_
schnellerhase Aug 11, 2025
24a1e28
Remove (unused) _ufl_is_restriction_
schnellerhase Aug 11, 2025
3a0657f
Remove (unused) _ufl_is_evaluation_
schnellerhase Aug 11, 2025
c9ff724
Remove (unused) _ufl_is_differential_
schnellerhase Aug 11, 2025
6bb684f
Remove (unused) _ufl_is_literal_
schnellerhase Aug 11, 2025
9ed883e
Revert "Remove (unused) _ufl_is_literal_"
schnellerhase Aug 11, 2025
e795017
Remove use_default_hash
schnellerhase Aug 12, 2025
81d8cae
No more type consistency checks
schnellerhase Aug 12, 2025
01cc31d
Move Expr properties back
schnellerhase Aug 12, 2025
bb66ec9
Clean up expr further
schnellerhase Aug 12, 2025
1c7b51e
Remove more required methods
schnellerhase Aug 12, 2025
f4ba329
Extend custom type test to differentiation
schnellerhase Aug 12, 2025
e66ae05
Merge branch 'main' into schnellerhase/remove-type-system
schnellerhase Aug 21, 2025
73fde95
Ruff + revert branches
schnellerhase Aug 21, 2025
d96f2a8
Check without operands
schnellerhase Aug 21, 2025
104bf9d
Merge branch 'main' into schnellerhase/remove-type-system
schnellerhase Aug 29, 2025
aa2a456
Merge branch 'main' into schnellerhase/remove-type-system
schnellerhase Sep 4, 2025
0351ce4
Ruff check
schnellerhase Sep 4, 2025
838ac4d
Merge branch 'main' into schnellerhase/remove-type-system
schnellerhase Sep 8, 2025
cab9de2
Merge branch 'main' into schnellerhase/remove-type-system
schnellerhase Sep 10, 2025
04f410b
ruff
schnellerhase Sep 10, 2025
197a145
Merge branch 'main' into schnellerhase/remove-type-system
schnellerhase Sep 10, 2025
25bc1e9
Merge branch 'main' into schnellerhase/remove-type-system
schnellerhase Sep 13, 2025
5480aa0
Merge branch 'main' into schnellerhase/remove-type-system
schnellerhase Sep 20, 2025
e704e46
Merge branch 'main' into schnellerhase/remove-type-system
schnellerhase Oct 1, 2025
f41247c
Merge branch 'main' into schnellerhase/remove-type-system
schnellerhase Oct 15, 2025
606c360
Merge branch 'main' into schnellerhase/remove-type-system
schnellerhase Oct 19, 2025
b666d50
Adapt to changes on main
schnellerhase Oct 19, 2025
d09a898
Ruff
schnellerhase Oct 19, 2025
556c5c8
Merge branch 'main' into schnellerhase/remove-type-system
schnellerhase Oct 21, 2025
0be41c4
Merge branch 'main' into schnellerhase/remove-type-system
schnellerhase Oct 23, 2025
059cee1
Adapt to main
schnellerhase Oct 23, 2025
4ab930b
One more
schnellerhase Oct 23, 2025
36e07d0
Merge branch 'main' into schnellerhase/remove-type-system
schnellerhase Oct 24, 2025
c0c2230
Merge branch 'main' into schnellerhase/remove-type-system
schnellerhase Oct 27, 2025
45bdbd8
Merge branch 'main' into schnellerhase/remove-type-system
schnellerhase Dec 27, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 7 additions & 6 deletions test/test_classcoverage.py
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,7 @@
Tanh,
all_ufl_classes,
)
from ufl.core.ufl_type import UFLRegistry

has_repr = set()
has_dict = set()
Expand Down Expand Up @@ -722,15 +723,15 @@ def testAll(self):
# e = action(b)

# --- Check which classes have been created
ic, _dc = Expr.ufl_disable_profiling()
Expr.ufl_disable_profiling()
ic = UFLRegistry().object_tracking

constructed = set()
unused = set(Expr._ufl_all_classes_)
for cls in Expr._ufl_all_classes_:
tc = cls._ufl_typecode_
if ic[tc]:
unused = set(UFLRegistry().all_classes)
for cls in ic.keys():
if ic[cls][0] > 0:
constructed.add(cls)
if cls._ufl_is_abstract_:
else:
unused.remove(cls)

if unused:
Expand Down
54 changes: 54 additions & 0 deletions test/test_custom_type.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
# Copyright (C) 2025 Paul T. Kühner
#
# This file is part of UFL (https://www.fenicsproject.org)
#
# SPDX-License-Identifier: LGPL-3.0-or-later
#

from utils import LagrangeElement

from ufl import Mesh, triangle
from ufl.algebra import Product
from ufl.algorithms.analysis import extract_constants, has_exact_type
from ufl.algorithms.apply_algebra_lowering import apply_algebra_lowering
from ufl.constant import Constant
from ufl.core.ufl_type import ufl_type
from ufl.geometry import SpatialCoordinate
from ufl.operators import diff


@ufl_type()
class LabeledConstant(Constant):
def __init__(self, domain, shape=(), count=None, label: str = "c"):
Constant.__init__(self, domain, shape, count)
self._label = label

@property
def label(self) -> str:
return self._label


def test():
domain = Mesh(LagrangeElement(triangle, 1, (2,)))
a = LabeledConstant(domain, label="a")
b = LabeledConstant(domain, label="b")

assert a.label == "a"
assert b.label == "b"

ab = a * b
assert isinstance(ab, Product)
# assert ab.ufl_operands == (a, b)

assert apply_algebra_lowering(ab) == ab

x = SpatialCoordinate(domain)

a_dx = diff(a, x)
assert a_dx == 0
assert a_dx.ufl_shape == (2,)

# TODO: expression does not get simplified, so can't check here for ax_dx == a
ax_dx = diff(a * x, x)
assert has_exact_type(ax_dx, LabeledConstant)
assert extract_constants(ax_dx)[0].label == "a"
117 changes: 103 additions & 14 deletions ufl/algebra.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,13 +20,7 @@
# --- Algebraic operators ---


@ufl_type(
num_ops=2,
inherit_shape_from_operand=0,
inherit_indices_from_operand=0,
binop="__add__",
rbinop="__radd__",
)
@ufl_type()
class Sum(Operator):
"""Sum."""

Expand Down Expand Up @@ -103,8 +97,23 @@ def __str__(self):
"""Format as a string."""
return " + ".join([parstr(o, self) for o in self.ufl_operands])

@property
def ufl_shape(self):
"""Return shape."""
return self.ufl_operands[0].ufl_shape

@ufl_type(num_ops=2, binop="__mul__", rbinop="__rmul__")
@property
def ufl_free_indices(self):
"""Return free indices."""
return self.ufl_operands[0].ufl_free_indices

@property
def ufl_index_dimensions(self):
"""Retrun index dimensions."""
return self.ufl_operands[0].ufl_index_dimensions


@ufl_type()
class Product(Operator):
"""The product of two or more UFL objects."""

Expand Down Expand Up @@ -201,7 +210,7 @@ def __str__(self):
return " * ".join((parstr(a, self), parstr(b, self)))


@ufl_type(num_ops=2, inherit_indices_from_operand=0, binop="__div__", rbinop="__rdiv__")
@ufl_type()
class Division(Operator):
"""Division."""

Expand Down Expand Up @@ -265,8 +274,18 @@ def __str__(self):
"""Format as a string."""
return f"{parstr(self.ufl_operands[0], self)} / {parstr(self.ufl_operands[1], self)}"

@property
def ufl_free_indices(self):
"""Return free indices."""
return self.ufl_operands[0].ufl_free_indices

@property
def ufl_index_dimensions(self):
"""Retrun index dimensions."""
return self.ufl_operands[0].ufl_index_dimensions


@ufl_type(num_ops=2, inherit_indices_from_operand=0, binop="__pow__", rbinop="__rpow__")
@ufl_type()
class Power(Operator):
"""Power."""

Expand Down Expand Up @@ -327,8 +346,18 @@ def __str__(self):
a, b = self.ufl_operands
return f"{parstr(a, self)} ** {parstr(b, self)}"

@property
def ufl_free_indices(self):
"""Return free indices."""
return self.ufl_operands[0].ufl_free_indices

@ufl_type(num_ops=1, inherit_shape_from_operand=0, inherit_indices_from_operand=0, unop="__abs__")
@property
def ufl_index_dimensions(self):
"""Retrun index dimensions."""
return self.ufl_operands[0].ufl_index_dimensions


@ufl_type()
class Abs(Operator):
"""Absolute value."""

Expand Down Expand Up @@ -362,8 +391,23 @@ def __str__(self):
(a,) = self.ufl_operands
return f"|{parstr(a, self)}|"

@property
def ufl_shape(self):
"""Return shape."""
return self.ufl_operands[0].ufl_shape

@property
def ufl_free_indices(self):
"""Return free indices."""
return self.ufl_operands[0].ufl_free_indices

@property
def ufl_index_dimensions(self):
"""Retrun index dimensions."""
return self.ufl_operands[0].ufl_index_dimensions


@ufl_type(num_ops=1, inherit_shape_from_operand=0, inherit_indices_from_operand=0)
@ufl_type()
class Conj(Operator):
"""Complex conjugate."""

Expand Down Expand Up @@ -397,8 +441,23 @@ def __str__(self):
(a,) = self.ufl_operands
return f"conj({parstr(a, self)})"

@property
def ufl_shape(self):
"""Return shape."""
return self.ufl_operands[0].ufl_shape

@ufl_type(num_ops=1, inherit_shape_from_operand=0, inherit_indices_from_operand=0)
@property
def ufl_free_indices(self):
"""Return free indices."""
return self.ufl_operands[0].ufl_free_indices

@property
def ufl_index_dimensions(self):
"""Retrun index dimensions."""
return self.ufl_operands[0].ufl_index_dimensions


@ufl_type()
class Real(Operator):
"""Real part."""

Expand Down Expand Up @@ -434,8 +493,23 @@ def __str__(self):
(a,) = self.ufl_operands
return f"Re[{parstr(a, self)}]"

@property
def ufl_shape(self):
"""Return shape."""
return self.ufl_operands[0].ufl_shape

@property
def ufl_free_indices(self):
"""Return free indices."""
return self.ufl_operands[0].ufl_free_indices

@property
def ufl_index_dimensions(self):
"""Retrun index dimensions."""
return self.ufl_operands[0].ufl_index_dimensions

@ufl_type(num_ops=1, inherit_shape_from_operand=0, inherit_indices_from_operand=0)

@ufl_type()
class Imag(Operator):
"""Imaginary part."""

Expand Down Expand Up @@ -468,3 +542,18 @@ def __str__(self):
"""Format as a string."""
(a,) = self.ufl_operands
return f"Im[{parstr(a, self)}]"

@property
def ufl_shape(self):
"""Return shape."""
return self.ufl_operands[0].ufl_shape

@property
def ufl_free_indices(self):
"""Return free indices."""
return self.ufl_operands[0].ufl_free_indices

@property
def ufl_index_dimensions(self):
"""Retrun index dimensions."""
return self.ufl_operands[0].ufl_index_dimensions
15 changes: 9 additions & 6 deletions ufl/algorithms/apply_coefficient_split.py
Original file line number Diff line number Diff line change
Expand Up @@ -105,8 +105,9 @@ def _(
if reference_value:
raise RuntimeError(f"Can not apply ReferenceValue on a ReferenceValue: got {o}")
(op,) = o.ufl_operands
if not op._ufl_terminal_modifiers_:
raise ValueError(f"Must be a terminal modifier: {op!r}.")
# TODO: correct?
# if not op._ufl_is_terminal_modifier_:
# raise ValueError(f"Must be a terminal modifier: {op!r}.")
return self(
op,
reference_value=True,
Expand All @@ -124,8 +125,9 @@ def _(
) -> Expr:
"""Handle ReferenceGrad."""
(op,) = o.ufl_operands
if not op._ufl_terminal_modifiers_:
raise ValueError(f"Must be a terminal modifier: {op!r}.")
# TODO:
# if not op._ufl_is_terminal_modifier_:
# raise ValueError(f"Must be a terminal modifier: {op!r}.")
return self(
op,
reference_value=reference_value,
Expand All @@ -145,8 +147,9 @@ def _(
if restricted is not None:
raise RuntimeError(f"Can not apply Restricted on a Restricted: got {o}")
(op,) = o.ufl_operands
if not op._ufl_terminal_modifiers_:
raise ValueError(f"Must be a terminal modifier: {op!r}.")
# TODO:
# if not op._ufl_is_terminal_modifier_:
# raise ValueError(f"Must be a terminal modifier: {op!r}.")
return self(
op,
reference_value=reference_value,
Expand Down
17 changes: 7 additions & 10 deletions ufl/algorithms/apply_derivatives.py
Original file line number Diff line number Diff line change
Expand Up @@ -166,13 +166,11 @@ def __init__(

def unexpected(self, o):
"""Raise error about unexpected type."""
raise ValueError(f"Unexpected type {o._ufl_class_.__name__} in AD rules.")
raise ValueError(f"Unexpected type {type(o).__name__} in AD rules.")

def override(self, o):
"""Raise error about overriding."""
raise ValueError(
f"Type {o._ufl_class_.__name__} must be overridden in specialized AD rule set."
)
raise ValueError(f"Type {type(o).__name__} must be overridden in specialized AD rule set.")

# --- Some types just don't have any derivative, this is just to
# --- make algorithm structure generic
Expand Down Expand Up @@ -214,16 +212,15 @@ def process(self, o: Expr) -> Expr:
def _(self, o: Expr) -> Expr:
"""Raise error."""
raise ValueError(
f"Missing differentiation handler for type {o._ufl_class_.__name__}. "
f"Missing differentiation handler for type {type(o).__name__}. "
"Have you added a new type?"
)

@process.register(Derivative)
def _(self, o: Expr) -> Expr:
"""Raise error."""
raise ValueError(
f"Unhandled derivative type {o._ufl_class_.__name__}, "
"nested differentiation has failed."
f"Unhandled derivative type {type(o).__name__}, nested differentiation has failed."
)

@process.register(Label)
Expand Down Expand Up @@ -321,7 +318,7 @@ def _(self, o: Expr) -> Expr:

@process.register(Indexed)
@DAGTraverser.postorder
def _(self, o: Indexed, Ap: Expr, ii: MultiIndex) -> Expr:
def _(self, o: Indexed, Ap: Expr, ii: MultiIndex) -> Indexed:
"""Differentiate an indexed."""
# Propagate zeros
if isinstance(Ap, Zero):
Expand Down Expand Up @@ -905,8 +902,8 @@ def _(self, o: Expr) -> Expr:
if not f._ufl_is_in_reference_frame_:
raise RuntimeError("Expecting a reference frame type")
while not f._ufl_is_terminal_:
(f,) = f.ufl_operands
element = f.ufl_function_space().ufl_element()
(f,) = f.ufl_operands # type: ignore
element = f.ufl_function_space().ufl_element() # type: ignore
if element.num_sub_elements != len(domain):
raise RuntimeError(f"{element.num_sub_elements} != {len(domain)}")
# Get monolithic representation of rgrad(o); o might live in a mixed space.
Expand Down
3 changes: 2 additions & 1 deletion ufl/algorithms/apply_geometry_lowering.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@
)
from ufl.compound_expressions import cross_expr, determinant_expr, inverse_expr
from ufl.core.multiindex import Index, indices
from ufl.core.ufl_type import UFLRegistry
from ufl.corealg.map_dag import map_expr_dag
from ufl.corealg.multifunction import MultiFunction, memoized_handler
from ufl.domain import extract_unique_domain
Expand All @@ -58,7 +59,7 @@ def __init__(self, preserve_types=()):
"""Initialise."""
MultiFunction.__init__(self)
# Store preserve_types as boolean lookup table
self._preserve_types = [False] * Expr._ufl_num_typecodes_
self._preserve_types = [False] * UFLRegistry().number_registered_classes
for cls in preserve_types:
self._preserve_types[cls._ufl_typecode_] = True

Expand Down
Loading