View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *      http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  package org.apache.commons.math4.legacy.ode.events;
18  
19  import java.util.ArrayList;
20  import java.util.List;
21  
22  import org.apache.commons.math4.legacy.analysis.solvers.BaseSecantSolver;
23  import org.apache.commons.math4.legacy.analysis.solvers.PegasusSolver;
24  import org.apache.commons.math4.legacy.exception.DimensionMismatchException;
25  import org.apache.commons.math4.legacy.exception.MaxCountExceededException;
26  import org.apache.commons.math4.legacy.exception.NoBracketingException;
27  import org.apache.commons.math4.legacy.exception.NumberIsTooSmallException;
28  import org.apache.commons.math4.legacy.ode.FirstOrderDifferentialEquations;
29  import org.apache.commons.math4.legacy.ode.FirstOrderIntegrator;
30  import org.apache.commons.math4.legacy.ode.nonstiff.DormandPrince853Integrator;
31  import org.junit.Assert;
32  import org.junit.Test;
33  
34  /** Tests for overlapping state events. Also tests an event function that does
35   * not converge to zero, but does have values of opposite sign around its root.
36   */
37  public class OverlappingEventsTest implements FirstOrderDifferentialEquations {
38  
39      /** Expected event times for first event. */
40      private static final double[] EVENT_TIMES1 = {1.0, 2.0, 3.0, 4.0, 5.0, 6.0,
41                                                    7.0, 8.0, 9.0};
42  
43      /** Expected event times for second event. */
44      private static final double[] EVENT_TIMES2 = {0.5, 1.0, 1.5, 2.0, 2.5, 3.0,
45                                                    3.5, 4.0, 4.5, 5.0, 5.5, 6.0,
46                                                    6.5, 7.0, 7.5, 8.0, 8.5, 9.0,
47                                                    9.5};
48  
49      /** Test for events that occur at the exact same time, but due to numerical
50       * calculations occur very close together instead. Uses event type 0. See
51       * {@link org.apache.commons.math4.legacy.ode.events.EventHandler#g(double, double[])
52       * EventHandler.g(double, double[])}.
53       */
54      @Test
55      public void testOverlappingEvents0()
56          throws DimensionMismatchException, NumberIsTooSmallException,
57                 MaxCountExceededException, NoBracketingException {
58          test(0);
59      }
60  
61      /** Test for events that occur at the exact same time, but due to numerical
62       * calculations occur very close together instead. Uses event type 1. See
63       * {@link org.apache.commons.math4.legacy.ode.events.EventHandler#g(double, double[])
64       * EventHandler.g(double, double[])}.
65       */
66      @Test
67      public void testOverlappingEvents1()
68          throws DimensionMismatchException, NumberIsTooSmallException,
69                 MaxCountExceededException, NoBracketingException {
70          test(1);
71      }
72  
73      /** Test for events that occur at the exact same time, but due to numerical
74       * calculations occur very close together instead.
75       * @param eventType the type of events to use. See
76       * {@link org.apache.commons.math4.legacy.ode.events.EventHandler#g(double, double[])
77       * EventHandler.g(double, double[])}.
78       */
79      public void test(int eventType)
80          throws DimensionMismatchException, NumberIsTooSmallException,
81                 MaxCountExceededException, NoBracketingException {
82          double e = 1e-15;
83          FirstOrderIntegrator integrator = new DormandPrince853Integrator(e, 100.0, 1e-7, 1e-7);
84          BaseSecantSolver rootSolver = new PegasusSolver(e, e);
85          EventHandler evt1 = new Event(0, eventType);
86          EventHandler evt2 = new Event(1, eventType);
87          integrator.addEventHandler(evt1, 0.1, e, 999, rootSolver);
88          integrator.addEventHandler(evt2, 0.1, e, 999, rootSolver);
89          double t = 0.0;
90          double tEnd = 10.0;
91          double[] y = {0.0, 0.0};
92          List<Double> events1 = new ArrayList<>();
93          List<Double> events2 = new ArrayList<>();
94          while (t < tEnd) {
95              t = integrator.integrate(this, t, y, tEnd, y);
96              //System.out.println("t=" + t + ",\t\ty=[" + y[0] + "," + y[1] + "]");
97  
98              if (y[0] >= 1.0) {
99                  y[0] = 0.0;
100                 events1.add(t);
101                 //System.out.println("Event 1 @ t=" + t);
102             }
103             if (y[1] >= 1.0) {
104                 y[1] = 0.0;
105                 events2.add(t);
106                 //System.out.println("Event 2 @ t=" + t);
107             }
108         }
109         Assert.assertEquals(EVENT_TIMES1.length, events1.size());
110         Assert.assertEquals(EVENT_TIMES2.length, events2.size());
111         for(int i = 0; i < EVENT_TIMES1.length; i++) {
112             Assert.assertEquals(EVENT_TIMES1[i], events1.get(i), 1e-7);
113         }
114         for(int i = 0; i < EVENT_TIMES2.length; i++) {
115             Assert.assertEquals(EVENT_TIMES2[i], events2.get(i), 1e-7);
116         }
117         //System.out.println();
118     }
119 
120     /** {@inheritDoc} */
121     @Override
122     public int getDimension() {
123         return 2;
124     }
125 
126     /** {@inheritDoc} */
127     @Override
128     public void computeDerivatives(double t, double[] y, double[] yDot) {
129         yDot[0] = 1.0;
130         yDot[1] = 2.0;
131     }
132 
133     /** State events for this unit test. */
134     private static final class Event implements EventHandler {
135         /** The index of the continuous variable to use. */
136         private final int idx;
137 
138         /** The event type to use. See {@link #g}. */
139         private final int eventType;
140 
141         /** Constructor for the {@link Event} class.
142          * @param idx the index of the continuous variable to use
143          * @param eventType the type of event to use. See {@link #g}
144          */
145         Event(int idx, int eventType) {
146             this.idx = idx;
147             this.eventType = eventType;
148         }
149 
150         /** {@inheritDoc} */
151         @Override
152         public void init(double t0, double[] y0, double t) {
153         }
154 
155         /** {@inheritDoc} */
156         @Override
157         public double g(double t, double[] y) {
158             return (eventType == 0) ? y[idx] >= 1.0 ? 1.0 : -1.0
159                                     : y[idx] - 1.0;
160         }
161 
162         /** {@inheritDoc} */
163         @Override
164         public Action eventOccurred(double t, double[] y, boolean increasing) {
165             return Action.STOP;
166         }
167 
168         /** {@inheritDoc} */
169         @Override
170         public void resetState(double t, double[] y) {
171             // Never called.
172         }
173     }
174 }