001/* An HybridModalDirector governs the execution of the discrete dynamics of a
002 hybrid system model.
003
004 Copyright (c) 2005-2015 The Regents of the University of California.
005 All rights reserved.
006 Permission is hereby granted, without written agreement and without
007 license or royalty fees, to use, copy, modify, and distribute this
008 software and its documentation for any purpose, provided that the above
009 copyright notice and the following two paragraphs appear in all copies
010 of this software.
011
012 IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY
013 FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
014 ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF
015 THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF
016 SUCH DAMAGE.
017
018 THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES,
019 INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
020 MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE
021 PROVIDED HEREUNDER IS ON AN "AS IS" BASIS, AND THE UNIVERSITY OF
022 CALIFORNIA HAS NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES,
023 ENHANCEMENTS, OR MODIFICATIONS.
024
025 PT_COPYRIGHT_VERSION_2
026 COPYRIGHTENDKEY
027 */
028package ptolemy.domains.continuous.kernel;
029
030import java.util.Iterator;
031import java.util.List;
032import java.util.Map;
033
034import ptolemy.actor.Actor;
035import ptolemy.actor.CompositeActor;
036import ptolemy.actor.Director;
037import ptolemy.actor.continuous.ContinuousStatefulComponent;
038import ptolemy.actor.continuous.ContinuousStepSizeController;
039import ptolemy.data.expr.ParseTreeEvaluator;
040import ptolemy.domains.modal.kernel.FSMActor;
041import ptolemy.domains.modal.kernel.FSMDirector;
042import ptolemy.domains.modal.kernel.ParseTreeEvaluatorForGuardExpression;
043import ptolemy.domains.modal.kernel.RelationList;
044import ptolemy.domains.modal.kernel.State;
045import ptolemy.domains.modal.kernel.Transition;
046import ptolemy.kernel.CompositeEntity;
047import ptolemy.kernel.util.IllegalActionException;
048import ptolemy.kernel.util.InternalErrorException;
049import ptolemy.kernel.util.NameDuplicationException;
050import ptolemy.kernel.util.NamedObj;
051import ptolemy.kernel.util.Workspace;
052
053///////////////////////////////////////////////////////////////////
054//// HybridModalDirector
055
056/**
057 An HybridModalDirector governs the execution of the discrete dynamics of a hybrid
058 system model. It extends ModalDirector by implementing the ContinuousStatefulComponent
059 and ContinuousStepSizeController interfaces by delegating the function of those
060 interfaces to the currently active state refinement.
061 <p>
062 Note that when a multi-step solver is used, the guards on the transitions are
063 only evaluated when either the step size is zero or the multi-step solver has
064 just completed its last step. The guards are not evaluated during intermediate
065 steps.
066 <p>
067 This director is based on HSFSMDirector by Xiaojun Liu and Haiyang Zheng.
068
069 @author Edward A. Lee, Haiyang Zheng
070 @version $Id$
071 @since Ptolemy II 5.2
072 @Pt.ProposedRating Yellow (eal)
073 @Pt.AcceptedRating Red (liuxj)
074 */
075public class HybridModalDirector extends FSMDirector
076        implements ContinuousStatefulComponent, ContinuousStepSizeController {
077
078    /** Construct a director in the given container with the given name.
079     *  The container argument must not be null, or a
080     *  NullPointerException will be thrown.
081     *  If the name argument is null, then the name is set to the
082     *  empty string. Increment the version number of the workspace.
083     *  @param container Container of this director.
084     *  @param name Name of this director.
085     *  @exception IllegalActionException If the name has a period in it, or
086     *   the director is not compatible with the specified container.
087     *  @exception NameDuplicationException If the container is not a
088     *   CompositeActor and the name collides with an entity in the container.
089     */
090    public HybridModalDirector(CompositeEntity container, String name)
091            throws IllegalActionException, NameDuplicationException {
092        super(container, name);
093        // _verbose = true;
094    }
095
096    ///////////////////////////////////////////////////////////////////
097    ////                         public methods                    ////
098
099    /** Clone the actor into the specified workspace. This calls the
100     *  base class and then sets the attribute public members to refer
101     *  to the attributes of the new actor.
102     *  @param workspace The workspace for the new actor.
103     *  @return A new FSMActor.
104     *  @exception CloneNotSupportedException If a derived class contains
105     *   an attribute that cannot be cloned.
106     */
107    @Override
108    public Object clone(Workspace workspace) throws CloneNotSupportedException {
109        HybridModalDirector newObject = (HybridModalDirector) super.clone(
110                workspace);
111        newObject._enclosingContinuousDirector = null;
112        newObject._enclosingContinuousDirectorVersion = -1;
113        return newObject;
114    }
115
116    /** Fire the model model for one iteration. Override the base class
117     *  to avoid firing the controller during intermediate stages of a
118     *  multi-step solver.
119     *  @exception IllegalActionException If there is more than one
120     *   transition enabled and nondeterminism is not permitted,
121     *   or there is no controller, or it is thrown by any
122     *   choice action.
123     */
124    @Override
125    public void fire() throws IllegalActionException {
126        ContinuousDirector enclosingDirector = _enclosingContinuousDirector();
127        if (enclosingDirector != null
128                && enclosingDirector._isIntermediateStep()) {
129            FSMActor controller = getController();
130            State currentState = controller.currentState();
131            if (_debugging) {
132                _debug("*** Firing during intermediate stage of solver "
133                        + getFullName(), " at time " + getModelTime());
134                _debug("Current state is:", currentState.getName());
135            }
136            Actor[] stateRefinements = currentState.getRefinement();
137
138            if (stateRefinements != null) {
139                for (int i = 0; i < stateRefinements.length; ++i) {
140                    if (_stopRequested) {
141                        break;
142                    }
143                    if (stateRefinements[i].prefire()) {
144                        if (_debugging) {
145                            _debug("Fire state refinement:",
146                                    stateRefinements[i].getName());
147                        }
148                        stateRefinements[i].fire();
149                        _getStateRefinementsToPostfire()
150                                .add(stateRefinements[i]);
151                    }
152                }
153            }
154            controller.readOutputsFromRefinement();
155        } else {
156            super.fire();
157        }
158    }
159
160    /** Return error tolerance used for detecting enabled transitions.
161     *  If there is an enclosing continuous director, then get the
162     *  error tolerance from that director. Otherwise, return 1e-4.
163     *  @return The error tolerance used for detecting enabled transitions.
164     */
165    public final double getErrorTolerance() {
166        ContinuousDirector enclosingDirector = _enclosingContinuousDirector();
167        if (enclosingDirector == null) {
168            return 1e-4;
169        }
170        return enclosingDirector.getErrorTolerance();
171    }
172
173    /** Return the parse tree evaluator used to evaluate guard expressions.
174     *  In this class, an instance
175     *  of {@link ParseTreeEvaluatorForGuardExpression} is returned.
176     *  The parse tree evaluator is set to construction mode.
177     *  @return ParseTreeEvaluator used to evaluate guard expressions.
178     */
179    @Override
180    public ParseTreeEvaluator getParseTreeEvaluator() {
181        RelationList relationList = new RelationList();
182        ParseTreeEvaluatorForGuardExpression evaluator = new ParseTreeEvaluatorForGuardExpression(
183                relationList, getErrorTolerance());
184        evaluator.setConstructionMode();
185        return evaluator;
186    }
187
188    /** Return true if all actors that were fired in the current iteration
189     *  report that the step size is accurate and if no transition is enabled.
190     *  @return True if the current step is accurate.
191     */
192    @Override
193    public boolean isStepSizeAccurate() {
194        _lastDistanceToBoundary = 0.0;
195        _distanceToBoundary = 0.0;
196        boolean foundActor = false;
197
198        // Double iterator over two lists.
199        Iterator actors = null;
200        try {
201            actors = new ActorsFiredIterator();
202        } catch (IllegalActionException e) {
203            throw new InternalErrorException(e);
204        }
205        while (actors.hasNext()) {
206            Actor actor = (Actor) actors.next();
207            foundActor = true;
208            if (actor instanceof ContinuousStepSizeController) {
209                if (!((ContinuousStepSizeController) actor)
210                        .isStepSizeAccurate()) {
211                    return false;
212                }
213            } else if (actor instanceof CompositeActor) {
214                // Delegate to the director.
215                Director director = actor.getDirector();
216                if (director instanceof ContinuousStepSizeController) {
217                    if (!((ContinuousStepSizeController) director)
218                            .isStepSizeAccurate()) {
219                        return false;
220                    }
221                }
222            }
223        }
224        // Next check for enabled transitions.  Do this even if the above
225        // result is false, because as a side effect we calculate the
226        // data needed to suggest the next step size when refinedStepSize()
227        // is called.
228        // All non-preemptive and preemptive transitions are checked below,
229        // because even if a preemptive transition is enabled, the non-preemptive
230        // transitions never even get a chance to be evaluated.
231        // However, do this only if there is an enclosing
232        // ContinuousDirector.
233        ContinuousDirector enclosingDirector = _enclosingContinuousDirector();
234        if (enclosingDirector == null) {
235            return true;
236        }
237        try {
238            // Check whether there is any preemptive transition enabled.
239            FSMActor controller = getController();
240            State currentState = controller.currentState();
241            List transitionList = currentState.outgoingPort
242                    .linkedRelationList();
243            List preemptiveEnabledTransitions = controller
244                    .enabledTransitions(transitionList, true, false);
245
246            // Check whether there is any non-preemptive transition enabled.
247            List nonpreemptiveEnabledTransitions = controller
248                    .enabledTransitions(transitionList, false, false);
249
250            // Check whether there is any event detected for preemptive transitions.
251            Transition preemptiveTrWithEvent = _checkEvent(
252                    currentState.preemptiveTransitionList());
253
254            // Check whether there is any event detected for
255            // nonpreemptive transitions.
256            Transition nonPreemptiveTrWithEvent = _checkEvent(
257                    currentState.nonpreemptiveTransitionList());
258
259            if (_debugging && _verbose) {
260                if (preemptiveEnabledTransitions.size() != 0) {
261                    _debug("Found enabled preemptive transitions.");
262                }
263                if (nonpreemptiveEnabledTransitions.size() != 0) {
264                    _debug("Found enabled non-preemptive transitions.");
265                }
266                if (preemptiveTrWithEvent != null) {
267                    _debug("Detected event for transition:  "
268                            + preemptiveTrWithEvent.getGuardExpression());
269                }
270                if (nonPreemptiveTrWithEvent != null) {
271                    _debug("Detected event for transition:  "
272                            + nonPreemptiveTrWithEvent.getGuardExpression());
273                }
274            }
275
276            // If there is no transition enabled, the last step size is
277            // accurate for transitions. The states will be committed at
278            // the postfire method.
279            // Set the local variables to be used to suggest a step
280            // size in the next call to refinedStepSize().
281            if (preemptiveEnabledTransitions.size() == 0
282                    && nonpreemptiveEnabledTransitions.size() == 0
283                    && preemptiveTrWithEvent == null
284                    && nonPreemptiveTrWithEvent == null) {
285                _lastDistanceToBoundary = 0.0;
286                _distanceToBoundary = 0.0;
287                return true;
288            } else {
289                Transition enabledTransition = null;
290
291                // We check the maximum difference of the relations that change
292                // their status for step size refinement.
293                _distanceToBoundary = Double.MIN_VALUE;
294
295                Iterator iterator = preemptiveEnabledTransitions.iterator();
296
297                while (iterator.hasNext()) {
298                    Transition transition = (Transition) iterator.next();
299                    ParseTreeEvaluatorForGuardExpression parseTreeEvaluator = (ParseTreeEvaluatorForGuardExpression) transition
300                            .getParseTreeEvaluator();
301                    RelationList relationList = parseTreeEvaluator
302                            .getRelationList();
303
304                    double distanceToBoundary = relationList
305                            .getMaximumDifference();
306                    // The distance to boundary is the difference between
307                    // the value of a variable in a relation (comparison
308                    // operation) and the threshold value against which it
309                    // is being compared. The previous distance is the last
310                    // committed distance.
311                    if (distanceToBoundary > _distanceToBoundary) {
312                        _distanceToBoundary = distanceToBoundary;
313                        _lastDistanceToBoundary = relationList
314                                .getPreviousMaximumDistance();
315                        enabledTransition = transition;
316                    }
317                }
318
319                iterator = nonpreemptiveEnabledTransitions.iterator();
320
321                while (iterator.hasNext()) {
322                    Transition transition = (Transition) iterator.next();
323                    ParseTreeEvaluatorForGuardExpression parseTreeEvaluator = (ParseTreeEvaluatorForGuardExpression) transition
324                            .getParseTreeEvaluator();
325                    RelationList relationList = parseTreeEvaluator
326                            .getRelationList();
327
328                    double distanceToBoundary = relationList
329                            .getMaximumDifference();
330
331                    if (distanceToBoundary > _distanceToBoundary) {
332                        _distanceToBoundary = distanceToBoundary;
333                        _lastDistanceToBoundary = relationList
334                                .getPreviousMaximumDistance();
335                        enabledTransition = transition;
336                    }
337                }
338
339                if (preemptiveTrWithEvent != null) {
340                    ParseTreeEvaluatorForGuardExpression parseTreeEvaluator = (ParseTreeEvaluatorForGuardExpression) preemptiveTrWithEvent
341                            .getParseTreeEvaluator();
342                    RelationList relationList = parseTreeEvaluator
343                            .getRelationList();
344                    double distanceToBoundary = relationList
345                            .getMaximumDifference();
346
347                    if (distanceToBoundary > _distanceToBoundary) {
348                        _distanceToBoundary = distanceToBoundary;
349                        _lastDistanceToBoundary = relationList
350                                .getPreviousMaximumDistance();
351                        enabledTransition = preemptiveTrWithEvent;
352                    }
353                }
354
355                if (nonPreemptiveTrWithEvent != null) {
356                    ParseTreeEvaluatorForGuardExpression parseTreeEvaluator = (ParseTreeEvaluatorForGuardExpression) nonPreemptiveTrWithEvent
357                            .getParseTreeEvaluator();
358                    RelationList relationList = parseTreeEvaluator
359                            .getRelationList();
360                    double distanceToBoundary = relationList
361                            .getMaximumDifference();
362
363                    if (distanceToBoundary > _distanceToBoundary) {
364                        _distanceToBoundary = distanceToBoundary;
365                        _lastDistanceToBoundary = relationList
366                                .getPreviousMaximumDistance();
367                        enabledTransition = nonPreemptiveTrWithEvent;
368                    }
369                }
370
371                if (_debugging && _verbose) {
372                    if (enabledTransition != null) {
373                        _debug("The guard " + enabledTransition.getGuardExpression()
374                                + " has the biggest difference to boundary as "
375                                + _distanceToBoundary);
376                    } else {
377                        _debug("No enabled transition.");
378                    }
379                }
380
381                // If we are close enough, then the flipping of the guard is OK.
382                double errorTolerance = enclosingDirector.getErrorTolerance();
383                if (_distanceToBoundary < errorTolerance) {
384                    _distanceToBoundary = 0.0;
385                    _lastDistanceToBoundary = 0.0;
386                    return true;
387                } else {
388                    // Return false only if there is a refinement.
389                    if (!foundActor) {
390                        return true;
391                    }
392                    return false;
393                }
394            }
395        } catch (Throwable throwable) {
396            // Can not evaluate guard expression.
397            throw new InternalErrorException(throwable);
398        }
399    }
400
401    /** Override the base class so that if there is no enabled transition
402     *  then we record for each comparison operation in each
403     *  guard expression the distance between the current value of the
404     *  variable being compared and the threshold.
405     *  @exception IllegalActionException If thrown by any commit action
406     *  or there is no controller.
407     */
408    @Override
409    public boolean postfire() throws IllegalActionException {
410        FSMActor controller = getController();
411        State currentState = controller.currentState();
412        Map<State, Transition> lastChosenTransitions = controller
413                .getLastChosenTransitions();
414        if (lastChosenTransitions.size() == 0) {
415            // No transition was chosen.
416            // Record the current values on either side of each
417            // comparison operation (called a relation) in each guard.
418            Iterator iterator = currentState.nonpreemptiveTransitionList()
419                    .listIterator();
420            while (iterator.hasNext()) {
421                Transition transition = (Transition) iterator.next();
422                ParseTreeEvaluatorForGuardExpression parseTreeEvaluator = (ParseTreeEvaluatorForGuardExpression) transition
423                        .getParseTreeEvaluator();
424                RelationList relationList = parseTreeEvaluator
425                        .getRelationList();
426                relationList.commitRelationValues();
427            }
428
429            iterator = currentState.preemptiveTransitionList().listIterator();
430            while (iterator.hasNext()) {
431                Transition transition = (Transition) iterator.next();
432                ParseTreeEvaluatorForGuardExpression parseTreeEvaluator = (ParseTreeEvaluatorForGuardExpression) transition
433                        .getParseTreeEvaluator();
434                RelationList relationList = parseTreeEvaluator
435                        .getRelationList();
436                relationList.commitRelationValues();
437            }
438        } else {
439            // It is important to clear the history information of the
440            // relation list since after this breakpoint, no history
441            // information is valid.
442            Iterator iterator = currentState.nonpreemptiveTransitionList()
443                    .listIterator();
444            while (iterator.hasNext()) {
445                Transition transition = (Transition) iterator.next();
446                ParseTreeEvaluatorForGuardExpression parseTreeEvaluator = (ParseTreeEvaluatorForGuardExpression) transition
447                        .getParseTreeEvaluator();
448                RelationList relationList = parseTreeEvaluator
449                        .getRelationList();
450                relationList.resetRelationList();
451            }
452
453            iterator = currentState.preemptiveTransitionList().listIterator();
454            while (iterator.hasNext()) {
455                Transition transition = (Transition) iterator.next();
456                ParseTreeEvaluatorForGuardExpression parseTreeEvaluator = (ParseTreeEvaluatorForGuardExpression) transition
457                        .getParseTreeEvaluator();
458                RelationList relationList = parseTreeEvaluator
459                        .getRelationList();
460                relationList.resetRelationList();
461            }
462        }
463        return super.postfire();
464    }
465
466    /** Override the base class to set current time to match that of
467     *  the enclosing executive director, if there is one, regardless
468     *  of whether that time is in the future or past. The superclass
469     *  sets current time only if the local time is less than the
470     *  environment time.
471     *  Initialize the firing of the director by resetting all receivers to
472     *  unknown.
473     *  @return Whatever the superclass returns.
474     *  @exception IllegalActionException If thrown by the superclass.
475     */
476    @Override
477    public boolean prefire() throws IllegalActionException {
478        boolean result = super.prefire();
479        // If any refinement is not ready to fire, stop prefiring the
480        // remaining actors, call super.prefire(), and return false;
481        State st = getController().currentState();
482        Actor[] actors = st.getRefinement();
483        if (actors != null) {
484            for (int i = 0; i < actors.length; ++i) {
485                if (_stopRequested) {
486                    break;
487                }
488                if (_debugging) {
489                    _debug("Prefire the refinement of the current state: ",
490                            actors[i].getFullName());
491                }
492                if (!actors[i].prefire()) {
493                    result = false;
494                    break;
495                }
496            }
497        }
498        return result;
499    }
500
501    /** Return the minimum of the step sizes suggested by any
502     *  actors that were fired in the current iteration.
503     *  @return The suggested refined step size.
504     *  @exception IllegalActionException If the step size cannot be further refined.
505     */
506    @Override
507    public double refinedStepSize() throws IllegalActionException {
508        double result = Double.POSITIVE_INFINITY;
509        Iterator actors = new ActorsFiredIterator();
510        while (actors.hasNext()) {
511            Actor actor = (Actor) actors.next();
512            if (actor instanceof ContinuousStepSizeController) {
513                double candidate = ((ContinuousStepSizeController) actor)
514                        .refinedStepSize();
515                if (candidate < result) {
516                    result = candidate;
517                }
518            } else if (actor instanceof CompositeActor) {
519                // Delegate to the director.
520                Director director = actor.getDirector();
521                if (director instanceof ContinuousStepSizeController) {
522                    double candidate = ((ContinuousStepSizeController) director)
523                            .refinedStepSize();
524                    if (candidate < result) {
525                        result = candidate;
526                    }
527                }
528            }
529        }
530
531        // If this is inside a ContinuousDirector and there was a guard that
532        // became enabled, then guess as to the new step size based on the
533        // linear interpolation performed in the last invocation of
534        // isStepSizeAccurate().
535        if (_distanceToBoundary > 0.0) {
536            ContinuousDirector enclosingDirector = _enclosingContinuousDirector();
537            if (enclosingDirector != null) {
538                double errorTolerance = enclosingDirector.getErrorTolerance();
539                double currentStepSize = enclosingDirector.getCurrentStepSize();
540
541                // Linear interpolation to refine the step size.
542                // The "distances" here are not in time, but in the value
543                // of continuous variables. The "last distance" is the distance
544                // as of the previous postfire(), i.e., the previously committed
545                // values of the continuous variables.
546                // Note the step size is refined such that the distanceToBoundary
547                // expected at the new step size is half of errorTolerance.
548                double refinedStepSize = currentStepSize
549                        * (_lastDistanceToBoundary + errorTolerance / 2)
550                        / (_lastDistanceToBoundary + _distanceToBoundary);
551
552                result = Math.min(result, refinedStepSize);
553                // Note: To see how well this algorithm is working, you can
554                // uncomment the following line.
555                // System.out.println("refined step size: " + result);
556            }
557        }
558
559        return result;
560    }
561
562    /** Roll back to committed state.
563     *  This will roll back any actors that were fired in the current iteration.
564     *  @exception IllegalActionException If the rollback attempts to go
565     *   back further than the last committed time.
566     */
567    @Override
568    public void rollBackToCommittedState() throws IllegalActionException {
569        Iterator actors = null;
570        try {
571            actors = new ActorsFiredIterator();
572        } catch (IllegalActionException e1) {
573            throw new InternalErrorException(e1);
574        }
575        while (actors.hasNext()) {
576            Actor actor = (Actor) actors.next();
577            if (actor instanceof ContinuousStatefulComponent) {
578                ((ContinuousStatefulComponent) actor)
579                        .rollBackToCommittedState();
580            } else if (actor instanceof CompositeActor) {
581                // Delegate to the director.
582                Director director = actor.getDirector();
583                // On May 5, 2015, in Ptolemy Hackers, Stefan
584                // Resmerita wrote: "When rolling back the refinement
585                // of a state, this method delegates to the director
586                // of the refinement only if that one is a
587                // ContinuousDirector. Thus, models where the
588                // refinement director is a HybridModalDirector may
589                // not work.
590
591                // "This is illustrated in the attached example, which
592                // is a modified BouncingBall model where the
593                // refinement of the original "free" state has been
594                // moved one level down in FSM hierarchy.
595
596                // "At first sight, it would make sense to delegate
597                // rollback to a ContinuousStatefulComponent rather
598                // than a ContinuousDirector, but I'm unsure about
599                // possible side effects... "
600
601                if (director instanceof ContinuousStatefulComponent) {
602                    ((ContinuousStatefulComponent) director)
603                            .rollBackToCommittedState();
604                }
605            }
606        }
607        // Reset the last chosen transition of the FSM controller to null
608        // because upon moving forward again we may choose a different
609        // transition.
610        try {
611            FSMActor controller = getController();
612            if (controller != null) {
613                Map<State, Transition> chosenTransitions = controller
614                        .getLastChosenTransitions();
615                chosenTransitions.clear();
616            }
617        } catch (IllegalActionException e) {
618            // This should not occur.
619            throw new InternalErrorException(e);
620        }
621    }
622
623    /** Return the minimum of the step sizes suggested by any
624     *  actors that were fired in current iteration.
625     *  @return The suggested next step size.
626     *  @exception IllegalActionException If an actor requests an
627     *   illegal step size.
628     */
629    @Override
630    public double suggestedStepSize() throws IllegalActionException {
631        double result = Double.POSITIVE_INFINITY;
632        Iterator actors = new ActorsFiredIterator();
633        while (actors.hasNext()) {
634            Actor actor = (Actor) actors.next();
635            if (actor instanceof ContinuousStepSizeController) {
636                double candidate = ((ContinuousStepSizeController) actor)
637                        .suggestedStepSize();
638                if (candidate < result) {
639                    result = candidate;
640                }
641            } else if (actor instanceof CompositeActor) {
642                // Delegate to the director.
643                Director director = actor.getDirector();
644                if (director instanceof ContinuousStepSizeController) {
645                    double candidate = ((ContinuousStepSizeController) director)
646                            .suggestedStepSize();
647                    if (candidate < result) {
648                        result = candidate;
649                    }
650                }
651            }
652        }
653        return result;
654    }
655
656    ///////////////////////////////////////////////////////////////////
657    ////                         protected methods                 ////
658
659    /** Return the enclosing continuous director, or null if there
660     *  is none.  The enclosing continuous director is a director
661     *  above this in the hierarchy, possibly separated by composite
662     *  actors with other foreign directors.
663     *  @return The enclosing ContinuousDirector, or null if there is none.
664     */
665    protected ContinuousDirector _enclosingContinuousDirector() {
666        if (_enclosingContinuousDirectorVersion != _workspace.getVersion()) {
667            // Update the cache.
668            _enclosingContinuousDirector = null;
669            NamedObj container = getContainer();
670            while (container != null) {
671                // On the first pass, the container will be the immediate
672                // container, whose director is this one, so update the
673                // container first.
674                container = container.getContainer();
675                if (container instanceof Actor) {
676                    Director director = ((Actor) container).getDirector();
677                    if (director instanceof ContinuousDirector) {
678                        _enclosingContinuousDirector = (ContinuousDirector) director;
679                        break;
680                    }
681                }
682            }
683            _enclosingContinuousDirectorVersion = _workspace.getVersion();
684        }
685        return _enclosingContinuousDirector;
686    }
687
688    ///////////////////////////////////////////////////////////////////
689    ////                         private methods                   ////
690
691    /** Return the first transition in the specified list that has
692     *  an "event", which is a change in the boolean value of its guard
693     *  since the last evaluation.
694     *  @return A transition with a change in the value of the guard.
695     */
696    private Transition _checkEvent(List transitionList) {
697        Iterator transitionRelations = transitionList.iterator();
698
699        while (transitionRelations.hasNext() && !_stopRequested) {
700            Transition transition = (Transition) transitionRelations.next();
701            ParseTreeEvaluatorForGuardExpression parseTreeEvaluator = (ParseTreeEvaluatorForGuardExpression) transition
702                    .getParseTreeEvaluator();
703            RelationList relationList = parseTreeEvaluator.getRelationList();
704            if (relationList.hasEvent()) {
705                return transition;
706            }
707        }
708        return null;
709    }
710
711    ///////////////////////////////////////////////////////////////////
712    ////                         private variables                 ////
713
714    /** Local variable to indicate the distance to boundary. */
715    private double _distanceToBoundary = 0.0;
716
717    /** The enclosing continuous director, if there is one. */
718    private ContinuousDirector _enclosingContinuousDirector = null;
719
720    /** The version for __enclosingContinuousDirector. */
721    private long _enclosingContinuousDirectorVersion = -1;
722
723    /** Local variable to indicate the last committed distance to boundary. */
724    private double _lastDistanceToBoundary = 0.0;
725
726    ///////////////////////////////////////////////////////////////////
727    ////                         inner classes                     ////
728
729    /** Iterator over _stateRefinementsToPostfire followed by
730     *  _transitionRefinementsToPostfire.
731     */
732    private class ActorsFiredIterator implements Iterator {
733        public ActorsFiredIterator() throws IllegalActionException {
734            _iterator = _getStateRefinementsToPostfire().iterator();
735        }
736
737        @Override
738        public boolean hasNext() {
739            if (_iterator.hasNext()) {
740                return true;
741            }
742            try {
743                _iterator = _getTransitionRefinementsToPostfire().iterator();
744            } catch (IllegalActionException e) {
745                throw new InternalErrorException(e);
746            }
747            return _iterator.hasNext();
748        }
749
750        @Override
751        public Object next() {
752            return _iterator.next();
753        }
754
755        @Override
756        public void remove() {
757            // Ignore.
758        }
759
760        private Iterator _iterator;
761    }
762}