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

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.
......
......@@ -687,6 +687,209 @@ public abstract class CloseEventsAbstractTest {
Assert.assertSame(detectorC, events.get(1).getDetector());
}
/**
* test when one event detector changes the definition of another's g function before
* the end of the step as a result of an event occurring. In this case the change
* cancels the occurrence of the event.
*/
@Test
public void testEventChangesGFunctionDefinitionCancel() {
// setup
double maxCheck = 5;
double tolerance = 1e-6;
double t1 = 11, t2 = 11.1;
// shared event list so we know the order in which they occurred
List<Event<EventDetector>> events = new ArrayList<>();
// mutable boolean
boolean[] swap = new boolean[1];
final ContinuousDetector detectorA = new ContinuousDetector(t1)
.withMaxCheck(maxCheck)
.withThreshold(tolerance)
.withHandler(new RecordAndContinue<EventDetector>(events) {
@Override
public Action eventOccurred(SpacecraftState state,
EventDetector detector,
boolean increasing) {
swap[0] = true;
super.eventOccurred(state, detector, increasing);
return Action.RESET_EVENTS;
}
});
final ContinuousDetector detectorB = new ContinuousDetector(t2);
EventDetector detectorC = new AbstractDetector<EventDetector>
(maxCheck, tolerance, 100, new RecordAndContinue<>(events)) {
@Override
public double g(SpacecraftState state) {
if (!swap[0]) {
return detectorB.g(state);
} else {
return -1;
}
}
@Override
protected EventDetector create(
double newMaxCheck,
double newThreshold,
int newMaxIter,
EventHandler<? super EventDetector> newHandler) {
return null;
}
};
Propagator propagator = getPropagator(10);
propagator.addEventDetector(detectorA);
propagator.addEventDetector(detectorC);
// action
propagator.propagate(epoch.shiftedBy(30));
// verify
Assert.assertEquals(1, events.size());
Assert.assertEquals(t1, events.get(0).getState().getDate().durationFrom(epoch), tolerance);
Assert.assertEquals(true, events.get(0).isIncreasing());
Assert.assertSame(detectorA, events.get(0).getDetector());
}
/**
* test when one event detector changes the definition of another's g function before
* the end of the step as a result of an event occurring. In this case the change
* delays the occurrence of the event.
*/
@Test
public void testEventChangesGFunctionDefinitionDelay() {
// setup
double maxCheck = 5;
double tolerance = 1e-6;
double t1 = 11, t2 = 11.1, t3 = 11.2;
// shared event list so we know the order in which they occurred
List<Event<EventDetector>> events = new ArrayList<>();
// mutable boolean
boolean[] swap = new boolean[1];
final ContinuousDetector detectorA = new ContinuousDetector(t1)
.withMaxCheck(maxCheck)
.withThreshold(tolerance)
.withHandler(new RecordAndContinue<EventDetector>(events) {
@Override
public Action eventOccurred(SpacecraftState state,
EventDetector detector,
boolean increasing) {
swap[0] = true;
super.eventOccurred(state, detector, increasing);
return Action.RESET_EVENTS;
}
});
final ContinuousDetector detectorB = new ContinuousDetector(t2);
final ContinuousDetector detectorD = new ContinuousDetector(t3);
EventDetector detectorC = new AbstractDetector<EventDetector>
(maxCheck, tolerance, 100, new RecordAndContinue<>(events)) {
@Override
public double g(SpacecraftState state) {