Skip to content

Commit

Permalink
v0.4.1
Browse files Browse the repository at this point in the history
  • Loading branch information
xyluo25 committed Mar 31, 2024
1 parent 409cc28 commit 14b8ec7
Show file tree
Hide file tree
Showing 8 changed files with 284 additions and 124 deletions.
2 changes: 1 addition & 1 deletion grid2demand/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
from .utils_lib.pkg_settings import pkg_settings
from ._grid2demand import GRID2DEMAND

print('grid2demand, version 0.4.0')
print('grid2demand, version 0.4.1')


__all__ = ["read_node", "read_poi", "read_network",
Expand Down
221 changes: 154 additions & 67 deletions grid2demand/_grid2demand.py

Large diffs are not rendered by default.

11 changes: 8 additions & 3 deletions grid2demand/func_lib/gen_agent_demand.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,10 @@


def gen_agent_based_demand(node_dict: dict, zone_dict: dict,
path_demand: str = "", df_demand: pd.DataFrame = "",
agent_type: str = "v") -> pd.DataFrame:
path_demand: str = "",
df_demand: pd.DataFrame = "",
agent_type: str = "v",
verbose: bool = False) -> pd.DataFrame:
# either path_demand or df_demand must be provided

# if path_demand is provided, read demand data from path_demand
Expand Down Expand Up @@ -57,5 +59,8 @@ def gen_agent_based_demand(node_dict: dict, zone_dict: dict,
departure_time=departure_time
)
)
print(" : Successfully generated agent-based demand data.")

if verbose:
print(" :Successfully generated agent-based demand data.")

