Skip to content

Commit

Permalink
Merge pull request #30 from firedrakeproject/merge_fenics
Browse files Browse the repository at this point in the history
Merge fenics
  • Loading branch information
dham authored Oct 13, 2022
2 parents 9135be1 + 3f5ad50 commit 0c592ec
Show file tree
Hide file tree
Showing 19 changed files with 195 additions and 82 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,5 @@ test/.pytest_cache
*.egg-info
dist/
release/

.*.swp
2 changes: 1 addition & 1 deletion setup.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
# future
[metadata]
name = fenics-ufl
version = 2022.2.0.dev0
version = 2022.3.0.dev0
author = FEniCS Project Contributors
email = [email protected]
maintainer = FEniCS Project Steering Council
Expand Down
23 changes: 21 additions & 2 deletions test/test_sobolevspace.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,16 @@
FiniteElement, triangle, interval,
quadrilateral, HDiv, HCurl)
from ufl.sobolevspace import SobolevSpace, DirectionalSobolevSpace
from ufl import H2, H1, HDiv, HCurl, L2
from ufl import H2, H1, HDiv, HCurl, L2, HInf
from math import inf


# Construct directional Sobolev spaces, with varying smoothness in
# spatial coordinates
H0dx0dy = DirectionalSobolevSpace((0, 0))
H1dx1dy = DirectionalSobolevSpace((1, 1))
H2dx2dy = DirectionalSobolevSpace((2, 2))
Hinfdxinfdy = DirectionalSobolevSpace((inf, inf))
H1dx = DirectionalSobolevSpace((1, 0))
H1dy = DirectionalSobolevSpace((0, 1))
H000 = DirectionalSobolevSpace((0, 0, 0))
Expand Down Expand Up @@ -44,6 +46,8 @@ def test_directional_space_relations():
assert H1dx1dy <= HCurl
assert H2dx2dy <= H1dx1dy
assert H2dhH1dz < H1
assert Hinfdxinfdy <= HInf
assert Hinfdxinfdy < H2dx2dy
assert H1dz > H2dhH1dz
assert H1dh < L2
assert H1dz < L2
Expand All @@ -63,7 +67,6 @@ def xtest_contains_mixed():

def test_contains_l2():
l2_elements = [
FiniteElement("Real", triangle, 0),
FiniteElement("DG", triangle, 0),
FiniteElement("DG", triangle, 1),
FiniteElement("DG", triangle, 2),
Expand Down Expand Up @@ -132,6 +135,22 @@ def test_contains_h2():
assert h2_element in H0dx0dy


def test_contains_hinf():
hinf_elements = [
FiniteElement("R", triangle, 0)
]
for hinf_element in hinf_elements:
assert hinf_element in HInf
assert hinf_element in H2
assert hinf_element in H2dx2dy
assert hinf_element in H1
assert hinf_element in H1dx1dy
assert hinf_element in HDiv
assert hinf_element in HCurl
assert hinf_element in L2
assert hinf_element in H0dx0dy


def test_contains_hdiv():
hdiv_elements = [
FiniteElement("RT", triangle, 1),
Expand Down
7 changes: 5 additions & 2 deletions ufl/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -68,8 +68,11 @@
- L2
- H1
- H2
- HInf
- HDiv
- HCurl
- HEin
- HDivDiv
* Elements::
Expand Down Expand Up @@ -272,7 +275,7 @@
)

# Sobolev spaces
from ufl.sobolevspace import L2, H1, H2, HDiv, HCurl
from ufl.sobolevspace import L2, H1, H2, HDiv, HCurl, HEin, HDivDiv, HInf

# Finite elements classes
from ufl.finiteelement import FiniteElementBase, FiniteElement, \
Expand Down Expand Up @@ -369,7 +372,7 @@
'UFLException', 'DEBUG', 'INFO', 'WARNING', 'ERROR', 'CRITICAL',
'as_cell', 'AbstractCell', 'Cell', 'TensorProductCell',
'as_domain', 'AbstractDomain', 'Mesh', 'MeshView', 'TensorProductMesh',
'L2', 'H1', 'H2', 'HCurl', 'HDiv',
'L2', 'H1', 'H2', 'HCurl', 'HDiv', 'HInf', 'HEin', 'HDivDiv',
'SpatialCoordinate',
'CellVolume', 'CellDiameter', 'Circumradius',
'MinCellEdgeLength', 'MaxCellEdgeLength',
Expand Down
8 changes: 2 additions & 6 deletions ufl/algorithms/apply_restrictions.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
from ufl.corealg.map_dag import map_expr_dag
from ufl.algorithms.map_integrands import map_integrand_dags
from ufl.measure import integral_type_to_measure_name
from ufl.sobolevspace import H1


class RestrictionPropagator(MultiFunction):
Expand Down Expand Up @@ -128,12 +129,7 @@ def reference_value(self, o):

def coefficient(self, o):
"Allow coefficients to be unrestricted (apply default if so) if the values are fully continuous across the facet."
e = o.ufl_element()
d = e.degree()
f = e.family()
# TODO: Move this choice to the element class?
continuous_families = ["Lagrange", "Q", "S"]
if (f in continuous_families and d > 0) or f == "Real":
if o.ufl_element() in H1:
# If the coefficient _value_ is _fully_ continuous
return self._default_restricted(o) # Must still be computed from one of the sides, we just don't care which
else:
Expand Down
6 changes: 6 additions & 0 deletions ufl/constant.py
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,12 @@ def __eq__(self, other):
self._ufl_domain == other._ufl_domain and
self._ufl_shape == self._ufl_shape)

