From 7f2d3da6961bd57d56c16e4b7098b51eeb270555 Mon Sep 17 00:00:00 2001 From: Connor Ward Date: Wed, 15 Jul 2026 09:52:09 +0100 Subject: [PATCH 1/3] Make EigenSolver and VarSolver have an OptionsManager, not be one Composition > inheritance. This is a step along the way to cleaning up the appctxs in Firedrake. --- firedrake/eigensolver.py | 45 ++++++++++++++++++++++++++++----- firedrake/variational_solver.py | 45 +++++++++++++++++++++++++++------ 2 files changed, 76 insertions(+), 14 deletions(-) diff --git a/firedrake/eigensolver.py b/firedrake/eigensolver.py index be768b3049..639454672e 100644 --- a/firedrake/eigensolver.py +++ b/firedrake/eigensolver.py @@ -1,16 +1,23 @@ """Specify and solve finite element eigenproblems.""" +import contextlib +import warnings +from functools import cached_property + +from petsc4py import PETSc from petsctools import OptionsManager, flatten_parameters +from ufl import replace, inner, dx + from firedrake.assemble import assemble from firedrake.bcs import extract_subdomain_ids, restricted_function_space +from firedrake.exceptions import ConvergenceError from firedrake.function import Function from firedrake.ufl_expr import TrialFunction, TestFunction -from firedrake.exceptions import ConvergenceError -from ufl import replace, inner, dx -from functools import cached_property + try: from slepc4py import SLEPc except ImportError: SLEPc = None + __all__ = ["LinearEigenproblem", "LinearEigensolver"] @@ -96,7 +103,7 @@ def dm(self): return self.output_space.dm -class LinearEigensolver(OptionsManager): +class LinearEigensolver: r"""Solve a LinearEigenproblem. Parameters @@ -159,8 +166,34 @@ def __init__(self, problem, n_evals, *, options_prefix=None, for key in self.DEFAULT_EPS_PARAMETERS: value = self.DEFAULT_EPS_PARAMETERS[key] solver_parameters.setdefault(key, value) - super().__init__(solver_parameters, options_prefix) - self.set_from_options(self.es) + + self.options_manager = OptionsManager(solver_parameters, options_prefix) + self.options_manager.set_from_options(self.es) + + @property + def parameters(self) -> dict: + return self.options_manager.parameters + + @property + def options_prefix(self) -> str: + return self.options_manager.options_prefix + + def set_from_options(self, snes: PETSc.SNES) -> None: + warnings.warn( + "'solver.set_from_options' is deprecated, use " + "'solver.options_manager.set_from_options' instead", + FutureWarning + ) + self.options_manager.set_from_options(snes) + + @contextlib.contextmanager + def inserted_options(self, snes: PETSc.SNES): + warnings.warn( + "'solver.inserted_options' is deprecated, use " + "'solver.options_manager.inserted_options' instead", + FutureWarning + ) + yield from self.options_manager.inserted_options def check_es_convergence(self): r"""Check the convergence of the eigenvalue problem.""" diff --git a/firedrake/variational_solver.py b/firedrake/variational_solver.py index 4031bf3c5c..a361cdd538 100644 --- a/firedrake/variational_solver.py +++ b/firedrake/variational_solver.py @@ -1,10 +1,14 @@ from __future__ import annotations -import ufl -from itertools import chain +import contextlib +import warnings from contextlib import ExitStack +from itertools import chain from types import MappingProxyType + +import ufl from petsctools import OptionsManager, flatten_parameters +from ufl import replace, Form from firedrake import dmhooks, slate, solving, solving_utils, ufl_expr, utils from firedrake.petsc import PETSc, DEFAULT_KSP_PARAMETERS, DEFAULT_SNES_PARAMETERS @@ -14,7 +18,6 @@ from firedrake.ufl_expr import TrialFunction, TestFunction from firedrake.bcs import DirichletBC, EquationBC, extract_subdomain_ids, restricted_function_space from firedrake.adjoint_utils import NonlinearVariationalProblemMixin, NonlinearVariationalSolverMixin -from ufl import replace, Form from functools import cached_property __all__ = ["LinearVariationalProblem", @@ -168,7 +171,7 @@ def compute_bc_lifting(J: ufl.BaseForm | slate.TensorBase, return F -class NonlinearVariationalSolver(OptionsManager, NonlinearVariationalSolverMixin): +class NonlinearVariationalSolver(NonlinearVariationalSolverMixin): r"""Solves a :class:`NonlinearVariationalProblem`.""" DEFAULT_SNES_PARAMETERS = DEFAULT_SNES_PARAMETERS @@ -275,8 +278,9 @@ def update_diffusivity(current_solution): problem.J.arguments(), ksp_defaults=self.DEFAULT_KSP_PARAMETERS, snes_defaults=self.DEFAULT_SNES_PARAMETERS) - super().__init__(solver_parameters, options_prefix, - default_prefix="firedrake") + + self.options_manager = OptionsManager(solver_parameters, options_prefix, + default_prefix="firedrake") # Now the correct parameters live in self.parameters (via the # OptionsManager mixin) mat_type = self.parameters.get("mat_type") @@ -322,12 +326,37 @@ def update_diffusivity(current_solution): # on a subKSP the context is available. dm = self.snes.getDM() with dmhooks.add_hooks(dm, self, appctx=self._ctx, save=False): - self.set_from_options(self.snes) + self.options_manager.set_from_options(self.snes) # Used for custom grid transfer. self._transfer_operators = () self._setup = False + @property + def parameters(self) -> dict: + return self.options_manager.parameters + + @property + def options_prefix(self) -> str: + return self.options_manager.options_prefix + + def set_from_options(self, snes: PETSc.SNES) -> None: + warnings.warn( + "'solver.set_from_options' is deprecated, use " + "'solver.options_manager.set_from_options' instead", + FutureWarning + ) + self.options_manager.set_from_options(snes) + + @contextlib.contextmanager + def inserted_options(self, snes: PETSc.SNES): + warnings.warn( + "'solver.inserted_options' is deprecated, use " + "'solver.options_manager.inserted_options' instead", + FutureWarning + ) + yield from self.options_manager.inserted_options + def set_transfer_manager(self, manager): r"""Set the object that manages transfer between grid levels. Typically a :class:`~.TransferManager` object. @@ -391,7 +420,7 @@ def solve(self, bounds=None): with ExitStack() as stack: # Ensure options database has full set of options (so monitors # work right) - for ctx in chain([self.inserted_options()], + for ctx in chain([self.options_manager.inserted_options()], [dmhooks.add_hooks(dm, self, appctx=self._ctx) for dm in problem_dms], self._transfer_operators): stack.enter_context(ctx) From c64812b32c5b6d06d4f951a7e13c0ed28a5077a2 Mon Sep 17 00:00:00 2001 From: Connor Ward Date: Wed, 15 Jul 2026 09:56:23 +0100 Subject: [PATCH 2/3] fixup warning messages --- firedrake/eigensolver.py | 8 ++++---- firedrake/variational_solver.py | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/firedrake/eigensolver.py b/firedrake/eigensolver.py index 639454672e..130cc25e67 100644 --- a/firedrake/eigensolver.py +++ b/firedrake/eigensolver.py @@ -180,8 +180,8 @@ def options_prefix(self) -> str: def set_from_options(self, snes: PETSc.SNES) -> None: warnings.warn( - "'solver.set_from_options' is deprecated, use " - "'solver.options_manager.set_from_options' instead", + "'LinearEigensolver.set_from_options' is deprecated, use " + "'LinearEigensolver.options_manager.set_from_options' instead", FutureWarning ) self.options_manager.set_from_options(snes) @@ -189,8 +189,8 @@ def set_from_options(self, snes: PETSc.SNES) -> None: @contextlib.contextmanager def inserted_options(self, snes: PETSc.SNES): warnings.warn( - "'solver.inserted_options' is deprecated, use " - "'solver.options_manager.inserted_options' instead", + "'LinearEigensolver.inserted_options' is deprecated, use " + "'LinearEigensolver.options_manager.inserted_options' instead", FutureWarning ) yield from self.options_manager.inserted_options diff --git a/firedrake/variational_solver.py b/firedrake/variational_solver.py index a361cdd538..a5ebb143f3 100644 --- a/firedrake/variational_solver.py +++ b/firedrake/variational_solver.py @@ -342,8 +342,8 @@ def options_prefix(self) -> str: def set_from_options(self, snes: PETSc.SNES) -> None: warnings.warn( - "'solver.set_from_options' is deprecated, use " - "'solver.options_manager.set_from_options' instead", + "'NonlinearVariationalSolver.set_from_options' is deprecated, use " + "'NonlinearVariationalSolver.options_manager.set_from_options' instead", FutureWarning ) self.options_manager.set_from_options(snes) @@ -351,8 +351,8 @@ def set_from_options(self, snes: PETSc.SNES) -> None: @contextlib.contextmanager def inserted_options(self, snes: PETSc.SNES): warnings.warn( - "'solver.inserted_options' is deprecated, use " - "'solver.options_manager.inserted_options' instead", + "'NonlinearVariationalSolver.inserted_options' is deprecated, use " + "'NonlinearVariationalSolver.options_manager.inserted_options' instead", FutureWarning ) yield from self.options_manager.inserted_options From e783caadf2bd0a59c38e8715169cccedb1bf2966 Mon Sep 17 00:00:00 2001 From: Connor Ward Date: Wed, 15 Jul 2026 11:03:15 +0100 Subject: [PATCH 3/3] fixup --- firedrake/eigensolver.py | 8 +++----- firedrake/variational_solver.py | 6 ++---- 2 files changed, 5 insertions(+), 9 deletions(-) diff --git a/firedrake/eigensolver.py b/firedrake/eigensolver.py index 130cc25e67..7248f98437 100644 --- a/firedrake/eigensolver.py +++ b/firedrake/eigensolver.py @@ -1,5 +1,4 @@ """Specify and solve finite element eigenproblems.""" -import contextlib import warnings from functools import cached_property @@ -186,14 +185,13 @@ def set_from_options(self, snes: PETSc.SNES) -> None: ) self.options_manager.set_from_options(snes) - @contextlib.contextmanager - def inserted_options(self, snes: PETSc.SNES): + def inserted_options(self): warnings.warn( "'LinearEigensolver.inserted_options' is deprecated, use " "'LinearEigensolver.options_manager.inserted_options' instead", FutureWarning ) - yield from self.options_manager.inserted_options + return self.options_manager.inserted_options() def check_es_convergence(self): r"""Check the convergence of the eigenvalue problem.""" @@ -226,7 +224,7 @@ def solve(self): self.es.setDimensions(nev=self.n_evals, ncv=self.ncv, mpd=self.mpd) self.es.setOperators(self.A_mat, self.M_mat) - with self.inserted_options(): + with self.options_manager.inserted_options(): self.es.solve() nconv = self.es.getConverged() if nconv == 0: diff --git a/firedrake/variational_solver.py b/firedrake/variational_solver.py index a5ebb143f3..708b314e99 100644 --- a/firedrake/variational_solver.py +++ b/firedrake/variational_solver.py @@ -1,6 +1,5 @@ from __future__ import annotations -import contextlib import warnings from contextlib import ExitStack from itertools import chain @@ -348,14 +347,13 @@ def set_from_options(self, snes: PETSc.SNES) -> None: ) self.options_manager.set_from_options(snes) - @contextlib.contextmanager - def inserted_options(self, snes: PETSc.SNES): + def inserted_options(self): warnings.warn( "'NonlinearVariationalSolver.inserted_options' is deprecated, use " "'NonlinearVariationalSolver.options_manager.inserted_options' instead", FutureWarning ) - yield from self.options_manager.inserted_options + return self.options_manager.inserted_options() def set_transfer_manager(self, manager): r"""Set the object that manages transfer between grid levels.