Friday, January 18, 2008

Resource Utilization and AnyLogic and You

A weird thing just happened to me when calculating resource utilization. Thought it was worth sharing. Our AnyLogic gurus probably already know this, but I didn't....

AnyLogic 6 will automatically collect statistics for you on your resources. Each ResourcePool object has an "enableStats" checkbox. Checking this will create an internal statsUtilization object, which is of type StatisticsContinuous.



The continuous statistics are used for things like queue utilization or resource utilization, where the value persists in continuous time but only changes at discrete time moments (like an entity entering or exiting the queue, or a resource turning on or off). This works like a "DSTAT" in Arena.

You can then call myResource.statsUtilization.mean() to return the average utilization over time.

But! You'd better double-check to make sure that you get the value you expect!

Let's do an example.

For instance, let's say that the total run length is 100 minutes. The resource turns on at time 30, and turns off at time 50, for a total of 20 minutes of use. The expected utilization is 20 / 100 = 20% busy. This is illustrated in the diagram below.


|--- total run length ------------|

|-------[ busy ]------------------|



But lo and behold, if you run this exact setup, you will see that statsUtilization.mean() = 40%, twice as high as you expected! Why is that?

It turns out that the statsUtilization is only updated when the statistics change. That's shown by the width of the |---stats---| below. The statistics were collected over a shorter period of time than I expected. In our example, the last time the statistics changed was at time 50, when the resource was released. So the utilization reports 20 / 50 = 40% busy.


|--- total run length ------------|

|-------[ busy ]------------------|

|--- stats ----|



Now, it's possible that AnyLogic has some internal logic that will correctly calculate these statistics at the end of the run, by updating the denominator to reflect the total run time. However, I was calling a function writeStatistics() within my fModelComplete() function that writes out statsUtilization.mean() to the summary report. At the time I called the method, the statsUtilization had not updated the denominator.

So until I figure out where's the proper place to call my writeStatistics() function, I'm resorting to collecting the utilization the old-fashioned way: total busy time / total run time.

Point of all this: Double check your outputs!