bgamazay commited on
Commit
20e589d
·
verified ·
1 Parent(s): ba1686c

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +47 -27
app.py CHANGED
@@ -43,7 +43,7 @@ def load_data():
43
  # Sanitize Headers (removes hidden spaces)
44
  df.columns = df.columns.str.strip()
45
 
46
- # Validation: Check if columns exist
47
  required_cols = ['Power (MW)', 'Carbon Intensity', 'Annual Million tCO2']
48
  missing = [c for c in required_cols if c not in df.columns]
49
  if missing:
@@ -64,13 +64,28 @@ def load_data():
64
  df['Carbon Intensity'] = df['Carbon Intensity'].apply(clean_numeric)
65
  df['Annual Million tCO2'] = df['Annual Million tCO2'].apply(clean_numeric)
66
 
67
- # --- MATH CHECK (Verification) ---
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
68
  # Formula: MW * 8760 hours * (Intensity kg/MWh / 1000 to get tonnes) / 1,000,000 to get Million Tonnes
69
  # We calculate this to double-check the CSV's reported numbers
70
  df['Calculated_Mt'] = (df['Power (MW)'] * 8760 * df['Carbon Intensity']) / 1e9
71
 
72
  # Use the Reported number, but normalize it (Handle the 13,093 vs 13.1 issue)
73
- # If the number is > 100, it's likely in Kilotonnes, so divide by 1000
74
  df['Emissions_Mt'] = df['Annual Million tCO2'].apply(lambda x: x / 1000 if x > 100 else x)
75
 
76
  # --- Geocoding (Manual Overrides for missing Lat/Long) ---
@@ -86,7 +101,7 @@ def load_data():
86
  mask = df['Project'].astype(str).str.contains(key, case=False, na=False)
87
  df.loc[mask, ['Latitude', 'Longitude']] = coords
88
 
89
- # Parse DMS coordinates (e.g., 42°40'28"N) if they exist
90
  def dms_to_dd(val):
91
  if isinstance(val, str) and '°' in val:
92
  try:
@@ -101,7 +116,6 @@ def load_data():
101
  df[col] = df[col].apply(dms_to_dd)
102
  df[col] = pd.to_numeric(df[col], errors='coerce')
103
 
104
- # Drop rows that still have no location
105
  df = df.dropna(subset=['Latitude', 'Longitude'])
106
 
107
  # --- Enrichment for Tooltip ---
@@ -128,22 +142,26 @@ df = load_data()
128
  st.sidebar.header("🌍 Frontier AI Emissions")
129
  st.sidebar.markdown("Filter the map to analyze the carbon footprint of planned AI infrastructure.")
130
 
131
- # Filters
 
 
132
  grid_filter = st.sidebar.multiselect(
133
  "Connection Type",
134
- options=df['Grid Status'].unique(),
135
- default=df['Grid Status'].unique()
136
  )
137
 
 
 
138
  owner_filter = st.sidebar.multiselect(
139
  "Owner",
140
- options=df['Owner'].unique(),
141
- default=df['Owner'].unique()
142
  )
143
 
144
  # Apply filters
145
  filtered_df = df[
146
- (df['Grid Status'].isin(grid_filter)) &
147
  (df['Owner'].isin(owner_filter))
148
  ]
149
 
@@ -158,7 +176,7 @@ st.sidebar.markdown("### 📊 Aggregate Impact")
158
 
159
  col1, col2 = st.sidebar.columns(2)
160
  col1.metric("Total Power", f"{total_power:.1f} GW", help="Total capacity of visible projects")
161
- col2.metric("Annual Emissions", f"{total_emissions:.1f} Mt", help="Million Tonnes CO2/year")
162
 
163
  st.sidebar.markdown(f"""
164
  <div class="metric-card">
@@ -173,8 +191,8 @@ st.sidebar.markdown(f"**Avg Carbon Intensity:** {avg_intensity:.0f} kg/MWh")
173
  st.title("The Carbon Footprint of Frontier AI")
174
  st.markdown(
175
  "This map visualizes the annual emissions of major planned AI data centers. "
176
- "**Bubble size** represents CO₂ emissions. **Color** indicates grid status "
177
- "(<span style='color:#FF4136'><b>Red = Off-Grid/Gas</b></span>, <span style='color:#0074D9'><b>Blue = Grid</b></span>).",
178
  unsafe_allow_html=True
179
  )
180
 
@@ -195,20 +213,19 @@ layer = pdk.Layer(
195
  get_line_color=[0, 0, 0],
196
  )
197
 
198
- # Tooltip
199
  tooltip = {
200
  "html": """
201
- <div style="font-family: sans-serif; padding: 10px; color: white;">
202
- <h3 style="margin:0;">{Project}</h3>
203
- <hr style="border-top: 1px solid #555;">
204
  <b>Owner:</b> {Owner}<br/>
205
- <b>Location:</b> {Location}<br/>
206
  <b>Power:</b> {Power (MW)} MW<br/>
207
- <b>Status:</b> {Grid Status}<br/>
208
  <br/>
209
- <b style="font-size: 1.1em; color: #ffcccb;">Annual Emissions: {Emissions_Mt} MtCO₂</b><br/>
210
- <i style="font-size: 0.9em;">(Intensity: {Carbon Intensity} kg/MWh)</i>
211
- <hr style="border-top: 1px dashed #555;">
212
  <b>🚗 Equal to:</b> {Cars_Equivalent_Millions} Million Cars<br/>
213
  <b>🏭 Equal to:</b> {Coal_Plants_Equivalent} Coal Power Plants
214
  </div>
@@ -217,13 +234,16 @@ tooltip = {
217
  "backgroundColor": "#1E1E1E",
218
  "border": "1px solid #333",
219
  "borderRadius": "8px",
220
- "color": "white"
 
221
  }
222
  }
223
 
224
  # Render Map
 
 
