Skip to content

Commit

Permalink
Merge pull request #1396 from amath-idm/rc3.1.4
Browse files Browse the repository at this point in the history
Version 3.1.4
  • Loading branch information
cliffckerr authored Oct 22, 2022
2 parents a8254e0 + 5e9fef7 commit a3db333
Show file tree
Hide file tree
Showing 45 changed files with 1,433 additions and 619 deletions.
10 changes: 6 additions & 4 deletions .github/workflows/tests.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,9 @@ jobs:
run: pip install -r requirements_test.txt
- name: Run integration tests
working-directory: ./tests
run: pytest -v test_*.py --workers auto --durations=0
- name: Run unit tests
working-directory: ./tests/unittests
run: pytest -v test_*.py --workers auto --durations=0
run: pytest -v test_*.py unittests/test_*.py --workers auto --durations=0 --junitxml=test-results.xml
- name: Publish test Results
uses: EnricoMi/publish-unit-test-result-action@v2
if: always()
with:
junit_files: tests/test-results.xml
26 changes: 26 additions & 0 deletions CHANGELOG.rst
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,32 @@ Latest versions (3.1.x)
~~~~~~~~~~~~~~~~~~~~~~~


Version 3.1.4 (2022-10-07)
--------------------------

Improvements
^^^^^^^^^^^^
- Added a new ``isolated`` state to explicitly track people who are in isolation (like quarantine).
- Switched default parallelizer from ``multiprocess`` to ``concurrent.futures``; the latter is faster but less robust, so will automatically try again using ``multiprocess`` if ``concurrent.futures`` fails.
- Added ``cv.Fit.summarize()`` to quickly get information about the mismatches.

Bugfixes
^^^^^^^^
- Fixed plotting bug that prevented ``MultiSim`` plots from appearing correctly in Jupyter notebooks with (ironically) ``cv.options(jupyter=True)``.
- Fixed bug with Matplotlib options (e.g. ``cv.options(dpi=150)``) not being set properly.
- ``cv.Calibration.remove_db()`` now deletes the Optuna study as well.
- Fixed several issues with ``cv.git_info()`` pulling information from the wrong frame (i.e. file location).
- Fixed shrunken sims dropping interventions/analyzers that were functions rather than classes.

Other
^^^^^
- Updated Sciris dependency to 2.0.1.
- Updated license to MIT instead of Creative Commons.
- Added ``papers.rst`` and ``papers.bib`` that list additional Covasim publications.
- Added a style guide and draft code linting scripts.
- *GitHub info*: PR `1396 <https://github.com/amath-idm/covasim/pull/1396>`_


Version 3.1.3 (2022-07-19)
--------------------------
- Fixed a bug with using ``'seir'`` as a default plot option. (Thanks Rik Belew for finding and fixing.)
Expand Down
377 changes: 21 additions & 356 deletions LICENSE

Large diffs are not rendered by default.

30 changes: 5 additions & 25 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -34,29 +34,17 @@ GitHub_ page. Full information about Covasim is provided in the documentation_.
Background
==========

Covasim has been used for analyses in over a dozen countries, both to inform policy decisions (including in the US, UK, and Australia), and as part of research studies. Academic papers that have been written using Covasim include:
Covasim has been used for analyses in over a dozen countries, both to inform policy decisions (including in the US, UK, and Australia), and as part of research studies. Some key papers that have been written using Covasim include:

1. **Controlling COVID-19 via test-trace-quarantine**. Kerr CC, Mistry D, Stuart RM, Rosenfeld R, Hart G, Núñez RC, Selvaraj P, Cohen JA, Abeysuriya RG, George L, Hagedorn B, Jastrzębski M, Fagalde M, Duchin J, Famulare M, Klein DJ (2021). *Nature Communications* 12:2993. doi: https://doi.org/10.1038/s41467-021-23276-9.

