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.analysis.differentiation.Gradient;
20  import org.hipparchus.geometry.euclidean.threed.Vector3D;
21  import org.junit.jupiter.api.Assertions;
22  import org.junit.jupiter.api.Test;
23  import org.orekit.control.indirect.shooting.boundary.CartesianBoundaryConditionChecker;
24  import org.orekit.control.indirect.shooting.boundary.FixedTimeBoundaryOrbits;
25  import org.orekit.control.indirect.shooting.boundary.FixedTimeCartesianBoundaryStates;
26  import org.orekit.control.indirect.shooting.boundary.NormBasedCartesianConditionChecker;
27  import org.orekit.control.indirect.shooting.propagation.AdjointDynamicsProvider;
28  import org.orekit.control.indirect.shooting.propagation.CartesianAdjointDynamicsProviderFactory;
29  import org.orekit.control.indirect.shooting.propagation.ShootingIntegrationSettings;
30  import org.orekit.control.indirect.shooting.propagation.ShootingIntegrationSettingsFactory;
31  import org.orekit.control.indirect.shooting.propagation.ShootingPropagationSettings;
32  import org.orekit.frames.FramesFactory;
33  import org.orekit.orbits.CartesianOrbit;
34  import org.orekit.orbits.Orbit;
35  import org.orekit.propagation.FieldSpacecraftState;
36  import org.orekit.time.AbsoluteDate;
37  import org.orekit.utils.AbsolutePVCoordinates;
38  import org.orekit.utils.PVCoordinates;
39  
40  import java.util.ArrayList;
41  
42  class AbstractFixedBoundaryCartesianSingleShootingTest {
43  
44      @Test
45      void testSolveZeroTimeOfFlight() {
46          // GIVEN
47          final String adjointName = "adjoint";
48          final AbsolutePVCoordinates initialCondition = new AbsolutePVCoordinates(FramesFactory.getGCRF(),
49                  AbsoluteDate.ARBITRARY_EPOCH, Vector3D.PLUS_I, Vector3D.MINUS_J);
50          final FixedTimeCartesianBoundaryStates boundaryStates = new FixedTimeCartesianBoundaryStates(initialCondition,
51                  initialCondition);
52          final TestSingleShooting testSingleShooting = new TestSingleShooting(buildPropagationSettings(), boundaryStates,
53                  new NormBasedCartesianConditionChecker(10, 1, 1));
54          final double[] initialGuess = new double[6];
55          // WHEN
56          final ShootingBoundaryOutput boundarySolution = testSingleShooting.solve(1., initialGuess);
57          // THEN
58          Assertions.assertArrayEquals(initialGuess, boundarySolution.getInitialState().getAdditionalState(adjointName));
59      }
60  
61      private static ShootingPropagationSettings buildPropagationSettings() {
62          final ShootingIntegrationSettings integrationSettings = ShootingIntegrationSettingsFactory.getClassicalRungeKuttaIntegratorSettings(10.);
63          final AdjointDynamicsProvider derivativesProvider = CartesianAdjointDynamicsProviderFactory.buildUnboundedEnergyProviderNeglectingMass("adjoint");
64          return new ShootingPropagationSettings(new ArrayList<>(), derivativesProvider, integrationSettings);
65      }
66  
67      @Test
68      void testSolveImpossibleTolerance() {
69          // GIVEN
70          final double impossibleTolerance = 0.;
71          final TestSingleShooting testSingleShooting = new TestSingleShooting(buildPropagationSettings(),
72                  createBoundaries(), new NormBasedCartesianConditionChecker(10,  impossibleTolerance, impossibleTolerance));
73          final double[] initialGuess = new double[6];
74          // WHEN
75          final ShootingBoundaryOutput output = testSingleShooting.solve(1., initialGuess);
76          // THEN
77          Assertions.assertFalse(output.isConverged());
78          Assertions.assertEquals(testSingleShooting.getPropagationSettings(), output.getShootingPropagationSettings());
79      }
80  
81      private static FixedTimeBoundaryOrbits createBoundaries() {
82          final Orbit initialCondition = new CartesianOrbit(new PVCoordinates(Vector3D.PLUS_I,
83                  Vector3D.MINUS_J), FramesFactory.getGCRF(), AbsoluteDate.ARBITRARY_EPOCH, 1.);
84          return new FixedTimeBoundaryOrbits(initialCondition, initialCondition);
85      }
86  
87      private static class TestSingleShooting extends AbstractFixedBoundaryCartesianSingleShooting {
88  
89          protected TestSingleShooting(ShootingPropagationSettings propagationSettings, FixedTimeCartesianBoundaryStates boundaryConditions, CartesianBoundaryConditionChecker convergenceChecker) {
90              super(propagationSettings, boundaryConditions, convergenceChecker);
91          }
92  
93          protected TestSingleShooting(ShootingPropagationSettings propagationSettings, FixedTimeBoundaryOrbits boundaryConditions, CartesianBoundaryConditionChecker convergenceChecker) {
94              super(propagationSettings, boundaryConditions, convergenceChecker);
95          }
96  
97          @Override
98          public int getMaximumIterationCount() {
99              return 0;
100         }
101 
102         @Override
103         protected double[] updateShootingVariables(double[] originalShootingVariables, FieldSpacecraftState<Gradient> fieldTerminalState) {
104             return originalShootingVariables;
105         }
106     }
107 
108 }