Ejemplo n.º 1
0
    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)
Ejemplo n.º 2
0
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
Ejemplo n.º 3
0
    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
Ejemplo n.º 4
0
    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