Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
55 commits
Select commit Hold shift + click to select a range
772d7dd
add add void deck to format helper, add checker that floors_ag - void…
ShiZhongming Sep 9, 2025
471bf9f
add add void deck to format helper, add checker that floors_ag - void…
ShiZhongming Sep 14, 2025
cecbac8
Update archetypes_mapper.py
ShiZhongming Sep 14, 2025
89f30a8
unify the source of truth to architecture
ShiZhongming Sep 14, 2025
e4368a0
Update result_summary.py
ShiZhongming Sep 14, 2025
d94b8cf
Merge branch 'master' into some-fix-from-idp-shanghai---void-deck-and…
ShiZhongming Sep 19, 2025
d59270a
Refactor void_deck validation in migrator
reyery Sep 19, 2025
ca9dc84
Remove migrate_void_deck_data call from main
reyery Sep 19, 2025
261e502
Vectorize architecture property calculations
reyery Sep 19, 2025
3c6908b
Add type hint for dataframe
reyery Sep 19, 2025
f49e9d8
Update docstring for generate_architecture_csv
reyery Sep 19, 2025
2378c8d
Generate architecture CSV before demand calculation
reyery Sep 19, 2025
a3c5083
Generate architecture CSV if missing in plot_main
reyery Sep 19, 2025
9555831
Remove unused import
reyery Sep 19, 2025
5b160d7
Fix warning message formatting in void_deck_migrator
reyery Sep 19, 2025
a3a0cc6
Refactor architecture and void_deck utilities
reyery Sep 19, 2025
869dbc8
Refactor imports to use cea.datamanagement.utils
reyery Sep 19, 2025
0f7f254
Generate architecture CSV in thermal loads test setup
reyery Sep 19, 2025
6dfdf54
Add void deck data migration to thermal loads test
reyery Sep 19, 2025
6b4c12a
Fix merge conflicts in architecture CSV generation
reyery Sep 19, 2025
fbe780d
Get geometry data from zone dataframe
reyery Sep 19, 2025
261ab2c
Save updated zone geometry after void_deck migration
reyery Sep 19, 2025
15d3817
Call migrate_void_deck_data in generate_architecture_csv
reyery Sep 19, 2025
a48c124
Improve architecture CSV generation error handling
reyery Sep 19, 2025
c5650eb
Add docstring to calc_useful_areas function
reyery Sep 19, 2025
46e4390
Refactor useful area calculations in demand module
reyery Sep 19, 2025
7b26b9b
Use DataFrame index for merge
reyery Sep 19, 2025
2eb9c7b
Refactor geometry_reader_radiation_daysim method
reyery Sep 20, 2025
22d9c8e
Update geometry type to GeoDataFrame in BuildingRCModel
reyery Sep 20, 2025
7a25a5d
Refactor geometry_reader_radiation_daysim to update envelope DataFrame
reyery Sep 20, 2025
00812aa
Restrict zone geometry columns in BuildingGeometry
reyery Sep 20, 2025
f82c3d1
Refactor RC model calculations to use areas_df and envelope
reyery Sep 20, 2025
0447871
Fix solar calculation to use envelope U-values
reyery Sep 20, 2025
49313cf
Remove duplicated U-value fields from RCModelProperties
reyery Sep 20, 2025
bf104c5
Move area fields from RCModelProperties to EnvelopeProperties
reyery Sep 20, 2025
03cc1c1
Fix void_deck reference in RC model calculation
reyery Sep 20, 2025
38901a9
Remove footprint attribute from RCModelProperties
reyery Sep 20, 2025
0db063b
Ensure RCModelProperties has relevant properties
reyery Sep 20, 2025
60e2758
Use RC model GFA for small building detection
reyery Sep 20, 2025
8c64beb
Refactor to use envelope attributes after changes
reyery Sep 20, 2025
93e8b12
Use bpr attributes instead of recalculating
reyery Sep 20, 2025
47390bb
Remove 'name' column from hourly demand dataframes
reyery Sep 20, 2025
0f0bee5
Remove architecture CSV generation from test setup
reyery Sep 20, 2025
bdba1c5
Remove architecture CSV generation from demand_main
reyery Sep 20, 2025
38621da
Remove unused imports
reyery Sep 20, 2025
d8f9ce3
Handle void_deck column conflict in DataFrame merge
reyery Sep 20, 2025
565f8f8
Remove unused imports and architecture CSV generation
reyery Sep 21, 2025
f9890f6
Add architecture metrics handling to result summary
reyery Sep 21, 2025
4944435
Remove architecture output methods from InputLocator
reyery Sep 21, 2025
f6d3239
Remove generate_architecture_csv function
reyery Sep 21, 2025
45c1853
Remove 'name' from BUILDINGS_DEMANDS_COLUMNS
reyery Sep 21, 2025
96e8071
Refactor substation HEX data structures to use Series
reyery Sep 21, 2025
c59da2a
Refactor substation_HEX_sizing return values
reyery Sep 21, 2025
46b7eb7
Use .items instead of .keys
reyery Sep 21, 2025
ab9746a
Refactor demand DataFrame usage in substation_matrix
reyery Sep 21, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion cea/analysis/lca/embodied.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
import cea.inputlocator
from cea.constants import SERVICE_LIFE_OF_BUILDINGS, SERVICE_LIFE_OF_TECHNICAL_SYSTEMS, \
CONVERSION_AREA_TO_FLOOR_AREA_RATIO, EMISSIONS_EMBODIED_TECHNICAL_SYSTEMS
from cea.datamanagement.void_deck_migrator import migrate_void_deck_data
from cea.datamanagement.utils import migrate_void_deck_data

