Module nlisim.modules.phagocyte

Expand source code
from abc import abstractmethod
from enum import IntEnum, unique
from typing import TYPE_CHECKING, Tuple

from attr import attrs
import numpy as np

from nlisim.cell import CellData, CellFields, CellList
from nlisim.coordinates import Point, Voxel
from nlisim.grid import RectangularGrid
from nlisim.module import ModuleModel, ModuleState
from nlisim.state import State

if TYPE_CHECKING:  # prevent circular imports for type checking
    from nlisim.modules.afumigatus import AfumigatusCellData

MAX_CONIDIA = (
    30  # note: this the max that we can set the max to. i.e. not an actual model parameter
)


class PhagocyteCellData(CellData):
    PHAGOCYTE_FIELDS: CellFields = [
        ('phagosome', np.int64, MAX_CONIDIA),
    ]

    dtype = np.dtype(CellData.FIELDS + PHAGOCYTE_FIELDS, align=True)  # type: ignore

    @classmethod
    def create_cell_tuple(
        cls,
        **kwargs,
    ) -> Tuple:
        initializer = {
            'phagosome': kwargs.get('phagosome', -1 * np.ones(MAX_CONIDIA, dtype=np.int64)),
        }

        # ensure that these come in the correct order
        return CellData.create_cell_tuple(**kwargs) + tuple(
            [initializer[key] for key, *_ in PhagocyteCellData.PHAGOCYTE_FIELDS]
        )


@attrs(kw_only=True)
class PhagocyteModuleState(ModuleState):
    max_conidia: int  # units: count


class PhagocyteModel(ModuleModel):
    def single_step_move(
        self, state: State, cell: PhagocyteCellData, cell_index: int, cell_list: CellList
    ) -> None:
        """
        Move the phagocyte one 1 µm, probabilistically.

        depending on single_step_probabilistic_drift

        Parameters
        ----------
        state : State
            the global simulation state
        cell : PhagocyteCellData
            the cell to move
        cell_index : int
            index of cell in cell_list
        cell_list : CellList
            the CellList for the cell-type (macrophage/neutrophil/etc) of cell


        Returns
        -------
        nothing
        """
        grid: RectangularGrid = state.grid

        # At this moment, there is no inter-voxel geometry.
        cell_voxel: Voxel = grid.get_voxel(cell['point'])
        new_point: Point = self.single_step_probabilistic_drift(state, cell, cell_voxel)
        cell['point'] = new_point
        cell_list.update_voxel_index([cell_index])

    @abstractmethod
    def single_step_probabilistic_drift(
        self, state: State, cell: PhagocyteCellData, voxel: Voxel
    ) -> Point:
        ...

    @staticmethod
    def release_phagosome(state: State, phagocyte_cell: PhagocyteCellData) -> None:
        """
        Release afumigatus cells in the phagosome

        Parameters
        ----------
        state : State
            global simulation state
        phagocyte_cell : PhagocyteCellData


        Returns
        -------
        Nothing
        """
        from nlisim.modules.afumigatus import AfumigatusCellState, AfumigatusState

        afumigatus: AfumigatusState = state.afumigatus

        for fungal_cell_index in phagocyte_cell['phagosome']:
            if fungal_cell_index == -1:
                continue
            afumigatus.cells[fungal_cell_index]['state'] = AfumigatusCellState.RELEASING
        phagocyte_cell['phagosome'][:] = -1


# TODO: better name
@unique
class PhagocyteState(IntEnum):
    FREE = 0
    INTERACTING = 1


# TODO: name
@unique
class PhagocyteStatus(IntEnum):
    INACTIVE = 0
    INACTIVATING = 1
    RESTING = 2
    ACTIVATING = 3
    ACTIVE = 4
    APOPTOTIC = 5
    NECROTIC = 6
    DEAD = 7
    ANERGIC = 8
    INTERACTING = 9


