Spaces:
Running
Running
| # -*- 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.") | |