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.cost;
18  
19  
20  import org.hipparchus.geometry.euclidean.threed.Vector3D;
21  import org.orekit.propagation.SpacecraftState;
22  import org.orekit.propagation.events.EventDetectionSettings;
23  import org.orekit.propagation.events.EventDetector;
24  
25  import java.util.stream.Stream;
26  
27  /**
28   * Class for fuel cost with Cartesian coordinates.
29   * It is the integral over time of the Euclidean norm of the thrust vector.
30   *
31   * @author Romain Serra
32   * @see CartesianCost
33   * @since 13.0
34   */
35  public class CartesianFuelCost extends AbstractCartesianCost {
36  
37      /** Maximum value of thrust force Euclidean norm. */
38      private final double maximumThrustMagnitude;
39  
40      /** Detection settings for singularity detection. */
41      private final EventDetectionSettings eventDetectionSettings;
42  
43      /**
44       * Constructor with default detection settings.
45       * @param name name
46       * @param massFlowRateFactor mass flow rate factor
47       * @param maximumThrustMagnitude maximum thrust magnitude
48       */
49      public CartesianFuelCost(final String name, final double massFlowRateFactor, final double maximumThrustMagnitude) {
50          this(name, massFlowRateFactor, maximumThrustMagnitude, EventDetectionSettings.getDefaultEventDetectionSettings());
51      }
52  
53      /**
54       * Constructor.
55       * @param name name
56       * @param massFlowRateFactor mass flow rate factor
57       * @param maximumThrustMagnitude maximum thrust magnitude
58       * @param eventDetectionSettings singularity event detection settings
59       */
60      public CartesianFuelCost(final String name, final double massFlowRateFactor, final double maximumThrustMagnitude,
61                               final EventDetectionSettings eventDetectionSettings) {
62          super(name, massFlowRateFactor);
63          this.maximumThrustMagnitude = maximumThrustMagnitude;
64          this.eventDetectionSettings = eventDetectionSettings;
65      }
66  
67      /** Getter for maximum thrust magnitude.
68       * @return maximum thrust
69       */
70      public double getMaximumThrustMagnitude() {
71          return maximumThrustMagnitude;
72      }
73  
74      /**
75       * Getter for event detection settings.
76       * @return detection settings.
77       */
78      public EventDetectionSettings getEventDetectionSettings() {
79          return eventDetectionSettings;
80      }
81  
82      /**
83       * Evaluate switching function (whose sign determines the bang-bang control profile).
84       * @param adjointVariables adjoint vector
85       * @param mass mass
86       * @return value of switch function
87       */
88      private double evaluateSwitchFunction(final double[] adjointVariables, final double mass) {
89          double switchFunction = getAdjointVelocityNorm(adjointVariables) / mass - 1.;
90          if (getAdjointDimension() > 6) {
91              switchFunction -= adjointVariables[6] * getMassFlowRateFactor();
92          }
93          return switchFunction;
94      }
95  
96      /**
97       * Computes the direction of thrust.
98       * @param adjointVariables adjoint vector
99       * @return thrust direction
100      */
101     private Vector3D getThrustDirection(final double[] adjointVariables) {
102         return new Vector3D(adjointVariables[3], adjointVariables[4], adjointVariables[5]).normalize();
103     }
104 
105     /** {@inheritDoc} */
106     @Override
107     public Vector3D getThrustAccelerationVector(final double[] adjointVariables, final double mass) {
108         final double switchFunction = evaluateSwitchFunction(adjointVariables, mass);
109         if (switchFunction > 0.) {
110             return getThrustDirection(adjointVariables).scalarMultiply(maximumThrustMagnitude / mass);
111         } else {
112             return Vector3D.ZERO;
113         }
114     }
115 
116     /** {@inheritDoc} */
117     @Override
118     public void updateAdjointDerivatives(final double[] adjointVariables, final double mass,
119                                          final double[] adjointDerivatives) {
120         if (getAdjointDimension() > 6) {
121             final double switchFunction = evaluateSwitchFunction(adjointVariables, mass);
122             if (switchFunction > 0.) {
123                 adjointDerivatives[6] += getAdjointVelocityNorm(adjointVariables) * maximumThrustMagnitude / (mass * mass);
124             }
125         }
126     }
127 
128     /** {@inheritDoc} */
129     @Override
130     public double getHamiltonianContribution(final double[] adjointVariables, final double mass) {
131         final Vector3D thrustForce = getThrustAccelerationVector(adjointVariables, mass).scalarMultiply(mass);
132         return -thrustForce.getNorm();
133     }
134 
135     /** {@inheritDoc} */
136     @Override
137     public Stream<EventDetector> getEventDetectors() {
138         return Stream.of(new FuelCostSwitchDetector(eventDetectionSettings));
139     }
140 
141     /**
142      * Event detector for bang-bang switches.
143      */
144     private class FuelCostSwitchDetector extends ControlSwitchDetector {
145 
146         /**
147          * Constructor.
148          * @param detectionSettings detection settings.
149          */
150         FuelCostSwitchDetector(final EventDetectionSettings detectionSettings) {
151             super(detectionSettings);
152         }
153 
154         /** {@inheritDoc} */
155         @Override
156         public double g(final SpacecraftState state) {
157             final double[] adjoint = state.getAdditionalState(getAdjointName());
158             return evaluateSwitchFunction(adjoint, state.getMass());
159         }
160     }
161 
162 }