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.gravity;
18  
19  import java.util.List;
20  
21  import org.hipparchus.util.FastMath;
22  import org.orekit.data.BodiesElements;
23  import org.orekit.data.FundamentalNutationArguments;
24  import org.orekit.forces.gravity.potential.NormalizedSphericalHarmonicsProvider;
25  import org.orekit.forces.gravity.potential.OceanTidesWave;
26  import org.orekit.forces.gravity.potential.TideSystem;
27  import org.orekit.time.AbsoluteDate;
28  import org.orekit.time.TimeVectorFunction;
29  
30  /** Gravity field corresponding to ocean tides.
31   * <p>
32   * This ocean tides force model implementation corresponds to the method described
33   * in <a href="http://www.iers.org/nn_11216/IERS/EN/Publications/TechnicalNotes/tn36.html">
34   * IERS conventions (2010)</a>, chapter 6, section 6.3.
35   * </p>
36   * <p>
37   * Note that this class is <em>not</em> thread-safe, and that tides computation
38   * are computer intensive if repeated. So this class is really expected to
39   * be wrapped within a {@link
40   * org.orekit.forces.gravity.potential.CachedNormalizedSphericalHarmonicsProvider}
41   * that will both ensure thread safety and improve performances using caching.
42   * </p>
43   * @see OceanTides
44   * @author Luc Maisonobe
45   * @since 6.1
46   */
47  class OceanTidesField implements NormalizedSphericalHarmonicsProvider {
48  
49      /** Maximum degree. */
50      private final int degree;
51  
52      /** Maximum order. */
53      private final int order;
54  
55      /** Central body reference radius. */
56      private final double ae;
57  
58      /** Central body attraction coefficient. */
59      private final double mu;
60  
61      /** Tides model. */
62      private final List<OceanTidesWave> waves;
63  
64      /** Object computing the fundamental arguments. */
65      private final FundamentalNutationArguments arguments;
66  
67      /** Function computing pole tide terms (ΔC₂₁, ΔS₂₁). */
68      private final TimeVectorFunction poleTideFunction;
69  
70      /** Simple constructor.
71       * @param ae central body reference radius
72       * @param mu central body attraction coefficient
73       * @param waves ocean tides waves
74       * @param arguments object computing the fundamental arguments
75       * @param poleTideFunction function computing ocean pole tide terms (ΔC₂₁, ΔS₂₁), may be null
76       */
77      OceanTidesField(final double ae, final double mu,
78                             final List<OceanTidesWave> waves,
79                             final FundamentalNutationArguments arguments,
80                             final TimeVectorFunction poleTideFunction) {
81  
82          // store mode parameters
83          this.ae  = ae;
84          this.mu  = mu;
85  
86          // waves
87          this.waves = waves;
88          int m = 0;
89          int n = 0;
90          for (final OceanTidesWave wave : waves) {
91              m = FastMath.max(m, wave.getMaxDegree());
92              n = FastMath.max(n, wave.getMaxOrder());
93          }
94          degree = m;
95          order  = n;
96  
97          this.arguments = arguments;
98  
99          // pole tide
100         this.poleTideFunction = poleTideFunction;
101 
102     }
103 
104     /** {@inheritDoc} */
105     @Override
106     public int getMaxDegree() {
107         return degree;
108     }
109 
110     /** {@inheritDoc} */
111     @Override
112     public int getMaxOrder() {
113         return order;
114     }
115 
116     /** {@inheritDoc} */
117     @Override
118     public double getMu() {
119         return mu;
120     }
121 
122     /** {@inheritDoc} */
123     @Override
124     public double getAe() {
125         return ae;
126     }
127 
128     /** {@inheritDoc} */
129     @Override
130     public AbsoluteDate getReferenceDate() {
131         return AbsoluteDate.ARBITRARY_EPOCH;
132     }
133 
134     /** {@inheritDoc} */
135     @Override
136     public TideSystem getTideSystem() {
137         // not really used here, but for consistency we can state that either
138         // we add the permanent tide or it was already in the central attraction
139         return TideSystem.ZERO_TIDE;
140     }
141 
142     /** {@inheritDoc} */
143     @Override
144     public NormalizedSphericalHarmonics onDate(final AbsoluteDate date) {
145 
146         // computed Cnm and Snm coefficients
147         final int        rows = degree + 1;
148         final double[][] cnm  = new double[rows][];
149         final double[][] snm  = new double[rows][];
150         for (int i = 0; i <= degree; ++i) {
151             final int m = FastMath.min(i, order) + 1;
152             cnm[i] = new double[m];
153             snm[i] = new double[m];
154         }
155 
156         final BodiesElements bodiesElements = arguments.evaluateAll(date);
157         for (final OceanTidesWave wave : waves) {
158             wave.addContribution(bodiesElements, cnm, snm);
159         }
160 
161         if (poleTideFunction != null && degree > 1 && order > 0) {
162             // add pole tide
163             poleTide(date, cnm, snm);
164         }
165 
166         return new TideHarmonics(date, cnm, snm);
167 
168     }
169 
170     /** Update coefficients applying pole tide.
171      * @param date current date
172      * @param cnm the Cnm coefficients. Modified in place.
173      * @param snm the Snm coefficients. Modified in place.
174      */
175     private void poleTide(final AbsoluteDate date, final double[][] cnm, final double[][] snm) {
176         final double[] deltaCS = poleTideFunction.value(date);
177         cnm[2][1] += deltaCS[0]; // ΔC₂₁
178         snm[2][1] += deltaCS[1]; // ΔS₂₁
179     }
180 
181     /** The Tidal geopotential evaluated on a specific date. */
182     private static class TideHarmonics implements NormalizedSphericalHarmonics {
183 
184         /** evaluation date. */
185         private final AbsoluteDate date;
186 
187         /** Cached cnm. */
188         private final double[][] cnm;
189 
190         /** Cached snm. */
191         private final double[][] snm;
192 
193         /** Construct the tidal harmonics on the given date.
194          *
195          * @param date of evaluation
196          * @param cnm the Cnm coefficients. Not copied.
197          * @param snm the Snm coeffiecients. Not copied.
198          */
199         private TideHarmonics(final AbsoluteDate date,
200                               final double[][] cnm,
201                               final double[][] snm) {
202             this.date = date;
203             this.cnm = cnm;
204             this.snm = snm;
205         }
206 
207         /** {@inheritDoc} */
208         @Override
209         public AbsoluteDate getDate() {
210             return date;
211         }
212 
213         /** {@inheritDoc} */
214         @Override
215         public double getNormalizedCnm(final int n, final int m) {
216             return cnm[n][m];
217         }
218 
219         /** {@inheritDoc} */
220         @Override
221         public double getNormalizedSnm(final int n, final int m) {
222             return snm[n][m];
223         }
224 
225     }
226 
227 }