Skip to content

Conversation

@Ennazus
Copy link
Collaborator

@Ennazus Ennazus commented Jan 12, 2026

County Network Test Framework Enhancements

Summary

This PR enhances the county network testing framework with improved documentation, defensive attribute checking, resource monitoring, and comprehensive network inspection tools. The changes make the testing system more robust and easier to use, especially when working with networks that may have incomplete attribute initialization (e.g., OSM-derived networks).

Statistics

  • 46 files changed: +5,645 additions, -1,267 deletions
  • Net change: +4,378 lines
  • 14 commits ahead of develop
  • No merge conflicts

Key Changes

1. 📚 Documentation Overhaul (~2,500 lines)

New comprehensive testing documentation in docs/testing/:

  • county-test-guide.md (417 lines) - Complete guide for county-level highway testing
  • configuration.md (360 lines) - Detailed configuration documentation
  • data-flow.md (473 lines) - Visual data flow diagrams and explanations
  • emme-manager-flow.md (262 lines) - EMME manager interaction flows
  • quick-start.md (389 lines) - Quick start guide for new users
  • network-thinning.md (202 lines) - Network thinning strategies
  • index.md (87 lines) - Testing documentation index

Also added:

  • field-name-mapping.md (157 lines) - CTRAMP field mapping reference
  • Updated mkdocs.yml with new testing section

2. 🛡️ Defensive Attribute Checking (Production Code)

Enhanced tm2py/components/network/highway/highway_network.py (+151/-4):

Added comprehensive attribute checking in 4 methods:

  • _set_tolls(): Checks for @tollbooth, @tollseg, @useclass

    • Gracefully skips toll processing if missing (logs warning)
    • Useful for networks without toll facility coding
  • _set_vdf_attributes(): Checks for @capclass, @lanes, @ft, @free_flow_speed

    • Raises clear KeyError if missing (critical attributes)
  • _set_link_modes(): Checks for @drive_link

    • Raises clear KeyError if missing (critical attribute)
  • _calc_link_skim_lengths(): Checks for @useclass, @tollbooth

    • Gracefully skips HOV/toll length calculations if missing

Benefits:

  • Clear, actionable error messages instead of cryptic KeyErrors
  • Helps diagnose network attribute initialization issues
  • Backward compatible with existing networks
  • Documents required vs optional attributes

3. 📊 Resource Monitoring

Enhanced tm2py/components/network/highway/highway_assign.py (+88/-4):

Added monitor_resources() context manager:

  • Monitors CPU and memory usage during SOLA assignment
  • Logs to console and separate resource_monitor.log file
  • Updates every 5 minutes during long-running assignments
  • Non-blocking threaded implementation

Applied to:

  • SOLA assignment without path analysis
  • SOLA assignment with path analysis

4. 🔧 Test Framework Improvements

Major refactor of tests/run_county_test.py (+478/-105):

  • Better prerequisite checking
  • Enhanced logging and error handling
  • More flexible configuration system

New tests/county_test_config.toml (102 lines):

  • Centralized configuration
  • Supports split EMME project and inputs sources

Enhanced tests/highway_assign_skim_controller.py (+130/-2):

  • Better configuration handling

5. 🔍 Network Inspection Tools (New Utilities)

Created comprehensive network diagnostic tools:

  • inspect_emme_network.py (257 lines) - Full network inspection
  • analyze_osm_network.py (128 lines) - OSM network distribution analysis
  • list_network_attributes.py (78 lines) - Attribute lister
  • quick_network_check.py (76 lines) - Fast scenario checker
  • check_modes.py (16 lines) - Mode validation
  • check_network_volumes.py (53 lines) - Volume validation

6. 📝 OSM Network Investigation

Documentation of OSM network attribute investigation:

  • OSM_Network_Investigation_Summary.md (101 lines) - Investigation findings
  • OSM_Network_Available_Attributes.md (84 lines) - Attribute inventory
  • osm_network_issues.md (66 lines) - Issue tracker

7. ⚙️ Configuration Templates

  • New fixed_san_mateo_model.toml (972 lines) - Complete configuration
  • New fixed_san_mateo_scenario.toml (76 lines) - Scenario config
  • Binary capclass files added
  • Empty placeholder files for tolls and interchange nodes
  • Removed outdated san_mateo configs

