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

  18. import java.util.List;

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

  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 FES Cnm, Snm tides files. */
  44.     public static final String FES_CNM_SNM_FILENAME = "^fes(\\d)+_Cnm-Snm.dat$";

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

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

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

  56.     /* Data loading methods. */

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

  394.         return factor;

  395.     }

  396. }