## Code for policy section

```python
# Load libraries
import numpy as np
import matplotlib.pyplot as plt
import matplotlib as mlp

# Ensure type 1 fonts are used
mlp.rcParams['ps.useafm'] = True
mlp.rcParams['pdf.use14corefonts'] = True
mlp.rcParams['text.usetex'] = True

import seaborn as sns
import pandas as pd
import pickle
import itertools as it
```

## Solve for the final size of the outbreak in Lombardy, Italy

```python
# Estimate based on the value of the basic reproduction number as provided by best fit
# For formula, see here: https://web.stanford.edu/~jhj1/teachingdocs/Jones-on-R0.pdf
from sympy import Symbol, solve, log

x = Symbol('x')
r0 = 3.16
s_inf = solve(log(x)-r0_max*(x-1),x)[0]

print("% of the population that is still susceptible by the end of the outbreak in Lombardy, Italy: {0:10.4f}".format(s_inf*100))
print("% of the population that has ever been infected by the end of the outbreak in Lombardy, Italy: {0:10.4f}".format(100-s_inf*100))
```

    % of the population that is still susceptible by the end of the outbreak in Lombardy, Italy:     4.9629
    % of the population that has ever been infected by the end of the outbreak in Lombardy, Italy:    95.0371

```python
# Set of colors
# For age group policies
color_list_shahin = ['orange','green','blue','purple','black']
# For additional baseline policies (50% or 100% of the population being asked to shelter-in-place)
color_list_add = ['dodgerblue','hotpink']
```

```python
# Number of distinct ages in the UN age distribution
# Currently ages 0-100, with each age counted separately
n_ages = 101

# Shelter-in-place probabilities per age group, equivalent to 1 million of the considered generation in each case
age_ranges = [(0,14), (15,29), (30,49), (50,69), (70,100)]
isolation_rates_by_age = [0.803689, 0.713332, 0.380842, 0.358301, 0.516221]

# Learn about the structure of the folder containing the simulation results
all_possible_combos = []

for a, iso_rate in zip(age_ranges, isolation_rates_by_age):
    combo = np.zeros(n_ages)
    combo[a[0]:a[1]+1] = iso_rate
    all_possible_combos.append(combo)

# Two possibilities for mean time to isolation: either 4.6 days (default value) or a large number to mimic no isolation in place
mean_time_to_isolations = [4.6, 10000]
all_possible_combos = list(it.product(mean_time_to_isolations, all_possible_combos))
NUM_COMBOS = len(all_possible_combos)
print("NUM COMBOS:",NUM_COMBOS)
```

    NUM COMBOS: 10

```python
mtti_val_even = all_possible_combos[0][0]
combo_frac_stay_home_even = all_possible_combos[0][1]
mtti_val_odd = all_possible_combos[1][0]
combo_frac_stay_home_odd = all_possible_combos[1][1]

print("Value of mean time to isolation - even index: ", mtti_val_even)
print("Combo fraction stay home - even index", combo_frac_stay_home_even)

print("Value of mean time to isolation - odd index: ", mtti_val_odd)
print("Combo fraction stay home - odd index: ", combo_frac_stay_home_odd)
```

    Value of mean time to isolation - even index:  4.6
    Combo fraction stay home - even index [0.803689 0.803689 0.803689 0.803689 0.803689 0.803689 0.803689 0.803689
     0.803689 0.803689 0.803689 0.803689 0.803689 0.803689 0.803689 0.
     0.       0.       0.       0.       0.       0.       0.       0.
     0.       0.       0.       0.       0.       0.       0.       0.
     0.       0.       0.       0.       0.       0.       0.       0.
     0.       0.       0.       0.       0.       0.       0.       0.
     0.       0.       0.       0.       0.       0.       0.       0.
     0.       0.       0.       0.       0.       0.       0.       0.
     0.       0.       0.       0.       0.       0.       0.       0.
     0.       0.       0.       0.       0.       0.       0.       0.
     0.       0.       0.       0.       0.       0.       0.       0.
     0.       0.       0.       0.       0.       0.       0.       0.
     0.       0.       0.       0.       0.      ]
    Value of mean time to isolation - odd index:  4.6
    Combo fraction stay home - odd index:  [0.       0.       0.       0.       0.       0.       0.       0.
     0.       0.       0.       0.       0.       0.       0.       0.713332
     0.713332 0.713332 0.713332 0.713332 0.713332 0.713332 0.713332 0.713332
     0.713332 0.713332 0.713332 0.713332 0.713332 0.713332 0.       0.
     0.       0.       0.       0.       0.       0.       0.       0.
     0.       0.       0.       0.       0.       0.       0.       0.
     0.       0.       0.       0.       0.       0.       0.       0.
     0.       0.       0.       0.       0.       0.       0.       0.
     0.       0.       0.       0.       0.       0.       0.       0.
     0.       0.       0.       0.       0.       0.       0.       0.
     0.       0.       0.       0.       0.       0.       0.       0.
     0.       0.       0.       0.       0.       0.       0.       0.
     0.       0.       0.       0.       0.      ]

```python
# Learn about the structure of the folder containing the simulation results
all_possible_combos = []

for a in age_ranges:
    # Either 50% or 100% of the population in each age group is asked to shelter-in-place
    for val in [0.5, 1.0]:
        combo = np.zeros(n_ages)
        combo[a[0]:a[1]+1]=val
        all_possible_combos.append(combo)

# Two possibilities for mean time to isolation: either 4.6 days (default value) or a large number to mimic no isolation in place
mean_time_to_isolations = [4.6, 10000]
all_possible_combos = list(it.product(mean_time_to_isolations, all_possible_combos))
NUM_COMBOS = len(all_possible_combos)
print("NUM COMBOS:",NUM_COMBOS)
```

    NUM COMBOS: 20

```python
mtti_val_even = all_possible_combos[0][0]
combo_frac_stay_home_even = all_possible_combos[0][1]

mtti_val_odd = all_possible_combos[1][0]
combo_frac_stay_home_odd = all_possible_combos[1][1]

print("Value of mean time to isolation - even index: ", mtti_val_even)
print("Combo fraction stay home - even index: ", combo_frac_stay_home_even)

print("Value of mean time to isolation - odd index: ", mtti_val_even)
print("Combo fraction stay home - odd index: ", combo_frac_stay_home_even)
```

    Value of mean time to isolation - even index:  4.6
    Combo fraction stay home - even index:  [0.5 0.5 0.5 0.5 0.5 0.5 0.5 0.5 0.5 0.5 0.5 0.5 0.5 0.5 0.5 0.  0.  0.
     0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.
     0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.
     0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.
     0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.
     0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0. ]
    Value of mean time to isolation - odd index:  4.6
    Combo fraction stay home - odd index:  [0.5 0.5 0.5 0.5 0.5 0.5 0.5 0.5 0.5 0.5 0.5 0.5 0.5 0.5 0.5 0.  0.  0.
     0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.
     0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.
     0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.
     0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.
     0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0. ]

```python
# Set font sizes for plots
legend_fontsize = 13
title_fontsize = 15
xlab_fontsize = 23
ylab_fontsize = 23
xtick_fontsize = 17
ytick_fontsize = 17
```

## Functions to be used to plot four subgraphs in Figure 8

### Function to be used to plot the projected percentage of infected people in the population over time, in the absence of physical distancing
### Figures 8(a) and 8(b)

