GravityFieldFactory.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.forces.gravity.potential;

  18. import org.hipparchus.util.FastMath;
  19. import org.hipparchus.util.Precision;
  20. import org.orekit.annotation.DefaultDataContext;
  21. import org.orekit.data.DataContext;
  22. import org.orekit.errors.OrekitException;
  23. import org.orekit.errors.OrekitMessages;
  24. import org.orekit.time.AbsoluteDate;

  25. import java.util.List;

  26. /** Factory used to read gravity field files in several supported formats.
  27.  * @author Fabien Maussion
  28.  * @author Pascal Parraud
  29.  * @author Luc Maisonobe
  30.  */
  31. public class GravityFieldFactory {

  32.     /* These constants were left here instead of being moved to LazyLoadedGravityFields
  33.      * because they are public.
  34.      */

  35.     /** Default regular expression for ICGEM files. */
  36.     public static final String ICGEM_FILENAME = "^(.*\\.gfc)|(g(\\d)+_eigen[-_](\\w)+_coef)$";

  37.     /** Default regular expression for SHM files. */
  38.     public static final String SHM_FILENAME = "^eigen[-_](\\w)+_coef$";

  39.     /** Default regular expression for EGM files. */
  40.     public static final String EGM_FILENAME = "^egm\\d\\d_to\\d.*$";

  41.     /** Default regular expression for GRGS files. */
  42.     public static final String GRGS_FILENAME = "^grim\\d_.*$";

  43.     /** Default regular expression for SHA files. */
  44.     public static final String SHA_FILENAME = "^sha\\..*$";

  45.     /** Default regular expression for FES Cnm, Snm tides files. */
  46.     public static final String FES_CNM_SNM_FILENAME = "^fes(\\d)+_Cnm-Snm.dat$";

  47.     /** Default regular expression for FES C hat and epsilon tides files. */
  48.     public static final String FES_CHAT_EPSILON_FILENAME = "^fes(\\d)+.dat$";

  49.     /** Default regular expression for FES Hf tides files. */
  50.     public static final String FES_HF_FILENAME = "^hf-fes(\\d)+.dat$";

  51.     /** Private constructor.
  52.      * <p>This class is a utility class, it should neither have a public
  53.      * nor a default constructor. This private constructor prevents
  54.      * the compiler from generating one automatically.</p>
  55.      */
  56.     private GravityFieldFactory() {
  57.     }

  58.     /* Data loading methods. */

  59.     /**
  60.      * Get the instance of {@link GravityFields} that is called by the static methods of
  61.      * this class.
  62.      *
  63.      * @return the gravity fields used by this factory.
  64.      * @since 10.1
  65.      */
  66.     @DefaultDataContext
  67.     public static LazyLoadedGravityFields getGravityFields() {
  68.         return DataContext.getDefault().getGravityFields();
  69.     }

  70.     /** Add a reader for gravity fields.
  71.      * @param reader custom reader to add for the gravity field
  72.      * @see #addDefaultPotentialCoefficientsReaders()
  73.      * @see #clearPotentialCoefficientsReaders()
  74.      */
  75.     @DefaultDataContext
  76.     public static void addPotentialCoefficientsReader(final PotentialCoefficientsReader reader) {
  77.         getGravityFields().addPotentialCoefficientsReader(reader);
  78.     }

  79.     /** Add the default readers for gravity fields.
  80.      * <p>
  81.      * The default READERS supports ICGEM, SHM, EGM, GRGS and SHA formats with the
  82.      * default names {@link #ICGEM_FILENAME}, {@link #SHM_FILENAME}, {@link
  83.      * #EGM_FILENAME}, {@link #GRGS_FILENAME}, {@link #SHA_FILENAME}
  84.      * and don't allow missing coefficients.
  85.      * </p>
  86.      * @see #addPotentialCoefficientsReader(PotentialCoefficientsReader)
  87.      * @see #clearPotentialCoefficientsReaders()
  88.      */
  89.     @DefaultDataContext
  90.     public static void addDefaultPotentialCoefficientsReaders() {
  91.         getGravityFields().addDefaultPotentialCoefficientsReaders();
  92.     }

  93.     /** Clear gravity field readers.
  94.      * @see #addPotentialCoefficientsReader(PotentialCoefficientsReader)
  95.      * @see #addDefaultPotentialCoefficientsReaders()
  96.      */
  97.     @DefaultDataContext
  98.     public static void clearPotentialCoefficientsReaders() {
  99.         getGravityFields().clearPotentialCoefficientsReaders();
  100.     }

  101.     /** Add a reader for ocean tides.
  102.      * @param reader custom reader to add for the gravity field
  103.      * @see #addDefaultPotentialCoefficientsReaders()
  104.      * @see #clearPotentialCoefficientsReaders()
  105.      */
  106.     @DefaultDataContext
  107.     public static void addOceanTidesReader(final OceanTidesReader reader) {
  108.         getGravityFields().addOceanTidesReader(reader);
  109.     }

  110.     /** Configure ocean load deformation coefficients.
  111.      * @param oldc ocean load deformation coefficients
  112.      * @see #getOceanLoadDeformationCoefficients()
  113.      */
  114.     @DefaultDataContext
  115.     public static void configureOceanLoadDeformationCoefficients(final OceanLoadDeformationCoefficients oldc) {
  116.         getGravityFields().configureOceanLoadDeformationCoefficients(oldc);
  117.     }

  118.     /** Get the configured ocean load deformation coefficients.
  119.      * <p>
  120.      * If {@link #configureOceanLoadDeformationCoefficients(OceanLoadDeformationCoefficients)
  121.      * configureOceanLoadDeformationCoefficients} has never been called, the default
  122.      * value will be the {@link OceanLoadDeformationCoefficients#IERS_2010 IERS 2010}
  123.      * coefficients.
  124.      * </p>
  125.      * @return ocean load deformation coefficients
  126.      * @see #configureOceanLoadDeformationCoefficients(OceanLoadDeformationCoefficients)
  127.      */
  128.     @DefaultDataContext
  129.     public static OceanLoadDeformationCoefficients getOceanLoadDeformationCoefficients() {
  130.         return getGravityFields().getOceanLoadDeformationCoefficients();
  131.     }

  132.     /** Add the default READERS for ocean tides.
  133.      * <p>
  134.      * The default READERS supports files similar to the fes2004_Cnm-Snm.dat and
  135.      * fes2004.dat as published by IERS, using the {@link
  136.      * #configureOceanLoadDeformationCoefficients(OceanLoadDeformationCoefficients)
  137.      * configured} ocean load deformation coefficients, which by default are the
  138.      * IERS 2010 coefficients, which are limited to degree 6. If higher degree
  139.      * coefficients are needed, the {@link
  140.      * #configureOceanLoadDeformationCoefficients(OceanLoadDeformationCoefficients)
  141.      * configureOceanLoadDeformationCoefficients} method can be called prior to
  142.      * loading the ocean tides model with the {@link
  143.      * OceanLoadDeformationCoefficients#GEGOUT high degree coefficients} computed
  144.      * by Pascal Gégout.
  145.      * </p>
  146.      * <p>
  147.      * WARNING: the files referenced in the published conventions have some errors.
  148.      * These errors have been corrected and the updated files can be found here:
  149.      * <a href="http://tai.bipm.org/iers/convupdt/convupdt_c6.html">
  150.      * http://tai.bipm.org/iers/convupdt/convupdt_c6.html</a>.
  151.      * </p>
  152.           * @see #addPotentialCoefficientsReader(PotentialCoefficientsReader)
  153.      * @see #clearPotentialCoefficientsReaders()
  154.      * @see #configureOceanLoadDeformationCoefficients(OceanLoadDeformationCoefficients)
  155.      * @see #getOceanLoadDeformationCoefficients()
  156.      */
  157.     @DefaultDataContext
  158.     public static void addDefaultOceanTidesReaders() {
  159.         getGravityFields().addDefaultOceanTidesReaders();
  160.     }

  161.     /** Clear ocean tides readers.
  162.      * @see #addPotentialCoefficientsReader(PotentialCoefficientsReader)
  163.      * @see #addDefaultPotentialCoefficientsReaders()
  164.      */
  165.     @DefaultDataContext
  166.     public static void clearOceanTidesReaders() {
  167.         getGravityFields().clearOceanTidesReaders();
  168.     }

  169.     /** Get the constant gravity field coefficients provider from the first supported file.
  170.      * <p>
  171.      * If no {@link PotentialCoefficientsReader} has been added by calling {@link
  172.      * #addPotentialCoefficientsReader(PotentialCoefficientsReader)
  173.      * addPotentialCoefficientsReader} or if {@link #clearPotentialCoefficientsReaders()
  174.      * clearPotentialCoefficientsReaders} has been called afterwards, the {@link
  175.      * #addDefaultPotentialCoefficientsReaders() addDefaultPotentialCoefficientsReaders}
  176.      * method will be called automatically.
  177.      * </p>
  178.      * @param degree maximal degree
  179.      * @param order maximal order
  180.      * @param freezingDate freezing epoch
  181.      * @return a gravity field coefficients provider containing already loaded data
  182.      * @since 12.0
  183.      * @see #getNormalizedProvider(int, int)
  184.      */
  185.     @DefaultDataContext
  186.     public static NormalizedSphericalHarmonicsProvider getConstantNormalizedProvider(final int degree, final int order,
  187.                                                                                      final AbsoluteDate freezingDate) {
  188.         return getGravityFields().getConstantNormalizedProvider(degree, order, freezingDate);
  189.     }

  190.     /** Get the gravity field coefficients provider from the first supported file.
  191.      * <p>
  192.      * If no {@link PotentialCoefficientsReader} has been added by calling {@link
  193.      * #addPotentialCoefficientsReader(PotentialCoefficientsReader)
  194.      * addPotentialCoefficientsReader} or if {@link #clearPotentialCoefficientsReaders()
  195.      * clearPotentialCoefficientsReaders} has been called afterwards, the {@link
  196.      * #addDefaultPotentialCoefficientsReaders() addDefaultPotentialCoefficientsReaders}
  197.      * method will be called automatically.
  198.      * </p>
  199.      * @param degree maximal degree
  200.      * @param order maximal order
  201.      * @return a gravity field coefficients provider containing already loaded data
  202.      * @since 6.0
  203.      * @see #getConstantNormalizedProvider(int, int, AbsoluteDate)
  204.      */
  205.     @DefaultDataContext
  206.     public static NormalizedSphericalHarmonicsProvider getNormalizedProvider(final int degree,
  207.                                                                              final int order) {
  208.         return getGravityFields().getNormalizedProvider(degree, order);
  209.     }

  210.     /** Get the constant gravity field coefficients provider from the first supported file.
  211.      * <p>
  212.      * If no {@link PotentialCoefficientsReader} has been added by calling {@link
  213.      * #addPotentialCoefficientsReader(PotentialCoefficientsReader)
  214.      * addPotentialCoefficientsReader} or if {@link #clearPotentialCoefficientsReaders()
  215.      * clearPotentialCoefficientsReaders} has been called afterwards, the {@link
  216.      * #addDefaultPotentialCoefficientsReaders() addDefaultPotentialCoefficientsReaders}
  217.      * method will be called automatically.
  218.      * </p>
  219.      * @param degree maximal degree
  220.      * @param order maximal order
  221.      * @param freezingDate freezing epoch
  222.      * @return a gravity field coefficients provider containing already loaded data
  223.      * @since 6.0
  224.      * @see #getUnnormalizedProvider(int, int)
  225.      */
  226.     @DefaultDataContext
  227.     public static UnnormalizedSphericalHarmonicsProvider getConstantUnnormalizedProvider(final int degree, final int order,
  228.                                                                                          final AbsoluteDate freezingDate) {
  229.         return getGravityFields().getConstantUnnormalizedProvider(degree, order, freezingDate);
  230.     }

  231.     /** Get the gravity field coefficients provider from the first supported file.
  232.      * <p>
  233.      * If no {@link PotentialCoefficientsReader} has been added by calling {@link
  234.      * #addPotentialCoefficientsReader(PotentialCoefficientsReader)
  235.      * addPotentialCoefficientsReader} or if {@link #clearPotentialCoefficientsReaders()
  236.      * clearPotentialCoefficientsReaders} has been called afterwards, the {@link
  237.      * #addDefaultPotentialCoefficientsReaders() addDefaultPotentialCoefficientsReaders}
  238.      * method will be called automatically.
  239.      * </p>
  240.      * @param degree maximal degree
  241.      * @param order maximal order
  242.      * @return a gravity field coefficients provider containing already loaded data
  243.      * @since 6.0
  244.      * @see #getConstantUnnormalizedProvider(int, int, AbsoluteDate)
  245.      */
  246.     @DefaultDataContext
  247.     public static UnnormalizedSphericalHarmonicsProvider getUnnormalizedProvider(final int degree,
  248.                                                                                  final int order) {
  249.         return getGravityFields().getUnnormalizedProvider(degree, order);
  250.     }

  251.     /** Read a gravity field coefficients provider from the first supported file.
  252.      * <p>
  253.      * If no {@link PotentialCoefficientsReader} has been added by calling {@link
  254.      * #addPotentialCoefficientsReader(PotentialCoefficientsReader)
  255.      * addPotentialCoefficientsReader} or if {@link #clearPotentialCoefficientsReaders()
  256.      * clearPotentialCoefficientsReaders} has been called afterwards, the {@link
  257.      * #addDefaultPotentialCoefficientsReaders() addDefaultPotentialCoefficientsReaders}
  258.      * method will be called automatically.
  259.      * </p>
  260.      * @param maxParseDegree maximal degree to parse
  261.      * @param maxParseOrder maximal order to parse
  262.      * @return a reader containing already loaded data
  263.      * @since 6.0
  264.      */
  265.     @DefaultDataContext
  266.     public static PotentialCoefficientsReader readGravityField(final int maxParseDegree,
  267.                                                                final int maxParseOrder) {
  268.         return getGravityFields().readGravityField(maxParseDegree, maxParseOrder);
  269.     }

  270.     /** Get the ocean tides waves from the first supported file.
  271.      * <p>
  272.      * If no {@link OceanTidesReader} has been added by calling {@link
  273.      * #addOceanTidesReader(OceanTidesReader)
  274.      * addOceanTidesReader} or if {@link #clearOceanTidesReaders()
  275.      * clearOceanTidesReaders} has been called afterwards, the {@link
  276.      * #addDefaultOceanTidesReaders() addDefaultOceanTidesReaders}
  277.      * method will be called automatically.
  278.      * </p>
  279.      * <p><span style="color:red">
  280.      * WARNING: as of 2013-11-17, there seem to be an inconsistency when loading
  281.      * one or the other file, for wave Sa (Doodson number 56.554) and P1 (Doodson
  282.      * number 163.555). The sign of the coefficients are different. We think the
  283.      * problem lies in the input files from IERS and not in the conversion (which
  284.      * works for all other waves), but cannot be sure. For this reason, ocean
  285.      * tides are still considered experimental at this date.
  286.      * </span></p>
  287.      * @param degree maximal degree
  288.      * @param order maximal order
  289.      * @return list of tides waves containing already loaded data
  290.      * @since 6.1
  291.      */
  292.     @DefaultDataContext
  293.     public static List<OceanTidesWave> getOceanTidesWaves(final int degree, final int order) {
  294.         return getGravityFields().getOceanTidesWaves(degree, order);
  295.     }

  296.     /* static helper methods that don't load data. */

  297.     /** Create a time-independent {@link NormalizedSphericalHarmonicsProvider} from canonical coefficients.
  298.      * <p>
  299.      * Note that contrary to the other factory method, this one does not read any data, it simply uses
  300.      * the provided data
  301.      * </p>
  302.      * @param ae central body reference radius
  303.      * @param mu central body attraction coefficient
  304.      * @param tideSystem tide system
  305.      * @param normalizedC normalized tesseral-sectorial coefficients (cosine part)
  306.      * @param normalizedS normalized tesseral-sectorial coefficients (sine part)
  307.      * @return provider for normalized coefficients
  308.      * @since 6.0
  309.      */
  310.     public static NormalizedSphericalHarmonicsProvider getNormalizedProvider(final double ae, final double mu,
  311.                                                                              final TideSystem tideSystem,
  312.                                                                              final double[][] normalizedC,
  313.                                                                              final double[][] normalizedS) {
  314.         final Flattener flattener = new Flattener(normalizedC.length - 1, normalizedC[normalizedC.length - 1].length - 1);
  315.         final RawSphericalHarmonicsProvider constant =
  316.                         new ConstantSphericalHarmonics(ae, mu, tideSystem, flattener,
  317.                                                        flattener.flatten(normalizedC), flattener.flatten(normalizedS));
  318.         return new WrappingNormalizedProvider(constant);
  319.     }

  320.     /** Create a {@link NormalizedSphericalHarmonicsProvider} from an {@link UnnormalizedSphericalHarmonicsProvider}.
  321.      * <p>
  322.      * Note that contrary to the other factory method, this one does not read any data, it simply uses
  323.      * the provided data.
  324.      * </p>
  325.      * @param unnormalized provider to normalize
  326.      * @return provider for normalized coefficients
  327.      * @since 6.0
  328.      */
  329.     public static NormalizedSphericalHarmonicsProvider getNormalizedProvider(final UnnormalizedSphericalHarmonicsProvider unnormalized) {
  330.         return new Normalizer(unnormalized);
  331.     }

  332.     /** Create a time-independent {@link UnnormalizedSphericalHarmonicsProvider} from canonical coefficients.
  333.      * <p>
  334.      * Note that contrary to the other factory method, this one does not read any data, it simply uses
  335.      * the provided data
  336.      * </p>
  337.      * @param ae central body reference radius
  338.      * @param mu central body attraction coefficient
  339.      * @param tideSystem tide system
  340.      * @param unnormalizedC un-normalized tesseral-sectorial coefficients (cosine part)
  341.      * @param unnormalizedS un-normalized tesseral-sectorial coefficients (sine part)
  342.      * @return provider for un-normalized coefficients
  343.      * @since 6.0
  344.      */
  345.     public static UnnormalizedSphericalHarmonicsProvider getUnnormalizedProvider(final double ae, final double mu,
  346.                                                                                  final TideSystem tideSystem,
  347.                                                                                  final double[][] unnormalizedC,
  348.                                                                                  final double[][] unnormalizedS) {
  349.         final Flattener flattener = new Flattener(unnormalizedC.length - 1, unnormalizedC[unnormalizedC.length - 1].length - 1);
  350.         final RawSphericalHarmonicsProvider constant =
  351.                         new ConstantSphericalHarmonics(ae, mu, tideSystem, flattener,
  352.                                                        flattener.flatten(unnormalizedC), flattener.flatten(unnormalizedS));
  353.         return new WrappingUnnormalizedProvider(constant);
  354.     }

  355.     /** Create an {@link UnnormalizedSphericalHarmonicsProvider} from a {@link NormalizedSphericalHarmonicsProvider}.
  356.      * <p>
  357.      * Note that contrary to the other factory method, this one does not read any data, it simply uses
  358.      * the provided data.
  359.      * </p>
  360.      * @param normalized provider to un-normalize
  361.      * @return provider for un-normalized coefficients
  362.      * @since 6.0
  363.      */
  364.     public static UnnormalizedSphericalHarmonicsProvider getUnnormalizedProvider(final NormalizedSphericalHarmonicsProvider normalized) {
  365.         return new Unnormalizer(normalized);
  366.     }

  367.     /** Get a un-normalization factors array.
  368.      * <p>
  369.      * Un-normalized coefficients are obtained by multiplying normalized
  370.      * coefficients by the factors array elements.
  371.      * </p>
  372.      * @param degree maximal degree
  373.      * @param order maximal order
  374.      * @return triangular un-normalization factors array
  375.      * @since 6.0
  376.      */
  377.     public static double[][] getUnnormalizationFactors(final int degree, final int order) {

  378.         // allocate a triangular array
  379.         final int rows = degree + 1;
  380.         final double[][] factor = new double[rows][];
  381.         factor[0] = new double[] {1.0};

  382.         // compute the factors
  383.         for (int n = 1; n <= degree; n++) {
  384.             final double[] row = new double[FastMath.min(n, order) + 1];
  385.             row[0] = FastMath.sqrt(2 * n + 1);
  386.             double coeff = 2.0 * (2 * n + 1);
  387.             for (int m = 1; m < row.length; m++) {
  388.                 coeff /= (n - m + 1) * (n + m);
  389.                 row[m] = FastMath.sqrt(coeff);
  390.                 if (row[m] < Precision.SAFE_MIN) {
  391.                     throw new OrekitException(OrekitMessages.GRAVITY_FIELD_NORMALIZATION_UNDERFLOW,
  392.                             n, m);
  393.                 }
  394.             }
  395.             factor[n] = row;
  396.         }

  397.         return factor;

  398.     }

  399. }