Spaces:
Sleeping
Sleeping
[FEAT] Possibility to choose between running the optimization with manually entered values or by adding a pre-existing scenario (JSON) -> in this case, the values are automatically populated in all other input tabs
Browse files- data/cost_sliders.json +1 -1
- requirements.txt +0 -0
- tabs/tab1_inputs.py +46 -1
- tabs/tab3_coefficients.py +1 -1
- tabs/tab4_optimization.py +71 -46
- tabs/tab6_scenarios.py +1 -11
- utils/data_utils.py +17 -4
data/cost_sliders.json
CHANGED
|
@@ -67,7 +67,7 @@
|
|
| 67 |
"max": 1296342.17
|
| 68 |
}
|
| 69 |
},
|
| 70 |
-
"Coastal revegetation (mangrove, seaweeds
|
| 71 |
"Non-arable land (ha/tCO2)": {
|
| 72 |
"min": 0.0078,
|
| 73 |
"median": 0.12056618,
|
|
|
|
| 67 |
"max": 1296342.17
|
| 68 |
}
|
| 69 |
},
|
| 70 |
+
"Coastal revegetation (mangrove, seaweeds)": {
|
| 71 |
"Non-arable land (ha/tCO2)": {
|
| 72 |
"min": 0.0078,
|
| 73 |
"median": 0.12056618,
|
requirements.txt
CHANGED
|
Binary files a/requirements.txt and b/requirements.txt differ
|
|
|
tabs/tab1_inputs.py
CHANGED
|
@@ -14,7 +14,6 @@ def render_tab():
|
|
| 14 |
for r in resource_order:
|
| 15 |
if r not in st.session_state.resource_caps:
|
| 16 |
st.session_state.resource_caps[r] = 0.0
|
| 17 |
-
|
| 18 |
st.header("Resource inputs")
|
| 19 |
|
| 20 |
st.info("""
|
|
@@ -28,6 +27,7 @@ def render_tab():
|
|
| 28 |
# Reset button
|
| 29 |
st.markdown("### Controls")
|
| 30 |
col_reset, _ = st.columns([1, 5])
|
|
|
|
| 31 |
with col_reset:
|
| 32 |
if st.button("🔄 Reset All", type="secondary"):
|
| 33 |
for r in st.session_state.resource_caps:
|
|
@@ -35,6 +35,51 @@ def render_tab():
|
|
| 35 |
st.success("All resource values reset to 0.")
|
| 36 |
st.rerun()
|
| 37 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 38 |
# Inputs by resource group
|
| 39 |
st.markdown("### Set available resources for one year")
|
| 40 |
for group, rlist in resource_groups.items():
|
|
|
|
| 14 |
for r in resource_order:
|
| 15 |
if r not in st.session_state.resource_caps:
|
| 16 |
st.session_state.resource_caps[r] = 0.0
|
|
|
|
| 17 |
st.header("Resource inputs")
|
| 18 |
|
| 19 |
st.info("""
|
|
|
|
| 27 |
# Reset button
|
| 28 |
st.markdown("### Controls")
|
| 29 |
col_reset, _ = st.columns([1, 5])
|
| 30 |
+
|
| 31 |
with col_reset:
|
| 32 |
if st.button("🔄 Reset All", type="secondary"):
|
| 33 |
for r in st.session_state.resource_caps:
|
|
|
|
| 35 |
st.success("All resource values reset to 0.")
|
| 36 |
st.rerun()
|
| 37 |
|
| 38 |
+
# Dowload template
|
| 39 |
+
st.markdown("**Import resource values**")
|
| 40 |
+
col_template, col_upload = st.columns([1, 1])
|
| 41 |
+
with col_template:
|
| 42 |
+
st.markdown("Use the template below to fill in your resource availability values and import them directly into the tool.")
|
| 43 |
+
with open("data/resource_template.xlsx", "rb") as f:
|
| 44 |
+
st.download_button(
|
| 45 |
+
label="📥 Download Excel Template",
|
| 46 |
+
data=f,
|
| 47 |
+
file_name="resource_template.xlsx",
|
| 48 |
+
use_container_width= True,
|
| 49 |
+
mime="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"
|
| 50 |
+
)
|
| 51 |
+
with col_upload:
|
| 52 |
+
uploaded_file = st.file_uploader("Upload your Excel file", type="xlsx")
|
| 53 |
+
if uploaded_file is not None:
|
| 54 |
+
try:
|
| 55 |
+
df = pd.read_excel(uploaded_file)
|
| 56 |
+
required_cols = {"Resource", "Available Amount"}
|
| 57 |
+
if not required_cols.issubset(df.columns):
|
| 58 |
+
raise ValueError(f"Missing columns: {required_cols - set(df.columns)}")
|
| 59 |
+
for _, row in df.iterrows():
|
| 60 |
+
r = row["Resource"]
|
| 61 |
+
val = row["Available Amount"]
|
| 62 |
+
if r not in st.session_state.resource_caps:
|
| 63 |
+
continue
|
| 64 |
+
if pd.isna(val):
|
| 65 |
+
continue
|
| 66 |
+
try:
|
| 67 |
+
val = float(val)
|
| 68 |
+
except (ValueError, TypeError):
|
| 69 |
+
continue
|
| 70 |
+
if val == 0:
|
| 71 |
+
continue
|
| 72 |
+
st.session_state.resource_caps[r] = val
|
| 73 |
+
st.success(f"Resource values imported successfully.", icon="✅")
|
| 74 |
+
|
| 75 |
+
for r, v in st.session_state.resource_caps.items():
|
| 76 |
+
st.session_state[f"cap_{r}"] = v
|
| 77 |
+
|
| 78 |
+
except Exception as e:
|
| 79 |
+
st.error(f"Error importing resource values: {e}")
|
| 80 |
+
|
| 81 |
+
col_reset, _ = st.columns([1, 5])
|
| 82 |
+
|
| 83 |
# Inputs by resource group
|
| 84 |
st.markdown("### Set available resources for one year")
|
| 85 |
for group, rlist in resource_groups.items():
|
tabs/tab3_coefficients.py
CHANGED
|
@@ -10,7 +10,7 @@ def render_tab(cost_sliders):
|
|
| 10 |
st.header("Resource efficiency coefficients")
|
| 11 |
|
| 12 |
# === Initialization ===
|
| 13 |
-
if "costs" not in st.session_state:
|
| 14 |
st.session_state.costs = {
|
| 15 |
m: {
|
| 16 |
r: (0.0 if (m, r) in secondary_resources else cost_sliders[m][r]["median"])
|
|
|
|
| 10 |
st.header("Resource efficiency coefficients")
|
| 11 |
|
| 12 |
# === Initialization ===
|
| 13 |
+
if "costs" not in st.session_state or st.session_state.costs == {}:
|
| 14 |
st.session_state.costs = {
|
| 15 |
m: {
|
| 16 |
r: (0.0 if (m, r) in secondary_resources else cost_sliders[m][r]["median"])
|
tabs/tab4_optimization.py
CHANGED
|
@@ -11,6 +11,7 @@ import streamlit as st
|
|
| 11 |
from config.config import method_order, resource_order, resource_units
|
| 12 |
from optimization.optimization import run_optimization
|
| 13 |
from utils.ui_helpers import format_results_summary
|
|
|
|
| 14 |
|
| 15 |
|
| 16 |
def render_tab():
|
|
@@ -22,59 +23,83 @@ def render_tab():
|
|
| 22 |
Run the optimization tool, explore results, and save scenarios for comparison or export.
|
| 23 |
""")
|
| 24 |
|
| 25 |
-
#
|
| 26 |
-
if "
|
| 27 |
-
st.
|
| 28 |
-
|
| 29 |
-
|
|
|
|
|
|
|
| 30 |
if "scenarios" not in st.session_state:
|
| 31 |
st.session_state.scenarios = {}
|
| 32 |
-
|
| 33 |
-
if sum(st.session_state.resource_caps.values()) <= 0:
|
| 34 |
-
st.warning("\u26a0\ufe0f All resources are currently set to 0. Please increase availability.")
|
| 35 |
-
return
|
| 36 |
-
|
| 37 |
# Run optimization
|
| 38 |
-
|
| 39 |
-
|
| 40 |
-
|
| 41 |
-
|
| 42 |
-
|
| 43 |
-
)
|
| 44 |
-
|
| 45 |
-
|
| 46 |
-
|
| 47 |
-
|
| 48 |
-
|
| 49 |
-
|
| 50 |
-
|
| 51 |
-
|
| 52 |
-
|
| 53 |
-
|
| 54 |
-
|
| 55 |
-
|
| 56 |
-
|
| 57 |
-
|
| 58 |
-
|
| 59 |
-
|
| 60 |
-
|
| 61 |
-
|
| 62 |
-
|
| 63 |
-
|
| 64 |
-
|
| 65 |
-
|
| 66 |
-
|
| 67 |
-
|
| 68 |
-
|
| 69 |
-
|
|
|
|
|
|
|
|
|
|
| 70 |
else:
|
| 71 |
-
|
| 72 |
-
|
| 73 |
-
st.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 74 |
|
| 75 |
if "latest_result" not in st.session_state:
|
| 76 |
return
|
| 77 |
-
|
| 78 |
latest = st.session_state.latest_result
|
| 79 |
allocations = latest["allocations"]
|
| 80 |
resource_caps = latest["resource_caps"]
|
|
|
|
| 11 |
from config.config import method_order, resource_order, resource_units
|
| 12 |
from optimization.optimization import run_optimization
|
| 13 |
from utils.ui_helpers import format_results_summary
|
| 14 |
+
from utils.data_utils import is_valid_scenario
|
| 15 |
|
| 16 |
|
| 17 |
def render_tab():
|
|
|
|
| 23 |
Run the optimization tool, explore results, and save scenarios for comparison or export.
|
| 24 |
""")
|
| 25 |
|
| 26 |
+
# Initialize necessary session state variables if they don't exist
|
| 27 |
+
if "resource_caps" not in st.session_state:
|
| 28 |
+
st.session_state.resource_caps = {}
|
| 29 |
+
if "constraints" not in st.session_state:
|
| 30 |
+
st.session_state.constraints = {}
|
| 31 |
+
if "costs" not in st.session_state:
|
| 32 |
+
st.session_state.costs = {}
|
| 33 |
if "scenarios" not in st.session_state:
|
| 34 |
st.session_state.scenarios = {}
|
| 35 |
+
|
|
|
|
|
|
|
|
|
|
|
|
|
| 36 |
# Run optimization
|
| 37 |
+
st.markdown("### Run optimization")
|
| 38 |
+
col_run, col_load = st.columns(2)
|
| 39 |
+
|
| 40 |
+
with col_run:
|
| 41 |
+
st.markdown("**From current input values**")
|
| 42 |
+
run_clicked = st.button("▶️ Run Optimization", use_container_width=False)
|
| 43 |
+
|
| 44 |
+
with col_load:
|
| 45 |
+
st.markdown("**From a saved JSON scenario**")
|
| 46 |
+
uploaded_scenario = st.file_uploader("Upload a saved JSON scenario", type="json", key="scenario_loader")
|
| 47 |
+
if uploaded_scenario is not None:
|
| 48 |
+
try:
|
| 49 |
+
data = json.load(uploaded_scenario)
|
| 50 |
+
if is_valid_scenario(data):
|
| 51 |
+
if st.button("↩️ Reload this scenario into all tabs", use_container_width=True):
|
| 52 |
+
st.session_state.resource_caps = data["resource_caps"].copy()
|
| 53 |
+
st.session_state.constraints = data["method_constraints"].copy()
|
| 54 |
+
st.session_state.costs = data["method_costs"].copy()
|
| 55 |
+
keys_to_delete = [k for k in st.session_state if any(
|
| 56 |
+
k.startswith(prefix) for prefix in ["slider_", "manual_", "toggle_"]
|
| 57 |
+
)]
|
| 58 |
+
for k in keys_to_delete:
|
| 59 |
+
del st.session_state[k]
|
| 60 |
+
st.session_state.latest_result = data
|
| 61 |
+
st.success("Scenario loaded. All tabs have been updated.")
|
| 62 |
+
st.rerun()
|
| 63 |
+
else:
|
| 64 |
+
st.error("Invalid scenario format.")
|
| 65 |
+
except Exception as e:
|
| 66 |
+
st.error(f"Error loading scenario: {e}")
|
| 67 |
+
|
| 68 |
+
# Vérifications et exécution — uniquement si Run Optimization cliqué
|
| 69 |
+
if run_clicked:
|
| 70 |
+
if sum(st.session_state.resource_caps.values()) <= 0:
|
| 71 |
+
st.warning("⚠️ All resources are currently set to 0. Please increase availability or load a saved scenario.")
|
| 72 |
else:
|
| 73 |
+
success, result = run_optimization(
|
| 74 |
+
resource_caps=st.session_state.resource_caps,
|
| 75 |
+
method_constraints=st.session_state.constraints,
|
| 76 |
+
method_costs=st.session_state.costs,
|
| 77 |
+
)
|
| 78 |
+
if success:
|
| 79 |
+
allocations = result["method_usage"]
|
| 80 |
+
total_removed = result["total_removed"]
|
| 81 |
+
if total_removed <= 0:
|
| 82 |
+
st.warning("❌ Optimization removed 0 tCO₂. Relax constraints or increase resources.")
|
| 83 |
+
else:
|
| 84 |
+
total_cost = sum(
|
| 85 |
+
allocations[m] * sum(st.session_state.costs[m].values())
|
| 86 |
+
for m in allocations
|
| 87 |
+
)
|
| 88 |
+
st.session_state.latest_result = {
|
| 89 |
+
"summary": {"total_co2_removed": total_removed, "total_cost": total_cost},
|
| 90 |
+
"allocations": allocations,
|
| 91 |
+
"resource_caps": st.session_state.resource_caps.copy(),
|
| 92 |
+
"method_constraints": st.session_state.constraints,
|
| 93 |
+
"method_costs": st.session_state.costs,
|
| 94 |
+
}
|
| 95 |
+
st.success(f"✅ Optimization successful: {total_removed:,.0f} tCO₂ removed")
|
| 96 |
+
else:
|
| 97 |
+
st.error("❌ Optimization failed")
|
| 98 |
+
if "message" in result:
|
| 99 |
+
st.error(result["message"])
|
| 100 |
|
| 101 |
if "latest_result" not in st.session_state:
|
| 102 |
return
|
|
|
|
| 103 |
latest = st.session_state.latest_result
|
| 104 |
allocations = latest["allocations"]
|
| 105 |
resource_caps = latest["resource_caps"]
|
tabs/tab6_scenarios.py
CHANGED
|
@@ -4,17 +4,7 @@ import matplotlib.pyplot as plt
|
|
| 4 |
import pandas as pd
|
| 5 |
import seaborn as sns
|
| 6 |
import streamlit as st
|
| 7 |
-
|
| 8 |
-
|
| 9 |
-
def is_valid_scenario(scenario):
|
| 10 |
-
return (
|
| 11 |
-
isinstance(scenario, dict)
|
| 12 |
-
and "allocations" in scenario
|
| 13 |
-
and "resource_caps" in scenario
|
| 14 |
-
and "method_costs" in scenario
|
| 15 |
-
and "method_constraints" in scenario
|
| 16 |
-
and "summary" in scenario
|
| 17 |
-
)
|
| 18 |
|
| 19 |
def render_tab():
|
| 20 |
st.header("Scenario Comparison")
|
|
|
|
| 4 |
import pandas as pd
|
| 5 |
import seaborn as sns
|
| 6 |
import streamlit as st
|
| 7 |
+
from utils.data_utils import is_valid_scenario
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 8 |
|
| 9 |
def render_tab():
|
| 10 |
st.header("Scenario Comparison")
|
utils/data_utils.py
CHANGED
|
@@ -1,5 +1,5 @@
|
|
| 1 |
import json
|
| 2 |
-
|
| 3 |
import streamlit as st
|
| 4 |
|
| 5 |
|
|
@@ -19,14 +19,27 @@ def render_resource_input(name_clean, r, tooltip=None):
|
|
| 19 |
else:
|
| 20 |
st.markdown(f"**{name_clean}**")
|
| 21 |
|
| 22 |
-
|
| 23 |
-
|
|
|
|
|
|
|
| 24 |
|
| 25 |
cap = st.number_input(
|
| 26 |
label="label",
|
| 27 |
label_visibility="collapsed",
|
| 28 |
min_value=0.0,
|
| 29 |
format="%.2f",
|
| 30 |
-
key=
|
| 31 |
)
|
|
|
|
| 32 |
st.session_state.resource_caps[r] = cap
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
import json
|
| 2 |
+
import pandas as pd
|
| 3 |
import streamlit as st
|
| 4 |
|
| 5 |
|
|
|
|
| 19 |
else:
|
| 20 |
st.markdown(f"**{name_clean}**")
|
| 21 |
|
| 22 |
+
widget_key = f"cap_{r}"
|
| 23 |
+
|
| 24 |
+
if widget_key not in st.session_state:
|
| 25 |
+
st.session_state[widget_key] = st.session_state.resource_caps.get(r, 0.0)
|
| 26 |
|
| 27 |
cap = st.number_input(
|
| 28 |
label="label",
|
| 29 |
label_visibility="collapsed",
|
| 30 |
min_value=0.0,
|
| 31 |
format="%.2f",
|
| 32 |
+
key=widget_key,
|
| 33 |
)
|
| 34 |
+
|
| 35 |
st.session_state.resource_caps[r] = cap
|
| 36 |
+
|
| 37 |
+
def is_valid_scenario(scenario):
|
| 38 |
+
return (
|
| 39 |
+
isinstance(scenario, dict)
|
| 40 |
+
and "allocations" in scenario
|
| 41 |
+
and "resource_caps" in scenario
|
| 42 |
+
and "method_costs" in scenario
|
| 43 |
+
and "method_constraints" in scenario
|
| 44 |
+
and "summary" in scenario
|
| 45 |
+
)
|