```python
def perc_infected_age_group_node_removal(pop_size, group_vec_age, t_lockdown_vec, n_sims, sim_end, today, combo_start, combo_end, folder1, folder2, filename1, filename2, option, specific_title):
    if option == 2:
        nb = 0

        # baseline
        base_filename = 'lombardy_distributed_agepolicy_nolockdown_baseline_0_paramsweep_n10000000.0_i0_N'
        base_folder = 'nolockdown_noage/'
        Infected_Trials = np.zeros((n_sims,sim_end+1))
        for i in range(n_sims):
                Mild = pd.read_csv(base_folder + base_filename + str(i) + '_p0.029_m4_s22_mild.csv',delimiter='	',header=None)
                Severe = pd.read_csv(base_folder + base_filename + str(i) + '_p0.029_m4_s22_severe.csv',delimiter='	',header=None)
                Critical = pd.read_csv(base_folder + base_filename + str(i) + '_p0.029_m4_s22_critical.csv',delimiter='	',header=None)
                R = pd.read_csv(base_folder + base_filename + str(i) + '_p0.029_m4_s22_recovered.csv',delimiter='	',header=None)
                D = pd.read_csv(base_folder + base_filename + str(i) + '_p0.029_m4_s22_deaths.csv',delimiter='	',header=None)
                Infected_Trials[i,:] = Mild+Severe+Critical+R+D
        Infected_Trials = Infected_Trials.mean(axis=0)
        Infected_Trials = Infected_Trials/pop_size*100.

        print("Baseline 0: No intervention")
        print("% infected on lockdown day: ", Infected_Trials[t_lockdown_vec[0]])
        print("% infected today: ", Infected_Trials[today])
        print("% infected at the end of the simulation: ", Infected_Trials[sim_end])

        plt.plot(Infected_Trials,color='gray',linestyle='-.')

        for j in range(combo_start,combo_end,2):
                nb +=1
                Infected_Trials = np.zeros((n_sims,sim_end+1))
                for i in range(n_sims):
                    if i < 50:
                        folder = folder1
                        filename = filename1
                    else:
                        folder = folder2
                        filename = filename2
                    Mild = pd.read_csv( folder + filename + str(j) + '_N' + str(i%50) + '_p0.029_m4_s22_mild.csv',delimiter='	',header=None)
                    Severe = pd.read_csv(folder + filename + str(j) + '_N' + str(i%50) + '_p0.029_m4_s22_severe.csv',delimiter='	',header=None)
                    Critical = pd.read_csv(folder + filename + str(j) + '_N' + str(i%50) + '_p0.029_m4_s22_critical.csv',delimiter='	',header=None)
                    R = pd.read_csv(folder + filename + str(j) + '_N' + str(i%50) + '_p0.029_m4_s22_recovered.csv',delimiter='	',header=None)
                    D = pd.read_csv(folder + filename + str(j) + '_N' + str(i%50) + '_p0.029_m4_s22_deaths.csv',delimiter='	',header=None)
                    Infected_Trials[i,:] = Mild+Severe+Critical+R+D

                Infected_Trials = Infected_Trials.mean(axis=0)
                Infected_Trials = Infected_Trials/pop_size*100.

                print("Age group: ", group_vec_age[nb-1])
                print("% infected on lockdown day: ", Infected_Trials[t_lockdown_vec[0]])
                print("% infected today: ", Infected_Trials[today])
                print("% infected at the end of the simulation: ", Infected_Trials[sim_end])

                plt.plot(Infected_Trials,color=color_list_shahin[nb-1])

        # new baseline - 50% population is isolated
        base2_filename = 'lombardy_distributed_agepolicy_nolockdown_baseline2_0_paramsweep_n10000000.0_i'
        base2_folder = 'nolockdown_fullisolation/'

        for j in range(2,4):
                Infected_Trials = np.zeros((n_sims,sim_end+1))
                for i in range(n_sims):
                    Mild = pd.read_csv( base2_folder + base2_filename + str(j) + '_N' + str(i) + '_p0.029_m4_s22_mild.csv',delimiter='	',header=None)
                    Severe = pd.read_csv(base2_folder + base2_filename + str(j) + '_N' + str(i) + '_p0.029_m4_s22_severe.csv',delimiter='	',header=None)
                    Critical = pd.read_csv(base2_folder + base2_filename + str(j) + '_N' + str(i) + '_p0.029_m4_s22_critical.csv',delimiter='	',header=None)
                    R = pd.read_csv(base2_folder + base2_filename + str(j) + '_N' + str(i) + '_p0.029_m4_s22_recovered.csv',delimiter='	',header=None)
                    D = pd.read_csv(base2_folder + base2_filename + str(j) + '_N' + str(i) + '_p0.029_m4_s22_deaths.csv',delimiter='	',header=None)
                    Infected_Trials[i,:] = Mild+Severe+Critical+R+D

                Infected_Trials = Infected_Trials.mean(axis=0)
                Infected_Trials = Infected_Trials/pop_size*100.

                print("Baseline: ", j-1)
                print("% infected on lockdown day: ", Infected_Trials[t_lockdown_vec[0]])
                print("% infected today: ", Infected_Trials[today])
                print("% infected at the end of the simulation: ", Infected_Trials[sim_end])

                plt.plot(Infected_Trials,color=color_list_add[j-2],linestyle='-.')

        plt.axvline(t_lockdown_vec[0], 0,color='red',linestyle='--')
        plt.legend(['Absence of\n intervention']+['Ages ' + str(group_vec_age[i]) for i in range(len(group_vec_age))]+['All ages\n50\% confined','All ages\n100\% confined'], fontsize = 13)
        plt.ylim(0,100)
        plt.title(specific_title,fontsize=15)
        plt.xticks(fontsize=17)
        plt.yticks(fontsize=17)
        plt.ylabel('Percentage of infected', fontsize=23)
        plt.xlabel('Days since patient zero', fontsize=23)

        return(plt)

    elif option == 1:
        nb = 0

        # baseline
        base_filename = 'lombardy_distributed_agepolicy_nolockdown_baseline_0_paramsweep_n10000000.0_i0_N'
        base_folder = 'nolockdown_noage/'
        Infected_Trials=np.zeros((n_sims,sim_end+1))
        for i in range(n_sims):
                Mild = pd.read_csv( base_folder + base_filename + str(i) + '_p0.029_m4_s22_mild.csv',delimiter='	',header=None)
                Severe = pd.read_csv( base_folder + base_filename + str(i) + '_p0.029_m4_s22_severe.csv',delimiter='	',header=None)
                Critical = pd.read_csv( base_folder + base_filename + str(i) + '_p0.029_m4_s22_critical.csv',delimiter='	',header=None)
                R = pd.read_csv( base_folder + base_filename + str(i) + '_p0.029_m4_s22_recovered.csv',delimiter='	',header=None)
                D = pd.read_csv( base_folder + base_filename + str(i) + '_p0.029_m4_s22_deaths.csv',delimiter='	',header=None)
                Infected_Trials[i,:] = Mild+Severe+Critical+R+D
        Infected_Trials = Infected_Trials.mean(axis=0)
        Infected_Trials = Infected_Trials/pop_size*100.

        print("Baseline 0: No intervention")
        print("% infected on lockdown day: ", Infected_Trials[t_lockdown_vec[0]])
        print("% infected today: ", Infected_Trials[today])
        print("% infected at the end of the simulation: ", Infected_Trials[sim_end])

        plt.plot(Infected_Trials,color='gray',linestyle='-.')

        for j in range(combo_start+1,combo_end,2):
                nb +=1
                Infected_Trials = np.zeros((n_sims,sim_end+1))
                for i in range(n_sims):
                    if i < 50:
                        folder = folder1
                        filename = filename1
                    else:
                        folder = folder2
                        filename = filename2
                    Mild = pd.read_csv( folder + filename + str(j) + '_N' + str(i%50) + '_p0.029_m4_s22_mild.csv',delimiter='	',header=None)
                    Severe = pd.read_csv(folder + filename + str(j) + '_N' + str(i%50) + '_p0.029_m4_s22_severe.csv',delimiter='	',header=None)
                    Critical = pd.read_csv(folder + filename + str(j) + '_N' + str(i%50) + '_p0.029_m4_s22_critical.csv',delimiter='	',header=None)
                    R = pd.read_csv(folder + filename + str(j) + '_N' + str(i%50) + '_p0.029_m4_s22_recovered.csv',delimiter='	',header=None)
                    D = pd.read_csv(folder + filename + str(j) + '_N' + str(i%50) + '_p0.029_m4_s22_deaths.csv',delimiter='	',header=None)
                    Infected_Trials[i,:] = Mild+Severe+Critical+R+D
                Infected_Trials = Infected_Trials.mean(axis=0)
                Infected_Trials = Infected_Trials/pop_size*100.

                print("Age group: ", group_vec_age[nb-1])
                print("% infected on lockdown day: ", Infected_Trials[t_lockdown_vec[0]])
                print("% infected today: ", Infected_Trials[today])
                print("% infected at the end of the simulation: ", Infected_Trials[sim_end])

                plt.plot(Infected_Trials,color=color_list_shahin[nb-1])

        # new baseline - 50% population is isolated
        base2_filename = 'lombardy_distributed_agepolicy_nolockdown_baseline2_0_paramsweep_n10000000.0_i'
        base2_folder = 'nolockdown_fullisolation/'

        for j in range(2,4):
                Infected_Trials = np.zeros((n_sims,sim_end+1))
                for i in range(n_sims):
                    Mild = pd.read_csv( base2_folder + base2_filename + str(j) + '_N' + str(i) + '_p0.029_m4_s22_mild.csv',delimiter='	',header=None)
                    Severe = pd.read_csv(base2_folder + base2_filename + str(j) + '_N' + str(i) + '_p0.029_m4_s22_severe.csv',delimiter='	',header=None)
                    Critical = pd.read_csv(base2_folder + base2_filename + str(j) + '_N' + str(i) + '_p0.029_m4_s22_critical.csv',delimiter='	',header=None)
                    R = pd.read_csv(base2_folder + base2_filename + str(j) + '_N' + str(i) + '_p0.029_m4_s22_recovered.csv',delimiter='	',header=None)
                    D = pd.read_csv(base2_folder + base2_filename + str(j) + '_N' + str(i) + '_p0.029_m4_s22_deaths.csv',delimiter='	',header=None)
                    Infected_Trials[i,:] = Mild+Severe+Critical+R+D

                Infected_Trials = Infected_Trials.mean(axis=0)
                Infected_Trials = Infected_Trials/pop_size*100.

                print("Baseline: ", j-1)
                print("% infected on lockdown day: ", Infected_Trials[t_lockdown_vec[0]])
                print("% infected today: ", Infected_Trials[today])
                print("% infected at the end of the simulation: ", Infected_Trials[sim_end])
                plt.plot(Infected_Trials,color=color_list_add[j-2],linestyle='-.')

        plt.axvline(t_lockdown_vec[0], 0,color='red',linestyle='--')
        plt.legend(['Absence of\nintervention']+['Ages ' + str(group_vec_age[i]) for i in range(len(group_vec_age))]+['All ages\n50\% confined','All ages\n100\% confined'], fontsize = 13)
        plt.ylim(0,100)
        plt.title(specific_title,fontsize=15)
        plt.ylabel('Percentage of infected', fontsize=23)
        plt.xticks(fontsize=17)
        plt.yticks(fontsize=17)
        plt.xlabel('Days since patient zero', fontsize=23)

        return(plt)

    else:
        nb = 0

        # baseline
        base_filename = 'lombardy_distributed_agepolicy_nolockdown_baseline_0_paramsweep_n10000000.0_i0_N'
        base_folder = 'nolockdown_noage/'
        Infected_Trials=np.zeros((100,sim_end+1))
        for i in range(100):
                Mild = pd.read_csv( base_folder + base_filename + str(i) + '_p0.029_m4_s22_mild.csv',delimiter='	',header=None)
                Documented = pd.read_csv( base_folder + base_filename + str(i) + '_p0.029_m4_s22_documented.csv',delimiter='	',header=None)
                Severe = pd.read_csv( base_folder + base_filename + str(i) + '_p0.029_m4_s22_severe.csv',delimiter='	',header=None)
                Critical = pd.read_csv( base_folder + base_filename + str(i) + '_p0.029_m4_s22_critical.csv',delimiter='	',header=None)
                R = pd.read_csv( base_folder + base_filename + str(i) + '_p0.029_m4_s22_recovered.csv',delimiter='	',header=None)
                D = pd.read_csv( base_folder + base_filename + str(i) + '_p0.029_m4_s22_deaths.csv',delimiter='	',header=None)
                Infected_Trials[i,:] = Mild+Severe+Critical+R+D
        Infected_Trials = Infected_Trials.mean(axis=0)
        Infected_Trials = Infected_Trials/pop_size*100.

        print("Baseline 0: No intervention")
        print("% infected on lockdown day: ", Infected_Trials[t_lockdown_vec[0]])
        print("% infected today: ", Infected_Trials[today])
        print("% infected at the end of the simulation: ", Infected_Trials[sim_end])
        plt.plot(Infected_Trials,color='gray',linestyle='-.')

        for j in range(combo_start,combo_end):
            nb +=1
            Infected_Trials = np.zeros((n_sims,sim_end+1))
            for i in range(n_sims):
                if i < 50:
                    folder = folder1
                    filename = filename1
                else:
                    folder = folder2
                    filename = filename2
                Mild = pd.read_csv( folder + filename + str(j) + '_N' + str(i%50) + '_p0.029_m4_s22_mild.csv',delimiter='	',header=None)
                Severe = pd.read_csv(folder + filename + str(j) + '_N' + str(i%50) + '_p0.029_m4_s22_severe.csv',delimiter='	',header=None)
                Critical = pd.read_csv(folder + filename + str(j) + '_N' + str(i%50) + '_p0.029_m4_s22_critical.csv',delimiter='	',header=None)
                R = pd.read_csv(folder + filename + str(j) + '_N' + str(i%50) + '_p0.029_m4_s22_recovered.csv',delimiter='	',header=None)
                D = pd.read_csv(folder + filename + str(j) + '_N' + str(i%50) + '_p0.029_m4_s22_deaths.csv',delimiter='	',header=None)
                Infected_Trials[i,:] = Mild+Severe+Critical+R+D
            Infected_Trials = Infected_Trials.mean(axis=0)
            Infected_Trials = Infected_Trials/pop_size

            print("Age group: ", group_vec_age[nb-1])
            print("% infected on lockdown day: ", Infected_Trials[t_lockdown_vec[0]])
            print("% infected today: ", Infected_Trials[today])
            print("% infected at the end of the simulation: ", Infected_Trials[sim_end])
            plt.plot(Infected_Trials,color=color_list_shahin[nb-1])

        # new baseline - 50% population is isolated
        base2_filename = 'lombardy_distributed_agepolicy_nolockdown_baseline2_0_paramsweep_n10000000.0_i'
        base2_folder = 'nolockdown_fullisolation/'

        for j in range(2,4):

                Infected_Trials = np.zeros((n_sims,sim_end+1))
                for i in range(n_sims):
                    Mild = pd.read_csv( base2_folder + base2_filename + str(j) + '_N' + str(i) + '_p0.029_m4_s22_mild.csv',delimiter='	',header=None)
                    Severe = pd.read_csv(base2_folder + base2_filename + str(j) + '_N' + str(i) + '_p0.029_m4_s22_severe.csv',delimiter='	',header=None)
                    Critical = pd.read_csv(base2_folder + base2_filename + str(j) + '_N' + str(i) + '_p0.029_m4_s22_critical.csv',delimiter='	',header=None)
                    R = pd.read_csv(base2_folder + base2_filename + str(j) + '_N' + str(i) + '_p0.029_m4_s22_recovered.csv',delimiter='	',header=None)
                    D = pd.read_csv(base2_folder + base2_filename + str(j) + '_N' + str(i) + '_p0.029_m4_s22_deaths.csv',delimiter='	',header=None)
                    Infected_Trials[i,:] = Mild+Severe+Critical+R+D
                Infected_Trials = Infected_Trials.mean(axis=0)
                Infected_Trials = Infected_Trials/pop_size*100.

                print("Baseline: ", j-1)
                print("% infected on lockdown day: ", Infected_Trials[t_lockdown_vec[0]])
                print("% infected today: ", Infected_Trials[today])
                print("% infected at the end of the simulation: ", Infected_Trials[sim_end])
                plt.plot(Infected_Trials,color=color_list_add[j-2],linestyle='-.')

        plt.axvline(t_lockdown_vec[0], 0,color='red',linestyle='--')
        plt.ylim(0,100)
        plt.legend(['Absence of\nintervention']+['Ages ' + str(group_vec_age[i]) for i in range(5)]+['All ages\n50\% confined','All ages\n100\% confined'], fontsize = 13)
        plt.title(specific_title, fontsize=23)
        plt.xticks(fontsize=17)
        plt.yticks(fontsize=17)
        plt.ylabel('Percentage of infected', fontsize=23)
        plt.xlabel('Days since patient zero', fontsize=23)

        return(plt)
```

