Sleep Debt Simulation
Models sleep patterns, caffeine intake, and sleep debt accumulation over time with reinforcing and balancing feedback.
Level:Intermediate
Dynamic Behavior Patterns
Explore common system behaviors: exponential growth, goal-seeking decay, overshoot-and-collapse, and S-curve saturation.
Explore Dynamic Behavior Patternssimulation.py
Sleep debt and the lure of caffeine
This simulation keeps tabs on lost sleep while caffeine tries to hide the fatigue. It's a fun way to explore habits and half-life effects.
import random
from tys import probe, progress
Simulate how sleep debt accumulates and caffeine masks fatigue.
def simulate(cfg: dict):
import simpy
env = simpy.Environment()
Parameters
sleep_debt_hours = cfg["initial_debt"] # current sleep debt (h)
days = cfg["sim_days"] # simulation length (d)
sleep_need = cfg["sleep_need"] # ideal nightly sleep (h)
coffee_hit = cfg["coffee_mg"] # mg caffeine per cup
half_life = cfg["half_life"] # caffeine half-life (h)
cups_per_3h = cfg["cups_per_3h"] # cups every 3h when sleepy
drink_threshold = cfg["debt_to_drink"] # start coffee above this debt
nap_repay = cfg["nap_repay"] # hours debt repaid by 20-min nap
weekend_nap = cfg["weekend_nap"] # take Saturday nap? (bool)
caffeine_mg = 0.0 # mg in bloodstream
rng = random.Random(cfg["seed"])
hours = 24 * days
done = env.event()
Record current sleep debt and perceived energy.
def recorder():
while True:
energy = max(0, 1 - sleep_debt_hours/10) + caffeine_mg/400
probe("sleep_debt", env.now, sleep_debt_hours)
probe("energy_level", env.now, energy)
yield env.timeout(1)
env.process(recorder())
Simulate caffeine intake and nightly sleep.
def cycle():
nonlocal sleep_debt_hours, caffeine_mg
for h in range(int(hours)):
day = h // 24
hour_in_day = h % 24
caffeine_mg *= 0.5 ** (1/half_life)
if 7 <= hour_in_day < 23:
if sleep_debt_hours > drink_threshold and rng.random() < cups_per_3h: # sleepy binge
caffeine_mg += coffee_hit
progress(int(100*h/hours),
f"☕ {day=}, hour {hour_in_day}: another coffee")
if hour_in_day == 23:
sleep_loss = min(4, caffeine_mg/200) # jittery!
sleep_hours = max(4, sleep_need - sleep_debt_hours - sleep_loss) # can't always repay
sleep_debt_hours = max(0, sleep_debt_hours - sleep_hours + sleep_need) # debt update
if weekend_nap and hour_in_day == 23 and day % 7 == 5:
sleep_debt_hours = max(0, sleep_debt_hours - nap_repay)
progress(int(100*h/hours),
f"🛌 Weekend nap repaid {nap_repay:.1f}h debt")
if sleep_debt_hours > 20:
progress(int(100*h/hours),
"⚠︎ Burnout spiral — consider an intervention!")
yield env.timeout(1)
progress(100)
done.succeed({"final_debt": sleep_debt_hours, "avg_caffeine": caffeine_mg / hours})
env.process(cycle())
env.run(until=done)
return done.value
def requirements():
return {
"builtin": ["micropip", "pyyaml"],
"external": ["simpy==4.1.1"],
}
config.yaml
initial_debt: 6
sim_days: 30
sleep_need: 8
coffee_mg: 100
half_life: 5
cups_per_3h: 0.3
debt_to_drink: 5
nap_repay: 1.5
weekend_nap: true
seed: 42