Assignment 11

The Simio BBQ Smoke Pit is an up-and-coming local restaurant. It features a variety of BBQ smoked meats and classic sides. This makeshift cookout is nestled in an older, renovated building in a bustling downtown block. Due to size constraints, this is a carry-out only establishment. However, as Simio BBQ Smoke Pit continues to grow in popularity, customers are facing longer wait times due to product outages and a constrained labor force. To keep up with demand, the restaurant needs help determining what new policies to enact to resolve their service bottlenecks.

Due to the long smoking time required for the BBQ meats and lengthy cook time for some sides, the restaurant is struggling to keep a reasonable level of cooked food available. Having too little inventory will affect customer satisfaction. If food shortages occur too often, wait times could increase, causing customers to leave and losing potential business for the restaurant. On the other hand, creating excess inventory could accrue extra costs. The meat is expensive and excess uneaten food must be disposed of at the end of the day.

The challenge is to balance staffing and food production to maximize profits and customer satisfaction. The restaurant is looking to investigate customer arrivals and ordering patterns, resource requirements, and food production rates. Simio BBQ Smoke Pit desires the best strategy to replenish each of the different food items on their menu. This replenishment strategy not only affects when and how much food to cook, but also how to allocate the cooked portions between the meal assembly stations and the holding cabinets. Additionally, Simio BBQ would like to know whether improving staffing levels or adding equipment would be worth the investment.

Identify:

Staffing issues. Is any work shift or service station causing delays? The base model is available at BBQDemo.zip

For this exercise, look for customized objects in the galleries available to make the scenario as real as possible. Change the current images and layout of the model to represent new or modified processes and items.

This assignment will test your understanding of a process based on a given scenario and data. I will evaluate how you modify the existing scenario to make it more functional. Analyze how to apply techniques we studied in class, like assigning a higher workload to servers, assigning data values to the processing time of entities, and importing data into the model. As well, how you manage workers and orders.

The exercise does not have a unique solution. I’ll evaluate how you interpret the process and suggest changes to it. Be creative and have fun!

I will be building a discrete event simulation of a BBQ quick service system.

library(simmer)
library(simmer.plot)
## Warning: package 'simmer.plot' was built under R version 4.5.1
## Loading required package: ggplot2
## Warning: package 'ggplot2' was built under R version 4.5.1
## 
## Attaching package: 'simmer.plot'
## The following objects are masked from 'package:simmer':
## 
##     get_mon_arrivals, get_mon_attributes, get_mon_resources
library(readr)
library(readxl)
library(dplyr)
## 
## Attaching package: 'dplyr'
## The following object is masked from 'package:simmer':
## 
##     select
## The following objects are masked from 'package:stats':
## 
##     filter, lag
## The following objects are masked from 'package:base':
## 
##     intersect, setdiff, setequal, union
library(lubridate)
## 
## Attaching package: 'lubridate'
## The following objects are masked from 'package:simmer':
## 
##     now, rollback
## The following objects are masked from 'package:base':
## 
##     date, intersect, setdiff, union

Imported the excel sheet on simio BBq pit, which had two sheets named WorkSchedule and CustomerArrivals.