8. 🧹 Documentation Cleanup

Removed outdated documentation (content moved to docs/testing/):

  • tests/COUNTY_TEST_FRAMEWORK_UPDATE.md (205 lines)
  • tests/HIGHWAY_ASSIGN_SKIM_README.md (405 lines)
  • tests/TESTING_INSTRUCTIONS.md (185 lines)

9. 🐛 Minor Fixes

tm2py/emme/manager.py (1 line):

  • Removed .lower() on time_period lookup (preserves case sensitivity)

Testing

  • ✅ Tested with 2023 San Mateo county network
  • ✅ Defensive attribute checking validated with OSM network
  • ✅ Resource monitoring tested during assignments
  • ✅ All network inspection tools executed successfully

Backward Compatibility

  • ✅ All changes are backward compatible
  • ✅ Existing networks with proper attributes work as before
  • ✅ Networks without attributes now get clear error messages instead of cryptic failures

Dependencies

  • Uses psutil for resource monitoring (already in requirements)
  • Uses threading (stdlib)

Review Notes

  1. Documentation: Comprehensive testing docs now in proper location (docs/testing/)
  2. Error Messages: Much clearer for users when network attributes are missing
  3. Debugging: New inspection tools make network troubleshooting easier
  4. Monitoring: Resource usage visibility during long-running assignments
  5. OSM Investigation: Documents findings about OSM-derived networks lacking TM2 attributes

Questions for Reviewers

  1. Should the OSM investigation docs stay in tests/ or move to docs/?
  2. Any concerns about the defensive attribute checking approach?
  3. Is the resource monitoring frequency (5 minutes) appropriate?

Reviewer: @lmz (Lisa Zorn)


@Ennazus Ennazus requested a review from lmz January 12, 2026 17:39
@Ennazus Ennazus added the feature New feature or request label Jan 12, 2026
@lmz
Copy link
Member

lmz commented Jan 14, 2026

This is my attempt to run it.

Create working directory and copy hwy input aux files

E:\GitHub\tm2\tm2py\tests>mkdir E:\2015_TM2_20260113\inputs\hwy

E:\GitHub\tm2\tm2py\tests>copy "E:\Box\Modeling and Surveys\Development\Travel Model Two Conversion\Model Outputs\2015-tm22-dev-sprint-04\inputs\hwy\tolls.csv" E:\2015_TM2_20260113\inputs\hwy

E:\GitHub\tm2\tm2py\tests>copy "E:\Box\Modeling and Surveys\Development\Travel Model Two Conversion\Model Outputs\2015-tm22-dev-sprint-04\inputs\hwy\interchange_nodes.csv" E:\2015_TM2_20260113\inputs\hwy

Copy in land use input

E:\GitHub\tm2\tm2py\tests>mkdir E:\2015_TM2_20260113\inputs\landuse

E:\GitHub\tm2\tm2py\tests>copy "E:\Box\Modeling and Surveys\Development\Travel Model Two Conversion\Model Outputs\2015-tm22-dev-sprint-04\inputs\landuse\maz_data.csv" E:\2015_TM2_20260113\inputs\landuse

Copy in demand input

E:\GitHub\tm2\tm2py\tests>mkdir E:\2015_TM2_20260113\demand_matrices\highway\household

E:\GitHub\tm2\tm2py\tests>copy "E:\Box\Modeling and Surveys\Development\Travel Model Two Conversion\Model Outputs\2015-tm22-dev-sprint-04\demand_matrices\highway\household\TAZ_Demand_??.omx"  E:\2015_TM2_20260113\demand_matrices\highway\household

copy in hwy network input

E:\GitHub\tm2\tm2py\tests>mkdir E:\2015_TM2_20260113\emme_project

E:\GitHub\tm2\tm2py\tests>copy "E:\Box\Modeling and Surveys\Development\Travel Model Two Conversion\Model Outputs\2015-tm22-dev-sprint-04\emme_project\mtc_emme.emp" E:\2015_TM2_20260113\emme_project

E:\GitHub\tm2\tm2py\tests>copy "E:\Box\Modeling and Surveys\Development\Travel Model Two Conversion\Model Inputs\2015-tm22-dev-sprint-04\emme_network\Database_highway_EMME_24.01.00.zip" E:\2015_TM2_20260113\emme_project

