Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
d2ae8a2
Various fixes to nonlinear solve.
jorgensd Aug 25, 2026
90d0994
Start on blocked linear problem
jorgensd Aug 25, 2026
4f823d3
Various fixes for blocked linear problem
jorgensd Aug 26, 2026
271fc57
Fix adjoint and tlm for blocked problems.
jorgensd Aug 26, 2026
87d9b59
Simplified block forms
jorgensd Aug 26, 2026
a974f1f
Use direct solver to ensure that initial guess doesn't influence the …
jorgensd Aug 26, 2026
1bca24c
Various dolfinx.la.vectors switched to petsc for safe management.
jorgensd Aug 26, 2026
e508da2
Use localform on petsc
jorgensd Aug 27, 2026
654abce
Various fixes thanks to @finsberg and his Claude prompting. Summary o…
jorgensd Aug 27, 2026
9c5721e
Ruff
jorgensd Aug 27, 2026
4ec2733
Last fixes to make code nice.
jorgensd Aug 27, 2026
b5f35d4
Further simplifications
jorgensd Aug 27, 2026
6bb0733
Type-orama
jorgensd Aug 27, 2026
a3a6eb6
Add more typing
jorgensd Aug 27, 2026
b4041ec
Further cleanup. Personal notes:
jorgensd Aug 27, 2026
50fefb3
Move dFdu_adj outside of dependency loop in linear and nonlinear solver.
jorgensd Aug 27, 2026
874631c
Simplify assigning mixed parts to data. Start debugging the time dist…
jorgensd Aug 27, 2026
16289d3
Nighmare trying to be resolved.
jorgensd Aug 27, 2026
696d1f2
Add failing test. and updated AI slop code that doesn't work.
jorgensd Aug 27, 2026
84bc256
Fix code
jorgensd Aug 27, 2026
724cdc1
Add more info to demo
jorgensd Aug 27, 2026
c4672af
Final fix
jorgensd Aug 27, 2026
e32f860
Merge pull request #75 from scientificcomputing/dokken/timdep-nightmare
finsberg Aug 27, 2026
654950e
Fix mypy issues
finsberg Aug 27, 2026
1637188
Add another test. Increase perturbation to avoid floating issues. Als…
jorgensd Aug 27, 2026
6b371c3
Fix mypy issues
finsberg Aug 28, 2026
555ed89
Another mypy issue
finsberg Aug 28, 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
1 change: 1 addition & 0 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -27,5 +27,6 @@ repos:
rev: v2.3.0
hooks:
- id: mypy
language: system
files: ^src/|^tests/
args: ["--config-file", "pyproject.toml"]
78 changes: 70 additions & 8 deletions demos/time_distributed_control.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,23 +38,25 @@ def solve_heat(ctrls):
v = ufl.TestFunction(V)

f = dolfinx_adjoint.Function(V, name="source")
u_0 = dolfinx_adjoint.Function(V, name="solution")

F = ((u - u_0) / dt * v + nu * ufl.inner(ufl.grad(u), ufl.grad(v)) - f * v) * ufl.dx
u_prev = dolfinx_adjoint.Function(V, name="u_prev")
uh = dolfinx_adjoint.Function(V, name="solution")
dolfinx_adjoint.assign(0.0, uh)
F = ((u - u_prev) / dt * v + nu * ufl.inner(ufl.grad(u), ufl.grad(v)) - f * v) * ufl.dx
a, L = ufl.system(F)
mesh.topology.create_connectivity(mesh.topology.dim - 1, mesh.topology.dim)
exterior_facets = dolfinx.mesh.exterior_facet_indices(mesh.topology)
exterior_dofs = dolfinx.fem.locate_dofs_topological(V, mesh.topology.dim - 1, exterior_facets)

bc = dolfinx.fem.dirichletbc(0.0, exterior_dofs, V)

j = 0.5 * float(dt) * dolfinx_adjoint.assemble_scalar((u_0 - d) ** 2 * ufl.dx)
j = 0.5 * float(dt) * dolfinx_adjoint.assemble_scalar((uh - d) ** 2 * ufl.dx)

