Stephen / src /streamlit_app.py
LOLMAN65's picture
Update src/streamlit_app.py
04f44b8 verified
# Collaborative Decision Hub (Python Demo) — Streamlit
# DISCLAIMER: This is a toy simulation for demonstration only. Numbers and models are illustrative.
import streamlit as st
import numpy as np
import pandas as pd
import altair as alt
# -----------------------------
# App config
# -----------------------------
st.set_page_config(page_title="Collaborative Decision Hub (Python Demo)", layout="wide")
# -----------------------------
# Scenario constants (toy values)
# -----------------------------
SCENARIO = {
"airport": "ZNY",
"window_startZ": "14:00Z",
"window_endZ": "18:00Z",
"hours": 4.0,
"arrivals_impacted": 126,
"departures_impacted": 40,
"runway_config_note": "Runway 22R closed",
"airspace_note": "Convective weather causing sector restrictions",
"sector_capacity_per_hour": [32, 32, 36, 36],
"airport_arr_capacity_per_hour": [38, 38, 38, 38],
}
# Economic/operational constants (toy)
EMISSION_FACTOR_CO2_PER_KG_FUEL = 3.16 # kg CO2 per kg fuel
FUEL_COST_PER_KG_USD = 0.8
DELAY_COST_PER_MIN_USD = 75
MISCONNECT_DELAY_THRESHOLD_MIN = 20
MISCONNECT_PER_FLIGHT_PER_MIN_FACTOR = 0.002
# -----------------------------
# Utility helpers
# -----------------------------
def clamp(x, low=0.0, high=1.0):
return max(low, min(high, x))
def normalize(val, ref_max):
if ref_max <= 0:
return 0.0
return clamp(val / ref_max, 0.0, 1.0)
# -----------------------------
# Compute KPIs given controls
# -----------------------------
def compute_metrics(
flights_total,
arrivals,
hours,
sector_capacity_per_hour,
airport_arr_capacity_per_hour,
reroute_pct,
fuel_saved_per_rerouted_flight_kg,
reroute_delay_delta_min_per_flight,
gdp_avg_delay_min,
meter_reduction_pct,
slot_swap_pct,
):
flights_rerouted = int(round(flights_total * reroute_pct / 100.0))
flights_not_rerouted = flights_total - flights_rerouted
avg_delay_min = gdp_avg_delay_min + (reroute_delay_delta_min_per_flight if flights_rerouted > 0 else 0)
avg_delay_min = max(0.0, avg_delay_min)
total_delay_min = flights_total * avg_delay_min
fuel_saved_kg = flights_rerouted * fuel_saved_per_rerouted_flight_kg
co2_saved_t = (fuel_saved_kg * EMISSION_FACTOR_CO2_PER_KG_FUEL) / 1000.0
cost_usd = total_delay_min * DELAY_COST_PER_MIN_USD - fuel_saved_kg * FUEL_COST_PER_KG_USD
avg_sector_capacity = np.mean(sector_capacity_per_hour)
base_arrival_rate = arrivals / hours
effective_arrival_rate = base_arrival_rate * (1.0 - meter_reduction_pct / 100.0)
sector_relief = 0.2 * (reroute_pct / 100.0) * base_arrival_rate
effective_sector_demand = max(0.0, effective_arrival_rate - sector_relief)
overload = max(0.0, effective_sector_demand - avg_sector_capacity)
safety_score = clamp(100.0 - 120.0 * (overload / max(1.0, avg_sector_capacity)), 0.0, 100.0)
avg_airport_capacity = np.mean(airport_arr_capacity_per_hour)
slot_utilization_pct = clamp((effective_arrival_rate / avg_airport_capacity) * 100.0, 0.0, 150.0)
delay_over_threshold = max(0.0, avg_delay_min - MISCONNECT_DELAY_THRESHOLD_MIN)
misconnects_est = flights_total * delay_over_threshold * MISCONNECT_PER_FLIGHT_PER_MIN_FACTOR * (1.0 - slot_swap_pct / 100.0)
otp_rate = clamp(1.0 / (1.0 + np.exp((avg_delay_min - 15.0) / 3.0)), 0.0, 1.0)
return {
"flights_rerouted": flights_rerouted,
"avg_delay_min": avg_delay_min,
"total_delay_min": total_delay_min,
"fuel_saved_kg": fuel_saved_kg,
"co2_saved_t": co2_saved_t,
"cost_usd": cost_usd,
"safety_score": safety_score,
"slot_utilization_pct": slot_utilization_pct,
"misconnects_est": misconnects_est,
"otp_rate": otp_rate,
"effective_sector_demand_per_hr": effective_sector_demand,
"avg_sector_capacity": avg_sector_capacity,
"avg_airport_capacity": avg_airport_capacity,
}
# -----------------------------
# Scoring (weighted multi-objective)
# -----------------------------
def score_solution(metrics, weights):
norm_ontime = clamp(1.0 - normalize(metrics["avg_delay_min"], 30.0))
norm_sust = normalize(metrics["fuel_saved_kg"], 6000.0)
norm_safety = clamp(metrics["safety_score"] / 100.0)
norm_cost = clamp(normalize(-metrics["cost_usd"], 120000.0))
return (
weights["On-time"] * norm_ontime +
weights["Sustainability"] * norm_sust +
weights["Safety"] * norm_safety +
weights["Cost"] * norm_cost
), {
"On-time": norm_ontime,
"Sustainability": norm_sust,
"Safety": norm_safety,
"Cost": norm_cost
}
# -----------------------------
# Grid search recommender
# -----------------------------
def recommend_best(flights_total, arrivals, hours, sector_cap, airport_cap, weights):
candidates = []
for reroute_pct in range(0, 35, 5):
for gdp_delay in range(0, 25, 5):
for meter_pct in range(0, 35, 10):
for slot_swap in range(0, 35, 10):
m = compute_metrics(
flights_total=flights_total,
arrivals=arrivals,
hours=hours,
sector_capacity_per_hour=sector_cap,
airport_arr_capacity_per_hour=airport_cap,
reroute_pct=reroute_pct,
fuel_saved_per_rerouted_flight_kg=150.0,
reroute_delay_delta_min_per_flight=-2.0,
gdp_avg_delay_min=gdp_delay,
meter_reduction_pct=meter_pct,
slot_swap_pct=slot_swap,
)
s, norms = score_solution(m, weights)
candidates.append({
"reroute_pct": reroute_pct,
"gdp_avg_delay_min": gdp_delay,
"meter_reduction_pct": meter_pct,
"slot_swap_pct": slot_swap,
"score": s,
"cost_usd": m["cost_usd"],
"co2_saved_t": m["co2_saved_t"],
"metrics": m,
"norms": norms
})
best = max(candidates, key=lambda x: x["score"]) if candidates else None
return best, candidates
# -----------------------------
# Sidebar: Objective Weights
# -----------------------------
st.sidebar.title("Objective Weights")
w_ontime = st.sidebar.slider("On-time performance", 0.0, 1.0, 0.30, 0.05)
w_cost = st.sidebar.slider("Cost (savings)", 0.0, 1.0, 0.25, 0.05)
w_sust = st.sidebar.slider("Sustainability (CO₂/fuel)", 0.0, 1.0, 0.25, 0.05)
w_safety = st.sidebar.slider("ANSP Safety", 0.0, 1.0, 0.20, 0.05)
w_sum = w_ontime + w_cost + w_sust + w_safety
weights = {
"On-time": w_ontime / w_sum if w_sum else 0.25,
"Cost": w_cost / w_sum if w_sum else 0.25,
"Sustainability": w_sust / w_sum if w_sum else 0.25,
"Safety": w_safety / w_sum if w_sum else 0.25,
}
st.sidebar.caption(f"Weights normalized: On-time {weights['On-time']:.2f}, Cost {weights['Cost']:.2f}, Sustain {weights['Sustainability']:.2f}, Safety {weights['Safety']:.2f}")
# -----------------------------
# Header
# -----------------------------
st.title("Collaborative Decision Hub (Python Demo)")
st.write(f"{SCENARIO['airport']} | Window {SCENARIO['window_startZ']}{SCENARIO['window_endZ']} | {SCENARIO['runway_config_note']} | {SCENARIO['airspace_note']}")
# -----------------------------
# Layout columns
# -----------------------------
left, center, right = st.columns([1.1, 1.2, 1.0])
# LEFT: Scenario + What-if Controls
with left:
st.subheader("Scenario")
st.markdown(
f"- Arrivals impacted: {SCENARIO['arrivals_impacted']}\n"
f"- Departures impacted: {SCENARIO['departures_impacted']}\n"
f"- Sector capacity (hr): {SCENARIO['sector_capacity_per_hour']}\n"
f"- Airport arrivals capacity (hr): {SCENARIO['airport_arr_capacity_per_hour']}"
)
st.subheader("What-if Controls")
reroute_pct = st.slider("Targeted reroutes (% of flights)", 0, 40, 10, 1)
fuel_saved_per_rerouted_flight_kg = st.slider("Fuel saved per rerouted flight (kg)", 0, 400, 150, 10)
reroute_delay_delta = st.slider("Reroute delay change per rerouted flight (min; negative is good)", -5.0, 5.0, -2.0, 0.5)
gdp_avg_delay_min = st.slider("GDP average delay (min)", 0, 30, 15, 1)
meter_reduction_pct = st.slider("Departure metering reduction (%)", 0, 40, 10, 1)
slot_swap_pct = st.slider("Slot swaps applied (%)", 0, 40, 10, 1)
# CENTER: Collaboration (agent chat) + Recommendations
with center:
st.subheader("Agent Collaboration")
flights_total = SCENARIO["arrivals_impacted"] + SCENARIO["departures_impacted"]
m = compute_metrics(
flights_total=flights_total,
arrivals=SCENARIO["arrivals_impacted"],
hours=SCENARIO["hours"],
sector_capacity_per_hour=SCENARIO["sector_capacity_per_hour"],
airport_arr_capacity_per_hour=SCENARIO["airport_arr_capacity_per_hour"],
reroute_pct=reroute_pct,
fuel_saved_per_rerouted_flight_kg=fuel_saved_per_rerouted_flight_kg,
reroute_delay_delta_min_per_flight=reroute_delay_delta,
gdp_avg_delay_min=gdp_avg_delay_min,
meter_reduction_pct=meter_reduction_pct,
slot_swap_pct=slot_swap_pct,
)
score, norms = score_solution(m, weights)
consensus_pct = int(round(score * 100))
with st.container(border=True):
st.markdown("**Airline AI**")
st.write(
f"With {reroute_pct}% reroutes (~{m['flights_rerouted']} flights), "
f"forecast avg delay {m['avg_delay_min']:.1f} min, fuel saved {m['fuel_saved_kg']:.0f} kg "
f"({m['co2_saved_t']:.2f} t CO₂), cost