Learning Objectives

  • To be able to model non-stationary arrivals using arrival schedules.

  • To be able to model the staffing/scheduling of resources using resource capacity schedules.

  • To be able to capture statistics over specific periods of time.

What Does Non-Stationary Mean?

  • If a process does not depend on time, it is said to be stationary. When a process depends on time, it is said to be non-stationary.

  • Examples

    • Arrivals to food court
    • Staffing schedules
    • Seasonal demand

Non-Stationary Arrivals

Interval Mon Tue Wed Thurs Fri Sat Sun
12 am – 6 am 3 2 1 2 4 2 1
6 am - 9 am 6 4 6 7 8 7 4
9 am - 12 pm 10 6 4 8 10 9 5
12 pm - 2 pm 24 25 22 19 26 27 20
2 pm - 5 pm 10 16 14 16 13 16 15
5 pm - 8 pm 30 36 26 35 33 32 18
8 pm - 12 am 14 12 8 13 12 15 10

Non-Stationary Poisson Process (NSPP)

  • Time-varying behavior of the mean arrival rates over the course of a day.
Interval Avg. Length (hrs) Rate per hour
12 am – 6 am 2.14 6 0.36
6 am - 9 am 6.0 3 2.0
9 am - 12 pm 7.43 3 2.48
12 pm - 2 pm 23.29 2 11.645
2 pm - 5 pm 14.29 3 4.76
5 pm - 8 pm 30.0 3 10
8 pm - 12 am 12.0 4 3.0
  • Assume that the distribution for the number of arrivals in an interval is Poisson with time varying mean arrival rates.

Methods for Generating a NSPP

  • Thinning - generates arrival at some overall maximum rate and probabilistically rejects arrivals based on time-varying proportion for each interval.
  • Rate Inversion - Uses the theory of NHPP to invert the mean rate function. Then, generates a rate 1 Poisson process which is then transformed by the inverted mean rate function as time proceeds.
    • KSL provides for piecewise constant and piecewise linear rate functions.
    • Provides a the NHPPTimeBtwEventRV and the NHPPEventGenerator classes.

Modeling Resources with Changing Capacity

There are two key resource variables for understanding resources and their states.

  • \(c(t)\): Resource capacity, \(c(t)\), returns the number of capacity units currently defined for the specified resource. This value can be changed by the user via a capacity schedule or through the use of capacity change notices.

  • \(b(t)\): Number of busy resource units at any time \(t\). Each time an entity seizes a resource, \(b(t)\) changes accordingly.

  • \(a(t) = c(t) - b(t)\): Number of available resource units at any time \(t\). Each time an entity seizes a resource or a capacity change occurs, \(a(t)\) changes accordingly.

These variables may change over time for a resource. The changing of these variables defines the possible states of a resource.

Resource States

Idle

A resource is in the idle state when all units are idle. That is, a resource is idle if there are no busy units, i.e. \(b(t) = 0\).

Busy

A resource is in the busy state when it has one or more busy (seized) units. That is, when \(b(t) > 0\).

Inactive

A resource is in the inactive state when it has zero capacity and it is not busy. That is, when \(b(t) = 0\) and \(c(t) = 0\). Because of how capacity changes can be invoked, it is possible for the capacity of the resource to be \(c(t) = 0\) while resources are still busy. This will be discussed further in what follows.

Capacity Schedules

  • A capacity schedule governs how capacity changes. The CapacitySchedule class specifies the amount of capacity for various periods of time.
  • For example, in the following code, we specify a resource with a default initial capacity of 2 units, which is overridden by specifying the use of a capacity schedule.
    private val resource: ResourceWithQ = ResourceWithQ(this, name = "Resource", capacity = 1)
    private val schedule: CapacitySchedule

    init {
        schedule= CapacitySchedule(this, 0.0)
        schedule.addItem(capacity = 0, duration = 15.0)
        schedule.addItem(capacity = 1, duration = 35.0)
        schedule.addItem(capacity = 0, duration = 20.0)
        schedule.addItem(capacity = 2, duration = 30.0)
        resource.useSchedule(schedule, changeRule = CapacityChangeRule.IGNORE)
    }
  • We need rules to govern what happens when the capacity changes and the resource is busy.

KSL Capacity Change Rules

