Skip to content

Go back to LBYL programming style (instead of EAFP) and several mypy fixes in fem.petsc and la.petsc #3790

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

Merged
merged 25 commits into from
Jul 19, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
a426b0c
Go back to isinstance
francesco-ballarin Jul 9, 2025
7e85c10
ruff
francesco-ballarin Jul 9, 2025
37e1053
Fix instance check
francesco-ballarin Jul 9, 2025
b745485
ruff
francesco-ballarin Jul 9, 2025
8ad65f7
Apply to la petsc as well
francesco-ballarin Jul 9, 2025
d13b760
handle trivial case
francesco-ballarin Jul 9, 2025
6ffc628
Apply suggestions from code review
francesco-ballarin Jul 9, 2025
4ca0f45
Merge branch 'main' into francesco/isinstance-2
francesco-ballarin Jul 10, 2025
9060b33
mypy fixes: PETSc
francesco-ballarin Jul 11, 2025
0380e9b
mypy fixes part 2
francesco-ballarin Jul 11, 2025
a86e8a0
Now can apply suggestion by Paul
francesco-ballarin Jul 11, 2025
fa3b16e
Tweak singledispatch register
francesco-ballarin Jul 11, 2025
fd422da
A few more mypy fixes
francesco-ballarin Jul 11, 2025
81729e2
A few more mypy fixes
francesco-ballarin Jul 11, 2025
07087ce
ruff
francesco-ballarin Jul 11, 2025
acf502e
Revert "A few more mypy fixes"
francesco-ballarin Jul 11, 2025
367e8ad
One more ignore
francesco-ballarin Jul 11, 2025
0b8ac67
Further ignore
francesco-ballarin Jul 11, 2025
2e21235
Apply suggestions from code review
francesco-ballarin Jul 16, 2025
71dd0e7
Fix imports
francesco-ballarin Jul 16, 2025
6a954ec
Merge branch 'main' into francesco/isinstance-2
francesco-ballarin Jul 16, 2025
7e613e1
Update python/dolfinx/la/petsc.py
francesco-ballarin Jul 16, 2025
64e19f0
Merge branch 'main' into francesco/isinstance-2
francesco-ballarin Jul 18, 2025
56326dd
Fix conflict
francesco-ballarin Jul 18, 2025
9120be6
Merge branch 'main' into francesco/isinstance-2
francesco-ballarin Jul 19, 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
98 changes: 55 additions & 43 deletions python/dolfinx/fem/assemble.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,10 @@

from __future__ import annotations

import collections
import functools
import typing
import warnings
from collections.abc import Iterable
from collections.abc import Sequence

import numpy as np
import numpy.typing as npt
Expand All @@ -28,7 +27,7 @@

def pack_constants(
form: typing.Union[Form, typing.Sequence[Form]],
) -> typing.Union[np.ndarray, typing.Sequence[np.ndarray]]:
) -> typing.Union[npt.NDArray, typing.Sequence[npt.NDArray]]:
"""Pack form constants for use in assembly.

Pack the 'constants' that appear in forms. The packed constants can
Expand All @@ -50,7 +49,7 @@ def pack_constants(
def _pack(form):
if form is None:
return None
elif isinstance(form, collections.abc.Iterable):
elif isinstance(form, Sequence):
return list(map(lambda sub_form: _pack(sub_form), form))
else:
return _pack_constants(form._cpp_object)
Expand Down Expand Up @@ -84,7 +83,7 @@ def pack_coefficients(
def _pack(form):
if form is None:
return {}
elif isinstance(form, collections.abc.Iterable):
elif isinstance(form, Sequence):
return list(map(lambda sub_form: _pack(sub_form), form))
else:
return _pack_coefficients(form._cpp_object)
Expand All @@ -106,7 +105,7 @@ def create_vector(L: Form) -> la.Vector:
"""
# Can just take the first dofmap here, since all dof maps have the same
# index map in mixed-topology meshes
dofmap = L.function_spaces[0].dofmaps(0) # type: ignore
dofmap = L.function_spaces[0].dofmaps(0) # type: ignore[attr-defined]
return la.vector(dofmap.index_map, dofmap.index_map_bs, dtype=L.dtype)


Expand All @@ -133,7 +132,11 @@ def create_matrix(a: Form, block_mode: typing.Optional[la.BlockMode] = None) ->
# -- Scalar assembly ------------------------------------------------------


