From 5eb5fb75b9112a9ebba1b0d2f97d6e9a729532a4 Mon Sep 17 00:00:00 2001 From: PavelMakarchuk Date: Mon, 30 Mar 2026 20:11:54 +0200 Subject: [PATCH 1/3] Fix CTC v22/v23 output mapping to correctly split nonrefundable and refundable portions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Previously v22 mapped to ctc_value (total CTC) and v23 was unimplemented. Per TAXSIM-35 docs, v22 is "Child Tax Credit (as adjusted)" (nonrefundable) and v23 is "Additional Child Tax Credit" (refundable). Now: v22 → non_refundable_ctc, v23 → refundable_ctc. Co-Authored-By: Claude Opus 4.6 --- changelog.d/fix-ctc-v22-v23.fixed.md | 1 + dashboard/public/config-data.json | 2 +- ...olicyengine_taxsim_household_with_dependent_output.csv | 4 ++-- ...xsim_household_with_dependent_single_parent_output.csv | 4 ++-- output/policyengine_taxsim_joint_household_output.csv | 6 +++--- output/policyengine_taxsim_single_household_output.csv | 6 +++--- policyengine_taxsim/config/variable_mappings.yaml | 8 ++++---- 7 files changed, 16 insertions(+), 15 deletions(-) create mode 100644 changelog.d/fix-ctc-v22-v23.fixed.md diff --git a/changelog.d/fix-ctc-v22-v23.fixed.md b/changelog.d/fix-ctc-v22-v23.fixed.md new file mode 100644 index 0000000..1d3334e --- /dev/null +++ b/changelog.d/fix-ctc-v22-v23.fixed.md @@ -0,0 +1 @@ +Fix CTC v22/v23 output mapping to correctly split nonrefundable and refundable portions. diff --git a/dashboard/public/config-data.json b/dashboard/public/config-data.json index d424d0d..03c23e4 100644 --- a/dashboard/public/config-data.json +++ b/dashboard/public/config-data.json @@ -280,5 +280,5 @@ } ] }, - "lastUpdated": "2026-03-24T15:14:08.434Z" + "lastUpdated": "2026-03-30T13:59:03.214Z" } \ No newline at end of file diff --git a/output/policyengine_taxsim_household_with_dependent_output.csv b/output/policyengine_taxsim_household_with_dependent_output.csv index 0c93453..f41873b 100644 --- a/output/policyengine_taxsim_household_with_dependent_output.csv +++ b/output/policyengine_taxsim_household_with_dependent_output.csv @@ -1,2 +1,2 @@ -taxsimid,year,state,fiitax,siitax,fica,tfica,v10,v11,v12,v13,v14,v17,qbid,niit,v18,v19,v22,v24,v25,v26,v27,v28,v29,v32,v34,v35,v36,v37,v38,v39,v40,v42,v43,v44,cares,frate,srate -6,2023,39,3956.0,2486.7,0.0,6196.5,81000.0,0.0,0.0,27700.0,0.0,2486.7,0.0,0.0,53300.0,5956.0,2000.0,0.0,0.0,81000.0,0.0,5956.0,6196.5,81000.0,0.0,0.0,81000.0,0.0,0.0,0.0,0.0,0.0,0.0,1174.5,0.0,12.0,3.0701 +taxsimid,year,state,fiitax,siitax,fica,tfica,v10,v11,v12,v13,v14,v17,qbid,niit,v18,v19,v22,v23,v24,v25,v26,v27,v28,v29,v32,v34,v35,v36,v37,v38,v39,v40,v42,v43,v44,cares,srate,frate +6,2023,39,3956.0,2486.7,0.0,6196.5,81000.0,0.0,0.0,27700.0,0.0,2486.7,0.0,0.0,53300.0,5956.0,2000.0,0.0,0.0,0.0,81000.0,0.0,5956.0,6196.5,81000.0,0.0,0.0,81000.0,0.0,0.0,0.0,0.0,0.0,0.0,1174.5,0.0,3.0701,12.0 diff --git a/output/policyengine_taxsim_household_with_dependent_single_parent_output.csv b/output/policyengine_taxsim_household_with_dependent_single_parent_output.csv index 2c3932c..c521f33 100644 --- a/output/policyengine_taxsim_household_with_dependent_single_parent_output.csv +++ b/output/policyengine_taxsim_household_with_dependent_single_parent_output.csv @@ -1,2 +1,2 @@ -taxsimid,year,state,fiitax,siitax,fica,tfica,v10,v11,v12,v13,v14,v17,qbid,niit,v18,v19,v22,v24,v25,v26,v27,v28,v29,v32,v34,v35,v36,v37,v38,v39,v40,v42,v43,v44,cares,srate,frate -6,2023,39,4945.0,2486.7,0.0,6196.5,81000.0,0.0,0.0,20800.0,0.0,2486.7,0.0,0.0,60200.0,6945.0,2000.0,0.0,0.0,81000.0,0.0,6945.0,6196.5,81000.0,0.0,0.0,81000.0,0.0,0.0,0.0,0.0,0.0,0.0,1174.5,0.0,3.0701,22.0 +taxsimid,year,state,fiitax,siitax,fica,tfica,v10,v11,v12,v13,v14,v17,qbid,niit,v18,v19,v22,v23,v24,v25,v26,v27,v28,v29,v32,v34,v35,v36,v37,v38,v39,v40,v42,v43,v44,cares,frate,srate +6,2023,39,4945.0,2486.7,0.0,6196.5,81000.0,0.0,0.0,20800.0,0.0,2486.7,0.0,0.0,60200.0,6945.0,2000.0,0.0,0.0,0.0,81000.0,0.0,6945.0,6196.5,81000.0,0.0,0.0,81000.0,0.0,0.0,0.0,0.0,0.0,0.0,1174.5,0.0,22.0,3.0701 diff --git a/output/policyengine_taxsim_joint_household_output.csv b/output/policyengine_taxsim_joint_household_output.csv index aa29fb9..20e402e 100644 --- a/output/policyengine_taxsim_joint_household_output.csv +++ b/output/policyengine_taxsim_joint_household_output.csv @@ -1,3 +1,3 @@ -taxsimid,year,state,fiitax,siitax,fica,tfica,v10,v11,v12,v13,v14,v17,qbid,niit,v18,v19,v22,v24,v25,v26,v27,v28,v29,v32,v34,v35,v36,v37,v38,v39,v40,v42,v43,v44,cares,srate,frate -999,2023,44,1716.0,0.0,0.0,6043.5,79000.0,0.0,0.0,27700.0,0.0,1927.2,0.0,0.0,51300.0,5716.0,4000.0,0.0,0.0,79000.0,0.0,5716.0,6043.5,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,1145.5,0.0,0.0,12.0 -11,2023,44,1476.0,0.0,0.0,5890.5,77000.0,0.0,0.0,27700.0,0.0,1927.2,0.0,0.0,49300.0,5476.0,4000.0,0.0,0.0,77000.0,0.0,5476.0,5890.5,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,1116.5,0.0,0.0,12.0 +taxsimid,year,state,fiitax,siitax,fica,tfica,v10,v11,v12,v13,v14,v17,qbid,niit,v18,v19,v22,v23,v24,v25,v26,v27,v28,v29,v32,v34,v35,v36,v37,v38,v39,v40,v42,v43,v44,cares,frate,srate +999,2023,44,1716.0,0.0,0.0,6043.5,79000.0,0.0,0.0,27700.0,0.0,1927.2,0.0,0.0,51300.0,5716.0,4000.0,0.0,0.0,0.0,79000.0,0.0,5716.0,6043.5,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,1145.5,0.0,12.0,0.0 +11,2023,44,1476.0,0.0,0.0,5890.5,77000.0,0.0,0.0,27700.0,0.0,1927.2,0.0,0.0,49300.0,5476.0,4000.0,0.0,0.0,0.0,77000.0,0.0,5476.0,5890.5,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,1116.5,0.0,12.0,0.0 diff --git a/output/policyengine_taxsim_single_household_output.csv b/output/policyengine_taxsim_single_household_output.csv index f14a873..9160a03 100644 --- a/output/policyengine_taxsim_single_household_output.csv +++ b/output/policyengine_taxsim_single_household_output.csv @@ -1,3 +1,3 @@ -taxsimid,year,state,fiitax,siitax,fica,tfica,v10,v11,v12,v13,v14,v17,qbid,niit,v18,v19,v22,v24,v25,v26,v27,v28,v29,v32,v34,v35,v36,v37,v38,v39,v40,v42,v43,v44,cares,srate,frate -999,2021,3,2775.0,1008.87,0.0,3748.5,49000.0,0.0,0.0,12550.0,0.0,1020.0,0.0,0.0,36450.0,4175.0,0.0,0.0,0.0,49000.0,0.0,4175.0,3748.5,49000.0,12550.0,1020.0,36450.0,0.0,0.0,0.0,0.0,0.0,0.0,710.5,1400.0,3.34,12.0 -11,2021,3,2535.0,942.07,0.0,3595.5,47000.0,0.0,0.0,12550.0,0.0,1020.0,0.0,0.0,34450.0,3935.0,0.0,0.0,0.0,47000.0,0.0,3935.0,3595.5,47000.0,12550.0,1020.0,34450.0,0.0,0.0,0.0,0.0,0.0,0.0,681.5,1400.0,3.34,12.0 +taxsimid,year,state,fiitax,siitax,fica,tfica,v10,v11,v12,v13,v14,v17,qbid,niit,v18,v19,v22,v23,v24,v25,v26,v27,v28,v29,v32,v34,v35,v36,v37,v38,v39,v40,v42,v43,v44,cares,srate,frate +999,2021,3,2775.0,1008.87,0.0,3748.5,49000.0,0.0,0.0,12550.0,0.0,1020.0,0.0,0.0,36450.0,4175.0,0.0,0.0,0.0,0.0,49000.0,0.0,4175.0,3748.5,49000.0,12550.0,1020.0,36450.0,0.0,0.0,0.0,0.0,0.0,0.0,710.5,1400.0,3.34,12.0 +11,2021,3,2535.0,942.07,0.0,3595.5,47000.0,0.0,0.0,12550.0,0.0,1020.0,0.0,0.0,34450.0,3935.0,0.0,0.0,0.0,0.0,47000.0,0.0,3935.0,3595.5,47000.0,12550.0,1020.0,34450.0,0.0,0.0,0.0,0.0,0.0,0.0,681.5,1400.0,3.34,12.0 diff --git a/policyengine_taxsim/config/variable_mappings.yaml b/policyengine_taxsim/config/variable_mappings.yaml index 3ac6306..f3233d8 100644 --- a/policyengine_taxsim/config/variable_mappings.yaml +++ b/policyengine_taxsim/config/variable_mappings.yaml @@ -249,7 +249,7 @@ policyengine_to_taxsim: full_text_group: "Federal Tax Calculation" group_column: 1 v22: - variable: ctc_value + variable: non_refundable_ctc implemented: true idtl: - full: 2 @@ -259,12 +259,12 @@ policyengine_to_taxsim: full_text_group: "Federal Tax Calculation" group_column: 1 v23: - variable: na_pe - implemented: false + variable: refundable_ctc + implemented: true idtl: - full: 2 - full_text: 5 - text_description: "Reserved" + text_description: "Additional Child Tax Credit" group_order: 3 full_text_group: "Federal Tax Calculation" group_column: 1 From fdc43b428a324dddcbff365a5ce6c9c101145eb9 Mon Sep 17 00:00:00 2001 From: PavelMakarchuk Date: Tue, 31 Mar 2026 12:16:46 +0200 Subject: [PATCH 2/3] Revert "Fix CTC v22/v23 output mapping to correctly split nonrefundable and refundable portions" This reverts commit 5eb5fb75b9112a9ebba1b0d2f97d6e9a729532a4. --- changelog.d/fix-ctc-v22-v23.fixed.md | 1 - dashboard/public/config-data.json | 2 +- ...olicyengine_taxsim_household_with_dependent_output.csv | 4 ++-- ...xsim_household_with_dependent_single_parent_output.csv | 4 ++-- output/policyengine_taxsim_joint_household_output.csv | 6 +++--- output/policyengine_taxsim_single_household_output.csv | 6 +++--- policyengine_taxsim/config/variable_mappings.yaml | 8 ++++---- 7 files changed, 15 insertions(+), 16 deletions(-) delete mode 100644 changelog.d/fix-ctc-v22-v23.fixed.md diff --git a/changelog.d/fix-ctc-v22-v23.fixed.md b/changelog.d/fix-ctc-v22-v23.fixed.md deleted file mode 100644 index 1d3334e..0000000 --- a/changelog.d/fix-ctc-v22-v23.fixed.md +++ /dev/null @@ -1 +0,0 @@ -Fix CTC v22/v23 output mapping to correctly split nonrefundable and refundable portions. diff --git a/dashboard/public/config-data.json b/dashboard/public/config-data.json index 03c23e4..d424d0d 100644 --- a/dashboard/public/config-data.json +++ b/dashboard/public/config-data.json @@ -280,5 +280,5 @@ } ] }, - "lastUpdated": "2026-03-30T13:59:03.214Z" + "lastUpdated": "2026-03-24T15:14:08.434Z" } \ No newline at end of file diff --git a/output/policyengine_taxsim_household_with_dependent_output.csv b/output/policyengine_taxsim_household_with_dependent_output.csv index f41873b..0c93453 100644 --- a/output/policyengine_taxsim_household_with_dependent_output.csv +++ b/output/policyengine_taxsim_household_with_dependent_output.csv @@ -1,2 +1,2 @@ -taxsimid,year,state,fiitax,siitax,fica,tfica,v10,v11,v12,v13,v14,v17,qbid,niit,v18,v19,v22,v23,v24,v25,v26,v27,v28,v29,v32,v34,v35,v36,v37,v38,v39,v40,v42,v43,v44,cares,srate,frate -6,2023,39,3956.0,2486.7,0.0,6196.5,81000.0,0.0,0.0,27700.0,0.0,2486.7,0.0,0.0,53300.0,5956.0,2000.0,0.0,0.0,0.0,81000.0,0.0,5956.0,6196.5,81000.0,0.0,0.0,81000.0,0.0,0.0,0.0,0.0,0.0,0.0,1174.5,0.0,3.0701,12.0 +taxsimid,year,state,fiitax,siitax,fica,tfica,v10,v11,v12,v13,v14,v17,qbid,niit,v18,v19,v22,v24,v25,v26,v27,v28,v29,v32,v34,v35,v36,v37,v38,v39,v40,v42,v43,v44,cares,frate,srate +6,2023,39,3956.0,2486.7,0.0,6196.5,81000.0,0.0,0.0,27700.0,0.0,2486.7,0.0,0.0,53300.0,5956.0,2000.0,0.0,0.0,81000.0,0.0,5956.0,6196.5,81000.0,0.0,0.0,81000.0,0.0,0.0,0.0,0.0,0.0,0.0,1174.5,0.0,12.0,3.0701 diff --git a/output/policyengine_taxsim_household_with_dependent_single_parent_output.csv b/output/policyengine_taxsim_household_with_dependent_single_parent_output.csv index c521f33..2c3932c 100644 --- a/output/policyengine_taxsim_household_with_dependent_single_parent_output.csv +++ b/output/policyengine_taxsim_household_with_dependent_single_parent_output.csv @@ -1,2 +1,2 @@ -taxsimid,year,state,fiitax,siitax,fica,tfica,v10,v11,v12,v13,v14,v17,qbid,niit,v18,v19,v22,v23,v24,v25,v26,v27,v28,v29,v32,v34,v35,v36,v37,v38,v39,v40,v42,v43,v44,cares,frate,srate -6,2023,39,4945.0,2486.7,0.0,6196.5,81000.0,0.0,0.0,20800.0,0.0,2486.7,0.0,0.0,60200.0,6945.0,2000.0,0.0,0.0,0.0,81000.0,0.0,6945.0,6196.5,81000.0,0.0,0.0,81000.0,0.0,0.0,0.0,0.0,0.0,0.0,1174.5,0.0,22.0,3.0701 +taxsimid,year,state,fiitax,siitax,fica,tfica,v10,v11,v12,v13,v14,v17,qbid,niit,v18,v19,v22,v24,v25,v26,v27,v28,v29,v32,v34,v35,v36,v37,v38,v39,v40,v42,v43,v44,cares,srate,frate +6,2023,39,4945.0,2486.7,0.0,6196.5,81000.0,0.0,0.0,20800.0,0.0,2486.7,0.0,0.0,60200.0,6945.0,2000.0,0.0,0.0,81000.0,0.0,6945.0,6196.5,81000.0,0.0,0.0,81000.0,0.0,0.0,0.0,0.0,0.0,0.0,1174.5,0.0,3.0701,22.0 diff --git a/output/policyengine_taxsim_joint_household_output.csv b/output/policyengine_taxsim_joint_household_output.csv index 20e402e..aa29fb9 100644 --- a/output/policyengine_taxsim_joint_household_output.csv +++ b/output/policyengine_taxsim_joint_household_output.csv @@ -1,3 +1,3 @@ -taxsimid,year,state,fiitax,siitax,fica,tfica,v10,v11,v12,v13,v14,v17,qbid,niit,v18,v19,v22,v23,v24,v25,v26,v27,v28,v29,v32,v34,v35,v36,v37,v38,v39,v40,v42,v43,v44,cares,frate,srate -999,2023,44,1716.0,0.0,0.0,6043.5,79000.0,0.0,0.0,27700.0,0.0,1927.2,0.0,0.0,51300.0,5716.0,4000.0,0.0,0.0,0.0,79000.0,0.0,5716.0,6043.5,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,1145.5,0.0,12.0,0.0 -11,2023,44,1476.0,0.0,0.0,5890.5,77000.0,0.0,0.0,27700.0,0.0,1927.2,0.0,0.0,49300.0,5476.0,4000.0,0.0,0.0,0.0,77000.0,0.0,5476.0,5890.5,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,1116.5,0.0,12.0,0.0 +taxsimid,year,state,fiitax,siitax,fica,tfica,v10,v11,v12,v13,v14,v17,qbid,niit,v18,v19,v22,v24,v25,v26,v27,v28,v29,v32,v34,v35,v36,v37,v38,v39,v40,v42,v43,v44,cares,srate,frate +999,2023,44,1716.0,0.0,0.0,6043.5,79000.0,0.0,0.0,27700.0,0.0,1927.2,0.0,0.0,51300.0,5716.0,4000.0,0.0,0.0,79000.0,0.0,5716.0,6043.5,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,1145.5,0.0,0.0,12.0 +11,2023,44,1476.0,0.0,0.0,5890.5,77000.0,0.0,0.0,27700.0,0.0,1927.2,0.0,0.0,49300.0,5476.0,4000.0,0.0,0.0,77000.0,0.0,5476.0,5890.5,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,1116.5,0.0,0.0,12.0 diff --git a/output/policyengine_taxsim_single_household_output.csv b/output/policyengine_taxsim_single_household_output.csv index 9160a03..f14a873 100644 --- a/output/policyengine_taxsim_single_household_output.csv +++ b/output/policyengine_taxsim_single_household_output.csv @@ -1,3 +1,3 @@ -taxsimid,year,state,fiitax,siitax,fica,tfica,v10,v11,v12,v13,v14,v17,qbid,niit,v18,v19,v22,v23,v24,v25,v26,v27,v28,v29,v32,v34,v35,v36,v37,v38,v39,v40,v42,v43,v44,cares,srate,frate -999,2021,3,2775.0,1008.87,0.0,3748.5,49000.0,0.0,0.0,12550.0,0.0,1020.0,0.0,0.0,36450.0,4175.0,0.0,0.0,0.0,0.0,49000.0,0.0,4175.0,3748.5,49000.0,12550.0,1020.0,36450.0,0.0,0.0,0.0,0.0,0.0,0.0,710.5,1400.0,3.34,12.0 -11,2021,3,2535.0,942.07,0.0,3595.5,47000.0,0.0,0.0,12550.0,0.0,1020.0,0.0,0.0,34450.0,3935.0,0.0,0.0,0.0,0.0,47000.0,0.0,3935.0,3595.5,47000.0,12550.0,1020.0,34450.0,0.0,0.0,0.0,0.0,0.0,0.0,681.5,1400.0,3.34,12.0 +taxsimid,year,state,fiitax,siitax,fica,tfica,v10,v11,v12,v13,v14,v17,qbid,niit,v18,v19,v22,v24,v25,v26,v27,v28,v29,v32,v34,v35,v36,v37,v38,v39,v40,v42,v43,v44,cares,srate,frate +999,2021,3,2775.0,1008.87,0.0,3748.5,49000.0,0.0,0.0,12550.0,0.0,1020.0,0.0,0.0,36450.0,4175.0,0.0,0.0,0.0,49000.0,0.0,4175.0,3748.5,49000.0,12550.0,1020.0,36450.0,0.0,0.0,0.0,0.0,0.0,0.0,710.5,1400.0,3.34,12.0 +11,2021,3,2535.0,942.07,0.0,3595.5,47000.0,0.0,0.0,12550.0,0.0,1020.0,0.0,0.0,34450.0,3935.0,0.0,0.0,0.0,47000.0,0.0,3935.0,3595.5,47000.0,12550.0,1020.0,34450.0,0.0,0.0,0.0,0.0,0.0,0.0,681.5,1400.0,3.34,12.0 diff --git a/policyengine_taxsim/config/variable_mappings.yaml b/policyengine_taxsim/config/variable_mappings.yaml index f3233d8..3ac6306 100644 --- a/policyengine_taxsim/config/variable_mappings.yaml +++ b/policyengine_taxsim/config/variable_mappings.yaml @@ -249,7 +249,7 @@ policyengine_to_taxsim: full_text_group: "Federal Tax Calculation" group_column: 1 v22: - variable: non_refundable_ctc + variable: ctc_value implemented: true idtl: - full: 2 @@ -259,12 +259,12 @@ policyengine_to_taxsim: full_text_group: "Federal Tax Calculation" group_column: 1 v23: - variable: refundable_ctc - implemented: true + variable: na_pe + implemented: false idtl: - full: 2 - full_text: 5 - text_description: "Additional Child Tax Credit" + text_description: "Reserved" group_order: 3 full_text_group: "Federal Tax Calculation" group_column: 1 From 1f49197fa20353ca22e70867b51057e6634c2a3c Mon Sep 17 00:00:00 2001 From: PavelMakarchuk Date: Tue, 31 Mar 2026 12:23:37 +0200 Subject: [PATCH 3/3] Add statefip (FIPS code) input support MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit TAXSIM-35 accepts both SOI codes (state column) and FIPS codes (statefip column). When statefip is provided and state is 0 or missing, automatically convert FIPS→SOI. This prevents users from accidentally passing FIPS codes in the state column and getting wrong state tax results. Co-Authored-By: Claude Opus 4.6 --- changelog.d/add-statefip-support.added.md | 1 + dashboard/src/components/CsvRunner.jsx | 2 +- policyengine_taxsim/api.py | 1 + policyengine_taxsim/core/utils.py | 3 +++ policyengine_taxsim/runners/base_runner.py | 13 +++++++++++++ 5 files changed, 19 insertions(+), 1 deletion(-) create mode 100644 changelog.d/add-statefip-support.added.md diff --git a/changelog.d/add-statefip-support.added.md b/changelog.d/add-statefip-support.added.md new file mode 100644 index 0000000..41f9fbc --- /dev/null +++ b/changelog.d/add-statefip-support.added.md @@ -0,0 +1 @@ +Add statefip (FIPS code) input support, matching TAXSIM-35 behavior. diff --git a/dashboard/src/components/CsvRunner.jsx b/dashboard/src/components/CsvRunner.jsx index 5fd7bdd..3180af0 100644 --- a/dashboard/src/components/CsvRunner.jsx +++ b/dashboard/src/components/CsvRunner.jsx @@ -40,7 +40,7 @@ const SAMPLE_DATASETS = [ const fmt = (n) => Number(n).toLocaleString(); const KNOWN_COLUMNS = new Set([ - 'taxsimid', 'year', 'state', 'mstat', 'page', 'sage', + 'taxsimid', 'year', 'state', 'statefip', 'mstat', 'page', 'sage', 'dependent_exemption', 'depx', 'pwages', 'swages', 'psemp', 'ssemp', 'dividends', 'intrec', 'stcg', 'ltcg', 'otherprop', 'nonprop', 'pensions', 'gssi', diff --git a/policyengine_taxsim/api.py b/policyengine_taxsim/api.py index 818cbc0..e4550b4 100644 --- a/policyengine_taxsim/api.py +++ b/policyengine_taxsim/api.py @@ -49,6 +49,7 @@ "taxsimid", "year", "state", + "statefip", "mstat", "page", "sage", diff --git a/policyengine_taxsim/core/utils.py b/policyengine_taxsim/core/utils.py index 0a29b69..b489a8c 100644 --- a/policyengine_taxsim/core/utils.py +++ b/policyengine_taxsim/core/utils.py @@ -284,3 +284,6 @@ def get_ordinal(n): 50: 55, # Wisconsin 51: 56, # Wyoming } + +# Reverse mapping: FIPS → SOI (for statefip input support) +FIPS_TO_SOI_MAP = {fips: soi for soi, fips in SOI_TO_FIPS_MAP.items()} diff --git a/policyengine_taxsim/runners/base_runner.py b/policyengine_taxsim/runners/base_runner.py index 33889fe..dc490d2 100644 --- a/policyengine_taxsim/runners/base_runner.py +++ b/policyengine_taxsim/runners/base_runner.py @@ -5,8 +5,10 @@ try: from ..core.io import write_output + from ..core.utils import FIPS_TO_SOI_MAP except ImportError: from policyengine_taxsim.core.io import write_output + from policyengine_taxsim.core.utils import FIPS_TO_SOI_MAP class BaseTaxRunner(ABC): @@ -39,6 +41,17 @@ def _validate_input(self): if "year" not in self.input_df.columns: raise ValueError("Input data must contain a 'year' column") + # Convert statefip (FIPS) to state (SOI) per row. + # Matches TAXSIM-35 behavior: for each record, if state==0 + # and statefip is set, convert FIPS→SOI. + if "statefip" in self.input_df.columns: + if "state" not in self.input_df.columns: + self.input_df["state"] = 0 + fips_vals = self.input_df["statefip"].astype(int) + soi_from_fips = fips_vals.map(FIPS_TO_SOI_MAP).fillna(0).astype(int) + use_fips = (self.input_df["state"] == 0) & (fips_vals > 0) + self.input_df.loc[use_fips, "state"] = soi_from_fips[use_fips] + # Auto-assign taxsimid if not present if "taxsimid" not in self.input_df.columns: # Assign sequential IDs starting from 1