Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions activitysim/abm/models/__init__.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -22,12 +22,15 @@
from . import non_mandatory_destination
from . import non_mandatory_scheduling
from . import non_mandatory_tour_frequency
from . import parking_location_choice
from . import stop_frequency
from . import tour_mode_choice
from . import trip_destination
from . import trip_mode_choice
from . import trip_purpose
from . import trip_purpose_and_destination
from . import trip_scheduling
from . import trip_departure_choice
from . import trip_scheduling_choice
from . import trip_matrices
from . import summarize
311 changes: 311 additions & 0 deletions activitysim/abm/models/parking_location_choice.py
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,311 @@
# ActivitySim
# See full license in LICENSE.txt.
import logging

import numpy as np
import pandas as pd

from activitysim.core import config
from activitysim.core import inject
from activitysim.core import pipeline
from activitysim.core import simulate
from activitysim.core import tracing

from activitysim.core import expressions
from activitysim.core.interaction_sample_simulate import interaction_sample_simulate
from activitysim.core.logit import interaction_dataset
from activitysim.core.util import assign_in_place
from activitysim.core.tracing import print_elapsed_time

from .util import estimation


logger = logging.getLogger(__name__)

NO_DESTINATION = -1


def wrap_skims(model_settings):
"""
wrap skims of trip destination using origin, dest column names from model settings.
Various of these are used by destination_sample, compute_logsums, and destination_simulate
so we create them all here with canonical names.

Note that compute_logsums aliases their names so it can use the same equations to compute
logsums from origin to alt_dest, and from alt_dest to primarly destination

odt_skims - SkimStackWrapper: trip origin, trip alt_dest, time_of_day
dot_skims - SkimStackWrapper: trip alt_dest, trip origin, time_of_day
dpt_skims - SkimStackWrapper: trip alt_dest, trip primary_dest, time_of_day
pdt_skims - SkimStackWrapper: trip primary_dest,trip alt_dest, time_of_day
od_skims - SkimDictWrapper: trip origin, trip alt_dest
dp_skims - SkimDictWrapper: trip alt_dest, trip primary_dest

Parameters
----------
model_settings

Returns
-------
dict containing skims, keyed by canonical names relative to tour orientation
"""

network_los = inject.get_injectable('network_los')
skim_dict = network_los.get_default_skim_dict()

origin = model_settings['TRIP_ORIGIN']
park_zone = model_settings['ALT_DEST_COL_NAME']
destination = model_settings['TRIP_DESTINATION']
time_period = model_settings['TRIP_DEPARTURE_PERIOD']

skims = {
"odt_skims": skim_dict.wrap_3d(orig_key=origin, dest_key=destination, dim3_key=time_period),
"dot_skims": skim_dict.wrap_3d(orig_key=destination, dest_key=origin, dim3_key=time_period),
"opt_skims": skim_dict.wrap_3d(orig_key=origin, dest_key=park_zone, dim3_key=time_period),
"pdt_skims": skim_dict.wrap_3d(orig_key=park_zone, dest_key=destination, dim3_key=time_period),
"od_skims": skim_dict.wrap(origin, destination),
"do_skims": skim_dict.wrap(destination, origin),
"op_skims": skim_dict.wrap(origin, park_zone),
"pd_skims": skim_dict.wrap(park_zone, destination),
}

return skims


def get_spec_for_segment(model_settings, spec_name, segment):

omnibus_spec = simulate.read_model_spec(file_name=model_settings[spec_name])

spec = omnibus_spec[[segment]]

# might as well ignore any spec rows with 0 utility
spec = spec[spec.iloc[:, 0] != 0]
assert spec.shape[0] > 0

return spec


def parking_destination_simulate(
segment_name,
trips,
destination_sample,
model_settings,
skims,
chunk_size, trace_hh_id,
trace_label):
"""
Chose destination from destination_sample (with od_logsum and dp_logsum columns added)


Returns
-------
choices - pandas.Series
destination alt chosen
"""
trace_label = tracing.extend_trace_label(trace_label, 'trip_destination_simulate')

spec = get_spec_for_segment(model_settings, 'SPECIFICATION', segment_name)

alt_dest_col_name = model_settings['ALT_DEST_COL_NAME']

logger.info("Running trip_destination_simulate with %d trips", len(trips))

locals_dict = config.get_model_constants(model_settings).copy()
locals_dict.update(skims)

parking_locations = interaction_sample_simulate(
choosers=trips,
alternatives=destination_sample,
spec=spec,
choice_column=alt_dest_col_name,
want_logsums=False,
allow_zero_probs=True, zero_prob_choice_val=NO_DESTINATION,
skims=skims,
locals_d=locals_dict,
chunk_size=chunk_size,
trace_label=trace_label,
trace_choice_name='parking_loc')

# drop any failed zero_prob destinations
if (parking_locations == NO_DESTINATION).any():
logger.debug("dropping %s failed parking locations", (parking_locations == NO_DESTINATION).sum())
parking_locations = parking_locations[parking_locations != NO_DESTINATION]

return parking_locations


def choose_parking_location(
segment_name,
trips,
alternatives,
model_settings,
want_sample_table,
skims,
chunk_size, trace_hh_id,
trace_label):

logger.info("choose_parking_location %s with %d trips", trace_label, trips.shape[0])

t0 = print_elapsed_time()

alt_dest_col_name = model_settings['ALT_DEST_COL_NAME']
destination_sample = interaction_dataset(trips, alternatives, alt_index_id=alt_dest_col_name)
destination_sample.index = np.repeat(trips.index.values, len(alternatives))
destination_sample.index.name = trips.index.name
destination_sample = destination_sample[[alt_dest_col_name]].copy()

# # - trip_destination_simulate
destinations = parking_destination_simulate(
segment_name=segment_name,
trips=trips,
destination_sample=destination_sample,
model_settings=model_settings,
skims=skims,
chunk_size=chunk_size, trace_hh_id=trace_hh_id,
trace_label=trace_label)

if want_sample_table:
# FIXME - sample_table
destination_sample.set_index(model_settings['ALT_DEST_COL_NAME'], append=True, inplace=True)
else:
destination_sample = None

t0 = print_elapsed_time("%s.parking_location_simulate" % trace_label, t0)

return destinations, destination_sample


def run_parking_destination(
model_settings,
trips, land_use,
chunk_size, trace_hh_id,
trace_label,
fail_some_trips_for_testing=False):

chooser_filter_column = model_settings.get('CHOOSER_FILTER_COLUMN_NAME')
chooser_segment_column = model_settings.get('CHOOSER_SEGMENT_COLUMN_NAME')

parking_location_column_name = model_settings['ALT_DEST_COL_NAME']
sample_table_name = model_settings.get('DEST_CHOICE_SAMPLE_TABLE_NAME')
want_sample_table = config.setting('want_dest_choice_sample_tables') and sample_table_name is not None

choosers = trips[trips[chooser_filter_column]]
choosers = choosers.sort_index()

# Placeholder for trips without a parking choice
trips[parking_location_column_name] = -1

skims = wrap_skims(model_settings)

alt_column_filter_name = model_settings.get('ALTERNATIVE_FILTER_COLUMN_NAME')
alternatives = land_use[land_use[alt_column_filter_name]]

# don't need size terms in alternatives, just TAZ index
alternatives = alternatives.drop(alternatives.columns, axis=1)
alternatives.index.name = parking_location_column_name

choices_list = []
sample_list = []
for segment_name, chooser_segment in choosers.groupby(chooser_segment_column):
if chooser_segment.shape[0] == 0:
logger.info("%s skipping segment %s: no choosers", trace_label, segment_name)
continue

choices, destination_sample = choose_parking_location(
segment_name,
chooser_segment,
alternatives,
model_settings,
want_sample_table,
skims,
chunk_size, trace_hh_id,
trace_label=tracing.extend_trace_label(trace_label, segment_name))

choices_list.append(choices)
if want_sample_table:
assert destination_sample is not None
sample_list.append(destination_sample)

if len(choices_list) > 0:
parking_df = pd.concat(choices_list)

if fail_some_trips_for_testing:
parking_df = parking_df.drop(parking_df.index[0])

assign_in_place(trips, parking_df.to_frame(parking_location_column_name))
trips[parking_location_column_name] = trips[parking_location_column_name].fillna(-1)
else:
trips[parking_location_column_name] = -1

save_sample_df = pd.concat(sample_list) if len(sample_list) > 0 else None

return trips[parking_location_column_name], save_sample_df


@inject.step()
def parking_location(
trips,
trips_merged,
land_use,
network_los,
chunk_size,
trace_hh_id):
"""
Given a set of trips, each trip needs to have a parking location if
it is eligible for remote parking.
"""

trace_label = 'parking_location'
model_settings = config.read_model_settings('parking_location_choice.yaml')
alt_destination_col_name = model_settings['ALT_DEST_COL_NAME']

preprocessor_settings = model_settings.get('PREPROCESSOR', None)

trips_df = trips.to_frame()
trips_merged_df = trips_merged.to_frame()
land_use_df = land_use.to_frame()

locals_dict = {
'network_los': network_los
}
locals_dict.update(config.get_model_constants(model_settings))

if preprocessor_settings:
expressions.assign_columns(
df=trips_merged_df,
model_settings=preprocessor_settings,
locals_dict=locals_dict,
trace_label=trace_label)

parking_locations, save_sample_df = run_parking_destination(
model_settings,
trips_merged_df, land_use_df,
chunk_size=chunk_size,
trace_hh_id=trace_hh_id,
trace_label=trace_label,
)

assign_in_place(trips_df, parking_locations.to_frame(alt_destination_col_name))

pipeline.replace_table("trips", trips_df)

if trace_hh_id:
tracing.trace_df(trips_df,
label=trace_label,
slicer='trip_id',
index_label='trip_id',
warn_if_empty=True)

if save_sample_df is not None:
assert len(save_sample_df.index.get_level_values(0).unique()) == \
len(trips_df[trips_df.trip_num < trips_df.trip_count])

sample_table_name = model_settings.get('PARKING_LOCATION_SAMPLE_TABLE_NAME')
assert sample_table_name is not None

logger.info("adding %s samples to %s" % (len(save_sample_df), sample_table_name))

