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.models.earth.weather;
18  
19  import java.io.IOException;
20  import java.net.URISyntaxException;
21  import java.net.URL;
22  
23  import org.hipparchus.CalculusFieldElement;
24  import org.hipparchus.Field;
25  import org.hipparchus.util.Binary64Field;
26  import org.hipparchus.util.FastMath;
27  import org.junit.jupiter.api.Assertions;
28  import org.junit.jupiter.api.Test;
29  import org.orekit.Utils;
30  import org.orekit.bodies.FieldGeodeticPoint;
31  import org.orekit.bodies.GeodeticPoint;
32  import org.orekit.data.DataContext;
33  import org.orekit.data.DataSource;
34  import org.orekit.errors.OrekitException;
35  import org.orekit.errors.OrekitMessages;
36  import org.orekit.models.earth.troposphere.FieldViennaACoefficients;
37  import org.orekit.models.earth.troposphere.TroposphericModelUtils;
38  import org.orekit.models.earth.troposphere.ViennaACoefficients;
39  import org.orekit.time.AbsoluteDate;
40  import org.orekit.time.FieldAbsoluteDate;
41  import org.orekit.utils.Constants;
42  
43  public class GlobalPressureTemperature2Test {
44  
45      private static final double epsilon = 1.0e-12;
46  
47      @Test
48      public void testMatlabRef0() throws IOException, URISyntaxException {
49  
50          Utils.setDataRoot("regular-data");
51  
52          // reference output obtained by running TU-Wien https://vmf.geo.tuwien.ac.at/codes/gpt2.m matlab script
53          final double latitude  = FastMath.toRadians(48.20);
54          final double longitude = FastMath.toRadians(16.37);
55          final double height    = 156.0;
56          final AbsoluteDate date = AbsoluteDate.J2000_EPOCH.shiftedBy(4596.5 * Constants.JULIAN_DAY);
57          final URL url = GlobalPressureTemperature2Test.class.getClassLoader().getResource("gpt-grid/gpt2_5.grd");
58          final GlobalPressureTemperature2 model =
59                          new GlobalPressureTemperature2(new DataSource(url.toURI()),
60                                                         DataContext.getDefault().getTimeScales());
61  
62          final GeodeticPoint                 location = new GeodeticPoint(latitude, longitude, height);
63          final ViennaACoefficients           a        = model.getA(location, date);
64          final PressureTemperatureHumidity   pth      = model.getWeatherParameters(location, date);
65  
66          Assertions.assertEquals(0.001264669,           a.getAh(),                   1.0e-9);
67          Assertions.assertEquals(0.000572557,           a.getAw(),                   1.0e-9);
68          Assertions.assertEquals(273.15 + 22.121274184, pth.getTemperature(),        1.0e-9);
69          Assertions.assertEquals(1002.555031902,        TroposphericModelUtils.HECTO_PASCAL.fromSI(pth.getPressure()),           1.0e-9);
70          Assertions.assertEquals(15.625300653,          TroposphericModelUtils.HECTO_PASCAL.fromSI(pth.getWaterVaporPressure()), 1.0e-9);
71  
72      }
73  
74      @Test
75      public void testMatlabRef1() throws IOException, URISyntaxException {
76  
77          Utils.setDataRoot("regular-data");
78  
79          // reference output obtained by running TU-Wien https://vmf.geo.tuwien.ac.at/codes/gpt2.m matlab script
80          final double latitude  = FastMath.toRadians(77.5);
81          final double longitude = FastMath.toRadians(137.5);
82          final double height    = 0.0;
83          final AbsoluteDate date = AbsoluteDate.J2000_EPOCH.shiftedBy(7395 * Constants.JULIAN_DAY);
84          final URL url = GlobalPressureTemperature2Test.class.getClassLoader().getResource("gpt-grid/gpt2_5.grd");
85          final GlobalPressureTemperature2 model =
86                          new GlobalPressureTemperature2(new DataSource(url.toURI()),
87                                                         DataContext.getDefault().getTimeScales());
88  
89          final GeodeticPoint                 location = new GeodeticPoint(latitude, longitude, height);
90          final ViennaACoefficients           a        = model.getA(location, date);
91          final PressureTemperatureHumidity   pth      = model.getWeatherParameters(location, date);
92  
93          Assertions.assertEquals(0.001174547,           a.getAh(),                   1.0e-9);
94          Assertions.assertEquals(0.000579181,           a.getAw(),                   1.0e-9);
95          Assertions.assertEquals(273.15 - 19.053324536, pth.getTemperature(),        1.0e-9);
96          Assertions.assertEquals(1020.047182276,        TroposphericModelUtils.HECTO_PASCAL.fromSI(pth.getPressure()),           1.0e-9);
97          Assertions.assertEquals(1.189457053,           TroposphericModelUtils.HECTO_PASCAL.fromSI(pth.getWaterVaporPressure()), 1.0e-9);
98  
99      }
100 
101     @Test
102     public void testMatlabRef2() throws IOException, URISyntaxException {
103 
104         Utils.setDataRoot("regular-data");
105 
106         // reference output obtained by running TU-Wien https://vmf.geo.tuwien.ac.at/codes/gpt2.m matlab script
107         final double latitude  = FastMath.toRadians(80.0);
108         final double longitude = FastMath.toRadians(120.0);
109         final double height    = 100.0;
110         final AbsoluteDate date = AbsoluteDate.J2000_EPOCH.shiftedBy(7395 * Constants.JULIAN_DAY);
111         final URL url = GlobalPressureTemperature2Test.class.getClassLoader().getResource("gpt-grid/gpt2_5.grd");
112         final GlobalPressureTemperature2 model =
113                         new GlobalPressureTemperature2(new DataSource(url.toURI()),
114                                                        DataContext.getDefault().getTimeScales());
115 
116         final GeodeticPoint                 location = new GeodeticPoint(latitude, longitude, height);
117         final ViennaACoefficients           a        = model.getA(location, date);
118         final PressureTemperatureHumidity   pth      = model.getWeatherParameters(location, date);
119 
120         Assertions.assertEquals(0.001172082,           a.getAh(),                   1.0e-9);
121         Assertions.assertEquals(0.000582310,           a.getAw(),                   1.0e-9);
122         Assertions.assertEquals(273.15 - 20.146149484, pth.getTemperature(),        1.0e-9);
123         Assertions.assertEquals(1005.468561351,        TroposphericModelUtils.HECTO_PASCAL.fromSI(pth.getPressure()),           1.0e-9);
124         Assertions.assertEquals(1.002152409,           TroposphericModelUtils.HECTO_PASCAL.fromSI(pth.getWaterVaporPressure()), 1.0e-9);
125 
126     }
127 
128     @Test
129     public void testFieldMatlabRef0() throws IOException, URISyntaxException {
130         doTestFieldMatlabRef0(Binary64Field.getInstance());
131     }
132 
133     protected <T extends CalculusFieldElement<T>> void doTestFieldMatlabRef0(final Field<T> field)
134         throws IOException, URISyntaxException {
135 
136         Utils.setDataRoot("regular-data");
137 
138         // reference output obtained by running TU-Wien https://vmf.geo.tuwien.ac.at/codes/gpt2.m matlab script
139         final T latitude  = FastMath.toRadians(field.getZero().newInstance(48.20));
140         final T longitude = FastMath.toRadians(field.getZero().newInstance(16.37));
141         final T height    = field.getZero().newInstance(156.0);
142         final FieldAbsoluteDate<T> date = FieldAbsoluteDate.getJ2000Epoch(field).shiftedBy(4596.5 * Constants.JULIAN_DAY);
143         final URL url = GlobalPressureTemperature2Test.class.getClassLoader().getResource("gpt-grid/gpt2_5.grd");
144         final GlobalPressureTemperature2 model =
145                         new GlobalPressureTemperature2(new DataSource(url.toURI()),
146                                                        DataContext.getDefault().getTimeScales());
147 
148         final FieldGeodeticPoint<T>                 location = new FieldGeodeticPoint<>(latitude, longitude, height);
149         final FieldViennaACoefficients<T>           a        = model.getA(location, date);
150         final FieldPressureTemperatureHumidity<T>   pth      = model.getWeatherParameters(location, date);
151 
152         Assertions.assertEquals(0.001264669,           a.getAh().getReal(),                   1.0e-9);
153         Assertions.assertEquals(0.000572557,           a.getAw().getReal(),                   1.0e-9);
154         Assertions.assertEquals(273.15 + 22.121274184, pth.getTemperature().getReal(),        1.0e-9);
155         Assertions.assertEquals(1002.555031902,        TroposphericModelUtils.HECTO_PASCAL.fromSI(pth.getPressure()).getReal(),           1.0e-9);
156         Assertions.assertEquals(15.625300653,          TroposphericModelUtils.HECTO_PASCAL.fromSI(pth.getWaterVaporPressure()).getReal(), 1.0e-9);
157 
158     }
159 
160     @Test
161     public void testFieldMatlabRef1() throws IOException, URISyntaxException {
162         doTestFieldMatlabRef1(Binary64Field.getInstance());
163     }
164 
165     protected <T extends CalculusFieldElement<T>> void doTestFieldMatlabRef1(final Field<T> field)
166         throws IOException, URISyntaxException {
167 
168         Utils.setDataRoot("regular-data");
169 
170         // reference output obtained by running TU-Wien https://vmf.geo.tuwien.ac.at/codes/gpt2.m matlab script
171         final T latitude  = FastMath.toRadians(field.getZero().newInstance(77.5));
172         final T longitude = FastMath.toRadians(field.getZero().newInstance(137.5));
173         final T height    = field.getZero().newInstance(0.0);
174         final FieldAbsoluteDate<T> date = FieldAbsoluteDate.getJ2000Epoch(field).shiftedBy(7395 * Constants.JULIAN_DAY);
175         final URL url = GlobalPressureTemperature2Test.class.getClassLoader().getResource("gpt-grid/gpt2_5.grd");
176         final GlobalPressureTemperature2 model =
177                         new GlobalPressureTemperature2(new DataSource(url.toURI()),
178                                                        DataContext.getDefault().getTimeScales());
179 
180         final FieldGeodeticPoint<T>                 location = new FieldGeodeticPoint<>(latitude, longitude, height);
181         final FieldViennaACoefficients<T>           a        = model.getA(location, date);
182         final FieldPressureTemperatureHumidity<T>   pth      = model.getWeatherParameters(location, date);
183 
184         Assertions.assertEquals(0.001174547,           a.getAh().getReal(),                   1.0e-9);
185         Assertions.assertEquals(0.000579181,           a.getAw().getReal(),                   1.0e-9);
186         Assertions.assertEquals(273.15 - 19.053324536, pth.getTemperature().getReal(),        1.0e-9);
187         Assertions.assertEquals(1020.047182276,        TroposphericModelUtils.HECTO_PASCAL.fromSI(pth.getPressure()).getReal(),           1.0e-9);
188         Assertions.assertEquals(1.189457053,           TroposphericModelUtils.HECTO_PASCAL.fromSI(pth.getWaterVaporPressure()).getReal(), 1.0e-9);
189 
190     }
191 
192     @Test
193     public void testFieldMatlabRef2() throws IOException, URISyntaxException {
194         doTestFieldMatlabRef2(Binary64Field.getInstance());
195     }
196 
197     protected <T extends CalculusFieldElement<T>> void doTestFieldMatlabRef2(final Field<T> field)
198         throws IOException, URISyntaxException {
199 
200         Utils.setDataRoot("regular-data");
201 
202         // reference output obtained by running TU-Wien https://vmf.geo.tuwien.ac.at/codes/gpt2_5.m matlab script
203         final T latitude  = FastMath.toRadians(field.getZero().newInstance(80.0));
204         final T longitude = FastMath.toRadians(field.getZero().newInstance(120.0));
205         final T height    = field.getZero().newInstance(100.0);
206         final FieldAbsoluteDate<T> date = FieldAbsoluteDate.getJ2000Epoch(field).shiftedBy(7395 * Constants.JULIAN_DAY);
207         final URL url = GlobalPressureTemperature2Test.class.getClassLoader().getResource("gpt-grid/gpt2_5.grd");
208         final GlobalPressureTemperature2 model =
209                         new GlobalPressureTemperature2(new DataSource(url.toURI()),
210                                                        DataContext.getDefault().getTimeScales());
211 
212         final FieldGeodeticPoint<T>                 location = new FieldGeodeticPoint<>(latitude, longitude, height);
213         final FieldViennaACoefficients<T>           a        = model.getA(location, date);
214         final FieldPressureTemperatureHumidity<T>   pth      = model.getWeatherParameters(location, date);
215 
216         Assertions.assertEquals(0.001172082,           a.getAh().getReal(),                   1.0e-9);
217         Assertions.assertEquals(0.000582310,           a.getAw().getReal(),                   1.0e-9);
218         Assertions.assertEquals(273.15 - 20.146149484, pth.getTemperature().getReal(),        1.0e-9);
219         Assertions.assertEquals(1005.468561351,        TroposphericModelUtils.HECTO_PASCAL.fromSI(pth.getPressure()).getReal(),           1.0e-9);
220         Assertions.assertEquals(1.002152409,           TroposphericModelUtils.HECTO_PASCAL.fromSI(pth.getWaterVaporPressure()).getReal(), 1.0e-9);
221 
222     }
223 
224     @Test
225     public void testEquality() throws IOException, URISyntaxException {
226         doTestEquality("gpt-grid/gpt2_5.grd");
227     }
228 
229     @Test
230     public void testEqualityLoadingGpt2w() throws IOException, URISyntaxException {
231         doTestEquality("gpt-grid/gpt2_5w.grd");
232     }
233 
234     @Test
235     public void testEqualityLoadingGpt3() throws IOException, URISyntaxException {
236         doTestEquality("gpt-grid/gpt3_5.grd");
237     }
238 
239     private void doTestEquality(final String resourceName) throws IOException, URISyntaxException {
240 
241         Utils.setDataRoot("regular-data");
242 
243         // Commons parameters
244         final AbsoluteDate date = AbsoluteDate.J2000_EPOCH.shiftedBy(4596.5 * Constants.JULIAN_DAY);
245         final double latitude   = FastMath.toRadians(45.0);
246         final double height     = 0.0;
247 
248         final URL url = GlobalPressureTemperature2Test.class.getClassLoader().getResource(resourceName);
249         GlobalPressureTemperature2 model = new GlobalPressureTemperature2(new DataSource(url.toURI()),
250                                                                           DataContext.getDefault().getTimeScales());
251 
252         // Test longitude = 181° and longitude = -179°
253         GeodeticPoint               location1 = new GeodeticPoint(latitude, FastMath.toRadians(181.0), height);
254         ViennaACoefficients         a1        = model.getA(location1, date);
255         PressureTemperatureHumidity pth1      = model.getWeatherParameters(location1, date);
256         GeodeticPoint               location2 = new GeodeticPoint(latitude, FastMath.toRadians(-179.0), height);
257         ViennaACoefficients         a2        = model.getA(location2, date);
258         PressureTemperatureHumidity pth2      = model.getWeatherParameters(location2, date);
259 
260         Assertions.assertEquals(pth1.getTemperature(),        pth2.getTemperature(),        epsilon);
261         Assertions.assertEquals(pth1.getPressure(),           pth2.getPressure(),           epsilon);
262         Assertions.assertEquals(pth1.getWaterVaporPressure(), pth2.getWaterVaporPressure(), epsilon);
263         Assertions.assertEquals(a1.getAh(),                   a2.getAh(),                   epsilon);
264         Assertions.assertEquals(a1.getAw(),                   a2.getAw(),                   epsilon);
265 
266         // Test longitude = 180° and longitude = -180°
267         location1 = new GeodeticPoint(latitude, FastMath.toRadians(180.0), height);
268         a1        = model.getA(location1, date);
269         pth1      = model.getWeatherParameters(location1, date);
270         location2 = new GeodeticPoint(latitude, FastMath.toRadians(-180.0), height);
271         a2        = model.getA(location2, date);
272         pth2      = model.getWeatherParameters(location2, date);
273 
274         Assertions.assertEquals(pth1.getTemperature(),        pth2.getTemperature(),        epsilon);
275         Assertions.assertEquals(pth1.getPressure(),           pth2.getPressure(),           epsilon);
276         Assertions.assertEquals(pth1.getWaterVaporPressure(), pth2.getWaterVaporPressure(), epsilon);
277         Assertions.assertEquals(a1.getAh(),                   a2.getAh(),                   epsilon);
278         Assertions.assertEquals(a1.getAw(),                   a2.getAw(),                   epsilon);
279 
280         // Test longitude = 0° and longitude = 360°
281         location1 = new GeodeticPoint(latitude, FastMath.toRadians(0.0), height);
282         a1        = model.getA(location1, date);
283         pth1      = model.getWeatherParameters(location1, date);
284         location2 = new GeodeticPoint(latitude, FastMath.toRadians(360.0), height);
285         a2        = model.getA(location2, date);
286         pth2      = model.getWeatherParameters(location2, date);
287 
288         Assertions.assertEquals(pth1.getTemperature(),        pth2.getTemperature(),        epsilon);
289         Assertions.assertEquals(pth1.getPressure(),           pth2.getPressure(),           epsilon);
290         Assertions.assertEquals(pth1.getWaterVaporPressure(), pth2.getWaterVaporPressure(), epsilon);
291         Assertions.assertEquals(a1.getAh(),                   a2.getAh(),                   epsilon);
292         Assertions.assertEquals(a1.getAw(),                   a2.getAw(),                   epsilon);
293 
294     }
295 
296     @Test
297     public void testCorruptedFileBadData() throws IOException, URISyntaxException {
298 
299         Utils.setDataRoot("regular-data");
300 
301         final String fileName = "corrupted-bad-data-gpt3_15.grd";
302         final URL url = GlobalPressureTemperature2Test.class.getClassLoader().getResource("gpt-grid/" + fileName);
303         try {
304             new GlobalPressureTemperature2(new DataSource(url.toURI()),
305                                            DataContext.getDefault().getTimeScales());
306             Assertions.fail("An exception should have been thrown");
307         } catch (OrekitException oe) {
308             Assertions.assertEquals(OrekitMessages.UNABLE_TO_PARSE_LINE_IN_FILE, oe.getSpecifier());
309             Assertions.assertEquals(6, ((Integer) oe.getParts()[0]).intValue());
310             Assertions.assertTrue(((String) oe.getParts()[1]).endsWith(fileName));
311         }
312 
313     }
314 
315     @Test
316     public void testCorruptedIrregularGrid() throws IOException, URISyntaxException {
317 
318         Utils.setDataRoot("regular-data");
319 
320         final String fileName = "corrupted-irregular-grid-gpt3_15.grd";
321         final URL url = GlobalPressureTemperature2Test.class.getClassLoader().getResource("gpt-grid/" + fileName);
322         try {
323             new GlobalPressureTemperature2(new DataSource(url.toURI()),
324                                            DataContext.getDefault().getTimeScales());
325             Assertions.fail("An exception should have been thrown");
326         } catch (OrekitException oe) {
327             Assertions.assertEquals(OrekitMessages.IRREGULAR_OR_INCOMPLETE_GRID, oe.getSpecifier());
328             Assertions.assertTrue(((String) oe.getParts()[0]).endsWith(fileName));
329         }
330 
331     }
332 
333     @Test
334     public void testCorruptedIncompleteHeader() throws IOException, URISyntaxException {
335 
336         Utils.setDataRoot("regular-data");
337 
338         final String fileName = "corrupted-incomplete-header.grd";
339         final URL url = GlobalPressureTemperature2Test.class.getClassLoader().getResource("gpt-grid/" + fileName);
340         try {
341             new GlobalPressureTemperature2(new DataSource(url.toURI()),
342                                            DataContext.getDefault().getTimeScales());
343             Assertions.fail("An exception should have been thrown");
344         } catch (OrekitException oe) {
345             Assertions.assertEquals(OrekitMessages.UNABLE_TO_PARSE_LINE_IN_FILE, oe.getSpecifier());
346             Assertions.assertEquals(1, ((Integer) oe.getParts()[0]).intValue());
347             Assertions.assertTrue(((String) oe.getParts()[1]).endsWith(fileName));
348         }
349 
350     }
351 
352     @Test
353     public void testCorruptedMissingSeasonalColumns() throws IOException, URISyntaxException {
354 
355         Utils.setDataRoot("regular-data");
356 
357         final String fileName = "corrupted-missing-seasonal-columns-gpt3_15.grd";
358         final URL url = GlobalPressureTemperature2Test.class.getClassLoader().getResource("gpt-grid/" + fileName);
359         try {
360             new GlobalPressureTemperature2(new DataSource(url.toURI()),
361                                            DataContext.getDefault().getTimeScales());
362             Assertions.fail("An exception should have been thrown");
363         } catch (OrekitException oe) {
364             Assertions.assertEquals(OrekitMessages.UNABLE_TO_PARSE_LINE_IN_FILE, oe.getSpecifier());
365             Assertions.assertEquals(1, ((Integer) oe.getParts()[0]).intValue());
366             Assertions.assertTrue(((String) oe.getParts()[1]).endsWith(fileName));
367         }
368 
369     }
370 
371     @Test
372     public void testCorruptedMissingDataFields() throws IOException, URISyntaxException {
373 
374         Utils.setDataRoot("regular-data");
375 
376         final String fileName = "corrupted-missing-data-fields-gpt3_15.grd";
377         final URL url = GlobalPressureTemperature2Test.class.getClassLoader().getResource("gpt-grid/" + fileName);
378         try {
379             new GlobalPressureTemperature2(new DataSource(url.toURI()),
380                                            DataContext.getDefault().getTimeScales());
381             Assertions.fail("An exception should have been thrown");
382         } catch (OrekitException oe) {
383             Assertions.assertEquals(OrekitMessages.UNABLE_TO_PARSE_LINE_IN_FILE, oe.getSpecifier());
384             Assertions.assertEquals(4, ((Integer) oe.getParts()[0]).intValue());
385             Assertions.assertTrue(((String) oe.getParts()[1]).endsWith(fileName));
386         }
387 
388     }
389 
390 }