def _ufl_signature_data_(self, renumbering):
"Signature data for constant depends on renumbering"
return "Constant({}, {}, {})".format(
self._ufl_domain._ufl_signature_data_(renumbering), repr(self._ufl_shape),
repr(renumbering[self]))


def VectorConstant(domain, count=None):
domain = as_domain(domain)
Expand Down
15 changes: 11 additions & 4 deletions ufl/finiteelement/brokenelement.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,13 @@
# Modified by Massimiliano Leoni, 2016

from ufl.finiteelement.finiteelementbase import FiniteElementBase
from ufl.sobolevspace import L2


class BrokenElement(FiniteElementBase):
"""The discontinuous version of an existing Finite Element space."""
def __init__(self, element):
self._element = element
self._repr = "BrokenElement(%s)" % repr(element)

family = "BrokenElement"
cell = element.cell()
Expand All @@ -25,15 +25,22 @@ def __init__(self, element):
FiniteElementBase.__init__(self, family, cell, degree,
quad_scheme, value_shape, reference_value_shape)

def __repr__(self):
return f"BrokenElement({repr(self._element)})"

def mapping(self):
return self._element.mapping()

def sobolev_space(self):
"""Return the underlying Sobolev space."""
return L2

def reconstruct(self, **kwargs):
return BrokenElement(self._element.reconstruct(**kwargs))

def __str__(self):
return "BrokenElement(%s)" % str(self._element)
return f"BrokenElement({repr(self._element)})"

def shortstr(self):
"Format as string for pretty printing."
return "BrokenElement(%s)" % str(self._element.shortstr())
"""Format as string for pretty printing."""
return f"BrokenElement({repr(self._element)})"
7 changes: 2 additions & 5 deletions ufl/finiteelement/elementlist.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
from numpy import asarray

from ufl.log import warning, error
from ufl.sobolevspace import L2, H1, H2, HDiv, HCurl, HEin, HDivDiv
from ufl.sobolevspace import L2, H1, H2, HDiv, HCurl, HEin, HDivDiv, HInf
from ufl.utils.formatting import istr
from ufl.cell import Cell, TensorProductCell

Expand Down Expand Up @@ -137,7 +137,7 @@ def show_elements():
register_element("FacetBubble", "FB", 0, H1, "identity", (2, None), simplices)
register_element("Quadrature", "Quadrature", 0, L2, "identity", (0, None),
any_cell)
register_element("Real", "R", 0, L2, "identity", (0, 0),
register_element("Real", "R", 0, HInf, "identity", (0, 0),
any_cell + ("TensorProductCell",))
register_element("Undefined", "U", 0, L2, "identity", (0, None), any_cell)
register_element("Radau", "Rad", 0, L2, "identity", (0, None), ("interval",))
Expand Down Expand Up @@ -464,9 +464,6 @@ def canonical_element_description(family, cell, order, form_degree):
error('Order "%s" invalid for "%s" finite element.' %
(istr(order), family))

# Override sobolev_space for piecewise constants (TODO: necessary?)
if order == 0:
sobolev_space = L2
if value_rank == 2:
# Tensor valued fundamental elements in HEin have this shape
if gdim is None or tdim is None:
Expand Down
18 changes: 14 additions & 4 deletions ufl/finiteelement/enrichedelement.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,14 +61,11 @@ def __init__(self, *elements):
quad_scheme, value_shape,
reference_value_shape)

# Cache repr string
self._repr = "%s(%s)" % (class_name, ", ".join(repr(e) for e in self._elements))

