diff --git a/malaria.py b/malaria.py index 09e1fa5..010bdde 100644 --- a/malaria.py +++ b/malaria.py @@ -8,7 +8,7 @@ from enum import IntEnum class Model: def __init__(self, width=32, height=32, humandens=0.15, mosquitodens=0.10, immunepct=0.1, mosqinfpct=0.1, hm_infpct=0.5, mh_infpct=0.5, - hinfdiepct=0.01, mhungrypct=0.1, humandiepct=10**-3, + hinfdiepct=0.01, mhungrypct=0.1, humandiepct=10**-6, mosqdiepct=10**-3, mosqnetdens=0.05, time_steps=2000, graphical=True): @@ -25,9 +25,9 @@ class Model: self.immunepct = immunepct # Chance for a mosquito to be infectuous self.mosqinfpct = mosqinfpct - # Chance for a human to be infected by a mosquito bite + # Chance for a mosquito to be infected by a human self.hm_infpct = hm_infpct - # Chance for a mosquito to be infected from biting an infected human + # Chance for a human to be infected from a mosquito self.mh_infpct = mh_infpct # Chance that an infected human dies self.hinfdiepct = hinfdiepct @@ -46,6 +46,17 @@ class Model: self.mosquitos = self.gen_mosquitos() self.nets = self.gen_nets() + # statistics + self.stats = { + "natural deaths": 0, + "malaria deaths": 0, + "total deaths": 0, + "mosquitos fed": 0, + "humans infected": 0, + "mosquitos infected": 0, + "net count": 0 + } + if self.graphical: self.init_draw() @@ -57,6 +68,9 @@ class Model: self.norm = matplotlib.colors.BoundaryNorm(bounds, self.colors.N) def recycle_human(self): + """ + Determine if a human dies of natural causes and then replace them by a new human + """ # Get all living humans humans = np.transpose(np.where(self.grid != Human.DEAD)) @@ -71,6 +85,7 @@ class Model: death_count = len(humans) - humans_survive + self.stats["natural deaths"] += death_count # Pick a random, unpopulated spot births = np.array(random.sample(list(np.transpose(np.where(self.grid == Human.DEAD))), @@ -93,8 +108,40 @@ class Model: # Now let's kill them self.grid[infected[deaths][:, 0], infected[deaths][:, 1]] = Human.DEAD + + self.stats["malaria deaths"] += len(np.where(deaths)[0]) + + def feed(self): + #TODO: dit refactoren? + """ + Feed the mosquitos that want to and can be fed + """ + for mos in self.mosquitos: + if not mos.hungry: + continue + # state of current place on the grid where mosquito lives + state = self.grid[mos.x, mos.y] + + if state != Human.DEAD: + self.stats["mosquitos fed"] += 1 + mos.hungry = False + + # check if healthy human needs to be infected or mosquito becomes infected from eating + if state == Human.HEALTHY and mos.infected and random.uniform(0, 1) < self.mh_infpct: + self.grid[mos.x, mos.y] = Human.INFECTED + self.stats["humans infected"] += 1 + elif state == Human.INFECTED and not mos.infected and random.uniform(0, 1) < self.hm_infpct: + self.stats["mosquitos infected"] += 1 + mos.infected = True + + def determine_hunger(self): + """ + Determines which mosquitos should get hungry + """ + for mos in self.mosquitos: + mos.hungry = not mos.hungry and random.uniform(0, 1) < self.mhungrypct - def get_movementbox(self, x, y): + def get_movementbox(self, x: int, y: int): """ Returns indices of a moore neighbourhood around the given index """ @@ -176,17 +223,36 @@ class Model: p=[1-self.mosqnetdens, self.mosqnetdens], size=(self.width, self.height)) - def run(self): """ This functions runs the simulation """ + print(chr(27) + "[2J") for t in range(self.time_steps): + print("Simulating timestep: {}".format(t), end='\r') self.step() if self.graphical: self.draw(t) - else: - print("Simulating timestep: {}".format(t), end='\r') + + print(chr(27) + "[2J") + self.compile_stats() + self.print_stats() + + def compile_stats(self): + """ + Compiles a comprehensive list of statistics of the simulation + """ + self.stats["total deaths"] = self.stats["malaria deaths"] + self.stats["natural deaths"] + + # print(np.where(self.nets)) + self.stats["net count"] = len(np.where(self.nets)[0]) + + def print_stats(self): + """ + Prints the gathered statistics from the simulation + """ + for stat in self.stats: + print(f"{stat}: {self.stats[stat]}") def step(self): """ @@ -198,9 +264,15 @@ class Model: self.recycle_human() # move mosquitos self.move_mosquitos() + # feed hungry mosquitos + self.feed() + # make mosquitos hungry again + self.determine_hunger() - def draw(self, t: int): + """ + Draws the grid of humans, tents and mosquitos + """ # this function draws the humans plt.title("t={}".format(t)) # draw the grid @@ -240,9 +312,9 @@ class Human(IntEnum): HEALTHY = 1 INFECTED = 2 IMMUNE = 3 - + if __name__ == "__main__": - model = Model(graphical=True) + model = Model(graphical=False) model.run()