Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,22 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [Unreleased]

### Bug Fixes

- Fixes Issue [#1199](https://github.com/PSLmodels/OG-Core/issues/1199):
`aggregates.revenue` counted payroll tax revenue twice. Each household's
`income_payroll_tax_liab` already includes `T_P = tau_payroll *
labor_income`, so payroll revenue is inside `iit_payroll_tax_revenue`;
PR #1184 then added `get_payroll_tax_revenue` (the same take, re-derived
from the aggregate wage bill) into that total again when `tau_payroll`
was nonzero. Revenue was overstated, the government over-spent, and any
model with a nonzero `tau_payroll` failed the steady-state resource
constraint (OG-USA was unaffected only because its `tau_payroll` is 0).
The erroneous addition is removed; `get_payroll_tax_revenue` still
provides the income vs payroll reporting split, which is unchanged.

## [0.19.1] - 2026-08-10 12:00:00

### Added
Expand Down
12 changes: 6 additions & 6 deletions ogcore/aggregates.py
Original file line number Diff line number Diff line change
Expand Up @@ -415,15 +415,15 @@ def revenue(
cons_tax_revenue = (
(tax.cons_tax_liab(c, p_i, p, method) * pop_weights).sum(1).sum(1)
)
# Payroll tax revenue is already inside iit_payroll_tax_revenue: each
# household's income_payroll_tax_liab includes T_P = tau_payroll *
# labor_income (see tax.income_tax_liab). get_payroll_tax_revenue just
# re-derives that same amount from the aggregate wage bill so the
# income vs payroll split can be reported; it must NOT be added into
# the total again (that double-counted payroll revenue -- Issue #1199).
payroll_tax_revenue = get_payroll_tax_revenue(
w, L, iit_payroll_tax_revenue, p, method
)
# When payroll taxes are modeled explicitly via tau_payroll, they are
# excluded from the income and payroll tax functions, so payroll tax
# revenue must be added into iit_payroll_tax_revenue (which enters
# total revenue) before the income tax portion is separated out.
if np.any(p.tau_payroll != 0):
iit_payroll_tax_revenue += payroll_tax_revenue
business_tax_revenue = tax.get_biz_tax(w, Y, L, K, p_m, p, m, method).sum(
-1
)
Expand Down
184 changes: 92 additions & 92 deletions tests/test_aggregates.py
Original file line number Diff line number Diff line change
Expand Up @@ -1474,111 +1474,111 @@ def test_get_C(c, p, method, expected):
# vector of output prices
p_m = np.ones((p.T, p.M))

expected1 = 0.7598207140204407
expected1 = 0.5688319028341413
expected2 = np.array(
[
0.78077777,
0.72068128,
0.77564557,
0.75220586,
0.78498646,
0.82226742,
0.78953295,
0.75499567,
0.75017449,
0.79555133,
0.77677308,
0.78748408,
0.73859649,
0.75895348,
0.75059024,
0.85599023,
0.77630208,
0.84016811,
0.90505241,
0.82363203,
0.80717282,
0.77141901,
0.73973490,
0.72241447,
0.74309837,
0.84927868,
0.78312174,
0.83015615,
0.77981290,
0.74258374,
0.58978896,
0.5318829,
0.58291302,
0.56616446,
0.60152253,
0.63685373,
0.60718972,
0.56236328,
0.56929121,
0.60536959,
0.58763365,
0.59627562,
0.55409009,
0.56782614,
0.56400569,
0.6636463,
0.59160813,
0.64735391,
0.72066489,
0.64096484,
0.61899218,
0.58806093,
0.54783766,
0.5393597,
0.55685316,
0.65395071,
0.58946501,
0.64129696,
0.58759922,
0.5580478,
]
)
expected3 = (
np.array(
[
0.78077777,
0.72068128,
0.77564557,
0.75220586,
0.78498646,
0.82226742,
0.78953295,
0.75499567,
0.75017449,
0.79555133,
0.77677308,
0.78748408,
0.73859649,
0.75895348,
0.75059024,
0.85599023,
0.77630208,
0.84016811,
0.90505241,
0.82363203,
0.80717282,
0.77141901,
0.73973490,
0.72241447,
0.74309837,
0.84927868,
0.78312174,
0.83015615,
0.77981290,
0.74258374,
0.58978896,
0.5318829,
0.58291302,
0.56616446,
0.60152253,
0.63685373,
0.60718972,
0.56236328,
0.56929121,
0.60536959,
0.58763365,
0.59627562,
0.55409009,
0.56782614,
0.56400569,
0.6636463,
0.59160813,
0.64735391,
0.72066489,
0.64096484,
0.61899218,
0.58806093,
0.54783766,
0.5393597,
0.55685316,
0.65395071,
0.58946501,
0.64129696,
0.58759922,
0.5580478,
]
)
- inv_tax_cred_rev3
)
expected4 = 0.7598207140204407
expected4 = 0.5688319028341413
expected5 = np.array(
[
0.78077777,
0.72068128,
0.77564557,
0.75220586,
0.78498646,
0.82226742,
0.78953295,
0.75499567,
0.75017449,
0.79555133,
0.77677308,
0.78748408,
0.73859649,
0.75895348,
0.75059024,
0.85599023,
0.77630208,
0.84016811,
0.90505241,
0.82363203,
0.80717282,
0.77141901,
0.73973490,
0.72241447,
0.74309837,
0.84927868,
0.78312174,
0.83015615,
0.77981290,
0.74258374,
0.58978896,
0.5318829,
0.58291302,
0.56616446,
0.60152253,
0.63685373,
0.60718972,
0.56236328,
0.56929121,
0.60536959,
0.58763365,
0.59627562,
0.55409009,
0.56782614,
0.56400569,
0.6636463,
0.59160813,
0.64735391,
0.72066489,
0.64096484,
0.61899218,
0.58806093,
0.54783766,
0.5393597,
0.55685316,
0.65395071,
0.58946501,
0.64129696,
0.58759922,
0.5580478,
]
)
test_data = [
Expand Down