# lest they try to put tour samples into the same table
if pipeline.is_table(sample_table_name):
raise RuntimeError("sample table %s already exists" % sample_table_name)
pipeline.extend_table(sample_table_name, save_sample_df)
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions activitysim/abm/models/__init__.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -22,12 +22,15 @@
from . import non_mandatory_destination
from . import non_mandatory_scheduling
from . import non_mandatory_tour_frequency
from . import parking_location_choice
from . import stop_frequency
from . import tour_mode_choice
from . import trip_destination
from . import trip_mode_choice
from . import trip_purpose
from . import trip_purpose_and_destination
from . import trip_scheduling
from . import trip_departure_choice
from . import trip_scheduling_choice
from . import trip_matrices
from . import summarize
311 changes: 311 additions & 0 deletions activitysim/abm/models/parking_location_choice.py
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,311 @@
# ActivitySim
# See full license in LICENSE.txt.
import logging

import numpy as np
import pandas as pd

from activitysim.core import config
from activitysim.core import inject
from activitysim.core import pipeline
from activitysim.core import simulate
from activitysim.core import tracing

from activitysim.core import expressions
from activitysim.core.interaction_sample_simulate import interaction_sample_simulate
from activitysim.core.logit import interaction_dataset
from activitysim.core.util import assign_in_place
from activitysim.core.tracing import print_elapsed_time

from .util import estimation


logger = logging.getLogger(__name__)

NO_DESTINATION = -1


def wrap_skims(model_settings):
"""
wrap skims of trip destination using origin, dest column names from model settings.
Various of these are used by destination_sample, compute_logsums, and destination_simulate
so we create them all here with canonical names.

Note that compute_logsums aliases their names so it can use the same equations to compute
logsums from origin to alt_dest, and from alt_dest to primarly destination

odt_skims - SkimStackWrapper: trip origin, trip alt_dest, time_of_day
dot_skims - SkimStackWrapper: trip alt_dest, trip origin, time_of_day
dpt_skims - SkimStackWrapper: trip alt_dest, trip primary_dest, time_of_day
pdt_skims - SkimStackWrapper: trip primary_dest,trip alt_dest, time_of_day
od_skims - SkimDictWrapper: trip origin, trip alt_dest
dp_skims - SkimDictWrapper: trip alt_dest, trip primary_dest

Parameters
----------
model_settings

Returns
-------
dict containing skims, keyed by canonical names relative to tour orientation
"""

network_los = inject.get_injectable('network_los')
skim_dict = network_los.get_default_skim_dict()

origin = model_settings['TRIP_ORIGIN']
park_zone = model_settings['ALT_DEST_COL_NAME']
destination = model_settings['TRIP_DESTINATION']
time_period = model_settings['TRIP_DEPARTURE_PERIOD']

skims = {
"odt_skims": skim_dict.wrap_3d(orig_key=origin, dest_key=destination, dim3_key=time_period),
"dot_skims": skim_dict.wrap_3d(orig_key=destination, dest_key=origin, dim3_key=time_period),
"opt_skims": skim_dict.wrap_3d(orig_key=origin, dest_key=park_zone, dim3_key=time_period),
"pdt_skims": skim_dict.wrap_3d(orig_key=park_zone, dest_key=destination, dim3_key=time_period),
"od_skims": skim_dict.wrap(origin, destination),
"do_skims": skim_dict.wrap(destination, origin),
"op_skims": skim_dict.wrap(origin, park_zone),
"pd_skims": skim_dict.wrap(park_zone, destination),
}

return skims


def get_spec_for_segment(model_settings, spec_name, segment):

omnibus_spec = simulate.read_model_spec(file_name=model_settings[spec_name])

spec = omnibus_spec[[segment]]

# might as well ignore any spec rows with 0 utility
spec = spec[spec.iloc[:, 0] != 0]
assert spec.shape[0] > 0

return spec


def parking_destination_simulate(
segment_name,
trips,
destination_sample,
model_settings,
skims,
chunk_size, trace_hh_id,
trace_label):
"""
Chose destination from destination_sample (with od_logsum and dp_logsum columns added)


Returns
-------
choices - pandas.Series
destination alt chosen
"""
trace_label = tracing.extend_trace_label(trace_label, 'trip_destination_simulate')

spec = get_spec_for_segment(model_settings, 'SPECIFICATION', segment_name)

alt_dest_col_name = model_settings['ALT_DEST_COL_NAME']

logger.info("Running trip_destination_simulate with %d trips", len(trips))

locals_dict = config.get_model_constants(model_settings).copy()
locals_dict.update(skims)

parking_locations = interaction_sample_simulate(
choosers=trips,
alternatives=destination_sample,
spec=spec,
choice_column=alt_dest_col_name,
want_logsums=False,
allow_zero_probs=True, zero_prob_choice_val=NO_DESTINATION,
skims=skims,
locals_d=locals_dict,
chunk_size=chunk_size,
trace_label=trace_label,
trace_choice_name='parking_loc')

# drop any failed zero_prob destinations
if (parking_locations == NO_DESTINATION).any():
logger.debug("dropping %s failed parking locations", (parking_locations == NO_DESTINATION).sum())
parking_locations = parking_locations[parking_locations != NO_DESTINATION]

return parking_locations


def choose_parking_location(
segment_name,
trips,
alternatives,
model_settings,
want_sample_table,
skims,
chunk_size, trace_hh_id,
trace_label):

logger.info("choose_parking_location %s with %d trips", trace_label, trips.shape[0])

t0 = print_elapsed_time()

alt_dest_col_name = model_settings['ALT_DEST_COL_NAME']
destination_sample = interaction_dataset(trips, alternatives, alt_index_id=alt_dest_col_name)
destination_sample.index = np.repeat(trips.index.values, len(alternatives))
destination_sample.index.name = trips.index.name
destination_sample = destination_sample[[alt_dest_col_name]].copy()

# # - trip_destination_simulate
destinations = parking_destination_simulate(
segment_name=segment_name,
trips=trips,
destination_sample=destination_sample,
model_settings=model_settings,
skims=skims,
chunk_size=chunk_size, trace_hh_id=trace_hh_id,
trace_label=trace_label)

if want_sample_table:
# FIXME - sample_table
destination_sample.set_index(model_settings['ALT_DEST_COL_NAME'], append=True, inplace=True)
else:
destination_sample = None

t0 = print_elapsed_time("%s.parking_location_simulate" % trace_label, t0)

return destinations, destination_sample


def run_parking_destination(
model_settings,
trips, land_use,
chunk_size, trace_hh_id,
trace_label,
fail_some_trips_for_testing=False):

chooser_filter_column = model_settings.get('CHOOSER_FILTER_COLUMN_NAME')
chooser_segment_column = model_settings.get('CHOOSER_SEGMENT_COLUMN_NAME')

parking_location_column_name = model_settings['ALT_DEST_COL_NAME']
sample_table_name = model_settings.get('DEST_CHOICE_SAMPLE_TABLE_NAME')
want_sample_table = config.setting('want_dest_choice_sample_tables') and sample_table_name is not None

choosers = trips[trips[chooser_filter_column]]
choosers = choosers.sort_index()

# Placeholder for trips without a parking choice
trips[parking_location_column_name] = -1

skims = wrap_skims(model_settings)

alt_column_filter_name = model_settings.get('ALTERNATIVE_FILTER_COLUMN_NAME')
alternatives = land_use[land_use[alt_column_filter_name]]

# don't need size terms in alternatives, just TAZ index
alternatives = alternatives.drop(alternatives.columns, axis=1)
alternatives.index.name = parking_location_column_name

choices_list = []
sample_list = []
for segment_name, chooser_segment in choosers.groupby(chooser_segment_column):
if chooser_segment.shape[0] == 0:
logger.info("%s skipping segment %s: no choosers", trace_label, segment_name)
continue

choices, destination_sample = choose_parking_location(
segment_name,
chooser_segment,
alternatives,
model_settings,
want_sample_table,
skims,
chunk_size, trace_hh_id,
trace_label=tracing.extend_trace_label(trace_label, segment_name))

choices_list.append(choices)
if want_sample_table:
assert destination_sample is not None
sample_list.append(destination_sample)

if len(choices_list) > 0:
parking_df = pd.concat(choices_list)

if fail_some_trips_for_testing:
parking_df = parking_df.drop(parking_df.index[0])

assign_in_place(trips, parking_df.to_frame(parking_location_column_name))
trips[parking_location_column_name] = trips[parking_location_column_name].fillna(-1)
else:
trips[parking_location_column_name] = -1

save_sample_df = pd.concat(sample_list) if len(sample_list) > 0 else None

return trips[parking_location_column_name], save_sample_df


@inject.step()
def parking_location(
trips,
trips_merged,
land_use,
network_los,
chunk_size,
trace_hh_id):
"""
Given a set of trips, each trip needs to have a parking location if
it is eligible for remote parking.
"""

trace_label = 'parking_location'
model_settings = config.read_model_settings('parking_location_choice.yaml')
alt_destination_col_name = model_settings['ALT_DEST_COL_NAME']

preprocessor_settings = model_settings.get('PREPROCESSOR', None)

trips_df = trips.to_frame()
trips_merged_df = trips_merged.to_frame()
land_use_df = land_use.to_frame()

locals_dict = {
'network_los': network_los
}
locals_dict.update(config.get_model_constants(model_settings))

if preprocessor_settings:
expressions.assign_columns(
df=trips_merged_df,
model_settings=preprocessor_settings,
locals_dict=locals_dict,
trace_label=trace_label)

parking_locations, save_sample_df = run_parking_destination(
model_settings,
trips_merged_df, land_use_df,
chunk_size=chunk_size,
trace_hh_id=trace_hh_id,
trace_label=trace_label,
)

assign_in_place(trips_df, parking_locations.to_frame(alt_destination_col_name))

pipeline.replace_table("trips", trips_df)

if trace_hh_id:
tracing.trace_df(trips_df,
label=trace_label,
slicer='trip_id',
index_label='trip_id',
warn_if_empty=True)

if save_sample_df is not None:
assert len(save_sample_df.index.get_level_values(0).unique()) == \
len(trips_df[trips_df.trip_num < trips_df.trip_count])

sample_table_name = model_settings.get('PARKING_LOCATION_SAMPLE_TABLE_NAME')
assert sample_table_name is not None

logger.info("adding %s samples to %s" % (len(save_sample_df), sample_table_name))