Ignore

starts the time duration of the schedule change immediately, but allows the busy resource to finish processing the current entity before causing the capacity change.

Wait

waits until the busy resource has finished processing the current entity before changing the resource capacity and starting the time duration of the schedule change.

  • It is important to note that the capacity change rule is invoked only when the resource is busy and there is a requested decrease in capacity.

Ignore Case Example 1

Let’s suppose that your simulation professor has office hours throughout the day, except from 12-12:30 pm for lunch. What happens for each rule if you arrive at 11:55 am with a question?

  • You arrive at 11:55 am with a 10 minute question and begin service.

  • At Noon, the professor gets up and hangs a Lunch in Progress sign.

  • Your professor continues to answer your question for the remaining service time of 5 minutes.

  • You leave at 12:05 pm.

  • Whether or not there are any students waiting in the hallway, the professor still starts lunch.

  • At 12:30 pm the professor finishes lunch and takes down the lunch in progress sign. If there were any students waiting in the hallway, they can begin service.

The net effect of case 1 is that the professor lost 5 minutes of lunch time. During the 30 minute scheduled break, the professor was busy for 5 minutes and inactive for 25 minutes.

Ignore Case Example 2

  • You arrive at 11:55 am with a 45 minute question and begin service.

  • At Noon, the professor gets up and hangs a Lunch in Progress sign.

  • Your professor continues to answer your question for the remaining service time of 40 minutes.

  • At 12:30 pm the professor gets up and takes down the lunch in progress sign and continues to answer your question.

  • You leave at 12:40 pm

The net effect of case 2 is that the professor did not get to eat lunch that day. During the 30 minute scheduled break, the professor was busy for 30 minutes.

Wait Case Example 1

  • You arrive at 11:55 am with a 10 minute question and begin service.

  • At Noon, the professor’s lunch reminder rings on his or her computer. The professor recognizes the reminder but doesn’t act on it, yet.

  • Your professor continues to answer your question for the remaining service time of 5 minutes.

  • You leave at 12:05 pm. The professor recalls the lunch reminder and hangs a Lunch in Progress sign. Whether or not there are any students waiting in the hallway, the professor still hangs the sign and starts a 30 minute lunch.

  • At 12:35 pm the professor finishes lunch and takes down the lunch in progress sign. If there were any students waiting in the hallway, they can begin service.

Wait Case Example 2

  • You arrive at 11:55 am with a 45 minute question and begin service.

  • At Noon, the professor’s lunch reminder rings on his or her computer. The professor recognizes the reminder but doesn’t act on it, yet.

  • Your professor continues to answer your question for the remaining service time of 40 minutes.

  • You leave at 12:40 pm. The professor recalls the lunch reminder and hangs a Lunch in Progress sign. Whether or not there are any students waiting in the hallway, the professor still hangs the sign and starts a 30 minute lunch.

  • At 1:10 pm the professor finishes lunch and takes down the lunch in progress sign. If there were any students waiting in the hallway, they can begin service.

Capacity Change Effects on Utilization

The time average number of busy resources is:

\[\overline{B} = \frac{1}{T}\int\limits_0^T \mathit{b}(t) \mathrm{d}t\]

The time average capacity of the resource is:

\[\overline{C} = \frac{1}{T}\int\limits_0^T \mathit{c}(t) \mathrm{d}t\] Instantaneous utilization at time \(t\) is:

\[IU(t) = \begin{cases} 0 & c(t) = 0\\ 1 & b(t) \geq c(t)\\ b(t)/c(t) & \text{otherwise} \end{cases}\]

Time Average Instantaneous Utilization and Schedule Utilization

Thus, the time average instantaneous utilization is:

\[\overline{\mathit{IU}} = \frac{1}{T}\int\limits_0^T \mathit{IU}(t)\mathrm{d}t\] The scheduled utilization is the time average number of busy resources divided by the time average number scheduled. This is the same as the total time spent busy divided by the total time available for all resource units.

\[ \overline{\mathit{SU}} = \frac{\overline{B}}{\overline{C}} = \frac{\frac{1}{T}\int\limits_0^T b(t)dt}{\frac{1}{T}\int\limits_0^T c(t)dt} = \frac{\int\limits_0^T b(t)dt}{\int\limits_0^T c(t)dt} \]

