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