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.CalculusFieldElement;
20  import org.hipparchus.Field;
21  import org.hipparchus.ode.ODEIntegrator;
22  import org.hipparchus.ode.nonstiff.FieldExplicitRungeKuttaIntegrator;
23  import org.orekit.control.indirect.shooting.propagation.ShootingPropagationSettings;
24  import org.orekit.forces.ForceModel;
25  import org.orekit.forces.gravity.NewtonianAttraction;
26  import org.orekit.orbits.FieldOrbit;
27  import org.orekit.orbits.Orbit;
28  import org.orekit.propagation.FieldSpacecraftState;
29  import org.orekit.propagation.SpacecraftState;
30  import org.orekit.propagation.conversion.FieldExplicitRungeKuttaIntegratorBuilder;
31  import org.orekit.propagation.conversion.ODEIntegratorBuilder;
32  import org.orekit.propagation.integration.AdditionalDerivativesProvider;
33  import org.orekit.propagation.numerical.NumericalPropagator;
34  
35  /**
36   * Abstract class for indirect shooting methods with numerical propagation.
37   *
38   * @author Romain Serra
39   * @since 12.2
40   */
41  public abstract class AbstractIndirectShooting {
42  
43      /** Default value for convergence tolerance on mass adjoint variable. */
44      public static final double DEFAULT_TOLERANCE_MASS_ADJOINT = 1e-10;
45  
46      /** Propagation settings. */
47      private final ShootingPropagationSettings propagationSettings;
48  
49      /**
50       * Constructor.
51       * @param propagationSettings propagation settings
52       */
53      protected AbstractIndirectShooting(final ShootingPropagationSettings propagationSettings) {
54          this.propagationSettings = propagationSettings;
55      }
56  
57      /**
58       * Getter for the propagation settings.
59       * @return propagation settings
60       */
61      public ShootingPropagationSettings getPropagationSettings() {
62          return propagationSettings;
63      }
64  
65      /**
66       * Solve for the boundary conditions, given an initial mass and an initial guess for the adjoint variables.
67       * @param initialMass initial mass
68       * @param initialGuess initial guess
69       * @return boundary problem solution
70       */
71      public abstract ShootingBoundaryOutput solve(double initialMass, double[] initialGuess);
72  
73      /**
74       * Create numerical propagator.
75       * @param initialState initial state
76       * @return numerical propagator
77       */
78      protected NumericalPropagator buildPropagator(final SpacecraftState initialState) {
79          final ODEIntegrator integrator = buildIntegrator(initialState);
80          final NumericalPropagator propagator =
81                new NumericalPropagator(integrator, propagationSettings.getAttitudeProvider());
82          propagator.setIgnoreCentralAttraction(true);
83          propagator.setInitialState(initialState);
84          propagator.setIgnoreCentralAttraction(false);
85          propagator.removeForceModels();
86          if (initialState.isOrbitDefined()) {
87              propagator.setOrbitType(initialState.getOrbit().getType());
88          } else {
89              if (propagationSettings.getForceModels().stream().noneMatch(NewtonianAttraction.class::isInstance)) {
90                  propagator.setIgnoreCentralAttraction(true);
91              }
92              propagator.setOrbitType(null);
93          }
94          for (final ForceModel forceModel: propagationSettings.getForceModels()) {
95              propagator.addForceModel(forceModel);
96          }
97          final AdditionalDerivativesProvider derivativesProvider = propagationSettings.getAdjointDynamicsProvider()
98                  .buildAdditionalDerivativesProvider();
99          propagator.addAdditionalDerivativesProvider(derivativesProvider);
100         return propagator;
101     }
102 
103     /**
104      * Create integrator.
105      * @param initialState initial state
106      * @return integrator
107      */
108     private ODEIntegrator buildIntegrator(final SpacecraftState initialState) {
109         final ODEIntegratorBuilder integratorBuilder = propagationSettings.getIntegrationSettings().getIntegratorBuilder();
110         if (initialState.isOrbitDefined()) {
111             final Orbit orbit = initialState.getOrbit();
112             return integratorBuilder.buildIntegrator(orbit, orbit.getType(), NumericalPropagator.DEFAULT_POSITION_ANGLE_TYPE);
113         } else {
114             return integratorBuilder.buildIntegrator(initialState.getAbsPVA());
115         }
116     }
117 
118     /**
119      * Create Field integrator.
120      * @param initialState initial state
121      * @param <T> field type
122      * @return integrator.
123      */
124     protected <T extends CalculusFieldElement<T>> FieldExplicitRungeKuttaIntegrator<T> buildFieldIntegrator(final FieldSpacecraftState<T> initialState) {
125         final Field<T> field = initialState.getMass().getField();
126         final FieldExplicitRungeKuttaIntegratorBuilder<T> integratorBuilder = propagationSettings.getIntegrationSettings()
127             .getFieldIntegratorBuilder(field);
128         if (initialState.isOrbitDefined()) {
129             final FieldOrbit<T> orbit = initialState.getOrbit();
130             return integratorBuilder.buildIntegrator(field, orbit.toOrbit(), orbit.getType(),
131                     NumericalPropagator.DEFAULT_POSITION_ANGLE_TYPE);
132         } else {
133             return integratorBuilder.buildIntegrator(initialState.getAbsPVA());
134         }
135     }
136 
137 }