Skip to content
Open
Show file tree
Hide file tree
Changes from 23 commits
Commits
Show all changes
74 commits
Select commit Hold shift + click to select a range
9031a1d
make getform work (without bcs).
jorgensd Mar 9, 2026
f5cbeea
Some minor logic fixes in BoundsConstraindDirichletBC
jorgensd Mar 9, 2026
e292ed6
Merge remote-tracking branch 'upstream' into dokken/getForm
jorgensd May 4, 2026
547c0d2
Start reverting to Pablo's suggestions.
jorgensd May 4, 2026
ad84b2b
Fix bcs for firedrake (revert to proper functionality as well as star…
jorgensd May 4, 2026
60ed4ee
Add try accept for fml
jorgensd May 4, 2026
301d79f
Further work on the irksome DOLFINx interface
jorgensd May 8, 2026
d5fd814
Apply suggestions from code review
jorgensd May 18, 2026
2d3c3b7
Apply suggestions from code review
jorgensd May 18, 2026
8b5a652
Apply suggestions from code review
jorgensd May 18, 2026
9905eab
Rename create_linear and nonlinear var solver to create_*_solver.
jorgensd May 18, 2026
b804f65
Add backend to most time steppers after moving replace to backend
jorgensd May 18, 2026
5ecb387
Some more fixes to ensure that that firedrake tests run.
jorgensd May 18, 2026
134da90
Further fixes to replace from backend
jorgensd May 18, 2026
ef38d2a
Merge branch 'master' into dokken/getForm
pbrubeck May 18, 2026
dcd835c
Apply suggestions from code review
pbrubeck May 18, 2026
a2da34b
Apply suggestion from @pbrubeck
pbrubeck May 18, 2026
752351e
Apply suggestion from @pbrubeck
pbrubeck May 18, 2026
a06b382
Apply suggestions from code review
pbrubeck May 18, 2026
3361f0b
Remove backend replace and some other minor fixes to get firedrake te…
jorgensd May 18, 2026
0d039d2
Apply suggestions from code review
pbrubeck May 18, 2026
9a0080f
Apply suggestion from @pbrubeck
pbrubeck May 18, 2026
a673fc5
Apply suggestion from @pbrubeck
pbrubeck May 18, 2026
f7530e0
Apply suggestion from @pbrubeck
pbrubeck May 18, 2026
70680b6
Update DOLFINx solver to new API and make a try except on firedrake fml
jorgensd May 18, 2026
31c50ff
Revert formatting
jorgensd May 18, 2026
b4e56fe
Revert formatting
jorgensd May 18, 2026
c249980
Apply suggestions from code review
jorgensd May 18, 2026
291fb2b
Revert formatting
jorgensd May 18, 2026
6162838
Revert init file
jorgensd May 18, 2026
ac2fa25
Apply suggestions from code review
jorgensd May 18, 2026
33a27cd
Apply suggestions from code review
jorgensd May 18, 2026
0244bc4
Revert labelling logic
jorgensd May 18, 2026
705f9e8
Apply suggestions from code review
jorgensd May 18, 2026
4e4b15c
Apply suggestions from code review
jorgensd May 18, 2026
a4cc88f
Revert formatting file to main state
jorgensd May 18, 2026
9bcaf3c
Remove breakpoint
jorgensd May 18, 2026
0a99dbd
Apply suggestions from code review
pbrubeck May 18, 2026
c5960c3
Apply suggestion from @pbrubeck
pbrubeck May 18, 2026
380f15c
Simplify import and propagate backend
jorgensd May 18, 2026
ebbd629
Add note regarding storage convention
jorgensd May 18, 2026
b9c3872
Apply suggestions from code review
jorgensd May 18, 2026
9fbc12f
Fix naming
jorgensd May 18, 2026
ea00e3c
Introduce backend to nystrok dirk time-stepper
jorgensd May 18, 2026
72fde72
Apply suggestions from code review
pbrubeck May 18, 2026
97b82d1
Apply suggestion from @pbrubeck
pbrubeck May 18, 2026
0ebf19d
Apply suggestions from code review
pbrubeck May 18, 2026
957a39b
Apply suggestions from code review
pbrubeck May 18, 2026
21c1a9a
Minor fixes
jorgensd May 18, 2026
7f3572d
Revert ruff formatting
jorgensd May 18, 2026
b18358c
Apply suggestions from code review
jorgensd May 18, 2026
bbbf40b
Apply suggestions from code review
pbrubeck May 18, 2026
e206352
Some more fixes (galerkin stepper constant)
jorgensd May 18, 2026
55637cf
Apply suggestions from code review
jorgensd May 18, 2026
e2272ab
Apply suggestions from code review
jorgensd May 18, 2026
7dc9fa8
Update irksome/tools.py
pbrubeck May 18, 2026
6364076
Update irksome/stage_value.py
pbrubeck May 18, 2026
0d8be42
Update irksome/nystrom_dirk_stepper.py
pbrubeck May 18, 2026
b444c6c
Update irksome/imex.py
pbrubeck May 18, 2026
32fd197
Update irksome/nystrom_dirk_stepper.py
pbrubeck May 18, 2026
86a598d
Apply suggestion from @pbrubeck
pbrubeck May 18, 2026
e498394
Apply suggestions from code review
pbrubeck May 18, 2026
c26f468
Apply suggestions from code review
pbrubeck May 18, 2026
221c55d
Revert meshconstant in steppers
jorgensd May 18, 2026
ce53883
Apply suggestions from code review
pbrubeck May 18, 2026
df05ee1
Linting and remove demo
jorgensd May 18, 2026
780266d
Apply suggestions from code review
pbrubeck May 18, 2026
ae33c01
Apply suggestions from code review
pbrubeck May 18, 2026
2e3aa3d
Apply suggestion from @pbrubeck
pbrubeck May 18, 2026
05334a8
Apply suggestions from code review
pbrubeck May 18, 2026
4997a00
Revert constant to meshless in galerkin timestepper
jorgensd May 18, 2026
3b8a62c
Apply suggestion from @pbrubeck
pbrubeck May 18, 2026
fc7caa5
Simplify fetching backend
jorgensd May 18, 2026
88999f3
Revert to reduce mul from MixedFunctionSpace + some more backend call…
jorgensd May 18, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
58 changes: 58 additions & 0 deletions demo_dolfinx.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
from mpi4py import MPI
import dolfinx
import ufl
from irksome import GaussLegendre, Dt, MeshConstant
from irksome.stage_derivative import getForm
from irksome.tools import get_stage_space
from ufl import pi, atan, div, grad, inner, dx

butcher_tableau = GaussLegendre(2)
N = 64

x0 = 0.0
x1 = 10.0
y0 = 0.0
y1 = 10.0

msh = dolfinx.mesh.create_rectangle(MPI.COMM_WORLD, [[x0, y0], [x1, y1]], [N, N])
V = dolfinx.fem.functionspace(msh, ("Lagrange", 1))
x, y = ufl.SpatialCoordinate(msh)

MC = MeshConstant(msh, backend="dolfinx")
dt = MC.Constant(10 / N)
t = MC.Constant(0.0)

Constant = lambda val: dolfinx.fem.Constant(msh, val)
S = Constant(2.0)
C = Constant(1000.0)


B = (
(x - Constant(x0))
* (x - Constant(x1))
* (y - Constant(y0))
* (y - Constant(y1))
/ C
)
R = (x * x + y * y) ** 0.5
uexact = B * atan(t) * (pi / 2.0 - atan(S * (R - t)))

rhs = Dt(uexact) - div(grad(uexact))

u = dolfinx.fem.Function(V)
u.interpolate(dolfinx.fem.Expression(uexact, V.element.interpolation_points))

v = ufl.TestFunction(V)
F = inner(Dt(u), v) * dx + inner(grad(u), grad(v)) * dx - inner(rhs, v) * dx

bc = []
# bc = DirichletBC(V, 0, "on_boundary")

# Get the function space for the stage-coupled problem and a function to hold the stages we're computing::

Vbig = get_stage_space(V, butcher_tableau.num_stages, backend="dolfinx")
k = dolfinx.fem.Function(Vbig)

# Get the variational form and bcs for the stage-coupled variational problem::

Fnew, bcnew = getForm(F, butcher_tableau, t, dt, u, k, bcs=bc, backend="dolfinx")
2 changes: 1 addition & 1 deletion demos/navier_stokes/demo_nse_unsteady.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
from firedrake import * # noqa: F403
from irksome import GaussLegendre, getForm, Dt, MeshConstant, TimeStepper
from irksome import GaussLegendre, Dt, MeshConstant, TimeStepper
import numpy


Expand Down
5 changes: 3 additions & 2 deletions irksome/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
from .scheme import ContinuousPetrovGalerkinScheme, DiscontinuousGalerkinScheme
from .scheme import GalerkinCollocationScheme, DiscontinuousGalerkinCollocationScheme


__all__ = [
"AdamsBashforth",
"AdamsMoulton",
Expand Down Expand Up @@ -66,12 +67,12 @@
from .bcs import BoundsConstrainedDirichletBC
from .dirk_stepper import DIRKTimeStepper
from .imex import RadauIIAIMEXMethod, DIRKIMEXMethod
from .stage_derivative import getForm
from .nystrom_dirk_stepper import DIRKNystromTimeStepper, ExplicitNystromTimeStepper
from .nystrom_stepper import (
StageDerivativeNystromTimeStepper,
ClassicNystrom4Tableau,
)
from .stage_derivative import getForm
from .stage_value import StageValueTimeStepper

from .pc import (
Expand All @@ -92,7 +93,6 @@
__all__ += [
"DIRKTimeStepper",
"BoundsConstrainedDirichletBC",
"getForm",
"RadauIIAIMEXMethod",
"DIRKIMEXMethod",
"DIRKNystromTimeStepper",
Expand All @@ -112,6 +112,7 @@
"MultistepTimeStepper",
"TimeQuadratureLabel",
"TimeStepper",
"getForm",
]

except ModuleNotFoundError:
Expand Down
54 changes: 53 additions & 1 deletion irksome/backend.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from typing import Protocol
from typing import Protocol, Any, Sequence
import ufl
from importlib import import_module

Expand All @@ -7,6 +7,13 @@ class Backend(Protocol):
def get_function_space(self, V: ufl.Coefficient) -> ufl.FunctionSpace:
"""Get a function space from the backend"""

def extract_bcs(bcs: Any) -> tuple[Any]:
"""Extract boundary conditions"""

class Function: ...
Comment thread
jorgensd marked this conversation as resolved.
Outdated

class DirichletBC: ...
Comment thread
jorgensd marked this conversation as resolved.
Outdated

Comment thread
pbrubeck marked this conversation as resolved.
def get_stages(self, V: ufl.FunctionSpace, num_stages: int) -> ufl.Coefficient:
"""
Given a function space for a single time-step, get a duplicate of this space,
Expand Down Expand Up @@ -38,6 +45,51 @@ def ConstantOrZero(
def get_mesh_constant(MC: MeshConstant | None) -> ufl.core.expr.Expr:
"""Get a backend class to construct a mesh constant from"""

def TestFunction(space: ufl.FunctionSpace, part: int | None = None) -> ufl.Argument:
"""Return a test-function that can be used by forms in the backend."""

def TrialFunction(
space: ufl.FunctionSpace, part: int | None = None
) -> ufl.Argument:
"""Return a trial-function that can be used by forms in the backend."""

def create_variational_problem(
F: ufl.Form,
u: ufl.Coefficient,
bcs: DirichletBC | Sequence | None = None,
**kwargs,
) -> Any:
"""Create a variational problem in the backend language."""

def create_variational_solver(
problem: Any,
solver_parameters: dict | None = None,
**kwargs,
):
"""Create a variational solver in the backend language."""

def get_stage_spaces(V: ufl.FunctionSpace, num_stages: int) -> ufl.FunctionSpace:
"""Create a stage space with M number of components."""

def norm(
v: ufl.core.expr.Expr, norm_type: str = "L2", mesh: ufl.Mesh | None = None
) -> float:
"""Compute the norm of a function in the backend language."""

def assemble(expr: ufl.core.expr.Expr) -> Any:
"""Assemble a UFL expression in the backend language."""

def derivative(
form: ufl.Form,
u: ufl.Coefficient,
du: ufl.Argument | None = None,
coefficient_derivatives: dict | None = None,
) -> ufl.Form:
"""Compute the derivative of a form with respect to a coefficient in the backend language."""

def invalidate_jacobian(solver: Any):
"""Invalidate the Jacobian matrix in the backend language."""


def get_backend(backend: str) -> Backend:
"""Get backend class from backend name.
Expand Down
132 changes: 127 additions & 5 deletions irksome/backends/dolfinx.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,76 @@
"""DOLFINx backend for Irksome"""

try:
from mpi4py import MPI
import basix.ufl
import dolfinx
import dolfinx.fem.petsc
import ufl
import typing
import numpy as np

def get_stage_space(V: ufl.FunctionSpace, num_stages: int) -> ufl.FunctionSpace:
if num_stages == 1:
me = V.ufl_elemet()
else:
el = V.ufl_element()
if el.num_sub_elements > 0:
me = basix.ufl.mixed_element(
np.tile(el.sub_elements, num_stages).tolist()
)
else:
me = basix.ufl.blocked_element(el, shape=(num_stages,))
return dolfinx.fem.functionspace(V.mesh, me)

def extract_bcs(bcs: typing.Any) -> tuple[typing.Any]:
"""Extract boundary conditions"""
return bcs

def create_linearvariational_problem(
a: ufl.Form,
L: ufl.Form,
u: ufl.Coefficient | typing.Sequence[ufl.Coefficient],
bcs: typing.Sequence | None = None,
aP: ufl.Form | None = None,
**kwargs,
) -> dolfinx.fem.petsc.LinearProblem:
return dolfinx.fem.petsc.LinearProblem(
a,
L,
u,
bcs=bcs,
petsc_options_prefix="IrkSomeLinearSolver",
P=aP,
**kwargs,
)

Comment thread
jorgensd marked this conversation as resolved.
Outdated
def create_linear_solver(
problem: dolfinx.fem.petsc.LinearProblem,
solver_parameters: dict | None = None,
**kwargs,
):
"""Create a linear variational solver that uses PETSc KSP."""
Comment thread
jorgensd marked this conversation as resolved.
Outdated
return problem

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Suggested change
def create_linear_solver(
problem: dolfinx.fem.petsc.LinearProblem,
solver_parameters: dict | None = None,
**kwargs,
):
"""Create a linear variational solver that uses PETSc KSP."""
return problem

def create_nonlinearvariational_problem(
Comment thread
jorgensd marked this conversation as resolved.
Outdated
F: ufl.Form,
g: ufl.Coefficient,
bcs: typing.Sequence | None = None,
solver_parameters: dict | None = None,
) -> dolfinx.fem.petsc.NonlinearProblem:
Comment thread
jorgensd marked this conversation as resolved.
Outdated
return dolfinx.fem.petsc.NonlinearProblem(
F,
g,
petsc_options_prefix="IrkSomeNonlinearSolver",
bcs=bcs,
petsc_options=solver_parameters,
)

def create_nonlinear_solver(
problem: dolfinx.fem.petsc.NonlinearProblem,
solver_parameters: dict | None = None,
):
"""Create a non-linear variational solver that uses PETSc SNES."""
return problem
Comment thread
jorgensd marked this conversation as resolved.
Outdated

def get_function_space(u: ufl.Coefficient) -> ufl.FunctionSpace:
return u.ufl_function_space()
Expand Down Expand Up @@ -31,11 +98,20 @@ class MeshConstant(object):
def __init__(self, msh):
self.msh = msh
try:
import scifem
except ModuleNotFoundError:
raise RuntimeError("Scifem is required to make mesh-constants")
import basix.ufl

self.V = scifem.create_real_functionspace(msh, ())
r_el = basix.ufl.real_element(
msh.basix_cell(), value_shape=(), dtype=dolfinx.default_scalar_type
)
self.V = dolfinx.fem.functionspace(msh, r_el)
except TypeError:
try:
import scifem
except ModuleNotFoundError:
raise RuntimeError(
"DOLFINx with real element support or Scifem is required to make mesh-constants"
)
self.V = scifem.create_real_functionspace(msh, ())

def Constant(self, val=0.0) -> ufl.Coefficient:
v = dolfinx.fem.Function(self.V)
Expand All @@ -45,5 +121,51 @@ def Constant(self, val=0.0) -> ufl.Coefficient:
def get_mesh_constant(MC: MeshConstant | None) -> ufl.core.expr.Expr:
return MC.Constant if MC is not None else ufl.constantvalue.ComplexValue

class DirichletBC(dolfinx.fem.DirichletBC):
pass

def norm(
v: ufl.core.Expr, norm_type: str = "L2", mesh: ufl.Mesh | None = None
) -> float:
"""Compute the norm of a function in the backend language."""
if mesh is not None:
dx = ufl.Mesure("dx", domain=mesh)
else:
dx = ufl.dx
p = 2
if norm_type.startswith("L"):
p = int(norm_type[1:])
if p < 1:
raise ValueError(f"Invalid norm type {norm_type}")
expr = ufl.inner(v, v) ** (p / 2)
form = dolfinx.fem.form(expr * dx)
else:
raise NotImplementedError(f"Norm type {norm_type} not implemented")
norm_loc = dolfinx.fem.assemble_scalar(form)
return form.mesh.comm.Allreduce(MPI.IN_PLACE, norm_loc, op=MPI.SUM) ** (1 / p)

def assemble(expr: ufl.core.Expr | float):
"""Assemble a UFL expression in the backend language."""
if isinstance(expr, float):
return float
else:
form = dolfinx.fem.form(expr)
if form.rank == 0:
return dolfinx.fem.assemble_scalar(form)
elif form.rank == 1:
return dolfinx.fem.assemble_vector(form)
elif form.rank == 2:
return dolfinx.fem.assemble_matrix(form)
else:
raise ValueError(f"Cannot assemble form of rank {form.rank}")

derivative = ufl.derivative
TrialFunction = ufl.TrialFunction
Function = dolfinx.fem.Function
TestFunction = ufl.TestFunction

def invalidate_jacobian(solver: dolfinx.fem.petsc.LinearProblem):
"""Invalidate the Jacobian matrix in the backend language."""
raise RuntimeError("DOLFINx does not support Jacobian invalidation")
except ModuleNotFoundError:
pass
12 changes: 12 additions & 0 deletions irksome/backends/firedrake.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,16 @@
import firedrake
import ufl
from ..tools import get_stage_space
Comment thread
jorgensd marked this conversation as resolved.
Outdated
import typing


def get_stage_space(V: ufl.FunctionSpace, num_stages: int) -> ufl.FunctionSpace:
return firedrake.MixedFunctionSpace(tuple(V) * num_stages)


def extract_bcs(bcs: typing.Any) -> tuple[typing.Any]:
"""Return an iterable of boundary conditions on the residual form"""
return tuple(bc.extract_form("F") for bc in firedrake.solving._extract_bcs(bcs))


def get_function_space(u: ufl.Coefficient) -> firedrake.FunctionSpace:
Expand Down Expand Up @@ -63,8 +73,10 @@ def invalidate_jacobian(solver):
return firedrake.LinearVariationalSolver.invalidate_jacobian(solver)


assemble = firedrake.assemble
derivative = firedrake.derivative
norm = firedrake.norm
Function = firedrake.Function
TestFunction = firedrake.TestFunction
TrialFunction = firedrake.TrialFunction
DirichletBC = firedrake.DirichletBC
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

W292 no newline at end of file

Comment thread
jorgensd marked this conversation as resolved.
Outdated
Loading