{"id":1129,"date":"2016-11-14T16:39:47","date_gmt":"2016-11-14T21:39:47","guid":{"rendered":"http:\/\/rpchurchill.com\/?p=1129"},"modified":"2017-02-03T14:26:04","modified_gmt":"2017-02-03T19:26:04","slug":"a-simple-discrete-event-simulation-part-41","status":"publish","type":"post","link":"https:\/\/rpchurchill.com\/wordpress\/posts\/2016\/11\/14\/a-simple-discrete-event-simulation-part-41\/","title":{"rendered":"A Simple Discrete-Event Simulation: Part 41"},"content":{"rendered":"<p>Today I took a step back to slightly simpler version of the simulation that uses the original version of the Path components (the ones that have the nodes built into them and act like standalone components).  I took two steps forward, however, in that I added new complexity to the framework by providing the ability to include multiple entry points and the ability to either step through the simulation or run it so it steps through the simulation automatically.  I also added the ability to pause the simulation when it&#8217;s running automatically.<\/p>\n<p><iframe loading=\"lazy\" width=\"418px\" height=\"800px\" src=\"https:\/\/www.rpchurchill.com\/demo\/des\/discrete-event-sim_20161114.html\" frameborder=\"0\" allowfullscreen><\/iframe><\/p>\n<p>Up to now I&#8217;ve assumed that components can only be linked together in one line; each component was linked to exactly one subsequent and\/or one previous component.  I started to allow for more arbitrary constructions when I developed separate nodes that could be linked to multiple incoming or outgoing paths.  We aren&#8217;t using independent nodes here so we have to recreate that capability using the components we do have.  We can start by allowing the Arrivals component to be linked to multiple Entry components.  You can see this in the demo; there is one Arrivals component and two Entry components.  Each Entry component is linked to a separate Path component and each Path component is then linked to the single Exit component.  It&#8217;s a pretty simple setup, but it includes everything needed to illustrate the principle.<\/p>\n<p>The code for the Arrivals component includes a few changes.  We&#8217;ve eliminated the single value for <code>nextComponent<\/code> and replaced it with an array of values called <code>entryList<\/code>.  This allows the Arrivals component to be linked to as many Entry components as desired.<\/p>\n<p>We&#8217;ve also added an additional input parameter to the constructor or initial call, in the form of an <code>entryDistributionArray<\/code>.  This array should have as many primary members as there are time slices in the model (we&#8217;ve been going with twelve time slices of thirty time units each).  Each member of the array should itself be an array with as many members as there are linked Entry components.  This information is used in the <code>forwardEntity<\/code> method in the following way.  The values in the secondary dimension of the array represent the cumulative chance that any given entity arrives at that Entry component, ranging from 0.0 to 1.0 and always increasing.  We initialize each array to have values of 0.7 and 1.0, which means that 70 percent of the entities will be forwarded to the first Entry component while the other 30% will be forwarded to the second Entry component.  If we had assigned five possible Entry components we might specify a secondary array of [0.4,0.45,0.6,0.9,1.0], which means that Entry components one through five would be expected to receive 40, 5, 15, 30, and 10 percent of the generated entries, respectively.  The actual selection is based on the result of a call to <code>Math.random()<\/code>, which generates a value between 0.0 (inclusive) and 1.0 (not inclusive).<\/p>\n<pre class=\"toolbar-overlay:false wrap:false height-set:true lang:default decode:true \">\r\n    \/\/Arrivals component: generates entities that will be processed by the model\r\n    function ArrivalsComponent(scheduleBlockMinutes,scheduleArray,entryDistributionArray) {\r\n      setOfComponents.push(this);\r\n      this.componentID = getNewID();\r\n      \/\/this.componentName = \"Arrivals\";\r\n      this.componentName = \"\";\r\n      this.startBlockTime = globalSimClock;\r\n      this.currentBlockTime = globalSimClock;\r\n      this.incrementTime = scheduleBlockMinutes;\r\n      this.endBlockTime = globalSimClock + this.incrementTime;\r\n      this.entitiesScheduled = 0;\r\n      this.entitiesRemaining = 0;\r\n      this.currentEntityID = \"\";\r\n      this.activity = \"creation\";\r\n      this.scheduleIndex = -1;\r\n      this.endTime = endSimTime;\r\n      this.entryList = [];\r\n      this.entryCount = 0;\r\n      \/\/this.nextState = \"increment\";\r\n      feq.newItem(globalSimClock,this,\"increment\");  \/\/assume all components created at time zero\r\n        \r\n      \/\/this.dataGroup = new DisplayGroup(this.componentName,10,10,80,\"#00FFFF\",\"#FF0000\",\"#FFFF00\");\r\n      this.dataGroup = new DisplayGroup1();\r\n      this.defineDataGroup = function(x,y,vw,bc,vc,lc) {\r\n        this.dataGroup.define(this.componentName,x,y,vw,bc,vc,lc);\r\n      };\r\n      this.dataGroup.addValue(this.componentID,\"Comp. ID\",\"integer\");\r\n      this.dataGroup.addValue(this.startBlockTime,\"Start Time\",\"numdec\",5);\r\n      this.dataGroup.addValue(this.currentBlockTime,\"CurrentTime\",\"numdec\",5);\r\n      this.dataGroup.addValue(this.endBlockTime,\"End Time\",\"numdec\",5);\r\n      this.dataGroup.addValue(this.entitiesScheduled,\"# Entities\",\"integer\");\r\n      this.dataGroup.addValue(this.entitiesRemaining,\"# Remaining\",\"integer\");\r\n      this.dataGroup.addValue(this.currentEntityID,\"Entity ID\",\"integer\");\r\n      this.dataGroup.addValue(this.activity,\"Activity\",\"text\");\r\n      \r\n      this.assignDisplayValues = function() {\r\n        this.dataGroup.valueList[0].value = this.componentID;\r\n        this.dataGroup.valueList[1].value = this.startBlockTime;\r\n        this.dataGroup.valueList[2].value = this.currentBlockTime;\r\n        this.dataGroup.valueList[3].value = this.endBlockTime;\r\n        this.dataGroup.valueList[4].value = this.entitiesScheduled;\r\n        this.dataGroup.valueList[5].value = this.entitiesRemaining;\r\n        this.dataGroup.valueList[6].value = this.currentEntityID;\r\n        this.dataGroup.valueList[7].value = this.activity;\r\n      };\r\n      this.drawData = function() {\r\n        this.assignDisplayValues();\r\n        this.dataGroup.drawBasic();\r\n      };\r\n      this.addToEntryList = function(entry) {\r\n        this.entryList.push(entry);\r\n        this.entryCount++;\r\n      };\r\n      this.forwardEntity = function(entity) {\r\n        \/\/add test to ensure nextComponent is actually assigned\r\n        var dest = 0;\r\n        var test = Math.random();\r\n        while (test > entryDistributionArray[this.scheduleIndex][dest]) {\r\n          dest++;\r\n        }\r\n        if (dest > this.entryCount) {\r\n          alert(\"Arrival comp. tried to assign to entry with too high of an index\");\r\n        }\r\n        displayProgressText(\"Arrivals comp. \"+this.componentID+\" test: \"+test.toFixed(4)+\" vs. \"+entryDistributionArray[this.scheduleIndex][dest]);\r\n        this.entryList[dest].receiveEntity(entity);\r\n      };\r\n      this.makeEntity = function() {\r\n        \/\/var e = generateNewEntity(this);\r\n        var e = generateNewEntity();\r\n        this.currentBlockTime = globalSimClock;\r\n        this.entitiesRemaining--;\r\n        this.currentEntityID = e.entityID;  \/\/should create function to get this\r\n        this.activity = \"make entity\";\r\n        displayProgressText(\"Arrivals comp. \"+this.componentID+\" generates entity: \"+this.currentEntityID+\" at time \"+globalSimClock.toFixed(6));\r\n        \/\/do something to send it to the entry component\r\n        this.forwardEntity(e);\r\n        \/\/more to generate\r\n        if (this.entitiesRemaining > 0) {\r\n          var nextIndex = this.entitiesScheduled-this.entitiesRemaining;\r\n          var nextTime = this.arrivalArray[nextIndex];\r\n          var thisIndex = nextIndex-1;\r\n          var thisTime = this.arrivalArray[thisIndex];\r\n          advance(nextTime-thisTime,\"makeEntity\");\r\n          \/\/advance(arrivalArray[this.entitiesScheduled-this.entitiesRemaining]-arrivalArray[this.entitiesScheduled-this.entitiesRemaining-1],\"makeEntity\");\r\n        }\r\n      };  \/\/this.makeEntity\r\n      this.blockIncrement = function() {  \/\/should be called at the beginning of every time block\r\n        \/\/get arrival count per array index\r\n        this.scheduleIndex++;  \/\/starts from -1, updates at beginning of each window so has same value through duration of window\r\n        this.entitiesScheduled = scheduleArray[this.scheduleIndex];\r\n        if (this.entitiesScheduled > 0) {\r\n          \/\/var arrivalArray = [];\r\n          this.arrivalArray = [];\r\n          for (var i=0; i<this.entitiesScheduled; i++) {\r\n            this.arrivalArray[i] = Math.random() * scheduleBlockMinutes;\r\n          } \r\n          \/\/sort the array\r\n          this.arrivalArray.sort(compareNumeric);\r\n          \/\/generate the arrivals          \r\n          \/\/for (var i=0; i<this.entitiesScheduled; i++) {\r\n          \/\/  this.generateNewEntity(this,globalSimClock + this.arrivalArray[i]);\r\n          \/\/}\r\n          feq.newItem(globalSimClock+this.arrivalArray[0],this,\"makeEntity\");  \/\/assume all components created at time zero\r\n        }\r\n        displayProgressText(\"Arrivals comp. \"+this.componentID+\" generates \"+this.entitiesScheduled+\" new entities at time \"+globalSimClock.toFixed(6));\r\n        \/\/set values for display-------------\r\n        this.startBlockTime = globalSimClock;\r\n        this.currentBlockTime = globalSimClock;\r\n        this.endBlockTime = globalSimClock + this.incrementTime;\r\n        this.entitiesRemaining = this.entitiesScheduled;\r\n        this.currentEntityID = \"\";\r\n        this.activity = \"generate\";\r\n        \/\/----------------------------------\r\n        if (globalSimClock + this.incrementTime >= this.endTime) {\r\n          \/\/this.nextState = \"destroy\";\r\n          advance(this.incrementTime,\"destroy\");\r\n        } else {\r\n          advance(this.incrementTime,\"increment\");\r\n        }\r\n      };  \/\/this.blockIncrement\r\n      this.destroy = function() {\r\n        displayProgressText(\"Arrivals comp. \"+this.componentID+\" terminates at time \"+globalSimClock.toFixed(6));\r\n        \/\/do something to take this element out of the global element list - setOfComponents\r\n      };  \/\/this.destroy\r\n      this.activate = function(nextState) {\r\n        if (nextState == \"makeEntity\") {\r\n          this.makeEntity();\r\n        } else if (nextState == \"increment\") {\r\n          this.blockIncrement();\r\n        } else if (nextState == \"destroy\") {\r\n          this.destroy();\r\n        } else {\r\n          errorUndefinedAdvanceState(this.entityID,nextState);\r\n        }\r\n        \/\/clearCanvas(\"#000000\");\r\n        \/\/this.drawData();\r\n      }\r\n    }  \/\/ArrivalsComponent\r\n<\/pre>\n<p>That&#8217;s really all there is to it.  At this point none of the other components had to be changed.  That said, I did add a counter to each Entry component to keep track of the number of arriving entities routed to each.  Since I assigned values of 0.7 and 1.0 for each of the 12 time slices we would expect roughly two-thirds of the entities to arrive through the first (left hand) entry components.  Since the results are generated randomly and the browser (I tested this only on Firefox and Safari) doesn&#8217;t reset the random number seeds when I refresh the page to rerun the model, the exact distribution varies a little bit between runs.<\/p>\n<p>Here&#8217;s the code that initializes everything, including the division of arrivals by Entry component.<\/p>\n<pre class=\"toolbar-overlay:false wrap:false height-set:true lang:default decode:true \">\r\n    \/\/schedule for six hours of arrivals in half-hour blocks\r\n    var arrivalSchedule = [0,0,1,3,4,4,5,6,3,2,1,0]; \r\n    var entryDistribution = [[0.7,1.0],[0.7,1.0],[0.7,1.0],[0.7,1.0],[0.7,1.0],[0.7,1.0],[0.7,1.0],[0.7,1.0],[0.7,1.0],[0.7,1.0],[0.7,1.0],[0.7,1.0]];\r\n    \r\n    var arrival1 = new ArrivalsComponent(30.0,arrivalSchedule,entryDistribution);\r\n    arrival1.defineDataGroup(90,10,80,\"#00FFFF\",\"#FF0000\",\"#FFFF00\");\r\n\r\n    var entry1 = new EntryComponent(2.0);\r\n    entry1.defineDataGroup(10,115,80,\"#00FFFF\",\"#FF0000\",\"#FFFF00\");\r\n    \r\n    var entry2 = new EntryComponent(2.0);\r\n    entry2.defineDataGroup(175,115,80,\"#00FFFF\",\"#FF0000\",\"#FFFF00\");\r\n\r\n    arrival1.addToEntryList(entry1);\r\n    arrival1.addToEntryList(entry2);\r\n    \r\n    \r\n    var path1 = new PathComponent();\r\n    path1.setStartPoint(90,190);\r\n    path1.setEndPoint(168,250);\r\n    path1.setSpeedTime(7.5,1.0);\r\n    path1.calcPathLength();\r\n\r\n    entry1.assignNextComponent(path1);\r\n    \r\n    var path2 = new PathComponent();\r\n    path2.setStartPoint(245,190);\r\n    path2.setEndPoint(168,250);\r\n    path2.setSpeedTime(7.5,1.0);\r\n    path2.calcPathLength();\r\n\r\n    entry2.assignNextComponent(path2);\r\n    \r\n    var exit1 = new ExitComponent(2.0);\r\n    exit1.defineDataGroup(90,250,80,\"#00FFFF\",\"#FF0000\",\"#FFFF00\");\r\n    \r\n    path1.assignNextComponent(exit1);\r\n    path2.assignNextComponent(exit1);\r\n<\/pre>\n<p>We&#8217;ve assigned the diversion percentages for the arrivals (from the Arrivals component to the various Entry components) by time slice but there may not be a need to add that much complexity.  If the division by entry point doesn&#8217;t change over time then a single set of values could be used just as easily.  I&#8217;ve worked with some very complex models that made this assumption.<\/p>\n<p>Tomorrow I&#8217;ll continue adding complexity by adding the ability to specify multiple destinations to at least one other component type.<\/p>\n<p>Finally, I will also see if I can&#8217;t allow the user to reinitialize and rerun the simulation without having to refresh the whole browser page.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Today I took a step back to slightly simpler version of the simulation that uses the original version of the Path components (the ones that have the nodes built into them and act like standalone components). I took two steps &hellip; <a href=\"https:\/\/rpchurchill.com\/wordpress\/posts\/2016\/11\/14\/a-simple-discrete-event-simulation-part-41\/\">Continue reading <span class=\"meta-nav\">&rarr;<\/span><\/a><\/p>\n","protected":false},"author":2,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[60],"tags":[121,49],"_links":{"self":[{"href":"https:\/\/rpchurchill.com\/wordpress\/wp-json\/wp\/v2\/posts\/1129"}],"collection":[{"href":"https:\/\/rpchurchill.com\/wordpress\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/rpchurchill.com\/wordpress\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/rpchurchill.com\/wordpress\/wp-json\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/rpchurchill.com\/wordpress\/wp-json\/wp\/v2\/comments?post=1129"}],"version-history":[{"count":18,"href":"https:\/\/rpchurchill.com\/wordpress\/wp-json\/wp\/v2\/posts\/1129\/revisions"}],"predecessor-version":[{"id":1424,"href":"https:\/\/rpchurchill.com\/wordpress\/wp-json\/wp\/v2\/posts\/1129\/revisions\/1424"}],"wp:attachment":[{"href":"https:\/\/rpchurchill.com\/wordpress\/wp-json\/wp\/v2\/media?parent=1129"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/rpchurchill.com\/wordpress\/wp-json\/wp\/v2\/categories?post=1129"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/rpchurchill.com\/wordpress\/wp-json\/wp\/v2\/tags?post=1129"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}