Commit 956e81d3 authored by Evan Ward's avatar Evan Ward

Add Action.RESET_EVENTS

Add an action to re-check all event detectors without recomputing the step.
Upgraded Hipparchus to 1.5-SNAPSHOT in order to use the corresponding
RESET_EVENTS feature in Hipparchus.

Fixes #507
parent 07afe5fb
......@@ -46,7 +46,7 @@
<orekit.nexus-staging-maven-plugin.version>1.6.8</orekit.nexus-staging-maven-plugin.version>
<orekit.maven-gpg-plugin.version>1.6</orekit.maven-gpg-plugin.version>
<orekit.maven-install-plugin.version>3.0.0-M1</orekit.maven-install-plugin.version>
<orekit.hipparchus.version>1.4</orekit.hipparchus.version>
<orekit.hipparchus.version>1.5-SNAPSHOT</orekit.hipparchus.version>
<orekit.junit.version>4.12</orekit.junit.version>
<orekit.compiler.source>1.8</orekit.compiler.source>
<orekit.compiler.target>1.8</orekit.compiler.target>
......
......@@ -192,6 +192,8 @@ public abstract class AbstractAnalyticalPropagator extends AbstractPropagator {
SpacecraftState previous = interpolator.getPreviousState();
final SpacecraftState current = interpolator.getCurrentState();
OrekitStepInterpolator restricted = interpolator;
// initialize the events states if needed
if (!statesInitialized) {
......@@ -217,96 +219,111 @@ public abstract class AbstractAnalyticalPropagator extends AbstractPropagator {
}
});
for (final EventState<?> state : eventsStates) {
if (state.evaluateStep(interpolator)) {
// the event occurs during the current step
occurringEvents.add(state);
}
}
OrekitStepInterpolator restricted = interpolator;
boolean doneWithStep = false;
resetEvents:
do {
eventLoop:
while (!occurringEvents.isEmpty()) {
// Evaluate all event detectors for events
occurringEvents.clear();
for (final EventState<?> state : eventsStates) {
if (state.evaluateStep(interpolator)) {
// the event occurs during the current step
occurringEvents.add(state);
}
}
// handle the chronologically first event
final EventState<?> currentEvent = occurringEvents.poll();
do {
// get state at event time
SpacecraftState eventState = restricted.getInterpolatedState(currentEvent.getEventDate());
eventLoop:
while (!occurringEvents.isEmpty()) {
// handle the chronologically first event
final EventState<?> currentEvent = occurringEvents.poll();
// get state at event time
SpacecraftState eventState = restricted.getInterpolatedState(currentEvent.getEventDate());
// try to advance all event states to current time
for (final EventState<?> 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
final EventOccurrence 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.
eventState = interpolator.getInterpolatedState(occurrence.getStopDate());
restricted = restricted.restrictStep(previous, eventState);
}
// try to advance all event states to current time
for (final EventState<?> 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;
// handle the first part of the step, up to the event
if (getStepHandler() != null) {
getStepHandler().handleStep(restricted, isLastStep);
}
}
// all event detectors agree we can advance to the current event time
final EventOccurrence 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.
eventState = interpolator.getInterpolatedState(occurrence.getStopDate());
restricted = restricted.restrictStep(previous, eventState);
}
// handle the first part of the step, up to the event
if (getStepHandler() != null) {
getStepHandler().handleStep(restricted, isLastStep);
}
if (isLastStep) {
// the event asked to stop integration
return eventState;
}
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 SpacecraftState resetState = occurrence.getNewState();
if (resetState != null) {
resetIntermediateState(resetState, interpolator.isForward());
return resetState;
}
}
// at this point action == Action.CONTINUE or Action.RESET_EVENTS
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 SpacecraftState resetState = occurrence.getNewState();
if (resetState != null) {
resetIntermediateState(resetState, interpolator.isForward());
return resetState;
// prepare handling of the remaining part of the step
previous = eventState;
restricted = new BasicStepInterpolator(restricted.isForward(), eventState, current);
if (action == Action.RESET_EVENTS) {
continue resetEvents;
}
}
// at this point we know action == Action.CONTINUE
// prepare handling of the remaining part of the step
previous = eventState;
restricted = new BasicStepInterpolator(restricted.isForward(), eventState, current);
// at this point 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);
}
// 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
// may be a new event here if the last event modified the g function of
// another event detector.
for (final EventState<?> state : eventsStates) {
if (state.tryAdvance(current, interpolator)) {
occurringEvents.add(state);
// 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 EventState<?> state : eventsStates) {
if (state.tryAdvance(current, interpolator)) {
occurringEvents.add(state);
}
}
}
} while (!occurringEvents.isEmpty());
} while (!occurringEvents.isEmpty());
doneWithStep = true;
} while (!doneWithStep);
isLastStep = target.equals(current.getDate());
......
......@@ -190,6 +190,8 @@ public abstract class FieldAbstractAnalyticalPropagator<T extends RealFieldEleme
FieldSpacecraftState<T> previous = interpolator.getPreviousState();
final FieldSpacecraftState<T> current = interpolator.getCurrentState();
FieldBasicStepInterpolator restricted = interpolator;
// initialize the events states if needed
if (!statesInitialized) {
......@@ -212,89 +214,104 @@ public abstract class FieldAbstractAnalyticalPropagator<T extends RealFieldEleme
return orderingSign * es0.getEventDate().compareTo(es1.getEventDate());
}
});
for (final FieldEventState<?, T> state : eventsStates) {
if (state.evaluateStep(interpolator)) {
// the event occurs during the current step
occurringEvents.add(state);
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());
// 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
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.
eventState = interpolator.getInterpolatedState(occurrence.getStopDate());
restricted = new FieldBasicStepInterpolator(restricted.isForward(), previous, eventState);
}
// handle the first part of the step, up to the event
if (getStepHandler() != null) {
getStepHandler().handleStep(restricted, isLastStep);
}
FieldBasicStepInterpolator restricted = interpolator;
if (isLastStep) {
// the event asked to stop integration
return eventState;
}
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());
// 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
occurringEvents.add(currentEvent);
occurringEvents.remove(state); // remove if already has pending event
occurringEvents.add(state);
continue eventLoop;
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();
if (resetState != null) {
resetIntermediateState(resetState, interpolator.isForward());
return resetState;
}
}
}
// all event detectors agree we can advance to the current event time
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.
eventState = interpolator.getInterpolatedState(occurrence.getStopDate());
restricted = new FieldBasicStepInterpolator(restricted.isForward(), previous, eventState);
}
// handle the first part of the step, up to the event
if (getStepHandler() != null) {
getStepHandler().handleStep(restricted, isLastStep);
}
// at this point action == Action.CONTINUE or Action.RESET_EVENTS
if (isLastStep) {
// the event asked to stop integration
return eventState;
}
// prepare handling of the remaining part of the step
previous = eventState;
restricted = new FieldBasicStepInterpolator(restricted.isForward(), eventState, current);
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();
if (resetState != null) {
resetIntermediateState( resetState, interpolator.isForward());
return resetState;
if (action == Action.RESET_EVENTS) {
continue resetEvents;
}
}
// at this point we know action == Action.CONTINUE
// prepare handling of the remaining part of the step
previous = eventState;
restricted = new FieldBasicStepInterpolator(restricted.isForward(), eventState, current);
// 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);
}
// 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
// may be a new event here if the last event modified the g function of
// another event detector.
for (final FieldEventState<?, T> state : eventsStates) {
if (state.tryAdvance(current, interpolator)) {
occurringEvents.add(state);
// last part of the step, after the last event
// may be a new event here if the last event modified the g function of
// another event detector.
for (final FieldEventState<?, T> state : eventsStates) {
if (state.tryAdvance(current, interpolator)) {
occurringEvents.add(state);
}
}
}
} while (!occurringEvents.isEmpty());
} while (!occurringEvents.isEmpty());
doneWithStep = true;
} while (!doneWithStep);
final T remaining = target.durationFrom(current.getDate());
if (interpolator.isForward()) {
......
......@@ -67,7 +67,19 @@ public interface EventHandler<T extends EventDetector> {
* #eventOccurred eventOccurred} method when the propagation should go
* on after the event ending the current step.</p>
*/
CONTINUE;
CONTINUE,
/**
* Reset events indicator.
*
* <p> This value should be used as the return value of the {@code eventOccurred}
* method when the integration should go on, but first recheck all event detectors
* for occurring events. Use when the {@link EventDetector#eventOccurred(SpacecraftState,
* boolean)} method of one event detector has a side effect that changes the
* {@link EventDetector#g(SpacecraftState)} function of another event detector.
*/
RESET_EVENTS;
}
......
......@@ -69,7 +69,19 @@ public interface FieldEventHandler<KK extends FieldEventDetector<T>, T extends R
* #eventOccurred eventOccurred} method when the propagation should go
* on after the event ending the current step.</p>
*/
CONTINUE;
CONTINUE,
/**
* Reset events indicator.
*
* <p> This value should be used as the return value of the {@code eventOccurred}
* method when the integration should go on, but first recheck all event detectors
* for occurring events. Use when the {@link FieldEventDetector#eventOccurred(FieldSpacecraftState,
* boolean)} method of one event detector has a side effect that changes the
* {@link FieldEventDetector#g(FieldSpacecraftState)} function of another event
* detector.
*/
RESET_EVENTS;
}
......
......@@ -795,6 +795,8 @@ public abstract class AbstractIntegratedPropagator extends AbstractPropagator {
return Action.RESET_STATE;
case RESET_DERIVATIVES :
return Action.RESET_DERIVATIVES;
case RESET_EVENTS:
return Action.RESET_EVENTS;
default :
return Action.CONTINUE;
}
......
......@@ -810,6 +810,8 @@ public abstract class FieldAbstractIntegratedPropagator<T extends RealFieldEleme
return Action.RESET_STATE;
case RESET_DERIVATIVES :
return Action.RESET_DERIVATIVES;
case RESET_EVENTS:
return Action.RESET_EVENTS;
default :
return Action.CONTINUE;
}
......
......@@ -21,6 +21,10 @@
</properties>
<body>
<release version="TBD" date="TBD" description="TBD">
<action dev="evan" type="add" issue="507">
Add Action.RESET_EVENTS to check all detectors for events without recomputing the
propagation step.
</action>
<action dev="evan" type="add" issue="507">
Add toString() implementations to SpacecraftState, RecordAndContinue.Event and
Field versions.
......
/*
* Licensed to the Hipparchus project under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF 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.events;
import org.hipparchus.Field;
import org.hipparchus.util.Decimal64;
import org.hipparchus.util.Decimal64Field;
import org.orekit.propagation.FieldPropagator;
import org.orekit.propagation.Propagator;
import org.orekit.propagation.analytical.FieldKeplerianPropagator;
import org.orekit.propagation.analytical.KeplerianPropagator;
/**
* Test event handling on a {@link KeplerianPropagator}.
*
* @author Evan Ward
*/
public class FieldCloseEventsAnalyticalKeplerianTest extends FieldCloseEventsAbstractTest<Decimal64> {
public FieldCloseEventsAnalyticalKeplerianTest(){
super(Decimal64Field.getInstance());
}
@Override
public FieldPropagator<Decimal64> getPropagator(double stepSize) {
return new FieldKeplerianPropagator<>(initialOrbit);
}
}
/*
* Licensed to the Hipparchus project under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF 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.events;
import org.hipparchus.ode.nonstiff.AdamsBashforthFieldIntegrator;
import org.hipparchus.ode.nonstiff.DormandPrince853FieldIntegrator;
import org.hipparchus.util.Decimal64;
import org.hipparchus.util.Decimal64Field;
import org.orekit.orbits.OrbitType;
import org.orekit.propagation.FieldPropagator;
import org.orekit.propagation.FieldSpacecraftState;
import org.orekit.propagation.numerical.FieldNumericalPropagator;
/**
* Test event handling with a {@link FieldNumericalPropagator} and a {@link
* AdamsBashforthFieldIntegrator}.
*
* @author Evan Ward
*/
public class FieldCloseEventsNumericalABTest extends FieldCloseEventsAbstractTest<Decimal64> {
/** Constructor. */
public FieldCloseEventsNumericalABTest() {
super(Decimal64Field.getInstance());
}
/**
* Create a propagator using the {@link #initialOrbit}.
*
* @param stepSize of integrator.
* @return a usable propagator.
*/
public FieldPropagator<Decimal64> getPropagator(double stepSize) {
double[][] tol = FieldNumericalPropagator
.tolerances(v(10000), initialOrbit, OrbitType.CARTESIAN);
final AdamsBashforthFieldIntegrator<Decimal64> integrator =
new AdamsBashforthFieldIntegrator<>(field, 4, stepSize, stepSize, tol[0], tol[1]);
final DormandPrince853FieldIntegrator<Decimal64> starter =
new DormandPrince853FieldIntegrator<>(
field, stepSize / 100, stepSize / 10, tol[0], tol[1]);
starter.setInitialStepSize(v(stepSize / 20));
integrator.setStarterIntegrator(starter);
final FieldNumericalPropagator<Decimal64> propagator =
new FieldNumericalPropagator<>(field, integrator);
propagator.setInitialState(new FieldSpacecraftState<>(initialOrbit));
propagator.setOrbitType(OrbitType.CARTESIAN);
return propagator;
}
}
/*
* Licensed to the Hipparchus project under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF 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.events;
import org.hipparchus.ode.nonstiff.AdamsBashforthIntegrator;
import org.hipparchus.ode.nonstiff.AdamsMoultonFieldIntegrator;
import org.hipparchus.ode.nonstiff.DormandPrince853FieldIntegrator;
import org.hipparchus.util.Decimal64;
import org.hipparchus.util.Decimal64Field;
import org.orekit.orbits.OrbitType;
import org.orekit.propagation.FieldPropagator;
import org.orekit.propagation.FieldSpacecraftState;
import org.orekit.propagation.SpacecraftState;
import org.orekit.propagation.numerical.FieldNumericalPropagator;
import org.orekit.propagation.numerical.NumericalPropagator;
/**
* Test event handling with a {@link NumericalPropagator} and a {@link
* AdamsBashforthIntegrator}.
*
* @author Evan Ward
*/
public class FieldCloseEventsNumericalAMTest extends FieldCloseEventsAbstractTest<Decimal64> {
/** Constructor. */
public FieldCloseEventsNumericalAMTest() {
super(Decimal64Field.getInstance());
}
/**
* Create a propagator using the {@link #initialOrbit}.
*
* @param stepSize of integrator.
* @return a usable propagator.
*/
public FieldPropagator<Decimal64> getPropagator(double stepSize) {
double[][] tol = FieldNumericalPropagator
.tolerances(v(1), initialOrbit, OrbitType.CARTESIAN);
final AdamsMoultonFieldIntegrator<Decimal64> integrator =
new AdamsMoultonFieldIntegrator<>(field, 4, stepSize, stepSize, tol[0], tol[1]);
final DormandPrince853FieldIntegrator<Decimal64> starter =
new DormandPrince853FieldIntegrator<>(
field, stepSize / 100, stepSize / 10, tol[0], tol[1]);
starter.setInitialStepSize(v(stepSize / 20));
integrator.setStarterIntegrator(starter);
final FieldNumericalPropagator<Decimal64> propagator = new FieldNumericalPropagator<>(field, integrator);
propagator.setInitialState(new FieldSpacecraftState<>(initialOrbit));
propagator.setOrbitType(OrbitType.CARTESIAN);
return propagator;
}
}
/*
* Licensed to the Hipparchus project under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF 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.events;
import org.hipparchus.ode.nonstiff.DormandPrince853FieldIntegrator;
import org.hipparchus.ode.nonstiff.DormandPrince853Integrator;
import org.hipparchus.util.Decimal64;
import org.hipparchus.util.Decimal64Field;
import org.orekit.orbits.OrbitType;
import org.orekit.propagation.FieldPropagator;
import org.orekit.propagation.FieldSpacecraftState;