diff --git a/src/arcade/core/sim/Series.java b/src/arcade/core/sim/Series.java index 39193d707..cbb09e325 100644 --- a/src/arcade/core/sim/Series.java +++ b/src/arcade/core/sim/Series.java @@ -329,6 +329,43 @@ protected static void parseParameter( } } + /** + * Parses parameter values based on default value. + * + * @param box the parameter map + * @param parameter the parameter name + * @param defaultParameter the default parameter value + * @param values the map of parameter values + * @param scales the map of parameter scaling + * @param ics the map of initial condition parameters + */ + protected static void parseParameter( + MiniBox box, + String parameter, + String defaultParameter, + MiniBox values, + MiniBox scales, + MiniBox ics) { + String value = values.contains(parameter) ? values.get(parameter) : defaultParameter; + Matcher match = Pattern.compile(DISTRIBUTION_REGEX).matcher(value); + + if (match.find()) { + box.put("(DISTRIBUTION)" + TAG_SEPARATOR + parameter, match.group(1).toUpperCase()); + for (int i = 0; i < (match.groupCount() - 1) / 2; i++) { + int index = 2 * (i + 1); + box.put(parameter + "_" + match.group(index), match.group(index + 1)); + } + } else { + box.put(parameter, value); + if (scales.contains(parameter)) { + box.put(parameter, box.getDouble(parameter) * scales.getDouble(parameter)); + } + } + if (ics.contains(parameter)) { + box.put(parameter + "_IC", ics.get(parameter)); + } + } + /** * Updates conversion string into a value. * diff --git a/src/arcade/core/util/Parameters.java b/src/arcade/core/util/Parameters.java index 75cd68aac..733571afa 100644 --- a/src/arcade/core/util/Parameters.java +++ b/src/arcade/core/util/Parameters.java @@ -4,6 +4,7 @@ import java.util.HashMap; import java.util.HashSet; import ec.util.MersenneTwisterFast; +import arcade.core.util.distributions.DegenerateDistribution; import arcade.core.util.distributions.Distribution; /** @@ -39,13 +40,16 @@ public Parameters( MiniBox distributionsBox = popParameters.filter("(DISTRIBUTION)"); for (String key : distributionsBox.getKeys()) { Distribution distribution; - if (cellParameters != null && cellParameters.distributions.containsKey(key)) { distribution = cellParameters.distributions.get(key).rebase(random); + if (distribution instanceof DegenerateDistribution) { + distribution = popParameters.getDistribution(key, random); + } + } else if (popParameters.contains(key + "_IC")) { + distribution = new DegenerateDistribution(key, popParameters, random); } else { distribution = popParameters.getDistribution(key, random); } - distributions.put(key, distribution); } } diff --git a/src/arcade/core/util/distributions/DegenerateDistribution.java b/src/arcade/core/util/distributions/DegenerateDistribution.java new file mode 100644 index 000000000..2adb68547 --- /dev/null +++ b/src/arcade/core/util/distributions/DegenerateDistribution.java @@ -0,0 +1,95 @@ +package arcade.core.util.distributions; + +import ec.util.MersenneTwisterFast; +import arcade.core.util.MiniBox; + +/** Container class for degenerate distribution (constant value). */ +public class DegenerateDistribution implements Distribution { + /** The constant value of the distribution. */ + private final double value; + + /** + * Creates a degenerate {@code Distribution} from parameters dictionary. + * + * @param name the distribution parameter name + * @param parameters the distribution parameters dictionary + * @param random the random number generator instance (unused for degenerate distribution) + */ + public DegenerateDistribution(String name, MiniBox parameters, MersenneTwisterFast random) { + this(getValueFromParameters(name, parameters)); + } + + /** + * Helper method to get the value from parameters based on IC type. + * + * @param name the distribution parameter name + * @param parameters the distribution parameters dictionary + * @return the value of the distribution + */ + private static double getValueFromParameters(String name, MiniBox parameters) { + String icType = parameters.get(name + "_IC"); + if (icType.equals("MU")) { + return parameters.getDouble(name + "_MU"); + } else if (icType.equals("MIN")) { + return parameters.getDouble(name + "_MIN"); + } else if (icType.equals("MAX")) { + return parameters.getDouble(name + "_MAX"); + } else { + try { + if (icType.contains(".")) { + return Double.parseDouble(icType); + } else { + return Integer.parseInt(icType); + } + } catch (NumberFormatException e) { + throw new IllegalArgumentException("Invalid IC value: " + icType, e); + } + } + } + + /** + * Creates a degenerate {@code Distribution} with a constant value. + * + * @param value the constant value of the distribution + */ + public DegenerateDistribution(double value) { + this.value = value; + } + + @Override + public MiniBox getParameters() { + MiniBox parameters = new MiniBox(); + parameters.put("VALUE", value); + return parameters; + } + + @Override + public double getExpected() { + return value; + } + + @Override + public double getDoubleValue() { + return value; + } + + @Override + public int getIntValue() { + return (int) Math.round(value); + } + + @Override + public double nextDouble() { + return value; + } + + @Override + public int nextInt() { + return (int) Math.round(value); + } + + @Override + public Distribution rebase(MersenneTwisterFast random) { + return new DegenerateDistribution(value); + } +} diff --git a/src/arcade/patch/agent/cell/PatchCellFactory.java b/src/arcade/patch/agent/cell/PatchCellFactory.java index 0492e0801..8c053b746 100644 --- a/src/arcade/patch/agent/cell/PatchCellFactory.java +++ b/src/arcade/patch/agent/cell/PatchCellFactory.java @@ -132,7 +132,6 @@ public void createCells(Series series) { } int pop = population.getInt("CODE"); - for (int i = 0; i < init; i++) { PatchCellContainer container = createCellForPopulation(id, pop); cells.put(id, container); @@ -152,9 +151,7 @@ public void createCells(Series series) { public PatchCellContainer createCellForPopulation(int id, int pop) { MiniBox population = popToParameters.get(pop); Parameters parameters = new Parameters(population, null, random); - double compression = parameters.getDouble("COMPRESSION_TOLERANCE"); - double volume = parameters.getDouble("CELL_VOLUME"); double height = parameters.getDouble("CELL_HEIGHT"); int age = parameters.getInt("CELL_AGE"); diff --git a/src/arcade/patch/sim/PatchSeries.java b/src/arcade/patch/sim/PatchSeries.java index 861002b09..ff09aa71d 100644 --- a/src/arcade/patch/sim/PatchSeries.java +++ b/src/arcade/patch/sim/PatchSeries.java @@ -175,7 +175,7 @@ protected void updatePopulations( Box parameters = box.filterBoxByTag("PARAMETER"); MiniBox parameterValues = parameters.getIdValForTagAtt("PARAMETER", "value"); MiniBox parameterScales = parameters.getIdValForTagAtt("PARAMETER", "scale"); - + MiniBox parameterICs = parameters.getIdValForTagAtt("PARAMETER", "ic"); // Apply conversion factors. for (String convert : populationConversions.getKeys()) { double conversion = parseConversion(populationConversions.get(convert), ds, dt); @@ -194,9 +194,9 @@ protected void updatePopulations( parameter, populationDefaults.get(parameter), parameterValues, - parameterScales); + parameterScales, + parameterICs); } - // Get list of links, if valid. MiniBox links = box.filterBoxByTag("LINK").getIdValForTagAtt("LINK", "weight"); for (String link : links.getKeys()) { diff --git a/test/arcade/core/sim/SeriesTest.java b/test/arcade/core/sim/SeriesTest.java index 5292f2453..adad02c57 100644 --- a/test/arcade/core/sim/SeriesTest.java +++ b/test/arcade/core/sim/SeriesTest.java @@ -693,6 +693,36 @@ public void parseParameter_withTwoParameterDistributionValue_usesValue() { assertEquals(valueB, box.getDouble(parameter + "_B"), EPSILON); } + @Test + public void parseParameter_withIC_putsICInBox() { + MiniBox box = new MiniBox(); + String parameter = randomString(); + double defaultParameter = randomDoubleBetween(0, 100); + + MiniBox values = new MiniBox(); + MiniBox scales = new MiniBox(); + MiniBox ics = new MiniBox(); + ics.put(parameter, "MU"); + Series.parseParameter(box, parameter, "" + defaultParameter, values, scales, ics); + + assertEquals("MU", box.get(parameter + "_IC")); + } + + @Test + public void parseParameter_withoutIC_doesNotPutICInBox() { + MiniBox box = new MiniBox(); + String parameter = randomString(); + double defaultParameter = randomDoubleBetween(0, 100); + + MiniBox values = new MiniBox(); + MiniBox scales = new MiniBox(); + MiniBox ics = new MiniBox(); + + Series.parseParameter(box, parameter, "" + defaultParameter, values, scales, ics); + + assertFalse(box.contains(parameter + "_IC")); + } + @Test public void parseConversion_invalidConversion_returnsOne() { assertEquals(1, Series.parseConversion(randomString(), DS, DZ, DT), EPSILON); diff --git a/test/arcade/core/util/distributions/DegenerateDistributionTest.java b/test/arcade/core/util/distributions/DegenerateDistributionTest.java new file mode 100644 index 000000000..1ae086288 --- /dev/null +++ b/test/arcade/core/util/distributions/DegenerateDistributionTest.java @@ -0,0 +1,117 @@ +package arcade.core.util.distributions; + +import org.junit.jupiter.api.Test; +import ec.util.MersenneTwisterFast; +import arcade.core.util.MiniBox; +import static org.junit.jupiter.api.Assertions.*; +import static arcade.core.ARCADETestUtilities.*; + +public class DegenerateDistributionTest { + + private static final double EPSILON = 1E-5; + + private static final MersenneTwisterFast RANDOM = new MersenneTwisterFast(); + + @Test + public void constructor_withDirectValue_setsValueCorrectly() { + double expected = 42.0; + DegenerateDistribution dist = new DegenerateDistribution(expected); + + assertEquals(expected, dist.getDoubleValue(), EPSILON); + assertEquals(expected, dist.getExpected(), EPSILON); + assertEquals((int) Math.round(expected), dist.getIntValue()); + } + + @Test + public void constructor_withICMU_setsValueFromMU() { + MiniBox parameters = new MiniBox(); + String name = randomString().toUpperCase(); + double mu = randomDoubleBetween(0, 100); + parameters.put(name + "_IC", "MU"); + parameters.put(name + "_MU", mu); + + DegenerateDistribution dist = new DegenerateDistribution(name, parameters, RANDOM); + + assertEquals(mu, dist.getDoubleValue(), EPSILON); + } + + @Test + public void constructor_withICNumber_setsValueFromNumber() { + MiniBox parameters = new MiniBox(); + String name = randomString().toUpperCase(); + String numberString = "50"; + parameters.put(name + "_IC", numberString); + + DegenerateDistribution dist = new DegenerateDistribution(name, parameters, RANDOM); + + assertEquals(50, dist.getDoubleValue(), EPSILON); + } + + @Test + public void constructor_withICMIN_setsValueFromMIN() { + MiniBox parameters = new MiniBox(); + String name = randomString().toUpperCase(); + double min = randomDoubleBetween(0, 100); + parameters.put(name + "_IC", "MIN"); + parameters.put(name + "_MIN", min); + + DegenerateDistribution dist = new DegenerateDistribution(name, parameters, RANDOM); + + assertEquals(min, dist.getDoubleValue(), EPSILON); + } + + @Test + public void constructor_withICMAX_setsValueFromMAX() { + MiniBox parameters = new MiniBox(); + String name = randomString().toUpperCase(); + double max = randomDoubleBetween(0, 100); + parameters.put(name + "_IC", "MAX"); + parameters.put(name + "_MAX", max); + + DegenerateDistribution dist = new DegenerateDistribution(name, parameters, RANDOM); + + assertEquals(max, dist.getDoubleValue(), EPSILON); + } + + @Test + public void constructor_withInvalidIC_throwsException() { + MiniBox parameters = new MiniBox(); + String name = randomString().toUpperCase(); + parameters.put(name + "_IC", "INVALID"); + + Exception exception = + assertThrows( + IllegalArgumentException.class, + () -> new DegenerateDistribution(name, parameters, RANDOM)); + + assertTrue(exception.getMessage().contains("Invalid IC")); + } + + @Test + public void getParameters_returnsCorrectMiniBox() { + double expected = randomDoubleBetween(0, 100); + DegenerateDistribution dist = new DegenerateDistribution(expected); + MiniBox parameters = dist.getParameters(); + + assertEquals(expected, parameters.getDouble("VALUE"), EPSILON); + } + + @Test + public void rebase_returnsIdenticalDistribution() { + double value = randomDoubleBetween(0, 100); + DegenerateDistribution dist = new DegenerateDistribution(value); + DegenerateDistribution rebased = (DegenerateDistribution) dist.rebase(RANDOM); + + assertEquals(value, rebased.getDoubleValue(), EPSILON); + assertEquals(value, rebased.getExpected(), EPSILON); + } + + @Test + public void nextDouble_and_nextInt_returnConstantValue() { + double value = randomDoubleBetween(0, 100); + DegenerateDistribution dist = new DegenerateDistribution(value); + + assertEquals(value, dist.nextDouble(), EPSILON); + assertEquals((int) Math.round(value), dist.nextInt()); + } +} diff --git a/test/arcade/patch/agent/cell/PatchCellContainerTest.java b/test/arcade/patch/agent/cell/PatchCellContainerTest.java new file mode 100644 index 000000000..de9806835 --- /dev/null +++ b/test/arcade/patch/agent/cell/PatchCellContainerTest.java @@ -0,0 +1,144 @@ +package arcade.patch.agent.cell; + +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; +import sim.engine.Schedule; +import ec.util.MersenneTwisterFast; +import arcade.core.agent.cell.CellState; +import arcade.core.env.location.*; +import arcade.core.util.MiniBox; +import arcade.core.util.Parameters; +import arcade.core.util.distributions.Distribution; +import arcade.core.util.distributions.NormalDistribution; +import arcade.patch.env.location.PatchLocation; +import arcade.patch.sim.PatchSimulation; +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.*; +import static arcade.core.ARCADETestUtilities.*; +import static arcade.patch.util.PatchEnums.State; + +public class PatchCellContainerTest { + static PatchSimulation simMock; + + static PatchLocation locationMock; + + static Parameters parametersMock; + + static MersenneTwisterFast randomMock; + + static int cellID = randomIntBetween(1, 10); + + static int cellParent = randomIntBetween(1, 10); + + static int cellPop = randomIntBetween(1, 10); + + static int cellAge = randomIntBetween(1, 1000); + + static int cellDivisions = randomIntBetween(1, 100); + + static double cellVolume = randomDoubleBetween(10, 100); + + static double cellHeight = randomDoubleBetween(10, 100); + + static double cellCriticalVolume = randomDoubleBetween(10, 100); + + static double cellCriticalHeight = randomDoubleBetween(10, 100); + + static State cellState = State.QUIESCENT; + + static PatchCellContainer baseContainer = + new PatchCellContainer( + cellID, + cellParent, + cellPop, + cellAge, + cellDivisions, + cellState, + cellVolume, + cellHeight, + cellCriticalVolume, + cellCriticalHeight); + + static class PatchCellMock extends PatchCellTissue { + PatchCellMock(PatchCellContainer container, Location location, Parameters parameters) { + super(container, location, parameters, null); + } + + @Override + public PatchCellContainer make(int newID, CellState newState, MersenneTwisterFast random) { + return new PatchCellContainer( + newID, + id, + pop, + age, + divisions, + newState, + volume, + height, + criticalVolume, + criticalHeight); + } + } + + @BeforeAll + public static void setupMocks() { + simMock = mock(PatchSimulation.class); + locationMock = mock(PatchLocation.class); + parametersMock = spy(new Parameters(new MiniBox(), null, null)); + randomMock = mock(MersenneTwisterFast.class); + } + + @Test + public void setState_proliferateWithIC_drawsFromDistribution() { + Schedule scheduleMock = mock(Schedule.class); + doReturn(cellID + 1).when(simMock).getID(); + doReturn(scheduleMock).when(simMock).getSchedule(); + doReturn(null).when(scheduleMock).scheduleRepeating(anyInt(), anyInt(), any()); + PatchCellFactory factoryMock = mock(PatchCellFactory.class); + doReturn(null).when(factoryMock).getLinks(anyInt()); + doReturn(factoryMock).when(simMock).getCellFactory(); + doReturn(0.5).when(randomMock).nextDouble(); + + MiniBox population = new MiniBox(); + double volume = 500; + population.put("CLASS", "tissue"); + population.put("COMPRESSION_TOLERANCE", 0); + population.put("(DISTRIBUTION)/NECROTIC_FRACTION", "NORMAL"); + population.put("NECROTIC_FRACTION_MU", 0.5); + population.put("NECROTIC_FRACTION_SIGMA", 0.1); + population.put("NECROTIC_FRACTION_IC", "MU"); + population.put("proliferation/SYNTHESIS_DURATION", 1); + population.put("SENESCENT_FRACTION", 0); + population.put("ENERGY_THRESHOLD", 0); + population.put("APOPTOSIS_AGE", 0); + population.put("ACCURACY", 0); + population.put("AFFINITY", 0); + population.put("DIVISION_POTENTIAL", 50); + population.put("MAX_DENSITY", 0); + population.put("CAR_ANTIGENS", 0); + population.put("SELF_TARGETS", 0); + doReturn(population).when(factoryMock).getParameters(anyInt()); + Parameters parameters = new Parameters(population, null, randomMock); + double critHeight = 10; + double critVolume = 250; + PatchCellContainer daughterContainer = + new PatchCellContainer( + cellID + 1, + cellParent, + cellPop, + cellAge, + cellDivisions, + State.UNDEFINED, + volume, + cellHeight, + critVolume, + critHeight); + PatchCell daughter = + (PatchCell) + daughterContainer.convert( + factoryMock, locationMock, randomMock, parameters); + Distribution dist = daughter.getParameters().getDistribution("NECROTIC_FRACTION"); + assertTrue(dist instanceof NormalDistribution); + } +} diff --git a/test/arcade/patch/agent/cell/PatchCellFactoryTest.java b/test/arcade/patch/agent/cell/PatchCellFactoryTest.java index 103b6d3bc..5d1b29ae3 100644 --- a/test/arcade/patch/agent/cell/PatchCellFactoryTest.java +++ b/test/arcade/patch/agent/cell/PatchCellFactoryTest.java @@ -39,6 +39,155 @@ public void createCells_noPopulation_createsEmpty() { assertEquals(0, factory.popToIDs.size()); } + @Test + public void createCells_withICEqualMU_createsList() { + int count = randomIntBetween(1, 10); + PatchSeries series = createSeries(new int[] {count}, new String[] {"COUNT"}); + + double volumeMu = randomDoubleBetween(1, 10); + double volumeSigma = randomDoubleBetween(1, 10); + double height = randomDoubleBetween(1, 10); + int age = randomIntBetween(1, 100); + double compression = randomDoubleBetween(1, 10); + + MiniBox parameters = new MiniBox(); + parameters.put("(DISTRIBUTION)/CELL_VOLUME", "NORMAL"); + parameters.put("CELL_VOLUME_MU", volumeMu); + parameters.put("CELL_VOLUME_SIGMA", volumeSigma); + parameters.put("CELL_VOLUME_IC", "MU"); + parameters.put("CELL_HEIGHT", height); + parameters.put("CELL_AGE", age); + parameters.put("COMPRESSION_TOLERANCE", compression); + + PatchCellFactory factory = new PatchCellFactory(); + factory.popToIDs.put(1, new HashSet<>()); + factory.popToParameters.put(1, parameters); + factory.createCells(series); + + assertEquals(count, factory.cells.size()); + assertEquals(count, factory.popToIDs.get(1).size()); + + for (int i : factory.popToIDs.get(1)) { + PatchCellContainer patchCellContainer = factory.cells.get(i); + assertEquals(volumeMu, patchCellContainer.criticalVolume, EPSILON); + assertEquals(height + compression, patchCellContainer.criticalHeight, EPSILON); + assertEquals(age, patchCellContainer.age); + assertEquals(0, patchCellContainer.divisions); + } + } + + @Test + public void createCells_withICEqualNumber_createsList() { + int count = randomIntBetween(1, 10); + PatchSeries series = createSeries(new int[] {count}, new String[] {"COUNT"}); + + double volumeMu = randomDoubleBetween(1, 10); + double volumeSigma = randomDoubleBetween(1, 10); + double height = randomDoubleBetween(1, 10); + int age = randomIntBetween(1, 100); + double compression = randomDoubleBetween(1, 10); + String numberString = "50"; + + MiniBox parameters = new MiniBox(); + parameters.put("(DISTRIBUTION)/CELL_VOLUME", "NORMAL"); + parameters.put("CELL_VOLUME_MU", volumeMu); + parameters.put("CELL_VOLUME_SIGMA", volumeSigma); + parameters.put("CELL_VOLUME_IC", numberString); + parameters.put("CELL_HEIGHT", height); + parameters.put("CELL_AGE", age); + parameters.put("COMPRESSION_TOLERANCE", compression); + + PatchCellFactory factory = new PatchCellFactory(); + factory.popToIDs.put(1, new HashSet<>()); + factory.popToParameters.put(1, parameters); + factory.createCells(series); + + assertEquals(count, factory.cells.size()); + assertEquals(count, factory.popToIDs.get(1).size()); + + for (int i : factory.popToIDs.get(1)) { + PatchCellContainer patchCellContainer = factory.cells.get(i); + assertEquals(50, patchCellContainer.criticalVolume, EPSILON); + assertEquals(height + compression, patchCellContainer.criticalHeight, EPSILON); + assertEquals(age, patchCellContainer.age); + assertEquals(0, patchCellContainer.divisions); + } + } + + @Test + public void createCells_withICEqualMin_createsList() { + int count = randomIntBetween(1, 10); + PatchSeries series = createSeries(new int[] {count}, new String[] {"COUNT"}); + + double volumeMin = randomDoubleBetween(1, 10); + double volumeMax = randomDoubleBetween(1, 10); + double height = randomDoubleBetween(1, 10); + int age = randomIntBetween(1, 100); + double compression = randomDoubleBetween(1, 10); + + MiniBox parameters = new MiniBox(); + parameters.put("(DISTRIBUTION)/CELL_VOLUME", "UNIFORM"); + parameters.put("CELL_VOLUME_MIN", volumeMin); + parameters.put("CELL_VOLUME_MAX", volumeMax); + parameters.put("CELL_VOLUME_IC", "MIN"); + parameters.put("CELL_HEIGHT", height); + parameters.put("CELL_AGE", age); + parameters.put("COMPRESSION_TOLERANCE", compression); + + PatchCellFactory factory = new PatchCellFactory(); + factory.popToIDs.put(1, new HashSet<>()); + factory.popToParameters.put(1, parameters); + factory.createCells(series); + + assertEquals(count, factory.cells.size()); + assertEquals(count, factory.popToIDs.get(1).size()); + + for (int i : factory.popToIDs.get(1)) { + PatchCellContainer patchCellContainer = factory.cells.get(i); + assertEquals(volumeMin, patchCellContainer.criticalVolume, EPSILON); + assertEquals(height + compression, patchCellContainer.criticalHeight, EPSILON); + assertEquals(age, patchCellContainer.age); + assertEquals(0, patchCellContainer.divisions); + } + } + + @Test + public void createCells_withICNotMatchDistribution_returnsNaN() { + int count = randomIntBetween(1, 10); + PatchSeries series = createSeries(new int[] {count}, new String[] {"COUNT"}); + + double volumeMin = randomDoubleBetween(1, 10); + double volumeMax = randomDoubleBetween(1, 10); + double height = randomDoubleBetween(1, 10); + int age = randomIntBetween(1, 100); + double compression = randomDoubleBetween(1, 10); + + MiniBox parameters = new MiniBox(); + parameters.put("(DISTRIBUTION)/CELL_VOLUME", "UNIFORM"); + parameters.put("CELL_VOLUME_MIN", volumeMin); + parameters.put("CELL_VOLUME_MAX", volumeMax); + parameters.put("CELL_VOLUME_IC", "MU"); + parameters.put("CELL_HEIGHT", height); + parameters.put("CELL_AGE", age); + parameters.put("COMPRESSION_TOLERANCE", compression); + + PatchCellFactory factory = new PatchCellFactory(); + factory.popToIDs.put(1, new HashSet<>()); + factory.popToParameters.put(1, parameters); + factory.createCells(series); + + assertEquals(count, factory.cells.size()); + assertEquals(count, factory.popToIDs.get(1).size()); + + for (int i : factory.popToIDs.get(1)) { + PatchCellContainer patchCellContainer = factory.cells.get(i); + assertEquals(Double.NaN, patchCellContainer.criticalVolume, EPSILON); + assertEquals(height + compression, patchCellContainer.criticalHeight, EPSILON); + assertEquals(age, patchCellContainer.age); + assertEquals(0, patchCellContainer.divisions); + } + } + @Test public void createCells_onePopulationInitByCount_createsList() { int count = randomIntBetween(1, 10);