def assemble_scalar(M: Form, constants: typing.Optional[np.ndarray] = None, coeffs=None):
def assemble_scalar(
M: Form,
constants: typing.Optional[npt.NDArray] = None,
coeffs: typing.Optional[dict[tuple[IntegralType, int], npt.NDArray]] = None,
):
"""Assemble functional. The returned value is local and not
accumulated across processes.

Expand All @@ -155,22 +158,28 @@ def assemble_scalar(M: Form, constants: typing.Optional[np.ndarray] = None, coef
To compute the functional value on the whole domain, the output
of this function is typically summed across all MPI ranks.
"""
constants = pack_constants(M) if constants is None else constants # type: ignore
coeffs = coeffs or pack_coefficients(M)
constants = pack_constants(M) if constants is None else constants # type: ignore[assignment]
coeffs = pack_coefficients(M) if coeffs is None else constants # type: ignore[assignment]
return _cpp.fem.assemble_scalar(M._cpp_object, constants, coeffs)


# -- Vector assembly ------------------------------------------------------


@functools.singledispatch
def assemble_vector(L: typing.Any, constants: typing.Optional[np.ndarray] = None, coeffs=None):
def assemble_vector(
L: typing.Any,
constants: typing.Optional[npt.NDArray] = None,
coeffs: typing.Optional[dict[tuple[IntegralType, int], npt.NDArray]] = None,
):
return _assemble_vector_form(L, constants, coeffs)


@assemble_vector.register(Form)
@assemble_vector.register
def _assemble_vector_form(
L: Form, constants: typing.Optional[np.ndarray] = None, coeffs=None
L: Form,
constants: typing.Optional[npt.NDArray] = None,
coeffs: typing.Optional[dict[tuple[IntegralType, int], npt.NDArray]] = None,
) -> la.Vector:
"""Assemble linear form into a new Vector.

Expand All @@ -197,15 +206,18 @@ def _assemble_vector_form(
"""
b = create_vector(L)
b.array[:] = 0
constants = constants or pack_constants(L) # type: ignore
coeffs = coeffs or pack_coefficients(L)
constants = pack_constants(L) if constants is None else constants # type: ignore[assignment]
coeffs = pack_coefficients(L) if coeffs is None else coeffs # type: ignore[assignment]
_assemble_vector_array(b.array, L, constants, coeffs)
return b


@assemble_vector.register(np.ndarray)
def _assemble_vector_array(
b: np.ndarray, L: Form, constants: typing.Optional[np.ndarray] = None, coeffs=None
b: npt.NDArray,
L: Form,
constants: typing.Optional[npt.NDArray] = None,
coeffs: typing.Optional[dict[tuple[IntegralType, int], npt.NDArray]] = None,
):
"""Assemble linear form into an existing array.

Expand All @@ -229,8 +241,8 @@ def _assemble_vector_array(
:func:`dolfinx.la.Vector.scatter_reverse` on the return vector
can accumulate ghost contributions.
"""
constants = pack_constants(L) if constants is None else constants # type: ignore
coeffs = pack_coefficients(L) if coeffs is None else coeffs
constants = pack_constants(L) if constants is None else constants # type: ignore[assignment]
coeffs = pack_coefficients(L) if coeffs is None else coeffs # type: ignore[assignment]
_cpp.fem.assemble_vector(b, L._cpp_object, constants, coeffs)
return b

Expand All @@ -241,10 +253,10 @@ def _assemble_vector_array(
@functools.singledispatch
def assemble_matrix(
a: typing.Any,
bcs: typing.Optional[list[DirichletBC]] = None,
diagonal: float = 1.0,
constants: typing.Optional[np.ndarray] = None,
coeffs=None,
bcs: typing.Optional[Sequence[DirichletBC]] = None,
diag: float = 1.0,
constants: typing.Optional[npt.NDArray] = None,
coeffs: typing.Optional[dict[tuple[IntegralType, int], npt.NDArray]] = None,
block_mode: typing.Optional[la.BlockMode] = None,
):
"""Assemble bilinear form into a matrix.
Expand Down Expand Up @@ -274,18 +286,18 @@ def assemble_matrix(
"""
bcs = [] if bcs is None else bcs
A: la.MatrixCSR = create_matrix(a, block_mode)
_assemble_matrix_csr(A, a, bcs, diagonal, constants, coeffs)
_assemble_matrix_csr(A, a, bcs, diag, constants, coeffs)
return A


@assemble_matrix.register(la.MatrixCSR)
@assemble_matrix.register
def _assemble_matrix_csr(
A: la.MatrixCSR,
a: Form,
bcs: typing.Optional[list[DirichletBC]] = None,
diagonal: float = 1.0,
constants: typing.Optional[np.ndarray] = None,
coeffs=None,
bcs: typing.Optional[Sequence[DirichletBC]] = None,
diag: float = 1.0,
constants: typing.Optional[npt.NDArray] = None,
coeffs: typing.Optional[dict[tuple[IntegralType, int], npt.NDArray]] = None,
) -> la.MatrixCSR:
"""Assemble bilinear form into a matrix.

Expand All @@ -311,28 +323,28 @@ def _assemble_matrix_csr(
accumulated.
"""
bcs = [] if bcs is None else [bc._cpp_object for bc in bcs]
constants = pack_constants(a) if constants is None else constants # type: ignore
coeffs = pack_coefficients(a) if coeffs is None else coeffs
constants = pack_constants(a) if constants is None else constants # type: ignore[assignment]
coeffs = pack_coefficients(a) if coeffs is None else coeffs # type: ignore[assignment]
_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._cpp_object, a.function_spaces[0], bcs, diagonal)
_cpp.fem.insert_diagonal(A._cpp_object, a.function_spaces[0], bcs, diag)
return A


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


