{"id":1336,"date":"2017-01-19T16:27:50","date_gmt":"2017-01-19T21:27:50","guid":{"rendered":"http:\/\/rpchurchill.com\/?p=1336"},"modified":"2017-02-03T12:51:38","modified_gmt":"2017-02-03T17:51:38","slug":"a-simple-discrete-event-simulation-part-74","status":"publish","type":"post","link":"https:\/\/rpchurchill.com\/wordpress\/posts\/2017\/01\/19\/a-simple-discrete-event-simulation-part-74\/","title":{"rendered":"A Simple Discrete-Event Simulation: Part 74"},"content":{"rendered":"<p><iframe loading=\"lazy\" width=\"418px\" height=\"1050px\" src=\"https:\/\/www.rpchurchill.com\/demo\/des\/discrete-event-sim_20170119.html\" frameborder=\"0\" allowfullscreen><\/iframe><\/p>\n<p>Following yesterday&#8217;s work I had three tasks to complete.  The first task was to fix the reporting for cases where a Process or Bag component is not the last item in a component group.  The fix was to only run the reporting process for exiting a group when the entity leaving a Process or Bag component is going to a different non-Path component that&#8217;s in a different group.  This involved embedding the report function at the end of the <code>forwardEntity<\/code> function in the <code>if<\/code> statement shown below.<\/p>\n<pre class=\"toolbar-overlay:false wrap:false height-set:true lang:default decode:true \">\r\n              \/\/record stats\r\n              if (this.nextComponentList[dest].getComponentGroup() != entity.getComponentGroup()) {  \/\/truly leaving a component group along the current connection (specified by dest)\r\n                recordGroupStatsWrapper(this.componentGroup, entity.getComponentGroupEntryTime(), entity);\r\n              }\r\n<\/pre>\n<p>The <code>getComponentGroup<\/code> function in Path components had to be modified so it actually returns the component group of <em>its<\/em> next component, so it always returns the component group of the next non-Path component.<\/p>\n<p>The second task was to experiment with different ways of specifying the process time for certain components in a modular way.  I originally passed in an array of values and called a fixed, external function to determine the array index to use base on the relevant entity&#8217;s user-defined properties.  Later I added an additional index to the process so different Process or Bag components could generate lookup indices based on different properties or combinations of properties.  I then realized that I could simply pass a custom function in for each case.  The code for one way of doing this is here:<\/p>\n<pre class=\"toolbar-overlay:false wrap:false height-set:true lang:default decode:true \">\r\n    function processTimeBag1(entity) {\r\n      var processTime;\r\n      if (entity.getPropertyValue(\"Residency\") == \"Citizen\") {\r\n        processTime = 30;\r\n      } else if (entity.getPropertyValue(\"Residency\") == \"LPR\") {\r\n        processTime = 50;\r\n      } else if (entity.getPropertyValue(\"Residency\") == \"Visitor\") {\r\n        processTime = 80;\r\n      }\r\n      return processTime;\r\n    }\r\n    var routingTableB1 = [[1.0],[1.0],[1.0]];\r\n    var bag1 = new BagComponent(processTimeBag1, 12, routingTableB1);\r\n      ...\r\n        var pTime = processTime(entity);  \/\/processTime is the internal name for the processTimeBag1 parameter\r\n        advance(pTime, this, \"processComplete\",entity);\r\n<\/pre>\n<p>If we want to retain some additional form of external modularity of parameters we could do something like this:<\/p>\n<pre class=\"toolbar-overlay:false wrap:false height-set:true lang:default decode:true \">\r\n    var globalProcessTimeSettings = [[5,7,9],[30,50,80]];\r\n    function processTimeBag1(entity) {\r\n      var processTime;\r\n      var globalIndex = 1;\r\n      if (entity.getPropertyValue(\"Residency\") == \"Citizen\") {\r\n        processTime = globalProcessTimeSettings[globalIndex][0];\r\n      } else if (entity.getPropertyValue(\"Residency\") == \"LPR\") {\r\n        processTime = globalProcessTimeSettings[globalIndex][1];\r\n      } else if (entity.getPropertyValue(\"Residency\") == \"Visitor\") {\r\n        processTime = globalProcessTimeSettings[globalIndex][2];\r\n      }\r\n      return processTime;\r\n    }\r\n    var routingTableB1 = [[1.0],[1.0],[1.0]];\r\n    var bag1 = new BagComponent(processTimeBag1, 12, routingTableB1);\r\n      ...\r\n        var pTime = processTime(entity);  \/\/processTime is the internal name for the processTimeBag1 parameter\r\n        advance(pTime, this, \"processComplete\",entity);\r\n<\/pre>\n<p>The possibilities are many.<\/p>\n<p>The third and final task was to allow the simulation to run until it is empty if a specific flag is set.  I defined a global variable called <code>runToEmptyFlag<\/code> and modified two tests that control when to stop the simulation and when to stop recording data.  The logic says to run to at least the minimum time and then stop if <code>runToEmptyFlag<\/code> is false, or keep going until there are no entities left in the system if <code>runToEmptyFlag<\/code> is true.  Today&#8217;s version of the model has the flag set to true, so it will run beyond 630 time units as long as there are entities still in the system.  If the first run doesn&#8217;t run past that time because it clears out then reset and rerun it until you see the desired behavior.  The runtimes can vary quite a bit depending on how the type and routing dice fall.<\/p>\n<pre class=\"toolbar-overlay:false wrap:false height-set:true lang:default decode:true \">\r\n      this.increment = function() {  \/\/function in the DisplayControlComponent object\r\n        \/\/update the displays \r\n        displayControlUpdate = true;\r\n        \r\n        \/\/this.displayEnd is set by the global value for when to stop running, globalEndTime\r\n        if ((globalSimClock >= this.displayEnd) && (!runToEmptyFlag || (setOfEntities.length == 0))) {\r\n          keepRunning = false;  \/\/tells the main event loop to stop pulling events, if any are avialable\r\n        } else {\r\n          \/\/calculate next update time\r\n          var nextTime = this.displayIncrement * this.displayMultiplier;\r\n          while ((this.displayClock + nextTime) < globalSimClock) {\r\n            nextTime += this.displayIncrement * this.displayMultiplier;\r\n          }\r\n          this.displayClock += nextTime;\r\n          advance(nextTime,this,\"increment\");  \/\/currently set to run in 30-unit time intervals\r\n        }\r\n      };\r\n<\/pre>\n<pre class=\"toolbar-overlay:false wrap:false height-set:true lang:default decode:true \">\r\n      this.increment = function() {  \/\/function in the StatsTimerComponent object\r\n        incrementStatsTimeIndexWrapper();\r\n        \/\/this.timeEnd  is set by the global value for when to stop running, globalEndTime\r\n        if ((globalSimClock < this.timeEnd) || \r\n            (!runToEmptyFlag) || \r\n            ((globalSimClock >= this.timeEnd) && runToEmptyFlag && (setOfEntities.length > 0))\r\n           ) {\r\n           advance(statsInterval, this, \"increment\");  \/\/currently set to run in 30-unit time intervals\r\n        }\r\n      };\r\n<\/pre>\n<p>I noticed that the final time increment would not be reported properly if the systems runs to empty beyond its allotted time.  The fix was to add in an extra reporting increment if the global clock is greater than the number of reporting cycles run times the number of time units per cycle.<\/p>\n<pre class=\"toolbar-overlay:false wrap:false height-set:true lang:default decode:true \">\r\n    function displayEndMessage() {\r\n      globalCTX.font = \"12px Arial\";\r\n      globalCTX.fillStyle = \"#FF00FF\";\r\n      globalCTX.textAlign = \"left\";\r\n      globalCTX.fillText(\"Simulation has ended\", 10.0, canvas.height - 2);\r\n      if (globalSimClock > (statsTimeIndex * statsUpdateInterval)) {  \/\/over allotted time test and increment added here\r\n        incrementStatsTimeIndexWrapper();\r\n      }\r\n      reportStatsWrapper();\r\n    }  \/\/displayEndMessage\r\n<\/pre>\n","protected":false},"excerpt":{"rendered":"<p>Following yesterday&#8217;s work I had three tasks to complete. The first task was to fix the reporting for cases where a Process or Bag component is not the last item in a component group. The fix was to only run &hellip; <a href=\"https:\/\/rpchurchill.com\/wordpress\/posts\/2017\/01\/19\/a-simple-discrete-event-simulation-part-74\/\">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],"_links":{"self":[{"href":"https:\/\/rpchurchill.com\/wordpress\/wp-json\/wp\/v2\/posts\/1336"}],"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=1336"}],"version-history":[{"count":7,"href":"https:\/\/rpchurchill.com\/wordpress\/wp-json\/wp\/v2\/posts\/1336\/revisions"}],"predecessor-version":[{"id":1394,"href":"https:\/\/rpchurchill.com\/wordpress\/wp-json\/wp\/v2\/posts\/1336\/revisions\/1394"}],"wp:attachment":[{"href":"https:\/\/rpchurchill.com\/wordpress\/wp-json\/wp\/v2\/media?parent=1336"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/rpchurchill.com\/wordpress\/wp-json\/wp\/v2\/categories?post=1336"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/rpchurchill.com\/wordpress\/wp-json\/wp\/v2\/tags?post=1336"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}