Del 3: Mindskning af smitte =========================== Succes! Regeringen er godt tilfreds med din model, der viser spredningen af smitte, og efterfølgende immunitet, over tid. Nu har de givet dig en ny opgave: kom på tiltag til at begrænse smitten, og simulér dem så i modellen, for at se, om de faktisk virker. Heldigvis har dine kollegaer en masse idéer til, hvordan man kan mindske smittespredning. Hold afstand ------------ *Forslag: Agenter prøver på at undvige andre syge agenter.* Vi vil gøre sådan, at alle agenter, der ser en syg agent indenfor en vis afstand, vender sig om og går i den modsatte retning. Erstat denne linje i ``Person.step``:: self.direction += randint(-10,10) med disse:: avg_direction = 0 nearby_agents = 0 for agent in self.agents_nearby(20): if agent.category == 1: avg_direction += self.direction_to(agent.x,agent.y) nearby_agents += 1 if nearby_agents > 0: self.direction = (avg_direction / nearby_agents) + 180 else: self.direction += randint(-10,10) Det virker af meget, men ovenstående kode er faktisk ikke så indviklet. Vi laver først to variabler, ``avg_direction`` og ``nearby_agents``, hvor den første kommer til at indeholde den gennemsnitlige retning til alle de smittede agenter, og ``nearby_agents`` indeholder antallet af smittede agenter tæt på. Derefter undersøger vi agenter i nærheden, også dem, som er udenfor smitteradius. Hvis der er en smittet agent, lægger vi retningen til agenten til ``avg_direction``, og 1 til ``nearby_agents``. Når alle agenterne er blevet undersøgt, skal vi ændre retning. Hvis der ingen smittede agenter er tæt på, justerer vi bare, som normalt, den nuværende retning med op til 10 grader. Hvis der *er* smittede agenter, finder vi den gennemsnitlige retning med :math:`\frac{ \texttt{avg_direction} }{\texttt{nearby_agents}}`, og peger så i den modsatte retning (ved at lægge 180 til). Kør det resulterende program, og observer effekten. For at gøre det mere realistisk, kan man f.eks. ændre på programmet sådan, at ikke alle holder lige god afstand (brug ``randint``), eller at folk holder mindre afstand over tid (brug en variabel i stil med ``infection_level``, der tæller ned). Inddeling i grupper ------------------- *Forslag: Agenter inddeles i grupper, og holder afstand til andre grupper.* Det er meget effektivt at undgå de syge agenter, men i virkeligheden kan det være svært at se med det samme, om nogen er smittede, specielt da folk kan have varierende grader af symptomer. Derfor prøver vi nu en ny taktik: Folk inddeles i 5 grupper, og må kun have kontakt med dem, der er i samme gruppe. I takt med, at vi indfører forskellige tiltag til at begrænse smitten, kunne det være smart, hvis vi kunne slå disse tiltag til og fra, uden at vi behøvede at ændre i koden hver gang. Vi starter derfor med at tilføje en *checkbox*, så man kan slå grupperne til og fra. Tilføj denne linje efter, at du har tilføjet knapperne til modellen:: epidemic_model.add_checkbox("enable_groups") Nu kan vi gå i gang med faktisk at lave gruppefunktionaliteten. Tilføj, nederst i ``Person.setup``, denne linje:: if model.enable_groups: self.group = randint(1,5) Dette tildeler agenten til en tilfældig gruppe, identificeret med et ID mellem 1 og 5. For at vi kan se forskel på de forskellige grupper, tegner vi en cirkel udenom agenterne, hvor farven på cirklen afhænger af deres gruppe. Agenter i samme gruppe har således samme farvecirkel. Tilføj disse linjer kode til ``if``-sætningen:: self.group_indicator = model.add_ellipse(self.x-10,self.y-10,20,20,(0,0,0)) if self.group == 1: self.group_indicator.color = (200,200,0) elif self.group == 2: self.group_indicator.color = (0,200,200) elif self.group == 3: self.group_indicator.color = (200,0,200) elif self.group == 4: self.group_indicator.color = (100,100,100) elif self.group == 5: self.group_indicator.color = (250,150,0) Dette gemmer agentens farvecirkel i variablen ``group_indicator``, og giver den en farve afhængigt af ``group``-id'et. Ændr så linje i ``Person.step``:: if agent.category == 1: til denne:: if model.enable_groups and agent.group != self.group: Det får agenten til at undgå alle, der ikke er i dens egen gruppe, fremfor dem der er smittede. Tilføj til sidst, nederst i ``Person.step``:: if model.enable_groups: self.group_indicator.x = self.x-10 self.group_indicator.y = self.y-10 Dette får agentens "gruppe-indikator" til at følge med den rundt. .. image:: ../images/epidemic/epidemic-3.2.png :height: 400 Mere/mindre afstand ------------------- *Prøv at variere afstand, agenterne holder, og den afstand, de kan smitte på.* For at afprøve virkningen af forskellige tiltag, gør vi nu sådan, at agenternes fysiske afstand og smitterækkevidde kan justeres, imens simulationen køres. Tilføj to *sliders* til modellen med følgende kode (indsæt dem samme sted, som du laver knapper/checkboxes):: epidemic_model.add_controller_row() epidemic_model.add_slider("social_distance", 50, 0, 80) epidemic_model.add_controller_row() epidemic_model.add_slider("infection_distance", 15, 0, 40) Dette giver to sliders, som kan bruges til at justere variablene ``social_distance`` og ``infection_distance``. De to første tal er minimums- og maksimumsværdierne, og det sidste tal er startværdien. Ændr nu denne linje i ``Person.step``:: for agent in self.agents_nearby(20): til denne:: for agent in self.agents_nearby(model.social_distance): og ændr denne:: for agent in self.agents_nearby(12): til denne:: for agent in self.agents_nearby(model.infection_distance): .. image:: ../images/epidemic/epidemic-3.3.png :height: 400 Prøv at køre simulationen, og juster på værdierne undervejs. Overvej, hvilken indflydelse forholdet mellem de to værdier har på smittetallene. Samlet kode ----------- Her er den samlede kode du gerne skulle have nu:: from agents import Model, Agent, run from random import randint class Person(Agent): def setup(self, model): model.Susceptible += 1 self.category = 0 self.color = (0, 200, 0) if randint(1, 50) == 1: self.infect(model) if model.enable_groups: self.group = randint(1, 5) self.group_indicator = model.add_ellipse( self.x - 10, self.y - 10, 20, 20, (0, 0, 0) ) if self.group == 1: self.group_indicator.color = (200, 200, 0) elif self.group == 2: self.group_indicator.color = (0, 200, 200) elif self.group == 3: self.group_indicator.color = (200, 0, 200) elif self.group == 4: self.group_indicator.color = (100, 100, 100) elif self.group == 5: self.group_indicator.color = (250, 150, 0) def step(self, model): if model.enable_groups: self.group_indicator.x = self.x - 10 self.group_indicator.y = self.y - 10 new_direction = 0 nearby_agents = 0 for agent in self.agents_nearby(model.social_distance): if model.enable_groups and agent.group != self.group: new_direction += self.direction_to(agent.x, agent.y) nearby_agents += 1 if nearby_agents > 0: self.direction = (new_direction / nearby_agents) + 180 else: self.direction += randint(-10, 10) self.forward() if self.category == 1: for agent in self.agents_nearby(model.infection_distance): if agent.category == 0: agent.infect(model) self.infection_level -= 1 if self.infection_level == 0: self.turn_immune(model) def infect(self, model): model.Susceptible -= 1 model.Infectious += 1 self.color = (200, 0, 0) self.category = 1 self.infection_level = 600 def turn_immune(self, model): model.Infectious -= 1 model.Recovered += 1 self.color = (0, 0, 200) self.category = 2 def model_setup(model): model.reset() model.Susceptible = 0 model.Infectious = 0 model.Recovered = 0 for person in range(100): model.add_agent(Person()) def model_step(model): for person in model.agents: person.step(model) model.update_plots() epidemic_model = Model("Epidemimodel", 100, 100) epidemic_model.add_button("Setup", model_setup) epidemic_model.add_button("Go", model_step, toggle=True) epidemic_model.line_chart( ["Susceptible", "Infectious", "Recovered"], [(0, 200, 0), (200, 0, 0), (0, 0, 200)] ) epidemic_model.bar_chart(["Susceptible", "Infectious", "Recovered"], (200, 200, 200)) epidemic_model.add_checkbox("enable_groups") epidemic_model.add_controller_row() epidemic_model.add_slider("social_distance", 50, 0, 80) epidemic_model.add_controller_row() epidemic_model.add_slider("infection_distance", 15, 0, 40) run(epidemic_model)