/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.commons.math4.ode.events; import org.apache.commons.math4.analysis.solvers.BrentSolver; import org.apache.commons.math4.exception.DimensionMismatchException; import org.apache.commons.math4.exception.MaxCountExceededException; import org.apache.commons.math4.exception.NoBracketingException; import org.apache.commons.math4.exception.NumberIsTooSmallException; import org.apache.commons.math4.ode.ExpandableStatefulODE; import org.apache.commons.math4.ode.FirstOrderDifferentialEquations; import org.apache.commons.math4.ode.SecondaryEquations; import org.apache.commons.math4.ode.events.EventHandler; import org.apache.commons.math4.ode.events.EventState; import org.apache.commons.math4.ode.nonstiff.DormandPrince853Integrator; import org.apache.commons.math4.ode.nonstiff.LutherIntegrator; import org.apache.commons.math4.ode.sampling.AbstractStepInterpolator; import org.apache.commons.math4.ode.sampling.DummyStepInterpolator; import org.junit.Assert; import org.junit.Test; public class EventStateTest { // JIRA: MATH-322 @Test public void closeEvents() throws MaxCountExceededException, NoBracketingException { final double r1 = 90.0; final double r2 = 135.0; final double gap = r2 - r1; final double tolerance = 0.1; EventState es = new EventState(new CloseEventsGenerator(r1, r2), 1.5 * gap, tolerance, 100, new BrentSolver(tolerance)); es.setExpandable(new ExpandableStatefulODE(new FirstOrderDifferentialEquations() { @Override public int getDimension() { return 0; } @Override public void computeDerivatives(double t, double[] y, double[] yDot) { } })); AbstractStepInterpolator interpolator = new DummyStepInterpolator(new double[0], new double[0], true); interpolator.storeTime(r1 - 2.5 * gap); interpolator.shift(); interpolator.storeTime(r1 - 1.5 * gap); es.reinitializeBegin(interpolator); interpolator.shift(); interpolator.storeTime(r1 - 0.5 * gap); Assert.assertFalse(es.evaluateStep(interpolator)); interpolator.shift(); interpolator.storeTime(0.5 * (r1 + r2)); Assert.assertTrue(es.evaluateStep(interpolator)); Assert.assertEquals(r1, es.getEventTime(), tolerance); es.stepAccepted(es.getEventTime(), new double[0]); interpolator.shift(); interpolator.storeTime(r2 + 0.4 * gap); Assert.assertTrue(es.evaluateStep(interpolator)); Assert.assertEquals(r2, es.getEventTime(), tolerance); } // Jira: MATH-695 @Test public void testIssue695() throws DimensionMismatchException, NumberIsTooSmallException, MaxCountExceededException, NoBracketingException { FirstOrderDifferentialEquations equation = new FirstOrderDifferentialEquations() { @Override public int getDimension() { return 1; } @Override 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; } @Override public void init(double t0, double[] y0, double t) { } @Override 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; } @Override 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; } @Override public void resetState(double t, double[] y) { y[0] += 1.0; } } // Jira: MATH-965 @Test public void testIssue965() throws DimensionMismatchException, NumberIsTooSmallException, MaxCountExceededException, NoBracketingException { ExpandableStatefulODE equation = new ExpandableStatefulODE(new FirstOrderDifferentialEquations() { @Override public int getDimension() { return 1; } @Override public void computeDerivatives(double t, double[] y, double[] yDot) { yDot[0] = 2.0; } }); equation.setTime(0.0); equation.setPrimaryState(new double[1]); equation.addSecondaryEquations(new SecondaryEquations() { @Override public int getDimension() { return 1; } @Override public void computeDerivatives(double t, double[] primary, double[] primaryDot, double[] secondary, double[] secondaryDot) { secondaryDot[0] = -3.0; } }); int index = equation.getSecondaryMappers()[0].getFirstIndex(); DormandPrince853Integrator integrator = new DormandPrince853Integrator(0.001, 1000, 1.0e-14, 1.0e-14); integrator.addEventHandler(new SecondaryStateEvent(index, -3.0), 0.1, 1.0e-9, 1000); integrator.setInitialStepSize(3.0); integrator.integrate(equation, 30.0); Assert.assertEquals( 1.0, equation.getTime(), 1.0e-10); Assert.assertEquals( 2.0, equation.getPrimaryState()[0], 1.0e-10); Assert.assertEquals(-3.0, equation.getSecondaryState(0)[0], 1.0e-10); } private static class SecondaryStateEvent implements EventHandler { private int index; private final double target; public SecondaryStateEvent(final int index, final double target) { this.index = index; this.target = target; } @Override public void init(double t0, double[] y0, double t) { } @Override public double g(double t, double[] y) { return y[index] - target; } @Override public Action eventOccurred(double t, double[] y, boolean increasing) { return Action.STOP; } @Override public void resetState(double t, double[] y) { } } @Test public void testEventsCloserThanThreshold() throws DimensionMismatchException, NumberIsTooSmallException, MaxCountExceededException, NoBracketingException { FirstOrderDifferentialEquations equation = new FirstOrderDifferentialEquations() { @Override public int getDimension() { return 1; } @Override public void computeDerivatives(double t, double[] y, double[] yDot) { yDot[0] = 1.0; } }; LutherIntegrator integrator = new LutherIntegrator(20.0); CloseEventsGenerator eventsGenerator = new CloseEventsGenerator(9.0 - 1.0 / 128, 9.0 + 1.0 / 128); integrator.addEventHandler(eventsGenerator, 1.0, 0.02, 1000); double[] y = new double[1]; double tEnd = integrator.integrate(equation, 0.0, y, 100.0, y); Assert.assertEquals( 2, eventsGenerator.getCount()); Assert.assertEquals( 9.0 + 1.0 / 128, tEnd, 1.0 / 32.0); } private class CloseEventsGenerator implements EventHandler { final double r1; final double r2; int count; public CloseEventsGenerator(final double r1, final double r2) { this.r1 = r1; this.r2 = r2; this.count = 0; } @Override public void init(double t0, double[] y0, double t) { } @Override public void resetState(double t, double[] y) { } @Override public double g(double t, double[] y) { return (t - r1) * (r2 - t); } @Override public Action eventOccurred(double t, double[] y, boolean increasing) { return ++count < 2 ? Action.CONTINUE : Action.STOP; } public int getCount() { return count; } } }