Skip to content

Commit 25e4f6c

Browse files
authored
Merge pull request #758 from camsys/cfg-documentation-vy
Config Settings and Documentation
2 parents 3d66176 + b24874a commit 25e4f6c

199 files changed

Lines changed: 12203 additions & 6312 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/workflows/branch-docs.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ jobs:
4040
id: cache
4141

4242
- name: Update environment
43-
run: mamba env update -n docbuild -f conda-environments/docbuild.yml
43+
run: mamba env update --verbose -n docbuild -f conda-environments/docbuild.yml
4444
if: steps.cache.outputs.cache-hit != 'true'
4545

4646
- name: Install activitysim

.gitignore

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@ sandbox/
55
.pytest_cache
66
.vagrant
77

8-
98
# Byte-compiled / optimized / DLL files
109
__pycache__/
1110
*.py[cod]

activitysim/abm/misc.py

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
import logging
66

7+
import numpy as np
78
import pandas as pd
89

910
from activitysim.core import workflow
@@ -16,16 +17,16 @@
1617

1718

1819
@workflow.cached_object
19-
def households_sample_size(state: workflow.State, override_hh_ids):
20+
def households_sample_size(state: workflow.State, override_hh_ids) -> int:
2021

2122
if override_hh_ids is None:
22-
return state.settings, households_sample_size
23+
return state.settings.households_sample_size
2324
else:
24-
return 0 if override_hh_ids is None else len(override_hh_ids)
25+
return len(override_hh_ids)
2526

2627

2728
@workflow.cached_object
28-
def override_hh_ids(state: workflow.State):
29+
def override_hh_ids(state: workflow.State) -> np.ndarray | None:
2930

3031
hh_ids_filename = state.settings.hh_ids
3132
if hh_ids_filename is None:
@@ -63,12 +64,12 @@ def override_hh_ids(state: workflow.State):
6364

6465

6566
@workflow.cached_object
66-
def trace_od(state: workflow.State):
67+
def trace_od(state: workflow.State) -> tuple[int, int] | None:
6768

6869
od = state.settings.trace_od
6970

7071
if od and not (
71-
isinstance(od, (list, tuple))
72+
isinstance(od, list | tuple)
7273
and len(od) == 2
7374
and all(isinstance(x, int) for x in od)
7475
):
@@ -81,12 +82,12 @@ def trace_od(state: workflow.State):
8182

8283

8384
@workflow.cached_object
84-
def chunk_size(state: workflow.State):
85+
def chunk_size(state: workflow.State) -> int:
8586
_chunk_size = int(state.settings.chunk_size or 0)
8687

8788
return _chunk_size
8889

8990

9091
@workflow.cached_object
91-
def check_for_variability(state: workflow.State):
92+
def check_for_variability(state: workflow.State) -> bool:
9293
return bool(state.settings.check_for_variability)

activitysim/abm/models/accessibility.py

Lines changed: 40 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -3,16 +3,36 @@
33
from __future__ import annotations
44

55
import logging
6+
from typing import Any
67

78
import numba as nb
89
import numpy as np
910
import pandas as pd
1011

1112
from activitysim.core import assign, chunk, los, workflow
13+
from activitysim.core.configuration.base import PydanticReadable
1214

1315
logger = logging.getLogger(__name__)
1416

1517

