-
Notifications
You must be signed in to change notification settings - Fork 4
Update GMC Proliferation Module #219
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
57817c1
6392d9e
488ab02
4376f32
731082f
6cb465d
64ce606
2ce8a27
3e8bebc
9238f51
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -20,20 +20,55 @@ | |
| */ | ||
| public class PottsModuleFlyGMCDifferentiation extends PottsModuleProliferationVolumeBasedDivision { | ||
|
|
||
| /** | ||
| * Indicates whether GMC growth rate is based on individual cell conditions or average cell | ||
| * conditions. | ||
| */ | ||
| 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); | ||
| } | ||
|
|
||
| /** | ||
| * Computes the expected equilibrium average GMC volume over one cell cycle. | ||
| * | ||
| * <p>In the Potts model, a cell's target volume is initialized to {@code criticalVolume} on | ||
| * reset. The Potts energy immediately drives the cell's actual volume toward this target, | ||
| * regardless of the current growth rate. As a result, the volume-regulated growth phase | ||
| * effectively begins at {@code criticalVolume} (not the birth volume), even when {@code | ||
| * VOLUME_BASED_CRITICAL_VOLUME} is off and birth volume is below {@code criticalVolume}. | ||
| * | ||
| * <p>The regulated growth phase therefore runs from {@code criticalVolume} to {@code sizeTarget | ||
| * * criticalVolume}. Under constant-rate growth, the time-average volume over this phase is the | ||
| * arithmetic mean of the two endpoints: | ||
| * | ||
| * <pre> | ||
| * V_ref = (criticalVolume + sizeTarget * criticalVolume) / 2 | ||
| * = criticalVolume * (1 + sizeTarget) / 2 | ||
| * </pre> | ||
| * | ||
| * <p>This formula is consistent with the PDE-like branch, which uses {@code avgCritVol * (1 + | ||
| * sizeTarget) / 2}, and holds whether or not {@code VOLUME_BASED_CRITICAL_VOLUME} is enabled. | ||
| * | ||
| * @return the expected equilibrium average GMC volume | ||
| */ | ||
| double computeEquilibriumVolume() { | ||
| return cell.getCriticalVolume() * (1.0 + sizeTarget) / 2.0; | ||
| } | ||
|
|
||
| /** | ||
| * Adds a cell to the simulation. | ||
| * | ||
| * <p>The cell location is split. The new neuron cell is created, initialized, and added to the | ||
| * schedule. This cell's location is also assigned to a new Neuron cell. | ||
| * schedule. This cell's location is also assigned to a new Neuron cell. The critical volume of | ||
| * both neurons is set to the initial volume of each neuron's location. | ||
| * | ||
| * @param random the random number generator | ||
| * @param sim the simulation instance | ||
|
|
@@ -55,7 +90,7 @@ void addCell(MersenneTwisterFast random, Simulation sim) { | |
| (PottsCell) newContainer.convert(sim.getCellFactory(), newLocation, random); | ||
| sim.getGrid().addObject(newCell, null); | ||
| potts.register(newCell); | ||
| newCell.reset(potts.ids, potts.regions); | ||
| newCell.initialize(potts.ids, potts.regions); | ||
| newCell.schedule(sim.getSchedule()); | ||
|
|
||
| // remove old GMC cell from simulation | ||
|
|
@@ -88,7 +123,56 @@ void addCell(MersenneTwisterFast random, Simulation sim) { | |
|
|
||
| sim.getGrid().addObject(differentiatedGMC, null); | ||
| potts.register(differentiatedGMC); | ||
| differentiatedGMC.reset(potts.ids, potts.regions); | ||
| differentiatedGMC.initialize(potts.ids, potts.regions); | ||
| differentiatedGMC.schedule(sim.getSchedule()); | ||
| } | ||
|
|
||
| /** | ||
| * Updates the effective growth rate according to boolean flags specified in parameters. | ||
| * | ||
| * @param sim the simulation | ||
| */ | ||
| public void updateGrowthRate(Simulation sim) { | ||
| if (!dynamicGrowthRateVolume) { | ||
| cellGrowthRate = cellGrowthRateBase; | ||
| } else { | ||
| if (!pdeLike) { | ||
| updateCellVolumeBasedGrowthRate( | ||
| cell.getLocation().getVolume(), computeEquilibriumVolume()); | ||
| } else { | ||
| // PDE-like: use population-wide averages for GMCs (same pop as this cell). | ||
| // The reference volume is the population-average equilibrium volume: | ||
| // avgVRef = avgCritVol * (1 + sizeTarget) / 2 | ||
| 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)) { | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. To clarify, is this if statement for avoiding environmental objects (like vasculature)? Otherwise, we can't have a simulation where Patch/Potts cells both exist right? |
||
| 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; | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think Allison touched on this in an earlier comment, but I think that finding the average volume of the Potts cells can be moved out into their own function like |
||
| double avgCritVol = critSum / count; | ||
| double avgVRef = avgCritVol * (1.0 + sizeTarget) / 2.0; | ||
| updateCellVolumeBasedGrowthRate(avgVolume, avgVRef); | ||
| } | ||
| } | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -7,20 +7,31 @@ | |
| import arcade.potts.util.PottsEnums.Phase; | ||
|
|
||
| /** | ||
| * Implementation of {@link PottsModule} for fly GMC agents. The links must be set in the setup file | ||
| * so that 100% of the daughter cells are Neurons. | ||
| * Implementation of {@link PottsModule} for agents that divide upon reaching a volume threshold | ||
| * without any cell-cycle duration requirements. | ||
| */ | ||
| public abstract class PottsModuleProliferationVolumeBasedDivision extends PottsModuleProliferation { | ||
|
|
||
| /** Overall growth rate for cell (voxels/tick). */ | ||
| final double cellGrowthRate; | ||
| /** Base growth rate for cells (voxels/tick). */ | ||
| final double cellGrowthRateBase; | ||
|
|
||
| /** Current growth rate for stem cells (voxels/tick). */ | ||
| double cellGrowthRate; | ||
|
|
||
| /** | ||
| * Target ratio of critical volume for division size checkpoint (cell must reach CRITICAL_VOLUME | ||
| * * SIZE_TARGET * SIZE_CHECKPOINT to divide). | ||
| */ | ||
| final double sizeTarget; | ||
|
|
||
| /** Boolean flag indicating whether the growth rate should follow volume-sensitive ruleset. */ | ||
| final boolean dynamicGrowthRateVolume; | ||
|
|
||
| /** | ||
| * Sensitivity of growth rate to cell volume, only relevant if dynamicGrowthRateVolume is true. | ||
| */ | ||
| final double growthRateVolumeSensitivity; | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ignore if this was implemented elsewhere (or if its impossible to make this error), but might be good to throw an error if |
||
|
|
||
| /** | ||
| * Creates a proliferation module in which division is solely dependent on cell volume. | ||
| * | ||
|
|
@@ -30,16 +41,52 @@ public PottsModuleProliferationVolumeBasedDivision(PottsCell cell) { | |
| super(cell); | ||
| Parameters parameters = cell.getParameters(); | ||
| sizeTarget = parameters.getDouble("proliferation/SIZE_TARGET"); | ||
| cellGrowthRate = parameters.getDouble("proliferation/CELL_GROWTH_RATE"); | ||
| cellGrowthRateBase = parameters.getDouble("proliferation/CELL_GROWTH_RATE"); | ||
| dynamicGrowthRateVolume = | ||
| (parameters.getInt("proliferation/DYNAMIC_GROWTH_RATE_VOLUME") != 0); | ||
| growthRateVolumeSensitivity = | ||
| parameters.getDouble("proliferation/GROWTH_RATE_VOLUME_SENSITIVITY"); | ||
| setPhase(Phase.UNDEFINED); | ||
| cellGrowthRate = cellGrowthRateBase; | ||
| } | ||
|
|
||
| @Override | ||
| public void step(MersenneTwisterFast random, Simulation sim) { | ||
| updateGrowthRate(sim); | ||
| cell.updateTarget(cellGrowthRate, sizeTarget); | ||
| boolean sizeCheck = cell.getVolume() >= sizeTarget * cell.getCriticalVolume(); | ||
| if (sizeCheck) { | ||
| addCell(random, sim); | ||
| } | ||
| } | ||
|
|
||
| /** | ||
| * Updates the effective growth rate according to boolean flags specified in parameters. | ||
| * | ||
| * @param sim the simulation | ||
| */ | ||
| public abstract void updateGrowthRate(Simulation sim); | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Could the I am a little confused as to why the updateGrowthRate logic is a method rather than some switch statements in the abstract class |
||
|
|
||
| /** | ||
| * Updates {@code cellGrowthRate} from a power-law relationship between current volume and a | ||
| * reference volume. | ||
| * | ||
| * <p>The updated rate is | ||
| * | ||
| * <pre> | ||
| * cellGrowthRate = cellGrowthRateBase * (volume / referenceVolume)^growthRateVolumeSensitivity | ||
| * </pre> | ||
| * | ||
| * <p>The reference volume is the cell volume at which the basal growth rate is recovered. In | ||
| * the simplest case this can be the cell's critical volume, but users may use another | ||
| * biologically motivated reference such as an equilibrium or population-averaged volume. | ||
| * | ||
| * @param volume the current volume used in the growth-rate scaling | ||
| * @param referenceVolume the reference volume that defines the baseline growth-rate scale | ||
| */ | ||
| public void updateCellVolumeBasedGrowthRate(double volume, double referenceVolume) { | ||
| double refVol = referenceVolume; | ||
| cellGrowthRate = | ||
| cellGrowthRateBase * Math.pow((volume / refVol), growthRateVolumeSensitivity); | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -64,6 +64,13 @@ | |
| <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 ──────── --> | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Another very nitpick comment so feel free to ignore
Eg:
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I agree that the |
||
| <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 ───────────────────── --> | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I recall that there were non-volume based proliferation options, do you intend to have a section for that too? After some reading I do think it would make sense to have something like: |
||
| <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="3.0" description="exponent in (volume/criticalVolume)^sensitivity when volume rule is enabled" /> | ||
|
|
||
| <!-- apoptosis module parameters --> | ||
| <population.module module="apoptosis" id="RATE_EARLY" value="4" units="steps/hour" conversion="DT" description="rate of events in early apoptosis phase" /> | ||
| <population.module module="apoptosis" id="RATE_LATE" value="1.8" units="steps/hour" conversion="DT" description="rate of events in late apoptosis phase" /> | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Very nitpick comments so feel free to ignore:
Consider factoring out the volume averaging logic to its own function
For readability it might be easier to do switch statements or if/else statements where the boolean is true. For example something like: