Restaurant Simulation with SimPy: From Seating to Serving
<p>Restaurants are fascinating systems. Multiple resources. Parallel processes. Variable service times. Customer behaviour. All in one simulation.</p> <h2 id="the-restaurant-model">The Restaurant Model</h2> <p>Components: - <strong>Customers</strong> - Arrive in groups - <strong>Tables</strong> - Limited seating - <strong>Waiters</strong> - Take orders, serve food - <strong>Kitchen</strong> - Prepares meals - <strong>Bill</strong> - Payment process</p> <h2 id="basic-restaurant">Basic Restaurant</h2> <div class="code-block"><pre><span></span><code><span class="kn">import</span><span class="w"> </span><span class="nn">simpy</span> <span class="kn">import</span><span class="w"> </span><span class="nn">random</span> <span class="k">class</span><span class="w"> </span><span class="nc">Restaurant</span><span class="p">:</span> <span class="k">def</span><span class="w"> </span><span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">env</span><span class="p">,</span> <span class="n">num_tables</span><span class="p">,</span> <span class="n">num_waiters</span><span class="p">,</span> <span class="n">num_cooks</span><span class="p">):</span> <span class="bp">self</span><span class="o">.</span><span class="n">env</span> <span class="o">=</span> <span class="n">env</span> <span class="bp">self</span><span class="o">.</span><span class="n">tables</span> <span class="o">=</span> <span class="n">simpy</span><span class="o">.</span><span class="n">Resource</span><span class="p">(</span><span class="n">env</span><span class="p">,</span> <span class="n">capacity</span><span class="o">=</span><span class="n">num_tables</span><span class="p">)</span> <span class="bp">self</span><span class="o">.</span><span class="n">waiters</span> <span class="o">=</span> <span class="n">simpy</span><span class="o">.</span><span class="n">Resource</span><span class="p">(</span><span class="n">env</span><span class="p">,</span> <span class="n">capacity</span><span class="o">=</span><span class="n">num_waiters</span><span class="p">)</span> <span class="bp">self</span><span class="o">.</span><span class="n">kitchen</span> <span class="o">=</span> <span class="n">simpy</span><span class="o">.</span><span class="n">Resource</span><span class="p">(</span><span class="n">env</span><span class="p">,</span> <span class="n">capacity</span><span class="o">=</span><span class="n">num_cooks</span><span class="p">)</span> <span class="bp">self</span><span class="o">.</span><span class="n">stats</span> <span class="o">=</span> <span class="p">[]</span> <span class="k">def</span><span class="w"> </span><span class="nf">customer_group</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">group_id</span><span class="p">,</span> <span class="n">group_size</span><span class="p">):</span> <span class="n">arrival</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">env</span><span class="o">.</span><span class="n">now</span> <span class="c1"># Wait for table</span> <span class="k">with</span> <span class="bp">self</span><span class="o">.</span><span class="n">tables</span><span class="o">.</span><span class="n">request</span><span class="p">()</span> <span class="k">as</span> <span class="n">table</span><span class="p">:</span> <span class="k">yield</span> <span class="n">table</span> <span class="n">seated</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">env</span><span class="o">.</span><span class="n">now</span> <span class="c1"># Waiter takes order</span> <span class="k">with</span> <span class="bp">self</span><span class="o">.</span><span class="n">waiters</span><span class="o">.</span><span class="n">request</span><span class="p">()</span> <span class="k">as</span> <span class="n">waiter</span><span class="p">:</span> <span class="k">yield</span> <span class="n">waiter</span> <span class="k">yield</span> <span class="bp">self</span><span class="o">.</span><span class="n">env</span><span class="o">.</span><span class="n">timeout</span><span class="p">(</span><span class="n">random</span><span class="o">.</span><span class="n">uniform</span><span class="p">(</span><span class="mi">2</span><span class="p">,</span> <span class="mi">5</span><span class="p">))</span> <span class="c1"># Ordering</span> <span class="c1"># Kitchen prepares food</span> <span class="k">with</span> <span class="bp">self</span><span class="o">.</span><span class="n">kitchen</span><span class="o">.</span><span class="n">request</span><span class="p">()</span> <span class="k">as</span> <span class="n">cook</span><span class="p">:</span> <span class="k">yield</span> <span class="n">cook</span> <span class="k">yield</span> <span class="bp">self</span><span class="o">.</span><span class="n">env</span><span class="o">.</span><span class="n">timeout</span><span class="p">(</span><span class="n">random</span><span class="o">.</span><span class="n">uniform</span><span class="p">(</span><span class="mi">10</span><span class="p">,</span> <span class="mi">25</span><span class="p">))</span> <span class="c1"># Cooking</span> <span class="c1"># Waiter serves food</span> <span class="k">with</span> <span class="bp">self</span><span class="o">.</span><span class="n">waiters</span><span class="o">.</span><span class="n">request</span><span class="p">()</span> <span class="k">as</span> <span class="n">waiter</span><span class="p">:</span> <span class="k">yield</span> <span class="n">waiter</span> <span class="k">yield</span> <span class="bp">self</span><span class="o">.</span><span class="n">env</span><span class="o">.</span><span class="n">timeout</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span> <span class="c1"># Serving</span> <span class="c1"># Eating</span> <span class="k">yield</span> <span class="bp">self</span><span class="o">.</span><span class="n">env</span><span class="o">.</span><span class="n">timeout</span><span class="p">(</span><span class="n">random</span><span class="o">.</span><span class="n">uniform</span><span class="p">(</span><span class="mi">20</span><span class="p">,</span> <span class="mi">40</span><span class="p">))</span> <span class="c1"># Waiter brings bill</span> <span class="k">with</span> <span class="bp">self</span><span class="o">.</span><span class="n">waiters</span><span class="o">.</span><span class="n">request</span><span class="p">()</span> <span class="k">as</span> <span class="n">waiter</span><span class="p">:</span> <span class="k">yield</span> <span class="n">waiter</span> <span class="k">yield</span> <span class="bp">self</span><span class="o">.</span><span class="n">env</span><span class="o">.</span><span class="n">timeout</span><span class="p">(</span><span class="n">random</span><span class="o">.</span><span class="n">uniform</span><span class="p">(</span><span class="mi">3</span><span class="p">,</span> <span class="mi">8</span><span class="p">))</span> <span class="c1"># Bill and payment</span> <span class="n">departure</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">env</span><span class="o">.</span><span class="n">now</span> <span class="bp">self</span><span class="o">.</span><span class="n">stats</span><span class="o">.</span><span class="n">append</span><span class="p">({</span> <span class="s1">'group'</span><span class="p">:</span> <span class="n">group_id</span><span class="p">,</span> <span class="s1">'size'</span><span class="p">:</span> <span class="n">group_size</span><span class="p">,</span> <span class="s1">'arrival'</span><span class="p">:</span> <span class="n">arrival</span><span class="p">,</span> <span class="s1">'seated'</span><span class="p">:</span> <span class="n">seated</span><span class="p">,</span> <span class="s1">'departure'</span><span class="p">:</span> <span class="n">departure</span><span class="p">,</span> <span class="s1">'wait_for_table'</span><span class="p">:</span> <span class="n">seated</span> <span class="o">-</span> <span class="n">arrival</span><span class="p">,</span> <span class="s1">'total_time'</span><span class="p">:</span> <span class="n">departure</span> <span class="o">-</span> <span class="n">arrival</span> <span class="p">})</span> <span class="k">def</span><span class="w"> </span><span class="nf">arrivals</span><span class="p">(</span><span class="n">env</span><span class="p">,</span> <span class="n">restaurant</span><span class="p">):</span> <span class="n">group_id</span> <span class="o">=</span> <span class="mi">0</span> <span class="k">while</span> <span class="kc">True</span><span class="p">:</span> <span class="k">yield</span> <span class="n">env</span><span class="o">.</span><span class="n">timeout</span><span class="p">(</span><span class="n">random</span><span class="o">.</span><span class="n">expovariate</span><span class="p">(</span><span class="mi">1</span><span class="o">/</span><span class="mi">10</span><span class="p">))</span> <span class="c1"># ~6 groups/hour</span> <span class="n">group_size</span> <span class="o">=</span> <span class="n">random</span><span class="o">.</span><span class="n">choices</span><span class="p">([</span><span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="mi">3</span><span class="p">,</span> <span class="mi">4</span><span class="p">,</span> <span class="mi">5</span><span class="p">,</span> <span class="mi">6</span><span class="p">],</span> <span class="n">weights</span><span class="o">=</span><span class="p">[</span><span class="mi">10</span><span class="p">,</span> <span class="mi">30</span><span class="p">,</span> <span class="mi">25</span><span class="p">,</span> <span class="mi">20</span><span class="p">,</span> <span class="mi">10</span><span class="p">,</span> <span class="mi">5</span><span class="p">])[</span><span class="mi">0</span><span class="p">]</span> <span class="n">env</span><span class="o">.</span><span class="n">process</span><span class="p">(</span><span class="n">restaurant</span><span class="o">.</span><span class="n">customer_group</span><span class="p">(</span><span class="n">group_id</span><span class="p">,</span> <span class="n">group_size</span><span class="p">))</span> <span class="n">group_id</span> <span class="o">+=</span> <span class="mi">1</span> <span class="n">env</span> <span class="o">=</span> <span class="n">simpy</span><span class="o">.</span><span class="n">Environment</span><span class="p">()</span> <span class="n">restaurant</span> <span class="o">=</span> <span class="n">Restaurant</span><span class="p">(</span><span class="n">env</span><span class="p">,</span> <span class="n">num_tables</span><span class="o">=</span><span class="mi">10</span><span class="p">,</span> <span class="n">num_waiters</span><span class="o">=</span><span class="mi">3</span><span class="p">,</span> <span class="n">num_cooks</span><span class="o">=</span><span class="mi">2</span><span class="p">)</span> <span class="n">env</span><span class="o">.</span><span class="n">process</span><span class="p">(</span><span class="n">arrivals</span><span class="p">(</span><span class="n">env</span><span class="p">,</span> <span class="n">restaurant</span><span class="p">))</span> <span class="n">env</span><span class="o">.</span><span class="n">run</span><span class="p">(</span><span class="n">until</span><span class="o">=</span><span class="mi">240</span><span class="p">)</span> <span class="c1"># 4 hours</span> </code></pre></div> <h2 id="table-management">Table Management</h2> <p>Different table sizes:</p> <div class="code-block"><pre><span></span><code><span class="k">class</span><span class="w"> </span><span class="nc">TableManager</span><span class="p">:</span> <span class="k">def</span><span class="w"> </span><span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">env</span><span class="p">,</span> <span class="n">table_config</span><span class="p">):</span> <span class="w"> </span><span class="sd">"""</span> <span class="sd"> table_config = {2: 5, 4: 8, 6: 3} # 5 two-tops, 8 four-tops, 3 six-tops</span> <span class="sd"> """</span> <span class="bp">self</span><span class="o">.</span><span class="n">env</span> <span class="o">=</span> <span class="n">env</span> <span class="bp">self</span><span class="o">.</span><span class="n">tables</span> <span class="o">=</span> <span class="p">{}</span> <span class="k">for</span> <span class="n">size</span><span class="p">,</span> <span class="n">count</span> <span class="ow">in</span> <span class="n">table_config</span><span class="o">.</span><span class="n">items</span><span class="p">():</span> <span class="bp">self</span><span class="o">.</span><span class="n">tables</span><span class="p">[</span><span class="n">size</span><span class="p">]</span> <span class="o">=</span> <span class="n">simpy</span><span class="o">.</span><span class="n">Resource</span><span class="p">(</span><span class="n">env</span><span class="p">,</span> <span class="n">capacity</span><span class="o">=</span><span class="n">count</span><span class="p">)</span> <span class="k">def</span><span class="w"> </span><span class="nf">get_table</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">group_size</span><span class="p">):</span> <span class="w"> </span><span class="sd">"""Find smallest table that fits the group."""</span> <span class="n">suitable</span> <span class="o">=</span> <span class="p">[</span><span class="n">s</span> <span class="k">for</span> <span class="n">s</span> <span class="ow">in</span> <span class="nb">sorted</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">tables</span><span class="o">.</span><span class="n">keys</span><span class="p">())</span> <span class="k">if</span> <span class="n">s</span> <span class="o">>=</span> <span class="n">group_size</span><span class="p">]</span> <span class="k">if</span> <span class="ow">not</span> <span class="n">suitable</span><span class="p">:</span> <span class="k">return</span> <span class="kc">None</span> <span class="c1"># No table big enough</span> <span class="k">for</span> <span class="n">size</span> <span class="ow">in</span> <span class="n">suitable</span><span class="p">:</span> <span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">tables</span><span class="p">[</span><span class="n">size</span><span class="p">]</span><span class="o">.</span><span class="n">count</span> <span class="o"><</span> <span class="bp">self</span><span class="o">.</span><span class="n">tables</span><span class="p">[</span><span class="n">size</span><span class="p">]</span><span class="o">.</span><span class="n">capacity</span><span class="p">:</span> <span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">tables</span><span class="p">[</span><span class="n">size</span><span class="p">],</span> <span class="n">size</span> <span class="c1"># All suitable tables busy - wait for smallest that fits</span> <span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">tables</span><span class="p">[</span><span class="n">suitable</span><span class="p">[</span><span class="mi">0</span><span class="p">]],</span> <span class="n">suitable</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="k">def</span><span class="w"> </span><span class="nf">customer_group</span><span class="p">(</span><span class="n">env</span><span class="p">,</span> <span class="n">table_manager</span><span class="p">,</span> <span class="n">group_id</span><span class="p">,</span> <span class="n">group_size</span><span class="p">):</span> <span class="n">result</span> <span class="o">=</span> <span class="n">table_manager</span><span class="o">.</span><span class="n">get_table</span><span class="p">(</span><span class="n">group_size</span><span class="p">)</span> <span class="k">if</span> <span class="n">result</span> <span class="ow">is</span> <span class="kc">None</span><span class="p">:</span> <span class="nb">print</span><span class="p">(</span><span class="sa">f</span><span class="s2">"Group </span><span class="si">{</span><span class="n">group_id</span><span class="si">}</span><span class="s2"> (size </span><span class="si">{</span><span class="n">group_size</span><span class="si">}</span><span class="s2">) left - no suitable table"</span><span class="p">)</span> <span class="k">return</span> <span class="n">table_resource</span><span class="p">,</span> <span class="n">table_size</span> <span class="o">=</span> <span class="n">result</span> <span class="k">with</span> <span class="n">table_resource</span><span class="o">.</span><span class="n">request</span><span class="p">()</span> <span class="k">as</span> <span class="n">req</span><span class="p">:</span> <span class="k">yield</span> <span class="n">req</span> <span class="nb">print</span><span class="p">(</span><span class="sa">f</span><span class="s2">"Group </span><span class="si">{</span><span class="n">group_id</span><span class="si">}</span><span class="s2"> seated at </span><span class="si">{</span><span class="n">table_size</span><span class="si">}</span><span class="s2">-top"</span><span class="p">)</span> <span class="k">yield</span> <span class="n">env</span><span class="o">.</span><span class="n">timeout</span><span class="p">(</span><span class="n">random</span><span class="o">.</span><span class="n">uniform</span><span class="p">(</span><span class="mi">30</span><span class="p">,</span> <span class="mi">60</span><span class="p">))</span> <span class="c1"># Dining time</span> </code></pre></div> <h2 id="kitchen-with-multiple-stations">Kitchen with Multiple Stations</h2> <div class="code-block"><pre><span></span><code><span class="k">class</span><span class="w"> </span><span class="nc">Kitchen</span><span class="p">:</span> <span class="k">def</span><span class="w"> </span><span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">env</span><span class="p">,</span> <span class="n">config</span><span class="p">):</span> <span class="bp">self</span><span class="o">.</span><span class="n">env</span> <span class="o">=</span> <span class="n">env</span> <span class="bp">self</span><span class="o">.</span><span class="n">stations</span> <span class="o">=</span> <span class="p">{</span> <span class="s1">'grill'</span><span class="p">:</span> <span class="n">simpy</span><span class="o">.</span><span class="n">Resource</span><span class="p">(</span><span class="n">env</span><span class="p">,</span> <span class="n">capacity</span><span class="o">=</span><span class="n">config</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s1">'grill'</span><span class="p">,</span> <span class="mi">2</span><span class="p">)),</span> <span class="s1">'fryer'</span><span class="p">:</span> <span class="n">simpy</span><span class="o">.</span><span class="n">Resource</span><span class="p">(</span><span class="n">env</span><span class="p">,</span> <span class="n">capacity</span><span class="o">=</span><span class="n">config</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s1">'fryer'</span><span class="p">,</span> <span class="mi">1</span><span class="p">)),</span> <span class="s1">'salad'</span><span class="p">:</span> <span class="n">simpy</span><span class="o">.</span><span class="n">Resource</span><span class="p">(</span><span class="n">env</span><span class="p">,</span> <span class="n">capacity</span><span class="o">=</span><span class="n">config</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s1">'salad'</span><span class="p">,</span> <span class="mi">1</span><span class="p">)),</span> <span class="s1">'dessert'</span><span class="p">:</span> <span class="n">simpy</span><span class="o">.</span><span class="n">Resource</span><span class="p">(</span><span class="n">env</span><span class="p">,</span> <span class="n">capacity</span><span class="o">=</span><span class="n">config</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s1">'dessert'</span><span class="p">,</span> <span class="mi">1</span><span class="p">))</span> <span class="p">}</span> <span class="bp">self</span><span class="o">.</span><span class="n">orders_completed</span> <span class="o">=</span> <span class="mi">0</span> <span class="k">def</span><span class="w"> </span><span class="nf">prepare_order</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">order</span><span class="p">):</span> <span class="w"> </span><span class="sd">"""Prepare all items in an order."""</span> <span class="c1"># Group items by station</span> <span class="n">station_items</span> <span class="o">=</span> <span class="p">{}</span> <span class="k">for</span> <span class="n">item</span> <span class="ow">in</span> <span class="n">order</span><span class="p">[</span><span class="s1">'items'</span><span class="p">]:</span> <span class="n">station</span> <span class="o">=</span> <span class="n">item</span><span class="p">[</span><span class="s1">'station'</span><span class="p">]</span> <span class="k">if</span> <span class="n">station</span> <span class="ow">not</span> <span class="ow">in</span> <span class="n">station_items</span><span class="p">:</span> <span class="n">station_items</span><span class="p">[</span><span class="n">station</span><span class="p">]</span> <span class="o">=</span> <span class="p">[]</span> <span class="n">station_items</span><span class="p">[</span><span class="n">station</span><span class="p">]</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">item</span><span class="p">)</span> <span class="c1"># Prepare at each station (can be parallel across stations)</span> <span class="n">processes</span> <span class="o">=</span> <span class="p">[]</span> <span class="k">for</span> <span class="n">station</span><span class="p">,</span> <span class="n">items</span> <span class="ow">in</span> <span class="n">station_items</span><span class="o">.</span><span class="n">items</span><span class="p">():</span> <span class="n">proc</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">env</span><span class="o">.</span><span class="n">process</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">prepare_at_station</span><span class="p">(</span><span class="n">station</span><span class="p">,</span> <span class="n">items</span><span class="p">))</span> <span class="n">processes</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">proc</span><span class="p">)</span> <span class="c1"># Wait for all stations to complete</span> <span class="k">for</span> <span class="n">proc</span> <span class="ow">in</span> <span class="n">processes</span><span class="p">:</span> <span class="k">yield</span> <span class="n">proc</span> <span class="bp">self</span><span class="o">.</span><span class="n">orders_completed</span> <span class="o">+=</span> <span class="mi">1</span> <span class="k">def</span><span class="w"> </span><span class="nf">prepare_at_station</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">station_name</span><span class="p">,</span> <span class="n">items</span><span class="p">):</span> <span class="k">with</span> <span class="bp">self</span><span class="o">.</span><span class="n">stations</span><span class="p">[</span><span class="n">station_name</span><span class="p">]</span><span class="o">.</span><span class="n">request</span><span class="p">()</span> <span class="k">as</span> <span class="n">req</span><span class="p">:</span> <span class="k">yield</span> <span class="n">req</span> <span class="k">for</span> <span class="n">item</span> <span class="ow">in</span> <span class="n">items</span><span class="p">:</span> <span class="k">yield</span> <span class="bp">self</span><span class="o">.</span><span class="n">env</span><span class="o">.</span><span class="n">timeout</span><span class="p">(</span><span class="n">item</span><span class="p">[</span><span class="s1">'prep_time'</span><span class="p">])</span> </code></pre></div> <h2 id="order-queue">Order Queue</h2> <div class="code-block"><pre><span></span><code><span class="k">class</span><span class="w"> </span><span class="nc">OrderSystem</span><span class="p">:</span> <span class="k">def</span><span class="w"> </span><span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">env</span><span class="p">,</span> <span class="n">kitchen</span><span class="p">):</span> <span class="bp">self</span><span class="o">.</span><span class="n">env</span> <span class="o">=</span> <span class="n">env</span> <span class="bp">self</span><span class="o">.</span><span class="n">kitchen</span> <span class="o">=</span> <span class="n">kitchen</span> <span class="bp">self</span><span class="o">.</span><span class="n">order_queue</span> <span class="o">=</span> <span class="n">simpy</span><span class="o">.</span><span class="n">Store</span><span class="p">(</span><span class="n">env</span><span class="p">)</span> <span class="bp">self</span><span class="o">.</span><span class="n">ready_orders</span> <span class="o">=</span> <span class="n">simpy</span><span class="o">.</span><span class="n">FilterStore</span><span class="p">(</span><span class="n">env</span><span class="p">)</span> <span class="n">env</span><span class="o">.</span><span class="n">process</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">kitchen_process</span><span class="p">())</span> <span class="k">def</span><span class="w"> </span><span class="nf">place_order</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">table_id</span><span class="p">,</span> <span class="n">items</span><span class="p">):</span> <span class="n">order</span> <span class="o">=</span> <span class="p">{</span> <span class="s1">'id'</span><span class="p">:</span> <span class="sa">f</span><span class="s2">"</span><span class="si">{</span><span class="n">table_id</span><span class="si">}</span><span class="s2">_</span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">env</span><span class="o">.</span><span class="n">now</span><span class="si">:</span><span class="s2">.0f</span><span class="si">}</span><span class="s2">"</span><span class="p">,</span> <span class="s1">'table'</span><span class="p">:</span> <span class="n">table_id</span><span class="p">,</span> <span class="s1">'items'</span><span class="p">:</span> <span class="n">items</span><span class="p">,</span> <span class="s1">'placed_at'</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">env</span><span class="o">.</span><span class="n">now</span> <span class="p">}</span> <span class="k">yield</span> <span class="bp">self</span><span class="o">.</span><span class="n">order_queue</span><span class="o">.</span><span class="n">put</span><span class="p">(</span><span class="n">order</span><span class="p">)</span> <span class="k">return</span> <span class="n">order</span><span class="p">[</span><span class="s1">'id'</span><span class="p">]</span> <span class="k">def</span><span class="w"> </span><span class="nf">kitchen_process</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span> <span class="k">while</span> <span class="kc">True</span><span class="p">:</span> <span class="n">order</span> <span class="o">=</span> <span class="k">yield</span> <span class="bp">self</span><span class="o">.</span><span class="n">order_queue</span><span class="o">.</span><span class="n">get</span><span class="p">()</span> <span class="k">yield from</span> <span class="bp">self</span><span class="o">.</span><span class="n">kitchen</span><span class="o">.</span><span class="n">prepare_order</span><span class="p">(</span><span class="n">order</span><span class="p">)</span> <span class="n">order</span><span class="p">[</span><span class="s1">'ready_at'</span><span class="p">]</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">env</span><span class="o">.</span><span class="n">now</span> <span class="k">yield</span> <span class="bp">self</span><span class="o">.</span><span class="n">ready_orders</span><span class="o">.</span><span class="n">put</span><span class="p">(</span><span class="n">order</span><span class="p">)</span> <span class="k">def</span><span class="w"> </span><span class="nf">collect_order</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">order_id</span><span class="p">):</span> <span class="n">order</span> <span class="o">=</span> <span class="k">yield</span> <span class="bp">self</span><span class="o">.</span><span class="n">ready_orders</span><span class="o">.</span><span class="n">get</span><span class="p">(</span> <span class="k">lambda</span> <span class="n">o</span><span class="p">:</span> <span class="n">o</span><span class="p">[</span><span class="s1">'id'</span><span class="p">]</span> <span class="o">==</span> <span class="n">order_id</span> <span class="p">)</span> <span class="k">return</span> <span class="n">order</span> </code></pre></div> <h2 id="complete-restaurant-simulation">Complete Restaurant Simulation</h2> <div class="code-block"><pre><span></span><code><span class="kn">import</span><span class="w"> </span><span class="nn">simpy</span> <span class="kn">import</span><span class="w"> </span><span class="nn">random</span> <span class="kn">import</span><span class="w"> </span><span class="nn">numpy</span><span class="w"> </span><span class="k">as</span><span class="w"> </span><span class="nn">np</span> <span class="k">class</span><span class="w"> </span><span class="nc">FullRestaurantSimulation</span><span class="p">:</span> <span class="k">def</span><span class="w"> </span><span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">env</span><span class="p">,</span> <span class="n">config</span><span class="p">):</span> <span class="bp">self</span><span class="o">.</span><span class="n">env</span> <span class="o">=</span> <span class="n">env</span> <span class="bp">self</span><span class="o">.</span><span class="n">config</span> <span class="o">=</span> <span class="n">config</span> <span class="c1"># Resources</span> <span class="bp">self</span><span class="o">.</span><span class="n">tables</span> <span class="o">=</span> <span class="n">simpy</span><span class="o">.</span><span class="n">Resource</span><span class="p">(</span><span class="n">env</span><span class="p">,</span> <span class="n">capacity</span><span class="o">=</span><span class="n">config</span><span class="p">[</span><span class="s1">'tables'</span><span class="p">])</span> <span class="bp">self</span><span class="o">.</span><span class="n">waiters</span> <span class="o">=</span> <span class="n">simpy</span><span class="o">.</span><span class="n">Resource</span><span class="p">(</span><span class="n">env</span><span class="p">,</span> <span class="n">capacity</span><span class="o">=</span><span class="n">config</span><span class="p">[</span><span class="s1">'waiters'</span><span class="p">])</span> <span class="bp">self</span><span class="o">.</span><span class="n">kitchen</span> <span class="o">=</span> <span class="n">simpy</span><span class="o">.</span><span class="n">Resource</span><span class="p">(</span><span class="n">env</span><span class="p">,</span> <span class="n">capacity</span><span class="o">=</span><span class="n">config</span><span class="p">[</span><span class="s1">'cooks'</span><span class="p">])</span> <span class="c1"># Stats</span> <span class="bp">self</span><span class="o">.</span><span class="n">stats</span> <span class="o">=</span> <span class="p">{</span> <span class="s1">'groups'</span><span class="p">:</span> <span class="p">[],</span> <span class="s1">'turnaways'</span><span class="p">:</span> <span class="mi">0</span> <span class="p">}</span> <span class="k">def</span><span class="w"> </span><span class="nf">customer_group</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">group_id</span><span class="p">,</span> <span class="n">group_size</span><span class="p">):</span> <span class="n">arrival</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">env</span><span class="o">.</span><span class="n">now</span> <span class="c1"># Check if willing to wait</span> <span class="k">if</span> <span class="nb">len</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">tables</span><span class="o">.</span><span class="n">queue</span><span class="p">)</span> <span class="o">></span> <span class="bp">self</span><span class="o">.</span><span class="n">config</span><span class="p">[</span><span class="s1">'max_wait_queue'</span><span class="p">]:</span> <span class="bp">self</span><span class="o">.</span><span class="n">stats</span><span class="p">[</span><span class="s1">'turnaways'</span><span class="p">]</span> <span class="o">+=</span> <span class="mi">1</span> <span class="k">return</span> <span class="n">record</span> <span class="o">=</span> <span class="p">{</span> <span class="s1">'id'</span><span class="p">:</span> <span class="n">group_id</span><span class="p">,</span> <span class="s1">'size'</span><span class="p">:</span> <span class="n">group_size</span><span class="p">,</span> <span class="s1">'arrival'</span><span class="p">:</span> <span class="n">arrival</span> <span class="p">}</span> <span class="c1"># Get table</span> <span class="k">with</span> <span class="bp">self</span><span class="o">.</span><span class="n">tables</span><span class="o">.</span><span class="n">request</span><span class="p">()</span> <span class="k">as</span> <span class="n">table</span><span class="p">:</span> <span class="n">result</span> <span class="o">=</span> <span class="k">yield</span> <span class="n">table</span> <span class="o">|</span> <span class="bp">self</span><span class="o">.</span><span class="n">env</span><span class="o">.</span><span class="n">timeout</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">config</span><span class="p">[</span><span class="s1">'max_wait_time'</span><span class="p">])</span> <span class="k">if</span> <span class="n">table</span> <span class="ow">not</span> <span class="ow">in</span> <span class="n">result</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">stats</span><span class="p">[</span><span class="s1">'turnaways'</span><span class="p">]</span> <span class="o">+=</span> <span class="mi">1</span> <span class="k">return</span> <span class="n">record</span><span class="p">[</span><span class="s1">'seated_at'</span><span class="p">]</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">env</span><span class="o">.</span><span class="n">now</span> <span class="n">record</span><span class="p">[</span><span class="s1">'wait_for_table'</span><span class="p">]</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">env</span><span class="o">.</span><span class="n">now</span> <span class="o">-</span> <span class="n">arrival</span> <span class="c1"># Order</span> <span class="k">with</span> <span class="bp">self</span><span class="o">.</span><span class="n">waiters</span><span class="o">.</span><span class="n">request</span><span class="p">()</span> <span class="k">as</span> <span class="n">waiter</span><span class="p">:</span> <span class="k">yield</span> <span class="n">waiter</span> <span class="k">yield</span> <span class="bp">self</span><span class="o">.</span><span class="n">env</span><span class="o">.</span><span class="n">timeout</span><span class="p">(</span><span class="n">random</span><span class="o">.</span><span class="n">uniform</span><span class="p">(</span><span class="mi">3</span><span class="p">,</span> <span class="mi">6</span><span class="p">))</span> <span class="n">record</span><span class="p">[</span><span class="s1">'ordered_at'</span><span class="p">]</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">env</span><span class="o">.</span><span class="n">now</span> <span class="c1"># Cook</span> <span class="k">with</span> <span class="bp">self</span><span class="o">.</span><span class="n">kitchen</span><span class="o">.</span><span class="n">request</span><span class="p">()</span> <span class="k">as</span> <span class="n">cook</span><span class="p">:</span> <span class="k">yield</span> <span class="n">cook</span> <span class="n">cook_time</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">config</span><span class="p">[</span><span class="s1">'cook_time_base'</span><span class="p">]</span> <span class="o">+</span> <span class="n">group_size</span> <span class="o">*</span> <span class="mi">2</span> <span class="k">yield</span> <span class="bp">self</span><span class="o">.</span><span class="n">env</span><span class="o">.</span><span class="n">timeout</span><span class="p">(</span><span class="n">random</span><span class="o">.</span><span class="n">uniform</span><span class="p">(</span><span class="n">cook_time</span> <span class="o">*</span> <span class="mf">0.8</span><span class="p">,</span> <span class="n">cook_time</span> <span class="o">*</span> <span class="mf">1.2</span><span class="p">))</span> <span class="n">record</span><span class="p">[</span><span class="s1">'food_ready'</span><span class="p">]</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">env</span><span class="o">.</span><span class="n">now</span> <span class="c1"># Serve</span> <span class="k">with</span> <span class="bp">self</span><span class="o">.</span><span class="n">waiters</span><span class="o">.</span><span class="n">request</span><span class="p">()</span> <span class="k">as</span> <span class="n">waiter</span><span class="p">:</span> <span class="k">yield</span> <span class="n">waiter</span> <span class="k">yield</span> <span class="bp">self</span><span class="o">.</span><span class="n">env</span><span class="o">.</span><span class="n">timeout</span><span class="p">(</span><span class="n">random</span><span class="o">.</span><span class="n">uniform</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">))</span> <span class="c1"># Eat</span> <span class="n">eat_time</span> <span class="o">=</span> <span class="n">random</span><span class="o">.</span><span class="n">uniform</span><span class="p">(</span><span class="mi">20</span><span class="p">,</span> <span class="mi">40</span><span class="p">)</span> <span class="k">yield</span> <span class="bp">self</span><span class="o">.</span><span class="n">env</span><span class="o">.</span><span class="n">timeout</span><span class="p">(</span><span class="n">eat_time</span><span class="p">)</span> <span class="c1"># Bill</span> <span class="k">with</span> <span class="bp">self</span><span class="o">.</span><span class="n">waiters</span><span class="o">.</span><span class="n">request</span><span class="p">()</span> <span class="k">as</span> <span class="n">waiter</span><span class="p">:</span> <span class="k">yield</span> <span class="n">waiter</span> <span class="k">yield</span> <span class="bp">self</span><span class="o">.</span><span class="n">env</span><span class="o">.</span><span class="n">timeout</span><span class="p">(</span><span class="n">random</span><span class="o">.</span><span class="n">uniform</span><span class="p">(</span><span class="mi">3</span><span class="p">,</span> <span class="mi">8</span><span class="p">))</span> <span class="n">record</span><span class="p">[</span><span class="s1">'departure'</span><span class="p">]</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">env</span><span class="o">.</span><span class="n">now</span> <span class="n">record</span><span class="p">[</span><span class="s1">'total_time'</span><span class="p">]</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">env</span><span class="o">.</span><span class="n">now</span> <span class="o">-</span> <span class="n">arrival</span> <span class="bp">self</span><span class="o">.</span><span class="n">stats</span><span class="p">[</span><span class="s1">'groups'</span><span class="p">]</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">record</span><span class="p">)</span> <span class="k">def</span><span class="w"> </span><span class="nf">arrivals</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span> <span class="n">group_id</span> <span class="o">=</span> <span class="mi">0</span> <span class="k">while</span> <span class="kc">True</span><span class="p">:</span> <span class="c1"># Time-varying arrivals</span> <span class="n">hour</span> <span class="o">=</span> <span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">env</span><span class="o">.</span><span class="n">now</span> <span class="o">/</span> <span class="mi">60</span><span class="p">)</span> <span class="o">%</span> <span class="mi">24</span> <span class="k">if</span> <span class="mi">12</span> <span class="o"><=</span> <span class="n">hour</span> <span class="o"><=</span> <span class="mi">14</span> <span class="ow">or</span> <span class="mi">18</span> <span class="o"><=</span> <span class="n">hour</span> <span class="o"><=</span> <span class="mi">21</span><span class="p">:</span> <span class="n">rate</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">config</span><span class="p">[</span><span class="s1">'peak_rate'</span><span class="p">]</span> <span class="k">else</span><span class="p">:</span> <span class="n">rate</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">config</span><span class="p">[</span><span class="s1">'off_peak_rate'</span><span class="p">]</span> <span class="k">yield</span> <span class="bp">self</span><span class="o">.</span><span class="n">env</span><span class="o">.</span><span class="n">timeout</span><span class="p">(</span><span class="n">random</span><span class="o">.</span><span class="n">expovariate</span><span class="p">(</span><span class="n">rate</span><span class="p">))</span> <span class="c1"># Group size distribution</span> <span class="n">sizes</span> <span class="o">=</span> <span class="p">[</span><span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="mi">3</span><span class="p">,</span> <span class="mi">4</span><span class="p">,</span> <span class="mi">5</span><span class="p">,</span> <span class="mi">6</span><span class="p">]</span> <span class="n">probs</span> <span class="o">=</span> <span class="p">[</span><span class="mf">0.10</span><span class="p">,</span> <span class="mf">0.35</span><span class="p">,</span> <span class="mf">0.25</span><span class="p">,</span> <span class="mf">0.20</span><span class="p">,</span> <span class="mf">0.07</span><span class="p">,</span> <span class="mf">0.03</span><span class="p">]</span> <span class="n">group_size</span> <span class="o">=</span> <span class="n">random</span><span class="o">.</span><span class="n">choices</span><span class="p">(</span><span class="n">sizes</span><span class="p">,</span> <span class="n">weights</span><span class="o">=</span><span class="n">probs</span><span class="p">)[</span><span class="mi">0</span><span class="p">]</span> <span class="bp">self</span><span class="o">.</span><span class="n">env</span><span class="o">.</span><span class="n">process</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">customer_group</span><span class="p">(</span><span class="n">group_id</span><span class="p">,</span> <span class="n">group_size</span><span class="p">))</span> <span class="n">group_id</span> <span class="o">+=</span> <span class="mi">1</span> <span class="k">def</span><span class="w"> </span><span class="nf">run</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">duration</span><span class="p">):</span> <span class="bp">self</span><span class="o">.</span><span class="n">env</span><span class="o">.</span><span class="n">process</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">arrivals</span><span class="p">())</span> <span class="bp">self</span><span class="o">.</span><span class="n">env</span><span class="o">.</span><span class="n">run</span><span class="p">(</span><span class="n">until</span><span class="o">=</span><span class="n">duration</span><span class="p">)</span> <span class="k">def</span><span class="w"> </span><span class="nf">report</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span> <span class="n">groups</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">stats</span><span class="p">[</span><span class="s1">'groups'</span><span class="p">]</span> <span class="nb">print</span><span class="p">(</span><span class="s2">"</span><span class="se">\n</span><span class="s2">=== Restaurant Report ==="</span><span class="p">)</span> <span class="nb">print</span><span class="p">(</span><span class="sa">f</span><span class="s2">"Groups served: </span><span class="si">{</span><span class="nb">len</span><span class="p">(</span><span class="n">groups</span><span class="p">)</span><span class="si">}</span><span class="s2">"</span><span class="p">)</span> <span class="nb">print</span><span class="p">(</span><span class="sa">f</span><span class="s2">"Groups turned away: </span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">stats</span><span class="p">[</span><span class="s1">'turnaways'</span><span class="p">]</span><span class="si">}</span><span class="s2">"</span><span class="p">)</span> <span class="k">if</span> <span class="n">groups</span><span class="p">:</span> <span class="n">waits</span> <span class="o">=</span> <span class="p">[</span><span class="n">g</span><span class="p">[</span><span class="s1">'wait_for_table'</span><span class="p">]</span> <span class="k">for</span> <span class="n">g</span> <span class="ow">in</span> <span class="n">groups</span><span class="p">]</span> <span class="n">totals</span> <span class="o">=</span> <span class="p">[</span><span class="n">g</span><span class="p">[</span><span class="s1">'total_time'</span><span class="p">]</span> <span class="k">for</span> <span class="n">g</span> <span class="ow">in</span> <span class="n">groups</span><span class="p">]</span> <span class="nb">print</span><span class="p">(</span><span class="sa">f</span><span class="s2">"</span><span class="se">\n</span><span class="s2">Wait for table:"</span><span class="p">)</span> <span class="nb">print</span><span class="p">(</span><span class="sa">f</span><span class="s2">" Mean: </span><span class="si">{</span><span class="n">np</span><span class="o">.</span><span class="n">mean</span><span class="p">(</span><span class="n">waits</span><span class="p">)</span><span class="si">:</span><span class="s2">.1f</span><span class="si">}</span><span class="s2"> min"</span><span class="p">)</span> <span class="nb">print</span><span class="p">(</span><span class="sa">f</span><span class="s2">" Max: </span><span class="si">{</span><span class="nb">max</span><span class="p">(</span><span class="n">waits</span><span class="p">)</span><span class="si">:</span><span class="s2">.1f</span><span class="si">}</span><span class="s2"> min"</span><span class="p">)</span> <span class="nb">print</span><span class="p">(</span><span class="sa">f</span><span class="s2">"</span><span class="se">\n</span><span class="s2">Total time in restaurant:"</span><span class="p">)</span> <span class="nb">print</span><span class="p">(</span><span class="sa">f</span><span class="s2">" Mean: </span><span class="si">{</span><span class="n">np</span><span class="o">.</span><span class="n">mean</span><span class="p">(</span><span class="n">totals</span><span class="p">)</span><span class="si">:</span><span class="s2">.1f</span><span class="si">}</span><span class="s2"> min"</span><span class="p">)</span> <span class="c1"># Table turnover</span> <span class="n">turnover</span> <span class="o">=</span> <span class="nb">len</span><span class="p">(</span><span class="n">groups</span><span class="p">)</span> <span class="o">/</span> <span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">config</span><span class="p">[</span><span class="s1">'tables'</span><span class="p">]</span> <span class="o">*</span> <span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">env</span><span class="o">.</span><span class="n">now</span> <span class="o">/</span> <span class="mi">60</span><span class="p">))</span> <span class="nb">print</span><span class="p">(</span><span class="sa">f</span><span class="s2">"</span><span class="se">\n</span><span class="s2">Table turnover: </span><span class="si">{</span><span class="n">turnover</span><span class="si">:</span><span class="s2">.2f</span><span class="si">}</span><span class="s2"> per hour"</span><span class="p">)</span> <span class="c1"># Config</span> <span class="n">config</span> <span class="o">=</span> <span class="p">{</span> <span class="s1">'tables'</span><span class="p">:</span> <span class="mi">15</span><span class="p">,</span> <span class="s1">'waiters'</span><span class="p">:</span> <span class="mi">4</span><span class="p">,</span> <span class="s1">'cooks'</span><span class="p">:</span> <span class="mi">3</span><span class="p">,</span> <span class="s1">'peak_rate'</span><span class="p">:</span> <span class="mf">0.2</span><span class="p">,</span> <span class="c1"># Groups per minute</span> <span class="s1">'off_peak_rate'</span><span class="p">:</span> <span class="mf">0.05</span><span class="p">,</span> <span class="s1">'max_wait_queue'</span><span class="p">:</span> <span class="mi">5</span><span class="p">,</span> <span class="s1">'max_wait_time'</span><span class="p">:</span> <span class="mi">15</span><span class="p">,</span> <span class="s1">'cook_time_base'</span><span class="p">:</span> <span class="mi">12</span> <span class="p">}</span> <span class="n">random</span><span class="o">.</span><span class="n">seed</span><span class="p">(</span><span class="mi">42</span><span class="p">)</span> <span class="n">env</span> <span class="o">=</span> <span class="n">simpy</span><span class="o">.</span><span class="n">Environment</span><span class="p">()</span> <span class="n">sim</span> <span class="o">=</span> <span class="n">FullRestaurantSimulation</span><span class="p">(</span><span class="n">env</span><span class="p">,</span> <span class="n">config</span><span class="p">)</span> <span class="n">sim</span><span class="o">.</span><span class="n">run</span><span class="p">(</span><span class="n">duration</span><span class="o">=</span><span class="mi">240</span><span class="p">)</span> <span class="c1"># 4 hours</span> <span class="n">sim</span><span class="o">.</span><span class="n">report</span><span class="p">()</span> </code></pre></div> <h2 id="summary">Summary</h2> <p>Restaurant simulation teaches: - Multiple resource types - Sequential processes with parallel elements - Customer behaviour and patience - Time-varying demand - Table management optimisation</p> <p>Every restaurant is a system. Simulate yours.</p> <h2 id="next-steps">Next Steps</h2> <ul> <li><a href="/blog_posts/simpy-bank-queue-simulation.html">Bank Queue Simulation</a></li> <li><a href="/blog_posts/simpy-hospital-simulation.html">Hospital Simulation</a></li> <li><a href="/blog_posts/simpy-call-center-simulation.html">Call Centre Simulation</a></li> </ul>Build Professional Simulations
Break free from commercial software and learn how to build powerful, industry-standard simulations in Python. The Complete Simulation in Python with SimPy Bootcamp gives you everything you need.
Explore the Bootcamp