#%% uses the latest version of aggregated inputs to create the dictionary of costs import json import sys import pandas as pd from pathlib import Path sys.path.insert(0, str(Path(__file__).parent.parent)) from utils.data_utils import strip_unit_suffix def pop_by_name(d: dict, name: str): """Pop a key from dict by resource name, ignoring the unit suffix.""" key = next((k for k in d if strip_unit_suffix(k) == name), None) if key: d.pop(key) # Which base methods are replaced by derived variants. # Methods listed here must have derived entries added in create_sliders_inputs(). REPLACED_BY_DERIVED = {"Bio-char", "Enhanced weathering"} # Which derived methods follow each base method (in order). DERIVED_FROM = { "Bio-char": [ "Bio-char - Forest biomass", "Bio-char - Non-forest biomass", "Bio-char - Unspecified biomass", ], "Enhanced weathering": [ "Enhanced weathering - Basalt mineral", "Enhanced weathering - Olivine mineral", "Enhanced weathering - Wollastonite mineral", "Enhanced weathering - Unspecified mineral", ], "BioCCS with combustion": ["BioCCS with combustion & growing biomass"], "BioCCS with anaerobic digestion/fermentation": [ "BioCCS with anaerobic digestion/fermentation & growing biomass" ], } def build_method_groups(df, cost_sliders): """Build the method_groups dict from the CSV Method group column + derivation rules above. Group and method order follows first appearance in inputs_aggregated.csv.""" base_methods_per_group = {} for _, row in df.drop_duplicates(subset=["Implementation"]).iterrows(): m, g = row["Implementation"], row["Method group"] base_methods_per_group.setdefault(g, []) if m not in base_methods_per_group[g]: base_methods_per_group[g].append(m) method_groups = {} for g, base_list in base_methods_per_group.items(): methods = [] for base in base_list: if base not in REPLACED_BY_DERIVED: methods.append(base) for derived in DERIVED_FROM.get(base, []): if derived in cost_sliders: methods.append(derived) if methods: method_groups[g] = methods return method_groups def build_secondary_resources(df, cost_sliders): """Build the full set of (method, resource) secondary pairs from the CSV 'Is secondary' column, then propagate to derived methods via DERIVED_FROM.""" # Base pairs from CSV base_secondary = set() for _, row in df[df["Is secondary"] == True].iterrows(): base_secondary.add((row["Implementation"], row["Resource"])) # Expand to derived methods: derived methods inherit secondary resources from their base full_secondary = set(base_secondary) for base, deriveds in DERIVED_FROM.items(): base_resources_secondary = {r for (m, r) in base_secondary if m == base} for derived in deriveds: if derived in cost_sliders: for r in base_resources_secondary: full_secondary.add((derived, r)) return full_secondary def create_sliders_inputs(): df = pd.read_csv("data/inputs_aggregated.csv") cost_sliders = {} # Build cost_sliders from CSV: one entry per (method, resource) row. # Bio-char, Enhanced weathering and BioCCS variants are derived below. for _, row in df.iterrows(): method = row["Implementation"] resource = row["Resource"] + " (" + row["Unit"] + ")" if row["min"] == row["max"] and row["Resource"] != "Geologic storage": row["min"] = row["min"] * .5 entry = { "min": float(row["min"]), "median": float(row["median"]), "max": float(row["max"]) } cost_sliders.setdefault(method, {})[resource] = entry # ── Derived methods ─────────────────────────────────────────────────────── # Bio-char: split into 3 biomass-specific variants, remove generic base cost_sliders["Bio-char - Forest biomass"] = cost_sliders["Bio-char"].copy() pop_by_name(cost_sliders["Bio-char - Forest biomass"], "Non-forest biomass") pop_by_name(cost_sliders["Bio-char - Forest biomass"], "Unspecified biomass") cost_sliders["Bio-char - Non-forest biomass"] = cost_sliders["Bio-char"].copy() pop_by_name(cost_sliders["Bio-char - Non-forest biomass"], "Forest biomass") pop_by_name(cost_sliders["Bio-char - Non-forest biomass"], "Unspecified biomass") cost_sliders["Bio-char - Unspecified biomass"] = cost_sliders["Bio-char"].copy() pop_by_name(cost_sliders["Bio-char - Unspecified biomass"], "Forest biomass") pop_by_name(cost_sliders["Bio-char - Unspecified biomass"], "Non-forest biomass") del cost_sliders["Bio-char"] # Enhanced weathering: split into 4 mineral-specific variants, remove generic base cost_sliders["Enhanced weathering - Basalt mineral"] = cost_sliders["Enhanced weathering"].copy() pop_by_name(cost_sliders["Enhanced weathering - Basalt mineral"], "Olivine mineral") pop_by_name(cost_sliders["Enhanced weathering - Basalt mineral"], "Wollastonite mineral") pop_by_name(cost_sliders["Enhanced weathering - Basalt mineral"], "Unspecified mineral") cost_sliders["Enhanced weathering - Olivine mineral"] = cost_sliders["Enhanced weathering"].copy() pop_by_name(cost_sliders["Enhanced weathering - Olivine mineral"], "Basalt mineral") pop_by_name(cost_sliders["Enhanced weathering - Olivine mineral"], "Wollastonite mineral") pop_by_name(cost_sliders["Enhanced weathering - Olivine mineral"], "Unspecified mineral") cost_sliders["Enhanced weathering - Wollastonite mineral"] = cost_sliders["Enhanced weathering"].copy() pop_by_name(cost_sliders["Enhanced weathering - Wollastonite mineral"], "Basalt mineral") pop_by_name(cost_sliders["Enhanced weathering - Wollastonite mineral"], "Olivine mineral") pop_by_name(cost_sliders["Enhanced weathering - Wollastonite mineral"], "Unspecified mineral") cost_sliders["Enhanced weathering - Unspecified mineral"] = cost_sliders["Enhanced weathering"].copy() pop_by_name(cost_sliders["Enhanced weathering - Unspecified mineral"], "Basalt mineral") pop_by_name(cost_sliders["Enhanced weathering - Unspecified mineral"], "Olivine mineral") pop_by_name(cost_sliders["Enhanced weathering - Unspecified mineral"], "Wollastonite mineral") del cost_sliders["Enhanced weathering"] # BioCCS: add growing-biomass variants alongside their base methods cost_sliders["BioCCS with combustion & growing biomass"] = cost_sliders["BioCCS with combustion"].copy() pop_by_name(cost_sliders["BioCCS with combustion"], "Arable land") pop_by_name(cost_sliders["BioCCS with combustion & growing biomass"], "Forest biomass") pop_by_name(cost_sliders["BioCCS with combustion & growing biomass"], "Unspecified biomass") cost_sliders["BioCCS with anaerobic digestion/fermentation & growing biomass"] = cost_sliders["BioCCS with anaerobic digestion/fermentation"].copy() pop_by_name(cost_sliders["BioCCS with anaerobic digestion/fermentation"], "Arable land") pop_by_name(cost_sliders["BioCCS with anaerobic digestion/fermentation & growing biomass"], "Non-forest biomass") pop_by_name(cost_sliders["BioCCS with anaerobic digestion/fermentation & growing biomass"], "Unspecified biomass") # ── Save outputs ────────────────────────────────────────────────────────── with open("data/cost_sliders.json", "w") as f: json.dump(cost_sliders, f, indent=2) print("Saved cost_sliders.json") method_groups = build_method_groups(df, cost_sliders) with open("data/method_groups.json", "w") as f: json.dump(method_groups, f, indent=2) print("Saved method_groups.json") secondary_resources = build_secondary_resources(df, cost_sliders) with open("data/secondary_resources.json", "w") as f: json.dump(sorted(secondary_resources), f, indent=2) print(f"Saved secondary_resources.json ({len(secondary_resources)} pairs)") if __name__ == "__main__": create_sliders_inputs() print("Script executed successfully.") # %%