__author__ = "Jimeno A. Fonseca"
__copyright__ = "Copyright 2015, Architecture and Building Systems - ETH Zurich"
Expand Down
2 changes: 1 addition & 1 deletion cea/datamanagement/archetypes_mapper.py
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,7 @@ def archetypes_mapper(locator,
supply_mapper(locator, building_typology_df)



def indoor_comfort_mapper(list_uses, locator, occupant_densities, building_typology_df):
comfort_DB = pd.read_csv(locator.get_database_archetypes_use_type())
# define comfort
Expand Down Expand Up @@ -400,7 +401,6 @@ def verify_building_standards(building_typology_df, db_standards):
diff = typology_standards.difference(db_standards)
raise ValueError(f'The following standards are not found in the database: {", ".join(diff)}')


def main(config):
"""
Run the properties script with input from the reference case and compare the results. This ensures that changes
Expand Down
19 changes: 13 additions & 6 deletions cea/datamanagement/format_helper/cea4_migrate.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
from cea.datamanagement.format_helper.cea4_verify import cea4_verify, verify_shp, \
COLUMNS_ZONE_4, print_verification_results_4, path_to_input_file_without_db_4, CSV_BUILDING_PROPERTIES_3_CSV
from cea.datamanagement.format_helper.cea4_verify_db import check_directory_contains_csv
from cea.datamanagement.utils import migrate_void_deck_data
from cea.utilities.dbf import dbf_to_dataframe

COLUMNS_ZONE_3 = ['Name', 'floors_bg', 'floors_ag', 'height_bg', 'height_ag']
Expand Down Expand Up @@ -550,9 +551,12 @@ def migrate_cea3_to_cea4(scenario, verbose=False):
pass
# print('For Scenario: {scenario}, '.format(scenario=scenario_name), 'zone.shp already follows the CEA-4 format.')
else:
raise ValueError('! zone.shp exists but follows neither the CEA-3 nor CEA-4 format. CEA cannot proceed with the data migration.'
'Check the following column(s) for CEA-3 format: {list_missing_attributes_zone_3}.'.format(list_missing_attributes_zone_3=list_missing_attributes_zone_3),
'Check the following column(s) for CEA-4 format: {list_missing_attributes_zone_4}.'.format(list_missing_attributes_zone_4=list_missing_attributes_zone_4)
if list_missing_attributes_zone_4[0] == 'void_deck' and len(list_missing_attributes_zone_4) == 1:
config = cea.config.Configuration()
locator = cea.inputlocator.InputLocator(config.scenario)
migrate_void_deck_data(locator)
else:
raise ValueError('! zone.shp exists but follows neither the CEA-3 nor CEA-4 format. CEA cannot proceed with the data migration. Check the following column(s) for CEA-3 format: {list_missing_attributes_zone_3}.'.format(list_missing_attributes_zone_3=list_missing_attributes_zone_3), 'Check the following column(s) for CEA-4 format: {list_missing_attributes_zone_4}.'.format(list_missing_attributes_zone_4=list_missing_attributes_zone_4)
Comment on lines +554 to +559
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Guard the ‘void_deck-only’ check and build the locator from the passed scenario (also avoids indexing errors).

  • Indexing list_missing_attributes_zone_4[0] will crash when the list is empty.
  • Use an equality check against ['void_deck'] and instantiate InputLocator with the function’s scenario argument (not the global config). Also, cea.inputlocator isn’t imported in this file.

Apply this diff:

-            else:
-                if list_missing_attributes_zone_4[0] == 'void_deck' and len(list_missing_attributes_zone_4) == 1:
-                    config = cea.config.Configuration()
-                    locator = cea.inputlocator.InputLocator(config.scenario)
-                    migrate_void_deck_data(locator)
-                else:
+            else:
+                if list_missing_attributes_zone_4 == ['void_deck']:
+                    locator = cea.inputlocator.InputLocator(scenario)
+                    migrate_void_deck_data(locator)
+                else:
                     raise ValueError('! zone.shp exists but follows neither the CEA-3 nor CEA-4 format. CEA cannot proceed with the data migration. Check the following column(s) for CEA-3 format: {list_missing_attributes_zone_3}.'.format(list_missing_attributes_zone_3=list_missing_attributes_zone_3), 'Check the following column(s) for CEA-4 format: {list_missing_attributes_zone_4}.'.format(list_missing_attributes_zone_4=list_missing_attributes_zone_4)
                                  )

Add the missing import near the top of the file:

import cea.inputlocator
🤖 Prompt for AI Agents
In cea/datamanagement/format_helper/cea4_migrate.py around lines 554 to 559,
replace the unsafe indexing check and wrong locator creation: guard against
empty list by checking equality to ['void_deck'] (i.e.
list_missing_attributes_zone_4 == ['void_deck']) instead of
list_missing_attributes_zone_4[0], instantiate the InputLocator using the
function's scenario argument (locator = cea.inputlocator.InputLocator(scenario))
rather than creating a new global config, and add the missing import "import
cea.inputlocator" near the top of the file so the module is available.

)
else:
print("! Ensure zone.shp (CEA-3 format) is present in building-geometry folder.")
Expand All @@ -571,9 +575,12 @@ def migrate_cea3_to_cea4(scenario, verbose=False):
pass
# print('For Scenario: {scenario}, '.format(scenario=scenario_name), 'surroundings.shp already follows the CEA-4 format.')
else:
raise ValueError('surroundings.shp exists but follows neither the CEA-3 nor CEA-4 format. CEA cannot proceed with the data migration.'
'Check the following column(s) for CEA-3 format: {list_missing_attributes_surroundings_3}.'.format(list_missing_attributes_surroundings_3=list_missing_attributes_surroundings_3),
'Check the following column(s) for CEA-4 format: {list_missing_attributes_surroundings_4}.'.format(list_missing_attributes_surroundings_4=list_missing_attributes_surroundings_4)
if list_missing_attributes_zone_4[0] == 'void_deck' and len(list_missing_attributes_zone_4) == 1:
config = cea.config.Configuration()
locator = cea.inputlocator.InputLocator(config.scenario)
migrate_void_deck_data(locator)
else:
raise ValueError('surroundings.shp exists but follows neither the CEA-3 nor CEA-4 format. CEA cannot proceed with the data migration. Check the following column(s) for CEA-3 format: {list_missing_attributes_surroundings_3}.'.format(list_missing_attributes_surroundings_3=list_missing_attributes_surroundings_3), 'Check the following column(s) for CEA-4 format: {list_missing_attributes_surroundings_4}.'.format(list_missing_attributes_surroundings_4=list_missing_attributes_surroundings_4)
)
Comment on lines +578 to 584
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Wrong list referenced in surroundings branch; remove void_deck migration here.

void_deck applies to zone.shp, not surroundings.shp. This branch incorrectly inspects list_missing_attributes_zone_4 and calls the void_deck migrator. It should just raise the existing error for surroundings.

Apply this diff:

-            else:
-                if list_missing_attributes_zone_4[0] == 'void_deck' and len(list_missing_attributes_zone_4) == 1:
-                    config = cea.config.Configuration()
-                    locator = cea.inputlocator.InputLocator(config.scenario)
-                    migrate_void_deck_data(locator)
-                else:
-                    raise ValueError('surroundings.shp exists but follows neither the CEA-3 nor CEA-4 format. CEA cannot proceed with the data migration. Check the following column(s) for CEA-3 format: {list_missing_attributes_surroundings_3}.'.format(list_missing_attributes_surroundings_3=list_missing_attributes_surroundings_3), 'Check the following column(s) for CEA-4 format: {list_missing_attributes_surroundings_4}.'.format(list_missing_attributes_surroundings_4=list_missing_attributes_surroundings_4)
-                                 )
+            else:
+                raise ValueError('surroundings.shp exists but follows neither the CEA-3 nor CEA-4 format. CEA cannot proceed with the data migration. Check the following column(s) for CEA-3 format: {list_missing_attributes_surroundings_3}.'.format(list_missing_attributes_surroundings_3=list_missing_attributes_surroundings_3), 'Check the following column(s) for CEA-4 format: {list_missing_attributes_surroundings_4}.'.format(list_missing_attributes_surroundings_4=list_missing_attributes_surroundings_4))
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if list_missing_attributes_zone_4[0] == 'void_deck' and len(list_missing_attributes_zone_4) == 1:
config = cea.config.Configuration()
locator = cea.inputlocator.InputLocator(config.scenario)
migrate_void_deck_data(locator)
else:
raise ValueError('surroundings.shp exists but follows neither the CEA-3 nor CEA-4 format. CEA cannot proceed with the data migration. Check the following column(s) for CEA-3 format: {list_missing_attributes_surroundings_3}.'.format(list_missing_attributes_surroundings_3=list_missing_attributes_surroundings_3), 'Check the following column(s) for CEA-4 format: {list_missing_attributes_surroundings_4}.'.format(list_missing_attributes_surroundings_4=list_missing_attributes_surroundings_4)
)
else:
raise ValueError('surroundings.shp exists but follows neither the CEA-3 nor CEA-4 format. CEA cannot proceed with the data migration. Check the following column(s) for CEA-3 format: {list_missing_attributes_surroundings_3}.'.format(list_missing_attributes_surroundings_3=list_missing_attributes_surroundings_3), 'Check the following column(s) for CEA-4 format: {list_missing_attributes_surroundings_4}.'.format(list_missing_attributes_surroundings_4=list_missing_attributes_surroundings_4))
🤖 Prompt for AI Agents
In cea/datamanagement/format_helper/cea4_migrate.py around lines 578 to 584, the
code incorrectly checks list_missing_attributes_zone_4 and calls
migrate_void_deck_data in the surroundings.shp branch; remove that conditional
and the migrate_void_deck_data call, and instead always raise the existing
ValueError for surroundings (combine the two formatted messages as currently
written) so surroundings.shp does not trigger void_deck migration.

else:
print('! (optional) Run Surroundings Helper to generate surroundings.shp after the data migration.')
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,15 @@
from __future__ import annotations
from typing import TYPE_CHECKING
import warnings

import pandas as pd
import geopandas as gpd
import cea.inputlocator
import warnings

if TYPE_CHECKING:
from cea.inputlocator import InputLocator

def migrate_void_deck_data(locator: cea.inputlocator.InputLocator) -> None:

def migrate_void_deck_data(locator: InputLocator) -> None:
"""Check if void_deck exists in zone.shp and copy it from envelope.csv if necessary.

:param locator: the input locator object.
Expand All @@ -23,6 +28,8 @@ def migrate_void_deck_data(locator: cea.inputlocator.InputLocator) -> None:
envelope_df[["name", "void_deck"]], on="name", how="left"
)
zone_gdf["void_deck"] = zone_gdf["void_deck"].fillna(0)
zone_gdf.to_file(locator.get_zone_geometry())

