Main /
CapeCodeZeroDelayLoopsSee Cape Code Host -> Loops for how to avoid zero delay loops in Cape Code. This page is about the details of the implementation. The ProblemWhen the model is run, the following stack trace appears ptolemy.kernel.util.IllegalActionException: Found a zero delay loop containing .WebSocketClientJS.WebSocketServer in .WebSocketClientJS and .WebSocketClientJS.WebSocketServer at ptolemy.actor.util.CausalityInterfaceForComposites._computeOutputPortDepth(CausalityInterfaceForComposites.java:755) at ptolemy.actor.util.CausalityInterfaceForComposites._computeInputDepth(CausalityInterfaceForComposites.java:694) at ptolemy.actor.util.CausalityInterfaceForComposites._computeOutputPortDepth(CausalityInterfaceForComposites.java:763) at ptolemy.actor.util.CausalityInterfaceForComposites._computeInputDepth(CausalityInterfaceForComposites.java:694) at ptolemy.actor.util.CausalityInterfaceForComposites._computeActorDepth(CausalityInterfaceForComposites.java:530) at ptolemy.actor.util.CausalityInterfaceForComposites.checkForCycles(CausalityInterfaceForComposites.java:95) at ptolemy.domains.de.kernel.DEDirector.preinitialize(DEDirector.java:1338) at ptolemy.actor.CompositeActor.preinitialize(CompositeActor.java:1820) at ptolemy.actor.Manager.preinitializeAndResolveTypes(Manager.java:1030) at ptolemy.actor.Manager.initialize(Manager.java:702) at ptolemy.actor.Manager.execute(Manager.java:354) at ptolemy.actor.Manager.run(Manager.java:1252) at ptolemy.actor.Manager$PtolemyRunThread.run(Manager.java:1903) The proposed fix is to set the this.output('connection', {'spontaneous': true}); However, this does not solve the problem. AnalysisThe MicrostepDelay actor can be used to fix the zero delay loop, see The MicrostepDelay actor has two methods that are used to fix the loop: /** Declare that the output does not depend on the input in a firing. * @exception IllegalActionException If the causality interface * cannot be computed. * @see #getCausalityInterface() */ @Override public void declareDelayDependency() throws IllegalActionException { _declareDelayDependency(input, output, 0.0); } ... /** Return false indicating that this actor can be fired even if * the inputs are unknown. * @return False. */ @Override public boolean isStrict() { return false; }
/** Assign priorities to contained accessors based on a topological sort of the * connectivity graph. Priorities are integers (positive or negative), where a lower * number indicates a higher priority. The number for an accessor is assured of being * higher than the number for any upstream accessor. An accessor A is upstream of * an accessor B if A has an output connected to an input of B and that output is not * marked "spontaneous" (that is, it does not have an option with name "spontaneous" * and value true). A spontaneous output is produced by an asynchronous callback * rather than as a response to an input. Every directed cycle in a connectivity * graph must contain at least one spontaneous output or there will be a deadlock * due to a causality loop. */ Accessor.prototype.assignPriorities = function() { ...
/** Assuming that the specified accessor has an assigned priority, follow its * connections downstream and assign priorities to connected accessors. * @param accessor The contained accessor with a priority. * @param cyclePriority If we encounter an accessor with this priority, then * there is a causality loop. */ Accessor.prototype.assignImpliedPrioritiesDownstream = function(accessor, cyclePriority) { ... and /** Assuming that the specified accessor has an assigned priority, follow its * connections upstream and assign priorities to connected accessors. * @param accessor The contained accessor with a priority. * @param cyclePriority If we encounter an accessor with this priority, then * there is a causality loop. */ Accessor.prototype.assignImpliedPrioritiesUpstream = function(accessor, cyclePriority) { var myPriority = accessor.priority; // To get repeatable priorities, iterate over outputs in order. for (var i = 0; i < accessor.outputList.length; i++) { var output = accessor.outputs[accessor.outputList[i]]; if (output.spontaneous) { // Output is spontaneous, so my priority has no implications // for downstream accessors. continue; } if (output.destinations) { // There are destination accessors. ... These two functions loop through the outputs and if the output is marked as spontaneous The priorities are used here: /** Schedule a reaction of the specified contained accessor. * This puts the accessor onto the event queue in priority order. * This assumes that priorities are unique to each accessor. * It is necessary for the react() function to be invoked for this event * to be handled, so this function uses setTimeout(function, time) with time * argument 0 to schedule a reaction. This ensures that the reaction occurs * after the currently executing function has completely executed. * @param accessor The accessor. */ Accessor.prototype.scheduleEvent = function(accessor) { ... SolutionThe solution was to add |