If \(c(t)\) is constant, then \(\overline{\mathit{IU}} = \overline{\mathit{SU}}\). Caution should be used in interpreting \(\overline{\mathit{IU}}\) when \(c(t)\) varies with time.

Scheduled Utilization Example

  • Let’s also assume for simplicity that the professor had a take home exam due the next day and was therefore busy all day long.

  • What would be the average instantaneous utilization and the scheduled utilization of the professor?

Time interval Busy time Scheduled time \(b(t)\) \(c(t)\) \(\mathit{IU}(t)\)
8 am - noon 240 240 1.0 1.0 1.0
12 - 12:30 pm 30 0 1.0 0 1.0
12:30 - 4 pm 210 210 1.0 1.0 1.0
480 450

Results

We can compute \(\overline{B}\) and \(\overline{C}\) as:

\[\begin{aligned} \overline{B} & = \frac{1}{480}\int\limits_{0}^{480} 1.0 \mathrm{d}t = \frac{480}{480} = 1.0 \\ \overline{C} & = \frac{1}{480}\int\limits_{0}^{480} \mathit{c(t)} \mathrm{d}t = \frac{1}{480}\biggl\lbrace\int\limits_{0}^{240} 1.0 \mathrm{d}t + \int\limits_{270}^{480} 1.0 \mathrm{d}t \biggr\rbrace = 450/480 = 0.9375\\ \overline{\mathit{IU}} & = \frac{1}{480}\int\limits_{0}^{480} 1.0 \mathrm{d}t = \frac{480}{480} = 1.0\\ \overline{\mathit{SU}} & = \frac{\overline{\mathit{NR}}}{\overline{\mathit{MR}}} = \frac{1.0}{0.9375} = 1.0\bar{6}\end{aligned}\]

  • Scheduled utilization is higher than 100%

Enhanced STEM Fair Mixer

  • 15 minutes before closing an announcement is made about the end of the mixer.

  • The doors to the mixture are closed to new arrivals. In other words, a closed sign goes up so that no new students are admitted to the mixer.

  • Any students that were chatting within the conversation area finish up their conversations and then head for the exit, without visiting the recruiting stations.

  • Any student walking to the name tag area, the conversation area or to the recruiting area proceed to the exit.

  • Any students already at the recruiting stations, either in line waiting, or talking with the recruiter are allowed to finish up their visit and then depart the mixer.

Enhanced STEM Fair Mixer

  • Because the distances between the stations in the hall are now important an enhanced drawing of the system has been made that better illustrates the standard layout that has been used in the past for the mixer. The distances shown in the drawing are rough estimates.

  • New data suggest that 60% of the arriving students do not visit the conversation area, but instead go directly to the recruiting area to pick one of the two recruiting stations to visit, with equal probability.

  • The remaining 40% of students will first visit the conversation area. The time spent within the conversation area is a little less than previously noted to exclude the time spent walking. The conversation time is triangularly distribution with a minimum of 10 minutes, a most likely value of 15 minutes, and a maximum value of 30 minutes. After having their conversations, 90% of the students decide to visit one of the two recruiting stations. The other 10% are too tired or timid and decide to leave.

  • The speed of people walking can vary greatly, but prior data suggests that for short distances within and around buildings, people walk between 1 mile per hour and 3 miles per hour, with a most likely time of 2 miles per hour, triangularly distributed.

Enhance STEM Fair Mixer

Data was collected over 30 minute intervals during the 6 hours of the mixer and it showed that there was a larger arrival rate during the middle hours of the operating hours than during the beginning or ending hours of the data.

Period Time Frame Duration Mean Arrival Rate per Hour
1 2 - 2:30 pm 30 5
2 2:30 – 3 pm 30 10
3 3 - 3:30 pm 30 15
4 3:30 – 4 pm 30 25
5 4 - 4:30 pm 30 40
6 4:30 – 4 pm 30 50
7 5 - 5:30 pm 30 55
8 5:30 – 6 pm 30 60
9 6 - 6:30 pm 30 60
10 6:30 – 7 pm 30 30
11 7 - 7:30 pm 30 5
12 7:30 – 8 pm 30 5

