Source code for pyEpiabm.core.microcell

#
# Microcell Class
#

import typing
from numbers import Number
import logging
import re

from pyEpiabm.property import InfectionStatus

from .person import Person
from .place import Place
from .household import Household
from ._compartment_counter import _CompartmentCounter


[docs] class Microcell: """Class representing a Microcell (Group of people and places). Collection of :class:`Person` s. Parameters ---------- cell : Cell An instance of :class:`Cell` """ def __init__(self, cell): """Constructor Method. Parameters ---------- cell: Cell Microcell's parent :class:`Cell` instance """ self.persons = [] self.places = [] self.households = [] self.cell = cell self.location = cell.location self.id = self.cell.id + "." + str(len(self.cell.microcells)) self.compartment_counter = _CompartmentCounter( f"Microcell {id(self)}") def __repr__(self): """Returns a string representation of Microcell. Returns ------- str String representation of Microcell """ return f"Microcell ({self.id}) with {len(self.persons)} people" + \ f" at location {self.location}."
[docs] def set_id(self, id): """Updates id of current microcell (i.e. for input from file). Parameters ---------- id : str Identity of microcell """ # Ensure id is a string if not isinstance(id, str): raise TypeError("Provided id must be a string") # This regex will match on any string which takes the form "i.j" where # i and j are integers if not re.match("^-?\\d+\\.-?\\d+$", id): raise ValueError(f"Invalid id: {id}. id must be of the form 'i.j' " f"where i, j are integers") # Finally, check for duplicates microcell_ids = [microcell.id for microcell in self.cell.microcells] if id in microcell_ids: raise ValueError(f"Duplicate id: {id}.") self.id = id
[docs] def add_person(self, person): """Adds :class:`Person` with given :class:`InfectionStatus` and given age group to Microcell. Parameters ---------- person : Person Newly instantiated person with InfectionStatus and associated age group """ status = person.infection_status age_group = person.age_group self.compartment_counter._increment_compartment(1, status, age_group) self.cell.compartment_counter._increment_compartment(1, status, age_group) self.cell.persons.append(person) self.persons.append(person)
[docs] def add_people(self, n, status=InfectionStatus.Susceptible, age_group=None): """Adds n default :class:`Person` of given status to Microcell. Parameters ---------- n : int Number of default :class:`Person` s to add status : InfectionStatus Status of persons to add to cell age_group : Age group index Person's associated age group """ for _ in range(n): p = Person(self, age_group) self.cell.persons.append(p) self.persons.append(p) p.infection_status = status self.compartment_counter._increment_compartment( 1, p.infection_status, p.age_group) self.cell.compartment_counter._increment_compartment( 1, p.infection_status, p.age_group)
[docs] def add_place(self, n: int, loc: typing.Tuple[float, float], place_type): """Adds n default :class:`Place` to Microcell. Parameters ---------- n : int Number of default :class:`Place` s to add """ for _ in range(n): p = Place(loc, place_type, self.cell, self) self.cell.places.append(p) self.places.append(p)
[docs] def add_household(self, people: list, update_person_id: bool = True): """Adds a default :class:`Household` to Microcell and fills it with a number of :class:`Person` s. Parameters ---------- people : list List of :class:`People` to add to household update_person_id : bool Boolean representing whether we wish to update the id of the people of the household when the function is called or not """ if len(people) != 0: household = Household(self, loc=self.location) for i, person in enumerate(people): household.add_person(person) if update_person_id: person.set_id(household.id + "." + str(i)) else: # If the person already has a household, then do not change # their id logging.info(f"Person {person.id} has moved to " f"household {household.id} but has " f"not changed id") else: logging.info("Cannot create an empty household")
[docs] def notify_person_status_change( self, old_status: InfectionStatus, new_status: InfectionStatus, age_group) -> None: """Notify Microcell that a person's status has changed. Parameters ---------- old_status : InfectionStatus Person's old infection status new_status : InfectionStatus Person's new infection status age_group : Age group index Person's associated age group """ self.compartment_counter.report(old_status, new_status, age_group) self.cell.notify_person_status_change(old_status, new_status, age_group)
[docs] def set_location(self, loc: typing.Tuple[float, float]): """Method to set or change the location of a microcell. Parameters ---------- loc : Tuple[float, float] (x,y) coordinates of the microcell """ if not (len(loc) == 2 and isinstance(loc[0], Number) and isinstance(loc[1], Number)): raise ValueError("Location must be a tuple of float-type") self.location = loc
def count_icu(self): return sum(map(lambda person: person.infection_status == InfectionStatus.InfectICU, self.persons)) def count_infectious(self): return sum(map(lambda person: person.is_infectious() is True, self.persons))