print("Migrated void_deck data from envelope.csv to zone.shp.")
envelope_df.drop(columns=["void_deck"], inplace=True)
envelope_df.to_csv(locator.get_building_architecture(), index=False)
Expand All @@ -32,4 +39,11 @@ def migrate_void_deck_data(locator: cea.inputlocator.InputLocator) -> None:
warnings.warn(
"No void_deck data found in envelope.csv, setting to 0 in zone.shp"
)
zone_gdf.to_file(locator.get_zone_geometry(), driver="ESRI Shapefile")

# Validate that floors_ag is larger than void_deck for each building
actual_floors = zone_gdf["floors_ag"] - zone_gdf["void_deck"]
invalid_floors = zone_gdf[actual_floors <= 0]
if len(invalid_floors) > 0:
invalid_buildings = invalid_floors["name"].tolist()
warnings.warn(f"Some buildings have void_deck greater than floors_ag: {invalid_buildings}",
RuntimeWarning)
8 changes: 5 additions & 3 deletions cea/demand/building_properties/building_geometry.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
from __future__ import annotations
from geopandas import GeoDataFrame as Gdf

from cea.datamanagement.databases_verification import COLUMNS_ZONE_GEOMETRY
from cea.utilities.standardize_coordinates import get_lat_lon_projected_shapefile, get_projected_coordinate_system

