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.propagation.numerical.cr3bp;
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.analysis.differentiation.FDSFactory;
26  import org.hipparchus.analysis.differentiation.FieldDerivativeStructure;
27  import org.hipparchus.geometry.euclidean.threed.FieldVector3D;
28  import org.hipparchus.geometry.euclidean.threed.Vector3D;
29  import org.hipparchus.util.FastMath;
30  import org.orekit.bodies.CR3BPSystem;
31  import org.orekit.forces.ForceModel;
32  import org.orekit.propagation.FieldSpacecraftState;
33  import org.orekit.propagation.SpacecraftState;
34  import org.orekit.utils.ParameterDriver;
35  
36  /** Class calculating the acceleration induced by CR3BP model.
37   * @see "Dynamical systems, the three-body problem, and space mission design, Koon, Lo, Marsden, Ross"
38   * @author Vincent Mouraux
39   * @since 10.2
40   */
41  public class CR3BPForceModel implements ForceModel {
42  
43      /** Suffix for parameter name for Mass Ratio enabling Jacobian processing. */
44      public static final String MASS_RATIO_SUFFIX =  "CR3BP System Mass Ratio";
45  
46      /**
47       * Central attraction scaling factor.
48       * <p>
49       * We use a power of 2 to avoid numeric noise introduction in the
50       * multiplications/divisions sequences.
51       * </p>
52       */
53      private static final double MU_SCALE = FastMath.scalb(1.0, 32);
54  
55      /** Driver for gravitational parameter. */
56      private final ParameterDriver muParameterDriver;
57  
58      /** Simple constructor.
59       * @param cr3bp Name of the CR3BP System
60       */
61      public CR3BPForceModel(final CR3BPSystem cr3bp) {
62          muParameterDriver = new ParameterDriver(cr3bp.getName() + MASS_RATIO_SUFFIX,
63                                                  cr3bp.getMassRatio(), MU_SCALE, 0.0,
64                                                  Double.POSITIVE_INFINITY);
65      }
66  
67      /** {@inheritDoc} */
68      public Vector3D acceleration(final SpacecraftState s,
69                                   final double[] parameters) {
70  
71          // Spacecraft Velocity
72          final double vx = s.getPVCoordinates().getVelocity().getX();
73          final double vy = s.getPVCoordinates().getVelocity().getY();
74  
75          // Spacecraft Potential
76          final DerivativeStructure potential = getPotential(s);
77  
78          // Potential derivatives
79          final double[] dU = potential.getAllDerivatives();
80  
81          // first order derivatives index
82          final int idX = potential.getFactory().getCompiler().getPartialDerivativeIndex(1, 0, 0);
83          final int idY = potential.getFactory().getCompiler().getPartialDerivativeIndex(0, 1, 0);
84          final int idZ = potential.getFactory().getCompiler().getPartialDerivativeIndex(0, 0, 1);
85  
86          // Acceleration calculation according to CR3BP Analytical Model
87          final double accx = dU[idX] + 2.0 * vy;
88          final double accy = dU[idY] - 2.0 * vx;
89          final double accz = dU[idZ];
90  
91          // compute absolute acceleration
92          return new Vector3D(accx, accy, accz);
93  
94      }
95  
96      /** {@inheritDoc} */
97      public <T extends CalculusFieldElement<T>> FieldVector3D<T> acceleration(final FieldSpacecraftState<T> s,
98                                                                           final T[] parameters) {
99  
100         // Spacecraft Velocity
101         final T vx = s.getPVCoordinates().getVelocity().getX();
102         final T vy = s.getPVCoordinates().getVelocity().getY();
103 
104         // Spacecraft Potential
105         final FieldDerivativeStructure<T> fieldPotential = getPotential(s);
106         // Potential derivatives
107         final T[] dU = fieldPotential.getAllDerivatives();
108 
109         // first order derivatives index
110         final int idX = fieldPotential.getFactory().getCompiler().getPartialDerivativeIndex(1, 0, 0);
111         final int idY = fieldPotential.getFactory().getCompiler().getPartialDerivativeIndex(0, 1, 0);
112         final int idZ = fieldPotential.getFactory().getCompiler().getPartialDerivativeIndex(0, 0, 1);
113 
114         // Acceleration calculation according to CR3BP Analytical Model
115         final T accx = dU[idX].add(vy.multiply(2.0));
116         final T accy = dU[idY].subtract(vx.multiply(2.0));
117         final T accz = dU[idZ];
118 
119         // compute absolute acceleration
120         return new FieldVector3D<>(accx, accy, accz);
121 
122     }
123 
124     /**
125      * Calculate spacecraft potential.
126      * @param s SpacecraftState
127      * @return Spacecraft Potential
128      */
129     public DerivativeStructure getPotential(final SpacecraftState s) {
130 
131         // Spacecraft Position
132         final double x = s.getPosition().getX();
133         final double y = s.getPosition().getY();
134         final double z = s.getPosition().getZ();
135 
136         final DSFactory factoryP = new DSFactory(3, 2);
137         final DerivativeStructure fpx = factoryP.variable(0, x);
138         final DerivativeStructure fpy = factoryP.variable(1, y);
139         final DerivativeStructure fpz = factoryP.variable(2, z);
140 
141         final DerivativeStructure zero = fpx.getField().getZero();
142 
143         // Get CR3BP System mass ratio
144         // By construction, mudriver has 1 value for the all time period that is why
145         // the getValue can be called with any date argument or null argument
146         final DerivativeStructure mu = zero.newInstance(muParameterDriver.getValue(s.getDate()));
147 
148         // Normalized distances between primaries and barycenter in CR3BP
149         final DerivativeStructure d1 = mu;
150         final DerivativeStructure d2 = mu.negate().add(1.0);
151 
152         // Norm of the Spacecraft position relative to the primary body
153         final DerivativeStructure r1 =
154             FastMath.sqrt((fpx.add(d1)).multiply(fpx.add(d1)).add(fpy.square())
155                 .add(fpz.square()));
156 
157         // Norm of the Spacecraft position relative to the secondary body
158         final DerivativeStructure r2 =
159             FastMath.sqrt((fpx.subtract(d2)).multiply(fpx.subtract(d2))
160                 .add(fpy.square()).add(fpz.square()));
161 
162         // Potential of the Spacecraft
163         return (mu.negate().add(1.0).divide(r1)).add(mu.divide(r2))
164                 .add(fpx.square().add(fpy.square()).multiply(0.5)).add(d1.multiply(d2).multiply(0.5));
165     }
166 
167     /**
168      * Calculate spacecraft potential.
169      * @param <T> Field element
170      * @param s SpacecraftState
171      * @return Spacecraft Potential
172      */
173     public <T extends CalculusFieldElement<T>> FieldDerivativeStructure<T> getPotential(final FieldSpacecraftState<T> s) {
174 
175         // Spacecraft Position
176         final T x = s.getPosition().getX();
177         final T y = s.getPosition().getY();
178         final T z = s.getPosition().getZ();
179 
180         final FDSFactory<T> factoryP = new FDSFactory<>(s.getDate().getField(), 3, 2);
181         final FieldDerivativeStructure<T> fpx = factoryP.variable(0, x);
182         final FieldDerivativeStructure<T> fpy = factoryP.variable(1, y);
183         final FieldDerivativeStructure<T> fpz = factoryP.variable(2, z);
184         final FieldDerivativeStructure<T> zero = fpx.getField().getZero();
185 
186         // Get CR3BP System mass ratio
187         // By construction, mudriver has 1 value for the all time period that is why
188         // the getValue can be called with any date argument or null argument
189         final FieldDerivativeStructure<T> mu = zero.newInstance(muParameterDriver.getValue(s.getDate().toAbsoluteDate()));
190 
191         // Normalized distances between primaries and barycenter in CR3BP
192         final FieldDerivativeStructure<T> d1 = mu;
193         final FieldDerivativeStructure<T> d2 = mu.negate().add(1.0);
194 
195         // Norm of the Spacecraft position relative to the primary body
196         final FieldDerivativeStructure<T> r1 =
197             FastMath.sqrt((fpx.add(d1)).multiply(fpx.add(d1)).add(fpy.square())
198                 .add(fpz.square()));
199 
200         // Norm of the Spacecraft position relative to the secondary body
201         final FieldDerivativeStructure<T> r2 =
202             FastMath.sqrt((fpx.subtract(d2)).multiply(fpx.subtract(d2))
203                 .add(fpy.square()).add(fpz.square()));
204 
205         // Potential of the Spacecraft
206         return (mu.negate().add(1.0).divide(r1)).add(mu.divide(r2))
207                 .add(fpx.square().add(fpy.square()).multiply(0.5)).add(d1.multiply(d2).multiply(0.5));
208     }
209 
210     /** {@inheritDoc} */
211     public List<ParameterDriver> getParametersDrivers() {
212         return Collections.singletonList(muParameterDriver);
213     }
214 
215     /** {@inheritDoc} */
216     public boolean dependsOnPositionOnly() {
217         return true;
218     }
219 }