001/* Continuous-time director. 002 003 Copyright (c) 1998-2016 The Regents of the University of California. 004 All rights reserved. 005 Permission is hereby granted, without written agreement and without 006 license or royalty fees, to use, copy, modify, and distribute this 007 software and its documentation for any purpose, provided that the above 008 copyright notice and the following two paragraphs appear in all copies 009 of this software. 010 011 IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY 012 FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES 013 ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF 014 THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF 015 SUCH DAMAGE. 016 017 THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES, 018 INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 019 MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE 020 PROVIDED HEREUNDER IS ON AN "AS IS" BASIS, AND THE UNIVERSITY OF 021 CALIFORNIA HAS NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, 022 ENHANCEMENTS, OR MODIFICATIONS. 023 024 PT_COPYRIGHT_VERSION_2 025 COPYRIGHTENDKEY 026 027 */ 028package ptolemy.domains.continuous.kernel; 029 030import java.util.Iterator; 031import java.util.LinkedList; 032import java.util.List; 033 034import ptolemy.actor.Actor; 035import ptolemy.actor.CompositeActor; 036import ptolemy.actor.Director; 037import ptolemy.actor.IOPort; 038import ptolemy.actor.QuasiTransparentDirector; 039import ptolemy.actor.continuous.ContinuousStatefulComponent; 040import ptolemy.actor.continuous.ContinuousStatefulDirector; 041import ptolemy.actor.continuous.ContinuousStepSizeController; 042import ptolemy.actor.sched.FixedPointDirector; 043import ptolemy.actor.util.GeneralComparator; 044import ptolemy.actor.util.SuperdenseTime; 045import ptolemy.actor.util.Time; 046import ptolemy.actor.util.TotallyOrderedSet; 047import ptolemy.data.DoubleToken; 048import ptolemy.data.IntToken; 049import ptolemy.data.expr.Parameter; 050import ptolemy.data.expr.StringParameter; 051import ptolemy.data.type.BaseType; 052import ptolemy.kernel.CompositeEntity; 053import ptolemy.kernel.util.Attribute; 054import ptolemy.kernel.util.IllegalActionException; 055import ptolemy.kernel.util.InternalErrorException; 056import ptolemy.kernel.util.InvalidStateException; 057import ptolemy.kernel.util.NameDuplicationException; 058import ptolemy.kernel.util.NamedObj; 059import ptolemy.kernel.util.Settable; 060import ptolemy.kernel.util.Workspace; 061 062/////////////////////////////////////////////////////////////////// 063//// ContinuousDirector 064 065/** 066 The continuous time domain is a timed domain that supports 067 continuous-time signals, discrete-event signals, and mixtures of the 068 two. There is a global notion of time that all the actors are aware of. 069 The semantics of this domain is given in the following two papers: 070 <ul> 071 <li> 072 Edward A. Lee and Haiyang Zheng, "<a href="http://ptolemy.eecs.berkeley.edu/publications/papers/05/OperationalSemantics/#in_browser">Operational Semantics of Hybrid Systems</a>," 073 Invited paper in Proceedings of Hybrid Systems: Computation and Control 074 (HSCC) LNCS 3414, Zurich, Switzerland, March 9-11, 2005.</li> 075 <li> 076 Edward A. Lee, Haiyang Zheng, "<a href="http://ptolemy.eecs.berkeley.edu/publications/papers/07/unifying/index.htm#in_browser">Leveraging Synchronous Language Principles for Heterogeneous Modeling and Design of Embedded Systems</a>," 077 EMSOFT 07, September 30-October 3, 2007, Salzburg, Austria.</li> 078 </ul> 079 <p> 080 A signal is a set of "events," each of which has a tag and value. 081 The set of values includes a special element, called "absent", denoting 082 the absence of a (normal) value. 083 This director uses superdense time, where every event has a tag 084 that is a member of the set RxN. 085 R is a connected subset of the real numbers (giving "time", 086 and approximated by instances of the Time class), 087 and N is the natural numbers (giving an "index"). 088 At a time <i>t</i>, a signal 089 may have multiple values in sequence with tags 090 (<i>t</i>, 0), (<i>t</i>, 1)... Its "initial value" is the value 091 at tag (<i>t</i>, 0). It typically settles to 092 a "final value" after a finite number of indices. 093 If it fails to settle to a final value, the signal is said to 094 have a "stuttering Zeno" condition, and time will not progress.</p> 095 <p> 096 In our semantics, all signals are piecewise continuous. 097 This means that the initial value, as a function of time, 098 is continuous on the left, the final value, as a function 099 of time, is continuous on the right, and the signal 100 has exactly one value (meaning the initial value and the final value 101 are the same) at all times except those 102 on a discrete subset D.</p> 103 <p> 104 A purely continuous signal has exactly one value at 105 all times, meaning that the final value equals the initial 106 value at all times. 107 A purely discrete signal has 108 initial value "absent" and final value "absent" at all 109 times, and at a discrete subset of the times, it may 110 have non-absent values. The only signal that is both 111 purely continuous and purely discrete is the one that 112 is absent at all tags.</p> 113 <p> 114 A signal may be mostly continuous, 115 but have multiple values at a discrete subset of times. 116 These multiple values semantically represent discontinuities 117 in a continuous signal that is not purely continuous.</p> 118 <p> 119 The set of times where signals have more than one distinct value 120 is a discrete subset D of the time line. These times are called 121 "breakpoints" and are treated specially in the execution. 122 Between these times, an ordinary differential equation (ODE) 123 solver governs the execution. Initial values are always given 124 by the ODE solver.</p> 125 <p> 126 The parameters of this director are: 127 <ul> 128 <li> <i>startTime</i>: The start time of the 129 execution.</li> 130 131 <li> <i>stopTime</i>: The stop time of the execution. 132 When the current time reaches this value, postfire() will return false. 133 This will occur whether or not this director is at the top level.</li> 134 135 <li> <i>initStepSize</i>: The suggested integration step size. 136 If the ODE solver is a fixed step size solver, then this parameter 137 gives the step size taken. Otherwise, at the start of execution, 138 this provides the first guess for the integration step size. 139 In later iterations, the integrators provide the suggested step 140 size. This is a double with default value 0.1</li> 141 142 <li> <i>maxStepSize</i>: The maximum step size. 143 This can be used to prevent the solver from too few 144 samples of signals. That is, for certain models, it might 145 be possible to get accurate results for very large step 146 sizes, but plots of the signals may be misleading (even 147 if they are accurate) because they represent the signal 148 with only a few samples. The default value is 1.0.</li> 149 150 <li> <i>maxIterations</i>: 151 The maximum number of iterations that an 152 ODE solver can use to resolve the states of integrators. 153 Implicit solvers, for example, iterate until they converge, 154 and this parameter bounds the number of iterations. 155 An example of an implicit solver is the BackwardsEuler solver. 156 The default value is 20, and the type is int. 157 FIXME: Currently, this package implements no implicit solvers.</li> 158 159 <li> <i>ODESolver</i>: 160 The class name of the ODE solver used for integration. 161 This is a string that defaults to "ExplicitRK23Solver", 162 a solver that tends to deliver smooth renditions of signals. 163 The "ExplicitRK45Solver" may be more efficient in that it can 164 use larger step sizes, but the resulting signals when displayed 165 may be more jagged in appearance. 166 Solvers are all required to be in package 167 "ptolemy.domains.continuous.kernel.solver". 168 If there is another ContinuousDirector above this one 169 in the hierarchy, then the value of this parameter is ignored and the 170 solver given by the first ContinuousDirector above will be used.</li> 171 172 <li> <i>errorTolerance</i>: This is the local truncation 173 error tolerance, used for controlling the integration accuracy 174 in variable step size ODE solvers, and also for determining whether 175 unpredictable breakpoints have been accurately identified. Any actor 176 that implements ContinuousStepSizeControl may use this error 177 tolerance to determine whether the current step is accurate. 178 For example, if the local truncation error 179 in some integrator is greater than this tolerance, then the 180 integration step is considered to have failed, and should be restarted with 181 a reduced step size. The default value is 1e-4.</li> 182 </ul> 183 <p> 184 This director maintains a breakpoint table to record all predictable 185 breakpoints that are greater than or equal to 186 the current time. The breakpoints are sorted in chronological order. 187 Breakpoints at the same time are considered to be identical, and the 188 breakpoint table does not contain duplicate time points. A breakpoint can 189 be inserted into the table by calling the fireAt() method. The fireAt method 190 may be requested by the director, which inserts the stop time of the 191 execution. The fireAt method may also be requested by actors and the 192 requested firing time will be inserted into the breakpoint table.</p> 193 <p> 194 This director is designed to work with any other director that 195 implements the strict actor semantics. As long as the other director does 196 not commit state changes except in postfire(), that director 197 can be used within the model controlled by 198 this director. If, in addition to implementing the strict 199 actor semantics that other director also respects calls to 200 fireAt(), then this director may be used within a model 201 governed by that director.</p> 202 <p> 203 This director is based on the CTDirector by Jie Liu and Haiyang Zheng, 204 but it has a much simpler scheduler and a fixed-point semantics.</p> 205 206 @author Haiyang Zheng and Edward A. Lee, based on CTDirector by Jie Liu and Haiyang Zheng 207 @version $Id$ 208 @since Ptolemy II 6.0 209 @Pt.ProposedRating Yellow (hyzheng) 210 @Pt.AcceptedRating Red (hyzheng) 211 */ 212public class ContinuousDirector extends FixedPointDirector 213 implements ContinuousStatefulDirector, ContinuousStepSizeController { 214 215 /** Construct a director in the given container with the given name. 216 * The container argument must not be null, or a NullPointerException 217 * will be thrown. 218 * If the name argument is null, then the name is set to the 219 * empty string. All the parameters take their default values. 220 * @param container The container. 221 * @param name Name of this director. 222 * @exception IllegalActionException If the director is not compatible 223 * with the specified container. May be thrown by a derived class. 224 * @exception NameDuplicationException If the name collides with 225 * a property in the container. 226 */ 227 public ContinuousDirector(CompositeEntity container, String name) 228 throws IllegalActionException, NameDuplicationException { 229 super(container, name); 230 _initParameters(); 231 setScheduler(new ContinuousScheduler(this, "scheduler")); 232 } 233 234 /////////////////////////////////////////////////////////////////// 235 //// parameters //// 236 237 /** Error tolerance for data values, used with variable step 238 * size solvers to determine whether the current step size is accurate. 239 * The default value is 1e-4, and the type is double. 240 */ 241 public Parameter errorTolerance; 242 243 /** User's hint for the initial integration step size. 244 * The default value is 0.1, and the type is double. 245 */ 246 public Parameter initStepSize; 247 248 /** The maximum number of rounds that an 249 * ODE solver can use to resolve the states of integrators. 250 * Many solvers, such as RK 2-3 and RK 4-5, use a fixed number 251 * of rounds (3 and 6, respectively). Implicit ODE solvers use 252 * however many rounds it takes to converge to a solution within 253 * a specified accuracy (given by <i>errorTolerance</i>). 254 * An example of an implicit solver is the BackwardsEuler solver. 255 * This parameter limits the number of rounds. 256 * The default value is 20, and the type is int. 257 */ 258 public Parameter maxIterations; 259 260 /** The maximum step size. 261 * The default value is 1.0, and the type is double. 262 */ 263 public Parameter maxStepSize; 264 265 /** The class name of the ODE solver used for integration. 266 * This is a string that defaults to "ExplicitRK23Solver". 267 * Solvers are all required to be in package 268 * "ptolemy.domains.continuous.kernel.solver". 269 * If a solver is changed during execution, the 270 * change does not take effect until the next execution 271 * of the model. 272 * If there is another ContinuousDirector above this one 273 * in the hierarchy, separated possibly by MultiComposite, 274 * then the value of this parameter is ignored and the 275 * solver given by the other ContinuousDirector will be used. 276 */ 277 public StringParameter ODESolver; 278 279 /////////////////////////////////////////////////////////////////// 280 //// public methods //// 281 282 /** React to a change in an attribute. If the changed attribute 283 * matches a parameter of the director, then the corresponding 284 * local copy of the parameter value will be updated. 285 * @param attribute The changed parameter. 286 * @exception IllegalActionException If the new parameter value 287 * is not valid. 288 */ 289 @Override 290 public void attributeChanged(Attribute attribute) 291 throws IllegalActionException { 292 if (_debugging) { 293 _debug("attributeChanged: Updating ContinuousDirector parameter: " 294 + attribute.getName()); 295 } 296 if (attribute == errorTolerance) { 297 double value = ((DoubleToken) errorTolerance.getToken()) 298 .doubleValue(); 299 if (value < 0.0) { 300 throw new IllegalActionException(this, 301 "Cannot set a negative error tolerance."); 302 } 303 _errorTolerance = value; 304 } else if (attribute == initStepSize) { 305 double value = ((DoubleToken) initStepSize.getToken()) 306 .doubleValue(); 307 if (value < 0.0) { 308 throw new IllegalActionException(this, 309 "Cannot set a negative step size."); 310 } 311 _initStepSize = value; 312 } else if (attribute == maxIterations) { 313 int value = ((IntToken) maxIterations.getToken()).intValue(); 314 315 if (value < 1) { 316 throw new IllegalActionException(this, 317 "Cannot set a zero or negative iteration number."); 318 } 319 _maxIterations = value; 320 } else if (attribute == maxStepSize) { 321 double value = ((DoubleToken) maxStepSize.getToken()).doubleValue(); 322 if (value < 0.0) { 323 throw new IllegalActionException(this, 324 "Cannot set a negative step size."); 325 } 326 _maxStepSize = value; 327 } else { 328 super.attributeChanged(attribute); 329 } 330 } 331 332 /** Clone the object into the specified workspace. The new object is 333 * <i>not</i> added to the directory of that workspace (you must do this 334 * yourself if you want it there). 335 * @param workspace The workspace for the cloned object. 336 * @exception CloneNotSupportedException Not thrown in this base class 337 * @return The new Attribute. 338 */ 339 @Override 340 public Object clone(Workspace workspace) throws CloneNotSupportedException { 341 ContinuousDirector newObject = (ContinuousDirector) super.clone( 342 workspace); 343 newObject._breakpoints = null; 344 newObject._enclosingContinuousDirectorVersion = -1L; 345 newObject._ODESolver = null; 346 newObject._statefulComponents = new LinkedList(); 347 newObject._statefulComponentsVersion = -1L; 348 newObject._stepSizeControllers = new LinkedList(); 349 newObject._stepSizeControllersVersion = -1L; 350 return newObject; 351 } 352 353 /** Perform an integration step. This invokes prefire() and fire() of 354 * actors (possibly repeatedly) and advances the local view of 355 * time by one step. This normally involves three nested iterative procedures. 356 * The outer procedure invokes the possibly multiple steps of 357 * the solver (if it is a multistep solver), unless the step size 358 * is zero. The middle one iterates until a suitable step size is 359 * found. The inner one, implemented by the superclass, 360 * iterates until a fixed point is found at each time point. 361 * <p> 362 * If there is an enclosing ContinuousDirector, however, then this 363 * method simply performs the current round of execution of the enclosing 364 * director, using the step size of the enclosing director. 365 * @exception IllegalActionException If an actor throws it. 366 */ 367 @Override 368 public void fire() throws IllegalActionException { 369 if (_debugging) { 370 _debug("Calling fire() at time " + getModelTime() + " index " 371 + _index); 372 } 373 // If there is an enclosing director, then just execute 374 // its current round. 375 ContinuousDirector enclosingContinuousDirector = _enclosingContinuousDirector(); 376 if (enclosingContinuousDirector != null) { 377 // The local model time is not synchronized here because 378 // the prefire method is responsible for it. 379 _currentStepSize = enclosingContinuousDirector._currentStepSize; 380 int round = enclosingContinuousDirector._ODESolver._getRound(); 381 _ODESolver._setRound(round); 382 if (_debugging) { 383 _debug("-- Get step size from enclosing Continuous director: " 384 + _currentStepSize + ", and also the solver round: " 385 + round + "."); 386 } 387 _resetAllReceivers(); 388 _transferInputsToInside(); 389 super.fire(); 390 _transferOutputsToEnvironment(); 391 return; 392 } 393 394 // If there is a commit pending, then this firing should just 395 // produce the outputs from the speculative execution. 396 // The commit will occur in postfire. 397 if (_commitIsPending) { 398 // We are presumably at superdense index 0 of the environment, 399 // but have already executed superdense index 0 of the local model. 400 // Hence, we should produce outputs now and increment our local 401 // superdense index to 1. 402 _transferOutputsToEnvironment(); 403 return; 404 } 405 406 _resetAllReceivers(); 407 408 // If there is an enclosing director, we know it is not a ContinuousDirector 409 // because we would have returned above if it were. Inputs from any 410 // enclosing domain that is not continuous are discrete, so we should 411 // not read them if the current superdense index is 0. This would 412 // violate the piecewise continuity constraint. 413 if (!_redoingSolverIteration && _index == 0 && isEmbedded()) { 414 // Check the input ports to see if any has an input. If 415 // it does, set the current step size to zero and execute 416 // one round without the input. The input will be read at 417 // the next superdense time index. 418 List<IOPort> ports = ((Actor) getContainer()).inputPortList(); 419 for (IOPort port : ports) { 420 for (int i = 0; i < port.getWidth(); i++) { 421 if (port.isKnown(i) && port.hasToken(i)) { 422 // There is an input. Do a zero step-size iteration. 423 _currentStepSize = 0.0; 424 _ODESolver._reset(); 425 // Before firing, we need to assert absent on all inside 426 // ports, since we aren't transferring the inputs. 427 _assertAbsentInside(); 428 super.fire(); 429 _transferOutputsToEnvironment(); 430 return; 431 } 432 } 433 } 434 } 435 436 // If there are external inputs and there is no enclosing continuous 437 // director, then those external inputs are to be understood as discrete 438 // inputs. We must process them when we have a step size of zero. 439 // However, if we just backtracked (due to our previous step size 440 // having been too big, and the speculative execution having been 441 // nullified by the appearance of an unexpected event), then we should 442 // not call _transferInptusToInside(), because the environment time 443 // is ahead of the local time and any inputs that might present 444 // are in the future. Presumably, since we are redoing a speculative 445 // solver iteration, there must have been no inputs at the 446 // _iterationBeginTime, which is now our _currentTime. 447 if (!_redoingSolverIteration && _transferInputsToInside() 448 || _currentStepSize == 0.0) { 449 _currentStepSize = 0.0; 450 _ODESolver._reset(); 451 super.fire(); 452 _transferOutputsToEnvironment(); 453 _redoingSolverIteration = false; 454 return; 455 } 456 457 // If we get to here, then we are either at the top level, or we 458 // are enclosed within a director that is not a ContinuousDirector 459 // and there are no external inputs and the step size is greater 460 // than zero. We now need to perform integration. 461 boolean outputsProduced = false; 462 while (!_stopRequested) { 463 // Some solvers take multiple rounds to perform an integration step. 464 // Tell the solver we are starting an integration step by resetting 465 // the solver. 466 _ODESolver._reset(); 467 468 if (_debugging) { 469 _debug("-- Execute from iteration begin time " 470 + _iterationBeginTime + " with step size " 471 + _currentStepSize + " at index " + _index + "."); 472 } 473 474 // Iterate until the solver is done with the integration step 475 // or the maximum number of iterations is reached. 476 int iterations = 0; 477 try { 478 _isIntermediateStep = true; 479 while (!_ODESolver._isStepFinished() 480 && iterations < _maxIterations && !_stopRequested) { 481 482 _resetAllReceivers(); 483 484 // Set receivers connected to the inputs to "absent." 485 // Note that this cannot be done by calling 486 // _transferInputsToInside() because if we are redoing 487 // the solver iteration then there are inputs available, 488 // but they should not be consumed yet. 489 // FIXME: Shouldn't this be done only if enclosingContinousDirector is null? 490 if (enclosingContinuousDirector == null) { 491 _assertAbsentInside(); 492 } 493 494 super.fire(); 495 // Outputs should only be produced on the first iteration of 496 // the solver because after that we are computing values in 497 // the future. We should not produce more outputs if we 498 // reduce the step size and try again because that could 499 // result in multiple output events where there should only 500 // be one. 501 if (!_redoingSolverIteration && !outputsProduced) { 502 // Outputs need to be produced now, since the 503 // values at the output ports are now the correct 504 // values at the _iterationBeginTime, which on 505 // the first pass through, will match the 506 // current environment time. We do not do this 507 // if we are redoing the solver iteration, since 508 // presumably those outputs were already produced, 509 // and current time in the environment has passed 510 // the time that they were produced. 511 _transferOutputsToEnvironment(); 512 outputsProduced = true; 513 } 514 515 // Advance the local view of time such that 516 // the inputs to the integrators 517 // can be calculated by firing all the actors. 518 // Note that this doesn't change global model time. 519 // It only changes the local view of time. 520 double timeIncrement = _ODESolver._getRoundTimeIncrement(); 521 // This will return 1.0 when there is one or zero more 522 // steps for the solver. Hence, the next step will not 523 // be an intermediate step. 524 if (timeIncrement == 1.0) { 525 _isIntermediateStep = false; 526 } 527 localClock.setLocalTime(_iterationBeginTime 528 .add(_currentStepSize * timeIncrement)); 529 _index = 0; 530 if (_debugging) { 531 _debug("-- Setting current time for the next ODE solver round: " 532 + getModelTime() + " and index to 0."); 533 } 534 _ODESolver._setRound(_ODESolver._getRound() + 1); 535 536 if (_debugging) { 537 _debug("ODE solver solves the round #" 538 + _ODESolver._getRound()); 539 } 540 // Increase the iteration count. 541 iterations++; 542 } 543 } finally { 544 _isIntermediateStep = false; 545 _redoingSolverIteration = false; 546 } 547 // If the step size is accurate and we did not reach the 548 // maximum number of iterations then we are done. 549 // Otherwise, we have to try again with a smaller step size. 550 if (isStepSizeAccurate() && iterations <= _maxIterations) { 551 // All actors agree with the current step size, 552 // or we have reached the maximum allowed number of iterations. 553 // The integration step is finished. 554 break; 555 } else { 556 if (iterations > _maxIterations) { 557 // If any step size control actor is unsatisfied with the 558 // current step size, refine the step size to a smaller one. 559 _setCurrentStepSize(_currentStepSize / 2); 560 } else { 561 // There is some step size control actor that is 562 // unsatisfied with the current step size, refine the 563 // step size to a smaller one. 564 double refinedStep = refinedStepSize(); 565 // Make sure the result is actually a change. 566 if (refinedStep >= _currentStepSize) { 567 // Actors suggestion is not useful. 568 // Choose instead half the current step size. 569 refinedStep = _currentStepSize * 0.5; 570 if (_debugging) { 571 _debug("-- Adjusting step size to: " + refinedStep); 572 } 573 } 574 _setCurrentStepSize(refinedStep); 575 } 576 577 if (_debugging) { 578 _debug("-- Step was not accurate. Refine the step size to: " 579 + _currentStepSize); 580 } 581 // Restore the saved state of the stateful actors, 582 // including the save starting time of this integration. 583 rollBackToCommittedState(); 584 } 585 } 586 } 587 588 /** Handle firing requests from the contained actors by registering 589 * breakpoints. If the specified time is earlier than the current time, 590 * then request a firing at the current time. 591 * Otherwise, insert the specified time into the 592 * breakpoint table. If the specified time is earlier than or 593 * equal to the current time, then for the breakpoint table entry, 594 * use an index one larger than the current index, unless this director 595 * is currently in initialize(), in which case use the current index. 596 * If the requested time is in the future, then use the requested index. 597 * @param actor The actor that requests the firing. 598 * @param time The requested firing time. 599 * @param index The microstep. 600 * @return The time at which the actor passed as an argument 601 * will be fired. 602 * @exception IllegalActionException If the time is earlier than 603 * the current time. 604 */ 605 @Override 606 public Time fireAt(Actor actor, Time time, int index) 607 throws IllegalActionException { 608 if (_debugging) { 609 String name = "this director"; 610 if (actor != null) { 611 name = actor.getName(); 612 } 613 _debug("** fireAt() called by " + name 614 + ", which requests refiring at " + time); 615 } 616 synchronized (_breakpoints) { 617 // Check if the request time is earlier than the current time. 618 Time currentTime = getModelTime(); 619 int comparisonResult = time.compareTo(currentTime); 620 // Adjust time to at least match the current time. 621 if (comparisonResult < 0) { 622 time = currentTime; 623 } 624 if (comparisonResult <= 0) { 625 // If during initialization, do not increase the microstep. 626 // This is based on the assumption that an actor only requests 627 // one firing during initialization. In fact, if an actor requests 628 // several firings at the same time, 629 // only the first request will be granted. 630 if (index <= _index) { 631 if (_isInitializing) { 632 index = _index; 633 } else { 634 index = _index + 1; 635 } 636 } 637 } 638 // Insert a superdense time object as a breakpoint into the 639 // breakpoint table. 640 _breakpoints.insert(new SuperdenseTime(time, index)); 641 if (_debugging) { 642 _debug("** Inserted breakpoint with time = " + time 643 + ", and index = " + index); 644 } 645 return time; 646 } 647 } 648 649 /** Return the current integration step size. 650 * @return The current integration step size. 651 */ 652 @Override 653 public final double getCurrentStepSize() { 654 // This method is final for performance reason. 655 return _currentStepSize; 656 } 657 658 /** Return the local truncation error tolerance. 659 * @return The local truncation error tolerance. 660 */ 661 @Override 662 public final double getErrorTolerance() { 663 // This method is final for performance reason. 664 return _errorTolerance; 665 } 666 667 /** Initialize model after type resolution. 668 * If a start time has been explicitly set, then set the start 669 * time to that value. Otherwise, inherit if from the environment, 670 * if there is one, and set it to 0.0 otherwise. 671 * In addition to calling the initialize() method of the super class, 672 * this method records the current system time as the "real" starting 673 * time of the execution. This starting time is used when the 674 * execution is synchronized to real time. 675 * 676 * @exception IllegalActionException If the super class throws it. 677 */ 678 @Override 679 public void initialize() throws IllegalActionException { 680 _isInitializing = true; 681 682 // In case we are being reinitialized by a reset transition, 683 // clear the breakpoint table. This must be done before 684 // actors are initialized because they may call fireAt(), 685 // which will insert items in the breakpoint table. 686 _breakpoints.clear(); 687 688 // set current time and initialize actors. 689 super.initialize(); 690 691 // The above method sets the microstep to match the enclosing 692 // director if this director is embedded. In the Continuous 693 // domain, however, this is not the right thing to do. We 694 // might be getting initialized within a modal model, in 695 // which case the enclosing director may have a larger 696 // microstep. But we still want to begin with a microstep of zero. 697 // FIXME: No idea whether this is right! 698 // _index = 0; 699 700 // Make sure the first step has zero step size. 701 // This ensures that actors like plotters will be postfired at 702 // the start time. 703 _currentStepSize = 0.0; 704 Time startTime = getModelStartTime(); 705 Time stopTime = getModelStopTime(); 706 Time currentTime = getModelTime(); 707 708 // If this director is embedded, then request a firing at the 709 // start and stop times. However, do not do this if there is 710 // an enclosing ContinuousDirector. 711 712 // The reason for doing this is that if a Continuous composite actor 713 // is embedded in a DE model but has no input ports, without the 714 // following statements, the composite actor has no chance to be fired. 715 // Also, we want to be sure to be fired at the stop time. 716 if (isEmbedded() && _enclosingContinuousDirector() == null) { 717 // In preinitialize(), _startTime was set to the start time of 718 // the executive director. However, this invocation of initialize() 719 // may be occurring later in the execution of a model, as a result 720 // for example of a reset transition. In that case, _startTime 721 // may be in the past relative to the environment time. 722 // The super.initialize() call above should have set our 723 // current time to match the environment time, so we use 724 // the maximum of _startTime and the current time as the 725 // first firing time. 726 if (startTime.compareTo(currentTime) >= 0) { 727 fireContainerAt(startTime, 0); 728 } else { 729 // Use a microstep of 1 here on the assumption 730 // that initialization could create discontinuities. 731 fireContainerAt(currentTime, 1); 732 } 733 if (!stopTime.isInfinite() 734 && stopTime.compareTo(currentTime) >= 0) { 735 fireContainerAt(stopTime); 736 } 737 } 738 // Set a breakpoint with index 0 for the stop time. 739 if (!stopTime.isInfinite() && stopTime.compareTo(currentTime) >= 0) { 740 _breakpoints.insert(new SuperdenseTime(stopTime, 0)); 741 } 742 743 _commitIsPending = false; 744 _postfireReturns = true; 745 _isInitializing = false; 746 _redoingSolverIteration = false; 747 748 // NOTE: If we are being reinitialized during execution (e.g. because 749 // of a reset transition), then our current local time is set to match 750 // the environment time if startTime is blank (the default), meaning that 751 // start time is inherited from the environment. Otherwise, the accumulated 752 // suspend time should be set to the difference between the current time 753 // of the environment and the start time. In either case, this is the 754 // difference between the current time and the environment time. 755 } 756 757 /** Return true if all step size control actors agree that the current 758 * step is accurate and if there are no breakpoints in the past. 759 * @return True if all step size control actors agree with the current 760 * step size. 761 */ 762 @Override 763 public boolean isStepSizeAccurate() { 764 _debug("-- Check accuracy for output step size control actors:"); 765 766 // A zero step size is always accurate. 767 if (_currentStepSize == 0) { 768 return true; 769 } 770 771 boolean accurate = true; 772 773 // Ask ALL the actors whether the current step size is accurate. 774 // Note that ALL the step size controllers need to be queried. 775 Iterator stepSizeControlActors = _stepSizeControllers().iterator(); 776 while (stepSizeControlActors.hasNext() && !_stopRequested) { 777 ContinuousStepSizeController actor = (ContinuousStepSizeController) stepSizeControlActors 778 .next(); 779 boolean thisAccurate = actor.isStepSizeAccurate(); 780 if (_debugging) { 781 _debug("---- Checking output step size control actor: " 782 + ((NamedObj) actor).getFullName() + ", which returns " 783 + thisAccurate); 784 } 785 accurate = accurate && thisAccurate; 786 } 787 788 if (!_breakpoints.isEmpty()) { 789 SuperdenseTime nextBreakpoint = (SuperdenseTime) _breakpoints 790 .first(); 791 Time breakpointTime = nextBreakpoint.timestamp(); 792 int comparison = breakpointTime.compareTo(getModelTime()); 793 if (comparison < 0) { 794 accurate = false; 795 } 796 } 797 798 if (_debugging) { 799 _debug("-- Result of accuracy check: " + accurate); 800 } 801 return accurate; 802 } 803 804 /** If this director is not at the top level and the breakpoint table 805 * is not empty, request a refiring at the first breakpoint or at 806 * the local current time (iteration start time plus the step size), 807 * whichever is less. Postfire all controlled actors. 808 * <p> 809 * If the <i>synchronizeToRealTime</i> parameter is <i>true</i>, 810 * then this method will block execution until the real time catches 811 * up with current model time. The units for time are seconds. 812 * @return True if the Director wants to be fired again in the future. 813 * @exception IllegalActionException If the current model time exceeds 814 * the stop time, or refiring can not be granted, or the super class throws it. 815 */ 816 @Override 817 public boolean postfire() throws IllegalActionException { 818 if (_debugging) { 819 _debug("Calling postfire()."); 820 } 821 // If time exceeds the stop time, then either we failed 822 // to execute at the stop time, or the return value of 823 // false was ignored, or the return value of false in 824 // prefire() was ignored. All of these conditions are bugs. 825 if (getModelTime().compareTo(getModelStopTime()) > 0) { 826 throw new IllegalActionException(this, 827 "Current time exceeds the specified stopTime."); 828 } 829 boolean result = _postfireReturns; 830 // This code is sufficiently confusing that, at the expense 831 // of code duplication, we completely separate three cases. 832 if (_enclosingContinuousDirector() != null) { 833 // Need to closely coordinate with the enclosing director 834 result = result && _postfireWithEnclosingContinuousDirector(); 835 } else if (isEmbedded()) { 836 result = result && _postfireWithEnclosingNonContinuousDirector(); 837 } else { 838 // This director is at the top level. 839 result = result && _postfireAtTopLevel(); 840 } 841 return result; 842 } 843 844 /** Initialize the fixed-point iteration by setting all receivers to 845 * unknown, and return true if we have not passed the stop time. 846 * 847 * Record the current model time as the beginning time of the current 848 * iteration, and if there is a pending invocation of postfire() 849 * from a previous integration step, invoke that now. 850 * @return True if this director is ready to fire. 851 * @exception IllegalActionException If thrown by the super class, 852 * or if the model time of the environment is less than our current 853 * model time. 854 */ 855 @Override 856 public boolean prefire() throws IllegalActionException { 857 if (_debugging) { 858 _debug("\nCalling prefire() at time " + getModelTime() 859 + " and index " + _index); 860 } 861 // This code is sufficiently confusing that, at the expense 862 // of code duplication, we completely separate three cases. 863 if (_enclosingContinuousDirector() != null) { 864 // Need to closely coordinate with the enclosing director 865 return _prefireWithEnclosingContinuousDirector(); 866 } else if (isEmbedded()) { 867 return _prefireWithEnclosingNonContinuousDirector(); 868 } else { 869 // This director is at the top level. 870 return _prefireAtTopLevel(); 871 } 872 } 873 874 /** Preinitialize the model for an execution. This method is 875 * called only once for each simulation. 876 * 877 * @exception IllegalActionException If the super class throws it, or 878 * local variables cannot be initialized. 879 */ 880 @Override 881 public void preinitialize() throws IllegalActionException { 882 super.preinitialize(); 883 // Time objects can only be instantiated after super.preinitialize() 884 // is called, where the time resolution is resolved. 885 _initializeLocalVariables(); 886 } 887 888 /** Return the refined step size, which is the minimum of the 889 * current step size and the suggested step size of all actors that 890 * implement ContinuousStepSizeController and that also ensures 891 * that we do not pass a breakpoint. If these actors 892 * request a step size smaller than the time resolution, then 893 * the first time this happens this method returns the time resolution. 894 * If it happens again on the next call to this method, then this 895 * method throws an exception. 896 * @return The refined step size. 897 * @exception IllegalActionException If the scheduler throws it or the 898 * refined step size is less than the time resolution. 899 */ 900 @Override 901 public double refinedStepSize() throws IllegalActionException { 902 if (_debugging) { 903 _debug("-- Refining the current step size from " 904 + _currentStepSize); 905 } 906 907 double timeResolution = getTimeResolution(); 908 double refinedStep = _currentStepSize; 909 910 Iterator stepSizeControlActors = _stepSizeControllers().iterator(); 911 while (stepSizeControlActors.hasNext() && !_stopRequested) { 912 ContinuousStepSizeController actor = (ContinuousStepSizeController) stepSizeControlActors 913 .next(); 914 refinedStep = Math.min(refinedStep, actor.refinedStepSize()); 915 } 916 917 // If the requested step size is smaller than the time 918 // resolution, then set the step size to the time resolution. 919 // Set a flag indicating that we have done that so that if 920 // the step size as time resolution is still too large, 921 // throw an exception. 922 if (refinedStep < timeResolution) { 923 if (!_triedTheMinimumStepSize) { 924 // First time requested step size is less than time resolution 925 refinedStep = timeResolution; 926 _triedTheMinimumStepSize = true; 927 } else { 928 throw new IllegalActionException(this, 929 "The refined step size is less than the time " 930 + "resolution, at time " + getModelTime()); 931 } 932 } else { 933 _triedTheMinimumStepSize = false; 934 } 935 936 if (_debugging) { 937 _debug("-- Refined step size suggested by the actors: " 938 + refinedStep); 939 } 940 941 if (!_breakpoints.isEmpty()) { 942 SuperdenseTime nextBreakpoint = (SuperdenseTime) _breakpoints 943 .first(); 944 Time nextBreakpointTime = nextBreakpoint.timestamp(); 945 int comparison = nextBreakpointTime 946 .compareTo(_iterationBeginTime.add(refinedStep)); 947 if (comparison < 0) { 948 refinedStep = nextBreakpointTime.subtract(_iterationBeginTime) 949 .getDoubleValue(); 950 if (refinedStep < 0.0) { 951 throw new IllegalActionException(this, 952 "Cannot set a step size to respect the breakpoint at " 953 + nextBreakpoint); 954 } 955 if (_debugging) { 956 _debug("-- Refined step size determined by the breakpoint table: " 957 + refinedStep); 958 } 959 } 960 } 961 962 return refinedStep; 963 } 964 965 /** Resume the actor at the specified time. If the actor has not 966 * been suspended since the last call to initialize(), then this 967 * has no effect. 968 * @exception IllegalActionException If the fireAt() request throws it. 969 */ 970 @Override 971 public void resume() throws IllegalActionException { 972 super.resume(); 973 // Request a firing at the current time to ensure that 974 // we restart here. 975 fireAt(null, getModelTime()); 976 } 977 978 /** Roll back all actors that implement ContinuousStatefulComponent 979 * to committed state, and set local model time to the start 980 * of the integration period. 981 * @exception IllegalActionException If the rollback attempts to go 982 * back further than the last committed time. 983 */ 984 @Override 985 public void rollBackToCommittedState() throws IllegalActionException { 986 // Restore the local view of model time to 987 // the start of the integration step. 988 localClock.setLocalTime(_iterationBeginTime); 989 _index = _iterationBeginIndex; 990 991 if (_debugging) { 992 _debug("-- Roll back time to: " + _iterationBeginTime 993 + " and index " + _index); 994 } 995 996 Iterator rollbacks = _statefulComponents().iterator(); 997 while (rollbacks.hasNext()) { 998 ContinuousStatefulComponent actor = (ContinuousStatefulComponent) rollbacks 999 .next(); 1000 actor.rollBackToCommittedState(); 1001 } 1002 } 1003 1004 /** Set a new value to the current time of the model. This overrides 1005 * the base class to allow time to move backwards (to support rollback) 1006 * and to discard any breakpoints in the breakpoint table that are 1007 * in the past relative to the specified time. 1008 * This overrides the setCurrentTime() in the Director base class. 1009 * Although this method is public, actors should never call it. 1010 * @param newTime The new current simulation time. 1011 * @exception IllegalActionException If the time is in the past 1012 * relative to the time of local committed state. 1013 */ 1014 @Override 1015 public final void setModelTime(Time newTime) throws IllegalActionException { 1016 // We don't call super.getModelTime() because Director.setModelTime() 1017 // merely sets the local time of the localClock. We want to do more here. 1018 1019 if (_ignoreSetTime) { 1020 return; 1021 } 1022 // This method is final for performance reason. 1023 if (_debugging) { 1024 _debug("** Environment is setting current time to " + newTime); 1025 } 1026 Time currentTime = getModelTime(); 1027 int comparison = newTime.compareTo(currentTime); 1028 if (comparison > 0) { 1029 // New time is ahead of the current local time. 1030 // This should not happen. 1031 throw new IllegalActionException(this, 1032 "ContinuousDirector expected to be invoked at time " 1033 + currentTime 1034 + ", but instead its time is being set to " 1035 + newTime); 1036 // NOTE: An alternative would be invalidate a pending commit 1037 // and discard intermediate breakpoints. 1038 // Code is below. 1039 /* 1040 if (_commitIsPending) { 1041 _commitIsPending = false; 1042 rollBackToCommittedState(); 1043 } 1044 _currentTime = newTime; 1045 _iterationBeginTime = _currentTime; 1046 _handleBreakpointsBefore(_currentTime, 0); 1047 // Set the step size to 0.0, which will ensure that any 1048 // actors (like plotters) are postfired at the new model time. 1049 // This also reinitializes the adaptive step size calculation, 1050 // which is probably reasonable since things may have 1051 // changed significantly. 1052 _currentStepSize = 0.0; 1053 */ 1054 } else if (comparison < 0) { 1055 // The new time is behind the current time. 1056 // This is legal only if we have a commit pending. 1057 if (!_commitIsPending) { 1058 throw new IllegalActionException(this, 1059 "Attempting to roll back time from " + currentTime 1060 + " to " + newTime 1061 + ", but state has been committed."); 1062 } 1063 // We have to re-do the integration 1064 // with a smaller step size that brings us up to the current 1065 // environment time. 1066 // NOTE: This depends on the property that if an integration 1067 // step with a larger step size was successful and produced 1068 // no events, then an integration step with this now smaller 1069 // step size will also be successful and produce no events. 1070 _currentStepSize = newTime.subtract(_iterationBeginTime) 1071 .getDoubleValue(); 1072 if (_debugging) { 1073 _debug("**** Setting step size to: " + _currentStepSize); 1074 } 1075 // If the step size is now negative, then we are trying 1076 // to roll back too far. 1077 if (_currentStepSize < 0.0) { 1078 throw new IllegalActionException(this, 1079 "Attempting to roll back time from " 1080 + _iterationBeginTime + " to " + newTime 1081 + ", but state has been committed."); 1082 } 1083 rollBackToCommittedState(); 1084 } 1085 } 1086 1087 /** Return an array of suggested ModalModel directors to use 1088 * with ContinuousDirector. The only available director is 1089 * ModalDirector because we need a director that implements 1090 * the strict actor semantics. 1091 * @return An array of suggested directors to be used with ModalModel. 1092 */ 1093 @Override 1094 public String[] suggestedModalModelDirectors() { 1095 // This method does not call the method defined in the super class, 1096 // because this method provides complete new information. 1097 String[] defaultSuggestions = new String[1]; 1098 defaultSuggestions[0] = "ptolemy.domains.continuous.kernel.HybridModalDirector"; 1099 return defaultSuggestions; 1100 } 1101 1102 /** Return the suggested step size for next integration. The suggested step 1103 * size is the minimum of suggestions from all step size control actors 1104 * and the time until the next breakpoint, 1105 * and it never exceeds 10 times of the current step size. 1106 * If there are no step size control actors at all, then return 1107 * 5 times of the current step size. However, the suggested step size 1108 * never exceeds the maximum step size. If this director has not been 1109 * fired since initialize() or the previous call to suggestedStepSize(), 1110 * then return the value of <i>maxStepSize</i>. 1111 * @return The suggested step size for next integration. 1112 * @exception IllegalActionException If an actor suggests an illegal step size. 1113 */ 1114 @Override 1115 public double suggestedStepSize() throws IllegalActionException { 1116 double suggestedStep = _initStepSize; 1117 1118 if (_currentStepSize != 0.0) { 1119 // Increase the current step size, then ask the step-size control 1120 // actors for their suggestions and choose the minimum. 1121 // We could use _maxStepSize here, but if the current step 1122 // size is very small w.r.t. the maximum, then this will almost 1123 // certainly be too much of a change in step size. Hence, we 1124 // increase the step size more slowly. 1125 suggestedStep = 10.0 * _currentStepSize; 1126 if (_debugging) { 1127 _debug("---- Speculatively set step size to " + suggestedStep); 1128 } 1129 1130 Iterator stepSizeControlActors = _stepSizeControllers().iterator(); 1131 while (stepSizeControlActors.hasNext() && !_stopRequested) { 1132 ContinuousStepSizeController actor = (ContinuousStepSizeController) stepSizeControlActors 1133 .next(); 1134 double suggestedStepSize = actor.suggestedStepSize(); 1135 if (suggestedStepSize < 0.0) { 1136 throw new IllegalActionException((Actor) actor, 1137 "Actor requests invalid step size: " 1138 + suggestedStepSize); 1139 } 1140 if (_debugging) { 1141 _debug("---- Step size control actor " 1142 + ((NamedObj) actor).getName() 1143 + " suggests next step size = " 1144 + suggestedStepSize); 1145 } 1146 if (suggestedStep > suggestedStepSize) { 1147 if (_debugging) { 1148 _debug("----- Revising step size to " 1149 + suggestedStepSize); 1150 } 1151 suggestedStep = suggestedStepSize; 1152 } 1153 } 1154 } 1155 // The suggested step size should not exceed the maximum step size. 1156 if (suggestedStep > _maxStepSize) { 1157 suggestedStep = _maxStepSize; 1158 } 1159 1160 // If there are breakpoints, set the suggested step size as the 1161 // time to the next breakpoint, if it is less than the value 1162 // so far. 1163 if (!_breakpoints.isEmpty()) { 1164 SuperdenseTime nextBreakpoint = (SuperdenseTime) _breakpoints 1165 .first(); 1166 Time breakpointTime = nextBreakpoint.timestamp(); 1167 double result = breakpointTime.subtract(getModelTime()) 1168 .getDoubleValue(); 1169 if (result < 0.0) { 1170 throw new InternalErrorException( 1171 "Missed a breakpoint at time " + breakpointTime 1172 + ". Current time is " + getModelTime()); 1173 } 1174 if (result < suggestedStep) { 1175 suggestedStep = result; 1176 if (_debugging) { 1177 _debug("----- The first breakpoint is at " 1178 + nextBreakpoint); 1179 _debug("----- Revising suggested step size due to breakpoint to " 1180 + suggestedStep); 1181 } 1182 } 1183 } 1184 1185 // Next ensure the selected step size does not take us 1186 // past the stop time. 1187 // NOTE: This test could possibly be eliminated by 1188 // putting the stop time on the breakpoint table. Be sure, 1189 // however, that this results in the right number of 1190 // events generated at the stop time. This is very tricky, 1191 // and probably not worth the effort. 1192 Time targetTime = getModelTime().add(suggestedStep); 1193 Time stopTime = getModelStopTime(); 1194 if (targetTime.compareTo(stopTime) > 0) { 1195 suggestedStep = stopTime.subtract(getModelTime()).getDoubleValue(); 1196 if (_debugging) { 1197 _debug("----- Revising step size due to stop time to " 1198 + suggestedStep); 1199 } 1200 } 1201 return suggestedStep; 1202 } 1203 1204 /** Override the base class to do nothing. The fire() method of 1205 * this director handles transferring inputs. 1206 * @param port The port to transfer tokens from. 1207 * @return False. 1208 * @exception IllegalActionException Not thrown in this base class. 1209 */ 1210 @Override 1211 public boolean transferInputs(IOPort port) throws IllegalActionException { 1212 return false; 1213 } 1214 1215 /** Override the base class to do nothing. The fire() method of 1216 * this director handles transferring outputs. 1217 * @param port The port to transfer tokens from. 1218 * @return False. 1219 * @exception IllegalActionException Not thrown in this base class. 1220 */ 1221 @Override 1222 public boolean transferOutputs(IOPort port) throws IllegalActionException { 1223 return false; 1224 } 1225 1226 /////////////////////////////////////////////////////////////////// 1227 //// protected methods //// 1228 1229 /** Return the current step size. 1230 * @return The current step size. 1231 */ 1232 protected double _getCurrentStepSize() { 1233 return _currentStepSize; 1234 } 1235 1236 /** Return the ODE solver used to resolve states by the director. 1237 * @return The ODE solver used to resolve states by the director. 1238 */ 1239 protected final ContinuousODESolver _getODESolver() { 1240 // This method is final for performance reason. 1241 return _ODESolver; 1242 } 1243 1244 /** Create and initialize all parameters to their default values. 1245 * This is called by the constructor. 1246 */ 1247 protected void _initParameters() { 1248 try { 1249 initStepSize = new Parameter(this, "initStepSize"); 1250 initStepSize.setExpression("0.1"); 1251 initStepSize.setTypeEquals(BaseType.DOUBLE); 1252 1253 maxStepSize = new Parameter(this, "maxStepSize"); 1254 maxStepSize.setExpression("1.0"); 1255 maxStepSize.setTypeEquals(BaseType.DOUBLE); 1256 1257 maxIterations = new Parameter(this, "maxIterations"); 1258 maxIterations.setExpression("20"); 1259 maxIterations.setTypeEquals(BaseType.INT); 1260 1261 errorTolerance = new Parameter(this, "errorTolerance"); 1262 errorTolerance.setExpression("1e-4"); 1263 errorTolerance.setTypeEquals(BaseType.DOUBLE); 1264 1265 iterations.setVisibility(Settable.NONE); 1266 1267 ODESolver = new StringParameter(this, "ODESolver"); 1268 ODESolver.setExpression("ExplicitRK23Solver"); 1269 ODESolver.addChoice("ExplicitRK23Solver"); 1270 ODESolver.addChoice("ExplicitRK45Solver"); 1271 /* FIXME: These solvers are currently not implemented in this package. 1272 ODESolver.addChoice(new StringToken("BackwardEulerSolver") 1273 .toString()); 1274 ODESolver.addChoice(new StringToken("ForwardEulerSolver") 1275 .toString()); 1276 */ 1277 } catch (IllegalActionException e) { 1278 //Should never happens. The parameters are always compatible. 1279 throw new InternalErrorException("Parameter creation error: " + e); 1280 } catch (NameDuplicationException ex) { 1281 throw new InvalidStateException(this, 1282 "Parameter name duplication: " + ex); 1283 } 1284 } 1285 1286 /** Instantiate an ODESolver from its classname. Given the solver's full 1287 * class name, this method will try to instantiate it by looking 1288 * for the corresponding java class. 1289 * @param className The solver's full class name. 1290 * @return a new ODE solver. 1291 * @exception IllegalActionException If the solver can not be created. 1292 */ 1293 protected final ContinuousODESolver _instantiateODESolver(String className) 1294 throws IllegalActionException { 1295 1296 // All solvers must be in the package given by _solverClasspath. 1297 if (!className.trim().startsWith(_solverClasspath)) { 1298 className = _solverClasspath + className; 1299 } 1300 1301 if (_debugging) { 1302 _debug("instantiating solver..." + className); 1303 } 1304 1305 ContinuousODESolver newSolver; 1306 1307 try { 1308 Class solver = Class.forName(className); 1309 newSolver = (ContinuousODESolver) solver.newInstance(); 1310 } catch (ClassNotFoundException e) { 1311 throw new IllegalActionException(this, 1312 "ODESolver: " + className + " is not found."); 1313 } catch (InstantiationException e) { 1314 throw new IllegalActionException(this, 1315 "ODESolver: " + className + " instantiation failed." + e); 1316 } catch (IllegalAccessException e) { 1317 throw new IllegalActionException(this, 1318 "ODESolver: " + className + " is not accessible."); 1319 } 1320 1321 newSolver._makeSolverOf(this); 1322 return newSolver; 1323 } 1324 1325 /** Return true if debugging is turned on. 1326 * This exposes whether debugging is happening to the package. 1327 * @return True if debugging is turned on. 1328 */ 1329 protected final boolean _isDebugging() { 1330 return _debugging; 1331 } 1332 1333 /** Return true if the solver is at the first or intermediate steps 1334 * of a multi-step solver. If there is an enclosing continuous 1335 * director, this method delegates to that director. Otherwise, 1336 * it checks the current step of the solver. 1337 * @return True if either the solver is not doing a multi-step 1338 * solution or it is at the last step of the multi-step solution. 1339 * @exception IllegalActionException If the Time class throws it. 1340 */ 1341 protected boolean _isIntermediateStep() throws IllegalActionException { 1342 ContinuousDirector enclosingDirector = _enclosingContinuousDirector(); 1343 if (enclosingDirector != null) { 1344 return enclosingDirector._isIntermediateStep(); 1345 } 1346 return _isIntermediateStep; 1347 } 1348 1349 /** Expose the debug method to the package. 1350 * @param message The message that is to be reported. 1351 */ 1352 protected void _reportDebugMessage(String message) { 1353 _debug(message); 1354 } 1355 1356 /////////////////////////////////////////////////////////////////// 1357 //// protected variables //// 1358 1359 /** A local boolean variable indicating whether this director is in 1360 * initialization phase execution. 1361 */ 1362 protected boolean _isInitializing = false; 1363 1364 /** The current time at the start of the current integration step. 1365 */ 1366 protected Time _iterationBeginTime; 1367 1368 /////////////////////////////////////////////////////////////////// 1369 //// private methods //// 1370 1371 /** Set all receivers on the inside of input ports to 1372 * absent. 1373 * @exception IllegalActionException If the send fails. 1374 */ 1375 private void _assertAbsentInside() throws IllegalActionException { 1376 CompositeActor container = (CompositeActor) getContainer(); 1377 Iterator inports = container.inputPortList().iterator(); 1378 while (inports.hasNext()) { 1379 IOPort port = (IOPort) inports.next(); 1380 int insideWidth = port.getWidthInside(); 1381 for (int i = 0; i < insideWidth; i++) { 1382 port.sendInside(i, null); 1383 } 1384 } 1385 } 1386 1387 /** Commit the current state by postfiring the actors under the 1388 * control of this director. Also, set the current time, index, 1389 * and step size for the next iteration. If the current step size 1390 * is 0.0, then this just increments the index. Otherwise, it sets 1391 * the index to 0. 1392 * @return True if it is OK to fire again. 1393 */ 1394 private boolean _commit() throws IllegalActionException { 1395 Time currentTime = getModelTime(); 1396 if (_debugging) { 1397 _debug("Committing the current states at " + currentTime); 1398 } 1399 1400 // If current time matches a time on the breakpoint table 1401 // with the current index, then remove that breakpoint because we have 1402 // just completed execution of that iteration. Only do this, however, 1403 // if the current step size is 0.0, since breakpoints should be dealt 1404 // with always with step size 0.0 1405 if (_currentStepSize == 0.0 && !_breakpoints.isEmpty()) { 1406 SuperdenseTime nextBreakpoint = (SuperdenseTime) _breakpoints 1407 .first(); 1408 Time breakpointTime = nextBreakpoint.timestamp(); 1409 int comparison = breakpointTime.compareTo(currentTime); 1410 if (comparison == 0 && nextBreakpoint.index() <= _index) { 1411 if (_debugging) { 1412 _debug("Removing breakpoint at " + nextBreakpoint); 1413 } 1414 _breakpoints.removeFirst(); 1415 } 1416 } 1417 1418 // Postfire the contained actors. 1419 // This must be done before refining the step size and 1420 // before updating _index because the actors may call fireAt() 1421 // in their postfire() method, which will insert breakpoints, 1422 // which will affect the next step size. 1423 // NOTE: This increments _index. 1424 // FIXME: This also resets all receivers, which is redundant since in this 1425 // director, resetting receivers is handled by fire(). Efficiency concern? 1426 _postfireReturns = super.postfire(); 1427 1428 // If the current time is equal to the stop time, return false. 1429 // Check, however, to make sure that the breakpoints table 1430 // does not contain the current model time, which would mean 1431 // that more events may be generated at this time. 1432 // If the index of the breakpoint is the same as the current 1433 // index, then we have executed it. This must be called 1434 // after postfire() of the controlled actors is called, because 1435 // they may call fireAt(), which inserts events in the breakpoint 1436 // table. 1437 if (currentTime.equals(getModelStopTime())) { 1438 SuperdenseTime nextBreakpoint = (SuperdenseTime) _breakpoints 1439 .first(); 1440 if (nextBreakpoint == null 1441 || nextBreakpoint.timestamp().compareTo(currentTime) > 0) { 1442 return false; 1443 } 1444 } 1445 1446 // Update the index. Note that this must happen before 1447 // we refine the step size because the updated value gives 1448 // the step size for the _next_ iteration. 1449 if (_currentStepSize != 0.0) { 1450 _index = 0; 1451 } 1452 1453 // Set the suggested step size for next integration step by polling 1454 // the contained actors and examining the breakpoint table. 1455 _setCurrentStepSize(suggestedStepSize()); 1456 1457 // Set the start time of the current iteration. 1458 // The iterationBegintime will be used for roll back when the current 1459 // step size is incorrect. 1460 _iterationBeginTime = currentTime; 1461 _iterationBeginIndex = _index; 1462 1463 return _postfireReturns; 1464 } 1465 1466 /** Return the enclosing continuous director, or null if there 1467 * is none. The enclosing continuous director is a director 1468 * above this in the hierarchy, possibly separated by composite 1469 * actors with actors that implement the QuasiTransparentDirector 1470 * interface, such as FSMDirector or CaseDirector. 1471 * @return The enclosing ContinuousDirector, or null if there is none. 1472 */ 1473 private ContinuousDirector _enclosingContinuousDirector() { 1474 if (_enclosingContinuousDirectorVersion != _workspace.getVersion()) { 1475 // Update the cache. 1476 _enclosingContinuousDirector = null; 1477 NamedObj container = getContainer().getContainer(); 1478 while (container != null) { 1479 if (container instanceof Actor) { 1480 Director director = ((Actor) container).getDirector(); 1481 if (director instanceof ContinuousDirector) { 1482 _enclosingContinuousDirector = (ContinuousDirector) director; 1483 break; 1484 } 1485 if (!(director instanceof QuasiTransparentDirector)) { 1486 break; 1487 } 1488 } 1489 container = container.getContainer(); 1490 } 1491 _enclosingContinuousDirectorVersion = _workspace.getVersion(); 1492 } 1493 return _enclosingContinuousDirector; 1494 } 1495 1496 /** Throw an exception if there are any breakpoints in the breakpoint 1497 * table before the specified time. 1498 * @param time The time. 1499 * @param index The superdense time index. 1500 * @exception IllegalActionException If there are any breakpoints in the 1501 * breakpoint table earlier than the specified time. 1502 */ 1503 private void _handleBreakpointsBefore(Time time, int index) 1504 throws IllegalActionException { 1505 if (!_breakpoints.isEmpty()) { 1506 SuperdenseTime nextBreakpoint = (SuperdenseTime) _breakpoints 1507 .first(); 1508 Time breakpointTime = nextBreakpoint.timestamp(); 1509 if (breakpointTime.compareTo(time) < 0) { 1510 throw new IllegalActionException(this, 1511 "ContinuousDirector expected to be fired at time " 1512 + breakpointTime 1513 + " but instead is being fired at time " 1514 + time); 1515 } 1516 } 1517 // NOTE: An alternative would be to discard breakpoints earlier 1518 // than the specified time. The code for doing that is below. 1519 /* 1520 while (!_breakpoints.isEmpty()) { 1521 SuperdenseTime nextBreakpoint = (SuperdenseTime) _breakpoints 1522 .first(); 1523 Time breakpointTime = nextBreakpoint.timestamp(); 1524 int comparison = breakpointTime.compareTo(time); 1525 if (comparison > 0 1526 || (comparison == 0 && nextBreakpoint.index() > index)) { 1527 // Next breakpoint is in the future. 1528 break; 1529 } else { 1530 if (_debugging) { 1531 _debug(">>>> Discarding a breakpoint from the breakpoint table: " 1532 + nextBreakpoint); 1533 } 1534 _breakpoints.removeFirst(); 1535 } 1536 } 1537 */ 1538 } 1539 1540 /** Initialize the local variables of this ContinuousDirector. Create or 1541 * clear the breakpoints table. Instantiate an ODE solver. 1542 * This method is called in the preinitialize method. 1543 */ 1544 private void _initializeLocalVariables() throws IllegalActionException { 1545 _maxIterations = ((IntToken) maxIterations.getToken()).intValue(); 1546 _maxStepSize = ((DoubleToken) maxStepSize.getToken()).doubleValue(); 1547 _currentStepSize = _initStepSize; 1548 1549 _iterationBeginTime = getModelStartTime(); 1550 _iterationBeginIndex = 0; 1551 1552 // clear the existing breakpoint table or 1553 // create a breakpoint table if necessary 1554 if (_breakpoints != null) { 1555 if (_debugging) { 1556 _debug(getFullName(), "clears the breakpoint table."); 1557 } 1558 _breakpoints.clear(); 1559 } else { 1560 if (_debugging) { 1561 _debug(getFullName(), "creates a breakpoint table."); 1562 } 1563 _breakpoints = new TotallyOrderedSet(new GeneralComparator()); 1564 } 1565 1566 // Instantiate an ODE solver, using the class name given 1567 // by ODESolver parameter, which is a string parameter. 1568 String solverClassName; 1569 // If there is an enclosing ContinuousDirector, then use its solver 1570 // specification instead of the local one. 1571 ContinuousDirector enclosingDirector = _enclosingContinuousDirector(); 1572 if (enclosingDirector != null) { 1573 solverClassName = enclosingDirector.ODESolver.stringValue().trim(); 1574 } else { 1575 solverClassName = ODESolver.stringValue().trim(); 1576 } 1577 _ODESolver = _instantiateODESolver(_solverClasspath + solverClassName); 1578 } 1579 1580 /** Postfire method when this director is at the top level. 1581 * @return True if it is OK to fire again. 1582 */ 1583 private boolean _postfireAtTopLevel() throws IllegalActionException { 1584 // Commit the current state and postfire all actors. 1585 boolean result = _commit(); 1586 // The above call will increment the index if the current step 1587 // size is zero, and set it to zero otherwise. At the top level, 1588 // we want to set it to 1, not zero because we just committed 1589 // the index 0 iteration for the current time. The next iteration 1590 // should run with index 1. 1591 if (_index == 0) { 1592 _index++; 1593 } 1594 return result; 1595 } 1596 1597 /** Postfire method when the enclosing director 1598 * is an instance of this same class. 1599 * @return True if it is OK to fire again. 1600 */ 1601 private boolean _postfireWithEnclosingContinuousDirector() 1602 throws IllegalActionException { 1603 boolean postfireResult = _commit(); 1604 // The above call will increment the index if the current step 1605 // size is zero, and set it to zero otherwise. At the top level, 1606 // we want to set it to 1, not zero because we just committed 1607 // the index 0 iteration for the current time. The next iteration 1608 // should run with index 1. 1609 if (_index == 0) { 1610 _index++; 1611 } 1612 1613 // Request a refiring at the current time if the 1614 // next step size has been set to 0.0, and at the 1615 // next breakpoint time otherwise. 1616 if (_currentStepSize == 0.0) { 1617 // We assume the enclosing director will 1618 // post this firing request at the next microstep. 1619 fireContainerAt(getModelTime()); 1620 } else if (_breakpoints.size() > 0) { 1621 // Request a firing at the time of the first breakpoint. 1622 SuperdenseTime nextBreakpoint = (SuperdenseTime) _breakpoints 1623 .first(); 1624 fireContainerAt(nextBreakpoint.timestamp(), nextBreakpoint.index()); 1625 } 1626 1627 return postfireResult; 1628 } 1629 1630 /** Postfire method when the enclosing director 1631 * is not an instance of this same class. 1632 * @return True if it is OK to fire again. 1633 */ 1634 private boolean _postfireWithEnclosingNonContinuousDirector() 1635 throws IllegalActionException { 1636 Time currentTime = getModelTime(); 1637 // If a commit is pending, just do the commit and request a refiring 1638 // at the current time. 1639 if (_commitIsPending) { 1640 _commitIsPending = false; 1641 fireContainerAt(currentTime, 0); 1642 1643 // Commit the current state and postfire all actors. 1644 boolean result = _commit(); 1645 // The above call will increment the index if the current step 1646 // size is zero, and set it to zero otherwise. At the top level, 1647 // we want to set it to 1, not zero because we just committed 1648 // the index 0 iteration for the current time. The next iteration 1649 // should run with index 1. 1650 if (_index == 0) { 1651 _index++; 1652 } 1653 return result; 1654 } 1655 Director enclosingDirector = ((Actor) getContainer()) 1656 .getExecutiveDirector(); 1657 int currentTimeAheadOfOutsideTime = currentTime 1658 .compareTo(enclosingDirector.getModelTime()); 1659 if (currentTimeAheadOfOutsideTime > 0) { 1660 // We have to defer the commit until current time of the environment 1661 // matches our local current time. Call fireAt() to ensure that the 1662 // enclosing director invokes prefire at the local current time. 1663 // This local current time should not exceed the least time on 1664 // the breakpoint table. 1665 // The following will throw an exception if the enclosing director 1666 // does not respect the fireAt() request exactly. 1667 fireContainerAt(currentTime, 0); 1668 // When that firing occurs, we want the index to be 0. 1669 _index = 0; 1670 _commitIsPending = true; 1671 if (_debugging) { 1672 _debug("postfire() requests refiring at time " + currentTime 1673 + " and defers the commit."); 1674 } 1675 return true; 1676 } else { 1677 // Current time is less than or equal to the environment time. 1678 // NOTE: It is, in theory, impossible for local current time to 1679 // be less than the environment time because the prefire() method 1680 // would have thrown an exception. Hence, current time must match 1681 // the environment time. 1682 1683 // Request a refiring at the current time. 1684 // The reason for this is that local time has not advanced, 1685 // so we can't be sure of any interval of future time over which 1686 // we will not produce an event. Only when the step size is 1687 // greater than zero, as we have speculatively executed into 1688 // the future, can we allow the enclosing director to advance time. 1689 // The following will throw an exception if the enclosing director 1690 // does not respect the fireAt() request exactly. 1691 // However, it could be that the current iteration is actually 1692 // a deferred commit, in which case local time has advanced 1693 // and we don't need to request a refiring at the current time. 1694 fireContainerAt(currentTime, 0); 1695 1696 // The following call will increment the index if the current step 1697 // size is zero, and set it to zero otherwise. At the top level, 1698 return _commit(); 1699 } 1700 } 1701 1702 /** Prefire method when this director is at the top level. 1703 * @return True if it is OK to fire. 1704 */ 1705 private boolean _prefireAtTopLevel() throws IllegalActionException { 1706 Time currentTime = getModelTime(); 1707 // If the current time and index matches the first entry in the breakpoint 1708 // table, then remove that entry. 1709 if (!_breakpoints.isEmpty()) { 1710 SuperdenseTime nextBreakpoint = (SuperdenseTime) _breakpoints 1711 .first(); 1712 Time breakpointTime = nextBreakpoint.timestamp(); 1713 int comparison = breakpointTime.compareTo(currentTime); 1714 // Note that we don't compare the superdense time of the breakpoint 1715 // because it is not important. It was created, probably, with 1716 // fireAt(), which does not take a superdense time index. 1717 if (comparison < 0) { 1718 // At the top level, we should not have missed a breakpoint. 1719 throw new IllegalActionException(this, 1720 "Missed a breakpoint time at " + breakpointTime 1721 + ", with index " + nextBreakpoint.index()); 1722 } else if (comparison == 0 && nextBreakpoint.index() <= _index) { 1723 if (_debugging) { 1724 _debug("-- The current superdense time is a breakpoint, " 1725 + nextBreakpoint + ", which is removed."); 1726 } 1727 _breakpoints.removeFirst(); 1728 } 1729 } 1730 boolean result = super.prefire(); 1731 // Initialize everything for the fixedpoint iteration. 1732 // NOTE: This is done in fire. Not necessary here. 1733 // _resetAllReceivers(); 1734 1735 if (_debugging) { 1736 _debug("ContinuousDirector: prefire() returns " + result); 1737 } 1738 return result; 1739 } 1740 1741 /** Prefire method when the enclosing director 1742 * is an instance of this same class. 1743 * @return True if it is OK to fire. 1744 */ 1745 private boolean _prefireWithEnclosingContinuousDirector() 1746 throws IllegalActionException { 1747 // Call the super.prefire() method, which will synchronize time to the outside time. 1748 // That time may get modified below to align not with the immediate outside time, 1749 // but rather with the time of the enclosing continuous director, modified as 1750 // needed by accumulated suspend time. As a consequence, we set a flag to 1751 // ignore the time that is set. 1752 boolean result = true; 1753 Time currentTime = getModelTime(); 1754 try { 1755 _ignoreSetTime = true; 1756 result = super.prefire(); 1757 } finally { 1758 _ignoreSetTime = false; 1759 } 1760 1761 // Set the time and step size to match that of the enclosing director. 1762 ContinuousDirector enclosingDirector = _enclosingContinuousDirector(); 1763 _currentStepSize = enclosingDirector._currentStepSize; 1764 // Do not use setCommittedTime on the following line because we 1765 // are probably speculatively executing into the future. 1766 localClock.setLocalTime( 1767 localClock.getLocalTimeForCurrentEnvironmentTime()); 1768 1769 if (_debugging) { 1770 _debug("-- Setting current time to " + currentTime 1771 + ", which aligns with the enclosing director's time of " 1772 + enclosingDirector.getModelTime()); 1773 } 1774 1775 _iterationBeginTime = localClock.getLocalTimeForEnvironmentTime( 1776 enclosingDirector._iterationBeginTime); 1777 1778 // FIXME: Probably shouldn't make the index match that of the environment! 1779 // There may have been suspensions happening. So what should the index be? 1780 // Here we set it to zero if the round is greater than zero. But what 1781 // about other conditions? 1782 // _index = enclosingDirector._index; 1783 int round = enclosingDirector._ODESolver._getRound(); 1784 if (round > 0) { 1785 _index = 0; 1786 } else if (_currentStepSize == 0.0) { 1787 /* FIXME: Bogus. This will force the first execution to be at microstep 1. 1788 _index++; 1789 */ 1790 } 1791 1792 _iterationBeginIndex = enclosingDirector._iterationBeginIndex; 1793 1794 // If we have passed the stop time, then return false. 1795 // This can occur if we are inside a modal model and were not 1796 // active when the stop time elapsed. 1797 if (_iterationBeginTime.compareTo(getModelStopTime()) > 0) { 1798 if (_debugging) { 1799 _debug("-- prefire() returns false because stop time is exceeded at " 1800 + _iterationBeginTime); 1801 } 1802 return false; 1803 } 1804 1805 // Handle breakpoint entries earlier 1806 // than or equal to the iteration begin time. 1807 _handleBreakpointsBefore(_iterationBeginTime, _index); 1808 1809 // Initialize everything for the fixedpoint iteration. 1810 // NOTE: This is done in fire(), and anyway, should not be done 1811 // if there is commit pending. 1812 // _resetAllReceivers(); 1813 1814 if (_debugging) { 1815 _debug("-- prefire() returns " + result); 1816 } 1817 return result; 1818 } 1819 1820 /** Prefire method when the enclosing director 1821 * is not an instance of this same class. 1822 * @return True if it is OK to fire. 1823 */ 1824 private boolean _prefireWithEnclosingNonContinuousDirector() 1825 throws IllegalActionException { 1826 CompositeActor container = (CompositeActor) getContainer(); 1827 Director executiveDirector = ((Actor) container).getExecutiveDirector(); 1828 Time currentTime = getModelTime(); 1829 1830 // Check the enclosing model time against the local model time. 1831 // Note that time has already been automatically adjusted with the 1832 // accumulated suspend time. 1833 Time outTime = localClock.getLocalTimeForCurrentEnvironmentTime(); 1834 1835 int localTimeExceedsOutsideTime = currentTime.compareTo(outTime); 1836 1837 Time modifiedTime = _consultTimeRegulators(currentTime); 1838 int modifiedTimeExceedsLocalTime = modifiedTime.compareTo(currentTime); 1839 1840 // Rollback has to occur if either the local time exceeds the modified time 1841 // or a time regulator requires a smaller current time than the current time. 1842 if (localTimeExceedsOutsideTime > 0 1843 || modifiedTimeExceedsLocalTime < 0) { 1844 /////////////////////////////////////////////////////////////// 1845 // First case: Local current time exceeds that of the environment. 1846 if (!_commitIsPending) { 1847 throw new IllegalActionException(this, 1848 "The model time of " + container.getFullName() 1849 + " is greater than the environment time. " 1850 + "Environment: " + outTime 1851 + ", the model time (iteration begin time): " 1852 + currentTime); 1853 } 1854 if (modifiedTimeExceedsLocalTime < 0 1855 && modifiedTime.compareTo(outTime) < 0) { 1856 throw new IllegalActionException(this, 1857 "A TimeRegulator requires time to be set back to " 1858 + modifiedTime 1859 + ", which is less than the last commit time of " 1860 + outTime); 1861 } 1862 // If we get here, local time exceeds the environment time 1863 // and we have speculatively executed past that local time. 1864 // But now, we cannot commit that execution because an unexpected 1865 // event has occurred. Instead, we have to re-do the integration 1866 // with a smaller step size that brings us up to the current 1867 // environment time. 1868 // NOTE: This depends on the property that if an integration 1869 // step with a larger step size was successful and produced 1870 // no events, then an integration step with this now smaller 1871 // step size will also be successful and produce no events. 1872 // FIXME: This property is not true in general. E.g., we 1873 // might have missed a level crossing with a larger step 1874 // size that we won't miss with the smaller one! What 1875 // to do about this? 1876 _currentStepSize = outTime.subtract(_iterationBeginTime) 1877 .getDoubleValue(); 1878 // If the step size is now negative, then we are trying 1879 // to roll back too far. 1880 if (_currentStepSize < 0.0) { 1881 throw new IllegalActionException(this, 1882 "Attempting to roll back time from " 1883 + _iterationBeginTime + " to " + outTime 1884 + ", but state has been committed."); 1885 } 1886 rollBackToCommittedState(); 1887 _commitIsPending = false; 1888 // Set a flag to prevent the fire() method from reading inputs 1889 // and resetting the step size to 0.0 if there are inputs 1890 // present. Presumably, there are no inputs present at the 1891 // _iterationBeginTime because had there been any, then 1892 // the speculative execution that we are rolling back 1893 // would not have occurred. However, it could be a new input 1894 // has occurred at the _iterationBeginTime, but with a 1895 // larger microstep. In this case, there could be new inputs. 1896 if (_currentStepSize != 0.0) { 1897 _redoingSolverIteration = true; 1898 } 1899 } else if (localTimeExceedsOutsideTime == 0 && _commitIsPending) { 1900 /////////////////////////////////////////////////////////////// 1901 // Second case: 1902 // A commit is pending and the environment time matches 1903 // our local time. This means that no additional events have 1904 // arrived during the interval of the last successful integration 1905 // step. This microstep will simply do the commit that is pending, 1906 // producing any outputs previously computed in the speculative 1907 // execution. It will then request a refiring at the current time. 1908 // FIXME: Why set the current step size to zero? 1909 _currentStepSize = 0.0; 1910 // If the enclosing director implements SuperdenseTimeDirector, 1911 // then we used to get the current index from it. 1912 // However, we can't really trust the outside director to have 1913 // incremented the index, and it would incorrect to execute 1914 // without increment it. So we use our own index. 1915 /* 1916 if (executiveDirector instanceof SuperdenseTimeDirector) { 1917 _index = ((SuperdenseTimeDirector) executiveDirector).getIndex(); 1918 } 1919 */ 1920 return true; 1921 } else if (localTimeExceedsOutsideTime < 0) { 1922 /////////////////////////////////////////////////////////////// 1923 // Third case: 1924 // Local current time is behind environment time. 1925 // This should not happen. This used to happen if 1926 // we are inside a modal model and have been 1927 // disabled at the time that the commit 1928 // would be effective. We should cancel any pending commit. 1929 if (_commitIsPending) { 1930 _commitIsPending = false; 1931 rollBackToCommittedState(); 1932 } 1933 1934 // We should set current time to the environment time and set 1935 // the step size to zero. 1936 this.localClock.setLocalTime( 1937 localClock.getLocalTimeForCurrentEnvironmentTime()); 1938 1939 if (_debugging) { 1940 _debug("-- Setting current time to match enclosing non-ContinuousDirector: " 1941 + currentTime + ", and step size to 0.0."); 1942 } 1943 _currentStepSize = 0.0; 1944 } else { 1945 /////////////////////////////////////////////////////////////// 1946 // Fourth case: 1947 // Environment time matches local time and there is no pending 1948 // commit. 1949 // Adjust the step size to 1950 // make sure the time does not exceed the next iteration 1951 // time of the environment during this next integration step. 1952 Time environmentNextIterationTime = localClock 1953 .getLocalTimeForEnvironmentTime( 1954 executiveDirector.getModelNextIterationTime()); 1955 1956 Time localTargetTime = _iterationBeginTime.add(_currentStepSize); 1957 if (environmentNextIterationTime.compareTo(localTargetTime) < 0) { 1958 _currentStepSize = environmentNextIterationTime 1959 .subtract(currentTime).getDoubleValue(); 1960 if (_debugging) { 1961 _debug("-- Revising step size due to environment's next iteration time to " 1962 + _currentStepSize); 1963 } 1964 } 1965 // If the enclosing director implements SuperdenseTimeDirector, 1966 // then we used to get the current index from it. 1967 // But we can't trust the enclosing director to do the right 1968 // thing with the index. It becomes too fragile. 1969 /* 1970 if (executiveDirector instanceof SuperdenseTimeDirector) { 1971 _index = ((SuperdenseTimeDirector) executiveDirector).getIndex(); 1972 } 1973 */ 1974 } 1975 1976 // If the current time and index match the first entry in the breakpoint 1977 // table, then remove that entry. An index is deemed to match if 1978 // the breakpoint index is less than or equal to the current time index. 1979 if (!_breakpoints.isEmpty()) { 1980 SuperdenseTime nextBreakpoint = (SuperdenseTime) _breakpoints 1981 .first(); 1982 Time breakpointTime = nextBreakpoint.timestamp(); 1983 localTimeExceedsOutsideTime = breakpointTime.compareTo(currentTime); 1984 if (localTimeExceedsOutsideTime < 0) { 1985 throw new IllegalActionException(this, 1986 "ContinuousDirector expected to be fired at time " 1987 + breakpointTime 1988 + " but instead is being fired at time " 1989 + currentTime); 1990 } 1991 1992 // NOTE: An alternative would be 1993 // to remove any breakpoints from the table that are now in the past. 1994 // In theory, this should not happen. The code for doing that is below. 1995 /* 1996 while (localTimeExceedsOutsideTime < 0 1997 || (localTimeExceedsOutsideTime == 0 && nextBreakpoint 1998 .index() < _index)) { 1999 if (_debugging) { 2000 _debug("Remove a breakpoint that we missed: " 2001 + breakpointTime); 2002 } 2003 _breakpoints.removeFirst(); 2004 if (_breakpoints.isEmpty()) { 2005 break; 2006 } 2007 nextBreakpoint = (SuperdenseTime) _breakpoints.first(); 2008 breakpointTime = nextBreakpoint.timestamp(); 2009 localTimeExceedsOutsideTime = breakpointTime 2010 .compareTo(_currentTime); 2011 } 2012 */ 2013 if (localTimeExceedsOutsideTime == 0 2014 && nextBreakpoint.index() <= _index) { 2015 if (_debugging) { 2016 _debug("-- The current superdense time is a breakpoint, " 2017 + nextBreakpoint + ", which is removed."); 2018 } 2019 _breakpoints.removeFirst(); 2020 } 2021 } 2022 2023 // Do not call super.prefire() because it sets the local mode 2024 // time to match that of the executive director, defeating the 2025 // above logic. Therefore, we have duplicate what it does here. 2026 _synchronizeToRealTime(); 2027 _postfireReturns = true; 2028 2029 // Initialize everything for the fixedpoint iteration. 2030 // NOTE: This is handled by fire(), and anyway, should 2031 // not be done if there is a commit pending. 2032 // _resetAllReceivers(); 2033 2034 if (_debugging) { 2035 _debug("Called prefire(), which returns true."); 2036 } 2037 return true; 2038 } 2039 2040 /** Set the current step size. 2041 * @param stepSize The step size to be set. 2042 * @see #_currentStepSize 2043 */ 2044 private void _setCurrentStepSize(double stepSize) { 2045 if (_debugging) { 2046 _debug("----- Setting the current step size to " + stepSize); 2047 } 2048 _currentStepSize = stepSize; 2049 } 2050 2051 /** Return a list of stateful components, which includes actors 2052 * deeply contained within the container of this director that 2053 * implement the ContinuousStatefulComponent interface and 2054 * directors of opaque composite actors within the container 2055 * of this director that implement the same interface. 2056 * @return A list of objects implementing ContinuousStatefulComponent. 2057 */ 2058 private List _statefulComponents() { 2059 if (_workspace.getVersion() != _statefulComponentsVersion) { 2060 // Construct the list. 2061 _statefulComponents.clear(); 2062 CompositeEntity container = (CompositeEntity) getContainer(); 2063 Iterator actors = container.deepEntityList().iterator(); 2064 while (actors.hasNext()) { 2065 Object actor = actors.next(); 2066 if (actor instanceof ContinuousStatefulComponent) { 2067 _statefulComponents.add(actor); 2068 } else if (actor instanceof CompositeActor 2069 && ((CompositeEntity) actor).isOpaque() 2070 && !((CompositeEntity) actor).isAtomic()) { 2071 Director director = ((Actor) actor).getDirector(); 2072 if (director instanceof ContinuousStatefulComponent) { 2073 _statefulComponents.add(director); 2074 } 2075 } 2076 } 2077 _statefulComponentsVersion = _workspace.getVersion(); 2078 } 2079 return _statefulComponents; 2080 } 2081 2082 /** Return a list of step-size controllers. 2083 * @return A list of step-size control actors. 2084 */ 2085 private List _stepSizeControllers() { 2086 if (_workspace.getVersion() != _stepSizeControllersVersion) { 2087 // Construct the list. 2088 _stepSizeControllers.clear(); 2089 CompositeEntity container = (CompositeEntity) getContainer(); 2090 Iterator actors = container.deepEntityList().iterator(); 2091 while (actors.hasNext()) { 2092 Object actor = actors.next(); 2093 if (actor instanceof ContinuousStepSizeController) { 2094 _stepSizeControllers.add(actor); 2095 } else if (actor instanceof CompositeActor 2096 && ((CompositeEntity) actor).isOpaque() 2097 && !((CompositeEntity) actor).isAtomic()) { 2098 Director director = ((Actor) actor).getDirector(); 2099 if (director instanceof ContinuousStepSizeController) { 2100 _stepSizeControllers.add(director); 2101 } 2102 } 2103 } 2104 _stepSizeControllersVersion = _workspace.getVersion(); 2105 } 2106 return _stepSizeControllers; 2107 } 2108 2109 /** Transfer inputs from the environment to inside. 2110 * @exception IllegalActionException If the transferInputs(Port) 2111 * method throws it. 2112 * @return True if at least one token is transferred. 2113 */ 2114 private boolean _transferInputsToInside() throws IllegalActionException { 2115 // If there are no input ports, this method does nothing. 2116 if (_debugging) { 2117 _debug("** Transfer inputs to the inside."); 2118 } 2119 boolean result = false; 2120 CompositeActor container = (CompositeActor) getContainer(); 2121 Iterator inports = container.inputPortList().iterator(); 2122 while (inports.hasNext() && !_stopRequested) { 2123 IOPort port = (IOPort) inports.next(); 2124 result = super.transferInputs(port) || result; 2125 } 2126 return result; 2127 } 2128 2129 /** Transfer outputs to the environment. 2130 * @exception IllegalActionException If the transferOutputs(Port) 2131 * method throws it. 2132 */ 2133 private void _transferOutputsToEnvironment() throws IllegalActionException { 2134 if (_debugging) { 2135 _debug("** Transfer outputs to the environment."); 2136 } 2137 2138 // If there are no output ports, this method does nothing. 2139 CompositeActor container = (CompositeActor) getContainer(); 2140 Iterator outports = container.outputPortList().iterator(); 2141 while (outports.hasNext() && !_stopRequested) { 2142 IOPort p = (IOPort) outports.next(); 2143 super.transferOutputs(p); 2144 } 2145 } 2146 2147 /////////////////////////////////////////////////////////////////// 2148 //// private variables //// 2149 2150 /** A table for breakpoints. */ 2151 private TotallyOrderedSet _breakpoints; 2152 2153 /** Flag indicating that postfire() did not commit the state at the 2154 * local current time. 2155 */ 2156 private boolean _commitIsPending = false; 2157 2158 /** Simulation step sizes. */ 2159 private double _currentStepSize; 2160 2161 /** The enclosing continuous director. */ 2162 private ContinuousDirector _enclosingContinuousDirector = null; 2163 2164 /** The version for __enclosingContinuousDirector. */ 2165 private long _enclosingContinuousDirectorVersion = -1; 2166 2167 /** The error tolerance for state resolution. */ 2168 private double _errorTolerance; 2169 2170 /** Flag to temporarily ignore the setModelTime() calls. */ 2171 private boolean _ignoreSetTime = false; 2172 2173 /** A cache of the value of initStepSize. */ 2174 private double _initStepSize; 2175 2176 /** Flag indicating that the solver is iterating through the first or 2177 * intermediate steps in a multi-step solver. 2178 */ 2179 private boolean _isIntermediateStep = false; 2180 2181 /** The index of the time at which the current integration step began. */ 2182 private int _iterationBeginIndex; 2183 2184 /** The maximum iterations for implicit ODE solver to resolve states. */ 2185 private int _maxIterations; 2186 2187 /** The maximum step size used for integration. */ 2188 private double _maxStepSize; 2189 2190 /** The ODE solver, which is an instance of the class given by 2191 * the <i>ODESolver</i> parameter. 2192 */ 2193 private ContinuousODESolver _ODESolver = null; 2194 2195 /** Flag indicating that we are redoing a speculative solver iteration. */ 2196 private boolean _redoingSolverIteration = false; 2197 2198 /** The package name for the solvers supported by this director. */ 2199 private static String _solverClasspath = "ptolemy.domains.continuous.kernel.solver."; 2200 2201 /** The list of stateful actors. */ 2202 private List _statefulComponents = new LinkedList(); 2203 2204 /** The version for the list of step size control actors. */ 2205 private long _statefulComponentsVersion = -1; 2206 2207 /** The list of step size control actors. */ 2208 private List _stepSizeControllers = new LinkedList(); 2209 2210 /** The version for the list of step size control actors. */ 2211 private long _stepSizeControllersVersion = -1; 2212 2213 /** The local flag variable indicating whether the we have tried 2214 * the time resolution as the integration step size. */ 2215 private boolean _triedTheMinimumStepSize = false; 2216}