-
Notifications
You must be signed in to change notification settings - Fork 3
CARCADE Refactor 3: Treat Action (+params and tests) #175
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
5e6d5eb
99fabb5
0d1c6b6
3bb4786
f354383
9bf8920
92c6151
b5e75ec
e72f787
37357f2
34a1d64
3404f20
da46f7e
f80d49d
f870fac
b8caa60
acb5965
8e31666
cbdae06
66287ca
4fc613c
77a713c
2b8607b
fb816ef
996c004
f774218
190d1b2
f4ea633
5063ced
0c12f03
0c25a1d
0f57c6d
da6240a
db340a9
3e8d6c0
027fa79
128d66f
6ae5839
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,378 @@ | ||
| 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; | ||
| 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.Immune; | ||
| import arcade.patch.util.PatchEnums.Ordering; | ||
|
|
||
| /** | ||
| * Implementation of {@link Action} for inserting T-cell agents. | ||
| * | ||
| * <p>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. | ||
| */ | ||
| public class PatchActionTreat implements Action { | ||
|
|
||
| /** Delay before calling the helper (in minutes). */ | ||
| private final int delay; | ||
|
|
||
| /** Total number of CAR T-cells to treat with. */ | ||
| private final int dose; | ||
|
|
||
| /** Maximum damage value at which T-cells can spawn next to in source or pattern source. */ | ||
| private final double maxDamage; | ||
|
|
||
| /** 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. */ | ||
| private int latPositions; | ||
|
|
||
| /** Coordinate system used for simulation. */ | ||
| private final String coord; | ||
|
|
||
| /** List of populations. */ | ||
| private final ArrayList<MiniBox> populations; | ||
|
|
||
| /** parameters. */ | ||
| MiniBox parameters; | ||
|
|
||
| /** Maximum confluency of cells in any location. */ | ||
| int maxConfluency; | ||
|
|
||
| /** | ||
| * 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) { | ||
| this.delay = parameters.getInt("TIME_DELAY"); | ||
| this.dose = parameters.getInt("DOSE"); | ||
| this.maxDamage = parameters.getDouble("MAX_DAMAGE_SEED"); | ||
| this.minDamageRadius = parameters.getDouble("MIN_RADIUS_SEED"); | ||
| this.parameters = parameters; | ||
| this.coord = | ||
| ((PatchSeries) series).patch.get("GEOMETRY").equalsIgnoreCase("HEX") | ||
| ? "Hex" | ||
| : "Rect"; | ||
|
|
||
| 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 action to insert cells of the treatment population(s). | ||
| * | ||
| * @param simstate the MASON simulation state | ||
| */ | ||
| public void step(SimState simstate) { | ||
allison-li-1016 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| PatchSimulation sim = (PatchSimulation) simstate; | ||
| String type = "null"; | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I would strongly prefer if you didn't do this. |
||
| 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) { | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. this should all happen at the abstraction level of PatchComponentSites |
||
| type = "source"; | ||
| } else if (comp instanceof PatchComponentSitesPattern) { | ||
| type = "pattern"; | ||
| } else if (comp instanceof PatchComponentSitesGraph) { | ||
| type = "graph"; | ||
| } | ||
|
|
||
| Set<String> 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."); | ||
| } | ||
|
|
||
| maxConfluency = population.getInt("MAX_DENSITY"); | ||
|
|
||
| int pop = population.getInt("CODE"); | ||
|
|
||
| ArrayList<LocationContainer> locs = sim.getLocations(); | ||
| ArrayList<Location> siteLocs = new ArrayList<Location>(); | ||
|
|
||
| // 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<LocationContainer> locs, | ||
| ArrayList<Location> siteLocs, | ||
| PatchSimulation sim) { | ||
| if (type.equals("graph")) { | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Would it be better to use switch cases here?
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. switch + enums please |
||
| findGraphSites(comp, locs, siteLocs); | ||
| } 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(); | ||
| } | ||
| pruneSite(locs, sim, damage, sitesLat, siteLocs); | ||
| } else { | ||
| throw new IllegalArgumentException( | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. using an enum means that you don't need this. |
||
| "Invalid component type: " | ||
| + type | ||
| + ". Must be of type source, pattern, or graph."); | ||
| } | ||
| } | ||
|
|
||
| /** | ||
| * 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<LocationContainer> locs, | ||
| ArrayList<Location> siteLocs) { | ||
| Graph graph = ((PatchComponentSitesGraph) comp).getGraph(); | ||
| Bag allEdges = new Bag(graph.getAllEdges()); | ||
| PatchComponentSitesGraph graphSites = (PatchComponentSitesGraph) comp; | ||
|
|
||
| Set<Coordinate> coordinateSet = | ||
| locs.stream() | ||
| .map(container -> ((PatchLocationContainer) container).coordinate) | ||
| .collect(Collectors.toSet()); | ||
|
|
||
| for (Object edgeObj : allEdges) { | ||
| SiteEdge edge = (SiteEdge) edgeObj; | ||
| Bag allEdgeLocs = new Bag(); | ||
| if (Objects.equals(coord, "Hex")) { | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. why not just graphSites.getSpan() rather than casting and specifying the type of the graphsites? |
||
| 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 (coordinateSet.contains(((PatchLocation) loc).getCoordinate())) { | ||
| 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<LocationContainer> locs, | ||
| PatchSimulation sim, | ||
| double[][][] damage, | ||
| boolean[][][] sitesLat, | ||
| ArrayList<Location> 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. | ||
| * | ||
| * @param grid the simulation grid | ||
| * @param loc the current location being looked | ||
| * @return the density of agents at the location | ||
| */ | ||
| private int computeDensity(PatchGrid grid, Location loc) { | ||
| Bag bag = new Bag(grid.getObjectsAtLocation(loc)); | ||
| int numAgents = bag.numObjs; | ||
| return numAgents; | ||
| } | ||
|
|
||
| /** | ||
| * Helper method to add cells into the grid. | ||
| * | ||
| * @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<Location> coordinates, SimState simstate, int pop) { | ||
| PatchSimulation sim = (PatchSimulation) simstate; | ||
| PatchGrid grid = (PatchGrid) sim.getGrid(); | ||
| Utilities.shuffleList(coordinates, sim.random); | ||
|
|
||
| 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; | ||
| } | ||
|
|
||
| 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); | ||
|
|
||
| grid.addObject(cell, location); | ||
| cell.schedule(sim.getSchedule()); | ||
| } | ||
| } | ||
|
|
||
| /** | ||
| * 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) { | ||
allison-li-1016 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| 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) { | ||
| // no cells in location | ||
| available = true; | ||
| } else if (n >= locMax) { | ||
| // location already full | ||
| available = false; | ||
| } 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; | ||
| } | ||
| } | ||
Uh oh!
There was an error while loading. Please reload this page.