Skip to content

gavdoubleu/Rat_network_builder

Repository files navigation

Rat_network_builder

Builds spatial metapopulation ODE graphs for plague simulation. Two graph types:

  • Medieval venue graph — SGU + venue nodes from a MAY world state HDF5
  • HLC landscape graph — 500m×500m grid nodes from the National Historic Landscape Characterisation geodatabase
  • Combined graph — HLC grid with SGU venue sub-networks embedded inside it

Outputs HDF5 consumed by a C++ ODE solver. Standalone — no MAY framework dependency at runtime.

Concept

Each metacompartment is a node running an independent ODE. Nodes are connected by weighted edges encoding migration probability. The C++ solver defines equations and compartment count; this module only builds the spatial graph and venue labels.

Node types (distinction is Python-only — identical to the solver):

  • HLC node — one per 500m×500m HLC grid cell (DominantType in whitelist), at the cell centroid
  • Venue node — one per selected venue, scattered fictitiously within the SGU's bounding square

Edge types (all coexist in one CSR):

  • HLC ↔ HLC: undirected rook adjacency (N/S/E/W), w = 1.0
  • venue ↔ venue: Delaunay triangulation within each SGU, max distance configurable, w = 1.0
  • venue → HLC: each convex-hull venue connects outward to the nearest HLC node, w = 1.0

Repo Structure

Rat_network_builder/
├── explore_db.py              # extract unique landscape types from HLC geodatabase
├── build_hlc_graph.py         # HLC rook-adjacency grid → HDF5
├── build_combined_graph.py    # combined HLC grid + SGU venue nodes → HDF5
├── build_medieval_graph.py    # medieval venue-only graph (SGU + venue nodes) → HDF5
├── requirements.txt
├── data/
│   └── unique_types/          # CSVs of unique HLC type values (from explore_db.py)
│       ├── allowed_types.csv  # whitelist for HLC node inclusion
│       ├── dominant_type.csv
│       ├── dominant_hlc_legend.csv
│       ├── all_hlc_types.csv
│       └── all_nhlc_types.csv
└── src/
    └── compartment_model_network_builder/
        ├── world_reader.py    # reads SGU + venue records from world HDF5
        ├── graph_builder.py   # MetapopulationGraphBuilder — accumulates nodes/edges → CSR
        ├── node_sources.py    # NodeSet dataclass; load_sgu_nodes(); load_venue_nodes()
        ├── venue_positioner.py
        ├── edge_builders.py   # register_edge_builder decorator + radius_ball, delaunay
        └── hdf5_export.py     # export_hdf5() — writes HDF5; always includes n_nodes attr

Scripts

explore_db.py — explore HLC geodatabase

Extracts unique landscape character type values from an Esri SQLite Mobile Geodatabase (.geodatabase). Outputs CSVs to data/unique_types/.

python explore_db.py [--db DATA.geodatabase] [--output-dir data/unique_types/]

Reads four columns: DominantType, DominantHLCLegend, AllHLCTypes, AllNHLCTypes. AllHLCTypes / AllNHLCTypes are semicolon-delimited compound fields; percentage suffixes are stripped automatically.


build_hlc_graph.py — HLC rook-adjacency grid

Nodes = HLC 500m×500m cells whose DominantType is in allowed_types.csv. Edges = undirected rook adjacency (N/S/E/W), w = 1.0. Isolated nodes included.

python build_hlc_graph.py [--db DATA.geodatabase] [--types allowed_types.csv] [--out hlc_rook_graph.h5]

Coordinates converted from OSGB36 BNG (EPSG:27700) → WGS84 (EPSG:4326) via pyproj. venue_id_data stores the OBJECTID of each HLC cell for downstream traceability.


build_combined_graph.py — combined HLC grid + venue nodes

Embeds SGU venue sub-networks from a MAY world state into the HLC rook grid.

