FieldAbstractAnalyticalPropagator.java
- /* Copyright 2002-2024 CS GROUP
- * Licensed to CS GROUP (CS) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * CS licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- package org.orekit.propagation.analytical;
- import java.util.ArrayList;
- import java.util.Collection;
- import java.util.Collections;
- import java.util.Comparator;
- import java.util.List;
- import java.util.PriorityQueue;
- import java.util.Queue;
- import org.hipparchus.CalculusFieldElement;
- import org.hipparchus.Field;
- import org.hipparchus.exception.MathRuntimeException;
- import org.hipparchus.ode.events.Action;
- import org.orekit.attitudes.AttitudeProvider;
- import org.orekit.attitudes.FieldAttitude;
- import org.orekit.errors.OrekitException;
- import org.orekit.errors.OrekitInternalError;
- import org.orekit.frames.Frame;
- import org.orekit.orbits.FieldOrbit;
- import org.orekit.propagation.BoundedPropagator;
- import org.orekit.propagation.FieldAbstractPropagator;
- import org.orekit.propagation.FieldAdditionalStateProvider;
- import org.orekit.propagation.FieldBoundedPropagator;
- import org.orekit.propagation.FieldEphemerisGenerator;
- import org.orekit.propagation.FieldSpacecraftState;
- import org.orekit.propagation.events.FieldEventDetector;
- import org.orekit.propagation.events.FieldEventState;
- import org.orekit.propagation.events.FieldEventState.EventOccurrence;
- import org.orekit.propagation.sampling.FieldOrekitStepInterpolator;
- import org.orekit.time.FieldAbsoluteDate;
- import org.orekit.utils.FieldPVCoordinatesProvider;
- import org.orekit.utils.ParameterDriver;
- import org.orekit.utils.ParameterDriversProvider;
- import org.orekit.utils.TimeStampedFieldPVCoordinates;
- /** Common handling of {@link org.orekit.propagation.FieldPropagator} methods for analytical propagators.
- * <p>
- * This abstract class allows to provide easily the full set of {@link
- * org.orekit.propagation.FieldPropagator FieldPropagator} methods, including all propagation
- * modes support and discrete events support for any simple propagation method. Only
- * two methods must be implemented by derived classes: {@link #propagateOrbit(FieldAbsoluteDate, CalculusFieldElement[])}
- * and {@link #getMass(FieldAbsoluteDate)}. The first method should perform straightforward
- * propagation starting from some internally stored initial state up to the specified target date.
- * </p>
- * @author Luc Maisonobe
- * @param <T> type of the field elements
- */
- public abstract class FieldAbstractAnalyticalPropagator<T extends CalculusFieldElement<T>> extends FieldAbstractPropagator<T>
- implements ParameterDriversProvider {
- /** Provider for attitude computation. */
- private FieldPVCoordinatesProvider<T> pvProvider;
- /** Start date of last propagation. */
- private FieldAbsoluteDate<T> lastPropagationStart;
- /** End date of last propagation. */
- private FieldAbsoluteDate<T> lastPropagationEnd;
- /** Initialization indicator of events states. */
- private boolean statesInitialized;
- /** Indicator for last step. */
- private boolean isLastStep;
- /** Event steps. */
- private final Collection<FieldEventState<?, T>> eventsStates;
- /** Build a new instance.
- * @param attitudeProvider provider for attitude computation
- * @param field field used as default
- */
- protected FieldAbstractAnalyticalPropagator(final Field<T> field, final AttitudeProvider attitudeProvider) {
- super(field);
- setAttitudeProvider(attitudeProvider);
- pvProvider = new FieldLocalPVProvider();
- lastPropagationStart = FieldAbsoluteDate.getPastInfinity(field);
- lastPropagationEnd = FieldAbsoluteDate.getFutureInfinity(field);
- statesInitialized = false;
- eventsStates = new ArrayList<>();
- }
- /** {@inheritDoc} */
- @Override
- public FieldEphemerisGenerator<T> getEphemerisGenerator() {
- return () -> new FieldBoundedPropagatorView(lastPropagationStart, lastPropagationEnd);
- }
- /** {@inheritDoc} */
- public <D extends FieldEventDetector<T>> void addEventDetector(final D detector) {
- eventsStates.add(new FieldEventState<>(detector));
- }
- /** {@inheritDoc} */
- @Override
- public Collection<FieldEventDetector<T>> getEventsDetectors() {
- final List<FieldEventDetector<T>> list = new ArrayList<>();
- for (final FieldEventState<?, T> state : eventsStates) {
- list.add(state.getEventDetector());
- }
- return Collections.unmodifiableCollection(list);
- }
- /** {@inheritDoc} */
- @Override
- public void clearEventsDetectors() {
- eventsStates.clear();
- }
- /** {@inheritDoc} */
- @Override
- public FieldSpacecraftState<T> propagate(final FieldAbsoluteDate<T> start, final FieldAbsoluteDate<T> target) {
- try {
- initializePropagation();
- lastPropagationStart = start;
- // Initialize additional states
- initializeAdditionalStates(target);
- final boolean isForward = target.compareTo(start) >= 0;
- FieldSpacecraftState<T> state = updateAdditionalStates(basicPropagate(start));
- // initialize event detectors
- for (final FieldEventState<?, T> es : eventsStates) {
- es.init(state, target);
- }
- // initialize step handlers
- getMultiplexer().init(state, target);
- // iterate over the propagation range, need loop due to reset events
- statesInitialized = false;
- isLastStep = false;
- do {
- // attempt to advance to the target date
- final FieldSpacecraftState<T> previous = state;
- final FieldSpacecraftState<T> current = updateAdditionalStates(basicPropagate(target));
- final FieldBasicStepInterpolator interpolator =
- new FieldBasicStepInterpolator(isForward, previous, current);
- // accept the step, trigger events and step handlers
- state = acceptStep(interpolator, target);
- // Update the potential changes in the spacecraft state due to the events
- // especially the potential attitude transition
- state = updateAdditionalStates(basicPropagate(state.getDate()));
- } while (!isLastStep);
- // return the last computed state
- lastPropagationEnd = state.getDate();
- setStartDate(state.getDate());
- return state;
- } catch (MathRuntimeException mrte) {
- throw OrekitException.unwrap(mrte);
- }
- }
- /** Accept a step, triggering events and step handlers.
- * @param interpolator interpolator for the current step
- * @param target final propagation time
- * @return state at the end of the step
- * @exception MathRuntimeException if an event cannot be located
- */
- protected FieldSpacecraftState<T> acceptStep(final FieldBasicStepInterpolator interpolator,
- final FieldAbsoluteDate<T> target)
- throws MathRuntimeException {
- FieldSpacecraftState<T> previous = interpolator.getPreviousState();
- final FieldSpacecraftState<T> current = interpolator.getCurrentState();
- FieldBasicStepInterpolator restricted = interpolator;
- // initialize the events states if needed
- if (!statesInitialized) {
- if (!eventsStates.isEmpty()) {
- // initialize the events states
- for (final FieldEventState<?, T> state : eventsStates) {
- state.reinitializeBegin(interpolator);
- }
- }
- statesInitialized = true;
- }
- // search for next events that may occur during the step
- final int orderingSign = interpolator.isForward() ? +1 : -1;
- final Queue<FieldEventState<?, T>> occurringEvents = new PriorityQueue<>(new Comparator<FieldEventState<?, T>>() {
- /** {@inheritDoc} */
- @Override
- public int compare(final FieldEventState<?, T> es0, final FieldEventState<?, T> es1) {
- return orderingSign * es0.getEventDate().compareTo(es1.getEventDate());
- }
- });
- boolean doneWithStep = false;
- resetEvents:
- do {
- // Evaluate all event detectors for events
- occurringEvents.clear();
- for (final FieldEventState<?, T> state : eventsStates) {
- if (state.evaluateStep(interpolator)) {
- // the event occurs during the current step
- occurringEvents.add(state);
- }
- }
- do {
- eventLoop:
- while (!occurringEvents.isEmpty()) {
- // handle the chronologically first event
- final FieldEventState<?, T> currentEvent = occurringEvents.poll();
- // get state at event time
- FieldSpacecraftState<T> eventState = restricted.getInterpolatedState(currentEvent.getEventDate());
- // restrict the interpolator to the first part of the step, up to the event
- restricted = restricted.restrictStep(previous, eventState);
- // try to advance all event states to current time
- for (final FieldEventState<?, T> state : eventsStates) {
- if (state != currentEvent && state.tryAdvance(eventState, interpolator)) {
- // we need to handle another event first
- // remove event we just updated to prevent heap corruption
- occurringEvents.remove(state);
- // add it back to update its position in the heap
- occurringEvents.add(state);
- // re-queue the event we were processing
- occurringEvents.add(currentEvent);
- continue eventLoop;
- }
- }
- // all event detectors agree we can advance to the current event time
- // handle the first part of the step, up to the event
- getMultiplexer().handleStep(restricted);
- // acknowledge event occurrence
- final EventOccurrence<T> occurrence = currentEvent.doEvent(eventState);
- final Action action = occurrence.getAction();
- isLastStep = action == Action.STOP;
- if (isLastStep) {
- // ensure the event is after the root if it is returned STOP
- // this lets the user integrate to a STOP event and then restart
- // integration from the same time.
- final FieldSpacecraftState<T> savedState = eventState;
- eventState = interpolator.getInterpolatedState(occurrence.getStopDate());
- restricted = restricted.restrictStep(savedState, eventState);
- // handle the almost zero size last part of the final step, at event time
- getMultiplexer().handleStep(restricted);
- getMultiplexer().finish(restricted.getCurrentState());
- }
- if (isLastStep) {
- // the event asked to stop integration
- return eventState;
- }
- if (action == Action.RESET_DERIVATIVES || action == Action.RESET_STATE) {
- // some event handler has triggered changes that
- // invalidate the derivatives, we need to recompute them
- final FieldSpacecraftState<T> resetState = occurrence.getNewState();
- resetIntermediateState(resetState, interpolator.isForward());
- return resetState;
- }
- // at this point action == Action.CONTINUE or Action.RESET_EVENTS
- // prepare handling of the remaining part of the step
- previous = eventState;
- restricted = new FieldBasicStepInterpolator(restricted.isForward(), eventState, current);
- if (action == Action.RESET_EVENTS) {
- continue resetEvents;
- }
- // at this pint action == Action.CONTINUE
- // check if the same event occurs again in the remaining part of the step
- if (currentEvent.evaluateStep(restricted)) {
- // the event occurs during the current step
- occurringEvents.add(currentEvent);
- }
- }
- // last part of the step, after the last event. Advance all detectors to
- // the end of the step. Should only detect a new event here if an event
- // modified the g function of another detector. Detecting such events here
- // is unreliable and RESET_EVENTS should be used instead. Might as well
- // re-check here because we have to loop through all the detectors anyway
- // and the alternative is to throw an exception.
- for (final FieldEventState<?, T> state : eventsStates) {
- if (state.tryAdvance(current, interpolator)) {
- occurringEvents.add(state);
- }
- }
- } while (!occurringEvents.isEmpty());
- doneWithStep = true;
- } while (!doneWithStep);
- isLastStep = target.equals(current.getDate());
- // handle the remaining part of the step, after all events if any
- getMultiplexer().handleStep(restricted);
- if (isLastStep) {
- getMultiplexer().finish(restricted.getCurrentState());
- }
- return current;
- }
- /** Get the mass.
- * @param date target date for the orbit
- * @return mass mass
- */
- protected abstract T getMass(FieldAbsoluteDate<T> date);
- /** Get PV coordinates provider.
- * @return PV coordinates provider
- */
- public FieldPVCoordinatesProvider<T> getPvProvider() {
- return pvProvider;
- }
- /** Reset an intermediate state.
- * @param state new intermediate state to consider
- * @param forward if true, the intermediate state is valid for
- * propagations after itself
- */
- protected abstract void resetIntermediateState(FieldSpacecraftState<T> state, boolean forward);
- /** Extrapolate an orbit up to a specific target date.
- * @param date target date for the orbit
- * @param parameters model parameters
- * @return extrapolated parameters
- */
- protected abstract FieldOrbit<T> propagateOrbit(FieldAbsoluteDate<T> date, T[] parameters);
- /** Propagate an orbit without any fancy features.
- * <p>This method is similar in spirit to the {@link #propagate} method,
- * except that it does <strong>not</strong> call any handler during
- * propagation, nor any discrete events, not additional states. It always
- * stop exactly at the specified date.</p>
- * @param date target date for propagation
- * @return state at specified date
- */
- protected FieldSpacecraftState<T> basicPropagate(final FieldAbsoluteDate<T> date) {
- try {
- // evaluate orbit
- final FieldOrbit<T> orbit = propagateOrbit(date, getParameters(date.getField(), date.getDate()));
- // evaluate attitude
- final FieldAttitude<T> attitude =
- getAttitudeProvider().getAttitude(pvProvider, date, orbit.getFrame());
- // build raw state
- return new FieldSpacecraftState<>(orbit, attitude, getMass(date));
- } catch (OrekitException oe) {
- throw new OrekitException(oe);
- }
- }
- /** Internal FieldPVCoordinatesProvider<T> for attitude computation. */
- private class FieldLocalPVProvider implements FieldPVCoordinatesProvider<T> {
- /** {@inheritDoc} */
- @Override
- public TimeStampedFieldPVCoordinates<T> getPVCoordinates(final FieldAbsoluteDate<T> date, final Frame frame) {
- return propagateOrbit(date, getParameters(date.getField(), date)).getPVCoordinates(frame);
- }
- }
- /** {@link BoundedPropagator} view of the instance. */
- private class FieldBoundedPropagatorView extends FieldAbstractAnalyticalPropagator<T>
- implements FieldBoundedPropagator<T> {
- /** Min date. */
- private final FieldAbsoluteDate<T> minDate;
- /** Max date. */
- private final FieldAbsoluteDate<T> maxDate;
- /** Simple constructor.
- * @param startDate start date of the propagation
- * @param endDate end date of the propagation
- */
- FieldBoundedPropagatorView(final FieldAbsoluteDate<T> startDate, final FieldAbsoluteDate<T> endDate) {
- super(startDate.durationFrom(endDate).getField(), FieldAbstractAnalyticalPropagator.this.getAttitudeProvider());
- super.resetInitialState(FieldAbstractAnalyticalPropagator.this.getInitialState());
- if (startDate.compareTo(endDate) <= 0) {
- minDate = startDate;
- maxDate = endDate;
- } else {
- minDate = endDate;
- maxDate = startDate;
- }
- try {
- // copy the same additional state providers as the original propagator
- for (FieldAdditionalStateProvider<T> provider : FieldAbstractAnalyticalPropagator.this.getAdditionalStateProviders()) {
- addAdditionalStateProvider(provider);
- }
- } catch (OrekitException oe) {
- // as the providers are already compatible with each other,
- // this should never happen
- throw new OrekitInternalError(null);
- }
- }
- /** {@inheritDoc} */
- @Override
- public FieldAbsoluteDate<T> getMinDate() {
- return minDate;
- }
- /** {@inheritDoc} */
- @Override
- public FieldAbsoluteDate<T> getMaxDate() {
- return maxDate;
- }
- /** {@inheritDoc} */
- @Override
- protected FieldOrbit<T> propagateOrbit(final FieldAbsoluteDate<T> target, final T[] parameters) {
- return FieldAbstractAnalyticalPropagator.this.propagateOrbit(target, parameters);
- }
- /** {@inheritDoc} */
- @Override
- public T getMass(final FieldAbsoluteDate<T> date) {
- return FieldAbstractAnalyticalPropagator.this.getMass(date);
- }
- /** {@inheritDoc} */
- @Override
- public void resetInitialState(final FieldSpacecraftState<T> state) {
- super.resetInitialState(state);
- FieldAbstractAnalyticalPropagator.this.resetInitialState(state);
- }
- /** {@inheritDoc} */
- @Override
- protected void resetIntermediateState(final FieldSpacecraftState<T> state, final boolean forward) {
- FieldAbstractAnalyticalPropagator.this.resetIntermediateState(state, forward);
- }
- /** {@inheritDoc} */
- @Override
- public FieldSpacecraftState<T> getInitialState() {
- return FieldAbstractAnalyticalPropagator.this.getInitialState();
- }
- /** {@inheritDoc} */
- @Override
- public Frame getFrame() {
- return FieldAbstractAnalyticalPropagator.this.getFrame();
- }
- /** {@inheritDoc} */
- @Override
- public List<ParameterDriver> getParametersDrivers() {
- return FieldAbstractAnalyticalPropagator.this.getParametersDrivers();
- }
- }
- /** Internal class for local propagation. */
- private class FieldBasicStepInterpolator implements FieldOrekitStepInterpolator<T> {
- /** Previous state. */
- private final FieldSpacecraftState<T> previousState;
- /** Current state. */
- private final FieldSpacecraftState<T> currentState;
- /** Forward propagation indicator. */
- private final boolean forward;
- /** Simple constructor.
- * @param isForward integration direction indicator
- * @param previousState start of the step
- * @param currentState end of the step
- */
- FieldBasicStepInterpolator(final boolean isForward,
- final FieldSpacecraftState<T> previousState,
- final FieldSpacecraftState<T> currentState) {
- this.forward = isForward;
- this.previousState = previousState;
- this.currentState = currentState;
- }
- /** {@inheritDoc} */
- @Override
- public FieldSpacecraftState<T> getPreviousState() {
- return previousState;
- }
- /** {@inheritDoc} */
- @Override
- public FieldSpacecraftState<T> getCurrentState() {
- return currentState;
- }
- /** {@inheritDoc} */
- @Override
- public FieldSpacecraftState<T> getInterpolatedState(final FieldAbsoluteDate<T> date) {
- // compute the basic spacecraft state
- final FieldSpacecraftState<T> basicState = basicPropagate(date);
- // add the additional states
- return updateAdditionalStates(basicState);
- }
- /** {@inheritDoc} */
- @Override
- public boolean isForward() {
- return forward;
- }
- /** {@inheritDoc} */
- @Override
- public FieldBasicStepInterpolator restrictStep(final FieldSpacecraftState<T> newPreviousState,
- final FieldSpacecraftState<T> newCurrentState) {
- return new FieldBasicStepInterpolator(forward, newPreviousState, newCurrentState);
- }
- }
- }