### Function to be used to plot the projected number of deaths over time, in the absence of physical distancing
### Figures 8(c) and 8(d)

```python
def death_age_group_node_removal(group_vec_age, t_lockdown_vec, n_sims, sim_end, today, combo_start, combo_end,
                                 folder1, folder2, filename1, filename2, option, specific_title):
    if option == 2:
        nb = 0

        # Baseline - No intervention
        base_filename = 'lombardy_distributed_agepolicy_nolockdown_baseline_0_paramsweep_n10000000.0_i0_N'
        base_folder = 'nolockdown_noage/'
        D=np.zeros((n_sims,sim_end+1))
        for i in range(n_sims):
            Deaths = pd.read_csv(base_folder + base_filename + str(i) + '_p0.029_m4_s22_deaths.csv', delimiter='	',header=None)
            D[i,:]=Deaths
        D = D.mean(axis=0)

        print("Baseline 0: No intervention")
        print("# of deaths on lockdown day: ", D[t_lockdown_vec[0]])
        print("# of deaths today: ", D[today])
        print("# of deaths at the end of the simulation: ", D[sim_end])

        D = D/1000.
        plt.plot(D,color='gray',linestyle='-.')

        for j in range(combo_start,combo_end,2):
                nb +=1
                D = np.zeros((n_sims,sim_end+1))
                for i in range(n_sims):
                    if i < 50:
                        folder = folder1
                        filename = filename1
                    else:
                        folder = folder2
                        filename = filename2
                    Deaths = pd.read_csv(folder + filename + str(j) + '_N' + str(i%50) + '_p0.029_m4_s22_deaths.csv', delimiter='	',header=None)
                    D[i,:] = Deaths
                D = D.mean(axis=0)

                print("Age group: ", group_vec_age[nb-1])
                print("# of deaths on lockdown day: ", D[t_lockdown_vec[0]])
                print("# of deaths today : ", D[today])
                print("# of deaths at the end of the simulation: ", D[sim_end])

                D = D/1000.
                plt.plot(D,color=color_list_shahin[nb-1])

        # Additional baselines - 50% and 100% of population stays home
        base2_filename = 'lombardy_distributed_agepolicy_nolockdown_baseline2_0_paramsweep_n10000000.0_i'
        base2_folder = 'nolockdown_fullisolation/'

        for j in range(2,4):
            D = np.zeros((n_sims,sim_end+1))
            for i in range(n_sims):
                Deaths = pd.read_csv(base2_folder + base2_filename + str(j) + '_N' + str(i) + '_p0.029_m4_s22_deaths.csv', delimiter='	',header=None)
                D[i,:]=Deaths

            D = D.mean(axis=0)
            print("Baseline: ", j-1)
            print("# of deaths on lockdown day: ", D[t_lockdown_vec[0]])
            print("# of deaths today: ", D[today])
            print("# of deaths at the end of the simulation: ", D[sim_end])

            D = D/1000.
            plt.plot(D,color=color_list_add[j-2], linestyle='-.')

        plt.axvline(t_lockdown_vec[0], 0, color='red', linestyle='--')
        plt.legend(['Absence of\n intervention']+['Ages ' + str(group_vec_age[i]) for i in range(len(group_vec_age))]+['All ages\n50\% confined','All ages\n100\% confined'], fontsize=legend_fontsize)
        plt.ylim(0,400)
        plt.title(specific_title, fontsize=title_fontsize)
        plt.xlabel('Days since patient zero', fontsize=xlab_fontsize)
        plt.ylabel('Total deaths (thousands)', fontsize=ylab_fontsize)
        plt.xticks(fontsize=xtick_fontsize)
        plt.yticks(fontsize=ytick_fontsize)

        return(plt)

    elif option == 1:
        nb = 0

        # Baseline - No intervention
        base_filename = 'lombardy_distributed_agepolicy_nolockdown_baseline_0_paramsweep_n10000000.0_i0_N'
        base_folder = 'nolockdown_noage/'
        D = np.zeros((n_sims,sim_end+1))
        for i in range(n_sims):
            Deaths = pd.read_csv(base_folder + base_filename + str(i) + '_p0.029_m4_s22_deaths.csv', delimiter='	', header=None)
            D[i,:]=Deaths
        D = D.mean(axis=0)

        print("Baseline 0: No intervention")
        print("# of deaths on lockdown day: ", D[t_lockdown_vec[0]])
        print("# of deaths today: ", D[today])
        print("# of deaths at the end of the simulation: ", D[sim_end])
        D = D/1000.
        plt.plot(D,color='gray',linestyle='-.')

        # Average simulations per age group over n_sims random seeds
        for j in range(combo_start+1,combo_end,2):
                nb +=1
                D = np.zeros((n_sims,sim_end+1))
                for i in range(n_sims):
                    if i < 50:
                        folder = folder1
                        filename = filename1
                    else:
                        folder = folder2
                        filename = filename2
                    Deaths = pd.read_csv(folder + filename + str(j) + '_N' + str(i%50) + '_p0.029_m4_s22_deaths.csv', delimiter='	',header=None)
                    D[i,:] = Deaths

                D = D.mean(axis=0)
                print("Age group: ", group_vec_age[nb-1])
                print("# of deaths on lockdown day: ", D[t_lockdown_vec[0]])
                print("# of deaths today: ", D[today])
                print("# of deaths at the end of the simulatuon: ", D[today])

                D = D/1000.
                plt.plot(D,color=color_list_shahin[nb-1])

        # Additional baselines - 50% and 100% of population stays home
        base2_filename = 'lombardy_distributed_agepolicy_nolockdown_baseline2_0_paramsweep_n10000000.0_i'
        base2_folder = 'nolockdown_fullisolation/'

        for j in range(2,4):

                D = np.zeros((n_sims,sim_end+1))
                for i in range(n_sims):
                    Deaths = pd.read_csv(base2_folder + base2_filename + str(j) + '_N' + str(i) + '_p0.029_m4_s22_deaths.csv',delimiter='	',header=None)
                    D[i,:]=Deaths

                D = D.mean(axis=0)
                print("Baseline: ", j-1)
                print("# of deaths on lockdown day: ", D[t_lockdown_vec[0]])
                print("# of deaths today: ", D[today])
                print("# of deaths at the end of the simulation: ", D[sim_end])

                D = D/1000.
                plt.plot(D,color=color_list_add[j-2],linestyle='-.')

        plt.axvline(t_lockdown_vec[0], 0,color='red',linestyle='--')
        plt.legend(['Absence of\nintervention']+['Ages ' + str(group_vec_age[i]) for i in range(5)]+['All ages\n50\% confined','All ages\n100\% confined'], fontsize = 13)
        plt.ylim(0,400)
        plt.title(specific_title,fontsize=15)
        plt.ylabel('Total deaths (thousands)', fontsize=23)
        plt.xticks(fontsize=17)
        plt.yticks(fontsize=17)
        plt.xlabel('Days since patient zero', fontsize=23)

        return(plt)

    else:
        nb = 0

        # baseline
        base_filename = 'lombardy_distributed_agepolicy_nolockdown_baseline_0_paramsweep_n10000000.0_i0_N'
        base_folder = 'nolockdown_noage/'
        D=np.zeros((n_sims,sim_end+1))
        for i in range(n_sims):
                Deaths = pd.read_csv( base_folder + base_filename + str(i) + '_p0.029_m4_s22_deaths.csv',delimiter='	',header=None)
                D[i,:]=Deaths
        D = D.mean(axis=0)

        print("Baseline 0: No intervention")
        print("# of deaths on lockdown day: ", Infected_Trials[t_lockdown_vec[0]])
        print("# of deaths today: ", D[today])
        print("# of deaths at the end of the simulation: ", Infected_Trials[sim_end])

        D = D/1000.
        plt.plot(D,color='gray',linestyle='-.')

        for j in range(combo_start,combo_end):
            nb = nb+1
            D = np.zeros((n_sims,sim_end+1))
            for i in range(n_sims):
                if i < 50:
                    folder = folder1
                    filename = filename1
                else:
                    folder = folder2
                    filename = filename2
                Deaths = pd.read_csv(folder + filename + str(j) + '_N' + str(i%50) + '_p0.029_m4_s22_deaths.csv',delimiter='	',header=None)
                D[i,:] = Deaths

            D = D.mean(axis=0)
            print("Age group: ", group_vec_age[nb-1])
            print("# of deaths on lockdown day: ", D[t_lockdown_vec[0]])
            print("# of deaths today: ", D[today])
            print("# of deaths at the end of the simulation: ", D[sim_end])

            D = D/1000.
            plt.plot(D,color=color_list_shahin[nb-1])

        # new baseline - 50% population is isolated
        base2_filename = 'lombardy_distributed_agepolicy_nolockdown_baseline2_0_paramsweep_n10000000.0_i'
        base2_folder = 'nolockdown_fullisolation/'

        for j in range(2,4):

                D = np.zeros((n_sims,sim_end+1))
                for i in range(n_sims):
                    Deaths = pd.read_csv(base2_folder + base2_filename + str(j) + '_N' + str(i) + '_p0.029_m4_s22_deaths.csv',delimiter='	',header=None)
                    D[i,:]=Deaths
                D = D.mean(axis=0)

                print("Baseline: ", j-1)
                print("% infected on lockdown day: ", D[t_lockdown_vec[0]])
                print("# of deaths today: ", D[today])
                print("# of deaths at the end of the simulation: ", D[sim_end])

                D = D/1000.
                plt.plot(D,color=color_list_add[j-2],linestyle='-.')

        plt.axvline(t_lockdown_vec[0], 0,color='red',linestyle='--')
        plt.ylim(0,400)
        plt.legend(['Absence of\nintervention']+['Ages ' + str(group_vec_age[i]) for i in range(5)]+['All ages\n50\% confined','All ages\n100\% confined'], fontsize = 13)
        plt.title(specific_title, fontsize=23)
        plt.xticks(fontsize=17)
        plt.yticks(fontsize=17)
        plt.ylabel('Total deaths (thousands)', fontsize=23)
        plt.xlabel('Days since patient zero', fontsize=23)

        return(plt)
```

## Functions to be used to plot four subgraphs in Figure 9

### Function to be used to plot the projected percentage of infected people in the population over time, when physical distancing is in place
### Figures 9(a) and 9(b)

