AbstractGlobalPressureTemperature.java

  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. import java.io.BufferedInputStream;
  19. import java.io.IOException;
  20. import java.io.InputStream;

  21. import org.hipparchus.CalculusFieldElement;
  22. import org.orekit.bodies.FieldGeodeticPoint;
  23. import org.orekit.bodies.GeodeticPoint;
  24. import org.orekit.data.DataSource;
  25. import org.orekit.models.earth.troposphere.AzimuthalGradientCoefficients;
  26. import org.orekit.models.earth.troposphere.AzimuthalGradientProvider;
  27. import org.orekit.models.earth.troposphere.FieldAzimuthalGradientCoefficients;
  28. import org.orekit.models.earth.troposphere.FieldViennaACoefficients;
  29. import org.orekit.models.earth.troposphere.TroposphericModelUtils;
  30. import org.orekit.models.earth.troposphere.ViennaACoefficients;
  31. import org.orekit.models.earth.troposphere.ViennaAProvider;
  32. import org.orekit.time.AbsoluteDate;
  33. import org.orekit.time.FieldAbsoluteDate;

  34. /** Base class for Global Pressure and Temperature 2, 2w and 3 models.
  35.  * These models are empirical models that provide the temperature, the pressure and the water vapor pressure
  36.  * of a site depending its latitude and  longitude. These models also {@link ViennaACoefficients provide}
  37.  * the a<sub>h</sub> and a<sub>w</sub> coefficients for Vienna models.
  38.  * <p>
  39.  * The requisite coefficients for the computation of the weather parameters are provided by the
  40.  * Department of Geodesy and Geoinformation of the Vienna University. They are based on an
  41.  * external grid file like "gpt2_1.grd" (1° x 1°), "gpt2_5.grd" (5° x 5°), "gpt2_1w.grd" (1° x 1°),
  42.  * "gpt2_5w.grd" (5° x 5°), "gpt3_1.grd" (1° x 1°), or "gpt3_5.grd" (5° x 5°) available at:
  43.  * <a href="https://vmf.geo.tuwien.ac.at/codes/"> link</a>
  44.  * </p>
  45.  * <p>
  46.  * A bilinear interpolation is performed in order to obtained the correct values of the weather parameters.
  47.  * </p>
  48.  * <p>
  49.  * The format is always the same, with and example shown below for the pressure and the temperature.
  50.  * The "GPT2w" model (w stands for wet) also provide humidity parameters and the "GPT3" model also
  51.  * provides horizontal gradient, so the number of columns vary depending on the model.
  52.  * <p>
  53.  * Example:
  54.  * </p>
  55.  * <pre>
  56.  * %  lat    lon   p:a0    A1   B1   A2   B2  T:a0    A1   B1   A2   B2
  57.  *   87.5    2.5 101421    21  409 -217 -122 259.2 -13.2 -6.1  2.6  0.3
  58.  *   87.5    7.5 101416    21  411 -213 -120 259.3 -13.1 -6.1  2.6  0.3
  59.  *   87.5   12.5 101411    22  413 -209 -118 259.3 -13.1 -6.1  2.6  0.3
  60.  *   87.5   17.5 101407    23  415 -205 -116 259.4 -13.0 -6.1  2.6  0.3
  61.  *   ...
  62.  * </pre>
  63.  *
  64.  * @see "K. Lagler, M. Schindelegger, J. Böhm, H. Krasna, T. Nilsson (2013),
  65.  * GPT2: empirical slant delay model for radio space geodetic techniques. Geophys
  66.  * Res Lett 40(6):1069–1073. doi:10.1002/grl.50288"
  67.  *
  68.  * @author Bryan Cazabonne
  69.  * @author Luc Maisonobe
  70.  * @since 12.1
  71.  */
  72. public abstract class AbstractGlobalPressureTemperature
  73.     implements ViennaAProvider, AzimuthalGradientProvider, PressureTemperatureHumidityProvider {

  74.     /** Loaded grid. */
  75.     private final Grid grid;

  76.     /**
  77.      * Constructor with source of GPTn auxiliary data given by user.
  78.      *
  79.      * @param source grid data source
  80.      * @param expected expected seasonal models types
  81.      * @exception IOException if grid data cannot be read
  82.      */
  83.     protected AbstractGlobalPressureTemperature(final DataSource source, final SeasonalModelType... expected)
  84.         throws IOException {

  85.         // load the grid data
  86.         try (InputStream         is     = source.getOpener().openStreamOnce();
  87.              BufferedInputStream bis    = new BufferedInputStream(is)) {
  88.             final GptNParser     parser = new GptNParser(expected);
  89.             parser.loadData(bis, source.getName());
  90.             grid = parser.getGrid();
  91.         }

  92.     }

  93.     /** Get duration since reference date.
  94.      * @param date date
  95.      * @return duration since reference date
  96.      * @since 13.0
  97.      */
  98.     protected abstract double deltaRef(AbsoluteDate date);

  99.     /** Get duration since reference date.
  100.      * @param <T> type of the field elements
  101.      * @param date date
  102.      * @return duration since reference date
  103.      * @since 13.0
  104.      */
  105.     protected abstract <T extends CalculusFieldElement<T>> T deltaRef(FieldAbsoluteDate<T> date);

  106.     /** {@inheritDoc} */
  107.     @Override
  108.     public ViennaACoefficients getA(final GeodeticPoint location, final AbsoluteDate date) {

  109.         // set up interpolation parameters
  110.         final CellInterpolator interpolator =
  111.             grid.getInterpolator(location.getLatitude(), location.getLongitude(), location.getAltitude(),
  112.                                  deltaRef(date));

  113.         // ah and aw coefficients
  114.         return new ViennaACoefficients(interpolator.interpolate(e -> e.getEvaluatedModel(SeasonalModelType.AH)) * 0.001,
  115.                                        interpolator.interpolate(e -> e.getEvaluatedModel(SeasonalModelType.AW)) * 0.001);

  116.     }

  117.     /** {@inheritDoc} */
  118.     @Override
  119.     public PressureTemperatureHumidity getWeatherParameters(final GeodeticPoint location, final AbsoluteDate date) {

  120.         // set up interpolation parameters
  121.         final CellInterpolator interpolator =
  122.             grid.getInterpolator(location.getLatitude(), location.getLongitude(), location.getAltitude(),
  123.                                  deltaRef(date));

  124.         // Temperature [K]
  125.         final double temperature = interpolator.interpolate(EvaluatedGridEntry::getTemperature);

  126.         // Pressure [hPa]
  127.         final double pressure = interpolator.interpolate(EvaluatedGridEntry::getPressure);

  128.         // water vapor decrease factor
  129.         final double lambda = grid.hasModels(SeasonalModelType.LAMBDA) ?
  130.                               interpolator.interpolate(e -> e.getEvaluatedModel(SeasonalModelType.LAMBDA)) :
  131.                               Double.NaN;

  132.         // Water vapor pressure [hPa]
  133.         final double el;
  134.         if (Double.isNaN(lambda)) {
  135.             // GPT2
  136.             final double qv = interpolator.interpolate(e -> e.getEvaluatedModel(SeasonalModelType.QV)) * 0.001;
  137.             el = qv * pressure / (0.622 + 0.378 * qv);
  138.         } else {
  139.             // GPT2w and GPT3
  140.             el = interpolator.interpolate(EvaluatedGridEntry::getWaterVaporPressure);
  141.         }

  142.         // mean temperature weighted with water vapor pressure
  143.         final double tm = grid.hasModels(SeasonalModelType.TM) ?
  144.                           interpolator.interpolate(e -> e.getEvaluatedModel(SeasonalModelType.TM)) :
  145.                           Double.NaN;

  146.         return new PressureTemperatureHumidity(location.getAltitude(),
  147.                                                TroposphericModelUtils.HECTO_PASCAL.toSI(pressure),
  148.                                                temperature,
  149.                                                TroposphericModelUtils.HECTO_PASCAL.toSI(el),
  150.                                                tm,
  151.                                                lambda);

  152.     }

  153.     /** {@inheritDoc} */
  154.     @Override
  155.     public AzimuthalGradientCoefficients getGradientCoefficients(final GeodeticPoint location, final AbsoluteDate date) {

  156.         if (grid.hasModels(SeasonalModelType.GN_H, SeasonalModelType.GE_H, SeasonalModelType.GN_W, SeasonalModelType.GE_W)) {
  157.             // set up interpolation parameters
  158.             final CellInterpolator interpolator = grid.getInterpolator(location.getLatitude(), location.getLongitude(),
  159.                                                                        location.getAltitude(), deltaRef(date));

  160.             return new AzimuthalGradientCoefficients(interpolator.interpolate(e -> e.getEvaluatedModel(SeasonalModelType.GN_H) * 0.00001),
  161.                                                      interpolator.interpolate(e -> e.getEvaluatedModel(SeasonalModelType.GE_H) * 0.00001),
  162.                                                      interpolator.interpolate(e -> e.getEvaluatedModel(SeasonalModelType.GN_W) * 0.00001),
  163.                                                      interpolator.interpolate(e -> e.getEvaluatedModel(SeasonalModelType.GE_W) * 0.00001));
  164.         } else {
  165.             return null;
  166.         }

  167.     }

  168.     /** {@inheritDoc} */
  169.     @Override
  170.     public <T extends CalculusFieldElement<T>> FieldViennaACoefficients<T> getA(final FieldGeodeticPoint<T> location,
  171.                                                                                 final FieldAbsoluteDate<T> date) {

  172.         // set up interpolation parameters
  173.         final FieldCellInterpolator<T> interpolator =
  174.             grid.getInterpolator(location.getLatitude(), location.getLongitude(), location.getAltitude(),
  175.                                  deltaRef(date));

  176.         // ah and aw coefficients
  177.         return new FieldViennaACoefficients<>(interpolator.fieldInterpolate(e -> e.getEvaluatedModel(SeasonalModelType.AH)).multiply(0.001),
  178.                                               interpolator.fieldInterpolate(e -> e.getEvaluatedModel(SeasonalModelType.AW)).multiply(0.001));

  179.     }

  180.     /** {@inheritDoc} */
  181.     @Override
  182.     public <T extends CalculusFieldElement<T>> FieldPressureTemperatureHumidity<T> getWeatherParameters(final FieldGeodeticPoint<T> location,
  183.                                                                                                         final FieldAbsoluteDate<T> date) {

  184.         // set up interpolation parameters
  185.         final FieldCellInterpolator<T> interpolator =
  186.             grid.getInterpolator(location.getLatitude(), location.getLongitude(), location.getAltitude(),
  187.                                  deltaRef(date));

  188.         // Temperature [K]
  189.         final T temperature = interpolator.fieldInterpolate(FieldEvaluatedGridEntry::getTemperature);

  190.         // Pressure [hPa]
  191.         final T pressure = interpolator.fieldInterpolate(FieldEvaluatedGridEntry::getPressure);

  192.         // water vapor decrease factor
  193.         final T lambda = grid.hasModels(SeasonalModelType.LAMBDA) ?
  194.                          interpolator.fieldInterpolate(e -> e.getEvaluatedModel(SeasonalModelType.LAMBDA)) :
  195.                          date.getField().getZero().newInstance(Double.NaN);

  196.         // Water vapor pressure [hPa]
  197.         final T el;
  198.         if (lambda.isNaN()) {
  199.             // GPT2
  200.             final T qv = interpolator.fieldInterpolate(e -> e.getEvaluatedModel(SeasonalModelType.QV)).multiply(0.001);
  201.             el = qv.multiply(pressure).divide(qv.multiply(0.378).add(0.622));
  202.         } else {
  203.             // GPT3
  204.             el = interpolator.fieldInterpolate(FieldEvaluatedGridEntry::getWaterVaporPressure);
  205.         }

  206.         // mean temperature weighted with water vapor pressure
  207.         final T tm = grid.hasModels(SeasonalModelType.TM) ?
  208.                      interpolator.fieldInterpolate(e -> e.getEvaluatedModel(SeasonalModelType.TM)) :
  209.                      date.getField().getZero().newInstance(Double.NaN);

  210.         return new FieldPressureTemperatureHumidity<>(location.getAltitude(),
  211.                                                       TroposphericModelUtils.HECTO_PASCAL.toSI(pressure),
  212.                                                       temperature,
  213.                                                       TroposphericModelUtils.HECTO_PASCAL.toSI(el),
  214.                                                       tm,
  215.                                                       lambda);

  216.     }

  217.     /** {@inheritDoc} */
  218.     @Override
  219.     public <T extends CalculusFieldElement<T>> FieldAzimuthalGradientCoefficients<T> getGradientCoefficients(final FieldGeodeticPoint<T> location,
  220.                                                                                                              final FieldAbsoluteDate<T> date) {

  221.         if (grid.hasModels(SeasonalModelType.GN_H, SeasonalModelType.GE_H, SeasonalModelType.GN_W, SeasonalModelType.GE_W)) {
  222.             // set up interpolation parameters
  223.             final FieldCellInterpolator<T> interpolator =
  224.                 grid.getInterpolator(location.getLatitude(), location.getLongitude(), location.getAltitude(),
  225.                                      deltaRef(date));

  226.             return new FieldAzimuthalGradientCoefficients<>(interpolator.fieldInterpolate(e -> e.getEvaluatedModel(SeasonalModelType.GN_H)).multiply(0.00001),
  227.                                                             interpolator.fieldInterpolate(e -> e.getEvaluatedModel(SeasonalModelType.GE_H)).multiply(0.00001),
  228.                                                             interpolator.fieldInterpolate(e -> e.getEvaluatedModel(SeasonalModelType.GN_W)).multiply(0.00001),
  229.                                                             interpolator.fieldInterpolate(e -> e.getEvaluatedModel(SeasonalModelType.GE_W)).multiply(0.00001));
  230.         } else {
  231.             return null;
  232.         }

  233.     }

  234. }