Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
82 commits
Select commit Hold shift + click to select a range
d6ca6b0
Initial commit of baydag
dhensle May 6, 2025
789d1d9
running with cropped data and latest version of activitysim
dhensle May 7, 2025
307e1b6
removed transponder ownership, cleaned up write matrices
dhensle May 8, 2025
59cab01
changing model ordering
dhensle May 8, 2025
93ff0ff
removed driving age student person type
dhensle May 8, 2025
18b6b6e
remove unused external student models
dhensle May 8, 2025
f677a88
initial CI test setup
dhensle May 8, 2025
719d1ba
cloning main branch of activitysim instead of pip install
dhensle May 8, 2025
be92387
Trying different CI call
dhensle May 8, 2025
7921dd9
adding missed coverage install
dhensle May 8, 2025
d6e5a37
trying to capture output in GH actions
dhensle May 8, 2025
daa8819
calling python directly instead of coverage
dhensle May 8, 2025
a6e4bbc
initial tour mode choice changes
dhensle May 9, 2025
83e034f
trip mode choice initial mode changes
dhensle May 9, 2025
76d8347
CI tests looking at estimation_enhancements branch
dhensle May 15, 2025
7fdae00
running with metro synthetic population and sandag skims
dhensle Jun 2, 2025
57c2742
Adding metro visum stuff from Metro_ABM repo
lukegordon333 Jun 17, 2025
e383a58
initial implementation of park_and_ride_lot_choice
dhensle Jun 20, 2025
eef53c3
initial coding for tour mode choice with pnr
dhensle Jun 24, 2025
6e484c4
first pass at tour mode choice utility updates
dhensle Jun 24, 2025
2c5a554
KNR and other skimming scripts
lukegordon333 Jun 26, 2025
aa22f31
maz_walk_stop skims
ednaaguilar Jun 30, 2025
44822cd
updates to pnr configs with activitysim integration
dhensle Jun 30, 2025
a4711ba
turning on pnr with logsums
dhensle Jul 4, 2025
9c4c17d
matching landuse shape to skims shape
dhensle Jul 4, 2025
a047d78
Merge pull request #1 from RSGInc/pnr_modeling
dhensle Jul 4, 2025
5d8266c
Merge pull request #2 from RSGInc/ims_Luke
dhensle Jul 4, 2025
4866d6d
metro example with working skims
dhensle Jul 8, 2025
c6ab42e
Merge branch 'initial_model_setup' of https://github.com/RSGInc/SimOR…
dhensle Jul 8, 2025
54082e0
removed unused lcog configs, update README
dhensle Jul 8, 2025
8fbf839
CI Setup (#3)
dhensle Jul 18, 2025
a77ec8f
updating skimming scripts (#4)
dhensle Jul 18, 2025
3b69d0b
updating folder structure
dhensle Aug 6, 2025
e5e09c1
Update .gitignore
lukegordon333 Aug 19, 2025
918c1dc
deleted unneeded files + move xmls to config
lukegordon333 Aug 19, 2025
493edca
Update .gitignore
lukegordon333 Sep 15, 2025
d9cc59d
consistency across landuse, hhs, persons in synthetic pop
dhensle Sep 19, 2025
8c717ca
pnr config updates
dhensle Sep 29, 2025
1a09279
Merge branch 'initial_model_setup' into pnr_capacity
dhensle Sep 29, 2025
8d0f43b
maz duplicate fix and data paths update
tedlini Oct 2, 2025
6d15fd1
add missing pnr coeff
tedlini Oct 2, 2025
cb7ea5b
add pnr data with some placeholders in land_use.csv
tedlini Oct 7, 2025
6bad34c
add pnr capacity and cost in configs
tedlini Oct 7, 2025
0f24578
make work tours unavaiable for children 15 and under
tedlini Oct 7, 2025
f0d17fc
add missing trip purpose probabilities
tedlini Oct 7, 2025
dfdbffe
fix availability condition for micromobility modes
tedlini Oct 9, 2025
e1687e3
update pnr lot capacity in land_use.csv
tedlini Oct 16, 2025
888f908
fix parking cost unit conversion in trip mode choice
tedlini Oct 16, 2025
18c0bea
pnr config updates
dhensle Sep 29, 2025
d5ab26f
maz duplicate fix and data paths update
tedlini Oct 2, 2025
6b00b27
add missing pnr coeff
tedlini Oct 2, 2025
189a18c
add pnr data with some placeholders in land_use.csv
tedlini Oct 7, 2025
a15fe64
add pnr capacity and cost in configs
tedlini Oct 7, 2025
9530458
make work tours unavaiable for children 15 and under
tedlini Oct 7, 2025
972d1f5
add missing trip purpose probabilities
tedlini Oct 7, 2025
f9d2f24
fix availability condition for micromobility modes
tedlini Oct 9, 2025
854a9a0
update pnr lot capacity in land_use.csv
tedlini Oct 16, 2025
8c31adc
fix parking cost unit conversion in trip mode choice
tedlini Oct 16, 2025
bed43bb
Create .gitignore
lukegordon333 Oct 29, 2025
e755c8b
Create .gitignore
lukegordon333 Oct 29, 2025
e0fcef9
Merge branch 'file_cleanup' into initial_model_setup
lukegordon333 Oct 29, 2025
cd0255a
Update KNRConnectors_Setup.py
lukegordon333 Oct 29, 2025
3eb5565
adding support files for all streets network
lukegordon333 Oct 29, 2025
f5a032d
bike analysis scripts
ednaaguilar Nov 5, 2025
d52ed03
modify tour mc coeff to test pnr
tedlini Nov 11, 2025
b834f64
add stop type and vehicle type constants to transit utilities
tedlini Nov 11, 2025
b16ba6f
update pnr parking cost utility
tedlini Nov 11, 2025
def3d1e
Bike comfort model (#9)
ednaaguilar Dec 19, 2025
5fc320c
License holding model (#8)
ednaaguilar Dec 20, 2025
1227632
Use updated land use file and update MAZ/TAZ to new zone system
ednaaguilar Dec 24, 2025
8d6bae5
drop hh's with no MAZ match in new zones system
ednaaguilar Dec 24, 2025
7885ec9
Merge branch 'initial_model_setup' into pnr_capacity
dhensle Jan 12, 2026
80f4dc4
col name change
ednaaguilar Jan 15, 2026
68d0979
Merge branch 'pnr_capacity' of https://github.com/RSGInc/SimOR into p…
ednaaguilar Jan 15, 2026
a5dba1e
Add preprocessing script
ednaaguilar Jan 15, 2026
8df1f40
working with new landuse file
dhensle Jan 15, 2026
2b352e2
Merge branch 'pnr_capacity' of https://github.com/RSGInc/SimOR into p…
dhensle Jan 15, 2026
c81147a
recreating test example dataset using new landuse
dhensle Jan 15, 2026
69d9539
using sov dist for bike time
dhensle Jan 15, 2026
be00190
fixing CI test to use UV
dhensle Jan 15, 2026
86641dc
Merge pull request #10 from RSGInc/pnr_capacity
dhensle Jan 16, 2026
76cc470
use shadow pricing and update mp steps
dhensle Jan 16, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
55 changes: 55 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
name: CI Test for Cropped Example

on:
push:
branches:
- '**'
pull_request:
branches:
- '**'

jobs:
oregon_metro_cropped_example:
strategy:
matrix:
python-version:
- "3.10"
fail-fast: false

name: cropped-example-py${{ matrix.python-version }}
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v4

# Clone ActivitySim from SimOR_pnr branch
- name: Clone ActivitySim
run: |
cd ..
git clone --branch SimOR_pnr https://github.com/RSGInc/activitysim.git
pwd
ls -la

# Install UV
- name: Install UV
uses: astral-sh/setup-uv@v4
with:
version: "latest"

# Set up Python with UV
- name: Set up Python
run: uv python install ${{ matrix.python-version }}

# Install dependencies using UV and the lock file from ActivitySim
- name: Install dependencies with UV
run: |
cd ../activitysim
uv sync --frozen
uv pip install -e . --no-deps
uv pip list

# Run the test
- name: Run cropped example test
run: |
cd ../activitysim
uv run python $GITHUB_WORKSPACE/resident/test/test_cropped_dataset.py
16 changes: 16 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
*__pycache__*
.vscode/*
resident/model_data/metro/data_full/*
resident/outputs/*

# Unit test / coverage reports
htmlcov/
.tox/
.coverage
.cache
nosetests.xml
coverage.xml

Metro_outputs/

skimming_and_assignment/visum/*.ver
43 changes: 43 additions & 0 deletions .vscode/launch.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"name": "Run Full Example",
"type": "debugpy",
"request": "launch",
"cwd": "${workspaceFolder}/resident",
"program": "simulation.py",
"console": "integratedTerminal",
"args": [
"-c", "configs", // check the settings.yaml file for run settings!
"-d", "model_data/metro/data_full",
"-o", "outputs/full",
]
},
{
"name": "Run Cropped Example",
"type": "debugpy",
"request": "launch",
"cwd": "${workspaceFolder}/resident",
"program": "simulation.py",
"console": "integratedTerminal",
"args": [
"-c", "configs", // check the settings.yaml file for run settings!
"-d", "model_data/metro/data_cropped",
"-o", "outputs/cropped",
]
},
{
"name": "Run Preprocessor",
"type": "debugpy",
"request": "launch",
"cwd": "${workspaceFolder}/resident",
"program": "preprocessor.py",
"console": "integratedTerminal",
"args": "preprocessor_settings.yaml"
}
]
}
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
# SimOR
Simulate Oregon (SimOR) - Oregon's Jointly Estimated ActivitySim Model
![image](SimOR.png)

## Currently in development
To run the prototype mini example, download and install the correct fork of Activitysim from [here](https://github.com/RSGInc/activitysim/tree/SimOR_pnr) and follow the launch commands in the .vscode/launch.json file. This version of ActivitySim contains the necessary park-and-ride changes to run the SimOR model.
1 change: 1 addition & 0 deletions misc/bike_analysis/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
src/__pycache__/
241 changes: 241 additions & 0 deletions misc/bike_analysis/scripts/01_clean.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,241 @@
import pandas as pd
import numpy as np
import os
pd.set_option('display.max_columns', None)
os.chdir(os.path.dirname(os.path.dirname(__file__)))

# %%
# Directories
data_dir = 'data'
raw_dir = f'{data_dir}/raw'
interim_dir = f'{data_dir}/interim'
processed_dir = f'{data_dir}/processed'

# %%
# Load data
hh = pd.read_csv(f'{raw_dir}/ex_hh.csv')
# day = pd.read_csv(f'{raw_dir}/ex_day.csv')
person = pd.read_csv(f'{raw_dir}/ex_person.csv')
trips = pd.read_csv(f'{raw_dir}/ex_trip_unlinked.csv')
ltrips = pd.read_csv(f'{raw_dir}/ex_trip_linked.csv')

# %% Rename variables
ltrips.rename(
columns=
{'linked_trip_weight': 'ltrip_weight'},
inplace=True
)

# Re-map values to labels
person_mapping = {
'gender': {1: 'Female', 2: 'Male', 3:'Non-binary', 995:'Missing', 997:'Other', 999:'PNTA'},
'age': {1:'Under5', 2:'5-10', 3:'11-15', 4:'16-17', 5:'18-24', 6:'25-34', 7:'35-44', 8:'45-54', 9:'55-64', 10:'65-74', 11:'75-84', 12:'85plus'},
'bike_freq': {1: '6-7days', 2: '5days', 3: '4days', 4: '3days', 5: '2days', 6: '1day', 7: '1-3month', 8: '<1month', 996: 'Never', 995: 'Missing'},
'bike_attitude': {1:'PhysicallyUnable', 2:'DoesNotKnowHow', 3:'NotInterested', 4:'WantBikeLess', 5:'HappyWithCurrent', 6:'WantBikeMore', 995:'Missing'}
}

# Re-map to broader categories
bike_freq_map = {
'6-7days': 'VeryFrequent',
'5days': 'VeryFrequent',
'4days': 'VeryFrequent',
'3days': 'Occasional',
'2days': 'Occasional',
'1day': 'Occasional',
'1-3month': 'Infrequent',
'<1month': 'Infrequent',
'Never': 'Never',
'Missing': 'Missing'
}

bike_att_bin = {
'PhysicallyUnable': 'NotInterestedCant',
'DoesNotKnowHow': 'NotInterestedCant',
'NotInterested': 'NotInterestedCant',
'WantBikeLess': 'WantBikeLess',
'HappyWithCurrent': 'HappyWithCurrent',
'WantBikeMore': 'WantBikeMore',
'Missing': 'Missing'
}

income_bin = {
1: 'Low',
2: 'Low',
3: 'Mid',
4: 'Mid',
5: 'High',
6: 'High',
995: 'Missing',
999: 'Missing'
}

income_num = {
1: 25000,
2: 37500,
3: 62500,
4: 87500,
5: 150000,
6: 200000,
995: np.nan,
999: np.nan
}

age_bin = {
'Under5': 'Under10',
'5-10': 'Under10',
'11-15': '10-17',
'16-17': '10-17',
'18-24': '18-34',
'25-34': '18-34',
'35-44': '35-64',
'45-54': '35-64',
'55-64': '35-64',
'65-74': '65+',
'75-84': '65+',
'85plus': '65+'
}

age_num = {
'Under5': 2.5,
'5_10': 8,
'11-15': 13,
'16-17': 16,
'18-24': 21,
'25-34': 30,
'35-44': 40,
'45-54': 50,
'55-64': 60,
'65-74': 70,
'75-84': 80,
'85plus': 85
}

person['gender_num'] = np.where(
person['gender'].isin([1,3,997]), 1, # Female & Others
np.where(person['gender'] == 2, 2, 3) # Male and Missing
)
person['gender_lab'] = person['gender'].map(person_mapping['gender'])
person['gender_bin'] = person['gender_num'].map({1:'Female & Others', 2: 'Male', 3: 'Missing'})

person['age_lab'] = person['age'].map(person_mapping['age'])
person['age_bin'] = person['age_lab'].map(age_bin)
person['age_num'] = person['age_lab'].map(age_num)

person['bike_freq_lab'] = person['bike_freq'].map(person_mapping['bike_freq'])
person['bike_freq_bin'] = person['bike_freq_lab'].map(bike_freq_map)

person['bike_att'] = person['bike_attitude'].map(person_mapping['bike_attitude'])
person['bike_att_bin'] = person['bike_att'].map(bike_att_bin)

hh['income_bin'] = hh['income_broad'].map(income_bin)
hh['income_num'] = hh['income_broad'].map(income_num)

# %%
# Define comfort variable
comfort_cols = [col for col in person.columns if 'bike_comfort' in col]
likert_scale = {1:4, 2:3, 3:2, 4:1} # Reverse scoring for comfort
person[comfort_cols] = person[comfort_cols].replace(likert_scale)
person['avg_comfort'] = person[comfort_cols].replace(995, pd.NA).mean(axis=1, skipna=True).fillna(0)
comfort_labels = ['Uncomfortable', 'Comfortable']
person['comfort_bin'] = np.where(
person['avg_comfort'] >= 3, 'Comfortable',
np.where(person['avg_comfort'] != 0 , 'Uncomfortable', pd.NA)
)

# %%
# Identify complete rmove households (for comfort classification - 04_classify.py)
print(f"Total households: {hh['hh_weight'].sum():,.2f}")

hh_rmove = hh[hh['diary_platform'] == 'rmove']
print(f"Total households with rmove: {hh_rmove['hh_weight'].sum():,.2f}")

hh_rmove_complete = hh_rmove[hh_rmove['num_days_complete'] == 7]
print(f"Total number of households with 7 complete days: {hh_rmove_complete['hh_weight'].sum():,.2f}")
print(f"Total sample size of households with 7 complete days: {hh_rmove_complete.shape[0]:,}")

bike_complete_hh_ids = hh_rmove_complete['hh_id'].unique().tolist()

# Add flag to identify complete records
person['bike_complete_flag'] = 0
person.loc[person['hh_id'].isin(bike_complete_hh_ids), 'bike_complete_flag'] = 1

# %% Merge bike trips with person table
# Count bike trips
bike_trip_mask = (ltrips['linked_trip_mode'] == 11)
bike_trips= ltrips[bike_trip_mask].copy()
bike_counts = bike_trips.groupby('person_id').size().reset_index(name='bike_trips')

# Keep relevant cols
per_cols = ['hh_id', 'person_id', 'age_lab', 'age_bin', 'age_num',
'gender_bin', 'num_days_complete', 'bike_complete_flag',
'avg_comfort', 'comfort_bin',
'student', 'person_weight']
bike_cols_per = [col for col in person.columns if 'bike' in col]

# Merge bike trip info with person data
person_btrips = pd.merge(
person[per_cols + bike_cols_per],
bike_counts,
on='person_id',
how='left',
validate='1:1'
).fillna({'bike_trips': 0})

# Merge with hh data
hh_cols = ['hh_id', 'num_bicycle_adult', 'num_bicycle_child', 'income_broad', 'income_bin', 'income_num']
person_btrips = pd.merge(
person_btrips,
hh[hh_cols],
on = 'hh_id',
how = 'left'
)

# Add dummy if person reported bike trip
person_btrips['recorded_btrip'] = np.where(person_btrips['bike_trips'] > 0, 1, 0)

# %% Merge bike trips with person info
btrips_person = pd.merge(
bike_trips.drop(columns = ['hh_id']),
person[per_cols + bike_cols_per],
how = 'left',
on = 'person_id'
)

# Add hh data
btrips_person = pd.merge(
btrips_person,
hh[hh_cols],
how = 'left',
on = 'hh_id'
)

# Add bike type to bike trip info
ltrip_bike_ids = bike_trips['linked_trip_id'].to_list()
subset = trips[trips['linked_trip_id'].isin(ltrip_bike_ids)] # bike type is unlinked trip table

bike_modes = [2, 3, 4, 5, 69, 70, 82, 103, 300] # bike modes and hierarchy

def get_highest_bike_mode(modes):
found_bikes = [m for m in modes if m in bike_modes]
if found_bikes:
return max(found_bikes)
else:
return None

modes = subset.groupby('linked_trip_id')['mode_1'].agg(list).reset_index().rename(columns = {'mode_1':'mode_list'})
modes['bike_mode'] = modes['mode_list'].apply(get_highest_bike_mode)

# Add bike type
btrips_person = pd.merge(
btrips_person,
modes[['linked_trip_id', 'bike_mode']],
how = 'left',
on = 'linked_trip_id',
)

ebikes = [70, 82] # ebike from bikeshare, ebike in household
btrips_person['bike_type'] = np.where(btrips_person['bike_mode'].isin(ebikes), 'ebike', 'standard')

# %% Export
person_btrips.to_csv(f'{interim_dir}/person_btrips.csv', index=False)
btrips_person.to_csv(f'{interim_dir}/btrips_person.csv', index=False)
Loading