# lest they try to put tour samples into the same table
if pipeline.is_table(sample_table_name):
raise RuntimeError("sample table %s already exists" % sample_table_name)
pipeline.extend_table(sample_table_name, save_sample_df)
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions activitysim/abm/models/__init__.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -22,12 +22,15 @@
from . import non_mandatory_destination
from . import non_mandatory_scheduling
from . import non_mandatory_tour_frequency
from . import parking_location_choice
from . import stop_frequency
from . import tour_mode_choice
from . import trip_destination
from . import trip_mode_choice
from . import trip_purpose
from . import trip_purpose_and_destination
from . import trip_scheduling
from . import trip_departure_choice
from . import trip_scheduling_choice
from . import trip_matrices
from . import summarize
311 changes: 311 additions & 0 deletions activitysim/abm/models/parking_location_choice.py
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,311 @@
# ActivitySim
# See full license in LICENSE.txt.
import logging

import numpy as np
import pandas as pd

from activitysim.core import config
from activitysim.core import inject
from activitysim.core import pipeline
from activitysim.core import simulate
from activitysim.core import tracing

from activitysim.core import expressions
from activitysim.core.interaction_sample_simulate import interaction_sample_simulate
from activitysim.core.logit import interaction_dataset
from activitysim.core.util import assign_in_place
from activitysim.core.tracing import print_elapsed_time

from .util import estimation


logger = logging.getLogger(__name__)

NO_DESTINATION = -1


def wrap_skims(model_settings):
"""
wrap skims of trip destination using origin, dest column names from model settings.
Various of these are used by destination_sample, compute_logsums, and destination_simulate
so we create them all here with canonical names.

Note that compute_logsums aliases their names so it can use the same equations to compute
logsums from origin to alt_dest, and from alt_dest to primarly destination

odt_skims - SkimStackWrapper: trip origin, trip alt_dest, time_of_day
dot_skims - SkimStackWrapper: trip alt_dest, trip origin, time_of_day
dpt_skims - SkimStackWrapper: trip alt_dest, trip primary_dest, time_of_day
pdt_skims - SkimStackWrapper: trip primary_dest,trip alt_dest, time_of_day
od_skims - SkimDictWrapper: trip origin, trip alt_dest
dp_skims - SkimDictWrapper: trip alt_dest, trip primary_dest

Parameters
----------
model_settings

Returns
-------
dict containing skims, keyed by canonical names relative to tour orientation
"""

network_los = inject.get_injectable('network_los')
skim_dict = network_los.get_default_skim_dict()

origin = model_settings['TRIP_ORIGIN']
park_zone = model_settings['ALT_DEST_COL_NAME']
destination = model_settings['TRIP_DESTINATION']
time_period = model_settings['TRIP_DEPARTURE_PERIOD']

skims = {
"odt_skims": skim_dict.wrap_3d(orig_key=origin, dest_key=destination, dim3_key=time_period),
"dot_skims": skim_dict.wrap_3d(orig_key=destination, dest_key=origin, dim3_key=time_period),
"opt_skims": skim_dict.wrap_3d(orig_key=origin, dest_key=park_zone, dim3_key=time_period),
"pdt_skims": skim_dict.wrap_3d(orig_key=park_zone, dest_key=destination, dim3_key=time_period),
"od_skims": skim_dict.wrap(origin, destination),
"do_skims": skim_dict.wrap(destination, origin),
"op_skims": skim_dict.wrap(origin, park_zone),
"pd_skims": skim_dict.wrap(park_zone, destination),
}

return skims


def get_spec_for_segment(model_settings, spec_name, segment):

omnibus_spec = simulate.read_model_spec(file_name=model_settings[spec_name])

spec = omnibus_spec[[segment]]

# might as well ignore any spec rows with 0 utility
spec = spec[spec.iloc[:, 0] != 0]
assert spec.shape[0] > 0

return spec


def parking_destination_simulate(
segment_name,
trips,
destination_sample,
model_settings,
skims,
chunk_size, trace_hh_id,
trace_label):
"""
Chose destination from destination_sample (with od_logsum and dp_logsum columns added)


Returns
-------
choices - pandas.Series
destination alt chosen
"""
trace_label = tracing.extend_trace_label(trace_label, 'trip_destination_simulate')

spec = get_spec_for_segment(model_settings, 'SPECIFICATION', segment_name)

alt_dest_col_name = model_settings['ALT_DEST_COL_NAME']

logger.info("Running trip_destination_simulate with %d trips", len(trips))

locals_dict = config.get_model_constants(model_settings).copy()
locals_dict.update(skims)

parking_locations = interaction_sample_simulate(
choosers=trips,
alternatives=destination_sample,
spec=spec,
choice_column=alt_dest_col_name,
want_logsums=False,
allow_zero_probs=True, zero_prob_choice_val=NO_DESTINATION,
skims=skims,
locals_d=locals_dict,
chunk_size=chunk_size,
trace_label=trace_label,
trace_choice_name='parking_loc')

# drop any failed zero_prob destinations
if (parking_locations == NO_DESTINATION).any():
logger.debug("dropping %s failed parking locations", (parking_locations == NO_DESTINATION).sum())
parking_locations = parking_locations[parking_locations != NO_DESTINATION]

return parking_locations


def choose_parking_location(
segment_name,
trips,
alternatives,
model_settings,
want_sample_table,
skims,
chunk_size, trace_hh_id,
trace_label):

logger.info("choose_parking_location %s with %d trips", trace_label, trips.shape[0])

t0 = print_elapsed_time()

alt_dest_col_name = model_settings['ALT_DEST_COL_NAME']
destination_sample = interaction_dataset(trips, alternatives, alt_index_id=alt_dest_col_name)
destination_sample.index = np.repeat(trips.index.values, len(alternatives))
destination_sample.index.name = trips.index.name
destination_sample = destination_sample[[alt_dest_col_name]].copy()

# # - trip_destination_simulate
destinations = parking_destination_simulate(
segment_name=segment_name,
trips=trips,
destination_sample=destination_sample,
model_settings=model_settings,
skims=skims,
chunk_size=chunk_size, trace_hh_id=trace_hh_id,
trace_label=trace_label)

if want_sample_table:
# FIXME - sample_table
destination_sample.set_index(model_settings['ALT_DEST_COL_NAME'], append=True, inplace=True)
else:
destination_sample = None

t0 = print_elapsed_time("%s.parking_location_simulate" % trace_label, t0)

return destinations, destination_sample


def run_parking_destination(
model_settings,
trips, land_use,
chunk_size, trace_hh_id,
trace_label,
fail_some_trips_for_testing=False):

chooser_filter_column = model_settings.get('CHOOSER_FILTER_COLUMN_NAME')
chooser_segment_column = model_settings.get('CHOOSER_SEGMENT_COLUMN_NAME')

parking_location_column_name = model_settings['ALT_DEST_COL_NAME']
sample_table_name = model_settings.get('DEST_CHOICE_SAMPLE_TABLE_NAME')
want_sample_table = config.setting('want_dest_choice_sample_tables') and sample_table_name is not None

choosers = trips[trips[chooser_filter_column]]
choosers = choosers.sort_index()

# Placeholder for trips without a parking choice
trips[parking_location_column_name] = -1

skims = wrap_skims(model_settings)

alt_column_filter_name = model_settings.get('ALTERNATIVE_FILTER_COLUMN_NAME')
alternatives = land_use[land_use[alt_column_filter_name]]

# don't need size terms in alternatives, just TAZ index
alternatives = alternatives.drop(alternatives.columns, axis=1)
alternatives.index.name = parking_location_column_name

choices_list = []
sample_list = []
for segment_name, chooser_segment in choosers.groupby(chooser_segment_column):
if chooser_segment.shape[0] == 0:
logger.info("%s skipping segment %s: no choosers", trace_label, segment_name)
continue

choices, destination_sample = choose_parking_location(
segment_name,
chooser_segment,
alternatives,
model_settings,
want_sample_table,
skims,
chunk_size, trace_hh_id,
trace_label=tracing.extend_trace_label(trace_label, segment_name))

choices_list.append(choices)
if want_sample_table:
assert destination_sample is not None
sample_list.append(destination_sample)

if len(choices_list) > 0:
parking_df = pd.concat(choices_list)

if fail_some_trips_for_testing:
parking_df = parking_df.drop(parking_df.index[0])

assign_in_place(trips, parking_df.to_frame(parking_location_column_name))
trips[parking_location_column_name] = trips[parking_location_column_name].fillna(-1)
else:
trips[parking_location_column_name] = -1

save_sample_df = pd.concat(sample_list) if len(sample_list) > 0 else None

return trips[parking_location_column_name], save_sample_df


@inject.step()
def parking_location(
trips,
trips_merged,
land_use,
network_los,
chunk_size,
trace_hh_id):
"""
Given a set of trips, each trip needs to have a parking location if
it is eligible for remote parking.
"""

trace_label = 'parking_location'
model_settings = config.read_model_settings('parking_location_choice.yaml')
alt_destination_col_name = model_settings['ALT_DEST_COL_NAME']

preprocessor_settings = model_settings.get('PREPROCESSOR', None)

trips_df = trips.to_frame()
trips_merged_df = trips_merged.to_frame()
land_use_df = land_use.to_frame()

locals_dict = {
'network_los': network_los
}
locals_dict.update(config.get_model_constants(model_settings))

if preprocessor_settings:
expressions.assign_columns(
df=trips_merged_df,
model_settings=preprocessor_settings,
locals_dict=locals_dict,
trace_label=trace_label)

parking_locations, save_sample_df = run_parking_destination(
model_settings,
trips_merged_df, land_use_df,
chunk_size=chunk_size,
trace_hh_id=trace_hh_id,
trace_label=trace_label,
)

assign_in_place(trips_df, parking_locations.to_frame(alt_destination_col_name))

pipeline.replace_table("trips", trips_df)

if trace_hh_id:
tracing.trace_df(trips_df,
label=trace_label,
slicer='trip_id',
index_label='trip_id',
warn_if_empty=True)

if save_sample_df is not None:
assert len(save_sample_df.index.get_level_values(0).unique()) == \
len(trips_df[trips_df.trip_num < trips_df.trip_count])

sample_table_name = model_settings.get('PARKING_LOCATION_SAMPLE_TABLE_NAME')
assert sample_table_name is not None

logger.info("adding %s samples to %s" % (len(save_sample_df), sample_table_name))

# lest they try to put tour samples into the same table
if pipeline.is_table(sample_table_name):
raise RuntimeError("sample table %s already exists" % sample_table_name)
pipeline.extend_table(sample_table_name, save_sample_df)
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions activitysim/abm/models/__init__.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -22,12 +22,15 @@
from . import non_mandatory_destination
from . import non_mandatory_scheduling
from . import non_mandatory_tour_frequency
from . import parking_location_choice
from . import stop_frequency
from . import tour_mode_choice
from . import trip_destination
from . import trip_mode_choice
from . import trip_purpose
from . import trip_purpose_and_destination
from . import trip_scheduling
from . import trip_departure_choice
from . import trip_scheduling_choice
from . import trip_matrices
from . import summarize
311 changes: 311 additions & 0 deletions activitysim/abm/models/parking_location_choice.py
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,311 @@
# ActivitySim
# See full license in LICENSE.txt.
import logging

import numpy as np
import pandas as pd

from activitysim.core import config
from activitysim.core import inject
from activitysim.core import pipeline
from activitysim.core import simulate
from activitysim.core import tracing

from activitysim.core import expressions
from activitysim.core.interaction_sample_simulate import interaction_sample_simulate
from activitysim.core.logit import interaction_dataset
from activitysim.core.util import assign_in_place
from activitysim.core.tracing import print_elapsed_time

from .util import estimation


logger = logging.getLogger(__name__)

NO_DESTINATION = -1


def wrap_skims(model_settings):
"""
wrap skims of trip destination using origin, dest column names from model settings.
Various of these are used by destination_sample, compute_logsums, and destination_simulate
so we create them all here with canonical names.

Note that compute_logsums aliases their names so it can use the same equations to compute
logsums from origin to alt_dest, and from alt_dest to primarly destination

odt_skims - SkimStackWrapper: trip origin, trip alt_dest, time_of_day
dot_skims - SkimStackWrapper: trip alt_dest, trip origin, time_of_day
dpt_skims - SkimStackWrapper: trip alt_dest, trip primary_dest, time_of_day
pdt_skims - SkimStackWrapper: trip primary_dest,trip alt_dest, time_of_day
od_skims - SkimDictWrapper: trip origin, trip alt_dest
dp_skims - SkimDictWrapper: trip alt_dest, trip primary_dest

Parameters
----------
model_settings

Returns
-------
dict containing skims, keyed by canonical names relative to tour orientation
"""

network_los = inject.get_injectable('network_los')
skim_dict = network_los.get_default_skim_dict()

origin = model_settings['TRIP_ORIGIN']
park_zone = model_settings['ALT_DEST_COL_NAME']
destination = model_settings['TRIP_DESTINATION']
time_period = model_settings['TRIP_DEPARTURE_PERIOD']

skims = {
"odt_skims": skim_dict.wrap_3d(orig_key=origin, dest_key=destination, dim3_key=time_period),
"dot_skims": skim_dict.wrap_3d(orig_key=destination, dest_key=origin, dim3_key=time_period),
"opt_skims": skim_dict.wrap_3d(orig_key=origin, dest_key=park_zone, dim3_key=time_period),
"pdt_skims": skim_dict.wrap_3d(orig_key=park_zone, dest_key=destination, dim3_key=time_period),
"od_skims": skim_dict.wrap(origin, destination),
"do_skims": skim_dict.wrap(destination, origin),
"op_skims": skim_dict.wrap(origin, park_zone),
"pd_skims": skim_dict.wrap(park_zone, destination),
}

return skims


def get_spec_for_segment(model_settings, spec_name, segment):

omnibus_spec = simulate.read_model_spec(file_name=model_settings[spec_name])

spec = omnibus_spec[[segment]]

# might as well ignore any spec rows with 0 utility
spec = spec[spec.iloc[:, 0] != 0]
assert spec.shape[0] > 0

return spec


def parking_destination_simulate(
segment_name,
trips,
destination_sample,
model_settings,
skims,
chunk_size, trace_hh_id,
trace_label):
"""
Chose destination from destination_sample (with od_logsum and dp_logsum columns added)


Returns
-------
choices - pandas.Series
destination alt chosen
"""
trace_label = tracing.extend_trace_label(trace_label, 'trip_destination_simulate')

spec = get_spec_for_segment(model_settings, 'SPECIFICATION', segment_name)

alt_dest_col_name = model_settings['ALT_DEST_COL_NAME']

logger.info("Running trip_destination_simulate with %d trips", len(trips))

locals_dict = config.get_model_constants(model_settings).copy()
locals_dict.update(skims)

parking_locations = interaction_sample_simulate(
choosers=trips,
alternatives=destination_sample,
spec=spec,
choice_column=alt_dest_col_name,
want_logsums=False,
allow_zero_probs=True, zero_prob_choice_val=NO_DESTINATION,
skims=skims,
locals_d=locals_dict,
chunk_size=chunk_size,
trace_label=trace_label,
trace_choice_name='parking_loc')

# drop any failed zero_prob destinations
if (parking_locations == NO_DESTINATION).any():
logger.debug("dropping %s failed parking locations", (parking_locations == NO_DESTINATION).sum())
parking_locations = parking_locations[parking_locations != NO_DESTINATION]

return parking_locations


def choose_parking_location(
segment_name,
trips,
alternatives,
model_settings,
want_sample_table,
skims,
chunk_size, trace_hh_id,
trace_label):

logger.info("choose_parking_location %s with %d trips", trace_label, trips.shape[0])

t0 = print_elapsed_time()

alt_dest_col_name = model_settings['ALT_DEST_COL_NAME']
destination_sample = interaction_dataset(trips, alternatives, alt_index_id=alt_dest_col_name)
destination_sample.index = np.repeat(trips.index.values, len(alternatives))
destination_sample.index.name = trips.index.name
destination_sample = destination_sample[[alt_dest_col_name]].copy()

# # - trip_destination_simulate
destinations = parking_destination_simulate(
segment_name=segment_name,
trips=trips,
destination_sample=destination_sample,
model_settings=model_settings,
skims=skims,
chunk_size=chunk_size, trace_hh_id=trace_hh_id,
trace_label=trace_label)

if want_sample_table:
# FIXME - sample_table
destination_sample.set_index(model_settings['ALT_DEST_COL_NAME'], append=True, inplace=True)
else:
destination_sample = None

t0 = print_elapsed_time("%s.parking_location_simulate" % trace_label, t0)

return destinations, destination_sample


def run_parking_destination(
model_settings,
trips, land_use,
chunk_size, trace_hh_id,
trace_label,
fail_some_trips_for_testing=False):

chooser_filter_column = model_settings.get('CHOOSER_FILTER_COLUMN_NAME')
chooser_segment_column = model_settings.get('CHOOSER_SEGMENT_COLUMN_NAME')

parking_location_column_name = model_settings['ALT_DEST_COL_NAME']
sample_table_name = model_settings.get('DEST_CHOICE_SAMPLE_TABLE_NAME')
want_sample_table = config.setting('want_dest_choice_sample_tables') and sample_table_name is not None

choosers = trips[trips[chooser_filter_column]]
choosers = choosers.sort_index()

# Placeholder for trips without a parking choice
trips[parking_location_column_name] = -1

skims = wrap_skims(model_settings)

alt_column_filter_name = model_settings.get('ALTERNATIVE_FILTER_COLUMN_NAME')
alternatives = land_use[land_use[alt_column_filter_name]]

# don't need size terms in alternatives, just TAZ index
alternatives = alternatives.drop(alternatives.columns, axis=1)
alternatives.index.name = parking_location_column_name

choices_list = []
sample_list = []
for segment_name, chooser_segment in choosers.groupby(chooser_segment_column):
if chooser_segment.shape[0] == 0:
logger.info("%s skipping segment %s: no choosers", trace_label, segment_name)
continue

choices, destination_sample = choose_parking_location(
segment_name,
chooser_segment,
alternatives,
model_settings,
want_sample_table,
skims,
chunk_size, trace_hh_id,
trace_label=tracing.extend_trace_label(trace_label, segment_name))

choices_list.append(choices)
if want_sample_table:
assert destination_sample is not None
sample_list.append(destination_sample)

if len(choices_list) > 0:
parking_df = pd.concat(choices_list)

if fail_some_trips_for_testing:
parking_df = parking_df.drop(parking_df.index[0])

assign_in_place(trips, parking_df.to_frame(parking_location_column_name))
trips[parking_location_column_name] = trips[parking_location_column_name].fillna(-1)
else:
trips[parking_location_column_name] = -1

save_sample_df = pd.concat(sample_list) if len(sample_list) > 0 else None

return trips[parking_location_column_name], save_sample_df


@inject.step()
def parking_location(
trips,
trips_merged,
land_use,
network_los,
chunk_size,
trace_hh_id):
"""
Given a set of trips, each trip needs to have a parking location if
it is eligible for remote parking.
"""

trace_label = 'parking_location'
model_settings = config.read_model_settings('parking_location_choice.yaml')
alt_destination_col_name = model_settings['ALT_DEST_COL_NAME']

preprocessor_settings = model_settings.get('PREPROCESSOR', None)

trips_df = trips.to_frame()
trips_merged_df = trips_merged.to_frame()
land_use_df = land_use.to_frame()

locals_dict = {
'network_los': network_los
}
locals_dict.update(config.get_model_constants(model_settings))

if preprocessor_settings:
expressions.assign_columns(
df=trips_merged_df,
model_settings=preprocessor_settings,
locals_dict=locals_dict,
trace_label=trace_label)

parking_locations, save_sample_df = run_parking_destination(
model_settings,
trips_merged_df, land_use_df,
chunk_size=chunk_size,
trace_hh_id=trace_hh_id,
trace_label=trace_label,
)

assign_in_place(trips_df, parking_locations.to_frame(alt_destination_col_name))

pipeline.replace_table("trips", trips_df)

if trace_hh_id:
tracing.trace_df(trips_df,
label=trace_label,
slicer='trip_id',
index_label='trip_id',
warn_if_empty=True)

if save_sample_df is not None:
assert len(save_sample_df.index.get_level_values(0).unique()) == \
len(trips_df[trips_df.trip_num < trips_df.trip_count])

sample_table_name = model_settings.get('PARKING_LOCATION_SAMPLE_TABLE_NAME')
assert sample_table_name is not None

logger.info("adding %s samples to %s" % (len(save_sample_df), sample_table_name))