from typing import TYPE_CHECKING
Expand All @@ -25,18 +26,19 @@ def __init__(self, locator: InputLocator, building_names: list[str]):
:param locator: an InputLocator for locating the input files
:param building_names: list of buildings to read properties for
"""
prop_geometry: Gdf = Gdf.from_file(locator.get_zone_geometry()).set_index('name').loc[building_names]
prop_geometry = Gdf.from_file(locator.get_zone_geometry())[COLUMNS_ZONE_GEOMETRY + ['geometry']].set_index('name').loc[building_names]

# reproject to projected coordinate system (in meters) to calculate area
lat, lon = get_lat_lon_projected_shapefile(prop_geometry)
target_crs = get_projected_coordinate_system(float(lat), float(lon))
prop_geometry: Gdf = prop_geometry.to_crs(target_crs)
prop_geometry = prop_geometry.to_crs(target_crs)

# TODO: Check usage of footprint and perimeter in other parts of the code
prop_geometry['footprint'] = prop_geometry.area
prop_geometry['perimeter'] = prop_geometry.length
prop_geometry['Blength'], prop_geometry['Bwidth'] = self.calc_bounding_box_geom(prop_geometry)

self._prop_geometry = prop_geometry.drop('geometry', axis=1)
self._prop_geometry = prop_geometry

@staticmethod
def calc_bounding_box_geom(gdf: Gdf) -> tuple[list[float], list[float]]:
Expand Down
5 changes: 1 addition & 4 deletions cea/demand/building_properties/building_properties.py
Original file line number Diff line number Diff line change
Expand Up @@ -108,10 +108,7 @@ def check_buildings(self, min_gfa: float = 100):
"""

