AbstractPropagatorConverter.java

  1. /* Copyright 2002-2013 CS Systèmes d'Information
  2.  * Licensed to CS Systèmes d'Information (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.propagation.conversion;

  18. import java.util.ArrayList;
  19. import java.util.Collection;
  20. import java.util.List;

  21. import org.apache.commons.math3.analysis.MultivariateMatrixFunction;
  22. import org.apache.commons.math3.analysis.MultivariateVectorFunction;
  23. import org.apache.commons.math3.exception.MaxCountExceededException;
  24. import org.apache.commons.math3.optim.InitialGuess;
  25. import org.apache.commons.math3.optim.MaxEval;
  26. import org.apache.commons.math3.optim.PointVectorValuePair;
  27. import org.apache.commons.math3.optim.SimpleVectorValueChecker;
  28. import org.apache.commons.math3.optim.nonlinear.vector.ModelFunction;
  29. import org.apache.commons.math3.optim.nonlinear.vector.ModelFunctionJacobian;
  30. import org.apache.commons.math3.optim.nonlinear.vector.Target;
  31. import org.apache.commons.math3.optim.nonlinear.vector.Weight;
  32. import org.apache.commons.math3.optim.nonlinear.vector.jacobian.LevenbergMarquardtOptimizer;
  33. import org.apache.commons.math3.util.FastMath;
  34. import org.orekit.errors.OrekitException;
  35. import org.orekit.errors.OrekitExceptionWrapper;
  36. import org.orekit.errors.OrekitMessages;
  37. import org.orekit.frames.Frame;
  38. import org.orekit.propagation.Propagator;
  39. import org.orekit.propagation.SpacecraftState;
  40. import org.orekit.time.AbsoluteDate;
  41. import org.orekit.utils.PVCoordinates;

  42. /** Common handling of {@link PropagatorConverter} methods for propagators conversions.
  43.  * <p>
  44.  * This abstract class factorizes the common code for propagators conversion.
  45.  * Only one method must be implemented by derived classes: {@link #getObjectiveFunction()}.
  46.  * </p>
  47.  * <p>
  48.  * The converter uses the LevenbergMarquardtOptimizer from the <a
  49.  * href="http://commons.apache.org/math/">commons math</a> library.
  50.  * Different implementations correspond to different methods for computing the jacobian.
  51.  * </p>
  52.  * @author Pascal Parraud
  53.  * @since 6.0
  54.  */
  55. public abstract class AbstractPropagatorConverter implements PropagatorConverter {

  56.     /** Spacecraft states sample. */
  57.     private List<SpacecraftState> sample;

  58.     /** Reference date for conversion (1st date of the states sample). */
  59.     private AbsoluteDate date;

  60.     /** Target position and velocities at sample points. */
  61.     private double[] target;

  62.     /** Weight for residuals. */
  63.     private double[] weight;

  64.     /** Auxiliary outputData: RMS of solution. */
  65.     private double rms;

  66.     /** Position use indicator. */
  67.     private boolean onlyPosition;

  68.     /** Adapted propagator. */
  69.     private Propagator adapted;

  70.     /** List of the desired free parameters names. */
  71.     private Collection<String> parameters;

  72.     /** List of the available free parameters names. */
  73.     private final Collection<String> availableParameters;

  74.     /** Propagator builder. */
  75.     private final PropagatorBuilder builder;

  76.     /** Frame. */
  77.     private final Frame frame;

  78.     /** Optimizer for fitting. */
  79.     private final LevenbergMarquardtOptimizer optimizer;

  80.     /** Maximum number of iterations for optimization. */
  81.     private final int maxIterations;

  82.     /** Build a new instance.
  83.      * @param builder propagator builder
  84.      * @param threshold absolute convergence threshold for optimization algorithm
  85.      * @param maxIterations maximum number of iterations for fitting
  86.      */
  87.     protected AbstractPropagatorConverter(final PropagatorBuilder builder,
  88.                                           final double threshold,
  89.                                           final int maxIterations) {
  90.         this.builder             = builder;
  91.         this.frame               = builder.getFrame();
  92.         this.availableParameters = builder.getParametersNames();
  93.         this.optimizer           = new LevenbergMarquardtOptimizer(new SimpleVectorValueChecker(-1.0, threshold));
  94.         this.maxIterations       = maxIterations;
  95.         this.sample              = new ArrayList<SpacecraftState>();
  96.     }

  97.     /** Convert a propagator to another.
  98.      * @param source initial propagator
  99.      * @param timeSpan time span for fitting
  100.      * @param nbPoints number of fitting points over time span
  101.      * @param freeParameters names of the free parameters
  102.      * @return adapted propagator
  103.      * @exception OrekitException if propagator cannot be adapted
  104.      * @exception IllegalArgumentException if one of the parameters cannot be free
  105.      */
  106.     public Propagator convert(final Propagator source,
  107.                               final double timeSpan,
  108.                               final int nbPoints,
  109.                               final Collection<String> freeParameters) throws OrekitException {

  110.         checkParameters(freeParameters);
  111.         final List<SpacecraftState> states = createSample(source, timeSpan, nbPoints);
  112.         return convert(states, false, freeParameters);
  113.     }

  114.     /** Convert a propagator to another.
  115.      * @param source initial propagator
  116.      * @param timeSpan time span for fitting
  117.      * @param nbPoints number of fitting points over time span
  118.      * @param freeParameters names of the free parameters
  119.      * @return adapted propagator
  120.      * @exception OrekitException if propagator cannot be adapted
  121.      * @exception IllegalArgumentException if one of the parameters cannot be free
  122.      */
  123.     public Propagator convert(final Propagator source,
  124.                               final double timeSpan,
  125.                               final int nbPoints,
  126.                               final String ... freeParameters) throws OrekitException {

  127.         checkParameters(freeParameters);
  128.         final List<SpacecraftState> states = createSample(source, timeSpan, nbPoints);
  129.         return convert(states, false, freeParameters);
  130.     }

  131.     /** Find the propagator that minimize the mean square error for a sample of {@link SpacecraftState states}.
  132.      * @param states spacecraft states sample to fit
  133.      * @param positionOnly if true, consider only position data otherwise both position and velocity are used
  134.      * @param freeParameters names of the free parameters
  135.      * @return adapted propagator
  136.      * @exception OrekitException if propagator cannot be adapted
  137.      * @exception IllegalArgumentException if one of the parameters cannot be free
  138.      */
  139.     public Propagator convert(final List<SpacecraftState> states,
  140.                               final boolean positionOnly,
  141.                               final Collection<String> freeParameters) throws OrekitException {

  142.         checkParameters(freeParameters);

  143.         parameters = new ArrayList<String>();
  144.         parameters.addAll(freeParameters);

  145.         builder.setFreeParameters(parameters);

  146.         return adapt(states, positionOnly);
  147.     }

  148.     /** Find the propagator that minimize the mean square error for a sample of {@link SpacecraftState states}.
  149.      * @param states spacecraft states sample to fit
  150.      * @param positionOnly if true, consider only position data otherwise both position and velocity are used
  151.      * @param freeParameters names of the free parameters
  152.      * @return adapted propagator
  153.      * @exception OrekitException if propagator cannot be adapted
  154.      * @exception IllegalArgumentException if one of the parameters cannot be free
  155.      */
  156.     public Propagator convert(final List<SpacecraftState> states,
  157.                               final boolean positionOnly,
  158.                               final String ... freeParameters) throws OrekitException {

  159.         checkParameters(freeParameters);

  160.         parameters = new ArrayList<String>();
  161.         for (final String name : freeParameters) {
  162.             parameters.add(name);
  163.         }

  164.         builder.setFreeParameters(parameters);

  165.         return adapt(states, positionOnly);
  166.     }

  167.     /** Get the available free parameters.
  168.      * @return available free parameters
  169.      */
  170.     public Collection<String> getAvailableParameters() {
  171.         return availableParameters;
  172.     }

  173.     /** Check if a parameter can be free.
  174.      * @param name parameter name to check
  175.      * @return true if the parameter can be free
  176.      */
  177.     public boolean isAvailable(final String name) {
  178.         for (final String supportedName : availableParameters) {
  179.             if (supportedName.equals(name)) {
  180.                 return true;
  181.             }
  182.         }
  183.         return false;
  184.     }

  185.     /** Get the adapted propagator.
  186.      * @return adapted propagator
  187.      */
  188.     public Propagator getAdaptedPropagator() {
  189.         return adapted;
  190.     }

  191.     /** Get the Root Mean Square Deviation of the fitting.
  192.      * @return RMSD
  193.      */
  194.     public double getRMS() {
  195.         return rms;
  196.     }

  197.     /** Get the number of objective function evaluations.
  198.      *  @return the number of objective function evaluations.
  199.      */
  200.     public int getEvaluations() {
  201.         return optimizer.getEvaluations();
  202.     }

  203.     /** Get the function computing position/velocity at sample points.
  204.      * @return function computing position/velocity at sample points
  205.      */
  206.     protected abstract MultivariateVectorFunction getObjectiveFunction();

  207.     /** Get the Jacobian of the function computing position/velocity at sample points.
  208.      * @return Jacobian of the function computing position/velocity at sample points
  209.      */
  210.     protected abstract MultivariateMatrixFunction getObjectiveFunctionJacobian();

  211.     /** Check if fitting uses only sample positions.
  212.      * @return true if fitting uses only sample positions
  213.      */
  214.     protected boolean isOnlyPosition() {
  215.         return onlyPosition;
  216.     }

  217.     /** Get the size of the target.
  218.      * @return target size
  219.      */
  220.     protected int getTargetSize() {
  221.         return target.length;
  222.     }

  223.     /** Get the date of the initial state.
  224.      * @return the date
  225.      */
  226.     protected AbsoluteDate getDate() {
  227.         return date;
  228.     }

  229.     /** Get the frame of the initial state.
  230.      * @return the orbit frame
  231.      */
  232.     protected Frame getFrame() {
  233.         return frame;
  234.     }

  235.     /** Get the states sample.
  236.      * @return the states sample
  237.      */
  238.     protected List<SpacecraftState> getSample() {
  239.         return sample;
  240.     }

  241.     /** Get the free parameters.
  242.      * @return the free parameters
  243.      */
  244.     protected Collection<String>  getFreeParameters() {
  245.         return parameters;
  246.     }

  247.     /** Create a sample of {@link SpacecraftState}.
  248.      * @param source initial propagator
  249.      * @param timeSpan time span for the sample
  250.      * @param nbPoints number of points for the sample over the time span
  251.      * @return a sample of {@link SpacecraftState}
  252.      * @exception OrekitException if one of the sample point cannot be computed
  253.      */
  254.     private List<SpacecraftState> createSample(final Propagator source,
  255.                                                final double timeSpan,
  256.                                                final int nbPoints) throws OrekitException {

  257.         final SpacecraftState initialState = source.getInitialState();

  258.         final List<SpacecraftState> states = new ArrayList<SpacecraftState>();

  259.         final double stepSize = timeSpan / (nbPoints - 1);
  260.         final AbsoluteDate iniDate = source.getInitialState().getDate();
  261.         for (double dt = 0; dt < timeSpan; dt += stepSize) {
  262.             states.add(source.propagate(iniDate.shiftedBy(dt)));
  263.         }

  264.         source.resetInitialState(initialState);

  265.         return states;
  266.     }

  267.     /** Check if parameters can be free.
  268.      * @param freeParameters names of the free parameters
  269.      * @exception OrekitException if one of the parameters cannot be free
  270.      */
  271.     private void checkParameters(final Collection<String> freeParameters) throws OrekitException {
  272.         for (String parameter : freeParameters) {
  273.             checkParameter(parameter);
  274.         }
  275.     }

  276.     /** Check if parameters can be free.
  277.      * @param freeParameters names of the free parameters
  278.      * @exception OrekitException if one of the parameters cannot be free
  279.      */
  280.     private void checkParameters(final String ... freeParameters) throws OrekitException {
  281.         for (String parameter : freeParameters) {
  282.             checkParameter(parameter);
  283.         }
  284.     }

  285.     /** Check if parameter can be free.
  286.      * @param parameter name of the free parameter
  287.      * @exception OrekitException if the parameter cannot be free
  288.      */
  289.     private void checkParameter(final String parameter) throws OrekitException {

  290.         if ( !availableParameters.contains(parameter) ) {

  291.             // build the list of supported parameters
  292.             final StringBuilder sBuilder = new StringBuilder();
  293.             for (final String available : availableParameters) {
  294.                 if (sBuilder.length() > 0) {
  295.                     sBuilder.append(", ");
  296.                 }
  297.                 sBuilder.append(available);
  298.             }

  299.             throw new OrekitException(OrekitMessages.UNSUPPORTED_PARAMETER_NAME,
  300.                                       parameter, sBuilder.toString());

  301.         }

  302.     }

  303.     /** Adapt a propagator to minimize the mean square error for a set of {@link SpacecraftState states}.
  304.      * @param states set of spacecraft states to fit
  305.      * @param positionOnly if true, consider only position data otherwise both position and velocity are used
  306.      * @return adapted propagator
  307.      * @exception OrekitException if propagator cannot be adapted
  308.      */
  309.     private Propagator adapt(final List<SpacecraftState> states,
  310.                              final boolean positionOnly) throws OrekitException {

  311.         this.date = states.get(0).getDate();
  312.         this.onlyPosition = positionOnly;

  313.         // very rough first guess using osculating parameters of first sample point
  314.         final double[] initial = new double[6 + parameters.size()];
  315.         final PVCoordinates pv = states.get(0).getPVCoordinates(frame);
  316.         initial[0] = pv.getPosition().getX();
  317.         initial[1] = pv.getPosition().getY();
  318.         initial[2] = pv.getPosition().getZ();
  319.         initial[3] = pv.getVelocity().getX();
  320.         initial[4] = pv.getVelocity().getY();
  321.         initial[5] = pv.getVelocity().getZ();
  322.         int i = 6;
  323.         for (String name : parameters) {
  324.             initial[i++] = builder.getParameter(name);
  325.         }

  326.         // warm-up iterations, using only a few points
  327.         setSample(states.subList(0, onlyPosition ? 2 : 1));
  328.         final double[] intermediate = fit(initial);

  329.         // final search using all points
  330.         setSample(states);
  331.         final double[] result = fit(intermediate);

  332.         rms = getRMS(result);
  333.         adapted = buildAdaptedPropagator(result);

  334.         return adapted;
  335.     }

  336.     /** Find the propagator that minimize the mean square error for a sample of {@link SpacecraftState states}.
  337.      * @param initial initial estimation parameters (position, velocity, free parameters)
  338.      * @return fitted parameters
  339.      * @exception OrekitException if propagator cannot be adapted
  340.      * @exception MaxCountExceededException if maximal number of iterations is exceeded
  341.      */
  342.     private double[] fit(final double[] initial)
  343.         throws OrekitException, MaxCountExceededException {

  344.         final PointVectorValuePair optimum = optimizer.optimize(new MaxEval(maxIterations),
  345.                                                                 new ModelFunction(getObjectiveFunction()),
  346.                                                                 new ModelFunctionJacobian(getObjectiveFunctionJacobian()),
  347.                                                                 new Target(target),
  348.                                                                 new Weight(weight),
  349.                                                                 new InitialGuess(initial));
  350.         return optimum.getPoint();
  351.     }

  352.     /** Get the Root Mean Square Deviation for a given parameters set.
  353.      * @param parameterSet position/velocity parameters set
  354.      * @return RMSD
  355.      * @exception OrekitException if position/velocity cannot be computed at some date
  356.      */
  357.     private double getRMS(final double[] parameterSet) throws OrekitException {

  358.         final double[] residuals = getResiduals(parameterSet);
  359.         double sum2 = 0;
  360.         for (final double residual : residuals) {
  361.             sum2 += residual * residual;
  362.         }
  363.         return FastMath.sqrt(sum2 / residuals.length);

  364.     }

  365.     /** Get the residuals for a given position/velocity parameters set.
  366.      * @param parameterSet position/velocity parameters set
  367.      * @return residuals
  368.      * @exception OrekitException if position/velocity cannot be computed at some date
  369.      */
  370.     private double[] getResiduals(final double[] parameterSet) throws OrekitException {
  371.         try {
  372.             final double[] residuals = getObjectiveFunction().value(parameterSet);
  373.             for (int i = 0; i < residuals.length; ++i) {
  374.                 residuals[i] = target[i] - residuals[i];
  375.             }
  376.             return residuals;
  377.         } catch (OrekitExceptionWrapper oew) {
  378.             throw oew.getException();
  379.         }
  380.     }

  381.     /** Build the adpated propagator for a given position/velocity(/free) parameters set.
  382.      * @param parameterSet position/velocity(/free) parameters set
  383.      * @return adapted propagator
  384.      * @exception OrekitException if propagator cannot be build
  385.      */
  386.     private Propagator buildAdaptedPropagator(final double[] parameterSet)
  387.         throws OrekitException {
  388.         return builder.buildPropagator(date, parameterSet);
  389.     }

  390.     /** Set the states sample.
  391.      * @param states spacecraft states sample
  392.      * @exception OrekitException if position/velocity cannot be extracted from sample
  393.      */
  394.     private void setSample(final List<SpacecraftState> states) throws OrekitException {

  395.         this.sample = states;

  396.         if (onlyPosition) {
  397.             target = new double[states.size() * 3];
  398.             weight = new double[states.size() * 3];
  399.         } else {
  400.             target = new double[states.size() * 6];
  401.             weight = new double[states.size() * 6];
  402.         }

  403.         int k = 0;
  404.         for (int i = 0; i < states.size(); i++) {

  405.             final PVCoordinates pv = states.get(i).getPVCoordinates(frame);

  406.             // position
  407.             target[k]   = pv.getPosition().getX();
  408.             weight[k++] = 1;
  409.             target[k]   = pv.getPosition().getY();
  410.             weight[k++] = 1;
  411.             target[k]   = pv.getPosition().getZ();
  412.             weight[k++] = 1;

  413.             // velocity
  414.             if (!onlyPosition) {
  415.                 // velocity weight relative to position
  416.                 final double r2 = pv.getPosition().getNormSq();
  417.                 final double v  = pv.getVelocity().getNorm();
  418.                 final double vWeight = v * r2 / states.get(i).getMu();

  419.                 target[k]   = pv.getVelocity().getX();
  420.                 weight[k++] = vWeight;
  421.                 target[k]   = pv.getVelocity().getY();
  422.                 weight[k++] = vWeight;
  423.                 target[k]   = pv.getVelocity().getZ();
  424.                 weight[k++] = vWeight;
  425.             }

  426.         }

  427.     }

  428. }