# noinspection PyUnresolvedReferences
def interact_with_aspergillus(
    *,
    phagocyte_cell: PhagocyteCellData,
    phagocyte_cell_index: int,
    phagocyte_cells: CellList,
    aspergillus_cell: 'AfumigatusCellData',
    aspergillus_cell_index: int,
    phagocyte: PhagocyteModuleState,
    phagocytize: bool = False,
) -> None:
    """
    Possibly have a phagocyte phagocytize a fungal cell.

    Parameters
    ----------
    phagocyte_cell : PhagocyteCellData
    phagocyte_cell_index: int
    aspergillus_cell : AfumigatusCellData
    aspergillus_cell_index : int
    phagocyte : PhagocyteState
    phagocytize : bool
    """
    from nlisim.modules.afumigatus import AfumigatusCellState, AfumigatusCellStatus

    # We cannot internalize an already internalized fungal cell
    if aspergillus_cell['state'] != AfumigatusCellState.FREE:
        return

    # internalize conidia
    if phagocytize or aspergillus_cell['status'] in {
        AfumigatusCellStatus.RESTING_CONIDIA,
        AfumigatusCellStatus.SWELLING_CONIDIA,
        AfumigatusCellStatus.STERILE_CONIDIA,
    }:
        if phagocyte_cell['status'] not in {
            PhagocyteStatus.NECROTIC,
            PhagocyteStatus.APOPTOTIC,
            PhagocyteStatus.DEAD,
        }:
            # check to see if we have room before we add in another cell to the phagosome
            num_cells_in_phagosome = np.sum(phagocyte_cell['phagosome'] >= 0)
            if num_cells_in_phagosome < phagocyte.max_conidia:
                aspergillus_cell['state'] = AfumigatusCellState.INTERNALIZING
                # place the fungal cell in the phagosome,
                # sorting makes sure that an 'empty' i.e. -1 slot is first
                phagocyte_cell['phagosome'].sort()
                phagocyte_cell['phagosome'][0] = aspergillus_cell_index
                # move the phagocyte to the location of the aspergillus
                phagocyte_cell['point'] = aspergillus_cell['point']
                phagocyte_cells.update_voxel_index([phagocyte_cell_index])

    # All phagocytes are activated by their interaction, except with resting conidia
    if aspergillus_cell['status'] == AfumigatusCellStatus.RESTING_CONIDIA:
        return
    phagocyte_cell['state'] = PhagocyteStatus.INTERACTING
    if phagocyte_cell['status'] != PhagocyteStatus.ACTIVE:
        # non-active phagocytes begin the activation stage
        if phagocyte_cell['status'] != PhagocyteStatus.ACTIVATING:
            # reset the counter, first time only
            phagocyte_cell['status_iteration'] = 0
        phagocyte_cell['status'] = PhagocyteStatus.ACTIVATING
    else:
        # active phagocytes are kept active by resetting their iteration counter
        phagocyte_cell['status_iteration'] = 0

Functions

def interact_with_aspergillus(*, phagocyte_cell: PhagocyteCellData, phagocyte_cell_index: int, phagocyte_cells: CellList, aspergillus_cell: AfumigatusCellData, aspergillus_cell_index: int, phagocyte: PhagocyteModuleState, phagocytize: bool = False)

Possibly have a phagocyte phagocytize a fungal cell.

Parameters

phagocyte_cell : PhagocyteCellData
 
phagocyte_cell_index : int
 
aspergillus_cell : AfumigatusCellData
 
aspergillus_cell_index : int
 
phagocyte : PhagocyteState
 
phagocytize : bool
 
Expand source code
def interact_with_aspergillus(
    *,
    phagocyte_cell: PhagocyteCellData,
    phagocyte_cell_index: int,
    phagocyte_cells: CellList,
    aspergillus_cell: 'AfumigatusCellData',
    aspergillus_cell_index: int,
    phagocyte: PhagocyteModuleState,
    phagocytize: bool = False,
) -> None:
    """
    Possibly have a phagocyte phagocytize a fungal cell.

    Parameters
    ----------
    phagocyte_cell : PhagocyteCellData
    phagocyte_cell_index: int
    aspergillus_cell : AfumigatusCellData
    aspergillus_cell_index : int
    phagocyte : PhagocyteState
    phagocytize : bool
    """
    from nlisim.modules.afumigatus import AfumigatusCellState, AfumigatusCellStatus

    # We cannot internalize an already internalized fungal cell
    if aspergillus_cell['state'] != AfumigatusCellState.FREE:
        return

    # internalize conidia
    if phagocytize or aspergillus_cell['status'] in {
        AfumigatusCellStatus.RESTING_CONIDIA,
        AfumigatusCellStatus.SWELLING_CONIDIA,
        AfumigatusCellStatus.STERILE_CONIDIA,
    }:
        if phagocyte_cell['status'] not in {
            PhagocyteStatus.NECROTIC,
            PhagocyteStatus.APOPTOTIC,
            PhagocyteStatus.DEAD,
        }:
            # check to see if we have room before we add in another cell to the phagosome
            num_cells_in_phagosome = np.sum(phagocyte_cell['phagosome'] >= 0)
            if num_cells_in_phagosome < phagocyte.max_conidia:
                aspergillus_cell['state'] = AfumigatusCellState.INTERNALIZING
                # place the fungal cell in the phagosome,
                # sorting makes sure that an 'empty' i.e. -1 slot is first
                phagocyte_cell['phagosome'].sort()
                phagocyte_cell['phagosome'][0] = aspergillus_cell_index
                # move the phagocyte to the location of the aspergillus
                phagocyte_cell['point'] = aspergillus_cell['point']
                phagocyte_cells.update_voxel_index([phagocyte_cell_index])

    # All phagocytes are activated by their interaction, except with resting conidia
    if aspergillus_cell['status'] == AfumigatusCellStatus.RESTING_CONIDIA:
        return
    phagocyte_cell['state'] = PhagocyteStatus.INTERACTING
    if phagocyte_cell['status'] != PhagocyteStatus.ACTIVE:
        # non-active phagocytes begin the activation stage
        if phagocyte_cell['status'] != PhagocyteStatus.ACTIVATING:
            # reset the counter, first time only
            phagocyte_cell['status_iteration'] = 0
        phagocyte_cell['status'] = PhagocyteStatus.ACTIVATING
    else:
        # active phagocytes are kept active by resetting their iteration counter
        phagocyte_cell['status_iteration'] = 0

