Skip to content
or press P · arrows to step · Esc to leavetap or swipe to step

A first look at Quisim

Build your first queueing model, block by block

Quisim

What is Quisim? ​

A model is a network of blocks ​

diagramdiagram
Model codeOpen in Quisim
qml
NumberInput { id: number0; initValue:125; label:"1/λ"; lower:1 }
NumberInput { id: number1; initValue:100; label:"1/µ"; lower:1 }
Wire { from: number0.measureValue; to: rnd0.parameterMean }
Wire { from: number1.measureValue; to: rnd1.parameterMean }
RandomExponential { id:rnd0 }
RandomExponential { id:rnd1 }
Wire { from:rnd0.measureRandomVariate; to:arrive0.parameterDuration }
Wire { from:rnd1.measureRandomVariate; to:perform0.parameterDuration }
Arrive { id:arrive0 }
Pipe { from:arrive0.outlet; to:queue0.inlet }
Queue { id:queue0 }
Pipe { from:queue0.outlet; to:perform0.inlet }
Perform { id:perform0 }
Pipe { from:perform0.outlet; to:leave0.inlet }
Leave { id:leave0 }
Wire { from: queue0.measureLoad; to: chart0.parameters[0] }
Statistics { id: statistics0; height:48; width:128; label:"Sojourn\ntime" }
Wire { from: leave0.measureSojourn; to: statistics0.parameters[0] }
Chart { id: chart0; height:66; width:384 }
  • Discrete-event simulation: the state changes only at events (an arrival, the end of a service); the clock jumps from one event to the next
  • By the end of this session you will have built this model yourself. More: How a discrete-event simulation runs

The Quisim window ​

The Quisim window: toolbar, canvas, code editor and tabsThe Quisim window: toolbar, canvas, code editor and tabs

Canvas on the left, model code on the right: they are two views of the same model. See the UI guide.

How to follow along in the free demo ​

Hands on, in the browser ​

  1. Show the editor pane (pen icon). Examples tab: load a ready model. New: an empty canvas
  2. Blocks tab: click a block to add it; drag from socket to socket to draw a pipe or a wire
  3. Type values into the NumberInput fields, even while it runs (other properties are fixed in the demo)
  4. Animate (film icon) on, Start clean (Ctrl+Enter); Pause, Step; the Monitor tab shows the event list
  • The demo kit: NumberInput, Arrive, Queue, Perform, Leave, 2 × RandomExponential, Chart, Statistics. Just enough for today
  • Full version: open Model code under any picture and paste it into the editor instead

Times are in time units (milliseconds in the demo). Without a Steering block a run lasts 10 000, about 10 s of animation.

Blocks, sockets, pipes and wires ​

Sockets of a Queue blockSockets of a Queue block

  • Entity blocks move entities (Arrive, Queue, Perform, Leave); value blocks give numbers; report blocks show results
  • Green entity sockets are joined by pipes: entities flow. Silver data sockets are joined by wires: numbers flow, from a measure (bottom right) to a parameter (bottom left)

More: Block Sockets, connecting blocks

Build along: your first queue ​

Build it: 1. Arrivals at a fixed pace ​

  1. New, then Blocks tab, Entity: click Arrive and Leave
  2. Drag Arrive's outlet O onto Leave's inlet I: a pipe
  3. Value: click NumberInput, type 100 into its field
  4. Drag its measure N onto Arrive's parameter D: a wire
  5. Animate on, Start clean
Model codeOpen in Quisim
qml
NumberInput { id: number0; initValue:100; label:"D" }
Wire { from: number0.measureValue; to: arrive0.parameterDuration }
Arrive { id:arrive0 }
Pipe { from:arrive0.outlet; to:leave0.inlet }
Leave { id:leave0 }

One entity every 100 time units. Arrive cannot store entities, so Leave takes each one away at once.

Predict: how many entities? ​

Question ​

The run lasts 10 000. How many entities reach Leave? How many events wait in the event list at any moment?

Run it: hover Leave's socket N: 101 entities, one at time 0 and then one every 100 up to time 10 000.

  • Pause, open Monitor > Events, press Step: one entity event at a time, the next arrival, and the clock jumps by exactly 100