2. **Determining the optimal strategy for reopening schools, the impact of test and trace interventions, and the risk of occurrence of a second COVID-19 epidemic wave in the UK: a modelling study**. Panovska-Griffiths J, Kerr CC, Stuart RM, Mistry D, Klein DJ, Viner R, Bonnell C (2020-08-03). *Lancet Child and Adolescent Health* S2352-4642(20) 30250-9. doi: https://doi.org/10.1016/S2352-4642(20)30250-9.

3. **Estimating and mitigating the risk of COVID-19 epidemic rebound associated with reopening of international borders in Vietnam: a modelling study**. Pham QD, Stuart RM, Nguyen TV, Luong QC, Tran DQ, Phan LT, Dang TQ, Tran DN, Mistry D, Klein DJ, Abeysuriya RG, Oron AP, Kerr CC (2021-04-12). *Lancet Global Health* S2214-109X(21) 00103-0; doi: https://doi.org/10.1016/S2214-109X(21)00103-0.

4. **Modelling the impact of reducing control measures on the COVID-19 pandemic in a low transmission setting**. Scott N, Palmer A, Delport D, Abeysuriya RG, Stuart RM, Kerr CC, Mistry D, Klein DJ, Sacks-Davis R, Heath K, Hainsworth S, Pedrana A, Stoove M, Wilson DP, Hellard M (in press; accepted 2020-09-02). *Medical Journal of Australia* [`Preprint <https://www.mja.com.au/journal/2020/modelling-impact-reducing-control-measures-covid-19-pandemic-low-transmission-setting>`__]; doi: https://doi.org/10.1101/2020.06.11.20127027.
A more complete list of papers is given in ``papers.rst``.

5. **The role of masks, testing and contact tracing in preventing COVID-19 resurgences: a case study from New South Wales, Australia**. Stuart RM, Abeysuriya RG, Kerr CC, Mistry D, Klein DJ, Gray R, Hellard M, Scott N (in press; accepted 2021-03-19). *BMJ Open*; doi: https://doi.org/10.1101/2020.09.02.20186742.

6. **The potential contribution of face coverings to the control of SARS-CoV-2 transmission in schools and broader society in the UK: a modelling study**. Panovska-Griffiths J, Kerr CC, Waites W, Stuart RM, Mistry D, Foster D, Klein DJ, Viner R, Bonnell C (in press; accepted 2021-04-08). *Nature Scientific Reports*; doi: https://doi.org/10.1101/2020.09.28.20202937.

7. **Schools are not islands: Balancing COVID-19 risk and educational benefits using structural and temporal countermeasures**. Cohen JA, Mistry D, Kerr CC, Klein DJ (under review; posted 2020-09-10). *medRxiv* 2020.09.08.20190942; doi: https://doi.org/10.1101/2020.09.08.20190942.

8. **COVID-19 reopening strategies at the county level in the face of uncertainty: Multiple Models for Outbreak Decision Support**. Shea K, Borchering RK, Probert WJM, et al. (under review; posted 2020-11-05). *medRxiv* 2020.11.03.20225409; doi: https://doi.org/10.1101/2020.11.03.20225409.

9. **Preventing a cluster from becoming a new wave in settings with zero community COVID-19 cases**. Abeysuriya RG, Delport D, Stuart RM, Sacks-Davis R, Kerr CC, Mistry D, Klein DJ, Hellard M, Scott N (under review; posted 2020-12-22). *medRxiv* 2020.12.21.20248595; doi: https://doi.org/10.1101/2020.12.21.20248595.

10. **Modelling the impact of reopening schools in early 2021 in the presence of the new SARS-CoV-2 variant in the UK**. Panovska-Griffiths J, Kerr CC, Waites W, Stuart RM, Mistry D, Foster D, Klein DJ, Viner R, Bonnell C (under review; posted 2021-02-09). *medRxiv* 2021.02.07.21251287; doi: https://doi.org/10.1101/2021.02.07.21251287.

If you have written a paper or report using Covasim, we'd love to know about it! Please write to us `here <mailto:[email protected]>`__.
If you've written a paper or report using Covasim, we'd love to know about it! Please write to us `here <mailto:[email protected]>`__.


