Skip to content

Commit

Permalink
Merge branch 'develop' into serialInterval
Browse files Browse the repository at this point in the history
  • Loading branch information
cliffckerr committed Apr 11, 2020
2 parents ad6ad7e + c001993 commit de7315c
Show file tree
Hide file tree
Showing 39 changed files with 960 additions and 182 deletions.
26 changes: 26 additions & 0 deletions .github/workflows/build.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
name: Covasim CI workflow
on:
push:
branches:
- master

jobs:
build_and_push_docker:
runs-on: ubuntu-latest
name: Build and Publish Docker Image
steps:
- name: Checkout sources
uses: actions/checkout@v1

- name: Set lowercase repo env variable
shell: python
run: print("::set-env name=LOWER_REPO::{}".format('${{github.repository}}'.lower()))
- name: Build Docker
run: |
docker build -f docker/Dockerfile -t docker.pkg.github.com/${LOWER_REPO}/covasim:latest .
- name: Push the image to GPR
run: |
docker login docker.pkg.github.com -u publisher -p "${GITHUB_PACKAGE_REGISTRY_TOKEN}"
docker push docker.pkg.github.com/${LOWER_REPO}/covasim:latest
env:
GITHUB_PACKAGE_REGISTRY_TOKEN: ${{ secrets.GITHUB_TOKEN }}
2 changes: 1 addition & 1 deletion .github/workflows/ubuntu.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -44,4 +44,4 @@ jobs:
run: pytest test*.py --durations=0 # Run actual tests
- name: Run other examples
working-directory: ./
run: pytest ./examples/*.py ./covasim/cruise_ship/test*.py
run: pytest ./examples/*.py ./covasim/cruise_ship/test*.py
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ covasim/cova_seattle/capacity_analysis
covasim/cova_seattle/school_analysis
covasim/cova_seattle/seattle_analysis
results/

data/epi_data

# Byte-compiled / optimized / DLL files
__pycache__/
Expand Down
38 changes: 31 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,24 @@ Covasim is a stochastic agent-based simulator designed to be used for COVID-19
indicators such as numbers of infections and peak hospital demand. Covasim can
also be used to explore the potential impact of different interventions.

<!--ts-->
* [Requirements](#Requirements)
* [Quick start guide](#quick-start-guide)
* [Detailed installation instructions](#detailed-installation-instructions)
* [Detailed usage](#detailed-usage)
* [Module structure](#module-structure)
* [cruise_ship](#cruise_ship)
* [webapp](#webapp)
* [Other folders](#other-folders)
* [bin](#bin)
* [docker](#docker)
* [examples](#examples)
* [licenses](#licenses)
* [tests](#tests)
* [sweep](#sweep)
* [Disclaimer](#disclaimer)
<!--te-->


## Requirements

Expand Down Expand Up @@ -70,7 +88,7 @@ follows:
This shows a more complex example, including running an intervention scenario, plotting uncertainty, and performing a health systems analysis.


## Structure
## Module structure

All core model code is located in the `covasim` subfolder; standard usage is
`import covasim as cv`. The other subfolders, `cruise_ship` and `webapp`, are
Expand All @@ -80,9 +98,6 @@ The model consists of two core classes: the `Person` class (which contains
information on health state), and the `Sim` class (which contains methods for
running, calculating results, plotting, etc.).


### covasim

The structure of the `covasim` folder is as follows:

* `base.py`: The `ParsObj` class, plus basic methods of the `BaseSim` class, and associated functions.
Expand All @@ -107,14 +122,14 @@ slight variations to the model (`model.py`).

### webapp

For running the interactive web application: please see the `README.md` in that
folder for more information.
For running the interactive web application. See the [webapp readme](./covasim/webapp) for more information.


## Other folders

Please see the readme in each subfolder for more information.


### bin

This folder contains a command-line interface (CLI) version of Covasim; example usage:
Expand All @@ -126,7 +141,12 @@ covasim --pars "{pop_size:20000, pop_infected:1, n_days:360, rand_seed:1}"
Note: the CLI is currently not compatible with Windows. You will need to add
this folder to your path to run from other folders.

### docker
### data

Scripts to automatically scrape data (including demographics and COVID epidemiology data),
and the data files themselves (which are not part of the repository).

### docker

This folder contains the `Dockerfile` and other files that allow Covasim to be
run as a webapp via Docker.
Expand All @@ -143,6 +163,10 @@ Licensing information and legal notices.

Integration, development, and unit tests.

### sweep

Utilites for hyperparameter sweeps, using [Weights and Biases](https://www.wandb.com/). See the [sweep readme](./sweep) for more information.


## Disclaimer

Expand Down
3 changes: 2 additions & 1 deletion covasim/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@ This file describes the expected behavior of each parameter in the model. Note:
* `pop_scale` = Multiplicative scale for results. Test: run 2 sims, set to 10, `sim.results['cum_exposed']` in 2nd sim should be 10x higher than first.
* `pop_size` = Nmber of people in the simulation. Test: `len(sim.people)` should equal this number.
* `pop_infected` = Initial number of people infected. Test: if 0, there should be no infections; if equals `n`, should be no _new_ infections.
* `pop_type` = Whether or not to use the `synthpops` library for contact matrices. Consult that library's documentation for tests.
* `pop_type` = The type of population structure to use with the model; default `'random'`, using Seattle demographics; other options are `'hybrid'` (structured households and random school, work, and community contacts), `'clustered'` (all clustered contacts), and `'synthpops'` (requires the `synthpops` library, but uses data-based population structure)
* `location` = Which country or state to load demographic data from (optional)

## Simulation parameters
* `start_day` = The calendar date of the start day of the simulation.
Expand Down
1 change: 1 addition & 0 deletions covasim/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
from .version import __version__, __versiondate__, __license__
print(__license__)


#%% Check that requirements are met
from . import requirements

Expand Down
20 changes: 14 additions & 6 deletions covasim/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -146,20 +146,25 @@ def set_seed(self, seed=-1):
def n(self):
''' Count the number of people -- if it fails, assume none '''
try: # By default, the length of the people dict
output = len(self.people)
return len(self.people)
except: # If it's None or missing
output = 0
return output
return 0

@property
def npts(self):
''' Count the number of time points '''
return int(self['n_days'] + 1)
try:
return int(self['n_days'] + 1)
except:
return 0

@property
def tvec(self):
''' Create a time vector '''
return np.arange(self.npts)
try:
return np.arange(self.npts)
except:
return np.arange([])

@property
def datevec(self):
Expand All @@ -171,7 +176,10 @@ def datevec(self):
simulation time step
"""
return self['start_day'] + self.tvec * dt.timedelta(days=1)
try:
return self['start_day'] + self.tvec * dt.timedelta(days=1)
except:
return []


def inds2dates(self, inds, dateformat=None):
Expand Down
1 change: 1 addition & 0 deletions covasim/data/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
from .loaders import *
8 changes: 8 additions & 0 deletions covasim/data/country_age_distributions.py

Large diffs are not rendered by default.

93 changes: 93 additions & 0 deletions covasim/data/loaders.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
'''
Load data
'''

#%% Housekeeping
import numpy as np
import sciris as sc
from . import country_age_distributions as cad

__all__ = ['get_age_distribution']


def get_age_distribution(location=None):
'''
Load age distribution for a given country or countries.
Args:
location (str or list): name of the country or countries to load the age distribution for
Returns:
age_data (array): Numpy array of age distributions
'''

# Load the raw data
json = cad.get_country_age_distributions()
countries = [entry["country"].lower() for entry in json] # Pull out available countries

# Set parameters
max_age = 99
if location is None:
location = countries
else:
location = sc.promotetolist(location)

# Define a mapping for common mistakes
mapping = {
'Bolivia': 'Bolivia (Plurinational State of)',
'Burkina': 'Burkina Faso',
'Cape Verde': 'Cabo Verdeo',
'Hong Kong': 'China, Hong Kong Special Administrative Region',
'Macao': 'China, Macao Special Administrative Region',
"Cote d'Ivore": 'Côte d’Ivoire',
'DRC': 'Democratic Republic of the Congo',
'Iran': 'Iran (Islamic Republic of)',
'Laos': "Lao People's Democratic Republic",
'Micronesia': 'Micronesia (Federated States of)',
'Korea': 'Republic of Korea',
'South Korea': 'Republic of Korea',
'Moldova': 'Republic of Moldova',
'Russia': 'Russian Federation',
'Palestine': 'State of Palestine',
'Syria': 'Syrian Arab Republic',
'Taiwan': 'Taiwan Province of China',
'Macedonia': 'The former Yugoslav Republic of Macedonia',
'UK': 'United Kingdom of Great Britain and Northern Ireland',
'United Kingdom': 'United Kingdom of Great Britain and Northern Ireland',
'Tanzania': 'United Republic of Tanzania',
'USA': 'United States of America',
'United States': 'United States of America',
'Venezuela': 'Venezuela (Bolivarian Republic of)',
'Vietnam': 'Viet Nam',
}
mapping = {key.lower():val.lower() for key,val in mapping.items()} # Convert to lowercase

result = {}
for loc in location:
loc = loc.lower()
if loc in mapping:
loc = mapping[loc]
try:
ind = countries.index(loc.lower())
entry = json[ind]
except ValueError:
suggestions = sc.suggest(loc, countries, n=4)
errormsg = f'Location "{loc}" not recognized, did you mean {suggestions}?'
raise ValueError(errormsg)
age_distribution = entry["ageDistribution"]
total_pop = sum(age_distribution.values())
local_pop = []

for age, age_pop in age_distribution.items():
if age[-1] == '+':
val = [int(age[:-1]), max_age, age_pop/total_pop]
else:
ages = age.split('-')
val = [int(ages[0]), int(ages[1]), age_pop/total_pop]
local_pop.append(val)
result[loc] = np.array(local_pop)

if len(location) == 1:
result = result[loc]

return result
2 changes: 1 addition & 1 deletion covasim/defaults.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
'severe': 'Number of severe cases',
'critical': 'Number of critical cases',
'diagnosed': 'Number of confirmed cases',
'quarantined': 'Number in quarantined',
'quarantined': 'Number in quarantine',
}

# The types of result that are counted as flows -- used in sim.py; value is the label suffix
Expand Down
3 changes: 2 additions & 1 deletion covasim/parameters.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,8 @@ def make_pars(set_prognoses=False, prog_by_age=True, use_layers=False, **kwargs)
# Population parameters
pars['pop_size'] = 20e3 # Number ultimately susceptible to CoV
pars['pop_infected'] = 10 # Number of initial infections
pars['pop_type'] = 'random' # What type of population data to use -- random (fastest), synthpops (best), realistic (compromise), or clustered (not recommended)
pars['pop_type'] = 'random' # What type of population data to use -- random (fastest), synthpops (best), hybrid (compromise), or clustered (not recommended)
pars['location'] = None # What location to load data from -- default Seattle

# Simulation parameters
pars['start_day'] = '2020-03-01' # Start day of the simulation
Expand Down
Loading

0 comments on commit de7315c

Please sign in to comment.