Finish base code and keep input building file and network file

This commit is contained in:
Ruben1729 2024-02-14 14:27:10 -05:00
parent f691449d42
commit 7c15ee4399
15 changed files with 2724 additions and 160 deletions

3
.gitignore vendored
View File

@ -3,6 +3,9 @@
# Ignore files that start with 'building' in the 'input_files' directory
input_files/*
!input_files/merged-network.osm.pbf
!input_files/summerschool_all_buildings.geojson
# Ignore all files inside the 'output_files' directory
output_files/*
output/*

Binary file not shown.

View File

@ -1,78 +0,0 @@
import hub.helpers.constants as cte
class HubFunctionToMatsimActivity:
"""
Hub function to matsim activity construction function class
"""
def __init__(self):
self._dictionary = {
cte.RESIDENTIAL: 'home',
cte.SINGLE_FAMILY_HOUSE: 'home',
cte.MULTI_FAMILY_HOUSE: 'home',
cte.ROW_HOUSE: 'home',
cte.MID_RISE_APARTMENT: 'home',
cte.HIGH_RISE_APARTMENT: 'home',
cte.OFFICE_AND_ADMINISTRATION: 'work',
cte.SMALL_OFFICE: 'work',
cte.MEDIUM_OFFICE: 'work',
cte.LARGE_OFFICE: 'work',
cte.COURTHOUSE: 'work',
cte.FIRE_STATION: 'work',
cte.PENITENTIARY: 'work',
cte.POLICE_STATION: 'work',
cte.POST_OFFICE: 'work',
cte.LIBRARY: 'edu',
cte.EDUCATION: 'edu',
cte.PRIMARY_SCHOOL: 'edu',
cte.PRIMARY_SCHOOL_WITH_SHOWER: 'edu',
cte.SECONDARY_SCHOOL: 'edu',
cte.UNIVERSITY: 'edu',
cte.LABORATORY_AND_RESEARCH_CENTER: 'edu',
cte.STAND_ALONE_RETAIL: 'work,secondary',
cte.HOSPITAL: 'work',
cte.OUT_PATIENT_HEALTH_CARE: 'work',
cte.HEALTH_CARE: 'work',
cte.RETIREMENT_HOME_OR_ORPHANAGE: 'home,secondary',
cte.COMMERCIAL: 'work,secondary',
cte.STRIP_MALL: 'work,secondary',
cte.SUPERMARKET: 'work,secondary',
cte.RETAIL_SHOP_WITHOUT_REFRIGERATED_FOOD: 'work,secondary',
cte.RETAIL_SHOP_WITH_REFRIGERATED_FOOD: 'work,secondary',
cte.RESTAURANT: 'work,secondary',
cte.QUICK_SERVICE_RESTAURANT: 'work,secondary',
cte.FULL_SERVICE_RESTAURANT: 'work,secondary',
cte.HOTEL: 'work,secondary',
cte.HOTEL_MEDIUM_CLASS: 'work,secondary',
cte.SMALL_HOTEL: 'work,secondary',
cte.LARGE_HOTEL: 'work,secondary',
cte.DORMITORY: 'work',
cte.EVENT_LOCATION: 'work,secondary',
cte.CONVENTION_CENTER: 'work,secondary',
cte.HALL: 'work',
cte.GREEN_HOUSE: 'work',
cte.INDUSTRY: 'work',
cte.WORKSHOP: 'work',
cte.WAREHOUSE: 'work',
cte.WAREHOUSE_REFRIGERATED: 'work',
cte.SPORTS_LOCATION: 'work,secondary',
cte.SPORTS_ARENA: 'work,secondary',
cte.GYMNASIUM: 'work,secondary',
cte.MOTION_PICTURE_THEATRE: 'work,secondary',
cte.MUSEUM: 'work,secondary',
cte.PERFORMING_ARTS_THEATRE: 'work,secondary',
cte.TRANSPORTATION: 'work',
cte.AUTOMOTIVE_FACILITY: 'work',
cte.PARKING_GARAGE: 'work',
cte.RELIGIOUS: 'work,secondary',
cte.NON_HEATED: 'work',
cte.DATACENTER: 'work',
cte.FARM: 'work'
}
@property
def dictionary(self) -> dict:
"""
Get the dictionary
:return: {}
"""
return self._dictionary

Binary file not shown.

File diff suppressed because it is too large Load Diff

View File

@ -7,6 +7,7 @@ from hub.helpers.dictionaries import Dictionaries
from matsim_engine import MatSimEngine
from matsim import Matsim
from matsim_visualizer import MatsimVisualizer
try:
file_path = (Path(__file__).parent / 'input_files' / 'summerschool_all_buildings.geojson')
@ -24,11 +25,11 @@ try:
ConstructionFactory(construction_format, city).enrich()
UsageFactory(usage_format, city).enrich()
# Matsim(city, 'output_files')._export()
Matsim(city, 'output_files')._export()
MatSimEngine('output_files/Montreal_config.xml').run()
# visualizer = MatSimVisualizer('output_files/network.xml.gz', 'output_files/output_events.xml.gz')
# visualizer.visualize()
visualizer = MatsimVisualizer('output_files/Montreal/output_network.xml.gz', 'output_files/Montreal/output_events.xml.gz')
visualizer.visualize()
except Exception as ex:
print('error: ', ex)

290
matsim.py
View File

@ -13,7 +13,6 @@ FACILITIES_DTD = "http://www.matsim.org/files/dtd/facilities_v1.dtd"
POPULATION_DTD = "http://www.matsim.org/files/dtd/population_v5.dtd"
# TODO: remove xmltodict completely and replace with lxml as it doesnt allow for repeated mixed ordered tags
class Matsim:
def __init__(self, city, output_file_path):
self._city = city
@ -69,23 +68,23 @@ class Matsim:
}
facility_xml = etree.SubElement(facilities_xml, 'facility', {
'id': facility['id'],
'x': facility['x'],
'y': facility['y'],
'id': f"{facility['id']}",
'x': f"{facility['x']}",
'y': f"{facility['y']}",
})
activity_xml = etree.SubElement(facility_xml, 'activity', {
'type': activity_info['type']
'type': f"{activity_info['type']}"
})
etree.SubElement(activity_xml, 'capacity', {
'value': activity_info['capacity']
'value': f"{activity_info['capacity']}"
})
etree.SubElement(activity_xml, 'opentime', {
'day': activity_info['opentime'][0]['day'],
'start_time': activity_info['opentime'][0]['start_time'],
'end_time': activity_info['opentime'][0]['end_time']
'day': f"{activity_info['opentime'][0]['day']}",
'start_time': f"{activity_info['opentime'][0]['start_time']}",
'end_time': f"{activity_info['opentime'][0]['end_time']}"
})
facility['activity'].append(activity_info)
@ -98,13 +97,13 @@ class Matsim:
gdf.to_file("input_files/buildings_shapefile.shp")
# Convert the Python dictionary to an XML string
xml_content = etree.tostring(facilities_xml, pretty_print=True, encoding='UTF-8', xml_declaration=True).decode('utf-8')
xml_content = etree.tostring(facilities_xml, pretty_print=True, encoding='UTF-8').decode('utf-8')
# Write the XML to the file
output_file = f"{self._output_file_path}/{self._city.name}_facilities.xml"
with open(output_file, 'w') as file:
file.write("<?xml version=\"1.0\" encoding=\"utf-8\"?>")
file.write(f"<!DOCTYPE facilities SYSTEM \"{FACILITIES_DTD}\">")
file.write("<?xml version=\"1.0\" encoding=\"utf-8\"?>\n")
file.write(f"<!DOCTYPE facilities SYSTEM \"{FACILITIES_DTD}\">\n")
file.write(xml_content)
with open(output_file, 'rb') as f_in:
@ -145,8 +144,8 @@ class Matsim:
current_work = 0
for facility in self._facilities['facility']:
if facility['activity'][0]['type'] == cte.RESIDENTIAL:
max_capacity = int(facility['activity'][0]['capacity']['value'])
for _ in range(max_capacity):
max_capacity = int(facility['activity'][0]['capacity'])
for i in range(max_capacity):
person = etree.SubElement(population, 'person', {
'id': str(id),
'sex': 'm',
@ -158,10 +157,10 @@ class Matsim:
# Residential activity
etree.SubElement(plan, 'act', {
'type': facility['activity'][0]['type'],
'facility': facility['id'],
'x': facility['x'],
'y': facility['y'],
'type': f"{facility['activity'][0]['type']}",
'facility': f"{facility['id']}",
'x': f"{facility['x']}",
'y': f"{facility['y']}",
'end_time': '7:30:00'
})
@ -170,12 +169,12 @@ class Matsim:
# Work activity
etree.SubElement(plan, 'act', {
'type': work[current_work]['type'],
'facility': work[current_work]['facility'],
'x': work[current_work]['x'],
'y': work[current_work]['y'],
'start_time': work[current_work]['start_time'],
'end_time': work[current_work]['end_time'],
'type': f"{work[current_work]['type']}",
'facility': f"{work[current_work]['facility']}",
'x': f"{work[current_work]['x']}",
'y': f"{work[current_work]['y']}",
'start_time': f"{work[current_work]['start_time']}",
'end_time': f"{work[current_work]['end_time']}",
})
# Leg to home
@ -183,10 +182,10 @@ class Matsim:
# Residential activity (return)
etree.SubElement(plan, 'act', {
'type': facility['activity'][0]['type'],
'facility': facility['id'],
'x': facility['x'],
'y': facility['y'],
'type': f"{facility['activity'][0]['type']}",
'facility': f"{facility['id']}",
'x': f"{facility['x']}",
'y': f"{facility['y']}",
})
work[current_work]['capacity'] -= 1
@ -196,42 +195,235 @@ class Matsim:
id += 1
# Convert the Python dictionary to an XML string
xml_content = etree.tostring(population, pretty_print=True, encoding='UTF-8', xml_declaration=True).decode('utf-8')
xml_content = etree.tostring(population, pretty_print=True, encoding='UTF-8').decode('utf-8')
# Write the XML to the file
output_file = f"{self._output_file_path}/{self._city.name}_population.xml"
with open(output_file, 'w') as file:
file.write("<?xml version=\"1.0\" encoding=\"utf-8\"?>\n")
file.write(f"<!DOCTYPE population SYSTEM \"{POPULATION_DTD}\">\n")
file.write(xml_content)
with open(output_file, 'rb') as f_in:
with gzip.open(output_file + '.gz', 'wb') as f_out:
shutil.copyfileobj(f_in, f_out)
# TODO: Reimplement this after figuring out what's wrong with other files
def _export_config(self):
pass
root = etree.Element('config')
# ======== NETWORK ========= #
network_path_module = etree.SubElement(root, 'module', {
'name': 'network'
})
_add_param(network_path_module, 'inputNetworkFile', f"{self._city.name}_network.xml.gz")
# ======== POPULATION ========= #
population_path_module = etree.SubElement(root, 'module', {
'name': 'plans'
})
_add_param(population_path_module, 'inputPlansFile', f"{self._city.name}_population.xml.gz")
# ======== FACILITIES ========= #
facilities_path_module = etree.SubElement(root, 'module', {
'name': 'facilities'
})
_add_param(facilities_path_module, 'inputFacilitiesFile', f"{self._city.name}_facilities.xml.gz")
_add_param(facilities_path_module, 'facilitiesSource', 'fromFile')
# ======== CONTROLER ========= #
controler_module = etree.SubElement(root, 'module', {
'name': 'controler'
})
controler_params = [
('writeEventsInterval', '1000'),
('writePlansInterval', '1000'),
('eventsFileFormat', 'xml'),
('outputDirectory', f"{self._output_file_path}/Montreal"),
('firstIteration', '0'),
('lastIteration', '10'),
('mobsim', 'qsim'),
]
for param in controler_params:
_add_param(controler_module, param[0], param[1])
# ======== QSIM ========= #
qsim_module = etree.SubElement(root, 'module', {
'name': 'qsim'
})
qsim_params = [
('startTime', '00:00:00'),
('endTime', '30:00:00'),
('flowCapacityFactor', '1.00'),
('storageCapacityFactor', '1.00'),
('numberOfThreads', '1'),
('snapshotperiod', '00:00:01'),
('removeStuckVehicles', 'false'),
('stuckTime', '3600.0'),
('timeStepSize', '00:00:01'),
('trafficDynamics', 'queue'),
]
for param in qsim_params:
_add_param(qsim_module, param[0], param[1])
# ======== SCORING ========= #
score_module = etree.SubElement(root, 'module', {
'name': 'planCalcScore'
})
_add_param(score_module, 'BrainExpBeta', '1.0')
_add_param(score_module, 'learningRate', '1.0')
scoring_paramset = etree.SubElement(score_module, 'parameterset', {
'type': 'scoringParameters'
})
scoring_paramset_params = [
('earlyDeparture', '0.0'),
('lateArrival', '0.0'),
('marginalUtilityOfMoney', '0.062'),
('performing', '0.96'),
('utilityOfLineSwitch', '0.0'),
('waitingPt', '-0.18'),
]
for param in scoring_paramset_params:
_add_param(scoring_paramset, param[0], param[1])
mode_paramsets = [
("car",
[
("marginalUtilityOfTraveling_util_hr", "0.0"),
("constant", "-0.562"),
("monetaryDistanceRate", "-0.0004")
]),
("walk",
[
("marginalUtilityOfTraveling_util_hr", "-1.14"),
("constant", "0.0"),
("marginalUtilityOfDistance_util_m", "0.0")
])
]
for mode, parameters in mode_paramsets:
_add_parameterset(scoring_paramset, "modeParams", [("mode", mode)] + parameters)
activity_paramsets = [
('residential',[
('priority', '1'),
('typicalDuration', '13:00:00'),
('minimalDuration', '01:00:00'),
]),
('medium office',[
('priority', '1'),
('typicalDuration', '09:00:00'),
('minimalDuration', '08:00:00'),
('openingTime', '08:00:00'),
('earliestEndTime', '17:00:00'),
('latestStartTime', '09:00:00'),
('closingTime', '18:00:00'),
]),
('warehouse',[
('priority', '1'),
('typicalDuration', '09:00:00'),
('minimalDuration', '08:00:00'),
('openingTime', '08:00:00'),
('earliestEndTime', '17:00:00'),
('latestStartTime', '09:00:00'),
('closingTime', '18:00:00'),
]),
('stand alone retail',[
('priority', '1'),
('typicalDuration', '09:00:00'),
('minimalDuration', '08:00:00'),
('openingTime', '08:00:00'),
('earliestEndTime', '17:00:00'),
('latestStartTime', '09:00:00'),
('closingTime', '18:00:00'),
]),
]
for activity_type, parameters in activity_paramsets:
_add_parameterset(scoring_paramset, "activityParams", [("activityType", activity_type)] + parameters)
# ======== STRATEGY ========= #
strategy_module = etree.SubElement(root, 'module', {
'name': 'strategy'
})
_add_param(strategy_module, 'maxAgentPlanMemorySize', '6')
strategy_paramsets = [
("ChangeExpBeta",[
("weight", "0.7"),
]),
("ReRoute",[
("disableAfterIteration", "2900"),
("weight", "0.01"),
]),
("SubtourModeChoice", [
("disableAfterIteration", "2900"),
("weight", "0.01"),
]),
("TimeAllocationMutator", [
("disableAfterIteration", "2900"),
("weight", "0.01"),
]),
]
for name, parameters in strategy_paramsets:
_add_parameterset(strategy_module, "strategysettings", [("strategyName", name)] + parameters)
# ======== SUBTOUR MODE CHOICE ========= #
subtour_module = etree.SubElement(root, 'module', {
'name': 'subtourModeChoice'
})
# Defines the chain-based modes, seperated by commas
_add_param(subtour_module, 'chainBasedModes', 'car')
# Defines whether car availability must be considered or not. An agent has no car only if it has no license, or never access to a car
_add_param(subtour_module, 'considerCarAvailability', 'true')
# Defines all the modes available, including chain-based modes, seperated by commas
_add_param(subtour_module, 'modes', 'car,walk')
xml_content = etree.tostring(root, pretty_print=True, encoding='UTF-8').decode('utf-8')
# Write the XML to the file
output_file = f"{self._output_file_path}/{self._city.name}_config.xml"
with open(output_file, 'w') as file:
file.write("<?xml version=\"1.0\" encoding=\"utf-8\"?>\n")
file.write(f"<!DOCTYPE config SYSTEM \"{CONFIG_DTD}\">\n")
file.write(xml_content)
def _add_param(parent, name, value):
etree.SubElement(parent, "param", {
'name': name,
'value': value
})
def _add_parameterset(parent, type, parameters):
parameterset = etree.SubElement(parent, "parameterset", {
'type': type
})
for name, value in parameters:
_add_param(parameterset, name, value)
def _convert_schedules(building_schedules):
converted_schedules = []
for schedule in building_schedules:
opening_hour = 0
closing_hour = 0
opening_hour = 0
closing_hour = 0
schedule = building_schedules[0]
for i, value in enumerate(schedule.values):
if value > 0:
opening_hour = i
break
for i, value in enumerate(schedule.values):
if value > 0:
opening_hour = i
break
for i, value in reversed(list(enumerate(schedule.values))):
if value > 0:
closing_hour = i
break
for i, value in reversed(list(enumerate(schedule.values))):
if value > 0:
closing_hour = i
break
for day in schedule.day_types:
if day[0:3] != 'hol':
converted_schedules.append({
'day': day[0:3],
'start_time': f"{opening_hour:02}:00:00",
'end_time': f"{closing_hour:02}:00:00"
})
for day in schedule.day_types:
if day[0:3] != 'hol':
converted_schedules.append({
'day': day[0:3],
'start_time': opening_hour,
'end_time': closing_hour
})
return converted_schedules

View File

@ -1,19 +0,0 @@
class MatsimActivityToMatsimSchedule:
"""
Hub function to nrcan construction function class
"""
def __init__(self):
self._dictionary = {
'work': '08:00:00-18:00:00',
'home': '00:00:00-30:00:00',
'edu': '08:00:00-18:00:00',
'secondary': '08:00:00-20:00:00'
}
@property
def dictionary(self) -> dict:
"""
Get the dictionary
:return: {}
"""
return self._dictionary

View File

@ -1,12 +1,4 @@
import math
import subprocess
import xmltodict
import geopandas as gpd
from shapely.geometry import Point
from matsim_activity_to_matsim_schedule import MatsimActivityToMatsimSchedule
from hub_function_to_matsim_activity import HubFunctionToMatsimActivity
class MatSimEngine:
def __init__(self, config_file_path):
@ -20,4 +12,4 @@ class MatSimEngine:
# Must generate this config file first.
command.append(self._config_file_path)
subprocess.run(command)
subprocess.run(command, check=True)

View File

@ -8,7 +8,7 @@ from collections import defaultdict
import matplotlib.cm as cm
import matplotlib.colors as colors
class MatSimVisualizer():
class MatsimVisualizer():
def __init__(self, network_file_path, events_file_path):
self.network_file_path = network_file_path
self.events_file_path = events_file_path
@ -92,7 +92,7 @@ class MatSimVisualizer():
edge_widths = [2 + self.norm(traffic_data.get(link['id'], 0)) * 3 for link in self.links]
plt.cla()
nx.draw(self.G, self.pos, node_size=10, node_color='blue', width=edge_widths, edge_color=edge_colors, with_labels=False,
nx.draw(self.G, self.pos, node_size=0, node_color='blue', width=edge_widths, edge_color=edge_colors, with_labels=False,
edge_cmap=self.cmap)
plt.title(f"Time: {tick}")

BIN
traffic_animation.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 MiB