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  
18  package org.apache.commons.math.ode;
19  
20  import java.io.Serializable;
21  
22  import org.apache.commons.math.ConvergenceException;
23  import org.apache.commons.math.FunctionEvaluationException;
24  import org.apache.commons.math.analysis.BrentSolver;
25  import org.apache.commons.math.analysis.UnivariateRealFunction;
26  import org.apache.commons.math.analysis.UnivariateRealSolver;
27  
28  /** This class handles the state for one {@link SwitchingFunction
29   * switching function} during integration steps.
30   *
31   * <p>Each time the integrator proposes a step, the switching function
32   * should be checked. This class handles the state of one function
33   * during one integration step, with references to the state at the
34   * end of the preceding step. This information is used to determine if
35   * the function should trigger an event or not during the proposed
36   * step (and hence the step should be reduced to ensure the event
37   * occurs at a bound rather than inside the step).</p>
38   *
39   * @version $Revision: 620312 $ $Date: 2008-02-10 12:28:59 -0700 (Sun, 10 Feb 2008) $
40   * @since 1.2
41   */
42  class SwitchState implements Serializable {
43  
44    /** Serializable version identifier. */
45    private static final long serialVersionUID = -7307007422156119622L;
46  
47    /** Switching function. */
48    private SwitchingFunction function;
49  
50    /** Maximal time interval between switching function checks. */
51    private double maxCheckInterval;
52  
53    /** Convergence threshold for event localisation. */
54    private double convergence;
55  
56    /** Upper limit in the iteration count for event localisation. */
57    private int maxIterationCount;
58  
59    /** Time at the beginning of the step. */
60    private double t0;
61  
62    /** Value of the switching function at the beginning of the step. */
63    private double g0;
64  
65    /** Simulated sign of g0 (we cheat when crossing events). */
66    private boolean g0Positive;
67  
68    /** Indicator of event expected during the step. */
69    private boolean pendingEvent;
70  
71    /** Occurrence time of the pending event. */
72    private double pendingEventTime;
73  
74    /** Occurrence time of the previous event. */
75    private double previousEventTime;
76  
77    /** Variation direction around pending event.
78     *  (this is considered with respect to the integration direction)
79     */
80    private boolean increasing;
81  
82    /** Next action indicator. */
83    private int nextAction;
84  
85    /** Simple constructor.
86     * @param function switching function
87     * @param maxCheckInterval maximal time interval between switching
88     * function checks (this interval prevents missing sign changes in
89     * case the integration steps becomes very large)
90     * @param convergence convergence threshold in the event time search
91     * @param maxIterationCount upper limit of the iteration count in
92     * the event time search
93     */
94    public SwitchState(SwitchingFunction function, double maxCheckInterval,
95                       double convergence, int maxIterationCount) {
96      this.function          = function;
97      this.maxCheckInterval  = maxCheckInterval;
98      this.convergence       = Math.abs(convergence);
99      this.maxIterationCount = maxIterationCount;
100 
101     // some dummy values ...
102     t0                = Double.NaN;
103     g0                = Double.NaN;
104     g0Positive        = true;
105     pendingEvent      = false;
106     pendingEventTime  = Double.NaN;
107     previousEventTime = Double.NaN;
108     increasing        = true;
109     nextAction        = SwitchingFunction.CONTINUE;
110 
111   }
112 
113   /** Reinitialize the beginning of the step.
114    * @param t0 value of the independent <i>time</i> variable at the
115    * beginning of the step
116    * @param y0 array containing the current value of the state vector
117    * at the beginning of the step
118    * @exception FunctionEvaluationException if the switching function
119    * value cannot be evaluated at the beginning of the step
120    */
121   public void reinitializeBegin(double t0, double[] y0)
122     throws FunctionEvaluationException {
123     this.t0 = t0;
124     g0 = function.g(t0, y0);
125     g0Positive = (g0 >= 0);
126   }
127 
128   /** Evaluate the impact of the proposed step on the switching function.
129    * @param interpolator step interpolator for the proposed step
130    * @return true if the switching function triggers an event before
131    * the end of the proposed step (this implies the step should be
132    * rejected)
133    * @exception DerivativeException if the interpolator fails to
134    * compute the function somewhere within the step
135    * @exception FunctionEvaluationException if the switching function
136    * cannot be evaluated
137    * @exception ConvergenceException if an event cannot be located
138    */
139   public boolean evaluateStep(final StepInterpolator interpolator)
140     throws DerivativeException, FunctionEvaluationException, ConvergenceException {
141 
142     try {
143 
144       double t1 = interpolator.getCurrentTime();
145       int    n  = Math.max(1, (int) Math.ceil(Math.abs(t1 - t0) / maxCheckInterval));
146       double h  = (t1 - t0) / n;
147 
148       double ta = t0;
149       double ga = g0;
150       double tb = t0 + ((t1 > t0) ? convergence : -convergence);
151       for (int i = 0; i < n; ++i) {
152 
153         // evaluate function value at the end of the substep
154         tb += h;
155         interpolator.setInterpolatedTime(tb);
156         double gb = function.g(tb, interpolator.getInterpolatedState());
157 
158         // check events occurrence
159         if (g0Positive ^ (gb >= 0)) {
160           // there is a sign change: an event is expected during this step
161 
162           // variation direction, with respect to the integration direction
163           increasing = (gb >= ga);
164 
165           UnivariateRealSolver solver = new BrentSolver(new UnivariateRealFunction() {
166               public double value(double t) throws FunctionEvaluationException {
167                   try {
168                       interpolator.setInterpolatedTime(t);
169                       return function.g(t, interpolator.getInterpolatedState());
170                   } catch (DerivativeException e) {
171                       throw new FunctionEvaluationException(t, e);
172                   }
173               }
174           });
175           solver.setAbsoluteAccuracy(convergence);
176           solver.setMaximalIterationCount(maxIterationCount);
177           double root = solver.solve(ta, tb);
178           if (Double.isNaN(previousEventTime) || (Math.abs(previousEventTime - root) > convergence)) {
179               pendingEventTime = root;
180               if (pendingEvent && (Math.abs(t1 - pendingEventTime) <= convergence)) {
181                   // we were already waiting for this event which was
182                   // found during a previous call for a step that was
183                   // rejected, this step must now be accepted since it
184                   // properly ends exactly at the event occurrence
185                   return false;
186               }
187               // either we were not waiting for the event or it has
188               // moved in such a way the step cannot be accepted
189               pendingEvent = true;
190               return true;
191           }
192 
193         } else {
194           // no sign change: there is no event for now
195           ta = tb;
196           ga = gb;
197         }
198 
199       }
200 
201       // no event during the whole step
202       pendingEvent     = false;
203       pendingEventTime = Double.NaN;
204       return false;
205 
206     } catch (FunctionEvaluationException e) {
207      Throwable cause = e.getCause();
208       if ((cause != null) && (cause instanceof DerivativeException)) {
209         throw (DerivativeException) cause;
210       }
211       throw e;
212     }
213 
214   }
215 
216   /** Get the occurrence time of the event triggered in the current
217    * step.
218    * @return occurrence time of the event triggered in the current
219    * step.
220    */
221   public double getEventTime() {
222     return pendingEventTime;
223   }
224 
225   /** Acknowledge the fact the step has been accepted by the integrator.
226    * @param t value of the independent <i>time</i> variable at the
227    * end of the step
228    * @param y array containing the current value of the state vector
229    * at the end of the step
230    * @exception FunctionEvaluationException if the value of the switching
231    * function cannot be evaluated
232    */
233   public void stepAccepted(double t, double[] y)
234     throws FunctionEvaluationException {
235 
236     t0 = t;
237     g0 = function.g(t, y);
238 
239     if (pendingEvent) {
240       // force the sign to its value "just after the event"
241       previousEventTime = t;
242       g0Positive        = increasing;
243       nextAction        = function.eventOccurred(t, y);
244     } else {
245       g0Positive = (g0 >= 0);
246       nextAction = SwitchingFunction.CONTINUE;
247     }
248   }
249 
250   /** Check if the integration should be stopped at the end of the
251    * current step.
252    * @return true if the integration should be stopped
253    */
254   public boolean stop() {
255     return nextAction == SwitchingFunction.STOP;
256   }
257 
258   /** Let the switching function reset the state if it wants.
259    * @param t value of the independent <i>time</i> variable at the
260    * beginning of the next step
261    * @param y array were to put the desired state vector at the beginning
262    * of the next step
263    * @return true if the integrator should reset the derivatives too
264    */
265   public boolean reset(double t, double[] y) {
266 
267     if (! pendingEvent) {
268       return false;
269     }
270 
271     if (nextAction == SwitchingFunction.RESET_STATE) {
272       function.resetState(t, y);
273     }
274     pendingEvent      = false;
275     pendingEventTime  = Double.NaN;
276 
277     return (nextAction == SwitchingFunction.RESET_STATE) ||
278            (nextAction == SwitchingFunction.RESET_DERIVATIVES);
279 
280   }
281 
282 }