diff --git a/src/arcade/patch/agent/action/PatchActionMutate.java b/src/arcade/patch/agent/action/PatchActionMutate.java new file mode 100644 index 000000000..3df65fc25 --- /dev/null +++ b/src/arcade/patch/agent/action/PatchActionMutate.java @@ -0,0 +1,132 @@ +package arcade.patch.agent.action; + +import java.util.ArrayList; +import sim.engine.Schedule; +import sim.engine.SimState; +import sim.util.Bag; +import arcade.core.agent.action.Action; +import arcade.core.env.location.Location; +import arcade.core.sim.Series; +import arcade.core.sim.Simulation; +import arcade.core.util.MiniBox; +import arcade.patch.agent.cell.PatchCell; +import arcade.patch.agent.cell.PatchCellContainer; +import arcade.patch.env.grid.PatchGrid; +import arcade.patch.env.location.Coordinate; +import arcade.patch.sim.PatchSeries; +import arcade.patch.sim.PatchSimulation; +import static arcade.patch.util.PatchEnums.Ordering; + +/** + * Implementation of {@link Action} for converting cells to a different class. + * + *

The action is stepped once after {@code TIME_DELAY}. The action will convert all the healthy + * tissue cells within the given radius into cancer cells. + */ +public class PatchActionMutate implements Action { + /** Time delay before calling the action [min]. */ + private final int timeDelay; + + /** Grid radius where cells are mutated. */ + private final int mutateRadius; + + /** Population code for cancer. */ + private int cancerPop; + + /** Population code for healthy cells. */ + private int healthyPop; + + /** Grid depth that cells are mutated. */ + private final int mutateDepth; + + /** + * Creates a {@link Action} for converting healthy cell agents into cancer cell agents. + * + *

Loaded parameters include: + * + *

+ * + * @param series the simulation series + * @param parameters the component parameters dictionary + */ + public PatchActionMutate(Series series, MiniBox parameters) { + // Set loaded parameters. + timeDelay = parameters.getInt("TIME_DELAY"); + mutateRadius = parameters.getInt("MUTATE_RADIUS"); + mutateDepth = ((PatchSeries) series).depth; + healthyPop = -1; + cancerPop = -1; + // Grab popuation codes for healthy and cancer cells. + for (String popName : series.populations.keySet()) { + if (series.populations.get(popName).get("CLASS").contains("tissue")) { + healthyPop = series.populations.get(popName).getInt("CODE"); + } else if (series.populations.get(popName).get("CLASS").contains("cancer_stem") + || series.populations.get(popName).get("CLASS").contains("cancer")) { + cancerPop = series.populations.get(popName).getInt("CODE"); + } + } + if (healthyPop == -1 || cancerPop == -1) { + throw new IllegalArgumentException( + "Please initialize both healthy and cancer populations in input file."); + } + } + + @Override + public void schedule(Schedule schedule) { + schedule.scheduleOnce(timeDelay, Ordering.ACTIONS.ordinal(), this); + } + + @Override + public void register(Simulation sim, String population) {} + + @Override + public void step(SimState simstate) { + PatchSimulation sim = (PatchSimulation) simstate; + PatchGrid grid = (PatchGrid) sim.getGrid(); + + // Get cells at center of simulation. + ArrayList coordinates = + sim.locationFactory.getCoordinates(mutateRadius, mutateDepth); + for (Coordinate coordinate : coordinates) { + Bag bag = (Bag) grid.getObjectAt(coordinate.hashCode()); + + if (bag == null) { + continue; + } + + for (int i = 0; i < bag.numObjs; i++) { + // Select old cell and remove from simulation. + PatchCell oldCell = (PatchCell) bag.get(i); + // Only remove and mutate if cell is healthy. + if (oldCell.getPop() == healthyPop) { + Location location = oldCell.getLocation(); + grid.removeObject(oldCell, oldCell.getLocation()); + oldCell.stop(); + + // Create new cancer cell and add to simulation. + PatchCellContainer cellContainer = + new PatchCellContainer( + oldCell.getID(), + oldCell.getParent(), + cancerPop, + oldCell.getAge(), + oldCell.getDivisions(), + oldCell.getState(), + oldCell.getVolume(), + oldCell.getHeight(), + oldCell.getCriticalVolume(), + oldCell.getCriticalHeight()); + PatchCell newCell = + (PatchCell) + cellContainer.convert(sim.cellFactory, location, sim.random); + grid.addObject(newCell, location); + newCell.schedule(sim.getSchedule()); + } + } + } + } +} diff --git a/src/arcade/patch/parameter.patch.xml b/src/arcade/patch/parameter.patch.xml index 40824712c..30a685727 100644 --- a/src/arcade/patch/parameter.patch.xml +++ b/src/arcade/patch/parameter.patch.xml @@ -125,6 +125,10 @@ + + + + diff --git a/src/arcade/patch/sim/PatchSimulationHex.java b/src/arcade/patch/sim/PatchSimulationHex.java index 6eddf28b5..fa63efbaf 100644 --- a/src/arcade/patch/sim/PatchSimulationHex.java +++ b/src/arcade/patch/sim/PatchSimulationHex.java @@ -6,6 +6,7 @@ import arcade.core.util.MiniBox; import arcade.patch.agent.action.PatchActionConvert; import arcade.patch.agent.action.PatchActionInsert; +import arcade.patch.agent.action.PatchActionMutate; import arcade.patch.agent.action.PatchActionRemove; import arcade.patch.agent.cell.PatchCellFactory; import arcade.patch.env.component.PatchComponentCycle; @@ -58,6 +59,8 @@ public Action makeAction(String actionClass, MiniBox parameters) { return new PatchActionRemove(series, parameters); case "convert": return new PatchActionConvert(series, parameters); + case "mutate": + return new PatchActionMutate(series, parameters); default: return null; } diff --git a/src/arcade/patch/sim/PatchSimulationRect.java b/src/arcade/patch/sim/PatchSimulationRect.java index 6623fefec..e6d7e0ea6 100644 --- a/src/arcade/patch/sim/PatchSimulationRect.java +++ b/src/arcade/patch/sim/PatchSimulationRect.java @@ -6,6 +6,7 @@ import arcade.core.util.MiniBox; import arcade.patch.agent.action.PatchActionConvert; import arcade.patch.agent.action.PatchActionInsert; +import arcade.patch.agent.action.PatchActionMutate; import arcade.patch.agent.action.PatchActionRemove; import arcade.patch.agent.cell.PatchCellFactory; import arcade.patch.env.component.PatchComponentCycle; @@ -58,6 +59,8 @@ public Action makeAction(String actionClass, MiniBox parameters) { return new PatchActionRemove(series, parameters); case "convert": return new PatchActionConvert(series, parameters); + case "mutate": + return new PatchActionMutate(series, parameters); default: return null; }