From 3469d9541033beb637f3abd1cb45e7424b7bc41b Mon Sep 17 00:00:00 2001 From: Sam Wolk <36545842+szvsw@users.noreply.github.com> Date: Wed, 4 Mar 2026 13:05:55 -0500 Subject: [PATCH] use different pattern for wall constructions --- benchmarking/benchmark.py | 25 +- epinterface/sbem/components/materials.py | 1 + .../sbem/flat_constructions/__init__.py | 1 + .../sbem/flat_constructions/assemblies.py | 105 ++ epinterface/sbem/flat_constructions/base.py | 144 ++ .../sbem/flat_constructions/materials.py | 540 +++++++ epinterface/sbem/flat_constructions/roofs.py | 49 + epinterface/sbem/flat_constructions/slabs.py | 178 +++ epinterface/sbem/flat_constructions/walls.py | 731 +++++++++ epinterface/sbem/flat_model.py | 1345 +++-------------- 10 files changed, 1939 insertions(+), 1180 deletions(-) create mode 100644 epinterface/sbem/flat_constructions/__init__.py create mode 100644 epinterface/sbem/flat_constructions/assemblies.py create mode 100644 epinterface/sbem/flat_constructions/base.py create mode 100644 epinterface/sbem/flat_constructions/materials.py create mode 100644 epinterface/sbem/flat_constructions/roofs.py create mode 100644 epinterface/sbem/flat_constructions/slabs.py create mode 100644 epinterface/sbem/flat_constructions/walls.py diff --git a/benchmarking/benchmark.py b/benchmarking/benchmark.py index 53ac38c..43a32dc 100644 --- a/benchmarking/benchmark.py +++ b/benchmarking/benchmark.py @@ -27,9 +27,18 @@ def benchmark() -> None: Rotation=45, WWR=0.3, NFloors=2, - FacadeRValue=3.0, + FacadeFramingSystem="2x4 16OCC Woodframe", + FacadeCavityInsulationRValue=1.2, + FacadeExteriorInsulationRValue=1.0, + FacadeInteriorInsulationRValue=0.0, + FacadeInteriorFinish="drywall", + FacadeExteriorFinish="brick_veneer", RoofRValue=3.0, - SlabRValue=3.0, + GroundSlabSystem="BasicConcrete", + GroundSlabInsulationRValue=1.5, + GroundSlabInsulationMaterial="XPSBoard", + GroundSlabInteriorFinish="wood_floor", + GroundSlabStructuralThickness=0.15, WindowUValue=3.0, WindowSHGF=0.7, WindowTVis=0.5, @@ -65,18 +74,6 @@ def benchmark() -> None: OccupancyPMInterp=0.5, OccupancyWeekendPeakInterp=0.15, OccupancySummerPeakInterp=0.85, - # HSPRegularWeekdayWorkhours=21, - # HSPRegularWeekdayNight=21, - # HSPSummerWeekdayWorkhours=21, - # HSPSummerWeekdayNight=21, - # HSPWeekendWorkhours=21, - # HSPWeekendNight=21, - # CSPRegularWeekdayWorkhours=23, - # CSPRegularWeekdayNight=23, - # CSPSummerWeekdayWorkhours=23, - # CSPSummerWeekdayNight=23, - # CSPWeekendWorkhours=23, - # CSPWeekendNight=23, HeatingSetpointBase=21, SetpointDeadband=2, HeatingSetpointSetback=2, diff --git a/epinterface/sbem/components/materials.py b/epinterface/sbem/components/materials.py index 1e65ea7..4b2880a 100644 --- a/epinterface/sbem/components/materials.py +++ b/epinterface/sbem/components/materials.py @@ -78,6 +78,7 @@ class CommonMaterialPropertiesMixin(BaseModel): "Finishes", "Siding", "Sealing", + "Bio", ] MaterialRoughness = Literal[ diff --git a/epinterface/sbem/flat_constructions/__init__.py b/epinterface/sbem/flat_constructions/__init__.py new file mode 100644 index 0000000..c75fd4e --- /dev/null +++ b/epinterface/sbem/flat_constructions/__init__.py @@ -0,0 +1 @@ +"""Flat construction objects for SBEM assemblies.""" diff --git a/epinterface/sbem/flat_constructions/assemblies.py b/epinterface/sbem/flat_constructions/assemblies.py new file mode 100644 index 0000000..31767ae --- /dev/null +++ b/epinterface/sbem/flat_constructions/assemblies.py @@ -0,0 +1,105 @@ +"""Envelope assembly builders for semi-flat construction definitions.""" + +from epinterface.sbem.components.envelope import ( + ConstructionAssemblyComponent, + ConstructionLayerComponent, +) +from epinterface.sbem.flat_constructions.materials import ( + CEMENT_MORTAR, + CONCRETE_RC_DENSE, + GYPSUM_BOARD, + GYPSUM_PLASTER, + SOFTWOOD_GENERAL, + URETHANE_CARPET, +) + + +def build_partition_assembly( + *, name: str = "Partition" +) -> 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, +# ) diff --git a/epinterface/sbem/flat_constructions/base.py b/epinterface/sbem/flat_constructions/base.py new file mode 100644 index 0000000..ff4fd87 --- /dev/null +++ b/epinterface/sbem/flat_constructions/base.py @@ -0,0 +1,144 @@ +"""An AB base class for flat construction objects which defines the interface for all flat construction objects.""" + +from typing import Literal + +from pydantic.dataclasses import dataclass + +from epinterface.sbem.components.materials import ConstructionMaterialComponent +from epinterface.sbem.flat_constructions.materials import ( + MATERIALS_BY_NAME, + MaterialName, +) + +SheathingMaterialName = Literal[ + "Plywood", + "OSB", + "ParticleBoard", + "Fiberboard", + "ConcreteMC_Light", + # "MetalSheathing", + # "Masonry", +] +CavityInsulationMaterialName = Literal[ + "FiberglassBatt", + "MineralWoolBatt", + "CelluloseBatt", +] +ContinuousInsulationMaterialName = Literal[ + "XPSBoard", + "PolyisoBoard", + "EPSBoard", + "MineralWoolBoard", +] +FramingMaterialName = Literal[ + "SoftwoodGeneral", + "ConcreteMC_Light", + "ConcreteRC_Dense", + # "LightGaugeSteel", + # "StructuralSteel", + # "CMU", +] + +MonolithicInfillMaterialName = Literal[ + "ConcreteMC_Light", + "ConcreteRC_Dense", + "ClayBrick", +] + +MonolithicFramingMaterialName = Literal[ + "ConcreteMC_Light", + "ConcreteRC_Dense", + "StructuralSteel", +] + +BasicGroundSlabMaterialName = Literal[ + "ConcreteMC_Light", + "ConcreteRC_Dense", +] + + +def get_sheathing_material( + sheathing_material_name: SheathingMaterialName, +) -> ConstructionMaterialComponent: + """Get the sheathing material component from the material name.""" + return MATERIALS_BY_NAME[sheathing_material_name] + + +@dataclass(frozen=True) +class MatWithThickness: + """A material with a thickness.""" + + material: ConstructionMaterialComponent + thickness_m: float + + +def get_cavity_insulation_material( + cavity_insulation_material_name: CavityInsulationMaterialName, + nominal_r_value: float, +) -> MatWithThickness: + """Get the cavity insulation material component and thickness from the material name and nominal R-value. + + Args: + cavity_insulation_material_name (CavityInsulationMaterialName): The name of the cavity insulation material. + nominal_r_value (float): The nominal R-value of the cavity insulation material. + + Returns: + material_with_thickness (MatWithThickness): The cavity insulation material component and thickness. + """ + mat = MATERIALS_BY_NAME[cavity_insulation_material_name] + thickness_m = nominal_r_value * mat.Conductivity + return MatWithThickness(material=mat, thickness_m=thickness_m) + + +def get_continuous_insulation_material( + continuous_insulation_material_name: ContinuousInsulationMaterialName, + nominal_r_value: float, +) -> MatWithThickness: + """Get the continuous insulation material component and thickness from the material name and nominal R-value. + + Args: + continuous_insulation_material_name (ContinuousInsulationMaterialName): The name of the continuous insulation material. + nominal_r_value (float): The nominal R-value of the continuous insulation material. + + Returns: + material_with_thickness (MatWithThickness): The continuous insulation material component and thickness. + """ + mat = MATERIALS_BY_NAME[continuous_insulation_material_name] + thickness_m = nominal_r_value * mat.Conductivity + return MatWithThickness(material=mat, thickness_m=thickness_m) + + +def get_framing_material( + framing_material_name: FramingMaterialName, +) -> ConstructionMaterialComponent: + """Get the framing material component from the material name.""" + return MATERIALS_BY_NAME[framing_material_name] + + +def get_monolithic_framing_material( + monolithic_framing_material_name: MonolithicFramingMaterialName, +) -> ConstructionMaterialComponent: + """Get the monolithic framing material component from the material name.""" + return MATERIALS_BY_NAME[monolithic_framing_material_name] + + +def get_monolithic_infill_material( + monolithic_infill_material_name: MonolithicInfillMaterialName, +) -> ConstructionMaterialComponent: + """Get the monolithic infill material component from the material name.""" + return MATERIALS_BY_NAME[monolithic_infill_material_name] + + +def get_basic_ground_slab_material( + basic_ground_slab_material_name: BasicGroundSlabMaterialName, +) -> ConstructionMaterialComponent: + """Get the basic ground slab material component from the material name.""" + return MATERIALS_BY_NAME[basic_ground_slab_material_name] + + +@dataclass(frozen=True) +class FinishTemplate: + """Default finish material and thickness assumptions.""" + + material_name: MaterialName + thickness_m: float diff --git a/epinterface/sbem/flat_constructions/materials.py b/epinterface/sbem/flat_constructions/materials.py new file mode 100644 index 0000000..b876eb9 --- /dev/null +++ b/epinterface/sbem/flat_constructions/materials.py @@ -0,0 +1,540 @@ +"""Shared opaque materials used by semi-flat construction builders.""" + +from typing import Literal, cast, get_args + +from epinterface.sbem.components.materials import ConstructionMaterialComponent + +MaterialName = Literal[ + "Plywood", + "OSB", + "ParticleBoard", + "Fiberboard", + "XPSBoard", + "PolyisoBoard", + "EPSBoard", + "MineralWoolBoard", + "ConcreteMC_Light", + "ConcreteRC_Dense", + "GypsumBoard", + "GypsumPlaster", + "SoftwoodGeneral", + "ClayBrick", + # "ConcreteBlockH", + "FiberglassBatt", + "CementMortar", + "CeramicTile", + "UrethaneCarpet", + "SteelPanel", + "RammedEarth", + "SIPCore", + "FiberCementBoard", + "RoofMembrane", + "CoolRoofMembrane", + "AcousticTile", + "VinylSiding", + "AsphaltShingle", + "NaturalStone", + "AACBlock", + "SandcreteBlock", + "HollowClayBlock", + "StabilizedSoilBlock", + "WattleDaub", + "ThatchReed", + "AdobeBlock", + "CompressedEarthBlock", + "CobEarth", + "BambooComposite", + "CelluloseBatt", + "MineralWoolBatt", + "StructuralSteel", +] + +MATERIAL_NAME_VALUES: tuple[MaterialName, ...] = get_args(MaterialName) + +DEFAULT_THERMAL_ABSORPTANCE = 0.9 +DEFAULT_SOLAR_ABSORPTANCE = 0.6 +DEFAULT_VISIBLE_ABSORPTANCE = 0.6 + + +def _material( + *, + name: str, + conductivity: float, + density: float, + specific_heat: float, + mat_type: str, + thermal_absorptance: float | None = None, + solar_absorptance: float | None = None, + visible_absorptance: float | None = None, + roughness: str = "MediumRough", +) -> ConstructionMaterialComponent: + """Create a construction material component with optional optical overrides.""" + return ConstructionMaterialComponent( + Name=name, + Conductivity=conductivity, + Density=density, + SpecificHeat=specific_heat, + ThermalAbsorptance=( + DEFAULT_THERMAL_ABSORPTANCE + if thermal_absorptance is None + else thermal_absorptance + ), + SolarAbsorptance=( + DEFAULT_SOLAR_ABSORPTANCE + if solar_absorptance is None + else solar_absorptance + ), + VisibleAbsorptance=( + DEFAULT_VISIBLE_ABSORPTANCE + if visible_absorptance is None + else visible_absorptance + ), + TemperatureCoefficientThermalConductivity=0.0, + Roughness=roughness, # pyright: ignore[reportArgumentType] + Type=mat_type, # pyright: ignore[reportArgumentType] + ) + + +STRUCTURAL_STEEL = _material( + name="StructuralSteel", + conductivity=45.0, + density=7850, + specific_heat=500, + mat_type="Metal", +) + +XPS_BOARD = _material( + name="XPSBoard", + conductivity=0.037, + density=40, + specific_heat=1200, + mat_type="Insulation", +) + +POLYISO_BOARD = _material( + name="PolyisoBoard", + conductivity=0.024, + density=32, + specific_heat=1400, + mat_type="Insulation", +) + +CONCRETE_MC_LIGHT = _material( + name="ConcreteMC_Light", + conductivity=1.20, + density=1700, + specific_heat=900, + mat_type="Concrete", +) + +CONCRETE_RC_DENSE = _material( + name="ConcreteRC_Dense", + conductivity=1.95, + density=2300, + specific_heat=900, + mat_type="Concrete", +) + +GYPSUM_BOARD = _material( + name="GypsumBoard", + conductivity=0.16, + density=950, + specific_heat=840, + mat_type="Finishes", +) + +GYPSUM_PLASTER = _material( + name="GypsumPlaster", + conductivity=0.42, + density=900, + specific_heat=840, + mat_type="Finishes", +) + +SOFTWOOD_GENERAL = _material( + name="SoftwoodGeneral", + conductivity=0.12, + density=500, + specific_heat=1630, + mat_type="Timber", +) + +CLAY_BRICK = _material( + name="ClayBrick", + conductivity=0.69, + density=1700, + specific_heat=840, + mat_type="Masonry", + solar_absorptance=0.70, + visible_absorptance=0.70, +) + +# CONCRETE_BLOCK_H = _material( +# name="ConcreteBlockH", +# conductivity=0.51, +# density=1100, +# specific_heat=840, +# mat_type="Concrete", +# solar_absorptance=0.65, +# visible_absorptance=0.65, +# ) + +FIBERGLASS_BATTS = _material( + name="FiberglassBatt", + conductivity=0.043, + density=12, + specific_heat=840, + mat_type="Insulation", +) + +CEMENT_MORTAR = _material( + name="CementMortar", + conductivity=0.72, + density=1850, + specific_heat=840, + mat_type="Other", + solar_absorptance=0.65, + visible_absorptance=0.65, +) + +CERAMIC_TILE = _material( + name="CeramicTile", + conductivity=1.05, + density=2000, + specific_heat=840, + mat_type="Finishes", + roughness="MediumSmooth", +) + +URETHANE_CARPET = _material( + name="UrethaneCarpet", + conductivity=0.06, + density=160, + specific_heat=840, + mat_type="Finishes", +) + +STEEL_PANEL = _material( + name="SteelPanel", + conductivity=45.0, + density=7850, + specific_heat=500, + mat_type="Metal", + solar_absorptance=0.55, + visible_absorptance=0.55, + roughness="Smooth", +) + +RAMMED_EARTH = _material( + name="RammedEarth", + conductivity=1.10, + density=1900, + specific_heat=1000, + mat_type="Masonry", + solar_absorptance=0.70, + visible_absorptance=0.70, +) + +SIP_CORE = _material( + name="SIPCore", + conductivity=0.026, + density=35, + specific_heat=1400, + mat_type="Insulation", +) + +FIBER_CEMENT_BOARD = _material( + name="FiberCementBoard", + conductivity=0.35, + density=1350, + specific_heat=840, + mat_type="Siding", + solar_absorptance=0.65, + visible_absorptance=0.65, +) + +ROOF_MEMBRANE = _material( + name="RoofMembrane", + conductivity=0.17, + density=1200, + specific_heat=900, + mat_type="Sealing", + solar_absorptance=0.88, + visible_absorptance=0.88, + roughness="Smooth", +) + +COOL_ROOF_MEMBRANE = _material( + name="CoolRoofMembrane", + conductivity=0.17, + density=1200, + specific_heat=900, + mat_type="Sealing", + solar_absorptance=0.30, + visible_absorptance=0.30, + roughness="Smooth", +) + +ACOUSTIC_TILE = _material( + name="AcousticTile", + conductivity=0.065, + density=280, + specific_heat=840, + mat_type="Boards", +) + +VINYL_SIDING = _material( + name="VinylSiding", + conductivity=0.17, + density=1380, + specific_heat=1000, + mat_type="Siding", + solar_absorptance=0.55, + visible_absorptance=0.55, + roughness="Smooth", +) + +ASPHALT_SHINGLE = _material( + name="AsphaltShingle", + conductivity=0.06, + density=1120, + specific_heat=920, + mat_type="Finishes", + solar_absorptance=0.85, + visible_absorptance=0.85, + roughness="Rough", +) + +NATURAL_STONE = _material( + name="NaturalStone", + conductivity=2.90, + density=2500, + specific_heat=840, + mat_type="Masonry", + solar_absorptance=0.55, + visible_absorptance=0.55, +) + +EPS_BOARD = _material( + name="EPSBoard", + conductivity=0.037, + density=25, + specific_heat=1400, + mat_type="Insulation", +) + +MINERAL_WOOL_BOARD = _material( + name="MineralWoolBoard", + conductivity=0.035, + density=140, + specific_heat=840, + mat_type="Insulation", +) + +AAC_BLOCK = _material( + name="AACBlock", + conductivity=0.11, + density=500, + specific_heat=1000, + mat_type="Masonry", + solar_absorptance=0.65, + visible_absorptance=0.65, +) + +SANDCRETE_BLOCK = _material( + name="SandcreteBlock", + conductivity=0.72, + density=1700, + specific_heat=840, + mat_type="Masonry", + solar_absorptance=0.65, + visible_absorptance=0.65, +) + +HOLLOW_CLAY_BLOCK = _material( + name="HollowClayBlock", + conductivity=0.35, + density=900, + specific_heat=840, + mat_type="Masonry", + solar_absorptance=0.70, + visible_absorptance=0.70, +) + +STABILIZED_SOIL_BLOCK = _material( + name="StabilizedSoilBlock", + conductivity=0.65, + density=1600, + specific_heat=900, + mat_type="Masonry", + solar_absorptance=0.70, + visible_absorptance=0.70, +) + +WATTLE_DAUB = _material( + name="WattleDaub", + conductivity=0.07, + density=800, + specific_heat=1000, + mat_type="Bio", + solar_absorptance=0.70, + visible_absorptance=0.70, +) + +THATCH_REED = _material( + name="ThatchReed", + conductivity=0.09, + density=150, + specific_heat=1000, + mat_type="Bio", + solar_absorptance=0.75, + visible_absorptance=0.75, + roughness="Rough", +) + +ADOBE_BLOCK = _material( + name="AdobeBlock", + conductivity=0.60, + density=1500, + specific_heat=900, + mat_type="Masonry", + solar_absorptance=0.70, + visible_absorptance=0.70, +) + +COMPRESSED_EARTH_BLOCK = _material( + name="CompressedEarthBlock", + conductivity=0.80, + density=1800, + specific_heat=900, + mat_type="Masonry", + solar_absorptance=0.70, + visible_absorptance=0.70, +) + +COB_EARTH = _material( + name="CobEarth", + conductivity=0.80, + density=1550, + specific_heat=1000, + mat_type="Bio", + solar_absorptance=0.70, + visible_absorptance=0.70, +) + +BAMBOO_COMPOSITE = _material( + name="BambooComposite", + conductivity=0.17, + density=700, + specific_heat=1200, + mat_type="Bio", + solar_absorptance=0.65, + visible_absorptance=0.65, +) + +CELLULOSE_BATT = _material( + name="CelluloseBatt", + conductivity=0.040, + density=50, + specific_heat=1380, + mat_type="Insulation", +) + +MINERAL_WOOL_BATT = _material( + name="MineralWoolBatt", + conductivity=0.038, + density=30, + specific_heat=840, + mat_type="Insulation", +) + +FIBERBOARD = _material( + name="Fiberboard", + conductivity=0.17, + density=700, + specific_heat=1200, + mat_type="Siding", +) + +OSB = _material( + name="OSB", + conductivity=0.17, + density=700, + specific_heat=1200, + mat_type="Siding", +) + +PARTICLE_BOARD = _material( + name="ParticleBoard", + conductivity=0.17, + density=700, + specific_heat=1200, + mat_type="Siding", +) + +PLYWOOD = _material( + name="Plywood", + conductivity=0.17, + density=700, + specific_heat=1200, + mat_type="Siding", +) + +_ALL_MATERIALS = ( + XPS_BOARD, + POLYISO_BOARD, + CONCRETE_MC_LIGHT, + CONCRETE_RC_DENSE, + GYPSUM_BOARD, + GYPSUM_PLASTER, + SOFTWOOD_GENERAL, + CLAY_BRICK, + # CONCRETE_BLOCK_H, + FIBERGLASS_BATTS, + CEMENT_MORTAR, + CERAMIC_TILE, + URETHANE_CARPET, + STEEL_PANEL, + RAMMED_EARTH, + SIP_CORE, + FIBER_CEMENT_BOARD, + ROOF_MEMBRANE, + COOL_ROOF_MEMBRANE, + ACOUSTIC_TILE, + VINYL_SIDING, + ASPHALT_SHINGLE, + NATURAL_STONE, + EPS_BOARD, + MINERAL_WOOL_BOARD, + AAC_BLOCK, + SANDCRETE_BLOCK, + HOLLOW_CLAY_BLOCK, + STABILIZED_SOIL_BLOCK, + WATTLE_DAUB, + THATCH_REED, + ADOBE_BLOCK, + COMPRESSED_EARTH_BLOCK, + COB_EARTH, + BAMBOO_COMPOSITE, + CELLULOSE_BATT, + MINERAL_WOOL_BATT, + FIBERBOARD, + OSB, + PARTICLE_BOARD, + PLYWOOD, + STRUCTURAL_STEEL, +) + +MATERIALS_BY_NAME: dict[MaterialName, ConstructionMaterialComponent] = { + cast(MaterialName, mat.Name): mat for mat in _ALL_MATERIALS +} + +_names_in_map = set(MATERIALS_BY_NAME.keys()) +_expected_names = set(MATERIAL_NAME_VALUES) +if _names_in_map != _expected_names: + missing = sorted(_expected_names - _names_in_map) + extra = sorted(_names_in_map - _expected_names) + msg = ( + f"Material name definitions are out of sync. Missing={missing}, Extra={extra}." + ) + raise ValueError(msg) diff --git a/epinterface/sbem/flat_constructions/roofs.py b/epinterface/sbem/flat_constructions/roofs.py new file mode 100644 index 0000000..fc571e6 --- /dev/null +++ b/epinterface/sbem/flat_constructions/roofs.py @@ -0,0 +1,49 @@ +"""Roof construction objects for SBEM assemblies.""" + +from epinterface.sbem.components.envelope import ( + ConstructionAssemblyComponent, + ConstructionLayerComponent, +) +from epinterface.sbem.flat_constructions.materials import ( + ACOUSTIC_TILE, + CONCRETE_MC_LIGHT, + XPS_BOARD, +) + + +# TODO: make the roof more configurable similar to slab and walls +def build_roof_assembly( + r_value: float, +): + """Build a roof assembly with the given R-value.""" + roof = ConstructionAssemblyComponent( + Name="Roof", + Type="FlatRoof", + Layers=[ + ConstructionLayerComponent( + ConstructionMaterial=CONCRETE_MC_LIGHT, + Thickness=0.15, + LayerOrder=0, + ), + ConstructionLayerComponent( + ConstructionMaterial=XPS_BOARD, + Thickness=0.1, + LayerOrder=1, + ), + ConstructionLayerComponent( + ConstructionMaterial=ACOUSTIC_TILE, + Thickness=0.02, + LayerOrder=2, + ), + ], + ) + + roof_r_value_without_xps = roof.r_value - roof.sorted_layers[1].r_value + roof_r_value_delta = r_value - roof_r_value_without_xps + required_xps_thickness = XPS_BOARD.Conductivity * roof_r_value_delta + if required_xps_thickness < 0.003: + msg = f"Required Roof XPS thickness is less than 3mm because the desired total roof R-value is {r_value} m²K/W but the concrete layers already have a total R-value of {roof_r_value_without_xps} m²K/W." + raise ValueError(msg) + + roof.sorted_layers[1].Thickness = required_xps_thickness + return roof diff --git a/epinterface/sbem/flat_constructions/slabs.py b/epinterface/sbem/flat_constructions/slabs.py new file mode 100644 index 0000000..f14735b --- /dev/null +++ b/epinterface/sbem/flat_constructions/slabs.py @@ -0,0 +1,178 @@ +"""Slab construction objects for SBEM assemblies.""" + +import json +from abc import ABC, abstractmethod +from typing import Literal, get_args + +from pydantic import BaseModel + +from epinterface.sbem.components.envelope import ( + ConstructionAssemblyComponent, + ConstructionLayerComponent, +) +from epinterface.sbem.flat_constructions.base import ( + BasicGroundSlabMaterialName, + ContinuousInsulationMaterialName, + FinishTemplate, + get_basic_ground_slab_material, + get_continuous_insulation_material, +) +from epinterface.sbem.flat_constructions.materials import MATERIALS_BY_NAME + +GroundSlabInteriorFinishName = Literal[ + "tile", + "wood_floor", + "carpet", + "polished_concrete", + "cement_screed", + "none", +] + +INTERIOR_FINISHES: dict[GroundSlabInteriorFinishName, FinishTemplate | None] = { + "tile": FinishTemplate( + material_name="CeramicTile", + thickness_m=0.015, + ), + "wood_floor": FinishTemplate( + material_name="SoftwoodGeneral", + thickness_m=0.015, + ), + "carpet": FinishTemplate( + material_name="UrethaneCarpet", + thickness_m=0.012, + ), + "polished_concrete": FinishTemplate( + material_name="CementMortar", + thickness_m=0.015, + ), + "cement_screed": FinishTemplate( + material_name="CementMortar", + thickness_m=0.02, + ), + "none": None, +} + + +class FlatGroundSlabConstruction(ABC, BaseModel): + """A base class for slab construction templates.""" + + insulation_location: Literal["below", "above"] + + @abstractmethod + def to_construction_assembly( + self, + *, + insul_name: ContinuousInsulationMaterialName | Literal["None"], + insul_rval: float, + structural_thickness: float, + interior_finish: GroundSlabInteriorFinishName, + ) -> ConstructionAssemblyComponent: + """Translate the slab construction object into an EnergyPlus construction assembly.""" + + +class BasicGroundSlab(FlatGroundSlabConstruction): + """A basic slab construction object.""" + + slab_material: BasicGroundSlabMaterialName + + def to_construction_assembly( + self, + *, + insul_name: ContinuousInsulationMaterialName | Literal["None"], + insul_rval: float, + structural_thickness: float, + interior_finish: GroundSlabInteriorFinishName, + ) -> ConstructionAssemblyComponent: + """Translate the basic slab construction object into an EnergyPlus construction assembly.""" + layers: list[ConstructionLayerComponent] = [] + layer_order = 0 + + ####### HANDLE INSULATION BELOW SLAB LAYER ####### + if ( + insul_rval > 0 + and insul_name != "None" + and self.insulation_location == "below" + ): + thickened_insul = get_continuous_insulation_material(insul_name, insul_rval) + layers.append( + ConstructionLayerComponent( + ConstructionMaterial=thickened_insul.material, + Thickness=thickened_insul.thickness_m, + LayerOrder=layer_order, + ) + ) + layer_order += 1 + + ####### HANDLE STRUCTURAL MATERIAL LAYER ####### + structural_mat = get_basic_ground_slab_material(self.slab_material) + layers.append( + ConstructionLayerComponent( + ConstructionMaterial=structural_mat, + Thickness=structural_thickness, + LayerOrder=layer_order, + ) + ) + layer_order += 1 + + ####### HANDLE INSULATION ABOVE SLAB LAYER ####### + if ( + insul_rval > 0 + and insul_name != "None" + and self.insulation_location == "above" + ): + thickened_insul = get_continuous_insulation_material(insul_name, insul_rval) + layers.append( + ConstructionLayerComponent( + ConstructionMaterial=thickened_insul.material, + Thickness=thickened_insul.thickness_m, + LayerOrder=layer_order, + ) + ) + layer_order += 1 + + ####### HANDLE INTERIOR FINISH LAYER ####### + if interior_finish_template := INTERIOR_FINISHES[interior_finish]: + layers.append( + ConstructionLayerComponent( + ConstructionMaterial=MATERIALS_BY_NAME[ + interior_finish_template.material_name + ], + Thickness=interior_finish_template.thickness_m, + LayerOrder=layer_order, + ) + ) + layer_order += 1 + return ConstructionAssemblyComponent( + Name=f"BasicGroundSlab_{self.slab_material}_{insul_name}_{ + self.insulation_location + }_{insul_rval}_{structural_thickness}_{interior_finish}_{ + hash(json.dumps([layer.model_dump(mode='json') for layer in layers])) + }", + Layers=layers, + Type="GroundSlab", + ) + + +BasicConcreteGroundSlab = BasicGroundSlab( + slab_material="ConcreteMC_Light", + insulation_location="below", +) + +BasicRCGroundSlab = BasicGroundSlab( + slab_material="ConcreteRC_Dense", + insulation_location="below", +) + +GroundSlabSystemName = Literal[ + "BasicConcrete", + "BasicRC", +] + +GroundSlabSystems: dict[GroundSlabSystemName, FlatGroundSlabConstruction] = { + "BasicConcrete": BasicConcreteGroundSlab, + "BasicRC": BasicRCGroundSlab, +} +for name in get_args(GroundSlabSystemName): + if name not in GroundSlabSystems: + msg = f"Ground slab system {name} not found in GroundSlabSystems." + raise ValueError(msg) diff --git a/epinterface/sbem/flat_constructions/walls.py b/epinterface/sbem/flat_constructions/walls.py new file mode 100644 index 0000000..437e3c4 --- /dev/null +++ b/epinterface/sbem/flat_constructions/walls.py @@ -0,0 +1,731 @@ +"""Wall construction objects for SBEM assemblies.""" + +import json +import warnings +from abc import ABC, abstractmethod +from typing import Literal, get_args + +from pydantic import BaseModel, Field + +from epinterface.sbem.components.envelope import ( + ConstructionAssemblyComponent, + ConstructionLayerComponent, +) +from epinterface.sbem.components.materials import ConstructionMaterialComponent +from epinterface.sbem.flat_constructions.base import ( + CavityInsulationMaterialName, + ContinuousInsulationMaterialName, + FinishTemplate, + FramingMaterialName, + MonolithicFramingMaterialName, + MonolithicInfillMaterialName, + SheathingMaterialName, + get_cavity_insulation_material, + get_continuous_insulation_material, + get_framing_material, + get_monolithic_framing_material, + get_monolithic_infill_material, + get_sheathing_material, +) +from epinterface.sbem.flat_constructions.materials import MATERIALS_BY_NAME + +WallInteriorFinishName = Literal[ + "none", + "drywall", + "plaster", + "cement_plaster", + "wood_panel", +] +WallExteriorFinishName = Literal[ + "brick_veneer", + "stucco", + "fiber_cement", + "metal_panel", + "vinyl_siding", + "wood_siding", + "stone_veneer", + "none", +] + + +INTERIOR_FINISHES: dict[WallInteriorFinishName, FinishTemplate | None] = { + "drywall": FinishTemplate( + material_name="GypsumBoard", + thickness_m=0.0127, + ), + "plaster": FinishTemplate( + material_name="GypsumPlaster", + thickness_m=0.013, + ), + "cement_plaster": FinishTemplate( + material_name="CementMortar", + thickness_m=0.015, + ), + "wood_panel": FinishTemplate( + material_name="SoftwoodGeneral", + thickness_m=0.012, + ), +} + +EXTERIOR_FINISHES: dict[WallExteriorFinishName, FinishTemplate | None] = { + "brick_veneer": FinishTemplate( + material_name="ClayBrick", + thickness_m=0.090, + ), + "stucco": FinishTemplate( + material_name="CementMortar", + thickness_m=0.020, + ), + "fiber_cement": FinishTemplate( + material_name="FiberCementBoard", + thickness_m=0.012, + ), + "metal_panel": FinishTemplate( + material_name="SteelPanel", + thickness_m=0.001, + ), + "vinyl_siding": FinishTemplate( + material_name="VinylSiding", + thickness_m=0.0015, + ), + "wood_siding": FinishTemplate( + material_name="SoftwoodGeneral", + thickness_m=0.018, + ), + "stone_veneer": FinishTemplate( + material_name="NaturalStone", + thickness_m=0.025, + ), +} + + +class FlatWallConstruction(ABC, BaseModel): + """A base class for flat wall construction objects.""" + + @abstractmethod + def to_construction_assembly( + self, + *, + cav_insul_name: CavityInsulationMaterialName | Literal["None"], + ext_insul_name: ContinuousInsulationMaterialName | Literal["None"], + int_insul_name: ContinuousInsulationMaterialName | Literal["None"], + cav_insul_rval: float, + ext_insul_rval: float, + int_insul_rval: float, + int_finish: WallInteriorFinishName, + ext_finish: WallExteriorFinishName, + ) -> ConstructionAssemblyComponent: + """Translate the flat wall construction object into an EnergyPlus construction assembly.""" + + +class FramedWallConstruction(FlatWallConstruction): + """A framed wall construction object.""" + + framing_material: FramingMaterialName + framing_fraction: float = Field( + ..., + ge=0, + le=1, + description="The fraction of the wall that is the framing material.", + ) + cavity_depth_m: float = Field( + ..., ge=0, description="The depth of the cavity in the wall." + ) + cavity_airgap_strategy: Literal[ + "fit-to-insulation-r", + "gap-then-clip", # standard studs + insulation + "gap-gap-then-clip", # two layers of cmu against each other + "gap-gap-gap-then-clip", # three layers of cmu against or each other or double stud wall + ] + framing_path_r_value_override: float | None = Field( + default=None, + description="The R-value of the framing path in the wall if the material + thickness should be ignored..", + ) + sheathing_material: SheathingMaterialName | Literal["None"] + sheathing_material_thickness_m: float = Field( + ..., + ge=0, + description="The thickness of the sheathing material in the wall.", + ) + + def to_construction_assembly( # noqa: C901 + self, + *, + cav_insul_name: CavityInsulationMaterialName | Literal["None"], + ext_insul_name: ContinuousInsulationMaterialName | Literal["None"], + int_insul_name: ContinuousInsulationMaterialName | Literal["None"], + cav_insul_rval: float, + ext_insul_rval: float, + int_insul_rval: float, + int_finish: WallInteriorFinishName, + ext_finish: WallExteriorFinishName, + ) -> ConstructionAssemblyComponent: + """Translate the woodframed wall construction object into an EnergyPlus construction assembly.""" + layers: list[ConstructionLayerComponent] = [] + layer_order = 0 + + ####### HANDLE EXTERIOR FINISH LAYER ####### + if exterior_finish_template := EXTERIOR_FINISHES[ext_finish]: + exterior_layer = ConstructionLayerComponent( + ConstructionMaterial=MATERIALS_BY_NAME[ + exterior_finish_template.material_name + ], + Thickness=exterior_finish_template.thickness_m, + LayerOrder=layer_order, + ) + layers.append(exterior_layer) + layer_order += 1 + + ####### HANDLE EXTERIOR INSULATION LAYER ####### + if ext_insul_rval > 0 and ext_insul_name != "None": + thickened_insul = get_continuous_insulation_material( + ext_insul_name, ext_insul_rval + ) + layers.append( + ConstructionLayerComponent( + ConstructionMaterial=thickened_insul.material, + Thickness=thickened_insul.thickness_m, + LayerOrder=layer_order, + ) + ) + layer_order += 1 + + if self.sheathing_material != "None": + sheathing_material = get_sheathing_material(self.sheathing_material) + layers.append( + ConstructionLayerComponent( + ConstructionMaterial=sheathing_material, + Thickness=self.sheathing_material_thickness_m, + LayerOrder=layer_order, + ) + ) + layer_order += 1 + + ####### HANDLE FRAMED CAVITY LAYER ####### + # TODO: + # 13 mm (0.5 in) Air Space: Approx. RSI 0.16-0.18 + # 25 mm (1 in) Air Space: Approx. RSI 0.30-0.34 + # 100 mm (4 in) Air Space: Approx. RSI 0.35-0.40 + if cav_insul_rval > 0 and cav_insul_name != "None": + thickend_cav_insul = get_cavity_insulation_material( + cav_insul_name, cav_insul_rval + ) + cavity_insulation_mat = thickend_cav_insul.material + required_thickness_m = thickend_cav_insul.thickness_m + difference = self.cavity_depth_m - required_thickness_m + if self.cavity_airgap_strategy == "gap-then-clip": + # TODO: airgap only starts having an effect if > 13mm + gap_r = 0.17 if difference > 0 else 0 + elif self.cavity_airgap_strategy == "gap-gap-then-clip": + gap_r = ( + (0.17 * 2) + if difference > self.cavity_depth_m * 0.5 + else 0.17 + if difference > 0 + else 0 + ) + elif self.cavity_airgap_strategy == "gap-gap-gap-then-clip": + gap_r = ( + ( + (0.17 * 2) + if difference > self.cavity_depth_m * 0.5 + else 0.17 + if difference > 0 + else 0 + ) + + 0.17 + ) # additional gap between the two layers of cmu/double stud walls + elif self.cavity_airgap_strategy == "fit-to-insulation-r": + gap_r = 0 + else: + raise NotImplementedError( + f"Unsupported cavity airgap strategy: {self.cavity_airgap_strategy}" + ) + + cavity_insul_thickness_m = ( + required_thickness_m + if difference > 0 + else ( + self.cavity_depth_m + if self.cavity_airgap_strategy != "fit-to-insulation-r" + else required_thickness_m + ) + ) + + effective_r_from_insul = ( + cavity_insul_thickness_m / cavity_insulation_mat.Conductivity + ) + cavity_effective_r = effective_r_from_insul + gap_r + + framing_mat = get_framing_material(self.framing_material) + framing_thickness_m = ( + self.cavity_depth_m + if self.cavity_airgap_strategy != "fit-to-insulation-r" + else cavity_insul_thickness_m + ) + framing_r = ( + self.framing_path_r_value_override + if self.framing_path_r_value_override is not None + else (framing_thickness_m / framing_mat.Conductivity) + ) + framing_effective_k = framing_thickness_m / framing_r + + framing_frac = self.framing_fraction + cavity_frac = 1 - self.framing_fraction + cavity_effective_k = framing_thickness_m / cavity_effective_r + equivalent_k = ( + framing_frac * framing_effective_k + cavity_frac * cavity_effective_k + ) + # equivalent_r = 1 / ( + # framing_frac / framing_r + cavity_frac / cavity_effective_r + # ) + # equivalent_k_alt = framing_thickness_m / equivalent_r + # assert equivalent_k == pytest.approx(equivalent_k_alt, rel=1e-6) + + framing_mass_per_m2 = self.cavity_depth_m * framing_mat.Density + cavity_mass_per_m2 = ( + cavity_insul_thickness_m * cavity_insulation_mat.Density + ) + # TODO: ignoring mass of air in the cavity + cavity_mixed_mass_per_m2 = cavity_mass_per_m2 * cavity_frac + framing_mixed_mass_per_m2 = framing_mass_per_m2 * framing_frac + total_mass_per_m2 = cavity_mixed_mass_per_m2 + framing_mixed_mass_per_m2 + equivalent_rho = total_mass_per_m2 / self.cavity_depth_m + + cavity_mass_frac = cavity_mixed_mass_per_m2 / total_mass_per_m2 + framing_mass_frac = framing_mixed_mass_per_m2 / total_mass_per_m2 + + equivalent_cp = ( + framing_mass_frac * framing_mat.SpecificHeat + + cavity_mass_frac * cavity_insulation_mat.SpecificHeat + ) + + layers.append( + ConstructionLayerComponent( + ConstructionMaterial=ConstructionMaterialComponent( + Name=f"FramedWallConstructionCavity_{self.framing_material}_{self.framing_fraction:.3f}_{cav_insul_name}_R{cav_insul_rval:.3f}_{hash(self.model_dump_json())}", + Conductivity=equivalent_k, + Density=equivalent_rho, + SpecificHeat=equivalent_cp, + ThermalAbsorptance=0.9, + SolarAbsorptance=0.6, + VisibleAbsorptance=0.6, + TemperatureCoefficientThermalConductivity=0.0, + Roughness="MediumRough", + Type="Other", + ), + Thickness=framing_thickness_m, + LayerOrder=layer_order, + ) + ) + layer_order += 1 + else: + # TODO: deal with different thicknesses of air + gap_r = ( + 0.17 + if self.cavity_airgap_strategy == "gap-then-clip" + else ( + (0.17 * 2) + if self.cavity_airgap_strategy == "gap-gap-then-clip" + else ( + (0.17 * 3) + if self.cavity_airgap_strategy == "gap-gap-gap-then-clip" + else None + ) + ) + ) + if gap_r is None: + msg = "`fit-to-insulation-r` air gap strategy requires incompatible with no insulation.." + raise ValueError(msg) + framing_mat = get_framing_material(self.framing_material) + framing_thickness_m = self.cavity_depth_m + framing_r = framing_thickness_m / framing_mat.Conductivity + framing_frac = self.framing_fraction + gap_frac = 1 - framing_frac + effective_r = 1 / (framing_frac / framing_r + gap_frac / gap_r) + effective_k = framing_thickness_m / effective_r + framing_mass_per_m2 = framing_mat.Density * framing_thickness_m + air_mass_per_m2 = 0 # TODO: approximation, air has *some* mass + framing_mixed_mass_per_m2 = framing_mass_per_m2 * framing_frac + air_mixed_mass_per_m2 = air_mass_per_m2 * gap_frac + total_mass_per_m2 = framing_mixed_mass_per_m2 + air_mixed_mass_per_m2 + equivalent_rho = total_mass_per_m2 / framing_thickness_m + framing_mass_frac = framing_mixed_mass_per_m2 / total_mass_per_m2 + air_mass_frac = air_mixed_mass_per_m2 / total_mass_per_m2 + equivalent_cp = ( + framing_mass_frac * framing_mat.SpecificHeat + + air_mass_frac * 0 # TODO: approximation, air has *some* mass + ) + + layers.append( + ConstructionLayerComponent( + ConstructionMaterial=ConstructionMaterialComponent( + Name=f"FramedWallConstructionCavity_{self.framing_material}_{self.framing_fraction:.3f}_{cav_insul_name}_R{cav_insul_rval:.3f}_{hash(self.model_dump_json())}", + Conductivity=effective_k, + Density=equivalent_rho, + SpecificHeat=equivalent_cp, + ThermalAbsorptance=0.9, + SolarAbsorptance=0.6, + VisibleAbsorptance=0.6, + TemperatureCoefficientThermalConductivity=0.0, + Roughness="MediumRough", + Type="Other", + ), + Thickness=framing_thickness_m, + LayerOrder=layer_order, + ) + ) + layer_order += 1 + + if self.sheathing_material != "None": + sheathing_material = get_sheathing_material(self.sheathing_material) + layers.append( + ConstructionLayerComponent( + ConstructionMaterial=sheathing_material, + Thickness=self.sheathing_material_thickness_m, + LayerOrder=layer_order, + ) + ) + layer_order += 1 + + ####### HANDLE INTERIOR INSULATION LAYER ####### + if int_insul_rval > 0 and int_insul_name != "None": + thickened_int_insul = get_continuous_insulation_material( + int_insul_name, int_insul_rval + ) + layers.append( + ConstructionLayerComponent( + ConstructionMaterial=thickened_int_insul.material, + Thickness=thickened_int_insul.thickness_m, + LayerOrder=layer_order, + ) + ) + layer_order += 1 + + ####### HANDLE INTERIOR FINISH LAYER ####### + if interior_finish_template := INTERIOR_FINISHES[int_finish]: + layers.append( + ConstructionLayerComponent( + ConstructionMaterial=MATERIALS_BY_NAME[ + interior_finish_template.material_name + ], + Thickness=interior_finish_template.thickness_m, + LayerOrder=layer_order, + ) + ) + layer_order += 1 + return ConstructionAssemblyComponent( + Name=f"FramedWallConstruction_{hash(json.dumps([layer.model_dump(mode='json') for layer in layers]))}", + Type="Facade", + Layers=layers, + ) + + +class MonolithicWallConstruction(FlatWallConstruction): + """A wall with no cavity, e.g. poured concrete, masonry, etc. + + Includes support for blended materials, e.g. rc frame + masonry infill. + + For a completely monolithic wall, set the structural fraction to 1.0 + """ + + structural_material: MonolithicFramingMaterialName + infill_material: MonolithicInfillMaterialName + structural_fraction: float = Field( + default=..., + ge=0, + le=1, + description="The fraction of the wall that is the structural material.", + ) + thickness_m: float = Field( + default=..., + ge=0, + description="The thickness of the wall in meters.", + ) + + def to_construction_assembly( + self, + *, + ext_insul_name: ContinuousInsulationMaterialName | Literal["None"], + int_insul_name: ContinuousInsulationMaterialName | Literal["None"], + cav_insul_name: CavityInsulationMaterialName | Literal["None"], + ext_insul_rval: float, + int_insul_rval: float, + cav_insul_rval: float, + int_finish: WallInteriorFinishName, + ext_finish: WallExteriorFinishName, + ) -> ConstructionAssemblyComponent: + """Translate the monolithic wall construction object into an EnergyPlus construction assembly.""" + layers: list[ConstructionLayerComponent] = [] + layer_order = 0 + + ####### HANDLE EXTERIOR FINISH LAYER ####### + if exterior_finish_template := EXTERIOR_FINISHES[ext_finish]: + layers.append( + ConstructionLayerComponent( + ConstructionMaterial=MATERIALS_BY_NAME[ + exterior_finish_template.material_name + ], + Thickness=exterior_finish_template.thickness_m, + LayerOrder=layer_order, + ) + ) + layer_order += 1 + + ####### HANDLE EXTERIOR INSULATION LAYER ####### + if ext_insul_rval > 0 and ext_insul_name != "None": + thickened_ext_insul = get_continuous_insulation_material( + ext_insul_name, ext_insul_rval + ) + layers.append( + ConstructionLayerComponent( + ConstructionMaterial=thickened_ext_insul.material, + Thickness=thickened_ext_insul.thickness_m, + LayerOrder=layer_order, + ) + ) + layer_order += 1 + + ####### HANDLE STRUCTURAL MATERIAL LAYER ####### + structural_mat = get_monolithic_framing_material(self.structural_material) + infill_mat = get_monolithic_infill_material(self.infill_material) + blended_material_k = ( + structural_mat.Conductivity * self.structural_fraction + + infill_mat.Conductivity * (1 - self.structural_fraction) + ) + structural_mat_mass_per_m2 = structural_mat.Density * self.structural_fraction + infill_mat_mass_per_m2 = infill_mat.Density * (1 - self.structural_fraction) + total_mass_per_m2 = structural_mat_mass_per_m2 + infill_mat_mass_per_m2 + structural_mass_frac = structural_mat_mass_per_m2 / total_mass_per_m2 + infill_mass_frac = infill_mat_mass_per_m2 / total_mass_per_m2 + blended_material_rho = ( + structural_mass_frac * structural_mat.Density + + infill_mass_frac * infill_mat.Density + ) + blended_material_cp = ( + structural_mass_frac * structural_mat.SpecificHeat + + infill_mass_frac * infill_mat.SpecificHeat + ) + blended_material = ConstructionMaterialComponent( + Name=f"MonolithicWallConstruction_{self.structural_material}_{self.infill_material}_{self.structural_fraction:.3f}_{hash(self.model_dump_json())}", + Conductivity=blended_material_k, + Density=blended_material_rho, + SpecificHeat=blended_material_cp, + ThermalAbsorptance=0.9, + SolarAbsorptance=0.6, + VisibleAbsorptance=0.6, + Roughness="MediumRough", + Type="Other", + TemperatureCoefficientThermalConductivity=0.0, # TODO: verify that 0.0 is okay + ) + + layers.append( + ConstructionLayerComponent( + ConstructionMaterial=blended_material, + Thickness=self.thickness_m, + LayerOrder=layer_order, + ) + ) + layer_order += 1 + if cav_insul_rval > 0 and cav_insul_name != "None": + warnings.warn( + "Cavity insulation is not supported for monolithic walls, ignoring cavity insulation.", + stacklevel=2, + ) + + ####### HANDLE INTERIOR INSULATION LAYER ####### + if int_insul_rval > 0 and int_insul_name != "None": + thickened_int_insul = get_continuous_insulation_material( + int_insul_name, int_insul_rval + ) + layers.append( + ConstructionLayerComponent( + ConstructionMaterial=thickened_int_insul.material, + Thickness=thickened_int_insul.thickness_m, + LayerOrder=layer_order, + ) + ) + layer_order += 1 + + ####### HANDLE INTERIOR FINISH LAYER ####### + if interior_finish_template := INTERIOR_FINISHES[int_finish]: + layers.append( + ConstructionLayerComponent( + ConstructionMaterial=MATERIALS_BY_NAME[ + interior_finish_template.material_name + ], + Thickness=interior_finish_template.thickness_m, + LayerOrder=layer_order, + ) + ) + layer_order += 1 + return ConstructionAssemblyComponent( + Name=f"MonolithicWallConstruction_{hash(json.dumps([layer.model_dump(mode='json') for layer in layers]))}", + Type="Facade", + Layers=layers, + ) + + +# TODO: confined masonry walls, double leaf walls, AAC walls, rammed earth walls, adobe, wattle and daub walls, engineered timber panel walls (clt, glulam, etc.), informal settlement styles, etc. + + +# TODO: should we use a higher framing fraction to acount for doors, headers etc +TwoByFour16OCCWoodframeWallConstruction = FramedWallConstruction( + framing_material="SoftwoodGeneral", + framing_fraction=1.5 / 16, + cavity_depth_m=0.090, + sheathing_material="Plywood", + sheathing_material_thickness_m=0.012, + cavity_airgap_strategy="gap-then-clip", +) + + +TwoByFour24OCCWoodframeWallConstruction = FramedWallConstruction( + framing_material="SoftwoodGeneral", + framing_fraction=1.5 / 24, + cavity_depth_m=0.090, # 3.54in (90mm) stud + sheathing_material="Plywood", + sheathing_material_thickness_m=0.012, # 1/2in (12.7mm) plywood + cavity_airgap_strategy="gap-then-clip", +) + +TwoBySix16OCCWoodframeWallConstruction = FramedWallConstruction( + framing_material="SoftwoodGeneral", + framing_fraction=1.5 / 16, + cavity_depth_m=0.140, # 5.5in (140mm) stud + sheathing_material="Plywood", + sheathing_material_thickness_m=0.012, # 1/2in (12.7mm) plywood + cavity_airgap_strategy="gap-then-clip", +) + +TwoBySix24OCCWoodframeWallConstruction = FramedWallConstruction( + framing_material="SoftwoodGeneral", + framing_fraction=1.5 / 24, + cavity_depth_m=0.140, # 5.5in (140mm) stud + sheathing_material="Plywood", + sheathing_material_thickness_m=0.012, # 1/2in (12.7mm) plywood + cavity_airgap_strategy="gap-then-clip", +) + +TwoByEight16OCCWoodframeWallConstruction = FramedWallConstruction( + framing_material="SoftwoodGeneral", + framing_fraction=1.5 / 16, + cavity_depth_m=0.180, # 7in (180mm) stud + sheathing_material="Plywood", + sheathing_material_thickness_m=0.012, # 1/2in (12.7mm) plywood + cavity_airgap_strategy="gap-then-clip", +) + +TwoByEight24OCCWoodframeWallConstruction = FramedWallConstruction( + framing_material="SoftwoodGeneral", + framing_fraction=1.5 / 24, + cavity_depth_m=0.180, # 7in (180mm) stud + sheathing_material="Plywood", + sheathing_material_thickness_m=0.012, # 1/2in (12.7mm) plywood + cavity_airgap_strategy="gap-then-clip", +) +# TODO: double layer wall consructions using the gap-gap-gap-then-clip strategy + +# Face Shell: 1-1.25in (25.4mm - 31.75mm) +# End Shell: 1-1.25in (25.4mm - 31.75mm) +# Web: 1in (25.4mm) +# Standard dimensions: 8in (203.2mm) x 8in (203.2mm) x 16in (406.4mm) +# Framing fraction: (end + web + end) / total_length +# Sheathing thickness: face shell +# cavity_depth: total_depth - (face shell + face shell) +# for construction like: +# outside +# | +# v +# --------------- +# | | | +# --------------- +# ^ +# | +# inside +SingleLayerCMUWallConstruction = FramedWallConstruction( + framing_material="ConcreteMC_Light", + framing_fraction=(0.028 + 0.254 + 0.028) + / 0.406, # (end + web + end) / total_length + cavity_depth_m=0.406 - (0.028 + 0.028), # total_depth - (end + end) + sheathing_material="ConcreteMC_Light", + sheathing_material_thickness_m=0.028, # face shell + cavity_airgap_strategy="gap-then-clip", +) +# TODO: make sure that the air gap still gets added correctly since the max +DoubleLayerCMUWallConstruction = FramedWallConstruction( + framing_material="ConcreteMC_Light", + framing_fraction=(0.028 + 0.254 + 0.028) + / 0.406, # (end + web + end) / total_length + cavity_depth_m=2 * (0.406 - (0.028 + 0.028)), # total_depth - (end + end) + sheathing_material="ConcreteMC_Light", + sheathing_material_thickness_m=2 * 0.028, # face shell + cavity_airgap_strategy="gap-gap-then-clip", +) + + +# SIPs +StructuralInsulatedPanelWallConstruction = FramedWallConstruction( + framing_material="SoftwoodGeneral", + framing_fraction=0.03, # 3% framing fraction, still some joints between panels + sheathing_material="OSB", + sheathing_material_thickness_m=0.012, + cavity_airgap_strategy="fit-to-insulation-r", + cavity_depth_m=0.180, +) + +# Poured Concrete Wall Construction +PouredConcreteWallConstruction_6Inch = MonolithicWallConstruction( + structural_material="ConcreteRC_Dense", + infill_material="ConcreteRC_Dense", + structural_fraction=1.0, + thickness_m=0.15, # 15cm (6in) thick wall +) + +PouredConcreteWallConstruction_8Inch = MonolithicWallConstruction( + structural_material="ConcreteRC_Dense", + infill_material="ConcreteRC_Dense", + structural_fraction=1.0, + thickness_m=0.20, # 20cm (8in) thick wall +) + +PouredConcreteWallConstruction_10Inch = MonolithicWallConstruction( + structural_material="ConcreteRC_Dense", + infill_material="ConcreteRC_Dense", + structural_fraction=1.0, + thickness_m=0.25, # 25cm (10in) thick wall +) + +WallFramingSystemName = Literal[ + "2x4 16OCC Woodframe", + "2x4 24OCC Woodframe", + "2x6 16OCC Woodframe", + "2x6 24OCC Woodframe", + "2x8 16OCC Woodframe", + "2x8 24OCC Woodframe", + "SingleLayerCMU", + "DoubleLayerCMU", + "StructuralInsulatedPanel", + "PouredConcrete_6In", + "PouredConcrete_8In", + "PouredConcrete_10In", +] + +WallFramingSystems: dict[WallFramingSystemName, FlatWallConstruction] = { + "2x4 16OCC Woodframe": TwoByFour16OCCWoodframeWallConstruction, + "2x4 24OCC Woodframe": TwoByFour24OCCWoodframeWallConstruction, + "2x6 16OCC Woodframe": TwoBySix16OCCWoodframeWallConstruction, + "2x6 24OCC Woodframe": TwoBySix24OCCWoodframeWallConstruction, + "2x8 16OCC Woodframe": TwoByEight16OCCWoodframeWallConstruction, + "2x8 24OCC Woodframe": TwoByEight24OCCWoodframeWallConstruction, + "SingleLayerCMU": SingleLayerCMUWallConstruction, + "DoubleLayerCMU": DoubleLayerCMUWallConstruction, + "StructuralInsulatedPanel": StructuralInsulatedPanelWallConstruction, + "PouredConcrete_6In": PouredConcreteWallConstruction_6Inch, + "PouredConcrete_8In": PouredConcreteWallConstruction_8Inch, + "PouredConcrete_10In": PouredConcreteWallConstruction_10Inch, +} +for name in get_args(WallFramingSystemName): + if name not in WallFramingSystems: + msg = f"Wall framing system {name} not found in WallFramingSystems." + raise ValueError(msg) diff --git a/epinterface/sbem/flat_model.py b/epinterface/sbem/flat_model.py index b74e125..e791f04 100644 --- a/epinterface/sbem/flat_model.py +++ b/epinterface/sbem/flat_model.py @@ -2,6 +2,7 @@ from collections.abc import Callable from pathlib import Path +from typing import Literal from archetypal import IDF from pydantic import BaseModel, Field @@ -10,14 +11,11 @@ from epinterface.geometry import ShoeboxGeometry from epinterface.sbem.builder import AtticAssumptions, BasementAssumptions, Model from epinterface.sbem.components.envelope import ( - ConstructionAssemblyComponent, - ConstructionLayerComponent, EnvelopeAssemblyComponent, GlazingConstructionSimpleComponent, InfiltrationComponent, ZoneEnvelopeComponent, ) -from epinterface.sbem.components.materials import ConstructionMaterialComponent from epinterface.sbem.components.operations import ZoneOperationsComponent from epinterface.sbem.components.schedules import ( DayComponent, @@ -47,163 +45,27 @@ ZoneHVACComponent, ) from epinterface.sbem.components.zones import ZoneComponent -from epinterface.weather import WeatherUrl - -xps_board = ConstructionMaterialComponent( - Name="XPSBoard", - Conductivity=0.037, - Density=40, - SpecificHeat=1200, - ThermalAbsorptance=0.9, - SolarAbsorptance=0.6, - VisibleAbsorptance=0.6, - TemperatureCoefficientThermalConductivity=0.0, - Roughness="MediumRough", - Type="Insulation", -) - -concrete_mc_light = ConstructionMaterialComponent( - Name="ConcreteMC_Light", - Conductivity=1.65, - Density=2100, - SpecificHeat=1040, - ThermalAbsorptance=0.9, - SolarAbsorptance=0.6, - VisibleAbsorptance=0.6, - TemperatureCoefficientThermalConductivity=0.0, - Roughness="MediumRough", - Type="Concrete", +from epinterface.sbem.flat_constructions.assemblies import ( + build_floor_ceiling_assembly, + build_partition_assembly, ) - -concrete_rc_dense = ConstructionMaterialComponent( - Name="ConcreteRC_Dense", - Conductivity=1.75, - Density=2400, - SpecificHeat=840, - ThermalAbsorptance=0.9, - SolarAbsorptance=0.6, - VisibleAbsorptance=0.6, - TemperatureCoefficientThermalConductivity=0.0, - Roughness="MediumRough", - Type="Concrete", +from epinterface.sbem.flat_constructions.base import ( + CavityInsulationMaterialName, + ContinuousInsulationMaterialName, ) - -gypsum_board = ConstructionMaterialComponent( - Name="GypsumBoard", - Conductivity=0.16, - Density=950, - SpecificHeat=840, - ThermalAbsorptance=0.9, - SolarAbsorptance=0.6, - VisibleAbsorptance=0.6, - TemperatureCoefficientThermalConductivity=0.0, - Roughness="MediumRough", - Type="Finishes", +from epinterface.sbem.flat_constructions.roofs import build_roof_assembly +from epinterface.sbem.flat_constructions.slabs import ( + GroundSlabInteriorFinishName, + GroundSlabSystemName, + GroundSlabSystems, ) - -gypsum_plaster = ConstructionMaterialComponent( - Name="GypsumPlaster", - Conductivity=0.42, - Density=900, - SpecificHeat=840, - ThermalAbsorptance=0.9, - SolarAbsorptance=0.6, - VisibleAbsorptance=0.6, - TemperatureCoefficientThermalConductivity=0.0, - Roughness="MediumRough", - Type="Finishes", -) - -softwood_general = ConstructionMaterialComponent( - Name="SoftwoodGeneral", - Conductivity=0.13, - Density=496, - SpecificHeat=1630, - ThermalAbsorptance=0.9, - SolarAbsorptance=0.6, - VisibleAbsorptance=0.6, - TemperatureCoefficientThermalConductivity=0.0, - Roughness="MediumRough", - Type="Timber", -) - -clay_brick = ConstructionMaterialComponent( - Name="ClayBrick", - Conductivity=0.41, - Density=1000, - SpecificHeat=920, - ThermalAbsorptance=0.9, - SolarAbsorptance=0.6, - VisibleAbsorptance=0.6, - TemperatureCoefficientThermalConductivity=0.0, - Roughness="MediumRough", - Type="Masonry", -) - -concrete_block_h = ConstructionMaterialComponent( - Name="ConcreteBlockH", - Conductivity=1.25, - Density=880, - SpecificHeat=840, - ThermalAbsorptance=0.9, - SolarAbsorptance=0.6, - VisibleAbsorptance=0.6, - TemperatureCoefficientThermalConductivity=0.0, - Roughness="MediumRough", - Type="Concrete", -) - -fiberglass_batts = ConstructionMaterialComponent( - Name="FiberglassBatt", - Conductivity=0.043, - Density=12, - SpecificHeat=840, - ThermalAbsorptance=0.9, - SolarAbsorptance=0.6, - VisibleAbsorptance=0.6, - TemperatureCoefficientThermalConductivity=0.0, - Roughness="MediumRough", - Type="Insulation", -) - -cement_mortar = ConstructionMaterialComponent( - Name="CementMortar", - Conductivity=0.8, - Density=1900, - SpecificHeat=840, - ThermalAbsorptance=0.9, - SolarAbsorptance=0.6, - VisibleAbsorptance=0.6, - TemperatureCoefficientThermalConductivity=0.0, - Roughness="MediumRough", - Type="Other", -) - -ceramic_tile = ConstructionMaterialComponent( - Name="CeramicTile", - Conductivity=0.8, - Density=2243, - SpecificHeat=840, - ThermalAbsorptance=0.9, - SolarAbsorptance=0.6, - VisibleAbsorptance=0.6, - TemperatureCoefficientThermalConductivity=0.0, - Roughness="MediumRough", - Type="Finishes", -) - -urethane_carpet = ConstructionMaterialComponent( - Name="UrethaneCarpet", - Conductivity=0.045, - Density=110, - SpecificHeat=840, - ThermalAbsorptance=0.9, - SolarAbsorptance=0.6, - VisibleAbsorptance=0.6, - TemperatureCoefficientThermalConductivity=0.0, - Roughness="MediumRough", - Type="Finishes", +from epinterface.sbem.flat_constructions.walls import ( + WallExteriorFinishName, + WallFramingSystemName, + WallFramingSystems, + WallInteriorFinishName, ) +from epinterface.weather import WeatherUrl class ParametericYear(BaseModel): @@ -756,93 +618,12 @@ def to_schedules(self): return hsp_year, csp_year +ZoningMode = Literal["core/perim", "by_storey", "by_building", "auto"] + + class FlatModel(BaseModel): """A flattened set of parameters for invoking building energy models more conveniently.""" - # EquipmentSummerWeekdayNight: float = Field(ge=0, le=1) - # EquipmentSummerWeekdayEarlyMorning: float = Field(ge=0, le=1) - # EquipmentSummerWeekdayMorning: float = Field(ge=0, le=1) - # EquipmentSummerWeekdayLunch: float = Field(ge=0, le=1) - # EquipmentSummerWeekdayAfternoon: float = Field(ge=0, le=1) - # EquipmentSummerWeekdayEvening: float = Field(ge=0, le=1) - - # EquipmentSummerWeekendNight: float = Field(ge=0, le=1) - # EquipmentSummerWeekendEarlyMorning: float = Field(ge=0, le=1) - # EquipmentSummerWeekendMorning: float = Field(ge=0, le=1) - # EquipmentSummerWeekendLunch: float = Field(ge=0, le=1) - # EquipmentSummerWeekendAfternoon: float = Field(ge=0, le=1) - # EquipmentSummerWeekendEvening: float = Field(ge=0, le=1) - - # EquipmentRegularWeekdayNight: float = Field(ge=0, le=1) - # EquipmentRegularWeekdayEarlyMorning: float = Field(ge=0, le=1) - # EquipmentRegularWeekdayMorning: float = Field(ge=0, le=1) - # EquipmentRegularWeekdayLunch: float = Field(ge=0, le=1) - # EquipmentRegularWeekdayAfternoon: float = Field(ge=0, le=1) - # EquipmentRegularWeekdayEvening: float = Field(ge=0, le=1) - - # EquipmentRegularWeekendNight: float = Field(ge=0, le=1) - # EquipmentRegularWeekendEarlyMorning: float = Field(ge=0, le=1) - # EquipmentRegularWeekendMorning: float = Field(ge=0, le=1) - # EquipmentRegularWeekendLunch: float = Field(ge=0, le=1) - # EquipmentRegularWeekendAfternoon: float = Field(ge=0, le=1) - # EquipmentRegularWeekendEvening: float = Field(ge=0, le=1) - - # LightingSummerWeekdayNight: float = Field(ge=0, le=1) - # LightingSummerWeekdayEarlyMorning: float = Field(ge=0, le=1) - # LightingSummerWeekdayMorning: float = Field(ge=0, le=1) - # LightingSummerWeekdayLunch: float = Field(ge=0, le=1) - # LightingSummerWeekdayAfternoon: float = Field(ge=0, le=1) - # LightingSummerWeekdayEvening: float = Field(ge=0, le=1) - - # LightingSummerWeekendNight: float = Field(ge=0, le=1) - # LightingSummerWeekendEarlyMorning: float = Field(ge=0, le=1) - # LightingSummerWeekendMorning: float = Field(ge=0, le=1) - # LightingSummerWeekendLunch: float = Field(ge=0, le=1) - # LightingSummerWeekendAfternoon: float = Field(ge=0, le=1) - # LightingSummerWeekendEvening: float = Field(ge=0, le=1) - - # LightingRegularWeekdayNight: float = Field(ge=0, le=1) - # LightingRegularWeekdayEarlyMorning: float = Field(ge=0, le=1) - # LightingRegularWeekdayMorning: float = Field(ge=0, le=1) - # LightingRegularWeekdayLunch: float = Field(ge=0, le=1) - # LightingRegularWeekdayAfternoon: float = Field(ge=0, le=1) - # LightingRegularWeekdayEvening: float = Field(ge=0, le=1) - - # LightingRegularWeekendNight: float = Field(ge=0, le=1) - # LightingRegularWeekendEarlyMorning: float = Field(ge=0, le=1) - # LightingRegularWeekendMorning: float = Field(ge=0, le=1) - # LightingRegularWeekendLunch: float = Field(ge=0, le=1) - # LightingRegularWeekendAfternoon: float = Field(ge=0, le=1) - # LightingRegularWeekendEvening: float = Field(ge=0, le=1) - - # OccupancySummerWeekdayNight: float = Field(ge=0, le=1) - # OccupancySummerWeekdayEarlyMorning: float = Field(ge=0, le=1) - # OccupancySummerWeekdayMorning: float = Field(ge=0, le=1) - # OccupancySummerWeekdayLunch: float = Field(ge=0, le=1) - # OccupancySummerWeekdayAfternoon: float = Field(ge=0, le=1) - # OccupancySummerWeekdayEvening: float = Field(ge=0, le=1) - - # OccupancySummerWeekendNight: float = Field(ge=0, le=1) - # OccupancySummerWeekendEarlyMorning: float = Field(ge=0, le=1) - # OccupancySummerWeekendMorning: float = Field(ge=0, le=1) - # OccupancySummerWeekendLunch: float = Field(ge=0, le=1) - # OccupancySummerWeekendAfternoon: float = Field(ge=0, le=1) - # OccupancySummerWeekendEvening: float = Field(ge=0, le=1) - - # OccupancyRegularWeekdayNight: float = Field(ge=0, le=1) - # OccupancyRegularWeekdayEarlyMorning: float = Field(ge=0, le=1) - # OccupancyRegularWeekdayMorning: float = Field(ge=0, le=1) - # OccupancyRegularWeekdayLunch: float = Field(ge=0, le=1) - # OccupancyRegularWeekdayAfternoon: float = Field(ge=0, le=1) - # OccupancyRegularWeekdayEvening: float = Field(ge=0, le=1) - - # OccupancyRegularWeekendNight: float = Field(ge=0, le=1) - # OccupancyRegularWeekendEarlyMorning: float = Field(ge=0, le=1) - # OccupancyRegularWeekendMorning: float = Field(ge=0, le=1) - # OccupancyRegularWeekendLunch: float = Field(ge=0, le=1) - # OccupancyRegularWeekendAfternoon: float = Field(ge=0, le=1) - # OccupancyRegularWeekendEvening: float = Field(ge=0, le=1) - EquipmentBase: float = Field(ge=0, le=1) EquipmentAMInterp: float = Field(ge=0, le=1) EquipmentLunchInterp: float = Field(ge=0, le=1) @@ -864,20 +645,6 @@ class FlatModel(BaseModel): OccupancyWeekendPeakInterp: float = Field(ge=0, le=1) OccupancySummerPeakInterp: float = Field(ge=0, le=1) - # HSPRegularWeekdayWorkhours: float = Field(ge=0, le=23) - # HSPRegularWeekdayNight: float = Field(ge=0, le=23) - # HSPSummerWeekdayWorkhours: float = Field(ge=0, le=23) - # HSPSummerWeekdayNight: float = Field(ge=0, le=23) - # HSPWeekendWorkhours: float = Field(ge=0, le=23) - # HSPWeekendNight: float = Field(ge=0, le=23) - - # CSPRegularWeekdayWorkhours: float = Field(ge=20, le=30) - # CSPRegularWeekdayNight: float = Field(ge=20, le=30) - # CSPSummerWeekdayWorkhours: float = Field(ge=20, le=30) - # CSPSummerWeekdayNight: float = Field(ge=20, le=30) - # CSPWeekendWorkhours: float = Field(ge=20, le=30) - # CSPWeekendNight: float = Field(ge=20, le=30) - HeatingSetpointBase: float = Field(ge=0, le=23) SetpointDeadband: float = Field(ge=0, le=10) HeatingSetpointSetback: float = Field(ge=0, le=10) @@ -910,14 +677,60 @@ class FlatModel(BaseModel): DHWDistributionCOP: float InfiltrationACH: float + BasementInfiltrationACH: float WindowUValue: float WindowSHGF: float WindowTVis: float - FacadeRValue: float - RoofRValue: float - SlabRValue: float + FacadeFramingSystem: WallFramingSystemName = "2x4 16OCC Woodframe" + FacadeCavityInsulationRValue: float = Field(default=0, ge=0) + FacadeExteriorInsulationRValue: float = Field(default=0, ge=0) + FacadeInteriorInsulationRValue: float = Field(default=0, ge=0) + FacadeExteriorInsulationMaterial: ContinuousInsulationMaterialName = "XPSBoard" + FacadeInteriorInsulationMaterial: ContinuousInsulationMaterialName = "XPSBoard" + FacadeCavityInsulationMaterial: CavityInsulationMaterialName = "FiberglassBatt" + FacadeInteriorFinish: WallInteriorFinishName = "drywall" + FacadeExteriorFinish: WallExteriorFinishName = "wood_siding" + + SubterraneanWallFramingSystem: WallFramingSystemName = "2x4 16OCC Woodframe" + SubterraneanCavityInsulationRValue: float = Field(default=0, ge=0) + SubterraneanExteriorInsulationRValue: float = Field(default=0, ge=0) + SubterraneanInteriorInsulationRValue: float = Field(default=0, ge=0) + SubterraneanExteriorInsulationMaterial: ContinuousInsulationMaterialName = ( + "XPSBoard" + ) + SubterraneanInteriorInsulationMaterial: ContinuousInsulationMaterialName = ( + "XPSBoard" + ) + SubterraneanCavityInsulationMaterial: CavityInsulationMaterialName = ( + "FiberglassBatt" + ) + SubterraneanInteriorFinish: WallInteriorFinishName = "drywall" + SubterraneanExteriorFinish: WallExteriorFinishName = "wood_siding" + + # RoofStructuralSystem: RoofStructuralSystemType = "poured_concrete" + # RoofCavityInsulationRValue: float = Field(default=0, ge=0) + # RoofExteriorInsulationRValue: float = Field(default=2.5, ge=0) + # RoofInteriorInsulationRValue: float = Field(default=0, ge=0) + # RoofExteriorInsulationMaterial: ContinuousInsulationMaterial = "polyiso" + # RoofInteriorInsulationMaterial: ContinuousInsulationMaterial = "polyiso" + # RoofCavityInsulationMaterial: CavityInsulationMaterial = "fiberglass" + # RoofExteriorCavityType: ExteriorCavityType = "none" + # RoofInteriorFinish: RoofInteriorFinishType = "gypsum_board" + # RoofExteriorFinish: RoofExteriorFinishType = "epdm_membrane" + RoofRValue: float = Field(default=3.0, ge=0) + + GroundSlabSystem: GroundSlabSystemName = "BasicConcrete" + GroundSlabInsulationRValue: float = Field(default=1.5, ge=0) + GroundSlabInsulationMaterial: ContinuousInsulationMaterialName = "XPSBoard" + GroundSlabInteriorFinish: GroundSlabInteriorFinishName = "wood_floor" + GroundSlabStructuralThickness: float = Field( + default=0.15, + gt=0, + le=0.5, + description="The thickness of the structural material in the ground slab.", + ) WWR: float F2FHeight: float @@ -925,476 +738,12 @@ class FlatModel(BaseModel): Width: float Depth: float Rotation: float + ZoningMode: ZoningMode EPWURI: WeatherUrl | Path def to_zone(self) -> ZoneComponent: """Convert the flat model to a full zone.""" - # occ_regular_workday = DayComponent( - # Name="Occupancy_Regular_Workday", - # Type="Fraction", - # Hour_00=self.OccupancyRegularWeekdayNight, - # Hour_01=self.OccupancyRegularWeekdayNight, - # Hour_02=self.OccupancyRegularWeekdayNight, - # Hour_03=self.OccupancyRegularWeekdayNight, - # Hour_04=self.OccupancyRegularWeekdayNight, - # Hour_05=self.OccupancyRegularWeekdayNight, - # Hour_06=self.OccupancyRegularWeekdayEarlyMorning, - # Hour_07=self.OccupancyRegularWeekdayEarlyMorning, - # Hour_08=self.OccupancyRegularWeekdayEarlyMorning, - # Hour_09=self.OccupancyRegularWeekdayMorning, - # Hour_10=self.OccupancyRegularWeekdayMorning, - # Hour_11=self.OccupancyRegularWeekdayMorning, - # Hour_12=self.OccupancyRegularWeekdayLunch, - # Hour_13=self.OccupancyRegularWeekdayLunch, - # Hour_14=self.OccupancyRegularWeekdayAfternoon, - # Hour_15=self.OccupancyRegularWeekdayAfternoon, - # Hour_16=self.OccupancyRegularWeekdayAfternoon, - # Hour_17=self.OccupancyRegularWeekdayAfternoon, - # Hour_18=self.OccupancyRegularWeekdayEvening, - # Hour_19=self.OccupancyRegularWeekdayEvening, - # Hour_20=self.OccupancyRegularWeekdayEvening, - # Hour_21=self.OccupancyRegularWeekdayNight, - # Hour_22=self.OccupancyRegularWeekdayNight, - # Hour_23=self.OccupancyRegularWeekdayNight, - # ) - - # occ_regular_weekend = DayComponent( - # Name="Occupancy_Regular_Weekend", - # Type="Fraction", - # Hour_00=self.OccupancyRegularWeekendNight, - # Hour_01=self.OccupancyRegularWeekendNight, - # Hour_02=self.OccupancyRegularWeekendNight, - # Hour_03=self.OccupancyRegularWeekendNight, - # Hour_04=self.OccupancyRegularWeekendNight, - # Hour_05=self.OccupancyRegularWeekendNight, - # Hour_06=self.OccupancyRegularWeekendEarlyMorning, - # Hour_07=self.OccupancyRegularWeekendEarlyMorning, - # Hour_08=self.OccupancyRegularWeekendEarlyMorning, - # Hour_09=self.OccupancyRegularWeekendMorning, - # Hour_10=self.OccupancyRegularWeekendMorning, - # Hour_11=self.OccupancyRegularWeekendMorning, - # Hour_12=self.OccupancyRegularWeekendLunch, - # Hour_13=self.OccupancyRegularWeekendLunch, - # Hour_14=self.OccupancyRegularWeekendAfternoon, - # Hour_15=self.OccupancyRegularWeekendAfternoon, - # Hour_16=self.OccupancyRegularWeekendAfternoon, - # Hour_17=self.OccupancyRegularWeekendAfternoon, - # Hour_18=self.OccupancyRegularWeekendEvening, - # Hour_19=self.OccupancyRegularWeekendEvening, - # Hour_20=self.OccupancyRegularWeekendEvening, - # Hour_21=self.OccupancyRegularWeekendNight, - # Hour_22=self.OccupancyRegularWeekendNight, - # Hour_23=self.OccupancyRegularWeekendNight, - # ) - - # occ_summer_workday = DayComponent( - # Name="Occupancy_Summer_Workday", - # Type="Fraction", - # Hour_00=self.OccupancySummerWeekdayNight, - # Hour_01=self.OccupancySummerWeekdayNight, - # Hour_02=self.OccupancySummerWeekdayNight, - # Hour_03=self.OccupancySummerWeekdayNight, - # Hour_04=self.OccupancySummerWeekdayNight, - # Hour_05=self.OccupancySummerWeekdayNight, - # Hour_06=self.OccupancySummerWeekdayEarlyMorning, - # Hour_07=self.OccupancySummerWeekdayEarlyMorning, - # Hour_08=self.OccupancySummerWeekdayEarlyMorning, - # Hour_09=self.OccupancySummerWeekdayMorning, - # Hour_10=self.OccupancySummerWeekdayMorning, - # Hour_11=self.OccupancySummerWeekdayMorning, - # Hour_12=self.OccupancySummerWeekdayLunch, - # Hour_13=self.OccupancySummerWeekdayLunch, - # Hour_14=self.OccupancySummerWeekdayAfternoon, - # Hour_15=self.OccupancySummerWeekdayAfternoon, - # Hour_16=self.OccupancySummerWeekdayAfternoon, - # Hour_17=self.OccupancySummerWeekdayAfternoon, - # Hour_18=self.OccupancySummerWeekdayEvening, - # Hour_19=self.OccupancySummerWeekdayEvening, - # Hour_20=self.OccupancySummerWeekdayEvening, - # Hour_21=self.OccupancySummerWeekdayNight, - # Hour_22=self.OccupancySummerWeekdayNight, - # Hour_23=self.OccupancySummerWeekdayNight, - # ) - - # occ_summer_weekend = DayComponent( - # Name="Occupancy_Summer_Weekend", - # Type="Fraction", - # Hour_00=self.OccupancySummerWeekendNight, - # Hour_01=self.OccupancySummerWeekendNight, - # Hour_02=self.OccupancySummerWeekendNight, - # Hour_03=self.OccupancySummerWeekendNight, - # Hour_04=self.OccupancySummerWeekendNight, - # Hour_05=self.OccupancySummerWeekendNight, - # Hour_06=self.OccupancySummerWeekendEarlyMorning, - # Hour_07=self.OccupancySummerWeekendEarlyMorning, - # Hour_08=self.OccupancySummerWeekendEarlyMorning, - # Hour_09=self.OccupancySummerWeekendMorning, - # Hour_10=self.OccupancySummerWeekendMorning, - # Hour_11=self.OccupancySummerWeekendMorning, - # Hour_12=self.OccupancySummerWeekendLunch, - # Hour_13=self.OccupancySummerWeekendLunch, - # Hour_14=self.OccupancySummerWeekendAfternoon, - # Hour_15=self.OccupancySummerWeekendAfternoon, - # Hour_16=self.OccupancySummerWeekendAfternoon, - # Hour_17=self.OccupancySummerWeekendAfternoon, - # Hour_18=self.OccupancySummerWeekendEvening, - # Hour_19=self.OccupancySummerWeekendEvening, - # Hour_20=self.OccupancySummerWeekendEvening, - # Hour_21=self.OccupancySummerWeekendNight, - # Hour_22=self.OccupancySummerWeekendNight, - # Hour_23=self.OccupancySummerWeekendNight, - # ) - - # occ_regular_week = WeekComponent( - # Name="Occupancy_Regular_Week", - # Monday=occ_regular_workday, - # Tuesday=occ_regular_workday, - # Wednesday=occ_regular_workday, - # Thursday=occ_regular_workday, - # Friday=occ_regular_workday, - # Saturday=occ_regular_weekend, - # Sunday=occ_regular_weekend, - # ) - - # occ_summer_week = WeekComponent( - # Name="Occupancy_Summer_Week", - # Monday=occ_summer_workday, - # Tuesday=occ_summer_workday, - # Wednesday=occ_summer_workday, - # Thursday=occ_summer_workday, - # Friday=occ_summer_workday, - # Saturday=occ_summer_weekend, - # Sunday=occ_summer_weekend, - # ) - - # occ_year = YearComponent( - # Name="Occupancy_Schedule", - # Type="Occupancy", - # January=occ_regular_week, - # February=occ_regular_week, - # March=occ_regular_week, - # April=occ_regular_week, - # May=occ_regular_week, - # June=occ_summer_week, - # July=occ_summer_week, - # August=occ_summer_week, - # September=occ_regular_week, - # October=occ_regular_week, - # November=occ_regular_week, - # December=occ_regular_week, - # ) - - # lighting_regular_workday = DayComponent( - # Name="Lighting_Regular_Workday", - # Type="Fraction", - # Hour_00=self.LightingRegularWeekdayNight, - # Hour_01=self.LightingRegularWeekdayNight, - # Hour_02=self.LightingRegularWeekdayNight, - # Hour_03=self.LightingRegularWeekdayNight, - # Hour_04=self.LightingRegularWeekdayNight, - # Hour_05=self.LightingRegularWeekdayNight, - # Hour_06=self.LightingRegularWeekdayEarlyMorning, - # Hour_07=self.LightingRegularWeekdayEarlyMorning, - # Hour_08=self.LightingRegularWeekdayEarlyMorning, - # Hour_09=self.LightingRegularWeekdayMorning, - # Hour_10=self.LightingRegularWeekdayMorning, - # Hour_11=self.LightingRegularWeekdayMorning, - # Hour_12=self.LightingRegularWeekdayLunch, - # Hour_13=self.LightingRegularWeekdayLunch, - # Hour_14=self.LightingRegularWeekdayAfternoon, - # Hour_15=self.LightingRegularWeekdayAfternoon, - # Hour_16=self.LightingRegularWeekdayAfternoon, - # Hour_17=self.LightingRegularWeekdayAfternoon, - # Hour_18=self.LightingRegularWeekdayEvening, - # Hour_19=self.LightingRegularWeekdayEvening, - # Hour_20=self.LightingRegularWeekdayEvening, - # Hour_21=self.LightingRegularWeekdayNight, - # Hour_22=self.LightingRegularWeekdayNight, - # Hour_23=self.LightingRegularWeekdayNight, - # ) - - # lighting_regular_weekend = DayComponent( - # Name="Lighting_Regular_Weekend", - # Type="Fraction", - # Hour_00=self.LightingRegularWeekendNight, - # Hour_01=self.LightingRegularWeekendNight, - # Hour_02=self.LightingRegularWeekendNight, - # Hour_03=self.LightingRegularWeekendNight, - # Hour_04=self.LightingRegularWeekendNight, - # Hour_05=self.LightingRegularWeekendNight, - # Hour_06=self.LightingRegularWeekendEarlyMorning, - # Hour_07=self.LightingRegularWeekendEarlyMorning, - # Hour_08=self.LightingRegularWeekendEarlyMorning, - # Hour_09=self.LightingRegularWeekendMorning, - # Hour_10=self.LightingRegularWeekendMorning, - # Hour_11=self.LightingRegularWeekendMorning, - # Hour_12=self.LightingRegularWeekendLunch, - # Hour_13=self.LightingRegularWeekendLunch, - # Hour_14=self.LightingRegularWeekendAfternoon, - # Hour_15=self.LightingRegularWeekendAfternoon, - # Hour_16=self.LightingRegularWeekendAfternoon, - # Hour_17=self.LightingRegularWeekendAfternoon, - # Hour_18=self.LightingRegularWeekendEvening, - # Hour_19=self.LightingRegularWeekendEvening, - # Hour_20=self.LightingRegularWeekendEvening, - # Hour_21=self.LightingRegularWeekendNight, - # Hour_22=self.LightingRegularWeekendNight, - # Hour_23=self.LightingRegularWeekendNight, - # ) - - # lighting_summer_workday = DayComponent( - # Name="Lighting_Summer_Workday", - # Type="Fraction", - # Hour_00=self.LightingSummerWeekdayNight, - # Hour_01=self.LightingSummerWeekdayNight, - # Hour_02=self.LightingSummerWeekdayNight, - # Hour_03=self.LightingSummerWeekdayNight, - # Hour_04=self.LightingSummerWeekdayNight, - # Hour_05=self.LightingSummerWeekdayNight, - # Hour_06=self.LightingSummerWeekdayEarlyMorning, - # Hour_07=self.LightingSummerWeekdayEarlyMorning, - # Hour_08=self.LightingSummerWeekdayEarlyMorning, - # Hour_09=self.LightingSummerWeekdayMorning, - # Hour_10=self.LightingSummerWeekdayMorning, - # Hour_11=self.LightingSummerWeekdayMorning, - # Hour_12=self.LightingSummerWeekdayLunch, - # Hour_13=self.LightingSummerWeekdayLunch, - # Hour_14=self.LightingSummerWeekdayAfternoon, - # Hour_15=self.LightingSummerWeekdayAfternoon, - # Hour_16=self.LightingSummerWeekdayAfternoon, - # Hour_17=self.LightingSummerWeekdayAfternoon, - # Hour_18=self.LightingSummerWeekdayEvening, - # Hour_19=self.LightingSummerWeekdayEvening, - # Hour_20=self.LightingSummerWeekdayEvening, - # Hour_21=self.LightingSummerWeekdayNight, - # Hour_22=self.LightingSummerWeekdayNight, - # Hour_23=self.LightingSummerWeekdayNight, - # ) - - # lighting_summer_weekend = DayComponent( - # Name="Lighting_Summer_Weekend", - # Type="Fraction", - # Hour_00=self.LightingSummerWeekendNight, - # Hour_01=self.LightingSummerWeekendNight, - # Hour_02=self.LightingSummerWeekendNight, - # Hour_03=self.LightingSummerWeekendNight, - # Hour_04=self.LightingSummerWeekendNight, - # Hour_05=self.LightingSummerWeekendNight, - # Hour_06=self.LightingSummerWeekendEarlyMorning, - # Hour_07=self.LightingSummerWeekendEarlyMorning, - # Hour_08=self.LightingSummerWeekendEarlyMorning, - # Hour_09=self.LightingSummerWeekendMorning, - # Hour_10=self.LightingSummerWeekendMorning, - # Hour_11=self.LightingSummerWeekendMorning, - # Hour_12=self.LightingSummerWeekendLunch, - # Hour_13=self.LightingSummerWeekendLunch, - # Hour_14=self.LightingSummerWeekendAfternoon, - # Hour_15=self.LightingSummerWeekendAfternoon, - # Hour_16=self.LightingSummerWeekendAfternoon, - # Hour_17=self.LightingSummerWeekendAfternoon, - # Hour_18=self.LightingSummerWeekendEvening, - # Hour_19=self.LightingSummerWeekendEvening, - # Hour_20=self.LightingSummerWeekendEvening, - # Hour_21=self.LightingSummerWeekendNight, - # Hour_22=self.LightingSummerWeekendNight, - # Hour_23=self.LightingSummerWeekendNight, - # ) - - # lighting_regular_week = WeekComponent( - # Name="Lighting_Regular_Week", - # Monday=lighting_regular_workday, - # Tuesday=lighting_regular_workday, - # Wednesday=lighting_regular_workday, - # Thursday=lighting_regular_workday, - # Friday=lighting_regular_workday, - # Saturday=lighting_regular_weekend, - # Sunday=lighting_regular_weekend, - # ) - - # lighting_summer_week = WeekComponent( - # Name="Lighting_Summer_Week", - # Monday=lighting_summer_workday, - # Tuesday=lighting_summer_workday, - # Wednesday=lighting_summer_workday, - # Thursday=lighting_summer_workday, - # Friday=lighting_summer_workday, - # Saturday=lighting_summer_weekend, - # Sunday=lighting_summer_weekend, - # ) - - # lighting_year = YearComponent( - # Name="Lighting_Schedule", - # Type="Lighting", - # January=lighting_regular_week, - # February=lighting_regular_week, - # March=lighting_regular_week, - # April=lighting_regular_week, - # May=lighting_regular_week, - # June=lighting_summer_week, - # July=lighting_summer_week, - # August=lighting_summer_week, - # September=lighting_regular_week, - # October=lighting_regular_week, - # November=lighting_regular_week, - # December=lighting_regular_week, - # ) - - # equipment_regular_workday = DayComponent( - # Name="Equipment_Regular_Workday", - # Type="Fraction", - # Hour_00=self.EquipmentRegularWeekdayNight, - # Hour_01=self.EquipmentRegularWeekdayNight, - # Hour_02=self.EquipmentRegularWeekdayNight, - # Hour_03=self.EquipmentRegularWeekdayNight, - # Hour_04=self.EquipmentRegularWeekdayNight, - # Hour_05=self.EquipmentRegularWeekdayNight, - # Hour_06=self.EquipmentRegularWeekdayEarlyMorning, - # Hour_07=self.EquipmentRegularWeekdayEarlyMorning, - # Hour_08=self.EquipmentRegularWeekdayEarlyMorning, - # Hour_09=self.EquipmentRegularWeekdayMorning, - # Hour_10=self.EquipmentRegularWeekdayMorning, - # Hour_11=self.EquipmentRegularWeekdayMorning, - # Hour_12=self.EquipmentRegularWeekdayLunch, - # Hour_13=self.EquipmentRegularWeekdayLunch, - # Hour_14=self.EquipmentRegularWeekdayAfternoon, - # Hour_15=self.EquipmentRegularWeekdayAfternoon, - # Hour_16=self.EquipmentRegularWeekdayAfternoon, - # Hour_17=self.EquipmentRegularWeekdayAfternoon, - # Hour_18=self.EquipmentRegularWeekdayEvening, - # Hour_19=self.EquipmentRegularWeekdayEvening, - # Hour_20=self.EquipmentRegularWeekdayEvening, - # Hour_21=self.EquipmentRegularWeekdayNight, - # Hour_22=self.EquipmentRegularWeekdayNight, - # Hour_23=self.EquipmentRegularWeekdayNight, - # ) - - # equipment_regular_weekend = DayComponent( - # Name="Equipment_Regular_Weekend", - # Type="Fraction", - # Hour_00=self.EquipmentRegularWeekendNight, - # Hour_01=self.EquipmentRegularWeekendNight, - # Hour_02=self.EquipmentRegularWeekendNight, - # Hour_03=self.EquipmentRegularWeekendNight, - # Hour_04=self.EquipmentRegularWeekendNight, - # Hour_05=self.EquipmentRegularWeekendNight, - # Hour_06=self.EquipmentRegularWeekendEarlyMorning, - # Hour_07=self.EquipmentRegularWeekendEarlyMorning, - # Hour_08=self.EquipmentRegularWeekendEarlyMorning, - # Hour_09=self.EquipmentRegularWeekendMorning, - # Hour_10=self.EquipmentRegularWeekendMorning, - # Hour_11=self.EquipmentRegularWeekendMorning, - # Hour_12=self.EquipmentRegularWeekendLunch, - # Hour_13=self.EquipmentRegularWeekendLunch, - # Hour_14=self.EquipmentRegularWeekendAfternoon, - # Hour_15=self.EquipmentRegularWeekendAfternoon, - # Hour_16=self.EquipmentRegularWeekendAfternoon, - # Hour_17=self.EquipmentRegularWeekendAfternoon, - # Hour_18=self.EquipmentRegularWeekendEvening, - # Hour_19=self.EquipmentRegularWeekendEvening, - # Hour_20=self.EquipmentRegularWeekendEvening, - # Hour_21=self.EquipmentRegularWeekendNight, - # Hour_22=self.EquipmentRegularWeekendNight, - # Hour_23=self.EquipmentRegularWeekendNight, - # ) - - # equipment_summer_workday = DayComponent( - # Name="Equipment_Summer_Workday", - # Type="Fraction", - # Hour_00=self.EquipmentSummerWeekdayNight, - # Hour_01=self.EquipmentSummerWeekdayNight, - # Hour_02=self.EquipmentSummerWeekdayNight, - # Hour_03=self.EquipmentSummerWeekdayNight, - # Hour_04=self.EquipmentSummerWeekdayNight, - # Hour_05=self.EquipmentSummerWeekdayNight, - # Hour_06=self.EquipmentSummerWeekdayEarlyMorning, - # Hour_07=self.EquipmentSummerWeekdayEarlyMorning, - # Hour_08=self.EquipmentSummerWeekdayEarlyMorning, - # Hour_09=self.EquipmentSummerWeekdayMorning, - # Hour_10=self.EquipmentSummerWeekdayMorning, - # Hour_11=self.EquipmentSummerWeekdayMorning, - # Hour_12=self.EquipmentSummerWeekdayLunch, - # Hour_13=self.EquipmentSummerWeekdayLunch, - # Hour_14=self.EquipmentSummerWeekdayAfternoon, - # Hour_15=self.EquipmentSummerWeekdayAfternoon, - # Hour_16=self.EquipmentSummerWeekdayAfternoon, - # Hour_17=self.EquipmentSummerWeekdayAfternoon, - # Hour_18=self.EquipmentSummerWeekdayEvening, - # Hour_19=self.EquipmentSummerWeekdayEvening, - # Hour_20=self.EquipmentSummerWeekdayEvening, - # Hour_21=self.EquipmentSummerWeekdayNight, - # Hour_22=self.EquipmentSummerWeekdayNight, - # Hour_23=self.EquipmentSummerWeekdayNight, - # ) - - # equipment_summer_weekend = DayComponent( - # Name="Equipment_Summer_Weekend", - # Type="Fraction", - # Hour_00=self.EquipmentSummerWeekendNight, - # Hour_01=self.EquipmentSummerWeekendNight, - # Hour_02=self.EquipmentSummerWeekendNight, - # Hour_03=self.EquipmentSummerWeekendNight, - # Hour_04=self.EquipmentSummerWeekendNight, - # Hour_05=self.EquipmentSummerWeekendNight, - # Hour_06=self.EquipmentSummerWeekendEarlyMorning, - # Hour_07=self.EquipmentSummerWeekendEarlyMorning, - # Hour_08=self.EquipmentSummerWeekendEarlyMorning, - # Hour_09=self.EquipmentSummerWeekendMorning, - # Hour_10=self.EquipmentSummerWeekendMorning, - # Hour_11=self.EquipmentSummerWeekendMorning, - # Hour_12=self.EquipmentSummerWeekendLunch, - # Hour_13=self.EquipmentSummerWeekendLunch, - # Hour_14=self.EquipmentSummerWeekendAfternoon, - # Hour_15=self.EquipmentSummerWeekendAfternoon, - # Hour_16=self.EquipmentSummerWeekendAfternoon, - # Hour_17=self.EquipmentSummerWeekendAfternoon, - # Hour_18=self.EquipmentSummerWeekendEvening, - # Hour_19=self.EquipmentSummerWeekendEvening, - # Hour_20=self.EquipmentSummerWeekendEvening, - # Hour_21=self.EquipmentSummerWeekendNight, - # Hour_22=self.EquipmentSummerWeekendNight, - # Hour_23=self.EquipmentSummerWeekendNight, - # ) - - # equipment_regular_week = WeekComponent( - # Name="Equipment_Regular_Week", - # Monday=equipment_regular_workday, - # Tuesday=equipment_regular_workday, - # Wednesday=equipment_regular_workday, - # Thursday=equipment_regular_workday, - # Friday=equipment_regular_workday, - # Saturday=equipment_regular_weekend, - # Sunday=equipment_regular_weekend, - # ) - - # equipment_summer_week = WeekComponent( - # Name="Equipment_Summer_Week", - # Monday=equipment_summer_workday, - # Tuesday=equipment_summer_workday, - # Wednesday=equipment_summer_workday, - # Thursday=equipment_summer_workday, - # Friday=equipment_summer_workday, - # Saturday=equipment_summer_weekend, - # Sunday=equipment_summer_weekend, - # ) - - # equipment_year = YearComponent( - # Name="equipment_Schedule", - # Type="Equipment", - # January=equipment_regular_week, - # February=equipment_regular_week, - # March=equipment_regular_week, - # April=equipment_regular_week, - # May=equipment_regular_week, - # June=equipment_summer_week, - # July=equipment_summer_week, - # August=equipment_summer_week, - # September=equipment_regular_week, - # October=equipment_regular_week, - # November=lighting_regular_week, - # December=equipment_regular_week, - # ) - equipment_paramteric = ParametericYear( Base=self.EquipmentBase, AMInterp=self.EquipmentAMInterp, @@ -1432,258 +781,6 @@ def to_zone(self) -> ZoneComponent: name="Occupancy", category="Occupancy" ) - # hsp_regular_workday = DayComponent( - # Name="HeatingSetpoint_Regular_Workday", - # Type="Temperature", - # Hour_00=self.HSPRegularWeekdayNight, - # Hour_01=self.HSPRegularWeekdayNight, - # Hour_02=self.HSPRegularWeekdayNight, - # Hour_03=self.HSPRegularWeekdayNight, - # Hour_04=self.HSPRegularWeekdayNight, - # Hour_05=self.HSPRegularWeekdayNight, - # Hour_06=self.HSPRegularWeekdayWorkhours, - # Hour_07=self.HSPRegularWeekdayWorkhours, - # Hour_08=self.HSPRegularWeekdayWorkhours, - # Hour_09=self.HSPRegularWeekdayWorkhours, - # Hour_10=self.HSPRegularWeekdayWorkhours, - # Hour_11=self.HSPRegularWeekdayWorkhours, - # Hour_12=self.HSPRegularWeekdayWorkhours, - # Hour_13=self.HSPRegularWeekdayWorkhours, - # Hour_14=self.HSPRegularWeekdayWorkhours, - # Hour_15=self.HSPRegularWeekdayWorkhours, - # Hour_16=self.HSPRegularWeekdayWorkhours, - # Hour_17=self.HSPRegularWeekdayWorkhours, - # Hour_18=self.HSPRegularWeekdayWorkhours, - # Hour_19=self.HSPRegularWeekdayNight, - # Hour_20=self.HSPRegularWeekdayNight, - # Hour_21=self.HSPRegularWeekdayNight, - # Hour_22=self.HSPRegularWeekdayNight, - # Hour_23=self.HSPRegularWeekdayNight, - # ) - - # hsp_regular_weekend = DayComponent( - # Name="HeatingSetpoint_Regular_Weekend", - # Type="Temperature", - # Hour_00=self.HSPWeekendNight, - # Hour_01=self.HSPWeekendNight, - # Hour_02=self.HSPWeekendNight, - # Hour_03=self.HSPWeekendNight, - # Hour_04=self.HSPWeekendNight, - # Hour_05=self.HSPWeekendNight, - # Hour_06=self.HSPWeekendWorkhours, - # Hour_07=self.HSPWeekendWorkhours, - # Hour_08=self.HSPWeekendWorkhours, - # Hour_09=self.HSPWeekendWorkhours, - # Hour_10=self.HSPWeekendWorkhours, - # Hour_11=self.HSPWeekendWorkhours, - # Hour_12=self.HSPWeekendWorkhours, - # Hour_13=self.HSPWeekendWorkhours, - # Hour_14=self.HSPWeekendWorkhours, - # Hour_15=self.HSPWeekendWorkhours, - # Hour_16=self.HSPWeekendWorkhours, - # Hour_17=self.HSPWeekendWorkhours, - # Hour_18=self.HSPWeekendWorkhours, - # Hour_19=self.HSPWeekendNight, - # Hour_20=self.HSPWeekendNight, - # Hour_21=self.HSPWeekendNight, - # Hour_22=self.HSPWeekendNight, - # Hour_23=self.HSPWeekendNight, - # ) - - # hsp_summer_workday = DayComponent( - # Name="HeatingSetpoint_Summer_Workday", - # Type="Temperature", - # Hour_00=self.HSPSummerWeekdayNight, - # Hour_01=self.HSPSummerWeekdayNight, - # Hour_02=self.HSPSummerWeekdayNight, - # Hour_03=self.HSPSummerWeekdayNight, - # Hour_04=self.HSPSummerWeekdayNight, - # Hour_05=self.HSPSummerWeekdayNight, - # Hour_06=self.HSPSummerWeekdayWorkhours, - # Hour_07=self.HSPSummerWeekdayWorkhours, - # Hour_08=self.HSPSummerWeekdayWorkhours, - # Hour_09=self.HSPSummerWeekdayWorkhours, - # Hour_10=self.HSPSummerWeekdayWorkhours, - # Hour_11=self.HSPSummerWeekdayWorkhours, - # Hour_12=self.HSPSummerWeekdayWorkhours, - # Hour_13=self.HSPSummerWeekdayWorkhours, - # Hour_14=self.HSPSummerWeekdayWorkhours, - # Hour_15=self.HSPSummerWeekdayWorkhours, - # Hour_16=self.HSPSummerWeekdayWorkhours, - # Hour_17=self.HSPSummerWeekdayWorkhours, - # Hour_18=self.HSPSummerWeekdayWorkhours, - # Hour_19=self.HSPSummerWeekdayNight, - # Hour_20=self.HSPSummerWeekdayNight, - # Hour_21=self.HSPSummerWeekdayNight, - # Hour_22=self.HSPSummerWeekdayNight, - # Hour_23=self.HSPSummerWeekdayNight, - # ) - - # hsp_regular_week = WeekComponent( - # Name="HeatingSetpoint_Regular_Week", - # Monday=hsp_regular_workday, - # Tuesday=hsp_regular_workday, - # Wednesday=hsp_regular_workday, - # Thursday=hsp_regular_workday, - # Friday=hsp_regular_workday, - # Saturday=hsp_regular_weekend, - # Sunday=hsp_regular_weekend, - # ) - - # hsp_summer_week = WeekComponent( - # Name="HeatingSetpoint_Summer_Week", - # Monday=hsp_summer_workday, - # Tuesday=hsp_summer_workday, - # Wednesday=hsp_summer_workday, - # Thursday=hsp_summer_workday, - # Friday=hsp_summer_workday, - # Saturday=hsp_regular_weekend, - # Sunday=hsp_regular_weekend, - # ) - - # hsp_year = YearComponent( - # Name="HeatingSetpoint_Schedule", - # Type="Setpoint", - # January=hsp_regular_week, - # February=hsp_regular_week, - # March=hsp_regular_week, - # April=hsp_regular_week, - # May=hsp_regular_week, - # June=hsp_summer_week, - # July=hsp_summer_week, - # August=hsp_summer_week, - # September=hsp_regular_week, - # October=hsp_regular_week, - # November=hsp_regular_week, - # December=hsp_regular_week, - # ) - - # csp_regular_workday = DayComponent( - # Name="CoolingSetpoint_Regular_Workday", - # Type="Temperature", - # Hour_00=self.CSPRegularWeekdayNight, - # Hour_01=self.CSPRegularWeekdayNight, - # Hour_02=self.CSPRegularWeekdayNight, - # Hour_03=self.CSPRegularWeekdayNight, - # Hour_04=self.CSPRegularWeekdayNight, - # Hour_05=self.CSPRegularWeekdayNight, - # Hour_06=self.CSPRegularWeekdayWorkhours, - # Hour_07=self.CSPRegularWeekdayWorkhours, - # Hour_08=self.CSPRegularWeekdayWorkhours, - # Hour_09=self.CSPRegularWeekdayWorkhours, - # Hour_10=self.CSPRegularWeekdayWorkhours, - # Hour_11=self.CSPRegularWeekdayWorkhours, - # Hour_12=self.CSPRegularWeekdayWorkhours, - # Hour_13=self.CSPRegularWeekdayWorkhours, - # Hour_14=self.CSPRegularWeekdayWorkhours, - # Hour_15=self.CSPRegularWeekdayWorkhours, - # Hour_16=self.CSPRegularWeekdayWorkhours, - # Hour_17=self.CSPRegularWeekdayWorkhours, - # Hour_18=self.CSPRegularWeekdayWorkhours, - # Hour_19=self.CSPRegularWeekdayNight, - # Hour_20=self.CSPRegularWeekdayNight, - # Hour_21=self.CSPRegularWeekdayNight, - # Hour_22=self.CSPRegularWeekdayNight, - # Hour_23=self.CSPRegularWeekdayNight, - # ) - - # csp_regular_weekend = DayComponent( - # Name="CoolingSetpoint_Regular_Weekend", - # Type="Temperature", - # Hour_00=self.CSPWeekendNight, - # Hour_01=self.CSPWeekendNight, - # Hour_02=self.CSPWeekendNight, - # Hour_03=self.CSPWeekendNight, - # Hour_04=self.CSPWeekendNight, - # Hour_05=self.CSPWeekendNight, - # Hour_06=self.CSPWeekendWorkhours, - # Hour_07=self.CSPWeekendWorkhours, - # Hour_08=self.CSPWeekendWorkhours, - # Hour_09=self.CSPWeekendWorkhours, - # Hour_10=self.CSPWeekendWorkhours, - # Hour_11=self.CSPWeekendWorkhours, - # Hour_12=self.CSPWeekendWorkhours, - # Hour_13=self.CSPWeekendWorkhours, - # Hour_14=self.CSPWeekendWorkhours, - # Hour_15=self.CSPWeekendWorkhours, - # Hour_16=self.CSPWeekendWorkhours, - # Hour_17=self.CSPWeekendWorkhours, - # Hour_18=self.CSPWeekendWorkhours, - # Hour_19=self.CSPWeekendNight, - # Hour_20=self.CSPWeekendNight, - # Hour_21=self.CSPWeekendNight, - # Hour_22=self.CSPWeekendNight, - # Hour_23=self.CSPWeekendNight, - # ) - - # csp_summer_workday = DayComponent( - # Name="CoolingSetpoint_Summer_Workday", - # Type="Temperature", - # Hour_00=self.CSPSummerWeekdayNight, - # Hour_01=self.CSPSummerWeekdayNight, - # Hour_02=self.CSPSummerWeekdayNight, - # Hour_03=self.CSPSummerWeekdayNight, - # Hour_04=self.CSPSummerWeekdayNight, - # Hour_05=self.CSPSummerWeekdayNight, - # Hour_06=self.CSPSummerWeekdayWorkhours, - # Hour_07=self.CSPSummerWeekdayWorkhours, - # Hour_08=self.CSPSummerWeekdayWorkhours, - # Hour_09=self.CSPSummerWeekdayWorkhours, - # Hour_10=self.CSPSummerWeekdayWorkhours, - # Hour_11=self.CSPSummerWeekdayWorkhours, - # Hour_12=self.CSPSummerWeekdayWorkhours, - # Hour_13=self.CSPSummerWeekdayWorkhours, - # Hour_14=self.CSPSummerWeekdayWorkhours, - # Hour_15=self.CSPSummerWeekdayWorkhours, - # Hour_16=self.CSPSummerWeekdayWorkhours, - # Hour_17=self.CSPSummerWeekdayWorkhours, - # Hour_18=self.CSPSummerWeekdayWorkhours, - # Hour_19=self.CSPSummerWeekdayNight, - # Hour_20=self.CSPSummerWeekdayNight, - # Hour_21=self.CSPSummerWeekdayNight, - # Hour_22=self.CSPSummerWeekdayNight, - # Hour_23=self.CSPSummerWeekdayNight, - # ) - - # csp_regular_week = WeekComponent( - # Name="CoolingSetpoint_Regular_Week", - # Monday=csp_regular_workday, - # Tuesday=csp_regular_workday, - # Wednesday=csp_regular_workday, - # Thursday=csp_regular_workday, - # Friday=csp_regular_workday, - # Saturday=csp_regular_weekend, - # Sunday=csp_regular_weekend, - # ) - - # csp_summer_week = WeekComponent( - # Name="CoolingSetpoint_Summer_Week", - # Monday=csp_summer_workday, - # Tuesday=csp_summer_workday, - # Wednesday=csp_summer_workday, - # Thursday=csp_summer_workday, - # Friday=csp_summer_workday, - # Saturday=csp_regular_weekend, - # Sunday=csp_regular_weekend, - # ) - - # csp_year = YearComponent( - # Name="CoolingSetpoint_Schedule", - # Type="Setpoint", - # January=csp_regular_week, - # February=csp_regular_week, - # March=csp_regular_week, - # April=csp_regular_week, - # May=csp_regular_week, - # June=csp_summer_week, - # July=csp_summer_week, - # August=csp_summer_week, - # September=csp_regular_week, - # October=csp_regular_week, - # November=csp_regular_week, - # December=csp_regular_week, - # ) - setpoint_parametric = ParametricSetpoints( HeatingSetpoint=self.HeatingSetpointBase, DeadBand=self.SetpointDeadband, @@ -1705,16 +802,19 @@ def to_zone(self) -> ZoneComponent: CoolingSchedule=csp_year, ) + by_building_scaling_factor = ( + 1.0 if self.ZoningMode != "by_building" else self.NFloors + ) equipment = EquipmentComponent( Name="Equipment", - PowerDensity=self.EquipmentPowerDensity, + PowerDensity=self.EquipmentPowerDensity * by_building_scaling_factor, Schedule=equipment_schedule, IsOn=True, ) lighting = LightingComponent( Name="Lighting", - PowerDensity=self.LightingPowerDensity, + PowerDensity=self.LightingPowerDensity * by_building_scaling_factor, Schedule=lighting_schedule, IsOn=True, DimmingType="Off", @@ -1722,7 +822,7 @@ def to_zone(self) -> ZoneComponent: occupancy = OccupancyComponent( Name="Occupancy", - PeopleDensity=self.OccupantDensity, + PeopleDensity=self.OccupantDensity * by_building_scaling_factor, Schedule=occupancy_schedule, IsOn=True, ) @@ -1823,7 +923,7 @@ def to_zone(self) -> ZoneComponent: ventilation_system = VentilationComponent( Name="VentilationSystem", - FreshAirPerFloorArea=self.VentFlowRatePerArea, + FreshAirPerFloorArea=self.VentFlowRatePerArea * by_building_scaling_factor, FreshAirPerPerson=self.VentFlowRatePerPerson, Provider=self.VentProvider, # TODO: should hrv sensible/latent efficiency be configurable? (e.g. high/medium/low) @@ -1879,194 +979,73 @@ def to_zone(self) -> ZoneComponent: AFNAirMassFlowCoefficientCrack=0.0, FlowPerExteriorSurfaceArea=0.0, ) - - # TODO: verify interior/exterior - # TODO: are we okaky with mass assumptions? - facade = ConstructionAssemblyComponent( - Name="Facade", - Type="Facade", - Layers=[ - ConstructionLayerComponent( - ConstructionMaterial=clay_brick, - Thickness=0.002, - LayerOrder=0, - ), - ConstructionLayerComponent( - ConstructionMaterial=concrete_block_h, - Thickness=0.15, - LayerOrder=1, - ), - ConstructionLayerComponent( - ConstructionMaterial=fiberglass_batts, - Thickness=0.05, - LayerOrder=2, - ), - ConstructionLayerComponent( - ConstructionMaterial=gypsum_board, - Thickness=0.015, - LayerOrder=3, - ), - ], - ) - - facade_r_value_without_fiberglass = ( - facade.r_value - facade.sorted_layers[2].r_value - ) - - facade_r_value_delta = self.FacadeRValue - facade_r_value_without_fiberglass - required_fiberglass_thickness = ( - fiberglass_batts.Conductivity * facade_r_value_delta + basement_infiltration = InfiltrationComponent( + Name="BasementInfiltration", + IsOn=True, + CalculationMethod="AirChanges/Hour", + AirChangesPerHour=self.BasementInfiltrationACH, + ConstantCoefficient=0.0, + TemperatureCoefficient=0.0, + WindVelocityCoefficient=0.0, + WindVelocitySquaredCoefficient=0.0, + AFNAirMassFlowCoefficientCrack=0.0, + FlowPerExteriorSurfaceArea=0.0, ) - if required_fiberglass_thickness < 0.003: - msg = f"Required Facade Fiberglass thickness is less than 3mm because the desired total facade R-value is {self.FacadeRValue} m²K/W but the concrete and gypsum layers already have a total R-value of {facade_r_value_without_fiberglass} m²K/W." - raise ValueError(msg) - - facade.sorted_layers[2].Thickness = required_fiberglass_thickness - - roof = ConstructionAssemblyComponent( - Name="Roof", - Type="FlatRoof", - Layers=[ - ConstructionLayerComponent( - ConstructionMaterial=xps_board, - Thickness=0.1, - LayerOrder=0, - ), - ConstructionLayerComponent( - ConstructionMaterial=concrete_mc_light, - Thickness=0.15, - LayerOrder=1, - ), - ConstructionLayerComponent( - ConstructionMaterial=concrete_rc_dense, - Thickness=0.2, - LayerOrder=2, - ), - ConstructionLayerComponent( - ConstructionMaterial=gypsum_board, - Thickness=0.02, - LayerOrder=3, - ), - ], + facade = WallFramingSystems[self.FacadeFramingSystem].to_construction_assembly( + cav_insul_rval=self.FacadeCavityInsulationRValue, + ext_insul_rval=self.FacadeExteriorInsulationRValue, + int_insul_rval=self.FacadeInteriorInsulationRValue, + ext_insul_name=self.FacadeExteriorInsulationMaterial, + int_insul_name=self.FacadeInteriorInsulationMaterial, + cav_insul_name=self.FacadeCavityInsulationMaterial, + ext_finish=self.FacadeExteriorFinish, + int_finish=self.FacadeInteriorFinish, ) - - roof_r_value_without_xps = roof.r_value - roof.sorted_layers[0].r_value - roof_r_value_delta = self.RoofRValue - roof_r_value_without_xps - required_xps_thickness = xps_board.Conductivity * roof_r_value_delta - if required_xps_thickness < 0.003: - msg = f"Required Roof XPS thickness is less than 3mm because the desired total roof R-value is {self.RoofRValue} m²K/W but the concrete layers already have a total R-value of {roof_r_value_without_xps} m²K/W." - raise ValueError(msg) - - roof.sorted_layers[0].Thickness = required_xps_thickness - - partition = ConstructionAssemblyComponent( - Name="Partition", - 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, - ), - ], + ground_wall = WallFramingSystems[ + self.SubterraneanWallFramingSystem + ].to_construction_assembly( + cav_insul_rval=self.SubterraneanCavityInsulationRValue, + ext_insul_rval=self.SubterraneanExteriorInsulationRValue, + int_insul_rval=self.SubterraneanInteriorInsulationRValue, + ext_insul_name=self.SubterraneanExteriorInsulationMaterial, + int_insul_name=self.SubterraneanInteriorInsulationMaterial, + cav_insul_name=self.SubterraneanCavityInsulationMaterial, + ext_finish=self.SubterraneanExteriorFinish, + int_finish=self.SubterraneanInteriorFinish, ) - - floor_ceiling = ConstructionAssemblyComponent( - Name="FloorCeiling", - 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, - ), - ], + ground_slab = GroundSlabSystems[self.GroundSlabSystem].to_construction_assembly( + insul_name=self.GroundSlabInsulationMaterial, + insul_rval=self.GroundSlabInsulationRValue, + structural_thickness=self.GroundSlabStructuralThickness, + interior_finish=self.GroundSlabInteriorFinish, ) - ground_slab_assembly = ConstructionAssemblyComponent( - Name="GroundSlabAssembly", - Type="GroundSlab", - Layers=[ - ConstructionLayerComponent( - ConstructionMaterial=xps_board, - Thickness=0.02, - LayerOrder=0, - ), - ConstructionLayerComponent( - ConstructionMaterial=concrete_rc_dense, - Thickness=0.15, - LayerOrder=1, - ), - ConstructionLayerComponent( - ConstructionMaterial=concrete_mc_light, - Thickness=0.04, - LayerOrder=2, - ), - ConstructionLayerComponent( - ConstructionMaterial=cement_mortar, - Thickness=0.03, - LayerOrder=3, - ), - ConstructionLayerComponent( - ConstructionMaterial=ceramic_tile, - Thickness=0.02, - LayerOrder=4, - ), - ], - ) + roof = build_roof_assembly(r_value=self.RoofRValue) - ground_slab_r_value_without_xps = ( - ground_slab_assembly.r_value - ground_slab_assembly.sorted_layers[0].r_value - ) - ground_slab_r_value_delta = self.SlabRValue - ground_slab_r_value_without_xps - required_xps_thickness = xps_board.Conductivity * ground_slab_r_value_delta - if required_xps_thickness < 0.003: - msg = f"Required Ground Slab XPS thickness is less than 3mm because the desired total slab R-value is {self.SlabRValue} m²K/W but the concrete layers already have a total R-value of {ground_slab_r_value_without_xps} m²K/W." - raise ValueError(msg) + partition = build_partition_assembly() - ground_slab_assembly.sorted_layers[0].Thickness = required_xps_thickness + floor_ceiling = build_floor_ceiling_assembly() assemblies = EnvelopeAssemblyComponent( Name="EnvelopeAssemblies", + GroundWallAssembly=ground_wall, FacadeAssembly=facade, + GroundSlabAssembly=ground_slab, + # TODO: Basement ceiling assembly should be configurable; see mabi retrofits + BasementCeilingAssembly=floor_ceiling, + # TODO: attic roof assembly should be configurable; see mabi retrofits FlatRoofAssembly=roof, AtticRoofAssembly=roof, + AtticFloorAssembly=floor_ceiling, + # Unused, no overhangs + ExternalFloorAssembly=ground_slab, + # Constant PartitionAssembly=partition, + # Constant FloorCeilingAssembly=floor_ceiling, - AtticFloorAssembly=floor_ceiling, - BasementCeilingAssembly=floor_ceiling, - GroundSlabAssembly=ground_slab_assembly, - GroundWallAssembly=ground_slab_assembly, - ExternalFloorAssembly=ground_slab_assembly, ) - basement_infiltration = infiltration.model_copy(deep=True) envelope = ZoneEnvelopeComponent( Name="Envelope", AtticInfiltration=infiltration, @@ -2086,20 +1065,39 @@ def to_zone(self) -> ZoneComponent: def to_model(self) -> tuple[Model, Callable[[IDF], IDF]]: """Returns a tuple of a Model and a post-geometry callback.""" - zone = self.to_zone() # TODO: add in a shading mask + zone = self.to_zone() + perim_depth = 3 + effective_zoning_mode = ( + self.ZoningMode if self.ZoningMode != "by_building" else "by_storey" + ) + if effective_zoning_mode == "auto": + if (self.Width > (2 * perim_depth + 3)) and ( + self.Depth > (2 * perim_depth + 3) + ): + # Both the core and perim are large enough to support core/perim zoning + effective_zoning_mode = "core/perim" + else: + effective_zoning_mode = "by_storey" + effective_f2f_height = ( + self.F2FHeight + if effective_zoning_mode != "by_building" + else self.F2FHeight * self.NFloors + ) + geometry = ShoeboxGeometry( x=0, y=0, w=self.Width, d=self.Depth, - h=self.F2FHeight, + h=effective_f2f_height, num_stories=self.NFloors, - # TODO: should core/perim be dependent on width, depth > 9m? - zoning="core/perim", + zoning=effective_zoning_mode, roof_height=None, + perim_depth=3, wwr=self.WWR, basement=False, + exposed_basement_frac=0, ) def post_geometry_callback(idf: IDF) -> IDF: @@ -2110,6 +1108,7 @@ def post_geometry_callback(idf: IDF) -> IDF: Model( geometry=geometry, Zone=zone, + # TODO: enable attic and basement assumptions Attic=AtticAssumptions( UseFraction=None, Conditioned=False, @@ -2148,13 +1147,39 @@ def simulate( Rotation=45, WWR=0.3, NFloors=2, - FacadeRValue=3.0, + FacadeFramingSystem="2x4 16OCC Woodframe", + # Facade + FacadeCavityInsulationRValue=1.2, + FacadeExteriorInsulationRValue=1.0, + FacadeInteriorInsulationRValue=0.0, + FacadeExteriorInsulationMaterial="XPSBoard", + FacadeInteriorInsulationMaterial="XPSBoard", + FacadeCavityInsulationMaterial="FiberglassBatt", + FacadeInteriorFinish="drywall", + FacadeExteriorFinish="wood_siding", + # Subterranean + SubterraneanWallFramingSystem="2x4 16OCC Woodframe", + SubterraneanCavityInsulationRValue=1.2, + SubterraneanExteriorInsulationRValue=1.0, + SubterraneanInteriorInsulationRValue=0.0, + SubterraneanExteriorInsulationMaterial="XPSBoard", + SubterraneanInteriorInsulationMaterial="XPSBoard", + SubterraneanCavityInsulationMaterial="FiberglassBatt", + SubterraneanInteriorFinish="drywall", + SubterraneanExteriorFinish="wood_siding", + # GroundSlab + GroundSlabSystem="BasicConcrete", + GroundSlabInsulationRValue=1.5, + GroundSlabInsulationMaterial="XPSBoard", + GroundSlabInteriorFinish="wood_floor", + GroundSlabStructuralThickness=0.15, + # Roof RoofRValue=3.0, - SlabRValue=3.0, WindowUValue=3.0, WindowSHGF=0.7, WindowTVis=0.5, InfiltrationACH=0.5, + BasementInfiltrationACH=0.5, VentFlowRatePerArea=0.001, VentFlowRatePerPerson=0.0085, VentProvider="Mechanical", @@ -2186,18 +1211,6 @@ def simulate( OccupancyPMInterp=0.5, OccupancyWeekendPeakInterp=0.15, OccupancySummerPeakInterp=0.85, - # HSPRegularWeekdayWorkhours=21, - # HSPRegularWeekdayNight=21, - # HSPSummerWeekdayWorkhours=21, - # HSPSummerWeekdayNight=21, - # HSPWeekendWorkhours=21, - # HSPWeekendNight=21, - # CSPRegularWeekdayWorkhours=23, - # CSPRegularWeekdayNight=23, - # CSPSummerWeekdayWorkhours=23, - # CSPSummerWeekdayNight=23, - # CSPWeekendWorkhours=23, - # CSPWeekendNight=23, HeatingSetpointBase=21, SetpointDeadband=2, HeatingSetpointSetback=2,