Author: luc
Date: Wed Mar 24 22:11:51 2010
New Revision: 927202
URL: http://svn.apache.org/viewvc?rev=927202&view=rev
Log:
Fixed an error in events handling in ODE solvers. In some rare cases, events
occurring close to a step start were handled without truncating the step,
making them appear as is they occurred close to the step end
JIRA: MATH-358
Modified:
commons/proper/math/trunk/src/main/java/org/apache/commons/math/ode/nonstiff/AdamsBashforthIntegrator.java
commons/proper/math/trunk/src/main/java/org/apache/commons/math/ode/nonstiff/AdamsMoultonIntegrator.java
commons/proper/math/trunk/src/main/java/org/apache/commons/math/ode/nonstiff/EmbeddedRungeKuttaIntegrator.java
commons/proper/math/trunk/src/main/java/org/apache/commons/math/ode/nonstiff/RungeKuttaIntegrator.java
commons/proper/math/trunk/src/site/xdoc/changes.xml
commons/proper/math/trunk/src/test/java/org/apache/commons/math/ode/nonstiff/ClassicalRungeKuttaIntegratorTest.java
commons/proper/math/trunk/src/test/java/org/apache/commons/math/ode/nonstiff/DormandPrince853IntegratorTest.java
Modified:
commons/proper/math/trunk/src/main/java/org/apache/commons/math/ode/nonstiff/AdamsBashforthIntegrator.java
URL:
http://svn.apache.org/viewvc/commons/proper/math/trunk/src/main/java/org/apache/commons/math/ode/nonstiff/AdamsBashforthIntegrator.java?rev=927202&r1=927201&r2=927202&view=diff
==============================================================================
---
commons/proper/math/trunk/src/main/java/org/apache/commons/math/ode/nonstiff/AdamsBashforthIntegrator.java
(original)
+++
commons/proper/math/trunk/src/main/java/org/apache/commons/math/ode/nonstiff/AdamsBashforthIntegrator.java
Wed Mar 24 22:11:51 2010
@@ -271,8 +271,16 @@ public class AdamsBashforthIntegrator ex
if (manager.evaluateStep(interpolatorTmp)) {
final double dt = manager.getEventTime() - stepStart;
if (Math.abs(dt) <= Math.ulp(stepStart)) {
- // rejecting the step would lead to a too small
next step, we accept it
- loop = false;
+ // we cannot simply truncate the step, reject the
current computation
+ // and let the loop compute another state with the
truncated step.
+ // it is so small (much probably exactly 0 due to
limited accuracy)
+ // that the code above would fail handling it.
+ // So we set up an artificial 0 size step by
copying states
+ interpolator.storeTime(stepStart);
+ System.arraycopy(y, 0, yTmp, 0, y0.length);
+ hNew = 0;
+ stepSize = 0;
+ loop = false;
} else {
// reject the step to match exactly the next
switch time
hNew = dt;
Modified:
commons/proper/math/trunk/src/main/java/org/apache/commons/math/ode/nonstiff/AdamsMoultonIntegrator.java
URL:
http://svn.apache.org/viewvc/commons/proper/math/trunk/src/main/java/org/apache/commons/math/ode/nonstiff/AdamsMoultonIntegrator.java?rev=927202&r1=927201&r2=927202&view=diff
==============================================================================
---
commons/proper/math/trunk/src/main/java/org/apache/commons/math/ode/nonstiff/AdamsMoultonIntegrator.java
(original)
+++
commons/proper/math/trunk/src/main/java/org/apache/commons/math/ode/nonstiff/AdamsMoultonIntegrator.java
Wed Mar 24 22:11:51 2010
@@ -289,8 +289,16 @@ public class AdamsMoultonIntegrator exte
if (manager.evaluateStep(interpolatorTmp)) {
final double dt = manager.getEventTime() - stepStart;
if (Math.abs(dt) <= Math.ulp(stepStart)) {
- // rejecting the step would lead to a too small
next step, we accept it
- loop = false;
+ // we cannot simply truncate the step, reject the
current computation
+ // and let the loop compute another state with the
truncated step.
+ // it is so small (much probably exactly 0 due to
limited accuracy)
+ // that the code above would fail handling it.
+ // So we set up an artificial 0 size step by
copying states
+ interpolator.storeTime(stepStart);
+ System.arraycopy(y, 0, yTmp, 0, y0.length);
+ hNew = 0;
+ stepSize = 0;
+ loop = false;
} else {
// reject the step to match exactly the next
switch time
hNew = dt;
Modified:
commons/proper/math/trunk/src/main/java/org/apache/commons/math/ode/nonstiff/EmbeddedRungeKuttaIntegrator.java
URL:
http://svn.apache.org/viewvc/commons/proper/math/trunk/src/main/java/org/apache/commons/math/ode/nonstiff/EmbeddedRungeKuttaIntegrator.java?rev=927202&r1=927201&r2=927202&view=diff
==============================================================================
---
commons/proper/math/trunk/src/main/java/org/apache/commons/math/ode/nonstiff/EmbeddedRungeKuttaIntegrator.java
(original)
+++
commons/proper/math/trunk/src/main/java/org/apache/commons/math/ode/nonstiff/EmbeddedRungeKuttaIntegrator.java
Wed Mar 24 22:11:51 2010
@@ -292,8 +292,16 @@ public abstract class EmbeddedRungeKutta
if (manager.evaluateStep(interpolator)) {
final double dt = manager.getEventTime() - stepStart;
if (Math.abs(dt) <= Math.ulp(stepStart)) {
- // rejecting the step would lead to a too small next step,
we accept it
- loop = false;
+ // we cannot simply truncate the step, reject the current
computation
+ // and let the loop compute another state with the truncated
step.
+ // it is so small (much probably exactly 0 due to limited
accuracy)
+ // that the code above would fail handling it.
+ // So we set up an artificial 0 size step by copying states
+ interpolator.storeTime(stepStart);
+ System.arraycopy(y, 0, yTmp, 0, y0.length);
+ hNew = 0;
+ stepSize = 0;
+ loop = false;
} else {
// reject the step to match exactly the next switch time
hNew = dt;
Modified:
commons/proper/math/trunk/src/main/java/org/apache/commons/math/ode/nonstiff/RungeKuttaIntegrator.java
URL:
http://svn.apache.org/viewvc/commons/proper/math/trunk/src/main/java/org/apache/commons/math/ode/nonstiff/RungeKuttaIntegrator.java?rev=927202&r1=927201&r2=927202&view=diff
==============================================================================
---
commons/proper/math/trunk/src/main/java/org/apache/commons/math/ode/nonstiff/RungeKuttaIntegrator.java
(original)
+++
commons/proper/math/trunk/src/main/java/org/apache/commons/math/ode/nonstiff/RungeKuttaIntegrator.java
Wed Mar 24 22:11:51 2010
@@ -172,8 +172,15 @@ public abstract class RungeKuttaIntegrat
if (manager.evaluateStep(interpolator)) {
final double dt = manager.getEventTime() - stepStart;
if (Math.abs(dt) <= Math.ulp(stepStart)) {
- // rejecting the step would lead to a too small next step, we
accept it
- loop = false;
+ // we cannot simply truncate the step, reject the current
computation
+ // and let the loop compute another state with the truncated
step.
+ // it is so small (much probably exactly 0 due to limited
accuracy)
+ // that the code above would fail handling it.
+ // So we set up an artificial 0 size step by copying states
+ interpolator.storeTime(stepStart);
+ System.arraycopy(y, 0, yTmp, 0, y0.length);
+ stepSize = 0;
+ loop = false;
} else {
// reject the step to match exactly the next switch time
stepSize = dt;
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=927202&r1=927201&r2=927202&view=diff
==============================================================================
--- commons/proper/math/trunk/src/site/xdoc/changes.xml (original)
+++ commons/proper/math/trunk/src/site/xdoc/changes.xml Wed Mar 24 22:11:51 2010
@@ -90,6 +90,11 @@ The <action> type attribute can be add,u
used to solve Boundary Value Problems (BVP). There are no
implementations (yet)
of BVP solvers in the library.
</action>
+ <action dev="luc" type="fix" issue="MATH-358" >
+ Fixed an error in events handling in ODE solvers. In some rare cases,
events
+ occurring close to a step start were handled without truncating the
step, making
+ them appear as is they occurred close to the step end
+ </action>
<action dev="luc" type="fix" >
Fixed a problem with getInterpolatedDerivatives returning zero
derivatives when
an ODE step handler is configured to not use interpolation. It now
returns a
Modified:
commons/proper/math/trunk/src/test/java/org/apache/commons/math/ode/nonstiff/ClassicalRungeKuttaIntegratorTest.java
URL:
http://svn.apache.org/viewvc/commons/proper/math/trunk/src/test/java/org/apache/commons/math/ode/nonstiff/ClassicalRungeKuttaIntegratorTest.java?rev=927202&r1=927201&r2=927202&view=diff
==============================================================================
---
commons/proper/math/trunk/src/test/java/org/apache/commons/math/ode/nonstiff/ClassicalRungeKuttaIntegratorTest.java
(original)
+++
commons/proper/math/trunk/src/test/java/org/apache/commons/math/ode/nonstiff/ClassicalRungeKuttaIntegratorTest.java
Wed Mar 24 22:11:51 2010
@@ -41,6 +41,59 @@ public class ClassicalRungeKuttaIntegrat
super(name);
}
+ public void testMissedEndEvent() throws IntegratorException,
DerivativeException {
+ final double t0 = 1878250320.0000029;
+ final double tEvent = 1878250379.9999986;
+ final double[] k = { 1.0e-4, 1.0e-5, 1.0e-6 };
+ FirstOrderDifferentialEquations ode = new
FirstOrderDifferentialEquations() {
+
+ public int getDimension() {
+ return k.length;
+ }
+
+ public void computeDerivatives(double t, double[] y, double[] yDot) {
+ for (int i = 0; i < y.length; ++i) {
+ yDot[i] = k[i] * y[i];
+ }
+ }
+ };
+
+ ClassicalRungeKuttaIntegrator integrator = new
ClassicalRungeKuttaIntegrator(60.0);
+
+ double[] y0 = new double[k.length];
+ for (int i = 0; i < y0.length; ++i) {
+ y0[i] = i + 1;
+ }
+ double[] y = new double[k.length];
+
+ double finalT = integrator.integrate(ode, t0, y0, tEvent, y);
+ Assert.assertEquals(tEvent, finalT, 5.0e-6);
+ for (int i = 0; i < y.length; ++i) {
+ Assert.assertEquals(y0[i] * Math.exp(k[i] * (finalT - t0)), y[i],
1.0e-9);
+ }
+
+ integrator.addEventHandler(new EventHandler() {
+
+ public void resetState(double t, double[] y) {
+ }
+
+ public double g(double t, double[] y) {
+ return t - tEvent;
+ }
+
+ public int eventOccurred(double t, double[] y, boolean increasing) {
+ Assert.assertEquals(tEvent, t, 5.0e-6);
+ return CONTINUE;
+ }
+ }, Double.POSITIVE_INFINITY, 1.0e-20, 100);
+ finalT = integrator.integrate(ode, t0, y0, tEvent + 120, y);
+ Assert.assertEquals(tEvent + 120, finalT, 5.0e-6);
+ for (int i = 0; i < y.length; ++i) {
+ Assert.assertEquals(y0[i] * Math.exp(k[i] * (finalT - t0)), y[i],
1.0e-9);
+ }
+
+ }
+
public void testSanityChecks() {
try {
TestProblem1 pb = new TestProblem1();
Modified:
commons/proper/math/trunk/src/test/java/org/apache/commons/math/ode/nonstiff/DormandPrince853IntegratorTest.java
URL:
http://svn.apache.org/viewvc/commons/proper/math/trunk/src/test/java/org/apache/commons/math/ode/nonstiff/DormandPrince853IntegratorTest.java?rev=927202&r1=927201&r2=927202&view=diff
==============================================================================
---
commons/proper/math/trunk/src/test/java/org/apache/commons/math/ode/nonstiff/DormandPrince853IntegratorTest.java
(original)
+++
commons/proper/math/trunk/src/test/java/org/apache/commons/math/ode/nonstiff/DormandPrince853IntegratorTest.java
Wed Mar 24 22:11:51 2010
@@ -18,6 +18,7 @@
package org.apache.commons.math.ode.nonstiff;
import org.apache.commons.math.ode.DerivativeException;
+import org.apache.commons.math.ode.FirstOrderDifferentialEquations;
import org.apache.commons.math.ode.FirstOrderIntegrator;
import org.apache.commons.math.ode.IntegratorException;
import org.apache.commons.math.ode.TestProblem1;
@@ -40,6 +41,62 @@ public class DormandPrince853IntegratorT
super(name);
}
+ public void testMissedEndEvent() throws IntegratorException,
DerivativeException {
+ final double t0 = 1878250320.0000029;
+ final double tEvent = 1878250379.9999986;
+ final double[] k = { 1.0e-4, 1.0e-5, 1.0e-6 };
+ FirstOrderDifferentialEquations ode = new
FirstOrderDifferentialEquations() {
+
+ public int getDimension() {
+ return k.length;
+ }
+
+ public void computeDerivatives(double t, double[] y, double[] yDot) {
+ for (int i = 0; i < y.length; ++i) {
+ yDot[i] = k[i] * y[i];
+ }
+ }
+ };
+
+ DormandPrince853Integrator integrator = new
DormandPrince853Integrator(0.0, 100.0,
+
1.0e-10, 1.0e-10);
+
+ double[] y0 = new double[k.length];
+ for (int i = 0; i < y0.length; ++i) {
+ y0[i] = i + 1;
+ }
+ double[] y = new double[k.length];
+
+ integrator.setInitialStepSize(60.0);
+ double finalT = integrator.integrate(ode, t0, y0, tEvent, y);
+ Assert.assertEquals(tEvent, finalT, 5.0e-6);
+ for (int i = 0; i < y.length; ++i) {
+ Assert.assertEquals(y0[i] * Math.exp(k[i] * (finalT - t0)), y[i],
1.0e-9);
+ }
+
+ integrator.setInitialStepSize(60.0);
+ integrator.addEventHandler(new EventHandler() {
+
+ public void resetState(double t, double[] y) {
+ }
+
+ public double g(double t, double[] y) {
+ return t - tEvent;
+ }
+
+ public int eventOccurred(double t, double[] y, boolean increasing) {
+ Assert.assertEquals(tEvent, t, 5.0e-6);
+ return CONTINUE;
+ }
+ }, Double.POSITIVE_INFINITY, 1.0e-20, 100);
+ finalT = integrator.integrate(ode, t0, y0, tEvent + 120, y);
+ Assert.assertEquals(tEvent + 120, finalT, 5.0e-6);
+ for (int i = 0; i < y.length; ++i) {
+ Assert.assertEquals(y0[i] * Math.exp(k[i] * (finalT - t0)), y[i],
1.0e-9);
+ }
+
+ }
+
public void testDimensionCheck() {
try {
TestProblem1 pb = new TestProblem1();