WorkSchedule<-read_excel("~/downloads/Simio BBQ Smoke Pit Data .xlsx", sheet = "WorkSchedule", skip = 1) #skipped first row as column names are in row 2.
ArrivalsDF<-read_excel("~/downloads/Simio BBQ Smoke Pit Data .xlsx", sheet="CustomerArrivals")
head(WorkSchedule)
## # A tibble: 6 × 4
##   `Worker Name` Position        `Start of Shift` `End of Shift`
##   <chr>         <chr>           <chr>            <chr>         
## 1 Albert        Food Production 9:00am           4:30pm        
## 2 Bernise       Food Production 9:00am           4:30pm        
## 3 Chad          Food Production 9:00am           4:30pm        
## 4 Duncan        FoodProduction  4:30pm           11:30pm       
## 5 Emilio        FoodProduction  4:30pm           11:30pm       
## 6 Felicity      FoodProduction  4:30pm           11:30pm
head(ArrivalsDF)
## # A tibble: 6 × 5
##   Time                OrderID ItemID MenuItem                    `Side 1`   
##   <dttm>                <dbl>  <dbl> <chr>                       <chr>      
## 1 2024-11-22 10:11:47       1      1 Small Platter - Ribs        Green Beans
## 2 2024-11-22 10:11:47       1      2 Sandwich - Pulled Pork      Fries      
## 3 2024-11-22 10:15:26       2      1 Sandwich - Brisket          Fries      
## 4 2024-11-22 10:15:26       2      2 Small Platter - Pulled Pork Baked Beans
## 5 2024-11-22 10:19:40       3      1 Sandwich - Pulled Pork      Fries      
## 6 2024-11-22 10:19:40       3      2 Sandwich - Pulled Pork      Fries

Cleaned arrival time for simulation in Arrivals dataset.

ArrivalsDF$Time<- ymd_hms(ArrivalsDF$Time)
ArrivalsDF<- ArrivalsDF|>
  arrange(Time)

Converted arrivals into simulation input- into interarrival times

start_time<-min(ArrivalsDF$Time)

Arrivals<- ArrivalsDF|>
  mutate(time_min=as.numeric(difftime(Time, start_time, units="mins")))
arrivalTimes<-Arrivals$time_min
interarrival <- diff(c(0, arrivalTimes))

Arrival added using a function.

arrival_generator<- function(){
  if(length(interarrival)==0) return(Inf)
     val<- interarrival[1]
     interarrival<<- interarrival[-1]
     return(val)
}

cleaned order size from Arrivals dataset, to extract patterns.

orders<-Arrivals|>
  group_by(OrderID)|>
  summarise(items=n())
get_order_sz<-function(){
  sample(orders$items, 1)
}

Build simulation

#created an environment, resources adjusted for experiments.
env<- simmer("BBQ Pit")
env<-env|>
  add_resource("order_station", capacity = 2)|>
  add_resource("prep_station", capacity = 2)|>
  add_resource("pickup_station", capacity = 1)

Defined the customer trajectory. For each customer: Places an order, food is prepared, and picks up order. Order size was incorporated to affect prep time.

customerT<- trajectory("Customer Path")|>
  #ordering
  seize("order_station", 1)|>
  timeout(function() runif(1, 1, 3))|> # ordering take 1–3 min
  release("order_station", 1)|>
  
  # food preparation depending on order size.
  seize("prep_station", 1)|>
  timeout(function() {
    order_size <- get_order_sz()
    return(order_size * runif(1, 2, 4))  # larger orders would take longer
  }) |>
  release("prep_station", 1) |>
  
  # pickup
  seize("pickup_station", 1)|>
  timeout(function() runif(1, 0.5, 1.5))|>
  release("pickup_station", 1)
#added arrivals to environment
env<-env|>
  add_generator("customer", customerT, arrival_generator)

Ran simulation

sim_time<-max(arrivalTimes)+60
env<-env|>
  run(until=sim_time)

Results for resource utilization

res_usage<- get_mon_resources(env)

print(head(res_usage))
##        resource     time server queue capacity queue_size system limit
## 1 order_station 0.000000      1     0        2        Inf      1   Inf
## 2 order_station 0.000000      2     0        2        Inf      2   Inf
## 3 order_station 1.650164      1     0        2        Inf      1   Inf
## 4  prep_station 1.650164      1     0        2        Inf      1   Inf
## 5 order_station 2.915353      0     0        2        Inf      0   Inf
## 6  prep_station 2.915353      2     0        2        Inf      2   Inf
##   replication
## 1           1
## 2           1
## 3           1
## 4           1
## 5           1
## 6           1

