├── tests ├── __init__.py ├── testthat.R ├── adapter_covid19 │ ├── data │ │ ├── credit_score.csv │ │ ├── smallcap_cash.csv │ │ ├── wfh.csv │ │ ├── keyworker.csv │ │ ├── sme_rate_payer_vulnerability.csv │ │ ├── largecap_count.csv │ │ ├── vulnerability.csv │ │ ├── sme_count.csv │ │ ├── growth_rates.csv │ │ ├── largecap_pct_turnover.csv │ │ ├── furloughed.csv │ │ ├── populations.csv │ │ ├── input_output_final.csv │ │ ├── input_output.csv │ │ ├── input_output_primary.csv │ │ ├── wages.csv │ │ └── README.md │ ├── test_data_structures.py │ ├── test_personal_bankruptcy_models.py │ ├── test_corporate_bankruptcy_models.py │ ├── test_economics.py │ └── utilities.py ├── testthat │ ├── test-util.R │ ├── test-SeedingSchedule.R │ ├── test-Simulation.R │ └── test-VaccineSchedule.R ├── requirements.txt ├── test_R_INSTALL_win.bat ├── Dockerfile ├── data │ ├── hospital_baseline_parameters.csv │ └── hospital_baseline_parameters_transpose.csv ├── test_python_param_setting.py ├── conftest.py └── constant.py ├── src ├── COVID19 │ ├── __init__.py │ ├── default_params │ │ └── hospital_baseline_parameters.csv │ ├── strain.py │ ├── vaccine.py │ └── network.py ├── adapter_covid19 │ ├── __init__.py │ ├── constants.py │ ├── example.py │ ├── lockdown.py │ ├── README.md │ ├── metrics.py │ ├── economics.py │ └── datasources.py ├── Makevars.in ├── Makevars.win ├── strain_utils.i ├── list.h ├── nurse.c ├── doctor.c ├── model_utils.i ├── structure.h ├── nurse.h ├── doctor.h ├── vaccine_utils.i ├── setup.py ├── network_utils.i ├── demographics.h ├── ward.h ├── input.h ├── covid19.i ├── disease.h ├── constant.c ├── utilities.h ├── strain.h ├── list.c ├── network.h ├── hospital.h └── interventions.h ├── examples ├── results │ └── .gitkeep ├── geo_plot.py ├── example_101.py ├── example_101.R ├── examples_add_network.R ├── example_utils.py ├── example_vaccination.R ├── multi_region.py ├── example_102.R ├── multi_run_simulator.py ├── example_run_simulation_with_lockdown.py └── example_multi_strain_vaccinate.py ├── documentation ├── eqn_prob.png ├── eqn_hazard.png ├── covid19_tests.pdf ├── diagram_line.png ├── economic_model.pdf ├── hospital_model.pdf ├── susceptibility.png ├── eqn_transmission_rate.png ├── parameters │ ├── work-place_network_parameters.md │ ├── random_network_parameters.md │ ├── demographic_parameters.md │ ├── manual_tracing_parameters.md │ ├── passive_intervention_parameters.md │ └── infection_parameters.md └── output_files │ ├── individual_file.md │ ├── transmission_file.md │ └── timeseries.md ├── package-for-CRAN.sh ├── configure.ac ├── .github └── workflows │ └── docker-test.yml ├── R ├── swig_pointer_classes.R ├── VaccineSchedule.R ├── Strain.R └── Vaccine.R ├── OpenABM-Covid19.Rproj ├── man ├── Model.one_time_step.Rd ├── Model.get_param.Rd ├── Model.results.Rd ├── Model.get_transmissions.Rd ├── Model.run.Rd ├── Model.one_time_step_results.Rd ├── Model.update_running_params.Rd ├── Parameters.get_param.Rd ├── Parameters.set_param.Rd ├── Model.new.Rd ├── Parameters.new.Rd └── swig_methods.Rd ├── inst └── default_params │ └── hospital_baseline_parameters.csv ├── .Rbuildignore ├── performance └── test_performance.sh ├── .gitignore ├── NAMESPACE ├── CONTRIBUTING.md ├── python ├── create_output_file_dictionaries.py └── transpose_parameters.py ├── DESCRIPTION └── Makefile /tests/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/COVID19/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /examples/results/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/adapter_covid19/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/testthat.R: -------------------------------------------------------------------------------- 1 | library(testthat) 2 | library(OpenABMCovid19) 3 | 4 | test_check("OpenABMCovid19") 5 | -------------------------------------------------------------------------------- /documentation/eqn_prob.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BDI-pathogens/OpenABM-Covid19/HEAD/documentation/eqn_prob.png -------------------------------------------------------------------------------- /documentation/eqn_hazard.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BDI-pathogens/OpenABM-Covid19/HEAD/documentation/eqn_hazard.png -------------------------------------------------------------------------------- /documentation/covid19_tests.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BDI-pathogens/OpenABM-Covid19/HEAD/documentation/covid19_tests.pdf -------------------------------------------------------------------------------- /documentation/diagram_line.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BDI-pathogens/OpenABM-Covid19/HEAD/documentation/diagram_line.png -------------------------------------------------------------------------------- /documentation/economic_model.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BDI-pathogens/OpenABM-Covid19/HEAD/documentation/economic_model.pdf -------------------------------------------------------------------------------- /documentation/hospital_model.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BDI-pathogens/OpenABM-Covid19/HEAD/documentation/hospital_model.pdf -------------------------------------------------------------------------------- /documentation/susceptibility.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BDI-pathogens/OpenABM-Covid19/HEAD/documentation/susceptibility.png -------------------------------------------------------------------------------- /documentation/eqn_transmission_rate.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BDI-pathogens/OpenABM-Covid19/HEAD/documentation/eqn_transmission_rate.png -------------------------------------------------------------------------------- /src/Makevars.in: -------------------------------------------------------------------------------- 1 | GSL_CFLAGS=@GSL_CFLAGS@ 2 | GSL_LIBS=@GSL_LIBS@ 3 | 4 | PKG_CFLAGS=$(GSL_CFLAGS) -DBUILD_RPKG 5 | PKG_LIBS=$(GSL_LIBS) -lm 6 | -------------------------------------------------------------------------------- /package-for-CRAN.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # Initialize all generated files and run `R CMD build`. 3 | # 4 | autoconf 5 | make Rswig 6 | R -e 'devtools::document()' 7 | R CMD build . 8 | -------------------------------------------------------------------------------- /src/adapter_covid19/constants.py: -------------------------------------------------------------------------------- 1 | import re 2 | import string 3 | 4 | # Utilities 5 | URL_REGEX = re.compile(r"^(https?|file)://") 6 | 7 | START_OF_TIME = 0 8 | DAYS_IN_A_YEAR = 365.25 9 | WEEKS_IN_A_YEAR = 52.0 10 | -------------------------------------------------------------------------------- /src/Makevars.win: -------------------------------------------------------------------------------- 1 | # To comply with CRAN's build-infrastructure, environment variable LIB_GSL 2 | # should point to a Windows GNU GSL install. 3 | PKG_CFLAGS= -I$(LIB_GSL)/include -DBUILD_RPKG 4 | PKG_LIBS=-L$(LIB_GSL)/lib$(R_ARCH) -lgsl -lgslcblas -lm 5 | -------------------------------------------------------------------------------- /tests/adapter_covid19/data/credit_score.csv: -------------------------------------------------------------------------------- 1 | Region,mean,stdev 2 | C_NE,800,200 3 | D_NW,800,200 4 | E_YORKSHIRE,800,200 5 | F_EM,800,200 6 | G_WM,800,200 7 | H_E,800,200 8 | I_LONDON,800,200 9 | J_SE,800,200 10 | K_SW,800,200 11 | L_WALES,800,200 12 | M_SCOTLAND,800,200 13 | N_NI,800,200 14 | -------------------------------------------------------------------------------- /tests/testthat/test-util.R: -------------------------------------------------------------------------------- 1 | test_that("get_base_param_from_enum", { 2 | expect_equal(get_base_param_from_enum("fatality_fraction"), NULL) 3 | expect_equal(get_base_param_from_enum("fatality_fraction_20_29")$base_name, 'fatality_fraction') 4 | expect_equal(get_base_param_from_enum("fatality_fraction_20_29")$index, 2) 5 | }) 6 | -------------------------------------------------------------------------------- /tests/requirements.txt: -------------------------------------------------------------------------------- 1 | attrs==19.3.0 2 | more-itertools==8.2.0 3 | numpy==1.18.2 4 | packaging==20.3 5 | pandas==1.0.3 6 | pexpect==4.8.0 7 | pluggy==0.13.1 8 | ptyprocess==0.6.0 9 | py==1.8.1 10 | pyparsing==2.4.6 11 | pytest==5.4.1 12 | python-dateutil==2.8.1 13 | pytz==2019.3 14 | six==1.14.0 15 | wcwidth==0.1.9 16 | scipy==1.4.1 17 | wheel==0.34.2 18 | -------------------------------------------------------------------------------- /configure.ac: -------------------------------------------------------------------------------- 1 | AC_INIT([OpenABMCovid19], 0.3) 2 | 3 | AC_PATH_PROG([GSL_CONFIG], [gsl-config]) 4 | if test "${GSL_CONFIG}" != ""; then 5 | GSL_CFLAGS=`${GSL_CONFIG} --cflags` 6 | GSL_LIBS=`${GSL_CONFIG} --libs` 7 | AC_SUBST(GSL_CFLAGS) 8 | AC_SUBST(GSL_LIBS) 9 | AC_OUTPUT(src/Makevars) 10 | else 11 | AC_MSG_ERROR(['gsl-config not found, GNU GSL required']) 12 | fi 13 | 14 | -------------------------------------------------------------------------------- /.github/workflows/docker-test.yml: -------------------------------------------------------------------------------- 1 | name: OpenABM Tests 2 | 3 | on: 4 | push: 5 | branches: [master] 6 | pull_request: 7 | 8 | jobs: 9 | build: 10 | 11 | runs-on: ubuntu-latest 12 | 13 | steps: 14 | - uses: actions/checkout@v2 15 | - name: build container 16 | run: docker build --tag test -f tests/Dockerfile . 17 | - name: run tests 18 | run: docker run test 19 | -------------------------------------------------------------------------------- /tests/test_R_INSTALL_win.bat: -------------------------------------------------------------------------------- 1 | 2 | @echo off 3 | 4 | REM Add R-3.6.3 and Rtools35 5 | set PATH=C:\Rtools\bin;C:\Program Files\R\R-3.6.3\bin\x64;%PATH% 6 | 7 | REM Set LIB_GSL (64-bit); gcc needs forward-slashes 8 | set LIB_GSL=C:/gsl/x64 9 | 10 | REM Build the source package 11 | mkdir OpenABMCovid19.tmp 12 | R CMD INSTALL --library=OpenABMCovid19.tmp --no-multiarch --build ^ 13 | OpenABMCovid19_*.tar.gz 14 | -------------------------------------------------------------------------------- /R/swig_pointer_classes.R: -------------------------------------------------------------------------------- 1 | # R/swig_pointer_classes.R 2 | # Define SWIG pointer proxy S4 classes that the wrapper expects. 3 | # Add any other _p_* classes you see referenced by the generated wrapper. 4 | 5 | # char * 6 | if (!isClass("_p_char")) { 7 | setClass("_p_char", representation(ref = "character")) 8 | } 9 | 10 | # network * 11 | if (!isClass("_p_network")) { 12 | setClass("_p_network", representation(ref = "externalptr")) 13 | } 14 | -------------------------------------------------------------------------------- /tests/adapter_covid19/data/smallcap_cash.csv: -------------------------------------------------------------------------------- 1 | Sector,smallcap_cash 2 | A_AGRICULTURE,20 3 | B_MINING,20 4 | C_MANUFACTURING,20 5 | D_ELECTRICITY,20 6 | E_WATER,20 7 | F_CONSTRUCTION,20 8 | G_TRADE,20 9 | H_TRANSPORT,20 10 | I_ACCOMODATION,20 11 | J_COMMUNICATION,20 12 | K_FINANCIAL,20 13 | L_REAL_ESTATE,20 14 | M_PROFESSIONAL,20 15 | N_ADMINISTRATIVE,20 16 | O_PUBLIC,20 17 | P_EDUCATION,20 18 | Q_HEALTH,20 19 | R_ARTS,20 20 | S_OTHER,20 21 | T_HOUSEHOLD,20 22 | -------------------------------------------------------------------------------- /tests/adapter_covid19/data/wfh.csv: -------------------------------------------------------------------------------- 1 | Sector,wfh 2 | I_ACCOMODATION,0.3 3 | R_ARTS,0.3 4 | C_MANUFACTURING,0.3 5 | F_CONSTRUCTION,0.3 6 | B_MINING,0.3 7 | G_TRADE,0.3 8 | H_TRANSPORT,0.3 9 | L_REAL_ESTATE,0.3 10 | M_PROFESSIONAL,0.3 11 | J_COMMUNICATION,0.3 12 | P_EDUCATION,0.3 13 | K_FINANCIAL,0.3 14 | N_ADMINISTRATIVE,0.3 15 | D_ELECTRICITY,0.3 16 | S_OTHER,0.3 17 | E_WATER,0.3 18 | Q_HEALTH,0.3 19 | O_PUBLIC,0.3 20 | A_AGRICULTURE,0.3 21 | T_HOUSEHOLD,0.3 22 | -------------------------------------------------------------------------------- /examples/geo_plot.py: -------------------------------------------------------------------------------- 1 | import pandas as pd 2 | import plotly.express as px 3 | 4 | geofile = "~/Downloads/Sustainability_and_Transformation_Partnerships__April_2019__EN_BUC-shp/Sustainability_and_Transformation_Partnerships__April_2019__EN_BUC.shp" 5 | 6 | data = pd.read_csv( "temp.csv", sep = ",") 7 | 8 | t = data[ data["stp"] == "E54000007"] 9 | fig = px.scatter(data_frame = t, x="time", y="total_infected") 10 | fig.write_image("fig1.png") 11 | 12 | print(t) 13 | 14 | -------------------------------------------------------------------------------- /tests/adapter_covid19/data/keyworker.csv: -------------------------------------------------------------------------------- 1 | Sector,keyworker 2 | I_ACCOMODATION,0.1 3 | R_ARTS,0.1 4 | C_MANUFACTURING,0.1 5 | F_CONSTRUCTION,0.1 6 | B_MINING,0.1 7 | G_TRADE,0.1 8 | H_TRANSPORT,0.1 9 | L_REAL_ESTATE,0.1 10 | M_PROFESSIONAL,0.1 11 | J_COMMUNICATION,0.1 12 | P_EDUCATION,0.1 13 | K_FINANCIAL,0.1 14 | N_ADMINISTRATIVE,0.1 15 | D_ELECTRICITY,0.1 16 | S_OTHER,0.1 17 | E_WATER,0.1 18 | Q_HEALTH,0.1 19 | O_PUBLIC,0.1 20 | A_AGRICULTURE,0.1 21 | T_HOUSEHOLD,0.1 22 | -------------------------------------------------------------------------------- /tests/adapter_covid19/data/sme_rate_payer_vulnerability.csv: -------------------------------------------------------------------------------- 1 | Sector,vulnerability 2 | A_AGRICULTURE,10 3 | B_MINING,10 4 | C_MANUFACTURING,10 5 | D_ELECTRICITY,10 6 | E_WATER,10 7 | F_CONSTRUCTION,10 8 | G_TRADE,10 9 | H_TRANSPORT,10 10 | I_ACCOMODATION,10 11 | J_COMMUNICATION,10 12 | K_FINANCIAL,10 13 | L_REAL_ESTATE,10 14 | M_PROFESSIONAL,10 15 | N_ADMINISTRATIVE,10 16 | O_PUBLIC,0 17 | P_EDUCATION,10 18 | Q_HEALTH,10 19 | R_ARTS,10 20 | S_OTHER,10 21 | T_HOUSEHOLD,0 22 | -------------------------------------------------------------------------------- /examples/example_101.py: -------------------------------------------------------------------------------- 1 | """ 2 | Example 101 3 | Get the baseline parameters, model, run for a few time steps and print the output 4 | 5 | Created: 17 April 2020 6 | Author: roberthinch 7 | """ 8 | 9 | import COVID19.model as abm 10 | 11 | # get the model overiding a couple of params 12 | model = abm.Model( params = { "n_total" : 10000, "end_time": 20 } ) 13 | 14 | # run the model 15 | model.run() 16 | 17 | # print the basic output 18 | print( model.results ) 19 | 20 | 21 | -------------------------------------------------------------------------------- /tests/adapter_covid19/data/largecap_count.csv: -------------------------------------------------------------------------------- 1 | Sector,largecap_count 2 | A_AGRICULTURE,100 3 | B_MINING,100 4 | C_MANUFACTURING,100 5 | D_ELECTRICITY,100 6 | E_WATER,100 7 | F_CONSTRUCTION,100 8 | G_TRADE,100 9 | H_TRANSPORT,100 10 | I_ACCOMODATION,100 11 | J_COMMUNICATION,100 12 | K_FINANCIAL,100 13 | L_REAL_ESTATE,100 14 | M_PROFESSIONAL,100 15 | N_ADMINISTRATIVE,100 16 | O_PUBLIC,0 17 | P_EDUCATION,100 18 | Q_HEALTH,100 19 | R_ARTS,100 20 | S_OTHER,100 21 | T_HOUSEHOLD,0 22 | -------------------------------------------------------------------------------- /tests/adapter_covid19/data/vulnerability.csv: -------------------------------------------------------------------------------- 1 | Sector,vulnerability 2 | I_ACCOMODATION,0.8 3 | R_ARTS,0.8 4 | C_MANUFACTURING,0.8 5 | F_CONSTRUCTION,0.8 6 | B_MINING,0.8 7 | G_TRADE,0.8 8 | H_TRANSPORT,0.8 9 | L_REAL_ESTATE,0.8 10 | M_PROFESSIONAL,0.8 11 | J_COMMUNICATION,0.8 12 | P_EDUCATION,0.8 13 | K_FINANCIAL,0.8 14 | N_ADMINISTRATIVE,0.8 15 | D_ELECTRICITY,0.8 16 | S_OTHER,0.8 17 | E_WATER,0.8 18 | Q_HEALTH,0.8 19 | O_PUBLIC,0.8 20 | A_AGRICULTURE,0.8 21 | T_HOUSEHOLD,0.8 22 | -------------------------------------------------------------------------------- /tests/adapter_covid19/data/sme_count.csv: -------------------------------------------------------------------------------- 1 | Sector,sme_count 2 | A_AGRICULTURE,1000 3 | B_MINING,1000 4 | C_MANUFACTURING,1000 5 | D_ELECTRICITY,1000 6 | E_WATER,1000 7 | F_CONSTRUCTION,1000 8 | G_TRADE,1000 9 | H_TRANSPORT,1000 10 | I_ACCOMODATION,1000 11 | J_COMMUNICATION,1000 12 | K_FINANCIAL,1000 13 | L_REAL_ESTATE,1000 14 | M_PROFESSIONAL,1000 15 | N_ADMINISTRATIVE,1000 16 | O_PUBLIC,0 17 | P_EDUCATION,1000 18 | Q_HEALTH,1000 19 | R_ARTS,1000 20 | S_OTHER,1000 21 | T_HOUSEHOLD,0 22 | -------------------------------------------------------------------------------- /OpenABM-Covid19.Rproj: -------------------------------------------------------------------------------- 1 | Version: 1.0 2 | 3 | RestoreWorkspace: Default 4 | SaveWorkspace: Default 5 | AlwaysSaveHistory: Default 6 | 7 | EnableCodeIndexing: Yes 8 | UseSpacesForTab: Yes 9 | NumSpacesForTab: 2 10 | Encoding: UTF-8 11 | 12 | RnwWeave: Sweave 13 | LaTeX: pdfLaTeX 14 | 15 | AutoAppendNewline: Yes 16 | StripTrailingWhitespace: Yes 17 | LineEndingConversion: Posix 18 | 19 | BuildType: Package 20 | PackageUseDevtools: Yes 21 | PackageInstallArgs: --no-multiarch --with-keep.source 22 | -------------------------------------------------------------------------------- /examples/example_101.R: -------------------------------------------------------------------------------- 1 | # 2 | # Example 101 3 | # Get the baseline parameters, model, run for a few time steps and print the output 4 | # 5 | # Created: 8 April 2021 6 | # Author: roberthinch 7 | # 8 | 9 | library( OpenABMCovid19) 10 | 11 | # get the model overiding a couple of params 12 | model = Model.new( params = list( n_total = 10000, end_time = 20 ) ) 13 | 14 | # run the model 15 | Model.run( model ) 16 | 17 | # print the basic output 18 | print( Model.results( model ) ) 19 | 20 | 21 | -------------------------------------------------------------------------------- /tests/adapter_covid19/data/growth_rates.csv: -------------------------------------------------------------------------------- 1 | Sector,growth_rates 2 | I_ACCOMODATION,0.01 3 | N_ADMINISTRATIVE,0.01 4 | A_AGRICULTURE,0.01 5 | R_ARTS,0.01 6 | F_CONSTRUCTION,0.01 7 | P_EDUCATION,0.01 8 | D_ELECTRICITY,0.01 9 | K_FINANCIAL,0.01 10 | Q_HEALTH,0.01 11 | J_COMMUNICATION,0.01 12 | C_MANUFACTURING,0.01 13 | B_MINING,0.01 14 | S_OTHER,0.01 15 | M_PROFESSIONAL,0.01 16 | O_PUBLIC,0.01 17 | L_REAL_ESTATE,0.01 18 | H_TRANSPORT,0.01 19 | E_WATER,0.01 20 | G_TRADE,0.01 21 | T_HOUSEHOLD,0.01 22 | -------------------------------------------------------------------------------- /tests/adapter_covid19/data/largecap_pct_turnover.csv: -------------------------------------------------------------------------------- 1 | Sector,largecap_pct_turnover 2 | A_AGRICULTURE,0.5 3 | B_MINING,0.5 4 | C_MANUFACTURING,0.5 5 | D_ELECTRICITY,0.5 6 | E_WATER,0.5 7 | F_CONSTRUCTION,0.5 8 | G_TRADE,0.5 9 | H_TRANSPORT,0.5 10 | I_ACCOMODATION,0.5 11 | J_COMMUNICATION,0.5 12 | K_FINANCIAL,0.5 13 | L_REAL_ESTATE,0.5 14 | M_PROFESSIONAL,0.5 15 | N_ADMINISTRATIVE,0.5 16 | O_PUBLIC,0.5 17 | P_EDUCATION,0.5 18 | Q_HEALTH,0.5 19 | R_ARTS,0.5 20 | S_OTHER,0.5 21 | T_HOUSEHOLD,0.5 22 | -------------------------------------------------------------------------------- /src/strain_utils.i: -------------------------------------------------------------------------------- 1 | %module strain_utils 2 | 3 | #ifndef SWIGR 4 | #include 5 | %include 6 | %cstring_bounded_output(char* outstr, 1024); 7 | #endif 8 | 9 | %inline %{ 10 | 11 | short strain_idx( strain *pstrain ) { 12 | return pstrain->idx; 13 | } 14 | 15 | float strain_transmission_multiplier( strain *pstrain ) { 16 | return pstrain->transmission_multiplier; 17 | } 18 | 19 | long strain_total_infected( strain *pstrain ) { 20 | return pstrain->total_infected; 21 | } 22 | 23 | %} 24 | -------------------------------------------------------------------------------- /man/Model.one_time_step.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/Model.R 3 | \name{Model.one_time_step} 4 | \alias{Model.one_time_step} 5 | \title{Steps the model forward one time step (wrapper for \code{\link{Model}$one_time_step()})} 6 | \usage{ 7 | Model.one_time_step(model) 8 | } 9 | \arguments{ 10 | \item{model}{The Model object (R6 Class)} 11 | } 12 | \value{ 13 | Null 14 | } 15 | \description{ 16 | Steps the model forward one time step (wrapper for \code{\link{Model}$one_time_step()}) 17 | } 18 | -------------------------------------------------------------------------------- /man/Model.get_param.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/Model.R 3 | \name{Model.get_param} 4 | \alias{Model.get_param} 5 | \title{Gets the value of a parameter (wrapper for \ 6 | code{\link{Parameters}$get_param(param)})} 7 | \usage{ 8 | Model.get_param(model, param) 9 | } 10 | \arguments{ 11 | \item{model}{A Model object} 12 | 13 | \item{param}{The name of the parameter} 14 | } 15 | \description{ 16 | Gets the value of a parameter (wrapper for \ 17 | code{\link{Parameters}$get_param(param)}) 18 | } 19 | -------------------------------------------------------------------------------- /man/Model.results.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/Model.R 3 | \name{Model.results} 4 | \alias{Model.results} 5 | \title{Gets the simulation results for all time-steps run so far (wrapper for 6 | \code{\link{Model}$results()})} 7 | \usage{ 8 | Model.results(model) 9 | } 10 | \arguments{ 11 | \item{model}{The Model object (R6 Class)} 12 | } 13 | \value{ 14 | DataFrame 15 | } 16 | \description{ 17 | Gets the simulation results for all time-steps run so far (wrapper for 18 | \code{\link{Model}$results()}) 19 | } 20 | -------------------------------------------------------------------------------- /man/Model.get_transmissions.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/Model.R 3 | \name{Model.get_transmissions} 4 | \alias{Model.get_transmissions} 5 | \title{Gets all the transmissions until now (wrapper for 6 | \code{\link{Model}$get_transmissions()})} 7 | \usage{ 8 | Model.get_transmissions(model) 9 | } 10 | \arguments{ 11 | \item{model}{The Model object (R6 Class)} 12 | } 13 | \value{ 14 | DataFrame 15 | } 16 | \description{ 17 | Gets all the transmissions until now (wrapper for 18 | \code{\link{Model}$get_transmissions()}) 19 | } 20 | -------------------------------------------------------------------------------- /tests/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM python:3.7-buster 2 | 3 | ENV DEBIAN_FRONTEND noninteractive 4 | 5 | RUN apt-get update -qqy && apt-get install -qqy \ 6 | make \ 7 | clang \ 8 | curl \ 9 | gcc \ 10 | g++ \ 11 | libgsl23 \ 12 | libgsl-dev \ 13 | swig 14 | RUN python3.7 -m pip install numpy pandas scipy pytest 15 | 16 | COPY tests/requirements.txt /requirements.txt 17 | RUN python3 -m pip install -r /requirements.txt 18 | 19 | COPY . /OpenABM-Covid19 20 | 21 | WORKDIR /OpenABM-Covid19 22 | RUN make clean && make install 23 | 24 | WORKDIR /OpenABM-Covid19 25 | ENTRYPOINT ["/usr/local/bin/pytest", "-s"] 26 | -------------------------------------------------------------------------------- /man/Model.run.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/Model.R 3 | \name{Model.run} 4 | \alias{Model.run} 5 | \title{Runs the simulation until the \code{end_time} specified in the parameters 6 | (wrapper for \code{\link{Model}run()})} 7 | \usage{ 8 | Model.run(model, verbose = TRUE) 9 | } 10 | \arguments{ 11 | \item{model}{The Model object (R6 Class)} 12 | 13 | \item{verbose}{Show progress of the calculation (default = TRUE)} 14 | } 15 | \value{ 16 | Null 17 | } 18 | \description{ 19 | Runs the simulation until the \code{end_time} specified in the parameters 20 | (wrapper for \code{\link{Model}run()}) 21 | } 22 | -------------------------------------------------------------------------------- /man/Model.one_time_step_results.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/Model.R 3 | \name{Model.one_time_step_results} 4 | \alias{Model.one_time_step_results} 5 | \title{Gets the simulation results for the current time-step. (wrapper for 6 | \code{\link{Model}$one_time_step_results()})} 7 | \usage{ 8 | Model.one_time_step_results(model) 9 | } 10 | \arguments{ 11 | \item{model}{The Model object (R6 Class)} 12 | } 13 | \value{ 14 | Vector with names of variables 15 | } 16 | \description{ 17 | Gets the simulation results for the current time-step. (wrapper for 18 | \code{\link{Model}$one_time_step_results()}) 19 | } 20 | -------------------------------------------------------------------------------- /man/Model.update_running_params.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/Model.R 3 | \name{Model.update_running_params} 4 | \alias{Model.update_running_params} 5 | \title{Update a parameter during a simulation} 6 | \usage{ 7 | Model.update_running_params(model, param, value) 8 | } 9 | \arguments{ 10 | \item{model}{A Model object} 11 | 12 | \item{param}{name of parameter. See \code{\link{SAFE_UPDATE_PARAMS}} for 13 | allowed parameter names} 14 | 15 | \item{value}{value of parameter} 16 | } 17 | \description{ 18 | A subset of parameters may be updated whilst the model is 19 | evaluating these correspond to events. This function throws an error if 20 | \code{param} isn't safe to update. 21 | } 22 | -------------------------------------------------------------------------------- /man/Parameters.get_param.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/Parameters.R 3 | \name{Parameters.get_param} 4 | \alias{Parameters.get_param} 5 | \title{Gets the value of a parameter (wrapper for 6 | \code{\link{Parameters}$get_param(param)})} 7 | \usage{ 8 | Parameters.get_param(parameters, param) 9 | } 10 | \arguments{ 11 | \item{parameters}{A Parameters object} 12 | 13 | \item{param}{The name of the parameter} 14 | } 15 | \description{ 16 | Gets the value of a parameter (wrapper for 17 | \code{\link{Parameters}$get_param(param)}) 18 | } 19 | \seealso{ 20 | \href{https://github.com/BDI-pathogens/OpenABM-Covid19/blob/master/documentation/parameters/parameter_dictionary.md}{Online Documentation}. 21 | } 22 | -------------------------------------------------------------------------------- /tests/adapter_covid19/data/furloughed.csv: -------------------------------------------------------------------------------- 1 | Sector,furloughed 2 | C_MANUFACTURING,0.3096969696969697 3 | E_WATER,0.26454545454545453 4 | F_CONSTRUCTION,0.4412121212121212 5 | G_TRADE,0.2557575757575758 6 | I_ACCOMODATION,0.5139393939393939 7 | H_TRANSPORT,0.32727272727272727 8 | J_COMMUNICATION,0.10363636363636364 9 | M_PROFESSIONAL,0.2306060606060606 10 | N_ADMINISTRATIVE,0.3978787878787879 11 | P_EDUCATION,0.054242424242424245 12 | Q_HEALTH,0.3015151515151515 13 | R_ARTS,0.35090909090909084 14 | O_PUBLIC,0.0 15 | A_AGRICULTURE,0.29515151515151516 16 | B_MINING,0.29515151515151516 17 | D_ELECTRICITY,0.29515151515151516 18 | K_FINANCIAL,0.29515151515151516 19 | L_REAL_ESTATE,0.29515151515151516 20 | S_OTHER,0.29515151515151516 21 | T_HOUSEHOLD,0.29515151515151516 22 | -------------------------------------------------------------------------------- /man/Parameters.set_param.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/Parameters.R 3 | \name{Parameters.set_param} 4 | \alias{Parameters.set_param} 5 | \title{Sets the value of a parameter (wrapper for 6 | \code{\link{Parameters}$set_param(param,value)})} 7 | \usage{ 8 | Parameters.set_param(parameters, param, value) 9 | } 10 | \arguments{ 11 | \item{parameters}{A Parameters object} 12 | 13 | \item{param}{The name of the parameter} 14 | 15 | \item{value}{The new value} 16 | } 17 | \description{ 18 | Sets the value of a parameter (wrapper for 19 | \code{\link{Parameters}$set_param(param,value)}) 20 | } 21 | \seealso{ 22 | \href{https://github.com/BDI-pathogens/OpenABM-Covid19/blob/master/documentation/parameters/parameter_dictionary.md}{Online Documentation}. 23 | } 24 | -------------------------------------------------------------------------------- /tests/data/hospital_baseline_parameters.csv: -------------------------------------------------------------------------------- 1 | n_hospitals,n_covid_general_wards,n_beds_covid_general_ward,n_doctors_covid_general_ward,n_patient_doctor_required_interactions_covid_general,n_nurses_covid_general_ward,n_patient_nurse_required_interactions_covid_general_ward,n_covid_icu_wards,n_beds_covid_icu_ward,n_doctors_covid_icu_ward,n_patient_doctor_required_interactions_covid_icu_ward,n_nurses_covid_icu_ward,n_patient_nurse_required_interactions_covid_icu_ward,max_hcw_daily_interactions,hospitalised_waiting_mod,critical_waiting_mod,relative_transmission_hospital_work,relative_transmission_doctor_patient_general,relative_transmission_nurse_patient_general,relative_transmission_doctor_patient_icu,relative_transmission_nurse_patient_icu,hcw_mean_work_interactions 2 | 1,1,30,1,1,1,3,1,10,1,3,1,6,20,1.3,2,1,0.9,0.8,0.7,0.8,2 3 | -------------------------------------------------------------------------------- /inst/default_params/hospital_baseline_parameters.csv: -------------------------------------------------------------------------------- 1 | n_hospitals,n_covid_general_wards,n_beds_covid_general_ward,n_doctors_covid_general_ward,n_patient_doctor_required_interactions_covid_general,n_nurses_covid_general_ward,n_patient_nurse_required_interactions_covid_general_ward,n_covid_icu_wards,n_beds_covid_icu_ward,n_doctors_covid_icu_ward,n_patient_doctor_required_interactions_covid_icu_ward,n_nurses_covid_icu_ward,n_patient_nurse_required_interactions_covid_icu_ward,max_hcw_daily_interactions,hospitalised_waiting_mod,critical_waiting_mod,relative_transmission_hospital_work,relative_transmission_doctor_patient_general,relative_transmission_nurse_patient_general,relative_transmission_doctor_patient_icu,relative_transmission_nurse_patient_icu,hcw_mean_work_interactions 2 | 1,1,30,1,1,1,3,1,10,1,3,1,6,20,1.3,2,1,0.9,0.8,0.7,0.8,2 3 | -------------------------------------------------------------------------------- /src/COVID19/default_params/hospital_baseline_parameters.csv: -------------------------------------------------------------------------------- 1 | n_hospitals,n_covid_general_wards,n_beds_covid_general_ward,n_doctors_covid_general_ward,n_patient_doctor_required_interactions_covid_general,n_nurses_covid_general_ward,n_patient_nurse_required_interactions_covid_general_ward,n_covid_icu_wards,n_beds_covid_icu_ward,n_doctors_covid_icu_ward,n_patient_doctor_required_interactions_covid_icu_ward,n_nurses_covid_icu_ward,n_patient_nurse_required_interactions_covid_icu_ward,max_hcw_daily_interactions,hospitalised_waiting_mod,critical_waiting_mod,relative_transmission_hospital_work,relative_transmission_doctor_patient_general,relative_transmission_nurse_patient_general,relative_transmission_doctor_patient_icu,relative_transmission_nurse_patient_icu,hcw_mean_work_interactions 2 | 1,1,30,1,1,1,3,1,10,1,3,1,6,20,1.3,2,1,0.9,0.8,0.7,0.8,2 3 | -------------------------------------------------------------------------------- /src/list.h: -------------------------------------------------------------------------------- 1 | /* 2 | * list.h 3 | * 4 | * Created on: 16 Apr 2020 5 | * Author: vuurenk 6 | */ 7 | 8 | #ifndef LIST_H_ 9 | #define LIST_H_ 10 | 11 | #define WAITING_LIST_EMPTY -1 12 | 13 | typedef struct node node; 14 | 15 | struct node 16 | { 17 | long data; 18 | struct node *next; 19 | }; 20 | 21 | node* initialise_node( long data ); 22 | 23 | typedef struct list list; 24 | 25 | struct list 26 | { 27 | node* head; 28 | int size; 29 | }; 30 | 31 | void initialise_list( list *list ); 32 | long list_element_at( list* list, int index ); 33 | int list_elem_exists( long pdx, list *list ); 34 | void list_push_front( long data, list *list ); 35 | void list_push_back( long data, list *list ); 36 | void list_remove_element( long data, list *list); 37 | long list_pop( list* list ); 38 | void destroy_list( list* list ); 39 | 40 | #endif 41 | -------------------------------------------------------------------------------- /src/nurse.c: -------------------------------------------------------------------------------- 1 | /* 2 | * hospital.c 3 | * 4 | * Created on: 03 Apr 2020 5 | * Author: vuurenk 6 | */ 7 | 8 | #include "nurse.h" 9 | 10 | /***************************************************************************************** 11 | * Name: initialise_nurse 12 | * Description: initialises a nurse at the start of the simulation and assigns them to 13 | * a hospital. Can only be called once per individual. 14 | * Returns: void 15 | ******************************************************************************************/ 16 | void initialise_nurse( 17 | nurse *nurse, 18 | individual *indiv, 19 | int hospital_idx, 20 | int ward_idx, 21 | int ward_type 22 | ) 23 | { 24 | nurse->pdx = indiv->idx; 25 | nurse->hospital_idx = hospital_idx; 26 | nurse->ward_idx = ward_idx; 27 | nurse->ward_type = ward_type; 28 | } 29 | -------------------------------------------------------------------------------- /examples/examples_add_network.R: -------------------------------------------------------------------------------- 1 | library( OpenABMCovid19 ) 2 | 3 | # get the base model 4 | n_total = 10000 5 | m = Model.new( 6 | params = list( 7 | n_total = n_total, 8 | sd_infectiousness_multiplier = 0, # prevent super-spreading 9 | end_time = 20 10 | ) ) 11 | 12 | # create a network where everyone is connected to person 0 13 | ID_1 = rep( 0, n_total-1) 14 | ID_2 = 1:(n_total-1) 15 | df_network = data.frame( ID_1, ID_2) 16 | 17 | # add the network and print out info on all networks 18 | m$add_user_network( df_network, name = "my network") 19 | print( m$get_network_info() ) 20 | 21 | # infect person 0 and run for the simualtion 22 | m$seed_infect_by_idx( 0 ) 23 | Model.run(m, verbose = FALSE ) 24 | 25 | # see how many people infected by person 0 26 | trans = Model.get_transmissions(m) 27 | print( sprintf( "Person 0 infected %d people", sum(trans$ID_source == 0) ) ) 28 | 29 | -------------------------------------------------------------------------------- /src/doctor.c: -------------------------------------------------------------------------------- 1 | /* 2 | * doctor.c 3 | * 4 | * Created on: 03 Apr 2020 5 | * Author: vuurenk 6 | */ 7 | 8 | #include "doctor.h" 9 | 10 | /***************************************************************************************** 11 | * Name: initialise_doctor 12 | * Description: initialises a doctor and assigns them to a hospital at the start of the 13 | * simulation. Can only be called once per individual. 14 | * Returns: void 15 | ******************************************************************************************/ 16 | void initialise_doctor( 17 | doctor *doctor, 18 | individual *indiv, 19 | int hospital_idx, 20 | int ward_idx, 21 | int ward_type 22 | ) 23 | { 24 | doctor->pdx = indiv->idx; 25 | doctor->hospital_idx = hospital_idx; 26 | doctor->ward_idx = ward_idx; 27 | doctor->ward_type = ward_type; 28 | } 29 | -------------------------------------------------------------------------------- /src/COVID19/strain.py: -------------------------------------------------------------------------------- 1 | """ 2 | Class representing a strain 3 | 4 | Created: 2nd June 2021 5 | Author: roberthinch 6 | """ 7 | 8 | import covid19, pandas as pd 9 | 10 | class Strain: 11 | """ 12 | Strain object has information about a specific strain 13 | """ 14 | 15 | def __init__(self, model, strain_id): 16 | 17 | c_strain = covid19.get_strain_by_id( model.c_model, strain_id ) 18 | self._strain_id = strain_id 19 | self.c_strain = c_strain 20 | 21 | def idx(self): 22 | return covid19.strain_idx( self.c_strain ) 23 | 24 | def transmission_multiplier(self): 25 | return covid19.strain_transmission_multiplier( self.c_strain ) 26 | 27 | def show(self): 28 | print( "idx = " + str( self.idx() ) ) 29 | print( "transmission_multiplier = " + str( self.transmission_multiplier() ) ) 30 | -------------------------------------------------------------------------------- /tests/data/hospital_baseline_parameters_transpose.csv: -------------------------------------------------------------------------------- 1 | n_hospitals,1 2 | n_covid_general_wards,1 3 | n_beds_covid_general_ward,30 4 | n_doctors_covid_general_ward,1 5 | n_patient_doctor_required_interactions_covid_general,1 6 | n_nurses_covid_general_ward,1 7 | n_patient_nurse_required_interactions_covid_general_ward,3 8 | n_covid_icu_wards,1 9 | n_beds_covid_icu_ward,10 10 | n_doctors_covid_icu_ward,1 11 | n_patient_doctor_required_interactions_covid_icu_ward,3 12 | n_nurses_covid_icu_ward,1 13 | n_patient_nurse_required_interactions_covid_icu_ward,6 14 | max_hcw_daily_interactions,20 15 | hospitalised_waiting_mod,1.3 16 | critical_waiting_mod,2 17 | relative_transmission_hospital_work,1 18 | relative_transmission_doctor_patient_general,0.9 19 | relative_transmission_nurse_patient_general,0.8 20 | relative_transmission_doctor_patient_icu,0.7 21 | relative_transmission_nurse_patient_icu,0.8 22 | hcw_mean_work_interactions,2 23 | -------------------------------------------------------------------------------- /src/model_utils.i: -------------------------------------------------------------------------------- 1 | %module model_utils 2 | 3 | %inline %{ 4 | int utils_n_current( model *pmodel, int type ) { 5 | return pmodel->event_lists[type].n_current; 6 | } 7 | 8 | int utils_n_total( model *pmodel, int type ) { 9 | return pmodel->event_lists[type].n_total; 10 | } 11 | 12 | int utils_n_total_by_day( model *pmodel, int type, int day ) { 13 | return pmodel->event_lists[type].n_daily[day]; 14 | } 15 | 16 | int utils_n_total_age( model *pmodel, int type, int age ) { 17 | return pmodel->event_lists[type].n_total_by_age[age]; 18 | } 19 | 20 | int utils_n_daily( model *pmodel, int type, int day ) { 21 | return pmodel->event_lists[type].n_daily_current[day]; 22 | } 23 | 24 | int utils_n_daily_age( model *pmodel, int type, int day, int age) { 25 | return pmodel->event_lists[type].n_daily_by_age[day][age]; 26 | } 27 | 28 | 29 | %} 30 | 31 | 32 | %extend model{ 33 | ~model() { 34 | destroy_model($self); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /tests/adapter_covid19/data/populations.csv: -------------------------------------------------------------------------------- 1 | region,A0,A10,A20,A30,A40,A50,A60,A70,A80 2 | C_NE,300379,293480,358402,325910,316820,375955,314961,234814,115299 3 | D_NW,888907,834800,965137,932276,907795,997937,792974,616585,294904 4 | E_YORKSHIRE,665645,634727,749679,690179,677841,736746,594513,459481,225137 5 | F_EM,566717,549640,628470,586608,605639,665876,537309,424012,197647 6 | G_WM,737171,698460,801463,750054,732626,775167,615331,494775,244072 7 | H_E,767507,699058,715345,795861,809135,848448,675531,550682,277231 8 | I_LONDON,1215736,988894,1347602,1632405,1242488,1043675,687654,455600,239296 9 | J_SE,1107087,1060132,1086220,1137318,1212610,1262659,977400,793483,401037 10 | K_SW,621444,611503,676260,654469,684872,782317,671294,558494,274835 11 | L_WALES,353157,350883,411654,371851,375526,433915,373835,297351,140776 12 | M_SCOTLAND,577951,572354,732964,709255,691809,791347,636719,462067,221707 13 | N_NI,250851,234213,238554,249405,243631,254013,192023,139823,65990 14 | -------------------------------------------------------------------------------- /man/Model.new.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/Model.R 3 | \name{Model.new} 4 | \alias{Model.new} 5 | \title{Creates a new OpenABM Model (wrapper for \code{\link{Model}$new()})} 6 | \usage{ 7 | Model.new(params_object = NULL, params = NULL) 8 | } 9 | \arguments{ 10 | \item{params_object}{An object of type \code{\link{Parameters}} or NULL 11 | (for default params). The constructor will lock the parameter values (ie. 12 | \code{params_code} will become read-only).} 13 | 14 | \item{params}{A named list of parameters fo override (default NULL for no 15 | overrides)} 16 | } 17 | \value{ 18 | Model object (R6 Class), NULL on error or if out-of-memory. 19 | } 20 | \description{ 21 | Creates a new OpenABM \code{\link{Model}} instance from a 22 | \code{\link{Parameters}} object and/or a list of parameters overrides. 23 | 24 | Note that model instances are a memory/CPU extensive and this function 25 | can take a few seconds to complete. 26 | } 27 | -------------------------------------------------------------------------------- /src/structure.h: -------------------------------------------------------------------------------- 1 | /* 2 | * structure.h 3 | * 4 | * Created on: 7 Mar 2020 5 | * Author: hinchr 6 | * Description: forward definition of structures which are referenced by other structures 7 | */ 8 | 9 | #ifndef STRUCTURE_H_ 10 | #define STRUCTURE_H_ 11 | 12 | typedef struct interaction interaction; 13 | typedef struct interaction_block interaction_block; 14 | typedef struct infection_event infection_event; 15 | typedef struct event event; 16 | typedef struct event_block event_block; 17 | typedef struct event_list event_list; 18 | typedef struct edge edge; 19 | typedef struct model model; 20 | typedef struct directory directory; 21 | typedef struct trace_token trace_token; 22 | typedef struct trace_token_block trace_token_block; 23 | typedef struct demographic_household_table demographic_household_table; 24 | typedef struct demographic_occupation_network_table demographic_occupation_network_table; 25 | typedef struct strain strain; 26 | typedef struct vaccine vaccine; 27 | 28 | #endif /* STRUCTURE_H_ */ 29 | -------------------------------------------------------------------------------- /tests/adapter_covid19/data/input_output_final.csv: -------------------------------------------------------------------------------- 1 | Sector,C,K,E 2 | A_AGRICULTURE,6653.235262870313,1203.998,2779.4709999999995 3 | B_MINING,233.891,-3362.994,14130.261 4 | C_MANUFACTURING,70168.48795287416,22599.813940028656,142785.09199697446 5 | D_ELECTRICITY,28281.186,-77.98400000000001,217.0 6 | E_WATER,15890.160999167361,555.8679999999999,4066.868 7 | F_CONSTRUCTION,1843.927,144720.055,2246.972 8 | G_TRADE,150379.999,11636.991000000002,56478.28199999999 9 | H_TRANSPORT,27255.038,0.003,24803.564 10 | I_ACCOMODATION,83995.912,0.0,15427.0 11 | J_COMMUNICATION,34803.527,40773.073000000004,33131.111000000004 12 | K_FINANCIAL,67874.454,0.0,77476.936 13 | L_REAL_ESTATE,288966.727,9202.001,1520.003 14 | M_PROFESSIONAL,6998.019,36655.098,55024.041 15 | N_ADMINISTRATIVE,26143.587000000003,0.0,30027.997 16 | O_PUBLIC,135493.999,1069.0,2006.0 17 | P_EDUCATION,104997.992,0.001,8748.018 18 | Q_HEALTH,205743.796,0.001,233.002 19 | R_ARTS,34889.243,-419.0,5813.254 20 | S_OTHER,32422.203999999998,0.005,629.001 21 | T_HOUSEHOLD,4942.0,0.0,19.0 22 | -------------------------------------------------------------------------------- /.Rbuildignore: -------------------------------------------------------------------------------- 1 | # -*- mode: conf -*- 2 | 3 | # R files 4 | ^\.Rhistory$ 5 | ^\.RData$ 6 | ^\.Rproj\.user$ 7 | ^.*\.Rproj$ 8 | ^.*\.Rcheck$ 9 | 10 | # Emacs / Vim files 11 | TAGS$ 12 | tags$ 13 | 14 | # SWIG interface files 15 | .*\.i$ 16 | 17 | # Python files 18 | .*\.py$ 19 | .*\.pyc$ 20 | ^examples$ 21 | ^performance$ 22 | ^python$ 23 | ^src\/adapter_covid19$ 24 | ^src\/covid19_wrap\.c$ 25 | 26 | # Conf files 27 | ^autom4te.cache$ 28 | ^config\.log$ 29 | ^config\.status$ 30 | ^src\/Makevars$ 31 | 32 | # Build files 33 | ^Makefile$ 34 | ^package-for-CRAN\.sh$ 35 | ^OpenABMCovid19.tmp$ 36 | ^OpenABMCovid19_.*\.tar\.gz$ 37 | ^OpenABMCovid19_.*\.tgz$ 38 | ^OpenABMCovid19_.*\.zip$ 39 | 40 | # Test files 41 | ^src_test$ 42 | ^data_test$ 43 | ^tests\/test_R_INSTALL_win\.bat$ 44 | ^tests\/requirements\.txt$ 45 | ^tests\/src_test$ 46 | ^tests\/data_test$ 47 | ^tests\/adapter_covid19$ 48 | ^tests\/Dockerfile$ 49 | 50 | # Misc 51 | ^\.github$ 52 | ^README\.md$ 53 | ^CONTRIBUTING\.md$ 54 | ^LICENSE$ 55 | ^documentation$ 56 | ^src\/Makefile$ 57 | ^src\/main\.c$ 58 | ^R\MetaModelUtils\* 59 | -------------------------------------------------------------------------------- /documentation/parameters/work-place_network_parameters.md: -------------------------------------------------------------------------------- 1 | # Table: Work-place network parameters 2 | | Name | Value | Symbol | Description | Source | 3 | | ---- | ---- | ---- | ---- | ---- | 4 | | `mean_work_interactions_child` | 10 | - | Mean daily interactions at work (school) for children (aged 0-19) | Mossong et al, 2008 | 5 | | `mean_work_interactions_adult` | 7 | - | Mean daily interactions at work for adults (aged 20-69) | Mossong et al, 2008 | 6 | | `mean_work_interactions_elderly` | 3 | - | Mean daily interactions at work (or similar) for the elderly (aged 70+) | Mossong et al, 2008 | 7 | | `daily_fraction_work` | 0.5 | - | Fraction of people in work network that an individual interacts with each day | Mossong et al, 2008 | 8 | | `work_network_rewire` | 0.1 | - | Rewire parameter on the Watts-Strogatz work networks | - | 9 | | `child_network_adults` | 0.2 | - | Ratio of adults to children in work network for children (0-19) | Mossong et al, 2008 | 10 | | `elderly_network_adults` | 0.2 | - | Ratio of adults to elderly in work network for elderly (70+) | Mossong et al, 2008 | -------------------------------------------------------------------------------- /tests/adapter_covid19/test_data_structures.py: -------------------------------------------------------------------------------- 1 | """ 2 | Basic example testing the adaptER-covid19 data structures 3 | """ 4 | 5 | import sys 6 | 7 | import numpy as np 8 | import pytest 9 | 10 | from adapter_covid19.data_structures import Utilisation 11 | 12 | sys.path.append("src/adapter_covid19") 13 | 14 | 15 | utilisation_list = [ 16 | Utilisation( 17 | p_dead=0.0001, 18 | p_ill_wfo=0.01, 19 | p_ill_wfh=0.01, 20 | p_ill_furloughed=0.01, 21 | p_ill_unemployed=0.01, 22 | p_wfh=0.7, 23 | p_furloughed=0.8, 24 | p_not_employed=0.5, 25 | ) 26 | ] 27 | 28 | 29 | class TestClass: 30 | @pytest.mark.parametrize("utilisation", utilisation_list) 31 | def test_utilisation(self, utilisation): 32 | lambdas = utilisation.to_lambdas() 33 | utilisation2 = Utilisation.from_lambdas(lambdas) 34 | assert utilisation == utilisation2 35 | lambdas2 = utilisation2.to_lambdas() 36 | assert not lambdas.keys() ^ lambdas2.keys() 37 | for key in lambdas: 38 | assert np.isclose(lambdas[key], lambdas2[key]) 39 | -------------------------------------------------------------------------------- /documentation/parameters/random_network_parameters.md: -------------------------------------------------------------------------------- 1 | # Table: Random network parameters 2 | | Name | Value | Symbol | Description | Source | 3 | | ---- | ---- | ---- | ---- | ---- | 4 | | `mean_random_interactions_child` | 2 | - | Mean number of daily random interactions for children (0-19) | Mossong et al, 2008 | 5 | | `sd_random_interactions_child` | 2 | - | Standard deviation for daily random interactions for children (0-19) | Mossong et al, 2008 | 6 | | `mean_random_interactions_adult` | 4 | - | Mean number of daily random interactions for adults (20-69) | Mossong et al, 2008 | 7 | | `sd_random_interactions_adult` | 4 | - | Standard deviation for daily random interactions for adults (20-69) | Mossong et al, 2008 | 8 | | `mean_random_interactions_elderly` | 3 | - | Mean number of daily random interactions for the elderly (70+) | Mossong et al, 2008 | 9 | | `sd_random_interactions_elderly` | 3 | - | Standard deviation for daily random interactions for the elderly (70+) | Mossong et al, 2008 | 10 | | `random_interaction_distribution` | 1 | - | Distribution used for random interactions (0=fixed, age dep, 1=negative binomial) | - | -------------------------------------------------------------------------------- /examples/example_utils.py: -------------------------------------------------------------------------------- 1 | """ 2 | Utility functions for examples for OpenABM-Covid19 3 | 4 | Created: 17 April 2020 5 | Author: roberthinch 6 | """ 7 | import os 8 | 9 | from COVID19.model import Model, Parameters, ModelParameterException 10 | import COVID19.simulation as simulation 11 | 12 | def relative_path(filename: str) -> str: 13 | return os.path.join(os.path.dirname(__file__), filename) 14 | 15 | input_parameter_file = relative_path("../tests/data/baseline_parameters.csv") 16 | parameter_line_number = 1 17 | household_demographics_file = relative_path("../tests/data/baseline_household_demographics.csv") 18 | hospital_file = relative_path("../tests/data/hospital_baseline_parameters.csv") 19 | 20 | def get_baseline_parameters( output_dir = "."): 21 | params = Parameters(input_parameter_file, parameter_line_number, output_dir, household_demographics_file, hospital_file) 22 | return params 23 | 24 | def get_simulation( params ): 25 | params.set_param( "end_time", 500 ) 26 | model = simulation.COVID19IBM(model = Model(params)) 27 | sim = simulation.Simulation(env = model, end_time = params.get_param( "end_time" ) ) 28 | return sim 29 | -------------------------------------------------------------------------------- /src/nurse.h: -------------------------------------------------------------------------------- 1 | /* 2 | * nurse.h 3 | * 4 | * Created on: 03 Apr 2020 5 | * Author: vuurenk 6 | */ 7 | 8 | #ifndef nurse_H_ 9 | #define nurse_H_ 10 | 11 | /************************************************************************/ 12 | /******************************* Includes *******************************/ 13 | /************************************************************************/ 14 | #include "individual.h" 15 | /************************************************************************/ 16 | /****************************** Structures *****************************/ 17 | /************************************************************************/ 18 | 19 | typedef struct nurse nurse; 20 | 21 | struct nurse 22 | { 23 | int hospital_idx; 24 | int ward_idx; 25 | int ward_type; 26 | long pdx; 27 | }; 28 | 29 | /************************************************************************/ 30 | /****************************** Functions *****************************/ 31 | /************************************************************************/ 32 | 33 | void initialise_nurse( nurse *nurse, individual *indiv, int, int, int ); 34 | 35 | #endif /* nurse_H_ */ 36 | -------------------------------------------------------------------------------- /src/doctor.h: -------------------------------------------------------------------------------- 1 | /* 2 | * doctor.h 3 | * 4 | * Created on: 03 Apr 2020 5 | * Author: vuurenk 6 | */ 7 | 8 | #ifndef DOCTOR_H_ 9 | #define DOCTOR_H_ 10 | 11 | /************************************************************************/ 12 | /******************************* Includes *******************************/ 13 | /************************************************************************/ 14 | #include "individual.h" 15 | 16 | /************************************************************************/ 17 | /****************************** Structures *****************************/ 18 | /************************************************************************/ 19 | 20 | typedef struct doctor doctor; 21 | 22 | struct doctor 23 | { 24 | int hospital_idx; 25 | int ward_idx; 26 | int ward_type; 27 | long pdx; 28 | }; 29 | 30 | /************************************************************************/ 31 | /****************************** Functions *****************************/ 32 | /************************************************************************/ 33 | 34 | void initialise_doctor( doctor *doctor, individual *indiv, int, int, int ); 35 | 36 | #endif /* DOCTOR_H_ */ 37 | -------------------------------------------------------------------------------- /performance/test_performance.sh: -------------------------------------------------------------------------------- 1 | ########################################################################################## 2 | # File: test_performance.sh 3 | # Description: Run model and profile speed of memory 4 | ########################################################################################## 5 | 6 | #!/bin/bash 7 | PARAM_DIR="../tests/data/baseline_parameters.csv" 8 | EXE="../src/covid19ibm.exe" 9 | PROFILE=1 # 0=no profile; 1=time; 2=memory 10 | 11 | START=`date +%s` 12 | if [ $PROFILE == 1 ] 13 | then 14 | $EXE $PARAM_DIR 1 & PID=$! 15 | instruments -l 60000 -t Time\ Profiler -p $PID 16 | fi 17 | if [ $PROFILE == 2 ] 18 | then 19 | iprofiler -allocations -T 20s $EXE $PARAM_DIR 1 20 | fi 21 | if [ $PROFILE == 0 ] 22 | then 23 | $EXE $PARAM_DIR 1 24 | fi 25 | 26 | 27 | END=`date +%s` 28 | RUNTIME=$((END-START)) 29 | echo "execution time was" $RUNTIME "seconds" 30 | if [ $RUNTIME \> 40 ] 31 | then 32 | printf "\n*********************************\n** FAILURE - TOO SLOW\n*********************************\n\n" 33 | fi 34 | 35 | -------------------------------------------------------------------------------- /tests/test_python_param_setting.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | """ 3 | Set of tests to try setting values in the python parameters object 4 | """ 5 | 6 | import sys 7 | 8 | sys.path.append("src/COVID19") 9 | from model import Parameters, ModelParameterException, ParameterException 10 | import pytest 11 | 12 | 13 | class TestParameters(object): 14 | def test_set_parameters_arrays_init_to_zero(self): 15 | p = Parameters(input_households="notset.csv", read_param_file=False, read_hospital_param_file=False) 16 | assert p.get_param("population_40_49") == 0 17 | 18 | def test_set_parameters_arrays_set_single_value(self): 19 | p = Parameters(input_households="notset.csv", read_param_file=False, read_hospital_param_file=False) 20 | assert ( 21 | p.get_param("population_40_49") == 0 22 | ), "Array memebers not intilialised to zero" 23 | p.set_param("population_40_49", 400) 24 | assert ( 25 | p.get_param("population_40_49") == 400 26 | ), "Did not set pop group to 400" 27 | assert p.get_param("population_50_59") == 0 28 | 29 | def test_set_age_out_of_range(self): 30 | p = Parameters(input_households="notset.csv", read_param_file=False, read_hospital_param_file=False) 31 | with pytest.raises(ParameterException): 32 | p.set_param("population_80_89", 5000) 33 | -------------------------------------------------------------------------------- /src/vaccine_utils.i: -------------------------------------------------------------------------------- 1 | %module vaccine_utils 2 | 3 | #ifndef SWIGR 4 | #include 5 | %include 6 | %cstring_bounded_output(char* outstr, 1024); 7 | #endif 8 | 9 | %inline %{ 10 | 11 | short vaccine_idx( vaccine *pvaccine ) { 12 | return pvaccine->idx; 13 | } 14 | 15 | void vaccine_full_efficacy( vaccine *pvaccine, float *efficacy ) { 16 | 17 | short n_strains = pvaccine->n_strains; 18 | 19 | for( int idx = 0; idx < n_strains; idx++ ) 20 | efficacy[ idx ] = pvaccine->full_efficacy[ idx ]; 21 | } 22 | 23 | void vaccine_symptoms_efficacy( vaccine *pvaccine, float *efficacy ) { 24 | 25 | short n_strains = pvaccine->n_strains; 26 | 27 | for( int idx = 0; idx < n_strains; idx++ ) 28 | efficacy[ idx ] = pvaccine->symptoms_efficacy[ idx ]; 29 | } 30 | 31 | void vaccine_severe_efficacy( vaccine *pvaccine, float *efficacy ) { 32 | 33 | short n_strains = pvaccine->n_strains; 34 | 35 | for( int idx = 0; idx < n_strains; idx++ ) 36 | efficacy[ idx ] = pvaccine->severe_efficacy[ idx ]; 37 | } 38 | 39 | short vaccine_time_to_protect( vaccine *pvaccine ) { 40 | return pvaccine->time_to_protect; 41 | } 42 | 43 | short vaccine_vaccine_protection_period( vaccine *pvaccine ) { 44 | return pvaccine->vaccine_protection_period; 45 | } 46 | 47 | char *vaccine_name( vaccine *pvaccine ) { 48 | return pvaccine->name; 49 | } 50 | 51 | short vaccine_n_strains( vaccine *pvaccine ) { 52 | return pvaccine->n_strains; 53 | } 54 | 55 | %} -------------------------------------------------------------------------------- /tests/adapter_covid19/test_personal_bankruptcy_models.py: -------------------------------------------------------------------------------- 1 | """ 2 | Basic example testing the adaptER-covid19 personal bankruptcy models 3 | """ 4 | 5 | import sys 6 | 7 | from adapter_covid19.datasources import Reader 8 | from adapter_covid19.enums import Region 9 | from adapter_covid19.personal_insolvency import PersonalBankruptcyModel 10 | from tests.adapter_covid19.utilities import ( 11 | DATA_PATH, 12 | ALL_UTILISATIONS, 13 | UTILISATION_NO_COVID_NO_LOCKDOWN, 14 | state_from_utilisation, 15 | advance_state, 16 | ) 17 | 18 | sys.path.append("src/adapter_covid19") 19 | 20 | 21 | def pytest_generate_tests(metafunc): 22 | if "personal_bankruptcy_model_cls" in metafunc.fixturenames: 23 | metafunc.parametrize("personal_bankruptcy_model_cls", [PersonalBankruptcyModel]) 24 | if "utilisation" in metafunc.fixturenames: 25 | metafunc.parametrize("utilisation", ALL_UTILISATIONS) 26 | 27 | 28 | class TestClass: 29 | def test_interface(self, personal_bankruptcy_model_cls, utilisation): 30 | reader = Reader(DATA_PATH) 31 | state = state_from_utilisation(UTILISATION_NO_COVID_NO_LOCKDOWN) 32 | new_state = advance_state(state, utilisation) 33 | pb_model = personal_bankruptcy_model_cls() 34 | pb_model.load(reader) 35 | pb_model.simulate(state) 36 | pb_model.simulate(new_state) 37 | for region in Region: 38 | assert 0 <= new_state.personal_state.personal_bankruptcy[region] <= 1 39 | -------------------------------------------------------------------------------- /documentation/parameters/demographic_parameters.md: -------------------------------------------------------------------------------- 1 | # Table: Demographic parameters 2 | | Name | Value | Symbol | Description | Source | 3 | | ---- | ---- | ---- | ---- | ---- | 4 | | `n_total` | 1000000 | - | Total population simulated | - | 5 | | `household_size_1` | 7452 | - | Number of UK households with 1 person (thousands) | ONS UK | 6 | | `household_size_2` | 9936 | - | Number of UK households with 2 people (thousands) | ONS UK | 7 | | `household_size_3` | 4416 | - | Number of UK households with 3 people (thousands) | ONS UK | 8 | | `household_size_4` | 4140 | - | Number of UK households with 4 people (thousands) | ONS UK | 9 | | `household_size_5` | 1104 | - | Number of UK households with 5 people (thousands) | ONS UK | 10 | | `household_size_6` | 552 | - | Number of UK households with 6 people (thousands) | ONS UK | 11 | | `population_0_9` | 8054000 | - | UK population aged 0-9 | ONS UK | 12 | | `population_10_19` | 7528000 | - | UK population aged 10-19 | ONS UK | 13 | | `population_20_29` | 8712000 | - | UK population aged 20-29 | ONS UK | 14 | | `population_30_39` | 8835000 | - | UK population aged 30-39 | ONS UK | 15 | | `population_40_49` | 8500000 | - | UK population aged 40-49 | ONS UK | 16 | | `population_50_59` | 8968000 | - | UK population aged 50-59 | ONS UK | 17 | | `population_60_69` | 7069000 | - | UK population aged 60-69 | ONS UK | 18 | | `population_70_79` | 5488000 | - | UK population aged 70-79 | ONS UK | 19 | | `population_80` | 3281000 | - | UK population aged 80+ | ONS UK | -------------------------------------------------------------------------------- /documentation/parameters/manual_tracing_parameters.md: -------------------------------------------------------------------------------- 1 | # Table: Manual tracing parameters 2 | | Name | Value | Symbol | Description | Source | 3 | | ---- | ---- | ---- | ---- | ---- | 4 | | `manual_trace_on` | 0 | - | Turn on manual tracing (0=no, 1=yes) | - | 5 | | `manual_trace_time_on` | 10000 | - | Time (days) after which manual tracing is turned on | - | 6 | | `manual_trace_on_hospitalization` | 1 | - | Trace when hospitalized if tested positive (no effect if manual_trace_on_positive is on) | - | 7 | | `manual_trace_on_positive` | 0 | - | Trace when hospitalized if tested positive (no effect if manual_trace_on_positive is on) | - | 8 | | `manual_trace_delay` | 1 | - | Delay (days) between triggering manual tracing due to testing/hospitalization and tracing occurring | - | 9 | | `manual_trace_exclude_app_users` | 0 | - | Whether or not to exclude app users when performing manual tracing (exclude=1, include=0) | - | 10 | | `manual_trace_n_workers` | 300 | - | Number of Contact Tracing Workers | NACCHO Position Statement, 2020 | 11 | | `manual_trace_interviews_per_worker_day` | 6 | - | Number of interviews performed per worker per day | https://www.gwhwi.org/estimator-613404.html | 12 | | `manual_trace_notifications_per_worker_day` | 12 | - | Number of trace notifications performed per worker per day | https://www.gwhwi.org/estimator-613404.html | 13 | | `manual_traceable_fraction_household` | 1 | - | The fraction of household contacts that can be successfully traced | - | 14 | | `manual_traceable_fraction_occupation` | 0.8 | - | The fraction of occupation contacts that can be successfully traced | - | 15 | | `manual_traceable_fraction_random` | 0.05 | - | The fraction of random contacts that can be successfully traced | - | -------------------------------------------------------------------------------- /examples/example_vaccination.R: -------------------------------------------------------------------------------- 1 | # 2 | # Example Vaccination 3 | # 4 | # This examples demonstrates the effect of a vaccination programme started 5 | # during a lockdown period 6 | # 7 | # The population is populated at the rate of 2% of adults the population a day 8 | ## 9 | # Created: 8 April 2021 10 | # Author: roberthinch 11 | # 12 | 13 | library( OpenABMCovid19) 14 | 15 | # get the model overiding a couple of params 16 | n_total = 10000 17 | model = Model.new( params = list( n_total = n_total ) ) 18 | 19 | # Run the model until there are 2% infections 20 | model$one_time_step() 21 | while( model$one_time_step_results()[["total_infected"]] < n_total * 0.02 ) 22 | model$one_time_step() 23 | 24 | # Turn on lockdown and run the model for another 30 days and vaccinated 25 | Model.update_running_params( model, "self_quarantine_fraction", 0.75 ) 26 | model$update_running_params( "lockdown_on", 1 ) 27 | 28 | # define the vaccination programme 29 | vaccine = model$add_vaccine( full_efficacy = 0.7, symptoms_efficacy = 0.8) 30 | schedule = VaccineSchedule$new( 31 | frac_0_9 = 0, 32 | frac_10_19 = 0, 33 | frac_20_29 = 0.02, 34 | frac_30_39 = 0.02, 35 | frac_40_49 = 0.02, 36 | frac_50_59 = 0.02, 37 | frac_60_69 = 0.02, 38 | frac_70_79 = 0.02, 39 | frac_80 = 0.02, 40 | vaccine = vaccine 41 | ) 42 | 43 | for( t in 1:30 ) 44 | { 45 | model$one_time_step( ) 46 | model$vaccinate_schedule( schedule ) 47 | } 48 | 49 | # Turn off lockdown and run the model for another 50 days and vaccinated 50 | model$update_running_params( "lockdown_on", 0 ) 51 | for( t in 1:50 ) 52 | { 53 | model$one_time_step( ) 54 | model$vaccinate_schedule( schedule ) 55 | } 56 | 57 | results = model$results() 58 | plot( results$time, results$total_infected) 59 | -------------------------------------------------------------------------------- /tests/testthat/test-SeedingSchedule.R: -------------------------------------------------------------------------------- 1 | library(R6) 2 | library( OpenABMCovid19) 3 | library( testthat ) 4 | 5 | test_that("SeedingSchedule single strain", { 6 | 7 | # set up a model with no seed_infections and no transmitted infections 8 | base_params <- list( n_total = 10000, infectious_rate = 0, n_seed_infection = 0) 9 | abm <- Model.new( params = base_params) 10 | 11 | # add a seeding schedule and run model 12 | schedule <- matrix( c( 0, 3, 2,5,1,0,0,2 ), ncol = 1 ) 13 | abm$set_seeding_schedule( schedule ) 14 | abm$run( nrow( schedule ), verbose = FALSE ) 15 | 16 | # check that the number of infections is equal to the number in the scheulde 17 | total <- abm$total_infected[ -1 ] # note results have t=0 18 | 19 | expect_equal( total, cumsum( schedule[, 1] ), label = "incorrect number of seeded infections" ) 20 | }) 21 | 22 | 23 | test_that("SeedingSchedule multiple strain", { 24 | 25 | # set up a model with no seed_infections and no transmitted infections 26 | base_params <- list( n_total = 10000, infectious_rate = 0, n_seed_infection = 0, max_n_strains = 3 ) 27 | abm <- Model.new( params = base_params) 28 | 29 | # add the extra strains 30 | s1 <- abm$add_new_strain() 31 | s2 <- abm$add_new_strain() 32 | 33 | # add a seeding schedule and run model 34 | schedule <- matrix( c( 0, 3, 2,1, 1,2,3,0,2,1,2,1), ncol = 3 ) 35 | abm$set_seeding_schedule( schedule ) 36 | abm$run( nrow( schedule ), verbose = FALSE ) 37 | 38 | # check that the number of infections is equal to the number in the scheulde 39 | total <- abm$total_infected[ -1, ] # note results have t=0 40 | 41 | for( idx in 1:3) 42 | expect_equal( total[ , idx], cumsum( schedule[, idx] ), label = "incorrect number of seeded infections" ) 43 | }) 44 | -------------------------------------------------------------------------------- /src/setup.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | """ 4 | setup.py file for SWIG example 5 | """ 6 | 7 | from distutils.core import setup, Extension 8 | from subprocess import check_output 9 | 10 | def gsl_config(flag): 11 | out = check_output(['gsl-config'] + [flag]).decode('utf-8') 12 | out = out.replace("\n",'') # cut trailing \n 13 | return out.split(' ') 14 | 15 | CFLAGS = gsl_config('--cflags') 16 | LDFLAGS = gsl_config('--libs') 17 | 18 | covid19_module = Extension( 19 | "_covid19", 20 | sources=[ 21 | "covid19_wrap.c", 22 | "constant.c", 23 | "demographics.c", 24 | "disease.c", 25 | "doctor.c", 26 | "hospital.c", 27 | "individual.c", 28 | "input.c", 29 | "interventions.c", 30 | "list.c", 31 | "model.c", 32 | "network.c", 33 | "nurse.c", 34 | "params.c", 35 | "strain.c", 36 | "utilities.c", 37 | "ward.c" 38 | ], 39 | extra_compile_args=["-g", "-Wall", "-fmessage-length=0", "-O0"] + CFLAGS, 40 | extra_link_args=["-lm", "-O3"] + LDFLAGS, 41 | ) 42 | 43 | setup( 44 | name="covid19", 45 | version="0.2", 46 | author="SWIG Docs", 47 | description="""Individual-based model for modelling of a COVID-19 outbreak""", 48 | ext_modules=[covid19_module], 49 | packages=["COVID19", "adapter_covid19",], 50 | py_modules=["covid19",], 51 | package_dir = {"COVID19" : "COVID19"}, 52 | package_data = {"COVID19" : ["default_params/*.csv"] }, 53 | include_package_data=True, 54 | install_requires=[ 55 | "click", 56 | "matplotlib==3.2.2", 57 | "numpy", 58 | "pandas", 59 | "scipy", 60 | "tqdm", 61 | "dataclasses", 62 | ], 63 | ) 64 | -------------------------------------------------------------------------------- /tests/adapter_covid19/data/input_output.csv: -------------------------------------------------------------------------------- 1 | Sector,employee_compensation,taxes_minus_subsidies,capital_consumption,net_operating_surplus 2 | A_AGRICULTURE,4044.172074101395,-2091.7619587865433,3138.381976188842,5960.333898017153 3 | B_MINING,4160.344898955722,149.2319583758568,7668.199509147842,-1902.73055948931 4 | C_MANUFACTURING,105956.01321026249,2194.941669301164,26351.48436280115,37381.331903012295 5 | D_ELECTRICITY,7138.563257290103,1681.923713285241,6722.048701483962,10389.59245616917 6 | E_WATER,7123.618435195042,863.5633629945642,5586.206520917678,7787.47622274822 7 | F_CONSTRUCTION,49176.49939369927,1331.924367408831,5591.6858031807,52801.834132135 8 | G_TRADE,123534.30171955406,9335.906195443597,19872.889194142554,38596.73114880982 9 | H_TRANSPORT,51591.70316925505,1704.3078548984545,17204.267849684325,1930.6790494399006 10 | I_ACCOMODATION,43485.65493712944,3327.286628262991,7147.075800584376,9616.204663379473 11 | J_COMMUNICATION,65781.8525340792,598.3762040762167,19418.0370543674,25242.244501750487 12 | K_FINANCIAL,62870.64312871361,2965.9484782440186,10346.40851367124,41712.26290919411 13 | L_REAL_ESTATE,20323.45482068453,87.10434027585207,77858.64227067688,162894.3379673381 14 | M_PROFESSIONAL,89515.21165852857,1520.0437254288956,15639.731554354812,38813.80718959182 15 | N_ADMINISTRATIVE,58859.870299501374,1051.028706678282,9681.274617279705,24418.79466724897 16 | O_PUBLIC,60803.8668126968,0.0,24877.85232702926,-40.45500120605243 17 | P_EDUCATION,78821.68115532615,370.8532459634233,14195.79945795745,3163.639358643934 18 | Q_HEALTH,101670.63873499073,303.01563570273737,9254.103569344825,19784.06682740416 19 | R_ARTS,14119.741025509513,673.1577517548534,3320.1559626989338,7930.229935217381 20 | S_OTHER,13785.168734526906,377.14812069156096,1591.7549544880492,12019.618730595343 21 | T_HOUSEHOLD,4961.0,0.0,0.0,0.0 22 | -------------------------------------------------------------------------------- /src/network_utils.i: -------------------------------------------------------------------------------- 1 | %module network_utils 2 | 3 | #ifndef SWIGR 4 | #include 5 | %include 6 | %cstring_bounded_output(char* outstr, 1024); 7 | #endif 8 | 9 | %inline %{ 10 | int network_n_edges( network *pnetwork ) { 11 | return pnetwork->n_edges; 12 | } 13 | 14 | int network_n_vertices( network *pnetwork ) { 15 | return pnetwork->n_vertices; 16 | } 17 | 18 | char *network_name( network *pnetwork ) { 19 | return pnetwork->name; 20 | } 21 | 22 | int network_skip_hospitalised( network *pnetwork ) { 23 | return pnetwork->skip_hospitalised; 24 | } 25 | 26 | int network_skip_quarantined( network *pnetwork ) { 27 | return pnetwork->skip_quarantined; 28 | } 29 | 30 | int network_type( network *pnetwork ) { 31 | return pnetwork->type; 32 | } 33 | 34 | double network_daily_fraction( network *pnetwork ) { 35 | return pnetwork->daily_fraction; 36 | } 37 | 38 | float network_transmission_multiplier( network *pnetwork ) { 39 | return pnetwork->transmission_multiplier; 40 | } 41 | 42 | float network_transmission_multiplier_type( network *pnetwork ) { 43 | return pnetwork->transmission_multiplier_type; 44 | } 45 | 46 | float network_transmission_multiplier_combined( network *pnetwork ) { 47 | return pnetwork->transmission_multiplier_combined; 48 | } 49 | 50 | void set_network_transmission_multiplier( network *pnetwork, float val ) { 51 | update_transmission_multiplier( pnetwork, val ); 52 | pnetwork->transmission_multiplier = val; 53 | } 54 | 55 | int get_network( network *pnetwork, long *id1_array, long *id2_array) { 56 | 57 | long idx; 58 | 59 | // Loop through all edges in the network 60 | for(idx = 0; idx < pnetwork->n_edges; idx++) 61 | { 62 | id1_array[idx] = pnetwork->edges[idx].id1; 63 | id2_array[idx] = pnetwork->edges[idx].id2; 64 | } 65 | 66 | return TRUE; 67 | } 68 | 69 | %} -------------------------------------------------------------------------------- /src/adapter_covid19/example.py: -------------------------------------------------------------------------------- 1 | import logging 2 | import multiprocessing 3 | import os 4 | import pickle 5 | import sys 6 | 7 | try: 8 | from tqdm import tqdm 9 | except ModuleNotFoundError: 10 | 11 | def tqdm(x): 12 | return x 13 | 14 | 15 | from adapter_covid19.scenarios import * 16 | from adapter_covid19.simulator import Simulator 17 | 18 | logger = logging.getLogger(__file__) 19 | 20 | if __name__ == "__main__": 21 | 22 | logging.basicConfig(level=logging.INFO) 23 | 24 | if len(sys.argv) < 2 or not os.path.exists(sys.argv[1]): 25 | valid = "|".join(SCENARIOS) 26 | print(f"Example usage: python -m {sys.argv[0]} -m [{valid}, ...]") 27 | data_path = sys.argv[1] 28 | if len(sys.argv) > 2: 29 | scenario_names = sys.argv[2:] 30 | else: 31 | scenario_names = SCENARIOS.keys() 32 | 33 | unknown_scenarios = [n for n in scenario_names if n not in SCENARIOS.keys()] 34 | if len(unknown_scenarios) > 0: 35 | logger.error(f"Unknown scenarios! {unknown_scenarios}") 36 | exit(1) 37 | 38 | def _run_scenario(scenario_name): 39 | s = Simulator(data_path) 40 | logger.info(f"Running scenario {scenario_name}") 41 | scenario = SCENARIOS[scenario_name] 42 | result = s.simulate( 43 | scenario=scenario, show_plots=False, scenario_name=scenario_name 44 | ) 45 | file_name = f"scenario_{scenario_name}.pkl" 46 | logger.info(f"Writing file {file_name}") 47 | with open(file_name, "wb") as f: 48 | pickle.dump((scenario_name, scenario, result), f) 49 | logger.info(f"Finished writing") 50 | 51 | if len(scenario_names) == 1: 52 | # Makes debugging a little easier 53 | _run_scenario(scenario_names[0]) 54 | else: 55 | with multiprocessing.Pool() as pool: 56 | pool.map(_run_scenario, scenario_names) 57 | -------------------------------------------------------------------------------- /src/demographics.h: -------------------------------------------------------------------------------- 1 | /* 2 | * demographics.h 3 | * 4 | * Created on: 23 Mar 2020 5 | * Author: hinchr 6 | */ 7 | 8 | #ifndef DEMOGRAPHICS_H_ 9 | #define DEMOGRAPHICS_H_ 10 | 11 | /************************************************************************/ 12 | /******************************* Includes *******************************/ 13 | /************************************************************************/ 14 | 15 | #include "model.h" 16 | #include "network.h" 17 | 18 | /************************************************************************/ 19 | /****************************** Structures *****************************/ 20 | /************************************************************************/ 21 | 22 | struct demographic_household_table{ 23 | long n_households; 24 | long n_total; 25 | long *idx; 26 | int *age_group; 27 | long *house_no; 28 | }; 29 | 30 | struct demographic_occupation_network_table { 31 | long n_networks; 32 | int *age_type; 33 | long *network_no; // network assigned to each person 34 | double *mean_interactions; 35 | double *lockdown_occupation_multipliers; 36 | char **network_names; 37 | }; 38 | 39 | /************************************************************************/ 40 | /****************************** Functions *****************************/ 41 | /************************************************************************/ 42 | 43 | void set_up_household_distribution( model* ); 44 | void set_up_allocate_work_places( model* ); 45 | void set_up_allocate_default_work_places( model* ); 46 | void set_up_allocate_custom_work_places( model* ); 47 | void build_household_network_from_directroy( network*, directory* ); 48 | void add_reference_household( double *, long , int **); 49 | void generate_household_distribution( model *pmodel ); 50 | void assign_household_distribution( model*, demographic_household_table* ); 51 | 52 | #endif /* DEMOGRAPHICS_H_ */ 53 | -------------------------------------------------------------------------------- /src/adapter_covid19/lockdown.py: -------------------------------------------------------------------------------- 1 | import functools 2 | import itertools 3 | 4 | from adapter_covid19.enums import Region, Sector, Age 5 | 6 | from adapter_covid19.datasources import ( 7 | Reader, 8 | SectorDataSource, 9 | RegionSectorAgeDataSource, 10 | ) 11 | 12 | 13 | @functools.lru_cache() 14 | def _base_lockdown_state(data_path: str) -> float: 15 | """ 16 | Get proportion of workforce at work during lockdown 17 | 18 | :param data_path: path to economics data 19 | :return: 20 | """ 21 | reader = Reader(data_path) 22 | keyworker = SectorDataSource("keyworker").load(reader) 23 | workers = RegionSectorAgeDataSource("workers").load(reader) 24 | return sum( 25 | keyworker[s] * workers[r, s, a] 26 | for r, s, a in itertools.product(Region, Sector, Age) 27 | ) / sum(workers.values()) 28 | 29 | 30 | def get_lockdown_factor( 31 | lockdown: bool, slow_unlock: bool, lockdown_exit_time: int, time: int, 32 | ) -> float: 33 | """ 34 | Get how locked-down the country is 35 | 0 -> no lockdown 36 | 1 -> fully locked down 37 | 38 | :param lockdown: 39 | :param lockdown_exit_time: 40 | :param time: 41 | :return: 42 | """ 43 | if lockdown: 44 | return 1 45 | if not slow_unlock or not lockdown_exit_time: 46 | # i.e. we've never been in a lockdown 47 | return 0 48 | assert time >= lockdown_exit_time, (time, lockdown_exit_time) 49 | # Send 10% of the remaining people back every 10 days 50 | n = 1 + (time - lockdown_exit_time) // 10 51 | return 0.9 ** n 52 | 53 | 54 | def get_working_factor(data_path: str, lockdown_factor: float) -> float: 55 | """ 56 | Get proportion of people working 57 | 58 | :param data_path: 59 | :param lockdown_factor: 60 | :return: 61 | """ 62 | base_lockdown = _base_lockdown_state(data_path) 63 | return base_lockdown + (1 - base_lockdown) * (1 - lockdown_factor) 64 | -------------------------------------------------------------------------------- /documentation/parameters/passive_intervention_parameters.md: -------------------------------------------------------------------------------- 1 | # Table: Passive intervention parameters 2 | | Name | Value | Symbol | Description | Source | 3 | | ---- | ---- | ---- | ---- | ---- | 4 | | `daily_non_cov_symptoms_rate` | 0.002 | - | Daily probability of reporting similar symptoms which are not covid-19, including seasonal flu | UK flu survey | 5 | | `quarantine_length_self` | 7 | - | Maximum number of days quarantine for individuals self-reporting symptoms | - | 6 | | `quarantine_dropout_self` | 0.02 | - | Daily probability of drop out for an individual quarantining after self-reporting symptoms | - | 7 | | `quarantined_daily_interactions` | 0 | - | Daily random interactions of a quarantined individual | - | 8 | | `hospitalised_daily_interactions` | 0 | - | Daily random interactions of a hospitalised individual | - | 9 | | `self_quarantine_fraction` | 0 | - | Proportion of people who self-quarantine upon symptoms | - | 10 | | `lockdown_occupation_multiplier_primary_network` | 0.33 | - | Relative change in number of occupation network contacts on lockdown for primary age | - | 11 | | `lockdown_occupation_multiplier_secondary_network` | 0.33 | - | Relative change in number of occupation network contacts on lockdown for secondary age | - | 12 | | `lockdown_occupation_multiplier_working_network` | 0.33 | - | Relative change in number of occupation network contacts on lockdown for working age | Based on an estimate of the number of key workers | 13 | | `lockdown_occupation_multiplier_retired_network` | 0.33 | - | Relative change in number of occupation network contacts on lockdown for retired age | - | 14 | | `lockdown_occupation_multiplier_elderly_network` | 0.33 | - | Relative change in number of occupation network contacts on lockdown for elderly | - | 15 | | `lockdown_random_network_multiplier` | 0.33 | - | Relative change in random network contacts on lockdown | - | 16 | | `lockdown_house_interaction_multiplier` | 1.5 | - | Relative change in household network contacts on lockdown | - | -------------------------------------------------------------------------------- /src/ward.h: -------------------------------------------------------------------------------- 1 | /* 2 | * ward.h 3 | * 4 | * Created on: 03 Apr 2020 5 | * Author: vuurenk 6 | */ 7 | 8 | #ifndef WARD_H_ 9 | #define WARD_H_ 10 | 11 | /************************************************************************/ 12 | /******************************* Includes *******************************/ 13 | /************************************************************************/ 14 | #include "network.h" 15 | #include "doctor.h" 16 | #include "nurse.h" 17 | #include "list.h" 18 | #include "individual.h" 19 | 20 | /************************************************************************/ 21 | /****************************** Structures *****************************/ 22 | /************************************************************************/ 23 | 24 | typedef struct ward ward; 25 | 26 | struct ward 27 | { 28 | int ward_idx; 29 | int type; 30 | 31 | int n_beds; 32 | int n_max_hcw[N_WORKER_TYPES]; 33 | int n_worker[N_WORKER_TYPES]; 34 | 35 | doctor *doctors; 36 | nurse *nurses; 37 | list *patients; 38 | 39 | network *doctor_patient_network; 40 | network *nurse_patient_network; 41 | }; 42 | 43 | /************************************************************************/ 44 | /****************************** Functions *****************************/ 45 | /************************************************************************/ 46 | 47 | void initialise_ward(ward*, int, int , int n_beds, int n_max_doctors, int n_max_nurses); 48 | void set_up_ward_networks(model *pmodel, ward* ward , int max_hcw_daily_interactions); 49 | void build_ward_networks(model *pmodel, ward* pward ); 50 | void build_hcw_patient_network(ward* ward, network *network, long *hc_workers, int n_hcw_working, int n_patient_required_interactions, int max_hcw_daily_interactions ); 51 | int add_patient_to_ward( ward *ward, individual *indiv ); 52 | int ward_available_beds( ward* ward); 53 | void remove_patient_from_ward( ward* ward, individual *indiv ); 54 | void destroy_ward( ward* ); 55 | 56 | #endif /* WARD_H_ */ 57 | -------------------------------------------------------------------------------- /examples/multi_region.py: -------------------------------------------------------------------------------- 1 | import pandas as pd 2 | import sys 3 | import time 4 | import os 5 | 6 | sys.path.append("../src/COVID19") 7 | from multiRegion import MultiRegionModel 8 | 9 | 10 | if __name__ == '__main__': 11 | # some OS limit the number of process which can be spawned by default 12 | # for OSX it can ge increased with the following line, probably needs to be 13 | # changed for other operating systems 14 | os.system( "ulimit -Sn 10000") 15 | 16 | max_steps = 100 17 | n_pops = 10 18 | n_total = 100000 19 | 20 | params = pd.DataFrame( { 21 | "n_total" : [ n_total ] * n_pops, 22 | "idx" : range( n_pops ), 23 | "quarantine_days" : 1, 24 | "days_of_interactions" : 1, 25 | "rebuild_networks" : 0 26 | } ) 27 | model = MultiRegionModel( params ) 28 | 29 | t_start = time.time() 30 | initial_growth = True 31 | last_infected = 0 32 | for day in range( max_steps ) : 33 | model.one_step_wait() 34 | 35 | total_infected = sum(model.result_array( "total_infected")) 36 | print( "Time " + str( day ) + 37 | "; daily_infected = " + str( total_infected - last_infected ) + 38 | "; total_infected = " + str( total_infected ) ) 39 | 40 | if initial_growth == True : 41 | 42 | if total_infected > n_pops * n_total * 0.01 : 43 | initial_growth = False 44 | for idx in range(n_pops): 45 | model.update_running_params("relative_transmission_occupation", 0.3, index_val = idx) 46 | model.update_running_params("relative_transmission_random", 0.3 , index_val = idx) 47 | 48 | last_infected = total_infected 49 | 50 | t_end = time.time() 51 | 52 | print( "time = " + str( t_end - t_start) + 53 | "s; n_steps = " + str(max_steps )+ 54 | "; time per step = " + str( (t_end - t_start)/max_steps) ) 55 | 56 | del( model ) 57 | 58 | 59 | -------------------------------------------------------------------------------- /src/adapter_covid19/README.md: -------------------------------------------------------------------------------- 1 | # AdaptER-Covid19 2 | 3 | ## Description 4 | 5 | AdaptER-Covid19 is an economic model intended to simulate the economic effects of Covid19. For more information, see 6 | the [model documentation](../../documentation/economic_model.pdf)/ 7 | 8 | ## Compilation 9 | 10 | See [the root README](../../README.md). 11 | 12 | ## Usage 13 | 14 | AdaptER-Covid19 is intended to be used as part of the wider OpenABM-Covid19 model. Please see [the root README](../../README.md) for usage instructions. 15 | 16 | ## Examples 17 | 18 | [`adapter_covid19/example.py`](example.py) contains an example over how to run the economic model on its own. 19 | This can then be visualised in [`/examples/economics_visualisation.ipynb`](../../examples/economics_visualisation.ipynb). 20 | 21 | ```bash 22 | python examples/example_run_spread_model_for_economics.py basic 23 | python -m adapter_covid19.example tests/adapter_covid19/data basic 24 | ``` 25 | 26 | Alternatively: 27 | ```python 28 | from adapter_covid19.data_structures import Scenario, ModelParams 29 | from adapter_covid19.scenarios import BASIC_SCENARIO 30 | from adapter_covid19.simulator import Simulator 31 | 32 | from examples.example_run_spread_model_for_economics import run 33 | 34 | # Define data path 35 | data_path = "./tests/adapter_covid19/data" 36 | 37 | # Initialize simulator 38 | simulator = Simulator(data_path) 39 | 40 | # Define your scenario, or use a predefined one 41 | # scenario = Scenario() 42 | # or 43 | scenario = BASIC_SCENARIO 44 | 45 | # Run the spread model for the economics simulator 46 | run(scenario, data_path) 47 | 48 | # Run simulation 49 | help(simulator.simulate) # for info 50 | # Warning, this takes ~20 minutes to run and ~10GB of RAM 51 | econ, states = simulator.simulate(scenario) 52 | ``` 53 | 54 | ## Data 55 | 56 | Example data is [here](../../tests/adapter_covid19/data) and a data specification can be found [here](../../tests/adapter_covid19/data). 57 | 58 | ## LICENSE 59 | 60 | AdaptER-Covid19 is licensed separately to OpenABM-Covid19 under the [Apache 2.0 License](LICENSE). 61 | -------------------------------------------------------------------------------- /man/Parameters.new.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/Parameters.R 3 | \name{Parameters.new} 4 | \alias{Parameters.new} 5 | \title{Creates a new OpenABM Parameters object (wrapper for 6 | \code{\link{Parameters}$new()})} 7 | \usage{ 8 | Parameters.new( 9 | input_param_file = NA_character_, 10 | param_line_number = 1, 11 | output_file_dir = "./", 12 | input_households = NA_character_, 13 | hospital_input_param_file = NA_character_, 14 | hospital_param_line_number = NA_integer_, 15 | read_param_file = TRUE, 16 | read_hospital_param_file = FALSE 17 | ) 18 | } 19 | \arguments{ 20 | \item{input_param_file}{Input parameters CSV file path. 21 | Optional, default: 22 | \code{system.file("default_params", "baseline_parameters.csv", package = "OpenABMCovid19")}} 23 | 24 | \item{param_line_number}{Which column of the input param file to read.} 25 | 26 | \item{output_file_dir}{Where to write output files to.} 27 | 28 | \item{input_households}{Household demographics file. 29 | Optional, default: 30 | \code{system.file("default_params", "baseline_household_demographics.csv", package = "OpenABMCovid19")}} 31 | 32 | \item{hospital_input_param_file}{Hospital input parameters CSV file path. 33 | Optional, default: 34 | \code{system.file("default_params", "hospital_baseline_parameters.csv", package = "OpenABMCovid19")}} 35 | 36 | \item{hospital_param_line_number}{Which column of the hospital input 37 | param file to read.} 38 | 39 | \item{read_param_file}{A boolean. If \code{TRUE}, read 40 | \code{input_param_file}. If \code{FALSE}, ignore 41 | \code{input_param_file}.} 42 | 43 | \item{read_hospital_param_file}{A boolean. If \code{TRUE}, read 44 | \code{hospital_input_param_file}. If \code{FALSE}, ignore 45 | \code{hospital_input_param_file}.} 46 | } 47 | \value{ 48 | Parameters object (R6 Class) 49 | } 50 | \description{ 51 | Creates a new OpenABM Parameters object (wrapper for 52 | \code{\link{Parameters}$new()}) 53 | } 54 | \seealso{ 55 | \href{https://github.com/BDI-pathogens/OpenABM-Covid19/blob/master/documentation/parameters/parameter_dictionary.md}{Online Documentation}. 56 | } 57 | -------------------------------------------------------------------------------- /src/input.h: -------------------------------------------------------------------------------- 1 | /* 2 | * input.h 3 | * 4 | * Description: Top level model 'object' c 5 | * Created on: 6 Mar 2020 6 | * Author: p-robot 7 | */ 8 | 9 | #ifndef INPUT_H_ 10 | #define INPUT_H_ 11 | 12 | /************************************************************************/ 13 | /******************************* Includes *******************************/ 14 | /************************************************************************/ 15 | 16 | #include "params.h" 17 | #include "model.h" 18 | 19 | /************************************************************************/ 20 | /****************************** Functions *****************************/ 21 | /************************************************************************/ 22 | 23 | void read_command_line_args(parameters *, int, char **); 24 | void read_param_file(parameters *); 25 | void read_household_demographics_file(parameters *); 26 | void read_hospital_param_file( parameters *); 27 | void set_up_reference_household_memory(parameters *params); 28 | void write_output_files(model *, parameters *); 29 | void write_individual_file(model *, parameters *); 30 | void write_interactions( model* ); 31 | void write_transmissions( model* ); 32 | void write_trace_tokens( model* ); 33 | void write_trace_tokens_ts( model*, int ); 34 | void write_quarantine_reasons( model*, parameters *); 35 | void write_ward_data( model* ); 36 | int get_worker_ward_type( model *pmodel, int pdx ); 37 | void write_time_step_hospital_data( model *pmodel); 38 | void write_hospital_interactions(model *pmodel); 39 | 40 | void write_occupation_network(model *, parameters *, int ); 41 | void write_household_network(model *, parameters *); 42 | void write_random_network(model *, parameters *); 43 | void write_network(char *, network *); 44 | 45 | void print_interactions_averages( model*, int ); 46 | 47 | long get_n_transmissions( model* ); 48 | void get_transmissions( model*, 49 | long*, int*, long*, int*, int*, int*, int*, int*, int*, 50 | long*, int*, long*, int*, int*, int*, int*, int*, int*, 51 | int*, int*, int*, int*, int*, int*, int*, int*, 52 | int*, int*, int*, int*, int*, int*, int*, float*, float* 53 | ); 54 | 55 | #endif /* INPUT_H_ */ 56 | -------------------------------------------------------------------------------- /src/covid19.i: -------------------------------------------------------------------------------- 1 | /* covid19.i */ 2 | %module OpenABMCovid19 3 | 4 | /* Tell SWIG to ignore/rename problematic macros/idents used in headers */ 5 | %rename(_next) next; 6 | %rename(_TRUE) TRUE; 7 | %rename(_FALSE) FALSE; 8 | %ignore TRUE; 9 | %ignore FALSE; 10 | %ignore next; 11 | 12 | %{ 13 | #define SWIG_FILE_WITH_INIT 14 | #define SWIG_PYTHON_CAST_MODE 15 | #include "model.h" 16 | #include "params.h" 17 | #include "constant.h" 18 | #include "input.h" 19 | #include "individual.h" 20 | #include "interventions.h" 21 | #include "utilities.h" 22 | #include "disease.h" 23 | #include "network.h" 24 | #include "strain.h" 25 | %} 26 | 27 | %rename (create_model) new_model(parameters *params); 28 | %rename (create_event) new_event(model *pmodel); 29 | 30 | %nodefaultdtor; 31 | 32 | /* These structs shouldn't be used directly (memory is managed by create_model 33 | * and destroy_model). Ignoring these ctors is necessary to suppress NOTEs 34 | * from `R CMD check`. */ 35 | %nodefaultctor directory; 36 | %nodefaultdtor directory; 37 | %nodefaultctor edge; 38 | %nodefaultdtor edge; 39 | %nodefaultctor event; 40 | %nodefaultdtor event; 41 | %nodefaultctor event_block; 42 | %nodefaultdtor event_block; 43 | %nodefaultctor event_list; 44 | %nodefaultdtor event_list; 45 | %nodefaultctor individual; 46 | %nodefaultdtor individual; 47 | %nodefaultctor infection_event; 48 | %nodefaultdtor infection_event; 49 | %nodefaultctor interaction; 50 | %nodefaultdtor interaction; 51 | %nodefaultctor interaction_block; 52 | %nodefaultdtor interaction_block; 53 | %nodefaultctor incomplete_gamma_p_params; 54 | %nodefaultdtor incomplete_gamma_p_params; 55 | %nodefaultctor trace_token; 56 | %nodefaultdtor trace_token; 57 | %nodefaultctor trace_token_block; 58 | %nodefaultdtor trace_token_block; 59 | %nodefaultctor strain; 60 | %nodefaultdtor strain; 61 | 62 | %include "model.h" 63 | %include "params.h" 64 | %include "constant.h" 65 | %include "input.h" 66 | %include "individual.h" 67 | %include "interventions.h" 68 | %include "utilities.h" 69 | %include "disease.h" 70 | %include "network.h" 71 | %include "strain.h" 72 | %include model_utils.i 73 | %include params_utils.i 74 | %include network_utils.i 75 | %include vaccine_utils.i 76 | %include strain_utils.i 77 | -------------------------------------------------------------------------------- /examples/example_102.R: -------------------------------------------------------------------------------- 1 | # 2 | # Example 102 - Lockdown and Contract-Tracing 3 | # 4 | # This second example runs the simulation until 1% of the population has been 5 | # infected. At which point a lockdown is imposed for 30 days. After which a 6 | # number of other interventions are implemented such as digital contract-tracing. 7 | # 8 | # Created: 8 April 2021 9 | # Author: roberthinch 10 | # 11 | 12 | library( OpenABMCovid19 ) 13 | 14 | model = Model.new( params = list( n_total = 50000, end_time = 500 ) ) 15 | 16 | # Run the model until there are 500 infections 17 | Model.one_time_step( model ) 18 | while( Model.one_time_step_results( model )[["total_infected"]] < 500 ) 19 | Model.one_time_step( model ) 20 | 21 | # Turn on lockdown and run the model for another 30 days 22 | Model.update_running_params( model, "lockdown_on", 1 ) 23 | for( t in 1:30 ) 24 | Model.one_time_step( model ) 25 | 26 | # Now turn off the lockdown and turn on digital contract tracing, with the following options. 27 | # 28 | # 1. 80% of people self-quarantine along with their household when they develop symptoms. 29 | #2. Tracing happens as soon as somebody develops symptoms and contacts quarantine themselves. 30 | #3. The households members of those traced also quarantine 31 | 32 | # We then run the simulation for another 100 days. 33 | 34 | Model.update_running_params( model, "lockdown_on", 0) 35 | 36 | # 80% self-quarantine along with their households 37 | Model.update_running_params( model, "self_quarantine_fraction", 0.8 ) 38 | Model.update_running_params( model, "quarantine_household_on_symptoms", 1 ) 39 | 40 | # turn on the app and quarantine those people who have been traced along with their households 41 | Model.update_running_params( model, "app_turned_on", 1 ) 42 | Model.update_running_params( model, "quarantine_on_traced", 1 ) 43 | Model.update_running_params( model, "trace_on_symptoms", 1 ) 44 | Model.update_running_params( model, "quarantine_household_on_traced_symptoms", 1 ) 45 | 46 | # step forwrard another 100 days 47 | for( t in 1:100 ) 48 | Model.one_time_step( model ) 49 | 50 | # Plot total infected through time 51 | results = Model.results(model) 52 | plot(results$time, results$total_infected, 53 | type = "l", col = "blue", xlab = "Time", ylab = "Total infected") 54 | 55 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Mac-related files 2 | .DS_Store 3 | 4 | # File associated with configure 5 | config.log 6 | config.status 7 | configure 8 | src/Makevars 9 | autom4te.cache/ 10 | 11 | # File associated with compilation/linking 12 | src/**/*.o 13 | src/**/*.exe 14 | src/*.csv 15 | !src/adapter_covid19/data/*.csv 16 | src/**/*.so 17 | src/**/*.dll 18 | examples/results/*.csv 19 | cmake-build-debug/CMakeFiles/** 20 | 21 | # Python testing files 22 | __pycache__ 23 | .mypy_cache/ 24 | **/*.egg-info 25 | *.pkl 26 | 27 | # Eclipse-related files 28 | .project 29 | .cproject 30 | .settings 31 | .pydevproject 32 | 33 | # Other editor-related files 34 | *.swo 35 | *.swp 36 | .idea 37 | **/.tags* 38 | TAGS 39 | tags 40 | 41 | # temp copies 42 | *~ 43 | 44 | # latex unwanted files 45 | documentation/*copy* 46 | documentation/*synctex* 47 | documentation/*.log 48 | documentation/*.aux 49 | documentation/*.key 50 | documentation/*.dvi 51 | documentation/*.blg 52 | documentation/*.bbl 53 | documentation/*.out 54 | documentation/*.toc 55 | 56 | # R-related files 57 | .Rhistory 58 | .RData 59 | .Rproj.user 60 | 61 | # Generated by RStudio on Windows 62 | src-i386/ 63 | src-x64/ 64 | 65 | # R package build files 66 | /OpenABMCovid19/ 67 | /OpenABMCovid19.Rcheck/ 68 | /OpenABMCovid19_*.tar.gz 69 | /OpenABMCovid19_*.tgz 70 | /OpenABMCovid19_*.zip 71 | /OpenABMCovid19.tmp/ 72 | 73 | # Roxygen2 generated documentation 74 | /man/AgeGroupEnum.Rd 75 | /man/Agent.Rd 76 | /man/COVID19IBM.Rd 77 | /man/Environment.Rd 78 | /man/Model.Rd 79 | /man/NETWORK_CONSTRUCTIONS.Rd 80 | /man/Network.Rd 81 | /man/Parameters.Rd 82 | /man/SAFE_UPDATE_PARAMS.Rd 83 | /man/Simulation.Rd 84 | /man/VaccineSchedule.Rd 85 | /man/VACCINE_STATUS.Rd 86 | /man/VACCINE_TYPES.Rd 87 | 88 | 89 | # performance related 90 | performance/*.trace 91 | performance/*.dtps 92 | 93 | scratch/* 94 | 95 | src_test/* 96 | data_test/* 97 | 98 | venv/* 99 | 100 | # SWIG generated files 101 | src/covid19.py 102 | src/covid19_wrap.c 103 | src/covid19_wrap_R.c 104 | R/OpenABMCovid19.R 105 | 106 | sandbox/*.csv 107 | examples/*.png 108 | examples/images/* 109 | examples/*.csv 110 | examples/.ipynb_checkpoints 111 | sandbox/.ipynb_checkpoints 112 | /Debug/ 113 | /.pytest_cache/ 114 | -------------------------------------------------------------------------------- /tests/testthat/test-Simulation.R: -------------------------------------------------------------------------------- 1 | library(R6) 2 | 3 | Number <- R6Class( inherit = Environment, 4 | public = list( 5 | number = NULL, 6 | 7 | start_simulation = function() { 8 | self$number <- c(number = 0) 9 | return(super$start_simulation()) 10 | }, 11 | 12 | step = function(action) { 13 | self$number <- action(self$number) 14 | return(c(number = self$number[[1]])) 15 | } 16 | ) 17 | ) 18 | 19 | Fibonacci <- R6Class( inherit = Agent, 20 | public = list( 21 | previous.number = NULL, 22 | 23 | initialize = function(...) { 24 | super$initialize(verbose...) 25 | }, 26 | 27 | # action for fib(n=0) 28 | action.0 = function(...) { 29 | return(1) 30 | }, 31 | 32 | # action for fib(n=1) 33 | action.1 = function(...) { 34 | self$previous.number <- 1 35 | return(1) 36 | }, 37 | 38 | # action for fib(n) 39 | action.n = function(current.number) { 40 | new.number <- self$previous.number + current.number 41 | self$previous.number <- current.number 42 | return(new.number) 43 | }, 44 | 45 | start_simulation = function(state) { 46 | self$previous.number <- 0 47 | return(self$action.0) 48 | }, 49 | 50 | step = function(state) { 51 | if (self$previous.number == 0) { 52 | return(self$action.1) 53 | }else { 54 | return(self$action.n) 55 | } 56 | } 57 | ) 58 | ) 59 | 60 | test_that("Simulation:fibonacci", { 61 | sim <- Simulation$new( env = Number$new(), agent = Fibonacci$new() ) 62 | 63 | sim$start_simulation() 64 | sim$steps(7) 65 | sim$end_simulation() 66 | expect_equal(sim$timestep, 7) 67 | expect_equal(sim$results, list(number = c(1, 1, 2, 3, 5, 8, 13))) 68 | expect_equal(sim$results_all_simulations, list()) 69 | 70 | sim$start_simulation() 71 | sim$steps(3) 72 | sim$end_simulation() 73 | expect_equal(sim$timestep, 3) 74 | expect_equal(sim$results, list(number = c(1, 1, 2))) 75 | expect_equal(sim$results_all_simulations, list( 76 | list(number = c(1, 1, 2, 3, 5, 8, 13)) 77 | )) 78 | 79 | sim$start_simulation() 80 | expect_equal(sim$timestep, 0) 81 | expect_equal(sim$results_all_simulations, list( 82 | list(number = c(1, 1, 2, 3, 5, 8, 13)), 83 | list(number = c(1, 1, 2)) 84 | )) 85 | }) 86 | -------------------------------------------------------------------------------- /src/adapter_covid19/metrics.py: -------------------------------------------------------------------------------- 1 | from datetime import date 2 | from typing import List 3 | 4 | import numpy as np 5 | import pandas as pd 6 | 7 | from adapter_covid19.data_structures import SimulateState 8 | from adapter_covid19.economics import Economics 9 | 10 | GDP_REDUCTION_EST = { 11 | date(2020, 3, 31): 0.97, 12 | date(2020, 6, 30): 0.75, 13 | date(2020, 9, 30): 0.82, 14 | date(2020, 12, 31): 0.90, # whole year: 0.86 15 | } 16 | 17 | DEMAND_REDUCTION_EST = { 18 | date(2020, 6, 30): 0.60, 19 | } 20 | 21 | 22 | def get_quarterly_gdp_decline( 23 | starting_date: date, states: List[SimulateState], 24 | ) -> pd.DataFrame: 25 | s_gdp_decline_simu = pd.DataFrame( 26 | [state.gdp_state.fraction_gdp_by_sector() for state in states], 27 | index=pd.date_range(start=starting_date, periods=len(states)), 28 | ).sum(axis=1) 29 | 30 | return pd.concat( 31 | [ 32 | s_gdp_decline_simu.resample("1Q").last().rename("GDP decline simulation"), 33 | pd.Series(GDP_REDUCTION_EST).rename("GDP decline estimates"), 34 | ], 35 | axis=1, 36 | ) 37 | 38 | 39 | def get_quarterly_demand_decline( 40 | starting_date: date, econ: Economics, states: List[SimulateState], 41 | ) -> pd.DataFrame: 42 | s_demand_decline_simu_by_sector = pd.DataFrame( 43 | [state.personal_state.demand_reduction for state in states], 44 | index=pd.date_range(start=starting_date, periods=len(states)), 45 | ) 46 | 47 | expense_by_sector = np.array( 48 | list(econ.personal_model.expenses_by_expense_sector.values()) 49 | ) 50 | 51 | s_demand_decline_simu = pd.Series( 52 | np.ndarray.flatten( 53 | np.dot( 54 | 1 - s_demand_decline_simu_by_sector.values, 55 | expense_by_sector.reshape(-1, 1), 56 | ) 57 | ) 58 | / expense_by_sector.sum(), 59 | index=s_demand_decline_simu_by_sector.index, 60 | ) 61 | 62 | return pd.concat( 63 | [ 64 | s_demand_decline_simu.resample("1Q") 65 | .last() 66 | .rename("Demand decline simulation"), 67 | pd.Series(DEMAND_REDUCTION_EST).rename("Demand decline estimates"), 68 | ], 69 | axis=1, 70 | ) 71 | -------------------------------------------------------------------------------- /src/disease.h: -------------------------------------------------------------------------------- 1 | /* 2 | * disease.h 3 | * 4 | * Description: this file contains code relating to the dynamics of the disease 5 | * within an individual and transmission events during interactions 6 | * Created on: 18 Mar 2020 7 | * Author: hinchr 8 | */ 9 | 10 | #ifndef DISEASE_H_ 11 | #define DISEASE_H_ 12 | 13 | /************************************************************************/ 14 | /******************************* Includes *******************************/ 15 | /************************************************************************/ 16 | 17 | #include "individual.h" 18 | #include "model.h" 19 | #include "utilities.h" 20 | 21 | /************************************************************************/ 22 | /****************************** Functions *****************************/ 23 | /************************************************************************/ 24 | 25 | #define sample_transition_time( model, type ) ( sample_draw_list( model->transition_time_distributions[type] ) ) 26 | 27 | 28 | // set up distributions and infectious curves 29 | void set_up_infectious_curves( model* ); 30 | double estimate_mean_interactions_by_age( model *pmodel, int age ); 31 | 32 | // transmission of the virus 33 | void transmit_virus( model* ); 34 | void transmit_virus_by_type( model*, int ); 35 | 36 | // progression of the disease 37 | void new_infection( model*, individual*, individual*, int, strain* ); 38 | short seed_infect_by_idx( model*, long, int, int ); 39 | long seed_infect_n_people( model*, long, int, int ); 40 | void transition_to_symptomatic( model*, individual* ); 41 | void transition_to_symptomatic_mild( model*, individual* ); 42 | void transition_to_hospitalised( model*, individual* ); 43 | void transition_to_hospitalised_recovering( model*, individual* ); 44 | void transition_to_critical( model*, individual* ); 45 | void transition_to_recovered( model*, individual* ); 46 | void transition_to_susceptible( model*, individual* ); 47 | void transition_to_death( model*, individual* ); 48 | void transition_one_disese_event( model*, individual*, int, int, int ); 49 | short apply_cross_immunity( model*, individual*, short, short ); 50 | 51 | // calculation of R of disease 52 | long n_newly_infected( model*, int time ); 53 | double calculate_R_instanteous( model*, int, double ); 54 | 55 | // cross-immunity 56 | void set_cross_immunity_probability( model*, int, int, float ); 57 | 58 | #endif /* DISEASE_H_ */ 59 | -------------------------------------------------------------------------------- /NAMESPACE: -------------------------------------------------------------------------------- 1 | importFrom("methods", "as") 2 | importFrom("methods", "callNextMethod") 3 | importFrom("methods", "is") 4 | importFrom("methods", "new") 5 | importFrom("methods", "setClass") 6 | importFrom("methods", "slot") 7 | importFrom("R6", "R6Class") 8 | importFrom("isnullptr", "isnullptr") 9 | importFrom("parallel", "makeCluster") 10 | importFrom("parallel", "stopCluster") 11 | importFrom("parallel", "clusterApply") 12 | importFrom("parallel", "detectCores") 13 | import("data.table") 14 | importFrom( "plotly", "plot_ly" ) 15 | importFrom( "plotly", "layout" ) 16 | importFrom( "plotly", "animation_opts" ) 17 | importFrom( "plotly", "colorbar" ) 18 | importFrom( "plotly", "%>%" ) 19 | importFrom( "stringr", "str_replace_all") 20 | importFrom( "utils", "read.csv") 21 | 22 | # R6 Classes 23 | export("Parameters") 24 | export("Model") 25 | export("Network") 26 | export("Environment") 27 | export("Agent") 28 | export("Simulation") 29 | export("Strain") 30 | export("COVID19IBM") 31 | export("VaccineSchedule") 32 | export("Vaccine") 33 | export("MetaModel") 34 | 35 | # wrapper function for R6 calsses 36 | export("Parameters.new") 37 | export("Parameters.get_param") 38 | export("Parameters.set_param") 39 | export("Parameters.default_param") 40 | export("Model.new") 41 | export("Model.one_time_step") 42 | export("Model.one_time_step_results") 43 | export("Model.results") 44 | export("Model.run") 45 | export("Model.get_param") 46 | export("Model.update_running_params") 47 | export("Model.get_transmissions") 48 | 49 | # MetaModel 50 | export( "MetaModel.rectangle") 51 | export( "MetaModel.England") 52 | 53 | # Enums 54 | export("AgeGroupEnum") 55 | export("SAFE_UPDATE_PARAMS") 56 | export("NETWORK_CONSTRUCTIONS") 57 | export("VACCINE_TYPES") 58 | export("VACCINE_STATUS") 59 | 60 | # constants 61 | export( "UNKNOWN" ) 62 | export("plot.value.total_infected") 63 | export("plot.value.new_infected") 64 | export("plot.values") 65 | export("plot.value.total_infected_strain_0") 66 | export("plot.value.new_infected_strain_0") 67 | export("plot.value.total_infected_strain_1") 68 | export("plot.value.new_infected_strain_1") 69 | export("plot.value.total_infected_strain_2") 70 | export("plot.value.new_infected_strain_2") 71 | export("plot.value.total_infected_strain_3") 72 | export("plot.value.new_infected_strain_3") 73 | export("plot.value.total_infected_strain_4") 74 | export("plot.value.new_infected_strain_4") 75 | 76 | useDynLib(OpenABMCovid19, .registration = TRUE) 77 | -------------------------------------------------------------------------------- /R/VaccineSchedule.R: -------------------------------------------------------------------------------- 1 | #' R6Class VaccineSchedule 2 | #' 3 | #' @description 4 | #' VaccineSchedule object has information an age-group vaccination plan. 5 | #' 6 | #' @examples 7 | #' # Vaccinate 15% of age group 70-79 and 85% of age group 80+ 8 | #' vaccine.schedule <- OpenABMCovid19::VaccineSchedule$new( 9 | #' frac_70_79 = 0.15, 10 | #' frac_80 = 0.85, 11 | #' vaccine = Vaccine 12 | #' ) 13 | VaccineSchedule <- R6Class( classname = 'VaccineSchedule', cloneable = TRUE, 14 | 15 | public = list( 16 | #' @field fraction_to_vaccinate Get the vaccination fractions per age-group. 17 | fraction_to_vaccinate = NULL, 18 | 19 | #' @field total_vaccinated The total number of vaccinations per age-group. 20 | total_vaccinated = NULL, 21 | 22 | #' @field vaccinethe R vaccine object 23 | vaccine = NULL, 24 | 25 | #' @param frac_0_9 Fraction of age group 0-9. 26 | #' @param frac_10_19 Fraction of age group 10-19. 27 | #' @param frac_20_29 Fraction of age group 20-29. 28 | #' @param frac_30_39 Fraction of age group 30-39. 29 | #' @param frac_40_49 Fraction of age group 40-49. 30 | #' @param frac_50_59 Fraction of age group 50-59. 31 | #' @param frac_60_69 Fraction of age group 60-69. 32 | #' @param frac_70_79 Fraction of age group 70-79. 33 | #' @param frac_80 Fraction of age group >80. 34 | #' @param vaccine A vaccine object 35 | #' wanes. 36 | initialize = function( 37 | frac_0_9 = 0, 38 | frac_10_19 = 0, 39 | frac_20_29 = 0, 40 | frac_30_39 = 0, 41 | frac_40_49 = 0, 42 | frac_50_59 = 0, 43 | frac_60_69 = 0, 44 | frac_70_79 = 0, 45 | frac_80 = 0, 46 | vaccine = -1, 47 | fractions = NA 48 | ) 49 | { 50 | if( is.na( fractions ) ) 51 | fractions <- c(frac_0_9, frac_10_19, frac_20_29, frac_30_39, frac_40_49, 52 | frac_50_59, frac_60_69, frac_70_79, frac_80) 53 | n <- length(AgeGroupEnum) 54 | if (n != length(fractions)) { 55 | stop("length(AgeGroupEnum) doesn't match VaccineSchedule's age groups") 56 | } 57 | 58 | if (!is.R6(vaccine) || !('Vaccine' %in% class(vaccine))) 59 | stop("argument vaccine must be an object of type Vaccine") 60 | 61 | self$fraction_to_vaccinate <- fractions 62 | self$total_vaccinated <- as.integer(rep(0,n)) 63 | self$vaccine <- vaccine 64 | } 65 | ) 66 | ) 67 | -------------------------------------------------------------------------------- /tests/adapter_covid19/data/input_output_primary.csv: -------------------------------------------------------------------------------- 1 | Sector,IMPORTS,TAXES_PRODUCTS,COMPENSATION,TAXES_PRODUCTION,FIXED_CAPITAL_CONSUMPTION,NET_OPERATING_SURPLUS 2 | A_AGRICULTURE,747.5089937529019,474.33334639305895,4044.172074101395,-2091.7619587865433,3138.381976188842,5960.333898017153 3 | B_MINING,1696.452835511377,134.6347769650004,4160.344898955722,149.2319583758568,7668.199509147842,-1902.73055948931 4 | C_MANUFACTURING,90570.22786953085,2112.458811649176,105956.01321026249,2194.941669301164,26351.48436280115,37381.331903012295 5 | D_ELECTRICITY,7396.605233396666,1021.271543136393,7138.563257290103,1681.923713285241,6722.048701483962,10389.59245616917 6 | E_WATER,2242.5515582234825,1589.8423523466504,7123.618435195042,863.5633629945642,5586.206520917678,7787.47622274822 7 | F_CONSTRUCTION,6003.572394962132,6094.065956538927,49176.49939369927,1331.924367408831,5591.6858031807,52801.834132135 8 | G_TRADE,17480.588856789193,2899.4418816971697,123534.30171955406,9335.906195443597,19872.889194142554,38596.73114880982 9 | H_TRANSPORT,14576.740661990969,5480.153001637052,51591.70316925505,1704.3078548984545,17204.267849684325,1930.6790494399006 10 | I_ACCOMODATION,3584.078015795526,3398.238417751759,43485.65493712944,3327.286628262991,7147.075800584376,9616.204663379473 11 | J_COMMUNICATION,17516.436957178244,505.5472783033342,65781.8525340792,598.3762040762167,19418.0370543674,25242.244501750487 12 | K_FINANCIAL,21137.711900919247,11682.32801009512,62870.64312871361,2965.9484782440186,10346.40851367124,41712.26290919411 13 | L_REAL_ESTATE,5421.154399655259,1426.3061613174839,20323.45482068453,87.10434027585207,77858.64227067688,162894.3379673381 14 | M_PROFESSIONAL,14168.335592941572,997.6179122576366,89515.21165852857,1520.0437254288956,15639.731554354812,38813.80718959182 15 | N_ADMINISTRATIVE,8066.485055794931,1163.4184579565476,58859.870299501374,1051.028706678282,9681.274617279705,24418.79466724897 16 | O_PUBLIC,7988.179415462249,6355.370178247998,60803.8668126968,0.0,24877.85232702926,-40.45500120605243 17 | P_EDUCATION,1779.480012089003,3131.427141637338,78821.68115532615,370.8532459634233,14195.79945795745,3163.639358643934 18 | Q_HEALTH,9536.548197310789,8418.674683714042,101670.63873499073,303.01563570273737,9254.103569344825,19784.06682740416 19 | R_ARTS,1214.158292105473,1589.9853306056648,14119.741025509513,673.1577517548534,3320.1559626989338,7930.229935217381 20 | S_OTHER,1715.6069085051392,644.4365577494583,13785.168734526906,377.14812069156096,1591.7549544880492,12019.618730595343 21 | T_HOUSEHOLD,0.011,-0.02400000000000001,4961.0,0.0,0.0,0.0 22 | -------------------------------------------------------------------------------- /documentation/output_files/individual_file.md: -------------------------------------------------------------------------------- 1 | # Table: individual file 2 | | Column name | Description | 3 | | ---- | ---- | 4 | | `ID` | Unique identifier of the individual | 5 | | `current_status` | Disease status of the individual at the point at which the individual file is written to file. See the transmission file for the status of an individual through time. Note that this is a variable that may change throughout the simulation and so may be removed from this file in the future. | 6 | | `age_group` | Age group of the individual | 7 | | `occupation_network` | Occupation network to which this individual has membership (coded by the `enum OCCUPATION_NETWORKS` enum in constant.h) | 8 | | `worker_type` | Type of hospital worker (coded by the `enum WORKER_TYPES` in constant.h) (default -1) | 9 | | `assigned_worker_ward_type` | Type of ward within which this individual works (coded by the `enum HOSPITAL_WARD_TYPES` in constant.h) (default -1) | 10 | | `house_no` | Household identifier to which this individual belongs | 11 | | `quarantined` | Is the individual currently quarantined (1=Yes; 0=No). See the quarantine reasons at each time step for a complete list of quarantined individuals through time. Note that this is a variable that may change throughout the simulation and so may be removed from this file in the future. | 12 | | `time_quarantined` | Time at which the individual was quarantined if they are currently quarantined. Note that this is a variable that may change throughout the simulation and so may be removed from this file in the future. | 13 | | `test_status` | `quarantine_test_result` attribute of the individual struct. Currently mainly used for testing purposes. Takes the following values: -2: the individual is not currently being tested; -1 (-3): a (priority) test has been ordered; 0: the individual is waiting for a result which will be negative; 1: the individual is waiting for a test result which will be positive; note that regardless of the result once the test result has been received this variables returns to not current being tested (-2) | 14 | | `app_user` | Is this individual an app user (1=Yes; 0=No) | 15 | | `mean_interactions` | Number of random daily interactions of the individual (the random_interactions attribute of the individual struct) | 16 | | `infection_count` | Number of times this individual has been infected with SARS-CoV-2 | 17 | | `infectiousness_multiplier` | An individual multiplier of how infectious somebody is | 18 | | `vaccine_status` | The current vaccine status of the individual (0 = none; 1 = not protect; 2 = protected fully; 3 = protected from symptoms; 4 = waned )| -------------------------------------------------------------------------------- /src/constant.c: -------------------------------------------------------------------------------- 1 | /* 2 | * constant.c 3 | * 4 | * Created on: 22 Mar 2020 5 | * Author: hinchr 6 | */ 7 | 8 | #include "constant.h" 9 | 10 | const int AGE_OCCUPATION_MAP[N_AGE_GROUPS] = { 11 | PRIMARY_NETWORK, 12 | SECONDARY_NETWORK, 13 | WORKING_NETWORK, 14 | WORKING_NETWORK, 15 | WORKING_NETWORK, 16 | WORKING_NETWORK, 17 | WORKING_NETWORK, 18 | RETIRED_NETWORK, 19 | ELDERLY_NETWORK 20 | }; 21 | 22 | const int NETWORK_TYPE_MAP[N_DEFAULT_OCCUPATION_NETWORKS] = { 23 | NETWORK_TYPE_CHILD, 24 | NETWORK_TYPE_CHILD, 25 | NETWORK_TYPE_ADULT, 26 | NETWORK_TYPE_ELDERLY, 27 | NETWORK_TYPE_ELDERLY 28 | }; 29 | 30 | const int OCCUPATION_DEFAULT_MAP[N_DEFAULT_OCCUPATION_NETWORKS] = { 31 | OCCUPATION_PRIMARY_NETWORK, 32 | OCCUPATION_SECONDARY_NETWORK, 33 | OCCUPATION_WORKING_NETWORK, 34 | OCCUPATION_RETIRED_NETWORK, 35 | OCCUPATION_ELDERLY_NETWORK 36 | }; 37 | 38 | const char* DEFAULT_NETWORKS_NAMES[N_DEFAULT_NETWORKS] = { 39 | "Household network (default)", 40 | "Occupation primary school network (default)", 41 | "Occupation secondary school network (default)", 42 | "Occupation working network (default)", 43 | "Occupation retired network (default)", 44 | "Occupation elderly network (default)", 45 | "Random network (default)" 46 | }; 47 | 48 | const int AGE_TYPE_MAP[N_AGE_GROUPS] = { 49 | AGE_TYPE_CHILD, 50 | AGE_TYPE_CHILD, 51 | AGE_TYPE_ADULT, 52 | AGE_TYPE_ADULT, 53 | AGE_TYPE_ADULT, 54 | AGE_TYPE_ADULT, 55 | AGE_TYPE_ADULT, 56 | AGE_TYPE_ELDERLY, 57 | AGE_TYPE_ELDERLY 58 | }; 59 | 60 | const char* AGE_TEXT_MAP[N_AGE_GROUPS] = { 61 | "0-9 years", 62 | "10-19 years", 63 | "20-29 years", 64 | "30-39 years", 65 | "40-49 years", 66 | "50-59 years", 67 | "60-69 years", 68 | "70-79 years", 69 | "80+ years" 70 | }; 71 | 72 | const int EVENT_TYPE_TO_WARD_MAP[N_EVENT_TYPES] = { 73 | NOT_IN_HOSPITAL, 74 | NOT_IN_HOSPITAL, 75 | NOT_IN_HOSPITAL, 76 | NOT_IN_HOSPITAL, 77 | NOT_IN_HOSPITAL, 78 | NOT_IN_HOSPITAL, 79 | COVID_GENERAL, 80 | COVID_ICU, 81 | COVID_GENERAL, 82 | NOT_IN_HOSPITAL, 83 | NOT_IN_HOSPITAL, 84 | NOT_IN_HOSPITAL, 85 | NOT_IN_HOSPITAL, 86 | NOT_IN_HOSPITAL, 87 | NOT_IN_HOSPITAL, 88 | NOT_IN_HOSPITAL, 89 | NOT_IN_HOSPITAL, 90 | NOT_IN_HOSPITAL, 91 | NOT_IN_HOSPITAL, 92 | COVID_GENERAL, 93 | COVID_ICU, 94 | NOT_IN_HOSPITAL, 95 | NOT_IN_HOSPITAL, 96 | NOT_IN_HOSPITAL, 97 | NOT_IN_HOSPITAL, 98 | NOT_IN_HOSPITAL 99 | }; 100 | 101 | const int NEWLY_INFECTED_STATES[N_NEWLY_INFECTED_STATES] = { 102 | PRESYMPTOMATIC, 103 | PRESYMPTOMATIC_MILD, 104 | ASYMPTOMATIC, 105 | }; 106 | 107 | gsl_rng * rng; -------------------------------------------------------------------------------- /src/adapter_covid19/economics.py: -------------------------------------------------------------------------------- 1 | import logging 2 | 3 | from adapter_covid19.corporate_bankruptcy import CorporateBankruptcyModel 4 | from adapter_covid19.data_structures import ( 5 | SimulateState, 6 | Utilisations, 7 | ) 8 | from adapter_covid19.datasources import Reader 9 | from adapter_covid19.gdp import BaseGdpModel 10 | from adapter_covid19.personal_insolvency import PersonalBankruptcyModel 11 | 12 | LOGGER = logging.getLogger(__name__) 13 | 14 | 15 | class Economics: 16 | def __init__( 17 | self, 18 | gdp_model: BaseGdpModel, 19 | corporate_model: CorporateBankruptcyModel, 20 | personal_model: PersonalBankruptcyModel, 21 | **kwargs, 22 | ): 23 | """ 24 | Economics simulator 25 | 26 | Parameters 27 | ---------- 28 | gdp_model: Model to simulate GDP given reduced working numbers 29 | corporate_model: Model to simulate corporate bankruptcies 30 | personal_model: Model to simulate personal bankruptcies 31 | """ 32 | if kwargs: 33 | LOGGER.warning(f"Unused kwargs in {self.__class__.__name__}: {kwargs}") 34 | self.gdp_model = gdp_model 35 | self.corporate_model = corporate_model 36 | self.personal_model = personal_model 37 | 38 | def load(self, reader: Reader) -> None: 39 | """ 40 | Load data required for simulation 41 | 42 | Parameters 43 | ---------- 44 | reader: helper class to load data 45 | """ 46 | self.gdp_model.load(reader) 47 | self.corporate_model.load(reader) 48 | self.personal_model.load(reader) 49 | 50 | def simulate(self, simulate_state: SimulateState,) -> None: 51 | return self._simulate( 52 | simulate_state, simulate_state.time, simulate_state.utilisations, 53 | ) 54 | 55 | def _simulate( 56 | self, state: SimulateState, time: int, utilisations: Utilisations, 57 | ) -> None: 58 | """ 59 | Simulate the economy 60 | 61 | Parameters 62 | ---------- 63 | state: state 64 | time: from 0 to inf. Must be called sequentially 65 | utilisations: 66 | Mapping from region, sector and age group to a number 67 | between 0 and 1 describing the proportion of the 68 | workforce in work 69 | """ 70 | # There shouldn't really be any logic in this method; this should solely 71 | # provide the plumbing for the other three models 72 | 73 | self.gdp_model.simulate(state) 74 | self.corporate_model.simulate(state) 75 | self.personal_model.simulate(state) 76 | -------------------------------------------------------------------------------- /src/utilities.h: -------------------------------------------------------------------------------- 1 | /* 2 | * utilities.h 3 | * 4 | * Created on: 5 Mar 2020 5 | * Author: hinchr 6 | */ 7 | 8 | #ifndef UTILITIES_H_ 9 | #define UTILITIES_H_ 10 | 11 | #include 12 | 13 | /************************************************************************/ 14 | /****************************** Macros *****************************/ 15 | /************************************************************************/ 16 | 17 | #define max(x,y) ((x) > (y) ? (x) : (y)) 18 | #define min(x,y) ((x) < (y) ? (x) : (y)) 19 | #define ifelse(x,y,z) ((x) ? (y) : (z) ) 20 | #define round_random( x ) ( (long int) ( floor( x ) + gsl_ran_bernoulli( rng, x - floor(x) ) ) ) 21 | #define ring_inc( x, n ) ( ( x ) = ifelse( ( x ) == ( ( n ) -1 ), 0 , ( x ) + 1 ) ) 22 | #define ring_dec( x, n ) ( ( x ) = ifelse( ( x ) == 0 , ( n ) -1 , ( x ) - 1 ) ) 23 | #define sample_draw_list( x ) ( ( x[ gsl_rng_uniform_int( rng, N_DRAW_LIST ) ] ) ) 24 | #define printf(...) printf_w(__VA_ARGS__) 25 | 26 | /* Tell GCC to check printf formats for custom print functions. 27 | * https://gcc.gnu.org/onlinedocs/gcc-3.2/gcc/Function-Attributes.html */ 28 | #if __GNUC__ > 3 29 | #define _AF_PRINTF( _fmtPos, _valistPos) \ 30 | __attribute__((format( printf, _fmtPos, _valistPos ))) 31 | #else 32 | #define _AF_PRINTF( _fmtPos, _valistPos) 33 | #endif 34 | 35 | /************************************************************************/ 36 | /****************************** Functions *****************************/ 37 | /************************************************************************/ 38 | 39 | int printf_w( const char*, ... ) _AF_PRINTF(1,2); 40 | void print_now( char* ); 41 | void print_exit( char*, ... ) _AF_PRINTF(1,2); 42 | void fflush_stdout( void ); 43 | void gamma_draw_list( int*, int, double, double ); 44 | void bernoulli_draw_list( int*, int, double ); 45 | void geometric_max_draw_list( int*, int, double, int ); 46 | void shifted_geometric_draw_list( int*, int, double , int); 47 | void geometric_draw_list( int*, int, double ); 48 | void gamma_rate_curve( double*, int, double, double, double ); 49 | int negative_binomial_draw( double, double ); 50 | int discrete_draw( int, double* ); 51 | void normalize_array( double*, int ); 52 | void copy_array( double*, double*, int ); 53 | void copy_normalize_array( double*, double*, int ); 54 | double sum_square_diff_array( double*, double*, int ); 55 | int n_unique_elements( long*, int ); 56 | void setup_gsl_rng( int ); 57 | void free_gsl_rng(); 58 | struct incomplete_gamma_p_params { long n; double percentile; }; 59 | double incomplete_gamma_p( double, void *params ); 60 | double inv_incomplete_gamma_p( double, long ); 61 | 62 | #endif /* UTILITIES_H_ */ 63 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | This document outlines the basic workflow for contributing to the OpenABM-Covid19 model. Please also see notes at the bottom of this document. 4 | 5 | 1. Create a fork of the OpenABM-Covid19 repository and clone this repository. 6 | 7 | ```bash 8 | git clone https://github.com/USERNAME/OpenABM-Covid19.git 9 | ``` 10 | 11 | 2. Add the upstream repository for OpenABM-Covid19 12 | 13 | ```bash 14 | cd OpenABM-Covid19 15 | git remote add upstream https://github.com/BDI-pathogens/OpenABM-Covid19.git 16 | ``` 17 | 18 | 3. Sync upstream with the master branch of your fork 19 | 20 | ```bash 21 | git checkout master 22 | git fetch upstream master 23 | git merge master 24 | ``` 25 | 26 | 4. Make changes for any additions to the model in a branch on your fork 27 | 28 | ```bash 29 | git checkout -b feature:speed_up_fix 30 | 31 | # Add any new files with 'git add' etc 32 | git commit -m "Useful commit message" 33 | ``` 34 | 35 | 5. Push changes to your fork 36 | ```bash 37 | git push origin feature:speed_up_fix 38 | ``` 39 | 40 | 6. Head to the upstream repository at https://github.com/BDI-pathogens/OpenABM-Covid19 using a browser and a "pull request" button should be available. Click that and follow the prompts, tagging of one of the OpenABM-Covid19 core team in the pull request for review (roberthinch, p-robot, danielmonterocr, brynmathias). 41 | 42 | 43 | **Notes** 44 | 45 | * Any changes to the model need to pass all test (see testing guidelines in the main README.md) and new features of the model need to provide new tests of the added functionality. 46 | * PRs are only merged in the master (release) branch after all tests have passed. 47 | * If the contribution/PR changes the model parameterisation please add the new parameter(s) to `tests/data/baseline_parameters_transpose.csv`, including a description, default value, and reference (if possible) for the new parameters. Calling `python python/transpose_parameters.py` from the main project directory will create markdown documents of the parameter files and flat files `tests/data/baseline_parameters.csv` that can be included in the PR. You also to call `R -f tests/data/generate_Rda.R` to update the R data file in `data/baseline.rda`. 48 | * Parameters that wish to be updated via the Python interface need to be added to the list `PYTHON_SAFE_UPDATE_PARAMS` within `OpenABM-Covid19/src/COVID19/model.py`. 49 | * If the contribution/PR changes the Python/C interface please include documentation. 50 | * Changes to the Python interface should also include changes to the R interface. 51 | * If you're modifying the R interface, you should run a check on the package to ensure that they aren't any errors or warnings (run `devtools::check()` from R console or `R CMD check --as-cran` from the Unix terminal). 52 | -------------------------------------------------------------------------------- /tests/conftest.py: -------------------------------------------------------------------------------- 1 | # content of conftest.py 2 | 3 | import pytest, subprocess, shutil, os, sys 4 | from . import constant 5 | 6 | sys.path.append("src/COVID19") 7 | from parameters import ParameterSet 8 | 9 | 10 | def pytest_addoption(parser): 11 | parser.addoption( 12 | "--runslow", action="store_true", default=False, help="run slow tests" 13 | ) 14 | 15 | 16 | def pytest_configure(config): 17 | config.addinivalue_line("markers", "slow: mark test as slow to run") 18 | 19 | 20 | def pytest_collection_modifyitems(config, items): 21 | if config.getoption("--runslow"): 22 | # --runslow given in cli: do not skip slow tests 23 | return 24 | skip_slow = pytest.mark.skip(reason="need --runslow option to run") 25 | for item in items: 26 | if "slow" in item.keywords: 27 | item.add_marker(skip_slow) 28 | 29 | # "Session" (all files in folder) setup/teardown 30 | @pytest.fixture(scope = "session", autouse = True) 31 | def compile_covid_ibm(request): 32 | """ 33 | Compile the IBM in a temporary directory 34 | """ 35 | # Make a temporary copy of the code 36 | # (remove this temporary directory if it already exists) 37 | shutil.rmtree(constant.IBM_DIR_TEST, ignore_errors = True) 38 | shutil.copytree(constant.IBM_DIR, constant.IBM_DIR_TEST) 39 | 40 | # Construct the compilation command and compile 41 | compile_command = "make clean; make install" 42 | completed_compilation = subprocess.run( 43 | [compile_command], shell = True, cwd = constant.IBM_DIR_TEST, capture_output = True 44 | ) 45 | def fin(): 46 | # Teardown: remove the temporary code directory (when this class is removed) 47 | shutil.rmtree(constant.IBM_DIR_TEST, ignore_errors = True) 48 | request.addfinalizer(fin) 49 | 50 | 51 | # Method ("function") setup/teardown 52 | @pytest.fixture(scope = "function", autouse = True) 53 | def setup_covid_methods(request): 54 | """ 55 | Called before each method is run; creates a new data dir, copies test datasets 56 | """ 57 | os.mkdir(constant.DATA_DIR_TEST) 58 | shutil.copy(constant.TEST_DATA_TEMPLATE, constant.TEST_DATA_FILE) 59 | shutil.copy(constant.TEST_HOUSEHOLD_TEMPLATE, constant.TEST_HOUSEHOLD_FILE) 60 | shutil.copy(constant.TEST_HOSPITAL_TEMPLATE, constant.TEST_HOSPITAL_FILE) 61 | 62 | # Adjust any parameters that need adjusting for all tests 63 | params = ParameterSet(constant.TEST_DATA_FILE, line_number=1) 64 | params.set_param("n_total", 10000) 65 | params.set_param("end_time", 100) 66 | params.write_params(constant.TEST_DATA_FILE) 67 | def fin(): 68 | """ 69 | At the end of each method (test), remove the directory of test input/output data 70 | """ 71 | shutil.rmtree(constant.DATA_DIR_TEST, ignore_errors=True) 72 | request.addfinalizer(fin) 73 | -------------------------------------------------------------------------------- /R/Strain.R: -------------------------------------------------------------------------------- 1 | #' R6Class Strain 2 | #' 3 | #' @description 4 | #' Strain object has information about each new strain 5 | #' 6 | #' @examples 7 | #' # Add new strain with increased transmissibility 8 | #' strain = model$add_new_strain( transmission_multiplier = 1.3 ) 9 | #' 10 | Strain <- R6Class( classname = 'Strain', cloneable = FALSE, 11 | 12 | private = list( 13 | #' the strain ID 14 | id = NULL, 15 | 16 | #' .c_strain External pointer, reference to \code{strain} C struct. 17 | .c_strain = NULL, 18 | 19 | #' the C strain R pointer object 20 | c_strain_ptr = function() { 21 | return( self$c_strain@ref ) 22 | }, 23 | 24 | #' check the C strain still exists 25 | c_strain_valid = function() { 26 | return( !isnullptr( private$.c_strain@ref )) 27 | } 28 | ), 29 | 30 | active = list( 31 | #' @field c_strain the C strain R pointer object (SWIG wrapped) 32 | c_strain = function( val = NULL ) 33 | { 34 | if( is.null( val ) ) 35 | { 36 | if( private$c_strain_valid() ) 37 | return( private$.c_strain ) 38 | stop( "c_strain is no longer valid - create a new strain") 39 | } 40 | else 41 | stop( "cannot set c_strain" ) 42 | } 43 | ), 44 | 45 | public = list( 46 | 47 | #' @param model R6 Model object 48 | #' @param stain_id The strain ID. 49 | initialize = function( model, strain_id ) 50 | { 51 | if( !is.R6(model) || !inherits( model, "Model")) 52 | stop( "model must be a R6 class of type Model") 53 | 54 | private$.c_strain <- get_strain_by_id( model$c_model, strain_id ) 55 | private$id <- strain_id 56 | }, 57 | 58 | #' @description Wrapper for C API \code{strain$idx()}. 59 | #' @return the index of the strain 60 | idx = function() { 61 | return(strain_idx( self$c_strain )) 62 | }, 63 | 64 | #' @description Wrapper for C API \code{strain$transmission_multiplier()}. 65 | #' @return the transmission_multiplier of the strain 66 | transmission_multiplier = function() { 67 | return(strain_transmission_multiplier( self$c_strain )) 68 | }, 69 | 70 | #' @description Wrapper for C API \code{strain$total_infected()}. 71 | #' @return the total number of people infected with the strain 72 | total_infected = function() { 73 | return(strain_total_infected( self$c_strain )) 74 | }, 75 | 76 | #' @description Wrapper for C API \code{strain$hospitalised_fraction()}. 77 | #' @return the hospitalised fraction for the strain 78 | hospitalised_fraction = function() 79 | { 80 | c_strain_ptr = private$c_strain_ptr(); 81 | return( .Call('R_strain_hospitalised_fraction',c_strain_ptr,PACKAGE='OpenABMCovid19') ) 82 | } 83 | ) 84 | 85 | ) 86 | -------------------------------------------------------------------------------- /src/strain.h: -------------------------------------------------------------------------------- 1 | /* 2 | * strain.h 3 | * 4 | * Created on: 31 Mar 2021 5 | * Author: nikbaya 6 | */ 7 | 8 | #ifndef STRAIN_H_ 9 | #define STRAIN_H_ 10 | 11 | /************************************************************************/ 12 | /******************************* Includes *******************************/ 13 | /************************************************************************/ 14 | 15 | #include "structure.h" 16 | #include "constant.h" 17 | 18 | /************************************************************************/ 19 | /****************************** Structures *****************************/ 20 | /************************************************************************/ 21 | 22 | typedef struct strain strain; 23 | 24 | struct strain{ 25 | long idx; 26 | float transmission_multiplier; 27 | float mean_infectious_period; 28 | float sd_infectious_period; 29 | float mean_time_to_symptoms; 30 | float sd_time_to_symptoms; 31 | float mean_asymptomatic_to_recovery; 32 | float sd_asymptomatic_to_recovery; 33 | float mean_time_to_recover; 34 | float sd_time_to_recover; 35 | float mean_time_hospitalised_recovery; 36 | float sd_time_hospitalised_recovery; 37 | float mean_time_critical_survive; 38 | float sd_time_critical_survive; 39 | float mean_time_to_death; 40 | float sd_time_to_death; 41 | float mean_time_to_hospital; 42 | float mean_time_to_critical; 43 | float sd_time_to_critical; 44 | float mean_time_to_susceptible_after_shift; 45 | float time_to_susceptible_shift; 46 | float fraction_asymptomatic[N_AGE_GROUPS]; // fraction who are asymptomatic 47 | float mild_fraction[N_AGE_GROUPS]; // fraction who just have mild symptoms 48 | float hospitalised_fraction[N_AGE_GROUPS]; // fraction of symptomatic patients requiring hospitalisation 49 | float critical_fraction[N_AGE_GROUPS]; // fraction of hospitalised patients who require ICU treatment 50 | float fatality_fraction[N_AGE_GROUPS]; // fraction of ICU patients who die 51 | float location_death_icu[N_AGE_GROUPS]; // whether death occurs in the ICU or outside 52 | float asymptomatic_infectious_factor; 53 | float mild_infectious_factor; 54 | long total_infected; 55 | double **infectious_curve; 56 | int **transition_time_distributions; 57 | }; 58 | 59 | /************************************************************************/ 60 | /****************************** Functions *****************************/ 61 | /************************************************************************/ 62 | 63 | short add_new_strain( model*, float, double*, double*, double*, double*, double*, double*, 64 | double, double, double, double, double, double, double, 65 | double, double, double, double, double, double, double, 66 | double, double, double, double, double, double, double 67 | ); 68 | void destroy_strain( strain* ); 69 | strain* get_strain_by_id( model*, short ); 70 | 71 | #endif /* STRAIN_H_ */ 72 | -------------------------------------------------------------------------------- /documentation/parameters/infection_parameters.md: -------------------------------------------------------------------------------- 1 | # Table: Infection parameters 2 | | Name | Value | Symbol | Description | Source | 3 | | ---- | ---- | ---- | ---- | ---- | 4 | | `n_seed_infection` | 10 | - | Number of infections seeded at simulation start | - | 5 | | `mean_infectious_period` | 5.5 | μ | Mean of the generation time distribution (days) | Ferretti et al in prep 2020; Ferretti & Wymant et al 2020; Xia et al 2020; He et al 2020; Cheng et al 2020 | 6 | | `sd_infectious_period` | 2.14 | σ | Standard deviation (days) of infectious period | Ferretti et al in prep 2020; Ferretti & Wymant et al 2020; Xia et al 2020; He et al 2020; Cheng et al 2020 | 7 | | `infectious_rate` | 5.8 | *R* | Mean number of individuals infected by each infectious individual with moderate to severe symptoms | Derived from calibration | 8 | | `sd_infectiousness_multiplier` | 1.4 | - | SD of the lognormal used to vary the infectiousness of an individual | Derived from calibration | 9 | | `asymptomatic_infectious_factor` | 0.33 | *Aasym* | Infectious rate of asymptomatic individuals relative to symptomatic individuals | Personal communication, Sun | 10 | | `mild_infectious_factor` | 0.72 | *Amild* | Infectious rate of mildly symptomatic individuals relative to symptomatic individuals | Personal communication, Sun | 11 | | `relative_susceptibility_0_9` | 0.35 | *S0-9* | Relative susceptibility to infection, aged 0-9 | Zhang et al. 2020 | 12 | | `relative_susceptibility_10_19` | 0.69 | *S10-19* | Relative susceptibility to infection, aged 10-19 | Zhang et al. 2020 | 13 | | `relative_susceptibility_20_29` | 1.03 | *S20-29* | Relative susceptibility to infection, aged 20-29 | Zhang et al. 2020 | 14 | | `relative_susceptibility_30_39` | 1.03 | *S30-39* | Relative susceptibility to infection, aged 30-39 | Zhang et al. 2020 | 15 | | `relative_susceptibility_40_49` | 1.03 | *S40-49* | Relative susceptibility to infection, aged 40-49 | Zhang et al. 2020 | 16 | | `relative_susceptibility_50_59` | 1.03 | *S50-59* | Relative susceptibility to infection, aged 50-59 | Zhang et al. 2020 | 17 | | `relative_susceptibility_60_69` | 1.27 | *S60-69* | Relative susceptibility to infection, aged 60-69 | Zhang et al. 2020 | 18 | | `relative_susceptibility_70_79` | 1.52 | *S70-79* | Relative susceptibility to infection, aged 70-79 | Zhang et al. 2020 | 19 | | `relative_susceptibility_80` | 1.52 | *S80* | Relative susceptibility to infection, aged 80+ | Zhang et al. 2020 | 20 | | `relative_transmission_household` | 2 | *Bhome* | Relative infectious rate of household interaction | - | 21 | | `relative_transmission_occupation` | 1 | *Boccupation* | Relative infectious rate of workplace interaction | - | 22 | | `relative_transmission_random` | 1 | *Brandom* | Relative infectious rate of random interaction | - | -------------------------------------------------------------------------------- /src/COVID19/vaccine.py: -------------------------------------------------------------------------------- 1 | """ 2 | Class representing a vaccine 3 | 4 | Created: 27 May 2021 5 | Author: roberthinch 6 | """ 7 | 8 | import covid19, pandas as pd 9 | 10 | class Vaccine: 11 | """ 12 | Vaccine object has information about a specific vaccine 13 | """ 14 | 15 | def __init__(self, model, vaccine_id): 16 | 17 | c_vaccine = covid19.get_vaccine_by_id( model.c_model, vaccine_id ) 18 | self._vaccine_id = vaccine_id 19 | self.c_vaccine = c_vaccine 20 | 21 | def idx(self): 22 | return covid19.vaccine_idx( self.c_vaccine ) 23 | 24 | def full_efficacy(self): 25 | 26 | n_strains = self.n_strains() 27 | c_efficacy = covid19.floatArray(n_strains) 28 | covid19.vaccine_full_efficacy( self.c_vaccine, c_efficacy ) 29 | 30 | efficacy = [0] * n_strains 31 | for idx in range( n_strains ) : 32 | efficacy[idx] = c_efficacy[idx] 33 | 34 | return efficacy 35 | 36 | def symptoms_efficacy(self): 37 | 38 | n_strains = self.n_strains() 39 | c_efficacy = covid19.floatArray(n_strains) 40 | covid19.vaccine_symptoms_efficacy( self.c_vaccine, c_efficacy ) 41 | 42 | efficacy = [0] * n_strains 43 | for idx in range( n_strains ) : 44 | efficacy[idx] = c_efficacy[idx] 45 | 46 | return efficacy 47 | 48 | def severe_efficacy(self): 49 | 50 | n_strains = self.n_strains() 51 | c_efficacy = covid19.floatArray(n_strains) 52 | covid19.vaccine_severe_efficacy( self.c_vaccine, c_efficacy ) 53 | 54 | efficacy = [0] * n_strains 55 | for idx in range( n_strains ) : 56 | efficacy[idx] = c_efficacy[idx] 57 | 58 | return efficacy 59 | 60 | def time_to_protect(self): 61 | return covid19.vaccine_time_to_protect( self.c_vaccine ) 62 | 63 | def vaccine_protection_period(self): 64 | return covid19.vaccine_vaccine_protection_period( self.c_vaccine ) 65 | 66 | def name(self): 67 | return covid19.vaccine_name( self.c_vaccine ) 68 | 69 | def n_strains(self): 70 | return covid19.vaccine_n_strains( self.c_vaccine ) 71 | 72 | def show(self): 73 | print( "idx = " + str( self.idx() ) ) 74 | print( "name = " + self.name() ) 75 | print( "full_efficacy = " + str( self.full_efficacy() ) ) 76 | print( "symptoms_efficacy = " + str( self.symptoms_efficacy() ) ) 77 | print( "severe_efficacy = " + str( self.severe_efficacy() ) ) 78 | print( "time_to_protect = " + str( self.time_to_protect() ) ) 79 | print( "vaccine_protection_period = " + str( self.vaccine_protection_period() ) ) 80 | print( "n_strains = " + str( self.n_strains() ) ) 81 | 82 | 83 | -------------------------------------------------------------------------------- /tests/adapter_covid19/test_corporate_bankruptcy_models.py: -------------------------------------------------------------------------------- 1 | """ 2 | Basic example testing the adaptER-covid19 corporate bankruptcy models 3 | """ 4 | import itertools 5 | import sys 6 | 7 | from adapter_covid19.corporate_bankruptcy import CorporateBankruptcyModel 8 | from adapter_covid19.data_structures import IoGdpState 9 | from adapter_covid19.datasources import Reader 10 | from adapter_covid19.enums import PrimaryInput, Region, Sector, Age 11 | from tests.adapter_covid19.utilities import ( 12 | DATA_PATH, 13 | ALL_UTILISATIONS, 14 | state_from_utilisation, 15 | UTILISATION_NO_COVID_NO_LOCKDOWN, 16 | advance_state, 17 | ) 18 | 19 | sys.path.append("src/adapter_covid19") 20 | 21 | DUMMY_PRIMARY_INPUTS = [ 22 | { 23 | (PrimaryInput.NET_OPERATING_SURPLUS, r, s, a): 100 24 | for r, s, a in itertools.product(Region, Sector, Age) 25 | } 26 | ] 27 | DUMMY_FUSVDS = [ 28 | {s: 1.0 for s in Sector}, 29 | {s: 0.5 for s in Sector}, 30 | {s: 0.01 for s in Sector}, 31 | ] 32 | 33 | 34 | def pytest_generate_tests(metafunc): 35 | if "corporate_bankruptcy_model_cls" in metafunc.fixturenames: 36 | metafunc.parametrize( 37 | "corporate_bankruptcy_model_cls", [CorporateBankruptcyModel] 38 | ) 39 | if "utilisation" in metafunc.fixturenames: 40 | metafunc.parametrize( 41 | # TODO: figure out how to test this properly 42 | "utilisation", 43 | ALL_UTILISATIONS, 44 | ) 45 | if "final_use_shortfall_vs_demand" in metafunc.fixturenames: 46 | metafunc.parametrize("final_use_shortfall_vs_demand", DUMMY_FUSVDS) 47 | if "primary_inputs" in metafunc.fixturenames: 48 | metafunc.parametrize("primary_inputs", DUMMY_PRIMARY_INPUTS) 49 | 50 | 51 | class TestClass: 52 | def test_interface( 53 | self, 54 | corporate_bankruptcy_model_cls, 55 | utilisation, 56 | primary_inputs, 57 | final_use_shortfall_vs_demand, 58 | ): 59 | reader = Reader(DATA_PATH) 60 | state = state_from_utilisation(UTILISATION_NO_COVID_NO_LOCKDOWN) 61 | state.gdp_state = IoGdpState( 62 | primary_inputs=primary_inputs, 63 | final_use_shortfall_vs_demand=final_use_shortfall_vs_demand, 64 | ) 65 | new_state = advance_state(state, utilisation) 66 | new_state.gdp_state = IoGdpState( 67 | primary_inputs=primary_inputs, 68 | final_use_shortfall_vs_demand=final_use_shortfall_vs_demand, 69 | ) 70 | cb_model = corporate_bankruptcy_model_cls() 71 | cb_model.load(reader) 72 | cb_model.simulate(state) 73 | cb_model.simulate(new_state) 74 | for ( 75 | _business_size, 76 | mapping, 77 | ) in new_state.corporate_state.proportion_solvent.items(): 78 | for sector, solvent in mapping.items(): 79 | assert 0 <= solvent <= 1 80 | -------------------------------------------------------------------------------- /man/swig_methods.Rd: -------------------------------------------------------------------------------- 1 | %% This file contains S4 methods are auto-generated by SWIG 2 | %% in R/OpenABMCovid19.R 3 | %% 4 | %% `R CMD check` requires that all user-level methods have, at the 5 | %% very least, an \alias in the documentation. This file contains those 6 | %% \alias commands explains what they do and where they come from. 7 | %% 8 | \name{swig_methods} 9 | \title{SWIG Generated S4 methods} 10 | 11 | %% OpenABMCovid19 types: 12 | \alias{$,_p_directory-method} 13 | \alias{$,_p_doubleArray-method} 14 | \alias{$,_p_edge-method} 15 | \alias{$,_p_event-method} 16 | \alias{$,_p_event_block-method} 17 | \alias{$,_p_event_list-method} 18 | \alias{$,_p_incomplete_gamma_p_params-method} 19 | \alias{$,_p_individual-method} 20 | \alias{$,_p_infection_event-method} 21 | \alias{$,_p_intArray-method} 22 | \alias{$,_p_interaction-method} 23 | \alias{$,_p_interaction_block-method} 24 | \alias{$,_p_longArray-method} 25 | \alias{$,_p_model-method} 26 | \alias{$,_p_network-method} 27 | \alias{$,_p_parameters-method} 28 | \alias{$,_p_shortArray-method} 29 | \alias{$,_p_trace_token-method} 30 | \alias{$,_p_trace_token_block-method} 31 | \alias{$<-,_p_directory-method} 32 | \alias{$<-,_p_edge-method} 33 | \alias{$<-,_p_event-method} 34 | \alias{$<-,_p_event_block-method} 35 | \alias{$<-,_p_event_list-method} 36 | \alias{$<-,_p_incomplete_gamma_p_params-method} 37 | \alias{$<-,_p_individual-method} 38 | \alias{$<-,_p_infection_event-method} 39 | \alias{$<-,_p_interaction-method} 40 | \alias{$<-,_p_interaction_block-method} 41 | \alias{$<-,_p_model-method} 42 | \alias{$<-,_p_network-method} 43 | \alias{$<-,_p_parameters-method} 44 | \alias{$<-,_p_trace_token-method} 45 | \alias{$<-,_p_trace_token_block-method} 46 | \alias{[[<-,_p_directory,character-method} 47 | \alias{[[<-,_p_edge,character-method} 48 | \alias{[[<-,_p_event,character-method} 49 | \alias{[[<-,_p_event_block,character-method} 50 | \alias{[[<-,_p_event_list,character-method} 51 | \alias{[[<-,_p_incomplete_gamma_p_params,character-method} 52 | \alias{[[<-,_p_individual,character-method} 53 | \alias{[[<-,_p_infection_event,character-method} 54 | \alias{[[<-,_p_interaction,character-method} 55 | \alias{[[<-,_p_interaction_block,character-method} 56 | \alias{[[<-,_p_model,character-method} 57 | \alias{[[<-,_p_network,character-method} 58 | \alias{[[<-,_p_parameters,character-method} 59 | \alias{[[<-,_p_trace_token,character-method} 60 | \alias{[[<-,_p_trace_token_block,character-method} 61 | 62 | %% SWIG helper types 63 | \alias{[,ExternalReference-method} 64 | \alias{[<-,ExternalReference-method} 65 | \alias{length,SWIGArray-method} 66 | 67 | \description{ 68 | Extractor and replace methods auto-generated by SWIG. 69 | } 70 | 71 | \section{Classes}{ 72 | Class names prefixes with \code{_p_} are wrappers to their respective 73 | C type in the dynamic library. Example: \R class \code{_p_model} maps 74 | to the C struct \code{model}. 75 | 76 | Classes \code{ExternalReference} and \code{SWIGArray} are helper 77 | classes that are not part of the model framework. 78 | } 79 | -------------------------------------------------------------------------------- /tests/adapter_covid19/test_economics.py: -------------------------------------------------------------------------------- 1 | """ 2 | Basic example testing the adaptER-covid19 economics class 3 | """ 4 | 5 | import sys 6 | 7 | import pytest 8 | 9 | from adapter_covid19.corporate_bankruptcy import ( 10 | CorporateBankruptcyModel, 11 | NaiveCorporateBankruptcyModel, 12 | ) 13 | from adapter_covid19.datasources import Reader 14 | from adapter_covid19.economics import Economics 15 | from adapter_covid19.enums import Region 16 | from adapter_covid19.gdp import PiecewiseLinearCobbDouglasGdpModel 17 | from adapter_covid19.personal_insolvency import PersonalBankruptcyModel 18 | from tests.adapter_covid19.utilities import ( 19 | DATA_PATH, 20 | state_from_utilisation, 21 | UTILISATION_NO_COVID_NO_LOCKDOWN, 22 | advance_state, 23 | ALL_UTILISATIONS, 24 | ) 25 | 26 | sys.path.append("src/adapter_covid19") 27 | 28 | 29 | def pytest_generate_tests(metafunc): 30 | if "gdp_model_cls" in metafunc.fixturenames: 31 | metafunc.parametrize("gdp_model_cls", [PiecewiseLinearCobbDouglasGdpModel]) 32 | if "personal_bankruptcy_model_cls" in metafunc.fixturenames: 33 | metafunc.parametrize("personal_bankruptcy_model_cls", [PersonalBankruptcyModel]) 34 | if "corporate_bankruptcy_model_cls" in metafunc.fixturenames: 35 | metafunc.parametrize( 36 | "corporate_bankruptcy_model_cls", 37 | [NaiveCorporateBankruptcyModel, CorporateBankruptcyModel], 38 | ) 39 | if "utilisation" in metafunc.fixturenames: 40 | metafunc.parametrize("utilisation", ALL_UTILISATIONS) 41 | 42 | 43 | class TestClass: 44 | # Unfortunately pytest can only check for warnings in stdlib 45 | # Otherwise we should parameterise with scipy.optimize.OptimizeWarning 46 | @pytest.mark.filterwarnings("ignore:.*:Warning:adapter_covid19.gdp") 47 | def test_interface( 48 | self, 49 | gdp_model_cls, 50 | personal_bankruptcy_model_cls, 51 | corporate_bankruptcy_model_cls, 52 | utilisation, 53 | ): 54 | reader = Reader(DATA_PATH) 55 | state = state_from_utilisation(UTILISATION_NO_COVID_NO_LOCKDOWN) 56 | econ_model = Economics( 57 | gdp_model_cls(), 58 | corporate_bankruptcy_model_cls(), 59 | personal_bankruptcy_model_cls(), 60 | ) 61 | econ_model.load(reader) 62 | econ_model.simulate(state) 63 | new_state = advance_state(state, utilisation) 64 | econ_model.simulate(new_state) 65 | # Factor of 1.1 is because of the GDP backbone model 66 | assert ( 67 | 0 68 | <= sum(new_state.gdp_state.gdp.values()) 69 | <= new_state.gdp_state.max_gdp * 1.1 70 | ) 71 | for ( 72 | _business_size, 73 | mapping, 74 | ) in new_state.corporate_state.proportion_solvent.items(): 75 | for sector, solvent in mapping.items(): 76 | assert 0 <= solvent <= 1 77 | for region in Region: 78 | assert 0 <= new_state.personal_state.personal_bankruptcy[region] <= 1 79 | -------------------------------------------------------------------------------- /src/list.c: -------------------------------------------------------------------------------- 1 | /* 2 | * list.c 3 | * 4 | * Created on: 16 Apr 2020 5 | * Author: vuurenk 6 | */ 7 | #include 8 | #include "list.h" 9 | #include "constant.h" 10 | 11 | node* initialise_node( long data ) 12 | { 13 | node* node = malloc(sizeof(node)); 14 | 15 | if (!node) 16 | return NULL; 17 | 18 | node->data = data; 19 | node->next = NULL; 20 | return node; 21 | } 22 | 23 | void initialise_list( list *list ) 24 | { 25 | list->head = NULL; 26 | list->size = 0; 27 | } 28 | 29 | long list_element_at( list* list, int index ) 30 | { 31 | int i = 0; 32 | node* current = list->head; 33 | 34 | while( i < index ) 35 | { 36 | current = current->next; 37 | i++; 38 | } 39 | 40 | return current->data; 41 | } 42 | 43 | void list_push_front( long data, list *list ) 44 | { 45 | node* current = NULL; 46 | 47 | if( list->head == NULL ) 48 | { 49 | list->head = initialise_node( data ); 50 | } 51 | else 52 | { 53 | current = list->head; 54 | list->head = initialise_node( data ); 55 | list->head->next = current; 56 | } 57 | 58 | list->size++; 59 | } 60 | 61 | void list_push_back( long data, list *list ) 62 | { 63 | node *current = NULL; 64 | 65 | if( list->head == NULL ) 66 | list->head = initialise_node( data ); 67 | else 68 | { 69 | current = list->head; 70 | 71 | while (current->next != NULL ) 72 | current = current->next; 73 | 74 | current->next = initialise_node( data ); 75 | } 76 | list->size++; 77 | } 78 | 79 | int list_elem_exists( long data, list *list ) 80 | { 81 | if( list->head == NULL ) 82 | return FALSE; 83 | 84 | node* current = list->head; 85 | 86 | while( current != NULL ) 87 | { 88 | if( current->data == data ) 89 | return TRUE; 90 | 91 | current = current->next; 92 | } 93 | 94 | return FALSE; 95 | } 96 | 97 | long list_pop( list* list ) 98 | { 99 | long retval; 100 | 101 | node* top = list->head; 102 | node* next = NULL; 103 | 104 | if( top == NULL ) 105 | return WAITING_LIST_EMPTY; 106 | 107 | next = top->next; 108 | 109 | retval = top->data; 110 | free( top ); 111 | 112 | list->head = next; 113 | list->size--; 114 | 115 | return retval; 116 | } 117 | 118 | void list_remove_element( long data, list* list ) 119 | { 120 | if( list->head == NULL ) 121 | return; 122 | 123 | node *current = list->head; 124 | node *previous = current; 125 | 126 | while( current != NULL ) 127 | { 128 | if( current->data == data ) 129 | { 130 | previous->next = current->next; 131 | 132 | if( current == list->head ) 133 | list->head = current->next; 134 | 135 | free( current ); 136 | list->size--; 137 | return; 138 | } 139 | previous = current; 140 | current = current->next; 141 | } 142 | } 143 | 144 | void destroy_list( list* list ) 145 | { 146 | node* current = list->head; 147 | node* next = NULL; 148 | 149 | while( current != NULL ) 150 | { 151 | next = current->next; 152 | free( current ); 153 | current = next; 154 | } 155 | } 156 | -------------------------------------------------------------------------------- /src/network.h: -------------------------------------------------------------------------------- 1 | /* 2 | * structure.h 3 | * 4 | * Created on: 12 Mar 2020 5 | * Author: p-robot 6 | * Description: structures used for the contact network 7 | */ 8 | 9 | #ifndef NETWORK_H_ 10 | #define NETWORK_H_ 11 | 12 | /************************************************************************/ 13 | /******************************* Includes *******************************/ 14 | /************************************************************************/ 15 | 16 | #include "structure.h" 17 | #include "utilities.h" 18 | #include "constant.h" 19 | #include "params.h" 20 | 21 | /************************************************************************/ 22 | /****************************** Structures *****************************/ 23 | /************************************************************************/ 24 | 25 | typedef struct network network; 26 | 27 | struct edge{ 28 | long id1; 29 | long id2; 30 | }; 31 | 32 | struct network{ 33 | edge *edges; // array of edges 34 | long n_edges; // number of edges in the network 35 | long n_vertices; // number of vertices 36 | int type; // the type of network (.e. household/random/occupational) 37 | int skip_hospitalised; // include the network for hospitalised people 38 | int skip_quarantined; // include the network for quarantined people 39 | double daily_fraction; // fraction of the daily network sampled 40 | int network_id; // unique network ID 41 | char name[INPUT_CHAR_LEN]; // unique name of the network 42 | float transmission_multiplier; // bespoke transmission multiplier for network 43 | float transmission_multiplier_type; // combined network type and bespoke network multiplier 44 | float transmission_multiplier_combined; // combined network type and bespoke network multiplier 45 | 46 | int construction; // method used to construct the network 47 | long opt_n_indiv; // (OPTIONAL) number of distinct individuals on an network 48 | long *opt_pdx_array; // (OPTIONAL) individual index of each person on the network 49 | int *opt_int_array; // (OPTIONAL) an integer associated with each individual 50 | long opt_long; // (OPTIONAL) a long 51 | long *opt_long_array; // (OPTIONAL) an long array 52 | 53 | network *next_network; // pointer to the next network 54 | }; 55 | 56 | /************************************************************************/ 57 | /****************************** Functions *****************************/ 58 | /************************************************************************/ 59 | 60 | network* create_network(long n_total, int type, parameters* ); 61 | void build_watts_strogatz_network( network *, long, double, double, int ); 62 | int check_member_or_self(long , long, long *, long ); 63 | void remove_contact(long *, long , long *); 64 | void add_contact(long *, long , long *); 65 | void relabel_network( network*, long* ); 66 | void destroy_network( network* ); 67 | int update_daily_fraction( network*, double ); 68 | int update_transmission_multiplier( network*, float ); 69 | int update_transmission_multiplier_type( network*, float ); 70 | 71 | #endif /* NETWORK_H_ */ 72 | -------------------------------------------------------------------------------- /examples/multi_run_simulator.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | """Example script to evaluate models over time range with changes from the default parameter set 3 | This example sets different random seeds for each evaulation and collects the data into pandas dataframes 4 | Note that individual traces, interactions etc are not saved due to the large number of them. 5 | This is designed to be run to generate a stochastic window to gain a staticial interpretation of the model 6 | Run the senario once with full output on to enable detailed knowledge of the model 7 | """ 8 | from COVID19.model import Parameters, Model 9 | from tqdm import tqdm, trange 10 | from concurrent.futures import ProcessPoolExecutor 11 | import pandas as pd 12 | import random 13 | from pathlib import Path 14 | 15 | base_path = Path(__file__).parent.absolute() 16 | 17 | BASELINE_PARAMS = base_path.parent / "tests/data/baseline_parameters.csv" 18 | HOUSEHOLDS = base_path.parent / "tests/data/baseline_household_demographics.csv" 19 | OUTPUT_DIR = base_path / "results" 20 | 21 | 22 | def setup_parameters(d: dict = None): 23 | # Set up Parameters 24 | # Override defaults that we pass in input dict 25 | p = Parameters( 26 | input_param_file=str(BASELINE_PARAMS), 27 | param_line_number=1, 28 | output_file_dir=str(OUTPUT_DIR), 29 | input_households=str(HOUSEHOLDS), 30 | read_param_file=True, 31 | ) 32 | if d: 33 | for k, v in d.items(): 34 | p.set_param(k, v) 35 | return p 36 | 37 | 38 | def setup_model(d: dict = None): 39 | params = setup_parameters(d) 40 | params.set_param("sys_write_individual", 0) 41 | model = Model(params) 42 | return model 43 | 44 | 45 | def run_model(d: dict = None): 46 | m = setup_model(d) 47 | results = [] 48 | for _ in trange(100, desc="Model Progress"): 49 | m.one_time_step() 50 | results.append(m.one_time_step_results()) 51 | return pd.DataFrame(results) 52 | 53 | 54 | def run_many_inline(parameter_set_list, n_threads=None, progress_bar=True): 55 | if progress_bar: 56 | progress_monitor = tqdm 57 | else: 58 | progress_monitor = lambda x: x 59 | 60 | with ProcessPoolExecutor(n_threads) as ex: 61 | outputs = list( 62 | progress_monitor( 63 | ex.map(run_model, parameter_set_list), 64 | total=len(parameter_set_list), 65 | desc="Batch progress" 66 | ) 67 | ) 68 | return outputs 69 | 70 | 71 | if __name__ == "__main__": 72 | 73 | print(BASELINE_PARAMS, HOUSEHOLDS) 74 | # Edit so we only run over 100k people, default is 1m but 10x speed increase for testing. 75 | # Remove n_total setting to run over larger population. 76 | params_list = [ 77 | {"rng_seed": random.randint(0, 2 ** 32 - 1), "n_total": 100000} 78 | for _ in range(100) 79 | ] 80 | 81 | results_dataframes = run_many_inline(params_list, n_threads=4) 82 | 83 | # Ouput individual dataframes as CSVs 84 | for p, df in zip(params_list, results_dataframes): 85 | df.to_csv(OUTPUT_DIR / f"model_rng_seed_{p['rng_seed']}.csv", index=False) 86 | -------------------------------------------------------------------------------- /src/hospital.h: -------------------------------------------------------------------------------- 1 | /* 2 | * hospital.h 3 | * 4 | * Created on: 30 Mar 2020 5 | * Author: vuurenk 6 | */ 7 | 8 | #ifndef HOSPITAL_H_ 9 | #define HOSPITAL_H_ 10 | 11 | /************************************************************************/ 12 | /******************************* Includes *******************************/ 13 | /************************************************************************/ 14 | #include "structure.h" 15 | #include "utilities.h" 16 | #include "constant.h" 17 | #include "params.h" 18 | #include "network.h" 19 | #include "individual.h" 20 | #include "ward.h" 21 | #include "list.h" 22 | 23 | /************************************************************************/ 24 | /****************************** Structures *****************************/ 25 | /************************************************************************/ 26 | 27 | typedef struct hospital hospital; 28 | 29 | struct hospital 30 | { 31 | int hospital_idx; 32 | int n_workers[N_WORKER_TYPES]; 33 | 34 | network *hospital_workplace_network; 35 | 36 | list *waiting_list[N_HOSPITAL_WARD_TYPES]; 37 | int n_wards[N_HOSPITAL_WARD_TYPES]; 38 | ward **wards; 39 | }; 40 | 41 | /************************************************************************/ 42 | /****************************** Functions *****************************/ 43 | /************************************************************************/ 44 | 45 | void initialise_hospital( hospital*, parameters*, int ); 46 | void set_up_hospital_networks(model *pmodel); 47 | void rebuild_healthcare_worker_patient_networks( model *pmodel, hospital *phospital ); 48 | void add_hospital_network_interactions(model *pmodel, hospital *phospital); 49 | int healthcare_worker_working(individual* indiv); 50 | void destroy_hospital( hospital* ); 51 | 52 | void transition_one_hospital_event( model *pmodel, individual *indiv, int from, int to, int edge ); 53 | 54 | void transition_to_waiting( model *pmodel, individual *indiv ); 55 | void transition_to_general( model *pmodel, individual *indiv ); 56 | void transition_to_icu( model *pmodel, individual *indiv ); 57 | void transition_to_mortuary( model *pmodel, individual *indiv ); 58 | void transition_to_discharged( model *pmodel, individual *indiv ); 59 | 60 | void add_healthcare_worker_to_hospital(hospital *phospital, individual *indiv, int type); 61 | int add_patient_to_hospital( model* pmodel, individual *indiv, int required_ward ); 62 | void release_patient_from_hospital( individual *indiv, hospital *phospital ); 63 | void add_patient_to_waiting_list( individual *indiv, hospital *phospital, int ward_type); 64 | 65 | void hospital_waiting_list_transition_scheduler( model *pmodel, int disease_state ); 66 | void swap_waiting_general_and_icu_patients( model *pmodel ); 67 | void predict_patient_disease_progression(model *pmodel, individual *indiv, double patient_waiting_modifier, int type ); 68 | 69 | void remove_if_in_waiting_list( individual *indiv, hospital *phospital ); 70 | int hospital_available_beds( hospital *phospital, int ward_type ); 71 | int find_least_full_hospital(model* pmodel, int required_ward); 72 | 73 | int individual_eligible_to_become_healthcare_worker( individual *indiv ); 74 | 75 | #endif /* HOSPITAL_H_ */ 76 | -------------------------------------------------------------------------------- /python/create_output_file_dictionaries.py: -------------------------------------------------------------------------------- 1 | import sys 2 | from os.path import join 3 | import pandas as pd, numpy as np 4 | 5 | def create_markdown_from_df(df, title = "", include_file_type = False): 6 | """ 7 | Create text string of markdown table from pandas dataframe of OpenABM-Covid19 parameters 8 | Used in automated creation of markdown tables for model documentation. 9 | 10 | Arguments 11 | --------- 12 | df : pandas.DataFrame 13 | Dataframe of OpenABM-Covid19 parameters (transposed), with rows as columns 14 | and with the following columns: 15 | - Name (str, parameter name) 16 | - Description (str, description of the parameter) 17 | title : str 18 | Title string for header of the markdown file 19 | 20 | Returns 21 | ------- 22 | str of markdown table of the form: 23 | 24 | | Name | .... | Source | 25 | | ---- | ---- | ---- | 26 | | 11 | .... | 1N | 27 | | 21 | .... | 2N | 28 | | .... | .... | .... | 29 | | M1 | .... | MN | 30 | """ 31 | NCOLS = df.shape[1] 32 | 33 | title_text = ["# " + title] 34 | header = ["| " + " | ".join(df.columns) + " | "] 35 | hline = ["| " + "".join([" ---- |" for i in range(NCOLS)])] 36 | 37 | table_body = list() 38 | for i, row in df.iterrows(): 39 | 40 | if include_file_type: 41 | table_row = "| `{}` | {} | {} |".format( 42 | row["Column name"], 43 | row.Description, 44 | row["File type"]) 45 | else: 46 | table_row = "| `{}` | {} |".format( 47 | row["Column name"], 48 | row.Description) 49 | 50 | table_body.append(table_row) 51 | 52 | output = title_text + header + hline + table_body 53 | 54 | return("\n".join(output)) 55 | 56 | 57 | if __name__ == "__main__": 58 | 59 | # Parse command line arguments 60 | if len(sys.argv) > 1: 61 | output_file_csv = sys.argv[1] 62 | else: 63 | output_file_csv = join("documentation", "output_files", "output_file_dictionary.csv") 64 | 65 | if len(sys.argv) > 2: 66 | wide_parameter_file = sys.argv[2] 67 | else: 68 | wide_parameter_file = join("tests", "data", "baseline_parameters.csv") 69 | 70 | df = pd.read_csv(output_file_csv, dtype = str) 71 | 72 | # Generate markdown tables for each output file type (first strip on white space) 73 | parameter_types = df["File type"].dropna().str.strip().unique() 74 | 75 | for t in parameter_types: 76 | df_type = df.loc[df["File type"] == t] 77 | df_type = df_type.replace(np.nan, "-").drop(columns = ["File type"]) 78 | markdown_table = create_markdown_from_df(df_type, title = "Table: " + t) 79 | 80 | markdown_file = t.lower().replace(" ", "_") + '.md' 81 | with open(join("documentation", "output_files", markdown_file), 'w') as f: 82 | f.write(markdown_table) 83 | 84 | # Generate table for all parameters 85 | df_all = df.replace(np.nan, "-") 86 | markdown_table = create_markdown_from_df(df_all, title = "Table: Output file dictionary", include_file_type = True) 87 | 88 | with open(join("documentation", "output_files", "output_file_dictionary.md"), 'w') as f: 89 | f.write(markdown_table) 90 | 91 | -------------------------------------------------------------------------------- /DESCRIPTION: -------------------------------------------------------------------------------- 1 | Package: OpenABMCovid19 2 | Version: 1.0 3 | Title: Agent-Based Model for Modelling the COVID-19 4 | License: GPL-3 5 | Authors@R: 6 | c( 7 | person("Oli", "Legat", role = c("cre"), email = "olegat@google.com"), 8 | person("Robert", "Hinch", role = "aut", email = "robert.hinch@gmail.com"), 9 | person("Will", "Probert", role = "aut", comment = "a.k.a. p-robot", email = "will@probert.co.nz"), 10 | person("Ana", "Bulas Cruz", role = "ctb"), 11 | person("Andrea", "Stewart", role = "ctb"), 12 | person("Anel", "Nurtay", role = "ctb"), 13 | person("Anthony", "Finkelstein", role = "ctb"), 14 | person("Ares", "Meroueh", role = "ctb"), 15 | person("Arun", "Lobo", role = "ctb"), 16 | person("Bryn", "Mathias", role = "ctb"), 17 | person("Chris", "Wymant", role = "ctb"), 18 | person("Christophe", "Fraser", role = "ctb"), 19 | person("Daniel", "Montero", role = "ctb"), 20 | person("David", "G Bonsall", role = "ctb"), 21 | person("Evan", "Blumgart", role = "ctb"), 22 | person("Feifan", "Chen", role = "ctb"), 23 | person("Felix", "Breuer", role = "ctb"), 24 | person("James", "Lockyer", role = "ctb"), 25 | person("James", "Warren", role = "ctb"), 26 | person("Katrina", "Lythgoe", role = "ctb"), 27 | person("Kelvin", "Van Vurren", role = "ctb"), 28 | person("Lele", "Zhao", role = "ctb"), 29 | person("Luca", "Ferretti", role = "ctb"), 30 | person("Lucie", "Abeler-Dörner", role = "ctb"), 31 | person("Matthew", "Abueg", role = "ctb"), 32 | person("Matthew", "Hall", role = "ctb"), 33 | person("Michelle", "Kendall", role = "ctb"), 34 | person("Neo", "Wu", role = "ctb"), 35 | person("Nicole", "Mather", role = "ctb"), 36 | person("Ross", "Boylan", role = "ctb"), 37 | person("Scott", "Stevenson", role = "ctb"), 38 | person("Setrak", "Balian", role = "ctb"), 39 | person("feldnerd", "", role = "ctb", email = "56836977+feldnerd@users.noreply.github.com"), 40 | person("mattea", "", role = "ctb", email = "mattea@users.noreply.github.com"), 41 | person("neozwu", "", role = "ctb", email = "neozwu@users.noreply.github.com"), 42 | person("rory-improbable", "", role = "ctb", email = "rory@improbable.io")) 43 | Description: 44 | OpenABM-Covid19 is an agent-based model (ABM) developed to simulate the spread 45 | of COVID-19 in a city and to analyse the effect of both passive and active 46 | intervention strategies. Interactions between individuals are modelled on 47 | networks representing households, work-places and random contacts. The 48 | infection is transmitted between these contacts and the progression of the 49 | disease in individuals is modelled. Instantaneous contract-tracing and 50 | quarantining of contacts is modelled allowing the evaluation of the design and 51 | configuration of digital contract-tracing mobile phone apps. 52 | Robert Hinch, William J M Probert, et al. (2020) . 53 | Depends: R6 54 | Imports: methods, isnullptr, parallel, data.table, plotly, stringr 55 | Collate: 56 | OpenABMCovid19.R 57 | util.R 58 | Parameters.R 59 | Network.R 60 | Model.R 61 | MetaModel.R 62 | Simulation.R 63 | Strain.R 64 | swig_pointer_classes.R 65 | VaccineSchedule.R 66 | Vaccine.R 67 | Suggests: 68 | testthat 69 | RoxygenNote: 7.1.1 70 | Encoding: UTF-8 71 | SystemRequirements: GNU GSL 72 | Biarch: FALSE 73 | -------------------------------------------------------------------------------- /examples/example_run_simulation_with_lockdown.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | 4 | """ 5 | This example sets up a model from the base line parameters, sets a random rng seed 6 | and then runs for 50 days, after 50 days lock down is turned on 7 | """ 8 | 9 | 10 | from COVID19.model import Parameters, Model, OccupationNetworkEnum 11 | import json 12 | from pathlib import Path 13 | from random import randint 14 | from tqdm import trange 15 | from typing import Dict, Any 16 | import os 17 | 18 | import logging 19 | import pandas as pd 20 | 21 | LOGGER = logging.getLogger(__name__) 22 | 23 | base_path = Path(__file__).parent.absolute() 24 | 25 | BASELINE_PARAMS = base_path.parent / "tests/data/baseline_parameters.csv" 26 | HOUSEHOLDS = base_path.parent / "tests/data/baseline_household_demographics.csv" 27 | OUTPUT_DIR = base_path / "results" 28 | 29 | 30 | def setup_params(updated_params: Dict[str, Any] = None): 31 | """[summary] 32 | set up a parameters object from the baseline file 33 | Customise any parameters supplied in the updated params dict 34 | Keyword Arguments: 35 | updated_params {dict} -- [description] (default: None) 36 | Returns: 37 | Parameter set 38 | """ 39 | p = Parameters( 40 | input_param_file=os.fspath(BASELINE_PARAMS), 41 | output_file_dir=os.fspath(OUTPUT_DIR), 42 | param_line_number=1, 43 | input_households=os.fspath(HOUSEHOLDS), 44 | read_param_file=True, 45 | ) 46 | 47 | if updated_params: 48 | for k, v in updated_params.items(): 49 | p.set_param(k, v) 50 | return p 51 | 52 | 53 | def run_model(param_updates, n_steps=200, lockdown_at=None): 54 | params = setup_params(param_updates) 55 | # Create an instance of the Model 56 | model = Model(params) 57 | m_out = [] 58 | for step in trange(n_steps): 59 | # Evaluate each step and save the results 60 | model.one_time_step() 61 | # LOGGER.info( 62 | # model.one_time_step_results() 63 | # ) # If we want to see the results as we go uncomment this block 64 | m_out.append(model.one_time_step_results()) 65 | if lockdown_at: 66 | if step == lockdown_at: 67 | model.update_running_params("lockdown_on", 1) 68 | LOGGER.info(f"turning on lock down at step {step}") 69 | LOGGER.info( 70 | f'lockdown_house_interaction_multiplier = {params.get_param("lockdown_house_interaction_multiplier")}' 71 | ) 72 | LOGGER.info( 73 | f'lockdown_random_network_multiplier = {params.get_param("lockdown_random_network_multiplier")}' 74 | ) 75 | for oc_net in OccupationNetworkEnum: 76 | LOGGER.info( 77 | f'lockdown_occupation_multiplier{oc_net.name} = {params.get_param(f"lockdown_occupation_multiplier{oc_net.name}")}' 78 | ) 79 | df = pd.DataFrame(m_out) 80 | model.write_output_files() 81 | return df 82 | 83 | 84 | 85 | if __name__ == "__main__": 86 | logging.basicConfig(level=logging.INFO) 87 | param_updates = {"rng_seed": randint(0, 65000)} 88 | df = run_model(param_updates=param_updates, n_steps=200, lockdown_at=50) 89 | df.to_csv("results/covid_timeseries_Run1.csv", index=False) 90 | print(df) 91 | 92 | -------------------------------------------------------------------------------- /tests/adapter_covid19/utilities.py: -------------------------------------------------------------------------------- 1 | import copy 2 | import itertools 3 | 4 | from adapter_covid19.data_structures import SimulateState, Utilisation, Utilisations 5 | from adapter_covid19.datasources import Reader 6 | from adapter_covid19.enums import Region, Sector, Age, WorkerState 7 | 8 | DATA_PATH = "tests/adapter_covid19/data" 9 | 10 | ILL_STATES = { 11 | WorkerState.ILL_UNEMPLOYED, 12 | WorkerState.ILL_FURLOUGHED, 13 | WorkerState.ILL_WFH, 14 | WorkerState.ILL_WFO, 15 | } 16 | 17 | UTILISATION_NO_COVID_NO_LOCKDOWN = Utilisation( 18 | p_dead=0, 19 | p_ill_wfo=0, 20 | p_ill_wfh=0, 21 | p_ill_furloughed=0, 22 | p_ill_unemployed=0, 23 | p_wfh=0, 24 | p_furloughed=0, 25 | ) 26 | UTILISATION_COVID_NO_LOCKDOWN = Utilisation( 27 | p_dead=0.1, 28 | p_ill_wfo=0.5, 29 | p_ill_wfh=0.5, 30 | p_ill_furloughed=0.5, 31 | p_ill_unemployed=0.5, 32 | p_wfh=0.0, 33 | p_furloughed=0.0, 34 | ) 35 | UTILISATION_NO_COVID_LOCKDOWN = Utilisation( 36 | p_dead=0.0, 37 | p_ill_wfo=0.0, 38 | p_ill_wfh=0.0, 39 | p_ill_furloughed=0.0, 40 | p_ill_unemployed=0.0, 41 | p_wfh=0.9, 42 | p_furloughed=1.0, 43 | p_not_employed=0.1, 44 | ) 45 | UTILISATION_COVID_LOCKDOWN = Utilisation( 46 | p_dead=0.0001, 47 | p_ill_wfo=0.01, 48 | p_ill_wfh=0.01, 49 | p_ill_furloughed=0.01, 50 | p_ill_unemployed=0.01, 51 | p_wfh=0.9, 52 | p_furloughed=1.0, 53 | p_not_employed=0.1, 54 | ) 55 | 56 | ALL_UTILISATIONS = ( 57 | UTILISATION_NO_COVID_NO_LOCKDOWN, 58 | UTILISATION_COVID_NO_LOCKDOWN, 59 | UTILISATION_NO_COVID_LOCKDOWN, 60 | UTILISATION_COVID_LOCKDOWN, 61 | ) 62 | 63 | 64 | def state_from_utilisation( 65 | utilisation: Utilisation, 66 | new_spending_day: int = 10 ** 6, 67 | ccff_day: int = 10 ** 6, 68 | loan_guarantee_day: int = 10 ** 6, 69 | ) -> SimulateState: 70 | reader = Reader(DATA_PATH) 71 | utilisations = Utilisations( 72 | {k: copy.deepcopy(utilisation) for k in itertools.product(Region, Sector, Age)}, 73 | reader=reader, 74 | ) 75 | lambdas = utilisation.to_lambdas() 76 | ill = sum(v for k, v in lambdas.items() if k in ILL_STATES) 77 | dead = lambdas[WorkerState.DEAD] 78 | # Placeholder until quarantine is integrated from the covid model 79 | quarantine = 0 80 | ill, dead, quarantine, p_wfh = [ 81 | {k: x for k in itertools.product(Region, Sector, Age)} 82 | for x in [ill, dead, quarantine, utilisation.p_wfh] 83 | ] 84 | state = SimulateState( 85 | time=0, 86 | dead=dead, 87 | ill=ill, 88 | quarantine=quarantine, 89 | p_wfh=p_wfh, 90 | lockdown=float(utilisation.p_wfh > 0), 91 | furlough=utilisation.p_furloughed > 0, 92 | new_spending_day=new_spending_day, 93 | ccff_day=ccff_day, 94 | loan_guarantee_day=loan_guarantee_day, 95 | fear_factor_coef_lockdown=1.0, 96 | fear_factor_coef_ill=1.0, 97 | fear_factor_coef_dead=1.0, 98 | utilisations=utilisations, 99 | ) 100 | return state 101 | 102 | 103 | def advance_state(state: SimulateState, utilisation: Utilisation,) -> SimulateState: 104 | new_state = state_from_utilisation( 105 | utilisation, state.new_spending_day, state.ccff_day, state.loan_guarantee_day, 106 | ) 107 | new_state.time = state.time + 1 108 | new_state.previous = state 109 | return new_state 110 | -------------------------------------------------------------------------------- /documentation/output_files/transmission_file.md: -------------------------------------------------------------------------------- 1 | # Table: transmission file 2 | | Column name | Description | 3 | | ---- | ---- | 4 | | `ID_recipient` | Unique identifier of the recipient | 5 | | `age_group_recipient` | Age group of the recipient (coded by the `enum AGE_GROUPS` in constant.h) | 6 | | `house_no_recipient` | Household identifier of the recipient | 7 | | `occupation_network_recipient` | Occupation network of the recipient (coded by the `enum OCCUPATION_NETWORKS` within constant.h) | 8 | | `worker_type_recipient` | Type of hospital worker of the recipient (coded by the `enum WORKER_TYPES` in constant.h) (default -1) | 9 | | `hospital_state_recipient` | Hospital status of the recipient at time of transmission (coded by `enum EVENT_TYPES` in constant.h) (default NOT_IN_HOSPITAL) | 10 | | `infector_network` | Network within which the transmission took place (coded by the `enum INTERACTION_TYPE` within constant.h) | 11 | | `generation_time` | Generation time of this transmission event (days; time from infection of the source to transmission) (0 for seed cases) | 12 | | `ID_source` | Unique identifier of the source (same as ID_recipient for seed cases) | 13 | | `age_group_source` | Age group of the source (coded by the `enum AGE_GROUPS` in constant.h) | 14 | | `house_no_source` | Household identifier of the source | 15 | | `occupation_network_source` | Occupation network of the source (coded by the `enum OCCUPATION_NETWORKS` within constant.h) | 16 | | `worker_type_source` | Type of hospital worker of the source at time of transmission (coded by the `enum WORKER_TYPES` in constant.h) (default -1) | 17 | | `hospital_state_source` | Hospital status of the source (coded by `enum EVENT_TYPES` in constant.h) (default NOT_IN_HOSPITAL) | 18 | | `time_infected_source` | Time when source was infected | 19 | | `status_source` | Infectious status of the source at time of transmission (coded by `enum EVENT_TYPES` within constant.h) | 20 | | `time_infected` | Time at which transmission took place (time measured as day number of the simulation) | 21 | | `time_presymptomatic` | Time at which the recipient became presymptomatic (-1 if never) | 22 | | `time_presymptomatic_mild` | Time at which the recipient became presymptomatic (if mildly infected) (-1 if never) | 23 | | `time_presymptomatic_severe` | Time at which the recipient became presymptomatic (if severely infected) (-1 if never) | 24 | | `time_symptomatic` | Time at which the recipient became symptomatic (-1 if never) | 25 | | `time_symptomatic_mild` | Time at which the recipient became symptomatic (if mildly infected) (-1 if never) | 26 | | `time_symptomatic_severe` | Time at which the recipient became symptomatic (if severely infected) (-1 if never) | 27 | | `time_asymptomatic` | Time at which the recipient became asymptomatic (-1 if never) | 28 | | `time_hospitalised` | Time at which the recipient became hospitalised (-1 if never) | 29 | | `time_critical` | Time at which the recipient became critical (-1 if never) | 30 | | `time_hospitalised_recovering` | Time at which the recipient was discharged from critical but remained in hospital (-1 if never) | 31 | | `time_death` | Time at which the recipient died (-1 if never) | 32 | | `time_recovered` | Time at which the recipient recovered (-1 if never) | 33 | | `time_susceptible` | Time at which the recipient became susceptible again (if waning immunity is possible) | 34 | | `is_case` | Was the recipient identified as a case (positive test result) (1=Yes; 0=No) | 35 | | `strain_multiplier` | The relative transmissibility of the strain of the infector (1.0 = default transmissibility) | 36 | -------------------------------------------------------------------------------- /src/COVID19/network.py: -------------------------------------------------------------------------------- 1 | """ 2 | Class representing a network in the simulation 3 | 4 | Created: 15 October 2020 5 | Author: roberthinch 6 | """ 7 | 8 | import covid19, pandas as pd 9 | 10 | class Network: 11 | """ 12 | Network object has information about a specific network 13 | """ 14 | 15 | def __init__(self, model, network_id): 16 | 17 | c_network = covid19.get_network_by_id( model.c_model, network_id ) 18 | self._network_id = network_id 19 | self.c_network = c_network 20 | 21 | def n_edges(self): 22 | return covid19.network_n_edges( self.c_network ) 23 | 24 | def n_vertices(self): 25 | return covid19.network_n_vertices( self.c_network ) 26 | 27 | def name(self): 28 | return covid19.network_name( self.c_network ) 29 | 30 | def network_id(self): 31 | return self._network_id 32 | 33 | def skip_hospitalised(self): 34 | return covid19.network_skip_hospitalised( self.c_network ) 35 | 36 | def skip_quarantined(self): 37 | return covid19.network_skip_quarantined( self.c_network ) 38 | 39 | def type(self): 40 | return covid19.network_type( self.c_network ) 41 | 42 | def daily_fraction(self): 43 | return covid19.network_daily_fraction( self.c_network ) 44 | 45 | def update_daily_fraction(self,daily_fraction): 46 | return covid19.update_daily_fraction(self.c_network,daily_fraction) 47 | 48 | def set_network_transmission_multiplier(self,multiplier): 49 | covid19.set_network_transmission_multiplier(self.c_network,multiplier) 50 | 51 | def transmission_multiplier(self): 52 | return self.c_network.transmission_multiplier 53 | 54 | def transmission_multiplier_type(self): 55 | return self.c_network.transmission_multiplier_type 56 | 57 | def transmission_multiplier_combined(self): 58 | return self.c_network.transmission_multiplier_combined 59 | 60 | def show(self): 61 | print( "network_id = " + str( self.network_id() ) ) 62 | print( "name = " + self.name() ) 63 | print( "n_edges = " + str( self.n_edges() ) ) 64 | print( "n_vertices = " + str( self.n_vertices() ) ) 65 | print( "skip_hospitalised = " + str( self.skip_hospitalised() ) ) 66 | print( "skip_quarantined = " + str( self.skip_quarantined() ) ) 67 | print( "type = " + str( self.type() ) ) 68 | print( "daily_fraction = " + str( self.daily_fraction() ) ) 69 | print( "transmission_mult = " + str( self.transmission_multiplier() ) ) 70 | print( "transmission_mult_type = " + str( self.transmission_multiplier_type() ) ) 71 | print( "transmission_mult_comb = " + str( self.transmission_multiplier_combined() ) ) 72 | 73 | def get_network(self): 74 | """Return pandas.DataFrame of the network""" 75 | n_edges = self.n_edges() 76 | id1 = covid19.longArray(n_edges) 77 | id2 = covid19.longArray(n_edges) 78 | 79 | return_status = covid19.get_network(self.c_network, id1, id2) 80 | 81 | list_id1 = [None]*n_edges 82 | list_id2 = [None]*n_edges 83 | 84 | for idx in range(n_edges): 85 | list_id1[idx] = id1[idx] 86 | list_id2[idx] = id2[idx] 87 | 88 | df_network = pd.DataFrame( { 89 | 'ID1': list_id1, 90 | 'ID2': list_id2 91 | }) 92 | 93 | return df_network 94 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # For use of the icc compiler 2 | ifeq ($(compiler),icc) 3 | C = icc 4 | else 5 | C = gcc 6 | endif 7 | 8 | ifeq (Windows_NT, $(OS)) 9 | WHICH=where 10 | else 11 | WHICH=which 12 | endif 13 | 14 | 15 | ifeq (, $(shell $(WHICH) python3)) 16 | PYTHON = python 17 | else 18 | PYTHON = python3 19 | endif 20 | 21 | PIP_FLAGS := --upgrade 22 | 23 | # The 'in-place' flag is different on macOS (BSD) and Linux/minGW. 24 | # https://linux.die.net/man/1/sed 25 | # https://www.freebsd.org/cgi/man.cgi?query=sed&sektion=&n=1 26 | # Note on Windows: install Rtools for 'sed' 27 | ifeq ($(shell uname),Darwin) 28 | SED_I=sed -i '' 29 | else 30 | SED_I=sed -i 31 | endif 32 | 33 | OBJS = src/utilities.o src/constant.o src/demographics.o src/params.o src/model.o src/individual.o src/main.o src/input.o src/network.o src/disease.o src/interventions.o src/hospital.o src/doctor.o src/nurse.o src/ward.o src/list.o src/strain.o 34 | 35 | GSLFLAGS= -lgsl -lgslcblas -lm -O3 36 | LFLAGS = $(GSLFLAGS) 37 | 38 | # Name of executable 39 | _EXE = src/covid19ibm.exe 40 | EXE = $(_EXE) 41 | 42 | INC = /usr/local/include 43 | LIB = /usr/local/lib 44 | 45 | # Compilation options and libraries to be used 46 | CFLAGS = -g -Wall -fmessage-length=0 -I$(INC) $(shell gsl-config --cflags) -O0 47 | LDFLAGS = -L$(LIB) $(shell gsl-config --libs) 48 | 49 | # Swig's input 50 | SWIG_INPUT = src/disease.h src/ward.h src/nurse.h src/network_utils.i src/vaccine_utils.i src/strain_utils.i src/input.h src/individual.h src/hospital.h src/params.h src/structure.h src/constant.h src/doctor.h src/utilities.h src/model_utils.i src/covid19.i src/list.h src/network.h src/model.h src/interventions.h src/params_utils.i src/demographics.h src/strain.h 51 | 52 | # Swig's output 53 | SWIG_OUTPUT_PY = src/covid19_wrap.o src/covid19_wrap.c src/covid19.py src/_covid19.cpython-37m-darwin.so src/build src/covid19.egg-info 54 | SWIG_OUTPUT_R = src/covid19_wrap_R.c src/covid19_wrap_R.o R/OpenABMCovid19.R src/OpenABMCovid19.so 55 | SWIG_OUTPUT = $(SWIG_OUTPUT_PY) $(SWIG_OUTPUT_R) 56 | 57 | ifndef SWIG3 58 | SWIG3 = swig 59 | endif 60 | 61 | # Roxygen generated files 62 | ROXYGEN_OUTPUT= man/SAFE_UPDATE_PARAMS.Rd man/Parameters.Rd man/Environment.Rd man/Network.Rd man/Agent.Rd man/VaccineSchedule.Rd man/VACCINE_STATUS.Rd man/Model.Rd man/AgeGroupEnum.Rd man/NETWORK_CONSTRUCTIONS.Rd man/COVID19IBM.Rd man/VACCINE_TYPES.Rd man/Simulation.Rd 63 | 64 | # To compile 65 | install: $(OBJS) 66 | install: all; 67 | cd src && swig -python covid19.i 68 | cd src && $(PYTHON) -m pip install $(PIP_FLAGS) . 69 | 70 | dev: PIP_FLAGS += -e 71 | dev: install; 72 | 73 | all: $(OBJS) 74 | $(C) $(LDFLAGS) -o $(EXE) $(OBJS) $(LFLAGS) 75 | 76 | clean: 77 | cd src && $(PYTHON) -m pip uninstall -y covid19 78 | rm -rf $(OBJS) $(EXE) $(SWIG_OUTPUT) $(ROXYGEN_OUTPUT) 79 | 80 | .c.o: 81 | $(C) $(CFLAGS) -c $< -o $@ 82 | 83 | # Generating swig3 source for R bindings (and post-processing) 84 | R/OpenABMCovid19.R: $(SWIG_INPUT) 85 | $(SWIG3) -r -Isrc -o src/covid19_wrap_R.c -outdir R src/covid19.i 86 | # edit generated C source to mute R check note. 87 | $(SED_I) 's/R_registerRoutines/R_useDynamicSymbols(dll,0);R_registerRoutines/' src/covid19_wrap_R.c 88 | # edit generated src lines are cause R check warnings. 89 | $(SED_I) 's/.Call("R_SWIG_debug_getCallbackFunctionData"/.Call("R_SWIG_debug_getCallbackFunctionData", PACKAGE="OpenABMCovid19"/' R/OpenABMCovid19.R 90 | $(SED_I) 's/.Call("R_SWIG_R_pushCallbackFunctionData"/.Call("R_SWIG_R_pushCallbackFunctionData", PACKAGE="OpenABMCovid19"/' R/OpenABMCovid19.R 91 | # edit generated src that causes errors like: "p_char" is not a defined class 92 | $(SED_I) 's/ans <- new("_p_char"/#ans <- new("_p_char"/' R/OpenABMCovid19.R 93 | src/covid19_wrap_R.c: R/OpenABMCovid19.R 94 | Rswig: R/OpenABMCovid19.R 95 | 96 | .PHONY: install dev all clean Rswig 97 | -------------------------------------------------------------------------------- /R/Vaccine.R: -------------------------------------------------------------------------------- 1 | #' R6Class Vaccine 2 | #' 3 | #' @description 4 | #' Vaccine object has information about each new vaccine 5 | #' 6 | #' @examples 7 | #' # Add new vaccine 8 | #' vaccine = model$add_new_vaccine( ) 9 | #' 10 | Vaccine <- R6Class( classname = 'Vaccine', cloneable = FALSE, 11 | 12 | private = list( 13 | #' the vaccine ID 14 | id = NULL, 15 | 16 | #' .c_vaccine External pointer, reference to \code{vaccine} C struct. 17 | .c_vaccine = NULL, 18 | 19 | #' the C vaccine R pointer object 20 | c_vaccine_ptr = function() { 21 | return( self$c_vaccine@ref ) 22 | }, 23 | 24 | #' check the C vaccine still exists 25 | c_vaccine_valid = function() { 26 | return( !isnullptr( private$.c_vaccine@ref )) 27 | } 28 | ), 29 | 30 | active = list( 31 | #' @field c_vaccine the C vaccine R pointer object (SWIG wrapped) 32 | c_vaccine = function( val = NULL ) 33 | { 34 | if( is.null( val ) ) 35 | { 36 | if( private$c_vaccine_valid() ) 37 | return( private$.c_vaccine ) 38 | stop( "c_vaccine is no longer valid - create a new vaccine") 39 | } 40 | else 41 | stop( "cannot set c_vaccine" ) 42 | } 43 | ), 44 | 45 | public = list( 46 | 47 | #' @param model R6 Model object 48 | #' @param stain_id The vaccine ID. 49 | initialize = function( model, vaccine_id ) 50 | { 51 | private$id <- vaccine_id 52 | 53 | if( !is.R6(model) ) 54 | stop( "model must be a R6 class of type Model or MetaModel") 55 | 56 | if( inherits( model, "Model") ) { 57 | private$.c_vaccine <- get_vaccine_by_id( model$c_model, vaccine_id ) 58 | } else if( !inherits( model, "MetaModel") ) 59 | stop( "model must be a R6 class of type Model or MetaModel") 60 | }, 61 | 62 | #' @description Wrapper for C API \code{vaccine$idx()}. 63 | #' @return the index of the vaccine 64 | idx = function() { 65 | return( private$id ) 66 | }, 67 | 68 | #' @description Wrapper for C API \code{vaccine$n_strains()}. 69 | #' @return the number of strains the vaccine has efficacy for 70 | n_strain = function() { 71 | return(vaccine_n_strain( self$c_vaccine )) 72 | }, 73 | 74 | #' @description Wrapper for C API \code{vaccine$time_to_protect()}. 75 | #' @return the time_to_protect of the vaccine 76 | time_to_protect = function() { 77 | return(vaccine_time_to_protect( self$c_vaccine )) 78 | }, 79 | 80 | #' @description Wrapper for C API \code{vaccine$vaccine_protection_period()}. 81 | #' @return the vaccine_protection_period of the vaccine 82 | vaccine_protection_period = function() { 83 | return( vaccine_vaccine_protection_period( self$c_vaccine )) 84 | }, 85 | 86 | #' @description Wrapper for C API \code{vaccine$full_efficacy()}. 87 | #' @return the full_efficacy of vaccine by strain 88 | full_efficacy = function() { 89 | return( .Call('R_vaccine_full_efficacy', private$c_vaccine_ptr(), PACKAGE='OpenABMCovid19') ) 90 | }, 91 | 92 | #' @description Wrapper for C API \code{vaccine$symptoms_efficacy()}. 93 | #' @return the symptoms_efficacy of vaccine by strain 94 | symptoms_efficacy = function() { 95 | return( .Call('R_vaccine_symptoms_efficacy', private$c_vaccine_ptr(), PACKAGE='OpenABMCovid19') ) 96 | }, 97 | 98 | #' @description Wrapper for C API \code{vaccine$severe_efficacy()}. 99 | #' @return the severe_efficacy of vaccine by strain 100 | severe_efficacy = function() { 101 | return( .Call('R_vaccine_severe_efficacy', private$c_vaccine_ptr(), PACKAGE='OpenABMCovid19') ) 102 | } 103 | ) 104 | ) 105 | -------------------------------------------------------------------------------- /documentation/output_files/timeseries.md: -------------------------------------------------------------------------------- 1 | # Table: timeseries 2 | | Column name | Description | 3 | | ---- | ---- | 4 | | `time` | Day number of the simulation | 5 | | `lockdown` | Is lockdown currently on (1 = Yes; 0 = No) | 6 | | `test_on_symptoms` | Is testing on symptoms currently on (1=Yes; 0=No) | 7 | | `app_turned_on` | Is the app currently on (1= Yes; 0 = No) | 8 | | `total_infected` | Cumulative infected | 9 | | `total_infected_0_9` | Cumulative infected aged 0-9 years | 10 | | `total_infected_10_19` | Cumulative infected aged 10-19 years | 11 | | `total_infected_20_29` | Cumulative infected aged 20-29 years | 12 | | `total_infected_30_39` | Cumulative infected aged 30-39 years | 13 | | `total_infected_40_49` | Cumulative infected aged 40-49 years | 14 | | `total_infected_50_59` | Cumulative infected aged 50-59 years | 15 | | `total_infected_60_69` | Cumulative infected aged 60-69 years | 16 | | `total_infected_70_79` | Cumulative infected aged 70-79 years | 17 | | `total_infected_80` | Cumulative infected aged 80+ years | 18 | | `total_case` | Cumulative cases (a case is defined by a positive test result) | 19 | | `total_case_0_9` | Cumulative cases aged 0-9 years | 20 | | `total_case_10_19` | Cumulative cases aged 10-19 years | 21 | | `total_case_20_29` | Cumulative cases aged 20-29 years | 22 | | `total_case_30_39` | Cumulative cases aged 30-39 years | 23 | | `total_case_40_49` | Cumulative cases aged 40-49 years | 24 | | `total_case_50_59` | Cumulative cases aged 50-59 years | 25 | | `total_case_60_69` | Cumulative cases aged 60-69 years | 26 | | `total_case_70_79` | Cumulative cases aged 70-79 years | 27 | | `total_case_80` | Cumulative cases aged 80+ years | 28 | | `total_death` | Cumulative deaths (where COVID19 is the primary cause of death) | 29 | | `total_death_0_9` | Cumulative deaths aged 0-9 years | 30 | | `total_death_10_19` | Cumulative deaths aged 10-19 years | 31 | | `total_death_20_29` | Cumulative deaths aged 20-29 years | 32 | | `total_death_30_39` | Cumulative deaths aged 30-39 years | 33 | | `total_death_40_49` | Cumulative deaths aged 40-49 years | 34 | | `total_death_50_59` | Cumulative deaths aged 50-59 years | 35 | | `total_death_60_69` | Cumulative deaths aged 60-69 years | 36 | | `total_death_70_79` | Cumulative deaths aged 70-79 years | 37 | | `total_death_80` | Cumulative deaths aged 80+ years | 38 | | `n_presymptom` | Current number presymptomatic (both mild and severe) | 39 | | `n_asymptom` | Current number asymptomatic | 40 | | `n_quarantine` | Current number in quarantine | 41 | | `n_tests` | Current number of tests reporting results today | 42 | | `n_symptoms` | Current number of symptomatic (both mild and severe) | 43 | | `n_hospital` | Current number in hospital who have not yet required critical care | 44 | | `n_hospitalised_recovering` | Current number in hospital who left critical care but not been discharged | 45 | | `n_critical` | Current number in critical care | 46 | | `n_death` | Daily number of deaths | 47 | | `n_recovered` | Cumulative number recovered | 48 | | `hospital_admissions` | Daily hospital admissions | 49 | | `hospital_admissions_total` | Cumulative hospital admissions | 50 | | `hospital_to_critical_daily` | Daily transitions from hospital to critical | 51 | | `hospital_to_critical_total` | Cumulative transitions from hospital to critical | 52 | | `n_quarantine_infected` | Current number in quarantine that have ever been infected | 53 | | `n_quarantine_recovered` | Current number in quarantine that are recovered | 54 | | `n_quarantine_app_user` | Current number of app users in quarantine | 55 | | `n_quarantine_app_user_infected` | Current number of app users in quarantine that have ever been infected | 56 | | `n_quarantine_app_user_recovered` | Current number of app users in quarantine that are recovered | 57 | | `n_quarantine_events` | Daily number of quarantine events | 58 | | `n_quarantine_release_events` | Daily number of quarantine release events | 59 | | `n_quarantine_events_app_user` | Daily number of quarantine events of app users | 60 | | `n_quarantine_release_events_app_user` | Daily number of quarantine release events of app users | -------------------------------------------------------------------------------- /tests/adapter_covid19/data/wages.csv: -------------------------------------------------------------------------------- 1 | Sector,Age,wages 2 | A_AGRICULTURE,A16,21551.0 3 | A_AGRICULTURE,A18,21551.0 4 | A_AGRICULTURE,A25,21551.0 5 | A_AGRICULTURE,A35,26348.333333333332 6 | A_AGRICULTURE,A50,26137.35900039935 7 | A_AGRICULTURE,A65,28180.0 8 | B_MINING,A16,30303.0 9 | B_MINING,A18,30303.0 10 | B_MINING,A25,37876.96252550847 11 | B_MINING,A35,43644.0 12 | B_MINING,A50,38733.0 13 | B_MINING,A65,38733.0 14 | C_MANUFACTURING,A16,18412.0 15 | C_MANUFACTURING,A18,18412.0 16 | C_MANUFACTURING,A25,28645.268153346646 17 | C_MANUFACTURING,A35,32239.0 18 | C_MANUFACTURING,A50,33051.331806301365 19 | C_MANUFACTURING,A65,29522.0 20 | D_ELECTRICITY,A16,17542.0 21 | D_ELECTRICITY,A18,17542.0 22 | D_ELECTRICITY,A25,36741.53008421665 23 | D_ELECTRICITY,A35,45101.66666666667 24 | D_ELECTRICITY,A50,45083.95002043945 25 | D_ELECTRICITY,A65,33788.0 26 | E_WATER,A16,27715.0 27 | E_WATER,A18,27715.0 28 | E_WATER,A25,32592.324108904737 29 | E_WATER,A35,36022.33333333333 30 | E_WATER,A50,34476.169644298236 31 | E_WATER,A65,31415.0 32 | F_CONSTRUCTION,A16,17519.0 33 | F_CONSTRUCTION,A18,17519.0 34 | F_CONSTRUCTION,A25,33233.39693419256 35 | F_CONSTRUCTION,A35,37372.33333333333 36 | F_CONSTRUCTION,A50,35878.08564742886 37 | F_CONSTRUCTION,A65,34623.0 38 | G_TRADE,A16,16224.0 39 | G_TRADE,A18,16224.0 40 | G_TRADE,A25,25324.844600828186 41 | G_TRADE,A35,28200.0 42 | G_TRADE,A50,25929.669706024226 43 | G_TRADE,A65,23535.0 44 | H_TRANSPORT,A16,18155.0 45 | H_TRANSPORT,A18,18155.0 46 | H_TRANSPORT,A25,29590.23638777557 47 | H_TRANSPORT,A35,32337.999999999996 48 | H_TRANSPORT,A50,31442.627367009896 49 | H_TRANSPORT,A65,29641.0 50 | I_ACCOMODATION,A16,14080.0 51 | I_ACCOMODATION,A18,14080.0 52 | I_ACCOMODATION,A25,22567.608621380157 53 | I_ACCOMODATION,A35,23950.666666666664 54 | I_ACCOMODATION,A50,20925.233068833077 55 | I_ACCOMODATION,A65,19478.0 56 | J_COMMUNICATION,A16,19712.0 57 | J_COMMUNICATION,A18,19712.0 58 | J_COMMUNICATION,A25,37965.12059478999 59 | J_COMMUNICATION,A35,45559.33333333333 60 | J_COMMUNICATION,A50,46513.78161442382 61 | J_COMMUNICATION,A65,41274.0 62 | K_FINANCIAL,A16,19292.0 63 | K_FINANCIAL,A18,19292.0 64 | K_FINANCIAL,A25,40104.03816952961 65 | K_FINANCIAL,A35,50791.666666666664 66 | K_FINANCIAL,A50,48298.312408030135 67 | K_FINANCIAL,A65,43746.0 68 | L_REAL_ESTATE,A16,19064.0 69 | L_REAL_ESTATE,A18,19064.0 70 | L_REAL_ESTATE,A25,29090.7293652315 71 | L_REAL_ESTATE,A35,30701.333333333332 72 | L_REAL_ESTATE,A50,29071.359515680455 73 | L_REAL_ESTATE,A65,27768.0 74 | M_PROFESSIONAL,A16,17891.0 75 | M_PROFESSIONAL,A18,17891.0 76 | M_PROFESSIONAL,A25,37056.94928907557 77 | M_PROFESSIONAL,A35,43809.33333333333 78 | M_PROFESSIONAL,A50,40904.1651864642 79 | M_PROFESSIONAL,A65,34539.0 80 | N_ADMINISTRATIVE,A16,23815.0 81 | N_ADMINISTRATIVE,A18,23815.0 82 | N_ADMINISTRATIVE,A25,27270.35553378082 83 | N_ADMINISTRATIVE,A35,29092.999999999996 84 | N_ADMINISTRATIVE,A50,27443.4313795691 85 | N_ADMINISTRATIVE,A65,24863.0 86 | O_PUBLIC,A16,19775.0 87 | O_PUBLIC,A18,19775.0 88 | O_PUBLIC,A25,32267.21706798217 89 | O_PUBLIC,A35,36653.33333333333 90 | O_PUBLIC,A50,33559.03836643779 91 | O_PUBLIC,A65,28132.0 92 | P_EDUCATION,A16,15169.0 93 | P_EDUCATION,A18,15169.0 94 | P_EDUCATION,A25,31763.25182613012 95 | P_EDUCATION,A35,35007.66666666667 96 | P_EDUCATION,A50,31653.767114470895 97 | P_EDUCATION,A65,28165.0 98 | Q_HEALTH,A16,15377.0 99 | Q_HEALTH,A18,15377.0 100 | Q_HEALTH,A25,26505.288019847874 101 | Q_HEALTH,A35,29218.666666666664 102 | Q_HEALTH,A50,27995.43943818742 103 | Q_HEALTH,A65,23603.0 104 | R_ARTS,A16,16399.0 105 | R_ARTS,A18,16399.0 106 | R_ARTS,A25,25557.02559775625 107 | R_ARTS,A35,29309.666666666668 108 | R_ARTS,A50,26913.436161544832 109 | R_ARTS,A65,25416.0 110 | S_OTHER,A16,21605.0 111 | S_OTHER,A18,21605.0 112 | S_OTHER,A25,26329.528864015014 113 | S_OTHER,A35,28988.666666666668 114 | S_OTHER,A50,28934.92282520231 115 | S_OTHER,A65,26877.0 116 | T_HOUSEHOLD,A16,21764.0 117 | T_HOUSEHOLD,A18,21764.0 118 | T_HOUSEHOLD,A25,21764.0 119 | T_HOUSEHOLD,A35,21763.999999999996 120 | T_HOUSEHOLD,A50,21764.0 121 | T_HOUSEHOLD,A65,21764.0 122 | -------------------------------------------------------------------------------- /src/interventions.h: -------------------------------------------------------------------------------- 1 | /* 2 | * interventions.h 3 | * 4 | * Created on: 18 Mar 2020 5 | * Author: hinchr 6 | */ 7 | 8 | #ifndef INTERVENTIONS_H_ 9 | #define INTERVENTIONS_H_ 10 | 11 | #include "structure.h" 12 | #include "individual.h" 13 | 14 | /************************************************************************/ 15 | /****************************** Structures *****************************/ 16 | /************************************************************************/ 17 | struct trace_token_block{ 18 | trace_token *trace_tokens; 19 | trace_token_block *next; 20 | }; 21 | 22 | struct trace_token{ 23 | individual *individual; 24 | individual *traced_from; 25 | trace_token *next_index; 26 | trace_token *last_index; 27 | trace_token *next; 28 | trace_token *last; 29 | int contact_time; 30 | int index_status; 31 | }; 32 | 33 | struct vaccine{ 34 | short idx; 35 | float *full_efficacy; // efficacy against contracting the virus 36 | float *symptoms_efficacy; // efficacy preventing symptoms 37 | float *severe_efficacy; // efficacy preventing severe symptoms (i.e. not needing to be hospitalised) 38 | short time_to_protect; // time between having the vaccine and protection starting 39 | short vaccine_protection_period; // time for which protections lasts 40 | short is_full; // does it have some full protection 41 | short is_symptoms; // does it have some symptoms-only protection 42 | short is_severe; // does it have some severe-only protection 43 | short n_strains; // the number of strains 44 | char name[INPUT_CHAR_LEN]; // unique name of the network 45 | vaccine *next; 46 | }; 47 | 48 | /************************************************************************/ 49 | /****************************** Functions *****************************/ 50 | /************************************************************************/ 51 | 52 | void set_up_transition_times_intervention( model* ); 53 | void set_up_app_users( model* ); 54 | void set_up_risk_scores( model* ); 55 | void destroy_risk_scores( model* ); 56 | void update_intervention_policy( model*, int ); 57 | 58 | void set_up_trace_tokens( model*, float ); 59 | void add_trace_tokens( model*, float ); 60 | trace_token* create_trace_token( model*, individual*, int ); 61 | trace_token* index_trace_token( model*, individual* ); 62 | void remove_one_trace_token( model*, trace_token* ); 63 | void remove_traced_on_this_trace( model*, individual* ); 64 | void remove_traces_on_individual( model*, individual* ); 65 | void intervention_trace_token_release( model*, individual* ); 66 | 67 | int intervention_quarantine_until( model*, individual*, individual*, int, int, trace_token*, int, double ); 68 | void intervention_quarantine_release( model*, individual* ); 69 | void intervention_quarantine_household( model*, individual*, int, int, trace_token*, int ); 70 | void intervention_test_take( model*, individual* ); 71 | void intervention_test_result( model*, individual* ); 72 | void intervention_manual_trace( model *, individual *); 73 | void intervention_notify_contacts( model*, individual*, int, trace_token*, int ); 74 | void intervention_index_case_symptoms_to_positive( model*, trace_token* ); 75 | 76 | short add_vaccine( model*, float*, float*, float*, short, short ); 77 | vaccine* get_vaccine_by_id( model*, short ); 78 | short intervention_vaccinate( model*, individual*, vaccine* ); 79 | short intervention_vaccinate_by_idx( model*, long, vaccine* ); 80 | long intervention_vaccinate_age_group( model*, double[ N_AGE_GROUPS ], vaccine*, long[ N_AGE_GROUPS ] ); 81 | void intervention_vaccine_protect( model*, individual*, void* ); 82 | void intervention_vaccine_wane( model*, individual*, void* ); 83 | 84 | void intervention_on_symptoms( model*, individual* ); 85 | void intervention_on_hospitalised( model*, individual* ); 86 | void intervention_on_critical( model*, individual* ); 87 | void intervention_on_positive_result( model*, individual* ); 88 | void intervention_on_traced( model*, individual*, int, int, trace_token*, double, int ); 89 | 90 | void intervention_smart_release( model* ); 91 | int resolve_quarantine_reasons(int *); 92 | 93 | #endif /* INTERVENTIONS_H_ */ 94 | -------------------------------------------------------------------------------- /examples/example_multi_strain_vaccinate.py: -------------------------------------------------------------------------------- 1 | """ 2 | Example Multi-Strain with Vaccination 3 | 4 | Demonstrates running with multiple strains and a vaccination program 5 | 6 | An initial epidemic with the base strain is allowed to run and then 7 | surpressed by a combination of lockdown and vaccination 8 | 9 | A new strain is seeded which is more transmissible, only 80% cross immunity 10 | and lower vaccine efficacy. 11 | 12 | A new epidemic in cases occurs but hospitalisations are limited due to 13 | the vaccines be very effecitve against severe symptoms for the new strain. 14 | 15 | Created: 9 Jun 2021 16 | Author: roberthinch 17 | """ 18 | 19 | 20 | import pandas as pd 21 | import numpy as np 22 | import sys 23 | 24 | sys.path.append("../src/COVID19") 25 | from model import VaccineSchedule, Model 26 | from strain import Strain 27 | from vaccine import Vaccine 28 | 29 | if __name__ == '__main__': 30 | # some OS limit the number of process which can be spawned by default 31 | # for OSX it can ge increased with the following line, probably needs to be 32 | # changed for other operating systems 33 | 34 | n_total = 100000 35 | params = { "n_total" : n_total, "max_n_strains" : 2 } 36 | abm = Model( params = params ) 37 | 38 | # add a new strain 39 | transmission_multiplier = 1.6; 40 | hospitalised_fraction = [ 0.001, 0.001, 0.01,0.05, 0.05, 0.10, 0.15, 0.30, 0.5 ] 41 | 42 | strain_delta = abm.add_new_strain( 43 | transmission_multiplier = transmission_multiplier, 44 | hospitalised_fraction = hospitalised_fraction ) 45 | 46 | # set cross-immunity between strains 47 | cross_immunity_mat = [ 48 | [ 1.0, 0.8 ], 49 | [ 0.8, 1.0 ] 50 | ] 51 | abm.set_cross_immunity_matrix( cross_immunity_mat ) 52 | 53 | # add vaccine 54 | full_efficacy_base = 0.6 55 | full_efficacy_delta = 0.3 56 | symptoms_efficacy_base = 0.85 57 | symptoms_efficacy_delta = 0.6 58 | severe_efficacy_base = 0.95 59 | severe_efficacy_delta = 0.90 60 | vaccine = abm.add_vaccine( 61 | full_efficacy = [ full_efficacy_base, full_efficacy_delta ], 62 | symptoms_efficacy = [ symptoms_efficacy_base, symptoms_efficacy_delta ], 63 | severe_efficacy = [ severe_efficacy_base, severe_efficacy_delta ], 64 | time_to_protect = 14, 65 | vaccine_protection_period = 365 66 | ) 67 | 68 | # create a vaccine schedule 69 | schedule = VaccineSchedule( 70 | frac_0_9 = 0, 71 | frac_10_19 = 0, 72 | frac_20_29 = 0.02, 73 | frac_30_39 = 0.02, 74 | frac_40_49 = 0.02, 75 | frac_50_59 = 0.02, 76 | frac_60_69 = 0.02, 77 | frac_70_79 = 0.02, 78 | frac_80 = 0.02, 79 | vaccine = vaccine 80 | ) 81 | 82 | # run the model for 30 time steps 83 | for t in range( 30 ): 84 | abm.one_time_step() 85 | 86 | # add lockdown and start vaccination schedule 87 | abm.update_running_params( "lockdown_on", True ) 88 | 89 | # run the model for 70 time steps and vaccinate 90 | for t in range( 70 ): 91 | abm.vaccinate_schedule( schedule ) 92 | abm.one_time_step() 93 | 94 | # turn off lockdown and put in some social distancing (10% reduction in transmission rates outside), then run for 50 time steps 95 | abm.update_running_params( "lockdown_on", False ) 96 | abm.update_running_params( "relative_transmission_occupation", 0.9 ) 97 | abm.update_running_params( "relative_transmission_random", 0.9 ) 98 | for t in range( 40 ): 99 | abm.one_time_step() 100 | 101 | # seed delta strain in 20 random people 102 | idx_seed = np.random.choice( n_total, 20, replace=False) 103 | for idx in range( len( idx_seed ) ) : 104 | abm.seed_infect_by_idx( ID = idx_seed[ idx ], strain = strain_delta ) 105 | 106 | # run the model for 50 more time steps 107 | for t in range( 100 ): 108 | abm.one_time_step() 109 | 110 | results = abm.results 111 | results[ "new_infected"] = results[ "total_infected" ].diff() 112 | df_res = results.loc[:,["time", "new_infected", "hospital_admissions"]] 113 | 114 | print( df_res.to_numpy().astype(int)) 115 | 116 | 117 | 118 | -------------------------------------------------------------------------------- /python/transpose_parameters.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | """ 3 | Convert a parameter set with two columns (param name \t value) to a CSV with header as parameter 4 | name and first line of parameter values. Create markdown tables of parameters for documentation 5 | folder. This script ensures that the baseline parameters and parameter tables in the documentation 6 | are consistent. 7 | 8 | Created: June 2020 9 | Author: p-robot 10 | """ 11 | 12 | import sys 13 | import pandas as pd, numpy as np 14 | import os, glob, shutil 15 | from os.path import join 16 | from distutils.dir_util import copy_tree 17 | 18 | def create_markdown_from_df(df, title = ""): 19 | """ 20 | Create text string of markdown table from pandas dataframe of OpenABM-Covid19 parameters 21 | Used in automated creation of markdown tables for model documentation. 22 | 23 | Arguments 24 | --------- 25 | df : pandas.DataFrame 26 | Dataframe of OpenABM-Covid19 parameters (transposed), with rows as parameters 27 | and with the following columns: 28 | - Name (str, parameter name) 29 | - Value (float/int, default value) 30 | - Symbol (str, symbol for this parameter used in markdown documentation) 31 | - Description (str, description of the parameter) 32 | - Source (str, source for the default value) 33 | title : str 34 | Title string for header of the markdown file 35 | 36 | Returns 37 | ------- 38 | str of markdown table of the form: 39 | 40 | | Name | .... | Source | 41 | | ---- | ---- | ---- | 42 | | 11 | .... | 1N | 43 | | 21 | .... | 2N | 44 | | .... | .... | .... | 45 | | M1 | .... | MN | 46 | """ 47 | NCOLS = df.shape[1] 48 | 49 | title_text = ["# " + title] 50 | header = ["| " + " | ".join(df.columns) + " | "] 51 | hline = ["| " + "".join([" ---- |" for i in range(NCOLS)])] 52 | 53 | table_body = list() 54 | for i, row in df.iterrows(): 55 | 56 | table_row = "| `{}` | {} | {} | {} | {} |".format( 57 | row.Name, 58 | str(row.Value), 59 | row.Symbol, 60 | row.Description, 61 | row.Source) 62 | 63 | table_body.append(table_row) 64 | 65 | output = title_text + header + hline + table_body 66 | 67 | return("\n".join(output)) 68 | 69 | 70 | if __name__ == "__main__": 71 | 72 | # Parse command line arguments 73 | if len(sys.argv) > 1: 74 | long_parameter_file = sys.argv[1] 75 | else: 76 | long_parameter_file = join("tests", "data", "baseline_parameters_transpose.csv") 77 | 78 | if len(sys.argv) > 2: 79 | wide_parameter_file = sys.argv[2] 80 | else: 81 | wide_parameter_file = join("tests", "data", "baseline_parameters.csv") 82 | 83 | # Read "long/transpose" format of parameter file 84 | df = pd.read_csv(long_parameter_file, dtype = str) 85 | 86 | # Write parameters in a form readable by the model 87 | df[["Name", "Value"]].set_index("Name").transpose().to_csv(wide_parameter_file, index = False) 88 | 89 | # copy files to the defaul_params folder 90 | source_dir = join( "tests", "data" ) 91 | target_dir = join( "src", "COVID19", "default_params") 92 | target2_dir = join( "inst", "default_params") 93 | 94 | # remove all the old files before copying over the new ones 95 | for f in glob.glob( target_dir + "/*" ) : 96 | os.remove( f ) 97 | files = [ "baseline_parameters.csv", "baseline_household_demographics.csv", "hospital_baseline_parameters.csv" ] 98 | for f in files : 99 | shutil.copy( join( source_dir, f ), join( target_dir, f )) 100 | shutil.copy( join( source_dir, f ), join( target2_dir, f )) 101 | 102 | # Generate markdown tables for each parameter type (first strip on white space) 103 | parameter_types = df["Parameter type"].dropna().str.strip().unique() 104 | 105 | for t in parameter_types: 106 | df_type = df.loc[df["Parameter type"] == t] 107 | df_type = df_type.replace(np.nan, "-").drop(columns = ["Parameter type"]) 108 | markdown_table = create_markdown_from_df(df_type, title = "Table: " + t) 109 | 110 | markdown_file = t.lower().replace(" ", "_") + '.md' 111 | with open(join("documentation", "parameters", markdown_file), 'w') as f: 112 | f.write(markdown_table) 113 | 114 | # Generate table for all parameters 115 | df_all = df.replace(np.nan, "-").drop(columns = ["Parameter type"]) 116 | markdown_table = create_markdown_from_df(df_all, title = "Table: Parameter dictionary") 117 | 118 | with open(join("documentation", "parameters", "parameter_dictionary.md"), 'w') as f: 119 | f.write(markdown_table) 120 | -------------------------------------------------------------------------------- /tests/constant.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | """" 3 | constant.py 4 | 5 | constants used across all the testing files and 6 | constants from the C code which are used in testing 7 | (would be good to get this direct from C if possible) 8 | 9 | Created on: 31 Mar 2020 10 | Author: hinchr 11 | """ 12 | 13 | from os.path import join 14 | from enum import Enum 15 | from string import Template 16 | 17 | # Directories 18 | IBM_DIR = "src" 19 | IBM_DIR_TEST = "src_test" 20 | DATA_DIR_TEST = "data_test" 21 | 22 | TEST_DATA_TEMPLATE = "./tests/data/baseline_parameters.csv" 23 | TEST_DATA_FILE = join(DATA_DIR_TEST, "test_parameters.csv") 24 | 25 | TEST_HOSPITAL_TEMPLATE = "./tests/data/hospital_baseline_parameters.csv" 26 | TEST_HOSPITAL_FILE = join(DATA_DIR_TEST, "test_hospital_parameters.csv") 27 | 28 | TEST_OUTPUT_FILE = join(DATA_DIR_TEST, "test_output.csv") 29 | TEST_INDIVIDUAL_FILE = join(DATA_DIR_TEST, "individual_file_Run1.csv") 30 | TEST_INTERACTION_FILE = join(DATA_DIR_TEST, "interactions_Run1.csv") 31 | TEST_TRANSMISSION_FILE = join(DATA_DIR_TEST, "transmission_Run1.csv") 32 | TEST_TRACE_FILE = join(DATA_DIR_TEST, "trace_tokens_Run1.csv") 33 | TEST_QUARANTINE_REASONS_FILE = Template(join(DATA_DIR_TEST, "quarantine_reasons_file_Run1_T$T.csv")) 34 | TEST_HCW_FILE = join(DATA_DIR_TEST, "ward_output1.csv") 35 | TEST_OUTPUT_FILE_HOSPITAL_TIME_STEP = join(DATA_DIR_TEST, "time_step_hospital_output1.csv") 36 | TEST_OUTPUT_FILE_HOSPITAL_INTERACTIONS = join(DATA_DIR_TEST, "time_step_hospital_interactions1.csv") 37 | TEST_HOUSEHOLD_TEMPLATE = "./tests/data/baseline_household_demographics.csv" 38 | TEST_HOUSEHOLD_FILE = join(DATA_DIR_TEST, "test_household_demographics.csv") 39 | 40 | TEST_HOUSEHOLD_NETWORK_FILE = join(DATA_DIR_TEST, "household_network_Run1.csv") 41 | 42 | class EVENT_TYPES(Enum): 43 | SUSCEPTIBLE = 0 44 | PRESYMPTOMATIC = 1 45 | PRESYMPTOMATIC_MILD = 2 46 | ASYMPTOMATIC = 3 47 | SYMPTOMATIC = 4 48 | SYMPTOMATIC_MILD = 5 49 | HOSPITALISED = 6 50 | CRITICAL = 7 51 | HOSPITALISED_RECOVERING = 8 52 | RECOVERED = 9 53 | DEATH = 10 54 | QUARANTINED = 11 55 | QUARANTINE_RELEASE = 12 56 | TEST_TAKE = 13 57 | TEST_RESULT = 14 58 | CASE = 15 59 | TRACE_TOKEN_RELEASE = 16 60 | NOT_IN_HOSPITAL = 17 61 | WAITING = 18 62 | GENERAL = 19 63 | ICU = 20 64 | MORTUARY = 21 65 | DISCHARGED = 22 66 | MANUAL_CONTACT_TRACING = 23 67 | TRANSITION_TO_HOSPITAL = 24 68 | TRANSITION_TO_CRITICAL = 25 69 | N_EVENT_TYPES = 26 70 | 71 | # Age groups 72 | AGE_0_9 = 0 73 | AGE_10_19 = 1 74 | AGE_20_29 = 2 75 | AGE_30_39 = 3 76 | AGE_40_49 = 4 77 | AGE_50_59 = 5 78 | AGE_60_69 = 6 79 | AGE_70_79 = 7 80 | AGE_80 = 8 81 | N_AGE_GROUPS = 9 82 | AGES = [ 83 | AGE_0_9, 84 | AGE_10_19, 85 | AGE_20_29, 86 | AGE_30_39, 87 | AGE_40_49, 88 | AGE_50_59, 89 | AGE_60_69, 90 | AGE_70_79, 91 | AGE_80 92 | ] 93 | 94 | CHILD = 0 95 | ADULT = 1 96 | ELDERLY = 2 97 | AGE_TYPES = [CHILD, CHILD, ADULT, ADULT, ADULT, ADULT, ADULT, ELDERLY, ELDERLY] 98 | 99 | # network type 100 | HOUSEHOLD = 0 101 | OCCUPATION = 1 102 | RANDOM = 2 103 | HOSPITAL_WORK = 3 104 | HOSPITAL_DOCTOR_PATIENT_GENERAL = 4 105 | HOSPITAL_NURSE_PATIENT_GENERAL = 5 106 | HOSPITAL_DOCTOR_PATIENT_ICU = 6 107 | HOSPITAL_NURSE_PATIENT_ICU = 7 108 | 109 | # work networks 110 | HOSPITAL_WORK_NETWORK = -1 111 | PRIMARY_NETWORK = 0 112 | SECONDARY_NETWORK = 1 113 | WORKING_NETWORK = 2 114 | RETIRED_NETWORK = 3 115 | ELDERLY_NETWORK = 4 116 | N_DEFAULT_OCCUPATION_NETWORKS = 5 117 | NETWORKS = [PRIMARY_NETWORK, SECONDARY_NETWORK, WORKING_NETWORK, RETIRED_NETWORK, ELDERLY_NETWORK] 118 | 119 | # work type networks 120 | NETWORK_CHILD = 0 121 | NETWORK_ADULT = 1 122 | NETWORK_ELDERLY = 2 123 | NETWORK_TYPES = [NETWORK_CHILD, NETWORK_ADULT, NETWORK_ELDERLY] 124 | 125 | # network type map 126 | NETWORK_TYPE_MAP = [ 127 | NETWORK_CHILD, 128 | NETWORK_CHILD, 129 | NETWORK_ADULT, 130 | NETWORK_ELDERLY, 131 | NETWORK_ELDERLY 132 | ] 133 | 134 | # custom network type map 135 | CUSTOM_NETWORK_TYPE_MAP = [ 136 | NETWORK_CHILD, 137 | NETWORK_CHILD, 138 | NETWORK_ADULT, 139 | NETWORK_ADULT, 140 | NETWORK_ADULT, 141 | NETWORK_ADULT, 142 | NETWORK_ADULT, 143 | NETWORK_ADULT, 144 | NETWORK_ELDERLY, 145 | NETWORK_ELDERLY 146 | ] 147 | 148 | MAX_DAILY_INTERACTIONS_KEPT = 10 149 | 150 | PARAM_LINE_NUMBER = 1 151 | HOSPITAL_PARAM_LINE_NUMBER = 1 152 | 153 | NOT_HEALTHCARE_WORKER = -1 154 | class HCW_TYPES(Enum): 155 | DOCTOR = 0 156 | NURSE = 1 157 | 158 | class HOSPITAL_WARD_TYPES(Enum): 159 | COVID_GENERAL = 0 160 | COVID_ICU = 1 161 | 162 | # test statuses 163 | NO_TEST = -2 164 | TEST_ORDERED = -1 165 | 166 | # Construct the executable command 167 | EXE = f"covid19ibm.exe {TEST_DATA_FILE} {PARAM_LINE_NUMBER} "+\ 168 | f"{DATA_DIR_TEST} {TEST_HOUSEHOLD_FILE} {TEST_HOSPITAL_FILE}" 169 | 170 | 171 | command = join(IBM_DIR_TEST, EXE) 172 | -------------------------------------------------------------------------------- /tests/adapter_covid19/data/README.md: -------------------------------------------------------------------------------- 1 | # Data Schemas 2 | 3 | Specifications for all references to regions, sectors and age groups 4 | can be found in `enums.py` 5 | 6 | ## `company_size_and_turnover.csv` 7 | 8 | * Sector: UK sector 9 | * min_size: (0 to inf) lower bound of number of employees 10 | * num_companies: (0 to inf) total number of companies in this sector with employee counts in the min_size bucket 11 | * num_employees: (0 to inf) total number of employees in this sector for companies with employee counts in the min_size bucket 12 | * per_turnover: (0 to 100) proportion of turnover share within the sector in percentage terms 13 | 14 | 15 | ## `credit_score.csv` 16 | 17 | * Region: UK region 18 | * mean: (-inf to inf) mean personal credit score per region 19 | * stdev: (0 to inf) standard deviation of credit score per region 20 | 21 | ## `demand.csv` 22 | 23 | * columns: UK sector 24 | * rows: UK sector 25 | * values: (0 to 1) demand contribution from row sector 26 | to column sector. All rows sum to 1. 27 | 28 | ## `earnings.csv` 29 | 30 | * Region: UK region 31 | * earnings: (0 to inf) median personal earnings per region 32 | 33 | ## `expenses.csv` 34 | 35 | * Region: UK region 36 | * expenses: (0 to inf): minimum personal expenses per region 37 | 38 | ## `expenses_full.csv` 39 | 40 | * Region: UK region 41 | * Sector: UK sector 42 | * Decile: (one to nine): decile of personal income 43 | * expenses: (0 to inf): personal expenses per region per sector per decile in normal times 44 | 45 | ## `gdp.csv` 46 | 47 | * Region: UK Region 48 | * Sector: UK Sector 49 | * Age: Age banding 50 | * gdp: (0 to inf) GDP per region, sector, age group 51 | 52 | 53 | ## `growth_rates.csv` 54 | 55 | * Sector: UK Sector 56 | * growth_rates: (0 to inf) historic peacetime growth rates per sector 57 | 58 | ## `input_output.csv` 59 | 60 | * Sector: UK Sector 61 | * employee_compensation: (0 to inf): mean employee compensation per sector 62 | * taxes_minus_subsidies: (0 to inf): mean taxes minus subsidies per sector 63 | * capital_consumption: (0 to inf): mean capital consumption per sector 64 | * net_operating_surplus: (0 to inf): mean net operating surplus per sector 65 | 66 | ## `input_output_final.csv` 67 | 68 | * Sector: UK Sector 69 | * C: consumption 70 | * K: capital formation 71 | * E: exports 72 | 73 | ## `input_output_intermediate.csv` 74 | 75 | * Columns: UK Sector 76 | * Rows: UK Sector 77 | * Values: (0 to inf): consumption of products of row sector by column sector 78 | 79 | ## `input_output_primary.csv` 80 | 81 | * Sector: UK Sector 82 | * IMPORTS: (-inf to inf) 83 | * TAXES_PRODUCTS: (-inf to inf) 84 | * COMPENSATION: (-inf to inf) 85 | * TAXES_PRODUCTION: (-inf to inf) 86 | * FIXED_CAPITAL_CONSUMPTION: (-inf to inf) 87 | * IMPORTS: (-inf to inf) 88 | 89 | ## `keyworker.csv` 90 | 91 | * Sector: UK Sector 92 | * keyworker: (0 to 1): fraction workers per sector who still go to work and are unaffected by lockdown 93 | 94 | ## `largecap_count.csv` 95 | 96 | * Sector: UK Sector 97 | * largecap_count (0 to inf): number of large-cap companies per sector 98 | 99 | ## `largecap_pct_turnover.csv` 100 | 101 | * Sector: UK Sector 102 | * largecap_pct_turnover (0 to 1): fraction of turnover generated by large-cap corporations per sector 103 | 104 | ## `min_expenses_full.csv` 105 | 106 | * Region: UK region 107 | * Sector: UK sector 108 | * Decile: (one to nine): decile of personal income 109 | * expenses: (0 to inf): minimum personal expenses per region per sector per decile 110 | 111 | ## `populations.csv` 112 | 113 | * region: UK Region 114 | * columns: A0, A10, ..., A80 (10 year age bands) 115 | * values: (0 to inf): population of each region by age group 116 | 117 | ## `smallcap_cash.csv` 118 | 119 | * Sector: UK Sector 120 | * smallcap_cash: (0 to inf): number of days of surplus cashflow of cash reserves per sector for small-cap corporations 121 | 122 | ## `sme_count.csv` 123 | 124 | * Sector: UK Sector 125 | * sme_count: (0 to inf): number of small and medium enterprises per sector 126 | 127 | ## `sme_rate_payer_vulnerability.csv` 128 | 129 | * Sector: UK Sector 130 | * vulnerability: (0 to 100): vulnerability factor (higher = more vulnerable) for sectors which pay rates, and hence will have a higher proportion of companies eligible for new spending government stimulus 131 | 132 | ## `supply.csv'` 133 | 134 | * columns: UK sector 135 | * rows: UK sector 136 | * values: (0 to 1) supply contribution from row sector 137 | to column sector. All rows sum to 1. 138 | 139 | ## `vulnerability.csv` 140 | 141 | * Sector: UK Sector 142 | * vulnerability: (0 to 1): index representing maximum productivity of each sector under a lockdown situation 143 | 144 | ## `wages.csv` 145 | 146 | * Sector: UK Sector 147 | * Age: Lower bound of age band (see `src/adapter_covid19/enums.py` for definition) 148 | * wages: (0 to inf): yearly income pre-tax per sector per age band 149 | 150 | ## `wfh.csv` 151 | 152 | * Sector: UK Sector 153 | * wfh: (0 to 1): productivity of each sector when working from home 154 | 155 | ## `workers.csv` 156 | 157 | * Region: UK Region 158 | * Sector: UK Sector 159 | * Age: Age banding 160 | * workers: (0 to inf) Number of workers per region, sector, age group 161 | -------------------------------------------------------------------------------- /src/adapter_covid19/datasources.py: -------------------------------------------------------------------------------- 1 | import abc 2 | import os 3 | from typing import Tuple, Mapping, Any, Union, Optional, Sequence 4 | import pickle 5 | 6 | import numpy as np 7 | import pandas as pd 8 | 9 | from adapter_covid19.enums import Region, Sector, Age, FinalUse, PrimaryInput, Decile 10 | 11 | ALL_ENUMS = [Region, Sector, Age, FinalUse, PrimaryInput, Decile] 12 | 13 | 14 | class Reader: 15 | def __init__(self, data_path: str): 16 | """ 17 | Helper class to read data from disk 18 | 19 | Parameters 20 | ---------- 21 | data_path: path to data 22 | """ 23 | self.data_path = data_path 24 | 25 | def _get_filepath(self, filename: str) -> str: 26 | return os.path.join(self.data_path, filename) 27 | 28 | def load_csv( 29 | self, 30 | filename: str, 31 | orient: str = "dataframe", 32 | index_col: Optional[Union[int, Sequence[int]]] = None, 33 | ) -> Union[pd.DataFrame, Mapping[str, Any]]: 34 | data = pd.read_csv(self._get_filepath(f"{filename}.csv"), index_col=index_col) 35 | if orient.lower() == "dataframe": 36 | return data 37 | return data.to_dict(orient) 38 | 39 | def load_pkl(self, filename: str,) -> Any: 40 | with open(self._get_filepath(f"{filename}.pkl"), "rb") as f: 41 | data = pickle.load(f) 42 | return data 43 | 44 | 45 | class DataSource(abc.ABC): 46 | def __init__(self, filename: str): 47 | """ 48 | Read and parse a dataset from disk 49 | Parameters 50 | ---------- 51 | filename: filename of dataset 52 | """ 53 | self.filename = filename 54 | 55 | @abc.abstractmethod 56 | def load(self, reader: Reader) -> Any: 57 | """ 58 | Load datasource from disk 59 | 60 | Parameters 61 | ---------- 62 | reader: Reader class 63 | 64 | Returns 65 | ------- 66 | Loaded and parsed data 67 | """ 68 | raise NotImplementedError 69 | 70 | 71 | class RegionDataSource(DataSource): 72 | def load( 73 | self, reader: Reader 74 | ) -> Union[Mapping[Region, float], Mapping[str, Mapping[Region, float]]]: 75 | data = reader.load_csv(self.filename, orient="dict", index_col=0) 76 | data = {k: {Region[kk]: vv for kk, vv in v.items()} for k, v in data.items()} 77 | if len(data) > 1: 78 | return data 79 | return next(iter(data.values())) 80 | 81 | 82 | class SectorDataSource(DataSource): 83 | def load( 84 | self, reader: Reader 85 | ) -> Union[Mapping[Sector, float], Mapping[str, Mapping[Sector, float]]]: 86 | data = reader.load_csv(self.filename, orient="dict", index_col=0) 87 | data = {k: {Sector[kk]: vv for kk, vv in v.items()} for k, v in data.items()} 88 | if len(data) > 1: 89 | return data 90 | return next(iter(data.values())) 91 | 92 | 93 | class RegionSectorAgeDataSource(DataSource): 94 | def load( 95 | self, reader: Reader 96 | ) -> Union[ 97 | Mapping[Tuple[Region, Sector, Age], float], 98 | Mapping[str, Mapping[Tuple[Region, Sector, Age], float]], 99 | ]: 100 | data = reader.load_csv(self.filename, orient="dict", index_col=[0, 1, 2]) 101 | data = { 102 | k: {(Region[kk[0]], Sector[kk[1]], Age[kk[2]]): vv for kk, vv in v.items()} 103 | for k, v in data.items() 104 | } 105 | if len(data) > 1: 106 | return data 107 | return next(iter(data.values())) 108 | 109 | 110 | class RegionDecileSource(DataSource): 111 | def load(self, reader: Reader) -> Mapping[Tuple[Region, Decile], float]: 112 | frame = reader.load_csv(self.filename) 113 | data = { 114 | (Region[t.Region], Decile[t.Decile]): t[-1] 115 | for t in frame.itertuples(index=False) 116 | } 117 | return data 118 | 119 | 120 | class RegionSectorDecileSource(DataSource): 121 | def load(self, reader: Reader) -> Mapping[Tuple[Region, Sector, Decile], float]: 122 | frame = reader.load_csv(self.filename) 123 | data = { 124 | (Region[t.Region], Sector[t.Sector], Decile[t.Decile]): t[-1] 125 | for t in frame.itertuples(index=False) 126 | } 127 | return data 128 | 129 | 130 | class DataFrameDataSource(DataSource): 131 | def load(self, reader: Reader) -> pd.DataFrame: 132 | frame = reader.load_csv(self.filename) 133 | frame = frame.set_index(frame.columns[0]) 134 | for enum in ALL_ENUMS: 135 | try: 136 | frame = frame.rename(index=lambda x: enum[x]) 137 | except KeyError: 138 | pass 139 | else: 140 | break 141 | for enum in ALL_ENUMS: 142 | try: 143 | frame = frame.rename(columns=lambda x: enum[x]) 144 | except KeyError: 145 | pass 146 | else: 147 | break 148 | return frame 149 | 150 | 151 | class WeightMatrix(DataSource): 152 | def load(self, reader: Reader) -> np.array: 153 | frame = reader.load_csv(self.filename).set_index("Sector") 154 | return frame.values 155 | -------------------------------------------------------------------------------- /tests/testthat/test-VaccineSchedule.R: -------------------------------------------------------------------------------- 1 | library(R6) 2 | library( data.table ) 3 | 4 | test_that("VaccineSchedule initialization", { 5 | 6 | abm = Model.new( params = list( n_total = 1e4 ) ) 7 | vac = abm$add_vaccine( full_efficacy = 1, time_to_protect = 15, vaccine_protection_period = 365) 8 | 9 | v <- VaccineSchedule$new( 10 | frac_0_9 = 0.0, 11 | frac_10_19 = 0.1, 12 | frac_20_29 = 0.2, 13 | frac_30_39 = 0.3, 14 | frac_40_49 = 0.4, 15 | frac_50_59 = 0.5, 16 | frac_60_69 = 0.6, 17 | frac_70_79 = 0.7, 18 | frac_80 = 0.8, 19 | vaccine = vac ) 20 | 21 | expect_equal(v$fraction_to_vaccinate, seq(0, 0.8, 0.1)) 22 | expect_equal(v$total_vaccinated, rep(0,9)) 23 | expect_equal(v$vaccine$full_efficacy(), 1) 24 | expect_equal(v$vaccine$time_to_protect(), 15) 25 | expect_equal(v$vaccine$vaccine_protection_period(), 365 ) 26 | 27 | rm( abm ) 28 | }) 29 | 30 | 31 | # check the correct number of people have been vaccinated 32 | check_n_vaccinated <- function( abm, vac_frac, days) 33 | { 34 | indiv <- as.data.table( abm$get_individuals() ) 35 | N_70 <- indiv[ age_group == AgeGroupEnum[ "_70_79"], .N ] 36 | V_70 <- indiv[ age_group == AgeGroupEnum[ "_70_79"] & vaccine_status > 0 , .N ] 37 | N_80 <- indiv[ age_group == AgeGroupEnum[ "_80"], .N ] 38 | V_80 <- indiv[ age_group == AgeGroupEnum[ "_80"] & vaccine_status > 0 , .N ] 39 | V_else <- indiv[ age_group != AgeGroupEnum[ "_70_79"] & age_group != AgeGroupEnum[ "_80"] & vaccine_status > 0 , .N ] 40 | 41 | expect_equal(V_else, 0, label = "<70 vaccinated when not scheduled" ) 42 | expect_lt( abs( V_70 - N_70 * vac_frac * days ), days + 0.01, label = "incorrect 70-79 vaccinated" ) 43 | expect_lt( abs( V_80 - N_80 * vac_frac * days ), days + 0.01, label = "incorrect 80+ vaccinated" ) 44 | } 45 | 46 | test_that("VaccineSchedule - single day application", { 47 | 48 | n_total <- 1e4 49 | vac_frac <- 0.1 50 | abm <- Model.new( params = list( n_total = 1e4 ) ) 51 | vac <- abm$add_vaccine( full_efficacy = 1, time_to_protect = 15, vaccine_protection_period = 365) 52 | 53 | v <- VaccineSchedule$new( 54 | frac_0_9 = 0.0, 55 | frac_10_19 = 0.0, 56 | frac_20_29 = 0.0, 57 | frac_30_39 = 0.0, 58 | frac_40_49 = 0.0, 59 | frac_50_59 = 0.0, 60 | frac_60_69 = 0.0, 61 | frac_70_79 = vac_frac, 62 | frac_80 = vac_frac, 63 | vaccine = vac ) 64 | 65 | for( day in 1:5 ) { 66 | abm$run( 1, verbose = FALSE ) 67 | abm$vaccinate_schedule( v ) 68 | check_n_vaccinated( abm, vac_frac, day ) 69 | } 70 | 71 | rm( abm ) 72 | }) 73 | 74 | test_that("VaccineSchedule - repeating schedule", { 75 | 76 | n_total <- 1e4 77 | vac_frac <- 0.1 78 | abm <- Model.new( params = list( n_total = 1e4 ) ) 79 | vac <- abm$add_vaccine( full_efficacy = 1, time_to_protect = 15, vaccine_protection_period = 365) 80 | 81 | v <- VaccineSchedule$new( 82 | frac_0_9 = 0.0, 83 | frac_10_19 = 0.0, 84 | frac_20_29 = 0.0, 85 | frac_30_39 = 0.0, 86 | frac_40_49 = 0.0, 87 | frac_50_59 = 0.0, 88 | frac_60_69 = 0.0, 89 | frac_70_79 = vac_frac, 90 | frac_80 = vac_frac, 91 | vaccine = vac ) 92 | 93 | # set up a recurring schedule 94 | abm$run( 1, verbose = FALSE ) 95 | abm$vaccinate_schedule( v, recurring = TRUE ) 96 | 97 | # run for 5 days, check it is added each day 98 | abm$run( 5, verbose = FALSE ) 99 | check_n_vaccinated( abm, vac_frac, 5 ) 100 | 101 | # remove it, run for longer and check it is not still applied 102 | abm$vaccinate_schedule( NULL ) 103 | abm$run( 5, verbose = FALSE ) 104 | check_n_vaccinated( abm, vac_frac, 5 ) 105 | 106 | 107 | rm( abm ) 108 | }) 109 | 110 | test_that("VaccineSchedule - list schedule", { 111 | 112 | n_total <- 1e4 113 | vac_frac <- 0.1 114 | abm <- Model.new( params = list( n_total = 1e4 ) ) 115 | vac <- abm$add_vaccine( full_efficacy = 1, time_to_protect = 15, vaccine_protection_period = 365) 116 | 117 | v <- VaccineSchedule$new( 118 | frac_0_9 = 0.0, 119 | frac_10_19 = 0.0, 120 | frac_20_29 = 0.0, 121 | frac_30_39 = 0.0, 122 | frac_40_49 = 0.0, 123 | frac_50_59 = 0.0, 124 | frac_60_69 = 0.0, 125 | frac_70_79 = vac_frac, 126 | frac_80 = vac_frac, 127 | vaccine = vac ) 128 | 129 | # set up a multi-day different schedule 130 | sched_list = vector( mode = "list", length = 10 ) 131 | sched_list[[ 2 ]] <- v 132 | sched_list[[ 3 ]] <- v 133 | sched_list[[ 6 ]] <- v 134 | sched_list[[ 8 ]] <- v 135 | sched_list[[ 9 ]] <- v 136 | expected <- cumsum(unlist( lapply( sched_list, is.R6) )) 137 | abm$vaccinate_schedule( sched_list, recurring = TRUE ) 138 | 139 | # run for 8 days and check the amount each day 140 | for( day in 1:8 ) 141 | { 142 | abm$run( 1, verbose = FALSE ) 143 | check_n_vaccinated( abm, vac_frac, expected[ day ] ) 144 | } 145 | 146 | # run for 5 days, check the whole schedule has been applied 147 | abm$run( 5, verbose = FALSE ) 148 | check_n_vaccinated( abm, vac_frac, max( expected ) ) 149 | 150 | rm( abm ) 151 | }) 152 | 153 | --------------------------------------------------------------------------------