Classes

class PhagocyteCellData (arg: Union[int, Iterable[ForwardRef('CellData')]], initialize: bool = False, **kwargs)

A low-level data contain for an array cells.

This class is a subtype of numpy.recarray containing the lowest level representation of a list of "cells" in a simulation. The underlying data format of this type are identical to a simple array of C structures with the fields given in the static "dtype" variable.

The base class contains only a single coordinate representing the location of the center of the cell. Most implementations will want to override this class to append more fields. Subclasses must also override the base implementation of create_cell to construct a single record containing the additional fields.

For example, the following derived class adds an addition floating point value associated with each cell.

class DerivedCell(CellData):
    FIELDS = CellData.FIELDS + [
        ('iron_content', 'f8')
    ]

    dtype = np.dtype(CellData.FIELDS, align=True)

    @classmethod
    def create_cell_tuple(cls, iron_content=0, **kwargs) -> Tuple:
        return CellData.create_cell_tuple(**kwargs) + (iron_content,)
Expand source code
class PhagocyteCellData(CellData):
    PHAGOCYTE_FIELDS: CellFields = [
        ('phagosome', np.int64, MAX_CONIDIA),
    ]

    dtype = np.dtype(CellData.FIELDS + PHAGOCYTE_FIELDS, align=True)  # type: ignore

    @classmethod
    def create_cell_tuple(
        cls,
        **kwargs,
    ) -> Tuple:
        initializer = {
            'phagosome': kwargs.get('phagosome', -1 * np.ones(MAX_CONIDIA, dtype=np.int64)),
        }

        # ensure that these come in the correct order
        return CellData.create_cell_tuple(**kwargs) + tuple(
            [initializer[key] for key, *_ in PhagocyteCellData.PHAGOCYTE_FIELDS]
        )

Ancestors

Subclasses

Class variables

var PHAGOCYTE_FIELDS : List[Union[Tuple[str, numpy.dtype], Tuple[str, Type[Any]], Tuple[str, Type[Any], int], Tuple[str, str], Tuple[str, str, int]]]

Inherited members

class PhagocyteModel (config: SimulationConfig)
Expand source code
class PhagocyteModel(ModuleModel):
    def single_step_move(
        self, state: State, cell: PhagocyteCellData, cell_index: int, cell_list: CellList
    ) -> None:
        """
        Move the phagocyte one 1 µm, probabilistically.

        depending on single_step_probabilistic_drift

        Parameters
        ----------
        state : State
            the global simulation state
        cell : PhagocyteCellData
            the cell to move
        cell_index : int
            index of cell in cell_list
        cell_list : CellList
            the CellList for the cell-type (macrophage/neutrophil/etc) of cell


        Returns
        -------
        nothing
        """
        grid: RectangularGrid = state.grid

        # At this moment, there is no inter-voxel geometry.
        cell_voxel: Voxel = grid.get_voxel(cell['point'])
        new_point: Point = self.single_step_probabilistic_drift(state, cell, cell_voxel)
        cell['point'] = new_point
        cell_list.update_voxel_index([cell_index])

    @abstractmethod
    def single_step_probabilistic_drift(
        self, state: State, cell: PhagocyteCellData, voxel: Voxel
    ) -> Point:
        ...

    @staticmethod
    def release_phagosome(state: State, phagocyte_cell: PhagocyteCellData) -> None:
        """
        Release afumigatus cells in the phagosome

        Parameters
        ----------
        state : State
            global simulation state
        phagocyte_cell : PhagocyteCellData


        Returns
        -------
        Nothing
        """
        from nlisim.modules.afumigatus import AfumigatusCellState, AfumigatusState

        afumigatus: AfumigatusState = state.afumigatus

        for fungal_cell_index in phagocyte_cell['phagosome']:
            if fungal_cell_index == -1:
                continue
            afumigatus.cells[fungal_cell_index]['state'] = AfumigatusCellState.RELEASING
        phagocyte_cell['phagosome'][:] = -1