E:\GitHub\tm2\tm2py\tests>"C:\Program Files\7-Zip\7z.exe" x E:\2015_TM2_20260113\emme_project_source\Database_highway_EMME_24.01.00.zip -oE:\2015_TM2_20260113\emme_project

copy in config

E:\GitHub\tm2\tm2py\tests>mkdir E:\2015_TM2_20260113\san_mateo_test\config

E:\GitHub\tm2\tm2py\tests>copy config_templates\fixed_san_mateo_model.toml E:\2015_TM2_20260113\san_mateo_test\config\model.toml

E:\GitHub\tm2\tm2py\tests>copy config_templates\fixed_san_mateo_scenario.toml E:\2015_TM2_20260113\san_mateo_test\config\scenario.toml

@lmz
Copy link
Member

lmz commented Jan 14, 2026

When I ran:

E:\GitHub\tm2\tm2py\tests>python run_county_test.py --config county_test_config.toml
Loading configuration from: county_test_config.toml
INFO: County Test Framework - Starting
INFO: Configuration file: county_test_config.toml
INFO: ======================================================================
INFO: CONFIGURATION SUMMARY
INFO: ======================================================================
INFO: County: San Mateo
INFO: EMME project source: E:/2015_TM2_20260113/emme_project
INFO: Inputs source: E:/2015_TM2_20260113
INFO: Output directory: E:/2015_TM2_20260113/san_mateo_test
INFO: Filter demand: True
INFO: Skip EMME copy: False
INFO: Skip setup: True
INFO: ======================================================================
INFO: ======================================================================
INFO: CHECKING PREREQUISITES
INFO: ======================================================================
INFO: County: San Mateo
INFO: ✓ EMME project found: E:\2015_TM2_20260113\emme_project
INFO: ✓ EMME database found: E:\2015_TM2_20260113\emme_project\Database_highway\emmebank
INFO: ✓ Inputs source found: E:\2015_TM2_20260113
INFO: ✓ MAZ data found: E:\2015_TM2_20260113\inputs\landuse\maz_data.csv
INFO: ✓ Tolls found: E:\2015_TM2_20260113\inputs\hwy\tolls.csv
INFO: ✓ AM Demand found: E:\2015_TM2_20260113\demand_matrices\highway\household\TAZ_Demand_AM.omx
INFO: ✓ Config templates found: E:\GitHub\tm2\tm2py\tests\config_templates
INFO: ✓ All prerequisites met!
INFO: Auto-confirm enabled, proceeding with test
INFO: Skipping setup, using existing directory: E:\2015_TM2_20260113\san_mateo_test
INFO: File logging started: E:\2015_TM2_20260113\san_mateo_test\logs\county_test_20260113_175028.log
INFO: Starting test execution...
INFO: ======================================================================
INFO: RUNNING HIGHWAY TEST
INFO: ======================================================================
INFO: Importing CountyHighwayController...
INFO: Initializing controller for San Mateo County...

Emme says: 'File E:\tests\san_mateo_test\emme_project\mtc_emme.emp specified as the startup project does not exist!'

=> updated tm2py\tests\config_templates\fixed_san_mateo_scenario.toml section [emme] so harcoded paths are E:/2015_TM2_20260113/ instead of E:/tests/san_mateo_test

@lmz
Copy link
Member

lmz commented Jan 14, 2026

Next error:

2026-01-13 17:58:21,715 - INFO - File logging started: E:\2015_TM2_20260113\san_mateo_test\logs\county_test_20260113_175821.log
2026-01-13 17:58:21,716 - INFO - Starting test execution...
2026-01-13 17:58:21,716 - INFO - ======================================================================
2026-01-13 17:58:21,716 - INFO - RUNNING HIGHWAY TEST
2026-01-13 17:58:21,716 - INFO - ======================================================================
2026-01-13 17:58:21,717 - INFO - Importing CountyHighwayController...
2026-01-13 17:58:25,603 - INFO - Initializing controller for San Mateo County...
2026-01-13 17:58:25,604 - DEBUG - Scenario config: E:\2015_TM2_20260113\san_mateo_test\config\scenario.toml
2026-01-13 17:58:25,605 - DEBUG - Model config: E:\2015_TM2_20260113\san_mateo_test\config\model.toml
2026-01-13 17:58:25,605 - DEBUG - Run directory: E:\2015_TM2_20260113\san_mateo_test
2026-01-13 17:58:32,621 - ERROR - Test failed with error: 
2026-01-13 17:58:32,621 - DEBUG - Full traceback:
Traceback (most recent call last):
  File "inro\director\application\core.py", line 86, in __new__