def apply_lifting(
b: np.ndarray,
a: Iterable[Form],
bcs: Iterable[Iterable[DirichletBC]],
x0: typing.Optional[Iterable[np.ndarray]] = None,
b: npt.NDArray,
a: Sequence[Form],
bcs: Sequence[Sequence[DirichletBC]],
x0: typing.Optional[Sequence[npt.NDArray]] = None,
alpha: float = 1,
constants: typing.Optional[Iterable[np.ndarray]] = None,
coeffs=None,
constants: typing.Optional[npt.NDArray] = None,
coeffs: typing.Optional[dict[tuple[IntegralType, int], npt.NDArray]] = None,
) -> None:
"""Modify right-hand side vector ``b`` for lifting of Dirichlet
boundary conditions.
Expand Down Expand Up @@ -430,12 +442,12 @@ def apply_lifting(
"""
x0 = [] if x0 is None else x0
constants = (
[pack_constants(form) if form is not None else np.array([], dtype=b.dtype) for form in a] # type: ignore
[pack_constants(form) if form is not None else np.array([], dtype=b.dtype) for form in a] # type: ignore[assignment]
if constants is None
else constants
)
coeffs = (
[{} if form is None else pack_coefficients(form) for form in a]
[{} if form is None else pack_coefficients(form) for form in a] # type: ignore[assignment]
if coeffs is None
else coeffs
)
Expand All @@ -445,10 +457,10 @@ def apply_lifting(


def set_bc(
b: np.ndarray,
bcs: list[DirichletBC],
x0: typing.Optional[np.ndarray] = None,
scale: float = 1,
b: npt.NDArray,
bcs: Sequence[DirichletBC],
x0: typing.Optional[npt.NDArray] = None,
alpha: float = 1,
) -> None:
"""Insert boundary condition values into vector.

Expand All @@ -464,4 +476,4 @@ def set_bc(
DeprecationWarning,
)
for bc in bcs:
bc.set(b, x0, scale)
bc.set(b, x0, alpha)
32 changes: 15 additions & 17 deletions python/dolfinx/fem/bcs.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,16 +11,14 @@

import numbers
import typing

import numpy.typing as npt

if typing.TYPE_CHECKING:
from dolfinx.fem.function import Constant, Function
from collections.abc import Iterable

import numpy as np
import numpy.typing as npt

import dolfinx
from dolfinx import cpp as _cpp
from dolfinx.fem.function import Constant, Function


def locate_dofs_geometrical(
Expand Down Expand Up @@ -48,11 +46,11 @@ def locate_dofs_geometrical(
Returned degree-of-freedom indices are unique and ordered by the
first column.
"""
try:
return _cpp.fem.locate_dofs_geometrical(V._cpp_object, marker) # type: ignore
except AttributeError:
_V = [space._cpp_object for space in V] # type: ignore
return _cpp.fem.locate_dofs_geometrical(_V, marker)
if not isinstance(V, Iterable):
return _cpp.fem.locate_dofs_geometrical(V._cpp_object, marker)

_V = [space._cpp_object for space in V]
return _cpp.fem.locate_dofs_geometrical(_V, marker)


def locate_dofs_topological(
Expand Down Expand Up @@ -84,11 +82,11 @@ def locate_dofs_topological(
first column.
"""
_entities = np.asarray(entities, dtype=np.int32)
try:
return _cpp.fem.locate_dofs_topological(V._cpp_object, entity_dim, _entities, remote) # type: ignore
except AttributeError:
_V = [space._cpp_object for space in V] # type: ignore
return _cpp.fem.locate_dofs_topological(_V, entity_dim, _entities, remote)
if not isinstance(V, Iterable):
return _cpp.fem.locate_dofs_topological(V._cpp_object, entity_dim, _entities, remote)

_V = [space._cpp_object for space in V]
return _cpp.fem.locate_dofs_topological(_V, entity_dim, _entities, remote)


class DirichletBC:
Expand Down Expand Up @@ -223,9 +221,9 @@ def dirichletbc(
_value = value
else:
try:
_value = value._cpp_object # type: ignore
_value = value._cpp_object
except AttributeError:
_value = value # type: ignore
_value = value # type: ignore[assignment]

if V is not None:
try:
Expand Down
Loading
Loading