Source code for pyEpiabm.core.cell
#
# Cell Class
#
import typing
import numpy as np
import re
from queue import Queue
from numbers import Number
from pyEpiabm.property import InfectionStatus
from pyEpiabm.utility import DistanceFunctions
from .microcell import Microcell
from .parameters import Parameters
from .person import Person
from ._compartment_counter import _CompartmentCounter
[docs]
class Cell:
"""Class representing a Cell (Subset of Population).
Collection of :class:`Microcell` s and :class:`Person` s.
"""
def __init__(self, loc: typing.Tuple[float, float] = (0, 0)):
"""Constructor Method.
Parameters
----------
loc : Tuple(float, float)
Location of the cell, as an (x,y) tuple
"""
self.location = loc
self.id = str(hash(self))
self.microcells = []
self.persons = []
self.places = []
self.households = []
self.person_queue = Queue()
self.PCR_queue = Queue()
self.LFT_queue = Queue()
self.compartment_counter = _CompartmentCounter(f"Cell {id(self)}")
self.nearby_cell_distances = dict()
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")
def __repr__(self):
"""Returns a string representation of the Cell.
Returns
-------
str
String representation of the Cell
"""
return f"Cell ({self.id}) with {len(self.microcells)} microcells " + \
f"and {len(self.persons)} people at location {self.location}."
[docs]
def add_microcells(self, n):
"""Add n empty :class:`Microcell` s to Cell.
Parameters
----------
n : int
Number of empty :class:`Microcell` s to add
"""
for _ in range(n):
self.microcells.append(Microcell(self))
[docs]
def set_id(self, id: str, cells: typing.List):
"""Updates id of current cell (i.e. for input from file).
Parameters
----------
id : str
Identity of cell
cells : list
List of all current cells
"""
# 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 has 1 or more digits
if not re.match("^-?\\d+$", id):
raise ValueError(f"Invalid id: {id}. id must be of the form 'i' "
f"where i is an integer")
# Finally, check for duplicates
cell_ids = [cell.id for cell in cells]
if id in cell_ids:
raise ValueError(f"Duplicate id: {id}.")
self.id = id
[docs]
def enqueue_person(self, person: Person):
"""Add person to queue for processing at end of iteration, provided
they are not already recovered (and so may be infected).
Parameters
----------
person : Person
Person to enqueue
"""
if person.infection_status != InfectionStatus.Recovered:
self.person_queue.put(person)
[docs]
def enqueue_PCR_testing(self, person: Person):
"""Add person to PCR testing queue for processing in testing
sweep.
Detailed description of the implementation can be found in github wiki:
https://github.com/SABS-R3-Epidemiology/epiabm/wiki/Interventions#testing
Parameters
----------
person : Person
Person to enqueue.
"""
self.PCR_queue.put(person)
[docs]
def enqueue_LFT_testing(self, person: Person):
"""Add person to LFT testing queue for processing in testing
sweep.
Detailed description of the implementation can be found in github wiki:
https://github.com/SABS-R3-Epidemiology/epiabm/wiki/Interventions#testing
Parameters
----------
person : Person
Person to enqueue.
"""
self.LFT_queue.put(person)
[docs]
def notify_person_status_change(
self,
old_status: InfectionStatus,
new_status: InfectionStatus,
age_group) -> None:
"""Notify Cell 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)
[docs]
def number_infectious(self):
"""Returns the total number of infectious people in each
cell, all ages combined.
Returns
-------
int
Total infectors in cell
"""
cell_data = self.compartment_counter.retrieve()
total_infectors = 0
for status in InfectionStatus:
if str(status).startswith('InfectionStatus.Infect'):
total_infectors += np.sum(cell_data[status])
return total_infectors
[docs]
def set_location(self, loc: typing.Tuple[float, float]):
"""Method to set or change the location of a cell.
Parameters
----------
loc : Tuple[float, float]
(x,y) coordinates of the place
"""
self.location = loc
[docs]
def find_nearby_cells(self, other_cells):
'''
Helper function which takes in a given cell and the list of all cells
and generates a list of nearby cells which are
closer than the cutoff for cross-cell infection.
Populates: self.find_nearby_cells
Dictionary of all cells with distance below cutoff.
Dictionary stores cell.id and distance between the 2 cells
These are stored in the form:
cell.id: distance
Parameters
----------
other_cells : typing.List[Cell]
List of all cells except cell
'''
cutoff = Parameters.instance().infection_radius
for cell2 in other_cells:
distance = DistanceFunctions.dist(self.location, cell2.location)
if distance < cutoff:
self.nearby_cell_distances[cell2.id] = distance
# Dict of near neighbours, cells which are closer than the
# cutoff for cross-cell infection