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  
18  package org.orekit.forces.maneuvers.propulsion;
19  
20  import org.hipparchus.CalculusFieldElement;
21  import org.hipparchus.geometry.euclidean.threed.FieldVector3D;
22  import org.hipparchus.geometry.euclidean.threed.Vector3D;
23  import org.hipparchus.util.Precision;
24  import org.orekit.attitudes.Attitude;
25  import org.orekit.attitudes.FieldAttitude;
26  import org.orekit.propagation.FieldSpacecraftState;
27  import org.orekit.propagation.SpacecraftState;
28  import org.orekit.utils.Constants;
29  
30  /** Interface for a thrust-based propulsion model.
31   * @author Maxime Journot
32   * @since 10.2
33   */
34  public interface ThrustPropulsionModel extends PropulsionModel {
35  
36      /**
37       * Method computing the effective exhaust velocity from the specific impulse.
38       * @param isp specific impulse (s)
39       * @return effective exhaust velocity (m/s)
40       * @since 13.0
41       */
42      static double getExhaustVelocity(final double isp) {
43          return Constants.G0_STANDARD_GRAVITY * isp;
44      }
45  
46      /** Get the specific impulse (s).
47       * @param s current spacecraft state
48       * @return specific impulse (s).
49       */
50      default double getIsp(SpacecraftState s) {
51          final double flowRate = getFlowRate(s);
52          return -getControl3DVectorCostType().evaluate(getThrustVector(s)) / (Constants.G0_STANDARD_GRAVITY * flowRate);
53      }
54  
55      /** Get the thrust direction in spacecraft frame.
56       * <p>
57       * Return a zero vector if there is no thrust for given spacecraft state.
58       * @param s current spacecraft state
59       * @return thrust direction in spacecraft frame
60       */
61      default Vector3D getDirection(SpacecraftState s) {
62          final Vector3D thrustVector = getThrustVector(s);
63          final double   norm         = thrustVector.getNorm();
64          if (norm <= Precision.EPSILON) {
65              return Vector3D.ZERO;
66          }
67          return thrustVector.scalarMultiply(1. / norm);
68      }
69  
70      /** Get the thrust vector in spacecraft frame (N).
71       * @param s current spacecraft state
72       * @return thrust vector in spacecraft frame (N)
73       */
74      Vector3D getThrustVector(SpacecraftState s);
75  
76      /** Get the flow rate (kg/s).
77       * @param s current spacecraft state
78       * @return flow rate (kg/s)
79       */
80      double getFlowRate(SpacecraftState s);
81  
82      /** Get the thrust vector in spacecraft frame (N).
83       * @param s current spacecraft state
84       * @param parameters propulsion model parameters
85       * @return thrust vector in spacecraft frame (N)
86       */
87      Vector3D getThrustVector(SpacecraftState s, double[] parameters);
88  
89      /** Get the flow rate (kg/s).
90       * @param s current spacecraft state
91       * @param parameters propulsion model parameters
92       * @return flow rate (kg/s)
93       */
94      double getFlowRate(SpacecraftState s, double[] parameters);
95  
96      /** Get the thrust vector in spacecraft frame (N).
97       * @param s current spacecraft state
98       * @param parameters propulsion model parameters
99       * @param <T> extends CalculusFieldElement&lt;T&gt;
100      * @return thrust vector in spacecraft frame (N)
101      */
102     <T extends CalculusFieldElement<T>> FieldVector3D<T> getThrustVector(FieldSpacecraftState<T> s, T[] parameters);
103 
104     /** Get the flow rate (kg/s).
105      * @param s current spacecraft state
106      * @param parameters propulsion model parameters
107      * @param <T> extends CalculusFieldElement&lt;T&gt;
108      * @return flow rate (kg/s)
109      */
110     <T extends CalculusFieldElement<T>> T getFlowRate(FieldSpacecraftState<T> s, T[] parameters);
111 
112     /** {@inheritDoc}
113      * Acceleration is computed here using the thrust vector in S/C frame.
114      */
115     @Override
116     default Vector3D getAcceleration(final SpacecraftState s, final Attitude maneuverAttitude,
117                                      final double[] parameters) {
118 
119         final Vector3D thrustVector = getThrustVector(s, parameters);
120         final double thrustNorm = thrustVector.getNorm();
121         if (thrustNorm == 0) {
122             return Vector3D.ZERO;
123         }
124         final Vector3D direction = thrustVector.normalize();
125 
126         // Compute thrust acceleration in inertial frame
127         // It seems under-efficient to rotate direction and apply thrust
128         // instead of just rotating the whole thrust vector itself.
129         // However it has to be done that way to avoid numerical discrepancies with legacy tests.
130         return new Vector3D(thrustNorm / s.getMass(),
131                             maneuverAttitude.getRotation().applyInverseTo(direction));
132     }
133 
134     /** {@inheritDoc}
135      * Acceleration is computed here using the thrust vector in S/C frame.
136      */
137     @Override
138     default <T extends CalculusFieldElement<T>> FieldVector3D<T> getAcceleration(final FieldSpacecraftState<T> s,
139                                                                                  final FieldAttitude<T> maneuverAttitude,
140                                                                                  final T[] parameters) {
141         // Extract thrust & direction from thrust vector
142         final FieldVector3D<T> thrustVector = getThrustVector(s, parameters);
143         final T thrustNorm = thrustVector.getNorm();
144         if (thrustNorm.getReal() == 0) {
145             return maneuverAttitude.getRotation().applyInverseTo(thrustVector.scalarMultiply(s.getMass().reciprocal()));
146         }
147         final FieldVector3D<T> direction = thrustVector.normalize();
148 
149         // Compute thrust acceleration in inertial frame
150         // It seems under-efficient to rotate direction and apply thrust
151         // instead of just rotating the whole thrust vector itself.
152         // However it has to be done that way to avoid numerical discrepancies with legacy tests.
153         return new FieldVector3D<>(thrustNorm.divide(s.getMass()),
154                         maneuverAttitude.getRotation().applyInverseTo(direction));
155     }
156 
157     /** {@inheritDoc}
158      * Mass derivatives are directly extracted here from the flow rate value.
159      */
160     @Override
161     default double getMassDerivatives(SpacecraftState s, double[] parameters) {
162         return getFlowRate(s, parameters);
163     }
164 
165     /** {@inheritDoc}
166      * Mass derivatives are directly extracted here from the flow rate value.
167      */
168     @Override
169     default <T extends CalculusFieldElement<T>> T getMassDerivatives(FieldSpacecraftState<T> s, T[] parameters) {
170         return getFlowRate(s, parameters);
171     }
172 
173 }