Supply Chain Simulation with SimPy: From Supplier to Customer
<p>Supply chains are networks of queues, buffers, and delays. Orders flow one way. Goods flow the other. Simulation reveals where they get stuck.</p> <h2 id="the-supply-chain-model">The Supply Chain Model</h2> <p>Key elements: - <strong>Suppliers</strong> - Source of materials - <strong>Warehouses</strong> - Storage buffers - <strong>Manufacturing</strong> - Transformation - <strong>Distribution</strong> - Delivery to customers - <strong>Demand</strong> - Customer orders</p> <h2 id="basic-inventory-model">Basic Inventory Model</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">Warehouse</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">name</span><span class="p">,</span> <span class="n">capacity</span><span class="p">,</span> <span class="n">reorder_point</span><span class="p">,</span> <span class="n">order_quantity</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">name</span> <span class="o">=</span> <span class="n">name</span> <span class="bp">self</span><span class="o">.</span><span class="n">inventory</span> <span class="o">=</span> <span class="n">simpy</span><span class="o">.</span><span class="n">Container</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">capacity</span><span class="p">,</span> <span class="n">init</span><span class="o">=</span><span class="n">capacity</span><span class="o">//</span><span class="mi">2</span><span class="p">)</span> <span class="bp">self</span><span class="o">.</span><span class="n">reorder_point</span> <span class="o">=</span> <span class="n">reorder_point</span> <span class="bp">self</span><span class="o">.</span><span class="n">order_quantity</span> <span class="o">=</span> <span class="n">order_quantity</span> <span class="bp">self</span><span class="o">.</span><span class="n">orders_placed</span> <span class="o">=</span> <span class="mi">0</span> <span class="bp">self</span><span class="o">.</span><span class="n">stockouts</span> <span class="o">=</span> <span class="mi">0</span> <span class="k">def</span><span class="w"> </span><span class="nf">fulfill_demand</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">quantity</span><span class="p">):</span> <span class="w"> </span><span class="sd">"""Try to fulfill demand from inventory."""</span> <span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">inventory</span><span class="o">.</span><span class="n">level</span> <span class="o">>=</span> <span class="n">quantity</span><span class="p">:</span> <span class="k">yield</span> <span class="bp">self</span><span class="o">.</span><span class="n">inventory</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="n">quantity</span><span class="p">)</span> <span class="k">return</span> <span class="kc">True</span> <span class="k">else</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">stockouts</span> <span class="o">+=</span> <span class="mi">1</span> <span class="k">return</span> <span class="kc">False</span> <span class="k">def</span><span class="w"> </span><span class="nf">receive_shipment</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">quantity</span><span class="p">):</span> <span class="w"> </span><span class="sd">"""Receive goods into inventory."""</span> <span class="k">yield</span> <span class="bp">self</span><span class="o">.</span><span class="n">inventory</span><span class="o">.</span><span class="n">put</span><span class="p">(</span><span class="n">quantity</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="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">name</span><span class="si">}</span><span class="s2">: Received </span><span class="si">{</span><span class="n">quantity</span><span class="si">}</span><span class="s2">, level now </span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">inventory</span><span class="o">.</span><span class="n">level</span><span class="si">}</span><span class="s2">"</span><span class="p">)</span> <span class="k">def</span><span class="w"> </span><span class="nf">reorder_check</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">lead_time</span><span class="p">):</span> <span class="w"> </span><span class="sd">"""Check and reorder when inventory is low."""</span> <span class="k">while</span> <span class="kc">True</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="mi">1</span><span class="p">)</span> <span class="c1"># Check daily</span> <span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">inventory</span><span class="o">.</span><span class="n">level</span> <span class="o"><=</span> <span class="bp">self</span><span class="o">.</span><span class="n">reorder_point</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">orders_placed</span> <span class="o">+=</span> <span class="mi">1</span> <span class="nb">print</span><span class="p">(</span><span class="sa">f</span><span class="s2">"</span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">name</span><span class="si">}</span><span class="s2">: Ordering </span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">order_quantity</span><span class="si">}</span><span class="s2"> at </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">"</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">lead_time</span><span class="p">)</span> <span class="k">yield from</span> <span class="bp">self</span><span class="o">.</span><span class="n">receive_shipment</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">order_quantity</span><span class="p">)</span> </code></pre></div> <h2 id="multi-echelon-supply-chain">Multi-Echelon Supply Chain</h2> <div class="code-block"><pre><span></span><code><span class="k">class</span><span class="w"> </span><span class="nc">SupplyChainNode</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">name</span><span class="p">,</span> <span class="n">capacity</span><span class="p">,</span> <span class="n">upstream</span><span class="o">=</span><span class="kc">None</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">name</span> <span class="o">=</span> <span class="n">name</span> <span class="bp">self</span><span class="o">.</span><span class="n">inventory</span> <span class="o">=</span> <span class="n">simpy</span><span class="o">.</span><span class="n">Container</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">capacity</span><span class="p">,</span> <span class="n">init</span><span class="o">=</span><span class="n">capacity</span><span class="o">//</span><span class="mi">2</span><span class="p">)</span> <span class="bp">self</span><span class="o">.</span><span class="n">upstream</span> <span class="o">=</span> <span class="n">upstream</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">'fulfilled'</span><span class="p">:</span> <span class="mi">0</span><span class="p">,</span> <span class="s1">'stockouts'</span><span class="p">:</span> <span class="mi">0</span><span class="p">,</span> <span class="s1">'orders_placed'</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">request_material</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">quantity</span><span class="p">,</span> <span class="n">lead_time</span><span class="p">):</span> <span class="w"> </span><span class="sd">"""Request material from upstream."""</span> <span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">upstream</span><span class="p">:</span> <span class="c1"># Place order with upstream</span> <span class="bp">self</span><span class="o">.</span><span class="n">stats</span><span class="p">[</span><span class="s1">'orders_placed'</span><span class="p">]</span> <span class="o">+=</span> <span class="mi">1</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">lead_time</span><span class="p">)</span> <span class="n">success</span> <span class="o">=</span> <span class="k">yield from</span> <span class="bp">self</span><span class="o">.</span><span class="n">upstream</span><span class="o">.</span><span class="n">fulfill_order</span><span class="p">(</span><span class="n">quantity</span><span class="p">)</span> <span class="k">if</span> <span class="n">success</span><span class="p">:</span> <span class="k">yield</span> <span class="bp">self</span><span class="o">.</span><span class="n">inventory</span><span class="o">.</span><span class="n">put</span><span class="p">(</span><span class="n">quantity</span><span class="p">)</span> <span class="k">else</span><span class="p">:</span> <span class="c1"># We are the source - unlimited supply</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">lead_time</span><span class="p">)</span> <span class="k">yield</span> <span class="bp">self</span><span class="o">.</span><span class="n">inventory</span><span class="o">.</span><span class="n">put</span><span class="p">(</span><span class="n">quantity</span><span class="p">)</span> <span class="k">def</span><span class="w"> </span><span class="nf">fulfill_order</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">quantity</span><span class="p">):</span> <span class="w"> </span><span class="sd">"""Fulfill order from downstream."""</span> <span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">inventory</span><span class="o">.</span><span class="n">level</span> <span class="o">>=</span> <span class="n">quantity</span><span class="p">:</span> <span class="k">yield</span> <span class="bp">self</span><span class="o">.</span><span class="n">inventory</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="n">quantity</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">'fulfilled'</span><span class="p">]</span> <span class="o">+=</span> <span class="mi">1</span> <span class="k">return</span> <span class="kc">True</span> <span class="k">else</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">'stockouts'</span><span class="p">]</span> <span class="o">+=</span> <span class="mi">1</span> <span class="k">return</span> <span class="kc">False</span> <span class="c1"># Create chain: Supplier -> DC -> Store</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">supplier</span> <span class="o">=</span> <span class="n">SupplyChainNode</span><span class="p">(</span><span class="n">env</span><span class="p">,</span> <span class="s2">"Supplier"</span><span class="p">,</span> <span class="n">capacity</span><span class="o">=</span><span class="mi">1000</span><span class="p">,</span> <span class="n">upstream</span><span class="o">=</span><span class="kc">None</span><span class="p">)</span> <span class="n">dc</span> <span class="o">=</span> <span class="n">SupplyChainNode</span><span class="p">(</span><span class="n">env</span><span class="p">,</span> <span class="s2">"DC"</span><span class="p">,</span> <span class="n">capacity</span><span class="o">=</span><span class="mi">500</span><span class="p">,</span> <span class="n">upstream</span><span class="o">=</span><span class="n">supplier</span><span class="p">)</span> <span class="n">store</span> <span class="o">=</span> <span class="n">SupplyChainNode</span><span class="p">(</span><span class="n">env</span><span class="p">,</span> <span class="s2">"Store"</span><span class="p">,</span> <span class="n">capacity</span><span class="o">=</span><span class="mi">100</span><span class="p">,</span> <span class="n">upstream</span><span class="o">=</span><span class="n">dc</span><span class="p">)</span> </code></pre></div> <h2 id="order-fulfilment">Order Fulfilment</h2> <div class="code-block"><pre><span></span><code><span class="k">class</span><span class="w"> </span><span class="nc">OrderManager</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">warehouse</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">warehouse</span> <span class="o">=</span> <span class="n">warehouse</span> <span class="bp">self</span><span class="o">.</span><span class="n">pending_orders</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">completed_orders</span> <span class="o">=</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">order_id</span><span class="p">,</span> <span class="n">quantity</span><span class="p">,</span> <span class="n">customer</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="n">order_id</span><span class="p">,</span> <span class="s1">'quantity'</span><span class="p">:</span> <span class="n">quantity</span><span class="p">,</span> <span class="s1">'customer'</span><span class="p">:</span> <span class="n">customer</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">pending_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">process_orders</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">pending_orders</span><span class="o">.</span><span class="n">get</span><span class="p">()</span> <span class="c1"># Check inventory</span> <span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">warehouse</span><span class="o">.</span><span class="n">inventory</span><span class="o">.</span><span class="n">level</span> <span class="o">>=</span> <span class="n">order</span><span class="p">[</span><span class="s1">'quantity'</span><span class="p">]:</span> <span class="k">yield</span> <span class="bp">self</span><span class="o">.</span><span class="n">warehouse</span><span class="o">.</span><span class="n">inventory</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="n">order</span><span class="p">[</span><span class="s1">'quantity'</span><span class="p">])</span> <span class="n">order</span><span class="p">[</span><span class="s1">'fulfilled_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">order</span><span class="p">[</span><span class="s1">'status'</span><span class="p">]</span> <span class="o">=</span> <span class="s1">'fulfilled'</span> <span class="k">else</span><span class="p">:</span> <span class="n">order</span><span class="p">[</span><span class="s1">'status'</span><span class="p">]</span> <span class="o">=</span> <span class="s1">'backordered'</span> <span class="c1"># Wait for restock then retry</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">5</span><span class="p">)</span> <span class="k">continue</span> <span class="bp">self</span><span class="o">.</span><span class="n">completed_orders</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">order</span><span class="p">)</span> <span class="nb">print</span><span class="p">(</span><span class="sa">f</span><span class="s2">"Order </span><span class="si">{</span><span class="n">order</span><span class="p">[</span><span class="s1">'id'</span><span class="p">]</span><span class="si">}</span><span class="s2"> fulfilled at </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">"</span><span class="p">)</span> </code></pre></div> <h2 id="transportation-and-delivery">Transportation and Delivery</h2> <div class="code-block"><pre><span></span><code><span class="k">class</span><span class="w"> </span><span class="nc">DeliveryNetwork</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_trucks</span><span class="p">,</span> <span class="n">warehouse</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">trucks</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_trucks</span><span class="p">)</span> <span class="bp">self</span><span class="o">.</span><span class="n">warehouse</span> <span class="o">=</span> <span class="n">warehouse</span> <span class="bp">self</span><span class="o">.</span><span class="n">deliveries</span> <span class="o">=</span> <span class="p">[]</span> <span class="k">def</span><span class="w"> </span><span class="nf">delivery</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">destination</span><span class="p">,</span> <span class="n">quantity</span><span class="p">):</span> <span class="w"> </span><span class="sd">"""Pick, load, deliver."""</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 truck</span> <span class="k">with</span> <span class="bp">self</span><span class="o">.</span><span class="n">trucks</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="c1"># Pick from warehouse</span> <span class="k">yield</span> <span class="bp">self</span><span class="o">.</span><span class="n">warehouse</span><span class="o">.</span><span class="n">inventory</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="n">quantity</span><span class="p">)</span> <span class="c1"># Load time</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">30</span><span class="p">))</span> <span class="c1"># Transit time (based on destination)</span> <span class="n">transit</span> <span class="o">=</span> <span class="n">destination</span> <span class="o">*</span> <span class="mf">0.5</span> <span class="c1"># 0.5 hours per km</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">transit</span><span class="p">)</span> <span class="bp">self</span><span class="o">.</span><span class="n">deliveries</span><span class="o">.</span><span class="n">append</span><span class="p">({</span> <span class="s1">'order_id'</span><span class="p">:</span> <span class="n">order_id</span><span class="p">,</span> <span class="s1">'lead_time'</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="n">arrival</span> <span class="p">})</span> <span class="c1"># Return journey (empty)</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">transit</span><span class="p">)</span> <span class="k">def</span><span class="w"> </span><span class="nf">stats</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span> <span class="n">times</span> <span class="o">=</span> <span class="p">[</span><span class="n">d</span><span class="p">[</span><span class="s1">'lead_time'</span><span class="p">]</span> <span class="k">for</span> <span class="n">d</span> <span class="ow">in</span> <span class="bp">self</span><span class="o">.</span><span class="n">deliveries</span><span class="p">]</span> <span class="k">return</span> <span class="p">{</span> <span class="s1">'deliveries'</span><span class="p">:</span> <span class="nb">len</span><span class="p">(</span><span class="n">times</span><span class="p">),</span> <span class="s1">'avg_lead_time'</span><span class="p">:</span> <span class="nb">sum</span><span class="p">(</span><span class="n">times</span><span class="p">)</span> <span class="o">/</span> <span class="nb">len</span><span class="p">(</span><span class="n">times</span><span class="p">)</span> <span class="k">if</span> <span class="n">times</span> <span class="k">else</span> <span class="mi">0</span> <span class="p">}</span> </code></pre></div> <h2 id="demand-forecasting-impact">Demand Forecasting Impact</h2> <div class="code-block"><pre><span></span><code><span class="k">def</span><span class="w"> </span><span class="nf">demand_generator</span><span class="p">(</span><span class="n">env</span><span class="p">,</span> <span class="n">order_manager</span><span class="p">,</span> <span class="n">demand_pattern</span><span class="p">):</span> <span class="w"> </span><span class="sd">"""Generate customer demand."""</span> <span class="n">order_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"># Demand varies by day of week</span> <span class="n">day</span> <span class="o">=</span> <span class="nb">int</span><span class="p">(</span><span class="n">env</span><span class="o">.</span><span class="n">now</span> <span class="o">/</span> <span class="mi">24</span><span class="p">)</span> <span class="o">%</span> <span class="mi">7</span> <span class="n">daily_demand</span> <span class="o">=</span> <span class="n">demand_pattern</span><span class="p">[</span><span class="n">day</span><span class="p">]</span> <span class="n">quantity</span> <span class="o">=</span> <span class="nb">max</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="nb">int</span><span class="p">(</span><span class="n">random</span><span class="o">.</span><span class="n">gauss</span><span class="p">(</span><span class="n">daily_demand</span><span class="p">,</span> <span class="n">daily_demand</span> <span class="o">*</span> <span class="mf">0.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">order_manager</span><span class="o">.</span><span class="n">place_order</span><span class="p">(</span><span class="n">order_id</span><span class="p">,</span> <span class="n">quantity</span><span class="p">,</span> <span class="sa">f</span><span class="s2">"Customer"</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">4</span><span class="p">))</span> <span class="c1"># ~4 hours between orders</span> <span class="n">order_id</span> <span class="o">+=</span> <span class="mi">1</span> <span class="c1"># Weekly pattern (Mon-Sun)</span> <span class="n">demand_pattern</span> <span class="o">=</span> <span class="p">[</span><span class="mi">100</span><span class="p">,</span> <span class="mi">110</span><span class="p">,</span> <span class="mi">120</span><span class="p">,</span> <span class="mi">115</span><span class="p">,</span> <span class="mi">130</span><span class="p">,</span> <span class="mi">80</span><span class="p">,</span> <span class="mi">60</span><span class="p">]</span> </code></pre></div> <h2 id="complete-supply-chain-example">Complete Supply Chain Example</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">SupplyChainSimulation</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"># Create nodes</span> <span class="bp">self</span><span class="o">.</span><span class="n">supplier</span> <span class="o">=</span> <span class="n">simpy</span><span class="o">.</span><span class="n">Container</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="nb">float</span><span class="p">(</span><span class="s1">'inf'</span><span class="p">),</span> <span class="n">init</span><span class="o">=</span><span class="nb">float</span><span class="p">(</span><span class="s1">'inf'</span><span class="p">))</span> <span class="bp">self</span><span class="o">.</span><span class="n">dc</span> <span class="o">=</span> <span class="n">simpy</span><span class="o">.</span><span class="n">Container</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">'dc_capacity'</span><span class="p">],</span> <span class="n">init</span><span class="o">=</span><span class="n">config</span><span class="p">[</span><span class="s1">'dc_initial'</span><span class="p">])</span> <span class="bp">self</span><span class="o">.</span><span class="n">store</span> <span class="o">=</span> <span class="n">simpy</span><span class="o">.</span><span class="n">Container</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">'store_capacity'</span><span class="p">],</span> <span class="n">init</span><span class="o">=</span><span class="n">config</span><span class="p">[</span><span class="s1">'store_initial'</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="s1">'sales'</span><span class="p">:</span> <span class="mi">0</span><span class="p">,</span> <span class="s1">'stockouts'</span><span class="p">:</span> <span class="mi">0</span><span class="p">,</span> <span class="s1">'inventory_levels'</span><span class="p">:</span> <span class="p">[],</span> <span class="s1">'dc_orders'</span><span class="p">:</span> <span class="mi">0</span><span class="p">,</span> <span class="s1">'supplier_orders'</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_demand</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span> <span class="w"> </span><span class="sd">"""Generate customer demand at store."""</span> <span class="k">while</span> <span class="kc">True</span><span class="p">:</span> <span class="c1"># Demand arrives</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="bp">self</span><span class="o">.</span><span class="n">config</span><span class="p">[</span><span class="s1">'demand_rate'</span><span class="p">]))</span> <span class="n">demand</span> <span class="o">=</span> <span class="n">random</span><span class="o">.</span><span class="n">randint</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="mi">5</span><span class="p">)</span> <span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">store</span><span class="o">.</span><span class="n">level</span> <span class="o">>=</span> <span class="n">demand</span><span class="p">:</span> <span class="k">yield</span> <span class="bp">self</span><span class="o">.</span><span class="n">store</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="n">demand</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">'sales'</span><span class="p">]</span> <span class="o">+=</span> <span class="n">demand</span> <span class="k">else</span><span class="p">:</span> <span class="c1"># Partial fulfillment</span> <span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">store</span><span class="o">.</span><span class="n">level</span> <span class="o">></span> <span class="mi">0</span><span class="p">:</span> <span class="n">fulfilled</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">store</span><span class="o">.</span><span class="n">level</span> <span class="k">yield</span> <span class="bp">self</span><span class="o">.</span><span class="n">store</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="n">fulfilled</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">'sales'</span><span class="p">]</span> <span class="o">+=</span> <span class="n">fulfilled</span> <span class="bp">self</span><span class="o">.</span><span class="n">stats</span><span class="p">[</span><span class="s1">'stockouts'</span><span class="p">]</span> <span class="o">+=</span> <span class="mi">1</span> <span class="k">def</span><span class="w"> </span><span class="nf">store_reorder</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span> <span class="w"> </span><span class="sd">"""Store reorders from DC."""</span> <span class="k">while</span> <span class="kc">True</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="mi">1</span><span class="p">)</span> <span class="c1"># Daily check</span> <span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">store</span><span class="o">.</span><span class="n">level</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">'store_reorder_point'</span><span class="p">]:</span> <span class="n">order_qty</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">'store_order_qty'</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">'dc_orders'</span><span class="p">]</span> <span class="o">+=</span> <span class="mi">1</span> <span class="c1"># Check DC inventory</span> <span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">dc</span><span class="o">.</span><span class="n">level</span> <span class="o">>=</span> <span class="n">order_qty</span><span class="p">:</span> <span class="k">yield</span> <span class="bp">self</span><span class="o">.</span><span class="n">dc</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="n">order_qty</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="bp">self</span><span class="o">.</span><span class="n">config</span><span class="p">[</span><span class="s1">'dc_to_store_lead'</span><span class="p">])</span> <span class="k">yield</span> <span class="bp">self</span><span class="o">.</span><span class="n">store</span><span class="o">.</span><span class="n">put</span><span class="p">(</span><span class="n">order_qty</span><span class="p">)</span> <span class="k">def</span><span class="w"> </span><span class="nf">dc_reorder</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span> <span class="w"> </span><span class="sd">"""DC reorders from supplier."""</span> <span class="k">while</span> <span class="kc">True</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="mi">1</span><span class="p">)</span> <span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">dc</span><span class="o">.</span><span class="n">level</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">'dc_reorder_point'</span><span class="p">]:</span> <span class="n">order_qty</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">'dc_order_qty'</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">'supplier_orders'</span><span class="p">]</span> <span class="o">+=</span> <span class="mi">1</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="bp">self</span><span class="o">.</span><span class="n">config</span><span class="p">[</span><span class="s1">'supplier_lead_time'</span><span class="p">])</span> <span class="k">yield</span> <span class="bp">self</span><span class="o">.</span><span class="n">dc</span><span class="o">.</span><span class="n">put</span><span class="p">(</span><span class="n">order_qty</span><span class="p">)</span> <span class="k">def</span><span class="w"> </span><span class="nf">monitor</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">interval</span><span class="o">=</span><span class="mi">1</span><span class="p">):</span> <span class="w"> </span><span class="sd">"""Track inventory levels."""</span> <span class="k">while</span> <span class="kc">True</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">'inventory_levels'</span><span class="p">]</span><span class="o">.</span><span class="n">append</span><span class="p">({</span> <span class="s1">'time'</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="s1">'store'</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">store</span><span class="o">.</span><span class="n">level</span><span class="p">,</span> <span class="s1">'dc'</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">dc</span><span class="o">.</span><span class="n">level</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">interval</span><span class="p">)</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">customer_demand</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">store_reorder</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">dc_reorder</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">monitor</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="nb">print</span><span class="p">(</span><span class="s2">"</span><span class="se">\n</span><span class="s2">=== Supply Chain Report ==="</span><span class="p">)</span> <span class="nb">print</span><span class="p">(</span><span class="sa">f</span><span class="s2">"Duration: </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">"</span><span class="p">)</span> <span class="nb">print</span><span class="p">(</span><span class="sa">f</span><span class="s2">"Total sales: </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">'sales'</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">"Stockouts: </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">'stockouts'</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">"DC orders: </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">'dc_orders'</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">"Supplier orders: </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">'supplier_orders'</span><span class="p">]</span><span class="si">}</span><span class="s2">"</span><span class="p">)</span> <span class="n">fill_rate</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">'sales'</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">stats</span><span class="p">[</span><span class="s1">'sales'</span><span class="p">]</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">'stockouts'</span><span class="p">])</span> <span class="nb">print</span><span class="p">(</span><span class="sa">f</span><span class="s2">"Fill rate: </span><span class="si">{</span><span class="n">fill_rate</span><span class="si">:</span><span class="s2">.1%</span><span class="si">}</span><span class="s2">"</span><span class="p">)</span> <span class="n">levels</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">'inventory_levels'</span><span class="p">]</span> <span class="n">store_levels</span> <span class="o">=</span> <span class="p">[</span><span class="n">l</span><span class="p">[</span><span class="s1">'store'</span><span class="p">]</span> <span class="k">for</span> <span class="n">l</span> <span class="ow">in</span> <span class="n">levels</span><span class="p">]</span> <span class="n">dc_levels</span> <span class="o">=</span> <span class="p">[</span><span class="n">l</span><span class="p">[</span><span class="s1">'dc'</span><span class="p">]</span> <span class="k">for</span> <span class="n">l</span> <span class="ow">in</span> <span class="n">levels</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">Avg store inventory: </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">store_levels</span><span class="p">)</span><span class="si">:</span><span class="s2">.0f</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">"Avg DC inventory: </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">dc_levels</span><span class="p">)</span><span class="si">:</span><span class="s2">.0f</span><span class="si">}</span><span class="s2">"</span><span class="p">)</span> <span class="c1"># Configuration</span> <span class="n">config</span> <span class="o">=</span> <span class="p">{</span> <span class="s1">'dc_capacity'</span><span class="p">:</span> <span class="mi">500</span><span class="p">,</span> <span class="s1">'dc_initial'</span><span class="p">:</span> <span class="mi">300</span><span class="p">,</span> <span class="s1">'dc_reorder_point'</span><span class="p">:</span> <span class="mi">150</span><span class="p">,</span> <span class="s1">'dc_order_qty'</span><span class="p">:</span> <span class="mi">200</span><span class="p">,</span> <span class="s1">'store_capacity'</span><span class="p">:</span> <span class="mi">100</span><span class="p">,</span> <span class="s1">'store_initial'</span><span class="p">:</span> <span class="mi">60</span><span class="p">,</span> <span class="s1">'store_reorder_point'</span><span class="p">:</span> <span class="mi">30</span><span class="p">,</span> <span class="s1">'store_order_qty'</span><span class="p">:</span> <span class="mi">50</span><span class="p">,</span> <span class="s1">'dc_to_store_lead'</span><span class="p">:</span> <span class="mi">2</span><span class="p">,</span> <span class="s1">'supplier_lead_time'</span><span class="p">:</span> <span class="mi">7</span><span class="p">,</span> <span class="s1">'demand_rate'</span><span class="p">:</span> <span class="mi">1</span><span class="o">/</span><span class="mf">0.5</span> <span class="c1"># 2 per day</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">SupplyChainSimulation</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">90</span><span class="p">)</span> <span class="c1"># 90 days</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>Supply chain simulation reveals: - Inventory positioning and levels - Lead time impacts - Bullwhip effect dynamics - Order policy optimisation - Network bottlenecks</p> <p>Supply chains are long queues. Simulate to shorten them.</p> <h2 id="next-steps">Next Steps</h2> <ul> <li><a href="/blog_posts/simpy-warehouse-simulation.html">Warehouse Simulation</a></li> <li><a href="/blog_posts/simpy-manufacturing-simulation.html">Manufacturing Simulation</a></li> <li><a href="/blog_posts/simpy-container-explained.html">Container Explained</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