Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Remove Python 'meta classes' #2683

Merged
merged 20 commits into from
Jun 27, 2023
Merged
5 changes: 2 additions & 3 deletions cpp/dolfinx/la/MatrixCSR.h
Expand Up @@ -303,14 +303,13 @@ class MatrixCSR
void finalize_begin();

/// @brief End transfer of ghost row data to owning ranks.
/// @note Must be preceded by MatrixCSR::finalize_begin()
/// @note Must be preceded by MatrixCSR::finalize_begin().
/// @note Matrix data received from other processes will be
/// accumulated into locally owned rows, and ghost rows will be
/// zeroed.
void finalize_end();

/// @brief Compute the Frobenius norm squared across all processes
/// @note This does not include ghost rows.
/// @brief Compute the Frobenius norm squared across all processes.
/// @note MPI Collective
double squared_norm() const;

Expand Down
2 changes: 1 addition & 1 deletion python/demo/demo_biharmonic.py
Expand Up @@ -163,7 +163,7 @@
dofs = fem.locate_dofs_topological(V=V, entity_dim=1, entities=facets)

# and use {py:func}`dirichletbc <dolfinx.fem.dirichletbc>` to create a
# {py:class}`DirichletBCMetaClass <dolfinx.fem.DirichletBCMetaClass>`
# {py:class}`DirichletBC <dolfinx.fem.DirichletBC>`
# class that represents the boundary condition. In this case, we impose
# Dirichlet boundary conditions with value $0$ on the entire boundary
# $\partial\Omega$.
Expand Down
4 changes: 2 additions & 2 deletions python/demo/demo_poisson.py
Expand Up @@ -112,8 +112,8 @@
dofs = fem.locate_dofs_topological(V=V, entity_dim=1, entities=facets)

# and use {py:func}`dirichletbc <dolfinx.fem.dirichletbc>` to create a
# {py:class}`DirichletBCMetaClass <dolfinx.fem.DirichletBCMetaClass>`
# class that represents the boundary condition:
# {py:class}`DirichletBC <dolfinx.fem.DirichletBC>` class that
# represents the boundary condition:

bc = fem.dirichletbc(value=ScalarType(0), dofs=dofs, V=V)

