Skip to content

GoalAdaptiveNonlinearVariationalSolver - #4893

Closed
pbrubeck wants to merge 83 commits into
mainfrom
pbrubeck/goal-adaptive-solver
Closed

GoalAdaptiveNonlinearVariationalSolver#4893
pbrubeck wants to merge 83 commits into
mainfrom
pbrubeck/goal-adaptive-solver

Conversation

@pbrubeck

@pbrubeck pbrubeck commented Feb 16, 2026

Copy link
Copy Markdown
Contributor

Description

Implements a new GoalAdaptiveNonlinearVariationalSolver class to do adaptive refinement on a NonlinearVariationalProblem defined on a coarse (netgen) mesh. The adaptive procedure relies on a user-specified functional of interest, refered to as goal functional. The solver will iteratively solve -> estimate -> mark -> refine until an automatic error estimate to the goal functional falls below a user specified tolerance.

Comment thread firedrake/adaptive_variational_solver.py Outdated
@pbrubeck
pbrubeck force-pushed the pbrubeck/goal-adaptive-solver branch from daf58a1 to b28fbf5 Compare February 26, 2026 13:52
Comment thread firedrake/mg/ufl_utils.py Outdated
@connorjward

Copy link
Copy Markdown
Contributor

For example we might want to have MeshAdapter and SolverMonitor abstract classes that would own bits of what the GoalAdaptiveVariationalSolver does.

@connorjward

Copy link
Copy Markdown
Contributor

How does this compare to all the mesh adaptivity work that has already been done with Firedrake (https://github.com/mesh-adaptation)? In particular https://github.com/mesh-adaptation/goalie

@stephankramer
@joewallwork

@pefarrell

Copy link
Copy Markdown
Contributor

I can add my perspective. I'm sure @stephankramer and @joewallwork will have other interesting things to say!

Compared to goalie, this PR

  • addresses only stationary problems, whereas goalie (only?) considers transient ones;
  • employs adaptive mesh refinement from netgen, whereas goalie/animate use anisotropic adaptive remeshing or mesh movement from animate/movement;
  • involves a very small amount of work to convert a code that solves one discrete nonlinear problem to apply goal-based adaptivity, whereas using goalie would require substantial rewriting in the goalie format;
  • gives explicit error estimates for goal functionals, which I don't believe goalie does (at least I don't see it in the demos).

This PR also composes nicely with the rest of Firedrake. When the mesh is refined, the integration with netgen ensures the geometry is better approximated (this is not true with goalie). The adaptive mesh hierarchy constructed by the GoalAdaptiveNonlinearVariationalSolver can be used for all solvers Firedrake supports, including geometric multigrid. (I don't believe this is the case with goalie but may be wrong.)

More about adaptive mesh refinement vs adaptive remeshing, as there is a major mathematical difference. In the adaptive mesh refinement used in this PR, the adapted mesh is a refinement of the input mesh (ignoring better geometry approximation). This means that for normal elements the refined function space is a superspace of the original one, which is very advantageous for stable approximation: loosely speaking, your approximation can only get better as you refine. In contrast, with adaptive remeshing, the input and output meshes are in principle unrelated (although they are in practice, because the algorithm transforms the one into the other step by step), and so you don't have nice hierarchical approximations. I suspect this is why 95%+ of the community around adaptive discretisations use adaptive mesh refinement.

@joewallwork

Copy link
Copy Markdown
Contributor

Hi all, great to see this contribution! Nice work. I'll try to find some time to play around with this.

To respond to @pefarrell's points:

* addresses only stationary problems, whereas goalie (only?) considers transient ones;

Yes, Goalie is designed to handle time-dependent problems that consider multiple meshes during the simulation. You can apply it to stationary problems, too.

* employs adaptive mesh refinement from netgen, whereas goalie/animate use anisotropic adaptive remeshing or mesh movement from animate/movement;

I had proposed an MSc project for a student to hook up AMR in Goalie using Netgen but unfortunately it didn't go ahead. It was designed to be general enough to allow different adaptor approaches but we have mainly used Animate so far.

* involves a very small amount of work to convert a code that solves one discrete nonlinear problem to apply goal-based adaptivity, whereas using goalie would require substantial rewriting in the goalie format;

Yeah this is one of the main limitations of Goalie.

* gives explicit error estimates for goal functionals, which I don't believe goalie does (at least I don't see it in the demos).

