# 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