Author: luc Date: Wed Oct 26 09:08:22 2011 New Revision: 1189086 URL: http://svn.apache.org/viewvc?rev=1189086&view=rev Log: Fixed an event resetting issue in ODE.
When several discrete events occur during the same ODE integration step, they are handled chronologically or reverse chronologically depending on the integration direction. If one of the event truncates the step (for example because its eventOccurred method returns RESET or RESET_DERIVATIVES for example), the stepAccepted method of the pending events later in the step were not called. This implied that in the next step, these events were still referring to data from previous step, they had lost synchronization with the integrator. JIRA: MATH-695 Modified: commons/proper/math/trunk/src/main/java/org/apache/commons/math/ode/AbstractIntegrator.java commons/proper/math/trunk/src/site/xdoc/changes.xml commons/proper/math/trunk/src/test/java/org/apache/commons/math/ode/events/EventStateTest.java Modified: commons/proper/math/trunk/src/main/java/org/apache/commons/math/ode/AbstractIntegrator.java URL: http://svn.apache.org/viewvc/commons/proper/math/trunk/src/main/java/org/apache/commons/math/ode/AbstractIntegrator.java?rev=1189086&r1=1189085&r2=1189086&view=diff ============================================================================== --- commons/proper/math/trunk/src/main/java/org/apache/commons/math/ode/AbstractIntegrator.java (original) +++ commons/proper/math/trunk/src/main/java/org/apache/commons/math/ode/AbstractIntegrator.java Wed Oct 26 09:08:22 2011 @@ -40,7 +40,6 @@ import org.apache.commons.math.ode.sampl import org.apache.commons.math.ode.sampling.StepHandler; import org.apache.commons.math.util.FastMath; import org.apache.commons.math.util.Incrementor; -import org.apache.commons.math.util.MathUtils; import org.apache.commons.math.util.Precision; /** @@ -278,7 +277,6 @@ public abstract class AbstractIntegrator double previousT = interpolator.getGlobalPreviousTime(); final double currentT = interpolator.getGlobalCurrentTime(); - resetOccurred = false; // initialize the events states if needed if (! statesInitialized) { @@ -332,6 +330,9 @@ public abstract class AbstractIntegrator if (isLastStep) { // the event asked to stop integration System.arraycopy(eventY, 0, y, 0, y.length); + for (final EventState remaining : occuringEvents) { + remaining.stepAccepted(eventT, eventY); + } return eventT; } @@ -341,6 +342,9 @@ public abstract class AbstractIntegrator System.arraycopy(eventY, 0, y, 0, y.length); computeDerivatives(eventT, y, yDot); resetOccurred = true; + for (final EventState remaining : occuringEvents) { + remaining.stepAccepted(eventT, eventY); + } return eventT; } Modified: commons/proper/math/trunk/src/site/xdoc/changes.xml URL: http://svn.apache.org/viewvc/commons/proper/math/trunk/src/site/xdoc/changes.xml?rev=1189086&r1=1189085&r2=1189086&view=diff ============================================================================== --- commons/proper/math/trunk/src/site/xdoc/changes.xml (original) +++ commons/proper/math/trunk/src/site/xdoc/changes.xml Wed Oct 26 09:08:22 2011 @@ -52,6 +52,9 @@ The <action> type attribute can be add,u If the output is not quite correct, check for invisible trailing spaces! --> <release version="3.0" date="TBD" description="TBD"> + <action dev="luc" type="fix" issue="MATH-695"> + Fixed an event resetting issue in ODE. + </action> <action dev="erans" type="update" issue="MATH-696"> Default implementation for "addToEntry" and "multiplyEntry" in "AbstractRealMatrix". Modified: commons/proper/math/trunk/src/test/java/org/apache/commons/math/ode/events/EventStateTest.java URL: http://svn.apache.org/viewvc/commons/proper/math/trunk/src/test/java/org/apache/commons/math/ode/events/EventStateTest.java?rev=1189086&r1=1189085&r2=1189086&view=diff ============================================================================== --- commons/proper/math/trunk/src/test/java/org/apache/commons/math/ode/events/EventStateTest.java (original) +++ commons/proper/math/trunk/src/test/java/org/apache/commons/math/ode/events/EventStateTest.java Wed Oct 26 09:08:22 2011 @@ -19,6 +19,8 @@ package org.apache.commons.math.ode.even import org.apache.commons.math.analysis.solvers.BrentSolver; +import org.apache.commons.math.ode.FirstOrderDifferentialEquations; +import org.apache.commons.math.ode.nonstiff.DormandPrince853Integrator; import org.apache.commons.math.ode.sampling.AbstractStepInterpolator; import org.apache.commons.math.ode.sampling.DummyStepInterpolator; import org.junit.Assert; @@ -73,4 +75,64 @@ public class EventStateTest { } + // Jira: MATH-695 + @Test + public void testIssue695() { + + FirstOrderDifferentialEquations equation = new FirstOrderDifferentialEquations() { + + public int getDimension() { + return 1; + } + + public void computeDerivatives(double t, double[] y, double[] yDot) { + yDot[0] = 1.0; + } + }; + + DormandPrince853Integrator integrator = new DormandPrince853Integrator(0.001, 1000, 1.0e-14, 1.0e-14); + integrator.addEventHandler(new ResettingEvent(10.99), 0.1, 1.0e-9, 1000); + integrator.addEventHandler(new ResettingEvent(11.01), 0.1, 1.0e-9, 1000); + integrator.setInitialStepSize(3.0); + + double target = 30.0; + double[] y = new double[1]; + double tEnd = integrator.integrate(equation, 0.0, y, target, y); + Assert.assertEquals(target, tEnd, 1.0e-10); + Assert.assertEquals(32.0, y[0], 1.0e-10); + + } + + private static class ResettingEvent implements EventHandler { + + private static double lastTriggerTime = Double.NEGATIVE_INFINITY; + private final double tEvent; + + public ResettingEvent(final double tEvent) { + this.tEvent = tEvent; + } + + public double g(double t, double[] y) { + // the bug corresponding to issue 695 causes the g function + // to be called at obsolete times t despite an event + // occurring later has already been triggered. + // When this occurs, the following assertion is violated + Assert.assertTrue("going backard in time! (" + t + " < " + lastTriggerTime + ")", + t >= lastTriggerTime); + return t - tEvent; + } + + public Action eventOccurred(double t, double[] y, boolean increasing) { + // remember in a class variable when the event was triggered + lastTriggerTime = t; + return Action.RESET_STATE; + } + + public void resetState(double t, double[] y) { + y[0] += 1.0; + } + + }; + + }