Skip to content
Open
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
70 changes: 66 additions & 4 deletions ogcore/aggregates.py
Original file line number Diff line number Diff line change
Expand Up @@ -399,7 +399,6 @@ def revenue(
cons_tax_revenue = (
tax.cons_tax_liab(c, p_i, p, method) * pop_weights
).sum()
payroll_tax_revenue = p.frac_tax_payroll[-1] * iit_payroll_tax_revenue
elif method == "TPI":
p_i = (
np.tile(p.io_matrix.reshape(1, p.I, p.M), (p.T, 1, 1))
Expand All @@ -416,9 +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 = (
p.frac_tax_payroll[: p.T] * iit_payroll_tax_revenue
)
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 Expand Up @@ -446,6 +451,63 @@ def revenue(
)


def get_payroll_tax_revenue(w, L, iit_payroll_tax_revenue, p, method):
r"""
Calculate aggregate payroll tax revenue.

How payroll tax revenue is computed depends on how the user has
chosen to represent payroll taxes in the model. If payroll taxes
are included directly through the ``tau_payroll`` parameter, then
revenue is the payroll tax rate times aggregate labor income:

.. math::
PR_{t} = \tau^{p}_{t}w_{t}L_{t}

Otherwise, payroll taxes are assumed to be embedded in the estimated
income and payroll tax functions (the default), and payroll tax
revenue is separated out as a fraction ``frac_tax_payroll`` of the
combined income and payroll tax revenue. These two calculations are
identical when ``tau_payroll`` is zero.

Args:
w (array_like): the real wage rate
L (array_like): aggregate labor by industry
iit_payroll_tax_revenue (array_like): aggregate income and
payroll tax revenue
p (OG-Core Specifications object): model parameters
method (str): adjusts calculation dimensions based on 'SS' or
'TPI'

Returns:
payroll_tax_revenue (array_like): aggregate payroll tax revenue

"""
if np.any(p.tau_payroll != 0):
# Payroll taxes are modeled explicitly via tau_payroll, so
# revenue is the payroll tax rate times aggregate labor income
# (summing labor across industries).
L_total = L.sum(-1)
if method == "SS":
payroll_tax_revenue = p.tau_payroll[-1] * w * L_total
else: # TPI
payroll_tax_revenue = (
p.tau_payroll[: p.T] * w[: p.T] * L_total[: p.T]
)
else:
# Payroll taxes are embedded in the income and payroll tax
# functions, so revenue is a fraction of the combined revenue.
if method == "SS":
payroll_tax_revenue = (
p.frac_tax_payroll[-1] * iit_payroll_tax_revenue
)
else: # TPI
payroll_tax_revenue = (
p.frac_tax_payroll[: p.T] * iit_payroll_tax_revenue
)

return payroll_tax_revenue


def get_r_p(r, r_gov, p_m, K_vec, K_g, D, MPKg_vec, p, method):
r"""
Compute the interest rate on the household's portfolio of assets,
Expand Down
223 changes: 131 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.5688319028341413
expected1 = 0.7598207140204407
expected2 = np.array(
[
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,
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,
]
)
expected3 = (
np.array(
[
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,
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,
]
)
- inv_tax_cred_rev3
)
expected4 = 0.5688319028341413
expected4 = 0.7598207140204407
expected5 = np.array(
[
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,
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,
]
)
test_data = [
Expand Down Expand Up @@ -1744,6 +1744,45 @@ def test_revenue(
assert np.allclose(revenue, expected)


def test_get_payroll_tax_revenue():
"""
Test of the aggregates.get_payroll_tax_revenue function.

Checks both ways of representing payroll taxes: embedded in the
income and payroll tax functions (tau_payroll == 0, the default) and
modeled explicitly via the tau_payroll parameter.
"""
p = Specifications()
p.T = 3
iit_payroll_ss = 10.0
iit_payroll_tpi = np.array([10.0, 11.0, 12.0])
w_ss = 1.2
L_ss = np.array([2.0, 3.0]) # labor by industry
w_tpi = np.array([1.0, 1.1, 1.2])
L_tpi = np.array([[1.0, 2.0], [1.5, 2.5], [2.0, 3.0]]) # (T, M)

# Payroll taxes embedded in the tax functions (tau_payroll == 0)
p.tau_payroll = np.zeros(p.T)
p.frac_tax_payroll = np.array([0.5, 0.5, 0.5])
pr_ss = aggr.get_payroll_tax_revenue(w_ss, L_ss, iit_payroll_ss, p, "SS")
assert np.allclose(pr_ss, 0.5 * iit_payroll_ss)
pr_tpi = aggr.get_payroll_tax_revenue(
w_tpi, L_tpi, iit_payroll_tpi, p, "TPI"
)
assert np.allclose(pr_tpi, 0.5 * iit_payroll_tpi)

# Payroll taxes modeled explicitly via tau_payroll
p.tau_payroll = np.array([0.1, 0.2, 0.3])
pr_ss = aggr.get_payroll_tax_revenue(w_ss, L_ss, iit_payroll_ss, p, "SS")
assert np.allclose(pr_ss, 0.3 * w_ss * L_ss.sum())
pr_tpi = aggr.get_payroll_tax_revenue(
w_tpi, L_tpi, iit_payroll_tpi, p, "TPI"
)
assert np.allclose(
pr_tpi, np.array([0.1, 0.2, 0.3]) * w_tpi * L_tpi.sum(-1)
)


test_data = [
(
0.04,
Expand Down