```python
def perc_infected_age_group_node_removal_lockdown(pop_size, group_vec_age, t_lockdown_vec, n_sims, sim_end, today, combo_start, combo_end, folder, filename, option, specific_title):
    if option == 2:
        nb = 0
        Infected_Trials = np.zeros((n_sims,sim_end+1))

        # Baseline - "No intervention" scenario
        base_filename = 'lombardy_distributed_agepolicy_nolockdown_baseline_0_paramsweep_n10000000.0_i0_N'
        base_folder = 'nolockdown_noage/'
        for i in range(n_sims):
                Mild = pd.read_csv(base_folder + base_filename + str(i) + '_p0.029_m4_s22_mild.csv', delimiter='	', header=None)
                Severe = pd.read_csv(base_folder + base_filename + str(i) + '_p0.029_m4_s22_severe.csv', delimiter='	', header=None)
                Critical = pd.read_csv( base_folder + base_filename + str(i) + '_p0.029_m4_s22_critical.csv', delimiter='	',header=None)
                R = pd.read_csv(base_folder + base_filename + str(i) + '_p0.029_m4_s22_recovered.csv', delimiter='	', header=None)
                D = pd.read_csv(base_folder + base_filename + str(i) + '_p0.029_m4_s22_deaths.csv', delimiter='	', header=None)
                Infected_Trials[i,:] = Mild+Severe+Critical+R+D
        Infected_Trials = Infected_Trials.mean(axis=0)
        Infected_Trials = Infected_Trials/pop_size*100.

        print("Baseline 0: No intervention")
        print("% infected on lockdown day: ", Infected_Trials[t_lockdown_vec[0]])
        print("% infected today: ", Infected_Trials[today])
        print("% infected at the end of the simulation: ", Infected_Trials[sim_end])
        plt.plot(Infected_Trials,color='gray',linestyle='-.')

        for j in range(combo_start,combo_end,2):
                nb +=1
                Infected_Trials = np.zeros((n_sims,sim_end+1))
                for i in range(n_sims):
                    Mild = pd.read_csv( folder + filename + str(j) + '_N' + str(i) + '_p0.029_m4_s22_mild.csv', delimiter='	',header=None)
                    Severe = pd.read_csv(folder + filename + str(j) + '_N' + str(i) + '_p0.029_m4_s22_severe.csv', delimiter='	',header=None)
                    Critical = pd.read_csv(folder + filename + str(j) + '_N' + str(i) + '_p0.029_m4_s22_critical.csv', delimiter='	',header=None)
                    R = pd.read_csv(folder + filename + str(j) + '_N' + str(i) + '_p0.029_m4_s22_recovered.csv', delimiter='	',header=None)
                    D = pd.read_csv(folder + filename + str(j) + '_N' + str(i) + '_p0.029_m4_s22_deaths.csv', delimiter='	',header=None)
                    Infected_Trials[i,:] = Mild+Severe+Critical+R+D
                Infected_Trials = Infected_Trials.mean(axis=0)
                Infected_Trials = Infected_Trials/pop_size*100.

                print("Age group: ", group_vec_age[nb-1])
                print("% infected on lockdown day: ", Infected_Trials[t_lockdown_vec[0]])
                print("% infected today: ", Infected_Trials[today])
                print("% infected at the end of the simulation: ", Infected_Trials[sim_end])
                plt.plot(Infected_Trials,color=color_list_shahin[nb-1])

        # new baseline - 50% or 100% of the population of an age group is isolated
        base2_filename = 'lombardy_distributed_agepolicy_nolockdown_baseline2_0_paramsweep_n10000000.0_i'
        base2_folder = 'nolockdown_fullisolation/'

        for j in range(2,4):

                Infected_Trials = np.zeros((n_sims,sim_end+1))
                for i in range(n_sims):
                    Mild = pd.read_csv( base2_folder + base2_filename + str(j) + '_N' + str(i) + '_p0.029_m4_s22_mild.csv',delimiter='	',header=None)
                    Severe = pd.read_csv(base2_folder + base2_filename + str(j) + '_N' + str(i) + '_p0.029_m4_s22_severe.csv',delimiter='	',header=None)
                    Critical = pd.read_csv(base2_folder + base2_filename + str(j) + '_N' + str(i) + '_p0.029_m4_s22_critical.csv',delimiter='	',header=None)
                    R = pd.read_csv(base2_folder + base2_filename + str(j) + '_N' + str(i) + '_p0.029_m4_s22_recovered.csv',delimiter='	',header=None)
                    D = pd.read_csv(base2_folder + base2_filename + str(j) + '_N' + str(i) + '_p0.029_m4_s22_deaths.csv',delimiter='	',header=None)
                    Infected_Trials[i,:] = Mild+Severe+Critical+R+D
                Infected_Trials = Infected_Trials/pop_size*100.
                Infected_Trials = Infected_Trials.mean(axis=0)

                print("Baseline: ", j-1)
                print("% infected on lockdown day: ", Infected_Trials[t_lockdown_vec[0]])
                print("% infected today: ", Infected_Trials[today])
                print("% infected at the end of the simulation: ", Infected_Trials[sim_end])
                plt.plot(Infected_Trials,color=color_list_add[j-2],linestyle='-.')

        plt.axvline(t_lockdown_vec[0], 0,linestyle='--',color='red')
        plt.legend(['Absence of\nintervention']+['Ages ' + str(group_vec_age[i]) for i in range(len(group_vec_age))]+['All ages\n50\% confined','All ages\n100\% confined'],fontsize=13)
        plt.ylim(0,100)
        plt.title(specific_title)
        plt.ylabel('Percentage of infected',fontsize=23)
        plt.xticks(fontsize=17)
        plt.yticks(fontsize=17)
        plt.xlabel('Days since patient zero',fontsize=23)

        return(plt)

    elif option == 1:
        nb = 0
        Infected_Trials = np.zeros((n_sims,sim_end+1))

        # baseline
        base_filename = 'lombardy_distributed_agepolicy_nolockdown_baseline_0_paramsweep_n10000000.0_i0_N'
        base_folder = 'nolockdown_noage/'
        for i in range(n_sims):
                Mild = pd.read_csv( base_folder + base_filename + str(i) + '_p0.029_m4_s22_mild.csv',delimiter='	',header=None)
                Severe = pd.read_csv( base_folder + base_filename + str(i) + '_p0.029_m4_s22_severe.csv',delimiter='	',header=None)
                Critical = pd.read_csv( base_folder + base_filename + str(i) + '_p0.029_m4_s22_critical.csv',delimiter='	',header=None)
                R = pd.read_csv( base_folder + base_filename + str(i) + '_p0.029_m4_s22_recovered.csv',delimiter='	',header=None)
                D = pd.read_csv( base_folder + base_filename + str(i) + '_p0.029_m4_s22_deaths.csv',delimiter='	',header=None)
                Infected_Trials[i,:] = Mild+Severe+Critical+R+D
        Infected_Trials = Infected_Trials.mean(axis=0)
        Infected_Trials = Infected_Trials/pop_size*100.

        print("Baseline 0: No intervention")
        print("% infected on lockdown day: ", Infected_Trials[t_lockdown_vec[0]])
        print("% infected today: ", Infected_Trials[today])
        print("% infected at the end of the simulation: ", Infected_Trials[sim_end])

        plt.plot(Infected_Trials,color='gray',linestyle='-.')

        for j in range(combo_start+1,combo_end,2):
                nb = nb+1
                Infected_Trials = np.zeros((n_sims,sim_end+1))
                for i in range(n_sims):
                    Mild = pd.read_csv( folder + filename + str(j) + '_N' + str(i) + '_p0.029_m4_s22_mild.csv',delimiter='	',header=None)
                    Severe = pd.read_csv(folder + filename + str(j) + '_N' + str(i) + '_p0.029_m4_s22_severe.csv',delimiter='	',header=None)
                    Critical = pd.read_csv(folder + filename + str(j) + '_N' + str(i) + '_p0.029_m4_s22_critical.csv',delimiter='	',header=None)
                    R = pd.read_csv(folder + filename + str(j) + '_N' + str(i) + '_p0.029_m4_s22_recovered.csv',delimiter='	',header=None)
                    D = pd.read_csv(folder + filename + str(j) + '_N' + str(i) + '_p0.029_m4_s22_deaths.csv',delimiter='	',header=None)
                    Infected_Trials[i,:] = Mild+Severe+Critical+R+D
                Infected_Trials = Infected_Trials.mean(axis=0)
                Infected_Trials = Infected_Trials/pop_size*100.

                print("Age group: ", group_vec_age[nb-1])
                print("% infected on lockdown day: ", Infected_Trials[t_lockdown_vec[0]])
                print("% infected today: ", Infected_Trials[today])
                print("% infected at the end of the simulation: ", Infected_Trials[sim_end])
                plt.plot(Infected_Trials,color=color_list_shahin[nb-1])

        # new baseline - 50% population is isolated
        base2_filename = 'lombardy_distributed_agepolicy_nolockdown_baseline2_0_paramsweep_n10000000.0_i'
        base2_folder = 'nolockdown_fullisolation/'

        for j in range(2,4):

                Infected_Trials = np.zeros((n_sims,sim_end+1))

                for i in range(n_sims):
                    Mild = pd.read_csv( base2_folder + base2_filename + str(j) + '_N' + str(i) + '_p0.029_m4_s22_mild.csv',delimiter='	',header=None)
                    Severe = pd.read_csv(base2_folder + base2_filename + str(j) + '_N' + str(i) + '_p0.029_m4_s22_severe.csv',delimiter='	',header=None)
                    Critical = pd.read_csv(base2_folder + base2_filename + str(j) + '_N' + str(i) + '_p0.029_m4_s22_critical.csv',delimiter='	',header=None)
                    R = pd.read_csv(base2_folder + base2_filename + str(j) + '_N' + str(i) + '_p0.029_m4_s22_recovered.csv',delimiter='	',header=None)
                    D = pd.read_csv(base2_folder + base2_filename + str(j) + '_N' + str(i) + '_p0.029_m4_s22_deaths.csv',delimiter='	',header=None)
                    Infected_Trials[i,:] = Mild+Severe+Critical+R+D

                Infected_Trials = Infected_Trials.mean(axis=0)
                Infected_Trials = Infected_Trials/pop_size*100.

                print("Baseline: ", j-1)
                print("% infected on lockdown day: ", Infected_Trials[t_lockdown_vec[0]])
                print("% infected today: ", Infected_Trials[today])
                print("% infected at the end of the simulation: ", Infected_Trials[sim_end])

                plt.plot(Infected_Trials,color=color_list_add[j-2],linestyle='-.')

        plt.axvline(t_lockdown_vec[0], 0,linestyle='--',color='red')
        plt.legend(['Absence of\nintervention']+['Ages ' + str(group_vec_age[i]) for i in range(5)]+['All ages\n50\% confined','All ages\n100\% confined'],fontsize=13)
        plt.ylim(0,100)
        plt.title(specific_title)
        plt.ylabel('Percentage of infected',fontsize=23)
        plt.xticks(fontsize=17)
        plt.yticks(fontsize=17)
        plt.xlabel('Days since patient zero',fontsize=23)

        return(plt)

    else:
        nb = 0
        Infected_Trials = np.zeros((n_sims,sim+end+1))

        # baseline
        base_filename = 'lombardy_distributed_agepolicy_nolockdown_baseline_0_paramsweep_n10000000.0_i0_N'
        base_folder = 'nolockdown_noage/'
        for i in range(n_sims):
                Mild = pd.read_csv(base_folder + base_filename + str(i) + '_p0.029_m4_s22_mild.csv',delimiter='	',header=None)
                Documented = pd.read_csv(base_folder + base_filename + str(i) + '_p0.029_m4_s22_documented.csv',delimiter='	',header=None)
                Severe = pd.read_csv(base_folder + base_filename + str(i) + '_p0.029_m4_s22_severe.csv',delimiter='	',header=None)
                Critical = pd.read_csv(base_folder + base_filename + str(i) + '_p0.029_m4_s22_critical.csv',delimiter='	',header=None)
                R = pd.read_csv(base_folder + base_filename + str(i) + '_p0.029_m4_s22_recovered.csv',delimiter='	',header=None)
                D = pd.read_csv(base_folder + base_filename + str(i) + '_p0.029_m4_s22_deaths.csv',delimiter='	',header=None)
                Infected_Trials[i,:] = Mild+Severe+Critical+R+D
        Infected_Trials = Infected_Trials.mean(axis=0)
        Infected_Trials = Infected_Trials/pop_size*100.

        print("Baseline 0: No intervention")
        print("% infected on lockdown day: ", Infected_Trials[t_lockdown_vec[0]])
        print("% infected today: ", Infected_Trials[today])
        print("% infected at the end of the simulation: ", Infected_Trials[sim_end])

        plt.plot(Infected_Trials,color='gray',linestyle='-.')

        for j in range(combo_start,combo_end):
            nb +=1
            Infected_Trials = np.zeros((n_sims,sim_end+1))

            for i in range(n_sims):
                Mild = pd.read_csv(folder + filename + str(j) + '_N' + str(i) + '_p0.029_m4_s22_mild.csv',delimiter='	',header=None)
                Severe = pd.read_csv(folder + filename + str(j) + '_N' + str(i) + '_p0.029_m4_s22_severe.csv',delimiter='	',header=None)
                Critical = pd.read_csv(folder + filename + str(j) + '_N' + str(i) + '_p0.029_m4_s22_critical.csv',delimiter='	',header=None)
                R = pd.read_csv(folder + filename + str(j) + '_N' + str(i) + '_p0.029_m4_s22_recovered.csv',delimiter='	',header=None)
                D = pd.read_csv(folder + filename + str(j) + '_N' + str(i) + '_p0.029_m4_s22_deaths.csv',delimiter='	',header=None)
                Infected_Trials[i,:] = Mild+Severe+Critical+R+D
            Infected_Trials = Infected_Trials.mean(axis=0)
            Infected_Trials = Infected_Trials/pop_size*100.

            print("Age group: ", group_vec_age[j-1])
            print("% infected on lockdown day: ", Infected_Trials[t_lockdown_vec[0]])
            print("% infected today: ", Infected_Trials[today])
            print("% infected at the end of the simulation: ", Infected_Trials[sim_end])
            plt.plot(Infected_Trials, color=color_list_shahin[nb-1])

        # new baseline - 50% population is isolated
        base2_filename = 'lombardy_distributed_agepolicy_nolockdown_baseline2_0_paramsweep_n10000000.0_i'
        base2_folder = 'nolockdown_fullisolation/'

        for j in range(2,4):

                Infected_Trials = np.zeros((n_sims,sim_end+1))
                for i in range(n_sims):
                    Mild = pd.read_csv(base2_folder + base2_filename + str(j) + '_N' + str(i) + '_p0.029_m4_s22_mild.csv',delimiter='	',header=None)
                    Severe = pd.read_csv(base2_folder + base2_filename + str(j) + '_N' + str(i) + '_p0.029_m4_s22_severe.csv',delimiter='	',header=None)
                    Critical = pd.read_csv(base2_folder + base2_filename + str(j) + '_N' + str(i) + '_p0.029_m4_s22_critical.csv',delimiter='	',header=None)
                    R = pd.read_csv(base2_folder + base2_filename + str(j) + '_N' + str(i) + '_p0.029_m4_s22_recovered.csv',delimiter='	',header=None)
                    D = pd.read_csv(base2_folder + base2_filename + str(j) + '_N' + str(i) + '_p0.029_m4_s22_deaths.csv',delimiter='	',header=None)
                    Infected_Trials[i,:] = Mild+Severe+Critical+R+D
                Infected_Trials = Infected_Trials.mean(axis=0)
                Infected_Trials = Infected_Trials/pop_size*100.

                print("Baseline ",j-1)
                print("% infected on lockdown day: ", Infected_Trials[t_lockdown_vec[0]])
                print("% infected today: ", Infected_Trials[today])
                print("% infected at the end of the simulation: ", Infected_Trials[sim_end])
                plt.plot(Infected_Trials,color=color_list_add[j-2],linestyle='-.')

        plt.axvline(t_lockdown_vec[0], 0,linestyle='--',color='red')
        plt.legend(['Absence of\nintervention']+['Ages ' + str(group_vec_age[i]) for i in range(len(group_vec_age))]+['All ages\n50\% confined','All ages\n100\% confined'],fontsize=13)
        plt.ylim(0,100)
        plt.title(specific_title)
        plt.ylabel('Percentage of infected',fontsize=23)
        plt.xticks(fontsize=17)
        plt.yticks(fontsize=17)
        plt.xlabel('Days since patient zero',fontsize=23)

        return(plt)
```

