From 8f132991caefe952c3bb3b7bf7fa173a7df99a6a Mon Sep 17 00:00:00 2001 From: asiripanich <17020181+asiripanich@users.noreply.github.com> Date: Thu, 30 Oct 2025 12:46:00 +1100 Subject: [PATCH 1/3] fix(jtp): Correct the participant_num assignment logic for estimation mode in joint tour participation --- .../abm/models/joint_tour_participation.py | 24 ++++++++++++------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/activitysim/abm/models/joint_tour_participation.py b/activitysim/abm/models/joint_tour_participation.py index 47bc2b8ff..528fef643 100644 --- a/activitysim/abm/models/joint_tour_participation.py +++ b/activitysim/abm/models/joint_tour_participation.py @@ -451,14 +451,22 @@ def joint_tour_participation( PARTICIPANT_COLS = ["tour_id", "household_id", "person_id"] participants = candidates[participate][PARTICIPANT_COLS].copy() - # assign participant_num - # FIXME do we want something smarter than the participant with the lowest person_id? - participants["participant_num"] = ( - participants.sort_values(by=["tour_id", "person_id"]) - .groupby("tour_id") - .cumcount() - + 1 - ) + if estimator: + # In estimation mode, use participant_num from survey data to preserve consistency + # with the original survey data. ActivitySim treats participant_num=1 as the tour + # leader, so the joint tour in the tour table will be associated with the tour + # leader's person_id. We merge participant_num from survey data using the + # participant_id as the join key to ensure the correct tour leader is identified. + participants["participant_num"] = survey_participants_df.reindex(participants.index)["participant_num"] + else: + # assign participant_num + # FIXME do we want something smarter than the participant with the lowest person_id? + participants["participant_num"] = ( + participants.sort_values(by=["tour_id", "person_id"]) + .groupby("tour_id") + .cumcount() + + 1 + ) state.add_table("joint_tour_participants", participants) From 2e7629e6d52120b22f45f1dcec23f0ba34b0a061 Mon Sep 17 00:00:00 2001 From: asiripanich <17020181+asiripanich@users.noreply.github.com> Date: Thu, 30 Oct 2025 12:46:44 +1100 Subject: [PATCH 2/3] fix(jtf): Handle missing PNUM column by selecting the first person in the household --- activitysim/abm/models/joint_tour_frequency.py | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/activitysim/abm/models/joint_tour_frequency.py b/activitysim/abm/models/joint_tour_frequency.py index cace98d7d..d2dc67bc8 100644 --- a/activitysim/abm/models/joint_tour_frequency.py +++ b/activitysim/abm/models/joint_tour_frequency.py @@ -137,7 +137,15 @@ def joint_tour_frequency( # - but we don't know the tour participants yet # - so we arbitrarily choose the first person in the household # - to be point person for the purpose of generating an index and setting origin - temp_point_persons = persons.loc[persons.PNUM == 1] + if "PNUM" in persons.columns: + temp_point_persons = persons.loc[persons.PNUM == 1] + else: + # if PNUM is not available, we can still get the first person in the household + temp_point_persons = ( + persons.sort_index() # ensure stable ordering + .groupby("household_id", as_index=False) + .first() + ) temp_point_persons["person_id"] = temp_point_persons.index temp_point_persons = temp_point_persons.set_index("household_id") temp_point_persons = temp_point_persons[["person_id", "home_zone_id"]] From 9028f670516b800edaa4a87f4f4cde8f38a95327 Mon Sep 17 00:00:00 2001 From: asiripanich <17020181+asiripanich@users.noreply.github.com> Date: Thu, 30 Oct 2025 12:56:48 +1100 Subject: [PATCH 3/3] style: blacken --- activitysim/abm/models/joint_tour_participation.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/activitysim/abm/models/joint_tour_participation.py b/activitysim/abm/models/joint_tour_participation.py index 528fef643..2e5ec618d 100644 --- a/activitysim/abm/models/joint_tour_participation.py +++ b/activitysim/abm/models/joint_tour_participation.py @@ -457,7 +457,9 @@ def joint_tour_participation( # leader, so the joint tour in the tour table will be associated with the tour # leader's person_id. We merge participant_num from survey data using the # participant_id as the join key to ensure the correct tour leader is identified. - participants["participant_num"] = survey_participants_df.reindex(participants.index)["participant_num"] + participants["participant_num"] = survey_participants_df.reindex( + participants.index + )["participant_num"] else: # assign participant_num # FIXME do we want something smarter than the participant with the lowest person_id?