18+
class AccessibilitySettings(PydanticReadable):
19+
"""
20+
Settings for aggregate accessibility component.
21+
"""
22+
23+
CONSTANTS: dict[str, Any] = {}
24+
25+
land_use_columns: list[str] = []
26+
"""Only include the these columns in the computational tables
27+
28+
Memory usage is reduced by only listing the minimum columns needed by
29+
the SPEC, and nothing extra.
30+
"""
31+
32+
SPEC: str = "accessibility.csv"
33+
"""Filename for the accessibility specification (csv) file."""
34+
35+
1636
@nb.njit
1737
def _accumulate_accessibility(arr, orig_zone_count, dest_zone_count):
1838
assert arr.size == orig_zone_count * dest_zone_count
@@ -144,6 +164,10 @@ def compute_accessibility(
144164
land_use: pd.DataFrame,
145165
accessibility: pd.DataFrame,
146166
network_los: los.Network_LOS,
167+
model_settings: AccessibilitySettings | None = None,
168+
model_settings_file_name: str = "accessibility.yaml",
169+
trace_label: str = "compute_accessibility",
170+
output_table_name: str = "accessibility",
147171
) -> None:
148172
"""
149173
Compute accessibility for each zone in land use file using expressions from accessibility_spec
@@ -160,40 +184,44 @@ def compute_accessibility(
160184
product mutes large differences. The decay function on the walk accessibility measure is
161185
steeper than automobile or transit. The minimum accessibility is zero.
162186
"""
187+
if model_settings is None:
188+
model_settings = AccessibilitySettings.read_settings_file(
189+
state.filesystem, model_settings_file_name
190+
)
163191

164-
trace_label = "compute_accessibility"
165-
model_settings = state.filesystem.read_model_settings("accessibility.yaml")
166192
assignment_spec = assign.read_assignment_spec(
167-
state.filesystem.get_config_file_path("accessibility.csv")
193+
state.filesystem.get_config_file_path(model_settings.SPEC)
168194
)
169195

170196
accessibility_df = accessibility
171197
if len(accessibility_df.columns) > 0:
172198
logger.warning(
173-
f"accessibility table is not empty. Columns:{list(accessibility_df.columns)}"
199+
f"accessibility table is not empty. "
200+
f"Columns:{list(accessibility_df.columns)}"
174201
)
175202
raise RuntimeError("accessibility table is not empty.")
176203

177-
constants = model_settings.get("CONSTANTS", {})
204+
constants = model_settings.CONSTANTS
178205

179-
# only include the land_use columns needed by spec, as specified by land_use_columns model_setting
180-
land_use_columns = model_settings.get("land_use_columns", [])
206+
# only include the land_use columns needed by spec,
207+
# as specified by land_use_columns model_setting
208+
land_use_columns = model_settings.land_use_columns
181209
land_use_df = land_use
182210
land_use_df = land_use_df[land_use_columns]
183211

184212
logger.info(
185-
f"Running {trace_label} with {len(accessibility_df.index)} orig zones {len(land_use_df)} dest zones"
213+
f"Running {trace_label} with {len(accessibility_df.index)} orig zones "
214+
f"{len(land_use_df)} dest zones"
186215
)
187216

188217
accessibilities_list = []
189218

