1   /* Copyright 2002-2026 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.gravity;
18  
19  import org.hipparchus.CalculusFieldElement;
20  import org.hipparchus.util.MathArrays;
21  import org.orekit.bodies.CelestialBody;
22  import org.orekit.forces.ForceModel;
23  import org.orekit.forces.ForceModelModifier;
24  import org.orekit.forces.gravity.potential.CachedNormalizedSphericalHarmonicsProvider;
25  import org.orekit.forces.gravity.potential.NormalizedSphericalHarmonicsProvider;
26  import org.orekit.forces.gravity.potential.TideSystem;
27  import org.orekit.frames.Frame;
28  import org.orekit.time.AbsoluteDate;
29  import org.orekit.time.FieldAbsoluteDate;
30  import org.orekit.time.TimeVectorFunction;
31  import org.orekit.time.UT1Scale;
32  import org.orekit.utils.Constants;
33  import org.orekit.utils.IERSConventions;
34  import org.orekit.utils.LoveNumbers;
35  import org.orekit.utils.OrekitConfiguration;
36  
37  /** Solid tides force model.
38   * @since 6.1
39   * @author Luc Maisonobe
40   * @author Rafael Ayala
41   */
42  public class SolidTides implements ForceModelModifier {
43  
44      /**
45       * Default step for tides field sampling (seconds).
46       */
47      public static final double DEFAULT_STEP = 600.0;
48  
49      /**
50       * Default number of points tides field sampling.
51       */
52      public static final int DEFAULT_POINTS = 12;
53  
54      /**
55       * Zero frequency-dependent corrections function for bodies without
56       * frequency-dependent tidal data.
57       *
58       * @since 13.1.7
59       */
60      private static final TimeVectorFunction ZERO_FREQUENCY_FUNCTION = new TimeVectorFunction() {
61          @Override
62          public double[] value(final AbsoluteDate date) {
63              return new double[5];
64          }
65          @Override
66          public <T extends CalculusFieldElement<T>> T[] value(final FieldAbsoluteDate<T> date) {
67              return MathArrays.buildArray(date.getField(), 5);
68          }
69      };
70  
71      /**
72       * Underlying attraction model.
73       */
74      private final ForceModel attractionModel;
75  
76      /**
77       * Private constructor with the force model only.
78       * @param attractionModel underlying attraction model
79       * @since 13.1.7
80       */
81      private SolidTides(final ForceModel attractionModel) {
82          this.attractionModel = attractionModel;
83      }
84  
85      /**
86       * Simple constructor.
87       * <p>
88       * This constructor uses pole tides, the default {@link #DEFAULT_STEP step} and default
89       * {@link #DEFAULT_POINTS number of points} for the tides field interpolation.
90       * </p>
91       *
92       * @param centralBodyFrame  rotating body frame
93       * @param ae                central body reference radius
94       * @param mu                central body attraction coefficient
95       * @param centralTideSystem tide system used in the central attraction model
96       * @param conventions       IERS conventions used for loading Love numbers
97       * @param ut1               UT1 time scale
98       * @param bodies            tide generating bodies (typically Sun and Moon)
99       * @see #DEFAULT_STEP
100      * @see #DEFAULT_POINTS
101      * @see #SolidTides(Frame, double, double, TideSystem, boolean, double, int, IERSConventions, UT1Scale, CelestialBody...)
102      */
103     public SolidTides(final Frame centralBodyFrame, final double ae, final double mu,
104                       final TideSystem centralTideSystem,
105                       final IERSConventions conventions, final UT1Scale ut1,
106                       final CelestialBody... bodies) {
107         this(centralBodyFrame, ae, mu, centralTideSystem, true,
108                 DEFAULT_STEP, DEFAULT_POINTS, conventions, ut1, bodies);
109     }
110 
111     /**
112      * Simple constructor.
113      *
114      * @param centralBodyFrame  rotating body frame
115      * @param ae                central body reference radius
116      * @param mu                central body attraction coefficient
117      * @param centralTideSystem tide system used in the central attraction model
118      * @param poleTide          if true, pole tide is computed
119      * @param step              time step between sample points for interpolation
120      * @param nbPoints          number of points to use for interpolation, if less than 2
121      *                          then no interpolation is performed (thus greatly increasing computation cost)
122      * @param conventions       IERS conventions used for loading Love numbers
123      * @param ut1               UT1 time scale
124      * @param bodies            tide generating bodies (typically Sun and Moon)
125      */
126     public SolidTides(final Frame centralBodyFrame, final double ae, final double mu,
127                       final TideSystem centralTideSystem, final boolean poleTide,
128                       final double step, final int nbPoints,
129                       final IERSConventions conventions, final UT1Scale ut1,
130                       final CelestialBody... bodies) {
131         this(buildAttractionModel(centralBodyFrame,
132                 new SolidTidesField(conventions.getLoveNumbers(),
133                         conventions.getTideFrequencyDependenceFunction(ut1, ut1.getEOPHistory().getTimeScales()),
134                         conventions.getPermanentTide(),
135                         poleTide ? conventions.getSolidPoleTide(ut1.getEOPHistory()) : null,
136                         centralBodyFrame, ae, mu, centralTideSystem, bodies),
137                 step, nbPoints));
138     }
139 
140     /**
141      * Constructor with custom Love numbers for any central body.
142      * This constructor allows using body-specific Love numbers (e.g. for the Moon)
143      * instead of IERS Earth conventions. Note that frequency-dependent corrections and pole
144      * tide are not applied, and only the frequency-independent tidal deformation
145      * (IERS 2010 equations 6.6 and 6.7) is computed.
146      *
147      * @param centralBodyFrame  rotating body frame
148      * @param ae                central body reference radius
149      * @param mu                central body attraction coefficient
150      * @param centralTideSystem tide system used in the central attraction model
151      * @param loveNumbers       body-specific Love numbers
152      * @param step              time step between sample points for interpolation
153      * @param nbPoints          number of points to use for interpolation, if less than 2
154      *                          then no interpolation is performed (thus greatly increasing computation cost)
155      * @param bodies            tide generating bodies (typically Sun and Moon)
156      * @since 13.1.7
157      */
158     public SolidTides(final Frame centralBodyFrame, final double ae, final double mu,
159                       final TideSystem centralTideSystem,
160                       final LoveNumbers loveNumbers,
161                       final double step, final int nbPoints,
162                       final CelestialBody... bodies) {
163         this(buildAttractionModel(centralBodyFrame,
164                 new SolidTidesField(loveNumbers, ZERO_FREQUENCY_FUNCTION,
165                         0.0, null,
166                         centralBodyFrame, ae, mu, centralTideSystem, bodies),
167                 step, nbPoints));
168     }
169 
170     /**
171      * Constructor with custom Love numbers using default interpolation settings.
172      * <p>
173      * This constructor uses the default {@link #DEFAULT_STEP step} and default
174      * {@link #DEFAULT_POINTS number of points} for the tides field interpolation.
175      * </p>
176      *
177      * @param centralBodyFrame  rotating body frame
178      * @param ae                central body reference radius
179      * @param mu                central body attraction coefficient
180      * @param centralTideSystem tide system used in the central attraction model
181      * @param loveNumbers       body-specific Love numbers
182      * @param bodies            tide generating bodies (typically Sun and Moon)
183      * @see #DEFAULT_STEP
184      * @see #DEFAULT_POINTS
185      * @see #SolidTides(Frame, double, double, TideSystem, LoveNumbers, double, int, CelestialBody...)
186      * @since 13.1.7
187      */
188     public SolidTides(final Frame centralBodyFrame, final double ae, final double mu,
189                       final TideSystem centralTideSystem,
190                       final LoveNumbers loveNumbers,
191                       final CelestialBody... bodies) {
192         this(centralBodyFrame, ae, mu, centralTideSystem, loveNumbers,
193                 DEFAULT_STEP, DEFAULT_POINTS, bodies);
194     }
195 
196     /** Build the attraction model from a raw provider.
197      * @param centralBodyFrame rotating body frame
198      * @param rawProvider      raw spherical harmonics provider
199      * @param step             time step between sample points for interpolation
200      * @param nbPoints         number of points to use for interpolation
201      * @return the attraction model
202      */
203     private static ForceModel buildAttractionModel(final Frame centralBodyFrame,
204                                                    final NormalizedSphericalHarmonicsProvider rawProvider,
205                                                    final double step, final int nbPoints) {
206         final NormalizedSphericalHarmonicsProvider provider;
207         if (nbPoints < 2) {
208             provider = rawProvider;
209         } else {
210             provider =
211                     new CachedNormalizedSphericalHarmonicsProvider(rawProvider, step, nbPoints,
212                             OrekitConfiguration.getCacheSlotsNumber(),
213                             7 * Constants.JULIAN_DAY,
214                             0.5 * Constants.JULIAN_DAY);
215         }
216         return new HolmesFeatherstoneAttractionModel(centralBodyFrame, provider);
217     }
218 
219     @Override
220     public ForceModel getUnderlyingModel() {
221         return attractionModel;
222     }
223 }