Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
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
20 changes: 17 additions & 3 deletions benchmarking/benchmark.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,23 @@ def benchmark() -> None:
Rotation=45,
WWR=0.3,
NFloors=2,
FacadeRValue=3.0,
RoofRValue=3.0,
SlabRValue=3.0,
FacadeStructuralSystem="cmu",
FacadeCavityInsulationRValue=1.2,
FacadeExteriorInsulationRValue=1.0,
FacadeInteriorInsulationRValue=0.0,
FacadeInteriorFinish="drywall",
FacadeExteriorFinish="brick_veneer",
RoofStructuralSystem="poured_concrete",
RoofCavityInsulationRValue=0.0,
RoofExteriorInsulationRValue=2.5,
RoofInteriorInsulationRValue=0.2,
RoofInteriorFinish="gypsum_board",
RoofExteriorFinish="epdm_membrane",
SlabStructuralSystem="slab_on_grade",
SlabInsulationRValue=2.2,
SlabInsulationPlacement="auto",
SlabInteriorFinish="tile",
SlabExteriorFinish="none",
WindowUValue=3.0,
WindowSHGF=0.7,
WindowTVis=0.5,
Expand Down
27 changes: 24 additions & 3 deletions epinterface/sbem/components/envelope.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
"""Envelope components for the SBEM library."""

import warnings
from typing import Literal

from archetypal.idfclass import IDF
Expand Down Expand Up @@ -177,14 +178,34 @@ def name(self):
# return f"{self.LayerOrder}_{self.ConstructionMaterial.Name}_{self.Thickness}m"
return f"{self.ConstructionMaterial.Name}_{self.Thickness}m"

_MIN_EP_THICKNESS = 0.003

@property
def ep_material(self):
"""Return the EP material for the layer."""
"""Return the EP material for the layer.

EnergyPlus Material objects require a minimum thickness of 3mm.
For thinner layers, thickness is clamped to 3mm and density is
adjusted to preserve thermal mass per unit area.
"""
thickness = self.Thickness
density = self.ConstructionMaterial.Density

if thickness < self._MIN_EP_THICKNESS and thickness > 0:
warnings.warn(
f"Layer '{self.ConstructionMaterial.Name}' thickness "
f"{thickness * 1000:.1f}mm is below the EnergyPlus 3mm minimum. "
f"Clamping to 3mm with adjusted density to preserve thermal mass.",
stacklevel=2,
)
density = density * thickness / self._MIN_EP_THICKNESS
thickness = self._MIN_EP_THICKNESS

return Material(
Name=self.name,
Thickness=self.Thickness,
Thickness=thickness,
Conductivity=self.ConstructionMaterial.Conductivity,
Density=self.ConstructionMaterial.Density,
Density=density,
Specific_Heat=self.ConstructionMaterial.SpecificHeat,
Thermal_Absorptance=self.ConstructionMaterial.ThermalAbsorptance,
Solar_Absorptance=self.ConstructionMaterial.SolarAbsorptance,
Expand Down
1 change: 1 addition & 0 deletions epinterface/sbem/components/materials.py
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@ class CommonMaterialPropertiesMixin(BaseModel):
"Finishes",
"Siding",
"Sealing",
"Bio",
]

