Skip to content

Commit

Permalink
Merge pull request PSLmodels#2819 from martinholmer/enhance-ppp
Browse files Browse the repository at this point in the history
Thoroughly revise the ppp.py script to make it usable
  • Loading branch information
martinholmer authored Oct 1, 2024
2 parents 6b6674f + 2c8ceb1 commit a6251fc
Show file tree
Hide file tree
Showing 3 changed files with 137 additions and 68 deletions.
12 changes: 9 additions & 3 deletions docs/contributing/RELEASING.md
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
Releasing Tax-Caclculator Packages
==================================

The following outlines the process to release Tax-Calculator Packages on Conda-Forge and creating a new branch to fix a bug in a previous release.
The following outlines the process to release Tax-Calculator Packages
on Conda-Forge and creating a new branch to fix a bug in a previous
release.

Create new `taxcalc` packages
===========================
=============================
```
--> on branch X-Y-Z, edit docs/about/releases.md to finalize X.Y.Z info
Expand All @@ -13,7 +15,11 @@ Create new `taxcalc` packages
--> merge master branch into X-Y-Z branch
--> run `python extend_tcja.py` [update taxcalc/reforms/ext.json if needed]
--> run `python update_pcl.py` [to update policy_current_law.json if needed]
--> run `python ppp.py` [to update policy_current_law.json if needed]
--> run `python extend_tcja.py > ext.json` [to update reforms/ext.json if needed]
--> run `make tctest-jit` [to make sure JIT decorators are not hiding bugs]
Expand Down
185 changes: 123 additions & 62 deletions ppp.py
Original file line number Diff line number Diff line change
@@ -1,77 +1,138 @@
"""
Policy parameter projection script, which calculates policy parameter
values that were changed by TCJA and will revert to their pre-TCJA
values in 2026 (adjusted for inflation). The script should be run
when the inflation factor values change in growfactors.csv.
This policy parameter projection script, which calculates policy
parameter values that were changed by TCJA and will revert to
their pre-TCJA values in 2026 (adjusted for inflation).
USAGE: $ python ppp.py
USAGE: (taxcalc-dev) T-C% python ppp.py
THEN CHECK: % diff pcl.json taxcalc/policy_current_law.json
IF DIFFS OK % mv pcl.json taxcalc/policy_current_law.json
Note: running this script will write the new 2026 parameter values
directly to policy_current_law.json. So, if you want to compare
the new 2026 parameter values with the old 2026 parameter values,
be sure to copy policy_current_law.json before running this script.
WHEN TO USE: use this script to update taxcalc/policy_current_law.json
whenever post-2016 inflation rates in the growfactors.csv files are changed.
"""
import json
import collections

import sys
import re
from pathlib import Path
from taxcalc import Policy

params = Policy()
REVERTING_PARAMS = {
'ALD_BusinessLosses_c': 2029,
'II_em': 2026,
'II_em_ps': 2026,
'STD': 2026,
'ID_AllTaxes_c': 2026,
'ID_ps': 2026,
'II_brk1': 2026,
'II_brk2': 2026,
'II_brk3': 2026,
'II_brk4': 2026,
'II_brk5': 2026,
'II_brk6': 2026,
'II_brk7': 2026,
'PT_brk1': 2026,
'PT_brk2': 2026,
'PT_brk3': 2026,
'PT_brk4': 2026,
'PT_brk5': 2026,
'PT_brk6': 2026,
'PT_brk7': 2026,
'PT_qbid_taxinc_thd': 2026,
'AMT_em': 2026,
'AMT_em_ps': 2026,
'AMT_em_pe': 2026,
}
MARS_VALUES = ['single', 'mjoint', 'mseparate', 'headhh', 'widow']
PYEAR = 2017 # prior year before TCJA first implemented
FYEAR = 2026 # final year in which parameter values revert

# parameters that will revert
long_params = ['II_brk7', 'II_brk6', 'II_brk5', 'II_brk4',
'II_brk3', 'II_brk2', 'II_brk1',
'PT_brk7', 'PT_brk6', 'PT_brk5', 'PT_brk4',
'PT_brk3', 'PT_brk2', 'PT_brk1',
'PT_qbid_taxinc_thd',
'ALD_BusinessLosses_c',
'STD', 'II_em', 'II_em_ps',
'AMT_em', 'AMT_em_ps', 'AMT_em_pe',
'ID_ps', 'ID_AllTaxes_c']

# calculate the inflation factor used to calculate the
# inflation-adjusted 2026 parameter values
FINAL_IFACTOR = 1.0
PYEAR = 2017 # prior year before TCJA first implemented
FYEAR = 2026 # final year in which parameter values revert to
# pre-TCJA values
# construct final-year inflation factor from prior year
# NOTE: pvalue[t+1] = pvalue[t] * ( 1 + irate[t] )
for year in range(PYEAR, FYEAR):
yindex = year - params.start_year
rate = params._inflation_rates[yindex] # pylint: disable=protected-access
FINAL_IFACTOR *= 1 + rate
def cumulative_ifactor():
"""
Return PYEAR-to_FYEAR inflation factor.
"""
pol = Policy()
# calculate the inflation factor used to calculate the
# inflation-adjusted 2026 reverting parameter values
# NOTE: pvalue[t+1] = pvalue[t] * ( 1 + irate[t] )
cum_ifactor = 1.0
for year in range(PYEAR, FYEAR):
# pylint: disable=protected-access
rate = pol._inflation_rates[year - pol.start_year]
cum_ifactor *= 1.0 + rate
return cum_ifactor

long_param_vals = collections.defaultdict(list)

for param in long_params:
vos = params.select_eq(param, year=PYEAR)
# use FINAL_IFACTOR to inflate from 2017 to 2026
for vo in vos:
long_param_vals[param].append(
# create new dict to avoid modifying the original
dict(
vo,
value=min(9e99, round(
vo["value"] * FINAL_IFACTOR, 0)),
year=FYEAR,
def integrate_fragments(fragments):
"""
Integrate specified fragments with the policy_current_law.json text.
"""
p_c_l_path = Path('.') / 'taxcalc' / 'policy_current_law.json'
with open(p_c_l_path, 'r', encoding='utf-8') as ofile:
olines = ofile.readlines()
# pylint: disable=consider-using-with
nfile = open('pcl.json', 'w', encoding='utf-8')
re_param_line = re.compile(r'^\s{4}"(\w+)": {$')
re_ryear26_line = re.compile(r'^\s{16}"year": 2026,$')
re_ryear29_line = re.compile(r'^\s{16}"year": 2029,$')
re_value_line = re.compile(r'^\s{16}"value":')
writing_oline = True
pname_has_fragments = False
for oline in olines:
pmatch = re_param_line.search(oline)
if pmatch:
pname = pmatch.group(1)
pname_has_fragments = pname in fragments
if pname_has_fragments:
if fragments[pname][0]['year'] == 2026:
re_ryear_line = re_ryear26_line
else:
re_ryear_line = re_ryear29_line
idx = -1
elif pname_has_fragments:
ymatch = re_ryear_line.search(oline)
if ymatch:
idx += 1
if pname_has_fragments and idx >= 0:
if re_value_line.search(oline):
writing_oline = False
else:
writing_oline = True
if writing_oline:
nfile.write(oline)
else:
nfile.write(
f' "value": {fragments[pname][idx]["value"]}\n'
)
)
nfile.close()
return 0


def main():
"""
High-level script logic.
"""
ifactor = cumulative_ifactor()

# construct policy_current_law.json revert-year fragments
# for each reverting parameter in a fragments dictionary
fragments = {}
parameters = Policy()
for pname, ryear in REVERTING_PARAMS.items():
vos = parameters.select_eq(pname, year=PYEAR)
frag_list = []
for vo in vos:
rval = min(9e99, round(vo['value'] * ifactor, 0))
frag_list.append({'year': ryear, 'value': rval})
fragments[pname] = frag_list

# call adjust method for new 2026 values
params.adjust(long_param_vals)
params.clear_state()
param_data = params.specification(
meta_data=False, serializable=True, use_state=False, _auto=False)
# integrate fragment info into existing policy_current_law.json
rcode = integrate_fragments(fragments)

# read existing policy_current_law.json
with open('taxcalc/policy_current_law.json', 'r', encoding='utf-8') as pcl_old:
pcl = json.load(pcl_old)
# return exit code returned by integrate_fragments function
return rcode
# end main function code

# replace 2026 values in the pcl dictionary
for param in param_data:
pcl[param]["value"] = param_data[param]

# write new pcl dictionary to policy_current_law.json
with open('taxcalc/policy_current_law.json', 'w', encoding='utf-8') as pcl_new:
json.dump(pcl, pcl_new, indent=4)
pcl_new.write('\n')
if __name__ == '__main__':
sys.exit(main())
8 changes: 5 additions & 3 deletions update_pcl.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,12 @@
This script uses known values of inflation-indexed policy parameters after
2022 to write an updated version of the taxcalc/policy_current_law.json file.
USAGE: (taxcalc-dev) Tax-Calculator% python update_pcl.py
USAGE: (taxcalc-dev) T-C% python update_pcl.py
THEN CHECK: % diff pcl.json taxcalc/policy_current_law.json
IF DIFFS OK: % mv pcl.json taxcalc/policy_current_law.json
THEN: look at differences between the newly written pcl.json file and
the old existing taxcalc/policy_current_law.json file
WHEN TO USE: use this script to update taxcalc/policy_current_law.json
whenever post-2016 inflation rates in the growfactors.csv files are changed.
"""

import os
Expand Down

0 comments on commit a6251fc

Please sign in to comment.