Expand Down
16 changes: 8 additions & 8 deletions python/demo/demo_static-condensation.py
Expand Up @@ -30,8 +30,8 @@
from basix.ufl import element
from dolfinx.cpp.fem import (Form_complex64, Form_complex128, Form_float32,
Form_float64)
from dolfinx.fem import (Function, FunctionSpace, IntegralType, dirichletbc,
form, locate_dofs_topological)
from dolfinx.fem import (Form, Function, FunctionSpace, IntegralType,
dirichletbc, form, locate_dofs_topological)
from dolfinx.fem.petsc import (apply_lifting, assemble_matrix, assemble_vector,
set_bc)
from dolfinx.io import XDMFFile
Expand Down Expand Up @@ -155,21 +155,21 @@ def tabulate_condensed_tensor_A(A_, w_, c_, coords_, entity_local_index, permuta


# Prepare a Form with a condensed tabulation kernel
Form = None
formtype = None
if PETSc.ScalarType == np.float32:
Form = Form_float32
formtype = Form_float32
elif PETSc.ScalarType == np.float64:
Form = Form_float64
formtype = Form_float64
elif PETSc.ScalarType == np.complex64:
Form = Form_complex64
formtype = Form_complex64
elif PETSc.ScalarType == np.complex128:
Form = Form_complex128
formtype = Form_complex128
else:
raise RuntimeError(f"Unsupported PETSc ScalarType '{PETSc.ScalarType }'.")

cells = range(msh.topology.index_map(msh.topology.dim).size_local)
integrals = {IntegralType.cell: [(-1, tabulate_condensed_tensor_A.address, cells)]}
a_cond = Form([U._cpp_object, U._cpp_object], integrals, [], [], False, None)
a_cond = Form(formtype([U._cpp_object, U._cpp_object], integrals, [], [], False, None))

A_cond = assemble_matrix(a_cond, bcs=[bc])
A_cond.assemble()
Expand Down
10 changes: 5 additions & 5 deletions python/dolfinx/fem/__init__.py
Expand Up @@ -12,16 +12,16 @@
from dolfinx.fem import petsc
from dolfinx.fem.assemble import (apply_lifting, assemble_matrix,
assemble_scalar, assemble_vector, set_bc)
from dolfinx.fem.bcs import (DirichletBCMetaClass, bcs_by_block, dirichletbc,
from dolfinx.fem.bcs import (DirichletBC, bcs_by_block, dirichletbc,
locate_dofs_geometrical, locate_dofs_topological)
from dolfinx.fem.dofmap import DofMap
from dolfinx.fem.forms import FormMetaClass, extract_function_spaces, form
from dolfinx.fem.forms import Form, extract_function_spaces, form
from dolfinx.fem.function import (Constant, Expression, Function,
FunctionSpace, TensorFunctionSpace,
VectorFunctionSpace)


def create_sparsity_pattern(a: FormMetaClass):
def create_sparsity_pattern(a: Form):
"""Create a sparsity pattern from a bilinear form.

Args:
Expand All @@ -31,15 +31,15 @@ def create_sparsity_pattern(a: FormMetaClass):
Sparsity pattern for the form ``a``.

"""
return _create_sparsity_pattern(a)
return _create_sparsity_pattern(a._cpp_object)


__all__ = [
"Constant", "Expression", "Function",
"FunctionSpace", "TensorFunctionSpace",
"VectorFunctionSpace", "create_sparsity_pattern",
"assemble_scalar", "assemble_matrix", "assemble_vector", "apply_lifting", "set_bc",
"DirichletBCMetaClass", "dirichletbc", "bcs_by_block", "DofMap", "FormMetaClass",
"DirichletBC", "dirichletbc", "bcs_by_block", "DofMap", "Form",
"form", "IntegralType",
"locate_dofs_geometrical", "locate_dofs_topological",
"extract_function_spaces", "petsc", "create_nonmatching_meshes_interpolation_data"]
93 changes: 46 additions & 47 deletions python/dolfinx/fem/assemble.py
Expand Up @@ -18,13 +18,12 @@
from dolfinx import la
from dolfinx.cpp.fem import pack_coefficients as _pack_coefficients
from dolfinx.cpp.fem import pack_constants as _pack_constants
from dolfinx.fem.bcs import DirichletBCMetaClass
from dolfinx.fem.forms import FormMetaClass, form_types
from dolfinx.fem.bcs import DirichletBC
from dolfinx.fem.forms import Form


def pack_constants(form: typing.Union[FormMetaClass,
typing.Sequence[FormMetaClass]]) -> typing.Union[np.ndarray,
typing.Sequence[np.ndarray]]:
def pack_constants(form: typing.Union[Form, typing.Sequence[Form]]) -> typing.Union[np.ndarray,
typing.Sequence[np.ndarray]]:
"""Compute form constants.

Pack the `constants` that appear in forms. The packed constants can
Expand Down Expand Up @@ -53,7 +52,7 @@ def _pack(form):
return _pack(form)


def pack_coefficients(form: typing.Union[FormMetaClass, typing.Sequence[FormMetaClass]]):
def pack_coefficients(form: typing.Union[Form, typing.Sequence[Form]]):
"""Compute form coefficients.

Pack the `coefficients` that appear in forms. The packed
Expand Down Expand Up @@ -84,20 +83,18 @@ def _pack(form):
# -- Vector and matrix instantiation -----------------------------------------


def create_vector(L: FormMetaClass) -> la.VectorMetaClass:
def create_vector(L: Form) -> la.Vector:
"""Create a Vector that is compatible with a given linear form"""
dofmap = L.function_spaces[0].dofmap
return la.vector(dofmap.index_map, dofmap.index_map_bs, dtype=L.dtype)


def create_matrix(a: FormMetaClass, block_mode: typing.Optional[la.BlockMode] = None) -> la.MatrixCSRMetaClass:
def create_matrix(a: Form, block_mode: typing.Optional[la.BlockMode] = None) -> la.MatrixCSR:
"""Create a sparse matrix that is compatible with a given bilinear form.

Args:
a: Bilinear form to assemble.
block_mode: Block mode of the CSR matrix. If ``None``, default
is used.

Returns:
Assembled sparse matrix.

Expand All @@ -112,7 +109,7 @@ def create_matrix(a: FormMetaClass, block_mode: typing.Optional[la.BlockMode] =

# -- Scalar assembly ---------------------------------------------------------

def assemble_scalar(M: FormMetaClass, constants=None, coeffs=None):
def assemble_scalar(M: Form, constants=None, coeffs=None):
"""Assemble functional. The returned value is local and not
accumulated across processes.

Expand All @@ -135,9 +132,9 @@ def assemble_scalar(M: FormMetaClass, constants=None, coeffs=None):
of this function is typically summed across all MPI ranks.

"""
constants = constants or _pack_constants(M)
coeffs = coeffs or _pack_coefficients(M)
return _cpp.fem.assemble_scalar(M, constants, coeffs)
constants = constants or _pack_constants(M._cpp_object)
coeffs = coeffs or _pack_coefficients(M._cpp_object)
return _cpp.fem.assemble_scalar(M._cpp_object, constants, coeffs)


# -- Vector assembly ---------------------------------------------------------
Expand All @@ -148,8 +145,8 @@ def assemble_vector(L: typing.Any,
return _assemble_vector_form(L, constants, coeffs)


@assemble_vector.register(FormMetaClass)
def _assemble_vector_form(L: form_types, constants=None, coeffs=None) -> la.VectorMetaClass:
@assemble_vector.register(Form)
def _assemble_vector_form(L: Form, constants=None, coeffs=None) -> la.Vector:
"""Assemble linear form into a new Vector.

Args:
Expand All @@ -170,20 +167,20 @@ def _assemble_vector_form(L: form_types, constants=None, coeffs=None) -> la.Vect
Note:
The returned vector is not finalised, i.e. ghost values are not
accumulated on the owning processes. Calling
:func:`dolfinx.la.VectorMetaClass.scatter_reverse` on the
:func:`dolfinx.la.Vector.scatter_reverse` on the
return vector can accumulate ghost contributions.

"""
b = create_vector(L)
b.array[:] = 0
constants = constants or _pack_constants(L)
coeffs = coeffs or _pack_coefficients(L)
constants = constants or _pack_constants(L._cpp_object)
coeffs = coeffs or _pack_coefficients(L._cpp_object)
_assemble_vector_array(b.array, L, constants, coeffs)
return b


@assemble_vector.register(np.ndarray)
def _assemble_vector_array(b: np.ndarray, L: FormMetaClass, constants=None, coeffs=None):
def _assemble_vector_array(b: np.ndarray, L: Form, constants=None, coeffs=None):
"""Assemble linear form into a new Vector.

Args:
Expand All @@ -203,25 +200,24 @@ def _assemble_vector_array(b: np.ndarray, L: FormMetaClass, constants=None, coef
Note:
The returned vector is not finalised, i.e. ghost values are not
accumulated on the owning processes. Calling
:func:`dolfinx.la.VectorMetaClass.scatter_reverse` on the
:func:`dolfinx.la.Vector.scatter_reverse` on the
return vector can accumulate ghost contributions.

"""

constants = _pack_constants(L) if constants is None else constants
coeffs = _pack_coefficients(L) if coeffs is None else coeffs
_cpp.fem.assemble_vector(b, L, constants, coeffs)
constants = _pack_constants(L._cpp_object) if constants is None else constants
coeffs = _pack_coefficients(L._cpp_object) if coeffs is None else coeffs
_cpp.fem.assemble_vector(b, L._cpp_object, constants, coeffs)
return b

# -- Matrix assembly ---------------------------------------------------------


@functools.singledispatch
def assemble_matrix(a: typing.Any,
bcs: typing.Optional[typing.List[DirichletBCMetaClass]] = None,
def assemble_matrix(a: typing.Any, bcs: typing.Optional[typing.List[DirichletBC]] = None,
diagonal: float = 1.0, constants=None, coeffs=None,
block_mode: typing.Optional[la.BlockMode] = None):
"""Create matrix representation (assemble) of a bilinear form.
"""Assemble bilinear form into a matrix.

Args:
a: The bilinear form assemble.
Expand All @@ -233,7 +229,7 @@ def assemble_matrix(a: typing.Any,
any required constants will be computed.
coeffs: Coefficients that appear in the form. If not provided,
any required coefficients will be computed.
block_mode: Block size mode for the returned space matrix. If
block_mode: Block size mode for the returned space matrix. If
``None``, default is used.

Returns:
Expand All @@ -245,27 +241,27 @@ def assemble_matrix(a: typing.Any,

"""
bcs = [] if bcs is None else bcs
A: la.MatrixCSRMetaClass = create_matrix(a, block_mode)
A: la.MatrixCSR = create_matrix(a, block_mode)
_assemble_matrix_csr(A, a, bcs, diagonal, constants, coeffs)
return A


@assemble_matrix.register
def _assemble_matrix_csr(A: la.MatrixCSRMetaClass, a: form_types,
bcs: typing.Optional[typing.List[DirichletBCMetaClass]] = None,
diagonal: float = 1.0, constants=None, coeffs=None) -> la.MatrixCSRMetaClass:
"""Assemble a bilinear form into a matrix.
def _assemble_matrix_csr(A: la.MatrixCSR, a: Form, bcs: typing.Optional[typing.List[DirichletBC]] = None,
diagonal: float = 1.0, constants=None, coeffs=None) -> la.MatrixCSR:
"""Assemble bilinear form into a matrix.

Args:
Args:
A: The matrix to assemble into. It must have been initialized
with the correct sparsity pattern.
a: The bilinear form assemble.
bcs: Boundary conditions that affect the assembled matrix.
Degrees-of-freedom constrained by a boundary condition will
have their rows/columns zeroed and the value ``diagonal``
set on on the matrix diagonal.
set on on
constants: Constants that appear in the form. If not provided,
any required constants will be computed.
the matrix diagonal.
coeffs: Coefficients that appear in the form. If not provided,
any required coefficients will be computed.

Expand All @@ -274,23 +270,23 @@ def _assemble_matrix_csr(A: la.MatrixCSRMetaClass, a: form_types,
accumulated.

"""
bcs = [] if bcs is None else bcs
constants = _pack_constants(a) if constants is None else constants
coeffs = _pack_coefficients(a) if coeffs is None else coeffs
_cpp.fem.assemble_matrix(A, a, constants, coeffs, bcs)
bcs = [] if bcs is None else [bc._cpp_object for bc in bcs]
constants = _pack_constants(a._cpp_object) if constants is None else constants
coeffs = _pack_coefficients(a._cpp_object) if coeffs is None else coeffs
_cpp.fem.assemble_matrix(A._cpp_object, a._cpp_object, constants, coeffs, bcs)

# If matrix is a 'diagonal'block, set diagonal entry for constrained
# dofs
if a.function_spaces[0] is a.function_spaces[1]:
_cpp.fem.insert_diagonal(A, a.function_spaces[0], bcs, diagonal)
_cpp.fem.insert_diagonal(A._cpp_object, a.function_spaces[0], bcs, diagonal)
return A


# -- Modifiers for Dirichlet conditions ---------------------------------------


def apply_lifting(b: np.ndarray, a: typing.List[FormMetaClass],
bcs: typing.List[typing.List[DirichletBCMetaClass]],
def apply_lifting(b: np.ndarray, a: typing.List[Form],
bcs: typing.List[typing.List[DirichletBC]],
x0: typing.Optional[typing.List[np.ndarray]] = None,
scale: float = 1.0, constants=None, coeffs=None) -> None:
"""Modify RHS vector b for lifting of Dirichlet boundary conditions.
Expand All @@ -313,17 +309,20 @@ def apply_lifting(b: np.ndarray, a: typing.List[FormMetaClass],

"""
x0 = [] if x0 is None else x0
constants = [form and _pack_constants(form) for form in a] if constants is None else constants
coeffs = [{} if form is None else _pack_coefficients(form) for form in a] if coeffs is None else coeffs
_cpp.fem.apply_lifting(b, a, constants, coeffs, bcs, x0, scale)
constants = [form and _pack_constants(form._cpp_object) for form in a] if constants is None else constants
coeffs = [{} if form is None else _pack_coefficients(form._cpp_object) for form in a] if coeffs is None else coeffs
_a = [None if form is None else form._cpp_object for form in a]
_bcs = [[bc._cpp_object for bc in bcs0] for bcs0 in bcs]
_cpp.fem.apply_lifting(b, _a, constants, coeffs, _bcs, x0, scale)


def set_bc(b: np.ndarray, bcs: typing.List[DirichletBCMetaClass],
def set_bc(b: np.ndarray, bcs: typing.List[DirichletBC],
x0: typing.Optional[np.ndarray] = None, scale: float = 1.0) -> None:
"""Insert boundary condition values into vector. Only local (owned)
entries are set, hence communication after calling this function is
not required unless ghost entries need to be updated to the boundary
condition value.

"""
_cpp.fem.set_bc(b, bcs, x0, scale)
_bcs = [bc._cpp_object for bc in bcs]
_cpp.fem.set_bc(b, _bcs, x0, scale)