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