Plot of resource usage.

plot(res_usage)
## Warning: Removed 6 rows containing missing values or values outside the scale range
## (`geom_line()`).

Bottleneck

Bottleneck analysis which confirms that the preparation station has the highest utilization, indicating it is the primary bottleneck in the system.

Bottleneck<- res_usage|>
  group_by(resource)|>
  summarise(avg_utilization= mean(server/capacity, na.rm=TRUE))|>
  arrange(desc(avg_utilization))

print(Bottleneck)
## # A tibble: 3 × 2
##   resource       avg_utilization
##   <chr>                    <dbl>
## 1 prep_station             0.999
## 2 order_station            0.978
## 3 pickup_station           0.571

Scenario

For the scenario I increased the prep staff.

# reset interarrival for second simulation
interarrival <- diff(c(0, arrivalTimes))

env2 <- simmer("BBQ Improved") |>
  add_resource("order_station", capacity = 2) |>
  add_resource("prep_station", capacity = 3) |>
  add_resource("pickup_station", capacity = 1) |>
  add_generator("customer", customerT, arrival_generator) |>
  run(until = sim_time)

Calculated wait time indicate that customers experience increased waiting time due to limited prep capacity and variability in order sizes.

sim_output <- get_mon_arrivals(env)
sim_output2 <- get_mon_arrivals(env2)

# computed waiting time
sim_output$waiting_time <- 
  sim_output$end_time - sim_output$start_time - sim_output$activity_time

sim_output2$waiting_time <- 
  sim_output2$end_time - sim_output2$start_time - sim_output2$activity_time

# converted to numeric so analysis
sim_output$waiting_time <- as.numeric(sim_output$waiting_time)
sim_output2$waiting_time <- as.numeric(sim_output2$waiting_time)

cat("\nOriginal Avg Wait:", mean(sim_output$waiting_time, na.rm = TRUE), "\n")
## 
## Original Avg Wait: 225.4418
cat("Improved Avg Wait:", mean(sim_output2$waiting_time, na.rm = TRUE), "\n")
## Improved Avg Wait: 178.5424
#baseline analysis
summary(sim_output$waiting_time)
##    Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
##    0.00   95.28  217.34  225.44  359.50  484.67
quantile(sim_output$waiting_time, c(0.5, 0.9, 0.95), na.rm = TRUE)
##      50%      90%      95% 
## 217.3406 425.9001 455.0926

Overall, increasing prep station capacity reduced the average waiting time and demonstrated that system performance is highly sensitive to kitchen throughput.

Conclusion

The simulation analysis of the Simio BBQ smoke pit system identified the primary service bottleneck as the food preparation (prep) station. Resource utilization results showed that the prep station consistently operated at the highest capacity compared to the order and pick up stations, leading to queue buildup and increased customer waiting times. This is consistent with the operational characteristics of BBQ service, where long cooking and preparation times constrain throughput. The customer wait time analysis confirmed that the cause of delays was primarily driven by the limited preparation capacity rather than inefficiencies at the ordering or pick up stages. The variability in order sizes amplifies this effect as larger orders require more preparation time and would increase congestion at the prep station.

A scenario analysis was conducted by increasing the capacity of the prep station by adding prep staff. The results showed reduction in average customer waiting time, showing that the system performance is highly sensitive to kitchen staffing and throughput. This suggests that investing in additional kitchen staff or improving preparation efficiency would yield meaningful improvements in customer experience. From a manager’s perspective the findings indicate that staffing decision should be a priority for the preparation stage rather. Increasing capacity may improve service levels but the restaurant would have to also consider the cost trade offs, as over staffing or overproduction could reduce profitability due to the higher labor cost and the potential of wasting food.

Overall the simulation provided a data driven framework for evaluating operational policies and highlighted the importance of balancing resource with demand variability. A future enhancement for the model would be to include food inventory constraints and customer abandonment behavior to better reflect a real world condition.