Spaces:
Sleeping
Sleeping
Download tabs/tab4_optimization.py from CGdev/CRRA_optimisation_tool: direct link, hf CLI and curl.
- Browser
- Download file 11.1 kB
-
https://huggingface.co/spaces/CGdev/CRRA_optimisation_tool/resolve/fbd166f7b60aa4763e9ec44bbde8cd79d526ef36/tabs/tab4_optimization.py
- Command line
-
hf download hf://spaces/CGdev/CRRA_optimisation_tool@fbd166f7b60aa4763e9ec44bbde8cd79d526ef36/tabs/tab4_optimization.py
-
curl -L -o tab4_optimization.py https://huggingface.co/spaces/CGdev/CRRA_optimisation_tool/resolve/fbd166f7b60aa4763e9ec44bbde8cd79d526ef36/tabs/tab4_optimization.py
11.1 kB
| import io | |
| import json | |
| import unicodedata | |
| import zipfile | |
| import matplotlib.pyplot as plt | |
| import pandas as pd | |
| import seaborn as sns | |
| import streamlit as st | |
| from config.config import method_order, resource_order, resource_units | |
| from optimization.optimization import run_optimization | |
| from utils.ui_helpers import format_results_summary | |
| from utils.data_utils import is_valid_scenario | |
| def render_tab(): | |
| st.header("Portfolio generation") | |
| st.caption("Tab 4 - Seaborn fallback for vertical histograms") | |
| st.info(""" | |
| **Objective:** | |
| Run the optimization tool, explore results, and save scenarios for comparison or export. | |
| """) | |
| # Initialize necessary session state variables if they don't exist | |
| if "resource_caps" not in st.session_state: | |
| st.session_state.resource_caps = {} | |
| if "constraints" not in st.session_state: | |
| st.session_state.constraints = {} | |
| if "costs" not in st.session_state: | |
| st.session_state.costs = {} | |
| if "scenarios" not in st.session_state: | |
| st.session_state.scenarios = {} | |
| # Run optimization | |
| st.markdown("### Run optimization") | |
| col_run, col_load = st.columns(2) | |
| with col_run: | |
| st.markdown("**From current input values**") | |
| run_clicked = st.button("▶️ Run Optimization", use_container_width=False) | |
| with col_load: | |
| st.markdown("**From a saved JSON scenario**") | |
| uploaded_scenario = st.file_uploader("Upload a saved JSON scenario", type="json", key="scenario_loader") | |
| if uploaded_scenario is not None: | |
| try: | |
| data = json.load(uploaded_scenario) | |
| if is_valid_scenario(data): | |
| if st.button("↩️ Reload this scenario into all tabs", use_container_width=True): | |
| st.session_state.resource_caps = data["resource_caps"].copy() | |
| st.session_state.constraints = data["method_constraints"].copy() | |
| st.session_state.costs = data["method_costs"].copy() | |
| keys_to_delete = [k for k in st.session_state if any( | |
| k.startswith(prefix) for prefix in ["slider_", "manual_", "toggle_"] | |
| )] | |
| for k in keys_to_delete: | |
| del st.session_state[k] | |
| st.session_state.latest_result = data | |
| st.success("Scenario loaded. All tabs have been updated.") | |
| st.rerun() | |
| else: | |
| st.error("Invalid scenario format.") | |
| except Exception as e: | |
| st.error(f"Error loading scenario: {e}") | |
| # Vérifications et exécution — uniquement si Run Optimization cliqué | |
| if run_clicked: | |
| if sum(st.session_state.resource_caps.values()) <= 0: | |
| st.warning("⚠️ All resources are currently set to 0. Please increase availability or load a saved scenario.") | |
| else: | |
| success, result = run_optimization( | |
| resource_caps=st.session_state.resource_caps, | |
| method_constraints=st.session_state.constraints, | |
| method_costs=st.session_state.costs, | |
| ) | |
| if success: | |
| allocations = result["method_usage"] | |
| total_removed = result["total_removed"] | |
| if total_removed <= 0: | |
| st.warning("❌ Optimization removed 0 tCO₂. Relax constraints or increase resources.") | |
| else: | |
| total_cost = sum( | |
| allocations[m] * sum(st.session_state.costs[m].values()) | |
| for m in allocations | |
| ) | |
| st.session_state.latest_result = { | |
| "summary": {"total_co2_removed": total_removed, "total_cost": total_cost}, | |
| "allocations": allocations, | |
| "resource_caps": st.session_state.resource_caps.copy(), | |
| "method_constraints": st.session_state.constraints, | |
| "method_costs": st.session_state.costs, | |
| } | |
| st.success(f"✅ Optimization successful: {total_removed:,.0f} tCO₂ removed") | |
| else: | |
| st.error("❌ Optimization failed") | |
| if "message" in result: | |
| st.error(result["message"]) | |
| if "latest_result" not in st.session_state: | |
| return | |
| latest = st.session_state.latest_result | |
| allocations = latest["allocations"] | |
| resource_caps = latest["resource_caps"] | |
| method_costs = latest["method_costs"] | |
| if not allocations or sum(allocations.values()) == 0: | |
| return | |
| # Metrics | |
| col1, col2 = st.columns(2) | |
| col1.metric("Total CO₂ Removed", f"{latest['summary']['total_co2_removed']:,.0f} t") | |
| col2.metric("Methods Used", len(allocations)) | |
| # Summary table | |
| st.subheader("Optimization Summary") | |
| df_result = pd.DataFrame.from_dict(allocations, orient="index", columns=["tCO₂ removed"]) | |
| df_result["tCO₂ removed"] = pd.to_numeric(df_result["tCO₂ removed"], errors="coerce") | |
| total = df_result["tCO₂ removed"].sum() | |
| if total > 0: | |
| df_result["Share (%)"] = (df_result["tCO₂ removed"] / total * 100).round(2) | |
| df_result = df_result.sort_values(by="tCO₂ removed", ascending=False) | |
| st.table(format_results_summary(df_result)) | |
| # Donut chart | |
| st.markdown("### Method allocation breakdown") | |
| labels = df_result.index.tolist() | |
| values = df_result["tCO₂ removed"].tolist() | |
| import plotly.graph_objects as go | |
| fig_donut = go.Figure(data=[go.Pie( | |
| labels=labels, | |
| values=values, | |
| hole=0.4, | |
| textinfo="label+percent", | |
| textposition="outside", | |
| marker=dict(line=dict(color="#000000", width=1)), | |
| showlegend=False | |
| )]) | |
| fig_donut.update_layout(margin=dict(t=10, b=10)) | |
| st.plotly_chart(fig_donut, use_container_width=True) | |
| # Heatmap | |
| st.markdown("### Resource usage heatmap (% of cap)") | |
| heatmap_matrix, heatmap_y = [], [] | |
| available_resources = [r for r in resource_order if resource_caps.get(r, 0) > 0] | |
| for method in df_result.index: | |
| row = [] | |
| for r in available_resources: | |
| usage = method_costs.get(method, {}).get(r, 0.0) * allocations.get(method, 0.0) | |
| row.append((usage / resource_caps[r]) * 100 if resource_caps[r] > 0 else 0) | |
| heatmap_matrix.append(row) | |
| heatmap_y.append(method) | |
| fig_heatmap = go.Figure(data=go.Heatmap( | |
| z=heatmap_matrix, | |
| x=available_resources, | |
| y=heatmap_y, | |
| colorscale="Blues", | |
| hovertemplate="Method: %{y}<br>Resource: %{x}<br>% Used: %{z:.2f}%<extra></extra>", | |
| )) | |
| fig_heatmap.update_layout( | |
| title="Resource Usage vs. Cap", | |
| xaxis_title="Resource", | |
| yaxis_title="Method", | |
| height=400, | |
| ) | |
| st.plotly_chart(fig_heatmap, use_container_width=True) | |
| # Absolute usage (fallback with seaborn) | |
| st.markdown("### Absolute resource usage by unit (Seaborn)") | |
| df_abs = [] | |
| for m, co2_removed in allocations.items(): | |
| for unit, resources in resource_units.items(): | |
| for r in resources: | |
| if r in method_costs.get(m, {}): | |
| used = method_costs[m][r] * co2_removed | |
| if used > 0: | |
| df_abs.append({ | |
| "Method": m, | |
| "Resource": r, | |
| "Used": used, | |
| "Unit": unit | |
| }) | |
| if df_abs: | |
| df_abs = pd.DataFrame(df_abs) | |
| for unit in df_abs["Unit"].unique(): | |
| st.markdown(f"#### Resources in {unit}") | |
| df_unit = df_abs[df_abs["Unit"] == unit] | |
| fig, ax = plt.subplots(figsize=(8, 4 + 0.3 * df_unit["Resource"].nunique())) | |
| sns.barplot(data=df_unit, x="Resource", y="Used", hue="Method", ax=ax) | |
| ax.set_ylabel(f"Used ({unit})") | |
| ax.set_xlabel("Resource") | |
| ax.tick_params(axis='x', rotation=45) | |
| ax.legend(title="Method", bbox_to_anchor=(1.05, 1), loc='upper left') | |
| st.pyplot(fig) | |
| # Save scenario | |
| st.markdown("### Save scenario") | |
| st.text_input("Scenario name", key="scenario_name") | |
| if st.button("Save Scenario"): | |
| name = st.session_state.get("scenario_name", "").strip() | |
| if not name: | |
| st.warning("Please enter a name before saving.") | |
| elif name in st.session_state.scenarios: | |
| st.warning("Scenario name already exists.") | |
| else: | |
| st.session_state.scenarios[name] = latest | |
| st.success(f"Scenario '{name}' saved.") | |
| # Preview saved scenario | |
| current_name = st.session_state.get("scenario_name", "").strip() | |
| if current_name and current_name in st.session_state.scenarios: | |
| scenario = st.session_state.scenarios[current_name] | |
| st.markdown(f"#### Scenario: {current_name}") | |
| df_saved = pd.DataFrame.from_dict( | |
| scenario["allocations"], orient="index", columns=["tCO₂ removed"] | |
| ) | |
| df_saved["tCO₂ removed"] = pd.to_numeric(df_saved["tCO₂ removed"], errors="coerce") | |
| total = df_saved["tCO₂ removed"].sum() | |
| if total > 0: | |
| df_saved["Share (%)"] = (df_saved["tCO₂ removed"] / total * 100).round(2) | |
| df_saved = df_saved.sort_values(by="tCO₂ removed", ascending=False) | |
| st.table(format_results_summary(df_saved)) | |
| # Export buttons | |
| safe_name = unicodedata.normalize("NFKD", current_name).encode("ascii", "ignore").decode("ascii") | |
| st.download_button( | |
| label="Export JSON", | |
| data=json.dumps(scenario, indent=2), | |
| file_name=f"{safe_name}_scenario.json", | |
| mime="application/json" | |
| ) | |
| inputs_df = pd.DataFrame.from_dict(scenario["resource_caps"], orient="index", columns=["Available"]) | |
| constraints_df = pd.DataFrame.from_dict({m: v.get("resources", {}) for m, v in scenario["method_constraints"].items()}, orient="index").fillna(0) | |
| allocations_df = pd.DataFrame.from_dict(scenario["allocations"], orient="index", columns=["tCO₂ removed"]) | |
| csv_buffer = io.BytesIO() | |
| with zipfile.ZipFile(csv_buffer, "w") as zipf: | |
| zipf.writestr("resource_caps.csv", inputs_df.to_csv()) | |
| zipf.writestr("method_constraints.csv", constraints_df.to_csv()) | |
| zipf.writestr("allocations.csv", allocations_df.to_csv()) | |
| st.download_button( | |
| label="Export CSV", | |
| data=csv_buffer.getvalue(), | |
| file_name=f"{safe_name}_scenario.zip", | |
| mime="application/zip" | |
| ) | |