diff --git a/UPGRADING.md b/UPGRADING.md new file mode 100644 index 000000000..1ea15b84a --- /dev/null +++ b/UPGRADING.md @@ -0,0 +1,94 @@ +Upgrading to Tax-Calculator 2.0 +=============================== + +Tax-Calculator version 2.0 (and higher) is incompatible with earlier +versions. The tax logic is unchanged, but 2.0 introduces a simplified +and standardized way of specifying tax reforms (or economic assumption +changes). This note explains what you have to do in order to use +Tax-Calculator version 2.0 and higher. + +What you need to do is change the way you specify tax policy reforms. +Obviously, you need to do this only for reforms you want to analyze +using Tax-Calculator 2.0 and higher. + +The details of what to do depend on whether you are in the habit of +specifying reforms in JSON files (as demonstrated in the [user +guide](https://PSLmodels.github.io/Tax-Calculator/index.html) and +[cookbook](https://PSLmodels.github.io/Tax-Calculator/cookbook.html)) +or whether you take a low-level approach and specify reforms as Python +dictionaries directly in your Python scripts (as done in hundreds of +Tax-Calculator tests). + +In both cases you need to do the following: + +1. Remove the leading underscore character from each parameter name. + +2. Remove the brackets around the value of scalar parameters (i.e., + those that do not vary by filing-unit type, number of EITC eligible + children, or type of itemized deduction, and therefore, are just a + single value in any year). + +3. Remove **one** of the double set of brackets around the values of + vector parameters(i.e., those that are not scalar parameters). + This will leave vector parameter values enclosed in just one pair + of brackets. + +4. Only if you are changing whether a parameter is (wage or price) + indexed for inflation, you need to change the trailing `_cpi` + characters to be `-indexed` (making sure to use a dash and not an + underscore). + +5. Only if you are changing CPI offset policy, you need to change the + old parameter name `_cpi_offset` to `CPI_offset`. + +That is all that needs to be done to convert JSON reform (or +assumption) files so that they work with Tax-Calculator 2.0 and +higher. (If you have many JSON reform files that you want to convert, +the first three steps may be able to be automated using the +`Tax-Calculator/new_json.py` script, but be sure to read the short +script before using it. If your JSON reform files are not formatted +in the way extected by the `new_json.py` script, then make the changes +in the above steps by hand editing each JSON file.) + +If you have specified reforms (or changes in economic assumption +parameters) directly in your Python scripts using dictionaries, then +there is another set of changes you need to make to each of those +dictionaries. In older versions of Tax-Calcualtor, each reform +dictionary was structured using a `year : param : value` format. +Here is an example of the old format after making the changes +listed above: + +``` +reform = { + 2020: { + 'SS_Earnings_c': 200000, + 2021: { + 'SS_Earnings_c': 300000, + 'SS_Earnings_c-indexed': False + }, + 2022: { + 'STD': [14000, 28000, 14000, 21000, 28000] + } +} +``` + +Notice that the primary keys in the above dictionary are years and the +secondary keys are parameter names. Beginning with Tax-Calculator +2.0, that order is reversed making the new `param : year : value` +format consistent with the format of the JSON reform files. + +So, the above dictionary needs to be converted (by hand editing) to +the following: + +``` +reform = { + 'SS_Earnings_c': {2020: 200000, + 2021: 300000}, + 'SS_Earnings_c-indexed': {2021: False}, + 'STD': {2022: [14000, 28000, 14000, 21000, 28000]} +} +``` + +If you have any problems or questions converting your reforms for +use with Tax-Calculator 2.0, please raise an issue +[here](https://github.com/PSLmodels/Tax-Calculator/issues). diff --git a/docs/cookbook/recipe00.res b/docs/cookbook/recipe00.res index de4164608..840296908 100644 --- a/docs/cookbook/recipe00.res +++ b/docs/cookbook/recipe00.res @@ -1,46 +1,44 @@ -You loaded data for 2014. -Tax-Calculator startup automatically extrapolated your data to 2014. -You loaded data for 2014. -Tax-Calculator startup automatically extrapolated your data to 2014. REFORM DOCUMENTATION Baseline Growth-Difference Assumption Values by Year: -none: using default baseline growth assumptions +none: using default growth assumptions +Response Growth-Difference Assumption Values by Year: +none: using default growth assumptions Policy Reform Parameter Values by Year: 2020: - _II_em : 1000 + II_em : 1000.0 name: Personal and dependent exemption amount desc: Subtracted from AGI in the calculation of taxable income, per taxpayer and dependent. baseline_value: 0.0 - _II_rt5 : 0.36 + II_rt5 : 0.36 name: Personal income (regular/non-AMT/non-pass-through) tax rate 5 desc: The third highest tax rate, applied to the portion of taxable income below tax bracket 5 and above tax bracket 4. baseline_value: 0.32 - _II_rt6 : 0.39 + II_rt6 : 0.39 name: Personal income (regular/non-AMT/non-pass-through) tax rate 6 desc: The second higher tax rate, applied to the portion of taxable income below tax bracket 6 and above tax bracket 5. baseline_value: 0.35 - _II_rt7 : 0.41 + II_rt7 : 0.41 name: Personal income (regular/non-AMT/non-pass-through) tax rate 7 desc: The tax rate applied to the portion of taxable income below tax bracket 7 and above tax bracket 6. baseline_value: 0.37 - _PT_rt5 : 0.36 + PT_rt5 : 0.36 name: Pass-through income tax rate 5 desc: The third highest tax rate, applied to the portion of income from sole proprietorships, partnerships and S-corporations below tax bracket 5 and above tax bracket 4. baseline_value: 0.32 - _PT_rt6 : 0.39 + PT_rt6 : 0.39 name: Pass-through income tax rate 6 desc: The second higher tax rate, applied to the portion of income from sole proprietorships, partnerships and S-corporations below tax bracket 6 and above tax bracket 5. baseline_value: 0.35 - _PT_rt7 : 0.41 + PT_rt7 : 0.41 name: Pass-through income tax rate 7 desc: The highest tax rate, applied to the portion of income from sole proprietorships, partnerships and S-corporations below tax bracket 7 diff --git a/docs/cookbook/recipe03.res b/docs/cookbook/recipe03.res index 6cfbcb937..c09fa2a97 100644 --- a/docs/cookbook/recipe03.res +++ b/docs/cookbook/recipe03.res @@ -1,5 +1,3 @@ -You loaded data for 2014. -Tax-Calculator startup automatically extrapolated your data to 2014. Filing Units Receiving EITC and Average Positive EITC by AGI Category AGI Category Num(#M) Avg($K) [-9e+99, 1) 0.047 1.244 diff --git a/docs/cookbook/reformA.json b/docs/cookbook/reformA.json index 15ee142aa..93dddc7c8 100644 --- a/docs/cookbook/reformA.json +++ b/docs/cookbook/reformA.json @@ -2,16 +2,16 @@ // from (0.32, 0.35, 0.37) to (0.36, 0.39, 0.41) beginning in 2020. { "policy": { - // raise personal exemption amount and index in subsequent years - "_II_em": {"2020": [1000]}, - // raise tax rates in the top three brackets - // ... raise non-AMT rates on non-pass-through income - "_II_rt5": {"2020": [0.36]}, - "_II_rt6": {"2020": [0.39]}, - "_II_rt7": {"2020": [0.41]}, - // ... raise non-AMT rates on pass-through income - "_PT_rt5": {"2020": [0.36]}, - "_PT_rt6": {"2020": [0.39]}, - "_PT_rt7": {"2020": [0.41]} + // raise personal exemption amount and index in subsequent years + "II_em": {"2020": 1000}, + // raise tax rates in the top three brackets + // ... raise non-AMT rates on non-pass-through income + "II_rt5": {"2020": 0.36}, + "II_rt6": {"2020": 0.39}, + "II_rt7": {"2020": 0.41}, + // ... raise non-AMT rates on pass-through income + "PT_rt5": {"2020": 0.36}, + "PT_rt6": {"2020": 0.39}, + "PT_rt7": {"2020": 0.41} } } diff --git a/docs/cookbook/reformB.json b/docs/cookbook/reformB.json index 028df3da5..199e17d8e 100644 --- a/docs/cookbook/reformB.json +++ b/docs/cookbook/reformB.json @@ -1,7 +1,7 @@ // Raise personal exemption and lower standard deduction beginning in 2020. { "policy": { - "_II_em": {"2020": [2000]}, - "_STD": {"2020": [[6000, 12000, 6000, 9000, 12000]]} + "II_em": {"2020": 2000}, + "STD": {"2020": [6000, 12000, 6000, 9000, 12000]} } } diff --git a/docs/index.htmx b/docs/index.htmx index 57c270005..86b883355 100644 --- a/docs/index.htmx +++ b/docs/index.htmx @@ -640,7 +640,7 @@ the ref3.json file, the content of which is: // after which it continues to be indexed to price inflation. { "policy": { - "_II_em": {"2022": [8000]} + "II_em": {"2022": 8000} } }

diff --git a/docs/make_index.py b/docs/make_index.py index a3ea386ba..f2fa557b6 100644 --- a/docs/make_index.py +++ b/docs/make_index.py @@ -90,25 +90,25 @@ def policy_param_text(pname, param): else: txt += 'False' txt += '
Can Be Inflation Indexed: ' - if param['cpi_inflatable']: + if param['indexable']: txt += 'True' else: txt += 'False' txt += '     Is Inflation Indexed: ' - if param['cpi_inflated']: + if param['indexed']: txt += 'True' else: txt += 'False' txt += '
Value Type: {}'.format(param['value_type']) txt += '
Known Values:' - if len(param['col_label']) > 0: - cols = ', '.join(param['col_label']) + if len(param.get('vi_vals', [])) > 0: + cols = ', '.join(param['vi_name']) txt += '
   for: [{}]'.format(cols) - for cyr, val in zip(param['row_label'], param['value']): + for cyr, val in zip(param['value_yrs'], param['value']): final_cyr = cyr final_val = val txt += '
{}: {}'.format(cyr, val) - if not param['cpi_inflated']: + if not param['indexed']: fcyr = int(final_cyr) if fcyr < Policy.LAST_KNOWN_YEAR: # extrapolate final_val thru Policy.LAST_KNOWN_YEAR if not indexed @@ -212,9 +212,10 @@ def assumption_param_text(pname, ptype, param): Extract info from param for pname of ptype and return as HTML string. """ # pylint: disable=len-as-condition - sec1 = param['section_1'] + sec1 = param.get('section_1', '') if len(sec1) > 0: - txt = '

{} — {}'.format(sec1, param['section_2']) + txt = '

{} — {}'.format(sec1, + param.get('section_2', '')) else: txt = '

{} — {}'.format('Assumption Parameter', ptype.capitalize()) @@ -224,13 +225,13 @@ def assumption_param_text(pname, ptype, param): else: txt += '
Long Name: {}'.format(param['long_name']) txt += '
Description: {}'.format(param['description']) - if len(param['notes']) > 0: + if len(param.get('notes', '')) > 0: txt += '
Notes: {}'.format(param['notes']) txt += '
Default Value:' - if len(param['col_label']) > 0: - cols = ', '.join(param['col_label']) + if len(param.get('vi_vals', [])) > 0: + cols = ', '.join(param['vi_vals']) txt += '
   for: [{}]'.format(cols) - for cyr, val in zip(param['row_label'], param['value']): + for cyr, val in zip(param['value_yrs'], param['value']): txt += '
{}: {}'.format(cyr, val) txt += '
Valid Range:' minval = param['valid_values']['min'] diff --git a/docs/ref3.json b/docs/ref3.json index 9023e2f6a..a6404cdc0 100644 --- a/docs/ref3.json +++ b/docs/ref3.json @@ -2,6 +2,6 @@ // after which it continues to be indexed to price inflation. { "policy": { - "_II_em": {"2022": [8000]} + "II_em": {"2022": 8000} } } diff --git a/new_json.py b/new_json.py index 7c3f7aa4e..8c88942fe 100644 --- a/new_json.py +++ b/new_json.py @@ -1,6 +1,6 @@ """ Command-line tool that converts Tax-Calculator JSON reform/assumption file -from the old (1.x) format to the new (2.0) format. +from the old (1.x) format to the new (2.0+) format. ------------------------------------------------------------------------ WARNING: This program make certain assumptions about how the JSON file is formatted, so it will not work correctly on a JSON file diff --git a/taxcalc/tests/test_policy.py b/taxcalc/tests/test_policy.py index 8365996dc..8c0a8e0ca 100644 --- a/taxcalc/tests/test_policy.py +++ b/taxcalc/tests/test_policy.py @@ -66,7 +66,7 @@ def test_policy_json_content_consistency(): # check entries in vi_vals list vivals = data['vi_vals'] if vivals: - assert set(vivals) == set(expected_vi_vals[data['vi_name']]) + assert vivals == expected_vi_vals[data['vi_name']] # pylint: disable=protected-access,no-member