blob: 081788d02491d2135e90902e9e28bf3186de349d [file] [log] [blame]
/*
* 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.math3.ode.events;
import java.util.ArrayList;
import java.util.List;
import org.apache.commons.math3.analysis.solvers.BaseSecantSolver;
import org.apache.commons.math3.analysis.solvers.PegasusSolver;
import org.apache.commons.math3.exception.DimensionMismatchException;
import org.apache.commons.math3.exception.MaxCountExceededException;
import org.apache.commons.math3.exception.NoBracketingException;
import org.apache.commons.math3.exception.NumberIsTooSmallException;
import org.apache.commons.math3.ode.FirstOrderDifferentialEquations;
import org.apache.commons.math3.ode.FirstOrderIntegrator;
import org.apache.commons.math3.ode.nonstiff.DormandPrince853Integrator;
import org.junit.Assert;
import org.junit.Test;
/** Tests for overlapping state events. Also tests an event function that does
* not converge to zero, but does have values of opposite sign around its root.
*/
public class OverlappingEventsTest implements FirstOrderDifferentialEquations {
/** Expected event times for first event. */
private static final double[] EVENT_TIMES1 = {1.0, 2.0, 3.0, 4.0, 5.0, 6.0,
7.0, 8.0, 9.0};
/** Expected event times for second event. */
private static final double[] EVENT_TIMES2 = {0.5, 1.0, 1.5, 2.0, 2.5, 3.0,
3.5, 4.0, 4.5, 5.0, 5.5, 6.0,
6.5, 7.0, 7.5, 8.0, 8.5, 9.0,
9.5};
/** Test for events that occur at the exact same time, but due to numerical
* calculations occur very close together instead. Uses event type 0. See
* {@link org.apache.commons.math3.ode.events.EventHandler#g(double, double[])
* EventHandler.g(double, double[])}.
*/
@Test
public void testOverlappingEvents0()
throws DimensionMismatchException, NumberIsTooSmallException,
MaxCountExceededException, NoBracketingException {
test(0);
}
/** Test for events that occur at the exact same time, but due to numerical
* calculations occur very close together instead. Uses event type 1. See
* {@link org.apache.commons.math3.ode.events.EventHandler#g(double, double[])
* EventHandler.g(double, double[])}.
*/
@Test
public void testOverlappingEvents1()
throws DimensionMismatchException, NumberIsTooSmallException,
MaxCountExceededException, NoBracketingException {
test(1);
}
/** Test for events that occur at the exact same time, but due to numerical
* calculations occur very close together instead.
* @param eventType the type of events to use. See
* {@link org.apache.commons.math3.ode.events.EventHandler#g(double, double[])
* EventHandler.g(double, double[])}.
*/
public void test(int eventType)
throws DimensionMismatchException, NumberIsTooSmallException,
MaxCountExceededException, NoBracketingException {
double e = 1e-15;
FirstOrderIntegrator integrator = new DormandPrince853Integrator(e, 100.0, 1e-7, 1e-7);
BaseSecantSolver rootSolver = new PegasusSolver(e, e);
EventHandler evt1 = new Event(0, eventType);
EventHandler evt2 = new Event(1, eventType);
integrator.addEventHandler(evt1, 0.1, e, 999, rootSolver);
integrator.addEventHandler(evt2, 0.1, e, 999, rootSolver);
double t = 0.0;
double tEnd = 10.0;
double[] y = {0.0, 0.0};
List<Double> events1 = new ArrayList<Double>();
List<Double> events2 = new ArrayList<Double>();
while (t < tEnd) {
t = integrator.integrate(this, t, y, tEnd, y);
//System.out.println("t=" + t + ",\t\ty=[" + y[0] + "," + y[1] + "]");
if (y[0] >= 1.0) {
y[0] = 0.0;
events1.add(t);
//System.out.println("Event 1 @ t=" + t);
}
if (y[1] >= 1.0) {
y[1] = 0.0;
events2.add(t);
//System.out.println("Event 2 @ t=" + t);
}
}
Assert.assertEquals(EVENT_TIMES1.length, events1.size());
Assert.assertEquals(EVENT_TIMES2.length, events2.size());
for(int i = 0; i < EVENT_TIMES1.length; i++) {
Assert.assertEquals(EVENT_TIMES1[i], events1.get(i), 1e-7);
}
for(int i = 0; i < EVENT_TIMES2.length; i++) {
Assert.assertEquals(EVENT_TIMES2[i], events2.get(i), 1e-7);
}
//System.out.println();
}
/** {@inheritDoc} */
public int getDimension() {
return 2;
}
/** {@inheritDoc} */
public void computeDerivatives(double t, double[] y, double[] yDot) {
yDot[0] = 1.0;
yDot[1] = 2.0;
}
/** State events for this unit test. */
private class Event implements EventHandler {
/** The index of the continuous variable to use. */
private final int idx;
/** The event type to use. See {@link #g}. */
private final int eventType;
/** Constructor for the {@link Event} class.
* @param idx the index of the continuous variable to use
* @param eventType the type of event to use. See {@link #g}
*/
public Event(int idx, int eventType) {
this.idx = idx;
this.eventType = eventType;
}
/** {@inheritDoc} */
public void init(double t0, double[] y0, double t) {
}
/** {@inheritDoc} */
public double g(double t, double[] y) {
return (eventType == 0) ? y[idx] >= 1.0 ? 1.0 : -1.0
: y[idx] - 1.0;
}
/** {@inheritDoc} */
public Action eventOccurred(double t, double[] y, boolean increasing) {
return Action.STOP;
}
/** {@inheritDoc} */
public void resetState(double t, double[] y) {
// Never called.
}
}
}