FieldEventEnablingPredicateFilter.java

  1. /* Copyright 2022-2025 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>> implements FieldDetectorModifier<T> {

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

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

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

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

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

  69.     /** Event detection settings. */
  70.     private final FieldEventDetectionSettings<T> detectionSettings;

  71.     /** Specialized event handler. */
  72.     private final LocalHandler<T> handler;

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

  75.     /** Extreme time encountered so far. */
  76.     private FieldAbsoluteDate<T> extremeT;

  77.     /** Detector function value at extremeT. */
  78.     private T extremeG;

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

  87.     /** Constructor with full parameters.
  88.      * @param detectionSettings event detection settings
  89.      * @param rawDetector event detector to wrap
  90.      * @param enabler event enabling function to use
  91.      * @since 13.0
  92.      */
  93.     @SuppressWarnings("unchecked")
  94.     public FieldEventEnablingPredicateFilter(final FieldEventDetectionSettings<T> detectionSettings,
  95.                                              final FieldEventDetector<T> rawDetector,
  96.                                              final FieldEnablingPredicate<T> enabler) {
  97.         this.detectionSettings = detectionSettings;
  98.         this.handler = new LocalHandler<>();
  99.         this.rawDetector  = rawDetector;
  100.         this.enabler      = enabler;
  101.         this.transformers = new Transformer[HISTORY_SIZE];
  102.         this.updates      = (FieldAbsoluteDate<T>[]) Array.newInstance(FieldAbsoluteDate.class, HISTORY_SIZE);
  103.     }

  104.     /**
  105.      * Builds a new instance from the input detection settings.
  106.      * @param settings event detection settings to be used
  107.      * @return a new detector
  108.      */
  109.     public FieldEventEnablingPredicateFilter<T> withDetectionSettings(final FieldEventDetectionSettings<T> settings) {
  110.         return new FieldEventEnablingPredicateFilter<>(settings, rawDetector, enabler);
  111.     }

  112.     /**
  113.      * Get the wrapped raw detector.
  114.      * @return the wrapped raw detector
  115.      */
  116.     public FieldEventDetector<T> getDetector() {
  117.         return rawDetector;
  118.     }

  119.     /**  {@inheritDoc} */
  120.     @Override
  121.     public FieldEventHandler<T> getHandler() {
  122.         return handler;
  123.     }

  124.     /**  {@inheritDoc} */
  125.     @Override
  126.     public FieldEventDetectionSettings<T> getDetectionSettings() {
  127.         return detectionSettings;
  128.     }

  129.     /**  {@inheritDoc} */
  130.     @Override
  131.     public void init(final FieldSpacecraftState<T> s0, final FieldAbsoluteDate<T> t) {
  132.         FieldDetectorModifier.super.init(s0, t);

  133.         // initialize events triggering logic
  134.         forward  = FieldAbstractDetector.checkIfForward(s0, t);
  135.         extremeT = forward ?
  136.                    FieldAbsoluteDate.getPastInfinity(t.getField()) :
  137.                    FieldAbsoluteDate.getFutureInfinity(t.getField());
  138.         extremeG = t.getField().getZero().newInstance(Double.NaN);
  139.         Arrays.fill(transformers, Transformer.UNINITIALIZED);
  140.         Arrays.fill(updates, extremeT);

  141.     }

  142.     @Override
  143.     public void reset(final FieldSpacecraftState<T> state, final FieldAbsoluteDate<T> target) {
  144.         FieldDetectorModifier.super.reset(state, target);
  145.         forward  = FieldAbstractDetector.checkIfForward(state, target);
  146.         extremeT = forward ?
  147.                 FieldAbsoluteDate.getPastInfinity(target.getField()) :
  148.                 FieldAbsoluteDate.getFutureInfinity(target.getField());
  149.         extremeG = target.getField().getZero().newInstance(Double.NaN);
  150.     }

  151.     /**  {@inheritDoc} */
  152.     @Override
  153.     public T g(final FieldSpacecraftState<T> s) {

  154.         final T       rawG      = rawDetector.g(s);
  155.         final boolean isEnabled = enabler.eventIsEnabled(s, rawDetector, rawG);
  156.         if (extremeG.isNaN()) {
  157.             extremeG = rawG;
  158.         }

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

  164.                 // check if enabled status has changed
  165.                 final Transformer previous = transformers[last];
  166.                 final Transformer next     = selectTransformer(previous, extremeG, isEnabled);
  167.                 if (next != previous) {
  168.                     // there is a status change 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 change, 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 = s.getDate();
  180.                 extremeG = rawG;

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

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

  185.                 // select the transformer
  186.                 for (int i = last; i > 0; --i) {
  187.                     if (updates[i].compareTo(s.getDate()) <= 0) {
  188.                         // apply the transform
  189.                         return transformers[i].transformed(rawG);
  190.                     }
  191.                 }

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

  193.             }
  194.         } else {
  195.             if (s.getDate().compareTo(extremeT) < 0) {
  196.                 // we are at the backward end of the history

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

  212.                 extremeT = s.getDate();
  213.                 extremeG = rawG;

  214.                 // apply the transform
  215.                 return next.transformed(rawG);

  216.             } else {
  217.                 // we are in the middle of the history

  218.                 // select the transformer
  219.                 for (int i = 0; i < updates.length - 1; ++i) {
  220.                     if (s.getDate().compareTo(updates[i]) <= 0) {
  221.                         // apply the transform
  222.                         return transformers[i].transformed(rawG);
  223.                     }
  224.                 }

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

  226.             }
  227.         }

  228.     }

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

  265.     /** Check if the current propagation is forward or backward.
  266.      * @return true if the current propagation is forward
  267.      */
  268.     public boolean isForward() {
  269.         return forward;
  270.     }

  271.     /** Local handler.
  272.      * @param <T> type of the field elements
  273.      */
  274.     private static class LocalHandler<T extends CalculusFieldElement<T>> implements FieldEventHandler<T> {

  275.         /** {@inheritDoc} */
  276.         public Action eventOccurred(final FieldSpacecraftState<T> s, final FieldEventDetector<T> detector, final boolean increasing) {
  277.             final FieldEventEnablingPredicateFilter<T> ef = (FieldEventEnablingPredicateFilter<T>) detector;
  278.             final Transformer transformer = ef.forward ? ef.transformers[ef.transformers.length - 1] : ef.transformers[0];
  279.             return ef.rawDetector.getHandler().eventOccurred(s, ef.rawDetector, transformer == Transformer.PLUS ? increasing : !increasing);
  280.         }

  281.         /** {@inheritDoc} */
  282.         @Override
  283.         public FieldSpacecraftState<T> resetState(final FieldEventDetector<T> detector, final FieldSpacecraftState<T> oldState) {
  284.             final FieldEventEnablingPredicateFilter<T> ef = (FieldEventEnablingPredicateFilter<T>) detector;
  285.             return ef.rawDetector.getHandler().resetState(ef.rawDetector, oldState);
  286.         }

  287.     }

  288. }