diff --git a/changelog.d/vt-liheap-wave1d.added.md b/changelog.d/vt-liheap-wave1d.added.md new file mode 100644 index 00000000000..910900a063f --- /dev/null +++ b/changelog.d/vt-liheap-wave1d.added.md @@ -0,0 +1 @@ +Add Vermont Seasonal Fuel Assistance (LIHEAP regular heating) for the 2025-26 and 2026-27 heating seasons. diff --git a/policyengine_us/parameters/gov/states/vt/dcf/liheap/eligibility/fpg_rate.yaml b/policyengine_us/parameters/gov/states/vt/dcf/liheap/eligibility/fpg_rate.yaml new file mode 100644 index 00000000000..819834777a1 --- /dev/null +++ b/policyengine_us/parameters/gov/states/vt/dcf/liheap/eligibility/fpg_rate.yaml @@ -0,0 +1,24 @@ +description: Vermont limits gross household income to this share of the federal poverty guideline under the Low Income Home Energy Assistance Program. +# The income limit applies this rate to the whole-dollar monthly guideline +# (vt_liheap_fpg / 12) and rounds half up to the dollar (size 1: 1.85 x 1,304 = +# 2,412.40, so $2,412; size 2: 1.85 x 1,763 = 3,261.55, so $3,262). This +# reproduces all 15 P-2913 amounts for 2025-26 and all 15 DCF amounts for +# 2026-27 (size 1: 1.85 x 1,330 = 2,460.50, so $2,461). The same rate times the +# annual guideline caps that amount where it rounds above 185% (size 2 in 2026: +# 39,127.50 a year, not 3,262 x 12 = 39,144). +values: + 2024-06-22: 1.85 + +metadata: + unit: /1 + period: year + label: Vermont LIHEAP poverty guideline income limit rate + reference: + - title: 33 V.S.A. § 2604(a) + href: https://legislature.vermont.gov/statutes/section/33/026/02604 + - title: Vermont Seasonal Fuel Assistance Rule 2913(b) + href: https://outside.vermont.gov/dept/DCF/Shared%20Documents/ESD/Rules/2900-Seasonal-Fuel-Assistance.pdf#page=38 + - title: Vermont DCF Procedure P-2913, Income Guidelines (effective March 1, 2025 to February 28, 2026) + href: https://outside.vermont.gov/dept/DCF/Policies%20Procedures%20Guidance/ESD-Procedure-P2913.pdf + - title: Vermont DCF Fuel Assistance Income Guidelines for 2026-2027 + href: https://dcf.vermont.gov/esd/fuel/income-guidelines diff --git a/policyengine_us/parameters/gov/states/vt/dcf/liheap/fpg_table_size.yaml b/policyengine_us/parameters/gov/states/vt/dcf/liheap/fpg_table_size.yaml new file mode 100644 index 00000000000..8aa90e07329 --- /dev/null +++ b/policyengine_us/parameters/gov/states/vt/dcf/liheap/fpg_table_size.yaml @@ -0,0 +1,27 @@ +description: Vermont rounds the monthly poverty guideline of each household size up to this size, and adds the rounded monthly additional-person amount for each person above it, under the Low Income Home Energy Assistance Program. +# HHS lists the guideline for households of up to eight people, plus an amount for +# each additional person. P-2913 and P-2914A print monthly dollars for sizes 1-15 +# without stating their derivation. Dividing each listed guideline by 12 and rounding +# half up to the dollar, then adding the additional-person amount divided by 12 and +# rounded the same way for each person above eight, reproduces all 165 P-2914A band +# tops, all 15 P-2913 limits and the 15 DCF 2026-27 limits; this is a numerical +# inference. Rounding each size's own guideline divided by 12 reproduces only 2 of +# the 55 band tops for sizes 11-15 (the other 53 by $1-$4). +values: + 2025-03-01: 8 + +metadata: + unit: person + period: year + label: Vermont LIHEAP poverty guideline maximum tabulated household size + reference: + - title: HHS 2025 Poverty Guidelines, 90 FR 5917 + href: https://www.govinfo.gov/content/pkg/FR-2025-01-17/pdf/2025-01377.pdf#page=1 + - title: HHS 2026 Poverty Guidelines, 91 FR 1798 + href: https://www.govinfo.gov/content/pkg/FR-2026-01-15/pdf/2026-00755.pdf#page=2 + - title: Vermont DCF Procedure P-2913, Income Guidelines (effective March 1, 2025 to February 28, 2026) + href: https://outside.vermont.gov/dept/DCF/Policies%20Procedures%20Guidance/ESD-Procedure-P2913.pdf + - title: "Vermont DCF Procedure P-2914A, Percentage of Poverty, 2025-2026 #page 1-3" + href: https://outside.vermont.gov/dept/DCF/Policies%20Procedures%20Guidance/ESD-Procedure-P2914A.pdf#page=1 + - title: Vermont DCF Fuel Assistance Income Guidelines for 2026-2027 + href: https://dcf.vermont.gov/esd/fuel/income-guidelines diff --git a/policyengine_us/parameters/gov/states/vt/dcf/liheap/fpg_year_lag.yaml b/policyengine_us/parameters/gov/states/vt/dcf/liheap/fpg_year_lag.yaml new file mode 100644 index 00000000000..4f1f0b0b97c --- /dev/null +++ b/policyengine_us/parameters/gov/states/vt/dcf/liheap/fpg_year_lag.yaml @@ -0,0 +1,19 @@ +description: Vermont uses the federal poverty guidelines from this many years before the model year for the income limit and the percentage of poverty under the Low Income Home Energy Assistance Program. +# P-2913 applies the 2025 guidelines to applications from March 1, 2025 through +# February 28, 2026, and P-2910 starts the new guidelines each March, so model +# period 2026 (the 2025-26 heating season) uses the 2025 guidelines and 2027 +# (the 2026-27 season) the 2026 guidelines. Earlier years are unverified backfill. +values: + 2025-03-01: 1 + +metadata: + unit: year + period: year + label: Vermont LIHEAP poverty guideline year lag + reference: + - title: Vermont DCF Procedure P-2913, Income Guidelines (effective March 1, 2025 to February 28, 2026) + href: https://outside.vermont.gov/dept/DCF/Policies%20Procedures%20Guidance/ESD-Procedure-P2913.pdf + - title: Vermont DCF Procedure P-2910, Income + href: https://outside.vermont.gov/dept/DCF/Policies%20Procedures%20Guidance/ESD-Procedure-P2910.pdf + - title: Vermont DCF Fuel Assistance Income Guidelines for 2026-2027 + href: https://dcf.vermont.gov/esd/fuel/income-guidelines diff --git a/policyengine_us/parameters/gov/states/vt/dcf/liheap/income/child_earnings_age_limit.yaml b/policyengine_us/parameters/gov/states/vt/dcf/liheap/income/child_earnings_age_limit.yaml new file mode 100644 index 00000000000..ae21d9900f8 --- /dev/null +++ b/policyengine_us/parameters/gov/states/vt/dcf/liheap/income/child_earnings_age_limit.yaml @@ -0,0 +1,11 @@ +description: Vermont excludes the earned income of student household members below this age under the Low Income Home Energy Assistance Program. +values: + 2024-06-22: 18 + +metadata: + unit: year + period: year + label: Vermont LIHEAP child student earnings exclusion age limit + reference: + - title: Vermont Seasonal Fuel Assistance Rule 2912(a)(12) + href: https://outside.vermont.gov/dept/DCF/Shared%20Documents/ESD/Rules/2900-Seasonal-Fuel-Assistance.pdf#page=34 diff --git a/policyengine_us/parameters/gov/states/vt/dcf/liheap/income/sources/earned.yaml b/policyengine_us/parameters/gov/states/vt/dcf/liheap/income/sources/earned.yaml new file mode 100644 index 00000000000..1bdc4b374b8 --- /dev/null +++ b/policyengine_us/parameters/gov/states/vt/dcf/liheap/income/sources/earned.yaml @@ -0,0 +1,30 @@ +description: Vermont uses these sources as earned income under the Low Income Home Energy Assistance Program. +# Rule 2911.3 counts self-employment income, including rent from property a +# member manages and roomer or boarder payments, under the 3SquaresVT method. +# The model has only net business income, so each listed source is floored at +# zero and counted in full; a loss in one source cannot offset another. +# Under 7 CFR 273.11(a)(2)(ii) a farmer with $1,000 or more of annual gross +# proceeds offsets a farm loss against other income; with no gross-proceeds +# input, farm_operations_income is floored like the other sources. +values: + 2024-06-22: + - employment_income + - self_employment_income + - sstb_self_employment_income + - farm_operations_income + # Partnership and S corporation income from a member's own business. + - partnership_s_corp_income + +metadata: + unit: list + period: year + label: Vermont LIHEAP earned income sources + reference: + - title: "Vermont Seasonal Fuel Assistance Rules 2910, 2911.1 and 2911.3 #page 25,27,29" + href: https://outside.vermont.gov/dept/DCF/Shared%20Documents/ESD/Rules/2900-Seasonal-Fuel-Assistance.pdf#page=25 + - title: Vermont DCF Procedure P-2910, Income + href: https://outside.vermont.gov/dept/DCF/Policies%20Procedures%20Guidance/ESD-Procedure-P2910.pdf + - title: Vermont FY2026 LIHEAP State Plan, Section 1.9 + href: https://liheapch.acf.gov/docs/2026/state-plans/VT_Plan_2026.pdf#page=6 + - title: 7 CFR 273.11(a)(2)(ii) + href: https://www.law.cornell.edu/cfr/text/7/273.11 diff --git a/policyengine_us/parameters/gov/states/vt/dcf/liheap/income/sources/unearned.yaml b/policyengine_us/parameters/gov/states/vt/dcf/liheap/income/sources/unearned.yaml new file mode 100644 index 00000000000..31512d4ee84 --- /dev/null +++ b/policyengine_us/parameters/gov/states/vt/dcf/liheap/income/sources/unearned.yaml @@ -0,0 +1,58 @@ +description: Vermont uses these sources as unearned income under the Low Income Home Energy Assistance Program. +# Rule 2911.2(b) items: public assistance (SSI and General Assistance; Reach Up +# is added once through the SPM-unit tanf aggregate), annuities, pensions and +# retirement benefits, veterans' and disability benefits, workers' and +# unemployment compensation, survivors or social security benefits (Social +# Security counted gross), strike benefits, rental income, child support and +# alimony, dividends and interest, trust withdrawals and other direct money +# payments. Each listed source is floored at zero. Royalties, non-excluded scholarships and foster care or +# difficulty of care payments have no specific inputs. +values: + 2024-06-22: + - ssi + # The state AABD supplement to SSI is public assistance under rule + # 2911.2(b)(1). + - vt_aabd + - general_assistance + - pension_income + # Railroad Retirement annuities are annuities and retirement benefits under + # rule 2911.2(b)(2)-(4). + - railroad_benefits + # Military retired pay and Survivor Benefit Plan annuities are separate + # inputs from the pension inputs that pension_income adds. + - military_retirement_income + # Retirement account distributions are retirement benefits under rule + # 2911.2(b)(4) and a separate input from pension_income. + - retirement_distributions + - veterans_benefits + - disability_benefits + - workers_compensation + - total_unemployment_compensation + - social_security + # Survivor benefits other than Social Security survivor benefits. + - survivor_benefits + - strike_benefits + # Rent from property a member manages is self-employment income under rule + # 2911.3(a)(1); either way it counts, so it is listed once here. + - rental_income + - farm_rent_income + - child_support_received + - alimony_income + - interest_income + - ordinary_dividend_income + - estate_income + # Rule 2911.2(b)(16) also counts "all other direct money payments from any + # source that can be construed to be a gain or benefit". + - miscellaneous_income + +metadata: + unit: list + period: year + label: Vermont LIHEAP unearned income sources + reference: + - title: "Vermont Seasonal Fuel Assistance Rules 2910, 2911.2 and 2911.3 #page 25,27-29" + href: https://outside.vermont.gov/dept/DCF/Shared%20Documents/ESD/Rules/2900-Seasonal-Fuel-Assistance.pdf#page=25 + - title: Vermont DCF Procedure P-2910, Income + href: https://outside.vermont.gov/dept/DCF/Policies%20Procedures%20Guidance/ESD-Procedure-P2910.pdf + - title: "Vermont FY2026 LIHEAP State Plan, Section 1.9 #page 6-7" + href: https://liheapch.acf.gov/docs/2026/state-plans/VT_Plan_2026.pdf#page=6 diff --git a/policyengine_us/parameters/gov/states/vt/dcf/liheap/payment/heat_cost_percentage.yaml b/policyengine_us/parameters/gov/states/vt/dcf/liheap/payment/heat_cost_percentage.yaml new file mode 100644 index 00000000000..1cc30a7d69a --- /dev/null +++ b/policyengine_us/parameters/gov/states/vt/dcf/liheap/payment/heat_cost_percentage.yaml @@ -0,0 +1,77 @@ +description: Vermont pays this percentage of the standard heating cost, based on gross income as a share of the federal poverty guideline, under the Low Income Home Energy Assistance Program. +# Rule 2915(b) delegates the percentage of poverty to the P-2914A table of +# monthly dollar bands. Each threshold after -.inf tops the band below it: the +# band's monthly dollar top is the threshold times the whole-dollar monthly +# guideline (vt_liheap_fpg / 12), rounded up to the dollar, and monthly income at +# or below a top stays in that band (right-closed). This reproduces all 165 +# printed 2025-26 tops for sizes 1-15 (size 1: 0.74 x 1,304 = 964.96, top $965; +# size 2: 0.74 x 1,763 = 1,304.62, top $1,305). No 2026-27 P-2914A is published; +# 2027 applies the same construction to the 2026 guideline. Income above the +# income limit is excluded by eligibility, not here. +brackets: + - threshold: + 2024-06-22: -.inf + amount: + 2024-06-22: 0.9 + - threshold: + 2024-06-22: 0.74 + amount: + 2024-06-22: 0.87 + - threshold: + 2024-06-22: 0.84 + amount: + 2024-06-22: 0.84 + - threshold: + 2024-06-22: 0.94 + amount: + 2024-06-22: 0.81 + - threshold: + 2024-06-22: 1.04 + amount: + 2024-06-22: 0.78 + - threshold: + 2024-06-22: 1.14 + amount: + 2024-06-22: 0.75 + - threshold: + 2024-06-22: 1.24 + amount: + 2024-06-22: 0.72 + - threshold: + 2024-06-22: 1.34 + amount: + 2024-06-22: 0.69 + - threshold: + 2024-06-22: 1.44 + amount: + 2024-06-22: 0.66 + - threshold: + 2024-06-22: 1.54 + amount: + 2024-06-22: 0.33 + - threshold: + 2024-06-22: 1.64 + amount: + 2024-06-22: 0.3 + - threshold: + 2024-06-22: 1.74 + amount: + 2024-06-22: 0.27 + +metadata: + type: single_amount + threshold_unit: /1 + amount_unit: /1 + period: year + label: Vermont LIHEAP heat cost percentage + reference: + - title: Vermont Seasonal Fuel Assistance Rule 2915(b)-(d) + href: https://outside.vermont.gov/dept/DCF/Shared%20Documents/ESD/Rules/2900-Seasonal-Fuel-Assistance.pdf#page=42 + - title: Vermont DCF Procedure P-2915, Heat Cost Percentage Table + href: https://outside.vermont.gov/dept/DCF/Policies%20Procedures%20Guidance/ESD-Procedure-P2915.pdf + - title: "Vermont DCF Procedure P-2914A, Percentage of Poverty, 2025-2026 #page 1-3" + href: https://outside.vermont.gov/dept/DCF/Policies%20Procedures%20Guidance/ESD-Procedure-P2914A.pdf#page=1 + - title: Vermont AHS 2026-2027 Benefits Matrix, Heat Cost Percentage Table + href: https://humanservices.vermont.gov/sites/ahsnew/files/documents/2026-2027%20Benefits%20Matrx.pdf#page=1 + - title: 33 V.S.A. § 2605(a)-(b) + href: https://legislature.vermont.gov/statutes/section/33/026/02605 diff --git a/policyengine_us/parameters/gov/states/vt/dcf/liheap/payment/heat_in_rent_minimum.yaml b/policyengine_us/parameters/gov/states/vt/dcf/liheap/payment/heat_in_rent_minimum.yaml new file mode 100644 index 00000000000..8c6d94bd886 --- /dev/null +++ b/policyengine_us/parameters/gov/states/vt/dcf/liheap/payment/heat_in_rent_minimum.yaml @@ -0,0 +1,13 @@ +description: Vermont provides at least this annual benefit to households outside subsidized housing whose heat is included in rent under the Low Income Home Energy Assistance Program. +values: + 2024-06-22: 50 + +metadata: + unit: currency-USD + period: year + label: Vermont LIHEAP heat-in-rent minimum benefit + reference: + - title: Vermont Seasonal Fuel Assistance Rule 2914(c)(2)(i) + href: https://outside.vermont.gov/dept/DCF/Shared%20Documents/ESD/Rules/2900-Seasonal-Fuel-Assistance.pdf#page=40 + - title: 33 V.S.A. § 2605(e) + href: https://legislature.vermont.gov/statutes/section/33/026/02605 diff --git a/policyengine_us/parameters/gov/states/vt/dcf/liheap/payment/heat_in_rent_rate.yaml b/policyengine_us/parameters/gov/states/vt/dcf/liheap/payment/heat_in_rent_rate.yaml new file mode 100644 index 00000000000..e407275ee14 --- /dev/null +++ b/policyengine_us/parameters/gov/states/vt/dcf/liheap/payment/heat_in_rent_rate.yaml @@ -0,0 +1,13 @@ +description: Vermont pays this share of the calculated benefit to households outside subsidized housing whose heat is included in rent under the Low Income Home Energy Assistance Program. +values: + 2024-06-22: 0.3 + +metadata: + unit: /1 + period: year + label: Vermont LIHEAP heat-in-rent benefit rate + reference: + - title: Vermont Seasonal Fuel Assistance Rule 2914(c)(2) + href: https://outside.vermont.gov/dept/DCF/Shared%20Documents/ESD/Rules/2900-Seasonal-Fuel-Assistance.pdf#page=40 + - title: 33 V.S.A. § 2605(e) + href: https://legislature.vermont.gov/statutes/section/33/026/02605 diff --git a/policyengine_us/parameters/gov/states/vt/dcf/liheap/payment/heating_cost.yaml b/policyengine_us/parameters/gov/states/vt/dcf/liheap/payment/heating_cost.yaml new file mode 100644 index 00000000000..7f9fef69aa1 --- /dev/null +++ b/policyengine_us/parameters/gov/states/vt/dcf/liheap/payment/heating_cost.yaml @@ -0,0 +1,141 @@ +description: Vermont sets this standard annual heating cost by dwelling type, primary heating fuel and number of bedrooms under the Low Income Home Energy Assistance Program. +# P-2916 prints these cells for the 2025-26 heating season, and the AHS +# 2026-2027 Benefits Matrix prints the same cells for the 2026-27 season. +# The wood_and_coal row is the "Firewood, Wood Pellets and Coal" row; the +# formula maps both WOOD and COAL heating types to it, and SOLAR to the +# electricity row. The 4 column is the "4+ Bedrooms" column. Earlier years are +# unverified backfill. + +SINGLE_FAMILY_OR_MOBILE_HOME: + natural_gas: + 1: + 2025-07-01: 1_228 + 2: + 2025-07-01: 1_454 + 3: + 2025-07-01: 1_679 + 4: + 2025-07-01: 1_904 + propane: + 1: + 2025-07-01: 2_141 + 2: + 2025-07-01: 2_760 + 3: + 2025-07-01: 3_379 + 4: + 2025-07-01: 3_998 + oil: + 1: + 2025-07-01: 2_408 + 2: + 2025-07-01: 3_123 + 3: + 2025-07-01: 3_837 + 4: + 2025-07-01: 4_552 + kerosene: + 1: + 2025-07-01: 2_300 + 2: + 2025-07-01: 2_897 + 3: + 2025-07-01: 3_494 + 4: + 2025-07-01: 4_091 + electricity: + 1: + 2025-07-01: 1_692 + 2: + 2025-07-01: 1_785 + 3: + 2025-07-01: 1_877 + 4: + 2025-07-01: 1_970 + wood_and_coal: + 1: + 2025-07-01: 1_550 + 2: + 2025-07-01: 1_604 + 3: + 2025-07-01: 1_658 + 4: + 2025-07-01: 1_711 +MULTI_FAMILY: + natural_gas: + 1: + 2025-07-01: 923 + 2: + 2025-07-01: 1_148 + 3: + 2025-07-01: 1_373 + 4: + 2025-07-01: 1_599 + propane: + 1: + 2025-07-01: 1_109 + 2: + 2025-07-01: 1_728 + 3: + 2025-07-01: 2_347 + 4: + 2025-07-01: 2_966 + oil: + 1: + 2025-07-01: 2_124 + 2: + 2025-07-01: 2_839 + 3: + 2025-07-01: 3_553 + 4: + 2025-07-01: 4_268 + kerosene: + 1: + 2025-07-01: 1_610 + 2: + 2025-07-01: 2_223 + 3: + 2025-07-01: 2_831 + 4: + 2025-07-01: 3_435 + electricity: + 1: + 2025-07-01: 967 + 2: + 2025-07-01: 1_060 + 3: + 2025-07-01: 1_152 + 4: + 2025-07-01: 1_244 + wood_and_coal: + 1: + 2025-07-01: 1_085 + 2: + 2025-07-01: 1_231 + 3: + 2025-07-01: 1_343 + 4: + 2025-07-01: 1_437 + +metadata: + unit: currency-USD + period: year + label: Vermont LIHEAP standard heating cost + breakdown: + - vt_liheap_dwelling_type + - [natural_gas, propane, oil, kerosene, electricity, wood_and_coal] + - range(1, 5) + breakdown_labels: + - Dwelling type + - Primary heating fuel + - Bedrooms + propagate_metadata_to_children: true + reference: + - title: Vermont DCF Procedure P-2916, Standard Heating Cost Tables for the 2025-26 Heating Season + href: https://outside.vermont.gov/dept/DCF/Policies%20Procedures%20Guidance/ESD-Procedure-P2916.pdf#page=1 + - title: "Vermont AHS 2026-2027 Benefits Matrix, Standard Heating Cost Tables for the 2026-27 Heating Season #page 2-3" + href: https://humanservices.vermont.gov/sites/ahsnew/files/documents/2026-2027%20Benefits%20Matrx.pdf#page=2 + - title: "Vermont Seasonal Fuel Assistance Rules 2914(a)(6) and 2916 #page 39,43" + href: https://outside.vermont.gov/dept/DCF/Shared%20Documents/ESD/Rules/2900-Seasonal-Fuel-Assistance.pdf#page=39 + - title: 33 V.S.A. § 2604(b) + href: https://legislature.vermont.gov/statutes/section/33/026/02604 diff --git a/policyengine_us/parameters/gov/states/vt/dcf/liheap/payment/max_bedrooms.yaml b/policyengine_us/parameters/gov/states/vt/dcf/liheap/payment/max_bedrooms.yaml new file mode 100644 index 00000000000..840377be1bd --- /dev/null +++ b/policyengine_us/parameters/gov/states/vt/dcf/liheap/payment/max_bedrooms.yaml @@ -0,0 +1,13 @@ +description: Vermont uses the standard heating cost column for this many bedrooms for units with this many or more bedrooms under the Low Income Home Energy Assistance Program. +values: + 2025-07-01: 4 + +metadata: + unit: int + period: year + label: Vermont LIHEAP standard heating cost maximum bedrooms + reference: + - title: Vermont DCF Procedure P-2916, Standard Heating Cost Tables for the 2025-26 Heating Season + href: https://outside.vermont.gov/dept/DCF/Policies%20Procedures%20Guidance/ESD-Procedure-P2916.pdf#page=1 + - title: "Vermont AHS 2026-2027 Benefits Matrix, Standard Heating Cost Tables for the 2026-27 Heating Season #page 2-3" + href: https://humanservices.vermont.gov/sites/ahsnew/files/documents/2026-2027%20Benefits%20Matrx.pdf#page=2 diff --git a/policyengine_us/parameters/gov/states/vt/dcf/liheap/payment/nominal_benefit.yaml b/policyengine_us/parameters/gov/states/vt/dcf/liheap/payment/nominal_benefit.yaml new file mode 100644 index 00000000000..11a29093daa --- /dev/null +++ b/policyengine_us/parameters/gov/states/vt/dcf/liheap/payment/nominal_benefit.yaml @@ -0,0 +1,13 @@ +description: Vermont provides this nominal annual benefit to households in Section 8 or other public housing whose heat is included in rent under the Low Income Home Energy Assistance Program. +values: + 2024-06-22: 21 + +metadata: + unit: currency-USD + period: year + label: Vermont LIHEAP nominal benefit + reference: + - title: Vermont Seasonal Fuel Assistance Rule 2914(c)(4) + href: https://outside.vermont.gov/dept/DCF/Shared%20Documents/ESD/Rules/2900-Seasonal-Fuel-Assistance.pdf#page=41 + - title: 33 V.S.A. § 2605(f) + href: https://legislature.vermont.gov/statutes/section/33/026/02605 diff --git a/policyengine_us/parameters/gov/states/vt/dcf/liheap/payment/payment_rate.yaml b/policyengine_us/parameters/gov/states/vt/dcf/liheap/payment/payment_rate.yaml new file mode 100644 index 00000000000..746c73a7c0c --- /dev/null +++ b/policyengine_us/parameters/gov/states/vt/dcf/liheap/payment/payment_rate.yaml @@ -0,0 +1,32 @@ +description: Vermont pays this share of the base benefit as the ratable benefit under the Low Income Home Energy Assistance Program. +# ESTIMATE. DCF sets the payment rate by procedure each season (33 V.S.A. +# 2605(c); rule 2914(a)(8)) and does not publish it: P-2914B sends staff to the +# ACCESS system for the current rate. 0.51 is inferred from the FFY2026 plan's +# $2,089 maximum benefit, which equals floor(4,552 x 0.90 x 0.51), the largest +# standard heating cost times the top heat cost percentage; 51% is the only +# whole percent that reproduces it. P-2914D's worked example uses 29% for +# training only. DCF had not set the 2026-27 rate by September 16, 2026, and on +# October 23, 2026 the Emergency Board funded a larger 2026-27 benefit for oil +# and kerosene households (about 62 percent of estimated heating need, against +# about 45 percent in 2025-26) without publishing a rate, so 0.51 carries +# forward as an unverified estimate. +values: + 2025-10-01: 0.51 + +metadata: + unit: /1 + period: year + label: Vermont LIHEAP payment rate + reference: + - title: Vermont FY2026 LIHEAP State Plan, Section 2.6 + href: https://liheapch.acf.gov/docs/2026/state-plans/VT_Plan_2026.pdf#page=10 + - title: Vermont DCF Procedure P-2914B, Payment Rate + href: https://outside.vermont.gov/dept/DCF/Policies%20Procedures%20Guidance/ESD-Procedure-P2914B.pdf + - title: 33 V.S.A. § 2605(c) + href: https://legislature.vermont.gov/statutes/section/33/026/02605 + - title: Vermont Seasonal Fuel Assistance Rule 2914(a)(8) + href: https://outside.vermont.gov/dept/DCF/Shared%20Documents/ESD/Rules/2900-Seasonal-Fuel-Assistance.pdf#page=39 + - title: Vermont DCF memo to the Joint Fiscal Committee, Fuel Assistance Update, September 16, 2026 + href: https://ljfo.vermont.gov/assets/Meetings/Joint-Fiscal-Committee/2026-09-16/5a1aec4660/2026.09.16.-LIHEAP-Funds.-JFC.pdf#page=1 + - title: "Vermont Emergency Board materials, October 23, 2026, Item 1, LIHEAP Fuel Proposal #page 3-5" + href: https://aoa.vermont.gov/sites/aoa/files/2026-10-23%20E%20Board%20BINDER%20-%20Final.pdf#page=3 diff --git a/policyengine_us/parameters/gov/states/vt/dcf/liheap/payment/subsidized_rate.yaml b/policyengine_us/parameters/gov/states/vt/dcf/liheap/payment/subsidized_rate.yaml new file mode 100644 index 00000000000..22d714c154d --- /dev/null +++ b/policyengine_us/parameters/gov/states/vt/dcf/liheap/payment/subsidized_rate.yaml @@ -0,0 +1,18 @@ +description: Vermont pays this share of the calculated benefit to households in Section 8 or other public housing that pay a fuel supplier directly under the Low Income Home Energy Assistance Program. +# 33 V.S.A. 2604(c)(1) deems residents of subsidized units to have no heating +# cost except to the extent federal law requires when federal funds are used, +# and 33 V.S.A. 2605(f) sets $21 for subsidized heat in rent. Rule 2914(c)(3)-(4) +# carries these out: 55% for subsidized households paying a supplier and the $21 +# nominal benefit for subsidized heat in rent. +values: + 2024-06-22: 0.55 + +metadata: + unit: /1 + period: year + label: Vermont LIHEAP subsidized housing benefit rate + reference: + - title: Vermont Seasonal Fuel Assistance Rule 2914(c)(3) + href: https://outside.vermont.gov/dept/DCF/Shared%20Documents/ESD/Rules/2900-Seasonal-Fuel-Assistance.pdf#page=41 + - title: 33 V.S.A. § 2604(c)(1) + href: https://legislature.vermont.gov/statutes/section/33/026/02604 diff --git a/policyengine_us/programs.yaml b/policyengine_us/programs.yaml index 03a1c81c7c0..e40ffda4fcf 100644 --- a/policyengine_us/programs.yaml +++ b/policyengine_us/programs.yaml @@ -987,7 +987,7 @@ programs: category: Benefits agency: HHS status: partial - coverage: AK, AR, AL, AZ, DC, GA, Riverside County, MA, FL, IL, IA, IN, KS, KY, MD, MN, MS, NC, ND, NE, NH, NJ, NM, NV, NY, OH, OK, OR, SD, RI, TN, TX, UT, VA, WA, WI, WY + coverage: AK, AR, AL, AZ, DC, GA, Riverside County, MA, FL, IL, IA, IN, KS, KY, MD, MN, MS, NC, ND, NE, NH, NJ, NM, NV, NY, OH, OK, OR, SD, RI, TN, TX, UT, VA, VT, WA, WI, WY state_implementations: - state: AL status: partial @@ -1242,6 +1242,14 @@ programs: parameter_prefix: gov.states.va.dss.eap verified_years: "2026" notes: FFY2026 Fuel Assistance only, using the latest published (FFY2025) value per point and placeholder fuel-type and energy-burden points until VDSS publishes its survey. Crisis, cooling and weatherization assistance are out of scope. + - state: VT + status: partial + name: Vermont Seasonal Fuel Assistance + full_name: Vermont Seasonal Fuel Assistance Program + variable: vt_liheap + parameter_prefix: gov.states.vt.dcf.liheap + verified_years: "2026" + notes: FY2026 regular heating with an estimated 51% payment rate, which DCF does not publish; later years carry it forward, so they omit the larger 2026-27 oil and kerosene benefit. Late-application proration of the regular benefit is not modeled; crisis, Heat and Eat and weatherization are out of scope. - state: WA status: partial name: Washington LIHEAP diff --git a/policyengine_us/tests/policy/baseline/gov/states/vt/dcf/liheap/eligibility/vt_liheap_eligible.yaml b/policyengine_us/tests/policy/baseline/gov/states/vt/dcf/liheap/eligibility/vt_liheap_eligible.yaml new file mode 100644 index 00000000000..eb35d1647a0 --- /dev/null +++ b/policyengine_us/tests/policy/baseline/gov/states/vt/dcf/liheap/eligibility/vt_liheap_eligible.yaml @@ -0,0 +1,185 @@ +# Eligibility: at least one citizen or qualified immigrant member (rule 2903(a)(2)), +# gross income no greater than the income limit (33 V.S.A. 2604(a); rule 2913(b)), and +# a primary heating fuel listed in the Standard Heating Cost Tables (rule 2916(b)), +# unless a subsidized household has heat included in its rent (rule 2914(c)(4)). +# Rules, "#page 10,38-39,41,43": +# https://outside.vermont.gov/dept/DCF/Shared%20Documents/ESD/Rules/2900-Seasonal-Fuel-Assistance.pdf#page=10 +# Statute: https://legislature.vermont.gov/statutes/section/33/026/02604 +# 2026 one-person limit: P-2913 prints 2,412 a month (28,944 a year), compared with +# monthly income (rule 2914(a)(3); vt_liheap_income_limit.yaml Case 1). Where the +# printed amount rounds above 185% of the annual guideline, the statute's 185% applies +# (two people: 3,262 a month printed, 39,127.50 a year under 33 V.S.A. 2604(a)). +# P-2913: https://outside.vermont.gov/dept/DCF/Policies%20Procedures%20Guidance/ESD-Procedure-P2913.pdf + +- name: Case 1, threshold edge, income at the printed monthly limit is eligible. + period: 2026 + input: + people: + head: + age: 40 + # Threshold edge: 2,412 a month x 12 = 28,944. + employment_income: 28_944 + spm_units: + spm_unit: + members: [head] + heating_type: FUEL_OIL + households: + household: + members: [head] + state_code: VT + bedrooms: 2 + output: + # 2,412 a month is not more than P-2913's 2,412; oil has a table cost (3,123). + vt_liheap_eligible: true + +- name: Case 2, threshold edge, income $1 a month above the printed limit is not eligible. + period: 2026 + input: + people: + head: + age: 40 + # Threshold edge: 2,413 a month x 12 = 28,956. + employment_income: 28_956 + spm_units: + spm_unit: + members: [head] + heating_type: FUEL_OIL + households: + household: + members: [head] + state_code: VT + bedrooms: 2 + output: + # 2,413 a month is more than P-2913's 2,412. + vt_liheap_eligible: false + +- name: Case 3, a household with no citizen or qualified immigrant is not eligible. + period: 2026 + input: + people: + head: + age: 34 + immigration_status: UNDOCUMENTED + # 14.42 x 30 hours x 52 weeks = 22,495.20. + employment_income: 22_495.2 + spm_units: + spm_unit: + members: [head] + heating_type: FUEL_OIL + households: + household: + members: [head] + state_code: VT + bedrooms: 2 + output: + # No eligible member: size 0, limit 0, not eligible. + vt_liheap_household_size: 0 + vt_liheap_income_limit: 0 + vt_liheap_eligible: false + +- name: Case 4, an unsubsidized renter with heat in rent and an unknown fuel is not eligible. + period: 2026 + input: + people: + head: + age: 70 + # Social Security of 1,300 a month. + social_security_retirement: 15_600 + ssi: 0 + spm_units: + spm_unit: + members: [head] + heating_type: UNSPECIFIED + heat_expense_included_in_rent: true + receives_housing_assistance: false + households: + household: + members: [head] + state_code: VT + bedrooms: 1 + is_in_public_housing: false + output: + # No table row for an unknown fuel (cost 0), and the 30% heat-in-rent benefit + # needs the table cost; only subsidized heat in rent pays without a fuel. + vt_liheap_heating_cost: 0 + vt_liheap_eligible: false + +- name: Case 5, a public housing tenant paying for a fuel outside the table is not eligible. + period: 2026 + input: + people: + head: + age: 70 + social_security_retirement: 15_600 + ssi: 0 + spm_units: + spm_unit: + members: [head] + heating_type: OTHER + heat_expense_included_in_rent: false + households: + household: + members: [head] + state_code: VT + bedrooms: 1 + is_in_public_housing: true + output: + # Subsidized housing alone does not qualify: the fuel has no table row and heat + # is not included in rent. + vt_liheap_heating_cost: 0 + vt_liheap_eligible: false + +- name: Case 6, a Section 8 tenant reporting no heating is not eligible even with heat in rent. + period: 2026 + input: + people: + head: + age: 70 + social_security_retirement: 15_600 + ssi: 0 + spm_units: + spm_unit: + members: [head] + heating_type: NONE + heat_expense_included_in_rent: true + receives_housing_assistance: true + households: + household: + members: [head] + state_code: VT + bedrooms: 1 + output: + # NONE means the home has no heating, so the $21 + # subsidized heat-in-rent path does not apply. With UNSPECIFIED instead, the + # household is eligible (vt_liheap.yaml Case 4). + vt_liheap_eligible: false + +- name: Case 7, two people under the printed monthly limit but above 185 percent of the annual guideline are not eligible. + period: 2026 + absolute_error_margin: 0.01 + input: + people: + head: + age: 28 + # Threshold edge: 3,261 a month x 12 = 39,132. + employment_income: 39_132 + child1: + age: 4 + spm_units: + spm_unit: + members: [head, child1] + # No Reach Up. + tanf: 0 + heating_type: FUEL_OIL + households: + household: + members: [head, child1] + state_code: VT + bedrooms: 2 + output: + # P-2913 prints 3,262 a month for two people (21,150 / 12 = 1,762.50, so 1,763; + # 1.85 x 1,763 = 3,261.55, so 3,262), and 3,261 a month is under it. 33 V.S.A. + # 2604(a): 1.85 x 21,150 = 39,127.50 a year (3,260.625 a month). The limit is + # min(39,144, 39,127.50, 53,714) = 39,127.50, and 39,132 > 39,127.50. + vt_liheap_income_limit: 39_127.5 + vt_liheap_eligible: false diff --git a/policyengine_us/tests/policy/baseline/gov/states/vt/dcf/liheap/eligibility/vt_liheap_income_limit.yaml b/policyengine_us/tests/policy/baseline/gov/states/vt/dcf/liheap/eligibility/vt_liheap_income_limit.yaml new file mode 100644 index 00000000000..3fd7bd34321 --- /dev/null +++ b/policyengine_us/tests/policy/baseline/gov/states/vt/dcf/liheap/eligibility/vt_liheap_income_limit.yaml @@ -0,0 +1,133 @@ +# Income limit = min(12 x the P-2913 monthly 185% amount, 185% of the annual guideline, +# LIHEAP maximum), where the LIHEAP maximum is the greater of 150% of the guideline and +# 60% of state median income. The statute's exact 185% caps the printed amount where +# that rounds above it. +# - P-2913 monthly amount: 185% of the whole-dollar monthly guideline (vt_liheap_fpg.yaml), +# rounded half up to the dollar (sizes 1-15 printed): +# https://outside.vermont.gov/dept/DCF/Policies%20Procedures%20Guidance/ESD-Procedure-P2913.pdf +# - 33 V.S.A. 2604(a): "gross household income no greater than 185 percent of the +# federal poverty level nor in excess of income maximums established by LIHEAP": +# https://legislature.vermont.gov/statutes/section/33/026/02604 +# - Rule 2913(b) repeats both tests: +# https://outside.vermont.gov/dept/DCF/Shared%20Documents/ESD/Rules/2900-Seasonal-Fuel-Assistance.pdf#page=38 +# - LIHEAP maximum, 42 U.S.C. 8624(b)(2)(B): https://www.law.cornell.edu/uscode/text/42/8624 +# Guidelines: 2026 uses the 2025 guideline (15,650 + 5,500 per additional person; P-2913, +# effective March 1, 2025); 2027 uses the 2026 guideline (15,960 + 5,680). +# Vermont four-person SMI: 131,654 (FFY2026) and 135,326 (FFY2027). 60% SMI is +# floor(floor(SMI x 0.6) x size factor), where the size factor is 0.52 + 0.16 per person +# for persons two to six, plus 0.03 per person above six (45 CFR 96.85(b)). +# The P-2913 table (sizes 10-15) and the DCF 2026-27 web table print monthly 185% +# amounts above 60% SMI. These cases follow the statute. + +- name: Case 1, one person in 2026 is limited to the P-2913 monthly 185% amount. + period: 2026 + absolute_error_margin: 0.01 + input: + people: + head: + age: 40 + spm_units: + spm_unit: + members: [head] + households: + household: + members: [head] + state_code: VT + output: + # P-2913 prints 2,412 a month (15,650 / 12 = 1,304.17, so 1,304; 1.85 x 1,304 = + # 2,412.40, so 2,412): 2,412 x 12 = 28,944, below 185% of the annual guideline + # (1.85 x 15,650 = 28,952.50). LIHEAP maximum = max(150% x 15,650 = 23,475, + # floor(floor(131,654 x 0.6) x 0.52) = floor(78,992 x 0.52) = 41,075) = 41,075. + # min(28,944, 28,952.50, 41,075) = 28,944. + vt_liheap_income_limit: 28_944 + +- name: Case 2, ten people in 2026 are limited by 60 percent of state median income. + period: 2026 + absolute_error_margin: 0.01 + input: + people: + head: {age: 42} + spouse: {age: 40} + child1: {age: 17} + child2: {age: 15} + child3: {age: 14} + child4: {age: 12} + child5: {age: 10} + child6: {age: 8} + child7: {age: 5} + child8: {age: 2} + spm_units: + spm_unit: + members: [head, spouse, child1, child2, child3, child4, child5, child6, child7, child8] + households: + household: + members: [head, spouse, child1, child2, child3, child4, child5, child6, child7, child8] + state_code: VT + output: + # P-2913 prints 10,044 a month: 10,044 x 12 = 120,528. Guideline 15,650 + 9 x 5,500 + # = 65,150; 185% = 120,527.50; 150% = 97,725. Size factor 0.52 + 5 x 0.16 + 4 x 0.03 + # = 1.44; 60% SMI = floor(78,992 x 1.44) = floor(113,748.48) = 113,748. LIHEAP + # maximum = 113,748. min(120,528, 120,527.50, 113,748) = 113,748. + vt_liheap_income_limit: 113_748 + +- name: Case 3, thirteen people in 2026 are limited by 150 percent of the guideline. + period: 2026 + absolute_error_margin: 0.01 + input: + people: + head: {age: 45} + spouse: {age: 43} + child1: {age: 19} + child2: {age: 17} + child3: {age: 16} + child4: {age: 15} + child5: {age: 13} + child6: {age: 12} + child7: {age: 10} + child8: {age: 8} + child9: {age: 6} + child10: {age: 4} + child11: {age: 1} + spm_units: + spm_unit: + members: [head, spouse, child1, child2, child3, child4, child5, child6, child7, child8, child9, child10, child11] + households: + household: + members: [head, spouse, child1, child2, child3, child4, child5, child6, child7, child8, child9, child10, child11] + state_code: VT + output: + # P-2913 prints 12,586 a month: 12,586 x 12 = 151,032. Guideline 15,650 + 12 x + # 5,500 = 81,650; 185% = 151,052.50; 150% = 122,475. Size factor 0.52 + 5 x 0.16 + + # 7 x 0.03 = 1.53; 60% SMI = floor(78,992 x 1.53) = floor(120,857.76) = 120,857. + # LIHEAP maximum = max(122,475, 120,857) = 122,475. + # min(151,032, 151,052.50, 122,475) = 122,475. + vt_liheap_income_limit: 122_475 + +- name: Case 4, ten people in 2027 use the 2026 guideline and the FFY2027 median income. + period: 2027 + absolute_error_margin: 0.01 + input: + people: + head: {age: 42} + spouse: {age: 40} + child1: {age: 17} + child2: {age: 15} + child3: {age: 14} + child4: {age: 12} + child5: {age: 10} + child6: {age: 8} + child7: {age: 5} + child8: {age: 2} + spm_units: + spm_unit: + members: [head, spouse, child1, child2, child3, child4, child5, child6, child7, child8] + households: + household: + members: [head, spouse, child1, child2, child3, child4, child5, child6, child7, child8] + state_code: VT + output: + # The DCF 2026-27 table prints 10,340 a month: 10,340 x 12 = 124,080. Guideline + # 15,960 + 9 x 5,680 = 67,080; 185% = 124,098; 150% = 100,620. 60% SMI = + # floor(floor(135,326 x 0.6) x 1.44) = floor(81,195 x 1.44) = floor(116,920.80) = + # 116,920. LIHEAP maximum = 116,920. min(124,080, 124,098, 116,920) = 116,920. + vt_liheap_income_limit: 116_920 diff --git a/policyengine_us/tests/policy/baseline/gov/states/vt/dcf/liheap/integration.yaml b/policyengine_us/tests/policy/baseline/gov/states/vt/dcf/liheap/integration.yaml new file mode 100644 index 00000000000..39f2716908a --- /dev/null +++ b/policyengine_us/tests/policy/baseline/gov/states/vt/dcf/liheap/integration.yaml @@ -0,0 +1,211 @@ +# End-to-end Vermont Seasonal Fuel Assistance cases. Sources: +# - Seasonal Fuel Assistance Rules 2900-2927 (adopted 06/22/2024): household (2903, +# 2907), gross income (2910-2912), income maximum (2913), benefit calculation (2914), +# heat cost percentage (2915) and standard heating costs (2916), "#page 10,16,25-43": +# https://outside.vermont.gov/dept/DCF/Shared%20Documents/ESD/Rules/2900-Seasonal-Fuel-Assistance.pdf#page=10 +# - 33 V.S.A. 2604(a) (185% and the LIHEAP maximum): +# https://legislature.vermont.gov/statutes/section/33/026/02604 +# - P-2915 heat cost percentages and P-2916 standard heating costs (2025-26 season): +# https://outside.vermont.gov/dept/DCF/Policies%20Procedures%20Guidance/ESD-Procedure-P2915.pdf +# https://outside.vermont.gov/dept/DCF/Policies%20Procedures%20Guidance/ESD-Procedure-P2916.pdf +# - 2026-27 heat cost percentages and standard heating costs (unchanged), "#page 1-3": +# https://humanservices.vermont.gov/sites/ahsnew/files/documents/2026-2027%20Benefits%20Matrx.pdf#page=1 +# Guidelines: 2026 uses 15,650 + 5,500 per additional person; 2027 uses 15,960 + 5,680. +# Income limits and percentage-of-poverty bands are monthly whole dollars (P-2913, +# P-2914A "#page 1-3", and the DCF 2026-27 income guidelines), read against annual +# income / 12: +# https://outside.vermont.gov/dept/DCF/Policies%20Procedures%20Guidance/ESD-Procedure-P2913.pdf +# https://outside.vermont.gov/dept/DCF/Policies%20Procedures%20Guidance/ESD-Procedure-P2914A.pdf#page=1 +# https://dcf.vermont.gov/esd/fuel/income-guidelines +# Payment rate 0.51 is an estimate (FFY2026 plan maximum $2,089), carried into 2027. +# Wages use the 2026 Vermont minimum wage, $14.42 an hour (Vermont Department of +# Labor, indexed under 21 V.S.A. 384(a)). + +# A 74-year-old living alone in an owned house, heated with fuel oil, with +# Social Security of 1,400 a month (16,800 a year), above the SSI benefit rate. +- name: Case 1, an older homeowner on Social Security heating with oil. + period: 2026 + absolute_error_margin: 0.01 + input: + people: + head: + age: 74 + social_security_retirement: 16_800 + ssi: 0 + spm_units: + spm_unit: + members: [head] + heating_type: FUEL_OIL + vt_liheap_dwelling_type: SINGLE_FAMILY_OR_MOBILE_HOME + heat_expense_included_in_rent: false + receives_housing_assistance: false + households: + household: + members: [head] + state_code: VT + bedrooms: 2 + is_in_public_housing: false + output: + vt_liheap_household_size: 1 + # Gross Social Security counts: 16,800. + vt_liheap_income: 16_800 + # P-2913 size 1: 2,412 a month = 28,944; 185% x 15,650 = 28,952.50; + # min(28,944, 28,952.50, max(23,475, 41,075)) = 28,944. + vt_liheap_income_limit: 28_944 + # 1,400 a month, in P-2914A size 1 "105% To 114%" ($1,358-$1,487): 78%. + vt_liheap_heat_cost_percentage: 0.78 + # Single-family, oil, 2 bedrooms. + vt_liheap_heating_cost: 3_123 + vt_liheap_eligible: true + # 3,123 x 0.78 = 2,435.94; x 0.51 = 1,242.3294; x 100% (pays the supplier); floor = 1,242. + vt_liheap: 1_242 + +# Two working parents (one full time, one 20 hours a week at the minimum wage) with +# children aged 7 and 4, renting a two-bedroom apartment whose gas heat is included in +# the rent, with no housing subsidy and no Reach Up. +- name: Case 2, a working family of four with heat included in an unsubsidized rent. + period: 2026 + absolute_error_margin: 0.01 + input: + people: + head: + age: 35 + # 14.42 x 40 hours x 52 weeks = 29,993.60. + employment_income: 29_993.6 + spouse: + age: 33 + # 14.42 x 20 hours x 52 weeks = 14,996.80. + employment_income: 14_996.8 + child1: + age: 7 + child2: + age: 4 + spm_units: + spm_unit: + members: [head, spouse, child1, child2] + tanf: 0 + heating_type: NATURAL_GAS + vt_liheap_dwelling_type: MULTI_FAMILY + heat_expense_included_in_rent: true + receives_housing_assistance: false + households: + household: + members: [head, spouse, child1, child2] + state_code: VT + bedrooms: 2 + is_in_public_housing: false + output: + vt_liheap_household_size: 4 + # 29,993.60 + 14,996.80 = 44,990.40. + vt_liheap_income: 44_990.4 + # P-2913 size 4: 4,956 a month = 59,472. Guideline 15,650 + 3 x 5,500 = 32,150; + # 185% = 59,477.50; LIHEAP maximum max(48,225, floor(78,992 x 1.00) = 78,992) = + # 78,992. Limit min(59,472, 59,477.50, 78,992) = 59,472. + vt_liheap_income_limit: 59_472 + # 44,990.40 / 12 = 3,749.20 a month, in P-2914A size 4 "135% To 144%" + # ($3,591-$3,858): 69%. + vt_liheap_heat_cost_percentage: 0.69 + # Multi-family, natural gas, 2 bedrooms. + vt_liheap_heating_cost: 1_148 + vt_liheap_eligible: true + # 1,148 x 0.69 = 792.12; x 0.51 = 403.9812; x 0.30 (heat in rent) = 121.19436, + # above the $50 minimum; floor = 121. + vt_liheap: 121 + +# A single parent working 30 hours a week at the minimum wage, receiving 100 a month +# in child support, with children aged 6 and 2. The family rents a three-bedroom house with a +# Section 8 voucher and buys propane from a dealer. +- name: Case 3, a family with a Section 8 voucher buying propane receives 55 percent. + period: 2026 + absolute_error_margin: 0.01 + input: + people: + head: + age: 29 + # 14.42 x 30 hours x 52 weeks = 22,495.20. + employment_income: 22_495.2 + child_support_received: 1_200 + child1: + age: 6 + child2: + age: 2 + spm_units: + spm_unit: + members: [head, child1, child2] + tanf: 0 + heating_type: PROPANE + vt_liheap_dwelling_type: SINGLE_FAMILY_OR_MOBILE_HOME + heat_expense_included_in_rent: false + receives_housing_assistance: true + households: + household: + members: [head, child1, child2] + state_code: VT + bedrooms: 3 + is_in_public_housing: false + output: + vt_liheap_household_size: 3 + # 22,495.20 + 1,200 = 23,695.20. + vt_liheap_income: 23_695.2 + # P-2913 size 3: 4,109 a month = 49,308. Guideline 15,650 + 2 x 5,500 = 26,650; + # 185% = 49,302.50 caps the printed amount; LIHEAP maximum max(39,975, + # floor(78,992 x 0.84) = 66,353) = 66,353. Limit min(49,308, 49,302.50, 66,353) = + # 49,302.50. + vt_liheap_income_limit: 49_302.5 + # 23,695.20 / 12 = 1,974.60 a month, in P-2914A size 3 "85% To 94%" + # ($1,867-$2,088): 84%. + vt_liheap_heat_cost_percentage: 0.84 + # Single-family, propane, 3 bedrooms. + vt_liheap_heating_cost: 3_379 + vt_liheap_eligible: true + # 3,379 x 0.84 = 2,838.36; x 0.51 = 1,447.5636; x 0.55 (subsidized) = 796.15998; + # floor = 796. + vt_liheap: 796 + +# A retired couple (70 and 68) heating their three-bedroom house with wood pellets in +# the 2026-27 season. Social Security of 1,600 and 750 a month. +- name: Case 4, a retired couple heating with wood pellets in the 2026-27 season. + period: 2027 + absolute_error_margin: 0.01 + input: + people: + head: + age: 70 + social_security_retirement: 19_200 + ssi: 0 + spouse: + age: 68 + social_security_retirement: 9_000 + ssi: 0 + spm_units: + spm_unit: + members: [head, spouse] + heating_type: WOOD + vt_liheap_dwelling_type: SINGLE_FAMILY_OR_MOBILE_HOME + heat_expense_included_in_rent: false + receives_housing_assistance: false + households: + household: + members: [head, spouse] + state_code: VT + bedrooms: 3 + is_in_public_housing: false + output: + vt_liheap_household_size: 2 + # 19,200 + 9,000 = 28,200. + vt_liheap_income: 28_200 + # The DCF 2026-27 table prints 3,336 a month = 40,032. 2026 guideline 15,960 + + # 5,680 = 21,640; 185% = 40,034; LIHEAP maximum max(32,460, floor(floor(135,326 x + # 0.6) x 0.68) = floor(81,195 x 0.68) = 55,212) = 55,212. Limit min(40,032, 40,034, + # 55,212) = 40,032. + vt_liheap_income_limit: 40_032 + # No 2026-27 P-2914A is published; the 2025-26 construction on the 2026 guideline: + # 21,640 / 12 = 1,803.33, so 1,803. "125% To 134%" runs from ceil(1.24 x 1,803) + 1 + # = ceil(2,235.72) + 1 = 2,237 to ceil(1.34 x 1,803) = ceil(2,416.02) = 2,417. + # 28,200 / 12 = 2,350 a month: 72%. + vt_liheap_heat_cost_percentage: 0.72 + # 2026-27 table (unchanged): single-family, firewood, wood pellets and coal, 3 bedrooms. + vt_liheap_heating_cost: 1_658 + vt_liheap_eligible: true + # 1,658 x 0.72 = 1,193.76; x 0.51 = 608.8176; x 100%; floor = 608. + vt_liheap: 608 diff --git a/policyengine_us/tests/policy/baseline/gov/states/vt/dcf/liheap/payment/vt_liheap_heat_cost_percentage.yaml b/policyengine_us/tests/policy/baseline/gov/states/vt/dcf/liheap/payment/vt_liheap_heat_cost_percentage.yaml new file mode 100644 index 00000000000..9e48a4cf9ee --- /dev/null +++ b/policyengine_us/tests/policy/baseline/gov/states/vt/dcf/liheap/payment/vt_liheap_heat_cost_percentage.yaml @@ -0,0 +1,143 @@ +# Heat cost percentage by income as a percentage of poverty (rule 2915(d); P-2915): +# 0-74% 90; 75-84 87; 85-94 84; 95-104 81; 105-114 78; 115-124 75; 125-134 72; +# 135-144 69; 145-154 66; 155-164 33; 165-174 30; 175-185 27. +# https://outside.vermont.gov/dept/DCF/Policies%20Procedures%20Guidance/ESD-Procedure-P2915.pdf +# Percentage of poverty (rule 2914(a)(4); P-2914A, 2025-26 table, "#page 1-3"): +# https://outside.vermont.gov/dept/DCF/Policies%20Procedures%20Guidance/ESD-Procedure-P2914A.pdf#page=1 +# Monthly income (annual / 12) is read against the printed P-2914A monthly dollar bands; +# income at or below a band's top stays in that band. Size 1 tops: 965, 1,096, 1,226, +# 1,357, 1,487, 1,617, 1,748, 1,878, 2,009, 2,139, 2,269, 2,412. Size 2 first band: +# 0 to 1,305; second band: 1,306 to 1,481. +# Wages use the 2026 Vermont minimum wage, $14.42 an hour (Vermont Department of +# Labor, indexed under 21 V.S.A. 384(a)). + +- name: Case 1, a person with no income gets 90 percent. + period: 2026 + absolute_error_margin: 0.001 + input: + people: + head: + age: 30 + spm_units: + spm_unit: + members: [head] + households: + household: + members: [head] + state_code: VT + output: + # 0 a month, in "0 To <75%" ($0-$965): 90%. + vt_liheap_heat_cost_percentage: 0.9 + +- name: Case 2, threshold edge, income at the printed top of the first band stays at 90 percent. + period: 2026 + absolute_error_margin: 0.001 + input: + people: + head: + age: 28 + # Threshold edge: 1,305 a month x 12 = 15,660. + employment_income: 15_660 + child1: + age: 4 + spm_units: + spm_unit: + members: [head, child1] + # No Reach Up. + tanf: 0 + households: + household: + members: [head, child1] + state_code: VT + output: + # P-2914A size 2: 1,305 a month is in "0 To <75%" ($0-$1,305): 90%. The top is + # ceil(0.74 x 1,763) = ceil(1,304.62), with 21,150 / 12 = 1,762.50 rounded half up; + # 15,660 is 74.04% of the annual guideline, but the printed top governs. + vt_liheap_heat_cost_percentage: 0.9 + +- name: Case 3, threshold edge, income $1 a month above the first band's printed top gets 87 percent. + period: 2026 + absolute_error_margin: 0.001 + input: + people: + head: + age: 28 + # Threshold edge: 1,306 a month x 12 = 15,672. + employment_income: 15_672 + child1: + age: 4 + spm_units: + spm_unit: + members: [head, child1] + # No Reach Up. + tanf: 0 + households: + household: + members: [head, child1] + state_code: VT + output: + # P-2914A size 2: 1,306 a month is in "75% To 84%" ($1,306-$1,481): 87%. + vt_liheap_heat_cost_percentage: 0.87 + +- name: Case 4, a retiree at 146 percent of poverty gets 66 percent. + period: 2026 + absolute_error_margin: 0.001 + input: + people: + head: + age: 70 + # Social Security of 1,900 a month. + social_security_retirement: 22_800 + # Social Security is above the SSI federal benefit rate. + ssi: 0 + spm_units: + spm_unit: + members: [head] + households: + household: + members: [head] + state_code: VT + output: + # 1,900 a month, in "145% To 154%" ($1,879-$2,009): 66%. + vt_liheap_heat_cost_percentage: 0.66 + +- name: Case 5, a retiree at 157 percent of poverty drops to 33 percent. + period: 2026 + absolute_error_margin: 0.001 + input: + people: + head: + age: 72 + # Social Security of 2,050 a month. + social_security_retirement: 24_600 + ssi: 0 + spm_units: + spm_unit: + members: [head] + households: + household: + members: [head] + state_code: VT + output: + # 2,050 a month, in "155% To 164%" ($2,010-$2,139): 33% (down from 66%). + vt_liheap_heat_cost_percentage: 0.33 + +- name: Case 6, a worker at 180 percent of poverty gets 27 percent. + period: 2026 + absolute_error_margin: 0.001 + input: + people: + head: + age: 26 + # 14.42 x 37.5 hours x 52 weeks = 28,119. + employment_income: 28_119 + spm_units: + spm_unit: + members: [head] + households: + household: + members: [head] + state_code: VT + output: + # 28,119 / 12 = 2,343.25 a month, in "175% To 185%" ($2,270-$2,412): 27%. + vt_liheap_heat_cost_percentage: 0.27 diff --git a/policyengine_us/tests/policy/baseline/gov/states/vt/dcf/liheap/payment/vt_liheap_heating_cost.yaml b/policyengine_us/tests/policy/baseline/gov/states/vt/dcf/liheap/payment/vt_liheap_heating_cost.yaml new file mode 100644 index 00000000000..5791c8b0104 --- /dev/null +++ b/policyengine_us/tests/policy/baseline/gov/states/vt/dcf/liheap/payment/vt_liheap_heating_cost.yaml @@ -0,0 +1,240 @@ +# Standard heating costs come from the P-2916 Standard Heating Cost Tables for the +# 2025-26 heating season (procedure 7/1/25, Bulletin 25-16): +# https://outside.vermont.gov/dept/DCF/Policies%20Procedures%20Guidance/ESD-Procedure-P2916.pdf +# Rules 2914(a)(6) and 2916 (fuel type, housing type and bedrooms), "#page 39,43": +# https://outside.vermont.gov/dept/DCF/Shared%20Documents/ESD/Rules/2900-Seasonal-Fuel-Assistance.pdf#page=39 +# The table has rows for natural gas, propane, oil, kerosene and electricity, plus one +# "Firewood, Wood Pellets and Coal" row. Columns are 1, 2, 3 and 4+ bedrooms. Zero +# bedrooms (a studio) uses the smallest, 1-bedroom column (modeling assumption). SOLAR +# uses the electricity row: rule 2901(a)(14) makes the primary heating fuel the listed +# type providing the most heat, electricity for a grid-tied solar home with electric +# heat. OTHER, NONE and UNSPECIFIED have no row. + +- name: Case 1, natural gas in a two-bedroom home uses the single-family default. + period: 2026 + absolute_error_margin: 0.01 + input: + people: + head: + age: 45 + spm_units: + spm_unit: + members: [head] + heating_type: NATURAL_GAS + households: + household: + members: [head] + state_code: VT + bedrooms: 2 + output: + # vt_liheap_dwelling_type is not supplied, so the single-family and mobile home + # table applies: natural gas, 2 bedrooms = 1,454. + vt_liheap_heating_cost: 1_454 + +- name: Case 2, propane in a three-bedroom apartment matches the P-2914D example. + period: 2026 + absolute_error_margin: 0.01 + input: + people: + head: + age: 45 + spm_units: + spm_unit: + members: [head] + heating_type: PROPANE + vt_liheap_dwelling_type: MULTI_FAMILY + households: + household: + members: [head] + state_code: VT + bedrooms: 3 + output: + # Multi-family, propane, 3 bedrooms = 2,347 (also the P-2914D worked example). + vt_liheap_heating_cost: 2_347 + +- name: Case 3, oil in a five-bedroom house uses the four-or-more-bedroom column. + period: 2026 + absolute_error_margin: 0.01 + input: + people: + head: + age: 45 + spm_units: + spm_unit: + members: [head] + heating_type: FUEL_OIL + vt_liheap_dwelling_type: SINGLE_FAMILY_OR_MOBILE_HOME + households: + household: + members: [head] + state_code: VT + bedrooms: 5 + output: + # Single-family, oil, 4+ bedrooms = 4,552 (5 bedrooms falls in the 4+ column). + vt_liheap_heating_cost: 4_552 + +- name: Case 4, kerosene in a studio apartment uses the one-bedroom column. + period: 2026 + absolute_error_margin: 0.01 + input: + people: + head: + age: 45 + spm_units: + spm_unit: + members: [head] + heating_type: KEROSENE + vt_liheap_dwelling_type: MULTI_FAMILY + households: + household: + members: [head] + state_code: VT + bedrooms: 0 + output: + # Multi-family, kerosene, 0 bedrooms -> 1-bedroom column = 1,610. + vt_liheap_heating_cost: 1_610 + +- name: Case 5, electricity in a two-bedroom apartment. + period: 2026 + absolute_error_margin: 0.01 + input: + people: + head: + age: 45 + spm_units: + spm_unit: + members: [head] + heating_type: ELECTRICITY + vt_liheap_dwelling_type: MULTI_FAMILY + households: + household: + members: [head] + state_code: VT + bedrooms: 2 + output: + # Multi-family, electricity, 2 bedrooms = 1,060. + vt_liheap_heating_cost: 1_060 + +- name: Case 6, wood in a two-bedroom apartment uses the firewood, pellets and coal row. + period: 2026 + absolute_error_margin: 0.01 + input: + people: + head: + age: 45 + spm_units: + spm_unit: + members: [head] + heating_type: WOOD + vt_liheap_dwelling_type: MULTI_FAMILY + households: + household: + members: [head] + state_code: VT + bedrooms: 2 + output: + # Multi-family, firewood, wood pellets and coal, 2 bedrooms = 1,231. + vt_liheap_heating_cost: 1_231 + +- name: Case 7, coal in a four-bedroom house uses the same row as wood. + period: 2026 + absolute_error_margin: 0.01 + input: + people: + head: + age: 45 + spm_units: + spm_unit: + members: [head] + heating_type: COAL + vt_liheap_dwelling_type: SINGLE_FAMILY_OR_MOBILE_HOME + households: + household: + members: [head] + state_code: VT + bedrooms: 4 + output: + # Single-family, firewood, wood pellets and coal, 4+ bedrooms = 1,711. + vt_liheap_heating_cost: 1_711 + +- name: Case 8, a solar home uses the electricity row. + period: 2026 + absolute_error_margin: 0.01 + input: + people: + head: + age: 45 + spm_units: + spm_unit: + members: [head] + heating_type: SOLAR + vt_liheap_dwelling_type: SINGLE_FAMILY_OR_MOBILE_HOME + households: + household: + members: [head] + state_code: VT + bedrooms: 2 + output: + # A grid-tied solar home heats with electricity (rule 2901(a)(14)): + # single-family, electricity, 2 bedrooms = 1,785. + vt_liheap_heating_cost: 1_785 + +- name: Case 9, a fuel outside the table has no standard heating cost. + period: 2026 + absolute_error_margin: 0.01 + input: + people: + head: + age: 45 + spm_units: + spm_unit: + members: [head] + heating_type: OTHER + households: + household: + members: [head] + state_code: VT + bedrooms: 2 + output: + # OTHER has no row in P-2916: 0. + vt_liheap_heating_cost: 0 + +- name: Case 10, a home with no heating has no standard heating cost. + period: 2026 + absolute_error_margin: 0.01 + input: + people: + head: + age: 45 + spm_units: + spm_unit: + members: [head] + heating_type: NONE + households: + household: + members: [head] + state_code: VT + bedrooms: 2 + output: + # NONE has no row in P-2916: 0. + vt_liheap_heating_cost: 0 + +- name: Case 11, an unspecified fuel has no standard heating cost. + period: 2026 + absolute_error_margin: 0.01 + input: + people: + head: + age: 45 + spm_units: + spm_unit: + members: [head] + heating_type: UNSPECIFIED + households: + household: + members: [head] + state_code: VT + bedrooms: 2 + output: + # UNSPECIFIED has no row in P-2916: 0. + vt_liheap_heating_cost: 0 diff --git a/policyengine_us/tests/policy/baseline/gov/states/vt/dcf/liheap/vt_liheap.yaml b/policyengine_us/tests/policy/baseline/gov/states/vt/dcf/liheap/vt_liheap.yaml new file mode 100644 index 00000000000..23b7b802ddf --- /dev/null +++ b/policyengine_us/tests/policy/baseline/gov/states/vt/dcf/liheap/vt_liheap.yaml @@ -0,0 +1,174 @@ +# Benefit = floor(heat cost percentage x standard heating cost x payment rate x housing +# factor). Rule 2914(a)(7)-(8), (c) and (d), "#page 39-41": +# https://outside.vermont.gov/dept/DCF/Shared%20Documents/ESD/Rules/2900-Seasonal-Fuel-Assistance.pdf#page=39 +# Housing factor (rule 2914(c); 33 V.S.A. 2605(d)-(f)): 100% when not subsidized and +# paying a supplier; 30% with a $50 minimum when not subsidized and heat is in the rent; +# 55% when subsidized and paying a supplier; a $21 nominal benefit when subsidized and +# heat is in the rent. Round down to whole dollars (rule 2914(d)). +# Statute: https://legislature.vermont.gov/statutes/section/33/026/02605 +# Worked example structure (P-2914D): multi-family, 3 bedrooms, propane = +# 2,347; 2,347 x 0.87 = 2,041.89; housing factor 100%; cents dropped. +# https://outside.vermont.gov/dept/DCF/Policies%20Procedures%20Guidance/ESD-Procedure-P2914D.pdf +# Payment rate 0.51 (an estimate inferred from the FFY2026 plan maximum of $2,089 = +# floor(4,552 x 0.90 x 0.51); DCF does not publish the rate). +# Wages use the 2026 Vermont minimum wage, $14.42 an hour (Vermont Department of +# Labor, indexed under 21 V.S.A. 384(a)). + +- name: Case 1, an unsubsidized family buying propane follows the P-2914D example. + period: 2026 + absolute_error_margin: 0.01 + input: + people: + head: + age: 33 + # 14.42 x 28 hours x 52 weeks = 20,995.52. + employment_income: 20_995.52 + child1: + age: 9 + child2: + age: 6 + spm_units: + spm_unit: + members: [head, child1, child2] + # No Reach Up. + tanf: 0 + heating_type: PROPANE + vt_liheap_dwelling_type: MULTI_FAMILY + heat_expense_included_in_rent: false + receives_housing_assistance: false + households: + household: + members: [head, child1, child2] + state_code: VT + bedrooms: 3 + is_in_public_housing: false + output: + # 20,995.52 / 12 = 1,749.63 a month, in P-2914A size 3 "75% To 84%" + # ($1,645-$1,866): 87%. + vt_liheap_heat_cost_percentage: 0.87 + vt_liheap_heating_cost: 2_347 + vt_liheap_eligible: true + # 2,347 x 0.87 = 2,041.89; x 0.51 = 1,041.3639; x 100% = 1,041.3639; floor = 1,041. + vt_liheap: 1_041 + +- name: Case 2, a public housing tenant who pays for electric heat gets 55 percent. + period: 2026 + absolute_error_margin: 0.01 + input: + people: + head: + age: 69 + # Social Security of 1,150 a month. + social_security_retirement: 13_800 + # Social Security is above the SSI federal benefit rate. + ssi: 0 + spm_units: + spm_unit: + members: [head] + heating_type: ELECTRICITY + vt_liheap_dwelling_type: MULTI_FAMILY + heat_expense_included_in_rent: false + receives_housing_assistance: false + households: + household: + members: [head] + state_code: VT + bedrooms: 1 + is_in_public_housing: true + output: + # 1,150 a month, in P-2914A size 1 "85% To 94%" ($1,097-$1,226): 84%. + vt_liheap_heat_cost_percentage: 0.84 + # Multi-family, electricity, 1 bedroom. + vt_liheap_heating_cost: 967 + # 967 x 0.84 = 812.28; x 0.51 = 414.2628; x 0.55 = 227.84454; floor = 227. + vt_liheap: 227 + +- name: Case 3, an unsubsidized renter with heat in rent receives the $50 minimum. + period: 2026 + absolute_error_margin: 0.01 + input: + people: + head: + age: 26 + # 14.42 x 37.5 hours x 52 weeks = 28,119. + employment_income: 28_119 + spm_units: + spm_unit: + members: [head] + heating_type: ELECTRICITY + vt_liheap_dwelling_type: MULTI_FAMILY + heat_expense_included_in_rent: true + receives_housing_assistance: false + households: + household: + members: [head] + state_code: VT + bedrooms: 1 + is_in_public_housing: false + output: + # 28,119 / 12 = 2,343.25 a month, in P-2914A size 1 "175% To 185%" + # ($2,270-$2,412): 27%. + vt_liheap_heat_cost_percentage: 0.27 + vt_liheap_heating_cost: 967 + vt_liheap_eligible: true + # 967 x 0.27 = 261.09; x 0.51 = 133.1559; x 0.30 = 39.94677 < 50: minimum 50. + vt_liheap: 50 + +- name: Case 4, a Section 8 tenant with heat in rent receives $21 even with an unknown fuel. + period: 2026 + absolute_error_margin: 0.01 + input: + people: + head: + age: 78 + # Social Security of 1,300 a month. + social_security_retirement: 15_600 + ssi: 0 + spm_units: + spm_unit: + members: [head] + heating_type: UNSPECIFIED + vt_liheap_dwelling_type: MULTI_FAMILY + heat_expense_included_in_rent: true + receives_housing_assistance: true + households: + household: + members: [head] + state_code: VT + bedrooms: 1 + is_in_public_housing: false + output: + # The fuel is unknown, so there is no table cost. + vt_liheap_heating_cost: 0 + # 1,300 a month <= P-2913's 2,412, and subsidized heat in rent needs no fuel. + vt_liheap_eligible: true + # Subsidized with heat in rent: the $21 nominal benefit. + vt_liheap: 21 + +- name: Case 5, a full-time worker above the income limit receives nothing. + period: 2026 + absolute_error_margin: 0.01 + input: + people: + head: + age: 30 + # 14.42 x 40 hours x 52 weeks = 29,993.60. + employment_income: 29_993.6 + spm_units: + spm_unit: + members: [head] + heating_type: FUEL_OIL + vt_liheap_dwelling_type: SINGLE_FAMILY_OR_MOBILE_HOME + heat_expense_included_in_rent: false + receives_housing_assistance: false + households: + household: + members: [head] + state_code: VT + bedrooms: 2 + is_in_public_housing: false + output: + # 29,993.60 / 12 = 2,499.47 a month > P-2913's 2,412: not eligible, so no + # benefit even though the top band (27%) and the oil cost (3,123) are positive. + vt_liheap_eligible: false + vt_liheap: 0 diff --git a/policyengine_us/tests/policy/baseline/gov/states/vt/dcf/liheap/vt_liheap_fpg.yaml b/policyengine_us/tests/policy/baseline/gov/states/vt/dcf/liheap/vt_liheap_fpg.yaml new file mode 100644 index 00000000000..7902de6bd32 --- /dev/null +++ b/policyengine_us/tests/policy/baseline/gov/states/vt/dcf/liheap/vt_liheap_fpg.yaml @@ -0,0 +1,62 @@ +# Annual guideline = 12 x the whole-dollar monthly guideline behind P-2913 and P-2914A: +# the HHS guideline for sizes 1-8 divided by 12 and rounded half up, plus, for each +# person above eight, the additional-person amount divided by 12 and rounded half up. +# P-2913: https://outside.vermont.gov/dept/DCF/Policies%20Procedures%20Guidance/ESD-Procedure-P2913.pdf +# P-2914A, "#page 1-3": +# https://outside.vermont.gov/dept/DCF/Policies%20Procedures%20Guidance/ESD-Procedure-P2914A.pdf#page=1 +# 2025 guidelines (used for 2026): 15,650 for one person, 54,150 for eight, 5,500 for +# each additional person (90 FR 5917): +# https://www.govinfo.gov/content/pkg/FR-2025-01-17/pdf/2025-01377.pdf#page=1 + +- name: Case 1, a two-person monthly guideline ending in 50 cents rounds up. + period: 2026 + absolute_error_margin: 0.01 + input: + people: + head: + age: 28 + child1: + age: 4 + spm_units: + spm_unit: + members: [head, child1] + households: + household: + members: [head, child1] + state_code: VT + output: + # 21,150 / 12 = 1,762.50, rounded half up to 1,763 (P-2914A size 2 tops, e.g. + # ceil(0.74 x 1,763) = 1,305; P-2913 1.85 x 1,763 = 3,261.55, so 3,262). + # 1,763 x 12 = 21,156. + vt_liheap_fpg: 21_156 + +- name: Case 2, an eleven-person guideline adds rounded monthly amounts above eight people. + period: 2026 + absolute_error_margin: 0.01 + input: + people: + head: {age: 44} + spouse: {age: 42} + child1: {age: 17} + child2: {age: 16} + child3: {age: 14} + child4: {age: 12} + child5: {age: 10} + child6: {age: 8} + child7: {age: 6} + child8: {age: 4} + child9: {age: 1} + spm_units: + spm_unit: + members: [head, spouse, child1, child2, child3, child4, child5, child6, child7, child8, child9] + households: + household: + members: [head, spouse, child1, child2, child3, child4, child5, child6, child7, child8, child9] + state_code: VT + output: + # Eight people: 54,150 / 12 = 4,512.50, so 4,513. Each additional person: + # 5,500 / 12 = 458.33, so 458. 4,513 + 3 x 458 = 5,887 (P-2914A size 11 first top + # ceil(0.74 x 5,887) = ceil(4,356.38) = 4,357; P-2913 1.85 x 5,887 = 10,890.95, so + # 10,891). The size-11 guideline itself, 70,650 / 12 = 5,887.50, would round to + # 5,888 and a 4,358 top. 5,887 x 12 = 70,644. + vt_liheap_fpg: 70_644 diff --git a/policyengine_us/tests/policy/baseline/gov/states/vt/dcf/liheap/vt_liheap_household_size.yaml b/policyengine_us/tests/policy/baseline/gov/states/vt/dcf/liheap/vt_liheap_household_size.yaml new file mode 100644 index 00000000000..34aa8898fc4 --- /dev/null +++ b/policyengine_us/tests/policy/baseline/gov/states/vt/dcf/liheap/vt_liheap_household_size.yaml @@ -0,0 +1,49 @@ +# Household size counts members who are U.S. citizens, nationals or qualified +# immigrants (rules 2903(a)(2) and 2907). Gross income counts every member, +# regardless of immigration status (rule 2910(a)). Rules, "#page 10,16,25": +# https://outside.vermont.gov/dept/DCF/Shared%20Documents/ESD/Rules/2900-Seasonal-Fuel-Assistance.pdf#page=10 +# Guideline for 2026 (P-2913, effective March 1, 2025): the 2025 guideline, +# 15,650 + 5,500 per additional person. Wages use the 2026 Vermont minimum wage, +# $14.42 an hour (Vermont Department of Labor, indexed under 21 V.S.A. 384(a)). + +- name: Case 1, an undocumented spouse is not counted but the spouse's wages are. + period: 2026 + absolute_error_margin: 0.01 + input: + people: + head: + age: 38 + immigration_status: LEGAL_PERMANENT_RESIDENT + # 14.42 x 30 hours x 52 weeks = 22,495.20. + employment_income: 22_495.2 + spouse: + age: 36 + immigration_status: UNDOCUMENTED + # 14.42 x 12 hours x 52 weeks = 8,998.08. + employment_income: 8_998.08 + child1: + age: 10 + immigration_status: CITIZEN + spm_units: + spm_unit: + members: [head, spouse, child1] + # No Reach Up. + tanf: 0 + households: + household: + members: [head, spouse, child1] + state_code: VT + output: + # The qualified immigrant head and the citizen child count; the spouse does not. + vt_liheap_household_size: 2 + # Everyone's wages count: 22,495.20 + 8,998.08 = 31,493.28. + vt_liheap_income: 31_493.28 + # P-2913 size 2: 3,262 a month = 39,144. Size 2 guideline 15,650 + 5,500 = + # 21,150; 185% = 39,127.50 caps the printed amount; LIHEAP maximum max(150% = + # 31,725, 60% SMI floor(floor(131,654 x 0.6) x 0.68) = 53,714) = 53,714. + # Limit = min(39,144, 39,127.50, 53,714) = 39,127.50. + vt_liheap_income_limit: 39_127.5 + # 31,493.28 / 12 = 2,624.44 a month, in P-2914A size 2 "145% To 154%" + # ($2,540-$2,716): 66%. A size of 3 would be in "115% To 124%" ($2,533-$2,755): + # 75%. + vt_liheap_heat_cost_percentage: 0.66 diff --git a/policyengine_us/tests/policy/baseline/gov/states/vt/dcf/liheap/vt_liheap_income.yaml b/policyengine_us/tests/policy/baseline/gov/states/vt/dcf/liheap/vt_liheap_income.yaml new file mode 100644 index 00000000000..3ddc20a777c --- /dev/null +++ b/policyengine_us/tests/policy/baseline/gov/states/vt/dcf/liheap/vt_liheap_income.yaml @@ -0,0 +1,264 @@ +# Gross household income with no deductions (rules 2910 and 2911; P-2910: "Fuel has +# no deductions for income"). Earned income (2911.1), unearned income including Reach +# Up and child support (2911.2) and self-employment (2911.3) count. The earned income +# of a member under 18 who lives with a parent and attends elementary or high school +# at least half time is excluded (2912(a)(12)), including during vacation breaks. +# Rules, "#page 25-29,34": +# https://outside.vermont.gov/dept/DCF/Shared%20Documents/ESD/Rules/2900-Seasonal-Fuel-Assistance.pdf#page=25 +# Modeling assumption: each source is floored at zero per person, so a self-employment loss +# cannot offset wages. Reach Up is the SPM unit's tanf, counted once. +# Wages use the 2026 Vermont minimum wage, $14.42 an hour (Vermont Department of +# Labor, indexed under 21 V.S.A. 384(a)). + +- name: Case 1, wages, child support and Reach Up add up with no deductions. + period: 2026 + absolute_error_margin: 0.01 + input: + people: + head: + age: 31 + # 14.42 x 20 hours x 52 weeks = 14,996.80. + employment_income: 14_996.8 + # 200 a month. + child_support_received: 2_400 + child1: + age: 5 + child2: + age: 3 + spm_units: + spm_unit: + members: [head, child1, child2] + # Reach Up of 600 a month. + tanf: 7_200 + households: + household: + members: [head, child1, child2] + state_code: VT + output: + # 14,996.80 + 2,400 + 7,200 = 24,596.80. + vt_liheap_income: 24_596.8 + +- name: Case 2, a self-employment loss does not reduce wages. + period: 2026 + absolute_error_margin: 0.01 + input: + people: + head: + age: 47 + # 14.42 x 25 hours x 52 weeks = 18,746. + employment_income: 18_746 + # A small craft business that lost money. + self_employment_income: -4_200 + spm_units: + spm_unit: + members: [head] + households: + household: + members: [head] + state_code: VT + output: + # Wages 18,746 + max(-4,200, 0) = 18,746, not 14,546. + vt_liheap_income: 18_746 + +- name: Case 3, a 16-year-old student's earnings are excluded but older or out-of-school teens' count. + period: 2026 + absolute_error_margin: 0.01 + input: + people: + head: + age: 44 + # 14.42 x 40 hours x 52 weeks = 29,993.60. + employment_income: 29_993.6 + child1: + age: 16 + is_in_k12_school: true + is_tax_unit_dependent: true + # Summer job: 14.42 x 15 hours x 10 weeks = 2,163. + employment_income: 2_163 + child2: + age: 17 + is_in_k12_school: false + is_tax_unit_dependent: true + # Left school and works 14.42 x 30 hours x 52 weeks = 22,495.20. + employment_income: 22_495.2 + child3: + age: 18 + is_in_k12_school: true + is_tax_unit_dependent: true + # High school senior: 14.42 x 12 hours x 40 weeks = 6,921.60. + employment_income: 6_921.6 + spm_units: + spm_unit: + members: [head, child1, child2, child3] + # No Reach Up. + tanf: 0 + households: + household: + members: [head, child1, child2, child3] + state_code: VT + output: + # child1 is under 18, a dependent and in school: excluded. child2 is not in + # school and child3 is 18: both counted. + # 29,993.60 + 22,495.20 + 6,921.60 = 59,410.40. + vt_liheap_income: 59_410.4 + +- name: Case 4, Social Security and IRA withdrawals both count in full. + period: 2026 + absolute_error_margin: 0.01 + input: + people: + head: + age: 73 + # Social Security of 1,200 a month, gross of the Medicare premium. + social_security_retirement: 14_400 + # Required minimum distributions from a traditional IRA. + taxable_ira_distributions: 3_000 + # Social Security is above the SSI federal benefit rate. + ssi: 0 + spm_units: + spm_unit: + members: [head] + households: + household: + members: [head] + state_code: VT + output: + # Rule 2911.2(b)(4) and (9) count retirement benefits, including retirement + # account distributions, and social security: 14,400 + 3,000 = 17,400. + vt_liheap_income: 17_400 + +- name: Case 5, a 16-year-old student who is not a tax dependent has her earnings counted. + period: 2026 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 16 + is_in_k12_school: true + # A teen parent who lives on her own with her child, so she is not + # under parental control or anyone's tax dependent. + is_tax_unit_dependent: false + # After-school job: 14.42 x 15 hours x 52 weeks = 11,247.60. + employment_income: 11_247.6 + person2: + age: 1 + spm_units: + spm_unit: + members: [person1, person2] + # No Reach Up. + tanf: 0 + households: + household: + members: [person1, person2] + state_code: VT + output: + # person1 is under 18 and in school but not a dependent, so rule + # 2912(a)(12) does not exclude her wages: 11,247.60. + vt_liheap_income: 11_247.6 + +- name: Case 6, every earned income source counts, including a positive self-employment profit. + period: 2026 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 52 + # One distinct amount per earned source (1, 2, 4, 8 and 16 x 1,000), so + # dropping any list entry changes the total. + employment_income: 16_000 + self_employment_income: 1_000 + sstb_self_employment_income: 2_000 + farm_operations_income: 4_000 + # partnership_s_corp_income adds partnership_income and s_corp_income. + partnership_income: 8_000 + spm_units: + spm_unit: + members: [person1] + # No Reach Up. + tanf: 0 + households: + household: + members: [person1] + state_code: VT + output: + # 16,000 + 1,000 + 2,000 + 4,000 + 8,000 = 31,000. + vt_liheap_income: 31_000 + +- name: Case 7, every unearned income source counts, including monthly SSI and AABD, railroad retirement, reported unemployment and a military survivor annuity. + period: 2026 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 67 + # The k-th unearned source in list order gets 120 x k, so dropping any + # list entry changes the total. Where a listed variable only adds other + # variables, the input is a component it adds. + # ssi and vt_aabd are monthly: an annual input of 120 and 240 is 10 and + # 20 a month, summed back over the year by add(). + ssi: 120 + vt_aabd: 240 + general_assistance: 360 + # pension_income adds taxable_pension_income, which adds this. + taxable_private_pension_income: 480 + railroad_benefits: 600 + # military_retirement_income adds this Survivor Benefit Plan annuity. + military_retirement_pay_survivors: 720 + # retirement_distributions adds taxable_retirement_distributions, which + # adds this. + taxable_401k_distributions: 840 + veterans_benefits: 960 + disability_benefits: 1_080 + workers_compensation: 1_200 + # A reported amount, so total_unemployment_compensation uses it. + unemployment_compensation: 1_320 + # social_security adds this. + social_security_retirement: 1_440 + survivor_benefits: 1_560 + strike_benefits: 1_680 + rental_income: 1_800 + farm_rent_income: 1_920 + child_support_received: 2_040 + alimony_income: 2_160 + # interest_income adds this. + taxable_interest_income: 2_280 + # ordinary_dividend_income adds this. + non_qualified_dividend_income: 2_400 + estate_income: 2_520 + miscellaneous_income: 2_640 + spm_units: + spm_unit: + members: [person1] + # No Reach Up. + tanf: 0 + households: + household: + members: [person1] + state_code: VT + output: + # 120 x (1 + 2 + ... + 22) = 120 x 253 = 30,360. + vt_liheap_income: 30_360 + +- name: Case 8, a rental loss does not reduce wages. + period: 2026 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 47 + # 14.42 x 25 hours x 52 weeks = 18,746. + employment_income: 18_746 + # A rented-out apartment that lost money. + rental_income: -3_600 + spm_units: + spm_unit: + members: [person1] + # No Reach Up. + tanf: 0 + households: + household: + members: [person1] + state_code: VT + output: + # Wages 18,746 + max(-3,600, 0) = 18,746, not 15,146. + vt_liheap_income: 18_746 diff --git a/policyengine_us/variables/gov/states/vt/dcf/liheap/eligibility/vt_liheap_eligible.py b/policyengine_us/variables/gov/states/vt/dcf/liheap/eligibility/vt_liheap_eligible.py new file mode 100644 index 00000000000..2a28709da43 --- /dev/null +++ b/policyengine_us/variables/gov/states/vt/dcf/liheap/eligibility/vt_liheap_eligible.py @@ -0,0 +1,35 @@ +from policyengine_us.model_api import * + + +class vt_liheap_eligible(Variable): + value_type = bool + entity = SPMUnit + definition_period = YEAR + label = "Vermont LIHEAP regular heating eligibility" + defined_for = StateCode.VT + reference = ( + # PDF pages 10, 16, 38, 43 + "https://outside.vermont.gov/dept/DCF/Shared%20Documents/ESD/Rules/2900-Seasonal-Fuel-Assistance.pdf#page=10", + "https://legislature.vermont.gov/statutes/section/33/026/02604", + "https://outside.vermont.gov/dept/DCF/Policies%20Procedures%20Guidance/ESD-Procedure-P2916.pdf#page=1", + ) + + def formula(spm_unit, period, parameters): + size = spm_unit("vt_liheap_household_size", period) + income = spm_unit("vt_liheap_income", period) + limit = spm_unit("vt_liheap_income_limit", period) + heating_cost = spm_unit("vt_liheap_heating_cost", period) + 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) + ) + # Rule 2916(b) requires a primary heating fuel listed in the standard + # heating cost tables. The $21 benefit for subsidized heat in rent needs + # no known fuel, but NONE means the home has no heating, so it is + # ineligible in every case. A listed fuel without heat in rent is treated + # as paid to the supplier directly; the benefit uses the standard cost, + # not a bill. + heating_type = spm_unit("heating_type", period) + has_heat = heating_type != heating_type.possible_values.NONE + has_heating_cost = (heating_cost > 0) | (heat_in_rent & subsidized) + return (size > 0) & (income <= limit) & has_heat & has_heating_cost diff --git a/policyengine_us/variables/gov/states/vt/dcf/liheap/eligibility/vt_liheap_income_limit.py b/policyengine_us/variables/gov/states/vt/dcf/liheap/eligibility/vt_liheap_income_limit.py new file mode 100644 index 00000000000..ce2679f332c --- /dev/null +++ b/policyengine_us/variables/gov/states/vt/dcf/liheap/eligibility/vt_liheap_income_limit.py @@ -0,0 +1,52 @@ +from policyengine_us.model_api import * +from policyengine_us.variables.gov.hhs.hhs_smi import liheap_smi_limit +from policyengine_us.variables.gov.hhs.tax_unit_fpg import fpg + + +class vt_liheap_income_limit(Variable): + value_type = float + entity = SPMUnit + definition_period = YEAR + unit = USD + label = "Vermont LIHEAP annual income limit" + defined_for = StateCode.VT + reference = ( + "https://legislature.vermont.gov/statutes/section/33/026/02604", + # PDF pages 38-39 + "https://outside.vermont.gov/dept/DCF/Shared%20Documents/ESD/Rules/2900-Seasonal-Fuel-Assistance.pdf#page=38", + "https://outside.vermont.gov/dept/DCF/Policies%20Procedures%20Guidance/ESD-Procedure-P2913.pdf", + "https://www.law.cornell.edu/uscode/text/42/8624#b_2_B", + ) + + def formula(spm_unit, period, parameters): + p = parameters(period).gov.states.vt.dcf.liheap + p_hhs = parameters(period).gov.hhs.liheap + size = spm_unit("vt_liheap_household_size", period) + state_group = spm_unit.household("state_group_str", period) + state = spm_unit.household("state_code_str", period) + # Rule 2914(a)(3) reads the maximum from the P-2913 table: 185% of the + # whole-dollar monthly guideline, rounded half up to the dollar. Monthly + # income at or below it is the same test as annual income at or below + # twelve times it. + monthly_guideline = spm_unit("vt_liheap_fpg", period) / MONTHS_IN_YEAR + monthly_limit = np.floor(monthly_guideline * p.eligibility.fpg_rate + 0.5) + guideline = fpg( + max_(size, 1), state_group, period, parameters, year_lag=p.fpg_year_lag + ) + # 33 V.S.A. 2604(a) and rule 2913(b) bar income above 185% of the + # poverty guideline or above the LIHEAP maximum, the greater of 150% of + # the guideline and 60% of state median income (42 U.S.C. + # 8624(b)(2)(B)). In FFY2026 the LIHEAP maximum is the lower limit for + # sizes 10-15, below the 185% amounts printed in P-2913; the code + # follows the statute and rule. + smi_limit = liheap_smi_limit(size, state, p_hhs.smi_limit, period, parameters) + liheap_maximum = max_(guideline * p_hhs.fpg_limit, smi_limit) + # The statute caps income at exactly 185% of the guideline. The rounded + # P-2913 amount applies where it is lower (size 1: 2,412 a month against + # 2,412.71); where it rounds above that (size 2: 3,262 against 3,260.625), + # the exact 185% applies. + poverty_limit = min_( + monthly_limit * MONTHS_IN_YEAR, guideline * p.eligibility.fpg_rate + ) + limit = min_(poverty_limit, liheap_maximum) + return where(size > 0, limit, 0) diff --git a/policyengine_us/variables/gov/states/vt/dcf/liheap/payment/vt_liheap_heat_cost_percentage.py b/policyengine_us/variables/gov/states/vt/dcf/liheap/payment/vt_liheap_heat_cost_percentage.py new file mode 100644 index 00000000000..017f688c719 --- /dev/null +++ b/policyengine_us/variables/gov/states/vt/dcf/liheap/payment/vt_liheap_heat_cost_percentage.py @@ -0,0 +1,33 @@ +from policyengine_us.model_api import * + + +class vt_liheap_heat_cost_percentage(Variable): + value_type = float + entity = SPMUnit + definition_period = YEAR + unit = "/1" + label = "Vermont LIHEAP heat cost percentage" + defined_for = StateCode.VT + reference = ( + # PDF pages 39, 42 + "https://outside.vermont.gov/dept/DCF/Shared%20Documents/ESD/Rules/2900-Seasonal-Fuel-Assistance.pdf#page=39", + # PDF pages 1-3 + "https://outside.vermont.gov/dept/DCF/Policies%20Procedures%20Guidance/ESD-Procedure-P2914A.pdf#page=1", + "https://outside.vermont.gov/dept/DCF/Policies%20Procedures%20Guidance/ESD-Procedure-P2915.pdf", + ) + + def formula(spm_unit, period, parameters): + p = parameters(period).gov.states.vt.dcf.liheap.payment.heat_cost_percentage + monthly_income = spm_unit("vt_liheap_income", period) / MONTHS_IN_YEAR + monthly_guideline = spm_unit("vt_liheap_fpg", period) / MONTHS_IN_YEAR + # Rule 2915(b) takes the percentage of poverty from the P-2914A table of + # monthly dollar bands. Each threshold after the open first one tops the + # band below it at that share of the monthly guideline, rounded up to the + # dollar; income at or below a top stays in that band. + # NOTE: Rounding to cents first keeps floating-point error from lifting + # an exact dollar product to the next dollar. + band = 0 + for rate in p.thresholds[1:]: + top = np.ceil(np.round(rate * monthly_guideline, 2)) + band = band + (monthly_income > top) + return np.array(p.amounts)[band] diff --git a/policyengine_us/variables/gov/states/vt/dcf/liheap/payment/vt_liheap_heating_cost.py b/policyengine_us/variables/gov/states/vt/dcf/liheap/payment/vt_liheap_heating_cost.py new file mode 100644 index 00000000000..8ac39cb124a --- /dev/null +++ b/policyengine_us/variables/gov/states/vt/dcf/liheap/payment/vt_liheap_heating_cost.py @@ -0,0 +1,53 @@ +from policyengine_us.model_api import * + + +class vt_liheap_heating_cost(Variable): + value_type = float + entity = SPMUnit + definition_period = YEAR + unit = USD + label = "Vermont LIHEAP standard heating cost" + defined_for = StateCode.VT + reference = ( + "https://outside.vermont.gov/dept/DCF/Policies%20Procedures%20Guidance/ESD-Procedure-P2916.pdf#page=1", + # PDF pages 2-3 + "https://humanservices.vermont.gov/sites/ahsnew/files/documents/2026-2027%20Benefits%20Matrx.pdf#page=2", + # PDF pages 6, 39, 43 + "https://outside.vermont.gov/dept/DCF/Shared%20Documents/ESD/Rules/2900-Seasonal-Fuel-Assistance.pdf#page=6", + ) + + def formula(spm_unit, period, parameters): + p = parameters(period).gov.states.vt.dcf.liheap.payment + dwelling_type = spm_unit("vt_liheap_dwelling_type", period) + # A unit with no bedrooms uses the 1-bedroom column. + bedrooms = clip(spm_unit.household("bedrooms", period), 1, p.max_bedrooms) + cost = p.heating_cost[dwelling_type] + fuel = spm_unit("heating_type", period) + fuels = fuel.possible_values + # Rule 2901(a)(14) defines the primary heating fuel as the energy type + # listed in the standard heating cost tables that provides the largest + # portion of heat. For a grid-tied solar home with electric heat, as + # heating_expense assumes, that listed type is electricity, so SOLAR + # reads the electricity row; an off-grid solar-only home, which has no + # listed fuel, cannot be distinguished. OTHER and NONE have no row + # (rule 2916(b)), and UNSPECIFIED is not priced because heating_type is + # a required input. + return select( + [ + fuel == fuels.NATURAL_GAS, + fuel == fuels.PROPANE, + fuel == fuels.FUEL_OIL, + fuel == fuels.KEROSENE, + (fuel == fuels.ELECTRICITY) | (fuel == fuels.SOLAR), + (fuel == fuels.WOOD) | (fuel == fuels.COAL), + ], + [ + cost.natural_gas[bedrooms], + cost.propane[bedrooms], + cost.oil[bedrooms], + cost.kerosene[bedrooms], + cost.electricity[bedrooms], + cost.wood_and_coal[bedrooms], + ], + default=0, + ) diff --git a/policyengine_us/variables/gov/states/vt/dcf/liheap/vt_liheap.py b/policyengine_us/variables/gov/states/vt/dcf/liheap/vt_liheap.py new file mode 100644 index 00000000000..865831cc929 --- /dev/null +++ b/policyengine_us/variables/gov/states/vt/dcf/liheap/vt_liheap.py @@ -0,0 +1,52 @@ +from policyengine_us.model_api import * + + +class vt_liheap(Variable): + value_type = float + entity = SPMUnit + definition_period = YEAR + unit = USD + label = "Vermont LIHEAP regular heating benefit" + defined_for = "vt_liheap_eligible" + reference = ( + # PDF pages 39-41 + "https://outside.vermont.gov/dept/DCF/Shared%20Documents/ESD/Rules/2900-Seasonal-Fuel-Assistance.pdf#page=39", + "https://legislature.vermont.gov/statutes/section/33/026/02605", + # PDF pages 6-9 + "https://outside.vermont.gov/dept/DCF/Policies%20Procedures%20Guidance/ESD-Procedure-P2914D.pdf#page=6", + ) + documentation = ( + "Annual Seasonal Fuel Assistance benefit, verified for the 2025-26 " + "heating season (model year 2026). Model year 2027 carries the estimated " + "payment rate forward and omits the larger 2026-27 oil and kerosene " + "benefit; it and earlier years are unverified estimates. The payment rate " + "is an estimate because DCF does not publish it. Applications are assumed " + "to arrive outside December to February, so the 80%, 60% and 40% " + "late-application reductions are not applied. Roomer or boarder " + "households ($21) and excludable members have no inputs, and annual " + "income stands in for the 30-day lookback." + ) + + def formula(spm_unit, period, parameters): + p = parameters(period).gov.states.vt.dcf.liheap.payment + percentage = spm_unit("vt_liheap_heat_cost_percentage", period) + heating_cost = spm_unit("vt_liheap_heating_cost", period) + ratable_benefit = percentage * heating_cost * p.payment_rate + 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) + ) + heat_in_rent_benefit = max_( + ratable_benefit * p.heat_in_rent_rate, p.heat_in_rent_minimum + ) + benefit = select( + [heat_in_rent & subsidized, heat_in_rent, subsidized], + [ + p.nominal_benefit, + heat_in_rent_benefit, + ratable_benefit * p.subsidized_rate, + ], + default=ratable_benefit, + ) + # Rule 2914(d) rounds the net benefit down to the whole dollar. + return np.floor(benefit) diff --git a/policyengine_us/variables/gov/states/vt/dcf/liheap/vt_liheap_dwelling_type.py b/policyengine_us/variables/gov/states/vt/dcf/liheap/vt_liheap_dwelling_type.py new file mode 100644 index 00000000000..61ed67ad6b8 --- /dev/null +++ b/policyengine_us/variables/gov/states/vt/dcf/liheap/vt_liheap_dwelling_type.py @@ -0,0 +1,25 @@ +from policyengine_us.model_api import * + + +class VTLIHEAPDwellingType(Enum): + SINGLE_FAMILY_OR_MOBILE_HOME = "Single-family home or mobile home" + MULTI_FAMILY = "Multi-family home" + + +class vt_liheap_dwelling_type(Variable): + value_type = Enum + entity = SPMUnit + possible_values = VTLIHEAPDwellingType + default_value = VTLIHEAPDwellingType.SINGLE_FAMILY_OR_MOBILE_HOME + definition_period = YEAR + label = "Vermont LIHEAP dwelling type" + documentation = ( + "Dwelling group of the Vermont standard heating cost tables: a " + "single-family home (including a modular home) or mobile home, or a " + "multi-family home such as a duplex, apartment, condo or townhouse. " + "Defaults to the single-family or mobile home table." + ) + reference = ( + "https://outside.vermont.gov/dept/DCF/Policies%20Procedures%20Guidance/ESD-Procedure-P2916.pdf#page=1", + "https://outside.vermont.gov/dept/DCF/Policies%20Procedures%20Guidance/ESD-Procedure-P2927B.pdf#page=1", + ) diff --git a/policyengine_us/variables/gov/states/vt/dcf/liheap/vt_liheap_fpg.py b/policyengine_us/variables/gov/states/vt/dcf/liheap/vt_liheap_fpg.py new file mode 100644 index 00000000000..5aabd485d62 --- /dev/null +++ b/policyengine_us/variables/gov/states/vt/dcf/liheap/vt_liheap_fpg.py @@ -0,0 +1,48 @@ +from policyengine_us.model_api import * +from policyengine_us.variables.gov.hhs.tax_unit_fpg import fpg + + +class vt_liheap_fpg(Variable): + value_type = float + entity = SPMUnit + definition_period = YEAR + unit = USD + label = "Vermont LIHEAP annual poverty guideline" + documentation = ( + "Twelve times the whole-dollar monthly poverty guideline on which the " + "P-2913 income maximums and the P-2914A percentage-of-poverty bands are " + "built." + ) + defined_for = StateCode.VT + reference = ( + "https://outside.vermont.gov/dept/DCF/Policies%20Procedures%20Guidance/ESD-Procedure-P2913.pdf", + # PDF pages 1-3 + "https://outside.vermont.gov/dept/DCF/Policies%20Procedures%20Guidance/ESD-Procedure-P2914A.pdf#page=1", + "https://dcf.vermont.gov/esd/fuel/income-guidelines", + ) + + def formula(spm_unit, period, parameters): + p = parameters(period).gov.states.vt.dcf.liheap + size = spm_unit("vt_liheap_household_size", period) + state_group = spm_unit.household("state_group_str", period) + table_size = clip(size, 1, p.fpg_table_size) + guideline = fpg( + table_size, state_group, period, parameters, year_lag=p.fpg_year_lag + ) + additional_guideline = ( + fpg( + table_size + 1, + state_group, + period, + parameters, + year_lag=p.fpg_year_lag, + ) + - guideline + ) + # Round the monthly guideline and the monthly additional-person amount + # half up to the dollar separately. + monthly_guideline = np.floor(guideline / MONTHS_IN_YEAR + 0.5) + monthly_increment = np.floor(additional_guideline / MONTHS_IN_YEAR + 0.5) + additional_people = max_(size - p.fpg_table_size, 0) + monthly_amount = monthly_guideline + additional_people * monthly_increment + return monthly_amount * MONTHS_IN_YEAR diff --git a/policyengine_us/variables/gov/states/vt/dcf/liheap/vt_liheap_household_size.py b/policyengine_us/variables/gov/states/vt/dcf/liheap/vt_liheap_household_size.py new file mode 100644 index 00000000000..d1ae69fc499 --- /dev/null +++ b/policyengine_us/variables/gov/states/vt/dcf/liheap/vt_liheap_household_size.py @@ -0,0 +1,17 @@ +from policyengine_us.model_api import * + + +class vt_liheap_household_size(Variable): + value_type = int + entity = SPMUnit + definition_period = YEAR + label = "Vermont LIHEAP eligible household size" + defined_for = StateCode.VT + reference = ( + "https://outside.vermont.gov/dept/DCF/Shared%20Documents/ESD/Rules/2900-Seasonal-Fuel-Assistance.pdf#page=39", + "https://outside.vermont.gov/dept/DCF/Policies%20Procedures%20Guidance/ESD-Procedure-P2910.pdf", + ) + + # Rule 2914(a)(1) and P-2910 leave ineligible members, such as an + # undocumented immigrant, out of the size while counting their income. + adds = ["is_citizen_or_legal_immigrant"] diff --git a/policyengine_us/variables/gov/states/vt/dcf/liheap/vt_liheap_income.py b/policyengine_us/variables/gov/states/vt/dcf/liheap/vt_liheap_income.py new file mode 100644 index 00000000000..9b3ae67853a --- /dev/null +++ b/policyengine_us/variables/gov/states/vt/dcf/liheap/vt_liheap_income.py @@ -0,0 +1,46 @@ +from policyengine_us.model_api import * + + +class vt_liheap_income(Variable): + value_type = float + entity = SPMUnit + definition_period = YEAR + unit = USD + label = "Vermont LIHEAP gross household income" + defined_for = StateCode.VT + reference = ( + # PDF pages 10, 25-29, 34 + "https://outside.vermont.gov/dept/DCF/Shared%20Documents/ESD/Rules/2900-Seasonal-Fuel-Assistance.pdf#page=10", + "https://outside.vermont.gov/dept/DCF/Policies%20Procedures%20Guidance/ESD-Procedure-P2910.pdf", + "https://legislature.vermont.gov/statutes/section/33/026/02604", + ) + + def formula(spm_unit, period, parameters): + p = parameters(period).gov.states.vt.dcf.liheap.income + person = spm_unit.members + # Gross income of all members, regardless of immigration status, with no + # deductions (rule 2910(a); P-2910). Annual income approximates the + # 30-day lookback of rule 2911(c). Each source is floored at zero, so a + # loss in one source cannot offset another and the total is never + # negative. + earned = 0 + for source in p.sources.earned: + earned = earned + max_(person(source, period), 0) + unearned = 0 + for source in p.sources.unearned: + # add() also sums monthly sources such as vt_aabd over the year. + unearned = unearned + max_(add(person, period, [source]), 0) + # Rule 2912(a)(12) excludes the earnings of a member under 18 who lives + # with a parent or under parental control and attends elementary, high + # school, home school or GED classes at least half time. K-12 enrollment + # stands in for that schooling and tax-unit dependency for parental + # control. + age = person("age", period) + excluded_child = ( + (age < p.child_earnings_age_limit) + & person("is_in_k12_school", period) + & person("is_tax_unit_dependent", period) + ) + counted_earned = where(excluded_child, 0, earned) + # Reach Up is counted once through the SPM-unit tanf aggregate. + return spm_unit.sum(counted_earned + unearned) + spm_unit("tanf", period) diff --git a/source_conflicts/candidates.yaml b/source_conflicts/candidates.yaml index 1bd189c54c3..a975e0b7dcc 100644 --- a/source_conflicts/candidates.yaml +++ b/source_conflicts/candidates.yaml @@ -8291,6 +8291,102 @@ candidates: note: Transmittal 71 (July 1, 2021) removed Appendix 3, so the agency schedule applied only in state fiscal year 2021 (July 2020 to June 2021). The 7/20 Chapter 900, Appendix II, Example 6 (a TANF-UP household of four in Group II) uses the $499 TANF standard. Before July 2020 and from July 2021 the manual has one schedule for all units. +- id: us-vt:liheap-income-maximum-large-households + program: liheap + years: + from: 2026 + type: federal-fiscal + question: Does the Seasonal Fuel Assistance income limit for households of 10 or more stop at the LIHEAP maximum + the statute adds, below the 185% amounts the procedure table prints? + sources: + - kind: statute + citation: 33 V.S.A. § 2604(a) + url: https://legislature.vermont.gov/statutes/section/33/026/02604 + - kind: regulation + citation: Vermont Seasonal Fuel Assistance Rule 2913(b) + url: https://outside.vermont.gov/dept/DCF/Shared%20Documents/ESD/Rules/2900-Seasonal-Fuel-Assistance.pdf#page=38 + - kind: manual + citation: DCF Procedure P-2913, Income Guidelines, effective March 1, 2025 to February 28, 2026 + url: https://outside.vermont.gov/dept/DCF/Policies%20Procedures%20Guidance/ESD-Procedure-P2913.pdf + readings: + statute: Gross income may exceed neither 185% of the poverty guideline nor the LIHEAP maximum (the greater of 150% + of the guideline and 60% of state median income), so the FFY2026 size-10 limit is $113,748 a year. + other: P-2913 prints the 185% amounts for sizes 10-15 (size 10 $10,044 a month, $120,528 a year) without the LIHEAP + maximum. + example: A 10-person household with $115,000 of gross income in 2026 is ineligible under the statute and eligible under + the printed table. + pe_us: + follows: statute + code: [] + prs: + - https://github.com/PolicyEngine/policyengine-us/pull/10436 + state: ruled + note: 'Rule 2913(b) repeats both statutory tests. No document shows whether DCF applies the LIHEAP maximum to large + households. Code not yet on main: vt_liheap_income_limit.' +- id: us-vt:liheap-income-maximum-rounding + program: liheap + years: + from: 2026 + type: federal-fiscal + question: Does the Seasonal Fuel Assistance income maximum stop at exactly 185% of the poverty guideline where + the procedure table's rounded monthly amounts are higher? + sources: + - kind: statute + citation: 33 V.S.A. § 2604(a) + url: https://legislature.vermont.gov/statutes/section/33/026/02604 + - kind: regulation + citation: Vermont Seasonal Fuel Assistance Rules 2913(b)-(c) and 2914(a)(3) + url: https://outside.vermont.gov/dept/DCF/Shared%20Documents/ESD/Rules/2900-Seasonal-Fuel-Assistance.pdf#page=38 + - kind: manual + citation: DCF Procedure P-2913, Income Guidelines, effective March 1, 2025 to February 28, 2026 + url: https://outside.vermont.gov/dept/DCF/Policies%20Procedures%20Guidance/ESD-Procedure-P2913.pdf + readings: + statute: Gross income may not exceed exactly 185% of the poverty guideline, so the FFY2026 two-person limit is + $39,127.50 a year ($3,260.625 a month). + other: P-2913 prints 185% of the monthly guideline rounded half up to the dollar, above 185% of the guideline for + some sizes (two people $3,262 a month, $39,144 a year). + example: A two-person household with $3,261 a month ($39,132 a year) of gross income in 2026 is eligible under the + printed table and ineligible under the statute. + pe_us: + follows: statute + code: [] + prs: + - https://github.com/PolicyEngine/policyengine-us/pull/10436 + state: ruled + note: 'The statute sets a ceiling, so the printed amount applies where it rounds below 185% (one person $2,412 a + month against $2,412.71). Code not yet on main: vt_liheap_income_limit.' +- id: us-vt:liheap-plan-income-items + program: liheap + years: + from: 2026 + type: federal-fiscal + question: Do Seasonal Fuel Assistance strike benefits count and does income stop at 185% of the poverty guideline, + as the statute and rules require, although the FY2026 plan leaves strike pay unchecked and reports 60% of state + median income for all sizes? + sources: + - kind: statute + citation: 33 V.S.A. § 2604(a) + url: https://legislature.vermont.gov/statutes/section/33/026/02604 + - kind: regulation + citation: Vermont Seasonal Fuel Assistance Rules 2911.2(b)(10) and 2913(b) + url: https://outside.vermont.gov/dept/DCF/Shared%20Documents/ESD/Rules/2900-Seasonal-Fuel-Assistance.pdf#page=28 + - kind: plan + citation: Vermont FY2026 LIHEAP State Plan, items 1.9 and 2.1 + url: https://liheapch.acf.gov/docs/2026/state-plans/VT_Plan_2026.pdf#page=6 + readings: + statute: Gross income follows the 3SquaresVT rules, which count strike benefits (7 CFR 273.9(b)(2)(ii); rule + 2911.2(b)(10)), and may not exceed 185% of the poverty guideline (rule 2913(b)). + other: Plan 1.9 leaves Strike Pay unchecked and plan 2.1 sets 60% of state median income for all household sizes. + example: One person in 2026 with $24,000 of wages and $6,000 of strike benefits has $30,000 of gross income, above + the $28,944 limit, and is ineligible under the statute and rules; the $24,000 of wages alone is within the plan's + $41,075 limit (60% of state median income), so the person is eligible under the plan. + pe_us: + follows: statute + code: [] + prs: + - https://github.com/PolicyEngine/policyengine-us/pull/10436 + state: ruled + note: 'Statute governs (2026-10-19 rule). Code not yet on main: vt_liheap_income, vt_liheap_income_limit.' - id: us-vt:low-income-cdcc-threshold-equality program: income-tax years: