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.potential;
18  
19  import java.util.ArrayList;
20  import java.util.Arrays;
21  import java.util.HashMap;
22  import java.util.List;
23  import java.util.Map;
24  
25  import org.hipparchus.util.FastMath;
26  import org.orekit.data.DataLoader;
27  import org.orekit.errors.OrekitException;
28  import org.orekit.errors.OrekitMessages;
29  
30  /** Reader for ocean tides coefficients.
31   * @author Luc Maisonobe
32   * @see OceanTidesWave
33   * @since 6.1
34   */
35  public abstract class OceanTidesReader implements DataLoader {
36  
37      /** Regular expression for supported files names. */
38      private final String supportedNames;
39  
40      /** Maximal degree to parse. */
41      private int maxParseDegree;
42  
43      /** Maximal order to parse. */
44      private int maxParseOrder;
45  
46      /** Loaded waves. */
47      private List<OceanTidesWave> waves;
48  
49      /** Name name of the parsed file (or zip entry). */
50      private String name;
51  
52      /** Triangular arrays to hold all coefficients. */
53      private Map<Integer, double[][][]> coefficients;
54  
55      /** Max degree encountered up to now. */
56      private int maxDegree;
57  
58      /** Max order encountered up to now. */
59      private int maxOrder;
60  
61      /** Simple constructor.
62       * @param supportedNames regular expression for supported files names
63       */
64      protected OceanTidesReader(final String supportedNames) {
65          this.supportedNames = supportedNames;
66          this.maxParseDegree = Integer.MAX_VALUE;
67          this.maxParseOrder  = Integer.MAX_VALUE;
68          this.waves          = new ArrayList<>();
69      }
70  
71      /** Get the regular expression for supported files names.
72       * @return regular expression for supported files names
73       */
74      public String getSupportedNames() {
75          return supportedNames;
76      }
77  
78      /** Set the degree limit for the next file parsing.
79       * @param maxParseDegree maximal degree to parse (may be safely
80       * set to {@link Integer#MAX_VALUE} to parse all available coefficients)
81       */
82      public void setMaxParseDegree(final int maxParseDegree) {
83          this.maxParseDegree = maxParseDegree;
84      }
85  
86      /** Get the degree limit for the next file parsing.
87       * @return degree limit for the next file parsing
88       */
89      public int getMaxParseDegree() {
90          return maxParseDegree;
91      }
92  
93      /** Set the order limit for the next file parsing.
94       * @param maxParseOrder maximal order to parse (may be safely
95       * set to {@link Integer#MAX_VALUE} to parse all available coefficients)
96       */
97      public void setMaxParseOrder(final int maxParseOrder) {
98          this.maxParseOrder = maxParseOrder;
99      }
100 
101     /** Get the order limit for the next file parsing.
102      * @return order limit for the next file parsing
103      */
104     public int getMaxParseOrder() {
105         return maxParseOrder;
106     }
107 
108     /** {@inheritDoc} */
109     @Override
110     public boolean stillAcceptsData() {
111         return !(getMaxAvailableDegree() == getMaxParseDegree() && getMaxAvailableOrder() == getMaxParseOrder());
112     }
113 
114     /** Start parsing.
115      * <p>
116      * This method must be called by subclasses when they start parsing a file
117      * </p>
118      * @param fileName name of the file (or zip entry)
119      */
120     protected void startParse(final String fileName) {
121         this.waves        = new ArrayList<>();
122         this.name         = fileName;
123         this.coefficients = new HashMap<>();
124         this.maxDegree    = -1;
125         this.maxOrder     = -1;
126     }
127 
128     /** Check if coefficients can be added.
129      * @param n degree of the coefficients
130      * @param m order of the coefficients
131      * @return true if coefficients can be added
132      */
133     public boolean canAdd(final int n, final int m) {
134         maxDegree = FastMath.max(maxDegree, n);
135         maxOrder  = FastMath.max(maxOrder,  m);
136         return n <= getMaxParseDegree() && m <= getMaxParseOrder();
137     }
138 
139     /** Add parsed coefficients.
140      * @param doodson Doodson number of the current wave
141      * @param n degree of the coefficients
142      * @param m order of the coefficients
143      * @param cPlus  C+(n,m)
144      * @param sPlus  S+(n,m)
145      * @param cMinus C-(n,m)
146      * @param sMinus S-(n,m)
147      * @param lineNumber number of the parsed line
148      * @param line text of the line
149      */
150     protected void addWaveCoefficients(final int doodson, final int n, final int m,
151                                        final double cPlus, final double sPlus,
152                                        final double cMinus, final double sMinus,
153                                        final int lineNumber, final String line) {
154 
155         if (!coefficients.containsKey(doodson)) {
156             // prepare the triangular array to hold coefficients
157             final double[][][] array = new double[getMaxParseDegree() + 1][][];
158             for (int i = 0; i <= getMaxParseDegree(); ++i) {
159                 array[i] = new double[FastMath.min(i, getMaxParseOrder()) + 1][4];
160                 for (double[] a : array[i]) {
161                     Arrays.fill(a, Double.NaN);
162                 }
163             }
164             coefficients.put(doodson, array);
165         }
166 
167         // store the fields
168         final double[] cs = coefficients.get(doodson)[n][m];
169         cs[0] = cPlus;
170         cs[1] = sPlus;
171         cs[2] = cMinus;
172         cs[3] = sMinus;
173 
174     }
175 
176     /** End parsing.
177      * <p>
178      * This method must be called by subclasses when they end parsing a file
179      * </p>
180      */
181     protected void endParse() {
182 
183         // check requested degree and order
184         if (maxDegree < getMaxParseDegree() || maxOrder < getMaxParseOrder()) {
185             throw new OrekitException(OrekitMessages.OCEAN_TIDE_DATA_DEGREE_ORDER_LIMITS,
186                                       name, maxDegree, maxOrder);
187         }
188 
189         for (final Map.Entry<Integer, double[][][]> entry : coefficients.entrySet()) {
190 
191             // check wave degree and order
192             int waveDegree = -1;
193             int waveOrder  = -1;
194             for (int i = 0; i < entry.getValue().length; ++i) {
195                 for (int j = 0; j < entry.getValue()[i].length; ++j) {
196                     if (!Double.isNaN(entry.getValue()[i][j][0])) {
197                         waveDegree = FastMath.max(waveDegree, i);
198                         waveOrder  = FastMath.max(waveOrder,  j);
199                     }
200                 }
201             }
202 
203             // create wave
204             waves.add(new OceanTidesWave(entry.getKey(), waveDegree, waveOrder, entry.getValue()));
205 
206         }
207 
208     }
209 
210     /** Get the loaded waves.
211      * @return loaded waves
212      */
213     public List<OceanTidesWave> getWaves() {
214         return waves;
215     }
216 
217    /** Get the maximal degree available in the last file parsed.
218     * @return maximal degree available in the last file parsed
219     * @since 12.0.1
220     */
221     public int getMaxAvailableDegree() {
222         return waves.stream().map(OceanTidesWave::getMaxDegree).max(Integer::compareTo).orElse(-1);
223     }
224 
225     /** Get the maximal order available in the last file parsed.
226      * @return maximal order available in the last file parsed
227      * @since 12.0.1
228      */
229     public int getMaxAvailableOrder() {
230         return waves.stream().map(OceanTidesWave::getMaxOrder).max(Integer::compareTo).orElse(-1);
231     }
232 
233 }