225
  st.pydeck_chart(pdk.Deck(
226
- map_style="mapbox://styles/mapbox/dark-v11", # Or use default 'dark' if no token
227
  initial_view_state=pdk.ViewState(
228
  latitude=39.8,
229
  longitude=-98.6,
@@ -238,6 +258,6 @@ st.pydeck_chart(pdk.Deck(
238
  st.markdown("---")
239
  st.caption(
240
  "**Methodology:** Emissions calculated based on publicly stated power capacity (MW) and regional/source-specific carbon intensity. "
241
- "Car equivalents assume 4.6 metric tonnes CO₂ per passenger vehicle per year (EPA). "
242
- "Coal plant equivalent assumes ~4.0 MtCO₂/year for a typical plant."
243
  )
 
43
  # Sanitize Headers (removes hidden spaces)
44
  df.columns = df.columns.str.strip()
45
 
46
+ # Validation
47
  required_cols = ['Power (MW)', 'Carbon Intensity', 'Annual Million tCO2']
48
  missing = [c for c in required_cols if c not in df.columns]
49
  if missing:
 
64
  df['Carbon Intensity'] = df['Carbon Intensity'].apply(clean_numeric)
65
  df['Annual Million tCO2'] = df['Annual Million tCO2'].apply(clean_numeric)
66
 
67
+ # --- CLEAN OWNER NAMES ---
68
+ # Remove "#confident", "#likely", etc.
69
+ if 'Owner' in df.columns:
70
+ df['Owner'] = df['Owner'].astype(str).str.split('#').str[0].str.strip()
71
+
72
+ # --- SIMPLIFY GRID STATUS ---
73
+ # Create a clean category for the filter (Grid vs Off-Grid vs Hybrid)
74
+ def simplify_status(status):
75
+ s = str(status).lower()
76
+ if 'off-grid' in s or 'gas' in s: return "Off-Grid / Fossil"
77
+ elif 'hybrid' in s or 'nuclear' in s: return "Hybrid / Nuclear"
78
+ elif 'grid' in s: return "Grid Connected"
79
+ else: return "Unknown"
80
+
81
+ df['Simple_Connection'] = df['Grid Status'].apply(simplify_status)
82
+
83
+ # --- MATH CHECK ---
84
  # Formula: MW * 8760 hours * (Intensity kg/MWh / 1000 to get tonnes) / 1,000,000 to get Million Tonnes
85
  # We calculate this to double-check the CSV's reported numbers
86
  df['Calculated_Mt'] = (df['Power (MW)'] * 8760 * df['Carbon Intensity']) / 1e9
87
 
88
  # Use the Reported number, but normalize it (Handle the 13,093 vs 13.1 issue)
 
89
  df['Emissions_Mt'] = df['Annual Million tCO2'].apply(lambda x: x / 1000 if x > 100 else x)
90
 
91
  # --- Geocoding (Manual Overrides for missing Lat/Long) ---
 
101
  mask = df['Project'].astype(str).str.contains(key, case=False, na=False)
102
  df.loc[mask, ['Latitude', 'Longitude']] = coords
103
 
104
+ # Parse DMS coordinates
105
  def dms_to_dd(val):
106
  if isinstance(val, str) and '°' in val:
107
  try:
 
116
  df[col] = df[col].apply(dms_to_dd)
117
  df[col] = pd.to_numeric(df[col], errors='coerce')
118
 
 
119
  df = df.dropna(subset=['Latitude', 'Longitude'])
120
 
121
  # --- Enrichment for Tooltip ---
 
142
  st.sidebar.header("🌍 Frontier AI Emissions")
143
  st.sidebar.markdown("Filter the map to analyze the carbon footprint of planned AI infrastructure.")
144
 
145
+ # Filters: Connection Type (Simplified)
146
+ # We sort them to ensure consistent order
147
+ connection_options = sorted(df['Simple_Connection'].unique())
148
  grid_filter = st.sidebar.multiselect(
149
  "Connection Type",
150
+ options=connection_options,
151
+ default=connection_options
152
  )
153
 
154
+ # Filters: Owner (Cleaned)
155
+ owner_options = sorted(df['Owner'].unique())
156
  owner_filter = st.sidebar.multiselect(
157
  "Owner",
158
+ options=owner_options,
159
+ default=owner_options
160
  )
161
 
162
  # Apply filters
163
  filtered_df = df[
164
+ (df['Simple_Connection'].isin(grid_filter)) &
165
  (df['Owner'].isin(owner_filter))
166
  ]
167
 
 
176
 
177
  col1, col2 = st.sidebar.columns(2)
178
  col1.metric("Total Power", f"{total_power:.1f} GW", help="Total capacity of visible projects")
179
+ col2.metric("Annual Emissions", f"{total_emissions:.1f} Mt", help="Million Tonnes CO2e/year")
180
 
181
  st.sidebar.markdown(f"""
182
  <div class="metric-card">
 
191
  st.title("The Carbon Footprint of Frontier AI")
192
  st.markdown(
193
  "This map visualizes the annual emissions of major planned AI data centers. "
194
+ "**Bubble size** represents CO₂e emissions. **Color** indicates grid status "
195
+ "(<span style='color:#FF4136'><b>Red = Off-Grid/Fossil</b></span>, <span style='color:#0074D9'><b>Blue = Grid Connected</b></span>).",
196
  unsafe_allow_html=True
197
  )
198
 
 
213
  get_line_color=[0, 0, 0],
214
  )
215
 
216
+ # Tooltip (Updated for Data Center Name & Subscript)
217
  tooltip = {
218
  "html": """
219
+ <div style="font-family: sans-serif; padding: 8px; color: white; max-width: 250px;">
220
+ <h4 style="margin:0; padding-bottom:5px;">{Data Center Name}</h4>
221
+ <hr style="border-top: 1px solid #555; margin: 5px 0;">
222
  <b>Owner:</b> {Owner}<br/>
 
223
  <b>Power:</b> {Power (MW)} MW<br/>
224
+ <b>Status:</b> {Simple_Connection}<br/>
225
  <br/>
226
+ <b style="font-size: 1.1em; color: #ffcccb;">Emissions: {Emissions_Mt} MtCO<sub>2</sub>e</b><br/>
227
+ <i style="font-size: 0.8em; color: #ccc;">(Intensity: {Carbon Intensity} kg/MWh)</i>
228
+ <hr style="border-top: 1px dashed #555; margin: 5px 0;">
229
  <b>🚗 Equal to:</b> {Cars_Equivalent_Millions} Million Cars<br/>
230
  <b>🏭 Equal to:</b> {Coal_Plants_Equivalent} Coal Power Plants
231
  </div>
 
234
  "backgroundColor": "#1E1E1E",
235
  "border": "1px solid #333",
236
  "borderRadius": "8px",
237
+ "color": "white",
238
+ "zIndex": "1000"
239
  }
240
  }
241
 
242
  # Render Map
243
+ # FIX: Removed 'mapbox://' style to prevent black screen.
244
+ # Using map_style=None uses the default adaptable map.
245
  st.pydeck_chart(pdk.Deck(
246
+ map_style=None,
247
  initial_view_state=pdk.ViewState(
248
  latitude=39.8,
249
  longitude=-98.6,
 
258
  st.markdown("---")
259
  st.caption(
260
  "**Methodology:** Emissions calculated based on publicly stated power capacity (MW) and regional/source-specific carbon intensity. "
261
+ "Car equivalents assume 4.6 metric tonnes CO₂e per passenger vehicle per year (EPA). "
262
+ "Coal plant equivalent assumes ~4.0 MtCO₂e/year for a typical plant."
263
  )