Machine Shop Simulation with SimPy: Jobs, Machines, and Breakdowns

<p>The machine shop is a classic simulation problem. Jobs arrive. Machines process them. Breakdowns happen. The question: can you meet your deadlines?</p> <h2 id="the-machine-shop-model">The Machine Shop Model</h2> <p>This is SimPy's official example, expanded and explained.</p> <p>Key elements: - <strong>Jobs</strong> - Work to be done - <strong>Machines</strong> - Process jobs - <strong>Repairmen</strong> - Fix breakdowns - <strong>Priorities</strong> - Some jobs matter more</p> <h2 id="basic-machine-shop">Basic Machine Shop</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="n">RANDOM_SEED</span> <span class="o">=</span> <span class="mi">42</span> <span class="n">PT_MEAN</span> <span class="o">=</span> <span class="mf">10.0</span> <span class="c1"># Mean processing time</span> <span class="n">PT_SIGMA</span> <span class="o">=</span> <span class="mf">2.0</span> <span class="c1"># Processing time std dev</span> <span class="n">MTTF</span> <span class="o">=</span> <span class="mf">300.0</span> <span class="c1"># Mean time to failure</span> <span class="n">REPAIR_TIME</span> <span class="o">=</span> <span class="mf">30.0</span> <span class="c1"># Time to repair</span> <span class="n">JOB_DURATION</span> <span class="o">=</span> <span class="mf">30.0</span> <span class="c1"># Time between job arrivals</span> <span class="n">NUM_MACHINES</span> <span class="o">=</span> <span class="mi">10</span> <span class="n">SIM_TIME</span> <span class="o">=</span> <span class="mi">4</span> <span class="o">*</span> <span class="mi">7</span> <span class="o">*</span> <span class="mi">24</span> <span class="o">*</span> <span class="mi">60</span> <span class="c1"># 4 weeks in minutes</span> <span class="k">def</span><span class="w"> </span><span class="nf">time_per_part</span><span class="p">():</span> <span class="w"> </span><span class="sd">&quot;&quot;&quot;Return actual processing time for a part.&quot;&quot;&quot;</span> <span class="k">return</span> <span class="nb">max</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="n">random</span><span class="o">.</span><span class="n">normalvariate</span><span class="p">(</span><span class="n">PT_MEAN</span><span class="p">,</span> <span class="n">PT_SIGMA</span><span class="p">))</span> <span class="k">def</span><span class="w"> </span><span class="nf">time_to_failure</span><span class="p">():</span> <span class="w"> </span><span class="sd">&quot;&quot;&quot;Return time until next failure.&quot;&quot;&quot;</span> <span class="k">return</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="n">MTTF</span><span class="p">)</span> <span class="k">class</span><span class="w"> </span><span class="nc">Machine</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">repairman</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">parts_made</span> <span class="o">=</span> <span class="mi">0</span> <span class="bp">self</span><span class="o">.</span><span class="n">broken</span> <span class="o">=</span> <span class="kc">False</span> <span class="bp">self</span><span class="o">.</span><span class="n">repairman</span> <span class="o">=</span> <span class="n">repairman</span> <span class="bp">self</span><span class="o">.</span><span class="n">process</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">working</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">break_machine</span><span class="p">())</span> <span class="k">def</span><span class="w"> </span><span class="nf">working</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span> <span class="w"> </span><span class="sd">&quot;&quot;&quot;Produce parts as long as the simulation runs.&quot;&quot;&quot;</span> <span class="k">while</span> <span class="kc">True</span><span class="p">:</span> <span class="n">done_in</span> <span class="o">=</span> <span class="n">time_per_part</span><span class="p">()</span> <span class="k">while</span> <span class="n">done_in</span><span class="p">:</span> <span class="k">try</span><span class="p">:</span> <span class="n">start</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">env</span><span class="o">.</span><span class="n">timeout</span><span class="p">(</span><span class="n">done_in</span><span class="p">)</span> <span class="n">done_in</span> <span class="o">=</span> <span class="mi">0</span> <span class="c1"># Part complete</span> <span class="k">except</span> <span class="n">simpy</span><span class="o">.</span><span class="n">Interrupt</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">broken</span> <span class="o">=</span> <span class="kc">True</span> <span class="n">done_in</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">start</span> <span class="c1"># Time remaining</span> <span class="c1"># Request repair</span> <span class="k">with</span> <span class="bp">self</span><span class="o">.</span><span class="n">repairman</span><span class="o">.</span><span class="n">request</span><span class="p">(</span><span class="n">priority</span><span class="o">=</span><span class="mi">1</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">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">REPAIR_TIME</span><span class="p">)</span> <span class="bp">self</span><span class="o">.</span><span class="n">broken</span> <span class="o">=</span> <span class="kc">False</span> <span class="bp">self</span><span class="o">.</span><span class="n">parts_made</span> <span class="o">+=</span> <span class="mi">1</span> <span class="k">def</span><span class="w"> </span><span class="nf">break_machine</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span> <span class="w"> </span><span class="sd">&quot;&quot;&quot;Break the machine periodically.&quot;&quot;&quot;</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="n">time_to_failure</span><span class="p">())</span> <span class="k">if</span> <span class="ow">not</span> <span class="bp">self</span><span class="o">.</span><span class="n">broken</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">process</span><span class="o">.</span><span class="n">interrupt</span><span class="p">()</span> <span class="c1"># Run simulation</span> <span class="n">random</span><span class="o">.</span><span class="n">seed</span><span class="p">(</span><span class="n">RANDOM_SEED</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">repairman</span> <span class="o">=</span> <span class="n">simpy</span><span class="o">.</span><span class="n">PreemptiveResource</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="mi">1</span><span class="p">)</span> <span class="n">machines</span> <span class="o">=</span> <span class="p">[</span><span class="n">Machine</span><span class="p">(</span><span class="n">env</span><span class="p">,</span> <span class="sa">f</span><span class="s1">&#39;Machine </span><span class="si">{</span><span class="n">i</span><span class="si">}</span><span class="s1">&#39;</span><span class="p">,</span> <span class="n">repairman</span><span class="p">)</span> <span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="n">NUM_MACHINES</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="n">SIM_TIME</span><span class="p">)</span> <span class="nb">print</span><span class="p">(</span><span class="s1">&#39;Machine shop results:&#39;</span><span class="p">)</span> <span class="k">for</span> <span class="n">machine</span> <span class="ow">in</span> <span class="n">machines</span><span class="p">:</span> <span class="nb">print</span><span class="p">(</span><span class="sa">f</span><span class="s1">&#39;</span><span class="si">{</span><span class="n">machine</span><span class="o">.</span><span class="n">name</span><span class="si">}</span><span class="s1"> made </span><span class="si">{</span><span class="n">machine</span><span class="o">.</span><span class="n">parts_made</span><span class="si">}</span><span class="s1"> parts.&#39;</span><span class="p">)</span> </code></pre></div> <h2 id="job-shop-with-routing">Job Shop with Routing</h2> <p>Jobs visit multiple machines in sequence:</p> <div class="code-block"><pre><span></span><code><span class="k">class</span><span class="w"> </span><span class="nc">Job</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">job_id</span><span class="p">,</span> <span class="n">routing</span><span class="p">,</span> <span class="n">due_date</span><span class="p">):</span> <span class="bp">self</span><span class="o">.</span><span class="n">id</span> <span class="o">=</span> <span class="n">job_id</span> <span class="bp">self</span><span class="o">.</span><span class="n">routing</span> <span class="o">=</span> <span class="n">routing</span> <span class="c1"># List of (machine, time) tuples</span> <span class="bp">self</span><span class="o">.</span><span class="n">due_date</span> <span class="o">=</span> <span class="n">due_date</span> <span class="bp">self</span><span class="o">.</span><span class="n">start_time</span> <span class="o">=</span> <span class="kc">None</span> <span class="bp">self</span><span class="o">.</span><span class="n">end_time</span> <span class="o">=</span> <span class="kc">None</span> <span class="k">class</span><span class="w"> </span><span class="nc">JobShop</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">machine_names</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">machines</span> <span class="o">=</span> <span class="p">{</span> <span class="n">name</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="mi">1</span><span class="p">)</span> <span class="k">for</span> <span class="n">name</span> <span class="ow">in</span> <span class="n">machine_names</span> <span class="p">}</span> <span class="bp">self</span><span class="o">.</span><span class="n">completed_jobs</span> <span class="o">=</span> <span class="p">[]</span> <span class="k">def</span><span class="w"> </span><span class="nf">process_job</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">job</span><span class="p">):</span> <span class="n">job</span><span class="o">.</span><span class="n">start_time</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">for</span> <span class="n">machine_name</span><span class="p">,</span> <span class="n">process_time</span> <span class="ow">in</span> <span class="n">job</span><span class="o">.</span><span class="n">routing</span><span class="p">:</span> <span class="n">machine</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">machines</span><span class="p">[</span><span class="n">machine_name</span><span class="p">]</span> <span class="k">with</span> <span class="n">machine</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">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">process_time</span><span class="p">)</span> <span class="n">job</span><span class="o">.</span><span class="n">end_time</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">completed_jobs</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">job</span><span class="p">)</span> <span class="n">lateness</span> <span class="o">=</span> <span class="nb">max</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="n">job</span><span class="o">.</span><span class="n">end_time</span> <span class="o">-</span> <span class="n">job</span><span class="o">.</span><span class="n">due_date</span><span class="p">)</span> <span class="k">if</span> <span class="n">lateness</span> <span class="o">&gt;</span> <span class="mi">0</span><span class="p">:</span> <span class="nb">print</span><span class="p">(</span><span class="sa">f</span><span class="s2">&quot;Job </span><span class="si">{</span><span class="n">job</span><span class="o">.</span><span class="n">id</span><span class="si">}</span><span class="s2"> late by </span><span class="si">{</span><span class="n">lateness</span><span class="si">:</span><span class="s2">.1f</span><span class="si">}</span><span class="s2">&quot;</span><span class="p">)</span> <span class="c1"># Example usage</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">shop</span> <span class="o">=</span> <span class="n">JobShop</span><span class="p">(</span><span class="n">env</span><span class="p">,</span> <span class="p">[</span><span class="s1">&#39;lathe&#39;</span><span class="p">,</span> <span class="s1">&#39;mill&#39;</span><span class="p">,</span> <span class="s1">&#39;drill&#39;</span><span class="p">,</span> <span class="s1">&#39;grinder&#39;</span><span class="p">])</span> <span class="c1"># Create jobs with different routings</span> <span class="n">jobs</span> <span class="o">=</span> <span class="p">[</span> <span class="n">Job</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="p">[(</span><span class="s1">&#39;lathe&#39;</span><span class="p">,</span> <span class="mi">10</span><span class="p">),</span> <span class="p">(</span><span class="s1">&#39;mill&#39;</span><span class="p">,</span> <span class="mi">15</span><span class="p">),</span> <span class="p">(</span><span class="s1">&#39;drill&#39;</span><span class="p">,</span> <span class="mi">5</span><span class="p">)],</span> <span class="n">due_date</span><span class="o">=</span><span class="mi">50</span><span class="p">),</span> <span class="n">Job</span><span class="p">(</span><span class="mi">2</span><span class="p">,</span> <span class="p">[(</span><span class="s1">&#39;mill&#39;</span><span class="p">,</span> <span class="mi">20</span><span class="p">),</span> <span class="p">(</span><span class="s1">&#39;grinder&#39;</span><span class="p">,</span> <span class="mi">10</span><span class="p">)],</span> <span class="n">due_date</span><span class="o">=</span><span class="mi">40</span><span class="p">),</span> <span class="n">Job</span><span class="p">(</span><span class="mi">3</span><span class="p">,</span> <span class="p">[(</span><span class="s1">&#39;lathe&#39;</span><span class="p">,</span> <span class="mi">8</span><span class="p">),</span> <span class="p">(</span><span class="s1">&#39;drill&#39;</span><span class="p">,</span> <span class="mi">12</span><span class="p">),</span> <span class="p">(</span><span class="s1">&#39;grinder&#39;</span><span class="p">,</span> <span class="mi">8</span><span class="p">)],</span> <span class="n">due_date</span><span class="o">=</span><span class="mi">60</span><span class="p">),</span> <span class="p">]</span> <span class="k">for</span> <span class="n">job</span> <span class="ow">in</span> <span class="n">jobs</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">shop</span><span class="o">.</span><span class="n">process_job</span><span class="p">(</span><span class="n">job</span><span class="p">))</span> <span class="n">env</span><span class="o">.</span><span class="n">run</span><span class="p">()</span> </code></pre></div> <h2 id="priority-scheduling">Priority Scheduling</h2> <p>Rush jobs jump the queue:</p> <div class="code-block"><pre><span></span><code><span class="k">class</span><span class="w"> </span><span class="nc">PriorityJobShop</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">machine_names</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">machines</span> <span class="o">=</span> <span class="p">{</span> <span class="n">name</span><span class="p">:</span> <span class="n">simpy</span><span class="o">.</span><span class="n">PriorityResource</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="mi">1</span><span class="p">)</span> <span class="k">for</span> <span class="n">name</span> <span class="ow">in</span> <span class="n">machine_names</span> <span class="p">}</span> <span class="k">def</span><span class="w"> </span><span class="nf">process_job</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">job</span><span class="p">,</span> <span class="n">priority</span><span class="p">):</span> <span class="w"> </span><span class="sd">&quot;&quot;&quot;Lower priority number = higher priority.&quot;&quot;&quot;</span> <span class="k">for</span> <span class="n">machine_name</span><span class="p">,</span> <span class="n">process_time</span> <span class="ow">in</span> <span class="n">job</span><span class="o">.</span><span class="n">routing</span><span class="p">:</span> <span class="n">machine</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">machines</span><span class="p">[</span><span class="n">machine_name</span><span class="p">]</span> <span class="k">with</span> <span class="n">machine</span><span class="o">.</span><span class="n">request</span><span class="p">(</span><span class="n">priority</span><span class="o">=</span><span class="n">priority</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">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">process_time</span><span class="p">)</span> <span class="c1"># Rush job (priority 1) vs normal job (priority 5)</span> <span class="n">env</span><span class="o">.</span><span class="n">process</span><span class="p">(</span><span class="n">shop</span><span class="o">.</span><span class="n">process_job</span><span class="p">(</span><span class="n">rush_job</span><span class="p">,</span> <span class="n">priority</span><span class="o">=</span><span class="mi">1</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">shop</span><span class="o">.</span><span class="n">process_job</span><span class="p">(</span><span class="n">normal_job</span><span class="p">,</span> <span class="n">priority</span><span class="o">=</span><span class="mi">5</span><span class="p">))</span> </code></pre></div> <h2 id="setup-times">Setup Times</h2> <p>Changing job types requires setup:</p> <div class="code-block"><pre><span></span><code><span class="k">class</span><span class="w"> </span><span class="nc">MachineWithSetup</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">setup_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">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">setup_time</span> <span class="o">=</span> <span class="n">setup_time</span> <span class="bp">self</span><span class="o">.</span><span class="n">resource</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="mi">1</span><span class="p">)</span> <span class="bp">self</span><span class="o">.</span><span class="n">current_job_type</span> <span class="o">=</span> <span class="kc">None</span> <span class="k">def</span><span class="w"> </span><span class="nf">process</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">job_type</span><span class="p">,</span> <span class="n">process_time</span><span class="p">):</span> <span class="k">with</span> <span class="bp">self</span><span class="o">.</span><span class="n">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="c1"># Setup if job type changed</span> <span class="k">if</span> <span class="n">job_type</span> <span class="o">!=</span> <span class="bp">self</span><span class="o">.</span><span class="n">current_job_type</span><span class="p">:</span> <span class="nb">print</span><span class="p">(</span><span class="sa">f</span><span class="s2">&quot;</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">: Setup for </span><span class="si">{</span><span class="n">job_type</span><span class="si">}</span><span class="s2">&quot;</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">setup_time</span><span class="p">)</span> <span class="bp">self</span><span class="o">.</span><span class="n">current_job_type</span> <span class="o">=</span> <span class="n">job_type</span> <span class="c1"># Process</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">process_time</span><span class="p">)</span> </code></pre></div> <h2 id="complete-machine-shop">Complete Machine Shop</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">MachineShopSimulation</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"># Machines</span> <span class="bp">self</span><span class="o">.</span><span class="n">machines</span> <span class="o">=</span> <span class="p">{}</span> <span class="k">for</span> <span class="n">name</span><span class="p">,</span> <span class="n">count</span> <span class="ow">in</span> <span class="n">config</span><span class="p">[</span><span class="s1">&#39;machines&#39;</span><span class="p">]</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">machines</span><span class="p">[</span><span class="n">name</span><span class="p">]</span> <span class="o">=</span> <span class="n">simpy</span><span class="o">.</span><span class="n">PreemptiveResource</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="c1"># Repairmen</span> <span class="bp">self</span><span class="o">.</span><span class="n">repairmen</span> <span class="o">=</span> <span class="n">simpy</span><span class="o">.</span><span class="n">PriorityResource</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">&#39;repairmen&#39;</span><span class="p">])</span> <span class="c1"># Stats</span> <span class="bp">self</span><span class="o">.</span><span class="n">jobs_completed</span> <span class="o">=</span> <span class="p">[]</span> <span class="bp">self</span><span class="o">.</span><span class="n">breakdowns</span> <span class="o">=</span> <span class="p">[]</span> <span class="k">def</span><span class="w"> </span><span class="nf">machine_process</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">machine_name</span><span class="p">,</span> <span class="n">job_id</span><span class="p">,</span> <span class="n">process_time</span><span class="p">,</span> <span class="n">priority</span><span class="p">):</span> <span class="w"> </span><span class="sd">&quot;&quot;&quot;Process a job on a machine, handling breakdowns.&quot;&quot;&quot;</span> <span class="n">remaining</span> <span class="o">=</span> <span class="n">process_time</span> <span class="k">while</span> <span class="n">remaining</span> <span class="o">&gt;</span> <span class="mi">0</span><span class="p">:</span> <span class="k">try</span><span class="p">:</span> <span class="k">with</span> <span class="bp">self</span><span class="o">.</span><span class="n">machines</span><span class="p">[</span><span class="n">machine_name</span><span class="p">]</span><span class="o">.</span><span class="n">request</span><span class="p">(</span><span class="n">priority</span><span class="o">=</span><span class="n">priority</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="n">start</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">env</span><span class="o">.</span><span class="n">timeout</span><span class="p">(</span><span class="n">remaining</span><span class="p">)</span> <span class="n">remaining</span> <span class="o">=</span> <span class="mi">0</span> <span class="k">except</span> <span class="n">simpy</span><span class="o">.</span><span class="n">Interrupt</span> <span class="k">as</span> <span class="n">interrupt</span><span class="p">:</span> <span class="n">remaining</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="n">start</span><span class="p">)</span> <span class="c1"># Handle breakdown</span> <span class="k">yield from</span> <span class="bp">self</span><span class="o">.</span><span class="n">repair</span><span class="p">(</span><span class="n">machine_name</span><span class="p">)</span> <span class="k">def</span><span class="w"> </span><span class="nf">repair</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">machine_name</span><span class="p">):</span> <span class="w"> </span><span class="sd">&quot;&quot;&quot;Request repair.&quot;&quot;&quot;</span> <span class="k">with</span> <span class="bp">self</span><span class="o">.</span><span class="n">repairmen</span><span class="o">.</span><span class="n">request</span><span class="p">(</span><span class="n">priority</span><span class="o">=</span><span class="mi">1</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="n">repair_time</span> <span class="o">=</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="bp">self</span><span class="o">.</span><span class="n">config</span><span class="p">[</span><span class="s1">&#39;mean_repair_time&#39;</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">repair_time</span><span class="p">)</span> <span class="k">def</span><span class="w"> </span><span class="nf">breakdown_generator</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">machine_name</span><span class="p">):</span> <span class="w"> </span><span class="sd">&quot;&quot;&quot;Generate random breakdowns for a machine.&quot;&quot;&quot;</span> <span class="k">while</span> <span class="kc">True</span><span class="p">:</span> <span class="n">ttf</span> <span class="o">=</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="bp">self</span><span class="o">.</span><span class="n">config</span><span class="p">[</span><span class="s1">&#39;mttf&#39;</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">ttf</span><span class="p">)</span> <span class="c1"># Interrupt current job if machine is busy</span> <span class="n">machine</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">machines</span><span class="p">[</span><span class="n">machine_name</span><span class="p">]</span> <span class="k">if</span> <span class="n">machine</span><span class="o">.</span><span class="n">count</span> <span class="o">&gt;</span> <span class="mi">0</span><span class="p">:</span> <span class="k">for</span> <span class="n">req</span> <span class="ow">in</span> <span class="n">machine</span><span class="o">.</span><span class="n">users</span><span class="p">:</span> <span class="k">if</span> <span class="nb">hasattr</span><span class="p">(</span><span class="n">req</span><span class="p">,</span> <span class="s1">&#39;proc&#39;</span><span class="p">)</span> <span class="ow">and</span> <span class="n">req</span><span class="o">.</span><span class="n">proc</span><span class="o">.</span><span class="n">is_alive</span><span class="p">:</span> <span class="n">req</span><span class="o">.</span><span class="n">proc</span><span class="o">.</span><span class="n">interrupt</span><span class="p">(</span><span class="sa">f</span><span class="s2">&quot;breakdown at </span><span class="si">{</span><span class="n">machine_name</span><span class="si">}</span><span class="s2">&quot;</span><span class="p">)</span> <span class="bp">self</span><span class="o">.</span><span class="n">breakdowns</span><span class="o">.</span><span class="n">append</span><span class="p">({</span> <span class="s1">&#39;machine&#39;</span><span class="p">:</span> <span class="n">machine_name</span><span class="p">,</span> <span class="s1">&#39;time&#39;</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">def</span><span class="w"> </span><span class="nf">job</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">job_id</span><span class="p">,</span> <span class="n">routing</span><span class="p">,</span> <span class="n">due_date</span><span class="p">,</span> <span class="n">priority</span><span class="p">):</span> <span class="w"> </span><span class="sd">&quot;&quot;&quot;Process a complete job through its routing.&quot;&quot;&quot;</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="k">for</span> <span class="n">machine_name</span><span class="p">,</span> <span class="n">process_time</span> <span class="ow">in</span> <span class="n">routing</span><span class="p">:</span> <span class="k">yield from</span> <span class="bp">self</span><span class="o">.</span><span class="n">machine_process</span><span class="p">(</span><span class="n">machine_name</span><span class="p">,</span> <span class="n">job_id</span><span class="p">,</span> <span class="n">process_time</span><span class="p">,</span> <span class="n">priority</span><span class="p">)</span> <span class="n">completion</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">jobs_completed</span><span class="o">.</span><span class="n">append</span><span class="p">({</span> <span class="s1">&#39;job_id&#39;</span><span class="p">:</span> <span class="n">job_id</span><span class="p">,</span> <span class="s1">&#39;arrival&#39;</span><span class="p">:</span> <span class="n">arrival</span><span class="p">,</span> <span class="s1">&#39;completion&#39;</span><span class="p">:</span> <span class="n">completion</span><span class="p">,</span> <span class="s1">&#39;due_date&#39;</span><span class="p">:</span> <span class="n">due_date</span><span class="p">,</span> <span class="s1">&#39;flow_time&#39;</span><span class="p">:</span> <span class="n">completion</span> <span class="o">-</span> <span class="n">arrival</span><span class="p">,</span> <span class="s1">&#39;lateness&#39;</span><span class="p">:</span> <span class="nb">max</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="n">completion</span> <span class="o">-</span> <span class="n">due_date</span><span class="p">)</span> <span class="p">})</span> <span class="k">def</span><span class="w"> </span><span class="nf">job_arrivals</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span> <span class="w"> </span><span class="sd">&quot;&quot;&quot;Generate incoming jobs.&quot;&quot;&quot;</span> <span class="n">job_id</span> <span class="o">=</span> <span class="mi">0</span> <span class="n">machine_names</span> <span class="o">=</span> <span class="nb">list</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">machines</span><span class="o">.</span><span class="n">keys</span><span class="p">())</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="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">&#39;job_rate&#39;</span><span class="p">]))</span> <span class="c1"># Random routing</span> <span class="n">num_ops</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">2</span><span class="p">,</span> <span class="mi">4</span><span class="p">)</span> <span class="n">routing</span> <span class="o">=</span> <span class="p">[</span> <span class="p">(</span><span class="n">random</span><span class="o">.</span><span class="n">choice</span><span class="p">(</span><span class="n">machine_names</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">5</span><span class="p">,</span> <span class="mi">20</span><span class="p">))</span> <span class="k">for</span> <span class="n">_</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="n">num_ops</span><span class="p">)</span> <span class="p">]</span> <span class="c1"># Due date</span> <span class="n">total_time</span> <span class="o">=</span> <span class="nb">sum</span><span class="p">(</span><span class="n">t</span> <span class="k">for</span> <span class="n">_</span><span class="p">,</span> <span class="n">t</span> <span class="ow">in</span> <span class="n">routing</span><span class="p">)</span> <span class="n">due_date</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">total_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="mf">1.5</span><span class="p">,</span> <span class="mi">3</span><span class="p">)</span> <span class="c1"># Priority (lower = more urgent)</span> <span class="n">priority</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">3</span><span class="p">,</span> <span class="mi">5</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">60</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">job</span><span class="p">(</span><span class="n">job_id</span><span class="p">,</span> <span class="n">routing</span><span class="p">,</span> <span class="n">due_date</span><span class="p">,</span> <span class="n">priority</span><span class="p">))</span> <span class="n">job_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="c1"># Start job arrivals</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">job_arrivals</span><span class="p">())</span> <span class="c1"># Start breakdown generators</span> <span class="k">for</span> <span class="n">machine_name</span> <span class="ow">in</span> <span class="bp">self</span><span class="o">.</span><span class="n">machines</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">breakdown_generator</span><span class="p">(</span><span class="n">machine_name</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">&quot;</span><span class="se">\n</span><span class="s2">=== Machine Shop Report ===&quot;</span><span class="p">)</span> <span class="nb">print</span><span class="p">(</span><span class="sa">f</span><span class="s2">&quot;Jobs completed: </span><span class="si">{</span><span class="nb">len</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">jobs_completed</span><span class="p">)</span><span class="si">}</span><span class="s2">&quot;</span><span class="p">)</span> <span class="nb">print</span><span class="p">(</span><span class="sa">f</span><span class="s2">&quot;Breakdowns: </span><span class="si">{</span><span class="nb">len</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">breakdowns</span><span class="p">)</span><span class="si">}</span><span class="s2">&quot;</span><span class="p">)</span> <span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">jobs_completed</span><span class="p">:</span> <span class="n">flow_times</span> <span class="o">=</span> <span class="p">[</span><span class="n">j</span><span class="p">[</span><span class="s1">&#39;flow_time&#39;</span><span class="p">]</span> <span class="k">for</span> <span class="n">j</span> <span class="ow">in</span> <span class="bp">self</span><span class="o">.</span><span class="n">jobs_completed</span><span class="p">]</span> <span class="n">lateness</span> <span class="o">=</span> <span class="p">[</span><span class="n">j</span><span class="p">[</span><span class="s1">&#39;lateness&#39;</span><span class="p">]</span> <span class="k">for</span> <span class="n">j</span> <span class="ow">in</span> <span class="bp">self</span><span class="o">.</span><span class="n">jobs_completed</span><span class="p">]</span> <span class="n">on_time</span> <span class="o">=</span> <span class="nb">sum</span><span class="p">(</span><span class="mi">1</span> <span class="k">for</span> <span class="n">j</span> <span class="ow">in</span> <span class="bp">self</span><span class="o">.</span><span class="n">jobs_completed</span> <span class="k">if</span> <span class="n">j</span><span class="p">[</span><span class="s1">&#39;lateness&#39;</span><span class="p">]</span> <span class="o">==</span> <span class="mi">0</span><span class="p">)</span> <span class="nb">print</span><span class="p">(</span><span class="sa">f</span><span class="s2">&quot;</span><span class="se">\n</span><span class="s2">Flow time:&quot;</span><span class="p">)</span> <span class="nb">print</span><span class="p">(</span><span class="sa">f</span><span class="s2">&quot; 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">flow_times</span><span class="p">)</span><span class="si">:</span><span class="s2">.1f</span><span class="si">}</span><span class="s2">&quot;</span><span class="p">)</span> <span class="nb">print</span><span class="p">(</span><span class="sa">f</span><span class="s2">&quot; Max: </span><span class="si">{</span><span class="nb">max</span><span class="p">(</span><span class="n">flow_times</span><span class="p">)</span><span class="si">:</span><span class="s2">.1f</span><span class="si">}</span><span class="s2">&quot;</span><span class="p">)</span> <span class="nb">print</span><span class="p">(</span><span class="sa">f</span><span class="s2">&quot;</span><span class="se">\n</span><span class="s2">On-time delivery: </span><span class="si">{</span><span class="n">on_time</span><span class="si">}</span><span class="s2">/</span><span class="si">{</span><span class="nb">len</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">jobs_completed</span><span class="p">)</span><span class="si">}</span><span class="s2"> (</span><span class="si">{</span><span class="n">on_time</span><span class="o">/</span><span class="nb">len</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">jobs_completed</span><span class="p">)</span><span class="si">:</span><span class="s2">.1%</span><span class="si">}</span><span class="s2">)&quot;</span><span class="p">)</span> <span class="nb">print</span><span class="p">(</span><span class="sa">f</span><span class="s2">&quot;Mean lateness (when late): </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">l</span><span class="w"> </span><span class="k">for</span><span class="w"> </span><span class="n">l</span><span class="w"> </span><span class="ow">in</span><span class="w"> </span><span class="n">lateness</span><span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="n">l</span><span class="w"> </span><span class="o">&gt;</span><span class="w"> </span><span class="mi">0</span><span class="p">])</span><span class="si">:</span><span class="s2">.1f</span><span class="si">}</span><span class="s2">&quot;</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">&#39;machines&#39;</span><span class="p">:</span> <span class="p">{</span><span class="s1">&#39;lathe&#39;</span><span class="p">:</span> <span class="mi">2</span><span class="p">,</span> <span class="s1">&#39;mill&#39;</span><span class="p">:</span> <span class="mi">2</span><span class="p">,</span> <span class="s1">&#39;drill&#39;</span><span class="p">:</span> <span class="mi">1</span><span class="p">,</span> <span class="s1">&#39;grinder&#39;</span><span class="p">:</span> <span class="mi">1</span><span class="p">},</span> <span class="s1">&#39;repairmen&#39;</span><span class="p">:</span> <span class="mi">1</span><span class="p">,</span> <span class="s1">&#39;mttf&#39;</span><span class="p">:</span> <span class="mi">200</span><span class="p">,</span> <span class="s1">&#39;mean_repair_time&#39;</span><span class="p">:</span> <span class="mi">20</span><span class="p">,</span> <span class="s1">&#39;job_rate&#39;</span><span class="p">:</span> <span class="mf">0.1</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">MachineShopSimulation</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">480</span><span class="p">)</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>Machine shop simulation covers: - Multiple machine types - Job routing and sequencing - Breakdowns and repairs - Priority scheduling - Setup times and changeovers</p> <p>The machine shop is simulation's proving ground. Master it.</p> <h2 id="next-steps">Next Steps</h2> <ul> <li><a href="/blog_posts/simpy-manufacturing-simulation.html">Manufacturing Simulation</a></li> <li><a href="/blog_posts/simpy-production-line-simulation.html">Production Line Simulation</a></li> <li><a href="/blog_posts/simpy-preemptiveresource-explained.html">PreemptiveResource 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