Coverage for python/src/dolfinx_mpc/container.py: 74%
61 statements
« prev ^ index » next coverage.py v7.16.2, created at 2026-10-07 09:58 +0000
« prev ^ index » next coverage.py v7.16.2, created at 2026-10-07 09:58 +0000
1import warnings
2from typing import Callable, Optional, Union
4import dolfinx
5import numpy
6import numpy.typing as npt
8import dolfinx_mpc.cpp.mpc
10_mpc_data_classes = Union[
11 dolfinx_mpc.cpp.mpc.mpc_data_double,
12 dolfinx_mpc.cpp.mpc.mpc_data_float,
13 dolfinx_mpc.cpp.mpc.mpc_data_complex_double,
14 dolfinx_mpc.cpp.mpc.mpc_data_complex_float,
15]
16_float_array_types = Union[
17 npt.NDArray[numpy.float32],
18 npt.NDArray[numpy.float64],
19 npt.NDArray[numpy.complex64],
20 npt.NDArray[numpy.complex128],
21]
23_mpc_classes = Union[
24 dolfinx_mpc.cpp.mpc.MultiPointConstraint_double,
25 dolfinx_mpc.cpp.mpc.MultiPointConstraint_float,
26 dolfinx_mpc.cpp.mpc.MultiPointConstraint_complex_double,
27 dolfinx_mpc.cpp.mpc.MultiPointConstraint_complex_float,
28]
29_float_classes = Union[numpy.float32, numpy.float64, numpy.complex128, numpy.complex64]
31_type_names = {
32 numpy.float32: "float",
33 numpy.float64: "double",
34 numpy.complex64: "complex_float",
35 numpy.complex128: "complex_double",
36}
39class _Unset:
40 """The default of a deprecated argument, to tell whether it was passed."""
42 def __repr__(self) -> str:
43 return "<unset>"
46_UNSET = _Unset()
49def _deprecated(old: str, new: str, stacklevel: int = 3):
50 """Warn that the argument `old` is deprecated in favour of `new`, at the caller of the public
51 function `stacklevel - 2` frames up."""
52 warnings.warn(f"`{old}` is deprecated, use {new} instead.", DeprecationWarning, stacklevel=stacklevel)
55def _default_tolerance(dtype: npt.DTypeLike) -> float:
56 """The default distance and coefficient tolerance: 500 machine epsilon of the real type of `dtype`.
58 Mirrors `dolfinx_mpc::default_tolerance` in C++.
59 """
60 return float(500 * numpy.finfo(dtype).eps)
63def _tolerance(value: Optional[float], dtype: npt.DTypeLike) -> float:
64 """`value` as a Python float, by default :func:`_default_tolerance` of `dtype`."""
65 return _default_tolerance(dtype) if value is None else float(value)
68def _cpp_function(name: str, dtype: npt.DTypeLike) -> Callable:
69 """The C++ function `name` for constraints of scalar type `dtype`, bound as `name_<type>`."""
70 return getattr(dolfinx_mpc.cpp.mpc, f"{name}_{_type_names[numpy.dtype(dtype).type]}")
73def _scalar_type(real_type: npt.DTypeLike, dtype: npt.DTypeLike | None = None) -> type:
74 """The scalar type of a constraint on a mesh with coordinates of `real_type`.
76 Args:
77 real_type: The type of the mesh coordinates
78 dtype: The scalar type asked for. Defaults to the default scalar type of DOLFINx, real or
79 complex, at the precision of the mesh.
81 Raises:
82 ValueError: If `dtype` is not one of float32, float64, complex64 and complex128, or its
83 precision differs from the mesh's.
84 """
85 real = numpy.dtype(real_type)
86 if dtype is None:
87 is_complex = numpy.issubdtype(dolfinx.default_scalar_type, numpy.complexfloating)
88 dtype = numpy.promote_types(real, numpy.complex64) if is_complex else real
89 scalar = numpy.dtype(dtype)
90 if scalar.type not in (numpy.float32, numpy.float64, numpy.complex64, numpy.complex128):
91 raise ValueError(f"Unsupported scalar type {scalar} for a constraint")
92 if numpy.finfo(scalar).dtype != real:
93 raise ValueError(
94 f"A constraint of scalar type {scalar} needs a mesh of {numpy.finfo(scalar).dtype}, not {real}"
95 )
96 return scalar.type
99class MPCData:
100 _cpp_object: _mpc_data_classes
102 def __init__(
103 self,
104 slaves: npt.NDArray[numpy.int32],
105 masters: npt.NDArray[numpy.int64],
106 coeffs: _float_array_types,
107 owners: npt.NDArray[numpy.int32],
108 offsets: npt.NDArray[numpy.int32],
109 ):
110 if coeffs.dtype.type == numpy.float32:
111 self._cpp_object = dolfinx_mpc.cpp.mpc.mpc_data_float(slaves, masters, coeffs, owners, offsets)
112 elif coeffs.dtype.type == numpy.float64:
113 self._cpp_object = dolfinx_mpc.cpp.mpc.mpc_data_double(slaves, masters, coeffs, owners, offsets)
114 elif coeffs.dtype.type == numpy.complex64:
115 self._cpp_object = dolfinx_mpc.cpp.mpc.mpc_data_complex_float(slaves, masters, coeffs, owners, offsets)
116 elif coeffs.dtype.type == numpy.complex128:
117 self._cpp_object = dolfinx_mpc.cpp.mpc.mpc_data_complex_double(slaves, masters, coeffs, owners, offsets)
118 else:
119 raise ValueError("Unsupported dtype {coeffs.dtype.type} for coefficients")
121 @property
122 def slaves(self):
123 return self._cpp_object.slaves
125 @property
126 def masters(self):
127 return self._cpp_object.masters
129 @property
130 def coeffs(self):
131 return self._cpp_object.coeffs
133 @property
134 def owners(self):
135 return self._cpp_object.owners
137 @property
138 def offsets(self):
139 return self._cpp_object.offsets