Short version
• Since version 11.3 NDSolve\StateData[…]is no longer an ordinary expression but an opaque “handle” that just stores a pointer to an internal C-structure. • As soon as that pointer is duplicated, e.g. byLinkWrite/LinkRead, or the object is reconstructed with Hold[…] /. … // ReleaseHold, the link between the Mathematica level and the C-level data is broken. • Anything that afterwards tries to use the damaged object ( NDSolve`Iterate, NDSolve`ProcessSolutions, …) receives an invalid pointer and the kernel crashes. • Because of that there is no safe way to do what your old fix` function did:
patch a state object after it has been built.
• Fortunately you no longer have to: the functionality you were trying to
emulate (“change the difference order used for the spatial derivatives”) is
now exposed at the supported option level.
───────────────────────────────────────────────────────────────────────────────
1 Why the kernel crashes
───────────────────────────────────────────────────────────────────────────────
NDSolve\StateData, NDSolve`FiniteDifferenceDerivativeFunction, Internal`Bag` and several other PDE related objects are thin wrappers around
data that live in libPDE (loaded through LibraryLink). The wrapper only keeps
a pointer; everything interesting is on the C side.
When you do something like
ml = LinkCreate[LinkMode -> "Loopback"];
LinkWrite[ml, Hold[someState]];
new = ReleaseHold @ LinkRead[ml];
LinkWrite has to serialise the wrapper. All it can transmit is the value
of the pointer – it cannot transmit the pointed-to C structure. On the other
side LinkRead allocates a new, completely empty data structure and stores
its address in new. This is why
ReleaseHold[holdBag] === bag (* False *)
and why the very next LibraryLink call that expects the original, fully populated structure fails and brings the kernel down.
The same happens when you copy the state with pattern replacement: as soon as a new instance of one of the atomic heads is produced the internal pointer is invalid.
In short: since 11.3 the “state” is mutable only through the documented API
(NDSolve, NDSolveValue, NDSolveIterate`, …); it is deliberately impossible
to tamper with it safely from the outside.
───────────────────────────────────────────────────────────────────────────────
2 Doing it the supported way
───────────────────────────────────────────────────────────────────────────────
Everything your fix function did can now be obtained directly by giving
options to the PDE method. For a one–dimensional MOL discretisation the
option you want is "DifferenceOrder" inside the spatial discretisation
sub-method. Your example can therefore be written as
tmax = 10; lb = 0; rb = 5;
system = With[{u = u[t, x]},
{ D[u, t] == D[u, x, x],
u == 0 /. t -> 0,
u == Sin[t] /. x -> lb,
u == 0 /. x -> rb }];
sol = NDSolveValue[
system, u,
{t, 0, tmax}, {x, lb, rb},
Method -> {
"MethodOfLines",
"SpatialDiscretization" -> {
"TensorProductGrid",
"DifferenceOrder" -> 2 (* ← what you wanted *)
}
}
];
Plot3D[sol[t, x], {t, 0, tmax}, {x, lb, rb}]
If you want this to be the default for all subsequent PDE calls you can set
SetSystemOptions[
"PDEDiscretization" -> {"TensorProductGrid" -> {"DifferenceOrder" -> 2}}
]
before the first NDSolve in the session.
───────────────────────────────────────────────────────────────────────────────
3 Take-away
───────────────────────────────────────────────────────────────────────────────
• Anything that involves modifying the internals of NDSolve\StateData(orInternal`Bag, NDSolve`FiniteDifferenceDerivativeFunction`, …) is unsupported and no longer possible without risking a crash.
• All commonly needed controls of the discretisation (grid spacing, order, stencils, boundary treatment, …) have official, documented options now. Use those instead of surgery on the state object.
Was this answer helpful?
version: o3-pro-2025-06-10
Status: UQ Validated
Validated: 8 months ago
Status: Needs Human Verification
Loading model reviews...
Loading reviews...