Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
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
10 changes: 8 additions & 2 deletions src/arcade/potts/agent/cell/PottsCellFactory.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package arcade.potts.agent.cell;

import java.security.InvalidParameterException;
import java.util.ArrayList;
import java.util.EnumMap;
import java.util.HashMap;
Expand Down Expand Up @@ -213,8 +214,13 @@ void parseValues(Series series) {
if (linkKeys.size() > 0) {
links = new GrabBag();
for (String linkKey : linkKeys) {
int popLink = series.populations.get(linkKey).getInt("CODE");
links.add(popLink, linksBox.getDouble(linkKey));
try {
int popLink = series.populations.get(linkKey).getInt("CODE");
links.add(popLink, linksBox.getDouble(linkKey));
} catch (Exception e) {
throw new InvalidParameterException(
"A population link is set that references a population that does not exist.");
}
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,13 +20,16 @@
*/
public class PottsModuleFlyGMCDifferentiation extends PottsModuleProliferationVolumeBasedDivision {

Boolean pdeLike;

/**
* Creates a fly GMC proliferation module.
*
* @param cell the cell to which this module is attached
*/
public PottsModuleFlyGMCDifferentiation(PottsCellFlyGMC cell) {
super(cell);
pdeLike = (cell.getParameters().getInt("proliferation/PDELIKE") != 0);
}

/**
Expand Down Expand Up @@ -93,10 +96,40 @@ void addCell(MersenneTwisterFast random, Simulation sim) {
}

public void updateGrowthRate(Simulation sim) {
if (dynamicGrowthRateVolume == false) {
if (!dynamicGrowthRateVolume) {
cellGrowthRate = cellGrowthRateBase;
} else if (dynamicGrowthRateVolume == true) {
updateVolumeBasedGrowthRate();
} else {
if (!pdeLike) {
updateCellVolumeBasedGrowthRate(
cell.getLocation().getVolume(), cell.getCriticalVolume());
} else {
// PDE-like: use population-wide averages for GMCs (same pop as this cell)
sim.util.Bag objs = sim.getGrid().getAllObjects();

double volSum = 0.0;
double critSum = 0.0;
int count = 0;

for (int i = 0; i < objs.numObjs; i++) {
Object o = objs.objs[i];
if (!(o instanceof arcade.potts.agent.cell.PottsCell)) continue;

arcade.potts.agent.cell.PottsCell c = (arcade.potts.agent.cell.PottsCell) o;
if (c.getPop() != cell.getPop()) continue; // keep to same population

if (o instanceof arcade.potts.agent.cell.PottsCellFlyGMC) {
arcade.potts.agent.cell.PottsCellFlyGMC gmc =
(arcade.potts.agent.cell.PottsCellFlyGMC) o;
volSum += gmc.getLocation().getVolume();
critSum += gmc.getCriticalVolume();
count++;
}
}
double avgVolume = volSum / count;
double avgCritVol = critSum / count;
updateCellVolumeBasedGrowthRate(avgVolume, avgCritVol);
System.out.println("GMC " + cell.getID() + "growth rate = " + cellGrowthRate);
}
}
}
}
113 changes: 68 additions & 45 deletions src/arcade/potts/agent/module/PottsModuleFlyStemProliferation.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import java.security.InvalidParameterException;
import java.util.ArrayList;
import java.util.HashSet;
import sim.util.Bag;
import sim.util.Double3D;
import ec.util.MersenneTwisterFast;
import arcade.core.env.location.Location;
Expand Down Expand Up @@ -59,7 +60,7 @@ public class PottsModuleFlyStemProliferation extends PottsModuleProliferationVol
final boolean volumeBasedCriticalVolume;

/** Boolean flag indicating whether growth rate should be regulated by NB-NB contact. */
final boolean dynamicGrowthRateNBContact;
final boolean dynamicGrowthRateNBSelfRepression;

final double volumeBasedCriticalVolumeMultiplier;

Expand All @@ -83,6 +84,12 @@ public class PottsModuleFlyStemProliferation extends PottsModuleProliferationVol

final double initialSize;

/**
* Boolean determining whether growth and division rates are universal across all NBs. If true
* model behaviors is PDE-like, if false it is ABM-like.
*/
final Boolean pdeLike;

/**
* Creates a proliferation {@code Module} for the given {@link PottsCellFlyStem}.
*
Expand Down Expand Up @@ -113,10 +120,10 @@ public PottsModuleFlyStemProliferation(PottsCellFlyStem cell) {
volumeBasedCriticalVolume =
(parameters.getInt("proliferation/VOLUME_BASED_CRITICAL_VOLUME") != 0);

dynamicGrowthRateNBContact =
(parameters.getInt("proliferation/DYNAMIC_GROWTH_RATE_NB_CONTACT") != 0);
dynamicGrowthRateNBSelfRepression =
(parameters.getInt("proliferation/DYNAMIC_GROWTH_RATE_NB_SELF_REPRESSION") != 0);

if (dynamicGrowthRateVolume && dynamicGrowthRateNBContact) {
if (dynamicGrowthRateVolume && dynamicGrowthRateNBSelfRepression) {
throw new InvalidParameterException(
"Dynamic growth rate can be either volume-based or NB-contact-based, not both.");
}
Expand All @@ -129,6 +136,8 @@ public PottsModuleFlyStemProliferation(PottsCellFlyStem cell) {

initialSize = cell.getVolume();

pdeLike = (parameters.getInt("proliferation/PDELIKE") != 0);

setPhase(Phase.UNDEFINED);
}

Expand Down Expand Up @@ -165,24 +174,42 @@ public void addCell(MersenneTwisterFast random, Simulation sim) {
*/
public void updateGrowthRate(Simulation sim) {
if (dynamicGrowthRateVolume == true) {
updateVolumeBasedGrowthRate();
} else if (dynamicGrowthRateNBContact == true) {
updateNBContactGrowthRate(sim);
updateVolumeBasedGrowthRate(sim);
} else if (dynamicGrowthRateNBSelfRepression == true) {
updateGrowthRateBasedOnOtherNBs(sim);
} else {
cellGrowthRate = cellGrowthRateBase;
}
}

public void updateVolumeBasedGrowthRate(Simulation sim) {
if (pdeLike == false) {
updateCellVolumeBasedGrowthRate(
cell.getLocation().getVolume(), cell.getCriticalVolume());
} else {
HashSet<PottsCellFlyStem> nbsInSimulation = getNBsInSimulation(sim);
double volSum = 0.0;
double critVolSum = 0.0;
for (PottsCellFlyStem nb : nbsInSimulation) {
volSum += nb.getLocation().getVolume();
critVolSum += nb.getCriticalVolume();
}
double avgVolume = volSum / nbsInSimulation.size();
double avgCritVol = critVolSum / nbsInSimulation.size();
updateCellVolumeBasedGrowthRate(avgVolume, avgCritVol);
}
}

/**
* Gets the number of neighbors of this cell that are unique neuroblasts.
* Gets the neighbors of this cell that are unique neuroblasts.
*
* @param sim the simulation
* @return the number of unique neuroblast neighbors
*/
protected Integer getNumNBNeighbors(Simulation sim) {
protected HashSet<PottsCellFlyStem> getNBNeighbors(Simulation sim) {
Potts potts = ((PottsSimulation) sim).getPotts();
ArrayList<Voxel> voxels = ((PottsLocation) cell.getLocation()).getVoxels();
HashSet<PottsCell> stemNeighbors = new HashSet<PottsCell>();
HashSet<PottsCellFlyStem> stemNeighbors = new HashSet<PottsCellFlyStem>();

for (Voxel v : voxels) {
HashSet<Integer> uniqueIDs = potts.getUniqueIDs(v.x, v.y, v.z);
Expand All @@ -193,50 +220,32 @@ protected Integer getNumNBNeighbors(Simulation sim) {
}
if (cell.getPop() == neighbor.getPop()) {
if (neighbor.getID() != cell.getID()) {
stemNeighbors.add((PottsCell) sim.getGrid().getObjectAt(id));
stemNeighbors.add((PottsCellFlyStem) sim.getGrid().getObjectAt(id));
}
}
}
}
return stemNeighbors.size();
return stemNeighbors;
}

/**
* Updates the cell's growth rate based on the number of neighboring neuroblasts.
*
* <p>This method applies a Hill-type repression function to scale the cell's base growth rate
* according to local neuroblast density. Specifically, it counts the number of neighboring
* neuroblasts (using {@link #getNumNBNeighbors(Simulation)}) and applies:
*
* <pre>
* hillRepression = K^n / (K^n + Np^n)
* cellGrowthRate = cellGrowthRateBase * hillRepression
* </pre>
*
* where:
*
* <ul>
* <li><code>Np</code> is the number of neighboring neuroblasts
* <li><code>K</code> is the half-max parameter for repression
* (proliferation/NB_CONTACT_HALF_MAX)
* <li><code>n</code> is the Hill coefficient controlling steepness
* (proliferation/NB_CONTACT_HILL_N)
* <li><code>cellGrowthRateBase</code> is the base growth rate in the absence of neighbors
* </ul>
*
* <p>This formulation ensures that when Np = 0, the cell grows at the base rate, and as the
* number of neighbors increases, growth is repressed toward zero.
*
* @param sim the simulation
*/
protected void updateNBContactGrowthRate(Simulation sim) {
int NpRaw = getNumNBNeighbors(sim);
double Np = Math.max(0.0, (double) NpRaw);
protected void updateGrowthRateBasedOnOtherNBs(Simulation sim) {
int npRaw;
if (pdeLike) {
npRaw = getNBsInSimulation(sim).size();
} else {
npRaw = getNBNeighbors(sim).size();
}
double np = Math.max(0.0, (double) npRaw);

double Kn = Math.pow(nbContactHalfMax, nbContactHillN);
double Npn = Math.pow(Np, nbContactHillN);
double Npn = Math.pow(np, nbContactHillN);

double hillRepression = Kn / (Kn + Npn);
double hillRepression;
if (Kn == 0.0) {
hillRepression = (np == 0.0) ? 1.0 : 0.0;
} else {
hillRepression = Kn / (Kn + Npn);
}

cellGrowthRate = cellGrowthRateBase * hillRepression;
}
Expand Down Expand Up @@ -578,4 +587,18 @@ public static PottsLocation getBasalLocation(

return (proj1 < proj2) ? loc2 : loc1; // higher projection = more basal
}

public HashSet<PottsCellFlyStem> getNBsInSimulation(Simulation sim) {
HashSet<PottsCellFlyStem> nbsInSimulation = new HashSet<>();
Bag simObjects = sim.getGrid().getAllObjects();
for (int i = 0; i < simObjects.numObjs; i++) {
Object o = simObjects.objs[i];
if (!(o instanceof PottsCell)) continue; // skip non-cell objects
PottsCell cellInSim = (PottsCell) o;
if (cell.getPop() == cellInSim.getPop() && o instanceof PottsCellFlyStem) {
nbsInSimulation.add((PottsCellFlyStem) o);
}
}
return nbsInSimulation;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -69,22 +69,8 @@ public void step(MersenneTwisterFast random, Simulation sim) {
*/
public abstract void updateGrowthRate(Simulation sim);

/**
* Updates the cell growth rate based on the volume of the cell.
*
* <p>The growth rate is scaled according to a power-law relationship between the current cell
* volume and its critical volume. As the cell volume increases relative to the critical volume,
* the growth rate accelerates proportionally:
*
* <pre>
* growthRate = baseGrowthRate * (volume / criticalVolume) ^ sensitivity
* </pre>
*
* This allows larger cells to grow faster, capturing volume-dependent growth dynamics.
*/
public void updateVolumeBasedGrowthRate() {
double volume = cell.getLocation().getVolume();
double Ka = cell.getCriticalVolume();
public void updateCellVolumeBasedGrowthRate(double volume, double cellCriticalVolume) {
double Ka = cellCriticalVolume;
cellGrowthRate = cellGrowthRateBase * Math.pow((volume / Ka), growthRateVolumeSensitivity);
}
}
8 changes: 6 additions & 2 deletions src/arcade/potts/parameter.potts.xml
Original file line number Diff line number Diff line change
Expand Up @@ -63,11 +63,15 @@
<population.module module="proliferation" id="BASAL_APOPTOSIS_RATE" value="0.00127128" units="hours^-1" conversion="DT" description="basal rate of apoptosis" />
<population.module module="proliferation" id="NUCLEUS_GROWTH_RATE" value="30" units="um^3/hour" conversion="DS^-3.DT" description="basal rate of nucleus growth" />
<population.module module="proliferation" id="NUCLEUS_CONDENSATION_FRACTION" value="0.5" description="fraction of nuclear volume when condensed" />

<!-- ── Fly cell proliferation module parameters ───────────────────── -->
<!-- ── Dictating whether NB behavior should be the same across all NBs ──────── -->
<population.module module="proliferation" id="PDELIKE" value="0" description="1 → all cells of a given type have same growth and division dynamics based on average metrics across the population; 0 → Cell growth and division is determined on a cell-by-cell basis, cells can differ from other cells of the same type" />
<!-- ── Growth rate sensitivity to cell volume dynamics ───────────────────── -->
<population.module module="proliferation" id="DYNAMIC_GROWTH_RATE_VOLUME" value="0" description="1 → enable volume-sensitive growth rate; 0 → disabled" />
<population.module module="proliferation" id="GROWTH_RATE_VOLUME_SENSITIVITY" value="1.0" description="exponent in (volume/criticalVolume)^sensitivity when volume rule is enabled" />
<!-- ── NB Growth rate sensitivity to neighboring NBs ─────────────────────── -->
<population.module module="proliferation" id="DYNAMIC_GROWTH_RATE_NB_CONTACT" value="0" description="1 → enable NB-contact repression; 0 → disabled (mutually exclusive with volume rule)" />
<!-- ── NB Growth rate sensitivity to neighboring NBs, only valid if growth and division is not universal ────── -->
<population.module module="proliferation" id="DYNAMIC_GROWTH_RATE_NB_SELF_REPRESSION" value="0" description="1 → enable NB-NB repression; 0 → disabled (mutually exclusive with volume rule)" />
<population.module module="proliferation" id="NB_CONTACT_HALF_MAX" value="1.0" units="neighbors" description="K: NB neighbor count giving 50% repression" />
<population.module module="proliferation" id="NB_CONTACT_HILL_N" value="2.0" description="Hill exponent controlling repression steepness" />
<!-- ── NB division rules ───────────────────────────── -->
Expand Down
Loading
Loading