With the new income-group demographics (PR #1165), a model calibrated with a mortality gradient fails the steady-state resource-constraint check. In an OG-ZAF calibration with South African mortality gradients, the violation is about 1% of GDP: RuntimeError: Steady state aggregate resource constraint not satisfied, resource constraint ≈ −0.0105.
The cause is in household.get_bq, in the use_zeta = False branch. Per-capita bequests are computed as BQ[j] / lambdas[j], which treats each group's population share as its birth share. That was exact before #1165. With a mortality gradient, groups no longer have λ-proportional populations — in our calibration the poorest group's actual population share is 0.226 against λ = 0.25, and the top group's is 0.011 against λ = 0.010 — so each group's receipts total BQ[j] × (actual share / λ_j) instead of BQ[j]. Bequests received no longer equal bequests left: with savings concentrated in the (longer-lived) top groups, households collectively receive about 6.5% more bequests than exist, and the phantom resources show up in the resource constraint.
Reproducer against ogcore 0.18.0 (any Specifications with a mortality gradient):
omega_SS = np.asarray(p.omega_SS) # (S, J), non-separable under gradients
b = np.outer(np.linspace(0.5, 2.0, p.S), # savings tilted toward top groups
np.array([0.2, 0.4, 0.7, 1.2, 2.0, 4.0, 10.0]))
BQ = np.asarray(aggregates.get_BQ(0.04, b, None, p, "SS", False)).ravel()
received = sum((household.get_bq(BQ, j, p, "SS") * omega_SS[:, j]).sum()
for j in range(p.J))
# BQ.sum() = 0.031880, received = 0.033957 -> +6.5% over-distribution
The use_zeta = True branch was already updated in #1165 — it divides by the actual omega_SS[:, j] / omega — so the fix is to give the non-zeta branch the same treatment: divide each group's pool by its actual population share (omega_SS[:, j].sum() in the SS, and the per-period group shares along the time path) instead of lambdas[j]. With demographics common across groups the two coincide, so existing results are unchanged.
draft PR to follow. cc: @rickecon @jdebacker
With the new income-group demographics (PR #1165), a model calibrated with a mortality gradient fails the steady-state resource-constraint check. In an OG-ZAF calibration with South African mortality gradients, the violation is about 1% of GDP:
RuntimeError: Steady state aggregate resource constraint not satisfied, resource constraint ≈ −0.0105.The cause is in
household.get_bq, in theuse_zeta = Falsebranch. Per-capita bequests are computed asBQ[j] / lambdas[j], which treats each group's population share as its birth share. That was exact before #1165. With a mortality gradient, groups no longer have λ-proportional populations — in our calibration the poorest group's actual population share is 0.226 against λ = 0.25, and the top group's is 0.011 against λ = 0.010 — so each group's receipts totalBQ[j] × (actual share / λ_j)instead ofBQ[j]. Bequests received no longer equal bequests left: with savings concentrated in the (longer-lived) top groups, households collectively receive about 6.5% more bequests than exist, and the phantom resources show up in the resource constraint.Reproducer against ogcore 0.18.0 (any Specifications with a mortality gradient):
The
use_zeta = Truebranch was already updated in #1165 — it divides by the actualomega_SS[:, j]/omega— so the fix is to give the non-zeta branch the same treatment: divide each group's pool by its actual population share (omega_SS[:, j].sum()in the SS, and the per-period group shares along the time path) instead oflambdas[j]. With demographics common across groups the two coincide, so existing results are unchanged.draft PR to follow. cc: @rickecon @jdebacker