NeQuickModel.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.ionosphere.nequick;

  18. import java.util.Collections;
  19. import java.util.List;

  20. import org.hipparchus.CalculusFieldElement;
  21. import org.hipparchus.Field;
  22. import org.hipparchus.util.FastMath;
  23. import org.hipparchus.util.MathArrays;
  24. import org.orekit.bodies.BodyShape;
  25. import org.orekit.bodies.FieldGeodeticPoint;
  26. import org.orekit.bodies.GeodeticPoint;
  27. import org.orekit.frames.TopocentricFrame;
  28. import org.orekit.models.earth.ionosphere.IonosphericModel;
  29. import org.orekit.propagation.FieldSpacecraftState;
  30. import org.orekit.propagation.SpacecraftState;
  31. import org.orekit.time.AbsoluteDate;
  32. import org.orekit.time.DateComponents;
  33. import org.orekit.time.DateTimeComponents;
  34. import org.orekit.time.FieldAbsoluteDate;
  35. import org.orekit.time.TimeScale;
  36. import org.orekit.utils.ParameterDriver;
  37. import org.orekit.utils.units.Unit;

  38. /**
  39.  * NeQuick ionospheric delay model.
  40.  *
  41.  * @author Bryan Cazabonne
  42.  *
  43.  * @see "European Union (2016). European GNSS (Galileo) Open Service-Ionospheric Correction
  44.  *       Algorithm for Galileo Single Frequency Users. 1.2."
  45.  * @see <a href="https://www.itu.int/rec/R-REC-P.531/en">ITU-R P.531</a>
  46.  *
  47.  * @since 10.1
  48.  */
  49. public abstract class NeQuickModel implements IonosphericModel {

  50.     /** Mean Earth radius in m (Ref Table 2.5.2). */
  51.     public static final double RE = 6371200.0;

  52.     /** Factor for the electron density computation. */
  53.     private static final double DENSITY_FACTOR = 1.0e11;

  54.     /** Factor for the path delay computation. */
  55.     private static final double DELAY_FACTOR = 40.3e16;

  56.     /** F2 coefficients used by the F2 layer (flatten array for cache efficiency). */
  57.     private final double[][] flattenF2;

  58.     /** Fm3 coefficients used by the M(3000)F2 layer(flatten array for cache efficiency). */
  59.     private final double[][] flattenFm3;

  60.     /** UTC time scale. */
  61.     private final TimeScale utc;

  62.     /** Simple constructor.
  63.      * @param utc UTC time scale
  64.      * @since 13.0
  65.      */
  66.     protected NeQuickModel(final TimeScale utc) {

  67.         this.utc = utc;

  68.         // F2 layer values
  69.         this.flattenF2  = new double[12][];
  70.         this.flattenFm3 = new double[12][];

  71.     }

  72.     /** Get UTC time scale.
  73.      * @return UTC time scale
  74.      * @since 13.0
  75.      */
  76.     public TimeScale getUtc() {
  77.         return utc;
  78.     }

  79.     /** {@inheritDoc} */
  80.     @Override
  81.     public double pathDelay(final SpacecraftState state, final TopocentricFrame baseFrame,
  82.                             final double frequency, final double[] parameters) {
  83.         // Point
  84.         final GeodeticPoint recPoint = baseFrame.getPoint();
  85.         // Date
  86.         final AbsoluteDate date = state.getDate();

  87.         // Reference body shape
  88.         final BodyShape ellipsoid = baseFrame.getParentShape();
  89.         // Satellite geodetic coordinates
  90.         final GeodeticPoint satPoint = ellipsoid.transform(state.getPosition(ellipsoid.getBodyFrame()),
  91.                                                            ellipsoid.getBodyFrame(), state.getDate());

  92.         // Total Electron Content
  93.         final double tec = stec(date, recPoint, satPoint);

  94.         // Ionospheric delay
  95.         final double factor = DELAY_FACTOR / (frequency * frequency);
  96.         return factor * tec;
  97.     }

  98.     /** {@inheritDoc} */
  99.     @Override
  100.     public <T extends CalculusFieldElement<T>> T pathDelay(final FieldSpacecraftState<T> state,
  101.                                                            final TopocentricFrame baseFrame,
  102.                                                            final double frequency,
  103.                                                            final T[] parameters) {
  104.         // Date
  105.         final FieldAbsoluteDate<T> date = state.getDate();
  106.         // Point
  107.         final FieldGeodeticPoint<T> recPoint = baseFrame.getPoint(date.getField());


  108.         // Reference body shape
  109.         final BodyShape ellipsoid = baseFrame.getParentShape();
  110.         // Satellite geodetic coordinates
  111.         final FieldGeodeticPoint<T> satPoint = ellipsoid.transform(state.getPosition(ellipsoid.getBodyFrame()), ellipsoid.getBodyFrame(), state.getDate());

  112.         // Total Electron Content
  113.         final T tec = stec(date, recPoint, satPoint);

  114.         // Ionospheric delay
  115.         final double factor = DELAY_FACTOR / (frequency * frequency);
  116.         return tec.multiply(factor);
  117.     }

  118.     /** {@inheritDoc} */
  119.     @Override
  120.     public List<ParameterDriver> getParametersDrivers() {
  121.         return Collections.emptyList();
  122.     }

  123.     /**
  124.      * This method allows the computation of the Slant Total Electron Content (STEC).
  125.      * @param date current date
  126.      * @param recP receiver position
  127.      * @param satP satellite position
  128.      * @return the STEC in TECUnits
  129.      */
  130.     public double stec(final AbsoluteDate date, final GeodeticPoint recP, final GeodeticPoint satP) {
  131.         return stec(date.getComponents(utc), new Ray(recP, satP));
  132.     }

  133.     /**
  134.      * This method allows the computation of the Slant Total Electron Content (STEC).
  135.      * @param <T> type of the elements
  136.      * @param date current date
  137.      * @param recP receiver position
  138.      * @param satP satellite position
  139.      * @return the STEC in TECUnits
  140.      */
  141.     public <T extends CalculusFieldElement<T>> T stec(final FieldAbsoluteDate<T> date,
  142.                                                       final FieldGeodeticPoint<T> recP,
  143.                                                       final FieldGeodeticPoint<T> satP) {
  144.         return stec(date.getComponents(utc), new FieldRay<>(recP, satP));
  145.     }

  146.     /** Compute modip for a location.
  147.      * @param latitude latitude
  148.      * @param longitude longitude
  149.      * @return modip at specified location
  150.      * @since 13.0
  151.      */
  152.     protected abstract double computeMODIP(double latitude, double longitude);

  153.     /** Compute modip for a location.
  154.      * @param <T> type of the field elements
  155.      * @param latitude latitude
  156.      * @param longitude longitude
  157.      * @return modip at specified location
  158.      * @since 13.0
  159.      */
  160.     protected abstract <T extends CalculusFieldElement<T>> T computeMODIP(T latitude, T longitude);

  161.     /**
  162.      * Compute Fourier time series.
  163.      * @param dateTime current date time components
  164.      * @param az effective ionisation level
  165.      * @return Fourier time series
  166.      * @since 13.0.1
  167.      */
  168.     public FourierTimeSeries computeFourierTimeSeries(final DateTimeComponents dateTime, final double az) {

  169.          // Load the correct CCIR file
  170.         loadsIfNeeded(dateTime.getDate());

  171.         return new FourierTimeSeries(dateTime, az,
  172.                                      flattenF2[dateTime.getDate().getMonth() - 1],
  173.                                      flattenFm3[dateTime.getDate().getMonth() - 1]);

  174.     }

  175.     /**
  176.      * Computes the electron density at a given height.
  177.      * @param dateTime date
  178.      * @param az effective ionization level
  179.      * @param latitude latitude along the integration path
  180.      * @param longitude longitude along the integration path
  181.      * @param h height along the integration path in m
  182.      * @return electron density [m⁻³]
  183.      * @since 13.0
  184.      */
  185.     public double electronDensity(final DateTimeComponents dateTime, final double az,
  186.                                   final double latitude, final double longitude, final double h) {
  187.         return electronDensity(computeFourierTimeSeries(dateTime, az), latitude, longitude, h);
  188.     }

  189.     /**
  190.      * Computes the electron density at a given height.
  191.      * @param fourierTimeSeries Fourier time series for foF2 and M(3000)F2 layer (flatten array)
  192.      * @param latitude latitude along the integration path
  193.      * @param longitude longitude along the integration path
  194.      * @param h height along the integration path in m
  195.      * @return electron density [m⁻³]
  196.      * @since 13.0.1
  197.      */
  198.     public double electronDensity(final FourierTimeSeries fourierTimeSeries,
  199.                                   final double latitude, final double longitude, final double h) {

  200.         final double modip = computeMODIP(latitude, longitude);
  201.         final NeQuickParameters parameters = new NeQuickParameters(fourierTimeSeries, latitude, longitude, modip);

  202.         // Convert height in kilometers
  203.         final double hInKm = Unit.KILOMETRE.fromSI(h);

  204.         // Electron density
  205.         final double n;
  206.         if (hInKm <= parameters.getHmF2()) {
  207.             n = bottomElectronDensity(hInKm, parameters);
  208.         } else {
  209.             n = topElectronDensity(hInKm, parameters);
  210.         }

  211.         return n;

  212.     }

  213.     /**
  214.      * Compute Fourier time series.
  215.      * @param <T> type of the elements
  216.      * @param dateTime current date time components
  217.      * @param az effective ionisation level
  218.      * @return Fourier time series
  219.      * @since 13.0.1
  220.      */
  221.     public <T extends CalculusFieldElement<T>> FieldFourierTimeSeries<T> computeFourierTimeSeries(final DateTimeComponents dateTime,
  222.                                                                                                   final T az) {

  223.          // Load the correct CCIR file
  224.         loadsIfNeeded(dateTime.getDate());

  225.         return new FieldFourierTimeSeries<>(dateTime, az,
  226.                                             flattenF2[dateTime.getDate().getMonth() - 1],
  227.                                             flattenFm3[dateTime.getDate().getMonth() - 1]);

  228.     }

  229.     /**
  230.      * Computes the electron density at a given height.
  231.      * @param <T> type of the elements
  232.      * @param dateTime date
  233.      * @param az effective ionization level
  234.      * @param latitude latitude along the integration path
  235.      * @param longitude longitude along the integration path
  236.      * @param h height along the integration path in m
  237.      * @return electron density [m⁻³]
  238.      * @since 13.0
  239.      * CalculusFieldElement, CalculusFieldElement, CalculusFieldElement)}
  240.      */
  241.     public <T extends CalculusFieldElement<T>> T electronDensity(final DateTimeComponents dateTime, final T az,
  242.                                                                  final T latitude, final T longitude, final T h) {
  243.         return electronDensity(computeFourierTimeSeries(dateTime, az), latitude, longitude, h);
  244.     }

  245.     /**
  246.      * Computes the electron density at a given height.
  247.      * @param <T> type of the elements
  248.      * @param fourierTimeSeries Fourier time series for foF2 and M(3000)F2 layer (flatten array)
  249.      * @param latitude latitude along the integration path
  250.      * @param longitude longitude along the integration path
  251.      * @param h height along the integration path in m
  252.      * @return electron density [m⁻³]
  253.      * @since 13.0.1
  254.      */
  255.     public <T extends CalculusFieldElement<T>> T electronDensity(final FieldFourierTimeSeries<T> fourierTimeSeries,
  256.                                                                  final T latitude, final T longitude, final T h) {

  257.         final T modip = computeMODIP(latitude, longitude);
  258.         final FieldNeQuickParameters<T> parameters = new FieldNeQuickParameters<>(fourierTimeSeries, latitude, longitude, modip);

  259.         // Convert height in kilometers
  260.         final T hInKm = Unit.KILOMETRE.fromSI(h);

  261.         // Electron density
  262.         final T n;
  263.         if (hInKm.getReal() <= parameters.getHmF2().getReal()) {
  264.             n = bottomElectronDensity(hInKm, parameters);
  265.         } else {
  266.             n = topElectronDensity(hInKm, parameters);
  267.         }

  268.         return n;

  269.     }

  270.     /**
  271.      * Computes the electron density of the bottomside.
  272.      * @param h height in km
  273.      * @param parameters NeQuick model parameters
  274.      * @return the electron density N in m⁻³
  275.      */
  276.     private double bottomElectronDensity(final double h, final NeQuickParameters parameters) {

  277.         // Select the relevant B parameter for the current height (Eq. 109 and 110)
  278.         final double be  = parameters.getBE(h);
  279.         final double bf1 = parameters.getBF1(h);
  280.         final double bf2 = parameters.getB2Bot();

  281.         // Useful array of constants
  282.         final double[] ct = new double[] {
  283.             1.0 / bf2, 1.0 / bf1, 1.0 / be
  284.         };

  285.         // Compute the exponential argument for each layer (Eq. 111 to 113)
  286.         final double[] arguments = computeExponentialArguments(h, parameters);

  287.         // S coefficients
  288.         final double[] s = new double[3];
  289.         // Array of corrective terms
  290.         final double[] ds = new double[3];

  291.         // Layer amplitudes
  292.         final double[] amplitudes = parameters.getLayerAmplitudes();

  293.         // Fill arrays (Eq. 114 to 118)
  294.         for (int i = 0; i < 3; i++) {
  295.             if (FastMath.abs(arguments[i]) > 25.0) {
  296.                 s[i]  = 0.0;
  297.                 ds[i] = 0.0;
  298.             } else {
  299.                 final double expA   = clipExp(arguments[i]);
  300.                 final double opExpA = 1.0 + expA;
  301.                 s[i]  = amplitudes[i] * (expA / (opExpA * opExpA));
  302.                 ds[i] = ct[i] * ((1.0 - expA) / (1.0 + expA));
  303.             }
  304.         }

  305.         // Electron density
  306.         final double aNo = s[0] + s[1] + s[2];
  307.         if (applyChapmanParameters(h)) {
  308.             // Chapman parameters (Eq. 119 and 120)
  309.             final double bc = 1.0 - 10.0 * (MathArrays.linearCombination(s[0], ds[0], s[1], ds[1], s[2], ds[2]) / aNo);
  310.             final double z  = 0.1 * (h - 100.0);
  311.             // Electron density (Eq. 121)
  312.             return aNo * clipExp(1.0 - bc * z - clipExp(-z)) * DENSITY_FACTOR;
  313.         } else {
  314.             return aNo * DENSITY_FACTOR;
  315.         }

  316.     }

  317.     /**
  318.      * Computes the electron density of the bottomside.
  319.      * @param <T> type of the elements
  320.      * @param h height in km
  321.      * @param parameters NeQuick model parameters
  322.      * @return the electron density N in m⁻³
  323.      */
  324.     private <T extends CalculusFieldElement<T>> T bottomElectronDensity(final T h,
  325.                                                                         final FieldNeQuickParameters<T> parameters) {

  326.         // Zero and One
  327.         final Field<T> field = h.getField();
  328.         final T zero = field.getZero();
  329.         final T one  = field.getOne();

  330.         // Select the relevant B parameter for the current height (Eq. 109 and 110)
  331.         final T be  = parameters.getBE(h);
  332.         final T bf1 = parameters.getBF1(h);
  333.         final T bf2 = parameters.getB2Bot();

  334.         // Useful array of constants
  335.         final T[] ct = MathArrays.buildArray(field, 3);
  336.         ct[0] = bf2.reciprocal();
  337.         ct[1] = bf1.reciprocal();
  338.         ct[2] = be.reciprocal();

  339.         // Compute the exponential argument for each layer (Eq. 111 to 113)
  340.         final T[] arguments = computeExponentialArguments(h, parameters);

  341.         // S coefficients
  342.         final T[] s = MathArrays.buildArray(field, 3);
  343.         // Array of corrective terms
  344.         final T[] ds = MathArrays.buildArray(field, 3);

  345.         // Layer amplitudes
  346.         final T[] amplitudes = parameters.getLayerAmplitudes();

  347.         // Fill arrays (Eq. 114 to 118)
  348.         for (int i = 0; i < 3; i++) {
  349.             if (FastMath.abs(arguments[i]).getReal() > 25.0) {
  350.                 s[i]  = zero;
  351.                 ds[i] = zero;
  352.             } else {
  353.                 final T expA   = clipExp(arguments[i]);
  354.                 final T opExpA = expA.add(1.0);
  355.                 s[i]  = amplitudes[i].multiply(expA.divide(opExpA.multiply(opExpA)));
  356.                 ds[i] = ct[i].multiply(expA.negate().add(1.0).divide(expA.add(1.0)));
  357.             }
  358.         }

  359.         // Electron density
  360.         final T aNo = s[0].add(s[1]).add(s[2]);
  361.         if (applyChapmanParameters(h.getReal())) {
  362.             // Chapman parameters (Eq. 119 and 120)
  363.             final T bc = one.linearCombination(s[0], ds[0], s[1], ds[1], s[2], ds[2]).divide(aNo).multiply(10.0).negate().add(1.0);
  364.             final T z  = h.subtract(100.0).multiply(0.1);
  365.             // Electron density (Eq. 121)
  366.             return aNo.multiply(clipExp(bc.multiply(z).add(clipExp(z.negate())).negate().add(1.0))).multiply(DENSITY_FACTOR);
  367.         } else {
  368.             return aNo.multiply(DENSITY_FACTOR);
  369.         }

  370.     }

  371.     /**
  372.      * Computes the electron density of the topside.
  373.      * @param h height in km
  374.      * @param parameters NeQuick model parameters
  375.      * @return the electron density N in m⁻³
  376.      */
  377.     private double topElectronDensity(final double h, final NeQuickParameters parameters) {

  378.         // Constant parameters (Eq. 122 and 123)
  379.         final double g = 0.125;
  380.         final double r = 100.0;

  381.         // Arguments deltaH and z (Eq. 124 and 125)
  382.         final double deltaH = h - parameters.getHmF2();
  383.         final double h0     = computeH0(parameters);
  384.         final double z      = deltaH / (h0 * (1.0 + (r * g * deltaH) / (r * h0 + g * deltaH)));

  385.         // Exponential (Eq. 126)
  386.         final double ee = clipExp(z);

  387.         // Electron density (Eq. 127)
  388.         if (ee > 1.0e11) {
  389.             return (4.0 * parameters.getNmF2() / ee) * DENSITY_FACTOR;
  390.         } else {
  391.             final double opExpZ = 1.0 + ee;
  392.             return ((4.0 * parameters.getNmF2() * ee) / (opExpZ * opExpZ)) * DENSITY_FACTOR;
  393.         }
  394.     }

  395.     /**
  396.      * Computes the electron density of the topside.
  397.      * @param <T> type of the elements
  398.      * @param h height in km
  399.      * @param parameters NeQuick model parameters
  400.      * @return the electron density N in m⁻³
  401.      */
  402.     private <T extends CalculusFieldElement<T>> T topElectronDensity(final T h,
  403.                                                                      final FieldNeQuickParameters<T> parameters) {

  404.         // Constant parameters (Eq. 122 and 123)
  405.         final double g = 0.125;
  406.         final double r = 100.0;

  407.         // Arguments deltaH and z (Eq. 124 and 125)
  408.         final T deltaH = h.subtract(parameters.getHmF2());
  409.         final T h0     = computeH0(parameters);
  410.         final T z      = deltaH.divide(h0.multiply(deltaH.multiply(r).multiply(g).divide(h0.multiply(r).add(deltaH.multiply(g))).add(1.0)));

  411.         // Exponential (Eq. 126)
  412.         final T ee = clipExp(z);

  413.         // Electron density (Eq. 127)
  414.         if (ee.getReal() > 1.0e11) {
  415.             return parameters.getNmF2().multiply(4.0).divide(ee).multiply(DENSITY_FACTOR);
  416.         } else {
  417.             final T opExpZ = ee.add(1.0);
  418.             return parameters.getNmF2().multiply(4.0).multiply(ee).divide(opExpZ.multiply(opExpZ)).multiply(DENSITY_FACTOR);
  419.         }
  420.     }

  421.     /**
  422.      * Lazy loading of CCIR data.
  423.      * @param date current date components
  424.      */
  425.     private void loadsIfNeeded(final DateComponents date) {

  426.         // Month index
  427.         final int monthIndex = date.getMonth() - 1;

  428.         // Check if CCIR has already been loaded for this month
  429.         if (flattenF2[monthIndex] == null) {

  430.             // Read file
  431.             final CCIRLoader loader = new CCIRLoader();
  432.             loader.loadCCIRCoefficients(date);

  433.             // Update arrays
  434.             this.flattenF2[monthIndex]  = flatten(loader.getF2());
  435.             this.flattenFm3[monthIndex] = flatten(loader.getFm3());
  436.         }
  437.     }

  438.     /** Flatten a 3-dimensions array.
  439.      * <p>
  440.      * This method convert 3-dimensions arrays into 1-dimension arrays
  441.      * optimized to avoid cache misses when looping over all elements.
  442.      * </p>
  443.      * @param original original array a[i][j][k]
  444.      * @return flatten array, for embedded loops on j (outer), k (intermediate), i (inner)
  445.      */
  446.     private double[] flatten(final double[][][] original) {
  447.         final double[] flatten = new double[original.length * original[0].length * original[0][0].length];
  448.         int index = 0;
  449.         for (int j = 0; j < original[0].length; j++) {
  450.             for (int k = 0; k < original[0][0].length; k++) {
  451.                 for (final double[][] doubles : original) {
  452.                     flatten[index++] = doubles[j][k];
  453.                 }
  454.             }
  455.         }
  456.         return flatten;
  457.     }

  458.     /**
  459.      * A clipped exponential function.
  460.      * <p>
  461.      * This function, describe in section F.2.12.2 of the reference document, is
  462.      * recommended for the computation of exponential values.
  463.      * </p>
  464.      * @param power power for exponential function
  465.      * @return clipped exponential value
  466.      */
  467.     protected double clipExp(final double power) {
  468.         if (power > 80.0) {
  469.             return 5.5406E34;
  470.         } else if (power < -80) {
  471.             return 1.8049E-35;
  472.         } else {
  473.             return FastMath.exp(power);
  474.         }
  475.     }

  476.     /**
  477.      * A clipped exponential function.
  478.      * <p>
  479.      * This function, describe in section F.2.12.2 of the reference document, is
  480.      * recommended for the computation of exponential values.
  481.      * </p>
  482.      * @param <T> type of the elements
  483.      * @param power power for exponential function
  484.      * @return clipped exponential value
  485.      */
  486.     protected <T extends CalculusFieldElement<T>> T clipExp(final T power) {
  487.         if (power.getReal() > 80.0) {
  488.             return power.newInstance(5.5406E34);
  489.         } else if (power.getReal() < -80) {
  490.             return power.newInstance(1.8049E-35);
  491.         } else {
  492.             return FastMath.exp(power);
  493.         }
  494.     }

  495.     /**
  496.      * This method allows the computation of the Slant Total Electron Content (STEC).
  497.      *
  498.      * @param dateTime current date
  499.      * @param ray      ray-perigee parameters
  500.      * @return the STEC in TECUnits
  501.      */
  502.     abstract double stec(DateTimeComponents dateTime, Ray ray);

  503.     /**
  504.      * This method allows the computation of the Slant Total Electron Content (STEC).
  505.      *
  506.      * @param <T>      type of the field elements
  507.      * @param dateTime current date
  508.      * @param ray      ray-perigee parameters
  509.      * @return the STEC in TECUnits
  510.      */
  511.     abstract <T extends CalculusFieldElement<T>> T stec(DateTimeComponents dateTime, FieldRay<T> ray);

  512.     /**
  513.      * Check if Chapman parameters should be applied.
  514.      *
  515.      * @param hInKm height in kilometers
  516.      * @return true if Chapman parameters should be applied
  517.      * @since 13.0
  518.      */
  519.     abstract boolean applyChapmanParameters(double hInKm);

  520.     /**
  521.      * Compute exponential arguments.
  522.      * @param h height in km
  523.      * @param parameters NeQuick model parameters
  524.      * @return exponential arguments
  525.      * @since 13.0
  526.      */
  527.     abstract double[] computeExponentialArguments(double h, NeQuickParameters parameters);

  528.     /**
  529.      * Compute exponential arguments.
  530.      * @param <T>   type of the field elements
  531.      * @param h height in km
  532.      * @param parameters NeQuick model parameters
  533.      * @return exponential arguments
  534.      * @since 13.0
  535.      */
  536.     abstract <T extends CalculusFieldElement<T>> T[] computeExponentialArguments(T h,
  537.                                                                                  FieldNeQuickParameters<T> parameters);

  538.     /**
  539.      * Compute topside thickness parameter.
  540.      * @param parameters NeQuick model parameters
  541.      * @return topside thickness parameter
  542.      * @since 13.0
  543.      */
  544.     abstract double computeH0(NeQuickParameters parameters);

  545.     /**
  546.      * Compute topside thickness parameter.
  547.      * @param <T>   type of the field elements
  548.      * @param parameters NeQuick model parameters
  549.      * @return topside thickness parameter
  550.      * @since 13.0
  551.      */
  552.     abstract <T extends CalculusFieldElement<T>> T computeH0(FieldNeQuickParameters<T> parameters);

  553. }