Modeling Walking

  • To model the walking within the mixer, we need to translate the distance traveled into time.

  • If we know the distance to walk, we can determine the time via the following relationship, where \(v\) is the speed (velocity), \(d\) is the distance, and \(t\) is the time.

\[v = \frac{d}{t}\]

  • Thus, we can randomly generate the value of \(v\), and use the relationship to determine the time taken.

\[t = \frac{d}{v}\]

We Need Distances

We have the following distances between the major locations of the system:

  • Entrance to Name Tags, 20 feet

  • Name Tags to Conversation Area, 30 feet

  • Name Tags to Recruiting Area (either JHBunt or MalMart), 80 feet

  • Name Tags to Exit, 140 feet

  • Conversation Area to Recruiting Area (either JHBunt or MalMart), 50 feet

  • Conversation Area to Exit, 110 feet

  • Recruiting Area (either JHBunt or MalMart) to Exit, 60 feet

For simplicity, we assume that the distance between the JHBunt and MalWart recruiting locations within the recruiting area can be ignored.

Implementing the Enhanced STEM Fair Mixer Model

  • Use a process model that represents the system.
class StemFairMixerEnhanced(parent: ModelElement, name: String? = null) : ProcessModel(parent, name) {

    var lengthOfMixer = 360.0
        set(value) {
            require(value > 0.0) { "The length of the mixer must be > 0.0" }
            field = value
        }
    var warningTime = 15.0
        set(value) {
            require(value > 0.0) { "The warning limit must be > 0.0" }
            field = value
        }

    val doorClosingTime
        get() = lengthOfMixer - warningTime

    var isClosed: Boolean = false
        private set

Define the Random Elements

    private val myNameTagTimeRV = RandomVariable(this, UniformRV((15.0 / 60.0), (45.0 / 60.0), 2))
    private val myDecideToMix = RandomVariable(this, BernoulliRV(0.4, 3))
    private val myDecideToLeave = RandomVariable(this, BernoulliRV(0.1, 4))
    private val myInteractionTimeRV = RandomVariable(this, TriangularRV(10.0, 15.0, 30.0, 5))
    private val myDecideRecruiter = RandomVariable(this, BernoulliRV(0.5, 6))
    private val myTalkWithJHBunt = RandomVariable(this, ExponentialRV(6.0, 7))
    private val myTalkWithMalMart = RandomVariable(this, ExponentialRV(3.0, 8))
    private val myWalkingSpeedRV = TriangularRV(88.0, 176.0, 264.0, 9)

    private val walkToNameTags = RandomVariable(this, 20.0 / myWalkingSpeedRV)
    private val walkFromNameTagsToConversationArea = RandomVariable(this, 30.0 / myWalkingSpeedRV)
    private val walkFromNameTagsToRecruiting = RandomVariable(this, 80.0 / myWalkingSpeedRV)
    private val walkFromNameTagsToExit = RandomVariable(this, 140.0 / myWalkingSpeedRV)
    private val walkFromConversationAreaToRecruiting = RandomVariable(this, 50.0 / myWalkingSpeedRV)
    private val walkFromConversationAreaToExit = RandomVariable(this, 110.0 / myWalkingSpeedRV)
    private val walkFromRecruitingToExit = RandomVariable(this, 60.0 / myWalkingSpeedRV)

Model the Non-Stationary Arrivals

    private val rateFunction: PiecewiseConstantRateFunction

    fun adjustRates(factor: Double){
        require(factor > 0.0) {"the adjustment factor must be >= 0.0"}
        generator.adjustRates(factor)
    }

    init {
        // set up the generator
        val durations = doubleArrayOf(
            30.0, 30.0, 30.0, 30.0, 30.0, 30.0,
            30.0, 30.0, 30.0, 30.0, 30.0, 30.0
        )
        val hourlyRates = doubleArrayOf(
            5.0, 10.0, 15.0, 25.0, 40.0, 50.0,
            55.0, 55.0, 60.0, 30.0, 5.0, 5.0
        )
        val ratesPerMinute = hourlyRates.divideConstant(60.0)
        rateFunction = PiecewiseConstantRateFunction(durations, ratesPerMinute)
    }

    private val generator = NHPPPiecewiseRateFunctionEventGenerator(this, this::createStudents,
        rateFunction = rateFunction, streamNum = 1)

Process for Students Visiting the Conversation Area

