EventFilter.java

  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. import java.util.Arrays;

  19. /** Wrapper used to detect only increasing or decreasing events.
  20.  *
  21.  * <p>General {@link EventHandler events} are defined implicitly
  22.  * by a {@link EventHandler#g(double, double[]) g function} crossing
  23.  * zero. This function needs to be continuous in the event neighborhood,
  24.  * and its sign must remain consistent between events. This implies that
  25.  * during an ODE integration, events triggered are alternately events
  26.  * for which the function increases from negative to positive values,
  27.  * and events for which the function decreases from positive to
  28.  * negative values.
  29.  * </p>
  30.  *
  31.  * <p>Sometimes, users are only interested in one type of event (say
  32.  * increasing events for example) and not in the other type. In these
  33.  * cases, looking precisely for all events location and triggering
  34.  * events that will later be ignored is a waste of computing time.</p>
  35.  *
  36.  * <p>Users can wrap a regular {@link EventHandler event handler} in
  37.  * an instance of this class and provide this wrapping instance to
  38.  * the {@link org.apache.commons.math4.legacy.ode.FirstOrderIntegrator ODE solver}
  39.  * in order to avoid wasting time looking for uninteresting events.
  40.  * The wrapper will intercept the calls to the {@link
  41.  * EventHandler#g(double, double[]) g function} and to the {@link
  42.  * EventHandler#eventOccurred(double, double[], boolean)
  43.  * eventOccurred} method in order to ignore uninteresting events. The
  44.  * wrapped regular {@link EventHandler event handler} will the see only
  45.  * the interesting events, i.e. either only {@code increasing} events or
  46.  * {@code decreasing} events. the number of calls to the {@link
  47.  * EventHandler#g(double, double[]) g function} will also be reduced.</p>
  48.  *
  49.  * @since 3.2
  50.  */

  51. public class EventFilter implements EventHandler {

  52.     /** Number of past transformers updates stored. */
  53.     private static final int HISTORY_SIZE = 100;

  54.     /** Wrapped event handler. */
  55.     private final EventHandler rawHandler;

  56.     /** Filter to use. */
  57.     private final FilterType filter;

  58.     /** Transformers of the g function. */
  59.     private final Transformer[] transformers;

  60.     /** Update time of the transformers. */
  61.     private final double[] updates;

  62.     /** Indicator for forward integration. */
  63.     private boolean forward;

  64.     /** Extreme time encountered so far. */
  65.     private double extremeT;

  66.     /** Wrap an {@link EventHandler event handler}.
  67.      * @param rawHandler event handler to wrap
  68.      * @param filter filter to use
  69.      */
  70.     public EventFilter(final EventHandler rawHandler, final FilterType filter) {
  71.         this.rawHandler   = rawHandler;
  72.         this.filter       = filter;
  73.         this.transformers = new Transformer[HISTORY_SIZE];
  74.         this.updates      = new double[HISTORY_SIZE];
  75.     }

  76.     /**  {@inheritDoc} */
  77.     @Override
  78.     public void init(double t0, double[] y0, double t) {

  79.         // delegate to raw handler
  80.         rawHandler.init(t0, y0, t);

  81.         // initialize events triggering logic
  82.         forward  = t >= t0;
  83.         extremeT = forward ? Double.NEGATIVE_INFINITY : Double.POSITIVE_INFINITY;
  84.         Arrays.fill(transformers, Transformer.UNINITIALIZED);
  85.         Arrays.fill(updates, extremeT);
  86.     }

  87.     /**  {@inheritDoc} */
  88.     @Override
  89.     public double g(double t, double[] y) {

  90.         final double rawG = rawHandler.g(t, y);

  91.         // search which transformer should be applied to g
  92.         if (forward) {
  93.             final int last = transformers.length - 1;
  94.             if (extremeT < t) {
  95.                 // we are at the forward end of the history

  96.                 // check if a new rough root has been crossed
  97.                 final Transformer previous = transformers[last];
  98.                 final Transformer next     = filter.selectTransformer(previous, rawG, forward);
  99.                 if (next != previous) {
  100.                     // there is a root somewhere between extremeT and t.
  101.                     // the new transformer is valid for t (this is how we have just computed
  102.                     // it above), but it is in fact valid on both sides of the root, so
  103.                     // it was already valid before t and even up to previous time. We store
  104.                     // the switch at extremeT for safety, to ensure the previous transformer
  105.                     // is not applied too close of the root
  106.                     System.arraycopy(updates,      1, updates,      0, last);
  107.                     System.arraycopy(transformers, 1, transformers, 0, last);
  108.                     updates[last]      = extremeT;
  109.                     transformers[last] = next;
  110.                 }

  111.                 extremeT = t;

  112.                 // apply the transform
  113.                 return next.transformed(rawG);
  114.             } else {
  115.                 // we are in the middle of the history

  116.                 // select the transformer
  117.                 for (int i = last; i > 0; --i) {
  118.                     if (updates[i] <= t) {
  119.                         // apply the transform
  120.                         return transformers[i].transformed(rawG);
  121.                     }
  122.                 }

  123.                 return transformers[0].transformed(rawG);
  124.             }
  125.         } else {
  126.             if (t < extremeT) {
  127.                 // we are at the backward end of the history

  128.                 // check if a new rough root has been crossed
  129.                 final Transformer previous = transformers[0];
  130.                 final Transformer next     = filter.selectTransformer(previous, rawG, forward);
  131.                 if (next != previous) {
  132.                     // there is a root somewhere between extremeT and t.
  133.                     // the new transformer is valid for t (this is how we have just computed
  134.                     // it above), but it is in fact valid on both sides of the root, so
  135.                     // it was already valid before t and even up to previous time. We store
  136.                     // the switch at extremeT for safety, to ensure the previous transformer
  137.                     // is not applied too close of the root
  138.                     System.arraycopy(updates,      0, updates,      1, updates.length - 1);
  139.                     System.arraycopy(transformers, 0, transformers, 1, transformers.length - 1);
  140.                     updates[0]      = extremeT;
  141.                     transformers[0] = next;
  142.                 }

  143.                 extremeT = t;

  144.                 // apply the transform
  145.                 return next.transformed(rawG);
  146.             } else {
  147.                 // we are in the middle of the history

  148.                 // select the transformer
  149.                 for (int i = 0; i < updates.length - 1; ++i) {
  150.                     if (t <= updates[i]) {
  151.                         // apply the transform
  152.                         return transformers[i].transformed(rawG);
  153.                     }
  154.                 }

  155.                 return transformers[updates.length - 1].transformed(rawG);
  156.             }
  157.        }
  158.     }

  159.     /**  {@inheritDoc} */
  160.     @Override
  161.     public Action eventOccurred(double t, double[] y, boolean increasing) {
  162.         // delegate to raw handler, fixing increasing status on the fly
  163.         return rawHandler.eventOccurred(t, y, filter.getTriggeredIncreasing());
  164.     }

  165.     /**  {@inheritDoc} */
  166.     @Override
  167.     public void resetState(double t, double[] y) {
  168.         // delegate to raw handler
  169.         rawHandler.resetState(t, y);
  170.     }
  171. }