def test_save_and_load_full_snapshot(): nplaces = 10 npeople = 100 nslots = 16 generated_snapshot = Snapshot.random(nplaces=nplaces, npeople=npeople, nslots=nslots) snapshot_path = "tests/opencl/random.npz" generated_snapshot.save(snapshot_path) loaded_snapshot = Snapshot.load_full_snapshot(snapshot_path) os.remove(snapshot_path) assert np.array_equal(generated_snapshot.buffers.people_ages, loaded_snapshot.buffers.people_ages) assert np.all( np.isclose(generated_snapshot.buffers.place_coords, loaded_snapshot.buffers.place_coords)) assert loaded_snapshot.nplaces == nplaces assert loaded_snapshot.npeople == npeople assert loaded_snapshot.nslots == nslots
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_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_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)
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_load_existing_snapshot(): # Load initial snapshot generated from the SnapshotConverter test loaded_snapshot = Snapshot.load_full_snapshot( "tests/opencl/test_snapshot.npz") expected_nplaces = 8 expected_npeople = 3 expected_nslots = 16 assert loaded_snapshot.nplaces == expected_nplaces assert loaded_snapshot.npeople == expected_npeople assert loaded_snapshot.nslots == expected_nslots expected_people_place_ids = np.full((expected_npeople, expected_nslots), sentinel_value) expected_people_place_ids[0][0:4] = [0, 5, 7, 3] expected_people_place_ids[1][0:4] = [1, 5, 6, 4] expected_people_place_ids[2][0:4] = [2, 3, 7, 6] expected_people_place_ids = expected_people_place_ids.flatten() expected_people_flows = np.zeros((expected_npeople, expected_nslots)) expected_people_flows[0][0:4] = [0.8, 0.1, 0.06, 0.04] expected_people_flows[1][0:4] = [0.7, 0.18, 0.09, 0.03] expected_people_flows[2][0:4] = [0.6, 0.2, 0.16, 0.04] expected_people_flows = expected_people_flows.flatten() assert np.array_equal(expected_people_place_ids, loaded_snapshot.buffers.people_place_ids) assert np.all( np.isclose(expected_people_flows, loaded_snapshot.buffers.people_baseline_flows))
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 test_more_overweight_become_symptomatic(): 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=2, corresponding to overweight people_obesity_test_data = np.full(npeople, 2, 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() base_proportion_asymptomatic = 0.79 params.proportion_asymptomatic = base_proportion_asymptomatic overweight_sympt_mplier = 1.46 params.overweight_sympt_mplier = overweight_sympt_mplier 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 base_proportion_symptomatic = 1 - base_proportion_asymptomatic expected_proportion_presymptomatic = overweight_sympt_mplier * base_proportion_symptomatic expected_proportion_asymptomatic = 1 - expected_proportion_presymptomatic 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 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_switch_to_healthier_population(): snapshot = Snapshot.random(nplaces=50, npeople=8, nslots=5) snapshot.buffers.people_obesity[:] = np.array([0, 0, 1, 1, 2, 2, 4, 4]) snapshot.switch_to_healthier_population() result_obesity = snapshot.buffers.people_obesity expected_obesity = np.array([0, 0, 1, 1, 1, 1, 3, 3]) assert np.array_equal(result_obesity, expected_obesity)
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_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_copy_snapshot(): snapshot = Snapshot.random(nplaces=50, npeople=8, nslots=5) snapshot_copy = copy.deepcopy(snapshot) # check buffer contents equal after copy assert np.array_equal(snapshot.buffers.people_baseline_flows, snapshot_copy.buffers.people_baseline_flows) # mutate original snapshot and check that the copy is no longer equal snapshot.buffers.people_baseline_flows[:] = snapshot.buffers.people_baseline_flows * 2.5 assert not np.array_equal(snapshot.buffers.people_baseline_flows, snapshot_copy.buffers.people_baseline_flows)
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_seed_prngs(): snapshot = Snapshot.random(nplaces=50, npeople=100, nslots=5) prngs_before = np.zeros(400, dtype=np.uint32) prngs_before[:] = snapshot.buffers.people_prngs snapshot.seed_prngs(46) prngs_after = np.zeros(400, dtype=np.uint32) prngs_after[:] = snapshot.buffers.people_prngs snapshot.seed_prngs(46) prngs_after_after = np.zeros(400, dtype=np.uint32) prngs_after_after[:] = snapshot.buffers.people_prngs assert np.any(prngs_before != prngs_after) assert np.all(prngs_after == prngs_after_after)
def test_generate_zeros_snapshot(): nplaces = 10 npeople = 100 nslots = 16 snapshot = Snapshot.zeros(nplaces=nplaces, npeople=npeople, nslots=nslots) assert snapshot.buffers.place_activities.shape[0] == nplaces assert snapshot.buffers.people_ages.shape[0] == npeople assert snapshot.buffers.people_place_ids.shape[0] == npeople * nslots assert snapshot.buffers.place_coords.shape[0] == nplaces * 2 # check all zeros assert not snapshot.buffers.place_activities.any() assert not snapshot.buffers.people_ages.any() assert not snapshot.buffers.people_place_ids.any() assert not snapshot.buffers.place_coords.any()
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 run_opencl_model(individuals_df, activity_locations, time_activity_multiplier, iterations, data_dir, base_dir, use_gui, use_gpu, use_cache, initialise, calibration_params, disease_params): snapshot_cache_filepath = base_dir + "/microsim/opencl/snapshots/cache.npz" # Choose whether to load snapshot file from cache, or create a snapshot from population data if not use_cache or not os.path.exists(snapshot_cache_filepath): print("\nGenerating Snapshot for OpenCL model") snapshot_converter = SnapshotConvertor(individuals_df, activity_locations, time_activity_multiplier, data_dir) snapshot = snapshot_converter.generate_snapshot() snapshot.save(snapshot_cache_filepath ) # store snapshot in cache so we can load later else: # load cached snapshot snapshot = Snapshot.load_full_snapshot(path=snapshot_cache_filepath) # set the random seed of the model snapshot.seed_prngs(42) # set params if calibration_params is not None and disease_params is not None: snapshot.update_params( create_params(calibration_params, disease_params)) if disease_params["improve_health"]: print("Switching to healthier population") snapshot.switch_to_healthier_population() if initialise: print( "Have finished initialising model. -init flag is set so not running it. Exitting" ) return run_mode = "GUI" if use_gui else "headless" print(f"\nRunning OpenCL model in {run_mode} mode") run_opencl(snapshot, iterations, data_dir, use_gui, use_gpu, num_seed_days=disease_params["seed_days"], quiet=False)
def generate_snapshot(self): people_ages = self.get_people_ages() people_obesity = self.get_people_obesity() people_cvd = self.get_people_cvd() people_diabetes = self.get_people_diabetes() people_blood_pressure = self.get_people_blood_pressure() area_codes = self.get_people_area_codes() not_home_probs = self.get_not_home_probs() people_place_ids, people_flows = self.get_people_place_data() place_activities = self.get_place_data() place_coordinates = self.get_place_coordinates() return Snapshot.from_arrays(people_ages, people_obesity, people_cvd, people_diabetes, people_blood_pressure, people_place_ids, people_flows, area_codes, not_home_probs, place_activities, place_coordinates, self.lockdown_multipliers)
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 draw_snapshots_window(self, width, height): imgui.set_next_window_size(width / 6, height / 4) imgui.set_next_window_position(width * 5 / 6, height * 3 / 4) imgui.begin("Snapshots", flags=default_flags) clicked, self.selected_snapshot = imgui.listbox( "", self.selected_snapshot, self.snapshots) if imgui.button("Load Selected"): self.snapshot = Snapshot.load_full_snapshot( f"snapshots/{self.snapshots[self.selected_snapshot]}") self.simulator.upload_all(self.snapshot.buffers) self.simulator.time = self.snapshot.time self.upload_hazards(self.snapshot.buffers.place_hazards) self.upload_locations(self.snapshot.buffers.place_coords) self.upload_links(self.snapshot.buffers.people_place_ids) self.current_snapshot = self.selected_snapshot if imgui.button("Save"): self.simulator.download_all(self.snapshot.buffers) self.snapshot.time = self.simulator.time self.snapshot.save( f"snapshots/{self.snapshots[self.current_snapshot]}") if imgui.button("Save As..."): self.show_saveas = True imgui.end()
def test_summary_update(): npeople = 50 + 34 + 101 + 551 summary = Summary(snapshot=Snapshot.random(nplaces=10, npeople=npeople, nslots=10), max_time=20) time = 10 statuses = np.concatenate(( np.full(50, 0), np.full(34, 1), np.full(101, 4), np.full(551, 6), )) np.random.shuffle(statuses) summary.update(time, statuses) assert summary.total_counts[0][time] == 50 assert summary.total_counts[1][time] == 34 assert summary.total_counts[2][time] == 0 assert summary.total_counts[3][time] == 0 assert summary.total_counts[4][time] == 101 assert summary.total_counts[5][time] == 0 assert summary.total_counts[6][time] == 551
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_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_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_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()