TypeError: cannot unpack non-iterable NoneType object

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "E:\GitHub\tm2\tm2py\tm2py\emme\manager.py", line 221, in __init__
    self._modeller = EmmeModeller(self.project)
                     ^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "inro\director\application\core.py", line 89, in __new__
RuntimeError: A project with an unlocked database must be open to initialize Modeller

=> I deleted references to the other non-highway databases in the mtc_emme.emp file

@lmz
Copy link
Member

lmz commented Jan 14, 2026

Next error:

2026-01-13 18:01:50,945 - INFO - File logging started: E:\2015_TM2_20260113\san_mateo_test\logs\county_test_20260113_180150.log
2026-01-13 18:01:50,946 - INFO - Starting test execution...
2026-01-13 18:01:50,946 - INFO - ======================================================================
2026-01-13 18:01:50,947 - INFO - RUNNING HIGHWAY TEST
2026-01-13 18:01:50,947 - INFO - ======================================================================
2026-01-13 18:01:50,947 - INFO - Importing CountyHighwayController...
2026-01-13 18:01:54,635 - INFO - Initializing controller for San Mateo County...
2026-01-13 18:01:54,635 - DEBUG - Scenario config: E:\2015_TM2_20260113\san_mateo_test\config\scenario.toml
2026-01-13 18:01:54,636 - DEBUG - Model config: E:\2015_TM2_20260113\san_mateo_test\config\model.toml
2026-01-13 18:01:54,636 - DEBUG - Run directory: E:\2015_TM2_20260113\san_mateo_test
2026-01-13 18:02:21,727 - ERROR - Test failed with error: Tolls file does not exist: E:\2015_TM2_20260113\san_mateo_test\inputs\hwy\tolls.csv
2026-01-13 18:02:21,728 - DEBUG - Full traceback:
Traceback (most recent call last):
  File "E:\GitHub\tm2\tm2py\tests\run_county_test.py", line 460, in run_test
    controller = CountyHighwayController(
                 ^^^^^^^^^^^^^^^^^^^^^^^^
  File "E:\GitHub\tm2\tm2py\tests\highway_assign_skim_controller.py", line 103, in __init__
    self.controller = RunController(
                      ^^^^^^^^^^^^^^
  File "E:\GitHub\tm2\tm2py\tm2py\controller.py", line 151, in __init__
    self._queue_components(run_components=run_components)
  File "E:\GitHub\tm2\tm2py\tm2py\controller.py", line 407, in _queue_components
    self._add_component_to_queue(0, _c_name)
  File "E:\GitHub\tm2\tm2py\tm2py\controller.py", line 448, in _add_component_to_queue
    _component.validate_inputs()
  File "E:\GitHub\tm2\tm2py\tm2py\components\network\highway\highway_network.py", line 119, in validate_inputs
    raise FileNotFoundError(f"Tolls file does not exist: {toll_file_path}")
FileNotFoundError: Tolls file does not exist: E:\2015_TM2_20260113\san_mateo_test\inputs\hwy\tolls.csv
2026-01-13 18:02:21,731 - ERROR - TEST FAILED

=> copied inputs into test dir

E:\GitHub\tm2\tm2py\tests>mkdir E:\2015_TM2_20260113\san_mateo_test\inputs\hwy\

E:\GitHub\tm2\tm2py\tests>copy E:\2015_TM2_20260113\inputs\hwy\*.csv E:\2015_TM2_20260113\san_mateo_test\inputs\hwy

@lmz
Copy link
Member

lmz commented Jan 14, 2026

Last test:

2026-01-13 18:05:31,022 - INFO - File logging started: E:\2015_TM2_20260113\san_mateo_test\logs\county_test_20260113_180531.log
2026-01-13 18:05:31,023 - INFO - Starting test execution...
2026-01-13 18:05:31,023 - INFO - ======================================================================
2026-01-13 18:05:31,024 - INFO - RUNNING HIGHWAY TEST
2026-01-13 18:05:31,024 - INFO - ======================================================================
2026-01-13 18:05:31,024 - INFO - Importing CountyHighwayController...
2026-01-13 18:05:34,762 - INFO - Initializing controller for San Mateo County...
2026-01-13 18:05:34,763 - DEBUG - Scenario config: E:\2015_TM2_20260113\san_mateo_test\config\scenario.toml
2026-01-13 18:05:34,765 - DEBUG - Model config: E:\2015_TM2_20260113\san_mateo_test\config\model.toml
2026-01-13 18:05:34,765 - DEBUG - Run directory: E:\2015_TM2_20260113\san_mateo_test
2026-01-13 18:06:01,991 - INFO - Starting highway components...
2026-01-13 18:06:01,991 - INFO - Components to run:
2026-01-13 18:06:01,992 - INFO -   1. prepare_network_highway - Prepare network attributes
2026-01-13 18:06:01,992 - INFO -   2. highway - Assignment and skimming
2026-01-13 18:06:01,992 - INFO - Executing controller.run_highway_only()...
2026-01-13 18:06:01,992 - INFO - This may take 5-15 minutes depending on network size...
2026-01-13 18:06:01,992 - INFO - The following steps will occur:
2026-01-13 18:06:01,993 - INFO -   - Loading network from EMME
2026-01-13 18:06:01,993 - INFO -   - Setting network attributes (@useclass, tolls, etc.)
2026-01-13 18:06:01,993 - INFO -   - Loading demand matrices
2026-01-13 18:06:01,993 - INFO -   - Running highway assignment (iterative convergence)
2026-01-13 18:06:01,994 - INFO -   - Computing skims
2026-01-13 18:06:01,994 - INFO -   - Exporting loaded network
2026-01-13 18:06:01,994 - INFO - 
2026-01-13 18:06:01,994 - INFO - Watch the EMME Modeller window for detailed progress...
2026-01-13 18:06:01,994 - INFO - 
2026-01-13 18:06:11,752 - ERROR - Test failed with error: Not enough room for extra attribute @cost_da
2026-01-13 18:06:11,752 - DEBUG - Full traceback:
Traceback (most recent call last):
  File "E:\GitHub\tm2\tm2py\tests\run_county_test.py", line 487, in run_test
    controller.run_highway_only()
  File "E:\GitHub\tm2\tm2py\tests\highway_assign_skim_controller.py", line 130, in run_highway_only
    self.controller.run()
  File "E:\GitHub\tm2\tm2py\tm2py\controller.py", line 293, in run
    self.run_next()
  File "E:\GitHub\tm2\tm2py\tm2py\controller.py", line 356, in run_next
    component.run()
  File "E:\GitHub\tm2\tm2py\tm2py\logger.py", line 643, in wrapper
    value = func(obj, *args, **kwargs)
            ^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "E:\GitHub\tm2\tm2py\tm2py\components\network\highway\highway_network.py", line 86, in run
    self._create_class_attributes(scenario, time)
  File "E:\GitHub\tm2\tm2py\tm2py\components\network\highway\highway_network.py", line 188, in _create_class_attributes
    create_attribute(domain, name, desc, overwrite=True, scenario=scenario)
  File "inro\director\logging\trail.py", line 279, in __trace
  File "standard\inro\emme\data\extra_attribute\create_extra_attribute.py", line 408, in __call__
  File "standard\inro\emme\data\extra_attribute\create_extra_attribute.py", line 468, in _create_extra_attribute
  File "inro\emme\database\scenario.py", line 409, in create_extra_attribute
inro.emme.core.exception.Error: Not enough room for extra attribute @cost_da
2026-01-13 18:06:11,755 - ERROR - TEST FAILED
2026-01-13 18:06:11,756 - ERROR - Check logs in: E:\2015_TM2_20260113\san_mateo_test\logs
2026-01-13 18:06:11,756 - ERROR - Full log: E:\2015_TM2_20260113\san_mateo_test\logs\county_test_20260113_180531.log

Hmm... another problem with the .emp file? @Ennazus - which one did you use?

@Ennazus
Copy link
Collaborator Author

Ennazus commented Jan 14, 2026

Can you attach the configs for this test? I used the San Mateo County emp. The way the test works is it copies the emp to a new location at the beginning.

@lmz
Copy link
Member

lmz commented Jan 14, 2026

These are the local changes to my config:

E:\GitHub\tm2\tm2py>git diff
diff --git a/tests/config_templates/fixed_san_mateo_scenario.toml b/tests/config_templates/fixed_san_mateo_scenario.toml
index a10d02a..11787ed 100644
--- a/tests/config_templates/fixed_san_mateo_scenario.toml
+++ b/tests/config_templates/fixed_san_mateo_scenario.toml
@@ -44,11 +44,11 @@ truck_highway_demand_file = ""
 [emme]
 # EmmeConfig - ALL required fields including database paths and num_processors
 # Use absolute paths to avoid resolution issues
-project_path = "E:/Tests/san_mateo_test/emme_project/mtc_emme.emp"
-highway_database_path = "E:/Tests/san_mateo_test/emme_project/Database_highway/emmebank"
-active_north_database_path = "E:/Tests/san_mateo_test/emme_project/Database_active_north/emmebank"
-active_south_database_path = "E:/Tests/san_mateo_test/emme_project/Database_active_south/emmebank"
-transit_database_path = "E:/Tests/san_mateo_test/emme_project/Database_transit/emmebank"
+project_path = "E:/2015_TM2_20260113/emme_project/mtc_emme.emp"
+highway_database_path = "E:/2015_TM2_20260113/emme_project/Database_highway/emmebank"
+active_north_database_path = "E:/2015_TM2_20260113/emme_project/Database_active_north/emmebank"
+active_south_database_path = "E:/2015_TM2_20260113/emme_project/Database_active_south/emmebank"
+transit_database_path = "E:/2015_TM2_20260113/emme_project/Database_transit/emmebank"
 all_day_scenario_id = 1
 num_processors = "MAX"
 num_processors_transit_skim = "MAX"
diff --git a/tests/county_test_config.toml b/tests/county_test_config.toml
index 04f984a..ac3b125 100644
--- a/tests/county_test_config.toml
+++ b/tests/county_test_config.toml
@@ -10,7 +10,8 @@
 #   emme/emme_project/
 #     mtc_emme.emp (or similar .emp file)
 #     Database_highway/emmebank
-emme_project_source = "M:/Development/Travel Model Two/Supply/Network Creation 2025/from_OSM/SanMateo/7_scenario/emme/emme_project"
+emme_project_source = "E:/2015_TM2_20260113/emme_project"
+# copied from E:\Box\Modeling and Surveys\Development\Travel Model Two Conversion\Model Outputs\2015-tm22-dev-sprint-04\emme_project

 # Input files directory (contains inputs and demand_matrices)
 # This is where tolls, MAZ data, demand matrices, etc. are located
@@ -26,15 +27,15 @@ emme_project_source = "M:/Development/Travel Model Two/Supply/Network Creation 2
 #       TAZ_Demand_MD.omx
 #       TAZ_Demand_PM.omx
 #       TAZ_Demand_EV.omx
-inputs_source = "E:/2015_TM2_20250619"
+inputs_source = "E:/2015_TM2_20260113"

 # Output/test directory where test results will be written
 # This will be created if it doesn't exist
-output_dir = "E:/Tests/san_mateo_test"
+output_dir = "E:/2015_TM2_20260113/san_mateo_test"

 # Crosswalk file for zone detection (maps TAZ/MAZ to counties)
 # Used to automatically detect zone ranges for the specified county
-crosswalk_file = "C:/GitHub/tm2py-utils/tm2py_utils/inputs/maz_taz/mazs_tazs_county_tract_PUMA_2.5.csv"
+crosswalk_file = "E:/GitHub/tm2py/tm2py-utils/tm2py_utils/inputs/maz_taz/mazs_tazs_county_tract_PUMA_2.5.csv"

 [test]
 # County name to test (must match county_name in crosswalk file)

I'd been using network outputs files instead of inputs, which obviscated the fact that some of the processing like create_tod_scenarios was not correctly included in the test.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

feature New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants