From 2b3b5ad2451ccdc7e88b01d635353f0489c4bd7f Mon Sep 17 00:00:00 2001 From: Silvia Rognone Date: Sun, 30 Nov 2025 22:07:21 +0000 Subject: [PATCH 1/5] add: building_archetype nullified along with stebbs parameters when stebbsmethod == 0 --- src/supy/data_model/core/site.py | 116 +++++++++--------- .../data_model/validation/pipeline/phase_b.py | 53 +++++--- 2 files changed, 92 insertions(+), 77 deletions(-) diff --git a/src/supy/data_model/core/site.py b/src/supy/data_model/core/site.py index 8c6ce37ab..6d2230e70 100644 --- a/src/supy/data_model/core/site.py +++ b/src/supy/data_model/core/site.py @@ -1177,22 +1177,22 @@ class ArchetypeProperties(BaseModel): # BuildingCode='1' # BuildingClass='SampleClass' - BuildingType: str = Field( + BuildingType: Optional[str] = Field( default="SampleType", description="Building archetype type [-]", json_schema_extra={"display_name": "Buildingtype"}, ) - BuildingName: str = Field( + BuildingName: Optional[str] = Field( default="SampleBuilding", description="Building archetype name [-]", json_schema_extra={"display_name": "Buildingname"}, ) - BuildingCount: FlexibleRefValue(int) = Field( + BuildingCount: Optional[FlexibleRefValue(int)] = Field( default=1, description="Number of buildings of this archetype [-]", json_schema_extra={"unit": "dimensionless", "display_name": "Buildingcount"}, ) - Occupants: FlexibleRefValue(int) = Field( + Occupants: Optional[FlexibleRefValue(int)] = Field( default=1, description="Number of occupants present in building [-]", json_schema_extra={"unit": "dimensionless", "display_name": "Occupants"}, @@ -1214,25 +1214,25 @@ class ArchetypeProperties(BaseModel): # age_19_64: int = Field(default=0, description="") # age_65plus: int = Field(default=0, description="") - stebbs_Height: FlexibleRefValue(float) = Field( + stebbs_Height: Optional[FlexibleRefValue(float)] = Field( default=10.0, description="Building height [m]", json_schema_extra={"unit": "m", "display_name": "Stebbs Height"}, gt=0.0, ) - FootprintArea: FlexibleRefValue(float) = Field( + FootprintArea: Optional[FlexibleRefValue(float)] = Field( default=64.0, description="Building footprint area [m2]", json_schema_extra={"unit": "m^2", "display_name": "Footprintarea"}, gt=0.0, ) - WallExternalArea: FlexibleRefValue(float) = Field( + WallExternalArea: Optional[FlexibleRefValue(float)] = Field( default=80.0, description="External wall area (including window area) [m2]", json_schema_extra={"unit": "m^2", "display_name": "Wallexternalarea"}, gt=0.0, ) - RatioInternalVolume: FlexibleRefValue(float) = Field( + RatioInternalVolume: Optional[FlexibleRefValue(float)] = Field( default=0.01, description="Ratio of internal mass volume to total building volume [-]", json_schema_extra={ @@ -1242,20 +1242,20 @@ class ArchetypeProperties(BaseModel): ge=0.0, le=1.0, ) - WWR: FlexibleRefValue(float) = Field( + WWR: Optional[FlexibleRefValue(float)] = Field( default=0.20, description="window to wall ratio [-]", json_schema_extra={"unit": "dimensionless", "display_name": "Wwr"}, ge=0.0, le=1.0, ) - WallThickness: FlexibleRefValue(float) = Field( + WallThickness: Optional[FlexibleRefValue(float)] = Field( default=0.2, description="Thickness of external wall [m]", json_schema_extra={"unit": "m", "display_name": "Wallthickness"}, gt=0.0, ) - WallEffectiveConductivity: FlexibleRefValue(float) = Field( + WallEffectiveConductivity: Optional[FlexibleRefValue(float)] = Field( default=0.6, description="Effective thermal conductivity of walls [W m-1 K-1]", json_schema_extra={ @@ -1264,24 +1264,24 @@ class ArchetypeProperties(BaseModel): }, gt=0.0, ) - WallDensity: FlexibleRefValue(float) = Field( + WallDensity: Optional[FlexibleRefValue(float)] = Field( default=1600.0, description="Effective density of the walls [kg m-3]", json_schema_extra={"unit": "kg m^-3", "display_name": "Walldensity"}, gt=0.0, ) - WallCp: FlexibleRefValue(float) = Field( + WallCp: Optional[FlexibleRefValue(float)] = Field( default=850.0, description="Effective specific heat capacity of walls [J kg-1 K-1]", gt=0.0, ) - WallextThickness: FlexibleRefValue(float) = Field( + WallextThickness: Optional[FlexibleRefValue(float)] = Field( default=20.0, description="Thickness of layers external to insulation in external wall [m]", json_schema_extra={"unit": "m", "display_name": "Wallextthickness"}, gt=0.0, ) - WallextEffectiveConductivity: FlexibleRefValue(float) = Field( + WallextEffectiveConductivity: Optional[FlexibleRefValue(float)] = Field( default=60.0, description="Effective thermal conductivity of layers external to insulation in walls [W m-1 K-1]", json_schema_extra={ @@ -1290,26 +1290,26 @@ class ArchetypeProperties(BaseModel): }, gt=0.0, ) - WallextDensity: FlexibleRefValue(float) = Field( + WallextDensity: Optional[FlexibleRefValue(float)] = Field( default=1600.0, description="Effective density of layers external to insulation in the walls [kg m-3]", json_schema_extra={"unit": "kg m^-3", "display_name": "Wallextdensity"}, gt=0.0, ) - WallextCp: FlexibleRefValue(float) = Field( + WallextCp: Optional[FlexibleRefValue(float)] = Field( default=850.0, description="Effective specific heat capacity of layers external to insulation in walls [J kg-1 K-1]", json_schema_extra={"unit": "J kg^-1 K^-1", "display_name": "Wallextcp"}, gt=0.0, ) - WallOuterCapFrac: FlexibleRefValue(float) = Field( + WallOuterCapFrac: Optional[FlexibleRefValue(float)] = Field( default=1.0, description="Weighting factor for heat capacity of walls [-]", json_schema_extra={"unit": "dimensionless", "display_name": "Walloutercapfrac"}, ge=0.0, le=1.0, ) - WallExternalEmissivity: FlexibleRefValue(float) = Field( + WallExternalEmissivity: Optional[FlexibleRefValue(float)] = Field( default=0.9, description="Emissivity of the external surface of walls [-]", json_schema_extra={ @@ -1319,7 +1319,7 @@ class ArchetypeProperties(BaseModel): ge=0.0, le=1.0, ) - WallInternalEmissivity: FlexibleRefValue(float) = Field( + WallInternalEmissivity: Optional[FlexibleRefValue(float)] = Field( default=0.9, description="Emissivity of the internal surface of walls [-]", json_schema_extra={ @@ -1329,7 +1329,7 @@ class ArchetypeProperties(BaseModel): ge=0.0, le=1.0, ) - WallTransmissivity: FlexibleRefValue(float) = Field( + WallTransmissivity: Optional[FlexibleRefValue(float)] = Field( default=0.0, description="Transmissivity of walls [-]", json_schema_extra={ @@ -1339,27 +1339,27 @@ class ArchetypeProperties(BaseModel): ge=0.0, le=1.0, ) - WallAbsorbtivity: FlexibleRefValue(float) = Field( + WallAbsorbtivity: Optional[FlexibleRefValue(float)] = Field( default=0.8, description="Absorbtivity of walls [-]", json_schema_extra={"unit": "dimensionless", "display_name": "Wallabsorbtivity"}, ge=0.0, le=1.0, ) - WallReflectivity: FlexibleRefValue(float) = Field( + WallReflectivity: Optional[FlexibleRefValue(float)] = Field( default=0.2, description="Reflectivity of the external surface of walls [-]", json_schema_extra={"unit": "dimensionless", "display_name": "Wallreflectivity"}, ge=0.0, le=1.0, ) - RoofThickness: FlexibleRefValue(float) = Field( + RoofThickness: Optional[FlexibleRefValue(float)] = Field( default=20.0, description="Thickness of roof [m]", json_schema_extra={"unit": "m", "display_name": "Roofthickness"}, gt=0.0, ) - RoofEffectiveConductivity: FlexibleRefValue(float) = Field( + RoofEffectiveConductivity: Optional[FlexibleRefValue(float)] = Field( default=60.0, description="Effective thermal conductivity of roof [W m-1 K-1]", json_schema_extra={ @@ -1368,24 +1368,24 @@ class ArchetypeProperties(BaseModel): }, gt=0.0, ) - RoofDensity: FlexibleRefValue(float) = Field( + RoofDensity: Optional[FlexibleRefValue(float)] = Field( default=1600.0, description="Effective density of the roof [kg m-3]", json_schema_extra={"unit": "kg m^-3", "display_name": "Roofdensity"}, gt=0.0, ) - RoofCp: FlexibleRefValue(float) = Field( + RoofCp: Optional[FlexibleRefValue(float)] = Field( default=850.0, description="Effective specific heat capacity of roof [J kg-1 K-1]", gt=0.0, ) - RoofextThickness: FlexibleRefValue(float) = Field( + RoofextThickness: Optional[FlexibleRefValue(float)] = Field( default=20.0, description="Thickness of layers external to insulation in roof [m]", json_schema_extra={"unit": "m", "display_name": "Roofextthickness"}, gt=0.0, ) - RoofextEffectiveConductivity: FlexibleRefValue(float) = Field( + RoofextEffectiveConductivity: Optional[FlexibleRefValue(float)] = Field( default=60.0, description="Effective thermal conductivity of layers external to insulation in roof [W m-1 K-1]", json_schema_extra={ @@ -1394,26 +1394,26 @@ class ArchetypeProperties(BaseModel): }, gt=0.0, ) - RoofextDensity: FlexibleRefValue(float) = Field( + RoofextDensity: Optional[FlexibleRefValue(float)] = Field( default=1600.0, description="Effective density of layers external to insulation in the roof [kg m-3]", json_schema_extra={"unit": "kg m^-3", "display_name": "Roofextdensity"}, gt=0.0, ) - RoofextCp: FlexibleRefValue(float) = Field( + RoofextCp: Optional[FlexibleRefValue(float)] = Field( default=850.0, description="Effective specific heat capacity of layers external to insulation in roof [J kg-1 K-1]", json_schema_extra={"unit": "J kg^-1 K^-1", "display_name": "Roofextcp"}, gt=0.0, ) - RoofOuterCapFrac: FlexibleRefValue(float) = Field( + RoofOuterCapFrac: Optional[FlexibleRefValue(float)] = Field( default=1.0, description="Weighting factor for heat capacity of roof [-]", json_schema_extra={"unit": "dimensionless", "display_name": "Roofoutercapfrac"}, ge=0.0, le=1.0, ) - RoofExternalEmissivity: FlexibleRefValue(float) = Field( + RoofExternalEmissivity: Optional[FlexibleRefValue(float)] = Field( default=0.9, description="Emissivity of the external surface of roof [-]", json_schema_extra={ @@ -1423,7 +1423,7 @@ class ArchetypeProperties(BaseModel): ge=0.0, le=1.0, ) - RoofInternalEmissivity: FlexibleRefValue(float) = Field( + RoofInternalEmissivity: Optional[FlexibleRefValue(float)] = Field( default=0.9, description="Emissivity of the internal surface of roof [-]", json_schema_extra={ @@ -1433,7 +1433,7 @@ class ArchetypeProperties(BaseModel): ge=0.0, le=1.0, ) - RoofTransmissivity: FlexibleRefValue(float) = Field( + RoofTransmissivity: Optional[FlexibleRefValue(float)] = Field( default=0.0, description="Transmissivity of roof [-]", json_schema_extra={ @@ -1443,27 +1443,27 @@ class ArchetypeProperties(BaseModel): ge=0.0, le=1.0, ) - RoofAbsorbtivity: FlexibleRefValue(float) = Field( + RoofAbsorbtivity: Optional[FlexibleRefValue(float)] = Field( default=0.8, description="Absorbtivity of roof [-]", json_schema_extra={"unit": "dimensionless", "display_name": "Roofabsorbtivity"}, ge=0.0, le=1.0, ) - RoofReflectivity: FlexibleRefValue(float) = Field( + RoofReflectivity: Optional[FlexibleRefValue(float)] = Field( default=0.2, description="Reflectivity of the external surface of roof [-]", json_schema_extra={"unit": "dimensionless", "display_name": "RoofReflectivity"}, ge=0.0, le=1.0, ) - FloorThickness: FlexibleRefValue(float) = Field( + FloorThickness: Optional[FlexibleRefValue(float)] = Field( default=0.2, description="Thickness of ground floor [m]", json_schema_extra={"unit": "m", "display_name": "Floorthickness"}, gt=0.0, ) - GroundFloorEffectiveConductivity: FlexibleRefValue(float) = Field( + GroundFloorEffectiveConductivity: Optional[FlexibleRefValue(float)] = Field( default=0.15, description="Effective thermal conductivity of ground floor [W m-1 K-1]", json_schema_extra={ @@ -1472,25 +1472,25 @@ class ArchetypeProperties(BaseModel): }, gt=0.0, ) - GroundFloorDensity: FlexibleRefValue(float) = Field( + GroundFloorDensity: Optional[FlexibleRefValue(float)] = Field( default=500.0, description="Effective density of the ground floor [kg m-3]", json_schema_extra={"unit": "kg m^-3", "display_name": "Groundfloordensity"}, gt=0.0, ) - GroundFloorCp: FlexibleRefValue(float) = Field( + GroundFloorCp: Optional[FlexibleRefValue(float)] = Field( default=1500.0, description="Effective specific heat capacity of the ground floor [J kg-1 K-1]", json_schema_extra={"unit": "J kg^-1 K^-1", "display_name": "Groundfloorcp"}, gt=0.0, ) - WindowThickness: FlexibleRefValue(float) = Field( + WindowThickness: Optional[FlexibleRefValue(float)] = Field( default=0.015, description="Window thickness [m]", json_schema_extra={"unit": "m", "display_name": "Windowthickness"}, gt=0.0, ) - WindowEffectiveConductivity: FlexibleRefValue(float) = Field( + WindowEffectiveConductivity: Optional[FlexibleRefValue(float)] = Field( default=1.0, description="Effective thermal conductivity of windows [W m-1 K-1]", json_schema_extra={ @@ -1499,19 +1499,19 @@ class ArchetypeProperties(BaseModel): }, gt=0.0, ) - WindowDensity: FlexibleRefValue(float) = Field( + WindowDensity: Optional[FlexibleRefValue(float)] = Field( default=2500.0, description="Effective density of the windows [kg m-3]", json_schema_extra={"unit": "kg m^-3", "display_name": "Windowdensity"}, gt=0.0, ) - WindowCp: FlexibleRefValue(float) = Field( + WindowCp: Optional[FlexibleRefValue(float)] = Field( default=840.0, description="Effective specific heat capacity of windows [J kg-1 K-1]", json_schema_extra={"unit": "J kg^-1 K^-1", "display_name": "Windowcp"}, gt=0.0, ) - WindowExternalEmissivity: FlexibleRefValue(float) = Field( + WindowExternalEmissivity: Optional[FlexibleRefValue(float)] = Field( default=0.90, description="Emissivity of the external surface of windows [-]", json_schema_extra={ @@ -1521,7 +1521,7 @@ class ArchetypeProperties(BaseModel): ge=0.0, le=1.0, ) - WindowInternalEmissivity: FlexibleRefValue(float) = Field( + WindowInternalEmissivity: Optional[FlexibleRefValue(float)] = Field( default=0.90, description="Emissivity of the internal surface of windows [-]", json_schema_extra={ @@ -1531,7 +1531,7 @@ class ArchetypeProperties(BaseModel): ge=0.0, le=1.0, ) - WindowTransmissivity: FlexibleRefValue(float) = Field( + WindowTransmissivity: Optional[FlexibleRefValue(float)] = Field( default=0.90, description="Transmissivity of windows [-]", json_schema_extra={ @@ -1541,7 +1541,7 @@ class ArchetypeProperties(BaseModel): ge=0.0, le=1.0, ) - WindowAbsorbtivity: FlexibleRefValue(float) = Field( + WindowAbsorbtivity: Optional[FlexibleRefValue(float)] = Field( default=0.01, description="Absorbtivity of windows [-]", json_schema_extra={ @@ -1551,7 +1551,7 @@ class ArchetypeProperties(BaseModel): ge=0.0, le=1.0, ) - WindowReflectivity: FlexibleRefValue(float) = Field( + WindowReflectivity: Optional[FlexibleRefValue(float)] = Field( default=0.09, description="Reflectivity of the external surface of windows [-]", json_schema_extra={ @@ -1562,17 +1562,17 @@ class ArchetypeProperties(BaseModel): le=1.0, ) # TODO: Add defaults below here - InternalMassDensity: FlexibleRefValue(float) = Field( + InternalMassDensity: Optional[FlexibleRefValue(float)] = Field( default=0.0, description="Effective density of the internal mass [kg m-3]", json_schema_extra={"unit": "kg m^-3", "display_name": "Internalmassdensity"}, ) - InternalMassCp: FlexibleRefValue(float) = Field( + InternalMassCp: Optional[FlexibleRefValue(float)] = Field( default=0.0, description="Effective specific heat capacity of internal mass [J kg-1 K-1]", json_schema_extra={"unit": "J kg^-1 K^-1", "display_name": "Internalmasscp"}, ) - InternalMassEmissivity: FlexibleRefValue(float) = Field( + InternalMassEmissivity: Optional[FlexibleRefValue(float)] = Field( default=0.0, description="Emissivity of internal mass [-]", json_schema_extra={ @@ -1580,25 +1580,25 @@ class ArchetypeProperties(BaseModel): "display_name": "Internalmassemissivity", }, ) - MaxHeatingPower: FlexibleRefValue(float) = Field( + MaxHeatingPower: Optional[FlexibleRefValue(float)] = Field( default=0.0, description="Maximum power demand of heating system [W]", json_schema_extra={"unit": "W", "display_name": "Maxheatingpower"}, ge=0.0, ) - WaterTankWaterVolume: FlexibleRefValue(float) = Field( + WaterTankWaterVolume: Optional[FlexibleRefValue(float)] = Field( default=0.15, description="Volume of water in hot water tank [m3]", json_schema_extra={"unit": "m^3", "display_name": "Watertankwatervolume"}, gt=0.0, ) - MaximumHotWaterHeatingPower: FlexibleRefValue(float) = Field( + MaximumHotWaterHeatingPower: Optional[FlexibleRefValue(float)] = Field( default=3000.0, description="Maximum power demand of water heating system [W]", json_schema_extra={"unit": "W", "display_name": "Maximumhotwaterheatingpower"}, gt=0.0, ) - HeatingSetpointTemperature: FlexibleRefValue(float) = Field( + HeatingSetpointTemperature: Optional[FlexibleRefValue(float)] = Field( default=0.0, description="Heating setpoint temperature [degC]", json_schema_extra={ @@ -1606,7 +1606,7 @@ class ArchetypeProperties(BaseModel): "display_name": "Heatingsetpointtemperature", }, ) - CoolingSetpointTemperature: FlexibleRefValue(float) = Field( + CoolingSetpointTemperature: Optional[FlexibleRefValue(float)] = Field( default=0.0, description="Cooling setpoint temperature [degC]", json_schema_extra={ diff --git a/src/supy/data_model/validation/pipeline/phase_b.py b/src/supy/data_model/validation/pipeline/phase_b.py index f521119de..320f311a9 100644 --- a/src/supy/data_model/validation/pipeline/phase_b.py +++ b/src/supy/data_model/validation/pipeline/phase_b.py @@ -1344,31 +1344,36 @@ def adjust_model_dependent_nullification( for site_idx, site in enumerate(sites): props = site.get("properties", {}) - stebbs_block = props.get("stebbs", {}) site_gridid = get_site_gridid(site) - if stebbs_block: - nullified_params = [] - - def _recursive_nullify(block: dict, path: str = ""): - for key, val in block.items(): - current_path = f"{path}.{key}" if path else key - - if isinstance(val, dict): - if "value" in val and val["value"] is not None: - val["value"] = None - nullified_params.append(current_path) - else: - _recursive_nullify(val, current_path) - - _recursive_nullify(stebbs_block) + def _nullify_block(block_name: str, block: Union[dict, list]) -> bool: + if not isinstance(block, (dict, list)) or not block: + return False + + nullified_params: List[str] = [] + + def _recursive_nullify(node: Any, path: str): + if isinstance(node, dict): + if "value" in node: + if node["value"] is not None: + node["value"] = None + nullified_params.append(path) + else: + for key, val in node.items(): + child_path = f"{path}.{key}" if path else key + _recursive_nullify(val, child_path) + elif isinstance(node, list): + for idx, item in enumerate(node): + child_path = f"{path}[{idx}]" if path else f"[{idx}]" + _recursive_nullify(item, child_path) + + _recursive_nullify(block, block_name) if nullified_params: param_list = ", ".join(nullified_params) - adjustments.append( ScientificAdjustment( - parameter="stebbs", + parameter=block_name, site_index=site_idx, site_gridid=site_gridid, old_value=f"stebbsmethod is switched off, nullified {len(nullified_params)} related parameters - {param_list}", @@ -1376,8 +1381,18 @@ def _recursive_nullify(block: dict, path: str = ""): reason=f"stebbsmethod switched off, nullified {len(nullified_params)} related parameters", ) ) + return True + + return False + + site_updated = False + for block_name in ("stebbs", "building_archetype"): + block = props.get(block_name) + if _nullify_block(block_name, block): + props[block_name] = block + site_updated = True - props["stebbs"] = stebbs_block + if site_updated: site["properties"] = props yaml_data["sites"][site_idx] = site From 163b8deb1470d01755b62dc2e242e248c25913ac Mon Sep 17 00:00:00 2001 From: Silvia Rognone Date: Sun, 30 Nov 2025 22:15:01 +0000 Subject: [PATCH 2/5] update CHANGELOG.md --- CHANGELOG.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 95fc8bd87..fa1a95bae 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -33,6 +33,11 @@ ## 2025 +### 30 Nov 2025 +- [bugfix] Add missing `rcmethod` to CRITICAL_PHYSICS_PARAMS in _check_critical_null_physics_params (config.py). +- [bugfix] Add check on ARCHETYPE_REQUIRED_PARAMS in _validate_stebbs (config.py). +- [bugfix] Fix nullification logic in phase_b.py for stebbs parameters to include building_archetype block when stebbsmethod == 0. + ### 29 Nov 2025 - [maintenance] Reorganise `.claude/` directory structure from 7 to 3 directories (#945) - Skills now single source of truth for all Claude Code knowledge From a03d5af94461c9902ec4c0ac1244da20fae74994 Mon Sep 17 00:00:00 2001 From: Silvia Rognone Date: Sun, 30 Nov 2025 22:15:43 +0000 Subject: [PATCH 3/5] updated CHANGELOG.md --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index fa1a95bae..aa2bcb2ca 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -21,7 +21,7 @@ | Year | Features | Bugfixes | Changes | Maintenance | Docs | Total | |------|----------|----------|---------|-------------|------|-------| -| 2025 | 39 | 27 | 18 | 36 | 19 | 135 | +| 2025 | 39 | 30 | 18 | 36 | 19 | 138 | | 2024 | 12 | 17 | 1 | 12 | 1 | 43 | | 2023 | 11 | 14 | 3 | 9 | 1 | 38 | | 2022 | 15 | 18 | 0 | 7 | 0 | 40 | From be06694b64a1375c0ae415b9c958ddf838375afb Mon Sep 17 00:00:00 2001 From: Silvia Rognone Date: Wed, 3 Dec 2025 12:01:21 +0000 Subject: [PATCH 4/5] add: test_stebbsmethod0_nullifies_building_archetype_values to cover also building_archetype nullification and fixed precheck_model_option_rules to include building_archetype in the nullification logic --- .../data_model/validation/core/yaml_helpers.py | 10 ++++++++++ test/data_model/test_yaml_processing.py | 14 ++++++++++++++ 2 files changed, 24 insertions(+) diff --git a/src/supy/data_model/validation/core/yaml_helpers.py b/src/supy/data_model/validation/core/yaml_helpers.py index 0ec2b38f6..ab58ae9d6 100644 --- a/src/supy/data_model/validation/core/yaml_helpers.py +++ b/src/supy/data_model/validation/core/yaml_helpers.py @@ -1428,6 +1428,16 @@ def _recursive_nullify(block: dict): if isinstance(stebbs_block, dict): _recursive_nullify(stebbs_block) props["stebbs"] = stebbs_block + for site_idx, site in enumerate(data.get("sites", [])): + props = site.get("properties", {}) or {} + archetype_block = props.get("building_archetype", {}) or {} + if isinstance(archetype_block, dict): + _recursive_nullify(archetype_block) + props["building_archetype"] = archetype_block + # ALWAYS write back props after making changes above + if isinstance(site, dict): + site["properties"] = props + data["sites"][site_idx] = site # --- EMISSIONS / CO2 RULE: when emissionsmethod 0..4, CO2 is not computed, nullify co2 params --- emissionsmethod = get_value_safe(physics, "emissionsmethod") diff --git a/test/data_model/test_yaml_processing.py b/test/data_model/test_yaml_processing.py index ebf46287f..dba03001a 100644 --- a/test/data_model/test_yaml_processing.py +++ b/test/data_model/test_yaml_processing.py @@ -1936,6 +1936,20 @@ def test_stebbsmethod1_leaves_stebbs_untouched(): out = result["sites"][0]["properties"]["stebbs"] assert out["WallInternalConvectionCoefficient"]["value"] == 5.0 +def test_stebbsmethod0_nullifies_building_archetype_values(): + yaml_input = { + "model": {"physics": {"stebbsmethod": {"value": 0}}}, + "sites": [{"properties": { + "building_archetype": { + "BuildingType": {"value": "SampleType"}, + "stebbs_Height": {"value": 10.0}, + } + }}], + } + result = precheck_model_option_rules(deepcopy(yaml_input)) + out = result["sites"][0]["properties"]["building_archetype"] + assert out["BuildingType"]["value"] is None + assert out["stebbs_Height"]["value"] is None def _build_site_with_co2(co2_block): return {"properties": {"anthropogenic_emissions": {"co2": co2_block}}} From de6e79f6e6f7f7242d73a76bcf2b4d79a53aede0 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Wed, 3 Dec 2025 12:02:16 +0000 Subject: [PATCH 5/5] style: auto-format code with ruff and fprettify Co-authored-by: dayantur <71443948+dayantur@users.noreply.github.com> --- test/data_model/test_yaml_processing.py | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/test/data_model/test_yaml_processing.py b/test/data_model/test_yaml_processing.py index dba03001a..67b49f121 100644 --- a/test/data_model/test_yaml_processing.py +++ b/test/data_model/test_yaml_processing.py @@ -1936,21 +1936,27 @@ def test_stebbsmethod1_leaves_stebbs_untouched(): out = result["sites"][0]["properties"]["stebbs"] assert out["WallInternalConvectionCoefficient"]["value"] == 5.0 + def test_stebbsmethod0_nullifies_building_archetype_values(): yaml_input = { "model": {"physics": {"stebbsmethod": {"value": 0}}}, - "sites": [{"properties": { - "building_archetype": { - "BuildingType": {"value": "SampleType"}, - "stebbs_Height": {"value": 10.0}, + "sites": [ + { + "properties": { + "building_archetype": { + "BuildingType": {"value": "SampleType"}, + "stebbs_Height": {"value": 10.0}, + } + } } - }}], + ], } result = precheck_model_option_rules(deepcopy(yaml_input)) out = result["sites"][0]["properties"]["building_archetype"] assert out["BuildingType"]["value"] is None assert out["stebbs_Height"]["value"] is None + def _build_site_with_co2(co2_block): return {"properties": {"anthropogenic_emissions": {"co2": co2_block}}}