Currently we make a choice of error estimate based on the dual-weighted residual. There were plans to support more/custom error estimates but we haven't had time.

This PR also composes nicely with the rest of Firedrake. When the mesh is refined, the integration with netgen ensures the geometry is better approximated (this is not true with goalie). The adaptive mesh hierarchy constructed by the GoalAdaptiveNonlinearVariationalSolver can be used for all solvers Firedrake supports, including geometric multigrid. (I don't believe this is the case with goalie but may be wrong.)

I don't recall Goalie having limitations in terms of solvers, although I haven't tried loads of them. However, it is currently limited to simple theta-method timestepping schemes.

@connorjward

Copy link
Copy Markdown
Contributor

Thanks for this useful exposition.

By raising this I wanted to point out that there is a more general appetite for adaptivity using Firedrake and therefore it would be very nice to set this up in a more general way so that potential future contributions would be able to reuse some of the code.

I think this should be brought to a Firedrake team meeting for further discussion.

Comment thread firedrake/mg/ufl_utils.py Outdated
pbrubeck added 3 commits May 16, 2026 18:00
Co-authored-by: Pablo Brubeck <brubeck@protonmail.com>
@pbrubeck
pbrubeck force-pushed the pbrubeck/goal-adaptive-solver branch from 998ef03 to 582c8f4 Compare May 17, 2026 17:08
* ``False`` (default) – never write.
* ``True`` – write at every iteration.
* A positive integer ``k`` – write every ``k`` iterations.
verbose

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this whole thing should be PETSc options. This would be cognate with what we do for NVS

self.eta_cell_sum_vec = []
self.eff1_vec = []
self.eff2_vec = []
self.eff3_vec = []

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why 3?


def print(self, *args, **kwargs):
if self.options.verbose:
PETSc.Sys.Print(*args, **kwargs)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

comm?

coef_map = {}
self.problem = refine(self.problem, refine, coefficient_mapping=coef_map)

def compute_efficiency_indices(self, eta_cell, eta_h, eta):

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

feels like it should somehow be programmable rather than being part of the abstract interface.

Comment thread firedrake/adaptive_variational_solver.py Outdated
A dictionary of solver parameters for the primal problem
dual_solver_parameters
A dictionary of solver parameters for the dual problem.
Defaults to `primal_solver_parameters`.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

namespace this into the options tree.

"""
u_out = self.u_high if self.u_high is not None else self.problem.u
error_estimate = self.etah_vec[-1]
return u_out, error_estimate

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

return the soltion, query for the error. Just like converged reason.

pbrubeck added a commit that referenced this pull request Aug 11, 2026
Add a global DWR estimate of the error in the goal functional, split into
the error committed by discretising and the error committed by not solving
the algebraic system exactly, and stop adapting once it meets a tolerance.

  * -dwr_atol/-dwr_rtol stop the loop once |eta| < max(atol, rtol*|J(u_h)|).
    A marking callback that returns None now means "stop adapting": the
    refine hook hands the same DM back, and a SNES convergence test reports
    immediate convergence, so the rest of -snes_adapt_sequence costs nothing.
    PETSc's DMAdaptor has no tolerance of its own to do this with.
  * -dwr_monitor reports the estimate once per cycle. An exact_solution kwarg
    adds the true error and an effectivity index to that output.
  * Warn when the solver error estimate exceeds the discretisation error
    estimate, since refining then cannot help.
  * A marking callback is no longer required: without one, adaptive
    refinement degenerates to uniform refinement, as grid sequencing asks.

Three fixes this uncovered:

  * dwr_ options were read from the reconstructed context, whose prefix is
    renamed after the multigrid level it becomes, so every one of them
    silently reverted to its default after the first refinement.
  * Function's second positional argument is val, not name, so the
    enriched-order primal solve raised on every mark.
  * Interpolation between a space and itself is the identity. PETSc asks for
    it when the adaptor hands back the DM it was given.

Port the p-Laplacian goal-based adaptivity demo from #4893, with netgen
replaced by a plain mesh, and give it and the elasticity demo names that say
which is linear and which is not. The elasticity demo needs MUMPS null-pivot
detection for its indefinite system.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@pbrubeck

Copy link
Copy Markdown
Contributor Author

Closed in favour of #5273

@pbrubeck pbrubeck closed this Aug 14, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants