From 9a6fd71e59eb0e0347d26db5c67d44f6984b0e63 Mon Sep 17 00:00:00 2001 From: Francesco Ballarin Date: Tue, 29 Aug 2023 12:56:48 +0200 Subject: [PATCH] plum is only able to correctly dispatch on keyword arguments if they have a default value. Make the number of modes and the tolerance optional in POD functions --- .../proper_orthogonal_decomposition.py | 25 +++++++++++++------ .../proper_orthogonal_decomposition.py | 21 +++++++++------- .../online/proper_orthogonal_decomposition.py | 23 +++++++++-------- ...ackends_proper_orthogonal_decomposition.py | 4 ++- ..._online_proper_orthogonal_decomposition.py | 4 ++- 5 files changed, 48 insertions(+), 29 deletions(-) diff --git a/rbnicsx/_backends/proper_orthogonal_decomposition.py b/rbnicsx/_backends/proper_orthogonal_decomposition.py index 7c97ff6..df1238e 100644 --- a/rbnicsx/_backends/proper_orthogonal_decomposition.py +++ b/rbnicsx/_backends/proper_orthogonal_decomposition.py @@ -18,11 +18,14 @@ from rbnicsx._cpp import cpp_library +real_zero = petsc4py.PETSc.RealType(0.0) + + def proper_orthogonal_decomposition_functions( # type: ignore[no-any-unimported] functions_list: FunctionsList[Function], compute_inner_product: typing.Callable[[Function], typing.Callable[[Function], petsc4py.PETSc.RealType]], scale: typing.Callable[[Function, petsc4py.PETSc.RealType], None], - N: int, tol: petsc4py.PETSc.RealType, normalize: bool = True + N: int = -1, tol: petsc4py.PETSc.RealType = real_zero, normalize: bool = True ) -> typing.Tuple[ np.typing.NDArray[petsc4py.PETSc.RealType], FunctionsList[Function], typing.List[petsc4py.PETSc.Vec] ]: @@ -39,9 +42,9 @@ def proper_orthogonal_decomposition_functions( # type: ignore[no-any-unimported scale A callable with signature scale(function, factor) to scale any function by a given factor. N - Maximum number of modes to be computed. + Maximum number of modes to be computed. If not provided, it will be set to the number of collected snapshots. tol - Tolerance on the retained energy. + Tolerance on the retained energy. If not provided, it will be set to zero. normalize If true (default), the modes are scaled to unit norm. @@ -67,8 +70,8 @@ def proper_orthogonal_decomposition_functions_block( # type: ignore[no-any-unim compute_inner_products: typing.Sequence[ typing.Callable[[Function], typing.Callable[[Function], petsc4py.PETSc.RealType]]], scale: typing.Callable[[Function, petsc4py.PETSc.RealType], None], - N: typing.Union[int, typing.List[int]], - tol: typing.Union[petsc4py.PETSc.RealType, typing.List[petsc4py.PETSc.RealType]], + N: typing.Union[int, typing.List[int]] = -1, + tol: typing.Union[petsc4py.PETSc.RealType, typing.List[petsc4py.PETSc.RealType]] = real_zero, normalize: bool = True ) -> typing.Tuple[ typing.List[np.typing.NDArray[petsc4py.PETSc.RealType]], typing.List[FunctionsList[Function]], @@ -92,9 +95,11 @@ def proper_orthogonal_decomposition_functions_block( # type: ignore[no-any-unim N Maximum number of modes to be computed. If an integer value is passed then the same maximum number is used for each block. To set a different maximum number of modes for each block pass a list of integers. + If not provided, it will be set to the number of collected snapshots. tol Tolerance on the retained energy. If a floating point value is passed then the same tolerance is used for each block. To set a different tolerance for each block pass a list of floating point numbers. + If not provided, it will be set to zero. normalize If true (default), the modes are scaled to unit norm. @@ -132,7 +137,7 @@ def proper_orthogonal_decomposition_functions_block( # type: ignore[no-any-unim def proper_orthogonal_decomposition_tensors( # type: ignore[no-any-unimported] - tensors_list: TensorsList, N: int, tol: petsc4py.PETSc.RealType, normalize: bool = True + tensors_list: TensorsList, N: int = -1, tol: petsc4py.PETSc.RealType = real_zero, normalize: bool = True ) -> typing.Tuple[ np.typing.NDArray[petsc4py.PETSc.RealType], TensorsList, typing.List[petsc4py.PETSc.Vec] ]: @@ -144,9 +149,9 @@ def proper_orthogonal_decomposition_tensors( # type: ignore[no-any-unimported] tensors_list Collected tensors. N - Maximum number of modes to be computed. + Maximum number of modes to be computed. If not provided, it will be set to the number of collected tensors. tol - Tolerance on the retained energy. + Tolerance on the retained energy. If not provided, it will be set to zero. normalize If true (default), the modes are scaled to unit norm. @@ -243,6 +248,10 @@ def _solve_eigenvalue_problem( # type: ignore[no-any-unimported] 3. Eigenvectors of the correlation matrix. Only the first few eigenvectors are returned, till either the maximum number N is reached or the tolerance on the retained energy is fulfilled. """ + assert N > 0 or N == -1 + if N == -1: + N = len(snapshots) + correlation_matrix = create_online_matrix(len(snapshots), len(snapshots)) for (j, snapshot_j) in enumerate(snapshots): compute_inner_product_partial_j = compute_inner_product(snapshot_j) diff --git a/rbnicsx/backends/proper_orthogonal_decomposition.py b/rbnicsx/backends/proper_orthogonal_decomposition.py index 78b0d39..b1e68a9 100644 --- a/rbnicsx/backends/proper_orthogonal_decomposition.py +++ b/rbnicsx/backends/proper_orthogonal_decomposition.py @@ -16,7 +16,8 @@ from rbnicsx._backends.proper_orthogonal_decomposition import ( proper_orthogonal_decomposition_functions as proper_orthogonal_decomposition_functions_super, proper_orthogonal_decomposition_functions_block as proper_orthogonal_decomposition_functions_block_super, - proper_orthogonal_decomposition_tensors as proper_orthogonal_decomposition_tensors_super) + proper_orthogonal_decomposition_tensors as proper_orthogonal_decomposition_tensors_super, + real_zero) from rbnicsx.backends.functions_list import FunctionsList from rbnicsx.backends.tensors_list import TensorsList @@ -30,7 +31,7 @@ def proper_orthogonal_decomposition( # type: ignore[no-any-unimported] functions_list: FunctionsList, compute_inner_product: typing.Callable[ [dolfinx.fem.Function], typing.Callable[[dolfinx.fem.Function], petsc4py.PETSc.RealType]], - N: int, tol: petsc4py.PETSc.RealType, normalize: bool = True + N: int = -1, tol: petsc4py.PETSc.RealType = real_zero, normalize: bool = True ) -> typing.Tuple[ np.typing.NDArray[petsc4py.PETSc.RealType], FunctionsList, typing.List[petsc4py.PETSc.Vec] ]: @@ -46,9 +47,9 @@ def proper_orthogonal_decomposition( # type: ignore[no-any-unimported] The resulting modes will be orthonormal w.r.t. this inner product. Use rbnicsx.backends.bilinear_form_action to generate the callable x from a UFL form. N - Maximum number of modes to be computed. + Maximum number of modes to be computed. If not provided, it will be set to the number of collected snapshots. tol - Tolerance on the retained energy. + Tolerance on the retained energy. If not provided, it will be set to zero. normalize If true (default), the modes are scaled to unit norm. @@ -68,7 +69,7 @@ def proper_orthogonal_decomposition( # type: ignore[no-any-unimported] @plum.overload def proper_orthogonal_decomposition( # type: ignore[no-any-unimported] # noqa: F811 - tensors_list: TensorsList, N: int, tol: petsc4py.PETSc.RealType, normalize: bool = True + tensors_list: TensorsList, N: int = -1, tol: petsc4py.PETSc.RealType = real_zero, normalize: bool = True ) -> typing.Tuple[ np.typing.NDArray[petsc4py.PETSc.RealType], TensorsList, typing.List[petsc4py.PETSc.Vec] ]: @@ -80,9 +81,9 @@ def proper_orthogonal_decomposition( # type: ignore[no-any-unimported] # noqa: tensors_list Collected tensors. N - Maximum number of modes to be computed. + Maximum number of modes to be computed. If not provided, it will be set to the number of collected tensors. tol - Tolerance on the retained energy. + Tolerance on the retained energy. If not provided, it will be set to zero. normalize If true (default), the modes are scaled to unit norm. @@ -109,8 +110,8 @@ def proper_orthogonal_decomposition_block( # type: ignore[no-any-unimported] functions_lists: typing.Sequence[FunctionsList], compute_inner_products: typing.Sequence[ typing.Callable[[dolfinx.fem.Function], typing.Callable[[dolfinx.fem.Function], petsc4py.PETSc.RealType]]], - N: typing.Union[int, typing.List[int]], - tol: typing.Union[petsc4py.PETSc.RealType, typing.List[petsc4py.PETSc.RealType]], + N: typing.Union[int, typing.List[int]] = -1, + tol: typing.Union[petsc4py.PETSc.RealType, typing.List[petsc4py.PETSc.RealType]] = real_zero, normalize: bool = True ) -> typing.Tuple[ typing.List[np.typing.NDArray[petsc4py.PETSc.RealType]], typing.List[FunctionsList], @@ -133,9 +134,11 @@ def proper_orthogonal_decomposition_block( # type: ignore[no-any-unimported] N Maximum number of modes to be computed. If an integer value is passed then the same maximum number is used for each block. To set a different maximum number of modes for each block pass a list of integers. + If not provided, it will be set to the number of collected snapshots. tol Tolerance on the retained energy. If a floating point value is passed then the same tolerance is used for each block. To set a different tolerance for each block pass a list of floating point numbers. + If not provided, it will be set to zero. normalize If true (default), the modes are scaled to unit norm. diff --git a/rbnicsx/online/proper_orthogonal_decomposition.py b/rbnicsx/online/proper_orthogonal_decomposition.py index f090bc3..8293935 100644 --- a/rbnicsx/online/proper_orthogonal_decomposition.py +++ b/rbnicsx/online/proper_orthogonal_decomposition.py @@ -15,7 +15,8 @@ from rbnicsx._backends.proper_orthogonal_decomposition import ( proper_orthogonal_decomposition_functions as proper_orthogonal_decomposition_functions_super, proper_orthogonal_decomposition_functions_block as proper_orthogonal_decomposition_functions_block_super, - proper_orthogonal_decomposition_tensors as proper_orthogonal_decomposition_tensors_super) + proper_orthogonal_decomposition_tensors as proper_orthogonal_decomposition_tensors_super, + real_zero) from rbnicsx.online.functions_list import FunctionsList from rbnicsx.online.projection import matrix_action from rbnicsx.online.tensors_list import TensorsList @@ -27,8 +28,8 @@ @plum.overload def proper_orthogonal_decomposition( # type: ignore[no-any-unimported] # noqa: F811 - functions_list: FunctionsList, inner_product: petsc4py.PETSc.Mat, N: int, tol: petsc4py.PETSc.RealType, - normalize: bool = True + functions_list: FunctionsList, inner_product: petsc4py.PETSc.Mat, N: int = -1, + tol: petsc4py.PETSc.RealType = real_zero, normalize: bool = True ) -> typing.Tuple[ np.typing.NDArray[petsc4py.PETSc.RealType], FunctionsList, typing.List[petsc4py.PETSc.Vec] ]: @@ -43,9 +44,9 @@ def proper_orthogonal_decomposition( # type: ignore[no-any-unimported] # noqa: Online matrix which defines the inner product. The resulting modes will be orthonormal w.r.t. this inner product. N - Maximum number of modes to be computed. + Maximum number of modes to be computed. If not provided, it will be set to the number of collected snapshots. tol - Tolerance on the retained energy. + Tolerance on the retained energy. If not provided, it will be set to zero. normalize : bool, optional If true (default), the modes are scaled to unit norm. @@ -67,7 +68,7 @@ def proper_orthogonal_decomposition( # type: ignore[no-any-unimported] # noqa: @plum.overload def proper_orthogonal_decomposition( # type: ignore[no-any-unimported] # noqa: F811 - tensors_list: TensorsList, N: int, tol: petsc4py.PETSc.RealType, normalize: bool = True + tensors_list: TensorsList, N: int = -1, tol: petsc4py.PETSc.RealType = real_zero, normalize: bool = True ) -> typing.Tuple[ np.typing.NDArray[petsc4py.PETSc.RealType], TensorsList, typing.List[petsc4py.PETSc.Vec] ]: @@ -79,9 +80,9 @@ def proper_orthogonal_decomposition( # type: ignore[no-any-unimported] # noqa: tensors_list Collected tensors. N - Maximum number of modes to be computed. + Maximum number of modes to be computed. If not provided, it will be set to the number of collected tensors. tol - Tolerance on the retained energy. + Tolerance on the retained energy. If not provided, it will be set to zero. normalize If true (default), the modes are scaled to unit norm. @@ -106,8 +107,8 @@ def proper_orthogonal_decomposition(*args, **kwargs): # type: ignore[no-untyped def proper_orthogonal_decomposition_block( # type: ignore[no-any-unimported] functions_lists: typing.Sequence[FunctionsList], inner_products: typing.List[petsc4py.PETSc.Mat], - N: typing.Union[int, typing.List[int]], - tol: typing.Union[petsc4py.PETSc.RealType, typing.List[petsc4py.PETSc.RealType]], + N: typing.Union[int, typing.List[int]] = -1, + tol: typing.Union[petsc4py.PETSc.RealType, typing.List[petsc4py.PETSc.RealType]] = real_zero, normalize: bool = True ) -> typing.Tuple[ typing.List[np.typing.NDArray[petsc4py.PETSc.RealType]], typing.List[FunctionsList], @@ -128,9 +129,11 @@ def proper_orthogonal_decomposition_block( # type: ignore[no-any-unimported] N Maximum number of modes to be computed. If an integer value is passed then the same maximum number is used for each block. To set a different maximum number of modes for each block pass a list of integers. + If not provided, it will be set to the number of collected snapshots. tol Tolerance on the retained energy. If a floating point value is passed then the same tolerance is used for each block. To set a different tolerance for each block pass a list of floating point numbers. + If not provided, it will be set to zero. normalize If true (default), the modes are scaled to unit norm. diff --git a/tests/unit/backends/test_backends_proper_orthogonal_decomposition.py b/tests/unit/backends/test_backends_proper_orthogonal_decomposition.py index f381f4e..123fcd2 100644 --- a/tests/unit/backends/test_backends_proper_orthogonal_decomposition.py +++ b/tests/unit/backends/test_backends_proper_orthogonal_decomposition.py @@ -13,6 +13,7 @@ import mpi4py.MPI import numpy as np import petsc4py.PETSc +import plum import pytest import ufl @@ -210,5 +211,6 @@ def test_backends_proper_orthogonal_decomposition_zero( # type: ignore[no-any-u def test_backends_proper_orthogonal_decomposition_wrong_iterable() -> None: """Check rbnicsx.backends.proper_orthogonal_decomposition raises when providing a plain list.""" - with pytest.raises(RuntimeError): + with pytest.raises(plum.NotFoundLookupError) as excinfo: rbnicsx.backends.proper_orthogonal_decomposition(list(), N=0, tol=0.0) # type: ignore[call-overload] + assert str(excinfo.value) == "For function `proper_orthogonal_decomposition`, `([],)` could not be resolved." diff --git a/tests/unit/online/test_online_proper_orthogonal_decomposition.py b/tests/unit/online/test_online_proper_orthogonal_decomposition.py index 04b9c33..77e9bd5 100644 --- a/tests/unit/online/test_online_proper_orthogonal_decomposition.py +++ b/tests/unit/online/test_online_proper_orthogonal_decomposition.py @@ -10,6 +10,7 @@ import _pytest.fixtures import numpy as np import petsc4py.PETSc +import plum import pytest import rbnicsx.online @@ -281,5 +282,6 @@ def test_online_proper_orthogonal_decomposition_zero( # type: ignore[no-any-uni def test_online_proper_orthogonal_decomposition_wrong_iterable() -> None: """Check rbnicsx.online.proper_orthogonal_decomposition raises when providing a plain list.""" - with pytest.raises(RuntimeError): + with pytest.raises(plum.NotFoundLookupError) as excinfo: rbnicsx.online.proper_orthogonal_decomposition(list(), N=0, tol=0.0) # type: ignore[call-overload] + assert str(excinfo.value) == "For function `proper_orthogonal_decomposition`, `([],)` could not be resolved."