8  Regions Plugin

The regions plugin manages the assignment of people to regions. As such, it is dependent on the people plugin. A region does not have an associated geo-location and does not specifically designate a county, state or any other regional concept. The interpretation of what defines a region is left to the modeler, although it will usually represent some sort of contiguous land area. When the regions plugin is being used, each person has a region association at all times and regions can be associated with zero to many people. Regions may also have a set of associated property values that can be dynamically defined. Regions are identified via the RegionId marker interface that does not define any methods. It is left to the modeler to implement a RegionId data type. When the number of regions is fixed and relatively small this can be accomplished via an enumeration. For larger or dynamic sets of region id values it is typical to implement a simple immutable class that wraps an integer.

8.1 Plugin Data Initialization

The plugin is initialized using a RegionsPluginData object that collects person to region assignments and region property values.

8.2 Plugin Behavior

The plugin adds a single data manager to the simulation as an instance of the RegionsDataManager that is initialized with the RegionsPluginData.

8.3 Data Manager

The data manager controls regions, their properties and the assignment of people to those regions. The data manager provides public methods that:

  • Add a region
  • Define a region property
  • Set a region property value
  • Move a person from one region to another
  • Answer various questions about:
    • Person membership in regions
    • Region property values

The data manager also produces observable events:

  • PersonRegionUpdateEvent – when a person is moved from one region to another
  • RegionAdditionEvent – when a region is added to the simulation
  • RegionPropertyDefintionEvent – when a new region property is defined
  • RegionPropertyUpdateEvent – when a region property value is assigned

8.4 Example Code (Lesson 15)

Example_15.java shows the use of the regions plugin. In it we will examine

  • The initialization of the regions plugin
  • The movement of people between regions
  • The dynamic addition of regions
  • The dynamic addition of region properties
  • The update of region property values

The example includes five plugins:

  • Regions Plugin– (GCM core plugin) used to manage regions, their properties and person membership in regions
  • People plugin – (GCM core plugin) used to manage people
  • Stochastics plugin – (GCM core plugin) used to generate random numbers used in various decisions
  • Model plugin – (local plugin) used to introduce three actors that will:
    • Move people between regions
    • Create new regions
    • Vaccinate people, reacting to changes in region properties
  • Vaccine plugin – (local plugin) used to track vaccinations for each person

The example’s main method starts in Code Block 8.1 by creating an instance of the example class rather than building the experiment directly since this example is somewhat more complex than previous examples.

Code Block 8.1: Executing example 15 with an output directory.
public static void main(String[] args) throws IOException {
    if (args.length == 0) {
        throw new RuntimeException("One output directory argument is required");
    }
    Path outputDirectory = Paths.get(args[0]);
    if (!Files.exists(outputDirectory)) {
        Files.createDirectory(outputDirectory);
    } else {
        if (!Files.isDirectory(outputDirectory)) {
            throw new IOException("Provided path is not a directory");
        }
    }

    new Example_15(outputDirectory).execute();
}

The execution method first gathers together the five plugins in Code Block 8.2:

