Arjen Markus (7 april 2020) I got the idea from a column in the newspaper: because of the corona crisis, at this moment in time the demand for all manner of medical supplies, such as face masks or pipettes for testing, is far greater than in ordinary times. However, the production of these items is geared to (cost) efficiency, like so many other things: they are produced "just in time" and there is little stock. So when the demand suddenly increases considerably, the production line cannot cope with the demand and the delivery times grow.
This can - in principle - be simulated in a straightforward way:
It is simply a lot of bookkeeping.
(From experiments with this little production model I found that it makes not much sense to have the parameters Pmax en Msupply be independent. The one with the lower value effectively controls the production.)
Here is a screenshot of the GUI and a simulation:
The code below consists of two parts:
When you run the simulation, you will see that sometimes it produces really large demands. The code does not analyse the consequences, though. That might be a useful addition.
# production_line.tcl -- # Simplified model of a production chain: # # TODO: # - Why can Dtotal get negative? # - A GUI to set the parameters and show the results in graph form # - Keep track of the time of delivery # # A factory is producing a product on demand according to the modern efficiency principles - # keep only small stocks of raw materials and finished products. If an order comes in, the # product is manufactured. This works fine, as long as the demand is not too large and the raw # materials can be delivered. # # We use a single unit of time (say a week), to measure the demand, the production and the # time of delivery. # # Parameters: # P - the amount of product that will be produced in this time unit (or number of products) # Pmax - the maximum production per time unit # D - the current demand for the product (comes in once a week/time unit) # Dtotal - the total demand at this moment (if we have a backlog) # Pstock - the amount of product in stock # Psmax - the maximum amount in stock - we will never produce more than can be stored # MP - the amount of raw materials per unit of product # M - the amount of raw materials required to make P of the product # Mstock - the amount of raw materials in stock # Msmax - the maximum amount of raw materials in stock # Msupply - the maximum supply of raw materials # # We need to keep track of: # - the demand over time # - the production over time # - the mean time of delivery over time # # Our experiment entails that the demand varies randomly over time, but can make a significant # jump, overloading the production facilities. # # Dmean - mean demand # Ddev - fluctuation in the demand - currently modelled via a uniformly random variation # # getDemand -- # Generate the demand for this time step # # Arguments: # None # # Returns: # The new value for the demand # proc getDemand {} { global Dmean global Ddev expr { $Dmean + $Ddev * (2.0 * rand() - 1.0) } } # getIdealProduction -- # Determine the ideal production level (assuming that the raw materials # are in plenty supply # # Arguments: # demand Total demand for this time unit # # Result: # Production to be made and amount of products from stock # proc getIdealProduction {demand} { global Pmax global Pstock global Psmax # # We may have some product in stock, in that case, use the stock # If that is not enough, produce what is demanded (up to the maximum) # If the stock is empty, produce extra # set Psold $Pstock if { $demand <= $Pstock } { set Pstock [expr {$Pstock - $demand}] set production 0.0 } elseif { $Pstock > 0.0 } { set production [expr {min($demand-$Pstock,$Pmax)}] set Pstock 0.0 } else { set production [expr {min($demand+$Psmax,$Pmax)}] set Pstock [expr {max(min($production-$demand,$Psmax),0.0)}] } return [list $production [expr {$Psold - $Pstock}]] } # getRawMaterials -- # Determine how much raw materials are needed/can be delivered # # Arguments: # production Ideal production # # Result: # Amount of raw materials that will be used for the production in this time unit, # the amount that is ordered to fill the stock and an indication whether it can # be delivered # proc getRawMaterials {production} { global MP global Mstock global Msmax global Msupply # # We may have some raw materials in stock, in that case, use the stock # If that is not enough, order what is demanded (up to the maximum) # If the stock is empty, order extra # set useRaw [expr {$MP * $production}] set orderExtra 0.0 if { $useRaw <= $Mstock } { set Mstock [expr {$Mstock - $useRaw}] set orderRaw 0.0 } elseif { $Mstock > 0.0 } { set orderRaw [expr {max($useRaw-$Mstock, 0.0)}] set Mstock [expr {max($Mstock-$useRaw, 0.0)}] } else { set orderRaw $useRaw set orderExtra $Msmax set Mstock $Msmax } # # Check that this amount can be delivered # # - hier komt de denkfout vandaan! # if { $orderRaw+$orderExtra > $Msupply } { set deliverable 0 set orderRaw $Msupply set orderExtra 0.0 set Mstock [expr {max($Msupply - $useRaw,0.0)}] } else { set deliverable 1 } return [list $orderRaw $orderExtra $deliverable] } # # Set defaults # set Pmax 215.0 set Pstock 50.0 set Psmax 100.0 set MP 1.0 set Msmax 10.0 set Mstock 0.0 set Dmean 200.0 set Ddev 50.0 # runModel -- # Run the simulation # # Arguments: # print Print the output (if 1, otherwise store the result) # # Result: # Output to the screen or results in a global array # proc runModel {print} { global Msupply global result global Pmax global Pstock global Mstock global MP set t -1 set demand 0.0 set production 0.0 set Dtotal 0.0 ;# No backlog set Msupply $Pmax ;# These must be in some relation ... if { $print } { puts [format %5d%10.2f%10.2f%10.2f%10.2f $t $demand $Dtotal $production $Pstock] } else { set result(t) {} set result(demand) {} set result(Dtotal) {} set result(production) {} set result(Pstock) {} set result(Mstock) {} set result(deliverable) {} } for {set t 0} {$t < 200} {incr t} { set demand [getDemand] set Dtotal [expr {$Dtotal + $demand}] lassign [getIdealProduction $Dtotal] production fromStock lassign [getRawMaterials $production] orderRaw orderExtra deliverable set prodIdeal $production if { ! $deliverable } { set production [expr {min( $production, $orderRaw / $MP )}] } set Dtotal [expr {$Dtotal - ($production+$fromStock)}] if { $print } { puts [format %5d%10.2f%10.2f%10.2f%10.2f%10.2f%10.2f%5d $t $demand $Dtotal $production $prodIdeal $Pstock $Mstock $deliverable] } else { lappend result(t) $t lappend result(demand) $demand lappend result(Dtotal) $Dtotal lappend result(production) $production lappend result(Pstock) $Pstock lappend result(Mstock) $Mstock lappend result(deliverable) $deliverable } } } # test -- # First test: # Can we produce? # if { ! [info exists gui] } { runModel 1 }
# production_gui.tcl -- # GUI for setting the parameters of the production line simulation and # displaying the results # package require Plotchart set gui 1 source production_line.tcl # setupMainWindow -- # Set up the main window # # Arguments: # None # # Result: # Main window filled in with several entry widgets and a canvas # proc setupMainWindow {} { global p canvas .c -bg white -height 500 -width 700 frame .f label .f.txt_pmax -text "Maximum production" ; entry .f.entry_pmax -textvariable Pmax label .f.txt_psmax -text "Maximum storage" ; entry .f.entry_psmax -textvariable Psmax label .f.txt_dmean -text "Mean demand" ; entry .f.entry_dmean -textvariable Dmean label .f.txt_ddev -text "Fluctuation in demand" ; entry .f.entry_ddev -textvariable Ddev label .f.empty -text "" label .f.empty2 -text "" button .f.run -text "Run model" -command [list runSimulation] grid .f.txt_pmax .f.entry_pmax -pady 2 -sticky w grid .f.txt_psmax .f.entry_psmax -pady 2 -sticky w grid .f.empty - -sticky w grid .f.txt_dmean .f.entry_dmean -pady 2 -sticky w grid .f.txt_ddev .f.entry_ddev -pady 2 -sticky w grid .f.empty2 - -sticky w grid .f.run -sticky w grid .f .c -sticky news -padx 3 set p [::Plotchart::createXYPlot .c {0 200 50} {0 300 50}] } # runSimulation -- # Run the simulation and present the result # # Arguments: # None # # Result: # Simulation results shown # proc runSimulation {} { global result global p runModel 0 $p deletedata $p dataconfig demand -colour blue $p dataconfig production -colour lime $p dataconfig backlog -colour red foreach t $result(t) demand $result(demand) production $result(production) Dtotal $result(Dtotal) { $p plot demand $t $demand $p plot production $t $production $p plot backlog $t $Dtotal } } # main -- # Start the program # setupMainWindow