TimeSpanParametricAcceleration.java

  1. /* Copyright 2002-2020 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.empirical;

  18. import java.util.ArrayList;
  19. import java.util.List;
  20. import java.util.NavigableSet;
  21. import java.util.stream.Stream;

  22. import org.hipparchus.Field;
  23. import org.hipparchus.RealFieldElement;
  24. import org.hipparchus.geometry.euclidean.threed.FieldVector3D;
  25. import org.hipparchus.geometry.euclidean.threed.Vector3D;
  26. import org.hipparchus.util.MathArrays;
  27. import org.orekit.attitudes.Attitude;
  28. import org.orekit.attitudes.AttitudeProvider;
  29. import org.orekit.attitudes.FieldAttitude;
  30. import org.orekit.forces.AbstractForceModel;
  31. import org.orekit.propagation.FieldSpacecraftState;
  32. import org.orekit.propagation.SpacecraftState;
  33. import org.orekit.propagation.events.EventDetector;
  34. import org.orekit.propagation.events.FieldEventDetector;
  35. import org.orekit.time.AbsoluteDate;
  36. import org.orekit.time.FieldAbsoluteDate;
  37. import org.orekit.utils.ParameterDriver;
  38. import org.orekit.utils.TimeSpanMap;
  39. import org.orekit.utils.TimeSpanMap.Span;
  40. import org.orekit.utils.TimeSpanMap.Transition;

  41. /** Time span parametric acceleration model.
  42.  *  <p>
  43.  *  This class is closely related to {@link org.orekit.forces.empirical.ParametricAcceleration ParametricAcceleration} class.<br>
  44.  *  The difference is that it has a {@link TimeSpanMap} of {@link AccelerationModel} objects as attribute
  45.  *  instead of a single {@link AccelerationModel} object. <br>
  46.  *  The idea behind this model is to allow the user to design a parametric acceleration model that can see its physical parameters
  47.  *  change with time, at dates chosen by the user. <br>
  48.  *  </p>
  49.  *  <p>
  50.  *  This is a behavior that can be sought in precise orbit determination.<br>
  51.  *  Indeed for this type of application, the empirical parameters must be revalued at
  52.  *  each new orbit.
  53.  *  </p>
  54.  *  <b>Usage</b>:<ul>
  55.  *  <li><u>Construction</u>: constructor takes an acceleration direction, an attitude mode (or an inertial flag) and
  56.  *  an AccelerationModel model.<br>
  57.  *  This last model will be your initial AccelerationModel model and it will be initially valid for the whole time line.<br>
  58.  *  The real validity of this first entry will be truncated as other AccelerationModel models are added.
  59.  *  <li><u>Time spans</u>: AccelerationModel models are added using methods {@link #addAccelerationModelValidAfter(AccelerationModel, AbsoluteDate)}
  60.  *   or {@link #addAccelerationModelValidBefore(AccelerationModel, AbsoluteDate)}.<br>
  61.  *   Recommendations are the same than the ones in {@link TimeSpanMap}, meaning: <ul>
  62.  *   <li>As an entry is added, it truncates the validity of the neighboring entries already present in the map;
  63.  *   <li><b>The transition dates should be entered only once</b>. Repeating a transition date will lead to unexpected result and is not supported;
  64.  *   <li>It is advised to order your AccelerationModel models chronologically when adding them to avoid any confusion.
  65.  *   </ul>
  66.  *   <li><u>Naming the parameter drivers</u>: It is strongly advised to give a custom name to the {@link ParameterDriver}(s)
  67.  *   of each AccelerationModel model that is added to the object. This will allow you keeping track of the evolution of your models.<br>
  68.  *   Different names are mandatory to differentiate the different drivers.<br>
  69.  *   Since there is no default name for acceleration model parameters, you must handle the driver names to consider
  70.  *   different names when adding a new acceleration model.
  71.  *   </ul>
  72.  * @author Bryan Cazabonne
  73.  * @since 10.3
  74.  */
  75. public class TimeSpanParametricAcceleration extends AbstractForceModel {

  76.     /** Prefix for dates before in the parameter drivers' name. */
  77.     public static final String DATE_BEFORE = " - Before ";

  78.     /** Prefix for dates after in the parameter drivers' name. */
  79.     public static final String DATE_AFTER = " - After ";

  80.     /** Direction of the acceleration in defining frame. */
  81.     private final Vector3D direction;

  82.     /** Flag for inertial acceleration direction. */
  83.     private final boolean isInertial;

  84.     /** The attitude to override, if set. */
  85.     private final AttitudeProvider attitudeOverride;

  86.     /** TimeSpanMap of AccelerationModel objects. */
  87.     private final TimeSpanMap<AccelerationModel> accelerationModelTimeSpanMap;

  88.     /** Simple constructor.
  89.      * @param direction acceleration direction in overridden spacecraft frame
  90.      * @param isInertial if true, direction is defined in the same inertial
  91.      * frame used for propagation (i.e. {@link SpacecraftState#getFrame()}),
  92.      * otherwise direction is defined in spacecraft frame (i.e. using the
  93.      * propagation {@link
  94.      * org.orekit.propagation.Propagator#setAttitudeProvider(AttitudeProvider)
  95.      * attitude law})
  96.      * @param accelerationModel acceleration model used to compute the contribution of the empirical acceleration
  97.      */
  98.     public TimeSpanParametricAcceleration(final Vector3D direction,
  99.                                           final boolean isInertial,
  100.                                           final AccelerationModel accelerationModel) {
  101.         this(direction, isInertial, null, accelerationModel);
  102.     }

  103.     /** Simple constructor.
  104.      * @param direction acceleration direction in overridden spacecraft frame
  105.      * frame used for propagation (i.e. {@link SpacecraftState#getFrame()}),
  106.      * otherwise direction is defined in spacecraft frame (i.e. using the
  107.      * propagation {@link
  108.      * org.orekit.propagation.Propagator#setAttitudeProvider(AttitudeProvider)
  109.      * attitude law})
  110.      * @param attitudeOverride provider for attitude used to compute acceleration
  111.      * @param accelerationModel acceleration model used to compute the contribution of the empirical acceleration
  112.      */
  113.     public TimeSpanParametricAcceleration(final Vector3D direction,
  114.                                           final AttitudeProvider attitudeOverride,
  115.                                           final AccelerationModel accelerationModel) {
  116.         this(direction, false, attitudeOverride, accelerationModel);
  117.     }

  118.     /** Simple constructor.
  119.      * @param direction acceleration direction in overridden spacecraft frame
  120.      * @param isInertial if true, direction is defined in the same inertial
  121.      * frame used for propagation (i.e. {@link SpacecraftState#getFrame()}),
  122.      * otherwise direction is defined in spacecraft frame (i.e. using the
  123.      * propagation {@link
  124.      * org.orekit.propagation.Propagator#setAttitudeProvider(AttitudeProvider)
  125.      * attitude law})
  126.      * @param attitudeOverride provider for attitude used to compute acceleration
  127.      * @param accelerationModel acceleration model used to compute the contribution of the empirical acceleration
  128.      */
  129.     private TimeSpanParametricAcceleration(final Vector3D direction,
  130.                                            final boolean isInertial,
  131.                                            final AttitudeProvider attitudeOverride,
  132.                                            final AccelerationModel accelerationModel) {
  133.         this.direction                    = direction;
  134.         this.isInertial                   = isInertial;
  135.         this.attitudeOverride             = attitudeOverride;
  136.         this.accelerationModelTimeSpanMap = new TimeSpanMap<>(accelerationModel);
  137.     }

  138.     /** {@inheritDoc} */
  139.     @Override
  140.     public void init(final SpacecraftState initialState, final AbsoluteDate target) {
  141.         accelerationModelTimeSpanMap.forEach(accelerationModel -> accelerationModel.init(initialState, target));
  142.     }

  143.     /** Add an AccelerationModel entry valid before a limit date.<br>
  144.      * <p>
  145.      * Using <code>addAccelerationModelValidBefore(entry, t)</code> will make <code>entry</code>
  146.      * valid in ]-∞, t[ (note the open bracket).
  147.      * <p>
  148.      * <b>WARNING</b>: Since there is no default name for acceleration model parameters,
  149.      * the user must handle itself the driver names to consider different names
  150.      * (i.e. different parameters) when adding a new acceleration model.
  151.      * @param accelerationModel AccelerationModel entry
  152.      * @param latestValidityDate date before which the entry is valid
  153.      * (must be different from <b>all</b> dates already used for transitions)
  154.      */
  155.     public void addAccelerationModelValidBefore(final AccelerationModel accelerationModel, final AbsoluteDate latestValidityDate) {
  156.         accelerationModelTimeSpanMap.addValidBefore(accelerationModel, latestValidityDate);
  157.     }

  158.     /** Add a AccelerationModel entry valid after a limit date.<br>
  159.      * <p>
  160.      * Using <code>addAccelerationModelValidAfter(entry, t)</code> will make <code>entry</code>
  161.      * valid in [t, +∞[ (note the closed bracket).
  162.      * <p>
  163.      * <b>WARNING</b>: Since there is no default name for acceleration model parameters,
  164.      * the user must handle itself the driver names to consider different names
  165.      * (i.e. different parameters) when adding a new acceleration model.
  166.      * @param accelerationModel AccelerationModel entry
  167.      * @param earliestValidityDate date after which the entry is valid
  168.      * (must be different from <b>all</b> dates already used for transitions)
  169.      */
  170.     public void addAccelerationModelValidAfter(final AccelerationModel accelerationModel, final AbsoluteDate earliestValidityDate) {
  171.         accelerationModelTimeSpanMap.addValidAfter(accelerationModel, earliestValidityDate);
  172.     }

  173.     /** Get the {@link AccelerationModel} model valid at a date.
  174.      * @param date the date of validity
  175.      * @return the AccelerationModel model valid at date
  176.      */
  177.     public AccelerationModel getAccelerationModel(final AbsoluteDate date) {
  178.         return accelerationModelTimeSpanMap.get(date);
  179.     }

  180.     /** Get the {@link AccelerationModel} {@link Span} containing a specified date.
  181.      * @param date date belonging to the desired time span
  182.      * @return the AccelerationModel time span containing the specified date
  183.      */
  184.     public Span<AccelerationModel> getAccelerationModelSpan(final AbsoluteDate date) {
  185.         return accelerationModelTimeSpanMap.getSpan(date);
  186.     }

  187.     /** Extract a range of the {@link AccelerationModel} map.
  188.      * <p>
  189.      * The object returned will be a new independent instance that will contain
  190.      * only the transitions that lie in the specified range.
  191.      * </p>
  192.      * See the {@link TimeSpanMap#extractRange TimeSpanMap.extractRange method} for more.
  193.      * @param start earliest date at which a transition is included in the range
  194.      * (may be set to {@link AbsoluteDate#PAST_INFINITY} to keep all early transitions)
  195.      * @param end latest date at which a transition is included in the r
  196.      * (may be set to {@link AbsoluteDate#FUTURE_INFINITY} to keep all late transitions)
  197.      * @return a new TimeSpanMap instance of AccelerationModel with all transitions restricted to the specified range
  198.      */
  199.     public TimeSpanMap<AccelerationModel> extractAccelerationModelRange(final AbsoluteDate start, final AbsoluteDate end) {
  200.         return accelerationModelTimeSpanMap.extractRange(start, end);
  201.     }

  202.     /** Get the {@link Transition}s of the acceleration model time span map.
  203.      * @return the {@link Transition}s for the acceleration model time span map
  204.      */
  205.     public NavigableSet<Transition<AccelerationModel>> getTransitions() {
  206.         return accelerationModelTimeSpanMap.getTransitions();
  207.     }

  208.     /** {@inheritDoc} */
  209.     @Override
  210.     public boolean dependsOnPositionOnly() {
  211.         return isInertial;
  212.     }

  213.     /** {@inheritDoc} */
  214.     @Override
  215.     public Vector3D acceleration(final SpacecraftState state,
  216.                                  final double[] parameters) {

  217.         // Date
  218.         final AbsoluteDate date = state.getDate();

  219.         // Compute inertial direction
  220.         final Vector3D inertialDirection;
  221.         if (isInertial) {
  222.             // the acceleration direction is already defined in the inertial frame
  223.             inertialDirection = direction;
  224.         } else {
  225.             final Attitude attitude;
  226.             if (attitudeOverride == null) {
  227.                 // the acceleration direction is defined in spacecraft frame as set by the propagator
  228.                 attitude = state.getAttitude();
  229.             } else {
  230.                 // the acceleration direction is defined in a dedicated frame
  231.                 attitude = attitudeOverride.getAttitude(state.getOrbit(), date, state.getFrame());
  232.             }
  233.             inertialDirection = attitude.getRotation().applyInverseTo(direction);
  234.         }

  235.         // Extract the proper parameters valid at date from the input array
  236.         final double[] extractedParameters = extractParameters(parameters, date);

  237.         // Compute and return the parametric acceleration
  238.         return new Vector3D(getAccelerationModel(date).signedAmplitude(state, extractedParameters), inertialDirection);

  239.     }

  240.     /** {@inheritDoc} */
  241.     @Override
  242.     public <T extends RealFieldElement<T>> FieldVector3D<T> acceleration(final FieldSpacecraftState<T> state,
  243.                                                                          final T[] parameters) {

  244.         // Date
  245.         final FieldAbsoluteDate<T> date = state.getDate();

  246.         // Compute inertial direction
  247.         final FieldVector3D<T> inertialDirection;
  248.         if (isInertial) {
  249.             // the acceleration direction is already defined in the inertial frame
  250.             inertialDirection = new FieldVector3D<>(state.getDate().getField(), direction);
  251.         } else {
  252.             final FieldAttitude<T> attitude;
  253.             if (attitudeOverride == null) {
  254.                 // the acceleration direction is defined in spacecraft frame as set by the propagator
  255.                 attitude = state.getAttitude();
  256.             } else {
  257.                 // the acceleration direction is defined in a dedicated frame
  258.                 attitude = attitudeOverride.getAttitude(state.getOrbit(), date, state.getFrame());
  259.             }
  260.             inertialDirection = attitude.getRotation().applyInverseTo(direction);
  261.         }

  262.         // Extract the proper parameters valid at date from the input array
  263.         final T[] extractedParameters = extractParameters(parameters, date);

  264.         // Compute and return the parametric acceleration
  265.         return new FieldVector3D<>(getAccelerationModel(date.toAbsoluteDate()).signedAmplitude(state, extractedParameters), inertialDirection);

  266.     }

  267.     /** {@inheritDoc} */
  268.     @Override
  269.     public Stream<EventDetector> getEventsDetectors() {
  270.         return Stream.empty();
  271.     }

  272.     /** {@inheritDoc} */
  273.     @Override
  274.     public <T extends RealFieldElement<T>> Stream<FieldEventDetector<T>> getFieldEventsDetectors(final Field<T> field) {
  275.         return Stream.empty();
  276.     }

  277.     /** {@inheritDoc}
  278.      * <p>
  279.      * All the parameter drivers of all AccelerationModel models are returned in an array.
  280.      * Models are ordered chronologically.
  281.      * </p>
  282.      */
  283.     @Override
  284.     public ParameterDriver[] getParametersDrivers() {

  285.         // Get all transitions from the TimeSpanMap
  286.         final List<ParameterDriver> listParameterDrivers = new ArrayList<>();
  287.         final NavigableSet<Transition<AccelerationModel>> accelerationModelTransitions =  getTransitions();

  288.         // Loop on the transitions
  289.         for (Transition<AccelerationModel> transition : accelerationModelTransitions) {
  290.             // Add all the "before" parameter drivers of each transition
  291.             for (ParameterDriver driver : transition.getBefore().getParametersDrivers()) {
  292.                 // Add the driver only if the name does not exist already
  293.                 if (!findByName(listParameterDrivers, driver.getName())) {
  294.                     listParameterDrivers.add(driver);
  295.                 }
  296.             }
  297.         }

  298.         // Finally, add the "after" parameter drivers of the last transition
  299.         for (ParameterDriver driver : accelerationModelTransitions.last().getAfter().getParametersDrivers()) {
  300.             // Adds only if the name does not exist already
  301.             if (!findByName(listParameterDrivers, driver.getName())) {
  302.                 listParameterDrivers.add(driver);
  303.             }
  304.         }

  305.         // Return an array of parameter drivers with no duplicated name
  306.         return listParameterDrivers.toArray(new ParameterDriver[0]);

  307.     }

  308.     /** Extract the proper parameter drivers' values from the array in input of the
  309.      * {@link #acceleration(SpacecraftState, double[]) acceleration} method.
  310.      *  Parameters are filtered given an input date.
  311.      * @param parameters the input parameters array
  312.      * @param date the date
  313.      * @return the parameters given the date
  314.      */
  315.     public double[] extractParameters(final double[] parameters, final AbsoluteDate date) {

  316.         // Get the acceleration model parameter drivers of the date
  317.         final ParameterDriver[] empiricalParameterDriver = getAccelerationModel(date).getParametersDrivers();

  318.         // Find out the indexes of the parameters in the whole array of parameters
  319.         final ParameterDriver[] allParameters = getParametersDrivers();
  320.         final double[] outParameters = new double[empiricalParameterDriver.length];
  321.         int index = 0;
  322.         for (int i = 0; i < allParameters.length; i++) {
  323.             final String driverName = allParameters[i].getName();
  324.             for (ParameterDriver accDriver : empiricalParameterDriver) {
  325.                 if (accDriver.getName().equals(driverName)) {
  326.                     outParameters[index++] = parameters[i];
  327.                 }
  328.             }
  329.         }
  330.         return outParameters;
  331.     }

  332.     /** Extract the proper parameter drivers' values from the array in input of the
  333.      * {@link #acceleration(FieldSpacecraftState, RealFieldElement[]) acceleration} method.
  334.      *  Parameters are filtered given an input date.
  335.      * @param parameters the input parameters array
  336.      * @param date the date
  337.      * @param <T> extends RealFieldElement
  338.      * @return the parameters given the date
  339.      */
  340.     public <T extends RealFieldElement<T>> T[] extractParameters(final T[] parameters,
  341.                                                                  final FieldAbsoluteDate<T> date) {

  342.         // Get the acceleration parameter drivers of the date
  343.         final ParameterDriver[] empiricalParameterDriver = getAccelerationModel(date.toAbsoluteDate()).getParametersDrivers();

  344.         // Find out the indexes of the parameters in the whole array of parameters
  345.         final ParameterDriver[] allParameters = getParametersDrivers();
  346.         final T[] outParameters = MathArrays.buildArray(date.getField(), empiricalParameterDriver.length);
  347.         int index = 0;
  348.         for (int i = 0; i < allParameters.length; i++) {
  349.             final String driverName = allParameters[i].getName();
  350.             for (ParameterDriver accDriver : empiricalParameterDriver) {
  351.                 if (accDriver.getName().equals(driverName)) {
  352.                     outParameters[index++] = parameters[i];
  353.                 }
  354.             }
  355.         }
  356.         return outParameters;
  357.     }

  358.     /** Find if a parameter driver with a given name already exists in a list of parameter drivers.
  359.      * @param driversList the list of parameter drivers
  360.      * @param name the parameter driver's name to filter with
  361.      * @return true if the name was found, false otherwise
  362.      */
  363.     private boolean findByName(final List<ParameterDriver> driversList, final String name) {
  364.         for (final ParameterDriver d : driversList) {
  365.             if (d.getName().equals(name)) {
  366.                 return true;
  367.             }
  368.         }
  369.         return false;
  370.     }

  371. }