# FIXME: This is not a very good indicator of potential issue which causes overheating problems in some cases.
footprint = self.geometry._prop_geometry.footprint
floors = self.geometry._prop_geometry.floors_ag

GFA_m2 = footprint * floors
GFA_m2 = self.rc_model._prop_rc_model['GFA_m2']
small_buildings = GFA_m2[GFA_m2 < min_gfa]

if len(small_buildings) > 0:
Expand Down
24 changes: 10 additions & 14 deletions cea/demand/building_properties/building_properties_row.py
Original file line number Diff line number Diff line change
Expand Up @@ -234,6 +234,13 @@ class EnvelopeProperties:
A_op: Annotated[float, "Opaque area above ground [m2]"]
win_wall: Annotated[float, "Window to wall ratio [-]"]

# Area fields added from BuildingRCModel
Aroof: Annotated[float, "Roof area [m2]"]
Aunderside: Annotated[float, "Underside area [m2]"]
Awall_ag: Annotated[float, "Above ground wall area [m2]"]
Awin_ag: Annotated[float, "Above ground window area [m2]"]
Aop_bg: Annotated[float, "Area of opaque surfaces below ground [m2]"]

Comment on lines +237 to +243
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

New area fields should have safe defaults

Envelope CSVs in the wild may not include these new columns yet. Without defaults, from_dict will raise. Provide 0.0 defaults to maintain backward compatibility.