Algorithm:

  1. Load HLC cells and SGU/venue data
  2. For each SGU: scatter n_total//2 venues uniformly in a square of side 10√n_total metres (BNG), centred on the SGU centroid
  3. Delete HLC cells whose centroid falls strictly inside any SGU's scatter square
  4. Build rook graph on surviving HLC cells
  5. Per SGU: Delaunay-triangulate venues (max distance MAX_VENUE_DIST_M); connect convex-hull vertices to nearest HLC node in the outward cone (max distance MAX_SCATTER_DIST_M)
  6. Write HDF5 with /nodes/node_type (0=HLC, 1=venue)
python build_combined_graph.py \
    --world /path/to/world_state_medieval.h5 \
    [--db DATA.geodatabase] [--types allowed_types.csv] [--out combined_graph.h5]
Parameter Default Meaning
MAX_VENUE_DIST_M 100.0 Max venue-venue Delaunay edge (m)
MAX_SCATTER_DIST_M 500.0 Max edge-venue → HLC edge (m)
SEED 42 RNG seed for venue scatter

build_medieval_graph.py — medieval venue-only graph

Legacy entry point: SGU centroid nodes + scattered venue nodes, no HLC grid.

python build_medieval_graph.py --world /path/to/world_state_medieval.h5 [--out rat_ode_medieval.h5]
Constant Default Meaning
MAX_SGU_DIST_KM 15.0 SGU-SGU Delaunay pruning distance
SGU_LAMBDA_KM 5.0 λ for SGU-SGU weights
VENUE_CONNECT_M 50.0 Venue-venue connection radius (m)
VENUE_LAMBDA_M 10.0 λ for venue-venue weights (m)
EDGE_MARGIN_M 10.0 Boundary margin for venue-SGU edges
VENUE_SGU_LAMBDA_KM 2.0 λ for venue-SGU weights

HDF5 Output Schema

/metadata/
    n_nodes         int     — always present; total node count
    + any kwargs passed to export_hdf5()
/nodes/
    latitudes       float32 (n,)
    longitudes      float32 (n,)
    venue_id_ptr    int32   (n+1,)   — CSR row pointers into venue_id_data
    venue_id_data   int32   (total,) — flat venue/cell IDs; empty slice = no link
    node_type       int8    (n,)     — 0=HLC, 1=venue  [combined graph only]
/graph/
    csr_row_ptr     int32   (n+1,)
    csr_col_idx     int32   (nnz,)
    edge_weights    float32 (nnz,)

Module Reference

graph_builder.pyMetapopulationGraphBuilder

Central accumulator. All node types are structurally identical to the solver.

b = MetapopulationGraphBuilder()
b.add_nodes(nodeset_a)
b.add_nodes(nodeset_b)
b.add_edges(method='delaunay',    max_dist_km=15.0, lambda_km=5.0)
b.add_edges(method='radius_ball', max_dist_km=1.0,  lambda_km=0.5)
b.add_edge(u, v, weight)          # single manual edge
row_ptr, col_idx, weights = b.build_csr()

Edges deduplicated by (u,v) key; higher weight wins on collision. Graph is always symmetric.

hdf5_export.pyexport_hdf5

export_hdf5(builder, output_path, **metadata_attrs)

Always writes metadata/n_nodes. Additional kwargs (e.g. coord_system='WGS84', n_hlc_nodes=N) are stored as further attributes on /metadata.

world_reader.pyload_world_data

world_data = load_world_data("world_state_medieval.h5")
# world_data.sgus   : list[SguRecord]   — id, name, latitude, longitude
# world_data.venues : list[VenueRecord] — id, latitude, longitude, sgu_name, venue_type

SGUs are the finest geographic level in the file. No MAY framework required.

edge_builders.py — registry

@register_edge_builder('my_method')
def _my(lats, lons, node_indices, **kwargs):
    return [(u, v, weight), ...]   # both (u,v) and (v,u) required

edges = build_edges(lats, lons, idx, method='my_method', max_dist_km=5.0, lambda_km=2.0)

Built-in: 'radius_ball' (cKDTree), 'delaunay' (scipy Delaunay + haversine prune).

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages