diff --git a/src/arcade/patch/PatchARCADE.java b/src/arcade/patch/PatchARCADE.java index 115823a6b..1759df94b 100644 --- a/src/arcade/patch/PatchARCADE.java +++ b/src/arcade/patch/PatchARCADE.java @@ -34,6 +34,7 @@ public OutputSaver getSaver(Series series) { PatchOutputSaver saver = new PatchOutputSaver(series); saver.saveGraph = settings.contains("SAVE_GRAPH"); saver.saveLattice = settings.contains("SAVE_LAYERS"); + saver.saveEvents = settings.contains("SAVE_EVENTS"); return saver; } } diff --git a/src/arcade/patch/agent/module/PatchModuleCytotoxicity.java b/src/arcade/patch/agent/module/PatchModuleCytotoxicity.java index f302193a2..35ddf5cf0 100644 --- a/src/arcade/patch/agent/module/PatchModuleCytotoxicity.java +++ b/src/arcade/patch/agent/module/PatchModuleCytotoxicity.java @@ -1,5 +1,7 @@ package arcade.patch.agent.module; +import java.util.HashMap; +import java.util.Map; import ec.util.MersenneTwisterFast; import arcade.core.sim.Simulation; import arcade.core.util.Parameters; @@ -7,6 +9,8 @@ import arcade.patch.agent.cell.PatchCellCART; import arcade.patch.agent.cell.PatchCellTissue; import arcade.patch.agent.process.PatchProcessInflammation; +import arcade.patch.env.location.PatchLocation; +import arcade.patch.sim.PatchSimulation; import static arcade.patch.util.PatchEnums.Domain; import static arcade.patch.util.PatchEnums.State; @@ -68,6 +72,18 @@ public void step(MersenneTwisterFast random, Simulation sim) { tissueCell.setState(State.APOPTOTIC); granzyme--; inflammation.setInternal("granzyme", granzyme); + + // Log cytotoxicity event + PatchSimulation patchSim = (PatchSimulation) sim; + Map eventData = new HashMap<>(); + eventData.put("t-cell-id", cell.getID()); + eventData.put("tissue-cell-id", target.getID()); + eventData.put("tissue-cell-type", target.getPop()); + eventData.put("type", "lysis"); + eventData.put("timestamp", (int) ((PatchSimulation) sim).getSchedule().getTime()); + eventData.put( + "tissue-location", ((PatchLocation) target.getLocation()).getCoordinate()); + patchSim.logEvent(eventData); } } diff --git a/src/arcade/patch/command.patch.xml b/src/arcade/patch/command.patch.xml index 4902b6289..2fda14ff3 100644 --- a/src/arcade/patch/command.patch.xml +++ b/src/arcade/patch/command.patch.xml @@ -1,4 +1,5 @@ + diff --git a/src/arcade/patch/sim/PatchSimulation.java b/src/arcade/patch/sim/PatchSimulation.java index a98b60158..0881dbd7f 100644 --- a/src/arcade/patch/sim/PatchSimulation.java +++ b/src/arcade/patch/sim/PatchSimulation.java @@ -4,6 +4,8 @@ import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; +import java.util.List; +import java.util.Map; import java.util.Set; import com.google.gson.reflect.TypeToken; import sim.engine.Schedule; @@ -55,6 +57,9 @@ public abstract class PatchSimulation extends SimState implements Simulation { /** Cell ID tracker. */ int id; + /** List to store events throughout the simulation. */ + private final List> events; + /** Location factory instance for the simulation. */ public final PatchLocationFactory locationFactory; @@ -81,6 +86,7 @@ public PatchSimulation(long seed, Series series) { this.locationFactory = makeLocationFactory(); this.cellFactory = makeCellFactory(); this.latticeFactory = makeLatticeFactory(); + this.events = new ArrayList<>(); } @Override @@ -214,6 +220,24 @@ public Set getAllLocations() { return locations; } + /** + * Log an event to the simulation's event list. + * + * @param event logging information corresponding to the event + */ + public void logEvent(Map event) { + events.add(event); + } + + /** + * Get the list of events logged during the simulation. + * + * @return the list of events + */ + public List> getEvents() { + return new ArrayList<>(events); + } + /** * Called at the start of the simulation to set up agents and environment and schedule actions * and components as needed. diff --git a/src/arcade/patch/sim/output/PatchOutputSaver.java b/src/arcade/patch/sim/output/PatchOutputSaver.java index 0a3447d8f..5cab846c7 100644 --- a/src/arcade/patch/sim/output/PatchOutputSaver.java +++ b/src/arcade/patch/sim/output/PatchOutputSaver.java @@ -1,5 +1,7 @@ package arcade.patch.sim.output; +import java.util.List; +import java.util.Map; import com.google.gson.Gson; import arcade.core.env.component.Component; import arcade.core.sim.Series; @@ -17,6 +19,9 @@ public final class PatchOutputSaver extends OutputSaver { /** {@code true} to save lattices, {@code false} otherwise. */ public boolean saveLattice; + /** {@code true} to save events, {@code false} otherwise. */ + public boolean saveEvents; + /** * Creates an {@code PatchOutputSaver} for the series. * @@ -61,6 +66,22 @@ public void saveLayers(int tick) { write(patch, format(json, FORMAT_ELEMENTS)); } + /** + * Save the collection of events to a JSON. + * + * @param tick the simulation tick + */ + public void saveEventsToFile(int tick) { + if (saveEvents) { + List> events = ((PatchSimulation) sim).getEvents(); + if (!events.isEmpty()) { + String json = gson.toJson(events); + String path = prefix + String.format("_%06d.EVENTS.json", tick); + write(path, format(json, FORMAT_ELEMENTS)); + } + } + } + @Override public void save(int tick) { super.save(tick); @@ -70,5 +91,8 @@ public void save(int tick) { if (saveLattice) { saveLayers(tick); } + if (saveEvents) { + saveEventsToFile(tick); + } } } diff --git a/test/arcade/patch/agent/module/PatchModuleCytotoxicityTest.java b/test/arcade/patch/agent/module/PatchModuleCytotoxicityTest.java index 352bd47fd..ea0e563e0 100644 --- a/test/arcade/patch/agent/module/PatchModuleCytotoxicityTest.java +++ b/test/arcade/patch/agent/module/PatchModuleCytotoxicityTest.java @@ -3,11 +3,14 @@ import java.lang.reflect.Field; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import sim.engine.Schedule; import ec.util.MersenneTwisterFast; import arcade.core.util.Parameters; import arcade.patch.agent.cell.PatchCellCART; import arcade.patch.agent.cell.PatchCellTissue; import arcade.patch.agent.process.PatchProcessInflammation; +import arcade.patch.env.location.Coordinate; +import arcade.patch.env.location.PatchLocation; import arcade.patch.sim.PatchSimulation; import arcade.patch.util.PatchEnums.State; import static org.mockito.Mockito.*; @@ -24,6 +27,10 @@ public class PatchModuleCytotoxicityTest { private PatchSimulation sim; + private PatchLocation mockLocation; + + private Schedule mockSchedule; + private MersenneTwisterFast randomMock; @BeforeEach @@ -31,6 +38,9 @@ public final void setUp() { mockCell = mock(PatchCellCART.class); mockTarget = mock(PatchCellTissue.class); mockInflammation = mock(PatchProcessInflammation.class); + mockLocation = mock(PatchLocation.class); + mockSchedule = mock(Schedule.class); + sim = mock(PatchSimulation.class); randomMock = mock(MersenneTwisterFast.class); Parameters parameters = mock(Parameters.class); @@ -41,6 +51,14 @@ public final void setUp() { when(mockCell.getProcess(any())).thenReturn(mockInflammation); when(mockInflammation.getInternal("granzyme")).thenReturn(1.0); when(mockCell.getBoundTarget()).thenReturn(mockTarget); + when(mockCell.getID()).thenReturn(1); + when(mockTarget.getID()).thenReturn(1); + when(mockTarget.getPop()).thenReturn(1); + when(mockTarget.getLocation()).thenReturn(mockLocation); + + when(mockLocation.getCoordinate()).thenReturn(mock(Coordinate.class)); + when(sim.getSchedule()).thenReturn(mockSchedule); + when(mockSchedule.getTime()).thenReturn(0.0); action = new PatchModuleCytotoxicity(mockCell); }