1 /* Copyright 2002-2022 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 represents providers for additional state data beyond {@link SpacecraftState}.
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 add these
27 * additional states incrementally after having computed the basic components
28 * (orbit, attitude and mass).
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 * #getAdditionalState(SpacecraftState) getAdditionalState} 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 * #yield(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 yied and cannot {@link #getAdditionalState(SpacecraftState) retrieve} their additional
55 * state. This happens 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 * @author Luc Maisonobe
73 */
74 public interface AdditionalStateProvider {
75
76 /** Get the name of the additional state.
77 * @return name of the additional state (names containing "orekit"
78 * with any case are reserved for the library internal use)
79 */
80 String getName();
81
82 /** Initialize the additional state provider at the start of propagation.
83 * @param initialState initial state information at the start of propagation
84 * @param target date of propagation
85 * @since 11.2
86 */
87 default void init(final SpacecraftState initialState, final AbsoluteDate target) {
88 // nothing by default
89 }
90
91 /** Check if this provider should yield so another provider has an opportunity to add missing parts.
92 * <p>
93 * Decision to yield is often based on an additional state being {@link SpacecraftState#hasAdditionalState(String)
94 * already available} in the provided {@code state} (but it could theoretically also depend on
95 * an additional state derivative being {@link SpacecraftState#hasAdditionalStateDerivative(String)
96 * already available}, or any other criterion). If for example a provider needs the state transition
97 * matrix, it could implement this method as:
98 * </p>
99 * <pre>{@code
100 * public boolean yield(final SpacecraftState state) {
101 * return !state.getAdditionalStates().containsKey("STM");
102 * }
103 * }</pre>
104 * <p>
105 * The default implementation returns {@code false}, meaning that state data can be
106 * {@link #getAdditionalState(SpacecraftState) generated} immediately.
107 * </p>
108 * @param state state to handle
109 * @return true if this provider should yield so another provider has an opportunity to add missing parts
110 * as the state is incrementally built up
111 * @since 11.1
112 */
113 default boolean yield(SpacecraftState state) {
114 return false;
115 }
116
117 /** Get the additional state.
118 * @param state spacecraft state to which additional state should correspond
119 * @return additional state corresponding to spacecraft state
120 */
121 double[] getAdditionalState(SpacecraftState state);
122
123 }