Skip to Content
Course content

197: Simulation Modeling for Logistics Networks

Click on the "Edit" button in the top corner of the screen to edit your slide content.

Imagine you're running a high-end bakery that delivers custom cakes across the city. You have a limited number of delivery drivers and a few staging areas where cakes sit waiting for a ride. If you just guess how many drivers you need, you'll either waste money on idle staff or, worse, have expensive cakes melting on a loading dock because no one was available to move them. To solve this without risking your business, you'd probably sketch out a "what-if" scenario on a whiteboard: "What happens if I get 50 orders on a Friday but only have 3 drivers?"

Simulation modeling in R is essentially that whiteboard sketch, but powered by math and code. Instead of guessing, we create a "digital twin" of your logistics network. Here is how the bakery analogy maps directly to the simulation concepts we're using today:

  • The Cake (The Entity): This is the individual unit moving through your system. In logistics, this could be a pallet, a shipping container, or a single parcel.
  • The Loading Dock (The Queue): This is where entities wait when the next step in the process is full. We track the length of this queue to find our bottlenecks.
  • The Driver (The Resource): This is the limited capacity. A driver can only move one cake at a time. If all drivers are busy, the "entity" stays in the "queue."
  • The Drive Time (The Stochastic Delay): In the real world, a trip doesn't always take 20 minutes. Sometimes it's 15, sometimes it's 45 due to traffic. We model this using probability distributions rather than fixed numbers.

Defining the Logistics Flow with Simmer

For this, we'll use the simmer package. It's a discrete-event simulation (DES) framework. Unlike a standard loop, DES jumps from one event (like a package arriving) to the next, ignoring the empty space in between, which makes it incredibly efficient for logistics.

library(simmer)

First, we define the "trajectory"—the path a package takes. I like to think of this as the "Standard Operating Procedure" (SOP) for the shipment.

# Define the SOP for a package
package_trajectory <- trajectory("package_path") %>%
  set_attribute("arrival_time", function() {now(env)}) %>%
  seize("truck") %>% 
  timeout(function() {rnorm(1, mean = 120, sd = 20)}) %>% # Transit time: 2 hours avg
  release("truck")

Notice that rnorm call. I'm not saying the trip takes 120 minutes; I'm saying it usually takes 120, but it varies. That's where the real insight comes from. If you only model the average, you'll never see the system crash during a peak surge.

Wiring the Network and Running the Clock

Now we need to actually build the environment and tell R how many "resources" (trucks) we have. This is where you can play "What If?" by changing a single digit.

env <- simmer("LogisticsNet") %>%
  add_resource("truck", capacity = 5) %>% # We have 5 trucks
  add_generator("package", package_trajectory, function() {rexp(1, 1/30)}) # New package every 30 mins avg

env %>% run(until = 1000) # Run for 1000 minutes of simulated time

I used rexp (exponential distribution) for the generator because shipments rarely arrive on a perfect clock; they arrive in clusters. This is a common mistake I see—using a fixed interval for arrivals. Real logistics are messy; your model should be too.

Extracting the Bottleneck Data

Running the simulation is the easy part. The value is in the data. We want to know: How long were the packages waiting?

arrivals <- get_mon_arrivals(env)
resources <- get_mon_resources(env)

# Calculate average waiting time
avg_wait <- mean(arrivals$`activity_time` - (arrivals$`end_time` - arrivals$`start_time`))
print(paste("Average Time in System:", round(avg_wait, 2), "minutes"))

If that average wait time is too high, you don't just "hope for the best." You go back to your add_resource line, bump the capacity from 5 to 6, and run it again. You've just performed a cost-benefit analysis without spending a dime on a new truck.




📋 Practical Task

Exercise: Optimizing the Regional Distribution Hub

You have been tasked with auditing a regional hub that ships medical supplies. The hub currently has 8 loading bays. Supplies arrive following an exponential distribution with an average of 15 minutes between arrivals. The loading process (seizing the bay and finishing the load) takes an average of 45 minutes, with a standard deviation of 10 minutes.

Your goal: Write a simulation script using simmer to determine the average time a shipment spends in the system. Then, determine the minimum number of loading bays required to ensure the average time a shipment spends in the system (including waiting) stays below 60 minutes.

Your code should include:

  • A trajectory defining the seizure and release of the "bay" resource.
  • A generator that simulates the arrivals.
  • A loop or a series of runs that tests different bay capacities (starting at 8) until the 60-minute threshold is met.
  • A final print statement identifying the optimal number of bays.
Rating
0 0

There are no comments for now.

to be the first to leave a comment.