A single well-mixed water box tracks three coupled state variables, integrated every frame with Euler sub-stepping (0.02 h steps):
Algae growth: dA/dt = growth_rate(N,I)*A*(1-A/K) - death_rate*A
Dead biomass: dD/dt = death_rate*A - decay_rate*D
Dissolved O2: dO2/dt = reaeration_rate*(O2sat-O2)
+ photosynthesis_rate*A*I - respiration
- decomposition_rate*D
Nutrient pool: dN/dt = runoff_inflow - uptake*growth
Nutrient loading sets the size of the initial spring-runoff nutrient pulse. A bigger pulse fuels faster, denser algal growth (Monod nutrient limitation, modulated by a real day/night light cycle). As the bloom consumes the nutrient pool, growth falls below the constant background mortality rate and the population crashes. Dead algae don't vanish instantly — they accumulate as decomposing detritus, and bacterial decomposition of that standing detritus pool is what actually draws down oxygen, which is why the oxygen crash always trails the biomass peak rather than tracking it in lockstep.
- Nutrient loading — sets the initial nutrient pool (fertilizer/sewage runoff pulse); heavier loading means a denser bloom and a deeper, longer oxygen crash.
- Time lag — the dead-biomass pool peaks after the algae peak (death then accumulation), and the oxygen minimum trails the algae peak further still, because decomposition draws down a standing stock of detritus rather than reacting to biomass instantaneously.
- Hypoxia threshold — dissolved oxygen below 2 mg/L is lethal to most fish and invertebrates; the water panel's color follows the live O2 value from oxygen-rich blue down through amber to hypoxic brown.
Real-world relevance: this nutrient → algal bloom → decomposition → hypoxia cascade, with its characteristic delay between bloom and oxygen crash, is the same mechanism behind real seasonal dead zones such as the Chesapeake Bay, Lake Erie's central basin, and the Gulf of Mexico's Mississippi plume.