Ancestors

Subclasses

Static methods

def release_phagosome(state: State, phagocyte_cell: PhagocyteCellData) ‑> None

Release afumigatus cells in the phagosome

Parameters

state : State
global simulation state
phagocyte_cell : PhagocyteCellData
 

Returns

Nothing
 
Expand source code
@staticmethod
def release_phagosome(state: State, phagocyte_cell: PhagocyteCellData) -> None:
    """
    Release afumigatus cells in the phagosome

    Parameters
    ----------
    state : State
        global simulation state
    phagocyte_cell : PhagocyteCellData


    Returns
    -------
    Nothing
    """
    from nlisim.modules.afumigatus import AfumigatusCellState, AfumigatusState

    afumigatus: AfumigatusState = state.afumigatus

    for fungal_cell_index in phagocyte_cell['phagosome']:
        if fungal_cell_index == -1:
            continue
        afumigatus.cells[fungal_cell_index]['state'] = AfumigatusCellState.RELEASING
    phagocyte_cell['phagosome'][:] = -1

Methods

def single_step_move(self, state: State, cell: PhagocyteCellData, cell_index: int, cell_list: CellList) ‑> None

Move the phagocyte one 1 µm, probabilistically.

depending on single_step_probabilistic_drift

Parameters

state : State
the global simulation state
cell : PhagocyteCellData
the cell to move
cell_index : int
index of cell in cell_list
cell_list : CellList
the CellList for the cell-type (macrophage/neutrophil/etc) of cell

Returns

nothing
 
Expand source code
def single_step_move(
    self, state: State, cell: PhagocyteCellData, cell_index: int, cell_list: CellList
) -> None:
    """
    Move the phagocyte one 1 µm, probabilistically.

    depending on single_step_probabilistic_drift

    Parameters
    ----------
    state : State
        the global simulation state
    cell : PhagocyteCellData
        the cell to move
    cell_index : int
        index of cell in cell_list
    cell_list : CellList
        the CellList for the cell-type (macrophage/neutrophil/etc) of cell


    Returns
    -------
    nothing
    """
    grid: RectangularGrid = state.grid

    # At this moment, there is no inter-voxel geometry.
    cell_voxel: Voxel = grid.get_voxel(cell['point'])
    new_point: Point = self.single_step_probabilistic_drift(state, cell, cell_voxel)
    cell['point'] = new_point
    cell_list.update_voxel_index([cell_index])
def single_step_probabilistic_drift(self, state: State, cell: PhagocyteCellData, voxel: Voxel) ‑> Point
Expand source code
@abstractmethod
def single_step_probabilistic_drift(
    self, state: State, cell: PhagocyteCellData, voxel: Voxel
) -> Point:
    ...

Inherited members

class PhagocyteModuleState (*, global_state: State)

Base type intended to store the state for simulation modules.

This class contains serialization support for basic types (float, int, str, bool) and numpy arrays of those types. Modules containing more complicated state must override the serialization mechanism with custom behavior.

Method generated by attrs for class PhagocyteModuleState.

Expand source code
class PhagocyteModuleState(ModuleState):
    max_conidia: int  # units: count

Ancestors

Subclasses

Class variables

var max_conidia : int

Inherited members

class PhagocyteState (value, names=None, *, module=None, qualname=None, type=None, start=1)

An enumeration.

Expand source code
class PhagocyteState(IntEnum):
    FREE = 0
    INTERACTING = 1

Ancestors

  • enum.IntEnum
  • builtins.int
  • enum.Enum

Class variables

var FREE
var INTERACTING
class PhagocyteStatus (value, names=None, *, module=None, qualname=None, type=None, start=1)

An enumeration.

Expand source code
class PhagocyteStatus(IntEnum):
    INACTIVE = 0
    INACTIVATING = 1
    RESTING = 2
    ACTIVATING = 3
    ACTIVE = 4
    APOPTOTIC = 5
    NECROTIC = 6
    DEAD = 7
    ANERGIC = 8
    INTERACTING = 9

Ancestors

  • enum.IntEnum
  • builtins.int
  • enum.Enum

Class variables

var ACTIVATING
var ACTIVE
var ANERGIC
var APOPTOTIC
var DEAD
var INACTIVATING
var INACTIVE
var INTERACTING
var NECROTIC
var RESTING