@@ -62,6 +62,19 @@ def format_noise_equation(terms: tuple[tuple[str, int], ...]) -> str:
6262 return rf"\eta(x) = \frac{{1}}{{{ len (pieces )} }}\left({ joined } \right)"
6363
6464
65+ def format_initial_conditions_latex (terms : tuple [tuple [str , int ], ...]) -> str :
66+ """Return the perturbation and initial conditions as one LaTeX block."""
67+ noise_equation = format_noise_equation (terms )
68+ return rf"""
69+ \begin{{aligned}}
70+ { noise_equation } \\
71+ u(x, 0) &= u_* \left(1 + \varepsilon \eta(x)\right) \\
72+ v(x, 0) &= v_* \left(1 + \varepsilon \eta(x)\right) \\
73+ \varepsilon &= { PERTURBATION_AMPLITUDE :.2f}
74+ \end{{aligned}}
75+ """
76+
77+
6578def initialize_state (
6679 x : np .ndarray ,
6780 a : float ,
@@ -129,10 +142,28 @@ def simulate_gierer_meinhardt(
129142 return x , times , v_history
130143
131144
132- def plot_profile (x : np .ndarray , values : np .ndarray , time_value : float ) -> plt .Figure :
145+ def simulation_ylim (v_history : np .ndarray ) -> tuple [float , float ]:
146+ """Return fixed y-limits based on the full stored simulation."""
147+ y_min = float (np .min (v_history ))
148+ y_max = float (np .max (v_history ))
149+ span = y_max - y_min
150+ if np .isclose (span , 0.0 ):
151+ margin = max (0.05 * max (abs (y_max ), 1.0 ), 1e-3 )
152+ else :
153+ margin = 0.05 * span
154+ return y_min - margin , y_max + margin
155+
156+
157+ def plot_profile (
158+ x : np .ndarray ,
159+ values : np .ndarray ,
160+ time_value : float ,
161+ y_limits : tuple [float , float ],
162+ ) -> plt .Figure :
133163 """Plot the inhibitor profile at one stored time."""
134164 fig , ax = plt .subplots (figsize = (9 , 4 ))
135165 ax .plot (x , values , color = "#1f77b4" , linewidth = 2 )
166+ ax .set_ylim (* y_limits )
136167 ax .set_xlabel ("x" )
137168 ax .set_ylabel ("v(x, t)" )
138169 ax .set_title (f"Gierer-Meinhardt 1D, t = { time_value :.2f} " )
@@ -237,11 +268,7 @@ def render_page() -> None:
237268 st .caption ("The parameters b = 1 and gamma = 1 are fixed." )
238269 st .markdown ("### Custom Perturbation" )
239270 terms = render_term_controls ()
240- st .latex (format_noise_equation (terms ))
241- st .caption (
242- "The initial condition is u(x, 0) = u_* [1 + epsilon eta(x)] and "
243- "v(x, 0) = v_* [1 + epsilon eta(x)] with epsilon = 0.01."
244- )
271+ st .latex (format_initial_conditions_latex (terms ))
245272
246273 if st .button ("Start" , type = "primary" ):
247274 with st .spinner ("Running 1000 stored time steps..." ):
@@ -276,6 +303,7 @@ def render_page() -> None:
276303 simulation ["x" ],
277304 simulation ["v_history" ][time_index ],
278305 float (simulation ["times" ][time_index ]),
306+ simulation_ylim (simulation ["v_history" ]),
279307 )
280308 st .pyplot (figure )
281309 plt .close (figure )
0 commit comments