Best practices
This section draws on the framework reviews to take a position on how each modeling concept should be expressed. Each position was checked against one test: a model should never be easier to write without the lingua franca than with it. Code is written as Starsim (import starsim as ss). For how to use these features, see the Starsim docs.
The positions in brief
- Declare transitions as data, in one vocabulary at several lengths. Operators (
S >> I @ beta * I) andss.Transition(...)build the same object, so the library can diagram, stratify, and run the model under different solvers. See model-structure. - Guess the default, and print it. Where the right answer is obvious nearly every time (exponential dwell times, homogeneous mixing, the solver), the library takes it and marks it in
describe(); errors are for cases where a wrong guess would be silently harmful. See principles. - Plain Python is always available and is not second class. Every slot that takes a number also takes a function, and any module can have a
step()method; the library tries it and says plainly where it cannot run. See principles. - The disease says how infectious the pathogen is; the networks say how contact happens. The user never writes
/N: with no network, mixing is homogeneous andbetais the classical transmission rate; with one,betais per contact. See population-and-mixing. - One model, with
solver=choosing how it runs. By default, a purely compartmental model runs as an ODE and anything that needs individuals runs as agents. If you ask for a solver that doesn’t quite fit, the library uses the standard approximation and says so, stopping only when there is none. See paradigm-conversion and compartmental. - Stratification is an operator applied to a finished model. Age, space, risk, strain, and vaccination status are all dimensions added with
stratify(...), and a metapopulation is a model stratified by place with optional mobility. See stratification and metapopulation. - Scale another module’s quantity with
ss.modify, and keep Starsim’s ordered loop. A modifier never overwrites the base value and modifiers combine multiplicatively by default; anything more complex is ordinary code instep(). See composition-and-effects. - An intervention is what plus delivery. Delivery is a few keywords (
eligible=,coverage=,capacity=) or one function, with eligibility written against the model’s real attributes; many interventions are just anss.transferor anss.modify. See interventions. - Results are declared with the model, and fitting needs no likelihood. Every state gets a count and every named transition a flow, data attaches to results by column name, and
sim.fit()fits every parameter that has bounds to every result that has data. See results-and-observation and calibration. - Keep what Starsim already gets right. Its time types, per-distribution random number streams, common random numbers, agent architecture, and single module abstraction stay. See time-and-units, stochasticity-and-reproducibility, and agent-based.
The reading guide says how each document is laid out and lists them all.
A separate review, cost modeling, looks at how disease models handle costs and health economic outcomes such as DALYs.