def cache_populations(seed=0, popfile=None): ''' Pre-generate the synthpops population ''' pars = sc.objdict( pop_size=pop_size, pop_type='synthpops', rand_seed=seed, ) if popfile is None: popfile = f'{popfile_stem}{pars.rand_seed}.ppl' T = sc.tic() print(f'Making "{popfile}"...') sim = cv.Sim(pars) cv.make_people(sim, popfile=popfile, save_pop=True, with_facilities=True, generate=True, layer_mapping={'LTCF': 'l'}) sc.toc(T) print('Done') return
def test_distances(doplot=False): npoints = 1000 nsamples = 2 npars = 2 test = pl.rand(nsamples, npars) train = pl.rand(npoints, npars) t1 = sc.tic() distances = pe.scaled_norm(test=test, train=train) t2 = sc.toc(t1, output=True) timestr = f'time = {t2*1e3:0.2f} ms' print(timestr) # Test a shape mismatch with pytest.raises(ValueError): pe.scaled_norm(test=pl.rand(7), train=pl.rand(7,4)) # Should be 4, not 7 if doplot: x_ind = 0 y_ind = 1 offset = 0.009 pl.figure(figsize=eqfigsize) sc.parulacolormap(apply=True) # or pl.set_map('parula') for pt in range(2): markers = ['<','>'] markersize = 50 bigmarker = 200 pl.scatter(train[:,x_ind]+offset*pt, train[:,y_ind], s=markersize, c=distances[pt], marker=markers[pt], label=f'Samples {pt+1}') pl.scatter(test[pt][0], test[pt][1], s=bigmarker, c='k', marker=markers[pt], label=f'Origin {pt+1}') pl.xlabel('Parameter 1') pl.ylabel('Parameter 2') pl.title(f'Distance calculations (color ∝ distance); {timestr}') pl.legend() pl.colorbar() pl.axis('square') return distances
def test_borderclosure(do_plot=False, do_show=True, do_save=False, fig_path=None): sc.heading('Test effect of border closures') sc.heading('Setting up...') sc.tic() n_runs = 2 verbose = 1 basepars = {'pop_size': 1000} basepars = {'n_imports': 5} metapars = {'n_runs': n_runs} sim = cv.Sim() # Define the scenarios scenarios = { 'baseline': { 'name': 'No border closures', 'pars': {} }, 'borderclosures_day10': { 'name': 'Close borders on day 10', 'pars': { 'interventions': [cv.dynamic_pars({'n_imports': { 'days': 10, 'vals': 0 }})] } }, } scens = cv.Scenarios(sim=sim, basepars=basepars, metapars=metapars, scenarios=scenarios) scens.run(verbose=verbose, debug=debug) if do_plot: scens.plot(do_save=do_save, do_show=do_show, fig_path=fig_path) return scens
def run(self, do_plot=False, verbose=None, **kwargs): ''' Run the simulation. Args: do_plot (bool): whether to plot verbose (int): level of detail to print kwargs (dict): passed to self.plot() Returns: results: the results object (also modifies in-place) ''' # Initialize T = sc.tic() if not self.initialized: self.initialize() if verbose is None: verbose = self['verbose'] # Main simulation loop for t in self.tvec: # Print progress if verbose >= 1: elapsed = sc.toc(output=True) simlabel = f'"{self.label}": ' if self.label else '' string = f' Running {simlabel}day {t:2.0f}/{self.pars["n_days"]} ({elapsed:0.2f} s) ' if verbose >= 2: sc.heading(string) elif verbose == 1: cvm.progressbar(t + 1, self.npts, label=string, newline=True) # Do the heavy lifting -- actually run the model! self.step() # Check if we were asked to stop elapsed = sc.toc(T, output=True) if elapsed > self['timelimit']: sc.printv(f"Time limit ({self['timelimit']} s) exceeded", 1, verbose) break elif self['stopping_func'] and self['stopping_func'](self): sc.printv("Stopping function terminated the simulation", 1, verbose) break # End of time loop; compute cumulative results outside of the time loop self.finalize(verbose=verbose) # Finalize the results sc.printv(f'Run finished after {elapsed:0.2f} s.\n', 1, verbose) self.summary = self.summary_stats(verbose=verbose) if do_plot: # Optionally plot self.plot(**kwargs) return self.results
def test_benchmark(do_save=do_save): ''' Compare benchmark performance ''' print('Running benchmark...') previous = sc.loadjson(benchmark_filename) # Create the sim sim = cv.Sim(verbose=0) # Time initialization t0 = sc.tic() sim.initialize() t_init = sc.toc(t0, output=True) # Time running t0 = sc.tic() sim.run() t_run = sc.toc(t0, output=True) # Construct json n_decimals = 3 json = { 'time': { 'initialize': round(t_init, n_decimals), 'run': round(t_run, n_decimals), }, 'parameters': { 'pop_size': sim['pop_size'], 'pop_type': sim['pop_type'], 'n_days': sim['n_days'], }, } print('Previous benchmark:') sc.pp(previous) print('\nNew benchmark:') sc.pp(json) if do_save: sc.savejson(filename=benchmark_filename, obj=json, indent=2) print('Done.') return json
def test_turnaround(do_plot=False, do_show=True, do_save=False, fig_path=None): sc.heading('Test impact of reducing delay time for getting test results') sc.heading('Setting up...') sc.tic() n_runs = 3 verbose = 1 base_pars = { 'pop_size': 1000, 'pop_type': 'hybrid', } base_sim = cv.Sim(base_pars) # create sim object n_people = base_sim['pop_size'] npts = base_sim.npts # Define overall testing assumptions testing_prop = 0.1 # Assumes we could test 10% of the population daily (!!) daily_tests = [testing_prop*n_people]*npts # Number of daily tests # Define the scenarios scenarios = { f'{d}dayturnaround': { 'name':f'Symptomatic testing with {d} days to get results', 'pars': { 'interventions': cv.test_num(daily_tests=daily_tests, test_delay=d) } } for d in range(1, 3+1, 2) } metapars = {'n_runs': n_runs} scens = cv.Scenarios(sim=base_sim, metapars=metapars, scenarios=scenarios) scens.run(verbose=verbose, debug=debug) to_plot = ['cum_infections', 'n_infectious', 'new_tests', 'new_diagnoses'] fig_args = dict(figsize=(20, 24)) if do_plot: scens.plot(do_save=do_save, do_show=do_show, fig_path=fig_path, interval=7, fig_args=fig_args, to_plot=to_plot) return scens
def make_app(**kwargs): T = sc.tic() app = sw.ScirisApp( name='HealthPrior', filepath=__file__, config=hp.webapp.config, RPC_dict=hp.webapp.rpcs.RPC_dict ) # Create the ScirisApp object. NOTE: app.config will thereafter contain all of the configuration parameters, including for Flask. sw.make_default_users(app) print('>> Webapp initialization complete (elapsed time: %0.2f s)' % sc.toc(T, output=True)) return app
def test_parallelization(doplot=False): sc.heading('Testing parallelization...') print('Running in serial') t1i = sc.tic() serial_result = pe.shellstep(lambda_func, startvals, parallelize=False, **kwargs) t1f = sc.toc(t1i, output=True) print('Running in parallel') t2i = sc.tic() parallel_result = pe.shellstep(wrapped_lambda_func, startvals, parallelize=True, **kwargs) t2f = sc.toc(t2i, output=True) print(f'Serial time: {t1f:0.3f} s; parallel time: {t2f:0.3f} s') return serial_result, parallel_result
def run(self, do_plot=False, verbose=None, **kwargs): ''' Run the simulation. Args: do_plot (bool): whether to plot verbose (int): level of detail to print kwargs (dict): passed to self.plot() Returns: results: the results object (also modifies in-place) ''' T = sc.tic() # Reset settings and results if not self.initialized: self.initialize() if verbose is None: verbose = self['verbose'] # Main simulation loop for t in range(self.npts): self.next(verbose=verbose) # Check timing and stopping function # Raise an error here because the simulation will be left in an incomplete and potentially # unusable state (it would be necessary to continue stepping the simulation and to finalize) # Also raising the exception means that we won't progress to finalizing the simulation # (which wouldn't make sense if the simulation was terminated anyway) # This error could be caught and then the simulation run could be continued simply by # calling `sim.run()` again (it will resume where it left off) elapsed = sc.toc(T, output=True) if elapsed > self['timelimit']: raise TimeoutError( f"Time limit ({self['timelimit']} s) exceeded; stopping..." ) elif self['stop_func'] and self['stop_func'](self): raise cvu.CancelError( "Stopping function terminated the simulation") # End of time loop; compute cumulative results outside of the time loop self.finalize(verbose=verbose) # Finalize the results sc.printv(f'\nRun finished after {elapsed:0.1f} s.\n', 1, verbose) self.summary = self.summary_stats(verbose=verbose) if do_plot: # Optionally plot self.plot(**kwargs) return self.results
def run(self, do_plot=False, verbose=None, **kwargs): ''' Run the simulation. Args: do_plot (bool): whether to plot verbose (int): level of detail to print kwargs (dict): passed to self.plot() Returns: results: the results object (also modifies in-place) ''' T = sc.tic() # Reset settings and results if not self.initialized: self.initialize() if verbose is None: verbose = self['verbose'] # Main simulation loop for t in range(self.npts): # Do the heavy lifting self.next(verbose=verbose) # Check if we were asked to stop elapsed = sc.toc(T, output=True) if elapsed > self['timelimit']: sc.printv(f"Time limit ({self['timelimit']} s) exceeded", 1, verbose) break elif self['stopping_func'] and self['stopping_func'](self): sc.printv("Stopping function terminated the simulation", 1, verbose) break # End of time loop; compute cumulative results outside of the time loop self.finalize(verbose=verbose) # Finalize the results sc.printv(f'\nRun finished after {elapsed:0.1f} s.\n', 1, verbose) self.summary = self.summary_stats(verbose=verbose) if do_plot: # Optionally plot self.plot(**kwargs) return self.results
def normalize_performance(): ''' Normalize performance across CPUs -- simple Numpy calculation ''' t_bls = [] bl_repeats = 5 n_outer = 10 n_inner = 1e6 for r in range(bl_repeats): t0 = sc.tic() for i in range(n_outer): a = np.random.random(int(n_inner)) b = np.random.random(int(n_inner)) a*b t_bl = sc.toc(t0, output=True) t_bls.append(t_bl) t_bl = min(t_bls) reference = 0.112 # Benchmarked on an Intel i9-8950HK CPU @ 2.90GHz ratio = reference/t_bl return ratio
def cova(): sc.tic() s1 = cv.Sim(pop_size=ps2, pop_type='random') s1.initialize() sc.toc() sc.tic() s2 = cv.Sim(pop_size=ps2, pop_type='hybrid') s2.initialize() sc.toc() sc.tic() s3 = cv.Sim(pop_size=ps2, pop_type='synthpops') s3.initialize() sc.toc() sc.tic() s4 = cv.Sim(pop_size=ps1, pop_type='synthpops') s4.initialize() sc.toc() return [s1, s2, s3, s4]
def cache_populations(seed=0, popfile=None): ''' Pre-generate the hybrid population ''' pars = sc.objdict( pop_size=225e3, pop_type='hybrid', rand_seed=seed, ) if popfile is None: popfile = f'inputs/kc_hybrid_seed{pars.rand_seed}.ppl' T = sc.tic() print(f'Making "{popfile}"...') sim = cv.Sim(pars) cv.make_people(sim, popfile=popfile, save_pop=True, school_type=True, school_type_ages=[[6, 11], [11, 14], [14, 18], [18, 22]]) sc.toc(T) print('Done') return
def test_interventions(do_plot=False, do_show=True, do_save=False, fig_path=None): sc.heading('Test of testing interventions') sc.heading('Setting up...') sc.tic() n_runs = 3 verbose = 1 base_pars = { 'pop_size': 1000, 'pop_type': 'hybrid', } base_sim = cv.Sim(base_pars) # create sim object n_people = base_sim['pop_size'] npts = base_sim.npts # Define overall testing assumptions # Remember that this is the daily % of the population that gets tested. S Korea (one of the highest-testing places) tested # an average of 10000 people/day over March, or 270,000 in total. This is ~200 people per million every day (0.02%).... max_optimistic_testing = 0.1 # ... which means that this is an artificially high number, for testing purposes only!! optimistic_daily_tests = [ max_optimistic_testing * n_people ] * npts # Very best-case scenario for asymptomatic testing # Define the scenarios scenarios = { 'baseline': { 'name': 'Status quo, no testing', 'pars': { 'interventions': None, } }, 'test_skorea': { 'name': 'Assuming South Korea testing levels of 0.02% daily (untargeted); isolate positives', 'pars': { 'interventions': cv.test_num(daily_tests=optimistic_daily_tests) } }, 'floating': { 'name': 'Test with constant probability based on symptoms', 'pars': { 'interventions': cv.test_prob(symp_prob=max_optimistic_testing, asymp_prob=0.0) } }, 'sequence': { 'name': 'Historical switching to probability', 'pars': { 'interventions': cv.sequence( days=[10, 51], interventions=[ cv.test_num(daily_tests=optimistic_daily_tests), cv.test_prob(symp_prob=0.2, asymp_prob=0.002), ]) } }, } metapars = {'n_runs': n_runs} scens = cv.Scenarios(sim=base_sim, metapars=metapars, scenarios=scenarios) scens.run(verbose=verbose, debug=debug) to_plot = ['cum_infections', 'n_infectious', 'new_tests', 'new_diagnoses'] fig_args = dict(figsize=(20, 24)) if do_plot: scens.plot(do_save=do_save, do_show=do_show, fig_path=fig_path, interval=7, fig_args=fig_args, to_plot=to_plot) return scens
def test_tracedelay(do_plot=False, do_show=True, do_save=False, fig_path=None): sc.heading( 'Test impact of reducing delay time for finding contacts of positives') sc.heading('Setting up...') sc.tic() n_runs = 3 verbose = 1 base_pars = { 'pop_size': 1000, 'pop_type': 'hybrid', } base_sim = cv.Sim(base_pars) # create sim object base_sim['n_days'] = 50 base_sim['beta'] = 0.03 # Increase beta n_people = base_sim['pop_size'] npts = base_sim.npts # Define overall testing assumptions testing_prop = 0.1 # Assumes we could test 10% of the population daily (way too optimistic!!) daily_tests = [testing_prop * n_people] * npts # Number of daily tests # Define the scenarios scenarios = { 'lowtrace': { 'name': 'Poor contact tracing', 'pars': { 'quar_eff': { 'h': 1, 's': 0.5, 'w': 0.5, 'c': 0.25 }, 'quar_period': 7, 'interventions': [ cv.test_num(daily_tests=daily_tests), cv.contact_tracing(trace_probs={ 'h': 0, 's': 0, 'w': 0, 'c': 0 }, trace_time={ 'h': 1, 's': 7, 'w': 7, 'c': 7 }) ] } }, 'modtrace': { 'name': 'Moderate contact tracing', 'pars': { 'quar_eff': { 'h': 0.75, 's': 0.25, 'w': 0.25, 'c': 0.1 }, 'quar_period': 10, 'interventions': [ cv.test_num(daily_tests=daily_tests), cv.contact_tracing(trace_probs={ 'h': 1, 's': 0.8, 'w': 0.5, 'c': 0.1 }, trace_time={ 'h': 0, 's': 3, 'w': 3, 'c': 8 }) ] } }, 'hightrace': { 'name': 'Fast contact tracing', 'pars': { 'quar_eff': { 'h': 0.5, 's': 0.1, 'w': 0.1, 'c': 0.1 }, 'quar_period': 14, 'interventions': [ cv.test_num(daily_tests=daily_tests), cv.contact_tracing(trace_probs={ 'h': 1, 's': 0.8, 'w': 0.8, 'c': 0.2 }, trace_time={ 'h': 0, 's': 1, 'w': 1, 'c': 5 }) ] } }, 'alltrace': { 'name': 'Same-day contact tracing', 'pars': { 'quar_eff': { 'h': 0.0, 's': 0.0, 'w': 0.0, 'c': 0.0 }, 'quar_period': 21, 'interventions': [ cv.test_num(daily_tests=daily_tests), cv.contact_tracing(trace_probs={ 'h': 1, 's': 1, 'w': 1, 'c': 1 }, trace_time={ 'h': 0, 's': 1, 'w': 1, 'c': 2 }) ] } }, } metapars = {'n_runs': n_runs} scens = cv.Scenarios(sim=base_sim, metapars=metapars, scenarios=scenarios) scens.run(verbose=verbose, debug=debug) if do_plot: to_plot = [ 'cum_infections', 'cum_recoveries', 'new_infections', 'n_quarantined', 'new_quarantined' ] fig_args = dict(figsize=(24, 16)) scens.plot(do_save=do_save, do_show=do_show, to_plot=to_plot, fig_path=fig_path, n_cols=2, fig_args=fig_args) return scens
def make_population(seed=0, popfile=None): ''' Pre-generate the synthpops population ''' pars = sc.objdict( pop_size=10e3, pop_type='synthpops', rand_seed=seed, ) use_two_group_reduction = True average_LTCF_degree = 20 ltcf_staff_age_min = 20 ltcf_staff_age_max = 60 with_school_types = True average_class_size = 20 inter_grade_mixing = 0.1 average_student_teacher_ratio = 20 average_teacher_teacher_degree = 3 teacher_age_min = 25 teacher_age_max = 75 with_non_teaching_staff = True # if with_non_teaching_staff is False, but generate is True, then average_all_staff_ratio should be average_student_teacher_ratio or 0 average_student_all_staff_ratio = 11 average_additional_staff_degree = 20 staff_age_min = 20 staff_age_max = 75 school_mixing_type = { 'pk': 'clustered', 'es': 'clustered', 'ms': 'clustered', 'hs': 'random', 'uv': 'random' } if popfile is None: popfile = f'inputs/kc_synthpops_clustered_withstaff_10e3.ppl' T = sc.tic() print(f'Making "{popfile}"...') sim = cv.Sim(pars) cv.make_people( sim, popfile=popfile, save_pop=True, generate=True, with_facilities=True, use_two_group_reduction=use_two_group_reduction, average_LTCF_degree=average_LTCF_degree, ltcf_staff_age_min=ltcf_staff_age_min, ltcf_staff_age_max=ltcf_staff_age_max, with_school_types=with_school_types, school_mixing_type=school_mixing_type, average_class_size=average_class_size, inter_grade_mixing=inter_grade_mixing, average_student_teacher_ratio=average_student_teacher_ratio, average_teacher_teacher_degree=average_teacher_teacher_degree, teacher_age_min=teacher_age_min, teacher_age_max=teacher_age_max, with_non_teaching_staff=with_non_teaching_staff, average_student_all_staff_ratio=average_student_all_staff_ratio, average_additional_staff_degree=average_additional_staff_degree, staff_age_min=staff_age_min, staff_age_max=staff_age_max) sc.toc(T) print('Done') return
# 2020-05-10 - 2020-01-27 days = (sc.readdate("2020-05-10") - sc.readdate("2020-01-27")).days + 1 yield_num_old = np.zeros(days) yield_num_new = np.zeros(days) yield_num_flat = np.zeros(days) # Need a couple of sims to make sure we are not looking at noise stage_old = {'mild': 0, 'sev': 0, 'crit': 0} stage_new = {'mild': 0, 'sev': 0, 'crit': 0} stage_flat = {'mild': 0, 'sev': 0, 'crit': 0} start = 1 end = 2 n_run = end - start for i in range(start, end): sim = single_sim_old(rand_seed=i) t = sc.tic() sim.run() idx = sim.people.diagnosed time_num_old = np.append( time_num_old, sim.people.date_diagnosed[idx] - sim.people.date_symptomatic[idx]) yield_num_old = yield_num_old + sim.results['test_yield'].values idx_dia = sim.people.diagnosed idx = ~np.isnan(sim.people.date_symptomatic) stage_old['mild'] += sum(idx[idx_dia]) / sum(idx) idx = ~np.isnan(sim.people.date_severe) stage_old['sev'] += sum(idx[idx_dia]) / sum(idx) idx = ~np.isnan(sim.people.date_critical) stage_old['crit'] += sum(idx[idx_dia]) / sum(idx) sc.toc(t)
def run(self, do_plot=False, until=None, verbose=None, **kwargs): ''' Run the simulation. Args: do_plot (bool): whether to plot until (int): day to run until verbose (int): level of detail to print kwargs (dict): passed to self.plot() Returns: results: the results object (also modifies in-place) ''' # Initialize T = sc.tic() if not self.initialized: self.initialize() else: self.validate_pars( ) # We always want to validate the parameters before running self.init_interventions() # And interventions if verbose is None: verbose = self['verbose'] if until: until = self.day(until) # Main simulation loop for t in self.tvec: # Print progress if verbose >= 1: elapsed = sc.toc(output=True) simlabel = f'"{self.label}": ' if self.label else '' string = f' Running {simlabel}{self.datevec[t]} ({t:2.0f}/{self.pars["n_days"]}) ({elapsed:0.2f} s) ' if verbose >= 2: sc.heading(string) elif verbose == 1: sc.progressbar(t + 1, self.npts, label=string, length=20, newline=True) # Do the heavy lifting -- actually run the model! self.step() # Check if we were asked to stop elapsed = sc.toc(T, output=True) if self['timelimit'] and elapsed > self['timelimit']: sc.printv(f"Time limit ({self['timelimit']} s) exceeded", 1, verbose) break elif self['stopping_func'] and self['stopping_func'](self): sc.printv("Stopping function terminated the simulation", 1, verbose) break if self.t == until: # If until is specified, just stop here return # End of time loop; compute cumulative results outside of the time loop self.finalize(verbose=verbose) # Finalize the results sc.printv(f'Run finished after {elapsed:0.2f} s.\n', 1, verbose) if do_plot: # Optionally plot self.plot(**kwargs) return self.results
def make_population(pop_size, rand_seed=1, max_pop_seeds=None, do_save=True, popfile=None, cohorting=True, n_brackets=20, community_contacts=20, **kwargs): ''' Generate the synthpops population. Args: pop_size (int): number of people in the model rand_seed (int): random seed to use for generating the population max_pop_seeds (int): if supplied, take the random seed as modulus of this to limit number of populations generated do_save (bool): whether to save the population popfile (str): if so, where to save it to cohorting (bool): whether to use cohorting n_brackets (int): whether to use 16- or 20-bracket age bins community_contacts (int): how many community contacts there are kwargs (dict): passed to sp.make_population() ''' sp.set_nbrackets( n_brackets) # Essential for getting the age distribution right pars = sc.objdict( n=pop_size, rand_seed=rand_seed, with_facilities=True, use_two_group_reduction=True, average_LTCF_degree=20, ltcf_staff_age_min=20, ltcf_staff_age_max=60, with_school_types=True, average_class_size=20, inter_grade_mixing=0.1, average_student_teacher_ratio=20, average_teacher_teacher_degree=3, teacher_age_min=25, teacher_age_max=75, with_non_teaching_staff=True, # if with_non_teaching_staff is False, but generate is True, then ,average_all_staff_ratio should be average_student_teacher_ratio or 0 average_student_all_staff_ratio=11, average_additional_staff_degree=20, staff_age_min=20, staff_age_max=75, ) pars.update(kwargs) # Update any parameters # For reference re: school_types # school_mixing_type = 'random' means that students in the school have edges randomly chosen from other students, teachers, and non teaching staff across the school. Students, teachers, and non teaching staff are treated the same in terms of edge generation. # school_mixing_type = 'age_clustered' means that students in the school have edges mostly within their own age/grade, with teachers, and non teaching staff. Strict classrooms are not generated. Teachers have some additional edges with other teachers. # school_mixing_type = 'age_and_class_clustered' means that students are cohorted into classes of students of the same age/grade with at least 1 teacher, and then some have contact with non teaching staff. Teachers have some additional edges with other teachers. if cohorting: strategy = 'clustered' # students in pre-k, elementary, and middle school are cohorted into strict classrooms pars.school_mixing_type = { 'pk': 'age_and_class_clustered', 'es': 'age_and_class_clustered', 'ms': 'age_and_class_clustered', 'hs': 'random', 'uv': 'random' } else: strategy = 'normal' pars.school_mixing_type = { 'pk': 'age_clustered', 'es': 'age_clustered', 'ms': 'age_clustered', 'hs': 'random', 'uv': 'random' } if popfile is None: popfile = os.path.join( 'inputs', f'kc_{strategy}_{int(pars.n)}_seed{pars.rand_seed}.ppl') T = sc.tic() print('Making population...') # Make the population population = sp.make_population(**pars) # Convert to a popdict popdict = cv.make_synthpop(population=sc.dcp(population), community_contacts=community_contacts) school_ids = [None] * int(pop_size) teacher_flag = [False] * int(pop_size) staff_flag = [False] * int(pop_size) student_flag = [False] * int(pop_size) school_types = {'pk': [], 'es': [], 'ms': [], 'hs': [], 'uv': []} school_type_by_person = [None] * int(pop_size) schools = dict() for uid, person in population.items(): if person['scid'] is not None: school_ids[uid] = person['scid'] school_type_by_person[uid] = person['sc_type'] if person['scid'] not in school_types[person['sc_type']]: school_types[person['sc_type']].append(person['scid']) if person['scid'] in schools: schools[person['scid']].append(uid) else: schools[person['scid']] = [uid] if person['sc_teacher'] is not None: teacher_flag[uid] = True elif person['sc_student'] is not None: student_flag[uid] = True elif person['sc_staff'] is not None: staff_flag[uid] = True popdict['school_id'] = np.array(school_ids) popdict['schools'] = schools popdict['teacher_flag'] = teacher_flag popdict['student_flag'] = student_flag popdict['staff_flag'] = staff_flag popdict['school_types'] = school_types popdict['school_type_by_person'] = school_type_by_person assert sum( popdict['teacher_flag'] ), 'Uh-oh, no teachers were found: as a school analysis this is treated as an error' assert sum( popdict['student_flag'] ), 'Uh-oh, no students were found: as a school analysis this is treated as an error' # Actually create the people people_pars = dict( pop_size=pars.n, beta_layer={k: 1.0 for k in 'hswcl' }, # Since this is used to define hat layers exist beta= 1.0, # TODO: this is required for plotting (people.plot()), but shouldn't be ) people = cv.People(people_pars, strict=False, uid=popdict['uid'], age=popdict['age'], sex=popdict['sex'], contacts=popdict['contacts'], school_id=popdict['school_id'], schools=popdict['schools'], school_types=popdict['school_types'], student_flag=popdict['student_flag'], teacher_flag=popdict['teacher_flag'], staff_flag=popdict['staff_flag'], school_type_by_person=popdict['school_type_by_person']) if do_save: print(f'Saving to "{popfile}"...') sc.saveobj(popfile, people) sc.toc(T) print('Done') return people
def run(self, initialize=True, do_plot=False, verbose=None, **kwargs): ''' Run the simulation ''' T = sc.tic() # Reset settings and results if verbose is None: verbose = self['verbose'] if initialize: self.initialize() # Create people, results, etc. # Main simulation loop self.stopped = False # We've just been asked to run, so ensure we're unstopped for t in range(self.npts): # Check timing and stopping function elapsed = sc.toc(T, output=True) if elapsed > self['timelimit']: print( f"Time limit ({self['timelimit']} s) exceeded; stopping..." ) self.stopped = { 'why': 'timelimit', 'message': 'Time limit exceeded at step {t}', 't': t } if self['stop_func']: self.stopped = self['stop_func']( self, t) # Feed in the current simulation object and the time # If this gets set, stop running -- e.g. if the time limit is exceeded if self.stopped: break # Zero counts for this time step. n_susceptible = 0 n_exposed = 0 n_deaths = 0 n_recoveries = 0 n_infectious = 0 n_infections = 0 n_symptomatic = 0 n_recovered = 0 # Extract these for later use. The values do not change in the person loop and the dictionary lookup is expensive. rand_popdata = (self['usepopdata'] == 'random') beta = self['beta'] asym_factor = self['asym_factor'] diag_factor = self['diag_factor'] cont_factor = self['cont_factor'] beta_pop = self['beta_pop'] # Print progress if verbose >= 1: string = f' Running day {t:0.0f} of {self.pars["n_days"]} ({elapsed:0.2f} s elapsed)...' if verbose >= 2: sc.heading(string) else: print(string) # Update each person, skipping people who are susceptible not_susceptible = filter(lambda p: not p.susceptible, self.people.values()) n_susceptible = len(self.people) for person in not_susceptible: n_susceptible -= 1 # If exposed, check if the person becomes infectious or develops symptoms if person.exposed: n_exposed += 1 if not person.infectious and t >= person.date_infectious: # It's the day they become infectious person.infectious = True sc.printv( f' Person {person.uid} became infectious!', 2, verbose) if not person.symptomatic and person.date_symptomatic is not None and t >= person.date_symptomatic: # It's the day they develop symptoms person.symptomatic = True sc.printv( f' Person {person.uid} developed symptoms!', 2, verbose) # If infectious, check if anyone gets infected if person.infectious: # Check for death died = person.check_death(t) n_deaths += died # Check for recovery recovered = person.check_recovery(t) n_recoveries += recovered # If the person didn't die or recover, check for onward transmission if not died and not recovered: n_infectious += 1 # Count this person as infectious # Calculate transmission risk based on whether they're asymptomatic/diagnosed/have been isolated thisbeta = beta * \ (asym_factor if person.symptomatic else 1.) * \ (diag_factor if person.diagnosed else 1.) * \ (cont_factor if person.known_contact else 1.) # Determine who gets infected if rand_popdata: # Flat contacts transmission_inds = cvu.bf(thisbeta, person.contacts) else: # Dictionary of contacts -- extra loop over layers transmission_inds = [] for ckey in self.contact_keys: layer_beta = thisbeta * beta_pop[ckey] transmission_inds.extend( cvu.bf(layer_beta, person.contacts[ckey])) # Loop over people who do for contact_ind in transmission_inds: target_person = self.get_person( contact_ind) # Stored by integer # This person was diagnosed last time step: time to flag their contacts if person.date_diagnosed is not None and person.date_diagnosed == t - 1: target_person.known_contact = True # Skip people who are not susceptible if target_person.susceptible: n_infections += target_person.infect( t, person) # Actually infect them sc.printv( f' Person {person.uid} infected person {target_person.uid}!', 2, verbose) # Count people who developed symptoms if person.symptomatic: n_symptomatic += 1 # Count people who recovered if person.recovered: n_recovered += 1 # End of person loop; apply interventions for intervention in self['interventions']: intervention.apply(self, t) if self['interv_func'] is not None: # Apply custom intervention function self = self['interv_func'](self, t) # Update counts for this time step self.results['n_susceptible'][t] = n_susceptible self.results['n_exposed'][t] = n_exposed self.results['deaths'][t] = n_deaths self.results['recoveries'][t] = n_recoveries self.results['n_infectious'][t] = n_infectious self.results['infections'][t] = n_infections self.results['n_symptomatic'][t] = n_symptomatic self.results['n_recovered'][t] = n_recovered # End of time loop; compute cumulative results outside of the time loop self.results['cum_exposed'].values = pl.cumsum( self.results['infections'].values) + self[ 'n_infected'] # Include initially infected people self.results['cum_tested'].values = pl.cumsum( self.results['tests'].values) self.results['cum_diagnosed'].values = pl.cumsum( self.results['diagnoses'].values) self.results['cum_deaths'].values = pl.cumsum( self.results['deaths'].values) self.results['cum_recoveries'].values = pl.cumsum( self.results['recoveries'].values) # Add in the results from the interventions for intervention in self['interventions']: intervention.finalize(self) # Execute any post-processing # Scale the results for reskey in self.reskeys: if self.results[reskey].scale: self.results[reskey].values *= self['scale'] # Perform calculations on results self.compute_doubling() self.compute_r_eff() self.likelihood() # Tidy up self.results_ready = True sc.printv(f'\nRun finished after {elapsed:0.1f} s.\n', 1, verbose) self.results['summary'] = self.summary_stats() if do_plot: self.plot(**kwargs) # Convert to an odict to allow e.g. sim.people[25] later, and results to an objdict to allow e.g. sim.results.diagnoses self.people = sc.odict(self.people) self.results = sc.objdict(self.results) return self.results
def run(self, do_plot=False, until=None, restore_pars=True, reset_seed=True, verbose=None, **kwargs): ''' Run the simulation. Args: do_plot (bool): whether to plot until (int): day to run until restore_pars (bool): whether to make a copy of the parameters before the run and restore it after, so runs are repeatable reset_seed (bool): whether to reset the random number stream immediately before run verbose (float): level of detail to print, e.g. 0 = no output, 0.2 = print every 5th day, 1 = print every day kwargs (dict): passed to sim.plot() Returns: results (dict): the results object (also modifies in-place) ''' # Initialize T = sc.tic() if not self.initialized: self.initialize() else: self.validate_pars( ) # We always want to validate the parameters before running self.init_interventions() # And interventions if reset_seed: self.set_seed( ) # Ensure the random number generator is freshly initialized if restore_pars: orig_pars = sc.dcp( self.pars ) # Create a copy of the parameters, to restore after the run, in case they are dynamically modified if verbose is None: verbose = self['verbose'] if until: until = self.day(until) # Main simulation loop for t in self.tvec: # Print progress if verbose: elapsed = sc.toc(output=True) simlabel = f'"{self.label}": ' if self.label else '' string = f' Running {simlabel}{self.datevec[t]} ({t:2.0f}/{self.pars["n_days"]}) ({elapsed:0.2f} s) ' if verbose >= 2: sc.heading(string) else: if not (t % int(1.0 / verbose)): sc.progressbar(t + 1, self.npts, label=string, length=20, newline=True) # Do the heavy lifting -- actually run the model! self.step() # Check if we were asked to stop elapsed = sc.toc(T, output=True) if self['timelimit'] and elapsed > self['timelimit']: sc.printv(f"Time limit ({self['timelimit']} s) exceeded", 1, verbose) break elif self['stopping_func'] and self['stopping_func'](self): sc.printv("Stopping function terminated the simulation", 1, verbose) break if self.t == until: # If until is specified, just stop here return # End of time loop; compute cumulative results outside of the time loop self.finalize(verbose=verbose) # Finalize the results sc.printv(f'Run finished after {elapsed:0.2f} s.\n', 1, verbose) if restore_pars: self.restore_pars(orig_pars) if do_plot: # Optionally plot self.plot(**kwargs) return self.results
def _do_RPC(self, verbose=False): # Check to see whether the RPC is getting passed in in request.form. # If so, we are doing an upload, and we want to download the RPC # request info from the form, not request.data. if 'funcname' in request.form: # Pull out the function name, args, and kwargs fn_name = request.form.get('funcname') try: args = json.loads(request.form.get('args', "[]"), object_pairs_hook=OrderedDict) except: args = [] # May or may not be present try: kwargs = json.loads(request.form.get('kwargs', "{}"), object_pairs_hook=OrderedDict) except: kwargs = {} # May or may not be present else: # Otherwise, we have a normal or download RPC, which means we pull the RPC request info from request.data. reqdict = json.loads(request.data, object_pairs_hook=OrderedDict) fn_name = reqdict['funcname'] args = reqdict.get('args', []) kwargs = reqdict.get('kwargs', {}) if verbose: print('RPC(): RPC properties:') print(' fn_name: %s' % fn_name) print(' args: %s' % args) print(' kwargs: %s' % kwargs) # If the function name is not in the RPC dictionary, return an error. if not sc.isstring(fn_name): return robustjsonify( {'error': 'Invalid RPC - must be a string (%s)' % fn_name}) if not fn_name in self.RPC_dict: return robustjsonify( {'error': 'Could not find requested RPC "%s"' % fn_name}) found_RPC = self.RPC_dict[fn_name] # Get the RPC we've found. ## Do any validation checks we need to do and return errors if they don't pass. # If the RPC is disabled, always return a Status 403 (Forbidden) if found_RPC.validation == 'disabled': if verbose: print('RPC(): RPC disabled') abort(403) # Only do other validation if DataStore and users are included -- NOTE: Any "unknown" validation values are treated like 'none'. if self.config['USE_DATASTORE'] and self.config['USE_USERS']: if found_RPC.validation == 'any' and not ( current_user.is_anonymous or current_user.is_authenticated): abort( 401 ) # If the RPC should be executable by any user, including an anonymous one, but there is no authorization or anonymous login, return a Status 401 (Unauthorized) elif found_RPC.validation == 'named' and ( current_user.is_anonymous or not current_user.is_authenticated): abort( 401 ) # Else if the RPC should be executable by any non-anonymous user, but there is no authorization or there is an anonymous login, return a Status 401 (Unauthorized) elif found_RPC.validation == 'admin': # Else if the RPC should be executable by any admin user, but there is no admin login or it's an anonymous login... if current_user.is_anonymous or not current_user.is_authenticated: abort( 401 ) # If the user is anonymous or no authenticated user is logged in, return Status 401 (Unauthorized). elif not current_user.is_admin: abort( 403 ) # Else, if the user is not an admin user, return Status 403 (Forbidden). # If we are doing an upload... if found_RPC.call_type == 'upload': if verbose: print('Starting upload...') thisfile = request.files[ 'uploadfile'] # Grab the formData file that was uploaded. filename = secure_filename( thisfile.filename ) # Extract a sanitized filename from the one we start with. try: uploaded_fname = os.path.join( self.datastore.tempfolder, filename) # Generate a full upload path/file name. except Exception as E: errormsg = 'Could not create filename for uploaded file: %s' % str( E) raise Exception(errormsg) try: thisfile.save( uploaded_fname) # Save the file to the uploads directory except Exception as E: errormsg = 'Could not save uploaded file: %s' % str(E) raise Exception(errormsg) args.insert( 0, uploaded_fname) # Prepend the file name to the args list. # Show the call of the function. callcolor = ['cyan', 'bgblue'] successcolor = ['green', 'bgblue'] failcolor = ['gray', 'bgred'] timestr = '[%s]' % sc.now(astype='str') try: userstr = ' <%s>' % current_user.username except: userstr = ' <no user>' RPCinfo = sc.objdict({ 'time': timestr, 'user': userstr, 'module': found_RPC.call_func.__module__, 'name': found_RPC.funcname }) if self.config['LOGGING_MODE'] == 'FULL': string = '%s%s RPC called: "%s.%s"' % ( RPCinfo.time, RPCinfo.user, RPCinfo.module, RPCinfo.name) sc.colorize(callcolor, string, enable=self.colorize) # Execute the function to get the results, putting it in a try block in case there are errors in what's being called. try: if verbose: print('RPC(): Starting RPC...') T = sc.tic() result = found_RPC.call_func(*args, **kwargs) if isinstance( result, dict ) and 'error' in result: # If the RPC returns an error, return it return robustjsonify({'error': result['error']}) elapsed = sc.toc(T, output=True) if self.config['LOGGING_MODE'] == 'FULL': string = '%s%s RPC finished in %0.2f s: "%s.%s"' % ( RPCinfo.time, RPCinfo.user, elapsed, RPCinfo.module, RPCinfo.name) sc.colorize(successcolor, string, enable=self.colorize) except Exception as E: if verbose: print('RPC(): Exception encountered...') shortmsg = str(E) exception = traceback.format_exc() # Grab the trackback stack hostname = '|%s| ' % socket.gethostname() tracemsg = '%s%s%s Exception during RPC "%s.%s" \nRequest: %s \n%.10000s' % ( hostname, RPCinfo.time, RPCinfo.user, RPCinfo.module, RPCinfo.name, request, exception) sc.colorize( failcolor, tracemsg, enable=self.colorize ) # Post an error to the Flask logger limiting the exception information to 10000 characters maximum (to prevent monstrous sqlalchemy outputs) if self.config['SLACK']: self.slacknotification(tracemsg) if isinstance( E, HTTPException ): # If we have a werkzeug exception, pass it on up to werkzeug to resolve and reply to. raise E code = 500 # Send back a response with status 500 that includes the exception traceback. fullmsg = shortmsg + '\n\nException details:\n' + tracemsg reply = { 'exception': fullmsg } # NB, not sure how to actually access 'traceback' on the FE, but keeping it here for future return make_response(robustjsonify(reply), code) # If we are doing a download, prepare the response and send it off. if found_RPC.call_type == 'download': # To download a file, use `this.$sciris.download` instead of `this.$sciris.rpc`. Decorate the RPC with # `@RPC(call_type='download')`. Finally, the RPC needs to specify the file and optionally the filename. # This is done with tuple unpacking. The following outputs are supported from `rpc_function()` # # 1 - filename_on_disk # 2 - BytesIO # 3 - filename_on_disk, download_filename # 4- BytesIO, download_filename # # Examples return values from the RPC are as follows # # 1 - "E:/test.xlsx" (uses "test.xlsx") # 2 - <BytesIO> (default filename will be generated in this function) # 3 - ("E:/test.xlsx","foo.xlsx") # 4 - (<BytesIO>,"foo.xlsx") # # On the RPC end, the most common cases would be it might look like # # return "E:/test.xlsx" # # OR # # return Blobject.to_file(), "foo.xlsx" if verbose: print('RPC(): Starting download...') if result is None: # If we got None for a result (the full file name), return an error to the client. return robustjsonify({ 'error': 'Could not find resource to download from RPC "%s": result is None' % fn_name }) elif sc.isstring(result): from_file = True dir_name, file_name = os.path.split(result) output_name = file_name elif isinstance(result, io.BytesIO): from_file = False bytesio = result output_name = 'download.obj' else: try: content = result[0] output_name = result[1] if sc.isstring(content): from_file = True dir_name, file_name = os.path.split(content) elif isinstance(content, io.BytesIO): from_file = False bytesio = content else: return robustjsonify( {'error': 'Unrecognized RPC output'}) except Exception as E: return robustjsonify( {'error': 'Error reading RPC result (%s)' % E}) if from_file: response = send_from_directory(dir_name, file_name, as_attachment=True) response.status_code = 201 # Status 201 = Created # Unfortunately, we cannot remove the actual file at this point # because it is in use during the actual download, so we rely on # later cleanup to remove download files. else: response = send_file(bytesio, as_attachment=True, attachment_filename=output_name) response.headers['filename'] = output_name print(response) return response # Return the response message. # Otherwise (normal and upload RPCs), else: if found_RPC.call_type == 'upload': # If we are doing an upload.... try: os.remove( uploaded_fname ) # Erase the physical uploaded file, since it is no longer needed. if verbose: print('RPC(): Removed uploaded file: %s' % uploaded_fname) except Exception as E: if verbose: print('RPC(): Could not remove uploaded file: %s' % str(E)) # Probably since moved by the user if result is None: # If None was returned by the RPC function, return ''. if verbose: print('RPC(): RPC finished, returning None') return '' else: # Otherwise, convert the result (probably a dict) to JSON and return it. output = robustjsonify(result) if verbose: print('RPC(): RPC finished, returning result') return output
def make_study(restart=True): ''' Make a study, deleting one if it already exists ''' try: if restart: print(f'About to delete {storage}:{name}, you have 5 seconds to intervene!') sc.timedsleep(5.0) op.delete_study(storage=storage, study_name=name) except: pass output = op.create_study(storage=storage, study_name=name, load_if_exists=not(restart)) return output if __name__ == '__main__': t0 = sc.tic() make_study(restart=False) run_workers() study = op.load_study(storage=storage, study_name=name) best_pars = study.best_params T = sc.toc(t0, output=True) print(f'Output: {best_pars}, time: {T}') sc.heading('Loading data...') best = cs.define_pars('best') bounds = cs.define_pars('bounds') sc.heading('Making results structure...') results = [] failed_trials = []
if __name__ == '__main__': datadir = sp.datadir state_location = 'Washington' location = 'seattle_metro' country_location = 'usa' popdict = {} n = 20000 options_args = {'use_microstructure': True} network_distr_args = {'Npop': int(n)} sc.tic() contacts = sp.make_contacts(popdict, state_location=state_location, location=location, options_args=options_args, network_distr_args=network_distr_args) # uids = contacts.keys() # uids = [uid for uid in uids] # print(contacts[uids[3]]['contacts']) # contacts = sp.trim_contacts(contacts,trimmed_size_dic=None,use_clusters=False) # print(contacts[uids[3]]['contacts']) sp.save_synthpop( os.path.join(datadir, 'demographics', country_location, state_location), contacts)
def test_benchmark(do_save=do_save): ''' Compare benchmark performance ''' print('Running benchmark...') previous = sc.loadjson(benchmark_filename) repeats = 5 t_inits = [] t_runs = [] def normalize_performance(): ''' Normalize performance across CPUs -- simple Numpy calculation ''' t_bls = [] bl_repeats = 5 n_outer = 10 n_inner = 1e6 for r in range(bl_repeats): t0 = sc.tic() for i in range(n_outer): a = np.random.random(int(n_inner)) b = np.random.random(int(n_inner)) a*b t_bl = sc.toc(t0, output=True) t_bls.append(t_bl) t_bl = min(t_bls) reference = 0.112 # Benchmarked on an Intel i9-8950HK CPU @ 2.90GHz ratio = reference/t_bl return ratio # Test CPU performance before the run r1 = normalize_performance() # Do the actual benchmarking for r in range(repeats): # Create the sim sim = cv.Sim(verbose=0) # Time initialization t0 = sc.tic() sim.initialize() t_init = sc.toc(t0, output=True) # Time running t0 = sc.tic() sim.run() t_run = sc.toc(t0, output=True) # Store results t_inits.append(t_init) t_runs.append(t_run) # Test CPU performance after the run r2 = normalize_performance() ratio = (r1+r2)/2 t_init = min(t_inits)*ratio t_run = min(t_runs)*ratio # Construct json n_decimals = 3 json = {'time': { 'initialize': round(t_init, n_decimals), 'run': round(t_run, n_decimals), }, 'parameters': { 'pop_size': sim['pop_size'], 'pop_type': sim['pop_type'], 'n_days': sim['n_days'], }, 'cpu_performance': ratio, } print('Previous benchmark:') sc.pp(previous) print('\nNew benchmark:') sc.pp(json) if do_save: sc.savejson(filename=benchmark_filename, obj=json, indent=2) print('Done.') return json
def run(self, do_plot=False, until=None, restore_pars=True, reset_seed=True, verbose=None, **kwargs): ''' Run the simulation. Args: do_plot (bool): whether to plot until (int/str): day or date to run until restore_pars (bool): whether to make a copy of the parameters before the run and restore it after, so runs are repeatable reset_seed (bool): whether to reset the random number stream immediately before run verbose (float): level of detail to print, e.g. 0 = no output, 0.2 = print every 5th day, 1 = print every day kwargs (dict): passed to sim.plot() Returns: results (dict): the results object (also modifies in-place) ''' # Initialization steps -- start the timer, initialize the sim and the seed, and check that the sim hasn't been run T = sc.tic() if verbose is None: verbose = self['verbose'] if not self.initialized: self.initialize() self._orig_pars = sc.dcp(self.pars) # Create a copy of the parameters, to restore after the run, in case they are dynamically modified if reset_seed: # Reset the RNG. If the simulation is newly created, then the RNG will be reset by sim.initialize() so the use case # for resetting the seed here is if the simulation has been partially run, and changing the seed is required self.set_seed() until = self.npts if until is None else self.day(until) if until > self.npts: raise AlreadyRunError(f'Requested to run until t={until} but the simulation end is t={self.npts}') if self.complete: raise AlreadyRunError('Simulation is already complete (call sim.initialize() to re-run)') if self.t >= until: # NB. At the start, self.t is None so this check must occur after initialization raise AlreadyRunError(f'Simulation is currently at t={self.t}, requested to run until t={until} which has already been reached') # Main simulation loop while self.t < until: # Check if we were asked to stop elapsed = sc.toc(T, output=True) if self['timelimit'] and elapsed > self['timelimit']: sc.printv(f"Time limit ({self['timelimit']} s) exceeded; call sim.finalize() to compute results if desired", 1, verbose) return elif self['stopping_func'] and self['stopping_func'](self): sc.printv("Stopping function terminated the simulation; call sim.finalize() to compute results if desired", 1, verbose) return # Print progress if verbose: simlabel = f'"{self.label}": ' if self.label else '' string = f' Running {simlabel}{self.datevec[self.t]} ({self.t:2.0f}/{self.pars["n_days"]}) ({elapsed:0.2f} s) ' if verbose >= 2: sc.heading(string) else: if not (self.t % int(1.0/verbose)): sc.progressbar(self.t+1, self.npts, label=string, length=20, newline=True) # Do the heavy lifting -- actually run the model! self.step() # If simulation reached the end, finalize the results if self.complete: self.finalize(verbose=verbose, restore_pars=restore_pars) sc.printv(f'Run finished after {elapsed:0.2f} s.\n', 1, verbose) if do_plot: # Optionally plot self.plot(**kwargs) return self.results else: return # If not complete, return nothing
def test_generate_microstructures_with_non_teaching_staff(): # # generate and write to file population1 = sp.generate_microstructure_with_facilities( datadir, location=location, state_location=state_location, country_location=country_location, n=n, use_two_group_reduction=use_two_group_reduction, average_LTCF_degree=average_LTCF_degree, ltcf_staff_age_min=ltcf_staff_age_min, ltcf_staff_age_max=ltcf_staff_age_max, with_school_types=with_school_types, school_mixing_type=school_mixing_type, average_class_size=average_class_size, inter_grade_mixing=inter_grade_mixing, average_student_teacher_ratio=average_student_teacher_ratio, average_teacher_teacher_degree=average_teacher_teacher_degree, teacher_age_min=teacher_age_min, teacher_age_max=teacher_age_max, average_student_all_staff_ratio=average_student_all_staff_ratio, average_additional_staff_degree=average_additional_staff_degree, staff_age_min=staff_age_min, staff_age_max=staff_age_max, write=write, plot=plot, return_popdict=return_popdict, use_default=use_default) # # # read in from file population2 = sp.make_contacts_with_facilities_from_microstructure( datadir, location=location, state_location=state_location, country_location=country_location, n=n, use_two_group_reduction=use_two_group_reduction, average_LTCF_degree=average_LTCF_degree, with_school_types=with_school_types, school_mixing_type=school_mixing_type, average_class_size=average_class_size, inter_grade_mixing=inter_grade_mixing, average_student_teacher_ratio=average_student_teacher_ratio, average_teacher_teacher_degree=average_teacher_teacher_degree, average_student_all_staff_ratio=average_student_all_staff_ratio, average_additional_staff_degree=average_additional_staff_degree) # # generate on the fly sc.tic() population3 = sp.make_population( n=n, generate=True, with_facilities=True, use_two_group_reduction=use_two_group_reduction, average_LTCF_degree=average_LTCF_degree, ltcf_staff_age_min=ltcf_staff_age_min, ltcf_staff_age_max=ltcf_staff_age_max, with_school_types=with_school_types, school_mixing_type=school_mixing_type, average_class_size=average_class_size, inter_grade_mixing=inter_grade_mixing, average_student_teacher_ratio=average_student_teacher_ratio, average_teacher_teacher_degree=average_teacher_teacher_degree, teacher_age_min=teacher_age_min, teacher_age_max=teacher_age_max, with_non_teaching_staff=with_non_teaching_staff, average_student_all_staff_ratio=average_student_all_staff_ratio, average_additional_staff_degree=average_additional_staff_degree, staff_age_min=staff_age_min, staff_age_max=staff_age_max, rand_seed=rand_seed) sc.toc() sp.check_all_residents_are_connected_to_staff(population3) return population1, population2, population3
def test_beds(do_plot=False, do_show=True, do_save=False, fig_path=None): sc.heading('Test of bed capacity estimation') sc.heading('Setting up...') sc.tic() n_runs = 3 verbose = 1 basepars = {'pop_size': 1000} metapars = {'n_runs': n_runs} sim = cv.Sim() # Define the scenarios scenarios = { 'baseline': { 'name': 'No bed constraints', 'pars': { 'pop_infected': 100 } }, 'bedconstraint': { 'name': 'Only 10 beds available', 'pars': { 'pop_infected': 100, 'n_beds': 10, } }, 'bedconstraint2': { 'name': 'Only 1 bed available', 'pars': { 'pop_infected': 100, 'n_beds': 1, # 'OR_no_treat': 10., # nb. scenarios cannot currently overwrite nested parameters # This prevents overwriting OR_no_treat due to recent refactoring but more generally # there are other nested parameters eg. all of those under pars['dur'] } }, } scens = cv.Scenarios(sim=sim, basepars=basepars, metapars=metapars, scenarios=scenarios) scens.run(verbose=verbose, debug=debug) if do_plot: to_plot = sc.odict({ 'cum_deaths': 'Cumulative deaths', 'bed_capacity': 'People needing beds / beds', 'n_severe': 'Number of cases requiring hospitalization', 'n_critical': 'Number of cases requiring ICU', }) scens.plot(to_plot=to_plot, do_save=do_save, do_show=do_show, fig_path=fig_path) return scens
''' Test plotting functions ''' import pylab as pl import sciris as sc import synthpops as sp import settings def test_plots(do_plot=False): ''' Basic plots ''' if not do_plot: pl.switch_backend('agg') # Plot but don't show pop = sp.Pop( n=settings.pop_sizes.small_medium) # default parameters, 5k people fig1 = pop.plot_people() # equivalent to cv.Sim.people.plot() fig2 = pop.plot_contacts() # equivalent to sp.plot_contact_matrix(popdict) return [fig1, fig2] if __name__ == '__main__': T = sc.tic() figs = test_plots(do_plot=True) sc.toc(T) print('Done.')
def run(self, seed_infections=1, verbose=None, calc_likelihood=False, do_plot=False, **kwargs): ''' Run the simulation ''' T = sc.tic() # Reset settings and results if verbose is None: verbose = self['verbose'] self.init_results() self.init_people( seed_infections=seed_infections) # Actually create the people daily_tests = self.data[ 'new_tests'] # Number of tests each day, from the data evacuated = self.data['evacuated'] # Number of people evacuated # Main simulation loop for t in range(self.npts): # Print progress if verbose >= 1: string = f' Running day {t:0.0f} of {self["n_days"]}...' if verbose >= 2: sc.heading(string) else: print(string) test_probs = { } # Store the probability of each person getting tested # Update each person for person in self.people.values(): # Count susceptibles if person.susceptible: self.results['n_susceptible'][t] += 1 continue # Don't bother with the rest of the loop # Handle testing probability if person.infectious: test_probs[person.uid] = self[ 'symptomatic'] # They're infectious: high probability of testing else: test_probs[person.uid] = 1.0 # If exposed, check if the person becomes infectious if person.exposed: self.results['n_exposed'][t] += 1 if not person.infectious and t >= person.date_infectious: # It's the day they become infectious person.infectious = True if verbose >= 2: print( f' Person {person.uid} became infectious!' ) # If infectious, check if anyone gets infected if person.infectious: # First, check for recovery if person.date_recovered and t >= person.date_recovered: # It's the day they become infectious person.exposed = False person.infectious = False person.recovered = True self.results['recoveries'][t] += 1 else: self.results['n_infectious'][ t] += 1 # Count this person as infectious n_contacts = cv.pt( person.contacts ) # Draw the number of Poisson contacts for this person contact_inds = cv.choose_people( max_ind=len(self.people), n=n_contacts) # Choose people at random for contact_ind in contact_inds: exposure = cv.bt(self['r_contact'] ) # Check for exposure per person if exposure: target_person = self.people[contact_ind] if target_person.susceptible: # Skip people who are not susceptible self.results['infections'][t] += 1 target_person.susceptible = False target_person.exposed = True target_person.date_exposed = t incub_pars = dict(dist='normal_int', par1=self['incub'], par2=self['incub_std']) dur_pars = dict(dist='normal_int', par1=self['dur'], par2=self['dur_std']) incub_dist = cv.sample(**incub_pars) dur_dist = cv.sample(**dur_pars) target_person.date_infectious = t + incub_dist target_person.date_recovered = target_person.date_infectious + dur_dist if verbose >= 2: print( f' Person {person.uid} infected person {target_person.uid}!' ) # Count people who recovered if person.recovered: self.results['n_recovered'][t] += 1 # Implement testing -- this is outside of the loop over people, but inside the loop over time if t < len( daily_tests ): # Don't know how long the data is, ensure we don't go past the end n_tests = daily_tests.iloc[t] # Number of tests for this day if n_tests and not pl.isnan( n_tests): # There are tests this day self.results['tests'][ t] = n_tests # Store the number of tests test_probs = pl.array(list(test_probs.values())) test_probs /= test_probs.sum() test_inds = cv.choose_people_weighted(probs=test_probs, n=n_tests) uids_to_pop = [] for test_ind in test_inds: tested_person = self.people[test_ind] if tested_person.infectious and cv.bt( self['sensitivity'] ): # Person was tested and is true-positive self.results['diagnoses'][t] += 1 tested_person.diagnosed = True if self['evac_positives']: uids_to_pop.append(tested_person.uid) if verbose >= 2: print( f' Person {person.uid} was diagnosed!' ) for uid in uids_to_pop: # Remove people from the ship once they're diagnosed self.off_ship[uid] = self.people.pop(uid) # Implement quarantine if t == self['quarantine']: if verbose >= 1: print(f'Implementing quarantine on day {t}...') for person in self.people.values(): if 'quarantine_eff' in self.pars.keys(): quarantine_eff = self['quarantine_eff'] # Both else: if person.crew: quarantine_eff = self['quarantine_eff_c'] # Crew else: quarantine_eff = self['quarantine_eff_g'] # Guests person.contacts *= quarantine_eff # Implement testing change if t == self['testing_change']: if verbose >= 1: print(f'Implementing testing change on day {t}...') self['symptomatic'] *= self[ 'testing_symptoms'] # Reduce the proportion of symptomatic testing # Implement evacuations if t < len(evacuated): n_evacuated = evacuated.iloc[ t] # Number of evacuees for this day if n_evacuated and not pl.isnan( n_evacuated ): # There are evacuees this day # TODO -- refactor with n_tests if verbose >= 1: print(f'Implementing evacuation on day {t}') evac_inds = cv.choose_people(max_ind=len(self.people), n=n_evacuated) uids_to_pop = [] for evac_ind in evac_inds: evac_person = self.people[evac_ind] if evac_person.infectious and cv.bt( self['sensitivity']): self.results['evac_diagnoses'][t] += 1 uids_to_pop.append(evac_person.uid) for uid in uids_to_pop: # Remove people from the ship once they're diagnosed self.off_ship[uid] = self.people.pop(uid) # Compute cumulative results self.results['cum_exposed'] = pl.cumsum(self.results['infections']) self.results['cum_tested'] = pl.cumsum(self.results['tests']) self.results['cum_diagnosed'] = pl.cumsum(self.results['diagnoses']) # Compute likelihood if calc_likelihood: self.likelihood() # Tidy up self.results['ready'] = True elapsed = sc.toc(T, output=True) if verbose >= 1: print(f'\nRun finished after {elapsed:0.1f} s.\n') summary = self.summary_stats() print(f"""Summary: {summary['n_susceptible']:5.0f} susceptible {summary['n_exposed']:5.0f} exposed {summary['n_infectious']:5.0f} infectious """) if do_plot: self.plot(**kwargs) return self.results