From 5bb906e70c9d06bf636010a0e317fc8e0160978c Mon Sep 17 00:00:00 2001 From: Luc Maisonobe <luc@orekit.org> Date: Tue, 30 Nov 2021 10:50:37 +0100 Subject: [PATCH] Synchronized field propagation with primitive double propagation. --- .../propagation/FieldAbstractPropagator.java | 107 ++---- .../FieldAdditionalStateProvider.java | 82 +++- .../FieldAdditionalStateProviderAdapter.java | 50 --- .../propagation/FieldClosedFormAdapter.java | 50 --- .../orekit/propagation/FieldPropagator.java | 26 -- .../propagation/FieldStackableGenerator.java | 119 ------ .../FieldAbstractAnalyticalPropagator.java | 10 +- .../FieldAbstractIntegratedPropagator.java | 355 ++++++++++++------ .../FieldAdditionalEquationAdapter.java | 82 ---- .../integration/FieldAdditionalEquations.java | 59 ++- .../integration/FieldIntegrableGenerator.java | 49 --- .../integration/FieldIntegratedEphemeris.java | 22 +- .../FieldAdditionalEquationsTest.java | 121 ++++-- .../FieldIntegrableGeneratorTest.java | 164 -------- .../FieldIntegratedEphemerisTest.java | 6 +- .../FieldNumericalPropagatorTest.java | 47 ++- 16 files changed, 559 insertions(+), 790 deletions(-) delete mode 100644 src/main/java/org/orekit/propagation/FieldAdditionalStateProviderAdapter.java delete mode 100644 src/main/java/org/orekit/propagation/FieldClosedFormAdapter.java delete mode 100644 src/main/java/org/orekit/propagation/FieldStackableGenerator.java delete mode 100644 src/main/java/org/orekit/propagation/integration/FieldAdditionalEquationAdapter.java delete mode 100644 src/main/java/org/orekit/propagation/integration/FieldIntegrableGenerator.java delete mode 100644 src/test/java/org/orekit/propagation/integration/FieldIntegrableGeneratorTest.java diff --git a/src/main/java/org/orekit/propagation/FieldAbstractPropagator.java b/src/main/java/org/orekit/propagation/FieldAbstractPropagator.java index 36bec62090..745bba4593 100644 --- a/src/main/java/org/orekit/propagation/FieldAbstractPropagator.java +++ b/src/main/java/org/orekit/propagation/FieldAbstractPropagator.java @@ -17,14 +17,12 @@ package org.orekit.propagation; import java.util.ArrayList; -import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Queue; -import java.util.stream.Collectors; import org.hipparchus.CalculusFieldElement; import org.hipparchus.Field; @@ -33,10 +31,9 @@ import org.orekit.errors.OrekitException; import org.orekit.errors.OrekitMessages; import org.orekit.frames.Frame; import org.orekit.propagation.sampling.FieldStepHandlerMultiplexer; -import org.orekit.time.AbsoluteDate; import org.orekit.time.FieldAbsoluteDate; import org.orekit.utils.FieldArrayDictionary; -import org.orekit.utils.TimeSpanMap; +import org.orekit.utils.FieldTimeSpanMap; import org.orekit.utils.TimeStampedFieldPVCoordinates; /** Common handling of {@link Propagator} methods for analytical propagators. @@ -59,13 +56,11 @@ public abstract class FieldAbstractPropagator<T extends CalculusFieldElement<T>> /** Attitude provider. */ private AttitudeProvider attitudeProvider; - /** Closed form generators. - * @since 11.1 - */ - private final List<FieldStackableGenerator<T>> closedFormGenerators; + /** Additional state providers. */ + private final List<FieldAdditionalStateProvider<T>> additionalStateProviders; - /** States managed by no generators. */ - private final Map<String, TimeSpanMap<T[]>> unmanagedStates; + /** States managed by neither additional equations nor state providers. */ + private final Map<String, FieldTimeSpanMap<T[], T>> unmanagedStates; /** Field used.*/ private final Field<T> field; @@ -77,10 +72,10 @@ public abstract class FieldAbstractPropagator<T extends CalculusFieldElement<T>> * @param field setting the field */ protected FieldAbstractPropagator(final Field<T> field) { - this.field = field; - multiplexer = new FieldStepHandlerMultiplexer<>(); - closedFormGenerators = new ArrayList<>(); - unmanagedStates = new HashMap<>(); + this.field = field; + multiplexer = new FieldStepHandlerMultiplexer<>(); + additionalStateProviders = new ArrayList<>(); + unmanagedStates = new HashMap<>(); } /** Set a start date. @@ -135,47 +130,29 @@ public abstract class FieldAbstractPropagator<T extends CalculusFieldElement<T>> } /** {@inheritDoc} */ - @Deprecated - @Override public void addAdditionalStateProvider(final FieldAdditionalStateProvider<T> additionalStateProvider) { - addClosedFormGenerator(new FieldAdditionalStateProviderAdapter<>(additionalStateProvider)); - } - - /** {@inheritDoc} */ - @SuppressWarnings("deprecation") - @Deprecated - @Override - public List<FieldAdditionalStateProvider<T>> getAdditionalStateProviders() { - return getClosedFormGenerators(). - stream(). - map(u -> new FieldClosedFormAdapter<>(u)). - collect(Collectors.toList()); - } - - /** {@inheritDoc} */ - public void addClosedFormGenerator(final FieldStackableGenerator<T> generator) { // check if the name is already used - if (isAdditionalStateManaged(generator.getName())) { + if (isAdditionalStateManaged(additionalStateProvider.getName())) { // this additional state is already registered, complain throw new OrekitException(OrekitMessages.ADDITIONAL_STATE_NAME_ALREADY_IN_USE, - generator.getName()); + additionalStateProvider.getName()); } // this is really a new name, add it - closedFormGenerators.add(generator); + additionalStateProviders.add(additionalStateProvider); } /** {@inheritDoc} */ - public List<FieldStackableGenerator<T>> getClosedFormGenerators() { - return Collections.unmodifiableList(closedFormGenerators); + public List<FieldAdditionalStateProvider<T>> getAdditionalStateProviders() { + return Collections.unmodifiableList(additionalStateProviders); } /** Update state by adding unmanaged states. * @param original original state * @return updated state, with unmanaged states included - * @see #updateAdditionalStates(FieldSpacecraftState) + * @see #updateAdditionalStates(SpacecraftState) */ protected FieldSpacecraftState<T> updateUnmanagedStates(final FieldSpacecraftState<T> original) { @@ -184,59 +161,45 @@ public abstract class FieldAbstractPropagator<T extends CalculusFieldElement<T>> FieldSpacecraftState<T> updated = original; // update the states not managed by providers - for (final Map.Entry<String, TimeSpanMap<T[]>> entry : unmanagedStates.entrySet()) { + for (final Map.Entry<String, FieldTimeSpanMap<T[], T>> entry : unmanagedStates.entrySet()) { updated = updated.addAdditionalState(entry.getKey(), - entry.getValue().get(original.getDate().toAbsoluteDate())); + entry.getValue().get(original.getDate())); } return updated; } - /** Get all generators. - * @return all generators - * @since 11.1 - */ - protected Collection<FieldStackableGenerator<T>> getAllGenerators() { - return closedFormGenerators; - } - /** Update state by adding all additional states. * @param original original state * @return updated state, with all additional states included - * (including {@link #updateUnmanagedStates(FieldSpacecraftState) unmanaged} states) - * @see #addClosedFormGenerator(FieldStackableGenerator) - * @see #updateUnmanagedStates(FieldSpacecraftState) + * @see #addAdditionalStateProvider(FieldAdditionalStateProvider) */ protected FieldSpacecraftState<T> updateAdditionalStates(final FieldSpacecraftState<T> original) { // start with original state and unmanaged states FieldSpacecraftState<T> updated = updateUnmanagedStates(original); - // set up queue for generators - final Queue<FieldStackableGenerator<T>> pending = new LinkedList<>(getAllGenerators()); + // set up queue for providers + final Queue<FieldAdditionalStateProvider<T>> pending = new LinkedList<>(getAdditionalStateProviders()); - // update the additional states managed by generators, taking care of dependencies + // update the additional states managed by providers, taking care of dependencies int yieldCount = 0; while (!pending.isEmpty()) { - final FieldStackableGenerator<T> generator = pending.remove(); - if (generator.yield(updated)) { + final FieldAdditionalStateProvider<T> provider = pending.remove(); + if (provider.yield(updated)) { // this generator has to wait for another one, // we put it again in the pending queue - pending.add(generator); + pending.add(provider); if (++yieldCount >= pending.size()) { - // all pending generators yielded!, they probably need data not yet initialized + // all pending providers yielded!, they probably need data not yet initialized // we let the propagation proceed, if these data are really needed right now // an appropriate exception will be triggered when caller tries to access them break; } } else { - // we can use this generator right now - if (generator.isClosedForm()) { - updated = updated.addAdditionalState(generator.getName(), generator.generate(updated)); - } else { - updated = updated.addAdditionalStateDerivative(generator.getName(), generator.generate(updated)); - } + // we can use this provider right now + updated = updated.addAdditionalState(provider.getName(), provider.getAdditionalState(updated)); yieldCount = 0; } } @@ -247,7 +210,7 @@ public abstract class FieldAbstractPropagator<T extends CalculusFieldElement<T>> /** {@inheritDoc} */ public boolean isAdditionalStateManaged(final String name) { - for (final FieldStackableGenerator<T> provider : closedFormGenerators) { + for (final FieldAdditionalStateProvider<T> provider : additionalStateProviders) { if (provider.getName().equals(name)) { return true; } @@ -257,9 +220,9 @@ public abstract class FieldAbstractPropagator<T extends CalculusFieldElement<T>> /** {@inheritDoc} */ public String[] getManagedAdditionalStates() { - final String[] managed = new String[closedFormGenerators.size()]; + final String[] managed = new String[additionalStateProviders.size()]; for (int i = 0; i < managed.length; ++i) { - managed[i] = closedFormGenerators.get(i).getName(); + managed[i] = additionalStateProviders.get(i).getName(); } return managed; } @@ -292,7 +255,9 @@ public abstract class FieldAbstractPropagator<T extends CalculusFieldElement<T>> if (!isAdditionalStateManaged(initial.getKey())) { // this additional state is in the initial state, but is unknown to the propagator // we store it in a way event handlers may change it - unmanagedStates.put(initial.getKey(), new TimeSpanMap<>(initial.getValue())); + unmanagedStates.put(initial.getKey(), + new FieldTimeSpanMap<>(initial.getValue(), + initialState.getDate().getField())); } } } @@ -302,10 +267,10 @@ public abstract class FieldAbstractPropagator<T extends CalculusFieldElement<T>> * @param state new state */ protected void stateChanged(final FieldSpacecraftState<T> state) { - final AbsoluteDate date = state.getDate().toAbsoluteDate(); - final boolean forward = date.durationFrom(getStartDate().toAbsoluteDate()) >= 0.0; + final FieldAbsoluteDate<T> date = state.getDate(); + final boolean forward = date.durationFrom(getStartDate()).getReal() >= 0.0; for (final FieldArrayDictionary<T>.Entry changed : state.getAdditionalStatesValues().getData()) { - final TimeSpanMap<T[]> tsm = unmanagedStates.get(changed.getKey()); + final FieldTimeSpanMap<T[], T> tsm = unmanagedStates.get(changed.getKey()); if (tsm != null) { // this is an unmanaged state if (forward) { diff --git a/src/main/java/org/orekit/propagation/FieldAdditionalStateProvider.java b/src/main/java/org/orekit/propagation/FieldAdditionalStateProvider.java index a23906e818..9c0da756d2 100644 --- a/src/main/java/org/orekit/propagation/FieldAdditionalStateProvider.java +++ b/src/main/java/org/orekit/propagation/FieldAdditionalStateProvider.java @@ -20,17 +20,57 @@ import org.hipparchus.CalculusFieldElement; /** This interface represents providers for additional state data beyond {@link SpacecraftState}. * <p> - * This interface is the analytical (read already integrated) counterpart of - * the {@link org.orekit.propagation.integration.AdditionalEquations} interface. - * It allows to append various additional state parameters to any {@link - * org.orekit.propagation.AbstractPropagator abstract propagator}. + * {@link FieldPropagator Propagators} generate {@link fieldSpacecraftState states} that contain at + * least orbit, attitude, and mass. These states may however also contain {@link + * FieldSpacecraftState#addAdditionalState(String, CalculusFieldElement...) additional states}. Instances of classes + * implementing this interface are intended to be registered to propagators so they can add these + * additional states incrementally after having computed the basic components + * (orbit, attitude and mass). * </p> - * @see org.orekit.propagation.AbstractPropagator - * @see org.orekit.propagation.integration.AdditionalEquations + * <p> + * Some additional states may depend on previous additional states to + * be already available the before they can be computed. It may even be impossible to compute some + * of these additional states at some time if they depend on conditions that are fulfilled only + * after propagation as started or some event has occurred. As the propagator builds the complete + * state incrementally, looping over the registered providers, it must call their {@link + * #getAdditionalState(FieldSpacecraftState) getAdditionalState} methods in an order that fulfill these dependencies that + * may be time-dependent and are not related to the order in which the providers are registered to + * the propagator. This reordering is performed each time the complete state is built, using a yield + * mechanism. The propagator first push all providers in a stack and then empty the stack, one provider + * at a time, taking care to select only providers that do <em>not</em> {@link + * #yield(SpacecraftState) yield} when asked. Consider for example a case where providers A, B and C + * have been registered and provider B needs in fact the additional state generated by state C. Then + * when a complete state is built, the propagator puts the three providers, and then starts the incremental + * generation of additional states. It first checks provider A which does not yield so it is popped from + * the stack and the additional state it generates is added. Then provider B is checked, but it yields + * because state from provider C is not yet available. So propagator checks provider C which does not + * yield, so it is popped out of the stack and applied. At this stage, provider B is the only remaining one + * in the stack, so it is checked again, but this time it does not yield because the state from provider + * C is available as it has just been added, so provider B is popped from the stack and applied. The stack + * is now empty and the propagator can return the completed state. + * </p> + * <p> + * It is possible that at some stages in the propagation, a subset of the providers registered to a + * propagator all yied and cannot {@link #getAdditionalState(FieldSpacecraftState) retrieve} their additional + * state. This happens for example during the initialization phase of a propagator that + * compute State Transition Matrices or Jacobian matrices. These features are managed as secondary equations + * in the ODE integrator, and initialized after the primary equations (which correspond to orbit) have + * been initialized. So when the primary equation are initialized, the providers that depend on the secondary + * state will all yield. This behavior is expected. Another case occurs when users set up additional states + * that induces dependency loop (state A depending on state B which depends on state C which depends on + * state A). In this case, the three corresponding providers will wait for each other and indefinitely yield. + * This second case is a deadlock and results from a design error of the additional states management at + * application level. The propagator cannot know it in advance if as subset of providers that all yield is + * normal or not. So at propagator level, when either situation is detected, the propagator just give up and + * returns the most complete state it was able to compute, without generating any error. Errors will indeed + * not be triggered in the first case (once the primary equations have been initialized, the secondary + * equations will be initialized too), and they will be triggered in the second case as soon as user attempts + * to retrieve an additional state that was not added. + * </p> + * @see org.orekit.propagation.FieldPropagator + * @see org.orekit.propagation.integration.FieldAdditionalEquations * @author Luc Maisonobe - * @deprecated as of 11.1, replaced by {@link FieldStackableGenerator} */ -@Deprecated public interface FieldAdditionalStateProvider<T extends CalculusFieldElement<T>> { /** Get the name of the additional state. @@ -38,6 +78,32 @@ public interface FieldAdditionalStateProvider<T extends CalculusFieldElement<T>> */ String getName(); + /** Check if this provider should yield so another provider has an opportunity to add missing parts. + * <p> + * Decision to yield is often based on an additional state being {@link FieldSpacecraftState#hasAdditionalState(String) + * already available} in the provided {@code state} (but it could theoretically also depend on + * an additional state derivative being {@link FieldSpacecraftState#hasAdditionalStateDerivative(String) + * already available}, or any other criterion). If for example a provider needs the state transition + * matrix, it could implement this method as: + * </p> + * <pre>{@code + * public boolean yield(final FieldSpacecraftState state) { + * return !state.getAdditionalStates().containsKey("STM"); + * } + * }</pre> + * <p> + * The default implementation returns {@code false}, meaning that state data can be + * {@link #getAdditionalState(FieldSpacecraftState) generated} immediately. + * </p> + * @param state state to handle + * @return true if this provider should yield so another provider has an opportunity to add missing parts + * as the state is incrementally built up + * @since 11.1 + */ + default boolean yield(FieldSpacecraftState<T> state) { + return false; + } + /** Get the additional state. * @param state spacecraft state to which additional state should correspond * @return additional state corresponding to spacecraft state diff --git a/src/main/java/org/orekit/propagation/FieldAdditionalStateProviderAdapter.java b/src/main/java/org/orekit/propagation/FieldAdditionalStateProviderAdapter.java deleted file mode 100644 index 966e75e9e0..0000000000 --- a/src/main/java/org/orekit/propagation/FieldAdditionalStateProviderAdapter.java +++ /dev/null @@ -1,50 +0,0 @@ -/* Copyright 2002-2021 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; - -import org.hipparchus.CalculusFieldElement; - -/** Adapter from {@link FieldAdditionalStateProvider} to {@link FieldStackableGenerator}. - * @param <T> type of the field elements - * @since 11.1 - * @deprecated this adapter is temporary and will be removed when {@link FieldAdditionalStateProvider} is removed - */ -public class FieldAdditionalStateProviderAdapter<T extends CalculusFieldElement<T>> implements FieldStackableGenerator<T> { - - /** Underlying provider. */ - private final FieldAdditionalStateProvider<T> provider; - - /** Simple constructor. - * @param provider underlying provider - */ - public FieldAdditionalStateProviderAdapter(final FieldAdditionalStateProvider<T> provider) { - this.provider = provider; - } - - /** {@inheritDoc} */ - @Override - public String getName() { - return provider.getName(); - } - - /** {@inheritDoc} */ - @Override - public T[] generate(final FieldSpacecraftState<T> state) { - return provider.getAdditionalState(state); - } - -} diff --git a/src/main/java/org/orekit/propagation/FieldClosedFormAdapter.java b/src/main/java/org/orekit/propagation/FieldClosedFormAdapter.java deleted file mode 100644 index a956cebc8b..0000000000 --- a/src/main/java/org/orekit/propagation/FieldClosedFormAdapter.java +++ /dev/null @@ -1,50 +0,0 @@ -/* Copyright 2002-2021 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; - -import org.hipparchus.CalculusFieldElement; - -/** Adapter from {@link FieldStackableGenerator} to {@link FieldAdditionalStateProvider}. - * @param <T> type of the field elements - * @since 11.1 - * @deprecated this adapter is temporary and will be removed when {@link FieldAdditionalStateProvider} is removed - */ -public class FieldClosedFormAdapter<T extends CalculusFieldElement<T>> implements FieldAdditionalStateProvider<T> { - - /** Underlying generator. */ - private final FieldStackableGenerator<T> generator; - - /** Simple constructor. - * @param generator underlying generator - */ - public FieldClosedFormAdapter(final FieldStackableGenerator<T> generator) { - this.generator = generator; - } - - /** {@inheritDoc} */ - @Override - public String getName() { - return generator.getName(); - } - - /** {@inheritDoc} */ - @Override - public T[] getAdditionalState(final FieldSpacecraftState<T> state) { - return generator.generate(state); - } - -} diff --git a/src/main/java/org/orekit/propagation/FieldPropagator.java b/src/main/java/org/orekit/propagation/FieldPropagator.java index 5f5e0789a2..db14c32c70 100644 --- a/src/main/java/org/orekit/propagation/FieldPropagator.java +++ b/src/main/java/org/orekit/propagation/FieldPropagator.java @@ -18,7 +18,6 @@ package org.orekit.propagation; import java.util.Collection; import java.util.List; -import java.util.stream.Collectors; import org.hipparchus.CalculusFieldElement; import org.orekit.attitudes.AttitudeProvider; @@ -142,39 +141,14 @@ public interface FieldPropagator<T extends CalculusFieldElement<T>> extends Fiel /** Add a set of user-specified state parameters to be computed along with the orbit propagation. * @param additionalStateProvider provider for additional state - * @deprecated as of 11.1, replaced by {@link #addClosedFormGenerator(FieldStackableGenerator)} */ - @Deprecated void addAdditionalStateProvider(FieldAdditionalStateProvider<T> additionalStateProvider); /** Get an unmodifiable list of providers for additional state. * @return providers for the additional states - * @deprecated as of 11.1, replaced by {@link #getClosedFormGenerators()} */ - @Deprecated List<FieldAdditionalStateProvider<T>> getAdditionalStateProviders(); - /** Add a generator for user-specified state parameters to be computed along with the orbit propagation. - * @param generator generator for additional state - * @since 11.1 - */ - @SuppressWarnings("deprecation") - default void addClosedFormGenerator(FieldStackableGenerator<T> generator) { - addAdditionalStateProvider(new FieldClosedFormAdapter<>(generator)); - } - - /** Get an unmodifiable list of generators for additional states. - * @return generators for the additional states - * @since 11.1 - */ - @SuppressWarnings("deprecation") - default List<FieldStackableGenerator<T>> getClosedFormGenerators() { - return getAdditionalStateProviders(). - stream(). - map(asp -> new FieldAdditionalStateProviderAdapter<>(asp)). - collect(Collectors.toList()); - } - /** Check if an additional state is managed. * <p> * Managed states are states for which the propagators know how to compute diff --git a/src/main/java/org/orekit/propagation/FieldStackableGenerator.java b/src/main/java/org/orekit/propagation/FieldStackableGenerator.java deleted file mode 100644 index cf9261bd5f..0000000000 --- a/src/main/java/org/orekit/propagation/FieldStackableGenerator.java +++ /dev/null @@ -1,119 +0,0 @@ -/* Copyright 2002-2021 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; - -import org.hipparchus.CalculusFieldElement; - -/** This interface represents one element of a stack used to build up {@link FieldSpacecraftState states} - * incrementally. - * <p> - * {@link FieldPropagator Propagators} generate {@link FieldSpacecraftState states} that contain at - * least orbit, attitude, and mass. These states may however also contain {@link - * FieldSpacecraftState#addAdditionalState(String, CalculusFieldElement...) additional states} and {@link - * FieldSpacecraftState#addAdditionalStateDerivative(String, CalculusFieldElement...) derivatives}. Instances of classes - * implementing this interface are intended to be registered to propagators so they can add these - * additional states and derivatives incrementally after having computed the basic components - * (orbit, attitude and mass). - * </p> - * <p> - * Some additional states or derivatives may depend on previous additional states or derivatives - * to be already available the before they can be computed. As an example, lets consider Jacobians - * computation. We may managed the state transition matrix ∂y/∂y₀ as one additional state and - * each column of the Jacobian matrix ∂y/∂pₘ corresponding to the partial derivatives of state - * with respect to propagation parameter pₘ (drag coefficient, thrust, coefficient of a parametric - * acceleration…) as separate additional states. Then the generators managing the time derivatives - * d(∂y/∂pₘ)/dt depend on the already integrated state transition matrix ∂y/∂y₀ to be available. - * The propagator builds the complete state incrementally, looping over the registered generators, - * calling their {@link #generate(FieldSpacecraftState) generate} method and pushing the generated data - * into the state before iterating to next generator. At each iteration, it uses the {@link - * #yield(FieldSpacecraftState) yield} method of the candidate generator to check if its {@link - * #generate(FieldSpacecraftState) generate} method can be called at this iteration or if it should be - * postponed later in the loop. This allows to satisfy dependencies requirements easily. - * </p> - * <p> - * It is possible that at some stages in the propagation, a subset of the generators registered to a - * propagator all yied and cannot {@link #generate(FieldSpacecraftState) generate} their additional - * state or derivative. This happens for example when some additional states are must be integrated. - * They are managed as secondary equations in the ODE integrator, and initialized after the primary - * equations (which correspond to orbit) have been initialized. So when the primary - * equation are initialized, the generators that depend on the secondary state will all yield. This - * behavior is expected. Another case occurs when users set up additional states that induces - * dependency loop (state A depending on state B which depends on state C which depends on state A). - * In this case, the three corresponding generators will wait for each other and indefinitely yield. - * This second case is not normal. The propagator cannot know it in advance if as subset of generators - * that all yield is normal or not. So at propagator level, when such a situation is detected, the - * propagator just give up and returns the most complete state it was able to compute, without - * generating any error. Errors will indeed not be triggered in the first case (because once the - * primary equations have been initialized, the secondary equations will be initialized too), and - * they will be triggered in the second case as soon as user attempts to retrieve an additional - * state or derivative that was not added. - * </p> - * @param <T> type of the field elements - * @see FieldPropagator - * @author Luc Maisonobe - * @since 11.1 - */ -public interface FieldStackableGenerator<T extends CalculusFieldElement<T>> { - - /** Get the name of the additional state or derivative {@link #generate(SpacecraftState) generated} - * by the instance. - * @return name of the additional state or derivative - */ - String getName(); - - /** Check if the generated data is a closed form state or a derivative that needs to be integrated. - * @return true if the array {@link #generate(FieldSpacecraftState) generate} returns should be - * {@link FieldSpacecraftState#addAdditionalState(String, CalculusFieldElement...) added as a state}, and false if it should be - * {@link FieldSpacecraftState#addAdditionalStateDerivative(String, CalculusFieldElement...) added as a derivative}. - */ - default boolean isClosedForm() { - return true; - } - - /** Check if this generator should yield so another generator has an opportunity to add missing parts. - * <p> - * Decision to yield is often based on an additional state being {@link FieldSpacecraftState#hasAdditionalState(String) - * already available} in the provided {@code state} (but it could theoretically also depend on - * an additional state derivative being {@link FieldSpacecraftState#hasAdditionalStateDerivative(String) - * already available}, or any other criterion). If for example a generator needs the state transition - * matrix, it could implement this method as: - * </p> - * <pre>{@code - * public boolean yield(final FieldSpacecraftState<T> state) { - * return !state.getAdditionalStates().containsKey("STM"); - * } - * }</pre> - * <p> - * The default implementation returns {@code false}, meaning that state or derivative data can be - * {@link #generate(FieldSpacecraftState) generated} immediately. - * </p> - * @param state state to handle - * @return true if this generator should yield so another generator has an opportunity to add missing parts - * as the state is incrementally built up - */ - default boolean yield(FieldSpacecraftState<T> state) { - return false; - } - - /** Generate an additional state or derivative. - * @param state original state to use - * @return generated state or derivative - * @see #isClosedForm() - */ - T[] generate(FieldSpacecraftState<T> state); - -} diff --git a/src/main/java/org/orekit/propagation/analytical/FieldAbstractAnalyticalPropagator.java b/src/main/java/org/orekit/propagation/analytical/FieldAbstractAnalyticalPropagator.java index ad9598d635..3a3b1cda40 100644 --- a/src/main/java/org/orekit/propagation/analytical/FieldAbstractAnalyticalPropagator.java +++ b/src/main/java/org/orekit/propagation/analytical/FieldAbstractAnalyticalPropagator.java @@ -37,8 +37,8 @@ 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.FieldStackableGenerator; import org.orekit.propagation.FieldEphemerisGenerator; import org.orekit.propagation.FieldSpacecraftState; import org.orekit.propagation.events.FieldEventDetector; @@ -443,12 +443,12 @@ public abstract class FieldAbstractAnalyticalPropagator<T extends CalculusFieldE } try { - // copy the same additional state generators as the original propagator - for (FieldStackableGenerator<T> generator : FieldAbstractAnalyticalPropagator.this.getClosedFormGenerators()) { - addClosedFormGenerator(generator); + // copy the same additional state providers as the original propagator + for (FieldAdditionalStateProvider<T> provider : FieldAbstractAnalyticalPropagator.this.getAdditionalStateProviders()) { + addAdditionalStateProvider(provider); } } catch (OrekitException oe) { - // as the generators are already compatible with each other, + // as the providers are already compatible with each other, // this should never happen throw new OrekitInternalError(null); } diff --git a/src/main/java/org/orekit/propagation/integration/FieldAbstractIntegratedPropagator.java b/src/main/java/org/orekit/propagation/integration/FieldAbstractIntegratedPropagator.java index 619c9d795d..5388401419 100644 --- a/src/main/java/org/orekit/propagation/integration/FieldAbstractIntegratedPropagator.java +++ b/src/main/java/org/orekit/propagation/integration/FieldAbstractIntegratedPropagator.java @@ -17,9 +17,12 @@ package org.orekit.propagation.integration; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collection; import java.util.Collections; +import java.util.HashMap; import java.util.List; +import java.util.Map; import org.hipparchus.CalculusFieldElement; import org.hipparchus.Field; @@ -51,7 +54,6 @@ import org.orekit.propagation.FieldAbstractPropagator; import org.orekit.propagation.FieldBoundedPropagator; import org.orekit.propagation.FieldEphemerisGenerator; import org.orekit.propagation.FieldSpacecraftState; -import org.orekit.propagation.FieldStackableGenerator; import org.orekit.propagation.PropagationType; import org.orekit.propagation.events.FieldEventDetector; import org.orekit.propagation.sampling.FieldOrekitStepHandler; @@ -67,6 +69,11 @@ import org.orekit.utils.FieldArrayDictionary; */ public abstract class FieldAbstractIntegratedPropagator<T extends CalculusFieldElement<T>> extends FieldAbstractPropagator<T> { + /** Internal name used for complete secondary state dimension. + * @since 11.1 + */ + private static final String SECONDARY_DIMENSION = "Orekit-secondary-dimension"; + /** Event detectors not related to force models. */ private final List<FieldEventDetector<T>> detectors; @@ -76,10 +83,13 @@ public abstract class FieldAbstractIntegratedPropagator<T extends CalculusFieldE /** Integrator selected by the user for the orbital extrapolation process. */ private final FieldODEIntegrator<T> integrator; - /** Integrable generators. + /** Offsets of secondary states managed by {@link AdditionalEquations}. * @since 11.1 */ - private final List<FieldIntegrableGenerator<T>> integrableGenerators; + private final Map<String, Integer> secondaryOffsets; + + /** Additional equations. */ + private List<FieldAdditionalEquations<T>> additionalEquations; /** Counter for differential equations calls. */ private int calls; @@ -105,12 +115,13 @@ public abstract class FieldAbstractIntegratedPropagator<T extends CalculusFieldE */ protected FieldAbstractIntegratedPropagator(final Field<T> field, final FieldODEIntegrator<T> integrator, final PropagationType propagationType) { super(field); - detectors = new ArrayList<>(); - ephemerisGenerators = new ArrayList<>(); - integrableGenerators = new ArrayList<>(); - this.integrator = integrator; - this.propagationType = propagationType; - this.resetAtEnd = true; + detectors = new ArrayList<>(); + ephemerisGenerators = new ArrayList<>(); + additionalEquations = new ArrayList<>(); + this.secondaryOffsets = new HashMap<>(); + this.integrator = integrator; + this.propagationType = propagationType; + this.resetAtEnd = true; } /** Allow/disallow resetting the initial state at end of propagation. @@ -227,8 +238,8 @@ public abstract class FieldAbstractIntegratedPropagator<T extends CalculusFieldE } // then look at states we integrate ourselves - for (final FieldIntegrableGenerator<T> generator : integrableGenerators) { - if (generator.getName().equals(name)) { + for (final FieldAdditionalEquations<T> equation : additionalEquations) { + if (equation.getName().equals(name)) { return true; } } @@ -240,56 +251,46 @@ public abstract class FieldAbstractIntegratedPropagator<T extends CalculusFieldE @Override public String[] getManagedAdditionalStates() { final String[] alreadyIntegrated = super.getManagedAdditionalStates(); - final String[] managed = new String[alreadyIntegrated.length + integrableGenerators.size()]; + final String[] managed = new String[alreadyIntegrated.length + additionalEquations.size()]; System.arraycopy(alreadyIntegrated, 0, managed, 0, alreadyIntegrated.length); - for (int i = 0; i < integrableGenerators.size(); ++i) { - managed[i + alreadyIntegrated.length] = integrableGenerators.get(i).getName(); + for (int i = 0; i < additionalEquations.size(); ++i) { + managed[i + alreadyIntegrated.length] = additionalEquations.get(i).getName(); } return managed; } /** Add a set of user-specified equations to be integrated along with the orbit propagation. * @param additional additional equations - * @deprecated as of 11.1, replaced by {@link #addIntegrableGenerator(FieldIntegrableGenerator)} */ - @Deprecated public void addAdditionalEquations(final FieldAdditionalEquations<T> additional) { - addIntegrableGenerator(new FieldAdditionalEquationAdapter<>(additional)); - } - - /** Add an generator for user-specified state parameters to be integrated along with the orbit propagation. - * @param generator generator for additional state - * @see #addClosedFormGenerator(FieldStackableGenerator) - * @since 11.1 - */ - public void addIntegrableGenerator(final FieldIntegrableGenerator<T> generator) { // check if the name is already used - if (isAdditionalStateManaged(generator.getName())) { + if (isAdditionalStateManaged(additional.getName())) { // this set of equations is already registered, complain throw new OrekitException(OrekitMessages.ADDITIONAL_STATE_NAME_ALREADY_IN_USE, - generator.getName()); + additional.getName()); } // this is really a new set of equations, add it - integrableGenerators.add(generator); + // FIXME: the following if statement should be removed in 12.0 + // when the dummy default implementation of AdditionalEquations.getDimension() is removed + if (additional.getDimension() < 0) { + // the additional equation does not implement getDimension by itself, we wrap it + additionalEquations.add(new AdditionalEquationsWrapper(additional)); + } else { + additionalEquations.add(additional); + } + + secondaryOffsets.clear(); } - /** Get an unmodifiable list of generators for additional state. - * @return generators for the additional states + /** Get an unmodifiable list of equations for additional state. + * @return equations for the additional states * @since 11.1 */ - public List<FieldIntegrableGenerator<T>> getIntegrableGenerators() { - return Collections.unmodifiableList(integrableGenerators); - } - - /** {@inheritDoc} */ - @Override - protected Collection<FieldStackableGenerator<T>> getAllGenerators() { - final List<FieldStackableGenerator<T>> allGenerators = new ArrayList<>(super.getAllGenerators()); - allGenerators.addAll(integrableGenerators); - return allGenerators; + public List<FieldAdditionalEquations<T>> getAdditionalEquations() { + return Collections.unmodifiableList(additionalEquations); } /** {@inheritDoc} */ @@ -454,10 +455,16 @@ public abstract class FieldAbstractIntegratedPropagator<T extends CalculusFieldE mathFinalState.getPrimaryState(), mathFinalState.getPrimaryDerivative(), propagationType); - for (int i = 0; i < integrableGenerators.size(); ++i) { - final T[] secondary = mathFinalState.getSecondaryState(i + 1); - finalState = finalState.addAdditionalState(integrableGenerators.get(i).getName(), - secondary); + if (!additionalEquations.isEmpty()) { + final T[] secondary = mathFinalState.getSecondaryState(1); + int offset = 0; + for (FieldAdditionalEquations<T> equations : additionalEquations) { + finalState = finalState.addAdditionalState(equations.getName(), + Arrays.copyOfRange(secondary, + offset, + offset + equations.getDimension())); + offset += equations.getDimension(); + } } finalState = updateAdditionalStates(finalState); @@ -488,28 +495,69 @@ public abstract class FieldAbstractIntegratedPropagator<T extends CalculusFieldE * @param initialState initial state in flight dynamics world * @return initial state in mathematics world */ - @SuppressWarnings("deprecation") private FieldODEState<T> createInitialState(final FieldSpacecraftState<T> initialState) { // retrieve initial state final T[] primary = MathArrays.buildArray(initialState.getA().getField(), getBasicDimension()); stateMapper.mapStateToArray(initialState, primary, null); - // secondary part of the ODE - final T[][] secondary = MathArrays.buildArray(initialState.getA().getField(), integrableGenerators.size(), -1); - for (int i = 0; i < integrableGenerators.size(); ++i) { - final FieldIntegrableGenerator<T> generator = integrableGenerators.get(i); - final T[] addState = getInitialState().getAdditionalState(generator.getName()); - secondary[i] = MathArrays.buildArray(initialState.getA().getField(), addState.length); - for (int j = 0; j < addState.length; j++) { - secondary[i][j] = addState[j]; - } - if (generator instanceof FieldAdditionalEquationAdapter) { - ((FieldAdditionalEquationAdapter<T>) generator).setDimension(secondary[i].length); + if (secondaryOffsets.isEmpty()) { + // compute dimension of the secondary state + int offset = 0; + for (final FieldAdditionalEquations<T> equations : additionalEquations) { + secondaryOffsets.put(equations.getName(), offset); + offset += equations.getDimension(); } + secondaryOffsets.put(SECONDARY_DIMENSION, offset); } - return new FieldODEState<>(initialState.getA().getField().getZero(), primary, secondary); + return new FieldODEState<>(initialState.getA().getField().getZero(), primary, secondary(initialState)); + + } + + /** Create secondary state. + * @param state spacecraft state + * @return secondary state + * @since 11.1 + */ + private T[][] secondary(final FieldSpacecraftState<T> state) { + + if (secondaryOffsets.isEmpty()) { + return null; + } + + final T[][] secondary = MathArrays.buildArray(state.getDate().getField(), 1, secondaryOffsets.get(SECONDARY_DIMENSION)); + for (final FieldAdditionalEquations<T> equations : additionalEquations) { + final String name = equations.getName(); + final int offset = secondaryOffsets.get(name); + final T[] additional = state.getAdditionalState(name); + System.arraycopy(additional, 0, secondary[0], offset, additional.length); + } + + return secondary; + + } + + /** Create secondary state derivative. + * @param state spacecraft state + * @return secondary state derivative + * @since 11.1 + */ + private T[][] secondaryDerivative(final FieldSpacecraftState<T> state) { + + if (secondaryOffsets.isEmpty()) { + return null; + } + + final T[][] secondaryDerivative = MathArrays.buildArray(state.getDate().getField(), 1, secondaryOffsets.get(SECONDARY_DIMENSION)); + for (final FieldAdditionalEquations<T> equations : additionalEquations) { + final String name = equations.getName(); + final int offset = secondaryOffsets.get(name); + final T[] additionalDerivative = state.getAdditionalStateDerivative(name); + System.arraycopy(additionalDerivative, 0, secondaryDerivative[0], offset, additionalDerivative.length); + } + + return secondaryDerivative; } @@ -525,12 +573,8 @@ public abstract class FieldAbstractIntegratedPropagator<T extends CalculusFieldE new FieldExpandableODE<>(new ConvertedMainStateEquations(getMainStateEquations(integ))); // secondary part of the ODE - for (int i = 0; i < integrableGenerators.size(); ++i) { - final FieldIntegrableGenerator<T> generator = integrableGenerators.get(i); - final FieldSecondaryODE<T> secondary = - new ConvertedSecondaryStateEquations(generator, - mathInitialState.getSecondaryStateDimension(i + 1)); - ode.addSecondaryEquations(secondary); + if (!additionalEquations.isEmpty()) { + ode.addSecondaryEquations(new ConvertedSecondaryStateEquations()); } return ode; @@ -578,24 +622,27 @@ public abstract class FieldAbstractIntegratedPropagator<T extends CalculusFieldE * @return space flight dynamics state */ private FieldSpacecraftState<T> convert(final FieldODEStateAndDerivative<T> os) { - try { - FieldSpacecraftState<T> s = - stateMapper.mapArrayToState(os.getTime(), - os.getPrimaryState(), - os.getPrimaryDerivative(), - propagationType); - for (int i = 0; i < integrableGenerators.size(); ++i) { - final T[] secondary = os.getSecondaryState(i + 1); - s = s.addAdditionalState(integrableGenerators.get(i).getName(), secondary); + FieldSpacecraftState<T> s = + stateMapper.mapArrayToState(os.getTime(), + os.getPrimaryState(), + os.getPrimaryDerivative(), + propagationType); + if (os.getNumberOfSecondaryStates() > 0) { + final T[] secondary = os.getSecondaryState(1); + final T[] secondaryDerivative = os.getSecondaryDerivative(1); + for (final FieldAdditionalEquations<T> equations : additionalEquations) { + final String name = equations.getName(); + final int offset = secondaryOffsets.get(name); + final int dimension = equations.getDimension(); + s = s.addAdditionalState(name, Arrays.copyOfRange(secondary, offset, offset + dimension)); + s = s.addAdditionalStateDerivative(name, Arrays.copyOfRange(secondaryDerivative, offset, offset + dimension)); } - s = updateAdditionalStates(s); + } + s = updateAdditionalStates(s); - return s; + return s; - } catch (OrekitException oe) { - throw new OrekitException(oe); - } } /** Convert a state from space flight dynamics world to mathematical world. @@ -610,15 +657,12 @@ public abstract class FieldAbstractIntegratedPropagator<T extends CalculusFieldE stateMapper.mapStateToArray(state, primary, primaryDot); // secondary part of the ODE - final T[][] secondary = MathArrays.buildArray(getField(), integrableGenerators.size(), -1); - for (int i = 0; i < integrableGenerators.size(); ++i) { - final FieldIntegrableGenerator<T> generator = integrableGenerators.get(i); - secondary[i] = state.getAdditionalState(generator.getName()); - } + final T[][] secondary = secondary(state); + final T[][] secondaryDerivative = secondaryDerivative(state); return new FieldODEStateAndDerivative<>(stateMapper.mapDateToDouble(state.getDate()), primary, primaryDot, - secondary, null); + secondary, secondaryDerivative); } @@ -692,26 +736,19 @@ public abstract class FieldAbstractIntegratedPropagator<T extends CalculusFieldE /** Differential equations for the secondary state (Jacobians, user variables ...), with converted API. */ private class ConvertedSecondaryStateEquations implements FieldSecondaryODE<T> { - /** Integrable generator. */ - private final FieldIntegrableGenerator<T> generator; - - /** Dimension of the additional state. */ - private final int dimension; + /** Dimension of the combined additional states. */ + private final int combinedDimension; /** Simple constructor. - * @param generator differential equations in the form of an integrable generator - * @param dimension dimension of the additional state */ - ConvertedSecondaryStateEquations(final FieldIntegrableGenerator<T> generator, - final int dimension) { - this.generator = generator; - this.dimension = dimension; + ConvertedSecondaryStateEquations() { + this.combinedDimension = secondaryOffsets.get(SECONDARY_DIMENSION); } /** {@inheritDoc} */ @Override public int getDimension() { - return dimension; + return combinedDimension; } /** {@inheritDoc} */ @@ -719,11 +756,13 @@ public abstract class FieldAbstractIntegratedPropagator<T extends CalculusFieldE public void init(final T t0, final T[] primary0, final T[] secondary0, final T finalTime) { // update space dynamics view - FieldSpacecraftState<T> initialState = stateMapper.mapArrayToState(t0, primary0, null, PropagationType.MEAN); - initialState = initialState.addAdditionalState(generator.getName(), secondary0); - initialState = updateAdditionalStates(initialState); + final FieldSpacecraftState<T> initialState = convert(t0, primary0, null, secondary0); + final FieldAbsoluteDate<T> target = stateMapper.mapDoubleToDate(finalTime); - generator.init(initialState, target); + for (final FieldAdditionalEquations<T> equations : additionalEquations) { + equations.init(initialState, target); + } + } /** {@inheritDoc} */ @@ -732,11 +771,44 @@ public abstract class FieldAbstractIntegratedPropagator<T extends CalculusFieldE final T[] primaryDot, final T[] secondary) { // update space dynamics view - FieldSpacecraftState<T> currentState = stateMapper.mapArrayToState(t, primary, primaryDot, PropagationType.MEAN); - currentState = currentState.addAdditionalState(generator.getName(), secondary); - currentState = updateAdditionalStates(currentState); + // the integrable generators generate method will be called here, + // according to the generators yield order + final FieldSpacecraftState<T> currentState = convert(t, primary, primaryDot, secondary); + + // gather the derivatives from all integrable generators + final T[] secondaryDot = MathArrays.buildArray(t.getField(), combinedDimension); + // FIXME: use yield to compute derivatives in dependencies order + for (final FieldAdditionalEquations<T> equations : additionalEquations) { + final String name = equations.getName(); + final int offset = secondaryOffsets.get(name); + final int dimension = equations.getDimension(); + System.arraycopy(equations.derivatives(currentState), 0, secondaryDot, offset, dimension); + } + + return secondaryDot; + + } - return currentState.getAdditionalStateDerivative(generator.getName()); + /** Convert mathematical view to space view. + * @param t current value of the independent <I>time</I> variable + * @param primary array containing the current value of the primary state vector + * @param primaryDot array containing the derivative of the primary state vector + * @param secondary array containing the current value of the secondary state vector + * @return space view of the state + */ + private FieldSpacecraftState<T> convert(final T t, final T[] primary, + final T[] primaryDot, final T[] secondary) { + + FieldSpacecraftState<T> initialState = stateMapper.mapArrayToState(t, primary, primaryDot, PropagationType.MEAN); + + for (final FieldAdditionalEquations<T> equations : additionalEquations) { + final String name = equations.getName(); + final int offset = secondaryOffsets.get(name); + final int dimension = equations.getDimension(); + initialState = initialState.addAdditionalState(name, Arrays.copyOfRange(secondary, offset, offset + dimension)); + } + + return updateAdditionalStates(initialState); } @@ -800,13 +872,12 @@ public abstract class FieldAbstractIntegratedPropagator<T extends CalculusFieldE stateMapper.mapStateToArray(newState, primary, null); // secondary part - final T[][] secondary = MathArrays.buildArray(getField(), integrableGenerators.size(), -1); - - for (int i = 0; i < integrableGenerators.size(); ++i) { - final FieldIntegrableGenerator<T> generator = integrableGenerators.get(i); - final T[] si = newState.getAdditionalState(generator.getName()); - secondary[i] = MathArrays.buildArray(getField(), si.length); - System.arraycopy(si, 0, secondary[i], 0, si.length); + final T[][] secondary = MathArrays.buildArray(getField(), 1, additionalEquations.size()); + for (final FieldAdditionalEquations<T> equations : additionalEquations) { + final String name = equations.getName(); + final int offset = secondaryOffsets.get(name); + final int dimension = equations.getDimension(); + System.arraycopy(newState.getAdditionalState(name), 0, secondary[0], offset, dimension); } return new FieldODEState<>(newState.getDate().durationFrom(getStartDate()), @@ -983,15 +1054,15 @@ public abstract class FieldAbstractIntegratedPropagator<T extends CalculusFieldE } // get the names of additional states managed by differential equations - final String[] names = new String[integrableGenerators.size()]; + final String[] names = new String[additionalEquations.size()]; for (int i = 0; i < names.length; ++i) { - names[i] = integrableGenerators.get(i).getName(); + names[i] = additionalEquations.get(i).getName(); } // create the ephemeris ephemeris = new FieldIntegratedEphemeris<>(startDate, minDate, maxDate, stateMapper, propagationType, model, - unmanaged, getClosedFormGenerators(), names); + unmanaged, getAdditionalStateProviders(), names); } @@ -1052,4 +1123,62 @@ public abstract class FieldAbstractIntegratedPropagator<T extends CalculusFieldE } + /** Temporary wrapper for {@link AdditionalEquations} that do not implement getDimension(). + * @since 11.1 + * @deprecated introduced in 11.1 as a temporary workaround for missing getDimension, must be removed in 12.0 + */ + @Deprecated + private class AdditionalEquationsWrapper implements FieldAdditionalEquations<T> { + + /** Wrapped equations. */ + private final FieldAdditionalEquations<T> equations; + + /** Dimension. */ + private int dimension; + + /** Simple constructor. + * @param equations wrapped equations + */ + AdditionalEquationsWrapper(final FieldAdditionalEquations<T> equations) { + this.equations = equations; + this.dimension = -1; + } + + /** {@inheritDoc} */ + @Override + public String getName() { + return equations.getName(); + } + + /** {@inheritDoc} */ + @Override + public int getDimension() { + if (dimension < 0) { + // retrieve the dimension the first time we need it + dimension = getInitialState().getAdditionalState(getName()).length; + } + return dimension; + } + + /** {@inheritDoc} */ + @Override + public boolean yield(final FieldSpacecraftState<T> state) { + return equations.yield(state); + } + + /** {@inheritDoc} */ + @Override + public void init(final FieldSpacecraftState<T> initialState, final FieldAbsoluteDate<T> target) { + equations.init(initialState, target); + } + + /** {@inheritDoc} */ + @Override + public T[] derivatives(final FieldSpacecraftState<T> s) { + final T[] pDot = MathArrays.buildArray(s.getDate().getField(), getDimension()); + computeDerivatives(s, pDot); + return pDot; + } + } + } diff --git a/src/main/java/org/orekit/propagation/integration/FieldAdditionalEquationAdapter.java b/src/main/java/org/orekit/propagation/integration/FieldAdditionalEquationAdapter.java deleted file mode 100644 index ec70ae03cb..0000000000 --- a/src/main/java/org/orekit/propagation/integration/FieldAdditionalEquationAdapter.java +++ /dev/null @@ -1,82 +0,0 @@ -/* Copyright 2002-2021 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.integration; - -import org.hipparchus.CalculusFieldElement; -import org.hipparchus.util.MathArrays; -import org.orekit.propagation.FieldSpacecraftState; -import org.orekit.time.FieldAbsoluteDate; - -/** Adapter from {@link FieldAdditionalEquations} to {@link FieldIntegrableGeneratorTest}. - * @param <T> the type of the field elements - * @since 11.1 - * @deprecated this adapter is temporary and will be removed when {@link FieldAdditionalEquations} is removed - */ -public class FieldAdditionalEquationAdapter<T extends CalculusFieldElement<T>> implements FieldIntegrableGenerator<T> { - - /** Underlying equations. */ - private final FieldAdditionalEquations<T> equations; - - /** Dimension of the equations. */ - private int dimension; - - /** Simple constructor. - * @param equations underlying equations - */ - public FieldAdditionalEquationAdapter(final FieldAdditionalEquations<T> equations) { - this.equations = equations; - } - - /** Set the dimension. - * @param dimension dimension of the equations - */ - void setDimension(final int dimension) { - this.dimension = dimension; - } - - /** {@inheritDoc} */ - @Override - public String getName() { - return equations.getName(); - } - - /** {@inheritDoc} - * <p> - * In order to compute the derivative of state {@link #getName()}, - * we must have the state itself, so we yield if it is not available. - * </p> - */ - @Override - public boolean yield(final FieldSpacecraftState<T> state) { - return !state.hasAdditionalState(getName()); - } - - /** {@inheritDoc} */ - @Override - public void init(final FieldSpacecraftState<T> initialState, final FieldAbsoluteDate<T> target) { - equations.init(initialState, target); - } - - /** {@inheritDoc} */ - @Override - public T[] generate(final FieldSpacecraftState<T> state) { - final T[] yDot = MathArrays.buildArray(state.getDate().getField(), dimension); - equations.computeDerivatives(state, yDot); - return yDot; - } - -} diff --git a/src/main/java/org/orekit/propagation/integration/FieldAdditionalEquations.java b/src/main/java/org/orekit/propagation/integration/FieldAdditionalEquations.java index 8de70df584..e4ff746675 100644 --- a/src/main/java/org/orekit/propagation/integration/FieldAdditionalEquations.java +++ b/src/main/java/org/orekit/propagation/integration/FieldAdditionalEquations.java @@ -17,7 +17,9 @@ package org.orekit.propagation.integration; import org.hipparchus.CalculusFieldElement; +import org.hipparchus.util.MathArrays; import org.orekit.propagation.FieldSpacecraftState; +import org.orekit.propagation.SpacecraftState; import org.orekit.time.FieldAbsoluteDate; /** This interface allows users to add their own differential equations to a numerical propagator. @@ -66,6 +68,41 @@ public interface FieldAdditionalEquations<T extends CalculusFieldElement<T>> { */ String getName(); + /** Get the dimension of the generated derivative. + * @return dimension of the generated + */ + default int getDimension() { + // FIXME: as of 11.1 there is a default implementation that intentionally returns a wrong (negative) size + // the default implementation should be removed in 12.0 + return -1; + } + + /** Check if this provider should yield so another provider has an opportunity to add missing parts. + * <p> + * Decision to yield is often based on an additional state being {@link SpacecraftState#hasAdditionalState(String) + * already available} in the provided {@code state} (but it could theoretically also depend on + * an additional state derivative being {@link SpacecraftState#hasAdditionalStateDerivative(String) + * already available}, or any other criterion). If for example a provider needs the state transition + * matrix, it could implement this method as: + * </p> + * <pre>{@code + * public boolean yield(final SpacecraftState state) { + * return !state.getAdditionalStates().containsKey("STM"); + * } + * }</pre> + * <p> + * The default implementation returns {@code false}, meaning that derivative data can be + * {@link #derivatives(SpacecraftState) computed} immediately. + * </p> + * @param state state to handle + * @return true if this provider should yield so another provider has an opportunity to add missing parts + * as the state is incrementally built up + * @since 11.1 + */ + default boolean yield(FieldSpacecraftState<T> state) { + return false; + } + /** * Initialize the equations at the start of propagation. * @@ -101,7 +138,27 @@ public interface FieldAdditionalEquations<T extends CalculusFieldElement<T>> { * should be put * @return cumulative effect of the equations on the main state (may be null if * equations do not change main state at all) + * @deprecated as of 11.1, replaced by {@link #derivatives(FieldSpacecraftState)} + */ + @Deprecated + default T[] computeDerivatives(FieldSpacecraftState<T> s, T[] pDot) { + return null; + } + + /** Compute the derivatives related to the additional state parameters. + * @param s current state information: date, kinematics, attitude, and + * additional states this equations depend on (according to the + * {@link #yield(SpacecraftState) yield} method) + * @return computed derivatives + * @since 11.1 */ - T[] computeDerivatives(FieldSpacecraftState<T> s, T[] pDot); + default T[] derivatives(FieldSpacecraftState<T> s) { + // FIXME: as of 11.1 there is a default implementation that delegates + // to computeDerivatives. This default implementation should be removed when + // computeDerivatives is removed in 12.0 + final T[] pDot = MathArrays.buildArray(s.getDate().getField(), getDimension()); + computeDerivatives(s, pDot); + return pDot; + } } diff --git a/src/main/java/org/orekit/propagation/integration/FieldIntegrableGenerator.java b/src/main/java/org/orekit/propagation/integration/FieldIntegrableGenerator.java deleted file mode 100644 index 04989429b3..0000000000 --- a/src/main/java/org/orekit/propagation/integration/FieldIntegrableGenerator.java +++ /dev/null @@ -1,49 +0,0 @@ -/* Copyright 2002-2021 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.integration; - -import org.hipparchus.CalculusFieldElement; -import org.orekit.propagation.FieldStackableGenerator; -import org.orekit.propagation.FieldSpacecraftState; -import org.orekit.time.FieldAbsoluteDate; - -/** {@link FieldStackableGenerator Stackable generator} relying on integration. - * @see org.orekit.propagation.Propagator - * @param <T> the type of the field elements - * @author Luc Maisonobe - * @since 11.1 - */ -public interface FieldIntegrableGenerator<T extends CalculusFieldElement<T>> extends FieldStackableGenerator<T> { - - /** {@inheritDoc} - * {@link FieldIntegrableGeneratorTest Integrable generator} are not closed form, - * so this method returns {@code false}. - */ - @Override - default boolean isClosedForm() { - return false; - } - - /** Initialize the generator at the start of propagation. - * @param initialState initial state information at the start of propagation - * @param target date of propagation - */ - default void init(final FieldSpacecraftState<T> initialState, final FieldAbsoluteDate<T> target) { - // nothing by default - } - -} diff --git a/src/main/java/org/orekit/propagation/integration/FieldIntegratedEphemeris.java b/src/main/java/org/orekit/propagation/integration/FieldIntegratedEphemeris.java index 9a435d2746..0ca187c1b5 100644 --- a/src/main/java/org/orekit/propagation/integration/FieldIntegratedEphemeris.java +++ b/src/main/java/org/orekit/propagation/integration/FieldIntegratedEphemeris.java @@ -19,7 +19,6 @@ package org.orekit.propagation.integration; import java.util.Collections; import java.util.List; import java.util.Map; -import java.util.stream.Collectors; import org.hipparchus.CalculusFieldElement; import org.hipparchus.ode.FieldDenseOutputModel; @@ -28,9 +27,9 @@ import org.orekit.errors.OrekitException; import org.orekit.errors.OrekitMessages; import org.orekit.frames.Frame; import org.orekit.orbits.FieldOrbit; +import org.orekit.propagation.FieldAdditionalStateProvider; import org.orekit.propagation.FieldBoundedPropagator; import org.orekit.propagation.FieldSpacecraftState; -import org.orekit.propagation.FieldStackableGenerator; import org.orekit.propagation.PropagationType; import org.orekit.propagation.analytical.FieldAbstractAnalyticalPropagator; import org.orekit.time.FieldAbsoluteDate; @@ -114,7 +113,6 @@ public class FieldIntegratedEphemeris <T extends CalculusFieldElement<T>> * FieldAbsoluteDate, FieldAbsoluteDate, FieldStateMapper, PropagationType, FieldDenseOutputModel, * List, Map, String[]) */ - @SuppressWarnings("deprecation") @Deprecated public FieldIntegratedEphemeris(final FieldAbsoluteDate<T> startDate, final FieldAbsoluteDate<T> minDate, final FieldAbsoluteDate<T> maxDate, @@ -125,7 +123,7 @@ public class FieldIntegratedEphemeris <T extends CalculusFieldElement<T>> final String[] equations) { this(startDate, minDate, maxDate, mapper, type, model, new FieldArrayDictionary<>(startDate.getField(), unmanaged), - providers.stream().map(asp -> new org.orekit.propagation.FieldAdditionalStateProviderAdapter<>(asp)).collect(Collectors.toList()), equations); + providers, equations); } /** Creates a new instance of IntegratedEphemeris. @@ -136,7 +134,7 @@ public class FieldIntegratedEphemeris <T extends CalculusFieldElement<T>> * @param type type of orbit to output (mean or osculating) * @param model underlying raw mathematical model * @param unmanaged unmanaged additional states that must be simply copied - * @param generators generators for pre-integrated states + * @param providers generators for pre-integrated states * @param equations names of additional equations * @since 11.1 */ @@ -145,7 +143,7 @@ public class FieldIntegratedEphemeris <T extends CalculusFieldElement<T>> final FieldStateMapper<T> mapper, final PropagationType type, final FieldDenseOutputModel<T> model, final FieldArrayDictionary<T> unmanaged, - final List<FieldStackableGenerator<T>> generators, + final List<FieldAdditionalStateProvider<T>> providers, final String[] equations) { super(startDate.getField(), mapper.getAttitudeProvider()); @@ -158,14 +156,14 @@ public class FieldIntegratedEphemeris <T extends CalculusFieldElement<T>> this.model = model; this.unmanaged = unmanaged; - // set up the pre-integrated generators - for (final FieldStackableGenerator<T> generator : generators) { - addClosedFormGenerator(generator); + // set up the pre-integrated providers + for (final FieldAdditionalStateProvider<T> provider : providers) { + addAdditionalStateProvider(provider); } // set up providers to map the final elements of the model array to additional states for (int i = 0; i < equations.length; ++i) { - addClosedFormGenerator(new LocalGenerator(equations[i], i)); + addAdditionalStateProvider(new LocalGenerator(equations[i], i)); } } @@ -265,7 +263,7 @@ public class FieldIntegratedEphemeris <T extends CalculusFieldElement<T>> } /** Local generator for additional state data. */ - private class LocalGenerator implements FieldStackableGenerator<T> { + private class LocalGenerator implements FieldAdditionalStateProvider<T> { /** Name of the additional state. */ private final String name; @@ -290,7 +288,7 @@ public class FieldIntegratedEphemeris <T extends CalculusFieldElement<T>> /** {@inheritDoc} */ @Override - public T[] generate(final FieldSpacecraftState<T> state) { + public T[] getAdditionalState(final FieldSpacecraftState<T> state) { // extract the part of the interpolated array corresponding to the additional state return getInterpolatedState(state.getDate()).getSecondaryState(index + 1); diff --git a/src/test/java/org/orekit/propagation/integration/FieldAdditionalEquationsTest.java b/src/test/java/org/orekit/propagation/integration/FieldAdditionalEquationsTest.java index d14adc4d2b..17f85f1681 100644 --- a/src/test/java/org/orekit/propagation/integration/FieldAdditionalEquationsTest.java +++ b/src/test/java/org/orekit/propagation/integration/FieldAdditionalEquationsTest.java @@ -16,8 +16,8 @@ */ package org.orekit.propagation.integration; -import org.hipparchus.Field; import org.hipparchus.CalculusFieldElement; +import org.hipparchus.Field; import org.hipparchus.geometry.euclidean.threed.Vector3D; import org.hipparchus.ode.nonstiff.AdaptiveStepsizeFieldIntegrator; import org.hipparchus.ode.nonstiff.DormandPrince853FieldIntegrator; @@ -38,11 +38,11 @@ import org.orekit.propagation.FieldSpacecraftState; import org.orekit.propagation.SpacecraftState; import org.orekit.propagation.numerical.FieldNumericalPropagator; import org.orekit.propagation.numerical.NumericalPropagator; +import org.orekit.propagation.semianalytical.dsst.FieldDSSTPropagator; import org.orekit.time.AbsoluteDate; import org.orekit.time.FieldAbsoluteDate; import org.orekit.utils.PVCoordinates; -@Deprecated public class FieldAdditionalEquationsTest { private double mu; @@ -57,11 +57,77 @@ public class FieldAdditionalEquationsTest { doTestInitNumerical(Decimal64Field.getInstance()); } + /** Test for issue #401 + * with a DSST propagator */ + @Test + public void testInitDSST() { + doTestInitDSST(Decimal64Field.getInstance()); + } + + @Test + public void testResetStateT() { + doTestResetState(Decimal64Field.getInstance()); + } + private <T extends CalculusFieldElement<T>> void doTestInitNumerical(Field<T> field) { // setup final double reference = 1.25; - InitCheckerEquations<T> checker = new InitCheckerEquations<>(reference); - Assert.assertFalse(checker.wasCalled()); + final double rate = 1.5; + final double dt = 600.0; + Linear<T> linear = new Linear<>("linear", reference, rate); + Assert.assertFalse(linear.wasCalled()); + + // action + AdaptiveStepsizeFieldIntegrator<T> integrator = new DormandPrince853FieldIntegrator<>(field, 0.001, 200, + tolerance[0], tolerance[1]); + integrator.setInitialStepSize(60); + FieldNumericalPropagator<T> propagatorNumerical = new FieldNumericalPropagator<>(field, integrator); + propagatorNumerical.setInitialState(new FieldSpacecraftState<>(field, initialState). + addAdditionalState(linear.getName(), field.getZero().newInstance(reference))); + propagatorNumerical.addAdditionalEquations(linear); + FieldSpacecraftState<T> finalState = propagatorNumerical.propagate(new FieldAbsoluteDate<>(field, initDate).shiftedBy(dt)); + + // verify + Assert.assertTrue(linear.wasCalled()); + Assert.assertEquals(reference + dt * rate, finalState.getAdditionalState(linear.getName())[0].getReal(), 1.0e-10); + + } + + private <T extends CalculusFieldElement<T>> void doTestInitDSST(Field<T> field) { + // setup + final double reference = 3.5; + final double rate = 1.5; + final double dt = 600.0; + Linear<T> linear = new Linear<>("linear", reference, rate); + Assert.assertFalse(linear.wasCalled()); + + // action + AdaptiveStepsizeFieldIntegrator<T> integrator = new DormandPrince853FieldIntegrator<>(field, 0.001, 200, + tolerance[0], tolerance[1]); + integrator.setInitialStepSize(60); + FieldDSSTPropagator<T> propagatorDSST = new FieldDSSTPropagator<>(field, integrator); + propagatorDSST.setInitialState(new FieldSpacecraftState<>(field, initialState). + addAdditionalState(linear.getName(), field.getZero().newInstance(reference))); + propagatorDSST.addAdditionalEquations(linear); + FieldSpacecraftState<T> finalState = propagatorDSST.propagate(new FieldAbsoluteDate<>(field, initDate).shiftedBy(dt)); + + // verify + Assert.assertTrue(linear.wasCalled()); + Assert.assertEquals(reference + dt * rate, finalState.getAdditionalState(linear.getName())[0].getReal(), 1.0e-10); + + } + + private <T extends CalculusFieldElement<T>> void doTestResetState(Field<T> field) { + // setup + final double reference1 = 3.5; + final double rate1 = 1.5; + Linear<T> linear1 = new Linear<>("linear-1", reference1, rate1); + Assert.assertFalse(linear1.wasCalled()); + final double reference2 = 4.5; + final double rate2 = 1.25; + Linear<T> linear2 = new Linear<>("linear-2", reference2, rate2); + Assert.assertFalse(linear2.wasCalled()); + final double dt = 600; // action AdaptiveStepsizeFieldIntegrator<T> integrator = new DormandPrince853FieldIntegrator<>(field, 0.001, 200, @@ -69,12 +135,17 @@ public class FieldAdditionalEquationsTest { integrator.setInitialStepSize(60); FieldNumericalPropagator<T> propagatorNumerical = new FieldNumericalPropagator<>(field, integrator); propagatorNumerical.setInitialState(new FieldSpacecraftState<>(field, initialState). - addAdditionalState(checker.getName(), field.getZero().add(reference))); - propagatorNumerical.addAdditionalEquations(checker); - propagatorNumerical.propagate(new FieldAbsoluteDate<>(field, initDate).shiftedBy(600)); + addAdditionalState(linear1.getName(), field.getZero().newInstance(reference1)). + addAdditionalState(linear2.getName(), field.getZero().newInstance(reference2))); + propagatorNumerical.addAdditionalEquations(linear1); + propagatorNumerical.addAdditionalEquations(linear2); + FieldSpacecraftState<T> finalState = propagatorNumerical.propagate(new FieldAbsoluteDate<>(field, initDate).shiftedBy(dt)); // verify - Assert.assertTrue(checker.wasCalled()); + Assert.assertTrue(linear1.wasCalled()); + Assert.assertTrue(linear2.wasCalled()); + Assert.assertEquals(reference1 + dt * rate1, finalState.getAdditionalState(linear1.getName())[0].getReal(), 1.0e-10); + Assert.assertEquals(reference2 + dt * rate2, finalState.getAdditionalState(linear2.getName())[0].getReal(), 1.0e-10); } @@ -99,33 +170,41 @@ public class FieldAdditionalEquationsTest { tolerance = null; } - public static class InitCheckerEquations<T extends CalculusFieldElement<T>> implements FieldAdditionalEquations<T> { + public static class Linear<T extends CalculusFieldElement<T>> implements FieldAdditionalEquations<T> { - private double expected; + private String name; + private double expectedAtInit; + private double rate; private boolean called; - public InitCheckerEquations(final double expected) { - this.expected = expected; - this.called = false; + public Linear(final String name, final double expectedAtInit, final double rate) { + this.name = name; + this.expectedAtInit = expectedAtInit; + this.rate = rate; + this.called = false; } @Override - public void init(FieldSpacecraftState<T> initiaState, FieldAbsoluteDate<T> target) - { - Assert.assertEquals(expected, initiaState.getAdditionalState(getName())[0].getReal(), 1.0e-15); + public void init(FieldSpacecraftState<T> initiaState, FieldAbsoluteDate<T> target) { + Assert.assertEquals(expectedAtInit, initiaState.getAdditionalState(getName())[0].getReal(), 1.0e-15); called = true; } @Override - public T[] computeDerivatives(FieldSpacecraftState<T> s, T[] pDot) - { - pDot[0] = s.getDate().getField().getZero().add(1.5); - return MathArrays.buildArray(s.getDate().getField(), 7); + public T[] derivatives(FieldSpacecraftState<T> s) { + final T[] pDot = MathArrays.buildArray(s.getDate().getField(), 1); + pDot[0] = s.getDate().getField().getZero().newInstance(rate); + return pDot; + } + + @Override + public int getDimension() { + return 1; } @Override public String getName() { - return "linear"; + return name; } public boolean wasCalled() { diff --git a/src/test/java/org/orekit/propagation/integration/FieldIntegrableGeneratorTest.java b/src/test/java/org/orekit/propagation/integration/FieldIntegrableGeneratorTest.java deleted file mode 100644 index 1f72286e35..0000000000 --- a/src/test/java/org/orekit/propagation/integration/FieldIntegrableGeneratorTest.java +++ /dev/null @@ -1,164 +0,0 @@ -/* Copyright 2002-2021 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.integration; - -import org.hipparchus.CalculusFieldElement; -import org.hipparchus.Field; -import org.hipparchus.geometry.euclidean.threed.Vector3D; -import org.hipparchus.ode.nonstiff.AdaptiveStepsizeFieldIntegrator; -import org.hipparchus.ode.nonstiff.DormandPrince853FieldIntegrator; -import org.hipparchus.util.Decimal64Field; -import org.hipparchus.util.MathArrays; -import org.junit.After; -import org.junit.Assert; -import org.junit.Before; -import org.junit.Test; -import org.orekit.Utils; -import org.orekit.forces.gravity.potential.GravityFieldFactory; -import org.orekit.forces.gravity.potential.SHMFormatReader; -import org.orekit.frames.FramesFactory; -import org.orekit.orbits.EquinoctialOrbit; -import org.orekit.orbits.Orbit; -import org.orekit.orbits.OrbitType; -import org.orekit.propagation.FieldSpacecraftState; -import org.orekit.propagation.SpacecraftState; -import org.orekit.propagation.numerical.FieldNumericalPropagator; -import org.orekit.propagation.numerical.NumericalPropagator; -import org.orekit.propagation.semianalytical.dsst.FieldDSSTPropagator; -import org.orekit.time.AbsoluteDate; -import org.orekit.time.FieldAbsoluteDate; -import org.orekit.utils.PVCoordinates; - -public class FieldIntegrableGeneratorTest { - - private double mu; - private AbsoluteDate initDate; - private SpacecraftState initialState; - private double[][] tolerance; - - /** Test for issue #401 - * with a numerical propagator */ - @Test - public void testInitNumerical() { - doTestInitNumerical(Decimal64Field.getInstance()); - } - - private <T extends CalculusFieldElement<T>> void doTestInitNumerical(Field<T> field) { - // setup - final double reference = 1.25; - InitCheckerEquations<T> checker = new InitCheckerEquations<>(reference); - Assert.assertFalse(checker.wasCalled()); - - // action - AdaptiveStepsizeFieldIntegrator<T> integrator = new DormandPrince853FieldIntegrator<>(field, 0.001, 200, - tolerance[0], tolerance[1]); - integrator.setInitialStepSize(60); - FieldNumericalPropagator<T> propagatorNumerical = new FieldNumericalPropagator<>(field, integrator); - propagatorNumerical.setInitialState(new FieldSpacecraftState<>(field, initialState). - addAdditionalState(checker.getName(), field.getZero().newInstance(reference))); - propagatorNumerical.addIntegrableGenerator(checker); - propagatorNumerical.propagate(new FieldAbsoluteDate<>(field, initDate).shiftedBy(600)); - - // verify - Assert.assertTrue(checker.wasCalled()); - - } - - /** Test for issue #401 - * with a DSST propagator */ - @Test - public void testInitDSST() { - doTestIniDSST(Decimal64Field.getInstance()); - } - - private <T extends CalculusFieldElement<T>> void doTestIniDSST(Field<T> field) { - // setup - final double reference = 3.5; - InitCheckerEquations<T> checker = new InitCheckerEquations<>(reference); - Assert.assertFalse(checker.wasCalled()); - - // action - AdaptiveStepsizeFieldIntegrator<T> integrator = new DormandPrince853FieldIntegrator<>(field, 0.001, 200, - tolerance[0], tolerance[1]); - integrator.setInitialStepSize(60); - FieldDSSTPropagator<T> propagatorDSST = new FieldDSSTPropagator<>(field, integrator); - propagatorDSST.setInitialState(new FieldSpacecraftState<>(field, initialState). - addAdditionalState(checker.getName(), field.getZero().newInstance(reference))); - propagatorDSST.addIntegrableGenerator(checker); - propagatorDSST.propagate(new FieldAbsoluteDate<>(field, initDate).shiftedBy(600)); - - // verify - Assert.assertTrue(checker.wasCalled()); - - } - - @Before - public void setUp() { - Utils.setDataRoot("regular-data:potential/shm-format"); - GravityFieldFactory.addPotentialCoefficientsReader(new SHMFormatReader("^eigen_cg03c_coef$", false)); - mu = GravityFieldFactory.getUnnormalizedProvider(0, 0).getMu(); - final Vector3D position = new Vector3D(7.0e6, 1.0e6, 4.0e6); - final Vector3D velocity = new Vector3D(-500.0, 8000.0, 1000.0); - initDate = AbsoluteDate.J2000_EPOCH; - final Orbit orbit = new EquinoctialOrbit(new PVCoordinates(position, velocity), - FramesFactory.getEME2000(), initDate, mu); - initialState = new SpacecraftState(orbit); - tolerance = NumericalPropagator.tolerances(0.001, orbit, OrbitType.EQUINOCTIAL); - } - - @After - public void tearDown() { - initDate = null; - initialState = null; - tolerance = null; - } - - public static class InitCheckerEquations<T extends CalculusFieldElement<T>> implements FieldIntegrableGenerator<T> { - - private double expected; - private boolean called; - - public InitCheckerEquations(final double expected) { - this.expected = expected; - this.called = false; - } - - @Override - public void init(FieldSpacecraftState<T> initiaState, FieldAbsoluteDate<T> target) { - Assert.assertEquals(expected, initiaState.getAdditionalState(getName())[0].getReal(), 1.0e-15); - called = true; - } - - @Override - public T[] generate(FieldSpacecraftState<T> s) { - final T[] pDot = MathArrays.buildArray(s.getDate().getField(), 1); - pDot[0] = s.getDate().getField().getZero().newInstance(1.5); - return pDot; - } - - @Override - public String getName() { - return "linear"; - } - - public boolean wasCalled() { - return called; - } - - } - -} diff --git a/src/test/java/org/orekit/propagation/integration/FieldIntegratedEphemerisTest.java b/src/test/java/org/orekit/propagation/integration/FieldIntegratedEphemerisTest.java index 6876b75ecc..f50146fc41 100644 --- a/src/test/java/org/orekit/propagation/integration/FieldIntegratedEphemerisTest.java +++ b/src/test/java/org/orekit/propagation/integration/FieldIntegratedEphemerisTest.java @@ -40,10 +40,10 @@ import org.orekit.frames.FramesFactory; import org.orekit.orbits.FieldEquinoctialOrbit; import org.orekit.orbits.FieldOrbit; import org.orekit.orbits.OrbitType; +import org.orekit.propagation.FieldAdditionalStateProvider; import org.orekit.propagation.FieldBoundedPropagator; import org.orekit.propagation.FieldEphemerisGenerator; import org.orekit.propagation.FieldSpacecraftState; -import org.orekit.propagation.FieldStackableGenerator; import org.orekit.propagation.analytical.FieldKeplerianPropagator; import org.orekit.propagation.events.FieldDateDetector; import org.orekit.propagation.numerical.FieldNumericalPropagator; @@ -144,7 +144,7 @@ public class FieldIntegratedEphemerisTest { numericalPropagator.propagate(finalDate); Assert.assertTrue(numericalPropagator.getCalls() < 3200); FieldBoundedPropagator<T> ephemeris = generator.getGeneratedEphemeris(); - ephemeris.addClosedFormGenerator(new FieldStackableGenerator<T>() { + ephemeris.addAdditionalStateProvider(new FieldAdditionalStateProvider<T>() { @Override public String getName() { @@ -152,7 +152,7 @@ public class FieldIntegratedEphemerisTest { } @Override - public T[] generate(FieldSpacecraftState<T> state) { + public T[] getAdditionalState(FieldSpacecraftState<T> state) { T[] array = MathArrays.buildArray(state.getDate().getField(), 1); array[0] = state.getDate().durationFrom(initialOrbit.getDate()); return array; diff --git a/src/test/java/org/orekit/propagation/numerical/FieldNumericalPropagatorTest.java b/src/test/java/org/orekit/propagation/numerical/FieldNumericalPropagatorTest.java index 4acafde50b..8185c40a20 100644 --- a/src/test/java/org/orekit/propagation/numerical/FieldNumericalPropagatorTest.java +++ b/src/test/java/org/orekit/propagation/numerical/FieldNumericalPropagatorTest.java @@ -65,7 +65,7 @@ import org.orekit.orbits.PositionAngle; import org.orekit.propagation.FieldBoundedPropagator; import org.orekit.propagation.FieldEphemerisGenerator; import org.orekit.propagation.FieldSpacecraftState; -import org.orekit.propagation.FieldStackableGenerator; +import org.orekit.propagation.FieldAdditionalStateProvider; import org.orekit.propagation.events.FieldAbstractDetector; import org.orekit.propagation.events.FieldApsideDetector; import org.orekit.propagation.events.FieldDateDetector; @@ -74,7 +74,7 @@ import org.orekit.propagation.events.handlers.FieldContinueOnEvent; import org.orekit.propagation.events.handlers.FieldEventHandler; import org.orekit.propagation.events.handlers.FieldStopOnEvent; import org.orekit.propagation.integration.FieldAbstractIntegratedPropagator; -import org.orekit.propagation.integration.FieldIntegrableGenerator; +import org.orekit.propagation.integration.FieldAdditionalEquations; import org.orekit.propagation.sampling.FieldOrekitStepHandler; import org.orekit.propagation.sampling.FieldOrekitStepInterpolator; import org.orekit.time.FieldAbsoluteDate; @@ -872,26 +872,34 @@ public class FieldNumericalPropagatorTest { propagator.setOrbitType(type); propagator.setInitialState(initialState); - propagator.addIntegrableGenerator(new FieldIntegrableGenerator<T>() { + propagator.addAdditionalEquations(new FieldAdditionalEquations<T>() { public String getName() { return "linear"; } - public T[] generate(FieldSpacecraftState<T> s) { + public int getDimension() { + return 1; + } + + public T[] derivatives(FieldSpacecraftState<T> s) { T[] pDot = MathArrays.buildArray(field, 1); pDot[0] = field.getOne(); return pDot; } }); try { - propagator.addIntegrableGenerator(new FieldIntegrableGenerator<T>() { + propagator.addAdditionalEquations(new FieldAdditionalEquations<T>() { public String getName() { return "linear"; } - public T[] generate(FieldSpacecraftState<T> s) { + public int getDimension() { + return 1; + } + + public T[] derivatives(FieldSpacecraftState<T> s) { T[] pDot = MathArrays.buildArray(field, 1); pDot[0] = field.getOne(); return pDot; @@ -902,12 +910,12 @@ public class FieldNumericalPropagatorTest { Assert.assertEquals(oe.getSpecifier(), OrekitMessages.ADDITIONAL_STATE_NAME_ALREADY_IN_USE); } try { - propagator.addClosedFormGenerator(new FieldStackableGenerator<T>() { + propagator.addAdditionalStateProvider(new FieldAdditionalStateProvider<T>() { public String getName() { return "linear"; } - public T[] generate(FieldSpacecraftState<T> state) { + public T[] getAdditionalState(FieldSpacecraftState<T> state) { return null; } }); @@ -915,12 +923,12 @@ public class FieldNumericalPropagatorTest { } catch (OrekitException oe) { Assert.assertEquals(oe.getSpecifier(), OrekitMessages.ADDITIONAL_STATE_NAME_ALREADY_IN_USE); } - propagator.addClosedFormGenerator(new FieldStackableGenerator<T>() { + propagator.addAdditionalStateProvider(new FieldAdditionalStateProvider<T>() { public String getName() { return "constant"; } - public T[] generate(FieldSpacecraftState<T> state) { + public T[] getAdditionalState(FieldSpacecraftState<T> state) { T[] ret = MathArrays.buildArray(field, 1); ret[0] = zero.add(1.0); return ret; @@ -980,13 +988,17 @@ public class FieldNumericalPropagatorTest { FieldNumericalPropagator<T> propagator = createPropagator(field); - propagator.addIntegrableGenerator(new FieldIntegrableGenerator<T>() { + propagator.addAdditionalEquations(new FieldAdditionalEquations<T>() { public String getName() { return "linear"; } - public T[] generate(FieldSpacecraftState<T> s) { + public int getDimension() { + return 1; + } + + public T[] derivatives(FieldSpacecraftState<T> s) { T[] pDot = MathArrays.buildArray(field, 1); pDot[0] = field.getOne(); return pDot; @@ -1133,21 +1145,24 @@ public class FieldNumericalPropagatorTest { FieldNumericalPropagator<T> propagator = createPropagator(field); FieldAbsoluteDate<T> initDate = propagator.getInitialState().getDate(); - propagator.addClosedFormGenerator(new FieldStackableGenerator<T>() { + propagator.addAdditionalStateProvider(new FieldAdditionalStateProvider<T>() { public String getName() { return "squaredA"; } - public T[] generate(FieldSpacecraftState<T> state) { + public T[] getAdditionalState(FieldSpacecraftState<T> state) { T[] a = MathArrays.buildArray(field, 1); a[0] = state.getA().multiply(state.getA()); return a; } }); - propagator.addIntegrableGenerator(new FieldIntegrableGenerator<T>() { + propagator.addAdditionalEquations(new FieldAdditionalEquations<T>() { public String getName() { return "extra"; } - public T[] generate(FieldSpacecraftState<T> s) { + public int getDimension() { + return 1; + } + public T[] derivatives(FieldSpacecraftState<T> s) { T[] pDot = MathArrays.buildArray(field, 1); pDot[0] = field.getZero().newInstance(rate); return pDot; -- GitLab