What happens
county_fips is documented as a five-digit string, and the SPM forecast provider rejects a county supplied as anything else ("County 36061 was supplied for 2024 as 36061, not as text"). That rejection is skipped when the measurement is calculated for a year the input was not stored for.
from policyengine_us import Simulation
situation = {
"people": {"person": {"age": {2024: 40}}},
"households": {
"household": {"members": ["person"], "county_fips": {2024: 36_061}}
},
}
sim = Simulation(situation=situation)
sim.calculate("county_fips", 2025) # array([36061], dtype=object): the integer, carried over
sim.calculate("spm_unit_spm_threshold", 2025) # returns about 22,091 instead of raising
sim.calculate("spm_unit_spm_threshold", 2024) # raises SPM_GEOGRAPHY_REQUIRED, as intended
Observed on main at fbe24ad with policyengine-core 3.32.8.
Why
SPMSimulationMixin._record_county_input_types (policyengine_us/spm.py) records non-text counties for each period the holder stores, and CountyRequiringSPMProvider.record_county_input_types keys them by (year, county text).
require_county_input(year, county_fips) looks the county up under the year being calculated.
- For an input variable with no value in the requested period, core's
Simulation._calculate returns the array stored for the latest known period (the auto_carry_over_input_variables block). So 2025 consumes the 2024 integer, while the record only has a 2024 key.
Dataset runs are mostly unaffected, because the dataset loader stores county_fips for every year it extends to. Household situations that give one input year and calculate another are the exposed path.
What a fix needs to keep
- Correcting an input clears its rejection for that year.
- A national geography selection records nothing.
- Each simulation and branch holds its own provider and is judged by the county it reads itself.
Carry-over semantics differ between core versions (PolicyEngine/policyengine-core#562 proposes carrying over inputs only), so the fix should key on what the simulation actually reads for the requested year instead of re-implementing core's rule.
Found while fixing the branch read of the same check.
What happens
county_fipsis documented as a five-digit string, and the SPM forecast provider rejects a county supplied as anything else ("County 36061 was supplied for 2024 as 36061, not as text"). That rejection is skipped when the measurement is calculated for a year the input was not stored for.Observed on
mainat fbe24ad with policyengine-core 3.32.8.Why
SPMSimulationMixin._record_county_input_types(policyengine_us/spm.py) records non-text counties for each period the holder stores, andCountyRequiringSPMProvider.record_county_input_typeskeys them by(year, county text).require_county_input(year, county_fips)looks the county up under the year being calculated.Simulation._calculatereturns the array stored for the latest known period (theauto_carry_over_input_variablesblock). So 2025 consumes the 2024 integer, while the record only has a 2024 key.Dataset runs are mostly unaffected, because the dataset loader stores
county_fipsfor every year it extends to. Household situations that give one input year and calculate another are the exposed path.What a fix needs to keep
Carry-over semantics differ between core versions (PolicyEngine/policyengine-core#562 proposes carrying over inputs only), so the fix should key on what the simulation actually reads for the requested year instead of re-implementing core's rule.
Found while fixing the branch read of the same check.