From d27eb2d9564bb5d19c4588b4a6ee69df66fad4c2 Mon Sep 17 00:00:00 2001 From: t-kramer Date: Mon, 14 Sep 2026 09:11:39 -0700 Subject: [PATCH] fix: minor adjustments to PR #180 --- pages/emissions_page.py | 4 ++++ src/config.py | 2 +- src/emissions.py | 24 ++++++++++++++++++++++-- src/energy.py | 8 ++------ 4 files changed, 29 insertions(+), 9 deletions(-) diff --git a/pages/emissions_page.py b/pages/emissions_page.py index d9e9e67..5157d39 100644 --- a/pages/emissions_page.py +++ b/pages/emissions_page.py @@ -310,6 +310,10 @@ def handle_emission_group_selection(group_id, metadata_data, selected_ids, store "year": default_year, } ) + # If the base was a Constant scenario it may have no grid_scenario; restore a + # sensible default so Cambium-based group variants don't fail validation. + if not base_scenario.get("grid_scenario"): + base_scenario["grid_scenario"] = "MidCase" # Reset to default 2/3/4 scenarios (a, b, optionally c & d) with group-specific values updated_scenarios = [] diff --git a/src/config.py b/src/config.py index f79c319..d839377 100644 --- a/src/config.py +++ b/src/config.py @@ -70,7 +70,7 @@ class EmissionScenarioDefaults(Enum): """Default values for emission scenario parameters.""" ELEC_EMISSION_SOURCE: str = "Marginal (Cambium, Long-run)" - ELEC_AVG_EMISSION_RATE_G_KWH: float = None + ELEC_AVG_EMISSION_RATE_G_KWH: float | None = None YEAR: int = 2025 REFRIGERANT_LEAKAGE: float = 0.02 EMISSION_TYPE: str = "Includes pre-combustion" diff --git a/src/emissions.py b/src/emissions.py index dd286e8..fef0f2f 100644 --- a/src/emissions.py +++ b/src/emissions.py @@ -1,16 +1,28 @@ from pathlib import Path +from typing import Literal import pandas as pd -from pydantic import BaseModel +from pydantic import BaseModel, model_validator from src import paths from src.mixins import DotAccessMixin +CAMBIUM_SOURCES = ( + "Marginal (Cambium, Long-run)", + "Marginal (Cambium, Short-run)", + "Average (Cambium)", +) + class EmissionScenario(DotAccessMixin, BaseModel): em_scen_id: str em_scen_name: str - elec_emission_source: str + elec_emission_source: Literal[ + "Marginal (Cambium, Long-run)", + "Marginal (Cambium, Short-run)", + "Average (Cambium)", + "Constant (User-provided)", + ] elec_avg_emission_rate_gCO2e_per_kWh: float | None = None grid_scenario: str | None = None gea_grid_region: str | None = None @@ -19,6 +31,14 @@ class EmissionScenario(DotAccessMixin, BaseModel): ng_emission_rate_gCO2e_per_kWh: float year: int + @model_validator(mode="after") + def check_source_fields(self) -> "EmissionScenario": + if self.elec_emission_source in CAMBIUM_SOURCES and not self.grid_scenario: + raise ValueError( + f"grid_scenario is required for emission source '{self.elec_emission_source}'" + ) + return self + class StandardEmissions: """ diff --git a/src/energy.py b/src/energy.py index cd85c28..c22eca5 100644 --- a/src/energy.py +++ b/src/energy.py @@ -2,7 +2,7 @@ import pandas as pd from src.config import Columns as Col -from src.emissions import get_emissions_data +from src.emissions import CAMBIUM_SOURCES, get_emissions_data from src.equipment import Equipment, EquipmentLibrary, PerformanceCurves from src.loads import StandardLoad from src.metadata import Metadata @@ -1161,11 +1161,7 @@ def site_to_source( base[Col.HOUR.value] = base.index.hour base[Col.DOY.value] = base.index.dayofyear - if em_scen.elec_emission_source in [ - "Marginal (Cambium, Long-run)", - "Marginal (Cambium, Short-run)", - "Average (Cambium)", - ]: + if em_scen.elec_emission_source in CAMBIUM_SOURCES: emissions_data = get_emissions_data(metadata[em_scen_id]) logger.debug( f"Cambium grid emissions: Loaded {len(emissions_data.df)} emission data rows"