Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
429c2ba
Add occupant CO2 Calculation.
veronikarichter Mar 31, 2026
64a6bd2
occupant CO2 calculation defaults to false.
veronikarichter Mar 31, 2026
ceb925e
Robustness improvements, cover cases with None values.
veronikarichter Mar 31, 2026
b0ef8b5
Improve typing and calculation of non-rectangular shapes
veronikarichter Mar 31, 2026
d3ddd49
Extend options of shadow calculations, include FullInteriorAndExterio…
veronikarichter Mar 31, 2026
9751fea
Improve handling of collinear and coincident vertices when setting up…
veronikarichter Mar 31, 2026
4bfa822
Add bim2sim elements for Plates, Covering, Insulation and add pattern…
veronikarichter Mar 31, 2026
e37e5b7
Add additional recognition of StoreyName structures for automatically…
veronikarichter Mar 31, 2026
14693d8
Update total number of subclasses in the bim2sim tool.
veronikarichter Mar 31, 2026
729d06c
Merge branch 'development' into 877-add-minor-modificaitons-to-handle…
veronikarichter Apr 14, 2026
47ea01d
update test resources for minor modifications
veronikarichter Apr 15, 2026
f18f32d
Add sim_setting to disable serialization of elements (e.g., for testi…
veronikarichter Apr 15, 2026
0b109a2
Disable serialization of elements in EnergyPlus tests.
veronikarichter Apr 15, 2026
fd7a46a
Merge branch 'development' into 877-add-minor-modificaitons-to-handle…
veronikarichter Apr 15, 2026
fcf8554
Add simsetting for shadow calculation method
veronikarichter Apr 15, 2026
ad55771
Reformatted file.
veronikarichter Apr 15, 2026
d07c17b
Add simsettings for window shading material.
veronikarichter Apr 15, 2026
2db73ee
Update number of resulting playground elements.
veronikarichter Apr 15, 2026
f52e0b5
update test resources for CFD changes
veronikarichter Apr 15, 2026
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 .gitmodules
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
[submodule "test/resources"]
path = test/resources
url = https://github.com/BIM2SIM/bim2sim-test-resources.git
branch = main
branch = 19-update-test-resources-for-minor-mods-for-complex-files
[submodule "butterfly"]
path = butterfly
url = https://github.com/BIM2SIM/butterfly.git
Expand Down
3 changes: 2 additions & 1 deletion bim2sim/elements/base_elements.py
Original file line number Diff line number Diff line change
Expand Up @@ -352,7 +352,8 @@ def filter_for_text_fragments(cls, ifc_element, ifc_units: dict,
results = []

# Check name matches
name_hits = [p.search(ifc_element.Name) for p in cls.pattern_ifc_type]
name_hits = [p.search(ifc_element.Name) for p in (
cls.pattern_ifc_type) if ifc_element.Name]
name_hits = [hit for hit in name_hits if hit is not None]
if name_hits:
quality_logger.info(
Expand Down
158 changes: 158 additions & 0 deletions bim2sim/elements/bps_elements.py
Original file line number Diff line number Diff line change
Expand Up @@ -1031,6 +1031,19 @@ def _get_bound_shape(self, name):
unify.Initialize(shape)
unify.Build()
shape = unify.Shape()
# apply removal of coincident and collinear points to all new faces
# to avoid errors lateron for mismatched vertex numbers
faces = PyOCCTools.get_faces_from_shape(shape)
if len(faces) > 1:
unify = ShapeUpgrade_UnifySameDomain()
unify.Initialize(shape)
unify.Build()
shape = unify.Shape()
faces = PyOCCTools.get_faces_from_shape(shape)
face = faces[0]
face = PyOCCTools.remove_coincident_and_collinear_points_from_face(
face)
shape = face

if self.bound_element is not None:
bi = self.bound_element
Expand Down Expand Up @@ -1740,6 +1753,11 @@ class InnerFloor(Slab):
"IfcSlab": ['FLOOR']
}

pattern_ifc_type = [
re.compile('Geschossdecke', flags=re.IGNORECASE),
re.compile('Ceiling', flags=re.IGNORECASE)
]

def calc_cost_group(self) -> int:
"""Calc cost group for Floors

Expand Down Expand Up @@ -1979,6 +1997,146 @@ def __init__(self, *args, **kwargs):
)


class Plate(BPSProductWithLayers):
ifc_types = {"IfcPlate": ['*', 'CURTAIN_PANEL', 'SHEET']}
def calc_cost_group(self) -> int:
"""Calc cost group for Plates
External: 337
Internal: 346
"""
if self.is_external:
return 337
elif not self.is_external:
return 346
else:
return 300
width = attribute.Attribute(
default_ps=("Qto_PlateBaseQuantities", "Width"),
functions=[BPSProductWithLayers.get_thickness_by_layers],
unit=ureg.m
)
net_volume = attribute.Attribute(
default_ps=("Qto_PlateBaseQuantities", "NetVolume"),
unit=ureg.m **3
)
gross_volume = attribute.Attribute(
default_ps=("Qto_PlateBaseQuantities", "GrossVolume"),
unit=ureg.m **3
)
net_area = attribute.Attribute(
default_ps=("Qto_PlateBaseQuantities", "NetArea"),
unit=ureg.m **3
)
gross_area = attribute.Attribute(
default_ps=("Qto_PlateBaseQuantities", "GrossArea"),
unit=ureg.m **3
)
net_weight = attribute.Attribute(
default_ps=("Qto_PlateBaseQuantities", "NetWeight"),
unit=ureg.m **3
)
gross_weight = attribute.Attribute(
default_ps=("Qto_PlateBaseQuantities", "GrossWeight"),
unit=ureg.m **3
)
is_load_bearing = attribute.Attribute(
default_ps=("Pset_PlateCommon", "LoadBearing"),
)
u_value = attribute.Attribute(
default_ps=("Pset_PlateCommon", "ThermalTransmittance"),
unit=ureg.W / ureg.K / ureg.meter ** 2,
functions=[BPSProductWithLayers.get_u_value],
)


class Covering(BPSProductWithLayers):
# todo connect covering with element via CoversElements and CoversSpaces
ifc_types = {'IfcCovering': [
'CEILING',
'FLOORING',
'CLADDING',
'ROOFING',
'MODLING',
'SKIRTINGBOARD'
]
}
def __init__(self, *args, **kwargs):
"""Covering __init__ function"""
super().__init__(*args, **kwargs)
def calc_cost_group(self) -> int:
"""Calc cost group for Coverings
"""
if self.predefined_type == "CEILING":
return 354
elif self.predefined_type == "ROOFING":
return 364
elif self.predefined_type == "FLOORING":
return 353
elif self.predefined_type == "CLADDING" and self.is_external:
return 335
elif self.predefined_type == "ROOFING" and not self.is_external:
return 336
elif self.predefined_type == "MOLDING" and self.is_external:
return 339
elif self.predefined_type == "MOLDING" and not self.is_external:
return 349
elif self.predefined_type == "SKIRTINGBOARD" and not self.is_external:
return 349
elif self.is_external:
return 330
elif not self.is_external:
return 340
else:
return 300
width = attribute.Attribute(
default_ps=("Qto_CoveringBaseQuantities", "Width"),
unit=ureg.m
)
gross_area = attribute.Attribute(
default_ps=("Qto_CoveringBaseQuantities", "GrossArea"),
unit=ureg.meter ** 2
)
net_area = attribute.Attribute(
default_ps=("Qto_CoveringBaseQuantities", "NetArea"),
unit=ureg.meter ** 2
)


class Insulation(Covering):
ifc_types = {'IfcCovering': ['INSULATION']}
pattern_ifc_type = [
re.compile('Dämmung', flags=re.IGNORECASE),
re.compile('Isolierung', flags=re.IGNORECASE),
re.compile('Isolation', flags=re.IGNORECASE),
re.compile('Insulation', flags=re.IGNORECASE),
]
def __init__(self, *args, **kwargs):
"""Insulation __init__ function"""
super().__init__(*args, **kwargs)
def calc_cost_group(self) -> int:
"""Calc cost group for Insulations
External: 330
Internal: 340
"""
if self.is_external:
return 330
elif not self.is_external:
return 340
else:
return 300

class SpaceBoundaryRepresentation(BPSProduct):
"""describes the geometric representation of space boundaries which are
created by the webtool to allow the """
ifc_types = {
"IFCBUILDINGELEMENTPROXY":
['USERDEFINED']
}
pattern_ifc_type = [
re.compile('ProxyBound', flags=re.IGNORECASE)
]


# collect all domain classes
items: Set[BPSProduct] = set()
for name, cls in inspect.getmembers(
Expand Down
2 changes: 2 additions & 0 deletions bim2sim/elements/mapping/ifc2python.py
Original file line number Diff line number Diff line change
Expand Up @@ -406,6 +406,8 @@ def get_guid(ifcElement):
def get_predefined_type(ifcElement) -> Union[str, None]:
"""Returns the predefined type of the IFC element"""
try:
if not hasattr(ifcElement, 'PredefinedType'):
return None
predefined_type = getattr(ifcElement, 'PredefinedType')
# todo cache "USERDEFINED" and check where information is stored
if predefined_type == "NOTDEFINED":
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -693,6 +693,14 @@ def rename_zone_usage(self, zone_dict, zone_dict_ifc_names, rename_keys,
storey_name = \
zone_dict_ifc_names[key]['StoreyName'].split(
'_')[0]
elif ' / ' in zone_dict_ifc_names[key]['StoreyName']:
storey_name = \
zone_dict_ifc_names[key]['StoreyName'].split(
' / ')[0]
elif '/' in zone_dict_ifc_names[key]['StoreyName']:
storey_name = \
zone_dict_ifc_names[key]['StoreyName'].split(
'/')[0]
else:
storey_name = zone_dict_ifc_names[key]['StoreyName']
if storey_name.upper() == 'Erdgeschoss'.upper():
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -189,6 +189,7 @@ def test_regression_AC20_FZK_Haus(self):
project.sim_settings.add_shadings = True
project.sim_settings.split_shadings = True
project.sim_settings.run_full_simulation = True
project.sim_settings.serialize_elements = False
# project.sim_settings.ep_install_path = 'C://EnergyPlusV9-4-0/'
answers = ()
handler = DebugDecisionHandler(answers)
Expand Down
66 changes: 55 additions & 11 deletions bim2sim/plugins/PluginEnergyPlus/bim2sim_energyplus/sim_settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -92,9 +92,9 @@ class EnergyPlusSimSettings(BuildingSimSettings):
'SummerTypical days are available in weather '
'file.'},
description='Choose whether to perform the system sizing for '
'DesignDays, extreme weather periods, typical weather '
'periods. value=Typical (i.e., apply system sizing for '
'typical summer/winter days). '
'DesignDays, extreme weather periods, typical weather '
'periods. value=Typical (i.e., apply system sizing for '
'typical summer/winter days). '
)
weather_file_for_sizing = PathSetting(
value=None,
Expand All @@ -115,7 +115,8 @@ class EnergyPlusSimSettings(BuildingSimSettings):
choices={
'FullExterior': 'Full exterior solar distribution',
'FullInteriorAndExterior': 'Full interior and exterior solar '
'distribution'
'distribution',
'FullInteriorAndExteriorWithReflections': 'Add reflections'
},
description='Choose solar distribution.',
for_frontend=True
Expand Down Expand Up @@ -161,7 +162,7 @@ class EnergyPlusSimSettings(BuildingSimSettings):
)
output_keys = ChoiceSetting(
value=['output_outdoor_conditions', 'output_zone_temperature',
'output_zone', 'output_infiltration', 'output_meters'],
'output_zone', 'output_infiltration', 'output_meters'],
choices={
'output_outdoor_conditions': 'Add outputs for outdoor conditions.',
'output_internal_gains': 'Add output for internal gains.',
Expand Down Expand Up @@ -196,11 +197,11 @@ class EnergyPlusSimSettings(BuildingSimSettings):
)
hvac_off_at_night = BooleanSetting(
value=False, description='Disable all HVAC systems at night from '
'10pm to 6am.'
'10pm to 6am.'
)
control_operative_temperature = BooleanSetting(
value=False, description='Use operative temperature instead of air '
'temperature for zonal temperature control.'
'temperature for zonal temperature control.'
)
ventilation_demand_control = ChoiceSetting(
value=None,
Expand All @@ -217,8 +218,8 @@ class EnergyPlusSimSettings(BuildingSimSettings):
'applied based on the differential '
'dry bulb temperature.',
'DifferentialEnthalpy': 'The outdoor air economizer is '
'applied based on the differential '
'enthalpy.'},
'applied based on the differential '
'enthalpy.'},
description='Choose which type of outdoor air economizer should be '
'applied to reduce cooling loads by an increased outdoor '
'air flow if cooling loads can be reduced. Default is '
Expand Down Expand Up @@ -256,8 +257,8 @@ class EnergyPlusSimSettings(BuildingSimSettings):
)
residential = BooleanSetting(
value=False, description='Choose True to use residential settings '
'for natural ventilation (DIN4108-2), '
'False for non-residential houses.'
'for natural ventilation (DIN4108-2), '
'False for non-residential houses.'
)
natural_ventilation_approach = ChoiceSetting(
value="Simple",
Expand All @@ -267,3 +268,46 @@ class EnergyPlusSimSettings(BuildingSimSettings):
"DIN4108": "use DIN4108-2 for natural ventilation."
}
)
add_occupant_co2 = BooleanSetting(
value=False,
description='Choose if the CO2 generation by occupants should be '
'considered.',
)
shading_calc_method = ChoiceSetting(
value="PolygonClipping",
choices={
"PixelCounting": "Use PixelCounting approach. If the "
"computational resources are not "
"sufficient for PixelCounting, EnergyPlus "
"uses PolygonClipping instead.",
"PolygonClipping": "Use PolygonClipping. "}
)
window_shading_solar_transmittance = NumberSetting(
value=0.3, min_value=0, max_value=0.999,
description="Solar transmittance of window shading material")
window_shading_solar_reflectance = NumberSetting(
value=0.5, min_value=0, max_value=0.999,
description="Solar reflectance of window shading material")
window_shading_visible_transmittance = NumberSetting(
value=0.3, min_value=0, max_value=0.999,
description="Visible transmittance of window shading material")
window_shading_visible_reflectance = NumberSetting(
value=0.5, min_value=0, max_value=0.999,
description="Visible reflectance of window shading material")
window_shading_infr_hemisph_emissivity = NumberSetting(
value=0.9, min_value=0.00001, max_value=0.999,
description="Infrared Hemispherical Emissivity of window shading "
"material")
window_shading_infr_transmittance = NumberSetting(
value=0.05, min_value=0, max_value=0.999,
description="Infrared transmittance of window shading material")
window_shading_thickness = NumberSetting(
value=0.003, min_value=0.00001, max_value=1000,
description="Thickness of window shading material")
window_shading_conductivity = NumberSetting(
value=0.1, min_value=0.00001, max_value=10000,
description="Conductivity of window shading material")
window_shading_airflow_permeability = NumberSetting(
value=0.0, min_value=0.0, max_value=0.8,
description="Airflow Permeability of window shading material")

Loading