From 5e6d5eb909850ec5d3c2a5a01feb6cce44e82f61 Mon Sep 17 00:00:00 2001 From: allison-li-1016 Date: Mon, 21 Apr 2025 17:10:17 +0000 Subject: [PATCH 01/36] adding initial classes --- .../patch/agent/action/PatchActionTreat.java | 368 ++++++++++++++++++ .../agent/action/PatchActionTreatTest.java | 211 ++++++++++ 2 files changed, 579 insertions(+) create mode 100644 src/arcade/patch/agent/action/PatchActionTreat.java create mode 100644 test/arcade/patch/agent/action/PatchActionTreatTest.java diff --git a/src/arcade/patch/agent/action/PatchActionTreat.java b/src/arcade/patch/agent/action/PatchActionTreat.java new file mode 100644 index 000000000..071d1942a --- /dev/null +++ b/src/arcade/patch/agent/action/PatchActionTreat.java @@ -0,0 +1,368 @@ +package arcade.patch.agent.action; + +import java.util.ArrayList; +import java.util.Objects; +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.env.location.LocationContainer; +import arcade.core.sim.Series; +import arcade.core.sim.Simulation; +import arcade.core.util.Graph; +import arcade.core.util.MiniBox; +import arcade.core.util.Utilities; +import arcade.patch.agent.cell.PatchCell; +import arcade.patch.agent.cell.PatchCellCART; +import arcade.patch.agent.cell.PatchCellContainer; +import arcade.patch.agent.cell.PatchCellTissue; +import arcade.patch.env.component.PatchComponentSites; +import arcade.patch.env.component.PatchComponentSitesGraph; +import arcade.patch.env.component.PatchComponentSitesGraph.SiteEdge; +import arcade.patch.env.component.PatchComponentSitesGraphRect; +import arcade.patch.env.component.PatchComponentSitesGraphTri; +import arcade.patch.env.component.PatchComponentSitesPattern; +import arcade.patch.env.component.PatchComponentSitesSource; +import arcade.patch.env.grid.PatchGrid; +import arcade.patch.env.location.Coordinate; +import arcade.patch.env.location.CoordinateXYZ; +import arcade.patch.env.location.PatchLocation; +import arcade.patch.env.location.PatchLocationContainer; +import arcade.patch.sim.PatchSeries; +import arcade.patch.sim.PatchSimulation; +import arcade.patch.util.PatchEnums.Ordering; + +/** + * Implementation of {@link Action} for removing cell agents. + * + *

The action is stepped once after {@code TIME_DELAY}. The {@code TreatAction} will add CAR + * T-cell agents of specified dose and ratio next to source points or vasculature. + */ +public class PatchActionTreat implements Action { + /** Serialization version identifier */ + private static final long serialVersionUID = 0; + + /** Delay before calling the helper (in minutes) */ + private final int delay; + + /** Total number of CAR T-cells to treat with */ + private final int dose; + + /** List of fraction of each population to treat with. CD4 to CD8 ratio */ + private final double treatFrac; + + /** Maximum damage value at which T-cells can spawn next to in source or pattern source */ + private double max_damage; + + /** Minimum radius value at which T- cells can spawn next to in graph source */ + private final double min_damage_radius; + + /** Number of agent positions per lattice site */ + private int latPositions; + + /** Coordinate system used for simulation */ + private final String coord; + + /** List of populations. */ + private final ArrayList populations; + + /** parameters */ + MiniBox parameters; + + /** Maximum confluency of cells in any location */ + final int maxConfluency; + + /** location of available places to insert T cells. For testing purposes only */ + private ArrayList siteLocations; + + /** + * Creates an {@code Action} to add agents after a delay. + * + * @param series the simulation series + * @param parameters the component parameters dictionary + */ + public PatchActionTreat(Series series, MiniBox parameters) { + // Set loaded parameters. + this.delay = parameters.getInt("TIME_DELAY"); + this.dose = parameters.getInt("DOSE"); + this.treatFrac = parameters.getDouble("RATIO"); + this.max_damage = parameters.getDouble("MAX_DAMAGE_SEED"); + this.min_damage_radius = parameters.getDouble("MIN_RADIUS_SEED"); + this.maxConfluency = 54; + this.parameters = parameters; + + this.coord = + ((PatchSeries) series).patch.get("GEOMETRY").equalsIgnoreCase("HEX") + ? "Hex" + : "Rect"; + if (coord.equals("Hex")) { + latPositions = 9; + } + if (coord.equals("Rect")) { + latPositions = 16; + } + + // Initialize population register. + populations = new ArrayList<>(); + } + + @Override + public void schedule(Schedule schedule) { + schedule.scheduleOnce(delay, Ordering.ACTIONS.ordinal(), this); + } + + @Override + public void register(Simulation sim, String population) { + populations.add(sim.getSeries().populations.get(population)); + } + + /** + * Steps the helper to insert cells of the treatment population(s). + * + * @param simstate the MASON simulation state + */ + public void step(SimState simstate) { + + PatchSimulation sim = (PatchSimulation) simstate; + String type = "null"; + PatchGrid grid = (PatchGrid) sim.getGrid(); + PatchComponentSites comp = (PatchComponentSites) sim.getComponent("SITES"); + + ArrayList locs = sim.getLocations(); + + ArrayList siteLocs0 = new ArrayList(); + ArrayList siteLocs1 = new ArrayList(); + ArrayList siteLocs2 = new ArrayList(); + ArrayList siteLocs3 = new ArrayList(); + ArrayList siteLocs = new ArrayList(); + + // Determine type of sites component implemented. + if (comp instanceof PatchComponentSitesSource) { + type = "source"; + } else if (comp instanceof PatchComponentSitesPattern) { + type = "pattern"; + } else if (comp instanceof PatchComponentSitesGraph) { + type = "graph"; + } + + // Find sites without specified level of damage based on component type. + switch (type) { + case "source": + case "pattern": + double[][][] damage; + boolean[][][] sitesLat; + + if (type.equals("source")) { + damage = ((PatchComponentSitesSource) comp).getDamage(); + sitesLat = ((PatchComponentSitesSource) comp).getSources(); + } else { + damage = ((PatchComponentSitesPattern) comp).getDamage(); + sitesLat = ((PatchComponentSitesPattern) comp).getPatterns(); + } + + // Iterate through list of locations and remove locations not next to a site. + for (LocationContainer l : locs) { + PatchLocationContainer contain = (PatchLocationContainer) l; + PatchLocation loc = + (PatchLocation) + contain.convert( + sim.locationFactory, + sim.cellFactory.createCellForPopulation( + 0, populations.get(0).getInt("CODE"))); + CoordinateXYZ coord = (CoordinateXYZ) loc.getSubcoordinate(); + int z = coord.z; + if (sitesLat[z][coord.x][coord.y] + && damage[z][coord.x][coord.y] <= this.max_damage) { + addCellsIntoList(grid, loc, siteLocs0, siteLocs1, siteLocs2, siteLocs3); + } + } + break; + + case "graph": + Graph G = ((PatchComponentSitesGraph) comp).getGraph(); + Bag allEdges = new Bag(G.getAllEdges()); + PatchComponentSitesGraph graphSites = (PatchComponentSitesGraph) comp; + + for (Object edgeObj : allEdges) { + SiteEdge edge = (SiteEdge) edgeObj; + Bag allEdgeLocs = new Bag(); + if (Objects.equals(coord, "Hex")) { + allEdgeLocs.add( + ((PatchComponentSitesGraphTri) graphSites) + .getSpan(edge.getFrom(), edge.getTo())); + } else { + allEdgeLocs.add( + ((PatchComponentSitesGraphRect) graphSites) + .getSpan(edge.getFrom(), edge.getTo())); + } + + for (Object locObj : allEdgeLocs) { + Location loc = (Location) locObj; + if (locs.contains(loc)) { + if (edge.getRadius() >= min_damage_radius) { + addCellsIntoList( + grid, loc, siteLocs0, siteLocs1, siteLocs2, siteLocs3); + } + } + } + } + break; + } + + Utilities.shuffleList(siteLocs3, sim.random); + Utilities.shuffleList(siteLocs2, sim.random); + Utilities.shuffleList(siteLocs1, sim.random); + Utilities.shuffleList(siteLocs0, sim.random); + siteLocs.addAll(siteLocs3); + siteLocs.addAll(siteLocs2); + siteLocs.addAll(siteLocs1); + siteLocs.addAll(siteLocs0); + siteLocations = (ArrayList) siteLocs.clone(); + insert(siteLocs, simstate); + } + + private void addCellsIntoList( + PatchGrid grid, + Location loc, + ArrayList siteLocs0, + ArrayList siteLocs1, + ArrayList siteLocs2, + ArrayList siteLocs3) { + Bag bag = new Bag(grid.getObjectsAtLocation(loc)); + int numAgents = bag.numObjs; + + if (numAgents == 0) { + for (int p = 0; p < latPositions; p++) { + siteLocs0.add(loc); + } + } else if (numAgents == 1) { + for (int p = 0; p < latPositions; p++) { + siteLocs1.add(loc); + } + } else if (numAgents == 2) { + for (int p = 0; p < latPositions; p++) { + siteLocs2.add(loc); + } + } else { + for (int p = 0; p < latPositions; p++) { + siteLocs3.add(loc); + } + } + // Remove break statement if more than one per hex can appear + // with break statement, each location can only be added to list once + // without it, places with more vasc sites get added more times to list + // break; + } + + private void insert(ArrayList coordinates, SimState simstate) { + PatchSimulation sim = (PatchSimulation) simstate; + PatchGrid grid = (PatchGrid) sim.getGrid(); + Utilities.shuffleList(coordinates, sim.random); + + int cd4Code = 0; + int cd8Code = 0; + + for (MiniBox population : populations) { + String className = population.get("CLASS"); + if (className.equals("cart_cd4")) { + cd4Code = population.getInt("CODE"); + } + if (className.equals("cart_cd8")) { + cd8Code = population.getInt("CODE"); + } + } + + for (int i = 0; i < dose; i++) { + + int id = sim.getID(); + + int pop = cd4Code; + + if (sim.random.nextDouble() > treatFrac) { + pop = cd8Code; + } + + PatchLocation loc = ((PatchLocation) coordinates.remove(0)); + Coordinate coord = loc.getCoordinate(); + + // find available location space + while (!coordinates.isEmpty() && !checkLocationSpace(loc, grid)) { + loc = (PatchLocation) coordinates.remove(0); + } + + if (coordinates.isEmpty()) { + break; + } + + PatchLocationContainer locationContainer = new PatchLocationContainer(id, coord); + PatchCellContainer cellContainer = sim.cellFactory.createCellForPopulation(id, pop); + Location location = locationContainer.convert(sim.locationFactory, cellContainer); + PatchCell cell = + (PatchCell) cellContainer.convert(sim.cellFactory, location, sim.random); + grid.addObject(cell, location); + cell.schedule(sim.getSchedule()); + } + } + + protected boolean checkLocationSpace(Location loc, PatchGrid grid) { + boolean available; + int locMax = this.maxConfluency; + double locVolume = ((PatchLocation) loc).getVolume(); + double locArea = ((PatchLocation) loc).getArea(); + + Bag bag = new Bag(grid.getObjectsAtLocation(loc)); + int n = bag.numObjs; // number of agents in location + + if (n == 0) { + available = true; + } // no cells in location + else if (n >= locMax) { + available = false; + } // location already full + else { + available = true; + double totalVol = PatchCell.calculateTotalVolume(bag); + double currentHeight = totalVol / locArea; + + if (totalVol > locVolume) { + available = false; + } + + for (Object cellObj : bag) { + PatchCell cell = (PatchCell) cellObj; + if (cell instanceof PatchCellCART) { + totalVol = + PatchCell.calculateTotalVolume(bag) + + parameters.getDouble("T_CELL_VOL_AVG"); + currentHeight = totalVol / locArea; + } + if (cell instanceof PatchCellTissue) { + if (currentHeight > cell.getCriticalHeight()) { + available = false; + } + } + } + } + + return available; + } + + /** + * Returns registered populations for the action. Exists for testing purposes only + * + * @return registered populations for the action + */ + public ArrayList getPopulations() { + return populations; + } + + /** + * Returns locations of sites available for insertion. Exists for testing purposes only + * + * @return Returns locations of sites available for insertion + */ + public ArrayList getSiteLocs() { + return siteLocations; + } +} diff --git a/test/arcade/patch/agent/action/PatchActionTreatTest.java b/test/arcade/patch/agent/action/PatchActionTreatTest.java new file mode 100644 index 000000000..2f23f9615 --- /dev/null +++ b/test/arcade/patch/agent/action/PatchActionTreatTest.java @@ -0,0 +1,211 @@ +package arcade.patch.agent.action; + +import java.lang.reflect.Field; +import java.util.ArrayList; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import sim.engine.Schedule; +import sim.util.Bag; +import ec.util.MersenneTwisterFast; +import arcade.core.env.location.LocationContainer; +import arcade.core.util.MiniBox; +import arcade.patch.agent.cell.PatchCell; +import arcade.patch.agent.cell.PatchCellContainer; +import arcade.patch.agent.cell.PatchCellFactory; +import arcade.patch.env.component.PatchComponentSitesSource; +import arcade.patch.env.grid.PatchGrid; +import arcade.patch.env.location.*; +import arcade.patch.sim.PatchSeries; +import arcade.patch.sim.PatchSimulation; +import arcade.patch.util.PatchEnums; +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.Mockito.*; + +public class PatchActionTreatTest { + + private PatchActionTreat action; + private PatchSimulation sim; + private PatchGrid gridMock; + private PatchCell cellMock; + MiniBox parameters; + PatchSeries series; + + @BeforeEach + public void setUp() throws NoSuchFieldException, IllegalAccessException { + parameters = mock(MiniBox.class); + when(parameters.getInt("TIME_DELAY")).thenReturn(10); + when(parameters.getInt("DOSE")).thenReturn(10); + when(parameters.getDouble("RATIO")).thenReturn(0.5); + when(parameters.getDouble("MAX_DAMAGE_SEED")).thenReturn(0.1); + when(parameters.getDouble("MIN_RADIUS_SEED")).thenReturn(0.01); + when(parameters.getDouble("T_CELL_VOL_AVG")).thenReturn(175.0); + series = mock(PatchSeries.class); + MiniBox patchMock = mock(MiniBox.class); + series.patch = patchMock; + gridMock = mock(PatchGrid.class); + Schedule mockSchedule = mock(Schedule.class); + when(patchMock.get("GEOMETRY")).thenReturn("HEX"); + sim = mock(PatchSimulation.class); + when(sim.getSeries()).thenReturn(series); + when(sim.getGrid()).thenReturn(gridMock); + when(sim.getSchedule()).thenReturn(mockSchedule); + PatchComponentSitesSource sources = mock(PatchComponentSitesSource.class); + double[][][] damage = new double[20][20][20]; + boolean[][][] sitesLat = new boolean[20][20][20]; + damage[0][0][0] = 0.05; + sitesLat[0][0][0] = true; + when(sources.getDamage()).thenReturn(damage); + when(sources.getSources()).thenReturn(sitesLat); + when(sim.getComponent("SITES")).thenReturn(sources); + ArrayList locations = new ArrayList<>(); + PatchLocationContainer container = mock(PatchLocationContainer.class); + PatchLocation loc = mock(PatchLocation.class); + CoordinateXYZ coord = mock(CoordinateXYZ.class); + Field x = CoordinateXYZ.class.getDeclaredField("x"); + x.setAccessible(true); + x.set(coord, 0); + Field y = CoordinateXYZ.class.getDeclaredField("y"); + y.setAccessible(true); + y.set(coord, 0); + Field z = Coordinate.class.getDeclaredField("z"); + z.setAccessible(true); + z.set(coord, 0); + CoordinateUVWZ c = mock(CoordinateUVWZ.class); + z.set(c, 0); + when(loc.getSubcoordinate()).thenReturn(coord); + when(loc.getCoordinate()).thenReturn(c); + when(container.convert(any(), any())).thenReturn(loc); + locations.add(container); + when(sim.getLocations()).thenReturn(locations); + sim.random = mock(MersenneTwisterFast.class); + + PatchCellFactory factoryMock = mock(PatchCellFactory.class); + PatchCellContainer patchCellContainerMock = mock(PatchCellContainer.class); + when(factoryMock.createCellForPopulation(any(Integer.class), any(Integer.class))) + .thenReturn(patchCellContainerMock); + cellMock = mock(PatchCell.class); + when(patchCellContainerMock.convert(any(), any(), any())).thenReturn(cellMock); + + Field factory = PatchSimulation.class.getDeclaredField("cellFactory"); + factory.setAccessible(true); + factory.set(sim, factoryMock); + } + + final Bag createPatchCellsWithVolumeAndCriticalHeight(int n, double volume, double critHeight) { + Bag bag = new Bag(); + for (int i = 0; i < n; i++) { + PatchCell cell = mock(PatchCell.class); + when(cell.getVolume()).thenReturn(volume); + when(cell.getHeight()).thenReturn(critHeight); + bag.add(cell); + } + return bag; + } + + @Test + public void schedule_callsScheduleOnAction() + throws NoSuchFieldException, IllegalAccessException { + action = new PatchActionTreat(series, parameters); + + ArrayList populations = new ArrayList<>(); + MiniBox populationMock = mock(MiniBox.class); + when(populationMock.getInt("CODE")).thenReturn(4); + when(populationMock.get("CLASS")).thenReturn("cart_cd4"); + populations.add(populationMock); + Field pops = PatchActionTreat.class.getDeclaredField("populations"); + pops.setAccessible(true); + pops.set(action, populations); + + Schedule schedule = mock(Schedule.class); + action.schedule(schedule); + verify(schedule) + .scheduleOnce(anyDouble(), eq(PatchEnums.Ordering.ACTIONS.ordinal()), eq(action)); + } + + @Test + public void step_addsObjectsToSim() throws NoSuchFieldException, IllegalAccessException { + action = new PatchActionTreat(series, parameters); + + ArrayList populations = new ArrayList<>(); + MiniBox populationMock = mock(MiniBox.class); + when(populationMock.getInt("CODE")).thenReturn(4); + when(populationMock.get("CLASS")).thenReturn("cart_cd4"); + populations.add(populationMock); + Field pops = PatchActionTreat.class.getDeclaredField("populations"); + pops.setAccessible(true); + pops.set(action, populations); + + action.step(sim); + assertFalse(action.getSiteLocs().isEmpty()); + verify(gridMock, times(action.getSiteLocs().size() - 1)).addObject(any(), any()); + verify(cellMock, times(action.getSiteLocs().size() - 1)).schedule(any()); + } + + @Test + public void step_zeroDose_doesNotAddObjects() + throws NoSuchFieldException, IllegalAccessException { + when(parameters.getInt("DOSE")).thenReturn(0); + action = new PatchActionTreat(series, parameters); + + ArrayList populations = new ArrayList<>(); + MiniBox populationMock = mock(MiniBox.class); + when(populationMock.getInt("CODE")).thenReturn(4); + when(populationMock.get("CLASS")).thenReturn("cart_cd4"); + populations.add(populationMock); + Field pops = PatchActionTreat.class.getDeclaredField("populations"); + pops.setAccessible(true); + pops.set(action, populations); + + action.step(sim); + verify(gridMock, times(0)).addObject(any(), any()); + verify(cellMock, times(0)).schedule(any()); + } + + @Test + public void checkLocationSpace_withEmptySpaces_returnsAvailable() + throws NoSuchFieldException, IllegalAccessException { + action = new PatchActionTreat(series, parameters); + ArrayList populations = new ArrayList<>(); + MiniBox populationMock = mock(MiniBox.class); + when(populationMock.getInt("CODE")).thenReturn(4); + when(populationMock.get("CLASS")).thenReturn("cart_cd4"); + populations.add(populationMock); + Field pops = PatchActionTreat.class.getDeclaredField("populations"); + pops.setAccessible(true); + pops.set(action, populations); + + PatchLocation locationMock = mock(PatchLocation.class); + when(locationMock.getArea()).thenReturn(3.0 / 2.0 / Math.sqrt(3.0) * 30 * 30); + when(locationMock.getVolume()).thenReturn(3.0 / 2.0 / Math.sqrt(3.0) * 30 * 30 * 8.7); + Bag testBag = createPatchCellsWithVolumeAndCriticalHeight(2, 10, 12.5); + when(gridMock.getObjectsAtLocation(locationMock)).thenReturn(testBag); + boolean result = action.checkLocationSpace(locationMock, gridMock); + assertTrue(result); + } + + @Test + public void checkLocation_maxConfluency_returnsUnavailable() + throws NoSuchFieldException, IllegalAccessException { + action = new PatchActionTreat(series, parameters); + Field density = PatchActionTreat.class.getDeclaredField("maxConfluency"); + density.setAccessible(true); + density.set(action, 1); + + ArrayList populations = new ArrayList<>(); + MiniBox populationMock = mock(MiniBox.class); + when(populationMock.getInt("CODE")).thenReturn(4); + when(populationMock.get("CLASS")).thenReturn("cart_cd4"); + populations.add(populationMock); + Field pops = PatchActionTreat.class.getDeclaredField("populations"); + pops.setAccessible(true); + pops.set(action, populations); + + PatchLocation locationMock = mock(PatchLocation.class); + when(locationMock.getArea()).thenReturn(3.0 / 2.0 / Math.sqrt(3.0) * 30 * 30); + when(locationMock.getVolume()).thenReturn(3.0 / 2.0 / Math.sqrt(3.0) * 30 * 30 * 8.7); + Bag testBag = createPatchCellsWithVolumeAndCriticalHeight(2, 10, 12.5); + when(gridMock.getObjectsAtLocation(locationMock)).thenReturn(testBag); + boolean result = action.checkLocationSpace(locationMock, gridMock); + assertFalse(result); + } +} From 99fabb53a8366445128d49541062d23b5e7fbf72 Mon Sep 17 00:00:00 2001 From: allison-li-1016 Date: Mon, 21 Apr 2025 17:11:35 +0000 Subject: [PATCH 02/36] adding treat action parameters --- src/arcade/patch/parameter.patch.xml | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/arcade/patch/parameter.patch.xml b/src/arcade/patch/parameter.patch.xml index f2263676d..4a7e2c574 100644 --- a/src/arcade/patch/parameter.patch.xml +++ b/src/arcade/patch/parameter.patch.xml @@ -120,6 +120,13 @@ + + + + + + + From 0d1c6b6afba61f27e5d216603c57072ead18a9d8 Mon Sep 17 00:00:00 2001 From: allison-li-1016 Date: Tue, 22 Apr 2025 05:02:46 +0000 Subject: [PATCH 03/36] linting, removing unnecessary param --- .../patch/agent/action/PatchActionTreat.java | 33 ++++++------------- .../agent/action/PatchActionTreatTest.java | 11 +++++-- 2 files changed, 18 insertions(+), 26 deletions(-) diff --git a/src/arcade/patch/agent/action/PatchActionTreat.java b/src/arcade/patch/agent/action/PatchActionTreat.java index 071d1942a..3271cdc70 100644 --- a/src/arcade/patch/agent/action/PatchActionTreat.java +++ b/src/arcade/patch/agent/action/PatchActionTreat.java @@ -40,42 +40,39 @@ * T-cell agents of specified dose and ratio next to source points or vasculature. */ public class PatchActionTreat implements Action { - /** Serialization version identifier */ + /** Serialization version identifier. */ private static final long serialVersionUID = 0; - /** Delay before calling the helper (in minutes) */ + /** Delay before calling the helper (in minutes). */ private final int delay; - /** Total number of CAR T-cells to treat with */ + /** Total number of CAR T-cells to treat with. */ private final int dose; - /** List of fraction of each population to treat with. CD4 to CD8 ratio */ + /** List of fraction of each population to treat with. CD4 to CD8 ratio. */ private final double treatFrac; - /** Maximum damage value at which T-cells can spawn next to in source or pattern source */ + /** Maximum damage value at which T-cells can spawn next to in source or pattern source. */ private double max_damage; - /** Minimum radius value at which T- cells can spawn next to in graph source */ + /** Minimum radius value at which T- cells can spawn next to in graph source. */ private final double min_damage_radius; - /** Number of agent positions per lattice site */ + /** Number of agent positions per lattice site. */ private int latPositions; - /** Coordinate system used for simulation */ + /** Coordinate system used for simulation. */ private final String coord; /** List of populations. */ private final ArrayList populations; - /** parameters */ + /** parameters. */ MiniBox parameters; - /** Maximum confluency of cells in any location */ + /** Maximum confluency of cells in any location. */ final int maxConfluency; - /** location of available places to insert T cells. For testing purposes only */ - private ArrayList siteLocations; - /** * Creates an {@code Action} to add agents after a delay. * @@ -218,7 +215,6 @@ public void step(SimState simstate) { siteLocs.addAll(siteLocs2); siteLocs.addAll(siteLocs1); siteLocs.addAll(siteLocs0); - siteLocations = (ArrayList) siteLocs.clone(); insert(siteLocs, simstate); } @@ -356,13 +352,4 @@ else if (n >= locMax) { public ArrayList getPopulations() { return populations; } - - /** - * Returns locations of sites available for insertion. Exists for testing purposes only - * - * @return Returns locations of sites available for insertion - */ - public ArrayList getSiteLocs() { - return siteLocations; - } } diff --git a/test/arcade/patch/agent/action/PatchActionTreatTest.java b/test/arcade/patch/agent/action/PatchActionTreatTest.java index 2f23f9615..f2156b7ec 100644 --- a/test/arcade/patch/agent/action/PatchActionTreatTest.java +++ b/test/arcade/patch/agent/action/PatchActionTreatTest.java @@ -24,10 +24,15 @@ public class PatchActionTreatTest { private PatchActionTreat action; + private PatchSimulation sim; + private PatchGrid gridMock; + private PatchCell cellMock; + MiniBox parameters; + PatchSeries series; @BeforeEach @@ -136,9 +141,9 @@ public void step_addsObjectsToSim() throws NoSuchFieldException, IllegalAccessEx pops.set(action, populations); action.step(sim); - assertFalse(action.getSiteLocs().isEmpty()); - verify(gridMock, times(action.getSiteLocs().size() - 1)).addObject(any(), any()); - verify(cellMock, times(action.getSiteLocs().size() - 1)).schedule(any()); + + verify(gridMock, atLeastOnce()).addObject(any(), any()); + verify(cellMock, atLeastOnce()).schedule(any()); } @Test From 3bb47863bc898520967737a3d633cbdd1ea2d366 Mon Sep 17 00:00:00 2001 From: allison-li-1016 Date: Tue, 22 Apr 2025 05:15:00 +0000 Subject: [PATCH 04/36] renaming silly var names --- .../patch/agent/action/PatchActionTreat.java | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/arcade/patch/agent/action/PatchActionTreat.java b/src/arcade/patch/agent/action/PatchActionTreat.java index 3271cdc70..2e6629203 100644 --- a/src/arcade/patch/agent/action/PatchActionTreat.java +++ b/src/arcade/patch/agent/action/PatchActionTreat.java @@ -53,10 +53,10 @@ public class PatchActionTreat implements Action { private final double treatFrac; /** Maximum damage value at which T-cells can spawn next to in source or pattern source. */ - private double max_damage; + private double maxDamage; /** Minimum radius value at which T- cells can spawn next to in graph source. */ - private final double min_damage_radius; + private final double minDamageRadius; /** Number of agent positions per lattice site. */ private int latPositions; @@ -84,8 +84,8 @@ public PatchActionTreat(Series series, MiniBox parameters) { this.delay = parameters.getInt("TIME_DELAY"); this.dose = parameters.getInt("DOSE"); this.treatFrac = parameters.getDouble("RATIO"); - this.max_damage = parameters.getDouble("MAX_DAMAGE_SEED"); - this.min_damage_radius = parameters.getDouble("MIN_RADIUS_SEED"); + this.maxDamage = parameters.getDouble("MAX_DAMAGE_SEED"); + this.minDamageRadius = parameters.getDouble("MIN_RADIUS_SEED"); this.maxConfluency = 54; this.parameters = parameters; @@ -170,15 +170,15 @@ public void step(SimState simstate) { CoordinateXYZ coord = (CoordinateXYZ) loc.getSubcoordinate(); int z = coord.z; if (sitesLat[z][coord.x][coord.y] - && damage[z][coord.x][coord.y] <= this.max_damage) { + && damage[z][coord.x][coord.y] <= this.maxDamage) { addCellsIntoList(grid, loc, siteLocs0, siteLocs1, siteLocs2, siteLocs3); } } break; case "graph": - Graph G = ((PatchComponentSitesGraph) comp).getGraph(); - Bag allEdges = new Bag(G.getAllEdges()); + Graph graph = ((PatchComponentSitesGraph) comp).getGraph(); + Bag allEdges = new Bag(graph.getAllEdges()); PatchComponentSitesGraph graphSites = (PatchComponentSitesGraph) comp; for (Object edgeObj : allEdges) { @@ -197,7 +197,7 @@ public void step(SimState simstate) { for (Object locObj : allEdgeLocs) { Location loc = (Location) locObj; if (locs.contains(loc)) { - if (edge.getRadius() >= min_damage_radius) { + if (edge.getRadius() >= minDamageRadius) { addCellsIntoList( grid, loc, siteLocs0, siteLocs1, siteLocs2, siteLocs3); } From f354383bcb326a3a5ad3a480ddece0c4623c5ec9 Mon Sep 17 00:00:00 2001 From: allison-li-1016 Date: Tue, 22 Apr 2025 20:27:08 +0000 Subject: [PATCH 05/36] adding option for default --- src/arcade/patch/agent/action/PatchActionTreat.java | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/arcade/patch/agent/action/PatchActionTreat.java b/src/arcade/patch/agent/action/PatchActionTreat.java index 2e6629203..4bf792036 100644 --- a/src/arcade/patch/agent/action/PatchActionTreat.java +++ b/src/arcade/patch/agent/action/PatchActionTreat.java @@ -205,6 +205,11 @@ public void step(SimState simstate) { } } break; + default: + throw new IllegalArgumentException( + "Invalid component type: " + + type + + ". Must be of type source, pattern, or graph."); } Utilities.shuffleList(siteLocs3, sim.random); From 9bf892023c4a5fa4355b819f9f91f5e86c666c03 Mon Sep 17 00:00:00 2001 From: allison-li-1016 Date: Tue, 22 Apr 2025 20:32:09 +0000 Subject: [PATCH 06/36] renaming variable so it does not literally match the field name --- src/arcade/patch/agent/action/PatchActionTreat.java | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/arcade/patch/agent/action/PatchActionTreat.java b/src/arcade/patch/agent/action/PatchActionTreat.java index 4bf792036..2ec6567dc 100644 --- a/src/arcade/patch/agent/action/PatchActionTreat.java +++ b/src/arcade/patch/agent/action/PatchActionTreat.java @@ -167,10 +167,10 @@ public void step(SimState simstate) { sim.locationFactory, sim.cellFactory.createCellForPopulation( 0, populations.get(0).getInt("CODE"))); - CoordinateXYZ coord = (CoordinateXYZ) loc.getSubcoordinate(); - int z = coord.z; - if (sitesLat[z][coord.x][coord.y] - && damage[z][coord.x][coord.y] <= this.maxDamage) { + CoordinateXYZ coordinate = (CoordinateXYZ) loc.getSubcoordinate(); + int z = coordinate.z; + if (sitesLat[z][coordinate.x][coordinate.y] + && damage[z][coordinate.x][coordinate.y] <= this.maxDamage) { addCellsIntoList(grid, loc, siteLocs0, siteLocs1, siteLocs2, siteLocs3); } } @@ -285,7 +285,7 @@ private void insert(ArrayList coordinates, SimState simstate) { } PatchLocation loc = ((PatchLocation) coordinates.remove(0)); - Coordinate coord = loc.getCoordinate(); + Coordinate coordinate = loc.getCoordinate(); // find available location space while (!coordinates.isEmpty() && !checkLocationSpace(loc, grid)) { @@ -296,7 +296,7 @@ private void insert(ArrayList coordinates, SimState simstate) { break; } - PatchLocationContainer locationContainer = new PatchLocationContainer(id, coord); + PatchLocationContainer locationContainer = new PatchLocationContainer(id, coordinate); PatchCellContainer cellContainer = sim.cellFactory.createCellForPopulation(id, pop); Location location = locationContainer.convert(sim.locationFactory, cellContainer); PatchCell cell = From 92c6151dfa67a93618d52af8e6f1ee8af5e34a5f Mon Sep 17 00:00:00 2001 From: allison-li-1016 Date: Tue, 22 Apr 2025 20:50:37 +0000 Subject: [PATCH 07/36] adding javadoc comments --- .../patch/agent/action/PatchActionTreat.java | 38 +++++++++++++++---- 1 file changed, 30 insertions(+), 8 deletions(-) diff --git a/src/arcade/patch/agent/action/PatchActionTreat.java b/src/arcade/patch/agent/action/PatchActionTreat.java index 2ec6567dc..815198e47 100644 --- a/src/arcade/patch/agent/action/PatchActionTreat.java +++ b/src/arcade/patch/agent/action/PatchActionTreat.java @@ -40,8 +40,6 @@ * T-cell agents of specified dose and ratio next to source points or vasculature. */ public class PatchActionTreat implements Action { - /** Serialization version identifier. */ - private static final long serialVersionUID = 0; /** Delay before calling the helper (in minutes). */ private final int delay; @@ -80,7 +78,6 @@ public class PatchActionTreat implements Action { * @param parameters the component parameters dictionary */ public PatchActionTreat(Series series, MiniBox parameters) { - // Set loaded parameters. this.delay = parameters.getInt("TIME_DELAY"); this.dose = parameters.getInt("DOSE"); this.treatFrac = parameters.getDouble("RATIO"); @@ -100,7 +97,6 @@ public PatchActionTreat(Series series, MiniBox parameters) { latPositions = 16; } - // Initialize population register. populations = new ArrayList<>(); } @@ -115,7 +111,7 @@ public void register(Simulation sim, String population) { } /** - * Steps the helper to insert cells of the treatment population(s). + * Steps the action to insert cells of the treatment population(s). * * @param simstate the MASON simulation state */ @@ -146,6 +142,8 @@ public void step(SimState simstate) { // Find sites without specified level of damage based on component type. switch (type) { case "source": + break; + case "pattern": double[][][] damage; boolean[][][] sitesLat; @@ -171,7 +169,7 @@ public void step(SimState simstate) { int z = coordinate.z; if (sitesLat[z][coordinate.x][coordinate.y] && damage[z][coordinate.x][coordinate.y] <= this.maxDamage) { - addCellsIntoList(grid, loc, siteLocs0, siteLocs1, siteLocs2, siteLocs3); + addLocationsIntoList(grid, loc, siteLocs0, siteLocs1, siteLocs2, siteLocs3); } } break; @@ -198,13 +196,14 @@ public void step(SimState simstate) { Location loc = (Location) locObj; if (locs.contains(loc)) { if (edge.getRadius() >= minDamageRadius) { - addCellsIntoList( + addLocationsIntoList( grid, loc, siteLocs0, siteLocs1, siteLocs2, siteLocs3); } } } } break; + default: throw new IllegalArgumentException( "Invalid component type: " @@ -223,7 +222,17 @@ public void step(SimState simstate) { insert(siteLocs, simstate); } - private void addCellsIntoList( + /** + * Helper method to sort locations into lists. + * + * @param grid the simulation grid + * @param loc the current location being looked at + * @param siteLocs0 the list of locations with 0 agents + * @param siteLocs1 the list of locations with 1 agent + * @param siteLocs2 the list of locations with 2 agents + * @param siteLocs3 the list of locations with 3 agents + */ + private void addLocationsIntoList( PatchGrid grid, Location loc, ArrayList siteLocs0, @@ -256,6 +265,12 @@ private void addCellsIntoList( // break; } + /** + * Helper method to add cells into the grid. + * + * @param coordinates the locations to insert the cells + * @param simstate the simulation state + */ private void insert(ArrayList coordinates, SimState simstate) { PatchSimulation sim = (PatchSimulation) simstate; PatchGrid grid = (PatchGrid) sim.getGrid(); @@ -306,6 +321,13 @@ private void insert(ArrayList coordinates, SimState simstate) { } } + /** + * Helper method to check if location is available. + * + * @param grid the simulation grid + * @param loc the current location being looked at + * @return boolean indicating if location is free + */ protected boolean checkLocationSpace(Location loc, PatchGrid grid) { boolean available; int locMax = this.maxConfluency; From b5e75ec7a7ec3c87348cca0b10206562e85bd919 Mon Sep 17 00:00:00 2001 From: allison-li-1016 Date: Tue, 22 Apr 2025 20:57:23 +0000 Subject: [PATCH 08/36] editing javadoc of the class to be for Treat rather than remove --- src/arcade/patch/agent/action/PatchActionTreat.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/arcade/patch/agent/action/PatchActionTreat.java b/src/arcade/patch/agent/action/PatchActionTreat.java index 815198e47..b5436e580 100644 --- a/src/arcade/patch/agent/action/PatchActionTreat.java +++ b/src/arcade/patch/agent/action/PatchActionTreat.java @@ -34,7 +34,7 @@ import arcade.patch.util.PatchEnums.Ordering; /** - * Implementation of {@link Action} for removing cell agents. + * Implementation of {@link Action} for inserting T cell agents. * *

The action is stepped once after {@code TIME_DELAY}. The {@code TreatAction} will add CAR * T-cell agents of specified dose and ratio next to source points or vasculature. From e72f7876ca74340b99b691e981e892c0dfa94b94 Mon Sep 17 00:00:00 2001 From: allison-li-1016 Date: Tue, 22 Apr 2025 21:10:46 +0000 Subject: [PATCH 09/36] I think this was causing issues? --- src/arcade/patch/agent/action/PatchActionTreat.java | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/arcade/patch/agent/action/PatchActionTreat.java b/src/arcade/patch/agent/action/PatchActionTreat.java index b5436e580..97273aaa0 100644 --- a/src/arcade/patch/agent/action/PatchActionTreat.java +++ b/src/arcade/patch/agent/action/PatchActionTreat.java @@ -142,8 +142,6 @@ public void step(SimState simstate) { // Find sites without specified level of damage based on component type. switch (type) { case "source": - break; - case "pattern": double[][][] damage; boolean[][][] sitesLat; From 37357f25c1275cf2324e67e13722076f42e66df3 Mon Sep 17 00:00:00 2001 From: allison-li-1016 Date: Tue, 22 Apr 2025 21:14:08 +0000 Subject: [PATCH 10/36] if/else statement silliness --- src/arcade/patch/agent/action/PatchActionTreat.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/arcade/patch/agent/action/PatchActionTreat.java b/src/arcade/patch/agent/action/PatchActionTreat.java index 97273aaa0..6fd16df06 100644 --- a/src/arcade/patch/agent/action/PatchActionTreat.java +++ b/src/arcade/patch/agent/action/PatchActionTreat.java @@ -336,12 +336,12 @@ protected boolean checkLocationSpace(Location loc, PatchGrid grid) { int n = bag.numObjs; // number of agents in location if (n == 0) { + // no cells in location available = true; - } // no cells in location - else if (n >= locMax) { + } else if (n >= locMax) { + // location already full available = false; - } // location already full - else { + } else { available = true; double totalVol = PatchCell.calculateTotalVolume(bag); double currentHeight = totalVol / locArea; From 34a1d64972fb0c8c31fd8899baa9be3926013ea7 Mon Sep 17 00:00:00 2001 From: allison-li-1016 Date: Tue, 22 Apr 2025 21:19:28 +0000 Subject: [PATCH 11/36] addressing linter comments in test class --- test/arcade/patch/agent/action/PatchActionTreatTest.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/test/arcade/patch/agent/action/PatchActionTreatTest.java b/test/arcade/patch/agent/action/PatchActionTreatTest.java index f2156b7ec..d39e26a9f 100644 --- a/test/arcade/patch/agent/action/PatchActionTreatTest.java +++ b/test/arcade/patch/agent/action/PatchActionTreatTest.java @@ -36,7 +36,7 @@ public class PatchActionTreatTest { PatchSeries series; @BeforeEach - public void setUp() throws NoSuchFieldException, IllegalAccessException { + public final void setUp() throws NoSuchFieldException, IllegalAccessException { parameters = mock(MiniBox.class); when(parameters.getInt("TIME_DELAY")).thenReturn(10); when(parameters.getInt("DOSE")).thenReturn(10); @@ -108,7 +108,7 @@ final Bag createPatchCellsWithVolumeAndCriticalHeight(int n, double volume, doub } @Test - public void schedule_callsScheduleOnAction() + public void schedule_called_schedulesOnAction() throws NoSuchFieldException, IllegalAccessException { action = new PatchActionTreat(series, parameters); @@ -128,7 +128,7 @@ public void schedule_callsScheduleOnAction() } @Test - public void step_addsObjectsToSim() throws NoSuchFieldException, IllegalAccessException { + public void step_called_addsObjectsToSim() throws NoSuchFieldException, IllegalAccessException { action = new PatchActionTreat(series, parameters); ArrayList populations = new ArrayList<>(); From 3404f20470a8f56002ba3ae20bf1f2d6f9b6915f Mon Sep 17 00:00:00 2001 From: allison-li-1016 Date: Tue, 22 Apr 2025 21:23:01 +0000 Subject: [PATCH 12/36] BRUH the indvidiual import statements --- test/arcade/patch/agent/action/PatchActionTreatTest.java | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/test/arcade/patch/agent/action/PatchActionTreatTest.java b/test/arcade/patch/agent/action/PatchActionTreatTest.java index d39e26a9f..65d3c2f68 100644 --- a/test/arcade/patch/agent/action/PatchActionTreatTest.java +++ b/test/arcade/patch/agent/action/PatchActionTreatTest.java @@ -14,7 +14,11 @@ import arcade.patch.agent.cell.PatchCellFactory; import arcade.patch.env.component.PatchComponentSitesSource; import arcade.patch.env.grid.PatchGrid; -import arcade.patch.env.location.*; +import arcade.patch.env.location.Coordinate; +import arcade.patch.env.location.CoordinateUVWZ; +import arcade.patch.env.location.CoordinateXYZ; +import arcade.patch.env.location.PatchLocation; +import arcade.patch.env.location.PatchLocationContainer; import arcade.patch.sim.PatchSeries; import arcade.patch.sim.PatchSimulation; import arcade.patch.util.PatchEnums; From da46f7e5a9f9f4266bb7c918008822ed3f92cce3 Mon Sep 17 00:00:00 2001 From: allison-li-1016 Date: Wed, 23 Apr 2025 17:15:36 +0000 Subject: [PATCH 13/36] removing frivolous method --- src/arcade/patch/agent/action/PatchActionTreat.java | 9 --------- 1 file changed, 9 deletions(-) diff --git a/src/arcade/patch/agent/action/PatchActionTreat.java b/src/arcade/patch/agent/action/PatchActionTreat.java index 6fd16df06..9fe31b223 100644 --- a/src/arcade/patch/agent/action/PatchActionTreat.java +++ b/src/arcade/patch/agent/action/PatchActionTreat.java @@ -368,13 +368,4 @@ protected boolean checkLocationSpace(Location loc, PatchGrid grid) { return available; } - - /** - * Returns registered populations for the action. Exists for testing purposes only - * - * @return registered populations for the action - */ - public ArrayList getPopulations() { - return populations; - } } From f80d49d3f56a3e3aa18dd9d1ba6ba46ea3e54d03 Mon Sep 17 00:00:00 2001 From: allison-li-1016 Date: Wed, 23 Apr 2025 17:19:43 +0000 Subject: [PATCH 14/36] removing frivolous comment --- src/arcade/patch/agent/action/PatchActionTreat.java | 1 - 1 file changed, 1 deletion(-) diff --git a/src/arcade/patch/agent/action/PatchActionTreat.java b/src/arcade/patch/agent/action/PatchActionTreat.java index 9fe31b223..ea0dfc03e 100644 --- a/src/arcade/patch/agent/action/PatchActionTreat.java +++ b/src/arcade/patch/agent/action/PatchActionTreat.java @@ -300,7 +300,6 @@ private void insert(ArrayList coordinates, SimState simstate) { PatchLocation loc = ((PatchLocation) coordinates.remove(0)); Coordinate coordinate = loc.getCoordinate(); - // find available location space while (!coordinates.isEmpty() && !checkLocationSpace(loc, grid)) { loc = (PatchLocation) coordinates.remove(0); } From b8caa6091f9f4a1c4f1f95db91e856427217c966 Mon Sep 17 00:00:00 2001 From: allison-li-1016 Date: Tue, 3 Jun 2025 21:12:20 +0000 Subject: [PATCH 15/36] removing switch statement to if/else --- .../patch/agent/action/PatchActionTreat.java | 114 +++++++++--------- 1 file changed, 54 insertions(+), 60 deletions(-) diff --git a/src/arcade/patch/agent/action/PatchActionTreat.java b/src/arcade/patch/agent/action/PatchActionTreat.java index ea0dfc03e..4f65f0657 100644 --- a/src/arcade/patch/agent/action/PatchActionTreat.java +++ b/src/arcade/patch/agent/action/PatchActionTreat.java @@ -140,73 +140,67 @@ public void step(SimState simstate) { } // Find sites without specified level of damage based on component type. - switch (type) { - case "source": - case "pattern": - double[][][] damage; - boolean[][][] sitesLat; - - if (type.equals("source")) { - damage = ((PatchComponentSitesSource) comp).getDamage(); - sitesLat = ((PatchComponentSitesSource) comp).getSources(); + if (type.equals("graph")) { + Graph graph = ((PatchComponentSitesGraph) comp).getGraph(); + Bag allEdges = new Bag(graph.getAllEdges()); + PatchComponentSitesGraph graphSites = (PatchComponentSitesGraph) comp; + + for (Object edgeObj : allEdges) { + SiteEdge edge = (SiteEdge) edgeObj; + Bag allEdgeLocs = new Bag(); + if (Objects.equals(coord, "Hex")) { + allEdgeLocs.add( + ((PatchComponentSitesGraphTri) graphSites) + .getSpan(edge.getFrom(), edge.getTo())); } else { - damage = ((PatchComponentSitesPattern) comp).getDamage(); - sitesLat = ((PatchComponentSitesPattern) comp).getPatterns(); + allEdgeLocs.add( + ((PatchComponentSitesGraphRect) graphSites) + .getSpan(edge.getFrom(), edge.getTo())); } - // Iterate through list of locations and remove locations not next to a site. - for (LocationContainer l : locs) { - PatchLocationContainer contain = (PatchLocationContainer) l; - PatchLocation loc = - (PatchLocation) - contain.convert( - sim.locationFactory, - sim.cellFactory.createCellForPopulation( - 0, populations.get(0).getInt("CODE"))); - CoordinateXYZ coordinate = (CoordinateXYZ) loc.getSubcoordinate(); - int z = coordinate.z; - if (sitesLat[z][coordinate.x][coordinate.y] - && damage[z][coordinate.x][coordinate.y] <= this.maxDamage) { - addLocationsIntoList(grid, loc, siteLocs0, siteLocs1, siteLocs2, siteLocs3); - } - } - break; - - case "graph": - Graph graph = ((PatchComponentSitesGraph) comp).getGraph(); - Bag allEdges = new Bag(graph.getAllEdges()); - PatchComponentSitesGraph graphSites = (PatchComponentSitesGraph) comp; - - for (Object edgeObj : allEdges) { - SiteEdge edge = (SiteEdge) edgeObj; - Bag allEdgeLocs = new Bag(); - if (Objects.equals(coord, "Hex")) { - allEdgeLocs.add( - ((PatchComponentSitesGraphTri) graphSites) - .getSpan(edge.getFrom(), edge.getTo())); - } else { - allEdgeLocs.add( - ((PatchComponentSitesGraphRect) graphSites) - .getSpan(edge.getFrom(), edge.getTo())); - } - - for (Object locObj : allEdgeLocs) { - Location loc = (Location) locObj; - if (locs.contains(loc)) { - if (edge.getRadius() >= minDamageRadius) { - addLocationsIntoList( - grid, loc, siteLocs0, siteLocs1, siteLocs2, siteLocs3); - } + for (Object locObj : allEdgeLocs) { + Location loc = (Location) locObj; + if (locs.contains(loc)) { + if (edge.getRadius() >= minDamageRadius) { + addLocationsIntoList( + grid, loc, siteLocs0, siteLocs1, siteLocs2, siteLocs3); } } } - break; + } + } else if (type.equals("source") || type.equals("pattern")) { + double[][][] damage; + boolean[][][] sitesLat; + + if (type.equals("source")) { + damage = ((PatchComponentSitesSource) comp).getDamage(); + sitesLat = ((PatchComponentSitesSource) comp).getSources(); + } else { + damage = ((PatchComponentSitesPattern) comp).getDamage(); + sitesLat = ((PatchComponentSitesPattern) comp).getPatterns(); + } - default: - throw new IllegalArgumentException( - "Invalid component type: " - + type - + ". Must be of type source, pattern, or graph."); + // Iterate through list of locations and remove locations not next to a site. + for (LocationContainer l : locs) { + PatchLocationContainer contain = (PatchLocationContainer) l; + PatchLocation loc = + (PatchLocation) + contain.convert( + sim.locationFactory, + sim.cellFactory.createCellForPopulation( + 0, populations.get(0).getInt("CODE"))); + CoordinateXYZ coordinate = (CoordinateXYZ) loc.getSubcoordinate(); + int z = coordinate.z; + if (sitesLat[z][coordinate.x][coordinate.y] + && damage[z][coordinate.x][coordinate.y] <= this.maxDamage) { + addLocationsIntoList(grid, loc, siteLocs0, siteLocs1, siteLocs2, siteLocs3); + } + } + } else { + throw new IllegalArgumentException( + "Invalid component type: " + + type + + ". Must be of type source, pattern, or graph."); } Utilities.shuffleList(siteLocs3, sim.random); From acb59659d7392d7c4afe670e127aa90697eb8118 Mon Sep 17 00:00:00 2001 From: allison-li-1016 Date: Tue, 3 Jun 2025 21:17:53 +0000 Subject: [PATCH 16/36] we have a parameter for max confluency --- src/arcade/patch/agent/action/PatchActionTreat.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/arcade/patch/agent/action/PatchActionTreat.java b/src/arcade/patch/agent/action/PatchActionTreat.java index 4f65f0657..bbe5d6b8b 100644 --- a/src/arcade/patch/agent/action/PatchActionTreat.java +++ b/src/arcade/patch/agent/action/PatchActionTreat.java @@ -83,7 +83,7 @@ public PatchActionTreat(Series series, MiniBox parameters) { this.treatFrac = parameters.getDouble("RATIO"); this.maxDamage = parameters.getDouble("MAX_DAMAGE_SEED"); this.minDamageRadius = parameters.getDouble("MIN_RADIUS_SEED"); - this.maxConfluency = 54; + this.maxConfluency = parameters.getInt("MAX_DENSITY"); this.parameters = parameters; this.coord = From 8e31666bec5a4bc743fc38401c4f4d53c9c3dfea Mon Sep 17 00:00:00 2001 From: allison-li-1016 Date: Tue, 3 Jun 2025 21:25:47 +0000 Subject: [PATCH 17/36] fixing tests --- test/arcade/patch/agent/action/PatchActionTreatTest.java | 1 + 1 file changed, 1 insertion(+) diff --git a/test/arcade/patch/agent/action/PatchActionTreatTest.java b/test/arcade/patch/agent/action/PatchActionTreatTest.java index 65d3c2f68..f2590c062 100644 --- a/test/arcade/patch/agent/action/PatchActionTreatTest.java +++ b/test/arcade/patch/agent/action/PatchActionTreatTest.java @@ -173,6 +173,7 @@ public void step_zeroDose_doesNotAddObjects() @Test public void checkLocationSpace_withEmptySpaces_returnsAvailable() throws NoSuchFieldException, IllegalAccessException { + when(parameters.getInt("MAX_DENSITY")).thenReturn(54); action = new PatchActionTreat(series, parameters); ArrayList populations = new ArrayList<>(); MiniBox populationMock = mock(MiniBox.class); From cbdae061ce4a2695d231a68b04218b4f48ad3f5a Mon Sep 17 00:00:00 2001 From: allison-li-1016 Date: Tue, 3 Jun 2025 21:58:18 +0000 Subject: [PATCH 18/36] changing test to use parameter --- test/arcade/patch/agent/action/PatchActionTreatTest.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/test/arcade/patch/agent/action/PatchActionTreatTest.java b/test/arcade/patch/agent/action/PatchActionTreatTest.java index f2590c062..0b02dd9c1 100644 --- a/test/arcade/patch/agent/action/PatchActionTreatTest.java +++ b/test/arcade/patch/agent/action/PatchActionTreatTest.java @@ -133,6 +133,7 @@ public void schedule_called_schedulesOnAction() @Test public void step_called_addsObjectsToSim() throws NoSuchFieldException, IllegalAccessException { + when(parameters.getInt("MAX_DENSITY")).thenReturn(54); action = new PatchActionTreat(series, parameters); ArrayList populations = new ArrayList<>(); @@ -153,6 +154,7 @@ public void step_called_addsObjectsToSim() throws NoSuchFieldException, IllegalA @Test public void step_zeroDose_doesNotAddObjects() throws NoSuchFieldException, IllegalAccessException { + when(parameters.getInt("MAX_DENSITY")).thenReturn(54); when(parameters.getInt("DOSE")).thenReturn(0); action = new PatchActionTreat(series, parameters); @@ -196,10 +198,8 @@ public void checkLocationSpace_withEmptySpaces_returnsAvailable() @Test public void checkLocation_maxConfluency_returnsUnavailable() throws NoSuchFieldException, IllegalAccessException { + when(parameters.getInt("MAX_DENSITY")).thenReturn(1); action = new PatchActionTreat(series, parameters); - Field density = PatchActionTreat.class.getDeclaredField("maxConfluency"); - density.setAccessible(true); - density.set(action, 1); ArrayList populations = new ArrayList<>(); MiniBox populationMock = mock(MiniBox.class); From 66287ca7edcb0a011cf6ca62ebd6d5a31e2b5291 Mon Sep 17 00:00:00 2001 From: allison-li-1016 Date: Tue, 3 Jun 2025 23:25:48 +0000 Subject: [PATCH 19/36] removing the need for 4 lists --- .../patch/agent/action/PatchActionTreat.java | 66 +++++-------------- .../agent/action/PatchActionTreatTest.java | 32 +++++---- 2 files changed, 33 insertions(+), 65 deletions(-) diff --git a/src/arcade/patch/agent/action/PatchActionTreat.java b/src/arcade/patch/agent/action/PatchActionTreat.java index bbe5d6b8b..b13b15a97 100644 --- a/src/arcade/patch/agent/action/PatchActionTreat.java +++ b/src/arcade/patch/agent/action/PatchActionTreat.java @@ -1,6 +1,7 @@ package arcade.patch.agent.action; import java.util.ArrayList; +import java.util.Comparator; import java.util.Objects; import sim.engine.Schedule; import sim.engine.SimState; @@ -123,11 +124,6 @@ public void step(SimState simstate) { PatchComponentSites comp = (PatchComponentSites) sim.getComponent("SITES"); ArrayList locs = sim.getLocations(); - - ArrayList siteLocs0 = new ArrayList(); - ArrayList siteLocs1 = new ArrayList(); - ArrayList siteLocs2 = new ArrayList(); - ArrayList siteLocs3 = new ArrayList(); ArrayList siteLocs = new ArrayList(); // Determine type of sites component implemented. @@ -162,8 +158,9 @@ public void step(SimState simstate) { Location loc = (Location) locObj; if (locs.contains(loc)) { if (edge.getRadius() >= minDamageRadius) { - addLocationsIntoList( - grid, loc, siteLocs0, siteLocs1, siteLocs2, siteLocs3); + for (int p = 0; p < latPositions; p++) { + siteLocs.add(loc); + } } } } @@ -193,7 +190,9 @@ public void step(SimState simstate) { int z = coordinate.z; if (sitesLat[z][coordinate.x][coordinate.y] && damage[z][coordinate.x][coordinate.y] <= this.maxDamage) { - addLocationsIntoList(grid, loc, siteLocs0, siteLocs1, siteLocs2, siteLocs3); + for (int p = 0; p < latPositions; p++) { + siteLocs.add(loc); + } } } } else { @@ -203,58 +202,23 @@ public void step(SimState simstate) { + ". Must be of type source, pattern, or graph."); } - Utilities.shuffleList(siteLocs3, sim.random); - Utilities.shuffleList(siteLocs2, sim.random); - Utilities.shuffleList(siteLocs1, sim.random); - Utilities.shuffleList(siteLocs0, sim.random); - siteLocs.addAll(siteLocs3); - siteLocs.addAll(siteLocs2); - siteLocs.addAll(siteLocs1); - siteLocs.addAll(siteLocs0); + Utilities.shuffleList(siteLocs, sim.random); + // sort locations in descending order from highest to lowest density + siteLocs.sort(Comparator.comparingInt(l -> -computeDensity(grid, l))); insert(siteLocs, simstate); } /** - * Helper method to sort locations into lists. + * Helper method to sort locations. * * @param grid the simulation grid - * @param loc the current location being looked at - * @param siteLocs0 the list of locations with 0 agents - * @param siteLocs1 the list of locations with 1 agent - * @param siteLocs2 the list of locations with 2 agents - * @param siteLocs3 the list of locations with 3 agents + * @param loc the current location being looked + * @return the density of agents at the location */ - private void addLocationsIntoList( - PatchGrid grid, - Location loc, - ArrayList siteLocs0, - ArrayList siteLocs1, - ArrayList siteLocs2, - ArrayList siteLocs3) { + private int computeDensity(PatchGrid grid, Location loc) { Bag bag = new Bag(grid.getObjectsAtLocation(loc)); int numAgents = bag.numObjs; - - if (numAgents == 0) { - for (int p = 0; p < latPositions; p++) { - siteLocs0.add(loc); - } - } else if (numAgents == 1) { - for (int p = 0; p < latPositions; p++) { - siteLocs1.add(loc); - } - } else if (numAgents == 2) { - for (int p = 0; p < latPositions; p++) { - siteLocs2.add(loc); - } - } else { - for (int p = 0; p < latPositions; p++) { - siteLocs3.add(loc); - } - } - // Remove break statement if more than one per hex can appear - // with break statement, each location can only be added to list once - // without it, places with more vasc sites get added more times to list - // break; + return numAgents; } /** diff --git a/test/arcade/patch/agent/action/PatchActionTreatTest.java b/test/arcade/patch/agent/action/PatchActionTreatTest.java index 0b02dd9c1..dc440d3bc 100644 --- a/test/arcade/patch/agent/action/PatchActionTreatTest.java +++ b/test/arcade/patch/agent/action/PatchActionTreatTest.java @@ -14,7 +14,6 @@ import arcade.patch.agent.cell.PatchCellFactory; import arcade.patch.env.component.PatchComponentSitesSource; import arcade.patch.env.grid.PatchGrid; -import arcade.patch.env.location.Coordinate; import arcade.patch.env.location.CoordinateUVWZ; import arcade.patch.env.location.CoordinateXYZ; import arcade.patch.env.location.PatchLocation; @@ -52,6 +51,7 @@ public final void setUp() throws NoSuchFieldException, IllegalAccessException { MiniBox patchMock = mock(MiniBox.class); series.patch = patchMock; gridMock = mock(PatchGrid.class); + cellMock = mock(PatchCell.class); Schedule mockSchedule = mock(Schedule.class); when(patchMock.get("GEOMETRY")).thenReturn("HEX"); sim = mock(PatchSimulation.class); @@ -63,28 +63,33 @@ public final void setUp() throws NoSuchFieldException, IllegalAccessException { boolean[][][] sitesLat = new boolean[20][20][20]; damage[0][0][0] = 0.05; sitesLat[0][0][0] = true; + damage[1][0][0] = 0.05; + sitesLat[1][0][0] = true; when(sources.getDamage()).thenReturn(damage); when(sources.getSources()).thenReturn(sitesLat); when(sim.getComponent("SITES")).thenReturn(sources); ArrayList locations = new ArrayList<>(); PatchLocationContainer container = mock(PatchLocationContainer.class); + PatchLocationContainer container2 = mock(PatchLocationContainer.class); PatchLocation loc = mock(PatchLocation.class); - CoordinateXYZ coord = mock(CoordinateXYZ.class); - Field x = CoordinateXYZ.class.getDeclaredField("x"); - x.setAccessible(true); - x.set(coord, 0); - Field y = CoordinateXYZ.class.getDeclaredField("y"); - y.setAccessible(true); - y.set(coord, 0); - Field z = Coordinate.class.getDeclaredField("z"); - z.setAccessible(true); - z.set(coord, 0); - CoordinateUVWZ c = mock(CoordinateUVWZ.class); - z.set(c, 0); + PatchLocation loc2 = mock(PatchLocation.class); + CoordinateXYZ coord = new CoordinateXYZ(0, 0, 0); + CoordinateUVWZ c = new CoordinateUVWZ(0, 0, 0, 0); when(loc.getSubcoordinate()).thenReturn(coord); when(loc.getCoordinate()).thenReturn(c); + Bag mockBag1 = new Bag(); when(container.convert(any(), any())).thenReturn(loc); + CoordinateXYZ coord2 = new CoordinateXYZ(0, 0, 1); + CoordinateUVWZ c2 = new CoordinateUVWZ(0, 0, 0, 1); + when(loc2.getSubcoordinate()).thenReturn(coord2); + when(loc2.getCoordinate()).thenReturn(c2); + when(container2.convert(any(), any())).thenReturn(loc2); + Bag mockBag2 = new Bag(); + mockBag2.add(cellMock); locations.add(container); + locations.add(container2); + when(gridMock.getObjectsAtLocation(loc)).thenReturn(mockBag1); + when(gridMock.getObjectsAtLocation(loc2)).thenReturn(mockBag2); when(sim.getLocations()).thenReturn(locations); sim.random = mock(MersenneTwisterFast.class); @@ -92,7 +97,6 @@ public final void setUp() throws NoSuchFieldException, IllegalAccessException { PatchCellContainer patchCellContainerMock = mock(PatchCellContainer.class); when(factoryMock.createCellForPopulation(any(Integer.class), any(Integer.class))) .thenReturn(patchCellContainerMock); - cellMock = mock(PatchCell.class); when(patchCellContainerMock.convert(any(), any(), any())).thenReturn(cellMock); Field factory = PatchSimulation.class.getDeclaredField("cellFactory"); From 4fc613cc2fb0ad6d9a7bfc893a1bcbd795159bed Mon Sep 17 00:00:00 2001 From: allison-li-1016 Date: Wed, 4 Jun 2025 22:32:21 +0000 Subject: [PATCH 20/36] adding options to treatment --- src/arcade/patch/agent/action/PatchActionTreat.java | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/arcade/patch/agent/action/PatchActionTreat.java b/src/arcade/patch/agent/action/PatchActionTreat.java index b13b15a97..75f7bb9d4 100644 --- a/src/arcade/patch/agent/action/PatchActionTreat.java +++ b/src/arcade/patch/agent/action/PatchActionTreat.java @@ -243,6 +243,12 @@ private void insert(ArrayList coordinates, SimState simstate) { if (className.equals("cart_cd8")) { cd8Code = population.getInt("CODE"); } + if (className.equals("killer")) { + cd8Code = population.getInt("CODE"); + } + if (className.equals("synnotch")) { + cd8Code = population.getInt("CODE"); + } } for (int i = 0; i < dose; i++) { From 77a713c6b4f68492e846f02ffc0eecb582be36d3 Mon Sep 17 00:00:00 2001 From: allison-li-1016 Date: Mon, 9 Jun 2025 20:36:17 +0000 Subject: [PATCH 21/36] refactor code into helper methods --- .../patch/agent/action/PatchActionTreat.java | 125 +++++++++++------- 1 file changed, 78 insertions(+), 47 deletions(-) diff --git a/src/arcade/patch/agent/action/PatchActionTreat.java b/src/arcade/patch/agent/action/PatchActionTreat.java index 75f7bb9d4..2b23343ed 100644 --- a/src/arcade/patch/agent/action/PatchActionTreat.java +++ b/src/arcade/patch/agent/action/PatchActionTreat.java @@ -137,34 +137,7 @@ public void step(SimState simstate) { // Find sites without specified level of damage based on component type. if (type.equals("graph")) { - Graph graph = ((PatchComponentSitesGraph) comp).getGraph(); - Bag allEdges = new Bag(graph.getAllEdges()); - PatchComponentSitesGraph graphSites = (PatchComponentSitesGraph) comp; - - for (Object edgeObj : allEdges) { - SiteEdge edge = (SiteEdge) edgeObj; - Bag allEdgeLocs = new Bag(); - if (Objects.equals(coord, "Hex")) { - allEdgeLocs.add( - ((PatchComponentSitesGraphTri) graphSites) - .getSpan(edge.getFrom(), edge.getTo())); - } else { - allEdgeLocs.add( - ((PatchComponentSitesGraphRect) graphSites) - .getSpan(edge.getFrom(), edge.getTo())); - } - - for (Object locObj : allEdgeLocs) { - Location loc = (Location) locObj; - if (locs.contains(loc)) { - if (edge.getRadius() >= minDamageRadius) { - for (int p = 0; p < latPositions; p++) { - siteLocs.add(loc); - } - } - } - } - } + findGraphSites(comp, locs, siteLocs); } else if (type.equals("source") || type.equals("pattern")) { double[][][] damage; boolean[][][] sitesLat; @@ -176,25 +149,7 @@ public void step(SimState simstate) { damage = ((PatchComponentSitesPattern) comp).getDamage(); sitesLat = ((PatchComponentSitesPattern) comp).getPatterns(); } - - // Iterate through list of locations and remove locations not next to a site. - for (LocationContainer l : locs) { - PatchLocationContainer contain = (PatchLocationContainer) l; - PatchLocation loc = - (PatchLocation) - contain.convert( - sim.locationFactory, - sim.cellFactory.createCellForPopulation( - 0, populations.get(0).getInt("CODE"))); - CoordinateXYZ coordinate = (CoordinateXYZ) loc.getSubcoordinate(); - int z = coordinate.z; - if (sitesLat[z][coordinate.x][coordinate.y] - && damage[z][coordinate.x][coordinate.y] <= this.maxDamage) { - for (int p = 0; p < latPositions; p++) { - siteLocs.add(loc); - } - } - } + pruneSite(locs, sim, damage, sitesLat, siteLocs); } else { throw new IllegalArgumentException( "Invalid component type: " @@ -208,6 +163,82 @@ public void step(SimState simstate) { insert(siteLocs, simstate); } + /** + * Helper method to check if radius is wide enough for T-cells to pass through. + * + * @param comp the component + * @param locs the locations to check + * @param siteLocs the locations that meet the criteria + */ + private void findGraphSites( + PatchComponentSites comp, + ArrayList locs, + ArrayList siteLocs) { + Graph graph = ((PatchComponentSitesGraph) comp).getGraph(); + Bag allEdges = new Bag(graph.getAllEdges()); + PatchComponentSitesGraph graphSites = (PatchComponentSitesGraph) comp; + + for (Object edgeObj : allEdges) { + SiteEdge edge = (SiteEdge) edgeObj; + Bag allEdgeLocs = new Bag(); + if (Objects.equals(coord, "Hex")) { + allEdgeLocs.add( + ((PatchComponentSitesGraphTri) graphSites) + .getSpan(edge.getFrom(), edge.getTo())); + } else { + allEdgeLocs.add( + ((PatchComponentSitesGraphRect) graphSites) + .getSpan(edge.getFrom(), edge.getTo())); + } + + for (Object locObj : allEdgeLocs) { + Location loc = (Location) locObj; + if (locs.contains(loc)) { + if (edge.getRadius() >= minDamageRadius) { + for (int p = 0; p < latPositions; p++) { + siteLocs.add(loc); + } + } + } + } + } + } + + /** + * Helper method to remove locations that are not next to a site or have too much damage for + * T-cells to pass through. + * + * @param locs the locations to check + * @param sim the simuation instance + * @param damage the damage array for sites + * @param sitesLat the lattice array for sites + * @param siteLocs the locations that meet the criteria + */ + public void pruneSite( + ArrayList locs, + PatchSimulation sim, + double[][][] damage, + boolean[][][] sitesLat, + ArrayList siteLocs) { + for (LocationContainer l : locs) { + PatchLocationContainer contain = (PatchLocationContainer) l; + PatchLocation loc = + (PatchLocation) + contain.convert( + sim.locationFactory, + sim.cellFactory.createCellForPopulation( + 0, populations.get(0).getInt("CODE"))); + CoordinateXYZ coordinate = (CoordinateXYZ) loc.getSubcoordinate(); + int z = coordinate.z; + if (sitesLat[z][coordinate.x][coordinate.y] + && damage[z][coordinate.x][coordinate.y] <= this.maxDamage) { + for (int p = 0; p < latPositions; p++) { + siteLocs.add(loc); + } + } + } + } + /** * Helper method to sort locations. * From 2b8607b4ea908ef961190744f90c8a7f12bd7251 Mon Sep 17 00:00:00 2001 From: allison-li-1016 Date: Thu, 12 Jun 2025 18:48:50 +0000 Subject: [PATCH 22/36] now the action can actually be run on input files --- src/arcade/patch/sim/PatchSimulationHex.java | 3 +++ src/arcade/patch/sim/PatchSimulationRect.java | 3 +++ 2 files changed, 6 insertions(+) diff --git a/src/arcade/patch/sim/PatchSimulationHex.java b/src/arcade/patch/sim/PatchSimulationHex.java index 6eddf28b5..0109aa872 100644 --- a/src/arcade/patch/sim/PatchSimulationHex.java +++ b/src/arcade/patch/sim/PatchSimulationHex.java @@ -7,6 +7,7 @@ import arcade.patch.agent.action.PatchActionConvert; import arcade.patch.agent.action.PatchActionInsert; import arcade.patch.agent.action.PatchActionRemove; +import arcade.patch.agent.action.PatchActionTreat; import arcade.patch.agent.cell.PatchCellFactory; import arcade.patch.env.component.PatchComponentCycle; import arcade.patch.env.component.PatchComponentDegrade; @@ -58,6 +59,8 @@ public Action makeAction(String actionClass, MiniBox parameters) { return new PatchActionRemove(series, parameters); case "convert": return new PatchActionConvert(series, parameters); + case "treat": + return new PatchActionTreat(series, parameters); default: return null; } diff --git a/src/arcade/patch/sim/PatchSimulationRect.java b/src/arcade/patch/sim/PatchSimulationRect.java index 6623fefec..0fdfe8523 100644 --- a/src/arcade/patch/sim/PatchSimulationRect.java +++ b/src/arcade/patch/sim/PatchSimulationRect.java @@ -7,6 +7,7 @@ import arcade.patch.agent.action.PatchActionConvert; import arcade.patch.agent.action.PatchActionInsert; import arcade.patch.agent.action.PatchActionRemove; +import arcade.patch.agent.action.PatchActionTreat; import arcade.patch.agent.cell.PatchCellFactory; import arcade.patch.env.component.PatchComponentCycle; import arcade.patch.env.component.PatchComponentDegrade; @@ -58,6 +59,8 @@ public Action makeAction(String actionClass, MiniBox parameters) { return new PatchActionRemove(series, parameters); case "convert": return new PatchActionConvert(series, parameters); + case "treat": + return new PatchActionTreat(series, parameters); default: return null; } From fb816efb143340c186e5604c535fad0238beb70f Mon Sep 17 00:00:00 2001 From: allison-li-1016 Date: Thu, 12 Jun 2025 20:04:32 +0000 Subject: [PATCH 23/36] not hardcoding cell codes --- .../patch/agent/action/PatchActionTreat.java | 29 +++++++++---------- src/arcade/patch/util/PatchEnums.java | 22 ++++++++++++++ 2 files changed, 36 insertions(+), 15 deletions(-) diff --git a/src/arcade/patch/agent/action/PatchActionTreat.java b/src/arcade/patch/agent/action/PatchActionTreat.java index 2b23343ed..20fd3a4b8 100644 --- a/src/arcade/patch/agent/action/PatchActionTreat.java +++ b/src/arcade/patch/agent/action/PatchActionTreat.java @@ -1,8 +1,11 @@ package arcade.patch.agent.action; import java.util.ArrayList; +import java.util.Arrays; import java.util.Comparator; import java.util.Objects; +import java.util.Set; +import java.util.stream.Collectors; import sim.engine.Schedule; import sim.engine.SimState; import sim.util.Bag; @@ -32,6 +35,7 @@ import arcade.patch.env.location.PatchLocationContainer; import arcade.patch.sim.PatchSeries; import arcade.patch.sim.PatchSimulation; +import arcade.patch.util.PatchEnums.Immune; import arcade.patch.util.PatchEnums.Ordering; /** @@ -263,33 +267,28 @@ private void insert(ArrayList coordinates, SimState simstate) { PatchGrid grid = (PatchGrid) sim.getGrid(); Utilities.shuffleList(coordinates, sim.random); - int cd4Code = 0; - int cd8Code = 0; + ArrayList populationCodes = new ArrayList<>(); + Set immuneCells = + Arrays.stream(Immune.values()).map(Enum::name).collect(Collectors.toSet()); for (MiniBox population : populations) { String className = population.get("CLASS"); - if (className.equals("cart_cd4")) { - cd4Code = population.getInt("CODE"); - } - if (className.equals("cart_cd8")) { - cd8Code = population.getInt("CODE"); - } - if (className.equals("killer")) { - cd8Code = population.getInt("CODE"); - } - if (className.equals("synnotch")) { - cd8Code = population.getInt("CODE"); + if (immuneCells.contains(className)) { + populationCodes.add(population.getInt("CODE")); } } + int pop1 = populationCodes.get(0); + int pop2 = populationCodes.get(1); + for (int i = 0; i < dose; i++) { int id = sim.getID(); - int pop = cd4Code; + int pop = pop1; if (sim.random.nextDouble() > treatFrac) { - pop = cd8Code; + pop = pop2; } PatchLocation loc = ((PatchLocation) coordinates.remove(0)); diff --git a/src/arcade/patch/util/PatchEnums.java b/src/arcade/patch/util/PatchEnums.java index 6cc940453..66fe2e343 100644 --- a/src/arcade/patch/util/PatchEnums.java +++ b/src/arcade/patch/util/PatchEnums.java @@ -197,6 +197,28 @@ public static AntigenFlag random(MersenneTwisterFast rng) { } } + /** Cell types for immune cell classification. */ + public enum Immune { + /** Code for cd4 T cell. */ + CART_CD4, + /** Code for cd8 T cell. */ + CART_CD8, + /** Code for macrophage cell. */ + MACROPHAGE, + /** Code for killer cd8 cell. */ + KILLER; + + /** + * Randomly selects a {@code Immune} cell type + * + * @param rng the random number generator + * @return a random {@code Immune} cell type + */ + public static Immune random(MersenneTwisterFast rng) { + return values()[rng.nextInt(values().length - 1) + 1]; + } + } + /** Operation category codes for patch simulations. */ public enum Category implements OperationCategory { /** Code for undefined category. */ From 996c004b34b4bf843e836ebe3f577d4d6ba11584 Mon Sep 17 00:00:00 2001 From: allison-li-1016 Date: Thu, 12 Jun 2025 20:08:58 +0000 Subject: [PATCH 24/36] add case for edge case --- src/arcade/patch/agent/action/PatchActionTreat.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/arcade/patch/agent/action/PatchActionTreat.java b/src/arcade/patch/agent/action/PatchActionTreat.java index 20fd3a4b8..b9c154907 100644 --- a/src/arcade/patch/agent/action/PatchActionTreat.java +++ b/src/arcade/patch/agent/action/PatchActionTreat.java @@ -278,6 +278,10 @@ private void insert(ArrayList coordinates, SimState simstate) { } } + if (populationCodes.isEmpty()) { + return; + } + int pop1 = populationCodes.get(0); int pop2 = populationCodes.get(1); From f774218c184b0798e8614b0d123f7a56850a4bed Mon Sep 17 00:00:00 2001 From: allison-li-1016 Date: Thu, 12 Jun 2025 20:10:34 +0000 Subject: [PATCH 25/36] add exception for illegal cell types --- src/arcade/patch/agent/action/PatchActionTreat.java | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/arcade/patch/agent/action/PatchActionTreat.java b/src/arcade/patch/agent/action/PatchActionTreat.java index b9c154907..58f2f4450 100644 --- a/src/arcade/patch/agent/action/PatchActionTreat.java +++ b/src/arcade/patch/agent/action/PatchActionTreat.java @@ -275,6 +275,12 @@ private void insert(ArrayList coordinates, SimState simstate) { String className = population.get("CLASS"); if (immuneCells.contains(className)) { populationCodes.add(population.getInt("CODE")); + } else { + throw new IllegalArgumentException( + "Population " + + population.get("CLASS") + + " is not an immune cell type. " + + "Only immune cells can be used for treat."); } } From 190d1b25919441a1a045353735f6bbf4213e4be1 Mon Sep 17 00:00:00 2001 From: allison-li-1016 Date: Thu, 12 Jun 2025 20:29:35 +0000 Subject: [PATCH 26/36] reverting exception --- src/arcade/patch/agent/action/PatchActionTreat.java | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/arcade/patch/agent/action/PatchActionTreat.java b/src/arcade/patch/agent/action/PatchActionTreat.java index 58f2f4450..b9c154907 100644 --- a/src/arcade/patch/agent/action/PatchActionTreat.java +++ b/src/arcade/patch/agent/action/PatchActionTreat.java @@ -275,12 +275,6 @@ private void insert(ArrayList coordinates, SimState simstate) { String className = population.get("CLASS"); if (immuneCells.contains(className)) { populationCodes.add(population.getInt("CODE")); - } else { - throw new IllegalArgumentException( - "Population " - + population.get("CLASS") - + " is not an immune cell type. " - + "Only immune cells can be used for treat."); } } From f4ea633c8f3cfa9e1e371f07f0b3200a1c340461 Mon Sep 17 00:00:00 2001 From: allison-li-1016 Date: Thu, 12 Jun 2025 20:47:49 +0000 Subject: [PATCH 27/36] fixing tests, adding edgecase checking --- src/arcade/patch/agent/action/PatchActionTreat.java | 4 ++-- .../patch/agent/action/PatchActionTreatTest.java | 12 ++++++++---- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/src/arcade/patch/agent/action/PatchActionTreat.java b/src/arcade/patch/agent/action/PatchActionTreat.java index b9c154907..4d26e9ef2 100644 --- a/src/arcade/patch/agent/action/PatchActionTreat.java +++ b/src/arcade/patch/agent/action/PatchActionTreat.java @@ -272,13 +272,13 @@ private void insert(ArrayList coordinates, SimState simstate) { Set immuneCells = Arrays.stream(Immune.values()).map(Enum::name).collect(Collectors.toSet()); for (MiniBox population : populations) { - String className = population.get("CLASS"); + String className = population.get("CLASS").toUpperCase(); if (immuneCells.contains(className)) { populationCodes.add(population.getInt("CODE")); } } - if (populationCodes.isEmpty()) { + if (populationCodes.size() < 2) { return; } diff --git a/test/arcade/patch/agent/action/PatchActionTreatTest.java b/test/arcade/patch/agent/action/PatchActionTreatTest.java index dc440d3bc..522714f21 100644 --- a/test/arcade/patch/agent/action/PatchActionTreatTest.java +++ b/test/arcade/patch/agent/action/PatchActionTreatTest.java @@ -182,10 +182,14 @@ public void checkLocationSpace_withEmptySpaces_returnsAvailable() when(parameters.getInt("MAX_DENSITY")).thenReturn(54); action = new PatchActionTreat(series, parameters); ArrayList populations = new ArrayList<>(); - MiniBox populationMock = mock(MiniBox.class); - when(populationMock.getInt("CODE")).thenReturn(4); - when(populationMock.get("CLASS")).thenReturn("cart_cd4"); - populations.add(populationMock); + MiniBox populationMock1 = new MiniBox(); + populationMock1.put("CLASS", "cart_cd4"); + populationMock1.put("CODE", 4); + MiniBox populationMock2 = new MiniBox(); + populationMock2.put("CLASS", "cart_cd8"); + populationMock2.put("CODE", 8); + populations.add(populationMock1); + populations.add(populationMock2); Field pops = PatchActionTreat.class.getDeclaredField("populations"); pops.setAccessible(true); pops.set(action, populations); From 5063cedd1d64cf7febf9be9a766b1594796cce3e Mon Sep 17 00:00:00 2001 From: allison-li-1016 Date: Thu, 12 Jun 2025 20:52:54 +0000 Subject: [PATCH 28/36] the tests should be working now --- .../agent/action/PatchActionTreatTest.java | 36 ++++++++++--------- 1 file changed, 20 insertions(+), 16 deletions(-) diff --git a/test/arcade/patch/agent/action/PatchActionTreatTest.java b/test/arcade/patch/agent/action/PatchActionTreatTest.java index 522714f21..1f459845b 100644 --- a/test/arcade/patch/agent/action/PatchActionTreatTest.java +++ b/test/arcade/patch/agent/action/PatchActionTreatTest.java @@ -141,10 +141,14 @@ public void step_called_addsObjectsToSim() throws NoSuchFieldException, IllegalA action = new PatchActionTreat(series, parameters); ArrayList populations = new ArrayList<>(); - MiniBox populationMock = mock(MiniBox.class); - when(populationMock.getInt("CODE")).thenReturn(4); - when(populationMock.get("CLASS")).thenReturn("cart_cd4"); - populations.add(populationMock); + MiniBox populationMock1 = new MiniBox(); + populationMock1.put("CLASS", "cart_cd4"); + populationMock1.put("CODE", 4); + MiniBox populationMock2 = new MiniBox(); + populationMock2.put("CLASS", "cart_cd8"); + populationMock2.put("CODE", 8); + populations.add(populationMock1); + populations.add(populationMock2); Field pops = PatchActionTreat.class.getDeclaredField("populations"); pops.setAccessible(true); pops.set(action, populations); @@ -163,10 +167,14 @@ public void step_zeroDose_doesNotAddObjects() action = new PatchActionTreat(series, parameters); ArrayList populations = new ArrayList<>(); - MiniBox populationMock = mock(MiniBox.class); - when(populationMock.getInt("CODE")).thenReturn(4); - when(populationMock.get("CLASS")).thenReturn("cart_cd4"); - populations.add(populationMock); + MiniBox populationMock1 = new MiniBox(); + populationMock1.put("CLASS", "cart_cd4"); + populationMock1.put("CODE", 4); + MiniBox populationMock2 = new MiniBox(); + populationMock2.put("CLASS", "cart_cd8"); + populationMock2.put("CODE", 8); + populations.add(populationMock1); + populations.add(populationMock2); Field pops = PatchActionTreat.class.getDeclaredField("populations"); pops.setAccessible(true); pops.set(action, populations); @@ -182,14 +190,10 @@ public void checkLocationSpace_withEmptySpaces_returnsAvailable() when(parameters.getInt("MAX_DENSITY")).thenReturn(54); action = new PatchActionTreat(series, parameters); ArrayList populations = new ArrayList<>(); - MiniBox populationMock1 = new MiniBox(); - populationMock1.put("CLASS", "cart_cd4"); - populationMock1.put("CODE", 4); - MiniBox populationMock2 = new MiniBox(); - populationMock2.put("CLASS", "cart_cd8"); - populationMock2.put("CODE", 8); - populations.add(populationMock1); - populations.add(populationMock2); + MiniBox populationMock = mock(MiniBox.class); + when(populationMock.getInt("CODE")).thenReturn(4); + when(populationMock.get("CLASS")).thenReturn("cart_cd4"); + populations.add(populationMock); Field pops = PatchActionTreat.class.getDeclaredField("populations"); pops.setAccessible(true); pops.set(action, populations); From 0c12f030afd3cbc2ee40d0542c32c5ccba661784 Mon Sep 17 00:00:00 2001 From: allison-li-1016 Date: Fri, 27 Jun 2025 20:22:50 +0000 Subject: [PATCH 29/36] removing ratio option so that treat is more similar to insert --- .../patch/agent/action/PatchActionTreat.java | 65 ++++++++----------- src/arcade/patch/parameter.patch.xml | 1 - 2 files changed, 28 insertions(+), 38 deletions(-) diff --git a/src/arcade/patch/agent/action/PatchActionTreat.java b/src/arcade/patch/agent/action/PatchActionTreat.java index 4d26e9ef2..8cce3a3dd 100644 --- a/src/arcade/patch/agent/action/PatchActionTreat.java +++ b/src/arcade/patch/agent/action/PatchActionTreat.java @@ -42,7 +42,7 @@ * Implementation of {@link Action} for inserting T cell agents. * *

The action is stepped once after {@code TIME_DELAY}. The {@code TreatAction} will add CAR - * T-cell agents of specified dose and ratio next to source points or vasculature. + * T-cell agents of specified dose next to source points or vasculature. */ public class PatchActionTreat implements Action { @@ -52,9 +52,6 @@ public class PatchActionTreat implements Action { /** Total number of CAR T-cells to treat with. */ private final int dose; - /** List of fraction of each population to treat with. CD4 to CD8 ratio. */ - private final double treatFrac; - /** Maximum damage value at which T-cells can spawn next to in source or pattern source. */ private double maxDamage; @@ -85,7 +82,6 @@ public class PatchActionTreat implements Action { public PatchActionTreat(Series series, MiniBox parameters) { this.delay = parameters.getInt("TIME_DELAY"); this.dose = parameters.getInt("DOSE"); - this.treatFrac = parameters.getDouble("RATIO"); this.maxDamage = parameters.getDouble("MAX_DAMAGE_SEED"); this.minDamageRadius = parameters.getDouble("MIN_RADIUS_SEED"); this.maxConfluency = parameters.getInt("MAX_DENSITY"); @@ -267,52 +263,47 @@ private void insert(ArrayList coordinates, SimState simstate) { PatchGrid grid = (PatchGrid) sim.getGrid(); Utilities.shuffleList(coordinates, sim.random); - ArrayList populationCodes = new ArrayList<>(); - Set immuneCells = Arrays.stream(Immune.values()).map(Enum::name).collect(Collectors.toSet()); + for (MiniBox population : populations) { String className = population.get("CLASS").toUpperCase(); - if (immuneCells.contains(className)) { - populationCodes.add(population.getInt("CODE")); - } - } - - if (populationCodes.size() < 2) { - return; - } - int pop1 = populationCodes.get(0); - int pop2 = populationCodes.get(1); + if (!immuneCells.contains(className)) { + throw new IllegalArgumentException( + "Population " + + population.get("CLASS") + + " is not an immune cell and cannot be treated."); + } - for (int i = 0; i < dose; i++) { + int pop = population.getInt("CODE"); - int id = sim.getID(); + for (int i = 0; i < dose; i++) { + int id = sim.getID(); - int pop = pop1; + PatchLocation loc = ((PatchLocation) coordinates.remove(0)); - if (sim.random.nextDouble() > treatFrac) { - pop = pop2; - } + while (!coordinates.isEmpty() && !checkLocationSpace(loc, grid)) { + loc = ((PatchLocation) coordinates.remove(0)); + } - PatchLocation loc = ((PatchLocation) coordinates.remove(0)); - Coordinate coordinate = loc.getCoordinate(); + if (coordinates.isEmpty()) { + break; + } - while (!coordinates.isEmpty() && !checkLocationSpace(loc, grid)) { loc = (PatchLocation) coordinates.remove(0); - } + Coordinate coordinate = loc.getCoordinate(); + PatchLocationContainer locationContainer = + new PatchLocationContainer(id, coordinate); + PatchCellContainer cellContainer = sim.cellFactory.createCellForPopulation(id, pop); - if (coordinates.isEmpty()) { - break; - } + Location location = locationContainer.convert(sim.locationFactory, cellContainer); + PatchCell cell = + (PatchCell) cellContainer.convert(sim.cellFactory, location, sim.random); - PatchLocationContainer locationContainer = new PatchLocationContainer(id, coordinate); - PatchCellContainer cellContainer = sim.cellFactory.createCellForPopulation(id, pop); - Location location = locationContainer.convert(sim.locationFactory, cellContainer); - PatchCell cell = - (PatchCell) cellContainer.convert(sim.cellFactory, location, sim.random); - grid.addObject(cell, location); - cell.schedule(sim.getSchedule()); + grid.addObject(cell, location); + cell.schedule(sim.getSchedule()); + } } } diff --git a/src/arcade/patch/parameter.patch.xml b/src/arcade/patch/parameter.patch.xml index e3143519d..4b2530311 100644 --- a/src/arcade/patch/parameter.patch.xml +++ b/src/arcade/patch/parameter.patch.xml @@ -122,7 +122,6 @@ - From 0c25a1d655b85e75c5950ab0a87e6c47cfc910f3 Mon Sep 17 00:00:00 2001 From: allison-li-1016 Date: Fri, 27 Jun 2025 20:53:26 +0000 Subject: [PATCH 30/36] refactoring methods such that Treat can now take multiple populations correctly --- .../patch/agent/action/PatchActionTreat.java | 105 ++++++++++-------- 1 file changed, 60 insertions(+), 45 deletions(-) diff --git a/src/arcade/patch/agent/action/PatchActionTreat.java b/src/arcade/patch/agent/action/PatchActionTreat.java index 8cce3a3dd..1b98a6a26 100644 --- a/src/arcade/patch/agent/action/PatchActionTreat.java +++ b/src/arcade/patch/agent/action/PatchActionTreat.java @@ -117,15 +117,11 @@ public void register(Simulation sim, String population) { * @param simstate the MASON simulation state */ public void step(SimState simstate) { - PatchSimulation sim = (PatchSimulation) simstate; String type = "null"; PatchGrid grid = (PatchGrid) sim.getGrid(); PatchComponentSites comp = (PatchComponentSites) sim.getComponent("SITES"); - ArrayList locs = sim.getLocations(); - ArrayList siteLocs = new ArrayList(); - // Determine type of sites component implemented. if (comp instanceof PatchComponentSitesSource) { type = "source"; @@ -135,7 +131,49 @@ public void step(SimState simstate) { type = "graph"; } - // Find sites without specified level of damage based on component type. + Set immuneCells = + Arrays.stream(Immune.values()).map(Enum::name).collect(Collectors.toSet()); + + for (MiniBox population : populations) { + String className = population.get("CLASS").toUpperCase(); + + if (!immuneCells.contains(className)) { + throw new IllegalArgumentException( + "Population " + + population.get("CLASS") + + " is not an immune cell and cannot be treated."); + } + + int pop = population.getInt("CODE"); + + ArrayList locs = sim.getLocations(); + ArrayList siteLocs = new ArrayList(); + + // Find sites without specified level of damage based on component type. + findLocations(comp, type, locs, siteLocs, sim); + Utilities.shuffleList(siteLocs, sim.random); + // sort locations in descending order from highest to lowest density + siteLocs.sort(Comparator.comparingInt(l -> -computeDensity(grid, l))); + insert(siteLocs, simstate, pop); + } + } + + /** + * Helper method to find possible locations to insert t cells. + * + * @param comp the component + * @param type the type of component (source, pattern, or graph) + * @param locs the locations to check + * @param siteLocs the locations that meet the criteria + * @param sim the simulation instance + * @throws IllegalArgumentException if the component type is invalid + */ + private void findLocations( + PatchComponentSites comp, + String type, + ArrayList locs, + ArrayList siteLocs, + PatchSimulation sim) { if (type.equals("graph")) { findGraphSites(comp, locs, siteLocs); } else if (type.equals("source") || type.equals("pattern")) { @@ -156,11 +194,6 @@ public void step(SimState simstate) { + type + ". Must be of type source, pattern, or graph."); } - - Utilities.shuffleList(siteLocs, sim.random); - // sort locations in descending order from highest to lowest density - siteLocs.sort(Comparator.comparingInt(l -> -computeDensity(grid, l))); - insert(siteLocs, simstate); } /** @@ -258,52 +291,34 @@ private int computeDensity(PatchGrid grid, Location loc) { * @param coordinates the locations to insert the cells * @param simstate the simulation state */ - private void insert(ArrayList coordinates, SimState simstate) { + private void insert(ArrayList coordinates, SimState simstate, int pop) { PatchSimulation sim = (PatchSimulation) simstate; PatchGrid grid = (PatchGrid) sim.getGrid(); Utilities.shuffleList(coordinates, sim.random); - Set immuneCells = - Arrays.stream(Immune.values()).map(Enum::name).collect(Collectors.toSet()); + for (int i = 0; i < dose; i++) { + int id = sim.getID(); - for (MiniBox population : populations) { - String className = population.get("CLASS").toUpperCase(); + PatchLocation loc = ((PatchLocation) coordinates.remove(0)); - if (!immuneCells.contains(className)) { - throw new IllegalArgumentException( - "Population " - + population.get("CLASS") - + " is not an immune cell and cannot be treated."); + while (!coordinates.isEmpty() && !checkLocationSpace(loc, grid)) { + loc = ((PatchLocation) coordinates.remove(0)); } - int pop = population.getInt("CODE"); - - for (int i = 0; i < dose; i++) { - int id = sim.getID(); - - PatchLocation loc = ((PatchLocation) coordinates.remove(0)); - - while (!coordinates.isEmpty() && !checkLocationSpace(loc, grid)) { - loc = ((PatchLocation) coordinates.remove(0)); - } - - if (coordinates.isEmpty()) { - break; - } + if (coordinates.isEmpty()) { + break; + } - loc = (PatchLocation) coordinates.remove(0); - Coordinate coordinate = loc.getCoordinate(); - PatchLocationContainer locationContainer = - new PatchLocationContainer(id, coordinate); - PatchCellContainer cellContainer = sim.cellFactory.createCellForPopulation(id, pop); + Coordinate coordinate = loc.getCoordinate(); + PatchLocationContainer locationContainer = new PatchLocationContainer(id, coordinate); + PatchCellContainer cellContainer = sim.cellFactory.createCellForPopulation(id, pop); - Location location = locationContainer.convert(sim.locationFactory, cellContainer); - PatchCell cell = - (PatchCell) cellContainer.convert(sim.cellFactory, location, sim.random); + Location location = locationContainer.convert(sim.locationFactory, cellContainer); + PatchCell cell = + (PatchCell) cellContainer.convert(sim.cellFactory, location, sim.random); - grid.addObject(cell, location); - cell.schedule(sim.getSchedule()); - } + grid.addObject(cell, location); + cell.schedule(sim.getSchedule()); } } From 0f57c6d598f2f5fd19235e744fad060735eb0d98 Mon Sep 17 00:00:00 2001 From: allison-li-1016 Date: Fri, 27 Jun 2025 21:46:17 +0000 Subject: [PATCH 31/36] I am now not hardcoding the subcoordinates --- src/arcade/patch/agent/action/PatchActionTreat.java | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/src/arcade/patch/agent/action/PatchActionTreat.java b/src/arcade/patch/agent/action/PatchActionTreat.java index 1b98a6a26..99e702c72 100644 --- a/src/arcade/patch/agent/action/PatchActionTreat.java +++ b/src/arcade/patch/agent/action/PatchActionTreat.java @@ -86,17 +86,10 @@ public PatchActionTreat(Series series, MiniBox parameters) { this.minDamageRadius = parameters.getDouble("MIN_RADIUS_SEED"); this.maxConfluency = parameters.getInt("MAX_DENSITY"); this.parameters = parameters; - this.coord = ((PatchSeries) series).patch.get("GEOMETRY").equalsIgnoreCase("HEX") ? "Hex" : "Rect"; - if (coord.equals("Hex")) { - latPositions = 9; - } - if (coord.equals("Rect")) { - latPositions = 16; - } populations = new ArrayList<>(); } @@ -122,6 +115,10 @@ public void step(SimState simstate) { PatchGrid grid = (PatchGrid) sim.getGrid(); PatchComponentSites comp = (PatchComponentSites) sim.getComponent("SITES"); + // Get a sample location to determine the number of subpositions + PatchLocation sampleLoc = (PatchLocation) sim.getAllLocations().iterator().next(); + latPositions = (int) (-3.5 * sampleLoc.getSubcoordinates().size() + 30); + // Determine type of sites component implemented. if (comp instanceof PatchComponentSitesSource) { type = "source"; From da6240a2a5a52ccaad7ec277f8f390109533dfeb Mon Sep 17 00:00:00 2001 From: allison-li-1016 Date: Mon, 30 Jun 2025 18:10:30 +0000 Subject: [PATCH 32/36] cleaning up tests --- .../agent/action/PatchActionTreatTest.java | 130 +++++++++++------- 1 file changed, 79 insertions(+), 51 deletions(-) diff --git a/test/arcade/patch/agent/action/PatchActionTreatTest.java b/test/arcade/patch/agent/action/PatchActionTreatTest.java index 1f459845b..125095d61 100644 --- a/test/arcade/patch/agent/action/PatchActionTreatTest.java +++ b/test/arcade/patch/agent/action/PatchActionTreatTest.java @@ -2,12 +2,17 @@ import java.lang.reflect.Field; import java.util.ArrayList; +import java.util.HashMap; +import java.util.Set; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import sim.engine.Schedule; import sim.util.Bag; import ec.util.MersenneTwisterFast; +import arcade.core.env.location.Location; import arcade.core.env.location.LocationContainer; +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; @@ -18,10 +23,14 @@ import arcade.patch.env.location.CoordinateXYZ; import arcade.patch.env.location.PatchLocation; import arcade.patch.env.location.PatchLocationContainer; +import arcade.patch.env.location.PatchLocationHex; import arcade.patch.sim.PatchSeries; import arcade.patch.sim.PatchSimulation; import arcade.patch.util.PatchEnums; -import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyDouble; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.*; public class PatchActionTreatTest { @@ -34,19 +43,23 @@ public class PatchActionTreatTest { private PatchCell cellMock; + private PatchLocation locationMock; + MiniBox parameters; PatchSeries series; @BeforeEach - public final void setUp() throws NoSuchFieldException, IllegalAccessException { + public final void setUp() + throws NoSuchFieldException, + SecurityException, + IllegalArgumentException, + IllegalAccessException { + // set up mocks parameters = mock(MiniBox.class); - when(parameters.getInt("TIME_DELAY")).thenReturn(10); - when(parameters.getInt("DOSE")).thenReturn(10); - when(parameters.getDouble("RATIO")).thenReturn(0.5); - when(parameters.getDouble("MAX_DAMAGE_SEED")).thenReturn(0.1); - when(parameters.getDouble("MIN_RADIUS_SEED")).thenReturn(0.01); - when(parameters.getDouble("T_CELL_VOL_AVG")).thenReturn(175.0); + when(parameters.getInt(anyString())).thenReturn(1); + when(parameters.getDouble(anyString())).thenReturn(1.0); + series = mock(PatchSeries.class); MiniBox patchMock = mock(MiniBox.class); series.patch = patchMock; @@ -72,6 +85,7 @@ public final void setUp() throws NoSuchFieldException, IllegalAccessException { PatchLocationContainer container = mock(PatchLocationContainer.class); PatchLocationContainer container2 = mock(PatchLocationContainer.class); PatchLocation loc = mock(PatchLocation.class); + locationMock = loc; PatchLocation loc2 = mock(PatchLocation.class); CoordinateXYZ coord = new CoordinateXYZ(0, 0, 0); CoordinateUVWZ c = new CoordinateUVWZ(0, 0, 0, 0); @@ -91,14 +105,15 @@ public final void setUp() throws NoSuchFieldException, IllegalAccessException { when(gridMock.getObjectsAtLocation(loc)).thenReturn(mockBag1); when(gridMock.getObjectsAtLocation(loc2)).thenReturn(mockBag2); when(sim.getLocations()).thenReturn(locations); + Set newLocations = new java.util.HashSet<>(); + newLocations.add(new PatchLocationHex(c2)); + when(sim.getAllLocations()).thenReturn(newLocations); sim.random = mock(MersenneTwisterFast.class); - PatchCellFactory factoryMock = mock(PatchCellFactory.class); PatchCellContainer patchCellContainerMock = mock(PatchCellContainer.class); when(factoryMock.createCellForPopulation(any(Integer.class), any(Integer.class))) .thenReturn(patchCellContainerMock); when(patchCellContainerMock.convert(any(), any(), any())).thenReturn(cellMock); - Field factory = PatchSimulation.class.getDeclaredField("cellFactory"); factory.setAccessible(true); factory.set(sim, factoryMock); @@ -115,31 +130,49 @@ final Bag createPatchCellsWithVolumeAndCriticalHeight(int n, double volume, doub return bag; } - @Test - public void schedule_called_schedulesOnAction() - throws NoSuchFieldException, IllegalAccessException { - action = new PatchActionTreat(series, parameters); + private static class TestPatchActionTreat extends PatchActionTreat { + + private final ArrayList testPopulations; + + public TestPatchActionTreat( + Series series, MiniBox parameters, ArrayList populations) { + super(series, parameters); + this.testPopulations = populations; + + HashMap populationsMap = new HashMap<>(); + for (MiniBox pop : populations) { + String className = pop.get("CLASS").toString(); + populationsMap.put(className, pop); + } + series.populations = populationsMap; + for (MiniBox pop : populations) { + Simulation mockSim = mock(Simulation.class); + when(mockSim.getSeries()).thenReturn(series); + String className = pop.get("CLASS").toString(); + register(mockSim, className); + } + } + } + @Test + public void schedule_called_schedulesOnAction() { ArrayList populations = new ArrayList<>(); MiniBox populationMock = mock(MiniBox.class); when(populationMock.getInt("CODE")).thenReturn(4); when(populationMock.get("CLASS")).thenReturn("cart_cd4"); populations.add(populationMock); - Field pops = PatchActionTreat.class.getDeclaredField("populations"); - pops.setAccessible(true); - pops.set(action, populations); + + action = new TestPatchActionTreat(series, parameters, populations); Schedule schedule = mock(Schedule.class); action.schedule(schedule); + verify(schedule) .scheduleOnce(anyDouble(), eq(PatchEnums.Ordering.ACTIONS.ordinal()), eq(action)); } @Test - public void step_called_addsObjectsToSim() throws NoSuchFieldException, IllegalAccessException { - when(parameters.getInt("MAX_DENSITY")).thenReturn(54); - action = new PatchActionTreat(series, parameters); - + public void step_called_addsObjectsToSim() { ArrayList populations = new ArrayList<>(); MiniBox populationMock1 = new MiniBox(); populationMock1.put("CLASS", "cart_cd4"); @@ -149,9 +182,8 @@ public void step_called_addsObjectsToSim() throws NoSuchFieldException, IllegalA populationMock2.put("CODE", 8); populations.add(populationMock1); populations.add(populationMock2); - Field pops = PatchActionTreat.class.getDeclaredField("populations"); - pops.setAccessible(true); - pops.set(action, populations); + + action = new TestPatchActionTreat(series, parameters, populations); action.step(sim); @@ -160,11 +192,8 @@ public void step_called_addsObjectsToSim() throws NoSuchFieldException, IllegalA } @Test - public void step_zeroDose_doesNotAddObjects() - throws NoSuchFieldException, IllegalAccessException { - when(parameters.getInt("MAX_DENSITY")).thenReturn(54); + public void step_zeroDose_doesNotAddObjects() { when(parameters.getInt("DOSE")).thenReturn(0); - action = new PatchActionTreat(series, parameters); ArrayList populations = new ArrayList<>(); MiniBox populationMock1 = new MiniBox(); @@ -175,59 +204,58 @@ public void step_zeroDose_doesNotAddObjects() populationMock2.put("CODE", 8); populations.add(populationMock1); populations.add(populationMock2); - Field pops = PatchActionTreat.class.getDeclaredField("populations"); - pops.setAccessible(true); - pops.set(action, populations); + + action = new TestPatchActionTreat(series, parameters, populations); action.step(sim); + verify(gridMock, times(0)).addObject(any(), any()); verify(cellMock, times(0)).schedule(any()); } @Test - public void checkLocationSpace_withEmptySpaces_returnsAvailable() - throws NoSuchFieldException, IllegalAccessException { + public void checkLocationSpace_withEmptySpaces_addsObject() { + when(parameters.getDouble("T_CELL_VOL_AVG")).thenReturn(175.0); when(parameters.getInt("MAX_DENSITY")).thenReturn(54); - action = new PatchActionTreat(series, parameters); + ArrayList populations = new ArrayList<>(); MiniBox populationMock = mock(MiniBox.class); when(populationMock.getInt("CODE")).thenReturn(4); when(populationMock.get("CLASS")).thenReturn("cart_cd4"); populations.add(populationMock); - Field pops = PatchActionTreat.class.getDeclaredField("populations"); - pops.setAccessible(true); - pops.set(action, populations); - - PatchLocation locationMock = mock(PatchLocation.class); when(locationMock.getArea()).thenReturn(3.0 / 2.0 / Math.sqrt(3.0) * 30 * 30); when(locationMock.getVolume()).thenReturn(3.0 / 2.0 / Math.sqrt(3.0) * 30 * 30 * 8.7); Bag testBag = createPatchCellsWithVolumeAndCriticalHeight(2, 10, 12.5); when(gridMock.getObjectsAtLocation(locationMock)).thenReturn(testBag); - boolean result = action.checkLocationSpace(locationMock, gridMock); - assertTrue(result); + + action = new TestPatchActionTreat(series, parameters, populations); + + action.step(sim); + + verify(gridMock, times(1)).addObject(any(), any()); + verify(cellMock, times(1)).schedule(any()); } @Test - public void checkLocation_maxConfluency_returnsUnavailable() - throws NoSuchFieldException, IllegalAccessException { + public void checkLocation_maxConfluency_doesNotAddObject() { + when(parameters.getDouble("T_CELL_VOL_AVG")).thenReturn(175.0); when(parameters.getInt("MAX_DENSITY")).thenReturn(1); - action = new PatchActionTreat(series, parameters); ArrayList populations = new ArrayList<>(); MiniBox populationMock = mock(MiniBox.class); when(populationMock.getInt("CODE")).thenReturn(4); when(populationMock.get("CLASS")).thenReturn("cart_cd4"); populations.add(populationMock); - Field pops = PatchActionTreat.class.getDeclaredField("populations"); - pops.setAccessible(true); - pops.set(action, populations); - - PatchLocation locationMock = mock(PatchLocation.class); when(locationMock.getArea()).thenReturn(3.0 / 2.0 / Math.sqrt(3.0) * 30 * 30); when(locationMock.getVolume()).thenReturn(3.0 / 2.0 / Math.sqrt(3.0) * 30 * 30 * 8.7); Bag testBag = createPatchCellsWithVolumeAndCriticalHeight(2, 10, 12.5); when(gridMock.getObjectsAtLocation(locationMock)).thenReturn(testBag); - boolean result = action.checkLocationSpace(locationMock, gridMock); - assertFalse(result); + + action = new TestPatchActionTreat(series, parameters, populations); + + action.step(sim); + + verify(gridMock, times(0)).addObject(any(), any()); + verify(cellMock, times(0)).schedule(any()); } } From db340a91bcc53f3fc5ca7e7fd93b089ab654de4a Mon Sep 17 00:00:00 2001 From: allison-li-1016 Date: Mon, 30 Jun 2025 18:14:48 +0000 Subject: [PATCH 33/36] cleaning up linting issues --- src/arcade/patch/agent/action/PatchActionTreat.java | 1 + src/arcade/patch/util/PatchEnums.java | 2 +- test/arcade/patch/agent/action/PatchActionTreatTest.java | 3 +-- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/arcade/patch/agent/action/PatchActionTreat.java b/src/arcade/patch/agent/action/PatchActionTreat.java index 99e702c72..113390074 100644 --- a/src/arcade/patch/agent/action/PatchActionTreat.java +++ b/src/arcade/patch/agent/action/PatchActionTreat.java @@ -287,6 +287,7 @@ private int computeDensity(PatchGrid grid, Location loc) { * * @param coordinates the locations to insert the cells * @param simstate the simulation state + * @param pop the population code for the cells */ private void insert(ArrayList coordinates, SimState simstate, int pop) { PatchSimulation sim = (PatchSimulation) simstate; diff --git a/src/arcade/patch/util/PatchEnums.java b/src/arcade/patch/util/PatchEnums.java index 66fe2e343..cd26917ad 100644 --- a/src/arcade/patch/util/PatchEnums.java +++ b/src/arcade/patch/util/PatchEnums.java @@ -209,7 +209,7 @@ public enum Immune { KILLER; /** - * Randomly selects a {@code Immune} cell type + * Randomly selects a {@code Immune} cell type. * * @param rng the random number generator * @return a random {@code Immune} cell type diff --git a/test/arcade/patch/agent/action/PatchActionTreatTest.java b/test/arcade/patch/agent/action/PatchActionTreatTest.java index 125095d61..ae38a3ea0 100644 --- a/test/arcade/patch/agent/action/PatchActionTreatTest.java +++ b/test/arcade/patch/agent/action/PatchActionTreatTest.java @@ -134,8 +134,7 @@ private static class TestPatchActionTreat extends PatchActionTreat { private final ArrayList testPopulations; - public TestPatchActionTreat( - Series series, MiniBox parameters, ArrayList populations) { + TestPatchActionTreat(Series series, MiniBox parameters, ArrayList populations) { super(series, parameters); this.testPopulations = populations; From 3e8d6c059f1613fc4475cfdfb9e09a10775d1dcd Mon Sep 17 00:00:00 2001 From: allison-li-1016 Date: Mon, 30 Jun 2025 19:05:49 +0000 Subject: [PATCH 34/36] correctly fetching max density from populations --- src/arcade/patch/agent/action/PatchActionTreat.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/arcade/patch/agent/action/PatchActionTreat.java b/src/arcade/patch/agent/action/PatchActionTreat.java index 113390074..c23430c27 100644 --- a/src/arcade/patch/agent/action/PatchActionTreat.java +++ b/src/arcade/patch/agent/action/PatchActionTreat.java @@ -71,7 +71,7 @@ public class PatchActionTreat implements Action { MiniBox parameters; /** Maximum confluency of cells in any location. */ - final int maxConfluency; + int maxConfluency; /** * Creates an {@code Action} to add agents after a delay. @@ -84,7 +84,6 @@ public PatchActionTreat(Series series, MiniBox parameters) { this.dose = parameters.getInt("DOSE"); this.maxDamage = parameters.getDouble("MAX_DAMAGE_SEED"); this.minDamageRadius = parameters.getDouble("MIN_RADIUS_SEED"); - this.maxConfluency = parameters.getInt("MAX_DENSITY"); this.parameters = parameters; this.coord = ((PatchSeries) series).patch.get("GEOMETRY").equalsIgnoreCase("HEX") @@ -133,6 +132,7 @@ public void step(SimState simstate) { for (MiniBox population : populations) { String className = population.get("CLASS").toUpperCase(); + maxConfluency = population.getInt("MAX_DENSITY"); if (!immuneCells.contains(className)) { throw new IllegalArgumentException( From 027fa7915e2b9f46cc64155e7bb79260d7f2201c Mon Sep 17 00:00:00 2001 From: allison-li-1016 Date: Mon, 30 Jun 2025 19:11:52 +0000 Subject: [PATCH 35/36] editing test to accomadate parameter change --- test/arcade/patch/agent/action/PatchActionTreatTest.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/test/arcade/patch/agent/action/PatchActionTreatTest.java b/test/arcade/patch/agent/action/PatchActionTreatTest.java index ae38a3ea0..75f91c629 100644 --- a/test/arcade/patch/agent/action/PatchActionTreatTest.java +++ b/test/arcade/patch/agent/action/PatchActionTreatTest.java @@ -215,12 +215,13 @@ public void step_zeroDose_doesNotAddObjects() { @Test public void checkLocationSpace_withEmptySpaces_addsObject() { when(parameters.getDouble("T_CELL_VOL_AVG")).thenReturn(175.0); - when(parameters.getInt("MAX_DENSITY")).thenReturn(54); ArrayList populations = new ArrayList<>(); MiniBox populationMock = mock(MiniBox.class); when(populationMock.getInt("CODE")).thenReturn(4); when(populationMock.get("CLASS")).thenReturn("cart_cd4"); + when(populationMock.getInt("MAX_DENSITY")).thenReturn(54); + when(populationMock.getDouble("T_CELL_VOL_AVG")).thenReturn(175.0); populations.add(populationMock); when(locationMock.getArea()).thenReturn(3.0 / 2.0 / Math.sqrt(3.0) * 30 * 30); when(locationMock.getVolume()).thenReturn(3.0 / 2.0 / Math.sqrt(3.0) * 30 * 30 * 8.7); @@ -238,12 +239,12 @@ public void checkLocationSpace_withEmptySpaces_addsObject() { @Test public void checkLocation_maxConfluency_doesNotAddObject() { when(parameters.getDouble("T_CELL_VOL_AVG")).thenReturn(175.0); - when(parameters.getInt("MAX_DENSITY")).thenReturn(1); ArrayList populations = new ArrayList<>(); MiniBox populationMock = mock(MiniBox.class); when(populationMock.getInt("CODE")).thenReturn(4); when(populationMock.get("CLASS")).thenReturn("cart_cd4"); + when(populationMock.getInt("MAX_DENSITY")).thenReturn(1); populations.add(populationMock); when(locationMock.getArea()).thenReturn(3.0 / 2.0 / Math.sqrt(3.0) * 30 * 30); when(locationMock.getVolume()).thenReturn(3.0 / 2.0 / Math.sqrt(3.0) * 30 * 30 * 8.7); From 6ae5839fc145e0cd0902bd355d742942646decd0 Mon Sep 17 00:00:00 2001 From: allison-li-1016 Date: Wed, 2 Jul 2025 18:33:00 +0000 Subject: [PATCH 36/36] revising locations list, making variable final --- .../patch/agent/action/PatchActionTreat.java | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/src/arcade/patch/agent/action/PatchActionTreat.java b/src/arcade/patch/agent/action/PatchActionTreat.java index c23430c27..fc418a77c 100644 --- a/src/arcade/patch/agent/action/PatchActionTreat.java +++ b/src/arcade/patch/agent/action/PatchActionTreat.java @@ -39,7 +39,7 @@ import arcade.patch.util.PatchEnums.Ordering; /** - * Implementation of {@link Action} for inserting T cell agents. + * Implementation of {@link Action} for inserting T-cell agents. * *

The action is stepped once after {@code TIME_DELAY}. The {@code TreatAction} will add CAR * T-cell agents of specified dose next to source points or vasculature. @@ -53,9 +53,9 @@ public class PatchActionTreat implements Action { private final int dose; /** Maximum damage value at which T-cells can spawn next to in source or pattern source. */ - private double maxDamage; + private final double maxDamage; - /** Minimum radius value at which T- cells can spawn next to in graph source. */ + /** Minimum radius value at which T-cells can spawn next to in graph source. */ private final double minDamageRadius; /** Number of agent positions per lattice site. */ @@ -132,7 +132,6 @@ public void step(SimState simstate) { for (MiniBox population : populations) { String className = population.get("CLASS").toUpperCase(); - maxConfluency = population.getInt("MAX_DENSITY"); if (!immuneCells.contains(className)) { throw new IllegalArgumentException( @@ -141,6 +140,8 @@ public void step(SimState simstate) { + " is not an immune cell and cannot be treated."); } + maxConfluency = population.getInt("MAX_DENSITY"); + int pop = population.getInt("CODE"); ArrayList locs = sim.getLocations(); @@ -156,7 +157,7 @@ public void step(SimState simstate) { } /** - * Helper method to find possible locations to insert t cells. + * Helper method to find possible locations to insert T-cells. * * @param comp the component * @param type the type of component (source, pattern, or graph) @@ -208,6 +209,11 @@ private void findGraphSites( Bag allEdges = new Bag(graph.getAllEdges()); PatchComponentSitesGraph graphSites = (PatchComponentSitesGraph) comp; + Set coordinateSet = + locs.stream() + .map(container -> ((PatchLocationContainer) container).coordinate) + .collect(Collectors.toSet()); + for (Object edgeObj : allEdges) { SiteEdge edge = (SiteEdge) edgeObj; Bag allEdgeLocs = new Bag(); @@ -223,7 +229,7 @@ private void findGraphSites( for (Object locObj : allEdgeLocs) { Location loc = (Location) locObj; - if (locs.contains(loc)) { + if (coordinateSet.contains(((PatchLocation) loc).getCoordinate())) { if (edge.getRadius() >= minDamageRadius) { for (int p = 0; p < latPositions; p++) { siteLocs.add(loc);