Apply:

-    Aroof: Annotated[float, "Roof area [m2]"]
-    Aunderside: Annotated[float, "Underside area [m2]"]
-    Awall_ag: Annotated[float, "Above ground wall area [m2]"]
-    Awin_ag: Annotated[float, "Above ground window area [m2]"]
-    Aop_bg: Annotated[float, "Area of opaque surfaces below ground [m2]"]
+    Aroof: Annotated[float, "Roof area [m2]"] = 0.0
+    Aunderside: Annotated[float, "Underside area [m2]"] = 0.0
+    Awall_ag: Annotated[float, "Above ground wall area [m2]"] = 0.0
+    Awin_ag: Annotated[float, "Above ground window area [m2]"] = 0.0
+    Aop_bg: Annotated[float, "Area of opaque surfaces below ground [m2]"] = 0.0
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
# Area fields added from BuildingRCModel
Aroof: Annotated[float, "Roof area [m2]"]
Aunderside: Annotated[float, "Underside area [m2]"]
Awall_ag: Annotated[float, "Above ground wall area [m2]"]
Awin_ag: Annotated[float, "Above ground window area [m2]"]
Aop_bg: Annotated[float, "Area of opaque surfaces below ground [m2]"]
# Area fields added from BuildingRCModel
Aroof: Annotated[float, "Roof area [m2]"] = 0.0
Aunderside: Annotated[float, "Underside area [m2]"] = 0.0
Awall_ag: Annotated[float, "Above ground wall area [m2]"] = 0.0
Awin_ag: Annotated[float, "Above ground window area [m2]"] = 0.0
Aop_bg: Annotated[float, "Area of opaque surfaces below ground [m2]"] = 0.0
🤖 Prompt for AI Agents
In cea/demand/building_properties/building_properties_row.py around lines 237 to
243, the new area fields Aroof, Aunderside, Awall_ag, Awin_ag and Aop_bg lack
defaults so from_dict will raise when CSVs omit them; add safe defaults of 0.0
to each field declaration (e.g. set each Annotated field to = 0.0) so missing
columns map to 0.0 and maintain backward compatibility.

