Skip to content
Open
Show file tree
Hide file tree
Changes from 14 commits
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
5e6d5eb
adding initial classes
allison-li-1016 Apr 21, 2025
99fabb5
adding treat action parameters
allison-li-1016 Apr 21, 2025
0d1c6b6
linting, removing unnecessary param
allison-li-1016 Apr 22, 2025
3bb4786
renaming silly var names
allison-li-1016 Apr 22, 2025
f354383
adding option for default
allison-li-1016 Apr 22, 2025
9bf8920
renaming variable so it does not literally match the field name
allison-li-1016 Apr 22, 2025
92c6151
adding javadoc comments
allison-li-1016 Apr 22, 2025
b5e75ec
editing javadoc of the class to be for Treat rather than remove
allison-li-1016 Apr 22, 2025
e72f787
I think this was causing issues?
allison-li-1016 Apr 22, 2025
37357f2
if/else statement silliness
allison-li-1016 Apr 22, 2025
34a1d64
addressing linter comments in test class
allison-li-1016 Apr 22, 2025
3404f20
BRUH the indvidiual import statements
allison-li-1016 Apr 22, 2025
da46f7e
removing frivolous method
allison-li-1016 Apr 23, 2025
f80d49d
removing frivolous comment
allison-li-1016 Apr 23, 2025
f870fac
Merge branch 'main' into allison/carcade-refactor-treat
allison-li-1016 Jun 3, 2025
b8caa60
removing switch statement to if/else
allison-li-1016 Jun 3, 2025
acb5965
we have a parameter for max confluency
allison-li-1016 Jun 3, 2025
8e31666
fixing tests
allison-li-1016 Jun 3, 2025
cbdae06
changing test to use parameter
allison-li-1016 Jun 3, 2025
66287ca
removing the need for 4 lists
allison-li-1016 Jun 3, 2025
4fc613c
adding options to treatment
allison-li-1016 Jun 4, 2025
77a713c
refactor code into helper methods
allison-li-1016 Jun 9, 2025
2b8607b
now the action can actually be run on input files
allison-li-1016 Jun 12, 2025
fb816ef
not hardcoding cell codes
allison-li-1016 Jun 12, 2025
996c004
add case for edge case
allison-li-1016 Jun 12, 2025
f774218
add exception for illegal cell types
allison-li-1016 Jun 12, 2025
190d1b2
reverting exception
allison-li-1016 Jun 12, 2025
f4ea633
fixing tests, adding edgecase checking
allison-li-1016 Jun 12, 2025
5063ced
the tests should be working now
allison-li-1016 Jun 12, 2025
0c12f03
removing ratio option so that treat is more similar to insert
allison-li-1016 Jun 27, 2025
0c25a1d
refactoring methods such that Treat can now take multiple populations…
allison-li-1016 Jun 27, 2025
0f57c6d
I am now not hardcoding the subcoordinates
allison-li-1016 Jun 27, 2025
da6240a
cleaning up tests
allison-li-1016 Jun 30, 2025
db340a9
cleaning up linting issues
allison-li-1016 Jun 30, 2025
3e8d6c0
correctly fetching max density from populations
allison-li-1016 Jun 30, 2025
027fa79
editing test to accomadate parameter change
allison-li-1016 Jun 30, 2025
128d66f
Merge remote-tracking branch 'origin/main' into allison/carcade-refac…
allison-li-1016 Jul 1, 2025
6ae5839
revising locations list, making variable final
allison-li-1016 Jul 2, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
370 changes: 370 additions & 0 deletions src/arcade/patch/agent/action/PatchActionTreat.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,370 @@
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 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 and ratio 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;

/** 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;

/** 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. */
final 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.treatFrac = parameters.getDouble("RATIO");
this.maxDamage = parameters.getDouble("MAX_DAMAGE_SEED");
this.minDamageRadius = 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;
}

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) {

PatchSimulation sim = (PatchSimulation) simstate;
String type = "null";
Copy link
Member

Choose a reason for hiding this comment

The 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");

ArrayList<LocationContainer> locs = sim.getLocations();

ArrayList<Location> siteLocs0 = new ArrayList<Location>();
ArrayList<Location> siteLocs1 = new ArrayList<Location>();
ArrayList<Location> siteLocs2 = new ArrayList<Location>();
ArrayList<Location> siteLocs3 = new ArrayList<Location>();
ArrayList<Location> siteLocs = new ArrayList<Location>();

// Determine type of sites component implemented.
if (comp instanceof PatchComponentSitesSource) {
Copy link
Member

Choose a reason for hiding this comment

The 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";
}

// 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 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);
}
}
}
}
break;

default:
throw new IllegalArgumentException(
"Invalid component type: "
+ type
+ ". 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);
insert(siteLocs, simstate);
}

/**
* 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<Location> siteLocs0,
ArrayList<Location> siteLocs1,
ArrayList<Location> siteLocs2,
ArrayList<Location> 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;
}

/**
* 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<Location> 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 coordinate = loc.getCoordinate();

while (!coordinates.isEmpty() && !checkLocationSpace(loc, grid)) {
loc = (PatchLocation) coordinates.remove(0);
}

if (coordinates.isEmpty()) {
break;
}

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) {
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;
}
}
7 changes: 7 additions & 0 deletions src/arcade/patch/parameter.patch.xml
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,13 @@
<!-- convert action parameters -->
<action class="convert" id="TIME_DELAY" value="0" units="min" description="time delay before calling the action" />

<!-- treat action parameters -->
<action class="treat" id="TIME_DELAY" value="0" units="hour" description="time delay before calling the action" />
<action class="treat" id="DOSE" value="0" description="number of CAR-T cells to add into the treatment" />
<action class="treat" id="RATIO" value="1" description="ratio of cd4 to cd8 cells in the dose" />
<action class="treat" id="MAX_DAMAGE_SEED" value="1E7" description="maximum damage value at which T-cells can seed next to in source or pattern source" />
<action class="treat" id="MIN_RADIUS_SEED" value="2.0" description="minimum radius value at which T-cells can seed next to in graph source" />

<!-- COMPONENTS ======================================================== -->

<!-- source sites component parameters -->
Expand Down
Loading
Loading