Build it: 2. Random arrivals ​

  1. Select the NumberInput, Delete component with connections (trash icon, Ctrl+Backspace)
  2. Random: click RandomExponential; wire its V onto Arrive's D
  3. Add a NumberInput, type 125: this is 1/λ1/\lambda
  4. Wire its N onto RandomExponential's mean µ
Model codeOpen in Quisim
qml
NumberInput { id: number0; initValue:125; label:"1/λ" }
Wire { from: number0.measureValue; to: rnd0.parameterMean }
RandomExponential { id:rnd0; seed:11 }
Wire { from:rnd0.measureRandomVariate; to:arrive0.parameterDuration }
Arrive { id:arrive0 }
Pipe { from:arrive0.outlet; to:leave0.inlet }
Leave { id:leave0 }

A new inter-arrival time is drawn for every entity, with mean 1/λ=1251/\lambda = 125. This is the Arrive→Leave example.

Predict: same run twice? ​

Question ​

Press Start clean twice. Do you get the same number of entities both times? About how many?

Run it: about 10 000/125=8010\,000 / 125 = 80, but a different count each time.

  • Without a fixed seed, every start draws new random numbers. The pictures on these slides use fixed seeds, so your numbers will differ a little
  • The random blocks also offer Uniform and Normal. For durations, prefer bounded distributions (min ≥ 0). More: Random Number Blocks

Build it: 3. A server ​

  1. Delete Leave (with connections)
  2. Add Perform; pipe Arrive O → Perform I
  3. Drag from Perform's O into the Blocks tab onto Leave's inlet: Leave is added, already connected
  4. Second RandomExponential: V → Perform's D
  5. New NumberInput, type 100 (1/μ1/\mu), N → its µ
diagramdiagram
Model codeOpen in Quisim
qml
NumberInput { id: number0; initValue:125; label:"1/λ" }
NumberInput { id: number1; initValue:100; label:"1/µ" }
Wire { from: number0.measureValue; to: rnd0.parameterMean }
Wire { from: number1.measureValue; to: rnd1.parameterMean }
RandomExponential { id:rnd0; seed:11 }
RandomExponential { id:rnd1; seed:22 }
Wire { from:rnd0.measureRandomVariate; to:arrive0.parameterDuration }
Wire { from:rnd1.measureRandomVariate; to:perform0.parameterDuration }
Arrive { id:arrive0 }
Pipe { from:arrive0.outlet; to:perform0.inlet }
Perform { id:perform0 }
Pipe { from:perform0.outlet; to:leave0.inlet }
Leave { id:leave0 }

Predict: a server without a queue ​

Question ​

Arrivals every 125 on average, service takes 100 on average. Do still about 80 entities pass in 10 000?

