From 7f49221997d57253558cdda02d0f66569d9fe7dc Mon Sep 17 00:00:00 2001 From: Pablo Brubeck Date: Mon, 5 Jan 2026 19:31:18 -0600 Subject: [PATCH 1/7] Enable solve(a == 0, u) --- firedrake/solving.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/firedrake/solving.py b/firedrake/solving.py index 21cc6bf58c..cb3fa5f568 100644 --- a/firedrake/solving.py +++ b/firedrake/solving.py @@ -164,7 +164,7 @@ def _solve_varproblem(*args, **kwargs): form_compiler_parameters['scalar_type'] = ScalarType appctx = kwargs.get("appctx", {}) - if isinstance(eq.lhs, (ufl.Form, MatrixBase)) and isinstance(eq.rhs, ufl.BaseForm): + if isinstance(eq.lhs, ufl.BaseForm) and len(eq.lhs.arguments()) == 2: # Create linear variational problem problem = vs.LinearVariationalProblem(eq.lhs, eq.rhs, u, bcs, Jp, form_compiler_parameters=form_compiler_parameters, From f1c27720200583414dd444dbc2e863aecd7bd8b0 Mon Sep 17 00:00:00 2001 From: Pablo Brubeck Date: Mon, 5 Jan 2026 19:37:38 -0600 Subject: [PATCH 2/7] add a test --- firedrake/solving.py | 1 - tests/firedrake/regression/test_solving_interface.py | 8 ++++++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/firedrake/solving.py b/firedrake/solving.py index cb3fa5f568..14b77f6839 100644 --- a/firedrake/solving.py +++ b/firedrake/solving.py @@ -22,7 +22,6 @@ import ufl import firedrake.linear_solver as ls -from firedrake.matrix import MatrixBase import firedrake.variational_solver as vs from firedrake.function import Function from firedrake.adjoint_utils import annotate_solve diff --git a/tests/firedrake/regression/test_solving_interface.py b/tests/firedrake/regression/test_solving_interface.py index 91093f84b6..9ceb69f94f 100644 --- a/tests/firedrake/regression/test_solving_interface.py +++ b/tests/firedrake/regression/test_solving_interface.py @@ -85,6 +85,14 @@ def test_assembled_solver_gced(a_L_out): assert before == after +def test_linear_solve_zero_rhs(a_L_out): + a, L, out = a_L_out + + out.assign(1) + solve(a == 0, out) + assert np_norm(out.dat.data_ro) < 1E-13 + + def test_nonlinear_solver_gced(a_L_out): a, L, out = a_L_out From f25301148eb86597f68c5d54ea00aea4fce155b2 Mon Sep 17 00:00:00 2001 From: Pablo Brubeck Date: Tue, 6 Jan 2026 09:15:34 -0600 Subject: [PATCH 3/7] Raise ValueError if eq.rhs != 0 --- firedrake/solving.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/firedrake/solving.py b/firedrake/solving.py index 14b77f6839..7c17667975 100644 --- a/firedrake/solving.py +++ b/firedrake/solving.py @@ -172,7 +172,7 @@ def _solve_varproblem(*args, **kwargs): else: # Create nonlinear variational problem if eq.rhs != 0: - raise TypeError("Only '0' support on RHS of nonlinear Equation, not %r" % eq.rhs) + raise ValueError("Only '0' support on RHS of nonlinear Equation, not %r" % eq.rhs) problem = vs.NonlinearVariationalProblem(eq.lhs, u, bcs, J, Jp, form_compiler_parameters=form_compiler_parameters, restrict=restrict) From ab501d5c6c45843f783b907b173eb1b1375d6f1d Mon Sep 17 00:00:00 2001 From: Pablo Brubeck Date: Tue, 6 Jan 2026 09:16:32 -0600 Subject: [PATCH 4/7] f-string --- firedrake/solving.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/firedrake/solving.py b/firedrake/solving.py index 7c17667975..0a161b90d9 100644 --- a/firedrake/solving.py +++ b/firedrake/solving.py @@ -172,7 +172,7 @@ def _solve_varproblem(*args, **kwargs): else: # Create nonlinear variational problem if eq.rhs != 0: - raise ValueError("Only '0' support on RHS of nonlinear Equation, not %r" % eq.rhs) + raise ValueError(f"Only '0' support on RHS of nonlinear Equation, not {eq.rhs}") problem = vs.NonlinearVariationalProblem(eq.lhs, u, bcs, J, Jp, form_compiler_parameters=form_compiler_parameters, restrict=restrict) From f4a94259bcdc0bd20066686cee2a33aa599337d2 Mon Sep 17 00:00:00 2001 From: Pablo Brubeck Date: Tue, 6 Jan 2026 11:04:36 -0600 Subject: [PATCH 5/7] Apply suggestions from code review Co-authored-by: David A. Ham --- firedrake/solving.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/firedrake/solving.py b/firedrake/solving.py index 0a161b90d9..e149ac9d9b 100644 --- a/firedrake/solving.py +++ b/firedrake/solving.py @@ -163,7 +163,10 @@ def _solve_varproblem(*args, **kwargs): form_compiler_parameters['scalar_type'] = ScalarType appctx = kwargs.get("appctx", {}) - if isinstance(eq.lhs, ufl.BaseForm) and len(eq.lhs.arguments()) == 2: + if not isinstance(eq.lhs, ufl.BaseForm): + raise TypeError(f"Equation LHS must be a ufl.BaseForm, not a {type(eq.lhs).__name__}") + + if len(eq.lhs.arguments()) == 2: # Create linear variational problem problem = vs.LinearVariationalProblem(eq.lhs, eq.rhs, u, bcs, Jp, form_compiler_parameters=form_compiler_parameters, @@ -172,7 +175,7 @@ def _solve_varproblem(*args, **kwargs): else: # Create nonlinear variational problem if eq.rhs != 0: - raise ValueError(f"Only '0' support on RHS of nonlinear Equation, not {eq.rhs}") + raise ValueError(f"RHS of nonlinear Equation must be `0`, not {eq.rhs}") problem = vs.NonlinearVariationalProblem(eq.lhs, u, bcs, J, Jp, form_compiler_parameters=form_compiler_parameters, restrict=restrict) From 671e8b82ffb9c5a23a2659dacd23c838dead8031 Mon Sep 17 00:00:00 2001 From: Pablo Brubeck Date: Tue, 6 Jan 2026 11:08:32 -0600 Subject: [PATCH 6/7] ufl.classes.Equation -> ufl.Equation --- firedrake/solving.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/firedrake/solving.py b/firedrake/solving.py index e149ac9d9b..3c112ad48f 100644 --- a/firedrake/solving.py +++ b/firedrake/solving.py @@ -138,7 +138,7 @@ def solve(*args, **kwargs): assert len(args) > 0 # Call variational problem solver if we get an equation - if isinstance(args[0], ufl.classes.Equation): + if isinstance(args[0], ufl.Equation): _solve_varproblem(*args, **kwargs) else: # Solve pre-assembled system @@ -165,7 +165,7 @@ def _solve_varproblem(*args, **kwargs): appctx = kwargs.get("appctx", {}) if not isinstance(eq.lhs, ufl.BaseForm): raise TypeError(f"Equation LHS must be a ufl.BaseForm, not a {type(eq.lhs).__name__}") - + if len(eq.lhs.arguments()) == 2: # Create linear variational problem problem = vs.LinearVariationalProblem(eq.lhs, eq.rhs, u, bcs, Jp, From 17700eed004c9d1817e473414feede2b0d71c118 Mon Sep 17 00:00:00 2001 From: Pablo Brubeck Date: Tue, 6 Jan 2026 12:59:16 -0600 Subject: [PATCH 7/7] Update firedrake/solving.py --- firedrake/solving.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/firedrake/solving.py b/firedrake/solving.py index 3c112ad48f..414870eba9 100644 --- a/firedrake/solving.py +++ b/firedrake/solving.py @@ -138,7 +138,7 @@ def solve(*args, **kwargs): assert len(args) > 0 # Call variational problem solver if we get an equation - if isinstance(args[0], ufl.Equation): + if isinstance(args[0], ufl.classes.Equation): _solve_varproblem(*args, **kwargs) else: # Solve pre-assembled system