From 440fd322a58fd9b754a1cb76ff45223f98d7f0f7 Mon Sep 17 00:00:00 2001 From: Sijia Wang Date: Tue, 16 Jun 2026 15:36:00 -0400 Subject: [PATCH 1/2] take segmentation from model setting --- activitysim/abm/tables/shadow_pricing.py | 22 +++++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/activitysim/abm/tables/shadow_pricing.py b/activitysim/abm/tables/shadow_pricing.py index 04c5eafc21..ee7c13ed62 100644 --- a/activitysim/abm/tables/shadow_pricing.py +++ b/activitysim/abm/tables/shadow_pricing.py @@ -176,6 +176,7 @@ def __init__( ) self.model_selector = model_settings.MODEL_SELECTOR + self.chooser_segment_column = model_settings.CHOOSER_SEGMENT_COLUMN_NAME if (self.num_processes > 1) and not state.settings.fail_fast: # if we are multiprocessing, then fail_fast should be true or we will wait forever for failed processes @@ -826,9 +827,24 @@ def update_shadow_prices(self, state): sampled_persons = pd.DataFrame() persons_merged = state.get_dataframe("persons_merged") - # need to join the segment to the choices to sample correct persons - segment_to_name_dict = self.shadow_settings.SEGMENT_TO_NAME - segment_name = segment_to_name_dict[self.model_selector] + # Use the model chooser segmentation to keep shadow-pricing resampling + # consistent with segment_ids in location choice settings. + segment_name = self.chooser_segment_column + if segment_name not in persons_merged.columns: + raise SystemConfigurationError( + f"Missing chooser segment column '{segment_name}' in persons_merged " + f"for {self.model_selector} simulation shadow pricing" + ) + + # Fail fast on obvious misconfiguration instead of silently sampling no one. + segment_values = set(self.segment_ids.values()) + chooser_values = set(persons_merged[segment_name].dropna().unique()) + if not segment_values.intersection(chooser_values): + raise SystemConfigurationError( + f"No overlap between SEGMENT_IDS values ({sorted(segment_values)}) and " + f"persons_merged['{segment_name}'] values for {self.model_selector} " + "simulation shadow pricing" + ) if type(self.choices_synced) != pd.DataFrame: self.choices_synced = self.choices_synced.to_frame() From f82f6e152b33583ec114e207e607fa0bbccf4d51 Mon Sep 17 00:00:00 2001 From: Sijia Wang Date: Tue, 16 Jun 2026 16:38:32 -0400 Subject: [PATCH 2/2] deprecate `SEGMENT_TO_NAME` --- activitysim/abm/tables/shadow_pricing.py | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/activitysim/abm/tables/shadow_pricing.py b/activitysim/abm/tables/shadow_pricing.py index ee7c13ed62..0cfe1f081d 100644 --- a/activitysim/abm/tables/shadow_pricing.py +++ b/activitysim/abm/tables/shadow_pricing.py @@ -62,12 +62,6 @@ TALLY_CHECKOUT = (1, -1) TALLY_PENDING_PERSONS = (2, -1) -default_segment_to_name_dict = { - # model_selector : persons_segment_name - "school": "school_segment", - "workplace": "income_segment", -} - def size_table_name(model_selector): """ @@ -134,12 +128,6 @@ class ShadowPriceSettings(PydanticReadable, extra="forbid"): WRITE_ITERATION_CHOICES: bool = False - SEGMENT_TO_NAME: dict[str, str] = { - "school": "school_segment", - "workplace": "income_segment", - } # pydantic uses deep copy, so mutable default value is ok here - """Mapping from model_selector to persons_segment_name.""" - class ShadowPriceCalculator: def __init__(