Run it: clearly fewer (hover Leave's N): about 68 in the pictured run, where the arrivals alone gave 86. With Animate on, Arrive's outlet turns red: while Perform is busy, the new entity cannot leave Arrive, it is blocked, and the next inter-arrival time starts late.

Key insight ​

Without a queue, a busy server silently changes the arrival process you specified.

Build it: 4. A waiting line ​

  1. Delete Arrive (with connections)
  2. Add Queue; pipe Queue O → Perform I
  3. Drag from Queue's I into the Blocks tab onto Arrive's outlet: Arrive comes back connected
  4. Wire the first RandomExponential's V → Arrive's D again
Model codeOpen in Quisim
qml
NumberInput { id: number0; initValue:125; label:"1/λ" }
NumberInput { id: number1; initValue:100; label:"1/µ" }
Wire { from: number0.measureValue; to: rnd0.parameterMean }
Wire { from: number1.measureValue; to: rnd1.parameterMean }
RandomExponential { id:rnd0; seed:11 }
RandomExponential { id:rnd1; seed:22 }
Wire { from:rnd0.measureRandomVariate; to:arrive0.parameterDuration }
Wire { from:rnd1.measureRandomVariate; to:perform0.parameterDuration }
Arrive { id:arrive0 }
Pipe { from:arrive0.outlet; to:queue0.inlet }
Queue { id:queue0 }
Pipe { from:queue0.outlet; to:perform0.inlet }
Perform { id:perform0 }
Pipe { from:perform0.outlet; to:leave0.inlet }
Leave { id:leave0 }

Entities now wait in the Queue, Arrive is never blocked. This is the M/M/1 queue: Markov arrivals, Markov service, 1 server.

Build it: 5. Watch the queue ​

  1. Report: click Chart
  2. Wire Queue's measure L (its length) onto the Chart's P[0]
  3. Start clean, hover the Chart to read values
diagramdiagram
Model codeOpen in Quisim
qml
NumberInput { id: number0; initValue:125; label:"1/λ" }
NumberInput { id: number1; initValue:100; label:"1/µ" }
Wire { from: number0.measureValue; to: rnd0.parameterMean }
Wire { from: number1.measureValue; to: rnd1.parameterMean }
RandomExponential { id:rnd0; seed:11 }
RandomExponential { id:rnd1; seed:22 }
Wire { from:rnd0.measureRandomVariate; to:arrive0.parameterDuration }
Wire { from:rnd1.measureRandomVariate; to:perform0.parameterDuration }
Arrive { id:arrive0 }
Pipe { from:arrive0.outlet; to:queue0.inlet }
Queue { id:queue0 }
Pipe { from:queue0.outlet; to:perform0.inlet }
Perform { id:perform0 }
Pipe { from:perform0.outlet; to:leave0.inlet }
Leave { id:leave0 }
Wire { from: queue0.measureLoad; to: chart0.parameters[0] }
Chart { id: chart0; height:66; width:384 }

The queue length jumps up and down: long quiet spells, then sudden bursts.

Build it: 6. Measure the time in system ​

  1. Report: click Statistics
  2. Wire Leave's measure LS (sojourn time of the last entity) onto its P[0]
  3. Start clean: it shows the mean time in system WW of the run
diagramdiagram
Model codeOpen in Quisim
qml
NumberInput { id: number0; initValue:125; label:"1/λ"; lower:1 }
NumberInput { id: number1; initValue:100; label:"1/µ"; lower:1 }
Wire { from: number0.measureValue; to: rnd0.parameterMean }
Wire { from: number1.measureValue; to: rnd1.parameterMean }
RandomExponential { id:rnd0; seed:11 }
RandomExponential { id:rnd1; seed:22 }
Wire { from:rnd0.measureRandomVariate; to:arrive0.parameterDuration }
Wire { from:rnd1.measureRandomVariate; to:perform0.parameterDuration }
Arrive { id:arrive0 }
Pipe { from:arrive0.outlet; to:queue0.inlet }
Queue { id:queue0 }
Pipe { from:queue0.outlet; to:perform0.inlet }
Perform { id:perform0 }
Pipe { from:perform0.outlet; to:leave0.inlet }
Leave { id:leave0 }
Wire { from: queue0.measureLoad; to: chart0.parameters[0] }
Statistics { id: statistics0; height:48; width:128; label:"Sojourn\ntime" }
Wire { from: leave0.measureSojourn; to: statistics0.parameters[0] }
Chart { id: chart0; height:66; width:384 }

Your model is now the M/M/1 example. Lost track? Load it from the Examples tab and carry on.

The M/M/1 queue in numbers ​

Your two NumberInputs set the two rates: λ=1/125\lambda = 1/125, μ=1/100\mu = 1/100.

  • Utilization ρ=λ/μ=100/125=0.8\rho = \lambda/\mu = 100/125 = 0.8: the server is busy 80% of the time
  • Theory: Nq=ρ2/(1−ρ)=3.2N_q = \rho^2/(1-\rho) = 3.2 waiting, waiting time Wq=400W_q = 400, time in system W=1/(μ−λ)=500W = 1/(\mu-\lambda) = 500
  • Compare with your Statistics block: one run of 10 000 is short, so expect to be off by quite a bit
  • The theory only holds for ρ<1\rho < 1. What happens beyond? Next slides

Your turn: step through the event list ​

M/M/1, Animate on, about 5 minutes ​

  1. Start clean, then Pause after a few seconds; open Monitor > Events
  2. Press Step ten times. For each step write down the clock time and the block that owned the event
  3. Answer: which blocks own events? Why is there never an event "entity joins the queue"? Does the clock advance in equal steps?
Step12345…
Time
Block

More: The Monitor tab, Event List

Predict: a slightly slower server ​

Question ​

Set 1/µ from 100 to 120: service is 20% slower, ρ=0.96\rho = 0.96. How much longer is the time in system WW?

diagramdiagram
Model codeOpen in Quisim
qml
NumberInput { id: number0; initValue:125; label:"1/λ"; lower:1 }
NumberInput { id: number1; initValue:120; label:"1/µ"; lower:1 }
Wire { from: number0.measureValue; to: rnd0.parameterMean }
Wire { from: number1.measureValue; to: rnd1.parameterMean }
RandomExponential { id:rnd0; seed:11 }
RandomExponential { id:rnd1; seed:22 }
Wire { from:rnd0.measureRandomVariate; to:arrive0.parameterDuration }
Wire { from:rnd1.measureRandomVariate; to:perform0.parameterDuration }
Arrive { id:arrive0 }
Pipe { from:arrive0.outlet; to:queue0.inlet }
Queue { id:queue0 }
Pipe { from:queue0.outlet; to:perform0.inlet }
Perform { id:perform0 }
Pipe { from:perform0.outlet; to:leave0.inlet }
Leave { id:leave0 }
Wire { from: queue0.measureLoad; to: chart0.parameters[0] }
Statistics { id: statistics0; height:48; width:128; label:"Sojourn\ntime" }
Wire { from: leave0.measureSojourn; to: statistics0.parameters[0] }
Chart { id: chart0; height:66; width:384 }

Run it: the queue climbs about twice as high (at most 15 instead of 8 in the pictured run), and WW is about 1270 instead of about 520.

  • Theory says W=1/(μ−λ)=3000W = 1/(\mu-\lambda) = 3000, six times as long as at 100. A run of 10 000 starts with an empty queue and is far too short to get there: near ρ=1\rho = 1 the queue needs a long time to build up

Your turn: service time vs. time in system ​

Your M/M/1 (or the example), 1/λ = 125, about 10 minutes ​

For each 1/µ: type it in, Start clean (Animate off for speed), note the Statistics mean and the highest queue length on the Chart.

1/µ5080100110120
ρ=(1/μ)/(1/λ)\rho = (1/\mu)/(1/\lambda)0.400.640.800.880.96
Theory WW832225009173000
Your WW
Max. queue
  • Plot WW against ρ\rho. At which ρ\rho does your run start to miss the theory badly, and why?

Predict: a server that is too slow ​

Question ​

Set 1/µ = 150, longer than the mean time between arrivals. What does the Chart show?

Run it: the queue drifts upward for the whole run (to about 25 in a seeded test run) and every Start clean gives a very different WW, from about 300 to over 2500 in our test runs. With ρ=150/125=1.2>1\rho = 150/125 = 1.2 > 1 there is no steady state: the longer the run, the longer the queue and the Statistics mean.

  • Try 130 too (ρ=1.04\rho = 1.04): in a run of 10 000 it looks much like 120, because the queue grows by only about 3 entities per 10 000 on average

Key insight ​

Waiting time does not grow in proportion to load: near ρ=1\rho = 1 a little extra load makes the queue explode.

Replications and confidence intervals ​

The Statistics example ​

diagramdiagram
Model codeOpen in Quisim
qml
NumberInput { id: number1; initValue:100; label:"1/µ\nservice time"; lower:1; height:58; width:96 }
NumberInput { id: number0; initValue:125; label:"1/λ\ninterarrival time"; lower:1; height:58; width:112 }
RandomExponential { id:rnd0 }
Wire { from: number0.measureValue; to: rnd0.parameterMean }
Wire { from: number1.measureValue; to: rnd1.parameterMean }
Wire { from:rnd0.measureRandomVariate; to:arrive0.parameterDuration }
Wire { from: number2.measureValue; to: perform0.parameterCapacity }
Wire { from:rnd0.measureRandomVariate; to:arrive0.parameterDuration }
Wire { from:rnd1.measureRandomVariate; to:perform0.parameterDuration }
NumberInput { id: numberinput1; initValue:10000; label:"Duration"; width:112; lower:1; upper:1000000 }
NumberInput { id: numberinput0; initValue:20; label:"Replications"; width:112; lower:1; upper:100 }
NumberInput { id: number2; initValue:1; label:"k servers"; lower:1 }
Wire { from: numberinput1.measureValue; to: steering0.parameterDuration }
Steering { id: steering0; duration:50000 }
Wire { from: numberinput0.measureValue; to: steering0.parameterReplications }
RandomExponential { id:rnd1 }
Arrive { id:arrive0 }
Pipe { from:arrive0.outlet; to:queue0.inlet }
Queue { id:queue0 }
Pipe { from:queue0.outlet; to:perform0.inlet }
Perform { id:perform0 }
Pipe { from:perform0.outlet; to:leave0.inlet }
Leave { id:leave0 }
Statistics { id: statistics0; timeAverage:true; numParameters:2; warmupWidth:5000; label:"Queue length\nService load"; height:192; width:152; x:646; y:245 }
Wire { from: queue0.measureLoad; to: statistics0.parameters[0] }
Wire { from: perform0.measureLoad; to: statistics0.parameters[1] }
Statistics { id: statistics2; warmupWidth:5000; label:"Sojourn time"; height:192; width:152; x:646; y:12 }
Statistics { id: statistics1; numParameters:2; warmupWidth:5000; label:"Waiting time\nService time"; height:192; width:152; x:470; y:245 }
Wire { from: queue0.measureWait; to: statistics1.parameters[0] }
Wire { from: rnd1.measureRandomVariate; to: statistics1.parameters[1] }
Wire { from: leave0.measureSojourn; to: statistics2.parameters[0] }

Your M/M/1, plus a Steering block run 20 times (Replications). One row per run, then mean ± 95% radius. Compare with Nq=3.2N_q = 3.2, ρ=0.8\rho = 0.8, Wq=400W_q = 400, W=500W = 500.

Replications and the seed rule ​

One run is one possible outcome. Replications repeat it with new random numbers.

  • Replication rr reseeds every random stream with seed + r+\, r: different, yet reproducible
  • Each run gives one average (e.g. the mean waiting time). Over mm runs, report the overall average Θ^±δ\hat\Theta \pm \delta, with δ=tm−1, α/2Sm2/m\delta = t_{m-1,\,\alpha/2}\sqrt{S_m^2/m} (Sm2S_m^2: variance of the mm averages)
  • The radius shrinks like 1/m1/\sqrt{m}: four times the replications halve it
  • Overlapping intervals do not show a significant difference

More: Reading the confidence interval

Your turn: how many replications? ​

Examples > Statistics, about 10 minutes ​

Animate off. Set Replications and Duration as in the table, Start clean, and copy the Sojourn time mean and its ± radius.

Replications5208020
Duration10 00010 00010 000100 000
Sojourn time WW ± radius
Contains 500?
  • Does 4 × the replications halve the radius? What does 10 × the duration do?
  • Set 1/µ to 120 and repeat the first column: does the interval contain the theory value W=3000W = 3000? Why not?

Key insight: replications ​

Key insight ​

A simulation result is an estimate: always report it with its confidence interval, and buy precision with more or longer runs.

  • The first 5000 of every run are cut (warmupWidth: 5000): the queue starts empty, which is not typical. More on this at the end

More servers: M/M/k ​

The M/M/k example ​

diagramdiagram
Model codeOpen in Quisim
qml
NumberInput { id: number0; initValue:55; label:"1/λ"; lower:1 }
NumberInput { id: number1; initValue:500; label:"1/µ"; lower:1 }
Wire { from: number0.measureValue; to: rnd0.parameterMean }
Wire { from: number1.measureValue; to: rnd1.parameterMean }
RandomExponential { id:rnd0 }
RandomExponential { id:rnd1 }
Wire { from:rnd0.measureRandomVariate; to:arrive0.parameterDuration }
Wire { from:rnd1.measureRandomVariate; to:perform0.parameterDuration }
Arrive { id:arrive0 }
Pipe { from:arrive0.outlet; to:queue0.inlet }
Queue { id:queue0 }
Pipe { from:queue0.outlet; to:perform0.inlet }
NumberInput { id: number2; initValue:10; label:"Capacity"; lower:1 }
Wire { from: number2.measureValue; to: perform0.parameterCapacity }
Perform { id:perform0 }
Pipe { from:perform0.outlet; to:leave0.inlet }
Leave { id:leave0 }
Wire { from: queue0.measureLoad; to: chart0.parameters[0] }
Wire { from: perform0.measureLoad; to: chart0.parameters[1] }
Statistics { id: statistics0; height:48; width:128; label:"Sojourn\ntime" }
Wire { from: leave0.measureSojourn; to: statistics0.parameters[0] }
Chart { id: chart0; numParameters:2; height:66; width:384 }

Capacity k=10k = 10: Perform serves up to 10 entities at once. ρ=λ/(kμ)=500/(10⋅55)=0.91\rho = \lambda / (k\mu) = 500/(10 \cdot 55) = 0.91. The Chart shows queue length and busy servers.

Predict: two servers fewer ​

Question ​

Open Examples > M/M/k and set Capacity from 10 to 8. What happens to the queue?

diagramdiagram
Model codeOpen in Quisim
qml
NumberInput { id: number0; initValue:55; label:"1/λ"; lower:1 }
NumberInput { id: number1; initValue:500; label:"1/µ"; lower:1 }
Wire { from: number0.measureValue; to: rnd0.parameterMean }
Wire { from: number1.measureValue; to: rnd1.parameterMean }
RandomExponential { id:rnd0; seed:11 }
RandomExponential { id:rnd1; seed:22 }
Wire { from:rnd0.measureRandomVariate; to:arrive0.parameterDuration }
Wire { from:rnd1.measureRandomVariate; to:perform0.parameterDuration }
Arrive { id:arrive0 }
Pipe { from:arrive0.outlet; to:queue0.inlet }
Queue { id:queue0 }
Pipe { from:queue0.outlet; to:perform0.inlet }
NumberInput { id: number2; initValue:8; label:"Capacity"; lower:1 }
Wire { from: number2.measureValue; to: perform0.parameterCapacity }
Perform { id:perform0 }
Pipe { from:perform0.outlet; to:leave0.inlet }
Leave { id:leave0 }
Wire { from: queue0.measureLoad; to: chart0.parameters[0] }
Wire { from: perform0.measureLoad; to: chart0.parameters[1] }
Statistics { id: statistics0; height:48; width:128; label:"Sojourn\ntime" }
Wire { from: leave0.measureSojourn; to: statistics0.parameters[0] }
Chart { id: chart0; numParameters:2; height:66; width:384 }

Run it: the 8 servers are busy almost all the time and the queue only grows, to about 30 at the end of the pictured run (WW about 1340). With the same random numbers and 10 servers the queue ends empty and WW is about 575. ρ=500/(8⋅55)=1.14>1\rho = 500/(8 \cdot 55) = 1.14 > 1: it needs k>500/55=9.1k > 500/55 = 9.1 servers.

Your turn: how many servers? ​

Examples > Erlang C, about 10 minutes ​

Set 1/λ = 55, 1/µ = 500, then each k servers below. Erlang C computes the theory, the model simulates it (20 replications). Record both.

k servers10111214
ρ0.910.830.760.65
P(wait), theory
W theory
W simulated ± radius
  • Each server costs the same. Where does one more server stop paying off?
  • Optional: k = 1 with 1/λ = 125, 1/µ = 100 is your M/M/1 again: do you get 500?

Key insight: servers ​

Key insight ​

The number of servers needed is set by the load λ/μ\lambda/\mu, but how long people wait is decided by the last one or two servers above it.

  • At k=10k = 10 almost 70% of the customers wait, at k=12k = 12 fewer than 30%

What the full version adds ​

Steering: duration, replications, seed ​

diagramdiagram
Model codeOpen in Quisim
qml
Steering { id: steering0; duration: 100000; replications: 10; seed: 1 }
RandomExponential { id: rnd0; mean: 125; seed: 88 }
RandomExponential { id: rnd1; mean: 100; seed: 143 }
Wire { from: rnd0.measureRandomVariate; to: arrive0.parameterDuration }
Wire { from: rnd1.measureRandomVariate; to: perform0.parameterDuration }
Arrive { id: arrive0 }
Pipe { from: arrive0.outlet; to: queue0.inlet }
Queue { id: queue0 }
Pipe { from: queue0.outlet; to: perform0.inlet }
Perform { id: perform0 }
Pipe { from: perform0.outlet; to: leave0.inlet }
Leave { id: leave0 }
Wire { from: queue0.measureLoad; to: stats0.parameters[0] }
Wire { from: perform0.measureLoad; to: stats0.parameters[1] }
Statistics { id: stats0; numParameters: 2; timeAverage: true; label: "Nq / ρ"; x: 520; y: 0 }
Wire { from: leave0.measureSojourn; to: statsW.parameters[0] }
Statistics { id: statsW; label: "W"; x: 760; y: 0 }
  • Every block and property is editable; Steering sets duration D, replications R, editions E, seed S, speed V
  • A fixed seed makes every run reproducible; seed 0 on a random block draws from the Steering seed. Levels need timeAverage: true

More: Running a simulation

Experiments: compare configurations ​

diagramdiagram
Model codeOpen in Quisim
qml
// Edition 0: one fast server (mean 100). Edition 1: two slow servers (mean 200 each)
Steering { id: steering0; duration: 200000; replications: 10; editions: 2; seed: 1 }
Function { id: fnConfig; numMeasures: 2; label: "configuration"
           code: "M[0] = [1, 2][P[0]]       // servers
M[1] = [100, 200][P[0]]   // mean service time" }
Wire { from: steering0.measureEdition; to: fnConfig.parameters[0] }
Wire { from: fnConfig.measures[0]; to: perform0.parameterCapacity }
Wire { from: fnConfig.measures[1]; to: rndB.parameterMean }
RandomExponential { id: rndA; mean: 125; seed: 11 }
Wire { from: rndA.measureRandomVariate; to: arrive0.parameterDuration }
Arrive { id: arrive0 }
Pipe { from: arrive0.outlet; to: queue0.inlet }
Queue { id: queue0 }
Pipe { from: queue0.outlet; to: perform0.inlet }
RandomExponential { id: rndB; seed: 22 }
Wire { from: rndB.measureRandomVariate; to: perform0.parameterDuration }
Perform { id: perform0 }
Pipe { from: perform0.outlet; to: leave0.inlet }
Leave { id: leave0 }
Statistics { id: stats0; numParameters: 2; label: "waiting time / time in system" }
Wire { from: queue0.measureWait; to: stats0.parameters[0] }
Wire { from: leave0.measureSojourn; to: stats0.parameters[1] }
  • Editions: the edition number EN drives a Function that sets each configuration; all editions share the seeds (common random numbers)
  • Two slow servers: shorter wait (317 vs. 364), longer time in system (517 vs. 464). BrentOptimize and Explorer search parameters

More: compare configurations, find the best parameter, Experiment and Optimization Blocks

Warm-up: cut the start-up phase ​

diagramdiagram
Model codeOpen in Quisim
qml
// M/M/1 (utilization 0.8) that starts with a backlog of 30 jobs: how long is the warm-up?
Steering { id: steering0; duration: 100000; replications: 10; seed: 1 }
Provide { id: provideBacklog }
Pipe { from: provideBacklog.outlet; to: meterBacklog.inlet }
Meter { id: meterBacklog; capacity: 30 }     // 30 jobs already waiting at time 0
Pipe { from: meterBacklog.outlet; to: queue0.inlet }
RandomExponential { id: rndA; mean: 125; seed: 11 }
Wire { from: rndA.measureRandomVariate; to: arrive0.parameterDuration }
Arrive { id: arrive0 }
Pipe { from: arrive0.outlet; to: queue0.inlet }
Queue { id: queue0 }
Pipe { from: queue0.outlet; to: perform0.inlet }
RandomExponential { id: rndB; mean: 100; seed: 22 }
Wire { from: rndB.measureRandomVariate; to: perform0.parameterDuration }
Perform { id: perform0 }
Pipe { from: perform0.outlet; to: leave0.inlet }
Leave { id: leave0 }
EnsemblePlot { id: welch0; timeAverage: true; batchWidth: 1000; label: "Welch plot of queue length" }
Wire { from: queue0.measureLoad; to: welch0.parameters[0] }
Statistics { id: statsAll; timeAverage: true; label: "queue length, no warm-up removed" }
Wire { from: queue0.measureLoad; to: statsAll.parameters[0] }
Statistics { id: statsMser; timeAverage: true; warmupMSER: true; label: "queue length, MSER-5 warm-up" }
Wire { from: queue0.measureLoad; to: statsMser.parameters[0] }
  • Non-terminating systems: the EnsemblePlot (Welch plot) shows where the start-up ends; warmupMSER: true cuts it. Queue length 2.8 ± 0.6 (theory 3.2) instead of 6.1 ± 1.4
  • Terminating systems (a shift, a day) have a natural end: keep the whole run

More: Output Analysis, warm-up recipe

Where to go next ​

Where to go next ​

Keep exploring in the demo ​

Try Bypass, Capacity, Renege, Triage vs. No Triage, To Shorter, Sensitivity: each has NumberInputs to play with.