Home/chiaha-SLX/Example models

Simulation models written as code

Five small discrete event models, from a one-chair barber shop to an inventory that reorders itself. Each is a short text file you can read top to bottom, and each one opens from the example list in the chiaha-SLX workspace.

A barber shop

This is the smallest whole model: one barber, customers arriving about every 18 minutes, haircuts of about 16 minutes. Both times are exponential.

The barber is a control boolean. A customer waits until it’s false, sets it to true, holds for the haircut and sets it back. The generator makes a new customer after each interarrival time, and the initial block runs the shop for 480 minutes and prints how many were served.

In queueing terms this is an M/M/1 queue. In theory the barber is busy 16 ÷ 18, or about 89% of the time, and the line gets long.

module barber
{
  control boolean barber_busy = FALSE;
  int             served = 0;

  class Customer
  {
    actions
    {
      wait until (!barber_busy);
      barber_busy = TRUE;
      advance rv_expo(1, 16.0);
      barber_busy = FALSE;
      served++;
      terminate;
    }
  }

  class Generator
  {
    actions
    {
      forever
      {
        advance rv_expo(2, 18.0);
        activate new Customer();
      }
    }
  }

  initial
  {
    activate new Generator();
    advance 480.0;
    print "Served = ", served;
  }
}
The whole model. The first number in rv_expo picks the random-number stream, so arrivals and haircuts draw from separate streams.

Three bank tellers

Change the barber from a yes-or-no into a count, and one server becomes several. Here free_tellers starts at 3. A customer waits until at least one teller is free, takes one, and gives it back after service. Customers arrive about every 4 minutes and service takes about 10.

When a teller frees up and several customers are waiting, they’re checked one at a time in the order they arrived. The first one takes the teller before the next one looks, so the count never goes below zero. This is an M/M/3 queue; in theory each teller is busy 10 ÷ (3 × 4), or about 83% of the time.

control int free_tellers = 3;

class Customer
{
  actions
  {
    wait until (free_tellers > 0);
    free_tellers--;
    advance rv_expo(1, 10.0);
    free_tellers++;
    terminate;
  }
}
The customer. The generator and run length match the barber shop, with arrivals every 4 minutes on average.

A machine that breaks down

Jobs arrive about every 6 minutes and need about 5 minutes on one machine. A separate object fails the machine about every 200 minutes and repairs it in about 20.

The job’s wait until reads two control variables, so it waits for a machine that’s both idle and up. While working, it waits for whichever comes first: its finish time or a breakdown. On a breakdown it saves the work left, waits out the repair and picks up where it stopped.

In theory the machine is up 200 ÷ 220, or about 91% of the time. The run covers 48 hours.

wait until (!machine_busy
            && !machine_down);
machine_busy = TRUE;
remaining = rv_expo(1, 5.0);
forever
{
  double t0 = time;
  wait until (machine_down
    or time == t0 + remaining);
  if (!machine_down)
    break;
  remaining = remaining
              - (time - t0);
  wait until (!machine_down);
}
machine_busy = FALSE;
terminate;
A job’s actions. time inside a condition sets a deadline, so no one has to keep checking the clock.

Three parts, then assembly

An order needs three components made at the same time. Each fork starts a copy of the order’s puck that makes one part in 4 to 8 minutes and adds one to the order’s done counter.

The order itself waits until done reaches 3, then packs for 3 to 5 minutes. Because done belongs to the order, each order counts only its own parts. Orders arrive about every 20 minutes for an 8-hour day.

class Order
{
  control int done;
  actions
  {
    done = 0;
    fork { advance rv_uniform(1, 4.0, 8.0);
           done++; terminate; }
    fork { advance rv_uniform(2, 4.0, 8.0);
           done++; terminate; }
    fork { advance rv_uniform(3, 4.0, 8.0);
           done++; terminate; }
    wait until (done == 3);
    advance rv_uniform(4, 3.0, 5.0);
    terminate;
  }
}
The order. Wherever the parts finish, the pack step starts when the last one is done.

An inventory that reorders itself

Stock starts at 100. Each day a demand of 1 to 9 units comes in. If there isn’t enough stock, the shortfall is counted and stock goes to zero.

When stock falls to the reorder point of 20 and no order is out, the model places one. The order is a fork, so it travels on its own for a lead time of 2 to 4 days while daily demand keeps coming. When it arrives, stock goes back up to 100. After a year the model prints the stock on hand and the total shortage. This is an (s, S) policy, and changing s and S is the obvious first experiment.

int qty = rv_uniform_int(1, 1, 9);
if (stock >= qty)
  stock = stock - qty;
else { shortage = shortage + qty;
       stock = 0; }

if (stock <= s && !order_out)
{
  order_out = TRUE;
  fork
  {
    advance rv_uniform(1, 2.0, 4.0);
    stock = S;
    order_out = FALSE;
    terminate;
  }
}
terminate;
One day’s demand. The reorder runs beside the demand that follows it.

The workspace has more examples, including a tool crib with two kinds of mechanic, a priority queue, customers who arrive in batches, and a group of GPSS classics written in SLX. If you write GPSS, the GPSS/H page shows how each GPSS block maps onto this kind of code. If you have SLX models of your own, the SLX page lists what runs today.

SLX and GPSS/H are trademarks of their respective owners. ChiAha is not affiliated with or endorsed by them.