Closed andrewsgoetz closed 3 years ago
Hi @andrewsgoetz
Your analysis seems fine to me. I wonder what made units test fail with the check. Could you set up a first merge request with this first fix attempt, it would be interesting to understand what happens there.
Also for the final merge request, beware that it would be better to check times within one ULP, and that the same fix must be done in the field version of the integrator.
Anyway, its great to have identified this bug!
I have made a merge request for my first fix attempt which did not work: https://github.com/Hipparchus-Math/hipparchus/pull/113.
The failing tests are:
DormandPrince54IntegratorTest.testUnstableDerivative DormandPrince853IntegratorTest.testUnstableDerivative HighamHall54IntegratorTest.testUnstableDerivative EventFilterTest.testTwoOppositeFilters EventFilterTest.testIncreasingOnly EventFilterTest.testDecreasingOnly CloseEventsTest.testSimultaneousDiscontinuousEventsAfterReset CloseEventsTest.testSimultaneousDiscontinuousEventsAfterResetReverse FieldCloseEventsTest.testSimultaneousDiscontinuousEventsAfterReset FieldCloseEventsTest.testSimultaneousDiscontinuousEventsAfterResetReverse
If you don't see any obvious way to patch this up I will make a second merge request with my fix that changes AbstractODEStateInterpolator
and AbstractFieldODEStateInterpolator
.
When running the following snippet:
the output is
Although this example is rather artificial (why reset derivatives at the end of integration?) it was extracted from this more realistic example (using Orekit):
which prints out
The first snippet is intended to capture the underlying basics of what is going on in the Orekit example which cause the NaNs. In the Orekit example, the event occurring at the end of integration is the end of the maneuver, which causes a reset of derivatives. From now on I will discuss the first Hipparchus example.
Digging deeper, what happens is that the event at the end of the integration causes a degenerate
ODEStateInterpolator
to be added to the end of the internal list maintained in theDenseOutputModel
whose "previous" and "current" states are the same and both at the end time of the integration. The call toDenseOutputModel.getInterpolatedState(final double time)
delegates to the methodAbstractODEStateInterpolator.getInterpolatedState(final double time)
of this final degenerate interpolator. Here is the current implementation ofAbstractODEStateInterpolator.getInterpolatedState(final double time)
:The NaN appears at line 166 as
theta
is computed as 0/0.As for the fix: I was looking into ways of stopping the degenerate
ODEStateInterpolator
from being added to theDenseOutputModel
(for example, inAbstractIntegrator.acceptStep(final AbstractODEStateInterpolator interpolator, final double tEnd)
) copying the check forisLastStep
from line 425 to occur also at line 359) but my attempts caused various unit tests to fail. The easiest fix I have found is to allow the degenerateODEStateInterpolator
to be added to theDenseOutputModel
and to just add simple check to the implementation ofAbstractODEStateInterpolator.getInterpolatedState(final double time)
:I can submit this change as a pull request if it is agreed that this is a bug and the fix is good.