DEBUG: diagnose the stochastic appctx loss in test_baseform_coarsening - #5386
Draft
pbrubeck wants to merge 1 commit into
Draft
DEBUG: diagnose the stochastic appctx loss in test_baseform_coarsening#5386pbrubeck wants to merge 1 commit into
pbrubeck wants to merge 1 commit into
Conversation
test_baseform_coarsening[scalar-mg] fails occasionally at nprocs = 1 when get_appctx returns None inside form_jacobian. CI reports only the assertion that add_hooks.__exit__ raises while unwinding, which discards the traceback of the error that caused it. Report a broken hook stack instead of asserting on it, and leave an in-flight exception alone so it reaches the report. Give the PETSc callbacks one appctx lookup that names the DM and the solver when the context is missing. Record the last 512 pushes and pops of a DM attribute stack, and print them with either report. Switch the parallel test steps off in the Linux default job. The failure is serial, so those runs are pure cost while this branch is re-run. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Description
AI-assisted (Claude Code)
Draft, for debugging. This branch exists to make one stochastic CI failure diagnosable. The
parallel test steps are switched off while it runs, so it must not be merged as it stands.
The failure
tests/firedrake/multigrid/test_poisson_gmg.py::test_baseform_coarsening[scalar-mg]failsoccasionally at
nprocs = 1(one instance). CI reports it as a bareAssertionErroratdmhooks.py:250, which is not where anything went wrong.The real error is in
solving_utils.form_jacobian:get_appctx(snes.getDM())returnedNone, that surfaced asPETSc.Error(101)out ofSNESSolve, and whileExitStackunwound it,add_hooks.__exit__asserted on a hook stack thatwas empty as well. The assertion is the failure pytest reports, and it discards the traceback of
the error that caused it.
The window is narrow. In
SNESSolve_KSPONLY,SNESComputeFunctionis line 27 andSNESComputeJacobianis line 41;form_functionperforms the identicalget_appctxlookup andsucceeded. Between the two, PETSc runs
SNESGetNormSchedule, a monitor block that is skipped(
snes->numbermonitorsis 0 here) andPetscTryTypeMethod(snes, update), for which Firedrakeregisters nothing. So the application context went missing from a live DM during
ctx._assemble_residual, and by unwind time the setup hooks had gone with it.I could not reproduce this, in isolation or otherwise. Ruled out so far: petsc4py holds
setAttrdata on the C object, where it survives collection, temporary-wrapper collection and reference
cycles routed through the shared dict; the DM cannot be silently rebuilt, since
DataSet.dmis acached_propertyon anObjectCachedDataSetheld inmesh._shared_data_cache, a plaindefaultdict(dict)with no eviction; and the push/pop of__appctx__balances exactly over allfour right-hand sides. The scalar-mg case survived 89 iterations of all eight parametrizations in
one process with
gc.set_threshold(1, 1, 1)and a forcedgc.collect()at the end of everyform_function. CI runs Python 3.14.4 against 3.12.13 locally, which is the obvious remainingdifference.
What's in it
dmhooks.add_hooks.__exit__no longer masks an in-flight exception. A broken hook stack nowraises a
RuntimeErrornaming the DM and the solver when nothing else is propagating, and warnsinstead of raising when something is, so the original error reaches the report intact. This is
the change that makes the next occurrence readable.
solving_utils._callback_appctxreplaces the five open-codedget_appctxlookups in thePETSc callbacks. A missing context is now a
RuntimeErrornaming the DM and the options prefix,rather than an
AttributeErroronNoneseveral lines later.dmhooks, printed by both of the above.A DM's stacks are touched a handful of times per solve, so recording the last 512 pushes and
pops costs nothing and shows which DM lost what, and when.
core.yml(nprocs2 to 8, and theparallel
tests/pyop2runs). The failure is serial, so the parallel runs are pure cost whilethis branch is being re-run. The
linux_extrajobs are untouched; they only run behind a label.What to do with it
Re-run the
nprocs = 1step until it trips. The report should then carry theRuntimeErrorfrom_callback_appctxtogether with the stack history, which says whether the appctx was popped, bywhat, and off which DM.
Notes for review
Two related weaknesses turned up while reading this code. Neither is addressed here, and neither
explains the failure, but both let a missing application context pass unnoticed:
mg/ufl_utils.py:447pushes an appctx onto the coarse DM and registers the matching teardownonly
if parentdm.getAttr("__setup_hooks__"). When that is falsy the push leaks, and theif get_appctx(newdm) is Noneguard then makes later solves reuse a stale context.dmhooks.get_transfer_managertreats a missing appctx as "not in a solve" and builds a freshTransferManager.test_baseform_coarsening[mixed-*]trips this today:reconstruct_functioncalls
get_transfer_manager(V.dm)for anIndexedProxyFunctionSpacecoming off aDirichletBC, and that sub-DM never receives an appctx. It warns and carries on; the samecondition in
form_jacobianis fatal.