The same payroll money enters total revenue through two different routes — the red trace and the blue trace below.
Red trace — through household tax bills (tax.py, aggregated in aggregates.py). Each household's bill includes the payroll tax, and total revenue sums the bills:
$$\textcolor{red}{\texttt{T\_P}} = \textcolor{red}{\texttt{tau\_payroll}} \times \texttt{labor\_income}$$
$$\textcolor{red}{\texttt{income\_payroll\_tax\_liab}} = \texttt{T\_I} + \textcolor{red}{\texttt{T\_P}}$$
$$\textcolor{red}{\texttt{iit\_payroll\_tax\_revenue}} = \sum \textcolor{red}{\texttt{income\_payroll\_tax\_liab}}$$
Payroll revenue is now fully inside iit_payroll_tax_revenue.
Blue trace — added by PR #1184 (aggregates.py, get_payroll_tax_revenue). The same take, recomputed from the economy-wide wage bill:
$$\textcolor{blue}{\texttt{payroll\_tax\_revenue}} = \textcolor{blue}{\texttt{tau\_payroll}} \times w \times L$$
Where they collide. In the line PR #1184 added (aggregates.py), the blue trace is added into the total that already contains the red trace:
$$\textcolor{red}{\texttt{iit\_payroll\_tax\_revenue}} \mathrel{+}= \textcolor{blue}{\texttt{payroll\_tax\_revenue}}$$
Red and blue are the same money: the payroll take, once from the household side and once from the aggregate side. Revenue is overstated by exactly that amount, the government spends it, and the steady state fails: Steady state aggregate resource constraint not satisfied. Any model with a nonzero tau_payroll is affected: OG-PHL fails; OG-USA is unaffected only because its tau_payroll is 0. The same parameter file solves under v0.19.0.
The two routes agree exactly, so the subtraction-based reporting split stays consistent after the fix: each household pays tau_payroll * w * e * n with no compliance or filer adjustment (those apply only to T_I), and aggregate L is the population-weighted sum of e * n , so summing the household payments gives tau_payroll * w * L, the blue formula.
Fix: delete the added statement entirely (the if and the += line) :
if np.any(p.tau_payroll != 0):
iit_payroll_tax_revenue += payroll_tax_revenue
so iit_payroll_tax_revenue stays what the line above builds: the sum of household tax bills, payroll already inside. Nothing replaces it. The rest of #1184 (the improved payroll/income reporting split) is fine. Happy to open a PR.
cc @jdebacker
The same payroll money enters total revenue through two different routes — the red trace and the blue trace below.
Red trace — through household tax bills (
tax.py, aggregated inaggregates.py). Each household's bill includes the payroll tax, and total revenue sums the bills:Payroll revenue is now fully inside
iit_payroll_tax_revenue.Blue trace — added by PR #1184 (
aggregates.py,get_payroll_tax_revenue). The same take, recomputed from the economy-wide wage bill:Where they collide. In the line PR #1184 added (
aggregates.py), the blue trace is added into the total that already contains the red trace:Red and blue are the same money: the payroll take, once from the household side and once from the aggregate side. Revenue is overstated by exactly that amount, the government spends it, and the steady state fails:
Steady state aggregate resource constraint not satisfied. Any model with a nonzerotau_payrollis affected: OG-PHL fails; OG-USA is unaffected only because itstau_payrollis 0. The same parameter file solves under v0.19.0.The two routes agree exactly, so the subtraction-based reporting split stays consistent after the fix: each household pays
tau_payroll * w * e * nwith no compliance or filer adjustment (those apply only toT_I), and aggregateLis the population-weighted sum ofe * n, so summing the household payments givestau_payroll * w * L, the blue formula.Fix: delete the added statement entirely (the
ifand the+=line) :so
iit_payroll_tax_revenuestays what the line above builds: the sum of household tax bills, payroll already inside. Nothing replaces it. The rest of #1184 (the improved payroll/income reporting split) is fine. Happy to open a PR.cc @jdebacker