diff --git a/.gitignore b/.gitignore index 1eb5162..247c113 100644 --- a/.gitignore +++ b/.gitignore @@ -53,3 +53,226 @@ coverage.xml docs/_build/ *.bak +scripts/BP Draft 2/Blueprint Basic/Blueprint Basic network_2020/trn/ACE.far +scripts/BP Draft 2/Blueprint Basic/Blueprint Basic network_2020/hwy/turnspm.pen +scripts/BP Draft 2/Blueprint Basic/Blueprint Basic network_2020/hwy/turnsop.pen +scripts/BP Draft 2/Blueprint Basic/Blueprint Basic network_2020/hwy/turnsam.pen +scripts/BP Draft 2/Blueprint Basic/Blueprint Basic network_2020/hwy/tolls.csv +scripts/BP Draft 2/Blueprint Basic/Blueprint Basic network_2020/hwy/freeflow.net +scripts/BP Draft 2/Blueprint Basic/Blueprint Basic network_2015/trn/xfare.far +scripts/BP Draft 2/Blueprint Basic/Blueprint Basic network_2015/trn/WALK_access.sup +scripts/BP Draft 2/Blueprint Basic/Blueprint Basic network_2015/trn/transitVehicleToCapacity.csv +scripts/BP Draft 2/Blueprint Basic/Blueprint Basic network_2015/trn/transitPrefixToVehicle.csv +scripts/BP Draft 2/Blueprint Basic/Blueprint Basic network_2015/trn/transitLineToVehicle.csv +scripts/BP Draft 2/Blueprint Basic/Blueprint Basic network_2015/trn/transitLines_light_rail.pnr +scripts/BP Draft 2/Blueprint Basic/Blueprint Basic network_2015/trn/transitLines_heavy_rail.pnr +scripts/BP Draft 2/Blueprint Basic/Blueprint Basic network_2015/trn/transitLines_ferry.pnr +scripts/BP Draft 2/Blueprint Basic/Blueprint Basic network_2015/trn/transitLines_express_bus.pnr +scripts/BP Draft 2/Blueprint Basic/Blueprint Basic network_2015/trn/transitLines_commuter_rail.pnr +scripts/BP Draft 2/Blueprint Basic/Blueprint Basic network_2015/trn/transitLines.zac +scripts/BP Draft 2/Blueprint Basic/Blueprint Basic network_2015/trn/transitLines.xfer +scripts/BP Draft 2/Blueprint Basic/Blueprint Basic network_2015/trn/transitLines.link +scripts/BP Draft 2/Blueprint Basic/Blueprint Basic network_2015/trn/transitLines.lin +scripts/BP Draft 2/Blueprint Basic/Blueprint Basic network_2015/trn/transitLines.access +scripts/BP Draft 2/Blueprint Basic/Blueprint Basic network_2015/trn/transit_faremat.block +scripts/BP Draft 2/Blueprint Basic/Blueprint Basic network_2015/trn/SMART.far +scripts/BP Draft 2/Blueprint Basic/Blueprint Basic network_2015/trn/HSR.far +scripts/BP Draft 2/Blueprint Basic/Blueprint Basic network_2015/trn/Ferry.far +scripts/BP Draft 2/Blueprint Basic/Blueprint Basic network_2015/trn/farelinks.far +scripts/BP Draft 2/Blueprint Basic/Blueprint Basic network_2015/trn/Caltrain.far +scripts/BP Draft 2/Blueprint Basic/Blueprint Basic network_2015/trn/BART.far +scripts/BP Draft 2/Blueprint Basic/Blueprint Basic network_2015/trn/Amtrak.far +scripts/BP Draft 2/Blueprint Basic/Blueprint Basic network_2015/trn/ACE.far +scripts/BP Draft 2/Blueprint Basic/Blueprint Basic network_2015/hwy/turnspm.pen +scripts/BP Draft 2/Blueprint Basic/Blueprint Basic network_2015/hwy/turnsop.pen +scripts/BP Draft 2/Blueprint Basic/Blueprint Basic network_2015/hwy/turnsam.pen +scripts/BP Draft 2/Blueprint Basic/Blueprint Basic network_2015/hwy/tolls.csv +scripts/BP Draft 2/Blueprint Basic/Blueprint Basic network_2015/hwy/freeflow.net +scripts/BP Draft 2/Blueprint Basic/Blueprint Basic network_2020/trn/transitLines.link +scripts/BP Draft 2/Blueprint Basic/Blueprint Basic network_2020/trn/transitLines.xfer +scripts/BP Draft 2/Blueprint Basic/Blueprint Basic network_2020/trn/transitLines.zac +scripts/BP Draft 2/Blueprint Basic/Blueprint Basic network_2020/trn/transitLines_commuter_rail.pnr +scripts/BP Draft 2/Blueprint Basic/Blueprint Basic network_2020/trn/transitLines_express_bus.pnr +scripts/BP Draft 2/Blueprint Basic/Blueprint Basic network_2020/trn/transitLines_ferry.pnr +scripts/BP Draft 2/Blueprint Basic/Blueprint Basic network_2020/trn/transitLines_heavy_rail.pnr +scripts/BP Draft 2/Blueprint Basic/Blueprint Basic network_2020/trn/transitLines_light_rail.pnr +scripts/BP Draft 2/Blueprint Basic/Blueprint Basic network_2020/trn/transitLineToVehicle.csv +scripts/BP Draft 2/Blueprint Basic/Blueprint Basic network_2020/trn/transitPrefixToVehicle.csv +scripts/BP Draft 2/Blueprint Basic/Blueprint Basic network_2020/trn/transitVehicleToCapacity.csv +scripts/BP Draft 2/Blueprint Basic/Blueprint Basic network_2020/trn/WALK_access.sup +scripts/BP Draft 2/Blueprint Basic/Blueprint Basic network_2020/trn/xfare.far +scripts/BP Draft 2/Blueprint Basic/Blueprint Basic network_2025/hwy/freeflow.net +scripts/BP Draft 2/Blueprint Basic/Blueprint Basic network_2025/hwy/tolls.csv +scripts/BP Draft 2/Blueprint Basic/Blueprint Basic network_2025/hwy/turnsam.pen +scripts/BP Draft 2/Blueprint Basic/Blueprint Basic network_2025/hwy/turnsop.pen +scripts/BP Draft 2/Blueprint Basic/Blueprint Basic network_2025/hwy/turnspm.pen +scripts/BP Draft 2/Blueprint Basic/Blueprint Basic network_2025/trn/ACE.far +scripts/BP Draft 2/Blueprint Basic/Blueprint Basic network_2025/trn/Amtrak.far +scripts/BP Draft 2/Blueprint Basic/Blueprint Basic network_2025/trn/BART.far +scripts/BP Draft 2/Blueprint Basic/Blueprint Basic network_2025/trn/Caltrain.far +scripts/BP Draft 2/Blueprint Basic/Blueprint Basic network_2025/trn/farelinks.far +scripts/BP Draft 2/Blueprint Basic/Blueprint Basic network_2025/trn/Ferry.far +scripts/BP Draft 2/Blueprint Basic/Blueprint Basic network_2025/trn/HSR.far +scripts/BP Draft 2/Blueprint Basic/Blueprint Basic network_2025/trn/SMART.far +scripts/BP Draft 2/Blueprint Basic/Blueprint Basic network_2025/trn/transit_faremat.block +scripts/BP Draft 2/Blueprint Basic/Blueprint Basic network_2025/trn/transitLines.access +scripts/BP Draft 2/Blueprint Basic/Blueprint Basic network_2025/trn/transitLines.lin +scripts/BP Draft 2/Blueprint Basic/Blueprint Basic network_2025/trn/transitLines.link +scripts/BP Draft 2/Blueprint Basic/Blueprint Basic network_2025/trn/transitLines.xfer +scripts/BP Draft 2/Blueprint Basic/Blueprint Basic network_2025/trn/transitLines.zac +scripts/BP Draft 2/Blueprint Basic/Blueprint Basic network_2025/trn/transitLines_commuter_rail.pnr +scripts/BP Draft 2/Blueprint Basic/Blueprint Basic network_2025/trn/transitLines_express_bus.pnr +scripts/BP Draft 2/Blueprint Basic/Blueprint Basic network_2025/trn/transitLines_ferry.pnr +scripts/BP Draft 2/Blueprint Basic/Blueprint Basic network_2025/trn/transitLines_heavy_rail.pnr +scripts/BP Draft 2/Blueprint Basic/Blueprint Basic network_2025/trn/transitLines_light_rail.pnr +scripts/BP Draft 2/Blueprint Basic/Blueprint Basic network_2025/trn/transitLineToVehicle.csv +scripts/BP Draft 2/Blueprint Basic/Blueprint Basic network_2025/trn/transitPrefixToVehicle.csv +scripts/BP Draft 2/Blueprint Basic/Blueprint Basic network_2025/trn/transitVehicleToCapacity.csv +scripts/BP Draft 2/Blueprint Basic/Blueprint Basic network_2025/trn/WALK_access.sup +scripts/BP Draft 2/Blueprint Basic/Blueprint Basic network_2025/trn/xfare.far +scripts/BP Draft 2/Blueprint Basic/Blueprint Basic network_2030/hwy/freeflow.net +scripts/BP Draft 2/Blueprint Basic/Blueprint Basic network_2030/hwy/tolls.csv +scripts/BP Draft 2/Blueprint Basic/Blueprint Basic network_2030/hwy/turnsam.pen +scripts/BP Draft 2/Blueprint Basic/Blueprint Basic network_2030/hwy/turnsop.pen +scripts/BP Draft 2/Blueprint Basic/Blueprint Basic network_2030/hwy/turnspm.pen +scripts/BP Draft 2/Blueprint Basic/Blueprint Basic network_2030/trn/ACE.far +scripts/BP Draft 2/Blueprint Basic/Blueprint Basic network_2030/trn/Amtrak.far +scripts/BP Draft 2/Blueprint Basic/Blueprint Basic network_2030/trn/BART.far +scripts/BP Draft 2/Blueprint Basic/Blueprint Basic network_2030/trn/Caltrain.far +scripts/BP Draft 2/Blueprint Basic/Blueprint Basic network_2030/trn/farelinks.far +scripts/BP Draft 2/Blueprint Basic/Blueprint Basic network_2030/trn/Ferry.far +scripts/BP Draft 2/Blueprint Basic/Blueprint Basic network_2030/trn/HSR.far +scripts/BP Draft 2/Blueprint Basic/Blueprint Basic network_2030/trn/SMART.far +scripts/BP Draft 2/Blueprint Basic/Blueprint Basic network_2030/trn/transit_faremat.block +scripts/BP Draft 2/Blueprint Basic/Blueprint Basic network_2030/trn/transitLines.access +scripts/BP Draft 2/Blueprint Basic/Blueprint Basic network_2030/trn/transitLines.lin +scripts/BP Draft 2/Blueprint Basic/Blueprint Basic network_2030/trn/transitLines.link +scripts/BP Draft 2/Blueprint Basic/Blueprint Basic network_2030/trn/transitLines.xfer +scripts/BP Draft 2/Blueprint Basic/Blueprint Basic network_2030/trn/transitLines.zac +scripts/BP Draft 2/Blueprint Basic/Blueprint Basic network_2030/trn/transitLines_commuter_rail.pnr +scripts/BP Draft 2/Blueprint Basic/Blueprint Basic network_2030/trn/transitLines_express_bus.pnr +scripts/BP Draft 2/Blueprint Basic/Blueprint Basic network_2030/trn/transitLines_ferry.pnr +scripts/BP Draft 2/Blueprint Basic/Blueprint Basic network_2030/trn/transitLines_heavy_rail.pnr +scripts/BP Draft 2/Blueprint Basic/Blueprint Basic network_2030/trn/transitLines_light_rail.pnr +scripts/BP Draft 2/Blueprint Basic/Blueprint Basic network_2030/trn/transitLineToVehicle.csv +scripts/BP Draft 2/Blueprint Basic/Blueprint Basic network_2030/trn/transitPrefixToVehicle.csv +scripts/BP Draft 2/Blueprint Basic/Blueprint Basic network_2030/trn/transitVehicleToCapacity.csv +scripts/BP Draft 2/Blueprint Basic/Blueprint Basic network_2030/trn/WALK_access.sup +scripts/BP Draft 2/Blueprint Basic/Blueprint Basic network_2030/trn/xfare.far +scripts/BP Draft 2/Blueprint Basic/Blueprint Basic network_2035/hwy/freeflow.net +scripts/BP Draft 2/Blueprint Basic/Blueprint Basic network_2035/hwy/tolls.csv +scripts/BP Draft 2/Blueprint Basic/Blueprint Basic network_2035/hwy/turnsam.pen +scripts/BP Draft 2/Blueprint Basic/Blueprint Basic network_2035/hwy/turnsop.pen +scripts/BP Draft 2/Blueprint Basic/Blueprint Basic network_2035/hwy/turnspm.pen +scripts/BP Draft 2/Blueprint Basic/Blueprint Basic network_2035/trn/ACE.far +scripts/BP Draft 2/Blueprint Basic/Blueprint Basic network_2035/trn/Amtrak.far +scripts/BP Draft 2/Blueprint Basic/Blueprint Basic network_2035/trn/BART.far +scripts/BP Draft 2/Blueprint Basic/Blueprint Basic network_2035/trn/Caltrain.far +scripts/BP Draft 2/Blueprint Basic/Blueprint Basic network_2035/trn/farelinks.far +scripts/BP Draft 2/Blueprint Basic/Blueprint Basic network_2035/trn/Ferry.far +scripts/BP Draft 2/Blueprint Basic/Blueprint Basic network_2035/trn/HSR.far +scripts/BP Draft 2/Blueprint Basic/Blueprint Basic network_2035/trn/SMART.far +scripts/BP Draft 2/Blueprint Basic/Blueprint Basic network_2035/trn/transit_faremat.block +scripts/BP Draft 2/Blueprint Basic/Blueprint Basic network_2035/trn/transitLines.access +scripts/BP Draft 2/Blueprint Basic/Blueprint Basic network_2035/trn/transitLines.lin +scripts/BP Draft 2/Blueprint Basic/Blueprint Basic network_2035/trn/transitLines.link +scripts/BP Draft 2/Blueprint Basic/Blueprint Basic network_2035/trn/transitLines.xfer +scripts/BP Draft 2/Blueprint Basic/Blueprint Basic network_2035/trn/transitLines.zac +scripts/BP Draft 2/Blueprint Basic/Blueprint Basic network_2035/trn/transitLines_commuter_rail.pnr +scripts/BP Draft 2/Blueprint Basic/Blueprint Basic network_2035/trn/transitLines_express_bus.pnr +scripts/BP Draft 2/Blueprint Basic/Blueprint Basic network_2035/trn/transitLines_ferry.pnr +scripts/BP Draft 2/Blueprint Basic/Blueprint Basic network_2035/trn/transitLines_heavy_rail.pnr +scripts/BP Draft 2/Blueprint Basic/Blueprint Basic network_2035/trn/transitLines_light_rail.pnr +scripts/BP Draft 2/Blueprint Basic/Blueprint Basic network_2035/trn/transitLineToVehicle.csv +scripts/BP Draft 2/Blueprint Basic/Blueprint Basic network_2035/trn/transitPrefixToVehicle.csv +scripts/BP Draft 2/Blueprint Basic/Blueprint Basic network_2035/trn/transitVehicleToCapacity.csv +scripts/BP Draft 2/Blueprint Basic/Blueprint Basic network_2035/trn/WALK_access.sup +scripts/BP Draft 2/Blueprint Basic/Blueprint Basic network_2035/trn/xfare.far +scripts/BP Draft 2/Blueprint Basic/Blueprint Basic network_2040/hwy/freeflow.net +scripts/BP Draft 2/Blueprint Basic/Blueprint Basic network_2040/hwy/tolls.csv +scripts/BP Draft 2/Blueprint Basic/Blueprint Basic network_2040/hwy/turnsam.pen +scripts/BP Draft 2/Blueprint Basic/Blueprint Basic network_2040/hwy/turnsop.pen +scripts/BP Draft 2/Blueprint Basic/Blueprint Basic network_2040/hwy/turnspm.pen +scripts/BP Draft 2/Blueprint Basic/Blueprint Basic network_2040/trn/ACE.far +scripts/BP Draft 2/Blueprint Basic/Blueprint Basic network_2040/trn/Amtrak.far +scripts/BP Draft 2/Blueprint Basic/Blueprint Basic network_2040/trn/BART.far +scripts/BP Draft 2/Blueprint Basic/Blueprint Basic network_2040/trn/Caltrain.far +scripts/BP Draft 2/Blueprint Basic/Blueprint Basic network_2040/trn/farelinks.far +scripts/BP Draft 2/Blueprint Basic/Blueprint Basic network_2040/trn/Ferry.far +scripts/BP Draft 2/Blueprint Basic/Blueprint Basic network_2040/trn/HSR.far +scripts/BP Draft 2/Blueprint Basic/Blueprint Basic network_2040/trn/SMART.far +scripts/BP Draft 2/Blueprint Basic/Blueprint Basic network_2040/trn/transit_faremat.block +scripts/BP Draft 2/Blueprint Basic/Blueprint Basic network_2040/trn/transitLines.access +scripts/BP Draft 2/Blueprint Basic/Blueprint Basic network_2040/trn/transitLines.lin +scripts/BP Draft 2/Blueprint Basic/Blueprint Basic network_2040/trn/transitLines.link +scripts/BP Draft 2/Blueprint Basic/Blueprint Basic network_2040/trn/transitLines.xfer +scripts/BP Draft 2/Blueprint Basic/Blueprint Basic network_2040/trn/transitLines.zac +scripts/BP Draft 2/Blueprint Basic/Blueprint Basic network_2040/trn/transitLines_commuter_rail.pnr +scripts/BP Draft 2/Blueprint Basic/Blueprint Basic network_2040/trn/transitLines_express_bus.pnr +scripts/BP Draft 2/Blueprint Basic/Blueprint Basic network_2040/trn/transitLines_ferry.pnr +scripts/BP Draft 2/Blueprint Basic/Blueprint Basic network_2040/trn/transitLines_heavy_rail.pnr +scripts/BP Draft 2/Blueprint Basic/Blueprint Basic network_2040/trn/transitLines_light_rail.pnr +scripts/BP Draft 2/Blueprint Basic/Blueprint Basic network_2040/trn/transitLineToVehicle.csv +scripts/BP Draft 2/Blueprint Basic/Blueprint Basic network_2040/trn/transitPrefixToVehicle.csv +scripts/BP Draft 2/Blueprint Basic/Blueprint Basic network_2040/trn/transitVehicleToCapacity.csv +scripts/BP Draft 2/Blueprint Basic/Blueprint Basic network_2040/trn/WALK_access.sup +scripts/BP Draft 2/Blueprint Basic/Blueprint Basic network_2040/trn/xfare.far +scripts/BP Draft 2/Blueprint Basic/Blueprint Basic network_2045/hwy/freeflow.net +scripts/BP Draft 2/Blueprint Basic/Blueprint Basic network_2045/hwy/tolls.csv +scripts/BP Draft 2/Blueprint Basic/Blueprint Basic network_2045/hwy/turnsam.pen +scripts/BP Draft 2/Blueprint Basic/Blueprint Basic network_2045/hwy/turnsop.pen +scripts/BP Draft 2/Blueprint Basic/Blueprint Basic network_2045/hwy/turnspm.pen +scripts/BP Draft 2/Blueprint Basic/Blueprint Basic network_2045/trn/ACE.far +scripts/BP Draft 2/Blueprint Basic/Blueprint Basic network_2045/trn/Amtrak.far +scripts/BP Draft 2/Blueprint Basic/Blueprint Basic network_2045/trn/BART.far +scripts/BP Draft 2/Blueprint Basic/Blueprint Basic network_2045/trn/Caltrain.far +scripts/BP Draft 2/Blueprint Basic/Blueprint Basic network_2045/trn/farelinks.far +scripts/BP Draft 2/Blueprint Basic/Blueprint Basic network_2045/trn/Ferry.far +scripts/BP Draft 2/Blueprint Basic/Blueprint Basic network_2045/trn/HSR.far +scripts/BP Draft 2/Blueprint Basic/Blueprint Basic network_2045/trn/SMART.far +scripts/BP Draft 2/Blueprint Basic/Blueprint Basic network_2045/trn/transit_faremat.block +scripts/BP Draft 2/Blueprint Basic/Blueprint Basic network_2045/trn/transitLines.access +scripts/BP Draft 2/Blueprint Basic/Blueprint Basic network_2045/trn/transitLines.lin +scripts/BP Draft 2/Blueprint Basic/Blueprint Basic network_2045/trn/transitLines.link +scripts/BP Draft 2/Blueprint Basic/Blueprint Basic network_2045/trn/transitLines.xfer +scripts/BP Draft 2/Blueprint Basic/Blueprint Basic network_2045/trn/transitLines.zac +scripts/BP Draft 2/Blueprint Basic/Blueprint Basic network_2045/trn/transitLines_commuter_rail.pnr +scripts/BP Draft 2/Blueprint Basic/Blueprint Basic network_2045/trn/transitLines_express_bus.pnr +scripts/BP Draft 2/Blueprint Basic/Blueprint Basic network_2045/trn/transitLines_ferry.pnr +scripts/BP Draft 2/Blueprint Basic/Blueprint Basic network_2045/trn/transitLines_heavy_rail.pnr +scripts/BP Draft 2/Blueprint Basic/Blueprint Basic network_2045/trn/transitLines_light_rail.pnr +scripts/BP Draft 2/Blueprint Basic/Blueprint Basic network_2045/trn/transitLineToVehicle.csv +scripts/BP Draft 2/Blueprint Basic/Blueprint Basic network_2045/trn/transitPrefixToVehicle.csv +scripts/BP Draft 2/Blueprint Basic/Blueprint Basic network_2045/trn/transitVehicleToCapacity.csv +scripts/BP Draft 2/Blueprint Basic/Blueprint Basic network_2045/trn/WALK_access.sup +scripts/BP Draft 2/Blueprint Basic/Blueprint Basic network_2045/trn/xfare.far +scripts/BP Draft 2/Blueprint Basic/Blueprint Basic network_2050/hwy/freeflow.net +scripts/BP Draft 2/Blueprint Basic/Blueprint Basic network_2050/hwy/tolls.csv +scripts/BP Draft 2/Blueprint Basic/Blueprint Basic network_2050/hwy/turnsam.pen +scripts/BP Draft 2/Blueprint Basic/Blueprint Basic network_2050/hwy/turnsop.pen +scripts/BP Draft 2/Blueprint Basic/Blueprint Basic network_2050/hwy/turnspm.pen +scripts/BP Draft 2/Blueprint Basic/Blueprint Basic network_2050/trn/ACE.far +scripts/BP Draft 2/Blueprint Basic/Blueprint Basic network_2050/trn/Amtrak.far +scripts/BP Draft 2/Blueprint Basic/Blueprint Basic network_2050/trn/BART.far +scripts/BP Draft 2/Blueprint Basic/Blueprint Basic network_2050/trn/Caltrain.far +scripts/BP Draft 2/Blueprint Basic/Blueprint Basic network_2050/trn/farelinks.far +scripts/BP Draft 2/Blueprint Basic/Blueprint Basic network_2050/trn/Ferry.far +scripts/BP Draft 2/Blueprint Basic/Blueprint Basic network_2050/trn/HSR.far +scripts/BP Draft 2/Blueprint Basic/Blueprint Basic network_2050/trn/SMART.far +scripts/BP Draft 2/Blueprint Basic/Blueprint Basic network_2050/trn/transit_faremat.block +scripts/BP Draft 2/Blueprint Basic/Blueprint Basic network_2050/trn/transitLines.access +scripts/BP Draft 2/Blueprint Basic/Blueprint Basic network_2050/trn/transitLines.lin +scripts/BP Draft 2/Blueprint Basic/Blueprint Basic network_2050/trn/transitLines.link +scripts/BP Draft 2/Blueprint Basic/Blueprint Basic network_2050/trn/transitLines.xfer +scripts/BP Draft 2/Blueprint Basic/Blueprint Basic network_2050/trn/transitLines.zac +scripts/BP Draft 2/Blueprint Basic/Blueprint Basic network_2050/trn/transitLines_commuter_rail.pnr +scripts/BP Draft 2/Blueprint Basic/Blueprint Basic network_2050/trn/transitLines_express_bus.pnr +scripts/BP Draft 2/Blueprint Basic/Blueprint Basic network_2050/trn/transitLines_ferry.pnr +scripts/BP Draft 2/Blueprint Basic/Blueprint Basic network_2050/trn/transitLines_heavy_rail.pnr +scripts/BP Draft 2/Blueprint Basic/Blueprint Basic network_2050/trn/transitLines_light_rail.pnr +scripts/BP Draft 2/Blueprint Basic/Blueprint Basic network_2050/trn/transitLineToVehicle.csv +scripts/BP Draft 2/Blueprint Basic/Blueprint Basic network_2050/trn/transitPrefixToVehicle.csv +scripts/BP Draft 2/Blueprint Basic/Blueprint Basic network_2050/trn/transitVehicleToCapacity.csv +scripts/BP Draft 2/Blueprint Basic/Blueprint Basic network_2050/trn/WALK_access.sup +scripts/BP Draft 2/Blueprint Basic/Blueprint Basic network_2050/trn/xfare.far +scripts/net_spec_test_flavia.py diff --git a/README.md b/README.md index 75d27f7..acf96fb 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,11 @@ +Current Branch Changes: +======================= +This branch's purpose is to be used for TM1.5.1.10 model runs. +It was created from the 9 Feb 2023 commit e95e5a3. + +please contact david.hensle@rsginc.com with questions + + NetworkWrangler =============== @@ -8,23 +16,42 @@ set of projects to that base network. The base network and resulting network are in the Citilabs Cube format (http://www.citilabs.com/software/cube/) +See also: [next generation Network Wrangler](https://github.com/wsp-sag/network_wrangler) + Contributors ======= NetworkWrangler is the brainchild of Billy Charlton, who was the Deputy Director for Technology Services at SFCTA through 2011. Contributors include: * Elizabeth Sall, 2010-2014 -* Lisa Zorn, 2010-2014 +* Lisa Zorn, 2010-2014, 2018-Present (MTC fork) * Dan Tischler, 2011-Present * Drew Cooper, 2013-Present * Bhargava Sana, 2015-Present -Usage +Install ======= +NetworkWrangler recently was updated to Python3. Recommand using conda to install and manage NetworkWrangler environment. For example, run the following commands in Anaconda Prompt: +``` +conda env create -f M:\Software\Anaconda\NetworkWrangler_py3.yml +conda activate NetworkWrangler-py3 +cd \NetworkWrangler +pip install -e . +``` -Build a network by running the `build_network.py` script in the `/scripts` folder. - - python build_network.py [-c configword] [-m test] network_specification.py +Usage +======= +If needed, set the PATHs before calling the build_network.py script, for example: +``` +set PATH=%PATH%;C:\Users\mtcpb\.conda\envs\NetworkWrangler-py3;C:\Users\mtcpb\.conda\envs\NetworkWrangler-py3\Scripts +set PATH=%PATH%;C:\Program Files\Citilabs\CubeVoyager; +set PYTHONPATH=%PYTHONPATH%;C:\Users\mtcpb\Documents\GitHub\NetworkWrangler +set PYTHONPATH=%PYTHONPATH%;C:\Users\mtcpb\Documents\GitHub\NetworkWrangler\_static +``` +With PATHs set, build a network by running the `build_network.py` script in the `/scripts` folder. +``` +python build_network.py [-c configword] [-m test] network_specification.py +``` This will build a network using the specifications in `network_specification.py`, which should define the variables listed below (in this script) @@ -34,4 +61,4 @@ If test mode is specified (with -m test), then the following happen: * TAG is not used for TEST_PROJECTS The [-c configword] is if you want an optional word for your network_specification.py - (e.g. to have multiple scenarios in one file). Access it via CONFIG_WORD. \ No newline at end of file + (e.g. to have multiple scenarios in one file). Access it via CONFIG_WORD. diff --git a/Wrangler/HighwayNetwork.py b/Wrangler/HighwayNetwork.py index a2b4f62..ea9eb5a 100644 --- a/Wrangler/HighwayNetwork.py +++ b/Wrangler/HighwayNetwork.py @@ -86,6 +86,19 @@ def applyBasenetwork(self, parentdir, networkdir, gitdir, tierNetworkName): # done self.applyingBasenetwork = False + def saveNetworkFiles(self, suffix, to_suffix): + """ + Since roadway networks are not stored in memory but in files, this is useful + for when the network builder is doing something tricky. + """ + for filename in ["FREEFLOW.BLD", "turnsam.pen", "turnspm.pen", "turnsop.pen", "tolls.csv"]: + if to_suffix: + shutil.copy2(src=filename, dst="{}{}".format(filename, suffix)) + WranglerLogger.debug("Copying {:20} to {}{}".format(filename, filename, suffix)) + else: + shutil.copy2(src="{}{}".format(filename, suffix), dst=filename) + WranglerLogger.debug("Copying {:20} to {}".format(filename+suffix, filename)) + def applyProject(self, parentdir, networkdir, gitdir, projectsubdir=None, **kwargs): """ Applies a roadway project by calling ``runtpp`` on the ``apply.s`` script. @@ -145,6 +158,7 @@ def applyProject(self, parentdir, networkdir, gitdir, projectsubdir=None, **kwar nodemerge = re.compile("NODEMERGE: \d+") linkmerge = re.compile("LINKMERGE: \d+-\d+") + cube_success = re.compile("\s*(VOYAGER)\s+(ReturnCode)\s*=\s*([01])\s+") license_error = False for line in cubeStdout: line = line.rstrip() @@ -165,9 +179,18 @@ def applyProject(self, parentdir, networkdir, gitdir, projectsubdir=None, **kwar time.sleep(1) continue + if cuberet != 0 and cuberet != 1 and len(cubeStdout) > 1: + + # cuberet may be wrong -- check last stdout + WranglerLogger.debug("checking cubeStdout[-1]: {}".format(cubeStdout[-1])) + # WranglerLogger.debug("match: {}".format(re.match(cube_success,cubeStdout[-1]))) + if re.match(cube_success,cubeStdout[-1]): + WranglerLogger.debug("Overriding cuberet {} with 0 due to last cubeStdout line".format(cuberet)) + cuberet = 0 if cuberet != 0 and cuberet != 1: - WranglerLogger.fatal("FAIL! Project: "+applyScript) + WranglerLogger.debug("cubeStdout: {}".format(cubeStdout)) + WranglerLogger.fatal("FAIL! Project: {} cuberet={}".format(applyScript, cuberet)) raise NetworkException("HighwayNetwork applyProject failed; see log file") else: @@ -212,7 +235,7 @@ def mergeTolls(self, tollsfile, newtollsfile): """ Merge the given tolls file with the existing. """ - WranglerLogger.debug("TODO: merging tolls") + WranglerLogger.debug("mergeTolls({},{}) called".format(tollsfile, newtollsfile)) # read the original file -- fac_index is the key tolls_config = collections.OrderedDict() @@ -222,6 +245,7 @@ def mergeTolls(self, tollsfile, newtollsfile): # not using csv.DictReader because in python2, it doesn't read an ordered dict :( for row in tolls_reader: row_dict = collections.OrderedDict(zip(fieldnames, row)) + WranglerLogger.debug("row_dict: {}".format(row_dict)) tolls_config[row_dict["fac_index"]] = row_dict tolls.close() @@ -245,7 +269,7 @@ def mergeTolls(self, tollsfile, newtollsfile): newtolls.close() # write it out - tolls = open(tollsfile, mode='wb') + tolls = open(tollsfile, mode='w', newline='') # newline arg passed because of https://docs.python.org/3/library/csv.html#id3 tolls_writer = csv.writer(tolls) tolls_writer.writerow(fieldnames) for fac_index, row in tolls_config.items(): diff --git a/Wrangler/Network.py b/Wrangler/Network.py index ce70e56..eb10c90 100644 --- a/Wrangler/Network.py +++ b/Wrangler/Network.py @@ -53,22 +53,31 @@ def _runAndLog(self, cmd, run_dir=".", logStdoutAndStderr=False, env=None): if env: myenv = copy.deepcopy(os.environ) myenv.update(env) - # ranglerLogger.debug("Using environment {}".format(myenv)) - - proc = subprocess.Popen( cmd, cwd = run_dir, stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True, env=myenv ) - retStdout = [] - for line in proc.stdout: - line = line.strip(b'\r\n') - if logStdoutAndStderr: WranglerLogger.debug("stdout: " + line) - retStdout.append(line) - - retStderr = [] - for line in proc.stderr: - line = line.strip(b'\r\n') - if logStdoutAndStderr: WranglerLogger.debug("stderr: " + line) - retStderr.append(line) - retcode = proc.wait() - WranglerLogger.debug("Received %d from [%s] run in [%s]" % (retcode, cmd, run_dir)) + # WranglerLogger.debug("Using environment {}".format(myenv)) + + try: + proc = subprocess.Popen( cmd, cwd = run_dir, stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True, env=myenv ) + retStdout = [] + for line in proc.stdout: + if type(line)==bytes: line = line.decode() # convert to string, not byetes + line = line.strip('\r\n') + if logStdoutAndStderr: WranglerLogger.debug("stdout: " + line) + retStdout.append(line) + + retStderr = [] + for line in proc.stderr: + if type(line)==bytes: line = line.decode() # convert to string, not byetes + line = line.strip('\r\n') + if logStdoutAndStderr: WranglerLogger.debug("stderr: " + line) + retStderr.append(line) + retcode = proc.wait() + WranglerLogger.debug("Received {} from [{}] run in [{}]".format(retcode, cmd, run_dir)) + + except Exception as inst: + WranglerLogger.error('Exception caught') + WranglerLogger.error(type(inst)) # the exception instance + WranglerLogger.error(inst.args) # arguments stored in .args + WranglerLogger.error(inst) # __str__ allows args to be printed directly, return (retcode, retStdout, retStderr) def getReqs(self, networkdir, projectsubdir=None, tag=None, projtype=None, tempdir=None): @@ -419,12 +428,17 @@ def getCommit(self, gitdir): Figures out the SHA1 hash commit string for the given gitdir (so gitdir is a git dir). (e.g. a 40-character hex string) """ + # WranglerLogger.debug("getCommit({}) called".format(gitdir)) + # SET_CAPCLASS is special and setup by NetworkWrangler + if gitdir.endswith("set_capclass"): + return "set_capclass_N/A" + cmd = r"git log -1" (retcode, retstdout, retstderr) = self._runAndLog(cmd, gitdir) if len(retstdout)<3: raise NetworkException("Git log failed; see log file") - m = re.match(git_commit_pattern, retstdout[0].decode("utf-8")) + m = re.match(git_commit_pattern, retstdout[0]) if not m: raise NetworkException("Didn't understand git log output: [" + retstdout[0] + "]") diff --git a/Wrangler/Node.py b/Wrangler/Node.py index 042a2aa..772d817 100644 --- a/Wrangler/Node.py +++ b/Wrangler/Node.py @@ -1,4 +1,5 @@ import os,sys +from .Logger import WranglerLogger __all__ = ['Node'] @@ -46,6 +47,19 @@ def getNum(self): """ return abs(int(self.num)) + def __eq__(self, other): + """ + For finding nodes in a list of Node objects + """ + if isinstance(other, int): + return int(self.num) == other + elif isinstance(other, Node): + return int(self.num) == int(other.num) + elif isinstance(other, str): + return int(self.num) == int(other) + else: + WranglerLogger.error("Node.__eq__ called with other type {}".format(type(other))) + def replaceNum(self, num): """ Replace the node number with the given num, diff --git a/Wrangler/Supplink.py b/Wrangler/Supplink.py index ed8b123..48f801c 100644 --- a/Wrangler/Supplink.py +++ b/Wrangler/Supplink.py @@ -19,7 +19,7 @@ class Supplink(dict): def __init__(self): dict.__init__(self) - MODES_INV = dict((v,k) for k,v in Supplink.MODES.iteritems()) + MODES_INV = dict((v,k) for k,v in Supplink.MODES.items()) self.id='' # string, e.g. "1-7719" self.comment=None diff --git a/Wrangler/TransitAssignmentData.py b/Wrangler/TransitAssignmentData.py index f979642..2857e7b 100644 --- a/Wrangler/TransitAssignmentData.py +++ b/Wrangler/TransitAssignmentData.py @@ -173,10 +173,9 @@ def initializeFields(self, headerRow=None): "OWNER", "AB_VOL","AB_BRDA","AB_XITA","AB_BRDB","AB_XITB", "BA_VOL","BA_BRDA","BA_XITA","BA_BRDB","BA_XITB"] - # print("csvColnames = {}".format(self.csvColnames)) - - self.colnameToCsvIndex = dict((self.csvColnames[idx],idx) for idx in range(len(self.csvColnames))) + self.colnameToCsvIndex = dict((self.csvColnames[idx],idx) for idx in range(len(self.csvColnames))) + # copy these directly self.trnAsgnFields = {"A": 'u4', "B": 'u4', @@ -189,7 +188,7 @@ def initializeFields(self, headerRow=None): "NAME": 'a13', "OWNER": 'a10', } - self.trnAsgnCopyFields = self.trnAsgnFields.keys() + self.trnAsgnCopyFields = list(self.trnAsgnFields.keys()) # these are in the dbf not the csv (grrrr) self.trnAsgnFields["FREQ"] = 'f4' self.trnAsgnFields["SEQ"] = 'u1' @@ -233,7 +232,6 @@ def initializeFields(self, headerRow=None): for field in self.trnAsgnAdditiveFields: self.aggregateFields[field]='f4' - def readTransitAssignmentCsvs(self): """ Read the transit assignment dbfs, the direct output of Cube's transit assignment. @@ -264,7 +262,7 @@ def readTransitAssignmentCsvs(self): numrecs = 0 totalrows = 0 - filereader = csv.reader(open(filename, 'rb'), delimiter=',', quoting=csv.QUOTE_NONE) + filereader = csv.reader(open(filename, 'r'), delimiter=',', quoting=csv.QUOTE_NONE) for row in filereader: # header row? @@ -294,7 +292,7 @@ def readTransitAssignmentCsvs(self): self.trnAsgnTable = DataTable(numRecords=numrecs, fieldNames=self.trnAsgnFields.keys(), - numpyFieldTypes=self.trnAsgnFields.values()) + numpyFieldTypes=list(self.trnAsgnFields.values())) ABNameSeqSet = set() WranglerLogger.debug("Created dataTable") @@ -302,8 +300,7 @@ def readTransitAssignmentCsvs(self): newrownum = 0 # row number in the trnAsgnTable,ABNameSeq_List -- rows we're keeping oldrownum = 0 # row number in the csv,dbf -- all input rows - filereader = csv.reader(open(filename, 'rb'), delimiter=',', quoting=csv.QUOTE_NONE) - + filereader = csv.reader(open(filename, 'r'), delimiter=',', quoting=csv.QUOTE_NONE) # for the first csv only, also read the dbf for the freq and seq fields if mode == self.MODES[0]: if self.modelType == Network.MODEL_TYPE_CHAMP: @@ -343,7 +340,6 @@ def readTransitAssignmentCsvs(self): for field in self.trnAsgnCopyFields: try: - # integer fields if self.trnAsgnFields[field][0] in ['u','b']: if row[self.colnameToCsvIndex[field]]=="": @@ -403,6 +399,7 @@ def readTransitAssignmentCsvs(self): ABNameSeq = tryABNameSeq self.trnAsgnTable[newrownum]["ABNAMESEQ"] = ABNameSeq ABNameSeqSet.add(ABNameSeq) + # WranglerLogger.debug("AABNAMESEQ={} type={}".format(ABNameSeq, type(ABNameSeq))) ABNameSeq_List.append((int(row[self.colnameToCsvIndex["A"]]), int(row[self.colnameToCsvIndex["B"]]), @@ -425,12 +422,12 @@ def readTransitAssignmentCsvs(self): self.trnAsgnTable[newrownum]["PERIODCAP"] = 0 # if we still don't have a system, warn - if self.trnAsgnTable[newrownum]["SYSTEM"] == "" and not warnline.has_key(linename): + if self.trnAsgnTable[newrownum]["SYSTEM"] == "" and linename not in warnline: WranglerLogger.warning("No default system: " + linename) warnline[linename] =1 #---------add in any grouping that may want to use - if self.lineToGroup.has_key(linename): + if linename in self.lineToGroup: self.trnAsgnTable[newrownum]["GROUP"] = self.lineToGroup[linename] else: self.trnAsgnTable[newrownum]["GROUP"] = "" @@ -446,7 +443,7 @@ def readTransitAssignmentCsvs(self): # Add in the subsequent assignment files else: - + # print oldrownum, newrownum, ABNameSeq_List[newrownum] # print row[self.colnameToCsvIndex["NAME"]], ABNameSeq_List[oldrownum][2] if ((int(row[self.colnameToCsvIndex["A"]]) != ABNameSeq_List[newrownum][0]) or @@ -462,7 +459,9 @@ def readTransitAssignmentCsvs(self): row[self.colnameToCsvIndex["B"]] + " " + \ row[self.colnameToCsvIndex["NAME"]].rstrip() if ABNameSeq_List[newrownum][3]>0: - ABNameSeq += " " + str(ABNameSeq_List[newrownum][3]) + ABNameSeq += " " + str(ABNameSeq_List[newrownum][3]) + # convert to bytes - the index in the dataTable is a byte string + ABNameSeq = ABNameSeq.encode('utf-8') for field in self.trnAsgnAdditiveFields: if row[self.colnameToCsvIndex[field]] !="": self.trnAsgnTable[ABNameSeq][field] += float(row[self.colnameToCsvIndex[field]]) @@ -512,8 +511,8 @@ def buildAggregateTable(self): ABSet.add(row["AB"]) self.aggregateTable = DataTable(numRecords=len(ABSet), - fieldNames=self.aggregateFields.keys(), - numpyFieldTypes=self.aggregateFields.values()) + fieldNames=list(self.aggregateFields.keys()), + numpyFieldTypes=list(self.aggregateFields.values())) ABtoRowIndex = {} rowsUsed = 0 for row in self.trnAsgnTable: diff --git a/Wrangler/TransitCapacity.py b/Wrangler/TransitCapacity.py index cfb9e3a..b69421d 100644 --- a/Wrangler/TransitCapacity.py +++ b/Wrangler/TransitCapacity.py @@ -154,7 +154,7 @@ def getSystemAndVehicleType(self, linename, timeperiod): Convenience function. Returns tuple: best guess of (system, vehicletype) """ linenameU = linename.upper() - if self.linenameToAttributes.has_key(linenameU): + if linenameU in self.linenameToAttributes: return (self.linenameToAttributes[linenameU][TransitCapacity.ATTR_SYSTEM], self.linenameToAttributes[linenameU][TransitCapacity.TIMEPERIOD_TO_VEHTYPIDX[timeperiod]]) @@ -183,7 +183,7 @@ def getFullname(self, linename, timeperiod): Returns best guess of fullname, or empty string if unknown """ linenameU = linename.upper() - if self.linenameToAttributes.has_key(linenameU): + if linenameU in self.linenameToAttributes: return self.linenameToAttributes[linenameU][TransitCapacity.ATTR_FULLNAME] else: return "" diff --git a/Wrangler/TransitLine.py b/Wrangler/TransitLine.py index 6208d65..2041ae6 100644 --- a/Wrangler/TransitLine.py +++ b/Wrangler/TransitLine.py @@ -122,6 +122,18 @@ def __iter__(self): self.currentStopIdx = 0 return self + def __eq__(self, other): + """ + Required to make list of TransitLines work e.g. lines_list.index("line_name") + """ + if isinstance(other, str): + return self.name == other + elif isinstance(other, TransitLine): + return self.name == other.name + else: + WranglerLogger.error("TransitLine.__eq__ called with other type {}".format(type(other))) + return False + def __next__(self): """ Method for iterator. Iterator usage:: @@ -139,7 +151,7 @@ def __next__(self): # python 2 backwards compat next = __next__ - def setFreqs(self, freqs, timepers=None, allowDowngrades=True): + def setFreqs(self, freqs, timepers=None, allowDowngrades=True, modeltype=Network.MODEL_TYPE_TM1): '''Set some or all five headways (AM,MD,PM,EV,EA) - freqs is a list of numbers (or can be one number if only setting one headway) also accepts list of strings of numbers e.g. ["8","0","8","0","0"] @@ -149,7 +161,11 @@ def setFreqs(self, freqs, timepers=None, allowDowngrades=True): - allowDowngrades (optional, pass either True or False) specifies whether headways may be increased (i.e., whether service may be reduced) with the current action. ''' - all_timepers = ['AM','MD','PM','EV','EA'] + if modeltype==Network.MODEL_TYPE_CHAMP: + all_timepers = ['AM','MD','PM','EV','EA'] + elif modeltype==Network.MODEL_TYPE_TM1: + all_timepers = ['EA','AM','MD','PM','EV'] + if timepers in (None, True, 'All', 'all', 'ALL'): if not len(freqs)==5: raise NetworkException('Must specify all 5 frequencies or specify time periods to set') num_freqs = 5 @@ -181,7 +197,7 @@ def setFreqs(self, freqs, timepers=None, allowDowngrades=True): if(allowDowngrades): self.attr[attr_set] = float(freqs[i]) else: - self.attr[attr_set] = min(float(freqs[i]),self.attr[attr_set]) + self.attr[attr_set] = min(float(freqs[i]),float(self.attr[attr_set])) def getFreqs(self): @@ -259,7 +275,7 @@ def getModeType(self, modeltype): (e.g. one of "Local", "BRT", "LRT", "Premium", "Ferry" or "BART") """ modenum = int(self.attr['MODE']) - for modetype,modelist in TransitLine.MODETYPE_TO_MODES[modeltype].iteritems(): + for modetype,modelist in TransitLine.MODETYPE_TO_MODES[modeltype].items(): if modenum in modelist: return modetype return None @@ -453,11 +469,14 @@ def extendLine(self, oldnode, newsection, beginning=True): If beginning, does this at the beginning; otherwise at the end. """ - try: - ind = self.n.index(oldnode) - except: - ind = self.n.index(-oldnode) - + oldnode_found = False + for ind in range(len(self.n)): + if self.n[ind].getNum()==oldnode: + oldnode_found = True + break + if oldnode_found == False: + raise NetworkException("extendLine() called but oldnode {} not found in line nodes {}".format(oldnode, self.listNodeIds(ignoreStops=False))) + # make the new nodes for i in range(len(newsection)): if isinstance(newsection[i],int): newsection[i] = Node(newsection[i]) @@ -478,7 +497,8 @@ def replaceSegment(self, node1, node2, newsection, preserveStopStatus=False): for i in range(len(new_section_ints)): if isinstance(new_section_ints[i],Node): new_section_ints[i] = new_section_ints[i].num - WranglerLogger.debug("replacing segment " + str(node1) + " "+str(node2)+" with "+str(new_section_ints)+" for "+self.name) + WranglerLogger.debug("replacing segment in {} {}-{} with {}".format( + self.name, node1, node2 ,new_section_ints)) try: ind1 = self.n.index(node1) stop1 = True diff --git a/Wrangler/TransitNetwork.py b/Wrangler/TransitNetwork.py index 0edfd5f..3228ace 100644 --- a/Wrangler/TransitNetwork.py +++ b/Wrangler/TransitNetwork.py @@ -382,7 +382,7 @@ def validateWnrsAndPnrs(self): # print it all out for lineset in nodeInfo.keys(): - stops = nodeInfo[lineset].keys() + stops = list(nodeInfo[lineset].keys()) stops.sort() WranglerLogger.debug("--------------- Line set %s %s -- hasOffstreet? %s------------------" % @@ -406,7 +406,7 @@ def validateWnrsAndPnrs(self): if critical_found: raise NetworkException("Critical errors found") - + def line(self, name): """ If a string is passed in, return the line for that name exactly (a :py:class:`TransitLine` object). @@ -426,10 +426,32 @@ def line(self, name): if name=='all': allLines = [] for i in range(len(self.lines)): + if isinstance(self.lines[i],str): continue allLines.append(self.lines[i]) return allLines raise NetworkException('Line name not found: %s' % (name,)) - + + def deleteLine(self, name): + """ + If a string is passed in, delete the line for that name exactly. (Throws an exception of it's not a line name) + If a regex, delete all the lines that match, debug-logging the deleted line names. + """ + if isinstance(name,str): + del self.lines[self.lines.index(name)] + return + + if str(type(name))==str(type(re.compile("."))): + for idx in range(len(self.lines)-1,-1,-1): # go backwards + if isinstance(self.lines[idx],str): continue + if name.match(self.lines[idx].name): + WranglerLogger.debug("Deleting line {}".format(self.lines[idx].name)) + del self.lines[idx] + return + + # didn't understand name argument + raise NetworkException("deleteLine() didn't understand name argument [{}]".format(name)) + + def deleteLinkForNodes(self, nodeA, nodeB, include_reverse=True): """ Delete any TransitLink in self.links[] from nodeA to nodeB (these should be integers). @@ -1363,13 +1385,15 @@ def moveBusesToHovAndExpressLanes(self): WranglerLogger.debug("\n:{}".format(links_df.head())) # filter out HOV and express lane links - hov_links_df = links_df.loc[ (links_df.USE == 2)|(links_df.USE==3) ] - el_links_df = links_df.loc[ links_df.TOLLCLASS >= 11 ] - gp_links_df = links_df.loc[ (links_df.USE==1)&((links_df.FT<=3)|(links_df.FT==5)|(links_df.FT==7)|(links_df.FT==8)|(links_df.FT==10))] - dummy_links_df = links_df.loc[ links_df.FT==6 ] + hov_links_df = links_df.loc[ (links_df.USE == 2)|(links_df.USE==3) ] + el_links_df = links_df.loc[ links_df.TOLLCLASS >= 11 ] + notruck_links_df = links_df.loc[ (links_df.TOLLCLASS==0) & (links_df.USE==4)] + gp_links_df = links_df.loc[ (links_df.USE==1)&((links_df.FT<=3)|(links_df.FT==5)|(links_df.FT==7)|(links_df.FT==8)|(links_df.FT==10))] + gp_notruck_links_df = gp_links_df.append(notruck_links_df) + dummy_links_df = links_df.loc[ links_df.FT==6 ] WranglerLogger.debug("Found {} hov links, {} express lane links and {} general purpose links".format( - len(hov_links_df), len(el_links_df), len(gp_links_df))) + len(hov_links_df), len(el_links_df), len(gp_notruck_links_df))) # dummy B -> hov A, a_GP1 will be the first point of dummy access link hov_group1_df = pandas.merge(left=hov_links_df, right=dummy_links_df[["a","b"]], @@ -1380,7 +1404,7 @@ def moveBusesToHovAndExpressLanes(self): how="inner", left_on=["b"], right_on=["a"], suffixes=["","_GP2"]).drop(columns="a_GP2") # merge to the full GP links for complete info - hov_group1_df = pandas.merge(left=hov_group1_df, right=gp_links_df, how="inner", + hov_group1_df = pandas.merge(left=hov_group1_df, right=gp_notruck_links_df, how="inner", left_on=["a_GP1", "b_GP2"], right_on=["a","b"], suffixes=["","_GP"]).drop(columns=["a_GP1","b_GP2"]) WranglerLogger.debug("Found general purpose links for {} out of {} hov links: \n{}".format( @@ -1391,6 +1415,9 @@ def moveBusesToHovAndExpressLanes(self): WranglerLogger.debug("\n{}".format(hov_unmatched_df.head())) hov_unmatched_df = hov_unmatched_df.loc[ pandas.isnull(hov_unmatched_df.a_GP) ].drop(columns=["a_GP","b_GP"]) WranglerLogger.debug("hov links without match ({}):\n{}".format(len(hov_unmatched_df), hov_unmatched_df)) + hov_unmatched_df_QAQC_FILE = 'hov_unmatched.csv' + WranglerLogger.info("Export these links to {} for debugging".format(hov_unmatched_df_QAQC_FILE)) + hov_unmatched_df.to_csv(hov_unmatched_df_QAQC_FILE, index=False) # replace all instances of a_GP, b_GP with a_GP,a,hov,b_hov,b_gp # keep hov_nodes and gp_nodes @@ -1428,7 +1455,7 @@ def moveBusesToHovAndExpressLanes(self): how="inner", left_on=["b"], right_on=["a"], suffixes=["","_GP2"]).drop(columns="a_GP2") # merge to the full GP links for complete info - el_group1_df = pandas.merge(left=el_group1_df, right=gp_links_df, how="inner", + el_group1_df = pandas.merge(left=el_group1_df, right=gp_notruck_links_df, how="inner", left_on=["a_GP1", "b_GP2"], right_on=["a","b"], suffixes=["","_GP"]).drop(columns=["a_GP1","b_GP2"]) WranglerLogger.debug("Found general purpose links for {} out of {} el links: \n{}".format( @@ -1476,10 +1503,15 @@ def checkValidityOfLinks(self, cubeNetFile): import Cube extra_link_vars = [] + link_var_names = {} + if self.modelType == Network.MODEL_TYPE_CHAMP: extra_link_vars=['STREETNAME', 'LANE_AM', 'LANE_OP','LANE_PM', 'BUSLANE_AM', 'BUSLANE_OP', 'BUSLANE_PM'] + elif self.modelType == Network.MODEL_TYPE_TM1: + extra_link_vars=['LANES','BRT'] + link_var_names={ 'DISTANCE':0, 'LANES':1, 'BRT':2 } (nodes_dict, links_dict) = Cube.import_cube_nodes_links_from_csvs(cubeNetFile, extra_link_vars=extra_link_vars, @@ -1487,6 +1519,8 @@ def checkValidityOfLinks(self, cubeNetFile): links_csv=os.path.join(os.getcwd(),"cubenet_validate_links.csv"), nodes_csv=os.path.join(os.getcwd(),"cubenet_validate_nodes.csv"), exportIfExists=True) + + WranglerLogger.debug("checkValidityOfLinks(): using links from {} to check lines".format(cubeNetFile)) for line in self: # todo fix this @@ -1509,7 +1543,16 @@ def checkValidityOfLinks(self, cubeNetFile): for (a,b) in link_list: # it's a road link - if (a,b) in links_dict: continue + if (a,b) in links_dict: + + if self.modelType == Network.MODEL_TYPE_TM1: + # if LANES = 0 and BRT != 1 then transit will be suuuuuuuper slow + if ((int(links_dict[(a,b)][link_var_names['LANES']]) == 0) and \ + (int(links_dict[(a,b)][link_var_names['BRT' ]]) != 1)): + msg = "line {} is running on link {}-{} which has LANES=0 and BRT!=1".format(line.name, a, b) + WranglerLogger.fatal(msg) + raise NetworkException(msg) + continue found_link = False for link in self.links: diff --git a/Wrangler/__init__.py b/Wrangler/__init__.py index 4284b7f..afd116f 100644 --- a/Wrangler/__init__.py +++ b/Wrangler/__init__.py @@ -1,4 +1,4 @@ -import sys +import os, sys from .Faresystem import Faresystem from .Linki import Linki from .Network import Network @@ -6,6 +6,10 @@ from .PTSystem import PTSystem from .PNRLink import PNRLink from .Supplink import Supplink + +# add ..\_static for dataTable import +sys.path.append(os.path.abspath(os.path.join(os.path.dirname(os.path.realpath(__file__)), "..","_static"))) # for dataTable + from .TransitAssignmentData import TransitAssignmentData ## from .TransitCapacity import TransitCapacity from .TransitLine import TransitLine diff --git a/_static/Cube/CubeNet.py b/_static/Cube/CubeNet.py index 621eb01..c77a136 100644 --- a/_static/Cube/CubeNet.py +++ b/_static/Cube/CubeNet.py @@ -7,10 +7,11 @@ -Lisa 2012.03.12 """ -import copy, os, time +import copy, os, re, time from socket import gethostname, getfqdn CUBE_COMPUTER = "vanness" +CUBE_SUCCESS = re.compile("\s*(VOYAGER)\s+(ReturnCode)\s*=\s*([01])\s+") def getCubeHostnames(): """ @@ -79,41 +80,46 @@ def export_cubenet_to_csvs(file, extra_link_vars=[], extra_node_vars=[], NUM_RETRIES = 5 for attempt in range(1,NUM_RETRIES+1): + cube_stdout = [] license_error = False if hostname not in getCubeHostnames(): if links_csv == None or nodes_csv == None: - print "export_cubenet_to_csvs requires a links_csv and nodes_csv output file if dispatching to %s (temp won't work)" % CUBE_COMPUTER + print("export_cubenet_to_csvs requires a links_csv and nodes_csv output file if dispatching to {} (temp won't work)".format(CUBE_COMPUTER)) sys.exit(2) env["MACHINES"] = CUBE_COMPUTER cmd = r'y:\champ\util\bin\dispatch-one.bat "runtpp ' + script + '"' - print cmd + print(cmd) proc = subprocess.Popen( cmd, cwd = filedir, stdout=subprocess.PIPE, stderr=subprocess.PIPE, env=env) for line in proc.stdout: + if type(line)==bytes: line = line.decode() # convert to string, not byetes line = line.strip('\r\n') - print "stdout: " + line + print("stdout: {}".format(line)) + cube_stdout.append(line) if line=="RUNTPP: Licensing error": license_error = True else: cmd = 'runtpp.exe ' + script - print cmd - print filedir + print(cmd) + print(filedir) proc = subprocess.Popen( cmd, cwd = filedir, stdout=subprocess.PIPE, stderr=subprocess.PIPE, env=env) for line in proc.stdout: + if type(line)==bytes: line = line.decode() # convert to string, not byetes line = line.strip('\r\n') - print "stdout: " + line + print("stdout: {}".format(line)) + cube_stdout.append(line) if line=="RUNTPP: Licensing error": license_error = True - print "EXPORTING CUBE NETWORK: ",env['CUBENET'] - print "...adding variables %s, %s:" % (env['XTRALINKVAR'], env['XTRANODEVAR']) - print "...running script: \n %s" % (script) + print("EXPORTING CUBE NETWORK: {}".format(env['CUBENET'])) + print("...adding variables {}, {}:".format(env['XTRALINKVAR'], env['XTRANODEVAR'])) + print("...running script: \n {}".format(script)) # retry on license error if license_error: - print "Received license error" + print("Received license error") if attempt == NUM_RETRIES: - print "Out of retry attempts" + print("Out of retry attempts") sys.exit(2) # retry @@ -123,9 +129,19 @@ def export_cubenet_to_csvs(file, extra_link_vars=[], extra_node_vars=[], retStderr = [] for line in proc.stderr: + if type(line)==bytes: line = line.decode() # convert to string, not byetes line = line.strip('\r\n') - print "stderr: " + line + print("stderr: {}".format(line)) retcode = proc.wait() + + if (retcode != 0) and len(cube_stdout)>0: + # retcode may be wrong -- check last stdout + print("checking cube_stdout[-1]: {}".format(cube_stdout[-1])) + # print("match: {}".format(re.match(retcode,cube_stdout[-1]))) + if re.match(CUBE_SUCCESS,cube_stdout[-1]): + print("Overriding cuberet {} with 0 due to last cubeStdout line".format(retcode)) + retcode = 0 + # success -- stop looping if retcode == 0: break @@ -133,8 +149,8 @@ def export_cubenet_to_csvs(file, extra_link_vars=[], extra_node_vars=[], if retcode != 0: raise - print "Received %d from [%s]" % (retcode, cmd) - print "Exported network to: %s, %s" % (env["CUBELINK_CSV"], env["CUBENODE_CSV"]) + print("Received {} from [{}]".format(retcode, cmd)) + print("Exported network to: {}, {}".format(env["CUBELINK_CSV"], env["CUBENODE_CSV"])) def import_cube_nodes_links_from_csvs(cubeNetFile, diff --git a/_static/__init__.py b/_static/__init__.py new file mode 100644 index 0000000..80578e3 --- /dev/null +++ b/_static/__init__.py @@ -0,0 +1,3 @@ +from .dataTable import DataTable + +__all__ = [ 'DataTable' ] \ No newline at end of file diff --git a/_static/dataTable.py b/_static/dataTable.py index 762c8c6..39beb91 100644 --- a/_static/dataTable.py +++ b/_static/dataTable.py @@ -139,6 +139,8 @@ def setIndex(self, fieldName = None, indexFunction = None): """Define a fieldName the values of which will serve as the index of the table. Alternativly, you can define a fucntion that takes a row as an input and returns a value serving as the index""" + # print("dataTable.setIndex(fieldName={}, indexFunction={})".format(fieldName, indexFunction)) + # print("self.fields.dtype.names={} type={}".format(self.fields.dtype.names, type(self.fields.dtype.names))) if fieldName: if fieldName not in self.fields.dtype.names: raise DataTableError("The field: %d does not exist" % str(fieldName)) @@ -160,6 +162,7 @@ def setIndex(self, fieldName = None, indexFunction = None): self._hasIndex = False self._indexFunction = None self._indexField = None + # print("self._index = {}".format(self._index)) def _createIndex(self, fieldName=None, indexFunction=None): """Create a dictionary the keys of which will serve as the new @@ -278,8 +281,12 @@ def __init__(self, name, _type, length, numDecimals): self.name = name + # convert string to byte + if type(_type) == str: + _type = _type.encode('utf-8') + if _type not in FieldType.TYPES: - raise FieldTypeError('Unknown field type {}'.format(_type)) + raise FieldTypeError('Unknown field type {} {}'.format(_type, type(_type))) self.type = _type self.length = length @@ -471,12 +478,14 @@ def _writeHeader(self): size = fieldType.length deci = fieldType.numDecimals - name = name.ljust(11, '\x00') + if type(name)==str: name = name.encode('utf-8') + + name = name.ljust(11, b'\x00') fld = pack('<11sc4xBB14x', name, typ, size, deci) self._bfstream.write(fld) # terminator - self._bfstream.write('\r') + self._bfstream.write(b'\r') def writeRecord(self, record): """Write a record that can be accessed as a dictionary to the @@ -484,7 +493,7 @@ def writeRecord(self, record): if self._currentRecord > self._numRecords: raise DbfWriteError("The number of records that can be writen to the file " "%s cannot exceed %d" % (self._fileName, self._numRecords)) - self._bfstream.write(" ") + self._bfstream.write(b" ") for fType in self.header: name = fType.name typ = fType.type @@ -505,15 +514,17 @@ def writeRecord(self, record): elif fType.type == 'L': value = str(value)[0].upper() else: - value = str(value)[:size].ljust(size, ' ') + value = value[:size].ljust(size, b' ') + if len(value) != size: print("Mismatch for {}; {} != {}; val={}".format(name, len(value), size, str(value))) assert len(value) == size + if type(value)==str: value=value.encode('utf-8') self._bfstream.write(value) self._currentRecord += 1 if self._currentRecord == self._numRecords: - self._bfstream.write('\x1A') + self._bfstream.write(b'\x1A') self._bfstream.close() def __del__(self): diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..0090d6d --- /dev/null +++ b/requirements.txt @@ -0,0 +1,8 @@ +# These can be difficult to install on windows so we often save the windows pre-built +# versions from Unofficial Windows Binaries for Python Extension Packages: https://www.lfd.uci.edu/~gohlke/pythonlibs +# to M:\Software\Python +xlrd +SimpleParse +numpy +pandas +pywin32 \ No newline at end of file diff --git a/scripts/.gitignore b/scripts/.gitignore index 58cec5d..059f428 100644 --- a/scripts/.gitignore +++ b/scripts/.gitignore @@ -1,9 +1,15 @@ CleanAndGreen RisingTides BackToTheFuture +BlueprintNetworks* +NGF_* +TRR_* Test_Scenario_* scratch *.LOG *.PRN TPPL* -net_* \ No newline at end of file +net_2* +net_P* +network_* +net_spec_test.py \ No newline at end of file diff --git a/scripts/build_network_mtc.py b/scripts/build_network_mtc.py index d09102c..386d547 100644 --- a/scripts/build_network_mtc.py +++ b/scripts/build_network_mtc.py @@ -1,4 +1,4 @@ -import argparse,collections,datetime,os,pandas,shutil,sys,time +import argparse,collections,copy,datetime,os,pandas,shutil,sys,time import Wrangler # Based on NetworkWrangler\scripts\build_network.py @@ -112,8 +112,12 @@ def getProjectYear(PROJECTS, my_proj, netmode): e.g. 2020.02 for second project in 2020 """ for year in PROJECTS.keys(): - if my_proj in PROJECTS[year][netmode]: - return year + (PROJECTS[year][netmode].index(my_proj)+1)*0.01 + for proj_idx in range(len(PROJECTS[year][netmode])): + proj = PROJECTS[year][netmode][proj_idx] + if type(proj) is dict and my_proj == proj['name']: + return "{}.{}.{:0>2d}".format(year,netmode,proj_idx+1) + elif proj == my_proj: + return "{}.{}.{:0>2d}".format(year,netmode,proj_idx+1) return -1 def checkRequirements(REQUIREMENTS, PROJECTS, req_type='prereq'): @@ -128,8 +132,8 @@ def checkRequirements(REQUIREMENTS, PROJECTS, req_type='prereq'): for netmode in REQUIREMENTS.keys(): for project in REQUIREMENTS[netmode].keys(): project_year = getProjectYear(PROJECTS, project, netmode) - if project_year == -1: - Wrangler.WranglerLogger.warn('Cannot find the %s project %s to check its requirements'.format(netmode, project)) + if (type(project_year) == int) and (project_year == -1): + Wrangler.WranglerLogger.warning('Cannot find the {} project {} to check its requirements'.format(netmode, project)) continue # raise? Wrangler.WranglerLogger.info('Checking {} project {} ({}) for {}'.format(netmode, project, project_year, req_type)) @@ -140,11 +144,16 @@ def checkRequirements(REQUIREMENTS, PROJECTS, req_type='prereq'): req_proj_years = {} for req_proj in req_proj_list: req_project_year = getProjectYear(PROJECTS, req_proj, req_netmode) + Wrangler.WranglerLogger.debug('req_project_year=[{}]'.format(req_project_year)) # prereq if req_type=="prereq": - if req_project_year < 0: is_ok = False # required project must be found - if req_project_year > project_year: is_ok = False # and implemented before or at the same time as the project + if (type(req_project_year) == int) and (req_project_year < 0): + is_ok = False # required project must be found + Wrangler.WranglerLogger.warning("required project not found") + if req_project_year > project_year: + is_ok = False # and implemented before or at the same time as the project + Wrangler.WranglerLogger.warning("required project year {} > project year {}".format(req_project_year, project_year)) # save into proj_years req_proj_years[req_proj] = req_project_year @@ -198,7 +207,7 @@ def getProjectAttributes(project): return (project_name, project_type, tag, kwargs) -def preCheckRequirementsForAllProjects(networks): +def preCheckRequirementsForAllProjects(networks, continue_on_warning): PRE_REQS = {'hwy':{},'trn':{}} CO_REQS = {'hwy':{},'trn':{}} CONFLICTS = {'hwy':{},'trn':{}} @@ -256,20 +265,22 @@ def preCheckRequirementsForAllProjects(networks): # if they're different, log more information and get approval (if not in test mode) if cloned_SHA1 != head_SHA1: - Wrangler.WranglerLogger.warn("Using non-head version of project of %s" % project_name) - Wrangler.WranglerLogger.warn(" Applying version [%s], Head is [%s]" % (cloned_SHA1, head_SHA1)) + Wrangler.WranglerLogger.warning("Using non-head version of project of %s" % project_name) + Wrangler.WranglerLogger.warning(" Applying version [%s], Head is [%s]" % (cloned_SHA1, head_SHA1)) cmd = "git log %s..%s" % (cloned_SHA1, head_SHA1) (retcode, retStdout, retStderr) = networks[netmode]._runAndLog(cmd, run_dir = cmd_dir) - Wrangler.WranglerLogger.warn(" The following commits are not included:") + Wrangler.WranglerLogger.warning(" The following commits are not included:") for line in retStdout: - Wrangler.WranglerLogger.warn(" %s" % line) + Wrangler.WranglerLogger.warning(" %s" % line) # test mode => warn is sufficient # non-test mode => get explicit approval - if BUILD_MODE !="test": - Wrangler.WranglerLogger.warn(" Is this ok? (y/n) ") - response = raw_input("") + if continue_on_warning: + Wrangler.WranglerLogger.warninging("Continuing (continue_on_warning)") + elif BUILD_MODE !="test": + Wrangler.WranglerLogger.warning(" Is this ok? (y/n) ") + response = input("") Wrangler.WranglerLogger.debug(" response = [%s]" % response) if response.strip().lower()[0] != "y": sys.exit(2) @@ -281,15 +292,17 @@ def preCheckRequirementsForAllProjects(networks): applied_commit_date = datetime.datetime.fromtimestamp(int(retStdout[0])) applied_commit_age = datetime.datetime.now() - applied_commit_date - # if older than one year, holler - STALE_YEARS = 2 + # if older than x years, holler + STALE_YEARS = 5 if applied_commit_age > datetime.timedelta(days=365*STALE_YEARS): - Wrangler.WranglerLogger.warn(" This project was last updated %.1f years ago (over %d), on %s" % \ + Wrangler.WranglerLogger.warning(" This project was last updated %.1f years ago (over %d), on %s" % \ (applied_commit_age.days/365.0, STALE_YEARS, applied_commit_date.strftime("%x"))) - if BUILD_MODE !="test": - Wrangler.WranglerLogger.warn(" Is this ok? (y/n) ") - response = raw_input("") + if continue_on_warning: + Wrangler.WranglerLogger.warninging("Continuing (continue_on_warning)") + elif BUILD_MODE !="test": + Wrangler.WranglerLogger.warning(" Is this ok? (y/n) ") + response = input("") Wrangler.WranglerLogger.debug(" response = [%s]" % response) if response.strip().lower() not in ["y", "yes"]: sys.exit(2) @@ -314,7 +327,7 @@ def preCheckRequirementsForAllProjects(networks): Wrangler.WranglerLogger.debug('All PRE-REQUISITES were found. Are the PRE-REQUISITES matches correct? (y/n)') else: Wrangler.WranglerLogger.debug('!!!WARNING!!! Some PRE-REQUISITES were not found or ordered correctly. Continue anyway? (y/n)') - response = raw_input("") + response = input("") Wrangler.WranglerLogger.debug(" response = [%s]" % response) if response.strip().lower() not in ["y", "yes"]: sys.exit(2) @@ -327,7 +340,7 @@ def preCheckRequirementsForAllProjects(networks): Wrangler.WranglerLogger.debug('All CO-REQUISITES were found. Are the CO-REQUISITE matches correct? (y/n)') else: Wrangler.WranglerLogger.debug('!!!WARNING!!! Some CO-REQUISITES were not found. Continue anyway? (y/n)') - response = raw_input("") + response = input("") Wrangler.WranglerLogger.debug(" response = [%s]" % response) if response.strip().lower() not in ["y", "yes"]: sys.exit(2) @@ -340,7 +353,7 @@ def preCheckRequirementsForAllProjects(networks): Wrangler.WranglerLogger.debug('!!!WARNING!!! Conflicting projects were found. Continue anyway? (y/n)') else: Wrangler.WranglerLogger.debug('No conflicting projects were found. Enter \'y\' to continue.') - response = raw_input("") + response = input("") Wrangler.WranglerLogger.debug(" response = [%s]" % response) if response.strip().lower() not in ["y", "yes"]: sys.exit(2) @@ -354,12 +367,23 @@ def preCheckRequirementsForAllProjects(networks): parser.add_argument("--configword", help="optional word for network specification script") parser.add_argument("--model_type", choices=[Wrangler.Network.MODEL_TYPE_TM1, Wrangler.Network.MODEL_TYPE_TM2], default=Wrangler.Network.MODEL_TYPE_TM1) + parser.add_argument("--continue_on_warning", help="Don't prompt the user to continue if there are warnings; just warn and continue", action="store_true") + parser.add_argument("--skip_precheck_requirements", help="Don't precheck network requirements, stale projects, non-HEAD projects, etc", action="store_true") + parser.add_argument("project_name", help="required project name, for example NGF") + parser.add_argument("--scenario", help="optional SCENARIO name") parser.add_argument("net_spec", metavar="network_specification.py", help="Script which defines required variables indicating how to build the network") + parser.add_argument("--NGF_netvariant", + choices=[ + "BlueprintSegmented", + "P1a_AllLaneTolling_ImproveTransit", "P1b_AllLaneTolling_Affordable", + "P2a_AllLaneTollingPlusArterials_ImproveTransit", "P2b_AllLaneTollingPlusArterials_Affordable", + "P3_3Cordons"], + help="Specify which network variant network to create.") args = parser.parse_args() NOW = time.strftime("%Y%b%d.%H%M%S") BUILD_MODE = None # regular - if args.model_type == Wrangler.Network.MODEL_TYPE_TM1: + if (args.model_type == Wrangler.Network.MODEL_TYPE_TM1) & (args.project_name != 'NGF'): PIVOT_DIR = r"M:\\Application\\Model One\\Networks\\TM1_2015_Base_Network" TRANSIT_CAPACITY_DIR = os.path.join(PIVOT_DIR, "trn") NETWORK_BASE_DIR = r"M:\\Application\\Model One\\NetworkProjects" @@ -367,6 +391,18 @@ def preCheckRequirementsForAllProjects(networks): TRN_NET_NAME = "Transit_Lines" HWY_SUBDIR = "hwy" HWY_NET_NAME = "freeflow.net" + elif (args.model_type == Wrangler.Network.MODEL_TYPE_TM1) & (args.project_name == 'NGF'): + PIVOT_DIR = r"L:\Application\Model_One\NextGenFwys\INPUT_DEVELOPMENT\Networks\NGF_Networks_NoProject_06\net_2035_NGFNoProject" + # for 3 cordons, the no project without the SF cordon is required + if (args.NGF_netvariant == "P3_3Cordons"): + PIVOT_DIR = r"L:\Application\Model_One\NextGenFwys\INPUT_DEVELOPMENT\Networks\NGF_Networks_NoProjectNoSFCordon_06\net_2035_NGFNoProjectNoSFCordon" + PIVOT_YEAR = 2035 + TRANSIT_CAPACITY_DIR = os.path.join(PIVOT_DIR, "trn") + NETWORK_BASE_DIR = r"M:\Application\Model One\NetworkProjects" + TRN_SUBDIR = "trn" + TRN_NET_NAME = "transitLines" + HWY_SUBDIR = "hwy" + HWY_NET_NAME = "freeflow.net" elif args.model_type == Wrangler.Network.MODEL_TYPE_TM2: PIVOT_DIR = os.path.join(os.environ["USERPROFILE"], "Box","Modeling and Surveys","Development","Travel Model Two Development","Model Inputs","2015_revised_mazs") TRANSIT_CAPACITY_DIR = None @@ -377,7 +413,21 @@ def preCheckRequirementsForAllProjects(networks): HWY_NET_NAME = "mtc_final_network_base.net" # Read the configuration - NETWORK_CONFIG = args.net_spec + NETWORK_CONFIG = args.net_spec + PROJECT = args.project_name + if args.scenario: SCENARIO = args.scenario + if args.project_name == 'NGF': + SCENARIO = args.NGF_netvariant + NET_VARIANT = args.NGF_netvariant + + OUT_DIR = "{}_network_".format(PROJECT) + "{}" + if SCENARIO: + OUT_DIR = "{}_{}_network_".format(PROJECT, SCENARIO) + "{}" + + LOG_FILENAME = "build%snetwork_%s_%s_%s.info.LOG" % ("TEST" if BUILD_MODE=="test" else "", PROJECT, SCENARIO, NOW) + Wrangler.setupLogging(LOG_FILENAME, + LOG_FILENAME.replace("info", "debug")) + exec(open(NETWORK_CONFIG).read()) # Verify mandatory fields are set @@ -386,7 +436,7 @@ def preCheckRequirementsForAllProjects(networks): sys.exit(2) if SCENARIO==None: print("SCENARIO not set in %s" % NETWORK_CONFIG) - sys.exit(2) + # sys.exit(2) if TAG==None: print("TAG not set in %s" % NETWORK_CONFIG) sys.exit(2) @@ -397,8 +447,6 @@ def preCheckRequirementsForAllProjects(networks): print("NETWORK_PROJECTS not set in %s" % NETWORK_CONFIG) sys.exit(2) - LOG_FILENAME = "build%snetwork_%s_%s_%s.info.LOG" % ("TEST" if BUILD_MODE=="test" else "", PROJECT, SCENARIO, NOW) - Wrangler.setupLogging(LOG_FILENAME, LOG_FILENAME.replace("info", "debug")) if TRANSIT_CAPACITY_DIR: Wrangler.TransitNetwork.capacity = Wrangler.TransitCapacity(directory=TRANSIT_CAPACITY_DIR) @@ -408,6 +456,12 @@ def preCheckRequirementsForAllProjects(networks): if not os.path.exists(SCRATCH_SUBDIR): os.mkdir(SCRATCH_SUBDIR) os.chdir(SCRATCH_SUBDIR) + if args.project_name == 'NGF': + os.environ["CHAMP_node_names"] = "M:\\Application\\Model One\\Networks\\TM1_2015_Base_Network\\Node Description.xls" + print() + else: + os.environ["CHAMP_node_names"] = os.path.join(PIVOT_DIR,"Node Description.xls") + networks = { 'hwy' :Wrangler.HighwayNetwork(modelType=args.model_type, modelVersion=1.0, basenetworkpath=os.path.join(PIVOT_DIR,"hwy"), @@ -437,12 +491,15 @@ def preCheckRequirementsForAllProjects(networks): # Wrangler.WranglerLogger.debug("NETWORK_PROJECTS=%s NET_MODES=%s" % (str(NETWORK_PROJECTS), str(NET_MODES))) - preCheckRequirementsForAllProjects(networks) + if args.skip_precheck_requirements: + Wrangler.WranglerLogger.info("skip_precheck_requirements passed so skipping preCheckRequirementsForAllProjects()") + else: + preCheckRequirementsForAllProjects(networks, args.continue_on_warning) # create the subdir for SET_CAPCLASS with set_capclass.job as apply.s SET_CAPCLASS = "set_capclass" SET_CAPCLASS_DIR = os.path.join(TEMP_SUBDIR, SET_CAPCLASS) - os.mkdir(SET_CAPCLASS_DIR) + os.makedirs(SET_CAPCLASS_DIR) source_file = os.path.join(os.path.dirname(THIS_FILE), "set_capclass.job") shutil.copyfile( source_file, os.path.join(SET_CAPCLASS_DIR, "apply.s")) @@ -453,11 +510,12 @@ def preCheckRequirementsForAllProjects(networks): appliedcount = 0 for netmode in NET_MODES: Wrangler.WranglerLogger.info("Building {} {} networks".format(YEAR, netmode)) + for project in projects_for_year[netmode]: (project_name, projType, tag, kwargs) = getProjectAttributes(project) if tag == None: tag = TAG - Wrangler.WranglerLogger.info("Applying project [%s] of type [%s] with tag [%s]" % (project_name, projType, tag)) + Wrangler.WranglerLogger.info("Applying project [{}] of type [{}] with tag [{}] and kwargs[{}]".format(project_name, projType, tag, kwargs)) if projType=='plan': continue @@ -469,10 +527,12 @@ def preCheckRequirementsForAllProjects(networks): applied_SHA1 = networks[netmode].applyProject(parentdir, networkdir, gitdir, projectsubdir, **kwargs) appliedcount += 1 - # apply set_capclass before writing any hwy network - if netmode == "hwy" and appliedcount > 0: - applied_SHA1 = networks[netmode].applyProject(parentdir=TEMP_SUBDIR, networkdir=SET_CAPCLASS, - gitdir=os.path.join(TEMP_SUBDIR, SET_CAPCLASS)) + # if hwy project has set_capclass override, copy it to set_capclass/apply.s + set_capclass_override = os.path.join(TEMP_SUBDIR, project_name, "set_capclass.job") + if os.path.exists(set_capclass_override): + dest_file = os.path.join(SET_CAPCLASS_DIR, "apply.s") + shutil.copyfile(set_capclass_override, dest_file) + Wrangler.WranglerLogger.info("Copied override {} to {}".format(set_capclass_override, dest_file)) if appliedcount == 0: Wrangler.WranglerLogger.info("No applied projects for this year -- skipping output") @@ -487,7 +547,7 @@ def preCheckRequirementsForAllProjects(networks): networks['hwy'].write(path=hwypath,name=HWY_NET_NAME,suppressQuery=True, suppressValidation=True) # MTC TM1 doesn't have turn penalties - os.environ["CHAMP_node_names"] = os.path.join(PIVOT_DIR,"Node Description.xls") + # os.environ["CHAMP_node_names"] = os.path.join(PIVOT_DIR,"Node Description.xls") hwy_abs_path = os.path.abspath( os.path.join(hwypath, HWY_NET_NAME) ) networks['trn'].write(path=trnpath, name="transitLines", diff --git a/scripts/build_network_mtc_add_project.py b/scripts/build_network_mtc_add_project.py index 719900c..4e89a72 100644 --- a/scripts/build_network_mtc_add_project.py +++ b/scripts/build_network_mtc_add_project.py @@ -8,9 +8,12 @@ Creates PPA_DIR\project_short_id with subdirs BASE_DIR_project_short_id + Alternatively, the user can specify the input directory, output directory and directory with + the network project. Default use case is PPA. + """ -PPA_DIR = "L:\RTP2021_PPA\Projects" -NODE_NAMES = "M:\Application\Model One\Networks\TM1_2015_Base_Network\Node Description.xls" +PPA_DIR = "L:\\RTP2021_PPA\\Projects" +NODE_NAMES = "M:\\Application\\Model One\\Networks\\TM1_2015_Base_Network\\Node Description.xls" THIS_FILE = os.path.realpath(__file__) # for transit network validation output @@ -64,7 +67,7 @@ def findBaseDirectory(future): def determineProjectDirectory(OUTPUT_DIR, BASE_DIR, project_short_id): dir_list = sorted(os.listdir(OUTPUT_DIR)) - dir_re_str = "^{}_{}_(\d\d)$".format(BASE_DIR, project_short_id) + dir_re_str = r"^{}_{}_(\d\d)$".format(re.escape(BASE_DIR), re.escape(project_short_id)) dir_re = re.compile(dir_re_str) existing_suffixes = [] @@ -81,9 +84,9 @@ def determineProjectDirectory(OUTPUT_DIR, BASE_DIR, project_short_id): proposed_suffix = 0 if len(existing_suffixes) > 0: proposed_suffix = existing_suffixes[-1] + 1 print("Which suffix number do you want to use? (No response means {}) ".format(proposed_suffix)) - response = raw_input("") + response = input("") print(" response = [%s]" % response) - + response_suffix = proposed_suffix if response != "": response_suffix = int(response.strip()) @@ -98,10 +101,15 @@ def determineProjectDirectory(OUTPUT_DIR, BASE_DIR, project_short_id): if __name__ == '__main__': parser = argparse.ArgumentParser(description=USAGE, formatter_class=argparse.RawDescriptionHelpFormatter) - parser.add_argument("future", choices=["CleanAndGreen", "RisingTides", "BackToTheFuture","all"], help="Specify which Future Scenario for which to create networks") + parser.add_argument("future", choices=["CleanAndGreen", "RisingTides", "BackToTheFuture", "all","None","FinalBlueprint","TransitRecovery"], help="Specify which Future Scenario for which to create networks") parser.add_argument("--hwy", dest='hwy', action='store_true', help="Pass if project is a roadway project") parser.add_argument("--trn", dest='trn', action='store_true', help="Pass if project is a transit project") - parser.add_argument("--kwarg", dest='kwarg', help="To pass keyword args to project apply(), pass keyword and value", nargs=2) + parser.add_argument("--input_network", dest='input_network', help="Pass input network path if desired; otherwise, PPA path is assumed") + parser.add_argument("--output_network", dest='output_network', help="Pass output network path if desired; otPherwise, PPA path is assumed") + parser.add_argument("--input_projects", dest='input_projects', help="Pass directory for network projects; if none passed, M:\\Application\\Model One\\NetworkProjects is assumed") + parser.add_argument("--kwarg", dest='kwarg', help="To pass keyword args to project apply(), pass keyword and value", nargs=2) + parser.add_argument("--kwarg2", dest='kwarg2', help="To pass keyword args to project apply(), pass keyword and value", nargs=2) + parser.add_argument("--tag", dest='tag', help="tags for project") parser.add_argument("project_short_id", help="Short ID of project, to be used for directory") parser.add_argument("project", help="Project to add", nargs="+") args = parser.parse_args() @@ -112,7 +120,16 @@ def determineProjectDirectory(OUTPUT_DIR, BASE_DIR, project_short_id): PROJECT = "PPA" - NETWORK_BASE_DIR = r"M:\\Application\\Model One\\NetworkProjects" + if args.future == "FinalBlueprint": + PROJECT = "FBP" + elif args.future == "TransitRecovery": + PROJECT = "TRR" + + if args.input_projects: + NETWORK_BASE_DIR = args.input_projects + else: + NETWORK_BASE_DIR = r"M:\\Application\\Model One\\NetworkProjects" + HWY_NET_NAME = "freeflow.net" TRN_NET_NAME = "transitLines" @@ -123,8 +140,13 @@ def determineProjectDirectory(OUTPUT_DIR, BASE_DIR, project_short_id): for SCENARIO in FUTURES: NOW = time.strftime("%Y%b%d.%H%M%S") - BASE_DIR = findBaseDirectory(SCENARIO) # e.g. 2050_TM150_PPA_BF_00 - PIVOT_DIR = os.path.join(PPA_DIR, BASE_DIR, "INPUT") # full path of input network + if args.input_network: + print("Using input network {}".format(args.input_network)) + BASE_DIR = args.input_network + PIVOT_DIR = args.input_network + else: + BASE_DIR = findBaseDirectory(SCENARIO) # e.g. 2050_TM150_PPA_BF_00 + PIVOT_DIR = os.path.join(PPA_DIR, BASE_DIR, "INPUT") # full path of input network TRANSIT_CAPACITY_DIR = os.path.join(PIVOT_DIR, "trn") # setup kwargs to pass @@ -135,30 +157,43 @@ def determineProjectDirectory(OUTPUT_DIR, BASE_DIR, project_short_id): kwargs[args.kwarg[0]] = '"{}"'.format(SCENARIO) else: kwargs[args.kwarg[0]] = '"{}"'.format(args.kwarg[1]) - - OUTPUT_DIR = os.path.join(PPA_DIR, args.project_short_id) - # make OUTPUT_DIR - if not os.path.exists(OUTPUT_DIR): os.mkdir(OUTPUT_DIR) - - OUTPUT_FUTURE_DIR = determineProjectDirectory(OUTPUT_DIR, BASE_DIR, args.project_short_id) - os.mkdir(OUTPUT_FUTURE_DIR) - # move into it so the scratch is here - os.chdir(OUTPUT_DIR) - + # second optional kwarg + if args.kwarg2: + kwargs[args.kwarg2[0]] = '"{}"'.format(args.kwarg2[1]) + + if args.output_network: + print("Using output network {}".format(args.output_network)) + OUTPUT_DIR = args.output_network + # make OUTPUT_DIR + if not os.path.exists(OUTPUT_DIR): os.mkdir(OUTPUT_DIR) + OUTPUT_FUTURE_DIR = OUTPUT_DIR + # move into it so the scratch is here + os.chdir(OUTPUT_DIR) + + else: + OUTPUT_DIR = os.path.join(PPA_DIR, args.project_short_id) + # make OUTPUT_DIR + if not os.path.exists(OUTPUT_DIR): os.mkdir(OUTPUT_DIR) + + OUTPUT_FUTURE_DIR = determineProjectDirectory(OUTPUT_DIR, BASE_DIR, args.project_short_id) + os.mkdir(OUTPUT_FUTURE_DIR) + # move into it so the scratch is here + os.chdir(OUTPUT_DIR) + # put log file into the run dir LOG_FILENAME = "addproject_{}_{}_{}_{}.info.LOG".format(PROJECT, SCENARIO, args.project_short_id, NOW) Wrangler.setupLogging(os.path.join(OUTPUT_FUTURE_DIR, LOG_FILENAME), os.path.join(OUTPUT_FUTURE_DIR, LOG_FILENAME.replace("info", "debug"))) - + Wrangler.WranglerLogger.info("Input args: {}".format(args)) Wrangler.WranglerLogger.info("Using base directory {}".format(PIVOT_DIR)) # Create a scratch directory to check out project repos into SCRATCH_SUBDIR = "scratch" - TEMP_SUBDIR = "Wrangler_tmp_" + NOW + TEMP_SUBDIR = "Wrangler_tmp_" + NOW if not os.path.exists(SCRATCH_SUBDIR): os.mkdir(SCRATCH_SUBDIR) os.chdir(SCRATCH_SUBDIR) - + networks = { 'hwy' :Wrangler.HighwayNetwork(modelType=Wrangler.Network.MODEL_TYPE_TM1, modelVersion=1.0, basenetworkpath=os.path.join(PIVOT_DIR,"hwy"), @@ -187,15 +222,15 @@ def determineProjectDirectory(OUTPUT_DIR, BASE_DIR, project_short_id): for netmode in ["hwy","trn"]: # if applying project if (netmode == "hwy" and args.hwy) or (netmode == "trn" and args.trn): - + # iterate through projects specified, since args.project is a list for my_project in args.project: - + Wrangler.WranglerLogger.info("Applying project [%s] of type [%s]" % (my_project, netmode)) - cloned_SHA1 = networks[netmode].cloneProject(networkdir=my_project, tag=None, + cloned_SHA1 = networks[netmode].cloneProject(networkdir=my_project, tag=args.tag, projtype="project", tempdir=TEMP_SUBDIR, **kwargs) (parentdir, networkdir, gitdir, projectsubdir) = networks[netmode].getClonedProjectArgs(my_project, None, "project", TEMP_SUBDIR) - + applied_SHA1 = networks[netmode].applyProject(parentdir, networkdir, gitdir, projectsubdir, **kwargs) # write networks @@ -211,6 +246,14 @@ def determineProjectDirectory(OUTPUT_DIR, BASE_DIR, project_short_id): source_file = os.path.join(os.path.dirname(THIS_FILE), "set_capclass.job") shutil.copyfile( source_file, os.path.join(SET_CAPCLASS_DIR, "apply.s")) + # if hwy project has set_capclass override, copy it to set_capclass/apply.s + for my_project in args.project: + set_capclass_override = os.path.join(TEMP_SUBDIR, my_project, "set_capclass.job") + if os.path.exists(set_capclass_override): + dest_file = os.path.join(SET_CAPCLASS_DIR, "apply.s") + shutil.copyfile(set_capclass_override, dest_file) + Wrangler.WranglerLogger.info("Copied override {} to {}".format(set_capclass_override, dest_file)) + # apply set_capclass before writing any hwy network try: applied_SHA1 = networks[netmode].applyProject(parentdir=TEMP_SUBDIR, networkdir=SET_CAPCLASS, @@ -218,7 +261,7 @@ def determineProjectDirectory(OUTPUT_DIR, BASE_DIR, project_short_id): except Wrangler.NetworkException as ne: Wrangler.WranglerLogger.debug("set_capclass exception: {}".format(ne.args[0])) # this is expected -- since we're using a hack and this isn't a git project - if ne.args[0].startswith("Git log failed"): + if ne.args[0].startswith("Git log failed"): pass else: raise ne @@ -237,7 +280,7 @@ def determineProjectDirectory(OUTPUT_DIR, BASE_DIR, project_short_id): except Wrangler.NetworkException as ne: Wrangler.WranglerLogger.debug("check_for_errors exception: {}".format(ne.args[0])) # this is expected -- since we're using a hack and this isn't a git project - if ne.args[0].startswith("Git log failed"): + if ne.args[0].startswith("Git log failed"): pass else: raise ne @@ -257,4 +300,3 @@ def determineProjectDirectory(OUTPUT_DIR, BASE_DIR, project_short_id): Wrangler.TransitNetwork.capacity.writeTransitPrefixToVehicle(directory = final_path) Wrangler.WranglerLogger.debug("Successfully completed running %s" % os.path.abspath(__file__)) - diff --git a/scripts/build_network_mtc_blueprint.py b/scripts/build_network_mtc_blueprint.py new file mode 100644 index 0000000..e158bc6 --- /dev/null +++ b/scripts/build_network_mtc_blueprint.py @@ -0,0 +1,602 @@ +import argparse,collections,copy,datetime,os,pandas,shutil,sys,time +import Wrangler + +# Based on NetworkWrangler\scripts\build_network.py +# +# Blueprint scenarios are No Project, Blueprint Basic, Blueprint Plus +# Specify the scenario when building +# Ex python build_network_blueprint.py net_spec_blueprint.py BlueprintBasic + +USAGE = """ + + Builds a network using the specifications in network_specification.py, which should + define the variables listed below (in this script) + + The [-c configword] is if you want an optional word for your network_specification.py + (e.g. to have multiple scenarios in one file). Access it via CONFIG_WORD. + +""" + +############################################################################### +# # +# Define the following in an input configuration file # +# # +############################################################################### +# MANDATORY. Set this to be the Project Name. +# e.g. "RTP2021", "TIP2021", etc +PROJECT = None + +# MANDATORY. Set this to be the Scenario Name +# e.g. "Base", "Baseline" +SCENARIO = None + +# MANDATORY. Set this to be the git tag for checking out network projects. +TAG = None + +# OPTIONAL. If you are building on top of a previously built network, this +# should be set to the location of those networks. This should be a directory +# which has "hwy" and "trn" subdirectories. +PIVOT_DIR = None + +# OPTIONAL. If PIVOT_DIR is specified, MANDATORY. Specifies year for PIVOT_DIR. +PIVOT_YEAR = 2015 + +# MANDATORY. Should be a dictionary with keys in NET_MODES +# to a list of projects. A project can either be a simple string, or it can be +# a dictionary with with keys 'name', 'tag' (optional), and 'kwargs' (optional) +# to specify a special tag or special keyword args for the projects apply() call. +# For example: +# {'name':"Muni_TEP", 'kwargs':{'servicePlan':"'2012oct'"}} +NETWORK_PROJECTS = None + +# OPTIONAL. The default route network project directory is Y:\networks. If +# projects are stored in another directory, then use this variable to specify it. +# For example: Y:\networks\projects +NETWORK_BASE_DIR = None +NETWORK_PROJECT_SUBDIR = None +NETWORK_SEED_SUBDIR = None +NETWORK_PLAN_SUBDIR = None + +# OPTIONAL. A list of project names which have been previously applied in the +# PIVOT_DIR network that projects in this project might rely on. For example +# if DoyleDrive exists, then Muni_TEP gets applied differently so transit lines +# run on the new Doyle Drive alignment +APPLIED_PROJECTS = None + +# OPTIONAL. A list of project names. For test mode, these projects won't use +# the TAG. This is meant for developing a network project. +TEST_PROJECTS = None + +TRN_MODES = ['trn'] +NET_MODES = ['hwy'] + TRN_MODES +THIS_FILE = os.path.realpath(__file__) + +############################################################################### + +############################################################################### +# # +# Helper functions # +# # +############################################################################### +def getProjectNameAndDir(project): + if type(project) == type({'this is':'a dict'}): + name = project['name'] + else: + name = project + (path,name) = os.path.split(name) + return (path,name) + +def getNetworkListIndex(project, networks): + for proj in networks: + (path,name) = getProjectNameAndDir(proj) + if project == name or project == os.path.join(path,name): + return networks.index(proj) + return None + +def getProjectMatchLevel(left, right): + (left_path,left_name) = getProjectNameAndDir(left) + (right_path,right_name) = getProjectNameAndDir(right) + match = 0 + if os.path.join(left_path,left_name) == os.path.join(right_path,right_name): + match = 2 + elif left_name == right_name: + match = 1 + #Wrangler.WranglerLogger.debug("Match level %d for %s and %s" % (match, os.path.join(left_path,left_name), os.path.join(right_path,right_name))) + return match + +def getProjectYear(PROJECTS, my_proj, netmode): + """ + PROJECTS is an OrderedDict, year -> netmode -> [ project list ] + Returns first year in which my_proj shows up in the netmode's project list, plus number in list + e.g. 2020.02 for second project in 2020 + """ + for year in PROJECTS.keys(): + for proj_idx in range(len(PROJECTS[year][netmode])): + proj = PROJECTS[year][netmode][proj_idx] + if type(proj) is dict and my_proj == proj['name']: + return "{}.{}.{:0>2d}".format(year,netmode,proj_idx+1) + elif proj == my_proj: + return "{}.{}.{:0>2d}".format(year,netmode,proj_idx+1) + return -1 + +def checkRequirements(REQUIREMENTS, PROJECTS, req_type='prereq'): + if req_type not in ('prereq','coreq','conflict'): + return (None, None) + + # Wrangler.WranglerLogger.debug("checkRequirements called with requirements=[{}] projects=[{}] req_typ={}".format(REQUIREMENTS, PROJECTS, req_type)) + + is_ok = True + + # REQUIREMENTS: netmode -> project -> netmode -> [list of projects] + for netmode in REQUIREMENTS.keys(): + for project in REQUIREMENTS[netmode].keys(): + project_year = getProjectYear(PROJECTS, project, netmode) + if project_year == -1: + Wrangler.WranglerLogger.warning('Cannot find the {} project {} to check its requirements'.format(netmode, project)) + continue # raise? + + Wrangler.WranglerLogger.info('Checking {} project {} ({}) for {}'.format(netmode, project, project_year, req_type)) + + for req_netmode in REQUIREMENTS[netmode][project].keys(): + + req_proj_list = REQUIREMENTS[netmode][project][req_netmode] + req_proj_years = {} + for req_proj in req_proj_list: + req_project_year = getProjectYear(PROJECTS, req_proj, req_netmode) + + # prereq + if req_type=="prereq": + if (type(req_project_year) == int) and (req_project_year < 0): + is_ok = False # required project must be found + Wrangler.WranglerLogger.warning("required project not found") + if req_project_year > project_year: + is_ok = False # and implemented before or at the same time as the project + Wrangler.WranglerLogger.warning("required project year {} > project year {}".format(req_project_year, project_year)) + + # save into proj_years + req_proj_years[req_proj] = req_project_year + + # sub out the list info with the project year info + REQUIREMENTS[netmode][project][req_netmode] = req_proj_years + + return (REQUIREMENTS, is_ok) + +def writeRequirements(REQUIREMENTS, PROJECTS, req_type='prereq'): + if req_type=='prereq': + print_req = 'Pre-requisite' + elif req_type=='coreq': + print_req = 'Co-requisite' + elif req_type=='conflict': + print_req = 'Conflict' + else: + return None + + Wrangler.WranglerLogger.info("Requirement verification - {}".format(print_req)) + Wrangler.WranglerLogger.info(" Year {:50} {:50} Year".format("Project",print_req+" " + "Project")) + # REQUIREMENTS: netmode -> project -> netmode -> req_proj -> req_proj_year + for netmode in REQUIREMENTS.keys(): + for project in REQUIREMENTS[netmode].keys(): + project_year = getProjectYear(PROJECTS, project, netmode) + for req_netmode in REQUIREMENTS[netmode][project].keys(): + for req_project in REQUIREMENTS[netmode][project][req_netmode].keys(): + Wrangler.WranglerLogger.info("{} {} {:50} {} {:50} {}".format(netmode, project_year, project, + req_netmode, req_project, REQUIREMENTS[netmode][project][req_netmode][req_project])) + +def getProjectAttributes(project): + # Start with TAG if not build mode, no kwargs + project_type = 'project' + tag = None + kwargs = {} + + # Use project name, tags, kwargs from dictionary + if type(project)==type({'this is':'a dictionary'}): + project_name = project['name'] + if 'tag' in project: tag = project['tag'] + if 'type' in project: project_type = project['type'] + if 'kwargs' in project: kwargs = project['kwargs'] + + # Use Project name directly + elif type(project)==type("string"): + project_name = project + + # Other structures not recognized + else: + Wrangler.WranglerLogger.fatal("Don't understand project %s" % str(project)) + + return (project_name, project_type, tag, kwargs) + +def preCheckRequirementsForAllProjects(networks, continue_on_warning): + PRE_REQS = {'hwy':{},'trn':{}} + CO_REQS = {'hwy':{},'trn':{}} + CONFLICTS = {'hwy':{},'trn':{}} + + # Network Loop #1: check out all the projects, check if they're stale, check if they're the head repository. Build completed + # project list so we can check pre-reqs, etc, in loop #2. + for netmode in NET_MODES: + # Build the networks! + Wrangler.WranglerLogger.info("Checking out %s networks" % netmode) + clonedcount = 0 + for model_year in NETWORK_PROJECTS.keys(): + for project in NETWORK_PROJECTS[model_year][netmode]: + (project_name, projType, tag, kwargs) = getProjectAttributes(project) + if tag == None: tag = TAG + + # test mode - don't use TAG for TEST_PROJECTS + if BUILD_MODE=="test" and type(TEST_PROJECTS)==type(['List']): + if project_name in TEST_PROJECTS: + Wrangler.WranglerLogger.debug("Skipping tag [%s] because test mode and [%s] is in TEST_PROJECTS" % + (TAG, project_name)) + tag = None + + Wrangler.WranglerLogger.debug("Project name = %s" % project_name) + + cloned_SHA1 = None + # if project = "dir1/dir2" assume dir1 is git, dir2 is the projectsubdir + (head,tail) = os.path.split(project_name) + if head: + cloned_SHA1 = networks[netmode].cloneProject(networkdir=head, projectsubdir=tail, tag=tag, + projtype=projType, tempdir=TEMP_SUBDIR, **kwargs) + (prereqs, coreqs, conflicts) = networks[netmode].getReqs(networkdir=head, projectsubdir=tail, tag=tag, + projtype=projType, tempdir=TEMP_SUBDIR) + else: + cloned_SHA1 = networks[netmode].cloneProject(networkdir=project_name, tag=tag, + projtype=projType, tempdir=TEMP_SUBDIR, **kwargs) + (prereqs, coreqs, conflicts) = networks[netmode].getReqs(networkdir=project_name, projectsubdir=tail, tag=tag, + projtype=projType, tempdir=TEMP_SUBDIR) + + # find out if the applied project is behind HEAD + # get the HEAD SHA1 + cmd = r"git show-ref --head master" + if projType=='project': + join_subdir = Wrangler.Network.NETWORK_PROJECT_SUBDIR + if projType=='seed': + join_subdir = Wrangler.Network.NETWORK_SEED_SUBDIR + + cmd_dir = os.path.join(Wrangler.Network.NETWORK_BASE_DIR, join_subdir, project_name) + (retcode, retStdout, retStderr) = networks[netmode]._runAndLog(cmd, run_dir = cmd_dir) + # Wrangler.WranglerLogger.debug("results of [%s]: %s %s %s" % (cmd, str(retcode), str(retStdout), str(retStderr))) + if retcode != 0: # this shouldn't happen -- wouldn't cloneAndApply have failed? + Wrangler.WranglerLogger.fatal("Couldn't run cmd [%s] in [%s]: stdout=[%s] stderr=[%s]" % \ + (cmd, cmd_dir, str(retStdout), str(retStderr))) + sys.exit(2) + head_SHA1 = retStdout[0].split()[0] + + # if they're different, log more information and get approval (if not in test mode) + if cloned_SHA1 != head_SHA1: + Wrangler.WranglerLogger.warning("Using non-head version of project of %s" % project_name) + Wrangler.WranglerLogger.warning(" Applying version [%s], Head is [%s]" % (cloned_SHA1, head_SHA1)) + + cmd = "git log %s..%s" % (cloned_SHA1, head_SHA1) + (retcode, retStdout, retStderr) = networks[netmode]._runAndLog(cmd, run_dir = cmd_dir) + Wrangler.WranglerLogger.warning(" The following commits are not included:") + for line in retStdout: + Wrangler.WranglerLogger.warning(" %s" % line) + + # test mode => warn is sufficient + # non-test mode => get explicit approval + if continue_on_warning: + Wrangler.WranglerLogger.warning("Continuing (continue_on_warning)") + elif BUILD_MODE !="test" and not continue_on_warning: + Wrangler.WranglerLogger.warning(" Is this ok? (y/n) ") + response = input("") + Wrangler.WranglerLogger.debug(" response = [%s]" % response) + if response.strip().lower()[0] != "y": + sys.exit(2) + + # find out if the project is stale + else: + cmd = 'git show -s --format="%%ct" %s' % cloned_SHA1 + (retcode, retStdout, retStderr) = networks[netmode]._runAndLog(cmd, run_dir = cmd_dir) + applied_commit_date = datetime.datetime.fromtimestamp(int(retStdout[0])) + applied_commit_age = datetime.datetime.now() - applied_commit_date + + # if older than STALE_YEARS year, holler + STALE_YEARS = 5 + if applied_commit_age > datetime.timedelta(days=365*STALE_YEARS): + Wrangler.WranglerLogger.warning(" This project was last updated %.1f years ago (over %d), on %s" % \ + (applied_commit_age.days/365.0, + STALE_YEARS, applied_commit_date.strftime("%x"))) + if continue_on_warning: + Wrangler.WranglerLogger.warning("Continuing (continue_on_warning)") + elif BUILD_MODE !="test": + Wrangler.WranglerLogger.warning(" Is this ok? (y/n) ") + response = input("") + Wrangler.WranglerLogger.debug(" response = [%s]" % response) + if response.strip().lower() not in ["y", "yes"]: + sys.exit(2) + + clonedcount += 1 + + # format: netmode -> project -> { netmode: [requirements] } + if len(prereqs ) > 0: PRE_REQS[ netmode][project_name] = prereqs + if len(coreqs ) > 0: CO_REQS[ netmode][project_name] = coreqs + if len(conflicts) > 0: CONFLICTS[netmode][project_name] = conflicts + + # Check requirements + prFile = 'prereqs.csv' + crFile = 'coreqs.csv' + cfFile = 'conflicts.csv' + + # Check prereqs + (PRE_REQS, allPrereqsFound) = checkRequirements(PRE_REQS, NETWORK_PROJECTS, req_type='prereq') + if len(PRE_REQS['trn'])>0 or len(PRE_REQS['hwy'])>0: + writeRequirements(PRE_REQS, NETWORK_PROJECTS, req_type='prereq') + if allPrereqsFound: + Wrangler.WranglerLogger.debug('All PRE-REQUISITES were found. Are the PRE-REQUISITES matches correct? (y/n)') + else: + Wrangler.WranglerLogger.debug('!!!WARNING!!! Some PRE-REQUISITES were not found or ordered correctly. Continue anyway? (y/n)') + response = input("") + Wrangler.WranglerLogger.debug(" response = [%s]" % response) + if response.strip().lower() not in ["y", "yes"]: + sys.exit(2) + + # Check coreqs + (CO_REQS, allCoreqsFound) = checkRequirements(CO_REQS, NETWORK_PROJECTS, req_type='coreq') + if len(CO_REQS['trn'])>0 or len(CO_REQS['hwy'])>0: + writeRequirements(CO_REQS, NETWORK_PROJECTS, req_type='coreq') + if allCoreqsFound: + Wrangler.WranglerLogger.debug('All CO-REQUISITES were found. Are the CO-REQUISITE matches correct? (y/n)') + else: + Wrangler.WranglerLogger.debug('!!!WARNING!!! Some CO-REQUISITES were not found. Continue anyway? (y/n)') + response = input("") + Wrangler.WranglerLogger.debug(" response = [%s]" % response) + if response.strip().lower() not in ["y", "yes"]: + sys.exit(2) + + # Check conflicts + (CONFLICTS, anyConflictFound) = checkRequirements(CONFLICTS, NETWORK_PROJECTS, req_type='conflict') + if len(CONFLICTS['trn'])>0 or len(CONFLICTS['hwy'])>0: + writeRequirements(CONFLICTS, NETWORK_PROJECTS, 'conflict') + if anyConflictFound: + Wrangler.WranglerLogger.debug('!!!WARNING!!! Conflicting projects were found. Continue anyway? (y/n)') + else: + Wrangler.WranglerLogger.debug('No conflicting projects were found. Enter \'y\' to continue.') + response = input("") + Wrangler.WranglerLogger.debug(" response = [%s]" % response) + if response.strip().lower() not in ["y", "yes"]: + sys.exit(2) + + # Wrangler.WranglerLogger.debug("NETWORK_PROJECTS=%s NET_MODES=%s" % (str(NETWORK_PROJECTS), str(NET_MODES))) + +############################################################################### + +if __name__ == '__main__': + parser = argparse.ArgumentParser(description=USAGE, formatter_class=argparse.RawDescriptionHelpFormatter) + parser.add_argument("--configword", help="optional word for network specification script") + parser.add_argument("--model_type", choices=[Wrangler.Network.MODEL_TYPE_TM1, Wrangler.Network.MODEL_TYPE_TM2], + default=Wrangler.Network.MODEL_TYPE_TM1) + parser.add_argument("--continue_on_warning", help="Don't prompt the user to continue if there are warnings; just warn and continue", action="store_true") + parser.add_argument("--skip_precheck_requirements", help="Don't precheck network requirements, stale projects, non-HEAD projects, etc", action="store_true") + parser.add_argument("net_spec", metavar="network_specification.py", help="Script which defines required variables indicating how to build the network") + parser.add_argument("netvariant", choices=["Baseline", "Blueprint", "Alt1", "Alt2", "NextGenFwy","TIP2023", "NGFNoProject", "NGFNoProjectNoSFCordon"], help="Specify which network variant network to create.") + args = parser.parse_args() + + NOW = time.strftime("%Y%b%d.%H%M%S") + BUILD_MODE = None # regular + if args.model_type == Wrangler.Network.MODEL_TYPE_TM1: + PIVOT_DIR = r"M:\\Application\\Model One\\Networks\\TM1_2015_Base_Network" + TRANSIT_CAPACITY_DIR = os.path.join(PIVOT_DIR, "trn") + NETWORK_BASE_DIR = r"M:\\Application\\Model One\\NetworkProjects" + TRN_SUBDIR = "trn" + TRN_NET_NAME = "Transit_Lines" + HWY_SUBDIR = "hwy" + HWY_NET_NAME = "freeflow.net" + elif args.model_type == Wrangler.Network.MODEL_TYPE_TM2: + PIVOT_DIR = os.path.join(os.environ["USERPROFILE"], "Box","Modeling and Surveys","Development","Travel Model Two Development","Model Inputs","2015_revised_mazs") + TRANSIT_CAPACITY_DIR = None + NETWORK_BASE_DIR = r"M:\\Application\\Model Two\\NetworkProjects" + TRN_SUBDIR = "trn" + TRN_NET_NAME = "transitLines" + HWY_SUBDIR = "hwy" + HWY_NET_NAME = "mtc_final_network_base.net" + + PROJECT = "Blueprint" + + # Read the configuration + NETWORK_CONFIG = args.net_spec + NET_VARIANT = args.netvariant + # Use the NGF_NoProject git tag when building a Next Gen Freeways No Project variant + if NET_VARIANT=="NGFNoProject" or NET_VARIANT=="NGFNoProjectNoSFCordon": + TAG = "NGF_NoProject" + + # networks and log file will be in BlueprintNetworks + if not os.path.exists("BlueprintNetworks"): + os.mkdir("BlueprintNetworks") + + LOG_FILENAME = "build%snetwork_%s_%s_%s.info.LOG" % ("TEST" if BUILD_MODE=="test" else "", PROJECT, NET_VARIANT, NOW) + Wrangler.setupLogging(os.path.join("BlueprintNetworks",LOG_FILENAME), + os.path.join("BlueprintNetworks",LOG_FILENAME.replace("info", "debug"))) + + exec(open(NETWORK_CONFIG).read()) + + # Verify mandatory fields are set + if PROJECT==None: + print("PROJECT not set in %s" % NETWORK_CONFIG) + sys.exit(2) + if TAG==None: + print("TAG not set in %s" % NETWORK_CONFIG) + sys.exit(2) + if NETWORK_PROJECTS==None: + print("NETWORK_PROJECTS not set in %s" % NETWORK_CONFIG) + sys.exit(2) + + if TRANSIT_CAPACITY_DIR: + Wrangler.TransitNetwork.capacity = Wrangler.TransitCapacity(directory=TRANSIT_CAPACITY_DIR) + + # Create a scratch directory to check out project repos into + SCRATCH_SUBDIR = "scratch" + TEMP_SUBDIR = "Wrangler_tmp_" + NOW + if not os.path.exists(SCRATCH_SUBDIR): os.mkdir(SCRATCH_SUBDIR) + os.chdir(SCRATCH_SUBDIR) + + os.environ["CHAMP_node_names"] = os.path.join(PIVOT_DIR,"Node Description.xls") + + networks = { + 'hwy' :Wrangler.HighwayNetwork(modelType=args.model_type, modelVersion=1.0, + basenetworkpath=os.path.join(PIVOT_DIR,"hwy"), + networkBaseDir=NETWORK_BASE_DIR, + networkProjectSubdir=NETWORK_PROJECT_SUBDIR, + networkSeedSubdir=NETWORK_SEED_SUBDIR, + networkPlanSubdir=NETWORK_PLAN_SUBDIR, + isTiered=True if PIVOT_DIR else False, + tag=TAG, + tempdir=TEMP_SUBDIR, + networkName="hwy", + tierNetworkName=HWY_NET_NAME), + 'trn':Wrangler.TransitNetwork( modelType=args.model_type, modelVersion=1.0, + basenetworkpath=os.path.join(PIVOT_DIR,"trn"), + networkBaseDir=NETWORK_BASE_DIR, + networkProjectSubdir=NETWORK_PROJECT_SUBDIR, + networkSeedSubdir=NETWORK_SEED_SUBDIR, + networkPlanSubdir=NETWORK_PLAN_SUBDIR, + isTiered=True if PIVOT_DIR else False, + networkName=TRN_NET_NAME) + } + + # For projects applied in a pivot network (because they won't show up in the current project list) + if APPLIED_PROJECTS != None: + for proj in APPLIED_PROJECTS: + networks['hwy'].appliedProjects[proj]=TAG + + + # Wrangler.WranglerLogger.debug("NETWORK_PROJECTS=%s NET_MODES=%s" % (str(NETWORK_PROJECTS), str(NET_MODES))) + if args.skip_precheck_requirements: + Wrangler.WranglerLogger.info("skip_precheck_requirements passed so skipping preCheckRequirementsForAllProjects()") + else: + preCheckRequirementsForAllProjects(networks, args.continue_on_warning) + + # create the subdir for SET_CAPCLASS with set_capclass.job as apply.s + SET_CAPCLASS = "set_capclass" + SET_CAPCLASS_DIR = os.path.join(TEMP_SUBDIR, SET_CAPCLASS) + os.makedirs(SET_CAPCLASS_DIR) + source_file = os.path.join(os.path.dirname(THIS_FILE), "set_capclass.job") + shutil.copyfile( source_file, os.path.join(SET_CAPCLASS_DIR, "apply.s")) + + networks_without_earthquake = {} + + # Network Loop #2: Now that everything has been checked, build the networks. + for YEAR in NETWORK_PROJECTS.keys(): + projects_for_year = NETWORK_PROJECTS[YEAR] + + appliedcount = 0 + for netmode in NET_MODES: + Wrangler.WranglerLogger.info("Building {} {} networks".format(YEAR, netmode)) + + for project in projects_for_year[netmode]: + (project_name, projType, tag, kwargs) = getProjectAttributes(project) + if tag == None: tag = TAG + + Wrangler.WranglerLogger.info("Applying project [{}] of type [{}] with tag [{}] and kwargs[{}]".format(project_name, projType, tag, kwargs)) + if projType=='plan': + continue + + applied_SHA1 = None + cloned_SHA1 = networks[netmode].cloneProject(networkdir=project_name, tag=tag, + projtype=projType, tempdir=TEMP_SUBDIR, **kwargs) + (parentdir, networkdir, gitdir, projectsubdir) = networks[netmode].getClonedProjectArgs(project_name, None, projType, TEMP_SUBDIR) + + applied_SHA1 = networks[netmode].applyProject(parentdir, networkdir, gitdir, projectsubdir, **kwargs) + appliedcount += 1 + + # if hwy project has set_capclass override, copy it to set_capclass/apply.s + set_capclass_override = os.path.join(TEMP_SUBDIR, project_name, "set_capclass.job") + if os.path.exists(set_capclass_override): + dest_file = os.path.join(SET_CAPCLASS_DIR, "apply.s") + shutil.copyfile(set_capclass_override, dest_file) + Wrangler.WranglerLogger.info("Copied override {} to {}".format(set_capclass_override, dest_file)) + + + if appliedcount == 0: + Wrangler.WranglerLogger.info("No applied projects for this year -- skipping output") + continue + + if NET_VARIANT!="Baseline" and YEAR==2015: + Wrangler.WranglerLogger.info("Blueprint 2015 == Baseline 2015 -- skipping output") + continue + + # Baseline AND YEAR >= 2035 get SLR, covered in next clause + if NET_VARIANT!="Baseline" or YEAR<2035: + + hwypath=os.path.join("..", "BlueprintNetworks", "net_{}_{}".format(YEAR, NET_VARIANT), HWY_SUBDIR) + if not os.path.exists(hwypath): os.makedirs(hwypath) + trnpath = os.path.join("..", "BlueprintNetworks", "net_{}_{}".format(YEAR, NET_VARIANT), TRN_SUBDIR) + if not os.path.exists(trnpath): os.makedirs(trnpath) + + # apply set_capclass before writing any hwy network + applied_SHA1 = networks['hwy'].applyProject(parentdir=TEMP_SUBDIR, networkdir=SET_CAPCLASS, + gitdir=os.path.join(TEMP_SUBDIR, SET_CAPCLASS), **kwargs) + + networks['hwy'].write(path=hwypath,name=HWY_NET_NAME,suppressQuery=True, + suppressValidation=True) # MTC doesn't have turn penalties + + networks['trn'].write(path=trnpath, + name="transitLines", + writeEmptyFiles = False, + suppressQuery = True if BUILD_MODE=="test" else False, + suppressValidation = False, + cubeNetFileForValidation = os.path.join(os.path.abspath(hwypath), HWY_NET_NAME)) + + + # Write the transit capacity configuration + Wrangler.TransitNetwork.capacity.writeTransitVehicleToCapacity(directory = trnpath) + Wrangler.TransitNetwork.capacity.writeTransitLineToVehicle(directory = trnpath) + Wrangler.TransitNetwork.capacity.writeTransitPrefixToVehicle(directory = trnpath) + + # build the Baseline, with Sea Level Rise effects + if NET_VARIANT=="Baseline" and YEAR>=2035: + + # Sea Level Rise effects + # no inundation prior to 2035 + # 1 foot between 2035 and 2045 + # 2 foot in 2050 + if YEAR >= 2035 and YEAR < 2050: + BP_SLR_PROJECT = {'name':"BP_Sea_Level_Rise_Inundation", 'kwargs':{'MODELYEAR':'2035'}} + if YEAR == 2050: + BP_SLR_PROJECT = {'name':"BP_Sea_Level_Rise_Inundation", 'kwargs':{'MODELYEAR':'2050'}} + + # it would be nice if this were more automatic... + networks['hwy'].saveNetworkFiles(suffix="_pre_SLR", to_suffix=True) + + networks_bp_baseline = {} + networks_bp_baseline['hwy'] = copy.deepcopy(networks['hwy']) + networks_bp_baseline['trn'] = copy.deepcopy(networks['trn']) + + for netmode in NET_MODES: + (project_name, projType, tag, kwargs) = getProjectAttributes(BP_SLR_PROJECT) + # Wrangler.WranglerLogger.debug("BP SLR Project {} has project_name=[{}] projType=[{}] tag=[{}] kwargs=[{}]".format(BP_SLR_PROJECT, + # project_name, projType, tag, kwargs)) + applied_SHA1 = None + copyloned_SHA1 = networks_bp_baseline[netmode].cloneProject(networkdir=project_name, tag=tag, + projtype=projType, tempdir=TEMP_SUBDIR, **kwargs) + (parentdir, networkdir, gitdir, projectsubdir) = networks_bp_baseline[netmode].getClonedProjectArgs(project_name, None, projType, TEMP_SUBDIR) + applied_SHA1 = networks_bp_baseline[netmode].applyProject(parentdir, networkdir, gitdir, projectsubdir, **kwargs) + + hwypath=os.path.join("..", "BlueprintNetworks", "net_{}_{}".format(YEAR, NET_VARIANT), HWY_SUBDIR) + if not os.path.exists(hwypath): os.makedirs(hwypath) + trnpath = os.path.join("..", "BlueprintNetworks", "net_{}_{}".format(YEAR, NET_VARIANT), TRN_SUBDIR) + if not os.path.exists(trnpath): os.makedirs(trnpath) + + applied_SHA1 = networks_bp_baseline['hwy'].applyProject(parentdir=TEMP_SUBDIR, networkdir=SET_CAPCLASS, + gitdir=os.path.join(TEMP_SUBDIR, SET_CAPCLASS)) + + networks_bp_baseline['hwy'].write(path=hwypath,name=HWY_NET_NAME,suppressQuery=True, + suppressValidation=True) # MTC doesn't have turn penalties + + networks_bp_baseline['trn'].write(path=trnpath, + name="transitLines", + writeEmptyFiles = False, + suppressQuery = True if BUILD_MODE=="test" else False, + suppressValidation = False, + cubeNetFileForValidation = os.path.join(os.path.abspath(hwypath), HWY_NET_NAME)) + + + # Write the transit capacity configuration + Wrangler.TransitNetwork.capacity.writeTransitVehicleToCapacity(directory = trnpath) + Wrangler.TransitNetwork.capacity.writeTransitLineToVehicle(directory = trnpath) + Wrangler.TransitNetwork.capacity.writeTransitPrefixToVehicle(directory = trnpath) + + # revert back to the plus rowadway network without BP_Sea_Level_Rise_Inundation + networks['hwy'].saveNetworkFiles(suffix="_pre_SLR", to_suffix=False) + + + Wrangler.WranglerLogger.debug("Successfully completed running %s" % os.path.abspath(__file__)) diff --git a/scripts/net_spec_NGF.py b/scripts/net_spec_NGF.py new file mode 100644 index 0000000..7e754f1 --- /dev/null +++ b/scripts/net_spec_NGF.py @@ -0,0 +1,170 @@ +import os +import collections +# MANDATORY. Set this to be the Project Name. +# e.g. "RTP2021", "TIP2021", etc +PROJECT = "NGF" + +# MANDATORY. Set this to be the git tag for checking out network projects. +#TAG = "HEAD" # Use this tag if you want NetworkWrangler to use the latest version in the local repo to build the network +#TAG = "PBA50_Blueprint" # Use this tag if you want to replicate the network built for PBA50 +TAG = "HEAD" + +# A project can either be a simple string, or it can be +# a dictionary with with keys 'name', 'tag' (optional), and 'kwargs' (optional) +# to specify a special tag or special keyword args for the projects apply() call. +# For example: +# {'name':"Muni_TEP", 'kwargs':{'servicePlan':"'2012oct'"}} + +########################################################### +# NextGenFwy projects + + +# Pathways - note these are 2035 projects +NGF_PROJECTS = { + 'BlueprintSegmented':{ + 'hwy':[ + 'NGF_BlueprintSegmented', # All lane tolling on freeways + ], + 'trn':[] + }, + # Pathway 1a: All-lane tolling + Improve Transit Experience + # https://app.asana.com/0/1203644633064654/1203644636776961/f + 'P1a_AllLaneTolling_ImproveTransit':{ + 'hwy':[ + 'NGF_BlueprintSegmented', # All lane tolling on freeways + 'Futures_C4_ReX_Express', # New Transit Service Near Tolling: ReX Express + 'ReX_link' # New Transit Service Near Tolling: ReX Link + ], + 'trn':[ + 'NGF_NoProject_farefiles', # ensures these files get included; note this is not a real project + 'Futures_C4_ReX_Express', # New Transit Service Near Tolling: ReX Express + 'ReX_link', # New Transit Service Near Tolling: Rex Link + # Local Transit Frequency Boosts 2 + {'name':'NGF_IncreaseTrnFreqXferRoutes2BartCaltrainFerry', 'kwargs':{ + 'top_n_local':'10', + # configure by mode: https://github.com/BayAreaMetro/modeling-website/wiki/TransitModes + 'min_headway':'{"local_default":30, 78:8, 111:10}', + 'include_connections_to_express_bus':'True', + # this directory is used to determine which routes have frequency increases. So to include ReX Express bus routes, + # use a directory that includes ReX Express routes (e.g. an earlier iteration of this scenario) + 'transit_assignment_dir':'r"L:\\Application\\Model_One\\NextGenFwys\\Scenarios\\2035_TM152_NGF_ReXExpress_ReXLink_trnassignment\\OUTPUT\\trn"' + }}, + # Trunkline Transit Frequency Bosts 2 + #{'name':'NGF_TrunklineTrnFreqBoosts', 'kwargs':{ + # 'min_headway':'10', + # 'include_rail':'False' + #}}, + # Extended Transit Service Hours + #{'name':'NGF_TrnExtendedServiceHours', 'kwargs':{ 'EV_headway':'15' }}, + ] + }, + # Pathway 1b: All-lane tolling + Focus on Affordability + # https://app.asana.com/0/1203644633064654/1203644636776965/f + 'P1b_AllLaneTolling_Affordable':{ + 'hwy':[ + 'NGF_BlueprintSegmented', # All lane tolling on freeways + ], + 'trn':[ + 'NGF_NoProject_farefiles', # ensures these files get included; note this is not a real project + # Trunkline Transit Frequency Bosts 2 + {'name':'NGF_TrunklineTrnFreqBoosts', 'kwargs':{ + 'min_headway':'10', + 'include_rail':'False' + }}, + # Extended Transit Service Hours + {'name':'NGF_TrnExtendedServiceHours', 'kwargs':{ 'EV_headway':'15' }}, + ] + }, + 'P2a_AllLaneTollingPlusArterials_ImproveTransit':{ + 'hwy':[ + 'NGF_BlueprintSegmented', # All lane tolling on freeways + ], + 'trn':[ + 'NGF_NoProject_farefiles', # ensures these files get included; note this is not a real project + ] + }, + 'P2b_AllLaneTollingPlusArterials_Affordable':{ + 'hwy':[ + 'NGF_BlueprintSegmented', # All lane tolling on freeways + ], + 'trn':[ + 'NGF_NoProject_farefiles', # ensures these files get included; note this is not a real project + ] + }, + 'P3_3Cordons':{ + 'hwy':[ + 'MAJ_SF_Congestion_Pricing', # San Francisco Cordon Pricing + 'NGF_AL_Cordon', # Oakland Cordon Pricing + 'NGF_SC_Cordon' # San Jose Cordon Pricing + ], + 'trn':[ + 'NGF_NoProject_farefiles', # ensures these files get included; note this is not a real project + # Test: Trunkline Transit Frequency Boosts 3 + {'name':'NGF_TrunklineTrnFreqBoosts', 'kwargs':{ + 'min_headway':'10', + 'include_rail':'True' + }}, + ] + }, +} + +# Put them together for NETWORK_PROJECTS +NETWORK_PROJECTS = collections.OrderedDict() + +# we're only building 2035 +for YEAR in [2035]: + + NETWORK_PROJECTS[YEAR] = { + 'hwy':NGF_PROJECTS[SCENARIO]['hwy'], + 'trn':NGF_PROJECTS[SCENARIO]['trn'] + } + # handle net_remove, nets keywords + for netmode in ['hwy','trn']: + + # iterate backwards via index to delete cleanly + for project_idx in range(len(NETWORK_PROJECTS[YEAR][netmode])-1,-1,-1): + project = NETWORK_PROJECTS[YEAR][netmode][project_idx] + # special handling requires project to be specified as dictionary + if not isinstance(project, dict): continue + + # variants_exclude: specifies list of network variants for which this project should be *excluded* + if 'variants_exclude' in project.keys() and NET_VARIANT in project['variants_exclude']: + Wrangler.WranglerLogger.info("Removing {} {} {}".format(YEAR, netmode, project)) + del NETWORK_PROJECTS[YEAR][netmode][project_idx] + continue + + # variants_include: specifies list of network variants for which this project should be *included* + # if this keyword is present, then this project is included *only* for variants in this list + if 'variants_include' in project.keys() and NET_VARIANT not in project['variants_include']: + Wrangler.WranglerLogger.info("Removing {} {} {}".format(YEAR, netmode, project)) + del NETWORK_PROJECTS[YEAR][netmode][project_idx] + continue + +# For every year where a project is applied do the following: +# Convert all zero-length links to 0.01 +# Move buses to HOV/Express lanes at the end +# +for YEAR in NETWORK_PROJECTS.keys(): + # if anything is applied + if ((len(NETWORK_PROJECTS[YEAR]['hwy']) > 0) or (len(NETWORK_PROJECTS[YEAR]['trn']) > 0)): + NETWORK_PROJECTS[YEAR]['hwy'].append('No_zero_length_links') + + if ((len(NETWORK_PROJECTS[YEAR]['hwy']) > 0) or (len(NETWORK_PROJECTS[YEAR]['trn']) > 0)): + NETWORK_PROJECTS[YEAR]['trn'].append('Move_buses_to_HOV_EXP_lanes') + + +# OPTIONAL. The default route network project directory is Y:\networks. If +# projects are stored in another directory, then use this variable to specify it. +# For example: Y:\networks\projects +# NETWORK_BASE_DIR = None +# NETWORK_PROJECT_SUBDIR = None +# NETWORK_SEED_SUBDIR = None +# NETWORK_PLAN_SUBDIR = None +# OPTIONAL. A list of project names which have been previously applied in the +# PIVOT_DIR network that projects in this project might rely on. For example +# if DoyleDrive exists, then Muni_TEP gets applied differently so transit lines +# run on the new Doyle Drive alignment +APPLIED_PROJECTS = None +# OPTIONAL. A list of project names. For test mode, these projects won't use +# the TAG. This is meant for developing a network project. +TEST_PROJECTS = [] diff --git a/scripts/net_spec_STIP2019_Project.py b/scripts/net_spec_STIP2019_Project.py deleted file mode 100644 index 9e06c23..0000000 --- a/scripts/net_spec_STIP2019_Project.py +++ /dev/null @@ -1,94 +0,0 @@ -import os - -# MANDATORY. Set this to be the Project Name. -# e.g. "RTP2021", "TIP2021", etc -PROJECT = "STIP 2019" - -# MANDATORY. Set this to be the Scenario Name -# e.g. "Base", "Baseline" -SCENARIO = "2040_Project" - -# MANDATORY. Set this to be the git tag for checking out network projects. -TAG = "HEAD" - -# MANDATORY. Set this to the directory in which to write your outputs. -# "hwy" and "trn" subdirectories will be created here. -OUT_DIR = SCENARIO + "_network_{}" # YEAR - -# MANDATORY. Should be a dictionary with keys "hwy", "muni", "rail", "bus" -# to a list of projects. A project can either be a simple string, or it can be -# a dictionary with with keys 'name', 'tag' (optional), and 'kwargs' (optional) -# to specify a special tag or special keyword args for the projects apply() call. -# For example: -# {'name':"Muni_TEP", 'kwargs':{'servicePlan':"'2012oct'"}} -########################################################### -# Baseline Projects -NETWORK_PROJECTS = collections.OrderedDict([ - (2015, - {'hwy':['PROJ_attributes'], - 'trn':[]}), - (2020, { - 'hwy':['STIP_Base_MarinNarrows', - 'STIP_HOT', - 'ALA150004_EastBay_BRT', - 'SF_070005_VanNess_BRT'], - 'trn':['SOL030002_FairfieldVacaville_Stn', - 'ALA150004_EastBay_BRT', - 'ALA050015_BART_to_WarmSprings', - 'STIP_ACBRT', - 'CC_050025_EBart_to_Antioch', - 'SCL110005_BART_to_Berryessa', - 'SF_010015_Transbay_Terminal', - 'SF_010037_Muni_Central_Subway', - 'SON090002_SMART', - 'SF_070005_VanNess_BRT', - 'CC_070062_Richmond_Ferry'] - }), - (2025, { - 'hwy':['SOL110006_Jepson_1B_1C', - 'STIP_US101_Managed_Lanes_I80_Solano', - 'STIP_US101_Managed_Lanes_NI380', - 'STIP_US101_ManagedLanes_Whipple_I380', - 'STIP_US101_ExpLanes_Phase5'], - 'trn':[] - }), - (2030, { - 'hwy':['MAJ_SR4_Operational_Improvements'], - ' trn':['MAJ_BRT030001_BART_to_SanJose'] - }), - (2035, { - 'hwy':[], 'trn':[] - }), - (2040, { - 'hwy':['STIP_17_06_0010_WoodsideRd', - 'STIP_ProduceAve', - 'STIP_ITS_SoSF', - 'STIP_ITS_SM', - 'STIP_FairgroundsDr'], - 'trn':[] - }), - (2045, { - 'hwy':[], 'trn':[] - }), - (2050, { - 'hwy':[], 'trn':[] - }) -]) - -# OPTIONAL. The default route network project directory is Y:\networks. If -# projects are stored in another directory, then use this variable to specify it. -# For example: Y:\networks\projects -# NETWORK_BASE_DIR = None -# NETWORK_PROJECT_SUBDIR = None -# NETWORK_SEED_SUBDIR = None -# NETWORK_PLAN_SUBDIR = None - -# OPTIONAL. A list of project names which have been previously applied in the -# PIVOT_DIR network that projects in this project might rely on. For example -# if DoyleDrive exists, then Muni_TEP gets applied differently so transit lines -# run on the new Doyle Drive alignment -APPLIED_PROJECTS = None - -# OPTIONAL. A list of project names. For test mode, these projects won't use -# the TAG. This is meant for developing a network project. -TEST_PROJECTS = [] diff --git a/scripts/net_spec_STIP2022.py b/scripts/net_spec_STIP2022.py new file mode 100644 index 0000000..2f489a1 --- /dev/null +++ b/scripts/net_spec_STIP2022.py @@ -0,0 +1,272 @@ +import os + +# MANDATORY. Set this to be the Project Name. +# e.g. "RTP2021", "TIP2021", etc +PROJECT = "STIP2022" + +# MANDATORY. Set this to be the Scenario Name +# Pass this as --scenario to build_network_mtc.py +assert(SCENARIO in ["NoProject","Project"]) + +# MANDATORY. Set this to be the git tag for checking out network projects. +TAG = "HEAD" + +# MANDATORY. Set this to the directory in which to write your outputs. +# "hwy" and "trn" subdirectories will be created here. +OUT_DIR = PROJECT + "_" + SCENARIO + "_network_{}" # YEAR + +# MANDATORY. Should be a dictionary with keys "hwy", "muni", "rail", "bus" +# to a list of projects. A project can either be a simple string, or it can be +# a dictionary with with keys 'name', 'tag' (optional), and 'kwargs' (optional) +# to specify a special tag or special keyword args for the projects apply() call. +# For example: +# {'name':"Muni_TEP", 'kwargs':{'servicePlan':"'2012oct'"}} + +########################################################### +COMMITTED_PROJECTS = collections.OrderedDict([ + (2015, { + 'hwy':['PROJ_attributes', # adds PROJ attributes to NODE and LINK + {'name':'Bridge_Toll_Updates_2_2pct', 'kwargs':{'MODELYEAR':'2015'}}], + 'trn':[] + }), + (2020, { + 'hwy':[{'name':'Bridge_Toll_Updates_2_2pct', 'kwargs':{'MODELYEAR':'2020'}}, + {'name':'EXP_237B', 'kwargs':{'FUTURE':"PBA50"}}, # todo: update this to support PBA50 + 'EXP_580C', + 'EXP_680D', + 'EXP_880A', + 'HOV_680F', + 'SCL130001_237_101_MAT_Int_Mod', + 'REG090003_SCLARA_FIP', + 'ALA130005_Dougherty_road_widening', + 'ALA130006_Dublin_Blvd_widening', + 'ALA130014_7th_St_road_diet', + 'ALA130026_Shattuck_Complete_Streets', + 'ALA170049_Central_AVE_Safety_Improvements', + 'ALA150004_EastBay_BRT', + 'CC_130001_BaileyRd_SR4', + 'CC_130046_I680_SR4_Int_Rec', + 'CC_070035_I80_SPDamRd_Int_Phase1', + 'CC_070011_Brentwood_Blvd_Widening', + 'CC_070075_Kirker_Pass_Truck_Lane', + 'CC_090019_Bollinger_Canyon_Widening', + 'CC_130006_Concord_BART_road_diets', + 'CC_170001_SanRamonValleyBlvd_Lane_Addition', + 'MRN150009_San_Rafael_Bridge_Improvements', + 'SF_070027_Yerba_Buena_Ramp_Imp', + 'SF_070005_VanNess_BRT', + 'SF_130011_2ndSt_Road_Diet', + 'SF_Market_Street_Closure', + 'SM_110047_SR92_ElCam_Ramp_Mod', + 'SOL110005_Jepson_Van_to_Com', + 'FBP_SL_042_Jepson_2A', + 'SON070004_101_MarinSonNarrows_Phase1', + 'ALA050014_SR84_Widening', + 'ALA170011_BayBridge_HOV_Connectors', + 'ALA150047_TelegraphAve_Complete_Streets', + 'SM_110047_SR92_ElCam_Ramp_Mod', + 'SCL190002_280_Foothill_improvement', + 'SCL190006_101SB_offramp_improvement', + 'I80_AdaptiveRampMetering', + 'VAR170021_Freeway_Performance_I880', + 'SonomaCounty_Transit_NoBuild2050', + 'SF_MuniForward_Committed', + 'FBP_MU_029_Broadway_Transit_Only_Lanes', + 'EXP_Blueprint_NoProject', + 'FBP_AL_067_Rte84Wide', + 'FBP_AL_065_Bancroft_Bus_Only', + 'FBP_SM_032_US101_Willow_Interchange'], + 'trn':['ALA050015_BART_to_WarmSprings', + 'ACGo', + 'CC_050025_EBart_to_Antioch', + 'GGTransit_Committed', + 'SCL110005_BART_to_Berryessa', + 'SF_010015_Transbay_Terminal', + 'SF_010037_Muni_Central_Subway', + 'SF_070027_Yerba_Buena_Ramp_Imp', + 'SOL030002_FairfieldVacaville_Stn', + 'SON090002_SMART', + 'SON090002_SMART_to_Larkspur', + 'CC_070062_Richmond_Ferry', + 'SF_MuniForward_Committed', + 'VTA_Next', + 'SCL130001_237_101_MAT_Int_Mod', + 'SonomaCounty_Transit_NoBuild2050', + 'SMART_Novato', + 'Xfare_update_2020', + 'ACTransit_Committed', + 'ferry_update_2019', + 'Napa_Solano_Updates_2020', + 'FBP_Beale_Transit_Only_Lane', + 'SamTrans_ECR_Rapid', + 'ALA150004_EastBay_BRT', + {'name':'FBP_SL_026_SolExpressBus', 'kwargs':{'MODELYEAR':'2020'}}], + }), + (2025, { + 'hwy':[{'name':'Bridge_Toll_Updates_2_2pct', 'kwargs':{'MODELYEAR':'2025'}}, + 'EXP_CC_050028_I680_SB_HOV_Completion', + 'EXP_101B1', + 'EXP_101B2', + 'EXP_680C1', + 'EXP_680F', + 'EXP_85D', + 'EXP_101C', + 'ALA150001_I680_SR84_Int_Wid', + 'ALA150043_Claremont_road_diet', + 'CC_070009_Slatten_Ranch_Rd_Extension', + 'SF_070004_Geary_BRT_Phase1', + 'SON070004_101_MarinSonNarrows_Phase2', + 'SOL110006_Jepson_1B_1C', + 'SCL190008_US101_DLC_Int_Imp', + 'I880_US101_AdaptiveRampMetering', + 'SOL070020_I80_I680_SR12_Int_1_2A', + 'FBP_NP_036_SR29_Imola_PNR', + 'ALA170052_Fruitvale_Ave_ped_improvements', + 'EXP_Blueprint_NoProject', + {'name': 'RRSP_Alameda_Point_Transit_Improvements', 'kwargs':{'BUILT':"'built'"}}], + 'trn':['SF_010028_Caltrain_Modernization', + 'SON090002_SMART_to_Windsor', + 'REG090037_New_BART_Trains', + 'FBP_NP_036_SR29_Imola_PNR', + 'SOL070020_I80_I680_SR12_Int_1_2A', + {'name': 'RRSP_Alameda_Point_Transit_Improvements', 'kwargs':{'BUILT':"'built'"}}] + }), + (2030, { + 'hwy':[{'name':'Bridge_Toll_Updates_2_2pct', 'kwargs':{'MODELYEAR':'2030'}}, + 'EXP_Blueprint_NoProject'], + 'trn':['BART_NoProject'] + }), + (2035, { + 'hwy':[{'name':'Bridge_Toll_Updates_2_2pct', 'kwargs':{'MODELYEAR':'2035'}}, + 'EXP_Blueprint_NoProject'], + 'trn':[] + }), + (2040, { + 'hwy':[{'name':'Bridge_Toll_Updates_2_2pct', 'kwargs':{'MODELYEAR':'2040'}}, + 'EXP_Blueprint_NoProject'], + 'trn':[] + }), + (2045, { + 'hwy':[{'name':'Bridge_Toll_Updates_2_2pct', 'kwargs':{'MODELYEAR':'2045'}}, + 'EXP_Blueprint_NoProject'], + 'trn':[] + }), + (2050, { + 'hwy':[{'name':'Bridge_Toll_Updates_2_2pct', 'kwargs':{'MODELYEAR':'2050'}}, + 'EXP_Blueprint_NoProject'], + 'trn':[] + }) +]) + +########################################################### +# STIP projects +STIP_PROJECTS = collections.OrderedDict([ + (2015, { + 'hwy':[], + 'trn':[] + }), + (2020, { + 'hwy':[], + 'trn':[], + }), + (2025, { + 'hwy':['STIP2022_SCL110001_US101SV_EXP_P5', + 'FBP_CC_050_SR4_Operation_Improvements_EB'], + 'trn':[] + }), + (2030, { + 'hwy':['STIP2022_SM190009_US101SM_EXP', + 'STIP2022_CC170017_I680NB_EXP_P1', + 'FBP_AL_045_Oak_Ala_Access_Pr'], + 'trn':['MAJ_BRT030001_BART_to_SanJose', + 'FBP_AL_045_Oak_Ala_Access_Pr'] + }), + (2035, { + 'hwy':[], + 'trn':[] + }), + (2040, { + 'hwy':['FBP_CC_051_SR4_Operation_Improvements_WB'], + 'trn':[] + }), + (2045, { + 'hwy':[], + 'trn':[] + }), + (2050, { + 'hwy':['STIP_ITS_SM', + 'STIP2022_CC170017_I680_ITS_TOS'], + 'trn':[] + }) +]) + +########################################################### +# Put them together for NETWORK_PROJECTS +NETWORK_PROJECTS = collections.OrderedDict() + +for YEAR in COMMITTED_PROJECTS.keys(): + if SCENARIO == "NoProject": + # baseline: just committed + NETWORK_PROJECTS[YEAR] = { + 'hwy':COMMITTED_PROJECTS[YEAR]['hwy'], + 'trn':COMMITTED_PROJECTS[YEAR]['trn'] + } + + else: + # stip + NETWORK_PROJECTS[YEAR] = { + 'hwy':COMMITTED_PROJECTS[YEAR]['hwy'] + STIP_PROJECTS[YEAR]['hwy'], + 'trn':COMMITTED_PROJECTS[YEAR]['trn'] + STIP_PROJECTS[YEAR]['trn'] + } + # handle net_remove, nets keywords + for netmode in ['hwy','trn']: + + # iterate backwards via index to delete cleanly + for project_idx in range(len(NETWORK_PROJECTS[YEAR][netmode])-1,-1,-1): + project = NETWORK_PROJECTS[YEAR][netmode][project_idx] + # special handling requires project to be specified as dictionary + if not isinstance(project, dict): continue + + # variants_exclude: specifies list of network variants for which this project should be *excluded* + if 'variants_exclude' in project.keys() and NET_VARIANT in project['variants_exclude']: + Wrangler.WranglerLogger.info("Removing {} {} {}".format(YEAR, netmode, project)) + del NETWORK_PROJECTS[YEAR][netmode][project_idx] + continue + + # variants_include: specifies list of network variants for which this project should be *included* + # if this keyword is present, then this project is included *only* for variants in this list + if 'variants_include' in project.keys() and NET_VARIANT not in project['variants_include']: + Wrangler.WranglerLogger.info("Removing {} {} {}".format(YEAR, netmode, project)) + del NETWORK_PROJECTS[YEAR][netmode][project_idx] + continue + +# +# For every year where a project is applied do the following: +# Convert all zero-length links to 0.01 +# Move buses to HOV/Express lanes at the end +# +for YEAR in NETWORK_PROJECTS.keys(): + # if anything is applied + if ((len(NETWORK_PROJECTS[YEAR]['hwy']) > 0) or (len(NETWORK_PROJECTS[YEAR]['trn']) > 0)): + NETWORK_PROJECTS[YEAR]['hwy'].append('No_zero_length_links') + + if ((len(NETWORK_PROJECTS[YEAR]['hwy']) > 0) or (len(NETWORK_PROJECTS[YEAR]['trn']) > 0)): + NETWORK_PROJECTS[YEAR]['trn'].append('Move_buses_to_HOV_EXP_lanes') + +# OPTIONAL. The default route network project directory is Y:\networks. If +# projects are stored in another directory, then use this variable to specify it. +# For example: Y:\networks\projects +# NETWORK_BASE_DIR = None +# NETWORK_PROJECT_SUBDIR = None +# NETWORK_SEED_SUBDIR = None +# NETWORK_PLAN_SUBDIR = None + +# OPTIONAL. A list of project names which have been previously applied in the +# PIVOT_DIR network that projects in this project might rely on. For example +# if DoyleDrive exists, then Muni_TEP gets applied differently so transit lines +# run on the new Doyle Drive alignment +APPLIED_PROJECTS = None + +# OPTIONAL. A list of project names. For test mode, these projects won't use +# the TAG. This is meant for developing a network project. +TEST_PROJECTS = [] diff --git a/scripts/net_spec_TIP2023.py b/scripts/net_spec_TIP2023.py new file mode 100644 index 0000000..b8e41d4 --- /dev/null +++ b/scripts/net_spec_TIP2023.py @@ -0,0 +1,545 @@ +import os + +# MANDATORY. Set this to be the Project Name. +# e.g. "RTP2021", "TIP2021", etc +PROJECT = "TIP2023" + +# MANDATORY. Set this to be the Scenario Name +# Pass this as --scenario to build_network_mtc.py +#assert(SCENARIO in ["NoProject","Project"]) + +# MANDATORY. Set this to be the git tag for checking out network projects. +TAG = "TIP_2023" + +# MANDATORY. Set this to the directory in which to write your outputs. +# "hwy" and "trn" subdirectories will be created here. +#OUT_DIR = PROJECT + "_" + SCENARIO + "_network_{}" # YEAR +OUT_DIR = PROJECT + "_" + "_network_{}" # YEAR + +# MANDATORY. Should be a dictionary with keys "hwy", "muni", "rail", "bus" +# to a list of projects. A project can either be a simple string, or it can be +# a dictionary with with keys 'name', 'tag' (optional), and 'kwargs' (optional) +# to specify a special tag or special keyword args for the projects apply() call. +# For example: +# {'name':"Muni_TEP", 'kwargs':{'servicePlan':"'2012oct'"}} + +########################################################### +COMMITTED_PROJECTS = collections.OrderedDict([ + (2015, { + 'hwy':['PROJ_attributes', # adds PROJ attributes to NODE and LINK + {'name':'Bridge_Toll_Updates_2_2pct', 'kwargs':{'MODELYEAR':'2015'}}], + 'trn':[] + }), + (2020, { + 'hwy':[{'name':'Bridge_Toll_Updates_2_2pct', 'kwargs':{'MODELYEAR':'2020'}}, + {'name':'EXP_237B', 'kwargs':{'FUTURE':"PBA50"}}, # todo: update this to support PBA50 + 'EXP_580C', + 'EXP_680D', + 'EXP_880A', + 'HOV_680F', + 'SCL130001_237_101_MAT_Int_Mod', + 'REG090003_SCLARA_FIP', + 'ALA130005_Dougherty_road_widening', + 'ALA130006_Dublin_Blvd_widening', + 'ALA130014_7th_St_road_diet', + 'ALA130026_Shattuck_Complete_Streets', + 'ALA170049_Central_AVE_Safety_Improvements', + 'ALA150004_EastBay_BRT', + 'CC_130001_BaileyRd_SR4', + 'CC_130046_I680_SR4_Int_Rec', + 'CC_070035_I80_SPDamRd_Int_Phase1', + 'CC_070011_Brentwood_Blvd_Widening', + 'CC_070075_Kirker_Pass_Truck_Lane', + 'CC_090019_Bollinger_Canyon_Widening', + 'CC_130006_Concord_BART_road_diets', + 'CC_170001_SanRamonValleyBlvd_Lane_Addition', + 'MRN150009_San_Rafael_Bridge_Improvements', + 'SF_070005_VanNess_BRT', + 'SF_130011_2ndSt_Road_Diet', + 'SF_Market_Street_Closure', + 'SM_110047_SR92_ElCam_Ramp_Mod', + 'SOL110005_Jepson_Van_to_Com', + 'FBP_SL_042_Jepson_2A', + 'SON070004_101_MarinSonNarrows_Phase1', + 'ALA050014_SR84_Widening', + 'ALA170011_BayBridge_HOV_Connectors', + 'ALA150047_TelegraphAve_Complete_Streets', + 'SM_110047_SR92_ElCam_Ramp_Mod', + 'SCL190002_280_Foothill_improvement', + 'SCL190006_101SB_offramp_improvement', + 'I80_AdaptiveRampMetering', + 'VAR170021_Freeway_Performance_I880', + 'SonomaCounty_Transit_NoBuild2050', + 'SF_MuniForward_Committed', + 'FBP_MU_029_Broadway_Transit_Only_Lanes', + 'EXP_Blueprint_NoProject', + 'FBP_AL_067_Rte84Wide', + 'FBP_AL_065_Bancroft_Bus_Only', + 'FBP_SM_032_US101_Willow_Interchange'], + 'trn':['ALA050015_BART_to_WarmSprings', + 'ACGo', + 'CC_050025_EBart_to_Antioch', + 'GGTransit_Committed', + 'SCL110005_BART_to_Berryessa', + 'SF_010015_Transbay_Terminal', + 'SF_010037_Muni_Central_Subway', + 'SOL030002_FairfieldVacaville_Stn', + 'SON090002_SMART', + 'SON090002_SMART_to_Larkspur', + 'CC_070062_Richmond_Ferry', + 'SF_MuniForward_Committed', + 'VTA_Next', + 'SCL130001_237_101_MAT_Int_Mod', + 'SonomaCounty_Transit_NoBuild2050', + 'SMART_Novato', + 'Xfare_update_2020', + 'ACTransit_Committed', + 'ferry_update_2019', + 'Napa_Solano_Updates_2020', + 'FBP_Beale_Transit_Only_Lane', + 'SamTrans_ECR_Rapid', + 'ALA150004_EastBay_BRT', + {'name':'FBP_SL_026_SolExpressBus', 'kwargs':{'MODELYEAR':'2020'}}], + }), + (2025, { + 'hwy':[{'name':'Bridge_Toll_Updates_2_2pct', 'kwargs':{'MODELYEAR':'2025'}}, + 'EXP_CC_050028_I680_SB_HOV_Completion', + 'EXP_101B1', + 'EXP_101B2', + 'EXP_680C1', + 'EXP_680F', + 'EXP_85D', + 'EXP_101C', + 'ALA150001_I680_SR84_Int_Wid', + 'ALA150043_Claremont_road_diet', + 'CC_070009_Slatten_Ranch_Rd_Extension', + 'SF_070004_Geary_BRT_Phase1', + 'SON070004_101_MarinSonNarrows_Phase2', + 'SOL110006_Jepson_1B_1C', + 'SCL190008_US101_DLC_Int_Imp', + 'I880_US101_AdaptiveRampMetering', + 'SOL070020_I80_I680_SR12_Int_1_2A', + 'FBP_NP_036_SR29_Imola_PNR', + 'ALA170052_Fruitvale_Ave_ped_improvements', + 'EXP_Blueprint_NoProject'], + 'trn':['SF_010028_Caltrain_Modernization', + 'REG090037_New_BART_Trains', + 'FBP_NP_036_SR29_Imola_PNR', + 'SOL070020_I80_I680_SR12_Int_1_2A'] + }), + (2030, { + 'hwy':[{'name':'Bridge_Toll_Updates_2_2pct', 'kwargs':{'MODELYEAR':'2030'}}, + 'EXP_Blueprint_NoProject'], + 'trn':['BART_NoProject', + 'SON090002_SMART_to_Windsor'], + }), + (2035, { + 'hwy':[{'name':'Bridge_Toll_Updates_2_2pct', 'kwargs':{'MODELYEAR':'2035'}}, + 'EXP_Blueprint_NoProject'], + 'trn':[] + }), + (2040, { + 'hwy':[{'name':'Bridge_Toll_Updates_2_2pct', 'kwargs':{'MODELYEAR':'2040'}}, + 'EXP_Blueprint_NoProject'], + 'trn':[] + }), + (2045, { + 'hwy':[{'name':'Bridge_Toll_Updates_2_2pct', 'kwargs':{'MODELYEAR':'2045'}}, + 'EXP_Blueprint_NoProject'], + 'trn':[] + }), + (2050, { + 'hwy':[{'name':'Bridge_Toll_Updates_2_2pct', 'kwargs':{'MODELYEAR':'2050'}}, + 'EXP_Blueprint_NoProject'], + 'trn':[] + }) +]) + +########################################################### +# TIP projects +TIP_PROJECTS = collections.OrderedDict([ + (2015, {'hwy':[], + 'trn':[] + }), + (2020, {'hwy':[], + 'trn':[] + }), + (2025, {'hwy':[{'name':'RRSP_Alameda_Point_Transit_Improvements', 'tag':'PBA50_Blueprint'}, + 'MAJ_MTC050027_Berkeley_Ferry', + 'MAJ_WETA_Service_Frequency_Increase', + 'FBP_MU_041_Hovercraft_Pilot', + 'BP_Vision_Zero', + 'EXP_Blueprint', + 'MAJ_AC_Frequency_Improvement', + 'MRN050034_101_MarinSonNarrows_Phase2', + 'FBP_MU_044_SouthSF_Ferry_Serv_Incr', + 'FBP_MU_029_ACRapid_2025', + 'RRSP_E14_Mission_Corridor', + 'FBP_MR_026_NovatoWide', + 'FBP_NP_038_TSP_On_SR29', + 'FBP_NP_044_Soscol_Junction', + {'name':'MAJ_SOL070020_I80_I680_SR12_Int_2B_7','kwargs':{'PHASE':"'2B'"}}, + {'name':'EXP_uncommitted_all', 'kwargs':{'MODELYEAR':'2025'}, 'variants_exclude':['Alt1', 'NextGenFwy']}, + {'name':'EIR1_EXP_uncommitted_all', 'kwargs':{'MODELYEAR':'2025'}, 'variants_include':['Alt1']}, + {'name':'FBP_CC_15_23rd_St_BRT', 'kwargs':{'MODELYEAR':'2025'}}, + 'FBP_CC_057_LoneTreeWide', + 'FBP_CC_067_WillowPassWide', + 'FBP_CC_065_LaurelWide', + 'FBP_SN_017_Arata_Int', + 'FBP_CC_017_Brentwood_Intermodal', + 'FBP_SF_030_Balboa_Park_Area_2', + 'EXP_Blueprint', + 'FBP_AL_039_I580_Interchange_Imps', + 'FBP_CC_056_LaurelExtension', + 'FBP_SC_084_10th_BridgeWide', + 'FBP_SL_053_PeabodyWide', + 'FBP_SC_073_BlossomHill_101Wide', + 'FBP_SC_082_US101_25_Interchange', + 'FBP_CC_045_SanPabloDam_Interchange_Phase2', + 'FBP_CC_030_OakleyAmtrak', + 'FBP_SM_034_Route92_ElCamino_Interchange', + 'FBP_SL_019_BeniciaRoad_Diet', + 'FBP_SL_023_WestTexasRoad_Diet', + 'FBP_SN_012_PetalumaBlvd_Diet', + 'SF_Park_Presidio_HOV_Pilot', + 'SF_Lombard_HOV_Pilot', + {'name':'EIR2_Val_Link_ExpressBus', 'variants_include':['Alt2']}, + {'name':'EIR2_ReXBlue', 'variants_include':['Alt2']}, + 'FBP_SC_072_US101_Trimble_Interchange', + {'name':'EXP_uncommitted_noAllLaneTolling', 'kwargs':{'MODELYEAR':'2025'}, 'variants_include':['NextGenFwy']}], + 'trn':['FBP_AL_001_NewarkFremPDA', + {'name':'FBP_MU_059_ACTransbay_Freq_Incr', 'variants_exclude':['Alt2']}, + 'MAJ_AC_Frequency_Improvement', + {'name':'RRSP_Alameda_Point_Transit_Improvements', 'tag':'PBA50_Blueprint'}, + 'MAJ_MTC050027_Berkeley_Ferry', + 'MAJ_WETA_Service_Frequency_Increase', + {'name':'FBP_MU_046_ACE_Freq_Inc', 'kwargs':{'MODELYEAR':'2025'}}, + 'FBP_MU_041_Hovercraft_Pilot', + 'FBP_MU_049_Caltrain_6TPHPD', + {'name':'FBP_MU_060_ReX_Blue', 'variants_exclude':['Alt2']}, + {'name':'EIR2_ReXBlue', 'variants_include':['Alt2']}, + 'FBP_MU_044_SouthSF_Ferry_Serv_Incr', + 'GGT_Service_Imp', + 'FBP_MU_029_ACRapid_2025', + 'RRSP_E14_Mission_Corridor', + 'FBP_NP_044_Soscol_Junction', + 'MAJ_Alameda_Point_SF_Ferry', + {'name':'MAJ_SOL070020_I80_I680_SR12_Int_2B_7','kwargs':{'PHASE':"'2B'"}}, + {'name':'FBP_CC_15_23rd_St_BRT', 'kwargs':{'MODELYEAR':'2025'}}, + 'FBP_CC_030_OakleyAmtrak', + 'MAJ_Sonoma_Frequency_Increase', + {'name':'EIR1_Freq_Boosts', 'kwargs':{'MODELYEAR':'2025'}, 'variants_include':['Alt1']}, + {'name':'EIR2_HRA_Freq_Incr', 'kwargs':{'MODELYEAR':'2025'}, 'variants_include':['Alt2']}, + {'name':'EIR2_PDA_Freq_Incr', 'kwargs':{'MODELYEAR':'2025'}, 'variants_include':['Alt2']}, + {'name':'EIR2_Val_Link_ExpressBus', 'variants_include':['Alt2']}] + }), + (2030, {'hwy':['MAJ_SanPablo_BRT', + 'SF_070027_Yerba_Buena_Ramp_Imp', + 'CC_170061_Bus_On_Shoulder_680BRT', + 'BP_Vision_Zero', + {'name':'FBP_AL_021_South_Bay_Connect', 'variants_exclude':['Alt2']}, + 'FBP_MU_044_Richmond_Ferry_Serv_Incr', + 'MAJ_REG090037_BART_Core_Cap', + {'name':'Transform_Valley_Link', 'variants_exclude':['Alt2']}, + 'FBP_NP_040_VINE_Exp_Bus_Enhancements', + 'FBP_AL_045_Oak_Ala_Access_Pr', + # 'FBP_MR_018_US101_BOS', + 'FBP_CC_036_I80_ExpBus_Impr', + {'name':'FBP_CC_040_041_042_I680_SR4_Int_Phases_1_2_4_5','kwargs':{'PHASE':"'4'"}, 'variants_exclude':['Alt1']}, + 'FBP_CC_021_Ant_Mart_Herc_Ferry', + {'name':'EIR1_EXP_uncommitted_all', 'kwargs':{'MODELYEAR':'2030'}, 'variants_include':['Alt1']}, + 'FBP_SC_104_OaklandWide', + {'name':'FBP_CC_15_23rd_St_BRT', 'kwargs':{'MODELYEAR':'2030'}}, + {'name':'FBP_SF_012_Geneva_Harney_BRT', 'kwargs':{'MODELYEAR':'2030'}}, + {'name':'MAJ_Bay_Area_Forward_all', 'kwargs':{'MODELYEAR':'2030'}}, + {'name':'BP_Tolls_On_Congested_Freeways_2030', 'variants_exclude':['NextGenFwy']}, + {'name':'EXP_uncommitted_all', 'kwargs':{'MODELYEAR':'2030'}, 'variants_exclude':['Alt1', 'NextGenFwy']}, + 'FBP_CC_064_CaminoTassajaraWide', + 'FBP_CC_066_CypressWide', + 'FBP_SC_059_SR237EBWide', + 'FBP_AL_064_UnionCityWide', + 'FBP_SC_074_US101_BuenaVista_Int', + 'EXP_Blueprint', + 'FBP_SC_054_SR17_Corridor_Relief', + 'FBP_AL_043_A_StreetWide', + 'FBP_CC_061_062_West_Leland_Ext_Phases1_2', + 'FBP_SM_042_Hwy1_ManorDrive', + 'FBP_SL_042_Jepson_2B_2C', + 'FBP_CC_024_Oakley_PNR_Tri_Delta', + 'FBP_SC_083_US101_Zanker_Skyport_Interchange', + 'FBP_SL_022_SonomaBlvd_Diet', + 'FBP_SM_027_US101_92', + 'FBP_SM_007_ElCamino_CompleteStreets', + 'FBP_AL_044_I880_Whipple_Imps', + 'ALA110002_I880_Industrial_Interchange', + 'FBP_AL_051_7St_Grade_Sep_West', + 'FBP_AL_055_DubBlvd_NCanyons_Ext', + 'FBP_AL_062_TassajaraWide', + 'ALA090020_I880_Industrial_Parkway_AuxLanes', + 'FBP_CC_063_BrentwoodWide', + 'FBP_CC_054_CrowCanyonWide', + 'MAJ_Geary_BRT_Phase2', + 'MAJ_SF_Congestion_Pricing', + 'MAJ_MissionBay_SF_Ferry', + 'FBP_SM_033_US101_Holly_Interchange', + 'FBP_SM_035_Peninsula_101_OnOffRamps', + 'STIP_ProduceAve', + 'MAJ_SCL050009_VTA_Eastridge_Extension', + 'FBP_SC_103_MontagueWide', + 'FBP_SL_033_FairgroundsWide', + 'ALA_Fallon_Realign', + 'ALA210027_180_Powell_Transit_Access', + 'ALA210028_I80_HOV_Bus_Lane_Ext', + 'SCL210026_Julian_James_Conversion', + {'name':'FBP_CC_040_041_042_I680_SR4_Int_Phases_1_2_4_5', 'kwargs':{'PHASE':"'1'"}, 'variants_exclude':['Alt1']}, + {'name':'FBP_CC_040_041_042_I680_SR4_Int_Phases_1_2_4_5', 'kwargs':{'PHASE':"'2'"}, 'variants_exclude':['Alt1']}, + {'name':'FBP_CC_050_SR4_Operation_Improvements_EB', 'variants_exclude':['Alt1']}, + {'name':'FBP_CC_051_SR4_Operation_Improvements_WB', 'variants_exclude':['Alt1']}, + {'name':'EXP_uncommitted_noAllLaneTolling', 'kwargs':{'MODELYEAR':'2030'}, 'variants_include':['NextGenFwy']}, + {'name':'Transform_SR37_Widening_Interim', 'variants_exclude':['Alt1']}], + 'trn':['BP_PDA_Transit_Enhancements', + 'SF_070027_Yerba_Buena_Ramp_Imp', + {'name':'FBP_MU_046_ACE_Freq_Inc', 'kwargs':{'MODELYEAR':'2030'}}, + 'MAJ_BRT030001_BART_to_SanJose', + 'BART_Irvington_Infill', + 'MAJ_REG090037_BART_Core_Cap', + {'name':'FBP_AL_021_South_Bay_Connect', 'variants_exclude':['Alt2']}, + 'FBP_MU_049_Caltrain_8TPHPD', + 'FBP_MU_061_ReX_Green', + 'MAJ_SanPablo_BRT', + 'FBP_MU_044_Richmond_Ferry_Serv_Incr', + {'name':'Transform_Valley_Link', 'variants_exclude':['Alt2']}, + 'FBP_SF_028_SF_Express_Bus_On_Exp_Lanes', + {'name':'MAJ_SF_050002_Caltrain_Ext_TransbayTerminal', 'variants_exclude':['Alt2']}, + 'FBP_SF_024_Historic_Streetcar_Ext', + 'FBP_MuniForward_Uncommitted_Rail', + 'FBP_CC_036_I80_ExpBus_Impr', + 'FBP_CC_021_Ant_Mart_Herc_Ferry', + 'FBP_AL_045_Oak_Ala_Access_Pr', + 'FBP_CC_028_Hercules_Station', + {'name':'FBP_SF_012_Geneva_Harney_BRT', 'kwargs':{'MODELYEAR':'2030'}}, + {'name':'FBP_CC_15_23rd_St_BRT', 'kwargs':{'MODELYEAR':'2030'}}, + 'FBP_CC_024_Oakley_PNR_Tri_Delta', + 'MAJ_Geary_BRT_Phase2', + 'MAJ_SF_Congestion_Pricing', + 'MAJ_MissionBay_SF_Ferry', + 'MAJ_RedwoodCity_SF_Ferry', + 'MAJ_SCL050009_VTA_Eastridge_Extension', + {'name':'FBP_SM_020_Regional_Express_Buses', 'kwargs':{'PHASE':"'Phase1_4Routes'"}}, + {'name':'EIR1_Freq_Boosts', 'kwargs':{'MODELYEAR':'2030'}, 'variants_include':['Alt1']}, + {'name':'EIR2_HRA_Freq_Incr', 'kwargs':{'MODELYEAR':'2030'}, 'variants_include':['Alt2']}, + {'name':'EIR2_PDA_Freq_Incr', 'kwargs':{'MODELYEAR':'2030'}, 'variants_include':['Alt2']}, + {'name':'EIR2_Fix_Alt2', 'kwargs':{'MODELYEAR':'2030'}, 'variants_include':['Alt2']}, + {'name':'Transform_SR37_Widening_Interim', 'variants_exclude':['Alt1']}, + {'name':'SON090002_SMART_NorthPetaluma', 'variants_exclude':['Baseline']}] + }), + (2035, {'hwy':['MAJ_MuniForward_Uncommitted', + 'MAJ_Treasure_Island_Congestion_Pricing', + 'BP_Vision_Zero', + 'RRSP_East_West_Connector', + 'Transform_I680_Multimodal_Imp', + 'FBP_SM_022_I380_Widening', + {'name':'MAJ_SOL070020_I80_I680_SR12_Int_2B_7', 'kwargs':{'PHASE':"'3'"}, 'variants_exclude':['Alt1']}, + {'name':'MAJ_SOL070020_I80_I680_SR12_Int_2B_7', 'kwargs':{'PHASE':"'4'"}, 'variants_exclude':['Alt1']}, + {'name':'MAJ_SOL070020_I80_I680_SR12_Int_2B_7', 'kwargs':{'PHASE':"'5'"}, 'variants_exclude':['Alt1']}, + {'name':'FBP_SF_012_Geneva_Harney_BRT', 'kwargs':{'MODELYEAR':'2035'}}, + {'name':'EXP_uncommitted_all', 'kwargs':{'MODELYEAR':'2035'}, 'variants_exclude':['Alt1', 'NextGenFwy']}, + {'name':'EIR1_EXP_uncommitted_all', 'kwargs':{'MODELYEAR':'2035'}, 'variants_include':['Alt1']}, + {'name':'MAJ_Bay_Area_Forward_all', 'kwargs':{'MODELYEAR':'2035'}}, + {'name':'BP_Tolls_On_Congested_Freeways_2035', 'variants_exclude':['NextGenFwy']}, + 'FBP_AL_076_TelegraphDiet', + 'FBP_SN_018_Cotati_101_RailroadAve_Impr', + 'FBP_NP_079_Trower_Ext', + 'EXP_Blueprint', + {'name':'EIR1_No_SR37', 'variants_include':['Alt1']}, + {'name':'EXP_uncommitted_noAllLaneTolling', 'kwargs':{'MODELYEAR':'2035'}, 'variants_include':['NextGenFwy']}], + 'trn':['MAJ_MuniForward_Uncommitted', + 'RRSP_South_East_Waterfront_Transit_Imp', + 'FBP_MU_062_ReX_Red', + 'Transform_I680_Multimodal_Imp', + 'Transform_SeamlessTransit', + 'MAJ_Treasure_Island_Congestion_Pricing', + 'RRSP_East_West_Connector', + 'MAJ_Treasure_Island_Ferry', + 'FBP_NP_079_Trower_Ext', + {'name':'FBP_SM_020_Regional_Express_Buses', 'kwargs':{'PHASE':"'Phase2_2Routes'"}}, + {'name':'MAJ_SOL070020_I80_I680_SR12_Int_2B_7', 'kwargs':{'PHASE':"'3'"}, 'variants_exclude':['Alt1']}, + {'name':'MAJ_SOL070020_I80_I680_SR12_Int_2B_7', 'kwargs':{'PHASE':"'4'"}, 'variants_exclude':['Alt1']}, + {'name':'FBP_SF_012_Geneva_Harney_BRT', 'kwargs':{'MODELYEAR':'2035'}}, + {'name':'MAJ_SOL070020_I80_I680_SR12_Int_2B_7', 'kwargs':{'PHASE':"'5'"}, 'variants_exclude':['Alt1']}, + {'name':'FBP_SL_026_SolExpressBus', 'kwargs':{'MODELYEAR':'2035'}}, + 'FBP_SL_020_MilitaryWest_Diet', + {'name':'EIR1_Freq_Boosts', 'kwargs':{'MODELYEAR':'2035'}, 'variants_include':['Alt1']}, + {'name':'EIR2_VTA_LRT_Orange', 'variants_include':['Alt2']}, + {'name':'EIR2_Fix_Alt2', 'kwargs':{'MODELYEAR':'2035'}, 'variants_include':['Alt2']}, + {'name':'EIR1_No_SR37', 'variants_include':['Alt1']}] + }), + (2040, {'hwy':['BP_Vision_Zero', + 'FBP_SC_050_I680_Montague_Int_Imp', + 'FBP_MU_029_ACRapid_2040', + 'FBP_NP_074_SoscolWide', + 'FBP_CC_059_PittAntiochWide', + {'name':'FBP_CC_051_SR4_Operation_Improvements_WB', 'variants_exclude':['Alt1']}, + 'FBP_CC_037_680_AuxLanes', + 'RRSP_EC_Cap_Imp_ECR_Bus', + {'name':'MAJ_SR_239', 'variants_exclude':['Alt1']}, + 'FBP_NP_033_Napa_PNR_Lots', + 'FBP_CC_018_BRT_Brentwood', + 'FBP_SC_043_I280_Mainline_Impr', + 'MAJ_ElCaminoReal_BRT', + 'FBP_AL_042_I680_Stoneridge_Widening', + {'name':'FBP_CC_040_041_042_I680_SR4_Int_Phases_1_2_4_5', 'kwargs':{'PHASE':"'5'"},'variants_exclude':['Alt1']}, + {'name':'EXP_uncommitted_all', 'kwargs':{'MODELYEAR':'2040'}, 'variants_exclude':['Alt1', 'NextGenFwy']}, + {'name':'EIR1_EXP_uncommitted_all', 'kwargs':{'MODELYEAR':'2040'}, 'variants_include':['Alt1']}, + {'name':'FBP_SF_012_Geneva_Harney_BRT', 'kwargs':{'MODELYEAR':'2040'}}, + {'name':'MAJ_Bay_Area_Forward_all', 'kwargs':{'MODELYEAR':'2040'}}, + 'FBP_SC_105_SanTomasWide', + 'FBP_SC_102_CalaverasWide', + 'FBP_CC_039_Eastbound24Wide', + {'name':'FBP_MU_064_SR37_LongTerm', 'variants_exclude':['Alt1']}, + 'FBP_SC_094_LawrenceWide', + 'FBP_NP_066_Newell_Dr', + 'EXP_Blueprint', + 'FBP_AL_052_AutoMallWide', + 'FBP_CC_038_SR242_Clayton_OnOffRamps', + 'FBP_SC_047_I280_Winchester_OffRamp', + 'FBP_SC_076_US101_Taylor_Interchange', + 'FBP_NP_051_Airport_Junction', + 'FBP_SC_101_BrokawBridgeWide', + 'FBP_SC_081_US101_SR237', + 'FBP_SC_088_Envision_Expwy', + 'FBP_SC_039_SR237WBWide', + {'name':'FBP_MR_021_101_580_Direct_Connector', 'variants_exclude':['Alt1']}, + {'name':'FBP_MU_056_Dumbarton_GRT', 'variants_exclude':['Alt2']}, + {'name':'EIR2_Val_Link_ExpressBus', 'kwargs':{'action':"'revert'"}, 'variants_include':['Alt2']}, + {'name':'Transform_Valley_Link', 'variants_include':['Alt2']}, + {'name':'FBP_AL_021_South_Bay_Connect', 'variants_include':['Alt2']}, + {'name':'Transform_AC_Transbay_Improvements', 'variants_include':['Alt2']}, + 'FBP_SC_042_I280_Downtown_Access_Improvements', + {'name':'EXP_uncommitted_noAllLaneTolling', 'kwargs':{'MODELYEAR':'2040'}, 'variants_include':['NextGenFwy']}], + 'trn':[{'name':'FBP_MU_046_ACE_Freq_Inc', 'kwargs':{'MODELYEAR':'2040'}}, + 'MAJ_Vasona_LRT_Extension', + 'FBP_MU_029_ACRapid_2040', + 'RRSP_EC_Cap_Imp_ECR_Bus', + 'MAJ_SJC_People_Mover', + 'FBP_NP_028_NapaVineRegRoutesFrequency', + 'FBP_NP_034_NapaVineRegExpServiceHrs', + 'FBP_NP_029_NapaVineLocExpServiceHrs', + 'FBP_NP_033_Napa_PNR_Lots', + 'FBP_SC_043_I280_Mainline_Impr', + 'FBP_CC_018_BRT_Brentwood', + {'name':'FBP_SF_012_Geneva_Harney_BRT', 'kwargs':{'MODELYEAR':'2040'}}, + 'MAJ_ElCaminoReal_BRT', + {'name':'FBP_MU_064_SR37_LongTerm', 'variants_exclude':['Alt1']}, + 'FBP_NP_051_Airport_Junction', + {'name':'FBP_MU_056_Dumbarton_GRT', 'variants_exclude':['Alt2']}, + 'FBP_SC_088_Envision_Expwy', + {'name':'HSR', 'variants_exclude':['Alt2']}, + {'name':'MAJ_SF_050002_Caltrain_Ext_TransbayTerminal', 'variants_include':['Alt2']}, + {'name':'EIR2_Val_Link_ExpressBus', 'kwargs':{'action':"'revert'"}, 'variants_include':['Alt2']}, + {'name':'Transform_Valley_Link', 'variants_include':['Alt2']}, + {'name':'FBP_AL_021_South_Bay_Connect', 'variants_include':['Alt2']}, + {'name':'Transform_AC_Transbay_Improvements', 'variants_include':['Alt2']}, + {'name':'EIR2_ReXGreen', 'variants_include':['Alt2']}, + 'FBP_CC_019_CCCTA_Freq_Increase', + {'name':'EIR2_Fix_Alt2', 'kwargs':{'MODELYEAR':'2040'}, 'variants_include':['Alt2']}] + }), + (2045, {'hwy':['BP_Vision_Zero', + {'name':'EXP_uncommitted_all', 'kwargs':{'MODELYEAR':'2045'}, 'variants_exclude':['Alt1', 'NextGenFwy']}, + {'name':'EIR1_EXP_uncommitted_all', 'kwargs':{'MODELYEAR':'2045'}, 'variants_include':['Alt1']}, + {'name':'MAJ_Bay_Area_Forward_all', 'kwargs':{'MODELYEAR':'2045'}}, + {'name':'FBP_AL_048_SR262_Phase1', 'variants_exclude':['Alt1']}, + 'FBP_NP_045_SR29_Gateway_Impr', + 'EXP_Blueprint', + {'name':'EXP_uncommitted_noAllLaneTolling', 'kwargs':{'MODELYEAR':'2045'}, 'variants_include':['NextGenFwy']}], + 'trn':[{'name':'FBP_MU_046_ACE_Freq_Inc', 'kwargs':{'MODELYEAR':'2045'}}, + 'FBP_SC_106_VTA_LRT_Modernization'] + }), + (2050, {'hwy':['BP_Vision_Zero', + {'name':'MAJ_SOL070020_I80_I680_SR12_Int_2B_7', 'kwargs':{'PHASE':"'6'"}, 'variants_exclude':['Alt1']}, + {'name':'MAJ_SOL070020_I80_I680_SR12_Int_2B_7', 'kwargs':{'PHASE':"'7'"}, 'variants_exclude':['Alt1']}, + 'FBP_SC_028_Stevens_Creek_LRT', + {'name':'MAJ_Bay_Area_Forward_all', 'kwargs':{'MODELYEAR':'2050'}}, + 'EXP_Blueprint', + 'FBP_SC_041_Envision_Highway_Minor', + 'STIP_ITS_SM', + {'name':'BP_Transbay_Crossing', 'variants_exclude':['Alt2']}], + 'trn':[{'name':'MAJ_SOL070020_I80_I680_SR12_Int_2B_7', 'kwargs':{'PHASE':"'6'"}, 'variants_exclude':['Alt1']}, + {'name':'MAJ_SOL070020_I80_I680_SR12_Int_2B_7', 'kwargs':{'PHASE':"'7'"}, 'variants_exclude':['Alt1']}, + 'FBP_SC_028_Stevens_Creek_LRT', + {'name':'BP_Transbay_Crossing', 'variants_exclude':['Alt2']}] + }) + ]) + + + +########################################################### +# Put them together for NETWORK_PROJECTS +NETWORK_PROJECTS = collections.OrderedDict() + +for YEAR in COMMITTED_PROJECTS.keys(): + if SCENARIO == "NoProject": + # baseline: just committed + NETWORK_PROJECTS[YEAR] = { + 'hwy':COMMITTED_PROJECTS[YEAR]['hwy'], + 'trn':COMMITTED_PROJECTS[YEAR]['trn'] + } + + else: + # tip + NETWORK_PROJECTS[YEAR] = { + 'hwy':COMMITTED_PROJECTS[YEAR]['hwy'] + TIP_PROJECTS[YEAR]['hwy'], + 'trn':COMMITTED_PROJECTS[YEAR]['trn'] + TIP_PROJECTS[YEAR]['trn'] + } + # handle net_remove, nets keywords + for netmode in ['hwy','trn']: + + # iterate backwards via index to delete cleanly + for project_idx in range(len(NETWORK_PROJECTS[YEAR][netmode])-1,-1,-1): + project = NETWORK_PROJECTS[YEAR][netmode][project_idx] + # special handling requires project to be specified as dictionary + if not isinstance(project, dict): continue + + # variants_exclude: specifies list of network variants for which this project should be *excluded* + if 'variants_exclude' in project.keys() and NET_VARIANT in project['variants_exclude']: + Wrangler.WranglerLogger.info("Removing {} {} {}".format(YEAR, netmode, project)) + del NETWORK_PROJECTS[YEAR][netmode][project_idx] + continue + + # variants_include: specifies list of network variants for which this project should be *included* + # if this keyword is present, then this project is included *only* for variants in this list + if 'variants_include' in project.keys() and NET_VARIANT not in project['variants_include']: + Wrangler.WranglerLogger.info("Removing {} {} {}".format(YEAR, netmode, project)) + del NETWORK_PROJECTS[YEAR][netmode][project_idx] + continue + +# +# For every year where a project is applied do the following: +# Convert all zero-length links to 0.01 +# Move buses to HOV/Express lanes at the end +# +for YEAR in NETWORK_PROJECTS.keys(): + # if anything is applied + if ((len(NETWORK_PROJECTS[YEAR]['hwy']) > 0) or (len(NETWORK_PROJECTS[YEAR]['trn']) > 0)): + NETWORK_PROJECTS[YEAR]['hwy'].append('No_zero_length_links') + + if ((len(NETWORK_PROJECTS[YEAR]['hwy']) > 0) or (len(NETWORK_PROJECTS[YEAR]['trn']) > 0)): + NETWORK_PROJECTS[YEAR]['trn'].append('Move_buses_to_HOV_EXP_lanes') + +# OPTIONAL. The default route network project directory is Y:\networks. If +# projects are stored in another directory, then use this variable to specify it. +# For example: Y:\networks\projects +# NETWORK_BASE_DIR = None +# NETWORK_PROJECT_SUBDIR = None +# NETWORK_SEED_SUBDIR = None +# NETWORK_PLAN_SUBDIR = None + +# OPTIONAL. A list of project names which have been previously applied in the +# PIVOT_DIR network that projects in this project might rely on. For example +# if DoyleDrive exists, then Muni_TEP gets applied differently so transit lines +# run on the new Doyle Drive alignment +APPLIED_PROJECTS = None + +# OPTIONAL. A list of project names. For test mode, these projects won't use +# the TAG. This is meant for developing a network project. +TEST_PROJECTS = [] diff --git a/scripts/net_spec_blueprint.py b/scripts/net_spec_blueprint.py new file mode 100644 index 0000000..8e19ac6 --- /dev/null +++ b/scripts/net_spec_blueprint.py @@ -0,0 +1,530 @@ +import os +# MANDATORY. Set this to be the Project Name. +# e.g. "RTP2021", "TIP2021", etc +PROJECT = "Blueprint" + +# MANDATORY. Set this to be the git tag for checking out network projects. +#TAG = "HEAD" # Use this tag if you want NetworkWrangler to use the latest version in the local repo to build the network +TAG = "PBA50_Blueprint" # This is the default tag since this is the netspec for the Blueprint +# For NGF variants, the default TAG will be replaced by the alternative TAG "NGF_NoProject" (this is handled with code in build_network_mtc_blueprint.py) + +# A Alamedaproject can either be a simple string, or it can be +# a dictionary with with keys 'name', 'tag' (optional), and 'kwargs' (optional) +# to specify a special tag or special keyword args for the projects apply() call. +# For example: +# {'name':"Muni_TEP", 'kwargs':{'servicePlan':"'2012oct'"}} +########################################################### +COMMITTED_PROJECTS = collections.OrderedDict([ + (2015, { + 'hwy':['PROJ_attributes', # adds PROJ attributes to NODE and LINK + {'name':'Bridge_Toll_Updates_2_2pct', 'kwargs':{'MODELYEAR':'2015'}}], + 'trn':[] + }), + (2020, { + 'hwy':[{'name':'Bridge_Toll_Updates_2_2pct', 'kwargs':{'MODELYEAR':'2020'}}, + {'name':'EXP_237B', 'kwargs':{'FUTURE':"PBA50"}}, # todo: update this to support PBA50 + 'EXP_580C', + 'EXP_680D', + 'EXP_880A', + 'HOV_680F', + 'SCL130001_237_101_MAT_Int_Mod', + 'REG090003_SCLARA_FIP', + 'ALA130005_Dougherty_road_widening', + 'ALA130006_Dublin_Blvd_widening', + 'ALA130014_7th_St_road_diet', + 'ALA130026_Shattuck_Complete_Streets', + 'ALA170049_Central_AVE_Safety_Improvements', + 'ALA150004_EastBay_BRT', + 'CC_130001_BaileyRd_SR4', + 'CC_130046_I680_SR4_Int_Rec', + 'CC_070035_I80_SPDamRd_Int_Phase1', + 'CC_070011_Brentwood_Blvd_Widening', + 'CC_070075_Kirker_Pass_Truck_Lane', + 'CC_090019_Bollinger_Canyon_Widening', + 'CC_130006_Concord_BART_road_diets', + 'CC_170001_SanRamonValleyBlvd_Lane_Addition', + 'MRN150009_San_Rafael_Bridge_Improvements', + 'SF_070027_Yerba_Buena_Ramp_Imp', + 'SF_070005_VanNess_BRT', + 'SF_130011_2ndSt_Road_Diet', + 'SF_Market_Street_Closure', + 'SM_110047_SR92_ElCam_Ramp_Mod', + 'SOL110005_Jepson_Van_to_Com', + 'FBP_SL_042_Jepson_2A', + 'SON070004_101_MarinSonNarrows_Phase1', + 'ALA050014_SR84_Widening', + 'ALA170011_BayBridge_HOV_Connectors', + 'ALA150047_TelegraphAve_Complete_Streets', + 'SM_110047_SR92_ElCam_Ramp_Mod', + 'SCL190002_280_Foothill_improvement', + 'SCL190006_101SB_offramp_improvement', + 'I80_AdaptiveRampMetering', + 'VAR170021_Freeway_Performance_I880', + 'SonomaCounty_Transit_NoBuild2050', + 'SF_MuniForward_Committed', + 'FBP_MU_029_Broadway_Transit_Only_Lanes', + 'EXP_Blueprint_NoProject', + 'FBP_AL_067_Rte84Wide', + 'FBP_AL_065_Bancroft_Bus_Only', + 'FBP_SM_032_US101_Willow_Interchange'], + 'trn':['ALA050015_BART_to_WarmSprings', + 'ACGo', + 'CC_050025_EBart_to_Antioch', + 'GGTransit_Committed', + 'SCL110005_BART_to_Berryessa', + 'SF_010015_Transbay_Terminal', + 'SF_010037_Muni_Central_Subway', + 'SF_070027_Yerba_Buena_Ramp_Imp', + 'SOL030002_FairfieldVacaville_Stn', + 'SON090002_SMART', + 'SON090002_SMART_to_Larkspur', + 'CC_070062_Richmond_Ferry', + 'SF_MuniForward_Committed', + {'name':'VTA_Next', 'kwargs':{'MODELYEAR':'2020'}, 'variants_exclude':['NGFNoProject', 'NGFNoProjectNoSFCordon']}, + 'SCL130001_237_101_MAT_Int_Mod', + 'SonomaCounty_Transit_NoBuild2050', + 'SMART_Novato', + 'Xfare_update_2020', + 'ACTransit_Committed', + 'ferry_update_2019', + 'Napa_Solano_Updates_2020', + 'FBP_Beale_Transit_Only_Lane', + 'SamTrans_ECR_Rapid', + 'ALA150004_EastBay_BRT', + {'name':'FBP_SL_026_SolExpressBus', 'kwargs':{'MODELYEAR':'2020'}, 'variants_exclude':['NGFNoProject', 'NGFNoProjectNoSFCordon']}], + }), + (2025, { + 'hwy':[{'name':'Bridge_Toll_Updates_2_2pct', 'kwargs':{'MODELYEAR':'2025'}}, + 'EXP_CC_050028_I680_SB_HOV_Completion', + 'EXP_101B1', + 'EXP_101B2', + 'EXP_680C1', + 'EXP_680F', + 'EXP_85D', + 'EXP_101C', + 'ALA150001_I680_SR84_Int_Wid', + 'ALA150043_Claremont_road_diet', + 'CC_070009_Slatten_Ranch_Rd_Extension', + 'SF_070004_Geary_BRT_Phase1', + 'SON070004_101_MarinSonNarrows_Phase2', + 'SOL110006_Jepson_1B_1C', + 'SCL190008_US101_DLC_Int_Imp', + 'CC_170061_Bus_On_Shoulder_680BRT', + 'I880_US101_AdaptiveRampMetering', + 'MAJ_SCL050009_VTA_Eastridge_Extension', + 'SOL070020_I80_I680_SR12_Int_1_2A', + 'FBP_NP_036_SR29_Imola_PNR', + 'ALA170052_Fruitvale_Ave_ped_improvements', + 'EXP_Blueprint_NoProject'], + 'trn':['SF_010028_Caltrain_Modernization', + 'SON090002_SMART_to_Windsor', + 'MAJ_SCL050009_VTA_Eastridge_Extension', + 'REG090037_New_BART_Trains', + 'FBP_NP_036_SR29_Imola_PNR', + 'SOL070020_I80_I680_SR12_Int_1_2A'] + }), + (2030, { + 'hwy':[{'name':'Bridge_Toll_Updates_2_2pct', 'kwargs':{'MODELYEAR':'2030'}}, + 'EXP_Blueprint_NoProject'], + 'trn':['BART_NoProject'] + }), + (2035, { + 'hwy':[{'name':'Bridge_Toll_Updates_2_2pct', 'kwargs':{'MODELYEAR':'2035'}}, + 'EXP_Blueprint_NoProject'], + 'trn':[] + }), + (2040, { + 'hwy':[{'name':'Bridge_Toll_Updates_2_2pct', 'kwargs':{'MODELYEAR':'2040'}}, + 'EXP_Blueprint_NoProject'], + 'trn':[] + }), + (2045, { + 'hwy':[{'name':'Bridge_Toll_Updates_2_2pct', 'kwargs':{'MODELYEAR':'2045'}}, + 'EXP_Blueprint_NoProject'], + 'trn':[] + }), + (2050, { + 'hwy':[{'name':'Bridge_Toll_Updates_2_2pct', 'kwargs':{'MODELYEAR':'2050'}}, + 'EXP_Blueprint_NoProject'], + 'trn':[] + }) +]) + +########################################################### +# Blueprint projects +BLUEPRINT_PROJECTS = collections.OrderedDict([ + (2015, {'hwy':[], + 'trn':[] + }), + (2020, {'hwy':[], + 'trn':[] + }), + (2025, {'hwy':['RRSP_Alameda_Point_Transit_Improvements', + 'MAJ_MTC050027_Berkeley_Ferry', + 'MAJ_WETA_Service_Frequency_Increase', + {'name':'Transform_SR37_Widening_Interim', 'variants_exclude':['Alt1']}, + {'name':'MAJ_SF_Congestion_Pricing', 'variants_exclude':['NGFNoProjectNoSFCordon']}, + 'MAJ_Geary_BRT_Phase2', + 'FBP_MU_041_Hovercraft_Pilot', + {'name':'BP_Vision_Zero', 'variants_exclude':['NGFNoProject', 'NGFNoProjectNoSFCordon']}, + 'EXP_Blueprint', + {'name':'MAJ_AC_Frequency_Improvement', 'variants_exclude':['NGFNoProject', 'NGFNoProjectNoSFCordon']}, + 'MRN050034_101_MarinSonNarrows_Phase2', + 'FBP_MU_044_SouthSF_Ferry_Serv_Incr', + 'FBP_MU_029_ACRapid_2025', + 'RRSP_E14_Mission_Corridor', + 'FBP_MR_026_NovatoWide', + 'FBP_CC_054_CrowCanyonWide', + 'FBP_NP_038_TSP_On_SR29', + {'name':'FBP_CC_050_SR4_Operation_Improvements_EB', 'variants_exclude':['Alt1']}, + 'FBP_NP_044_Soscol_Junction', + 'FBP_SL_033_FairgroundsWide', + {'name':'MAJ_SOL070020_I80_I680_SR12_Int_2B_7','kwargs':{'PHASE':"'2B'"}}, + {'name':'FBP_CC_040_041_042_I680_SR4_Int_Phases_1_2_4_5', 'kwargs':{'PHASE':"'1'"}, 'variants_exclude':['Alt1']}, + {'name':'FBP_CC_040_041_042_I680_SR4_Int_Phases_1_2_4_5', 'kwargs':{'PHASE':"'2'"}, 'variants_exclude':['Alt1']}, + {'name':'EXP_uncommitted_all', 'kwargs':{'MODELYEAR':'2025'}, 'variants_exclude':['Alt1', 'NGFNoProject', 'NGFNoProjectNoSFCordon']}, + {'name':'EXP_uncommitted_noAllLaneTolling', 'kwargs':{'MODELYEAR':'2025'}, 'variants_include':['NGFNoProject', 'NGFNoProjectNoSFCordon']}, + {'name':'EIR1_EXP_uncommitted_all', 'kwargs':{'MODELYEAR':'2025'}, 'variants_include':['Alt1']}, + {'name':'FBP_SF_012_Geneva_Harney_BRT', 'kwargs':{'MODELYEAR':'2025'}}, + {'name':'FBP_CC_15_23rd_St_BRT', 'kwargs':{'MODELYEAR':'2025'}}, + 'FBP_SC_103_MontagueWide', + {'name':'MAJ_Bay_Area_Forward_all', 'kwargs':{'MODELYEAR':'2025'}}, + 'FBP_CC_057_LoneTreeWide', + 'FBP_CC_063_BrentwoodWide', + 'FBP_CC_067_WillowPassWide', + 'FBP_CC_065_LaurelWide', + 'FBP_AL_062_TassajaraWide', + 'FBP_SC_039_SR237WBWide', + 'FBP_AL_051_7St_Grade_Sep_West', + 'FBP_AL_044_I880_Whipple_Imps', + 'FBP_AL_055_DubBlvd_NCanyons_Ext', + 'FBP_SN_017_Arata_Int', + 'FBP_CC_017_Brentwood_Intermodal', + 'FBP_SF_030_Balboa_Park_Area_2', + 'EXP_Blueprint', + 'FBP_AL_039_I580_Interchange_Imps', + 'FBP_CC_056_LaurelExtension', + 'FBP_SC_084_10th_BridgeWide', + 'FBP_SL_053_PeabodyWide', + 'FBP_SC_073_BlossomHill_101Wide', + 'FBP_SC_082_US101_25_Interchange', + 'FBP_SM_035_Peninsula_101_OnOffRamps', + 'FBP_CC_045_SanPabloDam_Interchange_Phase2', + 'FBP_CC_030_OakleyAmtrak', + 'STIP_ProduceAve', + 'FBP_SM_033_US101_Holly_Interchange', + 'FBP_SM_034_Route92_ElCamino_Interchange', + 'FBP_SL_019_BeniciaRoad_Diet', + 'FBP_SL_023_WestTexasRoad_Diet', + 'FBP_SN_012_PetalumaBlvd_Diet', + 'MAJ_MissionBay_SF_Ferry', + {'name':'EIR2_Val_Link_ExpressBus', 'variants_include':['Alt2']}, + {'name':'EIR2_ReXBlue', 'variants_include':['Alt2']}, + 'FBP_SC_072_US101_Trimble_Interchange'], + 'trn':['MAJ_Geary_BRT_Phase2', + 'FBP_AL_001_NewarkFremPDA', + {'name':'FBP_MU_059_ACTransbay_Freq_Incr', 'variants_exclude':['Alt2']}, + {'name':'MAJ_AC_Frequency_Improvement', 'variants_exclude':['NGFNoProject', 'NGFNoProjectNoSFCordon']}, + 'RRSP_Alameda_Point_Transit_Improvements', + 'MAJ_MTC050027_Berkeley_Ferry', + 'MAJ_WETA_Service_Frequency_Increase', + {'name':'FBP_MU_046_ACE_Freq_Inc', 'kwargs':{'MODELYEAR':'2025'}}, + {'name':'Transform_SR37_Widening_Interim', 'variants_exclude':['Alt1']}, + {'name':'MAJ_SF_Congestion_Pricing', 'variants_include':['NGFNoProjectNoSFCordon']}, + 'FBP_MU_041_Hovercraft_Pilot', + 'FBP_MU_049_Caltrain_6TPHPD', + {'name':'FBP_MU_060_ReX_Blue', 'variants_exclude':['NGFNoProject', 'NGFNoProjectNoSFCordon']}, + {'name':'EIR2_ReXBlue', 'variants_include':['Alt2']}, + 'FBP_MU_044_SouthSF_Ferry_Serv_Incr', + 'GGT_Service_Imp', + 'FBP_MU_029_ACRapid_2025', + 'RRSP_E14_Mission_Corridor', + 'FBP_NP_044_Soscol_Junction', + 'MAJ_RedwoodCity_SF_Ferry', + 'MAJ_Alameda_Point_SF_Ferry', + {'name':'MAJ_SOL070020_I80_I680_SR12_Int_2B_7','kwargs':{'PHASE':"'2B'"}}, + {'name':'FBP_SF_012_Geneva_Harney_BRT', 'kwargs':{'MODELYEAR':'2025'}}, + {'name':'FBP_CC_15_23rd_St_BRT', 'kwargs':{'MODELYEAR':'2025'}}, + 'FBP_CC_030_OakleyAmtrak', + 'FBP_SM_020_Regional_Express_Buses', + 'MAJ_MissionBay_SF_Ferry', + 'MAJ_Sonoma_Frequency_Increase', + {'name':'EIR1_Freq_Boosts', 'kwargs':{'MODELYEAR':'2025'}, 'variants_include':['Alt1']}, + {'name':'EIR2_HRA_Freq_Incr', 'kwargs':{'MODELYEAR':'2025'}, 'variants_include':['Alt2']}, + {'name':'EIR2_PDA_Freq_Incr', 'kwargs':{'MODELYEAR':'2025'}, 'variants_include':['Alt2']}, + {'name':'EIR2_Val_Link_ExpressBus', 'variants_include':['Alt2']}, + {'name':'SON090002_SMART_NorthPetaluma', 'variants_exclude':['Baseline']}] + }), + (2030, {'hwy':['MAJ_SanPablo_BRT', + {'name':'BP_Tolls_On_Congested_Freeways_2030', 'variants_exclude':['NGFNoProject', 'NGFNoProjectNoSFCordon']}, + {'name':'BP_Vision_Zero', 'variants_exclude':['NGFNoProject', 'NGFNoProjectNoSFCordon']}, + {'name':'FBP_AL_021_South_Bay_Connect', 'variants_exclude':['Alt2']}, + 'FBP_MU_044_Richmond_Ferry_Serv_Incr', + 'MAJ_REG090037_BART_Core_Cap', + {'name':'Transform_Valley_Link', 'variants_exclude':['Alt2']}, + {'name':'FBP_NP_040_VINE_Exp_Bus_Enhancements', 'variants_exclude':['NGFNoProject', 'NGFNoProjectNoSFCordon']}, + 'FBP_AL_045_Oak_Ala_Access_Pr', + {'name':'FBP_MR_021_101_580_Direct_Connector', 'variants_exclude':['Alt1']}, + {'name':'FBP_MR_018_US101_BOS', 'variants_exclude':['NGFNoProject', 'NGFNoProjectNoSFCordon']}, + {'name':'FBP_CC_036_I80_ExpBus_Impr', 'variants_exclude':['NGFNoProject', 'NGFNoProjectNoSFCordon']}, + {'name':'FBP_CC_040_041_042_I680_SR4_Int_Phases_1_2_4_5','kwargs':{'PHASE':"'4'"}, 'variants_exclude':['Alt1']}, + 'FBP_CC_021_Ant_Mart_Herc_Ferry', + {'name':'EXP_uncommitted_all', 'kwargs':{'MODELYEAR':'2030'}, 'variants_exclude':['Alt1', 'NGFNoProject', 'NGFNoProjectNoSFCordon']}, + {'name':'EXP_uncommitted_noAllLaneTolling', 'kwargs':{'MODELYEAR':'2030'}, 'variants_include':['NGFNoProject', 'NGFNoProjectNoSFCordon']}, + {'name':'EIR1_EXP_uncommitted_all', 'kwargs':{'MODELYEAR':'2030'}, 'variants_include':['Alt1']}, + 'FBP_SC_104_OaklandWide', + {'name':'FBP_CC_15_23rd_St_BRT', 'kwargs':{'MODELYEAR':'2030'}}, + {'name':'FBP_SF_012_Geneva_Harney_BRT', 'kwargs':{'MODELYEAR':'2030'}}, + {'name':'MAJ_Bay_Area_Forward_all', 'kwargs':{'MODELYEAR':'2030'}}, + 'FBP_CC_064_CaminoTassajaraWide', + 'FBP_CC_066_CypressWide', + 'FBP_SC_059_SR237EBWide', + 'FBP_AL_064_UnionCityWide', + 'FBP_SC_074_US101_BuenaVista_Int', + 'EXP_Blueprint', + 'FBP_SC_054_SR17_Corridor_Relief', + 'FBP_AL_043_A_StreetWide', + 'FBP_CC_061_062_West_Leland_Ext_Phases1_2', + 'FBP_SM_042_Hwy1_ManorDrive', + 'FBP_SL_042_Jepson_2B_2C', + 'FBP_CC_024_Oakley_PNR_Tri_Delta', + 'FBP_SC_083_US101_Zanker_Skyport_Interchange', + 'FBP_SL_022_SonomaBlvd_Diet', + 'FBP_SM_027_US101_92', + 'FBP_SM_007_ElCamino_CompleteStreets'], + 'trn':['BP_PDA_Transit_Enhancements', + {'name':'FBP_MU_046_ACE_Freq_Inc', 'kwargs':{'MODELYEAR':'2030'}}, + 'MAJ_BRT030001_BART_to_SanJose', + 'BART_Irvington_Infill', + 'MAJ_REG090037_BART_Core_Cap', + {'name':'FBP_AL_021_South_Bay_Connect', 'variants_exclude':['Alt2']}, + 'FBP_MU_049_Caltrain_8TPHPD', + {'name':'FBP_MU_061_ReX_Green', 'variants_exclude':['NGFNoProject', 'NGFNoProjectNoSFCordon']}, + 'MAJ_SanPablo_BRT', + 'FBP_MU_044_Richmond_Ferry_Serv_Incr', + {'name':'Transform_Valley_Link', 'variants_exclude':['Alt2']}, + 'FBP_SF_028_SF_Express_Bus_On_Exp_Lanes', + {'name':'MAJ_SF_050002_Caltrain_Ext_TransbayTerminal', 'variants_exclude':['Alt2']}, + 'FBP_SF_024_Historic_Streetcar_Ext', + {'name':'FBP_MuniForward_Uncommitted_Rail', 'variants_exclude':['NGFNoProject', 'NGFNoProjectNoSFCordon']}, + {'name':'FBP_CC_036_I80_ExpBus_Impr', 'variants_exclude':['NGFNoProject', 'NGFNoProjectNoSFCordon']}, + 'FBP_CC_021_Ant_Mart_Herc_Ferry', + 'FBP_AL_045_Oak_Ala_Access_Pr', + 'FBP_CC_028_Hercules_Station', + {'name':'FBP_SF_012_Geneva_Harney_BRT', 'kwargs':{'MODELYEAR':'2030'}}, + {'name':'FBP_CC_15_23rd_St_BRT', 'kwargs':{'MODELYEAR':'2030'}}, + 'FBP_CC_024_Oakley_PNR_Tri_Delta', + {'name':'EIR1_Freq_Boosts', 'kwargs':{'MODELYEAR':'2030'}, 'variants_include':['Alt1']}, + {'name':'EIR2_HRA_Freq_Incr', 'kwargs':{'MODELYEAR':'2030'}, 'variants_include':['Alt2']}, + {'name':'EIR2_PDA_Freq_Incr', 'kwargs':{'MODELYEAR':'2030'}, 'variants_include':['Alt2']}, + {'name':'EIR2_Fix_Alt2', 'kwargs':{'MODELYEAR':'2030'}, 'variants_include':['Alt2']}] + }), + (2035, {'hwy':[{'name':'MAJ_MuniForward_Uncommitted', 'variants_exclude':['NGFNoProject', 'NGFNoProjectNoSFCordon']}, + 'MAJ_Treasure_Island_Congestion_Pricing', + {'name':'BP_Tolls_On_Congested_Freeways_2035', 'variants_exclude':['NGFNoProject', 'NGFNoProjectNoSFCordon']}, + {'name':'BP_Vision_Zero', 'variants_exclude':['NGFNoProject', 'NGFNoProjectNoSFCordon']}, + 'RRSP_East_West_Connector', + {'name':'Transform_I680_Multimodal_Imp', 'variants_exclude':['NGFNoProject', 'NGFNoProjectNoSFCordon']}, + 'FBP_SM_022_I380_Widening', + {'name':'MAJ_SOL070020_I80_I680_SR12_Int_2B_7', 'kwargs':{'PHASE':"'3'"}, 'variants_exclude':['Alt1']}, + {'name':'MAJ_SOL070020_I80_I680_SR12_Int_2B_7', 'kwargs':{'PHASE':"'4'"}, 'variants_exclude':['Alt1']}, + {'name':'MAJ_SOL070020_I80_I680_SR12_Int_2B_7', 'kwargs':{'PHASE':"'5'"}, 'variants_exclude':['Alt1']}, + {'name':'FBP_SF_012_Geneva_Harney_BRT', 'kwargs':{'MODELYEAR':'2035'}}, + {'name':'EXP_uncommitted_all', 'kwargs':{'MODELYEAR':'2035'}, 'variants_exclude':['Alt1', 'NGFNoProject', 'NGFNoProjectNoSFCordon']}, + {'name':'EXP_uncommitted_noAllLaneTolling', 'kwargs':{'MODELYEAR':'2035'}, 'variants_include':['NGFNoProject', 'NGFNoProjectNoSFCordon']}, + {'name':'EIR1_EXP_uncommitted_all', 'kwargs':{'MODELYEAR':'2035'}, 'variants_include':['Alt1']}, + {'name':'MAJ_Bay_Area_Forward_all', 'kwargs':{'MODELYEAR':'2035'}}, + 'FBP_AL_076_TelegraphDiet', + 'FBP_SN_018_Cotati_101_RailroadAve_Impr', + 'FBP_NP_079_Trower_Ext', + 'EXP_Blueprint', + {'name':'EIR1_No_SR37', 'variants_include':['Alt1']}, + {'name':'NGF_NoProject_tollscsv', 'variants_include':['NGFNoProject', 'NGFNoProjectNoSFCordon']}], + 'trn':[{'name':'MAJ_MuniForward_Uncommitted', 'variants_exclude':['NGFNoProject', 'NGFNoProjectNoSFCordon']}, + 'RRSP_South_East_Waterfront_Transit_Imp', + {'name':'FBP_MU_062_ReX_Red', 'variants_exclude':['NGFNoProject', 'NGFNoProjectNoSFCordon']}, + {'name':'Transform_I680_Multimodal_Imp', 'variants_exclude':['NGFNoProject', 'NGFNoProjectNoSFCordon']}, + 'Transform_SeamlessTransit', + 'MAJ_Treasure_Island_Congestion_Pricing', + 'RRSP_East_West_Connector', + 'MAJ_Treasure_Island_Ferry', + 'FBP_NP_079_Trower_Ext', + {'name':'MAJ_SOL070020_I80_I680_SR12_Int_2B_7', 'kwargs':{'PHASE':"'3'"}, 'variants_exclude':['Alt1']}, + {'name':'MAJ_SOL070020_I80_I680_SR12_Int_2B_7', 'kwargs':{'PHASE':"'4'"}, 'variants_exclude':['Alt1']}, + {'name':'FBP_SF_012_Geneva_Harney_BRT', 'kwargs':{'MODELYEAR':'2035'}}, + {'name':'MAJ_SOL070020_I80_I680_SR12_Int_2B_7', 'kwargs':{'PHASE':"'5'"}, 'variants_exclude':['Alt1']}, + {'name':'FBP_SL_026_SolExpressBus', 'kwargs':{'MODELYEAR':'2035'}, 'variants_exclude':['NGFNoProject', 'NGFNoProjectNoSFCordon']}, + 'FBP_SL_020_MilitaryWest_Diet', + {'name':'EIR1_Freq_Boosts', 'kwargs':{'MODELYEAR':'2035'}, 'variants_include':['Alt1']}, + {'name':'EIR2_VTA_LRT_Orange', 'variants_include':['Alt2']}, + {'name':'EIR2_Fix_Alt2', 'kwargs':{'MODELYEAR':'2035'}, 'variants_include':['Alt2']}, + {'name':'EIR1_No_SR37', 'variants_include':['Alt1']}] + }), + (2040, {'hwy':[{'name':'BP_Vision_Zero', 'variants_exclude':['NGFNoProject', 'NGFNoProjectNoSFCordon']}, + 'FBP_SC_050_I680_Montague_Int_Imp', + 'FBP_MU_029_ACRapid_2040', + 'FBP_NP_074_SoscolWide', + 'FBP_CC_059_PittAntiochWide', + {'name':'FBP_CC_051_SR4_Operation_Improvements_WB', 'variants_exclude':['Alt1']}, + 'FBP_CC_037_680_AuxLanes', + 'RRSP_EC_Cap_Imp_ECR_Bus', + {'name':'MAJ_SR_239', 'variants_exclude':['Alt1']}, + 'FBP_NP_033_Napa_PNR_Lots', + 'FBP_CC_018_BRT_Brentwood', + 'FBP_SC_043_I280_Mainline_Impr', + 'MAJ_ElCaminoReal_BRT', + 'FBP_AL_042_I680_Stoneridge_Widening', + {'name':'FBP_CC_040_041_042_I680_SR4_Int_Phases_1_2_4_5', 'kwargs':{'PHASE':"'5'"}, 'variants_exclude':['Alt1']}, + {'name':'EXP_uncommitted_all', 'kwargs':{'MODELYEAR':'2040'}, 'variants_exclude':['Alt1', 'NGFNoProject', 'NGFNoProjectNoSFCordon']}, + {'name':'EXP_uncommitted_noAllLaneTolling', 'kwargs':{'MODELYEAR':'2040'}, 'variants_include':['NGFNoProject', 'NGFNoProjectNoSFCordon']}, + {'name':'EIR1_EXP_uncommitted_all', 'kwargs':{'MODELYEAR':'2040'}, 'variants_include':['Alt1']}, + {'name':'FBP_SF_012_Geneva_Harney_BRT', 'kwargs':{'MODELYEAR':'2040'}}, + {'name':'MAJ_Bay_Area_Forward_all', 'kwargs':{'MODELYEAR':'2040'}}, + 'FBP_SC_105_SanTomasWide', + 'FBP_SC_102_CalaverasWide', + 'FBP_CC_039_Eastbound24Wide', + {'name':'FBP_MU_064_SR37_LongTerm', 'variants_exclude':['Alt1']}, + 'FBP_SC_094_LawrenceWide', + 'FBP_NP_066_Newell_Dr', + 'EXP_Blueprint', + 'FBP_AL_052_AutoMallWide', + 'FBP_CC_038_SR242_Clayton_OnOffRamps', + 'FBP_SC_047_I280_Winchester_OffRamp', + 'FBP_SC_076_US101_Taylor_Interchange', + 'FBP_NP_051_Airport_Junction', + 'FBP_SC_101_BrokawBridgeWide', + 'FBP_SC_081_US101_SR237', + 'FBP_SC_088_Envision_Expwy', + {'name':'FBP_MU_056_Dumbarton_GRT', 'variants_exclude':['Alt2']}, + {'name':'EIR2_Val_Link_ExpressBus', 'kwargs':{'action':"'revert'"}, 'variants_include':['Alt2']}, + {'name':'Transform_Valley_Link', 'variants_include':['Alt2']}, + {'name':'FBP_AL_021_South_Bay_Connect', 'variants_include':['Alt2']}, + {'name':'Transform_AC_Transbay_Improvements', 'variants_include':['Alt2']}, + 'FBP_SC_042_I280_Downtown_Access_Improvements'], + 'trn':[{'name':'FBP_MU_046_ACE_Freq_Inc', 'kwargs':{'MODELYEAR':'2040'}}, + 'MAJ_Vasona_LRT_Extension', + 'FBP_MU_029_ACRapid_2040', + 'RRSP_EC_Cap_Imp_ECR_Bus', + 'MAJ_SJC_People_Mover', + 'FBP_NP_028_NapaVineRegRoutesFrequency', + 'FBP_NP_034_NapaVineRegExpServiceHrs', + 'FBP_NP_029_NapaVineLocExpServiceHrs', + 'FBP_NP_033_Napa_PNR_Lots', + 'FBP_SC_043_I280_Mainline_Impr', + 'FBP_CC_018_BRT_Brentwood', + {'name':'FBP_SF_012_Geneva_Harney_BRT', 'kwargs':{'MODELYEAR':'2040'}}, + 'MAJ_ElCaminoReal_BRT', + {'name':'FBP_MU_064_SR37_LongTerm', 'variants_exclude':['Alt1']}, + 'FBP_NP_051_Airport_Junction', + {'name':'FBP_MU_056_Dumbarton_GRT', 'variants_exclude':['Alt2']}, + 'FBP_SC_088_Envision_Expwy', + {'name':'HSR', 'variants_exclude':['Alt2']}, + {'name':'MAJ_SF_050002_Caltrain_Ext_TransbayTerminal', 'variants_include':['Alt2']}, + {'name':'EIR2_Val_Link_ExpressBus', 'kwargs':{'action':"'revert'"}, 'variants_include':['Alt2']}, + {'name':'Transform_Valley_Link', 'variants_include':['Alt2']}, + {'name':'FBP_AL_021_South_Bay_Connect', 'variants_include':['Alt2']}, + {'name':'Transform_AC_Transbay_Improvements', 'variants_include':['Alt2']}, + {'name':'EIR2_ReXGreen', 'variants_include':['Alt2']}, + 'FBP_CC_019_CCCTA_Freq_Increase', + {'name':'EIR2_Fix_Alt2', 'kwargs':{'MODELYEAR':'2040'}, 'variants_include':['Alt2']}] + }), + (2045, {'hwy':[{'name':'BP_Vision_Zero', 'variants_exclude':['NGFNoProject', 'NGFNoProjectNoSFCordon']}, + {'name':'EXP_uncommitted_all', 'kwargs':{'MODELYEAR':'2045'}, 'variants_exclude':['Alt1', 'NGFNoProject', 'NGFNoProjectNoSFCordon']}, + {'name':'EXP_uncommitted_noAllLaneTolling', 'kwargs':{'MODELYEAR':'2045'}, 'variants_include':['NGFNoProject', 'NGFNoProjectNoSFCordon']}, + {'name':'EIR1_EXP_uncommitted_all', 'kwargs':{'MODELYEAR':'2045'}, 'variants_include':['Alt1']}, + {'name':'MAJ_Bay_Area_Forward_all', 'kwargs':{'MODELYEAR':'2045'}}, + {'name':'FBP_AL_048_SR262_Phase1', 'variants_exclude':['Alt1']}, + 'FBP_NP_045_SR29_Gateway_Impr', + 'EXP_Blueprint'], + 'trn':[{'name':'FBP_MU_046_ACE_Freq_Inc', 'kwargs':{'MODELYEAR':'2045'}}, + 'FBP_SC_106_VTA_LRT_Modernization'] + }), + (2050, {'hwy':[{'name':'BP_Vision_Zero', 'variants_exclude':['NGFNoProject', 'NGFNoProjectNoSFCordon']}, + {'name':'MAJ_SOL070020_I80_I680_SR12_Int_2B_7', 'kwargs':{'PHASE':"'6'"}, 'variants_exclude':['Alt1']}, + {'name':'MAJ_SOL070020_I80_I680_SR12_Int_2B_7', 'kwargs':{'PHASE':"'7'"}, 'variants_exclude':['Alt1']}, + 'FBP_SC_028_Stevens_Creek_LRT', + {'name':'MAJ_Bay_Area_Forward_all', 'kwargs':{'MODELYEAR':'2050'}}, + 'EXP_Blueprint', + 'FBP_SC_041_Envision_Highway_Minor', + 'STIP_ITS_SM', + {'name':'BP_Transbay_Crossing', 'variants_exclude':['Alt2']}], + 'trn':[{'name':'MAJ_SOL070020_I80_I680_SR12_Int_2B_7', 'kwargs':{'PHASE':"'6'"}, 'variants_exclude':['Alt1']}, + {'name':'MAJ_SOL070020_I80_I680_SR12_Int_2B_7', 'kwargs':{'PHASE':"'7'"}, 'variants_exclude':['Alt1']}, + 'FBP_SC_028_Stevens_Creek_LRT', + {'name':'BP_Transbay_Crossing', 'variants_exclude':['Alt2']}] + }) + ]) + + +# Put them together for NETWORK_PROJECTS +NETWORK_PROJECTS = collections.OrderedDict() + +for YEAR in COMMITTED_PROJECTS.keys(): + if NET_VARIANT == "Baseline": + # baseline: just committed + NETWORK_PROJECTS[YEAR] = { + 'hwy':COMMITTED_PROJECTS[YEAR]['hwy'], + 'trn':COMMITTED_PROJECTS[YEAR]['trn'] + } + # todo: add sea level rise since it's unprotected + + else: + # blueprint, alt1, alt2 + + NETWORK_PROJECTS[YEAR] = { + 'hwy':COMMITTED_PROJECTS[YEAR]['hwy'] + BLUEPRINT_PROJECTS[YEAR]['hwy'], + 'trn':COMMITTED_PROJECTS[YEAR]['trn'] + BLUEPRINT_PROJECTS[YEAR]['trn'] + } + # handle net_remove, nets keywords + for netmode in ['hwy','trn']: + + # iterate backwards via index to delete cleanly + for project_idx in range(len(NETWORK_PROJECTS[YEAR][netmode])-1,-1,-1): + project = NETWORK_PROJECTS[YEAR][netmode][project_idx] + # special handling requires project to be specified as dictionary + if not isinstance(project, dict): continue + + # variants_exclude: specifies list of network variants for which this project should be *excluded* + if 'variants_exclude' in project.keys() and NET_VARIANT in project['variants_exclude']: + Wrangler.WranglerLogger.info("Removing {} {} {}".format(YEAR, netmode, project)) + del NETWORK_PROJECTS[YEAR][netmode][project_idx] + continue + + # variants_include: specifies list of network variants for which this project should be *included* + # if this keyword is present, then this project is included *only* for variants in this list + if 'variants_include' in project.keys() and NET_VARIANT not in project['variants_include']: + Wrangler.WranglerLogger.info("Removing {} {} {}".format(YEAR, netmode, project)) + del NETWORK_PROJECTS[YEAR][netmode][project_idx] + continue + + # NOTE: SLR is handled in build_network_mtc_blueprint.py + +# +# For every year where a project is applied do the following: +# Convert all zero-length links to 0.01 +# Move buses to HOV/Express lanes at the end +# +for YEAR in NETWORK_PROJECTS.keys(): + # if anything is applied + if ((len(NETWORK_PROJECTS[YEAR]['hwy']) > 0) or (len(NETWORK_PROJECTS[YEAR]['trn']) > 0)): + NETWORK_PROJECTS[YEAR]['hwy'].append('No_zero_length_links') + + if ((len(NETWORK_PROJECTS[YEAR]['hwy']) > 0) or (len(NETWORK_PROJECTS[YEAR]['trn']) > 0)): + NETWORK_PROJECTS[YEAR]['trn'].append('Move_buses_to_HOV_EXP_lanes') + + +# OPTIONAL. The default route network project directory is Y:\networks. If +# projects are stored in another directory, then use this variable to specify it. +# For example: Y:\networks\projects +# NETWORK_BASE_DIR = None +# NETWORK_PROJECT_SUBDIR = None +# NETWORK_SEED_SUBDIR = None +# NETWORK_PLAN_SUBDIR = None +# OPTIONAL. A list of project names which have been previously applied in the +# PIVOT_DIR network that projects in this project might rely on. For example +# if DoyleDrive exists, then Muni_TEP gets applied differently so transit lines +# run on the new Doyle Drive alignment +APPLIED_PROJECTS = None +# OPTIONAL. A list of project names. For test mode, these projects won't use +# the TAG. This is meant for developing a network project. +TEST_PROJECTS = [] diff --git a/scripts/net_spec_test.py b/scripts/net_spec_test.py index 6e98421..6973f3b 100644 --- a/scripts/net_spec_test.py +++ b/scripts/net_spec_test.py @@ -24,31 +24,44 @@ NETWORK_PROJECTS = collections.OrderedDict([ (2015, {'hwy':[ - 'PROJ_attributes', # adds PROJ attributes to NODE and LINK - {'name':'Bridge_Toll_Updates_2_2pct', 'kwargs':{'MODELYEAR':'2015'}} - ], 'trn':[]}), + 'PROJ_attributes' # adds PROJ attributes to NODE and LINK + ], + 'trn':[] + }), (2020, { - 'hwy':[{'name':'Bridge_Toll_Updates_2_2pct', 'kwargs':{'MODELYEAR':'2020'}}, - ], - 'trn':[{'name':'demo_project', 'kwargs':{'FUTURE':'"BackToTheFuture"'}}] + 'hwy':[ + {'name':'demo_project', 'kwargs':{'TEST':'BUNNIES'}} + ], + 'trn':[ + {'name':'demo_project', 'kwargs':{'FUTURE':"'CleanAndGreen'"}} + ] }), (2025, { - 'hwy':[], 'trn':[] + 'hwy':[ + ], + 'trn':[ + {'name':'demo_project', 'kwargs':{'FUTURE':"'BackToTheFuture'"}} + ] }), (2030, { - 'hwy':[], 'trn':[] + 'hwy':[], + 'trn':[] }), (2035, { - 'hwy':[], 'trn':[] + 'hwy':[], + 'trn':[] }), (2040, { - 'hwy':[], 'trn':[] + 'hwy':[], + 'trn':[] }), (2045, { - 'hwy':[], 'trn':[] + 'hwy':[], + 'trn':[] }), (2050, { - 'hwy':[], 'trn':[] + 'hwy':[], + 'trn':[] }) ]) diff --git a/scripts/net_spec_transit_recovery.py b/scripts/net_spec_transit_recovery.py new file mode 100644 index 0000000..0487d5f --- /dev/null +++ b/scripts/net_spec_transit_recovery.py @@ -0,0 +1,142 @@ +import os +import collections +# MANDATORY. Set this to be the Project Name. +# e.g. "RTP2021", "TIP2021", etc +PROJECT = "TransitRecov" + +# MANDATORY. Set this to be the git tag for checking out network projects. +#TAG = "HEAD" # Use this tag if you want NetworkWrangler to use the latest version in the local repo to build the network +#TAG = "PBA50_Blueprint" # Use this tag if you want to replicate the network built for PBA50 +TAG = "HEAD" + +# A Alamedaproject can either be a simple string, or it can be +# a dictionary with with keys 'name', 'tag' (optional), and 'kwargs' (optional) +# to specify a special tag or special keyword args for the projects apply() call. +# For example: +# {'name':"Muni_TEP", 'kwargs':{'servicePlan':"'2012oct'"}} +########################################################### +COMMITTED_PROJECTS = collections.OrderedDict([ + (2015, { + 'hwy':['PROJ_attributes', # adds PROJ attributes to NODE and LINK + {'name':'Bridge_Toll_Updates_2_2pct', 'kwargs':{'MODELYEAR':'2015'}}], + 'trn':[] + }), + (2020, { + 'hwy':[{'name':'Bridge_Toll_Updates_2_2pct', 'kwargs':{'MODELYEAR':'2020'}}, + {'name':'EXP_237B', 'kwargs':{'FUTURE':"PBA50"}}, # todo: update this to support PBA50 + 'EXP_580C', + 'EXP_680D', + 'EXP_880A', + 'HOV_680F', + 'SCL130001_237_101_MAT_Int_Mod', + 'REG090003_SCLARA_FIP', + 'ALA130005_Dougherty_road_widening', + 'ALA130006_Dublin_Blvd_widening', + 'ALA130014_7th_St_road_diet', + 'ALA130026_Shattuck_Complete_Streets', + 'ALA170049_Central_AVE_Safety_Improvements', + 'ALA150004_EastBay_BRT', + 'CC_130001_BaileyRd_SR4', + 'CC_130046_I680_SR4_Int_Rec', + 'CC_070035_I80_SPDamRd_Int_Phase1', + 'CC_070011_Brentwood_Blvd_Widening', + 'CC_070075_Kirker_Pass_Truck_Lane', + 'CC_090019_Bollinger_Canyon_Widening', + 'CC_130006_Concord_BART_road_diets', + 'CC_170001_SanRamonValleyBlvd_Lane_Addition', + 'MRN150009_San_Rafael_Bridge_Improvements', + 'SF_070027_Yerba_Buena_Ramp_Imp', + 'SF_070005_VanNess_BRT', + 'SF_130011_2ndSt_Road_Diet', + 'SF_Market_Street_Closure', + 'SM_110047_SR92_ElCam_Ramp_Mod', + 'SOL110005_Jepson_Van_to_Com', + 'FBP_SL_042_Jepson_2A', + 'SON070004_101_MarinSonNarrows_Phase1', + 'ALA050014_SR84_Widening', + 'ALA170011_BayBridge_HOV_Connectors', + 'ALA150047_TelegraphAve_Complete_Streets', + 'SM_110047_SR92_ElCam_Ramp_Mod', + 'SCL190002_280_Foothill_improvement', + 'SCL190006_101SB_offramp_improvement', + 'I80_AdaptiveRampMetering', + 'VAR170021_Freeway_Performance_I880', + 'SonomaCounty_Transit_NoBuild2050', + 'SF_MuniForward_Committed', + 'FBP_MU_029_Broadway_Transit_Only_Lanes', + 'EXP_Blueprint_NoProject', + 'FBP_AL_067_Rte84Wide', + 'FBP_AL_065_Bancroft_Bus_Only', + 'FBP_SM_032_US101_Willow_Interchange'], + 'trn':['ALA050015_BART_to_WarmSprings', + 'ACGo', + 'CC_050025_EBart_to_Antioch', + 'GGTransit_Committed', + 'SCL110005_BART_to_Berryessa', + 'SF_010015_Transbay_Terminal', + 'SF_010037_Muni_Central_Subway', + 'SF_070027_Yerba_Buena_Ramp_Imp', + 'SOL030002_FairfieldVacaville_Stn', + 'SON090002_SMART', + 'SON090002_SMART_to_Larkspur', + 'CC_070062_Richmond_Ferry', + 'SF_MuniForward_Committed', + 'VTA_Next', + 'SCL130001_237_101_MAT_Int_Mod', + 'SonomaCounty_Transit_NoBuild2050', + 'SMART_Novato', + 'Xfare_update_2020', + 'ACTransit_Committed', + 'ferry_update_2019', + 'Napa_Solano_Updates_2020', + 'FBP_Beale_Transit_Only_Lane', + 'SamTrans_ECR_Rapid', + 'ALA150004_EastBay_BRT', + {'name':'FBP_SL_026_SolExpressBus', 'kwargs':{'MODELYEAR':'2020'}}], + }), + (2025, { + 'hwy':[], + 'trn':['Xfare_FareCooordination'] + }) +]) + + + +# Put them together for NETWORK_PROJECTS +NETWORK_PROJECTS = collections.OrderedDict() + +for YEAR in COMMITTED_PROJECTS.keys(): + NETWORK_PROJECTS[YEAR] = { + 'hwy':COMMITTED_PROJECTS[YEAR]['hwy'], + 'trn':COMMITTED_PROJECTS[YEAR]['trn'] + } + +# +# For every year where a project is applied do the following: +# Convert all zero-length links to 0.01 +# Move buses to HOV/Express lanes at the end +# +for YEAR in NETWORK_PROJECTS.keys(): + # if anything is applied + if ((len(NETWORK_PROJECTS[YEAR]['hwy']) > 0) or (len(NETWORK_PROJECTS[YEAR]['trn']) > 0)): + NETWORK_PROJECTS[YEAR]['hwy'].append('No_zero_length_links') + + if ((len(NETWORK_PROJECTS[YEAR]['hwy']) > 0) or (len(NETWORK_PROJECTS[YEAR]['trn']) > 0)): + NETWORK_PROJECTS[YEAR]['trn'].append('Move_buses_to_HOV_EXP_lanes') + + +# OPTIONAL. The default route network project directory is Y:\networks. If +# projects are stored in another directory, then use this variable to specify it. +# For example: Y:\networks\projects +# NETWORK_BASE_DIR = None +# NETWORK_PROJECT_SUBDIR = None +# NETWORK_SEED_SUBDIR = None +# NETWORK_PLAN_SUBDIR = None +# OPTIONAL. A list of project names which have been previously applied in the +# PIVOT_DIR network that projects in this project might rely on. For example +# if DoyleDrive exists, then Muni_TEP gets applied differently so transit lines +# run on the new Doyle Drive alignment +APPLIED_PROJECTS = None +# OPTIONAL. A list of project names. For test mode, these projects won't use +# the TAG. This is meant for developing a network project. +TEST_PROJECTS = [] diff --git a/scripts/set_capclass.job b/scripts/set_capclass.job index 9b080ee..755304b 100644 --- a/scripts/set_capclass.job +++ b/scripts/set_capclass.job @@ -14,6 +14,7 @@ ; - Facility Type (FT) ; - Traffic Operations (TOS) ; - Signal Coordination (SIGCOR) +; - Route number (ROUTENUM) - I280 faster speeds ; ; => sets Free Flow Speed (FFS) based on CAPCLASS ; => sets Capacity (CAP) based on CAPCLASS @@ -83,6 +84,10 @@ RUN PGM=HWYNET IF (FT=7 & AT=4 & SIGCOR=1) CAPCLASS=60 IF (FT=7 & AT=5 & SIGCOR=1) CAPCLASS=60 + ; Update for 280 higher speeds + ; See also: https://github.com/BayAreaMetro/TM1_2015_Base_Network/commit/372870e8324aafc94ad834ca93ca1fc7d3170270 + IF ((ROUTENUM==280) && (AT>2)) CAPCLASS=62 + SPDCLASS=CAPCLASS ;----------------------------------------------------------------------- ; Code Free Flow Speeds for Links @@ -152,7 +157,8 @@ RUN PGM=HWYNET IF (CAPCLASS=58) FFS=65 IF (CAPCLASS=59) FFS=50 ;TOS Fwy-to-Fwy (AT=4,5) IF (CAPCLASS=60) FFS=40 ;Arterial Sig. Coor. (AT=4,5) - + ;----------------------------------------------------------------------- + IF (CAPCLASS=62) FFS=75 ;----------------------------------------------------------------------- ; Code Capacities for Links @@ -223,6 +229,7 @@ RUN PGM=HWYNET IF (CAPCLASS=59) CAP=2050 ;TOS Fwy-to-Fwy (AT=4,5) IF (CAPCLASS=60) CAP=1100 ;Arterial Signal Coordination (AT=4,5) ;----------------------------------------------------------------------- + IF (CAPCLASS=62) CAP=2150 ;----------------------------------------------------------------------- ; Free-Flow Time Variable @@ -243,7 +250,7 @@ RUN PGM=HWYNET SPDCAP SPEED[31]=45,60,45,25,35,18,30,60,50,25 SPDCAP SPEED[41]=50,65,50,30,40,18,35,65,45,30 SPDCAP SPEED[51]=50,65,55,35,40,18,40,65,50,40 - SPDCAP SPEED[61]=0,0,0 + SPDCAP SPEED[61]=50,75 ; INDEX is CAPCLASS SPDCAP CAPACITY[01]=1850,2050,1450,600,1450,0,900,2150,2100,1500 @@ -252,6 +259,7 @@ RUN PGM=HWYNET SPDCAP CAPACITY[31]=1950,2100,1600,700,1550,0,1000,2200,1950,1000 SPDCAP CAPACITY[41]=2000,2150,1650,900,1550,0,1050,2250,2000,1050 SPDCAP CAPACITY[51]=2000,2150,1650,950,1550,0,1050,2250,2050,1100 + SPDCAP CAPACITY[61]=2000,2150 REPORT SPEED=YES CAPACITY=YES ;report speed/capacity tables in network diff --git a/scripts/tag_project_repo.py b/scripts/tag_project_repo.py new file mode 100644 index 0000000..fff5268 --- /dev/null +++ b/scripts/tag_project_repo.py @@ -0,0 +1,107 @@ +USAGE = """ + +Tagging Network Project local git repository based on project coding version (e.g. Network scenario). +Example tags: 'PPA', 'dbp', 'STIP', 'PBA50_Blueprint', 'PBA50_NoProject' + +Inputs: + - tag_name: the name of the tag to be added + - tag_message: the tagging message + - network_creation_log: log file containing info on which Network Projects should be tagged + +Example call: +python tag_project_repo.py "PBA50_Blueprint" "version used to build PBA50 Blueprint Networks" "M:\\Application\\Model One\\RTP2021\\Blueprint\\INPUT_DEVELOPMENT\\Networks\\BlueprintNetworks_64\\buildnetwork_Blueprint_Blueprint_2021Jul29.170436.info.LOG" +python tag_project_repo.py "PBA50_NoProject" "version used to build PBA50 NoProject Networks" "M:\\Application\\Model One\\RTP2021\\Blueprint\\INPUT_DEVELOPMENT\\Networks\\BlueprintNetworks_62\\buildnetwork_Blueprint_Baseline_2021Jul13.222230.info.LOG" + +""" + + +import git +import pandas as pd +import os, argparse + + +if __name__ == '__main__': + + # NetworkProjects directory, this is where the local repos are located + networkProjects_folder = 'M:\\Application\\Model One\\NetworkProjects' + + # arguments + parser = argparse.ArgumentParser(description=USAGE, formatter_class=argparse.RawDescriptionHelpFormatter,) + parser.add_argument('tag_name', help='name of the tag to be created') + parser.add_argument('tag_message', help='tagging message') + parser.add_argument('network_creation_log', help='network creation log listing all projects to be tagged') + + args = parser.parse_args() + print('tag name: {}'.format(args.tag_name)) + print('tagging message: {}'.format(args.tag_message)) + print('network creation log: {}'.format(args.network_creation_log)) + + + # Step 1: create a dataframe to store project_name and the commit (SHA1_id) to tag + + projects_df = pd.DataFrame(columns=['project_name', 'SHA1_id']) + + with open(args.network_creation_log) as f: + log_lines = list(enumerate(f)) + for line_num, line in log_lines: + + # get name of project + if 'Applying project' in line: + project_name = line.split('] of type')[0].split('Applying project [')[1] + # print(project_name) + + # with NetworkWrangler log file format, the SHA1_id is usually in the next line + # which also contains the project's name + next_line = log_lines[line_num+1][1] + if project_name in next_line: + SHA1_id = next_line.split('| '+project_name)[0].split('|')[-1].strip() + # print(SHA1_id) + + # add 'project_name', 'SHA1_id' to the dataframe + projects_df.loc[len(projects_df.index)] = [project_name, SHA1_id] + + elif project_name not in next_line: + print('No SHA1_id in the next line for project: ', project_name) + + # manually add SHA1_id for project 'Move_buses_to_HOV_EXP_lanes' which has + # a different format in the log + projects_df.loc[len(projects_df.index)] = ['Move_buses_to_HOV_EXP_lanes', + 'b8ac63bfe873df1c80e2c8ecd67904ad3970b721'] + + # drop duplicates + projects_df.drop_duplicates(inplace=True) + print('tagging {} projects'.format(projects_df.shape[0])) + + # set project_name as index + projects_df.set_index('project_name', inplace=True) + + + # Step 2: loop through the projects and add tag to the corresponding commit + + for project in projects_df.index: + print('Project: ', project) + + # try to open the existing repo + try: + repo = git.Repo(os.path.join(networkProjects_folder, project)) + + # optional: print out existing tags, sorted by time of creation + # existing_tags = sorted(repo.tags, key=lambda t: t.commit.committed_date) + # print('existing_tags: {}'.format(existing_tags)) + + + # try creating the tag to the right commit + commit_ref = repo.commit(projects_df.SHA1_id[project]) + print('commit_reference: {}'.format(commit_ref)) + try: + print('create tag {} with comment {}'.format(args.tag_name, args.tag_message)) + repo.create_tag(args.tag_name, + ref=commit_ref, + message = args.tag_message) + + # if the tag already exists, cannot create, will skip + except: + print('cannot create tag "{}"'.format(args.tag_name)) + + except: + print('repo {} doest not exist'.format(project)) diff --git a/setup.py b/setup.py new file mode 100644 index 0000000..6333eef --- /dev/null +++ b/setup.py @@ -0,0 +1,45 @@ +""" +Installation script for NetworkWrangler package +""" + +import os +import setuptools + +VERSION="1.5" + +classifiers = [ + "Development Status :: 5 - Production/Stable", + "License :: OSI Approved :: Apache Software License", + "Natural Language :: English", + "Operating System :: OS Independent", + "Programming Language :: Python", + "Programming Language :: Python :: 3", + "Programming Language :: Python :: 3.6", + "Programming Language :: Python :: 3.10", +] + +# long description from README.md +with open("README.md") as f: + long_description = f.read() + +with open("requirements.txt") as f: + requirements = f.readlines() +install_requires = [r.strip() for r in requirements] + +setuptools.setup( + name = "NetworkWrangler", + version = VERSION, + description = "Wrangles networks for MTC Travel Model 1/1.5", + long_description = long_description, + long_description_content_type = "text/markdown", + url = "https://github.com/BayAreaMetro/NetworkWrangler", + license = "Apache 2", + platforms = "any", + packages = ["Wrangler"], + include_package_data = True, + install_requires = install_requires, + scripts = [ + "scripts/build_network_mtc.py", + "scripts/build_network_mtc_blueprint.py", + ], +) \ No newline at end of file