2. Streamlit fundamentals for CAE

Streamlit's execution model is the single most important concept to internalize before building any CAE analytics application. Unlike a traditional web framework where you write request handlers, a Streamlit script runs top to bottom on every user interaction. When a user drags a slider, clicks a button, or uploads a file, the entire script re-executes from line one. Understanding this rerun model explains why widgets behave the way they do, why session state exists, and why callbacks matter for simulation workflows where expensive computations must not be triggered accidentally.

The Rerun Model and Widget Identity

Every widget you create returns a value. That value is available only during the current run. On the next rerun, the widget re-creates itself and returns its current value again. Streamlit identifies widgets by a combination of their type, label, and an optional key. If you generate widgets inside a loop without stable keys, Streamlit may confuse them across reruns, producing duplicated widget errors or lost values.

import streamlit as st

st.title("Modal Frequency Explorer")

# Stable key ensures the widget keeps its identity across reruns
f_min = st.slider("Minimum frequency (Hz)", 0.0, 500.0, 10.0, key="f_min")
f_max = st.slider("Maximum frequency (Hz)", 0.0, 500.0, 200.0, key="f_max")

st.write(f"Analyzing modes between {f_min:.1f} Hz and {f_max:.1f} Hz")

For CAE data, this pattern is common: you load a result file, expose sliders for frequency bands, load cases, or time steps, and filter the dataset on each rerun. The filtering itself is cheap, so rerunning is fine. The problem arises when the rerun triggers a solver call, a large file parse, or a database query.

Session State: Persisting Simulation Context

st.session_state is a dictionary-like object that survives reruns. It is the correct place to store loaded result sets, solver handles, user selections that must persist, and computed caches. Without it, every rerun would reload a 200 MB result file.

if "results" not in st.session_state:
    st.session_state.results = load_result_set("modal_analysis.h5")

results = st.session_state.results
modes = results.query(frequencies=(f_min, f_max))
st.dataframe(modes.summary())

A practical rule for CAE apps: keep inputs in widgets, keep expensive artifacts in session state, and keep derived views as plain local variables recomputed each run. This separation prevents stale data and keeps memory usage predictable.

Callbacks: Controlling When Work Happens

Callbacks run before the script reruns, which makes them ideal for validating input, updating dependent widgets, or triggering a computation only when a button is pressed. The on_click and on_change parameters accept a function.

def run_solver():
    st.session_state.solver_output = solve(
        st.session_state.mesh_id,
        st.session_state.material,
    )

st.button("Run solve", on_click=run_solver, key="solve_btn")

Without a callback, a button click would still rerun the script, but the solve would execute on every subsequent rerun too if you placed it unconditionally. Callbacks scope the work to the interaction itself. For long-running CAE jobs, combine a callback with a status placeholder and a progress bar updated from a worker thread or a polling loop.

Layout Primitives for Engineering Dashboards

Streamlit offers several layout containers. Choosing the right one keeps a CAE dashboard readable when it mixes controls, tables, plots, and 3D views.

ContainerTypical CAE use
st.sidebarGlobal controls: load case, units, solver tolerance
st.columnsSide-by-side comparison of two result sets or two plots
st.tabsSeparating geometry, mesh, boundary conditions, and results
st.expanderCollapsing raw metadata or solver logs
st.containerGrouping a plot with its caption and export button

A common pattern is a sidebar for global simulation settings, a main area split into two columns for pre-processing and post-processing, and tabs for different result types. Avoid nesting more than two levels of columns; deep nesting breaks on narrow screens and makes the layout fragile.

Wiring It Together: A Minimal CAE Analytics Flow

The diagram below shows how a single user interaction propagates through a Streamlit CAE app. The key insight is that the rerun is the central mechanism, and session state is what makes the rerun cheap and consistent.

User interaction slider / button / upload Callback (optional) validate / trigger solve Script rerun top-to-bottom execution st.session_state cached result sets · solver handles · persistent selections Widgets return current values Rendered output tables · plots · 3D views · status messages Every interaction re-executes the script; session state preserves expensive artifacts across reruns.

Practical Guidance for Simulation Data

  • Cache aggressively, invalidate deliberately. Use @st.cache_data for pure functions that parse files or compute derived quantities, and @st.cache_resource for solver sessions or database connections. Key the cache on the file path and a modification timestamp so a re-exported result set is picked up.
  • Never mutate cached objects. Cached data is shared across sessions. Copy before modifying, or store mutable state in st.session_state instead.
  • Guard expensive operations with a button. A slider that triggers a 30-second solve on every drag is unusable. Bind the solve to a button callback and let sliders only update the preview.
  • Use st.form for batch input. When a user must set several parameters before a single solve, wrap them in a form so the script reruns only on submit, not on every field change.
  • Show progress for long jobs. st.progress and st.status give users feedback during mesh generation or transient solves, which is essential when a run takes minutes.
When debugging unexpected reruns, add st.write(st.session_state) at the top of your script. Watching which keys change between interactions quickly reveals whether a widget is losing its identity or a callback is firing more often than intended.
Do not store large NumPy arrays or DataFrames directly in widget return values and expect them to persist. Widgets return scalars, strings, and small selections. Bulk simulation data belongs in session state or a cache, referenced by a lightweight key.

With these fundamentals in place, you can structure a CAE analytics app so that controls, data loading, computation, and visualization each live in the right place. The next step is applying this structure to real result files and building the first end-to-end post-processing view.