1   /* Copyright 2002-2025 CS GROUP
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.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.DSFactory;
24  import org.hipparchus.analysis.differentiation.DerivativeStructure;
25  import org.hipparchus.geometry.euclidean.threed.FieldVector3D;
26  import org.hipparchus.geometry.euclidean.threed.Vector3D;
27  import org.hipparchus.util.FastMath;
28  import org.junit.jupiter.api.Assertions;
29  import org.junit.jupiter.api.Test;
30  import org.orekit.forces.maneuvers.Control3DVectorCostType;
31  import org.orekit.frames.FramesFactory;
32  import org.orekit.orbits.CircularOrbit;
33  import org.orekit.orbits.Orbit;
34  import org.orekit.propagation.FieldSpacecraftState;
35  import org.orekit.propagation.SpacecraftState;
36  import org.orekit.time.AbsoluteDate;
37  import org.orekit.utils.Constants;
38  import org.orekit.utils.PVCoordinates;
39  import org.orekit.utils.ParameterDriver;
40  
41  /** Test for AbstractConstantThrustPropulsionModel class and its sub-classes. */
42  public class AbstractConstantThrustPropulsionModelTest {
43  
44      /** Test non-abstract methods of constant thrust model. */
45      @Test
46      void testNonAbstractMethods() {
47  
48          final double thrust = 1.;
49          final double isp = 300.;
50          final Vector3D direction = Vector3D.PLUS_I;
51          final String name = "man";
52  
53          final Vector3D thrustVector = direction.scalarMultiply(thrust);
54          final Control3DVectorCostType control3DVectorCostType = Control3DVectorCostType.INF_NORM;
55          final double flowRate = -control3DVectorCostType.evaluate(thrustVector) / (Constants.G0_STANDARD_GRAVITY * isp);
56  
57          // A simple model without any parameter driver
58          final AbstractConstantThrustPropulsionModel model =
59                          new AbstractConstantThrustPropulsionModel(thrust, isp, direction, control3DVectorCostType, name) {
60  
61              @Override
62              public <T extends CalculusFieldElement<T>> FieldVector3D<T> getThrustVector(T[] parameters) {
63                  return new FieldVector3D<T>(parameters[0].getField(), thrustVector);
64              }
65  
66              @Override
67              public Vector3D getThrustVector(double[] parameters) {
68                  return thrustVector;
69              }
70  
71              @Override
72              public Vector3D getThrustVector(AbsoluteDate date) {
73                  return thrustVector;
74              }
75  
76              @Override
77              public <T extends CalculusFieldElement<T>> T getFlowRate(T[] parameters) {
78                  return parameters[0].getField().getZero().add(flowRate);
79              }
80  
81              @Override
82              public double getFlowRate(double[] parameters) {
83                  return flowRate;
84              }
85  
86              @Override
87              public double getFlowRate(AbsoluteDate date) {
88                  return flowRate;
89              }
90  
91  			@Override
92  			public Vector3D getThrustVector() {
93  				return thrustVector;
94  			}
95  
96  			@Override
97  			public double getFlowRate() {
98  				return flowRate;
99  			}
100 
101             @Override
102             public List<ParameterDriver> getParametersDrivers() {
103                 return Collections.emptyList();
104             }
105         };
106 
107         // Test non-abstract methods
108         Assertions.assertEquals(0, model.getParametersDrivers().size());
109         Assertions.assertEquals(name, model.getName());
110         Assertions.assertEquals(isp, model.getIsp(), 0.);
111         Assertions.assertArrayEquals(direction.toArray(), model.getDirection().toArray(), 0.);
112         Assertions.assertEquals(thrust, model.getThrustMagnitude(), 0.);
113 
114         // Dummy spacecraft state
115         Orbit orbit =  new CircularOrbit(new PVCoordinates(Vector3D.PLUS_I, Vector3D.PLUS_J),
116                                          FramesFactory.getEME2000(), AbsoluteDate.J2000_EPOCH, Constants.EIGEN5C_EARTH_MU);
117         SpacecraftState s = new SpacecraftState(orbit);
118 
119         Assertions.assertArrayEquals(thrustVector.toArray(), model.getThrustVector(s).toArray(), 0.);
120         Assertions.assertArrayEquals(thrustVector.toArray(), model.getThrustVector(s, new double[] {1.}).toArray(), 0.);
121         Assertions.assertEquals(flowRate, model.getFlowRate(s), 0.);
122         Assertions.assertEquals(flowRate, model.getFlowRate(s, new double[] {0.}), 0.);
123 
124         // Dummy DS factory
125         DSFactory factory = new DSFactory(1, 1);
126         DerivativeStructure ds = factory.build(1., 1.);
127         DerivativeStructure[] dsArray = new DerivativeStructure[] {ds};
128         FieldSpacecraftState<DerivativeStructure> fs = new FieldSpacecraftState<>(ds.getField(), s);
129 
130         // Thrust DS
131         Assertions.assertEquals(thrustVector.getX(), model.getThrustVector(fs, dsArray).getX().getReal(), 0.);
132         Assertions.assertEquals(0., model.getThrustVector(fs, dsArray).getX().getPartialDerivative(1), 0.);
133         Assertions.assertEquals(thrustVector.getY(), model.getThrustVector(fs, dsArray).getY().getReal(), 0.);
134         Assertions.assertEquals(0., model.getThrustVector(fs, dsArray).getY().getPartialDerivative(1), 0.);
135         Assertions.assertEquals(thrustVector.getZ(), model.getThrustVector(fs, dsArray).getZ().getReal(), 0.);
136         Assertions.assertEquals(0., model.getThrustVector(fs, dsArray).getZ().getPartialDerivative(1), 0.);
137 
138         // Flow rate DS
139         Assertions.assertEquals(flowRate, model.getFlowRate(fs, dsArray).getReal(), 0.);
140         Assertions.assertEquals(0., model.getFlowRate(fs, dsArray).getPartialDerivative(1), 0.);
141     }
142 
143     /** Test the 1-dimensional constant thrust model. */
144     @Test
145     void testConstantThrust1D() {
146 
147         final double thrust = 1.;
148         final double isp = 300.;
149         final Vector3D direction = new Vector3D(FastMath.PI / 3., FastMath.PI / 4);
150         final String name = "man-1";
151 
152         // 1D constant thrust model
153         final Control3DVectorCostType control3DVectorCostType = Control3DVectorCostType.ONE_NORM;
154         final BasicConstantThrustPropulsionModel model =
155                         new BasicConstantThrustPropulsionModel(thrust, isp, direction, control3DVectorCostType, name);
156         List<ParameterDriver> drivers = model.getParametersDrivers();
157 
158         // References
159         final Vector3D refThrustVector = direction.scalarMultiply(thrust);
160         final double refFlowRate = -control3DVectorCostType.evaluate(refThrustVector) / (Constants.G0_STANDARD_GRAVITY * isp);
161 
162 
163         // Thrust & flow rate
164         final double mult = 10.;
165         Assertions.assertArrayEquals(direction.toArray(), model.getDirection().toArray(), 0.);
166         Assertions.assertArrayEquals(refThrustVector.toArray(), model.getThrustVector().toArray(), 0.);
167         Assertions.assertArrayEquals(refThrustVector.scalarMultiply(mult).toArray(),
168                 model.getThrustVector(new double[] {mult * thrust, mult * refFlowRate}).toArray(), 0.);
169         Assertions.assertEquals(refFlowRate, model.getFlowRate(), 0.);
170         Assertions.assertEquals(mult * refFlowRate, model.getFlowRate(new double[] {mult * thrust, mult * refFlowRate}),
171                 0.);
172 
173         // Drivers
174         Assertions.assertEquals(2, drivers.size());
175         Assertions.assertEquals(name + BasicConstantThrustPropulsionModel.THRUST, drivers.get(0).getName());
176         Assertions.assertEquals(name + BasicConstantThrustPropulsionModel.FLOW_RATE, drivers.get(1).getName());
177         Assertions.assertEquals(thrust, drivers.get(0).getValue(), 0.);
178         Assertions.assertEquals(refFlowRate, drivers.get(1).getValue(), 0.);
179 
180         // Thrust DS
181         final DSFactory factory = new DSFactory(2, 1);
182         DerivativeStructure t = factory.build(mult * thrust, 1., 0.);
183         DerivativeStructure f = factory.build(mult * refFlowRate, 0., 1.);
184 
185         DerivativeStructure[] dsArray = new DerivativeStructure[] {t, f};
186 
187         Assertions.assertEquals(t.getReal() * direction.getX(), model.getThrustVector(dsArray).getX().getReal(), 0.);
188         Assertions.assertEquals(direction.getX(), model.getThrustVector(dsArray).getX().getPartialDerivative(1, 0), 0.);
189         Assertions.assertEquals(0., model.getThrustVector(dsArray).getX().getPartialDerivative(0, 1), 0.);
190 
191         Assertions.assertEquals(t.getReal() * direction.getY(), model.getThrustVector(dsArray).getY().getReal(), 0.);
192         Assertions.assertEquals(direction.getY(), model.getThrustVector(dsArray).getY().getPartialDerivative(1, 0), 0.);
193         Assertions.assertEquals(0., model.getThrustVector(dsArray).getY().getPartialDerivative(0, 1), 0.);
194 
195         Assertions.assertEquals(t.getReal() * direction.getZ(), model.getThrustVector(dsArray).getZ().getReal(), 0.);
196         Assertions.assertEquals(direction.getZ(), model.getThrustVector(dsArray).getZ().getPartialDerivative(1, 0), 0.);
197         Assertions.assertEquals(0., model.getThrustVector(dsArray).getZ().getPartialDerivative(0, 1), 0.);
198 
199         // Flow rate DS
200         Assertions.assertEquals(f.getReal(), model.getFlowRate(dsArray).getReal(), 0.);
201         Assertions.assertEquals(0., model.getFlowRate(dsArray).getPartialDerivative(1, 0), 0.);
202         Assertions.assertEquals(1., model.getFlowRate(dsArray).getPartialDerivative(0, 1), 0.);
203     }
204 
205   /** Test the 3-dimensional "scaled" constant thrust model. */
206   @Test
207   void testConstantThrust3DScaled() {
208 
209       final double thrust = 1.;
210       final double isp = 300.;
211       final Vector3D direction = new Vector3D(FastMath.PI / 3., FastMath.PI / 4);
212       final String name = "man-1";
213 
214       // 3D "scaled" constant thrust model
215       final ScaledConstantThrustPropulsionModel mod1 =
216                       new ScaledConstantThrustPropulsionModel(thrust, isp, direction, name);
217       List<ParameterDriver> drivers = mod1.getParametersDrivers();
218 
219       // References
220       final Vector3D refThrustVector = direction.scalarMultiply(thrust);
221       final Control3DVectorCostType control3DVectorCostType = Control3DVectorCostType.TWO_NORM;
222       final double refFlowRate = -control3DVectorCostType.evaluate(refThrustVector) / (Constants.G0_STANDARD_GRAVITY * isp);
223 
224 
225       // Thrust & flow rate
226       Assertions.assertArrayEquals(direction.toArray(), mod1.getDirection().toArray(), 0.);
227       Assertions.assertArrayEquals(refThrustVector.toArray(), mod1.getThrustVector().toArray(), 0.);
228       Assertions.assertArrayEquals(refThrustVector.toArray(), mod1.getThrustVector(new double[] {1., 1., 1.}).toArray(),
229               0.);
230       Assertions.assertEquals(refFlowRate, mod1.getFlowRate(), 0.);
231       Assertions.assertEquals(refFlowRate, mod1.getFlowRate(new double[] {0.}), 0.);
232 
233       // Drivers
234       Assertions.assertEquals(3, drivers.size());
235       Assertions.assertEquals(name + ScaledConstantThrustPropulsionModel.THRUSTX_SCALE_FACTOR,
236               drivers.get(0).getName());
237       Assertions.assertEquals(name + ScaledConstantThrustPropulsionModel.THRUSTY_SCALE_FACTOR,
238               drivers.get(1).getName());
239       Assertions.assertEquals(name + ScaledConstantThrustPropulsionModel.THRUSTZ_SCALE_FACTOR,
240               drivers.get(2).getName());
241       Assertions.assertEquals(1., drivers.get(0).getValue(), 0.);
242       Assertions.assertEquals(1., drivers.get(1).getValue(), 0.);
243       Assertions.assertEquals(1., drivers.get(2).getValue(), 0.);
244 
245       // Thrust DS
246       final DSFactory factory = new DSFactory(3, 1);
247       DerivativeStructure fx = factory.build(1., 1., 0., 0.);
248       DerivativeStructure fy = factory.build(0.5, 0., 1., 0.);
249       DerivativeStructure fz = factory.build(1.5, 0., 0., 1.);
250 
251       DerivativeStructure[] fArray = new DerivativeStructure[] {fx, fy, fz};
252 
253       Assertions.assertEquals(fx.getReal() * refThrustVector.getX(), mod1.getThrustVector(fArray).getX().getReal(), 0.);
254       Assertions.assertEquals(refThrustVector.getX(), mod1.getThrustVector(fArray).getX().getPartialDerivative(1, 0, 0),
255               0.);
256       Assertions.assertEquals(0., mod1.getThrustVector(fArray).getX().getPartialDerivative(0, 1, 0), 0.);
257       Assertions.assertEquals(0., mod1.getThrustVector(fArray).getX().getPartialDerivative(0, 0, 1), 0.);
258 
259       Assertions.assertEquals(fy.getReal() * refThrustVector.getY(), mod1.getThrustVector(fArray).getY().getReal(), 0.);
260       Assertions.assertEquals(0., mod1.getThrustVector(fArray).getY().getPartialDerivative(1, 0, 0), 0.);
261       Assertions.assertEquals(refThrustVector.getY(), mod1.getThrustVector(fArray).getY().getPartialDerivative(0, 1, 0),
262               0.);
263       Assertions.assertEquals(0., mod1.getThrustVector(fArray).getY().getPartialDerivative(0, 0, 1), 0.);
264 
265       Assertions.assertEquals(fz.getReal() * refThrustVector.getZ(), mod1.getThrustVector(fArray).getZ().getReal(), 0.);
266       Assertions.assertEquals(0., mod1.getThrustVector(fArray).getZ().getPartialDerivative(1, 0, 0), 0.);
267       Assertions.assertEquals(0., mod1.getThrustVector(fArray).getZ().getPartialDerivative(0, 1, 0), 0.);
268       Assertions.assertEquals(refThrustVector.getZ(), mod1.getThrustVector(fArray).getZ().getPartialDerivative(0, 0, 1),
269               0.);
270 
271       // Flow rate DS
272       Assertions.assertEquals(refFlowRate, mod1.getFlowRate(fArray).getReal(), 0.);
273       Assertions.assertEquals(0., mod1.getFlowRate(fArray).getPartialDerivative(1, 0, 0), 0.);
274       Assertions.assertEquals(0., mod1.getFlowRate(fArray).getPartialDerivative(0, 1, 0), 0.);
275       Assertions.assertEquals(0., mod1.getFlowRate(fArray).getPartialDerivative(0, 1, 0), 0.);
276   }
277 }