From 152566f54b467070f12655c11b0856106c814879 Mon Sep 17 00:00:00 2001 From: pveigadecamargo Date: Mon, 26 Jun 2023 21:10:35 +1000 Subject: [PATCH 01/16] assignment logging --- aequilibrae/paths/traffic_assignment.py | 11 +++++++- aequilibrae/paths/traffic_class.py | 37 ++++++++++++++++++++++--- 2 files changed, 43 insertions(+), 5 deletions(-) diff --git a/aequilibrae/paths/traffic_assignment.py b/aequilibrae/paths/traffic_assignment.py index 65be6fa3b..22d326626 100644 --- a/aequilibrae/paths/traffic_assignment.py +++ b/aequilibrae/paths/traffic_assignment.py @@ -1,4 +1,5 @@ import importlib.util as iutil +import logging import socket import sqlite3 from datetime import datetime @@ -102,6 +103,8 @@ def __init__(self, project=None) -> None: """""" proj = project or get_active_project(must_exist=False) + + self.logger = proj.logger if proj else logging.getLogger("aequilibrae") par = proj.parameters if proj else Parameters().parameters parameters = par["assignment"]["equilibrium"] @@ -408,10 +411,16 @@ def __validate_parameters(self, kwargs) -> bool: raise ValueError("List of functions {} for vdf {} has an inadequate set of parameters".format(q, self.vdf)) return True - def execute(self) -> None: + def execute(self, log_specification=True) -> None: """Processes assignment""" + if log_specification: + self.log_specification() self.assignment.execute() + def log_specification(self): + for cls in self.classes: + self.logger.info(str(cls.info)) + def save_results(self, table_name: str, keep_zero_flows=True, project=None) -> None: """Saves the assignment results to results_database.sqlite diff --git a/aequilibrae/paths/traffic_class.py b/aequilibrae/paths/traffic_class.py index 68bd15fc5..c2f864e6b 100644 --- a/aequilibrae/paths/traffic_class.py +++ b/aequilibrae/paths/traffic_class.py @@ -1,11 +1,12 @@ +import warnings +from copy import deepcopy from typing import Union, List, Tuple, Dict + import numpy as np -import pandas as pd -from aequilibrae.paths.graph import Graph from aequilibrae.matrix import AequilibraeMatrix +from aequilibrae.paths.graph import Graph from aequilibrae.paths.results import AssignmentResults -import warnings class TrafficClass: @@ -51,7 +52,7 @@ def __init__(self, name: str, graph: Graph, matrix: AequilibraeMatrix) -> None: if matrix.matrix_view.dtype != graph.default_types("float"): raise TypeError("Matrix's computational view need to be of type np.float64") - + self.__config = {} self.graph = graph self.logger = graph.logger self.matrix = matrix @@ -67,6 +68,25 @@ def __init__(self, name: str, graph: Graph, matrix: AequilibraeMatrix) -> None: self._selected_links = {} # maps human name to link_set self.__id__ = name + graph_config = { + "mode": graph.mode, + "block_through_centroids": graph.block_centroid_flows, + "centroids": graph.centroids, + "graph.num_centroids": graph.num_zones, + "links": graph.num_links, + "nodes": graph.num_nodes, + } + self.__config["graph"] = str(graph_config) + + mat_config = { + "source": matrix.file_path or "", + "matrix_cores": matrix.view_names, + "centroids": matrix.index, + "matrix_totals": {name: np.sum(matrix.matrix[name]) for name in matrix.view_names}, + "nodes": matrix.zones, + } + self.__config["matrix"] = str(mat_config) + def set_pce(self, pce: Union[float, int]) -> None: """Sets Passenger Car equivalent @@ -140,6 +160,12 @@ def set_select_links(self, links: Dict[str, List[Tuple[int, int]]]): else: link_ids.append(comp_id) self._selected_links[name] = np.array(link_ids, dtype=self.graph.default_types("int")) + self.__config["select_links"] = str(links) + + @property + def info(self) -> dict: + config = deepcopy(self.__config) + return {self.__id__: config} def __setattr__(self, key, value): if key not in [ @@ -157,6 +183,9 @@ def __setattr__(self, key, value): "fc_multiplier", "fixed_cost_field", "_selected_links", + "__config", ]: raise KeyError("Traffic Class does not have that element") self.__dict__[key] = value + + self.__config[key] = value From a80b1f03e2051b6efee02ebc25bdbc1fcb03c87d Mon Sep 17 00:00:00 2001 From: Renata Imai Date: Mon, 26 Jun 2023 15:00:05 -0300 Subject: [PATCH 02/16] Updates logging --- aequilibrae/paths/traffic_assignment.py | 3 ++- aequilibrae/paths/traffic_class.py | 8 ++++---- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/aequilibrae/paths/traffic_assignment.py b/aequilibrae/paths/traffic_assignment.py index 22d326626..7f82f7672 100644 --- a/aequilibrae/paths/traffic_assignment.py +++ b/aequilibrae/paths/traffic_assignment.py @@ -104,7 +104,6 @@ def __init__(self, project=None) -> None: proj = project or get_active_project(must_exist=False) - self.logger = proj.logger if proj else logging.getLogger("aequilibrae") par = proj.parameters if proj else Parameters().parameters parameters = par["assignment"]["equilibrium"] @@ -129,6 +128,8 @@ def __init__(self, project=None) -> None: self.__dict__["procedure_date"] = str(datetime.today()) self.__dict__["steps_below_needed_to_terminate"] = 1 self.__dict__["project"] = proj + self.__dict__["logger"] = None + self.logger = proj.logger if proj else logging.getLogger("aequilibrae") def __setattr__(self, instance, value) -> None: check, value, message = self.__check_attributes(instance, value) diff --git a/aequilibrae/paths/traffic_class.py b/aequilibrae/paths/traffic_class.py index c2f864e6b..622726bb1 100644 --- a/aequilibrae/paths/traffic_class.py +++ b/aequilibrae/paths/traffic_class.py @@ -82,7 +82,9 @@ def __init__(self, name: str, graph: Graph, matrix: AequilibraeMatrix) -> None: "source": matrix.file_path or "", "matrix_cores": matrix.view_names, "centroids": matrix.index, - "matrix_totals": {name: np.sum(matrix.matrix[name]) for name in matrix.view_names}, + "matrix_totals": { + name: np.sum(matrix.matrix_view) for name in matrix.view_names if name not in "matrix_totals" + }, "nodes": matrix.zones, } self.__config["matrix"] = str(mat_config) @@ -183,9 +185,7 @@ def __setattr__(self, key, value): "fc_multiplier", "fixed_cost_field", "_selected_links", - "__config", + "_TrafficClass__config", ]: raise KeyError("Traffic Class does not have that element") self.__dict__[key] = value - - self.__config[key] = value From d28eef60fc2b1c0b69784b48f96308c4f9940730 Mon Sep 17 00:00:00 2001 From: Renata Imai Date: Wed, 28 Jun 2023 10:14:36 -0300 Subject: [PATCH 03/16] Adds __config log --- aequilibrae/paths/traffic_assignment.py | 17 ++++++++++++++++- aequilibrae/paths/traffic_class.py | 16 +++++++++------- 2 files changed, 25 insertions(+), 8 deletions(-) diff --git a/aequilibrae/paths/traffic_assignment.py b/aequilibrae/paths/traffic_assignment.py index 7f82f7672..73dc8094d 100644 --- a/aequilibrae/paths/traffic_assignment.py +++ b/aequilibrae/paths/traffic_assignment.py @@ -1,3 +1,4 @@ +from copy import deepcopy import importlib.util as iutil import logging import socket @@ -128,9 +129,11 @@ def __init__(self, project=None) -> None: self.__dict__["procedure_date"] = str(datetime.today()) self.__dict__["steps_below_needed_to_terminate"] = 1 self.__dict__["project"] = proj + + self.__dict__["_TrafficAssignment__config"] = {} self.__dict__["logger"] = None self.logger = proj.logger if proj else logging.getLogger("aequilibrae") - + def __setattr__(self, instance, value) -> None: check, value, message = self.__check_attributes(instance, value) if check: @@ -252,6 +255,9 @@ def set_algorithm(self, algorithm: str): raise Exception("Algorithm not listed in the case selection") self.__dict__["algorithm"] = algo + self.__config["algorithm"] = algo + self.__config["max_iter"] = self.assignment.max_iter + self.__config["target_rgap"] = self.assignment.rgap_target def set_vdf_parameters(self, par: dict) -> None: """ @@ -293,6 +299,8 @@ def set_vdf_parameters(self, par: dict) -> None: raise ValueError(f"At least one {p1} is smaller than one. Results will make no sense") self.__dict__["vdf_parameters"] = pars + self.__config["vdf_parameter"] = par + self.__config["vdf_function"] = self.vdf.function.lower() def set_cores(self, cores: int) -> None: """Allows one to set the number of cores to be used AFTER traffic classes have been added @@ -367,6 +375,7 @@ def set_time_field(self, time_field: str) -> None: self.__dict__["congested_time"] = np.array(self.free_flow_tt, copy=True) self.__dict__["total_flow"] = np.zeros(self.free_flow_tt.shape[0], np.float64) self.time_field = time_field + self.__config["time_field"] = time_field def set_capacity_field(self, capacity_field: str) -> None: """ @@ -393,6 +402,8 @@ def set_capacity_field(self, capacity_field: str) -> None: self.__dict__["capacity"] = np.zeros(c.graph.graph.shape[0], c.graph.default_types("float")) self.__dict__["capacity"][c.graph.graph.__supernet_id__] = c.graph.graph[capacity_field] self.capacity_field = capacity_field + self.__config["num_cores"] = c.results.cores + self.__config["capacity_field"] = capacity_field # TODO: This function actually needs to return a human-readable dictionary, and not one with # tons of classes. Feeds into the class above @@ -416,9 +427,13 @@ def execute(self, log_specification=True) -> None: """Processes assignment""" if log_specification: self.log_specification() + self.logger.info("Traffic Assignment specification") + config = deepcopy(self.__config) + self.logger.info(config) self.assignment.execute() def log_specification(self): + self.logger.info("Traffic Class specification") for cls in self.classes: self.logger.info(str(cls.info)) diff --git a/aequilibrae/paths/traffic_class.py b/aequilibrae/paths/traffic_class.py index 622726bb1..4e1fb6ad6 100644 --- a/aequilibrae/paths/traffic_class.py +++ b/aequilibrae/paths/traffic_class.py @@ -71,8 +71,7 @@ def __init__(self, name: str, graph: Graph, matrix: AequilibraeMatrix) -> None: graph_config = { "mode": graph.mode, "block_through_centroids": graph.block_centroid_flows, - "centroids": graph.centroids, - "graph.num_centroids": graph.num_zones, + "num_centroids": graph.num_zones, "links": graph.num_links, "nodes": graph.num_nodes, } @@ -80,13 +79,16 @@ def __init__(self, name: str, graph: Graph, matrix: AequilibraeMatrix) -> None: mat_config = { "source": matrix.file_path or "", - "matrix_cores": matrix.view_names, - "centroids": matrix.index, - "matrix_totals": { - name: np.sum(matrix.matrix_view) for name in matrix.view_names if name not in "matrix_totals" - }, + "num_centroids": matrix.index.shape[0], "nodes": matrix.zones, + "matrix_cores": matrix.view_names, } + if len(matrix.view_names) == 1: + mat_config["matrix_totals"] = {nm: np.sum(matrix.matrix_view[:, :]) for nm in matrix.view_names} + else: + mat_config["matrix_totals"] = { + nm: np.sum(matrix.matrix_view[:, :, i]) for i, nm in enumerate(matrix.view_names) + } self.__config["matrix"] = str(mat_config) def set_pce(self, pce: Union[float, int]) -> None: From e9a0f12eced8709a15adb24226efb2743cac32aa Mon Sep 17 00:00:00 2001 From: Renata Imai Date: Wed, 28 Jun 2023 17:26:39 -0300 Subject: [PATCH 04/16] Adds test and docs --- aequilibrae/paths/traffic_assignment.py | 4 +- aequilibrae/paths/traffic_class.py | 6 +- .../other_applications/plot_check_logging.py | 102 ++++++++++++++++++ .../paths/test_traffic_assignment.py | 44 +++++++- 4 files changed, 150 insertions(+), 6 deletions(-) create mode 100644 docs/source/examples/other_applications/plot_check_logging.py diff --git a/aequilibrae/paths/traffic_assignment.py b/aequilibrae/paths/traffic_assignment.py index 73dc8094d..c4394f314 100644 --- a/aequilibrae/paths/traffic_assignment.py +++ b/aequilibrae/paths/traffic_assignment.py @@ -129,11 +129,11 @@ def __init__(self, project=None) -> None: self.__dict__["procedure_date"] = str(datetime.today()) self.__dict__["steps_below_needed_to_terminate"] = 1 self.__dict__["project"] = proj - + self.__dict__["_TrafficAssignment__config"] = {} self.__dict__["logger"] = None self.logger = proj.logger if proj else logging.getLogger("aequilibrae") - + def __setattr__(self, instance, value) -> None: check, value, message = self.__check_attributes(instance, value) if check: diff --git a/aequilibrae/paths/traffic_class.py b/aequilibrae/paths/traffic_class.py index 4e1fb6ad6..52c81aed1 100644 --- a/aequilibrae/paths/traffic_class.py +++ b/aequilibrae/paths/traffic_class.py @@ -84,10 +84,12 @@ def __init__(self, name: str, graph: Graph, matrix: AequilibraeMatrix) -> None: "matrix_cores": matrix.view_names, } if len(matrix.view_names) == 1: - mat_config["matrix_totals"] = {nm: np.sum(matrix.matrix_view[:, :]) for nm in matrix.view_names} + mat_config["matrix_totals"] = { + nm: np.sum(np.nan_to_num(matrix.matrix_view)[:, :]) for nm in matrix.view_names + } else: mat_config["matrix_totals"] = { - nm: np.sum(matrix.matrix_view[:, :, i]) for i, nm in enumerate(matrix.view_names) + nm: np.sum(np.nan_to_num(matrix.matrix_view)[:, :, i]) for i, nm in enumerate(matrix.view_names) } self.__config["matrix"] = str(mat_config) diff --git a/docs/source/examples/other_applications/plot_check_logging.py b/docs/source/examples/other_applications/plot_check_logging.py new file mode 100644 index 000000000..231a61c54 --- /dev/null +++ b/docs/source/examples/other_applications/plot_check_logging.py @@ -0,0 +1,102 @@ +""" +.. _useful-log-tips: + +Checking AequilibraE's log +========================== + +AequilibraE's log is a very useful tool to get more information about +what the software is doing under the hood. + +Information such as Traffic Class and Traffic Assignment stats, and Traffic Assignment +outputs. If you have created your project's network from OSM, you will also find +information on the number of nodes, links, and the area being downloaded. + +In this example, we'll use Sioux Falls data to check the logs, but we strongly encourage +you to go ahead and download a place of your choice and perform a traffic assignment. +""" +# %% +# Imports +from uuid import uuid4 +from tempfile import gettempdir +from os.path import join +from aequilibrae.utils.create_example import create_example +from aequilibrae.paths import TrafficAssignment, TrafficClass + +# %% +# We create an empty project on an arbitrary folder +fldr = join(gettempdir(), uuid4().hex) +project = create_example(fldr) + +# %% +# We build our graphs +project.network.build_graphs() + +graph = project.network.graphs["c"] +graph.set_graph("free_flow_time") +graph.set_skimming(["free_flow_time", "distance"]) +graph.set_blocked_centroid_flows(False) + +# %% +# We get our demand matrix from the project and create a computational view +proj_matrices = project.matrices +demand = proj_matrices.get_matrix("demand_omx") +demand.computational_view(["matrix"]) + +# %% +# Now let's perform our traffic assignment +assig = TrafficAssignment() + +assigclass = TrafficClass(name="car", graph=graph, matrix=demand) + +assig.add_class(assigclass) +assig.set_vdf("BPR") +assig.set_vdf_parameters({"alpha": 0.15, "beta": 4.0}) +assig.set_capacity_field("capacity") +assig.set_time_field("free_flow_time") +assig.set_algorithm("bfw") +assig.max_iter = 50 +assig.rgap_target = 0.001 + +assig.execute() + +# %% +# +with open(join(fldr, "aequilibrae.log")) as file: + for idx, line in enumerate(file): + print(idx+1, "-", line) + +#%% +# In lines 1-7, we receive some warnings that our fields name and lane have ``NaN`` values. +# As they are not relevant to our example, we can move on. +# +# In lines 8-9 we get the Traffic Class specifications. +# We can see that there is only one traffic class (car). Its **graph** key presents information +# on blocked flow through centroids, number of centroids, links, and nodes. +# In the **matrix** key, we find information on where in the disk the matrix file is located. +# We also have information on the number of centroids and nodes, as well as on the matrix/matrices +# used for computation. In our example, we only have one matrix named matrix, and the total +# sum of this matrix element is equal to 360,600. If you have more than one matrix its data +# will be also displayed in the *matrix_cores* and *matrix_totals* keys. +# +# In lines 10-11 the log shows the Traffic Assignment specifications. +# We can see that the VDF parameters, VDF function, capacity and time fields, algorithm, +# maximum number of iterations, and target gap are just like the ones we set previously. +# The only information that might be new to you is the number of cores used for computation. +# If you haven't set any, AequilibraE is going to use the largest number of CPU threads +# available. +# +# Line 12 displays us a warning to indicate that AequilibraE is converting the data type +# of the cost field. +# +# Lines 13-61 indicate that we'll receive the outputs of a *bfw* algorithm. +# In the log there are also the number of the iteration, its relative gap, and the stepsize. +# The outputs in lines 15-60 are exactly the same as the ones provided by the function +# ``assig.report()``. Finally, the last line shows us that the *bfw* assignment has finished +# after 46 iterations because its gap is smaller than the threshold we configured (0.001).7 +# +# In case you execute a new traffic assignment using different classes or changing the +# parameters values, these new specification values would be stored in the log file as well +# so you can always keep a record of what you have been doing. One last reminder is that +# if we had created our project from OSM, the lines on top of the log would have been +# different to display information on the queries done to the server to obtain the data. +# diff --git a/tests/aequilibrae/paths/test_traffic_assignment.py b/tests/aequilibrae/paths/test_traffic_assignment.py index 554e841a9..3e7356091 100644 --- a/tests/aequilibrae/paths/test_traffic_assignment.py +++ b/tests/aequilibrae/paths/test_traffic_assignment.py @@ -19,8 +19,8 @@ class TestTrafficAssignment(TestCase): def setUp(self) -> None: os.environ["PATH"] = os.path.join(gettempdir(), "temp_data") + ";" + os.environ["PATH"] - proj_path = os.path.join(gettempdir(), "test_traffic_assignment_" + uuid.uuid4().hex) - self.project = create_example(proj_path) + self.proj_path = os.path.join(gettempdir(), "test_traffic_assignment_" + uuid.uuid4().hex) + self.project = create_example(self.proj_path) self.project.network.build_graphs() self.car_graph = self.project.network.graphs["c"] # type: Graph self.car_graph.set_graph("free_flow_time") @@ -211,6 +211,46 @@ def test_execute_and_save_results(self): with self.assertRaises(ValueError): self.assignment.save_results("save_to_database") + self.assertTrue(os.path.isfile(os.path.join(self.proj_path, "aequilibrae.log"))) + lines = [] + with open(os.path.join(self.proj_path, "aequilibrae.log"), encoding="utf-8") as file: + for line in file: + lines.append(line) + + self.assertTrue( + lines[8].split(";", 1)[1] + == lines[27].split(";", 1)[1] + == lines[536].split(";", 1)[1] + == lines[658].split(";", 1)[1] + == lines[713].split(";", 1)[1] + ) + tclass_data = lines[8].split(";")[2].replace("\\", "").replace("'", "").split(", ") + self.assertEqual( + tclass_data[:5], + [ + ' {car: {graph: "{mode: c', + "block_through_centroids: False", + "num_centroids: 24", + "links: 76", + 'nodes: 24}"', + ], + ) + self.assertEqual(tclass_data[-2:], ["matrix_cores: [matrix]", 'matrix_totals: {matrix: 360600.0}}"}}\n']) + + assig_data_1 = lines[10].split(";")[2].replace("\\", "").replace("'", "").split(", ") + assig_data_2 = lines[29].split(";")[2].replace("\\", "").replace("'", "").split(", ") + assig_data_3 = lines[538].split(";")[2].replace("\\", "").replace("'", "").split(", ") + assig_data_4 = lines[660].split(";")[2].replace("\\", "").replace("'", "").split(", ") + assig_data_5 = lines[715].split(";")[2].replace("\\", "").replace("'", "").split(", ") + self.assertTrue( + assig_data_1[:5] == assig_data_2[:5] == assig_data_3[:5] == assig_data_4[:5] == assig_data_5[:5] + ) + self.assertEqual(assig_data_1[-3:], ["algorithm: msa", "max_iter: 10", "target_rgap: 0.0001}\n"]) + self.assertEqual(assig_data_2[-3:], ["algorithm: msa", "max_iter: 500", "target_rgap: 0.001}\n"]) + self.assertEqual(assig_data_3[-3:], ["algorithm: frank-wolfe", "max_iter: 500", "target_rgap: 0.001}\n"]) + self.assertEqual(assig_data_4[-3:], ["algorithm: cfw", "max_iter: 500", "target_rgap: 0.001}\n"]) + self.assertEqual(assig_data_5[-3:], ["algorithm: bfw", "max_iter: 500", "target_rgap: 0.001}\n"]) + def test_execute_no_project(self): conn = sqlite3.connect(os.path.join(siouxfalls_project, "project_database.sqlite")) results = pd.read_sql("select volume from links order by link_id", conn) From 559e862e0a03eb7fbf0048d9eef3761c2a0b76dc Mon Sep 17 00:00:00 2001 From: Pedro Camargo Date: Thu, 29 Jun 2023 21:37:50 +1000 Subject: [PATCH 05/16] Update aequilibrae/paths/traffic_assignment.py --- aequilibrae/paths/traffic_assignment.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/aequilibrae/paths/traffic_assignment.py b/aequilibrae/paths/traffic_assignment.py index c4394f314..ec30a704b 100644 --- a/aequilibrae/paths/traffic_assignment.py +++ b/aequilibrae/paths/traffic_assignment.py @@ -299,7 +299,7 @@ def set_vdf_parameters(self, par: dict) -> None: raise ValueError(f"At least one {p1} is smaller than one. Results will make no sense") self.__dict__["vdf_parameters"] = pars - self.__config["vdf_parameter"] = par + self.__config["vdf_parameter"] = pars self.__config["vdf_function"] = self.vdf.function.lower() def set_cores(self, cores: int) -> None: From 24b68c1bc5cba03d868add1d09910662c6e26b04 Mon Sep 17 00:00:00 2001 From: Renata Imai Date: Thu, 29 Jun 2023 09:59:33 -0300 Subject: [PATCH 06/16] Fixes tests --- aequilibrae/paths/traffic_assignment.py | 22 ++++++++-------- aequilibrae/paths/traffic_class.py | 26 +++++++++---------- .../other_applications/plot_check_logging.py | 14 +++++----- .../paths/test_traffic_assignment.py | 24 +++++++++-------- 4 files changed, 44 insertions(+), 42 deletions(-) diff --git a/aequilibrae/paths/traffic_assignment.py b/aequilibrae/paths/traffic_assignment.py index ec30a704b..80acd60e1 100644 --- a/aequilibrae/paths/traffic_assignment.py +++ b/aequilibrae/paths/traffic_assignment.py @@ -255,9 +255,9 @@ def set_algorithm(self, algorithm: str): raise Exception("Algorithm not listed in the case selection") self.__dict__["algorithm"] = algo - self.__config["algorithm"] = algo - self.__config["max_iter"] = self.assignment.max_iter - self.__config["target_rgap"] = self.assignment.rgap_target + self.__config["Algorithm"] = algo + self.__config["Maximum iterations"] = self.assignment.max_iter + self.__config["Target RGAP"] = self.assignment.rgap_target def set_vdf_parameters(self, par: dict) -> None: """ @@ -273,6 +273,7 @@ def set_vdf_parameters(self, par: dict) -> None: if self.classes is None or self.vdf.function.lower() not in all_vdf_functions: raise Exception("Before setting vdf parameters, you need to set traffic classes and choose a VDF function") self.__dict__["vdf_parameters"] = par + self.__config["VDF parameters"] = par pars = [] if self.vdf.function in ["BPR", "BPR2", "CONICAL", "INRETS"]: for p1 in ["alpha", "beta"]: @@ -299,8 +300,7 @@ def set_vdf_parameters(self, par: dict) -> None: raise ValueError(f"At least one {p1} is smaller than one. Results will make no sense") self.__dict__["vdf_parameters"] = pars - self.__config["vdf_parameter"] = pars - self.__config["vdf_function"] = self.vdf.function.lower() + self.__config["VDF function"] = self.vdf.function.lower() def set_cores(self, cores: int) -> None: """Allows one to set the number of cores to be used AFTER traffic classes have been added @@ -375,7 +375,7 @@ def set_time_field(self, time_field: str) -> None: self.__dict__["congested_time"] = np.array(self.free_flow_tt, copy=True) self.__dict__["total_flow"] = np.zeros(self.free_flow_tt.shape[0], np.float64) self.time_field = time_field - self.__config["time_field"] = time_field + self.__config["Time field"] = time_field def set_capacity_field(self, capacity_field: str) -> None: """ @@ -402,8 +402,8 @@ def set_capacity_field(self, capacity_field: str) -> None: self.__dict__["capacity"] = np.zeros(c.graph.graph.shape[0], c.graph.default_types("float")) self.__dict__["capacity"][c.graph.graph.__supernet_id__] = c.graph.graph[capacity_field] self.capacity_field = capacity_field - self.__config["num_cores"] = c.results.cores - self.__config["capacity_field"] = capacity_field + self.__config["Number of cores"] = c.results.cores + self.__config["Capacity field"] = capacity_field # TODO: This function actually needs to return a human-readable dictionary, and not one with # tons of classes. Feeds into the class above @@ -427,9 +427,6 @@ def execute(self, log_specification=True) -> None: """Processes assignment""" if log_specification: self.log_specification() - self.logger.info("Traffic Assignment specification") - config = deepcopy(self.__config) - self.logger.info(config) self.assignment.execute() def log_specification(self): @@ -437,6 +434,9 @@ def log_specification(self): for cls in self.classes: self.logger.info(str(cls.info)) + self.logger.info("Traffic Assignment specification") + self.logger.info(self.__config) + def save_results(self, table_name: str, keep_zero_flows=True, project=None) -> None: """Saves the assignment results to results_database.sqlite diff --git a/aequilibrae/paths/traffic_class.py b/aequilibrae/paths/traffic_class.py index 52c81aed1..5b5414a94 100644 --- a/aequilibrae/paths/traffic_class.py +++ b/aequilibrae/paths/traffic_class.py @@ -69,29 +69,29 @@ def __init__(self, name: str, graph: Graph, matrix: AequilibraeMatrix) -> None: self.__id__ = name graph_config = { - "mode": graph.mode, - "block_through_centroids": graph.block_centroid_flows, - "num_centroids": graph.num_zones, - "links": graph.num_links, - "nodes": graph.num_nodes, + "Mode": graph.mode, + "Block through centroids": graph.block_centroid_flows, + "Number of centroids": graph.num_zones, + "Links": graph.num_links, + "Nodes": graph.num_nodes, } - self.__config["graph"] = str(graph_config) + self.__config["Graph"] = str(graph_config) mat_config = { - "source": matrix.file_path or "", - "num_centroids": matrix.index.shape[0], - "nodes": matrix.zones, - "matrix_cores": matrix.view_names, + "Source": matrix.file_path or "", + "Number of centroids": matrix.index.shape[0], + "Nodes": matrix.zones, + "Matrix cores": matrix.view_names, } if len(matrix.view_names) == 1: - mat_config["matrix_totals"] = { + mat_config["Matrix totals"] = { nm: np.sum(np.nan_to_num(matrix.matrix_view)[:, :]) for nm in matrix.view_names } else: - mat_config["matrix_totals"] = { + mat_config["Matrix totals"] = { nm: np.sum(np.nan_to_num(matrix.matrix_view)[:, :, i]) for i, nm in enumerate(matrix.view_names) } - self.__config["matrix"] = str(mat_config) + self.__config["Matrix"] = str(mat_config) def set_pce(self, pce: Union[float, int]) -> None: """Sets Passenger Car equivalent diff --git a/docs/source/examples/other_applications/plot_check_logging.py b/docs/source/examples/other_applications/plot_check_logging.py index 231a61c54..c058534e6 100644 --- a/docs/source/examples/other_applications/plot_check_logging.py +++ b/docs/source/examples/other_applications/plot_check_logging.py @@ -9,10 +9,10 @@ Information such as Traffic Class and Traffic Assignment stats, and Traffic Assignment outputs. If you have created your project's network from OSM, you will also find -information on the number of nodes, links, and the area being downloaded. +information on the number of nodes, links, and the query performed to obtain the data. In this example, we'll use Sioux Falls data to check the logs, but we strongly encourage -you to go ahead and download a place of your choice and perform a traffic assignment. +you to go ahead and download a place of your choice and perform a traffic assignment! """ # %% # Imports @@ -60,12 +60,12 @@ assig.execute() # %% -# +# with open(join(fldr, "aequilibrae.log")) as file: for idx, line in enumerate(file): - print(idx+1, "-", line) + print(idx + 1, "-", line) -#%% +# %% # In lines 1-7, we receive some warnings that our fields name and lane have ``NaN`` values. # As they are not relevant to our example, we can move on. # @@ -92,11 +92,11 @@ # In the log there are also the number of the iteration, its relative gap, and the stepsize. # The outputs in lines 15-60 are exactly the same as the ones provided by the function # ``assig.report()``. Finally, the last line shows us that the *bfw* assignment has finished -# after 46 iterations because its gap is smaller than the threshold we configured (0.001).7 +# after 46 iterations because its gap is smaller than the threshold we configured (0.001). # # In case you execute a new traffic assignment using different classes or changing the # parameters values, these new specification values would be stored in the log file as well -# so you can always keep a record of what you have been doing. One last reminder is that +# so you can always keep a record of what you have been doing. One last reminder is that # if we had created our project from OSM, the lines on top of the log would have been # different to display information on the queries done to the server to obtain the data. # diff --git a/tests/aequilibrae/paths/test_traffic_assignment.py b/tests/aequilibrae/paths/test_traffic_assignment.py index 3e7356091..05bac9a8d 100644 --- a/tests/aequilibrae/paths/test_traffic_assignment.py +++ b/tests/aequilibrae/paths/test_traffic_assignment.py @@ -228,14 +228,14 @@ def test_execute_and_save_results(self): self.assertEqual( tclass_data[:5], [ - ' {car: {graph: "{mode: c', - "block_through_centroids: False", - "num_centroids: 24", - "links: 76", - 'nodes: 24}"', + ' {car: {Graph: "{Mode: c', + "Block through centroids: False", + "Number of centroids: 24", + "Links: 76", + 'Nodes: 24}"', ], ) - self.assertEqual(tclass_data[-2:], ["matrix_cores: [matrix]", 'matrix_totals: {matrix: 360600.0}}"}}\n']) + self.assertEqual(tclass_data[-2:], ["Matrix cores: [matrix]", 'Matrix totals: {matrix: 360600.0}}"}}\n']) assig_data_1 = lines[10].split(";")[2].replace("\\", "").replace("'", "").split(", ") assig_data_2 = lines[29].split(";")[2].replace("\\", "").replace("'", "").split(", ") @@ -245,11 +245,13 @@ def test_execute_and_save_results(self): self.assertTrue( assig_data_1[:5] == assig_data_2[:5] == assig_data_3[:5] == assig_data_4[:5] == assig_data_5[:5] ) - self.assertEqual(assig_data_1[-3:], ["algorithm: msa", "max_iter: 10", "target_rgap: 0.0001}\n"]) - self.assertEqual(assig_data_2[-3:], ["algorithm: msa", "max_iter: 500", "target_rgap: 0.001}\n"]) - self.assertEqual(assig_data_3[-3:], ["algorithm: frank-wolfe", "max_iter: 500", "target_rgap: 0.001}\n"]) - self.assertEqual(assig_data_4[-3:], ["algorithm: cfw", "max_iter: 500", "target_rgap: 0.001}\n"]) - self.assertEqual(assig_data_5[-3:], ["algorithm: bfw", "max_iter: 500", "target_rgap: 0.001}\n"]) + self.assertEqual(assig_data_1[-3:], ["Algorithm: msa", "Maximum iterations: 10", "Target RGAP: 0.0001}\n"]) + self.assertEqual(assig_data_2[-3:], ["Algorithm: msa", "Maximum iterations: 500", "Target RGAP: 0.001}\n"]) + self.assertEqual( + assig_data_3[-3:], ["Algorithm: frank-wolfe", "Maximum iterations: 500", "Target RGAP: 0.001}\n"] + ) + self.assertEqual(assig_data_4[-3:], ["Algorithm: cfw", "Maximum iterations: 500", "Target RGAP: 0.001}\n"]) + self.assertEqual(assig_data_5[-3:], ["Algorithm: bfw", "Maximum iterations: 500", "Target RGAP: 0.001}\n"]) def test_execute_no_project(self): conn = sqlite3.connect(os.path.join(siouxfalls_project, "project_database.sqlite")) From ebaf3433d2a50615c0321d1f12c593c567d101c7 Mon Sep 17 00:00:00 2001 From: pveigadecamargo Date: Mon, 3 Jul 2023 18:48:50 +1000 Subject: [PATCH 07/16] fixes coverage testing --- .coveragerc | 3 +++ .github/workflows/test_linux_with_coverage.yml | 12 +----------- 2 files changed, 4 insertions(+), 11 deletions(-) create mode 100644 .coveragerc diff --git a/.coveragerc b/.coveragerc new file mode 100644 index 000000000..3a2a8f12d --- /dev/null +++ b/.coveragerc @@ -0,0 +1,3 @@ +[report] +fail_under = 81.0 +show_missing = True diff --git a/.github/workflows/test_linux_with_coverage.yml b/.github/workflows/test_linux_with_coverage.yml index e8b4a2f47..b6dbe3a7e 100644 --- a/.github/workflows/test_linux_with_coverage.yml +++ b/.github/workflows/test_linux_with_coverage.yml @@ -32,14 +32,4 @@ jobs: - name: Generate coverage report run: | - python3 -m pytest --cov=./ --cov-report=xml - - name: Upload coverage to Codecov - if: ${{ (env.HAS_SECRETS == 'true') }} - uses: codecov/codecov-action@v3 - with: - token: ${{ secrets.CODECOV_TOKEN }} - file: ./coverage.xml - flags: unittests - name: codecov-umbrella - yml: ./codecov.yml - fail_ci_if_error: true \ No newline at end of file + python3 -m pytest --cov=./ \ No newline at end of file From 64f3cd9e3c3df146966c46dba672c256d3462c52 Mon Sep 17 00:00:00 2001 From: pveigadecamargo Date: Mon, 3 Jul 2023 19:20:43 +1000 Subject: [PATCH 08/16] merges testing changes --- .../paths/test_traffic_assignment.py | 44 ++++++++++++++++--- 1 file changed, 39 insertions(+), 5 deletions(-) diff --git a/tests/aequilibrae/paths/test_traffic_assignment.py b/tests/aequilibrae/paths/test_traffic_assignment.py index 28bf49a63..43c9baeb8 100644 --- a/tests/aequilibrae/paths/test_traffic_assignment.py +++ b/tests/aequilibrae/paths/test_traffic_assignment.py @@ -1,7 +1,8 @@ -import os import random import sqlite3 import string +from os.path import join, isfile +from pathlib import Path from random import choice import numpy as np @@ -9,7 +10,6 @@ import pytest from aequilibrae import TrafficAssignment, TrafficClass, Graph -from aequilibrae.project.project import Project from aequilibrae.utils.create_example import create_example from ...data import siouxfalls_project @@ -186,11 +186,12 @@ def matrix(self, request, matrix): return matrix def test_execute_and_save_results( - self, assignment: TrafficAssignment, assigclass: TrafficClass, car_graph: Graph, matrix + self, assignment: TrafficAssignment, assigclass: TrafficClass, car_graph: Graph, matrix ): - conn = sqlite3.connect(os.path.join(siouxfalls_project, "project_database.sqlite")) + conn = sqlite3.connect(join(siouxfalls_project, "project_database.sqlite")) results = pd.read_sql("select volume from links order by link_id", conn) + proj = assignment.project assignment.add_class(assigclass) assignment.set_vdf("BPR") assignment.set_vdf_parameters({"alpha": 0.15, "beta": 4.0}) @@ -262,8 +263,41 @@ def test_execute_and_save_results( with pytest.raises(ValueError): assignment.save_results("save_to_database") + # Let's test logging of assignment + log_ = Path(proj.path_to_file).parent / "aequilibrae.log" + assert isfile(log_) + + with open(log_, encoding="utf-8") as file: + # lines = ";".join([x.rstrip() for x in file.readlines()]) + lines = [x.rstrip() for x in file.readlines()] + + assert lines[8].split(";", 1)[1] == lines[27].split(";", 1)[1] == lines[536].split(";", 1)[1] == \ + lines[658].split(";", 1)[1] == lines[713].split(";", 1)[1] + + tclass_data = lines[8].split(";")[2].replace("\\", "").replace("'", "").split(", ") + assert tclass_data[:5] == [' {car: {Graph: "{Mode: c', + "Block through centroids: False", + "Number of centroids: 24", + "Links: 76", + 'Nodes: 24}"', + ] + assert tclass_data[-2:] == ["Matrix cores: [matrix]", 'Matrix totals: {matrix: 360600.0}}"}}'] + + assig_data_1 = lines[10].split(";")[2].replace("\\", "").replace("'", "").split(", ") + assig_data_2 = lines[29].split(";")[2].replace("\\", "").replace("'", "").split(", ") + assig_data_3 = lines[538].split(";")[2].replace("\\", "").replace("'", "").split(", ") + assig_data_4 = lines[660].split(";")[2].replace("\\", "").replace("'", "").split(", ") + assig_data_5 = lines[715].split(";")[2].replace("\\", "").replace("'", "").split(", ") + assert assig_data_1[:5] == assig_data_2[:5] == assig_data_3[:5] == assig_data_4[:5] == assig_data_5[:5] + assert assig_data_1[-3:] == ["Algorithm: msa", "Maximum iterations: 10", "Target RGAP: 0.0001}"] + assert assig_data_2[-3:] == ["Algorithm: msa", "Maximum iterations: 500", "Target RGAP: 0.001}"] + assert assig_data_3[-3:] == ["Algorithm: frank-wolfe", "Maximum iterations: 500", "Target RGAP: 0.001}"] + + assert assig_data_4[-3:] == ["Algorithm: cfw", "Maximum iterations: 500", "Target RGAP: 0.001}"] + assert assig_data_5[-3:] == ["Algorithm: bfw", "Maximum iterations: 500", "Target RGAP: 0.001}"] + def test_execute_no_project(self): - conn = sqlite3.connect(os.path.join(siouxfalls_project, "project_database.sqlite")) + conn = sqlite3.connect(join(siouxfalls_project, "project_database.sqlite")) results = pd.read_sql("select volume from links order by link_id", conn) project.close() From d07c08c57e815200cfa9af70933fab7c567da00a Mon Sep 17 00:00:00 2001 From: pveigadecamargo Date: Mon, 3 Jul 2023 19:26:04 +1000 Subject: [PATCH 09/16] style --- .../paths/test_traffic_assignment.py | 24 ++++++++++++------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/tests/aequilibrae/paths/test_traffic_assignment.py b/tests/aequilibrae/paths/test_traffic_assignment.py index 43c9baeb8..6cd92e82e 100644 --- a/tests/aequilibrae/paths/test_traffic_assignment.py +++ b/tests/aequilibrae/paths/test_traffic_assignment.py @@ -186,7 +186,7 @@ def matrix(self, request, matrix): return matrix def test_execute_and_save_results( - self, assignment: TrafficAssignment, assigclass: TrafficClass, car_graph: Graph, matrix + self, assignment: TrafficAssignment, assigclass: TrafficClass, car_graph: Graph, matrix ): conn = sqlite3.connect(join(siouxfalls_project, "project_database.sqlite")) results = pd.read_sql("select volume from links order by link_id", conn) @@ -271,16 +271,22 @@ def test_execute_and_save_results( # lines = ";".join([x.rstrip() for x in file.readlines()]) lines = [x.rstrip() for x in file.readlines()] - assert lines[8].split(";", 1)[1] == lines[27].split(";", 1)[1] == lines[536].split(";", 1)[1] == \ - lines[658].split(";", 1)[1] == lines[713].split(";", 1)[1] + assert ( + lines[8].split(";", 1)[1] + == lines[27].split(";", 1)[1] + == lines[536].split(";", 1)[1] + == lines[658].split(";", 1)[1] + == lines[713].split(";", 1)[1] + ) tclass_data = lines[8].split(";")[2].replace("\\", "").replace("'", "").split(", ") - assert tclass_data[:5] == [' {car: {Graph: "{Mode: c', - "Block through centroids: False", - "Number of centroids: 24", - "Links: 76", - 'Nodes: 24}"', - ] + assert tclass_data[:5] == [ + ' {car: {Graph: "{Mode: c', + "Block through centroids: False", + "Number of centroids: 24", + "Links: 76", + 'Nodes: 24}"', + ] assert tclass_data[-2:] == ["Matrix cores: [matrix]", 'Matrix totals: {matrix: 360600.0}}"}}'] assig_data_1 = lines[10].split(";")[2].replace("\\", "").replace("'", "").split(", ") From 46f7fa4ea3f0d4bd098fa63058b053082b40d237 Mon Sep 17 00:00:00 2001 From: pveigadecamargo Date: Mon, 3 Jul 2023 20:05:48 +1000 Subject: [PATCH 10/16] style --- tests/aequilibrae/paths/test_traffic_assignment.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/aequilibrae/paths/test_traffic_assignment.py b/tests/aequilibrae/paths/test_traffic_assignment.py index 6cd92e82e..9071f2a80 100644 --- a/tests/aequilibrae/paths/test_traffic_assignment.py +++ b/tests/aequilibrae/paths/test_traffic_assignment.py @@ -302,8 +302,8 @@ def test_execute_and_save_results( assert assig_data_4[-3:] == ["Algorithm: cfw", "Maximum iterations: 500", "Target RGAP: 0.001}"] assert assig_data_5[-3:] == ["Algorithm: bfw", "Maximum iterations: 500", "Target RGAP: 0.001}"] - def test_execute_no_project(self): - conn = sqlite3.connect(join(siouxfalls_project, "project_database.sqlite")) + def test_execute_no_project(self, project: Project, assignment: TrafficAssignment, assigclass: TrafficClass): + conn = sqlite3.connect(os.path.join(siouxfalls_project, "project_database.sqlite")) results = pd.read_sql("select volume from links order by link_id", conn) project.close() @@ -325,4 +325,4 @@ def test_execute_no_project(self): assert 0.8 < correl with pytest.raises(FileNotFoundError): - assignment.save_results("anything") + assignment.save_results("anything") \ No newline at end of file From 8faa5406d4dedf414b938742d9242050bbbfda21 Mon Sep 17 00:00:00 2001 From: pveigadecamargo Date: Mon, 3 Jul 2023 20:06:24 +1000 Subject: [PATCH 11/16] style --- tests/aequilibrae/paths/test_traffic_assignment.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/aequilibrae/paths/test_traffic_assignment.py b/tests/aequilibrae/paths/test_traffic_assignment.py index 9071f2a80..2a3065c9b 100644 --- a/tests/aequilibrae/paths/test_traffic_assignment.py +++ b/tests/aequilibrae/paths/test_traffic_assignment.py @@ -9,7 +9,7 @@ import pandas as pd import pytest -from aequilibrae import TrafficAssignment, TrafficClass, Graph +from aequilibrae import TrafficAssignment, TrafficClass, Graph, Project from aequilibrae.utils.create_example import create_example from ...data import siouxfalls_project @@ -303,7 +303,7 @@ def test_execute_and_save_results( assert assig_data_5[-3:] == ["Algorithm: bfw", "Maximum iterations: 500", "Target RGAP: 0.001}"] def test_execute_no_project(self, project: Project, assignment: TrafficAssignment, assigclass: TrafficClass): - conn = sqlite3.connect(os.path.join(siouxfalls_project, "project_database.sqlite")) + conn = sqlite3.connect(join(siouxfalls_project, "project_database.sqlite")) results = pd.read_sql("select volume from links order by link_id", conn) project.close() @@ -325,4 +325,4 @@ def test_execute_no_project(self, project: Project, assignment: TrafficAssignmen assert 0.8 < correl with pytest.raises(FileNotFoundError): - assignment.save_results("anything") \ No newline at end of file + assignment.save_results("anything") From 7336af6dd214995ea9aeae67d03da9fd57723558 Mon Sep 17 00:00:00 2001 From: pveigadecamargo Date: Mon, 3 Jul 2023 20:10:22 +1000 Subject: [PATCH 12/16] style --- .github/workflows/test_linux_with_coverage.yml | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/.github/workflows/test_linux_with_coverage.yml b/.github/workflows/test_linux_with_coverage.yml index b6dbe3a7e..b1083b27a 100644 --- a/.github/workflows/test_linux_with_coverage.yml +++ b/.github/workflows/test_linux_with_coverage.yml @@ -1,8 +1,6 @@ name: Code coverage -on: - pull_request: - types: [ready_for_review, merge] +on: [pull_request] jobs: testing: From cd8de1bc6099aaa85e73315d523791954680b48e Mon Sep 17 00:00:00 2001 From: pveigadecamargo Date: Mon, 3 Jul 2023 20:23:58 +1000 Subject: [PATCH 13/16] narrows coverage test --- .github/workflows/test_linux_with_coverage.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test_linux_with_coverage.yml b/.github/workflows/test_linux_with_coverage.yml index b1083b27a..5df7ce9c7 100644 --- a/.github/workflows/test_linux_with_coverage.yml +++ b/.github/workflows/test_linux_with_coverage.yml @@ -30,4 +30,4 @@ jobs: - name: Generate coverage report run: | - python3 -m pytest --cov=./ \ No newline at end of file + python3 -m pytest --cov=aequilibrae tests/ \ No newline at end of file From b4550a0a90e1da0ab9db216e5534106c90160917 Mon Sep 17 00:00:00 2001 From: Renata Imai Date: Tue, 4 Jul 2023 11:10:23 -0300 Subject: [PATCH 14/16] Update test_traffic_assignment.py --- .../paths/test_traffic_assignment.py | 67 ++++++++++--------- 1 file changed, 36 insertions(+), 31 deletions(-) diff --git a/tests/aequilibrae/paths/test_traffic_assignment.py b/tests/aequilibrae/paths/test_traffic_assignment.py index 2a3065c9b..3f9a98b50 100644 --- a/tests/aequilibrae/paths/test_traffic_assignment.py +++ b/tests/aequilibrae/paths/test_traffic_assignment.py @@ -263,44 +263,49 @@ def test_execute_and_save_results( with pytest.raises(ValueError): assignment.save_results("save_to_database") + num_cores = assignment.cores # Let's test logging of assignment log_ = Path(proj.path_to_file).parent / "aequilibrae.log" assert isfile(log_) - with open(log_, encoding="utf-8") as file: - # lines = ";".join([x.rstrip() for x in file.readlines()]) - lines = [x.rstrip() for x in file.readlines()] + file_text = "" + with open(log_, "r", encoding="utf-8") as file: + for line in file.readlines(): + file_text += line - assert ( - lines[8].split(";", 1)[1] - == lines[27].split(";", 1)[1] - == lines[536].split(";", 1)[1] - == lines[658].split(";", 1)[1] - == lines[713].split(";", 1)[1] + tc_spec = "INFO ; Traffic Class specification" + assert file_text.count(tc_spec) > 1 + + tc_graph = "INFO ; {'car': {'Graph': \"{'Mode': 'c', 'Block through centroids': False, 'Number of centroids': 24, 'Links': 76, 'Nodes': 24}\"," + assert file_text.count(tc_graph) > 1 + + tc_matrix = "'Number of centroids': 24, 'Nodes': 24, 'Matrix cores': ['matrix'], 'Matrix totals': {'matrix': 360600.0}}\"}}" + assert file_text.count(tc_matrix) > 1 + + assig_1 = "INFO ; {{'VDF parameters': {{'alpha': 'b', 'beta': 'power'}}, 'VDF function': 'bpr', 'Number of cores': {}, 'Capacity field': 'capacity', 'Time field': 'free_flow_time', 'Algorithm': 'msa', 'Maximum iterations': 10, 'Target RGAP': 0.0001}}".format( + num_cores + ) + assert assig_1 in file_text + + assig_2 = "INFO ; {{'VDF parameters': {{'alpha': 'b', 'beta': 'power'}}, 'VDF function': 'bpr', 'Number of cores': {}, 'Capacity field': 'capacity', 'Time field': 'free_flow_time', 'Algorithm': 'msa', 'Maximum iterations': 500, 'Target RGAP': 0.001}}".format( + num_cores ) + assert assig_2 in file_text - tclass_data = lines[8].split(";")[2].replace("\\", "").replace("'", "").split(", ") - assert tclass_data[:5] == [ - ' {car: {Graph: "{Mode: c', - "Block through centroids: False", - "Number of centroids: 24", - "Links: 76", - 'Nodes: 24}"', - ] - assert tclass_data[-2:] == ["Matrix cores: [matrix]", 'Matrix totals: {matrix: 360600.0}}"}}'] - - assig_data_1 = lines[10].split(";")[2].replace("\\", "").replace("'", "").split(", ") - assig_data_2 = lines[29].split(";")[2].replace("\\", "").replace("'", "").split(", ") - assig_data_3 = lines[538].split(";")[2].replace("\\", "").replace("'", "").split(", ") - assig_data_4 = lines[660].split(";")[2].replace("\\", "").replace("'", "").split(", ") - assig_data_5 = lines[715].split(";")[2].replace("\\", "").replace("'", "").split(", ") - assert assig_data_1[:5] == assig_data_2[:5] == assig_data_3[:5] == assig_data_4[:5] == assig_data_5[:5] - assert assig_data_1[-3:] == ["Algorithm: msa", "Maximum iterations: 10", "Target RGAP: 0.0001}"] - assert assig_data_2[-3:] == ["Algorithm: msa", "Maximum iterations: 500", "Target RGAP: 0.001}"] - assert assig_data_3[-3:] == ["Algorithm: frank-wolfe", "Maximum iterations: 500", "Target RGAP: 0.001}"] - - assert assig_data_4[-3:] == ["Algorithm: cfw", "Maximum iterations: 500", "Target RGAP: 0.001}"] - assert assig_data_5[-3:] == ["Algorithm: bfw", "Maximum iterations: 500", "Target RGAP: 0.001}"] + assig_3 = "INFO ; {{'VDF parameters': {{'alpha': 'b', 'beta': 'power'}}, 'VDF function': 'bpr', 'Number of cores': {}, 'Capacity field': 'capacity', 'Time field': 'free_flow_time', 'Algorithm': 'frank-wolfe', 'Maximum iterations': 500, 'Target RGAP': 0.001}}".format( + num_cores + ) + assert assig_3 in file_text + + assig_4 = "INFO ; {{'VDF parameters': {{'alpha': 'b', 'beta': 'power'}}, 'VDF function': 'bpr', 'Number of cores': {}, 'Capacity field': 'capacity', 'Time field': 'free_flow_time', 'Algorithm': 'cfw', 'Maximum iterations': 500, 'Target RGAP': 0.001}}".format( + num_cores + ) + assert assig_4 in file_text + + assig_5 = "INFO ; {{'VDF parameters': {{'alpha': 'b', 'beta': 'power'}}, 'VDF function': 'bpr', 'Number of cores': {}, 'Capacity field': 'capacity', 'Time field': 'free_flow_time', 'Algorithm': 'bfw', 'Maximum iterations': 500, 'Target RGAP': 0.001}}".format( + num_cores + ) + assert assig_5 in file_text def test_execute_no_project(self, project: Project, assignment: TrafficAssignment, assigclass: TrafficClass): conn = sqlite3.connect(join(siouxfalls_project, "project_database.sqlite")) From f964c90f5a386681aa7d589826949bdb5de18d7a Mon Sep 17 00:00:00 2001 From: Pedro Camargo Date: Wed, 5 Jul 2023 22:46:11 +1000 Subject: [PATCH 15/16] Apply suggestions from code review --- aequilibrae/paths/traffic_class.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/aequilibrae/paths/traffic_class.py b/aequilibrae/paths/traffic_class.py index 5b5414a94..aaf3451e8 100644 --- a/aequilibrae/paths/traffic_class.py +++ b/aequilibrae/paths/traffic_class.py @@ -79,8 +79,7 @@ def __init__(self, name: str, graph: Graph, matrix: AequilibraeMatrix) -> None: mat_config = { "Source": matrix.file_path or "", - "Number of centroids": matrix.index.shape[0], - "Nodes": matrix.zones, + "Number of centroids": matrix.zones, "Matrix cores": matrix.view_names, } if len(matrix.view_names) == 1: From e18cc2d72dc5b711b349fefce12eec16ffb49cc3 Mon Sep 17 00:00:00 2001 From: Renata Imai Date: Wed, 5 Jul 2023 10:04:20 -0300 Subject: [PATCH 16/16] Fixes test_traffic_assignment --- tests/aequilibrae/paths/test_traffic_assignment.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/aequilibrae/paths/test_traffic_assignment.py b/tests/aequilibrae/paths/test_traffic_assignment.py index 3f9a98b50..1af80a722 100644 --- a/tests/aequilibrae/paths/test_traffic_assignment.py +++ b/tests/aequilibrae/paths/test_traffic_assignment.py @@ -279,7 +279,7 @@ def test_execute_and_save_results( tc_graph = "INFO ; {'car': {'Graph': \"{'Mode': 'c', 'Block through centroids': False, 'Number of centroids': 24, 'Links': 76, 'Nodes': 24}\"," assert file_text.count(tc_graph) > 1 - tc_matrix = "'Number of centroids': 24, 'Nodes': 24, 'Matrix cores': ['matrix'], 'Matrix totals': {'matrix': 360600.0}}\"}}" + tc_matrix = "'Number of centroids': 24, 'Matrix cores': ['matrix'], 'Matrix totals': {'matrix': 360600.0}}\"}}" assert file_text.count(tc_matrix) > 1 assig_1 = "INFO ; {{'VDF parameters': {{'alpha': 'b', 'beta': 'power'}}, 'VDF function': 'bpr', 'Number of cores': {}, 'Capacity field': 'capacity', 'Time field': 'free_flow_time', 'Algorithm': 'msa', 'Maximum iterations': 10, 'Target RGAP': 0.0001}}".format(