# FIXME: These fields does not neccessarily describe the building envelope
Es: Annotated[float, "Heated/cooled share [-]"]
occupied_bg: Annotated[float, "Basement occupation factor [-]"]
Expand Down Expand Up @@ -263,18 +270,14 @@ def from_dict(cls, solar: dict):
@dataclass(frozen=True)
class RCModelProperties:
# --- Area properties ---
footprint: Annotated[float, "Building footprint area [m2]"]
Atot: Annotated[float, "Area of all surfaces facing the building zone [m2]"]
Am: Annotated[float, "Effective mass area [m2]"]
# TODO: Determine if these fields belong here (calculated using calc_useful_areas)
Comment on lines +273 to +276
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

RCModelProperties: add defaults for newly required fields

If upstream dict lacks 'footprint' or 'Am' (computed later), construction will fail. Provide safe defaults here too.

Apply:

-    footprint: Annotated[float, "Building footprint area [m2]"]
+    footprint: Annotated[float, "Building footprint area [m2]"] = 0.0
@@
-    Am: Annotated[float, "Effective mass area [m2]"]
+    Am: Annotated[float, "Effective mass area [m2]"] = 0.0
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
footprint: Annotated[float, "Building footprint area [m2]"]
Atot: Annotated[float, "Area of all surfaces facing the building zone [m2]"]
Am: Annotated[float, "Effective mass area [m2]"]
# TODO: Determine if these fields belong here (calculated using calc_useful_areas)
footprint: Annotated[float, "Building footprint area [m2]"] = 0.0
Atot: Annotated[float, "Area of all surfaces facing the building zone [m2]"]
Am: Annotated[float, "Effective mass area [m2]"] = 0.0
# TODO: Determine if these fields belong here (calculated using calc_useful_areas)
🤖 Prompt for AI Agents
In cea/demand/building_properties/building_properties_row.py around lines
273-276, RCModelProperties now requires 'footprint' and 'Am' but these keys may
be missing from upstream dict causing construction to fail; add safe default
values (e.g., footprint=0.0 and Am=0.0) to the dataclass/constructor/field
definitions so instances can be created when those fields are absent, and ensure
the defaults match the annotated types and do not break later computations that
will overwrite them.

Af: Annotated[float, "Conditioned floor area (areas that are heated or cooled) [m2]"]
GFA_m2: Annotated[float, "Gross floor area [m2]"]
footprint: Annotated[float, "Building footprint area [m2]"]
Aroof: Annotated[float, "Roof area [m2]"]
Aunderside: Annotated[float, "Underside area [m2]"]
Awall_ag: Annotated[float, "Above ground wall area [m2]"]
Awin_ag: Annotated[float, "Above ground window area [m2]"]
Am: Annotated[float, "Effective mass area [m2]"]
Aef: Annotated[float, "Electrified area (share of gross floor area that is electrified) [m2]"]
Aocc: Annotated[float, "Occupied floor area [m2]"]
Aop_bg: Annotated[float, "Area of opaque surfaces below ground [m2]"]
Hs_ag: Annotated[float, "Share of above-ground gross floor area that is conditioned [m2/m2]"]

# --- Thermal properties ---
Expand All @@ -289,13 +292,6 @@ class RCModelProperties:
Htr_op: Annotated[float, "Thermal transmission coefficient for opaque surfaces in RC-model [W/K]"]
Htr_w: Annotated[float, "Thermal transmission coefficient for windows in RC-model [W/K]"]

# FIXME: Decide if these fields should be in envelope or rc_model
# --- U-values (Duplicated Envelope properties) ---
U_wall: Annotated[float, "U-value of wall [W/m2K]"]
U_roof: Annotated[float, "U-value of roof [W/m2K]"]
U_win: Annotated[float, "U-value of windows [W/m2K]"]
U_base: Annotated[float, "U-value of floor [W/m2K]"]

@classmethod
def from_dict(cls, rc_model: dict):
field_names = cls.__annotations__.keys()
Expand Down
Loading
Loading