return pd.DataFrame(agent_lst)
55 changes: 40 additions & 15 deletions grid2demand/func_lib/gen_zone.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,10 @@ def get_lng_lat_min_max(node_dict: dict[int, Node]) -> list:
Returns:
list: [min_lng, max_lng, min_lat, max_lat]
"""
coord_x_min, coord_x_max = node_dict[0].x_coord, node_dict[0].x_coord
coord_y_min, coord_y_max = node_dict[0].y_coord, node_dict[0].y_coord
first_key = list(node_dict.keys())[0]

coord_x_min, coord_x_max = node_dict[first_key].x_coord, node_dict[first_key].x_coord
coord_y_min, coord_y_max = node_dict[first_key].y_coord, node_dict[first_key].y_coord

for node_id in node_dict:
if node_dict[node_id].x_coord < coord_x_min:
Expand All @@ -50,7 +52,9 @@ def net2zone(node_dict: dict[int, Node],
num_x_blocks: int = 0,
num_y_blocks: int = 0,
cell_width: float = 0,
cell_height: float = 0, unit: str = "km", use_zone_id: bool = False) -> dict[str, Zone]:
cell_height: float = 0, unit: str = "km",
use_zone_id: bool = False,
verbose: bool = False) -> dict[str, Zone]:
"""convert node_dict to zone_dict by grid.
The grid can be defined by num_x_blocks and num_y_blocks, or cell_width and cell_height.
if num_x_blocks and num_y_blocks are specified, the grid will be divided into num_x_blocks * num_y_blocks.
Expand All @@ -65,6 +69,8 @@ def net2zone(node_dict: dict[int, Node],
cell_width (float, optional): the width for each block/grid . Defaults to 0. unit: km.
cell_height (float, optional): the height for each block/grid. Defaults to 0. unit: km.
unit (str, optional): the unit of cell_width and cell_height. Defaults to "km". Options:"meter", "km", "mile".
use_zone_id (bool, optional): whether to use zone_id in node_dict. Defaults to False.
verbose (bool, optional): print processing information. Defaults to False.
Raises
ValueError: Please provide num_x_blocks and num_y_blocks or cell_width and cell_height
Expand Down Expand Up @@ -104,6 +110,16 @@ def net2zone(node_dict: dict[int, Node],

coord_x_min, coord_x_max, coord_y_min, coord_y_max = get_lng_lat_min_max(node_dict)

# get nodes within the boundary
if use_zone_id:
node_dict_within_boundary = {}
for node_id in node_dict:
if node_dict[node_id].x_coord >= coord_x_min and node_dict[node_id].x_coord <= coord_x_max and \
node_dict[node_id].y_coord >= coord_y_min and node_dict[node_id].y_coord <= coord_y_max:
node_dict_within_boundary[node_id] = node_dict[node_id]
else:
node_dict_within_boundary = node_dict

# Priority: num_x_blocks, number_y_blocks > cell_width, cell_height
# if num_x_blocks and num_y_blocks are given, use them to partition the study area
# else if cell_width and cell_height are given, use them to partition the study area
Expand Down Expand Up @@ -227,9 +243,11 @@ def generate_polygon(x_min, x_max, y_min, y_max) -> shapely.geometry.Polygon:
geometry=points_lst[i]
)
zone_id_flag += 1
print(f" : Successfully generated zone dictionary: {len(zone_dict) - 4 * len(zone_upper_row)} Zones generated, \
\n plus {4 * len(zone_upper_row)} boundary gates (points))")
return zone_dict

if verbose:
print(f" : Successfully generated zone dictionary: {len(zone_dict) - 4 * len(zone_upper_row)} Zones generated, \
\n plus {4 * len(zone_upper_row)} boundary gates (points))")
return (zone_dict, node_dict_within_boundary)


def sync_node_with_zones(args: tuple) -> tuple:
Expand All @@ -250,7 +268,7 @@ def sync_node_with_zones(args: tuple) -> tuple:


@func_running_time
def sync_zone_and_node_geometry(zone_dict: dict, node_dict: dict, cpu_cores: int = 1) -> dict:
def sync_zone_and_node_geometry(zone_dict: dict, node_dict: dict, cpu_cores: int = 1, verbose: bool = False) -> dict:
"""Map nodes to zone cells
Parameters
Expand All @@ -262,7 +280,8 @@ def sync_zone_and_node_geometry(zone_dict: dict, node_dict: dict, cpu_cores: int
"""
# Create a pool of worker processes
print(f" : Parallel synchronizing Nodes and Zones using Pool with {cpu_cores} CPUs. Please wait...")
if verbose:
print(f" : Parallel synchronizing Nodes and Zones using Pool with {cpu_cores} CPUs. Please wait...")
# Prepare arguments for the pool
args_list = [(node_id, node, zone_dict)
for node_id, node in node_dict.items()]
Expand All @@ -276,7 +295,9 @@ def sync_zone_and_node_geometry(zone_dict: dict, node_dict: dict, cpu_cores: int
zone_dict[zone_name].node_id_list.append(node_id)
node_dict[node_id] = node

print(" : Successfully synchronized zone and node geometry")
if verbose:
print(" : Successfully synchronized zone and node geometry")

return {"node_dict": node_dict, "zone_dict": zone_dict}


Expand All @@ -298,7 +319,7 @@ def sync_poi_with_zones(args: tuple) -> tuple:


@func_running_time
def sync_zone_and_poi_geometry(zone_dict: dict, poi_dict: dict, cpu_cores: int = 1) -> dict:
def sync_zone_and_poi_geometry(zone_dict: dict, poi_dict: dict, cpu_cores: int = 1, verbose: bool = False) -> dict:
"""Synchronize zone cells and POIs to update zone_id attribute for POIs and poi_id_list attribute for zone cells
Args:
Expand All @@ -310,7 +331,8 @@ def sync_zone_and_poi_geometry(zone_dict: dict, poi_dict: dict, cpu_cores: int =
"""

# Create a pool of worker processes
print(f" : Parallel synchronizing POIs and Zones using Pool with {cpu_cores} CPUs. Please wait...")
if verbose:
print(f" : Parallel synchronizing POIs and Zones using Pool with {cpu_cores} CPUs. Please wait...")
# Prepare arguments for the pool
args_list = [(poi_id, poi, zone_dict) for poi_id, poi in poi_dict.items()]

Expand All @@ -324,7 +346,8 @@ def sync_zone_and_poi_geometry(zone_dict: dict, poi_dict: dict, cpu_cores: int =
zone_dict[zone_name].poi_id_list.append(poi_id)
poi_dict[poi_id] = poi

print(" : Successfully synchronized zone and poi geometry")
if verbose:
print(" : Successfully synchronized zone and poi geometry")
return {"poi_dict": poi_dict, "zone_dict": zone_dict}


Expand All @@ -349,7 +372,7 @@ def distance_calculation(args: tuple) -> tuple:


@func_running_time
def calc_zone_od_matrix(zone_dict: dict, cpu_cores: int = 1) -> dict[tuple[str, str], dict]:
def calc_zone_od_matrix(zone_dict: dict, cpu_cores: int = 1, verbose: bool = False) -> dict[tuple[str, str], dict]:
"""Calculate the zone-to-zone distance matrix
Args:
Expand All @@ -369,7 +392,8 @@ def calc_zone_od_matrix(zone_dict: dict, cpu_cores: int = 1) -> dict[tuple[str,
len_df_zone = len(df_zone)

# Prepare arguments for the pool
print(f" : Parallel calculating zone-to-zone distance matrix using Pool with {cpu_cores} CPUs. Please wait...")
if verbose:
print(f" : Parallel calculating zone-to-zone distance matrix using Pool with {cpu_cores} CPUs. Please wait...")
args_list = [(i, j, df_zone) for i, j in itertools.product(range(len_df_zone), range(len_df_zone))]

with Pool(processes=cpu_cores) as pool:
Expand All @@ -379,5 +403,6 @@ def calc_zone_od_matrix(zone_dict: dict, cpu_cores: int = 1) -> dict[tuple[str,
# Gather results
dist_dict = dict(results)

print(" : Successfully calculated zone-to-zone distance matrix")
if verbose:
print(" : Successfully calculated zone-to-zone distance matrix")
return dist_dict
23 changes: 16 additions & 7 deletions grid2demand/func_lib/gravity_model.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,25 +10,31 @@
import pandas as pd


def calc_zone_production_attraction(node_dict: dict, zone_dict: dict) -> dict:
def calc_zone_production_attraction(node_dict: dict, zone_dict: dict, verbose: bool = False) -> dict:
# calculate zone production and attraction based on node production and attraction
for zone_name in zone_dict:
if zone_dict[zone_name].node_id_list:
for node_id in zone_dict[zone_name].node_id_list:
zone_dict[zone_name].production += node_dict[node_id].production
zone_dict[zone_name].attraction += node_dict[node_id].attraction
print(" : Successfully calculated zone production and attraction based on node production and attraction.")

if verbose:
print(" : Successfully calculated zone production and attraction based on node production and attraction.")

return zone_dict


def calc_zone_od_friction_attraction(zone_od_friction_matrix_dict: dict, zone_dict: dict) -> dict:
def calc_zone_od_friction_attraction(zone_od_friction_matrix_dict: dict, zone_dict: dict, verbose: bool = False) -> dict:
zone_od_friction_attraction_dict = {}
for zone_name, friction_val in zone_od_friction_matrix_dict.items():
if zone_name[0] not in zone_od_friction_attraction_dict:
zone_od_friction_attraction_dict[zone_name[0]] = friction_val * zone_dict[zone_name[1]].attraction
else:
zone_od_friction_attraction_dict[zone_name[0]] += friction_val * zone_dict[zone_name[1]].attraction
print(" : Successfully calculated zone od friction attraction.")

if verbose:
print(" : Successfully calculated zone od friction attraction.")

return zone_od_friction_attraction_dict


Expand All @@ -37,7 +43,8 @@ def run_gravity_model(zone_dict: dict,
trip_purpose: int = 1,
alpha: float = 28507,
beta: float = -0.02,
gamma: float = -0.123) -> dict:
gamma: float = -0.123,
verbose: bool = False) -> dict:
# if trip purpose is specified in trip_purpose_dict, use the default value
# otherwise, use the user-specified value
trip_purpose_dict = pkg_settings.get("trip_purpose_dict")
Expand All @@ -57,7 +64,7 @@ def run_gravity_model(zone_dict: dict,
}

# perform attraction calculation
zone_od_friction_attraction_dict = calc_zone_od_friction_attraction(zone_od_friction_matrix_dict, zone_dict)
zone_od_friction_attraction_dict = calc_zone_od_friction_attraction(zone_od_friction_matrix_dict, zone_dict, verbose=verbose)

# perform od trip flow (volume) calculation
for zone_name_pair in zone_od_friction_matrix_dict:
Expand All @@ -67,6 +74,8 @@ def run_gravity_model(zone_dict: dict,
zone_od_friction_attraction_dict[zone_name_pair[0]])

# Generate demand.csv
print(" : Successfully run gravity model to generate demand.csv.")
if verbose:
print(" : Successfully run gravity model to generate demand.csv.")

# return pd.DataFrame(list(zone_od_matrix_dict.values()))
return zone_od_dist_matrix
Loading

0 comments on commit 14b8ec7

Please sign in to comment.