diff --git a/build.gradle b/build.gradle
index 2e3a6e31b..21da1d2ca 100644
--- a/build.gradle
+++ b/build.gradle
@@ -163,6 +163,8 @@ task updateVersion (group: "versioning", description: "Syncs gradle version with
}
}
+copyJar.mustRunAfter spotlessMisc, spotlessJava, compileTestJava, test, jacocoTestReport
+
build.dependsOn copyJar
test.finalizedBy jacocoTestReport
diff --git a/input/1.xml b/input/1.xml
new file mode 100644
index 000000000..c47975017
--- /dev/null
+++ b/input/1.xml
@@ -0,0 +1,42 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/setup/1.xml b/setup/1.xml
new file mode 100644
index 000000000..ca9ab286c
--- /dev/null
+++ b/setup/1.xml
@@ -0,0 +1,42 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/arcade/potts/PottsARCADE.java b/src/arcade/potts/PottsARCADE.java
index f3a552e9e..ac685a4ee 100644
--- a/src/arcade/potts/PottsARCADE.java
+++ b/src/arcade/potts/PottsARCADE.java
@@ -31,6 +31,8 @@ public OutputLoader getLoader(Series series) {
@Override
public OutputSaver getSaver(Series series) {
- return new PottsOutputSaver(series);
+ PottsOutputSaver saver = new PottsOutputSaver(series);
+ saver.saveProspero = settings.contains("SAVE_PROSPERO");
+ return saver;
}
}
diff --git a/src/arcade/potts/agent/cell/PottsCellContainer.java b/src/arcade/potts/agent/cell/PottsCellContainer.java
index 10e3f170b..4567d6cc9 100644
--- a/src/arcade/potts/agent/cell/PottsCellContainer.java
+++ b/src/arcade/potts/agent/cell/PottsCellContainer.java
@@ -105,6 +105,8 @@ public PottsCellContainer(
/**
* Creates a {@code PottsCellContainer} instance.
*
+ *
The container does not have prospero.
+ *
* @param id the cell ID
* @param parent the parent ID
* @param pop the cell population index
diff --git a/src/arcade/potts/agent/cell/PottsCellFly.java b/src/arcade/potts/agent/cell/PottsCellFly.java
new file mode 100644
index 000000000..c792aef7d
--- /dev/null
+++ b/src/arcade/potts/agent/cell/PottsCellFly.java
@@ -0,0 +1,42 @@
+package arcade.potts.agent.cell;
+
+import arcade.core.env.location.Location;
+import arcade.core.util.GrabBag;
+import arcade.core.util.Parameters;
+
+/**
+ * Implementation of {@link PottsCell} for Potts Fly models.
+ *
+ *
Cells follow {@link PottsCell} rules, but additionally keep track of the amount of prospero.
+ *
+ *
[TODO: fix class comment]
+ */
+public abstract class PottsCellFly extends PottsCell {
+
+ /** Amount of prospero in cell. */
+ private double prospero;
+
+ public PottsCellFly(
+ PottsCellContainer container, Location location, Parameters parameters, GrabBag links) {
+ super(container, location, parameters, links);
+ this.prospero = 0;
+ }
+
+ /**
+ * Gets the amount of prospero in the cell.
+ *
+ * @return the amount of prospero in the cell.
+ */
+ public double getProspero() {
+ return prospero;
+ }
+
+ /**
+ * Sets the amount of prospero for the cell.
+ *
+ * @param prospero the amount of prospero in the cell.
+ */
+ public void setProspero(double prospero) {
+ this.prospero = prospero;
+ }
+}
diff --git a/src/arcade/potts/agent/cell/PottsCellFlyGMC.java b/src/arcade/potts/agent/cell/PottsCellFlyGMC.java
index f88ad4fc2..28c3f7a67 100644
--- a/src/arcade/potts/agent/cell/PottsCellFlyGMC.java
+++ b/src/arcade/potts/agent/cell/PottsCellFlyGMC.java
@@ -16,7 +16,7 @@
* PottsModuleProliferationVolumeBasedDivision} module. The basal apoptosis rate of this cell should
* be set to 0 in the setup file.
*/
-public class PottsCellFlyGMC extends PottsCell {
+public class PottsCellFlyGMC extends PottsCellFly {
/**
* Creates a fly GMC {@code PottsCell} agent.
diff --git a/src/arcade/potts/agent/cell/PottsCellFlyNeuron.java b/src/arcade/potts/agent/cell/PottsCellFlyNeuron.java
index ac1187aaf..90ac50de4 100644
--- a/src/arcade/potts/agent/cell/PottsCellFlyNeuron.java
+++ b/src/arcade/potts/agent/cell/PottsCellFlyNeuron.java
@@ -9,7 +9,7 @@
import static arcade.potts.util.PottsEnums.State;
/** Represents a fly neuron cell in the Potts model. This cell is quiescent. */
-public final class PottsCellFlyNeuron extends PottsCell {
+public final class PottsCellFlyNeuron extends PottsCellFly {
/**
* Creates a {@code PottsCellFlyNeuron} {@code PottsCell} agent.
diff --git a/src/arcade/potts/agent/cell/PottsCellFlyStem.java b/src/arcade/potts/agent/cell/PottsCellFlyStem.java
index 410e0c6e3..a10f64a95 100644
--- a/src/arcade/potts/agent/cell/PottsCellFlyStem.java
+++ b/src/arcade/potts/agent/cell/PottsCellFlyStem.java
@@ -11,7 +11,7 @@
import arcade.potts.util.PottsEnums.Phase;
import static arcade.potts.util.PottsEnums.State;
-public class PottsCellFlyStem extends PottsCell {
+public class PottsCellFlyStem extends PottsCellFly {
/** Enum outlining parameters for each cell type. */
public enum StemType {
/** Wild type stem cell. */
diff --git a/src/arcade/potts/agent/module/PottsModuleFlyGMCDifferentiation.java b/src/arcade/potts/agent/module/PottsModuleFlyGMCDifferentiation.java
index 0bfff0219..1ff9c237b 100644
--- a/src/arcade/potts/agent/module/PottsModuleFlyGMCDifferentiation.java
+++ b/src/arcade/potts/agent/module/PottsModuleFlyGMCDifferentiation.java
@@ -4,10 +4,7 @@
import arcade.core.agent.cell.CellContainer;
import arcade.core.env.location.Location;
import arcade.core.sim.Simulation;
-import arcade.potts.agent.cell.PottsCell;
-import arcade.potts.agent.cell.PottsCellContainer;
-import arcade.potts.agent.cell.PottsCellFlyGMC;
-import arcade.potts.agent.cell.PottsCellFlyNeuron;
+import arcade.potts.agent.cell.*;
import arcade.potts.env.location.PottsLocation2D;
import arcade.potts.sim.Potts;
import arcade.potts.sim.PottsSimulation;
@@ -22,6 +19,9 @@ public class PottsModuleFlyGMCDifferentiation extends PottsModuleProliferationVo
Boolean pdeLike;
+ /* Rate of Prospero degradation (ticks^-1). */
+ final double prosperoDegradationRate;
+
/**
* Creates a fly GMC proliferation module.
*
@@ -30,6 +30,21 @@ public class PottsModuleFlyGMCDifferentiation extends PottsModuleProliferationVo
public PottsModuleFlyGMCDifferentiation(PottsCellFlyGMC cell) {
super(cell);
pdeLike = (cell.getParameters().getInt("proliferation/PDELIKE") != 0);
+ prosperoDegradationRate =
+ cell.getParameters().getDouble("proliferation/PROSPERO_DEGRADATION_RATE");
+ if (prosperoDegradationRate < 0) {
+ throw new IllegalArgumentException("Prospero degradation rate should not be negative");
+ }
+ }
+
+ @Override
+ public void step(MersenneTwisterFast random, Simulation sim) {
+ super.step(random, sim);
+ ((PottsCellFly) cell)
+ .setProspero(
+ Math.max(0, ((PottsCellFly) cell).getProspero() - prosperoDegradationRate));
+ System.out.println(
+ "GMC ID " + cell.getID() + " prospero: " + ((PottsCellFly) cell).getProspero());
}
/**
diff --git a/src/arcade/potts/agent/module/PottsModuleFlyStemProliferation.java b/src/arcade/potts/agent/module/PottsModuleFlyStemProliferation.java
index a37fa386d..1ed446108 100644
--- a/src/arcade/potts/agent/module/PottsModuleFlyStemProliferation.java
+++ b/src/arcade/potts/agent/module/PottsModuleFlyStemProliferation.java
@@ -16,6 +16,7 @@
import arcade.core.util.distributions.UniformDistribution;
import arcade.potts.agent.cell.PottsCell;
import arcade.potts.agent.cell.PottsCellContainer;
+import arcade.potts.agent.cell.PottsCellFly;
import arcade.potts.agent.cell.PottsCellFlyStem;
import arcade.potts.agent.cell.PottsCellFlyStem.StemType;
import arcade.potts.env.location.PottsLocation;
@@ -38,6 +39,9 @@ public class PottsModuleFlyStemProliferation extends PottsModuleProliferationVol
/** Basal rate of apoptosis (ticks^-1). */
final double basalApoptosisRate;
+ /** Rate of Prospero synthesis (ticks^-1). */
+ final double prosperoSynthesisRate;
+
/** Distribution that determines rotational offset of cell's division plane. */
final NormalDistribution splitDirectionDistribution;
@@ -116,6 +120,7 @@ public PottsModuleFlyStemProliferation(PottsCellFlyStem cell) {
Parameters parameters = cell.getParameters();
basalApoptosisRate = parameters.getDouble("proliferation/BASAL_APOPTOSIS_RATE");
+ prosperoSynthesisRate = parameters.getDouble("proliferation/PROSPERO_SYNTHESIS_RATE");
splitDirectionDistribution =
(NormalDistribution)
parameters.getDistribution("proliferation/DIV_ROTATION_DISTRIBUTION");
@@ -160,6 +165,15 @@ public PottsModuleFlyStemProliferation(PottsCellFlyStem cell) {
setPhase(Phase.UNDEFINED);
}
+ @Override
+ public void step(MersenneTwisterFast random, Simulation sim) {
+ super.step(random, sim);
+ ((PottsCellFly) cell)
+ .setProspero(((PottsCellFly) cell).getProspero() + prosperoSynthesisRate);
+ System.out.println(
+ "Stem ID " + cell.getID() + " prospero: " + ((PottsCellFly) cell).getProspero());
+ }
+
@Override
public void addCell(MersenneTwisterFast random, Simulation sim) {
Potts potts = ((PottsSimulation) sim).getPotts();
@@ -478,7 +492,10 @@ private void makeDaughterStemCell(
PottsCellContainer container =
((PottsCellFlyStem) cell)
.make(newID, State.PROLIFERATIVE, random, cell.getPop(), criticalVol);
- scheduleNewCell(container, daughterLoc, sim, potts, random);
+
+ double daughterProspero = splitStemProspero(((PottsCellFly) cell).getProspero(), random);
+ System.out.print("Creating daughter stem cell with prospero " + daughterProspero + " and ");
+ scheduleNewCell(container, daughterLoc, sim, potts, random, daughterProspero);
}
/**
@@ -510,7 +527,39 @@ private void makeDaughterGMC(
PottsCellContainer container =
((PottsCellFlyStem) cell)
.make(newID, State.PROLIFERATIVE, random, newPop, criticalVolume);
- scheduleNewCell(container, daughterLoc, sim, potts, random);
+ PottsCellFlyStem flyStemCell = (PottsCellFlyStem) cell;
+
+ System.out.print(
+ "Creating daughter GMC with prospero "
+ + ((PottsCellFly) cell).getProspero()
+ + " and ");
+ scheduleNewCell(
+ container, daughterLoc, sim, potts, random, ((PottsCellFly) cell).getProspero());
+ }
+
+ /**
+ * Determines how prospero is split between the parent and daughter cell. Can be edited to
+ * change behavior. Current prototype behavior divides the prospero evenly 50% of the time, and
+ * randomly selects which cell gets all prospero the other 50% of the time.
+ *
+ * @param parentProspero the parent cell's prospero to be divided
+ * @param random the random number generator
+ * @return double with daughter prospero amount
+ */
+ static double splitStemProspero(double parentProspero, MersenneTwisterFast random) {
+ if (parentProspero <= 0) {
+ return 0;
+ }
+
+ if (random.nextBoolean()) { // 50% of the time, split prospero evenly
+ return parentProspero / 2;
+ } else { // 50% of the time, randomly choose which cell gets all the prospero
+ if (random.nextBoolean()) {
+ return parentProspero;
+ } else {
+ return 0;
+ }
+ }
}
/**
@@ -527,7 +576,8 @@ private void scheduleNewCell(
PottsLocation daughterLoc,
Simulation sim,
Potts potts,
- MersenneTwisterFast random) {
+ MersenneTwisterFast random,
+ double daughterProspero) {
PottsCell newCell =
(PottsCell) container.convert(sim.getCellFactory(), daughterLoc, random);
if (newCell.getClass() == PottsCellFlyStem.class) {
@@ -537,6 +587,10 @@ private void scheduleNewCell(
potts.register(newCell);
newCell.reset(potts.ids, potts.regions);
newCell.schedule(sim.getSchedule());
+ System.out.println("ID " + newCell.getID());
+
+ ((PottsCellFly) newCell).setProspero(daughterProspero);
+ ((PottsCellFly) cell).setProspero(((PottsCellFly) cell).getProspero() - daughterProspero);
}
/**
diff --git a/src/arcade/potts/command.potts.xml b/src/arcade/potts/command.potts.xml
index bae804332..42a48bddd 100644
--- a/src/arcade/potts/command.potts.xml
+++ b/src/arcade/potts/command.potts.xml
@@ -1,2 +1,3 @@
+
diff --git a/src/arcade/potts/parameter.potts.xml b/src/arcade/potts/parameter.potts.xml
index ce000fd3a..c7c40a19c 100644
--- a/src/arcade/potts/parameter.potts.xml
+++ b/src/arcade/potts/parameter.potts.xml
@@ -86,6 +86,9 @@
+
+
+
@@ -96,4 +99,5 @@
+
diff --git a/src/arcade/potts/sim/PottsSimulation.java b/src/arcade/potts/sim/PottsSimulation.java
index 5249a414d..4ab58e2e3 100644
--- a/src/arcade/potts/sim/PottsSimulation.java
+++ b/src/arcade/potts/sim/PottsSimulation.java
@@ -1,7 +1,10 @@
package arcade.potts.sim;
+import java.lang.reflect.Type;
import java.util.ArrayList;
+import java.util.HashMap;
import java.util.HashSet;
+import com.google.gson.reflect.TypeToken;
import sim.engine.Schedule;
import sim.engine.SimState;
import arcade.core.agent.action.Action;
@@ -19,12 +22,16 @@
import arcade.core.util.MiniBox;
import arcade.potts.agent.cell.PottsCell;
import arcade.potts.agent.cell.PottsCellFactory;
+import arcade.potts.agent.cell.PottsCellFly;
import arcade.potts.env.grid.PottsGrid;
import arcade.potts.env.location.PottsLocationFactory;
import static arcade.potts.util.PottsEnums.Ordering;
/** Abstract implementation for potts {@link Simulation} instances. */
public abstract class PottsSimulation extends SimState implements Simulation {
+
+ public static final Type PROSPERO_TYPE = new TypeToken>() {}.getType();
+
/** {@link arcade.core.sim.Series} object containing this simulation. */
final PottsSeries series;
@@ -281,4 +288,15 @@ public void doOutput(boolean isScheduled) {
series.saver.save(tick);
}
}
+
+ public final HashMap getAllProspero() {
+ HashMap prosperoMap = new HashMap<>();
+
+ for (Object obj : grid.getAllObjects()) {
+ PottsCellFly cell = (PottsCellFly) obj;
+ prosperoMap.put(cell.getID(), cell.getProspero());
+ }
+
+ return prosperoMap;
+ }
}
diff --git a/src/arcade/potts/sim/output/PottsOutputSaver.java b/src/arcade/potts/sim/output/PottsOutputSaver.java
index 16c267699..e2ce2a2d4 100644
--- a/src/arcade/potts/sim/output/PottsOutputSaver.java
+++ b/src/arcade/potts/sim/output/PottsOutputSaver.java
@@ -3,6 +3,8 @@
import com.google.gson.Gson;
import arcade.core.sim.Series;
import arcade.core.sim.output.OutputSaver;
+import arcade.potts.sim.PottsSimulation;
+import static arcade.potts.sim.PottsSimulation.PROSPERO_TYPE;
/** Custom saver for potts-specific serialization. */
public final class PottsOutputSaver extends OutputSaver {
@@ -15,8 +17,27 @@ public PottsOutputSaver(Series series) {
super(series);
}
+ /** {@code true} to save prospero, {@code false} otherwise. */
+ public boolean saveProspero;
+
@Override
protected Gson makeGSON() {
return PottsOutputSerializer.makeGSON();
}
+
+ public void saveProspero(int tick) {
+ if (sim instanceof PottsSimulation) {
+ String json = gson.toJson(((PottsSimulation) sim).getAllProspero(), PROSPERO_TYPE);
+ String patch = prefix + String.format("_%06d.PROSPERO.json", tick);
+ write(patch, format(json, FORMAT_ELEMENTS));
+ }
+ }
+
+ @Override
+ public void save(int tick) {
+ super.save(tick);
+ if (saveProspero) {
+ saveProspero(tick);
+ }
+ }
}
diff --git a/src/arcade/potts/sim/output/PottsOutputSerializer.java b/src/arcade/potts/sim/output/PottsOutputSerializer.java
index e8eaacb9b..dc9a9a66a 100644
--- a/src/arcade/potts/sim/output/PottsOutputSerializer.java
+++ b/src/arcade/potts/sim/output/PottsOutputSerializer.java
@@ -2,6 +2,7 @@
import java.lang.reflect.Type;
import java.util.ArrayList;
+import java.util.HashMap;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonArray;
@@ -18,6 +19,7 @@
import arcade.potts.env.location.Voxel;
import arcade.potts.sim.PottsSeries;
import static arcade.potts.env.location.Voxel.VOXEL_COMPARATOR;
+import static arcade.potts.sim.PottsSimulation.PROSPERO_TYPE;
import static arcade.potts.util.PottsEnums.Region;
import static arcade.potts.util.PottsEnums.State;
@@ -53,6 +55,7 @@ static Gson makeGSON() {
gsonBuilder.registerTypeAdapter(
PottsLocationContainer.class, new PottsLocationSerializer());
gsonBuilder.registerTypeAdapter(Voxel.class, new VoxelSerializer());
+ gsonBuilder.registerTypeAdapter(PROSPERO_TYPE, new ProsperoSerializer());
return gsonBuilder.create();
}
@@ -275,4 +278,19 @@ public JsonElement serialize(Voxel src, Type typeOfSrc, JsonSerializationContext
return json;
}
}
+
+ static class ProsperoSerializer implements JsonSerializer> {
+ @Override
+ public JsonElement serialize(
+ HashMap src, Type typeOfSrc, JsonSerializationContext context) {
+ JsonArray json = new JsonArray();
+ for (Integer id : src.keySet()) {
+ JsonObject entry = new JsonObject();
+ entry.addProperty("id", id);
+ entry.addProperty("prospero", src.get(id));
+ json.add(entry);
+ }
+ return json;
+ }
+ }
}
diff --git a/test/arcade/potts/agent/module/PottsModuleFlyGMCDifferentiationTest.java b/test/arcade/potts/agent/module/PottsModuleFlyGMCDifferentiationTest.java
index b3babde5d..6ebe0e608 100644
--- a/test/arcade/potts/agent/module/PottsModuleFlyGMCDifferentiationTest.java
+++ b/test/arcade/potts/agent/module/PottsModuleFlyGMCDifferentiationTest.java
@@ -22,10 +22,13 @@
import arcade.potts.sim.PottsSimulation;
import arcade.potts.util.PottsEnums.Region;
import arcade.potts.util.PottsEnums.State;
+import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.mockConstruction;
+import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -62,6 +65,8 @@ public class PottsModuleFlyGMCDifferentiationTest {
private MockedConstruction mockedConstruction;
+ float EPSILON = 1e-6f;
+
@BeforeEach
public final void setupMocks() {
dummyIDs = new int[1][1][1];
@@ -131,6 +136,44 @@ final void tearDown() {
mockedConstruction.close();
}
+ @Test
+ public void constructor_setsParameters() {
+ when(parameters.getInt("proliferation/PDELIKE")).thenReturn(0);
+ when(parameters.getDouble("proliferation/PROSPERO_DEGRADATION_RATE")).thenReturn(1.0);
+
+ PottsModuleFlyGMCDifferentiation module = new PottsModuleFlyGMCDifferentiation(gmcCell);
+
+ org.junit.jupiter.api.Assertions.assertFalse(module.pdeLike);
+ org.junit.jupiter.api.Assertions.assertEquals(1.0, module.prosperoDegradationRate, EPSILON);
+ }
+
+ @Test
+ public void constructor_negativeProsperoDegradationRate_throwsException() {
+ when(parameters.getDouble("proliferation/PROSPERO_DEGRADATION_RATE")).thenReturn(-1.0);
+
+ assertThrows(
+ IllegalArgumentException.class,
+ () -> new PottsModuleFlyGMCDifferentiation(gmcCell));
+ }
+
+ @Test
+ public void step_decrementsProspero_prosperoIsUpdated() {
+ when(parameters.getDouble("proliferation/PROSPERO_DEGRADATION_RATE")).thenReturn(6.0);
+
+ PottsModuleFlyGMCDifferentiation module =
+ spy(new PottsModuleFlyGMCDifferentiation(gmcCell));
+
+ doNothing().when(module).addCell(any(MersenneTwisterFast.class), any(Simulation.class));
+
+ when(gmcCell.getProspero()).thenReturn(10.0);
+ module.step(random, sim);
+ verify(gmcCell).setProspero(4.0);
+
+ when(gmcCell.getProspero()).thenReturn(4.0);
+ module.step(random, sim);
+ verify(gmcCell).setProspero(0.0);
+ }
+
@Test
public void addCell_called_callsExpectedMethods() {
// When the module calls make() on the cell, return Quiescent PottsCellContainer
diff --git a/test/arcade/potts/agent/module/PottsModuleFlyStemProliferationTest.java b/test/arcade/potts/agent/module/PottsModuleFlyStemProliferationTest.java
index ec3b6a778..2c65fc618 100644
--- a/test/arcade/potts/agent/module/PottsModuleFlyStemProliferationTest.java
+++ b/test/arcade/potts/agent/module/PottsModuleFlyStemProliferationTest.java
@@ -130,6 +130,30 @@ final void tearDown() {
// Constructor tests
+ @Test
+ public void constructor_setsParameters() {
+ when(parameters.getDouble("proliferation/BASAL_APOPTOSIS_RATE")).thenReturn(0.04);
+ when(parameters.getDouble("proliferation/PROSPERO_SYNTHESIS_RATE")).thenReturn(0.895);
+ when(parameters.getString("proliferation/APICAL_AXIS_RULESET")).thenReturn("global");
+ when(parameters.getDistribution("proliferation/APICAL_AXIS_ROTATION_DISTRIBUTION"))
+ .thenReturn(dist);
+ when(parameters.getInt("proliferation/VOLUME_BASED_CRITICAL_VOLUME")).thenReturn(0);
+ when(parameters.getInt("proliferation/DYNAMIC_GROWTH_RATE_NB_SELF_REPRESSION"))
+ .thenReturn(0);
+
+ module = new PottsModuleFlyStemProliferation(stemCell);
+
+ assertEquals(0.04, module.basalApoptosisRate, EPSILON);
+ assertEquals(0.895, module.prosperoSynthesisRate, EPSILON);
+ assertNotNull(module.splitDirectionDistribution);
+ assertEquals("volume", module.differentiationRuleset);
+ assertEquals(0.5, module.range, EPSILON);
+ assertEquals("global", module.apicalAxisRuleset);
+ assertNotNull(module.apicalAxisRotationDistribution);
+ assertFalse(module.volumeBasedCriticalVolume);
+ assertFalse(module.dynamicGrowthRateNBSelfRepression);
+ }
+
@Test
public void constructor_volumeRuleset_setsExpectedFields() {
when(parameters.getString("proliferation/DIFFERENTIATION_RULESET")).thenReturn("volume");
@@ -493,6 +517,43 @@ public void step_volumeAtCheckpoint_callsAddCellPhaseStaysUndefined() {
assertEquals(Phase.UNDEFINED, module.phase); // remains UNDEFINED
}
+ @Test
+ public void step_incrementsProspero_prosperoIsUpdated() {
+ when(parameters.getInt("proliferation/DYNAMIC_GROWTH_RATE_VOLUME")).thenReturn(0);
+ when(parameters.getDouble("proliferation/CELL_GROWTH_RATE")).thenReturn(4.0);
+ when(parameters.getDouble("proliferation/SIZE_TARGET")).thenReturn(1.2);
+ when(parameters.getDouble("proliferation/PROSPERO_SYNTHESIS_RATE")).thenReturn(1.0);
+ module = new PottsModuleFlyStemProliferation(stemCell);
+ when(stemCell.getVolume()).thenReturn(0.0); // we don't want addCell to be called
+ when(stemCell.getProspero()).thenReturn(5.0);
+ module.step(random, sim);
+ verify(stemCell).setProspero(6.0);
+ }
+
+ @Test
+ public void splitStemProspero_zeroProspero_returnsZero() {
+ assertEquals(0.0, PottsModuleFlyStemProliferation.splitStemProspero(0.0, random), EPSILON);
+ }
+
+ @Test
+ public void splitStemProspero_evenSplit_returnsHalf() {
+ when(random.nextBoolean()).thenReturn(true);
+ assertEquals(5.0, PottsModuleFlyStemProliferation.splitStemProspero(10.0, random), EPSILON);
+ }
+
+ @Test
+ public void splitStemProspero_unevenSplitDaughterGetsAll_returnsAll() {
+ when(random.nextBoolean()).thenReturn(false, true);
+ assertEquals(
+ 10.0, PottsModuleFlyStemProliferation.splitStemProspero(10.0, random), EPSILON);
+ }
+
+ @Test
+ public void splitStemProspero_unevenSplitDaughterGetsNone_returnsZero() {
+ when(random.nextBoolean()).thenReturn(false, false);
+ assertEquals(0.0, PottsModuleFlyStemProliferation.splitStemProspero(10.0, random), EPSILON);
+ }
+
// Apical axis rule tests
@Test
@@ -1050,7 +1111,7 @@ public void updateGrowthRateBasedOnOtherNBs_pdeLikeTrue_usesPopulationBranch() {
// N = 6 in-simulation (K = 3, n = 2 → 9/(9+36)=0.2 → 4.0)
HashSet six = new HashSet<>();
- for (int i = 0; i < 6; i++) {
+ for (int i = 0; i <= 6; i++) {
PottsCellFlyStem n = mock(PottsCellFlyStem.class);
when(n.getID()).thenReturn(200 + i);
six.add(n);
diff --git a/test/arcade/potts/agent/module/PottsModuleProliferationVolumeBasedDivisionTest.java b/test/arcade/potts/agent/module/PottsModuleProliferationVolumeBasedDivisionTest.java
index 4c41c1661..248e6b7eb 100644
--- a/test/arcade/potts/agent/module/PottsModuleProliferationVolumeBasedDivisionTest.java
+++ b/test/arcade/potts/agent/module/PottsModuleProliferationVolumeBasedDivisionTest.java
@@ -4,19 +4,21 @@
import ec.util.MersenneTwisterFast;
import arcade.core.sim.Simulation;
import arcade.core.util.Parameters;
-import arcade.potts.agent.cell.PottsCellFlyGMC;
+import arcade.potts.agent.cell.PottsCell;
import arcade.potts.env.location.PottsLocation2D;
import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.mockito.Mockito.*;
public class PottsModuleProliferationVolumeBasedDivisionTest {
+ private static final double EPSILON = 1E-10;
static class PottsModuleProliferationVolumeBasedDivisionMock
extends PottsModuleProliferationVolumeBasedDivision {
boolean addCellCalled = false;
boolean growthRateUpdated = false;
- PottsModuleProliferationVolumeBasedDivisionMock(PottsCellFlyGMC cell) {
+ PottsModuleProliferationVolumeBasedDivisionMock(PottsCell cell) {
super(cell);
}
@@ -31,9 +33,29 @@ public void updateGrowthRate(Simulation sim) {
}
}
+ @Test
+ public void constructor_setsParameters() {
+ PottsCell cell = mock(PottsCell.class);
+ Parameters parameters = mock(Parameters.class);
+ doReturn(parameters).when(cell).getParameters();
+
+ when(parameters.getDouble("proliferation/SIZE_TARGET")).thenReturn(1.5);
+ when(parameters.getDouble("proliferation/CELL_GROWTH_RATE")).thenReturn(3.0);
+ when(parameters.getInt("proliferation/DYNAMIC_GROWTH_RATE_VOLUME")).thenReturn(1);
+ when(parameters.getDouble("proliferation/GROWTH_RATE_VOLUME_SENSITIVITY")).thenReturn(2.0);
+
+ PottsModuleProliferationVolumeBasedDivisionMock module =
+ new PottsModuleProliferationVolumeBasedDivisionMock(cell);
+
+ assertEquals(1.5, module.sizeTarget, EPSILON);
+ assertEquals(3.0, module.cellGrowthRateBase, EPSILON);
+ assertTrue(module.dynamicGrowthRateVolume);
+ assertEquals(2.0, module.growthRateVolumeSensitivity, EPSILON);
+ }
+
@Test
public void step_belowCheckpoint_updatesTarget() {
- PottsCellFlyGMC cell = mock(PottsCellFlyGMC.class);
+ PottsCell cell = mock(PottsCell.class);
Parameters params = mock(Parameters.class);
when(cell.getParameters()).thenReturn(params);
when(params.getDouble("proliferation/SIZE_TARGET")).thenReturn(1.2);
@@ -54,7 +76,7 @@ public void step_belowCheckpoint_updatesTarget() {
@Test
public void step_atOrAboveCheckpoint_triggersAddCell() {
- PottsCellFlyGMC cell = mock(PottsCellFlyGMC.class);
+ PottsCell cell = mock(PottsCell.class);
Parameters params = mock(Parameters.class);
when(cell.getParameters()).thenReturn(params);
when(params.getDouble("proliferation/SIZE_TARGET")).thenReturn(1.2);
@@ -75,7 +97,7 @@ public void step_atOrAboveCheckpoint_triggersAddCell() {
@Test
public void updateVolumeBasedGrowthRate_ratioOne_keepsBaseRate() {
// baseGrowth = 4.0, volume = Ka => growth = 4.0
- PottsCellFlyGMC cell = mock(PottsCellFlyGMC.class);
+ PottsCell cell = mock(PottsCell.class);
Parameters params = mock(Parameters.class);
PottsLocation2D loc = mock(PottsLocation2D.class);
@@ -100,7 +122,7 @@ public void updateVolumeBasedGrowthRate_ratioOne_keepsBaseRate() {
@Test
public void updateVolumeBasedGrowthRate_ratioGreaterThanOne_scalesUpByPowerLaw() {
// baseGrowth = 2.0, ratio = 2.0, sensitivity = 3 => 2 * 2^3 = 2 * 8 = 12
- PottsCellFlyGMC cell = mock(PottsCellFlyGMC.class);
+ PottsCell cell = mock(PottsCell.class);
Parameters params = mock(Parameters.class);
PottsLocation2D loc = mock(PottsLocation2D.class);
@@ -125,7 +147,7 @@ public void updateVolumeBasedGrowthRate_ratioGreaterThanOne_scalesUpByPowerLaw()
@Test
public void updateVolumeBasedGrowthRate_ratioLessThanOne_scalesDownByPowerLaw() {
// baseGrowth = 4.0, ratio = 0.5, sensitivity = 2.0 => 4 * 0.5^2 = 1.0
- PottsCellFlyGMC cell = mock(PottsCellFlyGMC.class);
+ PottsCell cell = mock(PottsCell.class);
Parameters params = mock(Parameters.class);
PottsLocation2D loc = mock(PottsLocation2D.class);
@@ -150,7 +172,7 @@ public void updateVolumeBasedGrowthRate_ratioLessThanOne_scalesDownByPowerLaw()
@Test
public void updateVolumeBasedGrowthRate_zeroSensitivity_returnsBaseRateRegardlessOfVolume() {
// sensitivity = 0 => growth = baseGrowth * ratio^0 = baseGrowth
- PottsCellFlyGMC cell = mock(PottsCellFlyGMC.class);
+ PottsCell cell = mock(PottsCell.class);
Parameters params = mock(Parameters.class);
PottsLocation2D loc = mock(PottsLocation2D.class);
diff --git a/test/arcade/potts/sim/PottsSimulationTest.java b/test/arcade/potts/sim/PottsSimulationTest.java
index 0a3f6cd75..09495d549 100644
--- a/test/arcade/potts/sim/PottsSimulationTest.java
+++ b/test/arcade/potts/sim/PottsSimulationTest.java
@@ -18,6 +18,7 @@
import arcade.potts.agent.cell.PottsCell;
import arcade.potts.agent.cell.PottsCellContainer;
import arcade.potts.agent.cell.PottsCellFactory;
+import arcade.potts.agent.cell.PottsCellFly;
import arcade.potts.env.location.PottsLocation;
import arcade.potts.env.location.PottsLocationContainer;
import arcade.potts.env.location.PottsLocationFactory;
@@ -34,6 +35,8 @@ public class PottsSimulationTest {
private static final int TOTAL_LOCATIONS = 6;
+ float EPSILON = 1e-6f;
+
static Series seriesZeroPop;
static Series seriesOnePop;
@@ -652,4 +655,42 @@ public void doOutput_isNotScheduled_savesOutput() {
verify(saver, never()).schedule(eq(schedule));
verify(saver).save((int) time + 1);
}
+
+ @Test
+ public void getAllProspero_multipleCells_returnsCorrectMap() {
+ PottsSimulation sim = mock(PottsSimulation.class, CALLS_REAL_METHODS);
+ sim.grid = mock(Grid.class);
+
+ PottsCellFly cell1 = mock(PottsCellFly.class);
+ PottsCellFly cell2 = mock(PottsCellFly.class);
+ when(cell1.getID()).thenReturn(1);
+ when(cell1.getProspero()).thenReturn(3.0);
+ when(cell2.getID()).thenReturn(2);
+ when(cell2.getProspero()).thenReturn(7.0);
+
+ Bag objects = new Bag();
+ objects.add(cell1);
+ objects.add(cell2);
+
+ doReturn(objects).when(sim.grid).getAllObjects();
+ HashMap result = sim.getAllProspero();
+
+ assertEquals(2, result.size());
+ assertEquals(3.0, result.get(1), EPSILON);
+ assertEquals(7.0, result.get(2), EPSILON);
+ }
+
+ @Test
+ public void getAllProspero_emptyGrid_returnsEmptyMap() {
+ PottsSimulation sim = mock(PottsSimulation.class, CALLS_REAL_METHODS);
+ sim.grid = mock(Grid.class);
+ Bag empty = new Bag();
+
+ doReturn(empty).when(sim.grid).getAllObjects();
+
+ HashMap result = sim.getAllProspero();
+
+ assertNotNull(result);
+ assertTrue(result.isEmpty());
+ }
}
diff --git a/test/arcade/potts/sim/output/PottsOutputSaverTest.java b/test/arcade/potts/sim/output/PottsOutputSaverTest.java
index c40bc8487..d316e4ac0 100644
--- a/test/arcade/potts/sim/output/PottsOutputSaverTest.java
+++ b/test/arcade/potts/sim/output/PottsOutputSaverTest.java
@@ -1,10 +1,18 @@
package arcade.potts.sim.output;
+import java.lang.reflect.Field;
+import java.util.HashMap;
import org.junit.jupiter.api.Test;
import com.google.gson.Gson;
import arcade.core.sim.Series;
+import arcade.core.sim.output.OutputSaver;
import arcade.core.sim.output.OutputSerializerTest;
+import arcade.potts.sim.PottsSeries;
+import arcade.potts.sim.PottsSimulation;
import static org.mockito.Mockito.*;
+import static arcade.core.ARCADETestUtilities.randomIntBetween;
+import static arcade.core.ARCADETestUtilities.randomString;
+import static arcade.potts.sim.PottsSimulation.PROSPERO_TYPE;
public class PottsOutputSaverTest {
@Test
@@ -15,4 +23,38 @@ public void makeGSON_called_returnsObjects() {
OutputSerializerTest.checkAdaptors(gson);
PottsOutputSerializerTest.checkAdaptors(gson);
}
+
+ @Test
+ public void saveProspero_called_savesContents() {
+ HashMap prospero = new HashMap<>();
+ int tick = randomIntBetween(0, 10);
+ PottsSimulation sim = mock(PottsSimulation.class);
+ doReturn(prospero).when(sim).getAllProspero();
+
+ PottsSeries series = mock(PottsSeries.class);
+ PottsOutputSaver saver = spy(new PottsOutputSaver(series));
+ doNothing().when(saver).write(anyString(), anyString());
+
+ try {
+ Field field = OutputSaver.class.getDeclaredField("sim");
+ field.setAccessible(true);
+ field.set(saver, sim);
+ } catch (Exception ignored) {
+ }
+
+ Gson gson = mock(Gson.class);
+ String contents = randomString();
+ doReturn(contents).when(gson).toJson(prospero, PROSPERO_TYPE);
+
+ try {
+ Field field = OutputSaver.class.getDeclaredField("gson");
+ field.setAccessible(true);
+ field.set(saver, gson);
+ } catch (Exception ignored) {
+ }
+
+ saver.saveProspero(tick);
+ verify(gson).toJson(prospero, PROSPERO_TYPE);
+ verify(saver).write(saver.prefix + String.format("_%06d.PROSPERO.json", tick), contents);
+ }
}
diff --git a/test/arcade/potts/sim/output/PottsOutputSerializerTest.java b/test/arcade/potts/sim/output/PottsOutputSerializerTest.java
index 3b4fc0a94..f63230c44 100644
--- a/test/arcade/potts/sim/output/PottsOutputSerializerTest.java
+++ b/test/arcade/potts/sim/output/PottsOutputSerializerTest.java
@@ -3,6 +3,7 @@
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.EnumMap;
+import java.util.HashMap;
import org.junit.jupiter.api.Test;
import com.google.gson.Gson;
import com.google.gson.JsonArray;
@@ -513,4 +514,36 @@ public void serialize_forVoxel_createsJSON() {
JsonElement json = serializer.serialize(voxel, null, null);
assertEquals(expected, json.toString());
}
+
+ @Test
+ public void serialize_forProspero_createsJSON() {
+ ProsperoSerializer serializer = new ProsperoSerializer();
+ HashMap cells = new HashMap<>();
+ int numCells = randomIntBetween(1, 100);
+ int startId = randomIntBetween(1, 100);
+
+ for (int i = 0; i < numCells; i++) {
+ cells.put(startId, randomDoubleBetween(1, 100));
+ startId++;
+ }
+
+ StringBuilder expected = new StringBuilder();
+ int i = 0;
+ expected.append("[");
+ for (Integer id : cells.keySet()) {
+ expected.append("{\"id\":")
+ .append(id)
+ .append(",\"prospero\":")
+ .append(cells.get(id))
+ .append("}");
+ if (i < cells.size() - 1) {
+ expected.append(","); // to match JSON formatting
+ }
+ i++;
+ }
+ expected.append("]");
+
+ JsonElement json = serializer.serialize(cells, null, null);
+ assertEquals(expected.toString(), json.toString());
+ }
}