File size: 4,601 Bytes
9d09c45
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
"""Typed schemas and dataclasses for synthetic CityFlow generation."""

from __future__ import annotations

from dataclasses import dataclass, field
from pathlib import Path
from typing import Any, Literal

TopologyType = Literal[
    "rectangular_grid",
    "irregular_grid",
    "arterial_local",
    "ring_road",
    "mixed",
]

DistrictType = Literal["residential", "commercial", "industrial", "mixed"]
DemandIntensity = Literal[
    "normal",
    "moderate_rush",
    "heavy_rush",
    "overload",
    "accident_overload",
]
ScenarioType = Literal[
    "normal",
    "morning_rush",
    "evening_rush",
    "accident",
    "construction",
    "event_spike",
    "district_overload",
]


@dataclass(slots=True, frozen=True)
class TripMix:
    """Trip category distribution."""

    intra_district: float = 0.5
    adjacent_district: float = 0.3
    long_distance: float = 0.2


@dataclass(slots=True)
class DatasetGenerationConfig:
    """Top-level CLI / generation configuration."""

    num_cities: int
    output_dir: Path
    seed: int = 42
    min_districts: int = 6
    max_districts: int = 20
    min_intersections_per_district: int = 4
    max_intersections_per_district: int = 10
    topologies: list[TopologyType] = field(
        default_factory=lambda: ["irregular_grid"]
    )
    scenarios: list[ScenarioType] = field(
        default_factory=lambda: [
            "normal",
            "morning_rush",
            "evening_rush",
            "accident",
            "construction",
            "event_spike",
            "district_overload",
        ]
    )
    intensity_levels: list[DemandIntensity] = field(
        default_factory=lambda: [
            "normal",
            "moderate_rush",
            "heavy_rush",
            "overload",
            "accident_overload",
        ]
    )
    intensity_distribution: dict[DemandIntensity, float] = field(
        default_factory=lambda: {
            "normal": 0.20,
            "moderate_rush": 0.42,
            "heavy_rush": 0.24,
            "overload": 0.10,
            "accident_overload": 0.04,
        }
    )
    global_demand_multiplier: float = 1.25
    scenario_demand_multipliers: dict[str, float] = field(
        default_factory=lambda: {
            "normal": 1.15,
            "morning_rush": 1.35,
            "evening_rush": 1.35,
            "accident": 1.75,
            "construction": 1.55,
            "event_spike": 1.65,
            "district_overload": 1.70,
        }
    )
    ring_diagonal_keep_prob: float = 0.07
    ring_max_diagonal_fraction: float = 0.03
    simulation_steps: int = 3600
    interval: float = 1.0
    save_replay: bool = False
    fail_fast: bool = False


@dataclass(slots=True, frozen=True)
class RoadRecord:
    """Directed road edge record."""

    id: str
    start_intersection: str
    end_intersection: str
    length: float
    speed_limit: float
    num_lanes: int
    points: list[dict[str, float]]
    is_arterial: bool


@dataclass(slots=True)
class CityGraph:
    """Intermediate graph representation for generation."""

    city_id: str
    topology: TopologyType
    seed: int
    intersections: dict[str, tuple[float, float]]
    adjacency: dict[str, set[str]]
    directed_roads: dict[str, RoadRecord]
    roadnet: dict[str, Any]
    arterial_roads: set[str]
    gateway_intersections: set[str] = field(default_factory=set)
    gateway_roads: set[str] = field(default_factory=set)
    inter_district_roads: set[str] = field(default_factory=set)


@dataclass(slots=True)
class DistrictRecord:
    """District-level metadata."""

    id: str
    district_type: DistrictType
    intersections: list[str]
    neighbors: list[str]
    boundary_intersections: list[str]
    entry_roads: list[str]
    exit_roads: list[str]


@dataclass(slots=True)
class DistrictData:
    """District overlay output."""

    intersection_to_district: dict[str, str]
    districts: dict[str, DistrictRecord]
    district_neighbors: dict[str, list[str]]
    boundary_intersections: list[str]
    inter_district_roads: list[str]


@dataclass(slots=True)
class ScenarioPlan:
    """Scenario-specific route demand and impairment configuration."""

    name: ScenarioType
    intensity: DemandIntensity
    seed: int
    trip_multiplier: float
    trip_mix: TripMix
    departure_windows: list[tuple[float, float, float]]
    blocked_roads: set[str] = field(default_factory=set)
    penalized_roads: dict[str, float] = field(default_factory=dict)
    event_district: str | None = None
    overload_district: str | None = None
    metadata: dict[str, Any] = field(default_factory=dict)