# lest they try to put tour samples into the same table
if pipeline.is_table(sample_table_name):
raise RuntimeError("sample table %s already exists" % sample_table_name)
pipeline.extend_table(sample_table_name, save_sample_df)
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions activitysim/abm/models/__init__.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -22,12 +22,15 @@
from . import non_mandatory_destination
from . import non_mandatory_scheduling
from . import non_mandatory_tour_frequency
from . import parking_location_choice
from . import stop_frequency
from . import tour_mode_choice
from . import trip_destination
from . import trip_mode_choice
from . import trip_purpose
from . import trip_purpose_and_destination
from . import trip_scheduling
from . import trip_departure_choice
from . import trip_scheduling_choice
from . import trip_matrices
from . import summarize
311 changes: 311 additions & 0 deletions activitysim/abm/models/parking_location_choice.py
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,311 @@
# ActivitySim
# See full license in LICENSE.txt.
import logging

import numpy as np
import pandas as pd

from activitysim.core import config
from activitysim.core import inject
from activitysim.core import pipeline
from activitysim.core import simulate
from activitysim.core import tracing

from activitysim.core import expressions
from activitysim.core.interaction_sample_simulate import interaction_sample_simulate
from activitysim.core.logit import interaction_dataset
from activitysim.core.util import assign_in_place
from activitysim.core.tracing import print_elapsed_time

from .util import estimation


logger = logging.getLogger(__name__)

NO_DESTINATION = -1


def wrap_skims(model_settings):
"""
wrap skims of trip destination using origin, dest column names from model settings.
Various of these are used by destination_sample, compute_logsums, and destination_simulate
so we create them all here with canonical names.

Note that compute_logsums aliases their names so it can use the same equations to compute
logsums from origin to alt_dest, and from alt_dest to primarly destination

odt_skims - SkimStackWrapper: trip origin, trip alt_dest, time_of_day
dot_skims - SkimStackWrapper: trip alt_dest, trip origin, time_of_day
dpt_skims - SkimStackWrapper: trip alt_dest, trip primary_dest, time_of_day
pdt_skims - SkimStackWrapper: trip primary_dest,trip alt_dest, time_of_day
od_skims - SkimDictWrapper: trip origin, trip alt_dest
dp_skims - SkimDictWrapper: trip alt_dest, trip primary_dest

Parameters
----------
model_settings

Returns
-------
dict containing skims, keyed by canonical names relative to tour orientation
"""

network_los = inject.get_injectable('network_los')
skim_dict = network_los.get_default_skim_dict()

origin = model_settings['TRIP_ORIGIN']
park_zone = model_settings['ALT_DEST_COL_NAME']
destination = model_settings['TRIP_DESTINATION']
time_period = model_settings['TRIP_DEPARTURE_PERIOD']

skims = {
"odt_skims": skim_dict.wrap_3d(orig_key=origin, dest_key=destination, dim3_key=time_period),
"dot_skims": skim_dict.wrap_3d(orig_key=destination, dest_key=origin, dim3_key=time_period),
"opt_skims": skim_dict.wrap_3d(orig_key=origin, dest_key=park_zone, dim3_key=time_period),
"pdt_skims": skim_dict.wrap_3d(orig_key=park_zone, dest_key=destination, dim3_key=time_period),
"od_skims": skim_dict.wrap(origin, destination),
"do_skims": skim_dict.wrap(destination, origin),
"op_skims": skim_dict.wrap(origin, park_zone),
"pd_skims": skim_dict.wrap(park_zone, destination),
}

return skims


def get_spec_for_segment(model_settings, spec_name, segment):

omnibus_spec = simulate.read_model_spec(file_name=model_settings[spec_name])

spec = omnibus_spec[[segment]]

# might as well ignore any spec rows with 0 utility
spec = spec[spec.iloc[:, 0] != 0]
assert spec.shape[0] > 0

return spec


def parking_destination_simulate(
segment_name,
trips,
destination_sample,
model_settings,
skims,
chunk_size, trace_hh_id,
trace_label):
"""
Chose destination from destination_sample (with od_logsum and dp_logsum columns added)


Returns
-------
choices - pandas.Series
destination alt chosen
"""
trace_label = tracing.extend_trace_label(trace_label, 'trip_destination_simulate')

spec = get_spec_for_segment(model_settings, 'SPECIFICATION', segment_name)

alt_dest_col_name = model_settings['ALT_DEST_COL_NAME']

logger.info("Running trip_destination_simulate with %d trips", len(trips))

locals_dict = config.get_model_constants(model_settings).copy()
locals_dict.update(skims)

parking_locations = interaction_sample_simulate(
choosers=trips,
alternatives=destination_sample,
spec=spec,
choice_column=alt_dest_col_name,
want_logsums=False,
allow_zero_probs=True, zero_prob_choice_val=NO_DESTINATION,
skims=skims,
locals_d=locals_dict,
chunk_size=chunk_size,
trace_label=trace_label,
trace_choice_name='parking_loc')

# drop any failed zero_prob destinations
if (parking_locations == NO_DESTINATION).any():
logger.debug("dropping %s failed parking locations", (parking_locations == NO_DESTINATION).sum())
parking_locations = parking_locations[parking_locations != NO_DESTINATION]

return parking_locations


def choose_parking_location(
segment_name,
trips,
alternatives,
model_settings,
want_sample_table,
skims,
chunk_size, trace_hh_id,
trace_label):

logger.info("choose_parking_location %s with %d trips", trace_label, trips.shape[0])

t0 = print_elapsed_time()

alt_dest_col_name = model_settings['ALT_DEST_COL_NAME']
destination_sample = interaction_dataset(trips, alternatives, alt_index_id=alt_dest_col_name)
destination_sample.index = np.repeat(trips.index.values, len(alternatives))
destination_sample.index.name = trips.index.name
destination_sample = destination_sample[[alt_dest_col_name]].copy()

# # - trip_destination_simulate
destinations = parking_destination_simulate(
segment_name=segment_name,
trips=trips,
destination_sample=destination_sample,
model_settings=model_settings,
skims=skims,
chunk_size=chunk_size, trace_hh_id=trace_hh_id,
trace_label=trace_label)

if want_sample_table:
# FIXME - sample_table
destination_sample.set_index(model_settings['ALT_DEST_COL_NAME'], append=True, inplace=True)
else:
destination_sample = None

t0 = print_elapsed_time("%s.parking_location_simulate" % trace_label, t0)

return destinations, destination_sample


def run_parking_destination(
model_settings,
trips, land_use,
chunk_size, trace_hh_id,
trace_label,
fail_some_trips_for_testing=False):

chooser_filter_column = model_settings.get('CHOOSER_FILTER_COLUMN_NAME')
chooser_segment_column = model_settings.get('CHOOSER_SEGMENT_COLUMN_NAME')

parking_location_column_name = model_settings['ALT_DEST_COL_NAME']
sample_table_name = model_settings.get('DEST_CHOICE_SAMPLE_TABLE_NAME')
want_sample_table = config.setting('want_dest_choice_sample_tables') and sample_table_name is not None

choosers = trips[trips[chooser_filter_column]]
choosers = choosers.sort_index()

# Placeholder for trips without a parking choice
trips[parking_location_column_name] = -1

skims = wrap_skims(model_settings)

alt_column_filter_name = model_settings.get('ALTERNATIVE_FILTER_COLUMN_NAME')
alternatives = land_use[land_use[alt_column_filter_name]]

# don't need size terms in alternatives, just TAZ index
alternatives = alternatives.drop(alternatives.columns, axis=1)
alternatives.index.name = parking_location_column_name

choices_list = []
sample_list = []
for segment_name, chooser_segment in choosers.groupby(chooser_segment_column):
if chooser_segment.shape[0] == 0:
logger.info("%s skipping segment %s: no choosers", trace_label, segment_name)
continue

choices, destination_sample = choose_parking_location(
segment_name,
chooser_segment,
alternatives,
model_settings,
want_sample_table,
skims,
chunk_size, trace_hh_id,
trace_label=tracing.extend_trace_label(trace_label, segment_name))

choices_list.append(choices)
if want_sample_table:
assert destination_sample is not None
sample_list.append(destination_sample)

if len(choices_list) > 0:
parking_df = pd.concat(choices_list)

if fail_some_trips_for_testing:
parking_df = parking_df.drop(parking_df.index[0])

assign_in_place(trips, parking_df.to_frame(parking_location_column_name))
trips[parking_location_column_name] = trips[parking_location_column_name].fillna(-1)
else:
trips[parking_location_column_name] = -1

save_sample_df = pd.concat(sample_list) if len(sample_list) > 0 else None

return trips[parking_location_column_name], save_sample_df


@inject.step()
def parking_location(
trips,
trips_merged,
land_use,
network_los,
chunk_size,
trace_hh_id):
"""
Given a set of trips, each trip needs to have a parking location if
it is eligible for remote parking.
"""

trace_label = 'parking_location'
model_settings = config.read_model_settings('parking_location_choice.yaml')
alt_destination_col_name = model_settings['ALT_DEST_COL_NAME']

preprocessor_settings = model_settings.get('PREPROCESSOR', None)

trips_df = trips.to_frame()
trips_merged_df = trips_merged.to_frame()
land_use_df = land_use.to_frame()

locals_dict = {
'network_los': network_los
}
locals_dict.update(config.get_model_constants(model_settings))

if preprocessor_settings:
expressions.assign_columns(
df=trips_merged_df,
model_settings=preprocessor_settings,
locals_dict=locals_dict,
trace_label=trace_label)

parking_locations, save_sample_df = run_parking_destination(
model_settings,
trips_merged_df, land_use_df,
chunk_size=chunk_size,
trace_hh_id=trace_hh_id,
trace_label=trace_label,
)

assign_in_place(trips_df, parking_locations.to_frame(alt_destination_col_name))

pipeline.replace_table("trips", trips_df)

if trace_hh_id:
tracing.trace_df(trips_df,
label=trace_label,
slicer='trip_id',
index_label='trip_id',
warn_if_empty=True)

if save_sample_df is not None:
assert len(save_sample_df.index.get_level_values(0).unique()) == \
len(trips_df[trips_df.trip_num < trips_df.trip_count])

sample_table_name = model_settings.get('PARKING_LOCATION_SAMPLE_TABLE_NAME')
assert sample_table_name is not None

logger.info("adding %s samples to %s" % (len(save_sample_df), sample_table_name))

# lest they try to put tour samples into the same table
if pipeline.is_table(sample_table_name):
raise RuntimeError("sample table %s already exists" % sample_table_name)
pipeline.extend_table(sample_table_name, save_sample_df)
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions activitysim/abm/models/__init__.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -22,12 +22,15 @@
from . import non_mandatory_destination
from . import non_mandatory_scheduling
from . import non_mandatory_tour_frequency
from . import parking_location_choice
from . import stop_frequency
from . import tour_mode_choice
from . import trip_destination
from . import trip_mode_choice
from . import trip_purpose
from . import trip_purpose_and_destination
from . import trip_scheduling
from . import trip_departure_choice
from . import trip_scheduling_choice
from . import trip_matrices
from . import summarize
311 changes: 311 additions & 0 deletions activitysim/abm/models/parking_location_choice.py
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,311 @@
# ActivitySim
# See full license in LICENSE.txt.
import logging

import numpy as np
import pandas as pd

from activitysim.core import config
from activitysim.core import inject
from activitysim.core import pipeline
from activitysim.core import simulate
from activitysim.core import tracing

from activitysim.core import expressions
from activitysim.core.interaction_sample_simulate import interaction_sample_simulate
from activitysim.core.logit import interaction_dataset
from activitysim.core.util import assign_in_place
from activitysim.core.tracing import print_elapsed_time

from .util import estimation


logger = logging.getLogger(__name__)

NO_DESTINATION = -1


def wrap_skims(model_settings):
"""
wrap skims of trip destination using origin, dest column names from model settings.
Various of these are used by destination_sample, compute_logsums, and destination_simulate
so we create them all here with canonical names.

Note that compute_logsums aliases their names so it can use the same equations to compute
logsums from origin to alt_dest, and from alt_dest to primarly destination

odt_skims - SkimStackWrapper: trip origin, trip alt_dest, time_of_day
dot_skims - SkimStackWrapper: trip alt_dest, trip origin, time_of_day
dpt_skims - SkimStackWrapper: trip alt_dest, trip primary_dest, time_of_day
pdt_skims - SkimStackWrapper: trip primary_dest,trip alt_dest, time_of_day
od_skims - SkimDictWrapper: trip origin, trip alt_dest
dp_skims - SkimDictWrapper: trip alt_dest, trip primary_dest

Parameters
----------
model_settings

Returns
-------
dict containing skims, keyed by canonical names relative to tour orientation
"""

network_los = inject.get_injectable('network_los')
skim_dict = network_los.get_default_skim_dict()

origin = model_settings['TRIP_ORIGIN']
park_zone = model_settings['ALT_DEST_COL_NAME']
destination = model_settings['TRIP_DESTINATION']
time_period = model_settings['TRIP_DEPARTURE_PERIOD']

skims = {
"odt_skims": skim_dict.wrap_3d(orig_key=origin, dest_key=destination, dim3_key=time_period),
"dot_skims": skim_dict.wrap_3d(orig_key=destination, dest_key=origin, dim3_key=time_period),
"opt_skims": skim_dict.wrap_3d(orig_key=origin, dest_key=park_zone, dim3_key=time_period),
"pdt_skims": skim_dict.wrap_3d(orig_key=park_zone, dest_key=destination, dim3_key=time_period),
"od_skims": skim_dict.wrap(origin, destination),
"do_skims": skim_dict.wrap(destination, origin),
"op_skims": skim_dict.wrap(origin, park_zone),
"pd_skims": skim_dict.wrap(park_zone, destination),
}

return skims


def get_spec_for_segment(model_settings, spec_name, segment):

omnibus_spec = simulate.read_model_spec(file_name=model_settings[spec_name])

spec = omnibus_spec[[segment]]

# might as well ignore any spec rows with 0 utility
spec = spec[spec.iloc[:, 0] != 0]
assert spec.shape[0] > 0

return spec


def parking_destination_simulate(
segment_name,
trips,
destination_sample,
model_settings,
skims,
chunk_size, trace_hh_id,
trace_label):
"""
Chose destination from destination_sample (with od_logsum and dp_logsum columns added)


Returns
-------
choices - pandas.Series
destination alt chosen
"""
trace_label = tracing.extend_trace_label(trace_label, 'trip_destination_simulate')

spec = get_spec_for_segment(model_settings, 'SPECIFICATION', segment_name)

alt_dest_col_name = model_settings['ALT_DEST_COL_NAME']

logger.info("Running trip_destination_simulate with %d trips", len(trips))

locals_dict = config.get_model_constants(model_settings).copy()
locals_dict.update(skims)

parking_locations = interaction_sample_simulate(
choosers=trips,
alternatives=destination_sample,
spec=spec,
choice_column=alt_dest_col_name,
want_logsums=False,
allow_zero_probs=True, zero_prob_choice_val=NO_DESTINATION,
skims=skims,
locals_d=locals_dict,
chunk_size=chunk_size,
trace_label=trace_label,
trace_choice_name='parking_loc')

# drop any failed zero_prob destinations
if (parking_locations == NO_DESTINATION).any():
logger.debug("dropping %s failed parking locations", (parking_locations == NO_DESTINATION).sum())
parking_locations = parking_locations[parking_locations != NO_DESTINATION]

return parking_locations


def choose_parking_location(
segment_name,
trips,
alternatives,
model_settings,
want_sample_table,
skims,
chunk_size, trace_hh_id,
trace_label):

logger.info("choose_parking_location %s with %d trips", trace_label, trips.shape[0])

t0 = print_elapsed_time()

alt_dest_col_name = model_settings['ALT_DEST_COL_NAME']
destination_sample = interaction_dataset(trips, alternatives, alt_index_id=alt_dest_col_name)
destination_sample.index = np.repeat(trips.index.values, len(alternatives))
destination_sample.index.name = trips.index.name
destination_sample = destination_sample[[alt_dest_col_name]].copy()

# # - trip_destination_simulate
destinations = parking_destination_simulate(
segment_name=segment_name,
trips=trips,
destination_sample=destination_sample,
model_settings=model_settings,
skims=skims,
chunk_size=chunk_size, trace_hh_id=trace_hh_id,
trace_label=trace_label)

if want_sample_table:
# FIXME - sample_table
destination_sample.set_index(model_settings['ALT_DEST_COL_NAME'], append=True, inplace=True)
else:
destination_sample = None

t0 = print_elapsed_time("%s.parking_location_simulate" % trace_label, t0)

return destinations, destination_sample


def run_parking_destination(
model_settings,
trips, land_use,
chunk_size, trace_hh_id,
trace_label,
fail_some_trips_for_testing=False):

chooser_filter_column = model_settings.get('CHOOSER_FILTER_COLUMN_NAME')
chooser_segment_column = model_settings.get('CHOOSER_SEGMENT_COLUMN_NAME')

parking_location_column_name = model_settings['ALT_DEST_COL_NAME']
sample_table_name = model_settings.get('DEST_CHOICE_SAMPLE_TABLE_NAME')
want_sample_table = config.setting('want_dest_choice_sample_tables') and sample_table_name is not None

choosers = trips[trips[chooser_filter_column]]
choosers = choosers.sort_index()

# Placeholder for trips without a parking choice
trips[parking_location_column_name] = -1

skims = wrap_skims(model_settings)

alt_column_filter_name = model_settings.get('ALTERNATIVE_FILTER_COLUMN_NAME')
alternatives = land_use[land_use[alt_column_filter_name]]

# don't need size terms in alternatives, just TAZ index
alternatives = alternatives.drop(alternatives.columns, axis=1)
alternatives.index.name = parking_location_column_name

choices_list = []
sample_list = []
for segment_name, chooser_segment in choosers.groupby(chooser_segment_column):
if chooser_segment.shape[0] == 0:
logger.info("%s skipping segment %s: no choosers", trace_label, segment_name)
continue

choices, destination_sample = choose_parking_location(
segment_name,
chooser_segment,
alternatives,
model_settings,
want_sample_table,
skims,
chunk_size, trace_hh_id,
trace_label=tracing.extend_trace_label(trace_label, segment_name))

choices_list.append(choices)
if want_sample_table:
assert destination_sample is not None
sample_list.append(destination_sample)

if len(choices_list) > 0:
parking_df = pd.concat(choices_list)

if fail_some_trips_for_testing:
parking_df = parking_df.drop(parking_df.index[0])

assign_in_place(trips, parking_df.to_frame(parking_location_column_name))
trips[parking_location_column_name] = trips[parking_location_column_name].fillna(-1)
else:
trips[parking_location_column_name] = -1

save_sample_df = pd.concat(sample_list) if len(sample_list) > 0 else None

return trips[parking_location_column_name], save_sample_df


@inject.step()
def parking_location(
trips,
trips_merged,
land_use,
network_los,
chunk_size,
trace_hh_id):
"""
Given a set of trips, each trip needs to have a parking location if
it is eligible for remote parking.
"""

trace_label = 'parking_location'
model_settings = config.read_model_settings('parking_location_choice.yaml')
alt_destination_col_name = model_settings['ALT_DEST_COL_NAME']

preprocessor_settings = model_settings.get('PREPROCESSOR', None)

trips_df = trips.to_frame()
trips_merged_df = trips_merged.to_frame()
land_use_df = land_use.to_frame()

locals_dict = {
'network_los': network_los
}
locals_dict.update(config.get_model_constants(model_settings))

if preprocessor_settings:
expressions.assign_columns(
df=trips_merged_df,
model_settings=preprocessor_settings,
locals_dict=locals_dict,
trace_label=trace_label)

parking_locations, save_sample_df = run_parking_destination(
model_settings,
trips_merged_df, land_use_df,
chunk_size=chunk_size,
trace_hh_id=trace_hh_id,
trace_label=trace_label,
)

assign_in_place(trips_df, parking_locations.to_frame(alt_destination_col_name))

pipeline.replace_table("trips", trips_df)

if trace_hh_id:
tracing.trace_df(trips_df,
label=trace_label,
slicer='trip_id',
index_label='trip_id',
warn_if_empty=True)

if save_sample_df is not None:
assert len(save_sample_df.index.get_level_values(0).unique()) == \
len(trips_df[trips_df.trip_num < trips_df.trip_count])

sample_table_name = model_settings.get('PARKING_LOCATION_SAMPLE_TABLE_NAME')
assert sample_table_name is not None

logger.info("adding %s samples to %s" % (len(save_sample_df), sample_table_name))

