throws IllegalActionException {
CompositeActor container = (CompositeActor) getContainer();
Director executiveDirector = ((Actor) container).getExecutiveDirector();
// Check the enclosing model time against the local model time.
Time outTime = executiveDirector.getModelTime();
int comparison = _currentTime.compareTo(outTime);
if (comparison > 0) {
// Local current time exceeds that of the environment.
// We need to roll back. Make sure this is allowable.
if (!_commitIsPending) {
throw new IllegalActionException(this, "The model time of "
+ container.getFullName()
+ " is greater than the environment time. "
+ "Environment: " + outTime
+ ", the model time (iteration begin time): "
+ _currentTime);
}
// If we get here, local time exceeds the environment time
// and we have speculatively executed up to that local time.
// But now, we cannot commit that execution because an unexpected
// event has occurred. Instead, we have to re-do the integration
// with a smaller step size that brings us up to the current
// environment time.
// NOTE: This depends on the property that if an integration
// step with a larger step size was successful and produced
// no events, then an integration step with this now smaller
// step size will also be successful and produce no events.
_currentStepSize = outTime.subtract(_iterationBeginTime)
.getDoubleValue();
// If the step size is now negative, then we are trying
// to roll back too far.
if (_currentStepSize < 0.0) {
throw new IllegalActionException(this,
"Attempting to roll back time from "
+ _iterationBeginTime + " to " + outTime
+ ", but state has been committed.");
}
rollBackToCommittedState();
fire();
// It is safe to commit if we assume that the environment
// will not roll back time, and that it is not iterating to
// a fixed point. FIXME: Is this assumption right?
// If the enclosing director is a FixedPointDirector,
// there may inputs that are unknown! Perhaps the composite
// actor prefire() returns false if there unknown inputs?
_commit();
_commitIsPending = false;
} else if (comparison == 0 && _commitIsPending) {
// If we have a pending commit and current time matches the environment
// time, then perform the commit now.
_commitIsPending = false;
boolean result = _commit();
if (result == false) {
// The commit returned false, which means that either all
// actors return false in postfire or if we have reached the
// stop time. In this case, we should not execute further,
// so prefire() should return false.
// FIXME: Actually, postfire() should return false.
// Also, why do the other branches of the if ignore
// the return value of _commit()?
return false;
}
} else if (comparison < 0
&& executiveDirector != _enclosingContinuousDirector()) {
// Local current time is behind environment time, so
// We must be inside a modal model and have been
// disabled at the time that the commit
// would be effective. Cancel any pending commit.
if (_commitIsPending) {
_commitIsPending = false;
rollBackToCommittedState();
}
// Force current time to match the environment time, and treat
// this as if were were starting again in initialize().
// This ensures that actors like plotters will be postfired at
// the current time.
// FIXME: How do we know that that time matches the
// time at which the commit occurred?
// Shouldn't this be checked?
_currentTime = outTime;
if (_debugging) {
_debug("----- Setting current time to match enclosing non-ContinuousDirector: "
+ _currentTime);
}
_currentStepSize = 0.0;
}
// Adjust the step size to
// make sure the time does not exceed the next iteration
// time of the environment during this next integration step.
Time environmentNextIterationTime = executiveDirector
.getModelNextIterationTime();
Time localTargetTime = _iterationBeginTime.add(_currentStepSize);
if (environmentNextIterationTime.compareTo(localTargetTime) < 0) {
_currentStepSize = environmentNextIterationTime.subtract(
_currentTime).getDoubleValue();
if (_debugging) {
_debug("----- Revising step size due to environment to "
+ _currentStepSize);
}
}
// If the current time and index match the first entry in the breakpoint
// table, then remove that entry.
if (!_breakpoints.isEmpty()) {
SuperdenseTime nextBreakpoint = (SuperdenseTime) _breakpoints
.first();
Time breakpointTime = nextBreakpoint.timestamp();
comparison = breakpointTime.compareTo(_currentTime);
// Remove any breakpoints from the table that are now in the past.
// In theory, this should only happen if we are inside a modal model
// and we were not active at the time of the breakpoint. In such a
// case, the right thing to do is to ignore the breakpoint.
// NOTE: This requires that actors be written carefully, since
// they have to ensure that if they post something on the breakpoint
// table (by calling fireAt()) and are not fired at that requested
// time, then they recover the next time they are fired.
while (comparison < 0
|| (comparison == 0 && nextBreakpoint.index() < _index)) {
if (_debugging) {
_debug("Remove a breakpoint that we missed: "
+ breakpointTime);
}
_breakpoints.removeFirst();
if (_breakpoints.isEmpty()) {
break;
}
nextBreakpoint = (SuperdenseTime) _breakpoints.first();
breakpointTime = nextBreakpoint.timestamp();
comparison = breakpointTime.compareTo(_currentTime);
}
if (comparison == 0 && nextBreakpoint.index() == _index) {
if (_debugging) {
_debug("The current superdense time is a breakpoint, "
+ nextBreakpoint + " , which is removed.");