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:
+ *
+ *
+ * - {@code TIME_DELAY} = time delay before calling the action
+ *
- {@code MUTATE_RADIUS} = grid radius where cells are mutated
+ *
- {@code MUTATE_DEPTH} = grid depth that cells are mutated
+ *
+ *
+ * @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;
}