Dissolved Oxygen Balance: Sources, Sinks, and the DO Sag Curve

Dissolved oxygen is the single most important indicator of stream health. The EPA recommends minimum DO criteria for aquatic life protection. The DO balance in QUAL2K accounts for all sources and sinks of oxygen, producing the characteristic DO sag curve downstream of pollution sources.

Full DO Balance Equation

The rate of change of DO at each reach is:

dDOdt=ka(DOsatDO)kdcCBODfkdcsCBODsronknNH4SODH\frac{dDO}{dt} = k_a(DO_{sat} - DO) - k_{dc} \cdot CBOD_f - k_{dcs} \cdot CBOD_s - r_{on} \cdot k_n \cdot NH_4 - \frac{SOD}{H}

Term-by-Term Explanation

TermFormulaEffectTypical Magnitude
Reaerationka · (DOsat − DO)Source (+) when DO < DOsat1–10 mg/L/d
Fast CBOD oxidationkdc · CBODfSink (−) consumes O₂0.5–5 mg/L/d
Slow CBOD oxidationkdcs · CBODsSink (−) consumes O₂0.01–0.5 mg/L/d
Nitrification O₂ demandron · kn · NH₄Sink (−) consumes O₂0.5–3 mg/L/d
Sediment O₂ demandSOD / HSink (−) from benthos0.5–5 mg/L/d

The DO Sag Curve

When a pollution source adds CBOD to a river, the DO drops as bacteria consume oxygen to oxidize the organic matter. Downstream, reaeration from the atmosphere gradually restores DO, producing the classic sag shape:

Nitrogenous Oxygen Demand (NOD)

Nitrification consumes oxygen as bacteria oxidize ammonium to nitrate:

NH4++2O2NO3+2H++H2ONH_4^+ + 2O_2 \rightarrow NO_3^- + 2H^+ + H_2O

The stoichiometric ratio ron=4.57r_{on} = 4.57 mg O₂ per mg NH₄-N oxidized. This term can be the dominant oxygen sink in rivers receiving nitrogenous wastewater.

Sediment Oxygen Demand (SOD)

SOD represents oxygen consumed by microbial decomposition of organic matter in the stream bed sediments. It is expressed as a flux (g O₂/m²/d) and converted to a volumetric rate by dividing by stream depth:

SOD rate=SODH(mg/L/d)\text{SOD rate} = \frac{SOD}{H} \quad \text{(mg/L/d)}

Typical SOD values by substrate type

SubstrateSOD (g O₂/m²/d)Context
Sandy / gravel bed0.2 – 1.0Clean streams with minimal organic deposition
Silt / mud bed1.0 – 2.0Moderate organic enrichment
Organic-rich sediment2.0 – 5.0Downstream of WWTP outfalls
Heavily polluted5.0 – 10.0Industrial discharge zones
Municipal sludge deposits2.0 – 10.0Near combined sewer overflows

DO Saturation Reference Table

DO saturation (mg/L) at sea level

Temp (°C)DOsatTemp (°C)DOsatTemp (°C)DOsat
014.621011.29209.09
213.841210.78228.74
413.131410.31248.42
612.48169.87268.11
811.87189.47287.83

Python Implementation

pythonkinetics.py — DO balance calculation
def calculate_do_balance(do_in, cbod_fast, cbod_slow, nh4, rates, reach, temp, dt):
    ka = reach.get('ka', 0)
    ka_t = temp_correction(ka, 1.024, temp)  # Reaeration

    dosat = calculate_dosat(temp, reach.get('elev', 0))

    kdc = temp_correction(rates['kdc'], rates.get('kdc_theta', 1.047), temp)
    kdcs = temp_correction(rates['kdcs'], rates.get('kdcs_theta', 1.047), temp)
    kn = temp_correction(rates.get('kn', 0), rates.get('kn_theta', 1.083), temp)
    ron = rates.get('ron', 4.57)  # O₂:N stoichiometric ratio
    sod = reach.get('SOD', 0)
    h = reach.get('H', 1.0)

    # Source: reaeration
    reaeration = ka_t * (dosat - do_in) * dt
    # Sinks
    cbod_demand_f = kdc * cbod_fast * dt
    cbod_demand_s = kdcs * cbod_slow * dt
    nod_demand = ron * kn * nh4 * dt
    sod_demand = (sod / h) * dt if h > 0 else 0

    do_out = do_in + reaeration - cbod_demand_f - cbod_demand_s - nod_demand - sod_demand
    return max(do_out, 0.0)  # DO cannot go negative