diff --git a/.editorconfig b/.editorconfig index e7006ed3c..428584c7e 100644 --- a/.editorconfig +++ b/.editorconfig @@ -10,6 +10,7 @@ charset = utf-8 indent_style = tab insert_final_newline = true trim_trailing_whitespace = true +max_line_length = 150 [Makefile] indent_style = tab \ No newline at end of file diff --git a/pom.xml b/pom.xml index 3c4f0f6cc..7614c7d4d 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ 4.0.0 com.github.matsim-org matsim-episim - 22.6 + 22.12 MATSim Episim Epidemic simulation for MATSim diff --git a/src/main/R/Covid19ScenarioHubSubmission.R b/src/main/R/Covid19ScenarioHubSubmission.R new file mode 100644 index 000000000..8505c634e --- /dev/null +++ b/src/main/R/Covid19ScenarioHubSubmission.R @@ -0,0 +1,98 @@ + +library(lubridate) +library(tidyverse) +library(readr) + + + +rm(list=ls()) +source("/Users/jakob/git/matsim-episim/src/main/R/masterJR-utils.R", encoding = 'utf-8') + +# Global variables +directory <- "/Users/jakob/git/public-svn/matsim/scenarios/countries/de/episim/battery/jakob/2022-07-27/4-eu-noAgg/" + +origin_date <- ymd("2022-07-24") # first day (sunday) of epiweek 30, 2022 +end_date <- ymd("2023-07-29") # last day (saturday) of epiweek 30, 2023 (latest possible value) + + + +## read & prep infections +infections_raw <- read_combine_episim_output_zipped(directory,"infections.txt.csv") + +infections_incidence <- convert_infections_into_incidence(directory,infections_raw,FALSE) %>% + select(-c(infections_week,nShowingSymptomsCumulative, district, incidence)) + + +population_cologne <- infections_raw[1, "nSusceptible"] + +population_germany <- 83695430 #https://www.destatis.de/EN/Themes/Society-Environment/Population/Current-Population/Tables/liste-current-population.html +scale_factor <- population_germany / population_cologne # pop germany / pop koelln + +infections_ready <- infections_incidence %>% + filter(date >= origin_date & date <= end_date) %>% + # mutate(weekday = lubridate::wday(date, label = TRUE)) %>% + mutate(year = epiyear(date)) %>% + mutate(epiweek = epiweek(date)) %>% + group_by(seed,vacCamp,vacType,year,epiweek) %>% + summarise(value = sum(infections) * scale_factor, target_end_date = last(date) ) %>% + mutate(target_variable = "inc infection") %>% + select(year, epiweek, target_end_date, target_variable, value, seed, vacCamp, vacType) + + +## read & prep hospitalizations +hosp_raw <- read_combine_episim_output_zipped(directory,"post.hospital.tsv") + +hosp_ready <- hosp_raw %>% + filter(date >= origin_date & date <= end_date) %>% + mutate(year = year(date)) %>% + mutate(wkday = lubridate::wday(date, label = TRUE)) %>% + filter(wkday == "Sat") %>% + filter(measurement == "intakesHosp") %>% + filter(severity == "Omicron") %>% + mutate(epiweek = epiweek(date)) %>% + rename("target_end_date" = date, value = n) %>% + mutate(value = value * population_cologne / 100000 * scale_factor) %>% + mutate(target_variable = "inc hosp") %>% + select(year, epiweek, target_end_date, target_variable, value, seed, vacCamp, vacType) + +# combine two dataframes and modify columns to match specs +combined <- rbind(infections_ready, hosp_ready) +# combined <- infections_ready #todo revert + + + + +seed <- unique(combined$seed) +sample <- seq(length(seed)) +map <- data.frame(seed,sample) + +final <- combined %>% filter(vacCamp!="off") %>% + mutate(scenario_id = paste0(vacCamp,"_",vacType)) %>% + mutate(scenario_id = case_when(scenario_id == "60plus_omicronUpdate"~"A-2022-07-24", + scenario_id == "18plus_omicronUpdate"~"B-2022-07-24", + scenario_id == "60plus_mRNA"~"C-2022-07-24", + scenario_id == "18plus_mRNA"~"D-2022-07-24")) %>% + merge(map,by ="seed") %>% + mutate(horizon = case_when(year == 2022 ~ epiweek - 29, year == 2023~ (52-29) + epiweek)) %>% + mutate("origin_date" = "2022-07-24") %>% + mutate("location" = "DE") %>% + mutate(value = round(value)) %>% + select(origin_date,scenario_id, horizon, target_end_date, location, sample,target_variable, value) %>% + arrange(scenario_id,sample,horizon) %>% + mutate(horizon = paste0(horizon," wk")) + +write.csv(final,"/Users/jakob/git/covid19-scenario-hub-europe/data-processed/MODUS_Covid-Episim/2022-07-24-MODUS_Covid-Episim.csv", row.names = FALSE) + + + +# xxx <- read.delim("/Users/jakob/antibodies_2022-07-23.tsv",sep = "\t") +# +# yyy <- xxx %>% filter(nVaccinations == 0 & nInfections == 0) +# +# nrow(yyy)/nrow(xxx) + + + + + + diff --git a/src/main/R/analyseCases.R b/src/main/R/analyseCases.R new file mode 100644 index 000000000..e552fce58 --- /dev/null +++ b/src/main/R/analyseCases.R @@ -0,0 +1,89 @@ +library(lubridate) +library(tidyverse) +library(readr) + + + +rm(list=ls()) +source("/Users/jakob/git/matsim-episim/src/main/R/masterJR-utils.R", encoding = 'utf-8') + +# directory_snap_old <- "/Users/jakob/git/public-svn/matsim/scenarios/countries/de/episim/battery/jakob/2022-11-05/2-imm-snap/" +directory_base <- "/Users/jakob/git/public-svn/matsim/scenarios/countries/de/episim/battery/jakob/2022-11-24/1-makeImmHist/" +directory_imm <- "/Users/jakob/git/public-svn/matsim/scenarios/countries/de/episim/battery/jakob/2022-11-24/3-imm-20seeds/" +# file_root<- "antibodies.tsv" + +#infections +file_root_inf<- "infections.txt.csv" +snap_inf_raw <- read_combine_episim_output_zipped(directory_base, file_root_inf ) +# snap_inf_raw_old <- read_combine_episim_output_zipped(directory_snap, file_root_inf ) +imm_inf_raw <- read_combine_episim_output_zipped(directory_imm, file_root_inf) + +unique(snap_inf_raw$seed) + + +start_date <- ymd("2022-04-01") +end_date <- ymd("2022-09-01") +snap_inf <- snap_inf_raw %>% + filter(date >= start_date) %>% + filter(date <= end_date) %>% + filter(seed %in% unique(imm_inf_raw$seed)) + # filter(pHh == 0.0, immuneSigma == 0.0) + # mutate(vax = generic + mRNA + vector + ba1Update + ba5Update + natural) + + +imm_inf <- imm_inf_raw %>% + filter(date >= start_date) %>% + filter(date <= end_date) %>% + filter(StrainA == 2.0 & startFromImm =="sepSeeds") + + + # mutate(vax = generic + mRNA + vector + ba1Update + ba5Update + natural) +ggplot() + #nShowingSymptoms # SARS_CoV_2 + geom_line(imm_inf, mapping = aes(date, nShowingSymptoms , group = seed, col = "imm-hist")) + + geom_line(snap_inf, mapping = aes(date, nShowingSymptoms , group = seed, col = "snapshot")) + + scale_color_manual(name='Regression Model', + breaks=c('snapshot', 'imm-hist'), + values=c('snapshot'='red', 'imm-hist'='blue'))+ + labs(alt = "hello world") + + ggtitle("Infections") + + + +# antibodies +file_root_ab<- "antibodies.tsv" +snap_ab_raw <- read_combine_episim_output_zipped(directory_base, file_root_ab ) +imm_ab_raw <- read_combine_episim_output_zipped(directory_imm, file_root_ab) + + +start_date <- ymd("2021-11-15") +end_date <- ymd("2029-11-30") +snap_ab <- snap_ab_raw %>% + filter(date >= start_date) %>% + filter(date <= end_date) %>% + filter(pHh == 0.0, immuneSigma == 0.0) +# mutate(vax = generic + mRNA + vector + ba1Update + ba5Update + natural) +imm_ab <- imm_ab_raw %>% + filter(date >= start_date) %>% + filter(date <= end_date) %>% + filter(pHh == 0.0, immuneSigma == 0.0) +# mutate(vax = generic + mRNA + vector + ba1Update + ba5Update + natural) +ggplot() + #nShowingSymptoms # SARS_CoV_2 + geom_line(imm_ab, mapping = aes(date, SARS_CoV_2 , group = seed, col = "imm-hist")) + + geom_line(snap_ab, mapping = aes(date, SARS_CoV_2 , group = seed, col = "snapshot")) + + scale_color_manual(name='Regression Model', + breaks=c('snapshot', 'imm-hist'), + values=c('snapshot'='red', 'imm-hist'='blue'))+ + ggtitle("Antibodies") + + + + + # scale_colour_manual(name = "scenario", values = c("red"="red", "blue"="blue"), labels = c("snapshot", "immune history")) + + # facet_wrap(pHh ~ immuneSigma) + +# antibodies are a bit lower for immune history people +# jump in snapshot on first day is a bit sus... (maybe there is something wrong there..., not with immune history)) +# what happens to Antibodies from June 30 to July 1?? Why do they jump? + +# infections: now imm-Hist (blue) run has lower case numbers... too many antibodies or whats going on? +# but blue also has slightly lower antibodies. How can this be? \ No newline at end of file diff --git a/src/main/R/masterJR-utils.R b/src/main/R/masterJR-utils.R new file mode 100644 index 000000000..7d633dd3e --- /dev/null +++ b/src/main/R/masterJR-utils.R @@ -0,0 +1,621 @@ +# Title : TODO +# Objective : TODO +# Created by: jakob +# Created on: 7/22/2021 + +library(data.table) + + +# Functions: +# read_and_process_episim_infections <- function(directory, facilities_to_district_map) { +# fac_to_district_map <- read_delim(facilities_to_district_map, +# ";", escape_double = FALSE, col_names = FALSE, +# trim_ws = TRUE) %>% +# rename("facility" = "X1") %>% +# rename("district" = "X2") +# +# fac_to_district_map[is.na(fac_to_district_map)] <- "not_berlin" +# +# info_df <- read_delim(paste0(directory, "_info.txt"), delim = ";") +# +# # gathers column names that should be included in final dataframe +# col_names <- colnames(info_df) +# relevant_cols <- col_names[!col_names %in% c("RunScript", "RunId", "Config", "Output")] +# +# episim_df_all_runs <- data.frame() +# +# for (row in seq_len(nrow(info_df))) { +# +# runId <- info_df$RunId[row] +# seed <- info_df$seed[row] +# +# file_name <- paste0(directory, runId, ".infectionEvents.txt") +# +# if (!file.exists(file_name)) { +# warning(paste0(file_name, " does not exist")) +# next +# } +# +# df_for_run <- read_delim(file = file_name, +# "\t", escape_double = FALSE, trim_ws = TRUE) %>% +# select(date, facility) +# +# # adds important variables concerning run to df, so that individual runs can be filtered in later steps +# for (var in relevant_cols) { +# df_for_run[var] <- info_df[row, var] +# } +# +# episim_df_all_runs <- rbind(episim_df_all_runs, df_for_run) +# +# } +# +# episim_df2 <- episim_df_all_runs %>% filter(!grepl("^tr_", facility)) +# +# merged <- episim_df2 %>% +# left_join(fac_to_district_map, by = c("facility"), keep = TRUE) +# +# na_facs <- merged %>% +# filter(is.na(district)) %>% +# pull(facility.x) +# length(unique(na_facs)) +# +# episim_final <- merged %>% +# filter(!is.na(district)) %>% +# filter(district != "not_berlin") %>% +# select(!starts_with("facility")) %>% +# group_by_all() %>% +# count() %>% +# group_by(across(c(-n, -seed))) %>% +# summarise(infections = mean(n)) +# +# return(episim_final) +# } + +convert_infections_into_incidence <- function(directory, infections_raw, aggregate_seeds) { + run_params <- get_run_parameters(directory) + infections <- infections_raw %>% + select(date, nShowingSymptomsCumulative, nSusceptible, district, run_params) %>% + filter(district != "unknown") %>% + arrange(date) %>% + group_by(district, across(run_params)) %>% + mutate(infections_1dayAgo = lag(nShowingSymptomsCumulative, default = 0, order_by = date)) %>% + mutate(infections_7daysAgo = lag(nShowingSymptomsCumulative, default = 0, n = 7, order_by = date)) %>% + mutate(infections = nShowingSymptomsCumulative - infections_1dayAgo) %>% + mutate(infections_week = nShowingSymptomsCumulative - infections_7daysAgo) %>% + mutate(population = first(nSusceptible)) %>% + mutate(incidence = infections_week / population * 100000) %>% + ungroup() %>% + select(-c(population, infections_1dayAgo, infections_7daysAgo, nSusceptible)) %>% + arrange(date) + + if (aggregate_seeds == FALSE) { + return(infections) + } + + infections_aggregated <- infections %>% + group_by(across(-c(nShowingSymptomsCumulative, infections, infections_week, incidence, seed))) %>% + summarise(infections = mean(infections), + nShowingSymptomsCumulative = mean(nShowingSymptomsCumulative), + infections_week = mean(infections_week), + incidence = mean(incidence)) + return(infections_aggregated) + +} + + +geolocate_infections <- function(infections_raw, facilities_to_district_map) { + + # read facilities + fac_to_district_map <- read_delim(facilities_to_district_map, + ";", escape_double = FALSE, col_names = FALSE, + trim_ws = TRUE) %>% + rename("facility" = "X1") %>% + rename("district" = "X2") + + fac_to_district_map[is.na(fac_to_district_map)] <- "not_berlin" + + # filter, merge, & find count per district + merged <- infections_raw %>% + select(-c(time, infector, infected, infectionType, groupSize, virusStrain, probability)) %>% + filter(!grepl("^tr_", facility)) %>% + left_join(fac_to_district_map, by = c("facility"), keep = TRUE) %>% + filter(!is.na(district)) %>% + filter(district != "not_berlin") %>% + select(!starts_with("facility")) %>% + group_by_all() %>% + count() %>% + rename("infections" = "n") + + return(merged) + +} + + +# read_and_process_episim_timeUse <- function(directory) { +# +# info_df <- read_delim(paste0(directory, "_info.txt"), delim = ";") +# +# # gathers column names that should be included in final dataframe +# col_names <- colnames(info_df) +# relevant_cols <- col_names[!col_names %in% c("RunScript", "RunId", "Config", "Output")] +# +# episim_df_all_runs <- data.frame() +# for (row in seq_len(nrow(info_df))) { +# +# runId <- info_df$RunId[row] +# +# file_name <- paste0(directory, runId, ".timeUse.txt") +# +# if (!file.exists(file_name)) { +# warning(paste0(file_name, " does not exist")) +# next +# } +# +# df_for_run <- read_delim(file = file_name, +# "\t", escape_double = FALSE, trim_ws = TRUE) %>% +# pivot_longer(!c("day", "date"), names_to = "activity", values_to = "time") +# +# # adds important variables concerning run to df, so that individual runs can be filtered in later steps +# for (var in relevant_cols) { +# df_for_run[var] <- info_df[row, var] +# } +# +# episim_df_all_runs <- rbind(episim_df_all_runs, df_for_run) +# +# } +# } + +get_run_parameters <- function(directory) { + info_df <- read_delim(paste0(directory, "_info.txt"), delim = ";") + col_names <- colnames(info_df) + run_params <- col_names[!col_names %in% c("RunScript", "RunId", "Config", "Output")] + return(run_params) +} + +read_combine_episim_output <- function(directory, file_root, allow_missing_files) { + + info_df <- read_delim(paste0(directory, "_info.txt"), delim = ";") + + # gathers column names that should be included in final dataframe + col_names <- colnames(info_df) + relevant_cols <- col_names[!col_names %in% c("RunScript", "RunId", "Config", "Output")] + + episim_df_all_runs <- data.frame() + for (row in seq_len(nrow(info_df))) { + + runId <- info_df$RunId[row] + + file_name <- paste0(directory, runId, ".", file_root) + + if (!file.exists(file_name) & allow_missing_files) { + warning(paste0(file_name, " does not exist")) + next + } + + + df_for_run <- read_delim(file = file_name, "\t", escape_double = FALSE, trim_ws = TRUE) + + if (dim(df_for_run)[1] == 0) { + warning(paste0(file_name, " is empty")) + next + } + + # adds important variables concerning run to df, so that individual runs can be filtered in later steps + for (var in relevant_cols) { + df_for_run[var] <- info_df[row, var] + } + + episim_df_all_runs <- rbindlist(list(episim_df_all_runs, df_for_run)) + # episim_df_all_runs <- rbind(episim_df_all_runs, df_for_run) # inefficient + + } + + return(data.frame(episim_df_all_runs)) +} + +read_combine_episim_output_zipped <- function(directory, file_root) { + + info_df <- read_delim(paste0(directory, "_info.txt"), delim = ";") + + # gathers column names that should be included in final dataframe + col_names <- colnames(info_df) + relevant_cols <- col_names[!col_names %in% c("RunScript", "RunId", "Config", "Output")] + + episim_df_all_runs <- data.frame() + + # runsToInclude <- c("calibration1824","calibration288","calibration1056", "calibration2208","calibration672","calibration1440") + for (row in seq_len(nrow(info_df))) { + + runId <- info_df$RunId[row] + + # if(runId %in% runsToInclude){ + # + # } else{ + # next + # } + + + zipDir <- paste0(directory,"summaries/",runId,".zip") + + file_name <- paste0(runId, ".", file_root) + + df_for_run <- read_delim(unz(zipDir, file_name)) + + if (dim(df_for_run)[1] == 0) { + warning(paste0(file_name, " is empty")) + next + } + + # adds important variables concerning run to df, so that individual runs can be filtered in later steps + for (var in relevant_cols) { + df_for_run[var] <- info_df[row, var] + } + + episim_df_all_runs <- rbindlist(list(episim_df_all_runs, df_for_run)) + + } + + return(data.frame(episim_df_all_runs)) +} + + + +read_and_process_new_rki_data_incidenz <- function(filename) { + rki <- read_excel(filename, + sheet = "LK_7-Tage-Inzidenz (fixiert)", skip = 4) + + rki_berlin <- rki %>% + filter(grepl("berlin", LK, ignore.case = TRUE)) %>% + select(-c("...1", "LKNR")) %>% + pivot_longer(!contains("LK"), names_to = "date", values_to = "cases") + + for (i in seq_len(nrow(rki_berlin))) { + dateX <- as.character(rki_berlin$date[i]) + if (grepl("44", dateX)) { + date_improved <- as.character(excel_numeric_to_date(as.numeric(dateX))) + rki_berlin$date[i] <- date_improved + } else { + date_improved <- dateX + rki_berlin$date[i] <- date_improved + } + } + + ymd <- ymd(rki_berlin$date) + dmy <- dmy(rki_berlin$date) + ymd[is.na(ymd)] <- dmy[is.na(ymd)] # some dates are ambiguous, here we give + rki_berlin$date <- ymd + + + rki_berlin$LK <- rki_berlin$LK %>% + str_replace("SK Berlin ", "") %>% + str_replace("-", "_") %>% + str_replace("ö", "oe") + + + rki_berlin <- rki_berlin %>% + rename(district = LK, incidence = cases) + + return(rki_berlin) +} + +read_and_process_new_rki_data <- function(filename) { + rki <- read_excel(filename, + sheet = "LK_7-Tage-Fallzahlen (fixiert)", skip = 4) + + rki_berlin <- rki %>% + filter(grepl("berlin", LK, ignore.case = TRUE)) %>% + select(-c("...1", "LKNR")) %>% + pivot_longer(!contains("LK"), names_to = "date", values_to = "cases") + + for (i in seq_len(nrow(rki_berlin))) { + dateX <- as.character(rki_berlin$date[i]) + if (grepl("44", dateX)) { + date_improved <- as.character(excel_numeric_to_date(as.numeric(dateX))) + rki_berlin$date[i] <- date_improved + } else { + date_improved <- dateX + rki_berlin$date[i] <- date_improved + } + } + + ymd <- ymd(rki_berlin$date) + dmy <- dmy(rki_berlin$date) + ymd[is.na(ymd)] <- dmy[is.na(ymd)] # some dates are ambiguous, here we give + rki_berlin$date <- ymd + + + rki_berlin$LK <- rki_berlin$LK %>% + str_replace("SK Berlin ", "") %>% + str_replace("-", "_") %>% + str_replace("ö", "oe") + + glimpse(rki_berlin) + + rki_berlin <- rki_berlin %>% + mutate(cases = cases / 7) %>% + rename(district = LK, rki_new = cases) + + return(rki_berlin) +} + +read_and_process_old_rki_data <- function(filename) { + rki_old <- read_csv(filename) + rki_berlin_old <- rki_old %>% + filter(grepl("berlin", Landkreis, ignore.case = TRUE)) %>% + mutate(date = as.Date(Refdatum, format = "%m/%d/%Y"), district = Landkreis) %>% ## TODO: RefDatum or Meldedaturm + select(district, AnzahlFall, date) %>% + group_by(district, date) %>% + summarise(rki_cases_old = sum(AnzahlFall)) %>% + mutate(district = str_replace(district, "SK Berlin ", "")) %>% + mutate(district = str_replace(district, "-", "_")) %>% + mutate(district = str_replace(district, "ö", "oe")) %>% + rename(rki_old = rki_cases_old) + return(rki_berlin_old) +} + +merge_tidy_average <- function(episim_all_runs, rki_new, rki_old) { + # BEFORE MERGING: + # each dataset should have "date", "district" + a seperate column per situation (with good name) + # delete all other columns! + merged_tidy <- episim_all_runs %>% + full_join(rki_new, by = c("date", "district")) %>% + full_join(rki_old, by = c("date", "district")) %>% + pivot_longer(!c("date", "district"), names_to = "scenario", values_to = "infections") + + merged_weekly <- merged_tidy %>% + mutate(week = isoweek(date)) %>% # iso 8601 standard: week always begins with Monday; week 1 must contains January 4th + mutate(year = isoyear(date)) %>% + group_by(across(-c(infections, date))) %>% + summarise(infections = mean(infections, na.rm = TRUE), date = mean(date, na.rm = TRUE)) %>% + ungroup() %>% + select(!c(week, year)) + + return(merged_weekly) + +} + +### Plotting functions +build_plot <- function(df, scale_colors) { + plot <- ggplot(df) + + geom_line(aes(date, incidence, col = Scenario)) + + theme_minimal(base_size = 11) + + theme(legend.position = "bottom", axis.text.x = element_text(angle = 45, hjust=1)) + + labs(x = "Date", y = "7-Day Infections / 100k Pop.") + + scale_x_date(date_breaks = "2 month", date_labels = "%b-%y") + + facet_wrap(~district, ncol = 3) + + scale_color_manual(values = scale_colors) + + print(plot) + + return(plot) +} + +save_png_pdf <- function(.data, name) { + # print(.data) + ggsave(.data, filename = paste0(name, ".png"), path = gbl_image_output, width = 16, height = 12, units = "cm") + ggsave(.data, filename = paste0(name, ".pdf"), path = gbl_image_output, width = 16, height = 12, units = "cm") +} + + +### TIMELINE FUNCTIONS FOR ADAPTIVE RESTRICTIONS +make_timeline <- function(ar_filtered, title) { + timeline_data <- data.frame() + + locations <- unique(ar_filtered$location) + + for (loc in locations) { + + policy_filtered <- ar_filtered %>% filter(location == loc) + + timeline_data_district <- data.frame() + policy <- policy_filtered$policy[1] + start <- policy_filtered$date[1] + + for (row in seq(2, nrow(policy_filtered))) { + + if (policy_filtered$policy[row] == policy) { + next + } + + end <- policy_filtered$date[row] + single <- data.frame(location = loc, policy = policy, start = start, end = end) + timeline_data_district <- rbind(timeline_data_district, single) + + start <- end + policy <- policy_filtered$policy[row] + + } + + end <- policy_filtered$date[row] + + if (start != end) { + single <- data.frame(location = loc, policy = policy, start = start, end = end) + timeline_data_district <- rbind(timeline_data_district, single) + } + + timeline_data <- rbind(timeline_data, timeline_data_district) + } + + + timeline_data_final <- timeline_data %>% + mutate(location = str_replace(location, "_", "-")) %>% + mutate(color = policy) %>% + mutate(color = str_replace(color, "initial", "gray")) %>% + mutate(color = str_replace(color, "restricted", "indianred1")) %>% + mutate(color = str_replace(color, "open", "royalblue1")) + + + # following was adapted from https://github.com/wlhamilton/Patient-ward-movement-timelines/blob/main/R%20script%20for%20formatting%20ward%20movement%20data.R + + plot_data <- gg_vistime(data = timeline_data_final, col.group = "location", col.event = "policy", col.start = "start", col.end = "end", col.color = "color", show_labels = FALSE) + #theme_bw() + + ggplot2::theme( + plot.title = element_text(size = 14), + axis.text.x = element_text(size = 12, color = "black", angle = 30, vjust = 1, hjust = 1), + axis.text.y = element_text(size = 12, color = "black")) + + scale_x_datetime(breaks = breaks_width("1 month"), labels = date_format("%b %y")) + + labs(title = title) + + return(plot_data) + +} + +### SETUP TIMELINE DATA +make_timeline_data <- function(ar_filtered) { + timeline_data <- data.frame() + + locations <- unique(ar_filtered$location) + + for (loc in locations) { + + policy_filtered <- ar_filtered %>% filter(location == loc) + + timeline_data_district <- data.frame() + policy <- policy_filtered$policy[1] + start <- policy_filtered$date[1] + + for (row in seq(2, nrow(policy_filtered))) { + + if (policy_filtered$policy[row] == policy) { + next + } + + end <- policy_filtered$date[row] + single <- data.frame(location = loc, policy = policy, start = start, end = end) + timeline_data_district <- rbind(timeline_data_district, single) + + start <- end + policy <- policy_filtered$policy[row] + + } + + end <- policy_filtered$date[row] + + if (start != end) { + single <- data.frame(location = loc, policy = policy, start = start, end = end) + timeline_data_district <- rbind(timeline_data_district, single) + } + + timeline_data <- rbind(timeline_data, timeline_data_district) + } + + + timeline_data_final <- timeline_data %>% + mutate(location = str_replace(location, "_", "-")) %>% + mutate(color = policy) %>% + mutate(color = str_replace(color, "initial", "gray")) %>% + mutate(color = str_replace(color, "restricted", "indianred1")) %>% + mutate(color = str_replace(color, "open", "royalblue1")) + + return(timeline_data_final) +} + +make_legend <- function() { + data_legend <- data.frame(policy = c("Restriction Policy:", "Initial", "Restricted", "Open"), color = c(rgb(0, 0, 0, 0), "gray", "indianred1", "royalblue1")) + data_legend$start <- c(as.Date("2020-01-01"), as.Date("2020-01-07"), as.Date("2020-01-13"), as.Date("2020-01-19")) + data_legend$end <- c(as.Date("2020-01-06"), as.Date("2020-01-12"), as.Date("2020-01-18"), as.Date("2020-01-24")) + data_legend + plot_legend <- gg_vistime(data = data_legend, + col.event = "policy", + col.color = "color", + show_labels = TRUE, + linewidth = 20, + title = "Legend") + plot_legend + + # Tweak the legend plot + plot_legend <- plot_legend + + theme_void() + + ggplot2::theme( + # plot.title = element_text(size = 11), + plot.title = element_blank(), + # plot.title.position = "plot", + axis.title.x = element_blank(), + axis.text.x = element_blank(), + axis.ticks.x = element_blank(), + axis.title.y = element_blank(), + axis.text.y = element_blank(), + axis.ticks.y = element_blank()) + + return(plot_legend) +} + +make_text <- function(ar) { + + + lockdown_weeks <- ar %>% + group_by(location,policy) %>% + count() %>% + pivot_wider(names_from = policy, values_from = n, values_fill = 0) %>% + mutate(weeks_restricted = paste0(round(restricted / 7, 1), " wks")) %>% + arrange(location) + + + # data = timeline_data_final, col.group = "location", + lockdown_weeks$color <- rgb(0, 0, 0, 0) + lockdown_weeks$start <- c(as.Date("2020-01-1")) + lockdown_weeks$end <- c(as.Date("2020-01-2")) + lockdown_weeks + plot_text <- gg_vistime(data = lockdown_weeks, + col.group = "location", + col.event = "weeks_restricted", + col.color = "color", + show_labels = TRUE, + linewidth = 20, + title = "Lockdown") + + # Tweak the legend plot + plot_text <- plot_text + + ggplot2::theme( + plot.title = element_text(size = 14), + axis.ticks.length = unit(0, "in"), + axis.text.x = element_text(size = 12, color = rgb(0, 0, 0, 0), angle = 30, vjust = 1, hjust = 1), + axis.text.y = element_blank(), + axis.ticks = element_blank(), + axis.title.y = element_blank(), + axis.line.y = element_blank(), + ) + + scale_x_datetime(breaks = breaks_width("1 month"), labels = date_format("%b %y")) + + return(plot_text) + + + make_gt_table <- function(.data) { + gt(.data, rowname_col = "Rf") %>% + tab_stubhead(label = "Rf") %>% + tab_spanner( + label = "Trigger", + columns = trigs + ) %>% + fmt_number( + columns = trigs, + decimals = 1, + use_seps = FALSE + ) + } + + localMaxima <- function(x) { + # Use -Inf instead if x is numeric (non-integer) + y <- diff(c(-.Machine$integer.max, x)) > 0L + rle(y)$lengths + y <- cumsum(rle(y)$lengths) + y <- y[seq.int(1L, length(y), 2L)] + if (x[[1]] == x[[2]]) { + y <- y[-1] + } + y + } + + + localMinima <- function(x) { + # Use -Inf instead if x is numeric (non-integer) + y <- diff(c(.Machine$integer.max, x)) < 0L + rle(y)$lengths + y <- cumsum(rle(y)$lengths) + y <- y[seq.int(1L, length(y), 2L)] + if (x[[1]] == x[[2]]) { + y <- y[-1] + } + y + } +} + diff --git a/src/main/R/mobilityAndWeather.R b/src/main/R/mobilityAndWeather.R new file mode 100644 index 000000000..7cc6fd72b --- /dev/null +++ b/src/main/R/mobilityAndWeather.R @@ -0,0 +1,68 @@ +library(tidyverse) +library(lubridate) +library (RCurl) +library(gridExtra) + + +# Weather Data +weatherData <- read_delim("https://bulk.meteostat.net/daily/10382.csv.gz", delim = ",", col_names = FALSE, col_types = cols( + X1 = col_date(format = ""), + X2 = col_double(), + X3 = col_double(), + X4 = col_double(), + X5 = col_double(), + X6 = col_double(), + X7 = col_double(), + X8 = col_double(), + X9 = col_double(), + X10 = col_double(), + X11 = col_double() +)) +colnames(weatherData) <- c("date", "tavg", "tmin", "tmax", "prcp", "snow", "wdir", "wspd", "wpgt", "pres", "tsun") + +weatherDataByWeek <- weatherData %>% + mutate( week = paste0(isoweek(date), "-", isoyear(date))) %>% + group_by( week ) %>% + summarize( date=mean(date), tavg=mean(tavg), tmin=mean(tmin), tmax=mean(tmax), prcp=mean(prcp), snow=mean(snow), wdir=mean(wdir), wspd=mean(wspd), wpgt=mean(wpgt), pres=mean(pres), tsun=mean(tsun)) + + +# Google Mobility Report +googleMobilityReportRaw <- read_csv(getURL("https://www.gstatic.com/covid19/mobility/Global_Mobility_Report.csv")) + +googleMobilityReport <- googleMobilityReportRaw %>% + filter(sub_region_1 == "Berlin") %>% + select(-c(country_region_code, country_region, sub_region_1, sub_region_2, metro_area, iso_3166_2_code, census_fips_code, place_id)) %>% + mutate(notAtHome = -residential_percent_change_from_baseline) %>% + pivot_longer(!date, names_to = "type", values_to = "restriction") %>% + filter(type == "notAtHome") %>% + mutate(weekday = wday(date, week_start = 1)) %>% + filter(weekday < "6") %>% + mutate( week = paste0(isoweek(date), "-", isoyear(date))) %>% + group_by( week, type ) %>% + summarize( restriction=mean(restriction), date=mean(date)) + +# Senozon Restrictions +snzRestrictionsFile <- "BerlinSnzData_daily_until20220204.csv" +svnLocation <- "/Users/sebastianmuller/git/shared-svn/projects/episim/matsim-files/snz/BerlinV2/episim-input/" + +snzRestrictions <- read_delim(paste0(svnLocation, snzRestrictionsFile), delim = "\t") %>% + pivot_longer(!date, names_to = "type", values_to = "restriction") %>% + mutate(newDate = as.Date(strptime(date, "%Y%m%d"))) %>% + mutate(weekday = wday(newDate, week_start = 1)) %>% + filter(weekday < "6") %>% + filter(type == "notAtHome") %>% + mutate( week = paste0(isoweek(newDate), "-", isoyear(newDate))) %>% + group_by( week, type ) %>% + summarize( restriction=mean(restriction), newDate=mean(newDate)) + +# Google Mobility and weather +ggplot() + + geom_point(data = googleMobilityReport, mapping=aes(x = date, y = restriction), colour = "red") + + geom_point(data = snzRestrictions, mapping=aes(x = newDate, y = restriction), colour = "blue") + + geom_point(data = weatherDataByWeek, mapping=aes(x = date, y = tmax), colour = "black") + + theme(legend.position = "bottom") + + xlim(c(as.Date("2020-02-24"), as.Date("2022-07-30"))) + + labs( + title="Time spent not at home (google and senozon) and max temperature (Berlin)", + caption="Source: Google, Senozon", + x="date", y="Reduction in % / tmax") diff --git a/src/main/java/org/matsim/episim/EpisimConfigGroup.java b/src/main/java/org/matsim/episim/EpisimConfigGroup.java index ebe364d1a..d0e5be474 100644 --- a/src/main/java/org/matsim/episim/EpisimConfigGroup.java +++ b/src/main/java/org/matsim/episim/EpisimConfigGroup.java @@ -66,12 +66,11 @@ public final class EpisimConfigGroup extends ReflectiveConfigGroup { private static final String START_DATE = "startDate"; private static final String SNAPSHOT_INTERVAL = "snapshotInterval"; private static final String START_FROM_SNAPSHOT = "startFromSnapshot"; + private static final String START_FROM_IMMUNIZATION = "startFromImmunization"; private static final String SNAPSHOT_PREFIX = "snapshotPrefix"; private static final String SNAPSHOT_SEED = "snapshotSeed"; private static final String LEISUREOUTDOORFRACTION = "leisureOutdoorFraction"; private static final String INPUT_DAYS = "inputDays"; - private static final String AGE_SUSCEPTIBILITY = "ageSusceptibility"; - private static final String AGE_INFECTIVITY = "ageInfectivity"; private static final String DAYS_INFECTIOUS = "daysInfectious"; private static final String ACTIVITY_HANDLING = "activityHandling"; private static final String THREADS = "threads"; @@ -143,6 +142,7 @@ public final class EpisimConfigGroup extends ReflectiveConfigGroup { * Path to snapshot file. */ private String startFromSnapshot = null; + private String startFromImmunization = null; /** * Filename prefix for snapshot. @@ -167,23 +167,7 @@ public final class EpisimConfigGroup extends ReflectiveConfigGroup { private SingleEventFile singleEventFile = SingleEventFile.yes; private boolean endEarly = false; private int threads = 2; - /** - * Child susceptibility used in AgeDependentInfectionModelWithSeasonality. - * Taken from https://doi.org/10.1101/2020.06.03.20121145 - */ - private final NavigableMap ageSusceptibility = new TreeMap<>(Map.of( - 19, 0.45, - 20, 1d - )); - /** - * Child infectivity used in AgeDependentInfectionModelWithSeasonality. - * Taken from https://doi.org/10.1101/2020.06.03.20121145 - */ - private final NavigableMap ageInfectivity = new TreeMap<>(Map.of( - 19, 0.85, - 20, 1d - )); /** * Compliance if a curfew is set. @@ -389,6 +373,16 @@ public void setStartFromSnapshot(String startFromSnapshot) { this.startFromSnapshot = startFromSnapshot; } + @StringGetter(START_FROM_IMMUNIZATION) + public String getStartFromImmunization() { + return startFromImmunization; + } + + @StringSetter(START_FROM_IMMUNIZATION) + public void setStartFromImmunization(String startFromImmunization) { + this.startFromImmunization = startFromImmunization; + } + @StringGetter(SNAPSHOT_PREFIX) public String getSnapshotPrefix() { return snapshotPrefix; @@ -543,62 +537,6 @@ public void setDaysInfectious(int daysInfectious) { this.daysInfectious = daysInfectious; } - @StringGetter(AGE_SUSCEPTIBILITY) - String getAgeSusceptibilityString() { - return JOINER.join(ageSusceptibility); - } - - @StringSetter(AGE_SUSCEPTIBILITY) - void setAgeSusceptibility(String config) { - Map map = SPLITTER.split(config); - setAgeSusceptibility(map.entrySet().stream().collect(Collectors.toMap( - e -> Integer.parseInt(e.getKey()), e -> Double.parseDouble(e.getValue()) - ))); - } - - /** - * Return susceptibility for different age groups. - */ - public NavigableMap getAgeSusceptibility() { - return ageSusceptibility; - } - - /** - * Set susceptibility for all age groups, previous entries will be overwritten. - */ - public void setAgeSusceptibility(Map ageSusceptibility) { - this.ageSusceptibility.clear(); - this.ageSusceptibility.putAll(ageSusceptibility); - } - - @StringGetter(AGE_INFECTIVITY) - String getAgeInfectivityString() { - return JOINER.join(ageInfectivity); - } - - @StringSetter(AGE_INFECTIVITY) - void setAgeInfectivity(String config) { - Map map = SPLITTER.split(config); - setAgeInfectivity(map.entrySet().stream().collect(Collectors.toMap( - e -> Integer.parseInt(e.getKey()), e -> Double.parseDouble(e.getValue()) - ))); - } - - /** - * Return infectivity for different age groups. - */ - public NavigableMap getAgeInfectivity() { - return ageInfectivity; - } - - /** - * Set infectivity for all age groups, previous entries will be overwritten. - */ - public void setAgeInfectivity(Map ageSusceptibility) { - this.ageInfectivity.clear(); - this.ageInfectivity.putAll(ageSusceptibility); - } - /** * Sets the leisure outdoor fraction for the whole simulation period. */ diff --git a/src/main/java/org/matsim/episim/EpisimPerson.java b/src/main/java/org/matsim/episim/EpisimPerson.java index 92e5453a6..197c1ebf4 100644 --- a/src/main/java/org/matsim/episim/EpisimPerson.java +++ b/src/main/java/org/matsim/episim/EpisimPerson.java @@ -49,8 +49,7 @@ import java.util.function.BiFunction; import java.util.stream.Collectors; -import static org.matsim.episim.EpisimUtils.readChars; -import static org.matsim.episim.EpisimUtils.writeChars; +import static org.matsim.episim.EpisimUtils.*; /** * Persons current state in the simulation. @@ -156,7 +155,7 @@ public final class EpisimPerson implements Immunizable, Attributable { /** * Iteration when this person got into quarantine. Negative if person was never quarantined. */ - private int quarantineDate = -1; + private int quarantineDate = Integer.MIN_VALUE; /** * Iteration when this person was tested. Negative if person was never tested. @@ -208,10 +207,15 @@ public final class EpisimPerson implements Immunizable, Attributable { */ private final Object2DoubleMap antibodies = new Object2DoubleOpenHashMap<>(); + /** + * Maximal antibody level reached by agent w/ respect to each strain + */ + private final Object2DoubleMap maxAntibodies = new Object2DoubleOpenHashMap<>(); + /** * Antibody level at last infection. */ - private double antibodyLevelAtInfection = 0; + private double antibodyLevelAtInfection = 0.; /** * Immune response multiplier, which is used to scale the antibody increase due to an immunity event @@ -304,6 +308,12 @@ void read(ObjectInput in, Map, EpisimPerson> persons) throws IOExcept antibodies.put(strain, in.readDouble()); } + n = in.readInt(); + for (int i = 0; i < n; i++) { + VirusStrain strain = VirusStrain.values()[in.readInt()]; + maxAntibodies.put(strain, in.readDouble()); + } + status = DiseaseStatus.values()[in.readInt()]; quarantineStatus = QuarantineStatus.values()[in.readInt()]; quarantineDate = in.readInt(); @@ -371,6 +381,12 @@ void write(ObjectOutput out) throws IOException { out.writeDouble(kv.getDoubleValue()); } + out.writeInt(maxAntibodies.size()); + for (Object2DoubleMap.Entry kv : maxAntibodies.object2DoubleEntrySet()) { + out.writeInt(kv.getKey().ordinal()); + out.writeDouble(kv.getDoubleValue()); + } + out.writeInt(status.ordinal()); out.writeInt(quarantineStatus.ordinal()); out.writeInt(quarantineDate); @@ -411,7 +427,7 @@ public void setDiseaseStatus(double now, DiseaseStatus status) { */ public void setInitialInfection(double now, VirusStrain strain) { - reporting.reportInfection(new EpisimInitialInfectionEvent(now, getPersonId(), strain, antibodies.getDouble(strain))); + reporting.reportInfection(new EpisimInitialInfectionEvent(now, getPersonId(), strain, antibodies.getDouble(strain), maxAntibodies.getDouble(strain),getNumInfections())); virusStrains.add(strain); setDiseaseStatus(now, EpisimPerson.DiseaseStatus.infectedButNotContagious); @@ -419,6 +435,7 @@ public void setInitialInfection(double now, VirusStrain strain) { antibodyLevelAtInfection = antibodies.getDouble(strain); + // TODO: add max antibodies } /** @@ -576,6 +593,32 @@ public double getAntibodyLevelAtInfection() { return antibodyLevelAtInfection; } + /** + * get map with max antibodies reached per strain (before current infection) + */ + public Object2DoubleMap getMaxAntibodies() { + return maxAntibodies; + } + + /** + * Get max antibodies reached for a particular strain (before current infection) + */ + public double getMaxAntibodies(VirusStrain virusStrain) { + return maxAntibodies.getDouble(virusStrain); + } + + /** + * Updates maximum antibodies agent has had versus a particular strain (only if maxAb is in fact higher + * than previous maximum) + */ + public void updateMaxAntibodies(VirusStrain strain, double maxAb) { + + if (!this.maxAntibodies.containsKey(strain) || maxAb > this.maxAntibodies.getDouble(strain)) { + this.maxAntibodies.put(strain, maxAb); + } + + } + public double getAntibodies(VirusStrain strain) { return antibodies.getDouble(strain); } @@ -585,7 +628,9 @@ public Object2DoubleMap getAntibodies() { } public double setAntibodies(VirusStrain strain, double value) { + return antibodies.put(strain, value); + } /** @@ -653,10 +698,15 @@ public boolean hadStrain(VirusStrain strain) { */ @Beta public int daysSinceQuarantine(int currentDay) { - // yyyy since this API is so unstable, I would prefer to have the class non-public. kai, apr'20 // -> api now marked as unstable and containing an api note, because it is used by the models it has to be public. chr, apr'20 - if (quarantineDate < 0) throw new IllegalStateException("Person was never quarantined"); + + //check removed; when starting simulation with immunisation history, quarantine date can very well be negative. -jr, nov'22 + if (quarantineDate == Integer.MIN_VALUE) { + + throw new IllegalStateException("Person was never quarantined"); + + } return currentDay - quarantineDate; } @@ -1046,6 +1096,7 @@ public PerformedActivity getNextActivity(DayOfWeek day, double time) { return null; } + /** * Disease status of a person. */ diff --git a/src/main/java/org/matsim/episim/EpisimReporting.java b/src/main/java/org/matsim/episim/EpisimReporting.java index 93c612905..633b25754 100644 --- a/src/main/java/org/matsim/episim/EpisimReporting.java +++ b/src/main/java/org/matsim/episim/EpisimReporting.java @@ -29,6 +29,8 @@ import it.unimi.dsi.fastutil.objects.ObjectIntPair; import org.apache.commons.compress.archivers.tar.TarArchiveEntry; import org.apache.commons.compress.archivers.tar.TarArchiveOutputStream; +import org.apache.commons.csv.CSVFormat; +import org.apache.commons.csv.CSVPrinter; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.matsim.api.core.v01.Id; @@ -50,6 +52,7 @@ import java.nio.file.*; import java.text.DecimalFormat; import java.text.NumberFormat; +import java.time.LocalDate; import java.util.*; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.atomic.AtomicBoolean; @@ -64,6 +67,23 @@ */ public final class EpisimReporting implements BasicEventHandler, Closeable, Externalizable { + + /** + * Age groups used for various outputs. AgeGroup -> minimum age of age group. + * Important: age groups must be in descending order + */ + public enum AgeGroup { + age_60_plus(60), + age_18_59(18), + age_12_17(12), + age_0_11(0); + + public final int lowerBoundAge; + + AgeGroup(int lowerBoundAge) { + this.lowerBoundAge = lowerBoundAge; + } + } private static final Logger log = LogManager.getLogger(EpisimReporting.class); private static final AtomicInteger specificInfectionsCnt = new AtomicInteger(300); @@ -100,7 +120,7 @@ public final class EpisimReporting implements BasicEventHandler, Closeable, Exte public final Object2IntMap vaccinations = new Object2IntOpenHashMap<>(); /** - * Map of (VaccinationType, nth Vaccination) -> Number per day + * Map of (VaccinationType, nth Vaccination) -> Number per day */ public final Object2IntMap> vaccinationStats = new Object2IntOpenHashMap<>(); @@ -110,6 +130,8 @@ public final class EpisimReporting implements BasicEventHandler, Closeable, Exte private final NumberFormat decimalFormat = DecimalFormat.getInstance(Locale.GERMAN); private final double sampleSize; + private int totalContacts; + /** * Whether all events are written into one file. */ @@ -126,7 +148,6 @@ public final class EpisimReporting implements BasicEventHandler, Closeable, Exte private final ByteArrayOutputStream os; - private final Config config; private final EpisimConfigGroup episimConfig; private final VaccinationConfigGroup vaccinationConfig; @@ -147,6 +168,8 @@ public final class EpisimReporting implements BasicEventHandler, Closeable, Exte private BufferedWriter vaccinationsPerType; private BufferedWriter vaccinationsPerTypeAndNumber; + private final Map externalWriters = new HashMap<>(); + private String memorizedDate = null; /** @@ -205,7 +228,7 @@ public final class EpisimReporting implements BasicEventHandler, Closeable, Exte outdoorFraction = EpisimWriter.prepare(base + "outdoorFraction.tsv", "day", "date", "outdoorFraction"); virusStrains = EpisimWriter.prepare(base + "strains.tsv", "day", "date", (Object[]) VirusStrain.values()); cpuTime = EpisimWriter.prepare(base + "cputime.tsv", "iteration", "where", "what", "when", "thread"); - antibodiesPerPerson = EpisimWriter.prepare(base + "antibodies.tsv", "day", "date", (Object[]) VirusStrain.values()); + antibodiesPerPerson = EpisimWriter.prepare(base + "antibodies.tsv", "day", "date", (Object[]) VirusStrain.values()); vaccinationsPerType = EpisimWriter.prepare(base + "vaccinations.tsv", "day", "date", (Object[]) VaccinationType.values()); vaccinationsPerTypeAndNumber = EpisimWriter.prepare(base + "vaccinationsDetailed.tsv", "day", "date", "type", "number", "amount"); @@ -263,7 +286,7 @@ void append(String date) throws IOException { // Copy non prefixed files to base output if (!base.equals(outDir)) for (String file : List.of("infections.txt", "infectionEvents.txt", "restrictions.txt", "timeUse.txt", "diseaseImport.tsv", - "outdoorFraction.tsv", "strains.tsv", "antibodies.tsv", "vaccinations.tsv", "vaccinationsDetailed.tsv", "events.tar")) { + "outdoorFraction.tsv", "strains.tsv", "antibodies.tsv", "vaccinations.tsv", "vaccinationsDetailed.tsv", "events.tar")) { Path path = Path.of(outDir, file); if (Files.exists(path)) { Files.move(path, Path.of(base + file), StandardCopyOption.REPLACE_EXISTING); @@ -292,6 +315,35 @@ void append(String date) throws IOException { writeConfigFiles(); } + /** + * Create and registered a new writer. Note that this writer should not be used directly, instead use {@link #writeAsync(BufferedWriter, String)} + */ + public BufferedWriter registerWriter(String filename) { + + if (externalWriters.containsKey(filename)) + throw new IllegalStateException("Writer already registered: " + filename); + + BufferedWriter writer = EpisimWriter.prepare(base + filename); + externalWriters.put(filename, writer); + + return writer; + } + + /** + * Appends some content asynchronously to a writer in thread-safe manner. + */ + public void writeAsync(BufferedWriter writer, String content) { + this.writer.append(writer, content); + } + + /** + * Close writer asynchronously. + */ + public void closeAsync(BufferedWriter writer) { + externalWriters.values().removeIf(next -> next == writer); + this.writer.close(writer); + } + /** * Checks whether a person is vaccinated (and has full effectiveness). */ @@ -670,6 +722,17 @@ public synchronized void reportContact(double now, EpisimPerson person, EpisimPe } + /** + * Set number of total contacts. + * @param totalContacts + */ + public void reportTotalContacts(int totalContacts) { + this.totalContacts = totalContacts; + } + + public int getTotalContacts() { + return totalContacts; + } /** * Report the successful tracing between two persons. @@ -754,7 +817,7 @@ void reportVaccination(Id personId, int iteration, VaccinationType type, * Write container statistic to file. */ void reportContainerUsage(Object2IntMap> maxGroupSize, Object2IntMap> totalUsers, - Map, Object2IntMap> activityUsage) { + Map, Object2IntMap> activityUsage) { BufferedWriter out = EpisimWriter.prepare(base + "containerUsage.txt.gz", "id", "types", "totalUsers", "maxGroupSize"); @@ -798,12 +861,48 @@ void reportAntibodyLevel(Object2DoubleMap antibodies, int n, int it out[1] = date; for (int i = 0; i < VirusStrain.values().length; i++) { - out[i+2] = String.valueOf(antibodies.getDouble(VirusStrain.values()[i]) / n); + out[i + 2] = String.valueOf(antibodies.getDouble(VirusStrain.values()[i]) / n); } writer.append(antibodiesPerPerson, out); } + /** + * Write detailed person information. Huge files and for debugging only. + */ + void reportDetailedPersonStats(LocalDate date, Collection persons) { + + try (CSVPrinter csv = new CSVPrinter(Files.newBufferedWriter(Path.of(base + "antibodies_" + date + ".tsv")), CSVFormat.TDF)) { + + csv.print("personId"); + csv.print("age"); + csv.print("nVaccinations"); + csv.print("nInfections"); + csv.print("immuneResponseMultiplier"); + + for (VirusStrain strain : VirusStrain.values()) { + csv.print(strain.toString()); + } + csv.println(); + + for (EpisimPerson person : persons) { + csv.print(person.getPersonId().toString()); + csv.print(person.getAge()); + csv.print(person.getNumVaccinations()); + csv.print(person.getNumInfections()); + csv.print(person.getImmuneResponseMultiplier()); + + for (VirusStrain strain : VirusStrain.values()) { + csv.print(person.getAntibodies(strain)); + } + csv.println(); + + } + } catch (IOException e) { + e.printStackTrace(); + } + } + /** * Write outdoor fraction for each day. */ @@ -835,6 +934,10 @@ synchronized void reportCpuTime(int iteration, String where, String what, int ta String.valueOf(taskId)}); } + void reportStart(LocalDate startDate, String startFromImmunization) { + manager.processEvent(new EpisimStartEvent(startDate, startFromImmunization)); + } + @Override public void close() { @@ -850,6 +953,10 @@ public void close() { writer.close(vaccinationsPerType); writer.close(vaccinationsPerTypeAndNumber); + for (BufferedWriter v : externalWriters.values()) { + writer.close(v); + } + if (singleEvents) { try { zipOut.close(); @@ -867,11 +974,11 @@ public void close() { public void handleEvent(Event event) { // Events on 0th day are not needed - if (iteration == 0) return; + if (iteration == 0 && !(event instanceof EpisimStartEvent)) return; // Crucial episim events are always written, others only if enabled - if (event instanceof EpisimPersonStatusEvent || event instanceof EpisimInfectionEvent || event instanceof EpisimVaccinationEvent || event instanceof EpisimPotentialInfectionEvent || - event instanceof EpisimInitialInfectionEvent + if (event instanceof EpisimPersonStatusEvent || event instanceof EpisimInfectionEvent || event instanceof EpisimVaccinationEvent || event instanceof EpisimPotentialInfectionEvent + || event instanceof EpisimInitialInfectionEvent || event instanceof EpisimStartEvent || (writeEvents == EpisimConfigGroup.WriteEvents.tracing && event instanceof EpisimTracingEvent) || (writeEvents == EpisimConfigGroup.WriteEvents.tracing && event instanceof EpisimContactEvent)) { @@ -1001,7 +1108,7 @@ public void readExternal(ObjectInput in) throws IOException { enum InfectionsWriterFields { time, day, date, nSusceptible, nSusceptibleVaccinated, nInfectedButNotContagious, nInfectedButNotContagiousVaccinated, nContagious, nContagiousVaccinated, nShowingSymptoms, nShowingSymptomsVaccinated, nSeriouslySick, nSeriouslySickVaccinated, nCritical, nCriticalVaccinated, nTotalInfected, nTotalInfectedVaccinated, nInfectedCumulative, nInfectedCumulativeVaccinated, nContagiousCumulative, nContagiousCumulativeVaccinated, nShowingSymptomsCumulative, nShowingSymptomsCumulativeVaccinated, nSeriouslySickCumulative, nSeriouslySickCumulativeVaccinated, nCriticalCumulative, - nCriticalCumulativeVaccinated, nRecovered, nRecoveredVaccinated, nInQuarantineFull, nInQuarantineHome, nVaccinated, nReVaccinated, nTested, nDeceasedCumulative, nDeceasedCumulativeVaccinated, district + nCriticalCumulativeVaccinated, nRecovered, nRecoveredVaccinated, nInQuarantineFull, nInQuarantineHome, nVaccinated, nReVaccinated, nTested, nDeceasedCumulative, nDeceasedCumulativeVaccinated, district } enum InfectionEventsWriterFields {time, infector, infected, infectionType, date, groupSize, facility, virusStrain, probability} diff --git a/src/main/java/org/matsim/episim/EpisimRunner.java b/src/main/java/org/matsim/episim/EpisimRunner.java index 39aae36f1..e091c3744 100644 --- a/src/main/java/org/matsim/episim/EpisimRunner.java +++ b/src/main/java/org/matsim/episim/EpisimRunner.java @@ -24,6 +24,9 @@ import com.google.inject.Provider; import org.apache.commons.compress.archivers.*; import org.apache.commons.compress.archivers.zip.ZipArchiveEntry; +import org.apache.commons.compress.compressors.gzip.GzipCompressorOutputStream; +import org.apache.commons.csv.CSVFormat; +import org.apache.commons.csv.CSVPrinter; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.matsim.api.core.v01.events.Event; @@ -35,17 +38,13 @@ import org.matsim.episim.model.AntibodyModel; import org.matsim.episim.model.ProgressionModel; -import java.io.Externalizable; -import java.io.IOException; -import java.io.ObjectInputStream; -import java.io.ObjectOutputStream; +import java.io.*; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.StandardCopyOption; import java.time.DayOfWeek; import java.util.List; import java.util.Map; -import java.util.Random; /** * Main entry point and runner of one epidemic simulation. @@ -67,7 +66,7 @@ public final class EpisimRunner { @Inject public EpisimRunner(Config config, EventsManager manager, Provider handlerProvider, Provider replay, - Provider reportingProvider, Provider progressionProvider, Provider antibodyModelProvider) { + Provider reportingProvider, Provider progressionProvider, Provider antibodyModelProvider) { this.config = config; this.handlerProvider = handlerProvider; this.manager = manager; @@ -104,7 +103,9 @@ public void run(int maxIterations) { Path output = Path.of(config.controler().getOutputDirectory()); int iteration = 1; - if (episimConfig.getStartFromSnapshot() != null) { + if (episimConfig.getStartFromSnapshot() != null && episimConfig.getStartFromImmunization() != null) { + throw new RuntimeException("Cannot start from snapshot and immunization history simultaneously. Choose one."); + } else if (episimConfig.getStartFromSnapshot() != null) { reporting.close(); iteration = readSnapshot(output, Path.of(episimConfig.getStartFromSnapshot())); try { @@ -115,15 +116,21 @@ public void run(int maxIterations) { } handler.onSnapshotLoaded(iteration); - } - // recalculate antibodies for every agent if starting from snapshot. - // The antibodies profile is generated using the immunity event history in the - // snapshot; the antibody model config of the snapshot simulation will - // be superceded by the config of the current simulation. Thus, the antibody development - // during the snapshot can be rewritten without modifying the immunity event history. + // recalculate antibodies for every agent if starting from snapshot. + // The antibodies profile is generated using the immunity event history in the + // snapshot; the antibody model config of the snapshot simulation will + // be superceded by the config of the current simulation. Thus, the antibody development + // during the snapshot can be rewritten without modifying the immunity event history. + antibodyModel.recalculateAntibodiesAfterSnapshot(handler.getPersons(), iteration); + + } else if (episimConfig.getStartFromImmunization() != null) { - antibodyModel.init(handler.getPersons(), iteration); + antibodyModel.init(handler.getPersons(), iteration); + handler.initImmunization(Path.of(episimConfig.getStartFromImmunization())); + } else { + antibodyModel.init(handler.getPersons(), iteration); + } reporting.reportCpuTime(0, "Init", "finished", -1); @@ -131,8 +138,9 @@ public void run(int maxIterations) { for (; iteration <= maxIterations; iteration++) { - if (episimConfig.getSnapshotInterval() > 0 && iteration % episimConfig.getSnapshotInterval() == 0) + if (episimConfig.getSnapshotInterval() > 0 && iteration % episimConfig.getSnapshotInterval() == 0) { writeSnapshot(output, iteration); + } if (iteration % 10 == 0) Gbl.printMemoryUsage(); @@ -149,6 +157,7 @@ public void run(int maxIterations) { /** * Update events data and internal person data structure. + * * @param events */ public void updateEvents(Map> events) { diff --git a/src/main/java/org/matsim/episim/EpisimUtils.java b/src/main/java/org/matsim/episim/EpisimUtils.java index 5972fa323..78cd0cb8f 100644 --- a/src/main/java/org/matsim/episim/EpisimUtils.java +++ b/src/main/java/org/matsim/episim/EpisimUtils.java @@ -61,7 +61,7 @@ public final class EpisimUtils { /** * Seconds in one day. */ - static final double DAY = 24. * 3600; + public static final double DAY = 24. * 3600; private EpisimUtils() { } @@ -565,14 +565,14 @@ public static Map getOutDoorFractionFromDateAndTemp2(File wea double tMax = Double.parseDouble(record.get("tmax")); double prcp = Double.parseDouble(record.get("prcp")); - + if (date.isBefore(LocalDate.parse("2021-01-01"))) { outdoorFractions.put(date, maxOutdoorFraction * getOutDoorFractionFromDateAndTemp(date, TmidSpring2020, TmidFall2020, Trange, tMax, prcp, rainThreshold, alpha)); } else { outdoorFractions.put(date, maxOutdoorFraction * getOutDoorFractionFromDateAndTemp(date, TmidSpring, TmidFall, Trange, tMax, prcp, rainThreshold, alpha)); } - + lastDate = date; } diff --git a/src/main/java/org/matsim/episim/Immunizable.java b/src/main/java/org/matsim/episim/Immunizable.java index 46f8ab93e..bd2208d2a 100644 --- a/src/main/java/org/matsim/episim/Immunizable.java +++ b/src/main/java/org/matsim/episim/Immunizable.java @@ -2,6 +2,7 @@ import it.unimi.dsi.fastutil.doubles.DoubleList; import it.unimi.dsi.fastutil.ints.IntList; +import it.unimi.dsi.fastutil.objects.Object2DoubleMap; import org.matsim.core.api.internal.HasPersonId; import org.matsim.episim.model.VirusStrain; @@ -47,7 +48,20 @@ public interface Immunizable extends HasPersonId { */ double getAntibodyLevelAtInfection(); + /** + * Returns highest antibody level that agent has had in their past for all strains + */ + Object2DoubleMap getMaxAntibodies(); + /** + * Returns highest antibody level that agent has had in their past for a specific strain. + */ + double getMaxAntibodies(VirusStrain strain); + + /** + * sets max antibody level that agent has had in their past for a specific strain. + */ + void updateMaxAntibodies(VirusStrain strain, double maxAb); /** * Returns whether agent has experienced given disease status at any time in the course of the simulation */ diff --git a/src/main/java/org/matsim/episim/InfectionEventHandler.java b/src/main/java/org/matsim/episim/InfectionEventHandler.java index fccaf2aea..81f3641bd 100644 --- a/src/main/java/org/matsim/episim/InfectionEventHandler.java +++ b/src/main/java/org/matsim/episim/InfectionEventHandler.java @@ -37,7 +37,7 @@ import org.matsim.core.config.Config; import org.matsim.core.config.ConfigUtils; import org.matsim.core.utils.collections.Tuple; -import org.matsim.episim.events.EpisimInfectionEvent; +import org.matsim.episim.events.*; import org.matsim.episim.model.*; import org.matsim.episim.model.activity.ActivityParticipationModel; import org.matsim.episim.model.testing.TestingModel; @@ -45,22 +45,21 @@ import org.matsim.episim.policy.Restriction; import org.matsim.episim.policy.ShutdownPolicy; import org.matsim.facilities.ActivityFacility; +import org.matsim.run.AnalysisCommand; import org.matsim.utils.objectattributes.attributable.Attributes; import org.matsim.vehicles.Vehicle; -import java.io.Externalizable; -import java.io.IOException; -import java.io.ObjectInput; -import java.io.ObjectOutput; +import java.io.*; +import java.nio.file.Path; import java.time.DayOfWeek; import java.time.LocalDate; import java.util.*; import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletionException; import java.util.concurrent.ExecutorService; +import java.util.function.Function; -import static org.matsim.episim.EpisimUtils.readChars; -import static org.matsim.episim.EpisimUtils.writeChars; +import static org.matsim.episim.EpisimUtils.*; /** * Main event handler of episim. @@ -201,7 +200,7 @@ public InfectionEventHandler(Injector injector, SplittableRandom rnd) { this.policy = injector.getInstance(ShutdownPolicy.class); this.restrictions = episimConfig.createInitialRestrictions(); this.reporting = injector.getInstance(EpisimReporting.class); - this.localRnd = new SplittableRandom( 65536); // fixed seed, because it should not change between snapshots + this.localRnd = new SplittableRandom(65536); // fixed seed, because it should not change between snapshots this.progressionModel = injector.getInstance(ProgressionModel.class); this.antibodyModel = injector.getInstance(AntibodyModel.class); this.initialInfections = injector.getInstance(InitialInfectionHandler.class); @@ -253,9 +252,9 @@ void init(Map> events) { .sorted(Comparator.comparingInt(p -> ((EpisimPerson) p).getAgeOrDefault(-1)).reversed() .thenComparing(p -> ((EpisimPerson) p).getPersonId())) .forEach(p -> { - Double compliance = EpisimUtils.findValidEntry(vaccinationConfig.getCompliancePerAge(), 1.0, p.getAgeOrDefault(-1)); - p.setVaccinable(localRnd.nextDouble() < compliance); - }); + Double compliance = EpisimUtils.findValidEntry(vaccinationConfig.getCompliancePerAge(), 1.0, p.getAgeOrDefault(-1)); + p.setVaccinable(localRnd.nextDouble() < compliance); + }); listener = (Set) injector.getInstance(Key.get(Types.setOf(SimulationListener.class))); vaccinations = (Set) injector.getInstance(Key.get(Types.setOf(VaccinationModel.class))); @@ -282,6 +281,7 @@ void init(Map> events) { /** * Update events data and internal person data structure. + * * @param events */ void updateEvents(Map> events) { @@ -560,8 +560,7 @@ void updateEvents(Map> events) { */ void onSnapshotLoaded(int iteration) { - listener = (Set) injector.getInstance(Key.get(Types.setOf(SimulationListener.class))); - vaccinations = (Set) injector.getInstance(Key.get(Types.setOf(VaccinationModel.class))); + // Listener and vaccinations should already be present for (SimulationListener s : listener) { s.onSnapshotLoaded(iteration, localRnd, personMap, pseudoFacilityMap, vehicleMap); @@ -577,16 +576,16 @@ void onSnapshotLoaded(int iteration) { /** * Distribute the containers to the different ReplayEventTasks, by setting * the taskId attribute of the containers to values between 0 and episimConfig.getThreds() - 1, - * so that the sum of numUsers * maxGroupSize has an even distribution + * so that the sum of numUsers * maxGroupSize has an even distribution */ private void balanceContainersByLoad(List, Double>> estimatedLoad) { // We need the containers sorted by the load, with the highest load first. // To get a deterministic distribution, we use the containerId for // sorting the containers with the same estimatedLoad. Comparator, Double>> loadComperator = - Comparator., Double>,Double>comparing( - t -> t.getSecond(), Comparator.reverseOrder()). - thenComparing(t -> t.getFirst().getContainerId().toString()); + Comparator., Double>, Double>comparing( + t -> t.getSecond(), Comparator.reverseOrder()). + thenComparing(t -> t.getFirst().getContainerId().toString()); Collections.sort(estimatedLoad, loadComperator); final int numThreads = episimConfig.getThreads(); @@ -595,7 +594,7 @@ private void balanceContainersByLoad(List, Double>> est for (int i = 0; i < numThreads; i++) loadPerThread[i] = 0.0; - for(Tuple, Double> tuple : estimatedLoad) { + for (Tuple, Double> tuple : estimatedLoad) { // search for the thread/taskId with the minimal load int useThread = 0; Double minLoad = loadPerThread[0]; @@ -618,8 +617,9 @@ private void balanceContainersByLoad(List, Double>> est */ private void balanceContainersByHash(List, Double>> estimatedLoad) { for (Tuple, Double> tuple : estimatedLoad) { - final EpisimContainer container = tuple.getFirst(); - final int useThread = Math.abs(container.getContainerId().hashCode()) % episimConfig.getThreads(); container.setTaskId(useThread); + final EpisimContainer container = tuple.getFirst(); + final int useThread = Math.abs(container.getContainerId().hashCode()) % episimConfig.getThreads(); + container.setTaskId(useThread); } } @@ -753,6 +753,9 @@ public void reset(int iteration) { if (paramsMap.size() > 1000) log.warn("Params map contains many entries. Activity types may not be .intern() Strings"); + if (iteration == 1) + reporting.reportStart(episimConfig.getStartDate(), episimConfig.getStartFromImmunization()); + double now = EpisimUtils.getCorrectedTime(episimConfig.getStartOffset(), 0, iteration); LocalDate date = episimConfig.getStartDate().plusDays(iteration - 1); @@ -772,6 +775,14 @@ public void reset(int iteration) { } } + // uncomment if you want immunization stats to be printed on a certain + // date or e.g. every month. This produces a lot of large files so use + // sparingly. +// if (date.getDayOfMonth() == 1) { +// reporting.reportDetailedPersonStats(date, personMap.values()); +// } + + reporting.reportCpuTime(iteration, "ProgressionModelParallel", "start", -2); progressionModel.afterStateUpdates(personMap, iteration); reporting.reportCpuTime(iteration, "ProgressionModelParallel", "finished", -2); @@ -789,7 +800,7 @@ public void reset(int iteration) { // additional vaccinations: for (VaccinationModel vaccination : vaccinations) { - vaccination.handleVaccination(personMap, date, iteration, now); + vaccination.handleVaccination(personMap, date, iteration, now); } reporting.reportCpuTime(iteration, "VaccinationModel", "finished", -1); @@ -978,12 +989,35 @@ void handleEvents(DayOfWeek day, List events) { infections.stream().sorted() .forEach(reporting::reportInfection); + + int totalContacts = handlers.stream().mapToInt(TrajectoryHandler::getNumContacts).sum(); + + reporting.reportTotalContacts(totalContacts); + for (SimulationListener l : listener) { l.onIterationEnd(iteration, episimConfig.getStartDate().plusDays(iteration - 1)); } } + + /** + * Read immunization history and init persons. + */ + void initImmunization(Path history) { + + log.info("Reading immunization from {}", history); + + InitialImmunizationHandler handler = new InitialImmunizationHandler(personMap,episimConfig, antibodyModel,progressionModel); + List days = AnalysisCommand.forEachEvent(history, handler, true, handler); + + + if (handler.isContinueProcessingEvents()) { + throw new RuntimeException("Immunisation history is not long enough (only contains " + days.size() + "days)"); + } + + } + /** * Container that is always a vehicle. */ diff --git a/src/main/java/org/matsim/episim/TracingConfigGroup.java b/src/main/java/org/matsim/episim/TracingConfigGroup.java index f58949c9b..8ca429b3c 100644 --- a/src/main/java/org/matsim/episim/TracingConfigGroup.java +++ b/src/main/java/org/matsim/episim/TracingConfigGroup.java @@ -493,7 +493,7 @@ public enum QuarantineRelease { /** * Release persons without showing symptoms. */ - NON_SYMPTOMS + NON_SYMPTOMATIC } diff --git a/src/main/java/org/matsim/episim/TrajectoryHandler.java b/src/main/java/org/matsim/episim/TrajectoryHandler.java index 2326e9a7f..d15d55f92 100644 --- a/src/main/java/org/matsim/episim/TrajectoryHandler.java +++ b/src/main/java/org/matsim/episim/TrajectoryHandler.java @@ -46,9 +46,9 @@ final class TrajectoryHandler { @Inject public TrajectoryHandler(EpisimConfigGroup episimConfig, EpisimReporting reporting, ContactModel model, SplittableRandom rnd, - @Named("personMap") Map, EpisimPerson> personMap, - @Named("vehicleMap") Map, InfectionEventHandler.EpisimVehicle> vehicleMap, - @Named("pseudoFacilityMap") Map, InfectionEventHandler.EpisimFacility> pseudoFacilityMap) { + @Named("personMap") Map, EpisimPerson> personMap, + @Named("vehicleMap") Map, InfectionEventHandler.EpisimVehicle> vehicleMap, + @Named("pseudoFacilityMap") Map, InfectionEventHandler.EpisimFacility> pseudoFacilityMap) { this.rnd = rnd; this.episimConfig = episimConfig; this.reporting = reporting; @@ -68,6 +68,13 @@ void setRestrictionsForIteration(int iteration, ImmutableMap @@ -132,7 +139,7 @@ void checkAndHandleEndOfNonCircularTrajectory(EpisimPerson person, DayOfWeek day * @param responsible predicate for checking if the handler is responsible for a certain facility */ public void onStartDay(Predicate> responsibleFacility, - Predicate> responsibleVehicle) { + Predicate> responsibleVehicle) { double now = EpisimUtils.getCorrectedTime(episimConfig.getStartOffset(), 0, iteration); DayOfWeek day = EpisimUtils.getDayOfWeek(episimConfig, iteration); @@ -263,7 +270,7 @@ public void handleEvent(ActivityEndEvent activityEndEvent) { reporting.handleEvent(activityEndEvent); if (episimConfig.getContagiousOptimization() == EpisimConfigGroup.ContagiousOptimization.no || - episimFacility.containsContagious()) { + episimFacility.containsContagious()) { contactModel.infectionDynamicsFacility(episimPerson, episimFacility, now); } @@ -311,8 +318,8 @@ public void handleEvent(PersonLeavesVehicleEvent leavesVehicleEvent) { reporting.handleEvent(leavesVehicleEvent); - if (episimConfig.getContagiousOptimization() == EpisimConfigGroup.ContagiousOptimization.no || - episimVehicle.containsContagious()) { + if (episimConfig.getContagiousOptimization() == EpisimConfigGroup.ContagiousOptimization.no || + episimVehicle.containsContagious()) { contactModel.infectionDynamicsVehicle(episimPerson, episimVehicle, now); } diff --git a/src/main/java/org/matsim/episim/VaccinationConfigGroup.java b/src/main/java/org/matsim/episim/VaccinationConfigGroup.java index 1c7cb027a..8a2595ffb 100644 --- a/src/main/java/org/matsim/episim/VaccinationConfigGroup.java +++ b/src/main/java/org/matsim/episim/VaccinationConfigGroup.java @@ -36,7 +36,6 @@ public class VaccinationConfigGroup extends ReflectiveConfigGroup { private static final String GROUPNAME = "episimVaccination"; - /** * Amount of vaccinations available per day. */ @@ -253,7 +252,7 @@ public boolean getUseIgA() { } @StringSetter(TIME_PERIOD_IGA) - public void setTimePeriodIgA(double timePeriodIgA){ + public void setTimePeriodIgA(double timePeriodIgA) { this.timePeriodIgA = timePeriodIgA; } diff --git a/src/main/java/org/matsim/episim/VirusStrainConfigGroup.java b/src/main/java/org/matsim/episim/VirusStrainConfigGroup.java index 9498d57de..8c3dcacd9 100644 --- a/src/main/java/org/matsim/episim/VirusStrainConfigGroup.java +++ b/src/main/java/org/matsim/episim/VirusStrainConfigGroup.java @@ -1,17 +1,25 @@ package org.matsim.episim; +import com.google.common.base.Joiner; +import com.google.common.base.Splitter; import org.matsim.core.config.ConfigGroup; import org.matsim.core.config.ReflectiveConfigGroup; import org.matsim.episim.model.VirusStrain; import java.util.EnumMap; import java.util.Map; +import java.util.NavigableMap; +import java.util.TreeMap; +import java.util.stream.Collectors; /** * Config option specific to {@link org.matsim.episim.model.VirusStrain}. */ public class VirusStrainConfigGroup extends ReflectiveConfigGroup { + private static final Splitter.MapSplitter SPLITTER = Splitter.on(";").withKeyValueSeparator("="); + private static final Joiner.MapJoiner JOINER = Joiner.on(";").withKeyValueSeparator("="); + private static final String GROUPNAME = "virusStrains"; /** @@ -53,6 +61,13 @@ public StrainParams getOrAddParams(VirusStrain strain) { return strains.get(strain); } + /** + * Whether params for strain are present. + */ + public boolean hasParams(VirusStrain strain) { + return strains.containsKey(strain); + } + @Override public ConfigGroup createParameterSet(String type) { if (StrainParams.SET_TYPE.equals(type)) { @@ -84,6 +99,8 @@ public static final class StrainParams extends ReflectiveConfigGroup { private static final String FACTOR_SERIOUSLY_SICK = "factorSeriouslySick"; private static final String FACTOR_CRITICAL = "factorCritical"; private static final String FACTOR_SERIOUSLY_SICK_VAC = "factorSeriouslySickVaccinated"; + private static final String AGE_SUSCEPTIBILITY = "ageSusceptibility"; + private static final String AGE_INFECTIVITY = "ageInfectivity"; /** * Type of the strain. @@ -110,6 +127,24 @@ public static final class StrainParams extends ReflectiveConfigGroup { */ private double factorSeriouslySickVaccinated = 1.0; + /** + * Child susceptibility used in AgeDependentInfectionModelWithSeasonality. + * Taken from https://doi.org/10.1101/2020.06.03.20121145 + */ + private final NavigableMap ageSusceptibility = new TreeMap<>(Map.of( + 19, 0.45, + 20, 1d + )); + + /** + * Child infectivity used in AgeDependentInfectionModelWithSeasonality. + * Taken from https://doi.org/10.1101/2020.06.03.20121145 + */ + private final NavigableMap ageInfectivity = new TreeMap<>(Map.of( + 19, 0.85, + 20, 1d + )); + StrainParams() { super(SET_TYPE); } @@ -187,5 +222,65 @@ public void setFactorSeriouslySickVaccinated(double factorSeriouslySickVaccinate public double getFactorSeriouslySickVaccinated() { return factorSeriouslySickVaccinated; } + + + @StringGetter(AGE_SUSCEPTIBILITY) + String getAgeSusceptibilityString() { + return JOINER.join(ageSusceptibility); + } + + @StringSetter(AGE_SUSCEPTIBILITY) + void setAgeSusceptibility(String config) { + Map map = SPLITTER.split(config); + setAgeSusceptibility(map.entrySet().stream().collect(Collectors.toMap( + e -> Integer.parseInt(e.getKey()), e -> Double.parseDouble(e.getValue()) + ))); + } + + /** + * Return susceptibility for different age groups. + */ + public NavigableMap getAgeSusceptibility() { + return ageSusceptibility; + } + + /** + * Set susceptibility for all age groups, previous entries will be overwritten. + */ + public void setAgeSusceptibility(Map ageSusceptibility) { + this.ageSusceptibility.clear(); + this.ageSusceptibility.putAll(ageSusceptibility); + } + + @StringGetter(AGE_INFECTIVITY) + String getAgeInfectivityString() { + return JOINER.join(ageInfectivity); + } + + @StringSetter(AGE_INFECTIVITY) + void setAgeInfectivity(String config) { + Map map = SPLITTER.split(config); + setAgeInfectivity(map.entrySet().stream().collect(Collectors.toMap( + e -> Integer.parseInt(e.getKey()), e -> Double.parseDouble(e.getValue()) + ))); + } + + /** + * Return infectivity for different age groups. + */ + public NavigableMap getAgeInfectivity() { + return ageInfectivity; + } + + /** + * Set infectivity for all age groups, previous entries will be overwritten. + */ + public void setAgeInfectivity(Map ageInfectivity) { + this.ageInfectivity.clear(); + this.ageInfectivity.putAll(ageInfectivity); + } + } + + } diff --git a/src/main/java/org/matsim/episim/analysis/ExtractInfectionsByAge.java b/src/main/java/org/matsim/episim/analysis/ExtractInfectionsByAge.java index 363f58e58..b81904462 100644 --- a/src/main/java/org/matsim/episim/analysis/ExtractInfectionsByAge.java +++ b/src/main/java/org/matsim/episim/analysis/ExtractInfectionsByAge.java @@ -232,7 +232,7 @@ private void readScenario(Path path) throws IOException { } } , - (EpisimPersonStatusEventHandler) e -> { + false, (EpisimPersonStatusEventHandler) e -> { if (!counts.containsKey(e.getDiseaseStatus())) return; diff --git a/src/main/java/org/matsim/episim/analysis/FilterEvents.java b/src/main/java/org/matsim/episim/analysis/FilterEvents.java index b93bf76f8..64e182980 100644 --- a/src/main/java/org/matsim/episim/analysis/FilterEvents.java +++ b/src/main/java/org/matsim/episim/analysis/FilterEvents.java @@ -9,6 +9,8 @@ import org.matsim.api.core.v01.events.Event; import org.matsim.core.events.handler.BasicEventHandler; import org.matsim.episim.events.EpisimInfectionEvent; +import org.matsim.episim.events.EpisimInitialInfectionEvent; +import org.matsim.episim.events.EpisimStartEvent; import org.matsim.episim.events.EpisimVaccinationEvent; import org.matsim.episim.reporting.EpisimWriter; import org.matsim.run.AnalysisCommand; @@ -30,12 +32,12 @@ public class FilterEvents implements OutputAnalysis { private static final Logger log = LogManager.getLogger(FilterEvents.class); - // @CommandLine.Option(names = "--output", defaultValue = "../public-svn/matsim/scenarios/countries/de/episim/battery/cologne/2022-02-17/1/output-filtered") - @CommandLine.Option(names = "--output", defaultValue = "./output/") + @CommandLine.Option(names = "--output", defaultValue = "../public-svn/matsim/scenarios/countries/de/episim/battery/jakob/2022-08-02/2-vax-b/analysis") +// @CommandLine.Option(names = "--output", defaultValue = "./output/") private Path output; @CommandLine.Parameters(paramLabel = "TYPE", arity = "0..*", description = "Names of event types to keep") - private Set filter = Set.of(EpisimInfectionEvent.EVENT_TYPE, EpisimVaccinationEvent.EVENT_TYPE); + private Set filter = Set.of(EpisimStartEvent.EVENT_TYPE, EpisimInitialInfectionEvent.EVENT_TYPE, EpisimInfectionEvent.EVENT_TYPE, EpisimVaccinationEvent.EVENT_TYPE); public static void main(String[] args) { System.exit(new CommandLine(new FilterEvents()).execute(args)); @@ -77,7 +79,7 @@ public void analyzeOutput(Path output) throws IOException { AnalysisCommand.forEachEvent(output, s -> { handler.reset(-1); - }, handler); + }, false, handler); handler.closeEntry(); zipOut.close(); diff --git a/src/main/java/org/matsim/episim/analysis/HospitalNumbersFromEvents.java b/src/main/java/org/matsim/episim/analysis/HospitalNumbersFromEvents.java index be464ea69..6dd17f5f1 100644 --- a/src/main/java/org/matsim/episim/analysis/HospitalNumbersFromEvents.java +++ b/src/main/java/org/matsim/episim/analysis/HospitalNumbersFromEvents.java @@ -57,20 +57,23 @@ name = "hospitalNumbers", description = "Calculate hospital numbers from events" ) + public class HospitalNumbersFromEvents implements OutputAnalysis { - @CommandLine.Option(names = "--output", defaultValue = "./output/") -// @CommandLine.Option(names = "--output", defaultValue = "../public-svn/matsim/scenarios/countries/de/episim/battery/jakob/2022-06-16/2/analysis/policy_leis75/") + @CommandLine.Option(names = "--output", defaultValue = "/Users/jakob/git/matsim-episim/A_originalImmHist") +// @CommandLine.Option(names = "--output", defaultValue = "/Users/jakob/git/matsim-episim/B_startedFromImmHist") +// @CommandLine.Option(names = "--output", defaultValue = "/Users/jakob/git/public-svn/matsim/scenarios/countries/de/episim/battery/jakob/2022-10-18/3-meas/analysis/") private Path output; - @CommandLine.Option(names = "--input", defaultValue = "/scratch/projects/bzz0020/episim-input") -// @CommandLine.Option(names = "--input", defaultValue = "../shared-svn/projects/episim/matsim-files/snz/Cologne/episim-input") +// @CommandLine.Option(names = "--input", defaultValue = "/scratch/projects/bzz0020/episim-input") + @CommandLine.Option(names = "--input", defaultValue = "../shared-svn/projects/episim/matsim-files/snz/Cologne/episim-input") private String input; @CommandLine.Option(names = "--population-file", defaultValue = "/cologne_snz_entirePopulation_emptyPlans_withDistricts_25pt_split.xml.gz") private String populationFile; - @CommandLine.Option(names = "--start-date", defaultValue = "2020-02-24") +// @CommandLine.Option(names = "--start-date", defaultValue = "2022-04-01") + @CommandLine.Option(names = "--start-date", defaultValue = "2020-02-25") private LocalDate startDate; @CommandLine.Option(names = "--district", description = "District to filter for", defaultValue = "Köln") @@ -81,18 +84,14 @@ public class HospitalNumbersFromEvents implements OutputAnalysis { private final String DATE = "date"; private final String DAY = "day"; - private final String INTAKES_HOSP = "intakesHosp"; - private final String INTAKES_ICU = "intakesIcu"; - private final String OCCUPANCY_HOSP = "occupancyHosp"; - private final String OCCUPANCY_ICU = "occupancyIcu"; + @Inject private Scenario scenario; private Population population; - private List> filteredPopulationIds; - + // TODO: check age or strain based lags in literature // source: incidence wave vs. hospitalization wave in cologne/nrw (see https://docs.google.com/spreadsheets/d/1jmaerl27LKidD1uk3azdIL1LmvHuxazNQlhVo9xO1z8/edit?usp=sharing) private static final Object2IntMap lagBetweenInfectionAndHospitalisation = new Object2IntAVLTreeMap<>( Map.of(VirusStrain.SARS_CoV_2, 14, @@ -101,7 +100,8 @@ public class HospitalNumbersFromEvents implements OutputAnalysis { VirusStrain.OMICRON_BA1, 14, VirusStrain.OMICRON_BA2, 14, VirusStrain.OMICRON_BA5, 14, - VirusStrain.STRAIN_A, 14 + VirusStrain.STRAIN_A, 14, + VirusStrain.STRAIN_B, 14 )); // source: hospitalization wave vs. ICU wave in cologne/nrw (see https://docs.google.com/spreadsheets/d/1jmaerl27LKidD1uk3azdIL1LmvHuxazNQlhVo9xO1z8/edit?usp=sharing) @@ -112,7 +112,8 @@ public class HospitalNumbersFromEvents implements OutputAnalysis { VirusStrain.OMICRON_BA1, 6, VirusStrain.OMICRON_BA2, 6, VirusStrain.OMICRON_BA5, 6, - VirusStrain.STRAIN_A, 6 + VirusStrain.STRAIN_A, 6, + VirusStrain.STRAIN_B, 6 )); // Austria study in https://docs.google.com/spreadsheets/d/1jmaerl27LKidD1uk3azdIL1LmvHuxazNQlhVo9xO1z8/edit#gid=0 @@ -123,58 +124,65 @@ public class HospitalNumbersFromEvents implements OutputAnalysis { VirusStrain.OMICRON_BA1, 7, VirusStrain.OMICRON_BA2, 7, VirusStrain.OMICRON_BA5,7, - VirusStrain.STRAIN_A, 7 + VirusStrain.STRAIN_A, 7, + VirusStrain.STRAIN_B, 7 )); private static final Object2IntMap daysInICU = new Object2IntAVLTreeMap<>( Map.of(VirusStrain.SARS_CoV_2, 15, // Debeka & Ireland studies VirusStrain.ALPHA, 15, // Debeka & Ireland studies VirusStrain.DELTA, 15, // this and following values come from nrw analysis on Tabellenblatt 5 - VirusStrain.OMICRON_BA1, 10, + VirusStrain.OMICRON_BA1, 10, // TODO: Where does this number come from? VirusStrain.OMICRON_BA2, 10, VirusStrain.OMICRON_BA5,10, - VirusStrain.STRAIN_A, 10 + VirusStrain.STRAIN_A, 10, + VirusStrain.STRAIN_B, 10 )); // ?? private static final Object2IntMap daysInHospitalGivenICU = new Object2IntAVLTreeMap<>( - Map.of(VirusStrain.SARS_CoV_2, 60, + Map.of(VirusStrain.SARS_CoV_2, 60, // TODO: Where does this number come from? VirusStrain.ALPHA, 60, VirusStrain.DELTA, 60, VirusStrain.OMICRON_BA1, 60, VirusStrain.OMICRON_BA2, 60, VirusStrain.OMICRON_BA5,60, - VirusStrain.STRAIN_A, 60 + VirusStrain.STRAIN_A, 60, + VirusStrain.STRAIN_B, 60 )); - private static final double beta = 1.2; - private final int populationCntOfficialKoelln = 919_936; - private final int populationCntOfficialNrw = 17_930_000; + private static final double beta = 1.2; - private static final double hospitalFactor = 0.4; + private static final double hospitalFactor = 0.3; // Based on "guess & check", accounts for unreported cases TODO: Potential follow-up - // base private static final double factorWild = 1.0; private static final double factorAlpha = 1.0 * factorWild; // delta: 2.3x more severe than alpha - Hospital admission and emergency care attendance risk for SARS-CoV-2 delta (B.1.617.2) compared with alpha (B.1.1.7) variants of concern: a cohort study - private static final double factorDelta = 1.6 * factorWild; + private static final double factorDelta = 1.2 * factorWild; //1.6 * factorWild; // omicron: approx 0.3x (intrinsic) severity of delta - Comparative analysis of the risks of hospitalisation and death associated with SARS-CoV-2 omicron (B.1.1.529) and delta (B.1.617.2) variants in England: a cohort study - private static final double factorOmicron = 0.3 * factorDelta; // * reportedShareOmicron / reportedShareDelta + private static final double factorOmicron = 0.45 * factorDelta; // reportedShareOmicron / reportedShareDelta +// private static final double factorOmicron = 0.6 * factorDelta;// reportedShareOmicron / reportedShareDelta + + private static final double factorBA5 = 1.0 * factorOmicron; // old: 1.5 + + private static final double factorScen2 = factorBA5; // reportedShareOmicron / reportedShareDelta + private static final double factorScen3 = factorBA5 * 3;// reportedShareOmicron / reportedShareDelta + + +// private static final double factorBA5 = 1.0 * factorOmicron; -// private static final List strainFactors = List.of(factorOmicron, factorDelta); // ?? - private static final double factorWildAndAlphaICU = 1.; + private static final double factorWildAndAlphaICU = 1.; // TODO : Check literature for reasonable values private static final double factorDeltaICU = 1.; private static final double factorOmicronICU = 1.; - - private String outputAppendix = ""; + private static final double factorBA5ICU = 1.; public static void main(String[] args) { System.exit(new CommandLine(new HospitalNumbersFromEvents()).execute(args)); @@ -182,28 +190,27 @@ public static void main(String[] args) { @Override public Integer call() throws Exception { + // logger configuration Configurator.setLevel("org.matsim.core.config", Level.WARN); Configurator.setLevel("org.matsim.core.controler", Level.WARN); Configurator.setLevel("org.matsim.core.events", Level.WARN); Configurator.setLevel("org.matsim.core.utils", Level.WARN); + // check if events file exists if (!Files.exists(output)) { log.error("Output path {} does not exist.", output); return 2; } - + // read population population = PopulationUtils.readPopulation(input + populationFile); - // Here we define values factorSeriouslySickStrainA should have - - // Part 1: calculate hospitalizations for each seed and save as csv -// List pathList = new ArrayList<>(); + List pathList = new ArrayList<>(); AnalysisCommand.forEachScenario(output, pathToScenario -> { try { -// pathList.add(pathToScenario); + pathList.add(pathToScenario); // analyzeOutput is where the hospitalization post processing occurs analyzeOutput(pathToScenario); @@ -217,6 +224,11 @@ public Integer call() throws Exception { // Part 2: aggregate over multiple seeds & produce tsv output & plot // HospitalNumbersFromEventsPlotter.aggregateAndProducePlots(output, pathList); + //TODO: move to other class +// HospitalNumbersFromEventsPlotter.aggregateAndProducePlots(output, pathList, "_Omicron", startDate, "Omicron"); +// HospitalNumbersFromEventsPlotter.aggregateAndProducePlots(output, pathList, "_Delta", startDate, "Delta"); + + // } @@ -232,7 +244,7 @@ public void analyzeOutput(Path pathToScenario) throws IOException { String id = AnalysisCommand.getScenarioPrefix(pathToScenario); // builds the path to the output file that is produced by this analysis - final Path tsvPath = pathToScenario.resolve(id + "post.hospital" + outputAppendix + ".tsv"); + final Path tsvPath = pathToScenario.resolve(id + "post.hospital.tsv"); // calculates hospitalizations calculateHospitalizationsAndWriteOutput(pathToScenario, tsvPath); @@ -254,44 +266,20 @@ private void calculateHospitalizationsAndWriteOutput(Path pathToScenario, Path t BufferedWriter bw = Files.newBufferedWriter(tsvPath); bw.write(AnalysisCommand.TSV.join(DAY, DATE,"measurement", "severity", "n")); // + "\thospNoImmunity\thospBaseImmunity\thospBoosted\tincNoImmunity\tincBaseImmunity\tincBoosted")); - ConfigHolder holderOmicron = configure(factorOmicron,factorOmicronICU) ; - ConfigHolder holderDelta = configure(factorDelta,factorDeltaICU) ; + ConfigHolder holderOmicron = configure(factorScen2, 1.0); // scenario 2 + ConfigHolder holderDelta = configure(factorScen3, 1.0); //scenario 3 List handlers = List.of( - new Handler("Omicron", population, holderOmicron ), + new Handler("Omicron", population, holderOmicron), new Handler("Delta", population, holderDelta) ); // feed the output events file to the handler, so that the hospitalizations may be calculated - AnalysisCommand.forEachEvent(pathToScenario, s -> { - }, handlers.toArray(new Handler[0])); - -// for (Double facA : strainFactors) { - - // configure post processing run -// double facAICU = 0.; -// String diseaseSevName = ""; -// if (facA == factorWild) { -// diseaseSevName = "Alpha"; -// facAICU = factorWildAndAlphaICU; -// } else if (facA == factorDelta) { -// diseaseSevName = "Delta"; -// facAICU = factorDeltaICU; -// } else if (facA == factorOmicron) { -// diseaseSevName = "Omicron"; -// facAICU = factorOmicronICU; -// } else { -// throw new RuntimeException("not clear what to do"); -// } + List eventFiles = AnalysisCommand.forEachEvent(pathToScenario, s -> { + }, true, handlers.toArray(new Handler[0])); for (Handler handler : handlers) { - int maxIteration = Math.max( - Math.max(handler.postProcessHospitalAdmissions.keySet().lastInt(), - handler.postProcessICUAdmissions.keySet().lastInt()), - Math.max(handler.postProcessHospitalFilledBeds.keySet().lastInt(), - handler.postProcessHospitalFilledBedsICU.keySet().lastInt())); - // calculates the number of agents in the scenario's population (25% sample) who live in Cologne // this is used to normalize the hospitalization values @@ -299,7 +287,7 @@ private void calculateHospitalizationsAndWriteOutput(Path pathToScenario, Path t .filter(x -> x.getAttributes().getAttribute("district").equals(district)).count(); - for (int day = 0; day <= maxIteration; day++) { + for (int day = 0; day < eventFiles.size(); day++) { LocalDate date = startDate.plusDays(day); // calculates Incidence - 7day hospitalizations per 100,000 residents @@ -312,13 +300,13 @@ private void calculateHospitalizationsAndWriteOutput(Path pathToScenario, Path t double occupancyIcu = handler.postProcessHospitalFilledBedsICU.getOrDefault(day, 0) * 100_000. / popSize; bw.newLine(); - bw.write(AnalysisCommand.TSV.join(day, date, "intakesHosp", handler.name , intakesHosp)); + bw.write(AnalysisCommand.TSV.join(day, date, HospitalNumbersFromEventsPlotter.INTAKES_HOSP, handler.name , intakesHosp)); bw.newLine(); - bw.write(AnalysisCommand.TSV.join(day, date, "intakesICU", handler.name, intakesIcu)); + bw.write(AnalysisCommand.TSV.join(day, date, HospitalNumbersFromEventsPlotter.INTAKES_ICU, handler.name, intakesIcu)); bw.newLine(); - bw.write(AnalysisCommand.TSV.join(day, date, "occupancyHosp ", handler.name, occupancyHosp)); + bw.write(AnalysisCommand.TSV.join(day, date, HospitalNumbersFromEventsPlotter.OCCUPANCY_HOSP, handler.name, occupancyHosp)); bw.newLine(); - bw.write(AnalysisCommand.TSV.join(day, date, "occupancyICU", handler.name, occupancyIcu)); + bw.write(AnalysisCommand.TSV.join(day, date, HospitalNumbersFromEventsPlotter.OCCUPANCY_ICU, handler.name, occupancyIcu)); } } @@ -327,7 +315,7 @@ private void calculateHospitalizationsAndWriteOutput(Path pathToScenario, Path t } - private int getWeeklyHospitalizations(Int2IntMap hospMap, Integer today) { + static int getWeeklyHospitalizations(Int2IntMap hospMap, Integer today) { int weeklyHospitalizations = 0; for (int i = 0; i < 7; i++) { try { @@ -340,10 +328,7 @@ private int getWeeklyHospitalizations(Int2IntMap hospMap, Integer today) { } - public static final class Handler implements EpisimVaccinationEventHandler, EpisimInfectionEventHandler { - - - + public static final class Handler implements EpisimInfectionEventHandler, EpisimInitialInfectionEventHandler{ final Map, ImmunizablePerson> data; private final String name; private final Population population; @@ -355,24 +340,8 @@ public static final class Handler implements EpisimVaccinationEventHandler, Epis final Int2IntSortedMap postProcessHospitalFilledBeds; final Int2IntSortedMap postProcessHospitalFilledBedsICU; - private final Int2IntSortedMap changeBaseImmunity; - private final Int2IntMap changeNoImmunity; - private final Int2IntMap changeBoostered; - private final Int2IntMap hospNoImmunity; - private final Int2IntMap hospBoostered; - private final Int2IntMap hospBaseImmunity; - private final Int2IntMap incNoImmunity; - private final Int2IntMap incBaseImmunity; - private final Int2IntMap incBoostered; - -// private final EpisimWriter episimWriter; - BufferedWriter hospCalibration; - -// private CSVPrinter printer; - private final AgeDependentDiseaseStatusTransitionModel transitionModel; - Handler(String name, Population population, ConfigHolder holder) { // instantiate the custom event handler that calculates hospitalizations based on events @@ -382,21 +351,12 @@ public static final class Handler implements EpisimVaccinationEventHandler, Epis this.rnd = new Random(1234); this.holder = holder; + // key : iteration, value : admissions/filled beds this.postProcessHospitalAdmissions = new Int2IntAVLTreeMap(); this.postProcessICUAdmissions = new Int2IntAVLTreeMap(); this.postProcessHospitalFilledBeds = new Int2IntAVLTreeMap(); this.postProcessHospitalFilledBedsICU = new Int2IntAVLTreeMap(); - this.changeNoImmunity = new Int2IntAVLTreeMap(); - this.changeBaseImmunity = new Int2IntAVLTreeMap(); - this.changeBoostered = new Int2IntAVLTreeMap(); - this.hospNoImmunity = new Int2IntAVLTreeMap(); - this.hospBoostered = new Int2IntAVLTreeMap(); - this.hospBaseImmunity = new Int2IntAVLTreeMap(); - this.incNoImmunity = new Int2IntAVLTreeMap(); - this.incBaseImmunity = new Int2IntAVLTreeMap(); - this.incBoostered = new Int2IntAVLTreeMap(); - this.transitionModel = new AgeDependentDiseaseStatusTransitionModel(new SplittableRandom(1234), holder.episimConfig, holder.vaccinationConfig, holder.strainConfig); // try { @@ -407,9 +367,11 @@ public static final class Handler implements EpisimVaccinationEventHandler, Epis // } } + @Override public void handleEvent(EpisimInfectionEvent event) { + ImmunizablePerson person = data.computeIfAbsent(event.getPersonId(), personId -> new ImmunizablePerson(personId, getAge(personId))); @@ -419,21 +381,18 @@ public void handleEvent(EpisimInfectionEvent event) { return; } + VirusStrain virusStrain = event.getVirusStrain(); person.addInfection(event.getTime()); - person.setAntibodyLevelAtInfection(event.getAntibodies()); - person.setVirusStrain(event.getVirusStrain()); + person.setVirusStrain(virusStrain); + person.setNumVaccinations(event.getNumVaccinations()); + + person.updateMaxAntibodies(virusStrain, event.getMaxAntibodies()); int day = (int) (event.getTime() / 86_400); - updateHospitalizationsPost(person, event.getVirusStrain(), day); - if (person.getNumVaccinations()==0) { - incNoImmunity.mergeInt(day, 1, Integer::sum); - } else if (person.getNumVaccinations()==1) { - incBaseImmunity.mergeInt(day, 1, Integer::sum); - } else { - incBoostered.mergeInt(day, 1, Integer::sum); - } + updateHospitalizationsPost(person, virusStrain, day); + // print to csv @@ -454,31 +413,40 @@ public void handleEvent(EpisimInfectionEvent event) { } - - @Override - public void handleEvent(EpisimVaccinationEvent event) { - ImmunizablePerson person = data.computeIfAbsent(event.getPersonId(), personId -> new ImmunizablePerson(personId, getAge(personId))); - - String district = (String) population.getPersons().get(person.personId).getAttributes().getAttribute("district"); - - if (!district.equals("Köln")){ - return; - } - - int day = (int) (event.getTime() / 86_400); + public void handleEvent(EpisimInitialInfectionEvent event) { + handleEvent(event.asInfectionEvent()); + } - if (person.getNumVaccinations()==0) { - changeBaseImmunity.mergeInt(day, 1, Integer::sum); - changeNoImmunity.mergeInt(day, -1, Integer::sum); - } else if (person.getNumVaccinations() == 1) { - changeBoostered.mergeInt(day, 1, Integer::sum); - changeBaseImmunity.mergeInt(day, -1, Integer::sum); - } +// @Override +// public void handleEvent(EpisimVaccinationEvent event) { +// +//// if (!event.getPersonId().toString().equals("12102f5")) +//// return; +// +// ImmunizablePerson person = data.computeIfAbsent(event.getPersonId(), personId -> new ImmunizablePerson(personId, getAge(personId))); +// +// String district = (String) population.getPersons().get(person.personId).getAttributes().getAttribute("district"); +// +// if (!district.equals("Köln")){ +// return; +// } +// +// int day = (int) (event.getTime() / 86_400); +// +// if (person.getNumVaccinations()==0) { +// changeBaseImmunity.mergeInt(day, 1, Integer::sum); +// changeNoImmunity.mergeInt(day, -1, Integer::sum); +// } else if (person.getNumVaccinations() == 1) { +// changeBoostered.mergeInt(day, 1, Integer::sum); +// changeBaseImmunity.mergeInt(day, -1, Integer::sum); +// } +// +// +// person.addVaccination(day); - person.addVaccination(day); - } +// } private int getAge(Id personId) { return (int) population.getPersons().get(personId).getAttributes().getAttribute("microm:modeled:age"); @@ -486,7 +454,7 @@ private int getAge(Id personId) { private void updateHospitalizationsPost(ImmunizablePerson person, VirusStrain strain, int infectionIteration) { - + // check whether we entered all information for the strain if (!lagBetweenInfectionAndHospitalisation.containsKey(strain) || !lagBetweenHospitalizationAndICU.containsKey(strain) || !daysInHospitalGivenNoICU.containsKey(strain) @@ -496,22 +464,13 @@ private void updateHospitalizationsPost(ImmunizablePerson person, VirusStrain st } + // check if go to hospital if (goToHospital(person, infectionIteration)) { // newly admitted to hospital int inHospital = infectionIteration + lagBetweenInfectionAndHospitalisation.getInt(strain); postProcessHospitalAdmissions.mergeInt(inHospital, 1, Integer::sum); - - if (person.getNumVaccinations()==0) { - hospNoImmunity.mergeInt(inHospital, 1, Integer::sum); - } else if (person.getNumVaccinations()==1) { - hospBaseImmunity.mergeInt(inHospital, 1, Integer::sum); - } else { - hospBoostered.mergeInt(inHospital, 1, Integer::sum); - } - - if (goToICU(person, inHospital)) { // newly admitted to ICU @@ -580,7 +539,6 @@ private boolean goToICU(ImmunizablePerson person, int day) { } - /** * Data holder for attributes */ @@ -611,8 +569,15 @@ static final class ImmunizablePerson implements Immunizable{ * Antibody level at last infection. */ private double antibodyLevelAtInfection = 0; - private int age; + /** + * Maximal antibody level reached by agent w/ respect to each strain + */ + private final Object2DoubleMap maxAntibodies = new Object2DoubleOpenHashMap<>(); + + private final int age; + + private int numVaccinations; ImmunizablePerson(Id personId, int age) { this.personId = personId; @@ -626,7 +591,11 @@ public Id getPersonId() { @Override public int getNumVaccinations() { - return vaccinationDates.size(); + return numVaccinations; + } + public void setNumVaccinations(int numVaccinations) { + this.numVaccinations = numVaccinations; + } @Override @@ -644,9 +613,9 @@ public VirusStrain getVirusStrain() { return strain; } - public void addVaccination(int day) { - vaccinationDates.add(day); - } +// public void addVaccination(int day) { +// vaccinationDates.add(day); +// } @Override public IntList getVaccinationDates() { @@ -673,6 +642,20 @@ public double getAntibodyLevelAtInfection() { return antibodyLevelAtInfection; } + @Override + public Object2DoubleMap getMaxAntibodies() { + return maxAntibodies; + } + + @Override + public double getMaxAntibodies(VirusStrain strain) { + return maxAntibodies.getDouble(strain); + } + + public void updateMaxAntibodies(VirusStrain strain, double maxAb){ + this.maxAntibodies.put(strain, maxAb); + } + @Override public boolean hadDiseaseStatus(EpisimPerson.DiseaseStatus status) { throw new UnsupportedOperationException("this method is not supported by Holder"); @@ -702,7 +685,7 @@ public int getAge() { * necessary for post processing. * @param */ - private ConfigHolder configure(double facA, double facAICU) { + private static ConfigHolder configure(double facA, double facAICU) { Config config = ConfigUtils.createConfig(new EpisimConfigGroup()); @@ -724,12 +707,14 @@ private ConfigHolder configure(double facA, double facAICU) { strainConfig.getOrAddParams(VirusStrain.OMICRON_BA1).setFactorCritical(factorOmicronICU); strainConfig.getOrAddParams(VirusStrain.OMICRON_BA2).setFactorSeriouslySick(factorOmicron); strainConfig.getOrAddParams(VirusStrain.OMICRON_BA2).setFactorCritical(factorOmicronICU); - strainConfig.getOrAddParams(VirusStrain.OMICRON_BA5).setFactorSeriouslySick(factorOmicron); - strainConfig.getOrAddParams(VirusStrain.OMICRON_BA5).setFactorCritical(factorOmicronICU); + strainConfig.getOrAddParams(VirusStrain.OMICRON_BA5).setFactorSeriouslySick(factorBA5); + strainConfig.getOrAddParams(VirusStrain.OMICRON_BA5).setFactorCritical(factorBA5ICU); strainConfig.getOrAddParams(VirusStrain.STRAIN_A).setFactorSeriouslySick(facA); strainConfig.getOrAddParams(VirusStrain.STRAIN_A).setFactorCritical(facAICU); + strainConfig.getOrAddParams(VirusStrain.STRAIN_B).setFactorSeriouslySick(facA); + strainConfig.getOrAddParams(VirusStrain.STRAIN_B).setFactorCritical(facAICU); // configure vaccinationConfig: set beta factor VaccinationConfigGroup vaccinationConfig = ConfigUtils.addOrGetModule(config, VaccinationConfigGroup.class); @@ -738,13 +723,13 @@ private ConfigHolder configure(double facA, double facAICU) { return new ConfigHolder(episimConfig, vaccinationConfig, strainConfig); } - private static final class ConfigHolder { + static final class ConfigHolder { private final EpisimConfigGroup episimConfig; private final VaccinationConfigGroup vaccinationConfig; private final VirusStrainConfigGroup strainConfig; - private ConfigHolder(EpisimConfigGroup episimConfig, VaccinationConfigGroup vaccinationConfig, VirusStrainConfigGroup strainConfig) { + ConfigHolder(EpisimConfigGroup episimConfig, VaccinationConfigGroup vaccinationConfig, VirusStrainConfigGroup strainConfig) { this.episimConfig = episimConfig; this.vaccinationConfig = vaccinationConfig; this.strainConfig = strainConfig; diff --git a/src/main/java/org/matsim/episim/analysis/HospitalNumbersFromEventsPlotter.java b/src/main/java/org/matsim/episim/analysis/HospitalNumbersFromEventsPlotter.java index e4b5d7ca0..383c405a1 100644 --- a/src/main/java/org/matsim/episim/analysis/HospitalNumbersFromEventsPlotter.java +++ b/src/main/java/org/matsim/episim/analysis/HospitalNumbersFromEventsPlotter.java @@ -1,104 +1,146 @@ -//package org.matsim.episim.analysis; -// -//import it.unimi.dsi.fastutil.ints.*; -//import org.apache.commons.csv.CSVFormat; -//import org.apache.commons.csv.CSVParser; -//import org.apache.commons.csv.CSVPrinter; -//import org.apache.commons.csv.CSVRecord; -//import org.matsim.run.AnalysisCommand; -//import tech.tablesaw.api.*; -//import tech.tablesaw.plotly.components.Axis; -//import tech.tablesaw.plotly.components.Figure; -//import tech.tablesaw.plotly.components.Layout; -//import tech.tablesaw.plotly.components.Page; -//import tech.tablesaw.plotly.traces.ScatterTrace; -//import tech.tablesaw.table.TableSliceGroup; -// -//import java.io.*; -//import java.nio.charset.StandardCharsets; -//import java.nio.file.Files; -//import java.nio.file.Path; -//import java.time.LocalDate; -//import java.time.format.DateTimeFormatter; -//import java.time.temporal.ChronoUnit; -//import java.util.List; -// -//public class HospitalNumbersFromEventsPlotter { -// -// -// private static void aggregateAndProducePlots(Path output, List pathList) throws IOException { -// -// -// // read hospitalization tsv for all seeds and aggregate them! -// // NOTE: all other parameters should be the same, otherwise the results will be useless! -// Int2DoubleSortedMap intakeHosp = new Int2DoubleAVLTreeMap(); -// Int2DoubleSortedMap intakeIcu = new Int2DoubleAVLTreeMap(); -// Int2DoubleSortedMap occupancyHosp = new Int2DoubleAVLTreeMap(); -// Int2DoubleSortedMap occupancyIcu = new Int2DoubleAVLTreeMap(); -// -//// Int2DoubleSortedMap hospNoImmunity = new Int2DoubleAVLTreeMap(); -//// Int2DoubleSortedMap hospBaseImmunity = new Int2DoubleAVLTreeMap(); -//// Int2DoubleSortedMap hospBoosted = new Int2DoubleAVLTreeMap(); -//// -//// Int2DoubleSortedMap incNoImmunity = new Int2DoubleAVLTreeMap(); -//// Int2DoubleSortedMap incBaseImmunity = new Int2DoubleAVLTreeMap(); -//// Int2DoubleSortedMap incBoosted = new Int2DoubleAVLTreeMap(); -// -// -// -// -// for (Path path : pathList) { -// -// String id = AnalysisCommand.getScenarioPrefix(path); -// -// final Path tsvPath = path.resolve(id + "post.hospital" + outputAppendix + ".tsv"); -// -// try (CSVParser parser = new CSVParser(Files.newBufferedReader(tsvPath), CSVFormat.DEFAULT.withDelimiter('\t').withFirstRecordAsHeader())) { -// -// for (CSVRecord record : parser) { -// -// int day = Integer.parseInt(record.get(DAY)); +package org.matsim.episim.analysis; + +import it.unimi.dsi.fastutil.ints.*; +import org.apache.commons.csv.CSVFormat; +import org.apache.commons.csv.CSVParser; +import org.apache.commons.csv.CSVPrinter; +import org.apache.commons.csv.CSVRecord; +import org.matsim.episim.model.VirusStrain; +import org.matsim.run.AnalysisCommand; +import tech.tablesaw.api.*; +import tech.tablesaw.plotly.components.Axis; +import tech.tablesaw.plotly.components.Figure; +import tech.tablesaw.plotly.components.Layout; +import tech.tablesaw.plotly.components.Page; +import tech.tablesaw.plotly.traces.ScatterTrace; +import tech.tablesaw.table.TableSliceGroup; + +import java.io.*; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.time.LocalDate; +import java.time.format.DateTimeFormatter; +import java.time.temporal.ChronoUnit; +import java.util.List; + +public class HospitalNumbersFromEventsPlotter { + private static final String DATE = "date"; + private static final String DAY = "day"; + final static String INTAKES_HOSP = "intakesHosp"; + final static String INTAKES_ICU = "intakesIcu"; + final static String OCCUPANCY_HOSP = "occupancyHosp"; + final static String OCCUPANCY_ICU = "occupancyIcu"; + + private final static double populationCntOfficialNrw = 17_930_000; + private final static double populationCntOfficialKoelln = 919_936; + + + + + static void aggregateAndProducePlots(Path output, List pathList, String outputAppendix, LocalDate startDate, String strainToPlot) throws IOException { + + + // read hospitalization tsv for all seeds and aggregate them! + // NOTE: all other parameters should be the same, otherwise the results will be useless! + Int2DoubleSortedMap intakeHosp = new Int2DoubleAVLTreeMap(); + Int2DoubleSortedMap intakeIcu = new Int2DoubleAVLTreeMap(); + Int2DoubleSortedMap occupancyHosp = new Int2DoubleAVLTreeMap(); + Int2DoubleSortedMap occupancyIcu = new Int2DoubleAVLTreeMap(); + +// Int2DoubleSortedMap hospNoImmunity = new Int2DoubleAVLTreeMap(); +// Int2DoubleSortedMap hospBaseImmunity = new Int2DoubleAVLTreeMap(); +// Int2DoubleSortedMap hospBoosted = new Int2DoubleAVLTreeMap(); +// +// Int2DoubleSortedMap incNoImmunity = new Int2DoubleAVLTreeMap(); +// Int2DoubleSortedMap incBaseImmunity = new Int2DoubleAVLTreeMap(); +// Int2DoubleSortedMap incBoosted = new Int2DoubleAVLTreeMap(); + + + + + for (Path path : pathList) { + + String id = AnalysisCommand.getScenarioPrefix(path); + + final Path tsvPath = path.resolve(id + "post.hospital.tsv"); + + try (CSVParser parser = new CSVParser(Files.newBufferedReader(tsvPath), CSVFormat.DEFAULT.withDelimiter('\t').withFirstRecordAsHeader())) { + + for (CSVRecord record : parser) { + + if (!record.get("severity").equals(strainToPlot)) { + continue; + } + + int day = Integer.parseInt(record.get(DAY)); + + double n = Double.parseDouble(record.get("n")); + switch (record.get("measurement")) { + case INTAKES_HOSP: + intakeHosp.mergeDouble(day, n / pathList.size(), Double::sum); + break; + case INTAKES_ICU: + intakeIcu.mergeDouble(day, n / pathList.size(), Double::sum); + break; + case OCCUPANCY_HOSP: + occupancyHosp.mergeDouble(day, n / pathList.size(), Double::sum); + break; + case OCCUPANCY_ICU: + occupancyIcu.mergeDouble(day, n / pathList.size(), Double::sum); + break; + default: + throw new RuntimeException("not valid measurement"); + } + // intakeHosp.mergeDouble(day, Double.parseDouble(record.get(INTAKES_HOSP)) / pathList.size(), Double::sum); // intakeIcu.mergeDouble(day, Double.parseDouble(record.get(INTAKES_ICU)) / pathList.size(), Double::sum); // occupancyHosp.mergeDouble(day, Double.parseDouble(record.get(OCCUPANCY_HOSP)) / pathList.size(), Double::sum); // occupancyIcu.mergeDouble(day, Double.parseDouble(record.get(OCCUPANCY_ICU)) / pathList.size(), Double::sum); -// -//// hospNoImmunity.mergeDouble(day, Double.parseDouble(record.get("hospNoImmunity")) / pathList.size(), Double::sum); -//// hospBaseImmunity.mergeDouble(day, Double.parseDouble(record.get("hospBaseImmunity")) / pathList.size(), Double::sum); -//// hospBoosted.mergeDouble(day, Double.parseDouble(record.get("hospBoosted")) / pathList.size(), Double::sum); -//// -//// incNoImmunity.mergeDouble(day, Double.parseDouble(record.get("incNoImmunity")) / pathList.size(), Double::sum); -//// incBaseImmunity.mergeDouble(day, Double.parseDouble(record.get("incBaseImmunity")) / pathList.size(), Double::sum); -//// incBoosted.mergeDouble(day, Double.parseDouble(record.get("incBoosted")) / pathList.size(), Double::sum); -// } -// } -// } -// -// // read rki data and add to tsv -// Int2DoubleMap rkiHospIncidence = new Int2DoubleAVLTreeMap(); -// Int2DoubleMap rkiHospIncidenceAdj = new Int2DoubleAVLTreeMap(); -// try (CSVParser parser = new CSVParser(Files.newBufferedReader(Path.of("../covid-sim/src/assets/rki-deutschland-hospitalization.csv")), -// CSVFormat.DEFAULT.withDelimiter(',').withFirstRecordAsHeader())) { -// -// for (CSVRecord record : parser) { -// if (!record.get("Bundesland").equals("Nordrhein-Westfalen")) { -// continue; -// } -// LocalDate date = LocalDate.parse((record.get("Datum"))); -// int day = (int) startDate.until(date, ChronoUnit.DAYS); -// -// double incidence; -// try { -// incidence = Double.parseDouble(record.get("PS_adjustierte_7T_Hospitalisierung_Inzidenz")); -// } catch (NumberFormatException e) { -// incidence = Double.NaN; -// -// } -// -// -// rkiHospIncidence.put(day, incidence); -// -// double incidenceAdj; + +// hospNoImmunity.mergeDouble(day, Double.parseDouble(record.get("hospNoImmunity")) / pathList.size(), Double::sum); +// hospBaseImmunity.mergeDouble(day, Double.parseDouble(record.get("hospBaseImmunity")) / pathList.size(), Double::sum); +// hospBoosted.mergeDouble(day, Double.parseDouble(record.get("hospBoosted")) / pathList.size(), Double::sum); +// +// incNoImmunity.mergeDouble(day, Double.parseDouble(record.get("incNoImmunity")) / pathList.size(), Double::sum); +// incBaseImmunity.mergeDouble(day, Double.parseDouble(record.get("incBaseImmunity")) / pathList.size(), Double::sum); +// incBoosted.mergeDouble(day, Double.parseDouble(record.get("incBoosted")) / pathList.size(), Double::sum); + } + } + } + + // read rki data and add to tsv + Int2DoubleMap rkiHospIncidence = new Int2DoubleAVLTreeMap(); + Int2DoubleMap rkiHospIncidenceAdj = new Int2DoubleAVLTreeMap(); + try (CSVParser parser = new CSVParser(Files.newBufferedReader(Path.of("../covid-sim/src/assets/rki-deutschland-hospitalization.csv")), + CSVFormat.DEFAULT.withDelimiter(',').withFirstRecordAsHeader())) { + + for (CSVRecord record : parser) { + if (!record.get("Bundesland").equals("Nordrhein-Westfalen")) { + continue; + } + LocalDate date = LocalDate.parse((record.get("Datum"))); + int day = (int) startDate.until(date, ChronoUnit.DAYS); + + double incidence; + try { + incidence = Double.parseDouble(record.get("PS_adjustierte_7T_Hospitalisierung_Inzidenz")); + } catch (NumberFormatException e) { + incidence = Double.NaN; + + } + + + rkiHospIncidence.put(day, incidence); + + double incidenceAdj; + if (date.isBefore(LocalDate.of(2022, 11, 1))) { + incidenceAdj = incidence * 2 / 3; + } else{ + incidenceAdj = incidence / 3; + } + + // if (date.isBefore(LocalDate.of(2020, 12, 10))) { // incidenceAdj = incidence; // } else if (date.isBefore(LocalDate.of(2021, 1, 11))) { @@ -116,133 +158,133 @@ // } else { // incidenceAdj = 11./14 * incidence; // } -// rkiHospIncidenceAdj.put(day, incidenceAdj); -// } -// } -// -// Int2IntMap reportedIcuCases = new Int2IntAVLTreeMap(); -// -// try (CSVParser parser = new CSVParser(Files.newBufferedReader(Path.of("../public-svn/matsim/scenarios/countries/de/episim/original-data/Fallzahlen/DIVI/nrw-divi-processed-ICUincidence-until20220504.csv")), -// CSVFormat.DEFAULT.withDelimiter(',').withFirstRecordAsHeader())) { -// -// for (CSVRecord record : parser) { -// String dateStr = record.get("date").split("T")[0]; -// LocalDate date = LocalDate.parse(dateStr); -// int day = (int) startDate.until(date, ChronoUnit.DAYS); -// -// int cases = 0; -// try { -// cases = Integer.parseInt(record.get("erstaufnahmen")); -// } catch (NumberFormatException ignored) { -// -// } -// -// reportedIcuCases.put(day, cases); -// } -// } -// -// -// Int2DoubleMap reportedIcuIncidence = new Int2DoubleAVLTreeMap(); -// for (Integer day : reportedIcuCases.keySet()) { -// // calculates Incidence - 7day hospitalizations per 100,000 residents -// -// double xxx = getWeeklyHospitalizations(reportedIcuCases, day) * 100_000. / populationCntOfficialNrw; -// reportedIcuIncidence.put((int) day, xxx); -// -// } -// -// Int2DoubleMap hospIncidenceKoeln = new Int2DoubleAVLTreeMap(); -// try (CSVParser parser = new CSVParser(Files.newBufferedReader(Path.of("../public-svn/matsim/scenarios/countries/de/episim/original-data/hospital-cases/cologne/KoelnHospIncidence.csv")), -// CSVFormat.DEFAULT.withDelimiter(';').withFirstRecordAsHeader())) { -// -// for (CSVRecord record : parser) { -// String dateStr = record.get("date").split("T")[0]; -// LocalDate date = LocalDate.parse(dateStr); -// int day = (int) startDate.until(date, ChronoUnit.DAYS); -// -// double incidence; -// try { -// incidence = Double.parseDouble(record.get("7-Tage-KH-Inz-Koelln")) * 2 ; //TODO -// -// } catch (NumberFormatException e) { -// incidence = 0.; -// } -// -// hospIncidenceKoeln.put(day, incidence); -// } -// } -// -// // read rki data and add to columns -// // pink plot from covid-sim: general beds -// // https://svn.vsp.tu-berlin.de/repos/public-svn/matsim/scenarios/countries/de/episim/original-data/hospital-cases/cologne/KoelnAllgemeinpatienten.csv (pinke Linie) -// Int2DoubleMap reportedBeds = new Int2DoubleAVLTreeMap(); -// Int2DoubleMap reportedBedsAdj = new Int2DoubleAVLTreeMap(); -// try (CSVParser parser = new CSVParser(Files.newBufferedReader(Path.of("../public-svn/matsim/scenarios/countries/de/episim/original-data/hospital-cases/cologne/KoelnAllgemeinpatienten.csv")), -// CSVFormat.DEFAULT.withDelimiter(',').withFirstRecordAsHeader())) { -// -// for (CSVRecord record : parser) { -// String dateStr = record.get("date").split("T")[0]; -// LocalDate date = LocalDate.parse(dateStr); -// int day = (int) startDate.until(date, ChronoUnit.DAYS); -// -// double incidence; -// try { -// incidence = Double.parseDouble(record.get("allgemeinpatienten")) * 100_000. / populationCntOfficialKoelln; -// } catch (NumberFormatException e) { -// incidence = 0.; -// } -// -// reportedBeds.put(day, incidence); -// -// -// -// double incidenceAdj; -// if (date.isBefore(LocalDate.of(2020, 12, 10))) { -// incidenceAdj = incidence; -// } else if (date.isBefore(LocalDate.of(2021, 1, 11))) { -// incidenceAdj = 23. / 16. * incidence; -// } else if (date.isBefore(LocalDate.of(2021, 3, 22))) { -// incidenceAdj = 8. / 6. * incidence; -// } else if (date.isBefore(LocalDate.of(2021, 5, 3))) { -// incidenceAdj = 15./11. * incidence; -// } else if (date.isBefore(LocalDate.of(2021, 11, 8))) { -// incidenceAdj = incidence; -// } else if (date.isBefore(LocalDate.of(2021, 12, 6))) { -// incidenceAdj = 16. / 13. * incidence; -// } else if (date.isBefore(LocalDate.of(2022, 1, 24))) { -// incidenceAdj = incidence; -// } else { -// incidenceAdj = 11./14 * incidence; -// } -// reportedBedsAdj.put(day, incidenceAdj); -// } -// } -// -// -// //green plot from covid-sim (Ich denke, das ist die Spalte "faelle_covid_aktuell", aber ich bin nicht ganz sicher.) -// //https://svn.vsp.tu-berlin.de/repos/public-svn/matsim/scenarios/countries/de/episim/original-data/Fallzahlen/DIVI/cologne-divi-processed.csv (grüne Linie) -// Int2DoubleMap reportedBedsICU = new Int2DoubleAVLTreeMap(); -// try (CSVParser parser = new CSVParser(Files.newBufferedReader(Path.of("../public-svn/matsim/scenarios/countries/de/episim/original-data/Fallzahlen/DIVI/cologne-divi-processed.csv")), -// CSVFormat.DEFAULT.withDelimiter(',').withFirstRecordAsHeader())) { -// -// for (CSVRecord record : parser) { -// String dateStr = record.get("date").split("T")[0]; -// LocalDate date = LocalDate.parse(dateStr); -// int day = (int) startDate.until(date, ChronoUnit.DAYS); -// -// double incidence = 0.; -// try { -// incidence = Double.parseDouble(record.get("faelle_covid_aktuell")) * 100_000. / populationCntOfficialKoelln; -// } catch (NumberFormatException ignored) { -// -// } -// -// reportedBedsICU.put(day, incidence); -// } -// } -// -// -// //https://www.dkgev.de/dkg/coronavirus-fakten-und-infos/aktuelle-bettenbelegung/ + rkiHospIncidenceAdj.put(day, incidenceAdj); + } + } + + Int2IntMap reportedIcuCases = new Int2IntAVLTreeMap(); + + try (CSVParser parser = new CSVParser(Files.newBufferedReader(Path.of("../public-svn/matsim/scenarios/countries/de/episim/original-data/Fallzahlen/DIVI/nrw-divi-processed-ICUincidence-until20220504.csv")), + CSVFormat.DEFAULT.withDelimiter(',').withFirstRecordAsHeader())) { + + for (CSVRecord record : parser) { + String dateStr = record.get("date").split("T")[0]; + LocalDate date = LocalDate.parse(dateStr); + int day = (int) startDate.until(date, ChronoUnit.DAYS); + + int cases = 0; + try { + cases = Integer.parseInt(record.get("erstaufnahmen")); + } catch (NumberFormatException ignored) { + + } + + reportedIcuCases.put(day, cases); + } + } + + + Int2DoubleMap reportedIcuIncidence = new Int2DoubleAVLTreeMap(); + for (Integer day : reportedIcuCases.keySet()) { + // calculates Incidence - 7day hospitalizations per 100,000 residents + + double xxx = HospitalNumbersFromEvents.getWeeklyHospitalizations(reportedIcuCases, day) * 100_000. / populationCntOfficialNrw; + reportedIcuIncidence.put((int) day, xxx); + + } + + Int2DoubleMap hospIncidenceKoeln = new Int2DoubleAVLTreeMap(); + try (CSVParser parser = new CSVParser(Files.newBufferedReader(Path.of("../public-svn/matsim/scenarios/countries/de/episim/original-data/hospital-cases/cologne/KoelnHospIncidence.csv")), + CSVFormat.DEFAULT.withDelimiter(';').withFirstRecordAsHeader())) { + + for (CSVRecord record : parser) { + String dateStr = record.get("date").split("T")[0]; + LocalDate date = LocalDate.parse(dateStr); + int day = (int) startDate.until(date, ChronoUnit.DAYS); + + double incidence; + try { + incidence = Double.parseDouble(record.get("7-Tage-KH-Inz-Koelln")) * 2 ; //TODO + + } catch (NumberFormatException e) { + incidence = 0.; + } + + hospIncidenceKoeln.put(day, incidence); + } + } + + // read rki data and add to columns + // pink plot from covid-sim: general beds + // https://svn.vsp.tu-berlin.de/repos/public-svn/matsim/scenarios/countries/de/episim/original-data/hospital-cases/cologne/KoelnAllgemeinpatienten.csv (pinke Linie) + Int2DoubleMap reportedBeds = new Int2DoubleAVLTreeMap(); + Int2DoubleMap reportedBedsAdj = new Int2DoubleAVLTreeMap(); + try (CSVParser parser = new CSVParser(Files.newBufferedReader(Path.of("../public-svn/matsim/scenarios/countries/de/episim/original-data/hospital-cases/cologne/KoelnAllgemeinpatienten.csv")), + CSVFormat.DEFAULT.withDelimiter(',').withFirstRecordAsHeader())) { + + for (CSVRecord record : parser) { + String dateStr = record.get("date").split("T")[0]; + LocalDate date = LocalDate.parse(dateStr); + int day = (int) startDate.until(date, ChronoUnit.DAYS); + + double incidence; + try { + incidence = Double.parseDouble(record.get("allgemeinpatienten")) * 100_000. / populationCntOfficialKoelln; + } catch (NumberFormatException e) { + incidence = 0.; + } + + reportedBeds.put(day, incidence); + + + + double incidenceAdj; + if (date.isBefore(LocalDate.of(2020, 12, 10))) { + incidenceAdj = incidence; + } else if (date.isBefore(LocalDate.of(2021, 1, 11))) { + incidenceAdj = 23. / 16. * incidence; + } else if (date.isBefore(LocalDate.of(2021, 3, 22))) { + incidenceAdj = 8. / 6. * incidence; + } else if (date.isBefore(LocalDate.of(2021, 5, 3))) { + incidenceAdj = 15./11. * incidence; + } else if (date.isBefore(LocalDate.of(2021, 11, 8))) { + incidenceAdj = incidence; + } else if (date.isBefore(LocalDate.of(2021, 12, 6))) { + incidenceAdj = 16. / 13. * incidence; + } else if (date.isBefore(LocalDate.of(2022, 1, 24))) { + incidenceAdj = incidence; + } else { + incidenceAdj = 11./14 * incidence; + } + reportedBedsAdj.put(day, incidenceAdj); + } + } + + + //green plot from covid-sim (Ich denke, das ist die Spalte "faelle_covid_aktuell", aber ich bin nicht ganz sicher.) + //https://svn.vsp.tu-berlin.de/repos/public-svn/matsim/scenarios/countries/de/episim/original-data/Fallzahlen/DIVI/cologne-divi-processed.csv (grüne Linie) + Int2DoubleMap reportedBedsICU = new Int2DoubleAVLTreeMap(); + try (CSVParser parser = new CSVParser(Files.newBufferedReader(Path.of("../public-svn/matsim/scenarios/countries/de/episim/original-data/Fallzahlen/DIVI/cologne-divi-processed.csv")), + CSVFormat.DEFAULT.withDelimiter(',').withFirstRecordAsHeader())) { + + for (CSVRecord record : parser) { + String dateStr = record.get("date").split("T")[0]; + LocalDate date = LocalDate.parse(dateStr); + int day = (int) startDate.until(date, ChronoUnit.DAYS); + + double incidence = 0.; + try { + incidence = Double.parseDouble(record.get("faelle_covid_aktuell")) * 100_000. / populationCntOfficialKoelln; + } catch (NumberFormatException ignored) { + + } + + reportedBedsICU.put(day, incidence); + } + } + + + //https://www.dkgev.de/dkg/coronavirus-fakten-und-infos/aktuelle-bettenbelegung/ // Int2DoubleMap reportedBedsNrw = new Int2DoubleAVLTreeMap(); // Int2DoubleMap reportedBedsIcuNrw = new Int2DoubleAVLTreeMap(); // try (CSVParser parser = new CSVParser(Files.newBufferedReader(Path.of("/Users/jakob/Downloads/Covid_csvgesamt(2).csv")), @@ -272,208 +314,208 @@ // // } // } -// -// -// // https://datawrapper.dwcdn.net/sjUZF/334/ -// Int2DoubleMap reportedBedsNrw2 = new Int2DoubleAVLTreeMap(); -// Int2DoubleMap reportedBedsIcuNrw2 = new Int2DoubleAVLTreeMap(); -// try (CSVParser parser = new CSVParser(Files.newBufferedReader(Path.of("../public-svn/matsim/scenarios/countries/de/episim/original-data/hospital-cases/cologne/nrwBettBelegung.csv")), -// CSVFormat.DEFAULT.withDelimiter(',').withFirstRecordAsHeader())) { -// -// for (CSVRecord record : parser) { -// -// -// String dateStr = record.get("Datum"); -// LocalDate date = LocalDate.parse(dateStr, DateTimeFormatter.ofPattern("d.M.yyyy")); -// int day = (int) startDate.until(date, ChronoUnit.DAYS); -// -// try { -// double incidence = Double.parseDouble(record.get("Stationär")) * 100_000. / populationCntOfficialNrw; -// reportedBedsNrw2.put(day, incidence); -// } catch (NumberFormatException ignored) { -// } -// -// try { -// double incidence = Double.parseDouble(record.get("Patienten auf der
Intensivstation")) * 100_000. / populationCntOfficialNrw; -// reportedBedsIcuNrw2.put(day, incidence); -// } catch (NumberFormatException ignored) { -// } -// -// -// } -// } -// -// -// // Produce TSV w/ aggregated data as well as all rki numbers -// try (CSVPrinter printer = new CSVPrinter(Files.newBufferedWriter(output.resolve("post.hospital.agg" + outputAppendix + ".tsv")), CSVFormat.DEFAULT.withDelimiter('\t'))) { -// -// printer.printRecord(DAY, DATE, INTAKES_HOSP, INTAKES_ICU, OCCUPANCY_HOSP, OCCUPANCY_ICU, "rkiIncidence", "rkiHospRate", "rkiCriticalRate"); //"hospNoImmunity", "hospBaseImmunity", "hospBoosted", "incNoImmunity", "incBaseImmunity", "incBoosted"); -// -// double maxIteration = Double.max(Double.max(intakeHosp.lastIntKey(), intakeIcu.lastIntKey()), Double.max(occupancyHosp.lastIntKey(), occupancyIcu.lastIntKey())); -// -// for (int day = 0; day <= maxIteration; day++) { -// LocalDate date = startDate.plusDays(day); -// printer.printRecord( -// day, -// date, -// intakeHosp.get(day), -// intakeIcu.get(day), -// occupancyHosp.get(day), -// occupancyIcu.get(day), -// rkiHospIncidence.get(day), -// reportedBeds.get(day), -// reportedBedsICU.get(day) -//// hospNoImmunity.get(day), -//// hospBaseImmunity.get(day), -//// hospBoosted.get(day), -//// incNoImmunity.get(day), -//// incBaseImmunity.get(day), -//// incBoosted.get(day) -// ); -// } -// } -// -// // PLOT 1: People admitted to hospital -// { -// IntColumn records = IntColumn.create("day"); -// DateColumn recordsDate = DateColumn.create("date"); -// DoubleColumn values = DoubleColumn.create("hospitalizations"); -// StringColumn groupings = StringColumn.create("scenario"); -// -// // model: intakeHosp -// for (Int2DoubleMap.Entry entry : intakeHosp.int2DoubleEntrySet()) { -// int day = entry.getIntKey(); -// records.append(day); -// recordsDate.append(startDate.plusDays(day)); -// values.append(intakeHosp.getOrDefault(day, Double.NaN)); -// groupings.append("model: intakeHosp"); -// } -// -// // model: intakeIcu -// for (Int2DoubleMap.Entry entry : intakeIcu.int2DoubleEntrySet()) { -// int day = entry.getIntKey(); -// records.append(day); -// recordsDate.append(startDate.plusDays(day)); -// values.append(intakeIcu.getOrDefault(day, Double.NaN)); -// groupings.append("model: intakeICU"); -// } -// -// -// for (Int2DoubleMap.Entry entry : rkiHospIncidence.int2DoubleEntrySet()) { -// int day = entry.getIntKey(); -// records.append(day); -// recordsDate.append(startDate.plusDays(day)); -// final double value = rkiHospIncidence.getOrDefault(day, Double.NaN); -// if (Double.isNaN(value)) { -// values.appendMissing(); -// } else { -// values.append(value); -// } -// groupings.append("reported: intakeHosp (rki, nrw adjusted)"); -// } -// -// for (Int2DoubleMap.Entry entry : rkiHospIncidenceAdj.int2DoubleEntrySet()) { -// int day = entry.getIntKey(); -// records.append(day); -// recordsDate.append(startDate.plusDays(day)); -// final double value = rkiHospIncidenceAdj.getOrDefault(day, Double.NaN); -// if (Double.isNaN(value)) { -// values.appendMissing(); -// } else { -// values.append(value); -// } -// groupings.append("reported: intakeHosp (rki, nrw adjusted, SARI)"); -// } -// -// for (Int2DoubleMap.Entry entry : hospIncidenceKoeln.int2DoubleEntrySet()) { -// int day = entry.getIntKey(); -// records.append(day); -// recordsDate.append(startDate.plusDays(day)); -// final double value = hospIncidenceKoeln.getOrDefault(day, Double.NaN); -// if (Double.isNaN(value)) { -// values.appendMissing(); -// } else { -// values.append(value); -// } -// groupings.append("reported: intakeHosp (köln)"); -// } -// -// for (Int2DoubleMap.Entry entry : reportedIcuIncidence.int2DoubleEntrySet()) { -// int day = entry.getIntKey(); -// records.append(day); -// recordsDate.append(startDate.plusDays(day)); -// final double value = reportedIcuIncidence.getOrDefault(day, Double.NaN); -// if (Double.isNaN(value)) { -// values.appendMissing(); -// } else { -// values.append(value); -// } -// groupings.append("reported: intakeIcu (divi, nrw)"); -// } -// -// -// producePlot(recordsDate, values, groupings, "", "7-Tage Hospitalisierungsinzidenz", "HospIncidence" + outputAppendix + ".html"); -// } -// -// -// // PLOT 2: People taking up beds in hospital (regular and ICU) -// { -// IntColumn records = IntColumn.create("day"); -// DateColumn recordsDate = DateColumn.create("date"); -// DoubleColumn values = DoubleColumn.create("hospitalizations"); -// StringColumn groupings = StringColumn.create("scenario"); -// -// -// for (Int2DoubleMap.Entry entry : occupancyHosp.int2DoubleEntrySet()) { -// int day = entry.getIntKey(); -// records.append(day); -// recordsDate.append(startDate.plusDays(day)); -// -// values.append(occupancyHosp.get(day)); -// groupings.append("generalBeds"); -// } -// -// for (Int2DoubleMap.Entry entry : occupancyIcu.int2DoubleEntrySet()) { -// int day = entry.getIntKey(); -// records.append(day); -// recordsDate.append(startDate.plusDays(day)); -// -// values.append(occupancyIcu.get(day)); -// groupings.append("ICUBeds"); -// } -// -// -// for (Int2DoubleMap.Entry entry : reportedBeds.int2DoubleEntrySet()) { -// int day = entry.getIntKey(); -// records.append(day); -// recordsDate.append(startDate.plusDays(day)); -// -// values.append(entry.getDoubleValue()); -// groupings.append("Reported: General Beds"); -// -// } -// -// for (Int2DoubleMap.Entry entry : reportedBedsAdj.int2DoubleEntrySet()) { -// int day = entry.getIntKey(); -// records.append(day); -// recordsDate.append(startDate.plusDays(day)); -// -// values.append(entry.getDoubleValue()); -// groupings.append("Reported: General Beds (SARI)"); -// } -// -// -// for (Int2DoubleMap.Entry entry : reportedBedsICU.int2DoubleEntrySet()) { -// int day = entry.getIntKey(); -// records.append(day); -// recordsDate.append(startDate.plusDays(day)); -// -// values.append(entry.getDoubleValue()); -// groupings.append("Reported: ICU Beds"); -// -// } -// -// + + + // https://datawrapper.dwcdn.net/sjUZF/334/ + Int2DoubleMap reportedBedsNrw2 = new Int2DoubleAVLTreeMap(); + Int2DoubleMap reportedBedsIcuNrw2 = new Int2DoubleAVLTreeMap(); + try (CSVParser parser = new CSVParser(Files.newBufferedReader(Path.of("../public-svn/matsim/scenarios/countries/de/episim/original-data/hospital-cases/cologne/nrwBettBelegung.csv")), + CSVFormat.DEFAULT.withDelimiter(',').withFirstRecordAsHeader())) { + + for (CSVRecord record : parser) { + + + String dateStr = record.get("Datum"); + LocalDate date = LocalDate.parse(dateStr, DateTimeFormatter.ofPattern("d.M.yyyy")); + int day = (int) startDate.until(date, ChronoUnit.DAYS); + + try { + double incidence = Double.parseDouble(record.get("Stationär")) * 100_000. / populationCntOfficialNrw; + reportedBedsNrw2.put(day, incidence); + } catch (NumberFormatException ignored) { + } + + try { + double incidence = Double.parseDouble(record.get("Patienten auf der
Intensivstation")) * 100_000. / populationCntOfficialNrw; + reportedBedsIcuNrw2.put(day, incidence); + } catch (NumberFormatException ignored) { + } + + + } + } + + + // Produce TSV w/ aggregated data as well as all rki numbers + try (CSVPrinter printer = new CSVPrinter(Files.newBufferedWriter(output.resolve("post.hospital.agg.tsv")), CSVFormat.DEFAULT.withDelimiter('\t'))) { + + printer.printRecord(DAY, DATE, INTAKES_HOSP, INTAKES_ICU, OCCUPANCY_HOSP, OCCUPANCY_ICU, "rkiIncidence", "rkiHospRate", "rkiCriticalRate"); //"hospNoImmunity", "hospBaseImmunity", "hospBoosted", "incNoImmunity", "incBaseImmunity", "incBoosted"); + + double maxIteration = Double.max(Double.max(intakeHosp.lastIntKey(), intakeIcu.lastIntKey()), Double.max(occupancyHosp.lastIntKey(), occupancyIcu.lastIntKey())); + + for (int day = 0; day <= maxIteration; day++) { + LocalDate date = startDate.plusDays(day); + printer.printRecord( + day, + date, + intakeHosp.get(day), + intakeIcu.get(day), + occupancyHosp.get(day), + occupancyIcu.get(day), + rkiHospIncidence.get(day), + reportedBeds.get(day), + reportedBedsICU.get(day) +// hospNoImmunity.get(day), +// hospBaseImmunity.get(day), +// hospBoosted.get(day), +// incNoImmunity.get(day), +// incBaseImmunity.get(day), +// incBoosted.get(day) + ); + } + } + + // PLOT 1: People admitted to hospital + { + IntColumn records = IntColumn.create("day"); + DateColumn recordsDate = DateColumn.create("date"); + DoubleColumn values = DoubleColumn.create("hospitalizations"); + StringColumn groupings = StringColumn.create("scenario"); + + // model: intakeHosp + for (Int2DoubleMap.Entry entry : intakeHosp.int2DoubleEntrySet()) { + int day = entry.getIntKey(); + records.append(day); + recordsDate.append(startDate.plusDays(day)); + values.append(intakeHosp.getOrDefault(day, Double.NaN)); + groupings.append("model: intakeHosp"); + } + + // model: intakeIcu + for (Int2DoubleMap.Entry entry : intakeIcu.int2DoubleEntrySet()) { + int day = entry.getIntKey(); + records.append(day); + recordsDate.append(startDate.plusDays(day)); + values.append(intakeIcu.getOrDefault(day, Double.NaN)); + groupings.append("model: intakeICU"); + } + + + for (Int2DoubleMap.Entry entry : rkiHospIncidence.int2DoubleEntrySet()) { + int day = entry.getIntKey(); + records.append(day); + recordsDate.append(startDate.plusDays(day)); + final double value = rkiHospIncidence.getOrDefault(day, Double.NaN); + if (Double.isNaN(value)) { + values.appendMissing(); + } else { + values.append(value); + } + groupings.append("reported: intakeHosp (rki, nrw adjusted) WITH Covid"); + } + + for (Int2DoubleMap.Entry entry : rkiHospIncidenceAdj.int2DoubleEntrySet()) { + int day = entry.getIntKey(); + records.append(day); + recordsDate.append(startDate.plusDays(day)); + final double value = rkiHospIncidenceAdj.getOrDefault(day, Double.NaN); + if (Double.isNaN(value)) { + values.appendMissing(); + } else { + values.append(value); + } + groupings.append("reported: intakeHosp (rki, nrw adjusted) FROM Covid"); + } + + for (Int2DoubleMap.Entry entry : hospIncidenceKoeln.int2DoubleEntrySet()) { + int day = entry.getIntKey(); + records.append(day); + recordsDate.append(startDate.plusDays(day)); + final double value = hospIncidenceKoeln.getOrDefault(day, Double.NaN); + if (Double.isNaN(value)) { + values.appendMissing(); + } else { + values.append(value); + } + groupings.append("reported: intakeHosp (köln)"); + } + + for (Int2DoubleMap.Entry entry : reportedIcuIncidence.int2DoubleEntrySet()) { + int day = entry.getIntKey(); + records.append(day); + recordsDate.append(startDate.plusDays(day)); + final double value = reportedIcuIncidence.getOrDefault(day, Double.NaN); + if (Double.isNaN(value)) { + values.appendMissing(); + } else { + values.append(value); + } + groupings.append("reported: intakeIcu (divi, nrw)"); + } + + + producePlot(recordsDate, values, groupings, "", "7-Tage Hospitalisierungsinzidenz", "HospIncidence" + outputAppendix + ".html", output); + } + + + // PLOT 2: People taking up beds in hospital (regular and ICU) + { + IntColumn records = IntColumn.create("day"); + DateColumn recordsDate = DateColumn.create("date"); + DoubleColumn values = DoubleColumn.create("hospitalizations"); + StringColumn groupings = StringColumn.create("scenario"); + + + for (Int2DoubleMap.Entry entry : occupancyHosp.int2DoubleEntrySet()) { + int day = entry.getIntKey(); + records.append(day); + recordsDate.append(startDate.plusDays(day)); + + values.append(occupancyHosp.get(day)); + groupings.append("generalBeds"); + } + + for (Int2DoubleMap.Entry entry : occupancyIcu.int2DoubleEntrySet()) { + int day = entry.getIntKey(); + records.append(day); + recordsDate.append(startDate.plusDays(day)); + + values.append(occupancyIcu.get(day)); + groupings.append("ICUBeds"); + } + + + for (Int2DoubleMap.Entry entry : reportedBeds.int2DoubleEntrySet()) { + int day = entry.getIntKey(); + records.append(day); + recordsDate.append(startDate.plusDays(day)); + + values.append(entry.getDoubleValue()); + groupings.append("Reported: General Beds"); + + } + + for (Int2DoubleMap.Entry entry : reportedBedsAdj.int2DoubleEntrySet()) { + int day = entry.getIntKey(); + records.append(day); + recordsDate.append(startDate.plusDays(day)); + + values.append(entry.getDoubleValue()); + groupings.append("Reported: General Beds (SARI)"); + } + + + for (Int2DoubleMap.Entry entry : reportedBedsICU.int2DoubleEntrySet()) { + int day = entry.getIntKey(); + records.append(day); + recordsDate.append(startDate.plusDays(day)); + + values.append(entry.getDoubleValue()); + groupings.append("Reported: ICU Beds"); + + } + + // for (Int2DoubleMap.Entry entry : reportedBedsNrw.int2DoubleEntrySet()) { // int day = entry.getIntKey(); // records.append(day); @@ -483,8 +525,8 @@ // groupings.append("Reported: General Beds (NRW)"); // // } -// -// + + // for (Int2DoubleMap.Entry entry : reportedBedsIcuNrw.int2DoubleEntrySet()) { // int day = entry.getIntKey(); // records.append(day); @@ -494,68 +536,68 @@ // groupings.append("Reported: ICU Beds (NRW)"); // // } -// -// for (Int2DoubleMap.Entry entry : reportedBedsNrw2.int2DoubleEntrySet()) { -// int day = entry.getIntKey(); -// records.append(day); -// recordsDate.append(startDate.plusDays(day)); -// -// values.append(entry.getDoubleValue()); -// groupings.append("Reported: General Beds (NRW2)"); -// -// } -// -// for (Int2DoubleMap.Entry entry : reportedBedsIcuNrw2.int2DoubleEntrySet()) { -// int day = entry.getIntKey(); -// records.append(day); -// recordsDate.append(startDate.plusDays(day)); -// -// values.append(entry.getDoubleValue()); -// groupings.append("Reported: ICU Beds (NRW2)"); -// -// } -// -// -// -// // Make plot -// producePlot(recordsDate, values, groupings, "Filled Beds", "Beds Filled / 100k Population", "FilledBeds" + outputAppendix + ".html"); -// } -// -// -// } -// -// private static void producePlot(DateColumn records, DoubleColumn values, StringColumn groupings, String title, String yAxisTitle, String filename) { -// // Make plot -// Table table = Table.create(title); -// table.addColumns(records); -// table.addColumns(values); -// table.addColumns(groupings); -// -// TableSliceGroup tables = table.splitOn(table.categoricalColumn("scenario")); -// -// Axis xAxis = Axis.builder().title("Datum").build(); -// Axis yAxis = Axis.builder().range(0., 20.) -// // .type(Axis.Type.LOG) -// .title(yAxisTitle).build(); -// -// Layout layout = Layout.builder(title).xAxis(xAxis).yAxis(yAxis).showLegend(true).height(500).width(1000).build(); -// -// ScatterTrace[] traces = new ScatterTrace[tables.size()]; -// for (int i = 0; i < tables.size(); i++) { -// List tableList = tables.asTableList(); -// traces[i] = ScatterTrace.builder(tableList.get(i).dateColumn("date"), tableList.get(i).numberColumn("hospitalizations")) -// .showLegend(true) -// .name(tableList.get(i).name()) -// .mode(ScatterTrace.Mode.LINE) -// .build(); -// } -// var figure = new Figure(layout, traces); -// -// try (Writer writer = new OutputStreamWriter(new FileOutputStream(output.resolve(filename).toString()), StandardCharsets.UTF_8)) { -// writer.write(Page.pageBuilder(figure, "target").build().asJavascript()); -// } catch (IOException e) { -// throw new UncheckedIOException(e); -// } -// } -// -//} + + for (Int2DoubleMap.Entry entry : reportedBedsNrw2.int2DoubleEntrySet()) { + int day = entry.getIntKey(); + records.append(day); + recordsDate.append(startDate.plusDays(day)); + + values.append(entry.getDoubleValue()); + groupings.append("Reported: General Beds (NRW2)"); + + } + + for (Int2DoubleMap.Entry entry : reportedBedsIcuNrw2.int2DoubleEntrySet()) { + int day = entry.getIntKey(); + records.append(day); + recordsDate.append(startDate.plusDays(day)); + + values.append(entry.getDoubleValue()); + groupings.append("Reported: ICU Beds (NRW2)"); + + } + + + + // Make plot + producePlot(recordsDate, values, groupings, "Filled Beds", "Beds Filled / 100k Population", "FilledBeds" + outputAppendix + ".html", output); + } + + + } + + private static void producePlot(DateColumn records, DoubleColumn values, StringColumn groupings, String title, String yAxisTitle, String filename, Path output) { + // Make plot + Table table = Table.create(title); + table.addColumns(records); + table.addColumns(values); + table.addColumns(groupings); + + TableSliceGroup tables = table.splitOn(table.categoricalColumn("scenario")); + + Axis xAxis = Axis.builder().title("Datum").build(); + Axis yAxis = Axis.builder().range(0., 20.) + // .type(Axis.Type.LOG) + .title(yAxisTitle).build(); + + Layout layout = Layout.builder(title).xAxis(xAxis).yAxis(yAxis).showLegend(true).height(500).width(1000).build(); + + ScatterTrace[] traces = new ScatterTrace[tables.size()]; + for (int i = 0; i < tables.size(); i++) { + List
tableList = tables.asTableList(); + traces[i] = ScatterTrace.builder(tableList.get(i).dateColumn("date"), tableList.get(i).numberColumn("hospitalizations")) + .showLegend(true) + .name(tableList.get(i).name()) + .mode(ScatterTrace.Mode.LINE) + .build(); + } + var figure = new Figure(layout, traces); + + try (Writer writer = new OutputStreamWriter(new FileOutputStream(output.resolve(filename).toString()), StandardCharsets.UTF_8)) { + writer.write(Page.pageBuilder(figure, "target").build().asJavascript()); + } catch (IOException e) { + throw new UncheckedIOException(e); + } + } + +} diff --git a/src/main/java/org/matsim/episim/analysis/InicidencePerStrain.java b/src/main/java/org/matsim/episim/analysis/InicidencePerStrain.java index 1e99c72ec..1ac629133 100644 --- a/src/main/java/org/matsim/episim/analysis/InicidencePerStrain.java +++ b/src/main/java/org/matsim/episim/analysis/InicidencePerStrain.java @@ -25,26 +25,15 @@ import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.core.config.Configurator; -import org.matsim.api.core.v01.Id; import org.matsim.api.core.v01.Scenario; -import org.matsim.api.core.v01.events.ActivityStartEvent; -import org.matsim.api.core.v01.events.handler.ActivityStartEventHandler; import org.matsim.api.core.v01.population.Person; import org.matsim.api.core.v01.population.Population; -import org.matsim.core.api.experimental.events.EventsManager; import org.matsim.core.config.Config; import org.matsim.core.config.ConfigUtils; -import org.matsim.core.events.EventsUtils; -import org.matsim.core.events.MatsimEventsReader; import org.matsim.core.scenario.ScenarioUtils; -import org.matsim.core.utils.io.UncheckedIOException; -import org.matsim.episim.EpisimUtils; import org.matsim.episim.EpisimPerson.DiseaseStatus; import org.matsim.episim.events.*; -import org.matsim.episim.model.VaccinationType; -import org.matsim.episim.model.VirusStrain; import org.matsim.run.AnalysisCommand; -import org.matsim.run.modules.AbstractSnzScenario2020; import org.matsim.utils.objectattributes.attributable.Attributes; import picocli.CommandLine; @@ -54,11 +43,8 @@ import java.nio.file.Files; import java.nio.file.Path; import java.time.LocalDate; -import java.time.temporal.ChronoUnit; import java.util.*; -import java.util.Map.Entry; import java.util.concurrent.Callable; -import java.util.stream.Collectors; /** @@ -140,7 +126,7 @@ private void calcValues(Path scenario) throws IOException { Handler handler = new Handler(population, startDate, symptoms, seriouslySick); - AnalysisCommand.forEachEvent(scenario, s -> {}, handler); + AnalysisCommand.forEachEvent(scenario, s -> {}, false, handler); int persons = 0; for (Person p : population.getPersons().values()) { diff --git a/src/main/java/org/matsim/episim/analysis/REAnalyzeTimelineOfEvents.java b/src/main/java/org/matsim/episim/analysis/REAnalyzeTimelineOfEvents.java index baf2bc6d1..3a833cdf1 100644 --- a/src/main/java/org/matsim/episim/analysis/REAnalyzeTimelineOfEvents.java +++ b/src/main/java/org/matsim/episim/analysis/REAnalyzeTimelineOfEvents.java @@ -1,88 +1,88 @@ -package org.matsim.episim.analysis; - -import java.util.HashMap; -import java.util.Map; - -import org.matsim.api.core.v01.events.ActivityEndEvent; -import org.matsim.api.core.v01.events.handler.ActivityEndEventHandler; -import org.matsim.api.core.v01.population.Population; -import org.matsim.core.api.experimental.events.EventsManager; -import org.matsim.core.events.EventsUtils; -import org.matsim.core.events.MatsimEventsReader; -import org.matsim.core.population.PopulationUtils; - -public class REAnalyzeTimelineOfEvents { - - private final static Map> mapOfActivityEnds = new HashMap<>(); - private static int numberEndAct = 0; - private static Population population; - - public static void main(String[] args) { -// String inputFileEvents = "../shared-svn/projects/episim/matsim-files/snz/BerlinV2/episim-input/be_2020-week_snz_episim_events_wt_25pt_split.xml.gz"; - String inputFileEvents = "../shared-svn/projects/episim/matsim-files/snz/BerlinV2/episim-input/be_2020-week_snz_episim_events_sa_25pt_split.xml.gz"; -// String inputFileEvents = "../shared-svn/projects/episim/matsim-files/snz/BerlinV2/episim-input/be_2020-week_snz_episim_events_so_25pt_split.xml.gz"; - String inputFilePop = "../shared-svn/projects/episim/matsim-files/snz/BerlinV2/episim-input/be_2020-week_snz_entirePopulation_emptyPlans_withDistricts_25pt_split.xml.gz"; - - population = PopulationUtils.readPopulation(inputFilePop); - - EventsManager events = EventsUtils.createEventsManager(); - - REActivityTimelinenEventHandler reActivityDurationEventHandler = new REActivityTimelinenEventHandler(); - - events.addHandler(reActivityDurationEventHandler); - - MatsimEventsReader reader = new MatsimEventsReader(events); - - reader.readFile(inputFileEvents); - - for (int i = 0; i < Types.values().length; i++) { - System.out.print(Types.values()[i].toString() + ";"); - } - - for (Integer hours = 0; hours < 30; hours++) { - System.out.println(); - System.out.print(hours + ";"); - for (int i = 0; i < Types.values().length; i++) { - - String actType = Types.values()[i].toString(); - if (mapOfActivityEnds.get(actType).containsKey(hours)) - System.out.print(mapOfActivityEnds.get(actType).get(hours) * 4 + ";"); - else - System.out.print(0 + ";"); - } - System.out.println(); - } - System.out.println("Gesamt: " + numberEndAct * 4); - - } - - private static class REActivityTimelinenEventHandler implements ActivityEndEventHandler { - - @Override - public void handleEvent(ActivityEndEvent event) { - - if (population.getPersons().get(event.getPersonId()).getAttributes().getAttribute("district") != null - && population.getPersons().get(event.getPersonId()).getAttributes().getAttribute("district") - .toString().equals("Berlin")) { - numberEndAct++; - Integer beginningHour = Integer.valueOf((int) Math.floor(event.getTime() / 3600)); - if (mapOfActivityEnds.containsKey(event.getActType())) - if (mapOfActivityEnds.get(event.getActType()).containsKey(beginningHour)) - mapOfActivityEnds.get(event.getActType()).put(beginningHour, - mapOfActivityEnds.get(event.getActType()).get(beginningHour) + 1); - else { - mapOfActivityEnds.get(event.getActType()).put(beginningHour, 1); - } - else { - mapOfActivityEnds.put(event.getActType(), new HashMap<>()); - mapOfActivityEnds.get(event.getActType()).put(beginningHour, 1); - } - } - } - } - - private enum Types { - educ_kiga, business, errands, home, leisure, shop_daily, shop_other, visit, work, educ_higher, educ_tertiary, - educ_other, educ_primary, educ_secondary - } -} +package org.matsim.episim.analysis; + +import java.util.HashMap; +import java.util.Map; + +import org.matsim.api.core.v01.events.ActivityEndEvent; +import org.matsim.api.core.v01.events.handler.ActivityEndEventHandler; +import org.matsim.api.core.v01.population.Population; +import org.matsim.core.api.experimental.events.EventsManager; +import org.matsim.core.events.EventsUtils; +import org.matsim.core.events.MatsimEventsReader; +import org.matsim.core.population.PopulationUtils; + +public class REAnalyzeTimelineOfEvents { + + private final static Map> mapOfActivityEnds = new HashMap<>(); + private static int numberEndAct = 0; + private static Population population; + + public static void main(String[] args) { +// String inputFileEvents = "../shared-svn/projects/episim/matsim-files/snz/BerlinV2/episim-input/be_2020-week_snz_episim_events_wt_25pt_split.xml.gz"; + String inputFileEvents = "../shared-svn/projects/episim/matsim-files/snz/BerlinV2/episim-input/be_2020-week_snz_episim_events_sa_25pt_split.xml.gz"; +// String inputFileEvents = "../shared-svn/projects/episim/matsim-files/snz/BerlinV2/episim-input/be_2020-week_snz_episim_events_so_25pt_split.xml.gz"; + String inputFilePop = "../shared-svn/projects/episim/matsim-files/snz/BerlinV2/episim-input/be_2020-week_snz_entirePopulation_emptyPlans_withDistricts_25pt_split.xml.gz"; + + population = PopulationUtils.readPopulation(inputFilePop); + + EventsManager events = EventsUtils.createEventsManager(); + + REActivityTimelinenEventHandler reActivityDurationEventHandler = new REActivityTimelinenEventHandler(); + + events.addHandler(reActivityDurationEventHandler); + + MatsimEventsReader reader = new MatsimEventsReader(events); + + reader.readFile(inputFileEvents); + + for (int i = 0; i < Types.values().length; i++) { + System.out.print(Types.values()[i].toString() + ";"); + } + + for (Integer hours = 0; hours < 30; hours++) { + System.out.println(); + System.out.print(hours + ";"); + for (int i = 0; i < Types.values().length; i++) { + + String actType = Types.values()[i].toString(); + if (mapOfActivityEnds.get(actType).containsKey(hours)) + System.out.print(mapOfActivityEnds.get(actType).get(hours) * 4 + ";"); + else + System.out.print(0 + ";"); + } + System.out.println(); + } + System.out.println("Gesamt: " + numberEndAct * 4); + + } + + private static class REActivityTimelinenEventHandler implements ActivityEndEventHandler { + + @Override + public void handleEvent(ActivityEndEvent event) { + + if (population.getPersons().get(event.getPersonId()).getAttributes().getAttribute("district") != null + && population.getPersons().get(event.getPersonId()).getAttributes().getAttribute("district") + .toString().equals("Berlin")) { + numberEndAct++; + Integer beginningHour = Integer.valueOf((int) Math.floor(event.getTime() / 3600)); + if (mapOfActivityEnds.containsKey(event.getActType())) + if (mapOfActivityEnds.get(event.getActType()).containsKey(beginningHour)) + mapOfActivityEnds.get(event.getActType()).put(beginningHour, + mapOfActivityEnds.get(event.getActType()).get(beginningHour) + 1); + else { + mapOfActivityEnds.get(event.getActType()).put(beginningHour, 1); + } + else { + mapOfActivityEnds.put(event.getActType(), new HashMap<>()); + mapOfActivityEnds.get(event.getActType()).put(beginningHour, 1); + } + } + } + } + + private enum Types { + educ_kiga, business, errands, home, leisure, leisPrivate, leisPublic, shop_daily, shop_other, visit, work, educ_higher, educ_tertiary, + educ_other, educ_primary, educ_secondary + } +} diff --git a/src/main/java/org/matsim/episim/analysis/RValuesFromEvents.java b/src/main/java/org/matsim/episim/analysis/RValuesFromEvents.java index 6de4fffda..c42f2fb60 100644 --- a/src/main/java/org/matsim/episim/analysis/RValuesFromEvents.java +++ b/src/main/java/org/matsim/episim/analysis/RValuesFromEvents.java @@ -45,7 +45,6 @@ import java.time.LocalDate; import java.util.*; import java.util.Map.Entry; -import java.util.concurrent.Callable; import java.util.stream.Collectors; @@ -66,7 +65,7 @@ public class RValuesFromEvents implements OutputAnalysis { * Activity types used by this analysis. */ private static final List ACTIVITY_TYPES = List.of( - "home", "leisure", "schools", "day care", "university", "work&business", "pt", "other" + "home", "leisure", "leisPrivate", "leisPublic", "schools", "day care", "university", "work&business", "pt", "other" ); @CommandLine.Option(names = "--output", defaultValue = "./output/") @@ -115,7 +114,7 @@ public void analyzeOutput(Path output) throws IOException { RHandler rHandler = new RHandler(); List eventFiles = AnalysisCommand.forEachEvent(output, s -> { - }, infHandler, rHandler); + }, false, infHandler, rHandler); BufferedWriter bw = Files.newBufferedWriter(output.resolve(id + "infectionsPerActivity.txt")); bw.write("day\tdate\tactivity\tinfections\tinfectionsShare\tscenario"); @@ -286,6 +285,8 @@ private static String getActivityType(String infectionType) { else if (infectionType.endsWith("educ_higher")) activityType = "university"; else if (infectionType.endsWith("educ_kiga")) activityType = "day care"; else if (infectionType.endsWith("leisure")) activityType = "leisure"; + else if (infectionType.endsWith("leisPublic")) activityType = "leisPublic"; + else if (infectionType.endsWith("leisPrivate")) activityType = "leisPrivate"; else if (infectionType.endsWith("work") || infectionType.endsWith("business")) activityType = "work&business"; else if (infectionType.endsWith("home")) activityType = "home"; else if (infectionType.startsWith("pt")) activityType = "pt"; diff --git a/src/main/java/org/matsim/episim/analysis/SecondaryAttackRateFromEvents.java b/src/main/java/org/matsim/episim/analysis/SecondaryAttackRateFromEvents.java new file mode 100644 index 000000000..93a5e2eaf --- /dev/null +++ b/src/main/java/org/matsim/episim/analysis/SecondaryAttackRateFromEvents.java @@ -0,0 +1,257 @@ +/* *********************************************************************** * + * project: org.matsim.* + * EditRoutesTest.java + * * + * *********************************************************************** * + * * + * copyright : (C) 2020 by the members listed in the COPYING, * + * LICENSE and WARRANTY file. * + * email : info at matsim dot org * + * * + * *********************************************************************** * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * See also COPYING, LICENSE and WARRANTY file * + * * + * *********************************************************************** */ + +package org.matsim.episim.analysis; + +import com.google.inject.Inject; +import org.apache.logging.log4j.Level; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.apache.logging.log4j.core.config.Configurator; +import org.matsim.api.core.v01.Scenario; +import org.matsim.api.core.v01.population.Person; +import org.matsim.api.core.v01.population.Population; +import org.matsim.core.population.PopulationUtils; +import org.matsim.episim.EpisimPerson.DiseaseStatus; +import org.matsim.episim.events.EpisimPersonStatusEvent; +import org.matsim.episim.events.EpisimPersonStatusEventHandler; +import org.matsim.run.AnalysisCommand; +import picocli.CommandLine; + +import java.io.BufferedWriter; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.time.DayOfWeek; +import java.time.LocalDate; +import java.util.*; +import java.util.Map.Entry; +import java.util.function.Function; +import java.util.stream.Collectors; + + +/** + * @author jakobrehmann + * Calculates secondary attack rate for all runs in given directory + * Output is written to secondaryAttackRate.txt in the working directory + */ + +@CommandLine.Command( + name = "calculateSecondaryAttackRate", + description = "Calculate Secondary Attack Rate" +) +public class SecondaryAttackRateFromEvents implements OutputAnalysis { + + private static final Logger log = LogManager.getLogger(SecondaryAttackRateFromEvents.class); + + @CommandLine.Option(names = "--output", defaultValue = "./output/") +// @CommandLine.Option(names = "--output", defaultValue = "../public-svn/matsim/scenarios/countries/de/episim/battery/jakob/2022-07-22/1-calibration/output") + private Path output; + + @CommandLine.Option(names = "--start-date", defaultValue = "2020-02-24") + private LocalDate startDate; + + @CommandLine.Option(names = "--input", defaultValue = "/scratch/projects/bzz0020/episim-input") +// @CommandLine.Option(names = "--input", defaultValue = "../shared-svn/projects/episim/matsim-files/snz/Cologne/episim-input") + private String input; + + @CommandLine.Option(names = "--population-file", defaultValue = "/cologne_snz_entirePopulation_emptyPlans_withDistricts_25pt_split.xml.gz") + private String populationFile; + + + @Inject + private Scenario scenario; + + private Population population; + + + public static void main(String[] args) { + System.exit(new CommandLine(new SecondaryAttackRateFromEvents()).execute(args)); + } + + @Override + public Integer call() throws Exception { + Configurator.setLevel("org.matsim.core.config", Level.WARN); + Configurator.setLevel("org.matsim.core.controler", Level.WARN); + Configurator.setLevel("org.matsim.core.events", Level.WARN); + Configurator.setLevel("org.matsim.core.utils", Level.WARN); + + if (!Files.exists(output)) { + log.error("Output path {} does not exist.", output); + return 2; + } + + population = PopulationUtils.readPopulation(input + populationFile); + + AnalysisCommand.forEachScenario(output, scenario -> { + try { + analyzeOutput(scenario); + } catch (IOException e) { + log.error("Failed processing {}", scenario, e); + } + }); + + log.info("Done"); + + return 0; + } + + @Override + public void analyzeOutput(Path output) throws IOException { + + String id = AnalysisCommand.getScenarioPrefix(output); + + if (scenario != null) + population = scenario.getPopulation(); + + + Map personToHousehold = new HashMap<>(); + for (Person person : population.getPersons().values()) { + if(person.getAttributes().getAttribute("district").equals("Köln")) + personToHousehold.put(person.getId().toString(), person.getAttributes().getAttribute("homeId").toString()); + } + + Map personCountPerHousehold = personToHousehold.values().stream() + .collect(Collectors.groupingBy(Function.identity(), + Collectors.counting())); + + + SecondaryAttackRateHandler hhHandler = new SecondaryAttackRateHandler(personToHousehold); + + List eventFiles = AnalysisCommand.forEachEvent(output, s -> { + }, false, hhHandler); + + Map> itToHouseholdToInfections = hhHandler.getItToHouseholdToInfections(); + + BufferedWriter bw = Files.newBufferedWriter(output.resolve(id + "secondaryAttackRate.txt")); + bw.write("day\tdate\trate"); + + int rollingAverage = 3; + for (int i = 0 + rollingAverage; i <= eventFiles.size() - rollingAverage; i++) { + + if (startDate.plusDays(i).getDayOfWeek() != DayOfWeek.THURSDAY) { + continue; + } + + long totalHhInfectionsForWeek = 0; + long totalHhMemberCnt = 0; + for (int j = i - rollingAverage; j <= i + rollingAverage; j++) { + + if (itToHouseholdToInfections.containsKey(j)) { + for (Entry hhToInfections : itToHouseholdToInfections.get(j).entrySet()) { + totalHhInfectionsForWeek += hhToInfections.getValue(); + totalHhMemberCnt = totalHhMemberCnt + personCountPerHousehold.get(hhToInfections.getKey()) - 1; + } + } + } + double secondaryAttackRate = (double) totalHhInfectionsForWeek / totalHhMemberCnt; + bw.write("\n" + i + "\t" + startDate.plusDays(i).toString() + "\t" + secondaryAttackRate); + } + bw.close(); + log.info("Calculated results for scenario {}", output); + } + + private static class SecondaryAttackRateHandler implements EpisimPersonStatusEventHandler { + + /** + * personID to hh Id + */ + Map personToHousehold; + + /** + * keys: all agents who are currently the index agent for their household + * values: day at which they became contagious + */ + Map indexPersons = new HashMap<>(); + + /** + * keys: all households that currently contain an index agent. + * values: count of how many agents in index agent's hh are infected before index agent recovers + */ + Map householdToInfections = new HashMap<>(); + + /** + * This map aggregates all information required to calculate secondary attack rate. Households are + * entered here once the index agent recovers, but are backdated to the day they become contagious. + * keys: iterations at which index agents become contagious + * values: map containing household id and how many house mates were infected + */ + Map> itToHouseholdToInfections = new HashMap<>(); + + + public SecondaryAttackRateHandler(Map personToHousehold) { + this.personToHousehold = personToHousehold; + } + + public Map> getItToHouseholdToInfections() { + return itToHouseholdToInfections; + } + + @Override + public void handleEvent(EpisimPersonStatusEvent event) { + + int day = (int) (event.getTime() / 86400); + String personId = event.getPersonId().toString(); + + // if agent does not live in district being evaluated + if (!personToHousehold.containsKey(personId)) { + return; + } + + + String hhId = personToHousehold.get(personId); + + if (event.getDiseaseStatus() == DiseaseStatus.contagious) { + if (indexPersons.containsKey(personId)) { + throw new RuntimeException("person was already contagious, but was never removed from indexPersons when recovered"); + } + + //todo: if they become contagious on the same day, don't count that case? + if (householdToInfections.containsKey(hhId) ) { + householdToInfections.merge(hhId, 1, Integer::sum); + } else { + indexPersons.put(personId, day); + householdToInfections.put(hhId, 0); + } + } + + if (event.getDiseaseStatus() == DiseaseStatus.recovered) { + if (indexPersons.containsKey(personId)) { + + // update global map with household infections for index agent who has just recovered (back-dated to day contagious) + Integer dayContagious = indexPersons.get(personId); + itToHouseholdToInfections.putIfAbsent(dayContagious, new HashMap<>()); + itToHouseholdToInfections.get(dayContagious).put(hhId, householdToInfections.get(hhId)); + + // now that hh infections have been saved to global map, remove from temp maps. + householdToInfections.remove(hhId); + indexPersons.remove(personId); + + } + } + } + + } + +} + + + + diff --git a/src/main/java/org/matsim/episim/analysis/VaccinationEffectiveness.java b/src/main/java/org/matsim/episim/analysis/VaccinationEffectiveness.java index 8c3c6f8b4..f9434418b 100644 --- a/src/main/java/org/matsim/episim/analysis/VaccinationEffectiveness.java +++ b/src/main/java/org/matsim/episim/analysis/VaccinationEffectiveness.java @@ -47,7 +47,6 @@ import java.nio.file.Path; import java.time.LocalDate; import java.util.*; - import java.util.concurrent.Callable; /** @@ -159,7 +158,7 @@ public void analyzeOutput(Path output) throws IOException { Handler handler = new Handler(data, startDate); AnalysisCommand.forEachEvent(output, s -> { - }, handler); + }, false, handler); int days4aggregation = 14; @@ -399,7 +398,7 @@ public void analyzeOutput(Path output) throws IOException { if (vacOmicronPerPeriod.containsKey(i)) omicronInfected = vacOmicronPerPeriod.get(i); - + int omicronBA2Infected = 0; if (vacOmicronBA2PerPeriod.containsKey(i)) @@ -429,7 +428,7 @@ public void analyzeOutput(Path output) throws IOException { if (cgOmicronPerPeriod.containsKey(i)) cgOmicronInfected = cgOmicronPerPeriod.get(i); - + int cgOmicronBA2Infected = 0; if (cgOmicronBA2PerPeriod.containsKey(i)) diff --git a/src/main/java/org/matsim/episim/analysis/VaccinationEffectivenessFromPotentialInfections.java b/src/main/java/org/matsim/episim/analysis/VaccinationEffectivenessFromPotentialInfections.java index d70d27466..f35cb0d02 100644 --- a/src/main/java/org/matsim/episim/analysis/VaccinationEffectivenessFromPotentialInfections.java +++ b/src/main/java/org/matsim/episim/analysis/VaccinationEffectivenessFromPotentialInfections.java @@ -43,7 +43,6 @@ import java.nio.file.Files; import java.nio.file.Path; import java.util.*; -import java.util.concurrent.Callable; @CommandLine.Command( @@ -99,7 +98,7 @@ public void analyzeOutput(Path output) throws IOException { Handler handler = new Handler(); - AnalysisCommand.forEachEvent(output, s -> {}, handler); + AnalysisCommand.forEachEvent(output, s -> {}, false, handler); // Entries with rarely used vaccines are filtered List collect = new ArrayList<>(handler.vac.keySet()); diff --git a/src/main/java/org/matsim/episim/events/EpisimEventsReader.java b/src/main/java/org/matsim/episim/events/EpisimEventsReader.java index 8c6511983..ba09a0948 100644 --- a/src/main/java/org/matsim/episim/events/EpisimEventsReader.java +++ b/src/main/java/org/matsim/episim/events/EpisimEventsReader.java @@ -35,6 +35,7 @@ import org.xml.sax.Attributes; import org.xml.sax.SAXException; +import java.time.LocalDate; import java.util.Map; import java.util.Stack; @@ -54,6 +55,7 @@ public EpisimEventsReader(EventsManager events) { delegate.addCustomEventMapper(EpisimPersonStatusEvent.EVENT_TYPE, getEpisimPersonStatusEventMapper()); delegate.addCustomEventMapper(EpisimContactEvent.EVENT_TYPE, getEpisimContactEventMapper()); delegate.addCustomEventMapper(EpisimVaccinationEvent.EVENT_TYPE, getEpisimVaccinationEventMapper()); + delegate.addCustomEventMapper(EpisimStartEvent.EVENT_TYPE, getEpisimStartEventMapper()); } public void characters(char[] ch, int start, int length) throws SAXException { @@ -92,8 +94,17 @@ private MatsimEventsReader.CustomEventMapper getEpisimInfectionEventMapper() { antibodies = Double.parseDouble(attributes.get(EpisimInfectionEvent.ANTIBODIES)); } + double maxAntibodies = -1; + if (attributes.containsKey(EpisimInfectionEvent.MAX_ANTIBODIES)) { + maxAntibodies = Double.parseDouble(attributes.get(EpisimInfectionEvent.MAX_ANTIBODIES)); + } + + int vaccinationCnt = -1; + attr = attributes.get(EpisimInfectionEvent.NUM_VACCINATIONS); + if (attr != null) + vaccinationCnt = Integer.parseInt(attr); - return new EpisimInfectionEvent(time, person, infector, container, type, groupSize, virusStrain, probability, antibodies); + return new EpisimInfectionEvent(time, person, infector, container, type, groupSize, virusStrain, probability, antibodies, maxAntibodies, vaccinationCnt); }; } @@ -121,6 +132,7 @@ private MatsimEventsReader.CustomEventMapper getEpisimPotentialInfectionEventMap } return new EpisimPotentialInfectionEvent(time, person, infector, container, type, groupSize, virusStrain, probability, unVacProb, antibodies, rnd); + }; } @@ -137,7 +149,17 @@ private MatsimEventsReader.CustomEventMapper getEpisimInitialInfectionEventMappe antibodies = Double.parseDouble(attributes.get(EpisimInfectionEvent.ANTIBODIES)); } - return new EpisimInitialInfectionEvent(time, person,virusStrain, antibodies); + double maxAntibodies = -1; + if (attributes.containsKey(EpisimInfectionEvent.MAX_ANTIBODIES)) { + maxAntibodies = Double.parseDouble(attributes.get(EpisimInfectionEvent.MAX_ANTIBODIES)); + } + + int vaccinationCnt = -1; + String attr = attributes.get(EpisimInfectionEvent.NUM_VACCINATIONS); + if (attr != null) + vaccinationCnt = Integer.parseInt(attr); + + return new EpisimInitialInfectionEvent(time, person,virusStrain, antibodies, maxAntibodies, vaccinationCnt); }; } @@ -182,6 +204,17 @@ private MatsimEventsReader.CustomEventMapper getEpisimVaccinationEventMapper() { }; } + private MatsimEventsReader.CustomEventMapper getEpisimStartEventMapper() { + return event -> { + + Map attr = event.getAttributes(); + return new EpisimStartEvent( + LocalDate.parse(attr.get(EpisimStartEvent.START_DATE)), + attr.get(EpisimStartEvent.IMMUNIZATION) + ); + }; + } + @Override public void startTag(String name, Attributes atts, Stack context) { diff --git a/src/main/java/org/matsim/episim/events/EpisimInfectionEvent.java b/src/main/java/org/matsim/episim/events/EpisimInfectionEvent.java index 0a1e28e90..74007cbbf 100644 --- a/src/main/java/org/matsim/episim/events/EpisimInfectionEvent.java +++ b/src/main/java/org/matsim/episim/events/EpisimInfectionEvent.java @@ -25,6 +25,8 @@ public class EpisimInfectionEvent extends Event implements HasPersonId, Comparab static final String PROBABILITY = "probability"; static final String GROUP_SIZE = "groupSize"; static final String ANTIBODIES = "antibodies"; + static final String MAX_ANTIBODIES = "maxAntibodies"; + static final String NUM_VACCINATIONS = "numVaccinations"; private final Id personId; private final Id infectorId; @@ -34,13 +36,16 @@ public class EpisimInfectionEvent extends Event implements HasPersonId, Comparab private final VirusStrain virusStrain; private final double probability; private final double antibodies; + private final double maxAntibodies; + + private final int numVaccinations; /** * Constructor. */ public EpisimInfectionEvent(double time, Id personId, Id infectorId, Id containerId, String infectionType, - int groupSize, VirusStrain strain, double probability, double antibodies) { + int groupSize, VirusStrain strain, double probability, double antibodies, double maxAntibodies, int numVaccinations) { super(time); this.personId = personId; @@ -51,6 +56,8 @@ public EpisimInfectionEvent(double time, Id personId, Id infecto this.virusStrain = strain; this.probability = probability; this.antibodies = antibodies; + this.maxAntibodies = maxAntibodies; + this.numVaccinations = numVaccinations; } @Override @@ -103,6 +110,20 @@ public double getAntibodies() { return antibodies; } + /** + * Maximum antibodies ever reached by agent with respect to infecting strain + */ + public double getMaxAntibodies() { + return maxAntibodies; + } + + /** + * Number of vaccinations agent has received at time of infection + */ + public int getNumVaccinations(){ + return numVaccinations; + } + @Override public Map getAttributes() { Map attr = super.getAttributes(); @@ -114,6 +135,8 @@ public Map getAttributes() { attr.put(PROBABILITY, Double.toString(probability)); attr.put(VIRUS_STRAIN, virusStrain.toString()); attr.put(ANTIBODIES, Double.toString(antibodies)); + attr.put(MAX_ANTIBODIES, Double.toString(maxAntibodies)); + attr.put(NUM_VACCINATIONS, Integer.toString(numVaccinations)); return attr; } diff --git a/src/main/java/org/matsim/episim/events/EpisimInitialInfectionEvent.java b/src/main/java/org/matsim/episim/events/EpisimInitialInfectionEvent.java index 00fe8f89c..c05a6c2c9 100644 --- a/src/main/java/org/matsim/episim/events/EpisimInitialInfectionEvent.java +++ b/src/main/java/org/matsim/episim/events/EpisimInitialInfectionEvent.java @@ -14,22 +14,34 @@ */ public class EpisimInitialInfectionEvent extends Event implements HasPersonId, Comparable { - static final String EVENT_TYPE = "episimInitialInfection"; + public static final String EVENT_TYPE = "episimInitialInfection"; private final Id personId; private final VirusStrain virusStrain; private final double antibodies; + private final double maxAntibodies; + private final int numVaccinations; /** * Constructor. */ - public EpisimInitialInfectionEvent(double time, Id personId, VirusStrain strain, double antibodies) { + public EpisimInitialInfectionEvent(double time, Id personId, VirusStrain strain, double antibodies, double maxAntibodies, int numVaccinations) { + super(time); this.personId = personId; this.virusStrain = strain; this.antibodies = antibodies; + this.maxAntibodies = maxAntibodies; + this.numVaccinations = numVaccinations; + } + + /** + * Return initial infection as "normal" infection event. + */ + public EpisimInfectionEvent asInfectionEvent() { + return new EpisimInfectionEvent(getTime(), personId, null, null, null, -1, virusStrain, 0, antibodies, maxAntibodies, numVaccinations); } @Override @@ -51,6 +63,8 @@ public Map getAttributes() { Map attr = super.getAttributes(); attr.put(EpisimInfectionEvent.VIRUS_STRAIN, virusStrain.toString()); attr.put(EpisimInfectionEvent.ANTIBODIES, Double.toString(antibodies)); + attr.put(EpisimInfectionEvent.MAX_ANTIBODIES, Double.toString(maxAntibodies)); + attr.put(EpisimInfectionEvent.NUM_VACCINATIONS, Integer.toString(numVaccinations)); return attr; } diff --git a/src/main/java/org/matsim/episim/events/EpisimStartEvent.java b/src/main/java/org/matsim/episim/events/EpisimStartEvent.java new file mode 100644 index 000000000..ab9c3142c --- /dev/null +++ b/src/main/java/org/matsim/episim/events/EpisimStartEvent.java @@ -0,0 +1,49 @@ +package org.matsim.episim.events; + +import org.matsim.api.core.v01.Id; +import org.matsim.api.core.v01.events.Event; +import org.matsim.api.core.v01.population.Person; +import org.matsim.episim.model.VaccinationType; + +import java.time.LocalDate; +import java.util.Map; + +/** + * Notifies begin of episim simulation + */ +public class EpisimStartEvent extends Event { + + public static final String EVENT_TYPE = "episimStart"; + public static final String START_DATE = "startDate"; + public static final String IMMUNIZATION = "immunization"; + + private final LocalDate startDate; + private final String immunization; + + public EpisimStartEvent(LocalDate startDate, String immunization) { + super(0); + this.startDate = startDate; + this.immunization = immunization; + } + + public LocalDate getStartDate() { + return startDate; + } + + @Override + public String getEventType() { + return EVENT_TYPE; + } + + + + @Override + public Map getAttributes() { + Map attr = super.getAttributes(); + + attr.put(START_DATE, startDate.toString()); + attr.put(IMMUNIZATION, String.valueOf(immunization)); + + return attr; + } +} diff --git a/src/main/java/org/matsim/episim/events/EpisimStartEventHandler.java b/src/main/java/org/matsim/episim/events/EpisimStartEventHandler.java new file mode 100644 index 000000000..8f8aff7e5 --- /dev/null +++ b/src/main/java/org/matsim/episim/events/EpisimStartEventHandler.java @@ -0,0 +1,27 @@ +/* *********************************************************************** * + * project: org.matsim.* + * * + * *********************************************************************** * + * * + * copyright : (C) 2007 by the members listed in the COPYING, * + * LICENSE and WARRANTY file. * + * email : info at matsim dot org * + * * + * *********************************************************************** * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * See also COPYING, LICENSE and WARRANTY file * + * * + * *********************************************************************** */ + +package org.matsim.episim.events; + +import org.matsim.core.events.handler.EventHandler; + +public interface EpisimStartEventHandler extends EventHandler { + void handleEvent(EpisimStartEvent event); + +} diff --git a/src/main/java/org/matsim/episim/events/InitialImmunizationHandler.java b/src/main/java/org/matsim/episim/events/InitialImmunizationHandler.java new file mode 100644 index 000000000..056f90ee3 --- /dev/null +++ b/src/main/java/org/matsim/episim/events/InitialImmunizationHandler.java @@ -0,0 +1,100 @@ +package org.matsim.episim.events; + +import org.matsim.api.core.v01.Id; +import org.matsim.api.core.v01.population.Person; +import org.matsim.episim.EpisimConfigGroup; +import org.matsim.episim.EpisimPerson; +import org.matsim.episim.EpisimUtils; +import org.matsim.episim.model.AntibodyModel; +import org.matsim.episim.model.ProgressionModel; +import org.matsim.episim.model.VirusStrain; + +import java.time.temporal.ChronoUnit; +import java.util.Map; +import java.util.function.Function; + +import static org.matsim.episim.EpisimUtils.DAY; + +public final class InitialImmunizationHandler implements Function, + EpisimVaccinationEventHandler, EpisimInfectionEventHandler, + EpisimInitialInfectionEventHandler, EpisimStartEventHandler { + + private final Map, EpisimPerson> personMap; + private final EpisimConfigGroup episimConfig; + private final AntibodyModel antibodyModel; + private final ProgressionModel progressionModel; + + private Double startTimeOffset = null; + + private int iterationOffset; + private boolean continueProcessingEvents = true; + + int maxIterationReachedSoFar = 0; + + public InitialImmunizationHandler(Map, EpisimPerson> personMap, EpisimConfigGroup episimConfig, AntibodyModel antibodyModel, ProgressionModel progressionModel) { + this.personMap = personMap; + this.episimConfig = episimConfig; + this.antibodyModel = antibodyModel; + this.progressionModel = progressionModel; + } + + @Override + public void handleEvent(EpisimStartEvent event) { + this.iterationOffset = (int) ChronoUnit.DAYS.between(event.getStartDate(), episimConfig.getStartDate()); + this.startTimeOffset = this.iterationOffset * DAY; + } + + @Override + public void handleEvent(EpisimInfectionEvent event) { + int currentIteration = (int) (event.getTime() / EpisimUtils.DAY); + if (currentIteration >= iterationOffset + 1) { // starts returning when current iteration = 5 + continueProcessingEvents = false; + return; + } else if (maxIterationReachedSoFar < currentIteration) { + newDay(currentIteration); + } + + //TODO why should this be an initial infection? + personMap.get(event.getPersonId()).setInitialInfection(event.getTime() - startTimeOffset, event.getVirusStrain()); + } + + @Override + public void handleEvent(EpisimInitialInfectionEvent event) { + + handleEvent(event.asInfectionEvent()); + + } + + @Override + public void handleEvent(EpisimVaccinationEvent event) { + int currentIteration = (int) (event.getTime() / EpisimUtils.DAY); + if (currentIteration >= iterationOffset + 1) { + continueProcessingEvents = false; + return; + } else if (maxIterationReachedSoFar < currentIteration) { + newDay(currentIteration); + } + + personMap.get(event.getPersonId()).setVaccinationStatus(EpisimPerson.VaccinationStatus.yes, event.getVaccinationType(), currentIteration - iterationOffset); + } + + public void newDay(int currentIteration) { + while (this.maxIterationReachedSoFar < currentIteration) { + this.maxIterationReachedSoFar++; + for (EpisimPerson person : personMap.values()) { + antibodyModel.updateAntibodies(person, this.maxIterationReachedSoFar - this.iterationOffset); + progressionModel.updateState(person, this.maxIterationReachedSoFar - this.iterationOffset); + } + } + } + + @Override + public Boolean apply(String s) { + return continueProcessingEvents; + } + + public boolean isContinueProcessingEvents() { + return continueProcessingEvents; + } +} + diff --git a/src/main/java/org/matsim/episim/model/AbstractContactModel.java b/src/main/java/org/matsim/episim/model/AbstractContactModel.java index cdf0df3a7..096f3bcaa 100644 --- a/src/main/java/org/matsim/episim/model/AbstractContactModel.java +++ b/src/main/java/org/matsim/episim/model/AbstractContactModel.java @@ -72,6 +72,11 @@ public abstract class AbstractContactModel implements ContactModel { protected DayOfWeek day; private Map restrictions; + /** + * Count number of contacts per day. + */ + protected int numContacts = 0; + /** * Curfew compliance valid for the day. */ @@ -355,6 +360,11 @@ public void setRestrictionsForIteration(int iteration, Map this.infectionModel.setIteration(iteration); this.curfewCompliance = EpisimUtils.findValidEntry(episimConfig.getCurfewCompliance(), 1.0, episimConfig.getStartDate().plusDays(iteration - 1)); + this.numContacts = 0; + } + + public int getNumContacts() { + return numContacts; } /** @@ -388,8 +398,8 @@ protected void infectPerson(EpisimPerson personWrapper, EpisimPerson infector, d personWrapper.possibleInfection( new EpisimInfectionEvent(now, personWrapper.getPersonId(), infector.getPersonId(), - container.getContainerId(), infectionType.toString(), container.getPersons().size(), infector.getVirusStrain(), prob, - personWrapper.getAntibodies(infector.getVirusStrain())) + container.getContainerId(), infectionType.toString(), container.getPersons().size(), infector.getVirusStrain(), prob, + personWrapper.getAntibodies(infector.getVirusStrain()), personWrapper.getMaxAntibodies(infector.getVirusStrain()), personWrapper.getNumVaccinations()) ); // check infection immediately if there is only one thread diff --git a/src/main/java/org/matsim/episim/model/AgeAndProgressionDependentInfectionModelWithSeasonality.java b/src/main/java/org/matsim/episim/model/AgeAndProgressionDependentInfectionModelWithSeasonality.java index 77c495b71..869a8a262 100644 --- a/src/main/java/org/matsim/episim/model/AgeAndProgressionDependentInfectionModelWithSeasonality.java +++ b/src/main/java/org/matsim/episim/model/AgeAndProgressionDependentInfectionModelWithSeasonality.java @@ -8,6 +8,7 @@ import org.matsim.episim.*; import org.matsim.episim.policy.Restriction; +import java.util.EnumMap; import java.util.Map; import java.util.SplittableRandom; @@ -26,8 +27,8 @@ public final class AgeAndProgressionDependentInfectionModelWithSeasonality imple private final VaccinationConfigGroup vaccinationConfig; private final VirusStrainConfigGroup virusStrainConfig; - private final double[] susceptibility = new double[128]; - private final double[] infectivity = new double[susceptibility.length]; + private final Map susceptibility = new EnumMap<>(VirusStrain.class); + private final Map infectivity = new EnumMap<>(VirusStrain.class); private final RealDistribution distribution; /** @@ -50,11 +51,8 @@ public final class AgeAndProgressionDependentInfectionModelWithSeasonality imple this.reporting = reporting; this.rnd = rnd; - // pre-compute interpolated age dependent entries - for (int i = 0; i < susceptibility.length; i++) { - susceptibility[i] = EpisimUtils.interpolateEntry(episimConfig.getAgeSusceptibility(), i); - infectivity[i] = EpisimUtils.interpolateEntry(episimConfig.getAgeInfectivity(), i); - } + AgeDependentInfectionModelWithSeasonality.preComputeAgeDependency(susceptibility, infectivity, virusStrainConfig); + // based on https://arxiv.org/abs/2007.06602 distribution = new NormalDistribution(0.5, 2.6); scale = 1 / distribution.density(distribution.getNumericalMean()); @@ -81,8 +79,8 @@ public double calcInfectionProbability(EpisimPerson target, EpisimPerson infecto //noinspection ConstantConditions // ci corr can not be null, because sim is initialized with non null value double ciCorrection = Math.min(restrictions.get(act1.getContainerName()).getCiCorrection(), restrictions.get(act2.getContainerName()).getCiCorrection()); - double susceptibility = this.susceptibility[target.getAge()]; - double infectivity = this.infectivity[infector.getAge()]; + double susceptibility = this.susceptibility.get(infector.getVirusStrain())[target.getAge()]; + double infectivity = this.infectivity.get(infector.getVirusStrain())[infector.getAge()]; // apply reduced susceptibility of vaccinated persons VirusStrainConfigGroup.StrainParams strain = virusStrainConfig.getParams(infector.getVirusStrain()); @@ -110,8 +108,8 @@ private double calcUnVacInfectionProbability(EpisimPerson target, EpisimPerson i //noinspection ConstantConditions // ci corr can not be null, because sim is initialized with non null value double ciCorrection = Math.min(restrictions.get(act1.getContainerName()).getCiCorrection(), restrictions.get(act2.getContainerName()).getCiCorrection()); - double susceptibility = this.susceptibility[target.getAge()]; - double infectivity = this.infectivity[infector.getAge()]; + double susceptibility = this.susceptibility.get(infector.getVirusStrain())[target.getAge()]; + double infectivity = this.infectivity.get(infector.getVirusStrain())[infector.getAge()]; // apply reduced susceptibility of vaccinated persons VirusStrainConfigGroup.StrainParams strain = virusStrainConfig.getParams(infector.getVirusStrain()); diff --git a/src/main/java/org/matsim/episim/model/AgeDependentInfectionModelWithSeasonality.java b/src/main/java/org/matsim/episim/model/AgeDependentInfectionModelWithSeasonality.java index 99df4019d..081bba8bc 100644 --- a/src/main/java/org/matsim/episim/model/AgeDependentInfectionModelWithSeasonality.java +++ b/src/main/java/org/matsim/episim/model/AgeDependentInfectionModelWithSeasonality.java @@ -6,6 +6,7 @@ import org.matsim.episim.*; import org.matsim.episim.policy.Restriction; +import java.util.EnumMap; import java.util.Map; import java.util.SplittableRandom; @@ -23,8 +24,8 @@ public final class AgeDependentInfectionModelWithSeasonality implements Infectio private final VaccinationConfigGroup vaccinationConfig; private final VirusStrainConfigGroup virusStrainConfig; - private final double[] susceptibility = new double[128]; - private final double[] infectivity = new double[susceptibility.length]; + private final Map susceptibility = new EnumMap<>(VirusStrain.class); + private final Map infectivity = new EnumMap<>(VirusStrain.class); private double outdoorFactor; private int iteration; @@ -38,10 +39,28 @@ public final class AgeDependentInfectionModelWithSeasonality implements Infectio this.reporting = reporting; this.rnd = rnd; - // pre-compute interpolated age dependent entries - for (int i = 0; i < susceptibility.length; i++) { - susceptibility[i] = EpisimUtils.interpolateEntry(episimConfig.getAgeSusceptibility(), i); - infectivity[i] = EpisimUtils.interpolateEntry(episimConfig.getAgeInfectivity(), i); + preComputeAgeDependency(susceptibility, infectivity, virusStrainConfig); + } + + /** + * Pre-compute interpolated age dependent entries + */ + static void preComputeAgeDependency(Map susceptibility, Map infectivity, VirusStrainConfigGroup virusStrainConfig) { + + for (VirusStrain strain : VirusStrain.values()) { + + if (!virusStrainConfig.hasParams(strain)) + continue; + + double[] susp = susceptibility.computeIfAbsent(strain, k -> new double[128]); + double[] inf = infectivity.computeIfAbsent(strain, k -> new double[susp.length]); + + VirusStrainConfigGroup.StrainParams strainConfig = virusStrainConfig.getParams(strain); + + for (int i = 0; i < susp.length; i++) { + susp[i] = EpisimUtils.interpolateEntry(strainConfig.getAgeSusceptibility(), i); + inf[i] = EpisimUtils.interpolateEntry(strainConfig.getAgeInfectivity(), i); + } } } @@ -61,8 +80,8 @@ public double calcInfectionProbability(EpisimPerson target, EpisimPerson infecto //noinspection ConstantConditions // ci corr can not be null, because sim is initialized with non null value double ciCorrection = Math.min(restrictions.get(act1.getContainerName()).getCiCorrection(), restrictions.get(act2.getContainerName()).getCiCorrection()); - double susceptibility = this.susceptibility[target.getAge()]; - double infectivity = this.infectivity[infector.getAge()]; + double susceptibility = this.susceptibility.get(infector.getVirusStrain())[target.getAge()]; + double infectivity = this.infectivity.get(infector.getVirusStrain())[infector.getAge()]; // apply reduced susceptibility of vaccinated persons VirusStrainConfigGroup.StrainParams params = virusStrainConfig.getParams(infector.getVirusStrain()); diff --git a/src/main/java/org/matsim/episim/model/AntibodyModel.java b/src/main/java/org/matsim/episim/model/AntibodyModel.java index d58e9e43c..bbda67d6f 100644 --- a/src/main/java/org/matsim/episim/model/AntibodyModel.java +++ b/src/main/java/org/matsim/episim/model/AntibodyModel.java @@ -41,6 +41,8 @@ static AntibodyModel.Config newConfig(Map persons, int iteration); + void recalculateAntibodiesAfterSnapshot(Collection persons, int iteration); + /** * Class for antibody model configurations. */ @@ -50,7 +52,6 @@ public static class Config { final Map> antibodyRefreshFactors; private double immuneReponseSigma = 0.; - public Config() { //initial antibodies @@ -80,6 +81,10 @@ public Config() { //mRNAAlpha, mRNADelta, mRNABA1 comes from Sydney's calibration. //The other values come from Rössler et al. + double mutEscDelta = 29.2 / 10.9; + double mutEscBa1 = 10.9 / 1.9; + double mutEscBa5 = 2.9; + //Wildtype double mRNAAlpha = 29.2; initialAntibodies.get(VaccinationType.mRNA).put(VirusStrain.SARS_CoV_2, mRNAAlpha); @@ -89,6 +94,9 @@ public Config() { initialAntibodies.get(VirusStrain.DELTA).put(VirusStrain.SARS_CoV_2, mRNAAlpha * 210. / 700.); initialAntibodies.get(VirusStrain.OMICRON_BA1).put(VirusStrain.SARS_CoV_2, 0.01); initialAntibodies.get(VirusStrain.OMICRON_BA2).put(VirusStrain.SARS_CoV_2, 0.01); + initialAntibodies.get(VirusStrain.OMICRON_BA5).put(VirusStrain.SARS_CoV_2, 0.01); + initialAntibodies.get(VaccinationType.ba1Update).put(VirusStrain.SARS_CoV_2, mRNAAlpha / mutEscDelta / mutEscBa1); + initialAntibodies.get(VaccinationType.ba5Update).put(VirusStrain.SARS_CoV_2, mRNAAlpha / mutEscDelta / mutEscBa1 / mutEscBa5); //Alpha initialAntibodies.get(VaccinationType.mRNA).put(VirusStrain.ALPHA, mRNAAlpha); @@ -98,43 +106,65 @@ public Config() { initialAntibodies.get(VirusStrain.DELTA).put(VirusStrain.ALPHA, mRNAAlpha * 210. / 700.); initialAntibodies.get(VirusStrain.OMICRON_BA1).put(VirusStrain.ALPHA, 0.01); initialAntibodies.get(VirusStrain.OMICRON_BA2).put(VirusStrain.ALPHA, 0.01); + initialAntibodies.get(VirusStrain.OMICRON_BA5).put(VirusStrain.ALPHA, 0.01); + initialAntibodies.get(VaccinationType.ba1Update).put(VirusStrain.ALPHA, mRNAAlpha / mutEscDelta / mutEscBa1); + initialAntibodies.get(VaccinationType.ba5Update).put(VirusStrain.ALPHA, mRNAAlpha / mutEscDelta / mutEscBa1 / mutEscBa5); //DELTA - double mRNADelta = 10.9; + double mRNADelta = mRNAAlpha / mutEscDelta; initialAntibodies.get(VaccinationType.mRNA).put(VirusStrain.DELTA, mRNADelta); - initialAntibodies.get(VaccinationType.vector).put(VirusStrain.DELTA, mRNADelta * 150. / 300.); - initialAntibodies.get(VirusStrain.SARS_CoV_2).put(VirusStrain.DELTA, mRNADelta * 64. / 300.); - initialAntibodies.get(VirusStrain.ALPHA).put(VirusStrain.DELTA, mRNADelta * 64. / 300.); - initialAntibodies.get(VirusStrain.DELTA).put(VirusStrain.DELTA, mRNADelta * 450. / 300.); - initialAntibodies.get(VirusStrain.OMICRON_BA1).put(VirusStrain.DELTA, 0.2 / 6.4); - initialAntibodies.get(VirusStrain.OMICRON_BA2).put(VirusStrain.DELTA, 0.2 / 6.4); + initialAntibodies.get(VaccinationType.vector).put(VirusStrain.DELTA, mRNADelta * 150./300.); + initialAntibodies.get(VirusStrain.SARS_CoV_2).put(VirusStrain.DELTA, mRNADelta * 64./300.); + initialAntibodies.get(VirusStrain.ALPHA).put(VirusStrain.DELTA, mRNADelta * 64./300.); + initialAntibodies.get(VirusStrain.DELTA).put(VirusStrain.DELTA, mRNADelta * 450./300.); + initialAntibodies.get(VirusStrain.OMICRON_BA1).put(VirusStrain.DELTA, 0.01); + initialAntibodies.get(VirusStrain.OMICRON_BA2).put(VirusStrain.DELTA, 0.01); + initialAntibodies.get(VirusStrain.OMICRON_BA5).put(VirusStrain.DELTA, 0.01); + initialAntibodies.get(VaccinationType.ba1Update).put(VirusStrain.DELTA, mRNADelta / mutEscBa1); + initialAntibodies.get(VaccinationType.ba5Update).put(VirusStrain.DELTA, mRNADelta / mutEscBa1 / mutEscBa5); //BA.1 - double mRNABA1 = 1.9; + double mRNABA1 = mRNADelta / mutEscBa1; initialAntibodies.get(VaccinationType.mRNA).put(VirusStrain.OMICRON_BA1, mRNABA1); - initialAntibodies.get(VaccinationType.vector).put(VirusStrain.OMICRON_BA1, mRNABA1 * 4. / 20.); //??? - initialAntibodies.get(VirusStrain.SARS_CoV_2).put(VirusStrain.OMICRON_BA1, mRNABA1 * 6. / 20.); - initialAntibodies.get(VirusStrain.ALPHA).put(VirusStrain.OMICRON_BA1, mRNABA1 * 6. / 20.); - initialAntibodies.get(VirusStrain.DELTA).put(VirusStrain.OMICRON_BA1, mRNABA1 * 8. / 20.); + initialAntibodies.get(VaccinationType.vector).put(VirusStrain.OMICRON_BA1, mRNABA1 * 4./20.); //??? + initialAntibodies.get(VirusStrain.SARS_CoV_2).put(VirusStrain.OMICRON_BA1, mRNABA1 * 6./20.); + initialAntibodies.get(VirusStrain.ALPHA).put(VirusStrain.OMICRON_BA1, mRNABA1 * 6./20.); + initialAntibodies.get(VirusStrain.DELTA).put(VirusStrain.OMICRON_BA1, mRNABA1 * 8./20.); initialAntibodies.get(VirusStrain.OMICRON_BA1).put(VirusStrain.OMICRON_BA1, 64.0 / 300.); initialAntibodies.get(VirusStrain.OMICRON_BA2).put(VirusStrain.OMICRON_BA1, 64.0 / 300. / 1.4); - + initialAntibodies.get(VirusStrain.OMICRON_BA5).put(VirusStrain.OMICRON_BA1, 64.0 / 300. / mutEscBa5); //todo: is 1.4 + initialAntibodies.get(VaccinationType.ba1Update).put(VirusStrain.OMICRON_BA1, mRNAAlpha); + initialAntibodies.get(VaccinationType.ba5Update).put(VirusStrain.OMICRON_BA1, mRNAAlpha / mutEscBa5); //BA.2 - double mRNABA2 = mRNABA1 / 1.4; + double mRNABA2 = mRNABA1; initialAntibodies.get(VaccinationType.mRNA).put(VirusStrain.OMICRON_BA2, mRNABA2); - initialAntibodies.get(VaccinationType.vector).put(VirusStrain.OMICRON_BA2, mRNABA2 * 4. / 20.); - initialAntibodies.get(VirusStrain.SARS_CoV_2).put(VirusStrain.OMICRON_BA2, mRNABA2 * 6. / 20.); - initialAntibodies.get(VirusStrain.ALPHA).put(VirusStrain.OMICRON_BA2, mRNABA2 * 6. / 20.); - initialAntibodies.get(VirusStrain.DELTA).put(VirusStrain.OMICRON_BA2, mRNABA2 * 8. / 20.); + initialAntibodies.get(VaccinationType.vector).put(VirusStrain.OMICRON_BA2, mRNABA2 * 4./20.); + initialAntibodies.get(VirusStrain.SARS_CoV_2).put(VirusStrain.OMICRON_BA2, mRNABA2 * 6./20.); + initialAntibodies.get(VirusStrain.ALPHA).put(VirusStrain.OMICRON_BA2, mRNABA2 * 6./20.); + initialAntibodies.get(VirusStrain.DELTA).put(VirusStrain.OMICRON_BA2, mRNABA2 * 8./20.); initialAntibodies.get(VirusStrain.OMICRON_BA1).put(VirusStrain.OMICRON_BA2, 64.0 / 300. / 1.4); initialAntibodies.get(VirusStrain.OMICRON_BA2).put(VirusStrain.OMICRON_BA2, 64.0 / 300.); - - - //refresh factors - Map> antibodyRefreshFactors = new HashMap<>(); - - for (VaccinationType immunityType : VaccinationType.values()) { + initialAntibodies.get(VirusStrain.OMICRON_BA5).put(VirusStrain.OMICRON_BA2, 64.0 / 300. / mutEscBa5); + initialAntibodies.get(VaccinationType.ba1Update).put(VirusStrain.OMICRON_BA2, mRNAAlpha); + initialAntibodies.get(VaccinationType.ba5Update).put(VirusStrain.OMICRON_BA2, mRNAAlpha / mutEscBa5); + + + //BA.5 + double mRNABa5 = mRNABA2 / mutEscBa5; + initialAntibodies.get(VaccinationType.mRNA).put(VirusStrain.OMICRON_BA5, mRNABa5); + initialAntibodies.get(VaccinationType.vector).put(VirusStrain.OMICRON_BA5, mRNABa5 * 4./20.); + initialAntibodies.get(VirusStrain.SARS_CoV_2).put(VirusStrain.OMICRON_BA5, mRNABa5 * 6./20.); + initialAntibodies.get(VirusStrain.ALPHA).put(VirusStrain.OMICRON_BA5, mRNABa5 * 6./20.); + initialAntibodies.get(VirusStrain.DELTA).put(VirusStrain.OMICRON_BA5, mRNABa5 * 8./20.); + initialAntibodies.get(VirusStrain.OMICRON_BA1).put(VirusStrain.OMICRON_BA5, 64.0 / 300. / mutEscBa5);// todo: do we need 1.4? + initialAntibodies.get(VirusStrain.OMICRON_BA2).put(VirusStrain.OMICRON_BA5, 64.0 / 300./ mutEscBa5); + initialAntibodies.get(VirusStrain.OMICRON_BA5).put(VirusStrain.OMICRON_BA5, 64.0 / 300.); + initialAntibodies.get(VaccinationType.ba1Update).put(VirusStrain.OMICRON_BA5, mRNAAlpha / mutEscBa5); + initialAntibodies.get(VaccinationType.ba5Update).put(VirusStrain.OMICRON_BA5, mRNAAlpha); + Map> antibodyRefreshFactors = new HashMap<>(); + + for (VaccinationType immunityType : VaccinationType.values()) { antibodyRefreshFactors.put(immunityType, new EnumMap<>(VirusStrain.class)); for (VirusStrain virusStrain : VirusStrain.values()) { @@ -142,7 +172,14 @@ public Config() { antibodyRefreshFactors.get(immunityType).put(virusStrain, 15.0); } else if (immunityType == VaccinationType.vector) { antibodyRefreshFactors.get(immunityType).put(virusStrain, 5.0); - } else { + } + else if (immunityType == VaccinationType.ba1Update) { + antibodyRefreshFactors.get(immunityType).put(virusStrain, 15.0); + } + else if (immunityType == VaccinationType.ba5Update) { + antibodyRefreshFactors.get(immunityType).put(virusStrain, 15.0); + } + else { antibodyRefreshFactors.get(immunityType).put(virusStrain, Double.NaN); } @@ -173,6 +210,7 @@ public double getImmuneReponseSigma() { public void setImmuneReponseSigma(double immuneReponseSigma) { this.immuneReponseSigma = immuneReponseSigma; } + } } diff --git a/src/main/java/org/matsim/episim/model/ConfigurableProgressionModel.java b/src/main/java/org/matsim/episim/model/ConfigurableProgressionModel.java index 6bb31850f..a0e2e3a15 100644 --- a/src/main/java/org/matsim/episim/model/ConfigurableProgressionModel.java +++ b/src/main/java/org/matsim/episim/model/ConfigurableProgressionModel.java @@ -227,7 +227,7 @@ private boolean releasePerson(EpisimPerson person) { if (release == TracingConfigGroup.QuarantineRelease.SUSCEPTIBLE && status == DiseaseStatus.susceptible) return true; - if (release == TracingConfigGroup.QuarantineRelease.NON_SYMPTOMS && + if (release == TracingConfigGroup.QuarantineRelease.NON_SYMPTOMATIC && (status == DiseaseStatus.susceptible || status == DiseaseStatus.contagious || status == DiseaseStatus.infectedButNotContagious)) return true; diff --git a/src/main/java/org/matsim/episim/model/ContactModel.java b/src/main/java/org/matsim/episim/model/ContactModel.java index 1cee7e9ca..bb56340c2 100644 --- a/src/main/java/org/matsim/episim/model/ContactModel.java +++ b/src/main/java/org/matsim/episim/model/ContactModel.java @@ -35,12 +35,14 @@ public interface ContactModel { * This method is called when a persons leave a vehicle at {@code now}. */ void infectionDynamicsVehicle(EpisimPerson personLeavingVehicle, InfectionEventHandler.EpisimVehicle vehicle, double now); + void notifyEnterVehicle(EpisimPerson personEnteringVehicle, InfectionEventHandler.EpisimVehicle vehicle, double now); /** * This method is called when a persons leaves a facility at {@code now}. */ void infectionDynamicsFacility(EpisimPerson personLeavingFacility, InfectionEventHandler.EpisimFacility facility, double now); + void notifyEnterFacility(EpisimPerson personEnteringFacility, InfectionEventHandler.EpisimFacility facility, double now); /** @@ -48,4 +50,10 @@ public interface ContactModel { */ void setRestrictionsForIteration(int iteration, Map restrictions); + /** + * Number of processed contacts for the day. + */ + int getNumContacts(); + + } diff --git a/src/main/java/org/matsim/episim/model/DefaultAntibodyModel.java b/src/main/java/org/matsim/episim/model/DefaultAntibodyModel.java index 7345c8927..b040e796f 100644 --- a/src/main/java/org/matsim/episim/model/DefaultAntibodyModel.java +++ b/src/main/java/org/matsim/episim/model/DefaultAntibodyModel.java @@ -2,6 +2,7 @@ import com.google.inject.Inject; +import org.matsim.episim.EpisimConfigGroup; import org.matsim.episim.EpisimPerson; import org.matsim.episim.EpisimUtils; @@ -10,15 +11,21 @@ public class DefaultAntibodyModel implements AntibodyModel { + public static final double HALF_LIFE_DAYS = 60; // todo: would 40 work better? + private final AntibodyModel.Config antibodyConfig; private final SplittableRandom localRnd; + private final EpisimConfigGroup episimConfig; + @Inject - DefaultAntibodyModel(AntibodyModel.Config antibodyConfig) { + DefaultAntibodyModel(AntibodyModel.Config antibodyConfig, EpisimConfigGroup episimConfigGroup) { this.antibodyConfig = antibodyConfig; + this.episimConfig = episimConfigGroup; localRnd = new SplittableRandom(2938); // todo: should it be a fixed seed, i.e not change btwn snapshots + } @Override @@ -28,6 +35,7 @@ public void init(Collection persons, int iteration) { // mu = log(median); log(1)=0 + // we assume immune response multiplier follows log-normal distribution, bounded by 0.01 and 10. double immuneResponseMultiplier = 0; while (immuneResponseMultiplier < 0.1 || immuneResponseMultiplier > 10) { @@ -36,19 +44,26 @@ public void init(Collection persons, int iteration) { person.setImmuneResponseMultiplier(immuneResponseMultiplier); + } + + + } + + @Override + public void recalculateAntibodiesAfterSnapshot(Collection persons, int iteration) { + for (EpisimPerson person : persons) { + + // reset to 0.0 for (VirusStrain strain : VirusStrain.values()) { person.setAntibodies(strain, 0.0); } - if (iteration > 1) { - for (int it = 1; it < iteration; it++) { - updateAntibodies(person, it); - } + // recalculate antibodies from immune history + for (int it = 1; it < iteration; it++) { + updateAntibodies(person, it); } } - - } /** @@ -62,13 +77,6 @@ public void init(Collection persons, int iteration) { @Override public void updateAntibodies(EpisimPerson person, int day) { - // todo: is this needed, now that we have an init - if (day == 0) { - for (VirusStrain strain : VirusStrain.values()) { - person.setAntibodies(strain, 0.0); - } - } - //handle vaccination if (person.getVaccinationDates().contains(day - 1)) { int vaccinationIndex = person.getVaccinationDates().indexOf(day - 1); @@ -88,9 +96,8 @@ public void updateAntibodies(EpisimPerson person, int day) { // if no immunity event: exponential decay, day by day: for (VirusStrain strain : VirusStrain.values()) { - double halfLife_days = 60.; double oldAntibodyLevel = person.getAntibodies(strain); - person.setAntibodies(strain, oldAntibodyLevel * Math.pow(0.5, 1 / halfLife_days)); + person.setAntibodies(strain, oldAntibodyLevel * Math.pow(0.5, 1 / HALF_LIFE_DAYS)); } } @@ -107,6 +114,10 @@ private void handleImmunization(EpisimPerson person, ImmunityEvent immunityEvent antibodies = Math.min(150., antibodies * person.getImmuneResponseMultiplier()); person.setAntibodies(strain2, antibodies); + + // if antibodies against a strain2 are higher than previous maximum, replace maximum + // should always be the case for initial immunization + person.updateMaxAntibodies(strain2, antibodies); } @@ -117,10 +128,8 @@ private void handleImmunization(EpisimPerson person, ImmunityEvent immunityEvent // antibodies before refresh double antibodies = person.getAntibodies(strain2); - // refresh antibodies; ensure that antibody level does not decrease. - if (refreshFactor * person.getImmuneResponseMultiplier() >= 1) { - antibodies = antibodies * refreshFactor * person.getImmuneResponseMultiplier(); - } + // refresh antibodies + antibodies = antibodies * refreshFactor; // check that new antibody level at least as high as initial antibodies double initialAntibodies = antibodyConfig.initialAntibodies.get(immunityEventType).get(strain2) * person.getImmuneResponseMultiplier(); @@ -130,8 +139,10 @@ private void handleImmunization(EpisimPerson person, ImmunityEvent immunityEvent antibodies = Math.min(150., antibodies); person.setAntibodies(strain2, antibodies); - } + // if antibodies against a strain2 are higher than previous maximum, replace maximum + person.updateMaxAntibodies(strain2, antibodies); + } } } diff --git a/src/main/java/org/matsim/episim/model/InfectionModelWithAntibodies.java b/src/main/java/org/matsim/episim/model/InfectionModelWithAntibodies.java index 24444e702..d70712dfb 100644 --- a/src/main/java/org/matsim/episim/model/InfectionModelWithAntibodies.java +++ b/src/main/java/org/matsim/episim/model/InfectionModelWithAntibodies.java @@ -8,12 +8,14 @@ import org.matsim.episim.*; import org.matsim.episim.policy.Restriction; +import java.util.ArrayList; +import java.util.EnumMap; import java.util.List; import java.util.Map; import java.util.SplittableRandom; /** - * Extension of the {@link DefaultInfectionModel}, with age, time and seasonality-dependen additions. + * Extension of the {@link DefaultInfectionModel}, with age, time and seasonality-dependent additions. */ public final class InfectionModelWithAntibodies implements InfectionModel { @@ -25,8 +27,8 @@ public final class InfectionModelWithAntibodies implements InfectionModel { private final VaccinationConfigGroup vaccinationConfig; private final VirusStrainConfigGroup virusStrainConfig; - private final double[] susceptibility = new double[128]; - private final double[] infectivity = new double[susceptibility.length]; + private final Map susceptibility = new EnumMap<>(VirusStrain.class); + private final Map infectivity = new EnumMap<>(VirusStrain.class); private final RealDistribution distribution; /** @@ -49,11 +51,8 @@ public final class InfectionModelWithAntibodies implements InfectionModel { this.reporting = reporting; this.rnd = rnd; - // pre-compute interpolated age dependent entries - for (int i = 0; i < susceptibility.length; i++) { - susceptibility[i] = EpisimUtils.interpolateEntry(episimConfig.getAgeSusceptibility(), i); - infectivity[i] = EpisimUtils.interpolateEntry(episimConfig.getAgeInfectivity(), i); - } + AgeDependentInfectionModelWithSeasonality.preComputeAgeDependency(susceptibility, infectivity, virusStrainConfig); + // based on https://arxiv.org/abs/2007.06602 distribution = new NormalDistribution(0.5, 2.6); scale = 1 / distribution.density(distribution.getNumericalMean()); @@ -80,8 +79,8 @@ public double calcInfectionProbability(EpisimPerson target, EpisimPerson infecto //noinspection ConstantConditions // ci corr can not be null, because sim is initialized with non null value double ciCorrection = Math.min(restrictions.get(act1.getContainerName()).getCiCorrection(), restrictions.get(act2.getContainerName()).getCiCorrection()); - double susceptibility = this.susceptibility[target.getAge()]; - double infectivity = this.infectivity[infector.getAge()]; + double susceptibility = this.susceptibility.get(infector.getVirusStrain())[target.getAge()]; + double infectivity = this.infectivity.get(infector.getVirusStrain())[infector.getAge()]; VirusStrainConfigGroup.StrainParams strain = virusStrainConfig.getParams(infector.getVirusStrain()); @@ -97,7 +96,6 @@ public double calcInfectionProbability(EpisimPerson target, EpisimPerson infecto { double igaFactor = 0.0; - double igaTimePeriod = vaccinationConfig.getTimePeriodIgA(); if (target.hadStrain(infector.getVirusStrain())) { @@ -110,14 +108,41 @@ public double calcInfectionProbability(EpisimPerson target, EpisimPerson infecto // igaFactor = Math.exp( - target.daysSinceInfection(lastInfectionWithStrain, iteration) / 120.0); igaFactor = 1.0 / (1.0 + Math.exp(-2.0 * (1.0 - target.daysSinceInfection(lastInfectionWithStrain, iteration) / igaTimePeriod))); - } else if (vaccinationConfig.getUseIgA()) { - List crossImmunityStrains = List.of(VirusStrain.OMICRON_BA1,VirusStrain.OMICRON_BA2,VirusStrain.OMICRON_BA5,VirusStrain.STRAIN_A); + } + + ArrayList strainsLineA = new ArrayList(); + strainsLineA.add(VirusStrain.OMICRON_BA1); + strainsLineA.add(VirusStrain.OMICRON_BA2); + strainsLineA.add(VirusStrain.OMICRON_BA5); + strainsLineA.add(VirusStrain.STRAIN_A); + strainsLineA.add(VirusStrain.STRAIN_B); + + ArrayList strainsLineB = new ArrayList(); + strainsLineB.add(VirusStrain.OMICRON_BA1); + strainsLineB.add(VirusStrain.OMICRON_BA2); + strainsLineB.add(VirusStrain.OMICRON_BA5); + strainsLineB.add(VirusStrain.STRAIN_A); + strainsLineB.add(VirusStrain.STRAIN_B); + + if (vaccinationConfig.getUseIgA()) { + + for (VirusStrain str : VirusStrain.values()) { + if (str.toString().startsWith("A_")) + strainsLineA.add(str); + } + for (VirusStrain str : VirusStrain.values()) { + if (str.toString().startsWith("B_")) + strainsLineB.add(str); + } + } + +// if (vaccinationConfig.getUseIgA()) { - if(crossImmunityStrains.contains(infector.getVirusStrain())){ + if(strainsLineA.contains(infector.getVirusStrain())){ int lastInfectionWithStrain = 0; boolean targetHadStrain = false; for (int ii = 0; ii < target.getNumInfections(); ii++) { - if (crossImmunityStrains.contains(target.getVirusStrain(ii))){ + if (strainsLineA.contains(target.getVirusStrain(ii))){ targetHadStrain = true; lastInfectionWithStrain = ii; } @@ -129,13 +154,49 @@ public double calcInfectionProbability(EpisimPerson target, EpisimPerson infecto igaFactor = Math.max(fac, igaFactor); } } - } - susceptibility = susceptibility * (1.0 - igaFactor); + + if(strainsLineB.contains(infector.getVirusStrain())){ + int lastInfectionWithStrain = 0; + boolean targetHadStrain = false; + for (int ii = 0; ii < target.getNumInfections(); ii++) { + if (strainsLineB.contains(target.getVirusStrain(ii))){ + targetHadStrain = true; + lastInfectionWithStrain = ii; + } + } + + if (targetHadStrain) { + double fac = 1.0 / (1.0 + Math.exp(-2.0 * (1.0 - target.daysSinceInfection(lastInfectionWithStrain, iteration) / igaTimePeriod))); + fac = fac / 1.4; + igaFactor = Math.max(fac, igaFactor); + } + } + +// if(strainsLineA.contains(infector.getVirusStrain()) || strainsLineB.contains(infector.getVirusStrain())){ +// int lastInfectionWithStrain = 0; +// boolean targetHadStrain = false; +// for (int ii = 0; ii < target.getNumInfections(); ii++) { +// if (strainsLineA.contains(target.getVirusStrain(ii)) || strainsLineB.contains(target.getVirusStrain(ii))){ +// targetHadStrain = true; +// lastInfectionWithStrain = ii; +// } +// } +// +// if (targetHadStrain) { +// double fac = 1.0 / (1.0 + Math.exp(-2.0 * (1.0 - target.daysSinceInfection(lastInfectionWithStrain, iteration) / igaTimePeriod))); +// fac = fac / 1.4 / 1.4; +// igaFactor = Math.max(fac, igaFactor); +// } +// } + + susceptibility = susceptibility * (1.0 - igaFactor); +// } } lastUnVac = calcInfectionProbabilityWoImmunity(target, infector, restrictions, act1, act2, contactIntensity, jointTimeInContainer, indoorOutdoorFactor, shedding, intake, infectivity, susceptibility); + // remaining risk --> lower val, lower risk, max risk at 1 double immunityFactor = 1.0 / (1.0 + Math.pow(relativeAntibodyLevelTarget, vaccinationConfig.getBeta())); return 1 - Math.exp(-episimConfig.getCalibrationParameter() * susceptibility * infectivity * contactIntensity * jointTimeInContainer * ciCorrection diff --git a/src/main/java/org/matsim/episim/model/SymmetricContactModel.java b/src/main/java/org/matsim/episim/model/SymmetricContactModel.java index 0b51b8382..a1f456915 100644 --- a/src/main/java/org/matsim/episim/model/SymmetricContactModel.java +++ b/src/main/java/org/matsim/episim/model/SymmetricContactModel.java @@ -131,6 +131,9 @@ private void infectionDynamicsGeneralized(EpisimPerson personLeavingContainer, E continue; } + // counted as contact + numContacts++; + // we have thrown the random numbers, so we can bail out in some cases if we are not tracking: if (!trackingEnabled) { if (personLeavingContainer.getDiseaseStatus() == DiseaseStatus.infectedButNotContagious) { diff --git a/src/main/java/org/matsim/episim/model/VaccinationType.java b/src/main/java/org/matsim/episim/model/VaccinationType.java index c51b94efb..57f9be945 100644 --- a/src/main/java/org/matsim/episim/model/VaccinationType.java +++ b/src/main/java/org/matsim/episim/model/VaccinationType.java @@ -8,7 +8,8 @@ public enum VaccinationType implements ImmunityEvent { generic, mRNA, vector, - omicronUpdate, + ba1Update, + ba5Update, /** * Not a real vaccination, but used to describe the profile for persons that have been infected and gained a natural immunity. diff --git a/src/main/java/org/matsim/episim/model/VirusStrain.java b/src/main/java/org/matsim/episim/model/VirusStrain.java index cb117106c..cc278b5c8 100644 --- a/src/main/java/org/matsim/episim/model/VirusStrain.java +++ b/src/main/java/org/matsim/episim/model/VirusStrain.java @@ -37,9 +37,88 @@ public enum VirusStrain implements ImmunityEvent { OMICRON_BA2, OMICRON_BA5, - + STRAIN_A, - STRAIN_B + STRAIN_B, + + A_1, + + A_2, + + A_3, + + A_4, + + A_5, + + A_6, + + A_7, + + A_8, + + A_9, + + A_10, + + A_11, + + A_12, + + A_13, + + A_14, + + A_15, + + A_16, + + A_17, + + A_18, + + A_19, + + A_20, + + B_1, + B_2, + + B_3, + + B_4, + + B_5, + + B_6, + + B_7, + + B_8, + + B_9, + + B_10, + + B_11, + + B_12, + + B_13, + + B_14, + + B_15, + + B_16, + + B_17, + + B_18, + + B_19, + + B_20 } diff --git a/src/main/java/org/matsim/episim/model/input/CreateRestrictionsFromSnz.java b/src/main/java/org/matsim/episim/model/input/CreateRestrictionsFromSnz.java index 12171e614..27842d819 100644 --- a/src/main/java/org/matsim/episim/model/input/CreateRestrictionsFromSnz.java +++ b/src/main/java/org/matsim/episim/model/input/CreateRestrictionsFromSnz.java @@ -227,7 +227,7 @@ static HashMap> readDurations(File file, HashMa sums.mergeDouble("notAtHome", duration, Double::sum); - if (!actType.equals("education") && !actType.equals("leisure")) { + if (!actType.equals("education") && !actType.startsWith("leis")) { sums.mergeDouble("notAtHomeExceptLeisureAndEdu", duration, Double::sum); } if (!actType.equals("education")) { @@ -267,7 +267,7 @@ static Object2DoubleMap readDurations(File file, IntSet zipCodes) throws sums.mergeDouble("notAtHome", duration, Double::sum); - if (!actType.equals("education") && !actType.equals("leisure")) { + if (!actType.equals("education") && !actType.startsWith("leis")) { sums.mergeDouble("notAtHomeExceptLeisureAndEdu", duration, Double::sum); } if (!actType.equals("education")) { @@ -896,8 +896,8 @@ public HashMap findZipCodesForAnyArea(String anyArea) throws IOE + possibleAreas.toString() + " Choose one and start again."); return zipCodes; } - - + + /** Finds the date after the last day in the given output file. * @param startDateStillUsingBaseDays * @param filesWithData diff --git a/src/main/java/org/matsim/episim/model/listener/WriteAntibodies.java b/src/main/java/org/matsim/episim/model/listener/WriteAntibodies.java new file mode 100644 index 000000000..5c2ae65d5 --- /dev/null +++ b/src/main/java/org/matsim/episim/model/listener/WriteAntibodies.java @@ -0,0 +1,67 @@ +package org.matsim.episim.model.listener; + +import com.google.inject.Inject; +import org.matsim.api.core.v01.Id; +import org.matsim.api.core.v01.population.Person; +import org.matsim.episim.EpisimPerson; +import org.matsim.episim.EpisimReporting; +import org.matsim.episim.InfectionEventHandler; +import org.matsim.episim.model.SimulationListener; +import org.matsim.episim.model.VirusStrain; +import org.matsim.facilities.ActivityFacility; +import org.matsim.vehicles.Vehicle; + +import java.io.BufferedWriter; +import java.time.LocalDate; +import java.util.Arrays; +import java.util.Map; +import java.util.SplittableRandom; +import java.util.stream.Collectors; +import java.util.stream.IntStream; + +/** + * Writes antibody leven into a matrix + */ +public class WriteAntibodies implements SimulationListener { + + private Map, EpisimPerson> persons; + @Inject + private EpisimReporting reporting; + + @Override + public void init(SplittableRandom rnd, Map, EpisimPerson> persons, Map, InfectionEventHandler.EpisimFacility> facilities, Map, InfectionEventHandler.EpisimVehicle> vehicles) { + this.persons = persons; + } + + @Override + public void onIterationEnd(int iteration, LocalDate date) { + + String header = VirusStrain.class.getSimpleName() + "\t" + IntStream.range(0, 120).mapToObj(String::valueOf).collect(Collectors.joining("\t")) + "\n"; + + BufferedWriter writer = reporting.registerWriter(String.format("antibodiesPerAge-%s.tsv", date.toString())); + + reporting.writeAsync(writer, header); + + for (VirusStrain strain : VirusStrain.values()) { + + // Rolling mean per age group + int[] n = new int[120]; + double[] values = new double[120]; + + for (EpisimPerson p : persons.values()) { + + int i = p.getAge(); + n[i]++; + values[i] = values[i] + (p.getAntibodies(strain) - values[i]) / n[i]; + + } + + String row = strain.name() + "\t" + Arrays.stream(values).mapToObj(String::valueOf).collect(Collectors.joining("\t")) + "\n"; + + reporting.writeAsync(writer, row); + } + + reporting.closeAsync(writer); + } + +} diff --git a/src/main/java/org/matsim/episim/model/listener/WriteContactsPerDay.java b/src/main/java/org/matsim/episim/model/listener/WriteContactsPerDay.java new file mode 100644 index 000000000..0f37ea086 --- /dev/null +++ b/src/main/java/org/matsim/episim/model/listener/WriteContactsPerDay.java @@ -0,0 +1,48 @@ +package org.matsim.episim.model.listener; + +import com.google.inject.Inject; +import org.matsim.api.core.v01.Id; +import org.matsim.api.core.v01.population.Person; +import org.matsim.episim.EpisimPerson; +import org.matsim.episim.EpisimReporting; +import org.matsim.episim.InfectionEventHandler; +import org.matsim.episim.model.SimulationListener; +import org.matsim.episim.model.VirusStrain; +import org.matsim.facilities.ActivityFacility; +import org.matsim.vehicles.Vehicle; + +import java.io.BufferedWriter; +import java.time.LocalDate; +import java.util.Arrays; +import java.util.Map; +import java.util.SplittableRandom; +import java.util.stream.Collectors; +import java.util.stream.IntStream; + +/** + * Writes contacts for each day. + */ +public class WriteContactsPerDay implements SimulationListener { + + private Map, EpisimPerson> persons; + @Inject + private EpisimReporting reporting; + private BufferedWriter writer; + + @Override + public void init(SplittableRandom rnd, Map, EpisimPerson> persons, Map, InfectionEventHandler.EpisimFacility> facilities, Map, InfectionEventHandler.EpisimVehicle> vehicles) { + this.persons = persons; + } + + @Override + public void onIterationEnd(int iteration, LocalDate date) { + + if (writer == null) { + writer = reporting.registerWriter("contactsPerDay.tsv"); + reporting.writeAsync(writer, "date\tavgContacts\n"); + } + + reporting.writeAsync(writer, date + "\t" + reporting.getTotalContacts() / (double) persons.size() + "\n"); + + } +} diff --git a/src/main/java/org/matsim/episim/model/progression/AgeDependentDiseaseStatusTransitionModel.java b/src/main/java/org/matsim/episim/model/progression/AgeDependentDiseaseStatusTransitionModel.java index 85b7e564e..6f72b99c0 100644 --- a/src/main/java/org/matsim/episim/model/progression/AgeDependentDiseaseStatusTransitionModel.java +++ b/src/main/java/org/matsim/episim/model/progression/AgeDependentDiseaseStatusTransitionModel.java @@ -94,6 +94,8 @@ public double getProbaOfTransitioningToSeriouslySick(Immunizable person) { proba = 36. / 100; } + return proba * episimConfig.getHospitalFactor(); + // source 2 // if (age < 10) { @@ -153,7 +155,7 @@ public double getProbaOfTransitioningToSeriouslySick(Immunizable person) { // proba = 27.3 / 100; // } - return proba * episimConfig.getHospitalFactor(); + } @Override diff --git a/src/main/java/org/matsim/episim/model/progression/AntibodyDependentTransitionModel.java b/src/main/java/org/matsim/episim/model/progression/AntibodyDependentTransitionModel.java index db92cacf5..63dc0dc36 100644 --- a/src/main/java/org/matsim/episim/model/progression/AntibodyDependentTransitionModel.java +++ b/src/main/java/org/matsim/episim/model/progression/AntibodyDependentTransitionModel.java @@ -30,8 +30,8 @@ public final EpisimPerson.DiseaseStatus decideNextState(EpisimPerson person, Epi switch (status) { case infectedButNotContagious: return EpisimPerson.DiseaseStatus.contagious; - case contagious: + if (rnd.nextDouble() < getProbaOfTransitioningToShowingSymptoms(person) * getShowingSymptomsFactor(person, vaccinationConfig, day)) return EpisimPerson.DiseaseStatus.showingSymptoms; else @@ -114,94 +114,28 @@ public double getSeriouslySickFactor(Immunizable person, VaccinationConfigGroup int numVaccinations = person.getNumVaccinations(); int numInfections = person.getNumInfections() - 1; - // Version from Sander - // -- -// if (numVaccinations + numInfections == 1) { -// return 0.3; -// } -// -// if (numVaccinations + numInfections > 1) { -// return 0.1; -// } -// -// return 1.0; - - // -- - // version with antibodies - if (numVaccinations == 0 && numInfections == 0) - return 1.0; - - VirusStrain strain = person.getVirusStrain(); - - int lastVaccination = 0; - - if (numVaccinations > 0) - lastVaccination = person.getVaccinationDates().getInt(numVaccinations - 1); - - int lastInfection = 0; - - if (numInfections > 0) - lastInfection = (int) (person.getInfectionDates().getDouble(numInfections - 1) / 86400.); - int lastImmunityEvent = Math.max(lastVaccination, lastInfection); - int daysSinceLastImmunityEvent = day - lastImmunityEvent; +// if (numVaccinations == 0 && numInfections == 0) +// return 1.0; + VirusStrain strain = person.getVirusStrain(); - double antibodiesAfterLastImmunityEvent = person.getAntibodyLevelAtInfection() * Math.pow(2., daysSinceLastImmunityEvent / 60.); + double abNoWaning = person.getMaxAntibodies(strain); // Two modifications to antibody level below: // a) we multiply the antibody level by 4 if the agent is boostered if (numVaccinations > 1) { - antibodiesAfterLastImmunityEvent *= 4; + abNoWaning *= 4; } // b) if strain is omicron, an additional factor of 3.7 is applied - if (strain.equals(VirusStrain.OMICRON_BA1) || strain.equals(VirusStrain.OMICRON_BA2) || strain.equals(VirusStrain.OMICRON_BA5) || strain.equals(VirusStrain.STRAIN_A)) { - antibodiesAfterLastImmunityEvent *= 3.7; + if (strain.equals(VirusStrain.OMICRON_BA1) || strain.equals(VirusStrain.OMICRON_BA2) || strain.equals(VirusStrain.OMICRON_BA5) || strain.equals(VirusStrain.STRAIN_A) || strain.equals(VirusStrain.STRAIN_B) + || strain.toString().startsWith("A_") || strain.toString().startsWith("B_")) { + abNoWaning *= 3.7; } - return 1. / (1. + Math.pow( antibodiesAfterLastImmunityEvent, vaccinationConfig.getBeta())); - - - // -- - // even older version -// -// double veSeriouslySick = 0.0; -// -// //vaccinated persons that are boostered either by infection or by 3rd shot -// if (numVaccinations > 1 || (numVaccinations > 0 && numInfections > 1)) { -// if (strain == VirusStrain.OMICRON_BA1 || strain == VirusStrain.OMICRON_BA2) -// veSeriouslySick = 0.9; -// else -// veSeriouslySick = 0.95; -// } -// -// //vaccinated persons or persons who have had a severe course of disease in the past -// // I think this does not work, because old states are removed when changing from recovered to susceptible. SM -// else if (numVaccinations == 1 || person.hadDiseaseStatus(DiseaseStatus.seriouslySick)) { -//// else if (numVaccinations == 1 || person.hadStrain(VirusStrain.SARS_CoV_2) || person.hadStrain(VirusStrain.ALPHA) || person.hadStrain(VirusStrain.DELTA)) -// -// if (strain == VirusStrain.OMICRON_BA1 || strain == VirusStrain.OMICRON_BA2) -// veSeriouslySick = 0.55; -// else -// veSeriouslySick = 0.9; -// } -// -// -// else { -// if (strain == VirusStrain.OMICRON_BA1 || strain == VirusStrain.OMICRON_BA2) -// veSeriouslySick = 0.55; -// else -// veSeriouslySick = 0.6; -// } -// -// double factorInf = person.getImmunityFactor(vaccinationConfig.getBeta()); -// -// double factorSeriouslySick = (1.0 - veSeriouslySick) / factorInf; -// -// factorSeriouslySick = Math.min(1.0, factorSeriouslySick); -// factorSeriouslySick = Math.max(0.0, factorSeriouslySick); -// -// return factorSeriouslySick; + // returns remaining risk of infection (1 is full risk, 0 is no risk), opposite of vaccine effectiveness + return 1. / (1. + Math.pow(abNoWaning,vaccinationConfig.getBeta())); + } @Override @@ -215,71 +149,20 @@ public double getCriticalFactor(Immunizable person, VaccinationConfigGroup vacci VirusStrain strain = person.getVirusStrain(); - int lastVaccination = 0; - - if (numVaccinations > 0) - lastVaccination = person.getVaccinationDates().getInt(numVaccinations - 1); - - int lastInfection = 0; - - if (numInfections > 0) - lastInfection = (int) (person.getInfectionDates().getDouble(numInfections - 1) / 86400.); - - int lastImmunityEvent = Math.max(lastVaccination, lastInfection); - int daysSinceLastImmunityEvent = day - lastImmunityEvent; - - double antibodiesAfterLastImmunityEvent = person.getAntibodyLevelAtInfection() * Math.pow(2., daysSinceLastImmunityEvent / 60.); + double abNoWaning = person.getMaxAntibodies(strain); // Two modifications to antibody level below: // a) we multiply the antibody level by 4 if the agent is boostered if (numVaccinations > 1) { - antibodiesAfterLastImmunityEvent *= 4; + abNoWaning *= 4; } // b) if strain is omicron, an additional factor of 3.7 is applied if (strain.equals(VirusStrain.OMICRON_BA1) || strain.equals(VirusStrain.OMICRON_BA2)) { - antibodiesAfterLastImmunityEvent *= 3.7; + abNoWaning *= 3.7; } - return 1. / (1. + Math.pow(antibodiesAfterLastImmunityEvent, vaccinationConfig.getBeta())); - -// -// double veSeriouslySick = 0.0; -// -// //vaccinated persons that are boostered either by infection or by 3rd shot -// if (numVaccinations > 1 || (numVaccinations > 0 && numInfections > 1)) { -// if (strain == VirusStrain.OMICRON_BA1 || strain == VirusStrain.OMICRON_BA2) -// veSeriouslySick = 0.9; -// else -// veSeriouslySick = 0.95; -// } -// -// //vaccinated persons or persons who have had a severe course of disease in the past -// // I think this does not work, because old states are removed when changing from recovered to susceptible. SM -// else if (numVaccinations == 1 || person.hadDiseaseStatus(DiseaseStatus.seriouslySick)) { -//// else if (numVaccinations == 1 || person.hadStrain(VirusStrain.SARS_CoV_2) || person.hadStrain(VirusStrain.ALPHA) || person.hadStrain(VirusStrain.DELTA)) -// -// if (strain == VirusStrain.OMICRON_BA1 || strain == VirusStrain.OMICRON_BA2) -// veSeriouslySick = 0.55; -// else -// veSeriouslySick = 0.9; -// } -// -// -// else { -// if (strain == VirusStrain.OMICRON_BA1 || strain == VirusStrain.OMICRON_BA2) -// veSeriouslySick = 0.55; -// else -// veSeriouslySick = 0.6; -// } -// -// double factorInf = person.getImmunityFactor(vaccinationConfig.getBeta()); -// -// double factorSeriouslySick = (1.0 - veSeriouslySick) / factorInf; -// -// factorSeriouslySick = Math.min(1.0, factorSeriouslySick); -// factorSeriouslySick = Math.max(0.0, factorSeriouslySick); -// -// return factorSeriouslySick; + return 1. / (1. + Math.pow(abNoWaning, vaccinationConfig.getBeta())); + } @@ -287,11 +170,5 @@ protected double getProbaOfTransitioningToDeceased(EpisimPerson person) { return 0.0; } -// @Override -// public double getCriticalFactor(Immunizable person, VaccinationConfigGroup vaccinationConfig, int day) { -// return 1.0; -// } -// - } diff --git a/src/main/java/org/matsim/episim/model/testing/DefaultTestingModel.java b/src/main/java/org/matsim/episim/model/testing/DefaultTestingModel.java index 9ba82116a..2036656dc 100644 --- a/src/main/java/org/matsim/episim/model/testing/DefaultTestingModel.java +++ b/src/main/java/org/matsim/episim/model/testing/DefaultTestingModel.java @@ -203,9 +203,13 @@ protected boolean testAndQuarantine(EpisimPerson person, int day, TestingConfigG double rate = params.getFalseNegativeRate(); // TODO: configurable - if (params.getType().equals (TestType.RAPID_TEST) && (person.getVirusStrain() == VirusStrain.OMICRON_BA1 || + if (params.getType().equals(TestType.RAPID_TEST) && (person.getVirusStrain() == VirusStrain.OMICRON_BA1 || person.getVirusStrain() == VirusStrain.OMICRON_BA2 || - person.getVirusStrain() == VirusStrain.OMICRON_BA5) ){ + person.getVirusStrain() == VirusStrain.OMICRON_BA5 || + person.getVirusStrain() == VirusStrain.STRAIN_A || + person.getVirusStrain() == VirusStrain.STRAIN_B || + person.getVirusStrain().toString().startsWith("A_") || + person.getVirusStrain().toString().startsWith("B_"))) { rate = 0.5; } diff --git a/src/main/java/org/matsim/episim/model/vaccination/VaccinationFromData.java b/src/main/java/org/matsim/episim/model/vaccination/VaccinationFromData.java index eca9f62c5..2e74533a4 100644 --- a/src/main/java/org/matsim/episim/model/vaccination/VaccinationFromData.java +++ b/src/main/java/org/matsim/episim/model/vaccination/VaccinationFromData.java @@ -224,6 +224,7 @@ private int vaccinate(Map, EpisimPerson> persons, TreeMap candidates = perAge[ii]; diff --git a/src/main/java/org/matsim/episim/model/vaccination/VaccinationStrategyReoccurringCampaigns.java b/src/main/java/org/matsim/episim/model/vaccination/VaccinationStrategyReoccurringCampaigns.java new file mode 100644 index 000000000..c2604d694 --- /dev/null +++ b/src/main/java/org/matsim/episim/model/vaccination/VaccinationStrategyReoccurringCampaigns.java @@ -0,0 +1,166 @@ +package org.matsim.episim.model.vaccination; + +import com.google.inject.Inject; +import org.matsim.api.core.v01.Id; +import org.matsim.api.core.v01.IdSet; +import org.matsim.api.core.v01.Scenario; +import org.matsim.api.core.v01.population.Person; +import org.matsim.api.core.v01.population.Population; +import org.matsim.episim.EpisimPerson; +import org.matsim.episim.EpisimUtils; +import org.matsim.episim.model.VaccinationType; + +import java.time.LocalDate; +import java.util.*; +import java.util.stream.Collectors; + +/** + * Update vaccination campaign. + */ +public class VaccinationStrategyReoccurringCampaigns implements VaccinationModel { + + private final int dailyVaccinationsToBeDistributed; + + private final SplittableRandom rnd; + private final Config config; + + private final IdSet boostBa5Yes = new IdSet<>(Person.class); + + private final IdSet boostBa5Emergency = new IdSet<>(Person.class); + + + @Inject + public VaccinationStrategyReoccurringCampaigns(SplittableRandom rnd, Config config, Scenario scenario) { + this.rnd = rnd; + this.config = config; + + Population population = scenario.getPopulation(); + + for (Person person : population.getPersons().values()) { + + double randomNum = rnd.nextDouble(); + if (randomNum < 0.5) { + boostBa5Yes.add(person.getId()); + } + if (randomNum < 0.75) { + boostBa5Emergency.add(person.getId()); + } + } + + // calculate total number of vaccinations: + dailyVaccinationsToBeDistributed = 1_500 / 4; + + } + + @Override + public void handleVaccination(Map, EpisimPerson> persons, LocalDate date, int iteration, double now) { + + // we check that the compliance of at least one age group is greater than 0.0. If not, there will be no vaccinations anyway + if (dailyVaccinationsToBeDistributed <= 0) { + return; + } + + // Loop through all vaccination campaigns (via the start date of said campaign) + for (LocalDate vaccinationCampaignStartDate : config.startDateToVaccinationCampaign.keySet()) { + // Check whether current date falls within vaccination campaign + if (date.isAfter(vaccinationCampaignStartDate.minusDays(1)) && date.isBefore(vaccinationCampaignStartDate.plusDays(config.campaignDuration))) { + + // vaccine type associated with campaign + VaccinationType vaccinationType = config.startDateToVaccinationCampaign.get(vaccinationCampaignStartDate); + + // define eligible candidates for booster campaigns: + // ?) if the person is vaccinable, whatever that means + // a) not infected at the moment + // b) is either already vaccinated or boostered, depending on the configuration + // c) hasn't been vaccinated in the previous 90 days + final int minDaysAfterInfection; + if (date.isBefore(config.dateToTurnDownMinDaysAfterInfectionTo90)) { + minDaysAfterInfection = config.minDaysAfterInfection; + } else { + minDaysAfterInfection = 90; + } + + List candidates = persons.values().stream() + .filter(EpisimPerson::isVaccinable) + .filter(p -> p.getDiseaseStatus() == EpisimPerson.DiseaseStatus.susceptible) + .filter(p -> p.getNumVaccinations() >= config.vaccinationPool.vaxCnt) + .filter(p -> p.getNumVaccinations() == 0 || p.daysSinceVaccination(p.getNumVaccinations() - 1, iteration) > config.minDaysAfterVaccination) // only people who've had their last vaccination more than 90 days ago + .filter(p -> p.getNumInfections() == 0 || p.daysSinceInfection(p.getNumInfections() - 1, iteration) > minDaysAfterInfection) // only people who've had their last vaccination more than 90 days ago + .filter(p -> date.isAfter(config.emergencyDate.minusDays(1)) ? boostBa5Emergency.contains(p.getPersonId()) : boostBa5Yes.contains(p.getPersonId())) + .filter(p -> !p.hadVaccinationType(vaccinationType)) // todo remove in future + .collect(Collectors.toList()); + + + + // create vaccinations-remaining counter for current day + int vaccinationsLeft = this.dailyVaccinationsToBeDistributed; + if (date.isAfter(config.emergencyDate.minusDays(1))) { + vaccinationsLeft *= 10; + } + + // list is shuffled to avoid eventual bias + if (candidates.size() != 0) + Collections.shuffle(candidates, new Random(EpisimUtils.getSeed(rnd))); + + int n = Math.min(candidates.size(), vaccinationsLeft); + for (int i = 0; i < n; i++) { + EpisimPerson person = candidates.get(i); + vaccinate(person, iteration, vaccinationType); + vaccinationsLeft--; + } + } + } + } + + + public static class Config { + + /** + * Start Dates of vaccination campaigns. + */ + private final Map startDateToVaccinationCampaign; + /** + * Duration of vaccination campaign. + */ + private final int campaignDuration; + + private final VaccinationPool vaccinationPool; + + private final int minDaysAfterVaccination; + + private final int minDaysAfterInfection; + + private final LocalDate emergencyDate; + private final LocalDate dateToTurnDownMinDaysAfterInfectionTo90; + + public enum VaccinationPool { + + unvaccinated(0), + + vaccinated(1), + + boostered(2); + + private final int vaxCnt; + + VaccinationPool(int vaxCnt) { + this.vaxCnt = vaxCnt; + } + + } + + + public Config(Map startDateToVaccinationCampaign, int campaignDuration, VaccinationPool vaccinationPool, int minDaysAfterInfection, int minDaysAfterVaccination, LocalDate emergencyDate, LocalDate dateToTurnDownMinDaysAfterInfectionTo90) { + + this.startDateToVaccinationCampaign = startDateToVaccinationCampaign; + this.campaignDuration = campaignDuration; + this.vaccinationPool = vaccinationPool; + this.minDaysAfterInfection = minDaysAfterInfection; + this.minDaysAfterVaccination = minDaysAfterVaccination; + this.emergencyDate = emergencyDate; + this.dateToTurnDownMinDaysAfterInfectionTo90 = dateToTurnDownMinDaysAfterInfectionTo90; + + } + } + +} diff --git a/src/main/java/org/matsim/episim/policy/FixedPolicy.java b/src/main/java/org/matsim/episim/policy/FixedPolicy.java index d2a763906..5d5bee740 100644 --- a/src/main/java/org/matsim/episim/policy/FixedPolicy.java +++ b/src/main/java/org/matsim/episim/policy/FixedPolicy.java @@ -33,12 +33,10 @@ import javax.inject.Named; import java.time.LocalDate; import java.time.temporal.ChronoUnit; -import java.util.HashMap; -import java.util.Iterator; -import java.util.Map; -import java.util.Objects; +import java.util.*; import java.util.function.BiConsumer; import java.util.function.BiFunction; +import java.util.stream.Collectors; /** * Set the restrictions based on fixed rules with day and {@link Restriction#getRemainingFraction()}. @@ -96,12 +94,16 @@ static void initRestrictions(LocalDate start, ImmutableMap Config actConfig = config.getConfig(entry.getKey()); - for (Map.Entry days : actConfig.root().entrySet()) { + List> entries = actConfig.root().entrySet().stream().filter(e -> !e.getKey().startsWith("day")) + .sorted(Comparator.comparing(e -> LocalDate.parse(e.getKey()))) + .collect(Collectors.toList()); + + for (Map.Entry days : entries) { if (days.getKey().startsWith("day")) continue; LocalDate date = LocalDate.parse(days.getKey()); - if (date.isBefore(start)) { + if (date.isBefore(start.plusDays(1))) { Restriction r = Restriction.fromConfig(actConfig.getConfig(days.getKey())); entry.getValue().update(r); diff --git a/src/main/java/org/matsim/run/AnalysisCommand.java b/src/main/java/org/matsim/run/AnalysisCommand.java index ff7610d0d..df4261ccc 100644 --- a/src/main/java/org/matsim/run/AnalysisCommand.java +++ b/src/main/java/org/matsim/run/AnalysisCommand.java @@ -42,6 +42,7 @@ import java.nio.file.Path; import java.util.*; import java.util.function.Consumer; +import java.util.function.Function; import java.util.stream.Collectors; import java.util.zip.GZIPInputStream; @@ -57,7 +58,7 @@ CommandLine.HelpCommand.class, AutoComplete.GenerateCompletion.class, RValuesFromEvents.class, ExtractInfectionsByAge.class, CreateContactGraph.class, ExtractInfectionGraph.class, VaccinationEffectivenessFromPotentialInfections.class, - VaccinationEffectiveness.class, FilterEvents.class, HospitalNumbersFromEvents.class + VaccinationEffectiveness.class, FilterEvents.class, HospitalNumbersFromEvents.class, SecondaryAttackRateFromEvents.class }, subcommandsRepeatable = true ) @@ -106,17 +107,28 @@ public static void forEachScenario(Path output, Consumer function) throws }); } + /** + * See {@link #forEachEvent(Path, Function, boolean, EventHandler...)}. Callback will always return true. + */ + public static List forEachEvent(Path scenario, Consumer callback, boolean preferReducedEvents, EventHandler... handler) { + return forEachEvent(scenario, s-> { + callback.accept(s); + return true; + }, preferReducedEvents, handler); + } + /** * Reads in all event file from a scenario. * - * @param scenario path of the scenario, which contains the event folder - * @param callback will be executed before reading an event file and pass the path - * @param handler handler for the events + * @param scenario path of the scenario, which contains the event folder + * @param callback will be executed before reading an event file and pass the path. If false is returned, no more events will be read. + * @param preferReducedEvents + * @param handler handler for the events * @return list of read event files */ - public static List forEachEvent(Path scenario, Consumer callback, EventHandler... handler) { + public static List forEachEvent(Path scenario, Function callback, boolean preferReducedEvents, EventHandler... handler) { - Path events = getEvents(scenario); + Path events = getEvents(scenario, preferReducedEvents); if (events == null) { log.warn("No events found at {}", scenario); return List.of(); @@ -145,7 +157,9 @@ public static List forEachEvent(Path scenario, Consumer callback for (Path p : eventFiles) { try { String name = p.getFileName().toString(); - callback.accept(name); + if (!callback.apply(name)) { + break; + } new EpisimEventsReader(manager).readFile(p.toString()); read.add(name); @@ -159,7 +173,10 @@ public static List forEachEvent(Path scenario, Consumer callback ArchiveEntry entry; while ((entry = ar.getNextEntry()) != null) { - callback.accept(entry.getName()); + + if (!callback.apply(entry.getName())) { + break; + } new EpisimEventsReader(manager).parse(new NonClosingGZIPStream(ar)); @@ -208,19 +225,42 @@ public static String getScenarioPrefix(Path scenario) throws IOException { * Check if events are present for the scenario. This method fallbacks to reduced events, if original are not present. */ @Nullable - public static Path getEvents(Path scenario) { + public static Path getEvents(Path scenario, boolean preferReducedEvents) { + + // if a path to an events file is entered, return that directly + if (Files.isRegularFile(scenario)) { + if (scenario.getFileName().toString().endsWith("events_reduced.tar") || scenario.getFileName().toString().endsWith("events.tar")) { + return scenario; + } else { + throw new RuntimeException("A file was specified; however, it doesn't follow the naming conventions for events files"); + } + } + + // if a path to a directory called "events is passed", return that directory if (Files.isDirectory(scenario.resolve("events")) && !isEmpty(scenario.resolve("events"))) { return scenario.resolve("events"); } + // otherwise, search directory for *events_reduced.tar or *events.tar try { - Optional o = Files.list(scenario).filter(p -> p.getFileName().toString().endsWith("events.tar")).findFirst(); + Optional o; + if (preferReducedEvents) { + o = Files.list(scenario).filter(p -> p.getFileName().toString().endsWith("events_reduced.tar")).findFirst(); - if (o.isEmpty()) - o = Files.list(scenario).filter(p -> p.getFileName().toString().endsWith("events_reduced.tar")).findFirst(); + if (o.isEmpty()) { + o = Files.list(scenario).filter(p -> p.getFileName().toString().endsWith("events.tar")).findFirst(); + } + } else { + o = Files.list(scenario).filter(p -> p.getFileName().toString().endsWith("events.tar")).findFirst(); + + if (o.isEmpty()) { + o = Files.list(scenario).filter(p -> p.getFileName().toString().endsWith("events_reduced.tar")).findFirst(); + } + } return o.orElse(null); + } catch (IOException e) { log.error("Error finding event files for {}", scenario); return null; diff --git a/src/main/java/org/matsim/run/CreateBatteryForCluster.java b/src/main/java/org/matsim/run/CreateBatteryForCluster.java index 2f8a78654..0f59fc630 100644 --- a/src/main/java/org/matsim/run/CreateBatteryForCluster.java +++ b/src/main/java/org/matsim/run/CreateBatteryForCluster.java @@ -84,10 +84,10 @@ public class CreateBatteryForCluster implements Callable { @CommandLine.Option(names = "--jvm-opts", description = "Additional options for JVM", defaultValue = "-Xms82G -Xmx82G -XX:+UseParallelGC") private String jvmOpts; - @CommandLine.Option(names = "--setup", defaultValue = "org.matsim.run.batch.CologneJR") + @CommandLine.Option(names = "--setup", defaultValue = "org.matsim.run.batch.CologneBMBF202212XX_bq1") private Class> setup; - @CommandLine.Option(names = "--params", defaultValue = "org.matsim.run.batch.CologneJR$Params") + @CommandLine.Option(names = "--params", defaultValue = "org.matsim.run.batch.CologneBMBF202212XX_bq1$Params") private Class params; @SuppressWarnings("rawtypes") @@ -262,6 +262,7 @@ static void writeMetadata(Path dir, PreparedRun run) throws IOException { metadata.put("info", "_info.txt"); metadata.put("zipFolder", "summaries"); metadata.put("timestamp", LocalDate.now()); + metadata.put("viewerVersion", 2); metadata.putAll(run.getMetadata()); mapper.writeValue(yamlWriter, metadata); diff --git a/src/main/java/org/matsim/run/batch/Berlin220215.java b/src/main/java/org/matsim/run/batch/Berlin220215.java index 3506f74e6..4edb83da6 100644 --- a/src/main/java/org/matsim/run/batch/Berlin220215.java +++ b/src/main/java/org/matsim/run/batch/Berlin220215.java @@ -597,7 +597,7 @@ private void adaptVacinationEffectiveness(VaccinationConfigGroup vaccinationConf ) ; - vaccinationConfig.getOrAddParams(VaccinationType.omicronUpdate) + vaccinationConfig.getOrAddParams(VaccinationType.ba1Update) .setDaysBeforeFullEffect(fullEffectMRNA) .setFactorShowingSymptoms(VaccinationConfigGroup.forStrain(VirusStrain.SARS_CoV_2) .atFullEffect(factorSymptomsMRNA) @@ -697,7 +697,7 @@ private void configureBooster(VaccinationConfigGroup vaccinationConfig, double b .setBoostWaitPeriod(boostAfter * 30 + 6 * 7); ; - vaccinationConfig.getOrAddParams(VaccinationType.omicronUpdate) + vaccinationConfig.getOrAddParams(VaccinationType.ba1Update) .setBoostWaitPeriod(boostAfter * 30 + 6 * 7); ; diff --git a/src/main/java/org/matsim/run/batch/CologneBMBF20220805.java b/src/main/java/org/matsim/run/batch/CologneBMBF20220805.java new file mode 100644 index 000000000..9b5267e15 --- /dev/null +++ b/src/main/java/org/matsim/run/batch/CologneBMBF20220805.java @@ -0,0 +1,729 @@ +package org.matsim.run.batch; + +import com.google.inject.AbstractModule; +import com.google.inject.Module; +import com.google.inject.Singleton; +import com.google.inject.multibindings.Multibinder; +import com.google.inject.util.Modules; +import it.unimi.dsi.fastutil.ints.Int2DoubleAVLTreeMap; +import it.unimi.dsi.fastutil.ints.Int2DoubleMap; +import org.matsim.core.config.Config; +import org.matsim.core.config.ConfigUtils; +import org.matsim.episim.*; +import org.matsim.episim.analysis.*; +import org.matsim.episim.model.*; +import org.matsim.episim.model.testing.TestType; +import org.matsim.episim.model.vaccination.VaccinationModel; +import org.matsim.episim.model.vaccination.VaccinationStrategyBMBF0617; +import org.matsim.episim.policy.FixedPolicy; +import org.matsim.episim.policy.Restriction; +import org.matsim.run.RunParallel; +import org.matsim.run.modules.SnzCologneProductionScenario; + +import javax.annotation.Nullable; +import java.time.LocalDate; +import java.util.*; + + +/** + * Batch for Bmbf runs + */ +public class CologneBMBF20220805 implements BatchRun { + + boolean DEBUG_MODE = false; + int runCount = 0; + + @Nullable + @Override + public Module getBindings(int id, @Nullable Params params) { + return Modules.override(getBindings(0.0, params)).with(new AbstractModule() { + @Override + protected void configure() { + + Multibinder set = Multibinder.newSetBinder(binder(), VaccinationModel.class); + + set.addBinding().to(VaccinationStrategyBMBF0617.class).in(Singleton.class); + + double mutEscDelta = 29.2 / 10.9; + double mutEscBa1 = 10.9 / 1.9; + double mutEscBa5 = 2.9; + + double mutEscStrainA = 0.; + double mutEscStrainB = 0.; + + LocalDate start = null; + VaccinationType vaccinationType = VaccinationType.mRNA; + + Int2DoubleMap compliance = new Int2DoubleAVLTreeMap(); + compliance.put(60, 0.0); + compliance.put(18, 0.0); + compliance.put(12, 0.0); + compliance.put(0, 0.0); + + String vacCamp = "off"; + + if (params != null) { +// mutEscBa5 = params.ba5Esc; + + if (!params.vacType.equals("off")) { + vacCamp = "age"; + vaccinationType = VaccinationType.valueOf(params.vacType); + } + if (!params.StrainA.equals("off")) { + mutEscStrainA = Double.parseDouble(params.StrainA); + } + if (!params.StrainB.equals("off")) { + mutEscStrainB = Double.parseDouble(params.StrainB); + } + + start = LocalDate.parse(params.resDate); + + + + if (vacCamp.equals("age")) { + compliance.put(60, 0.85); // 60+ + compliance.put(18, 0.55); // 18-59 + compliance.put(12, 0.20); // 12-17 + compliance.put(0, 0.0); // 0 - 11 + } + else if (vacCamp.equals("eu")) { + compliance.put(60, 0.40); // half of 80% (which reflects the current percentage of people in Dland who are boostered) + compliance.put(18, 0.); + compliance.put(12, 0.); + compliance.put(0, 0.); + } + else if (vacCamp.equals("off")) { + + } else { + throw new RuntimeException("Not a valid option for vaccinationCampaignType"); + } + } +// + bind(VaccinationStrategyBMBF0617.Config.class).toInstance(new VaccinationStrategyBMBF0617.Config(start, 30, vaccinationType, compliance)); + + //initial antibodies + Map> initialAntibodies = new HashMap<>(); + Map> antibodyRefreshFactors = new HashMap<>(); + configureAntibodies(initialAntibodies, antibodyRefreshFactors, mutEscDelta, mutEscBa1, mutEscBa5, mutEscStrainA, mutEscStrainB); + + AntibodyModel.Config antibodyConfig = new AntibodyModel.Config(initialAntibodies, antibodyRefreshFactors); + + double immuneSigma = 3.0; + if (params != null) { + antibodyConfig.setImmuneReponseSigma(immuneSigma); + } + + bind(AntibodyModel.Config.class).toInstance(antibodyConfig); + + } + + private void configureAntibodies(Map> initialAntibodies, + Map> antibodyRefreshFactors, + double mutEscDelta, double mutEscBa1, double mutEscBa5, double mutEscStrainA, double mutEscStrainB) { + for (VaccinationType immunityType : VaccinationType.values()) { + initialAntibodies.put(immunityType, new EnumMap<>( VirusStrain.class ) ); + for (VirusStrain virusStrain : VirusStrain.values()) { + + if (immunityType == VaccinationType.mRNA) { + initialAntibodies.get(immunityType).put(virusStrain, 29.2); //10.0 + } + else if (immunityType == VaccinationType.vector) { + initialAntibodies.get(immunityType).put(virusStrain, 6.8); //2.5 + } + else { + initialAntibodies.get(immunityType).put(virusStrain, 5.0); + } + } + } + + for (VirusStrain immunityType : VirusStrain.values()) { + initialAntibodies.put(immunityType, new EnumMap<>( VirusStrain.class ) ); + for (VirusStrain virusStrain : VirusStrain.values()) { + initialAntibodies.get(immunityType).put(virusStrain, 5.0); + } + } + + //mRNAAlpha, mRNADelta, mRNABA1 comes from Sydney's calibration. + //The other values come from Rössler et al. + + //Wildtype + double mRNAAlpha = 29.2; + initialAntibodies.get(VaccinationType.mRNA).put(VirusStrain.SARS_CoV_2, mRNAAlpha); + initialAntibodies.get(VaccinationType.vector).put(VirusStrain.SARS_CoV_2, mRNAAlpha * 210. / 700.); + initialAntibodies.get(VirusStrain.SARS_CoV_2).put(VirusStrain.SARS_CoV_2, mRNAAlpha * 300. / 700.); + initialAntibodies.get(VirusStrain.ALPHA).put(VirusStrain.SARS_CoV_2, mRNAAlpha * 300. / 700.); + initialAntibodies.get(VirusStrain.DELTA).put(VirusStrain.SARS_CoV_2, mRNAAlpha * 210. / 700.); + initialAntibodies.get(VirusStrain.OMICRON_BA1).put(VirusStrain.SARS_CoV_2, 0.01); + initialAntibodies.get(VirusStrain.OMICRON_BA2).put(VirusStrain.SARS_CoV_2, 0.01); + initialAntibodies.get(VirusStrain.OMICRON_BA5).put(VirusStrain.SARS_CoV_2, 0.01); + initialAntibodies.get(VirusStrain.STRAIN_A).put(VirusStrain.SARS_CoV_2, 0.01); + initialAntibodies.get(VirusStrain.STRAIN_B).put(VirusStrain.SARS_CoV_2, 0.01); + initialAntibodies.get(VaccinationType.ba1Update).put(VirusStrain.SARS_CoV_2, mRNAAlpha / mutEscDelta / mutEscBa1); + initialAntibodies.get(VaccinationType.ba5Update).put(VirusStrain.SARS_CoV_2, mRNAAlpha / mutEscDelta / mutEscBa1 / mutEscBa5); + + //Alpha + initialAntibodies.get(VaccinationType.mRNA).put(VirusStrain.ALPHA, mRNAAlpha); + initialAntibodies.get(VaccinationType.vector).put(VirusStrain.ALPHA, mRNAAlpha * 210. / 700.); + initialAntibodies.get(VirusStrain.SARS_CoV_2).put(VirusStrain.ALPHA, mRNAAlpha * 300. / 700.); + initialAntibodies.get(VirusStrain.ALPHA).put(VirusStrain.ALPHA, mRNAAlpha * 300. / 700.); + initialAntibodies.get(VirusStrain.DELTA).put(VirusStrain.ALPHA, mRNAAlpha * 210. / 700.); + initialAntibodies.get(VirusStrain.OMICRON_BA1).put(VirusStrain.ALPHA, 0.01); + initialAntibodies.get(VirusStrain.OMICRON_BA2).put(VirusStrain.ALPHA, 0.01); + initialAntibodies.get(VirusStrain.OMICRON_BA5).put(VirusStrain.ALPHA, 0.01); + initialAntibodies.get(VirusStrain.STRAIN_A).put(VirusStrain.ALPHA, 0.01); + initialAntibodies.get(VirusStrain.STRAIN_B).put(VirusStrain.ALPHA, 0.01); + initialAntibodies.get(VaccinationType.ba1Update).put(VirusStrain.ALPHA, mRNAAlpha / mutEscDelta / mutEscBa1); + initialAntibodies.get(VaccinationType.ba5Update).put(VirusStrain.ALPHA, mRNAAlpha / mutEscDelta / mutEscBa1 / mutEscBa5); + + //DELTA + double mRNADelta = mRNAAlpha / mutEscDelta; + initialAntibodies.get(VaccinationType.mRNA).put(VirusStrain.DELTA, mRNADelta); + initialAntibodies.get(VaccinationType.vector).put(VirusStrain.DELTA, mRNADelta * 150./300.); + initialAntibodies.get(VirusStrain.SARS_CoV_2).put(VirusStrain.DELTA, mRNADelta * 64./300.); + initialAntibodies.get(VirusStrain.ALPHA).put(VirusStrain.DELTA, mRNADelta * 64./300.); + initialAntibodies.get(VirusStrain.DELTA).put(VirusStrain.DELTA, mRNADelta * 450./300.); + initialAntibodies.get(VirusStrain.OMICRON_BA1).put(VirusStrain.DELTA, 0.01); + initialAntibodies.get(VirusStrain.OMICRON_BA2).put(VirusStrain.DELTA, 0.01); + initialAntibodies.get(VirusStrain.OMICRON_BA5).put(VirusStrain.DELTA, 0.01); + initialAntibodies.get(VirusStrain.STRAIN_A).put(VirusStrain.DELTA, 0.01); + initialAntibodies.get(VirusStrain.STRAIN_B).put(VirusStrain.DELTA, 0.01); + initialAntibodies.get(VaccinationType.ba1Update).put(VirusStrain.DELTA, mRNADelta / mutEscBa1); + initialAntibodies.get(VaccinationType.ba5Update).put(VirusStrain.DELTA, mRNADelta / mutEscBa1 / mutEscBa5); + + //BA.1 + double mRNABA1 = mRNADelta / mutEscBa1; + initialAntibodies.get(VaccinationType.mRNA).put(VirusStrain.OMICRON_BA1, mRNABA1); + initialAntibodies.get(VaccinationType.vector).put(VirusStrain.OMICRON_BA1, mRNABA1 * 4./20.); //??? + initialAntibodies.get(VirusStrain.SARS_CoV_2).put(VirusStrain.OMICRON_BA1, mRNABA1 * 6./20.); + initialAntibodies.get(VirusStrain.ALPHA).put(VirusStrain.OMICRON_BA1, mRNABA1 * 6./20.); + initialAntibodies.get(VirusStrain.DELTA).put(VirusStrain.OMICRON_BA1, mRNABA1 * 8./20.); + initialAntibodies.get(VirusStrain.OMICRON_BA1).put(VirusStrain.OMICRON_BA1, 64.0 / 300.); + initialAntibodies.get(VirusStrain.OMICRON_BA2).put(VirusStrain.OMICRON_BA1, 64.0 / 300. / 1.4); + initialAntibodies.get(VirusStrain.OMICRON_BA5).put(VirusStrain.OMICRON_BA1, 64.0 / 300. / mutEscBa5); //todo: is 1.4 + initialAntibodies.get(VirusStrain.STRAIN_A).put(VirusStrain.OMICRON_BA1, 64.0 / 300. / mutEscBa5 / mutEscStrainA); + initialAntibodies.get(VirusStrain.STRAIN_B).put(VirusStrain.OMICRON_BA1, 64.0 / 300. / mutEscStrainB); + initialAntibodies.get(VaccinationType.ba1Update).put(VirusStrain.OMICRON_BA1, mRNAAlpha); + initialAntibodies.get(VaccinationType.ba5Update).put(VirusStrain.OMICRON_BA1, mRNAAlpha / mutEscBa5); + + //BA.2 + double mRNABA2 = mRNABA1; + initialAntibodies.get(VaccinationType.mRNA).put(VirusStrain.OMICRON_BA2, mRNABA2); + initialAntibodies.get(VaccinationType.vector).put(VirusStrain.OMICRON_BA2, mRNABA2 * 4./20.); + initialAntibodies.get(VirusStrain.SARS_CoV_2).put(VirusStrain.OMICRON_BA2, mRNABA2 * 6./20.); + initialAntibodies.get(VirusStrain.ALPHA).put(VirusStrain.OMICRON_BA2, mRNABA2 * 6./20.); + initialAntibodies.get(VirusStrain.DELTA).put(VirusStrain.OMICRON_BA2, mRNABA2 * 8./20.); + initialAntibodies.get(VirusStrain.OMICRON_BA1).put(VirusStrain.OMICRON_BA2, 64.0 / 300. / 1.4); + initialAntibodies.get(VirusStrain.OMICRON_BA2).put(VirusStrain.OMICRON_BA2, 64.0 / 300.); + initialAntibodies.get(VirusStrain.OMICRON_BA5).put(VirusStrain.OMICRON_BA2, 64.0 / 300. / mutEscBa5); + initialAntibodies.get(VirusStrain.STRAIN_A).put(VirusStrain.OMICRON_BA2, 64.0 / 300. / mutEscBa5 / mutEscStrainA); + initialAntibodies.get(VirusStrain.STRAIN_B).put(VirusStrain.OMICRON_BA2, 64.0 / 300. / mutEscStrainB); + initialAntibodies.get(VaccinationType.ba1Update).put(VirusStrain.OMICRON_BA2, mRNAAlpha); + initialAntibodies.get(VaccinationType.ba5Update).put(VirusStrain.OMICRON_BA2, mRNAAlpha / mutEscBa5); + + + //BA.5 + double mRNABa5 = mRNABA2 / mutEscBa5; + initialAntibodies.get(VaccinationType.mRNA).put(VirusStrain.OMICRON_BA5, mRNABa5); + initialAntibodies.get(VaccinationType.vector).put(VirusStrain.OMICRON_BA5, mRNABa5 * 4./20.); + initialAntibodies.get(VirusStrain.SARS_CoV_2).put(VirusStrain.OMICRON_BA5, mRNABa5 * 6./20.); + initialAntibodies.get(VirusStrain.ALPHA).put(VirusStrain.OMICRON_BA5, mRNABa5 * 6./20.); + initialAntibodies.get(VirusStrain.DELTA).put(VirusStrain.OMICRON_BA5, mRNABa5 * 8./20.); + initialAntibodies.get(VirusStrain.OMICRON_BA1).put(VirusStrain.OMICRON_BA5, 64.0 / 300. / mutEscBa5);// todo: do we need 1.4? + initialAntibodies.get(VirusStrain.OMICRON_BA2).put(VirusStrain.OMICRON_BA5, 64.0 / 300./ mutEscBa5); + initialAntibodies.get(VirusStrain.OMICRON_BA5).put(VirusStrain.OMICRON_BA5, 64.0 / 300.); + initialAntibodies.get(VirusStrain.STRAIN_A).put(VirusStrain.OMICRON_BA5, 64.0 / 300./ mutEscStrainA); //todo ??? + initialAntibodies.get(VirusStrain.STRAIN_B).put(VirusStrain.OMICRON_BA5, 64.0 / 300. / mutEscBa5 / mutEscStrainB); + initialAntibodies.get(VaccinationType.ba1Update).put(VirusStrain.OMICRON_BA5, mRNAAlpha / mutEscBa5); + initialAntibodies.get(VaccinationType.ba5Update).put(VirusStrain.OMICRON_BA5, mRNAAlpha); + + //StrainA + double mRNAStrainA = mRNABa5 / mutEscStrainA; + initialAntibodies.get(VaccinationType.mRNA).put(VirusStrain.STRAIN_A, mRNAStrainA); + initialAntibodies.get(VaccinationType.vector).put(VirusStrain.STRAIN_A, mRNAStrainA * 4./20.); + initialAntibodies.get(VirusStrain.SARS_CoV_2).put(VirusStrain.STRAIN_A, mRNAStrainA * 6./20.); + initialAntibodies.get(VirusStrain.ALPHA).put(VirusStrain.STRAIN_A, mRNAStrainA * 6./20.); + initialAntibodies.get(VirusStrain.DELTA).put(VirusStrain.STRAIN_A, mRNAStrainA * 8./20.); + initialAntibodies.get(VirusStrain.OMICRON_BA1).put(VirusStrain.STRAIN_A, 64.0 / 300. / mutEscBa5 /mutEscStrainA); + initialAntibodies.get(VirusStrain.OMICRON_BA2).put(VirusStrain.STRAIN_A, 64.0 / 300./ mutEscBa5 /mutEscStrainA); + initialAntibodies.get(VirusStrain.OMICRON_BA5).put(VirusStrain.STRAIN_A, 64.0 / 300. / mutEscStrainA); + initialAntibodies.get(VirusStrain.STRAIN_A).put(VirusStrain.STRAIN_A, 64.0 / 300.); + initialAntibodies.get(VirusStrain.STRAIN_B).put(VirusStrain.STRAIN_A, 64.0 / 300. / mutEscStrainA / mutEscStrainB / mutEscBa5); + initialAntibodies.get(VaccinationType.ba1Update).put(VirusStrain.STRAIN_A, mRNAAlpha / mutEscBa5 / mutEscStrainA); + initialAntibodies.get(VaccinationType.ba5Update).put(VirusStrain.STRAIN_A, mRNAAlpha / mutEscStrainA); + + //StrainB + double mRNAStrainB = mRNABA2 / mutEscStrainB; + initialAntibodies.get(VaccinationType.mRNA).put(VirusStrain.STRAIN_B, mRNAStrainB); + initialAntibodies.get(VaccinationType.vector).put(VirusStrain.STRAIN_B, mRNAStrainB * 4./20.); + initialAntibodies.get(VirusStrain.SARS_CoV_2).put(VirusStrain.STRAIN_B, mRNAStrainB * 6./20.); + initialAntibodies.get(VirusStrain.ALPHA).put(VirusStrain.STRAIN_B, mRNAStrainB * 6./20.); + initialAntibodies.get(VirusStrain.DELTA).put(VirusStrain.STRAIN_B, mRNAStrainB * 8./20.); + initialAntibodies.get(VirusStrain.OMICRON_BA1).put(VirusStrain.STRAIN_B, 64.0 / 300. / mutEscStrainB); + initialAntibodies.get(VirusStrain.OMICRON_BA2).put(VirusStrain.STRAIN_B, 64.0 / 300./ mutEscStrainB); + initialAntibodies.get(VirusStrain.OMICRON_BA5).put(VirusStrain.STRAIN_B, 64.0 / 300. / mutEscStrainB / mutEscBa5); + initialAntibodies.get(VirusStrain.STRAIN_A).put(VirusStrain.STRAIN_B, 64.0 / 300./ mutEscStrainA / mutEscStrainB / mutEscBa5); + initialAntibodies.get(VirusStrain.STRAIN_B).put(VirusStrain.STRAIN_B, 64.0 / 300.); + initialAntibodies.get(VaccinationType.ba1Update).put(VirusStrain.STRAIN_B, mRNAAlpha / mutEscStrainB); + initialAntibodies.get(VaccinationType.ba5Update).put(VirusStrain.STRAIN_B, mRNAAlpha / mutEscStrainB / mutEscBa5); + + + for (VaccinationType immunityType : VaccinationType.values()) { + antibodyRefreshFactors.put(immunityType, new EnumMap<>( VirusStrain.class ) ); + for (VirusStrain virusStrain : VirusStrain.values()) { + + if (immunityType == VaccinationType.mRNA) { + antibodyRefreshFactors.get(immunityType).put(virusStrain, 15.0); + } + else if (immunityType == VaccinationType.vector) { + antibodyRefreshFactors.get(immunityType).put(virusStrain, 5.0); + } + else if (immunityType == VaccinationType.ba1Update) { + antibodyRefreshFactors.get(immunityType).put(virusStrain, 15.0); + } + else if (immunityType == VaccinationType.ba5Update) { + antibodyRefreshFactors.get(immunityType).put(virusStrain, 15.0); + } + else { + antibodyRefreshFactors.get(immunityType).put(virusStrain, Double.NaN); + } + + } + } + + for (VirusStrain immunityType : VirusStrain.values()) { + antibodyRefreshFactors.put(immunityType, new EnumMap<>( VirusStrain.class ) ); + for (VirusStrain virusStrain : VirusStrain.values()) { + antibodyRefreshFactors.get(immunityType).put(virusStrain, 15.0); + } + } + + + +// System.out.print("immunityGiver"); +// for (VirusStrain immunityFrom : VirusStrain.values()) { +// if (immunityFrom == VirusStrain.OMICRON_BA1) { +// System.out.print( "," + "BA.1"); +// } else if (immunityFrom == VirusStrain.OMICRON_BA2) { +// System.out.print( "," + "BA.2"); +// } else { +// System.out.print( "," + immunityFrom); +// } +// } +// +// +// for (ImmunityEvent immunityGiver : VaccinationType.values()) { +// System.out.print("\n" + immunityGiver); +// for (VirusStrain immunityFrom : VirusStrain.values()) { +// System.out.print("," + String.format("%.3g", initialAntibodies.get(immunityGiver).get(immunityFrom))); +// } +// } +// for (ImmunityEvent immunityGiver : VirusStrain.values()) { +// System.out.print("\n" + immunityGiver); +// for (VirusStrain immunityFrom : VirusStrain.values()) { +// System.out.print("," + String.format("%.3g", initialAntibodies.get(immunityGiver).get(immunityFrom))); +// } +// } +// +// System.out.println(); + + } + }); + + } + + private SnzCologneProductionScenario getBindings(double pHousehold, Params params) { + return new SnzCologneProductionScenario.Builder() + .setCarnivalModel(SnzCologneProductionScenario.CarnivalModel.yes) + .setSebastianUpdate(true) + .setLeisureCorrection(1.3) //params == null ? 0.0 : params.actCorrection) + .setScaleForActivityLevels(1.3) + .setSuscHouseholds_pct(pHousehold) + .setActivityHandling(EpisimConfigGroup.ActivityHandling.startOfDay) +// .setTestingModel(params != null ? FlexibleTestingModel.class : DefaultTestingModel.class) + .setInfectionModel(InfectionModelWithAntibodies.class) + .build(); + } + + @Override + public Metadata getMetadata() { + return Metadata.of("cologne", "calibration"); + } + + @Override + public Collection postProcessing() { + return List.of( + new VaccinationEffectiveness().withArgs(), + new RValuesFromEvents().withArgs(), + new VaccinationEffectivenessFromPotentialInfections().withArgs("--remove-infected"), + new FilterEvents().withArgs("--output","./output/"), + new HospitalNumbersFromEvents().withArgs("--output","./output/","--input","/scratch/projects/bzz0020/episim-input") +// new SecondaryAttackRateFromEvents().withArgs() + ); + } + + @Override + public Config prepareConfig(int id, Params params) { + + if (DEBUG_MODE) { + if (runCount == 0){ //&& params.strAEsc != 0.0 && params.ba5Inf == 0. && params.eduTest.equals("true")) { + runCount++; + } else { + return null; + } + } + + SnzCologneProductionScenario module = getBindings(0.0, params); + + Config config = module.config(); + + + config.global().setRandomSeed(params.seed); + + EpisimConfigGroup episimConfig = ConfigUtils.addOrGetModule(config, EpisimConfigGroup.class); + + + episimConfig.setCalibrationParameter(episimConfig.getCalibrationParameter() * 1.2); + + + + //--------------------------------------- + // S T R A I N S + //--------------------------------------- + + VirusStrainConfigGroup virusStrainConfigGroup = ConfigUtils.addOrGetModule(config, VirusStrainConfigGroup.class); + + +// virusStrainConfigGroup.getOrAddParams(VirusStrain.DELTA).setInfectiousness(virusStrainConfigGroup.getOrAddParams(VirusStrain.DELTA).getInfectiousness() * params.deltaTheta); + + //BA5 + double ba5Inf = 1.0; + double oHos = virusStrainConfigGroup.getParams(VirusStrain.OMICRON_BA2).getFactorSeriouslySick(); + virusStrainConfigGroup.getOrAddParams(VirusStrain.OMICRON_BA5).setInfectiousness(virusStrainConfigGroup.getParams(VirusStrain.OMICRON_BA2).getInfectiousness() * ba5Inf); + virusStrainConfigGroup.getOrAddParams(VirusStrain.OMICRON_BA5).setFactorSeriouslySick(oHos); + virusStrainConfigGroup.getOrAddParams(VirusStrain.OMICRON_BA5).setFactorSeriouslySickVaccinated(oHos); + virusStrainConfigGroup.getOrAddParams(VirusStrain.OMICRON_BA5).setFactorCritical(oHos); + + +// STRAIN_A + if (!params.StrainA.equals("off")) { + + virusStrainConfigGroup.getOrAddParams(VirusStrain.STRAIN_A).setInfectiousness(virusStrainConfigGroup.getParams(VirusStrain.OMICRON_BA5).getInfectiousness() * ba5Inf); + virusStrainConfigGroup.getOrAddParams(VirusStrain.STRAIN_A).setFactorSeriouslySick(oHos); + virusStrainConfigGroup.getOrAddParams(VirusStrain.STRAIN_A).setFactorSeriouslySickVaccinated(oHos); + virusStrainConfigGroup.getOrAddParams(VirusStrain.STRAIN_A).setFactorCritical(oHos); + } + +// STRAIN_B + if (!params.StrainB.equals("off")) { + + virusStrainConfigGroup.getOrAddParams(VirusStrain.STRAIN_B).setInfectiousness(virusStrainConfigGroup.getParams(VirusStrain.OMICRON_BA5).getInfectiousness() * ba5Inf); + virusStrainConfigGroup.getOrAddParams(VirusStrain.STRAIN_B).setFactorSeriouslySick(oHos); + virusStrainConfigGroup.getOrAddParams(VirusStrain.STRAIN_B).setFactorSeriouslySickVaccinated(oHos); + virusStrainConfigGroup.getOrAddParams(VirusStrain.STRAIN_B).setFactorCritical(oHos); + } + + // remove age-based susceptibility of strains starting with DELTA + + String ageSusc = "false"; + if (!Boolean.parseBoolean(ageSusc)) { + TreeMap nonSteppedAgeSusceptibility = new TreeMap<>(Map.of( + 19, 1d, + 20, 1d + )); + + for (VirusStrain strain : List.of(VirusStrain.DELTA, VirusStrain.OMICRON_BA1, VirusStrain.OMICRON_BA2, VirusStrain.OMICRON_BA5, VirusStrain.STRAIN_A)) { + virusStrainConfigGroup.getOrAddParams(strain).setAgeSusceptibility(nonSteppedAgeSusceptibility); + } + } + + // increase infectivity of alpha + virusStrainConfigGroup.getOrAddParams(VirusStrain.ALPHA).setInfectiousness(virusStrainConfigGroup.getOrAddParams(VirusStrain.ALPHA).getInfectiousness() * 1.4); + + double deltaTheta = 0.9; + virusStrainConfigGroup.getOrAddParams(VirusStrain.DELTA).setInfectiousness(virusStrainConfigGroup.getOrAddParams(VirusStrain.DELTA).getInfectiousness() * deltaTheta); + double ba1Inf = 1.9; // 2.0,2.1,2.2 + double ba2Inf = 1.7; + virusStrainConfigGroup.getOrAddParams(VirusStrain.OMICRON_BA1).setInfectiousness(virusStrainConfigGroup.getOrAddParams(VirusStrain.DELTA).getInfectiousness() * ba1Inf); + virusStrainConfigGroup.getOrAddParams(VirusStrain.OMICRON_BA2).setInfectiousness(virusStrainConfigGroup.getOrAddParams(VirusStrain.DELTA).getInfectiousness() * ba1Inf * ba2Inf); + + + + + + + //--------------------------------------- + // I M P O R T + //--------------------------------------- + + Map infPerDayAlpha = new HashMap<>(episimConfig.getInfections_pers_per_day().getOrDefault(VirusStrain.ALPHA, new TreeMap<>())); + + + // reconfig disease import of alpha + LocalDate startDateAlpha = LocalDate.parse("2021-01-15"); + + for (int i = 0; i < 7; i++) { + infPerDayAlpha.put(startDateAlpha.plusDays(i), 4); + } + + + infPerDayAlpha.put(startDateAlpha.plusDays(7), 1); + + episimConfig.setInfections_pers_per_day(VirusStrain.ALPHA, infPerDayAlpha); + + + configureFutureDiseaseImport(params, episimConfig); + + //--------------------------------------- + // R E S T R I C T I O N S + //--------------------------------------- + + FixedPolicy.ConfigBuilder builder = FixedPolicy.parse(episimConfig.getPolicy()); + + //school + String schoolUpdate = "yes"; + if(schoolUpdate.equals("yes")) { + // school closed completely until 21.2.2022 + builder.restrict(LocalDate.parse("2021-01-11"), 0.2, "educ_primary", "educ_kiga", "educ_secondary", "educ_tertiary", "educ_other"); + builder.restrict(LocalDate.parse("2021-02-21"), 0.5, "educ_primary"); + builder.restrict(LocalDate.parse("2021-03-15"), 0.5, "educ_kiga", "educ_secondary", "educ_tertiary", "educ_other"); + + } else if (schoolUpdate.equals("no")) { + + } else { + throw new RuntimeException("param value doesn't exist"); + } + + String schoolTest = "later"; + if (schoolTest.equals("later")) { + TestingConfigGroup testingConfigGroup = ConfigUtils.addOrGetModule(config, TestingConfigGroup.class); + TestingConfigGroup.TestingParams rapidTest = testingConfigGroup.getOrAddParams(TestType.RAPID_TEST); +// TestingConfigGroup.TestingParams pcrTest = testingConfigGroup.getOrAddParams(TestType.PCR); + Map> testingRateForActivitiesRapid = rapidTest.getTestingRateForActivities(); +// Map> testingRateForActivitiesPCR = pcrTest.getTestingRateForActivities(); + + + for (LocalDate date = LocalDate.parse("2021-03-19"); date.isBefore(LocalDate.parse("2021-04-25")); date = date.plusDays(1)) { + + testingRateForActivitiesRapid.get("educ_kiga").put(date, 0.); + testingRateForActivitiesRapid.get("educ_primary").put(date, 0.); + testingRateForActivitiesRapid.get("educ_secondary").put(date, 0.); + testingRateForActivitiesRapid.get("educ_tertiary").put(date, 0.); + testingRateForActivitiesRapid.get("educ_other").put(date, 0.); + + } + + testingRateForActivitiesRapid.get("educ_kiga").put(LocalDate.parse("2021-09-20"), 0.); + testingRateForActivitiesRapid.get("educ_primary").put(LocalDate.parse("2021-09-20"), 0.); + +// testingRateForActivitiesPCR.get("educ_primary").put(LocalDate.parse("2021-05-10"), 0.4); + testingRateForActivitiesRapid.get("educ_secondary").put(LocalDate.parse("2021-05-10"), 0.4); + testingRateForActivitiesRapid.get("educ_tertiary").put(LocalDate.parse("2021-05-10"), 0.4); + testingRateForActivitiesRapid.get("educ_other").put(LocalDate.parse("2021-05-10"), 0.4); + + } else if (schoolTest.equals("base")) { + + }else { + throw new RuntimeException("param value doesn't exist"); + } + + + // masks + //pt: masks + String maskType = "45to45"; + if (maskType.equals("45to45")) { + for (LocalDate date = LocalDate.parse("2020-04-21"); date.isBefore(LocalDate.parse("2021-05-01")); date = date.plusDays(1)) { + builder.restrict(date, Restriction.ofMask(Map.of(FaceMask.CLOTH, 0.45, FaceMask.SURGICAL, 0.45)), "pt", "errands", "shop_daily", "shop_other"); + + } + } else if (maskType.equals("base")) { + + } else { + throw new RuntimeException("param value doesn't exist"); + } + + episimConfig.setPolicy(builder.build()); + + + //--------------------------------------- + // M I S C + //--------------------------------------- + + // vaccination + VaccinationConfigGroup vaccinationConfig = ConfigUtils.addOrGetModule(config, VaccinationConfigGroup.class); + vaccinationConfig.setUseIgA(Boolean.parseBoolean("true")); + vaccinationConfig.setTimePeriodIgA(730.); + + + //modify contact intensity + double workCi = 0.75; + episimConfig.getOrAddContainerParams("work").setContactIntensity(episimConfig.getOrAddContainerParams("work").getContactIntensity() * workCi); + episimConfig.getOrAddContainerParams("business").setContactIntensity(episimConfig.getOrAddContainerParams("business").getContactIntensity() * workCi); + + + double leisureCi = 0.4; + episimConfig.getOrAddContainerParams("leisure").setContactIntensity(episimConfig.getOrAddContainerParams("leisure").getContactIntensity() * leisureCi); + episimConfig.getOrAddContainerParams("visit").setContactIntensity(episimConfig.getOrAddContainerParams("visit").getContactIntensity() * leisureCi); + + + double schoolCi = 0.75; + episimConfig.getOrAddContainerParams("educ_kiga").setContactIntensity(episimConfig.getOrAddContainerParams("educ_kiga").getContactIntensity() * schoolCi); + episimConfig.getOrAddContainerParams("educ_primary").setContactIntensity(episimConfig.getOrAddContainerParams("educ_primary").getContactIntensity() * schoolCi); + episimConfig.getOrAddContainerParams("educ_secondary").setContactIntensity(episimConfig.getOrAddContainerParams("educ_secondary").getContactIntensity() * schoolCi); + episimConfig.getOrAddContainerParams("educ_tertiary").setContactIntensity(episimConfig.getOrAddContainerParams("educ_tertiary").getContactIntensity() * schoolCi); + episimConfig.getOrAddContainerParams("educ_higher").setContactIntensity(episimConfig.getOrAddContainerParams("educ_higher").getContactIntensity() * schoolCi); + episimConfig.getOrAddContainerParams("educ_other").setContactIntensity(episimConfig.getOrAddContainerParams("educ_other").getContactIntensity() * schoolCi); + + + if (DEBUG_MODE) { + UtilsJR.produceDiseaseImportPlot(episimConfig.getInfections_pers_per_day()); + + } + + return config; + } + + private void configureFutureDiseaseImport(Params params, EpisimConfigGroup episimConfig) { + Map infPerDayBa1 = new HashMap<>(episimConfig.getInfections_pers_per_day().getOrDefault(VirusStrain.OMICRON_BA1, new TreeMap<>())); + Map infPerDayBa2 = new HashMap<>(episimConfig.getInfections_pers_per_day().getOrDefault(VirusStrain.OMICRON_BA2, new TreeMap<>())); + Map infPerDayBa5 = new HashMap<>(episimConfig.getInfections_pers_per_day().getOrDefault(VirusStrain.OMICRON_BA5, new TreeMap<>())); + Map infPerDayStrA = new HashMap<>(episimConfig.getInfections_pers_per_day().getOrDefault(VirusStrain.STRAIN_A, new TreeMap<>())); + Map infPerDayStrB = new HashMap<>(episimConfig.getInfections_pers_per_day().getOrDefault(VirusStrain.STRAIN_B, new TreeMap<>())); + + + // add initial impulses for strains + //BA.1 +// LocalDate ba1Date = LocalDate.parse(params.ba1Date); +// for (int i = 0; i < 7; i++) { +// infPerDayBa1.put(ba1Date.plusDays(i), 4); +// } +// infPerDayBa1.put(ba1Date.plusDays(7), 1); + + + //BA.2 + LocalDate ba2Date = LocalDate.parse("2021-12-18"); + for (int i = 0; i < 7; i++) { + infPerDayBa2.put(ba2Date.plusDays(i), 4); + } + infPerDayBa2.put(ba2Date.plusDays(7), 1); + + //BA.5 + LocalDate ba5Date = LocalDate.parse("2022-04-10"); + for (int i = 0; i < 7; i++) { + infPerDayBa5.put(ba5Date.plusDays(i), 4); + } + infPerDayBa5.put(ba5Date.plusDays(7), 1); + + //StrainA + if (!params.StrainA.equals("off")) { + infPerDayStrA.put(LocalDate.parse("2020-01-01"), 0); + LocalDate strADate = LocalDate.parse("2022-11-01"); + for (int i = 0; i < 7; i++) { + infPerDayStrA.put(strADate.plusDays(i), 4); + } + infPerDayStrA.put(strADate.plusDays(7), 1); + } + + //StrainB + if (!params.StrainB.equals("off")) { + infPerDayStrB.put(LocalDate.parse("2020-01-01"), 0); + LocalDate strBDate = LocalDate.parse("2022-11-01"); + for (int i = 0; i < 7; i++) { + infPerDayStrB.put(strBDate.plusDays(i), 4); + } + infPerDayStrB.put(strBDate.plusDays(7), 1); + } + + + // add projected disease import for vacation waves after initial disease import + int facBa2 = 4; + int facBa5 = 4; + int facStrAB = 4; + + LocalDate dateBa2 = LocalDate.parse("2022-01-27"); // local min of disease import + LocalDate dateBa5 = LocalDate.parse("2022-05-01"); // after vaca import + LocalDate dateStrainAB = LocalDate.parse("2022-11-18"); // after vaca import + + + NavigableMap data = DataUtils.readDiseaseImport(SnzCologneProductionScenario.INPUT.resolve("cologneDiseaseImport_Projected.csv")); + LocalDate date = null; + for (Map.Entry entry : data.entrySet()) { + date = entry.getKey(); + double factor = 0.25 * 2352476. / 919936.; //25% sample, data is given for Cologne City so we have to scale it to the whole model +// + double cases = factor * entry.getValue(); + + if (date.isAfter(dateStrainAB) && (!params.StrainA.equals("off") || !params.StrainB.equals("off"))) { + if (!params.StrainA.equals("off") && !params.StrainB.equals("off")) { + infPerDayStrA.put(date, ((int) cases * facStrAB) == 0 ? 1 : (int) (0.5 * cases * facStrAB)); + infPerDayStrB.put(date, ((int) cases * facStrAB) == 0 ? 1 : (int) (0.5 * cases * facStrAB)); + } + else if (!params.StrainA.equals("off")) { + infPerDayStrA.put(date, ((int) cases * facStrAB) == 0 ? 1 : (int) (cases * facStrAB)); + } + else if (!params.StrainB.equals("off")) { + infPerDayStrB.put(date, ((int) cases * facStrAB) == 0 ? 1 : (int) (cases * facStrAB)); + } + else { + throw new RuntimeException(); + } + infPerDayBa5.put(date, 1); + infPerDayBa2.put(date, 1); + } else if (date.isAfter(dateBa5)) { + infPerDayBa5.put(date, ((int) cases * facBa5) == 0 ? 1 : (int) (cases * facBa5)); + infPerDayBa2.put(date, 1); + } else if (date.isAfter(dateBa2)) { + infPerDayBa2.put(date, ((int) cases * facBa2) == 0 ? 1 : (int) (cases * facBa2)); + } + + } + + // save disease import + episimConfig.setInfections_pers_per_day(VirusStrain.OMICRON_BA1, infPerDayBa1); + episimConfig.setInfections_pers_per_day(VirusStrain.OMICRON_BA2, infPerDayBa2); + episimConfig.setInfections_pers_per_day(VirusStrain.OMICRON_BA5, infPerDayBa5); + + if (!params.StrainA.equals("off")) { + episimConfig.setInfections_pers_per_day(VirusStrain.STRAIN_A, infPerDayStrA); + } + if (!params.StrainB.equals("off")) { + episimConfig.setInfections_pers_per_day(VirusStrain.STRAIN_B, infPerDayStrB); + } + } + + public static final class Params { + // general + @GenerateSeeds(5) + public long seed; + + @StringParameter({"off", "3.0", "6.0"}) + public String StrainA; + + @StringParameter({"off", "3.0", "6.0"}) + public String StrainB; + + @StringParameter({"2022-12-01"}) + public String resDate; + +// @StringParameter({"false", "true"}) +// public String igA; + // vaccination campaign + @StringParameter({"ba1Update", "ba5Update", "mRNA", "off"}) + public String vacType; + } + + + public static void main(String[] args) { + String[] args2 = { + RunParallel.OPTION_SETUP, CologneBMBF20220805.class.getName(), + RunParallel.OPTION_PARAMS, Params.class.getName(), + RunParallel.OPTION_TASKS, Integer.toString(1), + RunParallel.OPTION_ITERATIONS, Integer.toString(1000), + RunParallel.OPTION_METADATA + }; + + RunParallel.main(args2); + } + + +} + diff --git a/src/main/java/org/matsim/run/batch/CologneBMBF20220805_IfSG.java b/src/main/java/org/matsim/run/batch/CologneBMBF20220805_IfSG.java new file mode 100644 index 000000000..57bc3a72e --- /dev/null +++ b/src/main/java/org/matsim/run/batch/CologneBMBF20220805_IfSG.java @@ -0,0 +1,993 @@ +package org.matsim.run.batch; + +import com.google.inject.AbstractModule; +import com.google.inject.Module; +import com.google.inject.Singleton; +import com.google.inject.multibindings.Multibinder; +import com.google.inject.util.Modules; +import it.unimi.dsi.fastutil.ints.Int2DoubleAVLTreeMap; +import it.unimi.dsi.fastutil.ints.Int2DoubleMap; +import org.matsim.core.config.Config; +import org.matsim.core.config.ConfigUtils; +import org.matsim.episim.*; +import org.matsim.episim.analysis.*; +import org.matsim.episim.model.*; +import org.matsim.episim.model.testing.DefaultTestingModel; +import org.matsim.episim.model.testing.FlexibleTestingModel; +import org.matsim.episim.model.testing.TestType; +import org.matsim.episim.model.vaccination.VaccinationModel; +import org.matsim.episim.model.vaccination.VaccinationStrategyBMBF0617; +import org.matsim.episim.policy.FixedPolicy; +import org.matsim.episim.policy.Restriction; +import org.matsim.run.RunParallel; +import org.matsim.run.modules.SnzCologneProductionScenario; + +import javax.annotation.Nullable; +import java.time.DayOfWeek; +import java.time.LocalDate; +import java.util.*; +import java.util.function.BiFunction; + + +/** + * Batch for Bmbf runs + */ +public class CologneBMBF20220805_IfSG implements BatchRun { + + boolean DEBUG_MODE = false; + int runCount = 0; + + @Nullable + @Override + public Module getBindings(int id, @Nullable Params params) { + return Modules.override(getBindings(0.0, params)).with(new AbstractModule() { + @Override + protected void configure() { + + // BIND ANTIBODY MODEL + double mutEscDelta = 29.2 / 10.9; + double mutEscBa1 = 10.9 / 1.9; + double mutEscBa5 = 2.9; + + double mutEscStrainA = 0.; + double mutEscStrainB = 0.; + + if (params != null && !params.StrainA.equals("off")) { + mutEscStrainA = Double.parseDouble(params.StrainA); + } + if (params != null && !params.StrainB.equals("off")) { + mutEscStrainB = Double.parseDouble(params.StrainB); + } + + Map> initialAntibodies = new HashMap<>(); + Map> antibodyRefreshFactors = new HashMap<>(); + configureAntibodies(initialAntibodies, antibodyRefreshFactors, mutEscDelta, mutEscBa1, mutEscBa5, mutEscStrainA, mutEscStrainB); + + AntibodyModel.Config antibodyConfig = new AntibodyModel.Config(initialAntibodies, antibodyRefreshFactors); + + double immuneSigma = 3.0; + if (params != null) { + antibodyConfig.setImmuneReponseSigma(immuneSigma); + } + + bind(AntibodyModel.Config.class).toInstance(antibodyConfig); + + + // BIND VACCINATION MODEL + + Multibinder set = Multibinder.newSetBinder(binder(), VaccinationModel.class); + + set.addBinding().to(VaccinationStrategyBMBF0617.class).in(Singleton.class); + + LocalDate start = LocalDate.parse("2022-12-01"); + + VaccinationType vaccinationType = VaccinationType.mRNA; + + Int2DoubleMap compliance = new Int2DoubleAVLTreeMap(); + compliance.put(60, 0.0); + compliance.put(18, 0.0); + compliance.put(12, 0.0); + compliance.put(0, 0.0); + + String vacCamp = "off"; + + if (params != null) { + + if (!params.vacType.equals("off")) { + vacCamp = "age"; + vaccinationType = VaccinationType.valueOf(params.vacType); + } + +// start = LocalDate.parse(params.resDate); + + + switch (vacCamp) { + case "age": + compliance.put(60, 0.85); // 60+ + compliance.put(18, 0.55); // 18-59 + compliance.put(12, 0.20); // 12-17 + compliance.put(0, 0.0); // 0 - 11 + break; + case "eu": + compliance.put(60, 0.40); // half of 80% (which reflects the current percentage of people in Dland who are boostered) + compliance.put(18, 0.); + compliance.put(12, 0.); + compliance.put(0, 0.); + break; + case "off": + break; + default: + throw new RuntimeException("Not a valid option for vaccinationCampaignType"); + } + } + + bind(VaccinationStrategyBMBF0617.Config.class).toInstance(new VaccinationStrategyBMBF0617.Config(start, 30, vaccinationType, compliance)); + + + + // BIND TESTING MODEL + if (params != null){ + + // date of no restrictions and new restrictions + String unResDate = "2022-04-25"; + LocalDate unresDate = LocalDate.parse(unResDate); + LocalDate resDate = LocalDate.parse(params.resDate); + +// String[] s = params.gpMonths.split("-"); + + // Green pass validity for vaccinated and infected +// int vacDays = Integer.parseInt(s[0]) * 30; +// int infDays = Integer.parseInt(s[1]) * 30; + + // set days gp is valid + int vacDays = 3 * 30; + int infDays = 3 * 30; + + // set testing scheme for each activity type + final CologneBMBF220628_3G.TestScheme leisTest; + final CologneBMBF220628_3G.TestScheme workTest; + final CologneBMBF220628_3G.TestScheme eduTest; + if (params.leis.equals("test") || params.leis.equals("all")) { + leisTest= CologneBMBF220628_3G.TestScheme.gp; + } else if (params.leis.equals("none") || params.leis.equals("mask")) { + leisTest = CologneBMBF220628_3G.TestScheme.none; + } else { + throw new RuntimeException(); + } + + + if (params.work.equals("test") || params.work.equals("all")) { + workTest= CologneBMBF220628_3G.TestScheme.all; + } else if (params.work.equals("none") || params.work.equals("mask")|| params.work.equals("homeOff")) { + workTest = CologneBMBF220628_3G.TestScheme.none; + } else { + throw new RuntimeException(); + } + + if (params.edu.equals("maskVentTest")) { + eduTest = CologneBMBF220628_3G.TestScheme.all; + } else if (params.edu.equals("none")) { + eduTest = CologneBMBF220628_3G.TestScheme.none; + } else {throw new RuntimeException();} + + bind(FlexibleTestingModel.TestRate.class).toInstance((person, day, dow, date, test, vac) -> { + + if (date.isBefore(unresDate)) + return vac.hasValidVaccination(person, day, date); + + // When restrictions have been lifted, days valid is set to a high number + // many tests are voluntary + if (date.isBefore(resDate)) + return vac.hasValidVaccination(person, day, date, 360); + + // this returns true if for any activities, the person requires increased testing regime + BiFunction anyUnVac = (act, red) -> { + + boolean res = false; + CologneBMBF220628_3G.TestScheme scheme = null; + + + if (act.equals("leisure")) + scheme = leisTest; + else if (act.equals("work")) + scheme = workTest; + else if (act.startsWith("edu")) + scheme = eduTest; + + // null and none, will be false + if (scheme == CologneBMBF220628_3G.TestScheme.all) + res = true; + else if (scheme == CologneBMBF220628_3G.TestScheme.gp) { + res = (person.getNumVaccinations() == 0 || person.daysSince(EpisimPerson.VaccinationStatus.yes, day) > vacDays) + && !person.isRecentlyRecovered(day, infDays); + + } + + return red || res; + }; + + return !person.matchActivities(dow, anyUnVac, false); + }); + + bind(FlexibleTestingModel.TestPolicy.class).toInstance(new FlexibleTestingModel.TestPolicy() { + @Override + public boolean shouldTest(EpisimPerson person, int day, DayOfWeek dow, LocalDate date, + TestingConfigGroup test, VaccinationConfigGroup vac) { + + if (date.isBefore(unresDate)) { + + boolean testAllPersons = test.getTestAllPersonsAfter() != null && date.isAfter(test.getTestAllPersonsAfter()); + return testAllPersons || !vac.hasGreenPass(person, day, date); + } + + // after restrictions everybody is tested according to the rates + return true; + } + }); + + } + + } + + private void configureAntibodies(Map> initialAntibodies, + Map> antibodyRefreshFactors, + double mutEscDelta, double mutEscBa1, double mutEscBa5, double mutEscStrainA, double mutEscStrainB) { + for (VaccinationType immunityType : VaccinationType.values()) { + initialAntibodies.put(immunityType, new EnumMap<>( VirusStrain.class ) ); + for (VirusStrain virusStrain : VirusStrain.values()) { + + if (immunityType == VaccinationType.mRNA) { + initialAntibodies.get(immunityType).put(virusStrain, 29.2); //10.0 + } + else if (immunityType == VaccinationType.vector) { + initialAntibodies.get(immunityType).put(virusStrain, 6.8); //2.5 + } + else { + initialAntibodies.get(immunityType).put(virusStrain, 5.0); + } + } + } + + for (VirusStrain immunityType : VirusStrain.values()) { + initialAntibodies.put(immunityType, new EnumMap<>( VirusStrain.class ) ); + for (VirusStrain virusStrain : VirusStrain.values()) { + initialAntibodies.get(immunityType).put(virusStrain, 5.0); + } + } + + //mRNAAlpha, mRNADelta, mRNABA1 comes from Sydney's calibration. + //The other values come from Rössler et al. + + //Wildtype + double mRNAAlpha = 29.2; + initialAntibodies.get(VaccinationType.mRNA).put(VirusStrain.SARS_CoV_2, mRNAAlpha); + initialAntibodies.get(VaccinationType.vector).put(VirusStrain.SARS_CoV_2, mRNAAlpha * 210. / 700.); + initialAntibodies.get(VirusStrain.SARS_CoV_2).put(VirusStrain.SARS_CoV_2, mRNAAlpha * 300. / 700.); + initialAntibodies.get(VirusStrain.ALPHA).put(VirusStrain.SARS_CoV_2, mRNAAlpha * 300. / 700.); + initialAntibodies.get(VirusStrain.DELTA).put(VirusStrain.SARS_CoV_2, mRNAAlpha * 210. / 700.); + initialAntibodies.get(VirusStrain.OMICRON_BA1).put(VirusStrain.SARS_CoV_2, 0.01); + initialAntibodies.get(VirusStrain.OMICRON_BA2).put(VirusStrain.SARS_CoV_2, 0.01); + initialAntibodies.get(VirusStrain.OMICRON_BA5).put(VirusStrain.SARS_CoV_2, 0.01); + initialAntibodies.get(VirusStrain.STRAIN_A).put(VirusStrain.SARS_CoV_2, 0.01); + initialAntibodies.get(VirusStrain.STRAIN_B).put(VirusStrain.SARS_CoV_2, 0.01); + initialAntibodies.get(VaccinationType.ba1Update).put(VirusStrain.SARS_CoV_2, mRNAAlpha / mutEscDelta / mutEscBa1); + initialAntibodies.get(VaccinationType.ba5Update).put(VirusStrain.SARS_CoV_2, mRNAAlpha / mutEscDelta / mutEscBa1 / mutEscBa5); + + //Alpha + initialAntibodies.get(VaccinationType.mRNA).put(VirusStrain.ALPHA, mRNAAlpha); + initialAntibodies.get(VaccinationType.vector).put(VirusStrain.ALPHA, mRNAAlpha * 210. / 700.); + initialAntibodies.get(VirusStrain.SARS_CoV_2).put(VirusStrain.ALPHA, mRNAAlpha * 300. / 700.); + initialAntibodies.get(VirusStrain.ALPHA).put(VirusStrain.ALPHA, mRNAAlpha * 300. / 700.); + initialAntibodies.get(VirusStrain.DELTA).put(VirusStrain.ALPHA, mRNAAlpha * 210. / 700.); + initialAntibodies.get(VirusStrain.OMICRON_BA1).put(VirusStrain.ALPHA, 0.01); + initialAntibodies.get(VirusStrain.OMICRON_BA2).put(VirusStrain.ALPHA, 0.01); + initialAntibodies.get(VirusStrain.OMICRON_BA5).put(VirusStrain.ALPHA, 0.01); + initialAntibodies.get(VirusStrain.STRAIN_A).put(VirusStrain.ALPHA, 0.01); + initialAntibodies.get(VirusStrain.STRAIN_B).put(VirusStrain.ALPHA, 0.01); + initialAntibodies.get(VaccinationType.ba1Update).put(VirusStrain.ALPHA, mRNAAlpha / mutEscDelta / mutEscBa1); + initialAntibodies.get(VaccinationType.ba5Update).put(VirusStrain.ALPHA, mRNAAlpha / mutEscDelta / mutEscBa1 / mutEscBa5); + + //DELTA + double mRNADelta = mRNAAlpha / mutEscDelta; + initialAntibodies.get(VaccinationType.mRNA).put(VirusStrain.DELTA, mRNADelta); + initialAntibodies.get(VaccinationType.vector).put(VirusStrain.DELTA, mRNADelta * 150./300.); + initialAntibodies.get(VirusStrain.SARS_CoV_2).put(VirusStrain.DELTA, mRNADelta * 64./300.); + initialAntibodies.get(VirusStrain.ALPHA).put(VirusStrain.DELTA, mRNADelta * 64./300.); + initialAntibodies.get(VirusStrain.DELTA).put(VirusStrain.DELTA, mRNADelta * 450./300.); + initialAntibodies.get(VirusStrain.OMICRON_BA1).put(VirusStrain.DELTA, 0.01); + initialAntibodies.get(VirusStrain.OMICRON_BA2).put(VirusStrain.DELTA, 0.01); + initialAntibodies.get(VirusStrain.OMICRON_BA5).put(VirusStrain.DELTA, 0.01); + initialAntibodies.get(VirusStrain.STRAIN_A).put(VirusStrain.DELTA, 0.01); + initialAntibodies.get(VirusStrain.STRAIN_B).put(VirusStrain.DELTA, 0.01); + initialAntibodies.get(VaccinationType.ba1Update).put(VirusStrain.DELTA, mRNADelta / mutEscBa1); + initialAntibodies.get(VaccinationType.ba5Update).put(VirusStrain.DELTA, mRNADelta / mutEscBa1 / mutEscBa5); + + //BA.1 + double mRNABA1 = mRNADelta / mutEscBa1; + initialAntibodies.get(VaccinationType.mRNA).put(VirusStrain.OMICRON_BA1, mRNABA1); + initialAntibodies.get(VaccinationType.vector).put(VirusStrain.OMICRON_BA1, mRNABA1 * 4./20.); //??? + initialAntibodies.get(VirusStrain.SARS_CoV_2).put(VirusStrain.OMICRON_BA1, mRNABA1 * 6./20.); + initialAntibodies.get(VirusStrain.ALPHA).put(VirusStrain.OMICRON_BA1, mRNABA1 * 6./20.); + initialAntibodies.get(VirusStrain.DELTA).put(VirusStrain.OMICRON_BA1, mRNABA1 * 8./20.); + initialAntibodies.get(VirusStrain.OMICRON_BA1).put(VirusStrain.OMICRON_BA1, 64.0 / 300.); + initialAntibodies.get(VirusStrain.OMICRON_BA2).put(VirusStrain.OMICRON_BA1, 64.0 / 300. / 1.4); + initialAntibodies.get(VirusStrain.OMICRON_BA5).put(VirusStrain.OMICRON_BA1, 64.0 / 300. / mutEscBa5); //todo: is 1.4 + initialAntibodies.get(VirusStrain.STRAIN_A).put(VirusStrain.OMICRON_BA1, 64.0 / 300. / mutEscBa5 / mutEscStrainA); + initialAntibodies.get(VirusStrain.STRAIN_B).put(VirusStrain.OMICRON_BA1, 64.0 / 300. / mutEscStrainB); + initialAntibodies.get(VaccinationType.ba1Update).put(VirusStrain.OMICRON_BA1, mRNAAlpha); + initialAntibodies.get(VaccinationType.ba5Update).put(VirusStrain.OMICRON_BA1, mRNAAlpha / mutEscBa5); + + //BA.2 + double mRNABA2 = mRNABA1; + initialAntibodies.get(VaccinationType.mRNA).put(VirusStrain.OMICRON_BA2, mRNABA2); + initialAntibodies.get(VaccinationType.vector).put(VirusStrain.OMICRON_BA2, mRNABA2 * 4./20.); + initialAntibodies.get(VirusStrain.SARS_CoV_2).put(VirusStrain.OMICRON_BA2, mRNABA2 * 6./20.); + initialAntibodies.get(VirusStrain.ALPHA).put(VirusStrain.OMICRON_BA2, mRNABA2 * 6./20.); + initialAntibodies.get(VirusStrain.DELTA).put(VirusStrain.OMICRON_BA2, mRNABA2 * 8./20.); + initialAntibodies.get(VirusStrain.OMICRON_BA1).put(VirusStrain.OMICRON_BA2, 64.0 / 300. / 1.4); + initialAntibodies.get(VirusStrain.OMICRON_BA2).put(VirusStrain.OMICRON_BA2, 64.0 / 300.); + initialAntibodies.get(VirusStrain.OMICRON_BA5).put(VirusStrain.OMICRON_BA2, 64.0 / 300. / mutEscBa5); + initialAntibodies.get(VirusStrain.STRAIN_A).put(VirusStrain.OMICRON_BA2, 64.0 / 300. / mutEscBa5 / mutEscStrainA); + initialAntibodies.get(VirusStrain.STRAIN_B).put(VirusStrain.OMICRON_BA2, 64.0 / 300. / mutEscStrainB); + initialAntibodies.get(VaccinationType.ba1Update).put(VirusStrain.OMICRON_BA2, mRNAAlpha); + initialAntibodies.get(VaccinationType.ba5Update).put(VirusStrain.OMICRON_BA2, mRNAAlpha / mutEscBa5); + + + //BA.5 + double mRNABa5 = mRNABA2 / mutEscBa5; + initialAntibodies.get(VaccinationType.mRNA).put(VirusStrain.OMICRON_BA5, mRNABa5); + initialAntibodies.get(VaccinationType.vector).put(VirusStrain.OMICRON_BA5, mRNABa5 * 4./20.); + initialAntibodies.get(VirusStrain.SARS_CoV_2).put(VirusStrain.OMICRON_BA5, mRNABa5 * 6./20.); + initialAntibodies.get(VirusStrain.ALPHA).put(VirusStrain.OMICRON_BA5, mRNABa5 * 6./20.); + initialAntibodies.get(VirusStrain.DELTA).put(VirusStrain.OMICRON_BA5, mRNABa5 * 8./20.); + initialAntibodies.get(VirusStrain.OMICRON_BA1).put(VirusStrain.OMICRON_BA5, 64.0 / 300. / mutEscBa5);// todo: do we need 1.4? + initialAntibodies.get(VirusStrain.OMICRON_BA2).put(VirusStrain.OMICRON_BA5, 64.0 / 300./ mutEscBa5); + initialAntibodies.get(VirusStrain.OMICRON_BA5).put(VirusStrain.OMICRON_BA5, 64.0 / 300.); + initialAntibodies.get(VirusStrain.STRAIN_A).put(VirusStrain.OMICRON_BA5, 64.0 / 300./ mutEscStrainA); //todo ??? + initialAntibodies.get(VirusStrain.STRAIN_B).put(VirusStrain.OMICRON_BA5, 64.0 / 300. / mutEscBa5 / mutEscStrainB); + initialAntibodies.get(VaccinationType.ba1Update).put(VirusStrain.OMICRON_BA5, mRNAAlpha / mutEscBa5); + initialAntibodies.get(VaccinationType.ba5Update).put(VirusStrain.OMICRON_BA5, mRNAAlpha); + + //StrainA + double mRNAStrainA = mRNABa5 / mutEscStrainA; + initialAntibodies.get(VaccinationType.mRNA).put(VirusStrain.STRAIN_A, mRNAStrainA); + initialAntibodies.get(VaccinationType.vector).put(VirusStrain.STRAIN_A, mRNAStrainA * 4./20.); + initialAntibodies.get(VirusStrain.SARS_CoV_2).put(VirusStrain.STRAIN_A, mRNAStrainA * 6./20.); + initialAntibodies.get(VirusStrain.ALPHA).put(VirusStrain.STRAIN_A, mRNAStrainA * 6./20.); + initialAntibodies.get(VirusStrain.DELTA).put(VirusStrain.STRAIN_A, mRNAStrainA * 8./20.); + initialAntibodies.get(VirusStrain.OMICRON_BA1).put(VirusStrain.STRAIN_A, 64.0 / 300. / mutEscBa5 /mutEscStrainA); + initialAntibodies.get(VirusStrain.OMICRON_BA2).put(VirusStrain.STRAIN_A, 64.0 / 300./ mutEscBa5 /mutEscStrainA); + initialAntibodies.get(VirusStrain.OMICRON_BA5).put(VirusStrain.STRAIN_A, 64.0 / 300. / mutEscStrainA); + initialAntibodies.get(VirusStrain.STRAIN_A).put(VirusStrain.STRAIN_A, 64.0 / 300.); + initialAntibodies.get(VirusStrain.STRAIN_B).put(VirusStrain.STRAIN_A, 64.0 / 300. / mutEscStrainA / mutEscStrainB / mutEscBa5); + initialAntibodies.get(VaccinationType.ba1Update).put(VirusStrain.STRAIN_A, mRNAAlpha / mutEscBa5 / mutEscStrainA); + initialAntibodies.get(VaccinationType.ba5Update).put(VirusStrain.STRAIN_A, mRNAAlpha / mutEscStrainA); + + //StrainB + double mRNAStrainB = mRNABA2 / mutEscStrainB; + initialAntibodies.get(VaccinationType.mRNA).put(VirusStrain.STRAIN_B, mRNAStrainB); + initialAntibodies.get(VaccinationType.vector).put(VirusStrain.STRAIN_B, mRNAStrainB * 4./20.); + initialAntibodies.get(VirusStrain.SARS_CoV_2).put(VirusStrain.STRAIN_B, mRNAStrainB * 6./20.); + initialAntibodies.get(VirusStrain.ALPHA).put(VirusStrain.STRAIN_B, mRNAStrainB * 6./20.); + initialAntibodies.get(VirusStrain.DELTA).put(VirusStrain.STRAIN_B, mRNAStrainB * 8./20.); + initialAntibodies.get(VirusStrain.OMICRON_BA1).put(VirusStrain.STRAIN_B, 64.0 / 300. / mutEscStrainB); + initialAntibodies.get(VirusStrain.OMICRON_BA2).put(VirusStrain.STRAIN_B, 64.0 / 300./ mutEscStrainB); + initialAntibodies.get(VirusStrain.OMICRON_BA5).put(VirusStrain.STRAIN_B, 64.0 / 300. / mutEscStrainB / mutEscBa5); + initialAntibodies.get(VirusStrain.STRAIN_A).put(VirusStrain.STRAIN_B, 64.0 / 300./ mutEscStrainA / mutEscStrainB / mutEscBa5); + initialAntibodies.get(VirusStrain.STRAIN_B).put(VirusStrain.STRAIN_B, 64.0 / 300.); + initialAntibodies.get(VaccinationType.ba1Update).put(VirusStrain.STRAIN_B, mRNAAlpha / mutEscStrainB); + initialAntibodies.get(VaccinationType.ba5Update).put(VirusStrain.STRAIN_B, mRNAAlpha / mutEscStrainB / mutEscBa5); + + + for (VaccinationType immunityType : VaccinationType.values()) { + antibodyRefreshFactors.put(immunityType, new EnumMap<>( VirusStrain.class ) ); + for (VirusStrain virusStrain : VirusStrain.values()) { + + if (immunityType == VaccinationType.mRNA) { + antibodyRefreshFactors.get(immunityType).put(virusStrain, 15.0); + } + else if (immunityType == VaccinationType.vector) { + antibodyRefreshFactors.get(immunityType).put(virusStrain, 5.0); + } + else if (immunityType == VaccinationType.ba1Update) { + antibodyRefreshFactors.get(immunityType).put(virusStrain, 15.0); + } + else if (immunityType == VaccinationType.ba5Update) { + antibodyRefreshFactors.get(immunityType).put(virusStrain, 15.0); + } + else { + antibodyRefreshFactors.get(immunityType).put(virusStrain, Double.NaN); + } + + } + } + + for (VirusStrain immunityType : VirusStrain.values()) { + antibodyRefreshFactors.put(immunityType, new EnumMap<>( VirusStrain.class ) ); + for (VirusStrain virusStrain : VirusStrain.values()) { + antibodyRefreshFactors.get(immunityType).put(virusStrain, 15.0); + } + } + + + +// System.out.print("immunityGiver"); +// for (VirusStrain immunityFrom : VirusStrain.values()) { +// if (immunityFrom == VirusStrain.OMICRON_BA1) { +// System.out.print( "," + "BA.1"); +// } else if (immunityFrom == VirusStrain.OMICRON_BA2) { +// System.out.print( "," + "BA.2"); +// } else { +// System.out.print( "," + immunityFrom); +// } +// } +// +// +// for (ImmunityEvent immunityGiver : VaccinationType.values()) { +// System.out.print("\n" + immunityGiver); +// for (VirusStrain immunityFrom : VirusStrain.values()) { +// System.out.print("," + String.format("%.3g", initialAntibodies.get(immunityGiver).get(immunityFrom))); +// } +// } +// for (ImmunityEvent immunityGiver : VirusStrain.values()) { +// System.out.print("\n" + immunityGiver); +// for (VirusStrain immunityFrom : VirusStrain.values()) { +// System.out.print("," + String.format("%.3g", initialAntibodies.get(immunityGiver).get(immunityFrom))); +// } +// } +// +// System.out.println(); + + } + }); + + } + + private SnzCologneProductionScenario getBindings(double pHousehold, Params params) { + return new SnzCologneProductionScenario.Builder() + .setCarnivalModel(SnzCologneProductionScenario.CarnivalModel.yes) + .setSebastianUpdate(true) + .setLeisureCorrection(1.3) //params == null ? 0.0 : params.actCorrection) + .setScaleForActivityLevels(1.3) + .setSuscHouseholds_pct(pHousehold) + .setActivityHandling(EpisimConfigGroup.ActivityHandling.startOfDay) + .setTestingModel(params != null ? FlexibleTestingModel.class : DefaultTestingModel.class) + .setInfectionModel(InfectionModelWithAntibodies.class) + .build(); + } + + @Override + public Metadata getMetadata() { + return Metadata.of("cologne", "calibration"); + } + + @Override + public Collection postProcessing() { + return List.of( + new VaccinationEffectiveness().withArgs(), + new RValuesFromEvents().withArgs(), + new VaccinationEffectivenessFromPotentialInfections().withArgs("--remove-infected"), + new FilterEvents().withArgs("--output","./output/"), + new HospitalNumbersFromEvents().withArgs("--output","./output/","--input","/scratch/projects/bzz0020/episim-input") +// new SecondaryAttackRateFromEvents().withArgs() + ); + } + + @Override + public Config prepareConfig(int id, Params params) { + + if (DEBUG_MODE ) { + if (runCount == 0){ //&& params.strAEsc != 0.0 && params.ba5Inf == 0. && params.eduTest.equals("true")) { + runCount++; + } else { + return null; + } + } + + SnzCologneProductionScenario module = getBindings(0.0, params); + + Config config = module.config(); + + + config.global().setRandomSeed(params.seed); + + EpisimConfigGroup episimConfig = ConfigUtils.addOrGetModule(config, EpisimConfigGroup.class); + + + episimConfig.setCalibrationParameter(episimConfig.getCalibrationParameter() * 1.2); + + + + //--------------------------------------- + // S T R A I N S + //--------------------------------------- + + VirusStrainConfigGroup virusStrainConfigGroup = ConfigUtils.addOrGetModule(config, VirusStrainConfigGroup.class); + + +// virusStrainConfigGroup.getOrAddParams(VirusStrain.DELTA).setInfectiousness(virusStrainConfigGroup.getOrAddParams(VirusStrain.DELTA).getInfectiousness() * params.deltaTheta); + + //BA5 + double ba5Inf = 1.0; + double oHos = virusStrainConfigGroup.getParams(VirusStrain.OMICRON_BA2).getFactorSeriouslySick(); + virusStrainConfigGroup.getOrAddParams(VirusStrain.OMICRON_BA5).setInfectiousness(virusStrainConfigGroup.getParams(VirusStrain.OMICRON_BA2).getInfectiousness() * ba5Inf); + virusStrainConfigGroup.getOrAddParams(VirusStrain.OMICRON_BA5).setFactorSeriouslySick(oHos); + virusStrainConfigGroup.getOrAddParams(VirusStrain.OMICRON_BA5).setFactorSeriouslySickVaccinated(oHos); + virusStrainConfigGroup.getOrAddParams(VirusStrain.OMICRON_BA5).setFactorCritical(oHos); + + +// STRAIN_A + if (!params.StrainA.equals("off")) { + + virusStrainConfigGroup.getOrAddParams(VirusStrain.STRAIN_A).setInfectiousness(virusStrainConfigGroup.getParams(VirusStrain.OMICRON_BA5).getInfectiousness() * ba5Inf); + virusStrainConfigGroup.getOrAddParams(VirusStrain.STRAIN_A).setFactorSeriouslySick(oHos); + virusStrainConfigGroup.getOrAddParams(VirusStrain.STRAIN_A).setFactorSeriouslySickVaccinated(oHos); + virusStrainConfigGroup.getOrAddParams(VirusStrain.STRAIN_A).setFactorCritical(oHos); + } + +// STRAIN_B + if (!params.StrainB.equals("off")) { + + virusStrainConfigGroup.getOrAddParams(VirusStrain.STRAIN_B).setInfectiousness(virusStrainConfigGroup.getParams(VirusStrain.OMICRON_BA5).getInfectiousness() * ba5Inf); + virusStrainConfigGroup.getOrAddParams(VirusStrain.STRAIN_B).setFactorSeriouslySick(oHos); + virusStrainConfigGroup.getOrAddParams(VirusStrain.STRAIN_B).setFactorSeriouslySickVaccinated(oHos); + virusStrainConfigGroup.getOrAddParams(VirusStrain.STRAIN_B).setFactorCritical(oHos); + } + + // remove age-based susceptibility of strains starting with DELTA + + String ageSusc = "false"; + if (!Boolean.parseBoolean(ageSusc)) { + TreeMap nonSteppedAgeSusceptibility = new TreeMap<>(Map.of( + 19, 1d, + 20, 1d + )); + + for (VirusStrain strain : List.of(VirusStrain.DELTA, VirusStrain.OMICRON_BA1, VirusStrain.OMICRON_BA2, VirusStrain.OMICRON_BA5, VirusStrain.STRAIN_A)) { + virusStrainConfigGroup.getOrAddParams(strain).setAgeSusceptibility(nonSteppedAgeSusceptibility); + } + } + + // increase infectivity of alpha + virusStrainConfigGroup.getOrAddParams(VirusStrain.ALPHA).setInfectiousness(virusStrainConfigGroup.getOrAddParams(VirusStrain.ALPHA).getInfectiousness() * 1.4); + + double deltaTheta = 0.9; + virusStrainConfigGroup.getOrAddParams(VirusStrain.DELTA).setInfectiousness(virusStrainConfigGroup.getOrAddParams(VirusStrain.DELTA).getInfectiousness() * deltaTheta); + double ba1Inf = 1.9; // 2.0,2.1,2.2 + double ba2Inf = 1.7; + virusStrainConfigGroup.getOrAddParams(VirusStrain.OMICRON_BA1).setInfectiousness(virusStrainConfigGroup.getOrAddParams(VirusStrain.DELTA).getInfectiousness() * ba1Inf); + virusStrainConfigGroup.getOrAddParams(VirusStrain.OMICRON_BA2).setInfectiousness(virusStrainConfigGroup.getOrAddParams(VirusStrain.DELTA).getInfectiousness() * ba1Inf * ba2Inf); + + + //--------------------------------------- + // I M P O R T + //--------------------------------------- + + Map infPerDayAlpha = new HashMap<>(episimConfig.getInfections_pers_per_day().getOrDefault(VirusStrain.ALPHA, new TreeMap<>())); + + + // reconfig disease import of alpha + LocalDate startDateAlpha = LocalDate.parse("2021-01-15"); + + for (int i = 0; i < 7; i++) { + infPerDayAlpha.put(startDateAlpha.plusDays(i), 4); + } + + + infPerDayAlpha.put(startDateAlpha.plusDays(7), 1); + + episimConfig.setInfections_pers_per_day(VirusStrain.ALPHA, infPerDayAlpha); + + + configureFutureDiseaseImport(params, episimConfig); + + //--------------------------------------- + // R E S T R I C T I O N S + //--------------------------------------- + + FixedPolicy.ConfigBuilder builder = FixedPolicy.parse(episimConfig.getPolicy()); + + //school + String schoolUpdate = "yes"; + if(schoolUpdate.equals("yes")) { + // school closed completely until 21.2.2022 + builder.restrict(LocalDate.parse("2021-01-11"), 0.2, "educ_primary", "educ_kiga", "educ_secondary", "educ_tertiary", "educ_other"); + builder.restrict(LocalDate.parse("2021-02-21"), 0.5, "educ_primary"); + builder.restrict(LocalDate.parse("2021-03-15"), 0.5, "educ_kiga", "educ_secondary", "educ_tertiary", "educ_other"); + + } else if (schoolUpdate.equals("no")) { + + } else { + throw new RuntimeException("param value doesn't exist"); + } + + String schoolTest = "later"; + if (schoolTest.equals("later")) { + TestingConfigGroup testingConfigGroup = ConfigUtils.addOrGetModule(config, TestingConfigGroup.class); + TestingConfigGroup.TestingParams rapidTest = testingConfigGroup.getOrAddParams(TestType.RAPID_TEST); +// TestingConfigGroup.TestingParams pcrTest = testingConfigGroup.getOrAddParams(TestType.PCR); + Map> testingRateForActivitiesRapid = rapidTest.getTestingRateForActivities(); +// Map> testingRateForActivitiesPCR = pcrTest.getTestingRateForActivities(); + + + for (LocalDate date = LocalDate.parse("2021-03-19"); date.isBefore(LocalDate.parse("2021-04-25")); date = date.plusDays(1)) { + + testingRateForActivitiesRapid.get("educ_kiga").put(date, 0.); + testingRateForActivitiesRapid.get("educ_primary").put(date, 0.); + testingRateForActivitiesRapid.get("educ_secondary").put(date, 0.); + testingRateForActivitiesRapid.get("educ_tertiary").put(date, 0.); + testingRateForActivitiesRapid.get("educ_other").put(date, 0.); + + } + + testingRateForActivitiesRapid.get("educ_kiga").put(LocalDate.parse("2021-09-20"), 0.); + testingRateForActivitiesRapid.get("educ_primary").put(LocalDate.parse("2021-09-20"), 0.); + +// testingRateForActivitiesPCR.get("educ_primary").put(LocalDate.parse("2021-05-10"), 0.4); + testingRateForActivitiesRapid.get("educ_secondary").put(LocalDate.parse("2021-05-10"), 0.4); + testingRateForActivitiesRapid.get("educ_tertiary").put(LocalDate.parse("2021-05-10"), 0.4); + testingRateForActivitiesRapid.get("educ_other").put(LocalDate.parse("2021-05-10"), 0.4); + + } else if (schoolTest.equals("base")) { + + }else { + throw new RuntimeException("param value doesn't exist"); + } + + + // masks + //pt: masks + String maskType = "45to45"; + if (maskType.equals("45to45")) { + for (LocalDate date = LocalDate.parse("2020-04-21"); date.isBefore(LocalDate.parse("2021-05-01")); date = date.plusDays(1)) { + builder.restrict(date, Restriction.ofMask(Map.of(FaceMask.CLOTH, 0.45, FaceMask.SURGICAL, 0.45)), "pt", "errands", "shop_daily", "shop_other"); + + } + } else if (maskType.equals("base")) { + + } else { + throw new RuntimeException("param value doesn't exist"); + } + + // ----------------------------------------- + + // new restrictions from IfSG + LocalDate restrictionDate = LocalDate.parse(params.resDate); + + // testing rates + + double gpTestRate = 0.; + + TestingConfigGroup testingConfigGroup = ConfigUtils.addOrGetModule(config, TestingConfigGroup.class); + TestingConfigGroup.TestingParams pcrTest = testingConfigGroup.getOrAddParams(TestType.PCR); + Map> testingRateForActivitiesPcr = pcrTest.getTestingRateForActivities(); + Map> testingRateForActivitiesPcrVac = pcrTest.getTestingRateForActivitiesVaccinated(); + + TestingConfigGroup.TestingParams rapidTest = testingConfigGroup.getOrAddParams(TestType.RAPID_TEST); + Map> testingRateForActivitiesRapid = rapidTest.getTestingRateForActivities(); + Map> testingRateForActivitiesRapidVac = rapidTest.getTestingRateForActivitiesVaccinated(); + + + // school + { + //pcr tests for younger kids + testingRateForActivitiesPcr.get("educ_kiga").put(restrictionDate, 0.4); + testingRateForActivitiesPcr.get("educ_primary").put(restrictionDate, 0.4); + testingRateForActivitiesPcrVac.get("educ_kiga").put(restrictionDate, gpTestRate); + testingRateForActivitiesPcrVac.get("educ_primary").put(restrictionDate, gpTestRate); + + //add rapid tests for older kids + testingRateForActivitiesRapid.get("educ_secondary").put(restrictionDate, 0.6); + testingRateForActivitiesRapid.get("educ_tertiary").put(restrictionDate, 0.6); + testingRateForActivitiesRapid.get("educ_higher").put(restrictionDate, 0.6); + testingRateForActivitiesRapid.get("educ_other").put(restrictionDate, 0.6); + testingRateForActivitiesRapidVac.get("educ_secondary").put(restrictionDate, gpTestRate); + testingRateForActivitiesRapidVac.get("educ_tertiary").put(restrictionDate, gpTestRate); + testingRateForActivitiesRapidVac.get("educ_higher").put(restrictionDate, gpTestRate); + testingRateForActivitiesRapidVac.get("educ_other").put(restrictionDate, gpTestRate); + } + + // work + { + testingRateForActivitiesRapid.get("work").put(restrictionDate, 0.6); + testingRateForActivitiesRapidVac.get("work").put(restrictionDate, gpTestRate); + } + + // leisure: + { + testingRateForActivitiesRapid.get("leisure").put(restrictionDate, 0.45); + testingRateForActivitiesRapidVac.get("leisure").put(restrictionDate, gpTestRate); + } + + //WORK + double homeOfficeFactor = 0.5; + switch (params.work) { + case "none": + break; + case "homeOff": + builder.restrict(restrictionDate, 0.78 * homeOfficeFactor, "work"); // dont include business bc harder to do from home office + builder.applyToRf(restrictionDate.plusDays(1).toString(), restrictionDate.plusDays(1000).toString(), (d, rf) -> rf * homeOfficeFactor, "work"); + break; + case "test": + // handled in getBindings + break; + case "mask": + builder.restrict(restrictionDate, Restriction.ofMask(Map.of(FaceMask.SURGICAL, 0.9)), "work", "business"); + break; + case "all": + builder.restrict(restrictionDate, 0.78 * homeOfficeFactor, "work"); + builder.applyToRf(restrictionDate.plusDays(1).toString(), restrictionDate.plusDays(1000).toString(), (d, rf) -> rf * homeOfficeFactor, "work"); + builder.restrict(restrictionDate, Restriction.ofMask(Map.of(FaceMask.SURGICAL, 0.9)), "work", "business"); + break; + default: + throw new RuntimeException("invalid parameter"); + } + + //LEISURE + switch (params.leis) { + case "none": + break; + case "test": + // handled in getBindings + break; + case "mask": + builder.restrict(restrictionDate, Restriction.ofMask(Map.of(FaceMask.N95, 0.45)), "leisure"); + case "all": + builder.restrict(restrictionDate, Restriction.ofMask(Map.of(FaceMask.N95, 0.45)), "leisure"); + break; + default: + throw new RuntimeException("invalid parameter"); + } + + // shop, errands + switch (params.errands) { + case "none": + break; + case "mask": + builder.restrict(restrictionDate, Restriction.ofMask(Map.of(FaceMask.N95, 0.9)), "shop_daily", "shop_other", "errands"); + + break; + default: + throw new RuntimeException("invalid parameter"); + } + + //SCHOOL + // todo check mask rate + if (params.edu.equals("maskVentTest")) { + builder.restrict(restrictionDate, Restriction.ofCiCorrection(0.5), "educ_primary", "educ_kiga", "educ_secondary", "educ_tertiary", "educ_other", "educ_higher"); + builder.restrict(restrictionDate, Restriction.ofMask(Map.of( + FaceMask.CLOTH, 0.0, + FaceMask.N95, 0.25, + FaceMask.SURGICAL, 0.25)), + "educ_secondary", "educ_higher", "educ_tertiary", "educ_other"); + + } else if (params.edu.equals("none")) { + + } else { + throw new RuntimeException("invalid parameter"); + } + + + // pt + switch (params.pt) { + case "none": + break; + case "mask": + builder.restrict(restrictionDate, Restriction.ofMask(Map.of(FaceMask.N95, 0.9)), "pt"); + break; + default: + throw new RuntimeException("invalid parameter"); + } + + + episimConfig.setPolicy(builder.build()); + + + //--------------------------------------- + // M I S C + //--------------------------------------- + + // vaccination + VaccinationConfigGroup vaccinationConfig = ConfigUtils.addOrGetModule(config, VaccinationConfigGroup.class); + vaccinationConfig.setUseIgA(Boolean.parseBoolean("true")); + vaccinationConfig.setTimePeriodIgA(730.); + + + //modify contact intensity + double workCi = 0.75; + episimConfig.getOrAddContainerParams("work").setContactIntensity(episimConfig.getOrAddContainerParams("work").getContactIntensity() * workCi); + episimConfig.getOrAddContainerParams("business").setContactIntensity(episimConfig.getOrAddContainerParams("business").getContactIntensity() * workCi); + + + double leisureCi = 0.4; + episimConfig.getOrAddContainerParams("leisure").setContactIntensity(episimConfig.getOrAddContainerParams("leisure").getContactIntensity() * leisureCi); + episimConfig.getOrAddContainerParams("visit").setContactIntensity(episimConfig.getOrAddContainerParams("visit").getContactIntensity() * leisureCi); + + + double schoolCi = 0.75; + episimConfig.getOrAddContainerParams("educ_kiga").setContactIntensity(episimConfig.getOrAddContainerParams("educ_kiga").getContactIntensity() * schoolCi); + episimConfig.getOrAddContainerParams("educ_primary").setContactIntensity(episimConfig.getOrAddContainerParams("educ_primary").getContactIntensity() * schoolCi); + episimConfig.getOrAddContainerParams("educ_secondary").setContactIntensity(episimConfig.getOrAddContainerParams("educ_secondary").getContactIntensity() * schoolCi); + episimConfig.getOrAddContainerParams("educ_tertiary").setContactIntensity(episimConfig.getOrAddContainerParams("educ_tertiary").getContactIntensity() * schoolCi); + episimConfig.getOrAddContainerParams("educ_higher").setContactIntensity(episimConfig.getOrAddContainerParams("educ_higher").getContactIntensity() * schoolCi); + episimConfig.getOrAddContainerParams("educ_other").setContactIntensity(episimConfig.getOrAddContainerParams("educ_other").getContactIntensity() * schoolCi); + + + if (DEBUG_MODE) { + UtilsJR.produceDiseaseImportPlot(episimConfig.getInfections_pers_per_day()); + + } + + return config; + } + + private void configureFutureDiseaseImport(Params params, EpisimConfigGroup episimConfig) { + Map infPerDayBa1 = new HashMap<>(episimConfig.getInfections_pers_per_day().getOrDefault(VirusStrain.OMICRON_BA1, new TreeMap<>())); + Map infPerDayBa2 = new HashMap<>(episimConfig.getInfections_pers_per_day().getOrDefault(VirusStrain.OMICRON_BA2, new TreeMap<>())); + Map infPerDayBa5 = new HashMap<>(episimConfig.getInfections_pers_per_day().getOrDefault(VirusStrain.OMICRON_BA5, new TreeMap<>())); + Map infPerDayStrA = new HashMap<>(episimConfig.getInfections_pers_per_day().getOrDefault(VirusStrain.STRAIN_A, new TreeMap<>())); + Map infPerDayStrB = new HashMap<>(episimConfig.getInfections_pers_per_day().getOrDefault(VirusStrain.STRAIN_B, new TreeMap<>())); + + + // add initial impulses for strains + //BA.1 +// LocalDate ba1Date = LocalDate.parse(params.ba1Date); +// for (int i = 0; i < 7; i++) { +// infPerDayBa1.put(ba1Date.plusDays(i), 4); +// } +// infPerDayBa1.put(ba1Date.plusDays(7), 1); + + + //BA.2 + LocalDate ba2Date = LocalDate.parse("2021-12-18"); + for (int i = 0; i < 7; i++) { + infPerDayBa2.put(ba2Date.plusDays(i), 4); + } + infPerDayBa2.put(ba2Date.plusDays(7), 1); + + //BA.5 + LocalDate ba5Date = LocalDate.parse("2022-04-10"); + for (int i = 0; i < 7; i++) { + infPerDayBa5.put(ba5Date.plusDays(i), 4); + } + infPerDayBa5.put(ba5Date.plusDays(7), 1); + + //StrainA + if (!params.StrainA.equals("off")) { + infPerDayStrA.put(LocalDate.parse("2020-01-01"), 0); + LocalDate strADate = LocalDate.parse("2022-11-01"); + for (int i = 0; i < 7; i++) { + infPerDayStrA.put(strADate.plusDays(i), 4); + } + infPerDayStrA.put(strADate.plusDays(7), 1); + } + + //StrainB + if (!params.StrainB.equals("off")) { + infPerDayStrB.put(LocalDate.parse("2020-01-01"), 0); + LocalDate strBDate = LocalDate.parse("2022-11-01"); + for (int i = 0; i < 7; i++) { + infPerDayStrB.put(strBDate.plusDays(i), 4); + } + infPerDayStrB.put(strBDate.plusDays(7), 1); + } + + + // add projected disease import for vacation waves after initial disease import + int facBa2 = 4; + int facBa5 = 4; + int facStrAB = 4; + + LocalDate dateBa2 = LocalDate.parse("2022-01-27"); // local min of disease import + LocalDate dateBa5 = LocalDate.parse("2022-05-01"); // after vaca import + LocalDate dateStrainAB = LocalDate.parse("2022-11-18"); // after vaca import + + + NavigableMap data = DataUtils.readDiseaseImport(SnzCologneProductionScenario.INPUT.resolve("cologneDiseaseImport_Projected.csv")); + LocalDate date = null; + for (Map.Entry entry : data.entrySet()) { + date = entry.getKey(); + double factor = 0.25 * 2352476. / 919936.; //25% sample, data is given for Cologne City so we have to scale it to the whole model +// + double cases = factor * entry.getValue(); + + if (date.isAfter(dateStrainAB) && (!params.StrainA.equals("off") || !params.StrainB.equals("off"))) { + if (!params.StrainA.equals("off") && !params.StrainB.equals("off")) { + infPerDayStrA.put(date, ((int) cases * facStrAB) == 0 ? 1 : (int) (0.5 * cases * facStrAB)); + infPerDayStrB.put(date, ((int) cases * facStrAB) == 0 ? 1 : (int) (0.5 * cases * facStrAB)); + } + else if (!params.StrainA.equals("off")) { + infPerDayStrA.put(date, ((int) cases * facStrAB) == 0 ? 1 : (int) (cases * facStrAB)); + } + else if (!params.StrainB.equals("off")) { + infPerDayStrB.put(date, ((int) cases * facStrAB) == 0 ? 1 : (int) (cases * facStrAB)); + } + else { + throw new RuntimeException(); + } + infPerDayBa5.put(date, 1); + infPerDayBa2.put(date, 1); + } else if (date.isAfter(dateBa5)) { + infPerDayBa5.put(date, ((int) cases * facBa5) == 0 ? 1 : (int) (cases * facBa5)); + infPerDayBa2.put(date, 1); + } else if (date.isAfter(dateBa2)) { + infPerDayBa2.put(date, ((int) cases * facBa2) == 0 ? 1 : (int) (cases * facBa2)); + } + + } + + // save disease import + episimConfig.setInfections_pers_per_day(VirusStrain.OMICRON_BA1, infPerDayBa1); + episimConfig.setInfections_pers_per_day(VirusStrain.OMICRON_BA2, infPerDayBa2); + episimConfig.setInfections_pers_per_day(VirusStrain.OMICRON_BA5, infPerDayBa5); + + if (!params.StrainA.equals("off")) { + episimConfig.setInfections_pers_per_day(VirusStrain.STRAIN_A, infPerDayStrA); + } + if (!params.StrainB.equals("off")) { + episimConfig.setInfections_pers_per_day(VirusStrain.STRAIN_B, infPerDayStrB); + } + } + + public static final class Params { + // general + @GenerateSeeds(5) + public long seed; + + @StringParameter({"off","3.0","6.0"}) + public String StrainA; + + @StringParameter({"off", "3.0", "6.0"}) + public String StrainB; + + @StringParameter({"2022-10-01"}) + public String resDate; + + // @StringParameter({"false", "true"}) +// public String igA; + // vaccination campaign + @StringParameter({"ba1Update","ba5Update", "mRNA", "off"}) + public String vacType; + + + //measures in the work context: + // homeOff = 50% home office = work Rf cut in half + // +// @StringParameter({"none", "homeOff", "test", "mask", "all"}) + @StringParameter({"none"}) + public String work; + +// @StringParameter({"none", "mask", "test", "all"}) + @StringParameter({"none"}) + public String leis; + + // mask restrictions for "shop_daily", "shop_other", "errands" +// @StringParameter({"none", "mask"}) + @StringParameter({"none"}) + public String errands; + +// @StringParameter({"none", "maskVentTest"}) + @StringParameter({"none"}) + public String edu; + +// @StringParameter({"mask"}) + @StringParameter({"none"}) + public String pt; + } + + + public static void main(String[] args) { + String[] args2 = { + RunParallel.OPTION_SETUP, CologneBMBF20220805_IfSG.class.getName(), + RunParallel.OPTION_PARAMS, Params.class.getName(), + RunParallel.OPTION_TASKS, Integer.toString(1), + RunParallel.OPTION_ITERATIONS, Integer.toString(1000), + RunParallel.OPTION_METADATA + }; + + RunParallel.main(args2); + } + + +} + diff --git a/src/main/java/org/matsim/run/batch/CologneBMBF20220805_leisSplit.java b/src/main/java/org/matsim/run/batch/CologneBMBF20220805_leisSplit.java new file mode 100644 index 000000000..b3d96ffbf --- /dev/null +++ b/src/main/java/org/matsim/run/batch/CologneBMBF20220805_leisSplit.java @@ -0,0 +1,1008 @@ +package org.matsim.run.batch; + +import com.google.inject.AbstractModule; +import com.google.inject.Module; +import com.google.inject.Singleton; +import com.google.inject.multibindings.Multibinder; +import com.google.inject.util.Modules; +import it.unimi.dsi.fastutil.ints.Int2DoubleAVLTreeMap; +import it.unimi.dsi.fastutil.ints.Int2DoubleMap; +import org.matsim.core.config.Config; +import org.matsim.core.config.ConfigUtils; +import org.matsim.episim.*; +import org.matsim.episim.analysis.*; +import org.matsim.episim.model.*; +import org.matsim.episim.model.testing.DefaultTestingModel; +import org.matsim.episim.model.testing.FlexibleTestingModel; +import org.matsim.episim.model.testing.TestType; +import org.matsim.episim.model.vaccination.VaccinationModel; +import org.matsim.episim.model.vaccination.VaccinationStrategyBMBF0617; +import org.matsim.episim.policy.FixedPolicy; +import org.matsim.episim.policy.Restriction; +import org.matsim.run.RunParallel; +import org.matsim.run.modules.SnzCologneProductionScenario; + +import javax.annotation.Nullable; +import java.time.DayOfWeek; +import java.time.LocalDate; +import java.util.*; +import java.util.function.BiFunction; + + +/** + * Batch for Bmbf runs + */ +public class CologneBMBF20220805_leisSplit implements BatchRun { + + boolean DEBUG_MODE = false; + int runCount = 0; + + @Nullable + @Override + public Module getBindings(int id, @Nullable Params params) { + return Modules.override(getBindings(0.0, params)).with(new AbstractModule() { + @Override + protected void configure() { + + // BIND ANTIBODY MODEL + double mutEscDelta = 29.2 / 10.9; + double mutEscBa1 = 10.9 / 1.9; + double mutEscBa5 = 2.9; + + double mutEscStrainA = 0.; + double mutEscStrainB = 0.; + + if (params != null && !params.StrainA.equals("off")) { + mutEscStrainA = Double.parseDouble(params.StrainA); + } + if (params != null && !params.StrainB.equals("off")) { + mutEscStrainB = Double.parseDouble(params.StrainB); + } + + Map> initialAntibodies = new HashMap<>(); + Map> antibodyRefreshFactors = new HashMap<>(); + configureAntibodies(initialAntibodies, antibodyRefreshFactors, mutEscDelta, mutEscBa1, mutEscBa5, mutEscStrainA, mutEscStrainB); + + AntibodyModel.Config antibodyConfig = new AntibodyModel.Config(initialAntibodies, antibodyRefreshFactors); + + double immuneSigma = 3.0; + if (params != null) { + antibodyConfig.setImmuneReponseSigma(immuneSigma); + } + + bind(AntibodyModel.Config.class).toInstance(antibodyConfig); + + + // BIND VACCINATION MODEL + + Multibinder set = Multibinder.newSetBinder(binder(), VaccinationModel.class); + + set.addBinding().to(VaccinationStrategyBMBF0617.class).in(Singleton.class); + + LocalDate start = LocalDate.parse("2022-12-01"); + + VaccinationType vaccinationType = VaccinationType.mRNA; + + Int2DoubleMap compliance = new Int2DoubleAVLTreeMap(); + compliance.put(60, 0.0); + compliance.put(18, 0.0); + compliance.put(12, 0.0); + compliance.put(0, 0.0); + + String vacCamp = "off"; + + if (params != null) { + + if (!params.vacType.equals("off")) { + vacCamp = "age"; + vaccinationType = VaccinationType.valueOf(params.vacType); + } + +// start = LocalDate.parse(params.resDate); + + + switch (vacCamp) { + case "age": + compliance.put(60, 0.85); // 60+ + compliance.put(18, 0.55); // 18-59 + compliance.put(12, 0.20); // 12-17 + compliance.put(0, 0.0); // 0 - 11 + break; + case "eu": + compliance.put(60, 0.40); // half of 80% (which reflects the current percentage of people in Dland who are boostered) + compliance.put(18, 0.); + compliance.put(12, 0.); + compliance.put(0, 0.); + break; + case "off": + break; + default: + throw new RuntimeException("Not a valid option for vaccinationCampaignType"); + } + } + + bind(VaccinationStrategyBMBF0617.Config.class).toInstance(new VaccinationStrategyBMBF0617.Config(start, 30, vaccinationType, compliance)); + + + + // BIND TESTING MODEL + if (params != null){ + + // date of no restrictions and new restrictions + String unResDate = "2022-04-25"; + LocalDate unresDate = LocalDate.parse(unResDate); + LocalDate resDate = LocalDate.parse(params.resDate); + +// String[] s = params.gpMonths.split("-"); + + // Green pass validity for vaccinated and infected +// int vacDays = Integer.parseInt(s[0]) * 30; +// int infDays = Integer.parseInt(s[1]) * 30; + + // set days gp is valid + int vacDays = 3 * 30; + int infDays = 3 * 30; + + // set testing scheme for each activity type + final CologneBMBF220628_3G.TestScheme leisTest; + final CologneBMBF220628_3G.TestScheme workTest; + final CologneBMBF220628_3G.TestScheme eduTest; + if (params.leis.equals("test") || params.leis.equals("all")) { + leisTest= CologneBMBF220628_3G.TestScheme.gp; + } else if (params.leis.equals("none") || params.leis.equals("mask")) { + leisTest = CologneBMBF220628_3G.TestScheme.none; + } else { + throw new RuntimeException(); + } + + + if (params.work.equals("test") || params.work.equals("all")) { + workTest= CologneBMBF220628_3G.TestScheme.all; + } else if (params.work.equals("none") || params.work.equals("mask")|| params.work.equals("homeOff")) { + workTest = CologneBMBF220628_3G.TestScheme.none; + } else { + throw new RuntimeException(); + } + + if (params.edu.equals("maskVentTest")) { + eduTest = CologneBMBF220628_3G.TestScheme.all; + } else if (params.edu.equals("none")) { + eduTest = CologneBMBF220628_3G.TestScheme.none; + } else {throw new RuntimeException();} + + bind(FlexibleTestingModel.TestRate.class).toInstance((person, day, dow, date, test, vac) -> { + + if (date.isBefore(unresDate)) + return vac.hasValidVaccination(person, day, date); + + // When restrictions have been lifted, days valid is set to a high number + // many tests are voluntary + if (date.isBefore(resDate)) + return vac.hasValidVaccination(person, day, date, 360); + + // this returns true if for any activities, the person requires increased testing regime + BiFunction anyUnVac = (act, red) -> { + + boolean res = false; + CologneBMBF220628_3G.TestScheme scheme = null; + + + if (act.startsWith("leis")) + scheme = leisTest; + else if (act.equals("work")) + scheme = workTest; + else if (act.startsWith("edu")) + scheme = eduTest; + + // null and none, will be false + if (scheme == CologneBMBF220628_3G.TestScheme.all) + res = true; + else if (scheme == CologneBMBF220628_3G.TestScheme.gp) { + res = (person.getNumVaccinations() == 0 || person.daysSince(EpisimPerson.VaccinationStatus.yes, day) > vacDays) + && !person.isRecentlyRecovered(day, infDays); + + } + + return red || res; + }; + + return !person.matchActivities(dow, anyUnVac, false); + }); + + bind(FlexibleTestingModel.TestPolicy.class).toInstance(new FlexibleTestingModel.TestPolicy() { + @Override + public boolean shouldTest(EpisimPerson person, int day, DayOfWeek dow, LocalDate date, + TestingConfigGroup test, VaccinationConfigGroup vac) { + + if (date.isBefore(unresDate)) { + + boolean testAllPersons = test.getTestAllPersonsAfter() != null && date.isAfter(test.getTestAllPersonsAfter()); + return testAllPersons || !vac.hasGreenPass(person, day, date); + } + + // after restrictions everybody is tested according to the rates + return true; + } + }); + + } + + } + + private void configureAntibodies(Map> initialAntibodies, + Map> antibodyRefreshFactors, + double mutEscDelta, double mutEscBa1, double mutEscBa5, double mutEscStrainA, double mutEscStrainB) { + for (VaccinationType immunityType : VaccinationType.values()) { + initialAntibodies.put(immunityType, new EnumMap<>( VirusStrain.class ) ); + for (VirusStrain virusStrain : VirusStrain.values()) { + + if (immunityType == VaccinationType.mRNA) { + initialAntibodies.get(immunityType).put(virusStrain, 29.2); //10.0 + } + else if (immunityType == VaccinationType.vector) { + initialAntibodies.get(immunityType).put(virusStrain, 6.8); //2.5 + } + else { + initialAntibodies.get(immunityType).put(virusStrain, 5.0); + } + } + } + + for (VirusStrain immunityType : VirusStrain.values()) { + initialAntibodies.put(immunityType, new EnumMap<>( VirusStrain.class ) ); + for (VirusStrain virusStrain : VirusStrain.values()) { + initialAntibodies.get(immunityType).put(virusStrain, 5.0); + } + } + + //mRNAAlpha, mRNADelta, mRNABA1 comes from Sydney's calibration. + //The other values come from Rössler et al. + + //Wildtype + double mRNAAlpha = 29.2; + initialAntibodies.get(VaccinationType.mRNA).put(VirusStrain.SARS_CoV_2, mRNAAlpha); + initialAntibodies.get(VaccinationType.vector).put(VirusStrain.SARS_CoV_2, mRNAAlpha * 210. / 700.); + initialAntibodies.get(VirusStrain.SARS_CoV_2).put(VirusStrain.SARS_CoV_2, mRNAAlpha * 300. / 700.); + initialAntibodies.get(VirusStrain.ALPHA).put(VirusStrain.SARS_CoV_2, mRNAAlpha * 300. / 700.); + initialAntibodies.get(VirusStrain.DELTA).put(VirusStrain.SARS_CoV_2, mRNAAlpha * 210. / 700.); + initialAntibodies.get(VirusStrain.OMICRON_BA1).put(VirusStrain.SARS_CoV_2, 0.01); + initialAntibodies.get(VirusStrain.OMICRON_BA2).put(VirusStrain.SARS_CoV_2, 0.01); + initialAntibodies.get(VirusStrain.OMICRON_BA5).put(VirusStrain.SARS_CoV_2, 0.01); + initialAntibodies.get(VirusStrain.STRAIN_A).put(VirusStrain.SARS_CoV_2, 0.01); + initialAntibodies.get(VirusStrain.STRAIN_B).put(VirusStrain.SARS_CoV_2, 0.01); + initialAntibodies.get(VaccinationType.ba1Update).put(VirusStrain.SARS_CoV_2, mRNAAlpha / mutEscDelta / mutEscBa1); + initialAntibodies.get(VaccinationType.ba5Update).put(VirusStrain.SARS_CoV_2, mRNAAlpha / mutEscDelta / mutEscBa1 / mutEscBa5); + + //Alpha + initialAntibodies.get(VaccinationType.mRNA).put(VirusStrain.ALPHA, mRNAAlpha); + initialAntibodies.get(VaccinationType.vector).put(VirusStrain.ALPHA, mRNAAlpha * 210. / 700.); + initialAntibodies.get(VirusStrain.SARS_CoV_2).put(VirusStrain.ALPHA, mRNAAlpha * 300. / 700.); + initialAntibodies.get(VirusStrain.ALPHA).put(VirusStrain.ALPHA, mRNAAlpha * 300. / 700.); + initialAntibodies.get(VirusStrain.DELTA).put(VirusStrain.ALPHA, mRNAAlpha * 210. / 700.); + initialAntibodies.get(VirusStrain.OMICRON_BA1).put(VirusStrain.ALPHA, 0.01); + initialAntibodies.get(VirusStrain.OMICRON_BA2).put(VirusStrain.ALPHA, 0.01); + initialAntibodies.get(VirusStrain.OMICRON_BA5).put(VirusStrain.ALPHA, 0.01); + initialAntibodies.get(VirusStrain.STRAIN_A).put(VirusStrain.ALPHA, 0.01); + initialAntibodies.get(VirusStrain.STRAIN_B).put(VirusStrain.ALPHA, 0.01); + initialAntibodies.get(VaccinationType.ba1Update).put(VirusStrain.ALPHA, mRNAAlpha / mutEscDelta / mutEscBa1); + initialAntibodies.get(VaccinationType.ba5Update).put(VirusStrain.ALPHA, mRNAAlpha / mutEscDelta / mutEscBa1 / mutEscBa5); + + //DELTA + double mRNADelta = mRNAAlpha / mutEscDelta; + initialAntibodies.get(VaccinationType.mRNA).put(VirusStrain.DELTA, mRNADelta); + initialAntibodies.get(VaccinationType.vector).put(VirusStrain.DELTA, mRNADelta * 150./300.); + initialAntibodies.get(VirusStrain.SARS_CoV_2).put(VirusStrain.DELTA, mRNADelta * 64./300.); + initialAntibodies.get(VirusStrain.ALPHA).put(VirusStrain.DELTA, mRNADelta * 64./300.); + initialAntibodies.get(VirusStrain.DELTA).put(VirusStrain.DELTA, mRNADelta * 450./300.); + initialAntibodies.get(VirusStrain.OMICRON_BA1).put(VirusStrain.DELTA, 0.01); + initialAntibodies.get(VirusStrain.OMICRON_BA2).put(VirusStrain.DELTA, 0.01); + initialAntibodies.get(VirusStrain.OMICRON_BA5).put(VirusStrain.DELTA, 0.01); + initialAntibodies.get(VirusStrain.STRAIN_A).put(VirusStrain.DELTA, 0.01); + initialAntibodies.get(VirusStrain.STRAIN_B).put(VirusStrain.DELTA, 0.01); + initialAntibodies.get(VaccinationType.ba1Update).put(VirusStrain.DELTA, mRNADelta / mutEscBa1); + initialAntibodies.get(VaccinationType.ba5Update).put(VirusStrain.DELTA, mRNADelta / mutEscBa1 / mutEscBa5); + + //BA.1 + double mRNABA1 = mRNADelta / mutEscBa1; + initialAntibodies.get(VaccinationType.mRNA).put(VirusStrain.OMICRON_BA1, mRNABA1); + initialAntibodies.get(VaccinationType.vector).put(VirusStrain.OMICRON_BA1, mRNABA1 * 4./20.); //??? + initialAntibodies.get(VirusStrain.SARS_CoV_2).put(VirusStrain.OMICRON_BA1, mRNABA1 * 6./20.); + initialAntibodies.get(VirusStrain.ALPHA).put(VirusStrain.OMICRON_BA1, mRNABA1 * 6./20.); + initialAntibodies.get(VirusStrain.DELTA).put(VirusStrain.OMICRON_BA1, mRNABA1 * 8./20.); + initialAntibodies.get(VirusStrain.OMICRON_BA1).put(VirusStrain.OMICRON_BA1, 64.0 / 300.); + initialAntibodies.get(VirusStrain.OMICRON_BA2).put(VirusStrain.OMICRON_BA1, 64.0 / 300. / 1.4); + initialAntibodies.get(VirusStrain.OMICRON_BA5).put(VirusStrain.OMICRON_BA1, 64.0 / 300. / mutEscBa5); //todo: is 1.4 + initialAntibodies.get(VirusStrain.STRAIN_A).put(VirusStrain.OMICRON_BA1, 64.0 / 300. / mutEscBa5 / mutEscStrainA); + initialAntibodies.get(VirusStrain.STRAIN_B).put(VirusStrain.OMICRON_BA1, 64.0 / 300. / mutEscStrainB); + initialAntibodies.get(VaccinationType.ba1Update).put(VirusStrain.OMICRON_BA1, mRNAAlpha); + initialAntibodies.get(VaccinationType.ba5Update).put(VirusStrain.OMICRON_BA1, mRNAAlpha / mutEscBa5); + + //BA.2 + double mRNABA2 = mRNABA1; + initialAntibodies.get(VaccinationType.mRNA).put(VirusStrain.OMICRON_BA2, mRNABA2); + initialAntibodies.get(VaccinationType.vector).put(VirusStrain.OMICRON_BA2, mRNABA2 * 4./20.); + initialAntibodies.get(VirusStrain.SARS_CoV_2).put(VirusStrain.OMICRON_BA2, mRNABA2 * 6./20.); + initialAntibodies.get(VirusStrain.ALPHA).put(VirusStrain.OMICRON_BA2, mRNABA2 * 6./20.); + initialAntibodies.get(VirusStrain.DELTA).put(VirusStrain.OMICRON_BA2, mRNABA2 * 8./20.); + initialAntibodies.get(VirusStrain.OMICRON_BA1).put(VirusStrain.OMICRON_BA2, 64.0 / 300. / 1.4); + initialAntibodies.get(VirusStrain.OMICRON_BA2).put(VirusStrain.OMICRON_BA2, 64.0 / 300.); + initialAntibodies.get(VirusStrain.OMICRON_BA5).put(VirusStrain.OMICRON_BA2, 64.0 / 300. / mutEscBa5); + initialAntibodies.get(VirusStrain.STRAIN_A).put(VirusStrain.OMICRON_BA2, 64.0 / 300. / mutEscBa5 / mutEscStrainA); + initialAntibodies.get(VirusStrain.STRAIN_B).put(VirusStrain.OMICRON_BA2, 64.0 / 300. / mutEscStrainB); + initialAntibodies.get(VaccinationType.ba1Update).put(VirusStrain.OMICRON_BA2, mRNAAlpha); + initialAntibodies.get(VaccinationType.ba5Update).put(VirusStrain.OMICRON_BA2, mRNAAlpha / mutEscBa5); + + + //BA.5 + double mRNABa5 = mRNABA2 / mutEscBa5; + initialAntibodies.get(VaccinationType.mRNA).put(VirusStrain.OMICRON_BA5, mRNABa5); + initialAntibodies.get(VaccinationType.vector).put(VirusStrain.OMICRON_BA5, mRNABa5 * 4./20.); + initialAntibodies.get(VirusStrain.SARS_CoV_2).put(VirusStrain.OMICRON_BA5, mRNABa5 * 6./20.); + initialAntibodies.get(VirusStrain.ALPHA).put(VirusStrain.OMICRON_BA5, mRNABa5 * 6./20.); + initialAntibodies.get(VirusStrain.DELTA).put(VirusStrain.OMICRON_BA5, mRNABa5 * 8./20.); + initialAntibodies.get(VirusStrain.OMICRON_BA1).put(VirusStrain.OMICRON_BA5, 64.0 / 300. / mutEscBa5);// todo: do we need 1.4? + initialAntibodies.get(VirusStrain.OMICRON_BA2).put(VirusStrain.OMICRON_BA5, 64.0 / 300./ mutEscBa5); + initialAntibodies.get(VirusStrain.OMICRON_BA5).put(VirusStrain.OMICRON_BA5, 64.0 / 300.); + initialAntibodies.get(VirusStrain.STRAIN_A).put(VirusStrain.OMICRON_BA5, 64.0 / 300./ mutEscStrainA); //todo ??? + initialAntibodies.get(VirusStrain.STRAIN_B).put(VirusStrain.OMICRON_BA5, 64.0 / 300. / mutEscBa5 / mutEscStrainB); + initialAntibodies.get(VaccinationType.ba1Update).put(VirusStrain.OMICRON_BA5, mRNAAlpha / mutEscBa5); + initialAntibodies.get(VaccinationType.ba5Update).put(VirusStrain.OMICRON_BA5, mRNAAlpha); + + //StrainA + double mRNAStrainA = mRNABa5 / mutEscStrainA; + initialAntibodies.get(VaccinationType.mRNA).put(VirusStrain.STRAIN_A, mRNAStrainA); + initialAntibodies.get(VaccinationType.vector).put(VirusStrain.STRAIN_A, mRNAStrainA * 4./20.); + initialAntibodies.get(VirusStrain.SARS_CoV_2).put(VirusStrain.STRAIN_A, mRNAStrainA * 6./20.); + initialAntibodies.get(VirusStrain.ALPHA).put(VirusStrain.STRAIN_A, mRNAStrainA * 6./20.); + initialAntibodies.get(VirusStrain.DELTA).put(VirusStrain.STRAIN_A, mRNAStrainA * 8./20.); + initialAntibodies.get(VirusStrain.OMICRON_BA1).put(VirusStrain.STRAIN_A, 64.0 / 300. / mutEscBa5 /mutEscStrainA); + initialAntibodies.get(VirusStrain.OMICRON_BA2).put(VirusStrain.STRAIN_A, 64.0 / 300./ mutEscBa5 /mutEscStrainA); + initialAntibodies.get(VirusStrain.OMICRON_BA5).put(VirusStrain.STRAIN_A, 64.0 / 300. / mutEscStrainA); + initialAntibodies.get(VirusStrain.STRAIN_A).put(VirusStrain.STRAIN_A, 64.0 / 300.); + initialAntibodies.get(VirusStrain.STRAIN_B).put(VirusStrain.STRAIN_A, 64.0 / 300. / mutEscStrainA / mutEscStrainB / mutEscBa5); + initialAntibodies.get(VaccinationType.ba1Update).put(VirusStrain.STRAIN_A, mRNAAlpha / mutEscBa5 / mutEscStrainA); + initialAntibodies.get(VaccinationType.ba5Update).put(VirusStrain.STRAIN_A, mRNAAlpha / mutEscStrainA); + + //StrainB + double mRNAStrainB = mRNABA2 / mutEscStrainB; + initialAntibodies.get(VaccinationType.mRNA).put(VirusStrain.STRAIN_B, mRNAStrainB); + initialAntibodies.get(VaccinationType.vector).put(VirusStrain.STRAIN_B, mRNAStrainB * 4./20.); + initialAntibodies.get(VirusStrain.SARS_CoV_2).put(VirusStrain.STRAIN_B, mRNAStrainB * 6./20.); + initialAntibodies.get(VirusStrain.ALPHA).put(VirusStrain.STRAIN_B, mRNAStrainB * 6./20.); + initialAntibodies.get(VirusStrain.DELTA).put(VirusStrain.STRAIN_B, mRNAStrainB * 8./20.); + initialAntibodies.get(VirusStrain.OMICRON_BA1).put(VirusStrain.STRAIN_B, 64.0 / 300. / mutEscStrainB); + initialAntibodies.get(VirusStrain.OMICRON_BA2).put(VirusStrain.STRAIN_B, 64.0 / 300./ mutEscStrainB); + initialAntibodies.get(VirusStrain.OMICRON_BA5).put(VirusStrain.STRAIN_B, 64.0 / 300. / mutEscStrainB / mutEscBa5); + initialAntibodies.get(VirusStrain.STRAIN_A).put(VirusStrain.STRAIN_B, 64.0 / 300./ mutEscStrainA / mutEscStrainB / mutEscBa5); + initialAntibodies.get(VirusStrain.STRAIN_B).put(VirusStrain.STRAIN_B, 64.0 / 300.); + initialAntibodies.get(VaccinationType.ba1Update).put(VirusStrain.STRAIN_B, mRNAAlpha / mutEscStrainB); + initialAntibodies.get(VaccinationType.ba5Update).put(VirusStrain.STRAIN_B, mRNAAlpha / mutEscStrainB / mutEscBa5); + + + for (VaccinationType immunityType : VaccinationType.values()) { + antibodyRefreshFactors.put(immunityType, new EnumMap<>( VirusStrain.class ) ); + for (VirusStrain virusStrain : VirusStrain.values()) { + + if (immunityType == VaccinationType.mRNA) { + antibodyRefreshFactors.get(immunityType).put(virusStrain, 15.0); + } + else if (immunityType == VaccinationType.vector) { + antibodyRefreshFactors.get(immunityType).put(virusStrain, 5.0); + } + else if (immunityType == VaccinationType.ba1Update) { + antibodyRefreshFactors.get(immunityType).put(virusStrain, 15.0); + } + else if (immunityType == VaccinationType.ba5Update) { + antibodyRefreshFactors.get(immunityType).put(virusStrain, 15.0); + } + else { + antibodyRefreshFactors.get(immunityType).put(virusStrain, Double.NaN); + } + + } + } + + for (VirusStrain immunityType : VirusStrain.values()) { + antibodyRefreshFactors.put(immunityType, new EnumMap<>( VirusStrain.class ) ); + for (VirusStrain virusStrain : VirusStrain.values()) { + antibodyRefreshFactors.get(immunityType).put(virusStrain, 15.0); + } + } + + + +// System.out.print("immunityGiver"); +// for (VirusStrain immunityFrom : VirusStrain.values()) { +// if (immunityFrom == VirusStrain.OMICRON_BA1) { +// System.out.print( "," + "BA.1"); +// } else if (immunityFrom == VirusStrain.OMICRON_BA2) { +// System.out.print( "," + "BA.2"); +// } else { +// System.out.print( "," + immunityFrom); +// } +// } +// +// +// for (ImmunityEvent immunityGiver : VaccinationType.values()) { +// System.out.print("\n" + immunityGiver); +// for (VirusStrain immunityFrom : VirusStrain.values()) { +// System.out.print("," + String.format("%.3g", initialAntibodies.get(immunityGiver).get(immunityFrom))); +// } +// } +// for (ImmunityEvent immunityGiver : VirusStrain.values()) { +// System.out.print("\n" + immunityGiver); +// for (VirusStrain immunityFrom : VirusStrain.values()) { +// System.out.print("," + String.format("%.3g", initialAntibodies.get(immunityGiver).get(immunityFrom))); +// } +// } +// +// System.out.println(); + + } + }); + + } + + private SnzCologneProductionScenario getBindings(double pHousehold, Params params) { + return new SnzCologneProductionScenario.Builder() + .setCarnivalModel(SnzCologneProductionScenario.CarnivalModel.yes) + .setSebastianUpdate(true) + .setLeisureCorrection(1.3) //params == null ? 0.0 : params.actCorrection) + .setScaleForActivityLevels(1.3) + .setSuscHouseholds_pct(pHousehold) + .setActivityHandling(EpisimConfigGroup.ActivityHandling.startOfDay) + .setTestingModel(params != null ? FlexibleTestingModel.class : DefaultTestingModel.class) + .setInfectionModel(InfectionModelWithAntibodies.class) + .build(); + } + + @Override + public Metadata getMetadata() { + return Metadata.of("cologne", "calibration"); + } + + @Override + public Collection postProcessing() { + return List.of( + new VaccinationEffectiveness().withArgs(), + new RValuesFromEvents().withArgs(), + new VaccinationEffectivenessFromPotentialInfections().withArgs("--remove-infected"), + new FilterEvents().withArgs("--output","./output/"), + new HospitalNumbersFromEvents().withArgs("--output","./output/","--input","/scratch/projects/bzz0020/episim-input") +// new SecondaryAttackRateFromEvents().withArgs() + ); + } + + @Override + public Config prepareConfig(int id, Params params) { + + if (DEBUG_MODE) { + if (runCount == 0){ //&& params.strAEsc != 0.0 && params.ba5Inf == 0. && params.eduTest.equals("true")) { + runCount++; + } else { + return null; + } + } + + SnzCologneProductionScenario module = getBindings(0.0, params); + + Config config = module.config(); + + + config.global().setRandomSeed(params.seed); + + EpisimConfigGroup episimConfig = ConfigUtils.addOrGetModule(config, EpisimConfigGroup.class); + + + episimConfig.setCalibrationParameter(episimConfig.getCalibrationParameter() * 1.2); + + + + //--------------------------------------- + // S T R A I N S + //--------------------------------------- + + VirusStrainConfigGroup virusStrainConfigGroup = ConfigUtils.addOrGetModule(config, VirusStrainConfigGroup.class); + + +// virusStrainConfigGroup.getOrAddParams(VirusStrain.DELTA).setInfectiousness(virusStrainConfigGroup.getOrAddParams(VirusStrain.DELTA).getInfectiousness() * params.deltaTheta); + + //BA5 + double ba5Inf = 1.0; + double oHos = virusStrainConfigGroup.getParams(VirusStrain.OMICRON_BA2).getFactorSeriouslySick(); + virusStrainConfigGroup.getOrAddParams(VirusStrain.OMICRON_BA5).setInfectiousness(virusStrainConfigGroup.getParams(VirusStrain.OMICRON_BA2).getInfectiousness() * ba5Inf); + virusStrainConfigGroup.getOrAddParams(VirusStrain.OMICRON_BA5).setFactorSeriouslySick(oHos); + virusStrainConfigGroup.getOrAddParams(VirusStrain.OMICRON_BA5).setFactorSeriouslySickVaccinated(oHos); + virusStrainConfigGroup.getOrAddParams(VirusStrain.OMICRON_BA5).setFactorCritical(oHos); + + +// STRAIN_A + if (!params.StrainA.equals("off")) { + + virusStrainConfigGroup.getOrAddParams(VirusStrain.STRAIN_A).setInfectiousness(virusStrainConfigGroup.getParams(VirusStrain.OMICRON_BA5).getInfectiousness() * ba5Inf); + virusStrainConfigGroup.getOrAddParams(VirusStrain.STRAIN_A).setFactorSeriouslySick(oHos); + virusStrainConfigGroup.getOrAddParams(VirusStrain.STRAIN_A).setFactorSeriouslySickVaccinated(oHos); + virusStrainConfigGroup.getOrAddParams(VirusStrain.STRAIN_A).setFactorCritical(oHos); + } + +// STRAIN_B + if (!params.StrainB.equals("off")) { + + virusStrainConfigGroup.getOrAddParams(VirusStrain.STRAIN_B).setInfectiousness(virusStrainConfigGroup.getParams(VirusStrain.OMICRON_BA5).getInfectiousness() * ba5Inf); + virusStrainConfigGroup.getOrAddParams(VirusStrain.STRAIN_B).setFactorSeriouslySick(oHos); + virusStrainConfigGroup.getOrAddParams(VirusStrain.STRAIN_B).setFactorSeriouslySickVaccinated(oHos); + virusStrainConfigGroup.getOrAddParams(VirusStrain.STRAIN_B).setFactorCritical(oHos); + } + + // remove age-based susceptibility of strains starting with DELTA + + String ageSusc = "false"; + if (!Boolean.parseBoolean(ageSusc)) { + TreeMap nonSteppedAgeSusceptibility = new TreeMap<>(Map.of( + 19, 1d, + 20, 1d + )); + + for (VirusStrain strain : List.of(VirusStrain.DELTA, VirusStrain.OMICRON_BA1, VirusStrain.OMICRON_BA2, VirusStrain.OMICRON_BA5, VirusStrain.STRAIN_A)) { + virusStrainConfigGroup.getOrAddParams(strain).setAgeSusceptibility(nonSteppedAgeSusceptibility); + } + } + + // increase infectivity of alpha + virusStrainConfigGroup.getOrAddParams(VirusStrain.ALPHA).setInfectiousness(virusStrainConfigGroup.getOrAddParams(VirusStrain.ALPHA).getInfectiousness() * 1.4); + + double deltaTheta = 0.9; + virusStrainConfigGroup.getOrAddParams(VirusStrain.DELTA).setInfectiousness(virusStrainConfigGroup.getOrAddParams(VirusStrain.DELTA).getInfectiousness() * deltaTheta); + double ba1Inf = 1.9; // 2.0,2.1,2.2 + double ba2Inf = 1.7; + virusStrainConfigGroup.getOrAddParams(VirusStrain.OMICRON_BA1).setInfectiousness(virusStrainConfigGroup.getOrAddParams(VirusStrain.DELTA).getInfectiousness() * ba1Inf); + virusStrainConfigGroup.getOrAddParams(VirusStrain.OMICRON_BA2).setInfectiousness(virusStrainConfigGroup.getOrAddParams(VirusStrain.DELTA).getInfectiousness() * ba1Inf * ba2Inf); + + + //--------------------------------------- + // I M P O R T + //--------------------------------------- + + Map infPerDayAlpha = new HashMap<>(episimConfig.getInfections_pers_per_day().getOrDefault(VirusStrain.ALPHA, new TreeMap<>())); + + + // reconfig disease import of alpha + LocalDate startDateAlpha = LocalDate.parse("2021-01-15"); + + for (int i = 0; i < 7; i++) { + infPerDayAlpha.put(startDateAlpha.plusDays(i), 4); + } + + + infPerDayAlpha.put(startDateAlpha.plusDays(7), 1); + + episimConfig.setInfections_pers_per_day(VirusStrain.ALPHA, infPerDayAlpha); + + + configureFutureDiseaseImport(params, episimConfig); + + //--------------------------------------- + // R E S T R I C T I O N S + //--------------------------------------- + + FixedPolicy.ConfigBuilder builder = FixedPolicy.parse(episimConfig.getPolicy()); + + //school + String schoolUpdate = "yes"; + if(schoolUpdate.equals("yes")) { + // school closed completely until 21.2.2022 + builder.restrict(LocalDate.parse("2021-01-11"), 0.2, "educ_primary", "educ_kiga", "educ_secondary", "educ_tertiary", "educ_other"); + builder.restrict(LocalDate.parse("2021-02-21"), 0.5, "educ_primary"); + builder.restrict(LocalDate.parse("2021-03-15"), 0.5, "educ_kiga", "educ_secondary", "educ_tertiary", "educ_other"); + + } else if (schoolUpdate.equals("no")) { + + } else { + throw new RuntimeException("param value doesn't exist"); + } + + String schoolTest = "later"; + if (schoolTest.equals("later")) { + TestingConfigGroup testingConfigGroup = ConfigUtils.addOrGetModule(config, TestingConfigGroup.class); + TestingConfigGroup.TestingParams rapidTest = testingConfigGroup.getOrAddParams(TestType.RAPID_TEST); +// TestingConfigGroup.TestingParams pcrTest = testingConfigGroup.getOrAddParams(TestType.PCR); + Map> testingRateForActivitiesRapid = rapidTest.getTestingRateForActivities(); +// Map> testingRateForActivitiesPCR = pcrTest.getTestingRateForActivities(); + + + for (LocalDate date = LocalDate.parse("2021-03-19"); date.isBefore(LocalDate.parse("2021-04-25")); date = date.plusDays(1)) { + + testingRateForActivitiesRapid.get("educ_kiga").put(date, 0.); + testingRateForActivitiesRapid.get("educ_primary").put(date, 0.); + testingRateForActivitiesRapid.get("educ_secondary").put(date, 0.); + testingRateForActivitiesRapid.get("educ_tertiary").put(date, 0.); + testingRateForActivitiesRapid.get("educ_other").put(date, 0.); + + } + + testingRateForActivitiesRapid.get("educ_kiga").put(LocalDate.parse("2021-09-20"), 0.); + testingRateForActivitiesRapid.get("educ_primary").put(LocalDate.parse("2021-09-20"), 0.); + +// testingRateForActivitiesPCR.get("educ_primary").put(LocalDate.parse("2021-05-10"), 0.4); + testingRateForActivitiesRapid.get("educ_secondary").put(LocalDate.parse("2021-05-10"), 0.4); + testingRateForActivitiesRapid.get("educ_tertiary").put(LocalDate.parse("2021-05-10"), 0.4); + testingRateForActivitiesRapid.get("educ_other").put(LocalDate.parse("2021-05-10"), 0.4); + + } else if (schoolTest.equals("base")) { + + }else { + throw new RuntimeException("param value doesn't exist"); + } + + + // masks + //pt: masks + String maskType = "45to45"; + if (maskType.equals("45to45")) { + for (LocalDate date = LocalDate.parse("2020-04-21"); date.isBefore(LocalDate.parse("2021-05-01")); date = date.plusDays(1)) { + builder.restrict(date, Restriction.ofMask(Map.of(FaceMask.CLOTH, 0.45, FaceMask.SURGICAL, 0.45)), "pt", "errands", "shop_daily", "shop_other"); + + } + } else if (maskType.equals("base")) { + + } else { + throw new RuntimeException("param value doesn't exist"); + } + + // ----------------------------------------- + + // new restrictions from IfSG + LocalDate restrictionDate = LocalDate.parse(params.resDate); + + // testing rates + + double gpTestRate = 0.; + + TestingConfigGroup testingConfigGroup = ConfigUtils.addOrGetModule(config, TestingConfigGroup.class); + TestingConfigGroup.TestingParams pcrTest = testingConfigGroup.getOrAddParams(TestType.PCR); + Map> testingRateForActivitiesPcr = pcrTest.getTestingRateForActivities(); + Map> testingRateForActivitiesPcrVac = pcrTest.getTestingRateForActivitiesVaccinated(); + + TestingConfigGroup.TestingParams rapidTest = testingConfigGroup.getOrAddParams(TestType.RAPID_TEST); + Map> testingRateForActivitiesRapid = rapidTest.getTestingRateForActivities(); + Map> testingRateForActivitiesRapidVac = rapidTest.getTestingRateForActivitiesVaccinated(); + + + // school + { + //pcr tests for younger kids + testingRateForActivitiesPcr.get("educ_kiga").put(restrictionDate, 0.4); + testingRateForActivitiesPcr.get("educ_primary").put(restrictionDate, 0.4); + testingRateForActivitiesPcrVac.get("educ_kiga").put(restrictionDate, gpTestRate); + testingRateForActivitiesPcrVac.get("educ_primary").put(restrictionDate, gpTestRate); + + //add rapid tests for older kids + testingRateForActivitiesRapid.get("educ_secondary").put(restrictionDate, 0.6); + testingRateForActivitiesRapid.get("educ_tertiary").put(restrictionDate, 0.6); + testingRateForActivitiesRapid.get("educ_higher").put(restrictionDate, 0.6); + testingRateForActivitiesRapid.get("educ_other").put(restrictionDate, 0.6); + testingRateForActivitiesRapidVac.get("educ_secondary").put(restrictionDate, gpTestRate); + testingRateForActivitiesRapidVac.get("educ_tertiary").put(restrictionDate, gpTestRate); + testingRateForActivitiesRapidVac.get("educ_higher").put(restrictionDate, gpTestRate); + testingRateForActivitiesRapidVac.get("educ_other").put(restrictionDate, gpTestRate); + } + + // work + { + testingRateForActivitiesRapid.get("work").put(restrictionDate, 0.6); + testingRateForActivitiesRapidVac.get("work").put(restrictionDate, gpTestRate); + } + + // leisure: + { +// testingRateForActivitiesRapid.get("leisPrivate").put(restrictionDate, 0.0); // was 0.1 +// testingRateForActivitiesRapidVac.get("leisPrivate").put(restrictionDate, gpTestRate); + + testingRateForActivitiesRapid.get("leisPublic").put(restrictionDate, 0.9); + testingRateForActivitiesRapidVac.get("leisPublic").put(restrictionDate, gpTestRate); + } + + //WORK + double homeOfficeFactor = 0.5; + switch (params.work) { + case "none": + break; + case "homeOff": + builder.restrict(restrictionDate, 0.78 * homeOfficeFactor, "work"); // dont include business bc harder to do from home office + builder.applyToRf(restrictionDate.plusDays(1).toString(), restrictionDate.plusDays(1000).toString(), (d, rf) -> rf * homeOfficeFactor, "work"); + break; + case "test": + // handled in getBindings + break; + case "mask": + builder.restrict(restrictionDate, Restriction.ofMask(Map.of(FaceMask.SURGICAL, 0.9)), "work", "business"); + break; + case "all": + builder.restrict(restrictionDate, 0.78 * homeOfficeFactor, "work"); + builder.applyToRf(restrictionDate.plusDays(1).toString(), restrictionDate.plusDays(1000).toString(), (d, rf) -> rf * homeOfficeFactor, "work"); + builder.restrict(restrictionDate, Restriction.ofMask(Map.of(FaceMask.SURGICAL, 0.9)), "work", "business"); + break; + default: + throw new RuntimeException("invalid parameter"); + } + + //LEISURE + switch (params.leis) { + case "none": + break; + case "test": + // handled in getBindings + break; + case "mask": + builder.restrict(restrictionDate, Restriction.ofMask(Map.of(FaceMask.N95, 0.9)), "leisPublic"); + case "all": + builder.restrict(restrictionDate, Restriction.ofMask(Map.of(FaceMask.N95, 0.9)), "leisPublic"); + break; + default: + throw new RuntimeException("invalid parameter"); + } + + // shop, errands + switch (params.errands) { + case "none": + break; + case "mask": + builder.restrict(restrictionDate, Restriction.ofMask(Map.of(FaceMask.N95, 0.9)), "shop_daily", "shop_other", "errands"); + + break; + default: + throw new RuntimeException("invalid parameter"); + } + + //SCHOOL + if (params.edu.equals("maskVentTest")) { + builder.restrict(LocalDate.parse(params.resDate), Restriction.ofCiCorrection(0.5), "educ_primary", "educ_kiga", "educ_secondary", "educ_tertiary", "educ_other", "educ_higher"); + builder.restrict(LocalDate.parse(params.resDate), Restriction.ofMask(Map.of( + FaceMask.CLOTH, 0.0, + FaceMask.N95, 0.45, + FaceMask.SURGICAL, 0.45)), + "educ_secondary", "educ_tertiary", "educ_other"); + builder.restrict(LocalDate.parse(params.resDate), Restriction.ofMask(Map.of( + FaceMask.CLOTH, 0.0, + FaceMask.N95, 0.90)), + "educ_higher"); + + } else if (params.edu.equals("none")) { + + } else { + throw new RuntimeException("invalid parameter"); + } + + + // pt + switch (params.pt) { + case "none": + break; + case "mask": + builder.restrict(restrictionDate, Restriction.ofMask(Map.of(FaceMask.N95, 0.9)), "pt"); + break; + default: + throw new RuntimeException("invalid parameter"); + } + + + // LEISURE SPLIT +// Double leisureFactor = 0.0; +// if (params.leisSplit.equals("private") || params.leisSplit.equals("both")) { +// builder.applyToRf(LocalDate.of(2020,3,1).plusDays(1).toString(), restrictionDate.plusDays(1000).toString(), (d, rf) -> rf * leisureFactor, "leisPrivate"); +// } +// +// if (params.leisSplit.equals("public")|| params.leisSplit.equals("both")) { +// builder.applyToRf(LocalDate.of(2020,3,1).plusDays(1).toString(), restrictionDate.plusDays(1000).toString(), (d, rf) -> rf * leisureFactor, "leisPublic"); +// +// } + + + episimConfig.setPolicy(builder.build()); + + + //--------------------------------------- + // M I S C + //--------------------------------------- + + // vaccination + VaccinationConfigGroup vaccinationConfig = ConfigUtils.addOrGetModule(config, VaccinationConfigGroup.class); + vaccinationConfig.setUseIgA(Boolean.parseBoolean("true")); + vaccinationConfig.setTimePeriodIgA(730.); + + + //modify contact intensity + double workCi = 0.75; + episimConfig.getOrAddContainerParams("work").setContactIntensity(episimConfig.getOrAddContainerParams("work").getContactIntensity() * workCi); + episimConfig.getOrAddContainerParams("business").setContactIntensity(episimConfig.getOrAddContainerParams("business").getContactIntensity() * workCi); + + + double leisureCi = 0.4; + episimConfig.getOrAddContainerParams("leisure").setContactIntensity(episimConfig.getOrAddContainerParams("leisure").getContactIntensity() * leisureCi); + episimConfig.getOrAddContainerParams("leisPublic").setContactIntensity(episimConfig.getOrAddContainerParams("leisPublic").getContactIntensity() * leisureCi); + episimConfig.getOrAddContainerParams("leisPrivate").setContactIntensity(episimConfig.getOrAddContainerParams("leisPrivate").getContactIntensity() * leisureCi); + episimConfig.getOrAddContainerParams("visit").setContactIntensity(episimConfig.getOrAddContainerParams("visit").getContactIntensity() * leisureCi); + + + double schoolCi = 0.75; + episimConfig.getOrAddContainerParams("educ_kiga").setContactIntensity(episimConfig.getOrAddContainerParams("educ_kiga").getContactIntensity() * schoolCi); + episimConfig.getOrAddContainerParams("educ_primary").setContactIntensity(episimConfig.getOrAddContainerParams("educ_primary").getContactIntensity() * schoolCi); + episimConfig.getOrAddContainerParams("educ_secondary").setContactIntensity(episimConfig.getOrAddContainerParams("educ_secondary").getContactIntensity() * schoolCi); + episimConfig.getOrAddContainerParams("educ_tertiary").setContactIntensity(episimConfig.getOrAddContainerParams("educ_tertiary").getContactIntensity() * schoolCi); + episimConfig.getOrAddContainerParams("educ_higher").setContactIntensity(episimConfig.getOrAddContainerParams("educ_higher").getContactIntensity() * schoolCi); + episimConfig.getOrAddContainerParams("educ_other").setContactIntensity(episimConfig.getOrAddContainerParams("educ_other").getContactIntensity() * schoolCi); + + + if (DEBUG_MODE) { + UtilsJR.produceDiseaseImportPlot(episimConfig.getInfections_pers_per_day()); + + } + + return config; + } + + private void configureFutureDiseaseImport(Params params, EpisimConfigGroup episimConfig) { + Map infPerDayBa1 = new HashMap<>(episimConfig.getInfections_pers_per_day().getOrDefault(VirusStrain.OMICRON_BA1, new TreeMap<>())); + Map infPerDayBa2 = new HashMap<>(episimConfig.getInfections_pers_per_day().getOrDefault(VirusStrain.OMICRON_BA2, new TreeMap<>())); + Map infPerDayBa5 = new HashMap<>(episimConfig.getInfections_pers_per_day().getOrDefault(VirusStrain.OMICRON_BA5, new TreeMap<>())); + Map infPerDayStrA = new HashMap<>(episimConfig.getInfections_pers_per_day().getOrDefault(VirusStrain.STRAIN_A, new TreeMap<>())); + Map infPerDayStrB = new HashMap<>(episimConfig.getInfections_pers_per_day().getOrDefault(VirusStrain.STRAIN_B, new TreeMap<>())); + + + // add initial impulses for strains + //BA.1 +// LocalDate ba1Date = LocalDate.parse(params.ba1Date); +// for (int i = 0; i < 7; i++) { +// infPerDayBa1.put(ba1Date.plusDays(i), 4); +// } +// infPerDayBa1.put(ba1Date.plusDays(7), 1); + + + //BA.2 + LocalDate ba2Date = LocalDate.parse("2021-12-18"); + for (int i = 0; i < 7; i++) { + infPerDayBa2.put(ba2Date.plusDays(i), 4); + } + infPerDayBa2.put(ba2Date.plusDays(7), 1); + + //BA.5 + LocalDate ba5Date = LocalDate.parse("2022-04-10"); + for (int i = 0; i < 7; i++) { + infPerDayBa5.put(ba5Date.plusDays(i), 4); + } + infPerDayBa5.put(ba5Date.plusDays(7), 1); + + //StrainA + if (!params.StrainA.equals("off")) { + infPerDayStrA.put(LocalDate.parse("2020-01-01"), 0); + LocalDate strADate = LocalDate.parse("2022-11-01"); + for (int i = 0; i < 7; i++) { + infPerDayStrA.put(strADate.plusDays(i), 4); + } + infPerDayStrA.put(strADate.plusDays(7), 1); + } + + //StrainB + if (!params.StrainB.equals("off")) { + infPerDayStrB.put(LocalDate.parse("2020-01-01"), 0); + LocalDate strBDate = LocalDate.parse("2022-11-01"); + for (int i = 0; i < 7; i++) { + infPerDayStrB.put(strBDate.plusDays(i), 4); + } + infPerDayStrB.put(strBDate.plusDays(7), 1); + } + + + // add projected disease import for vacation waves after initial disease import + int facBa2 = 4; + int facBa5 = 4; + int facStrAB = 4; + + LocalDate dateBa2 = LocalDate.parse("2022-01-27"); // local min of disease import + LocalDate dateBa5 = LocalDate.parse("2022-05-01"); // after vaca import + LocalDate dateStrainAB = LocalDate.parse("2022-11-18"); // after vaca import + + + NavigableMap data = DataUtils.readDiseaseImport(SnzCologneProductionScenario.INPUT.resolve("cologneDiseaseImport_Projected.csv")); + LocalDate date = null; + for (Map.Entry entry : data.entrySet()) { + date = entry.getKey(); + double factor = 0.25 * 2352476. / 919936.; //25% sample, data is given for Cologne City so we have to scale it to the whole model +// + double cases = factor * entry.getValue(); + + if (date.isAfter(dateStrainAB) && (!params.StrainA.equals("off") || !params.StrainB.equals("off"))) { + if (!params.StrainA.equals("off") && !params.StrainB.equals("off")) { + infPerDayStrA.put(date, ((int) cases * facStrAB) == 0 ? 1 : (int) (0.5 * cases * facStrAB)); + infPerDayStrB.put(date, ((int) cases * facStrAB) == 0 ? 1 : (int) (0.5 * cases * facStrAB)); + } + else if (!params.StrainA.equals("off")) { + infPerDayStrA.put(date, ((int) cases * facStrAB) == 0 ? 1 : (int) (cases * facStrAB)); + } + else if (!params.StrainB.equals("off")) { + infPerDayStrB.put(date, ((int) cases * facStrAB) == 0 ? 1 : (int) (cases * facStrAB)); + } + else { + throw new RuntimeException(); + } + infPerDayBa5.put(date, 1); + infPerDayBa2.put(date, 1); + } else if (date.isAfter(dateBa5)) { + infPerDayBa5.put(date, ((int) cases * facBa5) == 0 ? 1 : (int) (cases * facBa5)); + infPerDayBa2.put(date, 1); + } else if (date.isAfter(dateBa2)) { + infPerDayBa2.put(date, ((int) cases * facBa2) == 0 ? 1 : (int) (cases * facBa2)); + } + + } + + // save disease import + episimConfig.setInfections_pers_per_day(VirusStrain.OMICRON_BA1, infPerDayBa1); + episimConfig.setInfections_pers_per_day(VirusStrain.OMICRON_BA2, infPerDayBa2); + episimConfig.setInfections_pers_per_day(VirusStrain.OMICRON_BA5, infPerDayBa5); + + if (!params.StrainA.equals("off")) { + episimConfig.setInfections_pers_per_day(VirusStrain.STRAIN_A, infPerDayStrA); + } + if (!params.StrainB.equals("off")) { + episimConfig.setInfections_pers_per_day(VirusStrain.STRAIN_B, infPerDayStrB); + } + } + + public static final class Params { + // general + @GenerateSeeds(5) + public long seed; + + @StringParameter({"6.0"}) + public String StrainA; + + @StringParameter({"off"}) + public String StrainB; + + @StringParameter({"2022-12-01"}) + public String resDate; + + // @StringParameter({"false", "true"}) +// public String igA; + // vaccination campaign + @StringParameter({"ba5Update", "mRNA", "off"}) + public String vacType; + + + //measures in the work context: + // homeOff = 50% home office = work Rf cut in half + // + @StringParameter({"none", "homeOff", "test", "mask", "all"}) + public String work; + + @StringParameter({"none", "mask", "test", "all"}) + public String leis; + + // mask restrictions for "shop_daily", "shop_other", "errands" + @StringParameter({"none", "mask"}) + public String errands; + + @StringParameter({"none", "maskVentTest"}) + public String edu; + + @StringParameter({"mask"}) + public String pt; + } + + + public static void main(String[] args) { + String[] args2 = { + RunParallel.OPTION_SETUP, CologneBMBF20220805_leisSplit.class.getName(), + RunParallel.OPTION_PARAMS, Params.class.getName(), + RunParallel.OPTION_TASKS, Integer.toString(1), + RunParallel.OPTION_ITERATIONS, Integer.toString(30), + RunParallel.OPTION_METADATA + }; + + RunParallel.main(args2); + } + + +} + diff --git a/src/main/java/org/matsim/run/batch/CologneBMBF20221024.java b/src/main/java/org/matsim/run/batch/CologneBMBF20221024.java new file mode 100644 index 000000000..798cd49e1 --- /dev/null +++ b/src/main/java/org/matsim/run/batch/CologneBMBF20221024.java @@ -0,0 +1,692 @@ +package org.matsim.run.batch; + +import com.google.inject.AbstractModule; +import com.google.inject.Module; +import com.google.inject.Singleton; +import com.google.inject.multibindings.Multibinder; +import com.google.inject.util.Modules; +import org.matsim.core.config.Config; +import org.matsim.core.config.ConfigUtils; +import org.matsim.episim.BatchRun; +import org.matsim.episim.EpisimConfigGroup; +import org.matsim.episim.VirusStrainConfigGroup; +import org.matsim.episim.analysis.*; +import org.matsim.episim.model.*; +import org.matsim.episim.model.listener.HouseholdSusceptibility; +import org.matsim.episim.model.vaccination.VaccinationModel; +import org.matsim.episim.model.vaccination.VaccinationStrategyReoccurringCampaigns; +import org.matsim.episim.policy.FixedPolicy; +import org.matsim.episim.policy.Restriction; +import org.matsim.run.RunParallel; +import org.matsim.run.modules.SnzCologneProductionScenario; + +import javax.annotation.Nullable; +import java.time.LocalDate; +import java.util.*; + + +/** + * Batch for Bmbf runs + */ +public class CologneBMBF20221024 implements BatchRun { + + boolean DEBUG_MODE = false; + int runCount = 0; + + LocalDate restrictionDatePhase1 = LocalDate.parse("2022-12-01"); + LocalDate restrictionDatePhase2 = restrictionDatePhase1.plusDays(10); + + + @Nullable + @Override + public Module getBindings(int id, @Nullable Params params) { + return Modules.override(getBindings(0.0, params)).with(new AbstractModule() { + @Override + protected void configure() { + + // VACCINATION MODEL + Multibinder set = Multibinder.newSetBinder(binder(), VaccinationModel.class); + set.addBinding().to(VaccinationStrategyReoccurringCampaigns.class).in(Singleton.class); + // fixed values + LocalDate start = LocalDate.parse("2022-10-15"); + VaccinationType vaccinationType = VaccinationType.ba5Update; + int campaignDuration = 300000; + + // default values, to be changed if params != null + int minDaysAfterInfection = 180; + int minDaysAfterVaccination = 180; + VaccinationStrategyReoccurringCampaigns.Config.VaccinationPool vaccinationPool = VaccinationStrategyReoccurringCampaigns.Config.VaccinationPool.vaccinated; + LocalDate emergencyDate = LocalDate.MAX; + LocalDate dateToTurnDownMinDaysAfterInfection = LocalDate.MAX; + Map startDateToVaccination = new HashMap<>(); + startDateToVaccination.put(start, vaccinationType); + + if (params != null) { + if (params.vacCamp.equals("base")) { // + + + } else if(params.vacCamp.equals("ph1_90")){ + minDaysAfterInfection = 90; + minDaysAfterVaccination = 90; + + emergencyDate = restrictionDatePhase1; + } else if(params.vacCamp.equals("ph1_90vax180")){ + minDaysAfterInfection = 90; + emergencyDate = restrictionDatePhase1; + } else if(params.vacCamp.equals("ph1_180")){ // + + emergencyDate = restrictionDatePhase1; + } else if (params.vacCamp.equals("ph1_180_ph2_inf90vax180")) { + emergencyDate = restrictionDatePhase1; + dateToTurnDownMinDaysAfterInfection = restrictionDatePhase2; + // same as ifsg180 but after phase 2 date, minDaysAfterInfection = 90; + }else if(params.vacCamp.equals("ph2_90")){ + minDaysAfterInfection = 90; + minDaysAfterVaccination = 90; + emergencyDate = restrictionDatePhase2; + } else if(params.vacCamp.equals("ph2_inf90vax180")){ + minDaysAfterInfection = 90; + emergencyDate = restrictionDatePhase2; + } else if(params.vacCamp.equals("ph2_180")) { + emergencyDate = restrictionDatePhase2; + }else { + throw new RuntimeException(); + } + } + + bind(VaccinationStrategyReoccurringCampaigns.Config.class).toInstance(new VaccinationStrategyReoccurringCampaigns.Config(startDateToVaccination, campaignDuration, vaccinationPool, minDaysAfterInfection, minDaysAfterVaccination, emergencyDate, dateToTurnDownMinDaysAfterInfection)); + + + // ANTIBODY MODEL + // default values + double mutEscDelta = 29.2 / 10.9; + double mutEscBa1 = 10.9 / 1.9; + double mutEscBa5 = 5.0; + + double mutEscStrainA = 0.; + double mutEscStrainB = 0.; + + + if (params != null) { +// mutEscBa1 = params.ba1Esc; +// mutEscBa5 = params.ba5Esc; + +// String StrainA = "6.0"; + String StrainB = "off"; + + + if (!params.StrainA.equals("off")) { + mutEscStrainA = Double.parseDouble(params.StrainA); + } + if (!StrainB.equals("off")) { + mutEscStrainB = Double.parseDouble(StrainB); + } + + } + + //initial antibodies + Map> initialAntibodies = new HashMap<>(); + Map> antibodyRefreshFactors = new HashMap<>(); + configureAntibodies(initialAntibodies, antibodyRefreshFactors, mutEscDelta, mutEscBa1, mutEscBa5, mutEscStrainA, mutEscStrainB); + + AntibodyModel.Config antibodyConfig = new AntibodyModel.Config(initialAntibodies, antibodyRefreshFactors); + + double immuneSigma = 3.0; + if (params != null) { + antibodyConfig.setImmuneReponseSigma(immuneSigma); + } + + bind(AntibodyModel.Config.class).toInstance(antibodyConfig); + + if (params == null) return; + + // HOUSEHOLD SUSCEPTIBILITY + // designates a 35% of households as super safe; the susceptibility of that subpopulation is reduced to 1% wrt to general population. + bind(HouseholdSusceptibility.Config.class).toInstance( + HouseholdSusceptibility.newConfig() + .withSusceptibleHouseholds(0.35, 0.01) +// .withNonVaccinableHouseholds(params.nonVaccinableHh) +// .withShape(SnzCologneProductionScenario.INPUT.resolve("CologneDistricts.zip")) +// .withFeature("STT_NAME", vingst, altstadtNord, bickendorf, weiden) + ); + + } + + private void configureAntibodies(Map> initialAntibodies, + Map> antibodyRefreshFactors, + double mutEscDelta, double mutEscBa1, double mutEscBa5, double mutEscStrainA, double mutEscStrainB) { + for (VaccinationType immunityType : VaccinationType.values()) { + initialAntibodies.put(immunityType, new EnumMap<>( VirusStrain.class ) ); + for (VirusStrain virusStrain : VirusStrain.values()) { + + if (immunityType == VaccinationType.mRNA) { + initialAntibodies.get(immunityType).put(virusStrain, 29.2); //10.0 + } + else if (immunityType == VaccinationType.vector) { + initialAntibodies.get(immunityType).put(virusStrain, 6.8); //2.5 + } + else { + initialAntibodies.get(immunityType).put(virusStrain, 5.0); + } + } + } + + for (VirusStrain immunityType : VirusStrain.values()) { + initialAntibodies.put(immunityType, new EnumMap<>( VirusStrain.class ) ); + for (VirusStrain virusStrain : VirusStrain.values()) { + initialAntibodies.get(immunityType).put(virusStrain, 5.0); + } + } + + //mRNAAlpha, mRNADelta, mRNABA1 comes from Sydney's calibration. + //The other values come from Rössler et al. + + //Wildtype + double mRNAAlpha = 29.2; + initialAntibodies.get(VaccinationType.mRNA).put(VirusStrain.SARS_CoV_2, mRNAAlpha); + initialAntibodies.get(VaccinationType.vector).put(VirusStrain.SARS_CoV_2, mRNAAlpha * 210. / 700.); + initialAntibodies.get(VirusStrain.SARS_CoV_2).put(VirusStrain.SARS_CoV_2, mRNAAlpha * 300. / 700.); + initialAntibodies.get(VirusStrain.ALPHA).put(VirusStrain.SARS_CoV_2, mRNAAlpha * 300. / 700.); + initialAntibodies.get(VirusStrain.DELTA).put(VirusStrain.SARS_CoV_2, mRNAAlpha * 210. / 700.); + initialAntibodies.get(VirusStrain.OMICRON_BA1).put(VirusStrain.SARS_CoV_2, 0.01); + initialAntibodies.get(VirusStrain.OMICRON_BA2).put(VirusStrain.SARS_CoV_2, 0.01); + initialAntibodies.get(VirusStrain.OMICRON_BA5).put(VirusStrain.SARS_CoV_2, 0.01); + initialAntibodies.get(VirusStrain.STRAIN_A).put(VirusStrain.SARS_CoV_2, 0.01); + initialAntibodies.get(VirusStrain.STRAIN_B).put(VirusStrain.SARS_CoV_2, 0.01); + initialAntibodies.get(VaccinationType.ba1Update).put(VirusStrain.SARS_CoV_2, mRNAAlpha / mutEscDelta / mutEscBa1); + initialAntibodies.get(VaccinationType.ba5Update).put(VirusStrain.SARS_CoV_2, mRNAAlpha / mutEscDelta / mutEscBa1 / mutEscBa5); + + //Alpha + initialAntibodies.get(VaccinationType.mRNA).put(VirusStrain.ALPHA, mRNAAlpha); + initialAntibodies.get(VaccinationType.vector).put(VirusStrain.ALPHA, mRNAAlpha * 210. / 700.); + initialAntibodies.get(VirusStrain.SARS_CoV_2).put(VirusStrain.ALPHA, mRNAAlpha * 300. / 700.); + initialAntibodies.get(VirusStrain.ALPHA).put(VirusStrain.ALPHA, mRNAAlpha * 300. / 700.); + initialAntibodies.get(VirusStrain.DELTA).put(VirusStrain.ALPHA, mRNAAlpha * 210. / 700.); + initialAntibodies.get(VirusStrain.OMICRON_BA1).put(VirusStrain.ALPHA, 0.01); + initialAntibodies.get(VirusStrain.OMICRON_BA2).put(VirusStrain.ALPHA, 0.01); + initialAntibodies.get(VirusStrain.OMICRON_BA5).put(VirusStrain.ALPHA, 0.01); + initialAntibodies.get(VirusStrain.STRAIN_A).put(VirusStrain.ALPHA, 0.01); + initialAntibodies.get(VirusStrain.STRAIN_B).put(VirusStrain.ALPHA, 0.01); + initialAntibodies.get(VaccinationType.ba1Update).put(VirusStrain.ALPHA, mRNAAlpha / mutEscDelta / mutEscBa1); + initialAntibodies.get(VaccinationType.ba5Update).put(VirusStrain.ALPHA, mRNAAlpha / mutEscDelta / mutEscBa1 / mutEscBa5); + + //DELTA + double mRNADelta = mRNAAlpha / mutEscDelta; + initialAntibodies.get(VaccinationType.mRNA).put(VirusStrain.DELTA, mRNADelta); + initialAntibodies.get(VaccinationType.vector).put(VirusStrain.DELTA, mRNADelta * 150./300.); + initialAntibodies.get(VirusStrain.SARS_CoV_2).put(VirusStrain.DELTA, mRNADelta * 64./300.); + initialAntibodies.get(VirusStrain.ALPHA).put(VirusStrain.DELTA, mRNADelta * 64./300.); + initialAntibodies.get(VirusStrain.DELTA).put(VirusStrain.DELTA, mRNADelta * 450./300.); + initialAntibodies.get(VirusStrain.OMICRON_BA1).put(VirusStrain.DELTA, 0.01); + initialAntibodies.get(VirusStrain.OMICRON_BA2).put(VirusStrain.DELTA, 0.01); + initialAntibodies.get(VirusStrain.OMICRON_BA5).put(VirusStrain.DELTA, 0.01); + initialAntibodies.get(VirusStrain.STRAIN_A).put(VirusStrain.DELTA, 0.01); + initialAntibodies.get(VirusStrain.STRAIN_B).put(VirusStrain.DELTA, 0.01); + initialAntibodies.get(VaccinationType.ba1Update).put(VirusStrain.DELTA, mRNADelta / mutEscBa1); + initialAntibodies.get(VaccinationType.ba5Update).put(VirusStrain.DELTA, mRNADelta / mutEscBa1 / mutEscBa5); + + //BA.1 + double mRNABA1 = mRNADelta / mutEscBa1; + initialAntibodies.get(VaccinationType.mRNA).put(VirusStrain.OMICRON_BA1, mRNABA1); + initialAntibodies.get(VaccinationType.vector).put(VirusStrain.OMICRON_BA1, mRNABA1 * 4./20.); //??? + initialAntibodies.get(VirusStrain.SARS_CoV_2).put(VirusStrain.OMICRON_BA1, mRNABA1 * 6./20.); + initialAntibodies.get(VirusStrain.ALPHA).put(VirusStrain.OMICRON_BA1, mRNABA1 * 6./20.); + initialAntibodies.get(VirusStrain.DELTA).put(VirusStrain.OMICRON_BA1, mRNABA1 * 8./20.); + initialAntibodies.get(VirusStrain.OMICRON_BA1).put(VirusStrain.OMICRON_BA1, 64.0 / 300.); + initialAntibodies.get(VirusStrain.OMICRON_BA2).put(VirusStrain.OMICRON_BA1, 64.0 / 300. / 1.4); + initialAntibodies.get(VirusStrain.OMICRON_BA5).put(VirusStrain.OMICRON_BA1, 64.0 / 300. / mutEscBa5); //todo: is 1.4 + initialAntibodies.get(VirusStrain.STRAIN_A).put(VirusStrain.OMICRON_BA1, 64.0 / 300. / mutEscBa5 / mutEscStrainA); + initialAntibodies.get(VirusStrain.STRAIN_B).put(VirusStrain.OMICRON_BA1, 64.0 / 300. / mutEscStrainB); + initialAntibodies.get(VaccinationType.ba1Update).put(VirusStrain.OMICRON_BA1, mRNAAlpha); + initialAntibodies.get(VaccinationType.ba5Update).put(VirusStrain.OMICRON_BA1, mRNAAlpha / mutEscBa5); + + //BA.2 + double mRNABA2 = mRNABA1; + initialAntibodies.get(VaccinationType.mRNA).put(VirusStrain.OMICRON_BA2, mRNABA2); + initialAntibodies.get(VaccinationType.vector).put(VirusStrain.OMICRON_BA2, mRNABA2 * 4./20.); + initialAntibodies.get(VirusStrain.SARS_CoV_2).put(VirusStrain.OMICRON_BA2, mRNABA2 * 6./20.); + initialAntibodies.get(VirusStrain.ALPHA).put(VirusStrain.OMICRON_BA2, mRNABA2 * 6./20.); + initialAntibodies.get(VirusStrain.DELTA).put(VirusStrain.OMICRON_BA2, mRNABA2 * 8./20.); + initialAntibodies.get(VirusStrain.OMICRON_BA1).put(VirusStrain.OMICRON_BA2, 64.0 / 300. / 1.4); + initialAntibodies.get(VirusStrain.OMICRON_BA2).put(VirusStrain.OMICRON_BA2, 64.0 / 300.); + initialAntibodies.get(VirusStrain.OMICRON_BA5).put(VirusStrain.OMICRON_BA2, 64.0 / 300. / mutEscBa5); + initialAntibodies.get(VirusStrain.STRAIN_A).put(VirusStrain.OMICRON_BA2, 64.0 / 300. / mutEscBa5 / mutEscStrainA); + initialAntibodies.get(VirusStrain.STRAIN_B).put(VirusStrain.OMICRON_BA2, 64.0 / 300. / mutEscStrainB); + initialAntibodies.get(VaccinationType.ba1Update).put(VirusStrain.OMICRON_BA2, mRNAAlpha); + initialAntibodies.get(VaccinationType.ba5Update).put(VirusStrain.OMICRON_BA2, mRNAAlpha / mutEscBa5); + + + //BA.5 + double mRNABa5 = mRNABA2 / mutEscBa5; + initialAntibodies.get(VaccinationType.mRNA).put(VirusStrain.OMICRON_BA5, mRNABa5); + initialAntibodies.get(VaccinationType.vector).put(VirusStrain.OMICRON_BA5, mRNABa5 * 4./20.); + initialAntibodies.get(VirusStrain.SARS_CoV_2).put(VirusStrain.OMICRON_BA5, mRNABa5 * 6./20.); + initialAntibodies.get(VirusStrain.ALPHA).put(VirusStrain.OMICRON_BA5, mRNABa5 * 6./20.); + initialAntibodies.get(VirusStrain.DELTA).put(VirusStrain.OMICRON_BA5, mRNABa5 * 8./20.); + initialAntibodies.get(VirusStrain.OMICRON_BA1).put(VirusStrain.OMICRON_BA5, 64.0 / 300. / mutEscBa5);// todo: do we need 1.4? + initialAntibodies.get(VirusStrain.OMICRON_BA2).put(VirusStrain.OMICRON_BA5, 64.0 / 300./ mutEscBa5); + initialAntibodies.get(VirusStrain.OMICRON_BA5).put(VirusStrain.OMICRON_BA5, 64.0 / 300.); + initialAntibodies.get(VirusStrain.STRAIN_A).put(VirusStrain.OMICRON_BA5, 64.0 / 300./ mutEscStrainA); //todo ??? + initialAntibodies.get(VirusStrain.STRAIN_B).put(VirusStrain.OMICRON_BA5, 64.0 / 300. / mutEscBa5 / mutEscStrainB); + initialAntibodies.get(VaccinationType.ba1Update).put(VirusStrain.OMICRON_BA5, mRNAAlpha / mutEscBa5); + initialAntibodies.get(VaccinationType.ba5Update).put(VirusStrain.OMICRON_BA5, mRNAAlpha); + + //StrainA + double mRNAStrainA = mRNABa5 / mutEscStrainA; + initialAntibodies.get(VaccinationType.mRNA).put(VirusStrain.STRAIN_A, mRNAStrainA); + initialAntibodies.get(VaccinationType.vector).put(VirusStrain.STRAIN_A, mRNAStrainA * 4./20.); + initialAntibodies.get(VirusStrain.SARS_CoV_2).put(VirusStrain.STRAIN_A, mRNAStrainA * 6./20.); + initialAntibodies.get(VirusStrain.ALPHA).put(VirusStrain.STRAIN_A, mRNAStrainA * 6./20.); + initialAntibodies.get(VirusStrain.DELTA).put(VirusStrain.STRAIN_A, mRNAStrainA * 8./20.); + initialAntibodies.get(VirusStrain.OMICRON_BA1).put(VirusStrain.STRAIN_A, 64.0 / 300. / mutEscBa5 /mutEscStrainA); + initialAntibodies.get(VirusStrain.OMICRON_BA2).put(VirusStrain.STRAIN_A, 64.0 / 300./ mutEscBa5 /mutEscStrainA); + initialAntibodies.get(VirusStrain.OMICRON_BA5).put(VirusStrain.STRAIN_A, 64.0 / 300. / mutEscStrainA); + initialAntibodies.get(VirusStrain.STRAIN_A).put(VirusStrain.STRAIN_A, 64.0 / 300.); + initialAntibodies.get(VirusStrain.STRAIN_B).put(VirusStrain.STRAIN_A, 64.0 / 300. / mutEscStrainA / mutEscStrainB / mutEscBa5); + initialAntibodies.get(VaccinationType.ba1Update).put(VirusStrain.STRAIN_A, mRNAAlpha / mutEscBa5 / mutEscStrainA); + initialAntibodies.get(VaccinationType.ba5Update).put(VirusStrain.STRAIN_A, mRNAAlpha / mutEscStrainA); + + //StrainB + double mRNAStrainB = mRNABA2 / mutEscStrainB; + initialAntibodies.get(VaccinationType.mRNA).put(VirusStrain.STRAIN_B, mRNAStrainB); + initialAntibodies.get(VaccinationType.vector).put(VirusStrain.STRAIN_B, mRNAStrainB * 4./20.); + initialAntibodies.get(VirusStrain.SARS_CoV_2).put(VirusStrain.STRAIN_B, mRNAStrainB * 6./20.); + initialAntibodies.get(VirusStrain.ALPHA).put(VirusStrain.STRAIN_B, mRNAStrainB * 6./20.); + initialAntibodies.get(VirusStrain.DELTA).put(VirusStrain.STRAIN_B, mRNAStrainB * 8./20.); + initialAntibodies.get(VirusStrain.OMICRON_BA1).put(VirusStrain.STRAIN_B, 64.0 / 300. / mutEscStrainB); + initialAntibodies.get(VirusStrain.OMICRON_BA2).put(VirusStrain.STRAIN_B, 64.0 / 300./ mutEscStrainB); + initialAntibodies.get(VirusStrain.OMICRON_BA5).put(VirusStrain.STRAIN_B, 64.0 / 300. / mutEscStrainB / mutEscBa5); + initialAntibodies.get(VirusStrain.STRAIN_A).put(VirusStrain.STRAIN_B, 64.0 / 300./ mutEscStrainA / mutEscStrainB / mutEscBa5); + initialAntibodies.get(VirusStrain.STRAIN_B).put(VirusStrain.STRAIN_B, 64.0 / 300.); + initialAntibodies.get(VaccinationType.ba1Update).put(VirusStrain.STRAIN_B, mRNAAlpha / mutEscStrainB); + initialAntibodies.get(VaccinationType.ba5Update).put(VirusStrain.STRAIN_B, mRNAAlpha / mutEscStrainB / mutEscBa5); + + + for (VaccinationType immunityType : VaccinationType.values()) { + antibodyRefreshFactors.put(immunityType, new EnumMap<>( VirusStrain.class ) ); + for (VirusStrain virusStrain : VirusStrain.values()) { + + if (immunityType == VaccinationType.mRNA) { + antibodyRefreshFactors.get(immunityType).put(virusStrain, 15.0); + } + else if (immunityType == VaccinationType.vector) { + antibodyRefreshFactors.get(immunityType).put(virusStrain, 5.0); + } + else if (immunityType == VaccinationType.ba1Update) { + antibodyRefreshFactors.get(immunityType).put(virusStrain, 15.0); + } + else if (immunityType == VaccinationType.ba5Update) { + antibodyRefreshFactors.get(immunityType).put(virusStrain, 15.0); + } + else { + antibodyRefreshFactors.get(immunityType).put(virusStrain, Double.NaN); + } + + } + } + + for (VirusStrain immunityType : VirusStrain.values()) { + antibodyRefreshFactors.put(immunityType, new EnumMap<>( VirusStrain.class ) ); + for (VirusStrain virusStrain : VirusStrain.values()) { + antibodyRefreshFactors.get(immunityType).put(virusStrain, 15.0); + } + } + + +// UtilsJR.printInitialAntibodiesToConsole(initialAntibodies); + + } + }); + + } + + private SnzCologneProductionScenario getBindings(double pHousehold, Params params) { + return new SnzCologneProductionScenario.Builder() + .setCarnivalModel(SnzCologneProductionScenario.CarnivalModel.yes) + .setSebastianUpdate(true) + .setLeisureCorrection(1.3) //params == null ? 0.0 : params.actCorrection) + .setScaleForActivityLevels(1.3) + .setSuscHouseholds_pct(pHousehold) + .setActivityHandling(EpisimConfigGroup.ActivityHandling.startOfDay) +// .setTestingModel(params != null ? FlexibleTestingModel.class : DefaultTestingModel.class) + .setInfectionModel(InfectionModelWithAntibodies.class) + .build(); + } + + @Override + public Metadata getMetadata() { + return Metadata.of("cologne", "calibration"); + } + + @Override + public Collection postProcessing() { + return List.of( + new VaccinationEffectiveness().withArgs(), + new RValuesFromEvents().withArgs(), + new VaccinationEffectivenessFromPotentialInfections().withArgs("--remove-infected"), + new FilterEvents().withArgs("--output","./output/"), + new HospitalNumbersFromEvents().withArgs("--output","./output/","--input","/scratch/projects/bzz0020/episim-input") +// new SecondaryAttackRateFromEvents().withArgs() + ); + } + + @Override + public Config prepareConfig(int id, Params params) { + + if (DEBUG_MODE) { + if (runCount == 0 && params.vacCamp.equals("emergency180")) { //&& params.strAEsc != 0.0 && params.ba5Inf == 0. && params.eduTest.equals("true")) { + runCount++; + } else { + return null; + } + } + + SnzCologneProductionScenario module = getBindings(0.0, params); + + Config config = module.config(); + + config.global().setRandomSeed(params.seed); + + EpisimConfigGroup episimConfig = ConfigUtils.addOrGetModule(config, EpisimConfigGroup.class); + + episimConfig.setCalibrationParameter(episimConfig.getCalibrationParameter() * 1.2 * 1.7); + + //snapshot +// episimConfig.setSnapshotInterval(30); +// episimConfig.setSnapshotPrefix(String.valueOf(params.seed)); + episimConfig.setStartFromSnapshot("/scratch/projects/bzz0020/episim-input/snapshots-cologne-2022-10-18/" + params.seed + "-960-2022-10-11.zip"); + episimConfig.setSnapshotSeed(EpisimConfigGroup.SnapshotSeed.restore); + //--------------------------------------- + // S T R A I N S + //--------------------------------------- + + VirusStrainConfigGroup virusStrainConfigGroup = ConfigUtils.addOrGetModule(config, VirusStrainConfigGroup.class); + + double ba5Inf = virusStrainConfigGroup.getOrAddParams(VirusStrain.OMICRON_BA5).getInfectiousness(); + double ba5Hos = virusStrainConfigGroup.getOrAddParams(VirusStrain.OMICRON_BA5).getFactorSeriouslySick(); + +// STRAIN_A + if (!params.StrainA.equals("off")) { + + virusStrainConfigGroup.getOrAddParams(VirusStrain.STRAIN_A).setInfectiousness(ba5Inf); + virusStrainConfigGroup.getOrAddParams(VirusStrain.STRAIN_A).setFactorSeriouslySick(ba5Hos); + virusStrainConfigGroup.getOrAddParams(VirusStrain.STRAIN_A).setFactorSeriouslySickVaccinated(ba5Hos); + virusStrainConfigGroup.getOrAddParams(VirusStrain.STRAIN_A).setFactorCritical(ba5Hos); + } + +// STRAIN_B +// if (!params.StrainB.equals("off")) { +// +// virusStrainConfigGroup.getOrAddParams(VirusStrain.STRAIN_B).setInfectiousness(virusStrainConfigGroup.getParams(VirusStrain.OMICRON_BA5).getInfectiousness() * ba5Inf); +// virusStrainConfigGroup.getOrAddParams(VirusStrain.STRAIN_B).setFactorSeriouslySick(ba5Hos); +// virusStrainConfigGroup.getOrAddParams(VirusStrain.STRAIN_B).setFactorSeriouslySickVaccinated(ba5Hos); +// virusStrainConfigGroup.getOrAddParams(VirusStrain.STRAIN_B).setFactorCritical(ba5Hos); +// } + + //--------------------------------------- + // I M P O R T + //--------------------------------------- + + configureFutureDiseaseImport(params, episimConfig); + + // modify import: + LocalDate impModDate = LocalDate.parse("2022-01-31"); + double impRedBa1 = 0.0; + double impRedBa2 = 0.0; + if (impRedBa1 != 1.0) { + NavigableMap impBa1 = episimConfig.getInfections_pers_per_day().get(VirusStrain.OMICRON_BA1); + for (Map.Entry entry : impBa1.entrySet()) { + if (entry.getKey().isAfter(impModDate)) { + impBa1.put(entry.getKey(), (int) (entry.getValue() * impRedBa1)); + } + } + } + + if (impRedBa2 != 1.0) { + NavigableMap impBa2 = episimConfig.getInfections_pers_per_day().get(VirusStrain.OMICRON_BA2); + for (Map.Entry entry : impBa2.entrySet()) { + if (entry.getKey().isAfter(impModDate)) { + impBa2.put(entry.getKey(), (int) (entry.getValue() * impRedBa2)); + } + } + } + + + + //--------------------------------------- + // R E S T R I C T I O N S + //--------------------------------------- + + FixedPolicy.ConfigBuilder builder = FixedPolicy.parse(episimConfig.getPolicy()); + + + + //ifsg + if ("base".equals(params.ifsg)) { + + } else if ("45".equals(params.ifsg) || "90".equals(params.ifsg)) { + double compliance = Double.parseDouble(params.ifsg) / 100.; + builder.restrict(restrictionDatePhase1, Restriction.ofMask(Map.of( + FaceMask.CLOTH, 0.0, + FaceMask.SURGICAL, compliance)), + "educ_secondary", "educ_tertiary", "educ_other", "educ_higher"); + builder.restrict(restrictionDatePhase1, Restriction.ofMask(Map.of(FaceMask.N95, compliance)), "leisPublic"); + builder.restrict(restrictionDatePhase1, Restriction.ofMask(Map.of(FaceMask.N95, compliance)), "shop_daily", "shop_other", "errands"); + builder.restrict(restrictionDatePhase1, Restriction.ofMask(Map.of(FaceMask.N95, 0.9)), "pt"); // pt has 90 compliance either way + } else { + throw new RuntimeException(); + } + + // EMERGENCY RESTRICTIONS + //work + builder.restrict(LocalDate.parse("2022-10-15"), 0.88, "work", "business"); + double homeOfficeFactor = 0.5; + switch (params.work) { + case "base": + break; + case "half": + builder.restrict(restrictionDatePhase2, 0.88 * homeOfficeFactor, "work"); // dont include business bc harder to do from home office + builder.applyToRf(restrictionDatePhase2.plusDays(1).toString(), restrictionDatePhase2.plusDays(1000).toString(), (d, rf) -> rf * homeOfficeFactor, "work"); + break; + case "half&mask": + builder.restrict(restrictionDatePhase2, 0.88 * homeOfficeFactor, "work"); // dont include business bc harder to do from home office + builder.applyToRf(restrictionDatePhase2.plusDays(1).toString(), restrictionDatePhase2.plusDays(1000).toString(), (d, rf) -> rf * homeOfficeFactor, "work"); + + builder.restrict(restrictionDatePhase2, Restriction.ofMask(Map.of(FaceMask.SURGICAL, 0.0, FaceMask.N95, 0.9)), "work", "business"); + break; + default: + throw new RuntimeException("invalid parameter"); + } + + // leisure public + private + switch (params.leis) { + case "base": + break; + case "pub50": + builder.restrict(restrictionDatePhase2, 0.88 * 0.5, "leisPublic"); + break; + case "pubPriv50": + builder.restrict(restrictionDatePhase2, 0.88 * 0.5, "leisPublic", "leisPrivate"); + break; + default: + throw new RuntimeException("invalid parameter"); + } + + //school + switch (params.edu) { + case "base": + break; + case "mask": + builder.restrict(restrictionDatePhase2, Restriction.ofMask(Map.of( + FaceMask.CLOTH, 0.0, + FaceMask.SURGICAL, 0.0, + FaceMask.N95, 0.90)), + "educ_secondary", "educ_tertiary", "educ_other", "educ_higher"); + break; + case "half&mask": + builder.restrict(restrictionDatePhase2, Restriction.ofMask(Map.of( + FaceMask.CLOTH, 0.0, + FaceMask.SURGICAL, 0.0, + FaceMask.N95, 0.90)), + "educ_secondary", "educ_tertiary", "educ_other", "educ_higher"); + + builder.restrict(restrictionDatePhase2, 0.5, "educ_primary", "educ_kiga", "educ_secondary", "educ_tertiary", "educ_other", "educ_higher"); + builder.applyToRf(restrictionDatePhase2.plusDays(1).toString(), restrictionDatePhase2.plusDays(1000).toString(), (d, rf) -> Math.min(0.5, rf), "educ_primary", "educ_kiga", "educ_secondary", "educ_tertiary", "educ_other", "educ_higher"); + break; + default: + throw new RuntimeException("invalid parameter"); + } + + // vary amount of "school" activity that takes place during vacation + builder.restrict(LocalDate.parse("2022-06-27"), 0.8, "educ_primary", "educ_kiga", "educ_secondary", "educ_tertiary", "educ_other"); + + episimConfig.setPolicy(builder.build()); + + + //--------------------------------------- + // M I S C + //--------------------------------------- + + + if (DEBUG_MODE) { + UtilsJR.produceDiseaseImportPlot(episimConfig.getInfections_pers_per_day()); + } + + return config; + } + + private void configureFutureDiseaseImport(Params params, EpisimConfigGroup episimConfig) { + Map infPerDayBa1 = new HashMap<>(episimConfig.getInfections_pers_per_day().getOrDefault(VirusStrain.OMICRON_BA1, new TreeMap<>())); + Map infPerDayBa2 = new HashMap<>(episimConfig.getInfections_pers_per_day().getOrDefault(VirusStrain.OMICRON_BA2, new TreeMap<>())); + Map infPerDayBa5 = new HashMap<>(episimConfig.getInfections_pers_per_day().getOrDefault(VirusStrain.OMICRON_BA5, new TreeMap<>())); + Map infPerDayStrA = new HashMap<>(episimConfig.getInfections_pers_per_day().getOrDefault(VirusStrain.STRAIN_A, new TreeMap<>())); + Map infPerDayStrB = new HashMap<>(episimConfig.getInfections_pers_per_day().getOrDefault(VirusStrain.STRAIN_B, new TreeMap<>())); + + //StrainA + if (!params.StrainA.equals("off")) { + infPerDayStrA.put(LocalDate.parse("2020-01-01"), 0); + LocalDate strADate = LocalDate.parse("2022-11-01"); + for (int i = 0; i < 7; i++) { + infPerDayStrA.put(strADate.plusDays(i), 4); + } + infPerDayStrA.put(strADate.plusDays(7), 1); + } + + //StrainB +// if (!params.StrainB.equals("off")) { +// infPerDayStrB.put(LocalDate.parse("2020-01-01"), 0); +// LocalDate strBDate = LocalDate.parse("2022-11-01"); +// for (int i = 0; i < 7; i++) { +// infPerDayStrB.put(strBDate.plusDays(i), 4); +// } +// infPerDayStrB.put(strBDate.plusDays(7), 1); +// } + + + // add projected disease import for vacation waves after initial disease import + int facBa2 = 4; + int facBa5 = 4; + int facStrAB = 4; + + LocalDate dateBa2 = LocalDate.parse("2022-01-27"); // local min of disease import + LocalDate dateBa5 = LocalDate.parse("2022-05-01"); // after vaca import + LocalDate dateStrainAB = LocalDate.parse("2022-11-18"); // after vaca import + +// String importSummer2022 = "off"; +// if (importSummer2022.equals("on")) { +// NavigableMap data = DataUtils.readDiseaseImport(SnzCologneProductionScenario.INPUT.resolve("cologneDiseaseImport_Projected.csv")); +// LocalDate date = null; +// for (Map.Entry entry : data.entrySet()) { +// date = entry.getKey(); +// double factor = 0.25 * 2352476. / 919936.; //25% sample, data is given for Cologne City so we have to scale it to the whole model +//// +// double cases = factor * entry.getValue(); +// +// if (date.isAfter(dateStrainAB) && (!params.StrainA.equals("off") || !params.StrainB.equals("off"))) { +// if (!params.StrainA.equals("off") && !params.StrainB.equals("off")) { +// infPerDayStrA.put(date, ((int) cases * facStrAB) == 0 ? 1 : (int) (0.5 * cases * facStrAB)); +// infPerDayStrB.put(date, ((int) cases * facStrAB) == 0 ? 1 : (int) (0.5 * cases * facStrAB)); +// } +// else if (!params.StrainA.equals("off")) { +// infPerDayStrA.put(date, ((int) cases * facStrAB) == 0 ? 1 : (int) (cases * facStrAB)); +// } +// else if (!params.StrainB.equals("off")) { +// infPerDayStrB.put(date, ((int) cases * facStrAB) == 0 ? 1 : (int) (cases * facStrAB)); +// } +// else { +// throw new RuntimeException(); +// } +// infPerDayBa5.put(date, 1); +// infPerDayBa2.put(date, 1); +// } else if (date.isAfter(dateBa5)) { +// infPerDayBa5.put(date, ((int) cases * facBa5) == 0 ? 1 : (int) (cases * facBa5)); +// infPerDayBa2.put(date, 1); +// } else if (date.isAfter(dateBa2)) { +// infPerDayBa2.put(date, ((int) cases * facBa2) == 0 ? 1 : (int) (cases * facBa2)); +// } +// +// } +// } else if (importSummer2022.equals("off")) { +// } else { +// throw new RuntimeException(); +// } + + + // save disease import + episimConfig.setInfections_pers_per_day(VirusStrain.OMICRON_BA1, infPerDayBa1); + episimConfig.setInfections_pers_per_day(VirusStrain.OMICRON_BA2, infPerDayBa2); + episimConfig.setInfections_pers_per_day(VirusStrain.OMICRON_BA5, infPerDayBa5); + + if (!params.StrainA.equals("off")) { + episimConfig.setInfections_pers_per_day(VirusStrain.STRAIN_A, infPerDayStrA); + } +// if (!params.StrainB.equals("off")) { +// episimConfig.setInfections_pers_per_day(VirusStrain.STRAIN_B, infPerDayStrB); +// } + } + + public static final class Params { + // general + @GenerateSeeds(5) + public long seed; + + //IFSG + @StringParameter({"base","45", "90"}) + public String ifsg; + + // Vacciantion Campaign + @StringParameter({"base", "ph1_180", "ph1_180_ph2_inf90vax180"}) + String vacCamp; + + // NEW RESTRICTIONS + @StringParameter({"base", "half", "half&mask"}) + public String work; + + // leisure Public + @StringParameter({"base","pubPriv50", "pub50"}) + public String leis; + + //edu + @StringParameter({"base", "half&mask", "mask"}) + public String edu; + + @StringParameter({"6.0"}) + public String StrainA; + + + } + + + public static void main(String[] args) { + String[] args2 = { + RunParallel.OPTION_SETUP, CologneBMBF20221024.class.getName(), + RunParallel.OPTION_PARAMS, Params.class.getName(), + RunParallel.OPTION_TASKS, Integer.toString(1), + RunParallel.OPTION_ITERATIONS, Integer.toString(1000), + RunParallel.OPTION_METADATA + }; + + RunParallel.main(args2); + } + + +} + diff --git a/src/main/java/org/matsim/run/batch/CologneBMBF20221024_snapshot.java b/src/main/java/org/matsim/run/batch/CologneBMBF20221024_snapshot.java new file mode 100644 index 000000000..cccb69816 --- /dev/null +++ b/src/main/java/org/matsim/run/batch/CologneBMBF20221024_snapshot.java @@ -0,0 +1,723 @@ +package org.matsim.run.batch; + +import com.google.inject.AbstractModule; +import com.google.inject.Module; +import com.google.inject.Singleton; +import com.google.inject.multibindings.Multibinder; +import com.google.inject.util.Modules; +import org.matsim.core.config.Config; +import org.matsim.core.config.ConfigUtils; +import org.matsim.episim.BatchRun; +import org.matsim.episim.EpisimConfigGroup; +import org.matsim.episim.VirusStrainConfigGroup; +import org.matsim.episim.analysis.*; +import org.matsim.episim.model.*; +import org.matsim.episim.model.listener.HouseholdSusceptibility; +import org.matsim.episim.model.vaccination.VaccinationModel; +import org.matsim.episim.model.vaccination.VaccinationStrategyReoccurringCampaigns; +import org.matsim.episim.policy.FixedPolicy; +import org.matsim.episim.policy.Restriction; +import org.matsim.run.RunParallel; +import org.matsim.run.modules.SnzCologneProductionScenario; + +import javax.annotation.Nullable; +import java.time.LocalDate; +import java.util.*; + + +/** + * Batch for Bmbf runs + */ +public class CologneBMBF20221024_snapshot implements BatchRun { + + boolean DEBUG_MODE = false; + int runCount = 0; + + LocalDate restrictionDatePhase1 = LocalDate.parse("2022-12-01"); + LocalDate restrictionDatePhase2 = restrictionDatePhase1.plusDays(10); + + + @Nullable + @Override + public Module getBindings(int id, @Nullable Params params) { + return Modules.override(getBindings(0.0, params)).with(new AbstractModule() { + @Override + protected void configure() { + + // VACCINATION MODEL + Multibinder set = Multibinder.newSetBinder(binder(), VaccinationModel.class); + set.addBinding().to(VaccinationStrategyReoccurringCampaigns.class).in(Singleton.class); + // fixed values + LocalDate start = LocalDate.parse("2022-10-15"); + VaccinationType vaccinationType = VaccinationType.ba5Update; + int campaignDuration = 300000; + + // default values, to be changed if params != null + int minDaysAfterInfection = 180; + int minDaysAfterVaccination = 180; + VaccinationStrategyReoccurringCampaigns.Config.VaccinationPool vaccinationPool = VaccinationStrategyReoccurringCampaigns.Config.VaccinationPool.vaccinated; + LocalDate emergencyDate = LocalDate.MAX; + LocalDate dateToTurnDownMinDaysAfterInfection = LocalDate.MAX; + Map startDateToVaccination = new HashMap<>(); + startDateToVaccination.put(start, vaccinationType); + + if (params != null) { + if (params.vacCamp.equals("base")) { // + + + } else if(params.vacCamp.equals("ph1_90")){ + minDaysAfterInfection = 90; + minDaysAfterVaccination = 90; + + emergencyDate = restrictionDatePhase1; + } else if(params.vacCamp.equals("ph1_90vax180")){ + minDaysAfterInfection = 90; + emergencyDate = restrictionDatePhase1; + } else if(params.vacCamp.equals("ph1_180")){ // + + emergencyDate = restrictionDatePhase1; + } else if (params.vacCamp.equals("ph1_180_ph2_inf90vax180")) { + emergencyDate = restrictionDatePhase1; + dateToTurnDownMinDaysAfterInfection = restrictionDatePhase2; + // same as ifsg180 but after phase 2 date, minDaysAfterInfection = 90; + }else if(params.vacCamp.equals("ph2_90")){ + minDaysAfterInfection = 90; + minDaysAfterVaccination = 90; + emergencyDate = restrictionDatePhase2; + } else if(params.vacCamp.equals("ph2_inf90vax180")){ + minDaysAfterInfection = 90; + emergencyDate = restrictionDatePhase2; + } else if(params.vacCamp.equals("ph2_180")) { + emergencyDate = restrictionDatePhase2; + }else { + throw new RuntimeException(); + } + } + + bind(VaccinationStrategyReoccurringCampaigns.Config.class).toInstance(new VaccinationStrategyReoccurringCampaigns.Config(startDateToVaccination, campaignDuration, vaccinationPool, minDaysAfterInfection, minDaysAfterVaccination, emergencyDate, dateToTurnDownMinDaysAfterInfection)); + + + // ANTIBODY MODEL + // default values + double mutEscDelta = 29.2 / 10.9; + double mutEscBa1 = 10.9 / 1.9; + double mutEscBa5 = 5.0; + + double mutEscStrainA = 0.; + double mutEscStrainB = 0.; + + + if (params != null) { +// mutEscBa1 = params.ba1Esc; +// mutEscBa5 = params.ba5Esc; + +// String StrainA = "6.0"; + String StrainB = "off"; + + + if (!params.StrainA.equals("off")) { + mutEscStrainA = Double.parseDouble(params.StrainA); + } + if (!StrainB.equals("off")) { + mutEscStrainB = Double.parseDouble(StrainB); + } + + } + + //initial antibodies + Map> initialAntibodies = new HashMap<>(); + Map> antibodyRefreshFactors = new HashMap<>(); + configureAntibodies(initialAntibodies, antibodyRefreshFactors, mutEscDelta, mutEscBa1, mutEscBa5, mutEscStrainA, mutEscStrainB); + + AntibodyModel.Config antibodyConfig = new AntibodyModel.Config(initialAntibodies, antibodyRefreshFactors); + + + double immuneSigma = 0.0; + if (params != null) { + immuneSigma = params.immuneSigma; + } + antibodyConfig.setImmuneReponseSigma(immuneSigma); + + bind(AntibodyModel.Config.class).toInstance(antibodyConfig); + + if (params == null) return; + + + + // HOUSEHOLD SUSCEPTIBILITY + // designates a 35% of households as super safe; the susceptibility of that subpopulation is reduced to 1% wrt to general population. + double pHouseholds = params.pHh; + bind(HouseholdSusceptibility.Config.class).toInstance( + HouseholdSusceptibility.newConfig() + .withSusceptibleHouseholds(pHouseholds, 0.01) +// .withNonVaccinableHouseholds(params.nonVaccinableHh) +// .withShape(SnzCologneProductionScenario.INPUT.resolve("CologneDistricts.zip")) +// .withFeature("STT_NAME", vingst, altstadtNord, bickendorf, weiden) + ); + + } + + private void configureAntibodies(Map> initialAntibodies, + Map> antibodyRefreshFactors, + double mutEscDelta, double mutEscBa1, double mutEscBa5, double mutEscStrainA, double mutEscStrainB) { + for (VaccinationType immunityType : VaccinationType.values()) { + initialAntibodies.put(immunityType, new EnumMap<>( VirusStrain.class ) ); + for (VirusStrain virusStrain : VirusStrain.values()) { + + if (immunityType == VaccinationType.mRNA) { + initialAntibodies.get(immunityType).put(virusStrain, 29.2); //10.0 + } + else if (immunityType == VaccinationType.vector) { + initialAntibodies.get(immunityType).put(virusStrain, 6.8); //2.5 + } + else { + initialAntibodies.get(immunityType).put(virusStrain, 5.0); + } + } + } + + for (VirusStrain immunityType : VirusStrain.values()) { + initialAntibodies.put(immunityType, new EnumMap<>( VirusStrain.class ) ); + for (VirusStrain virusStrain : VirusStrain.values()) { + initialAntibodies.get(immunityType).put(virusStrain, 5.0); + } + } + + //mRNAAlpha, mRNADelta, mRNABA1 comes from Sydney's calibration. + //The other values come from Rössler et al. + + //Wildtype + double mRNAAlpha = 29.2; + initialAntibodies.get(VaccinationType.mRNA).put(VirusStrain.SARS_CoV_2, mRNAAlpha); + initialAntibodies.get(VaccinationType.vector).put(VirusStrain.SARS_CoV_2, mRNAAlpha * 210. / 700.); + initialAntibodies.get(VirusStrain.SARS_CoV_2).put(VirusStrain.SARS_CoV_2, mRNAAlpha * 300. / 700.); + initialAntibodies.get(VirusStrain.ALPHA).put(VirusStrain.SARS_CoV_2, mRNAAlpha * 300. / 700.); + initialAntibodies.get(VirusStrain.DELTA).put(VirusStrain.SARS_CoV_2, mRNAAlpha * 210. / 700.); + initialAntibodies.get(VirusStrain.OMICRON_BA1).put(VirusStrain.SARS_CoV_2, 0.01); + initialAntibodies.get(VirusStrain.OMICRON_BA2).put(VirusStrain.SARS_CoV_2, 0.01); + initialAntibodies.get(VirusStrain.OMICRON_BA5).put(VirusStrain.SARS_CoV_2, 0.01); + initialAntibodies.get(VirusStrain.STRAIN_A).put(VirusStrain.SARS_CoV_2, 0.01); + initialAntibodies.get(VirusStrain.STRAIN_B).put(VirusStrain.SARS_CoV_2, 0.01); + initialAntibodies.get(VaccinationType.ba1Update).put(VirusStrain.SARS_CoV_2, mRNAAlpha / mutEscDelta / mutEscBa1); + initialAntibodies.get(VaccinationType.ba5Update).put(VirusStrain.SARS_CoV_2, mRNAAlpha / mutEscDelta / mutEscBa1 / mutEscBa5); + + //Alpha + initialAntibodies.get(VaccinationType.mRNA).put(VirusStrain.ALPHA, mRNAAlpha); + initialAntibodies.get(VaccinationType.vector).put(VirusStrain.ALPHA, mRNAAlpha * 210. / 700.); + initialAntibodies.get(VirusStrain.SARS_CoV_2).put(VirusStrain.ALPHA, mRNAAlpha * 300. / 700.); + initialAntibodies.get(VirusStrain.ALPHA).put(VirusStrain.ALPHA, mRNAAlpha * 300. / 700.); + initialAntibodies.get(VirusStrain.DELTA).put(VirusStrain.ALPHA, mRNAAlpha * 210. / 700.); + initialAntibodies.get(VirusStrain.OMICRON_BA1).put(VirusStrain.ALPHA, 0.01); + initialAntibodies.get(VirusStrain.OMICRON_BA2).put(VirusStrain.ALPHA, 0.01); + initialAntibodies.get(VirusStrain.OMICRON_BA5).put(VirusStrain.ALPHA, 0.01); + initialAntibodies.get(VirusStrain.STRAIN_A).put(VirusStrain.ALPHA, 0.01); + initialAntibodies.get(VirusStrain.STRAIN_B).put(VirusStrain.ALPHA, 0.01); + initialAntibodies.get(VaccinationType.ba1Update).put(VirusStrain.ALPHA, mRNAAlpha / mutEscDelta / mutEscBa1); + initialAntibodies.get(VaccinationType.ba5Update).put(VirusStrain.ALPHA, mRNAAlpha / mutEscDelta / mutEscBa1 / mutEscBa5); + + //DELTA + double mRNADelta = mRNAAlpha / mutEscDelta; + initialAntibodies.get(VaccinationType.mRNA).put(VirusStrain.DELTA, mRNADelta); + initialAntibodies.get(VaccinationType.vector).put(VirusStrain.DELTA, mRNADelta * 150./300.); + initialAntibodies.get(VirusStrain.SARS_CoV_2).put(VirusStrain.DELTA, mRNADelta * 64./300.); + initialAntibodies.get(VirusStrain.ALPHA).put(VirusStrain.DELTA, mRNADelta * 64./300.); + initialAntibodies.get(VirusStrain.DELTA).put(VirusStrain.DELTA, mRNADelta * 450./300.); + initialAntibodies.get(VirusStrain.OMICRON_BA1).put(VirusStrain.DELTA, 0.01); + initialAntibodies.get(VirusStrain.OMICRON_BA2).put(VirusStrain.DELTA, 0.01); + initialAntibodies.get(VirusStrain.OMICRON_BA5).put(VirusStrain.DELTA, 0.01); + initialAntibodies.get(VirusStrain.STRAIN_A).put(VirusStrain.DELTA, 0.01); + initialAntibodies.get(VirusStrain.STRAIN_B).put(VirusStrain.DELTA, 0.01); + initialAntibodies.get(VaccinationType.ba1Update).put(VirusStrain.DELTA, mRNADelta / mutEscBa1); + initialAntibodies.get(VaccinationType.ba5Update).put(VirusStrain.DELTA, mRNADelta / mutEscBa1 / mutEscBa5); + + //BA.1 + double mRNABA1 = mRNADelta / mutEscBa1; + initialAntibodies.get(VaccinationType.mRNA).put(VirusStrain.OMICRON_BA1, mRNABA1); + initialAntibodies.get(VaccinationType.vector).put(VirusStrain.OMICRON_BA1, mRNABA1 * 4./20.); //??? + initialAntibodies.get(VirusStrain.SARS_CoV_2).put(VirusStrain.OMICRON_BA1, mRNABA1 * 6./20.); + initialAntibodies.get(VirusStrain.ALPHA).put(VirusStrain.OMICRON_BA1, mRNABA1 * 6./20.); + initialAntibodies.get(VirusStrain.DELTA).put(VirusStrain.OMICRON_BA1, mRNABA1 * 8./20.); + initialAntibodies.get(VirusStrain.OMICRON_BA1).put(VirusStrain.OMICRON_BA1, 64.0 / 300.); + initialAntibodies.get(VirusStrain.OMICRON_BA2).put(VirusStrain.OMICRON_BA1, 64.0 / 300. / 1.4); + initialAntibodies.get(VirusStrain.OMICRON_BA5).put(VirusStrain.OMICRON_BA1, 64.0 / 300. / mutEscBa5); //todo: is 1.4 + initialAntibodies.get(VirusStrain.STRAIN_A).put(VirusStrain.OMICRON_BA1, 64.0 / 300. / mutEscBa5 / mutEscStrainA); + initialAntibodies.get(VirusStrain.STRAIN_B).put(VirusStrain.OMICRON_BA1, 64.0 / 300. / mutEscStrainB); + initialAntibodies.get(VaccinationType.ba1Update).put(VirusStrain.OMICRON_BA1, mRNAAlpha); + initialAntibodies.get(VaccinationType.ba5Update).put(VirusStrain.OMICRON_BA1, mRNAAlpha / mutEscBa5); + + //BA.2 + double mRNABA2 = mRNABA1; + initialAntibodies.get(VaccinationType.mRNA).put(VirusStrain.OMICRON_BA2, mRNABA2); + initialAntibodies.get(VaccinationType.vector).put(VirusStrain.OMICRON_BA2, mRNABA2 * 4./20.); + initialAntibodies.get(VirusStrain.SARS_CoV_2).put(VirusStrain.OMICRON_BA2, mRNABA2 * 6./20.); + initialAntibodies.get(VirusStrain.ALPHA).put(VirusStrain.OMICRON_BA2, mRNABA2 * 6./20.); + initialAntibodies.get(VirusStrain.DELTA).put(VirusStrain.OMICRON_BA2, mRNABA2 * 8./20.); + initialAntibodies.get(VirusStrain.OMICRON_BA1).put(VirusStrain.OMICRON_BA2, 64.0 / 300. / 1.4); + initialAntibodies.get(VirusStrain.OMICRON_BA2).put(VirusStrain.OMICRON_BA2, 64.0 / 300.); + initialAntibodies.get(VirusStrain.OMICRON_BA5).put(VirusStrain.OMICRON_BA2, 64.0 / 300. / mutEscBa5); + initialAntibodies.get(VirusStrain.STRAIN_A).put(VirusStrain.OMICRON_BA2, 64.0 / 300. / mutEscBa5 / mutEscStrainA); + initialAntibodies.get(VirusStrain.STRAIN_B).put(VirusStrain.OMICRON_BA2, 64.0 / 300. / mutEscStrainB); + initialAntibodies.get(VaccinationType.ba1Update).put(VirusStrain.OMICRON_BA2, mRNAAlpha); + initialAntibodies.get(VaccinationType.ba5Update).put(VirusStrain.OMICRON_BA2, mRNAAlpha / mutEscBa5); + + + //BA.5 + double mRNABa5 = mRNABA2 / mutEscBa5; + initialAntibodies.get(VaccinationType.mRNA).put(VirusStrain.OMICRON_BA5, mRNABa5); + initialAntibodies.get(VaccinationType.vector).put(VirusStrain.OMICRON_BA5, mRNABa5 * 4./20.); + initialAntibodies.get(VirusStrain.SARS_CoV_2).put(VirusStrain.OMICRON_BA5, mRNABa5 * 6./20.); + initialAntibodies.get(VirusStrain.ALPHA).put(VirusStrain.OMICRON_BA5, mRNABa5 * 6./20.); + initialAntibodies.get(VirusStrain.DELTA).put(VirusStrain.OMICRON_BA5, mRNABa5 * 8./20.); + initialAntibodies.get(VirusStrain.OMICRON_BA1).put(VirusStrain.OMICRON_BA5, 64.0 / 300. / mutEscBa5);// todo: do we need 1.4? + initialAntibodies.get(VirusStrain.OMICRON_BA2).put(VirusStrain.OMICRON_BA5, 64.0 / 300./ mutEscBa5); + initialAntibodies.get(VirusStrain.OMICRON_BA5).put(VirusStrain.OMICRON_BA5, 64.0 / 300.); + initialAntibodies.get(VirusStrain.STRAIN_A).put(VirusStrain.OMICRON_BA5, 64.0 / 300./ mutEscStrainA); //todo ??? + initialAntibodies.get(VirusStrain.STRAIN_B).put(VirusStrain.OMICRON_BA5, 64.0 / 300. / mutEscBa5 / mutEscStrainB); + initialAntibodies.get(VaccinationType.ba1Update).put(VirusStrain.OMICRON_BA5, mRNAAlpha / mutEscBa5); + initialAntibodies.get(VaccinationType.ba5Update).put(VirusStrain.OMICRON_BA5, mRNAAlpha); + + //StrainA + double mRNAStrainA = mRNABa5 / mutEscStrainA; + initialAntibodies.get(VaccinationType.mRNA).put(VirusStrain.STRAIN_A, mRNAStrainA); + initialAntibodies.get(VaccinationType.vector).put(VirusStrain.STRAIN_A, mRNAStrainA * 4./20.); + initialAntibodies.get(VirusStrain.SARS_CoV_2).put(VirusStrain.STRAIN_A, mRNAStrainA * 6./20.); + initialAntibodies.get(VirusStrain.ALPHA).put(VirusStrain.STRAIN_A, mRNAStrainA * 6./20.); + initialAntibodies.get(VirusStrain.DELTA).put(VirusStrain.STRAIN_A, mRNAStrainA * 8./20.); + initialAntibodies.get(VirusStrain.OMICRON_BA1).put(VirusStrain.STRAIN_A, 64.0 / 300. / mutEscBa5 /mutEscStrainA); + initialAntibodies.get(VirusStrain.OMICRON_BA2).put(VirusStrain.STRAIN_A, 64.0 / 300./ mutEscBa5 /mutEscStrainA); + initialAntibodies.get(VirusStrain.OMICRON_BA5).put(VirusStrain.STRAIN_A, 64.0 / 300. / mutEscStrainA); + initialAntibodies.get(VirusStrain.STRAIN_A).put(VirusStrain.STRAIN_A, 64.0 / 300.); + initialAntibodies.get(VirusStrain.STRAIN_B).put(VirusStrain.STRAIN_A, 64.0 / 300. / mutEscStrainA / mutEscStrainB / mutEscBa5); + initialAntibodies.get(VaccinationType.ba1Update).put(VirusStrain.STRAIN_A, mRNAAlpha / mutEscBa5 / mutEscStrainA); + initialAntibodies.get(VaccinationType.ba5Update).put(VirusStrain.STRAIN_A, mRNAAlpha / mutEscStrainA); + + //StrainB + double mRNAStrainB = mRNABA2 / mutEscStrainB; + initialAntibodies.get(VaccinationType.mRNA).put(VirusStrain.STRAIN_B, mRNAStrainB); + initialAntibodies.get(VaccinationType.vector).put(VirusStrain.STRAIN_B, mRNAStrainB * 4./20.); + initialAntibodies.get(VirusStrain.SARS_CoV_2).put(VirusStrain.STRAIN_B, mRNAStrainB * 6./20.); + initialAntibodies.get(VirusStrain.ALPHA).put(VirusStrain.STRAIN_B, mRNAStrainB * 6./20.); + initialAntibodies.get(VirusStrain.DELTA).put(VirusStrain.STRAIN_B, mRNAStrainB * 8./20.); + initialAntibodies.get(VirusStrain.OMICRON_BA1).put(VirusStrain.STRAIN_B, 64.0 / 300. / mutEscStrainB); + initialAntibodies.get(VirusStrain.OMICRON_BA2).put(VirusStrain.STRAIN_B, 64.0 / 300./ mutEscStrainB); + initialAntibodies.get(VirusStrain.OMICRON_BA5).put(VirusStrain.STRAIN_B, 64.0 / 300. / mutEscStrainB / mutEscBa5); + initialAntibodies.get(VirusStrain.STRAIN_A).put(VirusStrain.STRAIN_B, 64.0 / 300./ mutEscStrainA / mutEscStrainB / mutEscBa5); + initialAntibodies.get(VirusStrain.STRAIN_B).put(VirusStrain.STRAIN_B, 64.0 / 300.); + initialAntibodies.get(VaccinationType.ba1Update).put(VirusStrain.STRAIN_B, mRNAAlpha / mutEscStrainB); + initialAntibodies.get(VaccinationType.ba5Update).put(VirusStrain.STRAIN_B, mRNAAlpha / mutEscStrainB / mutEscBa5); + + + for (VaccinationType immunityType : VaccinationType.values()) { + antibodyRefreshFactors.put(immunityType, new EnumMap<>( VirusStrain.class ) ); + for (VirusStrain virusStrain : VirusStrain.values()) { + + if (immunityType == VaccinationType.mRNA) { + antibodyRefreshFactors.get(immunityType).put(virusStrain, 15.0); + } + else if (immunityType == VaccinationType.vector) { + antibodyRefreshFactors.get(immunityType).put(virusStrain, 5.0); + } + else if (immunityType == VaccinationType.ba1Update) { + antibodyRefreshFactors.get(immunityType).put(virusStrain, 15.0); + } + else if (immunityType == VaccinationType.ba5Update) { + antibodyRefreshFactors.get(immunityType).put(virusStrain, 15.0); + } + else { + antibodyRefreshFactors.get(immunityType).put(virusStrain, Double.NaN); + } + + } + } + + for (VirusStrain immunityType : VirusStrain.values()) { + antibodyRefreshFactors.put(immunityType, new EnumMap<>( VirusStrain.class ) ); + for (VirusStrain virusStrain : VirusStrain.values()) { + antibodyRefreshFactors.get(immunityType).put(virusStrain, 15.0); + } + } + + +// UtilsJR.printInitialAntibodiesToConsole(initialAntibodies); + + } + }); + + } + + private SnzCologneProductionScenario getBindings(double pHousehold, Params params) { + return new SnzCologneProductionScenario.Builder() + .setCarnivalModel(SnzCologneProductionScenario.CarnivalModel.yes) + .setSebastianUpdate(true) + .setLeisureCorrection(1.3) //params == null ? 0.0 : params.actCorrection) + .setScaleForActivityLevels(1.3) + .setSuscHouseholds_pct(pHousehold) + .setActivityHandling(EpisimConfigGroup.ActivityHandling.startOfDay) +// .setTestingModel(params != null ? FlexibleTestingModel.class : DefaultTestingModel.class) + .setInfectionModel(InfectionModelWithAntibodies.class) + .build(); + } + + @Override + public Metadata getMetadata() { + return Metadata.of("cologne", "calibration"); + } + + @Override + public Collection postProcessing() { + return List.of( +// new VaccinationEffectiveness().withArgs(), +// new RValuesFromEvents().withArgs(), +// new VaccinationEffectivenessFromPotentialInfections().withArgs("--remove-infected"), + new FilterEvents().withArgs("--output","./output/") +// new HospitalNumbersFromEvents().withArgs("--output","./output/","--input","/scratch/projects/bzz0020/episim-input") +// new SecondaryAttackRateFromEvents().withArgs() + ); + } + + @Override + public Config prepareConfig(int id, Params params) { + + if (DEBUG_MODE) { + if (runCount == 0 && params.vacCamp.equals("emergency180")) { //&& params.strAEsc != 0.0 && params.ba5Inf == 0. && params.eduTest.equals("true")) { + runCount++; + } else { + return null; + } + } + + SnzCologneProductionScenario module = getBindings(0.0, params); + + Config config = module.config(); + + config.global().setRandomSeed(params.seed); + + EpisimConfigGroup episimConfig = ConfigUtils.addOrGetModule(config, EpisimConfigGroup.class); + + episimConfig.setCalibrationParameter(episimConfig.getCalibrationParameter() * 1.2 * 1.7); + + + if (params.simStart.equals("createSnapshot")) { + episimConfig.setSnapshotInterval(630); + episimConfig.setSnapshotPrefix(params.immuneSigma + "-" + params.pHh); + } else if (params.simStart.equals("startFromSnapshot")) { + episimConfig.setStartFromSnapshot("/scratch/projects/bzz0020/runs/jakob/2022-11-05/1-snap/snapshot/" + params.immuneSigma + "-" + params.pHh + "-630-2021-11-15.zip"); + episimConfig.setSnapshotSeed(EpisimConfigGroup.SnapshotSeed.reseed); + } else { + episimConfig.setStartDate(LocalDate.parse(params.simStart)); + episimConfig.setStartFromImmunization("/scratch/projects/bzz0020/runs/jakob/2022-11-05/1-snap/imm-history-640/"+params.immuneSigma + "-" + params.pHh+"/"); + } + + //snapshot +// episimConfig.setSnapshotInterval(90); +// episimConfig.setSnapshotPrefix(String.valueOf(params.seed)); +// episimConfig.setStartFromSnapshot("/scratch/projects/bzz0020/episim-input/snapshots-cologne-2022-10-18/" + params.seed + "-960-2022-10-11.zip"); +// episimConfig.setSnapshotSeed(EpisimConfigGroup.SnapshotSeed.restore); + //--------------------------------------- + // S T R A I N S + //--------------------------------------- + + VirusStrainConfigGroup virusStrainConfigGroup = ConfigUtils.addOrGetModule(config, VirusStrainConfigGroup.class); + + double ba5Inf = virusStrainConfigGroup.getOrAddParams(VirusStrain.OMICRON_BA5).getInfectiousness(); + double ba5Hos = virusStrainConfigGroup.getOrAddParams(VirusStrain.OMICRON_BA5).getFactorSeriouslySick(); + +// STRAIN_A + if (!params.StrainA.equals("off")) { + + virusStrainConfigGroup.getOrAddParams(VirusStrain.STRAIN_A).setInfectiousness(ba5Inf); + virusStrainConfigGroup.getOrAddParams(VirusStrain.STRAIN_A).setFactorSeriouslySick(ba5Hos); + virusStrainConfigGroup.getOrAddParams(VirusStrain.STRAIN_A).setFactorSeriouslySickVaccinated(ba5Hos); + virusStrainConfigGroup.getOrAddParams(VirusStrain.STRAIN_A).setFactorCritical(ba5Hos); + } + +// STRAIN_B +// if (!params.StrainB.equals("off")) { +// +// virusStrainConfigGroup.getOrAddParams(VirusStrain.STRAIN_B).setInfectiousness(virusStrainConfigGroup.getParams(VirusStrain.OMICRON_BA5).getInfectiousness() * ba5Inf); +// virusStrainConfigGroup.getOrAddParams(VirusStrain.STRAIN_B).setFactorSeriouslySick(ba5Hos); +// virusStrainConfigGroup.getOrAddParams(VirusStrain.STRAIN_B).setFactorSeriouslySickVaccinated(ba5Hos); +// virusStrainConfigGroup.getOrAddParams(VirusStrain.STRAIN_B).setFactorCritical(ba5Hos); +// } + + //--------------------------------------- + // I M P O R T + //--------------------------------------- + + configureFutureDiseaseImport(params, episimConfig); + + // modify import: + LocalDate impModDate = LocalDate.parse("2022-01-31"); + double impRedBa1 = 0.0; + double impRedBa2 = 0.0; + if (impRedBa1 != 1.0) { + NavigableMap impBa1 = episimConfig.getInfections_pers_per_day().get(VirusStrain.OMICRON_BA1); + for (Map.Entry entry : impBa1.entrySet()) { + if (entry.getKey().isAfter(impModDate)) { + impBa1.put(entry.getKey(), (int) (entry.getValue() * impRedBa1)); + } + } + } + + if (impRedBa2 != 1.0) { + NavigableMap impBa2 = episimConfig.getInfections_pers_per_day().get(VirusStrain.OMICRON_BA2); + for (Map.Entry entry : impBa2.entrySet()) { + if (entry.getKey().isAfter(impModDate)) { + impBa2.put(entry.getKey(), (int) (entry.getValue() * impRedBa2)); + } + } + } + + + + //--------------------------------------- + // R E S T R I C T I O N S + //--------------------------------------- + + FixedPolicy.ConfigBuilder builder = FixedPolicy.parse(episimConfig.getPolicy()); + + + + //ifsg + if ("base".equals(params.ifsg)) { + + } else if ("45".equals(params.ifsg) || "90".equals(params.ifsg)) { + double compliance = Double.parseDouble(params.ifsg) / 100.; + builder.restrict(restrictionDatePhase1, Restriction.ofMask(Map.of( + FaceMask.CLOTH, 0.0, + FaceMask.SURGICAL, compliance)), + "educ_secondary", "educ_tertiary", "educ_other", "educ_higher"); + builder.restrict(restrictionDatePhase1, Restriction.ofMask(Map.of(FaceMask.N95, compliance)), "leisPublic"); + builder.restrict(restrictionDatePhase1, Restriction.ofMask(Map.of(FaceMask.N95, compliance)), "shop_daily", "shop_other", "errands"); + builder.restrict(restrictionDatePhase1, Restriction.ofMask(Map.of(FaceMask.N95, 0.9)), "pt"); // pt has 90 compliance either way + } else { + throw new RuntimeException(); + } + + // EMERGENCY RESTRICTIONS + //work + builder.restrict(LocalDate.parse("2022-10-15"), 0.88, "work", "business"); + double homeOfficeFactor = 0.5; + switch (params.work) { + case "base": + break; + case "half": + builder.restrict(restrictionDatePhase2, 0.88 * homeOfficeFactor, "work"); // dont include business bc harder to do from home office + builder.applyToRf(restrictionDatePhase2.plusDays(1).toString(), restrictionDatePhase2.plusDays(1000).toString(), (d, rf) -> rf * homeOfficeFactor, "work"); + break; + case "half&mask": + builder.restrict(restrictionDatePhase2, 0.88 * homeOfficeFactor, "work"); // dont include business bc harder to do from home office + builder.applyToRf(restrictionDatePhase2.plusDays(1).toString(), restrictionDatePhase2.plusDays(1000).toString(), (d, rf) -> rf * homeOfficeFactor, "work"); + + builder.restrict(restrictionDatePhase2, Restriction.ofMask(Map.of(FaceMask.SURGICAL, 0.0, FaceMask.N95, 0.9)), "work", "business"); + break; + default: + throw new RuntimeException("invalid parameter"); + } + + // leisure public + private + switch (params.leis) { + case "base": + break; + case "pub50": + builder.restrict(restrictionDatePhase2, 0.88 * 0.5, "leisPublic"); + break; + case "pubPriv50": + builder.restrict(restrictionDatePhase2, 0.88 * 0.5, "leisPublic", "leisPrivate"); + break; + default: + throw new RuntimeException("invalid parameter"); + } + + //school + switch (params.edu) { + case "base": + break; + case "mask": + builder.restrict(restrictionDatePhase2, Restriction.ofMask(Map.of( + FaceMask.CLOTH, 0.0, + FaceMask.SURGICAL, 0.0, + FaceMask.N95, 0.90)), + "educ_secondary", "educ_tertiary", "educ_other", "educ_higher"); + break; + case "half&mask": + builder.restrict(restrictionDatePhase2, Restriction.ofMask(Map.of( + FaceMask.CLOTH, 0.0, + FaceMask.SURGICAL, 0.0, + FaceMask.N95, 0.90)), + "educ_secondary", "educ_tertiary", "educ_other", "educ_higher"); + + builder.restrict(restrictionDatePhase2, 0.5, "educ_primary", "educ_kiga", "educ_secondary", "educ_tertiary", "educ_other", "educ_higher"); + builder.applyToRf(restrictionDatePhase2.plusDays(1).toString(), restrictionDatePhase2.plusDays(1000).toString(), (d, rf) -> Math.min(0.5, rf), "educ_primary", "educ_kiga", "educ_secondary", "educ_tertiary", "educ_other", "educ_higher"); + break; + default: + throw new RuntimeException("invalid parameter"); + } + + // vary amount of "school" activity that takes place during vacation + builder.restrict(LocalDate.parse("2022-06-27"), 0.8, "educ_primary", "educ_kiga", "educ_secondary", "educ_tertiary", "educ_other"); + + episimConfig.setPolicy(builder.build()); + + + //--------------------------------------- + // M I S C + //--------------------------------------- + + + if (DEBUG_MODE) { + UtilsJR.produceDiseaseImportPlot(episimConfig.getInfections_pers_per_day()); + } + + return config; + } + + private void configureFutureDiseaseImport(Params params, EpisimConfigGroup episimConfig) { + Map infPerDayBa1 = new HashMap<>(episimConfig.getInfections_pers_per_day().getOrDefault(VirusStrain.OMICRON_BA1, new TreeMap<>())); + Map infPerDayBa2 = new HashMap<>(episimConfig.getInfections_pers_per_day().getOrDefault(VirusStrain.OMICRON_BA2, new TreeMap<>())); + Map infPerDayBa5 = new HashMap<>(episimConfig.getInfections_pers_per_day().getOrDefault(VirusStrain.OMICRON_BA5, new TreeMap<>())); + Map infPerDayStrA = new HashMap<>(episimConfig.getInfections_pers_per_day().getOrDefault(VirusStrain.STRAIN_A, new TreeMap<>())); +// Map infPerDayStrB = new HashMap<>(episimConfig.getInfections_pers_per_day().getOrDefault(VirusStrain.STRAIN_B, new TreeMap<>())); + + //StrainA + if (!params.StrainA.equals("off")) { + infPerDayStrA.put(LocalDate.parse("2020-01-01"), 0); +// LocalDate strADate = LocalDate.parse("2022-11-01"); + LocalDate strADate = LocalDate.parse(params.strainADate); + + for (int i = 0; i < 7; i++) { + infPerDayStrA.put(strADate.plusDays(i), 4); + } + infPerDayStrA.put(strADate.plusDays(7), 1); + } + + //StrainB +// if (!params.StrainB.equals("off")) { +// infPerDayStrB.put(LocalDate.parse("2020-01-01"), 0); +// LocalDate strBDate = LocalDate.parse("2022-11-01"); +// for (int i = 0; i < 7; i++) { +// infPerDayStrB.put(strBDate.plusDays(i), 4); +// } +// infPerDayStrB.put(strBDate.plusDays(7), 1); +// } + + + // add projected disease import for vacation waves after initial disease import +// int facBa2 = 4; +// int facBa5 = 4; +// int facStrAB = 4; +// +// LocalDate dateBa2 = LocalDate.parse("2022-01-27"); // local min of disease import +// LocalDate dateBa5 = LocalDate.parse("2022-05-01"); // after vaca import +// LocalDate dateStrainAB = LocalDate.parse("2022-11-18"); // after vaca import + +// String importSummer2022 = "off"; +// if (importSummer2022.equals("on")) { +// NavigableMap data = DataUtils.readDiseaseImport(SnzCologneProductionScenario.INPUT.resolve("cologneDiseaseImport_Projected.csv")); +// LocalDate date = null; +// for (Map.Entry entry : data.entrySet()) { +// date = entry.getKey(); +// double factor = 0.25 * 2352476. / 919936.; //25% sample, data is given for Cologne City so we have to scale it to the whole model +//// +// double cases = factor * entry.getValue(); +// +// if (date.isAfter(dateStrainAB) && (!params.StrainA.equals("off") || !params.StrainB.equals("off"))) { +// if (!params.StrainA.equals("off") && !params.StrainB.equals("off")) { +// infPerDayStrA.put(date, ((int) cases * facStrAB) == 0 ? 1 : (int) (0.5 * cases * facStrAB)); +// infPerDayStrB.put(date, ((int) cases * facStrAB) == 0 ? 1 : (int) (0.5 * cases * facStrAB)); +// } +// else if (!params.StrainA.equals("off")) { +// infPerDayStrA.put(date, ((int) cases * facStrAB) == 0 ? 1 : (int) (cases * facStrAB)); +// } +// else if (!params.StrainB.equals("off")) { +// infPerDayStrB.put(date, ((int) cases * facStrAB) == 0 ? 1 : (int) (cases * facStrAB)); +// } +// else { +// throw new RuntimeException(); +// } +// infPerDayBa5.put(date, 1); +// infPerDayBa2.put(date, 1); +// } else if (date.isAfter(dateBa5)) { +// infPerDayBa5.put(date, ((int) cases * facBa5) == 0 ? 1 : (int) (cases * facBa5)); +// infPerDayBa2.put(date, 1); +// } else if (date.isAfter(dateBa2)) { +// infPerDayBa2.put(date, ((int) cases * facBa2) == 0 ? 1 : (int) (cases * facBa2)); +// } +// +// } +// } else if (importSummer2022.equals("off")) { +// } else { +// throw new RuntimeException(); +// } + + + // save disease import + episimConfig.setInfections_pers_per_day(VirusStrain.OMICRON_BA1, infPerDayBa1); + episimConfig.setInfections_pers_per_day(VirusStrain.OMICRON_BA2, infPerDayBa2); + episimConfig.setInfections_pers_per_day(VirusStrain.OMICRON_BA5, infPerDayBa5); + + if (!params.StrainA.equals("off")) { + episimConfig.setInfections_pers_per_day(VirusStrain.STRAIN_A, infPerDayStrA); + } +// if (!params.StrainB.equals("off")) { +// episimConfig.setInfections_pers_per_day(VirusStrain.STRAIN_B, infPerDayStrB); +// } + } + + public static final class Params { + // general + @GenerateSeeds(20) + public long seed; + + @Parameter({0.0, 3.0}) + public double immuneSigma; + + @Parameter({0.0, 0.35}) + public double pHh; + + @StringParameter({"2021-11-15"})//"2021-11-15"}) //"startFromSnapshot"})//"createSnapshot","2020-06-01", "2020-12-01", "2021-06-01", "2021-12-01", "2022-06-01", "2022-09-15"}) + public String simStart; + + //IFSG + @StringParameter({"base"}) + public String ifsg; + + // Vaccination Campaign + @StringParameter({"base"}) + String vacCamp; + + // NEW RESTRICTIONS + @StringParameter({"base"}) + public String work; + + // leisure Public + @StringParameter({"base"}) + public String leis; + + //edu + @StringParameter({"base"}) + public String edu; + + @StringParameter({"3.0"}) + public String StrainA; + + @StringParameter({"2022-09-19"}) + public String strainADate; + + + } + + + public static void main(String[] args) { + String[] args2 = { + RunParallel.OPTION_SETUP, CologneBMBF20221024_snapshot.class.getName(), + RunParallel.OPTION_PARAMS, Params.class.getName(), + RunParallel.OPTION_TASKS, Integer.toString(1), + RunParallel.OPTION_ITERATIONS, Integer.toString(1000), + RunParallel.OPTION_METADATA + }; + + RunParallel.main(args2); + } + + +} + diff --git a/src/main/java/org/matsim/run/batch/CologneBMBF202212XX_bq1.java b/src/main/java/org/matsim/run/batch/CologneBMBF202212XX_bq1.java new file mode 100644 index 000000000..a29b25077 --- /dev/null +++ b/src/main/java/org/matsim/run/batch/CologneBMBF202212XX_bq1.java @@ -0,0 +1,642 @@ +package org.matsim.run.batch; + +import com.google.inject.AbstractModule; +import com.google.inject.Module; +import com.google.inject.Singleton; +import com.google.inject.multibindings.Multibinder; +import com.google.inject.util.Modules; +import org.matsim.core.config.Config; +import org.matsim.core.config.ConfigUtils; +import org.matsim.episim.*; +import org.matsim.episim.analysis.*; +import org.matsim.episim.model.*; +import org.matsim.episim.model.listener.HouseholdSusceptibility; +import org.matsim.episim.model.vaccination.VaccinationModel; +import org.matsim.episim.model.vaccination.VaccinationStrategyReoccurringCampaigns; +import org.matsim.episim.policy.FixedPolicy; +import org.matsim.episim.policy.Restriction; +import org.matsim.run.RunParallel; +import org.matsim.run.modules.SnzCologneProductionScenario; + +import javax.annotation.Nullable; +import java.time.LocalDate; +import java.util.*; + + +/** + * Batch for Bmbf runs + */ +public class CologneBMBF202212XX_bq1 implements BatchRun { + + boolean DEBUG_MODE = false; + + String START_DATE = "2022-04-01"; + int runCount = 0; + + LocalDate restrictionDatePhase1 = LocalDate.parse("2022-12-01"); + LocalDate restrictionDatePhase2 = restrictionDatePhase1.plusDays(10); + + @Nullable + @Override + public Module getBindings(int id, @Nullable Params params) { + return Modules.override(getBindings(0.0, params)).with(new AbstractModule() { + @Override + protected void configure() { + + // VACCINATION MODEL + Multibinder set = Multibinder.newSetBinder(binder(), VaccinationModel.class); + set.addBinding().to(VaccinationStrategyReoccurringCampaigns.class).in(Singleton.class); + // fixed values + LocalDate start = LocalDate.parse("2022-10-15"); + VaccinationType vaccinationType = VaccinationType.ba5Update; + int campaignDuration = 300000; + + // default values, to be changed if params != null + int minDaysAfterInfection = 180; + int minDaysAfterVaccination = 180; + VaccinationStrategyReoccurringCampaigns.Config.VaccinationPool vaccinationPool = VaccinationStrategyReoccurringCampaigns.Config.VaccinationPool.vaccinated; + LocalDate emergencyDate = LocalDate.MAX; + LocalDate dateToTurnDownMinDaysAfterInfection = LocalDate.MAX; + Map startDateToVaccination = new HashMap<>(); + startDateToVaccination.put(start, vaccinationType); + + if (params != null) { + if (params.vacCamp.equals("base")) { // + + + } else if(params.vacCamp.equals("ph1_90")){ + minDaysAfterInfection = 90; + minDaysAfterVaccination = 90; + + emergencyDate = restrictionDatePhase1; + } else if(params.vacCamp.equals("ph1_90vax180")){ + minDaysAfterInfection = 90; + emergencyDate = restrictionDatePhase1; + } else if(params.vacCamp.equals("ph1_180")){ // + + emergencyDate = restrictionDatePhase1; + } else if (params.vacCamp.equals("ph1_180_ph2_inf90vax180")) { + emergencyDate = restrictionDatePhase1; + dateToTurnDownMinDaysAfterInfection = restrictionDatePhase2; + // same as ifsg180 but after phase 2 date, minDaysAfterInfection = 90; + }else if(params.vacCamp.equals("ph2_90")){ + minDaysAfterInfection = 90; + minDaysAfterVaccination = 90; + emergencyDate = restrictionDatePhase2; + } else if(params.vacCamp.equals("ph2_inf90vax180")){ + minDaysAfterInfection = 90; + emergencyDate = restrictionDatePhase2; + } else if(params.vacCamp.equals("ph2_180")) { + emergencyDate = restrictionDatePhase2; + }else { + throw new RuntimeException(); + } + } + + bind(VaccinationStrategyReoccurringCampaigns.Config.class).toInstance(new VaccinationStrategyReoccurringCampaigns.Config(startDateToVaccination, campaignDuration, vaccinationPool, minDaysAfterInfection, minDaysAfterVaccination, emergencyDate, dateToTurnDownMinDaysAfterInfection)); + + + // ANTIBODY MODEL + // default values + double mutEscDelta = 29.2 / 10.9; + double mutEscBa1 = 10.9 / 1.9; + double mutEscBa5 = 5.0; + + double mutEscStrainA = 0.; + double mutEscStrainB = 0.; + + + if (params != null) { + + mutEscStrainA = Double.parseDouble(params.StrainA); + + } + + //initial antibodies + Map> initialAntibodies = new HashMap<>(); + Map> antibodyRefreshFactors = new HashMap<>(); + configureAntibodies(initialAntibodies, antibodyRefreshFactors, mutEscDelta, mutEscBa1, mutEscBa5, mutEscStrainA, mutEscStrainB); + + AntibodyModel.Config antibodyConfig = new AntibodyModel.Config(initialAntibodies, antibodyRefreshFactors); + + double immuneSigma = 3.0; + if (params != null) { + antibodyConfig.setImmuneReponseSigma(immuneSigma); + } + + bind(AntibodyModel.Config.class).toInstance(antibodyConfig); + + if (params == null) return; + + // HOUSEHOLD SUSCEPTIBILITY + // designates a 35% of households as super safe; the susceptibility of that subpopulation is reduced to 1% wrt to general population. + bind(HouseholdSusceptibility.Config.class).toInstance( + HouseholdSusceptibility.newConfig() + .withSusceptibleHouseholds(0.35, 0.01) +// .withNonVaccinableHouseholds(params.nonVaccinableHh) +// .withShape(SnzCologneProductionScenario.INPUT.resolve("CologneDistricts.zip")) +// .withFeature("STT_NAME", vingst, altstadtNord, bickendorf, weiden) + ); + + } + + private void configureAntibodies(Map> initialAntibodies, + Map> antibodyRefreshFactors, + double mutEscDelta, double mutEscBa1, double mutEscBa5, double mutEscStrainA, double mutEscStrainB) { + for (VaccinationType immunityType : VaccinationType.values()) { + initialAntibodies.put(immunityType, new EnumMap<>( VirusStrain.class ) ); + for (VirusStrain virusStrain : VirusStrain.values()) { + + if (immunityType == VaccinationType.mRNA) { + initialAntibodies.get(immunityType).put(virusStrain, 29.2); //10.0 + } + else if (immunityType == VaccinationType.vector) { + initialAntibodies.get(immunityType).put(virusStrain, 6.8); //2.5 + } + else { + initialAntibodies.get(immunityType).put(virusStrain, 5.0); + } + } + } + + for (VirusStrain immunityType : VirusStrain.values()) { + initialAntibodies.put(immunityType, new EnumMap<>( VirusStrain.class ) ); + for (VirusStrain virusStrain : VirusStrain.values()) { + initialAntibodies.get(immunityType).put(virusStrain, 5.0); + } + } + + //mRNAAlpha, mRNADelta, mRNABA1 comes from Sydney's calibration. + //The other values come from Rössler et al. + + //Wildtype + double mRNAAlpha = 29.2; + initialAntibodies.get(VaccinationType.mRNA).put(VirusStrain.SARS_CoV_2, mRNAAlpha); + initialAntibodies.get(VaccinationType.vector).put(VirusStrain.SARS_CoV_2, mRNAAlpha * 210. / 700.); + initialAntibodies.get(VirusStrain.SARS_CoV_2).put(VirusStrain.SARS_CoV_2, mRNAAlpha * 300. / 700.); + initialAntibodies.get(VirusStrain.ALPHA).put(VirusStrain.SARS_CoV_2, mRNAAlpha * 300. / 700.); + initialAntibodies.get(VirusStrain.DELTA).put(VirusStrain.SARS_CoV_2, mRNAAlpha * 210. / 700.); + initialAntibodies.get(VirusStrain.OMICRON_BA1).put(VirusStrain.SARS_CoV_2, 0.01); + initialAntibodies.get(VirusStrain.OMICRON_BA2).put(VirusStrain.SARS_CoV_2, 0.01); + initialAntibodies.get(VirusStrain.OMICRON_BA5).put(VirusStrain.SARS_CoV_2, 0.01); + initialAntibodies.get(VirusStrain.STRAIN_A).put(VirusStrain.SARS_CoV_2, 0.01); + initialAntibodies.get(VirusStrain.STRAIN_B).put(VirusStrain.SARS_CoV_2, 0.01); + initialAntibodies.get(VaccinationType.ba1Update).put(VirusStrain.SARS_CoV_2, mRNAAlpha / mutEscDelta / mutEscBa1); + initialAntibodies.get(VaccinationType.ba5Update).put(VirusStrain.SARS_CoV_2, mRNAAlpha / mutEscDelta / mutEscBa1 / mutEscBa5); + + //Alpha + initialAntibodies.get(VaccinationType.mRNA).put(VirusStrain.ALPHA, mRNAAlpha); + initialAntibodies.get(VaccinationType.vector).put(VirusStrain.ALPHA, mRNAAlpha * 210. / 700.); + initialAntibodies.get(VirusStrain.SARS_CoV_2).put(VirusStrain.ALPHA, mRNAAlpha * 300. / 700.); + initialAntibodies.get(VirusStrain.ALPHA).put(VirusStrain.ALPHA, mRNAAlpha * 300. / 700.); + initialAntibodies.get(VirusStrain.DELTA).put(VirusStrain.ALPHA, mRNAAlpha * 210. / 700.); + initialAntibodies.get(VirusStrain.OMICRON_BA1).put(VirusStrain.ALPHA, 0.01); + initialAntibodies.get(VirusStrain.OMICRON_BA2).put(VirusStrain.ALPHA, 0.01); + initialAntibodies.get(VirusStrain.OMICRON_BA5).put(VirusStrain.ALPHA, 0.01); + initialAntibodies.get(VirusStrain.STRAIN_A).put(VirusStrain.ALPHA, 0.01); + initialAntibodies.get(VirusStrain.STRAIN_B).put(VirusStrain.ALPHA, 0.01); + initialAntibodies.get(VaccinationType.ba1Update).put(VirusStrain.ALPHA, mRNAAlpha / mutEscDelta / mutEscBa1); + initialAntibodies.get(VaccinationType.ba5Update).put(VirusStrain.ALPHA, mRNAAlpha / mutEscDelta / mutEscBa1 / mutEscBa5); + + //DELTA + double mRNADelta = mRNAAlpha / mutEscDelta; + initialAntibodies.get(VaccinationType.mRNA).put(VirusStrain.DELTA, mRNADelta); + initialAntibodies.get(VaccinationType.vector).put(VirusStrain.DELTA, mRNADelta * 150./300.); + initialAntibodies.get(VirusStrain.SARS_CoV_2).put(VirusStrain.DELTA, mRNADelta * 64./300.); + initialAntibodies.get(VirusStrain.ALPHA).put(VirusStrain.DELTA, mRNADelta * 64./300.); + initialAntibodies.get(VirusStrain.DELTA).put(VirusStrain.DELTA, mRNADelta * 450./300.); + initialAntibodies.get(VirusStrain.OMICRON_BA1).put(VirusStrain.DELTA, 0.01); + initialAntibodies.get(VirusStrain.OMICRON_BA2).put(VirusStrain.DELTA, 0.01); + initialAntibodies.get(VirusStrain.OMICRON_BA5).put(VirusStrain.DELTA, 0.01); + initialAntibodies.get(VirusStrain.STRAIN_A).put(VirusStrain.DELTA, 0.01); + initialAntibodies.get(VirusStrain.STRAIN_B).put(VirusStrain.DELTA, 0.01); + initialAntibodies.get(VaccinationType.ba1Update).put(VirusStrain.DELTA, mRNADelta / mutEscBa1); + initialAntibodies.get(VaccinationType.ba5Update).put(VirusStrain.DELTA, mRNADelta / mutEscBa1 / mutEscBa5); + + //BA.1 + double mRNABA1 = mRNADelta / mutEscBa1; + initialAntibodies.get(VaccinationType.mRNA).put(VirusStrain.OMICRON_BA1, mRNABA1); + initialAntibodies.get(VaccinationType.vector).put(VirusStrain.OMICRON_BA1, mRNABA1 * 4./20.); //??? + initialAntibodies.get(VirusStrain.SARS_CoV_2).put(VirusStrain.OMICRON_BA1, mRNABA1 * 6./20.); + initialAntibodies.get(VirusStrain.ALPHA).put(VirusStrain.OMICRON_BA1, mRNABA1 * 6./20.); + initialAntibodies.get(VirusStrain.DELTA).put(VirusStrain.OMICRON_BA1, mRNABA1 * 8./20.); + initialAntibodies.get(VirusStrain.OMICRON_BA1).put(VirusStrain.OMICRON_BA1, 64.0 / 300.); + initialAntibodies.get(VirusStrain.OMICRON_BA2).put(VirusStrain.OMICRON_BA1, 64.0 / 300. / 1.4); + initialAntibodies.get(VirusStrain.OMICRON_BA5).put(VirusStrain.OMICRON_BA1, 64.0 / 300. / mutEscBa5); //todo: is 1.4 + initialAntibodies.get(VirusStrain.STRAIN_A).put(VirusStrain.OMICRON_BA1, 64.0 / 300. / mutEscBa5 / mutEscStrainA); + initialAntibodies.get(VirusStrain.STRAIN_B).put(VirusStrain.OMICRON_BA1, 64.0 / 300. / mutEscStrainB); + initialAntibodies.get(VaccinationType.ba1Update).put(VirusStrain.OMICRON_BA1, mRNAAlpha); + initialAntibodies.get(VaccinationType.ba5Update).put(VirusStrain.OMICRON_BA1, mRNAAlpha / mutEscBa5); + + //BA.2 + double mRNABA2 = mRNABA1; + initialAntibodies.get(VaccinationType.mRNA).put(VirusStrain.OMICRON_BA2, mRNABA2); + initialAntibodies.get(VaccinationType.vector).put(VirusStrain.OMICRON_BA2, mRNABA2 * 4./20.); + initialAntibodies.get(VirusStrain.SARS_CoV_2).put(VirusStrain.OMICRON_BA2, mRNABA2 * 6./20.); + initialAntibodies.get(VirusStrain.ALPHA).put(VirusStrain.OMICRON_BA2, mRNABA2 * 6./20.); + initialAntibodies.get(VirusStrain.DELTA).put(VirusStrain.OMICRON_BA2, mRNABA2 * 8./20.); + initialAntibodies.get(VirusStrain.OMICRON_BA1).put(VirusStrain.OMICRON_BA2, 64.0 / 300. / 1.4); + initialAntibodies.get(VirusStrain.OMICRON_BA2).put(VirusStrain.OMICRON_BA2, 64.0 / 300.); + initialAntibodies.get(VirusStrain.OMICRON_BA5).put(VirusStrain.OMICRON_BA2, 64.0 / 300. / mutEscBa5); + initialAntibodies.get(VirusStrain.STRAIN_A).put(VirusStrain.OMICRON_BA2, 64.0 / 300. / mutEscBa5 / mutEscStrainA); + initialAntibodies.get(VirusStrain.STRAIN_B).put(VirusStrain.OMICRON_BA2, 64.0 / 300. / mutEscStrainB); + initialAntibodies.get(VaccinationType.ba1Update).put(VirusStrain.OMICRON_BA2, mRNAAlpha); + initialAntibodies.get(VaccinationType.ba5Update).put(VirusStrain.OMICRON_BA2, mRNAAlpha / mutEscBa5); + + + //BA.5 + double mRNABa5 = mRNABA2 / mutEscBa5; + initialAntibodies.get(VaccinationType.mRNA).put(VirusStrain.OMICRON_BA5, mRNABa5); + initialAntibodies.get(VaccinationType.vector).put(VirusStrain.OMICRON_BA5, mRNABa5 * 4./20.); + initialAntibodies.get(VirusStrain.SARS_CoV_2).put(VirusStrain.OMICRON_BA5, mRNABa5 * 6./20.); + initialAntibodies.get(VirusStrain.ALPHA).put(VirusStrain.OMICRON_BA5, mRNABa5 * 6./20.); + initialAntibodies.get(VirusStrain.DELTA).put(VirusStrain.OMICRON_BA5, mRNABa5 * 8./20.); + initialAntibodies.get(VirusStrain.OMICRON_BA1).put(VirusStrain.OMICRON_BA5, 64.0 / 300. / mutEscBa5);// todo: do we need 1.4? + initialAntibodies.get(VirusStrain.OMICRON_BA2).put(VirusStrain.OMICRON_BA5, 64.0 / 300./ mutEscBa5); + initialAntibodies.get(VirusStrain.OMICRON_BA5).put(VirusStrain.OMICRON_BA5, 64.0 / 300.); + initialAntibodies.get(VirusStrain.STRAIN_A).put(VirusStrain.OMICRON_BA5, 64.0 / 300./ mutEscStrainA); //todo ??? + initialAntibodies.get(VirusStrain.STRAIN_B).put(VirusStrain.OMICRON_BA5, 64.0 / 300. / mutEscBa5 / mutEscStrainB); + initialAntibodies.get(VaccinationType.ba1Update).put(VirusStrain.OMICRON_BA5, mRNAAlpha / mutEscBa5); + initialAntibodies.get(VaccinationType.ba5Update).put(VirusStrain.OMICRON_BA5, mRNAAlpha); + + //StrainA + double mRNAStrainA = mRNABa5 / mutEscStrainA; + initialAntibodies.get(VaccinationType.mRNA).put(VirusStrain.STRAIN_A, mRNAStrainA); + initialAntibodies.get(VaccinationType.vector).put(VirusStrain.STRAIN_A, mRNAStrainA * 4./20.); + initialAntibodies.get(VirusStrain.SARS_CoV_2).put(VirusStrain.STRAIN_A, mRNAStrainA * 6./20.); + initialAntibodies.get(VirusStrain.ALPHA).put(VirusStrain.STRAIN_A, mRNAStrainA * 6./20.); + initialAntibodies.get(VirusStrain.DELTA).put(VirusStrain.STRAIN_A, mRNAStrainA * 8./20.); + initialAntibodies.get(VirusStrain.OMICRON_BA1).put(VirusStrain.STRAIN_A, 64.0 / 300. / mutEscBa5 /mutEscStrainA); + initialAntibodies.get(VirusStrain.OMICRON_BA2).put(VirusStrain.STRAIN_A, 64.0 / 300./ mutEscBa5 /mutEscStrainA); + initialAntibodies.get(VirusStrain.OMICRON_BA5).put(VirusStrain.STRAIN_A, 64.0 / 300. / mutEscStrainA); + initialAntibodies.get(VirusStrain.STRAIN_A).put(VirusStrain.STRAIN_A, 64.0 / 300.); + initialAntibodies.get(VirusStrain.STRAIN_B).put(VirusStrain.STRAIN_A, 64.0 / 300. / mutEscStrainA / mutEscStrainB / mutEscBa5); + initialAntibodies.get(VaccinationType.ba1Update).put(VirusStrain.STRAIN_A, mRNAAlpha / mutEscBa5 / mutEscStrainA); + initialAntibodies.get(VaccinationType.ba5Update).put(VirusStrain.STRAIN_A, mRNAAlpha / mutEscStrainA); + + //StrainB + double mRNAStrainB = mRNABA2 / mutEscStrainB; + initialAntibodies.get(VaccinationType.mRNA).put(VirusStrain.STRAIN_B, mRNAStrainB); + initialAntibodies.get(VaccinationType.vector).put(VirusStrain.STRAIN_B, mRNAStrainB * 4./20.); + initialAntibodies.get(VirusStrain.SARS_CoV_2).put(VirusStrain.STRAIN_B, mRNAStrainB * 6./20.); + initialAntibodies.get(VirusStrain.ALPHA).put(VirusStrain.STRAIN_B, mRNAStrainB * 6./20.); + initialAntibodies.get(VirusStrain.DELTA).put(VirusStrain.STRAIN_B, mRNAStrainB * 8./20.); + initialAntibodies.get(VirusStrain.OMICRON_BA1).put(VirusStrain.STRAIN_B, 64.0 / 300. / mutEscStrainB); + initialAntibodies.get(VirusStrain.OMICRON_BA2).put(VirusStrain.STRAIN_B, 64.0 / 300./ mutEscStrainB); + initialAntibodies.get(VirusStrain.OMICRON_BA5).put(VirusStrain.STRAIN_B, 64.0 / 300. / mutEscStrainB / mutEscBa5); + initialAntibodies.get(VirusStrain.STRAIN_A).put(VirusStrain.STRAIN_B, 64.0 / 300./ mutEscStrainA / mutEscStrainB / mutEscBa5); + initialAntibodies.get(VirusStrain.STRAIN_B).put(VirusStrain.STRAIN_B, 64.0 / 300.); + initialAntibodies.get(VaccinationType.ba1Update).put(VirusStrain.STRAIN_B, mRNAAlpha / mutEscStrainB); + initialAntibodies.get(VaccinationType.ba5Update).put(VirusStrain.STRAIN_B, mRNAAlpha / mutEscStrainB / mutEscBa5); + + + for (VaccinationType immunityType : VaccinationType.values()) { + antibodyRefreshFactors.put(immunityType, new EnumMap<>( VirusStrain.class ) ); + for (VirusStrain virusStrain : VirusStrain.values()) { + + if (immunityType == VaccinationType.mRNA) { + antibodyRefreshFactors.get(immunityType).put(virusStrain, 15.0); + } + else if (immunityType == VaccinationType.vector) { + antibodyRefreshFactors.get(immunityType).put(virusStrain, 5.0); + } + else if (immunityType == VaccinationType.ba1Update) { + antibodyRefreshFactors.get(immunityType).put(virusStrain, 15.0); + } + else if (immunityType == VaccinationType.ba5Update) { + antibodyRefreshFactors.get(immunityType).put(virusStrain, 15.0); + } + else { + antibodyRefreshFactors.get(immunityType).put(virusStrain, Double.NaN); + } + + } + } + + for (VirusStrain immunityType : VirusStrain.values()) { + antibodyRefreshFactors.put(immunityType, new EnumMap<>( VirusStrain.class ) ); + for (VirusStrain virusStrain : VirusStrain.values()) { + antibodyRefreshFactors.get(immunityType).put(virusStrain, 15.0); + } + } + + +// UtilsJR.printInitialAntibodiesToConsole(initialAntibodies); + + } + }); + + } + + private SnzCologneProductionScenario getBindings(double pHousehold, Params params) { + return new SnzCologneProductionScenario.Builder() + .setCarnivalModel(SnzCologneProductionScenario.CarnivalModel.yes) + .setSebastianUpdate(true) + .setLeisureCorrection(1.3) //params == null ? 0.0 : params.actCorrection) + .setScaleForActivityLevels(1.3) + .setSuscHouseholds_pct(pHousehold) + .setActivityHandling(EpisimConfigGroup.ActivityHandling.startOfDay) +// .setTestingModel(params != null ? FlexibleTestingModel.class : DefaultTestingModel.class) + .setInfectionModel(InfectionModelWithAntibodies.class) + .build(); + } + + @Override + public Metadata getMetadata() { + return Metadata.of("cologne", "calibration"); + } + + @Override + public Collection postProcessing() { + return List.of( + new VaccinationEffectiveness().withArgs("--start-date", START_DATE), + new RValuesFromEvents().withArgs("--start-date", START_DATE), +// new VaccinationEffectivenessFromPotentialInfections().withArgs("--remove-infected"), + new FilterEvents().withArgs("--output", "./output/"), + new HospitalNumbersFromEvents().withArgs("--output", "./output/", "--input", "/scratch/projects/bzz0020/episim-input", "--start-date", START_DATE) +// new SecondaryAttackRateFromEvents().withArgs() + ); + } + + @Override + public Config prepareConfig(int id, Params params) { + + if (DEBUG_MODE) { + if (runCount == 0) { + runCount++; + } else { + return null; + } + } + + SnzCologneProductionScenario module = getBindings(0.0, params); + + Config config = module.config(); + + config.global().setRandomSeed(params.seed); + + EpisimConfigGroup episimConfig = ConfigUtils.addOrGetModule(config, EpisimConfigGroup.class); + + episimConfig.setCalibrationParameter(episimConfig.getCalibrationParameter() * 1.2 * 1.7); + + + // start from immunization history + episimConfig.setStartDate(LocalDate.parse(START_DATE)); + + if (params.startFromImm.equals("sepSeeds")) { + episimConfig.setStartFromImmunization("/scratch/projects/bzz0020/episim-input/imm-cologne-2022-11-24/" + params.seed + "-events_reduced.tar"); + } else if (params.startFromImm.equals("4711")) { + episimConfig.setStartFromImmunization("/scratch/projects/bzz0020/episim-input/imm-cologne-2022-11-24/4711-events_reduced.tar"); + } else { + throw new RuntimeException("invalid param"); + } + + //--------------------------------------- + // S T R A I N S + //--------------------------------------- + + VirusStrainConfigGroup virusStrainConfigGroup = ConfigUtils.addOrGetModule(config, VirusStrainConfigGroup.class); + + double ba5Inf = virusStrainConfigGroup.getOrAddParams(VirusStrain.OMICRON_BA5).getInfectiousness(); + double ba5Hos = virusStrainConfigGroup.getOrAddParams(VirusStrain.OMICRON_BA5).getFactorSeriouslySick(); + + + virusStrainConfigGroup.getOrAddParams(VirusStrain.STRAIN_A).setInfectiousness(ba5Inf); + virusStrainConfigGroup.getOrAddParams(VirusStrain.STRAIN_A).setFactorSeriouslySick(ba5Hos); + virusStrainConfigGroup.getOrAddParams(VirusStrain.STRAIN_A).setFactorSeriouslySickVaccinated(ba5Hos); + virusStrainConfigGroup.getOrAddParams(VirusStrain.STRAIN_A).setFactorCritical(ba5Hos); + + //--------------------------------------- + // I M P O R T + //--------------------------------------- + + configureFutureDiseaseImport(params, episimConfig); + + // modify import: + LocalDate impModDate = LocalDate.parse("2022-01-31"); + double impRedBa1 = 0.0; + double impRedBa2 = 0.0; + if (impRedBa1 != 1.0) { + NavigableMap impBa1 = episimConfig.getInfections_pers_per_day().get(VirusStrain.OMICRON_BA1); + for (Map.Entry entry : impBa1.entrySet()) { + if (entry.getKey().isAfter(impModDate)) { + impBa1.put(entry.getKey(), (int) (entry.getValue() * impRedBa1)); + } + } + } + + if (impRedBa2 != 1.0) { + NavigableMap impBa2 = episimConfig.getInfections_pers_per_day().get(VirusStrain.OMICRON_BA2); + for (Map.Entry entry : impBa2.entrySet()) { + if (entry.getKey().isAfter(impModDate)) { + impBa2.put(entry.getKey(), (int) (entry.getValue() * impRedBa2)); + } + } + } + + + + //--------------------------------------- + // R E S T R I C T I O N S + //--------------------------------------- + + FixedPolicy.ConfigBuilder builder = FixedPolicy.parse(episimConfig.getPolicy()); + + + + //ifsg + if ("base".equals(params.ifsg)) { + + } else if ("45".equals(params.ifsg) || "90".equals(params.ifsg)) { + double compliance = Double.parseDouble(params.ifsg) / 100.; + builder.restrict(restrictionDatePhase1, Restriction.ofMask(Map.of( + FaceMask.CLOTH, 0.0, + FaceMask.SURGICAL, compliance)), + "educ_secondary", "educ_tertiary", "educ_other", "educ_higher"); + builder.restrict(restrictionDatePhase1, Restriction.ofMask(Map.of(FaceMask.N95, compliance)), "leisPublic"); + builder.restrict(restrictionDatePhase1, Restriction.ofMask(Map.of(FaceMask.N95, compliance)), "shop_daily", "shop_other", "errands"); + builder.restrict(restrictionDatePhase1, Restriction.ofMask(Map.of(FaceMask.N95, 0.9)), "pt"); // pt has 90 compliance either way + } else { + throw new RuntimeException(); + } + + // EMERGENCY RESTRICTIONS + //work + builder.restrict(LocalDate.parse("2022-10-15"), 0.88, "work", "business"); + double homeOfficeFactor = 0.5; + switch (params.work) { + case "base": + break; + case "half": + builder.restrict(restrictionDatePhase2, 0.88 * homeOfficeFactor, "work"); // dont include business bc harder to do from home office + builder.applyToRf(restrictionDatePhase2.plusDays(1).toString(), restrictionDatePhase2.plusDays(1000).toString(), (d, rf) -> rf * homeOfficeFactor, "work"); + break; + case "half&mask": + builder.restrict(restrictionDatePhase2, 0.88 * homeOfficeFactor, "work"); // dont include business bc harder to do from home office + builder.applyToRf(restrictionDatePhase2.plusDays(1).toString(), restrictionDatePhase2.plusDays(1000).toString(), (d, rf) -> rf * homeOfficeFactor, "work"); + + builder.restrict(restrictionDatePhase2, Restriction.ofMask(Map.of(FaceMask.SURGICAL, 0.0, FaceMask.N95, 0.9)), "work", "business"); + break; + default: + throw new RuntimeException("invalid parameter"); + } + + // leisure public + private + switch (params.leis) { + case "base": + break; + case "pub50": + builder.restrict(restrictionDatePhase2, 0.88 * 0.5, "leisPublic"); + break; + case "pubPriv50": + builder.restrict(restrictionDatePhase2, 0.88 * 0.5, "leisPublic", "leisPrivate"); + break; + default: + throw new RuntimeException("invalid parameter"); + } + + //school + switch (params.edu) { + case "base": + break; + case "mask": + builder.restrict(restrictionDatePhase2, Restriction.ofMask(Map.of( + FaceMask.CLOTH, 0.0, + FaceMask.SURGICAL, 0.0, + FaceMask.N95, 0.90)), + "educ_secondary", "educ_tertiary", "educ_other", "educ_higher"); + break; + case "half&mask": + builder.restrict(restrictionDatePhase2, Restriction.ofMask(Map.of( + FaceMask.CLOTH, 0.0, + FaceMask.SURGICAL, 0.0, + FaceMask.N95, 0.90)), + "educ_secondary", "educ_tertiary", "educ_other", "educ_higher"); + + builder.restrict(restrictionDatePhase2, 0.5, "educ_primary", "educ_kiga", "educ_secondary", "educ_tertiary", "educ_other", "educ_higher"); + builder.applyToRf(restrictionDatePhase2.plusDays(1).toString(), restrictionDatePhase2.plusDays(1000).toString(), (d, rf) -> Math.min(0.5, rf), "educ_primary", "educ_kiga", "educ_secondary", "educ_tertiary", "educ_other", "educ_higher"); + break; + default: + throw new RuntimeException("invalid parameter"); + } + + if ("base".equals(params.maskPt)) { + } else { + builder.restrict(LocalDate.parse(params.maskPt), + Restriction.ofMask(Map.of( + FaceMask.CLOTH, 0.0, + FaceMask.SURGICAL, 0.0, + FaceMask.N95, 0.0)), + "pt"); + } + + + // vary amount of "school" activity that takes place during vacation + builder.restrict(LocalDate.parse("2022-06-27"), 0.8, "educ_primary", "educ_kiga", "educ_secondary", "educ_tertiary", "educ_other"); + + episimConfig.setPolicy(builder.build()); + + + //--------------------------------------- + // M I S C + //--------------------------------------- + + + if (DEBUG_MODE) { + UtilsJR.produceDiseaseImportPlot(episimConfig.getInfections_pers_per_day()); + } + + return config; + } + + private void configureFutureDiseaseImport(Params params, EpisimConfigGroup episimConfig) { + Map infPerDayBa1 = new HashMap<>(episimConfig.getInfections_pers_per_day().getOrDefault(VirusStrain.OMICRON_BA1, new TreeMap<>())); + Map infPerDayBa2 = new HashMap<>(episimConfig.getInfections_pers_per_day().getOrDefault(VirusStrain.OMICRON_BA2, new TreeMap<>())); + Map infPerDayBa5 = new HashMap<>(episimConfig.getInfections_pers_per_day().getOrDefault(VirusStrain.OMICRON_BA5, new TreeMap<>())); + Map infPerDayStrA = new HashMap<>(episimConfig.getInfections_pers_per_day().getOrDefault(VirusStrain.STRAIN_A, new TreeMap<>())); + + //StrainA + if (!params.StrainA.equals("off")) { + infPerDayStrA.put(LocalDate.parse("2020-01-01"), 0); + LocalDate strADate = LocalDate.parse(params.strainADate); + + for (int i = 0; i < 7; i++) { + infPerDayStrA.put(strADate.plusDays(i), 4); + } + infPerDayStrA.put(strADate.plusDays(7), 1); + } + + // save disease import + episimConfig.setInfections_pers_per_day(VirusStrain.OMICRON_BA1, infPerDayBa1); + episimConfig.setInfections_pers_per_day(VirusStrain.OMICRON_BA2, infPerDayBa2); + episimConfig.setInfections_pers_per_day(VirusStrain.OMICRON_BA5, infPerDayBa5); + + if (!params.StrainA.equals("off")) { + episimConfig.setInfections_pers_per_day(VirusStrain.STRAIN_A, infPerDayStrA); + } + } + + public static final class Params { + // general + @GenerateSeeds(20) + public long seed; + + @StringParameter({"sepSeeds", "4711"}) + public String startFromImm; + + // BQ 1 +// @StringParameter({"off", "2.0", "2.25", "2.5", "2.75", "3.0"}) +// @StringParameter({"1.0", "1.2", "1.4", "1.6", "1.8", "2.0"}) + @StringParameter({"2.0"}) + public String StrainA; + +// @StringParameter({"base", "2022-11-15", "2022-12-01", "2022-12-15", "2023-01-01"}) + @StringParameter({"base"}) + public String maskPt; + + + + +// @StringParameter({"2022-08-24", "2022-08-29", "2022-09-04", "2022-09-09", "2022-09-14", "2022-09-19"}) + @StringParameter({"2022-09-19"}) + public String strainADate; + + + //IFSG + @StringParameter({"base"}) + public String ifsg; + + // Vaccination Campaign + @StringParameter({"base"}) + String vacCamp; + + // NEW RESTRICTIONS + @StringParameter({"base"}) + public String work; + + // leisure Public + @StringParameter({"base"}) + public String leis; + + //edu + @StringParameter({"base"}) + public String edu; + + + + } + + + public static void main(String[] args) { + String[] args2 = { + RunParallel.OPTION_SETUP, CologneBMBF202212XX_bq1.class.getName(), + RunParallel.OPTION_PARAMS, Params.class.getName(), + RunParallel.OPTION_TASKS, Integer.toString(1), + RunParallel.OPTION_ITERATIONS, Integer.toString(1000), + RunParallel.OPTION_METADATA + }; + + RunParallel.main(args2); + } + + +} + diff --git a/src/main/java/org/matsim/run/batch/CologneBMBF202212XX_soup.java b/src/main/java/org/matsim/run/batch/CologneBMBF202212XX_soup.java new file mode 100644 index 000000000..0ce1f29ec --- /dev/null +++ b/src/main/java/org/matsim/run/batch/CologneBMBF202212XX_soup.java @@ -0,0 +1,744 @@ +package org.matsim.run.batch; + +import com.google.inject.AbstractModule; +import com.google.inject.Module; +import com.google.inject.Singleton; +import com.google.inject.multibindings.Multibinder; +import com.google.inject.util.Modules; +import org.matsim.core.config.Config; +import org.matsim.core.config.ConfigUtils; +import org.matsim.episim.BatchRun; +import org.matsim.episim.EpisimConfigGroup; +import org.matsim.episim.VaccinationConfigGroup; +import org.matsim.episim.VirusStrainConfigGroup; +import org.matsim.episim.analysis.*; +import org.matsim.episim.model.*; +import org.matsim.episim.model.listener.HouseholdSusceptibility; +import org.matsim.episim.model.vaccination.VaccinationModel; +import org.matsim.episim.model.vaccination.VaccinationStrategyReoccurringCampaigns; +import org.matsim.episim.policy.FixedPolicy; +import org.matsim.run.RunParallel; +import org.matsim.run.modules.SnzCologneProductionScenario; + +import javax.annotation.Nullable; +import java.time.LocalDate; +import java.time.temporal.ChronoUnit; +import java.util.*; +import java.util.Map.Entry; + + +/** + * Batch for Bmbf runs + */ +public class CologneBMBF202212XX_soup implements BatchRun { + + boolean DEBUG_MODE = true; + int runCount = 0; + + LocalDate restrictionDatePhase1 = LocalDate.parse("2022-12-01"); + LocalDate restrictionDatePhase2 = restrictionDatePhase1.plusDays(10); + + + @Nullable + @Override + public Module getBindings(int id, @Nullable Params params) { + return Modules.override(getBindings(0.0, params)).with(new AbstractModule() { + @Override + protected void configure() { + + // VACCINATION MODEL + Multibinder set = Multibinder.newSetBinder(binder(), VaccinationModel.class); + set.addBinding().to(VaccinationStrategyReoccurringCampaigns.class).in(Singleton.class); + // fixed values + LocalDate start = LocalDate.parse("2022-10-15"); + VaccinationType vaccinationType = VaccinationType.ba5Update; + int campaignDuration = 300000; + + // default values, to be changed if params != null + int minDaysAfterInfection = 180; + int minDaysAfterVaccination = 180; + VaccinationStrategyReoccurringCampaigns.Config.VaccinationPool vaccinationPool = VaccinationStrategyReoccurringCampaigns.Config.VaccinationPool.vaccinated; + LocalDate emergencyDate = LocalDate.MAX; + LocalDate dateToTurnDownMinDaysAfterInfection = LocalDate.MAX; + Map startDateToVaccination = new HashMap<>(); + startDateToVaccination.put(start, vaccinationType); + + if (params != null) { + if (params.vacCamp.equals("base")) { // + + + } else if(params.vacCamp.equals("ph1_90")){ + minDaysAfterInfection = 90; + minDaysAfterVaccination = 90; + + emergencyDate = restrictionDatePhase1; + } else if(params.vacCamp.equals("ph1_90vax180")){ + minDaysAfterInfection = 90; + emergencyDate = restrictionDatePhase1; + } else if(params.vacCamp.equals("ph1_180")){ // + + emergencyDate = restrictionDatePhase1; + } else if (params.vacCamp.equals("ph1_180_ph2_inf90vax180")) { + emergencyDate = restrictionDatePhase1; + dateToTurnDownMinDaysAfterInfection = restrictionDatePhase2; + // same as ifsg180 but after phase 2 date, minDaysAfterInfection = 90; + }else if(params.vacCamp.equals("ph2_90")){ + minDaysAfterInfection = 90; + minDaysAfterVaccination = 90; + emergencyDate = restrictionDatePhase2; + } else if(params.vacCamp.equals("ph2_inf90vax180")){ + minDaysAfterInfection = 90; + emergencyDate = restrictionDatePhase2; + } else if(params.vacCamp.equals("ph2_180")) { + emergencyDate = restrictionDatePhase2; + }else { + throw new RuntimeException(); + } + } + + bind(VaccinationStrategyReoccurringCampaigns.Config.class).toInstance(new VaccinationStrategyReoccurringCampaigns.Config(startDateToVaccination, campaignDuration, vaccinationPool, minDaysAfterInfection, minDaysAfterVaccination, emergencyDate, dateToTurnDownMinDaysAfterInfection)); + + + // ANTIBODY MODEL + // default values + double mutEscDelta = 29.2 / 10.9; + double mutEscBa1 = 10.9 / 1.9; + double mutEscBa5 = 5.0; + + double mutEscStrainA = 1.0; + double mutEscStrainB = 1.0; + + double escape = 12.; + int days = 30; + String strainSeed = "no"; + LocalDate strainADate = LocalDate.parse("2020-01-01"); + boolean lineB = true; + double escapeBetweenLines = 1.0; + + if (params != null) { +// mutEscBa1 = params.ba1Esc; +// mutEscBa5 = params.ba5Esc; + +// String StrainA = "6.0"; + String StrainB = "off"; + + + if (!params.StrainA.equals("off")) { + mutEscStrainA = Double.parseDouble(params.StrainA); + } + if (!StrainB.equals("off")) { + mutEscStrainB = Double.parseDouble(StrainB); + } + escape = params.esc; + days = params.days; + strainSeed = params.strainRnd; + strainADate = LocalDate.parse(params.strainADate); + lineB = Boolean.valueOf(params.lineB); + escapeBetweenLines = params.escL; + + } + + //initial antibodies + Map> initialAntibodies = new HashMap<>(); + Map> antibodyRefreshFactors = new HashMap<>(); + configureAntibodies(initialAntibodies, antibodyRefreshFactors, mutEscDelta, mutEscBa1, mutEscBa5, mutEscStrainA, mutEscStrainB, escape, days, strainSeed, strainADate, lineB, escapeBetweenLines); + + AntibodyModel.Config antibodyConfig = new AntibodyModel.Config(initialAntibodies, antibodyRefreshFactors); + + double immuneSigma = 3.0; + if (params != null) { + antibodyConfig.setImmuneReponseSigma(immuneSigma); + } + + bind(AntibodyModel.Config.class).toInstance(antibodyConfig); + + + UtilsJR.printInitialAntibodiesToConsole(initialAntibodies, true); + + if (params == null) return; + + // HOUSEHOLD SUSCEPTIBILITY + // designates a 35% of households as super safe; the susceptibility of that subpopulation is reduced to 1% wrt to general population. + bind(HouseholdSusceptibility.Config.class).toInstance( + HouseholdSusceptibility.newConfig() + .withSusceptibleHouseholds(0.35, 0.01) +// .withNonVaccinableHouseholds(params.nonVaccinableHh) +// .withShape(SnzCologneProductionScenario.INPUT.resolve("CologneDistricts.zip")) +// .withFeature("STT_NAME", vingst, altstadtNord, bickendorf, weiden) + ); + + } + + private void configureAntibodies(Map> initialAntibodies, + Map> antibodyRefreshFactors, + double mutEscDelta, double mutEscBa1, double mutEscBa5, double mutEscStrainA, double mutEscStrainB, + double escapePerYear, int days, String strainSeed, LocalDate strainADate, boolean lineB, double escapeBetweenLines) { + for (VaccinationType immunityType : VaccinationType.values()) { + initialAntibodies.put(immunityType, new EnumMap<>( VirusStrain.class ) ); + for (VirusStrain virusStrain : VirusStrain.values()) { + + if (immunityType == VaccinationType.mRNA) { + initialAntibodies.get(immunityType).put(virusStrain, 29.2); //10.0 + } + else if (immunityType == VaccinationType.vector) { + initialAntibodies.get(immunityType).put(virusStrain, 6.8); //2.5 + } + else { + initialAntibodies.get(immunityType).put(virusStrain, 5.0); + } + } + } + + for (VirusStrain immunityType : VirusStrain.values()) { + initialAntibodies.put(immunityType, new EnumMap<>( VirusStrain.class ) ); + for (VirusStrain virusStrain : VirusStrain.values()) { + initialAntibodies.get(immunityType).put(virusStrain, 5.0); + } + } + + //mRNAAlpha, mRNADelta, mRNABA1 comes from Sydney's calibration. + //The other values come from Rössler et al. + + //Wildtype + double mRNAAlpha = 29.2; + initialAntibodies.get(VaccinationType.mRNA).put(VirusStrain.SARS_CoV_2, mRNAAlpha); + initialAntibodies.get(VaccinationType.vector).put(VirusStrain.SARS_CoV_2, mRNAAlpha * 210. / 700.); + initialAntibodies.get(VirusStrain.SARS_CoV_2).put(VirusStrain.SARS_CoV_2, mRNAAlpha * 300. / 700.); + initialAntibodies.get(VirusStrain.ALPHA).put(VirusStrain.SARS_CoV_2, mRNAAlpha * 300. / 700.); + initialAntibodies.get(VirusStrain.DELTA).put(VirusStrain.SARS_CoV_2, mRNAAlpha * 210. / 700.); + initialAntibodies.get(VirusStrain.OMICRON_BA1).put(VirusStrain.SARS_CoV_2, 0.01); + initialAntibodies.get(VirusStrain.OMICRON_BA2).put(VirusStrain.SARS_CoV_2, 0.01); + initialAntibodies.get(VirusStrain.OMICRON_BA5).put(VirusStrain.SARS_CoV_2, 0.01); + initialAntibodies.get(VirusStrain.STRAIN_A).put(VirusStrain.SARS_CoV_2, 0.01); + initialAntibodies.get(VirusStrain.STRAIN_B).put(VirusStrain.SARS_CoV_2, 0.01); + initialAntibodies.get(VaccinationType.ba1Update).put(VirusStrain.SARS_CoV_2, mRNAAlpha / mutEscDelta / mutEscBa1); + initialAntibodies.get(VaccinationType.ba5Update).put(VirusStrain.SARS_CoV_2, mRNAAlpha / mutEscDelta / mutEscBa1 / mutEscBa5); + + //Alpha + initialAntibodies.get(VaccinationType.mRNA).put(VirusStrain.ALPHA, mRNAAlpha); + initialAntibodies.get(VaccinationType.vector).put(VirusStrain.ALPHA, mRNAAlpha * 210. / 700.); + initialAntibodies.get(VirusStrain.SARS_CoV_2).put(VirusStrain.ALPHA, mRNAAlpha * 300. / 700.); + initialAntibodies.get(VirusStrain.ALPHA).put(VirusStrain.ALPHA, mRNAAlpha * 300. / 700.); + initialAntibodies.get(VirusStrain.DELTA).put(VirusStrain.ALPHA, mRNAAlpha * 210. / 700.); + initialAntibodies.get(VirusStrain.OMICRON_BA1).put(VirusStrain.ALPHA, 0.01); + initialAntibodies.get(VirusStrain.OMICRON_BA2).put(VirusStrain.ALPHA, 0.01); + initialAntibodies.get(VirusStrain.OMICRON_BA5).put(VirusStrain.ALPHA, 0.01); + initialAntibodies.get(VirusStrain.STRAIN_A).put(VirusStrain.ALPHA, 0.01); + initialAntibodies.get(VirusStrain.STRAIN_B).put(VirusStrain.ALPHA, 0.01); + initialAntibodies.get(VaccinationType.ba1Update).put(VirusStrain.ALPHA, mRNAAlpha / mutEscDelta / mutEscBa1); + initialAntibodies.get(VaccinationType.ba5Update).put(VirusStrain.ALPHA, mRNAAlpha / mutEscDelta / mutEscBa1 / mutEscBa5); + + //DELTA + double mRNADelta = mRNAAlpha / mutEscDelta; + initialAntibodies.get(VaccinationType.mRNA).put(VirusStrain.DELTA, mRNADelta); + initialAntibodies.get(VaccinationType.vector).put(VirusStrain.DELTA, mRNADelta * 150./300.); + initialAntibodies.get(VirusStrain.SARS_CoV_2).put(VirusStrain.DELTA, mRNADelta * 64./300.); + initialAntibodies.get(VirusStrain.ALPHA).put(VirusStrain.DELTA, mRNADelta * 64./300.); + initialAntibodies.get(VirusStrain.DELTA).put(VirusStrain.DELTA, mRNADelta * 450./300.); + initialAntibodies.get(VirusStrain.OMICRON_BA1).put(VirusStrain.DELTA, 0.01); + initialAntibodies.get(VirusStrain.OMICRON_BA2).put(VirusStrain.DELTA, 0.01); + initialAntibodies.get(VirusStrain.OMICRON_BA5).put(VirusStrain.DELTA, 0.01); + initialAntibodies.get(VirusStrain.STRAIN_A).put(VirusStrain.DELTA, 0.01); + initialAntibodies.get(VirusStrain.STRAIN_B).put(VirusStrain.DELTA, 0.01); + initialAntibodies.get(VaccinationType.ba1Update).put(VirusStrain.DELTA, mRNADelta / mutEscBa1); + initialAntibodies.get(VaccinationType.ba5Update).put(VirusStrain.DELTA, mRNADelta / mutEscBa1 / mutEscBa5); + + //BA.1 + double mRNABA1 = mRNADelta / mutEscBa1; + initialAntibodies.get(VaccinationType.mRNA).put(VirusStrain.OMICRON_BA1, mRNABA1); + initialAntibodies.get(VaccinationType.vector).put(VirusStrain.OMICRON_BA1, mRNABA1 * 4./20.); //??? + initialAntibodies.get(VirusStrain.SARS_CoV_2).put(VirusStrain.OMICRON_BA1, mRNABA1 * 6./20.); + initialAntibodies.get(VirusStrain.ALPHA).put(VirusStrain.OMICRON_BA1, mRNABA1 * 6./20.); + initialAntibodies.get(VirusStrain.DELTA).put(VirusStrain.OMICRON_BA1, mRNABA1 * 8./20.); + initialAntibodies.get(VirusStrain.OMICRON_BA1).put(VirusStrain.OMICRON_BA1, 64.0 / 300.); + initialAntibodies.get(VirusStrain.OMICRON_BA2).put(VirusStrain.OMICRON_BA1, 64.0 / 300. / 1.4); + initialAntibodies.get(VirusStrain.OMICRON_BA5).put(VirusStrain.OMICRON_BA1, 64.0 / 300. / mutEscBa5); //todo: is 1.4 + initialAntibodies.get(VirusStrain.STRAIN_A).put(VirusStrain.OMICRON_BA1, 64.0 / 300. / mutEscBa5 / mutEscStrainA); + initialAntibodies.get(VirusStrain.STRAIN_B).put(VirusStrain.OMICRON_BA1, 64.0 / 300. / mutEscStrainB); + initialAntibodies.get(VaccinationType.ba1Update).put(VirusStrain.OMICRON_BA1, mRNAAlpha); + initialAntibodies.get(VaccinationType.ba5Update).put(VirusStrain.OMICRON_BA1, mRNAAlpha / mutEscBa5); + + //BA.2 + double mRNABA2 = mRNABA1; + initialAntibodies.get(VaccinationType.mRNA).put(VirusStrain.OMICRON_BA2, mRNABA2); + initialAntibodies.get(VaccinationType.vector).put(VirusStrain.OMICRON_BA2, mRNABA2 * 4./20.); + initialAntibodies.get(VirusStrain.SARS_CoV_2).put(VirusStrain.OMICRON_BA2, mRNABA2 * 6./20.); + initialAntibodies.get(VirusStrain.ALPHA).put(VirusStrain.OMICRON_BA2, mRNABA2 * 6./20.); + initialAntibodies.get(VirusStrain.DELTA).put(VirusStrain.OMICRON_BA2, mRNABA2 * 8./20.); + initialAntibodies.get(VirusStrain.OMICRON_BA1).put(VirusStrain.OMICRON_BA2, 64.0 / 300. / 1.4); + initialAntibodies.get(VirusStrain.OMICRON_BA2).put(VirusStrain.OMICRON_BA2, 64.0 / 300.); + initialAntibodies.get(VirusStrain.OMICRON_BA5).put(VirusStrain.OMICRON_BA2, 64.0 / 300. / mutEscBa5); + initialAntibodies.get(VirusStrain.STRAIN_A).put(VirusStrain.OMICRON_BA2, 64.0 / 300. / mutEscBa5 / mutEscStrainA); + initialAntibodies.get(VirusStrain.STRAIN_B).put(VirusStrain.OMICRON_BA2, 64.0 / 300. / mutEscStrainB); + initialAntibodies.get(VaccinationType.ba1Update).put(VirusStrain.OMICRON_BA2, mRNAAlpha); + initialAntibodies.get(VaccinationType.ba5Update).put(VirusStrain.OMICRON_BA2, mRNAAlpha / mutEscBa5); + + + //BA.5 + double mRNABa5 = mRNABA2 / mutEscBa5; + initialAntibodies.get(VaccinationType.mRNA).put(VirusStrain.OMICRON_BA5, mRNABa5); + initialAntibodies.get(VaccinationType.vector).put(VirusStrain.OMICRON_BA5, mRNABa5 * 4./20.); + initialAntibodies.get(VirusStrain.SARS_CoV_2).put(VirusStrain.OMICRON_BA5, mRNABa5 * 6./20.); + initialAntibodies.get(VirusStrain.ALPHA).put(VirusStrain.OMICRON_BA5, mRNABa5 * 6./20.); + initialAntibodies.get(VirusStrain.DELTA).put(VirusStrain.OMICRON_BA5, mRNABa5 * 8./20.); + initialAntibodies.get(VirusStrain.OMICRON_BA1).put(VirusStrain.OMICRON_BA5, 64.0 / 300. / mutEscBa5);// todo: do we need 1.4? + initialAntibodies.get(VirusStrain.OMICRON_BA2).put(VirusStrain.OMICRON_BA5, 64.0 / 300./ mutEscBa5); + initialAntibodies.get(VirusStrain.OMICRON_BA5).put(VirusStrain.OMICRON_BA5, 64.0 / 300.); + initialAntibodies.get(VirusStrain.STRAIN_A).put(VirusStrain.OMICRON_BA5, 64.0 / 300./ mutEscStrainA); //todo ??? + initialAntibodies.get(VirusStrain.STRAIN_B).put(VirusStrain.OMICRON_BA5, 64.0 / 300. / mutEscBa5 / mutEscStrainB); + initialAntibodies.get(VaccinationType.ba1Update).put(VirusStrain.OMICRON_BA5, mRNAAlpha / mutEscBa5); + initialAntibodies.get(VaccinationType.ba5Update).put(VirusStrain.OMICRON_BA5, mRNAAlpha); + + //StrainA + double mRNAStrainA = mRNABa5 / mutEscStrainA; + initialAntibodies.get(VaccinationType.mRNA).put(VirusStrain.STRAIN_A, mRNAStrainA); + initialAntibodies.get(VaccinationType.vector).put(VirusStrain.STRAIN_A, mRNAStrainA * 4./20.); + initialAntibodies.get(VirusStrain.SARS_CoV_2).put(VirusStrain.STRAIN_A, mRNAStrainA * 6./20.); + initialAntibodies.get(VirusStrain.ALPHA).put(VirusStrain.STRAIN_A, mRNAStrainA * 6./20.); + initialAntibodies.get(VirusStrain.DELTA).put(VirusStrain.STRAIN_A, mRNAStrainA * 8./20.); + initialAntibodies.get(VirusStrain.OMICRON_BA1).put(VirusStrain.STRAIN_A, 64.0 / 300. / mutEscBa5 /mutEscStrainA); + initialAntibodies.get(VirusStrain.OMICRON_BA2).put(VirusStrain.STRAIN_A, 64.0 / 300./ mutEscBa5 /mutEscStrainA); + initialAntibodies.get(VirusStrain.OMICRON_BA5).put(VirusStrain.STRAIN_A, 64.0 / 300. / mutEscStrainA); + initialAntibodies.get(VirusStrain.STRAIN_A).put(VirusStrain.STRAIN_A, 64.0 / 300.); + initialAntibodies.get(VirusStrain.STRAIN_B).put(VirusStrain.STRAIN_A, 64.0 / 300. / mutEscStrainA / mutEscStrainB / mutEscBa5); + initialAntibodies.get(VaccinationType.ba1Update).put(VirusStrain.STRAIN_A, mRNAAlpha / mutEscBa5 / mutEscStrainA); + initialAntibodies.get(VaccinationType.ba5Update).put(VirusStrain.STRAIN_A, mRNAAlpha / mutEscStrainA); + + //StrainB + double mRNAStrainB = mRNABA2 / mutEscStrainB; + initialAntibodies.get(VaccinationType.mRNA).put(VirusStrain.STRAIN_B, mRNAStrainB); + initialAntibodies.get(VaccinationType.vector).put(VirusStrain.STRAIN_B, mRNAStrainB * 4./20.); + initialAntibodies.get(VirusStrain.SARS_CoV_2).put(VirusStrain.STRAIN_B, mRNAStrainB * 6./20.); + initialAntibodies.get(VirusStrain.ALPHA).put(VirusStrain.STRAIN_B, mRNAStrainB * 6./20.); + initialAntibodies.get(VirusStrain.DELTA).put(VirusStrain.STRAIN_B, mRNAStrainB * 8./20.); + initialAntibodies.get(VirusStrain.OMICRON_BA1).put(VirusStrain.STRAIN_B, 64.0 / 300. / mutEscStrainB); + initialAntibodies.get(VirusStrain.OMICRON_BA2).put(VirusStrain.STRAIN_B, 64.0 / 300./ mutEscStrainB); + initialAntibodies.get(VirusStrain.OMICRON_BA5).put(VirusStrain.STRAIN_B, 64.0 / 300. / mutEscStrainB / mutEscBa5); + initialAntibodies.get(VirusStrain.STRAIN_A).put(VirusStrain.STRAIN_B, 64.0 / 300./ mutEscStrainA / mutEscStrainB / mutEscBa5); + initialAntibodies.get(VirusStrain.STRAIN_B).put(VirusStrain.STRAIN_B, 64.0 / 300.); + initialAntibodies.get(VaccinationType.ba1Update).put(VirusStrain.STRAIN_B, mRNAAlpha / mutEscStrainB); + initialAntibodies.get(VaccinationType.ba5Update).put(VirusStrain.STRAIN_B, mRNAAlpha / mutEscStrainB / mutEscBa5); + + + { + + ArrayList strains = getNewStrains(Boolean.valueOf(lineB)); + + ArrayList dates = getDatesNewStrains(strains, days, strainSeed, strainADate); + + for (int i = 0; i < strains.size(); i++) { + long daysSince = ChronoUnit.DAYS.between(strainADate, dates.get(i)); + double escape = 1. + (escapePerYear - 1.0) * daysSince / 365.; //factor 6, if variant appears 6 months later + VirusStrain strain = strains.get(i); + + initialAntibodies.get(strain).put(VirusStrain.SARS_CoV_2, 0.01); + initialAntibodies.get(strain).put(VirusStrain.ALPHA, 0.01); + initialAntibodies.get(strain).put(VirusStrain.DELTA, 0.01); + + initialAntibodies.get(strain).put(VirusStrain.OMICRON_BA1, 64.0 / 300. / mutEscBa5 / mutEscStrainA / escape); + initialAntibodies.get(strain).put(VirusStrain.OMICRON_BA2, 64.0 / 300. / mutEscBa5 / mutEscStrainA / escape); + initialAntibodies.get(strain).put(VirusStrain.OMICRON_BA5, 64.0 / 300./ mutEscStrainA / escape); + initialAntibodies.get(strain).put(VirusStrain.STRAIN_A, 64.0 / 300. / escape); + + double mRNAStrain = mRNAStrainA / escape; + initialAntibodies.get(VaccinationType.mRNA).put(strain, mRNAStrain); + initialAntibodies.get(VaccinationType.vector).put(strain, mRNAStrain * 4./20.); + initialAntibodies.get(VirusStrain.SARS_CoV_2).put(strain, mRNAStrain * 6./20.); + initialAntibodies.get(VirusStrain.ALPHA).put(strain, mRNAStrain * 6./20.); + initialAntibodies.get(VirusStrain.DELTA).put(strain, mRNAStrain * 8./20.); + + initialAntibodies.get(VirusStrain.OMICRON_BA1).put(strain, 64.0 / 300. / mutEscBa5 /mutEscStrainA / escape); + initialAntibodies.get(VirusStrain.OMICRON_BA2).put(strain, 64.0 / 300./ mutEscBa5 /mutEscStrainA / escape); + initialAntibodies.get(VirusStrain.OMICRON_BA5).put(strain, 64.0 / 300. / mutEscStrainA / escape); + initialAntibodies.get(VirusStrain.STRAIN_A).put(strain, 64.0 / 300. / escape); + // initialAntibodies.get(VirusStrain.STRAIN_B).put(strain, 64.0 / 300. / mutEscStrainA / mutEscStrainB / mutEscBa5 / escape); + initialAntibodies.get(VaccinationType.ba1Update).put(strain, mRNAAlpha / mutEscBa5 / mutEscStrainA / escape); + initialAntibodies.get(VaccinationType.ba5Update).put(strain, mRNAAlpha / mutEscStrainA / escape); + + + for (int j = 0; j < strains.size(); j++) { + LocalDate date1 = dates.get(i); + LocalDate date2 = dates.get(j); + long daysBetweenStrains = Math.abs(ChronoUnit.DAYS.between(date1, date2)); + double escapeBetweenStrains = 1. + (escapePerYear - 1.0) * daysBetweenStrains / 365.; //factor 6, if variant appears 6 months later + VirusStrain strain2 = strains.get(j); + + if (strain.toString().charAt(0) != strain2.toString().charAt(0)) + escapeBetweenStrains = escapeBetweenStrains * escapeBetweenLines; + + initialAntibodies.get(strain).put(strain2, 64.0 / 300. / escapeBetweenStrains); + } + + } + + + } + + + + for (VaccinationType immunityType : VaccinationType.values()) { + antibodyRefreshFactors.put(immunityType, new EnumMap<>( VirusStrain.class ) ); + for (VirusStrain virusStrain : VirusStrain.values()) { + + if (immunityType == VaccinationType.mRNA) { + antibodyRefreshFactors.get(immunityType).put(virusStrain, 15.0); + } + else if (immunityType == VaccinationType.vector) { + antibodyRefreshFactors.get(immunityType).put(virusStrain, 5.0); + } + else if (immunityType == VaccinationType.ba1Update) { + antibodyRefreshFactors.get(immunityType).put(virusStrain, 15.0); + } + else if (immunityType == VaccinationType.ba5Update) { + antibodyRefreshFactors.get(immunityType).put(virusStrain, 15.0); + } + else { + antibodyRefreshFactors.get(immunityType).put(virusStrain, Double.NaN); + } + + } + } + + for (VirusStrain immunityType : VirusStrain.values()) { + antibodyRefreshFactors.put(immunityType, new EnumMap<>( VirusStrain.class ) ); + for (VirusStrain virusStrain : VirusStrain.values()) { + antibodyRefreshFactors.get(immunityType).put(virusStrain, 15.0); + } + } + + +// UtilsJR.printInitialAntibodiesToConsole(initialAntibodies); + + } + }); + + } + + private SnzCologneProductionScenario getBindings(double pHousehold, Params params) { + return new SnzCologneProductionScenario.Builder() + .setCarnivalModel(SnzCologneProductionScenario.CarnivalModel.yes) + .setSebastianUpdate(true) + .setLeisureCorrection(1.3) //params == null ? 0.0 : params.actCorrection) + .setScaleForActivityLevels(1.3) + .setSuscHouseholds_pct(pHousehold) + .setActivityHandling(EpisimConfigGroup.ActivityHandling.startOfDay) +// .setTestingModel(params != null ? FlexibleTestingModel.class : DefaultTestingModel.class) + .setInfectionModel(InfectionModelWithAntibodies.class) + .build(); + } + + @Override + public Metadata getMetadata() { + return Metadata.of("cologne", "calibration"); + } + + @Override + public Collection postProcessing() { + return List.of( + new VaccinationEffectiveness().withArgs(), + new RValuesFromEvents().withArgs(), + new VaccinationEffectivenessFromPotentialInfections().withArgs("--remove-infected"), + new FilterEvents().withArgs("--output","./output/"), + new HospitalNumbersFromEvents().withArgs("--output","./output/","--input","/scratch/projects/bzz0020/episim-input") +// new SecondaryAttackRateFromEvents().withArgs() + ); + } + + @Override + public Config prepareConfig(int id, Params params) { + + if (DEBUG_MODE) { + if (runCount == 0 && Boolean.parseBoolean(params.lineB) && params.esc == 12. && params.escL == 6.0 ) { //&& params.strAEsc != 0.0 && params.ba5Inf == 0. && params.eduTest.equals("true")) { + runCount++; + } else { + return null; + } + } + + SnzCologneProductionScenario module = getBindings(0.0, params); + + Config config = module.config(); + + config.global().setRandomSeed(params.seed); + + EpisimConfigGroup episimConfig = ConfigUtils.addOrGetModule(config, EpisimConfigGroup.class); + + episimConfig.setCalibrationParameter(episimConfig.getCalibrationParameter() * 1.2 * 1.7); + + //snapshot +// episimConfig.setSnapshotInterval(30); +// episimConfig.setSnapshotPrefix(String.valueOf(params.seed)); +// episimConfig.setStartFromSnapshot("/scratch/projects/bzz0020/episim-input/snapshots-cologne-2022-10-27/" + params.seed + "-900-2022-08-12.zip"); +// episimConfig.setSnapshotSeed(EpisimConfigGroup.SnapshotSeed.restore); + //--------------------------------------- + // S T R A I N S + //--------------------------------------- + + VirusStrainConfigGroup virusStrainConfigGroup = ConfigUtils.addOrGetModule(config, VirusStrainConfigGroup.class); + + double ba5Inf = virusStrainConfigGroup.getOrAddParams(VirusStrain.OMICRON_BA5).getInfectiousness(); + double ba5Hos = virusStrainConfigGroup.getOrAddParams(VirusStrain.OMICRON_BA5).getFactorSeriouslySick(); + +// STRAIN_A + if (!params.StrainA.equals("off")) { + + virusStrainConfigGroup.getOrAddParams(VirusStrain.STRAIN_A).setInfectiousness(ba5Inf); + virusStrainConfigGroup.getOrAddParams(VirusStrain.STRAIN_A).setFactorSeriouslySick(ba5Hos); + virusStrainConfigGroup.getOrAddParams(VirusStrain.STRAIN_A).setFactorSeriouslySickVaccinated(ba5Hos); + virusStrainConfigGroup.getOrAddParams(VirusStrain.STRAIN_A).setFactorCritical(ba5Hos); + } + + for (VirusStrain strain : getNewStrains(Boolean.valueOf(params.lineB))) { + virusStrainConfigGroup.getOrAddParams(strain).setInfectiousness(ba5Inf); + virusStrainConfigGroup.getOrAddParams(strain).setFactorSeriouslySick(ba5Hos); + virusStrainConfigGroup.getOrAddParams(strain).setFactorSeriouslySickVaccinated(ba5Hos); + virusStrainConfigGroup.getOrAddParams(strain).setFactorCritical(ba5Hos); + } + + //--------------------------------------- + // I M P O R T + //--------------------------------------- + + configureFutureDiseaseImport(params, episimConfig); + + // modify import: + LocalDate impModDate = LocalDate.parse("2022-01-31"); + double impRedBa1 = 0.0; + double impRedBa2 = 0.0; + if (impRedBa1 != 1.0) { + NavigableMap impBa1 = episimConfig.getInfections_pers_per_day().get(VirusStrain.OMICRON_BA1); + for (Map.Entry entry : impBa1.entrySet()) { + if (entry.getKey().isAfter(impModDate)) { + impBa1.put(entry.getKey(), (int) (entry.getValue() * impRedBa1)); + } + } + } + + if (impRedBa2 != 1.0) { + NavigableMap impBa2 = episimConfig.getInfections_pers_per_day().get(VirusStrain.OMICRON_BA2); + for (Map.Entry entry : impBa2.entrySet()) { + if (entry.getKey().isAfter(impModDate)) { + impBa2.put(entry.getKey(), (int) (entry.getValue() * impRedBa2)); + } + } + } + + + + //--------------------------------------- + // R E S T R I C T I O N S + //--------------------------------------- + + FixedPolicy.ConfigBuilder builder = FixedPolicy.parse(episimConfig.getPolicy()); + + + // vary amount of "school" activity that takes place during vacation + builder.restrict(LocalDate.parse("2022-06-27"), 0.8, "educ_primary", "educ_kiga", "educ_secondary", "educ_tertiary", "educ_other"); + + episimConfig.setPolicy(builder.build()); + + + //--------------------------------------- + // M I S C + //--------------------------------------- + + VaccinationConfigGroup vaccinationConfig = ConfigUtils.addOrGetModule(config, VaccinationConfigGroup.class); + vaccinationConfig.setUseIgA(Boolean.valueOf(params.iga)); + + if (!Boolean.valueOf(params.seasonal)) { + + Map fractionsOld = episimConfig.getLeisureOutdoorFraction(); + Map fractionsNew = new HashMap(); + + for (Entry e : fractionsOld.entrySet()) { + if (e.getKey().isBefore(LocalDate.parse("2022-12-01"))) + fractionsNew.put(e.getKey(), e.getValue()); + + } + episimConfig.setLeisureOutdoorFraction(fractionsNew); + } + + + if (DEBUG_MODE) { + UtilsJR.produceDiseaseImportPlot(episimConfig.getInfections_pers_per_day()); + } + + return config; + } + + private void configureFutureDiseaseImport(Params params, EpisimConfigGroup episimConfig) { + Map infPerDayBa1 = new HashMap<>(episimConfig.getInfections_pers_per_day().getOrDefault(VirusStrain.OMICRON_BA1, new TreeMap<>())); + Map infPerDayBa2 = new HashMap<>(episimConfig.getInfections_pers_per_day().getOrDefault(VirusStrain.OMICRON_BA2, new TreeMap<>())); + Map infPerDayBa5 = new HashMap<>(episimConfig.getInfections_pers_per_day().getOrDefault(VirusStrain.OMICRON_BA5, new TreeMap<>())); + Map infPerDayStrA = new HashMap<>(episimConfig.getInfections_pers_per_day().getOrDefault(VirusStrain.STRAIN_A, new TreeMap<>())); +// Map infPerDayStrB = new HashMap<>(episimConfig.getInfections_pers_per_day().getOrDefault(VirusStrain.STRAIN_B, new TreeMap<>())); + + //StrainA + if (!params.StrainA.equals("off")) { + infPerDayStrA.put(LocalDate.parse("2020-01-01"), 0); +// LocalDate strADate = LocalDate.parse("2022-11-01"); + LocalDate strADate = LocalDate.parse(params.strainADate); + + for (int i = 0; i < 7; i++) { + infPerDayStrA.put(strADate.plusDays(i), 4); + } + infPerDayStrA.put(strADate.plusDays(7), 1); + } + + + for (int i = 0; i < getNewStrains(Boolean.valueOf(params.lineB)).size(); i++) { + LocalDate date = getDatesNewStrains(getNewStrains(Boolean.valueOf(params.lineB)), params.days, params.strainRnd, LocalDate.parse(params.strainADate)).get(i); + VirusStrain strain = getNewStrains(Boolean.valueOf(params.lineB)).get(i); + + Map infPerDayStrainX = new HashMap<>(episimConfig.getInfections_pers_per_day().getOrDefault(strain, new TreeMap<>())); + infPerDayStrainX.put(LocalDate.parse("2020-01-01"), 0); + for (int j = 0; j < 7; j++) { + infPerDayStrainX.put(date.plusDays(j), 4); + } + infPerDayStrainX.put(date.plusDays(7), 1); + episimConfig.setInfections_pers_per_day(strain, infPerDayStrainX); + } + + // save disease import + episimConfig.setInfections_pers_per_day(VirusStrain.OMICRON_BA1, infPerDayBa1); + episimConfig.setInfections_pers_per_day(VirusStrain.OMICRON_BA2, infPerDayBa2); + episimConfig.setInfections_pers_per_day(VirusStrain.OMICRON_BA5, infPerDayBa5); + + if (!params.StrainA.equals("off")) { + episimConfig.setInfections_pers_per_day(VirusStrain.STRAIN_A, infPerDayStrA); + } +// if (!params.StrainB.equals("off")) { +// episimConfig.setInfections_pers_per_day(VirusStrain.STRAIN_B, infPerDayStrB); +// } + } + + public static final class Params { + // general + @GenerateSeeds(5) + public long seed; + + // Vaccination Campaign + @StringParameter({"base"}) + String vacCamp; + + @StringParameter({"1.7"}) + public String StrainA; + +// @StringParameter({"2022-08-15", "2022-08-22", "2022-08-29", "2022-09-05", "2022-09-12", "2022-09-19", "2022-09-26"}) + @StringParameter({"2022-09-12"}) + public String strainADate; + + @Parameter({6., 12., 24.}) + public double esc; + + @Parameter({1., 6.}) + public double escL; + + @IntParameter({30}) + public int days; + + @StringParameter({"no"}) + public String strainRnd; + + @StringParameter({"true", "false"}) + public String lineB; + + @StringParameter({"true", "false"}) + public String iga; + + @StringParameter({"true", "false"}) + public String seasonal; + + } + + + public static void main(String[] args) { + String[] args2 = { + RunParallel.OPTION_SETUP, CologneBMBF202212XX_soup.class.getName(), + RunParallel.OPTION_PARAMS, Params.class.getName(), + RunParallel.OPTION_TASKS, Integer.toString(1), + RunParallel.OPTION_ITERATIONS, Integer.toString(1000), + RunParallel.OPTION_METADATA + }; + + RunParallel.main(args2); + } + + private static ArrayList getNewStrains(boolean lineB) { + ArrayList strains = new ArrayList(); + strains.add(VirusStrain.A_1); + strains.add(VirusStrain.A_2); + strains.add(VirusStrain.A_3); + strains.add(VirusStrain.A_4); + strains.add(VirusStrain.A_5); + strains.add(VirusStrain.A_6); + strains.add(VirusStrain.A_7); + strains.add(VirusStrain.A_8); + strains.add(VirusStrain.A_9); + strains.add(VirusStrain.A_10); + strains.add(VirusStrain.A_11); + strains.add(VirusStrain.A_12); + strains.add(VirusStrain.A_13); + strains.add(VirusStrain.A_14); + strains.add(VirusStrain.A_15); + strains.add(VirusStrain.A_16); + strains.add(VirusStrain.A_17); + strains.add(VirusStrain.A_18); + strains.add(VirusStrain.A_19); + strains.add(VirusStrain.A_20); + + + if (lineB) { + strains.add(1, VirusStrain.B_1); + strains.add(3, VirusStrain.B_2); + strains.add(5, VirusStrain.B_3); + strains.add(7, VirusStrain.B_4); + strains.add(9, VirusStrain.B_5); + strains.add(11, VirusStrain.B_6); + strains.add(13, VirusStrain.B_7); + strains.add(15, VirusStrain.B_8); + strains.add(17, VirusStrain.B_9); + strains.add(19, VirusStrain.B_10); + strains.add(21, VirusStrain.B_11); + strains.add(23, VirusStrain.B_12); + strains.add(25, VirusStrain.B_13); + strains.add(27, VirusStrain.B_14); + strains.add(29, VirusStrain.B_15); + strains.add(31, VirusStrain.B_16); + strains.add(33, VirusStrain.B_17); + strains.add(35, VirusStrain.B_18); + strains.add(37, VirusStrain.B_19); + strains.add(39, VirusStrain.B_20); + } + + return strains; + } + + private static ArrayList getDatesNewStrains(ArrayList strains, int days, String seed, LocalDate start) { + ArrayList dates = new ArrayList(); + + if (seed.equals("no")) { + for (LocalDate date = start; ; date = date.plusDays(1)) { + long daysBetween = ChronoUnit.DAYS.between(start, date); + if (daysBetween % days == 0) + dates.add(date); + if (dates.size() == strains.size()) + break; + } + return dates; + } + + else { + Random rand = new Random(Integer.parseInt(seed)); + for (LocalDate date = LocalDate.parse("2022-11-15"); ; date = date.plusDays(1)) { + if (rand.nextDouble() < 1. / days) + dates.add(date); + if (dates.size() == strains.size()) + break; + } + return dates; + } + + } + + +} + diff --git a/src/main/java/org/matsim/run/batch/CologneBMBF220211.java b/src/main/java/org/matsim/run/batch/CologneBMBF220211.java index dc98b9f81..85164364b 100644 --- a/src/main/java/org/matsim/run/batch/CologneBMBF220211.java +++ b/src/main/java/org/matsim/run/batch/CologneBMBF220211.java @@ -514,7 +514,7 @@ private void configureBooster(VaccinationConfigGroup vaccinationConfig, double b .setBoostWaitPeriod(boostAfter * 30 + 6 * 7); ; - vaccinationConfig.getOrAddParams(VaccinationType.omicronUpdate) + vaccinationConfig.getOrAddParams(VaccinationType.ba1Update) .setBoostWaitPeriod(boostAfter * 30 + 6 * 7); ; diff --git a/src/main/java/org/matsim/run/batch/CologneBMBF220217.java b/src/main/java/org/matsim/run/batch/CologneBMBF220217.java index 8509837e1..0bec78ff6 100644 --- a/src/main/java/org/matsim/run/batch/CologneBMBF220217.java +++ b/src/main/java/org/matsim/run/batch/CologneBMBF220217.java @@ -613,7 +613,7 @@ private void configureBooster(VaccinationConfigGroup vaccinationConfig, double b vaccinationConfig.setReVaccinationCapacity_pers_per_day(boosterVaccinations); vaccinationConfig.getOrAddParams(VaccinationType.mRNA).setBoostWaitPeriod(boostAfter_months * 30 + 6 * 7); - vaccinationConfig.getOrAddParams(VaccinationType.omicronUpdate).setBoostWaitPeriod(boostAfter_months * 30 + 6 * 7); + vaccinationConfig.getOrAddParams(VaccinationType.ba1Update).setBoostWaitPeriod(boostAfter_months * 30 + 6 * 7); vaccinationConfig.getOrAddParams(VaccinationType.vector).setBoostWaitPeriod(boostAfter_months * 30 + 9 * 7); ; } diff --git a/src/main/java/org/matsim/run/batch/CologneBMBF220217BA2.java b/src/main/java/org/matsim/run/batch/CologneBMBF220217BA2.java index e3a3cd79e..066f14c69 100644 --- a/src/main/java/org/matsim/run/batch/CologneBMBF220217BA2.java +++ b/src/main/java/org/matsim/run/batch/CologneBMBF220217BA2.java @@ -497,7 +497,7 @@ private void configureBooster(VaccinationConfigGroup vaccinationConfig, double b .setBoostWaitPeriod(boostAfter * 30 + 6 * 7); ; - vaccinationConfig.getOrAddParams(VaccinationType.omicronUpdate) + vaccinationConfig.getOrAddParams(VaccinationType.ba1Update) .setBoostWaitPeriod(boostAfter * 30 + 6 * 7); ; diff --git a/src/main/java/org/matsim/run/batch/CologneBMBF220426.java b/src/main/java/org/matsim/run/batch/CologneBMBF220426.java index a3ed2ee69..fcc6300ac 100644 --- a/src/main/java/org/matsim/run/batch/CologneBMBF220426.java +++ b/src/main/java/org/matsim/run/batch/CologneBMBF220426.java @@ -121,7 +121,7 @@ else if (immunityType == VaccinationType.vector) { initialAntibodies.get(VirusStrain.OMICRON_BA1).put(VirusStrain.SARS_CoV_2, 0.01); initialAntibodies.get(VirusStrain.OMICRON_BA2).put(VirusStrain.SARS_CoV_2, 0.01); initialAntibodies.get(VirusStrain.STRAIN_A).put(VirusStrain.SARS_CoV_2, 0.01); - initialAntibodies.get(VaccinationType.omicronUpdate).put(VirusStrain.SARS_CoV_2, mRNAAlpha); + initialAntibodies.get(VaccinationType.ba1Update).put(VirusStrain.SARS_CoV_2, mRNAAlpha); //Alpha initialAntibodies.get(VaccinationType.mRNA).put(VirusStrain.ALPHA, mRNAAlpha); @@ -132,7 +132,7 @@ else if (immunityType == VaccinationType.vector) { initialAntibodies.get(VirusStrain.OMICRON_BA1).put(VirusStrain.ALPHA, 0.01); initialAntibodies.get(VirusStrain.OMICRON_BA2).put(VirusStrain.ALPHA, 0.01); initialAntibodies.get(VirusStrain.STRAIN_A).put(VirusStrain.ALPHA, 0.01); - initialAntibodies.get(VaccinationType.omicronUpdate).put(VirusStrain.ALPHA, mRNAAlpha); + initialAntibodies.get(VaccinationType.ba1Update).put(VirusStrain.ALPHA, mRNAAlpha); //DELTA double mRNADelta = 10.9; @@ -144,7 +144,7 @@ else if (immunityType == VaccinationType.vector) { initialAntibodies.get(VirusStrain.OMICRON_BA1).put(VirusStrain.DELTA, 0.2 / 6.4); initialAntibodies.get(VirusStrain.OMICRON_BA2).put(VirusStrain.DELTA, 0.2 / 6.4); initialAntibodies.get(VirusStrain.STRAIN_A).put(VirusStrain.DELTA, 0.2 / 6.4); - initialAntibodies.get(VaccinationType.omicronUpdate).put(VirusStrain.DELTA, mRNADelta); + initialAntibodies.get(VaccinationType.ba1Update).put(VirusStrain.DELTA, mRNADelta); //BA.1 double mRNABA1 = 1.9; @@ -156,7 +156,7 @@ else if (immunityType == VaccinationType.vector) { initialAntibodies.get(VirusStrain.OMICRON_BA1).put(VirusStrain.OMICRON_BA1, 64.0 / 300.); initialAntibodies.get(VirusStrain.OMICRON_BA2).put(VirusStrain.OMICRON_BA1, 64.0 / 300. / 1.4); initialAntibodies.get(VirusStrain.STRAIN_A).put(VirusStrain.OMICRON_BA1, 64.0 / 300. / 1.4); - initialAntibodies.get(VaccinationType.omicronUpdate).put(VirusStrain.OMICRON_BA1, mRNAAlpha); + initialAntibodies.get(VaccinationType.ba1Update).put(VirusStrain.OMICRON_BA1, mRNAAlpha); //BA.2 double mRNABA2 = mRNABA1; @@ -168,7 +168,7 @@ else if (immunityType == VaccinationType.vector) { initialAntibodies.get(VirusStrain.OMICRON_BA1).put(VirusStrain.OMICRON_BA2, 64.0 / 300. / 1.4); initialAntibodies.get(VirusStrain.OMICRON_BA2).put(VirusStrain.OMICRON_BA2, 64.0 / 300.); initialAntibodies.get(VirusStrain.STRAIN_A).put(VirusStrain.OMICRON_BA2, 64.0 / 300.); - initialAntibodies.get(VaccinationType.omicronUpdate).put(VirusStrain.OMICRON_BA2, mRNAAlpha); + initialAntibodies.get(VaccinationType.ba1Update).put(VirusStrain.OMICRON_BA2, mRNAAlpha); //StrainA double mRNAStrainA = mRNABA2 / mutEscOm; @@ -180,7 +180,7 @@ else if (immunityType == VaccinationType.vector) { initialAntibodies.get(VirusStrain.OMICRON_BA1).put(VirusStrain.STRAIN_A, 64.0 / 300. / 1.4 / mutEscOm); initialAntibodies.get(VirusStrain.OMICRON_BA2).put(VirusStrain.STRAIN_A, 64.0 / 300./ mutEscOm); initialAntibodies.get(VirusStrain.STRAIN_A).put(VirusStrain.STRAIN_A, 64.0 / 300.); - initialAntibodies.get(VaccinationType.omicronUpdate).put(VirusStrain.STRAIN_A, mRNAAlpha / mutEscOm); + initialAntibodies.get(VaccinationType.ba1Update).put(VirusStrain.STRAIN_A, mRNAAlpha / mutEscOm); for (VaccinationType immunityType : VaccinationType.values()) { antibodyRefreshFactors.put(immunityType, new EnumMap<>( VirusStrain.class ) ); @@ -192,7 +192,7 @@ else if (immunityType == VaccinationType.vector) { else if (immunityType == VaccinationType.vector) { antibodyRefreshFactors.get(immunityType).put(virusStrain, 5.0); } - else if (immunityType == VaccinationType.omicronUpdate) { + else if (immunityType == VaccinationType.ba1Update) { antibodyRefreshFactors.get(immunityType).put(virusStrain, 15.0); } else { @@ -657,7 +657,7 @@ private void configureBooster(VaccinationConfigGroup vaccinationConfig, double b .setBoostWaitPeriod(boostAfter * 30 + 6 * 7); ; - vaccinationConfig.getOrAddParams(VaccinationType.omicronUpdate) + vaccinationConfig.getOrAddParams(VaccinationType.ba1Update) .setBoostWaitPeriod(boostAfter * 30 + 6 * 7); ; diff --git a/src/main/java/org/matsim/run/batch/CologneBMBF220628.java b/src/main/java/org/matsim/run/batch/CologneBMBF220628.java index f3b47b325..0bdcd0dde 100644 --- a/src/main/java/org/matsim/run/batch/CologneBMBF220628.java +++ b/src/main/java/org/matsim/run/batch/CologneBMBF220628.java @@ -143,7 +143,7 @@ else if (immunityType == VaccinationType.vector) { initialAntibodies.get(VirusStrain.OMICRON_BA2).put(VirusStrain.SARS_CoV_2, 0.01); initialAntibodies.get(VirusStrain.OMICRON_BA5).put(VirusStrain.SARS_CoV_2, 0.01); initialAntibodies.get(VirusStrain.STRAIN_A).put(VirusStrain.SARS_CoV_2, 0.01); - initialAntibodies.get(VaccinationType.omicronUpdate).put(VirusStrain.SARS_CoV_2, mRNAAlpha); + initialAntibodies.get(VaccinationType.ba1Update).put(VirusStrain.SARS_CoV_2, mRNAAlpha); //Alpha initialAntibodies.get(VaccinationType.mRNA).put(VirusStrain.ALPHA, mRNAAlpha); @@ -155,7 +155,7 @@ else if (immunityType == VaccinationType.vector) { initialAntibodies.get(VirusStrain.OMICRON_BA2).put(VirusStrain.ALPHA, 0.01); initialAntibodies.get(VirusStrain.OMICRON_BA5).put(VirusStrain.ALPHA, 0.01); initialAntibodies.get(VirusStrain.STRAIN_A).put(VirusStrain.ALPHA, 0.01); - initialAntibodies.get(VaccinationType.omicronUpdate).put(VirusStrain.ALPHA, mRNAAlpha); + initialAntibodies.get(VaccinationType.ba1Update).put(VirusStrain.ALPHA, mRNAAlpha); //DELTA double mRNADelta = 10.9; @@ -168,7 +168,7 @@ else if (immunityType == VaccinationType.vector) { initialAntibodies.get(VirusStrain.OMICRON_BA2).put(VirusStrain.DELTA, 0.2 / 6.4); initialAntibodies.get(VirusStrain.OMICRON_BA5).put(VirusStrain.DELTA, 0.2 / 6.4); initialAntibodies.get(VirusStrain.STRAIN_A).put(VirusStrain.DELTA, 0.2 / 6.4); - initialAntibodies.get(VaccinationType.omicronUpdate).put(VirusStrain.DELTA, mRNADelta); + initialAntibodies.get(VaccinationType.ba1Update).put(VirusStrain.DELTA, mRNADelta); //BA.1 double mRNABA1 = 1.9; @@ -181,7 +181,7 @@ else if (immunityType == VaccinationType.vector) { initialAntibodies.get(VirusStrain.OMICRON_BA2).put(VirusStrain.OMICRON_BA1, 64.0 / 300. / 1.4); initialAntibodies.get(VirusStrain.OMICRON_BA5).put(VirusStrain.OMICRON_BA1, 64.0 / 300. / 1.4); //todo: is 1.4 initialAntibodies.get(VirusStrain.STRAIN_A).put(VirusStrain.OMICRON_BA1, 64.0 / 300. / 1.4); - initialAntibodies.get(VaccinationType.omicronUpdate).put(VirusStrain.OMICRON_BA1, mRNAAlpha); + initialAntibodies.get(VaccinationType.ba1Update).put(VirusStrain.OMICRON_BA1, mRNAAlpha); //BA.2 double mRNABA2 = mRNABA1; @@ -194,7 +194,7 @@ else if (immunityType == VaccinationType.vector) { initialAntibodies.get(VirusStrain.OMICRON_BA2).put(VirusStrain.OMICRON_BA2, 64.0 / 300.); initialAntibodies.get(VirusStrain.OMICRON_BA5).put(VirusStrain.OMICRON_BA2, 64.0 / 300. / 1.4); initialAntibodies.get(VirusStrain.STRAIN_A).put(VirusStrain.OMICRON_BA2, 64.0 / 300.); - initialAntibodies.get(VaccinationType.omicronUpdate).put(VirusStrain.OMICRON_BA2, mRNAAlpha); + initialAntibodies.get(VaccinationType.ba1Update).put(VirusStrain.OMICRON_BA2, mRNAAlpha); //BA.5 @@ -208,7 +208,7 @@ else if (immunityType == VaccinationType.vector) { initialAntibodies.get(VirusStrain.OMICRON_BA2).put(VirusStrain.OMICRON_BA5, 64.0 / 300./ mutEscBa5); initialAntibodies.get(VirusStrain.OMICRON_BA5).put(VirusStrain.OMICRON_BA5, 64.0 / 300.); initialAntibodies.get(VirusStrain.STRAIN_A).put(VirusStrain.OMICRON_BA5, 64.0 / 300./ mutEscBa5); //todo ??? - initialAntibodies.get(VaccinationType.omicronUpdate).put(VirusStrain.OMICRON_BA5, mRNAAlpha / mutEscBa5); + initialAntibodies.get(VaccinationType.ba1Update).put(VirusStrain.OMICRON_BA5, mRNAAlpha / mutEscBa5); //StrainA double mRNAStrainA = mRNABa5 / mutEscStrainA; @@ -221,7 +221,7 @@ else if (immunityType == VaccinationType.vector) { initialAntibodies.get(VirusStrain.OMICRON_BA2).put(VirusStrain.STRAIN_A, 64.0 / 300./ mutEscBa5 /mutEscStrainA); initialAntibodies.get(VirusStrain.OMICRON_BA5).put(VirusStrain.STRAIN_A, 64.0 / 300. / mutEscBa5 / mutEscStrainA); initialAntibodies.get(VirusStrain.STRAIN_A).put(VirusStrain.STRAIN_A, 64.0 / 300.); - initialAntibodies.get(VaccinationType.omicronUpdate).put(VirusStrain.STRAIN_A, mRNAAlpha / mutEscBa5 /mutEscStrainA); + initialAntibodies.get(VaccinationType.ba1Update).put(VirusStrain.STRAIN_A, mRNAAlpha / mutEscBa5 /mutEscStrainA); for (VaccinationType immunityType : VaccinationType.values()) { antibodyRefreshFactors.put(immunityType, new EnumMap<>( VirusStrain.class ) ); @@ -233,7 +233,7 @@ else if (immunityType == VaccinationType.vector) { else if (immunityType == VaccinationType.vector) { antibodyRefreshFactors.get(immunityType).put(virusStrain, 5.0); } - else if (immunityType == VaccinationType.omicronUpdate) { + else if (immunityType == VaccinationType.ba1Update) { antibodyRefreshFactors.get(immunityType).put(virusStrain, 15.0); } else { diff --git a/src/main/java/org/matsim/run/batch/CologneBMBF220628_3G.java b/src/main/java/org/matsim/run/batch/CologneBMBF220628_3G.java index 741e260a6..66947f742 100644 --- a/src/main/java/org/matsim/run/batch/CologneBMBF220628_3G.java +++ b/src/main/java/org/matsim/run/batch/CologneBMBF220628_3G.java @@ -219,7 +219,7 @@ else if (immunityType == VaccinationType.vector) { initialAntibodies.get(VirusStrain.OMICRON_BA2).put(VirusStrain.SARS_CoV_2, 0.01); initialAntibodies.get(VirusStrain.OMICRON_BA5).put(VirusStrain.SARS_CoV_2, 0.01); initialAntibodies.get(VirusStrain.STRAIN_A).put(VirusStrain.SARS_CoV_2, 0.01); - initialAntibodies.get(VaccinationType.omicronUpdate).put(VirusStrain.SARS_CoV_2, mRNAAlpha); + initialAntibodies.get(VaccinationType.ba1Update).put(VirusStrain.SARS_CoV_2, mRNAAlpha); //Alpha initialAntibodies.get(VaccinationType.mRNA).put(VirusStrain.ALPHA, mRNAAlpha); @@ -231,7 +231,7 @@ else if (immunityType == VaccinationType.vector) { initialAntibodies.get(VirusStrain.OMICRON_BA2).put(VirusStrain.ALPHA, 0.01); initialAntibodies.get(VirusStrain.OMICRON_BA5).put(VirusStrain.ALPHA, 0.01); initialAntibodies.get(VirusStrain.STRAIN_A).put(VirusStrain.ALPHA, 0.01); - initialAntibodies.get(VaccinationType.omicronUpdate).put(VirusStrain.ALPHA, mRNAAlpha); + initialAntibodies.get(VaccinationType.ba1Update).put(VirusStrain.ALPHA, mRNAAlpha); //DELTA double mRNADelta = 10.9; @@ -244,7 +244,7 @@ else if (immunityType == VaccinationType.vector) { initialAntibodies.get(VirusStrain.OMICRON_BA2).put(VirusStrain.DELTA, 0.2 / 6.4); initialAntibodies.get(VirusStrain.OMICRON_BA5).put(VirusStrain.DELTA, 0.2 / 6.4); initialAntibodies.get(VirusStrain.STRAIN_A).put(VirusStrain.DELTA, 0.2 / 6.4); - initialAntibodies.get(VaccinationType.omicronUpdate).put(VirusStrain.DELTA, mRNADelta); + initialAntibodies.get(VaccinationType.ba1Update).put(VirusStrain.DELTA, mRNADelta); //BA.1 double mRNABA1 = 1.9; @@ -257,7 +257,7 @@ else if (immunityType == VaccinationType.vector) { initialAntibodies.get(VirusStrain.OMICRON_BA2).put(VirusStrain.OMICRON_BA1, 64.0 / 300. / 1.4); initialAntibodies.get(VirusStrain.OMICRON_BA5).put(VirusStrain.OMICRON_BA1, 64.0 / 300. / 1.4); //todo: is 1.4 initialAntibodies.get(VirusStrain.STRAIN_A).put(VirusStrain.OMICRON_BA1, 64.0 / 300. / 1.4); - initialAntibodies.get(VaccinationType.omicronUpdate).put(VirusStrain.OMICRON_BA1, mRNAAlpha); + initialAntibodies.get(VaccinationType.ba1Update).put(VirusStrain.OMICRON_BA1, mRNAAlpha); //BA.2 double mRNABA2 = mRNABA1; @@ -270,7 +270,7 @@ else if (immunityType == VaccinationType.vector) { initialAntibodies.get(VirusStrain.OMICRON_BA2).put(VirusStrain.OMICRON_BA2, 64.0 / 300.); initialAntibodies.get(VirusStrain.OMICRON_BA5).put(VirusStrain.OMICRON_BA2, 64.0 / 300. / 1.4); initialAntibodies.get(VirusStrain.STRAIN_A).put(VirusStrain.OMICRON_BA2, 64.0 / 300.); - initialAntibodies.get(VaccinationType.omicronUpdate).put(VirusStrain.OMICRON_BA2, mRNAAlpha); + initialAntibodies.get(VaccinationType.ba1Update).put(VirusStrain.OMICRON_BA2, mRNAAlpha); //BA.5 @@ -284,7 +284,7 @@ else if (immunityType == VaccinationType.vector) { initialAntibodies.get(VirusStrain.OMICRON_BA2).put(VirusStrain.OMICRON_BA5, 64.0 / 300./ mutEscBa5); initialAntibodies.get(VirusStrain.OMICRON_BA5).put(VirusStrain.OMICRON_BA5, 64.0 / 300.); initialAntibodies.get(VirusStrain.STRAIN_A).put(VirusStrain.OMICRON_BA5, 64.0 / 300./ mutEscBa5); //todo ??? - initialAntibodies.get(VaccinationType.omicronUpdate).put(VirusStrain.OMICRON_BA5, mRNAAlpha / mutEscBa5); + initialAntibodies.get(VaccinationType.ba1Update).put(VirusStrain.OMICRON_BA5, mRNAAlpha / mutEscBa5); //StrainA double mRNAStrainA = mRNABa5 / mutEscStrainA; @@ -297,7 +297,7 @@ else if (immunityType == VaccinationType.vector) { initialAntibodies.get(VirusStrain.OMICRON_BA2).put(VirusStrain.STRAIN_A, 64.0 / 300./ mutEscBa5 /mutEscStrainA); initialAntibodies.get(VirusStrain.OMICRON_BA5).put(VirusStrain.STRAIN_A, 64.0 / 300. / mutEscBa5 / mutEscStrainA); initialAntibodies.get(VirusStrain.STRAIN_A).put(VirusStrain.STRAIN_A, 64.0 / 300.); - initialAntibodies.get(VaccinationType.omicronUpdate).put(VirusStrain.STRAIN_A, mRNAAlpha / mutEscBa5 /mutEscStrainA); + initialAntibodies.get(VaccinationType.ba1Update).put(VirusStrain.STRAIN_A, mRNAAlpha / mutEscBa5 /mutEscStrainA); for (VaccinationType immunityType : VaccinationType.values()) { antibodyRefreshFactors.put(immunityType, new EnumMap<>( VirusStrain.class ) ); @@ -309,7 +309,7 @@ else if (immunityType == VaccinationType.vector) { else if (immunityType == VaccinationType.vector) { antibodyRefreshFactors.get(immunityType).put(virusStrain, 5.0); } - else if (immunityType == VaccinationType.omicronUpdate) { + else if (immunityType == VaccinationType.ba1Update) { antibodyRefreshFactors.get(immunityType).put(virusStrain, 15.0); } else { diff --git a/src/main/java/org/matsim/run/batch/CologneCalibration.java b/src/main/java/org/matsim/run/batch/CologneCalibration.java index 3c1e8bca1..a1f650888 100644 --- a/src/main/java/org/matsim/run/batch/CologneCalibration.java +++ b/src/main/java/org/matsim/run/batch/CologneCalibration.java @@ -461,7 +461,7 @@ private void configureBooster(VaccinationConfigGroup vaccinationConfig, double b .setBoostWaitPeriod(boostAfter * 30 + 6 * 7); ; - vaccinationConfig.getOrAddParams(VaccinationType.omicronUpdate) + vaccinationConfig.getOrAddParams(VaccinationType.ba1Update) .setBoostWaitPeriod(boostAfter * 30 + 6 * 7); ; diff --git a/src/main/java/org/matsim/run/batch/CologneJR.java b/src/main/java/org/matsim/run/batch/CologneJR.java new file mode 100644 index 000000000..8d29664ef --- /dev/null +++ b/src/main/java/org/matsim/run/batch/CologneJR.java @@ -0,0 +1,771 @@ +package org.matsim.run.batch; + +import com.google.inject.AbstractModule; +import com.google.inject.Module; +import com.google.inject.Singleton; +import com.google.inject.multibindings.Multibinder; +import com.google.inject.util.Modules; +import it.unimi.dsi.fastutil.ints.Int2DoubleAVLTreeMap; +import it.unimi.dsi.fastutil.ints.Int2DoubleMap; +import org.matsim.core.config.Config; +import org.matsim.core.config.ConfigUtils; +import org.matsim.episim.*; +import org.matsim.episim.analysis.*; +import org.matsim.episim.model.*; +import org.matsim.episim.model.testing.TestType; +import org.matsim.episim.model.vaccination.VaccinationModel; +import org.matsim.episim.model.vaccination.VaccinationStrategyBMBF0617; +import org.matsim.episim.policy.FixedPolicy; +import org.matsim.episim.policy.Restriction; +import org.matsim.run.RunParallel; +import org.matsim.run.modules.SnzCologneProductionScenario; + +import javax.annotation.Nullable; +import java.time.LocalDate; +import java.util.*; + + +/** + * Batch for Bmbf runs + */ +public class CologneJR implements BatchRun { + + boolean DEBUG_MODE = false; + int runCount = 0; + + @Nullable + @Override + public Module getBindings(int id, @Nullable Params params) { + return Modules.override(getBindings(0.0, params)).with(new AbstractModule() { + @Override + protected void configure() { + +// Multibinder set = Multibinder.newSetBinder(binder(), VaccinationModel.class); +// +// set.addBinding().to(VaccinationStrategyBMBF0617.class).in(Singleton.class); + + + double mutEscBa5 = 3.0; + double mutEscStrainA = 0.; + +// LocalDate start = null; +// VaccinationType vaccinationType = null; + +// Int2DoubleMap compliance = new Int2DoubleAVLTreeMap(); +// compliance.put(60, 0.0); +// compliance.put(18, 0.0); +// compliance.put(12, 0.0); +// compliance.put(0, 0.0); + + + + if (params != null) { + mutEscBa5 = params.ba5Esc; + + mutEscStrainA = params.strAEsc; +// +// start = LocalDate.parse(params.resDate); +// vaccinationType = VaccinationType.valueOf(params.vacType); +// +// +// if (params.vacCamp.equals("age")) { +// compliance.put(60, 0.85); // 60+ +// compliance.put(18, 0.55); // 18-59 +// compliance.put(12, 0.20); // 12-17 +// compliance.put(0, 0.0); // 0 - 11 +// } +// else if (params.vacCamp.equals("eu")) { +// compliance.put(60, 0.40); // half of 80% (which reflects the current percentage of people in Dland who are boostered) +// compliance.put(18, 0.); +// compliance.put(12, 0.); +// compliance.put(0, 0.); +// } +// else if (params.vacCamp.equals("off")) { +// +// } else { +// throw new RuntimeException("Not a valid option for vaccinationCampaignType"); +// } + } +// +// bind(VaccinationStrategyBMBF0617.Config.class).toInstance(new VaccinationStrategyBMBF0617.Config(start, 30, vaccinationType, compliance)); + + //initial antibodies + Map> initialAntibodies = new HashMap<>(); + Map> antibodyRefreshFactors = new HashMap<>(); + configureAntibodies(initialAntibodies, antibodyRefreshFactors, mutEscBa5,mutEscStrainA); + + AntibodyModel.Config antibodyConfig = new AntibodyModel.Config(initialAntibodies, antibodyRefreshFactors); + + double immuneSigma = 3.0; + if (params != null) { + antibodyConfig.setImmuneReponseSigma(immuneSigma); + } + + bind(AntibodyModel.Config.class).toInstance(antibodyConfig); + + } + + private void configureAntibodies(Map> initialAntibodies, + Map> antibodyRefreshFactors, + double mutEscBa5, double mutEscStrainA) { + for (VaccinationType immunityType : VaccinationType.values()) { + initialAntibodies.put(immunityType, new EnumMap<>( VirusStrain.class ) ); + for (VirusStrain virusStrain : VirusStrain.values()) { + + if (immunityType == VaccinationType.mRNA) { + initialAntibodies.get(immunityType).put(virusStrain, 29.2); //10.0 + } + else if (immunityType == VaccinationType.vector) { + initialAntibodies.get(immunityType).put(virusStrain, 6.8); //2.5 + } + else { + initialAntibodies.get(immunityType).put(virusStrain, 5.0); + } + } + } + + for (VirusStrain immunityType : VirusStrain.values()) { + initialAntibodies.put(immunityType, new EnumMap<>( VirusStrain.class ) ); + for (VirusStrain virusStrain : VirusStrain.values()) { + initialAntibodies.get(immunityType).put(virusStrain, 5.0); + } + } + + //mRNAAlpha, mRNADelta, mRNABA1 comes from Sydney's calibration. + //The other values come from Rössler et al. + + //Wildtype + double mRNAAlpha = 29.2; + initialAntibodies.get(VaccinationType.mRNA).put(VirusStrain.SARS_CoV_2, mRNAAlpha); + initialAntibodies.get(VaccinationType.vector).put(VirusStrain.SARS_CoV_2, mRNAAlpha * 210. / 700.); + initialAntibodies.get(VirusStrain.SARS_CoV_2).put(VirusStrain.SARS_CoV_2, mRNAAlpha * 300. / 700.); + initialAntibodies.get(VirusStrain.ALPHA).put(VirusStrain.SARS_CoV_2, mRNAAlpha * 300. / 700.); + initialAntibodies.get(VirusStrain.DELTA).put(VirusStrain.SARS_CoV_2, mRNAAlpha * 210. / 700.); + initialAntibodies.get(VirusStrain.OMICRON_BA1).put(VirusStrain.SARS_CoV_2, 0.01); + initialAntibodies.get(VirusStrain.OMICRON_BA2).put(VirusStrain.SARS_CoV_2, 0.01); + initialAntibodies.get(VirusStrain.OMICRON_BA5).put(VirusStrain.SARS_CoV_2, 0.01); + initialAntibodies.get(VirusStrain.STRAIN_A).put(VirusStrain.SARS_CoV_2, 0.01); + initialAntibodies.get(VaccinationType.ba1Update).put(VirusStrain.SARS_CoV_2, mRNAAlpha); + + //Alpha + initialAntibodies.get(VaccinationType.mRNA).put(VirusStrain.ALPHA, mRNAAlpha); + initialAntibodies.get(VaccinationType.vector).put(VirusStrain.ALPHA, mRNAAlpha * 210. / 700.); + initialAntibodies.get(VirusStrain.SARS_CoV_2).put(VirusStrain.ALPHA, mRNAAlpha * 300. / 700.); + initialAntibodies.get(VirusStrain.ALPHA).put(VirusStrain.ALPHA, mRNAAlpha * 300. / 700.); + initialAntibodies.get(VirusStrain.DELTA).put(VirusStrain.ALPHA, mRNAAlpha * 210. / 700.); + initialAntibodies.get(VirusStrain.OMICRON_BA1).put(VirusStrain.ALPHA, 0.01); + initialAntibodies.get(VirusStrain.OMICRON_BA2).put(VirusStrain.ALPHA, 0.01); + initialAntibodies.get(VirusStrain.OMICRON_BA5).put(VirusStrain.ALPHA, 0.01); + initialAntibodies.get(VirusStrain.STRAIN_A).put(VirusStrain.ALPHA, 0.01); + initialAntibodies.get(VaccinationType.ba1Update).put(VirusStrain.ALPHA, mRNAAlpha); + + //DELTA + double mRNADelta = 10.9; + initialAntibodies.get(VaccinationType.mRNA).put(VirusStrain.DELTA, mRNADelta); + initialAntibodies.get(VaccinationType.vector).put(VirusStrain.DELTA, mRNADelta * 150./300.); + initialAntibodies.get(VirusStrain.SARS_CoV_2).put(VirusStrain.DELTA, mRNADelta * 64./300.); + initialAntibodies.get(VirusStrain.ALPHA).put(VirusStrain.DELTA, mRNADelta * 64./300.); + initialAntibodies.get(VirusStrain.DELTA).put(VirusStrain.DELTA, mRNADelta * 450./300.); + initialAntibodies.get(VirusStrain.OMICRON_BA1).put(VirusStrain.DELTA, 0.2 / 6.4); + initialAntibodies.get(VirusStrain.OMICRON_BA2).put(VirusStrain.DELTA, 0.2 / 6.4); + initialAntibodies.get(VirusStrain.OMICRON_BA5).put(VirusStrain.DELTA, 0.2 / 6.4); + initialAntibodies.get(VirusStrain.STRAIN_A).put(VirusStrain.DELTA, 0.2 / 6.4); + initialAntibodies.get(VaccinationType.ba1Update).put(VirusStrain.DELTA, mRNADelta); + + //BA.1 + double mRNABA1 = 1.9; + initialAntibodies.get(VaccinationType.mRNA).put(VirusStrain.OMICRON_BA1, mRNABA1); + initialAntibodies.get(VaccinationType.vector).put(VirusStrain.OMICRON_BA1, mRNABA1 * 4./20.); //??? + initialAntibodies.get(VirusStrain.SARS_CoV_2).put(VirusStrain.OMICRON_BA1, mRNABA1 * 6./20.); + initialAntibodies.get(VirusStrain.ALPHA).put(VirusStrain.OMICRON_BA1, mRNABA1 * 6./20.); + initialAntibodies.get(VirusStrain.DELTA).put(VirusStrain.OMICRON_BA1, mRNABA1 * 8./20.); + initialAntibodies.get(VirusStrain.OMICRON_BA1).put(VirusStrain.OMICRON_BA1, 64.0 / 300.); + initialAntibodies.get(VirusStrain.OMICRON_BA2).put(VirusStrain.OMICRON_BA1, 64.0 / 300. / 1.4); + initialAntibodies.get(VirusStrain.OMICRON_BA5).put(VirusStrain.OMICRON_BA1, 64.0 / 300. / 1.4); //todo: is 1.4 + initialAntibodies.get(VirusStrain.STRAIN_A).put(VirusStrain.OMICRON_BA1, 64.0 / 300. / 1.4); + initialAntibodies.get(VaccinationType.ba1Update).put(VirusStrain.OMICRON_BA1, mRNAAlpha); + + //BA.2 + double mRNABA2 = mRNABA1; + initialAntibodies.get(VaccinationType.mRNA).put(VirusStrain.OMICRON_BA2, mRNABA2); + initialAntibodies.get(VaccinationType.vector).put(VirusStrain.OMICRON_BA2, mRNABA2 * 4./20.); + initialAntibodies.get(VirusStrain.SARS_CoV_2).put(VirusStrain.OMICRON_BA2, mRNABA2 * 6./20.); + initialAntibodies.get(VirusStrain.ALPHA).put(VirusStrain.OMICRON_BA2, mRNABA2 * 6./20.); + initialAntibodies.get(VirusStrain.DELTA).put(VirusStrain.OMICRON_BA2, mRNABA2 * 8./20.); + initialAntibodies.get(VirusStrain.OMICRON_BA1).put(VirusStrain.OMICRON_BA2, 64.0 / 300. / 1.4); + initialAntibodies.get(VirusStrain.OMICRON_BA2).put(VirusStrain.OMICRON_BA2, 64.0 / 300.); + initialAntibodies.get(VirusStrain.OMICRON_BA5).put(VirusStrain.OMICRON_BA2, 64.0 / 300. / 1.4); + initialAntibodies.get(VirusStrain.STRAIN_A).put(VirusStrain.OMICRON_BA2, 64.0 / 300.); + initialAntibodies.get(VaccinationType.ba1Update).put(VirusStrain.OMICRON_BA2, mRNAAlpha); + + + //BA.5 + double mRNABa5 = mRNABA2 / mutEscBa5; + initialAntibodies.get(VaccinationType.mRNA).put(VirusStrain.OMICRON_BA5, mRNABa5); + initialAntibodies.get(VaccinationType.vector).put(VirusStrain.OMICRON_BA5, mRNABa5 * 4./20.); + initialAntibodies.get(VirusStrain.SARS_CoV_2).put(VirusStrain.OMICRON_BA5, mRNABa5 * 6./20.); + initialAntibodies.get(VirusStrain.ALPHA).put(VirusStrain.OMICRON_BA5, mRNABa5 * 6./20.); + initialAntibodies.get(VirusStrain.DELTA).put(VirusStrain.OMICRON_BA5, mRNABa5 * 8./20.); + initialAntibodies.get(VirusStrain.OMICRON_BA1).put(VirusStrain.OMICRON_BA5, 64.0 / 300. / 1.4 / mutEscBa5);// todo: do we need 1.4? + initialAntibodies.get(VirusStrain.OMICRON_BA2).put(VirusStrain.OMICRON_BA5, 64.0 / 300./ mutEscBa5); + initialAntibodies.get(VirusStrain.OMICRON_BA5).put(VirusStrain.OMICRON_BA5, 64.0 / 300.); + initialAntibodies.get(VirusStrain.STRAIN_A).put(VirusStrain.OMICRON_BA5, 64.0 / 300./ mutEscBa5); //todo ??? + initialAntibodies.get(VaccinationType.ba1Update).put(VirusStrain.OMICRON_BA5, mRNAAlpha / mutEscBa5); + + //StrainA + double mRNAStrainA = mRNABa5 / mutEscStrainA; + initialAntibodies.get(VaccinationType.mRNA).put(VirusStrain.STRAIN_A, mRNAStrainA); + initialAntibodies.get(VaccinationType.vector).put(VirusStrain.STRAIN_A, mRNAStrainA * 4./20.); + initialAntibodies.get(VirusStrain.SARS_CoV_2).put(VirusStrain.STRAIN_A, mRNAStrainA * 6./20.); + initialAntibodies.get(VirusStrain.ALPHA).put(VirusStrain.STRAIN_A, mRNAStrainA * 6./20.); + initialAntibodies.get(VirusStrain.DELTA).put(VirusStrain.STRAIN_A, mRNAStrainA * 8./20.); + initialAntibodies.get(VirusStrain.OMICRON_BA1).put(VirusStrain.STRAIN_A, 64.0 / 300. / mutEscBa5 /mutEscStrainA); + initialAntibodies.get(VirusStrain.OMICRON_BA2).put(VirusStrain.STRAIN_A, 64.0 / 300./ mutEscBa5 /mutEscStrainA); + initialAntibodies.get(VirusStrain.OMICRON_BA5).put(VirusStrain.STRAIN_A, 64.0 / 300. / mutEscBa5 / mutEscStrainA); + initialAntibodies.get(VirusStrain.STRAIN_A).put(VirusStrain.STRAIN_A, 64.0 / 300.); + initialAntibodies.get(VaccinationType.ba1Update).put(VirusStrain.STRAIN_A, mRNAAlpha / mutEscBa5 /mutEscStrainA); + + for (VaccinationType immunityType : VaccinationType.values()) { + antibodyRefreshFactors.put(immunityType, new EnumMap<>( VirusStrain.class ) ); + for (VirusStrain virusStrain : VirusStrain.values()) { + + if (immunityType == VaccinationType.mRNA) { + antibodyRefreshFactors.get(immunityType).put(virusStrain, 15.0); + } + else if (immunityType == VaccinationType.vector) { + antibodyRefreshFactors.get(immunityType).put(virusStrain, 5.0); + } + else if (immunityType == VaccinationType.ba1Update) { + antibodyRefreshFactors.get(immunityType).put(virusStrain, 15.0); + } + else { + antibodyRefreshFactors.get(immunityType).put(virusStrain, Double.NaN); + } + + } + } + + for (VirusStrain immunityType : VirusStrain.values()) { + antibodyRefreshFactors.put(immunityType, new EnumMap<>( VirusStrain.class ) ); + for (VirusStrain virusStrain : VirusStrain.values()) { + antibodyRefreshFactors.get(immunityType).put(virusStrain, 15.0); + } + } + + + +// System.out.print("immunityGiver"); +// for (VirusStrain immunityFrom : VirusStrain.values()) { +// if (immunityFrom == VirusStrain.OMICRON_BA1) { +// System.out.print( "," + "BA.1"); +// } else if (immunityFrom == VirusStrain.OMICRON_BA2) { +// System.out.print( "," + "BA.2"); +// } else { +// System.out.print( "," + immunityFrom); +// } +// } +// +// +// for (ImmunityEvent immunityGiver : VaccinationType.values()) { +// System.out.print("\n" + immunityGiver); +// for (VirusStrain immunityFrom : VirusStrain.values()) { +// System.out.print("," + String.format("%.3g", initialAntibodies.get(immunityGiver).get(immunityFrom))); +// } +// } +// for (ImmunityEvent immunityGiver : VirusStrain.values()) { +// System.out.print("\n" + immunityGiver); +// for (VirusStrain immunityFrom : VirusStrain.values()) { +// System.out.print("," + String.format("%.3g", initialAntibodies.get(immunityGiver).get(immunityFrom))); +// } +// } +// +// System.out.println(); + + } + }); + + } + + private SnzCologneProductionScenario getBindings(double pHousehold, Params params) { + return new SnzCologneProductionScenario.Builder() + .setCarnivalModel(SnzCologneProductionScenario.CarnivalModel.yes) + .setSebastianUpdate(true) + .setLeisureCorrection(1.3)//params == null ? 0.0 : params.actCorrection) + .setScaleForActivityLevels(1.3) + .setSuscHouseholds_pct(pHousehold) + .setActivityHandling(EpisimConfigGroup.ActivityHandling.startOfDay) +// .setTestingModel(params != null ? FlexibleTestingModel.class : DefaultTestingModel.class) + .setInfectionModel(InfectionModelWithAntibodies.class) + .build(); + } + + @Override + public Metadata getMetadata() { + return Metadata.of("cologne", "calibration"); + } + + @Override + public Collection postProcessing() { + return List.of( + new VaccinationEffectiveness().withArgs(), + new RValuesFromEvents().withArgs(), + new VaccinationEffectivenessFromPotentialInfections().withArgs("--remove-infected"), + new HospitalNumbersFromEvents().withArgs(), + new SecondaryAttackRateFromEvents().withArgs() + ); + } + + @Override + public Config prepareConfig(int id, Params params) { + + if (DEBUG_MODE) { + if (runCount == 0){ //&& params.strAEsc != 0.0 && params.ba5Inf == 0. && params.eduTest.equals("true")) { + runCount++; + } else { + return null; + } + } + + SnzCologneProductionScenario module = getBindings(0.0, params); + + Config config = module.config(); + + + config.global().setRandomSeed(params.seed); + + EpisimConfigGroup episimConfig = ConfigUtils.addOrGetModule(config, EpisimConfigGroup.class); + + + double thFactor = 1.2; + episimConfig.setCalibrationParameter(episimConfig.getCalibrationParameter() * thFactor); + + + + //--------------------------------------- + // S T R A I N S + //--------------------------------------- + + VirusStrainConfigGroup virusStrainConfigGroup = ConfigUtils.addOrGetModule(config, VirusStrainConfigGroup.class); + + +// virusStrainConfigGroup.getOrAddParams(VirusStrain.DELTA).setInfectiousness(virusStrainConfigGroup.getOrAddParams(VirusStrain.DELTA).getInfectiousness() * params.deltaTheta); + + //BA5 + double ba5Inf = 1.0; + double oHos = virusStrainConfigGroup.getParams(VirusStrain.OMICRON_BA2).getFactorSeriouslySick(); + virusStrainConfigGroup.getOrAddParams(VirusStrain.OMICRON_BA5).setInfectiousness(virusStrainConfigGroup.getParams(VirusStrain.OMICRON_BA2).getInfectiousness() * ba5Inf); + virusStrainConfigGroup.getOrAddParams(VirusStrain.OMICRON_BA5).setFactorSeriouslySick(oHos); + virusStrainConfigGroup.getOrAddParams(VirusStrain.OMICRON_BA5).setFactorSeriouslySickVaccinated(oHos); + virusStrainConfigGroup.getOrAddParams(VirusStrain.OMICRON_BA5).setFactorCritical(oHos); + + +// STRAIN_A + double strAInf = 1.0; + if (params.strAEsc != 0.) { + + virusStrainConfigGroup.getOrAddParams(VirusStrain.STRAIN_A).setInfectiousness(virusStrainConfigGroup.getParams(VirusStrain.OMICRON_BA2).getInfectiousness() * ba5Inf * strAInf); + virusStrainConfigGroup.getOrAddParams(VirusStrain.STRAIN_A).setFactorSeriouslySick(oHos); + virusStrainConfigGroup.getOrAddParams(VirusStrain.STRAIN_A).setFactorSeriouslySickVaccinated(oHos); + virusStrainConfigGroup.getOrAddParams(VirusStrain.STRAIN_A).setFactorCritical(oHos); + } + + // remove age-based susceptibility of strains starting with DELTA + + if (!Boolean.parseBoolean("false")) { + TreeMap nonSteppedAgeSusceptibility = new TreeMap<>(Map.of( + 19, 1d, + 20, 1d + )); + + for (VirusStrain strain : List.of(VirusStrain.DELTA, VirusStrain.OMICRON_BA1, VirusStrain.OMICRON_BA2, VirusStrain.OMICRON_BA5, VirusStrain.STRAIN_A)) { + virusStrainConfigGroup.getOrAddParams(strain).setAgeSusceptibility(nonSteppedAgeSusceptibility); + } + } + + // increase infectivity of alpha + virusStrainConfigGroup.getOrAddParams(VirusStrain.ALPHA).setInfectiousness(virusStrainConfigGroup.getOrAddParams(VirusStrain.ALPHA).getInfectiousness() * params.alphaTheta); + + virusStrainConfigGroup.getOrAddParams(VirusStrain.DELTA).setInfectiousness(virusStrainConfigGroup.getOrAddParams(VirusStrain.DELTA).getInfectiousness() * params.deltaTheta); + double ba1Inf = params.ba1Inf; // 2.0,2.1,2.2 + double ba2Inf = 1.7; + virusStrainConfigGroup.getOrAddParams(VirusStrain.OMICRON_BA1).setInfectiousness(virusStrainConfigGroup.getOrAddParams(VirusStrain.DELTA).getInfectiousness() * ba1Inf); + virusStrainConfigGroup.getOrAddParams(VirusStrain.OMICRON_BA2).setInfectiousness(virusStrainConfigGroup.getOrAddParams(VirusStrain.DELTA).getInfectiousness() * ba1Inf * ba2Inf); + + + + + + + //--------------------------------------- + // I M P O R T + //--------------------------------------- + + Map infPerDayAlpha = new HashMap<>(episimConfig.getInfections_pers_per_day().getOrDefault(VirusStrain.ALPHA, new TreeMap<>())); + + + // reconfig disease import of alpha + LocalDate startDateAlpha = LocalDate.parse(params.alphaDate); + + for (int i = 0; i < 7; i++) { + infPerDayAlpha.put(startDateAlpha.plusDays(i), 4); + } + + + infPerDayAlpha.put(startDateAlpha.plusDays(7), 1); + + episimConfig.setInfections_pers_per_day(VirusStrain.ALPHA, infPerDayAlpha); + + + configureFutureDiseaseImport(params, episimConfig); + + //--------------------------------------- + // R E S T R I C T I O N S + //--------------------------------------- + + FixedPolicy.ConfigBuilder builder = FixedPolicy.parse(episimConfig.getPolicy()); + + //school + String schoolUpdate = "yes"; + if(schoolUpdate.equals("yes")) { + // school closed completely until 21.2.2022 + builder.restrict(LocalDate.parse("2021-01-11"), 0.2, "educ_primary", "educ_kiga", "educ_secondary", "educ_tertiary", "educ_other"); + builder.restrict(LocalDate.parse("2021-02-21"), 0.5, "educ_primary"); + builder.restrict(LocalDate.parse("2021-03-15"), 0.5, "educ_kiga", "educ_secondary", "educ_tertiary", "educ_other"); + + } else if (schoolUpdate.equals("no")) { + + } else { + throw new RuntimeException("param value doesn't exist"); + } + + String schoolTest = "later"; + if (schoolTest.equals("later")) { + TestingConfigGroup testingConfigGroup = ConfigUtils.addOrGetModule(config, TestingConfigGroup.class); + TestingConfigGroup.TestingParams rapidTest = testingConfigGroup.getOrAddParams(TestType.RAPID_TEST); +// TestingConfigGroup.TestingParams pcrTest = testingConfigGroup.getOrAddParams(TestType.PCR); + Map> testingRateForActivitiesRapid = rapidTest.getTestingRateForActivities(); +// Map> testingRateForActivitiesPCR = pcrTest.getTestingRateForActivities(); + + + for (LocalDate date = LocalDate.parse("2021-03-19"); date.isBefore(LocalDate.parse("2021-04-25")); date = date.plusDays(1)) { + + testingRateForActivitiesRapid.get("educ_kiga").put(date, 0.); + testingRateForActivitiesRapid.get("educ_primary").put(date, 0.); + testingRateForActivitiesRapid.get("educ_secondary").put(date, 0.); + testingRateForActivitiesRapid.get("educ_tertiary").put(date, 0.); + testingRateForActivitiesRapid.get("educ_other").put(date, 0.); + + } + + testingRateForActivitiesRapid.get("educ_kiga").put(LocalDate.parse("2021-09-20"), 0.); + testingRateForActivitiesRapid.get("educ_primary").put(LocalDate.parse("2021-09-20"), 0.); + +// testingRateForActivitiesPCR.get("educ_primary").put(LocalDate.parse("2021-05-10"), 0.4); + testingRateForActivitiesRapid.get("educ_secondary").put(LocalDate.parse("2021-05-10"), 0.4); + testingRateForActivitiesRapid.get("educ_tertiary").put(LocalDate.parse("2021-05-10"), 0.4); + testingRateForActivitiesRapid.get("educ_other").put(LocalDate.parse("2021-05-10"), 0.4); + + } else if (schoolTest.equals("base")) { + + }else { + throw new RuntimeException("param value doesn't exist"); + } + + + // masks + //pt: masks + String maskType = "45to45"; + if (maskType.equals("45to45")) { + for (LocalDate date = LocalDate.parse("2020-04-21"); date.isBefore(LocalDate.parse("2021-05-01")); date = date.plusDays(1)) { + builder.restrict(date, Restriction.ofMask(Map.of(FaceMask.CLOTH, 0.45, FaceMask.SURGICAL, 0.45)), "pt", "errands", "shop_daily", "shop_other"); + + } + } else if (maskType.equals("base")) { + + } else { + throw new RuntimeException("param value doesn't exist"); + } + + episimConfig.setPolicy(builder.build()); + + + //--------------------------------------- + // M I S C + //--------------------------------------- + + // vaccination + VaccinationConfigGroup vaccinationConfig = ConfigUtils.addOrGetModule(config, VaccinationConfigGroup.class); + vaccinationConfig.setUseIgA(true); + vaccinationConfig.setTimePeriodIgA(730.); + + + //modify contact intensity + double workCi = 0.75; + episimConfig.getOrAddContainerParams("work").setContactIntensity(episimConfig.getOrAddContainerParams("work").getContactIntensity() * workCi); + episimConfig.getOrAddContainerParams("business").setContactIntensity(episimConfig.getOrAddContainerParams("business").getContactIntensity() * workCi); + + double leisureCi = 0.4; + episimConfig.getOrAddContainerParams("leisure").setContactIntensity(episimConfig.getOrAddContainerParams("leisure").getContactIntensity() * leisureCi); + episimConfig.getOrAddContainerParams("visit").setContactIntensity(episimConfig.getOrAddContainerParams("visit").getContactIntensity() * leisureCi); + + + double schoolCi = 0.75; + episimConfig.getOrAddContainerParams("educ_kiga").setContactIntensity(episimConfig.getOrAddContainerParams("educ_kiga").getContactIntensity() * schoolCi); + episimConfig.getOrAddContainerParams("educ_primary").setContactIntensity(episimConfig.getOrAddContainerParams("educ_primary").getContactIntensity() * schoolCi); + episimConfig.getOrAddContainerParams("educ_secondary").setContactIntensity(episimConfig.getOrAddContainerParams("educ_secondary").getContactIntensity() * schoolCi); + episimConfig.getOrAddContainerParams("educ_tertiary").setContactIntensity(episimConfig.getOrAddContainerParams("educ_tertiary").getContactIntensity() * schoolCi); + episimConfig.getOrAddContainerParams("educ_higher").setContactIntensity(episimConfig.getOrAddContainerParams("educ_higher").getContactIntensity() * schoolCi); + episimConfig.getOrAddContainerParams("educ_other").setContactIntensity(episimConfig.getOrAddContainerParams("educ_other").getContactIntensity() * schoolCi); + + + if (DEBUG_MODE) { + UtilsJR.produceDiseaseImportPlot(episimConfig.getInfections_pers_per_day()); + + } + + return config; + } + + private void configureFutureDiseaseImport(Params params, EpisimConfigGroup episimConfig) { + Map infPerDayBa1 = new HashMap<>(episimConfig.getInfections_pers_per_day().getOrDefault(VirusStrain.OMICRON_BA1, new TreeMap<>())); + Map infPerDayBa2 = new HashMap<>(episimConfig.getInfections_pers_per_day().getOrDefault(VirusStrain.OMICRON_BA2, new TreeMap<>())); + Map infPerDayBa5 = new HashMap<>(episimConfig.getInfections_pers_per_day().getOrDefault(VirusStrain.OMICRON_BA5, new TreeMap<>())); + Map infPerDayStrA = new HashMap<>(episimConfig.getInfections_pers_per_day().getOrDefault(VirusStrain.STRAIN_A, new TreeMap<>())); + + // add initial impulses for strains + //BA.1 + LocalDate ba1Date = LocalDate.parse(params.ba1Date); + for (int i = 0; i < 7; i++) { + infPerDayBa1.put(ba1Date.plusDays(i), 4); + } + infPerDayBa1.put(ba1Date.plusDays(7), 1); + + + //BA.2 + LocalDate ba2Date = LocalDate.parse("2021-12-18"); + for (int i = 0; i < 7; i++) { + infPerDayBa2.put(ba2Date.plusDays(i), 4); + } + infPerDayBa2.put(ba2Date.plusDays(7), 1); + + //BA.5 + LocalDate ba5Date = LocalDate.parse("2022-04-10"); + for (int i = 0; i < 7; i++) { + infPerDayBa5.put(ba5Date.plusDays(i), 4); + } + infPerDayBa5.put(ba5Date.plusDays(7), 1); + + //StrainA + + if (params.strAEsc != 0.) { + infPerDayStrA.put(LocalDate.parse("2020-01-01"), 0); + LocalDate strADate = LocalDate.parse(params.strADate); + for (int i = 0; i < 7; i++) { + infPerDayStrA.put(strADate.plusDays(i), 4); + } + infPerDayStrA.put(strADate.plusDays(7), 1); + } + + + // add projected disease import for vacation waves after initial disease import + int facBa2 = 4; + int facBa5 = 4; + int facStrA = 4; + + LocalDate dateBa2 = LocalDate.parse("2022-01-27"); // local min of disease import + LocalDate dateBa5 = LocalDate.parse("2022-05-01"); // after vaca import + LocalDate dateStrainA = LocalDate.parse("2022-11-18"); // after vaca import + + NavigableMap data = DataUtils.readDiseaseImport(SnzCologneProductionScenario.INPUT.resolve("cologneDiseaseImport_Projected.csv")); + LocalDate date = null; + for (Map.Entry entry : data.entrySet()) { + date = entry.getKey(); + double factor = 0.25 * 2352476. / 919936.; //25% sample, data is given for Cologne City so we have to scale it to the whole model +// + double cases = factor * entry.getValue(); + + if (date.isAfter(dateStrainA) && params.strAEsc != 0) { + infPerDayStrA.put(date, ((int) cases * facStrA) == 0 ? 1 : (int) (cases * facStrA)); + infPerDayBa5.put(date, 1); + infPerDayBa2.put(date, 1); + } else if (date.isAfter(dateBa5)) { + infPerDayBa5.put(date, ((int) cases * facBa5) == 0 ? 1 : (int) (cases * facBa5)); + infPerDayBa2.put(date, 1); + } else if (date.isAfter(dateBa2)) { + infPerDayBa2.put(date, ((int) cases * facBa2) == 0 ? 1 : (int) (cases * facBa2)); + } + + } + + if( params.strAEsc!=0.) { + infPerDayBa5.put(dateStrainA.plusDays(1), 1); + infPerDayStrA.put(date.plusDays(1), 1); + } else { + infPerDayBa5.put(date.plusDays(1), 1); + } + + // save disease import + episimConfig.setInfections_pers_per_day(VirusStrain.OMICRON_BA1, infPerDayBa1); + episimConfig.setInfections_pers_per_day(VirusStrain.OMICRON_BA2, infPerDayBa2); + episimConfig.setInfections_pers_per_day(VirusStrain.OMICRON_BA5, infPerDayBa5); + + if (params.strAEsc != 0.) { + episimConfig.setInfections_pers_per_day(VirusStrain.STRAIN_A, infPerDayStrA); + } + } + + public static final class Params { + + + // general + @GenerateSeeds(5) + public long seed; + + @Parameter({0.9}) + public double deltaTheta; + + @Parameter({1.7,1.9,2.1}) + public double ba1Inf; + + @StringParameter({"2021-11-20", "2021-11-27", "2021-12-04"}) + public String ba1Date; + + @Parameter({2.8, 2.9, 3.0}) + public double ba5Esc; + +// @Parameter({1.3}) +// @Parameter({0.0}) +// public double actCorrection; + + +// @Parameter({1.2}) +// @Parameter({1.0}) +// public double thFactor; + + // @Parameter({1.}) +// @Parameter({0.75}) +// public double schoolCi; + +// @Parameter({0.75}) +// @Parameter({1.}) +// public double workCi; + +// @Parameter({0.4}) +// @Parameter({ 1.}) +// public double leisureCi; + +// @StringParameter({"yes"}) +// public String schoolUpdate; + +// @StringParameter({"45to45"}) +// public String maskType; + +// @StringParameter({"later"}) +// public String schoolTest; + +// @StringParameter({"false"}) +// public String ageSusc; + +// @Parameter({0.90, .95, 1.}) +// public double deltaTheta; + + @StringParameter({"2021-01-15"}) + public String alphaDate; + + @Parameter({1.4}) + public double alphaTheta; + + @StringParameter({"2022-11-01"}) + public String strADate; + + @Parameter({0.0}) + public double strAEsc; + + + // General Restriction date +// @StringParameter({"2022-07-01","2022-12-01"}) +// @StringParameter({"2022-12-01"}) +// public String resDate; +// +// +// @StringParameter({"off", "age"}) +// @StringParameter({"off"}) +// String vacCamp; +// + // other restrictions + // schools & university // close: rf reduced // maskVent: ciCorrection reduced & surgical mask for most // normal: no changes made +// @StringParameter({"close", "maskVent", "normal"}) +// @StringParameter({ "normal"}) +// String edu; + + // shopping: mask +// @StringParameter({"true", "false"}) +// @StringParameter({"false"}) +// String maskShop; + + // pt: mask +// @StringParameter({"true", "false"}) +// @StringParameter({"false"}) +// String maskPt; + + // work: +// @Parameter({0.5, 1.0}) +// @Parameter({1.0}) +// double work; + + // leisure +// @Parameter({0.25, 0.5, 0.75, 1.0}) +// @Parameter({1.0}) +// double leis; + + + // vaccination campaign +// @StringParameter({"omicronUpdate"}) +// public String vacType; + +// @StringParameter({"2022-04-25"}) +// public String unResDate; + + // StrainA + +// @StringParameter({"true"}) +// public String sebaUp; + + // Antibody Model +// @Parameter({3.0}) +// double immuneSigma; + +// @Parameter({730.}) //120, +// public double igATime; + + // BA5 +// @StringParameter({"2022-04-10"}) +// public String ba5Date; +// +// @Parameter({0.9}) //,1.0,1.1,1.2,1.3}) +// double ba5Inf; +// +// @Parameter({3.}) +// public double ba5Esc; + + // @Parameter({0.0, 1.0}) +// public double strAInf; + + } + + + public static void main(String[] args) { + String[] args2 = { + RunParallel.OPTION_SETUP, CologneJR.class.getName(), + RunParallel.OPTION_PARAMS, Params.class.getName(), + RunParallel.OPTION_TASKS, Integer.toString(1), + RunParallel.OPTION_ITERATIONS, Integer.toString(1000), + RunParallel.OPTION_METADATA + }; + + RunParallel.main(args2); + } + + +} + diff --git a/src/main/java/org/matsim/run/batch/CologneSM.java b/src/main/java/org/matsim/run/batch/CologneSM.java index 75cd341e2..8a7c5ac87 100644 --- a/src/main/java/org/matsim/run/batch/CologneSM.java +++ b/src/main/java/org/matsim/run/batch/CologneSM.java @@ -124,7 +124,7 @@ else if (immunityType == VaccinationType.vector) { initialAntibodies.get(VirusStrain.OMICRON_BA1).put(VirusStrain.SARS_CoV_2, 0.01); initialAntibodies.get(VirusStrain.OMICRON_BA2).put(VirusStrain.SARS_CoV_2, 0.01); initialAntibodies.get(VirusStrain.STRAIN_A).put(VirusStrain.SARS_CoV_2, 0.01); - initialAntibodies.get(VaccinationType.omicronUpdate).put(VirusStrain.SARS_CoV_2, mRNAAlpha); + initialAntibodies.get(VaccinationType.ba1Update).put(VirusStrain.SARS_CoV_2, mRNAAlpha); //Alpha initialAntibodies.get(VaccinationType.mRNA).put(VirusStrain.ALPHA, mRNAAlpha); @@ -135,7 +135,7 @@ else if (immunityType == VaccinationType.vector) { initialAntibodies.get(VirusStrain.OMICRON_BA1).put(VirusStrain.ALPHA, 0.01); initialAntibodies.get(VirusStrain.OMICRON_BA2).put(VirusStrain.ALPHA, 0.01); initialAntibodies.get(VirusStrain.STRAIN_A).put(VirusStrain.ALPHA, 0.01); - initialAntibodies.get(VaccinationType.omicronUpdate).put(VirusStrain.ALPHA, mRNAAlpha); + initialAntibodies.get(VaccinationType.ba1Update).put(VirusStrain.ALPHA, mRNAAlpha); //DELTA double mRNADelta = 10.9; @@ -147,7 +147,7 @@ else if (immunityType == VaccinationType.vector) { initialAntibodies.get(VirusStrain.OMICRON_BA1).put(VirusStrain.DELTA, 0.2 / 6.4); initialAntibodies.get(VirusStrain.OMICRON_BA2).put(VirusStrain.DELTA, 0.2 / 6.4); initialAntibodies.get(VirusStrain.STRAIN_A).put(VirusStrain.DELTA, 0.2 / 6.4); - initialAntibodies.get(VaccinationType.omicronUpdate).put(VirusStrain.DELTA, mRNADelta); + initialAntibodies.get(VaccinationType.ba1Update).put(VirusStrain.DELTA, mRNADelta); //BA.1 double mRNABA1 = 1.9; @@ -159,7 +159,7 @@ else if (immunityType == VaccinationType.vector) { initialAntibodies.get(VirusStrain.OMICRON_BA1).put(VirusStrain.OMICRON_BA1, 64.0 / 300.); initialAntibodies.get(VirusStrain.OMICRON_BA2).put(VirusStrain.OMICRON_BA1, 64.0 / 300. / 1.4); initialAntibodies.get(VirusStrain.STRAIN_A).put(VirusStrain.OMICRON_BA1, 64.0 / 300. / 1.4); - initialAntibodies.get(VaccinationType.omicronUpdate).put(VirusStrain.OMICRON_BA1, mRNAAlpha); + initialAntibodies.get(VaccinationType.ba1Update).put(VirusStrain.OMICRON_BA1, mRNAAlpha); //BA.2 double mRNABA2 = mRNABA1; @@ -171,7 +171,7 @@ else if (immunityType == VaccinationType.vector) { initialAntibodies.get(VirusStrain.OMICRON_BA1).put(VirusStrain.OMICRON_BA2, 64.0 / 300. / 1.4); initialAntibodies.get(VirusStrain.OMICRON_BA2).put(VirusStrain.OMICRON_BA2, 64.0 / 300.); initialAntibodies.get(VirusStrain.STRAIN_A).put(VirusStrain.OMICRON_BA2, 64.0 / 300.); - initialAntibodies.get(VaccinationType.omicronUpdate).put(VirusStrain.OMICRON_BA2, mRNAAlpha); + initialAntibodies.get(VaccinationType.ba1Update).put(VirusStrain.OMICRON_BA2, mRNAAlpha); //StrainA double mRNAStrainA = mRNABA2 / mutEscOm; @@ -183,7 +183,7 @@ else if (immunityType == VaccinationType.vector) { initialAntibodies.get(VirusStrain.OMICRON_BA1).put(VirusStrain.STRAIN_A, 64.0 / 300. / 1.4 / mutEscOm); initialAntibodies.get(VirusStrain.OMICRON_BA2).put(VirusStrain.STRAIN_A, 64.0 / 300./ mutEscOm); initialAntibodies.get(VirusStrain.STRAIN_A).put(VirusStrain.STRAIN_A, 64.0 / 300.); - initialAntibodies.get(VaccinationType.omicronUpdate).put(VirusStrain.STRAIN_A, mRNAAlpha / mutEscOm); + initialAntibodies.get(VaccinationType.ba1Update).put(VirusStrain.STRAIN_A, mRNAAlpha / mutEscOm); for (VaccinationType immunityType : VaccinationType.values()) { antibodyRefreshFactors.put(immunityType, new EnumMap<>( VirusStrain.class ) ); @@ -195,7 +195,7 @@ else if (immunityType == VaccinationType.vector) { else if (immunityType == VaccinationType.vector) { antibodyRefreshFactors.get(immunityType).put(virusStrain, 5.0); } - else if (immunityType == VaccinationType.omicronUpdate) { + else if (immunityType == VaccinationType.ba1Update) { antibodyRefreshFactors.get(immunityType).put(virusStrain, 15.0); } else { diff --git a/src/main/java/org/matsim/run/batch/CologneScenarioHub.java b/src/main/java/org/matsim/run/batch/CologneScenarioHub.java new file mode 100644 index 000000000..d521f11ce --- /dev/null +++ b/src/main/java/org/matsim/run/batch/CologneScenarioHub.java @@ -0,0 +1,403 @@ +package org.matsim.run.batch; + +import com.google.inject.AbstractModule; +import com.google.inject.Module; +import com.google.inject.Singleton; +import com.google.inject.multibindings.Multibinder; +import com.google.inject.util.Modules; +import it.unimi.dsi.fastutil.ints.Int2DoubleAVLTreeMap; +import it.unimi.dsi.fastutil.ints.Int2DoubleMap; +import org.matsim.core.config.Config; +import org.matsim.core.config.ConfigUtils; +import org.matsim.episim.*; +import org.matsim.episim.analysis.*; +import org.matsim.episim.model.*; +import org.matsim.episim.model.vaccination.VaccinationModel; +import org.matsim.episim.model.vaccination.VaccinationStrategyBMBF0617; +import org.matsim.run.RunParallel; +import org.matsim.run.modules.SnzCologneProductionScenario; + +import javax.annotation.Nullable; +import java.time.LocalDate; +import java.util.*; + + +/** + * Batch for Bmbf runs + */ +public class CologneScenarioHub implements BatchRun { + + boolean DEBUG_MODE = true; + int runCount = 0; + + @Nullable + @Override + public Module getBindings(int id, @Nullable Params params) { + return Modules.override(getBindings(0.0, params)).with(new AbstractModule() { + @Override + protected void configure() { + + Multibinder set = Multibinder.newSetBinder(binder(), VaccinationModel.class); + + set.addBinding().to(VaccinationStrategyBMBF0617.class).in(Singleton.class); + + + double mutEscBa5 = 3.0; + + LocalDate start = LocalDate.of(2022, 9, 15); +// LocalDate start = LocalDate.of(2022, 12, 15); + VaccinationType vaccinationType = null; + + Int2DoubleMap compliance = new Int2DoubleAVLTreeMap(); + compliance.put(60, 0.0); + compliance.put(18, 0.0); + compliance.put(12, 0.0); + compliance.put(0, 0.0); + + if (params != null) { + vaccinationType = VaccinationType.valueOf(params.vacType); + + + if (params.vacCamp.equals("60plus")) { + compliance.put(60, 0.94/2); // 0.94 is boost rate July 16, 2022 + compliance.put(18, 0.); + compliance.put(12, 0.); + compliance.put(0, 0.); + } + // assumption: older age group 2boosted first, then younger, each age group + // will have rate of 50% 2boosted by end of campaign. + // motivation: if we give both age groups same rate, then the older people + // will not be boosted as much as younger people, which seems implausible... + else if (params.vacCamp.equals("18plus")) { + compliance.put(60, 0.94/2); // 0.94 is boost rate July 16, 2022 + compliance.put(18, 0.77/2); // 0.77 is boost rate July 16, 2022 + compliance.put(12, 0.); + compliance.put(0, 0.); + } + else if (params.vacCamp.equals("off")) { + + } else { + throw new RuntimeException("Not a valid option for vaccinationCampaignType"); + } + } + + bind(VaccinationStrategyBMBF0617.Config.class).toInstance(new VaccinationStrategyBMBF0617.Config(start, 91, vaccinationType, compliance)); + + //initial antibodies + Map> initialAntibodies = new HashMap<>(); + Map> antibodyRefreshFactors = new HashMap<>(); + configureAntibodies(initialAntibodies, antibodyRefreshFactors, mutEscBa5); + + AntibodyModel.Config antibodyConfig = new AntibodyModel.Config(initialAntibodies, antibodyRefreshFactors); + + double immuneSigma = 3.0; + if (params != null) { + antibodyConfig.setImmuneReponseSigma(immuneSigma); + } + + bind(AntibodyModel.Config.class).toInstance(antibodyConfig); + + } + + private void configureAntibodies(Map> initialAntibodies, + Map> antibodyRefreshFactors, + double mutEscBa5) { + for (VaccinationType immunityType : VaccinationType.values()) { + initialAntibodies.put(immunityType, new EnumMap<>( VirusStrain.class ) ); + for (VirusStrain virusStrain : VirusStrain.values()) { + + if (immunityType == VaccinationType.mRNA) { + initialAntibodies.get(immunityType).put(virusStrain, 29.2); //10.0 + } + else if (immunityType == VaccinationType.vector) { + initialAntibodies.get(immunityType).put(virusStrain, 6.8); //2.5 + } + else { + initialAntibodies.get(immunityType).put(virusStrain, 5.0); + } + } + } + + for (VirusStrain immunityType : VirusStrain.values()) { + initialAntibodies.put(immunityType, new EnumMap<>( VirusStrain.class ) ); + for (VirusStrain virusStrain : VirusStrain.values()) { + initialAntibodies.get(immunityType).put(virusStrain, 5.0); + } + } + + //mRNAAlpha, mRNADelta, mRNABA1 comes from Sydney's calibration. + //The other values come from Rössler et al. + + //Wildtype + double mRNAAlpha = 29.2; + initialAntibodies.get(VaccinationType.mRNA).put(VirusStrain.SARS_CoV_2, mRNAAlpha); + initialAntibodies.get(VaccinationType.vector).put(VirusStrain.SARS_CoV_2, mRNAAlpha * 210. / 700.); + initialAntibodies.get(VirusStrain.SARS_CoV_2).put(VirusStrain.SARS_CoV_2, mRNAAlpha * 300. / 700.); + initialAntibodies.get(VirusStrain.ALPHA).put(VirusStrain.SARS_CoV_2, mRNAAlpha * 300. / 700.); + initialAntibodies.get(VirusStrain.DELTA).put(VirusStrain.SARS_CoV_2, mRNAAlpha * 210. / 700.); + initialAntibodies.get(VirusStrain.OMICRON_BA1).put(VirusStrain.SARS_CoV_2, 0.01); + initialAntibodies.get(VirusStrain.OMICRON_BA2).put(VirusStrain.SARS_CoV_2, 0.01); + initialAntibodies.get(VirusStrain.OMICRON_BA5).put(VirusStrain.SARS_CoV_2, 0.01); + initialAntibodies.get(VirusStrain.STRAIN_A).put(VirusStrain.SARS_CoV_2, 0.01); + initialAntibodies.get(VaccinationType.ba1Update).put(VirusStrain.SARS_CoV_2, mRNAAlpha); + + //Alpha + initialAntibodies.get(VaccinationType.mRNA).put(VirusStrain.ALPHA, mRNAAlpha); + initialAntibodies.get(VaccinationType.vector).put(VirusStrain.ALPHA, mRNAAlpha * 210. / 700.); + initialAntibodies.get(VirusStrain.SARS_CoV_2).put(VirusStrain.ALPHA, mRNAAlpha * 300. / 700.); + initialAntibodies.get(VirusStrain.ALPHA).put(VirusStrain.ALPHA, mRNAAlpha * 300. / 700.); + initialAntibodies.get(VirusStrain.DELTA).put(VirusStrain.ALPHA, mRNAAlpha * 210. / 700.); + initialAntibodies.get(VirusStrain.OMICRON_BA1).put(VirusStrain.ALPHA, 0.01); + initialAntibodies.get(VirusStrain.OMICRON_BA2).put(VirusStrain.ALPHA, 0.01); + initialAntibodies.get(VirusStrain.OMICRON_BA5).put(VirusStrain.ALPHA, 0.01); + initialAntibodies.get(VirusStrain.STRAIN_A).put(VirusStrain.ALPHA, 0.01); + initialAntibodies.get(VaccinationType.ba1Update).put(VirusStrain.ALPHA, mRNAAlpha); + + //DELTA + double mRNADelta = 10.9; + initialAntibodies.get(VaccinationType.mRNA).put(VirusStrain.DELTA, mRNADelta); + initialAntibodies.get(VaccinationType.vector).put(VirusStrain.DELTA, mRNADelta * 150./300.); + initialAntibodies.get(VirusStrain.SARS_CoV_2).put(VirusStrain.DELTA, mRNADelta * 64./300.); + initialAntibodies.get(VirusStrain.ALPHA).put(VirusStrain.DELTA, mRNADelta * 64./300.); + initialAntibodies.get(VirusStrain.DELTA).put(VirusStrain.DELTA, mRNADelta * 450./300.); + initialAntibodies.get(VirusStrain.OMICRON_BA1).put(VirusStrain.DELTA, 0.2 / 6.4); + initialAntibodies.get(VirusStrain.OMICRON_BA2).put(VirusStrain.DELTA, 0.2 / 6.4); + initialAntibodies.get(VirusStrain.OMICRON_BA5).put(VirusStrain.DELTA, 0.2 / 6.4); + initialAntibodies.get(VirusStrain.STRAIN_A).put(VirusStrain.DELTA, 0.2 / 6.4); + initialAntibodies.get(VaccinationType.ba1Update).put(VirusStrain.DELTA, mRNADelta); + + //BA.1 + double mRNABA1 = 1.9; + initialAntibodies.get(VaccinationType.mRNA).put(VirusStrain.OMICRON_BA1, mRNABA1); + initialAntibodies.get(VaccinationType.vector).put(VirusStrain.OMICRON_BA1, mRNABA1 * 4./20.); //??? + initialAntibodies.get(VirusStrain.SARS_CoV_2).put(VirusStrain.OMICRON_BA1, mRNABA1 * 6./20.); + initialAntibodies.get(VirusStrain.ALPHA).put(VirusStrain.OMICRON_BA1, mRNABA1 * 6./20.); + initialAntibodies.get(VirusStrain.DELTA).put(VirusStrain.OMICRON_BA1, mRNABA1 * 8./20.); + initialAntibodies.get(VirusStrain.OMICRON_BA1).put(VirusStrain.OMICRON_BA1, 64.0 / 300.); + initialAntibodies.get(VirusStrain.OMICRON_BA2).put(VirusStrain.OMICRON_BA1, 64.0 / 300. / 1.4); + initialAntibodies.get(VirusStrain.OMICRON_BA5).put(VirusStrain.OMICRON_BA1, 64.0 / 300. / 1.4); //todo: is 1.4 + initialAntibodies.get(VirusStrain.STRAIN_A).put(VirusStrain.OMICRON_BA1, 64.0 / 300. / 1.4); + initialAntibodies.get(VaccinationType.ba1Update).put(VirusStrain.OMICRON_BA1, mRNADelta / mutEscBa5); + + //BA.2 + double mRNABA2 = mRNABA1; + initialAntibodies.get(VaccinationType.mRNA).put(VirusStrain.OMICRON_BA2, mRNABA2); + initialAntibodies.get(VaccinationType.vector).put(VirusStrain.OMICRON_BA2, mRNABA2 * 4./20.); + initialAntibodies.get(VirusStrain.SARS_CoV_2).put(VirusStrain.OMICRON_BA2, mRNABA2 * 6./20.); + initialAntibodies.get(VirusStrain.ALPHA).put(VirusStrain.OMICRON_BA2, mRNABA2 * 6./20.); + initialAntibodies.get(VirusStrain.DELTA).put(VirusStrain.OMICRON_BA2, mRNABA2 * 8./20.); + initialAntibodies.get(VirusStrain.OMICRON_BA1).put(VirusStrain.OMICRON_BA2, 64.0 / 300. / 1.4); + initialAntibodies.get(VirusStrain.OMICRON_BA2).put(VirusStrain.OMICRON_BA2, 64.0 / 300.); + initialAntibodies.get(VirusStrain.OMICRON_BA5).put(VirusStrain.OMICRON_BA2, 64.0 / 300. / 1.4); + initialAntibodies.get(VirusStrain.STRAIN_A).put(VirusStrain.OMICRON_BA2, 64.0 / 300.); + initialAntibodies.get(VaccinationType.ba1Update).put(VirusStrain.OMICRON_BA2, mRNADelta / mutEscBa5); + + + //BA.5 + double mRNABa5 = mRNABA2 / mutEscBa5; + initialAntibodies.get(VaccinationType.mRNA).put(VirusStrain.OMICRON_BA5, mRNABa5); + initialAntibodies.get(VaccinationType.vector).put(VirusStrain.OMICRON_BA5, mRNABa5 * 4./20.); + initialAntibodies.get(VirusStrain.SARS_CoV_2).put(VirusStrain.OMICRON_BA5, mRNABa5 * 6./20.); + initialAntibodies.get(VirusStrain.ALPHA).put(VirusStrain.OMICRON_BA5, mRNABa5 * 6./20.); + initialAntibodies.get(VirusStrain.DELTA).put(VirusStrain.OMICRON_BA5, mRNABa5 * 8./20.); + initialAntibodies.get(VirusStrain.OMICRON_BA1).put(VirusStrain.OMICRON_BA5, 64.0 / 300. / 1.4 / mutEscBa5);// todo: do we need 1.4? + initialAntibodies.get(VirusStrain.OMICRON_BA2).put(VirusStrain.OMICRON_BA5, 64.0 / 300./ mutEscBa5); + initialAntibodies.get(VirusStrain.OMICRON_BA5).put(VirusStrain.OMICRON_BA5, 64.0 / 300.); + initialAntibodies.get(VirusStrain.STRAIN_A).put(VirusStrain.OMICRON_BA5, 64.0 / 300./ mutEscBa5); //todo ??? + initialAntibodies.get(VaccinationType.ba1Update).put(VirusStrain.OMICRON_BA5, mRNADelta); // mRNAAlpha / mutEscBa5 + + + for (VaccinationType immunityType : VaccinationType.values()) { + antibodyRefreshFactors.put(immunityType, new EnumMap<>( VirusStrain.class ) ); + for (VirusStrain virusStrain : VirusStrain.values()) { + + if (immunityType == VaccinationType.mRNA) { + antibodyRefreshFactors.get(immunityType).put(virusStrain, 15.0); + } + else if (immunityType == VaccinationType.vector) { + antibodyRefreshFactors.get(immunityType).put(virusStrain, 5.0); + } + else if (immunityType == VaccinationType.ba1Update) { + antibodyRefreshFactors.get(immunityType).put(virusStrain, 15.0); + } + else { + antibodyRefreshFactors.get(immunityType).put(virusStrain, Double.NaN); + } + + } + } + + for (VirusStrain immunityType : VirusStrain.values()) { + antibodyRefreshFactors.put(immunityType, new EnumMap<>( VirusStrain.class ) ); + for (VirusStrain virusStrain : VirusStrain.values()) { + antibodyRefreshFactors.get(immunityType).put(virusStrain, 15.0); + } + } + + } + }); + + } + + private SnzCologneProductionScenario getBindings(double pHousehold, Params params) { + return new SnzCologneProductionScenario.Builder() + .setLeisureCorrection(0.0) + .setCarnivalModel(SnzCologneProductionScenario.CarnivalModel.yes) + .setSebastianUpdate(true) + .setScaleForActivityLevels(1.3) + .setSuscHouseholds_pct(pHousehold) + .setActivityHandling(EpisimConfigGroup.ActivityHandling.startOfDay) +// .setTestingModel(params != null ? FlexibleTestingModel.class : DefaultTestingModel.class) + .setInfectionModel(InfectionModelWithAntibodies.class) + .build(); + } + + @Override + public Metadata getMetadata() { + return Metadata.of("cologne", "calibration"); + } + + @Override + public Collection postProcessing() { + return List.of( + new VaccinationEffectiveness().withArgs(), + new RValuesFromEvents().withArgs(), + new VaccinationEffectivenessFromPotentialInfections().withArgs("--remove-infected"), + new HospitalNumbersFromEvents().withArgs(), + new SecondaryAttackRateFromEvents().withArgs() + ); + } + + @Override + public Config prepareConfig(int id, Params params) { + + if (DEBUG_MODE) { + if (runCount == 0){ //&& params.strAEsc != 0.0 && params.ba5Inf == 0. && params.eduTest.equals("true")) { + runCount++; + } else { + return null; + } + } + + SnzCologneProductionScenario module = getBindings(0.0, params); + + Config config = module.config(); + + + config.global().setRandomSeed(params.seed); + + EpisimConfigGroup episimConfig = ConfigUtils.addOrGetModule(config, EpisimConfigGroup.class); + + //mutations + VirusStrainConfigGroup virusStrainConfigGroup = ConfigUtils.addOrGetModule(config, VirusStrainConfigGroup.class); + + //configure new strains + //BA5 + double ba5Inf = 0.9; + double oHos = virusStrainConfigGroup.getParams(VirusStrain.OMICRON_BA2).getFactorSeriouslySick(); + virusStrainConfigGroup.getOrAddParams(VirusStrain.OMICRON_BA5).setInfectiousness(virusStrainConfigGroup.getParams(VirusStrain.OMICRON_BA2).getInfectiousness() * ba5Inf); + virusStrainConfigGroup.getOrAddParams(VirusStrain.OMICRON_BA5).setFactorSeriouslySick(oHos); + virusStrainConfigGroup.getOrAddParams(VirusStrain.OMICRON_BA5).setFactorSeriouslySickVaccinated(oHos); + virusStrainConfigGroup.getOrAddParams(VirusStrain.OMICRON_BA5).setFactorCritical(oHos); + + + //Configure Disease Import + + configureFutureDiseaseImport(params, episimConfig); + + + //vaccinations + VaccinationConfigGroup vaccinationConfig = ConfigUtils.addOrGetModule(config, VaccinationConfigGroup.class); + vaccinationConfig.setUseIgA(true); + vaccinationConfig.setTimePeriodIgA(730.); + + + return config; + } + + private void configureFutureDiseaseImport(Params params, EpisimConfigGroup episimConfig) { + Map infPerDayBa2 = new HashMap<>(episimConfig.getInfections_pers_per_day().getOrDefault(VirusStrain.OMICRON_BA2, new TreeMap<>())); + Map infPerDayBa5 = new HashMap<>(episimConfig.getInfections_pers_per_day().getOrDefault(VirusStrain.OMICRON_BA5, new TreeMap<>())); + // add initial impulses for strains + //BA.2 + LocalDate ba2Date = LocalDate.parse("2021-12-18"); + for (int i = 0; i < 7; i++) { + infPerDayBa2.put(ba2Date.plusDays(i), 4); + } + infPerDayBa2.put(ba2Date.plusDays(7), 1); + + //BA.5 + LocalDate ba5Date = LocalDate.parse("2022-04-10"); + for (int i = 0; i < 7; i++) { + infPerDayBa5.put(ba5Date.plusDays(i), 4); + } + infPerDayBa5.put(ba5Date.plusDays(7), 1); + + + // add projected disease import for vacation waves after initial disease import + int facBa2 = 4; + int facBa5 = 4; + + LocalDate dateBa2 = LocalDate.parse("2022-01-27"); // local min of disease import + LocalDate dateBa5 = LocalDate.parse("2022-05-01"); // after vaca import + + NavigableMap data = DataUtils.readDiseaseImport(SnzCologneProductionScenario.INPUT.resolve("cologneDiseaseImport_Projected.csv")); + LocalDate date = null; + for (Map.Entry entry : data.entrySet()) { + date = entry.getKey(); + double factor = 0.25 * 2352476. / 919936.; //25% sample, data is given for Cologne City so we have to scale it to the whole model +// + double cases = factor * entry.getValue(); + + if (date.isAfter(dateBa5)) { + infPerDayBa5.put(date, ((int) cases * facBa5) == 0 ? 1 : (int) (cases * facBa5)); + infPerDayBa2.put(date, 1); + } else if (date.isAfter(dateBa2)) { + infPerDayBa2.put(date, ((int) cases * facBa2) == 0 ? 1 : (int) (cases * facBa2)); + } + + } + + infPerDayBa5.put(date.plusDays(1), 1); + + // save disease import + episimConfig.setInfections_pers_per_day(VirusStrain.OMICRON_BA2, infPerDayBa2); + episimConfig.setInfections_pers_per_day(VirusStrain.OMICRON_BA5, infPerDayBa5); + + } + + + public static final class Params { + + // general + @GenerateSeeds(10) + public long seed; + + + // vaccination campaign + @StringParameter({"off", "60plus", "18plus"}) + String vacCamp; + + @StringParameter({"mRNA","omicronUpdate"}) + public String vacType; + + + + } + + + public static void main(String[] args) { + String[] args2 = { + RunParallel.OPTION_SETUP, CologneScenarioHub.class.getName(), + RunParallel.OPTION_PARAMS, Params.class.getName(), + RunParallel.OPTION_TASKS, Integer.toString(1), + RunParallel.OPTION_ITERATIONS, Integer.toString(70), + RunParallel.OPTION_METADATA + }; + + RunParallel.main(args2); + } + + +} + diff --git a/src/main/java/org/matsim/run/batch/CologneVaryHhSusceptibility.java b/src/main/java/org/matsim/run/batch/CologneVaryHhSusceptibility.java new file mode 100644 index 000000000..8a5dc83bb --- /dev/null +++ b/src/main/java/org/matsim/run/batch/CologneVaryHhSusceptibility.java @@ -0,0 +1,594 @@ +package org.matsim.run.batch; + +import com.google.inject.AbstractModule; +import com.google.inject.Module; +import com.google.inject.Singleton; +import com.google.inject.multibindings.Multibinder; +import com.google.inject.util.Modules; +import it.unimi.dsi.fastutil.ints.Int2DoubleAVLTreeMap; +import it.unimi.dsi.fastutil.ints.Int2DoubleMap; +import jdk.jshell.execution.Util; +import org.matsim.core.config.Config; +import org.matsim.core.config.ConfigUtils; +import org.matsim.episim.*; +import org.matsim.episim.analysis.*; +import org.matsim.episim.model.*; +import org.matsim.episim.model.listener.HouseholdSusceptibility; +import org.matsim.episim.model.testing.TestType; +import org.matsim.episim.model.vaccination.VaccinationModel; +import org.matsim.episim.model.vaccination.VaccinationStrategyBMBF0617; +import org.matsim.episim.policy.FixedPolicy; +import org.matsim.episim.policy.Restriction; +import org.matsim.run.RunParallel; +import org.matsim.run.modules.SnzCologneProductionScenario; + +import javax.annotation.Nullable; +import java.time.LocalDate; +import java.util.*; + + +/** + * Batch for Bmbf runs + */ +public class CologneVaryHhSusceptibility implements BatchRun { + + boolean DEBUG_MODE = false; + int runCount = 0; + + @Nullable + @Override + public Module getBindings(int id, @Nullable Params params) { + return Modules.override(getBindings(0.0, params)).with(new AbstractModule() { + @Override + protected void configure() { + + Multibinder set = Multibinder.newSetBinder(binder(), VaccinationModel.class); + + set.addBinding().to(VaccinationStrategyBMBF0617.class).in(Singleton.class); + + double mutEscDelta = 29.2 / 10.9; + double mutEscBa1 = 10.9 / 1.9; + double mutEscBa5 = 2.9; + + double mutEscStrainA = 0.; + double mutEscStrainB = 0.; + + LocalDate start = null; + VaccinationType vaccinationType = VaccinationType.mRNA; + + Int2DoubleMap compliance = new Int2DoubleAVLTreeMap(); + compliance.put(60, 0.0); + compliance.put(18, 0.0); + compliance.put(12, 0.0); + compliance.put(0, 0.0); + + String vacCamp = "off"; + + if (params != null) { +// mutEscBa5 = params.ba5Esc; + + if (!params.vacType.equals("off")) { + vacCamp = "age"; + vaccinationType = VaccinationType.valueOf(params.vacType); + } + if (!params.StrainA.equals("off")) { + mutEscStrainA = Double.parseDouble(params.StrainA); + } + if (!params.StrainB.equals("off")) { + mutEscStrainB = Double.parseDouble(params.StrainB); + } + + start = LocalDate.parse(params.resDate); + + + + if (vacCamp.equals("age")) { + compliance.put(60, 0.85); // 60+ + compliance.put(18, 0.55); // 18-59 + compliance.put(12, 0.20); // 12-17 + compliance.put(0, 0.0); // 0 - 11 + } + else if (vacCamp.equals("eu")) { + compliance.put(60, 0.40); // half of 80% (which reflects the current percentage of people in Dland who are boostered) + compliance.put(18, 0.); + compliance.put(12, 0.); + compliance.put(0, 0.); + } + else if (vacCamp.equals("off")) { + + } else { + throw new RuntimeException("Not a valid option for vaccinationCampaignType"); + } + } +// + bind(VaccinationStrategyBMBF0617.Config.class).toInstance(new VaccinationStrategyBMBF0617.Config(start, 30, vaccinationType, compliance)); + + //initial antibodies + Map> initialAntibodies = new HashMap<>(); + Map> antibodyRefreshFactors = new HashMap<>(); + configureAntibodies(initialAntibodies, antibodyRefreshFactors, mutEscDelta, mutEscBa1, mutEscBa5, mutEscStrainA, mutEscStrainB); + + AntibodyModel.Config antibodyConfig = new AntibodyModel.Config(initialAntibodies, antibodyRefreshFactors); + + double immuneSigma = 3.0; + if (params != null) { + antibodyConfig.setImmuneReponseSigma(immuneSigma); + } + + bind(AntibodyModel.Config.class).toInstance(antibodyConfig); + + if (params == null) return; + +// double pHousehold = 1.0; + // designates a 35% of households as super safe; the susceptibility of that subpopulation is reduced to 1% wrt to general population. + bind(HouseholdSusceptibility.Config.class).toInstance( + HouseholdSusceptibility.newConfig() + .withSusceptibleHouseholds(0.35, 0.01) +// .withNonVaccinableHouseholds(params.nonVaccinableHh) +// .withShape(SnzCologneProductionScenario.INPUT.resolve("CologneDistricts.zip")) +// .withFeature("STT_NAME", vingst, altstadtNord, bickendorf, weiden) + ); + + } + + private void configureAntibodies(Map> initialAntibodies, + Map> antibodyRefreshFactors, + double mutEscDelta, double mutEscBa1, double mutEscBa5, double mutEscStrainA, double mutEscStrainB) { + for (VaccinationType immunityType : VaccinationType.values()) { + initialAntibodies.put(immunityType, new EnumMap<>( VirusStrain.class ) ); + for (VirusStrain virusStrain : VirusStrain.values()) { + + if (immunityType == VaccinationType.mRNA) { + initialAntibodies.get(immunityType).put(virusStrain, 29.2); //10.0 + } + else if (immunityType == VaccinationType.vector) { + initialAntibodies.get(immunityType).put(virusStrain, 6.8); //2.5 + } + else { + initialAntibodies.get(immunityType).put(virusStrain, 5.0); + } + } + } + + for (VirusStrain immunityType : VirusStrain.values()) { + initialAntibodies.put(immunityType, new EnumMap<>( VirusStrain.class ) ); + for (VirusStrain virusStrain : VirusStrain.values()) { + initialAntibodies.get(immunityType).put(virusStrain, 5.0); + } + } + + //mRNAAlpha, mRNADelta, mRNABA1 comes from Sydney's calibration. + //The other values come from Rössler et al. + + //Wildtype + double mRNAAlpha = 29.2; + initialAntibodies.get(VaccinationType.mRNA).put(VirusStrain.SARS_CoV_2, mRNAAlpha); + initialAntibodies.get(VaccinationType.vector).put(VirusStrain.SARS_CoV_2, mRNAAlpha * 210. / 700.); + initialAntibodies.get(VirusStrain.SARS_CoV_2).put(VirusStrain.SARS_CoV_2, mRNAAlpha * 300. / 700.); + initialAntibodies.get(VirusStrain.ALPHA).put(VirusStrain.SARS_CoV_2, mRNAAlpha * 300. / 700.); + initialAntibodies.get(VirusStrain.DELTA).put(VirusStrain.SARS_CoV_2, mRNAAlpha * 210. / 700.); + initialAntibodies.get(VirusStrain.OMICRON_BA1).put(VirusStrain.SARS_CoV_2, 0.01); + initialAntibodies.get(VirusStrain.OMICRON_BA2).put(VirusStrain.SARS_CoV_2, 0.01); + initialAntibodies.get(VirusStrain.OMICRON_BA5).put(VirusStrain.SARS_CoV_2, 0.01); + initialAntibodies.get(VirusStrain.STRAIN_A).put(VirusStrain.SARS_CoV_2, 0.01); + initialAntibodies.get(VirusStrain.STRAIN_B).put(VirusStrain.SARS_CoV_2, 0.01); + initialAntibodies.get(VaccinationType.ba1Update).put(VirusStrain.SARS_CoV_2, mRNAAlpha / mutEscDelta / mutEscBa1); + initialAntibodies.get(VaccinationType.ba5Update).put(VirusStrain.SARS_CoV_2, mRNAAlpha / mutEscDelta / mutEscBa1 / mutEscBa5); + + //Alpha + initialAntibodies.get(VaccinationType.mRNA).put(VirusStrain.ALPHA, mRNAAlpha); + initialAntibodies.get(VaccinationType.vector).put(VirusStrain.ALPHA, mRNAAlpha * 210. / 700.); + initialAntibodies.get(VirusStrain.SARS_CoV_2).put(VirusStrain.ALPHA, mRNAAlpha * 300. / 700.); + initialAntibodies.get(VirusStrain.ALPHA).put(VirusStrain.ALPHA, mRNAAlpha * 300. / 700.); + initialAntibodies.get(VirusStrain.DELTA).put(VirusStrain.ALPHA, mRNAAlpha * 210. / 700.); + initialAntibodies.get(VirusStrain.OMICRON_BA1).put(VirusStrain.ALPHA, 0.01); + initialAntibodies.get(VirusStrain.OMICRON_BA2).put(VirusStrain.ALPHA, 0.01); + initialAntibodies.get(VirusStrain.OMICRON_BA5).put(VirusStrain.ALPHA, 0.01); + initialAntibodies.get(VirusStrain.STRAIN_A).put(VirusStrain.ALPHA, 0.01); + initialAntibodies.get(VirusStrain.STRAIN_B).put(VirusStrain.ALPHA, 0.01); + initialAntibodies.get(VaccinationType.ba1Update).put(VirusStrain.ALPHA, mRNAAlpha / mutEscDelta / mutEscBa1); + initialAntibodies.get(VaccinationType.ba5Update).put(VirusStrain.ALPHA, mRNAAlpha / mutEscDelta / mutEscBa1 / mutEscBa5); + + //DELTA + double mRNADelta = mRNAAlpha / mutEscDelta; + initialAntibodies.get(VaccinationType.mRNA).put(VirusStrain.DELTA, mRNADelta); + initialAntibodies.get(VaccinationType.vector).put(VirusStrain.DELTA, mRNADelta * 150./300.); + initialAntibodies.get(VirusStrain.SARS_CoV_2).put(VirusStrain.DELTA, mRNADelta * 64./300.); + initialAntibodies.get(VirusStrain.ALPHA).put(VirusStrain.DELTA, mRNADelta * 64./300.); + initialAntibodies.get(VirusStrain.DELTA).put(VirusStrain.DELTA, mRNADelta * 450./300.); + initialAntibodies.get(VirusStrain.OMICRON_BA1).put(VirusStrain.DELTA, 0.01); + initialAntibodies.get(VirusStrain.OMICRON_BA2).put(VirusStrain.DELTA, 0.01); + initialAntibodies.get(VirusStrain.OMICRON_BA5).put(VirusStrain.DELTA, 0.01); + initialAntibodies.get(VirusStrain.STRAIN_A).put(VirusStrain.DELTA, 0.01); + initialAntibodies.get(VirusStrain.STRAIN_B).put(VirusStrain.DELTA, 0.01); + initialAntibodies.get(VaccinationType.ba1Update).put(VirusStrain.DELTA, mRNADelta / mutEscBa1); + initialAntibodies.get(VaccinationType.ba5Update).put(VirusStrain.DELTA, mRNADelta / mutEscBa1 / mutEscBa5); + + //BA.1 + double mRNABA1 = mRNADelta / mutEscBa1; + initialAntibodies.get(VaccinationType.mRNA).put(VirusStrain.OMICRON_BA1, mRNABA1); + initialAntibodies.get(VaccinationType.vector).put(VirusStrain.OMICRON_BA1, mRNABA1 * 4./20.); //??? + initialAntibodies.get(VirusStrain.SARS_CoV_2).put(VirusStrain.OMICRON_BA1, mRNABA1 * 6./20.); + initialAntibodies.get(VirusStrain.ALPHA).put(VirusStrain.OMICRON_BA1, mRNABA1 * 6./20.); + initialAntibodies.get(VirusStrain.DELTA).put(VirusStrain.OMICRON_BA1, mRNABA1 * 8./20.); + initialAntibodies.get(VirusStrain.OMICRON_BA1).put(VirusStrain.OMICRON_BA1, 64.0 / 300.); + initialAntibodies.get(VirusStrain.OMICRON_BA2).put(VirusStrain.OMICRON_BA1, 64.0 / 300. / 1.4); + initialAntibodies.get(VirusStrain.OMICRON_BA5).put(VirusStrain.OMICRON_BA1, 64.0 / 300. / mutEscBa5); //todo: is 1.4 + initialAntibodies.get(VirusStrain.STRAIN_A).put(VirusStrain.OMICRON_BA1, 64.0 / 300. / mutEscBa5 / mutEscStrainA); + initialAntibodies.get(VirusStrain.STRAIN_B).put(VirusStrain.OMICRON_BA1, 64.0 / 300. / mutEscStrainB); + initialAntibodies.get(VaccinationType.ba1Update).put(VirusStrain.OMICRON_BA1, mRNAAlpha); + initialAntibodies.get(VaccinationType.ba5Update).put(VirusStrain.OMICRON_BA1, mRNAAlpha / mutEscBa5); + + //BA.2 + double mRNABA2 = mRNABA1; + initialAntibodies.get(VaccinationType.mRNA).put(VirusStrain.OMICRON_BA2, mRNABA2); + initialAntibodies.get(VaccinationType.vector).put(VirusStrain.OMICRON_BA2, mRNABA2 * 4./20.); + initialAntibodies.get(VirusStrain.SARS_CoV_2).put(VirusStrain.OMICRON_BA2, mRNABA2 * 6./20.); + initialAntibodies.get(VirusStrain.ALPHA).put(VirusStrain.OMICRON_BA2, mRNABA2 * 6./20.); + initialAntibodies.get(VirusStrain.DELTA).put(VirusStrain.OMICRON_BA2, mRNABA2 * 8./20.); + initialAntibodies.get(VirusStrain.OMICRON_BA1).put(VirusStrain.OMICRON_BA2, 64.0 / 300. / 1.4); + initialAntibodies.get(VirusStrain.OMICRON_BA2).put(VirusStrain.OMICRON_BA2, 64.0 / 300.); + initialAntibodies.get(VirusStrain.OMICRON_BA5).put(VirusStrain.OMICRON_BA2, 64.0 / 300. / mutEscBa5); + initialAntibodies.get(VirusStrain.STRAIN_A).put(VirusStrain.OMICRON_BA2, 64.0 / 300. / mutEscBa5 / mutEscStrainA); + initialAntibodies.get(VirusStrain.STRAIN_B).put(VirusStrain.OMICRON_BA2, 64.0 / 300. / mutEscStrainB); + initialAntibodies.get(VaccinationType.ba1Update).put(VirusStrain.OMICRON_BA2, mRNAAlpha); + initialAntibodies.get(VaccinationType.ba5Update).put(VirusStrain.OMICRON_BA2, mRNAAlpha / mutEscBa5); + + + //BA.5 + double mRNABa5 = mRNABA2 / mutEscBa5; + initialAntibodies.get(VaccinationType.mRNA).put(VirusStrain.OMICRON_BA5, mRNABa5); + initialAntibodies.get(VaccinationType.vector).put(VirusStrain.OMICRON_BA5, mRNABa5 * 4./20.); + initialAntibodies.get(VirusStrain.SARS_CoV_2).put(VirusStrain.OMICRON_BA5, mRNABa5 * 6./20.); + initialAntibodies.get(VirusStrain.ALPHA).put(VirusStrain.OMICRON_BA5, mRNABa5 * 6./20.); + initialAntibodies.get(VirusStrain.DELTA).put(VirusStrain.OMICRON_BA5, mRNABa5 * 8./20.); + initialAntibodies.get(VirusStrain.OMICRON_BA1).put(VirusStrain.OMICRON_BA5, 64.0 / 300. / mutEscBa5);// todo: do we need 1.4? + initialAntibodies.get(VirusStrain.OMICRON_BA2).put(VirusStrain.OMICRON_BA5, 64.0 / 300./ mutEscBa5); + initialAntibodies.get(VirusStrain.OMICRON_BA5).put(VirusStrain.OMICRON_BA5, 64.0 / 300.); + initialAntibodies.get(VirusStrain.STRAIN_A).put(VirusStrain.OMICRON_BA5, 64.0 / 300./ mutEscStrainA); //todo ??? + initialAntibodies.get(VirusStrain.STRAIN_B).put(VirusStrain.OMICRON_BA5, 64.0 / 300. / mutEscBa5 / mutEscStrainB); + initialAntibodies.get(VaccinationType.ba1Update).put(VirusStrain.OMICRON_BA5, mRNAAlpha / mutEscBa5); + initialAntibodies.get(VaccinationType.ba5Update).put(VirusStrain.OMICRON_BA5, mRNAAlpha); + + //StrainA + double mRNAStrainA = mRNABa5 / mutEscStrainA; + initialAntibodies.get(VaccinationType.mRNA).put(VirusStrain.STRAIN_A, mRNAStrainA); + initialAntibodies.get(VaccinationType.vector).put(VirusStrain.STRAIN_A, mRNAStrainA * 4./20.); + initialAntibodies.get(VirusStrain.SARS_CoV_2).put(VirusStrain.STRAIN_A, mRNAStrainA * 6./20.); + initialAntibodies.get(VirusStrain.ALPHA).put(VirusStrain.STRAIN_A, mRNAStrainA * 6./20.); + initialAntibodies.get(VirusStrain.DELTA).put(VirusStrain.STRAIN_A, mRNAStrainA * 8./20.); + initialAntibodies.get(VirusStrain.OMICRON_BA1).put(VirusStrain.STRAIN_A, 64.0 / 300. / mutEscBa5 /mutEscStrainA); + initialAntibodies.get(VirusStrain.OMICRON_BA2).put(VirusStrain.STRAIN_A, 64.0 / 300./ mutEscBa5 /mutEscStrainA); + initialAntibodies.get(VirusStrain.OMICRON_BA5).put(VirusStrain.STRAIN_A, 64.0 / 300. / mutEscStrainA); + initialAntibodies.get(VirusStrain.STRAIN_A).put(VirusStrain.STRAIN_A, 64.0 / 300.); + initialAntibodies.get(VirusStrain.STRAIN_B).put(VirusStrain.STRAIN_A, 64.0 / 300. / mutEscStrainA / mutEscStrainB / mutEscBa5); + initialAntibodies.get(VaccinationType.ba1Update).put(VirusStrain.STRAIN_A, mRNAAlpha / mutEscBa5 / mutEscStrainA); + initialAntibodies.get(VaccinationType.ba5Update).put(VirusStrain.STRAIN_A, mRNAAlpha / mutEscStrainA); + + //StrainB + double mRNAStrainB = mRNABA2 / mutEscStrainB; + initialAntibodies.get(VaccinationType.mRNA).put(VirusStrain.STRAIN_B, mRNAStrainB); + initialAntibodies.get(VaccinationType.vector).put(VirusStrain.STRAIN_B, mRNAStrainB * 4./20.); + initialAntibodies.get(VirusStrain.SARS_CoV_2).put(VirusStrain.STRAIN_B, mRNAStrainB * 6./20.); + initialAntibodies.get(VirusStrain.ALPHA).put(VirusStrain.STRAIN_B, mRNAStrainB * 6./20.); + initialAntibodies.get(VirusStrain.DELTA).put(VirusStrain.STRAIN_B, mRNAStrainB * 8./20.); + initialAntibodies.get(VirusStrain.OMICRON_BA1).put(VirusStrain.STRAIN_B, 64.0 / 300. / mutEscStrainB); + initialAntibodies.get(VirusStrain.OMICRON_BA2).put(VirusStrain.STRAIN_B, 64.0 / 300./ mutEscStrainB); + initialAntibodies.get(VirusStrain.OMICRON_BA5).put(VirusStrain.STRAIN_B, 64.0 / 300. / mutEscStrainB / mutEscBa5); + initialAntibodies.get(VirusStrain.STRAIN_A).put(VirusStrain.STRAIN_B, 64.0 / 300./ mutEscStrainA / mutEscStrainB / mutEscBa5); + initialAntibodies.get(VirusStrain.STRAIN_B).put(VirusStrain.STRAIN_B, 64.0 / 300.); + initialAntibodies.get(VaccinationType.ba1Update).put(VirusStrain.STRAIN_B, mRNAAlpha / mutEscStrainB); + initialAntibodies.get(VaccinationType.ba5Update).put(VirusStrain.STRAIN_B, mRNAAlpha / mutEscStrainB / mutEscBa5); + + + for (VaccinationType immunityType : VaccinationType.values()) { + antibodyRefreshFactors.put(immunityType, new EnumMap<>( VirusStrain.class ) ); + for (VirusStrain virusStrain : VirusStrain.values()) { + + if (immunityType == VaccinationType.mRNA) { + antibodyRefreshFactors.get(immunityType).put(virusStrain, 15.0); + } + else if (immunityType == VaccinationType.vector) { + antibodyRefreshFactors.get(immunityType).put(virusStrain, 5.0); + } + else if (immunityType == VaccinationType.ba1Update) { + antibodyRefreshFactors.get(immunityType).put(virusStrain, 15.0); + } + else if (immunityType == VaccinationType.ba5Update) { + antibodyRefreshFactors.get(immunityType).put(virusStrain, 15.0); + } + else { + antibodyRefreshFactors.get(immunityType).put(virusStrain, Double.NaN); + } + + } + } + + for (VirusStrain immunityType : VirusStrain.values()) { + antibodyRefreshFactors.put(immunityType, new EnumMap<>( VirusStrain.class ) ); + for (VirusStrain virusStrain : VirusStrain.values()) { + antibodyRefreshFactors.get(immunityType).put(virusStrain, 15.0); + } + } + + +// UtilsJR.printInitialAntibodiesToConsole(initialAntibodies); + + } + }); + + } + + private SnzCologneProductionScenario getBindings(double pHousehold, Params params) { + return new SnzCologneProductionScenario.Builder() + .setCarnivalModel(SnzCologneProductionScenario.CarnivalModel.yes) + .setSebastianUpdate(true) + .setLeisureCorrection(1.3) //params == null ? 0.0 : params.actCorrection) + .setScaleForActivityLevels(1.3) + .setSuscHouseholds_pct(pHousehold) + .setActivityHandling(EpisimConfigGroup.ActivityHandling.startOfDay) +// .setTestingModel(params != null ? FlexibleTestingModel.class : DefaultTestingModel.class) + .setInfectionModel(InfectionModelWithAntibodies.class) + .build(); + } + + @Override + public Metadata getMetadata() { + return Metadata.of("cologne", "calibration"); + } + + @Override + public Collection postProcessing() { + return List.of( + new VaccinationEffectiveness().withArgs(), + new RValuesFromEvents().withArgs(), + new VaccinationEffectivenessFromPotentialInfections().withArgs("--remove-infected"), + new FilterEvents().withArgs("--output","./output/"), + new HospitalNumbersFromEvents().withArgs("--output","./output/","--input","/scratch/projects/bzz0020/episim-input") +// new SecondaryAttackRateFromEvents().withArgs() + ); + } + + @Override + public Config prepareConfig(int id, Params params) { + + if (DEBUG_MODE) { + if (runCount == 0 && params.importSummer2022.equals("on")) { //&& params.strAEsc != 0.0 && params.ba5Inf == 0. && params.eduTest.equals("true")) { + runCount++; + } else { + return null; + } + } + + SnzCologneProductionScenario module = getBindings(0.0, params); + + Config config = module.config(); + + config.global().setRandomSeed(params.seed); + + EpisimConfigGroup episimConfig = ConfigUtils.addOrGetModule(config, EpisimConfigGroup.class); + + episimConfig.setCalibrationParameter(episimConfig.getCalibrationParameter() * 1.2 * 1.7); + + + //snapshot +// episimConfig.setSnapshotInterval(766); + episimConfig.setStartFromSnapshot("/scratch/projects/bzz0020/episim-input/snapshots-cologne-2022-10-07/" + params.seed + "-766-2022-03-31.zip"); + episimConfig.setSnapshotSeed(EpisimConfigGroup.SnapshotSeed.restore); + + + //--------------------------------------- + // S T R A I N S + //--------------------------------------- + + VirusStrainConfigGroup virusStrainConfigGroup = ConfigUtils.addOrGetModule(config, VirusStrainConfigGroup.class); + + double ba5Inf = virusStrainConfigGroup.getOrAddParams(VirusStrain.OMICRON_BA5).getInfectiousness(); + double ba5Hos = virusStrainConfigGroup.getOrAddParams(VirusStrain.OMICRON_BA5).getFactorSeriouslySick(); + +// STRAIN_A + if (!params.StrainA.equals("off")) { + + virusStrainConfigGroup.getOrAddParams(VirusStrain.STRAIN_A).setInfectiousness(virusStrainConfigGroup.getParams(VirusStrain.OMICRON_BA5).getInfectiousness() * ba5Inf); + virusStrainConfigGroup.getOrAddParams(VirusStrain.STRAIN_A).setFactorSeriouslySick(ba5Hos); + virusStrainConfigGroup.getOrAddParams(VirusStrain.STRAIN_A).setFactorSeriouslySickVaccinated(ba5Hos); + virusStrainConfigGroup.getOrAddParams(VirusStrain.STRAIN_A).setFactorCritical(ba5Hos); + } + +// STRAIN_B + if (!params.StrainB.equals("off")) { + + virusStrainConfigGroup.getOrAddParams(VirusStrain.STRAIN_B).setInfectiousness(virusStrainConfigGroup.getParams(VirusStrain.OMICRON_BA5).getInfectiousness() * ba5Inf); + virusStrainConfigGroup.getOrAddParams(VirusStrain.STRAIN_B).setFactorSeriouslySick(ba5Hos); + virusStrainConfigGroup.getOrAddParams(VirusStrain.STRAIN_B).setFactorSeriouslySickVaccinated(ba5Hos); + virusStrainConfigGroup.getOrAddParams(VirusStrain.STRAIN_B).setFactorCritical(ba5Hos); + } + + //--------------------------------------- + // I M P O R T + //--------------------------------------- + + configureFutureDiseaseImport(params, episimConfig); + + //--------------------------------------- + // R E S T R I C T I O N S + //--------------------------------------- + + FixedPolicy.ConfigBuilder builder = FixedPolicy.parse(episimConfig.getPolicy()); + + // Ci Correction after summer vacation 2022 (more air flow?) + builder.restrict(LocalDate.parse("2022-08-09"), Restriction.ofCiCorrection(params.ciCorr), "educ_primary", "educ_kiga", "educ_secondary", "educ_tertiary", "educ_other"); + + + // vary amount of "school" activity that takes place during vacation + builder.restrict(LocalDate.parse("2022-06-27"), params.eduRfVacation, "educ_primary", "educ_kiga", "educ_secondary", "educ_tertiary", "educ_other"); + + episimConfig.setPolicy(builder.build()); + + + //--------------------------------------- + // M I S C + //--------------------------------------- + + + // modify seasonality + episimConfig.getOrAddContainerParams("educ_kiga").setSeasonality(params.eduSeasonality); + episimConfig.getOrAddContainerParams("educ_primary").setSeasonality(params.eduSeasonality); + episimConfig.getOrAddContainerParams("educ_secondary").setSeasonality(params.eduSeasonality); + episimConfig.getOrAddContainerParams("educ_tertiary").setSeasonality(params.eduSeasonality); + episimConfig.getOrAddContainerParams("educ_higher").setSeasonality(params.eduSeasonality); + episimConfig.getOrAddContainerParams("educ_other").setSeasonality(params.eduSeasonality); + + + if (DEBUG_MODE) { + UtilsJR.produceDiseaseImportPlot(episimConfig.getInfections_pers_per_day()); + } + + return config; + } + + private void configureFutureDiseaseImport(Params params, EpisimConfigGroup episimConfig) { + Map infPerDayBa1 = new HashMap<>(episimConfig.getInfections_pers_per_day().getOrDefault(VirusStrain.OMICRON_BA1, new TreeMap<>())); + Map infPerDayBa2 = new HashMap<>(episimConfig.getInfections_pers_per_day().getOrDefault(VirusStrain.OMICRON_BA2, new TreeMap<>())); + Map infPerDayBa5 = new HashMap<>(episimConfig.getInfections_pers_per_day().getOrDefault(VirusStrain.OMICRON_BA5, new TreeMap<>())); + Map infPerDayStrA = new HashMap<>(episimConfig.getInfections_pers_per_day().getOrDefault(VirusStrain.STRAIN_A, new TreeMap<>())); + Map infPerDayStrB = new HashMap<>(episimConfig.getInfections_pers_per_day().getOrDefault(VirusStrain.STRAIN_B, new TreeMap<>())); + + //StrainA + if (!params.StrainA.equals("off")) { + infPerDayStrA.put(LocalDate.parse("2020-01-01"), 0); + LocalDate strADate = LocalDate.parse("2022-11-01"); + for (int i = 0; i < 7; i++) { + infPerDayStrA.put(strADate.plusDays(i), 4); + } + infPerDayStrA.put(strADate.plusDays(7), 1); + } + + //StrainB + if (!params.StrainB.equals("off")) { + infPerDayStrB.put(LocalDate.parse("2020-01-01"), 0); + LocalDate strBDate = LocalDate.parse("2022-11-01"); + for (int i = 0; i < 7; i++) { + infPerDayStrB.put(strBDate.plusDays(i), 4); + } + infPerDayStrB.put(strBDate.plusDays(7), 1); + } + + + // add projected disease import for vacation waves after initial disease import + int facBa2 = 4; + int facBa5 = 4; + int facStrAB = 4; + + LocalDate dateBa2 = LocalDate.parse("2022-01-27"); // local min of disease import + LocalDate dateBa5 = LocalDate.parse("2022-05-01"); // after vaca import + LocalDate dateStrainAB = LocalDate.parse("2022-11-18"); // after vaca import + + + if (params.importSummer2022.equals("on")) { + NavigableMap data = DataUtils.readDiseaseImport(SnzCologneProductionScenario.INPUT.resolve("cologneDiseaseImport_Projected.csv")); + LocalDate date = null; + for (Map.Entry entry : data.entrySet()) { + date = entry.getKey(); + double factor = 0.25 * 2352476. / 919936.; //25% sample, data is given for Cologne City so we have to scale it to the whole model +// + double cases = factor * entry.getValue(); + + if (date.isAfter(dateStrainAB) && (!params.StrainA.equals("off") || !params.StrainB.equals("off"))) { + if (!params.StrainA.equals("off") && !params.StrainB.equals("off")) { + infPerDayStrA.put(date, ((int) cases * facStrAB) == 0 ? 1 : (int) (0.5 * cases * facStrAB)); + infPerDayStrB.put(date, ((int) cases * facStrAB) == 0 ? 1 : (int) (0.5 * cases * facStrAB)); + } + else if (!params.StrainA.equals("off")) { + infPerDayStrA.put(date, ((int) cases * facStrAB) == 0 ? 1 : (int) (cases * facStrAB)); + } + else if (!params.StrainB.equals("off")) { + infPerDayStrB.put(date, ((int) cases * facStrAB) == 0 ? 1 : (int) (cases * facStrAB)); + } + else { + throw new RuntimeException(); + } + infPerDayBa5.put(date, 1); + infPerDayBa2.put(date, 1); + } else if (date.isAfter(dateBa5)) { + infPerDayBa5.put(date, ((int) cases * facBa5) == 0 ? 1 : (int) (cases * facBa5)); + infPerDayBa2.put(date, 1); + } else if (date.isAfter(dateBa2)) { + infPerDayBa2.put(date, ((int) cases * facBa2) == 0 ? 1 : (int) (cases * facBa2)); + } + + } + } else if (params.importSummer2022.equals("off")) { + } else { + throw new RuntimeException(); + } + + + // save disease import + episimConfig.setInfections_pers_per_day(VirusStrain.OMICRON_BA1, infPerDayBa1); + episimConfig.setInfections_pers_per_day(VirusStrain.OMICRON_BA2, infPerDayBa2); + episimConfig.setInfections_pers_per_day(VirusStrain.OMICRON_BA5, infPerDayBa5); + + if (!params.StrainA.equals("off")) { + episimConfig.setInfections_pers_per_day(VirusStrain.STRAIN_A, infPerDayStrA); + } + if (!params.StrainB.equals("off")) { + episimConfig.setInfections_pers_per_day(VirusStrain.STRAIN_B, infPerDayStrB); + } + } + + public static final class Params { + // general + @GenerateSeeds(5) + public long seed; + + @StringParameter({"on", "off"}) + public String importSummer2022; + + @Parameter({0.5, 0.75, 1.0}) + public double eduSeasonality; + + // ci correction of schools starting on Aug 9 (when school begins again), relates to air flow + @Parameter({0.25, 0.5, 0.75}) + public double ciCorr; + + //how much "school" activity takes places during vacation summmer 2022 + @Parameter({0.2, 0.4, 0.6, 0.8, 1.0}) + public double eduRfVacation; + + + +// @StringParameter({"off", "3.0", "6.0"}) + @StringParameter({"3.0"}) + public String StrainA; + +// @StringParameter({"off", "3.0", "6.0"}) + @StringParameter({"off"}) + public String StrainB; + + @StringParameter({"2022-11-15","2022-12-01","2022-12-15"}) + public String resDate; + + + // vaccination campaign +// @StringParameter({"ba1Update", "ba5Update", "mRNA", "off"}) + @StringParameter({"off"}) + public String vacType; + } + + + public static void main(String[] args) { + String[] args2 = { + RunParallel.OPTION_SETUP, CologneVaryHhSusceptibility.class.getName(), + RunParallel.OPTION_PARAMS, Params.class.getName(), + RunParallel.OPTION_TASKS, Integer.toString(1), + RunParallel.OPTION_ITERATIONS, Integer.toString(1000), + RunParallel.OPTION_METADATA + }; + + RunParallel.main(args2); + } + + +} + diff --git a/src/main/java/org/matsim/run/batch/StartFromImmunizations.java b/src/main/java/org/matsim/run/batch/StartFromImmunizations.java new file mode 100644 index 000000000..901abbcec --- /dev/null +++ b/src/main/java/org/matsim/run/batch/StartFromImmunizations.java @@ -0,0 +1,155 @@ +package org.matsim.run.batch; + +import com.google.inject.AbstractModule; +import com.google.inject.Module; +import com.google.inject.Singleton; +import com.google.inject.multibindings.Multibinder; +import com.google.inject.util.Modules; +import org.matsim.core.config.Config; +import org.matsim.core.config.ConfigUtils; +import org.matsim.episim.BatchRun; +import org.matsim.episim.EpisimConfigGroup; +import org.matsim.episim.analysis.*; +import org.matsim.episim.model.*; +import org.matsim.episim.model.vaccination.VaccinationModel; +import org.matsim.episim.model.vaccination.VaccinationStrategyReoccurringCampaigns; +import org.matsim.run.RunParallel; +import org.matsim.run.modules.SnzCologneProductionScenario; + +import javax.annotation.Nullable; +import java.time.LocalDate; +import java.util.*; + + +/** + * Batch for Bmbf runs + */ +public class StartFromImmunizations implements BatchRun { + + + @Nullable + @Override + public Module getBindings(int id, @Nullable Params params) { + return Modules.override(getBindings(0.0, params)).with(new AbstractModule() { + @Override + protected void configure() { + + // VACCINATION MODEL + Multibinder set = Multibinder.newSetBinder(binder(), VaccinationModel.class); + set.addBinding().to(VaccinationStrategyReoccurringCampaigns.class).in(Singleton.class); + // fixed values + LocalDate start = LocalDate.parse("2020-03-01"); + VaccinationType vaccinationType = VaccinationType.mRNA; + int campaignDuration = 300000; + + // default values, to be changed if params != null + int minDaysAfterInfection = 180; + int minDaysAfterVaccination = 180; + VaccinationStrategyReoccurringCampaigns.Config.VaccinationPool vaccinationPool = VaccinationStrategyReoccurringCampaigns.Config.VaccinationPool.unvaccinated; + LocalDate emergencyDate = LocalDate.MAX; + LocalDate dateToTurnDownMinDaysAfterInfection = LocalDate.MAX; + Map startDateToVaccination = new HashMap<>(); + startDateToVaccination.put(start, vaccinationType); + bind(VaccinationStrategyReoccurringCampaigns.Config.class).toInstance(new VaccinationStrategyReoccurringCampaigns.Config(startDateToVaccination, campaignDuration, vaccinationPool, minDaysAfterInfection, minDaysAfterVaccination, emergencyDate, dateToTurnDownMinDaysAfterInfection)); + + } + }); + + //ANTIBODY Model + + } + + private SnzCologneProductionScenario getBindings(double pHousehold, Params params) { + return new SnzCologneProductionScenario.Builder() + .setCarnivalModel(SnzCologneProductionScenario.CarnivalModel.yes) + .setSebastianUpdate(true) + .setLeisureCorrection(1.3) //params == null ? 0.0 : params.actCorrection) + .setScaleForActivityLevels(1.3) + .setSuscHouseholds_pct(pHousehold) + .setActivityHandling(EpisimConfigGroup.ActivityHandling.startOfDay) +// .setTestingModel(params != null ? FlexibleTestingModel.class : DefaultTestingModel.class) + .setInfectionModel(InfectionModelWithAntibodies.class) + .build(); + } + + @Override + public Metadata getMetadata() { + return Metadata.of("cologne", "calibration"); + } + + @Override + public Collection postProcessing() { + return List.of( +// new VaccinationEffectiveness().withArgs(), +// new RValuesFromEvents().withArgs(), +// new VaccinationEffectivenessFromPotentialInfections().withArgs("--remove-infected"), + new FilterEvents().withArgs("--output","./output/"), + new HospitalNumbersFromEvents().withArgs("--output","./output/","--input","/Users/jakob/git/shared-svn/projects/episim/matsim-files/snz/Cologne/episim-input") +// new SecondaryAttackRateFromEvents().withArgs() + ); + } + + @Override + public Config prepareConfig(int id, Params params) { + + + SnzCologneProductionScenario module = getBindings(0.0, params); + + Config config = module.config(); + + config.global().setRandomSeed(params.seed); + + EpisimConfigGroup episimConfig = ConfigUtils.addOrGetModule(config, EpisimConfigGroup.class); + + episimConfig.setCalibrationParameter(episimConfig.getCalibrationParameter() * 1.2 * 1.7); + + //snapshot + +// episimConfig.setSnapshotInterval(15); +// +// episimConfig.setStartDate(LocalDate.parse("2022-04-01")); +// episimConfig.setStartFromImmunization("/Users/jakob/imm-hist-970-2022-10-21"); + episimConfig.setStartDate(LocalDate.parse("2022-03-15")); + episimConfig.setStartFromImmunization("/Users/jakob/git/matsim-episim/seed_4711-1846/calibration1.events_reduced.tar"); + + +// episimConfig.setSnapshotSeed(EpisimConfigGroup.SnapshotSeed.reseed); +// episimConfig.setStartFromSnapshot("/Users/jakob/git/matsim-episim/output/seed_4711/2022-11-15/episim-snapshot-015-2020-03-10.zip"); + + + //--------------------------------------- + // S T R A I N S + //--------------------------------------- + +// VirusStrainConfigGroup virusStrainConfigGroup = ConfigUtils.addOrGetModule(config, VirusStrainConfigGroup.class); +// +// virusStrainConfigGroup.getOrAddParams(VirusStrain.SARS_CoV_2).setInfectiousness(virusStrainConfigGroup.getParams(VirusStrain.SARS_CoV_2).getInfectiousness() * 10); + + + return config; + } + + public static final class Params { + // general + @GenerateSeeds(1) + public long seed; + + + } + + + public static void main(String[] args) { + String[] args2 = { + RunParallel.OPTION_SETUP, StartFromImmunizations.class.getName(), + RunParallel.OPTION_PARAMS, Params.class.getName(), + RunParallel.OPTION_TASKS, Integer.toString(1), + RunParallel.OPTION_ITERATIONS, Integer.toString(30), + RunParallel.OPTION_METADATA + }; + + RunParallel.main(args2); + } + + +} + diff --git a/src/main/java/org/matsim/run/batch/UtilsJR.java b/src/main/java/org/matsim/run/batch/UtilsJR.java index 3ff3edfee..9883e471d 100644 --- a/src/main/java/org/matsim/run/batch/UtilsJR.java +++ b/src/main/java/org/matsim/run/batch/UtilsJR.java @@ -2,6 +2,8 @@ import com.typesafe.config.Config; import com.typesafe.config.ConfigValue; +import org.matsim.episim.model.ImmunityEvent; +import org.matsim.episim.model.VaccinationType; import org.matsim.episim.model.VirusStrain; import tech.tablesaw.api.DateColumn; import tech.tablesaw.api.DoubleColumn; @@ -17,6 +19,7 @@ import java.io.*; import java.nio.charset.StandardCharsets; import java.time.LocalDate; +import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.NavigableMap; @@ -104,7 +107,7 @@ static void produceMaskPlot(Config policyConfig) { // } } -// producePlot(recordsDate,values,groupings,"import by strain", "import", "importByStrain.html"); +// producePlot(recordsDate,values,groupings,"import by strain", "import", "importByStrain-old22.html"); @@ -146,4 +149,61 @@ private static void producePlot(DateColumn records, DoubleColumn values, StringC } + protected static void printInitialAntibodiesToConsole(Map> initialAntibodies, boolean ignoreSuperfluous) { + + List ignoredEvents = new ArrayList<>(); + if (ignoreSuperfluous) { + ignoredEvents = List.of(VirusStrain.STRAIN_B, VirusStrain.B1351, VaccinationType.generic, VaccinationType.natural, VaccinationType.ba1Update, VaccinationType.ba5Update); + } + System.out.print("immunityGiver"); + for (VirusStrain immunityFrom : VirusStrain.values()) { + if (ignoredEvents.contains(immunityFrom)) { + continue; + } + +// if (immunityFrom == VirusStrain.OMICRON_BA1) { +// System.out.print("," + "BA.1"); +// } else if (immunityFrom == VirusStrain.OMICRON_BA2) { +// System.out.print("," + "BA.2"); +// } else if (immunityFrom == VirusStrain.OMICRON_BA5) { +// System.out.print("," + "BA.5"); +// } else { + System.out.print("," + immunityFrom); +// } + + } + + + for (ImmunityEvent immunityGiver : VaccinationType.values()) { + + if (ignoredEvents.contains(immunityGiver)) { + continue; + } + + System.out.print("\n" + immunityGiver); + for (VirusStrain immunityFrom : VirusStrain.values()) { + if (ignoredEvents.contains(immunityFrom)) { + continue; + } + System.out.print("," + String.format("%.3g", initialAntibodies.get(immunityGiver).get(immunityFrom))); + } + } + for (ImmunityEvent immunityGiver : VirusStrain.values()) { + + if (ignoredEvents.contains(immunityGiver)) { + continue; + } + System.out.print("\n" + immunityGiver); + for (VirusStrain immunityFrom : VirusStrain.values()) { + + if (ignoredEvents.contains(immunityFrom)) { + continue; + } + + System.out.print("," + String.format("%.3g", initialAntibodies.get(immunityGiver).get(immunityFrom))); + } + } + + System.out.println(); + } } diff --git a/src/main/java/org/matsim/run/modules/AbstractSnzScenario.java b/src/main/java/org/matsim/run/modules/AbstractSnzScenario.java index a34c0d335..f80b41756 100644 --- a/src/main/java/org/matsim/run/modules/AbstractSnzScenario.java +++ b/src/main/java/org/matsim/run/modules/AbstractSnzScenario.java @@ -29,6 +29,10 @@ public static void addParams(EpisimConfigGroup episimConfig) { episimConfig.getOrAddContainerParams("leisure") .setContactIntensity(5.0); + episimConfig.getOrAddContainerParams("leisPublic") + .setContactIntensity(5.0); + episimConfig.getOrAddContainerParams("leisPrivate") + .setContactIntensity(5.0); episimConfig.getOrAddContainerParams("educ_kiga") .setContactIntensity(10.0); episimConfig.getOrAddContainerParams("educ_primary") diff --git a/src/main/java/org/matsim/run/modules/SnzCologneProductionScenario.java b/src/main/java/org/matsim/run/modules/SnzCologneProductionScenario.java index a71c9ca78..df5a084eb 100644 --- a/src/main/java/org/matsim/run/modules/SnzCologneProductionScenario.java +++ b/src/main/java/org/matsim/run/modules/SnzCologneProductionScenario.java @@ -30,6 +30,7 @@ import org.matsim.episim.model.activity.LocationBasedParticipationModel; import org.matsim.episim.model.input.CreateRestrictionsFromCSV; import org.matsim.episim.model.listener.HouseholdSusceptibility; + import org.matsim.episim.model.listener.WriteAntibodies; import org.matsim.episim.model.progression.AgeDependentDiseaseStatusTransitionModel; import org.matsim.episim.model.progression.DiseaseStatusTransitionModel; import org.matsim.episim.model.testing.TestType; @@ -69,7 +70,6 @@ public static class Builder extends SnzProductionScenario.Builder listener = Multibinder.newSetBinder(binder(), SimulationListener.class); + + listener.addBinding().to(HouseholdSusceptibility.class); + + // Write antibodies, iteration is hard-coded + + // listener.addBinding().to(WriteAntibodies.class); } @@ -268,11 +273,10 @@ protected void configure() { @Singleton public Config config() { - LocalDate restrictionDate = LocalDate.parse("2022-03-01"); double cologneFactor = 0.5; // Cologne model has about half as many agents as Berlin model, -> 2_352_480 - if (this.sample != 25 && this.sample != 100) - throw new RuntimeException("Sample size not calibrated! Currently only 25% is calibrated. Comment this line out to continue."); + if (this.sample != 25 && this.sample != 100) + throw new RuntimeException("Sample size not calibrated! Currently only 25% is calibrated. Comment this line out to continue."); //general config Config config = ConfigUtils.createConfig(new EpisimConfigGroup()); @@ -284,18 +288,20 @@ public Config config() { config.plans().setInputFile(inputForSample("cologne_snz_entirePopulation_emptyPlans_withDistricts_%dpt_split.xml.gz", sample)); + config.controler().setOutputDirectory("output-snzWeekScenario-" + sample + "%"); + //episim config EpisimConfigGroup episimConfig = ConfigUtils.addOrGetModule(config, EpisimConfigGroup.class); - episimConfig.setCalibrationParameter(episimConfig.getCalibrationParameter() * 0.96 * 1.06 ); +// episimConfig.setCalibrationParameter(episimConfig.getCalibrationParameter() * 0.96 * 1.06); - episimConfig.addInputEventsFile(inputForSample("cologne_snz_episim_events_wt_%dpt_split.xml.gz", sample)) + episimConfig.addInputEventsFile(inputForSample("cologne_snz_episim_events_wt_%dpt_split_withLeisureSplit.xml.gz", sample)) .addDays(DayOfWeek.MONDAY, DayOfWeek.TUESDAY, DayOfWeek.WEDNESDAY, DayOfWeek.THURSDAY, DayOfWeek.FRIDAY); - episimConfig.addInputEventsFile(inputForSample("cologne_snz_episim_events_sa_%dpt_split.xml.gz", sample)) + episimConfig.addInputEventsFile(inputForSample("cologne_snz_episim_events_sa_%dpt_split_withLeisureSplit.xml.gz", sample)) .addDays(DayOfWeek.SATURDAY); - episimConfig.addInputEventsFile(inputForSample("cologne_snz_episim_events_so_%dpt_split.xml.gz", sample)) + episimConfig.addInputEventsFile(inputForSample("cologne_snz_episim_events_so_%dpt_split_withLeisureSplit.xml.gz", sample)) .addDays(DayOfWeek.SUNDAY); episimConfig.setActivityHandling(activityHandling); @@ -309,52 +315,74 @@ public Config config() { episimConfig.setThreads(8); episimConfig.setDaysInfectious(Integer.MAX_VALUE); - if (sebastianUpdate) { - - episimConfig.getOrAddContainerParams("work").setSeasonality(0.5); - - double leisCi = 0.6; - - episimConfig.getOrAddContainerParams("leisure").setContactIntensity(9.24 * leisCi); - - episimConfig.getOrAddContainerParams("educ_kiga").setSeasonality(0.5); - episimConfig.getOrAddContainerParams("educ_primary").setSeasonality(0.5); - episimConfig.getOrAddContainerParams("educ_secondary").setSeasonality(0.5); - episimConfig.getOrAddContainerParams("educ_tertiary").setSeasonality(0.5); - episimConfig.getOrAddContainerParams("educ_higher").setSeasonality(0.5); - episimConfig.getOrAddContainerParams("educ_other").setSeasonality(0.5); - episimConfig.getOrAddContainerParams("errands").setSeasonality(0.5); - episimConfig.getOrAddContainerParams("business").setSeasonality(0.5); - episimConfig.getOrAddContainerParams("visit").setSeasonality(0.5); - episimConfig.getOrAddContainerParams("home").setSeasonality(0.5); - episimConfig.getOrAddContainerParams("quarantine_home").setSeasonality(0.5); - - } - //progression model //episimConfig.setProgressionConfig(AbstractSnzScenario2020.baseProgressionConfig(Transition.config()).build()); - episimConfig.setProgressionConfig(SnzProductionScenario.progressionConfig(Transition.config()).build());// TODO: why does this immediately override? + episimConfig.setProgressionConfig(SnzProductionScenario.progressionConfig(Transition.config()).build()); - //inital infections and import + //--------------------------------------- + // I M P O R T + //--------------------------------------- episimConfig.setInitialInfections(Integer.MAX_VALUE); if (this.diseaseImport != DiseaseImport.no) { - // SnzProductionScenario.configureDiseaseImport(episimConfig, diseaseImport, importOffset, - // cologneFactor * imprtFctMult, importFactorBeforeJune, importFactorAfterJune); - //disease import 2020 configureDiseaseImport(cologneFactor, episimConfig); } - //contact intensities + //---------------------------------------------------------------------------- + // C O N T A C T I N T E N S I T Y / S E A S O N A L I T Y + //---------------------------------------------------------------------------- + SnzProductionScenario.configureContactIntensities(episimConfig); + //work + double workCiMod = 0.75; + episimConfig.getOrAddContainerParams("work").setContactIntensity(episimConfig.getOrAddContainerParams("work").getContactIntensity() * workCiMod); + episimConfig.getOrAddContainerParams("business").setContactIntensity(episimConfig.getOrAddContainerParams("business").getContactIntensity() * workCiMod); + + //leisure & visit + double leisureCiMod = 0.4; + episimConfig.getOrAddContainerParams("leisure").setContactIntensity(episimConfig.getOrAddContainerParams("leisure").getContactIntensity() * leisureCiMod); + episimConfig.getOrAddContainerParams("leisPublic").setContactIntensity(episimConfig.getOrAddContainerParams("leisPublic").getContactIntensity() * leisureCiMod); + episimConfig.getOrAddContainerParams("leisPrivate").setContactIntensity(episimConfig.getOrAddContainerParams("leisPrivate").getContactIntensity() * leisureCiMod); + episimConfig.getOrAddContainerParams("visit").setContactIntensity(episimConfig.getOrAddContainerParams("visit").getContactIntensity() * leisureCiMod); + + + //school + double schoolCiMod = 0.75; + episimConfig.getOrAddContainerParams("educ_kiga").setContactIntensity(episimConfig.getOrAddContainerParams("educ_kiga").getContactIntensity() * schoolCiMod); + episimConfig.getOrAddContainerParams("educ_primary").setContactIntensity(episimConfig.getOrAddContainerParams("educ_primary").getContactIntensity() * schoolCiMod); + episimConfig.getOrAddContainerParams("educ_secondary").setContactIntensity(episimConfig.getOrAddContainerParams("educ_secondary").getContactIntensity() * schoolCiMod); + episimConfig.getOrAddContainerParams("educ_tertiary").setContactIntensity(episimConfig.getOrAddContainerParams("educ_tertiary").getContactIntensity() * schoolCiMod); + episimConfig.getOrAddContainerParams("educ_higher").setContactIntensity(episimConfig.getOrAddContainerParams("educ_higher").getContactIntensity() * schoolCiMod); + episimConfig.getOrAddContainerParams("educ_other").setContactIntensity(episimConfig.getOrAddContainerParams("educ_other").getContactIntensity() * schoolCiMod); + + + //SEASONALITY + episimConfig.getOrAddContainerParams("work").setSeasonality(0.5); + episimConfig.getOrAddContainerParams("educ_kiga").setSeasonality(0.5); + episimConfig.getOrAddContainerParams("educ_primary").setSeasonality(0.5); + episimConfig.getOrAddContainerParams("educ_secondary").setSeasonality(0.5); + episimConfig.getOrAddContainerParams("educ_tertiary").setSeasonality(0.5); + episimConfig.getOrAddContainerParams("educ_higher").setSeasonality(0.5); + episimConfig.getOrAddContainerParams("educ_other").setSeasonality(0.5); + episimConfig.getOrAddContainerParams("errands").setSeasonality(0.5); + episimConfig.getOrAddContainerParams("business").setSeasonality(0.5); + episimConfig.getOrAddContainerParams("visit").setSeasonality(0.5); + episimConfig.getOrAddContainerParams("home").setSeasonality(0.5); + episimConfig.getOrAddContainerParams("quarantine_home").setSeasonality(0.5); + + //--------------------------------------- + // R E S T R I C T I O N S + //--------------------------------------- + + Map inputDays = new HashMap<>(); + inputDays.put(LocalDate.parse("2021-11-01"), DayOfWeek.SUNDAY); - //restrictions and masks CreateRestrictionsFromCSV activityParticipation = new CreateRestrictionsFromCSV(episimConfig); - activityParticipation.setInput(INPUT.resolve("CologneSnzData_daily_until20220610.csv")); + activityParticipation.setInput(INPUT.resolve("CologneSnzData_daily_until20221111.csv")); activityParticipation.setScale(this.scale); activityParticipation.setLeisureAsNightly(this.leisureNightly); @@ -378,9 +406,10 @@ public Config config() { //Herbstferien builder.restrict(LocalDate.parse("2020-10-12"), 0.2, "educ_primary", "educ_kiga", "educ_secondary", "educ_tertiary", "educ_other"); builder.restrict(LocalDate.parse("2020-10-23"), 1.0, "educ_primary", "educ_kiga", "educ_secondary", "educ_tertiary", "educ_other"); - //Weihnachtsferien TODO: check end date; shouldn't it be 2021-01-06 + //Weihnachtsferien builder.restrict(LocalDate.parse("2020-12-23"), 0.2, "educ_primary", "educ_kiga", "educ_secondary", "educ_tertiary", "educ_other"); - builder.restrict(LocalDate.parse("2021-01-11"), 0.5, "educ_primary", "educ_kiga", "educ_secondary", "educ_tertiary", "educ_other"); + builder.restrict(LocalDate.parse("2021-02-21"), 0.5, "educ_primary"); + builder.restrict(LocalDate.parse("2021-03-15"), 0.5, "educ_kiga", "educ_secondary", "educ_tertiary", "educ_other"); //Osterferien builder.restrict(LocalDate.parse("2021-03-29"), 0.2, "educ_primary", "educ_kiga", "educ_secondary", "educ_tertiary", "educ_other"); builder.restrict(LocalDate.parse("2021-04-10"), 0.5, "educ_primary", "educ_kiga", "educ_secondary", "educ_tertiary", "educ_other"); @@ -409,15 +438,58 @@ public Config config() { //Herbstferien builder.restrict(LocalDate.parse("2022-10-04"), 0.2, "educ_primary", "educ_kiga", "educ_secondary", "educ_tertiary", "educ_other"); builder.restrict(LocalDate.parse("2022-10-15"), 1.0, "educ_primary", "educ_kiga", "educ_secondary", "educ_tertiary", "educ_other"); - //Weihnachtsferien - builder.restrict(LocalDate.parse("2022-12-23"), 0.2, "educ_primary", "educ_kiga", "educ_secondary", "educ_tertiary", "educ_other"); - builder.restrict(LocalDate.parse("2023-01-06"), 1.0, "educ_primary", "educ_kiga", "educ_secondary", "educ_tertiary", "educ_other"); + //Weihnachtsferien todo: reinstate xmas +// builder.restrict(LocalDate.parse("2022-12-23"), 0.2, "educ_primary", "educ_kiga", "educ_secondary", "educ_tertiary", "educ_other"); +// builder.restrict(LocalDate.parse("2023-01-06"), 1.0, "educ_primary", "educ_kiga", "educ_secondary", "educ_tertiary", "educ_other"); + +// builder.restrict(LocalDate.parse("2022-12-19"), 0.2, "educ_higher"); +// builder.restrict(LocalDate.parse("2022-12-31"), 1.0, "educ_higher"); + + if (carnivalModel.equals(CarnivalModel.yes)) { + // Friday 25.2 to Monday 28.2 (Rosenmontag) + builder.restrict(LocalDate.parse("2022-02-25"), 1., "work", "leisure","leisPublic","leisPrivate", "shop_daily", "shop_other", "visit", "errands", "business"); + builder.restrict(LocalDate.parse("2022-02-27"), 1., "work", "leisure","leisPublic","leisPrivate", "shop_daily", "shop_other", "visit", "errands", "business"); // sunday, to overwrite the setting on sundays + builder.restrict(LocalDate.parse("2022-03-01"), 0.7, "work", "leisure","leisPublic","leisPrivate", "shop_daily", "shop_other", "visit", "errands", "business"); // tuesday, back to normal after carnival + + builder.restrict(LocalDate.parse("2022-02-25"), Restriction.ofCiCorrection(2.0), "leisure","leisPublic","leisPrivate"); + builder.restrict(LocalDate.parse("2022-03-01"), Restriction.ofCiCorrection(1.0), "leisure","leisPublic","leisPrivate"); + inputDays.put(LocalDate.parse("2022-02-28"), DayOfWeek.SUNDAY); // set monday to be a sunday + } + episimConfig.setInputDays(inputDays); - builder.restrict(LocalDate.parse("2022-12-19"), 0.2, "educ_higher"); - builder.restrict(LocalDate.parse("2022-12-31"), 1.0, "educ_higher"); + //leisure & work factor + if (this.restrictions != Restrictions.no) { + if (leisureCorrection == 0.) { // assume old factor of 1.9, only applied to leisure TODO: get rid of this artifact + builder.apply("2020-10-15", "2020-12-14", (d, e) -> e.put("fraction", 1 - 1.9 * (1 - (double) e.get("fraction"))), "leisure","leisPublic","leisPrivate"); + } else if (leisureCorrection != 1) { + builder.apply("2020-10-15", "2020-12-14", (d, e) -> e.put("fraction", 1 - leisureCorrection * (1 - (double) e.get("fraction"))), "business", "errands", "leisure","leisPublic","leisPrivate", "shop_daily", "shop_other", "visit", "work"); + } + + // builder.applyToRf("2020-10-15", "2020-12-14", (d, rf) -> rf - leisureOffset, "leisure"); + BiFunction workVacFactor = (d, rf) -> rf * 0.92; + builder.applyToRf("2020-04-03", "2020-04-17", workVacFactor, "work", "business"); + builder.applyToRf("2020-06-26", "2020-08-07", workVacFactor, "work", "business"); + builder.applyToRf("2020-10-09", "2020-10-23", workVacFactor, "work", "business"); + builder.applyToRf("2020-12-18", "2021-01-01", workVacFactor, "work", "business"); + builder.applyToRf("2021-01-29", "2021-02-05", workVacFactor, "work", "business"); + builder.applyToRf("2021-03-26", "2021-04-09", workVacFactor, "work", "business"); + builder.applyToRf("2021-07-01", "2021-08-13", workVacFactor, "work", "business"); + builder.applyToRf("2021-10-08", "2021-10-22", workVacFactor, "work", "business"); + builder.applyToRf("2021-12-22", "2022-01-05", workVacFactor, "work", "business"); + builder.applyToRf("2022-04-11", "2022-04-23", workVacFactor, "work", "business"); + builder.applyToRf("2022-06-27", "2022-08-09", workVacFactor, "work", "business"); + + builder.restrict(LocalDate.parse("2022-10-04"), 0.78 * 0.92, "work", "business"); + builder.restrict(LocalDate.parse("2022-10-15"), 0.78, "work", "business"); + //todo: revert xmas factor +// builder.restrict(LocalDate.parse("2022-12-23"), 0.78 * 0.92, "work", "business"); +// builder.restrict(LocalDate.parse("2023-01-06"), 0.78, "work", "business"); + } + + //MASKS { LocalDate masksCenterDate = LocalDate.of(2020, 4, 27); for (int ii = 0; ii <= 14; ii++) { @@ -438,7 +510,7 @@ public Config config() { } //curfew - builder.restrict("2021-04-17", Restriction.ofClosingHours(21, 5), "leisure", "visit"); + builder.restrict("2021-04-17", Restriction.ofClosingHours(21, 5), "leisure","leisPublic","leisPrivate", "visit"); Map curfewCompliance = new HashMap(); curfewCompliance.put(LocalDate.parse("2021-04-17"), 1.0); curfewCompliance.put(LocalDate.parse("2021-05-31"), 0.0); @@ -447,96 +519,46 @@ public Config config() { // vaccinated individuals (even tho they are allowed to complete activities, they will naturally reduce their activities) LocalDate transition2gTo3g = LocalDate.of(2022, 3, 4); double leis = 1.0; - builder.restrict(LocalDate.parse("2021-12-01"), Restriction.ofVaccinatedRf(0.75), "leisure"); - builder.restrict(transition2gTo3g, Restriction.ofVaccinatedRf(leis), "leisure"); + builder.restrict(LocalDate.parse("2021-12-01"), Restriction.ofVaccinatedRf(0.75), "leisure","leisPublic","leisPrivate"); + builder.restrict(transition2gTo3g, Restriction.ofVaccinatedRf(leis), "leisure","leisPublic","leisPrivate"); //2G for unvaccinated susceptible agents (will move more activities in private homes, thats why we assume 0.75 for both vaccinated and unvaccinated) - builder.restrict(LocalDate.parse("2021-11-22"), Restriction.ofSusceptibleRf(0.75), "leisure"); - builder.restrict(transition2gTo3g, Restriction.ofSusceptibleRf(leis), "leisure"); + builder.restrict(LocalDate.parse("2021-11-22"), Restriction.ofSusceptibleRf(0.75), "leisure","leisPublic","leisPrivate"); + builder.restrict(transition2gTo3g, Restriction.ofSusceptibleRf(leis), "leisure","leisPublic","leisPrivate"); double schoolFac = 0.5; builder.restrict(LocalDate.parse("2021-08-17"), Restriction.ofCiCorrection(1 - (0.5 * schoolFac)), "educ_primary", "educ_kiga", "educ_secondary", "educ_tertiary", "educ_other"); + for (LocalDate date = LocalDate.parse("2020-04-21"); date.isBefore(LocalDate.parse("2021-05-01")); date = date.plusDays(1)) { + builder.restrict(date, Restriction.ofMask(Map.of(FaceMask.CLOTH, 0.45, FaceMask.SURGICAL, 0.45)), "pt", "errands", "shop_daily", "shop_other"); + } + builder.restrict(LocalDate.parse("2021-08-17"), Restriction.ofMask(FaceMask.N95, 0.9 * schoolFac), "educ_primary", "educ_secondary", "educ_higher", "educ_tertiary", "educ_other"); builder.restrict(LocalDate.parse("2021-11-02"), Restriction.ofMask(FaceMask.N95, 0.0), "educ_primary", "educ_secondary", "educ_tertiary", "educ_other"); builder.restrict(LocalDate.parse("2021-12-02"), Restriction.ofMask(FaceMask.N95, 0.9 * schoolFac), "educ_primary", "educ_secondary", "educ_tertiary", "educ_other"); // mask mandate removed - builder.restrict(LocalDate.of(2022, 4, 4), Restriction.ofMask(FaceMask.N95, 0.), "educ_primary", "educ_kiga", "educ_secondary", "educ_tertiary", "educ_other", "leisure", "work", "business"); //todo: check + builder.restrict(LocalDate.of(2022, 4, 4), Restriction.ofMask(FaceMask.N95, 0.), "educ_primary", "educ_kiga", "educ_secondary", "educ_tertiary", "educ_other", "educ_higher", "leisure","leisPublic","leisPrivate","work", "business"); builder.restrict(LocalDate.of(2022, 4, 4), Restriction.ofMask(Map.of( - FaceMask.CLOTH, 0.0, - FaceMask.N95, 0.0, - FaceMask.SURGICAL, 0.0)), - "shop_daily", "shop_other", "errands"); + FaceMask.CLOTH, 0.0, + FaceMask.N95, 0.0, + FaceMask.SURGICAL, 0.0)), + "shop_daily", "shop_other", "errands"); builder.restrict(LocalDate.of(2022, 4, 4), Restriction.ofMask(Map.of( - FaceMask.CLOTH, 0.0, - FaceMask.N95, 0.25, - FaceMask.SURGICAL, 0.25)), - "pt"); - - - //tracing - if (this.tracing == Tracing.yes) { - - SnzProductionScenario.configureTracing(config, cologneFactor); - - } - - - Map inputDays = new HashMap<>(); - inputDays.put(LocalDate.parse("2021-11-01"), DayOfWeek.SUNDAY); - episimConfig.setInputDays(inputDays); - - //outdoorFractions - if (this.weatherModel != WeatherModel.no) { - - double outdoorAlpha = sebastianUpdate ? 0.8 : 1.0; - SnzProductionScenario.configureWeather(episimConfig, weatherModel, - SnzCologneProductionScenario.INPUT.resolve("cologneWeather.csv").toFile(), - SnzCologneProductionScenario.INPUT.resolve("weatherDataAvgCologne2000-2020.csv").toFile(), outdoorAlpha - ); - - - } else { - episimConfig.setLeisureOutdoorFraction(Map.of( - LocalDate.of(2020, 1, 1), 0.) - ); - } - - //leisure & work factor - if (this.restrictions != Restrictions.no) { + FaceMask.CLOTH, 0.0, + FaceMask.N95, 0.25, + FaceMask.SURGICAL, 0.25)), + "pt"); - if (leisureCorrection != 1) - builder.apply("2020-10-15", "2020-12-14", (d, e) -> e.put("fraction", 1 - leisureCorrection * (1 - (double) e.get("fraction"))), "leisure"); - // builder.applyToRf("2020-10-15", "2020-12-14", (d, rf) -> rf - leisureOffset, "leisure"); - BiFunction workVacFactor = (d, rf) -> rf * 0.92; - builder.applyToRf("2020-04-03", "2020-04-17", workVacFactor, "work", "business"); - builder.applyToRf("2020-06-26", "2020-08-07", workVacFactor, "work", "business"); - builder.applyToRf("2020-10-09", "2020-10-23", workVacFactor, "work", "business"); - builder.applyToRf("2020-12-18", "2021-01-01", workVacFactor, "work", "business"); - builder.applyToRf("2021-01-29", "2021-02-05", workVacFactor, "work", "business"); - builder.applyToRf("2021-03-26", "2021-04-09", workVacFactor, "work", "business"); - builder.applyToRf("2021-07-01", "2021-08-13", workVacFactor, "work", "business"); - builder.applyToRf("2021-10-08", "2021-10-22", workVacFactor, "work", "business"); - builder.applyToRf("2021-12-22", "2022-01-05", workVacFactor, "work", "business"); - builder.restrict(LocalDate.parse("2022-04-11"), 0.78 * 0.92, "work", "business"); - builder.restrict(LocalDate.parse("2022-04-23"), 0.78, "work", "business"); - builder.restrict(LocalDate.parse("2022-06-27"), 0.78 * 0.92, "work", "business"); - builder.restrict(LocalDate.parse("2022-08-09"), 0.78, "work", "business"); - builder.restrict(LocalDate.parse("2022-10-04"), 0.78 * 0.92, "work", "business"); - builder.restrict(LocalDate.parse("2022-10-15"), 0.78, "work", "business"); - builder.restrict(LocalDate.parse("2022-12-23"), 0.78 * 0.92, "work", "business"); - builder.restrict(LocalDate.parse("2023-01-06"), 0.78, "work", "business"); - - - } - + //--------------------------------------- + // V A C C I N A T I O N S + //--------------------------------------- if (this.vaccinations.equals(Vaccinations.yes)) { VaccinationConfigGroup vaccinationConfig = ConfigUtils.addOrGetModule(config, VaccinationConfigGroup.class); @@ -576,74 +598,91 @@ public Config config() { } - if (carnivalModel.equals(CarnivalModel.yes)) { - // Friday 25.2 to Monday 28.2 (Rosenmontag) - builder.restrict(LocalDate.parse("2022-02-25"), 1., "work", "leisure", "shop_daily", "shop_other", "visit", "errands", "business"); - builder.restrict(LocalDate.parse("2022-02-27"), 1., "work", "leisure", "shop_daily", "shop_other", "visit", "errands", "business"); // sunday, to overwrite the setting on sundays - builder.restrict(LocalDate.parse("2022-03-01"), 0.7, "work", "leisure", "shop_daily", "shop_other", "visit", "errands", "business"); // tuesday, back to normal after carnival + builder.setHospitalScale(this.scale); - builder.restrict(LocalDate.parse("2022-02-25"), Restriction.ofCiCorrection(2.0), "leisure"); - builder.restrict(LocalDate.parse("2022-03-01"), Restriction.ofCiCorrection(1.0), "leisure"); + episimConfig.setPolicy(builder.build()); - inputDays.put(LocalDate.parse("2022-02-28"), DayOfWeek.SUNDAY); // set monday to be a sunday - } + //--------------------------------------- + // S T R A I N S + //--------------------------------------- - builder.setHospitalScale(this.scale); + VirusStrainConfigGroup virusStrainConfigGroup = ConfigUtils.addOrGetModule(config, VirusStrainConfigGroup.class); - episimConfig.setPolicy(builder.build()); - //configure strains //alpha - double aInf = sebastianUpdate ? 1.9 : 1.7; //todo: choose value, see CologneJR -// SnzProductionScenario.configureStrains(episimConfig, ConfigUtils.addOrGetModule(config, VirusStrainConfigGroup.class)); - VirusStrainConfigGroup virusStrainConfigGroup = ConfigUtils.addOrGetModule(config, VirusStrainConfigGroup.class); + double aInf = 1.9 * 1.4; virusStrainConfigGroup.getOrAddParams(VirusStrain.ALPHA).setInfectiousness(aInf); virusStrainConfigGroup.getOrAddParams(VirusStrain.ALPHA).setFactorSeriouslySick(0.5); virusStrainConfigGroup.getOrAddParams(VirusStrain.ALPHA).setFactorSeriouslySickVaccinated(0.5); //delta - double deltaInf = sebastianUpdate ? 3.1 : 2.7 ; //todo: choose value, see CologneJR - double deltaHos = sebastianUpdate ? 1.0 : 0.9;//todo: choose value, see CologneJR + double deltaInf = 3.1 * 0.9; + double deltaHos = 1.0; virusStrainConfigGroup.getOrAddParams(VirusStrain.DELTA).setInfectiousness(deltaInf); virusStrainConfigGroup.getOrAddParams(VirusStrain.DELTA).setFactorSeriouslySick(deltaHos); virusStrainConfigGroup.getOrAddParams(VirusStrain.DELTA).setFactorSeriouslySickVaccinated(deltaHos); - //omicron -// double oInf = params.ba1Inf; - double oHos = sebastianUpdate ? 0.2 : 0.13; - double ba1Inf = sebastianUpdate ? 2.2 : 2.4; - - if (ba1Inf > 0) { - virusStrainConfigGroup.getOrAddParams(VirusStrain.OMICRON_BA1).setInfectiousness(deltaInf * ba1Inf); - virusStrainConfigGroup.getOrAddParams(VirusStrain.OMICRON_BA1).setFactorSeriouslySick(oHos); - virusStrainConfigGroup.getOrAddParams(VirusStrain.OMICRON_BA1).setFactorSeriouslySickVaccinated(oHos); - virusStrainConfigGroup.getOrAddParams(VirusStrain.OMICRON_BA1).setFactorCritical(oHos); - } + //BA.1 + double oHos = 0.2; + double ba1Inf = 1.9 * deltaInf; + virusStrainConfigGroup.getOrAddParams(VirusStrain.OMICRON_BA1).setInfectiousness(ba1Inf); + virusStrainConfigGroup.getOrAddParams(VirusStrain.OMICRON_BA1).setFactorSeriouslySick(oHos); + virusStrainConfigGroup.getOrAddParams(VirusStrain.OMICRON_BA1).setFactorSeriouslySickVaccinated(oHos); + virusStrainConfigGroup.getOrAddParams(VirusStrain.OMICRON_BA1).setFactorCritical(oHos); //BA.2 - double ba2Inf= 1.7; - if (ba2Inf > 0) { - virusStrainConfigGroup.getOrAddParams(VirusStrain.OMICRON_BA2).setInfectiousness(deltaInf * ba1Inf * ba2Inf); - virusStrainConfigGroup.getOrAddParams(VirusStrain.OMICRON_BA2).setFactorSeriouslySick(oHos); - virusStrainConfigGroup.getOrAddParams(VirusStrain.OMICRON_BA2).setFactorSeriouslySickVaccinated(oHos); - virusStrainConfigGroup.getOrAddParams(VirusStrain.OMICRON_BA2).setFactorCritical(oHos); + double ba2Inf = 1.7 * ba1Inf; + virusStrainConfigGroup.getOrAddParams(VirusStrain.OMICRON_BA2).setInfectiousness(ba2Inf); + virusStrainConfigGroup.getOrAddParams(VirusStrain.OMICRON_BA2).setFactorSeriouslySick(oHos); + virusStrainConfigGroup.getOrAddParams(VirusStrain.OMICRON_BA2).setFactorSeriouslySickVaccinated(oHos); + virusStrainConfigGroup.getOrAddParams(VirusStrain.OMICRON_BA2).setFactorCritical(oHos); + + //BA.5 + double ba5Inf = ba2Inf; + virusStrainConfigGroup.getOrAddParams(VirusStrain.OMICRON_BA5).setInfectiousness(ba5Inf); + virusStrainConfigGroup.getOrAddParams(VirusStrain.OMICRON_BA5).setFactorSeriouslySick(oHos); + virusStrainConfigGroup.getOrAddParams(VirusStrain.OMICRON_BA5).setFactorSeriouslySickVaccinated(oHos); + virusStrainConfigGroup.getOrAddParams(VirusStrain.OMICRON_BA5).setFactorCritical(oHos); + + // remove age-differentiation in susceptibility for all strains expect WILD & ALPHA + TreeMap nonSteppedAgeSusceptibility = new TreeMap<>(Map.of( + 19, 1d, + 20, 1d + )); + + for (VirusStrain strain : VirusStrain.values()) { + if (strain.equals(VirusStrain.SARS_CoV_2) || strain.equals(VirusStrain.ALPHA) || strain.equals(VirusStrain.B1351)) { + continue; + } + virusStrainConfigGroup.getOrAddParams(strain).setAgeSusceptibility(nonSteppedAgeSusceptibility); } - //testing + //--------------------------------------- + // T E S T I N G + //--------------------------------------- + + // this section is split into four nested sections: + // A) RAPID test + // i) unvaccinated + // ii) vaccinated + // B) PCR Test + // i) unvaccinated + // ii) vaccinated + + // load testing config group and set general parameters TestingConfigGroup testingConfigGroup = ConfigUtils.addOrGetModule(config, TestingConfigGroup.class); testingConfigGroup.setTestAllPersonsAfter(LocalDate.parse("2021-10-01")); - TestingConfigGroup.TestingParams rapidTest = testingConfigGroup.getOrAddParams(TestType.RAPID_TEST); - TestingConfigGroup.TestingParams pcrTest = testingConfigGroup.getOrAddParams(TestType.PCR); - testingConfigGroup.setStrategy(TestingConfigGroup.Strategy.ACTIVITIES); List actsList = new ArrayList(); actsList.add("leisure"); + actsList.add("leisPublic"); + actsList.add("leisPrivate"); actsList.add("work"); actsList.add("business"); actsList.add("educ_kiga"); @@ -654,37 +693,41 @@ public Config config() { actsList.add("educ_higher"); testingConfigGroup.setActivities(actsList); + + // 1) Rapid test + TestingConfigGroup.TestingParams rapidTest = testingConfigGroup.getOrAddParams(TestType.RAPID_TEST); rapidTest.setFalseNegativeRate(0.3); rapidTest.setFalsePositiveRate(0.03); - pcrTest.setFalseNegativeRate(0.1); - pcrTest.setFalsePositiveRate(0.01); + // 1i) unvaccianted testingConfigGroup.setHouseholdCompliance(1.0); - LocalDate testingStartDate = LocalDate.parse("2021-03-19"); - Map leisureTests = new HashMap(); Map workTests = new HashMap(); Map eduTests = new HashMap(); Map kigaPrimaryTests = new HashMap(); Map uniTests = new HashMap(); + leisureTests.put(LocalDate.parse("2020-01-01"), 0.); workTests.put(LocalDate.parse("2020-01-01"), 0.); eduTests.put(LocalDate.parse("2020-01-01"), 0.); kigaPrimaryTests.put(LocalDate.parse("2020-01-01"), 0.); uniTests.put(LocalDate.parse("2020-01-01"), 0.); + + LocalDate testingStartDate = LocalDate.parse("2021-03-19"); + for (int i = 1; i <= 31; i++) { leisureTests.put(testingStartDate.plusDays(i), 0.1 * i / 31.); workTests.put(testingStartDate.plusDays(i), 0.1 * i / 31.); - eduTests.put(testingStartDate.plusDays(i), 0.4 * i / 31.); - kigaPrimaryTests.put(testingStartDate.plusDays(i), 0.4 * i / 31.); uniTests.put(testingStartDate.plusDays(i), 0.8 * i / 31.); } - kigaPrimaryTests.put(LocalDate.parse("2021-05-10"), 0.0); + eduTests.put(LocalDate.parse("2021-04-25"), 0.4); + +// kigaPrimaryTests.put(LocalDate.parse("2021-05-10"), 0.0); workTests.put(LocalDate.parse("2021-06-04"), 0.05); @@ -701,30 +744,30 @@ public Config config() { // 3g removed for work workTests.put(LocalDate.of(2022, 3, 20), 0.); -// if (testing.equals("no")) { // turn of school tests after easter break - kigaPrimaryTests.put(LocalDate.of(2022,4,25), 0.0); - eduTests.put(LocalDate.of(2022,4,25), 0.0); - uniTests.put(LocalDate.of(2022,4,25), 0.0); -// } - - // no more regulation regarding test, we assume once every 2 weeks - leisureTests.put(LocalDate.of(2022, 4, 25), 0.1); - - +// kigaPrimaryTests.put(LocalDate.of(2022, 4, 25), 0.0); + eduTests.put(LocalDate.of(2022, 4, 25), 0.0); + uniTests.put(LocalDate.of(2022, 4, 25), 0.0); + leisureTests.put(LocalDate.of(2022, 4, 25), 0.0); - rapidTest.setTestingRatePerActivityAndDate((Map.of( + Map> testingRatePerAct = new HashMap<>(Map.of( "leisure", leisureTests, "work", workTests, "business", workTests, - "educ_kiga", eduTests, - "educ_primary", eduTests, + "educ_kiga", kigaPrimaryTests, + "educ_primary", kigaPrimaryTests, "educ_secondary", eduTests, "educ_tertiary", eduTests, "educ_higher", uniTests, "educ_other", eduTests - ))); + )); + + testingRatePerAct.put("leisPrivate", leisureTests); + testingRatePerAct.put("leisPublic", leisureTests); + rapidTest.setTestingRatePerActivityAndDate(testingRatePerAct); + + // 1ii) vaccinated Map leisureTestsVaccinated = new HashMap<>(); Map workTestsVaccinated = new HashMap<>(); Map eduTestsVaccinated = new HashMap<>(); @@ -737,12 +780,12 @@ public Config config() { // if (testing.equals("no")) { leisureTestsVaccinated.put(LocalDate.of(2022, 4, 25), 0.0); - workTestsVaccinated.put(LocalDate.of(2022, 4, 25),0.0); + workTestsVaccinated.put(LocalDate.of(2022, 4, 25), 0.0); eduTestsVaccinated.put(LocalDate.of(2022, 4, 25), 0.0); // } - rapidTest.setTestingRatePerActivityAndDateVaccinated((Map.of( + Map> testingRatePerActVac = new HashMap<>(Map.of( "leisure", leisureTestsVaccinated, "work", workTestsVaccinated, "business", workTestsVaccinated, @@ -752,40 +795,53 @@ public Config config() { "educ_tertiary", eduTestsVaccinated, "educ_higher", eduTestsVaccinated, "educ_other", eduTestsVaccinated - ))); + )); + + testingRatePerActVac.put("leisPrivate", leisureTestsVaccinated); + testingRatePerActVac.put("leisPublic", leisureTestsVaccinated); + + + rapidTest.setTestingRatePerActivityAndDateVaccinated(testingRatePerActVac); + // 2) PCR Test + TestingConfigGroup.TestingParams pcrTest = testingConfigGroup.getOrAddParams(TestType.PCR); + pcrTest.setFalseNegativeRate(0.1); + pcrTest.setFalsePositiveRate(0.01); + + // 2i) unvaccinated + Map leisureTestsPCR = new HashMap(); Map workTestsPCR = new HashMap(); - Map kigaPramaryTestsPCR = new HashMap(); + Map kigaPrimaryTestsPCR = new HashMap(); Map eduTestsPCR = new HashMap(); leisureTestsPCR.put(LocalDate.parse("2020-01-01"), 0.); workTestsPCR.put(LocalDate.parse("2020-01-01"), 0.); - kigaPramaryTestsPCR.put(LocalDate.parse("2020-01-01"), 0.); + kigaPrimaryTestsPCR.put(LocalDate.parse("2020-01-01"), 0.); eduTestsPCR.put(LocalDate.parse("2020-01-01"), 0.); - kigaPramaryTestsPCR.put(LocalDate.parse("2021-05-10"), 0.4); - -// if (testing.equals("no")) { - leisureTestsPCR.put(LocalDate.of(2022, 4, 25), 0.0); - workTestsPCR.put(LocalDate.of(2022, 4, 25),0.0); - kigaPramaryTestsPCR.put(LocalDate.of(2022, 4, 25), 0.0); - eduTestsPCR.put(LocalDate.of(2022, 4, 25), 0.0); -// } + kigaPrimaryTestsPCR.put(LocalDate.parse("2021-05-10"), 0.4); + kigaPrimaryTestsPCR.put(LocalDate.of(2022, 4, 25), 0.0); - pcrTest.setTestingRatePerActivityAndDate((Map.of( + Map> testingRatePerActPCR = new HashMap<>(Map.of( "leisure", leisureTestsPCR, "work", workTestsPCR, "business", workTestsPCR, - "educ_kiga", kigaPramaryTestsPCR, - "educ_primary", kigaPramaryTestsPCR, + "educ_kiga", kigaPrimaryTestsPCR, + "educ_primary", kigaPrimaryTestsPCR, "educ_secondary", eduTestsPCR, "educ_tertiary", eduTestsPCR, "educ_higher", eduTestsPCR, "educ_other", eduTestsPCR - ))); + )); + + testingRatePerActPCR.put("leisPrivate", leisureTestsPCR); + testingRatePerActPCR.put("leisPublic", leisureTestsPCR); + pcrTest.setTestingRatePerActivityAndDate(testingRatePerActPCR); + + // 2ii) vaccinated Map leisureTestsPCRVaccinated = new HashMap<>(); Map workTestsPCRVaccinated = new HashMap<>(); @@ -794,7 +850,7 @@ public Config config() { workTestsPCRVaccinated.put(LocalDate.parse("2020-01-01"), 0.); eduTestsPCRVaccinated.put(LocalDate.parse("2020-01-01"), 0.); - pcrTest.setTestingRatePerActivityAndDateVaccinated((Map.of( + Map> testingRatePerActVacPCR = new HashMap<>(Map.of( "leisure", leisureTestsPCRVaccinated, "work", workTestsPCRVaccinated, "business", workTestsPCRVaccinated, @@ -804,7 +860,11 @@ public Config config() { "educ_tertiary", eduTestsPCRVaccinated, "educ_higher", eduTestsPCRVaccinated, "educ_other", eduTestsPCRVaccinated - ))); + )); + + testingRatePerActVacPCR.put("leisPrivate", leisureTestsPCRVaccinated); + testingRatePerActVacPCR.put("leisPublic", leisureTestsPCRVaccinated); + pcrTest.setTestingRatePerActivityAndDateVaccinated(testingRatePerActVacPCR); rapidTest.setTestingCapacity_pers_per_day(Map.of( LocalDate.of(1970, 1, 1), 0, @@ -814,7 +874,18 @@ public Config config() { LocalDate.of(1970, 1, 1), 0, testingStartDate, Integer.MAX_VALUE)); - //tracing + + //--------------------------------------- + // T R A C I N G + //--------------------------------------- + + + if (this.tracing == Tracing.yes) { + + SnzProductionScenario.configureTracing(config, cologneFactor); + + } + TracingConfigGroup tracingConfig = ConfigUtils.addOrGetModule(config, TracingConfigGroup.class); boolean qv = false; // if (params.qV.equals("yes")) { @@ -843,7 +914,24 @@ public Config config() { // restrictionDate, qs )); - config.controler().setOutputDirectory("output-snzWeekScenario-" + sample + "%"); + //--------------------------------------- + // W E A T H E R + //--------------------------------------- + if (this.weatherModel != WeatherModel.no) { + + double outdoorAlpha = 0.8; + SnzProductionScenario.configureWeather(episimConfig, weatherModel, + SnzCologneProductionScenario.INPUT.resolve("cologneWeather.csv").toFile(), + SnzCologneProductionScenario.INPUT.resolve("weatherDataAvgCologne2000-2020.csv").toFile(), outdoorAlpha + ); + + + } else { + episimConfig.setLeisureOutdoorFraction(Map.of( + LocalDate.of(2020, 1, 1), 0.) + ); + } + return config; } @@ -874,132 +962,95 @@ private void configureDiseaseImport(double cologneFactor, EpisimConfigGroup epis infPerDayWild.put(LocalDate.parse("2020-07-19"), (int) (0.5 * 32)); infPerDayWild.put(LocalDate.parse("2020-08-09"), 1); -// episimConfig.setInfections_pers_per_day(infPerDayWild); - - if (sebastianUpdate) { - - for (Map.Entry entry : infPerDayWild.entrySet()) { - if (entry.getKey().isBefore(LocalDate.parse("2020-08-12"))) { - int value = entry.getValue(); - value = Math.max(1, value); - infPerDayWild.put(entry.getKey(), value); - } + for (Map.Entry entry : infPerDayWild.entrySet()) { + if (entry.getKey().isBefore(LocalDate.parse("2020-08-12"))) { + int value = entry.getValue(); + value = Math.max(1, value); + infPerDayWild.put(entry.getKey(), value); } + } - double facWild = 4.0; - double facAlpha = 4.0; - double facDelta = 4.0; - double facBa1 = 4.0; - double facBa2 = 4.0; - double facBa5 = 4.0; - - LocalDate dateAlpha = LocalDate.parse("2021-01-23"); - LocalDate dateDelta = LocalDate.parse("2021-06-28"); - LocalDate dateBa1 = LocalDate.parse("2021-12-12"); - LocalDate dateBa2 = LocalDate.parse("2022-01-05"); + double facWild = 4.0; + double facAlpha = 4.0; + double facDelta = 4.0; + double facBa1 = 4.0; + double facBa2 = 4.0; - infPerDayAlpha.put(LocalDate.parse("2020-01-01"), 0); - infPerDayDelta.put(LocalDate.parse("2020-01-01"), 0); - infPerDayBa1.put(LocalDate.parse("2020-01-01"), 0); - infPerDayBa2.put(LocalDate.parse("2020-01-01"), 0); - infPerDayBa5.put(LocalDate.parse("2020-01-01"), 0); + infPerDayAlpha.put(LocalDate.parse("2020-01-01"), 0); + infPerDayDelta.put(LocalDate.parse("2020-01-01"), 0); + infPerDayBa1.put(LocalDate.parse("2020-01-01"), 0); + infPerDayBa2.put(LocalDate.parse("2020-01-01"), 0); + infPerDayBa5.put(LocalDate.parse("2020-01-01"), 0); - NavigableMap data = DataUtils.readDiseaseImport(SnzCologneProductionScenario.INPUT.resolve("cologneDiseaseImport.csv")); + NavigableMap data = DataUtils.readDiseaseImport(SnzCologneProductionScenario.INPUT.resolve("cologneDiseaseImport.csv")); - NavigableMap> shares = DataUtils.readVOC(SnzCologneProductionScenario.INPUT.resolve("VOC_Cologne_RKI.csv")); + NavigableMap> shares = DataUtils.readVOC(SnzCologneProductionScenario.INPUT.resolve("VOC_Cologne_RKI.csv")); - // Get import share - BiFunction lookup = (date, strain) -> shares.floorEntry(date).getValue().getOrDefault(strain, 0d); - LocalDate date = null; + // Get import share + BiFunction lookup = (date, strain) -> shares.floorEntry(date).getValue().getOrDefault(strain, 0d); + LocalDate date = null; - for (Map.Entry e : data.entrySet()) { + for (Map.Entry e : data.entrySet()) { - double factor = 0.25 * 2352476. / 919936.; //25% sample, data is given for Cologne City, so we have to scale it to the whole model + double factor = 0.25 * 2352476. / 919936.; //25% sample, data is given for Cologne City, so we have to scale it to the whole model - double cases = factor * e.getValue(); - date = e.getKey(); + double cases = factor * e.getValue(); + date = e.getKey(); // infPerDayBa5.put(date, (int) (lookup.apply(date, VirusStrain.OMICRON_BA5) * cases * facBa5)); - infPerDayBa2.put(date, (int) (lookup.apply(date, VirusStrain.OMICRON_BA2) * cases * facBa2)); - infPerDayBa1.put(date, (int) (lookup.apply(date, VirusStrain.OMICRON_BA1) * cases * facBa1)); - infPerDayDelta.put(date,(int) (lookup.apply(date, VirusStrain.DELTA) * cases * facDelta)); - infPerDayAlpha.put(date, (int) (lookup.apply(date, VirusStrain.ALPHA) * cases * facAlpha)); - infPerDayWild.put(date, (int) (lookup.apply(date, VirusStrain.SARS_CoV_2) * cases * facWild)); - - } - - LocalDate wildImportEnds = LocalDate.of(2021, 11, 21); - infPerDayWild = infPerDayWild.entrySet().stream().filter(entry -> entry.getKey().isBefore(wildImportEnds)).collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)); - infPerDayWild.put(wildImportEnds, 1); - - LocalDate alphaImportEnds = LocalDate.of(2021, 7, 17); - infPerDayAlpha = infPerDayAlpha.entrySet().stream().filter(entry -> entry.getKey().isBefore(alphaImportEnds)).collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)); - infPerDayAlpha.put(alphaImportEnds, 1); - - LocalDate deltaImportEnds = LocalDate.of(2022, 1, 16); - infPerDayDelta = infPerDayDelta.entrySet().stream().filter(entry -> entry.getKey().isBefore(deltaImportEnds)).collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)); - infPerDayDelta.put(deltaImportEnds, 1); - + infPerDayBa2.put(date, (int) (lookup.apply(date, VirusStrain.OMICRON_BA2) * cases * facBa2)); + infPerDayBa1.put(date, (int) (lookup.apply(date, VirusStrain.OMICRON_BA1) * cases * facBa1)); + infPerDayDelta.put(date, (int) (lookup.apply(date, VirusStrain.DELTA) * cases * facDelta)); + infPerDayAlpha.put(date, (int) (lookup.apply(date, VirusStrain.ALPHA) * cases * facAlpha)); + infPerDayWild.put(date, (int) (lookup.apply(date, VirusStrain.SARS_CoV_2) * cases * facWild)); - LocalDate dateAfterCsvEnds = date.plusDays(1); - infPerDayBa2.put(dateAfterCsvEnds, 1); - - - - - } else { - - //ALPHA -// Map infPerDayB117 = new HashMap<>(); - infPerDayAlpha.put(LocalDate.parse("2020-01-01"), 0); - - infPerDayAlpha.put(LocalDate.parse("2021-01-16"), 20); - infPerDayAlpha.put(LocalDate.parse("2021-01-16").plusDays(1), 1); - infPerDayAlpha.put(LocalDate.parse("2020-12-31"), 1); - -// episimConfig.setInfections_pers_per_day(VirusStrain.ALPHA, infPerDayAlpha); - - // DELTA -// Map infPerDayDelta = new HashMap<>(); - infPerDayDelta.put(LocalDate.parse("2020-01-01"), 0); - infPerDayDelta.put(LocalDate.parse("2021-06-21"), 4); - infPerDayDelta.put(LocalDate.parse("2021-06-21").plusDays(7), 1); + } - LocalDate summerHolidaysEnd = LocalDate.parse("2021-08-17").minusDays(14); - int imp1 = 120; - int imp2 = 10; - int imp3 = 40; + LocalDate wildImportEnds = LocalDate.of(2021, 11, 21); + infPerDayWild = infPerDayWild.entrySet().stream().filter(entry -> entry.getKey().isBefore(wildImportEnds)).collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)); + infPerDayWild.put(wildImportEnds, 1); - SnzCologneProductionScenario.interpolateImport(infPerDayDelta, 1.0, summerHolidaysEnd.minusDays(5 * 7), summerHolidaysEnd, 1, imp1); - SnzCologneProductionScenario.interpolateImport(infPerDayDelta, 1.0, summerHolidaysEnd, summerHolidaysEnd.plusDays(3 * 7), imp1, imp2); + LocalDate alphaImportEnds = LocalDate.of(2021, 7, 17); + infPerDayAlpha = infPerDayAlpha.entrySet().stream().filter(entry -> entry.getKey().isBefore(alphaImportEnds)).collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)); + infPerDayAlpha.put(alphaImportEnds, 1); + LocalDate deltaImportEnds = LocalDate.of(2022, 1, 16); + infPerDayDelta = infPerDayDelta.entrySet().stream().filter(entry -> entry.getKey().isBefore(deltaImportEnds)).collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)); + infPerDayDelta.put(deltaImportEnds, 1); - LocalDate autumnHolidaysEnd = LocalDate.parse("2021-10-17"); - SnzCologneProductionScenario.interpolateImport(infPerDayDelta, 1.0, autumnHolidaysEnd.minusDays(2 * 7), autumnHolidaysEnd, imp2, imp3); - SnzCologneProductionScenario.interpolateImport(infPerDayDelta, 1.0, autumnHolidaysEnd, autumnHolidaysEnd.plusDays(2 * 7), imp3, 1); + LocalDate dateAfterCsvEnds = date.plusDays(1); + infPerDayBa2.put(dateAfterCsvEnds, 1); + // initial import alpha + LocalDate startDateAlpha = LocalDate.parse("2021-01-15"); + for (int i = 0; i < 7; i++) { + infPerDayAlpha.put(startDateAlpha.plusDays(i), 4); + } + infPerDayAlpha.put(startDateAlpha.plusDays(7), 1); -// episimConfig.setInfections_pers_per_day(VirusStrain.DELTA, infPerDayDelta); + // initial import BA.1 // TODO: Why did we comment this out +// LocalDate ba1Date = LocalDate.parse(params.ba1Date); +// for (int i = 0; i < 7; i++) { +// infPerDayBa1.put(ba1Date.plusDays(i), 4); +// } +// infPerDayBa1.put(ba1Date.plusDays(7), 1); - //BA.1 - String ba1Date = "2021-11-21"; -// Map infPerDayBA1 = new HashMap<>(); - infPerDayBa1.put(LocalDate.parse("2020-01-01"), 0); - infPerDayBa1.put(LocalDate.parse(ba1Date), 4); - infPerDayBa1.put(LocalDate.parse(ba1Date).plusDays(7), 1); -// episimConfig.setInfections_pers_per_day(VirusStrain.OMICRON_BA1, infPerDayBa1); + // initial import BA.2 + LocalDate ba2Date = LocalDate.parse("2021-12-18"); + for (int i = 0; i < 7; i++) { + infPerDayBa2.put(ba2Date.plusDays(i), 4); + } + infPerDayBa2.put(ba2Date.plusDays(7), 1); - //BA.2 - String ba2Date = "2021-12-18"; -// Map infPerDayBA2 = new HashMap<>(); - infPerDayBa2.put(LocalDate.parse("2020-01-01"), 0); - infPerDayBa2.put(LocalDate.parse(ba2Date), 4); - infPerDayBa2.put(LocalDate.parse(ba2Date).plusDays(7), 1); -// episimConfig.setInfections_pers_per_day(VirusStrain.OMICRON_BA2, infPerDayBa2); + // initial import BA.5 + LocalDate ba5Date = LocalDate.parse("2022-04-10"); + for (int i = 0; i < 7; i++) { + infPerDayBa5.put(ba5Date.plusDays(i), 4); } + infPerDayBa5.put(ba5Date.plusDays(7), 1); // set all initial infections episimConfig.setInfections_pers_per_day(VirusStrain.SARS_CoV_2, infPerDayWild); @@ -1026,7 +1077,7 @@ private void configureBooster(VaccinationConfigGroup vaccinationConfig, double b .setBoostWaitPeriod(boostAfter * 30 + 6 * 7); ; - vaccinationConfig.getOrAddParams(VaccinationType.omicronUpdate) + vaccinationConfig.getOrAddParams(VaccinationType.ba1Update) .setBoostWaitPeriod(boostAfter * 30 + 6 * 7); ; diff --git a/src/main/java/org/matsim/run/modules/SnzProductionScenario.java b/src/main/java/org/matsim/run/modules/SnzProductionScenario.java index ce36a5a3a..80ad0d0c6 100644 --- a/src/main/java/org/matsim/run/modules/SnzProductionScenario.java +++ b/src/main/java/org/matsim/run/modules/SnzProductionScenario.java @@ -76,6 +76,8 @@ public static void configureContactIntensities(EpisimConfigGroup episimConfig) { episimConfig.getOrAddContainerParams("pt", "tr").setContactIntensity(10.0).setSpacesPerFacility(spaces); episimConfig.getOrAddContainerParams("work").setContactIntensity(1.47).setSpacesPerFacility(spaces); episimConfig.getOrAddContainerParams("leisure").setContactIntensity(9.24).setSpacesPerFacility(spaces).setSeasonality(1.0); + episimConfig.getOrAddContainerParams("leisPublic").setContactIntensity(9.24).setSpacesPerFacility(spaces).setSeasonality(1.0); + episimConfig.getOrAddContainerParams("leisPrivate").setContactIntensity(9.24).setSpacesPerFacility(spaces).setSeasonality(1.0); // episimConfig.getOrAddContainerParams("restaurant").setContactIntensity(9.24).setSpacesPerFacility(spaces).setSeasonal(true); episimConfig.getOrAddContainerParams("educ_kiga").setContactIntensity(11.0).setSpacesPerFacility(spaces); episimConfig.getOrAddContainerParams("educ_primary").setContactIntensity(11.0).setSpacesPerFacility(spaces); diff --git a/src/main/java/org/matsim/scenarioCreation/DifferentiateLeisureActivitiesInEventsCologne.java b/src/main/java/org/matsim/scenarioCreation/DifferentiateLeisureActivitiesInEventsCologne.java new file mode 100644 index 000000000..e13811793 --- /dev/null +++ b/src/main/java/org/matsim/scenarioCreation/DifferentiateLeisureActivitiesInEventsCologne.java @@ -0,0 +1,180 @@ +package org.matsim.scenarioCreation; + +import org.matsim.api.core.v01.Id; +import org.matsim.api.core.v01.events.*; +import org.matsim.api.core.v01.events.handler.ActivityEndEventHandler; +import org.matsim.api.core.v01.events.handler.ActivityStartEventHandler; +import org.matsim.api.core.v01.events.handler.PersonEntersVehicleEventHandler; +import org.matsim.api.core.v01.events.handler.PersonLeavesVehicleEventHandler; +import org.matsim.core.api.experimental.events.EventsManager; +import org.matsim.core.events.EventsUtils; +import org.matsim.core.events.algorithms.EventWriterXML; +import org.matsim.core.utils.io.IOUtils; +import org.matsim.facilities.ActivityFacility; + +import java.io.IOException; +import java.util.*; +import java.util.stream.Collectors; + +/* +appends living space to "home" activities in events file, +e.g. home_35 indicates that agent has between 30 and 40 m2 of living space. + */ +public class DifferentiateLeisureActivitiesInEventsCologne { + + public static final String ROOT = "../shared-svn/projects/episim/matsim-files/snz/Cologne/episim-input/"; + private static final int SAMPLE = 25; + + public static void main(String[] args) throws IOException { + + // collect event files + List eventFiles = new ArrayList<>(); + + eventFiles.add(ROOT + "cologne_snz_episim_events_wt_"+ SAMPLE +"pt_split.xml.gz"); + eventFiles.add(ROOT + "cologne_snz_episim_events_sa_"+ SAMPLE +"pt_split.xml.gz"); + eventFiles.add(ROOT + "cologne_snz_episim_events_so_"+ SAMPLE +"pt_split.xml.gz"); + + + // 1) get list of all facilities where leisure events take place + + Set leisureFacilitiesFullWeek = new HashSet<>(); + + for (String events : eventFiles) { + + EventsManager manager = EventsUtils.createEventsManager(); + + FacilityFinder handler = new FacilityFinder(); + + manager.addHandler(handler); + EventsUtils.readEvents(manager, events); + + leisureFacilitiesFullWeek.addAll(handler.leisureFacilities); + } + + // 2) make list of facilities assigned to leisPrivate & leisPublic + Set facilitiesPublic = new HashSet<>(); + Set facilitiesPrivate = new HashSet<>(); + + Random rnd = new Random(4711); + for (String facility : leisureFacilitiesFullWeek) { + if (rnd.nextDouble() > 0.5) { + facilitiesPublic.add(facility); + } else { + facilitiesPrivate.add(facility); + } + } + + + // 3) process events to replace all leisure activity types + for (String events : eventFiles) { + + EventsManager manager = EventsUtils.createEventsManager(); + + LeisureSplitter handler = new LeisureSplitter(facilitiesPublic,facilitiesPrivate); + + manager.addHandler(handler); + EventsUtils.readEvents(manager, events); + + + String outputName = events.replace(".xml.gz", "") + "_withLeisureSplit.xml.gz"; + + EventWriterXML writer = new EventWriterXML( + IOUtils.getOutputStream(IOUtils.getFileUrl(outputName), false) + ); + + handler.modifiedEvents.forEach(writer::handleEvent); + writer.closeFile(); + + } + + } + + public static class FacilityFinder implements ActivityEndEventHandler, ActivityStartEventHandler{ + + Set leisureFacilities = new HashSet<>(); + + @Override + public void handleEvent(ActivityEndEvent activityEndEvent) { + if (activityEndEvent.getActType().equals("leisure")) { + leisureFacilities.add(activityEndEvent.getFacilityId().toString()); + } + + } + + @Override + public void handleEvent(ActivityStartEvent activityStartEvent) { + if (activityStartEvent.getActType().equals("leisure")) { + leisureFacilities.add(activityStartEvent.getFacilityId().toString()); + } + } + } + + + public static class LeisureSplitter implements ActivityEndEventHandler, ActivityStartEventHandler, PersonEntersVehicleEventHandler, PersonLeavesVehicleEventHandler { + final List modifiedEvents = new ArrayList<>(); + + final Set facilitiesPublic; + final Set facilitiesPrivate; + + LeisureSplitter(Set facilitiesPublic, Set facilitiesPrivate) { + this.facilitiesPublic = facilitiesPublic; + this.facilitiesPrivate = facilitiesPrivate; + } + + @Override + public void handleEvent(ActivityEndEvent activityEndEvent) { + Id facilityId = activityEndEvent.getFacilityId(); + + String actType = activityEndEvent.getActType(); + actType = getModifiedLeisureAct(facilityId, actType); + + ActivityEndEvent modifiedEvent = new ActivityEndEvent(activityEndEvent.getTime(), + activityEndEvent.getPersonId(), + activityEndEvent.getLinkId(), + facilityId, + actType); + + modifiedEvents.add(modifiedEvent); + } + + + @Override + public void handleEvent(ActivityStartEvent activityStartEvent) { + Id facilityId = activityStartEvent.getFacilityId(); + String actType = activityStartEvent.getActType(); + + actType = getModifiedLeisureAct(facilityId, actType); + ActivityStartEvent modifiedEvent = new ActivityStartEvent(activityStartEvent.getTime(), + activityStartEvent.getPersonId(), + activityStartEvent.getLinkId(), + facilityId, + actType); + + modifiedEvents.add(modifiedEvent); + } + + private String getModifiedLeisureAct(Id facilityId, String actType) { + if (actType.equals("leisure")) { + if (facilitiesPublic.contains(facilityId.toString())) { + actType = "leisPublic"; + } else if (facilitiesPrivate.contains(facilityId.toString())) { + actType = "leisPrivate"; + } else { + throw new RuntimeException("all leisure facilities should have been designated either public or private"); + } + + } + return actType; + } + + @Override + public void handleEvent(PersonEntersVehicleEvent personEntersVehicleEvent) { + modifiedEvents.add(personEntersVehicleEvent); + } + + @Override + public void handleEvent(PersonLeavesVehicleEvent personLeavesVehicleEvent) { + modifiedEvents.add(personLeavesVehicleEvent); + } + } +} diff --git a/src/main/python/analysis/utils.py b/src/main/python/analysis/utils.py index 9aa067e45..0d162edff 100644 --- a/src/main/python/analysis/utils.py +++ b/src/main/python/analysis/utils.py @@ -217,9 +217,15 @@ def aggregate_batch_run(run): if idx not in idMap or f.endswith(".xml"): continue - with z.open(f) as zf: - df = pd.read_csv(zf, sep="\t") - runs[idMap[idx]][filename].append(df) + with z.open(f) as zf: + try: + df = pd.read_csv(zf, sep="\t") + runs[idMap[idx]][filename].append(df) + except pd.errors.EmptyDataError as e: + print("WARN: " + f + " is empty") + except: + print("Error reading " + f) + with zipfile.ZipFile(run.replace(".zip", "-aggr.zip"), mode="w", compresslevel=9, @@ -245,14 +251,25 @@ def aggregate_batch_run(run): with zipfile.ZipFile(zip_buffer, "w", compresslevel=9, compression=zipfile.ZIP_DEFLATED) as zInner: for filename, dfs in files.items(): + concat = pd.concat(dfs) - by_row_index = concat.groupby(concat.index) - # Ignore files that can't be aggregated - try: - means = by_row_index.mean() - except Exception as e: - continue + # Tidy data needs to be aggregated separatly + if "vaccinationsDetailed.tsv" in filename: + + grouped = concat.groupby(["day", "date", "type", "number"]) + means = grouped.agg(amount=("amount", "mean")) + means = means.reset_index() + + else: + + # Aggregate by computung the mean + try: + by_row_index = concat.groupby(concat.index) + means = by_row_index.mean() + except Exception as e: + # Ignore files that can't be aggregated + continue # attach non numeric columns without aggregating nonNumeric = dfs[0].columns.difference(means.columns) @@ -270,6 +287,19 @@ def aggregate_batch_run(run): buf = io.TextIOWrapper(zf, encoding="utf8", newline="\n") means.to_csv(buf, sep="\t", columns=list(dfs[0].columns), mode="w", line_terminator="\n", index=False) buf.flush() + + if "infections.txt.csv" in filename: + + cols = [dfs[0].day, dfs[0].date] + [df.nShowingSymptomsCumulative for df in dfs] + columns = pd.concat(cols, axis=1) + + columns.columns = [columns.columns[0], columns.columns[1]] + ["%s_%d" % (name, i) for i, name in enumerate(columns.columns[2:])] + + with zInner.open(str(runId) + ".infectionsPerSeed.tsv", "w") as zf: + buf = io.TextIOWrapper(zf, encoding="utf8", newline="\n") + columns.to_csv(buf, sep="\t", mode="w", line_terminator="\n", index=False) + buf.flush() + with z.open("summaries/" + str(runId) + ".zip", "w") as f: f.write(zip_buffer.getvalue()) @@ -310,8 +340,16 @@ def calc_r_reduction(base_case, base_variables, df, group_by=None): return result + if __name__ == "__main__": - aggregate_batch_run("../../../../output/summaries.zip") - + import argparse + + parser = argparse.ArgumentParser(description="Aggregate batch run over multiple seeds") + parser.add_argument("file", type=str, nargs=1, help="Path to summaries zip", default="../../../../output/summaries.zip") + args = parser.parse_args() + + aggregate_batch_run(args.file[0]) + + diff --git a/src/main/resources/collect.sh b/src/main/resources/collect.sh index b81a0f413..06b491666 100755 --- a/src/main/resources/collect.sh +++ b/src/main/resources/collect.sh @@ -48,6 +48,7 @@ aggregate_run() { copy_output *.strains.tsv $tmp/$run copy_output *.vaccinations.tsv $tmp/$run copy_output *.vaccinationsDetailed.tsv $tmp/$run + copy_output *.secondaryAttackRate.txt $tmp/$run copy_output *.config.xml $tmp/$run for OUTPUT in *.post.*.*; do @@ -79,4 +80,15 @@ zip "$cwd/summaries.zip" -r ./* cd "$cwd" || exit -rm -r tmp \ No newline at end of file +rm -r tmp + +if grep -q seed metadata.yaml; then + + echo "Aggregating seeds..." + + module load anaconda3/2019.10 + source "$EPISIM_INPUT/../env/bin/activate" + + python "$EPISIM_INPUT/../env/utils.py" "$cwd/summaries.zip" + +fi \ No newline at end of file diff --git a/src/test/java/org/matsim/episim/DataUtilsTest.java b/src/test/java/org/matsim/episim/DataUtilsTest.java index f414cdc41..c734c4ccc 100644 --- a/src/test/java/org/matsim/episim/DataUtilsTest.java +++ b/src/test/java/org/matsim/episim/DataUtilsTest.java @@ -39,7 +39,7 @@ public void voc() throws IOException { assertThat(result.get(LocalDate.of(2022,5,29))) .containsEntry(VirusStrain.OMICRON_BA1, 0.002) - .containsEntry(VirusStrain.OMICRON_BA2, 0.825); + .containsEntry(VirusStrain.OMICRON_BA2, 0.823); } } diff --git a/src/test/java/org/matsim/episim/EpisimTestUtils.java b/src/test/java/org/matsim/episim/EpisimTestUtils.java index a9e00f7c4..c2e0c0894 100644 --- a/src/test/java/org/matsim/episim/EpisimTestUtils.java +++ b/src/test/java/org/matsim/episim/EpisimTestUtils.java @@ -190,7 +190,7 @@ public static EpisimPerson createPerson(boolean vaccinable, int age) { * Helper method to infect a person. */ public static EpisimPerson infectPerson(EpisimPerson p, VirusStrain strain, double now) { - p.possibleInfection(new EpisimInfectionEvent(now, p.getPersonId(), p.getPersonId(), null, "undefined", 1, strain, 1.0, -1)); + p.possibleInfection(new EpisimInfectionEvent(now, p.getPersonId(), p.getPersonId(), null, "undefined", 1, strain, 1.0, -1, -1, p.getNumVaccinations())); p.checkInfection(); return p; } diff --git a/src/test/java/org/matsim/episim/analysis/HospitalNumbersFromEventsTest.java b/src/test/java/org/matsim/episim/analysis/HospitalNumbersFromEventsTest.java index f7a27e735..edd5e2084 100644 --- a/src/test/java/org/matsim/episim/analysis/HospitalNumbersFromEventsTest.java +++ b/src/test/java/org/matsim/episim/analysis/HospitalNumbersFromEventsTest.java @@ -1,265 +1,210 @@ -//package org.matsim.episim.analysis; -// -//import org.assertj.core.data.Offset; -//import org.assertj.core.data.Percentage; -//import org.junit.Before; -//import org.junit.Ignore; -//import org.junit.Test; -//import org.matsim.api.core.v01.Id; -//import org.matsim.api.core.v01.IdMap; -//import org.matsim.api.core.v01.Scenario; -//import org.matsim.api.core.v01.population.Person; -//import org.matsim.api.core.v01.population.Population; -//import org.matsim.api.core.v01.population.PopulationFactory; -//import org.matsim.core.config.Config; -//import org.matsim.core.config.ConfigUtils; -//import org.matsim.core.population.PopulationUtils; -//import org.matsim.core.scenario.ScenarioUtils; -//import org.matsim.episim.EpisimConfigGroup; -//import org.matsim.episim.EpisimTestUtils; -//import org.matsim.episim.VaccinationConfigGroup; -//import org.matsim.episim.VirusStrainConfigGroup; -//import org.matsim.episim.events.EpisimInfectionEvent; -//import org.matsim.episim.events.EpisimVaccinationEvent; -//import org.matsim.episim.model.VaccinationType; -//import org.matsim.episim.model.VirusStrain; -//import org.matsim.facilities.Facility; -//import org.matsim.testcases.MatsimTestUtils; -// -//import java.util.HashMap; -//import java.util.Map; -// -//import static org.junit.Assert.*; -//import static org.assertj.core.api.Assertions.assertThat; -// -//public class HospitalNumbersFromEventsTest { -// -// -// private HospitalNumbersFromEvents.Handler handler; -// private Population population; -// private final int populationSize = 1_000_000; -// private VirusStrainConfigGroup strainConfig; -// -// @Before -// public void setUp() throws Exception { -// -// // instantiate configs -// Config config = ConfigUtils.createConfig(new EpisimConfigGroup()); -// EpisimConfigGroup episimConfig = ConfigUtils.addOrGetModule(config, EpisimConfigGroup.class); -// strainConfig = ConfigUtils.addOrGetModule(config, VirusStrainConfigGroup.class); -// VaccinationConfigGroup vaccinationConfig = ConfigUtils.addOrGetModule(config, VaccinationConfigGroup.class); -// -// -// // create population -// final Scenario scenario = ScenarioUtils.createScenario(config); -// PopulationFactory popFac = scenario.getPopulation().getFactory(); -// population = scenario.getPopulation(); -// -// -// for (int i = 0; i < populationSize; i++) { -// Id personId = Id.createPersonId(i); -// Person person = popFac.createPerson(personId); -// person.getAttributes().putAttribute("microm:modeled:age", 25); -// person.getAttributes().putAttribute("district", "Köln"); -// population.addPerson(person); -// } -// -// // instantiate event handler -// Map, HospitalNumbersFromEvents.Handler.ImmunizablePerson> data = new IdMap<>(Person.class, population.getPersons().size()); -// handler = new HospitalNumbersFromEvents.Handler(data, population, episimConfig, strainConfig, vaccinationConfig); -// -// } -// -// /** -// * check's that once a vaccination or infection event is logged, the person is entered into the "data" of the handler. -// */ -// @Test -// public void testHandlerData() { -// -// Id personId1 = Id.createPersonId(1); -// assertThat(handler.data.isEmpty()).isTrue(); +package org.matsim.episim.analysis; + +import org.assertj.core.data.Percentage; +import org.junit.Before; +import org.junit.Test; +import org.matsim.api.core.v01.Id; +import org.matsim.api.core.v01.IdMap; +import org.matsim.api.core.v01.Scenario; +import org.matsim.api.core.v01.population.Person; +import org.matsim.api.core.v01.population.Population; +import org.matsim.api.core.v01.population.PopulationFactory; +import org.matsim.core.config.Config; +import org.matsim.core.config.ConfigUtils; +import org.matsim.core.scenario.ScenarioUtils; +import org.matsim.episim.EpisimConfigGroup; +import org.matsim.episim.VaccinationConfigGroup; +import org.matsim.episim.VirusStrainConfigGroup; +import org.matsim.episim.events.EpisimInfectionEvent; +import org.matsim.episim.model.VirusStrain; +import org.matsim.facilities.Facility; +import java.util.Map; + +import static org.junit.Assert.*; +import static org.assertj.core.api.Assertions.assertThat; +import static org.matsim.episim.analysis.HospitalNumbersFromEvents.*; + +public class HospitalNumbersFromEventsTest { + + + private Handler handler; + private Population population; + private final int populationSize = 1_000_000; + private VirusStrainConfigGroup strainConfig; + + @Before + public void setUp() throws Exception { + + // instantiate configs + Config config = ConfigUtils.createConfig(new EpisimConfigGroup()); + EpisimConfigGroup episimConfig = ConfigUtils.addOrGetModule(config, EpisimConfigGroup.class); + strainConfig = ConfigUtils.addOrGetModule(config, VirusStrainConfigGroup.class); + VaccinationConfigGroup vaccinationConfig = ConfigUtils.addOrGetModule(config, VaccinationConfigGroup.class); + + + // create population + final Scenario scenario = ScenarioUtils.createScenario(config); + PopulationFactory popFac = scenario.getPopulation().getFactory(); + population = scenario.getPopulation(); + + + for (int i = 0; i < populationSize; i++) { + Id personId = Id.createPersonId(i); + Person person = popFac.createPerson(personId); + person.getAttributes().putAttribute("microm:modeled:age", 25); + person.getAttributes().putAttribute("district", "Köln"); + population.addPerson(person); + } + + // instantiate event handler + Map, Handler.ImmunizablePerson> data = new IdMap<>(Person.class, population.getPersons().size()); + ConfigHolder configHolder = new ConfigHolder(episimConfig,vaccinationConfig,strainConfig); + + handler = new Handler("xxx", population, configHolder); + + } + + /** + * check's that once a vaccination or infection event is logged, the person is entered into the "data" of the handler. + */ + @Test + public void testHandlerData() { + + + // before any events, data should be empty + Id personId1 = Id.createPersonId(1); + assertTrue(handler.data.isEmpty()); + + // add vaccination event - data should contain person1 // handler.handleEvent(new EpisimVaccinationEvent(0., personId1, VaccinationType.mRNA, 1)); -// assertThat(handler.data.isEmpty()).isFalse(); -// assertThat(handler.data.containsKey(personId1)).isTrue(); -// -// handler.data.clear(); -// -// assertThat(handler.data.isEmpty()).isTrue(); -// handler.handleEvent(new EpisimInfectionEvent(0., personId1, Id.createPersonId("infector"), Id.create("facility", Facility.class), "", 2, VirusStrain.SARS_CoV_2, 0., 0.)); -// assertThat(handler.data.isEmpty()).isFalse(); -// assertThat(handler.data.containsKey(personId1)).isTrue(); -// -// -// // strain config -// -// // immunity config -// -//// handler.handleEvent(); -// -// } -// -// /** -// * Age-dependent probability of being admitted to the hospital given infection matches configuration. -// * In addition, probability of transitioning to critical (ICU) given hospitalization is checked. -// * Test is applied to 25 year-olds and 75 year-olds. -// */ -// @Ignore -// @Test -// public void testAgeComponent() { -// -// for (int i = 0; i < populationSize; i++) { -// Id personId = Id.createPersonId(i); -// handler.handleEvent(new EpisimInfectionEvent(0., personId, Id.createPersonId("infector"), Id.create("facility", Facility.class), "", 2, VirusStrain.SARS_CoV_2, 0., 0.)); -// } -// -// int hospitalizations25 = handler.postProcessHospitalAdmissions.get(handler.postProcessHospitalAdmissions.lastIntKey()); -// int icu25 = handler.postProcessICUAdmissions.get(handler.postProcessICUAdmissions.lastIntKey()); -// -// assertThat((double) hospitalizations25 / populationSize).isCloseTo(0.024, Percentage.withPercentage(2)); -// assertThat((double) icu25 / hospitalizations25).isCloseTo(0.15, Percentage.withPercentage(2)); -// -// handler.data.clear(); -// for (int i = 0; i < populationSize; i++) { -// Id personId = Id.createPersonId(i); -// Person person = population.getPersons().get(personId); -// person.getAttributes().putAttribute("microm:modeled:age", 75); -// handler.handleEvent(new EpisimInfectionEvent(10 * 24 * 3600., personId, Id.createPersonId("infector"), Id.create("facility", Facility.class), "", 2, VirusStrain.SARS_CoV_2, 0., 0.)); -// } -// -// int hospitalizations75 = handler.postProcessHospitalAdmissions.get(handler.postProcessHospitalAdmissions.lastIntKey()); -// int icu75 = handler.postProcessICUAdmissions.get(handler.postProcessICUAdmissions.lastIntKey()); -// -// -// assertThat((double) hospitalizations75 / populationSize).isCloseTo(0.23, Percentage.withPercentage(2)); -// assertThat((double) icu75 / hospitalizations75).isCloseTo(0.41, Percentage.withPercentage(2)); -// -// assertThat(hospitalizations75).isGreaterThan(hospitalizations25); -// assertThat(icu75).isGreaterThan(icu25); -// } -// -// /** -// * Checks differences in strain configuration when age & immunity is constant. In this scenario, a lower chance of -// * hospitalization (0.6x) and lower chance of ICU (0.8x) is associated with Omicron BA.1 as compared to the wild type -// */ -// @Test -// public void testStrainComponent() { -// -// // set up strain configuration -// strainConfig.getParams(VirusStrain.SARS_CoV_2).setFactorSeriouslySick(1.); -// strainConfig.getParams(VirusStrain.SARS_CoV_2).setFactorCritical(1.); -// -// strainConfig.getOrAddParams(VirusStrain.OMICRON_BA1).setFactorSeriouslySick(0.6); -// strainConfig.getOrAddParams(VirusStrain.OMICRON_BA1).setFactorCritical(0.8); -// -// -// // calculate hospitalizations/ICU for wild type -// for (int i = 0; i < populationSize; i++) { -// Id personId = Id.createPersonId(i); -// handler.handleEvent(new EpisimInfectionEvent(0., personId, Id.createPersonId("infector"), Id.create("facility", Facility.class), "", 2, VirusStrain.SARS_CoV_2, 0., 0.)); -// } -// -// int hospitalizations100 = handler.postProcessHospitalAdmissions.get(handler.postProcessHospitalAdmissions.lastIntKey()); -// int icu100 = handler.postProcessICUAdmissions.get(handler.postProcessICUAdmissions.lastIntKey()); +// assertFalse(handler.data.isEmpty()); +// assertTrue(handler.data.containsKey(personId1)); // // handler.data.clear(); -// -// -// // calculate hospitalizations/ICU for omicron -// for (int i = 0; i < populationSize; i++) { -// Id personId = Id.createPersonId(i); -// handler.handleEvent(new EpisimInfectionEvent(100*24*3600., personId, Id.createPersonId("infector"), Id.create("facility", Facility.class), "", 2, VirusStrain.OMICRON_BA1, 0., 0.)); -// } -// -// int hospitalizations60 = handler.postProcessHospitalAdmissions.get(handler.postProcessHospitalAdmissions.lastIntKey()); -// int icu80 = handler.postProcessICUAdmissions.get(handler.postProcessICUAdmissions.lastIntKey()); -// -// // omicron hospitalizations should be 0.6x wild type hospitalizations (see factorSeriouslySick above) -// assertThat(((double) hospitalizations60) / hospitalizations100).isCloseTo(0.6, Percentage.withPercentage(2)); -// -// // The chance to visit icu given hospitalization (factorCritical) for omicron should be 0.8x wild typ. -// // However, the chance of going to the hospital at all is also lower for omicron (see above assert). -// // Thus we need to multiply the two probabilities 0.8*0.6 -// assertThat(((double) icu80) / icu100).isCloseTo(0.8 * 0.6, Percentage.withPercentage(2)); -// -// } -// -// @Ignore + + // same thing for infection event + assertTrue(handler.data.isEmpty()); + handler.handleEvent(new EpisimInfectionEvent(0., personId1, Id.createPersonId("infector"), Id.create("facility", Facility.class), "", 2, VirusStrain.SARS_CoV_2, 0., 0.,0.,-1)); + assertFalse(handler.data.isEmpty()); + assertTrue(handler.data.containsKey(personId1)); + + } + + /** + * Age-dependent probability of being admitted to the hospital given infection matches configuration. + * In addition, probability of transitioning to critical (ICU) given hospitalization is checked. + * Test is applied to 25 year-olds and 75 year-olds. + */ + + @Test + public void testAgeComponent() { + + + // infect every person an infection + for (int i = 0; i < populationSize; i++) { + Id personId = Id.createPersonId(i); + handler.handleEvent(new EpisimInfectionEvent(0., personId, Id.createPersonId("infector"), Id.create("facility", Facility.class), "", 2, VirusStrain.SARS_CoV_2, 0., 0., 0.,0)); + } + + // extract total number of hospitalisations + int hospitalizations25 = handler.postProcessHospitalAdmissions.get(handler.postProcessHospitalAdmissions.lastIntKey()); + int icu25 = handler.postProcessICUAdmissions.get(handler.postProcessICUAdmissions.lastIntKey()); + + assertThat((double) hospitalizations25 / populationSize).isCloseTo(0.024, Percentage.withPercentage(2)); + assertThat((double) icu25 / hospitalizations25).isCloseTo(0.15, Percentage.withPercentage(2)); + + + // Now, check for 75+ year olds + handler.data.clear(); + for (int i = 0; i < populationSize; i++) { + Id personId = Id.createPersonId(i); + Person person = population.getPersons().get(personId); + person.getAttributes().putAttribute("microm:modeled:age", 75); + handler.handleEvent(new EpisimInfectionEvent(10 * 24 * 3600., personId, Id.createPersonId("infector"), Id.create("facility", Facility.class), "", 2, VirusStrain.SARS_CoV_2, 0., 0., 0., 0)); + } + + int hospitalizations75 = handler.postProcessHospitalAdmissions.get(handler.postProcessHospitalAdmissions.lastIntKey()); + int icu75 = handler.postProcessICUAdmissions.get(handler.postProcessICUAdmissions.lastIntKey()); + + + assertThat((double) hospitalizations75 / populationSize).isCloseTo(0.23, Percentage.withPercentage(2)); + assertThat((double) icu75 / hospitalizations75).isCloseTo(0.41, Percentage.withPercentage(2)); + + assertThat(hospitalizations75).isGreaterThan(hospitalizations25); + assertThat(icu75).isGreaterThan(icu25); + } + + /** + * Checks differences in strain configuration when age & immunity is constant. In this scenario, a lower chance of + * hospitalization (0.6x) and lower chance of ICU (0.8x) is associated with Omicron BA.1 as compared to the wild type + */ + @Test + public void testStrainComponent() { + + // set up strain configuration + strainConfig.getParams(VirusStrain.SARS_CoV_2).setFactorSeriouslySick(1.); + strainConfig.getParams(VirusStrain.SARS_CoV_2).setFactorCritical(1.); + + strainConfig.getOrAddParams(VirusStrain.OMICRON_BA1).setFactorSeriouslySick(0.6); + strainConfig.getOrAddParams(VirusStrain.OMICRON_BA1).setFactorCritical(0.8); + + + // calculate hospitalizations/ICU for wild type + for (int i = 0; i < populationSize; i++) { + Id personId = Id.createPersonId(i); + handler.handleEvent(new EpisimInfectionEvent(0., personId, Id.createPersonId("infector"), Id.create("facility", Facility.class), "", 2, VirusStrain.SARS_CoV_2, 0., 0., 0.,0)); + } + + int hospitalizations100 = handler.postProcessHospitalAdmissions.get(handler.postProcessHospitalAdmissions.lastIntKey()); + int icu100 = handler.postProcessICUAdmissions.get(handler.postProcessICUAdmissions.lastIntKey()); + + handler.data.clear(); + + + // calculate hospitalizations/ICU for omicron + for (int i = 0; i < populationSize; i++) { + Id personId = Id.createPersonId(i); + handler.handleEvent(new EpisimInfectionEvent(100 * 24 * 3600., personId, Id.createPersonId("infector"), Id.create("facility", Facility.class), "", 2, VirusStrain.OMICRON_BA1, 0., 0., 0.,0)); + } + + int hospitalizations60 = handler.postProcessHospitalAdmissions.get(handler.postProcessHospitalAdmissions.lastIntKey()); + int icu80 = handler.postProcessICUAdmissions.get(handler.postProcessICUAdmissions.lastIntKey()); + + // omicron hospitalizations should be 0.6x wild type hospitalizations (see factorSeriouslySick above) + assertThat(((double) hospitalizations60) / hospitalizations100).isCloseTo(0.6, Percentage.withPercentage(2)); + + // The chance to visit icu given hospitalization (factorCritical) for omicron should be 0.8x wild type. + // However, the chance of going to the hospital at all is also lower for omicron (see above assert). + // Thus, we need to multiply the two probabilities 0.8*0.6. + assertThat(((double) icu80) / icu100).isCloseTo(0.8 * 0.6, Percentage.withPercentage(2)); + + } + + // @Test // public void testImmunityComponent() { // // // calculate hospitalizations/ICU for people without any antibodies // for (int i = 0; i < populationSize; i++) { // Id personId = Id.createPersonId(i); -// handler.handleEvent(new EpisimInfectionEvent(0., personId, Id.createPersonId("infector"), Id.create("facility", Facility.class), "", 2, VirusStrain.SARS_CoV_2, 0., 0.)); -// } -// -// int hospitalizationsNoImmunity = handler.postProcessHospitalAdmissions.get(handler.postProcessHospitalAdmissions.lastIntKey()); -// int icuNoImmunity = handler.postProcessICUAdmissions.get(handler.postProcessICUAdmissions.lastIntKey()); -// -// // calculate hospitalizations/ICU for people with antibodies -// // this time we do not clear the the handler.data, because we need to know how long ago their last immunity event was. -// for (int i = 0; i < populationSize; i++) { -// Id personId = Id.createPersonId(i); -// handler.handleEvent(new EpisimInfectionEvent(10*24*3600., personId, Id.createPersonId("infector"), Id.create("facility", Facility.class), "", 2, VirusStrain.SARS_CoV_2, 0., 10.)); -// } -// -// int hospitalizationsWithImmunity = handler.postProcessHospitalAdmissions.get(handler.postProcessHospitalAdmissions.lastIntKey()); -// int icuWithImmunity = handler.postProcessICUAdmissions.get(handler.postProcessICUAdmissions.lastIntKey()); -// -// assertThat(hospitalizationsWithImmunity).isGreaterThan(hospitalizationsNoImmunity); -// assertThat(icuWithImmunity).isGreaterThan(icuNoImmunity); -// -// } -// -// @Test -// public void testImmunityComponent2() { -// -// // expected value (2 doses mRNA vs. DELTA) prob w/ respect to unvaccinated individual -// // p(hosp) of vaccinated person w/ respect to unvaccianted -//// double pHosp = 1. - 0.95; -//// // p(symptoms) -//// double pSym = 1. - 0.75; -//// // p(hosp|symptoms) -// -// strainConfig.getOrAddParams(VirusStrain.OMICRON_BA1); -// double pHospGivenSym = 0.1; -// -// // calculate hospitalizations/ICU for people without any antibodies -// for (int i = 0; i < populationSize; i++) { -// Id personId = Id.createPersonId(i); -// handler.handleEvent(new EpisimInfectionEvent(0., personId, Id.createPersonId("infector"), Id.create("facility", Facility.class), "", 2, VirusStrain.OMICRON_BA1, 0., 0.)); +// handler.handleEvent(new EpisimInfectionEvent(0., personId, Id.createPersonId("infector"), Id.create("facility", Facility.class), "", 2, VirusStrain.SARS_CoV_2, 0., 0., 0.,0)); // } // // int hospitalizationsNoImmunity = handler.postProcessHospitalAdmissions.get(handler.postProcessHospitalAdmissions.lastIntKey()); // int icuNoImmunity = handler.postProcessICUAdmissions.get(handler.postProcessICUAdmissions.lastIntKey()); // -// // calculate hospitalizations/ICU for people with antibodies -// // this time we do not clear the the handler.data, because we need to know how long ago their last immunity event was. -// handler.postProcessICUAdmissions.clear(); -// handler.postProcessHospitalAdmissions.clear(); // handler.data.clear(); // // for (int i = 0; i < populationSize; i++) { // Id personId = Id.createPersonId(i); -// handler.handleEvent(new EpisimVaccinationEvent(0., personId, VaccinationType.mRNA, 1)); -// handler.handleEvent(new EpisimInfectionEvent(1.5 * 24 * 3600, personId, Id.createPersonId("infector"), Id.create("facility", Facility.class), "", 2, VirusStrain.OMICRON_BA1, 0., 1.9)); +// handler.handleEvent(new EpisimInfectionEvent(10 * EpisimUtils.DAY, personId, Id.createPersonId("infector"), Id.create("facility", Facility.class), "", 2, VirusStrain.SARS_CoV_2, 0., 0., 0.,0)); +// handler.handleEvent(new EpisimInfectionEvent(15 * EpisimUtils.DAY, personId, Id.createPersonId("infector"), Id.create("facility", Facility.class), "", 2, VirusStrain.SARS_CoV_2, 0., 0., 50.,0)); // } // // int hospitalizationsWithImmunity = handler.postProcessHospitalAdmissions.get(handler.postProcessHospitalAdmissions.lastIntKey()); // int icuWithImmunity = handler.postProcessICUAdmissions.get(handler.postProcessICUAdmissions.lastIntKey()); // -// -// double probHosp = ((double) hospitalizationsWithImmunity) / hospitalizationsNoImmunity; -// -// return; -// -//// assertThat(hospitalizationsWithImmunity).isGreaterThan(hospitalizationsNoImmunity); -//// assertThat(icuWithImmunity).isGreaterThan(icuNoImmunity); -// +//// assertThat(hospitalizationsWithImmunity).isLessThan(hospitalizationsNoImmunity); +//// assertThat(icuWithImmunity).isLessThan(icuNoImmunity); // } -// -// -// -// -// -//} +} diff --git a/src/test/java/org/matsim/episim/events/InitialImmunizationHandlerTest.java b/src/test/java/org/matsim/episim/events/InitialImmunizationHandlerTest.java new file mode 100644 index 000000000..b3a4ee810 --- /dev/null +++ b/src/test/java/org/matsim/episim/events/InitialImmunizationHandlerTest.java @@ -0,0 +1,108 @@ +package org.matsim.episim.events; + +import org.apache.commons.math.stat.inference.TestUtils; +import org.junit.Before; +import org.junit.Test; +import org.matsim.api.core.v01.Id; +import org.matsim.api.core.v01.population.Person; +import org.matsim.core.api.experimental.events.EventsManager; +import org.matsim.core.events.EventsUtils; +import org.matsim.episim.*; +import org.matsim.episim.model.AntibodyModel; +import org.matsim.episim.model.ProgressionModel; +import org.matsim.episim.model.VaccinationType; +import org.matsim.episim.model.VirusStrain; +import org.mockito.Mockito; + +import java.time.LocalDate; +import java.util.HashMap; +import java.util.Map; + +import static org.junit.Assert.*; +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.Assert.assertTrue; + +public class InitialImmunizationHandlerTest { + + private EventsManager manager; + private InitialImmunizationHandler handler; + private Map, EpisimPerson> personMap; + + @Before + public void setUp() throws Exception { + + personMap = new HashMap<>(); + + EpisimConfigGroup episimConfig = new EpisimConfigGroup(); + episimConfig.setStartDate(LocalDate.parse("2022-01-05")); + + handler = new InitialImmunizationHandler(personMap, episimConfig, Mockito.mock(AntibodyModel.class), Mockito.mock(ProgressionModel.class)); + + manager = EventsUtils.createEventsManager(); + manager.addHandler(handler); + manager.initProcessing(); + } + + + // 1 2 3 4 5 6 7 8 9 <---- old count + //-3 -2 -1 0 1 2 3 4 5 <---- new count + + /** + * Tests functionality of InitialImmunizationHandler, which reads infection and vaccination events from an immunization + * history in order to initialize the EpisimPerson. + */ + @Test + public void person() { + + // create population of one + EpisimPerson patient0 = EpisimTestUtils.createPerson(); + personMap.put(patient0.getPersonId(), patient0); + + // start date on day 1 + LocalDate date1 = LocalDate.parse("2022-01-01"); // 1 DAY + manager.processEvent(new EpisimStartEvent(date1, "...")); + + // initial infection (from import) on day 2 + manager.processEvent(new EpisimInitialInfectionEvent(2 * EpisimUtils.DAY, patient0.getPersonId(), VirusStrain.SARS_CoV_2, -1, -1, -1)); + + assertThat(patient0.getNumInfections()).isEqualTo(1); + assertThat(patient0.getNumVaccinations()).isEqualTo(0); + assertThat(patient0.getVirusStrain()).isEqualTo(VirusStrain.SARS_CoV_2); + assertThat(patient0.getDiseaseStatus()).isEqualTo(EpisimPerson.DiseaseStatus.infectedButNotContagious); + assertThat(patient0.daysSinceInfection(0, 5)).isEqualTo(7); + assertTrue(patient0.getInfectionDates().contains(-2 * EpisimUtils.DAY)); + + // infection on day 3 + manager.processEvent(new EpisimInfectionEvent(3 * EpisimUtils.DAY, patient0.getPersonId(), patient0.getPersonId(), null, "undefined", 1, VirusStrain.SARS_CoV_2, 1.0, -1,-1,-1)); + + assertThat(patient0.getNumInfections()).isEqualTo(2); + assertThat(patient0.getNumVaccinations()).isEqualTo(0); + assertThat(patient0.getVirusStrain()).isEqualTo(VirusStrain.SARS_CoV_2); + assertThat(patient0.getDiseaseStatus()).isEqualTo(EpisimPerson.DiseaseStatus.infectedButNotContagious); + assertThat(patient0.daysSinceInfection(1, 5)).isEqualTo(6); + assertThat(patient0.getInfectionDates().size()).isEqualTo(2); + assertTrue(patient0.getInfectionDates().contains(-EpisimUtils.DAY)); + + // vaccination on day 4 + manager.processEvent(new EpisimVaccinationEvent(4 * EpisimUtils.DAY, patient0.getPersonId(), VaccinationType.mRNA,1 )); + + assertThat(patient0.getNumInfections()).isEqualTo(2); + assertThat(patient0.getNumVaccinations()).isEqualTo(1); + assertTrue(patient0.hadVaccinationType(VaccinationType.mRNA)); + assertThat(patient0.getVaccinationStatus()).isEqualTo(EpisimPerson.VaccinationStatus.yes); + assertThat(patient0.daysSince(EpisimPerson.VaccinationStatus.yes, 5)).isEqualTo(5); + assertThat(patient0.getVaccinationDates().size()).isEqualTo(1); + assertTrue(patient0.getVaccinationDates().contains(0)); + + // vaccination on day 5 and infection on day 6; neither should be registered because they occur on or after start date of new simulation + manager.processEvent(new EpisimVaccinationEvent(5 * EpisimUtils.DAY, patient0.getPersonId(), VaccinationType.mRNA,1 )); + manager.processEvent(new EpisimInfectionEvent(6 * EpisimUtils.DAY, patient0.getPersonId(), patient0.getPersonId(), null, "undefined", 1, VirusStrain.SARS_CoV_2, 1.0, -1,-1,-1)); + + assertThat(patient0.getNumInfections()).isEqualTo(2); + assertThat(patient0.getInfectionDates().size()).isEqualTo(2); + assertThat(patient0.getNumVaccinations()).isEqualTo(1); + assertThat(patient0.getVaccinationDates().size()).isEqualTo(1); + + + } +} diff --git a/src/test/java/org/matsim/episim/model/DefaultAntibodyModelTest.java b/src/test/java/org/matsim/episim/model/DefaultAntibodyModelTest.java index b36fbfa32..186883c51 100644 --- a/src/test/java/org/matsim/episim/model/DefaultAntibodyModelTest.java +++ b/src/test/java/org/matsim/episim/model/DefaultAntibodyModelTest.java @@ -10,6 +10,7 @@ import org.apache.log4j.Logger; import org.assertj.core.data.Offset; import org.junit.*; +import org.matsim.episim.EpisimConfigGroup; import org.matsim.episim.EpisimPerson; import org.matsim.episim.EpisimTestUtils; import org.matsim.testcases.MatsimTestUtils; @@ -53,7 +54,7 @@ public class DefaultAntibodyModelTest { public void setup() { antibodyConfig = AntibodyModel.newConfig(); - model = new DefaultAntibodyModel(antibodyConfig); + model = new DefaultAntibodyModel(antibodyConfig,new EpisimConfigGroup()); } @@ -178,10 +179,15 @@ public void testNoImmunityEvents() { @Test public void testMixOfVaccinesAndInfections() { - List immunityEvents = List.of(VirusStrain.SARS_CoV_2, VaccinationType.mRNA, VirusStrain.DELTA); - IntList immunityEventDays = IntList.of(50, 200, 600); + List immunityEvents = List.of(VirusStrain.DELTA, VaccinationType.mRNA, VirusStrain.OMICRON_BA1, VirusStrain.OMICRON_BA2, VirusStrain.OMICRON_BA5,VirusStrain.OMICRON_BA5,VirusStrain.OMICRON_BA5); + IntList immunityEventDays = IntList.of(538, 644,720,736,845,958,979); +// List immunityEvents = List.of(VaccinationType.mRNA); +// IntList immunityEventDays = IntList.of(1); - Int2ObjectMap> antibodyLevels = simulateAntibodyLevels(immunityEvents, immunityEventDays, 750, EpisimTestUtils.createPerson()); + EpisimPerson person = EpisimTestUtils.createPerson(); + person.setImmuneResponseMultiplier(0.1); + + Int2ObjectMap> antibodyLevels = simulateAntibodyLevels(immunityEvents, immunityEventDays, 1000, person); // Plot 1: nAb { @@ -220,7 +226,7 @@ public void testMixOfVaccinesAndInfections() { double nAb = strainToAntibodyMap.getOrDefault(strain, 0.); - var beta = 1.; + var beta = 1.2; var fact = 0.001; double immunityFactor = 1.0 / (1.0 + Math.pow(nAb, beta)); final double probaWVacc = 1 - Math.exp(-fact * immunityFactor); @@ -242,7 +248,7 @@ public void testMixOfVaccinesAndInfections() { * Tests the immuneResponseMultiplier of EpisimPerson; the agent w/ a higher immune response to vaccination/infection * will have a multiplier of 2 while the "normal" agent has a multiplier of 1. * a) 1st immunity event: high-response agent will gain 2x antibodies as regular-response agent - * b) 2nd immunity event: high-response agent will have their antibodies multiplied/refreshed by a factor 2x as high as the regular-response agent + * b) 2nd immunity event: high-response agent will have their antibodies multiplied/refreshed by a factor exactly the same as regular-response agent */ @Test public void testImmunityResponseMultiplier() { @@ -287,19 +293,17 @@ public void testImmunityResponseMultiplier() { } } - // antibody jump after 2nd infection will be 2x higher for high immunity agent. + // antibody jump after 2nd infection will be same for high immunity agent. for (VirusStrain strain : strainsToCheck) { double jumpNormal = antibodyLevelsNormal.get(secondImmunityEvent + 1).getDouble(strain) / antibodyLevelsNormal.get(secondImmunityEvent).getDouble(strain); double jumpHigh = antibodyLevelsHigh.get(secondImmunityEvent + 1).getDouble(strain) / antibodyLevelsHigh.get(secondImmunityEvent).getDouble(strain); - assertThat(jumpHigh).isCloseTo(2 * jumpNormal, OFFSET); + assertThat(jumpHigh).isCloseTo(jumpNormal, OFFSET); } } - - @Test public void immunizationByBa1() { diff --git a/src/test/java/org/matsim/episim/model/DefaultInfectionModelTest.java b/src/test/java/org/matsim/episim/model/DefaultInfectionModelTest.java index 3dbd5d501..090f00216 100644 --- a/src/test/java/org/matsim/episim/model/DefaultInfectionModelTest.java +++ b/src/test/java/org/matsim/episim/model/DefaultInfectionModelTest.java @@ -123,7 +123,7 @@ public void immunityEffectiveness() { EpisimPerson p = EpisimTestUtils.createPerson(true, -1); - p.possibleInfection(new EpisimInfectionEvent(0, p.getPersonId(), p.getPersonId(), null, "somewhere", 1, VirusStrain.SARS_CoV_2, 1d, -1)); + p.possibleInfection(new EpisimInfectionEvent(0, p.getPersonId(), p.getPersonId(), null, "somewhere", 1, VirusStrain.SARS_CoV_2, 1d, -1, -1, -1)); p.checkInfection(); p.setDiseaseStatus(1000, EpisimPerson.DiseaseStatus.recovered); p.setDiseaseStatus(2000, EpisimPerson.DiseaseStatus.susceptible);