def run_opencl(snapshot, iterations=100, data_dir="./data", use_gui=True, use_gpu=False, num_seed_days=5, quiet=False): """ Entry point for running the OpenCL simulation either with the UI or in headless mode. NB: in order to write output data for the OpenCL dashboard you must run in headless mode. """ if not quiet: print(f"\nSnapshot Size:\t{int(snapshot.num_bytes() / 1000000)} MB\n") # Create a simulator and upload the snapshot data to the OpenCL device simulator = Simulator(snapshot, use_gpu, num_seed_days=num_seed_days) simulator.upload_all(snapshot.buffers) if not quiet: print( f"Platform:\t{simulator.platform_name()}\nDevice:\t\t{simulator.device_name()}\n" ) if use_gui: run_with_gui(simulator, snapshot) else: summary, final_state = run_headless(simulator, snapshot, iterations, quiet) store_summary_data(summary, store_detailed_counts=True, data_dir=data_dir)
def test_seed_initial_infections_some_low_risk(): npeople = 200 snapshot = Snapshot.random(nplaces, npeople, nslots) # set all people as high risk snapshot.area_codes = np.full(npeople, "E02004187") # low risk area code snapshot.area_codes[1:4] = "E02004143" # high risk area code snapshot.not_home_probs = np.full(npeople, 0.0) snapshot.not_home_probs[1:4] = 0.8 num_seed_days = 5 simulator = Simulator(snapshot, gpu=False, num_seed_days=num_seed_days) simulator.upload_all(snapshot.buffers) people_statuses_before = np.zeros(npeople, dtype=np.uint32) simulator.download("people_statuses", people_statuses_before) # assert that no people are infected before seeding assert not people_statuses_before.any() # run one step with seeding and check number of infections simulator.step_with_seeding() people_statuses_after = np.zeros(snapshot.npeople, dtype=np.uint32) simulator.download("people_statuses", people_statuses_after) expected_num_infections = 3 # taken from devon_initial_cases.csv file num_people_infected = np.count_nonzero(people_statuses_after) assert num_people_infected == expected_num_infections
def test_susceptible_become_infected(): snapshot = Snapshot.random(nplaces, npeople, nslots) test_hazard = 0.4 people_hazards_test_data = np.full(npeople, test_hazard, dtype=np.float32) people_statuses_test_data = np.full(npeople, DiseaseStatus.Susceptible.value, dtype=np.uint32) people_transition_times_test_data = np.zeros(npeople, dtype=np.uint32) snapshot.buffers.people_hazards[:] = people_hazards_test_data snapshot.buffers.people_statuses[:] = people_statuses_test_data snapshot.buffers.people_transition_times[:] = people_transition_times_test_data simulator = Simulator(snapshot, gpu=False) simulator.upload_all(snapshot.buffers) people_statuses_before = np.zeros(npeople, dtype=np.uint32) simulator.download("people_statuses", people_statuses_before) assert np.array_equal(people_statuses_before, people_statuses_test_data) simulator.step_kernel("people_update_statuses") people_statuses_after = np.zeros(npeople, dtype=np.uint32) simulator.download("people_statuses", people_statuses_after) num_exposed = np.count_nonzero(people_statuses_after == DiseaseStatus.Exposed.value) proportion_exposed = num_exposed / npeople expected_proportion_infected = 1.0 - np.exp(-test_hazard) assert np.isclose(expected_proportion_infected, proportion_exposed, atol=0.01)
def test_seed_initial_infections_most_people_low_risk(): npeople = 200 snapshot = Snapshot.zeros(nplaces, npeople, nslots) # set all people as low risk snapshot.area_codes = np.full(npeople, "E02004187") # low risk area code snapshot.not_home_probs = np.full(npeople, 0.0) # set 3 people to be high risk snapshot.area_codes[1:4] = "E02004143" # high risk area code snapshot.not_home_probs[1:4] = 0.8 num_seed_days = 5 simulator = Simulator(snapshot, gpu=False, num_seed_days=num_seed_days) simulator.upload_all(snapshot.buffers) people_statuses_before = np.zeros(npeople, dtype=np.uint32) simulator.download("people_statuses", people_statuses_before) # assert that no people are infected before seeding assert not people_statuses_before.any() # run one step with seeding and check number of infections simulator.step_with_seeding() people_statuses_after = np.zeros(snapshot.npeople, dtype=np.uint32) simulator.download("people_statuses", people_statuses_after) # only high risk people will get infected (eg. in a high risk area code and with a high not_home_prob) # so only the 3 high risk people should be infected by the seeding expected_num_infections = 3 num_people_infected = np.count_nonzero(people_statuses_after) assert num_people_infected == expected_num_infections
def run_opencl_model(i: int, iterations: int, snapshot_filepath: str, params, opencl_dir: str, use_gpu: bool, use_healthier_pop: bool, store_detailed_counts: bool = True, quiet=False) -> (np.ndarray, np.ndarray): """ Run the OpenCL model. :param i: Simulation number (i.e. if run as part of an ensemble) :param iterations: Number of iterations to ru the model for :param snapshot_filepath: Location of the snapshot (the model must have already been initialised) :param params: a Params object containing the parameters used to define how the model behaves :param opencl_dir: Location of the OpenCL code :param use_gpu: Whether to use the GPU to process it or not :param store_detailed_counts: Whether to store the age distributions for diseases (default True, if false then the model runs much more quickly). :param quiet: Whether to print a message when the model starts :return: A summary python array that contains the results for each iteration and a final state """ # load snapshot snapshot = Snapshot.load_full_snapshot(path=snapshot_filepath) prev_obesity = np.copy(snapshot.buffers.people_obesity) if use_healthier_pop: snapshot.switch_to_healthier_population() print("testing obesity arrays not equal") print(np.mean(prev_obesity)) print(np.mean(snapshot.buffers.people_obesity)) # assert not np.array_equal(prev_obesity, snapshot.buffers.people_obesity) # print("arrays not equal") # set params snapshot.update_params(params) # set the random seed of the model for each repetition, otherwise it is completely deterministic snapshot.seed_prngs(i) # Create a simulator and upload the snapshot data to the OpenCL device simulator = Simulator(snapshot, opencl_dir=opencl_dir, gpu=use_gpu) simulator.upload_all(snapshot.buffers) if not quiet: print(f"Running simulation {i + 1}.") summary, final_state = run_headless( simulator, snapshot, iterations, quiet=True, store_detailed_counts=store_detailed_counts) return summary, final_state
def test_diabetes_higher_mortality(): snapshot = Snapshot.random(nplaces, npeople, nslots) people_ages_test_data = np.full(npeople, 65, dtype=np.uint16) people_statuses_test_data = np.full(npeople, DiseaseStatus.Symptomatic.value, dtype=np.uint32) # set all people to diabetes=1 people_diabetes_test_data = np.ones(npeople, dtype=np.uint8) people_transition_times_test_data = np.full(npeople, 1, dtype=np.uint32) snapshot.buffers.people_ages[:] = people_ages_test_data snapshot.buffers.people_statuses[:] = people_statuses_test_data snapshot.buffers.people_diabetes[:] = people_diabetes_test_data snapshot.buffers.people_transition_times[:] = people_transition_times_test_data params = Params() diabetes_multiplier = 1.4 params.diabetes_multiplier = diabetes_multiplier snapshot.update_params(params) simulator = Simulator(snapshot, gpu=False) simulator.upload_all(snapshot.buffers) people_statuses_before = np.zeros(npeople, dtype=np.uint32) simulator.download("people_statuses", people_statuses_before) assert np.array_equal(people_statuses_before, people_statuses_test_data) # run two timesteps so statuses should change simulator.step_kernel("people_update_statuses") simulator.step_kernel("people_update_statuses") # assert that statuses change to either recovered or dead in the correct proportion people_statuses_after_two_steps = np.zeros(npeople, dtype=np.uint32) simulator.download("people_statuses", people_statuses_after_two_steps) num_recovered = np.count_nonzero( people_statuses_after_two_steps == DiseaseStatus.Recovered.value) proportion_recovered = num_recovered / npeople num_dead = np.count_nonzero( people_statuses_after_two_steps == DiseaseStatus.Dead.value) proportion_dead = num_dead / npeople # expected recovery probability for ages 60-70 expected_proportion_dead = 0.0193 expected_proportion_dead *= diabetes_multiplier expected_proportion_recovered = 1 - expected_proportion_dead assert np.isclose(expected_proportion_recovered, proportion_recovered, atol=0.01) assert np.isclose(expected_proportion_dead, proportion_dead, atol=0.01)
def test_correct_send_hazard(): # Set up and upload the test data snapshot = Snapshot.random(nplaces, npeople, nslots) people_place_ids = np.full((npeople, nslots), sentinel_value, dtype=np.uint32) people_place_ids[0][0:4] = [0, 5, 7, 3] people_place_ids[1][0:4] = [1, 5, 6, 4] people_place_ids[2][0:4] = [2, 3, 7, 6] people_flows = np.zeros((npeople, nslots), dtype=np.float32) people_flows[0][0:4] = [0.8, 0.1, 0.06, 0.04] people_flows[1][0:4] = [0.7, 0.18, 0.09, 0.03] people_flows[2][0:4] = [0.6, 0.2, 0.16, 0.04] people_statuses = np.full(npeople, DiseaseStatus.Symptomatic.value, dtype=np.uint32) snapshot.buffers.people_flows[:] = people_flows.flatten() snapshot.buffers.people_place_ids[:] = people_place_ids.flatten() snapshot.buffers.people_statuses[:] = people_statuses snapshot.buffers.place_activities[:] = np.random.randint(4, size=nplaces, dtype=np.uint32) params = Params() params.place_hazard_multipliers = np.ones(5, dtype=np.float32) snapshot.update_params(params) simulator = Simulator(snapshot, gpu=False) simulator.upload_all(snapshot.buffers) # Run the kernel simulator.step_kernel("people_send_hazards") # Download the result place_hazards = np.zeros(nplaces, dtype=np.uint32) simulator.download("place_hazards", place_hazards) place_counts = np.zeros(nplaces, dtype=np.uint32) simulator.download("place_counts", place_counts) # Assert expected results expected_place_hazards_floats = np.array( [0.8, 0.7, 0.6, 0.24, 0.03, 0.28, 0.13, 0.22], dtype=np.float32) expected_place_hazards = (fixed_factor * expected_place_hazards_floats).astype(np.uint32) expected_place_counts = np.array([1, 1, 1, 2, 1, 2, 2, 2], dtype=np.uint32) assert np.allclose(expected_place_hazards, place_hazards) assert np.array_equal(expected_place_counts, place_counts)
def test_symptomatic_become_recovered_or_dead_old_age(): snapshot = Snapshot.random(nplaces, npeople, nslots) people_ages_test_data = np.full(npeople, 92, dtype=np.uint16) people_statuses_test_data = np.full(npeople, DiseaseStatus.Symptomatic.value, dtype=np.uint32) people_transition_times_test_data = np.full(npeople, 1, dtype=np.uint32) snapshot.buffers.people_ages[:] = people_ages_test_data snapshot.buffers.people_statuses[:] = people_statuses_test_data snapshot.buffers.people_transition_times[:] = people_transition_times_test_data simulator = Simulator(snapshot, gpu=False) simulator.upload_all(snapshot.buffers) people_statuses_before = np.zeros(npeople, dtype=np.uint32) simulator.download("people_statuses", people_statuses_before) assert np.array_equal(people_statuses_before, people_statuses_test_data) # run two timesteps so statuses should change simulator.step_kernel("people_update_statuses") simulator.step_kernel("people_update_statuses") # assert that statuses change to either recovered or dead in the correct proportion people_statuses_after_two_steps = np.zeros(npeople, dtype=np.uint32) simulator.download("people_statuses", people_statuses_after_two_steps) num_recovered = np.count_nonzero( people_statuses_after_two_steps == DiseaseStatus.Recovered.value) proportion_recovered = num_recovered / npeople num_dead = np.count_nonzero( people_statuses_after_two_steps == DiseaseStatus.Dead.value) proportion_dead = num_dead / npeople # expected recovery probability for ages 80+ expected_proportion_dead = 0.1737 expected_proportion_recovered = 1 - expected_proportion_dead assert np.isclose(expected_proportion_recovered, proportion_recovered, atol=0.01) assert np.isclose(expected_proportion_dead, proportion_dead, atol=0.01)
def test_correct_send_hazard(): # Set up and upload the test data snapshot = Snapshot.random(nplaces, npeople, nslots) place_hazards_floats = np.array([0.2, 0.0, 0.5, 0.03, 0.0123, 0.22, 0.0001, 0.73], dtype=np.float32) place_hazards = (fixed_factor * place_hazards_floats).astype(np.uint32) people_place_ids = np.full((npeople, nslots), sentinel_value, dtype=np.uint32) people_place_ids[0][0:4] = [1, 6, 0, 4] people_place_ids[1][0:4] = [2, 6, 7, 5] people_place_ids[2][0:4] = [3, 4, 0, 7] people_flows = np.zeros((npeople, nslots), dtype=np.float32) people_flows[0][0:4] = [0.6, 0.2, 0.16, 0.04] people_flows[1][0:4] = [0.7, 0.18, 0.09, 0.03] people_flows[2][0:4] = [0.8, 0.1, 0.06, 0.04] people_statuses = np.full(npeople, DiseaseStatus.Susceptible.value, dtype=np.uint32) people_statuses[0] = DiseaseStatus.Symptomatic.value people_hazards = np.zeros(npeople, dtype=np.float32) snapshot.buffers.people_flows[:] = people_flows.flatten() snapshot.buffers.people_place_ids[:] = people_place_ids.flatten() snapshot.buffers.people_statuses[:] = people_statuses snapshot.buffers.place_hazards[:] = place_hazards snapshot.buffers.people_hazards[:] = people_hazards simulator = Simulator(snapshot, gpu=False) simulator.upload_all(snapshot.buffers) # Run the kernel simulator.step_kernel("people_recv_hazards") # Download the result people_hazards = np.zeros(npeople, dtype=np.float32) simulator.download("people_hazards", people_hazards) # Assert expected results expected_people_hazards = np.array([0.0, 0.422318, 0.06643], dtype=np.float32) assert np.allclose(expected_people_hazards, people_hazards)
def test_exposed_become_asymptomatic_or_presymptomatic(): snapshot = Snapshot.random(nplaces, npeople, nslots) people_statuses_test_data = np.full(npeople, DiseaseStatus.Exposed.value, dtype=np.uint32) people_transition_times_test_data = np.full(npeople, 1, dtype=np.uint32) # set all people to obesity=0, corresponding to normal BMI people_obesity_test_data = np.full(npeople, 0, dtype=np.uint8) people_transition_times_test_data = np.full(npeople, 1, dtype=np.uint32) people_ages_test_data = np.full(npeople, 18, dtype=np.uint16) snapshot.buffers.people_statuses[:] = people_statuses_test_data snapshot.buffers.people_obesity[:] = people_obesity_test_data snapshot.buffers.people_transition_times[:] = people_transition_times_test_data snapshot.buffers.people_ages[:] = people_ages_test_data params = Params() expected_proportion_asymptomatic = 0.79 params.proportion_asymptomatic = expected_proportion_asymptomatic snapshot.update_params(params) simulator = Simulator(snapshot, gpu=False) simulator.upload_all(snapshot.buffers) people_statuses_before = np.zeros(npeople, dtype=np.uint32) simulator.download("people_statuses", people_statuses_before) assert np.array_equal(people_statuses_before, people_statuses_test_data) simulator.step_kernel("people_update_statuses") # check that statuses don't change after one step people_statuses_after_one_step = np.zeros(npeople, dtype=np.uint32) simulator.download("people_statuses", people_statuses_after_one_step) assert np.array_equal(people_statuses_after_one_step, people_statuses_test_data) # run another timestep, this time statuses should change simulator.step_kernel("people_update_statuses") # assert that statuses change to either symptomatic or asymptomatic in the correct proportion people_statuses_after_two_steps = np.zeros(npeople, dtype=np.uint32) simulator.download("people_statuses", people_statuses_after_two_steps) num_asymptomatic = np.count_nonzero(people_statuses_after_two_steps == DiseaseStatus.Asymptomatic.value) result_proportion_asymptomatic = num_asymptomatic / npeople num_presymptomatic = np.count_nonzero(people_statuses_after_two_steps == DiseaseStatus.Presymptomatic.value) result_proportion_presymptomatic = num_presymptomatic / npeople expected_proportion_presymptomatic = 1 - expected_proportion_asymptomatic assert np.isclose(expected_proportion_asymptomatic, result_proportion_asymptomatic, atol=0.01) assert np.isclose(expected_proportion_presymptomatic, result_proportion_presymptomatic, atol=0.01)
def test_infection_transition_times_distribution(visualize=False): npeople = 1000000 snapshot = Snapshot.random(nplaces, npeople, nslots) test_hazard = 0.9 people_hazards_test_data = np.full(npeople, test_hazard, dtype=np.float32) people_statuses_test_data = np.full(npeople, DiseaseStatus.Presymptomatic.value, dtype=np.uint32) people_transition_times_test_data = np.zeros(npeople, dtype=np.uint32) snapshot.buffers.people_hazards[:] = people_hazards_test_data snapshot.buffers.people_statuses[:] = people_statuses_test_data snapshot.buffers.people_transition_times[:] = people_transition_times_test_data params = Params() infection_log_scale = 0.75 infection_mode = 7.0 params.infection_log_scale = infection_log_scale params.infection_mode = infection_mode snapshot.update_params(params) simulator = Simulator(snapshot, gpu=False) simulator.upload_all(snapshot.buffers) simulator.step_kernel("people_update_statuses") people_statuses_after = np.zeros(npeople, dtype=np.uint32) simulator.download("people_statuses", people_statuses_after) people_transition_times_after = np.zeros(npeople, dtype=np.uint32) simulator.download("people_transition_times", people_transition_times_after) # Check that transition times are distributed with a log-normal distribution adjusted_transition_times = people_transition_times_after + 1 mean = adjusted_transition_times.mean() std_dev = adjusted_transition_times.std() mode = scipy.stats.mode(adjusted_transition_times)[0][0] meanlog = infection_log_scale**2 + np.log(infection_mode) expected_samples = np.random.lognormal(mean=meanlog, sigma=infection_log_scale, size=npeople) # round samples to nearest integer expected_samples = np.rint(expected_samples) expected_mean = expected_samples.mean() expected_std_dev = expected_samples.std() expected_mode = scipy.stats.mode(expected_samples)[0][0] # Float to integer rounding and clamping at zero makes the original random numbers hard # to recover so we have slightly larger tolerances here to avoid false negatives. assert np.isclose(expected_mean, mean, atol=0.7) assert np.isclose(expected_std_dev, std_dev, atol=0.4) assert np.isclose(expected_mode, mode, atol=1.0) # check that mode is similar to original mode parameter assert np.isclose(infection_mode, mode, atol=1.0) if visualize: # show histogram of distribution fig, ax = plt.subplots(1, 1) ax.hist(adjusted_transition_times, bins=50, range=[0, 60]) plt.title("Result Samples") plt.show() fig, ax = plt.subplots(1, 1) ax.hist(expected_samples, bins=50, range=[0, 60]) plt.title("Expected Samples") plt.show()
def test_transmission_times_decremented(): snapshot = Snapshot.random(nplaces, npeople, nslots) people_transition_times_test_data = np.full(npeople, 3, dtype=np.uint32) snapshot.buffers.people_transition_times[:] = people_transition_times_test_data simulator = Simulator(snapshot, gpu=False) simulator.upload_all(snapshot.buffers) # check decremented after one step simulator.step_kernel("people_update_statuses") people_transmission_times_after = np.zeros(npeople, dtype=np.uint32) simulator.download("people_transition_times", people_transmission_times_after) expected_people_transition_times = np.full(npeople, 2, dtype=np.uint32) assert np.array_equal(expected_people_transition_times, people_transmission_times_after) # check decremented after another step simulator.step_kernel("people_update_statuses") people_transmission_times_after = np.zeros(npeople, dtype=np.uint32) simulator.download("people_transition_times", people_transmission_times_after) expected_people_transition_times = np.full(npeople, 1, dtype=np.uint32) assert np.array_equal(expected_people_transition_times, people_transmission_times_after)
def test_correct_flow_calculation_no_lockdown(): snapshot = Snapshot.random(nplaces, npeople, nslots) people_place_ids_test_data = np.full((npeople, nslots), sentinel_value, dtype=np.uint32) people_place_ids_test_data[0][0:4] = [0, 5, 7, 3] people_place_ids_test_data[1][0:4] = [1, 5, 6, 4] people_place_ids_test_data[2][0:4] = [2, 3, 7, 6] people_flows_test_data = np.zeros((npeople, nslots), dtype=np.float32) people_flows_test_data[0][0:4] = [0.8, 0.1, 0.06, 0.04] people_flows_test_data[1][0:4] = [0.7, 0.18, 0.09, 0.03] people_flows_test_data[2][0:4] = [0.6, 0.2, 0.16, 0.04] people_statuses_test_data = np.full(npeople, DiseaseStatus.Susceptible.value, dtype=np.uint32) symptomatic_person_id = 1 people_statuses_test_data[symptomatic_person_id] = np.uint32( DiseaseStatus.Symptomatic.value) place_activities_test_data = np.full(nplaces, Activity.Retail.value, dtype=np.uint32) place_activities_test_data[:3] = Activity.Home.value snapshot.buffers.people_baseline_flows[:] = people_flows_test_data.flatten( ) snapshot.buffers.people_flows[:] = people_flows_test_data.flatten() snapshot.buffers.people_place_ids[:] = people_place_ids_test_data.flatten() snapshot.buffers.people_statuses[:] = people_statuses_test_data snapshot.buffers.place_activities[:] = place_activities_test_data params = Params() symptomatic_multiplier = 0.5 params.symptomatic_multiplier = symptomatic_multiplier snapshot.update_params(params) simulator = Simulator(snapshot, gpu=False) simulator.upload_all(snapshot.buffers) people_flows_before = np.zeros(npeople * nslots, dtype=np.float32) simulator.download("people_flows", people_flows_before) expected_people_flows_before = people_flows_test_data.flatten() assert np.array_equal(people_flows_before, expected_people_flows_before) simulator.step_kernel("people_update_flows") people_flows_after = np.zeros(npeople * nslots, dtype=np.float32) simulator.download("people_flows", people_flows_after) # adjust symptomatic persons flows according to symptomatic multiplier expected_people_flows_after = people_flows_test_data expected_people_flows_after[symptomatic_person_id][0:4] = [ 0.85, 0.09, 0.045, 0.015 ] expected_people_flows_after = expected_people_flows_after.flatten() assert not np.array_equal(people_flows_before, people_flows_after) assert np.array_equal(expected_people_flows_after, people_flows_after)
def run(self): # If this is the first data assimilation window, we can just run the model as normal if self.start_day == 0: assert self.current_particle_pop_df is None # Shouldn't have any preivously-created particles # load snapshot snapshot = Snapshot.load_full_snapshot(path=self.snapshot_file) # set params snapshot.update_params(self.params) # Can set the random seed to make it deterministic (None means np will choose one randomly) snapshot.seed_prngs(seed=None) # Create a simulator and upload the snapshot data to the OpenCL device simulator = Simulator(snapshot, opencl_dir=self.opencl_dir, gpu=self.use_gpu) simulator.upload_all(snapshot.buffers) if not self.quiet: # print(f"Running simulation {sim_number + 1}.") print(f"Running simulation") params = Params.fromarray( snapshot.buffers.params ) # XX Why extract Params? Can't just use PARAMS? summary = Summary( snapshot, store_detailed_counts=self.store_detailed_counts, max_time=self.run_length # Total length of the simulation ) # only show progress bar in quiet mode timestep_iterator = range(self.run_length) if self.quiet \ else tqdm(range(self.quiet), desc="Running simulation") iter_count = 0 # Count the total number of iterations # Run for iterations days for _ in timestep_iterator: # Update parameters based on lockdown params.set_lockdown_multiplier(snapshot.lockdown_multipliers, iter_count) simulator.upload("params", params.asarray()) # Step the simulator simulator.step() iter_count += 1 # Update the statuses simulator.download("people_statuses", snapshot.buffers.people_statuses) summary.update(iter_count, snapshot.buffers.people_statuses) if not self.quiet: for i in range(self.run_length): print(f"\nDay {i}") summary.print_counts(i) if not self.quiet: print("\nFinished") # Download the snapshot from OpenCL to host memory # XX This is 'None'. final_state = simulator.download_all(snapshot.buffers) pass else: # Otherwise we need to restart previous models stored in the current_particle_pop_df # XXXX CAN GET OLD MODEL STATES, WITH ALL DISEASE STATUSES, FROM THE DF. TWO ISSUES # 1. But need to work out how to draw these appropriately; can't assume they are each as good as # each other. THIS SHOULD BE OK, surely there's a way to go from the final particles and weights # to the DF of state vectors. Particle ID? Just try it out. # 2. Also: what to do about stochasticity. For a given (global) parameter combination, we will # get quite different results depending on the mode state. - I DON'T THINK THIS IS A PROBLEM. # ABC Commonly used with stochastic models. E.g. https://eprints.lancs.ac.uk/id/eprint/80439/1/mainR1.pdf # raise Exception("Not implemented yet") # Return the current state of the model in a dictionary describing what it is #return {"simulator": simulator} return {"simulator": snapshot}
def test_asymptomatics_add_less_hazard(): # Set up and upload the test data snapshot = Snapshot.random(nplaces, npeople, nslots) people_place_ids = np.full((npeople, nslots), sentinel_value, dtype=np.uint32) people_place_ids[0][0:4] = [0, 5, 7, 3] people_place_ids[1][0:4] = [1, 5, 6, 4] people_place_ids[2][0:4] = [2, 3, 7, 6] people_flows = np.zeros((npeople, nslots), dtype=np.float32) people_flows[0][0:4] = [0.8, 0.1, 0.06, 0.04] people_flows[1][0:4] = [0.7, 0.18, 0.09, 0.03] people_flows[2][0:4] = [0.6, 0.2, 0.16, 0.04] people_statuses = np.full(npeople, DiseaseStatus.Symptomatic.value, dtype=np.uint32) snapshot.buffers.people_flows[:] = people_flows.flatten() snapshot.buffers.people_place_ids[:] = people_place_ids.flatten() snapshot.buffers.people_statuses[:] = people_statuses snapshot.buffers.place_activities[:] = np.random.randint(4, size=nplaces, dtype=np.uint32) params = Params() params.place_hazard_multipliers = np.ones(5, dtype=np.float32) asymptomatic_multiplier = 0.5 params.individual_hazard_multipliers = np.array( [1.0, asymptomatic_multiplier, 1.0]) snapshot.update_params(params) simulator = Simulator(snapshot, gpu=False) simulator.upload_all(snapshot.buffers) # Run the kernel simulator.step_kernel("people_send_hazards") # Download the result place_hazards = np.zeros(nplaces, dtype=np.uint32) simulator.download("place_hazards", place_hazards) place_counts = np.zeros(nplaces, dtype=np.uint32) simulator.download("place_counts", place_counts) # Assert expected results expected_place_hazards_floats = np.array( [0.8, 0.7, 0.6, 0.24, 0.03, 0.28, 0.13, 0.22], dtype=np.float32) expected_place_hazards = (fixed_factor * expected_place_hazards_floats).astype(np.uint32) assert np.allclose(expected_place_hazards, place_hazards) # Change statuses so all people are asymptomatic people_statuses_asymptomatic = np.full(npeople, DiseaseStatus.Asymptomatic.value, dtype=np.uint32) simulator.upload("people_statuses", people_statuses_asymptomatic) # reset place hazards to zero simulator.step_kernel("places_reset") # run kernel with asymptomatic population simulator.step_kernel("people_send_hazards") # Download the result place_hazards_asymptomatic = np.zeros(nplaces, dtype=np.uint32) simulator.download("place_hazards", place_hazards_asymptomatic) # Assert expected results expected_place_hazards_floats_asymptomatic = expected_place_hazards_floats * asymptomatic_multiplier expected_place_hazards_asymptomatic = ( fixed_factor * expected_place_hazards_floats_asymptomatic).astype( np.uint32) assert np.allclose(expected_place_hazards_asymptomatic, place_hazards_asymptomatic) assert place_hazards_asymptomatic.sum() < place_hazards.sum()
def test_places_are_reset_to_zero(): snapshot = Snapshot.random(nplaces, npeople, nslots) hazards_test_data = np.array([4, 3, 5, 1, 6], dtype=np.uint32) counts_test_data = np.array([1, 4, 8, 10, 2], dtype=np.uint32) snapshot.buffers.place_hazards[:] = hazards_test_data snapshot.buffers.place_counts[:] = counts_test_data simulator = Simulator(snapshot, gpu=False) simulator.upload_all(snapshot.buffers) place_hazards_before = np.zeros(nplaces, dtype=np.uint32) simulator.download("place_hazards", place_hazards_before) place_counts_before = np.zeros(nplaces, dtype=np.uint32) simulator.download("place_counts", place_counts_before) assert np.array_equal(place_hazards_before, hazards_test_data) assert np.array_equal(place_counts_before, counts_test_data) simulator.step_kernel("places_reset") # initialise host buffers randomly so we know they are filled to zeros place_hazards_after = np.random.randint(0, 11, nplaces, dtype=np.uint32) simulator.download("place_hazards", place_hazards_after) place_counts_after = np.random.randint(0, 11, nplaces, dtype=np.uint32) simulator.download("place_counts", place_counts_after) assert np.array_equal(place_hazards_after, np.zeros(nplaces, dtype=np.uint32)) assert np.array_equal(place_counts_after, np.zeros(nplaces, dtype=np.uint32))
def test_symptomatic_become_recovered_or_dead_young_age(): # NB: run with more people since chance of young people dying is low npeople = 500000 snapshot = Snapshot.random(nplaces, npeople, nslots) people_ages_test_data = np.full(npeople, 25, dtype=np.uint16) people_statuses_test_data = np.full(npeople, DiseaseStatus.Symptomatic.value, dtype=np.uint32) people_transition_times_test_data = np.full(npeople, 1, dtype=np.uint32) snapshot.buffers.people_ages[:] = people_ages_test_data snapshot.buffers.people_statuses[:] = people_statuses_test_data snapshot.buffers.people_transition_times[:] = people_transition_times_test_data simulator = Simulator(snapshot, gpu=False) simulator.upload_all(snapshot.buffers) people_statuses_before = np.zeros(npeople, dtype=np.uint32) simulator.download("people_statuses", people_statuses_before) assert np.array_equal(people_statuses_before, people_statuses_test_data) simulator.step_kernel("people_update_statuses") # check that statuses don't change after one step people_statuses_after_one_step = np.zeros(npeople, dtype=np.uint32) simulator.download("people_statuses", people_statuses_after_one_step) assert np.array_equal(people_statuses_after_one_step, people_statuses_test_data) # run another timestep, this time statuses should change simulator.step_kernel("people_update_statuses") # assert that statuses change to either recovered or dead in the correct proportion people_statuses_after_two_steps = np.zeros(npeople, dtype=np.uint32) simulator.download("people_statuses", people_statuses_after_two_steps) num_recovered = np.count_nonzero(people_statuses_after_two_steps == DiseaseStatus.Recovered.value) proportion_recovered = num_recovered / npeople num_dead = np.count_nonzero(people_statuses_after_two_steps == DiseaseStatus.Dead.value) proportion_dead = num_dead / npeople # expected recovery probability for ages 20 to 29 expected_proportion_dead = 0.0004 expected_proportion_recovered = 1 - expected_proportion_dead assert np.isclose(expected_proportion_recovered, proportion_recovered, atol=0.0001) assert np.isclose(expected_proportion_dead, proportion_dead, atol=0.0001)
def test_correct_flow_calculation_with_lockdown(): snapshot = Snapshot.random(nplaces, npeople, nslots) people_place_ids_test_data = np.full((npeople, nslots), sentinel_value, dtype=np.uint32) people_place_ids_test_data[0][0:4] = [0, 5, 7, 3] people_place_ids_test_data[1][0:4] = [1, 5, 6, 4] people_place_ids_test_data[2][0:4] = [2, 3, 7, 6] people_flows_test_data = np.zeros((npeople, nslots), dtype=np.float32) people_flows_test_data[0][0:4] = [0.8, 0.1, 0.06, 0.04] people_flows_test_data[1][0:4] = [0.7, 0.18, 0.09, 0.03] people_flows_test_data[2][0:4] = [0.6, 0.2, 0.16, 0.04] people_statuses_test_data = np.full(npeople, DiseaseStatus.Susceptible.value, dtype=np.uint32) symptomatic_person_id = 1 people_statuses_test_data[symptomatic_person_id] = np.uint32( DiseaseStatus.Symptomatic.value) place_activities_test_data = np.full(nplaces, Activity.Retail.value, dtype=np.uint32) place_activities_test_data[:3] = Activity.Home.value snapshot.buffers.people_baseline_flows[:] = people_flows_test_data.flatten( ) snapshot.buffers.people_flows[:] = people_flows_test_data.flatten() snapshot.buffers.people_place_ids[:] = people_place_ids_test_data.flatten() snapshot.buffers.people_statuses[:] = people_statuses_test_data snapshot.buffers.place_activities[:] = place_activities_test_data params = Params() params.set_lockdown_multiplier(lockdown_multipliers, 0) params.symptomatic_multiplier = 0.5 snapshot.buffers.params[:] = params.asarray() simulator = Simulator(snapshot, gpu=False) simulator.upload_all(snapshot.buffers) people_flows_before = np.zeros(npeople * nslots, dtype=np.float32) simulator.download("people_flows", people_flows_before) expected_people_flows_before = people_flows_test_data.flatten() assert np.array_equal(people_flows_before, expected_people_flows_before) simulator.step_kernel("people_update_flows") people_flows_after = np.zeros(npeople * nslots, dtype=np.float32) simulator.download("people_flows", people_flows_after) assert not np.array_equal(people_flows_before, people_flows_after) # assert correct flows for symptomatic person expected_symptomatic_flows_after = np.array([0.85, 0.09, 0.045, 0.015]) symptomatic_person_start_idx = symptomatic_person_id * nslots symptomatic_flows_after = people_flows_after[ symptomatic_person_start_idx:symptomatic_person_start_idx + 4] assert np.allclose(expected_symptomatic_flows_after, symptomatic_flows_after) # assert correct flows for person who is not symptomatic # adjustments calculated using first lockdown multiplier (approx. 0.9084687) expected_non_symptomatic_flows_after = np.array( [0.63661252, 0.18169374, 0.145354992, 0.036338748]) non_symptomatic_person_id = 2 person_start_idx = non_symptomatic_person_id * nslots non_symptomatic_flows_after = people_flows_after[ person_start_idx:person_start_idx + 4] assert np.allclose(expected_non_symptomatic_flows_after, non_symptomatic_flows_after)
def test_all_asymptomatic_become_recovered(): snapshot = Snapshot.random(nplaces, npeople, nslots) people_statuses_test_data = np.full(npeople, DiseaseStatus.Asymptomatic.value, dtype=np.uint32) people_transition_times_test_data = np.full(npeople, 1, dtype=np.uint32) snapshot.buffers.people_statuses[:] = people_statuses_test_data snapshot.buffers.people_transition_times[:] = people_transition_times_test_data simulator = Simulator(snapshot, gpu=False) simulator.upload_all(snapshot.buffers) people_statuses_before = np.zeros(npeople, dtype=np.uint32) simulator.download("people_statuses", people_statuses_before) assert np.array_equal(people_statuses_before, people_statuses_test_data) simulator.step_kernel("people_update_statuses") # check that statuses don't change after one step people_statuses_after_one_step = np.zeros(npeople, dtype=np.uint32) simulator.download("people_statuses", people_statuses_after_one_step) assert np.array_equal(people_statuses_after_one_step, people_statuses_test_data) # run another timestep, this time statuses should change simulator.step_kernel("people_update_statuses") # assert that all statuses change to presymptomatic after two timesteps people_statuses_after_two_steps = np.zeros(npeople, dtype=np.uint32) simulator.download("people_statuses", people_statuses_after_two_steps) assert np.all(people_statuses_after_two_steps == DiseaseStatus.Recovered.value)