homeostasis / app.py
ProfRick's picture
Create app.py
d691bd4 verified
# -*- coding: utf-8 -*-
import streamlit as st
import numpy as np
import matplotlib.pyplot as plt
st.set_page_config(page_title="Homeostasis Explorer", layout="centered")
# -----------------------------
# Systems with disturbance-specific roles
# -----------------------------
SYSTEMS = {
"Glucose": {
"setpoint": 90.0,
"units": "mg/dL",
"events": {
"Meal (carbs) – hyperglycemia": {
"disturbance_magnitude": +40.0,
"roles": {
"Sensor": "Pancreatic β-cells (detect ↑ glucose)",
"Control Center": "Pancreatic β-cells (insulin secretion)",
"Effector(s)": "Insulin → liver (↑glycogenesis, ↓gluconeogenesis), muscle/adipose (↑GLUT4 uptake)"
},
"note": "Hyperglycemia evokes insulin to lower glucose."
},
"Light exercise / fasting – hypoglycemia": {
"disturbance_magnitude": -20.0,
"roles": {
"Sensor": "Pancreatic α-cells (detect ↓ glucose)",
"Control Center": "Pancreatic α-cells (glucagon secretion)",
"Effector(s)": "Glucagon → liver (↑glycogenolysis, ↑gluconeogenesis); adipose (↑lipolysis)"
},
"note": "Hypoglycemia evokes glucagon and counter-regulatory hormones to raise glucose."
}
},
"k_time_constant": 6.0,
"feedback_gain": 0.35
},
"Temperature": {
"setpoint": 37.0,
"units": "°C",
"events": {
"Cold exposure (↓T)": {
"disturbance_magnitude": -5.0,
"roles": {
"Sensor": "Peripheral & central thermoreceptors (detect ↓temp)",
"Control Center": "Hypothalamus (thermoregulatory center)",
"Effector(s)": "Shivering, cutaneous vasoconstriction, (± brown fat thermogenesis)"
},
"note": "Cold evokes heat production & conservation."
},
"Heat exposure (↑T)": {
"disturbance_magnitude": +2.5,
"roles": {
"Sensor": "Peripheral & central thermoreceptors (detect ↑temp)",
"Control Center": "Hypothalamus",
"Effector(s)": "Sweating, cutaneous vasodilation"
},
"note": "Heat evokes heat dissipation."
}
},
"k_time_constant": 5.0,
"feedback_gain": 0.40
},
"Blood Pressure (MAP)": {
"setpoint": 95.0,
"units": "mmHg (~MAP)",
"events": {
"Standing quickly (orthostatic ↓MAP)": {
"disturbance_magnitude": -15.0,
"roles": {
"Sensor": "Baroreceptors (carotid sinus, aortic arch) sense ↓ stretch",
"Control Center": "Medullary cardiovascular centers",
"Effector(s)": "↑HR, ↑contractility, arteriolar vasoconstriction, venoconstriction"
},
"note": "Baroreflex counters an acute drop in MAP."
},
"Pain/startle (↑MAP surge)": {
"disturbance_magnitude": +10.0,
"roles": {
"Sensor": "Baroreceptors sense ↑ stretch",
"Control Center": "Medullary cardiovascular centers",
"Effector(s)": "↑Vagal tone/↓sympathetic tone → ↓HR, ↓contractility, vasodilation"
},
"note": "Baroreflex counters an acute rise in MAP."
}
},
"k_time_constant": 4.5,
"feedback_gain": 0.45
},
"pH": {
"setpoint": 7.40,
"units": "pH",
"events": {
"Hypoventilation (resp. acidosis tendency)": {
"disturbance_magnitude": -0.10,
"roles": {
"Sensor": "Central & peripheral chemoreceptors (↑CO₂/H⁺)",
"Control Center": "Medullary respiratory centers",
"Effector(s)": "↑Ventilation (↑RR and/or tidal volume) to blow off CO₂"
},
"note": "Ventilation rapidly raises pH back toward 7.4."
},
"Hyperventilation (resp. alkalosis tendency)": {
"disturbance_magnitude": +0.10,
"roles": {
"Sensor": "Central & peripheral chemoreceptors (↓CO₂/H⁺)",
"Control Center": "Medullary respiratory centers",
"Effector(s)": "↓Ventilation (↓RR and/or tidal volume) to retain CO₂"
},
"note": "Ventilation lowers pH back toward 7.4."
}
},
"k_time_constant": 3.5,
"feedback_gain": 0.50
}
}
# -----------------------------------
# Simple negative-feedback model
# x' = D(t) - k*(x - set) - g*(x - set)
# -----------------------------------
def simulate(system_name, event_name, duration=60.0, dt=0.1):
sys = SYSTEMS[system_name]
setpoint = sys["setpoint"]
k = 1.0 / sys["k_time_constant"]
g = sys["feedback_gain"]
t = np.arange(0.0, duration + dt, dt)
x = np.ones_like(t) * setpoint
D = np.zeros_like(t)
mag = sys["events"][event_name]["disturbance_magnitude"]
D[(t >= 5.0) & (t < 15.0)] = mag
for i in range(1, len(t)):
error = x[i-1] - setpoint
feedback = -g * error
dxdt = D[i-1] - k*error + feedback
x[i] = x[i-1] + dxdt * dt
return t, x, setpoint, sys["units"]
# -----------------------------
# Streamlit UI
# -----------------------------
st.title("Homeostasis Explorer: Negative Feedback")
st.markdown(
"""Select a **System** and a **Disturbance**, then click **Run Simulation**.
Roles update based on the disturbance; the plot shows the variable returning toward its set point."""
)
# Controls
system = st.selectbox("System", list(SYSTEMS.keys()), index=0)
event = st.selectbox("Disturbance", list(SYSTEMS[system]["events"].keys()), index=0)
# Run button with initial auto-render
if "init" not in st.session_state:
st.session_state.init = True
run = st.button("Run Simulation")
if st.session_state.init or run:
st.session_state.init = False
t, x, setpoint, units = simulate(system, event, duration=60.0, dt=0.1)
fig, ax = plt.subplots(figsize=(8, 4))
ax.plot(t, x, label="Observed variable")
ax.axhline(setpoint, linestyle="--", label=f"Set point = {setpoint} {units}")
ax.axvspan(5.0, 15.0, alpha=0.1, label="Disturbance window")
ax.set_xlabel("Time (a.u.)")
ax.set_ylabel(f"{system} ({units})")
ax.set_title(f"{system} response to: {event}")
ax.legend(loc="best")
st.pyplot(fig)
roles = SYSTEMS[system]["events"][event]["roles"]
note = SYSTEMS[system]["events"][event]["note"]
st.subheader(f"Feedback Roles — _{event}_")
st.markdown(
f"- **Sensor:** {roles['Sensor']}\n"
f"- **Control Center:** {roles['Control Center']}\n"
f"- **Effector(s):** {roles['Effector(s)']}"
)
st.caption(f"*Note:* {note}")
st.info("Tip: Adjust the selections and click **Run Simulation** again to compare responses.")