AbstractMultipleShooting.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.utils;

  18. import java.util.ArrayList;
  19. import java.util.Arrays;
  20. import java.util.HashMap;
  21. import java.util.List;
  22. import java.util.Map;

  23. import org.hipparchus.geometry.euclidean.threed.Vector3D;
  24. import org.hipparchus.linear.MatrixUtils;
  25. import org.hipparchus.linear.RealMatrix;
  26. import org.hipparchus.linear.RealVector;
  27. import org.orekit.attitudes.Attitude;
  28. import org.orekit.attitudes.AttitudeProvider;
  29. import org.orekit.errors.OrekitException;
  30. import org.orekit.errors.OrekitMessages;
  31. import org.orekit.propagation.SpacecraftState;
  32. import org.orekit.propagation.numerical.NumericalPropagator;
  33. import org.orekit.time.AbsoluteDate;

  34. /**
  35.  * Multiple shooting method using only constraints on state vectors of patch points (and possibly on epoch and integration time).
  36.  * @see "TRAJECTORY DESIGN AND ORBIT MAINTENANCE STRATEGIES IN MULTI-BODY DYNAMICAL REGIMES by Thomas A. Pavlak, Purdue University"
  37.  * @author William Desprats
  38.  * @since 10.2
  39.  */
  40. public abstract class AbstractMultipleShooting implements MultipleShooting {

  41.     /** Patch points along the trajectory. */
  42.     private List<SpacecraftState> patchedSpacecraftStates;

  43.     /** Derivatives linked to the Propagators.
  44.      * @deprecated as of 11.1 not used anymore
  45.      */
  46.     @Deprecated
  47.     private List<org.orekit.propagation.integration.AdditionalEquations> additionalEquations;

  48.     /** List of Propagators. */
  49.     private final List<NumericalPropagator> propagatorList;

  50.     /** Duration of propagation along each arcs. */
  51.     private final double[] propagationTime;

  52.     /** Free components of patch points. */
  53.     private final boolean[] freePatchPointMap;

  54.     /** Free epoch of patch points. */
  55.     private final boolean[] freeEpochMap;

  56.     /** Number of free variables. */
  57.     private int nFree;

  58.     /** Number of free epoch. */
  59.     private int nEpoch;

  60.     /** Number of constraints. */
  61.     private int nConstraints;

  62.     /** Patch points components which are constrained. */
  63.     private final Map<Integer, Double> mapConstraints;

  64.     /** True if orbit is closed. */
  65.     private boolean isClosedOrbit;

  66.     /** Tolerance on the constraint vector. */
  67.     private final double tolerance;

  68.     /** Maximum number of iterations. */
  69.     private final int maxIter;

  70.     /** Expected name of the additional equations. */
  71.     private final String additionalName;

  72.     /** Simple Constructor.
  73.      * <p> Standard constructor for multiple shooting </p>
  74.      * @param initialGuessList initial patch points to be corrected.
  75.      * @param propagatorList list of propagators associated to each patch point.
  76.      * @param additionalEquations list of additional equations linked to propagatorList.
  77.      * @param arcDuration initial guess of the duration of each arc.
  78.      * @param tolerance convergence tolerance on the constraint vector.
  79.      * @param additionalName name of the additional equations
  80.      * @deprecated as of 11.1, replaced by {@link #AbstractMultipleShooting(List, List, double, double, int, String)}
  81.      */
  82.     @Deprecated
  83.     protected AbstractMultipleShooting(final List<SpacecraftState> initialGuessList, final List<NumericalPropagator> propagatorList,
  84.                                        final List<org.orekit.propagation.integration.AdditionalEquations> additionalEquations,
  85.                                        final double arcDuration, final double tolerance, final String additionalName) {
  86.         this(initialGuessList, propagatorList, arcDuration, tolerance, 1, additionalName);
  87.         this.additionalEquations = additionalEquations;
  88.     }

  89.     /** Simple Constructor.
  90.      * <p> Standard constructor for multiple shooting </p>
  91.      * @param initialGuessList initial patch points to be corrected.
  92.      * @param propagatorList list of propagators associated to each patch point.
  93.      * @param arcDuration initial guess of the duration of each arc.
  94.      * @param tolerance convergence tolerance on the constraint vector.
  95.      * @param maxIter maximum number of iterations
  96.      * @param additionalName name of the additional equations
  97.      * @since 11.1
  98.      */
  99.     protected AbstractMultipleShooting(final List<SpacecraftState> initialGuessList, final List<NumericalPropagator> propagatorList,
  100.                                        final double arcDuration, final double tolerance, final int maxIter, final String additionalName) {
  101.         this.patchedSpacecraftStates = initialGuessList;
  102.         this.propagatorList = propagatorList;
  103.         this.additionalName = additionalName;
  104.         // Should check if propagatorList.size() = initialGuessList.size() - 1
  105.         final int propagationNumber = initialGuessList.size() - 1;
  106.         propagationTime = new double[propagationNumber];
  107.         Arrays.fill(propagationTime, arcDuration);

  108.         // All the patch points are set initially as free variables
  109.         this.freePatchPointMap = new boolean[6 * initialGuessList.size()]; // epoch
  110.         Arrays.fill(freePatchPointMap, true);

  111.         //Except the first one, the epochs of the patch points are set free.
  112.         this.freeEpochMap = new boolean[initialGuessList.size()];
  113.         Arrays.fill(freeEpochMap, true);
  114.         freeEpochMap[0] = false;
  115.         this.nEpoch = initialGuessList.size() - 1;

  116.         this.nConstraints = 6 * propagationNumber;
  117.         this.nFree = 6 * initialGuessList.size() + 1;

  118.         this.tolerance = tolerance;
  119.         this.maxIter   = maxIter;

  120.         // All the additional constraints must be set afterward
  121.         this.mapConstraints = new HashMap<>();
  122.     }

  123.     /** Get a patch point.
  124.      * @param i index of the patch point
  125.      * @return state of the patch point
  126.      * @since 11.1
  127.      */
  128.     protected SpacecraftState getPatchPoint(final int i) {
  129.         return patchedSpacecraftStates.get(i);
  130.     }

  131.     /** Set a component of a patch point to free or not.
  132.      * @param patchNumber Patch point with constraint
  133.      * @param componentIndex Component of the patch points which are constrained.
  134.      * @param isFree constraint value
  135.      */
  136.     public void setPatchPointComponentFreedom(final int patchNumber, final int componentIndex, final boolean isFree) {
  137.         if (freePatchPointMap[6 * (patchNumber - 1) +  componentIndex] != isFree) {
  138.             nFree += isFree ? 1 : -1;
  139.             freePatchPointMap[6 * (patchNumber - 1) +  componentIndex] = isFree;
  140.         }
  141.     }

  142.     /** Add a constraint on one component of one patch point.
  143.      * @param patchNumber Patch point with constraint
  144.      * @param componentIndex Component of the patch points which are constrained.
  145.      * @param constraintValue constraint value
  146.      */
  147.     public void addConstraint(final int patchNumber, final int componentIndex, final double constraintValue) {
  148.         final int contraintIndex = (patchNumber - 1) * 6 + componentIndex;
  149.         if (!mapConstraints.containsKey(contraintIndex)) {
  150.             nConstraints++;
  151.         }
  152.         mapConstraints.put(contraintIndex, constraintValue);
  153.     }

  154.     /** Set the epoch a patch point to free or not.
  155.      * @param patchNumber Patch point
  156.      * @param isFree constraint value
  157.      */
  158.     public void setEpochFreedom(final int patchNumber, final boolean isFree) {
  159.         if (freeEpochMap[patchNumber - 1] != isFree) {
  160.             freeEpochMap[patchNumber - 1] = isFree;
  161.             final int eps = isFree ? 1 : -1;
  162.             nEpoch = nEpoch + eps;
  163.         }
  164.     }

  165.     /** {@inheritDoc} */
  166.     @Override
  167.     public List<SpacecraftState> compute() {

  168.         if (nFree > nConstraints) {
  169.             throw new OrekitException(OrekitMessages.MULTIPLE_SHOOTING_UNDERCONSTRAINED, nFree, nConstraints);
  170.         }

  171.         int iter = 0; // number of iteration

  172.         double fxNorm = 0;

  173.         do {

  174.             final List<SpacecraftState> propagatedSP = propagatePatchedSpacecraftState(); // multi threading see PropagatorsParallelizer
  175.             final RealMatrix M = computeJacobianMatrix(propagatedSP);
  176.             final RealVector fx = MatrixUtils.createRealVector(computeConstraint(propagatedSP));

  177.             // Solve linear system using minimum norm approach
  178.             // (i.e. minimize difference between solutions from successive iterations,
  179.             //  in other word try to stay close to initial guess; this is *not* a least squares solution)
  180.             // see equation 3.12 in Pavlak's thesis
  181.             final RealMatrix MMt = M.multiplyTransposed(M);
  182.             final RealVector dx  = M.transposeMultiply(MatrixUtils.inverse(MMt)).operate(fx);

  183.             // Apply correction from the free variable vector to all the variables (propagation time, pacthSpaceraftState)
  184.             updateTrajectory(dx);

  185.             fxNorm = fx.getNorm() / fx.getDimension();

  186.             iter++;

  187.         } while (fxNorm > tolerance && iter < maxIter); // Converge within tolerance and under max iterations

  188.         return patchedSpacecraftStates;
  189.     }

  190.     /** Compute the Jacobian matrix of the problem.
  191.      *  @param propagatedSP List of propagated SpacecraftStates (patch points)
  192.      *  @return jacobianMatrix Jacobian matrix
  193.      */
  194.     private RealMatrix computeJacobianMatrix(final List<SpacecraftState> propagatedSP) {

  195.         final int npoints         = patchedSpacecraftStates.size();
  196.         final int epochConstraint = nEpoch == 0 ? 0 : npoints - 1;
  197.         final int nrows    = getNumberOfConstraints() + epochConstraint;
  198.         final int ncolumns = getNumberOfFreeVariables() + nEpoch;

  199.         final RealMatrix M = MatrixUtils.createRealMatrix(nrows, ncolumns);

  200.         int index = 0;
  201.         int indexEpoch = nFree;
  202.         for (int i = 0; i < npoints - 1; i++) {

  203.             final SpacecraftState finalState = propagatedSP.get(i);
  204.             // The Jacobian matrix has the following form:

  205.             //          [     |     |     ]
  206.             //          [  A  |  B  |  C  ]
  207.             // DF(X) =  [     |     |     ]
  208.             //          [-----------------]
  209.             //          [  0  |  D  |  E  ]
  210.             //          [-----------------]
  211.             //          [  F  |  0  |  0  ]

  212.             //          [     |     ]
  213.             //          [  A  |  B  ]
  214.             // DF(X) =  [     |     ]
  215.             //          [-----------]
  216.             //          [  C  |  0  ]
  217.             //
  218.             // For a problem with all the components of each patch points is free, A is detailed below :
  219.             //      [ phi1     -I                                   ]
  220.             //      [         phi2     -I                           ]
  221.             // A =  [                 ....    ....                  ]   6(n-1)x6n
  222.             //      [                         ....     ....         ]
  223.             //      [                                 phin-1    -I  ]

  224.             // D is computing according to additional constraints
  225.             // (for now, closed orbit, or a component of a patch point equals to a specified value)
  226.             //

  227.             // If the duration of the propagation of each arc is the same :
  228.             //      [ xdot1f ]
  229.             //      [ xdot2f ]                      [  -1 ]
  230.             // B =  [  ....  ]   6(n-1)x1   and D = [ ... ]
  231.             //      [  ....  ]                      [  -1 ]
  232.             //      [xdotn-1f]

  233.             // Otherwise :
  234.             //      [ xdot1f                                ]
  235.             //      [        xdot2f                         ]
  236.             // B =  [                 ....                  ]   6(n-1)x(n-1) and D = -I
  237.             //      [                        ....           ]
  238.             //      [                             xdotn-1f  ]
  239.             //
  240.             // If the problem is not dependant of the epoch (e.g. CR3BP), the C and E matrices are not computed.
  241.             // Otherwise :
  242.             //      [ -dx1f/dtau1                                           0 ]
  243.             //      [          -dx2f/dtau2                                  0 ]
  244.             // C =  [                       ....                            0 ]   6(n-1)xn
  245.             //      [                               ....                    0 ]
  246.             //      [                                     -dxn-1f/dtaun-1   0 ]
  247.             //
  248.             //      [ -1   1  0                 ]
  249.             //      [     -1   1  0             ]
  250.             // E =  [          ..   ..          ] n-1xn
  251.             //      [               ..   ..     ]
  252.             //      [                    -1   1 ]

  253.             final PVCoordinates pvf = finalState.getPVCoordinates();

  254.             // Get State Transition Matrix phi
  255.             final double[][] phi = getStateTransitionMatrix(finalState);

  256.             // Matrix A
  257.             for (int j = 0; j < 6; j++) { // Loop on 6 component of the patch point
  258.                 if (freePatchPointMap[6 * i + j]) { // If this component is free
  259.                     for (int k = 0; k < 6; k++) { // Loop on the 6 component of the patch point constraint
  260.                         M.setEntry(6 * i + k, index, phi[k][j]);
  261.                     }
  262.                     if (i > 0) {
  263.                         M.setEntry(6 * (i - 1) + j, index, -1);
  264.                     }
  265.                     index++;
  266.                 }
  267.             }

  268.             // Matrix B
  269.             final double[][] pvfArray = new double[][] {
  270.                 {pvf.getVelocity().getX()},
  271.                 {pvf.getVelocity().getY()},
  272.                 {pvf.getVelocity().getZ()},
  273.                 {pvf.getAcceleration().getX()},
  274.                 {pvf.getAcceleration().getY()},
  275.                 {pvf.getAcceleration().getZ()}};

  276.             M.setSubMatrix(pvfArray, 6 * i, nFree - 1);

  277.             // Matrix C
  278.             if (freeEpochMap[i]) { // If this component is free
  279.                 final double[] derivatives = finalState.getAdditionalState("derivatives");
  280.                 final double[][] subC = new double[6][1];
  281.                 for (int j = 0; j < 6; j++) { // Loop on 6 component of the patch point
  282.                     subC[j][0] = -derivatives[derivatives.length - 6 + j];
  283.                 }
  284.                 M.setSubMatrix(subC, 6 * i, indexEpoch);
  285.                 indexEpoch++;
  286.             }
  287.         }

  288.         for (int j = 0; j < 6; j++) { // Loop on 6 component of the patch point
  289.             if (freePatchPointMap[6 * (npoints - 1) + j]) { // If this component is free
  290.                 M.setEntry(6 * (npoints - 2) + j, index, -1);
  291.                 index++;
  292.             }
  293.         }


  294.         // Matrices D and E.
  295.         if (nEpoch > 0) {
  296.             final double[][] subDE = computeEpochJacobianMatrix(propagatedSP);
  297.             M.setSubMatrix(subDE, 6 * (npoints - 1), nFree - 1);
  298.         }

  299.         // Matrices F.
  300.         final double[][] subF = computeAdditionalJacobianMatrix(propagatedSP);
  301.         if (subF.length > 0) {
  302.             M.setSubMatrix(subF, 6 * (npoints - 1) + epochConstraint, 0);
  303.         }

  304.         return M;
  305.     }

  306.     /** Compute the constraint vector of the problem.
  307.      *  @param propagatedSP propagated SpacecraftState
  308.      *  @return constraint vector
  309.      */
  310.     private double[] computeConstraint(final List<SpacecraftState> propagatedSP) {

  311.         // The Constraint vector has the following form :

  312.         //         [ x1f - x2i ]---
  313.         //         [ x2f - x3i ]   |
  314.         // F(X) =  [    ...    ] vectors' equality for a continuous trajectory
  315.         //         [    ...    ]   |
  316.         //         [xn-1f - xni]---
  317.         //         [ d2-(d1+T) ]   continuity between epoch
  318.         //         [    ...    ]   and integration time
  319.         //         [dn-(dn-1+T)]---
  320.         //         [    ...    ] additional
  321.         //         [    ...    ] constraints

  322.         final int npoints = patchedSpacecraftStates.size();

  323.         final double[] additionalConstraints = computeAdditionalConstraints(propagatedSP);
  324.         final boolean epoch = getNumberOfFreeEpoch() > 0;

  325.         final int nrows = epoch ? getNumberOfConstraints() + npoints - 1 : getNumberOfConstraints();

  326.         final double[] fx = new double[nrows];
  327.         for (int i = 0; i < npoints - 1; i++) {
  328.             final AbsolutePVCoordinates absPvi = patchedSpacecraftStates.get(i + 1).getAbsPVA();
  329.             final AbsolutePVCoordinates absPvf = propagatedSP.get(i).getAbsPVA();
  330.             final double[] ecartPos = absPvf.getPosition().subtract(absPvi.getPosition()).toArray();
  331.             final double[] ecartVel = absPvf.getVelocity().subtract(absPvi.getVelocity()).toArray();
  332.             for (int j = 0; j < 3; j++) {
  333.                 fx[6 * i + j] = ecartPos[j];
  334.                 fx[6 * i + 3 + j] = ecartVel[j];
  335.             }
  336.         }

  337.         int index = 6 * (npoints - 1);

  338.         if (epoch) {
  339.             for (int i = 0; i < npoints - 1; i++) {
  340.                 final double deltaEpoch = patchedSpacecraftStates.get(i + 1).getDate().durationFrom(patchedSpacecraftStates.get(i).getDate());
  341.                 fx[index] = deltaEpoch - propagationTime[i];
  342.                 index++;
  343.             }
  344.         }

  345.         for (int i = 0; i < additionalConstraints.length; i++) {
  346.             fx[index] = additionalConstraints[i];
  347.             index++;
  348.         }

  349.         return fx;
  350.     }


  351.     /** Update the trajectory, and the propagation time.
  352.      *  @param dx correction on the initial vector
  353.      */
  354.     private void updateTrajectory(final RealVector dx) {
  355.         // X = [x1, ..., xn, T1, ..., Tn, d1, ..., dn]
  356.         // X = [x1, ..., xn, T, d2, ..., dn]

  357.         final int n = getNumberOfFreeVariables();

  358.         final boolean epochFree = getNumberOfFreeEpoch() > 0;

  359.         // Update propagation time
  360.         //------------------------------------------------------
  361.         final double deltaT = dx.getEntry(n - 1);
  362.         for (int i = 0; i < propagationTime.length; i++) {
  363.             propagationTime[i] = propagationTime[i] - deltaT;
  364.         }

  365.         // Update patch points through SpacecrafStates
  366.         //--------------------------------------------------------------------------------

  367.         int index = 0;
  368.         int indexEpoch = 0;

  369.         for (int i = 0; i < patchedSpacecraftStates.size(); i++) {
  370.             // Get delta in position and velocity
  371.             final double[] deltaPV = new double[6];
  372.             for (int j = 0; j < 6; j++) { // Loop on 6 component of the patch point
  373.                 if (freePatchPointMap[6 * i + j]) { // If this component is free (is to be updated)
  374.                     deltaPV[j] = dx.getEntry(index);
  375.                     index++;
  376.                 }
  377.             }
  378.             final Vector3D deltaP = new Vector3D(deltaPV[0], deltaPV[1], deltaPV[2]);
  379.             final Vector3D deltaV = new Vector3D(deltaPV[3], deltaPV[4], deltaPV[5]);

  380.             // Update the PVCoordinates of the patch point
  381.             final AbsolutePVCoordinates currentAPV = patchedSpacecraftStates.get(i).getAbsPVA();
  382.             final Vector3D position = currentAPV.getPosition().subtract(deltaP);
  383.             final Vector3D velocity = currentAPV.getVelocity().subtract(deltaV);
  384.             final PVCoordinates pv = new PVCoordinates(position, velocity);

  385.             //Update epoch in the AbsolutePVCoordinates
  386.             AbsoluteDate epoch = currentAPV.getDate();
  387.             if (epochFree) {
  388.                 if (freeEpochMap[i]) {
  389.                     final double deltaEpoch = dx.getEntry(n + indexEpoch);
  390.                     epoch = epoch.shiftedBy(-deltaEpoch);
  391.                     indexEpoch++;
  392.                 }
  393.             } else {
  394.                 if (i > 0) {
  395.                     epoch = patchedSpacecraftStates.get(i - 1).getDate().shiftedBy(propagationTime[i - 1]);
  396.                 }
  397.             }
  398.             final AbsolutePVCoordinates updatedAPV = new AbsolutePVCoordinates(currentAPV.getFrame(), epoch, pv);

  399.             //Update attitude epoch
  400.             // Last point does not have an associated propagator. The previous one is then selected.
  401.             final int iAttitude = i < getPropagatorList().size() ? i : getPropagatorList().size() - 1;
  402.             final AttitudeProvider attitudeProvider = getPropagatorList().get(iAttitude).getAttitudeProvider();
  403.             final Attitude attitude = attitudeProvider.getAttitude(updatedAPV, epoch, currentAPV.getFrame());

  404.             //Update the SpacecraftState using previously updated attitude and AbsolutePVCoordinates
  405.             patchedSpacecraftStates.set(i, new SpacecraftState(updatedAPV, attitude));
  406.         }

  407.     }

  408.     /** Compute the Jacobian matrix of the problem.
  409.      *  @return propagatedSP propagated SpacecraftStates
  410.      */
  411.     private List<SpacecraftState> propagatePatchedSpacecraftState() {

  412.         final int n = patchedSpacecraftStates.size() - 1;

  413.         final ArrayList<SpacecraftState> propagatedSP = new ArrayList<>(n);

  414.         for (int i = 0; i < n; i++) {

  415.             // SpacecraftState initialization
  416.             final SpacecraftState augmentedInitialState = getAugmentedInitialState(i);

  417.             // Propagator initialization
  418.             propagatorList.get(i).setInitialState(augmentedInitialState);

  419.             final double integrationTime = propagationTime[i];

  420.             // Propagate trajectory
  421.             final SpacecraftState finalState = propagatorList.get(i).propagate(augmentedInitialState.getDate().shiftedBy(integrationTime));

  422.             propagatedSP.add(finalState);
  423.         }

  424.         return propagatedSP;
  425.     }

  426.     /** Get the state transition matrix.
  427.      * @param s current spacecraft state
  428.      * @return the state transition matrix
  429.      */
  430.     private double[][] getStateTransitionMatrix(final SpacecraftState s) {
  431.         // Additional states
  432.         final DoubleArrayDictionary dictionary = s.getAdditionalStatesValues();
  433.         // Initialize state transition matrix
  434.         final int        dim  = 6;
  435.         final double[][] phiM = new double[dim][dim];

  436.         // Loop on entry set
  437.         for (final DoubleArrayDictionary.Entry entry : dictionary.getData()) {
  438.             // Extract entry name
  439.             final String name = entry.getKey();
  440.             if (additionalName.equals(name)) {
  441.                 final double[] stm = entry.getValue();
  442.                 for (int i = 0; i < dim; i++) {
  443.                     for (int j = 0; j < 6; j++) {
  444.                         phiM[i][j] = stm[dim * i + j];
  445.                     }
  446.                 }
  447.             }
  448.         }

  449.         // Return state transition matrix
  450.         return phiM;
  451.     }

  452.     /** Compute a part of the Jacobian matrix with derivatives from epoch.
  453.      * The CR3BP is a time invariant problem. The derivatives w.r.t. epoch are zero.
  454.      *  @param propagatedSP propagatedSP
  455.      *  @return jacobianMatrix Jacobian sub-matrix
  456.      */
  457.     protected double[][] computeEpochJacobianMatrix(final List<SpacecraftState> propagatedSP) {

  458.         final boolean[] map = getFreeEpochMap();

  459.         final int nFreeEpoch = getNumberOfFreeEpoch();
  460.         final int ncolumns = 1 + nFreeEpoch;
  461.         final int nrows = patchedSpacecraftStates.size() - 1;

  462.         final double[][] M = new double[nrows][ncolumns];

  463.         // The Jacobian matrix has the following form:

  464.         //      [-1 -1   1  0                 ]
  465.         //      [-1     -1   1  0             ]
  466.         // F =  [..          ..   ..          ]
  467.         //      [..               ..   ..   0 ]
  468.         //      [-1                    -1   1 ]

  469.         int index = 1;
  470.         for (int i = 0; i < nrows; i++) {
  471.             M[i][0] = -1;
  472.             if (map[i]) {
  473.                 M[i][index] = -1;
  474.                 index++;
  475.             }
  476.             if (map[i + 1]) {
  477.                 M[i][index] =  1;
  478.             }
  479.         }

  480.         return M;
  481.     }

  482.     /** Update the array of additional constraints.
  483.      * @param startIndex start index
  484.      * @param fxAdditional array of additional constraints
  485.      */
  486.     protected void updateAdditionalConstraints(final int startIndex, final double[] fxAdditional) {
  487.         int iter = startIndex;
  488.         for (final Map.Entry<Integer, Double> entry : getConstraintsMap().entrySet()) {
  489.             // Extract entry values
  490.             final int    key   = entry.getKey();
  491.             final double value = entry.getValue();
  492.             final int np = key / 6;
  493.             final int nc = key % 6;
  494.             final AbsolutePVCoordinates absPv = getPatchedSpacecraftState().get(np).getAbsPVA();
  495.             if (nc < 3) {
  496.                 fxAdditional[iter] = absPv.getPosition().toArray()[nc] - value;
  497.             } else {
  498.                 fxAdditional[iter] = absPv.getVelocity().toArray()[nc - 3] -  value;
  499.             }
  500.             iter++;
  501.         }
  502.     }

  503.     /** Compute the additional constraints.
  504.      *  @param propagatedSP propagated SpacecraftState
  505.      *  @return fxAdditionnal additional constraints
  506.      */
  507.     protected abstract double[] computeAdditionalConstraints(List<SpacecraftState> propagatedSP);

  508.     /** Compute a part of the Jacobian matrix from additional constraints.
  509.      *  @param propagatedSP propagatedSP
  510.      *  @return jacobianMatrix Jacobian sub-matrix
  511.      */
  512.     protected abstract double[][] computeAdditionalJacobianMatrix(List<SpacecraftState> propagatedSP);


  513.     /** Compute the additional state from the additionalEquations.
  514.      *  @param initialState SpacecraftState without the additional state
  515.      *  @param additionalEquations2 Additional Equations.
  516.      *  @return augmentedSP SpacecraftState with the additional state within.
  517.      *  @deprecated as of 11.1, replaced by {@link #getAugmentedInitialState(int)}
  518.      */
  519.     @Deprecated
  520.     protected SpacecraftState getAugmentedInitialState(final SpacecraftState initialState,
  521.                                                        final org.orekit.propagation.integration.AdditionalEquations additionalEquations2) {
  522.         // should never be called, only implementations by derived classes should be called
  523.         throw new UnsupportedOperationException();
  524.     }

  525.     /** Compute the additional state from the additionalEquations.
  526.      *  @param i index of the state
  527.      *  @return augmentedSP SpacecraftState with the additional state within.
  528.      *  @since 11.1
  529.      */
  530.     protected SpacecraftState getAugmentedInitialState(final int i) {
  531.         // FIXME: this base implementation is only intended for version 11.1 to delegate to a deprecated method
  532.         // it should be removed in 12.0 when getAugmentedInitialState(SpacecraftState, AdditionalDerivativesProvider) is removed
  533.         // and the method should remain abstract in this class and be implemented by derived classes only
  534.         return getAugmentedInitialState(patchedSpacecraftStates.get(i), additionalEquations.get(i));
  535.     }

  536.     /** Set the constraint of a closed orbit or not.
  537.      *  @param isClosed true if orbit should be closed
  538.      */
  539.     public void setClosedOrbitConstraint(final boolean isClosed) {
  540.         if (this.isClosedOrbit != isClosed) {
  541.             nConstraints = nConstraints + (isClosed ? 6 : -6);
  542.             this.isClosedOrbit = isClosed;
  543.         }
  544.     }

  545.     /** Get the number of free variables.
  546.      * @return the number of free variables
  547.      */
  548.     protected int getNumberOfFreeVariables() {
  549.         return nFree;
  550.     }

  551.     /** Get the number of free epoch.
  552.      * @return the number of free epoch
  553.      */
  554.     protected int getNumberOfFreeEpoch() {
  555.         return nEpoch;
  556.     }

  557.     /** Get the number of constraints.
  558.      * @return the number of constraints
  559.      */
  560.     protected int getNumberOfConstraints() {
  561.         return nConstraints;
  562.     }

  563.     /** Get the flags representing the free components of patch points.
  564.      * @return an array of flags representing the free components of patch points
  565.      */
  566.     protected boolean[] getFreePatchPointMap() {
  567.         return freePatchPointMap;
  568.     }

  569.     /** Get the flags representing the free epoch of patch points.
  570.      * @return an array of flags representing the free epoch of patch points
  571.      */
  572.     protected boolean[] getFreeEpochMap() {
  573.         return freeEpochMap;
  574.     }

  575.     /** Get the map of patch points components which are constrained.
  576.      * @return a map of patch points components which are constrained
  577.      */
  578.     protected Map<Integer, Double> getConstraintsMap() {
  579.         return mapConstraints;
  580.     }

  581.     /** Get the list of patched spacecraft states.
  582.      * @return a list of patched spacecraft states
  583.      */
  584.     protected List<SpacecraftState> getPatchedSpacecraftState() {
  585.         return patchedSpacecraftStates;
  586.     }

  587.     /** Get the list of propagators.
  588.      * @return a list of propagators
  589.      */
  590.     protected List<NumericalPropagator> getPropagatorList() {
  591.         return propagatorList;
  592.     }

  593.     /** Get he flag representing if the orbit is closed.
  594.      * @return true if orbit is closed
  595.      */
  596.     protected boolean isClosedOrbit() {
  597.         return isClosedOrbit;
  598.     }

  599. }