Module nlisim.oldmodules.phagocyte
Expand source code
from enum import IntEnum
import math
import random
from typing import Tuple
import attr
import numpy as np
from nlisim.cell import CellData, CellList
from nlisim.coordinates import Point, Voxel
from nlisim.grid import RectangularGrid
from nlisim.random import rg
from nlisim.util import TissueType
MAX_PHAGOSOME_LENGTH = 100
class PhagocyteCellData(CellData):
RECRUIT_RATE = 0.0
LEAVE_RATE = 0.0
CHEMOKINE_THRESHOLD = 0.0
LEAVES_BOOL = True
MAX_CONIDIA = MAX_PHAGOSOME_LENGTH
class Status(IntEnum):
INACTIVE = 0
INACTIVATING = 1
RESTING = 2
ACTIVATING = 3
ACTIVE = 4
APOPTOTIC = 5
NECROTIC = 6
DEAD = 7
class State(IntEnum):
FREE = 0
INTERACTING = 1
PHAGOCYTE_FIELDS = [
('status', 'u1'),
('state', 'u1'),
('iron_pool', 'f8'),
('iteration', 'i4'),
('phagosome', (np.int32, (MAX_CONIDIA))),
]
dtype = np.dtype(CellData.FIELDS + PHAGOCYTE_FIELDS, align=True) # type: ignore
@classmethod
def create_cell_tuple(
cls,
*,
iron_pool: float = 0,
status: Status = Status.RESTING,
state: State = State.FREE,
**kwargs,
) -> Tuple:
iteration = 0
phagosome = np.empty(MAX_PHAGOSOME_LENGTH)
phagosome.fill(-1)
return CellData.create_cell_tuple(**kwargs) + (
status,
state,
iron_pool,
iteration,
phagosome,
)
@attr.s(kw_only=True, frozen=True, repr=False)
class PhagocyteCellList(CellList):
CellDataClass = PhagocyteCellData
def is_moveable(self, grid: RectangularGrid):
cells = self.cell_data
return self.alive(
(cells['status'] == PhagocyteCellData.Status.RESTING)
& cells.point_mask(cells['point'], grid)
)
def len_phagosome(self, index):
cell = self[index]
return len(np.argwhere(cell['phagosome'] != -1))
def append_to_phagosome(self, index, pathogen_index, max_size):
cell = self[index]
index_to_append = PhagocyteCellList.len_phagosome(self, index)
if index_to_append < MAX_PHAGOSOME_LENGTH and index_to_append < max_size:
cell['phagosome'][index_to_append] = pathogen_index
return True
else:
return False
def remove_from_phagosome(self, index, pathogen_index):
phagosome = self[index]['phagosome']
if pathogen_index in phagosome:
itemindex = np.argwhere(phagosome == pathogen_index)[0][0]
size = PhagocyteCellList.len_phagosome(self, index)
if itemindex == size - 1:
# full phagosome
phagosome[itemindex] = -1
return True
else:
phagosome[itemindex:-1] = phagosome[itemindex + 1 :]
phagosome[-1] = -1
return True
else:
return False
def clear_all_phagosome(self, index):
self[index]['phagosome'].fill(-1)
def recruit(self, rate, molecule, grid: RectangularGrid):
# TODO - add recruitment
# indices = np.argwhere(molecule_to_recruit >= threshold_value)
# then for each index create a cell with prob 'rec_rate'
return
def remove(self, rate, molecule, grid: RectangularGrid):
# TODO - add leaving
# indices = np.argwhere(molecule_to_leave <= threshold_value)
# then for each index kill a cell with prob 'leave_rate'
return
# move
def chemotaxis(
self,
molecule,
drift_lambda,
drift_bias,
tissue,
grid: RectangularGrid,
):
# 'molecule' = state.'molecule'.concentration
# prob = 0-1 random number to determine which voxel is chosen to move
# 1. Get cells that are alive
for index in self.alive():
prob = rg.random()
# 2. Get voxel for each cell to get molecule in that voxel
cell = self[index]
vox = grid.get_voxel(cell['point'])
# 3. Set prob for neighboring voxels
p = []
vox_list = []
p_tot = 0.0
i = -1
# calculate individual probability
for x in [0, 1, -1]:
for y in [0, 1, -1]:
for z in [0, 1, -1]:
p.append(0.0)
vox_list.append([x, y, z])
i += 1
zk = vox.z + z
yj = vox.y + y
xi = vox.x + x
if grid.is_valid_voxel(Voxel(x=xi, y=yj, z=zk)):
if tissue[zk, yj, xi] in [
TissueType.SURFACTANT.value,
TissueType.BLOOD.value,
TissueType.EPITHELIUM.value,
TissueType.PORE.value,
]:
p[i] = logistic(molecule[zk, yj, xi], drift_lambda, drift_bias)
p_tot += p[i]
# scale to sum of probabilities
if p_tot:
for i in range(len(p)):
p[i] = p[i] / p_tot
# chose vox from neighbors
cum_p = 0.0
for i in range(len(p)):
cum_p += p[i]
if prob <= cum_p:
cell['point'] = Point(
x=random.uniform(
grid.xv[vox.x + vox_list[i][0]], grid.xv[vox.x + vox_list[i][0] + 1]
),
y=random.uniform(
grid.yv[vox.y + vox_list[i][1]], grid.yv[vox.y + vox_list[i][1] + 1]
),
z=random.uniform(
grid.zv[vox.z + vox_list[i][2]], grid.zv[vox.z + vox_list[i][2] + 1]
),
)
self.update_voxel_index([index])
break
def logistic(x, lamb, bias):
return 1 - bias * math.exp(-((x / lamb) ** 2))
Functions
def logistic(x, lamb, bias)
-
Expand source code
def logistic(x, lamb, bias): return 1 - bias * math.exp(-((x / lamb) ** 2))
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): RECRUIT_RATE = 0.0 LEAVE_RATE = 0.0 CHEMOKINE_THRESHOLD = 0.0 LEAVES_BOOL = True MAX_CONIDIA = MAX_PHAGOSOME_LENGTH class Status(IntEnum): INACTIVE = 0 INACTIVATING = 1 RESTING = 2 ACTIVATING = 3 ACTIVE = 4 APOPTOTIC = 5 NECROTIC = 6 DEAD = 7 class State(IntEnum): FREE = 0 INTERACTING = 1 PHAGOCYTE_FIELDS = [ ('status', 'u1'), ('state', 'u1'), ('iron_pool', 'f8'), ('iteration', 'i4'), ('phagosome', (np.int32, (MAX_CONIDIA))), ] dtype = np.dtype(CellData.FIELDS + PHAGOCYTE_FIELDS, align=True) # type: ignore @classmethod def create_cell_tuple( cls, *, iron_pool: float = 0, status: Status = Status.RESTING, state: State = State.FREE, **kwargs, ) -> Tuple: iteration = 0 phagosome = np.empty(MAX_PHAGOSOME_LENGTH) phagosome.fill(-1) return CellData.create_cell_tuple(**kwargs) + ( status, state, iron_pool, iteration, phagosome, )
Ancestors
- CellData
- numpy.ndarray
Class variables
var CHEMOKINE_THRESHOLD
var LEAVES_BOOL
var LEAVE_RATE
var MAX_CONIDIA
var PHAGOCYTE_FIELDS
var RECRUIT_RATE
var State
-
An enumeration.
var Status
-
An enumeration.
Inherited members
class PhagocyteCellList (*, grid: RectangularGrid, max_cells: int = 1000000, cell_data: CellData = NOTHING)
-
A python view on top of a CellData array.
This class represents a pythonic interface to the data contained in a CellData array. Because the CellData class is a low-level object, it does not allow dynamically appending new elements. Objects of this class get around this limitation by pre-allocating a large block of memory that is transparently available. User-facing properties are sliced to make it appear as if the extra data is not there.
Subclassed types are expected to set the
CellDataClass
attribute to a subclass ofCellData
. This provides information about the underlying low-level array.Parameters
grid
:simulation.grid.RectangularGrid
max_cells
:int
, optionalcells
:simulation.cell.CellData
, optional
Method generated by attrs for class PhagocyteCellList.
Expand source code
class PhagocyteCellList(CellList): CellDataClass = PhagocyteCellData def is_moveable(self, grid: RectangularGrid): cells = self.cell_data return self.alive( (cells['status'] == PhagocyteCellData.Status.RESTING) & cells.point_mask(cells['point'], grid) ) def len_phagosome(self, index): cell = self[index] return len(np.argwhere(cell['phagosome'] != -1)) def append_to_phagosome(self, index, pathogen_index, max_size): cell = self[index] index_to_append = PhagocyteCellList.len_phagosome(self, index) if index_to_append < MAX_PHAGOSOME_LENGTH and index_to_append < max_size: cell['phagosome'][index_to_append] = pathogen_index return True else: return False def remove_from_phagosome(self, index, pathogen_index): phagosome = self[index]['phagosome'] if pathogen_index in phagosome: itemindex = np.argwhere(phagosome == pathogen_index)[0][0] size = PhagocyteCellList.len_phagosome(self, index) if itemindex == size - 1: # full phagosome phagosome[itemindex] = -1 return True else: phagosome[itemindex:-1] = phagosome[itemindex + 1 :] phagosome[-1] = -1 return True else: return False def clear_all_phagosome(self, index): self[index]['phagosome'].fill(-1) def recruit(self, rate, molecule, grid: RectangularGrid): # TODO - add recruitment # indices = np.argwhere(molecule_to_recruit >= threshold_value) # then for each index create a cell with prob 'rec_rate' return def remove(self, rate, molecule, grid: RectangularGrid): # TODO - add leaving # indices = np.argwhere(molecule_to_leave <= threshold_value) # then for each index kill a cell with prob 'leave_rate' return # move def chemotaxis( self, molecule, drift_lambda, drift_bias, tissue, grid: RectangularGrid, ): # 'molecule' = state.'molecule'.concentration # prob = 0-1 random number to determine which voxel is chosen to move # 1. Get cells that are alive for index in self.alive(): prob = rg.random() # 2. Get voxel for each cell to get molecule in that voxel cell = self[index] vox = grid.get_voxel(cell['point']) # 3. Set prob for neighboring voxels p = [] vox_list = [] p_tot = 0.0 i = -1 # calculate individual probability for x in [0, 1, -1]: for y in [0, 1, -1]: for z in [0, 1, -1]: p.append(0.0) vox_list.append([x, y, z]) i += 1 zk = vox.z + z yj = vox.y + y xi = vox.x + x if grid.is_valid_voxel(Voxel(x=xi, y=yj, z=zk)): if tissue[zk, yj, xi] in [ TissueType.SURFACTANT.value, TissueType.BLOOD.value, TissueType.EPITHELIUM.value, TissueType.PORE.value, ]: p[i] = logistic(molecule[zk, yj, xi], drift_lambda, drift_bias) p_tot += p[i] # scale to sum of probabilities if p_tot: for i in range(len(p)): p[i] = p[i] / p_tot # chose vox from neighbors cum_p = 0.0 for i in range(len(p)): cum_p += p[i] if prob <= cum_p: cell['point'] = Point( x=random.uniform( grid.xv[vox.x + vox_list[i][0]], grid.xv[vox.x + vox_list[i][0] + 1] ), y=random.uniform( grid.yv[vox.y + vox_list[i][1]], grid.yv[vox.y + vox_list[i][1] + 1] ), z=random.uniform( grid.zv[vox.z + vox_list[i][2]], grid.zv[vox.z + vox_list[i][2] + 1] ), ) self.update_voxel_index([index]) break
Ancestors
Class variables
var grid : RectangularGrid
var max_cells : int
Methods
def append_to_phagosome(self, index, pathogen_index, max_size)
-
Expand source code
def append_to_phagosome(self, index, pathogen_index, max_size): cell = self[index] index_to_append = PhagocyteCellList.len_phagosome(self, index) if index_to_append < MAX_PHAGOSOME_LENGTH and index_to_append < max_size: cell['phagosome'][index_to_append] = pathogen_index return True else: return False
def chemotaxis(self, molecule, drift_lambda, drift_bias, tissue, grid: RectangularGrid)
-
Expand source code
def chemotaxis( self, molecule, drift_lambda, drift_bias, tissue, grid: RectangularGrid, ): # 'molecule' = state.'molecule'.concentration # prob = 0-1 random number to determine which voxel is chosen to move # 1. Get cells that are alive for index in self.alive(): prob = rg.random() # 2. Get voxel for each cell to get molecule in that voxel cell = self[index] vox = grid.get_voxel(cell['point']) # 3. Set prob for neighboring voxels p = [] vox_list = [] p_tot = 0.0 i = -1 # calculate individual probability for x in [0, 1, -1]: for y in [0, 1, -1]: for z in [0, 1, -1]: p.append(0.0) vox_list.append([x, y, z]) i += 1 zk = vox.z + z yj = vox.y + y xi = vox.x + x if grid.is_valid_voxel(Voxel(x=xi, y=yj, z=zk)): if tissue[zk, yj, xi] in [ TissueType.SURFACTANT.value, TissueType.BLOOD.value, TissueType.EPITHELIUM.value, TissueType.PORE.value, ]: p[i] = logistic(molecule[zk, yj, xi], drift_lambda, drift_bias) p_tot += p[i] # scale to sum of probabilities if p_tot: for i in range(len(p)): p[i] = p[i] / p_tot # chose vox from neighbors cum_p = 0.0 for i in range(len(p)): cum_p += p[i] if prob <= cum_p: cell['point'] = Point( x=random.uniform( grid.xv[vox.x + vox_list[i][0]], grid.xv[vox.x + vox_list[i][0] + 1] ), y=random.uniform( grid.yv[vox.y + vox_list[i][1]], grid.yv[vox.y + vox_list[i][1] + 1] ), z=random.uniform( grid.zv[vox.z + vox_list[i][2]], grid.zv[vox.z + vox_list[i][2] + 1] ), ) self.update_voxel_index([index]) break
def clear_all_phagosome(self, index)
-
Expand source code
def clear_all_phagosome(self, index): self[index]['phagosome'].fill(-1)
def is_moveable(self, grid: RectangularGrid)
-
Expand source code
def is_moveable(self, grid: RectangularGrid): cells = self.cell_data return self.alive( (cells['status'] == PhagocyteCellData.Status.RESTING) & cells.point_mask(cells['point'], grid) )
def len_phagosome(self, index)
-
Expand source code
def len_phagosome(self, index): cell = self[index] return len(np.argwhere(cell['phagosome'] != -1))
def recruit(self, rate, molecule, grid: RectangularGrid)
-
Expand source code
def recruit(self, rate, molecule, grid: RectangularGrid): # TODO - add recruitment # indices = np.argwhere(molecule_to_recruit >= threshold_value) # then for each index create a cell with prob 'rec_rate' return
def remove(self, rate, molecule, grid: RectangularGrid)
-
Expand source code
def remove(self, rate, molecule, grid: RectangularGrid): # TODO - add leaving # indices = np.argwhere(molecule_to_leave <= threshold_value) # then for each index kill a cell with prob 'leave_rate' return
def remove_from_phagosome(self, index, pathogen_index)
-
Expand source code
def remove_from_phagosome(self, index, pathogen_index): phagosome = self[index]['phagosome'] if pathogen_index in phagosome: itemindex = np.argwhere(phagosome == pathogen_index)[0][0] size = PhagocyteCellList.len_phagosome(self, index) if itemindex == size - 1: # full phagosome phagosome[itemindex] = -1 return True else: phagosome[itemindex:-1] = phagosome[itemindex + 1 :] phagosome[-1] = -1 return True else: return False
Inherited members