A Repair Problem

Ross, Simulation 5th edition, Section 7.7, p. 124-126

Description

A system needs $n$ working machines to be operational. To guard against machine breakdown, additional machines are kept available as spares. Whenever a machine breaks down it is immediately replaced by a spare and is itself sent to the repair facility, which consists of a single repairperson who repairs failed machines one at a time. Once a failed machine has been repaired it becomes available as a spare to be used when the need arises. All repair times are independent random variables having the common distribution function $G$. Each time a machine is put into use the amount of time it functions before breaking down is a random variable, independent of the past, having distribution function $F$.

The system is said to “crash” when a machine fails and no spares are available. Assuming that there are initially $n + s$ functional machines of which $n$ are put in use and $s$ are kept as spares, we are interested in simulating this system so as to approximate $E[T]$, where $T$ is the time at which the system crashes.

Install packages


In [ ]:
Pkg.update()
Pkg.add("Distributions")
Pkg.add("SimJulia")

Use Distributions.jl and SimJulia.jl


In [1]:
using Distributions
using ResumableFunctions
using SimJulia

Define constants


In [2]:
const RUNS = 5
const N = 10
const S = 3
const SEED = 150
const LAMBDA = 100
const MU = 1

srand(SEED)
const F = Exponential(LAMBDA)
const G = Exponential(MU);

Define the behaviour of a machine


In [3]:
@resumable function machine(sim::Simulation, repair_facility::Resource, spares::Store{Process})
    while true
        try
            @yield timeout(sim, Inf)
        catch exc
        end
        #println("At time $(now(sim)): $(active_process(sim)) starts working.")
        @yield timeout(sim, rand(F))
        #println("At time $(now(sim)): $(active_process(sim)) stops working.")
        get_spare = get(spares)
        @yield get_spare | timeout(sim, 0.0)
        if state(get_spare) != SimJulia.idle
            interrupt(value(get_spare))
        else
            throw(SimJulia.StopSimulation("No more spares!"))
        end
        @yield request(repair_facility)
        #println("At time $(now(sim)): $(active_process(sim)) repair starts.")
        @yield timeout(sim, rand(G))
        @yield release(repair_facility)
        #println("At time $(now(sim)): $(active_process(sim)) is repaired.")
        @yield put(spares, active_process(sim))
    end
end


Out[3]:
machine (generic function with 1 method)

Startup procedure


In [4]:
@resumable function start_sim(sim::Simulation, repair_facility::Resource, spares::Store{Process})
    procs = Process[]
    for i=1:N
        push!(procs, @process machine(sim, repair_facility, spares))
    end
    @yield timeout(sim, 0.0)
    for proc in procs
        interrupt(proc)
    end
    for i=1:S
        @yield put(spares, @process machine(sim, repair_facility, spares))
    end
end


Out[4]:
start_sim (generic function with 1 method)

One simulation run


In [5]:
function sim_repair()
    sim = Simulation()
    repair_facility = Resource(sim)
    spares = Store{Process}(sim)
    @process start_sim(sim, repair_facility, spares)
    msg = run(sim)
    stop_time = now(sim)
    println("At time $stop_time: $msg")
    stop_time
end


Out[5]:
sim_repair (generic function with 1 method)

Multiple simulations


In [6]:
results = Float64[]
for i=1:RUNS
    push!(results, sim_repair())
end
println("Average crash time: ", sum(results)/RUNS)


At time 5573.772841846017: No more spares!
At time 1438.0294516073466: No more spares!
At time 7077.413276961621: No more spares!
At time 7286.490682742159: No more spares!
At time 6820.788098062124: No more spares!
Average crash time: 5639.298870243853

In [ ]: