Sensitivity analysis
Global sensitivity analysis (GSA) asks how much each input parameter contributes to the variability of a model output. ModelManager provides three generic GSA methods that work with any backend: Morris one-at-a-time screening, Sobol' variance decomposition, and RBD-FAST.
All three are subtypes of GSAMethod and share one entry point — run:
run(method, inputs, variations; functions = [f1, f2, ...])inputs— the baseInputFolders(or a reference monad).variations— the parameters to analyze, asDistributedVariations (orCoVariations), so each can be sampled across its range.functions— output functions of the formsimulation -> Real, orQoIs. Each is called once per simulation with aSimulation, and the values are combined over a monad's replicates bymean— pass aQoIto choose a different reduction.
run builds the appropriate sampling design, runs the simulations, evaluates your output functions, and returns a GSASampling result holding the sensitivity indices.
One measurement, several quantities
An entry of functions need not define a single output. If a QoI's reduce returns a Dict or NamedTuple instead of a number, each key becomes its own sensitivity analysis, labelled "<qoi name>.<key>":
counts = QoI("counts", finalPopulationCount;
reduce = per_sim -> Dict(k => mean(getindex.(per_sim, k)) for k in ("tumor", "immune")))
gsa = run(MOAT(15), inputs, dists; functions=[counts])
ModelManager.gsaLabels(gsa) # ["counts.immune", "counts.tumor"]
gsa.results["counts.tumor"]This is what lets the same measurement feed all three consumers: a Dict-valued quantity already worked as a CalibrationProblem's summary_statistic and as a post_processor, and no longer has to be rewritten once per key to ask a sensitivity question about it.
Two constraints follow from what a sensitivity index needs:
- Every monad must produce the same keys. Each key's indices are computed over a value from every monad in the design, so a monad missing one leaves a hole — and unlike
mseDistance, which treats an absent key as zero, there is no fill that makes the answer merely approximate rather than wrong. A mismatch is refused, naming both key sets. - A
Vectoris not spread by index. Only its length could be checked against the other monads', and equal length is not equal meaning: two series sampled at different times have the same length and different contents. Return aDictwhose keys name the components — you know what they mean, and the framework does not.
A QoI's name may not contain a ., since that is the separator: reserving it is what lets a label be read back to the QoI that produced it, and so what lets calculateGSA! decide from a name alone whether that measurement has already been evaluated.
MOAT — Morris screening
MOAT (Morris One-At-A-Time) is a cheap screening method: it perturbs one parameter at a time around a set of base points and measures the resulting "elementary effects." Good for quickly ranking which parameters matter before committing to a more expensive analysis.
gsa = run(MOAT(15), inputs, [dist1, dist2, dist3]; functions=[final_count])
gsa = run(MOAT(10; add_noise=true), inputs, dists; functions=[final_count])The integer is the number of base points (trajectories).
Sobol' — variance decomposition
Sobolʼ computes variance-based first-order and total-order indices: the fraction of output variance attributable to each parameter alone, and including all its interactions.
gsa = run(Sobolʼ(256), inputs, dists; functions=[final_count])
gsa = run(Sobolʼ(256; sobol_index_methods=(first_order=:Jansen1999, total_order=:Jansen1999)),
inputs, dists; functions=[final_count])The type is spelled Sobolʼ (with a rasp/prime, \rasp<tab> in the Julia REPL or VS Code) to avoid clashing with the Sobol package. The ASCII alias SobolMM is identical if you prefer to avoid the Unicode character.
RBD-FAST
RBD (Random Balance Design / Fourier Amplitude Sensitivity Test) estimates first-order indices from a single design by analyzing the output's frequency content.
gsa = run(RBD(128), inputs, dists; functions=[final_count])
gsa = run(RBD(128; num_harmonics=10), inputs, dists; functions=[final_count])Working with the result
The returned GSASampling carries the underlying Sampling and the computed indices. Helpers include:
gsaLabels— the labels of the analyses computed, sorted. Each indexesgsa.results.calculateGSA!— compute indices for a set of output functions, filing them under their labels. A measurement already evaluated is skipped, so adding a quantity later costs only the new one. Passrecompute=truewhen the measurement itself has changed — nothing can detect that, since redefining a function's body in place leaves it indistinguishable from the one already evaluated.getMonadIDDataFrame— the monad-ID design matrix used.simulationIDs— the simulations that were run.methodString— a label for the method/design.
Indices themselves live in gsa.results, a Dict keyed by those labels — one entry per quantity, which is one per functions entry unless a QoI spread into several.
Because GSA is built on the same space-filling designs and the same runner, its simulations are deduplicated and reused like any other trial. See the Sensitivity analysis (GSA) API reference for full details.
Visualizing GSA results
When a plotting backend is loaded, RecipesBase recipes turn each sampling result into a sensitivity chart. Every recipe draws one series per output function in the result (the series label includes the function name when more than one function was supplied); the x-axis is the varied parameters.
using Plots
# MOAT (Morris) — three chart styles selected by a positional symbol
plot(moat) # :bar (default) — µ* per parameter
plot(moat; show_sigma=true) # add σ as ±whiskers on the µ* bars
plot(moat, :scatter) # classic µ*–σ screening scatter, points labeled
plot(moat, :violin) # full elementary-effect distribution per parameter
# Sobol' — first-order (S1) bars, with total-order (ST) overlaid at reduced opacity
plot(sobol) # S1 + ST
plot(sobol; show_ST=false) # S1 only
# RBD — first-order index bars
plot(rbd)The :violin style needs a backend that provides the :violin series type (e.g. StatsPlots); the others work with any Plots- or Makie-compatible backend. See MOATSampling, SobolSampling, and RBDSampling for the full recipe documentation.