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.adjoint.cost;
18  
19  
20  import org.hipparchus.geometry.euclidean.threed.FieldVector3D;
21  import org.hipparchus.geometry.euclidean.threed.Vector3D;
22  import org.hipparchus.util.Binary64;
23  import org.hipparchus.util.Binary64Field;
24  import org.hipparchus.util.FastMath;
25  import org.hipparchus.util.MathArrays;
26  import org.junit.jupiter.api.Assertions;
27  import org.junit.jupiter.params.ParameterizedTest;
28  import org.junit.jupiter.params.provider.ValueSource;
29  import org.orekit.control.indirect.adjoint.CartesianAdjointDerivativesProvider;
30  import org.orekit.control.indirect.adjoint.FieldCartesianAdjointDerivativesProvider;
31  import org.orekit.frames.FramesFactory;
32  import org.orekit.orbits.CartesianOrbit;
33  import org.orekit.propagation.FieldSpacecraftState;
34  import org.orekit.propagation.SpacecraftState;
35  import org.orekit.propagation.integration.CombinedDerivatives;
36  import org.orekit.propagation.integration.FieldAdditionalDerivativesProvider;
37  import org.orekit.propagation.integration.FieldCombinedDerivatives;
38  import org.orekit.time.AbsoluteDate;
39  import org.orekit.utils.PVCoordinates;
40  
41  class FieldLogarithmicBarrierCartesianFuelTest {
42  
43      private static final String ADJOINT_NAME = "adjoint";
44  
45      @ParameterizedTest
46      @ValueSource(booleans = {false, true})
47      void testUpdateFieldAdjointDerivatives(final boolean withMass) {
48          // GIVEN
49          final Binary64 massFlowRateFactor = withMass ? Binary64.ONE : Binary64.ZERO;
50          final FieldLogarithmicBarrierCartesianFuel<Binary64> cost = new FieldLogarithmicBarrierCartesianFuel<>("adjoint", massFlowRateFactor,
51                  Binary64.PI, new Binary64(0.5));
52          final Binary64[] adjoint = MathArrays.buildArray(Binary64Field.getInstance(), withMass ? 7 : 6);
53          final Binary64[] derivatives = adjoint.clone();
54          adjoint[3] = Binary64.ONE;
55          // WHEN
56          cost.updateFieldAdjointDerivatives(adjoint, Binary64.ONE, derivatives);
57          // THEN
58          final Binary64 zero = Binary64.ZERO;
59          for (int i = 0; i < 6; ++i) {
60              Assertions.assertEquals(zero, derivatives[i]);
61          }
62          if (withMass) {
63              Assertions.assertNotEquals(zero, derivatives[derivatives.length - 1]);
64          } else {
65              Assertions.assertEquals(zero, derivatives[derivatives.length - 1]);
66          }
67      }
68  
69      @ParameterizedTest
70      @ValueSource(doubles = {0, 0.1, 0.5, 0.9})
71      void testEvaluateFieldPenaltyFunction(final double norm) {
72          // GIVEN
73          final FieldLogarithmicBarrierCartesianFuel<Binary64> penalizedCartesianFuel = new FieldLogarithmicBarrierCartesianFuel<>(
74                  ADJOINT_NAME, Binary64.ONE, Binary64.PI, Binary64.ZERO);
75          // WHEN
76          final Binary64 actualPenalty = penalizedCartesianFuel.evaluateFieldPenaltyFunction(Binary64.ONE.newInstance(norm));
77          // THEN
78          Assertions.assertEquals(FastMath.log(norm) + FastMath.log(1 - norm), actualPenalty.getReal());
79      }
80  
81      @ParameterizedTest
82      @ValueSource(doubles = {1e-3, 1e-2, 0.5, 0.999})
83      void testGetFieldHamiltonianContribution(final double epsilon) {
84          // GIVEN
85          final FieldLogarithmicBarrierCartesianFuel<Binary64> fieldCost = new FieldLogarithmicBarrierCartesianFuel<>(
86                  ADJOINT_NAME, Binary64.ONE, Binary64.PI, new Binary64(epsilon));
87          final double[] adjoint = new double[] {1, 2, 3, 4, 5, 6, 7};
88          final Binary64[] fieldAdjoint = MathArrays.buildArray(Binary64Field.getInstance(), adjoint.length);
89          for (int i = 0; i < adjoint.length; i++) {
90              fieldAdjoint[i] = fieldCost.getEpsilon().newInstance(adjoint[i]);
91          }
92          final Binary64 mass = new Binary64(100);
93          // WHEN
94          final Binary64 actualPenalty = fieldCost.getFieldHamiltonianContribution(fieldAdjoint, mass);
95          // THEN
96          final LogarithmicBarrierCartesianFuel cost = fieldCost.toCartesianCost();
97          Assertions.assertEquals(cost.getHamiltonianContribution(adjoint, mass.getReal()), actualPenalty.getReal());
98      }
99  
100     @ParameterizedTest
101     @ValueSource(doubles = {1e-3, 1e-2, 0.5, 0.999})
102     void testGetFieldThrustAccelerationVector(final double epsilon) {
103         // GIVEN
104         final FieldLogarithmicBarrierCartesianFuel<Binary64> fieldCost = new FieldLogarithmicBarrierCartesianFuel<>(
105                 ADJOINT_NAME, Binary64.ONE, Binary64.PI, new Binary64(epsilon));
106         final double[] adjoint = new double[] {1, 2, 3, 4, 5, 6, 7};
107         final Binary64[] fieldAdjoint = MathArrays.buildArray(Binary64Field.getInstance(), adjoint.length);
108         for (int i = 0; i < adjoint.length; i++) {
109             fieldAdjoint[i] = fieldCost.getEpsilon().newInstance(adjoint[i]);
110         }
111         final Binary64 mass = new Binary64(1000);
112         // WHEN
113         final FieldVector3D<Binary64> actualThrust = fieldCost.getFieldThrustAccelerationVector(fieldAdjoint, mass);
114         // THEN
115         final LogarithmicBarrierCartesianFuel cost = fieldCost.toCartesianCost();
116         Assertions.assertEquals(cost.getThrustAccelerationVector(adjoint, mass.getReal()), actualThrust.toVector3D());
117     }
118 
119     @ParameterizedTest
120     @ValueSource(doubles = {1, 1e2, 1e4})
121     void testAgainstNonField(final double mass) {
122         // GIVEN
123         final double massFlowRateFactor = 1.e-2;
124         final double maximumThrustMagnitude = 1e-3;
125         final double epsilon = 0.5;
126         final Binary64 zero = Binary64.ZERO;
127         final FieldLogarithmicBarrierCartesianFuel<Binary64> fieldCost = new FieldLogarithmicBarrierCartesianFuel<>(ADJOINT_NAME,
128                 zero.newInstance(massFlowRateFactor), zero.newInstance(maximumThrustMagnitude), zero.newInstance(epsilon));
129         final SpacecraftState state = buildState(mass);
130         final FieldSpacecraftState<Binary64> fieldState = new FieldSpacecraftState<>(Binary64Field.getInstance(), state);
131         final FieldAdditionalDerivativesProvider<Binary64> derivativesProvider = new FieldCartesianAdjointDerivativesProvider<>(fieldCost);
132         // WHEN
133         final FieldCombinedDerivatives<Binary64> actualDerivatives = derivativesProvider.combinedDerivatives(fieldState);
134         // THEN
135         final LogarithmicBarrierCartesianFuel cost = new LogarithmicBarrierCartesianFuel(ADJOINT_NAME,
136                 massFlowRateFactor, maximumThrustMagnitude, epsilon);
137         final CombinedDerivatives expectedDerivatives = new CartesianAdjointDerivativesProvider(cost)
138                 .combinedDerivatives(state);
139         for (int i = 0; i < expectedDerivatives.getMainStateDerivativesIncrements().length; i++) {
140             Assertions.assertEquals(expectedDerivatives.getMainStateDerivativesIncrements()[i],
141                     actualDerivatives.getMainStateDerivativesIncrements()[i].getReal(), 1e-12);
142         }
143         for (int i = 0; i < expectedDerivatives.getAdditionalDerivatives().length; i++) {
144             Assertions.assertEquals(expectedDerivatives.getAdditionalDerivatives()[i],
145                     actualDerivatives.getAdditionalDerivatives()[i].getReal());
146         }
147     }
148 
149     private SpacecraftState buildState(final double mass) {
150         final double[] adjoint = new double[] {1, 2, 3, 4, 5, 6, 7};
151         final CartesianOrbit orbit = new CartesianOrbit(new PVCoordinates(Vector3D.MINUS_I, Vector3D.MINUS_K),
152                 FramesFactory.getEME2000(), AbsoluteDate.ARBITRARY_EPOCH, 1.);
153         return new SpacecraftState(orbit, mass).addAdditionalData(ADJOINT_NAME, adjoint);
154     }
155 }