190219
for (
191-
i,
220+
_i,
192221
chooser_chunk,
193-
chunk_trace_label,
222+
_chunk_trace_label,
194223
chunk_sizer,
195224
) in chunk.adaptive_chunked_choosers(state, accessibility_df, trace_label):
196-
197225
accessibilities = compute_accessibilities_for_zones(
198226
state,
199227
chooser_chunk,
@@ -211,4 +239,4 @@ def compute_accessibility(
211239
logger.info(f"{trace_label} computed accessibilities {accessibility_df.shape}")
212240

213241
# - write table to pipeline
214-
state.add_table("accessibility", accessibility_df)
242+
state.add_table(output_table_name, accessibility_df)

activitysim/abm/models/atwork_subtour_destination.py

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88

99
from activitysim.abm.models.util import tour_destination
1010
from activitysim.core import config, estimation, los, tracing, workflow
11+
from activitysim.core.configuration.logit import TourLocationComponentSettings
1112
from activitysim.core.util import assign_in_place
1213

1314
logger = logging.getLogger(__name__)
@@ -20,25 +21,30 @@ def atwork_subtour_destination(
2021
tours: pd.DataFrame,
2122
persons_merged: pd.DataFrame,
2223
network_los: los.Network_LOS,
24+
model_settings: TourLocationComponentSettings | None = None,
25+
model_settings_file_name: str = "atwork_subtour_destination.yaml",
26+
trace_label: str = "atwork_subtour_destination",
2327
) -> None:
24-
trace_label = "atwork_subtour_destination"
25-
model_settings_file_name = "atwork_subtour_destination.yaml"
26-
model_settings = state.filesystem.read_model_settings(model_settings_file_name)
28+
if model_settings is None:
29+
model_settings = TourLocationComponentSettings.read_settings_file(
30+
state.filesystem,
31+
model_settings_file_name,
32+
)
2733

2834
future_settings = {
2935
"SIZE_TERM_SELECTOR": "atwork",
3036
"SEGMENTS": ["atwork"],
3137
"ORIG_ZONE_ID": "workplace_zone_id",
3238
}
33-
model_settings = config.future_model_settings(
39+
model_settings = config.future_component_settings(
3440
model_settings_file_name, model_settings, future_settings
3541
)
3642

3743
destination_column_name = "destination"
38-
logsum_column_name = model_settings.get("DEST_CHOICE_LOGSUM_COLUMN_NAME")
44+
logsum_column_name = model_settings.DEST_CHOICE_LOGSUM_COLUMN_NAME
3945
want_logsums = logsum_column_name is not None
4046

41-
sample_table_name = model_settings.get("DEST_CHOICE_SAMPLE_TABLE_NAME")
47+
sample_table_name = model_settings.DEST_CHOICE_SAMPLE_TABLE_NAME
4248
want_sample_table = (
4349
state.settings.want_dest_choice_sample_tables and sample_table_name is not None
4450
)
@@ -54,8 +60,8 @@ def atwork_subtour_destination(
5460
if estimator:
5561
estimator.write_coefficients(model_settings=model_settings)
5662
# estimator.write_spec(model_settings, tag='SAMPLE_SPEC')
57-
estimator.write_spec(model_settings, tag="SPEC")
58-
estimator.set_alt_id(model_settings["ALT_DEST_COL_NAME"])
63+
estimator.write_spec(file_name=model_settings.SPEC, tag="SPEC")
64+
estimator.set_alt_id(model_settings.ALT_DEST_COL_NAME)
5965
estimator.write_table(
6066
state.get_injectable("size_terms"), "size_terms", append=False
6167
)

activitysim/abm/models/atwork_subtour_frequency.py

Lines changed: 22 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@
1616
tracing,
1717
workflow,
1818
)
19+
from activitysim.core.configuration.base import PreprocessorSettings, PydanticReadable
20+
from activitysim.core.configuration.logit import LogitComponentSettings
1921

2022
logger = logging.getLogger(__name__)
2123

@@ -26,20 +28,30 @@ def add_null_results(state, trace_label, tours):
2628
state.add_table("tours", tours)
2729

2830

31+
class AtworkSubtourFrequencySettings(LogitComponentSettings, extra="forbid"):
32+
"""
33+
Settings for the `atwork_subtour_frequency` component.
34+
"""
35+
36+
preprocessor: PreprocessorSettings | None = None
37+
"""Setting for the preprocessor."""
38+
39+
2940
@workflow.step
3041
def atwork_subtour_frequency(
3142
state: workflow.State,
3243
tours: pd.DataFrame,
3344
persons_merged: pd.DataFrame,
45+
model_settings: AtworkSubtourFrequencySettings | None = None,
46+
model_settings_file_name: str = "atwork_subtour_frequency.yaml",
47+
trace_label: str = "atwork_subtour_frequency",
3448
) -> None:
3549
"""
3650
This model predicts the frequency of making at-work subtour tours
3751
(alternatives for this model come from a separate csv file which is
3852
configured by the user).
3953
"""
4054

41-
trace_label = "atwork_subtour_frequency"
42-
model_settings_file_name = "atwork_subtour_frequency.yaml"
4355
trace_hh_id = state.settings.trace_hh_id
4456
work_tours = tours[tours.tour_type == "work"]
4557

@@ -48,10 +60,15 @@ def atwork_subtour_frequency(
4860
add_null_results(state, trace_label, tours)
4961
return
5062

51-
model_settings = state.filesystem.read_model_settings(model_settings_file_name)
63+
if model_settings is None:
64+
model_settings = AtworkSubtourFrequencySettings.read_settings_file(
65+
state.filesystem,
66+
model_settings_file_name,
67+
)
68+
5269
estimator = estimation.manager.begin_estimation(state, "atwork_subtour_frequency")
5370

54-
model_spec = state.filesystem.read_model_spec(file_name=model_settings["SPEC"])
71+
model_spec = state.filesystem.read_model_spec(file_name=model_settings.SPEC)
5572
coefficients_df = state.filesystem.read_model_coefficients(model_settings)
5673
model_spec = simulate.eval_coefficients(
5774
state, model_spec, coefficients_df, estimator
@@ -72,7 +89,7 @@ def atwork_subtour_frequency(
7289
constants = config.get_model_constants(model_settings)
7390

7491
# - preprocessor
75-
preprocessor_settings = model_settings.get("preprocessor", None)
92+
preprocessor_settings = model_settings.preprocessor
7693
if preprocessor_settings:
7794
expressions.assign_columns(
7895
state,

activitysim/abm/models/atwork_subtour_mode_choice.py

Lines changed: 14 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99

1010
from activitysim.abm.models.util.mode import run_tour_mode_choice_simulate
1111
from activitysim.core import config, estimation, expressions, los, tracing, workflow
12+
from activitysim.core.configuration.logit import TourModeComponentSettings
1213
from activitysim.core.util import assign_in_place
1314

1415
logger = logging.getLogger(__name__)
@@ -20,19 +21,23 @@ def atwork_subtour_mode_choice(
2021
tours: pd.DataFrame,
2122
persons_merged: pd.DataFrame,
2223
network_los: los.Network_LOS,
24+
model_settings: TourModeComponentSettings | None = None,
25+
model_settings_file_name: str = "tour_mode_choice.yaml",
26+
trace_label: str = "atwork_subtour_mode_choice",
2327
) -> None:
2428
"""
2529
At-work subtour mode choice simulate
2630
"""
2731

28-
trace_label = "atwork_subtour_mode_choice"
29-
3032
trace_hh_id = state.settings.trace_hh_id
3133

32-
model_settings_file_name = "tour_mode_choice.yaml"
33-
model_settings = state.filesystem.read_model_settings(model_settings_file_name)
34+
if model_settings is None:
35+
model_settings = TourModeComponentSettings.read_settings_file(
36+
state.filesystem,
37+
model_settings_file_name,
38+
)
3439

35-
logsum_column_name = model_settings.get("MODE_CHOICE_LOGSUM_COLUMN_NAME")
40+
logsum_column_name = model_settings.MODE_CHOICE_LOGSUM_COLUMN_NAME
3641
mode_column_name = "tour_mode"
3742

3843
subtours = tours[tours.tour_category == "atwork"]
@@ -57,7 +62,7 @@ def atwork_subtour_mode_choice(
5762
)
5863

5964
constants = {}
60-
constants.update(config.get_model_constants(model_settings))
65+
constants.update(model_settings.CONSTANTS)
6166

6267
skim_dict = network_los.get_default_skim_dict()
6368

@@ -149,7 +154,7 @@ def atwork_subtour_mode_choice(
149154

150155
# add cached tvpb_logsum tap choices for modes specified in tvpb_mode_path_types
151156
if network_los.zone_system == los.THREE_ZONE:
152-
tvpb_mode_path_types = model_settings.get("tvpb_mode_path_types")
157+
tvpb_mode_path_types = model_settings.tvpb_mode_path_types
153158
for mode, path_types in tvpb_mode_path_types.items():
154159
for direction, skim in zip(
155160
["od", "do"], [tvpb_logsum_odt, tvpb_logsum_dot]
@@ -189,12 +194,12 @@ def atwork_subtour_mode_choice(
189194
state.add_table("tours", tours)
190195

191196
# - annotate tours table
192-
if model_settings.get("annotate_tours"):
197+
if model_settings.annotate_tours:
193198
tours = state.get_dataframe("tours")
194199
expressions.assign_columns(
195200
state,
196201
df=tours,
197-
model_settings=model_settings.get("annotate_tours"),
202+
model_settings=model_settings.annotate_tours,
198203
trace_label=tracing.extend_trace_label(trace_label, "annotate_tours"),
199204
)
200205
state.add_table("tours", tours)

0 commit comments

Comments
 (0)