@@ -68,9 +68,9 @@ function model(name: string, nodes: ModelNode[], infoLinks: InformationLink[]):
* feedback: it just shows the substrate (Stock, Flow, Source, Sink).
* feedback: it just shows the substrate (Stock, Flow, Source, Sink).
*/
*/
function bathtub ( ) : Model {
function bathtub ( ) : Model {
const source = makeCloud ( { x : - 27 8 , y : - 18 } )
const source = makeCloud ( { x : - 280 , y : 0 } )
const water = makeStock ( { x : - 48 , y : - 2 0 } , "Water" )
const water = makeStock ( { x : 0 , y : 0 } , "Water" )
const sink = makeCloud ( { x : 242 , y : - 18 } )
const sink = makeCloud ( { x : 280 , y : 0 } )
const filling = makeFlow (
const filling = makeFlow (
midpoint ( source . position , water . position ) ,
midpoint ( source . position , water . position ) ,
"filling" ,
"filling" ,
@@ -90,8 +90,8 @@ function savings(): Model {
// Source up-left, Balance down-right: the interest valve lands at their
// Source up-left, Balance down-right: the interest valve lands at their
// midpoint (above Balance), so the `Balance → interest` link arcs back up as a
// midpoint (above Balance), so the `Balance → interest` link arcs back up as a
// visible Reinforcing loop instead of overlapping the inflow pipe.
// visible Reinforcing loop instead of overlapping the inflow pipe.
const source = makeCloud ( { x : - 25 0 , y : - 7 0 } )
const source = makeCloud ( { x : - 24 0 , y : - 8 0 } )
const balance = makeStock ( { x : 11 0 , y : 3 0 } , "Balance" )
const balance = makeStock ( { x : 12 0 , y : 4 0 } , "Balance" )
const interest = makeFlow (
const interest = makeFlow (
midpoint ( source . position , balance . position ) ,
midpoint ( source . position , balance . position ) ,
"interest" ,
"interest" ,
@@ -108,15 +108,15 @@ function savings(): Model {
* (outflow) → Coffee has one `− ` → Balancing: it settles toward room temperature.
* (outflow) → Coffee has one `− ` → Balancing: it settles toward room temperature.
*/
*/
function coffee ( ) : Model {
function coffee ( ) : Model {
const coffee = makeStock ( { x : - 200 , y : - 1 0 } , "Coffee" )
const coffee = makeStock ( { x : - 200 , y : 0 } , "Coffee" )
const sink = makeCloud ( { x : 19 0, y : - 8 } )
const sink = makeCloud ( { x : 20 0, y : 0 } )
const heatLoss = makeFlow (
const heatLoss = makeFlow (
midpoint ( coffee . position , sink . position ) ,
midpoint ( coffee . position , sink . position ) ,
"heat loss" ,
"heat loss" ,
coffee . id ,
coffee . id ,
sink . id ,
sink . id ,
)
)
const room = makeConverter ( { x : - 29 , y : - 15 0 } , "room temperature" )
const room = makeConverter ( { x : 0 , y : - 16 0 } , "room temperature" )
return model (
return model (
"Coffee cooling" ,
"Coffee cooling" ,
[ coffee , sink , heatLoss , room ] ,
[ coffee , sink , heatLoss , room ] ,
@@ -131,18 +131,17 @@ function coffee(): Model {
* births (`+`), life expectancy lowers deaths (`− `).
* births (`+`), life expectancy lowers deaths (`− `).
*/
*/
function population ( ) : Model {
function population ( ) : Model {
const source = makeCloud ( { x : - 360 , y : - 8 } )
// A grid-aligned cascade (20px grid): Source and both Converters stack in the left
const people = makeStock ( { x : - 48 , y : - 10 } , "Population" )
// column, the Stock sits at the centre, and births → Population → deaths step down
const sink = makeCloud ( { x : 250 , y : - 8 } )
// toward the Sink — so every Information Link lands in open space, not on a pipe.
const births = makeFlow (
// Valves are placed by hand, not at the midpoint, to hold the steps.
midpoint ( source . position , people . position ) ,
const source = makeCloud ( { x : - 360 , y : - 240 } )
"births " ,
const fertility = makeConverter ( { x : - 360 , y : - 40 } , "fertility ")
source . id ,
const lifeExpectancy = makeConverter ( { x : - 360 , y : 240 } , "life expectancy" )
people . id ,
const people = makeStock ( { x : 0 , y : 0 } , "Population" )
)
const births = makeFlow ( { x : - 160 , y : - 160 } , "births" , source . id , people . id )
const deaths = makeF low ( midpoint ( people . position , sink . position ) , "deaths" , people . id , sink . id )
const sink = makeC loud ( { x : 360 , y : 240 } )
const fertility = makeConverter ( { x : - 204 , y : - 150 } , "fertility" )
const deaths = makeFlow ( { x : 160 , y : 160 } , "deaths" , people . id , sink . id )
const lifeExpectancy = makeConverter ( { x : 101 , y : - 150 } , "life expectancy" )
return model (
return model (
"Population" ,
"Population" ,
[ source , people , sink , births , deaths , fertility , lifeExpectancy ] ,
[ source , people , sink , births , deaths , fertility , lifeExpectancy ] ,
@@ -164,13 +163,13 @@ function population(): Model {
* the balancing loop without sitting on any cycle.
* the balancing loop without sitting on any cycle.
*/
*/
function limitsToGrowth ( ) : Model {
function limitsToGrowth ( ) : Model {
const source = makeCloud ( { x : - 30 0, y : 0 } )
const source = makeCloud ( { x : - 28 0, y : 0 } )
const yeast = makeStock ( { x : 40 , y : 0 } , "Yeast" )
const yeast = makeStock ( { x : 40 , y : 0 } , "Yeast" )
const growth = makeFlow ( midpoint ( source . position , yeast . position ) , "growth" , source . id , yeast . id )
const growth = makeFlow ( midpoint ( source . position , yeast . position ) , "growth" , source . id , yeast . id )
// crowding rides above the pipe; carrying capacity sits to its right so the
// crowding rides above the pipe; carrying capacity sits to its right so the
// `capacity → crowding` link is a short horizontal hop along the top.
// `capacity → crowding` link is a short horizontal hop along the top.
const crowding = makeConverter ( { x : - 40 , y : - 15 0 } , "crowding" )
const crowding = makeConverter ( { x : - 40 , y : - 16 0 } , "crowding" )
const capacity = makeConverter ( { x : 17 0 , y : - 15 0 } , "carrying capacity" )
const capacity = makeConverter ( { x : 16 0 , y : - 16 0 } , "carrying capacity" )
return model (
return model (
"Limits to growth" ,
"Limits to growth" ,
[ source , yeast , growth , crowding , capacity ] ,
[ source , yeast , growth , crowding , capacity ] ,
@@ -275,7 +274,7 @@ function epidemic(): Model {
infected . id ,
infected . id ,
recovered . id ,
recovered . id ,
)
)
const infectivity = makeConverter ( { x : - 140 , y : - 15 0 } , "infectivity" )
const infectivity = makeConverter ( { x : - 140 , y : - 16 0 } , "infectivity" )
return model (
return model (
"Epidemic" ,
"Epidemic" ,
[ susceptible , infected , recovered , infection , recovery , infectivity ] ,
[ susceptible , infected , recovered , infection , recovery , infectivity ] ,
@@ -304,7 +303,7 @@ function tragedyOfTheCommons(): Model {
// Two symmetric herds: cattle enter from a Source on the outside, grass leaves
// Two symmetric herds: cattle enter from a Source on the outside, grass leaves
// the Pasture downward to a Sink. The two `Pasture → growth` links are the weak
// the Pasture downward to a Sink. The two `Pasture → growth` links are the weak
// brake the trap overruns.
// brake the trap overruns.
const sourceA = makeCloud ( { x : - 62 0 , y : 0 } )
const sourceA = makeCloud ( { x : - 64 0 , y : 0 } )
const herdA = makeStock ( { x : - 360 , y : 0 } , "Herd A" )
const herdA = makeStock ( { x : - 360 , y : 0 } , "Herd A" )
const growthA = makeFlow (
const growthA = makeFlow (
midpoint ( sourceA . position , herdA . position ) ,
midpoint ( sourceA . position , herdA . position ) ,
@@ -312,14 +311,14 @@ function tragedyOfTheCommons(): Model {
sourceA . id ,
sourceA . id ,
herdA . id ,
herdA . id ,
)
)
const sinkA = makeCloud ( { x : - 18 0, y : 22 0 } )
const sinkA = makeCloud ( { x : - 20 0, y : 24 0 } )
const grazingA = makeFlow (
const grazingA = makeFlow (
midpoint ( pasture . position , sinkA . position ) ,
midpoint ( pasture . position , sinkA . position ) ,
"grazing A" ,
"grazing A" ,
pasture . id ,
pasture . id ,
sinkA . id ,
sinkA . id ,
)
)
const sourceB = makeCloud ( { x : 62 0 , y : 0 } )
const sourceB = makeCloud ( { x : 64 0 , y : 0 } )
const herdB = makeStock ( { x : 360 , y : 0 } , "Herd B" )
const herdB = makeStock ( { x : 360 , y : 0 } , "Herd B" )
const growthB = makeFlow (
const growthB = makeFlow (
midpoint ( sourceB . position , herdB . position ) ,
midpoint ( sourceB . position , herdB . position ) ,
@@ -327,7 +326,7 @@ function tragedyOfTheCommons(): Model {
sourceB . id ,
sourceB . id ,
herdB . id ,
herdB . id ,
)
)
const sinkB = makeCloud ( { x : 18 0, y : 22 0 } )
const sinkB = makeCloud ( { x : 20 0, y : 24 0 } )
const grazingB = makeFlow (
const grazingB = makeFlow (
midpoint ( pasture . position , sinkB . position ) ,
midpoint ( pasture . position , sinkB . position ) ,
"grazing B" ,
"grazing B" ,
@@ -362,7 +361,7 @@ function escalation(): Model {
// Source to its arsenal. The two cross-coupling links span the open centre and
// Source to its arsenal. The two cross-coupling links span the open centre and
// cross there, where the R badge lands, so the whole loop reads at a glance.
// cross there, where the R badge lands, so the whole loop reads at a glance.
const blueSource = makeCloud ( { x : - 560 , y : - 120 } )
const blueSource = makeCloud ( { x : - 560 , y : - 120 } )
const blueArsenal = makeStock ( { x : 30 0, y : - 120 } , "Blue arsenal" )
const blueArsenal = makeStock ( { x : 28 0, y : - 120 } , "Blue arsenal" )
const blueBuildup = makeFlow (
const blueBuildup = makeFlow (
midpoint ( blueSource . position , blueArsenal . position ) ,
midpoint ( blueSource . position , blueArsenal . position ) ,
"Blue buildup" ,
"Blue buildup" ,
@@ -370,7 +369,7 @@ function escalation(): Model {
blueArsenal . id ,
blueArsenal . id ,
)
)
const redSource = makeCloud ( { x : - 560 , y : 120 } )
const redSource = makeCloud ( { x : - 560 , y : 120 } )
const redArsenal = makeStock ( { x : 30 0, y : 120 } , "Red arsenal" )
const redArsenal = makeStock ( { x : 28 0, y : 120 } , "Red arsenal" )
const redBuildup = makeFlow (
const redBuildup = makeFlow (
midpoint ( redSource . position , redArsenal . position ) ,
midpoint ( redSource . position , redArsenal . position ) ,
"Red buildup" ,
"Red buildup" ,