1   /* Copyright 2020-2025 Exotrail
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    * Exotrail 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.forces.maneuvers.propulsion;
18  
19  import java.util.Collections;
20  import java.util.List;
21  
22  import org.hipparchus.CalculusFieldElement;
23  import org.hipparchus.analysis.differentiation.UnivariateDerivative1;
24  import org.hipparchus.analysis.differentiation.UnivariateDerivative1Field;
25  import org.hipparchus.geometry.euclidean.threed.FieldRotation;
26  import org.hipparchus.geometry.euclidean.threed.FieldVector3D;
27  import org.hipparchus.geometry.euclidean.threed.Vector3D;
28  import org.junit.jupiter.api.Assertions;
29  import org.junit.jupiter.api.DisplayName;
30  import org.junit.jupiter.api.Test;
31  import org.mockito.Mockito;
32  import org.orekit.attitudes.FieldAttitude;
33  import org.orekit.forces.maneuvers.Control3DVectorCostType;
34  import org.orekit.propagation.FieldSpacecraftState;
35  import org.orekit.propagation.SpacecraftState;
36  import org.orekit.utils.Constants;
37  import org.orekit.time.FieldAbsoluteDate;
38  import org.orekit.utils.ParameterDriver;
39  
40  class ThrustPropulsionModelTest {
41      @Test
42      @DisplayName("Test getDirection method / issue 1046 with zero thrust")
43      void testIssue1046ZeroThrust() {
44          // Given
45          final SpacecraftState stateMock = Mockito.mock(SpacecraftState.class);
46  
47          final Vector3D thrustVectorToReturn = Vector3D.ZERO;
48  
49          final ThrustPropulsionModel model = new ThrustPropulsionModel() {
50              @Override
51              public Vector3D getThrustVector(final SpacecraftState s) {
52                  return thrustVectorToReturn;
53              }
54  
55              @Override
56              public double getFlowRate(final SpacecraftState s) {
57                  return 0;
58              }
59  
60              @Override
61              public Vector3D getThrustVector(final SpacecraftState s, final double[] parameters) {
62                  return null;
63              }
64  
65              @Override
66              public double getFlowRate(final SpacecraftState s, final double[] parameters) {
67                  return 0;
68              }
69  
70              @Override
71              public <T extends CalculusFieldElement<T>> FieldVector3D<T> getThrustVector(final FieldSpacecraftState<T> s,
72                                                                                          final T[] parameters) {
73                  return null;
74              }
75  
76              @Override
77              public <T extends CalculusFieldElement<T>> T getFlowRate(final FieldSpacecraftState<T> s, final T[] parameters) {
78                  return null;
79              }
80  
81              @Override
82              public List<ParameterDriver> getParametersDrivers() {
83                  return Collections.emptyList();
84              }
85  
86              @Override
87              public Control3DVectorCostType getControl3DVectorCostType() {
88                  return Control3DVectorCostType.NONE;
89              }
90          };
91  
92          // When
93          final Vector3D returnedDirection = model.getDirection(stateMock);
94  
95          // Then
96          final Vector3D expectedDirection = Vector3D.ZERO;
97  
98          // Assert that returned direction is a zero vector
99          Assertions.assertEquals(expectedDirection, returnedDirection);
100     }
101 
102     @Test
103     @DisplayName("Test getDirection method / issue 1046 with non-zero thrust")
104     void testIssue1046NonZeroThrust() {
105         // Given
106         final SpacecraftState stateMock = Mockito.mock(SpacecraftState.class);
107 
108         final Vector3D thrustVectorToReturn = new Vector3D(1, 2, 3);
109 
110         final ThrustPropulsionModel model = new ThrustPropulsionModel() {
111             @Override
112             public Vector3D getThrustVector(final SpacecraftState s) {
113                 return thrustVectorToReturn;
114             }
115 
116             @Override
117             public double getFlowRate(final SpacecraftState s) {
118                 return 0;
119             }
120 
121             @Override
122             public Vector3D getThrustVector(final SpacecraftState s, final double[] parameters) {
123                 return null;
124             }
125 
126             @Override
127             public double getFlowRate(final SpacecraftState s, final double[] parameters) {
128                 return 0;
129             }
130 
131             @Override
132             public <T extends CalculusFieldElement<T>> FieldVector3D<T> getThrustVector(final FieldSpacecraftState<T> s,
133                                                                                         final T[] parameters) {
134                 return null;
135             }
136 
137             @Override
138             public <T extends CalculusFieldElement<T>> T getFlowRate(final FieldSpacecraftState<T> s, final T[] parameters) {
139                 return null;
140             }
141 
142             @Override
143             public List<ParameterDriver> getParametersDrivers() {
144                 return Collections.emptyList();
145             }
146 
147             @Override
148             public Control3DVectorCostType getControl3DVectorCostType() {
149                 return Control3DVectorCostType.NONE;
150             }
151         };
152 
153         // When
154         final Vector3D returnedDirection = model.getDirection(stateMock);
155 
156         // Then
157         final Vector3D expectedDirection = thrustVectorToReturn.normalize();
158 
159         // Assert that returned direction is a zero vector
160         Assertions.assertEquals(expectedDirection, returnedDirection);
161     }
162 
163     @Test
164     @SuppressWarnings("unchecked")
165     void testIssue1551() {
166         // GIVEN
167         final ThrustPropulsionModel mockedModel = new ThrustPropulsionModel() {
168             @Override
169             public Vector3D getThrustVector(final SpacecraftState s) {
170                 return Vector3D.PLUS_I;
171             }
172 
173             @Override
174             public double getFlowRate(final SpacecraftState s) {
175                 return 0;
176             }
177 
178             @Override
179             public Vector3D getThrustVector(final SpacecraftState s, final double[] parameters) {
180                 return null;
181             }
182 
183             @Override
184             public double getFlowRate(final SpacecraftState s, final double[] parameters) {
185                 return 0;
186             }
187 
188             @Override
189             public <T extends CalculusFieldElement<T>> FieldVector3D<T> getThrustVector(final FieldSpacecraftState<T> s,
190                                                                                         final T[] parameters) {
191                 return FieldVector3D.getPlusI(s.getDate().getField());
192             }
193 
194             @Override
195             public <T extends CalculusFieldElement<T>> T getFlowRate(final FieldSpacecraftState<T> s, final T[] parameters) {
196                 return null;
197             }
198 
199             @Override
200             public List<ParameterDriver> getParametersDrivers() {
201                 return Collections.emptyList();
202             }
203 
204             @Override
205             public Control3DVectorCostType getControl3DVectorCostType() {
206                 return Control3DVectorCostType.NONE;
207             }
208         };
209         final FieldSpacecraftState<UnivariateDerivative1> mockedState = Mockito.mock(FieldSpacecraftState.class);
210         Mockito.when(mockedState.getMass()).thenReturn(UnivariateDerivative1.PI);
211         final UnivariateDerivative1Field field = UnivariateDerivative1Field.getInstance();
212         Mockito.when(mockedState.getDate()).thenReturn(FieldAbsoluteDate.getArbitraryEpoch(field));
213         final FieldAttitude<UnivariateDerivative1> mockedAttitude = Mockito.mock(FieldAttitude.class);
214         Mockito.when(mockedAttitude.getRotation()).thenReturn(FieldRotation.getIdentity(field));
215         final UnivariateDerivative1[] parameters = new UnivariateDerivative1[0];
216          // WHEN
217         final FieldVector3D<UnivariateDerivative1> actualVector = mockedModel.getAcceleration(mockedState, mockedAttitude, parameters);
218         // THEN
219         Assertions.assertEquals(mockedState.getMass().reciprocal().getReal(), actualVector.getNorm().getReal());
220     }
221 
222     @Test
223     void testGetExhaustVelocity() {
224         // GIVEN
225         final double isp = 2.;
226         // WHEN
227         final double exhaustVelocity = ThrustPropulsionModel.getExhaustVelocity(isp);
228         // THEN
229         Assertions.assertEquals(Constants.G0_STANDARD_GRAVITY * isp, exhaustVelocity);
230     }
231 }