Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
53 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
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
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
  •  
  •  
  •  
67 changes: 67 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
name: CI Test for Cropped Example

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

jobs:
oregon_metro_mini_example:
env:
conda-env-prefix: /usr/share/miniconda3/envs/conda-abm_dev
label: linux-64
strategy:
matrix:
region:
- oregon_metro
python-version:
- "3.10"
fail-fast: false
defaults:
run:
shell: bash -l {0}

name: ${{ matrix.region }}-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
tree -L 2

- name: Setup Miniforge
uses: conda-incubator/setup-miniconda@v3
with:
auto-update-conda: true
miniforge-version: latest
mamba-version: "2.0.5"
conda-solver: classic
conda-remove-defaults: true
environment-file: ../activitysim/conda-environments/github-actions-tests.yml
activate-environment: asim-test
python-version: ${{ matrix.python-version }}

# Install cloned fork of ActivitySim
- name: Install ActivitySim in editable mode
run: |
pwd
cd ../activitysim
python -m pip install -e . --no-deps
cd ..
cd SimOR
ls
conda info -a
conda list

# Run the test
- name: Run cropped example test
run: python 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
34 changes: 34 additions & 0 deletions .vscode/launch.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
{
// 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/test",
]
}
]
}
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