JB2008.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.atmosphere;

  18. import org.hipparchus.CalculusFieldElement;
  19. import org.hipparchus.util.FastMath;
  20. import org.hipparchus.util.FieldSinCos;
  21. import org.hipparchus.util.MathUtils;
  22. import org.hipparchus.util.SinCos;
  23. import org.orekit.annotation.DefaultDataContext;
  24. import org.orekit.bodies.BodyShape;
  25. import org.orekit.data.DataContext;
  26. import org.orekit.time.AbsoluteDate;
  27. import org.orekit.time.FieldAbsoluteDate;
  28. import org.orekit.time.TimeScale;

  29. import org.orekit.utils.Constants;
  30. import org.orekit.utils.ExtendedPositionProvider;

  31. /** This is the realization of the Jacchia-Bowman 2008 atmospheric model.
  32.  * <p>
  33.  * It is described in the paper:<br>
  34.  * <a href="https://www.researchgate.net/publication/228621668_A_New_Empirical_Thermospheric_Density_Model_JB2008_Using_New_Solar_and_Geomagnetic_Indices">A
  35.  * New Empirical Thermospheric Density Model JB2008 Using New Solar Indices</a><br>
  36.  * <i>Bruce R. Bowman &amp; al.</i><br>
  37.  * AIAA 2008-6438<br>
  38.  * </p>
  39.  * <p>
  40.  * Two computation methods are proposed to the user:
  41.  * <ul>
  42.  * <li>one OREKIT independent and compliant with initial FORTRAN routine entry values:
  43.  *     {@link #getDensity(double, double, double, double, double, double, double, double, double, double, double, double, double, double, double)}. </li>
  44.  * <li>one compliant with OREKIT Atmosphere interface, necessary to the
  45.  *     {@link org.orekit.forces.drag.DragForce drag force model} computation.</li>
  46.  * </ul>
  47.  * <p>
  48.  * This model provides dense output for all altitudes and positions. Output data are :
  49.  * <ul>
  50.  * <li>Exospheric Temperature above Input Position (deg K)</li>
  51.  * <li>Temperature at Input Position (deg K)</li>
  52.  * <li>Total Mass-Density at Input Position (kg/m³)</li>
  53.  * </ul>
  54.  * <p>
  55.  * The model needs geographical and time information to compute general values,
  56.  * but also needs space weather data : mean and daily solar flux, retrieved through
  57.  * different indices, and planetary geomagnetic indices.<br>
  58.  * More information on these indices can be found on the <a
  59.  * href="http://sol.spacenvironment.net/~JB2008/indices.html">
  60.  * official JB2008 website.</a>
  61.  * </p>
  62.  *
  63.  * @author Bruce R Bowman (HQ AFSPC, Space Analysis Division), 2008: FORTRAN routine
  64.  * @author Pascal Parraud (java translation)
  65.  */
  66. public class JB2008 extends AbstractJacchiaBowmanModel {

  67.     /** FZ global model values (1997-2006 fit).  */
  68.     private static final double[] FZM = {
  69.         0.2689e+00, -0.1176e-01, 0.2782e-01, -0.2782e-01, 0.3470e-03
  70.     };

  71.     /** GT global model values (1997-2006 fit). */
  72.     private static final double[] GTM = {
  73.         -0.3633e+00, 0.8506e-01,  0.2401e+00, -0.1897e+00, -0.2554e+00,
  74.         -0.1790e-01, 0.5650e-03, -0.6407e-03, -0.3418e-02, -0.1252e-02
  75.     };

  76.     /** External data container. */
  77.     private final JB2008InputParameters inputParams;

  78.     /** Constructor with space environment information for internal computation.
  79.      *
  80.      * <p>This method uses the {@link DataContext#getDefault() default data context}.
  81.      *
  82.      * @param parameters the solar and magnetic activity data
  83.      * @param sun the sun position
  84.      * @param earth the earth body shape
  85.      * @see #JB2008(JB2008InputParameters, ExtendedPositionProvider, BodyShape, TimeScale)
  86.      */
  87.     @DefaultDataContext
  88.     public JB2008(final JB2008InputParameters parameters,
  89.                   final ExtendedPositionProvider sun, final BodyShape earth) {
  90.         this(parameters, sun, earth, DataContext.getDefault().getTimeScales().getUTC());
  91.     }

  92.     /**
  93.      * Constructor with space environment information for internal computation.
  94.      *
  95.      * @param parameters the solar and magnetic activity data
  96.      * @param sun        the sun position
  97.      * @param earth      the earth body shape
  98.      * @param utc        UTC time scale. Used to computed the day fraction.
  99.      * @since 10.1
  100.      */
  101.     public JB2008(final JB2008InputParameters parameters,
  102.                   final ExtendedPositionProvider sun,
  103.                   final BodyShape earth,
  104.                   final TimeScale utc) {
  105.         super(sun, utc, earth,
  106.               parameters == null ? AbsoluteDate.PAST_INFINITY : parameters.getMinDate(),
  107.               parameters == null ? AbsoluteDate.FUTURE_INFINITY : parameters.getMaxDate());
  108.         this.inputParams = parameters;
  109.     }

  110.     /** Get the local density with initial entries.
  111.      * <p>
  112.      * The method creates a new instance of {@link JB2008} model and set the input
  113.      * solar activity data equal to the provided one. These data are then
  114.      * available form {@link AbsoluteDate#PAST_INFINITY} to {@link AbsoluteDate#FUTURE_INFINITY}.
  115.      * </p>
  116.      * @param dateMJD date and time, in modified julian days and fraction
  117.      * @param sunRA Right Ascension of Sun (radians).
  118.      * @param sunDecli Declination of Sun (radians).
  119.      * @param satLon Right Ascension of position (radians)
  120.      * @param satLat Geocentric latitude of position (radians)
  121.      * @param satAlt Height of position (m)
  122.      * @param f10 10.7-cm Solar flux (1e<sup>-22</sup>*Watt/(m²*Hertz))<br>
  123.      *        (Tabular time 1.0 day earlier)
  124.      * @param f10B 10.7-cm Solar Flux, averaged 81-day centered on the input time<br>
  125.      *        (Tabular time 1.0 day earlier)
  126.      * @param s10 EUV index (26-34 nm) scaled to F10<br>
  127.      *        (Tabular time 1 day earlier)
  128.      * @param s10B UV 81-day averaged centered index
  129.      *        (Tabular time 1 day earlier)
  130.      * @param xm10 MG2 index scaled to F10<br>
  131.      *        (Tabular time 2.0 days earlier)
  132.      * @param xm10B MG2 81-day ave. centered index<br>
  133.      *        (Tabular time 2.0 days earlier)
  134.      * @param y10 Solar X-Ray &amp; Lya index scaled to F10<br>
  135.      *        (Tabular time 5.0 days earlier)
  136.      * @param y10B Solar X-Ray &amp; Lya 81-day ave. centered index<br>
  137.      *        (Tabular time 5.0 days earlier)
  138.      * @param dstdtc Temperature change computed from Dst index
  139.      * @return total mass-Density at input position (kg/m³)
  140.      */
  141.     public double getDensity(final double dateMJD, final double sunRA, final double sunDecli,
  142.                              final double satLon, final double satLat, final double satAlt,
  143.                              final double f10, final double f10B, final double s10,
  144.                              final double s10B, final double xm10, final double xm10B,
  145.                              final double y10, final double y10B, final double dstdtc) {
  146.         final LocalProvider provider = new LocalProvider(f10, f10B, s10, s10B, xm10, xm10B, y10, y10B, dstdtc);
  147.         final JB2008 modelWithLocalData = new JB2008(provider, getSun(), getEarth(), getUtc());
  148.         final double mjd = FastMath.floor(dateMJD);
  149.         final AbsoluteDate computationEpoch = AbsoluteDate.createMJDDate((int) mjd, (dateMJD - mjd) * Constants.JULIAN_DAY, getUtc());
  150.         return modelWithLocalData.computeDensity(computationEpoch, sunRA, sunDecli, satLon, satLat, satAlt);
  151.     }

  152.     /** Get the local density with initial entries.
  153.      * <p>
  154.      * The method creates a new instance of {@link JB2008} model and set the input
  155.      * solar activity data equal to the provided one. These data are then
  156.      * available form {@link AbsoluteDate#PAST_INFINITY} to {@link AbsoluteDate#FUTURE_INFINITY}.
  157.      * </p>
  158.      * @param dateMJD date and time, in modified julian days and fraction
  159.      * @param sunRA Right Ascension of Sun (radians).
  160.      * @param sunDecli Declination of Sun (radians).
  161.      * @param satLon Right Ascension of position (radians)
  162.      * @param satLat Geocentric latitude of position (radians)
  163.      * @param satAlt Height of position (m)
  164.      * @param f10 10.7-cm Solar flux (1e<sup>-22</sup>*Watt/(m²*Hertz))<br>
  165.      *        (Tabular time 1.0 day earlier)
  166.      * @param f10B 10.7-cm Solar Flux, averaged 81-day centered on the input time<br>
  167.      *        (Tabular time 1.0 day earlier)
  168.      * @param s10 EUV index (26-34 nm) scaled to F10<br>
  169.      *        (Tabular time 1 day earlier)
  170.      * @param s10B UV 81-day averaged centered index
  171.      *        (Tabular time 1 day earlier)
  172.      * @param xm10 MG2 index scaled to F10<br>
  173.      *        (Tabular time 2.0 days earlier)
  174.      * @param xm10B MG2 81-day ave. centered index<br>
  175.      *        (Tabular time 2.0 days earlier)
  176.      * @param y10 Solar X-Ray &amp; Lya index scaled to F10<br>
  177.      *        (Tabular time 5.0 days earlier)
  178.      * @param y10B Solar X-Ray &amp; Lya 81-day ave. centered index<br>
  179.      *        (Tabular time 5.0 days earlier)
  180.      * @param dstdtc Temperature change computed from Dst index
  181.      * @param <T> type of the elements
  182.      * @return total mass-Density at input position (kg/m³)
  183.      */
  184.     public <T extends CalculusFieldElement<T>> T getDensity(final T dateMJD, final T sunRA, final T sunDecli,
  185.                                                             final T satLon, final T satLat, final T satAlt,
  186.                                                             final double f10, final double f10B, final double s10,
  187.                                                             final double s10B, final double xm10, final double xm10B,
  188.                                                             final double y10, final double y10B, final double dstdtc) {
  189.         final LocalProvider provider = new LocalProvider(f10, f10B, s10, s10B, xm10, xm10B, y10, y10B, dstdtc);
  190.         final JB2008 modelWithLocalData = new JB2008(provider, getSun(), getEarth(), getUtc());
  191.         final T mjd = FastMath.floor(dateMJD);
  192.         final FieldAbsoluteDate<T> computationEpoch = FieldAbsoluteDate.createMJDDate((int) mjd.getReal(), dateMJD.subtract(mjd).multiply(Constants.JULIAN_DAY), getUtc());
  193.         return modelWithLocalData.computeDensity(computationEpoch, sunRA, sunDecli, satLon, satLat, satAlt);
  194.     }

  195.         /** {@inheritDoc} */
  196.     @Override
  197.     protected double computeTInf(final AbsoluteDate date, final double tsubl, final double dtclst) {
  198.         return tsubl + inputParams.getDSTDTC(date) + dtclst;
  199.     }

  200.     /** {@inheritDoc} */
  201.     @Override
  202.     protected double computeTc(final AbsoluteDate date) {
  203.         final double f10   = inputParams.getF10(date);
  204.         final double f10B  = inputParams.getF10B(date);
  205.         final double s10   = inputParams.getS10(date);
  206.         final double s10B  = inputParams.getS10B(date);
  207.         final double xm10  = inputParams.getXM10(date);
  208.         final double xm10B = inputParams.getXM10B(date);
  209.         final double y10   = inputParams.getY10(date);
  210.         final double y10B  = inputParams.getY10B(date);
  211.         final double fn    = FastMath.min(1.0, FastMath.pow(f10B / 240., 0.25));
  212.         final double fsb   = f10B * fn + s10B * (1. - fn);
  213.         return 392.4 + 3.227 * fsb + 0.298 * (f10 - f10B) + 2.259 * (s10 - s10B) + 0.312 * (xm10 - xm10B) + 0.178 * (y10 - y10B);
  214.     }

  215.     /** {@inheritDoc} */
  216.     @Override
  217.     protected double getF10(final AbsoluteDate date) {
  218.         return inputParams.getF10(date);
  219.     }

  220.     /** {@inheritDoc} */
  221.     @Override
  222.     protected double getF10B(final AbsoluteDate date) {
  223.         return inputParams.getF10B(date);
  224.     }

  225.     /** {@inheritDoc} */
  226.     @Override
  227.     protected <T extends CalculusFieldElement<T>> T computeTInf(final AbsoluteDate date, final T tsubl, final T dtclst) {
  228.         return tsubl.add(inputParams.getDSTDTC(date)).add(dtclst);
  229.     }

  230.     /** {@inheritDoc} */
  231.     @Override
  232.     protected double semian(final AbsoluteDate date, final double day, final double altKm) {

  233.         final double f10B = inputParams.getF10B(date);
  234.         final double s10B = inputParams.getS10B(date);
  235.         final double xm10B = inputParams.getXM10B(date);

  236.         final double htz = altKm / 1000.0;

  237.         // COMPUTE NEW 81-DAY CENTERED SOLAR INDEX FOR FZ
  238.         double fsmb = f10B - 0.70 * s10B - 0.04 * xm10B;

  239.         // SEMIANNUAL AMPLITUDE
  240.         final double fzz = FZM[0] + fsmb * (FZM[1] + htz * (FZM[2] + FZM[3] * htz + FZM[4] * fsmb));

  241.         // COMPUTE DAILY 81-DAY CENTERED SOLAR INDEX FOR GT
  242.         fsmb  = f10B - 0.75 * s10B - 0.37 * xm10B;

  243.         // SEMIANNUAL PHASE FUNCTION
  244.         final double tau   = MathUtils.TWO_PI * (day - 1.0) / 365;
  245.         final SinCos sc1P = FastMath.sinCos(tau);
  246.         final SinCos sc2P = SinCos.sum(sc1P, sc1P);
  247.         final double gtz = GTM[0] + GTM[1] * sc1P.sin() + GTM[2] * sc1P.cos() + GTM[3] * sc2P.sin() + GTM[4] * sc2P.cos() +
  248.                    fsmb * (GTM[5] + GTM[6] * sc1P.sin() + GTM[7] * sc1P.cos() + GTM[8] * sc2P.sin() + GTM[9] * sc2P.cos());

  249.         return FastMath.max(1.0e-6, fzz) * gtz;

  250.     }

  251.     /** {@inheritDoc} */
  252.     @Override
  253.     protected <T extends CalculusFieldElement<T>>  T semian(final AbsoluteDate date, final T day, final T altKm) {

  254.         final double f10B = inputParams.getF10B(date);
  255.         final double s10B = inputParams.getS10B(date);
  256.         final double xm10B = inputParams.getXM10B(date);

  257.         final T htz = altKm.divide(1000.0);

  258.         // COMPUTE NEW 81-DAY CENTERED SOLAR INDEX FOR FZ
  259.         double fsmb = f10B - 0.70 * s10B - 0.04 * xm10B;

  260.         // SEMIANNUAL AMPLITUDE
  261.         final T fzz = htz.multiply(FZM[3]).add(FZM[2] + FZM[4] * fsmb).multiply(htz).add(FZM[1]).multiply(fsmb).add(FZM[0]);

  262.         // COMPUTE DAILY 81-DAY CENTERED SOLAR INDEX FOR GT
  263.         fsmb  = f10B - 0.75 * s10B - 0.37 * xm10B;

  264.         // SEMIANNUAL PHASE FUNCTION
  265.         final T tau   = day.subtract(1).divide(365).multiply(MathUtils.TWO_PI);
  266.         final FieldSinCos<T> sc1P = FastMath.sinCos(tau);
  267.         final FieldSinCos<T> sc2P = FieldSinCos.sum(sc1P, sc1P);
  268.         final T gtz =           sc2P.cos().multiply(GTM[9]).
  269.                             add(sc2P.sin().multiply(GTM[8])).
  270.                             add(sc1P.cos().multiply(GTM[7])).
  271.                             add(sc1P.sin().multiply(GTM[6])).
  272.                             add(GTM[5]).multiply(fsmb).
  273.                         add(    sc2P.cos().multiply(GTM[4]).
  274.                             add(sc2P.sin().multiply(GTM[3])).
  275.                             add(sc1P.cos().multiply(GTM[2])).
  276.                             add(sc1P.sin().multiply(GTM[1])).
  277.                             add(GTM[0]));

  278.         return fzz.getReal() > 1.0e-6 ? gtz.multiply(fzz) : gtz.multiply(1.0e-6);

  279.     }

  280.     /** Local provider for solar activity data. */
  281.     private static class LocalProvider implements JB2008InputParameters {

  282.         /** 10.7-cm Solar flux. */
  283.         private double f10;

  284.         /** 10.7-cm Solar Flux, averaged 81-day centered on the input time. */
  285.         private double f10B;

  286.         /** EUV index (26-34 nm) scaled to F10. */
  287.         private double s10;

  288.         /** UV 81-day averaged centered index. */
  289.         private double s10B;

  290.         /** MG2 index scaled to F10. */
  291.         private double xm10;

  292.         /** MG2 81-day ave. centered index. */
  293.         private double xm10B;

  294.         /** Solar X-Ray &amp; Lya index scaled to F10. */
  295.         private double y10;

  296.         /** Solar X-Ray &amp; Lya 81-day ave. centered index. */
  297.         private double y10B;

  298.         /** Temperature change computed from Dst index. */
  299.         private double dstdtc;

  300.         /** Constructor.
  301.          * @param f10 10.7-cm Solar flux (1e<sup>-22</sup>*Watt/(m²*Hertz))<br>
  302.          *        (Tabular time 1.0 day earlier)
  303.          * @param f10B 10.7-cm Solar Flux, averaged 81-day centered on the input time<br>
  304.          *        (Tabular time 1.0 day earlier)
  305.          * @param s10 EUV index (26-34 nm) scaled to F10<br>
  306.          *        (Tabular time 1 day earlier)
  307.          * @param s10B UV 81-day averaged centered index
  308.          *        (Tabular time 1 day earlier)
  309.          * @param xm10 MG2 index scaled to F10<br>
  310.          *        (Tabular time 2.0 days earlier)
  311.          * @param xm10B MG2 81-day ave. centered index<br>
  312.          *        (Tabular time 2.0 days earlier)
  313.          * @param y10 Solar X-Ray &amp; Lya index scaled to F10<br>
  314.          *        (Tabular time 5.0 days earlier)
  315.          * @param y10B Solar X-Ray &amp; Lya 81-day ave. centered index<br>
  316.          *        (Tabular time 5.0 days earlier)
  317.          * @param dstdtc Temperature change computed from Dst index
  318.          */
  319.         LocalProvider(final double f10, final double f10B, final double s10,
  320.                       final double s10B, final double xm10, final double xm10B,
  321.                       final double y10, final double y10B, final double dstdtc) {
  322.             this.f10    = f10;
  323.             this.f10B   = f10B;
  324.             this.s10    = s10;
  325.             this.s10B   = s10B;
  326.             this.xm10   = xm10;
  327.             this.xm10B  = xm10B;
  328.             this.y10    = y10;
  329.             this.y10B   = y10B;
  330.             this.dstdtc = dstdtc;
  331.         }

  332.         /** {@inheritDoc} */
  333.         @Override
  334.         public AbsoluteDate getMinDate() {
  335.             return AbsoluteDate.PAST_INFINITY;
  336.         }

  337.         /** {@inheritDoc} */
  338.         @Override
  339.         public AbsoluteDate getMaxDate() {
  340.             return AbsoluteDate.FUTURE_INFINITY;
  341.         }

  342.         /** {@inheritDoc} */
  343.         @Override
  344.         public double getF10(final AbsoluteDate date) {
  345.             return f10;
  346.         }

  347.         /** {@inheritDoc} */
  348.         @Override
  349.         public double getF10B(final AbsoluteDate date) {
  350.             return f10B;
  351.         }

  352.         /** {@inheritDoc} */
  353.         @Override
  354.         public double getS10(final AbsoluteDate date) {
  355.             return s10;
  356.         }

  357.         /** {@inheritDoc} */
  358.         @Override
  359.         public double getS10B(final AbsoluteDate date) {
  360.             return s10B;
  361.         }

  362.         /** {@inheritDoc} */
  363.         @Override
  364.         public double getXM10(final AbsoluteDate date) {
  365.             return xm10;
  366.         }

  367.         /** {@inheritDoc} */
  368.         @Override
  369.         public double getXM10B(final AbsoluteDate date) {
  370.             return xm10B;
  371.         }

  372.         /** {@inheritDoc} */
  373.         @Override
  374.         public double getY10(final AbsoluteDate date) {
  375.             return y10;
  376.         }

  377.         /** {@inheritDoc} */
  378.         @Override
  379.         public double getY10B(final AbsoluteDate date) {
  380.             return y10B;
  381.         }

  382.         /** {@inheritDoc} */
  383.         @Override
  384.         public double getDSTDTC(final AbsoluteDate date) {
  385.             return dstdtc;
  386.         }
  387.     }
  388. }