Requirements
Expand Down Expand Up @@ -203,21 +191,13 @@ Examples of how to calibrate simulations, including `Optuna`_ (also covered in t
.. _Weights and Biases: https://www.wandb.com/


Licenses
--------

Licensing information and legal notices.


Tests
-----

Integration, development, and unit tests. While not (yet) beautifully curated, these folders contain many usage examples. See the `tests README`_ for more information.

.. _tests README: ./tests
Integration, development, and unit tests. While not (yet) beautifully curated, these folders contain many usage examples. See README in the tests folder for more information.


Disclaimer
==========

The code in this repository was developed by IDM to support our research in disease transmission and managing epidemics. We’ve made it publicly available under the Creative Commons Attribution-ShareAlike 4.0 International License to provide others with a better understanding of our research and an opportunity to build upon it for their own work. We make no representations that the code works as intended or that we will provide support, address issues that are found, or accept pull requests. You are welcome to create your own fork and modify the code to suit your own modeling needs as contemplated under the Creative Commons Attribution-ShareAlike 4.0 International License. See the contributing and code of conduct READMEs for more information.
The code in this repository was developed by IDM, the Burnet Institute, the University of Copenhagen, and other collaborators to support our joint research on COVID. We’ve made it publicly available under the MIT License to provide others with a better understanding of our research and an opportunity to build upon it for their own work. Note that Covasim depends on a number of user-installed Python packages that can be installed automatically via ``pip install``. We make no representations that the code works as intended or that we will provide support, address issues that are found, or accept pull requests. You are welcome to create your own fork and modify the code to suit your own modeling needs as contemplated under the MIT License. See the contributing and code of conduct READMEs for more information.
File renamed without changes.
File renamed without changes.
44 changes: 33 additions & 11 deletions covasim/analysis.py
Original file line number Diff line number Diff line change
Expand Up @@ -173,7 +173,7 @@ class snapshot(Analyzer):

def __init__(self, days, *args, die=True, **kwargs):
super().__init__(**kwargs) # Initialize the Analyzer object
days = sc.promotetolist(days) # Combine multiple days
days = sc.tolist(days) # Combine multiple days
days.extend(args) # Include additional arguments, if present
self.days = days # Converted to integer representations
self.die = die # Whether or not to raise an exception
Expand Down Expand Up @@ -297,7 +297,7 @@ def initialize(self, sim):
# Handle states
if self.states is None:
self.states = ['exposed', 'severe', 'dead', 'tested', 'diagnosed']
self.states = sc.promotetolist(self.states)
self.states = sc.tolist(self.states)
for s,state in enumerate(self.states):
self.states[s] = state.replace('date_', '') # Allow keys starting with date_ as input, but strip it off here

Expand Down Expand Up @@ -1222,6 +1222,18 @@ def compute_mismatch(self, use_median=False):
return self.mismatch


def summarize(self):
''' Print out results from the fit '''
if self.mismatch is not None:
print('Mismatch values for:')
print(self.mismatches)
print('\nTotal mismatch value:')
print(self.mismatch)
else:
print('Mismatch values not yet calculated; please run sim.compute_fit().')
return


def plot(self, keys=None, width=0.8, fig_args=None, axis_args=None, plot_args=None,
date_args=None, do_show=None, fig=None, **kwargs):
'''
Expand Down Expand Up @@ -1332,6 +1344,7 @@ def plot(self, keys=None, width=0.8, fig_args=None, axis_args=None, plot_args=No

return cvpl.handle_show_return(fig=fig, do_show=do_show)


def import_optuna():
''' A helper function to import Optuna, which is an optional dependency '''
try:
Expand All @@ -1341,6 +1354,7 @@ def import_optuna():
raise ModuleNotFoundError(errormsg)
return op


class Calibration(Analyzer):
'''
A class to handle calibration of Covasim simulations. Uses the Optuna hyperparameter
Expand All @@ -1356,7 +1370,7 @@ class Calibration(Analyzer):
sim (Sim) : the simulation to calibrate
calib_pars (dict) : a dictionary of the parameters to calibrate of the format dict(key1=[best, low, high])
fit_args (dict) : a dictionary of options that are passed to sim.compute_fit() to calculate the goodness-of-fit
par_samplers (dict) : an optional mapping from parameters to the Optuna sampler to use for choosing new points for each; by default, suggest_uniform
par_samplers (dict) : an optional mapping from parameters to the Optuna sampler to use for choosing new points for each; by default, suggest_float
custom_fn (func) : a custom function for modifying the simulation; receives the sim and calib_pars as inputs, should return the modified sim
n_trials (int) : the number of trials per worker
n_workers (int) : the number of parallel workers (default: maximum
Expand Down Expand Up @@ -1398,7 +1412,7 @@ def __init__(self, sim, calib_pars=None, fit_args=None, custom_fn=None, par_samp
if db_name is None: db_name = f'{name}.db'
if keep_db is None: keep_db = False
if storage is None: storage = f'sqlite:///{db_name}'
if total_trials is not None: n_trials = total_trials/n_workers
if total_trials is not None: n_trials = np.ceil(total_trials/n_workers)
self.run_args = sc.objdict(n_trials=int(n_trials), n_workers=int(n_workers), name=name, db_name=db_name, keep_db=keep_db, storage=storage)

# Handle other inputs
Expand Down Expand Up @@ -1462,7 +1476,7 @@ def run_trial(self, trial):
errormsg = 'The requested sampler function is not found: ensure it is a valid attribute of an Optuna Trial object'
raise AttributeError(errormsg) from E
else:
sampler_fn = trial.suggest_uniform
sampler_fn = trial.suggest_float
pars[key] = sampler_fn(key, low, high) # Sample from values within this range
mismatch = self.run_sim(pars)
return mismatch
Expand All @@ -1487,20 +1501,28 @@ def run_workers(self):
else: # Special case: just run one
output = [self.worker()]
return output


def remove_db(self):
'''
Remove the database file if keep_db is false and the path exists.
New in version 3.1.0.
'''
path = self.run_args.db_name
if os.path.exists(path):
os.remove(path)
try:
op = import_optuna()
op.delete_study(study_name=self.run_args.name, storage=self.run_args.storage)
if self.verbose:
print(f'Deleted study {self.run_args.name} in {self.run_args.storage}')
except Exception as E:
print('Could not delete study, skipping...')
print(str(E))
if os.path.exists(self.run_args.db_name):
os.remove(self.run_args.db_name)
if self.verbose:
print(f'Removed existing calibration: {path}')
print(f'Removed existing calibration {self.run_args.db_name}')
return



def make_study(self):
Expand Down
26 changes: 16 additions & 10 deletions covasim/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -192,11 +192,11 @@ def npts(self):
return len(self.values)


def set_metadata(obj, **kwargs):
def set_metadata(obj, frame=4, **kwargs):
''' Set standard metadata for an object '''
obj.created = kwargs.get('created', sc.now())
obj.version = kwargs.get('version', cvv.__version__)
obj.git_info = kwargs.get('git_info', cvm.git_info())
obj.git_info = kwargs.get('git_info', cvm.git_info(frame=frame)) # 4 = 2 (default) + base + caller
return


Expand Down Expand Up @@ -286,7 +286,7 @@ def update_pars(self, pars=None, create=False, **kwargs):

def set_metadata(self, simfile):
''' Set the metadata for the simulation -- creation time and filename '''
set_metadata(self)
set_metadata(self, frame=5)
if simfile is None:
self.simfile = 'covasim.sim'
return
Expand Down Expand Up @@ -397,7 +397,7 @@ def date(self, ind, *args, dateformat=None, as_date=False):

# Handle inputs
if not isinstance(ind, list): # If it's a number, string, or dateobj, convert it to a list
ind = sc.promotetolist(ind)
ind = sc.tolist(ind)
ind.extend(args)
if dateformat is None:
dateformat = '%Y-%m-%d'
Expand Down Expand Up @@ -547,7 +547,7 @@ def to_json(self, filename=None, keys=None, tostring=False, indent=2, verbose=Fa
# Handle keys
if keys is None:
keys = ['results', 'pars', 'summary']
keys = sc.promotetolist(keys)
keys = sc.tolist(keys)

# Convert to JSON-compatible format
d = {}
Expand Down Expand Up @@ -662,8 +662,14 @@ def shrink(self, skip_attrs=None, in_place=True):

# Shrink interventions and analyzers, with a lot of checking along the way
for key in ['interventions', 'analyzers']:
ias = self.pars[key] # List of interventions or analyzers
shrunken_ias = [ia.shrink(in_place=in_place) for ia in ias if isinstance(ia, (cvi.Intervention, cva.Analyzer))]
ias = sc.tolist(self.pars[key]) # List of interventions or analyzers
shrunken_ias = []
for ia in ias:
if isinstance(ia, (cvi.Intervention, cva.Analyzer)):
shrunken_ia = ia.shrink(in_place=in_place)
else:
shrunken_ia = ia
shrunken_ias.append(shrunken_ia)
self.pars[key] = shrunken_ias # Actually shrink, and re-store

# Don't return if in place
Expand Down Expand Up @@ -760,7 +766,7 @@ def _get_ia(self, which, label=None, partial=False, as_list=False, as_inds=False
label = np.arange(n_ia)
if isinstance(label, np.ndarray): # Allow arrays to be provided
label = label.tolist()
labels = sc.promotetolist(label)
labels = sc.tolist(label)

# Calculate the matches
matches = []
Expand Down Expand Up @@ -979,7 +985,7 @@ def _resize_arrays(self, new_size=None, keys=None):
# Reset sizes
if keys is None:
keys = self.keys()
keys = sc.promotetolist(keys)
keys = sc.tolist(keys)
for key in keys:
self[key].resize(new_size, refcheck=False) # Don't worry about cross-references to the arrays

Expand Down Expand Up @@ -1827,7 +1833,7 @@ def find_contacts(self, inds, as_array=True):

# Check types
if not isinstance(inds, np.ndarray):
inds = sc.promotetoarray(inds)
inds = sc.toarray(inds)
if inds.dtype != np.int64: # pragma: no cover # This is int64 since indices often come from cv.true(), which returns int64
inds = np.array(inds, dtype=np.int64)

Expand Down
5 changes: 5 additions & 0 deletions covasim/defaults.py
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ def __init__(self):
'dead',
'known_contact',
'quarantined',
'isolated',
'vaccinated',
]

Expand Down Expand Up @@ -110,6 +111,7 @@ def __init__(self):
self.dates = [f'date_{state}' for state in self.states] # Convert each state into a date
self.dates.append('date_pos_test') # Store the date when a person tested which will come back positive
self.dates.append('date_end_quarantine') # Store the date when a person comes out of quarantine
self.dates.append('date_end_isolation') # Store the date when a person comes out of isolation

# Duration of different states: these are floats per person -- used in people.py
self.durs = [
Expand Down Expand Up @@ -152,6 +154,7 @@ def __init__(self):
'diagnosed': 'Number of confirmed cases',
'known_dead': 'Number of confirmed deaths',
'quarantined': 'Number in quarantine',
'isolated': 'Number in isolation',
'vaccinated': 'Number of people vaccinated',
}

Expand All @@ -174,6 +177,7 @@ def __init__(self):
'diagnoses': 'diagnoses',
'known_deaths': 'known deaths',
'quarantined': 'quarantined people',
'isolated': 'isolated people',
'doses': 'vaccine doses',
'vaccinated': 'vaccinated people'
}
Expand Down Expand Up @@ -258,6 +262,7 @@ def get_default_colors():
c.diagnoses = '#5f5cd2'
c.diagnosed = c.diagnoses
c.quarantined = '#5c399c'
c.isolated = '#9756ff'
c.doses = c.quarantined # TODO: new color
c.vaccinated = c.quarantined
c.recoveries = '#9e1149'
Expand Down
Loading

0 comments on commit a3db333

Please sign in to comment.