"""
    pke_th_rhs_pj!(dx, x, t, plant, Q_sg, u)

Prompt-jump (singular-perturbation) reduced-order PKE + thermal-hydraulics.

The full PKE has prompt-neutron timescale Λ ≈ 10⁻⁴ s, while thermal
dynamics evolve on 1–100 s.  For reach analysis on horizons longer
than ~10 s, the prompt transient is irrelevant to safety but its
~50 µs timescale forces TMJets to take ~ms steps, exhausting the
step budget.

Singular-perturbation reduction: set dn/dt ≈ 0 in the neutron-balance
equation:
        0 = (ρ - β)/Λ · n + Σ λᵢ Cᵢ
    →   n ≈ Λ Σ λᵢ Cᵢ / (β - ρ)

Valid when β - ρ > 0 (sub-prompt-critical), which holds for normal
operation where ρ varies on the scale of pcm vs β = 650 pcm.

State drops from 10 to 9:
    x = [C₁..C₆, T_f, T_c, T_cold]   (no n; n is computed algebraically)

The C dynamics still depend on n; we substitute the algebraic form,
giving:
    dCᵢ/dt = (βᵢ/Λ) · n - λᵢ · Cᵢ
           = βᵢ · Σⱼ λⱼ Cⱼ / (β - ρ) - λᵢ · Cᵢ

This introduces a rational-function nonlinearity (1 / (β - ρ)) into
both the precursor balance and the fuel-temperature equation.  Taylor
models handle it as long as the denominator stays bounded away from
zero — a non-issue under normal operation.

Reach-analysis benefit: removes the Λ⁻¹ stiffness, so step size is
limited only by the slowest-precursor + thermal timescales.

Soundness cost: the prompt transient (∼50 µs after a reactivity
perturbation) is no longer captured.  For safety claims on hours-long
horizons this is acceptable; for prompt-supercritical excursions it is
fundamentally wrong (the algebraic form blows up as ρ → β).
"""
function pke_th_rhs_pj!(dx, x, t, plant, Q_sg, u)
    # Unpack 9-state x = [C1..C6, T_f, T_c, T_cold]
    C1, C2, C3, C4, C5, C6 = x[1], x[2], x[3], x[4], x[5], x[6]
    T_f    = x[7]
    T_c    = x[8]
    T_cold = x[9]
    T_hot  = 2 * T_c - T_cold

    # Total reactivity (controller u plus T-feedback).
    rho = u + plant.alpha_f * (T_f - plant.T_f0) +
              plant.alpha_c * (T_c - plant.T_c0)

    # Prompt-jump algebraic n.
    sum_lam_C = plant.lambda_i[1]*C1 + plant.lambda_i[2]*C2 +
                plant.lambda_i[3]*C3 + plant.lambda_i[4]*C4 +
                plant.lambda_i[5]*C5 + plant.lambda_i[6]*C6
    denom = plant.beta - rho
    n = plant.Lambda * sum_lam_C / denom

    inv_factor = sum_lam_C / denom    # used by precursor balance

    # Precursor balance under PJ.
    dx[1] = plant.beta_i[1] * inv_factor - plant.lambda_i[1] * C1
    dx[2] = plant.beta_i[2] * inv_factor - plant.lambda_i[2] * C2
    dx[3] = plant.beta_i[3] * inv_factor - plant.lambda_i[3] * C3
    dx[4] = plant.beta_i[4] * inv_factor - plant.lambda_i[4] * C4
    dx[5] = plant.beta_i[5] * inv_factor - plant.lambda_i[5] * C5
    dx[6] = plant.beta_i[6] * inv_factor - plant.lambda_i[6] * C6

    # Thermal-hydraulics (n appears algebraically in the fuel eq.).
    dx[7] = (plant.P0 * n - plant.hA * (T_f - T_c)) / (plant.M_f * plant.c_f)
    dx[8] = (plant.hA * (T_f - T_c) - 2 * plant.W * plant.c_c * (T_c - T_cold)) /
            (plant.M_c * plant.c_c)
    dx[9] = (plant.W * plant.c_c * (T_hot - T_cold) - Q_sg(t)) /
            (plant.M_sg * plant.c_c)

    return nothing
end

"""
    pke_initial_conditions_pj(plant; n_target=1.0)

Reduced-state initial condition for the prompt-jump model.  Returns
the 9-element vector [C1..C6, T_f, T_c, T_cold] consistent with
n = `n_target` at full-power equilibrium.
"""
function pke_initial_conditions_pj(plant; n_target=1.0)
    C0 = (plant.beta_i ./ (plant.lambda_i .* plant.Lambda)) .* n_target
    return [C0; plant.T_f0; plant.T_c0; plant.T_cold0]
end

"""
    pj_reconstruct_n(x, plant, u)

Algebraic n from the PJ reduced state.  For diagnostics / plotting.
"""
function pj_reconstruct_n(x, plant, u)
    T_f = x[7]
    T_c = x[8]
    rho = u + plant.alpha_f * (T_f - plant.T_f0) +
              plant.alpha_c * (T_c - plant.T_c0)
    sum_lam_C = sum(plant.lambda_i .* x[1:6])
    return plant.Lambda * sum_lam_C / (plant.beta - rho)
end
