1   /* Copyright 2022-2025 Romain Serra
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.control.indirect.shooting;
18  
19  import org.hipparchus.util.FastMath;
20  import org.orekit.attitudes.Attitude;
21  import org.orekit.control.indirect.shooting.boundary.CartesianBoundaryConditionChecker;
22  import org.orekit.control.indirect.shooting.boundary.FixedTimeBoundaryOrbits;
23  import org.orekit.control.indirect.shooting.boundary.FixedTimeCartesianBoundaryStates;
24  import org.orekit.control.indirect.shooting.propagation.ShootingPropagationSettings;
25  import org.orekit.frames.Frame;
26  import org.orekit.orbits.CartesianOrbit;
27  import org.orekit.orbits.Orbit;
28  import org.orekit.propagation.SpacecraftState;
29  import org.orekit.propagation.numerical.NumericalPropagator;
30  import org.orekit.utils.AbsolutePVCoordinates;
31  import org.orekit.utils.TimeStampedPVCoordinates;
32  
33  /**
34   * Abstract class for indirect single shooting methods with Cartesian coordinates for fixed time fixed boundary.
35   * Terminal mass is assumed to be free, thus corresponding adjoint must vanish at terminal time.
36   * On the other hand, other terminal adjoint variables are free because the Cartesian state is fixed.
37   *
38   * @author Romain Serra
39   * @since 12.2
40   * @see org.orekit.control.indirect.adjoint.CartesianAdjointDerivativesProvider
41   * @see org.orekit.control.indirect.adjoint.FieldCartesianAdjointDerivativesProvider
42   */
43  public abstract class AbstractFixedBoundaryCartesianSingleShooting extends AbstractFixedInitialCartesianSingleShooting {
44  
45      /** Default value for defects scaling. */
46      private static final double DEFAULT_SCALE = 1.;
47  
48      /** Terminal Cartesian coordinates. */
49      private final TimeStampedPVCoordinates terminalCartesianState;
50  
51      /** Condition checker. */
52      private final CartesianBoundaryConditionChecker conditionChecker;
53  
54      /** Scale for velocity defects (m). */
55      private double scaleVelocityDefects;
56  
57      /** Scale for position defects (m/s). */
58      private double scalePositionDefects;
59  
60      /** Tolerance for convergence on terminal mass adjoint, if applicable to dynamics. */
61      private double toleranceMassAdjoint = DEFAULT_TOLERANCE_MASS_ADJOINT;
62  
63      /**
64       * Constructor with boundary conditions as orbits.
65       * @param propagationSettings propagation settings
66       * @param boundaryConditions boundary conditions as {@link FixedTimeCartesianBoundaryStates}
67       * @param conditionChecker boundary condition checker
68       */
69      protected AbstractFixedBoundaryCartesianSingleShooting(final ShootingPropagationSettings propagationSettings,
70                                                             final FixedTimeCartesianBoundaryStates boundaryConditions,
71                                                             final CartesianBoundaryConditionChecker conditionChecker) {
72          super(propagationSettings, buildInitialStateTemplate(boundaryConditions.getInitialCartesianState(),
73                  propagationSettings));
74          this.conditionChecker = conditionChecker;
75          this.terminalCartesianState = boundaryConditions.getTerminalCartesianState()
76                  .getPVCoordinates(propagationSettings.getPropagationFrame());
77          this.scalePositionDefects = DEFAULT_SCALE;
78          this.scaleVelocityDefects = DEFAULT_SCALE;
79      }
80  
81      /**
82       * Constructor with boundary conditions as orbits.
83       * @param propagationSettings propagation settings
84       * @param boundaryConditions boundary conditions as {@link FixedTimeBoundaryOrbits}
85       * @param conditionChecker boundary condition checker
86       */
87      protected AbstractFixedBoundaryCartesianSingleShooting(final ShootingPropagationSettings propagationSettings,
88                                                             final FixedTimeBoundaryOrbits boundaryConditions,
89                                                             final CartesianBoundaryConditionChecker conditionChecker) {
90          super(propagationSettings, buildInitialStateTemplate(boundaryConditions.getInitialOrbit(),
91                  propagationSettings));
92          this.conditionChecker = conditionChecker;
93          this.terminalCartesianState = boundaryConditions.getTerminalOrbit().getPVCoordinates(propagationSettings.getPropagationFrame());
94          this.scalePositionDefects = DEFAULT_SCALE;
95          this.scaleVelocityDefects = DEFAULT_SCALE;
96      }
97  
98      /**
99       * Setter for scale of position defects.
100      * @param scalePositionDefects new scale
101      */
102     public void setScalePositionDefects(final double scalePositionDefects) {
103         this.scalePositionDefects = scalePositionDefects;
104     }
105 
106     /**
107      * Getter for scale of position defects.
108      * @return scale
109      */
110     public double getScalePositionDefects() {
111         return scalePositionDefects;
112     }
113 
114     /**
115      * Setter for scale of velocity defects.
116      * @param scaleVelocityDefects new scale
117      */
118     public void setScaleVelocityDefects(final double scaleVelocityDefects) {
119         this.scaleVelocityDefects = scaleVelocityDefects;
120     }
121 
122     /**
123      * Getter for scale of velocity defects.
124      * @return scale
125      */
126     public double getScaleVelocityDefects() {
127         return scaleVelocityDefects;
128     }
129 
130     /**
131      * Getter for the boundary condition checker.
132      * @return checker
133      */
134     protected CartesianBoundaryConditionChecker getConditionChecker() {
135         return conditionChecker;
136     }
137 
138     /**
139      * Getter for the target terminal Cartesian state vector.
140      * @return expected terminal state
141      */
142     protected TimeStampedPVCoordinates getTerminalCartesianState() {
143         return terminalCartesianState;
144     }
145 
146     /**
147      * Setter for mass adjoint tolerance.
148      * @param toleranceMassAdjoint new tolerance value
149      */
150     public void setToleranceMassAdjoint(final double toleranceMassAdjoint) {
151         this.toleranceMassAdjoint = FastMath.abs(toleranceMassAdjoint);
152     }
153 
154     /**
155      * Create template initial state (without adjoint varialbles) for propagation from orbits.
156      * @param initialOrbit initial orbit
157      * @param propagationSettings propagation settings
158      * @return template propagation state
159      */
160     private static SpacecraftState buildInitialStateTemplate(final Orbit initialOrbit,
161                                                              final ShootingPropagationSettings propagationSettings) {
162         final Frame frame = propagationSettings.getPropagationFrame();
163         final CartesianOrbit cartesianOrbit = new CartesianOrbit(initialOrbit.getPVCoordinates(frame), frame,
164             initialOrbit.getDate(), initialOrbit.getMu());
165         final Attitude attitude = propagationSettings.getAttitudeProvider()
166                 .getAttitude(cartesianOrbit, cartesianOrbit.getDate(), cartesianOrbit.getFrame());
167         return new SpacecraftState(cartesianOrbit).withAttitude(attitude);
168     }
169 
170     /**
171      * Create template initial state (without adjoint varialbles) for propagation.
172      * @param initialCartesianState initial Cartesian state
173      * @param propagationSettings propagation settings
174      * @return template propagation state
175      */
176     private static SpacecraftState buildInitialStateTemplate(final AbsolutePVCoordinates initialCartesianState,
177                                                              final ShootingPropagationSettings propagationSettings) {
178         final Frame frame = propagationSettings.getPropagationFrame();
179         final AbsolutePVCoordinates absolutePVCoordinates = new AbsolutePVCoordinates(frame,
180                 initialCartesianState.getPVCoordinates(frame));
181         final Attitude attitude = propagationSettings.getAttitudeProvider()
182                 .getAttitude(absolutePVCoordinates, absolutePVCoordinates.getDate(), absolutePVCoordinates.getFrame());
183         return new SpacecraftState(absolutePVCoordinates, attitude);
184     }
185 
186     /** {@inheritDoc} */
187     @Override
188     public ShootingBoundaryOutput computeCandidateSolution(final SpacecraftState initialState,
189                                                            final int iterationCount) {
190         final NumericalPropagator propagator = buildPropagator(initialState);
191         final SpacecraftState actualTerminalState = propagator.propagate(getTerminalCartesianState().getDate());
192         final boolean converged = checkConvergence(actualTerminalState);
193         return new ShootingBoundaryOutput(converged, iterationCount, initialState, getPropagationSettings(),
194                 actualTerminalState);
195     }
196 
197     /**
198      * Checks convergence.
199      * @param actualTerminalState achieved terminal state
200      * @return convergence flag
201      */
202     private boolean checkConvergence(final SpacecraftState actualTerminalState) {
203         final boolean isCartesianConverged = getConditionChecker().isConverged(getTerminalCartesianState(),
204                 actualTerminalState.getPVCoordinates());
205         if (isCartesianConverged) {
206             final String adjointName = getPropagationSettings().getAdjointDynamicsProvider().getAdjointName();
207             final double[] terminalAdjoint = actualTerminalState.getAdditionalState(adjointName);
208             if (terminalAdjoint.length == 7) {
209                 return FastMath.abs(terminalAdjoint[6]) < toleranceMassAdjoint;
210             } else {
211                 return true;
212             }
213         } else {
214             return false;
215         }
216     }
217 
218 }