Skip to content

idas: wire NLSstg LSolveFn in IDASetNonlinearSolverSensStg to prevent NULL crash#907

Open
OfficialDelta wants to merge 1 commit intollnl:mainfrom
OfficialDelta:fix/idas-staggered-fsa-null-lsolvefn
Open

idas: wire NLSstg LSolveFn in IDASetNonlinearSolverSensStg to prevent NULL crash#907
OfficialDelta wants to merge 1 commit intollnl:mainfrom
OfficialDelta:fix/idas-staggered-fsa-null-lsolvefn

Conversation

@OfficialDelta
Copy link
Copy Markdown

Summary

IDASetNonlinearSolverSensStg() does not wire LSolveFn (or LSetupFn) on the replacement NLSstg. These callbacks are only set later by idaNlsInitSensStg(), which runs inside IDAInitialSetup(). If the staggered sensitivity NLS is replaced after IDACalcIC() has already run IDAInitialSetup() (setting ida_SetupDone = TRUE), the subsequent IDASolve() call skips IDAInitialSetup(), and Newton iterations on the new NLSstg dereference a NULL LSolveFn, causing a segmentation fault.

Root Cause

  1. IDACalcIC() calls IDAInitialSetup(), which calls idaNlsInitSensStg() — this correctly sets LSolveFn = idaNlsLSolveSensStg on NLSstg and marks ida_SetupDone = TRUE.
  2. User calls IDASetNonlinearSolverSensStg(ida_mem, NLS_new) to replace NLSstg with a fresh SUNNonlinSol_NewtonSens. The new NLS has LSolveFn = NULL.
  3. IDASolve() checks ida_SetupDone and skips IDAInitialSetup() entirely.
  4. The Newton solver on NLSstg calls LSolveFn(...) → NULL pointer dereference → SIGSEGV.

The same crash pattern can occur even without explicit user NLS replacement, in any SUNDIALS configuration where idaNlsInitSensStg() has not yet run when the first Newton iteration fires on the staggered NLS.

Fix

Wire LSolveFn and LSetupFn eagerly in IDASetNonlinearSolverSensStg(), immediately after setting up the NLS, if the linear solver is already attached. This mirrors the existing pattern in idaNlsInitSensStg() but ensures the callbacks are present regardless of whether IDAInitialSetup() runs again.

The fix is safe when the linear solver is not yet set (ida_lsolve == NULL): in that case the early wiring is skipped and idaNlsInitSensStg() handles it during the normal init path.

Minimal Reproduction

A 10-species linear chain ODE with staggered FSA (Ns=10) demonstrates the crash:

// After IDACalcIC (sets ida_SetupDone=TRUE):
SUNNonlinearSolver NLS_new = SUNNonlinSol_NewtonSens(Ns, delta, sunctx);
IDASetNonlinearSolverSensStg(ida_mem, NLS_new);  // LSolveFn = NULL on new NLS
IDASolve(ida_mem, t_final, &tret, yy, yp, IDA_NORMAL);  // SIGSEGV

With the fix applied, the same sequence completes without crash, and FSA gradients match finite-difference gradients with cosine similarity > 0.999.

Testing

  • Validated on SUNDIALS 7.6.0 and 7.7.0 conda builds
  • 10-species system: crash reproduced without fix, FSA/FD cosine = 0.9994 with fix
  • 764K-species production system: no SEGFAULT with fix applied

Files Changed

  • src/idas/idas_nls_stg.c: Added early LSolveFn/LSetupFn wiring in IDASetNonlinearSolverSensStg()

… NULL crash

IDASetNonlinearSolverSensStg() did not set LSolveFn/LSetupFn on the
replacement NLSstg. These callbacks were only set by idaNlsInitSensStg()
during IDAInitialSetup(). If NLSstg is replaced after IDACalcIC()
(which marks ida_SetupDone=TRUE), the next IDASolve() skips
IDAInitialSetup() and Newton iterations crash with NULL LSolveFn.

Fix: eagerly wire LSolveFn and LSetupFn in IDASetNonlinearSolverSensStg()
when the linear solver is already attached.
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.

1 participant