citymind-engine 1.0.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- citymind/__init__.py +16 -0
- citymind/ai_enhancement/__init__.py +17 -0
- citymind/ai_enhancement/anomaly_detector.py +71 -0
- citymind/ai_enhancement/weight_optimizer.py +90 -0
- citymind/archival/__init__.py +6 -0
- citymind/archival/checksum.py +20 -0
- citymind/archival/writer.py +31 -0
- citymind/monitoring/__init__.py +5 -0
- citymind/monitoring/app.py +185 -0
- citymind/monitoring/dashboard.py +45 -0
- citymind/pipeline.py +107 -0
- citymind/sensors/__init__.py +6 -0
- citymind/sensors/energy_sensor.py +24 -0
- citymind/sensors/traffic_sensor.py +24 -0
- citymind/simulation/__init__.py +5 -0
- citymind/simulation/benchmarks.py +31 -0
- citymind/subsystems/__init__.py +15 -0
- citymind/subsystems/energy.py +54 -0
- citymind/subsystems/infrastructure.py +53 -0
- citymind/subsystems/mobility.py +61 -0
- citymind/subsystems/population.py +58 -0
- citymind/subsystems/transport.py +71 -0
- citymind/uhi.py +132 -0
- citymind/utils/__init__.py +5 -0
- citymind/utils/constants.py +42 -0
- citymind_engine-1.0.0.dist-info/METADATA +725 -0
- citymind_engine-1.0.0.dist-info/RECORD +31 -0
- citymind_engine-1.0.0.dist-info/WHEEL +5 -0
- citymind_engine-1.0.0.dist-info/entry_points.txt +3 -0
- citymind_engine-1.0.0.dist-info/licenses/LICENSE +21 -0
- citymind_engine-1.0.0.dist-info/top_level.txt +1 -0
citymind/__init__.py
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
"""CITYMIND: Urban Human Systems Intelligence Framework.
|
|
2
|
+
|
|
3
|
+
Independent Subsystem Modeling with AI-Enhanced Aggregation.
|
|
4
|
+
Urban Health Index (UHI) composite metric for city governance.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
__version__ = "1.0.0"
|
|
8
|
+
__author__ = "Samir Baladi"
|
|
9
|
+
__email__ = "gitdeeper@gmail.com"
|
|
10
|
+
__license__ = "MIT"
|
|
11
|
+
__doi__ = "10.5281/zenodo.20444647"
|
|
12
|
+
|
|
13
|
+
from citymind.pipeline import CityMindAssessor
|
|
14
|
+
from citymind.uhi import UrbanHealthIndex
|
|
15
|
+
|
|
16
|
+
__all__ = ["CityMindAssessor", "UrbanHealthIndex"]
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
"""AI enhancement layer for CITYMIND.
|
|
2
|
+
|
|
3
|
+
AI is ONLY:
|
|
4
|
+
- a predictive improvement tool
|
|
5
|
+
- a calibration mechanism for system outputs
|
|
6
|
+
- an adaptive weighting and anomaly detection layer
|
|
7
|
+
|
|
8
|
+
AI is NOT:
|
|
9
|
+
- a redefiner of the domain structure
|
|
10
|
+
- a unifier of subsystems
|
|
11
|
+
- a replacement of the underlying model logic
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
from citymind.ai_enhancement.weight_optimizer import AIWeightOptimizer
|
|
15
|
+
from citymind.ai_enhancement.anomaly_detector import AnomalyDetector
|
|
16
|
+
|
|
17
|
+
__all__ = ["AIWeightOptimizer", "AnomalyDetector"]
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
"""Anomaly detection for urban subsystems.
|
|
2
|
+
|
|
3
|
+
AI-assisted anomaly detection for identifying stressed subsystems.
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
import numpy as np
|
|
7
|
+
from typing import Dict, List, Optional
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class AnomalyDetector:
|
|
11
|
+
"""
|
|
12
|
+
Anomaly detection for urban subsystems.
|
|
13
|
+
|
|
14
|
+
Identifies which subsystem is experiencing stress.
|
|
15
|
+
Does NOT modify subsystem models.
|
|
16
|
+
"""
|
|
17
|
+
|
|
18
|
+
def __init__(self, threshold: float = 2.0):
|
|
19
|
+
self.threshold = threshold
|
|
20
|
+
self.history: List[Dict[str, float]] = []
|
|
21
|
+
|
|
22
|
+
def detect(self, scores: Dict[str, float]) -> Dict[str, any]:
|
|
23
|
+
"""
|
|
24
|
+
Detect anomalies in subsystem scores.
|
|
25
|
+
|
|
26
|
+
Returns:
|
|
27
|
+
Dictionary with detection results
|
|
28
|
+
"""
|
|
29
|
+
self.history.append(scores)
|
|
30
|
+
|
|
31
|
+
if len(self.history) < 3:
|
|
32
|
+
return {"anomaly_detected": False, "affected_subsystems": []}
|
|
33
|
+
|
|
34
|
+
anomalies = []
|
|
35
|
+
|
|
36
|
+
for subsystem, score in scores.items():
|
|
37
|
+
recent_scores = [h[subsystem] for h in self.history[-5:] if subsystem in h]
|
|
38
|
+
|
|
39
|
+
if len(recent_scores) >= 3:
|
|
40
|
+
mean = np.mean(recent_scores)
|
|
41
|
+
std = np.std(recent_scores)
|
|
42
|
+
|
|
43
|
+
if std > 0:
|
|
44
|
+
z_score = abs(score - mean) / std
|
|
45
|
+
if z_score > self.threshold:
|
|
46
|
+
anomalies.append({
|
|
47
|
+
"subsystem": subsystem,
|
|
48
|
+
"score": score,
|
|
49
|
+
"deviation": z_score,
|
|
50
|
+
"severity": "HIGH" if z_score > 3 else "MEDIUM"
|
|
51
|
+
})
|
|
52
|
+
|
|
53
|
+
return {
|
|
54
|
+
"anomaly_detected": len(anomalies) > 0,
|
|
55
|
+
"affected_subsystems": anomalies,
|
|
56
|
+
"threshold": self.threshold
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
def get_stressed_subsystem(self, scores: Dict[str, float]) -> Optional[str]:
|
|
60
|
+
"""Identify the most stressed subsystem."""
|
|
61
|
+
result = self.detect(scores)
|
|
62
|
+
|
|
63
|
+
if not result["anomaly_detected"]:
|
|
64
|
+
return None
|
|
65
|
+
|
|
66
|
+
anomalies = result["affected_subsystems"]
|
|
67
|
+
if not anomalies:
|
|
68
|
+
return None
|
|
69
|
+
|
|
70
|
+
# Return subsystem with highest deviation
|
|
71
|
+
return max(anomalies, key=lambda x: x["deviation"])["subsystem"]
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
"""AI-enhanced weight optimization for Urban Health Index.
|
|
2
|
+
|
|
3
|
+
AI strictly bounded to optimization of weights (Σ w_i = 1.0)
|
|
4
|
+
Does NOT modify subsystem models or merge them.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
import numpy as np
|
|
8
|
+
from typing import Dict, Optional
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class AIWeightOptimizer:
|
|
12
|
+
"""
|
|
13
|
+
AI weight optimizer for UHI aggregation.
|
|
14
|
+
|
|
15
|
+
Adjusts weights based on historical performance and anomaly detection.
|
|
16
|
+
Maintains constraint: Σ w_i = 1.0
|
|
17
|
+
"""
|
|
18
|
+
|
|
19
|
+
def __init__(self, learning_rate: float = 0.01):
|
|
20
|
+
self.learning_rate = learning_rate
|
|
21
|
+
self.historical_scores = []
|
|
22
|
+
self.current_weights = {
|
|
23
|
+
"transport": 0.20,
|
|
24
|
+
"population": 0.20,
|
|
25
|
+
"energy": 0.20,
|
|
26
|
+
"mobility": 0.20,
|
|
27
|
+
"infrastructure": 0.20
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
def optimize_weights(self, scores: Dict[str, float]) -> Dict[str, float]:
|
|
31
|
+
"""
|
|
32
|
+
Optimize subsystem weights based on recent performance.
|
|
33
|
+
|
|
34
|
+
Higher performing subsystems receive higher weights.
|
|
35
|
+
Constraint: Σ w_i = 1.0
|
|
36
|
+
"""
|
|
37
|
+
self.historical_scores.append(scores)
|
|
38
|
+
|
|
39
|
+
# Calculate performance-based weights
|
|
40
|
+
total_performance = sum(scores.values())
|
|
41
|
+
if total_performance > 0:
|
|
42
|
+
raw_weights = {k: v / total_performance for k, v in scores.items()}
|
|
43
|
+
else:
|
|
44
|
+
raw_weights = self.current_weights.copy()
|
|
45
|
+
|
|
46
|
+
# Apply learning rate for smooth transition
|
|
47
|
+
for k in self.current_weights:
|
|
48
|
+
self.current_weights[k] = (1 - self.learning_rate) * self.current_weights[k] + \
|
|
49
|
+
self.learning_rate * raw_weights[k]
|
|
50
|
+
|
|
51
|
+
# Ensure sum to 1.0
|
|
52
|
+
total = sum(self.current_weights.values())
|
|
53
|
+
if total > 0:
|
|
54
|
+
self.current_weights = {k: v / total for k, v in self.current_weights.items()}
|
|
55
|
+
|
|
56
|
+
return self.current_weights.copy()
|
|
57
|
+
|
|
58
|
+
def detect_anomaly(self, scores: Dict[str, float], threshold: float = 0.3) -> bool:
|
|
59
|
+
"""Detect anomalies in subsystem scores."""
|
|
60
|
+
if len(self.historical_scores) < 5:
|
|
61
|
+
return False
|
|
62
|
+
|
|
63
|
+
recent_scores = self.historical_scores[-5:]
|
|
64
|
+
mean_scores = {}
|
|
65
|
+
std_scores = {}
|
|
66
|
+
|
|
67
|
+
for subsystem in scores.keys():
|
|
68
|
+
values = [s[subsystem] for s in recent_scores]
|
|
69
|
+
mean_scores[subsystem] = np.mean(values)
|
|
70
|
+
std_scores[subsystem] = np.std(values)
|
|
71
|
+
|
|
72
|
+
for subsystem in scores.keys():
|
|
73
|
+
deviation = abs(scores[subsystem] - mean_scores[subsystem])
|
|
74
|
+
if std_scores[subsystem] > 0 and deviation / std_scores[subsystem] > threshold:
|
|
75
|
+
return True
|
|
76
|
+
|
|
77
|
+
return False
|
|
78
|
+
|
|
79
|
+
def forecast_uhi(self, scores: Dict[str, float], horizon_hours: int = 48) -> float:
|
|
80
|
+
"""Forecast future UHI using simple trend extrapolation."""
|
|
81
|
+
if len(self.historical_scores) < 10:
|
|
82
|
+
return sum(scores.values()) / len(scores)
|
|
83
|
+
|
|
84
|
+
# Simple linear trend
|
|
85
|
+
recent_uhi = [sum(s.values()) / len(s) for s in self.historical_scores[-10:]]
|
|
86
|
+
trend = (recent_uhi[-1] - recent_uhi[0]) / len(recent_uhi)
|
|
87
|
+
|
|
88
|
+
forecast = recent_uhi[-1] + trend * (horizon_hours / 24)
|
|
89
|
+
|
|
90
|
+
return max(0.0, min(forecast, 1.0))
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
"""SHA-256 checksum for data integrity."""
|
|
2
|
+
|
|
3
|
+
import hashlib
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def compute_checksum(filepath: str, algorithm: str = "sha256") -> str:
|
|
7
|
+
"""Compute file checksum for integrity verification."""
|
|
8
|
+
hash_func = hashlib.new(algorithm)
|
|
9
|
+
|
|
10
|
+
with open(filepath, "rb") as f:
|
|
11
|
+
for chunk in iter(lambda: f.read(4096), b""):
|
|
12
|
+
hash_func.update(chunk)
|
|
13
|
+
|
|
14
|
+
return hash_func.hexdigest()
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def verify_integrity(filepath: str, expected_checksum: str) -> bool:
|
|
18
|
+
"""Verify file integrity against expected checksum."""
|
|
19
|
+
actual = compute_checksum(filepath)
|
|
20
|
+
return actual == expected_checksum
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
"""Append-only data archival writer."""
|
|
2
|
+
|
|
3
|
+
import json
|
|
4
|
+
import csv
|
|
5
|
+
from datetime import datetime
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
from typing import Dict, Any
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class ArchivalWriter:
|
|
11
|
+
"""Append-only data archival writer."""
|
|
12
|
+
|
|
13
|
+
def __init__(self, base_path: str = "./data/archival"):
|
|
14
|
+
self.base_path = Path(base_path)
|
|
15
|
+
self.base_path.mkdir(parents=True, exist_ok=True)
|
|
16
|
+
|
|
17
|
+
def write_json(self, data: Dict[str, Any], filename: str):
|
|
18
|
+
"""Write data to JSON file with timestamp."""
|
|
19
|
+
filepath = self.base_path / f"{filename}_{datetime.now().strftime('%Y%m%d')}.json"
|
|
20
|
+
|
|
21
|
+
record = {
|
|
22
|
+
"timestamp": datetime.now().isoformat(),
|
|
23
|
+
"data": data
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
with open(filepath, "a") as f:
|
|
27
|
+
f.write(json.dumps(record) + "\n")
|
|
28
|
+
|
|
29
|
+
def write_uhi_record(self, uhi_result: Dict):
|
|
30
|
+
"""Write UHI assessment record."""
|
|
31
|
+
self.write_json(uhi_result, "uhi_assessment")
|
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
"""Streamlit application entry point for CITYMIND dashboard."""
|
|
2
|
+
|
|
3
|
+
import streamlit as st
|
|
4
|
+
import plotly.graph_objects as go
|
|
5
|
+
import plotly.express as px
|
|
6
|
+
import pandas as pd
|
|
7
|
+
import numpy as np
|
|
8
|
+
from datetime import datetime, timedelta
|
|
9
|
+
|
|
10
|
+
from citymind.pipeline import CityMindAssessor
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def run_dashboard():
|
|
14
|
+
"""Launch the CITYMIND monitoring dashboard."""
|
|
15
|
+
st.set_page_config(
|
|
16
|
+
page_title="CITYMIND Dashboard",
|
|
17
|
+
page_icon="🏙️",
|
|
18
|
+
layout="wide"
|
|
19
|
+
)
|
|
20
|
+
|
|
21
|
+
st.title("🏙️ CITYMIND - Urban Health Intelligence Dashboard")
|
|
22
|
+
st.markdown("*Independent Subsystem Modeling with AI-Enhanced Aggregation*")
|
|
23
|
+
|
|
24
|
+
# Initialize assessor
|
|
25
|
+
assessor = CityMindAssessor()
|
|
26
|
+
|
|
27
|
+
# Sidebar
|
|
28
|
+
with st.sidebar:
|
|
29
|
+
st.header("⚙️ Controls")
|
|
30
|
+
refresh = st.button("🔄 Refresh Data", type="primary")
|
|
31
|
+
ai_enabled = st.checkbox("🤖 AI Enhancement", value=True)
|
|
32
|
+
|
|
33
|
+
st.markdown("---")
|
|
34
|
+
st.markdown("### 🏗️ Urban Subsystems")
|
|
35
|
+
st.markdown("- 🚗 Transport Flow")
|
|
36
|
+
st.markdown("- 👥 Population Density")
|
|
37
|
+
st.markdown("- ⚡ Energy Consumption")
|
|
38
|
+
st.markdown("- 🚶 Mobility Behavior")
|
|
39
|
+
st.markdown("- 🏗️ Infrastructure Load")
|
|
40
|
+
|
|
41
|
+
st.markdown("---")
|
|
42
|
+
st.markdown("### 📊 Governance Thresholds")
|
|
43
|
+
st.markdown("🟢 UHI ≥ 0.85 - Optimized")
|
|
44
|
+
st.markdown("🟠 0.70 ≤ UHI < 0.85 - Stressed")
|
|
45
|
+
st.markdown("🟠 0.55 ≤ UHI < 0.70 - Mitigation")
|
|
46
|
+
st.markdown("🔴 UHI < 0.55 - Critical")
|
|
47
|
+
|
|
48
|
+
# Get results
|
|
49
|
+
if ai_enabled:
|
|
50
|
+
assessor.ai_enhancement = True
|
|
51
|
+
else:
|
|
52
|
+
assessor.ai_enhancement = False
|
|
53
|
+
|
|
54
|
+
result = assessor.evaluate()
|
|
55
|
+
|
|
56
|
+
# Main dashboard
|
|
57
|
+
col1, col2, col3 = st.columns(3)
|
|
58
|
+
|
|
59
|
+
with col1:
|
|
60
|
+
# UHI Gauge
|
|
61
|
+
fig = go.Figure(go.Indicator(
|
|
62
|
+
mode="gauge+number",
|
|
63
|
+
value=result.uhi_result.uhi,
|
|
64
|
+
title={"text": "Urban Health Index (UHI)"},
|
|
65
|
+
gauge={
|
|
66
|
+
"axis": {"range": [0, 1], "tickwidth": 1},
|
|
67
|
+
"bar": {"color": _get_uhi_color(result.uhi_result.uhi)},
|
|
68
|
+
"steps": [
|
|
69
|
+
{"range": [0, 0.55], "color": "#C0392B", "name": "Critical"},
|
|
70
|
+
{"range": [0.55, 0.70], "color": "#E67E22", "name": "Mitigation"},
|
|
71
|
+
{"range": [0.70, 0.85], "color": "#F39C12", "name": "Stressed"},
|
|
72
|
+
{"range": [0.85, 1.0], "color": "#27AE60", "name": "Optimized"}
|
|
73
|
+
],
|
|
74
|
+
"threshold": {
|
|
75
|
+
"line": {"color": "black", "width": 4},
|
|
76
|
+
"thickness": 0.75,
|
|
77
|
+
"value": result.uhi_result.uhi
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
))
|
|
81
|
+
fig.update_layout(height=300)
|
|
82
|
+
st.plotly_chart(fig, use_container_width=True)
|
|
83
|
+
|
|
84
|
+
# Signal badge
|
|
85
|
+
signal_color = {
|
|
86
|
+
"🟢 OPTIMIZED_URBAN_FLOW": "green",
|
|
87
|
+
"🟠 STRESSED_SUBSYSTEM_WARNING": "orange",
|
|
88
|
+
"🟠 SYSTEMIC_MITIGATION_PHASE": "orange",
|
|
89
|
+
"🔴 CRITICAL_INFRASTRUCTURE_BREACH": "red"
|
|
90
|
+
}.get(result.uhi_result.signal.value, "gray")
|
|
91
|
+
|
|
92
|
+
st.markdown(f"""
|
|
93
|
+
<div style="background-color: {signal_color}20; padding: 15px; border-radius: 10px; text-align: center;">
|
|
94
|
+
<h2>{result.uhi_result.signal.value}</h2>
|
|
95
|
+
</div>
|
|
96
|
+
""", unsafe_allow_html=True)
|
|
97
|
+
|
|
98
|
+
with col2:
|
|
99
|
+
st.metric("🚗 Transport", f"{result.uhi_result.transport_score:.3f}")
|
|
100
|
+
st.metric("👥 Population", f"{result.uhi_result.population_score:.3f}")
|
|
101
|
+
st.metric("⚡ Energy", f"{result.uhi_result.energy_score:.3f}")
|
|
102
|
+
|
|
103
|
+
with col3:
|
|
104
|
+
st.metric("🚶 Mobility", f"{result.uhi_result.mobility_score:.3f}")
|
|
105
|
+
st.metric("🏗️ Infrastructure", f"{result.uhi_result.infrastructure_score:.3f}")
|
|
106
|
+
st.metric("🤖 AI Anomaly", "Detected" if result.anomaly_detected else "None")
|
|
107
|
+
|
|
108
|
+
# Subsystem scores chart
|
|
109
|
+
st.markdown("---")
|
|
110
|
+
st.subheader("📊 Subsystem Performance")
|
|
111
|
+
|
|
112
|
+
scores_df = pd.DataFrame({
|
|
113
|
+
"Subsystem": ["Transport", "Population", "Energy", "Mobility", "Infrastructure"],
|
|
114
|
+
"Score": [
|
|
115
|
+
result.uhi_result.transport_score,
|
|
116
|
+
result.uhi_result.population_score,
|
|
117
|
+
result.uhi_result.energy_score,
|
|
118
|
+
result.uhi_result.mobility_score,
|
|
119
|
+
result.uhi_result.infrastructure_score
|
|
120
|
+
],
|
|
121
|
+
"Weight": [
|
|
122
|
+
result.ai_weights.get("transport", 0.2),
|
|
123
|
+
result.ai_weights.get("population", 0.2),
|
|
124
|
+
result.ai_weights.get("energy", 0.2),
|
|
125
|
+
result.ai_weights.get("mobility", 0.2),
|
|
126
|
+
result.ai_weights.get("infrastructure", 0.2)
|
|
127
|
+
]
|
|
128
|
+
})
|
|
129
|
+
|
|
130
|
+
fig = px.bar(scores_df, x="Subsystem", y="Score", color="Score",
|
|
131
|
+
color_continuous_scale="RdYlGn", range_color=[0, 1],
|
|
132
|
+
title="Subsystem Health Scores")
|
|
133
|
+
fig.update_layout(height=400)
|
|
134
|
+
st.plotly_chart(fig, use_container_width=True)
|
|
135
|
+
|
|
136
|
+
# AI weights
|
|
137
|
+
col1, col2 = st.columns(2)
|
|
138
|
+
|
|
139
|
+
with col1:
|
|
140
|
+
st.subheader("🤖 AI-Optimized Weights")
|
|
141
|
+
weights_df = pd.DataFrame({
|
|
142
|
+
"Subsystem": list(result.ai_weights.keys()),
|
|
143
|
+
"Weight": list(result.ai_weights.values())
|
|
144
|
+
})
|
|
145
|
+
fig = px.pie(weights_df, values="Weight", names="Subsystem", title="Weight Distribution")
|
|
146
|
+
fig.update_layout(height=350)
|
|
147
|
+
st.plotly_chart(fig, use_container_width=True)
|
|
148
|
+
|
|
149
|
+
with col2:
|
|
150
|
+
st.subheader("📈 Forecast")
|
|
151
|
+
if result.forecast_uhi:
|
|
152
|
+
st.metric("48h UHI Forecast", f"{result.forecast_uhi:.3f}")
|
|
153
|
+
trend = result.forecast_uhi - result.uhi_result.uhi
|
|
154
|
+
st.metric("Trend", f"{trend:+.3f}", delta=trend)
|
|
155
|
+
else:
|
|
156
|
+
st.info("AI enhancement disabled. Enable AI for forecasting.")
|
|
157
|
+
|
|
158
|
+
# Governance action
|
|
159
|
+
st.subheader("📋 Recommended Action")
|
|
160
|
+
action = assessor.uhi.get_governance_action(result.uhi_result.signal)
|
|
161
|
+
st.info(action)
|
|
162
|
+
|
|
163
|
+
# Footer
|
|
164
|
+
st.markdown("---")
|
|
165
|
+
st.markdown("""
|
|
166
|
+
<div style="text-align: center; color: gray; font-size: 12px;">
|
|
167
|
+
CITYMIND v1.0.0 | Urban Human Systems Intelligence |
|
|
168
|
+
Independent Subsystem Modeling with AI-Enhanced Aggregation
|
|
169
|
+
</div>
|
|
170
|
+
""", unsafe_allow_html=True)
|
|
171
|
+
|
|
172
|
+
|
|
173
|
+
def _get_uhi_color(uhi: float) -> str:
|
|
174
|
+
if uhi >= 0.85:
|
|
175
|
+
return "#27AE60"
|
|
176
|
+
elif uhi >= 0.70:
|
|
177
|
+
return "#F39C12"
|
|
178
|
+
elif uhi >= 0.55:
|
|
179
|
+
return "#E67E22"
|
|
180
|
+
else:
|
|
181
|
+
return "#C0392B"
|
|
182
|
+
|
|
183
|
+
|
|
184
|
+
if __name__ == "__main__":
|
|
185
|
+
run_dashboard()
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
"""CSII governance dashboard layout."""
|
|
2
|
+
|
|
3
|
+
import streamlit as st
|
|
4
|
+
import plotly.graph_objects as go
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def create_dashboard(uhi_value: float, signal: str, scores: dict):
|
|
8
|
+
"""Create full UHI governance dashboard."""
|
|
9
|
+
col1, col2, col3 = st.columns(3)
|
|
10
|
+
|
|
11
|
+
with col1:
|
|
12
|
+
fig = go.Figure(go.Indicator(
|
|
13
|
+
mode="gauge+number",
|
|
14
|
+
value=uhi_value,
|
|
15
|
+
title={"text": "UHI Score"},
|
|
16
|
+
gauge={
|
|
17
|
+
"axis": {"range": [0, 1]},
|
|
18
|
+
"bar": {"color": _get_color(uhi_value)},
|
|
19
|
+
"steps": [
|
|
20
|
+
{"range": [0, 0.55], "color": "red"},
|
|
21
|
+
{"range": [0.55, 0.70], "color": "orange"},
|
|
22
|
+
{"range": [0.70, 0.85], "color": "yellow"},
|
|
23
|
+
{"range": [0.85, 1.0], "color": "green"},
|
|
24
|
+
]
|
|
25
|
+
}
|
|
26
|
+
))
|
|
27
|
+
st.plotly_chart(fig)
|
|
28
|
+
|
|
29
|
+
with col2:
|
|
30
|
+
st.metric("Urban Signal", signal)
|
|
31
|
+
|
|
32
|
+
with col3:
|
|
33
|
+
for name, score in scores.items():
|
|
34
|
+
st.metric(name.capitalize(), f"{score:.3f}")
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def _get_color(uhi: float) -> str:
|
|
38
|
+
if uhi >= 0.85:
|
|
39
|
+
return "green"
|
|
40
|
+
elif uhi >= 0.70:
|
|
41
|
+
return "yellow"
|
|
42
|
+
elif uhi >= 0.55:
|
|
43
|
+
return "orange"
|
|
44
|
+
else:
|
|
45
|
+
return "red"
|
citymind/pipeline.py
ADDED
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
"""CITYMIND main assessment pipeline."""
|
|
2
|
+
|
|
3
|
+
from dataclasses import dataclass
|
|
4
|
+
from typing import Dict, Optional
|
|
5
|
+
|
|
6
|
+
import numpy as np
|
|
7
|
+
|
|
8
|
+
from citymind.uhi import UrbanHealthIndex, UHIResult, UrbanSignal
|
|
9
|
+
from citymind.subsystems.transport import TransportSubsystem
|
|
10
|
+
from citymind.subsystems.population import PopulationSubsystem
|
|
11
|
+
from citymind.subsystems.energy import EnergySubsystem
|
|
12
|
+
from citymind.subsystems.mobility import MobilitySubsystem
|
|
13
|
+
from citymind.subsystems.infrastructure import InfrastructureSubsystem
|
|
14
|
+
from citymind.ai_enhancement.weight_optimizer import AIWeightOptimizer
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
@dataclass
|
|
18
|
+
class PipelineResult:
|
|
19
|
+
"""Complete pipeline assessment result."""
|
|
20
|
+
uhi_result: UHIResult
|
|
21
|
+
subsystem_scores: Dict[str, float]
|
|
22
|
+
ai_weights: Dict[str, float]
|
|
23
|
+
anomaly_detected: bool
|
|
24
|
+
forecast_uhi: Optional[float]
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
class CityMindAssessor:
|
|
28
|
+
"""
|
|
29
|
+
CITYMIND main assessment pipeline.
|
|
30
|
+
|
|
31
|
+
Integrates five independent urban subsystems with AI enhancement
|
|
32
|
+
strictly bounded to optimization and anomaly detection.
|
|
33
|
+
"""
|
|
34
|
+
|
|
35
|
+
def __init__(
|
|
36
|
+
self,
|
|
37
|
+
config: Optional[Dict] = None,
|
|
38
|
+
ai_enhancement: bool = True
|
|
39
|
+
):
|
|
40
|
+
self.config = config or {}
|
|
41
|
+
self.ai_enhancement = ai_enhancement
|
|
42
|
+
|
|
43
|
+
# Initialize five independent subsystems
|
|
44
|
+
self.transport = TransportSubsystem()
|
|
45
|
+
self.population = PopulationSubsystem()
|
|
46
|
+
self.energy = EnergySubsystem()
|
|
47
|
+
self.mobility = MobilitySubsystem()
|
|
48
|
+
self.infrastructure = InfrastructureSubsystem()
|
|
49
|
+
|
|
50
|
+
# Initialize AI enhancement layer
|
|
51
|
+
self.ai_weight_optimizer = AIWeightOptimizer() if ai_enhancement else None
|
|
52
|
+
self.uhi = UrbanHealthIndex(ai_weight_optimization=ai_enhancement)
|
|
53
|
+
|
|
54
|
+
def evaluate(self) -> PipelineResult:
|
|
55
|
+
"""
|
|
56
|
+
Run full CITYMIND assessment pipeline.
|
|
57
|
+
|
|
58
|
+
Each subsystem is evaluated independently. AI only enhances
|
|
59
|
+
weight optimization and anomaly detection.
|
|
60
|
+
"""
|
|
61
|
+
# Evaluate each subsystem independently
|
|
62
|
+
transport_score = self.transport.compute()
|
|
63
|
+
population_score = self.population.compute()
|
|
64
|
+
energy_score = self.energy.compute()
|
|
65
|
+
mobility_score = self.mobility.compute()
|
|
66
|
+
infrastructure_score = self.infrastructure.compute()
|
|
67
|
+
|
|
68
|
+
subsystem_scores = {
|
|
69
|
+
"transport": transport_score,
|
|
70
|
+
"population": population_score,
|
|
71
|
+
"energy": energy_score,
|
|
72
|
+
"mobility": mobility_score,
|
|
73
|
+
"infrastructure": infrastructure_score
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
# AI-enhanced weight optimization
|
|
77
|
+
if self.ai_enhancement and self.ai_weight_optimizer:
|
|
78
|
+
weights = self.ai_weight_optimizer.optimize_weights(subsystem_scores)
|
|
79
|
+
anomaly_detected = self.ai_weight_optimizer.detect_anomaly(subsystem_scores)
|
|
80
|
+
forecast_uhi = self.ai_weight_optimizer.forecast_uhi(subsystem_scores)
|
|
81
|
+
else:
|
|
82
|
+
weights = None
|
|
83
|
+
anomaly_detected = False
|
|
84
|
+
forecast_uhi = None
|
|
85
|
+
|
|
86
|
+
# Compute UHI composite index
|
|
87
|
+
uhi_result = self.uhi.compute(
|
|
88
|
+
transport_score=transport_score,
|
|
89
|
+
population_score=population_score,
|
|
90
|
+
energy_score=energy_score,
|
|
91
|
+
mobility_score=mobility_score,
|
|
92
|
+
infrastructure_score=infrastructure_score,
|
|
93
|
+
weights=weights
|
|
94
|
+
)
|
|
95
|
+
|
|
96
|
+
return PipelineResult(
|
|
97
|
+
uhi_result=uhi_result,
|
|
98
|
+
subsystem_scores=subsystem_scores,
|
|
99
|
+
ai_weights=weights or self.uhi.DEFAULT_WEIGHTS,
|
|
100
|
+
anomaly_detected=anomaly_detected,
|
|
101
|
+
forecast_uhi=forecast_uhi
|
|
102
|
+
)
|
|
103
|
+
|
|
104
|
+
def get_urban_signal(self) -> UrbanSignal:
|
|
105
|
+
"""Get current urban governance signal."""
|
|
106
|
+
result = self.evaluate()
|
|
107
|
+
return result.uhi_result.signal
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
"""Energy consumption sensor data processor."""
|
|
2
|
+
|
|
3
|
+
import numpy as np
|
|
4
|
+
from typing import Dict
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class EnergySensor:
|
|
8
|
+
"""Energy grid sensor data processor."""
|
|
9
|
+
|
|
10
|
+
def __init__(self):
|
|
11
|
+
pass
|
|
12
|
+
|
|
13
|
+
def process(self, demand: float, supply: float) -> Dict:
|
|
14
|
+
"""Process energy data."""
|
|
15
|
+
return {
|
|
16
|
+
"demand": demand,
|
|
17
|
+
"supply": supply,
|
|
18
|
+
"deficit_ratio": max(0, (demand - supply) / supply) if supply > 0 else 1.0,
|
|
19
|
+
"score": (supply - demand) / supply if supply > demand else 0.0
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
def detect_deficit(self, demand: float, supply: float) -> bool:
|
|
23
|
+
"""Detect energy deficit."""
|
|
24
|
+
return demand > supply * 0.95
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
"""Traffic sensor data processor."""
|
|
2
|
+
|
|
3
|
+
import numpy as np
|
|
4
|
+
from typing import Dict, Optional
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class TrafficSensor:
|
|
8
|
+
"""Traffic flow sensor data processor."""
|
|
9
|
+
|
|
10
|
+
def __init__(self, sampling_rate: float = 60.0):
|
|
11
|
+
self.sampling_rate = sampling_rate
|
|
12
|
+
|
|
13
|
+
def process(self, raw_data: np.ndarray) -> Dict:
|
|
14
|
+
"""Process raw traffic sensor data."""
|
|
15
|
+
return {
|
|
16
|
+
"volume": float(np.mean(raw_data)),
|
|
17
|
+
"peak": float(np.max(raw_data)),
|
|
18
|
+
"congestion_ratio": float(np.mean(raw_data) / 10000),
|
|
19
|
+
"timestamp": None
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
def detect_congestion(self, volume: float, capacity: float = 10000) -> bool:
|
|
23
|
+
"""Detect traffic congestion."""
|
|
24
|
+
return volume / capacity > 0.85
|