### Function to be used to plot the projected number of deaths over time, when physical distancing is in place
### Figures 9(c) and 9(d)

```python
def death_age_group_node_removal_lockdown(group_vec_age, t_lockdown_vec, n_sims, sim_end, today, combo_start, combo_end, folder, filename, option, specific_title):
    if option == 2:
        nb = 0
        D=np.zeros((n_sims,sim_end+1))

        # baseline
        base_filename = 'lombardy_distributed_agepolicy_nolockdown_baseline_0_paramsweep_n10000000.0_i0_N'
        base_folder = 'nolockdown_noage/'
        for i in range(n_sims):
                Deaths = pd.read_csv(base_folder + base_filename + str(i) + '_p0.029_m4_s22_deaths.csv',delimiter='	',header=None)
                D[i,:]=Deaths
        D = D.mean(axis=0)

        print("Baseline 0: No intervention")
        print("# of deaths on lockdown day", D[t_lockdown_vec[0]])
        print("# of deaths today: ", D[today])
        print("# of deaths at the end of the simulation: ", D[sim_end])

        D = D/1000.
        plt.plot(D,color='gray',linestyle='-.')

        # not baseline
        for j in range(combo_start,combo_end,2):
                nb +=1
                D = np.zeros((n_sims,sim_end+1))

                for i in range(n_sims):
                    Deaths = pd.read_csv(folder + filename + str(j) + '_N' + str(i) + '_p0.029_m4_s22_deaths.csv',delimiter='	',header=None)
                    D[i,:]=Deaths

                D = D.mean(axis=0)
                print("Age group: ", group_vec_age[nb-1])
                print("# of deaths on lockdown day: ", D[t_lockdown_vec[0]])
                print("# of deaths today ", D[today])
                print("# of deaths at the end of the simulation: ", D[sim_end])
                D = D/1000.

                plt.plot(D,color=color_list_shahin[nb-1])

        # new baseline - 50% or 100% of the population of an age group is isolated
        base2_filename = 'lombardy_distributed_agepolicy_nolockdown_baseline2_0_paramsweep_n10000000.0_i'
        base2_folder = 'nolockdown_fullisolation/'

        for j in range(2,4):

                D = np.zeros((n_sims,sim_end+1))
                for i in range(n_sims):
                    Deaths = pd.read_csv(base2_folder + base2_filename + str(j) + '_N' + str(i) + '_p0.029_m4_s22_deaths.csv',delimiter='	',header=None)
                    D[i,:] = Deaths
                D = D.mean(axis=0)

                print("Baseline: ",j-1)
                print("# of deaths on lockdown day: ", D[t_lockdown_vec[0]])
                print("# of deaths today: ", D[today])
                print("# of deaths at the end of the simulation: ", D[sim_end])

                D = D/1000.
                plt.plot(D,color=color_list_add[j-2],linestyle='-.')

        plt.axvline(t_lockdown_vec[0], 0, linestyle='--',color='red')
        plt.legend(['Absence of\nintervention']+['Ages ' + str(group_vec_age[i]) for i in range(5)]+['All ages\n50\% confined','All ages\n100\% confined'],fontsize=13)
        plt.ylim(0,400)
        plt.title(specific_title)
        plt.ylabel('Total deaths (thousands)',fontsize=23)
        plt.xticks(fontsize=17)
        plt.yticks(fontsize=17)
        plt.xlabel('Days since patient zero',fontsize=23)

        return(plt)

    elif option == 1:
        nb = 0

        # Baseline
        D=np.zeros((n_sims,sim_end+1))
        base_filename = 'lombardy_distributed_agepolicy_nolockdown_baseline_0_paramsweep_n10000000.0_i0_N'
        base_folder = 'nolockdown_noage/'
        for i in range(n_sims):
                Deaths = pd.read_csv( base_folder + base_filename + str(i) + '_p0.029_m4_s22_deaths.csv',delimiter='	',header=None)
                D[i,:] = Deaths
        D = D.mean(axis=0)

        print("Baseline: No intervention")
        print("# of deaths on lockdown day: ", D[t_lockdown_vec[0]])
        print("# of deaths today: ", D[today])
        print("# of deaths at the end of the simulation: ", D[sim_end])

        D = D/1000.
        plt.plot(D,color='gray',linestyle='-.')

        # Per age group
        for j in range(combo_start+1,combo_end,2):
                nb = nb +1
                D = np.zeros((n_sims,sim_end+1))
                for i in range(n_sims):
                    Deaths = pd.read_csv(folder + filename + str(j) + '_N' + str(i) + '_p0.029_m4_s22_deaths.csv',delimiter='	',header=None)
                    D[i,:] = Deaths
                D = D.mean(axis=0)

                print("Age group: ", group_vec_age[nb-1])
                print("# of deaths on lockdown day: ", D[t_lockdown_vec[0]])
                print("# of deaths today: ", D[today])
                print("# of deaths at the end of the simulation: ", D[sim_end])

                D = D/1000.
                plt.plot(D,color=color_list_shahin[nb-1])

        # new baseline - 50% population is isolated
        base2_filename = 'lombardy_distributed_agepolicy_nolockdown_baseline2_0_paramsweep_n10000000.0_i'
        base2_folder = 'nolockdown_fullisolation/'

        for j in range(2,4):
                D = np.zeros((n_sims,sim_end+1))
                for i in range(n_sims):
                    Deaths = pd.read_csv(base2_folder + base2_filename + str(j) + '_N' + str(i) + '_p0.029_m4_s22_deaths.csv',delimiter='	',header=None)
                    D[i,:] = Deaths
                D = D.mean(axis=0)

                print("Baseline: ", j-1)
                print("# of deaths on lockdown day: " + str(D[t_lockdown_vec[0]]))
                print("# of deaths today: " + str(D[today]))
                print("# of deaths at the end of the simulation: " + str(D[sim_end]))

                D = D/1000.
                plt.plot(D,color=color_list_add[j-2],linestyle='-.')

        plt.axvline(t_lockdown_vec[0], 0,linestyle='--',color='red')
        plt.legend(['Absence of\nintervention']+['Ages ' + str(group_vec_age[i]) for i in range(5)]+['All ages\n50\% confined','All ages\n100\% confined'],fontsize=13)
        plt.ylim(0,400)
        plt.title(specific_title)
        plt.ylabel('Total deaths (thousands)',fontsize=23)
        plt.xticks(fontsize=17)
        plt.yticks(fontsize=17)
        plt.xlabel('Days since patient zero',fontsize=23)

        return(plt)

    else:
        nb = 0

        # baseline
        D = np.zeros((n_sims,sim_end+1))
        base_filename = 'lombardy_distributed_agepolicy_nolockdown_baseline_0_paramsweep_n10000000.0_i0_N'
        base_folder = 'nolockdown_noage/'
        for i in range(n_sims):
            Deaths = pd.read_csv( base_folder + base_filename + str(i) + '_p0.029_m4_s22_deaths.csv',delimiter='	',header=None)
            D[i,:] = Deaths
        D = D.mean(axis=0)

        print("Baseline: No intervention")
        print("# of deaths on lockdown day: " + str(D[t_lockdown_vec[0]]))
        print("# of deaths today: " + str(D[today]))
        print("# of deaths at the end of the simulation: " + str(D[sim_end]))

        D = D/1000.
        plt.plot(D,color='gray',linestyle='-.')

        # Per age group
        for j in range(combo_start,combo_end):
            nb +=1
            D = np.zeros((n_sims,sim_end+1))
            for i in range(n_sims):
                Deaths = pd.read_csv(folder + filename + str(j) + '_N' + str(i) + '_p0.029_m4_s22_deaths.csv',delimiter='	',header=None)
                D[i,:]=Deaths
            D = D.mean(axis=0)

            print("Age group: ", group_vec_age[nb-1])
            print("# of deaths on lockdown day: " + str(D[t_lockdown_vec[0]]))
            print("# of deaths today: " + str(D[today]))
            print("# of deaths at the end of the simulation: " + str(D[sim_end]))

            D = D/1000.
            plt.plot(D,color=color_list_shahin[nb-1])

        # new baseline - 50% population is isolated
        base2_filename = 'lombardy_distributed_agepolicy_nolockdown_baseline2_0_paramsweep_n10000000.0_i'
        base2_folder = 'nolockdown_fullisolation/'

        for j in range(2,4):
                D = np.zeros((n_sims,sim_end+1))
                for i in range(n_sims):
                    Deaths = pd.read_csv(base2_folder + base2_filename + str(j) + '_N' + str(i) + '_p0.029_m4_s22_deaths.csv',delimiter='	',header=None)
                    D[i,:] = Deaths
                D = D.mean(axis=0)

                print("Baseline: ",j-1)
                print("# of deaths on lockdown day:" + str(t_lockdown_vec[0]))
                print("# of deaths today: " + str(D[today]))
                print("# of deaths at the end of the simulation: "+ str(D[sim_end]))

                D = D/1000.
                plt.plot(D,color=color_list_add[j-2],linestyle='-.')

        plt.axvline(t_lockdown_vec[0], 0,linestyle='--',color='red')
        plt.legend(['Absence of\nintervention']+['Ages ' + str(group_vec_age[i]) for i in range(len(group_vec_age))]+['All ages\n50\% confined','All ages\n100\% confined'],fontsize=13)
        plt.ylim(0,400)
        plt.title(specific_title)
        plt.ylabel('Total deaths (thousands)',fontsize=23)
        plt.xticks(fontsize=17)
        plt.yticks(fontsize=17)
        plt.xlabel('Days since patient zero',fontsize=23)

        return(plt)
```

