SIRAnalysis_step5 - gama-platform/gama GitHub Wiki

5. Morris Sensitivity Analysis

Now that we know how much stochasticity contributes to output variability, we can apply the first sensitivity analysis method: Morris. This method screens parameters by their influence on nb_recovered and is the recommended starting point before the more expensive Sobol analysis — it gives qualitative rankings at a fraction of the cost.

In particular, this step presents:

  • The principle of the Morris method and its three indices μ, μ* and σ
  • How to declare method morris and configure its key facets
  • How to read and interpret the report file produced by GAMA
  • The computational cost of the method and how to tune it

The model file for this step can be found in: Library models/Tutorials/SIRAnalysis/models/SIR_Morris.gaml


How Morris works

Morris is a One-At-a-Time (OAT) screening method. It samples r random starting points (trajectories) in the parameter space and, from each, changes one parameter at a time while holding all others fixed. The change in output caused by each step is called an elementary effect. After r trajectories, three summary statistics are computed for each parameter:

Index Meaning
μ (mu) Mean of elementary effects — signed average influence. Can cancel if effects are of opposite sign across the space.
μ* (mu_star) Mean of absolute elementary effects — the main ranking index, robust to non-monotonic models.
σ (sigma) Standard deviation of elementary effects — captures non-linearity and interactions with other parameters.

The μ* vs σ plane is the standard diagnostic plot for Morris results:

μ* σ Interpretation
High Low Strong, approximately linear effect
High High Strong effect with interactions or non-linearities
Low Low Negligible parameter — can be fixed
Low High Influential only through interactions

Total run count

For r trajectories and k parameters, Morris requires exactly r × (k + 1) simulation runs. With sample: 10 and k = 5 parameters:

10 × (5 + 1) = 60 runs

Increasing sample improves the robustness of the indices but scales linearly with cost. A value of 10–20 trajectories is standard for a first screening.


The method morris statement

The facets are:

  • outputs: — list of global variables to use as sensitivity targets. Mandatory.
  • report: — path to the output file. Use .txt for a human-readable report. Mandatory.
  • results: — path to the raw CSV with one row per run. Optional but recommended.
  • levels: — number of grid levels for the OAT design. The default value of 4 is appropriate for most models. Must be even.
  • sample: — number of trajectories r. More trajectories give more stable estimates.

Reading the report

After the run, morris_report.txt contains one block per output variable listing μ, μ* and σ for each parameter. Here is an example obtained on our SIR model:

Morris Report

Reading by μ* rank: nb_agents and infection_rate dominate, followed at a distance by infection_distance and recovery_rate. nb_infected_init has negligible direct influence. The high σ of infection_rate relative to its μ* suggests it acts mainly through interactions rather than as a direct driver.

The morris_rax.csv contains the sampling strategy used by the experiment.


Complete model

model SIR

global {
    int    nb_agents          <- 200  min: 50   max: 500;
    int    nb_infected_init   <- 5    min: 1    max: 50;
    float  infection_rate     <- 0.5  min: 0.0  max: 1.0;
    int    infection_distance <- 5    min: 1    max: 20;
    int    recovery_time      <- 50   min: 10   max: 200;
    int    max_steps          <- 1000;

    int nb_susceptible <- 0;
    int nb_infected    <- 0;
    int nb_recovered   <- 0;

    init {
        create person number: nb_agents;
        ask nb_infected_init among (person as list) {
            status          <- "infected";
            infection_timer <- recovery_time;
        }
    }

    reflex update_counts {
        nb_susceptible <- person count (each.status = "susceptible");
        nb_infected    <- person count (each.status = "infected");
        nb_recovered   <- person count (each.status = "recovered");
    }

    reflex stop when: cycle >= max_steps or nb_infected = 0 {
        do halt;
    }
}

species person skills: [moving] {
    string status <- "susceptible";
    int infection_timer <- 0;
    float speed <- 1.0;

    reflex move {
        do wander();
    }

    reflex infect when: status = "infected" {
        ask (person at_distance infection_distance) where (each.status = "susceptible") {
            if flip(infection_rate) {
                status          <- "infected";
                infection_timer <- recovery_time;
            }
        }
        infection_timer <- infection_timer - 1;
        if infection_timer <= 0 {
            status <- "recovered";
        }
    }

    aspect base {
        draw circle(1) at: location color:
    		(status = "infected") ? #red : ((status = "recovered") ? #blue : #green);
    }    
}


experiment SIR_gui type: gui {
    parameter "Number of agents"    var: nb_agents;
    parameter "Initially infected"  var: nb_infected_init;
    parameter "Infection rate"      var: infection_rate;
    parameter "Infection distance"  var: infection_distance;
    parameter "Recovery time"       var: recovery_time;

    output {
        display "Population" type: java2D {
            species person aspect: base;
        }
        display "SIR Chart" type: java2D {
            chart "SIR dynamics" type: series {
                data "Susceptible" value: nb_susceptible color: #green;
                data "Infected"    value: nb_infected    color: #red;
                data "Recovered"   value: nb_recovered   color: #blue;
            }
        }
        monitor "Susceptible" value: nb_susceptible;
        monitor "Infected"    value: nb_infected;
        monitor "Recovered"   value: nb_recovered;
        monitor "Cycle"       value: cycle;
    }
}

// Total runs = sample × (k + 1) = 10 × 6 = 60 runs
experiment SIR_Morris type: batch
    repeat:    1
    keep_seed: false
    until: (cycle >= max_steps or nb_infected = 0) {

    parameter "Number of agents"    var: nb_agents          min: 50  max: 500;
    parameter "Initially infected"  var: nb_infected_init   min: 1   max: 50;
    parameter "Infection rate"      var: infection_rate     min: 0.0 max: 1.0;
    parameter "Infection distance"  var: infection_distance min: 1   max: 20;
    parameter "Recovery time"       var: recovery_time      min: 10  max: 200;

    method morris
        outputs: ["nb_recovered"]
        report:  "../results/morris_report.txt"
        results: "../results/morris_raw.csv"
        levels:  4
        sample:  10;
}

What's next?

Morris gives qualitative rankings. Step 6 introduces Sobol, which goes further by quantifying exactly how much of the output variance each parameter explains — both directly (S1) and through interactions (ST).