FieldEventEnablingPredicateFilter.java

  1. /* Copyright 2002-2024 Luc Maisonobe
  2.  * Licensed to CS GROUP (CS) under one or more
  3.  * contributor license agreements.  See the NOTICE file distributed with
  4.  * this work for additional information regarding copyright ownership.
  5.  * CS 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.orekit.propagation.events;

  18. import java.lang.reflect.Array;
  19. import java.util.Arrays;

  20. import org.hipparchus.CalculusFieldElement;
  21. import org.hipparchus.ode.events.Action;
  22. import org.orekit.propagation.FieldSpacecraftState;
  23. import org.orekit.propagation.events.handlers.FieldEventHandler;
  24. import org.orekit.time.FieldAbsoluteDate;

  25. /** Wrapper used to detect events only when enabled by an external predicated function.
  26.  *
  27.  * <p>General {@link FieldEventDetector events} are defined implicitly
  28.  * by a {@link FieldEventDetector#g(FieldSpacecraftState) g function} crossing
  29.  * zero. This implies that during an orbit propagation, events are
  30.  * triggered at all zero crossings.
  31.  * </p>
  32.  *
  33.  * <p>Sometimes, users would like to enable or disable events by themselves,
  34.  * for example to trigger them only for certain orbits, or to check elevation
  35.  * maximums only when elevation itself is positive (i.e. they want to
  36.  * discard elevation maximums below ground). In these cases, looking precisely
  37.  * for all events location and triggering events that will later be ignored
  38.  * is a waste of computing time.</p>
  39.  *
  40.  * <p>Users can wrap a regular {@link FieldEventDetector event detector} in
  41.  * an instance of this class and provide this wrapping instance to
  42.  * a {@link org.orekit.propagation.FieldPropagator}
  43.  * in order to avoid wasting time looking for uninteresting events.
  44.  * The wrapper will intercept the calls to the {@link
  45.  * FieldEventDetector#g(FieldSpacecraftState) g function} and to the {@link
  46.  * FieldEventHandler#eventOccurred(FieldSpacecraftState, FieldEventDetector, boolean)
  47.  * eventOccurred} method in order to ignore uninteresting events. The
  48.  * wrapped regular {@link FieldEventDetector event detector} will the see only
  49.  * the interesting events, i.e. either only events that occur when a
  50.  * user-provided event enabling predicate function is true, ignoring all events
  51.  * that occur when the event enabling predicate function is false. The number of
  52.  * calls to the {@link FieldEventDetector#g(FieldSpacecraftState) g function} will also be
  53.  * reduced.</p>
  54.  * @param <T> type of the field elements
  55.  * @see FieldEventSlopeFilter
  56.  * @since 12.0
  57.  */

  58. public class FieldEventEnablingPredicateFilter<T extends CalculusFieldElement<T>>
  59.     extends FieldAbstractDetector<FieldEventEnablingPredicateFilter<T>, T> {

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

  62.     /** Wrapped event detector. */
  63.     private final FieldEventDetector<T> rawDetector;

  64.     /** Enabling predicate function. */
  65.     private final FieldEnablingPredicate<T> enabler;

  66.     /** Transformers of the g function. */
  67.     private final Transformer[] transformers;

  68.     /** Update time of the transformers. */
  69.     private final FieldAbsoluteDate<T>[] updates;

  70.     /** Indicator for forward integration. */
  71.     private boolean forward;

  72.     /** Extreme time encountered so far. */
  73.     private FieldAbsoluteDate<T> extremeT;

  74.     /** Detector function value at extremeT. */
  75.     private T extremeG;

  76.     /** Wrap an {@link EventDetector event detector}.
  77.      * @param rawDetector event detector to wrap
  78.      * @param enabler event enabling predicate function to use
  79.      */
  80.     public FieldEventEnablingPredicateFilter(final FieldEventDetector<T> rawDetector,
  81.                                              final FieldEnablingPredicate<T> enabler) {
  82.         this(rawDetector.getMaxCheckInterval(), rawDetector.getThreshold(),
  83.              rawDetector.getMaxIterationCount(), new LocalHandler<>(),
  84.              rawDetector, enabler);
  85.     }

  86.     /** Protected constructor with full parameters.
  87.      * <p>
  88.      * This constructor is not public 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
  93.      * @param threshold convergence threshold (s)
  94.      * @param maxIter maximum number of iterations in the event time search
  95.      * @param handler event handler to call at event occurrences
  96.      * @param rawDetector event detector to wrap
  97.      * @param enabler event enabling function to use
  98.      */
  99.     @SuppressWarnings("unchecked")
  100.     protected FieldEventEnablingPredicateFilter(final FieldAdaptableInterval<T> maxCheck, final T threshold,
  101.                                                 final int maxIter, final FieldEventHandler<T> handler,
  102.                                                 final FieldEventDetector<T> rawDetector,
  103.                                                 final FieldEnablingPredicate<T> enabler) {
  104.         super(maxCheck, threshold, maxIter, handler);
  105.         this.rawDetector  = rawDetector;
  106.         this.enabler      = enabler;
  107.         this.transformers = new Transformer[HISTORY_SIZE];
  108.         this.updates      = (FieldAbsoluteDate<T>[]) Array.newInstance(FieldAbsoluteDate.class, HISTORY_SIZE);
  109.     }

  110.     /** {@inheritDoc} */
  111.     @Override
  112.     protected FieldEventEnablingPredicateFilter<T> create(final FieldAdaptableInterval<T> newMaxCheck, final T newThreshold,
  113.                                                           final int newMaxIter,
  114.                                                           final FieldEventHandler<T> newHandler) {
  115.         return new FieldEventEnablingPredicateFilter<>(newMaxCheck, newThreshold, newMaxIter, newHandler, rawDetector, enabler);
  116.     }

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

  124.     /**  {@inheritDoc} */
  125.     public void init(final FieldSpacecraftState<T> s0,
  126.                      final FieldAbsoluteDate<T> t) {
  127.         super.init(s0, t);

  128.         // delegate to raw detector
  129.         rawDetector.init(s0, t);

  130.         // initialize events triggering logic
  131.         forward  = t.compareTo(s0.getDate()) >= 0;
  132.         extremeT = forward ?
  133.                    FieldAbsoluteDate.getPastInfinity(t.getField()) :
  134.                    FieldAbsoluteDate.getFutureInfinity(t.getField());
  135.         extremeG = t.getField().getZero().newInstance(Double.NaN);
  136.         Arrays.fill(transformers, Transformer.UNINITIALIZED);
  137.         Arrays.fill(updates, extremeT);

  138.     }

  139.     /**  {@inheritDoc} */
  140.     public T g(final FieldSpacecraftState<T> s) {

  141.         final T       rawG      = rawDetector.g(s);
  142.         final boolean isEnabled = enabler.eventIsEnabled(s, rawDetector, rawG);
  143.         if (extremeG.isNaN()) {
  144.             extremeG = rawG;
  145.         }

  146.         // search which transformer should be applied to g
  147.         if (forward) {
  148.             final int last = transformers.length - 1;
  149.             if (extremeT.compareTo(s.getDate()) < 0) {
  150.                 // we are at the forward end of the history

  151.                 // check if enabled status has changed
  152.                 final Transformer previous = transformers[last];
  153.                 final Transformer next     = selectTransformer(previous, extremeG, isEnabled);
  154.                 if (next != previous) {
  155.                     // there is a status change somewhere between extremeT and t.
  156.                     // the new transformer is valid for t (this is how we have just computed
  157.                     // it above), but it is in fact valid on both sides of the change, so
  158.                     // it was already valid before t and even up to previous time. We store
  159.                     // the switch at extremeT for safety, to ensure the previous transformer
  160.                     // is not applied too close of the root
  161.                     System.arraycopy(updates,      1, updates,      0, last);
  162.                     System.arraycopy(transformers, 1, transformers, 0, last);
  163.                     updates[last]      = extremeT;
  164.                     transformers[last] = next;
  165.                 }

  166.                 extremeT = s.getDate();
  167.                 extremeG = rawG;

  168.                 // apply the transform
  169.                 return next.transformed(rawG);

  170.             } else {
  171.                 // we are in the middle of the history

  172.                 // select the transformer
  173.                 for (int i = last; i > 0; --i) {
  174.                     if (updates[i].compareTo(s.getDate()) <= 0) {
  175.                         // apply the transform
  176.                         return transformers[i].transformed(rawG);
  177.                     }
  178.                 }

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

  180.             }
  181.         } else {
  182.             if (s.getDate().compareTo(extremeT) < 0) {
  183.                 // we are at the backward end of the history

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

  199.                 extremeT = s.getDate();
  200.                 extremeG = rawG;

  201.                 // apply the transform
  202.                 return next.transformed(rawG);

  203.             } else {
  204.                 // we are in the middle of the history

  205.                 // select the transformer
  206.                 for (int i = 0; i < updates.length - 1; ++i) {
  207.                     if (s.getDate().compareTo(updates[i]) <= 0) {
  208.                         // apply the transform
  209.                         return transformers[i].transformed(rawG);
  210.                     }
  211.                 }

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

  213.             }
  214.         }

  215.     }

  216.     /** Get next function transformer in the specified direction.
  217.      * @param previous transformer active on the previous point with respect
  218.      * to integration direction (may be null if no previous point is known)
  219.      * @param previousG value of the g function at the previous point
  220.      * @param isEnabled if true the event should be enabled now
  221.      * @return next transformer transformer
  222.      */
  223.     private Transformer selectTransformer(final Transformer previous, final T previousG, final boolean isEnabled) {
  224.         if (isEnabled) {
  225.             // we need to select a transformer that can produce zero crossings,
  226.             // so it is either Transformer.PLUS or Transformer.MINUS
  227.             switch (previous) {
  228.                 case UNINITIALIZED :
  229.                     return Transformer.PLUS; // this initial choice is arbitrary, it could have been Transformer.MINUS
  230.                 case MIN :
  231.                     return previousG.getReal() >= 0 ? Transformer.MINUS : Transformer.PLUS;
  232.                 case MAX :
  233.                     return previousG.getReal() >= 0 ? Transformer.PLUS : Transformer.MINUS;
  234.                 default :
  235.                     return previous;
  236.             }
  237.         } else {
  238.             // we need to select a transformer that cannot produce any zero crossings,
  239.             // so it is either Transformer.MAX or Transformer.MIN
  240.             switch (previous) {
  241.                 case UNINITIALIZED :
  242.                     return Transformer.MAX; // this initial choice is arbitrary, it could have been Transformer.MIN
  243.                 case PLUS :
  244.                     return previousG.getReal() >= 0 ? Transformer.MAX : Transformer.MIN;
  245.                 case MINUS :
  246.                     return previousG.getReal() >= 0 ? Transformer.MIN : Transformer.MAX;
  247.                 default :
  248.                     return previous;
  249.             }
  250.         }
  251.     }

  252.     /** Local handler.
  253.      * @param <T> type of the field elements
  254.      */
  255.     private static class LocalHandler<T extends CalculusFieldElement<T>> implements FieldEventHandler<T> {

  256.         /** {@inheritDoc} */
  257.         public Action eventOccurred(final FieldSpacecraftState<T> s, final FieldEventDetector<T> detector, final boolean increasing) {
  258.             final FieldEventEnablingPredicateFilter<T> ef = (FieldEventEnablingPredicateFilter<T>) detector;
  259.             final Transformer transformer = ef.forward ? ef.transformers[ef.transformers.length - 1] : ef.transformers[0];
  260.             return ef.rawDetector.getHandler().eventOccurred(s, ef.rawDetector, transformer == Transformer.PLUS ? increasing : !increasing);
  261.         }

  262.         /** {@inheritDoc} */
  263.         @Override
  264.         public FieldSpacecraftState<T> resetState(final FieldEventDetector<T> detector, final FieldSpacecraftState<T> oldState) {
  265.             final FieldEventEnablingPredicateFilter<T> ef = (FieldEventEnablingPredicateFilter<T>) detector;
  266.             return ef.rawDetector.getHandler().resetState(ef.rawDetector, oldState);
  267.         }

  268.     }

  269. }