AbstractGlobalPressureTemperature.java

  1. /* Copyright 2002-2024 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.hipparchus.util.FastMath;
  23. import org.orekit.bodies.FieldGeodeticPoint;
  24. import org.orekit.bodies.GeodeticPoint;
  25. import org.orekit.data.DataSource;
  26. import org.orekit.models.earth.troposphere.AzimuthalGradientCoefficients;
  27. import org.orekit.models.earth.troposphere.AzimuthalGradientProvider;
  28. import org.orekit.models.earth.troposphere.FieldAzimuthalGradientCoefficients;
  29. import org.orekit.models.earth.troposphere.FieldViennaACoefficients;
  30. import org.orekit.models.earth.troposphere.TroposphericModelUtils;
  31. import org.orekit.models.earth.troposphere.ViennaACoefficients;
  32. import org.orekit.models.earth.troposphere.ViennaAProvider;
  33. import org.orekit.time.AbsoluteDate;
  34. import org.orekit.time.FieldAbsoluteDate;
  35. import org.orekit.time.TimeScale;
  36. import org.orekit.utils.Constants;

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

  77.     /** Standard gravity constant [m/s²]. */
  78.     private static final double G = Constants.G0_STANDARD_GRAVITY;

  79.     /** Ideal gas constant for dry air [J/kg/K]. */
  80.     private static final double R = 287.0;

  81.     /** Loaded grid. */
  82.     private final Grid grid;

  83.     /** UTC time scale. */
  84.     private final TimeScale utc;

  85.     /**
  86.      * Constructor with source of GPTn auxiliary data given by user.
  87.      *
  88.      * @param source grid data source
  89.      * @param utc UTC time scale.
  90.      * @param expected expected seasonal models types
  91.      * @exception IOException if grid data cannot be read
  92.      */
  93.     protected AbstractGlobalPressureTemperature(final DataSource source, final TimeScale utc,
  94.                                                 final SeasonalModelType... expected)
  95.         throws IOException {
  96.         this.utc = utc;

  97.         // load the grid data
  98.         try (InputStream         is     = source.getOpener().openStreamOnce();
  99.              BufferedInputStream bis    = new BufferedInputStream(is)) {
  100.             final GptNParser     parser = new GptNParser(expected);
  101.             parser.loadData(bis, source.getName());
  102.             grid = parser.getGrid();
  103.         }

  104.     }

  105.     /**
  106.      * Constructor with already loaded grid.
  107.      *
  108.      * @param grid loaded grid
  109.      * @param utc UTC time scale.
  110.      * @deprecated as of 12.1 used only by {@link GlobalPressureTemperature2Model}
  111.      */
  112.     @Deprecated
  113.     protected AbstractGlobalPressureTemperature(final Grid grid, final TimeScale utc) {
  114.         this.grid = grid;
  115.         this.utc  = utc;
  116.     }

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

  120.         // set up interpolation parameters
  121.         final CellInterpolator interpolator = grid.getInterpolator(location.getLatitude(), location.getLongitude());
  122.         final int dayOfYear = date.getComponents(utc).getDate().getDayOfYear();

  123.         // ah and aw coefficients
  124.         return new ViennaACoefficients(interpolator.interpolate(e -> e.getModel(SeasonalModelType.AH).evaluate(dayOfYear)) * 0.001,
  125.                                        interpolator.interpolate(e -> e.getModel(SeasonalModelType.AW).evaluate(dayOfYear)) * 0.001);

  126.     }

  127.     /** {@inheritDoc} */
  128.     @Override
  129.     public PressureTemperatureHumidity getWeatherParamerers(final GeodeticPoint location, final AbsoluteDate date) {

  130.         // set up interpolation parameters
  131.         final CellInterpolator interpolator = grid.getInterpolator(location.getLatitude(), location.getLongitude());
  132.         final int dayOfYear = date.getComponents(utc).getDate().getDayOfYear();

  133.         // Corrected height (can be negative)
  134.         final double undu            = interpolator.interpolate(e -> e.getUndulation());
  135.         final double correctedheight = location.getAltitude() - undu - interpolator.interpolate(e -> e.getHs());

  136.         // Temperature gradient [K/m]
  137.         final double dTdH = interpolator.interpolate(e -> e.getModel(SeasonalModelType.DT).evaluate(dayOfYear)) * 0.001;

  138.         // Specific humidity
  139.         final double qv = interpolator.interpolate(e -> e.getModel(SeasonalModelType.QV).evaluate(dayOfYear)) * 0.001;

  140.         // For the computation of the temperature and the pressure, we use
  141.         // the standard ICAO atmosphere formulas.

  142.         // Temperature [K]
  143.         final double t0 = interpolator.interpolate(e -> e.getModel(SeasonalModelType.TEMPERATURE).evaluate(dayOfYear));
  144.         final double temperature = t0 + dTdH * correctedheight;

  145.         // Pressure [hPa]
  146.         final double p0       = interpolator.interpolate(e -> e.getModel(SeasonalModelType.PRESSURE).evaluate(dayOfYear));
  147.         final double exponent = G / (dTdH * R);
  148.         final double pressure = p0 * FastMath.pow(1 - (dTdH / t0) * correctedheight, exponent) * 0.01;

  149.         // Water vapor pressure [hPa]
  150.         final double e0 = qv * pressure / (0.622 + 0.378 * qv);

  151.         // mean temperature weighted with water vapor pressure
  152.         final double tm = grid.hasModels(SeasonalModelType.TM) ?
  153.                           interpolator.interpolate(e -> e.getModel(SeasonalModelType.TM).evaluate(dayOfYear)) :
  154.                           Double.NaN;

  155.         // water vapor decrease factor
  156.         final double lambda = grid.hasModels(SeasonalModelType.LAMBDA) ?
  157.                               interpolator.interpolate(e -> e.getModel(SeasonalModelType.LAMBDA).evaluate(dayOfYear)) :
  158.                               Double.NaN;

  159.         return new PressureTemperatureHumidity(location.getAltitude(),
  160.                                                TroposphericModelUtils.HECTO_PASCAL.toSI(pressure),
  161.                                                temperature,
  162.                                                TroposphericModelUtils.HECTO_PASCAL.toSI(e0),
  163.                                                tm,
  164.                                                lambda);

  165.     }

  166.     /** {@inheritDoc} */
  167.     @Override
  168.     public AzimuthalGradientCoefficients getGradientCoefficients(final GeodeticPoint location, final AbsoluteDate date) {

  169.         if (grid.hasModels(SeasonalModelType.GN_H, SeasonalModelType.GE_H, SeasonalModelType.GN_W, SeasonalModelType.GE_W)) {
  170.             // set up interpolation parameters
  171.             final CellInterpolator interpolator = grid.getInterpolator(location.getLatitude(), location.getLongitude());
  172.             final int              dayOfYear    = date.getComponents(utc).getDate().getDayOfYear();

  173.             return new AzimuthalGradientCoefficients(interpolator.interpolate(e -> e.getModel(SeasonalModelType.GN_H).evaluate(dayOfYear)),
  174.                                                      interpolator.interpolate(e -> e.getModel(SeasonalModelType.GE_H).evaluate(dayOfYear)),
  175.                                                      interpolator.interpolate(e -> e.getModel(SeasonalModelType.GN_W).evaluate(dayOfYear)),
  176.                                                      interpolator.interpolate(e -> e.getModel(SeasonalModelType.GE_W).evaluate(dayOfYear)));
  177.         } else {
  178.             return null;
  179.         }

  180.     }

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

  185.         // set up interpolation parameters
  186.         final FieldCellInterpolator<T> interpolator = grid.getInterpolator(location.getLatitude(), location.getLongitude());
  187.         final int dayOfYear = date.getComponents(utc).getDate().getDayOfYear();

  188.         // ah and aw coefficients
  189.         return new FieldViennaACoefficients<>(interpolator.interpolate(e -> e.getModel(SeasonalModelType.AH).evaluate(dayOfYear)).multiply(0.001),
  190.                                               interpolator.interpolate(e -> e.getModel(SeasonalModelType.AW).evaluate(dayOfYear)).multiply(0.001));

  191.     }

  192.     /** {@inheritDoc} */
  193.     @Override
  194.     public <T extends CalculusFieldElement<T>> FieldPressureTemperatureHumidity<T> getWeatherParamerers(final FieldGeodeticPoint<T> location,
  195.                                                                                                         final FieldAbsoluteDate<T> date) {

  196.         // set up interpolation parameters
  197.         final FieldCellInterpolator<T> interpolator = grid.getInterpolator(location.getLatitude(), location.getLongitude());
  198.         final int dayOfYear = date.getComponents(utc).getDate().getDayOfYear();

  199.         // Corrected height (can be negative)
  200.         final T undu            = interpolator.interpolate(e -> e.getUndulation());
  201.         final T correctedheight = location.getAltitude().subtract(undu).subtract(interpolator.interpolate(e -> e.getHs()));

  202.         // Temperature gradient [K/m]
  203.         final T dTdH = interpolator.interpolate(e -> e.getModel(SeasonalModelType.DT).evaluate(dayOfYear)).multiply(0.001);

  204.         // Specific humidity
  205.         final T qv = interpolator.interpolate(e -> e.getModel(SeasonalModelType.QV).evaluate(dayOfYear)).multiply(0.001);

  206.         // For the computation of the temperature and the pressure, we use
  207.         // the standard ICAO atmosphere formulas.

  208.         // Temperature [K]
  209.         final T t0 = interpolator.interpolate(e -> e.getModel(SeasonalModelType.TEMPERATURE).evaluate(dayOfYear));
  210.         final T temperature = correctedheight.multiply(dTdH).add(t0);

  211.         // Pressure [hPa]
  212.         final T p0       = interpolator.interpolate(e -> e.getModel(SeasonalModelType.PRESSURE).evaluate(dayOfYear));
  213.         final T exponent = dTdH.multiply(R).reciprocal().multiply(G);
  214.         final T pressure = FastMath.pow(correctedheight.multiply(dTdH.negate().divide(t0)).add(1), exponent).multiply(p0.multiply(0.01));

  215.         // Water vapor pressure [hPa]
  216.         final T e0 = pressure.multiply(qv.divide(qv.multiply(0.378).add(0.622 )));

  217.         // mean temperature weighted with water vapor pressure
  218.         final T tm = grid.hasModels(SeasonalModelType.TM) ?
  219.                      interpolator.interpolate(e -> e.getModel(SeasonalModelType.TM).evaluate(dayOfYear)) :
  220.                      date.getField().getZero().newInstance(Double.NaN);

  221.         // water vapor decrease factor
  222.         final T lambda = grid.hasModels(SeasonalModelType.LAMBDA) ?
  223.                          interpolator.interpolate(e -> e.getModel(SeasonalModelType.LAMBDA).evaluate(dayOfYear)) :
  224.                          date.getField().getZero().newInstance(Double.NaN);

  225.         return new FieldPressureTemperatureHumidity<>(location.getAltitude(),
  226.                                                       TroposphericModelUtils.HECTO_PASCAL.toSI(pressure),
  227.                                                       temperature,
  228.                                                       TroposphericModelUtils.HECTO_PASCAL.toSI(e0),
  229.                                                       tm,
  230.                                                       lambda);

  231.     }

  232.     /** {@inheritDoc} */
  233.     @Override
  234.     public <T extends CalculusFieldElement<T>> FieldAzimuthalGradientCoefficients<T> getGradientCoefficients(final FieldGeodeticPoint<T> location,
  235.                                                                                                              final FieldAbsoluteDate<T> date) {

  236.         if (grid.hasModels(SeasonalModelType.GN_H, SeasonalModelType.GE_H, SeasonalModelType.GN_W, SeasonalModelType.GE_W)) {
  237.             // set up interpolation parameters
  238.             final FieldCellInterpolator<T> interpolator = grid.getInterpolator(location.getLatitude(), location.getLongitude());
  239.             final int                      dayOfYear    = date.getComponents(utc).getDate().getDayOfYear();

  240.             return new FieldAzimuthalGradientCoefficients<>(interpolator.interpolate(e -> e.getModel(SeasonalModelType.GN_H).evaluate(dayOfYear)),
  241.                                                             interpolator.interpolate(e -> e.getModel(SeasonalModelType.GE_H).evaluate(dayOfYear)),
  242.                                                             interpolator.interpolate(e -> e.getModel(SeasonalModelType.GN_W).evaluate(dayOfYear)),
  243.                                                             interpolator.interpolate(e -> e.getModel(SeasonalModelType.GE_W).evaluate(dayOfYear)));
  244.         } else {
  245.             return null;
  246.         }

  247.     }

  248. }