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