EventSlopeFilter.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.  *      https://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.  * This is not the original file distributed by the Apache Software Foundation
  19.  * It has been modified by the Hipparchus project
  20.  */

  21. package org.hipparchus.ode.events;

  22. import java.util.Arrays;

  23. import org.hipparchus.analysis.UnivariateFunction;
  24. import org.hipparchus.analysis.solvers.BracketedUnivariateSolver;
  25. import org.hipparchus.ode.ODEState;
  26. import org.hipparchus.ode.ODEStateAndDerivative;

  27. /** Wrapper used to detect only increasing or decreasing events.
  28.  *
  29.  * <p>General {@link ODEEventDetector events} are defined implicitly
  30.  * by a {@link ODEEventDetector#g(ODEStateAndDerivative) g function} crossing
  31.  * zero. This function needs to be continuous in the event neighborhood,
  32.  * and its sign must remain consistent between events. This implies that
  33.  * during an ODE integration, events triggered are alternately events
  34.  * for which the function increases from negative to positive values,
  35.  * and events for which the function decreases from positive to
  36.  * negative values.
  37.  * </p>
  38.  *
  39.  * <p>Sometimes, users are only interested in one type of event (say
  40.  * increasing events for example) and not in the other type. In these
  41.  * cases, looking precisely for all events location and triggering
  42.  * events that will later be ignored is a waste of computing time.</p>
  43.  *
  44.  * <p>Users can wrap a regular {@link ODEEventDetector event detector} in
  45.  * an instance of this class and provide this wrapping instance to
  46.  * the {@link org.hipparchus.ode.ODEIntegrator ODE solver}
  47.  * in order to avoid wasting time looking for uninteresting events.
  48.  * The wrapper will intercept the calls to the {@link
  49.  * ODEEventDetector#g(ODEStateAndDerivative) g function} and to the {@link
  50.  * ODEEventHandler#eventOccurred(ODEStateAndDerivative, ODEEventDetector, boolean)
  51.  * eventOccurred} method in order to ignore uninteresting events. The
  52.  * wrapped regular {@link ODEEventHandler event handler} will the see only
  53.  * the interesting events, i.e. either only {@code increasing} events or
  54.  * {@code decreasing} events. the number of calls to the {@link
  55.  * ODEEventDetector#g(ODEStateAndDerivative) g function} will also be reduced.</p>
  56.  * @param <T> type of the event detector
  57.  * @since 3.0
  58.  */
  59. public class EventSlopeFilter<T extends ODEEventDetector> extends AbstractODEDetector<EventSlopeFilter<T>> {

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

  62.     /** Wrapped event detector.
  63.      * @since 3.0
  64.      */
  65.     private final T rawDetector;

  66.     /** Filter to use. */
  67.     private final FilterType filter;

  68.     /** Transformers of the g function. */
  69.     private final Transformer[] transformers;

  70.     /** Update time of the transformers. */
  71.     private final double[] updates;

  72.     /** Indicator for forward integration. */
  73.     private boolean forward;

  74.     /** Extreme time encountered so far. */
  75.     private double extremeT;

  76.     /** Wrap an {@link ODEEventDetector event detector}.
  77.      * @param rawDetector event detector to wrap
  78.      * @param filter filter to use
  79.      * @since 3.0
  80.      */
  81.     public EventSlopeFilter(final T rawDetector, final FilterType filter) {
  82.         this(rawDetector.getMaxCheckInterval(), rawDetector.getMaxIterationCount(),
  83.              rawDetector.getSolver(), new LocalHandler<>(rawDetector.getHandler()),
  84.              rawDetector, filter);
  85.     }

  86.     /** Private constructor with full parameters.
  87.      * <p>
  88.      * This constructor is private as users are expected to use the builder
  89.      * API with the various {@code withXxx()} methods to set up the instance
  90.      * in a readable manner without using a huge amount of parameters.
  91.      * </p>
  92.      * @param maxCheck maximum checking interval (s)
  93.      * @param maxIter maximum number of iterations in the event time search
  94.      * @param solver root-finding algorithm to use to detect state events
  95.      * @param handler event handler to call at event occurrences
  96.      * @param rawDetector event detector to wrap
  97.      * @param filter filter to use
  98.      */
  99.     private EventSlopeFilter(final AdaptableInterval maxCheck, final int maxIter,
  100.                              final BracketedUnivariateSolver<UnivariateFunction> solver,
  101.                              final ODEEventHandler handler,
  102.                              final T rawDetector, final FilterType filter) {
  103.         super(maxCheck, maxIter, solver, handler);
  104.         this.rawDetector  = rawDetector;
  105.         this.filter       = filter;
  106.         this.transformers = new Transformer[HISTORY_SIZE];
  107.         this.updates      = new double[HISTORY_SIZE];
  108.     }

  109.     /** {@inheritDoc} */
  110.     @Override
  111.     protected EventSlopeFilter<T> create(final AdaptableInterval newMaxCheck, final int newMaxIter,
  112.                                          final BracketedUnivariateSolver<UnivariateFunction> newSolver,
  113.                                          final ODEEventHandler newHandler) {
  114.         return new EventSlopeFilter<>(newMaxCheck, newMaxIter, newSolver, newHandler,
  115.                                       rawDetector, filter);
  116.     }

  117.     /**
  118.      * Get the wrapped raw detector.
  119.      * @return the wrapped raw detector
  120.      */
  121.     public T getDetector() {
  122.         return rawDetector;
  123.     }

  124.     /**  {@inheritDoc} */
  125.     @Override
  126.     public void init(final ODEStateAndDerivative initialState, double finalTime) {
  127.         super.init(initialState, finalTime);

  128.         // delegate to raw handler
  129.         rawDetector.init(initialState, finalTime);

  130.         // initialize events triggering logic
  131.         forward  = finalTime >= initialState.getTime();
  132.         extremeT = forward ? Double.NEGATIVE_INFINITY : Double.POSITIVE_INFINITY;
  133.         Arrays.fill(transformers, Transformer.UNINITIALIZED);
  134.         Arrays.fill(updates, extremeT);

  135.     }

  136.     /**  {@inheritDoc} */
  137.     @Override
  138.     public void reset(final ODEStateAndDerivative intermediateState, final double finalTime) {
  139.         super.reset(intermediateState, finalTime);
  140.         rawDetector.reset(intermediateState, finalTime);
  141.     }

  142.     /**  {@inheritDoc} */
  143.     @Override
  144.     public boolean isForward() {
  145.         return forward;
  146.     }

  147.     /**  {@inheritDoc} */
  148.     @Override
  149.     public double g(final ODEStateAndDerivative state) {

  150.         final double rawG = rawDetector.g(state);

  151.         // search which transformer should be applied to g
  152.         if (isForward()) {
  153.             final int last = transformers.length - 1;
  154.             if (extremeT < state.getTime()) {
  155.                 // we are at the forward end of the history

  156.                 // check if a new rough root has been crossed
  157.                 final Transformer previous = transformers[last];
  158.                 final Transformer next     = filter.selectTransformer(previous, rawG, forward);
  159.                 if (next != previous) {
  160.                     // there is a root somewhere between extremeT and t.
  161.                     // the new transformer is valid for t (this is how we have just computed
  162.                     // it above), but it is in fact valid on both sides of the root, so
  163.                     // it was already valid before t and even up to previous time. We store
  164.                     // the switch at extremeT for safety, to ensure the previous transformer
  165.                     // is not applied too close of the root
  166.                     System.arraycopy(updates,      1, updates,      0, last);
  167.                     System.arraycopy(transformers, 1, transformers, 0, last);
  168.                     updates[last]      = extremeT;
  169.                     transformers[last] = next;
  170.                 }

  171.                 extremeT = state.getTime();

  172.                 // apply the transform
  173.                 return next.transformed(rawG);

  174.             } else {
  175.                 // we are in the middle of the history

  176.                 // select the transformer
  177.                 for (int i = last; i > 0; --i) {
  178.                     if (updates[i] <= state.getTime()) {
  179.                         // apply the transform
  180.                         return transformers[i].transformed(rawG);
  181.                     }
  182.                 }

  183.                 return transformers[0].transformed(rawG);

  184.             }
  185.         } else {
  186.             if (state.getTime() < extremeT) {
  187.                 // we are at the backward end of the history

  188.                 // check if a new rough root has been crossed
  189.                 final Transformer previous = transformers[0];
  190.                 final Transformer next     = filter.selectTransformer(previous, rawG, forward);
  191.                 if (next != previous) {
  192.                     // there is a root somewhere between extremeT and t.
  193.                     // the new transformer is valid for t (this is how we have just computed
  194.                     // it above), but it is in fact valid on both sides of the root, so
  195.                     // it was already valid before t and even up to previous time. We store
  196.                     // the switch at extremeT for safety, to ensure the previous transformer
  197.                     // is not applied too close of the root
  198.                     System.arraycopy(updates,      0, updates,      1, updates.length - 1);
  199.                     System.arraycopy(transformers, 0, transformers, 1, transformers.length - 1);
  200.                     updates[0]      = extremeT;
  201.                     transformers[0] = next;
  202.                 }

  203.                 extremeT = state.getTime();

  204.                 // apply the transform
  205.                 return next.transformed(rawG);

  206.             } else {
  207.                 // we are in the middle of the history

  208.                 // select the transformer
  209.                 for (int i = 0; i < updates.length - 1; ++i) {
  210.                     if (state.getTime() <= updates[i]) {
  211.                         // apply the transform
  212.                         return transformers[i].transformed(rawG);
  213.                     }
  214.                 }

  215.                 return transformers[updates.length - 1].transformed(rawG);

  216.             }
  217.        }

  218.     }

  219.     /** Local handler.
  220.      * @param <T> type of the event detector
  221.      */
  222.     private static class LocalHandler<T extends ODEEventDetector> implements ODEEventHandler {

  223.         /** Raw handler. */
  224.         private final ODEEventHandler rawHandler;

  225.         /** Simple constructor.
  226.          * @param rawHandler raw handler
  227.          */
  228.         LocalHandler(final ODEEventHandler rawHandler) {
  229.             this.rawHandler = rawHandler;
  230.         }

  231.         /**  {@inheritDoc} */
  232.         @Override
  233.         public Action eventOccurred(final ODEStateAndDerivative state, final ODEEventDetector detector, final boolean increasing) {
  234.             // delegate to raw handler, fixing increasing status on the fly
  235.             @SuppressWarnings("unchecked")
  236.             final EventSlopeFilter<T> esf = (EventSlopeFilter<T>) detector;
  237.             return rawHandler.eventOccurred(state, esf, esf.filter.isTriggeredOnIncreasing());
  238.         }

  239.         /**  {@inheritDoc} */
  240.         @Override
  241.         public ODEState resetState(final ODEEventDetector detector, final ODEStateAndDerivative state) {
  242.             // delegate to raw handler
  243.             @SuppressWarnings("unchecked")
  244.             final EventSlopeFilter<T> esf = (EventSlopeFilter<T>) detector;
  245.             return rawHandler.resetState(esf, state);
  246.         }

  247.     }

  248. }