File size: 7,313 Bytes
d691bd4
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
# -*- 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.")