From ce64001ef6683dee7a5f45f80e635db462dce6f0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=ADtor=20Fr=C3=B3is?= Date: Sun, 7 Apr 2024 12:56:55 -0300 Subject: [PATCH 1/8] created cholera voronoi example --- examples/cholera_voronoi/README.md | 27 ++++++ .../cholera_voronoi/cholera_voronoi/agents.py | 74 ++++++++++++++++ .../cholera_voronoi/cholera_voronoi/model.py | 87 +++++++++++++++++++ .../cholera_voronoi/cholera_voronoi/server.py | 51 +++++++++++ examples/cholera_voronoi/run.py | 3 + 5 files changed, 242 insertions(+) create mode 100644 examples/cholera_voronoi/README.md create mode 100644 examples/cholera_voronoi/cholera_voronoi/agents.py create mode 100644 examples/cholera_voronoi/cholera_voronoi/model.py create mode 100644 examples/cholera_voronoi/cholera_voronoi/server.py create mode 100644 examples/cholera_voronoi/run.py diff --git a/examples/cholera_voronoi/README.md b/examples/cholera_voronoi/README.md new file mode 100644 index 00000000..cf607d20 --- /dev/null +++ b/examples/cholera_voronoi/README.md @@ -0,0 +1,27 @@ +# Disease dynamics on Voronoi Grid + +This folder contains a implementation of Cholera spread analyzed by John Snow at London Soho district during the 19th century. The physicist discovered contaminated water from Broad Street Pump was the source of disease by drawing a Voronoi diagram around pumps and mapping cholera cases. + +The model has two agents: people and pumps. Pumps can infect people and neighbor pumps. People start as susceptible, can be infected by pumps and recover or die, according to a simple SIR model. Each cell has only one pump and is connected to neighbor cells according to Voronoi's diagram. The model aims to investigate how fast actions oriented by Voronoi diagrams can prevent disease spread. + +## How to Run + +To run the model interactively, run ``mesa runserver`` in this directory. e.g. + +``` + $ mesa runserver +``` + +Then open your browser to [http://127.0.0.1:8521/](http://127.0.0.1:8521/) and press ``run``. + +## Files + +* ``cholera_voronoi/agents.py``: Defines Pump and Person agents. +* ``cholera_voronoi/model.py``: Defines the model itself, initialized with John Snow study about Cholera Spread pump locations. +* ``cholera_voronoi/server.py``: Defines an interactive visualization. +* ``run.py``: Launches the visualization + +## Further reading +- [R Package for Analyzing John Snow's 1854 Cholera Map ](https://github.com/lindbrook/cholera) +- [Why this pattern shows up everywhere in nature | Voronoi Cell Pattern](https://www.youtube.com/watch?v=GafRRl5XRPM&t=183s) +- [John Snow, Cholera, the Broad Street Pump; Waterborne Diseases Then and Now](https://www.ncbi.nlm.nih.gov/pmc/articles/PMC7150208/) \ No newline at end of file diff --git a/examples/cholera_voronoi/cholera_voronoi/agents.py b/examples/cholera_voronoi/cholera_voronoi/agents.py new file mode 100644 index 00000000..f65a33f7 --- /dev/null +++ b/examples/cholera_voronoi/cholera_voronoi/agents.py @@ -0,0 +1,74 @@ +from collections.abc import Sequence +from mesa.experimental.cell_space import CellAgent + +SUSCEPTIBLE = 0 +INFECTIOUS = 1 +REMOVED = 2 + +class Person(CellAgent): + def __init__( + self, + unique_id, + model, + mortality_chance, + recovery_chance + ): + super().__init__(unique_id, model) + self.health_state = SUSCEPTIBLE + self.mortality_chance = mortality_chance + self.recovery_chance = recovery_chance + + def step(self): + if self.health_state == REMOVED: return + + if self.health_state == INFECTIOUS and self.model.random.random() < self.recovery_chance: + self.health_state = SUSCEPTIBLE + self.model.infectious -= 1 + self.model.susceptible += 1 + + if self.health_state == INFECTIOUS and self.model.random.random() < self.mortality_chance: + self.health_state = REMOVED + self.model.infectious -= 1 + self.model.removed += 1 + + +class Pump(CellAgent): + def __init__( + self, + unique_id, + model, + contaminated, + pumps_person_contamination_chance, + pumps_neighbor_contamination_chance, + cases_ratio_to_fix_pump + ): + super().__init__(unique_id, model) + self.state = contaminated + self.pumps_person_contamination_chance = pumps_person_contamination_chance + self.pumps_neighbor_contamination_chance = pumps_neighbor_contamination_chance + self.cases_ratio_to_fix_pump = cases_ratio_to_fix_pump + + def step(self): + if self.state is INFECTIOUS: + # Infect people in the cell + people = [obj for obj in self.cell.agents if isinstance(obj, Person) and obj.health_state is not REMOVED] + for person in people: + if person.health_state is SUSCEPTIBLE and self.model.random.random() < self.pumps_person_contamination_chance: + person.health_state = INFECTIOUS + self.model.susceptible -= 1 + self.model.infectious += 1 + + # Infect neighbor cells + if self.model.random.random() < self.pumps_neighbor_contamination_chance: + neighbor_cell = self.random.choice(list(self.cell._connections)) + neighbor_pump = neighbor_cell.agents[0] + if neighbor_pump.state is SUSCEPTIBLE: + neighbor_pump.state = INFECTIOUS + self.model.infected_pumps += 1 + + # If cases in total is too high, fix pump + cases = sum(1 for a in people if a.health_state is INFECTIOUS) + cases_ratio = cases / (self.model.susceptible + self.model.infectious) + if cases_ratio > self.cases_ratio_to_fix_pump: + self.state = SUSCEPTIBLE + self.model.infected_pumps -= 1 \ No newline at end of file diff --git a/examples/cholera_voronoi/cholera_voronoi/model.py b/examples/cholera_voronoi/cholera_voronoi/model.py new file mode 100644 index 00000000..fb42382b --- /dev/null +++ b/examples/cholera_voronoi/cholera_voronoi/model.py @@ -0,0 +1,87 @@ +import mesa +from collections.abc import Sequence +from .agents import Person, Pump + +SUSCEPTIBLE = 0 +INFECTIOUS = 1 +REMOVED = 2 + +cell_population = [400] * 8 + +points = [ + (9.909976449792431,11.542846828417543), + (0.40972334441912234,14.266853186123692), + (0.0,20.0), + (20.0,5.111991897435429), + (12.566609556906684,1.57960921165571), + (5.232766132031835,0.0), + (10.196872670067446,4.1842030053700165), + (16.553612933660478,4.449943091510793) +] + +is_pump_contaminated = [ + True, False, + False, False, + False, False, + False, False +] + +class Cholera(mesa.Model): + def __init__( + self, + cell_population: Sequence[int] = cell_population, + pumps_location: Sequence[Sequence[float]] = points, + is_pump_contaminated: Sequence[bool] = is_pump_contaminated, + cases_ratio_to_fix_pump: float = 9e-1, + pumps_neighbor_contamination_chance: float = 2e-1, + pumps_person_contamination_chance: float = 2e-1, + recovery_chance: float = 2e-1, + mortality_chance: float = 1e-1, + ): + super().__init__() + self.susceptible = 0 + for population in cell_population: + self.susceptible += population + self.infectious = 0 + self.removed = 0 + + self.infected_pumps = 0 + self.number_pumps = len(cell_population) + + self.schedule = mesa.time.RandomActivation(self) + + self.grid = mesa.experimental.cell_space.VoronoiGrid(centroids_coordinates=pumps_location, capacity=int(self.susceptible + 1), random=self.random) + + print(self.grid.all_cells) + for population, cell, contaminated in zip(cell_population, list(self.grid.all_cells), is_pump_contaminated): + pump_state = INFECTIOUS if contaminated else SUSCEPTIBLE + self.infected_pumps += pump_state + pump = Pump( + self.next_id(), self, pump_state, + pumps_person_contamination_chance, + pumps_neighbor_contamination_chance, + cases_ratio_to_fix_pump + ) + self.schedule.add(pump) + cell.add_agent(pump) + pump.move_to(cell) + for i in range(population): + person = Person(self.next_id(), self, mortality_chance, recovery_chance) + self.schedule.add(person) + cell.add_agent(person) + person.move_to(cell) + + self.datacollector = mesa.DataCollector( + model_reporters={ + "Susceptible": "susceptible", + "Infectious": "infectious", + "Removed": "removed" + } + ) + + def step(self): + self.datacollector.collect(self) + self.schedule.step() + + + diff --git a/examples/cholera_voronoi/cholera_voronoi/server.py b/examples/cholera_voronoi/cholera_voronoi/server.py new file mode 100644 index 00000000..8557d621 --- /dev/null +++ b/examples/cholera_voronoi/cholera_voronoi/server.py @@ -0,0 +1,51 @@ +from cholera.model import Cholera +import mesa + +def get_removed_people(model: Cholera): + """ + Display a text count of how many people were removed. + """ + return f"Number of removed people: {model.removed}" + +def get_infectious_pumps(model: Cholera): + """ + Display infected/total pumps count. + """ + return f"Infected pumps: {model.infected_pumps}/{model.number_pumps}" + + +model_params = { + "cases_ratio_to_fix_pump": mesa.visualization.Slider( + name="Ratio of cases in a neighborhood / total person in system to fix pump", + value=0.1, min_value=0, max_value=0.3, step=0.001 + ), + "pumps_neighbor_contamination_chance": mesa.visualization.Slider( + name="Pump chance of contaminate a neighbor", + value=2e-1, min_value=0, max_value=1, step=0.05, + ), + "pumps_person_contamination_chance": mesa.visualization.Slider( + name="Pump chance of contaminate a person", + value=2e-1, min_value=0, max_value=1, step=0.05, + ), + "recovery_chance": mesa.visualization.Slider( + name="Infected person recovery chance", + value=2e-1, min_value=0, max_value=1, step=0.05, + ), + "mortality_chance": mesa.visualization.Slider( + name="Infected person mortality chance", + value=1e-1, min_value=0, max_value=1, step=0.05, + ), +} + +chart = mesa.visualization.ChartModule( + [ + {"Label": "Susceptible", "Color": "#008000"}, + {"Label": "Infectious", "Color": "#FF0000"}, + {"Label": "Removed", "Color": "#808080"}, + ] +) + +server = mesa.visualization.ModularServer( + Cholera, [chart, get_removed_people, get_infectious_pumps], model_params=model_params +) + diff --git a/examples/cholera_voronoi/run.py b/examples/cholera_voronoi/run.py new file mode 100644 index 00000000..8f9bdacc --- /dev/null +++ b/examples/cholera_voronoi/run.py @@ -0,0 +1,3 @@ +from cholera.server import server + +server.launch(open_browser=True) \ No newline at end of file From ccfe6d6c246274d59c13229f00d46e223580103a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=ADtor=20Fr=C3=B3is?= Date: Sun, 7 Apr 2024 13:05:33 -0300 Subject: [PATCH 2/8] fixed errors --- .../cholera_voronoi/cholera_voronoi/agents.py | 43 ++++++++------ .../cholera_voronoi/cholera_voronoi/model.py | 57 ++++++++++--------- .../cholera_voronoi/cholera_voronoi/server.py | 44 +++++++++----- examples/cholera_voronoi/run.py | 2 +- 4 files changed, 88 insertions(+), 58 deletions(-) diff --git a/examples/cholera_voronoi/cholera_voronoi/agents.py b/examples/cholera_voronoi/cholera_voronoi/agents.py index f65a33f7..0ea1c7ec 100644 --- a/examples/cholera_voronoi/cholera_voronoi/agents.py +++ b/examples/cholera_voronoi/cholera_voronoi/agents.py @@ -1,32 +1,33 @@ -from collections.abc import Sequence from mesa.experimental.cell_space import CellAgent SUSCEPTIBLE = 0 INFECTIOUS = 1 REMOVED = 2 + class Person(CellAgent): - def __init__( - self, - unique_id, - model, - mortality_chance, - recovery_chance - ): + def __init__(self, unique_id, model, mortality_chance, recovery_chance): super().__init__(unique_id, model) self.health_state = SUSCEPTIBLE self.mortality_chance = mortality_chance self.recovery_chance = recovery_chance def step(self): - if self.health_state == REMOVED: return + if self.health_state == REMOVED: + return - if self.health_state == INFECTIOUS and self.model.random.random() < self.recovery_chance: + if ( + self.health_state == INFECTIOUS + and self.model.random.random() < self.recovery_chance + ): self.health_state = SUSCEPTIBLE self.model.infectious -= 1 self.model.susceptible += 1 - if self.health_state == INFECTIOUS and self.model.random.random() < self.mortality_chance: + if ( + self.health_state == INFECTIOUS + and self.model.random.random() < self.mortality_chance + ): self.health_state = REMOVED self.model.infectious -= 1 self.model.removed += 1 @@ -34,13 +35,13 @@ def step(self): class Pump(CellAgent): def __init__( - self, - unique_id, + self, + unique_id, model, contaminated, pumps_person_contamination_chance, pumps_neighbor_contamination_chance, - cases_ratio_to_fix_pump + cases_ratio_to_fix_pump, ): super().__init__(unique_id, model) self.state = contaminated @@ -51,9 +52,17 @@ def __init__( def step(self): if self.state is INFECTIOUS: # Infect people in the cell - people = [obj for obj in self.cell.agents if isinstance(obj, Person) and obj.health_state is not REMOVED] + people = [ + obj + for obj in self.cell.agents + if isinstance(obj, Person) and obj.health_state is not REMOVED + ] for person in people: - if person.health_state is SUSCEPTIBLE and self.model.random.random() < self.pumps_person_contamination_chance: + if ( + person.health_state is SUSCEPTIBLE + and self.model.random.random() + < self.pumps_person_contamination_chance + ): person.health_state = INFECTIOUS self.model.susceptible -= 1 self.model.infectious += 1 @@ -71,4 +80,4 @@ def step(self): cases_ratio = cases / (self.model.susceptible + self.model.infectious) if cases_ratio > self.cases_ratio_to_fix_pump: self.state = SUSCEPTIBLE - self.model.infected_pumps -= 1 \ No newline at end of file + self.model.infected_pumps -= 1 diff --git a/examples/cholera_voronoi/cholera_voronoi/model.py b/examples/cholera_voronoi/cholera_voronoi/model.py index fb42382b..67f2ed1d 100644 --- a/examples/cholera_voronoi/cholera_voronoi/model.py +++ b/examples/cholera_voronoi/cholera_voronoi/model.py @@ -1,5 +1,7 @@ -import mesa from collections.abc import Sequence + +import mesa + from .agents import Person, Pump SUSCEPTIBLE = 0 @@ -9,26 +11,22 @@ cell_population = [400] * 8 points = [ - (9.909976449792431,11.542846828417543), - (0.40972334441912234,14.266853186123692), - (0.0,20.0), - (20.0,5.111991897435429), - (12.566609556906684,1.57960921165571), - (5.232766132031835,0.0), - (10.196872670067446,4.1842030053700165), - (16.553612933660478,4.449943091510793) + (9.909976449792431, 11.542846828417543), + (0.40972334441912234, 14.266853186123692), + (0.0, 20.0), + (20.0, 5.111991897435429), + (12.566609556906684, 1.57960921165571), + (5.232766132031835, 0.0), + (10.196872670067446, 4.1842030053700165), + (16.553612933660478, 4.449943091510793), ] -is_pump_contaminated = [ - True, False, - False, False, - False, False, - False, False -] +is_pump_contaminated = [True, False, False, False, False, False, False, False] + class Cholera(mesa.Model): def __init__( - self, + self, cell_population: Sequence[int] = cell_population, pumps_location: Sequence[Sequence[float]] = points, is_pump_contaminated: Sequence[bool] = is_pump_contaminated, @@ -41,7 +39,7 @@ def __init__( super().__init__() self.susceptible = 0 for population in cell_population: - self.susceptible += population + self.susceptible += population self.infectious = 0 self.removed = 0 @@ -50,17 +48,25 @@ def __init__( self.schedule = mesa.time.RandomActivation(self) - self.grid = mesa.experimental.cell_space.VoronoiGrid(centroids_coordinates=pumps_location, capacity=int(self.susceptible + 1), random=self.random) - + self.grid = mesa.experimental.cell_space.VoronoiGrid( + centroids_coordinates=pumps_location, + capacity=int(self.susceptible + 1), + random=self.random, + ) + print(self.grid.all_cells) - for population, cell, contaminated in zip(cell_population, list(self.grid.all_cells), is_pump_contaminated): + for population, cell, contaminated in zip( + cell_population, list(self.grid.all_cells), is_pump_contaminated + ): pump_state = INFECTIOUS if contaminated else SUSCEPTIBLE self.infected_pumps += pump_state pump = Pump( - self.next_id(), self, pump_state, - pumps_person_contamination_chance, + self.next_id(), + self, + pump_state, + pumps_person_contamination_chance, pumps_neighbor_contamination_chance, - cases_ratio_to_fix_pump + cases_ratio_to_fix_pump, ) self.schedule.add(pump) cell.add_agent(pump) @@ -75,13 +81,10 @@ def __init__( model_reporters={ "Susceptible": "susceptible", "Infectious": "infectious", - "Removed": "removed" + "Removed": "removed", } ) def step(self): self.datacollector.collect(self) self.schedule.step() - - - diff --git a/examples/cholera_voronoi/cholera_voronoi/server.py b/examples/cholera_voronoi/cholera_voronoi/server.py index 8557d621..83441b15 100644 --- a/examples/cholera_voronoi/cholera_voronoi/server.py +++ b/examples/cholera_voronoi/cholera_voronoi/server.py @@ -1,5 +1,6 @@ -from cholera.model import Cholera import mesa +from cholera.model import Cholera + def get_removed_people(model: Cholera): """ @@ -7,6 +8,7 @@ def get_removed_people(model: Cholera): """ return f"Number of removed people: {model.removed}" + def get_infectious_pumps(model: Cholera): """ Display infected/total pumps count. @@ -16,24 +18,39 @@ def get_infectious_pumps(model: Cholera): model_params = { "cases_ratio_to_fix_pump": mesa.visualization.Slider( - name="Ratio of cases in a neighborhood / total person in system to fix pump", - value=0.1, min_value=0, max_value=0.3, step=0.001 + name="Ratio of cases in a neighborhood / total person in system to fix pump", + value=0.1, + min_value=0, + max_value=0.3, + step=0.001, ), "pumps_neighbor_contamination_chance": mesa.visualization.Slider( - name="Pump chance of contaminate a neighbor", - value=2e-1, min_value=0, max_value=1, step=0.05, + name="Pump chance of contaminate a neighbor", + value=2e-1, + min_value=0, + max_value=1, + step=0.05, ), "pumps_person_contamination_chance": mesa.visualization.Slider( - name="Pump chance of contaminate a person", - value=2e-1, min_value=0, max_value=1, step=0.05, + name="Pump chance of contaminate a person", + value=2e-1, + min_value=0, + max_value=1, + step=0.05, ), "recovery_chance": mesa.visualization.Slider( - name="Infected person recovery chance", - value=2e-1, min_value=0, max_value=1, step=0.05, + name="Infected person recovery chance", + value=2e-1, + min_value=0, + max_value=1, + step=0.05, ), "mortality_chance": mesa.visualization.Slider( - name="Infected person mortality chance", - value=1e-1, min_value=0, max_value=1, step=0.05, + name="Infected person mortality chance", + value=1e-1, + min_value=0, + max_value=1, + step=0.05, ), } @@ -46,6 +63,7 @@ def get_infectious_pumps(model: Cholera): ) server = mesa.visualization.ModularServer( - Cholera, [chart, get_removed_people, get_infectious_pumps], model_params=model_params + Cholera, + [chart, get_removed_people, get_infectious_pumps], + model_params=model_params, ) - diff --git a/examples/cholera_voronoi/run.py b/examples/cholera_voronoi/run.py index 8f9bdacc..fec37116 100644 --- a/examples/cholera_voronoi/run.py +++ b/examples/cholera_voronoi/run.py @@ -1,3 +1,3 @@ from cholera.server import server -server.launch(open_browser=True) \ No newline at end of file +server.launch(open_browser=True) From 54a8c91c1c53a898bb09d32806bde70e151c97ac Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Sun, 7 Apr 2024 16:11:29 +0000 Subject: [PATCH 3/8] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- examples/cholera_voronoi/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/cholera_voronoi/README.md b/examples/cholera_voronoi/README.md index cf607d20..e1c589b1 100644 --- a/examples/cholera_voronoi/README.md +++ b/examples/cholera_voronoi/README.md @@ -5,7 +5,7 @@ This folder contains a implementation of Cholera spread analyzed by John Snow at The model has two agents: people and pumps. Pumps can infect people and neighbor pumps. People start as susceptible, can be infected by pumps and recover or die, according to a simple SIR model. Each cell has only one pump and is connected to neighbor cells according to Voronoi's diagram. The model aims to investigate how fast actions oriented by Voronoi diagrams can prevent disease spread. ## How to Run - + To run the model interactively, run ``mesa runserver`` in this directory. e.g. ``` From 02729b10d0427981216d2f9dff6e1a08069532cc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=ADtor=20Fr=C3=B3is?= Date: Sun, 7 Apr 2024 13:15:43 -0300 Subject: [PATCH 4/8] fixed errors --- examples/cholera_voronoi/cholera_voronoi/model.py | 1 - examples/cholera_voronoi/cholera_voronoi/server.py | 2 +- examples/cholera_voronoi/run.py | 2 +- 3 files changed, 2 insertions(+), 3 deletions(-) diff --git a/examples/cholera_voronoi/cholera_voronoi/model.py b/examples/cholera_voronoi/cholera_voronoi/model.py index 67f2ed1d..afe21ae9 100644 --- a/examples/cholera_voronoi/cholera_voronoi/model.py +++ b/examples/cholera_voronoi/cholera_voronoi/model.py @@ -54,7 +54,6 @@ def __init__( random=self.random, ) - print(self.grid.all_cells) for population, cell, contaminated in zip( cell_population, list(self.grid.all_cells), is_pump_contaminated ): diff --git a/examples/cholera_voronoi/cholera_voronoi/server.py b/examples/cholera_voronoi/cholera_voronoi/server.py index 83441b15..765cd2c9 100644 --- a/examples/cholera_voronoi/cholera_voronoi/server.py +++ b/examples/cholera_voronoi/cholera_voronoi/server.py @@ -1,5 +1,5 @@ import mesa -from cholera.model import Cholera +from cholera_voronoi.model import Cholera def get_removed_people(model: Cholera): diff --git a/examples/cholera_voronoi/run.py b/examples/cholera_voronoi/run.py index fec37116..dce4fa59 100644 --- a/examples/cholera_voronoi/run.py +++ b/examples/cholera_voronoi/run.py @@ -1,3 +1,3 @@ -from cholera.server import server +from cholera_voronoi.server import server server.launch(open_browser=True) From c83d8ad669bc4e7e31cb3f93cf67eb26be15cc5b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=ADtor=20Fr=C3=B3is?= Date: Wed, 10 Jul 2024 18:06:14 -0300 Subject: [PATCH 5/8] updated files, visualization --- .../{cholera_voronoi => }/agents.py | 20 ++--- .../cholera_voronoi/cholera_voronoi/server.py | 69 --------------- .../{cholera_voronoi => }/model.py | 3 +- examples/cholera_voronoi/run.ipynb | 48 +++++++++++ examples/cholera_voronoi/run.py | 86 ++++++++++++++++++- examples/cholera_voronoi/visualization.py | 0 6 files changed, 143 insertions(+), 83 deletions(-) rename examples/cholera_voronoi/{cholera_voronoi => }/agents.py (82%) delete mode 100644 examples/cholera_voronoi/cholera_voronoi/server.py rename examples/cholera_voronoi/{cholera_voronoi => }/model.py (98%) create mode 100644 examples/cholera_voronoi/run.ipynb create mode 100644 examples/cholera_voronoi/visualization.py diff --git a/examples/cholera_voronoi/cholera_voronoi/agents.py b/examples/cholera_voronoi/agents.py similarity index 82% rename from examples/cholera_voronoi/cholera_voronoi/agents.py rename to examples/cholera_voronoi/agents.py index 0ea1c7ec..e1152eae 100644 --- a/examples/cholera_voronoi/cholera_voronoi/agents.py +++ b/examples/cholera_voronoi/agents.py @@ -8,27 +8,27 @@ class Person(CellAgent): def __init__(self, unique_id, model, mortality_chance, recovery_chance): super().__init__(unique_id, model) - self.health_state = SUSCEPTIBLE + self.state = SUSCEPTIBLE self.mortality_chance = mortality_chance self.recovery_chance = recovery_chance def step(self): - if self.health_state == REMOVED: + if self.state == REMOVED: return if ( - self.health_state == INFECTIOUS + self.state == INFECTIOUS and self.model.random.random() < self.recovery_chance ): - self.health_state = SUSCEPTIBLE + self.state = SUSCEPTIBLE self.model.infectious -= 1 self.model.susceptible += 1 if ( - self.health_state == INFECTIOUS + self.state == INFECTIOUS and self.model.random.random() < self.mortality_chance ): - self.health_state = REMOVED + self.state = REMOVED self.model.infectious -= 1 self.model.removed += 1 @@ -55,15 +55,15 @@ def step(self): people = [ obj for obj in self.cell.agents - if isinstance(obj, Person) and obj.health_state is not REMOVED + if isinstance(obj, Person) and obj.state is not REMOVED ] for person in people: if ( - person.health_state is SUSCEPTIBLE + person.state is SUSCEPTIBLE and self.model.random.random() < self.pumps_person_contamination_chance ): - person.health_state = INFECTIOUS + person.state = INFECTIOUS self.model.susceptible -= 1 self.model.infectious += 1 @@ -76,7 +76,7 @@ def step(self): self.model.infected_pumps += 1 # If cases in total is too high, fix pump - cases = sum(1 for a in people if a.health_state is INFECTIOUS) + cases = sum(1 for a in people if a.state is INFECTIOUS) cases_ratio = cases / (self.model.susceptible + self.model.infectious) if cases_ratio > self.cases_ratio_to_fix_pump: self.state = SUSCEPTIBLE diff --git a/examples/cholera_voronoi/cholera_voronoi/server.py b/examples/cholera_voronoi/cholera_voronoi/server.py deleted file mode 100644 index 765cd2c9..00000000 --- a/examples/cholera_voronoi/cholera_voronoi/server.py +++ /dev/null @@ -1,69 +0,0 @@ -import mesa -from cholera_voronoi.model import Cholera - - -def get_removed_people(model: Cholera): - """ - Display a text count of how many people were removed. - """ - return f"Number of removed people: {model.removed}" - - -def get_infectious_pumps(model: Cholera): - """ - Display infected/total pumps count. - """ - return f"Infected pumps: {model.infected_pumps}/{model.number_pumps}" - - -model_params = { - "cases_ratio_to_fix_pump": mesa.visualization.Slider( - name="Ratio of cases in a neighborhood / total person in system to fix pump", - value=0.1, - min_value=0, - max_value=0.3, - step=0.001, - ), - "pumps_neighbor_contamination_chance": mesa.visualization.Slider( - name="Pump chance of contaminate a neighbor", - value=2e-1, - min_value=0, - max_value=1, - step=0.05, - ), - "pumps_person_contamination_chance": mesa.visualization.Slider( - name="Pump chance of contaminate a person", - value=2e-1, - min_value=0, - max_value=1, - step=0.05, - ), - "recovery_chance": mesa.visualization.Slider( - name="Infected person recovery chance", - value=2e-1, - min_value=0, - max_value=1, - step=0.05, - ), - "mortality_chance": mesa.visualization.Slider( - name="Infected person mortality chance", - value=1e-1, - min_value=0, - max_value=1, - step=0.05, - ), -} - -chart = mesa.visualization.ChartModule( - [ - {"Label": "Susceptible", "Color": "#008000"}, - {"Label": "Infectious", "Color": "#FF0000"}, - {"Label": "Removed", "Color": "#808080"}, - ] -) - -server = mesa.visualization.ModularServer( - Cholera, - [chart, get_removed_people, get_infectious_pumps], - model_params=model_params, -) diff --git a/examples/cholera_voronoi/cholera_voronoi/model.py b/examples/cholera_voronoi/model.py similarity index 98% rename from examples/cholera_voronoi/cholera_voronoi/model.py rename to examples/cholera_voronoi/model.py index afe21ae9..fc05723b 100644 --- a/examples/cholera_voronoi/cholera_voronoi/model.py +++ b/examples/cholera_voronoi/model.py @@ -1,8 +1,7 @@ from collections.abc import Sequence import mesa - -from .agents import Person, Pump +from agents import Person, Pump SUSCEPTIBLE = 0 INFECTIOUS = 1 diff --git a/examples/cholera_voronoi/run.ipynb b/examples/cholera_voronoi/run.ipynb new file mode 100644 index 00000000..6f0a9893 --- /dev/null +++ b/examples/cholera_voronoi/run.ipynb @@ -0,0 +1,48 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [], + "source": [] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "page" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "venv", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.12.3" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/examples/cholera_voronoi/run.py b/examples/cholera_voronoi/run.py index dce4fa59..200a91c7 100644 --- a/examples/cholera_voronoi/run.py +++ b/examples/cholera_voronoi/run.py @@ -1,3 +1,85 @@ -from cholera_voronoi.server import server +import solara +from mesa.visualization import JupyterViz +from model import Cholera, Pump -server.launch(open_browser=True) +SUSCEPTIBLE = 0 +INFECTIOUS = 1 +REMOVED = 2 + + +def get_removed_people(model: Cholera): + """ + Display a text count of how many people were removed. + """ + return f"Number of removed people: {model.removed}" + + +def get_infectious_pumps(model: Cholera): + """ + Display infected/total pumps count. + """ + return f"Infected pumps: {model.infected_pumps}/{model.number_pumps}" + + +model_params = { + # "cases_ratio_to_fix_pump": { + # 'type': 'SliderFloat', + # 'label': "Ratio of cases in a neighborhood / total person in system to fix pump", + # 'value': 0.1, + # 'min_value': 0, + # 'max_value': 0.3, + # 'step': 0.001 + # }, + "pumps_neighbor_contamination_chance": { + "type": "SliderFloat", + "label": "Neighbor contamination ratio", + "value": 2e-1, + "min_value": 0, + "max_value": 1, + "step": 0.05, + }, + "pumps_person_contamination_chance": { + "type": "SliderFloat", + "label": "Person contamination ratio", + "value": 2e-1, + "min_value": 0, + "max_value": 1, + "step": 0.05, + }, + "recovery_chance": { + "type": "SliderFloat", + "label": "Recovery chance", + "value": 2e-1, + "min_value": 0, + "max_value": 1, + "step": 0.05, + }, + "mortality_chance": { + "type": "SliderFloat", + "label": "Mortality chance", + "value": 1e-1, + "min_value": 0, + "max_value": 1, + "step": 0.05, + }, +} + + +def agent_portrayal(agent): + if isinstance(agent, Pump): + if agent.state == INFECTIOUS: + return {"size": 200, "color": "tab:orange"} + elif agent.state == SUSCEPTIBLE: + return {"size": 200, "color": "tab:blue"} + return {"size": 0, "color": "tab:blue"} + + +@solara.component +def Page(): + JupyterViz( + Cholera, + model_params, + name="Cholera Model", + agent_portrayal=agent_portrayal, + measures=[["Susceptible", "Infectious", "Removed"]], + ) diff --git a/examples/cholera_voronoi/visualization.py b/examples/cholera_voronoi/visualization.py new file mode 100644 index 00000000..e69de29b From b4d0c60aa562e36a18766ad40b3ad40eec92591d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=ADtor=20Fr=C3=B3is?= Date: Mon, 15 Jul 2024 12:08:19 -0300 Subject: [PATCH 6/8] small model changes --- examples/cholera_voronoi/agents.py | 5 ++++- examples/cholera_voronoi/model.py | 1 + examples/cholera_voronoi/run.py | 4 ++-- 3 files changed, 7 insertions(+), 3 deletions(-) diff --git a/examples/cholera_voronoi/agents.py b/examples/cholera_voronoi/agents.py index e1152eae..1c7e6536 100644 --- a/examples/cholera_voronoi/agents.py +++ b/examples/cholera_voronoi/agents.py @@ -77,7 +77,10 @@ def step(self): # If cases in total is too high, fix pump cases = sum(1 for a in people if a.state is INFECTIOUS) - cases_ratio = cases / (self.model.susceptible + self.model.infectious) + cases_ratio = cases / ( + self.model.susceptible + self.model.infectious + 1e-1 + ) + self.cell.properties["cases_ratio"] = cases_ratio if cases_ratio > self.cases_ratio_to_fix_pump: self.state = SUSCEPTIBLE self.model.infected_pumps -= 1 diff --git a/examples/cholera_voronoi/model.py b/examples/cholera_voronoi/model.py index fc05723b..9ed22acc 100644 --- a/examples/cholera_voronoi/model.py +++ b/examples/cholera_voronoi/model.py @@ -51,6 +51,7 @@ def __init__( centroids_coordinates=pumps_location, capacity=int(self.susceptible + 1), random=self.random, + cell_coloring_attribute="cases_ratio", ) for population, cell, contaminated in zip( diff --git a/examples/cholera_voronoi/run.py b/examples/cholera_voronoi/run.py index 200a91c7..ace494cc 100644 --- a/examples/cholera_voronoi/run.py +++ b/examples/cholera_voronoi/run.py @@ -68,9 +68,9 @@ def get_infectious_pumps(model: Cholera): def agent_portrayal(agent): if isinstance(agent, Pump): if agent.state == INFECTIOUS: - return {"size": 200, "color": "tab:orange"} + return {"size": 30, "color": "tab:orange"} elif agent.state == SUSCEPTIBLE: - return {"size": 200, "color": "tab:blue"} + return {"size": 30, "color": "tab:blue"} return {"size": 0, "color": "tab:blue"} From 9f6bbdb906a14ecbc85804a5ace3e992af0c2022 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=ADtor=20Fr=C3=B3is?= Date: Mon, 15 Jul 2024 13:03:36 -0300 Subject: [PATCH 7/8] fix slider --- examples/cholera_voronoi/model.py | 2 +- examples/cholera_voronoi/run.py | 72 +++++++++++------------ examples/cholera_voronoi/visualization.py | 0 3 files changed, 35 insertions(+), 39 deletions(-) delete mode 100644 examples/cholera_voronoi/visualization.py diff --git a/examples/cholera_voronoi/model.py b/examples/cholera_voronoi/model.py index 9ed22acc..77614016 100644 --- a/examples/cholera_voronoi/model.py +++ b/examples/cholera_voronoi/model.py @@ -51,7 +51,7 @@ def __init__( centroids_coordinates=pumps_location, capacity=int(self.susceptible + 1), random=self.random, - cell_coloring_attribute="cases_ratio", + cell_coloring_property="cases_ratio", ) for population, cell, contaminated in zip( diff --git a/examples/cholera_voronoi/run.py b/examples/cholera_voronoi/run.py index ace494cc..857f77cc 100644 --- a/examples/cholera_voronoi/run.py +++ b/examples/cholera_voronoi/run.py @@ -1,5 +1,5 @@ import solara -from mesa.visualization import JupyterViz +from mesa.visualization import JupyterViz, Slider from model import Cholera, Pump SUSCEPTIBLE = 0 @@ -26,51 +26,47 @@ def get_infectious_pumps(model: Cholera): # 'type': 'SliderFloat', # 'label': "Ratio of cases in a neighborhood / total person in system to fix pump", # 'value': 0.1, - # 'min_value': 0, - # 'max_value': 0.3, + # 'min': 0, + # 'max': 0.3, # 'step': 0.001 - # }, - "pumps_neighbor_contamination_chance": { - "type": "SliderFloat", - "label": "Neighbor contamination ratio", - "value": 2e-1, - "min_value": 0, - "max_value": 1, - "step": 0.05, - }, - "pumps_person_contamination_chance": { - "type": "SliderFloat", - "label": "Person contamination ratio", - "value": 2e-1, - "min_value": 0, - "max_value": 1, - "step": 0.05, - }, - "recovery_chance": { - "type": "SliderFloat", - "label": "Recovery chance", - "value": 2e-1, - "min_value": 0, - "max_value": 1, - "step": 0.05, - }, - "mortality_chance": { - "type": "SliderFloat", - "label": "Mortality chance", - "value": 1e-1, - "min_value": 0, - "max_value": 1, - "step": 0.05, - }, + # ), + "pumps_neighbor_contamination_chance": Slider( + label="Neighbor contamination ratio", + value=2e-1, + min=0, + max=1, + step=0.05, + ), + "pumps_person_contamination_chance": Slider( + label="Person contamination ratio", + value=2e-1, + min=0, + max=1, + step=0.05, + ), + "recovery_chance": Slider( + label="Recovery chance", + value=2e-1, + min=0, + max=1, + step=0.05, + ), + "mortality_chance": Slider( + label="Mortality chance", + value=1e-1, + min=0, + max=1, + step=0.05, + ), } def agent_portrayal(agent): if isinstance(agent, Pump): if agent.state == INFECTIOUS: - return {"size": 30, "color": "tab:orange"} + return {"size": 50, "color": "tab:orange"} elif agent.state == SUSCEPTIBLE: - return {"size": 30, "color": "tab:blue"} + return {"size": 50, "color": "tab:blue"} return {"size": 0, "color": "tab:blue"} diff --git a/examples/cholera_voronoi/visualization.py b/examples/cholera_voronoi/visualization.py deleted file mode 100644 index e69de29b..00000000 From 3107d6d2740a022068f5d00faa2a4eecaf2bf72f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=ADtor=20Fr=C3=B3is?= Date: Mon, 15 Jul 2024 13:58:53 -0300 Subject: [PATCH 8/8] fixed errors --- .../{ => cholera_voronoi}/agents.py | 0 .../{ => cholera_voronoi}/model.py | 3 +- examples/cholera_voronoi/requirements.txt | 4 ++ examples/cholera_voronoi/run.ipynb | 48 ------------------- examples/cholera_voronoi/run.py | 2 +- 5 files changed, 7 insertions(+), 50 deletions(-) rename examples/cholera_voronoi/{ => cholera_voronoi}/agents.py (100%) rename examples/cholera_voronoi/{ => cholera_voronoi}/model.py (98%) create mode 100644 examples/cholera_voronoi/requirements.txt delete mode 100644 examples/cholera_voronoi/run.ipynb diff --git a/examples/cholera_voronoi/agents.py b/examples/cholera_voronoi/cholera_voronoi/agents.py similarity index 100% rename from examples/cholera_voronoi/agents.py rename to examples/cholera_voronoi/cholera_voronoi/agents.py diff --git a/examples/cholera_voronoi/model.py b/examples/cholera_voronoi/cholera_voronoi/model.py similarity index 98% rename from examples/cholera_voronoi/model.py rename to examples/cholera_voronoi/cholera_voronoi/model.py index 77614016..56e910fd 100644 --- a/examples/cholera_voronoi/model.py +++ b/examples/cholera_voronoi/cholera_voronoi/model.py @@ -1,7 +1,8 @@ from collections.abc import Sequence import mesa -from agents import Person, Pump + +from .agents import Person, Pump SUSCEPTIBLE = 0 INFECTIOUS = 1 diff --git a/examples/cholera_voronoi/requirements.txt b/examples/cholera_voronoi/requirements.txt new file mode 100644 index 00000000..f98d4fb8 --- /dev/null +++ b/examples/cholera_voronoi/requirements.txt @@ -0,0 +1,4 @@ +matplotlib +mesa +numpy +solara \ No newline at end of file diff --git a/examples/cholera_voronoi/run.ipynb b/examples/cholera_voronoi/run.ipynb deleted file mode 100644 index 6f0a9893..00000000 --- a/examples/cholera_voronoi/run.ipynb +++ /dev/null @@ -1,48 +0,0 @@ -{ - "cells": [ - { - "cell_type": "code", - "execution_count": 1, - "metadata": {}, - "outputs": [], - "source": [] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "page" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [] - } - ], - "metadata": { - "kernelspec": { - "display_name": "venv", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.12.3" - } - }, - "nbformat": 4, - "nbformat_minor": 2 -} diff --git a/examples/cholera_voronoi/run.py b/examples/cholera_voronoi/run.py index 857f77cc..45e9bde2 100644 --- a/examples/cholera_voronoi/run.py +++ b/examples/cholera_voronoi/run.py @@ -1,6 +1,6 @@ import solara +from cholera_voronoi.model import Cholera, Pump from mesa.visualization import JupyterViz, Slider -from model import Cholera, Pump SUSCEPTIBLE = 0 INFECTIOUS = 1