t_val = float(dt)
problem = dolfinx_adjoint.LinearProblem(
a,
L,
u=u_0,
u=uh,
bcs=[bc],
petsc_options={
"ksp_type": "preonly",
Expand All @@ -81,6 +83,7 @@ def solve_heat(ctrls):
dolfinx_adjoint.assign(ctrls[t_val], f)

# Update data function
dolfinx_adjoint.assign(uh, u_prev)

# Solve PDE
problem.solve()
Expand All @@ -90,12 +93,12 @@ def solve_heat(ctrls):
weight = 0.5
else:
weight = 1
j += weight * float(dt) * dolfinx_adjoint.assemble_scalar((u_0 - d) ** 2 * ufl.dx)
j += weight * float(dt) * dolfinx_adjoint.assemble_scalar((uh - d) ** 2 * ufl.dx)
# Update time
t_val += float(dt)
dolfinx_adjoint.assign(t_val, t)

return u_0, d, j
return uh, d, j


u, d, j = solve_heat(ctrls)
Expand All @@ -111,16 +114,75 @@ def solve_heat(ctrls):
J = j + dolfinx_adjoint.assemble_scalar(regularisation)
m = [pyadjoint.Control(c) for c in ctrls.values()]


rf = pyadjoint.ReducedFunctional(J, m)

# Check accuracy of gradient and Hessian using Taylor test
with pyadjoint.stop_annotating():
# Insert this diagnostic section into your demo right after J and m are defined:

# 1. Generate a non-zero base control point m_pert
m_pert = [dolfinx_adjoint.Function(V, name=f"pert_ctrl_{t_val}") for t_val in ctrls.keys()]
for c in m_pert:
c.x.array[:] = np.random.uniform(0.1, 1.0, size=c.x.array.shape)

# 2. Define random directions h
h = [pyadjoint.Control(dolfinx_adjoint.Function(V)) for _ in m]
for hi in h:
hi.control.x.array[:] = np.random.uniform(-0.1, 0.1, size=hi.control.x.array.shape)

print("\n=== 1. Taylor Test at NON-ZERO Control Point ===")
min_val_pert = pyadjoint.taylor_test(rf, m_pert, h)
print(f"Convergence rate at perturbed point: {min_val_pert:.4f}")
rf(m_pert)
print("\n=== 2. Second order taylor test at NON-ZERO Control Point ===")
dJdm = sum(drfi._ad_dot(hi) for drfi, hi in zip(rf.derivative(), h, strict=True))

H = rf.hessian([hi.control for hi in h])

# 2. Iterate and sum the Hessian dot products piecewise
dHddu = sum(Hi._ad_dot(hi) for Hi, hi in zip(H, h, strict=True))
min_val = pyadjoint.taylor_test(rf, m_pert, h, dJdm=dJdm, Hm=dHddu)
print(f"Convergence rate at perturbed point with Hessian: {min_val:.4f}")

print("\n=== 3. Direct Finite Difference Gradient Verification ===")
eps = 1e-6

# Compute Adjoint Directional Derivative at m_pert
rf(m_pert)
grad_adj = rf.derivative()
adj_dir_deriv = sum(g._ad_dot(hi) for g, hi in zip(grad_adj, h, strict=True))

# Forward Perturbation J(m + eps*h)
m_plus = [dolfinx_adjoint.Function(V) for _ in m_pert]
for mp, m_p, hi in zip(m_plus, m_pert, h, strict=True):
mp.x.array[:] = m_p.x.array[:] + eps * hi.control.x.array[:]
J_plus = float(rf(m_plus))

# Backward Perturbation J(m - eps*h)
m_minus = [dolfinx_adjoint.Function(V) for _ in m_pert]
for mm, m_p, hi in zip(m_minus, m_pert, h, strict=True):
mm.x.array[:] = m_p.x.array[:] - eps * hi.control.x.array[:]
J_minus = float(rf(m_minus))

# Central Finite Difference
fd_dir_deriv = (J_plus - J_minus) / (2 * eps)

print(f"Adjoint Directional Derivative: {adj_dir_deriv:.10e}")
print(f"Finite Difference Directional Dev: {fd_dir_deriv:.10e}")
rel_diff = abs(adj_dir_deriv - fd_dir_deriv) / (abs(fd_dir_deriv) + 1e-15)
print(f"Relative Mismatch: {rel_diff:.4e}")

assert rel_diff < 1e-4, f"Adjoint gradient mismatches finite differences! Relative error: {rel_diff}"

# Reset to ensure that we are at the original control point for the optimization
rf(list(ctrls.values()))

tape = pyadjoint.get_working_tape()
tape.visualise_dot("test.dot")

opt_ctrls = pyadjoint.minimize(
rf,
method="BFGS",
# method="Newton-CG",
options={"maxiter": 100, "disp": True},
)

Expand Down
1 change: 0 additions & 1 deletion src/dolfinx_adjoint/blocks/assembly.py
Original file line number Diff line number Diff line change
Expand Up @@ -188,7 +188,6 @@ def prepare_evaluate_adj(self, inputs, adj_inputs, relevant_dependencies):
c_rep = block_variable.saved_output
if coeff in self.form.coefficients():
replaced_coeffs[coeff] = c_rep

form = ufl.replace(self.form, replaced_coeffs)
return form

Expand Down
2 changes: 1 addition & 1 deletion src/dolfinx_adjoint/blocks/interpolation.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@
from ufl.algorithms.analysis import traverse_unique_terminals

from ..compat import get_interpolation_points
from ..types.function import Function, _create_function
from ..utils import unroll_dofmap
from ..types.function import _create_function, Function

if typing.TYPE_CHECKING:
from petsc4py import PETSc
Expand Down
Loading