EventEnablingPredicateFilter.java

  1. /* Copyright 2002-2025 CS GROUP
  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.util.Arrays;

  19. import org.hipparchus.ode.events.Action;
  20. import org.orekit.propagation.SpacecraftState;
  21. import org.orekit.propagation.events.handlers.EventHandler;
  22. import org.orekit.time.AbsoluteDate;

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

  55. public class EventEnablingPredicateFilter implements DetectorModifier {

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

  58.     /** Wrapped event detector. */
  59.     private final EventDetector rawDetector;

  60.     /** Enabling predicate function. */
  61.     private final EnablingPredicate predicate;

  62.     /** Transformers of the g function. */
  63.     private final Transformer[] transformers;

  64.     /** Update time of the transformers. */
  65.     private final AbsoluteDate[] updates;

  66.     /** Event detection settings. */
  67.     private final EventDetectionSettings detectionSettings;

  68.     /** Specialized event handler. */
  69.     private final LocalHandler handler;

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

  72.     /** Extreme time encountered so far. */
  73.     private AbsoluteDate extremeT;

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

  76.     /** Wrap an {@link EventDetector event detector}.
  77.      * @param rawDetector event detector to wrap (its detection settings are taken as well)
  78.      * @param enabler event enabling predicate function to use
  79.      */
  80.     public EventEnablingPredicateFilter(final EventDetector rawDetector,
  81.                                         final EnablingPredicate enabler) {
  82.         this(rawDetector.getDetectionSettings(), rawDetector, enabler);
  83.     }

  84.     /** Constructor with full parameters.
  85.      * @param detectionSettings event detection settings
  86.      * @param rawDetector event detector to wrap
  87.      * @param enabler event enabling function to use
  88.      * @since 13.0
  89.      */
  90.     public EventEnablingPredicateFilter(final EventDetectionSettings detectionSettings,
  91.                                         final EventDetector rawDetector, final EnablingPredicate enabler) {
  92.         this.detectionSettings = detectionSettings;
  93.         this.handler = new LocalHandler();
  94.         this.rawDetector  = rawDetector;
  95.         this.predicate = enabler;
  96.         this.transformers = new Transformer[HISTORY_SIZE];
  97.         this.updates      = new AbsoluteDate[HISTORY_SIZE];
  98.     }

  99.     /**
  100.      * Builds a new instance from the input detection settings.
  101.      * @param settings event detection settings to be used
  102.      * @return a new detector
  103.      */
  104.     public EventEnablingPredicateFilter withDetectionSettings(final EventDetectionSettings settings) {
  105.         return new EventEnablingPredicateFilter(settings, rawDetector, predicate);
  106.     }

  107.     /**
  108.      * Get the wrapped raw detector.
  109.      * @return the wrapped raw detector
  110.      * @since 11.1
  111.      */
  112.     @Override
  113.     public EventDetector getDetector() {
  114.         return rawDetector;
  115.     }

  116.     /** {@inheritDoc} */
  117.     @Override
  118.     public EventHandler getHandler() {
  119.         return handler;
  120.     }

  121.     /** {@inheritDoc} */
  122.     @Override
  123.     public EventDetectionSettings getDetectionSettings() {
  124.         return detectionSettings;
  125.     }

  126.     /**
  127.      * Getter for the enabling predicate.
  128.      * @return predicate
  129.      * @since 13.1
  130.      */
  131.     public EnablingPredicate getPredicate() {
  132.         return predicate;
  133.     }

  134.     /**  {@inheritDoc} */
  135.     @Override
  136.     public boolean dependsOnTimeOnly() {
  137.         return false;  // cannot know what predicate needs
  138.     }

  139.     /**  {@inheritDoc} */
  140.     @Override
  141.     public void init(final SpacecraftState s0, final AbsoluteDate t) {
  142.         DetectorModifier.super.init(s0, t);

  143.         // initialize events triggering logic
  144.         forward  = AbstractDetector.checkIfForward(s0, t);
  145.         extremeT = forward ? AbsoluteDate.PAST_INFINITY : AbsoluteDate.FUTURE_INFINITY;
  146.         extremeG = Double.NaN;

  147.         Arrays.fill(transformers, Transformer.UNINITIALIZED);
  148.         Arrays.fill(updates, extremeT);

  149.     }

  150.     /**  {@inheritDoc} */
  151.     @Override
  152.     public void reset(final SpacecraftState state, final AbsoluteDate target) {
  153.         DetectorModifier.super.reset(state, target);
  154.         forward  = AbstractDetector.checkIfForward(state, target);
  155.         extremeT = forward ? AbsoluteDate.PAST_INFINITY : AbsoluteDate.FUTURE_INFINITY;
  156.         extremeG = Double.NaN;
  157.     }

  158.     /**  {@inheritDoc} */
  159.     @Override
  160.     public double g(final SpacecraftState s) {

  161.         final double  rawG      = rawDetector.g(s);
  162.         final boolean isEnabled = predicate.eventIsEnabled(s, rawDetector, rawG);
  163.         if (Double.isNaN(extremeG)) {
  164.             extremeG = rawG;
  165.         }

  166.         // search which transformer should be applied to g
  167.         if (isForward()) {
  168.             final int last = transformers.length - 1;
  169.             if (extremeT.compareTo(s.getDate()) < 0) {
  170.                 // we are at the forward end of the history

  171.                 // check if enabled status has changed
  172.                 final Transformer previous = transformers[last];
  173.                 final Transformer next     = selectTransformer(previous, extremeG, isEnabled);
  174.                 if (next != previous) {
  175.                     // there is a status change somewhere between extremeT and t.
  176.                     // the new transformer is valid for t (this is how we have just computed
  177.                     // it above), but it is in fact valid on both sides of the change, so
  178.                     // it was already valid before t and even up to previous time. We store
  179.                     // the switch at extremeT for safety, to ensure the previous transformer
  180.                     // is not applied too close of the root
  181.                     System.arraycopy(updates,      1, updates,      0, last);
  182.                     System.arraycopy(transformers, 1, transformers, 0, last);
  183.                     updates[last]      = extremeT;
  184.                     transformers[last] = next;
  185.                 }

  186.                 extremeT = s.getDate();
  187.                 extremeG = rawG;

  188.                 // apply the transform
  189.                 return next.transformed(rawG);

  190.             } else {
  191.                 // we are in the middle of the history

  192.                 // select the transformer
  193.                 for (int i = last; i > 0; --i) {
  194.                     if (updates[i].compareTo(s.getDate()) <= 0) {
  195.                         // apply the transform
  196.                         return transformers[i].transformed(rawG);
  197.                     }
  198.                 }

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

  200.             }
  201.         } else {
  202.             if (s.getDate().compareTo(extremeT) < 0) {
  203.                 // we are at the backward end of the history

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

  219.                 extremeT = s.getDate();
  220.                 extremeG = rawG;

  221.                 // apply the transform
  222.                 return next.transformed(rawG);

  223.             } else {
  224.                 // we are in the middle of the history

  225.                 // select the transformer
  226.                 for (int i = 0; i < updates.length - 1; ++i) {
  227.                     if (s.getDate().compareTo(updates[i]) <= 0) {
  228.                         // apply the transform
  229.                         return transformers[i].transformed(rawG);
  230.                     }
  231.                 }

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

  233.             }
  234.         }

  235.     }

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

  272.     /** Check if the current propagation is forward or backward.
  273.      * @return true if the current propagation is forward
  274.      */
  275.     public boolean isForward() {
  276.         return forward;
  277.     }

  278.     /** Local handler. */
  279.     private static class LocalHandler implements EventHandler {

  280.         /** {@inheritDoc} */
  281.         public Action eventOccurred(final SpacecraftState s, final EventDetector detector, final boolean increasing) {
  282.             final EventEnablingPredicateFilter ef = (EventEnablingPredicateFilter) detector;
  283.             final Transformer transformer = ef.isForward() ? ef.transformers[ef.transformers.length - 1] : ef.transformers[0];
  284.             return ef.rawDetector.getHandler().eventOccurred(s, ef.rawDetector, transformer == Transformer.PLUS ? increasing : !increasing);
  285.         }

  286.         /** {@inheritDoc} */
  287.         @Override
  288.         public SpacecraftState resetState(final EventDetector detector, final SpacecraftState oldState) {
  289.             final EventEnablingPredicateFilter ef = (EventEnablingPredicateFilter) detector;
  290.             return ef.rawDetector.getHandler().resetState(ef.rawDetector, oldState);
  291.         }

  292.     }

  293. }