def mapping(self):
return self._elements[0].mapping()

def sobolev_space(self):
"Return the underlying Sobolev space."
"""Return the underlying Sobolev space."""
elements = [e for e in self._elements]
if all(e.sobolev_space() == elements[0].sobolev_space()
for e in elements):
Expand All @@ -85,6 +82,13 @@ def sobolev_space(self):
sobolev_space, = intersect
return sobolev_space

def variant(self):
try:
variant, = {e.variant() for e in self._elements}
return variant
except ValueError:
return None

def reconstruct(self, **kwargs):
return type(self)(*[e.reconstruct(**kwargs) for e in self._elements])

Expand All @@ -104,6 +108,9 @@ def is_cellwise_constant(self):
element is spatially constant over each cell."""
return all(e.is_cellwise_constant() for e in self._elements)

def __repr__(self):
return "EnrichedElement(" + ", ".join(repr(e) for e in self._elements) + ")"

def __str__(self):
"Format as string for pretty printing."
return "<%s>" % " + ".join(str(e) for e in self._elements)
Expand All @@ -127,6 +134,9 @@ def is_cellwise_constant(self):
element is spatially constant over each cell."""
return False

def __repr__(self):
return "NodalEnrichedElement(" + ", ".join(repr(e) for e in self._elements) + ")"

def __str__(self):
"Format as string for pretty printing."
return "<Nodal enriched element(%s)>" % ", ".join(str(e) for e in self._elements)
Expand Down
13 changes: 8 additions & 5 deletions ufl/finiteelement/finiteelement.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,8 @@
class FiniteElement(FiniteElementBase):
"The basic finite element class for all simple finite elements."
# TODO: Move these to base?
__slots__ = ("_short_name",
"_sobolev_space",
"_mapping",
"_variant")
__slots__ = ("_short_name", "_sobolev_space",
"_mapping", "_variant", "_repr")

def __new__(cls,
family,
Expand Down Expand Up @@ -188,11 +186,16 @@ def __init__(self,
repr(self.family()), repr(self.cell()), repr(self.degree()), quad_str, var_str)
assert '"' not in self._repr

def __repr__(self):
"""Format as string for evaluation as Python object."""
return self._repr

def mapping(self):
"""Return the mapping type for this element ."""
return self._mapping

def sobolev_space(self):
"Return the underlying Sobolev space."
"""Return the underlying Sobolev space."""
return self._sobolev_space

def variant(self):
Expand Down
35 changes: 17 additions & 18 deletions ufl/finiteelement/finiteelementbase.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,24 +15,19 @@
from ufl.utils.dicts import EmptyDict
from ufl.log import error
from ufl.cell import AbstractCell, as_cell
from abc import ABC, abstractmethod


class FiniteElementBase(object):
class FiniteElementBase(ABC):
"Base class for all finite elements."
__slots__ = ("_family",
"_cell",
"_degree",
"_quad_scheme",
"_value_shape",
"_reference_value_shape",
"_repr",
"__weakref__")
__slots__ = ("_family", "_cell", "_degree", "_quad_scheme",
"_value_shape", "_reference_value_shape", "__weakref__")

# TODO: Not all these should be in the base class! In particular
# family, degree, and quad_scheme do not belong here.
def __init__(self, family, cell, degree, quad_scheme, value_shape,
reference_value_shape):
"Initialize basic finite element data."
"""Initialize basic finite element data."""
if not isinstance(family, str):
error("Invalid family type.")
if not (degree is None or isinstance(degree, (int, tuple))):
Expand All @@ -54,12 +49,20 @@ def __init__(self, family, cell, degree, quad_scheme, value_shape,
self._reference_value_shape = reference_value_shape
self._quad_scheme = quad_scheme

@abstractmethod
def __repr__(self):
"""Format as string for evaluation as Python object.
"""Format as string for evaluation as Python object."""
pass

NB! Assumes subclass has assigned its repr string to self._repr.
"""
return self._repr
@abstractmethod
def sobolev_space(self):
"""Return the underlying Sobolev space."""
pass

@abstractmethod
def mapping(self):
"""Return the mapping type for this element."""
pass

def _ufl_hash_data_(self):
return repr(self)
Expand Down Expand Up @@ -101,10 +104,6 @@ def quadrature_scheme(self):
"Return quadrature scheme of finite element."
return self._quad_scheme

def mapping(self):
"Not implemented."
error("Missing implementation of mapping().")

def cell(self):
"Return cell of finite element."
return self._cell
Expand Down
Loading

0 comments on commit 0c592ec

Please sign in to comment.