FieldCartesianAdjointDerivativesProvider.java

  1. /* Copyright 2022-2025 Romain Serra
  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.control.indirect.adjoint;

  18. import org.hipparchus.CalculusFieldElement;
  19. import org.hipparchus.geometry.euclidean.threed.FieldVector3D;
  20. import org.hipparchus.util.FastMath;
  21. import org.hipparchus.util.MathArrays;
  22. import org.orekit.control.indirect.adjoint.cost.FieldCartesianCost;
  23. import org.orekit.errors.OrekitException;
  24. import org.orekit.errors.OrekitMessages;
  25. import org.orekit.frames.Frame;
  26. import org.orekit.orbits.OrbitType;
  27. import org.orekit.propagation.FieldSpacecraftState;
  28. import org.orekit.propagation.integration.FieldAdditionalDerivativesProvider;
  29. import org.orekit.propagation.integration.FieldCombinedDerivatives;
  30. import org.orekit.time.FieldAbsoluteDate;
  31. import org.orekit.utils.FieldPVCoordinates;

  32. /**
  33.  * Class defining the Field version of the adjoint dynamics for Cartesian coordinates, as defined in the Pontryagin Maximum Principle.
  34.  * @author Romain Serra
  35.  * @see FieldAdditionalDerivativesProvider
  36.  * @see org.orekit.propagation.numerical.FieldNumericalPropagator
  37.  * @see CartesianAdjointDerivativesProvider
  38.  * @since 12.2
  39.  */
  40. public class FieldCartesianAdjointDerivativesProvider<T extends CalculusFieldElement<T>> implements FieldAdditionalDerivativesProvider<T> {

  41.     /** Contributing terms to the adjoint equation. */
  42.     private final CartesianAdjointEquationTerm[] adjointEquationTerms;

  43.     /** Cost function. */
  44.     private final FieldCartesianCost<T> cost;

  45.     /**
  46.      * Constructor.
  47.      * @param cost cost function
  48.      * @param adjointEquationTerms terms contributing to the adjoint equations
  49.      */
  50.     public FieldCartesianAdjointDerivativesProvider(final FieldCartesianCost<T> cost,
  51.                                                     final CartesianAdjointEquationTerm... adjointEquationTerms) {
  52.         this.cost = cost;
  53.         this.adjointEquationTerms = adjointEquationTerms;
  54.     }

  55.     /**
  56.      * Getter for the cost.
  57.      * @return cost
  58.      */
  59.     public FieldCartesianCost<T> getCost() {
  60.         return cost;
  61.     }

  62.     /** Getter for the name.
  63.      * @return name */
  64.     public String getName() {
  65.         return cost.getAdjointName();
  66.     }

  67.     /** Getter for the dimension.
  68.      * @return dimension
  69.      */
  70.     public int getDimension() {
  71.         return cost.getAdjointDimension();
  72.     }

  73.     /** {@inheritDoc} */
  74.     @Override
  75.     public void init(final FieldSpacecraftState<T> initialState, final FieldAbsoluteDate<T> target) {
  76.         FieldAdditionalDerivativesProvider.super.init(initialState, target);
  77.         if (initialState.isOrbitDefined() && initialState.getOrbit().getType() != OrbitType.CARTESIAN) {
  78.             throw new OrekitException(OrekitMessages.WRONG_COORDINATES_FOR_ADJOINT_EQUATION);
  79.         }
  80.     }

  81.     /** {@inheritDoc} */
  82.     @Override
  83.     public FieldCombinedDerivatives<T> combinedDerivatives(final FieldSpacecraftState<T> state) {
  84.         // pre-computations
  85.         final T mass = state.getMass();
  86.         final T[] adjointVariables = state.getAdditionalState(getName());
  87.         final int adjointDimension = getDimension();
  88.         final T[] additionalDerivatives = MathArrays.buildArray(mass.getField(), adjointDimension);
  89.         final T[] cartesianVariablesAndMass = formCartesianAndMassVector(state);

  90.         // mass flow rate and control acceleration
  91.         final T[] mainDerivativesIncrements = MathArrays.buildArray(mass.getField(), 7);
  92.         final FieldVector3D<T> thrustAccelerationVector = getCost().getFieldThrustAccelerationVector(adjointVariables, mass);
  93.         mainDerivativesIncrements[3] = thrustAccelerationVector.getX();
  94.         mainDerivativesIncrements[4] = thrustAccelerationVector.getY();
  95.         mainDerivativesIncrements[5] = thrustAccelerationVector.getZ();
  96.         final T thrustAccelerationNorm = thrustAccelerationVector.getNorm();
  97.         if (thrustAccelerationVector.getNorm().getReal() != 0.) {
  98.             final T thrustForceMagnitude = thrustAccelerationNorm.multiply(mass);
  99.             mainDerivativesIncrements[6] = thrustForceMagnitude.multiply(getCost().getMassFlowRateFactor().negate());
  100.         }

  101.         // Cartesian position adjoint
  102.         additionalDerivatives[3] = adjointVariables[0].negate();
  103.         additionalDerivatives[4] = adjointVariables[1].negate();
  104.         additionalDerivatives[5] = adjointVariables[2].negate();

  105.         // Cartesian velocity adjoint
  106.         final FieldAbsoluteDate<T> date = state.getDate();
  107.         final Frame propagationFrame = state.getFrame();
  108.         for (final CartesianAdjointEquationTerm equationTerm: adjointEquationTerms) {
  109.             final T[] contribution = equationTerm.getFieldRatesContribution(date, cartesianVariablesAndMass, adjointVariables,
  110.                     propagationFrame);
  111.             for (int i = 0; i < FastMath.min(adjointDimension, contribution.length); i++) {
  112.                 additionalDerivatives[i] = additionalDerivatives[i].add(contribution[i]);
  113.             }
  114.         }

  115.         // other
  116.         getCost().updateFieldAdjointDerivatives(adjointVariables, mass, additionalDerivatives);

  117.         return new FieldCombinedDerivatives<>(additionalDerivatives, mainDerivativesIncrements);
  118.     }

  119.     /**
  120.      * Gather Cartesian variables and mass in same vector.
  121.      * @param state propagation state
  122.      * @return Cartesian variables and mass
  123.      */
  124.     private T[] formCartesianAndMassVector(final FieldSpacecraftState<T> state) {
  125.         final T mass = state.getMass();
  126.         final T[] cartesianVariablesAndMass = MathArrays.buildArray(mass.getField(), 7);
  127.         final FieldPVCoordinates<T> pvCoordinates = state.getPVCoordinates();
  128.         System.arraycopy(pvCoordinates.getPosition().toArray(), 0, cartesianVariablesAndMass, 0, 3);
  129.         System.arraycopy(pvCoordinates.getVelocity().toArray(), 0, cartesianVariablesAndMass, 3, 3);
  130.         cartesianVariablesAndMass[6] = mass;
  131.         return cartesianVariablesAndMass;
  132.     }

  133.     /**
  134.      * Evaluate the Hamiltonian from Pontryagin's Maximum Principle.
  135.      * @param state state assumed to hold the adjoint variables
  136.      * @return Hamiltonian
  137.      */
  138.     public T evaluateHamiltonian(final FieldSpacecraftState<T> state) {
  139.         final T[] cartesianAndMassVector = formCartesianAndMassVector(state);
  140.         final T[] adjointVariables = state.getAdditionalState(getName());
  141.         T hamiltonian = adjointVariables[0].multiply(cartesianAndMassVector[3]).add(adjointVariables[1].multiply(cartesianAndMassVector[4]))
  142.                 .add(adjointVariables[2].multiply(cartesianAndMassVector[5]));
  143.         final FieldAbsoluteDate<T> date = state.getDate();
  144.         final Frame propagationFrame = state.getFrame();
  145.         for (final CartesianAdjointEquationTerm adjointEquationTerm : adjointEquationTerms) {
  146.             final T contribution = adjointEquationTerm.getFieldHamiltonianContribution(date, cartesianAndMassVector,
  147.                 adjointVariables, propagationFrame);
  148.             hamiltonian = hamiltonian.add(contribution);
  149.         }
  150.         final T mass = state.getMass();
  151.         if (adjointVariables.length != 6) {
  152.             final T thrustAccelerationNorm = getCost().getFieldThrustAccelerationVector(adjointVariables, mass).getNorm();
  153.             final T thrustForceNorm = thrustAccelerationNorm.multiply(mass);
  154.             hamiltonian = hamiltonian.subtract(adjointVariables[6].multiply(getCost().getMassFlowRateFactor()).multiply(thrustForceNorm));
  155.         }
  156.         hamiltonian = hamiltonian.add(getCost().getFieldHamiltonianContribution(adjointVariables, mass));
  157.         return hamiltonian;
  158.     }
  159. }