From 6917822be5312c5a9fefd3ba762a04aba11aa601 Mon Sep 17 00:00:00 2001 From: Ziming Date: Thu, 1 Oct 2026 16:24:43 -0400 Subject: [PATCH 1/8] Add Maryland MEAP regular heating assistance --- changelog.d/md-meap.added.md | 1 + .../md/dhs/meap/eligibility/fpg_rate.yaml | 10 +++ .../md/dhs/meap/eligibility/fpg_year_lag.yaml | 10 +++ .../md/dhs/meap/eligibility/smi_min_size.yaml | 10 +++ .../md/dhs/meap/eligibility/smi_rate.yaml | 10 +++ .../meap/income/earned_income_min_age.yaml | 10 +++ .../md/dhs/meap/income/sources/earned.yaml | 14 ++++ .../md/dhs/meap/income/sources/unearned.yaml | 24 +++++++ .../dhs/meap/payment/amount/electricity.yaml | 24 +++++++ .../meap/payment/amount/gas_wood_coal.yaml | 24 +++++++ .../md/dhs/meap/payment/amount/oil.yaml | 24 +++++++ .../md/dhs/meap/payment/amount/propane.yaml | 24 +++++++ .../dhs/meap/payment/garrett_multiplier.yaml | 10 +++ .../md/dhs/meap/payment/income_level.yaml | 35 +++++++++ .../md/dhs/meap/payment/nominal_level.yaml | 10 +++ .../dhs/meap/payment/regional_counties.yaml | 11 +++ .../md/dhs/meap/payment/subsidized_level.yaml | 10 +++ policyengine_us/programs.yaml | 10 ++- .../md_meap_categorically_eligible.yaml | 11 +++ .../meap/eligibility/md_meap_eligible.yaml | 53 ++++++++++++++ .../eligibility/md_meap_income_limit.yaml | 16 +++++ .../md_meap_countable_earned_income.yaml | 13 ++++ .../meap/income/md_meap_countable_income.yaml | 38 ++++++++++ .../md/dhs/meap/income/md_meap_fpg.yaml | 8 +++ .../gov/states/md/dhs/meap/integration.yaml | 72 +++++++++++++++++++ .../gov/states/md/dhs/meap/md_meap.yaml | 69 ++++++++++++++++++ .../md/dhs/meap/payment/md_meap_level.yaml | 19 +++++ .../md_meap_categorically_eligible.py | 17 +++++ .../dhs/meap/eligibility/md_meap_eligible.py | 31 ++++++++ .../eligibility/md_meap_household_size.py | 16 +++++ .../meap/eligibility/md_meap_income_limit.py | 28 ++++++++ .../income/md_meap_countable_earned_income.py | 22 ++++++ .../meap/income/md_meap_countable_income.py | 26 +++++++ .../states/md/dhs/meap/income/md_meap_fpg.py | 20 ++++++ .../gov/states/md/dhs/meap/md_meap.py | 45 ++++++++++++ .../md/dhs/meap/payment/md_meap_level.py | 30 ++++++++ 36 files changed, 804 insertions(+), 1 deletion(-) create mode 100644 changelog.d/md-meap.added.md create mode 100644 policyengine_us/parameters/gov/states/md/dhs/meap/eligibility/fpg_rate.yaml create mode 100644 policyengine_us/parameters/gov/states/md/dhs/meap/eligibility/fpg_year_lag.yaml create mode 100644 policyengine_us/parameters/gov/states/md/dhs/meap/eligibility/smi_min_size.yaml create mode 100644 policyengine_us/parameters/gov/states/md/dhs/meap/eligibility/smi_rate.yaml create mode 100644 policyengine_us/parameters/gov/states/md/dhs/meap/income/earned_income_min_age.yaml create mode 100644 policyengine_us/parameters/gov/states/md/dhs/meap/income/sources/earned.yaml create mode 100644 policyengine_us/parameters/gov/states/md/dhs/meap/income/sources/unearned.yaml create mode 100644 policyengine_us/parameters/gov/states/md/dhs/meap/payment/amount/electricity.yaml create mode 100644 policyengine_us/parameters/gov/states/md/dhs/meap/payment/amount/gas_wood_coal.yaml create mode 100644 policyengine_us/parameters/gov/states/md/dhs/meap/payment/amount/oil.yaml create mode 100644 policyengine_us/parameters/gov/states/md/dhs/meap/payment/amount/propane.yaml create mode 100644 policyengine_us/parameters/gov/states/md/dhs/meap/payment/garrett_multiplier.yaml create mode 100644 policyengine_us/parameters/gov/states/md/dhs/meap/payment/income_level.yaml create mode 100644 policyengine_us/parameters/gov/states/md/dhs/meap/payment/nominal_level.yaml create mode 100644 policyengine_us/parameters/gov/states/md/dhs/meap/payment/regional_counties.yaml create mode 100644 policyengine_us/parameters/gov/states/md/dhs/meap/payment/subsidized_level.yaml create mode 100644 policyengine_us/tests/policy/baseline/gov/states/md/dhs/meap/eligibility/md_meap_categorically_eligible.yaml create mode 100644 policyengine_us/tests/policy/baseline/gov/states/md/dhs/meap/eligibility/md_meap_eligible.yaml create mode 100644 policyengine_us/tests/policy/baseline/gov/states/md/dhs/meap/eligibility/md_meap_income_limit.yaml create mode 100644 policyengine_us/tests/policy/baseline/gov/states/md/dhs/meap/income/md_meap_countable_earned_income.yaml create mode 100644 policyengine_us/tests/policy/baseline/gov/states/md/dhs/meap/income/md_meap_countable_income.yaml create mode 100644 policyengine_us/tests/policy/baseline/gov/states/md/dhs/meap/income/md_meap_fpg.yaml create mode 100644 policyengine_us/tests/policy/baseline/gov/states/md/dhs/meap/integration.yaml create mode 100644 policyengine_us/tests/policy/baseline/gov/states/md/dhs/meap/md_meap.yaml create mode 100644 policyengine_us/tests/policy/baseline/gov/states/md/dhs/meap/payment/md_meap_level.yaml create mode 100644 policyengine_us/variables/gov/states/md/dhs/meap/eligibility/md_meap_categorically_eligible.py create mode 100644 policyengine_us/variables/gov/states/md/dhs/meap/eligibility/md_meap_eligible.py create mode 100644 policyengine_us/variables/gov/states/md/dhs/meap/eligibility/md_meap_household_size.py create mode 100644 policyengine_us/variables/gov/states/md/dhs/meap/eligibility/md_meap_income_limit.py create mode 100644 policyengine_us/variables/gov/states/md/dhs/meap/income/md_meap_countable_earned_income.py create mode 100644 policyengine_us/variables/gov/states/md/dhs/meap/income/md_meap_countable_income.py create mode 100644 policyengine_us/variables/gov/states/md/dhs/meap/income/md_meap_fpg.py create mode 100644 policyengine_us/variables/gov/states/md/dhs/meap/md_meap.py create mode 100644 policyengine_us/variables/gov/states/md/dhs/meap/payment/md_meap_level.py diff --git a/changelog.d/md-meap.added.md b/changelog.d/md-meap.added.md new file mode 100644 index 00000000000..a51b667ef7e --- /dev/null +++ b/changelog.d/md-meap.added.md @@ -0,0 +1 @@ +Add Maryland MEAP regular heating assistance for state FY2026. diff --git a/policyengine_us/parameters/gov/states/md/dhs/meap/eligibility/fpg_rate.yaml b/policyengine_us/parameters/gov/states/md/dhs/meap/eligibility/fpg_rate.yaml new file mode 100644 index 00000000000..32046465ba9 --- /dev/null +++ b/policyengine_us/parameters/gov/states/md/dhs/meap/eligibility/fpg_rate.yaml @@ -0,0 +1,10 @@ +description: Maryland uses this poverty guideline share for income eligibility below the large-household cutoff under the Maryland Energy Assistance Program. +values: + '2025-07-01': 2 +metadata: + unit: /1 + period: year + label: Maryland MEAP poverty guideline rate + reference: + - title: FY2026 OHEP Income Guidelines + href: https://dhs.maryland.gov/documents/OHEP/Advisory%20Board/Income-Guidelines-FY2026-Updated-7.9.2025.pdf diff --git a/policyengine_us/parameters/gov/states/md/dhs/meap/eligibility/fpg_year_lag.yaml b/policyengine_us/parameters/gov/states/md/dhs/meap/eligibility/fpg_year_lag.yaml new file mode 100644 index 00000000000..9ba1e7b2057 --- /dev/null +++ b/policyengine_us/parameters/gov/states/md/dhs/meap/eligibility/fpg_year_lag.yaml @@ -0,0 +1,10 @@ +description: Maryland uses the poverty guideline from this many years before the model year under the Maryland Energy Assistance Program. +values: + '2025-07-01': 1 +metadata: + unit: year + period: year + label: Maryland MEAP poverty guideline year lag + reference: + - title: FY2026 OHEP Income Guidelines + href: https://dhs.maryland.gov/documents/OHEP/Advisory%20Board/Income-Guidelines-FY2026-Updated-7.9.2025.pdf diff --git a/policyengine_us/parameters/gov/states/md/dhs/meap/eligibility/smi_min_size.yaml b/policyengine_us/parameters/gov/states/md/dhs/meap/eligibility/smi_min_size.yaml new file mode 100644 index 00000000000..af6588734b7 --- /dev/null +++ b/policyengine_us/parameters/gov/states/md/dhs/meap/eligibility/smi_min_size.yaml @@ -0,0 +1,10 @@ +description: Maryland uses state median income instead of poverty guidelines starting at this household size under the Maryland Energy Assistance Program. +values: + '2025-07-01': 11 +metadata: + unit: person + period: year + label: Maryland MEAP SMI minimum household size + reference: + - title: FY2026 OHEP Income Guidelines + href: https://dhs.maryland.gov/documents/OHEP/Advisory%20Board/Income-Guidelines-FY2026-Updated-7.9.2025.pdf diff --git a/policyengine_us/parameters/gov/states/md/dhs/meap/eligibility/smi_rate.yaml b/policyengine_us/parameters/gov/states/md/dhs/meap/eligibility/smi_rate.yaml new file mode 100644 index 00000000000..dd0d96a567a --- /dev/null +++ b/policyengine_us/parameters/gov/states/md/dhs/meap/eligibility/smi_rate.yaml @@ -0,0 +1,10 @@ +description: Maryland uses this state median income share for large households under the Maryland Energy Assistance Program. +values: + '2025-07-01': 0.6 +metadata: + unit: /1 + period: year + label: Maryland MEAP SMI rate + reference: + - title: FY2026 OHEP Income Guidelines + href: https://dhs.maryland.gov/documents/OHEP/Advisory%20Board/Income-Guidelines-FY2026-Updated-7.9.2025.pdf diff --git a/policyengine_us/parameters/gov/states/md/dhs/meap/income/earned_income_min_age.yaml b/policyengine_us/parameters/gov/states/md/dhs/meap/income/earned_income_min_age.yaml new file mode 100644 index 00000000000..32c271d57c3 --- /dev/null +++ b/policyengine_us/parameters/gov/states/md/dhs/meap/income/earned_income_min_age.yaml @@ -0,0 +1,10 @@ +description: Maryland excludes earnings of children below this age under the Maryland Energy Assistance Program. +values: + '2025-07-01': 18 +metadata: + unit: year + period: year + label: Maryland MEAP minimum age for counted earnings + reference: + - title: COMAR 07.03.21.04, countable income and exclusions + href: https://regs.maryland.gov/us/md/exec/comar/07.03.21.04 diff --git a/policyengine_us/parameters/gov/states/md/dhs/meap/income/sources/earned.yaml b/policyengine_us/parameters/gov/states/md/dhs/meap/income/sources/earned.yaml new file mode 100644 index 00000000000..b37fc57c43c --- /dev/null +++ b/policyengine_us/parameters/gov/states/md/dhs/meap/income/sources/earned.yaml @@ -0,0 +1,14 @@ +description: Maryland uses these existing earnings sources without an additional business expense deduction under the Maryland Energy Assistance Program. +values: + '2025-07-01': + - employment_income + - self_employment_income + - sstb_self_employment_income + - farm_operations_income +metadata: + unit: list + period: year + label: Maryland MEAP earned sources + reference: + - title: COMAR 07.03.21.04, countable income and exclusions + href: https://regs.maryland.gov/us/md/exec/comar/07.03.21.04 diff --git a/policyengine_us/parameters/gov/states/md/dhs/meap/income/sources/unearned.yaml b/policyengine_us/parameters/gov/states/md/dhs/meap/income/sources/unearned.yaml new file mode 100644 index 00000000000..0c7e1b18449 --- /dev/null +++ b/policyengine_us/parameters/gov/states/md/dhs/meap/income/sources/unearned.yaml @@ -0,0 +1,24 @@ +description: Maryland counts these available unearned income sources under the Maryland Energy Assistance Program. +values: + '2025-07-01': + - ssi + - md_tca + - dividend_income + - interest_income + - estate_income + - pension_income + - retirement_distributions + - child_support_received + - alimony_income + - workers_compensation + - unemployment_compensation + - veterans_benefits + - strike_benefits + - rental_income +metadata: + unit: list + period: year + label: Maryland MEAP unearned sources + reference: + - title: COMAR 07.03.21.04, countable income and exclusions + href: https://regs.maryland.gov/us/md/exec/comar/07.03.21.04 diff --git a/policyengine_us/parameters/gov/states/md/dhs/meap/payment/amount/electricity.yaml b/policyengine_us/parameters/gov/states/md/dhs/meap/payment/amount/electricity.yaml new file mode 100644 index 00000000000..e1b9e120ed2 --- /dev/null +++ b/policyengine_us/parameters/gov/states/md/dhs/meap/payment/amount/electricity.yaml @@ -0,0 +1,24 @@ +description: Maryland provides this annual electricity heating payment by benefit level under the Maryland Energy Assistance Program. +'1': + '2025-07-01': 100 +'2': + '2025-07-01': 100 +'3': + '2025-07-01': 100 +'4': + '2025-07-01': 100 +'5': + '2025-07-01': 100 +'6': + '2025-07-01': 100 +'7': + '2025-07-01': 25 +metadata: + unit: currency-USD + period: year + label: Maryland MEAP electricity heating payments + breakdown: + - range(1, 8) + reference: + - title: FY26 MEAP Benefit Matrix + href: https://dhs.maryland.gov/documents/OHEP/Advisory%20Board/FY26-MEAP-Benefit-Matrix-2-1-1.pdf diff --git a/policyengine_us/parameters/gov/states/md/dhs/meap/payment/amount/gas_wood_coal.yaml b/policyengine_us/parameters/gov/states/md/dhs/meap/payment/amount/gas_wood_coal.yaml new file mode 100644 index 00000000000..7bab79cb15d --- /dev/null +++ b/policyengine_us/parameters/gov/states/md/dhs/meap/payment/amount/gas_wood_coal.yaml @@ -0,0 +1,24 @@ +description: Maryland provides this annual gas wood coal heating payment by benefit level under the Maryland Energy Assistance Program. +'1': + '2025-07-01': 550 +'2': + '2025-07-01': 475 +'3': + '2025-07-01': 400 +'4': + '2025-07-01': 360 +'5': + '2025-07-01': 300 +'6': + '2025-07-01': 225 +'7': + '2025-07-01': 25 +metadata: + unit: currency-USD + period: year + label: Maryland MEAP gas wood coal heating payments + breakdown: + - range(1, 8) + reference: + - title: FY26 MEAP Benefit Matrix + href: https://dhs.maryland.gov/documents/OHEP/Advisory%20Board/FY26-MEAP-Benefit-Matrix-2-1-1.pdf diff --git a/policyengine_us/parameters/gov/states/md/dhs/meap/payment/amount/oil.yaml b/policyengine_us/parameters/gov/states/md/dhs/meap/payment/amount/oil.yaml new file mode 100644 index 00000000000..c4ba7cd5087 --- /dev/null +++ b/policyengine_us/parameters/gov/states/md/dhs/meap/payment/amount/oil.yaml @@ -0,0 +1,24 @@ +description: Maryland provides this annual oil heating payment by benefit level under the Maryland Energy Assistance Program. +'1': + '2025-07-01': 1100 +'2': + '2025-07-01': 990 +'3': + '2025-07-01': 880 +'4': + '2025-07-01': 770 +'5': + '2025-07-01': 650 +'6': + '2025-07-01': 225 +'7': + '2025-07-01': 25 +metadata: + unit: currency-USD + period: year + label: Maryland MEAP oil heating payments + breakdown: + - range(1, 8) + reference: + - title: FY26 MEAP Benefit Matrix + href: https://dhs.maryland.gov/documents/OHEP/Advisory%20Board/FY26-MEAP-Benefit-Matrix-2-1-1.pdf diff --git a/policyengine_us/parameters/gov/states/md/dhs/meap/payment/amount/propane.yaml b/policyengine_us/parameters/gov/states/md/dhs/meap/payment/amount/propane.yaml new file mode 100644 index 00000000000..aa3b123ac9c --- /dev/null +++ b/policyengine_us/parameters/gov/states/md/dhs/meap/payment/amount/propane.yaml @@ -0,0 +1,24 @@ +description: Maryland provides this annual propane heating payment by benefit level under the Maryland Energy Assistance Program. +'1': + '2025-07-01': 1000 +'2': + '2025-07-01': 930 +'3': + '2025-07-01': 830 +'4': + '2025-07-01': 725 +'5': + '2025-07-01': 600 +'6': + '2025-07-01': 225 +'7': + '2025-07-01': 25 +metadata: + unit: currency-USD + period: year + label: Maryland MEAP propane heating payments + breakdown: + - range(1, 8) + reference: + - title: FY26 MEAP Benefit Matrix + href: https://dhs.maryland.gov/documents/OHEP/Advisory%20Board/FY26-MEAP-Benefit-Matrix-2-1-1.pdf diff --git a/policyengine_us/parameters/gov/states/md/dhs/meap/payment/garrett_multiplier.yaml b/policyengine_us/parameters/gov/states/md/dhs/meap/payment/garrett_multiplier.yaml new file mode 100644 index 00000000000..a03e50bfbf3 --- /dev/null +++ b/policyengine_us/parameters/gov/states/md/dhs/meap/payment/garrett_multiplier.yaml @@ -0,0 +1,10 @@ +description: Maryland multiplies regular heating grants in Garrett County by this factor under the Maryland Energy Assistance Program. +values: + '2025-07-01': 1.1 +metadata: + unit: /1 + period: year + label: Maryland MEAP Garrett multiplier + reference: + - title: FY2026 LIHEAP State Plan, section 2.5 + href: https://liheapch.acf.gov/docs/2026/state-plans/MD_Plan_2026.pdf#page=11 diff --git a/policyengine_us/parameters/gov/states/md/dhs/meap/payment/income_level.yaml b/policyengine_us/parameters/gov/states/md/dhs/meap/payment/income_level.yaml new file mode 100644 index 00000000000..0b99d444c96 --- /dev/null +++ b/policyengine_us/parameters/gov/states/md/dhs/meap/payment/income_level.yaml @@ -0,0 +1,35 @@ +description: Maryland uses these inclusive poverty-ratio band tops to assign regular heating payment levels under the Maryland Energy Assistance Program. +brackets: +- threshold: + '2025-07-01': -.inf + amount: + '2025-07-01': 1 +- threshold: + '2025-07-01': 0.25 + amount: + '2025-07-01': 2 +- threshold: + '2025-07-01': 0.5 + amount: + '2025-07-01': 3 +- threshold: + '2025-07-01': 1 + amount: + '2025-07-01': 4 +- threshold: + '2025-07-01': 1.5 + amount: + '2025-07-01': 5 +- threshold: + '2025-07-01': 2 + amount: + '2025-07-01': 7 +metadata: + type: single_amount + threshold_unit: /1 + amount_unit: /1 + period: year + label: Maryland MEAP income payment levels + reference: + - title: FY26 MEAP Benefit Matrix + href: https://dhs.maryland.gov/documents/OHEP/Advisory%20Board/FY26-MEAP-Benefit-Matrix-2-1-1.pdf diff --git a/policyengine_us/parameters/gov/states/md/dhs/meap/payment/nominal_level.yaml b/policyengine_us/parameters/gov/states/md/dhs/meap/payment/nominal_level.yaml new file mode 100644 index 00000000000..c3fec01205c --- /dev/null +++ b/policyengine_us/parameters/gov/states/md/dhs/meap/payment/nominal_level.yaml @@ -0,0 +1,10 @@ +description: Maryland assigns categorically eligible households above the highest income band to this payment level under the Maryland Energy Assistance Program. +values: + '2025-07-01': 7 +metadata: + unit: /1 + period: year + label: Maryland MEAP nominal payment level + reference: + - title: FY26 MEAP Benefit Matrix + href: https://dhs.maryland.gov/documents/OHEP/Advisory%20Board/FY26-MEAP-Benefit-Matrix-2-1-1.pdf diff --git a/policyengine_us/parameters/gov/states/md/dhs/meap/payment/regional_counties.yaml b/policyengine_us/parameters/gov/states/md/dhs/meap/payment/regional_counties.yaml new file mode 100644 index 00000000000..90103f1e98b --- /dev/null +++ b/policyengine_us/parameters/gov/states/md/dhs/meap/payment/regional_counties.yaml @@ -0,0 +1,11 @@ +description: Maryland applies its regional heating multiplier in these counties under the Maryland Energy Assistance Program. +values: + '2025-07-01': + - GARRETT_COUNTY_MD +metadata: + unit: list + period: year + label: Maryland MEAP regional multiplier counties + reference: + - title: FY2026 LIHEAP State Plan, section 2.5 + href: https://liheapch.acf.gov/docs/2026/state-plans/MD_Plan_2026.pdf#page=11 diff --git a/policyengine_us/parameters/gov/states/md/dhs/meap/payment/subsidized_level.yaml b/policyengine_us/parameters/gov/states/md/dhs/meap/payment/subsidized_level.yaml new file mode 100644 index 00000000000..51e0f8578a7 --- /dev/null +++ b/policyengine_us/parameters/gov/states/md/dhs/meap/payment/subsidized_level.yaml @@ -0,0 +1,10 @@ +description: Maryland assigns subsidized or sub-metered households to this payment level under the Maryland Energy Assistance Program. +values: + '2025-07-01': 6 +metadata: + unit: /1 + period: year + label: Maryland MEAP subsidized payment level + reference: + - title: FY26 MEAP Benefit Matrix + href: https://dhs.maryland.gov/documents/OHEP/Advisory%20Board/FY26-MEAP-Benefit-Matrix-2-1-1.pdf diff --git a/policyengine_us/programs.yaml b/policyengine_us/programs.yaml index 81c10bc47b6..4f08081de7a 100644 --- a/policyengine_us/programs.yaml +++ b/policyengine_us/programs.yaml @@ -802,8 +802,16 @@ programs: category: Benefits agency: HHS status: partial - coverage: OR, DC, Riverside County, MA, IL, TX + coverage: OR, DC, Riverside County, MA, IL, TX, MD state_implementations: + - state: MD + status: partial + name: Maryland MEAP + full_name: Maryland Energy Assistance Program + variable: md_meap + parameter_prefix: gov.states.md.dhs.meap + verified_years: "2026" + notes: Regular heating with annual income limits, categorical eligibility, fuel/poverty/subsidy payments and Garrett multiplier. Uses COMAR qualified-member size despite the manual child exception; income-source conflicts are resolved to COMAR. Sub-metering, means-tested VA, special income and precise subsidy definitions remain unsupported. Garrett rounding and heat-in-rent fuel assignment are documented inferences. Earlier years are unverified backfilled estimates. - state: OR status: in_progress name: LIHEAP diff --git a/policyengine_us/tests/policy/baseline/gov/states/md/dhs/meap/eligibility/md_meap_categorically_eligible.yaml b/policyengine_us/tests/policy/baseline/gov/states/md/dhs/meap/eligibility/md_meap_categorically_eligible.yaml new file mode 100644 index 00000000000..d62244a8442 --- /dev/null +++ b/policyengine_us/tests/policy/baseline/gov/states/md/dhs/meap/eligibility/md_meap_categorically_eligible.yaml @@ -0,0 +1,11 @@ +- name: Receipt of SNAP, TCA or SSI independently waives the income test + period: 2026 + input: + heating_type: [NATURAL_GAS, NATURAL_GAS, NATURAL_GAS, NATURAL_GAS] + state_code: [MD, MD, MD, MD] + snap: [1, 0, 0, 0] + md_tca: [0, 1, 0, 0] + ssi: [0, 0, 1, 0] + veterans_benefits: [0, 0, 0, 1000] + output: + md_meap_categorically_eligible: [true, true, true, false] diff --git a/policyengine_us/tests/policy/baseline/gov/states/md/dhs/meap/eligibility/md_meap_eligible.yaml b/policyengine_us/tests/policy/baseline/gov/states/md/dhs/meap/eligibility/md_meap_eligible.yaml new file mode 100644 index 00000000000..7af920170c6 --- /dev/null +++ b/policyengine_us/tests/policy/baseline/gov/states/md/dhs/meap/eligibility/md_meap_eligible.yaml @@ -0,0 +1,53 @@ +- name: Annual income limit is inclusive + period: 2026 + input: + state_code: [MD, MD] + heating_type: [NATURAL_GAS, NATURAL_GAS] + has_heating_expense: [true, true] + md_meap_countable_income: [31300, 31301] + md_meap_categorically_eligible: [false, false] + output: + md_meap_eligible: [true, false] +- name: Categorical receipt waives only the income test + period: 2026 + input: + state_code: [MD, MD] + heating_type: [NATURAL_GAS, NATURAL_GAS] + has_heating_expense: [true, false] + md_meap_countable_income: [100000, 100000] + md_meap_categorically_eligible: [true, true] + output: + md_meap_eligible: [true, false] +- name: Subsidized heat included in rent is ineligible even with a reported bill + period: 2026 + input: + state_code: [MD, MD] + heating_type: [NATURAL_GAS, NATURAL_GAS] + has_heating_expense: [true, true] + md_meap_countable_income: [0, 0] + md_meap_categorically_eligible: [false, false] + heat_expense_included_in_rent: [true, true] + receives_housing_assistance: [false, true] + output: + md_meap_eligible: [true, false] +- name: Qualified member is still required with categorical income eligibility + period: 2026 + input: + state_code: MD + heating_type: NATURAL_GAS + has_heating_expense: true + md_meap_countable_income: 0 + md_meap_categorically_eligible: true + immigration_status: UNDOCUMENTED + output: + md_meap_eligible: false +- name: Other states receive no Maryland benefit + period: 2026 + input: + state_code: VA + heating_type: NATURAL_GAS + has_heating_expense: true + md_meap_countable_income: 0 + md_meap_categorically_eligible: false + output: + md_meap_eligible: false diff --git a/policyengine_us/tests/policy/baseline/gov/states/md/dhs/meap/eligibility/md_meap_income_limit.yaml b/policyengine_us/tests/policy/baseline/gov/states/md/dhs/meap/eligibility/md_meap_income_limit.yaml new file mode 100644 index 00000000000..c3d19cf4a78 --- /dev/null +++ b/policyengine_us/tests/policy/baseline/gov/states/md/dhs/meap/eligibility/md_meap_income_limit.yaml @@ -0,0 +1,16 @@ +- name: All published annual poverty limits and sizes nine and ten follow the stated rate + period: 2026 + input: + heating_type: [NATURAL_GAS, NATURAL_GAS, NATURAL_GAS, NATURAL_GAS, NATURAL_GAS, NATURAL_GAS, NATURAL_GAS, NATURAL_GAS, NATURAL_GAS, NATURAL_GAS] + state_code: [MD, MD, MD, MD, MD, MD, MD, MD, MD, MD] + md_meap_household_size: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] + output: + md_meap_income_limit: [31300, 42300, 53300, 64300, 75300, 86300, 97300, 108300, 119300, 130300] +- name: Sizes eleven and twelve use the stated sixty percent SMI rule + period: 2026 + input: + heating_type: [NATURAL_GAS, NATURAL_GAS] + state_code: [MD, MD] + md_meap_household_size: [11, 12] + output: + md_meap_income_limit: [135930, 138705] diff --git a/policyengine_us/tests/policy/baseline/gov/states/md/dhs/meap/income/md_meap_countable_earned_income.yaml b/policyengine_us/tests/policy/baseline/gov/states/md/dhs/meap/income/md_meap_countable_earned_income.yaml new file mode 100644 index 00000000000..cc50d6ee7d6 --- /dev/null +++ b/policyengine_us/tests/policy/baseline/gov/states/md/dhs/meap/income/md_meap_countable_earned_income.yaml @@ -0,0 +1,13 @@ +- name: Child and full-time student earnings are excluded without a new business expense deduction + period: 2026 + input: + heating_type: [NATURAL_GAS, NATURAL_GAS, NATURAL_GAS, NATURAL_GAS] + state_code: [MD, MD, MD, MD] + age: [17, 18, 40, 40] + is_full_time_student: [false, true, false, false] + employment_income: [12000, 12000, 12000, 12000] + self_employment_income: [1000, 1000, 1000, -1000] + sstb_self_employment_income: [600, 600, 600, 600] + farm_operations_income: [1200, 1200, 1200, 1200] + output: + md_meap_countable_earned_income: [0, 0, 14800, 13800] diff --git a/policyengine_us/tests/policy/baseline/gov/states/md/dhs/meap/income/md_meap_countable_income.yaml b/policyengine_us/tests/policy/baseline/gov/states/md/dhs/meap/income/md_meap_countable_income.yaml new file mode 100644 index 00000000000..848cfc6b138 --- /dev/null +++ b/policyengine_us/tests/policy/baseline/gov/states/md/dhs/meap/income/md_meap_countable_income.yaml @@ -0,0 +1,38 @@ +# 10,000 earnings + 11,400 net SSA/railroad + 9,240 other receipts - 2,400 support = 28,240. +- name: Count pensions and IRA withdrawals separately, net Medicare and court-ordered support paid + period: 2026 + input: + heating_type: NATURAL_GAS + state_code: MD + md_meap_countable_earned_income: 10000 + social_security: 12000 + railroad_benefits: 600 + medicare_part_b_premium: 1200 + pension_income: 1200 + retirement_distributions: 2400 + interest_income: 120 + dividend_income: 240 + estate_income: 360 + rental_income: 1200 + child_support_received: 600 + alimony_income: 480 + workers_compensation: 720 + unemployment_compensation: 840 + veterans_benefits: 960 + strike_benefits: 120 + ssi: 0 + md_tca: 0 + child_support_expense: 2400 + output: + md_meap_countable_income: 28240 +- name: Support deduction cannot make countable income negative + period: 2026 + input: + heating_type: NATURAL_GAS + state_code: MD + md_meap_countable_earned_income: 0 + ssi: 0 + md_tca: 0 + child_support_expense: 10000 + output: + md_meap_countable_income: 0 diff --git a/policyengine_us/tests/policy/baseline/gov/states/md/dhs/meap/income/md_meap_fpg.yaml b/policyengine_us/tests/policy/baseline/gov/states/md/dhs/meap/income/md_meap_fpg.yaml new file mode 100644 index 00000000000..217131987a7 --- /dev/null +++ b/policyengine_us/tests/policy/baseline/gov/states/md/dhs/meap/income/md_meap_fpg.yaml @@ -0,0 +1,8 @@ +- name: Prior-year guideline follows the requested year even when payment parameters backfill + period: 2022 + input: + heating_type: NATURAL_GAS + state_code: MD + md_meap_household_size: 2 + output: + md_meap_fpg: 17420 diff --git a/policyengine_us/tests/policy/baseline/gov/states/md/dhs/meap/integration.yaml b/policyengine_us/tests/policy/baseline/gov/states/md/dhs/meap/integration.yaml new file mode 100644 index 00000000000..cb837cb7f45 --- /dev/null +++ b/policyengine_us/tests/policy/baseline/gov/states/md/dhs/meap/integration.yaml @@ -0,0 +1,72 @@ +- name: Mixed status follows the regulation household count and counts excluded income in full + period: 2026 + input: + people: + parent: + age: 40 + employment_income: 20000 + ssi: 0 + child: + age: 5 + immigration_status: UNDOCUMENTED + child_support_received: 1000 + ssi: 0 + spm_units: + unit: + members: + - parent + - child + heating_type: NATURAL_GAS + gas_expense: 1 + snap: 0 + md_tca: 0 + households: + home: + members: + - parent + - child + state_code: MD + county: BALTIMORE_COUNTY_MD + output: + md_meap_household_size: 1 + md_meap_countable_income: 21000 + md_meap_income_limit: 31300 + md_meap_fpg: 15650 + md_meap_level: 4 + md_meap_eligible: true + md_meap: 360 +- name: Categorically eligible high-income subsidized household receives the nominal payment + period: 2026 + input: + state_code: MD + age: 40 + employment_income: 100000 + snap: 120 + md_tca: 0 + ssi: 0 + receives_housing_assistance: true + heating_type: PROPANE + bottled_gas_expense: 1 + county: GARRETT_COUNTY_MD + output: + md_meap_categorically_eligible: true + md_meap_countable_income: 100000 + md_meap_level: 7 + md_meap_eligible: true + md_meap: 25 +- name: Unsubsidized heat in rent with known fuel uses its normal fuel row + period: 2026 + input: + state_code: MD + age: 40 + employment_income: 10000 + snap: 0 + md_tca: 0 + ssi: 0 + heat_expense_included_in_rent: true + heating_type: FUEL_OIL + output: + md_meap_countable_income: 10000 + md_meap_level: 3 + md_meap_eligible: true + md_meap: 880 diff --git a/policyengine_us/tests/policy/baseline/gov/states/md/dhs/meap/md_meap.yaml b/policyengine_us/tests/policy/baseline/gov/states/md/dhs/meap/md_meap.yaml new file mode 100644 index 00000000000..2ccaa3cef1a --- /dev/null +++ b/policyengine_us/tests/policy/baseline/gov/states/md/dhs/meap/md_meap.yaml @@ -0,0 +1,69 @@ +- name: Every published FY26 level for electricity + period: 2026 + input: + state_code: [MD, MD, MD, MD, MD, MD, MD] + heating_type: [ELECTRICITY, ELECTRICITY, ELECTRICITY, ELECTRICITY, ELECTRICITY, ELECTRICITY, ELECTRICITY] + md_meap_eligible: [true, true, true, true, true, true, true] + md_meap_level: [1, 2, 3, 4, 5, 6, 7] + county: [BALTIMORE_COUNTY_MD, BALTIMORE_COUNTY_MD, BALTIMORE_COUNTY_MD, BALTIMORE_COUNTY_MD, BALTIMORE_COUNTY_MD, BALTIMORE_COUNTY_MD, BALTIMORE_COUNTY_MD] + output: + md_meap: [100, 100, 100, 100, 100, 100, 25] +- name: Every published FY26 level for natural_gas + period: 2026 + input: + state_code: [MD, MD, MD, MD, MD, MD, MD] + heating_type: [NATURAL_GAS, NATURAL_GAS, NATURAL_GAS, NATURAL_GAS, NATURAL_GAS, NATURAL_GAS, NATURAL_GAS] + md_meap_eligible: [true, true, true, true, true, true, true] + md_meap_level: [1, 2, 3, 4, 5, 6, 7] + county: [BALTIMORE_COUNTY_MD, BALTIMORE_COUNTY_MD, BALTIMORE_COUNTY_MD, BALTIMORE_COUNTY_MD, BALTIMORE_COUNTY_MD, BALTIMORE_COUNTY_MD, BALTIMORE_COUNTY_MD] + output: + md_meap: [550, 475, 400, 360, 300, 225, 25] +- name: Every published FY26 level for fuel_oil + period: 2026 + input: + state_code: [MD, MD, MD, MD, MD, MD, MD] + heating_type: [FUEL_OIL, FUEL_OIL, FUEL_OIL, FUEL_OIL, FUEL_OIL, FUEL_OIL, FUEL_OIL] + md_meap_eligible: [true, true, true, true, true, true, true] + md_meap_level: [1, 2, 3, 4, 5, 6, 7] + county: [BALTIMORE_COUNTY_MD, BALTIMORE_COUNTY_MD, BALTIMORE_COUNTY_MD, BALTIMORE_COUNTY_MD, BALTIMORE_COUNTY_MD, BALTIMORE_COUNTY_MD, BALTIMORE_COUNTY_MD] + output: + md_meap: [1100, 990, 880, 770, 650, 225, 25] +- name: Every published FY26 level for propane + period: 2026 + input: + state_code: [MD, MD, MD, MD, MD, MD, MD] + heating_type: [PROPANE, PROPANE, PROPANE, PROPANE, PROPANE, PROPANE, PROPANE] + md_meap_eligible: [true, true, true, true, true, true, true] + md_meap_level: [1, 2, 3, 4, 5, 6, 7] + county: [BALTIMORE_COUNTY_MD, BALTIMORE_COUNTY_MD, BALTIMORE_COUNTY_MD, BALTIMORE_COUNTY_MD, BALTIMORE_COUNTY_MD, BALTIMORE_COUNTY_MD, BALTIMORE_COUNTY_MD] + output: + md_meap: [1000, 930, 830, 725, 600, 225, 25] +- name: Every published FY26 level for wood + period: 2026 + input: + state_code: [MD, MD, MD, MD, MD, MD, MD] + heating_type: [WOOD, WOOD, WOOD, WOOD, WOOD, WOOD, WOOD] + md_meap_eligible: [true, true, true, true, true, true, true] + md_meap_level: [1, 2, 3, 4, 5, 6, 7] + county: [BALTIMORE_COUNTY_MD, BALTIMORE_COUNTY_MD, BALTIMORE_COUNTY_MD, BALTIMORE_COUNTY_MD, BALTIMORE_COUNTY_MD, BALTIMORE_COUNTY_MD, BALTIMORE_COUNTY_MD] + output: + md_meap: [550, 475, 400, 360, 300, 225, 25] +- name: Garrett regional multiplier uses half-up dollars while the nominal grant stays flat + period: 2026 + input: + state_code: [MD, MD, MD, MD, MD] + county: [GARRETT_COUNTY_MD, GARRETT_COUNTY_MD, GARRETT_COUNTY_MD, GARRETT_COUNTY_MD, GARRETT_COUNTY_MD] + heating_type: [NATURAL_GAS, PROPANE, FUEL_OIL, FUEL_OIL, FUEL_OIL] + md_meap_eligible: [true, true, true, true, true] + md_meap_level: [2, 4, 6, 1, 7] + output: + md_meap: [523, 798, 248, 1210, 25] +- name: Kerosene and coal use the oil and wood-coal schedules + period: 2026 + input: + state_code: [MD, MD, MD, MD, MD, MD] + heating_type: [KEROSENE, COAL, SOLAR, OTHER, UNSPECIFIED, NONE] + md_meap_eligible: [true, true, true, true, true, true] + md_meap_level: [1, 1, 1, 1, 1, 1] + output: + md_meap: [1100, 550, 0, 0, 0, 0] diff --git a/policyengine_us/tests/policy/baseline/gov/states/md/dhs/meap/payment/md_meap_level.yaml b/policyengine_us/tests/policy/baseline/gov/states/md/dhs/meap/payment/md_meap_level.yaml new file mode 100644 index 00000000000..2d565530bd9 --- /dev/null +++ b/policyengine_us/tests/policy/baseline/gov/states/md/dhs/meap/payment/md_meap_level.yaml @@ -0,0 +1,19 @@ +- name: Inclusive poverty-band tops and immediately higher income + period: 2026 + input: + heating_type: [NATURAL_GAS, NATURAL_GAS, NATURAL_GAS, NATURAL_GAS, NATURAL_GAS, NATURAL_GAS, NATURAL_GAS, NATURAL_GAS, NATURAL_GAS, NATURAL_GAS, NATURAL_GAS] + state_code: [MD, MD, MD, MD, MD, MD, MD, MD, MD, MD, MD] + md_meap_fpg: [10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000] + md_meap_countable_income: [0, 2500, 2501, 5000, 5001, 10000, 10001, 15000, 15001, 20000, 20001] + output: + md_meap_level: [1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 7] +- name: Subsidized homes use level six unless above the categorical nominal threshold + period: 2026 + input: + heating_type: [NATURAL_GAS, NATURAL_GAS, NATURAL_GAS] + state_code: [MD, MD, MD] + md_meap_fpg: [10000, 10000, 10000] + md_meap_countable_income: [0, 20000, 20001] + receives_housing_assistance: [true, true, true] + output: + md_meap_level: [6, 6, 7] diff --git a/policyengine_us/variables/gov/states/md/dhs/meap/eligibility/md_meap_categorically_eligible.py b/policyengine_us/variables/gov/states/md/dhs/meap/eligibility/md_meap_categorically_eligible.py new file mode 100644 index 00000000000..b5d616b99e6 --- /dev/null +++ b/policyengine_us/variables/gov/states/md/dhs/meap/eligibility/md_meap_categorically_eligible.py @@ -0,0 +1,17 @@ +from policyengine_us.model_api import * + + +class md_meap_categorically_eligible(Variable): + value_type = bool + entity = SPMUnit + definition_period = YEAR + label = "Maryland MEAP categorical income eligibility" + defined_for = StateCode.MD + reference = ( + "https://dhs.maryland.gov/documents/OHEP/OHEP-Operations-Manual.pdf#page=11,62,63", + "https://liheapch.acf.gov/docs/2026/state-plans/MD_Plan_2026.pdf#page=5", + ) + documentation = "One member's receipt of SNAP, TCA or SSI waives the income test. Means-tested veterans benefits are also legally qualifying, but generic veterans_benefits cannot identify that subset. Recurring receipt in the program year is approximated by annual benefit receipt." + + def formula(spm_unit, period, parameters): + return add(spm_unit, period, ["snap", "md_tca", "ssi"]) > 0 diff --git a/policyengine_us/variables/gov/states/md/dhs/meap/eligibility/md_meap_eligible.py b/policyengine_us/variables/gov/states/md/dhs/meap/eligibility/md_meap_eligible.py new file mode 100644 index 00000000000..966357ab18e --- /dev/null +++ b/policyengine_us/variables/gov/states/md/dhs/meap/eligibility/md_meap_eligible.py @@ -0,0 +1,31 @@ +from policyengine_us.model_api import * + + +class md_meap_eligible(Variable): + value_type = bool + entity = SPMUnit + definition_period = YEAR + label = "Maryland MEAP regular heating eligibility" + defined_for = StateCode.MD + reference = ( + "https://regs.maryland.gov/us/md/exec/comar/07.03.21.03", + "https://regs.maryland.gov/us/md/exec/comar/07.03.21.06", + "https://dhs.maryland.gov/documents/OHEP/OHEP-Operations-Manual.pdf#page=11,12,16,39", + ) + documentation = "Regular heating has no resource test. Assisted-living residence, application documentation, emancipation, program-year duplicate awards and the optional medical waiver are not modeled. At least one qualified member is required." + + def formula(spm_unit, period, parameters): + heat_in_rent = spm_unit("heat_expense_included_in_rent", period) + subsidized = spm_unit( + "receives_housing_assistance", period + ) | spm_unit.household("is_in_public_housing", period) + income_eligible = spm_unit("md_meap_categorically_eligible", period) | ( + spm_unit("md_meap_countable_income", period) + <= spm_unit("md_meap_income_limit", period) + ) + return ( + income_eligible + & (spm_unit("md_meap_household_size", period) > 0) + & (spm_unit("has_heating_expense", period) | heat_in_rent) + & ~(heat_in_rent & subsidized) + ) diff --git a/policyengine_us/variables/gov/states/md/dhs/meap/eligibility/md_meap_household_size.py b/policyengine_us/variables/gov/states/md/dhs/meap/eligibility/md_meap_household_size.py new file mode 100644 index 00000000000..85d56440cff --- /dev/null +++ b/policyengine_us/variables/gov/states/md/dhs/meap/eligibility/md_meap_household_size.py @@ -0,0 +1,16 @@ +from policyengine_us.model_api import * + + +class md_meap_household_size(Variable): + value_type = int + entity = SPMUnit + definition_period = YEAR + label = "Maryland MEAP qualified household size" + defined_for = StateCode.MD + reference = ( + "https://regs.maryland.gov/us/md/exec/comar/07.03.21.04", + "https://dhs.maryland.gov/documents/OHEP/OHEP-Operations-Manual.pdf#page=107,108", + ) + documentation = "Follows COMAR 07.03.21.04C: nonqualified members are excluded from size and their income counts in full. The May 2025 manual instead includes all children under 18 regardless of status; this unresolved conflict is recorded explicitly, and this draft follows the regulation. SPM membership approximates the energy-sharing household. No SNAP-specific immigration bars apply." + + adds = ["is_citizen_or_legal_immigrant"] diff --git a/policyengine_us/variables/gov/states/md/dhs/meap/eligibility/md_meap_income_limit.py b/policyengine_us/variables/gov/states/md/dhs/meap/eligibility/md_meap_income_limit.py new file mode 100644 index 00000000000..5cb5050fd28 --- /dev/null +++ b/policyengine_us/variables/gov/states/md/dhs/meap/eligibility/md_meap_income_limit.py @@ -0,0 +1,28 @@ +from policyengine_us.model_api import * +from policyengine_us.variables.gov.hhs.hhs_smi import smi + + +class md_meap_income_limit(Variable): + value_type = float + entity = SPMUnit + definition_period = YEAR + label = "Maryland MEAP annual income limit" + unit = USD + defined_for = StateCode.MD + reference = ( + "https://dhs.maryland.gov/documents/OHEP/Advisory%20Board/Income-Guidelines-FY2026-Updated-7.9.2025.pdf", + "https://liheapch.acf.gov/docs/2026/state-plans/MD_Plan_2026.pdf#page=9", + ) + documentation = "The published annual 200% FPG amounts control sizes 1-10 rather than the plan's truncated equivalent SMI percentages or its inconsistent additional-member line. Annual inputs are compared with annual limits; the printed monthly amounts are rounded separately. Large-household dollar amounts are calculated from the stated SMI rule, not independently published FY26 dollar rows." + + def formula(spm_unit, period, parameters): + p = parameters(period).gov.states.md.dhs.meap.eligibility + size = max_(spm_unit("md_meap_household_size", period), 1) + state = spm_unit.household("state_code_str", period) + base = parameters(period).gov.hhs.smi.amount[state] + factor = smi(size, state, period, parameters) / base + # Federal 60% SMI table ordering; FY26 publishes the large-household + # rule, but no dollar rows at sizes 11 and above. + smi_limit = np.floor(factor * np.floor(base * p.smi_rate)) + poverty_limit = spm_unit("md_meap_fpg", period) * p.fpg_rate + return where(size >= p.smi_min_size, smi_limit, poverty_limit) diff --git a/policyengine_us/variables/gov/states/md/dhs/meap/income/md_meap_countable_earned_income.py b/policyengine_us/variables/gov/states/md/dhs/meap/income/md_meap_countable_earned_income.py new file mode 100644 index 00000000000..b88baf6fc1d --- /dev/null +++ b/policyengine_us/variables/gov/states/md/dhs/meap/income/md_meap_countable_earned_income.py @@ -0,0 +1,22 @@ +from policyengine_us.model_api import * + + +class md_meap_countable_earned_income(Variable): + value_type = float + entity = Person + definition_period = YEAR + label = "Maryland MEAP countable earnings" + unit = USD + defined_for = StateCode.MD + reference = ("https://regs.maryland.gov/us/md/exec/comar/07.03.21.04",) + documentation = "Uses existing net business income without an additional expense deduction. MEAP depreciation add-backs are unsupported. Flooring losses is a modeling convention because the sources do not specify their treatment." + + def formula(person, period, parameters): + p = parameters(period).gov.states.md.dhs.meap.income + earned = 0 + for source in p.sources.earned: + earned = earned + max_(person(source, period), 0) + counted = (person("age", period) >= p.earned_income_min_age) & ~person( + "is_full_time_student", period + ) + return where(counted, earned, 0) diff --git a/policyengine_us/variables/gov/states/md/dhs/meap/income/md_meap_countable_income.py b/policyengine_us/variables/gov/states/md/dhs/meap/income/md_meap_countable_income.py new file mode 100644 index 00000000000..6f1fe456915 --- /dev/null +++ b/policyengine_us/variables/gov/states/md/dhs/meap/income/md_meap_countable_income.py @@ -0,0 +1,26 @@ +from policyengine_us.model_api import * + + +class md_meap_countable_income(Variable): + value_type = float + entity = SPMUnit + definition_period = YEAR + label = "Maryland MEAP countable household income" + unit = USD + defined_for = StateCode.MD + reference = ("https://regs.maryland.gov/us/md/exec/comar/07.03.21.04",) + documentation = "Annual income approximates the 30-day application period. All members' income counts in full. Follows COMAR exclusions for nonrecurring lump sums and AmeriCorps/VISTA despite conflicting manual/plan checklists. Reported child support paid is assumed court-ordered. Veterans income approximates pension benefits and strike income is assumed not employee-funded. TDAP, royalties, gifts, loans, special allowances, home-care receipts and Medicare premiums beyond Part B are unsupported. Rental income is assumed nonnegative." + + def formula(spm_unit, period, parameters): + p = parameters(period).gov.states.md.dhs.meap.income + person = spm_unit.members + retirement = add(person, period, ["social_security", "railroad_benefits"]) + net_retirement = spm_unit.sum( + max_(retirement - person("medicare_part_b_premium", period), 0) + ) + income = ( + add(spm_unit, period, ["md_meap_countable_earned_income"]) + + add(spm_unit, period, p.sources.unearned) + + net_retirement + ) + return max_(income - add(spm_unit, period, ["child_support_expense"]), 0) diff --git a/policyengine_us/variables/gov/states/md/dhs/meap/income/md_meap_fpg.py b/policyengine_us/variables/gov/states/md/dhs/meap/income/md_meap_fpg.py new file mode 100644 index 00000000000..bc1a977fe4b --- /dev/null +++ b/policyengine_us/variables/gov/states/md/dhs/meap/income/md_meap_fpg.py @@ -0,0 +1,20 @@ +from policyengine_us.model_api import * +from policyengine_us.variables.gov.hhs.tax_unit_fpg import fpg + + +class md_meap_fpg(Variable): + value_type = float + entity = SPMUnit + definition_period = YEAR + label = "Maryland MEAP applicable federal poverty guideline" + unit = USD + defined_for = StateCode.MD + reference = ( + "https://dhs.maryland.gov/documents/OHEP/Advisory%20Board/Income-Guidelines-FY2026-Updated-7.9.2025.pdf", + ) + + def formula(spm_unit, period, parameters): + p = parameters(period).gov.states.md.dhs.meap.eligibility + size = max_(spm_unit("md_meap_household_size", period), 1) + state_group = spm_unit.household("state_group_str", period) + return fpg(size, state_group, period, parameters, year_lag=p.fpg_year_lag) diff --git a/policyengine_us/variables/gov/states/md/dhs/meap/md_meap.py b/policyengine_us/variables/gov/states/md/dhs/meap/md_meap.py new file mode 100644 index 00000000000..addde702ac0 --- /dev/null +++ b/policyengine_us/variables/gov/states/md/dhs/meap/md_meap.py @@ -0,0 +1,45 @@ +from policyengine_us.model_api import * + + +class md_meap(Variable): + value_type = float + entity = SPMUnit + definition_period = YEAR + label = "Maryland MEAP regular heating benefit" + unit = USD + defined_for = "md_meap_eligible" + reference = ( + "https://dhs.maryland.gov/documents/OHEP/Advisory%20Board/FY26-MEAP-Benefit-Matrix-2-1-1.pdf", + "https://liheapch.acf.gov/docs/2026/state-plans/MD_Plan_2026.pdf#page=9,10,11", + "https://regs.maryland.gov/us/md/exec/comar/07.03.21.07", + "https://dhs.maryland.gov/documents/OHEP/FY27-MEAP-Benefit-Matrices.pdf#page=1", + ) + documentation = "Annual regular heating assistance, verified for state FY26 (July 2025-June 2026), modeled in period 2026. Earlier results are unverified backfilled estimates. No actual-bill cap applies. Kerosene uses oil (explicitly labeled together in FY27); heat-in-rent uses the building's known fuel row, an inference because no separate renter table is published. Unknown fuels return zero; unknown county receives the statewide amount and cannot receive the Garrett supplement. Sub-metering and means-tested VA eligibility remain unsupported. Cooling, EUSP, arrearages, crisis and equipment benefits are excluded." + + def formula(spm_unit, period, parameters): + p = parameters(period).gov.states.md.dhs.meap.payment + level = spm_unit("md_meap_level", period) + fuel = spm_unit("heating_type", period) + types = fuel.possible_values + amount = select( + [ + fuel == types.ELECTRICITY, + (fuel == types.NATURAL_GAS) + | (fuel == types.WOOD) + | (fuel == types.COAL), + (fuel == types.FUEL_OIL) | (fuel == types.KEROSENE), + fuel == types.PROPANE, + ], + [ + p.amount.electricity[level], + p.amount.gas_wood_coal[level], + p.amount.oil[level], + p.amount.propane[level], + ], + default=0, + ) + county = spm_unit.household("county_str", period) + regional = np.isin(county, p.regional_counties) & (level != p.nominal_level) + # Half-up rounding and the flat nominal amount are inferred from the + # later published Garrett table; no FY26 Garrett dollar table was found. + return np.floor(amount * where(regional, p.garrett_multiplier, 1) + 0.5) diff --git a/policyengine_us/variables/gov/states/md/dhs/meap/payment/md_meap_level.py b/policyengine_us/variables/gov/states/md/dhs/meap/payment/md_meap_level.py new file mode 100644 index 00000000000..c48f093e2bd --- /dev/null +++ b/policyengine_us/variables/gov/states/md/dhs/meap/payment/md_meap_level.py @@ -0,0 +1,30 @@ +from policyengine_us.model_api import * + + +class md_meap_level(Variable): + value_type = int + entity = SPMUnit + definition_period = YEAR + label = "Maryland MEAP heating payment level" + defined_for = StateCode.MD + reference = ( + "https://dhs.maryland.gov/documents/OHEP/Advisory%20Board/FY26-MEAP-Benefit-Matrix-2-1-1.pdf", + "https://dhs.maryland.gov/documents/OHEP/OHEP-Operations-Manual.pdf#page=62,63", + ) + documentation = "Sub-metered homes also belong in Level 6, but no authorized input identifies them. The over-200% categorical nominal level takes precedence over subsidized status; this precedence follows the manual's nominal-payment instruction. Existing assistance inputs approximate the regulation's narrower subsidy definition." + + def formula(spm_unit, period, parameters): + p = parameters(period).gov.states.md.dhs.meap.payment + ratio = spm_unit("md_meap_countable_income", period) / spm_unit( + "md_meap_fpg", period + ) + # Inclusive band tops resolve the gaps between integer percentage labels. + income_level = p.income_level.calc(ratio, right=True) + subsidized = spm_unit( + "receives_housing_assistance", period + ) | spm_unit.household("is_in_public_housing", period) + return where( + (income_level != p.nominal_level) & subsidized, + p.subsidized_level, + income_level, + ) From 772e8665a7cf3cb14c3b03f10098284ab705eeee Mon Sep 17 00:00:00 2001 From: Ziming Date: Fri, 2 Oct 2026 12:14:53 -0400 Subject: [PATCH 2/8] Handle Maryland MEAP submetered and assisted-living dwellings --- policyengine_us/programs.yaml | 2 +- .../gov/states/md/dhs/meap/md_meap.yaml | 14 +++++++++++++ .../dhs/meap/eligibility/md_meap_eligible.py | 5 ++++- .../gov/states/md/dhs/meap/md_meap.py | 2 +- .../md/dhs/meap/md_meap_dwelling_type.py | 21 +++++++++++++++++++ .../md/dhs/meap/payment/md_meap_level.py | 6 ++++-- 6 files changed, 45 insertions(+), 5 deletions(-) create mode 100644 policyengine_us/variables/gov/states/md/dhs/meap/md_meap_dwelling_type.py diff --git a/policyengine_us/programs.yaml b/policyengine_us/programs.yaml index 4f08081de7a..9df24d0efe3 100644 --- a/policyengine_us/programs.yaml +++ b/policyengine_us/programs.yaml @@ -811,7 +811,7 @@ programs: variable: md_meap parameter_prefix: gov.states.md.dhs.meap verified_years: "2026" - notes: Regular heating with annual income limits, categorical eligibility, fuel/poverty/subsidy payments and Garrett multiplier. Uses COMAR qualified-member size despite the manual child exception; income-source conflicts are resolved to COMAR. Sub-metering, means-tested VA, special income and precise subsidy definitions remain unsupported. Garrett rounding and heat-in-rent fuel assignment are documented inferences. Earlier years are unverified backfilled estimates. + notes: Regular heating with annual income limits, categorical eligibility, fuel/poverty/subsidy/submetered payments, assisted-living exclusion and Garrett multiplier. Uses COMAR qualified-member size despite the manual child exception; income-source conflicts are resolved to COMAR. Means-tested VA, special income and precise subsidy definitions remain unsupported. Garrett rounding and heat-in-rent fuel assignment are documented inferences. Earlier years are unverified backfilled estimates. - state: OR status: in_progress name: LIHEAP diff --git a/policyengine_us/tests/policy/baseline/gov/states/md/dhs/meap/md_meap.yaml b/policyengine_us/tests/policy/baseline/gov/states/md/dhs/meap/md_meap.yaml index 2ccaa3cef1a..a71eab0eca1 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/md/dhs/meap/md_meap.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/md/dhs/meap/md_meap.yaml @@ -67,3 +67,17 @@ md_meap_level: [1, 1, 1, 1, 1, 1] output: md_meap: [1100, 550, 0, 0, 0, 0] +- name: Submetered dwelling payment and assisted-living exclusion + period: 2026 + input: + state_code: [MD, MD, MD, MD] + county_str: [MONTGOMERY_COUNTY_MD, MONTGOMERY_COUNTY_MD, MONTGOMERY_COUNTY_MD, MONTGOMERY_COUNTY_MD] + heating_type: [NATURAL_GAS, NATURAL_GAS, NATURAL_GAS, NATURAL_GAS] + has_heating_expense: [true, true, true, true] + md_meap_countable_income: [0, 0, 0, 40000] + md_meap_categorically_eligible: [false, false, false, true] + md_meap_dwelling_type: [STANDARD, SUB_METERED, ASSISTED_LIVING, SUB_METERED] + output: + md_meap_eligible: [true, true, false, true] + md_meap_level: [1, 6, 1, 7] + md_meap: [550, 225, 0, 25] diff --git a/policyengine_us/variables/gov/states/md/dhs/meap/eligibility/md_meap_eligible.py b/policyengine_us/variables/gov/states/md/dhs/meap/eligibility/md_meap_eligible.py index 966357ab18e..1405b77a63e 100644 --- a/policyengine_us/variables/gov/states/md/dhs/meap/eligibility/md_meap_eligible.py +++ b/policyengine_us/variables/gov/states/md/dhs/meap/eligibility/md_meap_eligible.py @@ -12,10 +12,12 @@ class md_meap_eligible(Variable): "https://regs.maryland.gov/us/md/exec/comar/07.03.21.06", "https://dhs.maryland.gov/documents/OHEP/OHEP-Operations-Manual.pdf#page=11,12,16,39", ) - documentation = "Regular heating has no resource test. Assisted-living residence, application documentation, emancipation, program-year duplicate awards and the optional medical waiver are not modeled. At least one qualified member is required." + documentation = "Regular heating has no resource test. Application documentation, emancipation and program-year duplicate awards are not modeled. The regulation permits an annually announced medical-expense waiver, but no such regular-heating waiver was found in the FY2026 plan. At least one qualified member is required." def formula(spm_unit, period, parameters): heat_in_rent = spm_unit("heat_expense_included_in_rent", period) + dwelling = spm_unit("md_meap_dwelling_type", period) + assisted_living = dwelling == dwelling.possible_values.ASSISTED_LIVING subsidized = spm_unit( "receives_housing_assistance", period ) | spm_unit.household("is_in_public_housing", period) @@ -28,4 +30,5 @@ def formula(spm_unit, period, parameters): & (spm_unit("md_meap_household_size", period) > 0) & (spm_unit("has_heating_expense", period) | heat_in_rent) & ~(heat_in_rent & subsidized) + & ~assisted_living ) diff --git a/policyengine_us/variables/gov/states/md/dhs/meap/md_meap.py b/policyengine_us/variables/gov/states/md/dhs/meap/md_meap.py index addde702ac0..1b491ce7821 100644 --- a/policyengine_us/variables/gov/states/md/dhs/meap/md_meap.py +++ b/policyengine_us/variables/gov/states/md/dhs/meap/md_meap.py @@ -14,7 +14,7 @@ class md_meap(Variable): "https://regs.maryland.gov/us/md/exec/comar/07.03.21.07", "https://dhs.maryland.gov/documents/OHEP/FY27-MEAP-Benefit-Matrices.pdf#page=1", ) - documentation = "Annual regular heating assistance, verified for state FY26 (July 2025-June 2026), modeled in period 2026. Earlier results are unverified backfilled estimates. No actual-bill cap applies. Kerosene uses oil (explicitly labeled together in FY27); heat-in-rent uses the building's known fuel row, an inference because no separate renter table is published. Unknown fuels return zero; unknown county receives the statewide amount and cannot receive the Garrett supplement. Sub-metering and means-tested VA eligibility remain unsupported. Cooling, EUSP, arrearages, crisis and equipment benefits are excluded." + documentation = "Annual regular heating assistance, verified for state FY26 (July 2025-June 2026), modeled in period 2026. Earlier results are unverified backfilled estimates. No actual-bill cap applies. Kerosene uses oil (explicitly labeled together in FY27); heat-in-rent uses the building's known fuel row, an inference because no separate renter table is published. Unknown fuels return zero; unknown county receives the statewide amount and cannot receive the Garrett supplement. Means-tested VA eligibility remains unsupported. Cooling, EUSP, arrearages, crisis and equipment benefits are excluded." def formula(spm_unit, period, parameters): p = parameters(period).gov.states.md.dhs.meap.payment diff --git a/policyengine_us/variables/gov/states/md/dhs/meap/md_meap_dwelling_type.py b/policyengine_us/variables/gov/states/md/dhs/meap/md_meap_dwelling_type.py new file mode 100644 index 00000000000..404c78c8e41 --- /dev/null +++ b/policyengine_us/variables/gov/states/md/dhs/meap/md_meap_dwelling_type.py @@ -0,0 +1,21 @@ +from policyengine_us.model_api import * + + +class MDMEAPDwellingType(Enum): + STANDARD = "Standard residence" + SUB_METERED = "Submetered residence" + ASSISTED_LIVING = "Assisted living facility" + + +class md_meap_dwelling_type(Variable): + value_type = Enum + possible_values = MDMEAPDwellingType + default_value = MDMEAPDwellingType.STANDARD + entity = SPMUnit + definition_period = YEAR + label = "Maryland MEAP dwelling arrangement" + reference = ( + "https://dhs.maryland.gov/documents/OHEP/Advisory%20Board/FY26-MEAP-Benefit-Matrix-2-1-1.pdf", + "https://regs.maryland.gov/us/md/exec/comar/07.03.21.06#D(3)(k)", + ) + documentation = "Submetered homes use the published Level 6 payment; assisted-living applicants are ineligible under COMAR 07.03.21.06D(3)(k). Ordinary subsidized homes use the existing housing-assistance inputs." diff --git a/policyengine_us/variables/gov/states/md/dhs/meap/payment/md_meap_level.py b/policyengine_us/variables/gov/states/md/dhs/meap/payment/md_meap_level.py index c48f093e2bd..56d0037a605 100644 --- a/policyengine_us/variables/gov/states/md/dhs/meap/payment/md_meap_level.py +++ b/policyengine_us/variables/gov/states/md/dhs/meap/payment/md_meap_level.py @@ -11,7 +11,7 @@ class md_meap_level(Variable): "https://dhs.maryland.gov/documents/OHEP/Advisory%20Board/FY26-MEAP-Benefit-Matrix-2-1-1.pdf", "https://dhs.maryland.gov/documents/OHEP/OHEP-Operations-Manual.pdf#page=62,63", ) - documentation = "Sub-metered homes also belong in Level 6, but no authorized input identifies them. The over-200% categorical nominal level takes precedence over subsidized status; this precedence follows the manual's nominal-payment instruction. Existing assistance inputs approximate the regulation's narrower subsidy definition." + documentation = "Submetered and subsidized homes use Level 6. The over-200% categorical nominal level takes precedence over these housing categories; this precedence follows the manual's nominal-payment instruction. Existing assistance inputs approximate the regulation's narrower subsidy definition." def formula(spm_unit, period, parameters): p = parameters(period).gov.states.md.dhs.meap.payment @@ -23,8 +23,10 @@ def formula(spm_unit, period, parameters): subsidized = spm_unit( "receives_housing_assistance", period ) | spm_unit.household("is_in_public_housing", period) + dwelling = spm_unit("md_meap_dwelling_type", period) + submetered = dwelling == dwelling.possible_values.SUB_METERED return where( - (income_level != p.nominal_level) & subsidized, + (income_level != p.nominal_level) & (subsidized | submetered), p.subsidized_level, income_level, ) From 764e38c49c202077a01f75fb7ac2ecdc83007b62 Mon Sep 17 00:00:00 2001 From: Ziming Date: Fri, 2 Oct 2026 12:56:44 -0400 Subject: [PATCH 3/8] Align Maryland MEAP income receipt and deferred deductions --- .../md/dhs/meap/income/sources/unearned.yaml | 7 ++++-- policyengine_us/programs.yaml | 2 +- .../md_meap_categorically_eligible.yaml | 15 +++++++++++- .../meap/income/md_meap_countable_income.yaml | 24 ++++++++++++++----- .../gov/states/md/dhs/meap/integration.yaml | 6 ++--- .../md_meap_categorically_eligible.py | 5 ++-- .../dhs/meap/eligibility/md_meap_eligible.py | 3 ++- .../eligibility/md_meap_household_size.py | 3 ++- .../meap/income/md_meap_countable_income.py | 17 ++++++------- .../gov/states/md/dhs/meap/md_meap.py | 3 ++- .../md/dhs/meap/payment/md_meap_level.py | 3 ++- 11 files changed, 59 insertions(+), 29 deletions(-) diff --git a/policyengine_us/parameters/gov/states/md/dhs/meap/income/sources/unearned.yaml b/policyengine_us/parameters/gov/states/md/dhs/meap/income/sources/unearned.yaml index 0c7e1b18449..2eebe4f71fa 100644 --- a/policyengine_us/parameters/gov/states/md/dhs/meap/income/sources/unearned.yaml +++ b/policyengine_us/parameters/gov/states/md/dhs/meap/income/sources/unearned.yaml @@ -1,9 +1,12 @@ description: Maryland counts these available unearned income sources under the Maryland Energy Assistance Program. values: '2025-07-01': + # Medicare deductions from Social Security and railroad benefits are deferred. + - social_security + - railroad_benefits - ssi - - md_tca - - dividend_income + - tanf + - ordinary_dividend_income - interest_income - estate_income - pension_income diff --git a/policyengine_us/programs.yaml b/policyengine_us/programs.yaml index 9df24d0efe3..201dbe65b95 100644 --- a/policyengine_us/programs.yaml +++ b/policyengine_us/programs.yaml @@ -811,7 +811,7 @@ programs: variable: md_meap parameter_prefix: gov.states.md.dhs.meap verified_years: "2026" - notes: Regular heating with annual income limits, categorical eligibility, fuel/poverty/subsidy/submetered payments, assisted-living exclusion and Garrett multiplier. Uses COMAR qualified-member size despite the manual child exception; income-source conflicts are resolved to COMAR. Means-tested VA, special income and precise subsidy definitions remain unsupported. Garrett rounding and heat-in-rent fuel assignment are documented inferences. Earlier years are unverified backfilled estimates. + notes: Regular heating eligibility, fuel/housing schedules and Garrett supplement. Conflicting child-membership rules and some income/housing details remain partial; earlier years are unverified estimates. - state: OR status: in_progress name: LIHEAP diff --git a/policyengine_us/tests/policy/baseline/gov/states/md/dhs/meap/eligibility/md_meap_categorically_eligible.yaml b/policyengine_us/tests/policy/baseline/gov/states/md/dhs/meap/eligibility/md_meap_categorically_eligible.yaml index d62244a8442..c9acc84fd28 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/md/dhs/meap/eligibility/md_meap_categorically_eligible.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/md/dhs/meap/eligibility/md_meap_categorically_eligible.yaml @@ -4,8 +4,21 @@ heating_type: [NATURAL_GAS, NATURAL_GAS, NATURAL_GAS, NATURAL_GAS] state_code: [MD, MD, MD, MD] snap: [1, 0, 0, 0] - md_tca: [0, 1, 0, 0] + tanf: [0, 1, 0, 0] ssi: [0, 0, 1, 0] veterans_benefits: [0, 0, 0, 1000] output: md_meap_categorically_eligible: [true, true, true, false] + +- name: Unclaimed state cash entitlement does not confer categorical eligibility + period: 2026 + input: + state_code: [MD, MD] + heating_type: [NATURAL_GAS, NATURAL_GAS] + age: [40, 40] + md_tca: [1200, 1200] + takes_up_tanf_if_eligible: [false, true] + snap: [0, 0] + ssi: [0, 0] + output: + md_meap_categorically_eligible: [false, true] diff --git a/policyengine_us/tests/policy/baseline/gov/states/md/dhs/meap/income/md_meap_countable_income.yaml b/policyengine_us/tests/policy/baseline/gov/states/md/dhs/meap/income/md_meap_countable_income.yaml index 848cfc6b138..db13efcc4fe 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/md/dhs/meap/income/md_meap_countable_income.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/md/dhs/meap/income/md_meap_countable_income.yaml @@ -1,5 +1,5 @@ -# 10,000 earnings + 11,400 net SSA/railroad + 9,240 other receipts - 2,400 support = 28,240. -- name: Count pensions and IRA withdrawals separately, net Medicare and court-ordered support paid +# 10,000 earnings + 12,600 SSA/railroad + 9,240 other receipts - 2,400 support = 29,440. +- name: Count pensions and IRA withdrawals separately, deduct court-ordered support while Medicare deductions remain deferred period: 2026 input: heating_type: NATURAL_GAS @@ -11,7 +11,7 @@ pension_income: 1200 retirement_distributions: 2400 interest_income: 120 - dividend_income: 240 + ordinary_dividend_income: 240 estate_income: 360 rental_income: 1200 child_support_received: 600 @@ -21,10 +21,10 @@ veterans_benefits: 960 strike_benefits: 120 ssi: 0 - md_tca: 0 + tanf: 0 child_support_expense: 2400 output: - md_meap_countable_income: 28240 + md_meap_countable_income: 29440 - name: Support deduction cannot make countable income negative period: 2026 input: @@ -32,7 +32,19 @@ state_code: MD md_meap_countable_earned_income: 0 ssi: 0 - md_tca: 0 + tanf: 0 child_support_expense: 10000 output: md_meap_countable_income: 0 + +- name: State cash entitlement counts only when TANF is taken up + period: 2026 + input: + state_code: [MD, MD] + heating_type: [NATURAL_GAS, NATURAL_GAS] + age: [40, 40] + md_tca: [1200, 1200] + takes_up_tanf_if_eligible: [false, true] + ssi: [0, 0] + output: + md_meap_countable_income: [0, 1200] diff --git a/policyengine_us/tests/policy/baseline/gov/states/md/dhs/meap/integration.yaml b/policyengine_us/tests/policy/baseline/gov/states/md/dhs/meap/integration.yaml index cb837cb7f45..a58c9212151 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/md/dhs/meap/integration.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/md/dhs/meap/integration.yaml @@ -19,7 +19,7 @@ heating_type: NATURAL_GAS gas_expense: 1 snap: 0 - md_tca: 0 + tanf: 0 households: home: members: @@ -42,7 +42,7 @@ age: 40 employment_income: 100000 snap: 120 - md_tca: 0 + tanf: 0 ssi: 0 receives_housing_assistance: true heating_type: PROPANE @@ -61,7 +61,7 @@ age: 40 employment_income: 10000 snap: 0 - md_tca: 0 + tanf: 0 ssi: 0 heat_expense_included_in_rent: true heating_type: FUEL_OIL diff --git a/policyengine_us/variables/gov/states/md/dhs/meap/eligibility/md_meap_categorically_eligible.py b/policyengine_us/variables/gov/states/md/dhs/meap/eligibility/md_meap_categorically_eligible.py index b5d616b99e6..d94f4a93a14 100644 --- a/policyengine_us/variables/gov/states/md/dhs/meap/eligibility/md_meap_categorically_eligible.py +++ b/policyengine_us/variables/gov/states/md/dhs/meap/eligibility/md_meap_categorically_eligible.py @@ -8,10 +8,11 @@ class md_meap_categorically_eligible(Variable): label = "Maryland MEAP categorical income eligibility" defined_for = StateCode.MD reference = ( - "https://dhs.maryland.gov/documents/OHEP/OHEP-Operations-Manual.pdf#page=11,62,63", + # PDF pages 11, 62, 63. + "https://dhs.maryland.gov/documents/OHEP/OHEP-Operations-Manual.pdf#page=11", "https://liheapch.acf.gov/docs/2026/state-plans/MD_Plan_2026.pdf#page=5", ) documentation = "One member's receipt of SNAP, TCA or SSI waives the income test. Means-tested veterans benefits are also legally qualifying, but generic veterans_benefits cannot identify that subset. Recurring receipt in the program year is approximated by annual benefit receipt." def formula(spm_unit, period, parameters): - return add(spm_unit, period, ["snap", "md_tca", "ssi"]) > 0 + return add(spm_unit, period, ["snap", "tanf", "ssi"]) > 0 diff --git a/policyengine_us/variables/gov/states/md/dhs/meap/eligibility/md_meap_eligible.py b/policyengine_us/variables/gov/states/md/dhs/meap/eligibility/md_meap_eligible.py index 1405b77a63e..b96ec0add51 100644 --- a/policyengine_us/variables/gov/states/md/dhs/meap/eligibility/md_meap_eligible.py +++ b/policyengine_us/variables/gov/states/md/dhs/meap/eligibility/md_meap_eligible.py @@ -10,7 +10,8 @@ class md_meap_eligible(Variable): reference = ( "https://regs.maryland.gov/us/md/exec/comar/07.03.21.03", "https://regs.maryland.gov/us/md/exec/comar/07.03.21.06", - "https://dhs.maryland.gov/documents/OHEP/OHEP-Operations-Manual.pdf#page=11,12,16,39", + # PDF pages 11, 12, 16, 39. + "https://dhs.maryland.gov/documents/OHEP/OHEP-Operations-Manual.pdf#page=11", ) documentation = "Regular heating has no resource test. Application documentation, emancipation and program-year duplicate awards are not modeled. The regulation permits an annually announced medical-expense waiver, but no such regular-heating waiver was found in the FY2026 plan. At least one qualified member is required." diff --git a/policyengine_us/variables/gov/states/md/dhs/meap/eligibility/md_meap_household_size.py b/policyengine_us/variables/gov/states/md/dhs/meap/eligibility/md_meap_household_size.py index 85d56440cff..6fe46192ac3 100644 --- a/policyengine_us/variables/gov/states/md/dhs/meap/eligibility/md_meap_household_size.py +++ b/policyengine_us/variables/gov/states/md/dhs/meap/eligibility/md_meap_household_size.py @@ -9,7 +9,8 @@ class md_meap_household_size(Variable): defined_for = StateCode.MD reference = ( "https://regs.maryland.gov/us/md/exec/comar/07.03.21.04", - "https://dhs.maryland.gov/documents/OHEP/OHEP-Operations-Manual.pdf#page=107,108", + # PDF pages 107, 108. + "https://dhs.maryland.gov/documents/OHEP/OHEP-Operations-Manual.pdf#page=107", ) documentation = "Follows COMAR 07.03.21.04C: nonqualified members are excluded from size and their income counts in full. The May 2025 manual instead includes all children under 18 regardless of status; this unresolved conflict is recorded explicitly, and this draft follows the regulation. SPM membership approximates the energy-sharing household. No SNAP-specific immigration bars apply." diff --git a/policyengine_us/variables/gov/states/md/dhs/meap/income/md_meap_countable_income.py b/policyengine_us/variables/gov/states/md/dhs/meap/income/md_meap_countable_income.py index 6f1fe456915..93d76dae81c 100644 --- a/policyengine_us/variables/gov/states/md/dhs/meap/income/md_meap_countable_income.py +++ b/policyengine_us/variables/gov/states/md/dhs/meap/income/md_meap_countable_income.py @@ -9,18 +9,15 @@ class md_meap_countable_income(Variable): unit = USD defined_for = StateCode.MD reference = ("https://regs.maryland.gov/us/md/exec/comar/07.03.21.04",) - documentation = "Annual income approximates the 30-day application period. All members' income counts in full. Follows COMAR exclusions for nonrecurring lump sums and AmeriCorps/VISTA despite conflicting manual/plan checklists. Reported child support paid is assumed court-ordered. Veterans income approximates pension benefits and strike income is assumed not employee-funded. TDAP, royalties, gifts, loans, special allowances, home-care receipts and Medicare premiums beyond Part B are unsupported. Rental income is assumed nonnegative." + documentation = "Annual income approximates the 30-day application period. All members' income counts in full. Follows COMAR exclusions for nonrecurring lump sums and AmeriCorps/VISTA despite conflicting manual/plan checklists. Reported child support paid is assumed court-ordered. Veterans income approximates pension benefits and strike income is assumed not employee-funded. TDAP, royalties, gifts, loans, special allowances and home-care receipts cannot be isolated reliably. Medicare-premium deductions remain deferred. Rental income is assumed nonnegative." def formula(spm_unit, period, parameters): p = parameters(period).gov.states.md.dhs.meap.income - person = spm_unit.members - retirement = add(person, period, ["social_security", "railroad_benefits"]) - net_retirement = spm_unit.sum( - max_(retirement - person("medicare_part_b_premium", period), 0) - ) - income = ( - add(spm_unit, period, ["md_meap_countable_earned_income"]) - + add(spm_unit, period, p.sources.unearned) - + net_retirement + # COMAR .04D(26) and E(20) allow Medicare deductions from the + # relevant benefits. Actual-premium handling remains deferred; neither + # modeled nor reported Medicare premiums are deducted here. + # The TANF source applies take-up to Maryland TCA entitlement. + income = add(spm_unit, period, ["md_meap_countable_earned_income"]) + add( + spm_unit, period, p.sources.unearned ) return max_(income - add(spm_unit, period, ["child_support_expense"]), 0) diff --git a/policyengine_us/variables/gov/states/md/dhs/meap/md_meap.py b/policyengine_us/variables/gov/states/md/dhs/meap/md_meap.py index 1b491ce7821..16f5501cc72 100644 --- a/policyengine_us/variables/gov/states/md/dhs/meap/md_meap.py +++ b/policyengine_us/variables/gov/states/md/dhs/meap/md_meap.py @@ -10,7 +10,8 @@ class md_meap(Variable): defined_for = "md_meap_eligible" reference = ( "https://dhs.maryland.gov/documents/OHEP/Advisory%20Board/FY26-MEAP-Benefit-Matrix-2-1-1.pdf", - "https://liheapch.acf.gov/docs/2026/state-plans/MD_Plan_2026.pdf#page=9,10,11", + # PDF pages 9, 10, 11. + "https://liheapch.acf.gov/docs/2026/state-plans/MD_Plan_2026.pdf#page=9", "https://regs.maryland.gov/us/md/exec/comar/07.03.21.07", "https://dhs.maryland.gov/documents/OHEP/FY27-MEAP-Benefit-Matrices.pdf#page=1", ) diff --git a/policyengine_us/variables/gov/states/md/dhs/meap/payment/md_meap_level.py b/policyengine_us/variables/gov/states/md/dhs/meap/payment/md_meap_level.py index 56d0037a605..f02c7baa88c 100644 --- a/policyengine_us/variables/gov/states/md/dhs/meap/payment/md_meap_level.py +++ b/policyengine_us/variables/gov/states/md/dhs/meap/payment/md_meap_level.py @@ -9,7 +9,8 @@ class md_meap_level(Variable): defined_for = StateCode.MD reference = ( "https://dhs.maryland.gov/documents/OHEP/Advisory%20Board/FY26-MEAP-Benefit-Matrix-2-1-1.pdf", - "https://dhs.maryland.gov/documents/OHEP/OHEP-Operations-Manual.pdf#page=62,63", + # PDF pages 62, 63. + "https://dhs.maryland.gov/documents/OHEP/OHEP-Operations-Manual.pdf#page=62", ) documentation = "Submetered and subsidized homes use Level 6. The over-200% categorical nominal level takes precedence over these housing categories; this precedence follows the manual's nominal-payment instruction. Existing assistance inputs approximate the regulation's narrower subsidy definition." From 02a0381e62588fe3251f78d3bc93bd0e8e923602 Mon Sep 17 00:00:00 2001 From: Ziming Date: Sat, 3 Oct 2026 01:52:27 -0400 Subject: [PATCH 4/8] Fix issues from review: floor the payment level, registry order, tests Floor the payment level before the matrix lookup so units outside Maryland index a valid key in mixed-state simulations, and add a two-state case. Name the Medicare premium variables that are not wired in and cite COMAR 07.03.21.04D(3). Place Maryland between IL and TX in programs.yaml and shorten the note. Bring the nine test files to the shared conventions with fuel bills, realistic amounts and derivation comments, and add end-to-end cases for multi-member, Garrett, over-income, public-housing, size 11+ and heat-in-rent households. Co-Authored-By: Claude Fable 5.1 --- .../md/dhs/meap/income/sources/unearned.yaml | 5 +- policyengine_us/programs.yaml | 18 +- .../md_meap_categorically_eligible.yaml | 33 +- .../meap/eligibility/md_meap_eligible.yaml | 86 +++- .../eligibility/md_meap_income_limit.yaml | 30 +- .../md_meap_countable_earned_income.yaml | 22 +- .../meap/income/md_meap_countable_income.yaml | 51 +- .../md/dhs/meap/income/md_meap_fpg.yaml | 26 +- .../gov/states/md/dhs/meap/integration.yaml | 434 ++++++++++++++++-- .../gov/states/md/dhs/meap/md_meap.yaml | 100 +++- .../md/dhs/meap/payment/md_meap_level.yaml | 51 +- .../meap/income/md_meap_countable_income.py | 7 +- .../gov/states/md/dhs/meap/md_meap.py | 3 +- 13 files changed, 744 insertions(+), 122 deletions(-) diff --git a/policyengine_us/parameters/gov/states/md/dhs/meap/income/sources/unearned.yaml b/policyengine_us/parameters/gov/states/md/dhs/meap/income/sources/unearned.yaml index 2eebe4f71fa..15470237144 100644 --- a/policyengine_us/parameters/gov/states/md/dhs/meap/income/sources/unearned.yaml +++ b/policyengine_us/parameters/gov/states/md/dhs/meap/income/sources/unearned.yaml @@ -1,7 +1,10 @@ description: Maryland counts these available unearned income sources under the Maryland Energy Assistance Program. values: '2025-07-01': - # Medicare deductions from Social Security and railroad benefits are deferred. + # COMAR 07.03.21.04D(3), D(26) and E(20) count Social Security and railroad + # retirement benefits less the Medicare payment deduction. + # medicare_part_b_premium and medicare_part_b_premiums_reported are not wired + # in yet, so both count gross. - social_security - railroad_benefits - ssi diff --git a/policyengine_us/programs.yaml b/policyengine_us/programs.yaml index 201dbe65b95..4ab7c62a47a 100644 --- a/policyengine_us/programs.yaml +++ b/policyengine_us/programs.yaml @@ -802,16 +802,8 @@ programs: category: Benefits agency: HHS status: partial - coverage: OR, DC, Riverside County, MA, IL, TX, MD + coverage: OR, DC, Riverside County, MA, IL, MD, TX state_implementations: - - state: MD - status: partial - name: Maryland MEAP - full_name: Maryland Energy Assistance Program - variable: md_meap - parameter_prefix: gov.states.md.dhs.meap - verified_years: "2026" - notes: Regular heating eligibility, fuel/housing schedules and Garrett supplement. Conflicting child-membership rules and some income/housing details remain partial; earlier years are unverified estimates. - state: OR status: in_progress name: LIHEAP @@ -831,6 +823,14 @@ programs: name: Illinois LIHEAP full_name: Illinois Low Income Home Energy Assistance Program variable: il_liheap + - state: MD + status: partial + name: Maryland MEAP + full_name: Maryland Energy Assistance Program + variable: md_meap + parameter_prefix: gov.states.md.dhs.meap + verified_years: "2026" + notes: FY2026 regular heating only, with eligibility, countable income, the benefit matrix and the Garrett County supplement. EUSP, crisis, cooling and equipment assistance are out of scope. - state: TX status: partial name: Texas CEAP diff --git a/policyengine_us/tests/policy/baseline/gov/states/md/dhs/meap/eligibility/md_meap_categorically_eligible.yaml b/policyengine_us/tests/policy/baseline/gov/states/md/dhs/meap/eligibility/md_meap_categorically_eligible.yaml index c9acc84fd28..128b8870e17 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/md/dhs/meap/eligibility/md_meap_categorically_eligible.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/md/dhs/meap/eligibility/md_meap_categorically_eligible.yaml @@ -1,24 +1,39 @@ -- name: Receipt of SNAP, TCA or SSI independently waives the income test +# Sources for the expected values in this file: +# - OHEP Operations Manual (May 2025), page 62, section 6.0 Categorical Eligibility: +# https://dhs.maryland.gov/documents/OHEP/OHEP-Operations-Manual.pdf#page=62 +# - FY2026 LIHEAP State Plan, page 5 (categorical eligibility by program): +# https://liheapch.acf.gov/docs/2026/state-plans/MD_Plan_2026.pdf#page=5 + +- name: Case 1, SNAP, TCA or SSI receipt each confers categorical eligibility. period: 2026 input: - heating_type: [NATURAL_GAS, NATURAL_GAS, NATURAL_GAS, NATURAL_GAS] state_code: [MD, MD, MD, MD] - snap: [1, 0, 0, 0] - tanf: [0, 1, 0, 0] - ssi: [0, 0, 1, 0] - veterans_benefits: [0, 0, 0, 1000] + heating_type: [NATURAL_GAS, NATURAL_GAS, NATURAL_GAS, NATURAL_GAS] + # $200 a month of SNAP, $300 a month of TCA, and SSI at the 2026 federal rate + # of $994 a month. + snap: [2_400, 0, 0, 0] + tanf: [0, 3_600, 0, 0] + ssi: [0, 0, 11_928, 0] + veterans_benefits: [0, 0, 0, 1_000] output: + # Manual page 62: households participating in SNAP, TANF, SSI or means-tested VA + # programs are categorically eligible, and any member's receipt qualifies the + # household. The fourth household has veterans benefits only: the model cannot + # tell whether they are means-tested, so they do not qualify it (documented + # limitation). md_meap_categorically_eligible: [true, true, true, false] -- name: Unclaimed state cash entitlement does not confer categorical eligibility +- name: Case 2, TCA entitlement that is not taken up does not confer categorical eligibility. period: 2026 input: state_code: [MD, MD] heating_type: [NATURAL_GAS, NATURAL_GAS] - age: [40, 40] - md_tca: [1200, 1200] + md_tca: [1_200, 1_200] takes_up_tanf_if_eligible: [false, true] snap: [0, 0] ssi: [0, 0] output: + # State Plan page 5: a household is categorically income-eligible if a member + # receives the benefit on an ongoing basis. The first household is entitled to + # TCA but does not receive it; the second receives it. md_meap_categorically_eligible: [false, true] diff --git a/policyengine_us/tests/policy/baseline/gov/states/md/dhs/meap/eligibility/md_meap_eligible.yaml b/policyengine_us/tests/policy/baseline/gov/states/md/dhs/meap/eligibility/md_meap_eligible.yaml index 7af920170c6..8b075cfeeff 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/md/dhs/meap/eligibility/md_meap_eligible.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/md/dhs/meap/eligibility/md_meap_eligible.yaml @@ -1,53 +1,117 @@ -- name: Annual income limit is inclusive +# Sources for the expected values in this file: +# - FY2026 OHEP Income Guidelines (one page): +# https://dhs.maryland.gov/documents/OHEP/Advisory%20Board/Income-Guidelines-FY2026-Updated-7.9.2025.pdf +# - OHEP Operations Manual (May 2025), pages 16 (general eligibility), 39 (automatic +# denials), 62-63 (categorical eligibility) and 108 (ineligible members): +# https://dhs.maryland.gov/documents/OHEP/OHEP-Operations-Manual.pdf#page=16 +# - FY2026 LIHEAP State Plan, page 9 (renters in subsidized housing): +# https://liheapch.acf.gov/docs/2026/state-plans/MD_Plan_2026.pdf#page=9 +# - COMAR 07.03.21.03 (residence), .04C (nonqualified members) and .06D(3) (denials): +# https://regs.maryland.gov/us/md/exec/comar/07.03.21.06 + +- name: Case 1, one-person household at and one dollar above the annual income limit. period: 2026 input: state_code: [MD, MD] heating_type: [NATURAL_GAS, NATURAL_GAS] - has_heating_expense: [true, true] - md_meap_countable_income: [31300, 31301] + gas_expense: [1_150, 1_150] + md_meap_countable_income: [31_300, 31_301] md_meap_categorically_eligible: [false, false] output: + # Income Guidelines, annual 200% FPG table, size 1: $31,300. Manual page 16: + # income "equal to or less than the established income guidelines" is eligible. md_meap_eligible: [true, false] -- name: Categorical receipt waives only the income test + +- name: Case 2, categorical eligibility waives the income test but not the heating cost. period: 2026 input: state_code: [MD, MD] heating_type: [NATURAL_GAS, NATURAL_GAS] - has_heating_expense: [true, false] - md_meap_countable_income: [100000, 100000] + gas_expense: [1_150, 0] + md_meap_countable_income: [40_000, 40_000] md_meap_categorically_eligible: [true, true] output: + # Manual page 62: a categorically eligible household stays eligible above 200% + # FPL; page 63: other eligibility rules are unchanged, and page 16 requires, for + # MEAP, that responsibility for heating costs is established. The second + # household heats with gas but has no gas bill. md_meap_eligible: [true, false] -- name: Subsidized heat included in rent is ineligible even with a reported bill + +- name: Case 3, heat included in rent is denied only in subsidized housing. period: 2026 input: state_code: [MD, MD] heating_type: [NATURAL_GAS, NATURAL_GAS] + # Set directly to show that the subsidized heat-in-rent denial does not depend on + # a reported heating cost. has_heating_expense: [true, true] md_meap_countable_income: [0, 0] md_meap_categorically_eligible: [false, false] heat_expense_included_in_rent: [true, true] receives_housing_assistance: [false, true] output: + # Manual page 16: heating costs may be paid as an undesignated component of rent. + # COMAR 07.03.21.06D(3)(i) and manual page 39 deny an applicant in public or + # subsidized housing where heat is included in the rent. md_meap_eligible: [true, false] -- name: Qualified member is still required with categorical income eligibility + +- name: Case 4, a qualified member is required even with categorical eligibility. period: 2026 input: state_code: MD heating_type: NATURAL_GAS - has_heating_expense: true + gas_expense: 1_150 md_meap_countable_income: 0 md_meap_categorically_eligible: true immigration_status: UNDOCUMENTED output: + # COMAR 07.03.21.04C(1) and manual page 108: a member who is not a citizen or + # qualified alien is not counted, and the household must have at least one + # eligible member. md_meap_eligible: false -- name: Other states receive no Maryland benefit + +- name: Case 5, other states receive no Maryland benefit. period: 2026 input: state_code: VA heating_type: NATURAL_GAS - has_heating_expense: true + gas_expense: 1_150 md_meap_countable_income: 0 md_meap_categorically_eligible: false output: + # COMAR 07.03.21.03A(1): the household must reside in Maryland. md_meap_eligible: false + +- name: Case 6, public housing tenant who pays the heating bill is eligible, heat in rent is not. + period: 2026 + input: + state_code: [MD, MD] + heating_type: [NATURAL_GAS, NATURAL_GAS] + gas_expense: [950, 0] + heat_expense_included_in_rent: [false, true] + is_in_public_housing: [true, true] + md_meap_countable_income: [19_500, 19_500] + md_meap_categorically_eligible: [false, false] + output: + # State Plan page 9: applicants in subsidized housing must show that they pay + # their own heating costs. COMAR 07.03.21.06D(3)(i) and manual page 39 deny an + # applicant in public housing where heat is included in the rent. + md_meap_eligible: [true, false] + +- name: Case 7, eleven-member household at and one dollar above the 60 percent SMI limit. + period: 2026 + input: + state_code: [MD, MD] + heating_type: [NATURAL_GAS, NATURAL_GAS] + gas_expense: [2_100, 2_100] + md_meap_household_size: [11, 11] + md_meap_countable_income: [135_930, 135_931] + md_meap_categorically_eligible: [false, false] + output: + # Income Guidelines footnote: "Households of 11+ must be based on 60% of the State + # Median Income." No dollar row is printed; the limit is 60% of the FFY2026 + # Maryland four-person median income, floor(0.6 x $154,117) = $92,470, times the + # 45 CFR 96.85(b) size factor for eleven, 1.47 (52% + 5 x 16% + 5 x 3%): + # floor($135,930.90) = $135,930. $135,931 is over the limit although it is below + # 200% of the eleven-person guideline ($141,300). + md_meap_eligible: [true, false] diff --git a/policyengine_us/tests/policy/baseline/gov/states/md/dhs/meap/eligibility/md_meap_income_limit.yaml b/policyengine_us/tests/policy/baseline/gov/states/md/dhs/meap/eligibility/md_meap_income_limit.yaml index c3d19cf4a78..ff21ca1f412 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/md/dhs/meap/eligibility/md_meap_income_limit.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/md/dhs/meap/eligibility/md_meap_income_limit.yaml @@ -1,16 +1,34 @@ -- name: All published annual poverty limits and sizes nine and ten follow the stated rate +# Source for the expected values in this file: FY2026 OHEP Income Guidelines (one +# page), annual 200% FPG table and the 60% SMI footnote for households of 11 or more: +# https://dhs.maryland.gov/documents/OHEP/Advisory%20Board/Income-Guidelines-FY2026-Updated-7.9.2025.pdf + +- name: Case 1, published annual limits for sizes one to eight and the rate for nine and ten. period: 2026 input: - heating_type: [NATURAL_GAS, NATURAL_GAS, NATURAL_GAS, NATURAL_GAS, NATURAL_GAS, NATURAL_GAS, NATURAL_GAS, NATURAL_GAS, NATURAL_GAS, NATURAL_GAS] state_code: [MD, MD, MD, MD, MD, MD, MD, MD, MD, MD] + heating_type: [NATURAL_GAS, NATURAL_GAS, NATURAL_GAS, NATURAL_GAS, NATURAL_GAS, NATURAL_GAS, NATURAL_GAS, NATURAL_GAS, NATURAL_GAS, NATURAL_GAS] md_meap_household_size: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] output: - md_meap_income_limit: [31300, 42300, 53300, 64300, 75300, 86300, 97300, 108300, 119300, 130300] -- name: Sizes eleven and twelve use the stated sixty percent SMI rule + # Income Guidelines, annual table, sizes 1 to 8: $31,300, $42,300, $53,300, + # $64,300, $75,300, $86,300, $97,300, $108,300, each 200% of the 2025 HHS poverty + # guideline ($15,650 plus $5,500 per additional person). Sizes 9 and 10 are not + # printed as rows; 200% of the guideline gives $119,300 and $130,300. The page's + # "for each additional HH member add $11,004" line would instead give $119,304 + # and $130,308; the model follows the 200% rate (noted in the variable + # documentation). + md_meap_income_limit: [31_300, 42_300, 53_300, 64_300, 75_300, 86_300, 97_300, 108_300, 119_300, 130_300] + +- name: Case 2, sizes eleven and twelve use 60 percent of state median income. period: 2026 input: - heating_type: [NATURAL_GAS, NATURAL_GAS] state_code: [MD, MD] + heating_type: [NATURAL_GAS, NATURAL_GAS] md_meap_household_size: [11, 12] output: - md_meap_income_limit: [135930, 138705] + # Income Guidelines footnote: "Households of 11+ must be based on 60% of the State + # Median Income." No FY26 dollar rows are printed. 60% of the FFY2026 Maryland + # four-person median income, floor(0.6 x $154,117) = $92,470, times the + # 45 CFR 96.85(b) size factors (52% + 5 x 16% + 3% per member above six): + # size 11: 1.47 x $92,470 = $135,930.90, floored to $135,930; + # size 12: 1.50 x $92,470 = $138,705. + md_meap_income_limit: [135_930, 138_705] diff --git a/policyengine_us/tests/policy/baseline/gov/states/md/dhs/meap/income/md_meap_countable_earned_income.yaml b/policyengine_us/tests/policy/baseline/gov/states/md/dhs/meap/income/md_meap_countable_earned_income.yaml index cc50d6ee7d6..5768e0554f2 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/md/dhs/meap/income/md_meap_countable_earned_income.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/md/dhs/meap/income/md_meap_countable_earned_income.yaml @@ -1,13 +1,23 @@ -- name: Child and full-time student earnings are excluded without a new business expense deduction +# Source for the expected values in this file: COMAR 07.03.21.04D(1)-(2) (countable +# wages and net self-employment income) and E(3) (excluded earnings of a child +# under 18 or a full-time student): +# https://regs.maryland.gov/us/md/exec/comar/07.03.21.04 + +- name: Case 1, child and full-time student earnings are excluded and business losses floor at zero. period: 2026 input: - heating_type: [NATURAL_GAS, NATURAL_GAS, NATURAL_GAS, NATURAL_GAS] state_code: [MD, MD, MD, MD] + heating_type: [NATURAL_GAS, NATURAL_GAS, NATURAL_GAS, NATURAL_GAS] age: [17, 18, 40, 40] is_full_time_student: [false, true, false, false] - employment_income: [12000, 12000, 12000, 12000] - self_employment_income: [1000, 1000, 1000, -1000] + employment_income: [12_000, 12_000, 12_000, 12_000] + self_employment_income: [1_000, 1_000, 1_000, -1_000] sstb_self_employment_income: [600, 600, 600, 600] - farm_operations_income: [1200, 1200, 1200, 1200] + farm_operations_income: [1_200, 1_200, 1_200, 1_200] output: - md_meap_countable_earned_income: [0, 0, 14800, 13800] + # COMAR 07.03.21.04E(3): employment income of a child younger than 18 or of a + # full-time student is not countable, $0 and $0. Adult: $12,000 wages + $1,000 + + # $600 + $1,200 net self-employment (D(1), D(2)) = $14,800. Adult with a $1,000 + # business loss: the loss is floored at $0 (the sources do not address losses), + # $12,000 + $0 + $600 + $1,200 = $13,800. + md_meap_countable_earned_income: [0, 0, 14_800, 13_800] diff --git a/policyengine_us/tests/policy/baseline/gov/states/md/dhs/meap/income/md_meap_countable_income.yaml b/policyengine_us/tests/policy/baseline/gov/states/md/dhs/meap/income/md_meap_countable_income.yaml index db13efcc4fe..ee483bfcfea 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/md/dhs/meap/income/md_meap_countable_income.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/md/dhs/meap/income/md_meap_countable_income.yaml @@ -1,19 +1,21 @@ -# 10,000 earnings + 12,600 SSA/railroad + 9,240 other receipts - 2,400 support = 29,440. -- name: Count pensions and IRA withdrawals separately, deduct court-ordered support while Medicare deductions remain deferred +# Source for the expected values in this file: COMAR 07.03.21.04D (countable income +# sources), E(13) and F (court-ordered child support paid is deducted): +# https://regs.maryland.gov/us/md/exec/comar/07.03.21.04 + +- name: Case 1, every modeled unearned source counts and court-ordered support is deducted. period: 2026 input: - heating_type: NATURAL_GAS state_code: MD - md_meap_countable_earned_income: 10000 - social_security: 12000 + heating_type: NATURAL_GAS + md_meap_countable_earned_income: 10_000 + social_security: 12_000 railroad_benefits: 600 - medicare_part_b_premium: 1200 - pension_income: 1200 - retirement_distributions: 2400 + pension_income: 1_200 + retirement_distributions: 2_400 interest_income: 120 ordinary_dividend_income: 240 estate_income: 360 - rental_income: 1200 + rental_income: 1_200 child_support_received: 600 alimony_income: 480 workers_compensation: 720 @@ -22,29 +24,42 @@ strike_benefits: 120 ssi: 0 tanf: 0 - child_support_expense: 2400 + child_support_expense: 2_400 output: - md_meap_countable_income: 29440 -- name: Support deduction cannot make countable income negative + # COMAR 07.03.21.04D: $10,000 earnings + $12,000 Social Security (D(3), counted + # gross: the Medicare premium deduction is not wired in) + $600 railroad + # retirement (D(26)) + $1,200 pensions (D(12)) + $2,400 retirement account + # disbursements (D(13)) + $120 interest (D(6)) + $240 dividends (D(5)) + $360 + # estate income (D(8)) + $1,200 rental income (D(2)) + $600 child support (D(14)) + # + $480 alimony (D(15)) + $720 workers' compensation (D(16)) + $840 unemployment + # (D(17)) + $960 veterans benefits (D(18)) + $120 strike funds (D(24)) = $31,840; + # less $2,400 court-ordered child support paid (E(13), F) = $29,440. + md_meap_countable_income: 29_440 + +- name: Case 2, the support deduction cannot make countable income negative. period: 2026 input: - heating_type: NATURAL_GAS state_code: MD + heating_type: NATURAL_GAS md_meap_countable_earned_income: 0 ssi: 0 tanf: 0 - child_support_expense: 10000 + child_support_expense: 10_000 output: + # COMAR 07.03.21.04F deducts verified support from gross income; with no income + # the result is floored at $0. md_meap_countable_income: 0 -- name: State cash entitlement counts only when TANF is taken up +- name: Case 3, TCA counts only when it is taken up. period: 2026 input: state_code: [MD, MD] heating_type: [NATURAL_GAS, NATURAL_GAS] - age: [40, 40] - md_tca: [1200, 1200] + md_tca: [1_200, 1_200] takes_up_tanf_if_eligible: [false, true] ssi: [0, 0] output: - md_meap_countable_income: [0, 1200] + # COMAR 07.03.21.04D(10): temporary cash assistance is countable. The first + # household is entitled to $1,200 of TCA but does not receive it, $0; the second + # receives it, $1,200. + md_meap_countable_income: [0, 1_200] diff --git a/policyengine_us/tests/policy/baseline/gov/states/md/dhs/meap/income/md_meap_fpg.yaml b/policyengine_us/tests/policy/baseline/gov/states/md/dhs/meap/income/md_meap_fpg.yaml index 217131987a7..7d5a401b44e 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/md/dhs/meap/income/md_meap_fpg.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/md/dhs/meap/income/md_meap_fpg.yaml @@ -1,8 +1,28 @@ -- name: Prior-year guideline follows the requested year even when payment parameters backfill +# Source for the expected values in this file: FY2026 OHEP Income Guidelines (one +# page), whose annual 200% FPG limits are twice the prior calendar year's HHS poverty +# guideline: +# https://dhs.maryland.gov/documents/OHEP/Advisory%20Board/Income-Guidelines-FY2026-Updated-7.9.2025.pdf + +- name: Case 1, the prior-year guideline follows the requested year when parameters backfill. period: 2022 input: - heating_type: NATURAL_GAS state_code: MD + heating_type: NATURAL_GAS md_meap_household_size: 2 output: - md_meap_fpg: 17420 + # One-year lag from the FY2026 guidelines applied to 2022: the 2021 HHS poverty + # guideline for two people, $12,880 + $4,540 = $17,420. Years before FY2026 are + # unverified backfilled estimates. + md_meap_fpg: 17_420 + +- name: Case 2, the 2026 guideline is the 2025 HHS poverty guideline behind the FY2026 limits. + period: 2026 + input: + state_code: [MD, MD, MD] + heating_type: [NATURAL_GAS, NATURAL_GAS, NATURAL_GAS] + md_meap_household_size: [1, 4, 8] + output: + # Income Guidelines, annual 200% FPG table: size 1 $31,300, size 4 $64,300, + # size 8 $108,300; half of each is the 2025 HHS poverty guideline ($15,650 plus + # $5,500 per additional person): $15,650, $32,150, $54,150. + md_meap_fpg: [15_650, 32_150, 54_150] diff --git a/policyengine_us/tests/policy/baseline/gov/states/md/dhs/meap/integration.yaml b/policyengine_us/tests/policy/baseline/gov/states/md/dhs/meap/integration.yaml index a58c9212151..853c1827ca7 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/md/dhs/meap/integration.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/md/dhs/meap/integration.yaml @@ -1,72 +1,442 @@ -- name: Mixed status follows the regulation household count and counts excluded income in full +# Sources for the expected values in this file: +# - FY26 MEAP Benefit Matrix (one page), "the matrix" below: +# https://dhs.maryland.gov/documents/OHEP/Advisory%20Board/FY26-MEAP-Benefit-Matrix-2-1-1.pdf +# - FY2026 OHEP Income Guidelines (one page), "the Income Guidelines" below; the annual +# 200% FPG limits are twice the 2025 HHS poverty guideline ($15,650 plus $5,500 per +# additional person), which is the guideline the levels are measured against: +# https://dhs.maryland.gov/documents/OHEP/Advisory%20Board/Income-Guidelines-FY2026-Updated-7.9.2025.pdf +# - FY2026 LIHEAP State Plan, pages 5 (categorical eligibility), 9 (renters in +# subsidized housing) and 11 (item 2.5, Garrett County multiplier): +# https://liheapch.acf.gov/docs/2026/state-plans/MD_Plan_2026.pdf#page=11 +# - OHEP Operations Manual (May 2025), pages 16 (general eligibility), 39 (automatic +# denials), 62-63 (categorical eligibility and the nominal benefit) and 108 +# (ineligible members): +# https://dhs.maryland.gov/documents/OHEP/OHEP-Operations-Manual.pdf#page=62 +# - FY27 MEAP Benefit Matrices, page 1 (Garrett County table used to infer rounding): +# https://dhs.maryland.gov/documents/OHEP/FY27-MEAP-Benefit-Matrices.pdf#page=1 +# - COMAR 07.03.21.04 (household members and countable income) and .06D(3) (denials): +# https://regs.maryland.gov/us/md/exec/comar/07.03.21.04 +# Every case states that the household does not receive SNAP, TCA or SSI unless the +# case is about that receipt. + +- name: Case 1, mixed-status family follows the regulation household count and counts the excluded member's income. period: 2026 input: people: - parent: + person1: age: 40 - employment_income: 20000 + employment_income: 20_000 ssi: 0 - child: + person2: age: 5 immigration_status: UNDOCUMENTED - child_support_received: 1000 + child_support_received: 1_000 ssi: 0 spm_units: - unit: - members: - - parent - - child + spm_unit: + members: [person1, person2] heating_type: NATURAL_GAS - gas_expense: 1 + gas_expense: 1_150 snap: 0 tanf: 0 households: - home: - members: - - parent - - child + household: + members: [person1, person2] state_code: MD county: BALTIMORE_COUNTY_MD output: + # COMAR 07.03.21.04C(1): a member who is not a citizen or qualified alien is not + # counted, but the member's income is included. (Manual page 108 would count a + # child under 18 regardless of status; that conflict awaits the owner's call.) md_meap_household_size: 1 - md_meap_countable_income: 21000 - md_meap_income_limit: 31300 - md_meap_fpg: 15650 + # $20,000 wages (D(1)) + $1,000 child support received by the child (D(14), C(1)). + md_meap_countable_income: 21_000 + # Income Guidelines, annual table, size 1: $31,300. + md_meap_income_limit: 31_300 + # $31,300 / 2, the 2025 one-person guideline. + md_meap_fpg: 15_650 + # $21,000 / $15,650 = 134%: matrix Level 4 (101-150% FPL). md_meap_level: 4 + # $21,000 is under the $31,300 limit; the parent is a counted member; the gas + # bill establishes a heating cost (manual page 16). md_meap_eligible: true + # Matrix, Gas row, Level 4: $360. md_meap: 360 -- name: Categorically eligible high-income subsidized household receives the nominal payment + +- name: Case 2, SSI recipient living with a working adult above 200 percent in subsidized Garrett County housing gets the nominal grant. period: 2026 input: - state_code: MD - age: 40 - employment_income: 100000 - snap: 120 - tanf: 0 - ssi: 0 - receives_housing_assistance: true - heating_type: PROPANE - bottled_gas_expense: 1 - county: GARRETT_COUNTY_MD + people: + person1: + age: 45 + employment_income: 36_000 + ssi: 0 + person2: + age: 23 + # SSI at the 2026 federal rate of $994 a month. + ssi: 11_928 + spm_units: + spm_unit: + members: [person1, person2] + heating_type: PROPANE + bottled_gas_expense: 1_900 + receives_housing_assistance: true + snap: 0 + tanf: 0 + households: + household: + members: [person1, person2] + state_code: MD + county: GARRETT_COUNTY_MD output: + md_meap_household_size: 2 + # State Plan page 5 and manual page 62: a member receiving SSI makes the household + # categorically eligible. md_meap_categorically_eligible: true - md_meap_countable_income: 100000 + # $36,000 wages (COMAR 07.03.21.04D(1)) + $11,928 SSI (D(4)). + md_meap_countable_income: 47_928 + # Income Guidelines, annual table, size 2: $42,300; the guideline is half of it. + md_meap_income_limit: 42_300 + md_meap_fpg: 21_150 + # $47,928 / $21,150 = 227%: matrix Level 7 (Over 200% FPL). Manual page 63 pays + # categorically eligible households above 200% FPL the nominal benefit, which + # takes precedence over the Level 6 subsidized column. md_meap_level: 7 + # Manual page 62: the household stays eligible above 200% FPL; it pays its own + # propane bill, so the subsidized heat-in-rent denial does not apply. md_meap_eligible: true + # Matrix, Propane row, Level 7: $25. The Garrett County multiplier (State Plan + # item 2.5, page 11) leaves the nominal grant flat, as every Level 7 cell of the + # FY27 Garrett County table (page 1) is $25. md_meap: 25 -- name: Unsubsidized heat in rent with known fuel uses its normal fuel row + +- name: Case 3, unsubsidized heat in rent with a known fuel uses the fuel row at the income level. period: 2026 input: state_code: MD age: 40 - employment_income: 10000 + employment_income: 10_000 snap: 0 tanf: 0 ssi: 0 heat_expense_included_in_rent: true heating_type: FUEL_OIL output: - md_meap_countable_income: 10000 + # $10,000 wages (COMAR 07.03.21.04D(1)). + md_meap_countable_income: 10_000 + # $10,000 / $15,650 = 64%: matrix Level 3 (51-100% FPL). md_meap_level: 3 + # Manual page 16: heating costs paid as an undesignated component of rent qualify; + # the household is not in subsidized housing, so COMAR 07.03.21.06D(3)(i) does + # not apply. md_meap_eligible: true + # Matrix, Oil row, Level 3: $880. Paying a landlord-billed home from the ordinary + # income level and fuel row is an inference; no published source states the level + # for heat included in rent outside subsidized housing (unverified). md_meap: 880 + +- name: Case 4, couple with two children at Level 5. + period: 2026 + input: + people: + person1: + age: 38 + employment_income: 38_000 + ssi: 0 + person2: + age: 36 + employment_income: 14_000 + ssi: 0 + person3: + age: 9 + ssi: 0 + person4: + age: 5 + ssi: 0 + spm_units: + spm_unit: + members: [person1, person2, person3, person4] + heating_type: NATURAL_GAS + gas_expense: 1_300 + snap: 0 + tanf: 0 + households: + household: + members: [person1, person2, person3, person4] + state_code: MD + county: ANNE_ARUNDEL_COUNTY_MD + output: + # Four citizens (COMAR 07.03.21.04C). + md_meap_household_size: 4 + # $38,000 + $14,000 wages (D(1)). + md_meap_countable_income: 52_000 + # Income Guidelines, annual table, size 4: $64,300; the guideline is half of it. + md_meap_income_limit: 64_300 + md_meap_fpg: 32_150 + md_meap_categorically_eligible: false + # $52,000 / $32,150 = 162%: matrix Level 5 (151-200% FPL). + md_meap_level: 5 + # $52,000 is under the $64,300 limit and the gas bill establishes a heating cost. + md_meap_eligible: true + # Matrix, Gas row, Level 5: $300. + md_meap: 300 + +- name: Case 5, retired couple in Garrett County with propane heat receives the multiplied Level 4 grant. + period: 2026 + input: + people: + person1: + age: 68 + social_security_retirement: 16_800 + ssi: 0 + person2: + age: 66 + social_security_retirement: 9_600 + ssi: 0 + spm_units: + spm_unit: + members: [person1, person2] + heating_type: PROPANE + bottled_gas_expense: 2_300 + snap: 0 + tanf: 0 + households: + household: + members: [person1, person2] + state_code: MD + county: GARRETT_COUNTY_MD + output: + md_meap_household_size: 2 + # $16,800 + $9,600 Social Security (COMAR 07.03.21.04D(3)), counted gross: the + # Medicare premium deduction is not wired in. + md_meap_countable_income: 26_400 + # Income Guidelines, annual table, size 2: $42,300; the guideline is half of it. + md_meap_income_limit: 42_300 + md_meap_fpg: 21_150 + # $26,400 / $21,150 = 125%: matrix Level 4 (101-150% FPL). + md_meap_level: 4 + md_meap_eligible: true + # Matrix, Propane row, Level 4: $725, times the 1.1 Garrett County multiplier + # (State Plan item 2.5, page 11) = $797.50, rounded half up to $798 (rounding + # inferred from the FY27 Garrett County table, page 1). + md_meap: 798 + +- name: Case 6, family over the income limit without categorical eligibility receives nothing. + period: 2026 + input: + people: + person1: + age: 41 + employment_income: 46_000 + ssi: 0 + person2: + age: 39 + employment_income: 24_000 + ssi: 0 + person3: + age: 7 + ssi: 0 + spm_units: + spm_unit: + members: [person1, person2, person3] + heating_type: NATURAL_GAS + gas_expense: 1_250 + snap: 0 + tanf: 0 + households: + household: + members: [person1, person2, person3] + state_code: MD + county: FREDERICK_COUNTY_MD + output: + md_meap_household_size: 3 + # $46,000 + $24,000 wages (COMAR 07.03.21.04D(1)). + md_meap_countable_income: 70_000 + # Income Guidelines, annual table, size 3: $53,300. + md_meap_income_limit: 53_300 + md_meap_categorically_eligible: false + # $70,000 is over the $53,300 limit; manual page 39: over-income applications + # without categorical eligibility are denied. + md_meap_eligible: false + md_meap: 0 + +- name: Case 7, public housing tenant who pays the gas bill receives the Level 6 grant. + period: 2026 + input: + people: + person1: + age: 31 + employment_income: 19_500 + ssi: 0 + person2: + age: 6 + ssi: 0 + spm_units: + spm_unit: + members: [person1, person2] + heating_type: NATURAL_GAS + gas_expense: 950 + snap: 0 + tanf: 0 + households: + household: + members: [person1, person2] + state_code: MD + county: BALTIMORE_CITY_MD + is_in_public_housing: true + output: + md_meap_household_size: 2 + # $19,500 wages (COMAR 07.03.21.04D(1)). + md_meap_countable_income: 19_500 + # Income Guidelines, annual table, size 2: $42,300; the guideline is half of it. + md_meap_income_limit: 42_300 + md_meap_fpg: 21_150 + # $19,500 / $21,150 = 92% would be Level 3, but State Plan page 9 gives renters in + # subsidized housing a lower benefit level because a utility allowance is in + # their subsidy: matrix Level 6 (Subsidized/Sub metered). + md_meap_level: 6 + # State Plan page 9: the tenant pays the heating bill directly, so the public + # housing heat-in-rent denial (COMAR 07.03.21.06D(3)(i)) does not apply. + md_meap_eligible: true + # Matrix, Gas row, Level 6: $225. + md_meap: 225 + +- name: Case 8, eleven-member household above the 60 percent SMI limit but below 200 percent of the guideline is over income. + period: 2026 + input: + people: + person1: + age: 46 + employment_income: 58_000 + ssi: 0 + person2: + age: 44 + employment_income: 41_000 + ssi: 0 + person3: + age: 22 + employment_income: 27_300 + ssi: 0 + person4: + age: 71 + social_security_retirement: 11_700 + ssi: 0 + person5: {age: 17, ssi: 0} + person6: {age: 15, ssi: 0} + person7: {age: 13, ssi: 0} + person8: {age: 11, ssi: 0} + person9: {age: 9, ssi: 0} + person10: {age: 6, ssi: 0} + person11: {age: 3, ssi: 0} + spm_units: + spm_unit: + members: [person1, person2, person3, person4, person5, person6, person7, person8, person9, person10, person11] + heating_type: NATURAL_GAS + gas_expense: 2_100 + snap: 0 + tanf: 0 + households: + household: + members: [person1, person2, person3, person4, person5, person6, person7, person8, person9, person10, person11] + state_code: MD + county: PRINCE_GEORGE_S_COUNTY_MD + output: + md_meap_household_size: 11 + # $58,000 + $41,000 + $27,300 wages (COMAR 07.03.21.04D(1)) + $11,700 Social + # Security (D(3)) = $138,000. + md_meap_countable_income: 138_000 + # 2025 HHS poverty guideline for eleven: $15,650 + 10 x $5,500 = $70,650. + md_meap_fpg: 70_650 + # Income Guidelines footnote: "Households of 11+ must be based on 60% of the State + # Median Income": floor(0.6 x $154,117) = $92,470 (FFY2026 Maryland four-person + # median income), times the 45 CFR 96.85(b) size factor 1.47 = $135,930. + md_meap_income_limit: 135_930 + md_meap_categorically_eligible: false + # $138,000 is over the $135,930 limit although it is below 200% of the guideline + # ($141,300); manual page 39: over-income applications without categorical + # eligibility are denied. + md_meap_eligible: false + md_meap: 0 + +- name: Case 9, heat included in rent with an unknown fuel is eligible but unpaid. + period: 2026 + input: + state_code: MD + county: BALTIMORE_CITY_MD + age: 29 + employment_income: 24_000 + snap: 0 + tanf: 0 + ssi: 0 + heat_expense_included_in_rent: true + heating_type: UNSPECIFIED + output: + # $24,000 wages (COMAR 07.03.21.04D(1)). + md_meap_countable_income: 24_000 + # Income Guidelines, annual table, size 1: $31,300. + md_meap_income_limit: 31_300 + # Manual page 16: heating costs paid as an undesignated component of rent qualify; + # the household is not in subsidized housing. + md_meap_eligible: true + # Every matrix row is a fuel, so no amount can be read for an unknown fuel: + # heating_type is a required input and an unspecified fuel pays $0. + md_meap: 0 + +- name: Case 10, categorically eligible eleven-member household above the 60 percent SMI limit but below 200 percent is paid at Level 5. + period: 2026 + input: + people: + person1: + age: 46 + employment_income: 58_000 + ssi: 0 + person2: + age: 44 + employment_income: 41_000 + ssi: 0 + person3: + age: 22 + employment_income: 27_300 + ssi: 0 + person4: + age: 71 + # SSI at the 2026 federal rate of $994 a month. + ssi: 11_928 + person5: {age: 17, ssi: 0} + person6: {age: 15, ssi: 0} + person7: {age: 13, ssi: 0} + person8: {age: 11, ssi: 0} + person9: {age: 9, ssi: 0} + person10: {age: 6, ssi: 0} + person11: {age: 3, ssi: 0} + spm_units: + spm_unit: + members: [person1, person2, person3, person4, person5, person6, person7, person8, person9, person10, person11] + heating_type: NATURAL_GAS + gas_expense: 2_100 + snap: 0 + tanf: 0 + households: + household: + members: [person1, person2, person3, person4, person5, person6, person7, person8, person9, person10, person11] + state_code: MD + county: PRINCE_GEORGE_S_COUNTY_MD + output: + md_meap_household_size: 11 + # State Plan page 5 and manual page 62: the grandparent's SSI makes the household + # categorically eligible. + md_meap_categorically_eligible: true + # $58,000 + $41,000 + $27,300 wages (COMAR 07.03.21.04D(1)) + $11,928 SSI (D(4)) + # = $138,228. + md_meap_countable_income: 138_228 + # As in Case 8: $70,650 guideline and $135,930 limit. + md_meap_fpg: 70_650 + md_meap_income_limit: 135_930 + # $138,228 / $70,650 = 196%: matrix Level 5 (151-200% FPL). The nominal Level 7 + # applies only above 200% FPL (manual page 63), not above the SMI limit. + md_meap_level: 5 + # Manual page 62: the household remains eligible above the income limit when a + # member receives a qualifying benefit. + md_meap_eligible: true + # Matrix, Gas row, Level 5: $300. + md_meap: 300 diff --git a/policyengine_us/tests/policy/baseline/gov/states/md/dhs/meap/md_meap.yaml b/policyengine_us/tests/policy/baseline/gov/states/md/dhs/meap/md_meap.yaml index a71eab0eca1..11c605e742b 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/md/dhs/meap/md_meap.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/md/dhs/meap/md_meap.yaml @@ -1,4 +1,18 @@ -- name: Every published FY26 level for electricity +# Sources for the expected values in this file: +# - FY26 MEAP Benefit Matrix (one page), "the matrix" below: +# https://dhs.maryland.gov/documents/OHEP/Advisory%20Board/FY26-MEAP-Benefit-Matrix-2-1-1.pdf +# - FY2026 OHEP Income Guidelines (one page): +# https://dhs.maryland.gov/documents/OHEP/Advisory%20Board/Income-Guidelines-FY2026-Updated-7.9.2025.pdf +# - FY2026 LIHEAP State Plan, item 2.5 on page 11 (Garrett County multiplier): +# https://liheapch.acf.gov/docs/2026/state-plans/MD_Plan_2026.pdf#page=11 +# - FY27 MEAP Benefit Matrices, page 1 (Oil/Kerosene row label, Garrett County table): +# https://dhs.maryland.gov/documents/OHEP/FY27-MEAP-Benefit-Matrices.pdf#page=1 +# - OHEP Operations Manual (May 2025), page 63 (nominal benefit above 200% FPL): +# https://dhs.maryland.gov/documents/OHEP/OHEP-Operations-Manual.pdf#page=63 +# - COMAR 07.03.21.03 and 07.03.21.06: +# https://regs.maryland.gov/us/md/exec/comar/07.03.21.06 + +- name: Case 1, electric heat at each FY26 matrix level. period: 2026 input: state_code: [MD, MD, MD, MD, MD, MD, MD] @@ -7,8 +21,10 @@ md_meap_level: [1, 2, 3, 4, 5, 6, 7] county: [BALTIMORE_COUNTY_MD, BALTIMORE_COUNTY_MD, BALTIMORE_COUNTY_MD, BALTIMORE_COUNTY_MD, BALTIMORE_COUNTY_MD, BALTIMORE_COUNTY_MD, BALTIMORE_COUNTY_MD] output: + # Matrix, Electric row, Levels 1 to 7: $100, $100, $100, $100, $100, $100, $25. md_meap: [100, 100, 100, 100, 100, 100, 25] -- name: Every published FY26 level for natural_gas + +- name: Case 2, natural gas heat at each FY26 matrix level. period: 2026 input: state_code: [MD, MD, MD, MD, MD, MD, MD] @@ -17,8 +33,10 @@ md_meap_level: [1, 2, 3, 4, 5, 6, 7] county: [BALTIMORE_COUNTY_MD, BALTIMORE_COUNTY_MD, BALTIMORE_COUNTY_MD, BALTIMORE_COUNTY_MD, BALTIMORE_COUNTY_MD, BALTIMORE_COUNTY_MD, BALTIMORE_COUNTY_MD] output: + # Matrix, Gas row, Levels 1 to 7: $550, $475, $400, $360, $300, $225, $25. md_meap: [550, 475, 400, 360, 300, 225, 25] -- name: Every published FY26 level for fuel_oil + +- name: Case 3, fuel oil heat at each FY26 matrix level. period: 2026 input: state_code: [MD, MD, MD, MD, MD, MD, MD] @@ -27,8 +45,10 @@ md_meap_level: [1, 2, 3, 4, 5, 6, 7] county: [BALTIMORE_COUNTY_MD, BALTIMORE_COUNTY_MD, BALTIMORE_COUNTY_MD, BALTIMORE_COUNTY_MD, BALTIMORE_COUNTY_MD, BALTIMORE_COUNTY_MD, BALTIMORE_COUNTY_MD] output: - md_meap: [1100, 990, 880, 770, 650, 225, 25] -- name: Every published FY26 level for propane + # Matrix, Oil row, Levels 1 to 7: $1,100, $990, $880, $770, $650, $225, $25. + md_meap: [1_100, 990, 880, 770, 650, 225, 25] + +- name: Case 4, propane heat at each FY26 matrix level. period: 2026 input: state_code: [MD, MD, MD, MD, MD, MD, MD] @@ -37,8 +57,10 @@ md_meap_level: [1, 2, 3, 4, 5, 6, 7] county: [BALTIMORE_COUNTY_MD, BALTIMORE_COUNTY_MD, BALTIMORE_COUNTY_MD, BALTIMORE_COUNTY_MD, BALTIMORE_COUNTY_MD, BALTIMORE_COUNTY_MD, BALTIMORE_COUNTY_MD] output: - md_meap: [1000, 930, 830, 725, 600, 225, 25] -- name: Every published FY26 level for wood + # Matrix, Propane row, Levels 1 to 7: $1,000, $930, $830, $725, $600, $225, $25. + md_meap: [1_000, 930, 830, 725, 600, 225, 25] + +- name: Case 5, wood heat at each FY26 matrix level. period: 2026 input: state_code: [MD, MD, MD, MD, MD, MD, MD] @@ -47,8 +69,10 @@ md_meap_level: [1, 2, 3, 4, 5, 6, 7] county: [BALTIMORE_COUNTY_MD, BALTIMORE_COUNTY_MD, BALTIMORE_COUNTY_MD, BALTIMORE_COUNTY_MD, BALTIMORE_COUNTY_MD, BALTIMORE_COUNTY_MD, BALTIMORE_COUNTY_MD] output: + # Matrix, Wood/Coal row, Levels 1 to 7: $550, $475, $400, $360, $300, $225, $25. md_meap: [550, 475, 400, 360, 300, 225, 25] -- name: Garrett regional multiplier uses half-up dollars while the nominal grant stays flat + +- name: Case 6, Garrett County multiplier rounds half up and leaves the nominal grant flat. period: 2026 input: state_code: [MD, MD, MD, MD, MD] @@ -57,8 +81,19 @@ md_meap_eligible: [true, true, true, true, true] md_meap_level: [2, 4, 6, 1, 7] output: - md_meap: [523, 798, 248, 1210, 25] -- name: Kerosene and coal use the oil and wood-coal schedules + # State Plan item 2.5 (page 11) applies a 1.1 multiplier to MEAP benefits for + # Garrett County residents. Matrix cells times 1.1: + # Gas, Level 2: $475 x 1.1 = $522.50, rounded half up to $523. + # Propane, Level 4: $725 x 1.1 = $797.50, rounded half up to $798. + # Oil, Level 6: $225 x 1.1 = $247.50, rounded half up to $248. + # Oil, Level 1: $1,100 x 1.1 = $1,210. + # Oil, Level 7: the $25 nominal grant, no multiplier. + # No FY26 Garrett County dollar table is published. Half-up rounding and the flat + # Level 7 amount are inferred from the FY27 Garrett County table (FY27 matrices, + # page 1), where every Level 7 cell is $25. + md_meap: [523, 798, 248, 1_210, 25] + +- name: Case 7, kerosene and coal rows and fuels without a matrix row. period: 2026 input: state_code: [MD, MD, MD, MD, MD, MD] @@ -66,18 +101,53 @@ md_meap_eligible: [true, true, true, true, true, true] md_meap_level: [1, 1, 1, 1, 1, 1] output: - md_meap: [1100, 550, 0, 0, 0, 0] -- name: Submetered dwelling payment and assisted-living exclusion + # Matrix, Level 1 cells. Kerosene is paid from the Oil row, $1,100 (the FY26 matrix + # has no kerosene row; the FY27 matrix, page 1, labels the row Oil/Kerosene). + # Coal is paid from the Wood/Coal row, $550. Solar, other fuels and no heating + # have no matrix row, $0. An unspecified fuel pays $0 because heating_type is a + # required input and the amount depends on the fuel. + md_meap: [1_100, 550, 0, 0, 0, 0] + +- name: Case 8, submetered home at Level 6 and assisted living denial. period: 2026 input: state_code: [MD, MD, MD, MD] - county_str: [MONTGOMERY_COUNTY_MD, MONTGOMERY_COUNTY_MD, MONTGOMERY_COUNTY_MD, MONTGOMERY_COUNTY_MD] + county: [MONTGOMERY_COUNTY_MD, MONTGOMERY_COUNTY_MD, MONTGOMERY_COUNTY_MD, MONTGOMERY_COUNTY_MD] heating_type: [NATURAL_GAS, NATURAL_GAS, NATURAL_GAS, NATURAL_GAS] - has_heating_expense: [true, true, true, true] - md_meap_countable_income: [0, 0, 0, 40000] + gas_expense: [1_150, 1_150, 1_150, 1_150] + md_meap_countable_income: [0, 0, 0, 40_000] md_meap_categorically_eligible: [false, false, false, true] md_meap_dwelling_type: [STANDARD, SUB_METERED, ASSISTED_LIVING, SUB_METERED] output: + # COMAR 07.03.21.06D(3)(k) denies an applicant who resides in an assisted living + # facility. The fourth household is over the $31,300 one-person limit (Income + # Guidelines) but categorically eligible, so the income test is waived. md_meap_eligible: [true, true, false, true] + # Matrix columns: no income is Level 1 (0-25% FPL); a submetered home is Level 6 + # (Subsidized/Sub metered); $40,000 for one person is 256% of the $15,650 + # guideline, Level 7 (Over 200% FPL), which the manual (page 63) pays as the + # nominal benefit ahead of the submetered level. md_meap_level: [1, 6, 1, 7] + # Matrix, Gas row: Level 1 $550, Level 6 $225, Level 7 $25. Ineligible: $0. md_meap: [550, 225, 0, 25] + +- name: Case 9, Maryland household and Virginia household in the same simulation. + period: 2026 + input: + state_code: [MD, VA] + county: [BALTIMORE_COUNTY_MD, FAIRFAX_COUNTY_VA] + age: [34, 34] + employment_income: [20_000, 20_000] + heating_type: [NATURAL_GAS, NATURAL_GAS] + gas_expense: [1_150, 1_150] + # Neither household receives SNAP, TCA or SSI. + snap: [0, 0] + tanf: [0, 0] + ssi: [0, 0] + output: + # Maryland: one person with $20,000 of wages is under the $31,300 annual limit for + # size 1 (Income Guidelines) and at 128% of the $15,650 guideline ($31,300 / 2), + # the matrix's Level 4 column (101-150% FPL). Matrix, Gas row, Level 4: $360. + # Virginia: MEAP requires that the household resides in Maryland + # (COMAR 07.03.21.03A(1)), so the same household pays $0 there. + md_meap: [360, 0] diff --git a/policyengine_us/tests/policy/baseline/gov/states/md/dhs/meap/payment/md_meap_level.yaml b/policyengine_us/tests/policy/baseline/gov/states/md/dhs/meap/payment/md_meap_level.yaml index 2d565530bd9..5ea1b85fe30 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/md/dhs/meap/payment/md_meap_level.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/md/dhs/meap/payment/md_meap_level.yaml @@ -1,19 +1,54 @@ -- name: Inclusive poverty-band tops and immediately higher income +# Sources for the expected values in this file: +# - FY26 MEAP Benefit Matrix (one page), column headings: Level 1 0-25% FPL, Level 2 +# 26-50% FPL, Level 3 51-100% FPL, Level 4 101-150% FPL, Level 5 151-200% FPL, +# Level 6 Subsidized/Sub metered, Level 7 Over 200% FPL: +# https://dhs.maryland.gov/documents/OHEP/Advisory%20Board/FY26-MEAP-Benefit-Matrix-2-1-1.pdf +# - OHEP Operations Manual (May 2025), page 63 (nominal benefit for categorically +# eligible households above 200% FPL): +# https://dhs.maryland.gov/documents/OHEP/OHEP-Operations-Manual.pdf#page=63 +# - FY2026 LIHEAP State Plan, page 9 (lower benefit level in subsidized housing): +# https://liheapch.acf.gov/docs/2026/state-plans/MD_Plan_2026.pdf#page=9 +# The $10,000 guideline is a round figure so that the incomes are exact percentages. + +- name: Case 1, income at each poverty band top and one dollar above it. period: 2026 input: - heating_type: [NATURAL_GAS, NATURAL_GAS, NATURAL_GAS, NATURAL_GAS, NATURAL_GAS, NATURAL_GAS, NATURAL_GAS, NATURAL_GAS, NATURAL_GAS, NATURAL_GAS, NATURAL_GAS] state_code: [MD, MD, MD, MD, MD, MD, MD, MD, MD, MD, MD] - md_meap_fpg: [10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000] - md_meap_countable_income: [0, 2500, 2501, 5000, 5001, 10000, 10001, 15000, 15001, 20000, 20001] + heating_type: [NATURAL_GAS, NATURAL_GAS, NATURAL_GAS, NATURAL_GAS, NATURAL_GAS, NATURAL_GAS, NATURAL_GAS, NATURAL_GAS, NATURAL_GAS, NATURAL_GAS, NATURAL_GAS] + md_meap_fpg: [10_000, 10_000, 10_000, 10_000, 10_000, 10_000, 10_000, 10_000, 10_000, 10_000, 10_000] + md_meap_countable_income: [0, 2_500, 2_501, 5_000, 5_001, 10_000, 10_001, 15_000, 15_001, 20_000, 20_001] output: + # Matrix columns by income as a share of the guideline: 0% and 25% are Level 1 + # (0-25%); 25.01% and 50% are Level 2 (26-50%); 50.01% and 100% are Level 3 + # (51-100%); 100.01% and 150% are Level 4 (101-150%); 150.01% and 200% are + # Level 5 (151-200%); 200.01% is Level 7 (Over 200%). Band tops are read as + # inclusive, matching the at-or-below income test. md_meap_level: [1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 7] -- name: Subsidized homes use level six unless above the categorical nominal threshold + +- name: Case 2, housing assistance uses Level 6 unless income is above 200 percent. period: 2026 input: - heating_type: [NATURAL_GAS, NATURAL_GAS, NATURAL_GAS] state_code: [MD, MD, MD] - md_meap_fpg: [10000, 10000, 10000] - md_meap_countable_income: [0, 20000, 20001] + heating_type: [NATURAL_GAS, NATURAL_GAS, NATURAL_GAS] + md_meap_fpg: [10_000, 10_000, 10_000] + md_meap_countable_income: [0, 20_000, 20_001] receives_housing_assistance: [true, true, true] output: + # Matrix: subsidized households are Level 6 (Subsidized/Sub metered) at 0% and at + # 200% of the guideline. At 200.01%, Level 7 (Over 200% FPL): the manual (page + # 63) pays categorically eligible households above 200% FPL a nominal benefit. md_meap_level: [6, 6, 7] + +- name: Case 3, public housing uses Level 6 unless income is above 200 percent. + period: 2026 + input: + state_code: [MD, MD] + heating_type: [NATURAL_GAS, NATURAL_GAS] + md_meap_fpg: [10_000, 10_000] + md_meap_countable_income: [8_000, 20_001] + is_in_public_housing: [true, true] + output: + # State Plan page 9: renters in subsidized housing receive a lower benefit level + # because a utility allowance is in their subsidy; matrix Level 6 at 80% of the + # guideline. At 200.01%, Level 7 (Over 200% FPL) as in Case 2. + md_meap_level: [6, 7] diff --git a/policyengine_us/variables/gov/states/md/dhs/meap/income/md_meap_countable_income.py b/policyengine_us/variables/gov/states/md/dhs/meap/income/md_meap_countable_income.py index 93d76dae81c..703684b0b92 100644 --- a/policyengine_us/variables/gov/states/md/dhs/meap/income/md_meap_countable_income.py +++ b/policyengine_us/variables/gov/states/md/dhs/meap/income/md_meap_countable_income.py @@ -13,9 +13,10 @@ class md_meap_countable_income(Variable): def formula(spm_unit, period, parameters): p = parameters(period).gov.states.md.dhs.meap.income - # COMAR .04D(26) and E(20) allow Medicare deductions from the - # relevant benefits. Actual-premium handling remains deferred; neither - # modeled nor reported Medicare premiums are deducted here. + # COMAR 07.03.21.04D(3), D(26) and E(20) count Social Security and + # railroad retirement benefits less the Medicare payment deduction. + # medicare_part_b_premium and medicare_part_b_premiums_reported are not + # wired in yet, so both benefits count gross. # The TANF source applies take-up to Maryland TCA entitlement. income = add(spm_unit, period, ["md_meap_countable_earned_income"]) + add( spm_unit, period, p.sources.unearned diff --git a/policyengine_us/variables/gov/states/md/dhs/meap/md_meap.py b/policyengine_us/variables/gov/states/md/dhs/meap/md_meap.py index 16f5501cc72..535c5e90bbf 100644 --- a/policyengine_us/variables/gov/states/md/dhs/meap/md_meap.py +++ b/policyengine_us/variables/gov/states/md/dhs/meap/md_meap.py @@ -19,7 +19,8 @@ class md_meap(Variable): def formula(spm_unit, period, parameters): p = parameters(period).gov.states.md.dhs.meap.payment - level = spm_unit("md_meap_level", period) + # The floor keeps units outside Maryland (level 0) on a valid amount key. + level = max_(spm_unit("md_meap_level", period), 1) fuel = spm_unit("heating_type", period) types = fuel.possible_values amount = select( From a6de5f3191a4bad7b14563bc4448c6527d183d30 Mon Sep 17 00:00:00 2001 From: Ziming Date: Sat, 3 Oct 2026 17:28:43 -0400 Subject: [PATCH 5/8] Count farm rental income in Maryland MEAP --- .../md/dhs/meap/income/sources/unearned.yaml | 1 + .../gov/states/md/dhs/meap/md_meap.yaml | 26 +++++++++++++++++++ 2 files changed, 27 insertions(+) diff --git a/policyengine_us/parameters/gov/states/md/dhs/meap/income/sources/unearned.yaml b/policyengine_us/parameters/gov/states/md/dhs/meap/income/sources/unearned.yaml index 15470237144..8545b2fd667 100644 --- a/policyengine_us/parameters/gov/states/md/dhs/meap/income/sources/unearned.yaml +++ b/policyengine_us/parameters/gov/states/md/dhs/meap/income/sources/unearned.yaml @@ -21,6 +21,7 @@ values: - veterans_benefits - strike_benefits - rental_income + - farm_rent_income metadata: unit: list period: year diff --git a/policyengine_us/tests/policy/baseline/gov/states/md/dhs/meap/md_meap.yaml b/policyengine_us/tests/policy/baseline/gov/states/md/dhs/meap/md_meap.yaml index 11c605e742b..7237ce606a6 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/md/dhs/meap/md_meap.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/md/dhs/meap/md_meap.yaml @@ -151,3 +151,29 @@ # Virginia: MEAP requires that the household resides in Maryland # (COMAR 07.03.21.03A(1)), so the same household pays $0 there. md_meap: [360, 0] + +# COMAR 07.03.21.04D(2) counts rental-property income after operating expenses: +# https://regs.maryland.gov/us/md/exec/comar/07.03.21.04 +- name: Case 10, farm rental income puts a gas-heating household over the income limit. + period: 2026 + input: + state_code: MD + county: BALTIMORE_COUNTY_MD + age: 45 + immigration_status: CITIZEN + employment_income: 24_000 + farm_rent_income: 12_000 + rental_income: 0 + ssi: 0 + tanf: 0 + snap: 0 + heating_type: NATURAL_GAS + gas_expense: 1_200 + output: + # No rental expenses: $24,000 wages + $12,000 farm rents = $36,000. + md_meap_countable_income: 36_000 + # Income Guidelines: the one-person annual limit is $31,300. + md_meap_income_limit: 31_300 + md_meap_categorically_eligible: false + md_meap_eligible: false + md_meap: 0 From 1d83d76f54e24579c1098a5b5dde400e5e541c0d Mon Sep 17 00:00:00 2001 From: Ziming Date: Sat, 3 Oct 2026 22:15:29 -0400 Subject: [PATCH 6/8] Count survivor benefits in Maryland MEAP COMAR 07.03.21.04D(12) pensions, D(13) annuity and retirement disbursements and D(18) veteran's pension benefits cover every component of survivor_benefits, and .04E excludes none of them. Co-Authored-By: Claude Fable 5.1 --- .../gov/states/md/dhs/meap/income/sources/unearned.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/policyengine_us/parameters/gov/states/md/dhs/meap/income/sources/unearned.yaml b/policyengine_us/parameters/gov/states/md/dhs/meap/income/sources/unearned.yaml index 8545b2fd667..0cb4ada4bcf 100644 --- a/policyengine_us/parameters/gov/states/md/dhs/meap/income/sources/unearned.yaml +++ b/policyengine_us/parameters/gov/states/md/dhs/meap/income/sources/unearned.yaml @@ -13,6 +13,7 @@ values: - interest_income - estate_income - pension_income + - survivor_benefits - retirement_distributions - child_support_received - alimony_income From 7ede048e5ae95e32272430ead5146a12e0286b6d Mon Sep 17 00:00:00 2001 From: Ziming Date: Mon, 5 Oct 2026 22:59:37 -0400 Subject: [PATCH 7/8] Tidy Maryland MEAP parameters and tests Write parameter date keys unquoted, wrap each variable's documentation string across lines, and assert the published income limits at the odd household sizes only; the integration cases cover the even sizes. Co-Authored-By: Claude Fable 5.1 --- .../md/dhs/meap/eligibility/fpg_rate.yaml | 2 +- .../md/dhs/meap/eligibility/fpg_year_lag.yaml | 2 +- .../md/dhs/meap/eligibility/smi_min_size.yaml | 2 +- .../md/dhs/meap/eligibility/smi_rate.yaml | 2 +- .../meap/income/earned_income_min_age.yaml | 2 +- .../md/dhs/meap/income/sources/earned.yaml | 2 +- .../md/dhs/meap/income/sources/unearned.yaml | 2 +- .../dhs/meap/payment/amount/electricity.yaml | 14 +++++------ .../meap/payment/amount/gas_wood_coal.yaml | 14 +++++------ .../md/dhs/meap/payment/amount/oil.yaml | 14 +++++------ .../md/dhs/meap/payment/amount/propane.yaml | 14 +++++------ .../dhs/meap/payment/garrett_multiplier.yaml | 2 +- .../md/dhs/meap/payment/income_level.yaml | 24 +++++++++---------- .../md/dhs/meap/payment/nominal_level.yaml | 2 +- .../dhs/meap/payment/regional_counties.yaml | 2 +- .../md/dhs/meap/payment/subsidized_level.yaml | 2 +- .../eligibility/md_meap_income_limit.yaml | 24 +++++++++---------- .../md_meap_categorically_eligible.py | 7 +++++- .../dhs/meap/eligibility/md_meap_eligible.py | 7 +++++- .../eligibility/md_meap_household_size.py | 9 ++++++- .../meap/eligibility/md_meap_income_limit.py | 9 ++++++- .../income/md_meap_countable_earned_income.py | 6 ++++- .../meap/income/md_meap_countable_income.py | 11 ++++++++- .../gov/states/md/dhs/meap/md_meap.py | 11 ++++++++- .../md/dhs/meap/md_meap_dwelling_type.py | 6 ++++- .../md/dhs/meap/payment/md_meap_level.py | 7 +++++- 26 files changed, 127 insertions(+), 72 deletions(-) diff --git a/policyengine_us/parameters/gov/states/md/dhs/meap/eligibility/fpg_rate.yaml b/policyengine_us/parameters/gov/states/md/dhs/meap/eligibility/fpg_rate.yaml index 32046465ba9..57018ac8c2b 100644 --- a/policyengine_us/parameters/gov/states/md/dhs/meap/eligibility/fpg_rate.yaml +++ b/policyengine_us/parameters/gov/states/md/dhs/meap/eligibility/fpg_rate.yaml @@ -1,6 +1,6 @@ description: Maryland uses this poverty guideline share for income eligibility below the large-household cutoff under the Maryland Energy Assistance Program. values: - '2025-07-01': 2 + 2025-07-01: 2 metadata: unit: /1 period: year diff --git a/policyengine_us/parameters/gov/states/md/dhs/meap/eligibility/fpg_year_lag.yaml b/policyengine_us/parameters/gov/states/md/dhs/meap/eligibility/fpg_year_lag.yaml index 9ba1e7b2057..d7afb9e190d 100644 --- a/policyengine_us/parameters/gov/states/md/dhs/meap/eligibility/fpg_year_lag.yaml +++ b/policyengine_us/parameters/gov/states/md/dhs/meap/eligibility/fpg_year_lag.yaml @@ -1,6 +1,6 @@ description: Maryland uses the poverty guideline from this many years before the model year under the Maryland Energy Assistance Program. values: - '2025-07-01': 1 + 2025-07-01: 1 metadata: unit: year period: year diff --git a/policyengine_us/parameters/gov/states/md/dhs/meap/eligibility/smi_min_size.yaml b/policyengine_us/parameters/gov/states/md/dhs/meap/eligibility/smi_min_size.yaml index af6588734b7..7c616ca157a 100644 --- a/policyengine_us/parameters/gov/states/md/dhs/meap/eligibility/smi_min_size.yaml +++ b/policyengine_us/parameters/gov/states/md/dhs/meap/eligibility/smi_min_size.yaml @@ -1,6 +1,6 @@ description: Maryland uses state median income instead of poverty guidelines starting at this household size under the Maryland Energy Assistance Program. values: - '2025-07-01': 11 + 2025-07-01: 11 metadata: unit: person period: year diff --git a/policyengine_us/parameters/gov/states/md/dhs/meap/eligibility/smi_rate.yaml b/policyengine_us/parameters/gov/states/md/dhs/meap/eligibility/smi_rate.yaml index dd0d96a567a..47660952e0d 100644 --- a/policyengine_us/parameters/gov/states/md/dhs/meap/eligibility/smi_rate.yaml +++ b/policyengine_us/parameters/gov/states/md/dhs/meap/eligibility/smi_rate.yaml @@ -1,6 +1,6 @@ description: Maryland uses this state median income share for large households under the Maryland Energy Assistance Program. values: - '2025-07-01': 0.6 + 2025-07-01: 0.6 metadata: unit: /1 period: year diff --git a/policyengine_us/parameters/gov/states/md/dhs/meap/income/earned_income_min_age.yaml b/policyengine_us/parameters/gov/states/md/dhs/meap/income/earned_income_min_age.yaml index 32c271d57c3..a882b5d9a69 100644 --- a/policyengine_us/parameters/gov/states/md/dhs/meap/income/earned_income_min_age.yaml +++ b/policyengine_us/parameters/gov/states/md/dhs/meap/income/earned_income_min_age.yaml @@ -1,6 +1,6 @@ description: Maryland excludes earnings of children below this age under the Maryland Energy Assistance Program. values: - '2025-07-01': 18 + 2025-07-01: 18 metadata: unit: year period: year diff --git a/policyengine_us/parameters/gov/states/md/dhs/meap/income/sources/earned.yaml b/policyengine_us/parameters/gov/states/md/dhs/meap/income/sources/earned.yaml index b37fc57c43c..9bff14fc760 100644 --- a/policyengine_us/parameters/gov/states/md/dhs/meap/income/sources/earned.yaml +++ b/policyengine_us/parameters/gov/states/md/dhs/meap/income/sources/earned.yaml @@ -1,6 +1,6 @@ description: Maryland uses these existing earnings sources without an additional business expense deduction under the Maryland Energy Assistance Program. values: - '2025-07-01': + 2025-07-01: - employment_income - self_employment_income - sstb_self_employment_income diff --git a/policyengine_us/parameters/gov/states/md/dhs/meap/income/sources/unearned.yaml b/policyengine_us/parameters/gov/states/md/dhs/meap/income/sources/unearned.yaml index 0cb4ada4bcf..30838da3b0a 100644 --- a/policyengine_us/parameters/gov/states/md/dhs/meap/income/sources/unearned.yaml +++ b/policyengine_us/parameters/gov/states/md/dhs/meap/income/sources/unearned.yaml @@ -1,6 +1,6 @@ description: Maryland counts these available unearned income sources under the Maryland Energy Assistance Program. values: - '2025-07-01': + 2025-07-01: # COMAR 07.03.21.04D(3), D(26) and E(20) count Social Security and railroad # retirement benefits less the Medicare payment deduction. # medicare_part_b_premium and medicare_part_b_premiums_reported are not wired diff --git a/policyengine_us/parameters/gov/states/md/dhs/meap/payment/amount/electricity.yaml b/policyengine_us/parameters/gov/states/md/dhs/meap/payment/amount/electricity.yaml index e1b9e120ed2..18b3c5f30ac 100644 --- a/policyengine_us/parameters/gov/states/md/dhs/meap/payment/amount/electricity.yaml +++ b/policyengine_us/parameters/gov/states/md/dhs/meap/payment/amount/electricity.yaml @@ -1,18 +1,18 @@ description: Maryland provides this annual electricity heating payment by benefit level under the Maryland Energy Assistance Program. '1': - '2025-07-01': 100 + 2025-07-01: 100 '2': - '2025-07-01': 100 + 2025-07-01: 100 '3': - '2025-07-01': 100 + 2025-07-01: 100 '4': - '2025-07-01': 100 + 2025-07-01: 100 '5': - '2025-07-01': 100 + 2025-07-01: 100 '6': - '2025-07-01': 100 + 2025-07-01: 100 '7': - '2025-07-01': 25 + 2025-07-01: 25 metadata: unit: currency-USD period: year diff --git a/policyengine_us/parameters/gov/states/md/dhs/meap/payment/amount/gas_wood_coal.yaml b/policyengine_us/parameters/gov/states/md/dhs/meap/payment/amount/gas_wood_coal.yaml index 7bab79cb15d..deaa8e66a59 100644 --- a/policyengine_us/parameters/gov/states/md/dhs/meap/payment/amount/gas_wood_coal.yaml +++ b/policyengine_us/parameters/gov/states/md/dhs/meap/payment/amount/gas_wood_coal.yaml @@ -1,18 +1,18 @@ description: Maryland provides this annual gas wood coal heating payment by benefit level under the Maryland Energy Assistance Program. '1': - '2025-07-01': 550 + 2025-07-01: 550 '2': - '2025-07-01': 475 + 2025-07-01: 475 '3': - '2025-07-01': 400 + 2025-07-01: 400 '4': - '2025-07-01': 360 + 2025-07-01: 360 '5': - '2025-07-01': 300 + 2025-07-01: 300 '6': - '2025-07-01': 225 + 2025-07-01: 225 '7': - '2025-07-01': 25 + 2025-07-01: 25 metadata: unit: currency-USD period: year diff --git a/policyengine_us/parameters/gov/states/md/dhs/meap/payment/amount/oil.yaml b/policyengine_us/parameters/gov/states/md/dhs/meap/payment/amount/oil.yaml index c4ba7cd5087..df8721faeda 100644 --- a/policyengine_us/parameters/gov/states/md/dhs/meap/payment/amount/oil.yaml +++ b/policyengine_us/parameters/gov/states/md/dhs/meap/payment/amount/oil.yaml @@ -1,18 +1,18 @@ description: Maryland provides this annual oil heating payment by benefit level under the Maryland Energy Assistance Program. '1': - '2025-07-01': 1100 + 2025-07-01: 1100 '2': - '2025-07-01': 990 + 2025-07-01: 990 '3': - '2025-07-01': 880 + 2025-07-01: 880 '4': - '2025-07-01': 770 + 2025-07-01: 770 '5': - '2025-07-01': 650 + 2025-07-01: 650 '6': - '2025-07-01': 225 + 2025-07-01: 225 '7': - '2025-07-01': 25 + 2025-07-01: 25 metadata: unit: currency-USD period: year diff --git a/policyengine_us/parameters/gov/states/md/dhs/meap/payment/amount/propane.yaml b/policyengine_us/parameters/gov/states/md/dhs/meap/payment/amount/propane.yaml index aa3b123ac9c..d53c3f9af9d 100644 --- a/policyengine_us/parameters/gov/states/md/dhs/meap/payment/amount/propane.yaml +++ b/policyengine_us/parameters/gov/states/md/dhs/meap/payment/amount/propane.yaml @@ -1,18 +1,18 @@ description: Maryland provides this annual propane heating payment by benefit level under the Maryland Energy Assistance Program. '1': - '2025-07-01': 1000 + 2025-07-01: 1000 '2': - '2025-07-01': 930 + 2025-07-01: 930 '3': - '2025-07-01': 830 + 2025-07-01: 830 '4': - '2025-07-01': 725 + 2025-07-01: 725 '5': - '2025-07-01': 600 + 2025-07-01: 600 '6': - '2025-07-01': 225 + 2025-07-01: 225 '7': - '2025-07-01': 25 + 2025-07-01: 25 metadata: unit: currency-USD period: year diff --git a/policyengine_us/parameters/gov/states/md/dhs/meap/payment/garrett_multiplier.yaml b/policyengine_us/parameters/gov/states/md/dhs/meap/payment/garrett_multiplier.yaml index a03e50bfbf3..86788f98add 100644 --- a/policyengine_us/parameters/gov/states/md/dhs/meap/payment/garrett_multiplier.yaml +++ b/policyengine_us/parameters/gov/states/md/dhs/meap/payment/garrett_multiplier.yaml @@ -1,6 +1,6 @@ description: Maryland multiplies regular heating grants in Garrett County by this factor under the Maryland Energy Assistance Program. values: - '2025-07-01': 1.1 + 2025-07-01: 1.1 metadata: unit: /1 period: year diff --git a/policyengine_us/parameters/gov/states/md/dhs/meap/payment/income_level.yaml b/policyengine_us/parameters/gov/states/md/dhs/meap/payment/income_level.yaml index 0b99d444c96..3e06667f138 100644 --- a/policyengine_us/parameters/gov/states/md/dhs/meap/payment/income_level.yaml +++ b/policyengine_us/parameters/gov/states/md/dhs/meap/payment/income_level.yaml @@ -1,29 +1,29 @@ description: Maryland uses these inclusive poverty-ratio band tops to assign regular heating payment levels under the Maryland Energy Assistance Program. brackets: - threshold: - '2025-07-01': -.inf + 2025-07-01: -.inf amount: - '2025-07-01': 1 + 2025-07-01: 1 - threshold: - '2025-07-01': 0.25 + 2025-07-01: 0.25 amount: - '2025-07-01': 2 + 2025-07-01: 2 - threshold: - '2025-07-01': 0.5 + 2025-07-01: 0.5 amount: - '2025-07-01': 3 + 2025-07-01: 3 - threshold: - '2025-07-01': 1 + 2025-07-01: 1 amount: - '2025-07-01': 4 + 2025-07-01: 4 - threshold: - '2025-07-01': 1.5 + 2025-07-01: 1.5 amount: - '2025-07-01': 5 + 2025-07-01: 5 - threshold: - '2025-07-01': 2 + 2025-07-01: 2 amount: - '2025-07-01': 7 + 2025-07-01: 7 metadata: type: single_amount threshold_unit: /1 diff --git a/policyengine_us/parameters/gov/states/md/dhs/meap/payment/nominal_level.yaml b/policyengine_us/parameters/gov/states/md/dhs/meap/payment/nominal_level.yaml index c3fec01205c..0e12ab2d330 100644 --- a/policyengine_us/parameters/gov/states/md/dhs/meap/payment/nominal_level.yaml +++ b/policyengine_us/parameters/gov/states/md/dhs/meap/payment/nominal_level.yaml @@ -1,6 +1,6 @@ description: Maryland assigns categorically eligible households above the highest income band to this payment level under the Maryland Energy Assistance Program. values: - '2025-07-01': 7 + 2025-07-01: 7 metadata: unit: /1 period: year diff --git a/policyengine_us/parameters/gov/states/md/dhs/meap/payment/regional_counties.yaml b/policyengine_us/parameters/gov/states/md/dhs/meap/payment/regional_counties.yaml index 90103f1e98b..f879a5ea4ef 100644 --- a/policyengine_us/parameters/gov/states/md/dhs/meap/payment/regional_counties.yaml +++ b/policyengine_us/parameters/gov/states/md/dhs/meap/payment/regional_counties.yaml @@ -1,6 +1,6 @@ description: Maryland applies its regional heating multiplier in these counties under the Maryland Energy Assistance Program. values: - '2025-07-01': + 2025-07-01: - GARRETT_COUNTY_MD metadata: unit: list diff --git a/policyengine_us/parameters/gov/states/md/dhs/meap/payment/subsidized_level.yaml b/policyengine_us/parameters/gov/states/md/dhs/meap/payment/subsidized_level.yaml index 51e0f8578a7..d5bc81aef70 100644 --- a/policyengine_us/parameters/gov/states/md/dhs/meap/payment/subsidized_level.yaml +++ b/policyengine_us/parameters/gov/states/md/dhs/meap/payment/subsidized_level.yaml @@ -1,6 +1,6 @@ description: Maryland assigns subsidized or sub-metered households to this payment level under the Maryland Energy Assistance Program. values: - '2025-07-01': 6 + 2025-07-01: 6 metadata: unit: /1 period: year diff --git a/policyengine_us/tests/policy/baseline/gov/states/md/dhs/meap/eligibility/md_meap_income_limit.yaml b/policyengine_us/tests/policy/baseline/gov/states/md/dhs/meap/eligibility/md_meap_income_limit.yaml index ff21ca1f412..fb9eaf77fd7 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/md/dhs/meap/eligibility/md_meap_income_limit.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/md/dhs/meap/eligibility/md_meap_income_limit.yaml @@ -2,21 +2,21 @@ # page), annual 200% FPG table and the 60% SMI footnote for households of 11 or more: # https://dhs.maryland.gov/documents/OHEP/Advisory%20Board/Income-Guidelines-FY2026-Updated-7.9.2025.pdf -- name: Case 1, published annual limits for sizes one to eight and the rate for nine and ten. +- name: Case 1, published annual limits at the odd sizes one to nine. period: 2026 input: - state_code: [MD, MD, MD, MD, MD, MD, MD, MD, MD, MD] - heating_type: [NATURAL_GAS, NATURAL_GAS, NATURAL_GAS, NATURAL_GAS, NATURAL_GAS, NATURAL_GAS, NATURAL_GAS, NATURAL_GAS, NATURAL_GAS, NATURAL_GAS] - md_meap_household_size: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] + state_code: [MD, MD, MD, MD, MD] + heating_type: [NATURAL_GAS, NATURAL_GAS, NATURAL_GAS, NATURAL_GAS, NATURAL_GAS] + md_meap_household_size: [1, 3, 5, 7, 9] output: - # Income Guidelines, annual table, sizes 1 to 8: $31,300, $42,300, $53,300, - # $64,300, $75,300, $86,300, $97,300, $108,300, each 200% of the 2025 HHS poverty - # guideline ($15,650 plus $5,500 per additional person). Sizes 9 and 10 are not - # printed as rows; 200% of the guideline gives $119,300 and $130,300. The page's - # "for each additional HH member add $11,004" line would instead give $119,304 - # and $130,308; the model follows the 200% rate (noted in the variable - # documentation). - md_meap_income_limit: [31_300, 42_300, 53_300, 64_300, 75_300, 86_300, 97_300, 108_300, 119_300, 130_300] + # Income Guidelines FY2026, annual table: sizes 1, 3, 5 and 7 print $31,300, + # $53,300, $75,300 and $97,300, each 200% of the 2025 HHS poverty guideline + # ($15,650 plus $5,500 per additional person). Size 9 has no printed row; + # 200% of the guideline gives $119,300, where the page's "for each additional + # HH member add $11,004" line would give $119,304; the model follows the 200% + # rate (noted in the variable documentation). Even sizes are exercised by the + # integration cases; the 60% SMI rule from size 11 is Case 2. + md_meap_income_limit: [31_300, 53_300, 75_300, 97_300, 119_300] - name: Case 2, sizes eleven and twelve use 60 percent of state median income. period: 2026 diff --git a/policyengine_us/variables/gov/states/md/dhs/meap/eligibility/md_meap_categorically_eligible.py b/policyengine_us/variables/gov/states/md/dhs/meap/eligibility/md_meap_categorically_eligible.py index d94f4a93a14..2b750d71d04 100644 --- a/policyengine_us/variables/gov/states/md/dhs/meap/eligibility/md_meap_categorically_eligible.py +++ b/policyengine_us/variables/gov/states/md/dhs/meap/eligibility/md_meap_categorically_eligible.py @@ -12,7 +12,12 @@ class md_meap_categorically_eligible(Variable): "https://dhs.maryland.gov/documents/OHEP/OHEP-Operations-Manual.pdf#page=11", "https://liheapch.acf.gov/docs/2026/state-plans/MD_Plan_2026.pdf#page=5", ) - documentation = "One member's receipt of SNAP, TCA or SSI waives the income test. Means-tested veterans benefits are also legally qualifying, but generic veterans_benefits cannot identify that subset. Recurring receipt in the program year is approximated by annual benefit receipt." + documentation = ( + "One member's receipt of SNAP, TCA or SSI waives the income test. Means-tested " + "veterans benefits are also legally qualifying, but generic veterans_benefits " + "cannot identify that subset. Recurring receipt in the program year is " + "approximated by annual benefit receipt." + ) def formula(spm_unit, period, parameters): return add(spm_unit, period, ["snap", "tanf", "ssi"]) > 0 diff --git a/policyengine_us/variables/gov/states/md/dhs/meap/eligibility/md_meap_eligible.py b/policyengine_us/variables/gov/states/md/dhs/meap/eligibility/md_meap_eligible.py index b96ec0add51..f122fc264cb 100644 --- a/policyengine_us/variables/gov/states/md/dhs/meap/eligibility/md_meap_eligible.py +++ b/policyengine_us/variables/gov/states/md/dhs/meap/eligibility/md_meap_eligible.py @@ -13,7 +13,12 @@ class md_meap_eligible(Variable): # PDF pages 11, 12, 16, 39. "https://dhs.maryland.gov/documents/OHEP/OHEP-Operations-Manual.pdf#page=11", ) - documentation = "Regular heating has no resource test. Application documentation, emancipation and program-year duplicate awards are not modeled. The regulation permits an annually announced medical-expense waiver, but no such regular-heating waiver was found in the FY2026 plan. At least one qualified member is required." + documentation = ( + "Regular heating has no resource test. Application documentation, emancipation " + "and program-year duplicate awards are not modeled. The regulation permits an " + "annually announced medical-expense waiver, but no such regular-heating waiver " + "was found in the FY2026 plan. At least one qualified member is required." + ) def formula(spm_unit, period, parameters): heat_in_rent = spm_unit("heat_expense_included_in_rent", period) diff --git a/policyengine_us/variables/gov/states/md/dhs/meap/eligibility/md_meap_household_size.py b/policyengine_us/variables/gov/states/md/dhs/meap/eligibility/md_meap_household_size.py index 6fe46192ac3..55ddd699fe5 100644 --- a/policyengine_us/variables/gov/states/md/dhs/meap/eligibility/md_meap_household_size.py +++ b/policyengine_us/variables/gov/states/md/dhs/meap/eligibility/md_meap_household_size.py @@ -12,6 +12,13 @@ class md_meap_household_size(Variable): # PDF pages 107, 108. "https://dhs.maryland.gov/documents/OHEP/OHEP-Operations-Manual.pdf#page=107", ) - documentation = "Follows COMAR 07.03.21.04C: nonqualified members are excluded from size and their income counts in full. The May 2025 manual instead includes all children under 18 regardless of status; this unresolved conflict is recorded explicitly, and this draft follows the regulation. SPM membership approximates the energy-sharing household. No SNAP-specific immigration bars apply." + documentation = ( + "Follows COMAR 07.03.21.04C: nonqualified members are excluded from size and " + "their income counts in full. The May 2025 manual instead includes all " + "children under 18 regardless of status; this unresolved conflict is recorded " + "explicitly, and this draft follows the regulation. SPM membership " + "approximates the energy-sharing household. No SNAP-specific immigration bars " + "apply." + ) adds = ["is_citizen_or_legal_immigrant"] diff --git a/policyengine_us/variables/gov/states/md/dhs/meap/eligibility/md_meap_income_limit.py b/policyengine_us/variables/gov/states/md/dhs/meap/eligibility/md_meap_income_limit.py index 5cb5050fd28..35d122897da 100644 --- a/policyengine_us/variables/gov/states/md/dhs/meap/eligibility/md_meap_income_limit.py +++ b/policyengine_us/variables/gov/states/md/dhs/meap/eligibility/md_meap_income_limit.py @@ -13,7 +13,14 @@ class md_meap_income_limit(Variable): "https://dhs.maryland.gov/documents/OHEP/Advisory%20Board/Income-Guidelines-FY2026-Updated-7.9.2025.pdf", "https://liheapch.acf.gov/docs/2026/state-plans/MD_Plan_2026.pdf#page=9", ) - documentation = "The published annual 200% FPG amounts control sizes 1-10 rather than the plan's truncated equivalent SMI percentages or its inconsistent additional-member line. Annual inputs are compared with annual limits; the printed monthly amounts are rounded separately. Large-household dollar amounts are calculated from the stated SMI rule, not independently published FY26 dollar rows." + documentation = ( + "The published annual 200% FPG amounts control sizes 1-10 rather than the " + "plan's truncated equivalent SMI percentages or its inconsistent " + "additional-member line. Annual inputs are compared with annual limits; the " + "printed monthly amounts are rounded separately. Large-household dollar " + "amounts are calculated from the stated SMI rule, not independently published " + "FY26 dollar rows." + ) def formula(spm_unit, period, parameters): p = parameters(period).gov.states.md.dhs.meap.eligibility diff --git a/policyengine_us/variables/gov/states/md/dhs/meap/income/md_meap_countable_earned_income.py b/policyengine_us/variables/gov/states/md/dhs/meap/income/md_meap_countable_earned_income.py index b88baf6fc1d..ab5b4da8611 100644 --- a/policyengine_us/variables/gov/states/md/dhs/meap/income/md_meap_countable_earned_income.py +++ b/policyengine_us/variables/gov/states/md/dhs/meap/income/md_meap_countable_earned_income.py @@ -9,7 +9,11 @@ class md_meap_countable_earned_income(Variable): unit = USD defined_for = StateCode.MD reference = ("https://regs.maryland.gov/us/md/exec/comar/07.03.21.04",) - documentation = "Uses existing net business income without an additional expense deduction. MEAP depreciation add-backs are unsupported. Flooring losses is a modeling convention because the sources do not specify their treatment." + documentation = ( + "Uses existing net business income without an additional expense deduction. " + "MEAP depreciation add-backs are unsupported. Flooring losses is a modeling " + "convention because the sources do not specify their treatment." + ) def formula(person, period, parameters): p = parameters(period).gov.states.md.dhs.meap.income diff --git a/policyengine_us/variables/gov/states/md/dhs/meap/income/md_meap_countable_income.py b/policyengine_us/variables/gov/states/md/dhs/meap/income/md_meap_countable_income.py index 703684b0b92..2167b375c98 100644 --- a/policyengine_us/variables/gov/states/md/dhs/meap/income/md_meap_countable_income.py +++ b/policyengine_us/variables/gov/states/md/dhs/meap/income/md_meap_countable_income.py @@ -9,7 +9,16 @@ class md_meap_countable_income(Variable): unit = USD defined_for = StateCode.MD reference = ("https://regs.maryland.gov/us/md/exec/comar/07.03.21.04",) - documentation = "Annual income approximates the 30-day application period. All members' income counts in full. Follows COMAR exclusions for nonrecurring lump sums and AmeriCorps/VISTA despite conflicting manual/plan checklists. Reported child support paid is assumed court-ordered. Veterans income approximates pension benefits and strike income is assumed not employee-funded. TDAP, royalties, gifts, loans, special allowances and home-care receipts cannot be isolated reliably. Medicare-premium deductions remain deferred. Rental income is assumed nonnegative." + documentation = ( + "Annual income approximates the 30-day application period. All members' income " + "counts in full. Follows COMAR exclusions for nonrecurring lump sums and " + "AmeriCorps/VISTA despite conflicting manual/plan checklists. Reported child " + "support paid is assumed court-ordered. Veterans income approximates pension " + "benefits and strike income is assumed not employee-funded. TDAP, royalties, " + "gifts, loans, special allowances and home-care receipts cannot be isolated " + "reliably. Medicare-premium deductions remain deferred. Rental income is " + "assumed nonnegative." + ) def formula(spm_unit, period, parameters): p = parameters(period).gov.states.md.dhs.meap.income diff --git a/policyengine_us/variables/gov/states/md/dhs/meap/md_meap.py b/policyengine_us/variables/gov/states/md/dhs/meap/md_meap.py index 535c5e90bbf..9629c69f6e6 100644 --- a/policyengine_us/variables/gov/states/md/dhs/meap/md_meap.py +++ b/policyengine_us/variables/gov/states/md/dhs/meap/md_meap.py @@ -15,7 +15,16 @@ class md_meap(Variable): "https://regs.maryland.gov/us/md/exec/comar/07.03.21.07", "https://dhs.maryland.gov/documents/OHEP/FY27-MEAP-Benefit-Matrices.pdf#page=1", ) - documentation = "Annual regular heating assistance, verified for state FY26 (July 2025-June 2026), modeled in period 2026. Earlier results are unverified backfilled estimates. No actual-bill cap applies. Kerosene uses oil (explicitly labeled together in FY27); heat-in-rent uses the building's known fuel row, an inference because no separate renter table is published. Unknown fuels return zero; unknown county receives the statewide amount and cannot receive the Garrett supplement. Means-tested VA eligibility remains unsupported. Cooling, EUSP, arrearages, crisis and equipment benefits are excluded." + documentation = ( + "Annual regular heating assistance, verified for state FY26 (July 2025-June " + "2026), modeled in period 2026. Earlier results are unverified backfilled " + "estimates. No actual-bill cap applies. Kerosene uses oil (explicitly labeled " + "together in FY27); heat-in-rent uses the building's known fuel row, an " + "inference because no separate renter table is published. Unknown fuels return " + "zero; unknown county receives the statewide amount and cannot receive the " + "Garrett supplement. Means-tested VA eligibility remains unsupported. Cooling, " + "EUSP, arrearages, crisis and equipment benefits are excluded." + ) def formula(spm_unit, period, parameters): p = parameters(period).gov.states.md.dhs.meap.payment diff --git a/policyengine_us/variables/gov/states/md/dhs/meap/md_meap_dwelling_type.py b/policyengine_us/variables/gov/states/md/dhs/meap/md_meap_dwelling_type.py index 404c78c8e41..1f930a03716 100644 --- a/policyengine_us/variables/gov/states/md/dhs/meap/md_meap_dwelling_type.py +++ b/policyengine_us/variables/gov/states/md/dhs/meap/md_meap_dwelling_type.py @@ -18,4 +18,8 @@ class md_meap_dwelling_type(Variable): "https://dhs.maryland.gov/documents/OHEP/Advisory%20Board/FY26-MEAP-Benefit-Matrix-2-1-1.pdf", "https://regs.maryland.gov/us/md/exec/comar/07.03.21.06#D(3)(k)", ) - documentation = "Submetered homes use the published Level 6 payment; assisted-living applicants are ineligible under COMAR 07.03.21.06D(3)(k). Ordinary subsidized homes use the existing housing-assistance inputs." + documentation = ( + "Submetered homes use the published Level 6 payment; assisted-living " + "applicants are ineligible under COMAR 07.03.21.06D(3)(k). Ordinary subsidized " + "homes use the existing housing-assistance inputs." + ) diff --git a/policyengine_us/variables/gov/states/md/dhs/meap/payment/md_meap_level.py b/policyengine_us/variables/gov/states/md/dhs/meap/payment/md_meap_level.py index f02c7baa88c..2b23c75f276 100644 --- a/policyengine_us/variables/gov/states/md/dhs/meap/payment/md_meap_level.py +++ b/policyengine_us/variables/gov/states/md/dhs/meap/payment/md_meap_level.py @@ -12,7 +12,12 @@ class md_meap_level(Variable): # PDF pages 62, 63. "https://dhs.maryland.gov/documents/OHEP/OHEP-Operations-Manual.pdf#page=62", ) - documentation = "Submetered and subsidized homes use Level 6. The over-200% categorical nominal level takes precedence over these housing categories; this precedence follows the manual's nominal-payment instruction. Existing assistance inputs approximate the regulation's narrower subsidy definition." + documentation = ( + "Submetered and subsidized homes use Level 6. The over-200% categorical " + "nominal level takes precedence over these housing categories; this precedence " + "follows the manual's nominal-payment instruction. Existing assistance inputs " + "approximate the regulation's narrower subsidy definition." + ) def formula(spm_unit, period, parameters): p = parameters(period).gov.states.md.dhs.meap.payment From 9423ea04879c1b32218e77f4296fdcb73c3a28c3 Mon Sep 17 00:00:00 2001 From: Ziming Date: Tue, 6 Oct 2026 17:07:05 -0400 Subject: [PATCH 8/8] Address review round 1 on Maryland MEAP - Encode the FY25 and FY27 benefit matrices beside FY26, splitting the gas and wood/coal rows; verified_years "2025-2027"; document the FY27 Garrett oil Level 4 cell. - Count every child under 18 regardless of status and exclude only nonqualified adults, following the OHEP manual; require a qualified member; share adult_age with the earned-income rule. - Count Social Security and railroad retirement net of the Medicare Part B premium; add military retirement pay; floor every unearned source. - Read the electricity row for solar heat; guard the FPG ratio divisor. - Add cases for the FY25 and FY27 rows and Garrett amounts, mixed-status household size, the Medicare deduction, rental and estate losses, categorical eligibility with assisted living and heat in rent, and model-computed SNAP conferring categorical eligibility. - Re-template descriptions and add manual, plan, matrix and COMAR references. Co-Authored-By: Claude Fable 5.1 --- changelog.d/md-meap.added.md | 2 +- .../gov/states/md/dhs/meap/adult_age.yaml | 12 ++ .../md/dhs/meap/eligibility/fpg_rate.yaml | 4 +- .../md/dhs/meap/eligibility/fpg_year_lag.yaml | 6 +- .../md/dhs/meap/eligibility/smi_rate.yaml | 2 + .../meap/income/earned_income_min_age.yaml | 10 -- .../md/dhs/meap/income/sources/earned.yaml | 10 +- .../md/dhs/meap/income/sources/unearned.yaml | 22 +-- .../dhs/meap/payment/amount/electricity.yaml | 28 ++-- .../md/dhs/meap/payment/amount/gas.yaml | 40 ++++++ .../meap/payment/amount/gas_wood_coal.yaml | 24 ---- .../md/dhs/meap/payment/amount/oil.yaml | 36 +++-- .../md/dhs/meap/payment/amount/propane.yaml | 36 +++-- .../md/dhs/meap/payment/amount/wood_coal.yaml | 39 ++++++ .../dhs/meap/payment/garrett_multiplier.yaml | 8 +- .../md/dhs/meap/payment/income_level.yaml | 30 ++-- .../md/dhs/meap/payment/nominal_level.yaml | 11 +- .../dhs/meap/payment/regional_counties.yaml | 6 +- .../md/dhs/meap/payment/subsidized_level.yaml | 10 +- policyengine_us/programs.yaml | 4 +- .../meap/eligibility/md_meap_eligible.yaml | 49 ++++--- .../eligibility/md_meap_household_size.yaml | 93 +++++++++++++ .../eligibility/md_meap_income_limit.yaml | 57 +++++--- .../md_meap_countable_earned_income.yaml | 28 ++-- .../meap/income/md_meap_countable_income.yaml | 70 ++++++++-- .../md/dhs/meap/income/md_meap_fpg.yaml | 2 +- .../gov/states/md/dhs/meap/integration.yaml | 127 +++++++++++++---- .../gov/states/md/dhs/meap/md_meap.yaml | 129 ++++++++++++++---- .../dhs/meap/eligibility/md_meap_eligible.py | 12 +- .../eligibility/md_meap_household_size.py | 26 ++-- .../meap/eligibility/md_meap_income_limit.py | 12 +- .../income/md_meap_countable_earned_income.py | 14 +- .../meap/income/md_meap_countable_income.py | 57 ++++++-- .../states/md/dhs/meap/income/md_meap_fpg.py | 6 + .../gov/states/md/dhs/meap/md_meap.py | 43 +++--- .../md/dhs/meap/md_meap_dwelling_type.py | 3 +- .../md/dhs/meap/payment/md_meap_level.py | 10 +- 37 files changed, 797 insertions(+), 281 deletions(-) create mode 100644 policyengine_us/parameters/gov/states/md/dhs/meap/adult_age.yaml delete mode 100644 policyengine_us/parameters/gov/states/md/dhs/meap/income/earned_income_min_age.yaml create mode 100644 policyengine_us/parameters/gov/states/md/dhs/meap/payment/amount/gas.yaml delete mode 100644 policyengine_us/parameters/gov/states/md/dhs/meap/payment/amount/gas_wood_coal.yaml create mode 100644 policyengine_us/parameters/gov/states/md/dhs/meap/payment/amount/wood_coal.yaml create mode 100644 policyengine_us/tests/policy/baseline/gov/states/md/dhs/meap/eligibility/md_meap_household_size.yaml diff --git a/changelog.d/md-meap.added.md b/changelog.d/md-meap.added.md index a51b667ef7e..c655dad75ae 100644 --- a/changelog.d/md-meap.added.md +++ b/changelog.d/md-meap.added.md @@ -1 +1 @@ -Add Maryland MEAP regular heating assistance for state FY2026. +Add Maryland MEAP regular heating assistance for state FY25 to FY27. diff --git a/policyengine_us/parameters/gov/states/md/dhs/meap/adult_age.yaml b/policyengine_us/parameters/gov/states/md/dhs/meap/adult_age.yaml new file mode 100644 index 00000000000..be420e07cb1 --- /dev/null +++ b/policyengine_us/parameters/gov/states/md/dhs/meap/adult_age.yaml @@ -0,0 +1,12 @@ +description: Maryland treats members below this age as children, who count in the household regardless of immigration status and whose earnings are excluded, under the Maryland Energy Assistance Program. +values: + 2025-07-01: 18 +metadata: + unit: year + period: year + label: Maryland MEAP adult age + reference: + - title: OHEP Operations Manual (May 2025), ineligible household members + href: https://dhs.maryland.gov/documents/OHEP/OHEP-Operations-Manual.pdf#page=108 + - title: COMAR 07.03.21.04E(3), excluded earnings of a child or full-time student + href: https://regs.maryland.gov/us/md/exec/comar/07.03.21.04#E(3) diff --git a/policyengine_us/parameters/gov/states/md/dhs/meap/eligibility/fpg_rate.yaml b/policyengine_us/parameters/gov/states/md/dhs/meap/eligibility/fpg_rate.yaml index 57018ac8c2b..7bb9f139362 100644 --- a/policyengine_us/parameters/gov/states/md/dhs/meap/eligibility/fpg_rate.yaml +++ b/policyengine_us/parameters/gov/states/md/dhs/meap/eligibility/fpg_rate.yaml @@ -1,6 +1,6 @@ description: Maryland uses this poverty guideline share for income eligibility below the large-household cutoff under the Maryland Energy Assistance Program. values: - 2025-07-01: 2 + 2024-07-01: 2 metadata: unit: /1 period: year @@ -8,3 +8,5 @@ metadata: reference: - title: FY2026 OHEP Income Guidelines href: https://dhs.maryland.gov/documents/OHEP/Advisory%20Board/Income-Guidelines-FY2026-Updated-7.9.2025.pdf + - title: FY2025 MEAP Benefit Matrix, income band table + href: https://liheapch.acf.gov/docs/2025/benefits-matricies/MD_BenefitMatrix_2025.pdf diff --git a/policyengine_us/parameters/gov/states/md/dhs/meap/eligibility/fpg_year_lag.yaml b/policyengine_us/parameters/gov/states/md/dhs/meap/eligibility/fpg_year_lag.yaml index d7afb9e190d..83cbb74204a 100644 --- a/policyengine_us/parameters/gov/states/md/dhs/meap/eligibility/fpg_year_lag.yaml +++ b/policyengine_us/parameters/gov/states/md/dhs/meap/eligibility/fpg_year_lag.yaml @@ -1,6 +1,8 @@ description: Maryland uses the poverty guideline from this many years before the model year under the Maryland Energy Assistance Program. +# Inferred: no sheet states the lag. The FY2026 limits equal twice the 2025 +# poverty guideline and the FY25 band tops twice the 2024 guideline. values: - 2025-07-01: 1 + 2024-07-01: 1 metadata: unit: year period: year @@ -8,3 +10,5 @@ metadata: reference: - title: FY2026 OHEP Income Guidelines href: https://dhs.maryland.gov/documents/OHEP/Advisory%20Board/Income-Guidelines-FY2026-Updated-7.9.2025.pdf + - title: FY2025 MEAP Benefit Matrix, income band table + href: https://liheapch.acf.gov/docs/2025/benefits-matricies/MD_BenefitMatrix_2025.pdf diff --git a/policyengine_us/parameters/gov/states/md/dhs/meap/eligibility/smi_rate.yaml b/policyengine_us/parameters/gov/states/md/dhs/meap/eligibility/smi_rate.yaml index 47660952e0d..7dbb7ba329d 100644 --- a/policyengine_us/parameters/gov/states/md/dhs/meap/eligibility/smi_rate.yaml +++ b/policyengine_us/parameters/gov/states/md/dhs/meap/eligibility/smi_rate.yaml @@ -8,3 +8,5 @@ metadata: reference: - title: FY2026 OHEP Income Guidelines href: https://dhs.maryland.gov/documents/OHEP/Advisory%20Board/Income-Guidelines-FY2026-Updated-7.9.2025.pdf + - title: ACF LIHEAP IM 2025-02, Attachment 4, 60 percent SMI by household size, PDF pages 2, 5 + href: https://acf.gov/sites/default/files/documents/ocs/COMM_LIHEAP_IM2025-02_SMIStateTable_Att4.pdf#page=5 diff --git a/policyengine_us/parameters/gov/states/md/dhs/meap/income/earned_income_min_age.yaml b/policyengine_us/parameters/gov/states/md/dhs/meap/income/earned_income_min_age.yaml deleted file mode 100644 index a882b5d9a69..00000000000 --- a/policyengine_us/parameters/gov/states/md/dhs/meap/income/earned_income_min_age.yaml +++ /dev/null @@ -1,10 +0,0 @@ -description: Maryland excludes earnings of children below this age under the Maryland Energy Assistance Program. -values: - 2025-07-01: 18 -metadata: - unit: year - period: year - label: Maryland MEAP minimum age for counted earnings - reference: - - title: COMAR 07.03.21.04, countable income and exclusions - href: https://regs.maryland.gov/us/md/exec/comar/07.03.21.04 diff --git a/policyengine_us/parameters/gov/states/md/dhs/meap/income/sources/earned.yaml b/policyengine_us/parameters/gov/states/md/dhs/meap/income/sources/earned.yaml index 9bff14fc760..880ebc76a3f 100644 --- a/policyengine_us/parameters/gov/states/md/dhs/meap/income/sources/earned.yaml +++ b/policyengine_us/parameters/gov/states/md/dhs/meap/income/sources/earned.yaml @@ -1,4 +1,6 @@ -description: Maryland uses these existing earnings sources without an additional business expense deduction under the Maryland Energy Assistance Program. +description: Maryland uses these sources as earned income under the Maryland Energy Assistance Program. +# Self-employment and farm sources are the model's net inputs; no further +# business-expense deduction is applied. values: 2025-07-01: - employment_income @@ -10,5 +12,7 @@ metadata: period: year label: Maryland MEAP earned sources reference: - - title: COMAR 07.03.21.04, countable income and exclusions - href: https://regs.maryland.gov/us/md/exec/comar/07.03.21.04 + - title: COMAR 07.03.21.04D(1)-(2), countable earned income + href: https://regs.maryland.gov/us/md/exec/comar/07.03.21.04#D(1) + - title: OHEP Operations Manual (May 2025), Attachment A, countable income + href: https://dhs.maryland.gov/documents/OHEP/OHEP-Operations-Manual.pdf#page=111 diff --git a/policyengine_us/parameters/gov/states/md/dhs/meap/income/sources/unearned.yaml b/policyengine_us/parameters/gov/states/md/dhs/meap/income/sources/unearned.yaml index 30838da3b0a..7e6713978b9 100644 --- a/policyengine_us/parameters/gov/states/md/dhs/meap/income/sources/unearned.yaml +++ b/policyengine_us/parameters/gov/states/md/dhs/meap/income/sources/unearned.yaml @@ -1,18 +1,18 @@ -description: Maryland counts these available unearned income sources under the Maryland Energy Assistance Program. +description: Maryland uses these sources as unearned income under the Maryland Energy Assistance Program. +# Social Security and railroad retirement benefits are counted in the formula, +# net of the Medicare premium (COMAR 07.03.21.04D(3), D(26) and E(20)). values: 2025-07-01: - # COMAR 07.03.21.04D(3), D(26) and E(20) count Social Security and railroad - # retirement benefits less the Medicare payment deduction. - # medicare_part_b_premium and medicare_part_b_premiums_reported are not wired - # in yet, so both count gross. - - social_security - - railroad_benefits - ssi - tanf - ordinary_dividend_income - interest_income - estate_income - pension_income + # Military retirement pay is a separate input from the pension inputs that + # pension_income adds, so both are listed. Its survivor payments are not + # separable from survivor_benefits, so a survivor annuity could count twice. + - military_retirement_pay - survivor_benefits - retirement_distributions - child_support_received @@ -28,5 +28,9 @@ metadata: period: year label: Maryland MEAP unearned sources reference: - - title: COMAR 07.03.21.04, countable income and exclusions - href: https://regs.maryland.gov/us/md/exec/comar/07.03.21.04 + - title: COMAR 07.03.21.04D, countable income, and E, exclusions + href: https://regs.maryland.gov/us/md/exec/comar/07.03.21.04#D(1) + - title: OHEP Operations Manual (May 2025), Attachments A and B, PDF pages 111, 112 + href: https://dhs.maryland.gov/documents/OHEP/OHEP-Operations-Manual.pdf#page=111 + - title: FY2026 LIHEAP State Plan, section 1.9, PDF pages 6, 7 + href: https://liheapch.acf.gov/docs/2026/state-plans/MD_Plan_2026.pdf#page=6 diff --git a/policyengine_us/parameters/gov/states/md/dhs/meap/payment/amount/electricity.yaml b/policyengine_us/parameters/gov/states/md/dhs/meap/payment/amount/electricity.yaml index 18b3c5f30ac..f7887c95ae5 100644 --- a/policyengine_us/parameters/gov/states/md/dhs/meap/payment/amount/electricity.yaml +++ b/policyengine_us/parameters/gov/states/md/dhs/meap/payment/amount/electricity.yaml @@ -1,18 +1,24 @@ -description: Maryland provides this annual electricity heating payment by benefit level under the Maryland Energy Assistance Program. -'1': +description: Maryland provides this annual regular heating payment for electricity by benefit level under the Maryland Energy Assistance Program. +1: + 2024-07-01: 102 2025-07-01: 100 -'2': +2: + 2024-07-01: 102 2025-07-01: 100 -'3': +3: + 2024-07-01: 102 2025-07-01: 100 -'4': +4: + 2024-07-01: 102 2025-07-01: 100 -'5': +5: + 2024-07-01: 102 2025-07-01: 100 -'6': +6: + 2024-07-01: 102 2025-07-01: 100 -'7': - 2025-07-01: 25 +7: + 2024-07-01: 25 metadata: unit: currency-USD period: year @@ -20,5 +26,9 @@ metadata: breakdown: - range(1, 8) reference: + - title: FY2025 MEAP Benefit Matrix + href: https://liheapch.acf.gov/docs/2025/benefits-matricies/MD_BenefitMatrix_2025.pdf - title: FY26 MEAP Benefit Matrix href: https://dhs.maryland.gov/documents/OHEP/Advisory%20Board/FY26-MEAP-Benefit-Matrix-2-1-1.pdf + - title: FY27 MEAP Benefit Matrices, statewide table, PDF page 1 + href: https://dhs.maryland.gov/documents/OHEP/FY27-MEAP-Benefit-Matrices.pdf#page=1 diff --git a/policyengine_us/parameters/gov/states/md/dhs/meap/payment/amount/gas.yaml b/policyengine_us/parameters/gov/states/md/dhs/meap/payment/amount/gas.yaml new file mode 100644 index 00000000000..3865d5e623b --- /dev/null +++ b/policyengine_us/parameters/gov/states/md/dhs/meap/payment/amount/gas.yaml @@ -0,0 +1,40 @@ +description: Maryland provides this annual regular heating payment for natural gas by benefit level under the Maryland Energy Assistance Program. +1: + 2024-07-01: 301 + 2025-07-01: 550 + 2026-07-01: 700 +2: + 2024-07-01: 241 + 2025-07-01: 475 + 2026-07-01: 665 +3: + 2024-07-01: 210 + 2025-07-01: 400 + 2026-07-01: 630 +4: + 2024-07-01: 181 + 2025-07-01: 360 + 2026-07-01: 560 +5: + 2024-07-01: 150 + 2025-07-01: 300 + 2026-07-01: 455 +6: + 2024-07-01: 102 + 2025-07-01: 225 + 2026-07-01: 315 +7: + 2024-07-01: 25 +metadata: + unit: currency-USD + period: year + label: Maryland MEAP gas heating payments + breakdown: + - range(1, 8) + reference: + - title: FY2025 MEAP Benefit Matrix + href: https://liheapch.acf.gov/docs/2025/benefits-matricies/MD_BenefitMatrix_2025.pdf + - title: FY26 MEAP Benefit Matrix + href: https://dhs.maryland.gov/documents/OHEP/Advisory%20Board/FY26-MEAP-Benefit-Matrix-2-1-1.pdf + - title: FY27 MEAP Benefit Matrices, statewide table, PDF page 1 + href: https://dhs.maryland.gov/documents/OHEP/FY27-MEAP-Benefit-Matrices.pdf#page=1 diff --git a/policyengine_us/parameters/gov/states/md/dhs/meap/payment/amount/gas_wood_coal.yaml b/policyengine_us/parameters/gov/states/md/dhs/meap/payment/amount/gas_wood_coal.yaml deleted file mode 100644 index deaa8e66a59..00000000000 --- a/policyengine_us/parameters/gov/states/md/dhs/meap/payment/amount/gas_wood_coal.yaml +++ /dev/null @@ -1,24 +0,0 @@ -description: Maryland provides this annual gas wood coal heating payment by benefit level under the Maryland Energy Assistance Program. -'1': - 2025-07-01: 550 -'2': - 2025-07-01: 475 -'3': - 2025-07-01: 400 -'4': - 2025-07-01: 360 -'5': - 2025-07-01: 300 -'6': - 2025-07-01: 225 -'7': - 2025-07-01: 25 -metadata: - unit: currency-USD - period: year - label: Maryland MEAP gas wood coal heating payments - breakdown: - - range(1, 8) - reference: - - title: FY26 MEAP Benefit Matrix - href: https://dhs.maryland.gov/documents/OHEP/Advisory%20Board/FY26-MEAP-Benefit-Matrix-2-1-1.pdf diff --git a/policyengine_us/parameters/gov/states/md/dhs/meap/payment/amount/oil.yaml b/policyengine_us/parameters/gov/states/md/dhs/meap/payment/amount/oil.yaml index df8721faeda..902b25f751d 100644 --- a/policyengine_us/parameters/gov/states/md/dhs/meap/payment/amount/oil.yaml +++ b/policyengine_us/parameters/gov/states/md/dhs/meap/payment/amount/oil.yaml @@ -1,18 +1,30 @@ -description: Maryland provides this annual oil heating payment by benefit level under the Maryland Energy Assistance Program. -'1': - 2025-07-01: 1100 -'2': +description: Maryland provides this annual regular heating payment for oil or kerosene by benefit level under the Maryland Energy Assistance Program. +1: + 2024-07-01: 750 + 2025-07-01: 1_100 + 2026-07-01: 1_225 +2: + 2024-07-01: 600 2025-07-01: 990 -'3': + 2026-07-01: 1_160 +3: + 2024-07-01: 525 2025-07-01: 880 -'4': + 2026-07-01: 1_080 +4: + 2024-07-01: 450 2025-07-01: 770 -'5': + 2026-07-01: 980 +5: + 2024-07-01: 375 2025-07-01: 650 -'6': + 2026-07-01: 795 +6: + 2024-07-01: 102 2025-07-01: 225 -'7': - 2025-07-01: 25 + 2026-07-01: 550 +7: + 2024-07-01: 25 metadata: unit: currency-USD period: year @@ -20,5 +32,9 @@ metadata: breakdown: - range(1, 8) reference: + - title: FY2025 MEAP Benefit Matrix + href: https://liheapch.acf.gov/docs/2025/benefits-matricies/MD_BenefitMatrix_2025.pdf - title: FY26 MEAP Benefit Matrix href: https://dhs.maryland.gov/documents/OHEP/Advisory%20Board/FY26-MEAP-Benefit-Matrix-2-1-1.pdf + - title: FY27 MEAP Benefit Matrices, statewide table, PDF page 1 + href: https://dhs.maryland.gov/documents/OHEP/FY27-MEAP-Benefit-Matrices.pdf#page=1 diff --git a/policyengine_us/parameters/gov/states/md/dhs/meap/payment/amount/propane.yaml b/policyengine_us/parameters/gov/states/md/dhs/meap/payment/amount/propane.yaml index d53c3f9af9d..dc489b88ac4 100644 --- a/policyengine_us/parameters/gov/states/md/dhs/meap/payment/amount/propane.yaml +++ b/policyengine_us/parameters/gov/states/md/dhs/meap/payment/amount/propane.yaml @@ -1,18 +1,30 @@ -description: Maryland provides this annual propane heating payment by benefit level under the Maryland Energy Assistance Program. -'1': - 2025-07-01: 1000 -'2': +description: Maryland provides this annual regular heating payment for propane by benefit level under the Maryland Energy Assistance Program. +1: + 2024-07-01: 644 + 2025-07-01: 1_000 + 2026-07-01: 1_325 +2: + 2024-07-01: 515 2025-07-01: 930 -'3': + 2026-07-01: 1_255 +3: + 2024-07-01: 450 2025-07-01: 830 -'4': + 2026-07-01: 1_190 +4: + 2024-07-01: 386 2025-07-01: 725 -'5': + 2026-07-01: 1_060 +5: + 2024-07-01: 321 2025-07-01: 600 -'6': + 2026-07-01: 860 +6: + 2024-07-01: 102 2025-07-01: 225 -'7': - 2025-07-01: 25 + 2026-07-01: 595 +7: + 2024-07-01: 25 metadata: unit: currency-USD period: year @@ -20,5 +32,9 @@ metadata: breakdown: - range(1, 8) reference: + - title: FY2025 MEAP Benefit Matrix + href: https://liheapch.acf.gov/docs/2025/benefits-matricies/MD_BenefitMatrix_2025.pdf - title: FY26 MEAP Benefit Matrix href: https://dhs.maryland.gov/documents/OHEP/Advisory%20Board/FY26-MEAP-Benefit-Matrix-2-1-1.pdf + - title: FY27 MEAP Benefit Matrices, statewide table, PDF page 1 + href: https://dhs.maryland.gov/documents/OHEP/FY27-MEAP-Benefit-Matrices.pdf#page=1 diff --git a/policyengine_us/parameters/gov/states/md/dhs/meap/payment/amount/wood_coal.yaml b/policyengine_us/parameters/gov/states/md/dhs/meap/payment/amount/wood_coal.yaml new file mode 100644 index 00000000000..cc191cf38d5 --- /dev/null +++ b/policyengine_us/parameters/gov/states/md/dhs/meap/payment/amount/wood_coal.yaml @@ -0,0 +1,39 @@ +description: Maryland provides this annual regular heating payment for wood or coal by benefit level under the Maryland Energy Assistance Program. +1: + 2024-07-01: 418 + 2025-07-01: 550 +2: + 2024-07-01: 334 + 2025-07-01: 475 + 2026-07-01: 520 +3: + 2024-07-01: 292 + 2025-07-01: 400 + 2026-07-01: 495 +4: + 2024-07-01: 250 + 2025-07-01: 360 + 2026-07-01: 440 +5: + 2024-07-01: 209 + 2025-07-01: 300 + 2026-07-01: 355 +6: + 2024-07-01: 102 + 2025-07-01: 225 + 2026-07-01: 245 +7: + 2024-07-01: 25 +metadata: + unit: currency-USD + period: year + label: Maryland MEAP wood and coal heating payments + breakdown: + - range(1, 8) + reference: + - title: FY2025 MEAP Benefit Matrix + href: https://liheapch.acf.gov/docs/2025/benefits-matricies/MD_BenefitMatrix_2025.pdf + - title: FY26 MEAP Benefit Matrix + href: https://dhs.maryland.gov/documents/OHEP/Advisory%20Board/FY26-MEAP-Benefit-Matrix-2-1-1.pdf + - title: FY27 MEAP Benefit Matrices, statewide table, PDF page 1 + href: https://dhs.maryland.gov/documents/OHEP/FY27-MEAP-Benefit-Matrices.pdf#page=1 diff --git a/policyengine_us/parameters/gov/states/md/dhs/meap/payment/garrett_multiplier.yaml b/policyengine_us/parameters/gov/states/md/dhs/meap/payment/garrett_multiplier.yaml index 86788f98add..dd0b2004671 100644 --- a/policyengine_us/parameters/gov/states/md/dhs/meap/payment/garrett_multiplier.yaml +++ b/policyengine_us/parameters/gov/states/md/dhs/meap/payment/garrett_multiplier.yaml @@ -1,10 +1,14 @@ -description: Maryland multiplies regular heating grants in Garrett County by this factor under the Maryland Energy Assistance Program. +description: Maryland sets this multiplier for regular heating payments in Garrett County under the Maryland Energy Assistance Program. values: - 2025-07-01: 1.1 + 2024-07-01: 1.1 metadata: unit: /1 period: year label: Maryland MEAP Garrett multiplier reference: + - title: FY2025 LIHEAP State Plan, section 2.5 + href: https://liheapch.acf.gov/docs/2025/state-plans/MD_Plan_2025.pdf#page=11 - title: FY2026 LIHEAP State Plan, section 2.5 href: https://liheapch.acf.gov/docs/2026/state-plans/MD_Plan_2026.pdf#page=11 + - title: FY27 MEAP Benefit Matrices, Garrett County table, PDF page 1 + href: https://dhs.maryland.gov/documents/OHEP/FY27-MEAP-Benefit-Matrices.pdf#page=1 diff --git a/policyengine_us/parameters/gov/states/md/dhs/meap/payment/income_level.yaml b/policyengine_us/parameters/gov/states/md/dhs/meap/payment/income_level.yaml index 3e06667f138..f22565f336b 100644 --- a/policyengine_us/parameters/gov/states/md/dhs/meap/payment/income_level.yaml +++ b/policyengine_us/parameters/gov/states/md/dhs/meap/payment/income_level.yaml @@ -1,29 +1,31 @@ description: Maryland uses these inclusive poverty-ratio band tops to assign regular heating payment levels under the Maryland Energy Assistance Program. brackets: - threshold: - 2025-07-01: -.inf + 2024-07-01: -.inf amount: - 2025-07-01: 1 + 2024-07-01: 1 - threshold: - 2025-07-01: 0.25 + 2024-07-01: 0.25 amount: - 2025-07-01: 2 + 2024-07-01: 2 - threshold: - 2025-07-01: 0.5 + 2024-07-01: 0.5 amount: - 2025-07-01: 3 + 2024-07-01: 3 - threshold: - 2025-07-01: 1 + 2024-07-01: 1 amount: - 2025-07-01: 4 + 2024-07-01: 4 - threshold: - 2025-07-01: 1.5 + 2024-07-01: 1.5 amount: - 2025-07-01: 5 + 2024-07-01: 5 - threshold: - 2025-07-01: 2 + 2024-07-01: 2 + # Over 200 percent is Level 7, the nominal level in nominal_level.yaml; + # the two must agree. amount: - 2025-07-01: 7 + 2024-07-01: 7 metadata: type: single_amount threshold_unit: /1 @@ -31,5 +33,9 @@ metadata: period: year label: Maryland MEAP income payment levels reference: + - title: FY2025 MEAP Benefit Matrix + href: https://liheapch.acf.gov/docs/2025/benefits-matricies/MD_BenefitMatrix_2025.pdf - title: FY26 MEAP Benefit Matrix href: https://dhs.maryland.gov/documents/OHEP/Advisory%20Board/FY26-MEAP-Benefit-Matrix-2-1-1.pdf + - title: FY27 MEAP Benefit Matrices, statewide table, PDF page 1 + href: https://dhs.maryland.gov/documents/OHEP/FY27-MEAP-Benefit-Matrices.pdf#page=1 diff --git a/policyengine_us/parameters/gov/states/md/dhs/meap/payment/nominal_level.yaml b/policyengine_us/parameters/gov/states/md/dhs/meap/payment/nominal_level.yaml index 0e12ab2d330..aca28f78950 100644 --- a/policyengine_us/parameters/gov/states/md/dhs/meap/payment/nominal_level.yaml +++ b/policyengine_us/parameters/gov/states/md/dhs/meap/payment/nominal_level.yaml @@ -1,10 +1,17 @@ -description: Maryland assigns categorically eligible households above the highest income band to this payment level under the Maryland Energy Assistance Program. +description: Maryland sets this payment level for categorically eligible households above 200 percent of the poverty guideline under the Maryland Energy Assistance Program. +# Level 7 is also the top bracket of income_level.yaml; the two must agree. values: - 2025-07-01: 7 + 2024-07-01: 7 metadata: unit: /1 period: year label: Maryland MEAP nominal payment level reference: + - title: FY2025 MEAP Benefit Matrix + href: https://liheapch.acf.gov/docs/2025/benefits-matricies/MD_BenefitMatrix_2025.pdf - title: FY26 MEAP Benefit Matrix href: https://dhs.maryland.gov/documents/OHEP/Advisory%20Board/FY26-MEAP-Benefit-Matrix-2-1-1.pdf + - title: FY27 MEAP Benefit Matrices, statewide table, PDF page 1 + href: https://dhs.maryland.gov/documents/OHEP/FY27-MEAP-Benefit-Matrices.pdf#page=1 + - title: OHEP Operations Manual (May 2025), section 6.0, nominal benefit + href: https://dhs.maryland.gov/documents/OHEP/OHEP-Operations-Manual.pdf#page=63 diff --git a/policyengine_us/parameters/gov/states/md/dhs/meap/payment/regional_counties.yaml b/policyengine_us/parameters/gov/states/md/dhs/meap/payment/regional_counties.yaml index f879a5ea4ef..e6916cf8feb 100644 --- a/policyengine_us/parameters/gov/states/md/dhs/meap/payment/regional_counties.yaml +++ b/policyengine_us/parameters/gov/states/md/dhs/meap/payment/regional_counties.yaml @@ -1,11 +1,13 @@ -description: Maryland applies its regional heating multiplier in these counties under the Maryland Energy Assistance Program. +description: Maryland uses the regional heating multiplier in these counties under the Maryland Energy Assistance Program. values: - 2025-07-01: + 2024-07-01: - GARRETT_COUNTY_MD metadata: unit: list period: year label: Maryland MEAP regional multiplier counties reference: + - title: FY2025 LIHEAP State Plan, section 2.5 + href: https://liheapch.acf.gov/docs/2025/state-plans/MD_Plan_2025.pdf#page=11 - title: FY2026 LIHEAP State Plan, section 2.5 href: https://liheapch.acf.gov/docs/2026/state-plans/MD_Plan_2026.pdf#page=11 diff --git a/policyengine_us/parameters/gov/states/md/dhs/meap/payment/subsidized_level.yaml b/policyengine_us/parameters/gov/states/md/dhs/meap/payment/subsidized_level.yaml index d5bc81aef70..5dc4bd2644b 100644 --- a/policyengine_us/parameters/gov/states/md/dhs/meap/payment/subsidized_level.yaml +++ b/policyengine_us/parameters/gov/states/md/dhs/meap/payment/subsidized_level.yaml @@ -1,10 +1,16 @@ -description: Maryland assigns subsidized or sub-metered households to this payment level under the Maryland Energy Assistance Program. +description: Maryland sets this payment level for subsidized or submetered households under the Maryland Energy Assistance Program. values: - 2025-07-01: 6 + 2024-07-01: 6 metadata: unit: /1 period: year label: Maryland MEAP subsidized payment level reference: + - title: FY2025 MEAP Benefit Matrix + href: https://liheapch.acf.gov/docs/2025/benefits-matricies/MD_BenefitMatrix_2025.pdf - title: FY26 MEAP Benefit Matrix href: https://dhs.maryland.gov/documents/OHEP/Advisory%20Board/FY26-MEAP-Benefit-Matrix-2-1-1.pdf + - title: FY27 MEAP Benefit Matrices, statewide table, PDF page 1 + href: https://dhs.maryland.gov/documents/OHEP/FY27-MEAP-Benefit-Matrices.pdf#page=1 + - title: FY2026 LIHEAP State Plan, section 2.3, renters in subsidized housing + href: https://liheapch.acf.gov/docs/2026/state-plans/MD_Plan_2026.pdf#page=9 diff --git a/policyengine_us/programs.yaml b/policyengine_us/programs.yaml index 30602cfd0b6..ce9c46158b6 100644 --- a/policyengine_us/programs.yaml +++ b/policyengine_us/programs.yaml @@ -853,8 +853,8 @@ programs: full_name: Maryland Energy Assistance Program variable: md_meap parameter_prefix: gov.states.md.dhs.meap - verified_years: "2026" - notes: FY2026 regular heating only, with eligibility, countable income, the benefit matrix and the Garrett County supplement. EUSP, crisis, cooling and equipment assistance are out of scope. + verified_years: "2025-2027" + notes: FY25-FY27 regular heating only, with the fuel tables, the Garrett County multiplier and the subsidized and nominal levels. The SNAP nominal payment, cooling, EUSP, arrearage, crisis and equipment benefits are out of scope. - state: MS status: partial name: Mississippi LIHEAP diff --git a/policyengine_us/tests/policy/baseline/gov/states/md/dhs/meap/eligibility/md_meap_eligible.yaml b/policyengine_us/tests/policy/baseline/gov/states/md/dhs/meap/eligibility/md_meap_eligible.yaml index 8b075cfeeff..fce98b98b2d 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/md/dhs/meap/eligibility/md_meap_eligible.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/md/dhs/meap/eligibility/md_meap_eligible.yaml @@ -1,12 +1,17 @@ # Sources for the expected values in this file: # - FY2026 OHEP Income Guidelines (one page): # https://dhs.maryland.gov/documents/OHEP/Advisory%20Board/Income-Guidelines-FY2026-Updated-7.9.2025.pdf -# - OHEP Operations Manual (May 2025), pages 16 (general eligibility), 39 (automatic -# denials), 62-63 (categorical eligibility) and 108 (ineligible members): +# - OHEP Operations Manual (May 2025), pages 16 (general eligibility), 38 (categorical +# eligibility denials), 39 (automatic denials), 62-63 (categorical eligibility) and +# 108 (ineligible members): # https://dhs.maryland.gov/documents/OHEP/OHEP-Operations-Manual.pdf#page=16 # - FY2026 LIHEAP State Plan, page 9 (renters in subsidized housing): # https://liheapch.acf.gov/docs/2026/state-plans/MD_Plan_2026.pdf#page=9 -# - COMAR 07.03.21.03 (residence), .04C (nonqualified members) and .06D(3) (denials): +# - COMAR 07.03.21.03 (residence): +# https://regs.maryland.gov/us/md/exec/comar/07.03.21.03 +# - COMAR 07.03.21.04C (nonqualified members): +# https://regs.maryland.gov/us/md/exec/comar/07.03.21.04 +# - COMAR 07.03.21.06D(3) (denials): # https://regs.maryland.gov/us/md/exec/comar/07.03.21.06 - name: Case 1, one-person household at and one dollar above the annual income limit. @@ -37,23 +42,25 @@ # household heats with gas but has no gas bill. md_meap_eligible: [true, false] -- name: Case 3, heat included in rent is denied only in subsidized housing. +- name: Case 3, heat included in rent is denied in subsidized housing, with or without categorical eligibility. period: 2026 input: - state_code: [MD, MD] - heating_type: [NATURAL_GAS, NATURAL_GAS] + state_code: [MD, MD, MD] + heating_type: [NATURAL_GAS, NATURAL_GAS, NATURAL_GAS] # Set directly to show that the subsidized heat-in-rent denial does not depend on # a reported heating cost. - has_heating_expense: [true, true] - md_meap_countable_income: [0, 0] - md_meap_categorically_eligible: [false, false] - heat_expense_included_in_rent: [true, true] - receives_housing_assistance: [false, true] + has_heating_expense: [true, true, true] + md_meap_countable_income: [0, 0, 0] + md_meap_categorically_eligible: [false, false, true] + heat_expense_included_in_rent: [true, true, true] + receives_housing_assistance: [false, true, true] output: # Manual page 16: heating costs may be paid as an undesignated component of rent. # COMAR 07.03.21.06D(3)(i) and manual page 39 deny an applicant in public or - # subsidized housing where heat is included in the rent. - md_meap_eligible: [true, false] + # subsidized housing where heat is included in the rent. Manual page 38: + # categorical eligibility applications "may be denied for other eligibility + # issues", so the third household is denied too. + md_meap_eligible: [true, false, false] - name: Case 4, a qualified member is required even with categorical eligibility. period: 2026 @@ -65,9 +72,9 @@ md_meap_categorically_eligible: true immigration_status: UNDOCUMENTED output: - # COMAR 07.03.21.04C(1) and manual page 108: a member who is not a citizen or - # qualified alien is not counted, and the household must have at least one - # eligible member. + # Manual page 108: the household must have at least one eligible (citizen or + # qualified alien) member; COMAR 07.03.21.04C(1) likewise does not count the + # member. md_meap_eligible: false - name: Case 5, other states receive no Maryland benefit. @@ -109,9 +116,9 @@ md_meap_categorically_eligible: [false, false] output: # Income Guidelines footnote: "Households of 11+ must be based on 60% of the State - # Median Income." No dollar row is printed; the limit is 60% of the FFY2026 - # Maryland four-person median income, floor(0.6 x $154,117) = $92,470, times the - # 45 CFR 96.85(b) size factor for eleven, 1.47 (52% + 5 x 16% + 5 x 3%): - # floor($135,930.90) = $135,930. $135,931 is over the limit although it is below - # 200% of the eleven-person guideline ($141,300). + # Median Income." The limit is 60% of the FFY2026 Maryland four-person median + # income, floor(0.6 x $154,117) = $92,470, times the 45 CFR 96.85(b) size factor + # for eleven, 1.47 (52% + 5 x 16% + 5 x 3%): floor($135,930.90) = $135,930, the + # row ACF LIHEAP IM 2025-02 Attachment 4 (page 5) prints. $135,931 is over the + # limit although it is below 200% of the eleven-person guideline ($141,300). md_meap_eligible: [true, false] diff --git a/policyengine_us/tests/policy/baseline/gov/states/md/dhs/meap/eligibility/md_meap_household_size.yaml b/policyengine_us/tests/policy/baseline/gov/states/md/dhs/meap/eligibility/md_meap_household_size.yaml new file mode 100644 index 00000000000..cf06f1fce1d --- /dev/null +++ b/policyengine_us/tests/policy/baseline/gov/states/md/dhs/meap/eligibility/md_meap_household_size.yaml @@ -0,0 +1,93 @@ +# Sources for the expected values in this file: +# - OHEP Operations Manual (May 2025), page 108: "Children under 18 are always +# counted in the household, regardless of citizenship status"; ineligible adults +# "are not counted in the household number but their income is included"; and +# "the household must have at least one eligible member": +# https://dhs.maryland.gov/documents/OHEP/OHEP-Operations-Manual.pdf#page=108 +# - COMAR 07.03.21.04C(1), which would not count any nonqualified member (the model +# follows the manual, the operating rule): +# https://regs.maryland.gov/us/md/exec/comar/07.03.21.04 + +# Four two-person households, each with a gas bill, no income and no SNAP, TCA or +# SSI: +# - citizen parent and undocumented child: both count, size 2, eligible; +# - undocumented parent and citizen child: only the child counts, size 1, eligible +# because the child is a qualified member; +# - undocumented parent and undocumented child: the child counts, size 1, but no +# member is eligible, so the household is not; +# - two undocumented adults: neither counts, size 0, not eligible. +- name: Case 1, children under 18 count regardless of status and nonqualified adults do not. + period: 2026 + input: + people: + parent1: + age: 40 + ssi: 0 + child1: + age: 5 + immigration_status: UNDOCUMENTED + ssi: 0 + parent2: + age: 40 + immigration_status: UNDOCUMENTED + ssi: 0 + child2: + age: 5 + ssi: 0 + parent3: + age: 40 + immigration_status: UNDOCUMENTED + ssi: 0 + child3: + age: 5 + immigration_status: UNDOCUMENTED + ssi: 0 + adult4a: + age: 40 + immigration_status: UNDOCUMENTED + ssi: 0 + adult4b: + age: 38 + immigration_status: UNDOCUMENTED + ssi: 0 + spm_units: + spm_unit1: + members: [parent1, child1] + heating_type: NATURAL_GAS + gas_expense: 1_150 + snap: 0 + tanf: 0 + spm_unit2: + members: [parent2, child2] + heating_type: NATURAL_GAS + gas_expense: 1_150 + snap: 0 + tanf: 0 + spm_unit3: + members: [parent3, child3] + heating_type: NATURAL_GAS + gas_expense: 1_150 + snap: 0 + tanf: 0 + spm_unit4: + members: [adult4a, adult4b] + heating_type: NATURAL_GAS + gas_expense: 1_150 + snap: 0 + tanf: 0 + households: + household1: + members: [parent1, child1] + state_code: MD + household2: + members: [parent2, child2] + state_code: MD + household3: + members: [parent3, child3] + state_code: MD + household4: + members: [adult4a, adult4b] + state_code: MD + output: + md_meap_household_size: [2, 1, 1, 0] + md_meap_eligible: [true, true, false, false] diff --git a/policyengine_us/tests/policy/baseline/gov/states/md/dhs/meap/eligibility/md_meap_income_limit.yaml b/policyengine_us/tests/policy/baseline/gov/states/md/dhs/meap/eligibility/md_meap_income_limit.yaml index fb9eaf77fd7..343b3005147 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/md/dhs/meap/eligibility/md_meap_income_limit.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/md/dhs/meap/eligibility/md_meap_income_limit.yaml @@ -1,34 +1,53 @@ -# Source for the expected values in this file: FY2026 OHEP Income Guidelines (one -# page), annual 200% FPG table and the 60% SMI footnote for households of 11 or more: -# https://dhs.maryland.gov/documents/OHEP/Advisory%20Board/Income-Guidelines-FY2026-Updated-7.9.2025.pdf +# Sources for the expected values in this file: +# - FY2026 OHEP Income Guidelines (one page), annual 200% FPG table and the 60% SMI +# footnote for households of 11 or more: +# https://dhs.maryland.gov/documents/OHEP/Advisory%20Board/Income-Guidelines-FY2026-Updated-7.9.2025.pdf +# - FY2025 MEAP Benefit Matrix, income band table (monthly tops of the 151-200% FPL +# band, Case 3): +# https://liheapch.acf.gov/docs/2025/benefits-matricies/MD_BenefitMatrix_2025.pdf +# - ACF LIHEAP IM 2025-02, Attachment 4, page 5 (60% SMI rows for sizes 7 to 12): +# https://acf.gov/sites/default/files/documents/ocs/COMM_LIHEAP_IM2025-02_SMIStateTable_Att4.pdf#page=5 -- name: Case 1, published annual limits at the odd sizes one to nine. +- name: Case 1, published annual limits for sizes one to ten. period: 2026 input: - state_code: [MD, MD, MD, MD, MD] - heating_type: [NATURAL_GAS, NATURAL_GAS, NATURAL_GAS, NATURAL_GAS, NATURAL_GAS] - md_meap_household_size: [1, 3, 5, 7, 9] + state_code: [MD, MD, MD, MD, MD, MD, MD, MD, MD, MD] + md_meap_household_size: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] output: - # Income Guidelines FY2026, annual table: sizes 1, 3, 5 and 7 print $31,300, - # $53,300, $75,300 and $97,300, each 200% of the 2025 HHS poverty guideline - # ($15,650 plus $5,500 per additional person). Size 9 has no printed row; - # 200% of the guideline gives $119,300, where the page's "for each additional - # HH member add $11,004" line would give $119,304; the model follows the 200% - # rate (noted in the variable documentation). Even sizes are exercised by the - # integration cases; the 60% SMI rule from size 11 is Case 2. - md_meap_income_limit: [31_300, 53_300, 75_300, 97_300, 119_300] + # Income Guidelines FY2026, annual table: sizes 1 to 8 print $31,300, $42,300, + # $53,300, $64,300, $75,300, $86,300, $97,300 and $108,300, each 200% of the + # 2025 HHS poverty guideline ($15,650 plus $5,500 per additional person). Sizes + # 9 and 10 have no printed row; 200% of the guideline gives $119,300 and + # $130,300, where the page's "for each additional HH member add $11,004" line + # would give $4 and $8 more; the model follows the 200% rate (noted in the + # variable documentation). The 60% SMI rule from size 11 is Case 2. + md_meap_income_limit: [31_300, 42_300, 53_300, 64_300, 75_300, 86_300, 97_300, 108_300, 119_300, 130_300] - name: Case 2, sizes eleven and twelve use 60 percent of state median income. period: 2026 input: state_code: [MD, MD] - heating_type: [NATURAL_GAS, NATURAL_GAS] md_meap_household_size: [11, 12] output: # Income Guidelines footnote: "Households of 11+ must be based on 60% of the State - # Median Income." No FY26 dollar rows are printed. 60% of the FFY2026 Maryland - # four-person median income, floor(0.6 x $154,117) = $92,470, times the - # 45 CFR 96.85(b) size factors (52% + 5 x 16% + 3% per member above six): + # Median Income." 60% of the FFY2026 Maryland four-person median income, + # floor(0.6 x $154,117) = $92,470, times the 45 CFR 96.85(b) size factors (52% + + # 5 x 16% + 3% per member above six): # size 11: 1.47 x $92,470 = $135,930.90, floored to $135,930; # size 12: 1.50 x $92,470 = $138,705. + # Both rows are printed in ACF LIHEAP IM 2025-02 Attachment 4 (page 5). 200% of + # the ten-person guideline ($130,300) is below 60% SMI for ten ($133,156), so + # the switch at eleven is the federal ceiling at work. md_meap_income_limit: [135_930, 138_705] + +- name: Case 3, FY25 limits in period 2025 are twice the 2024 guideline. + period: 2025 + input: + state_code: [MD, MD, MD] + md_meap_household_size: [1, 4, 8] + output: + # FY2025 MEAP Benefit Matrix, income band table, monthly tops of the 151-200% FPL + # band: size 1 $2,510, size 4 $5,200, size 8 $8,787. Twice the 2024 HHS poverty + # guideline ($15,060 plus $5,380 per additional person): $30,120, $62,400 and + # $105,440, whose twelfths are $2,510, $5,200 and $8,786.67 (printed rounded). + md_meap_income_limit: [30_120, 62_400, 105_440] diff --git a/policyengine_us/tests/policy/baseline/gov/states/md/dhs/meap/income/md_meap_countable_earned_income.yaml b/policyengine_us/tests/policy/baseline/gov/states/md/dhs/meap/income/md_meap_countable_earned_income.yaml index 5768e0554f2..086b4570149 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/md/dhs/meap/income/md_meap_countable_earned_income.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/md/dhs/meap/income/md_meap_countable_earned_income.yaml @@ -3,21 +3,21 @@ # under 18 or a full-time student): # https://regs.maryland.gov/us/md/exec/comar/07.03.21.04 -- name: Case 1, child and full-time student earnings are excluded and business losses floor at zero. +- name: Case 1, child and full-time student earnings are excluded, adults' count from age 18, and business losses floor at zero. period: 2026 input: - state_code: [MD, MD, MD, MD] - heating_type: [NATURAL_GAS, NATURAL_GAS, NATURAL_GAS, NATURAL_GAS] - age: [17, 18, 40, 40] - is_full_time_student: [false, true, false, false] - employment_income: [12_000, 12_000, 12_000, 12_000] - self_employment_income: [1_000, 1_000, 1_000, -1_000] - sstb_self_employment_income: [600, 600, 600, 600] - farm_operations_income: [1_200, 1_200, 1_200, 1_200] + state_code: [MD, MD, MD, MD, MD, MD] + age: [17, 18, 18, 40, 40, 40] + is_full_time_student: [false, true, false, true, false, false] + employment_income: [12_000, 12_000, 12_000, 12_000, 12_000, 12_000] + self_employment_income: [1_000, 1_000, 1_000, 1_000, 1_000, -1_000] + sstb_self_employment_income: [600, 600, 600, 600, 600, 600] + farm_operations_income: [1_200, 1_200, 1_200, 1_200, 1_200, 1_200] output: # COMAR 07.03.21.04E(3): employment income of a child younger than 18 or of a - # full-time student is not countable, $0 and $0. Adult: $12,000 wages + $1,000 + - # $600 + $1,200 net self-employment (D(1), D(2)) = $14,800. Adult with a $1,000 - # business loss: the loss is floored at $0 (the sources do not address losses), - # $12,000 + $0 + $600 + $1,200 = $13,800. - md_meap_countable_earned_income: [0, 0, 14_800, 13_800] + # full-time student is not countable: the 17-year-old, the 18-year-old student + # and the 40-year-old student each count $0. An 18-year-old who is not a student + # and a 40-year-old count $12,000 wages + $1,000 + $600 + $1,200 net + # self-employment (D(1), D(2)) = $14,800. With a $1,000 business loss the loss + # is floored at $0 for that source: $12,000 + $0 + $600 + $1,200 = $13,800. + md_meap_countable_earned_income: [0, 0, 14_800, 0, 14_800, 13_800] diff --git a/policyengine_us/tests/policy/baseline/gov/states/md/dhs/meap/income/md_meap_countable_income.yaml b/policyengine_us/tests/policy/baseline/gov/states/md/dhs/meap/income/md_meap_countable_income.yaml index ee483bfcfea..a66b8799fa3 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/md/dhs/meap/income/md_meap_countable_income.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/md/dhs/meap/income/md_meap_countable_income.yaml @@ -1,21 +1,29 @@ -# Source for the expected values in this file: COMAR 07.03.21.04D (countable income -# sources), E(13) and F (court-ordered child support paid is deducted): -# https://regs.maryland.gov/us/md/exec/comar/07.03.21.04 +# Sources for the expected values in this file: +# - COMAR 07.03.21.04D (countable income sources), E(13) and F (court-ordered child +# support paid is deducted), D(3), D(26) and E(20) (Social Security and railroad +# retirement less the Medicare deduction): +# https://regs.maryland.gov/us/md/exec/comar/07.03.21.04 +# - FY2026 LIHEAP State Plan, section 1.9 (page 6): SSA benefits "Excluding MediCare +# deduction": +# https://liheapch.acf.gov/docs/2026/state-plans/MD_Plan_2026.pdf#page=6 - name: Case 1, every modeled unearned source counts and court-ordered support is deducted. period: 2026 input: state_code: MD - heating_type: NATURAL_GAS md_meap_countable_earned_income: 10_000 social_security: 12_000 + medicare_part_b_premium: 0 railroad_benefits: 600 pension_income: 1_200 + military_retirement_pay: 1_800 + survivor_benefits: 300 retirement_distributions: 2_400 interest_income: 120 ordinary_dividend_income: 240 estate_income: 360 rental_income: 1_200 + farm_rent_income: 240 child_support_received: 600 alimony_income: 480 workers_compensation: 720 @@ -26,21 +34,21 @@ tanf: 0 child_support_expense: 2_400 output: - # COMAR 07.03.21.04D: $10,000 earnings + $12,000 Social Security (D(3), counted - # gross: the Medicare premium deduction is not wired in) + $600 railroad - # retirement (D(26)) + $1,200 pensions (D(12)) + $2,400 retirement account - # disbursements (D(13)) + $120 interest (D(6)) + $240 dividends (D(5)) + $360 - # estate income (D(8)) + $1,200 rental income (D(2)) + $600 child support (D(14)) - # + $480 alimony (D(15)) + $720 workers' compensation (D(16)) + $840 unemployment - # (D(17)) + $960 veterans benefits (D(18)) + $120 strike funds (D(24)) = $31,840; - # less $2,400 court-ordered child support paid (E(13), F) = $29,440. - md_meap_countable_income: 29_440 + # COMAR 07.03.21.04D: $10,000 earnings + $12,000 Social Security (D(3), with no + # Medicare premium to deduct) + $600 railroad retirement (D(26)) + $1,200 pensions + # + $1,800 military retirement pay (D(12)) + $300 survivor benefits (D(12), + # D(13), D(18)) + $2,400 retirement account disbursements (D(13)) + $120 interest + # (D(6)) + $240 dividends (D(5)) + $360 estate income (D(8)) + $1,200 rental + + # $240 farm rental income (D(2)) + $600 child support (D(14)) + $480 alimony + # (D(15)) + $720 workers' compensation (D(16)) + $840 unemployment (D(17)) + + # $960 veterans benefits (D(18)) + $120 strike funds (D(24)) = $34,180; less + # $2,400 court-ordered child support paid (E(13), F) = $31,780. + md_meap_countable_income: 31_780 - name: Case 2, the support deduction cannot make countable income negative. period: 2026 input: state_code: MD - heating_type: NATURAL_GAS md_meap_countable_earned_income: 0 ssi: 0 tanf: 0 @@ -54,7 +62,6 @@ period: 2026 input: state_code: [MD, MD] - heating_type: [NATURAL_GAS, NATURAL_GAS] md_tca: [1_200, 1_200] takes_up_tanf_if_eligible: [false, true] ssi: [0, 0] @@ -63,3 +70,36 @@ # household is entitled to $1,200 of TCA but does not receive it, $0; the second # receives it, $1,200. md_meap_countable_income: [0, 1_200] + +- name: Case 4, Social Security counts net of the Medicare Part B premium the person pays. + period: 2026 + input: + state_code: [MD, MD] + age: [70, 70] + social_security: [32_000, 32_000] + medicare_part_b_premium: [2_435, 0] + ssi: [0, 0] + tanf: [0, 0] + output: + # COMAR 07.03.21.04D(3) counts "Social Security income less Medicare payment + # deduction" and E(20) excludes the Medicare payments deducted from the check; + # plan 1.9 checks "Excluding MediCare deduction". $32,000 - $2,435 = $29,565 for + # the retiree who pays the premium; $32,000 when a Medicare Savings Program + # pays it. + md_meap_countable_income: [29_565, 32_000] + +- name: Case 5, a rental or estate loss is floored at zero and does not offset earnings. + period: 2026 + input: + state_code: [MD, MD] + md_meap_countable_earned_income: [33_000, 33_000] + rental_income: [-3_000, 3_000] + estate_income: [-1_000, 0] + ssi: [0, 0] + tanf: [0, 0] + output: + # COMAR 07.03.21.04D(2) treats self-employment and rental income as one item, + # and plan 1.8 (page 6) counts gross income, so each source is floored at zero + # like the earned sources: $33,000 + $0 + $0 = $33,000, and $33,000 + $3,000 = + # $36,000. + md_meap_countable_income: [33_000, 36_000] diff --git a/policyengine_us/tests/policy/baseline/gov/states/md/dhs/meap/income/md_meap_fpg.yaml b/policyengine_us/tests/policy/baseline/gov/states/md/dhs/meap/income/md_meap_fpg.yaml index 7d5a401b44e..12a9ea929f9 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/md/dhs/meap/income/md_meap_fpg.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/md/dhs/meap/income/md_meap_fpg.yaml @@ -11,7 +11,7 @@ md_meap_household_size: 2 output: # One-year lag from the FY2026 guidelines applied to 2022: the 2021 HHS poverty - # guideline for two people, $12,880 + $4,540 = $17,420. Years before FY2026 are + # guideline for two people, $12,880 + $4,540 = $17,420. Years before FY25 are # unverified backfilled estimates. md_meap_fpg: 17_420 diff --git a/policyengine_us/tests/policy/baseline/gov/states/md/dhs/meap/integration.yaml b/policyengine_us/tests/policy/baseline/gov/states/md/dhs/meap/integration.yaml index 853c1827ca7..5502ba76d25 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/md/dhs/meap/integration.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/md/dhs/meap/integration.yaml @@ -5,21 +5,24 @@ # 200% FPG limits are twice the 2025 HHS poverty guideline ($15,650 plus $5,500 per # additional person), which is the guideline the levels are measured against: # https://dhs.maryland.gov/documents/OHEP/Advisory%20Board/Income-Guidelines-FY2026-Updated-7.9.2025.pdf -# - FY2026 LIHEAP State Plan, pages 5 (categorical eligibility), 9 (renters in -# subsidized housing) and 11 (item 2.5, Garrett County multiplier): +# - FY2026 LIHEAP State Plan, pages 5 (categorical eligibility), 6 (SSA benefits +# "Excluding MediCare deduction"), 9 (renters in subsidized housing) and 11 (item +# 2.5, Garrett County multiplier): # https://liheapch.acf.gov/docs/2026/state-plans/MD_Plan_2026.pdf#page=11 # - OHEP Operations Manual (May 2025), pages 16 (general eligibility), 39 (automatic # denials), 62-63 (categorical eligibility and the nominal benefit) and 108 -# (ineligible members): +# (ineligible members and children under 18): # https://dhs.maryland.gov/documents/OHEP/OHEP-Operations-Manual.pdf#page=62 -# - FY27 MEAP Benefit Matrices, page 1 (Garrett County table used to infer rounding): +# - FY27 MEAP Benefit Matrices, page 1 (Garrett County table, half-up rounding): # https://dhs.maryland.gov/documents/OHEP/FY27-MEAP-Benefit-Matrices.pdf#page=1 -# - COMAR 07.03.21.04 (household members and countable income) and .06D(3) (denials): +# - COMAR 07.03.21.04 (household members and countable income): # https://regs.maryland.gov/us/md/exec/comar/07.03.21.04 +# - COMAR 07.03.21.06D(3) (denials): +# https://regs.maryland.gov/us/md/exec/comar/07.03.21.06 # Every case states that the household does not receive SNAP, TCA or SSI unless the -# case is about that receipt. +# case is about that receipt (Case 12 lets SNAP compute). -- name: Case 1, mixed-status family follows the regulation household count and counts the excluded member's income. +- name: Case 1, mixed-status family counts the child under the manual rule and the child's income. period: 2026 input: people: @@ -45,23 +48,23 @@ state_code: MD county: BALTIMORE_COUNTY_MD output: - # COMAR 07.03.21.04C(1): a member who is not a citizen or qualified alien is not - # counted, but the member's income is included. (Manual page 108 would count a - # child under 18 regardless of status; that conflict awaits the owner's call.) - md_meap_household_size: 1 - # $20,000 wages (D(1)) + $1,000 child support received by the child (D(14), C(1)). + # Manual page 108: "Children under 18 are always counted in the household, + # regardless of citizenship status", so the undocumented child counts (COMAR + # 07.03.21.04C(1) would not count the child; the manual is the operating rule). + md_meap_household_size: 2 + # $20,000 wages (D(1)) + $1,000 child support received by the child (D(14)). md_meap_countable_income: 21_000 - # Income Guidelines, annual table, size 1: $31,300. - md_meap_income_limit: 31_300 - # $31,300 / 2, the 2025 one-person guideline. - md_meap_fpg: 15_650 - # $21,000 / $15,650 = 134%: matrix Level 4 (101-150% FPL). - md_meap_level: 4 - # $21,000 is under the $31,300 limit; the parent is a counted member; the gas + # Income Guidelines, annual table, size 2: $42,300. + md_meap_income_limit: 42_300 + # $42,300 / 2, the 2025 two-person guideline. + md_meap_fpg: 21_150 + # $21,000 / $21,150 = 99%: matrix Level 3 (51-100% FPL). + md_meap_level: 3 + # $21,000 is under the $42,300 limit; the parent is a qualified member; the gas # bill establishes a heating cost (manual page 16). md_meap_eligible: true - # Matrix, Gas row, Level 4: $360. - md_meap: 360 + # Matrix, Gas row, Level 3: $400. + md_meap: 400 - name: Case 2, SSI recipient living with a working adult above 200 percent in subsidized Garrett County housing gets the nominal grant. period: 2026 @@ -166,7 +169,7 @@ state_code: MD county: ANNE_ARUNDEL_COUNTY_MD output: - # Four citizens (COMAR 07.03.21.04C). + # Four citizens (manual page 108). md_meap_household_size: 4 # $38,000 + $14,000 wages (D(1)). md_meap_countable_income: 52_000 @@ -207,8 +210,10 @@ county: GARRETT_COUNTY_MD output: md_meap_household_size: 2 - # $16,800 + $9,600 Social Security (COMAR 07.03.21.04D(3)), counted gross: the - # Medicare premium deduction is not wired in. + # $16,800 + $9,600 Social Security (COMAR 07.03.21.04D(3)), net of the Medicare + # Part B premium: at this income a Medicare Savings Program pays both spouses' + # premiums, so the model's out-of-pocket premium is $0 and nothing is deducted + # (Case 11 shows the deduction). md_meap_countable_income: 26_400 # Income Guidelines, annual table, size 2: $42,300; the guideline is half of it. md_meap_income_limit: 42_300 @@ -218,7 +223,7 @@ md_meap_eligible: true # Matrix, Propane row, Level 4: $725, times the 1.1 Garrett County multiplier # (State Plan item 2.5, page 11) = $797.50, rounded half up to $798 (rounding - # inferred from the FY27 Garrett County table, page 1). + # follows the FY27 Garrett County table, page 1). md_meap: 798 - name: Case 6, family over the income limit without categorical eligibility receives nothing. @@ -343,7 +348,8 @@ output: md_meap_household_size: 11 # $58,000 + $41,000 + $27,300 wages (COMAR 07.03.21.04D(1)) + $11,700 Social - # Security (D(3)) = $138,000. + # Security (D(3); a Medicare Savings Program pays the grandparent's premium at + # this income, so nothing is deducted) = $138,000. md_meap_countable_income: 138_000 # 2025 HHS poverty guideline for eleven: $15,650 + 10 x $5,500 = $70,650. md_meap_fpg: 70_650 @@ -378,8 +384,9 @@ # Manual page 16: heating costs paid as an undesignated component of rent qualify; # the household is not in subsidized housing. md_meap_eligible: true - # Every matrix row is a fuel, so no amount can be read for an unknown fuel: - # heating_type is a required input and an unspecified fuel pays $0. + # Every matrix row is a fuel, so no amount can be read for an unknown fuel: the + # $0 is a coverage gap, not a denial, and the unspecified default is tracked in + # #9754. md_meap: 0 - name: Case 10, categorically eligible eleven-member household above the 60 percent SMI limit but below 200 percent is paid at Level 5. @@ -440,3 +447,67 @@ md_meap_eligible: true # Matrix, Gas row, Level 5: $300. md_meap: 300 + +- name: Case 11, retiree whose Social Security is just over the limit qualifies once the Medicare premium is deducted. + period: 2026 + input: + state_code: MD + county: BALTIMORE_COUNTY_MD + age: 70 + social_security_retirement: 32_000 + snap: 0 + tanf: 0 + ssi: 0 + heating_type: NATURAL_GAS + gas_expense: 1_150 + output: + # $32,000 of Social Security is over the $31,300 one-person limit (Income + # Guidelines), but COMAR 07.03.21.04D(3) counts it "less Medicare payment + # deduction" and State Plan page 6 checks "Excluding MediCare deduction". The + # model's 2026 Part B premium for this retiree is $202.90 a month, $2,434.80 a + # year, and no Medicare Savings Program pays it at this income: + # $32,000 - $2,434.80 = $29,565.20. + md_meap_countable_income: 29_565.2 + md_meap_income_limit: 31_300 + # $29,565.20 / $15,650 = 189%: matrix Level 5 (151-200% FPL). + md_meap_level: 5 + md_meap_eligible: true + # Matrix, Gas row, Level 5: $300. + md_meap: 300 + +# Nothing is pinned here: SNAP, TCA and SSI are all computed. Wages are given as +# employment_income_before_lsr, the input the TANF earned-income list reads (the +# MD TCA tests use it too); employment_income derives from it. With $1,500 a month +# of wages the model's TCA countable earnings exceed the two-person grant, so TCA +# is $0, while SNAP is positive. +- name: Case 12, SNAP computed by the model makes a working parent categorically eligible. + period: 2026 + input: + people: + person1: + age: 30 + employment_income_before_lsr: 18_000 + person2: + age: 6 + spm_units: + spm_unit: + members: [person1, person2] + heating_type: NATURAL_GAS + gas_expense: 1_150 + households: + household: + members: [person1, person2] + state_code: MD + county: BALTIMORE_CITY_MD + output: + # The model's own SNAP for a parent with $18,000 of wages and one child is + # positive, which State Plan page 5 and manual page 62 treat as categorical + # eligibility; the model's TCA for the same household is $0. + md_meap_categorically_eligible: true + # $18,000 wages (COMAR 07.03.21.04D(1)); no TCA to add under D(10). + md_meap_countable_income: 18_000 + # $18,000 / $21,150 = 85%: matrix Level 3 (51-100% FPL). + md_meap_level: 3 + md_meap_eligible: true + # Matrix, Gas row, Level 3: $400. + md_meap: 400 diff --git a/policyengine_us/tests/policy/baseline/gov/states/md/dhs/meap/md_meap.yaml b/policyengine_us/tests/policy/baseline/gov/states/md/dhs/meap/md_meap.yaml index 7237ce606a6..5ed59cf4e52 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/md/dhs/meap/md_meap.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/md/dhs/meap/md_meap.yaml @@ -1,16 +1,26 @@ # Sources for the expected values in this file: # - FY26 MEAP Benefit Matrix (one page), "the matrix" below: # https://dhs.maryland.gov/documents/OHEP/Advisory%20Board/FY26-MEAP-Benefit-Matrix-2-1-1.pdf +# - FY2025 MEAP Benefit Matrix (one page; Cases 11 and 12): +# https://liheapch.acf.gov/docs/2025/benefits-matricies/MD_BenefitMatrix_2025.pdf +# - FY27 MEAP Benefit Matrices, page 1 (statewide and Garrett County tables; Cases +# 13 and 14, the Oil/Kerosene row label and the Garrett rounding): +# https://dhs.maryland.gov/documents/OHEP/FY27-MEAP-Benefit-Matrices.pdf#page=1 # - FY2026 OHEP Income Guidelines (one page): # https://dhs.maryland.gov/documents/OHEP/Advisory%20Board/Income-Guidelines-FY2026-Updated-7.9.2025.pdf # - FY2026 LIHEAP State Plan, item 2.5 on page 11 (Garrett County multiplier): # https://liheapch.acf.gov/docs/2026/state-plans/MD_Plan_2026.pdf#page=11 -# - FY27 MEAP Benefit Matrices, page 1 (Oil/Kerosene row label, Garrett County table): -# https://dhs.maryland.gov/documents/OHEP/FY27-MEAP-Benefit-Matrices.pdf#page=1 -# - OHEP Operations Manual (May 2025), page 63 (nominal benefit above 200% FPL): +# - FY2025 LIHEAP State Plan, item 2.5 on page 11 (the same multiplier): +# https://liheapch.acf.gov/docs/2025/state-plans/MD_Plan_2025.pdf#page=11 +# - OHEP Operations Manual (May 2025), page 6 (solar subscriptions), page 38 +# (categorical eligibility denials) and page 63 (nominal benefit above 200% FPL): # https://dhs.maryland.gov/documents/OHEP/OHEP-Operations-Manual.pdf#page=63 -# - COMAR 07.03.21.03 and 07.03.21.06: +# - COMAR 07.03.21.03 (eligibility): +# https://regs.maryland.gov/us/md/exec/comar/07.03.21.03 +# - COMAR 07.03.21.06 (denials): # https://regs.maryland.gov/us/md/exec/comar/07.03.21.06 +# A model year reads the parameters at January 1, inside state FY (July to June) of +# the same number: period 2025 is FY25, 2026 is FY26 and 2027 is FY27. - name: Case 1, electric heat at each FY26 matrix level. period: 2026 @@ -69,7 +79,8 @@ md_meap_level: [1, 2, 3, 4, 5, 6, 7] county: [BALTIMORE_COUNTY_MD, BALTIMORE_COUNTY_MD, BALTIMORE_COUNTY_MD, BALTIMORE_COUNTY_MD, BALTIMORE_COUNTY_MD, BALTIMORE_COUNTY_MD, BALTIMORE_COUNTY_MD] output: - # Matrix, Wood/Coal row, Levels 1 to 7: $550, $475, $400, $360, $300, $225, $25. + # Matrix, Wood/Coal row, Levels 1 to 7: $550, $475, $400, $360, $300, $225, $25, + # the same as the FY26 Gas row (the FY25 and FY27 rows differ). md_meap: [550, 475, 400, 360, 300, 225, 25] - name: Case 6, Garrett County multiplier rounds half up and leaves the nominal grant flat. @@ -89,11 +100,11 @@ # Oil, Level 1: $1,100 x 1.1 = $1,210. # Oil, Level 7: the $25 nominal grant, no multiplier. # No FY26 Garrett County dollar table is published. Half-up rounding and the flat - # Level 7 amount are inferred from the FY27 Garrett County table (FY27 matrices, - # page 1), where every Level 7 cell is $25. + # Level 7 amount follow the FY27 Garrett County table (FY27 matrices, page 1), + # where every Level 7 cell is $25. md_meap: [523, 798, 248, 1_210, 25] -- name: Case 7, kerosene and coal rows and fuels without a matrix row. +- name: Case 7, kerosene, coal and solar rows and fuels without a matrix row. period: 2026 input: state_code: [MD, MD, MD, MD, MD, MD] @@ -103,33 +114,37 @@ output: # Matrix, Level 1 cells. Kerosene is paid from the Oil row, $1,100 (the FY26 matrix # has no kerosene row; the FY27 matrix, page 1, labels the row Oil/Kerosene). - # Coal is paid from the Wood/Coal row, $550. Solar, other fuels and no heating - # have no matrix row, $0. An unspecified fuel pays $0 because heating_type is a - # required input and the amount depends on the fuel. - md_meap: [1_100, 550, 0, 0, 0, 0] + # Coal is paid from the Wood/Coal row, $550. A grid-tied solar home pays an + # electricity bill (manual page 6 lists solar subscriptions), so it is paid from + # the Electric row, $100. Other fuels and no heating have no matrix row, $0. An + # unspecified fuel pays $0 as a coverage gap: the amount depends on the fuel and + # the unspecified default is tracked in #9754. + md_meap: [1_100, 550, 100, 0, 0, 0] -- name: Case 8, submetered home at Level 6 and assisted living denial. +- name: Case 8, submetered home at Level 6 and the assisted living denial with and without categorical eligibility. period: 2026 input: - state_code: [MD, MD, MD, MD] - county: [MONTGOMERY_COUNTY_MD, MONTGOMERY_COUNTY_MD, MONTGOMERY_COUNTY_MD, MONTGOMERY_COUNTY_MD] - heating_type: [NATURAL_GAS, NATURAL_GAS, NATURAL_GAS, NATURAL_GAS] - gas_expense: [1_150, 1_150, 1_150, 1_150] - md_meap_countable_income: [0, 0, 0, 40_000] - md_meap_categorically_eligible: [false, false, false, true] - md_meap_dwelling_type: [STANDARD, SUB_METERED, ASSISTED_LIVING, SUB_METERED] + state_code: [MD, MD, MD, MD, MD] + county: [MONTGOMERY_COUNTY_MD, MONTGOMERY_COUNTY_MD, MONTGOMERY_COUNTY_MD, MONTGOMERY_COUNTY_MD, MONTGOMERY_COUNTY_MD] + heating_type: [NATURAL_GAS, NATURAL_GAS, NATURAL_GAS, NATURAL_GAS, NATURAL_GAS] + gas_expense: [1_150, 1_150, 1_150, 1_150, 1_150] + md_meap_countable_income: [0, 0, 0, 40_000, 40_000] + md_meap_categorically_eligible: [false, false, false, true, true] + md_meap_dwelling_type: [STANDARD, SUB_METERED, ASSISTED_LIVING, SUB_METERED, ASSISTED_LIVING] output: # COMAR 07.03.21.06D(3)(k) denies an applicant who resides in an assisted living # facility. The fourth household is over the $31,300 one-person limit (Income - # Guidelines) but categorically eligible, so the income test is waived. - md_meap_eligible: [true, true, false, true] + # Guidelines) but categorically eligible, so the income test is waived. The fifth + # is categorically eligible too, but manual page 38 lets such applications be + # denied "for other eligibility issues", so assisted living still denies it. + md_meap_eligible: [true, true, false, true, false] # Matrix columns: no income is Level 1 (0-25% FPL); a submetered home is Level 6 # (Subsidized/Sub metered); $40,000 for one person is 256% of the $15,650 # guideline, Level 7 (Over 200% FPL), which the manual (page 63) pays as the # nominal benefit ahead of the submetered level. - md_meap_level: [1, 6, 1, 7] + md_meap_level: [1, 6, 1, 7, 7] # Matrix, Gas row: Level 1 $550, Level 6 $225, Level 7 $25. Ineligible: $0. - md_meap: [550, 225, 0, 25] + md_meap: [550, 225, 0, 25, 0] - name: Case 9, Maryland household and Virginia household in the same simulation. period: 2026 @@ -160,10 +175,8 @@ state_code: MD county: BALTIMORE_COUNTY_MD age: 45 - immigration_status: CITIZEN employment_income: 24_000 farm_rent_income: 12_000 - rental_income: 0 ssi: 0 tanf: 0 snap: 0 @@ -177,3 +190,67 @@ md_meap_categorically_eligible: false md_meap_eligible: false md_meap: 0 + +# FY2025 MEAP Benefit Matrix ("*Flat Rates"), Level 1 (0-25% FPL) and Level 6 +# (Subsidized/Sub metered) by row: +# Electric: $102, $102. Gas: $301, $102. Oil: $750, $102. +# Propane: $644, $102. Wood/Coal: $418, $102. +# Level 6 is $102 for every fuel in FY25; the FY25 plan (page 11) prints the $750 +# maximum, the oil Level 1 cell. +- name: Case 11, FY25 matrix rows at Levels 1 and 6 in period 2025. + period: 2025 + input: + state_code: [MD, MD, MD, MD, MD, MD, MD, MD, MD, MD] + county: [BALTIMORE_COUNTY_MD, BALTIMORE_COUNTY_MD, BALTIMORE_COUNTY_MD, BALTIMORE_COUNTY_MD, BALTIMORE_COUNTY_MD, BALTIMORE_COUNTY_MD, BALTIMORE_COUNTY_MD, BALTIMORE_COUNTY_MD, BALTIMORE_COUNTY_MD, BALTIMORE_COUNTY_MD] + heating_type: [ELECTRICITY, NATURAL_GAS, FUEL_OIL, PROPANE, WOOD, ELECTRICITY, NATURAL_GAS, FUEL_OIL, PROPANE, WOOD] + md_meap_eligible: [true, true, true, true, true, true, true, true, true, true] + md_meap_level: [1, 1, 1, 1, 1, 6, 6, 6, 6, 6] + output: + md_meap: [102, 301, 750, 644, 418, 102, 102, 102, 102, 102] + +- name: Case 12, FY25 Garrett County multiplier in period 2025. + period: 2025 + input: + state_code: [MD, MD, MD] + county: [GARRETT_COUNTY_MD, GARRETT_COUNTY_MD, GARRETT_COUNTY_MD] + heating_type: [FUEL_OIL, NATURAL_GAS, PROPANE] + md_meap_eligible: [true, true, true] + md_meap_level: [1, 5, 7] + output: + # FY2025 plan item 2.5 (page 11) applies the same 1.1 multiplier. FY25 matrix + # cells times 1.1: Oil, Level 1: $750 x 1.1 = $825. Gas, Level 5: $150 x 1.1 = + # $165. Propane, Level 7: the $25 nominal grant, no multiplier. + md_meap: [825, 165, 25] + +# FY27 MEAP Benefit Matrix, statewide table (page 1), Level 1 and Level 6 by row: +# Electric: $100, $100. Gas: $700, $315. Oil/Kerosene: $1,225, $550. +# Propane: $1,325, $595. Wood/Coal: $550, $245. +- name: Case 13, FY27 matrix rows at Levels 1 and 6 in period 2027. + period: 2027 + input: + state_code: [MD, MD, MD, MD, MD, MD, MD, MD, MD, MD] + county: [BALTIMORE_COUNTY_MD, BALTIMORE_COUNTY_MD, BALTIMORE_COUNTY_MD, BALTIMORE_COUNTY_MD, BALTIMORE_COUNTY_MD, BALTIMORE_COUNTY_MD, BALTIMORE_COUNTY_MD, BALTIMORE_COUNTY_MD, BALTIMORE_COUNTY_MD, BALTIMORE_COUNTY_MD] + heating_type: [ELECTRICITY, NATURAL_GAS, FUEL_OIL, PROPANE, WOOD, ELECTRICITY, NATURAL_GAS, FUEL_OIL, PROPANE, WOOD] + md_meap_eligible: [true, true, true, true, true, true, true, true, true, true] + md_meap_level: [1, 1, 1, 1, 1, 6, 6, 6, 6, 6] + output: + md_meap: [100, 700, 1_225, 1_325, 550, 100, 315, 550, 595, 245] + +- name: Case 14, FY27 Garrett County table in period 2027. + period: 2027 + input: + state_code: [MD, MD, MD, MD, MD] + county: [GARRETT_COUNTY_MD, GARRETT_COUNTY_MD, GARRETT_COUNTY_MD, GARRETT_COUNTY_MD, GARRETT_COUNTY_MD] + heating_type: [ELECTRICITY, NATURAL_GAS, WOOD, FUEL_OIL, FUEL_OIL] + md_meap_eligible: [true, true, true, true, true] + md_meap_level: [1, 2, 5, 4, 7] + output: + # FY27 Garrett County table (page 1) against the statewide cells times 1.1: + # Electric, Level 1: $100 x 1.1 = $110, printed $110. + # Gas, Level 2: $665 x 1.1 = $731.50, rounded half up to $732, printed $732. + # Wood/Coal, Level 5: $355 x 1.1 = $390.50, rounded half up to $391, printed $391. + # Oil, Level 4: $980 x 1.1 = $1,078. The table prints $1,210 in this one cell + # (1.1 x the FY26 oil Level 1), which no other cell supports; the model keeps + # the plan's multiplier. + # Oil, Level 7: the $25 nominal grant, printed $25. + md_meap: [110, 732, 391, 1_078, 25] diff --git a/policyengine_us/variables/gov/states/md/dhs/meap/eligibility/md_meap_eligible.py b/policyengine_us/variables/gov/states/md/dhs/meap/eligibility/md_meap_eligible.py index f122fc264cb..57967995fef 100644 --- a/policyengine_us/variables/gov/states/md/dhs/meap/eligibility/md_meap_eligible.py +++ b/policyengine_us/variables/gov/states/md/dhs/meap/eligibility/md_meap_eligible.py @@ -10,14 +10,15 @@ class md_meap_eligible(Variable): reference = ( "https://regs.maryland.gov/us/md/exec/comar/07.03.21.03", "https://regs.maryland.gov/us/md/exec/comar/07.03.21.06", - # PDF pages 11, 12, 16, 39. + # PDF pages 11, 12, 16, 38, 39, 108. "https://dhs.maryland.gov/documents/OHEP/OHEP-Operations-Manual.pdf#page=11", ) documentation = ( "Regular heating has no resource test. Application documentation, emancipation " "and program-year duplicate awards are not modeled. The regulation permits an " "annually announced medical-expense waiver, but no such regular-heating waiver " - "was found in the FY2026 plan. At least one qualified member is required." + "was found in the FY2026 plan. At least one qualified member is required; a " + "child counted regardless of status does not satisfy that requirement." ) def formula(spm_unit, period, parameters): @@ -27,13 +28,18 @@ def formula(spm_unit, period, parameters): subsidized = spm_unit( "receives_housing_assistance", period ) | spm_unit.household("is_in_public_housing", period) + # Manual page 38: categorical eligibility waives the income test but + # not the other denials. income_eligible = spm_unit("md_meap_categorically_eligible", period) | ( spm_unit("md_meap_countable_income", period) <= spm_unit("md_meap_income_limit", period) ) + # Manual page 108: the household must have at least one eligible + # (citizen or qualified alien) member. + qualified_member = add(spm_unit, period, ["is_citizen_or_legal_immigrant"]) > 0 return ( income_eligible - & (spm_unit("md_meap_household_size", period) > 0) + & qualified_member & (spm_unit("has_heating_expense", period) | heat_in_rent) & ~(heat_in_rent & subsidized) & ~assisted_living diff --git a/policyengine_us/variables/gov/states/md/dhs/meap/eligibility/md_meap_household_size.py b/policyengine_us/variables/gov/states/md/dhs/meap/eligibility/md_meap_household_size.py index 55ddd699fe5..8f45c386859 100644 --- a/policyengine_us/variables/gov/states/md/dhs/meap/eligibility/md_meap_household_size.py +++ b/policyengine_us/variables/gov/states/md/dhs/meap/eligibility/md_meap_household_size.py @@ -8,17 +8,25 @@ class md_meap_household_size(Variable): label = "Maryland MEAP qualified household size" defined_for = StateCode.MD reference = ( - "https://regs.maryland.gov/us/md/exec/comar/07.03.21.04", # PDF pages 107, 108. - "https://dhs.maryland.gov/documents/OHEP/OHEP-Operations-Manual.pdf#page=107", + "https://dhs.maryland.gov/documents/OHEP/OHEP-Operations-Manual.pdf#page=108", + "https://regs.maryland.gov/us/md/exec/comar/07.03.21.04", ) documentation = ( - "Follows COMAR 07.03.21.04C: nonqualified members are excluded from size and " - "their income counts in full. The May 2025 manual instead includes all " - "children under 18 regardless of status; this unresolved conflict is recorded " - "explicitly, and this draft follows the regulation. SPM membership " - "approximates the energy-sharing household. No SNAP-specific immigration bars " - "apply." + "Follows the May 2025 manual (page 108): every child under 18 counts " + "regardless of immigration status, nonqualified adults are excluded from " + "size, and every member's income counts in full. COMAR 07.03.21.04C(1) would " + "exclude every nonqualified member; the manual is the operating rule. SPM " + "membership approximates the energy-sharing household. No SNAP-specific " + "immigration bars apply." ) - adds = ["is_citizen_or_legal_immigrant"] + def formula(spm_unit, period, parameters): + p = parameters(period).gov.states.md.dhs.meap + person = spm_unit.members + # Manual page 108: "Children under 18 are always counted in the + # household, regardless of citizenship status"; ineligible adults are + # not counted. + child = person("age", period) < p.adult_age + qualified = person("is_citizen_or_legal_immigrant", period) + return spm_unit.sum(qualified | child) diff --git a/policyengine_us/variables/gov/states/md/dhs/meap/eligibility/md_meap_income_limit.py b/policyengine_us/variables/gov/states/md/dhs/meap/eligibility/md_meap_income_limit.py index 35d122897da..8a87469af84 100644 --- a/policyengine_us/variables/gov/states/md/dhs/meap/eligibility/md_meap_income_limit.py +++ b/policyengine_us/variables/gov/states/md/dhs/meap/eligibility/md_meap_income_limit.py @@ -12,14 +12,16 @@ class md_meap_income_limit(Variable): reference = ( "https://dhs.maryland.gov/documents/OHEP/Advisory%20Board/Income-Guidelines-FY2026-Updated-7.9.2025.pdf", "https://liheapch.acf.gov/docs/2026/state-plans/MD_Plan_2026.pdf#page=9", + # PDF pages 2, 5. + "https://acf.gov/sites/default/files/documents/ocs/COMM_LIHEAP_IM2025-02_SMIStateTable_Att4.pdf#page=5", ) documentation = ( "The published annual 200% FPG amounts control sizes 1-10 rather than the " "plan's truncated equivalent SMI percentages or its inconsistent " "additional-member line. Annual inputs are compared with annual limits; the " - "printed monthly amounts are rounded separately. Large-household dollar " - "amounts are calculated from the stated SMI rule, not independently published " - "FY26 dollar rows." + "printed monthly amounts are rounded separately. Sizes 11 and above use 60% " + "SMI and reproduce the dollar rows ACF LIHEAP IM 2025-02 Attachment 4 prints " + "for sizes 7 to 12." ) def formula(spm_unit, period, parameters): @@ -28,8 +30,8 @@ def formula(spm_unit, period, parameters): state = spm_unit.household("state_code_str", period) base = parameters(period).gov.hhs.smi.amount[state] factor = smi(size, state, period, parameters) / base - # Federal 60% SMI table ordering; FY26 publishes the large-household - # rule, but no dollar rows at sizes 11 and above. + # ACF LIHEAP IM 2025-02 Attachment 4 (page 5) floors 60% of the + # four-person SMI, then floors each size's share of it. smi_limit = np.floor(factor * np.floor(base * p.smi_rate)) poverty_limit = spm_unit("md_meap_fpg", period) * p.fpg_rate return where(size >= p.smi_min_size, smi_limit, poverty_limit) diff --git a/policyengine_us/variables/gov/states/md/dhs/meap/income/md_meap_countable_earned_income.py b/policyengine_us/variables/gov/states/md/dhs/meap/income/md_meap_countable_earned_income.py index ab5b4da8611..c05b0849a69 100644 --- a/policyengine_us/variables/gov/states/md/dhs/meap/income/md_meap_countable_earned_income.py +++ b/policyengine_us/variables/gov/states/md/dhs/meap/income/md_meap_countable_earned_income.py @@ -8,19 +8,21 @@ class md_meap_countable_earned_income(Variable): label = "Maryland MEAP countable earnings" unit = USD defined_for = StateCode.MD - reference = ("https://regs.maryland.gov/us/md/exec/comar/07.03.21.04",) + reference = ("https://regs.maryland.gov/us/md/exec/comar/07.03.21.04#E(3)",) documentation = ( "Uses existing net business income without an additional expense deduction. " - "MEAP depreciation add-backs are unsupported. Flooring losses is a modeling " - "convention because the sources do not specify their treatment." + "MEAP depreciation add-backs are unsupported. Each source is floored at zero, " + "so a loss in one source cannot offset another." ) def formula(person, period, parameters): - p = parameters(period).gov.states.md.dhs.meap.income + p = parameters(period).gov.states.md.dhs.meap earned = 0 - for source in p.sources.earned: + for source in p.income.sources.earned: earned = earned + max_(person(source, period), 0) - counted = (person("age", period) >= p.earned_income_min_age) & ~person( + # COMAR 07.03.21.04E(3) excludes the employment income of a child + # younger than 18 or of a full-time student. + counted = (person("age", period) >= p.adult_age) & ~person( "is_full_time_student", period ) return where(counted, earned, 0) diff --git a/policyengine_us/variables/gov/states/md/dhs/meap/income/md_meap_countable_income.py b/policyengine_us/variables/gov/states/md/dhs/meap/income/md_meap_countable_income.py index 2167b375c98..555c22f672a 100644 --- a/policyengine_us/variables/gov/states/md/dhs/meap/income/md_meap_countable_income.py +++ b/policyengine_us/variables/gov/states/md/dhs/meap/income/md_meap_countable_income.py @@ -8,26 +8,55 @@ class md_meap_countable_income(Variable): label = "Maryland MEAP countable household income" unit = USD defined_for = StateCode.MD - reference = ("https://regs.maryland.gov/us/md/exec/comar/07.03.21.04",) + reference = ( + "https://regs.maryland.gov/us/md/exec/comar/07.03.21.04", + # PDF pages 111, 112. + "https://dhs.maryland.gov/documents/OHEP/OHEP-Operations-Manual.pdf#page=111", + # PDF pages 6, 7. + "https://liheapch.acf.gov/docs/2026/state-plans/MD_Plan_2026.pdf#page=6", + ) documentation = ( "Annual income approximates the 30-day application period. All members' income " - "counts in full. Follows COMAR exclusions for nonrecurring lump sums and " - "AmeriCorps/VISTA despite conflicting manual/plan checklists. Reported child " - "support paid is assumed court-ordered. Veterans income approximates pension " - "benefits and strike income is assumed not employee-funded. TDAP, royalties, " - "gifts, loans, special allowances and home-care receipts cannot be isolated " - "reliably. Medicare-premium deductions remain deferred. Rental income is " - "assumed nonnegative." + "counts in full. Follows COMAR where the checklists differ: nonrecurring lump " + "sums and AmeriCorps/VISTA are excluded although the manual and plan count " + "them; interest and dividends count although the plan leaves them unchecked; " + "strike funds without employee contributions count although manual " + "Attachment B excludes them. Reported child support paid is assumed " + "court-ordered. Veterans income approximates pension benefits and strike " + "income is assumed not employee-funded. Social Security and railroad " + "retirement count net of the computed Medicare Part B premium. Each source is " + "floored at zero, so a rental or estate loss does not offset other income. " + "TDAP, royalties, gifts, loans, special allowances and home-care receipts " + "cannot be isolated reliably." ) def formula(spm_unit, period, parameters): p = parameters(period).gov.states.md.dhs.meap.income + person = spm_unit.members # COMAR 07.03.21.04D(3), D(26) and E(20) count Social Security and - # railroad retirement benefits less the Medicare payment deduction. - # medicare_part_b_premium and medicare_part_b_premiums_reported are not - # wired in yet, so both benefits count gross. - # The TANF source applies take-up to Maryland TCA entitlement. - income = add(spm_unit, period, ["md_meap_countable_earned_income"]) + add( - spm_unit, period, p.sources.unearned + # railroad retirement benefits less the Medicare payment deduction, and + # plan 1.9 (page 6) checks "Excluding MediCare deduction". The computed + # medicare_part_b_premium is the out-of-pocket Part B premium, zero when + # a Medicare Savings Program pays it. + benefits = spm_unit.sum( + max_( + person("social_security", period) + + person("railroad_benefits", period) + - person("medicare_part_b_premium", period), + 0, + ) + ) + # COMAR 07.03.21.04D(2) treats self-employment and rental income as one + # item and plan 1.8 (page 6) counts gross income, so each unearned + # source is floored like the earned sources: a loss in one source + # cannot offset another. The TANF source applies take-up to Maryland + # TCA entitlement. + unearned = 0 + for source in p.sources.unearned: + unearned = unearned + max_(add(spm_unit, period, [source]), 0) + income = ( + add(spm_unit, period, ["md_meap_countable_earned_income"]) + + benefits + + unearned ) return max_(income - add(spm_unit, period, ["child_support_expense"]), 0) diff --git a/policyengine_us/variables/gov/states/md/dhs/meap/income/md_meap_fpg.py b/policyengine_us/variables/gov/states/md/dhs/meap/income/md_meap_fpg.py index bc1a977fe4b..c4a8e1381da 100644 --- a/policyengine_us/variables/gov/states/md/dhs/meap/income/md_meap_fpg.py +++ b/policyengine_us/variables/gov/states/md/dhs/meap/income/md_meap_fpg.py @@ -11,6 +11,12 @@ class md_meap_fpg(Variable): defined_for = StateCode.MD reference = ( "https://dhs.maryland.gov/documents/OHEP/Advisory%20Board/Income-Guidelines-FY2026-Updated-7.9.2025.pdf", + "https://liheapch.acf.gov/docs/2025/benefits-matricies/MD_BenefitMatrix_2025.pdf", + ) + documentation = ( + "The FY2026 limits are twice the 2025 poverty guideline and the FY25 matrix " + "band tops twice the 2024 guideline, so the guideline of the calendar year " + "before the model year applies; no sheet states the lag." ) def formula(spm_unit, period, parameters): diff --git a/policyengine_us/variables/gov/states/md/dhs/meap/md_meap.py b/policyengine_us/variables/gov/states/md/dhs/meap/md_meap.py index 9629c69f6e6..415c7f62a08 100644 --- a/policyengine_us/variables/gov/states/md/dhs/meap/md_meap.py +++ b/policyengine_us/variables/gov/states/md/dhs/meap/md_meap.py @@ -9,21 +9,28 @@ class md_meap(Variable): unit = USD defined_for = "md_meap_eligible" reference = ( + "https://liheapch.acf.gov/docs/2025/benefits-matricies/MD_BenefitMatrix_2025.pdf", "https://dhs.maryland.gov/documents/OHEP/Advisory%20Board/FY26-MEAP-Benefit-Matrix-2-1-1.pdf", + "https://dhs.maryland.gov/documents/OHEP/FY27-MEAP-Benefit-Matrices.pdf#page=1", + "https://liheapch.acf.gov/docs/2025/state-plans/MD_Plan_2025.pdf#page=11", # PDF pages 9, 10, 11. "https://liheapch.acf.gov/docs/2026/state-plans/MD_Plan_2026.pdf#page=9", "https://regs.maryland.gov/us/md/exec/comar/07.03.21.07", - "https://dhs.maryland.gov/documents/OHEP/FY27-MEAP-Benefit-Matrices.pdf#page=1", ) documentation = ( - "Annual regular heating assistance, verified for state FY26 (July 2025-June " - "2026), modeled in period 2026. Earlier results are unverified backfilled " - "estimates. No actual-bill cap applies. Kerosene uses oil (explicitly labeled " - "together in FY27); heat-in-rent uses the building's known fuel row, an " - "inference because no separate renter table is published. Unknown fuels return " - "zero; unknown county receives the statewide amount and cannot receive the " - "Garrett supplement. Means-tested VA eligibility remains unsupported. Cooling, " - "EUSP, arrearages, crisis and equipment benefits are excluded." + "Annual regular heating assistance, verified for state FY25 to FY27 (July " + "2024 to June 2027), which periods 2025 to 2027 read at January 1. Earlier " + "results are unverified backfilled estimates. No actual-bill cap applies. " + "Kerosene uses the oil row (labeled Oil/Kerosene from FY27) and a grid-tied " + "solar home uses the electricity row; heat-in-rent uses the building's known " + "fuel row, an inference because no separate renter table is published. Other " + "and unspecified fuels return zero as a coverage gap. Garrett County applies " + "the plan's 1.1 multiplier with half-up rounding; the published FY27 Garrett " + "table matches it in every cell but oil Level 4, which prints $1,210 where " + "1.1 times $980 is $1,078. An unknown county receives the statewide amount. " + "Means-tested VA eligibility remains unsupported. The $21 SNAP nominal " + "payment, cooling, EUSP, arrearage, crisis and equipment benefits are " + "excluded." ) def formula(spm_unit, period, parameters): @@ -32,25 +39,29 @@ def formula(spm_unit, period, parameters): level = max_(spm_unit("md_meap_level", period), 1) fuel = spm_unit("heating_type", period) types = fuel.possible_values + # A grid-tied solar home pays an electricity bill, the pairing + # heating_expense uses; manual 1.1.1 (page 6) lists solar subscriptions. amount = select( [ - fuel == types.ELECTRICITY, - (fuel == types.NATURAL_GAS) - | (fuel == types.WOOD) - | (fuel == types.COAL), + (fuel == types.ELECTRICITY) | (fuel == types.SOLAR), + fuel == types.NATURAL_GAS, + (fuel == types.WOOD) | (fuel == types.COAL), (fuel == types.FUEL_OIL) | (fuel == types.KEROSENE), fuel == types.PROPANE, ], [ p.amount.electricity[level], - p.amount.gas_wood_coal[level], + p.amount.gas[level], + p.amount.wood_coal[level], p.amount.oil[level], p.amount.propane[level], ], + # Other and unspecified fuels have no matrix row; the unspecified + # default is tracked in #9754. default=0, ) county = spm_unit.household("county_str", period) regional = np.isin(county, p.regional_counties) & (level != p.nominal_level) - # Half-up rounding and the flat nominal amount are inferred from the - # later published Garrett table; no FY26 Garrett dollar table was found. + # Half-up rounding and the flat nominal amount follow the FY27 Garrett + # table; FY25 and FY26 publish no Garrett dollar table. return np.floor(amount * where(regional, p.garrett_multiplier, 1) + 0.5) diff --git a/policyengine_us/variables/gov/states/md/dhs/meap/md_meap_dwelling_type.py b/policyengine_us/variables/gov/states/md/dhs/meap/md_meap_dwelling_type.py index 1f930a03716..fe608267eaf 100644 --- a/policyengine_us/variables/gov/states/md/dhs/meap/md_meap_dwelling_type.py +++ b/policyengine_us/variables/gov/states/md/dhs/meap/md_meap_dwelling_type.py @@ -21,5 +21,6 @@ class md_meap_dwelling_type(Variable): documentation = ( "Submetered homes use the published Level 6 payment; assisted-living " "applicants are ineligible under COMAR 07.03.21.06D(3)(k). Ordinary subsidized " - "homes use the existing housing-assistance inputs." + "homes use the existing housing-assistance inputs. If omitted, the model " + "assumes a standard residence." ) diff --git a/policyengine_us/variables/gov/states/md/dhs/meap/payment/md_meap_level.py b/policyengine_us/variables/gov/states/md/dhs/meap/payment/md_meap_level.py index 2b23c75f276..022e4a380c3 100644 --- a/policyengine_us/variables/gov/states/md/dhs/meap/payment/md_meap_level.py +++ b/policyengine_us/variables/gov/states/md/dhs/meap/payment/md_meap_level.py @@ -11,6 +11,7 @@ class md_meap_level(Variable): "https://dhs.maryland.gov/documents/OHEP/Advisory%20Board/FY26-MEAP-Benefit-Matrix-2-1-1.pdf", # PDF pages 62, 63. "https://dhs.maryland.gov/documents/OHEP/OHEP-Operations-Manual.pdf#page=62", + "https://liheapch.acf.gov/docs/2026/state-plans/MD_Plan_2026.pdf#page=9", ) documentation = ( "Submetered and subsidized homes use Level 6. The over-200% categorical " @@ -21,10 +22,13 @@ class md_meap_level(Variable): def formula(spm_unit, period, parameters): p = parameters(period).gov.states.md.dhs.meap.payment - ratio = spm_unit("md_meap_countable_income", period) / spm_unit( - "md_meap_fpg", period - ) + income = spm_unit("md_meap_countable_income", period) + fpg = spm_unit("md_meap_fpg", period) + # md_meap_fpg is zero outside Maryland; the substitute divisor avoids + # 0 / 0 on rows that defined_for masks afterwards. + ratio = income / where(fpg > 0, fpg, 1) # Inclusive band tops resolve the gaps between integer percentage labels. + # The top bracket is Level 7, which nominal_level also names. income_level = p.income_level.calc(ratio, right=True) subsidized = spm_unit( "receives_housing_assistance", period