## Figure 8(a)

```python
# Mean time to isolation 4.6 and 50% of age category removed
t_lockdown_vec = [46]
sim_end = 119
today = 67
group_vec_age = ['0-14','15-29','30-49','50-69','70+']

combo_start = 0
combo_end = 10
pop_size = 10000000

filename1 = 'lombardy_distributed_agepolicy_0_paramsweep_n10000000.0_i'
filename2 = 'lombardy_distributed_agepolicy_1_paramsweep_n10000000.0_i'

folder1 = 'perc_policy_results/run1/'
folder2 = 'perc_policy_results/run2/'
option = 2
specific_title = ''

perc_infected_age_group_node_removal(pop_size, group_vec_age, t_lockdown_vec, n_sims, sim_end, today, combo_start, combo_end, folder1, folder2, filename1, filename2, option, specific_title)
```

## Figure 8(c)

```python
# Mean time to isolation 4.6 and 50% of age category removed
t_lockdown_vec = [46]
sim_end = 119
today = 67
group_vec_age = ['0-14','15-29','30-49','50-69','70+']

combo_start = 0
combo_end = 10
pop_size = 10000000

filename1 = 'lombardy_distributed_agepolicy_0_paramsweep_n10000000.0_i'
filename2 = 'lombardy_distributed_agepolicy_1_paramsweep_n10000000.0_i'

folder1 = 'perc_policy_results/run1/'
folder2 = 'perc_policy_results/run2/'
option = 2
#specific_title = 'Mean Time to Isolation = 4.6 days for all' + '\n50% stay home, per age group'
specific_title = ''

death_age_group_node_removal( group_vec_age, t_lockdown_vec, n_sims, sim_end, today, combo_start, combo_end, folder1, folder2, filename1, filename2, option, specific_title)
```

## Figure 8(b)

```python
# Mean time to isolation 4.6 and 100% of age category removed
t_lockdown_vec = [46]
n_sims = 100
sim_end = 119
today = 67
group_vec_age = ['0-14','15-29','30-49','50-69','70+']

combo_start = 0
combo_end = 10
pop_size = 10000000

filename1 = 'lombardy_distributed_agepolicy_0_paramsweep_n10000000.0_i'
filename2 = 'lombardy_distributed_agepolicy_1_paramsweep_n10000000.0_i'

folder1 = 'perc_policy_results/run1/'
folder2 = 'perc_policy_results/run2/'
option = 1
specific_title = ''

perc_infected_age_group_node_removal(pop_size, group_vec_age, t_lockdown_vec, n_sims, sim_end, today, combo_start, combo_end, folder1, folder2, filename1, filename2, option, specific_title)
```

## Figure 8(d)

```python
# Mean time to isolation 4.6 and 100% of age category removed
t_lockdown_vec = [46]
n_sims = 100
sim_end = 119
today = 67
group_vec_age = ['0-14','15-29','30-49','50-69','70+']

combo_start = 0
combo_end = 10
pop_size = 10000000

filename1 = 'lombardy_distributed_agepolicy_0_paramsweep_n10000000.0_i'
filename2 = 'lombardy_distributed_agepolicy_1_paramsweep_n10000000.0_i'

folder1 = 'perc_policy_results/run1/'
folder2 = 'perc_policy_results/run2/'
option = 1
specific_title = ''

death_age_group_node_removal(group_vec_age,t_lockdown_vec, n_sims, sim_end, today, combo_start, combo_end, folder1, folder2, filename1, filename2, option, specific_title)
```

## Figure 9(a)

```python
# Mean time to isolation 4.6 and 50% of age category removed
t_lockdown_vec = [46]
n_sims = 100
sim_end = 119
# As of March 29 of 2020
today = 67
group_vec_age = ['0-14','15-29','30-49','50-69','70+']

combo_start = 0
combo_end = 10
pop_size = 10000000

filename = 'lombardy_distributed_agepolicy_yeslockdown_0_paramsweep_n10000000.0_i'
folder = 'lockdown_perc_policy_results/'
option = 2
specific_title = ''

perc_infected_age_group_node_removal_lockdown(pop_size, group_vec_age, t_lockdown_vec, n_sims, sim_end, today, combo_start, combo_end, folder, filename, option, specific_title)
```

## Figure 9(c)

```python
# Mean time to isolation 4.6 and 50% of age category removed
t_lockdown_vec = [46]
n_sims = 100
sim_end = 119
# As of March 29 of 2020
today = 67
group_vec_age = ['0-14','15-29','30-49','50-69','70+']

combo_start = 0
combo_end = 10
pop_size = 10000000

filename = 'lombardy_distributed_agepolicy_yeslockdown_0_paramsweep_n10000000.0_i'
folder = 'lockdown_perc_policy_results/'
option = 2
specific_title = ''

death_age_group_node_removal_lockdown(group_vec_age, t_lockdown_vec, n_sims, sim_end, today, combo_start, combo_end, folder, filename, option, specific_title)
```

## Figure 9(b)

```python
# Mean time to isolation 4.6 and 100% of age category removed
t_lockdown_vec = [46]
n_sims = 100
sim_end = 119
today = 67
group_vec_age = ['0-14','15-29','30-49','50-69','70+']

combo_start = 0
combo_end = 10
pop_size = 10000000

filename = 'lombardy_distributed_agepolicy_yeslockdown_0_paramsweep_n10000000.0_i'

folder = 'lockdown_perc_policy_results/'
option = 1
# Lombardy - Time of Lockdown = 46 days\n, \nInfected = Mild+Severe+Critical+R+D
#specific_title = 'Mean Time to Isolation = 4.6 days for all' + '\n100% stay home, per age group' + '\n+ Social distance increased by a factor of 2'
specific_title = ''

perc_infected_age_group_node_removal_lockdown(pop_size, group_vec_age, t_lockdown_vec, n_sims, sim_end, today, combo_start, combo_end, folder, filename, option, specific_title)
```

## Figure 9(d)

```python
# Mean time to isolation 4.6 and 100% of age category removed
t_lockdown_vec = [46]
n_sims = 100
sim_end = 119
today = 67
group_vec_age = ['0-14','15-29','30-49','50-69','70+']

combo_start = 0
combo_end = 10
pop_size = 10000000

filename = 'lombardy_distributed_agepolicy_yeslockdown_0_paramsweep_n10000000.0_i'

folder = 'lockdown_perc_policy_results/'
option = 1
specific_title = ''

death_age_group_node_removal_lockdown(group_vec_age, t_lockdown_vec, n_sims, sim_end, today, combo_start, combo_end, folder, filename, option, specific_title)
```


# The Piecewise-Parabolic Method

