diff --git a/activitysim/abm/models/trip_destination.py b/activitysim/abm/models/trip_destination.py index c3fffe013..853cfc35e 100644 --- a/activitysim/abm/models/trip_destination.py +++ b/activitysim/abm/models/trip_destination.py @@ -1496,23 +1496,42 @@ def run_trip_destination( for primary_purpose, trips_segment in nth_trips.groupby( "primary_purpose", observed=True ): - choices, destination_sample = choose_trip_destination( - state, - primary_purpose, - trips_segment, - alternatives, - tours_merged, - model_settings, - want_logsums, - want_sample_table, - size_term_matrix, - skim_hotel, - estimator, - chunk_size, - trace_label=tracing.extend_trace_label( - nth_trace_label, primary_purpose - ), - ) + try: + choices, destination_sample = choose_trip_destination( + state, + primary_purpose, + trips_segment, + alternatives, + tours_merged, + model_settings, + want_logsums, + want_sample_table, + size_term_matrix, + skim_hotel, + estimator, + chunk_size, + trace_label=tracing.extend_trace_label( + nth_trace_label, primary_purpose + ), + ) + except KeyError as err: + if err.args[0] == "purpose_index_num": + logger.error( + """ + + When using the trip destination model with sharrow, it is necessary + to set a value for `purpose_index_num` in the trip destination + annotate trips preprocessor. This allows for an optimized compiled + lookup of the size term from the array of size terms. The value of + `purpose_index_num` should be the integer column position in the size + matrix, with usual zero-based numpy indexing semantics (i.e. the first + column is zero). The preprocessor expression most likely needs to be + "size_terms.get_cols(df.purpose)" unless some unusual transform of + size terms has been employed. + + """ + ) + raise choices_list.append(choices) if want_sample_table: diff --git a/activitysim/core/simulate.py b/activitysim/core/simulate.py index e05781811..55caf050a 100644 --- a/activitysim/core/simulate.py +++ b/activitysim/core/simulate.py @@ -989,7 +989,7 @@ def to_array(x): # return utilities -def set_skim_wrapper_targets(df, skims): +def set_skim_wrapper_targets(df, skims, allow_partial_success: bool = True): """ Add the dataframe to the SkimWrapper object so that it can be dereferenced using the parameters of the skims object. @@ -1007,6 +1007,11 @@ def set_skim_wrapper_targets(df, skims): dataframe that comes back from interacting choosers with alternatives. See the skims module for more documentation on how the skims object is intended to be used. + allow_partial_success : bool, optional + If True (default), failures to set skim targets for some skim objects + (for example due to missing required columns in `df`) will be collected + and logged as warnings but will not raise an exception. If False, any + such failure will be raised immediately, preventing partial success. """ skims = ( @@ -1016,13 +1021,31 @@ def set_skim_wrapper_targets(df, skims): if isinstance(skims, dict) else [skims] ) + problems = [] # assume any object in skims can be treated as a skim for skim in skims: try: skim.set_df(df) except AttributeError: + # sometimes when passed as a dict, the skims have a few keys given as + # settings or constants, which are not actually "skim" objects and have + # no `set_df` attribute. This is fine and we just let them pass. pass + except AssertionError as e: + # An assertion error will get triggered if the columns of `df` are + # missing one of the required keys needed to look up values in the + # skims. This may not be a problem, if this particular set of skims + # is not actually used in this model component. So we'll warn about + # it but usually not raise a showstopping error. + problems.append(e) + if not allow_partial_success: + raise + + if problems: + # if problems were discovered, log them as warnings + for problem in problems: + logger.warning(str(problem)) # diff --git a/pyproject.toml b/pyproject.toml index e1fb5b5bc..a8fef9948 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -26,7 +26,7 @@ dependencies = [ "requests >= 2.7", "scikit-learn >= 1.2", "setuptools>=80.9.0", - "sharrow >= 2.9.1", + "sharrow>=2.15", "sparse", "tables >= 3.9", # pytables is tables in pypi "xarray >= 2024.05", diff --git a/uv.lock b/uv.lock index e3c68661f..f1ffb126d 100644 --- a/uv.lock +++ b/uv.lock @@ -1,5 +1,5 @@ version = 1 -revision = 3 +revision = 2 requires-python = ">=3.10" resolution-markers = [ "python_full_version >= '3.14'", @@ -121,7 +121,7 @@ requires-dist = [ { name = "requests", specifier = ">=2.7" }, { name = "scikit-learn", specifier = ">=1.2" }, { name = "setuptools", specifier = ">=80.9.0" }, - { name = "sharrow", specifier = ">=2.9.1" }, + { name = "sharrow", specifier = ">=2.15" }, { name = "sparse" }, { name = "tables", specifier = ">=3.9" }, { name = "xarray", specifier = ">=2024.5" }, @@ -5051,7 +5051,7 @@ wheels = [ [[package]] name = "sharrow" -version = "2.14.0" +version = "2.15.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "dask" }, @@ -5066,9 +5066,9 @@ dependencies = [ { name = "pyarrow" }, { name = "xarray" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/3d/ad/9fb609c6cfdd39fb976024fccd2dfa663a5310a4df1a392a80d21605b4cb/sharrow-2.14.0.tar.gz", hash = "sha256:d0d2afadd0b6a9f6c0b3ef6e602262c5f6c31bf33090865db555bae566217df4", size = 2138901, upload-time = "2025-05-29T14:17:12.878Z" } +sdist = { url = "https://files.pythonhosted.org/packages/b1/2b/cd163ad3fc6ad48b650cd472bd161f99bcc7cc4befe2b1b04188084c9f9d/sharrow-2.15.0.tar.gz", hash = "sha256:d4e6881f8abfe0719b009963c491285d14c61226c64382ded16b3ce3d6f232fe", size = 2344691, upload-time = "2025-10-31T00:35:35.171Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/75/b9/b460fbaf2213f1406d1b5537f50852671e203e1ad92b06d5048f5a132471/sharrow-2.14.0-py3-none-any.whl", hash = "sha256:5879481139297708234f796158b9c81333d01d024aab078452ccd8753de88167", size = 2224745, upload-time = "2025-05-29T14:17:11.543Z" }, + { url = "https://files.pythonhosted.org/packages/81/93/ff7835535a690a38c9bb3142e3c5b4b2512de09230bf9a20456e6f3db06f/sharrow-2.15.0-py3-none-any.whl", hash = "sha256:8b76bbe1f1ac941711d151cd84b7bc4aec6d2b44d62b56c831ead79eb533aad0", size = 2241380, upload-time = "2025-10-31T00:35:33.473Z" }, ] [[package]]