from enum import Enum
from typing import Iterable
import basix
import dolfinx
import numpy as np
QUADRATURE_FAMILY = 100
[docs]
def default_markers() -> dict[str, list[int]]:
"""
Default markers for the mesh boundaries
"""
return dict(base=[10], rv=[20], lv=[30], epi=[40])
[docs]
def parse_element(space_string: str, mesh: dolfinx.mesh.Mesh, dim: int) -> basix.ufl._ElementBase:
"""
Parse a string representation of a basix element family
"""
family_str, degree_str = space_string.split("_")
kwargs = {"degree": int(degree_str), "cell": mesh.ufl_cell().cellname()}
if dim > 1:
if family_str in ["Quadrature", "Q", "Quad"]:
kwargs["value_shape"] = (dim,)
else:
kwargs["shape"] = (dim,)
if family_str in ["Lagrange", "P", "CG"]:
el = basix.ufl.element(family=basix.ElementFamily.P, discontinuous=False, **kwargs)
elif family_str in ["Discontinuous Lagrange", "DG", "dP"]:
el = basix.ufl.element(family=basix.ElementFamily.P, discontinuous=True, **kwargs)
elif family_str in ["Quadrature", "Q", "Quad"]:
el = basix.ufl.quadrature_element(scheme="default", **kwargs)
else:
families = list(basix.ElementFamily.__members__.keys())
msg = f"Unknown element family: {family_str}, available families: {families}"
raise ValueError(msg)
return el
[docs]
def space_from_string(
space_string: str, mesh: dolfinx.mesh.Mesh, dim: int
) -> dolfinx.fem.functionspace:
"""
Constructed a finite elements space from a string
representation of the space
Arguments
---------
space_string : str
A string on the form {family}_{degree} which
determines the space. Example 'Lagrange_1'.
mesh : df.Mesh
The mesh
dim : int
1 for scalar space, 3 for vector space.
"""
el = parse_element(space_string, mesh, dim)
return dolfinx.fem.functionspace(mesh, el)
def element2array(el: basix.ufl._BlockedElement) -> np.ndarray:
try:
el = el.basix_element
family = int(el.family)
cell_type = int(el.cell_type)
degree = int(el.degree)
discontinuous = int(el.discontinuous)
except NotImplementedError:
assert el.family_name == "quadrature"
family = QUADRATURE_FAMILY
cell_type = int(el.cell_type)
degree = int(el.degree)
discontinuous = int(el.discontinuous)
return np.array(
[family, cell_type, degree, discontinuous],
dtype=np.uint8,
)
def number2Enum(num: int, enum: Iterable) -> Enum:
for e in enum:
if int(e) == num:
return e
raise ValueError(f"Invalid value {num} for enum {enum}")
def array2element(arr: np.ndarray) -> basix.finite_element.FiniteElement:
cell_type = number2Enum(arr[1], basix.CellType)
degree = int(arr[2])
discontinuous = bool(arr[3])
if arr[0] == QUADRATURE_FAMILY:
return basix.ufl.quadrature_element(
scheme="default",
cell=cell_type,
degree=degree,
value_shape=(3,),
)
else:
family = number2Enum(arr[0], basix.ElementFamily)
# TODO: Shape is hardcoded to (3,) for now, but this should also be stored
return basix.ufl.element(
family=family,
cell=cell_type,
degree=degree,
discontinuous=discontinuous,
shape=(3,),
)