# lest they try to put tour samples into the same table
if pipeline.is_table(sample_table_name):
raise RuntimeError("sample table %s already exists" % sample_table_name)
pipeline.extend_table(sample_table_name, save_sample_df)
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions activitysim/abm/models/__init__.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -22,12 +22,15 @@
from . import non_mandatory_destination
from . import non_mandatory_scheduling
from . import non_mandatory_tour_frequency
from . import parking_location_choice
from . import stop_frequency
from . import tour_mode_choice
from . import trip_destination
from . import trip_mode_choice
from . import trip_purpose
from . import trip_purpose_and_destination
from . import trip_scheduling
from . import trip_departure_choice
from . import trip_scheduling_choice
from . import trip_matrices
from . import summarize
311 changes: 311 additions & 0 deletions activitysim/abm/models/parking_location_choice.py
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,311 @@
# ActivitySim
# See full license in LICENSE.txt.
import logging

import numpy as np
import pandas as pd

from activitysim.core import config
from activitysim.core import inject
from activitysim.core import pipeline
from activitysim.core import simulate
from activitysim.core import tracing

from activitysim.core import expressions
from activitysim.core.interaction_sample_simulate import interaction_sample_simulate
from activitysim.core.logit import interaction_dataset
from activitysim.core.util import assign_in_place
from activitysim.core.tracing import print_elapsed_time

from .util import estimation


logger = logging.getLogger(__name__)

NO_DESTINATION = -1


def wrap_skims(model_settings):
"""
wrap skims of trip destination using origin, dest column names from model settings.
Various of these are used by destination_sample, compute_logsums, and destination_simulate
so we create them all here with canonical names.

Note that compute_logsums aliases their names so it can use the same equations to compute
logsums from origin to alt_dest, and from alt_dest to primarly destination

odt_skims - SkimStackWrapper: trip origin, trip alt_dest, time_of_day
dot_skims - SkimStackWrapper: trip alt_dest, trip origin, time_of_day
dpt_skims - SkimStackWrapper: trip alt_dest, trip primary_dest, time_of_day
pdt_skims - SkimStackWrapper: trip primary_dest,trip alt_dest, time_of_day
od_skims - SkimDictWrapper: trip origin, trip alt_dest
dp_skims - SkimDictWrapper: trip alt_dest, trip primary_dest

Parameters
----------
model_settings

Returns
-------
dict containing skims, keyed by canonical names relative to tour orientation
"""

network_los = inject.get_injectable('network_los')
skim_dict = network_los.get_default_skim_dict()

origin = model_settings['TRIP_ORIGIN']
park_zone = model_settings['ALT_DEST_COL_NAME']
destination = model_settings['TRIP_DESTINATION']
time_period = model_settings['TRIP_DEPARTURE_PERIOD']

skims = {
"odt_skims": skim_dict.wrap_3d(orig_key=origin, dest_key=destination, dim3_key=time_period),
"dot_skims": skim_dict.wrap_3d(orig_key=destination, dest_key=origin, dim3_key=time_period),
"opt_skims": skim_dict.wrap_3d(orig_key=origin, dest_key=park_zone, dim3_key=time_period),
"pdt_skims": skim_dict.wrap_3d(orig_key=park_zone, dest_key=destination, dim3_key=time_period),
"od_skims": skim_dict.wrap(origin, destination),
"do_skims": skim_dict.wrap(destination, origin),
"op_skims": skim_dict.wrap(origin, park_zone),
"pd_skims": skim_dict.wrap(park_zone, destination),
}

return skims


def get_spec_for_segment(model_settings, spec_name, segment):

omnibus_spec = simulate.read_model_spec(file_name=model_settings[spec_name])

spec = omnibus_spec[[segment]]

# might as well ignore any spec rows with 0 utility
spec = spec[spec.iloc[:, 0] != 0]
assert spec.shape[0] > 0

return spec


def parking_destination_simulate(
segment_name,
trips,
destination_sample,
model_settings,
skims,
chunk_size, trace_hh_id,
trace_label):
"""
Chose destination from destination_sample (with od_logsum and dp_logsum columns added)


Returns
-------
choices - pandas.Series
destination alt chosen
"""
trace_label = tracing.extend_trace_label(trace_label, 'trip_destination_simulate')

spec = get_spec_for_segment(model_settings, 'SPECIFICATION', segment_name)

alt_dest_col_name = model_settings['ALT_DEST_COL_NAME']

logger.info("Running trip_destination_simulate with %d trips", len(trips))

locals_dict = config.get_model_constants(model_settings).copy()
locals_dict.update(skims)

parking_locations = interaction_sample_simulate(
choosers=trips,
alternatives=destination_sample,
spec=spec,
choice_column=alt_dest_col_name,
want_logsums=False,
allow_zero_probs=True, zero_prob_choice_val=NO_DESTINATION,
skims=skims,
locals_d=locals_dict,
chunk_size=chunk_size,
trace_label=trace_label,
trace_choice_name='parking_loc')

# drop any failed zero_prob destinations
if (parking_locations == NO_DESTINATION).any():
logger.debug("dropping %s failed parking locations", (parking_locations == NO_DESTINATION).sum())
parking_locations = parking_locations[parking_locations != NO_DESTINATION]

return parking_locations


def choose_parking_location(
segment_name,
trips,
alternatives,
model_settings,
want_sample_table,
skims,
chunk_size, trace_hh_id,
trace_label):

logger.info("choose_parking_location %s with %d trips", trace_label, trips.shape[0])

t0 = print_elapsed_time()

alt_dest_col_name = model_settings['ALT_DEST_COL_NAME']
destination_sample = interaction_dataset(trips, alternatives, alt_index_id=alt_dest_col_name)
destination_sample.index = np.repeat(trips.index.values, len(alternatives))
destination_sample.index.name = trips.index.name
destination_sample = destination_sample[[alt_dest_col_name]].copy()

# # - trip_destination_simulate
destinations = parking_destination_simulate(
segment_name=segment_name,
trips=trips,
destination_sample=destination_sample,
model_settings=model_settings,
skims=skims,
chunk_size=chunk_size, trace_hh_id=trace_hh_id,
trace_label=trace_label)

if want_sample_table:
# FIXME - sample_table
destination_sample.set_index(model_settings['ALT_DEST_COL_NAME'], append=True, inplace=True)
else:
destination_sample = None

t0 = print_elapsed_time("%s.parking_location_simulate" % trace_label, t0)

return destinations, destination_sample


def run_parking_destination(
model_settings,
trips, land_use,
chunk_size, trace_hh_id,
trace_label,
fail_some_trips_for_testing=False):

chooser_filter_column = model_settings.get('CHOOSER_FILTER_COLUMN_NAME')
chooser_segment_column = model_settings.get('CHOOSER_SEGMENT_COLUMN_NAME')

parking_location_column_name = model_settings['ALT_DEST_COL_NAME']
sample_table_name = model_settings.get('DEST_CHOICE_SAMPLE_TABLE_NAME')
want_sample_table = config.setting('want_dest_choice_sample_tables') and sample_table_name is not None

choosers = trips[trips[chooser_filter_column]]
choosers = choosers.sort_index()

# Placeholder for trips without a parking choice
trips[parking_location_column_name] = -1

skims = wrap_skims(model_settings)

alt_column_filter_name = model_settings.get('ALTERNATIVE_FILTER_COLUMN_NAME')
alternatives = land_use[land_use[alt_column_filter_name]]

# don't need size terms in alternatives, just TAZ index
alternatives = alternatives.drop(alternatives.columns, axis=1)
alternatives.index.name = parking_location_column_name

choices_list = []
sample_list = []
for segment_name, chooser_segment in choosers.groupby(chooser_segment_column):
if chooser_segment.shape[0] == 0:
logger.info("%s skipping segment %s: no choosers", trace_label, segment_name)
continue

choices, destination_sample = choose_parking_location(
segment_name,
chooser_segment,
alternatives,
model_settings,
want_sample_table,
skims,
chunk_size, trace_hh_id,
trace_label=tracing.extend_trace_label(trace_label, segment_name))

choices_list.append(choices)
if want_sample_table:
assert destination_sample is not None
sample_list.append(destination_sample)

if len(choices_list) > 0:
parking_df = pd.concat(choices_list)

if fail_some_trips_for_testing:
parking_df = parking_df.drop(parking_df.index[0])

assign_in_place(trips, parking_df.to_frame(parking_location_column_name))
trips[parking_location_column_name] = trips[parking_location_column_name].fillna(-1)
else:
trips[parking_location_column_name] = -1

save_sample_df = pd.concat(sample_list) if len(sample_list) > 0 else None

return trips[parking_location_column_name], save_sample_df


@inject.step()
def parking_location(
trips,
trips_merged,
land_use,
network_los,
chunk_size,
trace_hh_id):
"""
Given a set of trips, each trip needs to have a parking location if
it is eligible for remote parking.
"""

trace_label = 'parking_location'
model_settings = config.read_model_settings('parking_location_choice.yaml')
alt_destination_col_name = model_settings['ALT_DEST_COL_NAME']

preprocessor_settings = model_settings.get('PREPROCESSOR', None)

trips_df = trips.to_frame()
trips_merged_df = trips_merged.to_frame()
land_use_df = land_use.to_frame()

locals_dict = {
'network_los': network_los
}
locals_dict.update(config.get_model_constants(model_settings))

if preprocessor_settings:
expressions.assign_columns(
df=trips_merged_df,
model_settings=preprocessor_settings,
locals_dict=locals_dict,
trace_label=trace_label)

parking_locations, save_sample_df = run_parking_destination(
model_settings,
trips_merged_df, land_use_df,
chunk_size=chunk_size,
trace_hh_id=trace_hh_id,
trace_label=trace_label,
)

assign_in_place(trips_df, parking_locations.to_frame(alt_destination_col_name))

pipeline.replace_table("trips", trips_df)

if trace_hh_id:
tracing.trace_df(trips_df,
label=trace_label,
slicer='trip_id',
index_label='trip_id',
warn_if_empty=True)

if save_sample_df is not None:
assert len(save_sample_df.index.get_level_values(0).unique()) == \
len(trips_df[trips_df.trip_num < trips_df.trip_count])

sample_table_name = model_settings.get('PARKING_LOCATION_SAMPLE_TABLE_NAME')
assert sample_table_name is not None

logger.info("adding %s samples to %s" % (len(save_sample_df), sample_table_name))