This notebook documents the function from the original `GiRaFFE` that implements the reconstruction algorithm used by the piecewise-parabolic method (PPM) of [Colella and Woodward (1984)](https://crd.lbl.gov/assets/pubs_presos/AMCS/ANAG/A141984.pdf).

The differential equations that `GiRaFFE` evolves have two different terms that contribute to the time evolution of some quantity: the flux term and the source term. The PPM method is what the original `GiRaFFE` uses to handle the flux term; hopefully, using this instead of finite-differencing will fix some of the problems we've been having with `GiRaFFE_HO`.

This algorithm is not quite as accessible as the much simpler finite-difference methods; as such, [this notebook](https://mybinder.org/v2/gh/python-hydro/how_to_write_a_hydro_code/master) is recommended as an introduction. It covers a simpler reconstruction scheme, and proved useful in preparing the documentation for this more complicated scheme.

The algorithm for finite-volume methods in general is as follows:

1. **The Reconstruction Step (This notebook)**
    1. **Within each cell, fit to a function that conserves the volume in that cell using information from the neighboring cells**
        * **For PPM, we will naturally use parabolas**
    1. **Use that fit to define the state at the left and right interface of each cell**
    1. **Apply a slope limiter to mitigate Gibbs phenomenon**
1. Solving the Riemann Problem
    1. Use the left and right reconstructed states to calculate the unique state at boundary
    1. Use the unique state to estimate the derivative in the cell
1. Repeat the above for each conservative gridfunction in each direction

# Table of Contents
$$\label{toc}$$

This module is organized as follows

0. [Step 0](#prelim): Preliminaries
1. [Step 1](#reconstruction): The reconstruction function
    1. [Step 1.a](#define): Some definitions and declarations
    1. [Step 1.b](#func): The function definition
    1. [Step 1.c](#face): Interpolate the face values
    1. [Step 1.d](#monotonize): Monotonize the values within each cell
    1. [Step 1.e](#shift): Shift indices
1. [Step 2](#slope_limit): The slope limiter
1. [Step 3](#monotonize_def): The monotonization algorithm
1. [Step 4](#latex_pdf_output): Output this module to $\LaTeX$-formatted PDF file

## Step 0: Preliminaries \[Back to [top](#toc)\]
$$\label{prelim}$$

This first block of code just sets up a subdirectory within `GiRaFFE_standalone_Ccodes/` to which we will write the C code.

```python
import cmdline_helper as cmd
import os
outdir = "GiRaFFE_standalone_Ccodes/PPM"
cmd.mkdir(os.path.join(outdir,"/"))
```

When we convert the code to work with NRPy+, we will be able to make a simplification: since `GiRaFFE_HO` does not use staggered grids, we will be able to skip reconstructing the staggered quantities

The structure `gf_and_gz_struct` is a C++ structure used to keep track of ghostzone information between routines. It contains a pointer and two arrays. It is specified by the following code:

```c
// Keeping track of ghostzones between routines is a nightmare, so
//   we instead attach ghostzone info to each gridfunction and set
//   the ghostzone information correctly within each routine.
struct gf_and_gz_struct {
  REAL *gf;
  int gz_lo[4],gz_hi[4];
};
```

## Step 1: The reconstruction function \[Back to [top](#toc)\]
$$\label{reconstruction}$$

### Step 1.a: Some definitions and declarations \[Back to [top](#toc)\]
$$\label{define}$$

This file contains the functions necessary for reconstruction. It is based on Colella & Woodward PPM in the case where pressure and density $P = \rho = 0$.

We start by defining the values of `MINUS2`...`PLUS2` as $\{0, \ldots ,4\}$ for the sake of convenience later on; we also define `MAXNUMINDICES` as 5 so we can easily loop over the above. We include `loop_defines_reconstruction.h` for some macros that will allow us to conveniently write common loops that we will use (this will be imminently replaced with the NRPy+ standard `LOOP_REGION` **(Actually, might be better to directly port these)**) and give the function prototypes for our slope limiter, `slope_limit()`, and our monotization algorithm, `monotonize()`.

```python
%%writefile $outdir/reconstruct_set_of_prims_PPM_GRFFE.C
/*****************************************
 * PPM Reconstruction Interface.
 * Zachariah B. Etienne (2013)
 *
 * This version of PPM implements the standard
 * Colella & Woodward PPM, but in the GRFFE
 * limit, where P=rho=0. Thus, e.g., ftilde=0.
 *****************************************/

#define MINUS2 0
#define MINUS1 1
#define PLUS0  2
#define PLUS1  3
#define PLUS2  4
#define MAXNUMINDICES 5
//    ^^^^^^^^^^^^^ Be _sure_ to define MAXNUMINDICES appropriately!

// You'll find the #define's for LOOP_DEFINE and SET_INDEX_ARRAYS inside:
#include "loop_defines_reconstruction.h"

static inline REAL slope_limit(const REAL dU,const REAL dUp1);
static inline void monotonize(const REAL U,REAL &Ur,REAL &Ul);

```

    Overwriting GiRaFFE_standalone_Ccodes/PPM/reconstruct_set_of_prims_PPM_GRFFE.C

### Step 1.b: The function definition \[Back to [top](#toc)\]
$$\label{func}$$

Here, we start the function definition for the main function for our reconstruction, `reconstruct_set_of_prims_PPM_GRFFE()`. Among its parameters are the arrays that define the grid (that will need to be replaced with NRPy+ equivalents), a flux direction, the integer array specifying which primitives to reconstruct (as well as the number of primitives to reconstruct), the input structure `in_prims`, the output structures `out_prims_r` and `out_prims_l`, and a temporary array (this will be used to help switch variable names).

We then check the number of ghostzones and error out if there are too few - this method requires three. Note the `for` loop here; it continues through the next two cells as well, looping over each primitive we will reconstruct in the chosen direction.

```python
%%writefile -a $outdir/reconstruct_set_of_prims_PPM_GRFFE.C

static void reconstruct_set_of_prims_PPM_GRFFE(const cGH *cctkGH,const int *cctk_lsh,const int flux_dirn,const int num_prims_to_reconstruct,const int *which_prims_to_reconstruct,
                                               const gf_and_gz_struct *in_prims,gf_and_gz_struct *out_prims_r,gf_and_gz_struct *out_prims_l, REAL *temporary) {

  REAL U[MAXNUMVARS][MAXNUMINDICES],dU[MAXNUMVARS][MAXNUMINDICES],slope_lim_dU[MAXNUMVARS][MAXNUMINDICES],
    Ur[MAXNUMVARS][MAXNUMINDICES],Ul[MAXNUMVARS][MAXNUMINDICES];
  int ijkgz_lo_hi[4][2];

  for(int ww=0;ww<num_prims_to_reconstruct;ww++) {
    const int whichvar=which_prims_to_reconstruct[ww];

    if(in_prims[whichvar].gz_lo[flux_dirn]!=0 || in_prims[whichvar].gz_hi[flux_dirn]!=0) {
      CCTK_VError(VERR_DEF_PARAMS,"TOO MANY GZ'S! WHICHVAR=%d: %d %d %d : %d %d %d DIRECTION %d",whichvar,
		  in_prims[whichvar].gz_lo[1],in_prims[whichvar].gz_lo[2],in_prims[whichvar].gz_lo[3],
		  in_prims[whichvar].gz_hi[1],in_prims[whichvar].gz_hi[2],in_prims[whichvar].gz_hi[3],flux_dirn);
    }

```

    Appending to GiRaFFE_standalone_Ccodes/PPM/reconstruct_set_of_prims_PPM_GRFFE.C

### Step 1.c: Interpolate the face values \[Back to [top](#toc)\]
$$\label{face}$$

In Loop 1, we will interpolate the face values at the left and right interfaces, `Ur` and `Ul`, respectively. This is done on a point-by-point basis as defined by the `LOOP_DEFINE`.

After reading in the relevant values from memory, we calculate thesimple `dU`:
\begin{align}
dU_{-1} &= U_{-1} - U_{-2} \\
dU_{+0} &= U_{+0} - U_{-1} \\
dU_{+1} &= U_{+1} - U_{+0} \\
dU_{+2} &= U_{+2} - U_{+1}. \\
\end{align}
From that, we compute the slope-limited `slope_lim_dU`, or $\nabla U$ (see [below](#slope_limit)). Then, we compute the face values using eq. A1 from [arxiv:astro-ph/050342](http://arxiv.org/pdf/astro-ph/0503420.pdf), adapted from 1.9 in [Colella and Woodward (1984)](https://crd.lbl.gov/assets/pubs_presos/AMCS/ANAG/A141984.pdf):
\begin{align}
U_r &= \frac{1}{2} \left( U_{+1} + U_{+0} \right) + \frac{1}{6} \left( \nabla U_{+0} - \nabla U_{+1} \right) \\
U_l &= \frac{1}{2} \left( U_{+0} + U_{-1} \right) + \frac{1}{6} \left( \nabla U_{-1} - \nabla U_{+0} \right). \\
\end{align}
(Note, however, that we use the standard coefficient $1/6$ instead of $1/8$.) Finally, we write the values to memory in the output structures.

```python
%%writefile -a $outdir/reconstruct_set_of_prims_PPM_GRFFE.C

    // *** LOOP 1: Interpolate to Ur and Ul, which are face values ***
    //  You will find that Ur depends on U at MINUS1,PLUS0, PLUS1,PLUS2, and
    //                     Ul depends on U at MINUS2,MINUS1,PLUS0,PLUS1.
    //  However, we define the below loop from MINUS2 to PLUS2. Why not split
    //     this up and get additional points? Maybe we should. In GRMHD, the
    //     reason is that later on, Ur and Ul depend on ftilde, which is
    //     defined from MINUS2 to PLUS2, so we would lose those points anyway.
    //     But in GRFFE, ftilde is set to zero, so there may be a potential
    //     for boosting performance here.
    LOOP_DEFINE(2,2,  cctk_lsh,flux_dirn,  ijkgz_lo_hi,in_prims[whichvar].gz_lo,in_prims[whichvar].gz_hi) {
      SET_INDEX_ARRAYS(-2,2,flux_dirn);
      /* *** LOOP 1a: READ INPUT *** */
      // Read in a primitive at all gridpoints between m = MINUS2 & PLUS2, where m's direction is given by flux_dirn. Store to U.
      for(int ii=MINUS2;ii<=PLUS2;ii++) U[whichvar][ii] = in_prims[whichvar].gf[index_arr[flux_dirn][ii]];

      /* *** LOOP 1b: DO COMPUTATION *** */
      /* First, compute simple dU = U(i) - U(i-1), where direction of i
       *         is given by flux_dirn, and U is a primitive variable:
       *         {vx,vy,vz,Bx,By,Bz}. */
      // Note that for Ur and Ul at i, we must compute dU(i-1),dU(i),dU(i+1),
      //         and dU(i+2)
      dU[whichvar][MINUS1] = U[whichvar][MINUS1]- U[whichvar][MINUS2];
      dU[whichvar][PLUS0]  = U[whichvar][PLUS0] - U[whichvar][MINUS1];
      dU[whichvar][PLUS1]  = U[whichvar][PLUS1] - U[whichvar][PLUS0];
      dU[whichvar][PLUS2]  = U[whichvar][PLUS2] - U[whichvar][PLUS1];

      // Then, compute slope-limited dU, using MC slope limiter:
      slope_lim_dU[whichvar][MINUS1]=slope_limit(dU[whichvar][MINUS1],dU[whichvar][PLUS0]);
      slope_lim_dU[whichvar][PLUS0] =slope_limit(dU[whichvar][PLUS0], dU[whichvar][PLUS1]);
      slope_lim_dU[whichvar][PLUS1] =slope_limit(dU[whichvar][PLUS1], dU[whichvar][PLUS2]);

      // Finally, compute face values Ur and Ul based on the PPM prescription
      //   (Eq. A1 in http://arxiv.org/pdf/astro-ph/0503420.pdf, but using standard 1/6=(1.0/6.0) coefficient)
      // Ur[PLUS0] represents U(i+1/2)
      // We applied a simplification to the following line: Ur=U+0.5*(U(i+1)-U) + ... = 0.5*(U(i+1)+U) + ...
      Ur[whichvar][PLUS0] = 0.5*(U[whichvar][PLUS1] + U[whichvar][PLUS0] ) + (1.0/6.0)*(slope_lim_dU[whichvar][PLUS0]  - slope_lim_dU[whichvar][PLUS1]);
      // Ul[PLUS0] represents U(i-1/2)
      // We applied a simplification to the following line: Ul=U(i-1)+0.5*(U-U(i-1)) + ... = 0.5*(U+U(i-1)) + ...
      Ul[whichvar][PLUS0] = 0.5*(U[whichvar][PLUS0] + U[whichvar][MINUS1]) + (1.0/6.0)*(slope_lim_dU[whichvar][MINUS1] - slope_lim_dU[whichvar][PLUS0]);

      /* *** LOOP 1c: WRITE OUTPUT *** */
      // Store right face values to {vxr,vyr,vzr,Bxr,Byr,Bzr},
      //    and left face values to {vxl,vyl,vzl,Bxl,Byl,Bzl}
      out_prims_r[whichvar].gf[index_arr[flux_dirn][PLUS0]] = Ur[whichvar][PLUS0];
      out_prims_l[whichvar].gf[index_arr[flux_dirn][PLUS0]] = Ul[whichvar][PLUS0];
    }

```

    Appending to GiRaFFE_standalone_Ccodes/PPM/reconstruct_set_of_prims_PPM_GRFFE.C

### Step 1.d: Monotonize the values within each cell \[Back to [top](#toc)\]
$$\label{monotonize}$$

We skip Loop 2 in GRFFE; then, we flatten the data in Loop 3 (but since we flattenbased on `ftilde_gf`, which is 0 in GRFFE, we again don't really do anything). Also in Loop 3, we call the `monotonize()` function on the face values. This function adjusts the face values to ensure that the data is monotonic within each cell to avoid the Gibbs phenomenon.

```python
%%writefile -a $outdir/reconstruct_set_of_prims_PPM_GRFFE.C

    // *** LOOP 2 (REMOVED): STEEPEN RHOB. RHOB DOES NOT EXIST IN GRFFE EQUATIONS ***
  }

  // *** LOOP 3: FLATTEN BASED ON FTILDE AND MONOTONIZE ***
  for(int ww=0;ww<num_prims_to_reconstruct;ww++) {
    const int whichvar=which_prims_to_reconstruct[ww];
    // ftilde() depends on P(MINUS2,MINUS1,PLUS1,PLUS2), THUS IS SET TO ZERO IN GRFFE
    LOOP_DEFINE(2,2,  cctk_lsh,flux_dirn,  ijkgz_lo_hi,in_prims[whichvar].gz_lo,in_prims[whichvar].gz_hi) {
      SET_INDEX_ARRAYS(0,0,flux_dirn);

      U[whichvar][PLUS0]  = in_prims[whichvar].gf[index_arr[flux_dirn][PLUS0]];
      Ur[whichvar][PLUS0] = out_prims_r[whichvar].gf[index_arr[flux_dirn][PLUS0]];
      Ul[whichvar][PLUS0] = out_prims_l[whichvar].gf[index_arr[flux_dirn][PLUS0]];

      // ftilde_gf was computed in the function compute_ftilde_gf(), called before this routine
      //REAL ftilde = ftilde_gf[index_arr[flux_dirn][PLUS0]];
      // ...and then flatten (local operation)
      Ur[whichvar][PLUS0]   = Ur[whichvar][PLUS0];
      Ul[whichvar][PLUS0]   = Ul[whichvar][PLUS0];

      // Then monotonize
      monotonize(U[whichvar][PLUS0],Ur[whichvar][PLUS0],Ul[whichvar][PLUS0]);

      out_prims_r[whichvar].gf[index_arr[flux_dirn][PLUS0]] = Ur[whichvar][PLUS0];
      out_prims_l[whichvar].gf[index_arr[flux_dirn][PLUS0]] = Ul[whichvar][PLUS0];
    }
    // Note: ftilde=0 in GRFFE. Ur depends on ftilde, which depends on points of U between MINUS2 and PLUS2
    out_prims_r[whichvar].gz_lo[flux_dirn]+=2;
    out_prims_r[whichvar].gz_hi[flux_dirn]+=2;
    // Note: ftilde=0 in GRFFE. Ul depends on ftilde, which depends on points of U between MINUS2 and PLUS2
    out_prims_l[whichvar].gz_lo[flux_dirn]+=2;
    out_prims_l[whichvar].gz_hi[flux_dirn]+=2;
  }

```

    Appending to GiRaFFE_standalone_Ccodes/PPM/reconstruct_set_of_prims_PPM_GRFFE.C

### Step 1.e: Shift indices \[Back to [top](#toc)\]
$$\label{shift}$$

In Loop 4, we will shift the indices of `Ur` and `Ul`. So far, we have been concerned with the behavior of the data within a single cell. In that context, it makes sense to call the value of data at the left end of the cell `Ul` and the data at the right end of the cell `Ur`. However, going forward, we will be concerned about the behavior of the data at the interface between cells. In this context, it sense to call the value of data on the left of the interface (which is at the right end of the cell!) `Ul` and the data on the right of the interface `Ur`. So, using the array `temporary`, we switch the two names.

```python
%%writefile -a $outdir/reconstruct_set_of_prims_PPM_GRFFE.C

// *** LOOP 4: SHIFT Ur AND Ul ***
  /* Currently face values are set so that
   *      a) Ur(i) represents U(i+1/2), and
   *      b) Ul(i) represents U(i-1/2)
   *    Here, we shift so that the indices are consistent:
   *      a) U(i-1/2+epsilon) = oldUl(i)   = newUr(i)
   *      b) U(i-1/2-epsilon) = oldUr(i-1) = newUl(i)
   *    Note that this step is not strictly necessary if you keep
   *      track of indices when computing the flux. */
  for(int ww=0;ww<num_prims_to_reconstruct;ww++) {
    const int whichvar=which_prims_to_reconstruct[ww];
    LOOP_DEFINE(3,2,  cctk_lsh,flux_dirn,  ijkgz_lo_hi,in_prims[whichvar].gz_lo,in_prims[whichvar].gz_hi) {
      SET_INDEX_ARRAYS(-1,0,flux_dirn);
      temporary[index_arr[flux_dirn][PLUS0]] = out_prims_r[whichvar].gf[index_arr[flux_dirn][MINUS1]];
    }

    LOOP_DEFINE(3,2,  cctk_lsh,flux_dirn,  ijkgz_lo_hi,in_prims[whichvar].gz_lo,in_prims[whichvar].gz_hi) {
      SET_INDEX_ARRAYS(0,0,flux_dirn);
      // Then shift so that Ur represents the gridpoint at i-1/2+epsilon,
      //                and Ul represents the gridpoint at i-1/2-epsilon.
      // Ur(i-1/2) = Ul(i-1/2)     = U(i-1/2+epsilon)
      // Ul(i-1/2) = Ur(i+1/2 - 1) = U(i-1/2-epsilon)
      out_prims_r[whichvar].gf[index_arr[flux_dirn][PLUS0]] = out_prims_l[whichvar].gf[index_arr[flux_dirn][PLUS0]];
      out_prims_l[whichvar].gf[index_arr[flux_dirn][PLUS0]] = temporary[index_arr[flux_dirn][PLUS0]];
    }
    // Ul was just shifted, so we lost another ghostzone.
    out_prims_l[whichvar].gz_lo[flux_dirn]+=1;
    out_prims_l[whichvar].gz_hi[flux_dirn]+=0;
    // As for Ur, we didn't need to get rid of another ghostzone,
    //    but we did ... seems wasteful!
    out_prims_r[whichvar].gz_lo[flux_dirn]+=1;
    out_prims_r[whichvar].gz_hi[flux_dirn]+=0;

  }
}

```

    Appending to GiRaFFE_standalone_Ccodes/PPM/reconstruct_set_of_prims_PPM_GRFFE.C

## Step 2: The slope limiter \[Back to [top](#toc)\]
$$\label{slope_limit}$$

The first function here implements the Monotonized Central (MC) reconstruction slope limiter:
$$ MC(a,b) = \left \{ \begin{array}{ll}
             0 & {\rm if} ab \leq 0 \\
             {\rm sign}(a) \min(2|a|,2|b|, |a+b|/2) & {\rm otherwise.}
             \end{array} \right.
             $$

This is adapted from eq. 1.8 of [Colella and Woodward (1984)](https://crd.lbl.gov/assets/pubs_presos/AMCS/ANAG/A141984.pdf).

```python
%%writefile -a $outdir/reconstruct_set_of_prims_PPM_GRFFE.C

// Set SLOPE_LIMITER_COEFF = 2.0 for MC, 1 for minmod
#define SLOPE_LIMITER_COEFF 2.0

//Eq. 60 in JOURNAL OF COMPUTATIONAL PHYSICS 123, 1-14 (1996)
//   [note the factor of 2 missing in the |a_{j+1} - a_{j}| term].
//   Recall that dU = U_{i} - U_{i-1}.
static inline REAL slope_limit(const REAL dU,const REAL dUp1) {
  if(dU*dUp1 > 0.0) {
    //delta_m_U=0.5 * [ (u_(i+1)-u_i) + (u_i-u_(i-1)) ] = (u_(i+1) - u_(i-1))/2  <-- first derivative, second-order; this should happen most of the time (smooth flows)
    const REAL delta_m_U = 0.5*(dU + dUp1);
    // EXPLANATION OF BELOW LINE OF CODE.
    // In short, sign_delta_a_j = sign(delta_m_U) = (0.0 < delta_m_U) - (delta_m_U < 0.0).
    //    If delta_m_U>0, then (0.0 < delta_m_U)==1, and (delta_m_U < 0.0)==0, so sign_delta_a_j=+1
    //    If delta_m_U<0, then (0.0 < delta_m_U)==0, and (delta_m_U < 0.0)==1, so sign_delta_a_j=-1
    //    If delta_m_U==0,then (0.0 < delta_m_U)==0, and (delta_m_U < 0.0)==0, so sign_delta_a_j=0
    const int sign_delta_m_U = (0.0 < delta_m_U) - (delta_m_U < 0.0);
    //Decide whether to use 2nd order derivative or first-order derivative, limiting slope.
    return sign_delta_m_U*MIN(fabs(delta_m_U),MIN(SLOPE_LIMITER_COEFF*fabs(dUp1),SLOPE_LIMITER_COEFF*fabs(dU)));
  }
  return 0.0;
}

```

    Appending to GiRaFFE_standalone_Ccodes/PPM/reconstruct_set_of_prims_PPM_GRFFE.C

## Step 3: The monotonization algorithm \[Back to [top](#toc)\]
$$\label{monotonize_def}$$

The next function monotonizes the slopes using the algorithm from [Colella and Woodward (1984)](https://crd.lbl.gov/assets/pubs_presos/AMCS/ANAG/A141984.pdf), eq. 1.10. We want the slope to be monotonic in a cell in order to reduce the impact of the Gibbs phenomenon. So, we consider three values in the cell: the cell average, `U`; on the left interface of the cell, `Ul`; and on the right interface of the cell, `Ur`. The goal of the algorithm is to ensure monotonicity; so, it first checks to see if the cell contains a local extremum. If it does, we make the interpolation function a constant.We must then also consider the case where `U` is "close" to `Ur` or `Ul`, and an interpolating polynomial between them would not be monotonic over the cell. So, the basic algorithm is as follows:

* `dU = Ur - Ul`
* `mU = 0.5*(Ur+Ul)`.
* If the cell has an extremum:
    * `Ur = U`
    * `Ul = U`
* If `U` is too close to `Ul`
    * Move `Ul` farther away
* If `U` is too close to `Ur`
    * Move `Ur` farther away

More rigorous definitions of "Too Close" and "Farther Away" are derived from parabolas with vertices on the interfaces, as can be seen in the code below:

```python
%%writefile -a $outdir/reconstruct_set_of_prims_PPM_GRFFE.C

static inline void monotonize(const REAL U,REAL &Ur,REAL &Ul) {
  const REAL dU = Ur - Ul;
  const REAL mU = 0.5*(Ur+Ul);

  if ( (Ur-U)*(U-Ul) <= 0.0) {
    Ur = U;
    Ul = U;
    return;
  }
  if ( dU*(U-mU) > (1.0/6.0)*SQR(dU)) {
    Ul = 3.0*U - 2.0*Ur;
    return;
  }
  if ( dU*(U-mU) < -(1.0/6.0)*SQR(dU)) {
    Ur = 3.0*U - 2.0*Ul;
    return;
  }
}

```

    Appending to GiRaFFE_standalone_Ccodes/PPM/reconstruct_set_of_prims_PPM_GRFFE.C

# Step 4: Output this module to $\LaTeX$-formatted PDF file \[Back to [top](#toc)\]
$$\label{latex_pdf_output}$$

The following code cell converts this Jupyter notebook into a proper, clickable $\LaTeX$-formatted PDF file. After the cell is successfully run, the generated PDF may be found in the root NRPy+ tutorial directory, with filename
[Tutorial-GiRaFFE_HO_Ccode_library-PPM.pdf](Tutorial-GiRaFFE_HO_Ccode_library-PPM.pdf) (Note that clicking on this link may not work; you may need to open the PDF file through another means.)

```python
!jupyter nbconvert --to latex --template latex_nrpy_style.tplx Tutorial-GiRaFFE_HO_Ccode_library-PPM.ipynb
!pdflatex -interaction=batchmode Tutorial-GiRaFFE_HO_Ccode_library-PPM.tex
!pdflatex -interaction=batchmode Tutorial-GiRaFFE_HO_Ccode_library-PPM.tex
!pdflatex -interaction=batchmode Tutorial-GiRaFFE_HO_Ccode_library-PPM.tex
!rm -f Tut*.out Tut*.aux Tut*.log
```