codexhomeostasis-temperature-feedback-circuit.py
homeostasis-temperature-feedback-circuit.py
"""Original circuit and synthetic thermal response; Python 3.14, NumPy 2.3.5,
Matplotlib 3.10.7. Run from _media; write an opaque PNG to the current directory.
All parameters are invented for teaching, not fitted physiological data.
"""
from pathlib import Path
import matplotlib.pyplot as plt
from matplotlib.patches import FancyArrowPatch, FancyBboxPatch
import numpy as np
def pulse_response(t, gain, tau=4.0, start=2.0, stop=10.0):
"""Exact solution of tau*x' = d - (1+gain)*x for a unit pulse."""
rate = (1.0 + gain) / tau
elapsed = np.clip(t - start, 0, stop - start)
return -np.expm1(-rate * elapsed) / (1.0 + gain) * np.exp(
-rate * np.maximum(t - stop, 0)
)
def main():
plt.rcParams.update({"font.size": 11, "axes.spines.top": False,
"axes.spines.right": False, "savefig.facecolor": "white"})
fig = plt.figure(figsize=(10.4, 8.0), facecolor="white")
gs = fig.add_gridspec(3, 1, height_ratios=[2.5, 0.8, 1.5], hspace=0.5)
ax = fig.add_subplot(gs[0])
ax.set(xlim=(0, 10), ylim=(0, 5))
ax.axis("off")
def box(x, y, w, h, text, color):
ax.add_patch(FancyBboxPatch((x, y), w, h, boxstyle="round,pad=0.08",
facecolor=color, edgecolor="#334155"))
ax.text(x+w/2, y+h/2, text, ha="center", va="center", fontsize=11)
def arrow(points, color="#334155", dashed=False):
for a, b in zip(points[:-2], points[1:-1]):
ax.plot([a[0], b[0]], [a[1], b[1]], color=color,
linestyle="--" if dashed else "-", linewidth=1.7)
ax.add_patch(FancyArrowPatch(points[-2], points[-1], arrowstyle="-|>",
mutation_scale=13, linewidth=1.7, color=color,
linestyle="--" if dashed else "-"))
box(.25, 2.45, 2.0, 1.0, "Sensor\nInternal temperature", "#dbeafe")
box(3.75, 2.45, 2.3, 1.0, "Integrator\nHypothalamic network", "#e0e7ff")
box(7.55, 2.45, 2.2, 1.0, "Effectors\nSweat / skin blood flow", "#dcfce7")
box(3.75, .2, 2.3, .9, "Regulated variable\nCore temperature", "#fef3c7")
arrow([(2.35, 2.95), (3.65, 2.95)])
arrow([(6.15, 2.95), (7.45, 2.95)])
ax.text(3.0, 3.16, "information", ha="center", fontsize=10)
ax.text(6.8, 3.16, "command", ha="center", fontsize=10)
arrow([(8.65, 2.35), (8.65, .65), (6.15, .65)])
ax.text(7.6, 1.65, "Cooling counters\nwarming", ha="center", fontsize=10)
arrow([(3.65, .65), (1.25, .65), (1.25, 2.35)])
ax.text(2.35, 1.65, "Sensed again", ha="center", fontsize=10)
ax.text(4.9, 4.75, "Current reference", ha="center")
arrow([(4.9, 4.5), (4.9, 3.55)])
ax.text(8.6, 4.75, "Skin-temperature input", ha="center", color="#7c3aed")
arrow([(8.6, 4.5), (8.6, 3.9), (6.7, 3.9), (6.05, 3.55)],
color="#7c3aed", dashed=True)
ax.text(8.4, 4.12, "Can anticipate a core change", ha="center", fontsize=9,
color="#7c3aed")
ax.text(4.9, 1.95, "Added heat load", ha="center", color="#b45309")
arrow([(4.9, 1.7), (4.9, 1.2)], color="#b45309")
ax.set_title("Qualitative thermoregulation circuit", loc="left", fontweight="bold")
t = np.linspace(0, 18, 1801)
load = ((t >= 2) & (t < 10)).astype(float)
disturbance = fig.add_subplot(gs[1])
disturbance.step(t, load, where="post", color="#b45309", linewidth=2)
disturbance.set(ylabel="Heat load d", ylim=(-.05, 1.2), xlim=(0, 18))
disturbance.set_title("SYNTHETIC PLOTS — invented parameters; no measured data",
loc="left", fontsize=12, fontweight="bold")
response = fig.add_subplot(gs[2], sharex=disturbance)
response.plot(t, pulse_response(t, 0), color="#64748b", linewidth=2.4,
label="Passive loss only (k = 0)")
response.plot(t, pulse_response(t, 3), color="#0369a1", linewidth=2.4,
label="Negative feedback (k = 3)")
response.axhline(0, color="#94a3b8", linewidth=.8)
response.set(ylabel="Displacement x", xlabel="Time (arbitrary units)",
ylim=(-.02, .96))
response.legend(loc="upper right", fontsize=10, frameon=False)
for a in (disturbance, response):
a.axvspan(2, 10, color="#fef3c7", alpha=.45)
a.grid(alpha=.15)
fig.subplots_adjust(left=.12, right=.97, top=.94, bottom=.12)
fig.text(.12, .025, "Lower model: 4 dx/dt = d − x − kx. All plotted quantities are scaled and synthetic.",
fontsize=10, color="#475569")
fig.savefig(Path.cwd() / (Path(__file__).stem + ".png"), dpi=120,
facecolor="white", transparent=False)
plt.close(fig)
if __name__ == "__main__":
main()