1   /* Copyright 2002-2024 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;
18  
19  import org.orekit.time.AbsoluteDate;
20  
21  /** This interface allows to modify {@link SpacecraftState} and set up additional state data.
22   * <p>
23   * {@link Propagator Propagators} generate {@link SpacecraftState states} that contain at
24   * least orbit, attitude, and mass. These states may however also contain {@link
25   * SpacecraftState#addAdditionalState(String, double...) additional states}. Instances of classes
26   * implementing this interface are intended to be registered to propagators so they can either
27   * modify the basic components (orbit, attitude and mass) or add additional states incrementally
28   * after having computed the basic components.
29   * </p>
30   * <p>
31   * Some additional states may depend on previous additional states to
32   * be already available the before they can be computed. It may even be impossible to compute some
33   * of these additional states at some time if they depend on conditions that are fulfilled only
34   * after propagation as started or some event has occurred. As the propagator builds the complete
35   * state incrementally, looping over the registered providers, it must call their {@link
36   * #update(SpacecraftState) update} methods in an order that fulfill these dependencies that
37   * may be time-dependent and are not related to the order in which the providers are registered to
38   * the propagator. This reordering is performed each time the complete state is built, using a yield
39   * mechanism. The propagator first pushes all providers in a stack and then empty the stack, one provider
40   * at a time, taking care to select only providers that do <em>not</em> {@link
41   * #yields(SpacecraftState) yield} when asked. Consider for example a case where providers A, B and C
42   * have been registered and provider B needs in fact the additional state generated by provider C. Then
43   * when a complete state is built, the propagator puts the three providers in a new stack, and then starts the incremental
44   * generation of additional states. It first checks provider A which does not yield so it is popped from
45   * the stack and the additional state it generates is added. Then provider B is checked, but it yields
46   * because state from provider C is not yet available. So propagator checks provider C which does not
47   * yield, so it is popped out of the stack and applied. At this stage, provider B is the only remaining one
48   * in the stack, so it is checked again, but this time it does not yield because the state from provider
49   * C is available as it has just been added, so provider B is popped from the stack and applied. The stack
50   * is now empty and the propagator can return the completed state.
51   * </p>
52   * <p>
53   * It is possible that at some stages in the propagation, a subset of the providers registered to a
54   * propagator all yield and cannot {@link #update(SpacecraftState) update} the state. This happens
55   * for example during the initialization phase of a propagator that
56   * computes State Transition Matrices or Jacobian matrices. These features are managed as secondary equations
57   * in the ODE integrator, and initialized after the primary equations (which correspond to orbit) have
58   * been initialized. So when the primary equation are initialized, the providers that depend on the secondary
59   * state will all yield. This behavior is expected. Another case occurs when users set up additional states
60   * that induce a dependency loop (state A depending on state B which depends on state C which depends on
61   * state A). In this case, the three corresponding providers will wait for each other and indefinitely yield.
62   * This second case is a deadlock and results from a design error of the additional states management at
63   * application level. The propagator cannot know it in advance if a subset of providers that all yield is
64   * normal or not. So at propagator level, when either situation is detected, the propagator just gives up and
65   * returns the most complete state it was able to compute, without generating any error. Errors will indeed
66   * not be triggered in the first case (once the primary equations have been initialized, the secondary
67   * equations will be initialized too), and they will be triggered in the second case as soon as user attempts
68   * to retrieve an additional state that was not added.
69   * </p>
70   * @see org.orekit.propagation.Propagator
71   * @see org.orekit.propagation.integration.AdditionalDerivativesProvider
72   * @see AbstractStateModifier
73   * @author Luc Maisonobe
74   */
75  public interface AdditionalStateProvider {
76  
77      /** Get the name of the additional state.
78       * <p>
79       * If a provider just modifies one of the basic elements (orbit, attitude
80       * or mass) without adding any new state, it should return the empty string
81       * as its name.
82       * </p>
83       * @return name of the additional state (names containing "orekit"
84       * with any case are reserved for the library internal use)
85       */
86      String getName();
87  
88      /** Initialize the additional state provider at the start of propagation.
89       * @param initialState initial state information at the start of propagation
90       * @param target       date of propagation
91       * @since 11.2
92       */
93      default void init(final SpacecraftState initialState, final AbsoluteDate target) {
94          // nothing by default
95      }
96  
97      /** Check if this provider should yield so another provider has an opportunity to add missing parts.
98       * <p>
99       * Decision to yield is often based on an additional state being {@link SpacecraftState#hasAdditionalState(String)
100      * already available} in the provided {@code state} (but it could theoretically also depend on
101      * an additional state derivative being {@link SpacecraftState#hasAdditionalStateDerivative(String)
102      * already available}, or any other criterion). If for example a provider needs the state transition
103      * matrix, it could implement this method as:
104      * </p>
105      * <pre>{@code
106      * public boolean yields(final SpacecraftState state) {
107      *     return !state.getAdditionalStates().containsKey("STM");
108      * }
109      * }</pre>
110      * <p>
111      * The default implementation returns {@code false}, meaning that state data can be
112      * {@link #getAdditionalState(SpacecraftState) generated} immediately.
113      * </p>
114      * @param state state to handle
115      * @return true if this provider should yield so another provider has an opportunity to add missing parts
116      * as the state is incrementally built up
117      * @since 11.1
118      */
119     default boolean yields(SpacecraftState state) {
120         return false;
121     }
122 
123     /** Get the additional state.
124      * @param state spacecraft state to which additional state should correspond
125      * @return additional state corresponding to spacecraft state
126      */
127     double[] getAdditionalState(SpacecraftState state);
128 
129     /** Update a state.
130      * @param state spacecraft state to update
131      * @return updated state
132      * @since 12.1
133      */
134     default SpacecraftState update(final SpacecraftState state) {
135         return state.addAdditionalState(getName(), getAdditionalState(state));
136     }
137 
138 }