# lest they try to put tour samples into the same table
if pipeline.is_table(sample_table_name):
raise RuntimeError("sample table %s already exists" % sample_table_name)
pipeline.extend_table(sample_table_name, save_sample_df)
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions activitysim/abm/models/__init__.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -22,12 +22,15 @@
from . import non_mandatory_destination
from . import non_mandatory_scheduling
from . import non_mandatory_tour_frequency
from . import parking_location_choice
from . import stop_frequency
from . import tour_mode_choice
from . import trip_destination
from . import trip_mode_choice
from . import trip_purpose
from . import trip_purpose_and_destination
from . import trip_scheduling
from . import trip_departure_choice
from . import trip_scheduling_choice
from . import trip_matrices
from . import summarize
311 changes: 311 additions & 0 deletions activitysim/abm/models/parking_location_choice.py
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,311 @@
# ActivitySim
# See full license in LICENSE.txt.
import logging

import numpy as np
import pandas as pd

from activitysim.core import config
from activitysim.core import inject
from activitysim.core import pipeline
from activitysim.core import simulate
from activitysim.core import tracing

from activitysim.core import expressions
from activitysim.core.interaction_sample_simulate import interaction_sample_simulate
from activitysim.core.logit import interaction_dataset
from activitysim.core.util import assign_in_place
from activitysim.core.tracing import print_elapsed_time

from .util import estimation


logger = logging.getLogger(__name__)

NO_DESTINATION = -1


def wrap_skims(model_settings):
"""
wrap skims of trip destination using origin, dest column names from model settings.
Various of these are used by destination_sample, compute_logsums, and destination_simulate
so we create them all here with canonical names.

Note that compute_logsums aliases their names so it can use the same equations to compute
logsums from origin to alt_dest, and from alt_dest to primarly destination

odt_skims - SkimStackWrapper: trip origin, trip alt_dest, time_of_day
dot_skims - SkimStackWrapper: trip alt_dest, trip origin, time_of_day
dpt_skims - SkimStackWrapper: trip alt_dest, trip primary_dest, time_of_day
pdt_skims - SkimStackWrapper: trip primary_dest,trip alt_dest, time_of_day
od_skims - SkimDictWrapper: trip origin, trip alt_dest
dp_skims - SkimDictWrapper: trip alt_dest, trip primary_dest

Parameters
----------
model_settings

Returns
-------
dict containing skims, keyed by canonical names relative to tour orientation
"""

network_los = inject.get_injectable('network_los')
skim_dict = network_los.get_default_skim_dict()

origin = model_settings['TRIP_ORIGIN']
park_zone = model_settings['ALT_DEST_COL_NAME']
destination = model_settings['TRIP_DESTINATION']
time_period = model_settings['TRIP_DEPARTURE_PERIOD']

skims = {
"odt_skims": skim_dict.wrap_3d(orig_key=origin, dest_key=destination, dim3_key=time_period),
"dot_skims": skim_dict.wrap_3d(orig_key=destination, dest_key=origin, dim3_key=time_period),
"opt_skims": skim_dict.wrap_3d(orig_key=origin, dest_key=park_zone, dim3_key=time_period),
"pdt_skims": skim_dict.wrap_3d(orig_key=park_zone, dest_key=destination, dim3_key=time_period),
"od_skims": skim_dict.wrap(origin, destination),
"do_skims": skim_dict.wrap(destination, origin),
"op_skims": skim_dict.wrap(origin, park_zone),
"pd_skims": skim_dict.wrap(park_zone, destination),
}

return skims


def get_spec_for_segment(model_settings, spec_name, segment):

omnibus_spec = simulate.read_model_spec(file_name=model_settings[spec_name])

spec = omnibus_spec[[segment]]

# might as well ignore any spec rows with 0 utility
spec = spec[spec.iloc[:, 0] != 0]
assert spec.shape[0] > 0

return spec


def parking_destination_simulate(
segment_name,
trips,
destination_sample,
model_settings,
skims,
chunk_size, trace_hh_id,
trace_label):
"""
Chose destination from destination_sample (with od_logsum and dp_logsum columns added)


Returns
-------
choices - pandas.Series
destination alt chosen
"""
trace_label = tracing.extend_trace_label(trace_label, 'trip_destination_simulate')

spec = get_spec_for_segment(model_settings, 'SPECIFICATION', segment_name)

alt_dest_col_name = model_settings['ALT_DEST_COL_NAME']

logger.info("Running trip_destination_simulate with %d trips", len(trips))

locals_dict = config.get_model_constants(model_settings).copy()
locals_dict.update(skims)

parking_locations = interaction_sample_simulate(
choosers=trips,
alternatives=destination_sample,
spec=spec,
choice_column=alt_dest_col_name,
want_logsums=False,
allow_zero_probs=True, zero_prob_choice_val=NO_DESTINATION,
skims=skims,
locals_d=locals_dict,
chunk_size=chunk_size,
trace_label=trace_label,
trace_choice_name='parking_loc')

# drop any failed zero_prob destinations
if (parking_locations == NO_DESTINATION).any():
logger.debug("dropping %s failed parking locations", (parking_locations == NO_DESTINATION).sum())
parking_locations = parking_locations[parking_locations != NO_DESTINATION]

return parking_locations


def choose_parking_location(
segment_name,
trips,
alternatives,
model_settings,
want_sample_table,
skims,
chunk_size, trace_hh_id,
trace_label):

logger.info("choose_parking_location %s with %d trips", trace_label, trips.shape[0])

t0 = print_elapsed_time()

alt_dest_col_name = model_settings['ALT_DEST_COL_NAME']
destination_sample = interaction_dataset(trips, alternatives, alt_index_id=alt_dest_col_name)
destination_sample.index = np.repeat(trips.index.values, len(alternatives))
destination_sample.index.name = trips.index.name
destination_sample = destination_sample[[alt_dest_col_name]].copy()

# # - trip_destination_simulate
destinations = parking_destination_simulate(
segment_name=segment_name,
trips=trips,
destination_sample=destination_sample,
model_settings=model_settings,
skims=skims,
chunk_size=chunk_size, trace_hh_id=trace_hh_id,
trace_label=trace_label)

if want_sample_table:
# FIXME - sample_table
destination_sample.set_index(model_settings['ALT_DEST_COL_NAME'], append=True, inplace=True)
else:
destination_sample = None

t0 = print_elapsed_time("%s.parking_location_simulate" % trace_label, t0)

return destinations, destination_sample


def run_parking_destination(
model_settings,
trips, land_use,
chunk_size, trace_hh_id,
trace_label,
fail_some_trips_for_testing=False):

chooser_filter_column = model_settings.get('CHOOSER_FILTER_COLUMN_NAME')
chooser_segment_column = model_settings.get('CHOOSER_SEGMENT_COLUMN_NAME')

parking_location_column_name = model_settings['ALT_DEST_COL_NAME']
sample_table_name = model_settings.get('DEST_CHOICE_SAMPLE_TABLE_NAME')
want_sample_table = config.setting('want_dest_choice_sample_tables') and sample_table_name is not None

choosers = trips[trips[chooser_filter_column]]
choosers = choosers.sort_index()

# Placeholder for trips without a parking choice
trips[parking_location_column_name] = -1

skims = wrap_skims(model_settings)

alt_column_filter_name = model_settings.get('ALTERNATIVE_FILTER_COLUMN_NAME')
alternatives = land_use[land_use[alt_column_filter_name]]

# don't need size terms in alternatives, just TAZ index
alternatives = alternatives.drop(alternatives.columns, axis=1)
alternatives.index.name = parking_location_column_name

choices_list = []
sample_list = []
for segment_name, chooser_segment in choosers.groupby(chooser_segment_column):
if chooser_segment.shape[0] == 0:
logger.info("%s skipping segment %s: no choosers", trace_label, segment_name)
continue

choices, destination_sample = choose_parking_location(
segment_name,
chooser_segment,
alternatives,
model_settings,
want_sample_table,
skims,
chunk_size, trace_hh_id,
trace_label=tracing.extend_trace_label(trace_label, segment_name))

choices_list.append(choices)
if want_sample_table:
assert destination_sample is not None
sample_list.append(destination_sample)

if len(choices_list) > 0:
parking_df = pd.concat(choices_list)

if fail_some_trips_for_testing:
parking_df = parking_df.drop(parking_df.index[0])

assign_in_place(trips, parking_df.to_frame(parking_location_column_name))
trips[parking_location_column_name] = trips[parking_location_column_name].fillna(-1)
else:
trips[parking_location_column_name] = -1

save_sample_df = pd.concat(sample_list) if len(sample_list) > 0 else None

return trips[parking_location_column_name], save_sample_df


@inject.step()
def parking_location(
trips,
trips_merged,
land_use,
network_los,
chunk_size,
trace_hh_id):
"""
Given a set of trips, each trip needs to have a parking location if
it is eligible for remote parking.
"""

trace_label = 'parking_location'
model_settings = config.read_model_settings('parking_location_choice.yaml')
alt_destination_col_name = model_settings['ALT_DEST_COL_NAME']

preprocessor_settings = model_settings.get('PREPROCESSOR', None)

trips_df = trips.to_frame()
trips_merged_df = trips_merged.to_frame()
land_use_df = land_use.to_frame()

locals_dict = {
'network_los': network_los
}
locals_dict.update(config.get_model_constants(model_settings))

if preprocessor_settings:
expressions.assign_columns(
df=trips_merged_df,
model_settings=preprocessor_settings,
locals_dict=locals_dict,
trace_label=trace_label)

parking_locations, save_sample_df = run_parking_destination(
model_settings,
trips_merged_df, land_use_df,
chunk_size=chunk_size,
trace_hh_id=trace_hh_id,
trace_label=trace_label,
)

assign_in_place(trips_df, parking_locations.to_frame(alt_destination_col_name))

pipeline.replace_table("trips", trips_df)

if trace_hh_id:
tracing.trace_df(trips_df,
label=trace_label,
slicer='trip_id',
index_label='trip_id',
warn_if_empty=True)

if save_sample_df is not None:
assert len(save_sample_df.index.get_level_values(0).unique()) == \
len(trips_df[trips_df.trip_num < trips_df.trip_count])

sample_table_name = model_settings.get('PARKING_LOCATION_SAMPLE_TABLE_NAME')
assert sample_table_name is not None

logger.info("adding %s samples to %s" % (len(save_sample_df), sample_table_name))

# lest they try to put tour samples into the same table
if pipeline.is_table(sample_table_name):
raise RuntimeError("sample table %s already exists" % sample_table_name)
pipeline.extend_table(sample_table_name, save_sample_df)
Loading