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 }