FieldEventSlopeFilter.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.CalculusFieldElement;
  24. import org.hipparchus.Field;
  25. import org.hipparchus.analysis.solvers.BracketedRealFieldUnivariateSolver;
  26. import org.hipparchus.ode.FieldODEState;
  27. import org.hipparchus.ode.FieldODEStateAndDerivative;
  28. import org.hipparchus.util.MathArrays;

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

  63. public class FieldEventSlopeFilter<T extends FieldODEEventDetector<E>, E extends CalculusFieldElement<E>>
  64.     extends AbstractFieldODEDetector<FieldEventSlopeFilter<T, E>, E> {

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

  67.     /** Wrapped event detector.
  68.      * @since 3.0
  69.      */
  70.     private final T rawDetector;

  71.     /** Filter to use. */
  72.     private final FilterType filter;

  73.     /** Transformers of the g function. */
  74.     private final Transformer[] transformers;

  75.     /** Update time of the transformers. */
  76.     private final E[] updates;

  77.     /** Indicator for forward integration. */
  78.     private boolean forward;

  79.     /** Extreme time encountered so far. */
  80.     private E extremeT;

  81.     /** Wrap a {@link FieldODEEventDetector eve,t detector}.
  82.      * @param field field to which array elements belong
  83.      * @param rawDetector event detector to wrap
  84.      * @param filter filter to use
  85.      */
  86.     public FieldEventSlopeFilter(final Field<E> field, final T rawDetector, final FilterType filter) {
  87.         this(field,
  88.              rawDetector.getMaxCheckInterval(), rawDetector.getMaxIterationCount(),
  89.              rawDetector.getSolver(), new LocalHandler<>(rawDetector.getHandler()),
  90.              rawDetector, filter);
  91.     }

  92.     /** Private constructor with full parameters.
  93.      * <p>
  94.      * This constructor is private as users are expected to use the builder
  95.      * API with the various {@code withXxx()} methods to set up the instance
  96.      * in a readable manner without using a huge amount of parameters.
  97.      * </p>
  98.      * @param field field to which array elements belong
  99.      * @param maxCheck maximum checking interval (s)
  100.      * @param maxIter maximum number of iterations in the event time search
  101.      * @param solver solver to user for locating event
  102.      * @param handler event handler to call at event occurrences
  103.      * @param rawDetector event detector to wrap
  104.      * @param filter filter to use
  105.      */
  106.     private FieldEventSlopeFilter(final Field<E> field,
  107.                                   final FieldAdaptableInterval<E> maxCheck, final int maxIter,
  108.                                   final BracketedRealFieldUnivariateSolver<E> solver,
  109.                                   final FieldODEEventHandler<E> handler,
  110.                                   final T rawDetector, final FilterType filter) {
  111.         super(maxCheck, maxIter, solver, handler);
  112.         this.rawDetector  = rawDetector;
  113.         this.filter       = filter;
  114.         this.transformers = new Transformer[HISTORY_SIZE];
  115.         this.updates      = MathArrays.buildArray(field, HISTORY_SIZE);
  116.     }

  117.     /** {@inheritDoc} */
  118.     @Override
  119.     protected FieldEventSlopeFilter<T, E> create(final FieldAdaptableInterval<E> newMaxCheck, final int newMaxIter,
  120.                                                  final BracketedRealFieldUnivariateSolver<E> newSolver,
  121.                                                  final FieldODEEventHandler<E> newHandler) {
  122.         return new FieldEventSlopeFilter<>(newSolver.getAbsoluteAccuracy().getField(), newMaxCheck, newMaxIter,
  123.                 newSolver, newHandler, rawDetector, filter);
  124.     }

  125.     /**
  126.      * Get the wrapped raw detector.
  127.      * @return the wrapped raw detector
  128.      */
  129.     public T getDetector() {
  130.         return rawDetector;
  131.     }

  132.     /**  {@inheritDoc} */
  133.     @Override
  134.     public void init(final FieldODEStateAndDerivative<E> initialState, E finalTime) {
  135.         super.init(initialState, finalTime);

  136.         // delegate to raw handler
  137.         rawDetector.init(initialState, finalTime);

  138.         // initialize events triggering logic
  139.         forward  = finalTime.subtract(initialState.getTime()).getReal() >= 0;
  140.         extremeT = finalTime.getField().getZero().newInstance(forward ? Double.NEGATIVE_INFINITY : Double.POSITIVE_INFINITY);
  141.         Arrays.fill(transformers, Transformer.UNINITIALIZED);
  142.         Arrays.fill(updates, extremeT);

  143.     }

  144.     /**  {@inheritDoc} */
  145.     @Override
  146.     public void reset(final FieldODEStateAndDerivative<E> intermediateState, final E finalTime) {
  147.         super.reset(intermediateState, finalTime);
  148.         rawDetector.reset(intermediateState, finalTime);
  149.     }

  150.     /**  {@inheritDoc} */
  151.     @Override
  152.     public boolean isForward() {
  153.         return forward;
  154.     }

  155.     /**  {@inheritDoc} */
  156.     @Override
  157.     public E g(final FieldODEStateAndDerivative<E> state) {

  158.         final E rawG = rawDetector.g(state);

  159.         // search which transformer should be applied to g
  160.         if (isForward()) {
  161.             final int last = transformers.length - 1;
  162.             if (extremeT.subtract(state.getTime()).getReal() < 0) {
  163.                 // we are at the forward end of the history

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

  179.                 extremeT = state.getTime();

  180.                 // apply the transform
  181.                 return next.transformed(rawG);

  182.             } else {
  183.                 // we are in the middle of the history

  184.                 // select the transformer
  185.                 for (int i = last; i > 0; --i) {
  186.                     if (updates[i].subtract(state.getTime()).getReal() <= 0) {
  187.                         // apply the transform
  188.                         return transformers[i].transformed(rawG);
  189.                     }
  190.                 }

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

  192.             }
  193.         } else {
  194.             if (state.getTime().subtract(extremeT).getReal() < 0) {
  195.                 // we are at the backward end of the history

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

  211.                 extremeT = state.getTime();

  212.                 // apply the transform
  213.                 return next.transformed(rawG);

  214.             } else {
  215.                 // we are in the middle of the history

  216.                 // select the transformer
  217.                 for (int i = 0; i < updates.length - 1; ++i) {
  218.                     if (state.getTime().subtract(updates[i]).getReal() <= 0) {
  219.                         // apply the transform
  220.                         return transformers[i].transformed(rawG);
  221.                     }
  222.                 }

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

  224.             }
  225.        }

  226.     }

  227.     /** Local handler.
  228.      * @param <T> type of the event detector
  229.      * @param <E> the type of the field elements
  230.      */
  231.     private static class LocalHandler<T extends FieldODEEventDetector<E>, E extends CalculusFieldElement<E>>
  232.         implements FieldODEEventHandler<E> {

  233.         /** Raw handler. */
  234.         private final FieldODEEventHandler<E> rawHandler;

  235.         /** Simple constructor.
  236.          * @param rawHandler raw handler
  237.          */
  238.         LocalHandler(final FieldODEEventHandler<E> rawHandler) {
  239.             this.rawHandler = rawHandler;
  240.         }

  241.         /**  {@inheritDoc} */
  242.         @Override
  243.         public Action eventOccurred(final FieldODEStateAndDerivative<E> state,
  244.                                     final FieldODEEventDetector<E> detector,
  245.                                     final boolean increasing) {
  246.             // delegate to raw handler, fixing increasing status on the fly
  247.             @SuppressWarnings("unchecked")
  248.             final FieldEventSlopeFilter<T, E> esf = (FieldEventSlopeFilter<T, E>) detector;
  249.             return rawHandler.eventOccurred(state, esf, esf.filter.isTriggeredOnIncreasing());
  250.         }

  251.         /**  {@inheritDoc} */
  252.         @Override
  253.         public FieldODEState<E> resetState(final FieldODEEventDetector<E> detector,
  254.                                            final FieldODEStateAndDerivative<E> state) {
  255.             // delegate to raw handler
  256.             @SuppressWarnings("unchecked")
  257.             final FieldEventSlopeFilter<T, E> esf = (FieldEventSlopeFilter<T, E>) detector;
  258.             return rawHandler.resetState(esf, state);
  259.         }

  260.     }

  261. }