TimeSpanDragForce.java

  1. /* Copyright 2002-2022 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.drag;

  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.CalculusFieldElement;
  23. import org.hipparchus.Field;
  24. import org.hipparchus.analysis.differentiation.DerivativeStructure;
  25. import org.hipparchus.analysis.differentiation.Gradient;
  26. import org.hipparchus.geometry.euclidean.threed.FieldVector3D;
  27. import org.hipparchus.geometry.euclidean.threed.Vector3D;
  28. import org.hipparchus.ode.events.Action;
  29. import org.hipparchus.util.MathArrays;
  30. import org.orekit.annotation.DefaultDataContext;
  31. import org.orekit.frames.Frame;
  32. import org.orekit.models.earth.atmosphere.Atmosphere;
  33. import org.orekit.propagation.FieldSpacecraftState;
  34. import org.orekit.propagation.SpacecraftState;
  35. import org.orekit.propagation.events.DateDetector;
  36. import org.orekit.propagation.events.EventDetector;
  37. import org.orekit.propagation.events.FieldDateDetector;
  38. import org.orekit.propagation.events.FieldEventDetector;
  39. import org.orekit.time.AbsoluteDate;
  40. import org.orekit.time.FieldAbsoluteDate;
  41. import org.orekit.time.TimeScale;
  42. import org.orekit.time.TimeScalesFactory;
  43. import org.orekit.utils.ParameterDriver;
  44. import org.orekit.utils.TimeSpanMap;
  45. import org.orekit.utils.TimeSpanMap.Span;
  46. import org.orekit.utils.TimeSpanMap.Transition;


  47. /** Time span atmospheric drag force model.
  48.  *  <p>
  49.  *  This class is closely related to {@link org.orekit.forces.drag.DragForce DragForce} class.<br>
  50.  *  The difference is that it has a {@link TimeSpanMap} of {@link DragSensitive} objects as attribute
  51.  *  instead of a single {@link DragSensitive} object. <br>
  52.  *  The idea behind this model is to allow the user to design a drag force model that can see its physical parameters
  53.  *  (drag coefficient and lift ratio) change with time, at dates chosen by the user. <br>
  54.  *  </p>
  55.  *  <p>
  56.  *  This is a behavior that can be sought in operational orbit determination.<br>
  57.  *  Indeed the solar activity has a strong influence on the local atmospheric density, and thus on the drag force effect.<br>
  58.  *  Solar activity is a physical phenomenon that is difficult to model and predict. <br>
  59.  *  The errors induced by this incomplete modeling can be estimated through the drag coefficients.<br>
  60.  *  Being able to define and estimate drag coefficients depending on user-chosen dates in a piecewise fashion allows for
  61.  *  a better  modeling of solar activity uncertainties.
  62.  *  </p>
  63.  *  <p>
  64.  *  A typical operational use case is to have a daily solar activity with three-hourly magnetic indexes provided by an
  65.  *  international organization (NOAA for example).<br>
  66.  *  Given this input, a user can define a piecewise drag force model with daily or three-hourly drag coefficients.<br>
  67.  *  Each timed coefficient will absorb a part of the uncertainties in the solar activity and will allow for a more accurate
  68.  *  orbit determination
  69.  *  </p>
  70.  *  <b>Usage</b>:<ul>
  71.  *  <li><u>Construction</u>: constructor takes an atmospheric model and a DragSensitive model.<br>
  72.  *  This last model will be your initial DragSensitive model and it will be initially valid for the whole time line.<br>
  73.  *  The real validity of this first entry will be truncated as other DragSensitive models are added.
  74.  *  <li><u>Time spans</u>: DragSensitive models are added using methods {@link #addDragSensitiveValidAfter(DragSensitive, AbsoluteDate)}
  75.  *   or {@link #addDragSensitiveValidBefore(DragSensitive, AbsoluteDate)}.<br>
  76.  *   Recommendations are the same than the ones in {@link TimeSpanMap}, meaning: <ul>
  77.  *   <li>As an entry is added, it truncates the validity of the neighboring entries already present in the map;
  78.  *   <li><b>The transition dates should be entered only once</b>. Repeating a transition date will lead to unexpected result and is not supported;
  79.  *   <li>It is advised to order your DragSensitive models chronologically when adding them to avoid any confusion.
  80.  *   </ul>
  81.  *   <li><u>Naming the parameter drivers</u>: It is strongly advised to give a custom name to the {@link ParameterDriver}(s)
  82.  *   of each DragSensitive model that is added to the object. This will allow you keeping track of the evolution of your models.<br>
  83.  *   Different names are mandatory to differentiate the different drivers.<br>
  84.  *   If you do not specify a name, a default name will be chosen. Example for the drag coefficient:<ul>
  85.  *   <li>Initial DragSensitive model: the driver's default name is "{@link DragSensitive#DRAG_COEFFICIENT}";
  86.  *   <li>Using {@link #addDragSensitiveValidAfter(DragSensitive, AbsoluteDate)}: the driver's default name is
  87.  *   "{@link DragSensitive#DRAG_COEFFICIENT} + {@link #DATE_AFTER} + date.toString()"
  88.  *   <li>Using {@link #addDragSensitiveValidBefore(DragSensitive, AbsoluteDate)}: the driver's default name is
  89.  *   "{@link DragSensitive#DRAG_COEFFICIENT} + {@link #DATE_BEFORE} + date.toString()"
  90.  *   </ul>
  91.  *   </ul>
  92.  *  <b>Example following previous recommendations</b>:<ul>
  93.  *  <li>Given:
  94.  *  <ul>
  95.  *  <li><code>atmosphere</code>: an {@link Atmosphere atmospheric model};
  96.  *  <li><code>isotropicDrag0, 1 and 2</code>: three {@link org.orekit.forces.drag.IsotropicDrag IsotropicDrag} models;
  97.  *  <li><code>date</code>: an {@link AbsoluteDate}.
  98.  *  </ul>
  99.  *  <li>Name the drivers:<br>
  100.  *  <code>isotropicDrag0.getDragParametersDrivers()[0].setName = "Cd0";</code><br>
  101.  *  <code>isotropicDrag1.getDragParametersDrivers()[0].setName = "Cd1";</code><br>
  102.  *  <code>isotropicDrag2.getDragParametersDrivers()[0].setName = "Cd2";</code><br>
  103.  *  <li>Initialize the model: <br>
  104.  *  <code>TimeSpanDragForce force = new TimeSpanDragForce(atmosphere, isotropicDrag0);</code>
  105.  *  <li>Set the second and third model one Julian day apart each:<br>
  106.  *  <code>force.addDragSensitiveValidAfter(isotropicDrag1, date.shiftedBy(Constants.JULIAN_DAY));</code><br>
  107.  *  <code>force.addDragSensitiveValidAfter(isotropicDrag2, date.shiftedBy(2 * Constants.JULIAN_DAY));</code><br>
  108.  *  <li>With this, your model will have the following properties:
  109.  *  <ul>
  110.  *  <li>t in ]-∞, date + 1 day [ / Cd = Cd0
  111.  *  <li>t in [date + 1 day, date + 2days [ / Cd = Cd1
  112.  *  <li>t in [date + 2 days, +∞ [ / Cd = Cd2
  113.  *  </ul>
  114.  *  </ul>
  115.  *  <p>
  116.  *  <b>Warning</b>:<br> The TimeSpanDragForce model is versatile and you could end up with non-physical modeling.<br>
  117.  *  For example you could add 2 {@link org.orekit.forces.drag.IsotropicDrag IsotropicDrag} models with different areas,
  118.  *  or one {@link org.orekit.forces.drag.IsotropicDrag IsotropicDrag} model and then one
  119.  *  {@link org.orekit.forces.BoxAndSolarArraySpacecraft BoxAndSolarArraySpacecraft} model.<br>
  120.  *  It is up to you to ensure that your models are consistent with each other, Orekit will not perform any check for that.
  121.  *  </p>
  122.  * @author Maxime Journot
  123.  * @since 10.2
  124.  */
  125. public class TimeSpanDragForce extends AbstractDragForceModel {

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

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

  130.     /** Atmospheric model. */
  131.     private final Atmosphere atmosphere;

  132.     /** TimeSpanMap of DragSensitive objects. */
  133.     private final TimeSpanMap<DragSensitive> dragSensitiveTimeSpanMap;

  134.     /** Time scale used for the default names of the drag parameter drivers. */
  135.     private final TimeScale timeScale;

  136.     /** Constructor with default UTC time scale for the default names of the drag parameter drivers.
  137.      * @param atmosphere atmospheric model
  138.      * @param spacecraft Time scale used for the default names of the drag parameter drivers
  139.      */
  140.     @DefaultDataContext
  141.     public TimeSpanDragForce(final Atmosphere atmosphere,
  142.                              final DragSensitive spacecraft) {
  143.         super(atmosphere);
  144.         this.atmosphere = atmosphere;
  145.         this.dragSensitiveTimeSpanMap = new TimeSpanMap<>(spacecraft);
  146.         this.timeScale = TimeScalesFactory.getUTC();
  147.     }

  148.     /** Constructor.
  149.      * @param atmosphere atmospheric model
  150.      * @param spacecraft the initial object physical and geometric information
  151.      * @param timeScale Time scale used for the default names of the drag parameter drivers
  152.      */
  153.     public TimeSpanDragForce(final Atmosphere atmosphere,
  154.                              final DragSensitive spacecraft,
  155.                              final TimeScale timeScale) {
  156.         super(atmosphere);
  157.         this.atmosphere = atmosphere;
  158.         this.dragSensitiveTimeSpanMap = new TimeSpanMap<>(spacecraft);
  159.         this.timeScale = timeScale;
  160.     }

  161.     /** Add a DragSensitive entry valid before a limit date.<br>
  162.      * Using <code>addDragSensitiveValidBefore(entry, t)</code> will make <code>entry</code>
  163.      * valid in ]-∞, t[ (note the open bracket).
  164.      * @param dragSensitive DragSensitive entry
  165.      * @param latestValidityDate date before which the entry is valid
  166.      * (must be different from <b>all</b> dates already used for transitions)
  167.      */
  168.     public void addDragSensitiveValidBefore(final DragSensitive dragSensitive, final AbsoluteDate latestValidityDate) {
  169.         dragSensitiveTimeSpanMap.addValidBefore(changeDragParameterDriversNames(dragSensitive,
  170.                                                                                 latestValidityDate,
  171.                                                                                 DATE_BEFORE),
  172.                                                 latestValidityDate, false);
  173.     }

  174.     /** Add a DragSensitive entry valid after a limit date.<br>
  175.      * Using <code>addDragSensitiveValidAfter(entry, t)</code> will make <code>entry</code>
  176.      * valid in [t, +∞[ (note the closed bracket).
  177.      * @param dragSensitive DragSensitive entry
  178.      * @param earliestValidityDate date after which the entry is valid
  179.      * (must be different from <b>all</b> dates already used for transitions)
  180.      */
  181.     public void addDragSensitiveValidAfter(final DragSensitive dragSensitive, final AbsoluteDate earliestValidityDate) {
  182.         dragSensitiveTimeSpanMap.addValidAfter(changeDragParameterDriversNames(dragSensitive,
  183.                                                                                earliestValidityDate,
  184.                                                                                DATE_AFTER),
  185.                                                earliestValidityDate, false);
  186.     }

  187.     /** Get the {@link DragSensitive} model valid at a date.
  188.      * @param date the date of validity
  189.      * @return the DragSensitive model valid at date
  190.      */
  191.     public DragSensitive getDragSensitive(final AbsoluteDate date) {
  192.         return dragSensitiveTimeSpanMap.get(date);
  193.     }

  194.     /** Get the {@link DragSensitive} {@link Span} containing a specified date.
  195.      * @param date date belonging to the desired time span
  196.      * @return the DragSensitive time span containing the specified date
  197.      */
  198.     public Span<DragSensitive> getDragSensitiveSpan(final AbsoluteDate date) {
  199.         return dragSensitiveTimeSpanMap.getSpan(date);
  200.     }

  201.     /** Extract a range of the {@link DragSensitive} map.
  202.      * <p>
  203.      * The object returned will be a new independent instance that will contain
  204.      * only the transitions that lie in the specified range.
  205.      * </p>
  206.      * See the {@link TimeSpanMap#extractRange TimeSpanMap.extractRange method} for more.
  207.      * @param start earliest date at which a transition is included in the range
  208.      * (may be set to {@link AbsoluteDate#PAST_INFINITY} to keep all early transitions)
  209.      * @param end latest date at which a transition is included in the r
  210.      * (may be set to {@link AbsoluteDate#FUTURE_INFINITY} to keep all late transitions)
  211.      * @return a new TimeSpanMap instance of DragSensitive with all transitions restricted to the specified range
  212.      */
  213.     public TimeSpanMap<DragSensitive> extractDragSensitiveRange(final AbsoluteDate start, final AbsoluteDate end) {
  214.         return dragSensitiveTimeSpanMap.extractRange(start, end);
  215.     }

  216.     /** Get the {@link Transition}s of the drag sensitive time span map.
  217.      * @return the {@link Transition}s for the drag sensitive time span map
  218.      * @deprecated as of 11.1, replaced by {@link #getFirstSpan()}
  219.      */
  220.     @Deprecated
  221.     public NavigableSet<Transition<DragSensitive>> getTransitions() {
  222.         return dragSensitiveTimeSpanMap.getTransitions();
  223.     }

  224.     /** Get the first {@link Span time span} of the drag sensitive time span map.
  225.      * @return the first {@link Span time span} of the drag sensitive time span map
  226.      * @since 11.1
  227.      */
  228.     public Span<DragSensitive> getFirstSpan() {
  229.         return dragSensitiveTimeSpanMap.getFirstSpan();
  230.     }

  231.     /** {@inheritDoc} */
  232.     @Override
  233.     public Vector3D acceleration(final SpacecraftState s, final double[] parameters) {

  234.         // Local atmospheric density
  235.         final AbsoluteDate date     = s.getDate();
  236.         final Frame        frame    = s.getFrame();
  237.         final Vector3D     position = s.getPVCoordinates().getPosition();
  238.         final double rho    = atmosphere.getDensity(date, position, frame);

  239.         // Spacecraft relative velocity with respect to the atmosphere
  240.         final Vector3D vAtm = atmosphere.getVelocity(date, position, frame);
  241.         final Vector3D relativeVelocity = vAtm.subtract(s.getPVCoordinates().getVelocity());

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

  244.         // Compute and return drag acceleration
  245.         return getDragSensitive(date).dragAcceleration(date, frame, position, s.getAttitude().getRotation(),
  246.                                                        s.getMass(), rho, relativeVelocity, extractedParameters);

  247.     }

  248.     /** {@inheritDoc} */
  249.     @SuppressWarnings("unchecked")
  250.     @Override
  251.     public <T extends CalculusFieldElement<T>> FieldVector3D<T> acceleration(final FieldSpacecraftState<T> s,
  252.                                                                          final T[] parameters) {
  253.         // Local atmospheric density
  254.         final FieldAbsoluteDate<T> date     = s.getDate();
  255.         final Frame                frame    = s.getFrame();
  256.         final FieldVector3D<T>     position = s.getPVCoordinates().getPosition();

  257.         // Density and its derivatives
  258.         final T rho;

  259.         // Check for faster computation dedicated to derivatives with respect to state
  260.         // Using finite differences instead of automatic differentiation as it seems to be much
  261.         // faster for the drag's derivatives' computation
  262.         if (isGradientStateDerivative(s)) {
  263.             rho = (T) this.getGradientDensityWrtStateUsingFiniteDifferences(date.toAbsoluteDate(), frame, (FieldVector3D<Gradient>) position);
  264.         } else if (isDSStateDerivative(s)) {
  265.             rho = (T) this.getDSDensityWrtStateUsingFiniteDifferences(date.toAbsoluteDate(), frame, (FieldVector3D<DerivativeStructure>) position);
  266.         } else {
  267.             rho = atmosphere.getDensity(date, position, frame);
  268.         }

  269.         // Spacecraft relative velocity with respect to the atmosphere
  270.         final FieldVector3D<T> vAtm = atmosphere.getVelocity(date, position, frame);
  271.         final FieldVector3D<T> relativeVelocity = vAtm.subtract(s.getPVCoordinates().getVelocity());

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

  274.         // Compute and return drag acceleration
  275.         return getDragSensitive(date.toAbsoluteDate()).dragAcceleration(date, frame, position, s.getAttitude().getRotation(),
  276.                                                                         s.getMass(), rho, relativeVelocity, extractedParameters);
  277.     }

  278.     /**{@inheritDoc}
  279.      * <p>
  280.      * A date detector is used to cleanly stop the propagator and reset
  281.      * the state derivatives at transition dates.
  282.      * </p>
  283.      */
  284.     @Override
  285.     public Stream<EventDetector> getEventsDetectors() {

  286.         // Get the transitions' dates from the TimeSpanMap
  287.         final AbsoluteDate[] transitionDates = getTransitionDates();

  288.         // Initialize the date detector
  289.         final DateDetector datesDetector = new DateDetector(transitionDates[0]).
  290.                         withMaxCheck(60.).
  291.                         withHandler((SpacecraftState state, DateDetector d, boolean increasing) -> {
  292.                             return Action.RESET_DERIVATIVES;
  293.                         });
  294.         // Add all transitions' dates to the date detector
  295.         for (int i = 1; i < transitionDates.length; i++) {
  296.             datesDetector.addEventDate(transitionDates[i]);
  297.         }

  298.         // Return the detector
  299.         return Stream.of(datesDetector);
  300.     }

  301.     /** {@inheritDoc}
  302.      * <p>
  303.      * A date detector is used to cleanly stop the propagator and reset
  304.      * the state derivatives at transition dates.
  305.      * </p>
  306.      */
  307.     @Override
  308.     public <T extends CalculusFieldElement<T>> Stream<FieldEventDetector<T>> getFieldEventsDetectors(final Field<T> field) {

  309.         // Get the transitions' dates from the TimeSpanMap
  310.         final AbsoluteDate[] transitionDates = getTransitionDates();

  311.         // Initialize the date detector
  312.         final FieldDateDetector<T> datesDetector =
  313.                         new FieldDateDetector<>(new FieldAbsoluteDate<>(field, transitionDates[0])).
  314.                         withMaxCheck(field.getZero().add(60.)).
  315.                         withHandler((FieldSpacecraftState<T> state, FieldDateDetector<T> d, boolean increasing) -> {
  316.                             return Action.RESET_DERIVATIVES;
  317.                         });
  318.         // Add all transitions' dates to the date detector
  319.         for (int i = 1; i < transitionDates.length; i++) {
  320.             datesDetector.addEventDate(new FieldAbsoluteDate<>(field, transitionDates[i]));
  321.         }

  322.         // Return the detector
  323.         return Stream.of(datesDetector);
  324.     }

  325.     /** {@inheritDoc}
  326.      * <p>
  327.      * All the parameter drivers of all DragSensitive models are returned in an array.
  328.      * Models are ordered chronologically.
  329.      * </p>
  330.      */
  331.     @Override
  332.     public List<ParameterDriver> getParametersDrivers() {

  333.         // Get all transitions from the TimeSpanMap
  334.         final List<ParameterDriver> listParameterDrivers = new ArrayList<>();

  335.         // Loop on the spans
  336.         for (Span<DragSensitive> span = getFirstSpan(); span != null; span = span.next()) {
  337.             // Add all the parameter drivers of the span
  338.             for (ParameterDriver driver : span.getData().getDragParametersDrivers()) {
  339.                 // Add the driver only if the name does not exist already
  340.                 if (!findByName(listParameterDrivers, driver.getName())) {
  341.                     listParameterDrivers.add(driver);
  342.                 }
  343.             }
  344.         }

  345.         // Return an array of parameter drivers with no duplicated name
  346.         return listParameterDrivers;

  347.     }

  348.     /** Extract the proper parameter drivers' values from the array in input of the
  349.      * {@link #acceleration(SpacecraftState, double[]) acceleration} method.
  350.      *  Parameters are filtered given an input date.
  351.      * @param parameters the input parameters array
  352.      * @param date the date
  353.      * @return the parameters given the date
  354.      */
  355.     public double[] extractParameters(final double[] parameters, final AbsoluteDate date) {

  356.         // Get the drag parameter drivers of the date
  357.         final List<ParameterDriver> dragParameterDriver = getDragSensitive(date).getDragParametersDrivers();

  358.         // Find out the indexes of the parameters in the whole array of parameters
  359.         final List<ParameterDriver> allParameters = getParametersDrivers();
  360.         final double[] outParameters = new double[dragParameterDriver.size()];
  361.         int index = 0;
  362.         for (int i = 0; i < allParameters.size(); i++) {
  363.             final String driverName = allParameters.get(i).getName();
  364.             for (ParameterDriver dragDriver : dragParameterDriver) {
  365.                 if (dragDriver.getName().equals(driverName)) {
  366.                     outParameters[index++] = parameters[i];
  367.                 }
  368.             }
  369.         }
  370.         return outParameters;
  371.     }

  372.     /** Extract the proper parameter drivers' values from the array in input of the
  373.      * {@link #acceleration(FieldSpacecraftState, CalculusFieldElement[]) acceleration} method.
  374.      *  Parameters are filtered given an input date.
  375.      * @param parameters the input parameters array
  376.      * @param date the date
  377.      * @param <T> extends CalculusFieldElement
  378.      * @return the parameters given the date
  379.      */
  380.     public <T extends CalculusFieldElement<T>> T[] extractParameters(final T[] parameters,
  381.                                                                  final FieldAbsoluteDate<T> date) {

  382.         // Get the drag parameter drivers of the date
  383.         final List<ParameterDriver> dragPD = getDragSensitive(date.toAbsoluteDate()).getDragParametersDrivers();

  384.         // Find out the indexes of the parameters in the whole array of parameters
  385.         final List<ParameterDriver> allParameters = getParametersDrivers();
  386.         final T[] outParameters = MathArrays.buildArray(date.getField(), dragPD.size());
  387.         int index = 0;
  388.         for (int i = 0; i < allParameters.size(); i++) {
  389.             final String driverName = allParameters.get(i).getName();
  390.             for (ParameterDriver dragDriver : dragPD) {
  391.                 if (dragDriver.getName().equals(driverName)) {
  392.                     outParameters[index++] = parameters[i];
  393.                 }
  394.             }
  395.         }
  396.         return outParameters;
  397.     }

  398.     /** Find if a parameter driver with a given name already exists in a list of parameter drivers.
  399.      * @param driversList the list of parameter drivers
  400.      * @param name the parameter driver's name to filter with
  401.      * @return true if the name was found, false otherwise
  402.      */
  403.     private boolean findByName(final List<ParameterDriver> driversList, final String name) {
  404.         for (final ParameterDriver d : driversList) {
  405.             if (d.getName().equals(name)) {
  406.                 return true;
  407.             }
  408.         }
  409.         return false;
  410.     }

  411.     /** Get the dates of the transitions for the drag sensitive models {@link TimeSpanMap}.
  412.      * @return dates of the transitions for the drag sensitive models {@link TimeSpanMap}
  413.      */
  414.     private AbsoluteDate[] getTransitionDates() {

  415.         // Get all transitions
  416.         final List<AbsoluteDate> listDates = new ArrayList<>();

  417.         // Extract all the transitions' dates
  418.         for (Transition<DragSensitive> transition = getFirstSpan().getEndTransition(); transition != null; transition = transition.next()) {
  419.             listDates.add(transition.getDate());
  420.         }
  421.         // Return the array of transition dates
  422.         return listDates.toArray(new AbsoluteDate[0]);
  423.     }

  424.     /** Change the parameter drivers names of a {@link DragSensitive} model, if needed.
  425.      * <p>
  426.      * This is done to avoid that several parameter drivers have the same name.<br>
  427.      * It is done only if the user hasn't modify the DragSensitive parameter drivers default names.
  428.      * </p>
  429.      * @param dragSensitive the DragSensitive model
  430.      * @param date the date used in the parameter driver's name
  431.      * @param datePrefix the date prefix used in the parameter driver's name
  432.      * @return the DragSensitive with its drivers' names changed
  433.      */
  434.     private DragSensitive changeDragParameterDriversNames(final DragSensitive dragSensitive,
  435.                                                           final AbsoluteDate date,
  436.                                                           final String datePrefix) {
  437.         // Loop on the parameter drivers of the DragSensitive model
  438.         for (ParameterDriver driver: dragSensitive.getDragParametersDrivers()) {
  439.             final String driverName = driver.getName();

  440.             // If the name is the default name for DragSensitive parameter drivers
  441.             // Modify the name to add the prefix and the date
  442.             if (driverName.equals(DragSensitive.DRAG_COEFFICIENT) || driverName.equals(DragSensitive.LIFT_RATIO)) {
  443.                 driver.setName(driverName + datePrefix + date.toString(timeScale));
  444.             }
  445.         }
  446.         return dragSensitive;
  447.     }

  448. }