Skip to content

Commit c509d85

Browse files
Zero MD county/local income tax in the emulator to match TAXSIM (#1066)
TAXSIM's Maryland siitax is state-only — it applies no county tax when the input carries no locality (verified: TAXSIM MD siitax = PE's state-only MD tax to the dollar). PE applies MD's residence-based county tax (~2.25-3.20%) to every MD resident, systematically over-stating MD siitax vs TAXSIM. The TAXSIM input has no county, so zero PE's md_local_income_tax_before_credits for MD records to match TAXSIM's coverage. MD is the only state affected: every other local-income-tax state (OH/PA/IN/ KY/MI/NYC/…) requires a locality PE isn't given, so those already read $0. See #1062. Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
1 parent 8eaddca commit c509d85

3 files changed

Lines changed: 68 additions & 0 deletions

File tree

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Zero PolicyEngine's Maryland county/local income tax in the emulator to match TAXSIM, whose MD siitax is state-only (it applies no county tax when the input carries no locality). MD is the only state with a residence-based local tax; other local-tax states already read $0 without a locality.

policyengine_taxsim/runners/policyengine_runner.py

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1193,6 +1193,32 @@ def _run_chunk(self, chunk_df: pd.DataFrame) -> pd.DataFrame:
11931193
),
11941194
)
11951195

1196+
# Maryland county/local income tax: TAXSIM's MD `siitax` is
1197+
# state-only — it applies no county tax when the input carries no
1198+
# locality (verified against the binary: TAXSIM MD siitax equals
1199+
# PE's state-only MD tax to the dollar). PE, by contrast, applies
1200+
# MD's *residence-based* county tax (~2.25-3.20%) to every MD
1201+
# resident even with no county specified, systematically
1202+
# over-stating MD siitax vs TAXSIM. The TAXSIM input has no county,
1203+
# so zero PE's MD local income tax to match TAXSIM's coverage. MD
1204+
# is the only state affected: every other local-income-tax state
1205+
# (OH/PA/IN/KY/MI/NYC/…) requires a locality PE isn't given, so
1206+
# those already compute $0 local and match TAXSIM.
1207+
if "state" in chunk_df.columns and (chunk_df["state"] == 21).any():
1208+
var = "md_local_income_tax_before_credits"
1209+
if var in sim.tax_benefit_system.variables:
1210+
n_md = sim.get_variable_population(var).count
1211+
for year in years:
1212+
sim.set_input(
1213+
variable_name=var,
1214+
value=np.zeros(n_md),
1215+
period=str(
1216+
int(year)
1217+
if isinstance(year, (float, np.floating))
1218+
else year
1219+
),
1220+
)
1221+
11961222
return self._extract_vectorized_results(sim, chunk_df)
11971223

11981224
finally:

tests/test_md_local_tax_parity.py

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
"""
2+
Maryland county/local income tax parity with TAXSIM.
3+
4+
TAXSIM's Maryland `siitax` is state-only: it applies no county tax when the
5+
input carries no locality. PolicyEngine applies MD's residence-based county
6+
tax (~2.25-3.20%) to every MD resident, over-stating MD siitax vs TAXSIM.
7+
Since the TAXSIM input has no county, the emulator zeros PE's MD local income
8+
tax to match TAXSIM's coverage. MD is the only state affected (every other
9+
local-income-tax state needs a locality that PE isn't given). See #1062.
10+
"""
11+
12+
import io
13+
14+
import pandas as pd
15+
from click.testing import CliRunner
16+
17+
from policyengine_taxsim.cli import cli
18+
19+
20+
def _siitax(state: int, wage: int = 100000):
21+
record = (
22+
"taxsimid,year,state,mstat,page,sage,depx,pwages,idtl\n"
23+
f"1,2025,{state},1,40,0,0,{wage},2\n"
24+
)
25+
result = CliRunner().invoke(cli, [], input=record)
26+
assert result.exit_code == 0, f"CLI failed: {result.output}\n{result.exception}"
27+
out = result.output
28+
idx = out.find("taxsimid,")
29+
return pd.read_csv(io.StringIO(out[idx:]))["siitax"].iloc[0]
30+
31+
32+
def test_md_siitax_excludes_county_tax():
33+
"""MD (SOI 21) siitax should be state-only — the ~3% county tax is zeroed
34+
to match TAXSIM (which applies no county tax absent a locality)."""
35+
md = _siitax(21, 100000)
36+
# State-only MD tax on $100k single (2025) ~= $4,386; with the county tax
37+
# it would be ~$7,200. Assert it's the state-only figure.
38+
assert 4000 < md < 5000, (
39+
f"MD siitax {md} looks like it still includes the county tax "
40+
"(state-only expected ~$4,386)"
41+
)

0 commit comments

Comments
 (0)