From d48cec27974ee6e363855d714c92a5f387a7c544 Mon Sep 17 00:00:00 2001 From: "martin.holmer@gmail.com" Date: Wed, 24 Jul 2024 15:23:13 -0400 Subject: [PATCH 1/6] Fix CDCC phase-out calculations --- taxcalc.egg-info/PKG-INFO | 7 ------ taxcalc/calcfunctions.py | 41 ++++++++++++++++++--------------- taxcalc/policy_current_law.json | 36 +++++++++++++++++++++++++---- 3 files changed, 53 insertions(+), 31 deletions(-) diff --git a/taxcalc.egg-info/PKG-INFO b/taxcalc.egg-info/PKG-INFO index 49fb2cacb..53bab738d 100644 --- a/taxcalc.egg-info/PKG-INFO +++ b/taxcalc.egg-info/PKG-INFO @@ -19,13 +19,6 @@ Classifier: Programming Language :: Python :: 3.12 Classifier: Topic :: Software Development :: Libraries :: Python Modules Description-Content-Type: text/markdown License-File: LICENSE -Requires-Dist: setuptools -Requires-Dist: numpy -Requires-Dist: pandas -Requires-Dist: bokeh -Requires-Dist: numba -Requires-Dist: requests -Requires-Dist: paramtools | | | | --- | --- | diff --git a/taxcalc/calcfunctions.py b/taxcalc/calcfunctions.py index 7e25332c8..13f9f79b1 100644 --- a/taxcalc/calcfunctions.py +++ b/taxcalc/calcfunctions.py @@ -2136,7 +2136,8 @@ def NetInvIncTax(e00300, e00600, e02000, e26270, c01000, @iterate_jit(nopython=True) def F2441(MARS, earned_p, earned_s, f2441, CDCC_c, e32800, exact, c00100, CDCC_ps, CDCC_ps2, CDCC_crt, CDCC_frt, - CDCC_prt, CDCC_refundable, c05800, e07300, c07180, CDCC_refund): + CDCC_po_step_size, CDCC_po_rate_per_step, CDCC_refundable, + c05800, e07300, c07180, CDCC_refund): """ Calculates Form 2441 child and dependent care expense credit, c07180. @@ -2166,16 +2167,18 @@ def F2441(MARS, earned_p, earned_s, f2441, CDCC_c, e32800, Child/dependent care credit phaseout rate ceiling CDCC_frt: float Child/dependent care credit phaseout rate floor - CDCC_prt: float - Child/dependent care credit phaseout rate + CDCC_po_step_size: float + Child/dependent care credit phaseout AGI step size + CDCC_po_rate_per_step: float + Child/dependent care credit phaseout rate per step size + CDCC_refund: bool + Indicator for whether CDCC is refundable c05800: float Total (regular + AMT) income tax liability before credits e07300: float Foreign tax credit from Form 1116 c07180: float Credit for child and dependent care expenses from Form 2441 - CDCC_refund: bool - Indicator for whether CDCC is refundable Returns ------- @@ -2192,22 +2195,22 @@ def F2441(MARS, earned_p, earned_s, f2441, CDCC_c, e32800, else: c32890 = earned_p c33000 = max(0., min(c32800, min(c32880, c32890))) - # credit is limited by AGI-related fraction + # credit rate is limited at high AGI + # ... first phase-down from CDCC_crt to CDCC_frt + steps_fractional = max(0., c00100 - CDCC_ps) / CDCC_po_step_size if exact == 1: # exact calculation as on tax forms - # first phase-down from 35 to 20 percent - tratio1 = math.ceil(max(((c00100 - CDCC_ps) * CDCC_prt), 0.)) - crate = max(CDCC_frt, CDCC_crt - min(CDCC_crt - CDCC_frt, tratio1)) - # second phase-down from 20 percent to zero - if c00100 > CDCC_ps2: - tratio2 = math.ceil(max(((c00100 - CDCC_ps2) * CDCC_prt), 0.)) - crate = max(0., CDCC_frt - min(CDCC_frt, tratio2)) + steps = math.ceil(steps_fractional) else: - crate = max(CDCC_frt, CDCC_crt - - max(((c00100 - CDCC_ps) * CDCC_prt), 0.)) - if c00100 > CDCC_ps2: - crate = max(0., CDCC_frt - - max(((c00100 - CDCC_ps2) * CDCC_prt), 0.)) - + steps = steps_fractional + crate = max(CDCC_frt, CDCC_crt - steps * CDCC_po_rate_per_step) + # ... second phase-down from CDCC_frt to zero + if c00100 > CDCC_ps2: + steps_fractional = (c00100 - CDCC_ps2) / CDCC_po_step_size + if exact == 1: # exact calculation as on tax forms + steps = math.ceil(steps_fractional) + else: + steps = steps_fractional + crate = max(0., CDCC_frt - steps * CDCC_po_rate_per_step) c33200 = c33000 * crate # credit is limited by tax liability if not refundable if CDCC_refundable: diff --git a/taxcalc/policy_current_law.json b/taxcalc/policy_current_law.json index 68117a77f..da519abec 100644 --- a/taxcalc/policy_current_law.json +++ b/taxcalc/policy_current_law.json @@ -16461,10 +16461,36 @@ "cps": true } }, - "CDCC_prt": { - "title": "Child & dependent care credit phaseout rate", - "description": "The CDCC credit rate is reduced by this many percentage points for each dollar of AGI over the phase-out thresholds.", - "notes": "In the law, the credit rate is reduced by 1 percentage point for every $2,000 of AGI over the limit. 0.01 / 2000 = 0.000005", + "CDCC_po_step_size": { + "title": "Child & dependent care credit phaseout step size", + "description": "The CDCC credit rate is reduced by CDCC_po_rate_per_step for each step (including fractional steps) above the phase-out start thresholds.", + "notes": "In the law, the credit rate is reduced by 1 percentage point for every $2,000 of AGI over the phase-out start thresholds", + "section_1": "Nonrefundable Credits", + "section_2": "Child And Dependent Care", + "indexable": true, + "indexed": false, + "type": "float", + "value": [ + { + "year": 2013, + "value": 2000 + } + ], + "validators": { + "range": { + "min": 0, + "max": 9e99 + } + }, + "compatible_data": { + "puf": true, + "cps": true + } + }, + "CDCC_po_rate_per_step": { + "title": "Child & dependent care credit phaseout rate per step", + "description": "The CDCC credit rate is reduced by this rate for for each step (including fractional steps) above the phase-out start thresholds.", + "notes": "In the law, the credit rate is reduced by 1 percentage point for every $2,000 of AGI over the phase-out start thresholds", "section_1": "Nonrefundable Credits", "section_2": "Child And Dependent Care", "indexable": false, @@ -16473,7 +16499,7 @@ "value": [ { "year": 2013, - "value": 5e-06 + "value": 0.01 } ], "validators": { From 294bdaf76174ea137680dd5451b3b99e1d84562e Mon Sep 17 00:00:00 2001 From: "martin.holmer@gmail.com" Date: Wed, 24 Jul 2024 15:41:53 -0400 Subject: [PATCH 2/6] Make RECID be first dump output variable --- taxcalc/taxcalcio.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/taxcalc/taxcalcio.py b/taxcalc/taxcalcio.py index ee99e5ec5..b0ddc5b95 100644 --- a/taxcalc/taxcalcio.py +++ b/taxcalc/taxcalcio.py @@ -518,6 +518,9 @@ def write_output_file(self, output_dump, dump_varset, self.calc, dump_varset, mtr_inctax, mtr_paytax ) column_order = sorted(outdf.columns) + # place RECID at start of column_order list + column_order.remove('RECID') + column_order.insert(0, 'RECID') else: outdf = self.minimal_output() column_order = outdf.columns From 10b3ab2a37c80865becfcbab4193b145ccf6fcdf Mon Sep 17 00:00:00 2001 From: "martin.holmer@gmail.com" Date: Wed, 24 Jul 2024 16:26:23 -0400 Subject: [PATCH 3/6] Fix test_output_options in test_taxcalcio.py module --- taxcalc/tests/test_taxcalcio.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/taxcalc/tests/test_taxcalcio.py b/taxcalc/tests/test_taxcalcio.py index 1f06a9e6e..edf1793e0 100644 --- a/taxcalc/tests/test_taxcalcio.py +++ b/taxcalc/tests/test_taxcalcio.py @@ -436,7 +436,7 @@ def test_output_options(reformfile1, assumpfile1): # --dump output with partial dump try: tcio.analyze(writing_output_file=True, - dump_varset=set(['combined']), + dump_varset=set(['RECID', 'combined']), output_dump=True) except Exception: # pylint: disable=broad-except if os.path.isfile(outfilepath): From 7dcb14bed6929ad0f41a06aff10d0b20b5e48b35 Mon Sep 17 00:00:00 2001 From: "martin.holmer@gmail.com" Date: Wed, 24 Jul 2024 16:59:33 -0400 Subject: [PATCH 4/6] Generalize putting RECID at beginning of dump output variable list --- taxcalc/taxcalcio.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/taxcalc/taxcalcio.py b/taxcalc/taxcalcio.py index b0ddc5b95..d6f6e920a 100644 --- a/taxcalc/taxcalcio.py +++ b/taxcalc/taxcalcio.py @@ -519,7 +519,8 @@ def write_output_file(self, output_dump, dump_varset, ) column_order = sorted(outdf.columns) # place RECID at start of column_order list - column_order.remove('RECID') + if 'RECID' in column_order: + column_order.remove('RECID') column_order.insert(0, 'RECID') else: outdf = self.minimal_output() From 4e167bae49007084fddc9fefd6059fc271a4f020 Mon Sep 17 00:00:00 2001 From: "martin.holmer@gmail.com" Date: Wed, 24 Jul 2024 17:04:43 -0400 Subject: [PATCH 5/6] Revert change in test_taxcalcio.py module --- taxcalc/tests/test_taxcalcio.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/taxcalc/tests/test_taxcalcio.py b/taxcalc/tests/test_taxcalcio.py index edf1793e0..1f06a9e6e 100644 --- a/taxcalc/tests/test_taxcalcio.py +++ b/taxcalc/tests/test_taxcalcio.py @@ -436,7 +436,7 @@ def test_output_options(reformfile1, assumpfile1): # --dump output with partial dump try: tcio.analyze(writing_output_file=True, - dump_varset=set(['RECID', 'combined']), + dump_varset=set(['combined']), output_dump=True) except Exception: # pylint: disable=broad-except if os.path.isfile(outfilepath): From f272eeea3094598a7cedc81c1b28bf701ef5b168 Mon Sep 17 00:00:00 2001 From: "martin.holmer@gmail.com" Date: Wed, 24 Jul 2024 17:52:41 -0400 Subject: [PATCH 6/6] Revise test to include RECID in dump output variable list --- taxcalc/taxcalcio.py | 4 ++-- taxcalc/tests/test_taxcalcio.py | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/taxcalc/taxcalcio.py b/taxcalc/taxcalcio.py index d6f6e920a..5296de83c 100644 --- a/taxcalc/taxcalcio.py +++ b/taxcalc/taxcalcio.py @@ -519,8 +519,8 @@ def write_output_file(self, output_dump, dump_varset, ) column_order = sorted(outdf.columns) # place RECID at start of column_order list - if 'RECID' in column_order: - column_order.remove('RECID') + assert 'RECID' in column_order, 'RECID not in dump output list' + column_order.remove('RECID') column_order.insert(0, 'RECID') else: outdf = self.minimal_output() diff --git a/taxcalc/tests/test_taxcalcio.py b/taxcalc/tests/test_taxcalcio.py index 1f06a9e6e..edf1793e0 100644 --- a/taxcalc/tests/test_taxcalcio.py +++ b/taxcalc/tests/test_taxcalcio.py @@ -436,7 +436,7 @@ def test_output_options(reformfile1, assumpfile1): # --dump output with partial dump try: tcio.analyze(writing_output_file=True, - dump_varset=set(['combined']), + dump_varset=set(['RECID', 'combined']), output_dump=True) except Exception: # pylint: disable=broad-except if os.path.isfile(outfilepath):