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