MaterialRoughness = Literal[
Expand Down
38 changes: 38 additions & 0 deletions epinterface/sbem/flat_constructions/REFERENCE_NOTES.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
# Semi-flat construction reference notes

This folder intentionally uses simplified/consolidated thermal methods for ML-friendly
inputs. The assumptions below document where steel/wood framing adjustments come from.

## Framed cavity modeling

For framed wall/roof systems we use an equivalent parallel-path layer:

- `U_eq = f_frame / R_frame + (1 - f_frame) / R_fill`
- `R_eq = 1 / U_eq`

For wood framing, `R_frame` is computed directly from framing material conductivity and
cavity depth.

For steel framing, `R_frame` is treated as an **effective framing-path R-value** to
account for 2D/3D heat-flow effects and thermal-bridge behavior not captured by pure
1D steel conductivity. The calibrated values are chosen to reproduce expected effective
batt performance ranges from code-table methods.

## Primary references

1. ASHRAE Standard 90.1 (Appendix A, envelope assembly/U-factor methodology for metal framing)

- https://www.ashrae.org/technical-resources/bookstore/standard-90-1

2. COMcheck envelope U-factor workflow and datasets (steel-framed assemblies)

- https://www.energycodes.gov/comcheck

3. PNNL Building America Solution Center (thermal bridging in metal-stud walls)
- https://basc.pnnl.gov/resource-guides/continuous-insulation-metal-stud-wall

## Scope note

These assumptions are intended for rapid parametric modeling and fixed-length feature
vectors, not project-specific code compliance documentation. For high-fidelity design,
replace defaults with project-calibrated values (e.g., THERM/ISO 10211 workflows).
73 changes: 73 additions & 0 deletions epinterface/sbem/flat_constructions/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
"""Semi-flat construction translators for SBEM flat models."""

from epinterface.sbem.flat_constructions.assemblies import build_envelope_assemblies
from epinterface.sbem.flat_constructions.audit import (
AuditIssue,
run_physical_sanity_audit,
)
from epinterface.sbem.flat_constructions.layers import (
CavityInsulationMaterial,
ContinuousInsulationMaterial,
ExteriorCavityType,
MaterialRef,
equivalent_framed_cavity_material,
layer_from_nominal_r,
resolve_material,
)
from epinterface.sbem.flat_constructions.materials import (
MATERIAL_NAME_VALUES,
MaterialName,
)
from epinterface.sbem.flat_constructions.roofs import (
RoofExteriorFinish,
RoofInteriorFinish,
RoofStructuralSystem,
SemiFlatRoofConstruction,
build_roof_assembly,
)
from epinterface.sbem.flat_constructions.slabs import (
SemiFlatSlabConstruction,
SlabExteriorFinish,
SlabInsulationPlacement,
SlabInteriorFinish,
SlabStructuralSystem,
build_slab_assembly,
)
from epinterface.sbem.flat_constructions.walls import (
SemiFlatWallConstruction,
WallExteriorFinish,
WallInteriorFinish,
WallStructuralSystem,
build_facade_assembly,
)

__all__ = [
"MATERIAL_NAME_VALUES",
"AuditIssue",
"CavityInsulationMaterial",
"ContinuousInsulationMaterial",
"ExteriorCavityType",
"MaterialName",
"MaterialRef",
"RoofExteriorFinish",
"RoofInteriorFinish",
"RoofStructuralSystem",
"SemiFlatRoofConstruction",
"SemiFlatSlabConstruction",
"SemiFlatWallConstruction",
"SlabExteriorFinish",
"SlabInsulationPlacement",
"SlabInteriorFinish",
"SlabStructuralSystem",
"WallExteriorFinish",
"WallInteriorFinish",
"WallStructuralSystem",
"build_envelope_assemblies",
"build_facade_assembly",
"build_roof_assembly",
"build_slab_assembly",
"equivalent_framed_cavity_material",
"layer_from_nominal_r",
"resolve_material",
"run_physical_sanity_audit",
]
118 changes: 118 additions & 0 deletions epinterface/sbem/flat_constructions/assemblies.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
"""Envelope assembly builders for semi-flat construction definitions."""

from epinterface.sbem.components.envelope import (
ConstructionAssemblyComponent,
ConstructionLayerComponent,
EnvelopeAssemblyComponent,
)
from epinterface.sbem.flat_constructions.materials import (
CEMENT_MORTAR,
Copy link
Collaborator

Choose a reason for hiding this comment

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

I wonder if the materials and their quantities can still be part of the configurations an inputs as well, unless we are collectively exhaustive here

Copy link
Owner Author

Choose a reason for hiding this comment

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

Part of the goal is for the abstraction to be sufficient that users are not defining materials, only assemblies of materials. And yes, there should be sufficient options in the library to express what is needed. I think that there are probably sufficient options in there now, but we can add more on an as needed basis.

CONCRETE_RC_DENSE,
GYPSUM_BOARD,
GYPSUM_PLASTER,
SOFTWOOD_GENERAL,
URETHANE_CARPET,
)
from epinterface.sbem.flat_constructions.roofs import (
SemiFlatRoofConstruction,
build_roof_assembly,
)
from epinterface.sbem.flat_constructions.slabs import (
SemiFlatSlabConstruction,
build_slab_assembly,
)
from epinterface.sbem.flat_constructions.walls import (
SemiFlatWallConstruction,
build_facade_assembly,
)


def build_partition_assembly(
*, name: str = "Partition"
Copy link
Collaborator

Choose a reason for hiding this comment

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

I think this needs to be more flexible/handle the boundaries of E+ constructions / what is possible to model in E+. I can test the boundaries a bit!

Copy link
Owner Author

Choose a reason for hiding this comment

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

Not sure I follow here. currently the assumption is that (a) partition construction does not really matter for the most part and (b) it's fine to use a uniform partition for all buildings.

) -> ConstructionAssemblyComponent:
"""Build the default interior partition assembly."""
return ConstructionAssemblyComponent(
Name=name,
Type="Partition",
Layers=[
ConstructionLayerComponent(
ConstructionMaterial=GYPSUM_PLASTER,
Thickness=0.02,
LayerOrder=0,
),
ConstructionLayerComponent(
ConstructionMaterial=SOFTWOOD_GENERAL,
Thickness=0.02,
LayerOrder=1,
),
ConstructionLayerComponent(
ConstructionMaterial=GYPSUM_PLASTER,
Thickness=0.02,
LayerOrder=2,
),
],
)


def build_floor_ceiling_assembly(
*,
name: str = "FloorCeiling",
) -> ConstructionAssemblyComponent:
"""Build the default interstitial floor/ceiling assembly."""
return ConstructionAssemblyComponent(
Name=name,
Type="FloorCeiling",
Layers=[
ConstructionLayerComponent(
ConstructionMaterial=URETHANE_CARPET,
Thickness=0.02,
LayerOrder=0,
),
ConstructionLayerComponent(
ConstructionMaterial=CEMENT_MORTAR,
Thickness=0.02,
LayerOrder=1,
),
ConstructionLayerComponent(
ConstructionMaterial=CONCRETE_RC_DENSE,
Thickness=0.15,
LayerOrder=2,
),
ConstructionLayerComponent(
ConstructionMaterial=GYPSUM_BOARD,
Thickness=0.02,
LayerOrder=3,
),
],
)


def build_envelope_assemblies(
*,
facade_wall: SemiFlatWallConstruction,
roof: SemiFlatRoofConstruction,
slab: SemiFlatSlabConstruction,
) -> EnvelopeAssemblyComponent:
"""Build envelope assemblies from the flat model construction semantics."""
facade = build_facade_assembly(facade_wall, name="Facade")
roof_assembly = build_roof_assembly(roof, name="Roof")
partition = build_partition_assembly(name="Partition")
floor_ceiling = build_floor_ceiling_assembly(name="FloorCeiling")
ground_slab = build_slab_assembly(
slab,
name="GroundSlabAssembly",
)

return EnvelopeAssemblyComponent(
Name="EnvelopeAssemblies",
FacadeAssembly=facade,
FlatRoofAssembly=roof_assembly,
AtticRoofAssembly=roof_assembly,
PartitionAssembly=partition,
FloorCeilingAssembly=floor_ceiling,
AtticFloorAssembly=floor_ceiling,
BasementCeilingAssembly=floor_ceiling,
GroundSlabAssembly=ground_slab,
GroundWallAssembly=ground_slab,
ExternalFloorAssembly=ground_slab,
)
Loading
Loading