  • Notice the checking for closing after the delays.
    private inner class Student : Entity() {
        val isMixer = myDecideToMix.value.toBoolean()
        val isLeaver = myDecideToLeave.value.toBoolean()

        val mixingStudentProcess = process {
            myNumInSystem.increment()
            delay(walkToNameTags)
            // at name tag station
            if (isClosed) {
                // mixer closed during walking
                delay(walkFromNameTagsToExit)
                departMixer(this@Student)
            } else {
                // get name tags
                delay(myNameTagTimeRV)
                if (isClosed) {
                    // mixer closed during name tag
                    delay(walkFromNameTagsToExit)
                    departMixer(this@Student)
                } else {
                    .
                    .

Process Continued

 // goto the conversation area
                    delay(walkFromNameTagsToConversationArea)
                    if (isClosed) {
                        // closed during walking, must leave
                        delay(walkFromConversationAreaToExit)
                        departMixer(this@Student)
                    } else {
                        // start the conversation
                        myNumInConversationArea.increment()
                        delay(myInteractionTimeRV)
                        myNumInConversationArea.decrement()
                        if (isClosed) {
                            // closed during conversation, must leave
                            delay(walkFromConversationAreaToExit)
                            departMixer(this@Student)
                        } else {
                            // decide to leave or go to recruiting
                            if (isLeaver) {
                                delay(walkFromConversationAreaToExit)
                                departMixer(this@Student)
                            } else {

Process Continued

                                delay(walkFromConversationAreaToRecruiting)
                                if (!isClosed) {
                                    // proceed with recruiting visit
                                    val firstRecruiter = decideRecruiter()
                                    if (firstRecruiter == myJHBuntRecruiters) {
                                        use(myJHBuntRecruiters, delayDuration = myTalkWithJHBunt)
                                        use(myMalWartRecruiters, delayDuration = myTalkWithMalMart)
                                    } else {
                                        use(myMalWartRecruiters, delayDuration = myTalkWithMalMart)
                                        use(myJHBuntRecruiters, delayDuration = myTalkWithJHBunt)
                                    }
                                }
                                // either closed or they visited recruiting
                                delay(walkFromRecruitingToExit)
                                departMixer(this@Student)
                            }
                        }
                    }
                }
            }
        }

Visiting The Recruiters

In this case, we use a function, decideRecruiter() that picks the first station to visit.

   private fun decideRecruiter(): ResourceWithQ {
        // check the equal case first to show no preference
        val j = myJHBuntRecruiters.waitingQ.size + myJHBuntRecruiters.numBusy
        val m = myMalWartRecruiters.waitingQ.size + myMalWartRecruiters.numBusy
        if (j == m ){
            if (myDecideRecruiter.value.toBoolean()) {
                return myJHBuntRecruiters
            } else {
                return myMalWartRecruiters
            }
        } else if (j < m) {
            return myJHBuntRecruiters
        } else  {
            // MalWart must be smaller
            return myMalWartRecruiters
        }
    }

Process for Visiting the Recruiters

        val recruitingOnlyStudentProcess = process {
            myNumInSystem.increment()
            delay(walkToNameTags)
            // at name tag station
            if (isClosed) {
                // mixer closed during walking
                delay(walkFromNameTagsToExit)
                departMixer(this@Student)
            } else {
                delay(myNameTagTimeRV)
                if (isClosed) {
                    // mixer closed during name tag
                    delay(walkFromNameTagsToExit)
                    departMixer(this@Student)
                } else {

Process for Visiting the Recruiters Continued

                    // proceed to recruiting
                    delay(walkFromNameTagsToRecruiting)
                    if (!isClosed) {
                        // proceed with recruiting visit
                        val firstRecruiter = decideRecruiter()
                        if (firstRecruiter == myJHBuntRecruiters) {
                            use(myJHBuntRecruiters, delayDuration = myTalkWithJHBunt)
                            use(myMalWartRecruiters, delayDuration = myTalkWithMalMart)
                        } else {
                            use(myMalWartRecruiters, delayDuration = myTalkWithMalMart)
                            use(myJHBuntRecruiters, delayDuration = myTalkWithJHBunt)
                        }
                    }
                    // either closed or they visited recruiting
                    delay(walkFromRecruitingToExit)
                    departMixer(this@Student)
                }
            }

Capturing Tally Based Non-Stationary Statistics

Let \(\left( x_{1},\ x_{2},x_{3},\cdots{,x}_{n(t)} \right)\) be a sequence of observations up to and including time \(t\).

Let \(n(t)\) be the number of observations up to and including time \(t\).

Let \(s\left( t \right)\) be the cumulative sum of the observations up to and including time \(t\). That is,

\[s\left( t \right) = \sum_{i = 1}^{n(t)}x_{i}\]

Let \(\overline{x}\left( t \right)\) be the cumulative average of the observations up to and including time \(t\). That is,

\[\overline{x}\left( t \right) = \frac{1}{n(t)}\sum_{i = 1}^{n(t)}x_{i} = \frac{s\left( t \right)}{n(t)}\]

Capturing Tally Based Non-Stationary Statistics

Note that \(s\left( t \right) = \overline{x}\left( t \right) \times n(t)\). Let \(t_{b}\) be the time at the beginning of a period (interval) of interest and let \(t_{e}\) be the time at the end of a period (interval) of interest such that \(t_{b} \leq t_{e}\). Define \(s(t_{b},t_{e}\rbrack\) as the sum of the observations during the interval, \((t_{b},t_{e}\rbrack\). Clearly, we have that,

\[s\left( t_{b},t_{e} \right\rbrack = s\left( t_{e} \right) - s\left( t_{b} \right)\]

Define \(n(t_{b},t_{e}\rbrack\) as the count of the observations during the interval, \((t_{b},t_{e}\rbrack\). Clearly, we have that,

\[n\left( t_{b},t_{e} \right\rbrack = n\left( t_{e} \right) - n\left( t_{b} \right)\]

Finally, we have that the average during the interval, \((t_{b},t_{e}\rbrack\) as

\[\overline{x}\left( t_{b},t_{e} \right\rbrack = \frac{s\left( t_{b},t_{e} \right\rbrack}{n\left( t_{b},t_{e} \right\rbrack}\]

Capturing Time Persistent Non-Stationary Statistics

Let \(y(t)\) represents the value of some state variable at any time \(t\). Here \(y(t)\) will take on constant values during intervals of time corresponding to when the state variable changes, for example. \(y(t)\) = {0, 1, 2, 3, …}. \(y(t)\) is a curve (a step function in this particular case) and we compute the time average over the interval \((t_{b},t_{e}\rbrack\).as follows.

\[\overline{y}\left( t_{b},t_{e} \right) = \frac{\int_{t_{b}}^{t_{e}}{y\left( t \right)\text{dt}}}{t_{e} - t_{b}}\]

Similar to the tally-based case, we can define the following notation. Let \(a\left( t \right)\) be the cumulative area under the state variable curve.

\[a\left( t \right) = \int_{0}^{t}{y\left( t \right)\text{dt}}\]

Capturing Time Persistent Non-Stationary Statistics

Define \(\overline{y}(t)\) as the cumulative average up to and including time \(t\), such that:

\[\overline{y}\left( t \right) = \frac{\int_{0}^{t}{y\left( t \right)\text{dt}}}{t} = \frac{a\left( t \right)}{t}\]

Thus, \(a\left( t \right) = t \times \overline{y}\left( t \right)\). So, if we have a function to compute \(\overline{y}\left( t \right)\) we have the ability to compute,

\[\overline{y}\left( t_{b},t_{e} \right) = \frac{\int_{t_{b}}^{t_{e}}{y\left( t \right)\text{dt}}}{t_{e} - t_{b}} = \frac{a\left( t_{e} \right) - a\left( t_{b} \right)}{t_{e} - t_{b}}\]

Use Events to Capture Observations

\[\overline{x}\left( t_{b},t_{e} \right\rbrack = \frac{s\left( t_{b},t_{e} \right\rbrack}{n\left( t_{b},t_{e} \right\rbrack}\]

\[\overline{y}\left( t_{b},t_{e} \right) = \frac{\int_{t_{b}}^{t_{e}}{y\left( t \right)\text{dt}}}{t_{e} - t_{b}} = \frac{a\left( t_{e} \right) - a\left( t_{b} \right)}{t_{e} - t_{b}}\]

Schedule events for \(t_{b}\) and \(t_{e}\) allows you to observe the required quantities at the beginning and end of the period and then observe the desired average.

KSL Constructs for Time-Varying Statistics

  • The ResponseInterval class represents an interval of time over which statistical collection should be performed. An interval is specified by providing an interval start time and a duration. The duration must be finite and greater than zero.

KSL Constructs for Time-Varying Statistics

  • Often we want to collect statistics across many intervals according to some timed pattern. The ResponseSchedule class facilitates the modeling of this situation.
  • The ResponseSchedule class allows the creation of a schedule that represents a list of intervals of time.
  • The user adds intervals and responses for which statistics need to be collected during the intervals.

Illustrating A Response Schedule

The following code illustrates the definition of a schedule to collect hourly responses for the STEM Fair Mixer situation.

    private val hourlyResponseSchedule = ResponseSchedule(this, 0.0, name = "Hourly")
    private val peakResponseInterval: ResponseInterval = ResponseInterval(this, 120.0, "PeakPeriod:[150.0,270.0]")

    init {
        hourlyResponseSchedule.scheduleRepeatFlag = false
        hourlyResponseSchedule.addIntervals(0.0, 6, 60.0)
        hourlyResponseSchedule.addResponseToAllIntervals(myJHBuntRecruiters.numBusyUnits)
        hourlyResponseSchedule.addResponseToAllIntervals(myMalWartRecruiters.numBusyUnits)
        hourlyResponseSchedule.addResponseToAllIntervals(myJHBuntRecruiters.waitingQ.timeInQ)
        hourlyResponseSchedule.addResponseToAllIntervals(myMalWartRecruiters.waitingQ.timeInQ)
        hourlyResponseSchedule.addResponseToAllIntervals(myJHBuntRecruiters.timeAvgInstantaneousUtil)
        hourlyResponseSchedule.addResponseToAllIntervals(myMalWartRecruiters.timeAvgInstantaneousUtil)
        peakResponseInterval.startTime = 150.0
        peakResponseInterval.addResponseToInterval(myTotalAtRecruiters)
        peakResponseInterval.addResponseToInterval(myJHBuntRecruiters.timeAvgInstantaneousUtil)
        peakResponseInterval.addResponseToInterval(myMalWartRecruiters.timeAvgInstantaneousUtil)
    }

Example Output

Name Count Average Half-Width
JHBuntR:InstantaneousUtil:IntervalAvg:Hourly:01:[0_0,60_0] 400 0.152 0.009
JHBuntR:InstantaneousUtil:ValueAtStart:Hourly:01:[0_0,60_0] 400 0 0
JHBuntR:InstantaneousUtil:IntervalAvg:Hourly:02:[60_0,120_0] 400 0.465 0.015
JHBuntR:InstantaneousUtil:ValueAtStart:Hourly:02:[60_0,120_0] 400 0.272 0.028
StudentsAtRecruiters:IntervalAvg:PeakPeriod:[150_0,270_0] 400 30.891 0.809
StudentsAtRecruiters:ValueAtStart:PeakPeriod:[150_0,270_0] 400 9.905 0.494
JHBuntR:InstantaneousUtil:IntervalAvg:PeakPeriod:[150_0,270_0] 400 0.992 0.002
JHBuntR:InstantaneousUtil:ValueAtStart:PeakPeriod:[150_0,270_0] 400 0.94 0.017
MalWartR:InstantaneousUtil:IntervalAvg:PeakPeriod:[150_0,270_0] 400 0.971 0.004
MalWartR:InstantaneousUtil:ValueAtStart:PeakPeriod:[150_0,270_0] 400 0.85 0.029

Summary of New Concepts

NHPPEventGenerator: A class that models the time between events from a non-homogeneous Poisson process.

NHPPTimeBtwEventRV: A specialized random variable that models the time between events according to a non-homogeneous Poisson process.

CapacitySchedule: A class that specifies that amount of capacity and the duration of availability for a resource.

ResponseInterval: A class that represents an interval of time over which statistical collection should be performed.

ResponseSchedule: A class that permits the specification of many response intervals and allows the repeating of the intervals over time.

⌂ Index