Code Block 8.2: The various plugins are gathered from their initial data.
private void execute() {
    /*
         * Create person ids and region ids that are shared across the plugins
         */
    initializePeopleAndRegions();

    /*
         * Create the reports
         */

    NIOReportItemHandler nioReportItemHandler = getNIOReportItemHandler();

    /*
         * Create the people plugin filled with 1000 people
         */
    Plugin peoplePlugin = getPeoplePlugin();

    /*
         * Create the region plugin 5 regions, each having a lat and lon and assign the
         * people to random regions.
         * 
         */
    Plugin regionsPlugin = getRegionsPlugin();

    /*
         * create the stochastics plugin and build a dimension with 5 seed values
         */
    Plugin stochasticsPlugin = getStochasticsPlugin();
    Dimension stochasticsDimension = getStochasticsDimension(5, randomGenerator.nextLong());

    /*
         * Create the vaccine and model plugins
         */
    Plugin vaccinePlugin = VaccinePlugin.getVaccinePlugin();

    Plugin modelPlugin = ModelPlugin.getModelPlugin();

The first action is to create 1000 people and 5 regions that will be used in the creation of both the people plugin and the regions plugin.

Code Block 8.3: Lists of initial people and regions are created and will be used to initialize the various plugins.
private void initializePeopleAndRegions() {
    for (int i = 0; i < 1000; i++) {
        initialPeople.add(new PersonId(i));
    }
    for (int i = 0; i < 5; i++) {
        initialRegions.add(new Region(i));
    }
}

The regions plugin defines a region id with a marker interface. Marker interfaces are used to differentiate arguments and reduce variable type ambiguities while not imposing any particular implementation on the modeler. Region ids might reasonably be implemented as integer based identifiers or as strings that represent place names. In this example we will implement the region ids with an integer based class, the Region (Code Block 8.4), which is a boiler-plate wrapper around an int id value.

Code Block 8.4: The region id is implemented as a wrapper class of int.
public final class Region implements RegionId {

    private final int id;

    /**
     * Constructs the region
     * 
     * @throws ContractException
     *                           <li>{@linkplain ModelError#NEGATIVE_REGION_ID}</li>
     */
    public Region(int id) {
        if (id < 0) {
            throw new ContractException(ModelError.NEGATIVE_REGION_ID);
        }
        this.id = id;
    }

    public int getValue() {
        return id;
    }

    @Override
    public int hashCode() {
        return id;
    }

    @Override
    public boolean equals(Object obj) {
        if (this == obj) {
            return true;
        }
        if (!(obj instanceof Region)) {
            return false;
        }
        Region other = (Region) obj;
        if (id != other.id) {
            return false;
        }
        return true;
    }

    @Override
    public String toString() {
        return "Region_" + id;
    }
}

The example contains three reports in Code Block 8.5:

  • RegionPropertyReport - shows changes to region property values
  • RegionTransferReport - shows movements of people between regions
  • VaccinationReport - shows vaccinations of people
Code Block 8.5: The region property, region transfer and vaccination reports are mapped to distinct file names.
private NIOReportItemHandler getNIOReportItemHandler() {
    return NIOReportItemHandler.builder()//
            .addReport(ModelReportLabel.REGION_PROPERTY_REPORT, //
                    outputDirectory.resolve("region_property_report.csv"))//
            .addReport(ModelReportLabel.REGION_TRANSFER_REPORT, //
                    outputDirectory.resolve("region_transfer_report.csv"))//
            .addReport(ModelReportLabel.VACCINATION, //
                    outputDirectory.resolve("vaccine_report.csv"))//
            .build();
}

The people plugin, Code Block 8.6, is built from the 1000 people created earlier.

Code Block 8.6: The people plugin is initialized with the starting population.
private Plugin getPeoplePlugin() {
    PeoplePluginData.Builder peoplePluginDataBuilder = PeoplePluginData.builder();
    for (PersonId personId : initialPeople) {
        peoplePluginDataBuilder.addPersonRange(new PersonRange(personId.getValue(), personId.getValue()));
    }
    PeoplePluginData peoplePluginData = peoplePluginDataBuilder.build();
    return PeoplePlugin.getPeoplePlugin(peoplePluginData);
}

Creating the regions plugin Code Block 8.7 is a bit more involved. First, the five regions created before are added to the plugin. Since the plugin requires that every person always have a region assignment, we assign a randomly selected region to each person. We define the LAT and LON properties to give the regions a geographic location. Notice that the definitions do not have default values since it does not make sense to say a region has a default position. This then will require that we assign specific latitude and longitude values for each region. Later on we will examine adding a new region property definition dynamically as the simulation is running.

Code Block 8.7: The regions plugin is initialized with the starting regions and people, with each person assigned to a randomly selected region. The two region-based reports are also initialized and added to the region plugin’s data.
private Plugin getRegionsPlugin() {
    // create the region plugin with an initial five regions, each region
    // having 200 people
    RegionsPluginData.Builder regionsPluginDataBuilder = RegionsPluginData.builder();
    for (Region region : initialRegions) {
        regionsPluginDataBuilder.addRegion(region);
    }

    for (PersonId personId : initialPeople) {
        Region region = initialRegions.get(randomGenerator.nextInt(initialRegions.size()));
        regionsPluginDataBuilder.addPerson(personId, region);
    }

    PropertyDefinition propertyDefinition = PropertyDefinition.builder()//
            .setType(Double.class)//
            .setPropertyValueMutability(false)//
            .build();
    regionsPluginDataBuilder.defineRegionProperty(RegionProperty.LAT, propertyDefinition);
    regionsPluginDataBuilder.defineRegionProperty(RegionProperty.LON, propertyDefinition);

    for (Region region : initialRegions) {
        regionsPluginDataBuilder.setRegionPropertyValue(region, RegionProperty.LAT,
                randomGenerator.nextDouble() + 45.0);
        regionsPluginDataBuilder.setRegionPropertyValue(region, RegionProperty.LON,
                randomGenerator.nextDouble() + 128.0);
    }

    RegionsPluginData regionsPluginData = regionsPluginDataBuilder.build();

    RegionPropertyReportPluginData regionPropertyReportPluginData = //
            RegionPropertyReportPluginData.builder()//
                    .setReportLabel(ModelReportLabel.REGION_PROPERTY_REPORT)//
                    .build();

    RegionTransferReportPluginData regionTransferReportPluginData = RegionTransferReportPluginData.builder()//
            .setReportLabel(ModelReportLabel.REGION_TRANSFER_REPORT)//
            .setReportPeriod(ReportPeriod.END_OF_SIMULATION)//
            .build();//

    return RegionsPlugin.builder()//
            .setRegionsPluginData(regionsPluginData)//
            .setRegionPropertyReportPluginData(regionPropertyReportPluginData)//
            .setRegionTransferReportPluginData(regionTransferReportPluginData)//
            .getRegionsPlugin();
}

Adding the stochastics plugin with a corresponding dimension that will create five scenarios proceeds in the usual way in Code Block 8.8:

Code Block 8.8: The stochastics plugin is initialized with a random seed value. A dimension is added to add new seeds to the resulting scenarios.
private Plugin getStochasticsPlugin() {

    WellState wellState = WellState.builder().setSeed(randomGenerator.nextLong()).build();
    StochasticsPluginData stochasticsPluginData = StochasticsPluginData.builder()//
            .setMainRNGState(wellState).build();
    return StochasticsPlugin.getStochasticsPlugin(stochasticsPluginData);
}

private Dimension getStochasticsDimension(int replicationCount, long seed) {
    FunctionalDimensionData.Builder builder = FunctionalDimensionData.builder();//

    RandomGenerator randomGenerator = RandomGeneratorProvider.getRandomGenerator(seed);

    List<Long> seedValues = new ArrayList<>();
    for (int i = 0; i < replicationCount; i++) {
        seedValues.add(randomGenerator.nextLong());
    }

    IntStream.range(0, seedValues.size()).forEach((i) -> {
        builder.addValue("Level_" + i, (context) -> {
            StochasticsPluginData.Builder stochasticsPluginDataBuilder = context
                    .getPluginDataBuilder(StochasticsPluginData.Builder.class);
            long seedValue = seedValues.get(i);
            WellState wellState = WellState.builder().setSeed(seedValue).build();
            stochasticsPluginDataBuilder.setMainRNGState(wellState);

            ArrayList<String> result = new ArrayList<>();
            result.add(Integer.toString(i));
            result.add(Long.toString(seedValue) + "L");

            return result;
        });//
    });

    builder.addMetaDatum("seed index");//
    builder.addMetaDatum("seed value");//

    FunctionalDimensionData functionalDimensionData = builder.build();
    return new FunctionalDimension(functionalDimensionData);
}

Finally, we add the vaccine and model plugins. This will add the vaccine data manager as well as three previously mentioned actors that will be used to demonstrate the various capabilities of the regions plugin.

  • PersonMover – used to move people between regions
  • RegionCreator – used to create new regions during the simulation run
  • Vaccinator – used to vaccinate people, reacting to changes in region properties

The execute method finishes (Code Block 8.9) by constructing and executing the experiment:

Code Block 8.9: The experiment is run with five scenarios, each using distinct random seed values.
Experiment.builder()//
        .addPlugin(modelPlugin)//
        .addPlugin(regionsPlugin)//
        .addPlugin(peoplePlugin)//
        .addPlugin(stochasticsPlugin)//
        .addPlugin(vaccinePlugin)//
        .addExperimentContextConsumer(nioReportItemHandler)//
        .addDimension(stochasticsDimension)//
        .build()//
        .execute();//

8.5 The actors

We will finish this chapter by reviewing the three actors of the model plugin and then examine the three reports.

The PersonMover actor, in Code Block 8.10 and Code Block 8.11, schedules 1000 random moves of a person from one region to another over the course of 100 days.

Code Block 8.10: The person mover actor plans for 1000 movements of people over time.
public void init(ActorContext actorContext) {
    for (int i = 0; i < 1000; i++) {
        double planTime = ((double) i) * 0.1;
        actorContext.addPlan(this::moveRandomPerson, planTime);
    }
}

Moving the person requires that we use the stochastics plugin and the people plugin to select a random person. We next use the regions plugin to first select a random new region for the person and then move the person to that region.

Code Block 8.11: The person mover actor attempts to move a randomly selected person from their current region to a new region.
private void moveRandomPerson(ActorContext actorContext) {
    StochasticsDataManager stochasticsDataManager = actorContext.getDataManager(StochasticsDataManager.class);
    RandomGenerator randomGenerator = stochasticsDataManager.getRandomGenerator();
    PeopleDataManager peopleDataManager = actorContext.getDataManager(PeopleDataManager.class);
    RegionsDataManager regionsDataManager = actorContext.getDataManager(RegionsDataManager.class);

    // pick a random person
    List<PersonId> people = peopleDataManager.getPeople();
    if (people.isEmpty()) {
        return;
    }
    PersonId personId = people.get(randomGenerator.nextInt(people.size()));

    // pick a new random new region for that person
    List<RegionId> regionIds = new ArrayList<>(regionsDataManager.getRegionIds());
    RegionId personRegion = regionsDataManager.getPersonRegion(personId);
    regionIds.remove(personRegion);
    if (regionIds.isEmpty()) {
        return;
    }
    RegionId newPersonRegion = regionIds.get(randomGenerator.nextInt(regionIds.size()));

    // assign the region to the person
    regionsDataManager.setPersonRegion(personId, newPersonRegion);
}

The RegionCreator actor, in Code Block 8.12 and Code Block 8.13, follows a similar pattern, scheduling the creation of five new regions over 101 days.

Code Block 8.12: The region creator actor plans the addition of five new regions.
public void init(ActorContext actorContext) {
    for (int i = 0; i < 5; i++) {
        double planTime = 20 * i + 1;
        actorContext.addPlan(this::addRegion, planTime);
    }
}

When adding a region, we have to be aware that the region will have LAT and LON properties and that these properties were not defined with default values. Thus we must supply values for the region’s latitude and longitude as part of the RegionConstructionData object that is passed to the regions data manager. We will similarly assign a new random Boolean value for the VACCINE_PRIORITY property. The VACCINE_PRIORITY is a dynamically added property that is introduced later. Note that we first check for the existence of the property and only then set a value since setting such a value before the property is defined will result in a runtime exception.

Note

Such considerations are unusual since properties are usually defined in the plugin initialization data or added very early in the simulation before any actors have initialized. We do so here for the purposes of demonstrating dynamic property definitions.

Code Block 8.13: When the region creator actor adds a new region, it assigns a random lat-lon corrdinate and possibly assigns a vaccine priority status to the region.
private void addRegion(ActorContext actorContext) {
    RegionsDataManager regionsDataManager = actorContext.getDataManager(RegionsDataManager.class);
    StochasticsDataManager stochasticsDataManager = actorContext.getDataManager(StochasticsDataManager.class);
    RandomGenerator randomGenerator = stochasticsDataManager.getRandomGenerator();

    Set<Region> regions = regionsDataManager.getRegionIds();
    int maxRegionValue = -1;
    for (Region region : regions) {
        int value = region.getValue();
        maxRegionValue = FastMath.max(value, maxRegionValue);
    }
    Region newRegion = new Region(maxRegionValue + 1);
    Builder regionBuilder = RegionConstructionData.builder().setRegionId(newRegion);
    regionBuilder.setRegionPropertyValue(RegionProperty.LAT, 35 + randomGenerator.nextDouble());
    regionBuilder.setRegionPropertyValue(RegionProperty.LON, 128 + randomGenerator.nextDouble());

    if (regionsDataManager.regionPropertyIdExists(RegionProperty.VACCINE_PRIORITY)) {
        regionBuilder.setRegionPropertyValue(RegionProperty.VACCINE_PRIORITY, randomGenerator.nextBoolean());
    }
    RegionConstructionData regionConstructionData = regionBuilder.build();
    regionsDataManager.addRegion(regionConstructionData);
}

The Vaccinator actor is somewhat more complicated than the other actors. It initializes (Code Block 8.14) by storing references to various data managers for convenience and then plans 5000 vaccinations spread over 100 days. It also plans to add the VACCINE_PRIORITY property on day 50.

Code Block 8.14: The vaccinator initializes by planning the vaccination of 5000 people carried out over approximately 50 days.
public void init(ActorContext actorContext) {
    StochasticsDataManager stochasticsDataManager = actorContext.getDataManager(StochasticsDataManager.class);
    randomGenerator = stochasticsDataManager.getRandomGenerator();
    regionsDataManager = actorContext.getDataManager(RegionsDataManager.class);
    vaccinationDataManager = actorContext.getDataManager(VaccinationDataManager.class);

    double planTime = randomGenerator.nextDouble();
    for (int i = 0; i < 5000; i++) {
        actorContext.addPlan(this::vaccinateRandomPerson, planTime);
        planTime += randomGenerator.nextDouble() * 0.02;
    }

    actorContext.addPlan(this::addVaccinePriorityPropertyToRegions, 50);
}

Let’s first look at the addition of the new region property on day 50 in Code Block 8.15. The new property is a Boolean value defaulted to false and indicates whether people should be chosen from regions randomly or by preferring people with the fewest vaccinations. Since the property has a default value, we do not have to set values for each region in the RegionPropertyDefinitionInitialization object that is passed to the regions data manager when creating the region. We do so anyway to demonstrate such value assignments. Once the new property is in place, the Vaccinator schedules the switching of the value for random regions once per day for the next 50 days.

Code Block 8.15: On day 50, the vaccinator defines the Boolean VACCINE PRIORITY property and assigns randomized values to the existing regions. It then plans for updates to 50 regional vaccine priority property values over 50 days.
private void addVaccinePriorityPropertyToRegions(ActorContext actorContext) {

    PropertyDefinition propertyDefinition = //
            PropertyDefinition.builder()//
                    .setType(Boolean.class)//
                    .setDefaultValue(false)//
                    .build();

    RegionPropertyDefinitionInitialization.Builder defBuilder = RegionPropertyDefinitionInitialization.builder()//
            .setPropertyDefinition(propertyDefinition)//
            .setRegionPropertyId(RegionProperty.VACCINE_PRIORITY);

    for (RegionId regionId : regionsDataManager.getRegionIds()) {
        defBuilder.addPropertyValue(regionId, randomGenerator.nextBoolean());
    }

    RegionPropertyDefinitionInitialization regionPropertyDefinitionInitialization = defBuilder.build();
    regionsDataManager.defineRegionProperty(regionPropertyDefinitionInitialization);

    for (int i = 0; i < 50; i++) {
        double planTime = actorContext.getTime() + i;
        actorContext.addPlan(this::alterVaccinePriorityPropertyOnRandomRegion, planTime);
    }
}

In Code Block 8.16 the Vaccinator performs this value switching:

Code Block 8.16: Toggling the vaccine priority for a randomly selected region.
private void alterVaccinePriorityPropertyOnRandomRegion(ActorContext actorContext) {
    List<RegionId> regionids = new ArrayList<>(regionsDataManager.getRegionIds());
    if (regionids.isEmpty()) {
        return;
    }
    RegionId regionId = regionids.get(randomGenerator.nextInt(regionids.size()));
    Boolean vaccinePriority = regionsDataManager.getRegionPropertyValue(regionId, RegionProperty.VACCINE_PRIORITY);
    regionsDataManager.setRegionPropertyValue(regionId, RegionProperty.VACCINE_PRIORITY, !vaccinePriority);
}

The Vaccinator vaccinates people at random (Code Block 8.17) by first selecting a random region and then selecting a random person in that region. The selection of the person is subject to the presence of the VACCINE_PRIORITY property and whether the value of the property is true for the selected region. If the priority selection is being used, then a first pass through the people in the region establishes the lowest number of vaccines received by any person. A second pass through the same people now selects only those having this number of vaccinations. Finally, a person is selected at random from the eligible people.

Code Block 8.17: The vaccinator selects a person at random from the population to vaccinate. If the region is using the VACCINE_PRIORITY policy, then those with the least number of vaccinations have preference.
private void vaccinateRandomPerson(ActorContext actorContext) {

    List<RegionId> regionIds = new ArrayList<>(regionsDataManager.getRegionIds());
    if (regionIds.isEmpty()) {
        return;
    }
    RegionId regionId = regionIds.get(randomGenerator.nextInt(regionIds.size()));
    List<PersonId> peopleInRegion = regionsDataManager.getPeopleInRegion(regionId);

    Boolean prioritizePeople = false;
    boolean vaccinePriorityPropertyExists = regionsDataManager
            .regionPropertyIdExists(RegionProperty.VACCINE_PRIORITY);
    if (vaccinePriorityPropertyExists) {
        prioritizePeople = regionsDataManager.getRegionPropertyValue(regionId, RegionProperty.VACCINE_PRIORITY);
    }

    PersonId selectedPersonId = null;
    if (prioritizePeople) {
        int minVaccinationCount = Integer.MAX_VALUE;
        for (PersonId personId : peopleInRegion) {
            int personVaccinationCount = vaccinationDataManager.getPersonVaccinationCount(personId);
            if (personVaccinationCount < minVaccinationCount) {
                minVaccinationCount = personVaccinationCount;
            }
        }
        List<PersonId> eligiblePeople = new ArrayList<>();
        for (PersonId personId : peopleInRegion) {
            int personVaccinationCount = vaccinationDataManager.getPersonVaccinationCount(personId);
            if (personVaccinationCount == minVaccinationCount) {
                eligiblePeople.add(personId);
            }
        }
        if (!eligiblePeople.isEmpty()) {
            selectedPersonId = eligiblePeople.get(randomGenerator.nextInt(eligiblePeople.size()));
        }
    } else {
        if (!peopleInRegion.isEmpty()) {
            selectedPersonId = peopleInRegion.get(randomGenerator.nextInt(peopleInRegion.size()));
        }
    }

    if (selectedPersonId != null) {
        vaccinationDataManager.vaccinatePerson(selectedPersonId);
    }
}

8.6 Inspecting the output

The region transfer report shows the number of transfers of a person from one region to another across all days in the simulation. The rows where the source and destination regions are the same represent the addition of people at the start of the simulation and, as expected, the sum of such transfers equals to 1000. We also expect to see regions that were added beyond the original five regions and that transfers in and out of those regions should be reduced compared to the original regions since they start out with no people and come into the simulation only after day 50.

Figure 8.1: Excerpts from the region transfer report.
scenario seed_index seed_value source_region destination_region transfers
0 0 -7834265884293137617L Region_2 Region_2 215
0 0 -7834265884293137617L Region_0 Region_0 188
0 0 -7834265884293137617L Region_3 Region_3 208
0 0 -7834265884293137617L Region_4 Region_4 215
0 0 -7834265884293137617L Region_1 Region_1 174
0 0 -7834265884293137617L Region_4 Region_3 26
0 0 -7834265884293137617L Region_4 Region_0 36
0 0 -7834265884293137617L Region_3 Region_4 24
1 1 -7320358285742045393L Region_2 Region_2 215
1 1 -7320358285742045393L Region_0 Region_0 188
1 1 -7320358285742045393L Region_3 Region_3 208
1 1 -7320358285742045393L Region_4 Region_4 215
1 1 -7320358285742045393L Region_1 Region_1 174
2 2 -4619948863677044400L Region_3 Region_0 26
2 2 -4619948863677044400L Region_3 Region_1 33
2 2 -4619948863677044400L Region_0 Region_4 22
2 2 -4619948863677044400L Region_1 Region_4 29
2 2 -4619948863677044400L Region_4 Region_0 32
4 4 -7584580254621783722L Region_8 Region_4 1
4 4 -7584580254621783722L Region_9 Region_1 1
4 4 -7584580254621783722L Region_9 Region_0 2
4 4 -7584580254621783722L Region_9 Region_6 2

In the region property report (Figure 8.2) we see that the LAT and LON properties were set for the first five regions at the start of the simulation. Starting on day 1, new regions were added and each has an assigned LAT and LON value at that time. Beginning on day 50, all regions are assigned a VACCINE_PRIOITY value and assignments to that property continue daily for random regions.

Figure 8.2: Excerpts from the region property report.
scenario seed_index seed_value time region property value
0 0 -7834265884293137617L 0.0 Region_0 LAT 45.08307901948476
0 0 -7834265884293137617L 0.0 Region_0 LON 128.5497267736204
0 0 -7834265884293137617L 0.0 Region_1 LAT 45.73317078392115
0 0 -7834265884293137617L 0.0 Region_1 LON 128.98292164396958
0 0 -7834265884293137617L 0.0 Region_2 LAT 45.74702447122078
0 0 -7834265884293137617L 0.0 Region_2 LON 128.5118606592755
0 0 -7834265884293137617L 0.0 Region_3 LAT 45.8303102139607
0 0 -7834265884293137617L 0.0 Region_3 LON 128.55192626408567
0 0 -7834265884293137617L 0.0 Region_4 LAT 45.59334365958997
0 0 -7834265884293137617L 0.0 Region_4 LON 128.7915941303198
0 0 -7834265884293137617L 1.0 Region_5 LAT 35.99754757587744
0 0 -7834265884293137617L 1.0 Region_5 LON 128.2594279657217
0 0 -7834265884293137617L 21.0 Region_6 LAT 35.84682720256188
0 0 -7834265884293137617L 21.0 Region_6 LON 128.2211833000355
0 0 -7834265884293137617L 41.0 Region_7 LAT 35.07267582475035
0 0 -7834265884293137617L 41.0 Region_7 LON 128.37457313813837
0 0 -7834265884293137617L 50.0 Region_0 VACCINE_PRIORITY false
0 0 -7834265884293137617L 50.0 Region_1 VACCINE_PRIORITY false
4 4 -7584580254621783722L 93.0 Region_2 VACCINE_PRIORITY false
4 4 -7584580254621783722L 94.0 Region_1 VACCINE_PRIORITY true
4 4 -7584580254621783722L 95.0 Region_2 VACCINE_PRIORITY true
4 4 -7584580254621783722L 96.0 Region_7 VACCINE_PRIORITY true
4 4 -7584580254621783722L 97.0 Region_0 VACCINE_PRIORITY false
4 4 -7584580254621783722L 98.0 Region_1 VACCINE_PRIORITY false
4 4 -7584580254621783722L 99.0 Region_0 VACCINE_PRIORITY true

Finally, the vaccine report shows the number of people having various vaccine counts at the end of each simulation. Although the priority policy was being used, most vaccinations were for randomly selected people so we expect a fairly wide distribution in those values.

Figure 8.3: The vaccine report shows vaccine counts at the end of each simulation.
scenario seed_index seed_value count_0 count_1 count_2 count_3 count_4 count_5 count_6+
0 0 -7834265884293137617L 13 67 145 168 170 148 289
1 1 -7320358285742045393L 13 63 141 181 187 133 282
2 2 -4619948863677044400L 17 53 157 196 176 140 261
3 3 3282202756261196294L 10 72 159 166 163 146 284
4 4 -7584580254621783722L 4 66 153 198 163 136 280