Skip to content

Commit

Permalink
Merge branch 'master' of github.com:djgroen/facs
Browse files Browse the repository at this point in the history
  • Loading branch information
djgroen committed Dec 1, 2023
2 parents 21b5f22 + 87b5f32 commit f6fbf2d
Show file tree
Hide file tree
Showing 29 changed files with 1,493 additions and 417 deletions.
3 changes: 3 additions & 0 deletions .coveragerc
@@ -0,0 +1,3 @@
[report]
exclude_lines =
if TYPE_CHECKING:
24 changes: 24 additions & 0 deletions .github/workflows/pytest.yml
@@ -0,0 +1,24 @@
name: Pytest

on: [push]

jobs:
build:
runs-on: ubuntu-latest
strategy:
matrix:
python-version: ["3.10"]
steps:
- uses: actions/checkout@v3
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v3
with:
python-version: ${{ matrix.python-version }}
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install -r requirements_serial.txt
pip install pytest
- name: Analysing the code with pytest
run: |
python -m pytest tests/
2 changes: 1 addition & 1 deletion README.md
@@ -1,6 +1,6 @@
# FACS: Flu And Coronavirus Simulator

[![Build Status](https://travis-ci.com/djgroen/facs.svg?branch=master)](https://travis-ci.com/djgroen/facs)
![Code Tests](https://github.com/djgroen/facs/actions/workflows/pytest.yml/badge.svg)
[![GitHub Issues](https://img.shields.io/github/issues/djgroen/facs.svg)](https://github.com/djgroen/facs/issues)
[![GitHub last-commit](https://img.shields.io/github/last-commit/djgroen/facs.svg)](https://github.com/djgroen/facs/commits/master)

Expand Down
1 change: 0 additions & 1 deletion brent-measures_uk.csv

This file was deleted.

21 changes: 21 additions & 0 deletions covid_data/building_types_map.yml
Expand Up @@ -3,18 +3,27 @@ park:
labels:
- park
default_sqm: 20000
neighbours: 1
fixed: true
weighted: false

hospital:
index: 1
labels:
- hospital
default_sqm: 1600
neighbours: 5
fixed: false
weighted: true

supermarket:
index: 2
labels:
- supermarket
default_sqm: 400
neighbours: 10
fixed: false
weighted: false

office:
index: 3
Expand All @@ -27,6 +36,9 @@ office:
- bank
- service
default_sqm: 1600
neighbours: 50
fixed: true
weighted: false

school:
index: 4
Expand All @@ -35,6 +47,9 @@ school:
- university
- college
default_sqm: 1600
neighbours: 5
fixed: true
weighted: false

leisure:
index: 5
Expand All @@ -54,6 +69,9 @@ leisure:
- library
- community_centre
default_sqm: 400
neighbours: 10
fixed: false
weighted: false

shopping:
index: 6
Expand All @@ -69,3 +87,6 @@ shopping:
- shops
- retail
default_sqm: 200
neighbours: 10
fixed: false
weighted: false
40 changes: 40 additions & 0 deletions covid_data/disease_measles.yml
@@ -0,0 +1,40 @@
name: Measles
infection_rate: 0.1
mortality:
- [4.5, 0.001] # Mortality rate for children under 5 years old
- [14.5, 0.02] # Mortality rate for children aged 5-14 years
- [24.5, 0.1] # Mortality rate for individuals aged 15-24 years
- [34.5, 0.3] # Mortality rate for individuals aged 25-34 years
- [44.5, 0.5] # Mortality rate for individuals aged 35-44 years
- [54.5, 1.0] # Mortality rate for individuals aged 45-54 years
- [64.5, 5.0] # Mortality rate for individuals aged 55-64 years
- [74.5, 10.0] # Mortality rate for individuals aged 65-74 years
- [84.5, 20.0] # Mortality rate for individuals aged 75-84 years
- [999, 50.0] # Mortality rate for individuals aged 85 and older

hospitalised:
- [4.5, 0.01] # Hospitalisation rate for children under 5 years old
- [14.5, 0.02] # Hospitalisation rate for children aged 5-14 years
- [24.5, 0.03] # Hospitalisation rate for individuals aged 15-24 years
- [34.5, 0.04] # Hospitalisation rate for individuals aged 25-34 years
- [44.5, 0.05] # Hospitalisation rate for individuals aged 35-44 years
- [54.5, 0.06] # Hospitalisation rate for individuals aged 45-54 years
- [64.5, 0.08] # Hospitalisation rate for individuals aged 55-64 yearsgit
- [74.5, 0.1] # Hospitalisation rate for individuals aged 65-74 years
- [84.5, 0.15] # Hospitalisation rate for individuals aged 75-84 years
- [999, 0.2] # Hospitalisation rate for individuals aged 85 and older

mortality_period: 7.0 # Measles mortality typically occurs 7 to 10 days after symptom onset due to complications like pneumonia or encephalitis.
recovery_period: 21.0 # Measles recovery lasts about 2 to 3 weeks from symptom onset as individuals gradually recover.
mild_recovery_period: 14.0 # Mild measles cases may have a shorter recovery period, typically 1 to 2 weeks.
incubation_period: 10.0 # Measles' incubation period, from virus exposure to symptom onset, is around 10 to 14 days (range: 7 to 21 days).
period_to_hospitalisation: 7.0 # The time to hospitalisation varies; severe cases may require it within days, while mild cases may not need hospitalisation.
immunity_duration: 9125.0 # Natural immunity after measles recovery is usually lifelong, but waning immunity can occur over decades. MMR vaccination provides long-lasting immunity, often for life.
genotypes:
D4:
infection_rate: 0.12
B3:
infection_rate: 0.15
H1:
infection_rate: 0.19

1 change: 1 addition & 0 deletions covid_data/vaccinations.yml
@@ -1,6 +1,7 @@
date_format: "%d/%m/%Y"
vaccine_effect_time: 21 # time between receiving vaccine and immunity conferred in days.
vaccine_immunity_duration: 287 # average time that vaccines give immunity to persons.
antivax_fraction: 0.05

1/12/2020:
vaccines_per_day: 500 # Total no. of vaccines handed out daily.
Expand Down
75 changes: 75 additions & 0 deletions facs/base/disease.py
@@ -1,7 +1,10 @@
"""Module for Disease class."""

import sys
import warnings
from dataclasses import dataclass, field


from .utils import get_interpolated_lists

AGE_CLASSES = 91
Expand All @@ -24,19 +27,91 @@ class Disease:
hospital: list[float] = field(default_factory=list, init=False, repr=False)
mortality: list[float] = field(default_factory=list, init=False, repr=False)
mutations: dict[str, dict] = field(default_factory=list, init=False, repr=False)
genotypes: dict[str, dict] = field(default_factory=list, init=False, repr=False)

def __post_init__(self):
"""Ensure that all the parameters are positive floats."""

positive_attributes = [
"infection_rate",
"incubation_period",
"mild_recovery_period",
"recovery_period",
"mortality_period",
"period_to_hospitalisation",
"immunity_duration",
]

for attr in positive_attributes:
if not isinstance(getattr(self, attr), float) and not isinstance(
getattr(self, attr), int
):
raise TypeError(f"{attr} must be int or float")

if getattr(self, attr) < 0:
raise ValueError(f"{attr} must not be negative")

if getattr(self, attr) == 0:
warnings.warn(f"{attr} is zero", RuntimeWarning)

def array_sanity_check(self, array: list[list[float]], name: str):
"""Sanity check for Hospitalisation and Mortality parameters."""

for item in array:
if len(item) != 2:
raise ValueError(f"{name} chances must be a list of pairs")

ages = [item[0] for item in array]
probs = [item[1] for item in array]

if ages != sorted(ages):
raise ValueError(f"{name} chances must be sorted by age")

if min(ages) < 0 or max(ages) > AGE_CLASSES - 1:
raise ValueError(f"{name} chances must be between 0 and {AGE_CLASSES - 1}")

if len(ages) != len(set(ages)):
raise ValueError(f"{name} chances must be unique for each age")

if min(probs) < 0 or max(probs) > 1:
raise ValueError(f"{name} chances must be between 0 and 1")

def dict_sanity_check(self, dictionary: dict[str, dict], name: str):
"""Sanity check for mutations and genotypes parameters."""

for key in dictionary:
if not isinstance(dictionary[key], dict):
raise TypeError(f"{name} must be a dictionary of dictionaries")

if not isinstance(key, str):
raise TypeError(f"{name} key must be string")

def add_hospitalisation_chances(self, hosp_array: list[list[float]]):
"""Add age-dependent hospitalisation chances."""

self.array_sanity_check(hosp_array, "Hospitalisation")

self.hospital = get_interpolated_lists(AGE_CLASSES, hosp_array)

def add_mortality_chances(self, mort_array: list[list[float]]):
"""Add age-dependent mortality chances."""

self.array_sanity_check(mort_array, "Mortality")

self.mortality = get_interpolated_lists(AGE_CLASSES, mort_array)

def add_mutations(self, mutations: dict[str, dict]):
"""Add mutations."""

self.dict_sanity_check(mutations, "Mutations")

print("Loading mutations:", mutations, file=sys.stderr)
self.mutations = mutations

def add_genotypes(self, genotypes: dict[str, dict]):
"""Add genotype with its parameters."""

self.dict_sanity_check(genotypes, "Genotypes")

print("Loading genotypes:", genotypes, file=sys.stderr)
self.genotypes = genotypes
7 changes: 4 additions & 3 deletions facs/base/facs.py
Expand Up @@ -337,14 +337,15 @@ def update_nearest_locations(self, dump_and_exit=False):
print(",".join(f"{x}" for x in building_types), file=f)

count = 0
print("Updating nearest locations...", file=sys.stderr)
for h in self.houses:
ni = h.find_nearest_locations(self)
if dump_and_exit == True:
print(",".join(f"{x}" for x in ni), file=f)
count += 1
if count % 1000 == 0:
print(count, "houses scanned.", file=sys.stderr)
print(count, "houses scanned.", file=sys.stderr)
print(f"{count} houses scanned.", file=sys.stderr, end="\r")
print(f"Total {count} houses scanned.", file=sys.stderr)

print(dump_and_exit)

Expand Down Expand Up @@ -478,7 +479,7 @@ def evolve(self, reduce_stochasticity=False):
h = self.houses[i]
for hh in h.households:
for a in hh.agents:
a.plan_visits(self, reduce_stochasticity)
a.plan_visits(self)
a.progress_condition(self, self.time, self.disease)

if (
Expand Down

0 comments on commit f6fbf2d

Please sign in to comment.