def initialize_spores(self, tissue: np.ndarray, init_num: int): """Initialize spores on epithelium cells.""" grid = self.grid if init_num > 0: points = np.zeros((init_num, 3)) indices = np.argwhere(tissue == TissueTypes.EPITHELIUM.value) if len(indices) > 0: rg.shuffle(indices) for i in range(init_num): # putting in some protection for the occasional time that we place the cell on # the boundary of the voxel-space if indices[i][2] == grid.xv.shape[0] - 1: x = grid.xv[indices[i][2]] else: x = rg.uniform(grid.xv[indices[i][2]], grid.xv[indices[i][2] + 1]) if indices[i][1] == grid.yv.shape[0] - 1: y = grid.yv[indices[i][1]] else: y = rg.uniform(grid.yv[indices[i][1]], grid.yv[indices[i][1] + 1]) if indices[i][0] == grid.zv.shape[0] - 1: z = grid.zv[indices[i][0]] else: z = rg.uniform(grid.zv[indices[i][0]], grid.zv[indices[i][0] + 1]) point = Point(x=x, y=y, z=z) points[i] = point self.spawn_spores(points)
def advance(state: State, target_time: float) -> Iterator[State]: """Advance a simulation to the given target time.""" initial_time = state.time @dataclass(order=True) class ModuleUpdateEvent: event_time: float previous_update: float module: ModuleModel = field(compare=False) # Create and fill a queue of modules to run. This allows for modules to # operate on disparate time scales. Modules which do not have a time step # set will not be run. queue: PriorityQueue[ModuleUpdateEvent] = PriorityQueue() for module in state.config.modules: if module.time_step is not None and module.time_step > 0: queue.put( ModuleUpdateEvent(event_time=initial_time, previous_update=initial_time, module=module)) # run the simulation until we meet or surpass the desired time # while-loop conditional is on previous time so that all pending # modules are run on final iteration previous_time: float = initial_time while previous_time < target_time and not queue.empty(): # fill a list with update events that are concurrent, and randomize their order concurrent_update_events = [queue.get()] event_time = concurrent_update_events[0].event_time while not queue.empty(): update_event = queue.get() if update_event.event_time == event_time: concurrent_update_events.append(update_event) else: queue.put(update_event) break rg.shuffle(concurrent_update_events) for update_event in concurrent_update_events: m: ModuleModel = update_event.module previous_time = update_event.previous_update state.time = update_event.event_time with validation_context(m.name): state = m.advance(state, previous_time) attr.validate(state) # reinsert module with updated time queue.put( ModuleUpdateEvent(event_time=state.time + m.time_step, previous_update=state.time, module=m)) yield state
def initialize(self, state: State): pneumocyte: PneumocyteState = state.pneumocyte voxel_volume: float = state.voxel_volume time_step_size: float = self.time_step lung_tissue: np.ndarray = state.lung_tissue pneumocyte.max_conidia = self.config.getint( 'max_conidia') # units: conidia pneumocyte.time_to_rest = self.config.getint( 'time_to_rest') # units: hours pneumocyte.time_to_change_state = self.config.getint( 'time_to_change_state') # units: hours pneumocyte.p_tnf_qtty = self.config.getfloat( 'p_tnf_qtty') # units: atto-mol * cell^-1 * h^-1 pneumocyte.pr_p_int_param = self.config.getfloat('pr_p_int_param') # computed values pneumocyte.iter_to_rest = int( pneumocyte.time_to_rest * (60 / self.time_step)) # units: hours * (min/hour) / (min/step) = step pneumocyte.iter_to_change_state = int( pneumocyte.time_to_change_state * (60 / self.time_step)) # units: hours * (min/hour) / (min/step) = step pneumocyte.pr_p_int = -math.expm1( -time_step_size / 60 / (voxel_volume * pneumocyte.pr_p_int_param)) # units: probability # initialize cells, placing one per epithelial voxel dz_field: np.ndarray = state.grid.delta(axis=0) dy_field: np.ndarray = state.grid.delta(axis=1) dx_field: np.ndarray = state.grid.delta(axis=2) epithelial_voxels = list( zip(*np.where(lung_tissue == TissueType.EPITHELIUM))) rg.shuffle(epithelial_voxels) for vox_z, vox_y, vox_x in epithelial_voxels[:self.config. getint('count')]: # the x,y,z coordinates are in the centers of the grids z = state.grid.z[vox_z] y = state.grid.y[vox_y] x = state.grid.x[vox_x] dz = dz_field[vox_z, vox_y, vox_x] dy = dy_field[vox_z, vox_y, vox_x] dx = dx_field[vox_z, vox_y, vox_x] pneumocyte.cells.append( PneumocyteCellData.create_cell(point=Point( x=x + rg.uniform(-dx / 2, dx / 2), y=y + rg.uniform(-dy / 2, dy / 2), z=z + rg.uniform(-dz / 2, dz / 2), ))) return state
def advance(self, state: State, previous_time: float) -> State: """Advance the state by a single time step.""" from nlisim.modules.iron import IronState from nlisim.modules.macrophage import MacrophageCellData, MacrophageState from nlisim.modules.molecules import MoleculesState from nlisim.modules.neutrophil import NeutrophilCellData, NeutrophilState from nlisim.modules.phagocyte import PhagocyteState, PhagocyteStatus from nlisim.modules.transferrin import TransferrinState lactoferrin: LactoferrinState = state.lactoferrin transferrin: TransferrinState = state.transferrin iron: IronState = state.iron molecules: MoleculesState = state.molecules macrophage: MacrophageState = state.macrophage neutrophil: NeutrophilState = state.neutrophil grid: RectangularGrid = state.grid voxel_volume = state.voxel_volume # macrophages uptake iron from lactoferrin live_macrophages = macrophage.cells.alive() rg.shuffle(live_macrophages) for macrophage_cell_index in live_macrophages: macrophage_cell: MacrophageCellData = macrophage.cells[ macrophage_cell_index] macrophage_cell_voxel: Voxel = grid.get_voxel( macrophage_cell['point']) uptake_proportion = np.minimum(lactoferrin.ma_iron_import_rate, 1.0) qtty_fe2 = (lactoferrin.grid['LactoferrinFe2'][tuple( macrophage_cell_voxel)] * uptake_proportion) qtty_fe = ( lactoferrin.grid['LactoferrinFe'][tuple(macrophage_cell_voxel)] * uptake_proportion) lactoferrin.grid['LactoferrinFe2'][tuple( macrophage_cell_voxel)] -= qtty_fe2 lactoferrin.grid['LactoferrinFe'][tuple( macrophage_cell_voxel)] -= qtty_fe macrophage_cell['iron_pool'] += 2 * qtty_fe2 + qtty_fe # active and interacting neutrophils secrete lactoferrin for neutrophil_cell_index in neutrophil.cells.alive(): neutrophil_cell: NeutrophilCellData = neutrophil.cells[ neutrophil_cell_index] if (neutrophil_cell['status'] != PhagocyteStatus.ACTIVE or neutrophil_cell['state'] != PhagocyteState.INTERACTING): continue neutrophil_cell_voxel: Voxel = grid.get_voxel( neutrophil_cell['point']) lactoferrin.grid['Lactoferrin'][ tuple(neutrophil_cell_voxel )] += lactoferrin.neutrophil_secretion_rate_unit_t # interaction with transferrin # - calculate iron transfer from transferrin+[1,2]Fe to lactoferrin dfe2_dt = michaelian_kinetics( substrate=transferrin.grid['TfFe2'], enzyme=lactoferrin.grid["Lactoferrin"], k_m=lactoferrin.k_m_tf_lac, h=self.time_step / 60, # units: (min/step) / (min/hour) k_cat=1.0, voxel_volume=voxel_volume, ) dfe_dt = michaelian_kinetics( substrate=transferrin.grid['TfFe'], enzyme=lactoferrin.grid['Lactoferrin'], k_m=lactoferrin.k_m_tf_lac, h=self.time_step / 60, # units: (min/step) / (min/hour) k_cat=1.0, voxel_volume=voxel_volume, ) # - enforce bounds from lactoferrin quantity dfex_dt = dfe2_dt + dfe_dt mask = dfex_dt > lactoferrin.grid['Lactoferrin'] rel = lactoferrin.grid['Lactoferrin'] / (dfex_dt + EPSILON) # enforce bounds rel[dfex_dt == 0] = 0.0 rel[:] = np.maximum(np.minimum(rel, 1.0), 0.0) dfe2_dt[mask] = (dfe2_dt * rel)[mask] dfe_dt[mask] = (dfe_dt * rel)[mask] # - calculate iron transfer from transferrin+[1,2]Fe to lactoferrin+Fe dfe2_dt_fe = michaelian_kinetics( substrate=transferrin.grid['TfFe2'], enzyme=lactoferrin.grid['LactoferrinFe'], k_m=lactoferrin.k_m_tf_lac, h=self.time_step / 60, # units: (min/step) / (min/hour) k_cat=1.0, voxel_volume=voxel_volume, ) dfe_dt_fe = michaelian_kinetics( substrate=transferrin.grid['TfFe'], enzyme=lactoferrin.grid['LactoferrinFe'], k_m=lactoferrin.k_m_tf_lac, h=self.time_step / 60, # units: (min/step) / (min/hour) k_cat=1.0, voxel_volume=voxel_volume, ) # - enforce bounds from lactoferrin+Fe quantity dfex_dt_fe = dfe2_dt_fe + dfe_dt_fe mask = dfex_dt_fe > lactoferrin.grid['LactoferrinFe'] rel = lactoferrin.grid['LactoferrinFe'] / (dfe2_dt_fe + dfe_dt_fe + EPSILON) # enforce bounds rel[dfex_dt_fe == 0] = 0.0 np.minimum(rel, 1.0, out=rel) np.maximum(rel, 0.0, out=rel) dfe2_dt_fe[mask] = (dfe2_dt_fe * rel)[mask] dfe_dt_fe[mask] = (dfe_dt_fe * rel)[mask] # transferrin+2Fe loses an iron, becomes transferrin+Fe transferrin.grid['TfFe2'] -= dfe2_dt + dfe2_dt_fe transferrin.grid['TfFe'] += dfe2_dt + dfe2_dt_fe # transferrin+Fe loses an iron, becomes transferrin transferrin.grid['TfFe'] -= dfe_dt + dfe_dt_fe transferrin.grid['Tf'] += dfe_dt + dfe_dt_fe # lactoferrin gains an iron, becomes lactoferrin+Fe lactoferrin.grid['Lactoferrin'] -= dfe2_dt + dfe_dt lactoferrin.grid['LactoferrinFe'] += dfe2_dt + dfe_dt # lactoferrin+Fe gains an iron, becomes lactoferrin+2Fe lactoferrin.grid['LactoferrinFe'] -= dfe2_dt_fe + dfe_dt_fe lactoferrin.grid['LactoferrinFe2'] += dfe2_dt_fe + dfe_dt_fe # interaction with iron lactoferrin_fe_capacity = (2 * lactoferrin.grid["Lactoferrin"] + lactoferrin.grid["LactoferrinFe"]) potential_reactive_quantity = np.minimum(iron.grid, lactoferrin_fe_capacity) rel_tf_fe = iron_tf_reaction( iron=potential_reactive_quantity, tf=lactoferrin.grid["Lactoferrin"], tf_fe=lactoferrin.grid["LactoferrinFe"], p1=lactoferrin.p1, p2=lactoferrin.p2, p3=lactoferrin.p3, ) tffe_qtty = rel_tf_fe * potential_reactive_quantity tffe2_qtty = (potential_reactive_quantity - tffe_qtty) / 2 lactoferrin.grid['Lactoferrin'] -= tffe_qtty + tffe2_qtty lactoferrin.grid['LactoferrinFe'] += tffe_qtty lactoferrin.grid['LactoferrinFe2'] += tffe2_qtty iron.grid -= potential_reactive_quantity # Degrade Lactoferrin # Note: ideally, this would be a constant computed in initialize, but we would have to # know that "molecules" is initialized first trnvr_rt = turnover_rate( x=np.array(1.0, dtype=np.float64), x_system=0.0, base_turnover_rate=molecules.turnover_rate, rel_cyt_bind_unit_t=molecules.rel_cyt_bind_unit_t, ) lactoferrin.grid['Lactoferrin'] *= trnvr_rt lactoferrin.grid['LactoferrinFe'] *= trnvr_rt lactoferrin.grid['LactoferrinFe2'] *= trnvr_rt # Diffusion of lactoferrin for component in {'Lactoferrin', 'LactoferrinFe', 'LactoferrinFe2'}: lactoferrin.grid[component][:] = apply_diffusion( variable=lactoferrin.grid[component], laplacian=molecules.laplacian, diffusivity=molecules.diffusion_constant, dt=self.time_step, ) return state