openph 0.1.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- openph/__init__.py +11 -0
- openph/from_HBJSON/__init__.py +9 -0
- openph/from_HBJSON/create_phpp.py +648 -0
- openph/model/__init__.py +0 -0
- openph/model/areas.py +452 -0
- openph/model/climate.py +793 -0
- openph/model/components.py +42 -0
- openph/model/constants.py +30 -0
- openph/model/constructions.py +47 -0
- openph/model/enums.py +49 -0
- openph/model/envelope.py +642 -0
- openph/model/hvac/__init__.py +0 -0
- openph/model/hvac/hot_water.py +16 -0
- openph/model/hvac/hvac.py +25 -0
- openph/model/hvac/ventilation_device.py +1253 -0
- openph/model/hvac/ventilation_system.py +471 -0
- openph/model/ihg.py +53 -0
- openph/model/loads/__init__.py +0 -0
- openph/model/loads/infiltration.py +44 -0
- openph/model/loads/ventilation.py +15 -0
- openph/model/programs/__init__.py +0 -0
- openph/model/programs/ventilation.py +24 -0
- openph/model/rooms.py +314 -0
- openph/model/schedules/__init__.py +0 -0
- openph/model/schedules/collection.py +81 -0
- openph/model/schedules/occupancy.py +15 -0
- openph/model/schedules/ventilation.py +98 -0
- openph/model/set_points.py +13 -0
- openph/phpp.py +99 -0
- openph/table_views/__init__.py +48 -0
- openph/table_views/areas.py +239 -0
- openph/table_views/climate.py +192 -0
- openph/table_views/cooling_demand.py +1184 -0
- openph/table_views/ground.py +83 -0
- openph/table_views/heating_demand.py +241 -0
- openph/table_views/rooms.py +119 -0
- openph/table_views/ventilation.py +419 -0
- openph-0.1.0.dist-info/METADATA +742 -0
- openph-0.1.0.dist-info/RECORD +40 -0
- openph-0.1.0.dist-info/WHEEL +4 -0
openph/__init__.py
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
"""Open-PH: Passive House energy processing toolkit.
|
|
2
|
+
|
|
3
|
+
This package provides exact PHPP (Passive House Planning Package) calculation
|
|
4
|
+
replication in Python, with complete transparency and validation capabilities.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from openph.from_HBJSON.create_phpp import from_phx_variant
|
|
8
|
+
from openph.phpp import OpPhPHPP
|
|
9
|
+
|
|
10
|
+
__version__ = "0.1.0"
|
|
11
|
+
__all__ = ["OpPhPHPP", "from_phx_variant"]
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
"""Input processing for Open-PH models from external sources.
|
|
2
|
+
|
|
3
|
+
This module provides functions to convert external building models
|
|
4
|
+
(HBJSON, PHX) into Open-PH data structures.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from .create_phpp import from_phx_variant
|
|
8
|
+
|
|
9
|
+
__all__ = ["from_phx_variant"]
|
|
@@ -0,0 +1,648 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
# -*- Python Version: 3.10 -*-
|
|
3
|
+
|
|
4
|
+
import math
|
|
5
|
+
|
|
6
|
+
from PHX.model.components import PhxApertureElement, PhxComponentOpaque
|
|
7
|
+
from PHX.model.constructions import (
|
|
8
|
+
PhxConstructionOpaque,
|
|
9
|
+
PhxConstructionWindow,
|
|
10
|
+
PhxWindowFrameElement,
|
|
11
|
+
)
|
|
12
|
+
from PHX.model.enums.hvac import PhxVentDuctType
|
|
13
|
+
from PHX.model.geometry import PhxPolygon, PhxPolygonRectangular
|
|
14
|
+
from PHX.model.hvac.ducting import PhxDuctElement
|
|
15
|
+
from PHX.model.hvac.ventilation import AnyPhxVentilation
|
|
16
|
+
from PHX.model.loads.ventilation import PhxLoadVentilation
|
|
17
|
+
from PHX.model.phx_site import PhxClimatePeakLoad
|
|
18
|
+
from PHX.model.project import PhxVariant
|
|
19
|
+
from PHX.model.schedules.ventilation import PhxScheduleVentilation
|
|
20
|
+
from PHX.model.spaces import PhxSpace
|
|
21
|
+
|
|
22
|
+
from openph.model.areas import OpPhAreas
|
|
23
|
+
from openph.model.climate import (
|
|
24
|
+
OpPhClimate,
|
|
25
|
+
OpPhClimateCalcPeriod,
|
|
26
|
+
OpPhClimatePeakCoolingLoad,
|
|
27
|
+
OpPhClimatePeakHeatingLoad,
|
|
28
|
+
)
|
|
29
|
+
from openph.model.components import OpPhConstructionCollection
|
|
30
|
+
from openph.model.constructions import (
|
|
31
|
+
OpPhConstructionAperture,
|
|
32
|
+
OpPhConstructionOpaque,
|
|
33
|
+
OpPhGlazing,
|
|
34
|
+
OpPhWindowFrameElement,
|
|
35
|
+
)
|
|
36
|
+
from openph.model.enums import ComponentExposureExterior, ComponentFaceType
|
|
37
|
+
from openph.model.envelope import OpPhApertureSurface, OpPhOpaqueSurface
|
|
38
|
+
from openph.model.hvac.hvac import OpPhHVAC
|
|
39
|
+
from openph.model.hvac.ventilation_device import (
|
|
40
|
+
OpPhDuct,
|
|
41
|
+
OpPhDuctType,
|
|
42
|
+
OpPhVentilationDeviceDucting,
|
|
43
|
+
)
|
|
44
|
+
from openph.model.hvac.ventilation_system import (
|
|
45
|
+
OpPhVentilationDevice,
|
|
46
|
+
OpPhVentilationSystem,
|
|
47
|
+
)
|
|
48
|
+
from openph.model.loads.infiltration import OpPhInfiltration
|
|
49
|
+
from openph.model.loads.ventilation import OpPhLoadVentilation
|
|
50
|
+
from openph.model.rooms import OpPhRoom, OpPhRooms
|
|
51
|
+
from openph.model.schedules.collection import OpPhScheduleCollection
|
|
52
|
+
from openph.model.schedules.ventilation import OpPhScheduleVentilation
|
|
53
|
+
from openph.phpp import OpPhPHPP
|
|
54
|
+
|
|
55
|
+
# ----------------------------------------------------------------------------------------------------------------------
|
|
56
|
+
# -- Errors
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
class VentilationDuctError(Exception):
|
|
60
|
+
def __init__(self, _ducts: list[PhxDuctElement], _duct_type: str):
|
|
61
|
+
self.msg = f"\nError: There are {len(_ducts)} {_duct_type} ducts assigned to the Ventilator. Expected 1?"
|
|
62
|
+
super().__init__(self.msg)
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
# ----------------------------------------------------------------------------------------------------------------------
|
|
66
|
+
# -- Climate
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
def create_ph_energy_peak_heating_load(
|
|
70
|
+
_ph_energy_phpp: OpPhPHPP, _phx_climate_peak_load: PhxClimatePeakLoad
|
|
71
|
+
) -> OpPhClimatePeakHeatingLoad:
|
|
72
|
+
"""Create a new OpPhClimatePeakHeatingLoad object from a PHX Climate."""
|
|
73
|
+
obj = OpPhClimatePeakHeatingLoad(
|
|
74
|
+
phpp=_ph_energy_phpp,
|
|
75
|
+
period_number=1,
|
|
76
|
+
_radiation_north_kwh_m2=_phx_climate_peak_load.radiation_north or 0.0,
|
|
77
|
+
_radiation_east_kwh_m2=_phx_climate_peak_load.radiation_east or 0.0,
|
|
78
|
+
_radiation_south_kwh_m2=_phx_climate_peak_load.radiation_south or 0.0,
|
|
79
|
+
_radiation_west_kwh_m2=_phx_climate_peak_load.radiation_west or 0.0,
|
|
80
|
+
_radiation_horizontal_kwh_m2=_phx_climate_peak_load.radiation_global or 0.0,
|
|
81
|
+
)
|
|
82
|
+
obj.temperature_air_c = _phx_climate_peak_load.temperature_air or 0.0
|
|
83
|
+
obj.temperature_sky_c = _phx_climate_peak_load.temperature_sky or 0.0
|
|
84
|
+
obj.temperature_dewpoint_c = _phx_climate_peak_load.temperature_dewpoint or 0.0
|
|
85
|
+
obj.temperature_ground_c = _phx_climate_peak_load.temperature_ground or 0.0
|
|
86
|
+
return obj
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
def create_ph_energy_peak_cooling_load(
|
|
90
|
+
_ph_energy_phpp: OpPhPHPP, _phx_climate_peak_load: PhxClimatePeakLoad
|
|
91
|
+
) -> OpPhClimatePeakCoolingLoad:
|
|
92
|
+
"""Create a new OpPhClimatePeakCoolingLoad object from a PHX Climate."""
|
|
93
|
+
obj = OpPhClimatePeakCoolingLoad(
|
|
94
|
+
phpp=_ph_energy_phpp,
|
|
95
|
+
period_number=1,
|
|
96
|
+
_radiation_north_kwh_m2=_phx_climate_peak_load.radiation_north or 0.0,
|
|
97
|
+
_radiation_east_kwh_m2=_phx_climate_peak_load.radiation_east or 0.0,
|
|
98
|
+
_radiation_south_kwh_m2=_phx_climate_peak_load.radiation_south or 0.0,
|
|
99
|
+
_radiation_west_kwh_m2=_phx_climate_peak_load.radiation_west or 0.0,
|
|
100
|
+
_radiation_horizontal_kwh_m2=_phx_climate_peak_load.radiation_global or 0.0,
|
|
101
|
+
)
|
|
102
|
+
obj.temperature_air_c = _phx_climate_peak_load.temperature_air or 0.0
|
|
103
|
+
obj.temperature_sky_c = _phx_climate_peak_load.temperature_sky or 0.0
|
|
104
|
+
obj.temperature_dewpoint_c = _phx_climate_peak_load.temperature_dewpoint or 0.0
|
|
105
|
+
obj.temperature_ground_c = _phx_climate_peak_load.temperature_ground or 0.0
|
|
106
|
+
return obj
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
def create_ph_energy_climate(
|
|
110
|
+
_ph_energy_phpp: OpPhPHPP, _phx_variant: PhxVariant
|
|
111
|
+
) -> OpPhClimate:
|
|
112
|
+
"""Create a OpPhClimate object from a PhxVariant."""
|
|
113
|
+
|
|
114
|
+
ph_energy_climate = OpPhClimate(_ph_energy_phpp)
|
|
115
|
+
|
|
116
|
+
# -------------------------------------------------------------------------
|
|
117
|
+
# -- Initialize all the calc-steps
|
|
118
|
+
for i, period in enumerate(_phx_variant.site.climate.monthly_hours, start=1):
|
|
119
|
+
period_name, period_length_hours = period
|
|
120
|
+
ph_energy_climate.periods.append(
|
|
121
|
+
OpPhClimateCalcPeriod(
|
|
122
|
+
phpp=_ph_energy_phpp,
|
|
123
|
+
period_number=i,
|
|
124
|
+
display_name=period_name,
|
|
125
|
+
_period_length_hours=period_length_hours,
|
|
126
|
+
)
|
|
127
|
+
)
|
|
128
|
+
|
|
129
|
+
# ------------------------------------------------------------------------
|
|
130
|
+
# -- Set the calc-step data
|
|
131
|
+
for i, period in enumerate(ph_energy_climate.periods):
|
|
132
|
+
# -- Set Climate Radiation
|
|
133
|
+
period.radiation_north_kwh_m2 = _phx_variant.site.climate.radiation_north[i]
|
|
134
|
+
period.radiation_east_kwh_m2 = _phx_variant.site.climate.radiation_east[i]
|
|
135
|
+
period.radiation_south_kwh_m2 = _phx_variant.site.climate.radiation_south[i]
|
|
136
|
+
period.radiation_west_kwh_m2 = _phx_variant.site.climate.radiation_west[i]
|
|
137
|
+
period.radiation_horizontal_kwh_m2 = _phx_variant.site.climate.radiation_global[
|
|
138
|
+
i
|
|
139
|
+
]
|
|
140
|
+
|
|
141
|
+
# -- Set Climate Temperatures
|
|
142
|
+
period.temperature_air_c = _phx_variant.site.climate.temperature_air[i]
|
|
143
|
+
period.temperature_sky_c = _phx_variant.site.climate.temperature_sky[i]
|
|
144
|
+
period.temperature_dewpoint_c = _phx_variant.site.climate.temperature_dewpoint[
|
|
145
|
+
i
|
|
146
|
+
]
|
|
147
|
+
|
|
148
|
+
# -------------------------------------------------------------------------
|
|
149
|
+
# --Calculate and set the period radiation factors for the Monthly Periods
|
|
150
|
+
for period in ph_energy_climate.periods:
|
|
151
|
+
period.calculate_radiation_factors()
|
|
152
|
+
|
|
153
|
+
# -------------------------------------------------------------------------
|
|
154
|
+
# -- Set Peak Load Data for Heating and Cooling
|
|
155
|
+
ph_energy_climate.peak_heating_1 = create_ph_energy_peak_heating_load(
|
|
156
|
+
_ph_energy_phpp, _phx_variant.site.climate.peak_heating_1
|
|
157
|
+
)
|
|
158
|
+
ph_energy_climate.peak_heating_2 = create_ph_energy_peak_heating_load(
|
|
159
|
+
_ph_energy_phpp, _phx_variant.site.climate.peak_heating_2
|
|
160
|
+
)
|
|
161
|
+
ph_energy_climate.peak_cooling_1 = create_ph_energy_peak_cooling_load(
|
|
162
|
+
_ph_energy_phpp, _phx_variant.site.climate.peak_cooling_1
|
|
163
|
+
)
|
|
164
|
+
ph_energy_climate.peak_cooling_2 = create_ph_energy_peak_cooling_load(
|
|
165
|
+
_ph_energy_phpp, _phx_variant.site.climate.peak_cooling_2
|
|
166
|
+
)
|
|
167
|
+
|
|
168
|
+
# -------------------------------------------------------------------------
|
|
169
|
+
# --Calculate and set the period radiation factors for the Peak-Load Periods
|
|
170
|
+
ph_energy_climate.peak_heating_1.calculate_radiation_factors()
|
|
171
|
+
ph_energy_climate.peak_heating_2.calculate_radiation_factors()
|
|
172
|
+
ph_energy_climate.peak_cooling_1.calculate_radiation_factors()
|
|
173
|
+
ph_energy_climate.peak_cooling_2.calculate_radiation_factors()
|
|
174
|
+
|
|
175
|
+
return ph_energy_climate
|
|
176
|
+
|
|
177
|
+
|
|
178
|
+
# ----------------------------------------------------------------------------------------------------------------------
|
|
179
|
+
# -- Envelope
|
|
180
|
+
|
|
181
|
+
|
|
182
|
+
def create_ph_energy_opaque_surface(
|
|
183
|
+
_phx_compo: PhxComponentOpaque,
|
|
184
|
+
_phx_polygon: PhxPolygon,
|
|
185
|
+
_construction: OpPhConstructionOpaque,
|
|
186
|
+
) -> OpPhOpaqueSurface:
|
|
187
|
+
"""Create a OpPhSurface_Opaque object from a PhxComponentOpaque and PhxPolygon."""
|
|
188
|
+
|
|
189
|
+
ph_energy_surface = OpPhOpaqueSurface(
|
|
190
|
+
construction=_construction,
|
|
191
|
+
id_num=_phx_polygon.id_num,
|
|
192
|
+
display_name=_phx_polygon.display_name,
|
|
193
|
+
area_m2=_phx_polygon.area,
|
|
194
|
+
angle_from_horizontal=_phx_polygon.angle_from_horizontal,
|
|
195
|
+
cardinal_orientation_angle=_phx_polygon.cardinal_orientation_angle,
|
|
196
|
+
face_type=ComponentFaceType(_phx_compo.face_type.value),
|
|
197
|
+
exposure_exterior=ComponentExposureExterior(_phx_compo.exposure_exterior.value),
|
|
198
|
+
)
|
|
199
|
+
|
|
200
|
+
return ph_energy_surface
|
|
201
|
+
|
|
202
|
+
|
|
203
|
+
def create_ph_energy_aperture_surface(
|
|
204
|
+
_phx_ap_element: PhxApertureElement,
|
|
205
|
+
_host_surface: OpPhOpaqueSurface,
|
|
206
|
+
_construction: OpPhConstructionAperture,
|
|
207
|
+
) -> OpPhApertureSurface | None:
|
|
208
|
+
"""Create a OpPhSurface_Aperture object from a PhxApertureElement."""
|
|
209
|
+
|
|
210
|
+
if not _phx_ap_element.polygon:
|
|
211
|
+
return None
|
|
212
|
+
|
|
213
|
+
# -- Set all the Open-PH-Aperture data from the PHX-Aperture
|
|
214
|
+
ph_e_ap = OpPhApertureSurface(
|
|
215
|
+
host=_host_surface,
|
|
216
|
+
construction=_construction,
|
|
217
|
+
face_type=ComponentFaceType(_phx_ap_element.host.face_type.value),
|
|
218
|
+
exposure_exterior=ComponentExposureExterior(
|
|
219
|
+
_phx_ap_element.host.exposure_exterior.value
|
|
220
|
+
),
|
|
221
|
+
id_num=_phx_ap_element.polygon.id_num,
|
|
222
|
+
display_name=_phx_ap_element.polygon.display_name,
|
|
223
|
+
)
|
|
224
|
+
ph_e_ap.heat_gain.winter.shading_factor = _phx_ap_element.winter_shading_factor
|
|
225
|
+
ph_e_ap.heat_gain.summer.shading_factor = _phx_ap_element.summer_shading_factor
|
|
226
|
+
|
|
227
|
+
if isinstance(_phx_ap_element.polygon, PhxPolygonRectangular):
|
|
228
|
+
ph_e_ap.height_m = _phx_ap_element.polygon.height
|
|
229
|
+
ph_e_ap.width_m = _phx_ap_element.polygon.width
|
|
230
|
+
else:
|
|
231
|
+
ph_e_ap.height_m = math.sqrt(_phx_ap_element.polygon.area)
|
|
232
|
+
ph_e_ap.width_m = math.sqrt(_phx_ap_element.polygon.area)
|
|
233
|
+
|
|
234
|
+
return ph_e_ap
|
|
235
|
+
|
|
236
|
+
|
|
237
|
+
def create_ph_energy_areas(
|
|
238
|
+
_ph_energy_phpp: OpPhPHPP, _phx_variant: PhxVariant
|
|
239
|
+
) -> OpPhAreas:
|
|
240
|
+
"""Create a OpPhAreas object from a PhxVariant."""
|
|
241
|
+
|
|
242
|
+
if len(_ph_energy_phpp.opaque_constructions) == 0:
|
|
243
|
+
raise ValueError(
|
|
244
|
+
"Make sure that you populate the Opaque Constructions Collection with all the constructions "
|
|
245
|
+
"before trying to create the OpPhAreas object. Use 'create_opaque_construction_collection()'."
|
|
246
|
+
)
|
|
247
|
+
|
|
248
|
+
if len(_ph_energy_phpp.aperture_constructions) == 0:
|
|
249
|
+
raise ValueError(
|
|
250
|
+
"Make sure that you populate the Aperture Constructions Collection with all the constructions "
|
|
251
|
+
"before trying to create the OpPhAreas object. Use 'create_aperture_construction_collection()'."
|
|
252
|
+
)
|
|
253
|
+
|
|
254
|
+
ph_energy_areas = OpPhAreas(_ph_energy_phpp)
|
|
255
|
+
for phx_compo in _phx_variant.building.opaque_components:
|
|
256
|
+
opaque_construction = (
|
|
257
|
+
_ph_energy_phpp.opaque_constructions.get_construction_by_identifier(
|
|
258
|
+
phx_compo.assembly.identifier
|
|
259
|
+
)
|
|
260
|
+
)
|
|
261
|
+
|
|
262
|
+
for phx_polygon in phx_compo.polygons:
|
|
263
|
+
# -----------------------------------------------------------------
|
|
264
|
+
# -- build the opaque surface
|
|
265
|
+
ph_energy_surface = create_ph_energy_opaque_surface(
|
|
266
|
+
phx_compo, phx_polygon, opaque_construction
|
|
267
|
+
)
|
|
268
|
+
ph_energy_areas._opaque_surface_list.append(ph_energy_surface)
|
|
269
|
+
|
|
270
|
+
# -----------------------------------------------------------------
|
|
271
|
+
# -- build the child apertures, if any
|
|
272
|
+
for child_polygon_id in phx_polygon.child_polygon_ids:
|
|
273
|
+
phx_aperture_element = phx_compo.get_aperture_element_by_polygon_id_num(
|
|
274
|
+
child_polygon_id
|
|
275
|
+
)
|
|
276
|
+
window_construction = _ph_energy_phpp.aperture_constructions.get_construction_by_identifier(
|
|
277
|
+
phx_aperture_element.host.window_type.identifier
|
|
278
|
+
)
|
|
279
|
+
|
|
280
|
+
ph_energy_aperture = create_ph_energy_aperture_surface(
|
|
281
|
+
phx_compo.get_aperture_element_by_polygon_id_num(child_polygon_id),
|
|
282
|
+
ph_energy_surface,
|
|
283
|
+
window_construction,
|
|
284
|
+
)
|
|
285
|
+
ph_energy_surface.add_aperture(ph_energy_aperture)
|
|
286
|
+
|
|
287
|
+
return ph_energy_areas
|
|
288
|
+
|
|
289
|
+
|
|
290
|
+
def create_opaque_construction(
|
|
291
|
+
_construction: PhxConstructionOpaque,
|
|
292
|
+
) -> OpPhConstructionOpaque:
|
|
293
|
+
"""Create a new OpPhConstructionOpaque object from a PhxConstructionOpaque."""
|
|
294
|
+
return OpPhConstructionOpaque(
|
|
295
|
+
identifier=_construction.identifier,
|
|
296
|
+
id_num=_construction.id_num,
|
|
297
|
+
display_name=_construction.display_name,
|
|
298
|
+
u_value=_construction.u_value,
|
|
299
|
+
)
|
|
300
|
+
|
|
301
|
+
|
|
302
|
+
def create_opaque_construction_collection(
|
|
303
|
+
_phx_variant: PhxVariant,
|
|
304
|
+
) -> OpPhConstructionCollection[OpPhConstructionOpaque]:
|
|
305
|
+
"""Create a new ConstructionCollection[OpPhConstructionOpaque] object from a PhxVariant."""
|
|
306
|
+
opaque_construction_collection = OpPhConstructionCollection[
|
|
307
|
+
OpPhConstructionOpaque
|
|
308
|
+
]()
|
|
309
|
+
for phx_compo in _phx_variant.building.opaque_components:
|
|
310
|
+
opaque_construction_collection.add_new_construction(
|
|
311
|
+
create_opaque_construction(phx_compo.assembly)
|
|
312
|
+
)
|
|
313
|
+
return opaque_construction_collection
|
|
314
|
+
|
|
315
|
+
|
|
316
|
+
def create_frame_element(
|
|
317
|
+
_phx_frame_element: PhxWindowFrameElement,
|
|
318
|
+
) -> OpPhWindowFrameElement:
|
|
319
|
+
"""Create a new OpPhWindowFrameElement object from a PhxWindowFrameElement."""
|
|
320
|
+
return OpPhWindowFrameElement(
|
|
321
|
+
width=_phx_frame_element.width,
|
|
322
|
+
u_value=_phx_frame_element.u_value,
|
|
323
|
+
psi_glazing=_phx_frame_element.psi_glazing,
|
|
324
|
+
psi_install=_phx_frame_element.psi_install,
|
|
325
|
+
)
|
|
326
|
+
|
|
327
|
+
|
|
328
|
+
def create_aperture_construction(
|
|
329
|
+
_construction: PhxConstructionWindow,
|
|
330
|
+
) -> OpPhConstructionAperture:
|
|
331
|
+
"""Create a new OpPhConstructionAperture object from a PhxConstructionWindow."""
|
|
332
|
+
return OpPhConstructionAperture(
|
|
333
|
+
identifier=_construction.identifier,
|
|
334
|
+
id_num=_construction.id_num,
|
|
335
|
+
display_name=_construction.display_name,
|
|
336
|
+
glazing_type_display_name=_construction.glazing_type_display_name,
|
|
337
|
+
frame_type_display_name=_construction.frame_type_display_name,
|
|
338
|
+
glazing=OpPhGlazing(
|
|
339
|
+
identifier=_construction.identifier,
|
|
340
|
+
id_num=_construction.id_num,
|
|
341
|
+
u_value=_construction.u_value_glass,
|
|
342
|
+
g_value=_construction.glass_g_value,
|
|
343
|
+
),
|
|
344
|
+
frame_top=create_frame_element(_construction.frame_top),
|
|
345
|
+
frame_bottom=create_frame_element(_construction.frame_bottom),
|
|
346
|
+
frame_left=create_frame_element(_construction.frame_left),
|
|
347
|
+
frame_right=create_frame_element(_construction.frame_right),
|
|
348
|
+
)
|
|
349
|
+
|
|
350
|
+
|
|
351
|
+
def create_aperture_construction_collection(
|
|
352
|
+
_phx_variant: PhxVariant,
|
|
353
|
+
) -> OpPhConstructionCollection[OpPhConstructionAperture]:
|
|
354
|
+
"""Create a new ConstructionCollection[OpPhConstructionAperture] object from a PhxVariant."""
|
|
355
|
+
aperture_construction_collection = OpPhConstructionCollection[
|
|
356
|
+
OpPhConstructionAperture
|
|
357
|
+
]()
|
|
358
|
+
for phx_compo in _phx_variant.building.aperture_components:
|
|
359
|
+
aperture_construction_collection.add_new_construction(
|
|
360
|
+
create_aperture_construction(phx_compo.window_type)
|
|
361
|
+
)
|
|
362
|
+
return aperture_construction_collection
|
|
363
|
+
|
|
364
|
+
|
|
365
|
+
# ----------------------------------------------------------------------------------------------------------------------
|
|
366
|
+
# -- Ventilation - Devices
|
|
367
|
+
|
|
368
|
+
|
|
369
|
+
def get_phx_duct_from_list(
|
|
370
|
+
_phx_ducts: list[PhxDuctElement], _duct_type: PhxVentDuctType
|
|
371
|
+
) -> PhxDuctElement:
|
|
372
|
+
"""Find the PHX-Duct of the specified type (Supply | Exhaust) for the designated Ventilator."""
|
|
373
|
+
|
|
374
|
+
phx_ducting = [d for d in _phx_ducts if d.duct_type == _duct_type]
|
|
375
|
+
if len(phx_ducting) != 1:
|
|
376
|
+
raise VentilationDuctError(phx_ducting, _duct_type.name)
|
|
377
|
+
return phx_ducting[0]
|
|
378
|
+
|
|
379
|
+
|
|
380
|
+
def create_ph_en_duct(
|
|
381
|
+
_device: OpPhVentilationDevice, _phx_duct: PhxDuctElement
|
|
382
|
+
) -> OpPhDuct:
|
|
383
|
+
"""Create a new OpPhDuct from a source PhxDuctElement."""
|
|
384
|
+
|
|
385
|
+
return OpPhDuct(
|
|
386
|
+
device=_device,
|
|
387
|
+
length_m=_phx_duct.length_m,
|
|
388
|
+
diameter_mm=_phx_duct.diameter_mm,
|
|
389
|
+
height_mm=_phx_duct.height_mm,
|
|
390
|
+
width_mm=_phx_duct.width_mm,
|
|
391
|
+
insulation_thickness_mm=_phx_duct.insulation_thickness_mm,
|
|
392
|
+
insulation_conductivity_w_mk=_phx_duct.insulation_conductivity_wmk,
|
|
393
|
+
insulation_reflective=_phx_duct.is_reflective,
|
|
394
|
+
duct_type=OpPhDuctType(_phx_duct.duct_type.value),
|
|
395
|
+
)
|
|
396
|
+
|
|
397
|
+
|
|
398
|
+
def create_ph_energy_ventilator_ducting(
|
|
399
|
+
_device: OpPhVentilationDevice,
|
|
400
|
+
_phx_supply_duct: PhxDuctElement,
|
|
401
|
+
_phx_exhaust_duct: PhxDuctElement,
|
|
402
|
+
) -> OpPhVentilationDeviceDucting:
|
|
403
|
+
"""Create a new OpPhVentilationDeviceDucting from source Supply and Exhaust PhxDuctElements."""
|
|
404
|
+
|
|
405
|
+
ducting = OpPhVentilationDeviceDucting(_device)
|
|
406
|
+
ducting.supply_ducting = create_ph_en_duct(_device, _phx_supply_duct)
|
|
407
|
+
ducting.exhaust_ducting = create_ph_en_duct(_device, _phx_exhaust_duct)
|
|
408
|
+
return ducting
|
|
409
|
+
|
|
410
|
+
|
|
411
|
+
def create_ph_energy_ventilator(
|
|
412
|
+
_ph_energy_phpp: OpPhPHPP,
|
|
413
|
+
_phx_ventilator: AnyPhxVentilation,
|
|
414
|
+
_phx_supply_duct: PhxDuctElement,
|
|
415
|
+
_phx_exhaust_duct: PhxDuctElement,
|
|
416
|
+
) -> OpPhVentilationDevice:
|
|
417
|
+
"""Create a new OpPhVentilationDevice (HRV/ERV) from a source PHX-Ventilation Device."""
|
|
418
|
+
|
|
419
|
+
device = OpPhVentilationDevice(
|
|
420
|
+
phpp=_ph_energy_phpp,
|
|
421
|
+
id_num=_phx_ventilator.id_num,
|
|
422
|
+
display_name=_phx_ventilator.display_name,
|
|
423
|
+
quantity=_phx_ventilator.quantity,
|
|
424
|
+
sensible_heat_recovery_effic=_phx_ventilator.params.sensible_heat_recovery,
|
|
425
|
+
latent_heat_recovery_effic=_phx_ventilator.params.latent_heat_recovery,
|
|
426
|
+
electric_efficiency_wh_m3=_phx_ventilator.params.electric_efficiency,
|
|
427
|
+
frost_protection_reqd=_phx_ventilator.params.frost_protection_reqd,
|
|
428
|
+
temperature_c_below_defrost_used=_phx_ventilator.params.temperature_below_defrost_used,
|
|
429
|
+
)
|
|
430
|
+
device.ducting = create_ph_energy_ventilator_ducting(
|
|
431
|
+
device, _phx_supply_duct, _phx_exhaust_duct
|
|
432
|
+
)
|
|
433
|
+
return device
|
|
434
|
+
|
|
435
|
+
|
|
436
|
+
def create_ph_energy_ventilation_system(
|
|
437
|
+
_ph_energy_phpp: OpPhPHPP,
|
|
438
|
+
_phx_variant: PhxVariant,
|
|
439
|
+
) -> OpPhVentilationSystem:
|
|
440
|
+
"""Create a new OpPhMechanicalSystemCollection object from a PhxMechanicalSystemCollection."""
|
|
441
|
+
|
|
442
|
+
ph_energy_vent_system = OpPhVentilationSystem(phpp=_ph_energy_phpp)
|
|
443
|
+
|
|
444
|
+
# -- Get the Mechanical Vent Devices
|
|
445
|
+
for phx_mech_collection in _phx_variant.mech_collections:
|
|
446
|
+
for phx_ventilator in phx_mech_collection.ventilation_devices:
|
|
447
|
+
phx_ducts = [
|
|
448
|
+
d
|
|
449
|
+
for d in phx_mech_collection.vent_ducting
|
|
450
|
+
if phx_ventilator.id_num in d.assigned_vent_unit_ids
|
|
451
|
+
]
|
|
452
|
+
ph_energy_ventilator = create_ph_energy_ventilator(
|
|
453
|
+
_ph_energy_phpp=_ph_energy_phpp,
|
|
454
|
+
_phx_ventilator=phx_ventilator,
|
|
455
|
+
_phx_supply_duct=get_phx_duct_from_list(
|
|
456
|
+
phx_ducts, PhxVentDuctType.SUPPLY
|
|
457
|
+
),
|
|
458
|
+
_phx_exhaust_duct=get_phx_duct_from_list(
|
|
459
|
+
phx_ducts, PhxVentDuctType.EXHAUST
|
|
460
|
+
),
|
|
461
|
+
)
|
|
462
|
+
|
|
463
|
+
ph_energy_vent_system.device_collection.add_new_mech_device(
|
|
464
|
+
str(ph_energy_ventilator.id_num), ph_energy_ventilator
|
|
465
|
+
)
|
|
466
|
+
return ph_energy_vent_system
|
|
467
|
+
|
|
468
|
+
|
|
469
|
+
# ----------------------------------------------------------------------------------------------------------------------
|
|
470
|
+
# -- Program
|
|
471
|
+
|
|
472
|
+
|
|
473
|
+
def create_ventilation_schedule(
|
|
474
|
+
_phx_schedule: PhxScheduleVentilation,
|
|
475
|
+
) -> OpPhScheduleVentilation:
|
|
476
|
+
"""Create a new Ph-Energy Ventilation Schedule from a PHX Ventilation Schedule"""
|
|
477
|
+
|
|
478
|
+
# -- Create the basic object with attributes from PHX
|
|
479
|
+
new_schedule = OpPhScheduleVentilation(
|
|
480
|
+
id_num=_phx_schedule.id_num,
|
|
481
|
+
name=_phx_schedule.name,
|
|
482
|
+
identifier=str(_phx_schedule.identifier),
|
|
483
|
+
operating_hours=_phx_schedule.operating_hours,
|
|
484
|
+
operating_days=_phx_schedule.operating_days,
|
|
485
|
+
operating_weeks=_phx_schedule.operating_weeks,
|
|
486
|
+
holiday_days=_phx_schedule.holiday_days,
|
|
487
|
+
)
|
|
488
|
+
|
|
489
|
+
# -- Transfer the Operating-Period values from PHX
|
|
490
|
+
new_schedule.operating_periods.high.period_operating_hours = (
|
|
491
|
+
_phx_schedule.operating_periods.high.period_operating_hours
|
|
492
|
+
)
|
|
493
|
+
new_schedule.operating_periods.high.period_operation_speed = (
|
|
494
|
+
_phx_schedule.operating_periods.high.period_operation_speed
|
|
495
|
+
)
|
|
496
|
+
new_schedule.operating_periods.basic.period_operating_hours = (
|
|
497
|
+
_phx_schedule.operating_periods.standard.period_operating_hours
|
|
498
|
+
)
|
|
499
|
+
new_schedule.operating_periods.standard.period_operation_speed = (
|
|
500
|
+
_phx_schedule.operating_periods.standard.period_operation_speed
|
|
501
|
+
)
|
|
502
|
+
new_schedule.operating_periods.basic.period_operating_hours = (
|
|
503
|
+
_phx_schedule.operating_periods.basic.period_operating_hours
|
|
504
|
+
)
|
|
505
|
+
new_schedule.operating_periods.basic.period_operation_speed = (
|
|
506
|
+
_phx_schedule.operating_periods.basic.period_operation_speed
|
|
507
|
+
)
|
|
508
|
+
new_schedule.operating_periods.minimum.period_operating_hours = (
|
|
509
|
+
_phx_schedule.operating_periods.minimum.period_operating_hours
|
|
510
|
+
)
|
|
511
|
+
new_schedule.operating_periods.minimum.period_operation_speed = (
|
|
512
|
+
_phx_schedule.operating_periods.minimum.period_operation_speed
|
|
513
|
+
)
|
|
514
|
+
|
|
515
|
+
return new_schedule
|
|
516
|
+
|
|
517
|
+
|
|
518
|
+
def create_ventilation_load(
|
|
519
|
+
_phx_ventilation_load: PhxLoadVentilation,
|
|
520
|
+
) -> OpPhLoadVentilation:
|
|
521
|
+
"""Return a new OpPhLoadVentilation object with values set from the PhxLoadVentilation."""
|
|
522
|
+
return OpPhLoadVentilation(
|
|
523
|
+
supply_airflow_m3_h=_phx_ventilation_load.flow_supply,
|
|
524
|
+
exhaust_airflow_m3_h=_phx_ventilation_load.flow_extract,
|
|
525
|
+
transfer_airflow_m3_h=_phx_ventilation_load.flow_transfer,
|
|
526
|
+
)
|
|
527
|
+
|
|
528
|
+
|
|
529
|
+
# ----------------------------------------------------------------------------------------------------------------------
|
|
530
|
+
# -- Rooms
|
|
531
|
+
|
|
532
|
+
|
|
533
|
+
def create_ph_energy_room(
|
|
534
|
+
_phx_space: PhxSpace,
|
|
535
|
+
_schedule_collection: OpPhScheduleCollection[OpPhScheduleVentilation],
|
|
536
|
+
_ph_en_hvac: OpPhHVAC,
|
|
537
|
+
) -> OpPhRoom:
|
|
538
|
+
"""Create a new OpPhRoom object from a PhxSpace."""
|
|
539
|
+
new_room = OpPhRoom()
|
|
540
|
+
new_room.quantity = _phx_space.quantity
|
|
541
|
+
new_room.display_name = _phx_space.display_name
|
|
542
|
+
new_room.floor_area_m2 = _phx_space.floor_area
|
|
543
|
+
try:
|
|
544
|
+
new_room.weighting_factor = (
|
|
545
|
+
_phx_space.weighted_floor_area / _phx_space.floor_area
|
|
546
|
+
)
|
|
547
|
+
except ZeroDivisionError:
|
|
548
|
+
new_room.weighting_factor = 0.0
|
|
549
|
+
new_room.net_volume_m3 = _phx_space.net_volume
|
|
550
|
+
|
|
551
|
+
# -- Assign Ventilator Device
|
|
552
|
+
new_room.vent_unit = (
|
|
553
|
+
_ph_en_hvac.ventilation_system.device_collection.get_device_by_key(
|
|
554
|
+
str(_phx_space.vent_unit_id_num)
|
|
555
|
+
)
|
|
556
|
+
)
|
|
557
|
+
|
|
558
|
+
# -- Build the Room's Ventilation Program
|
|
559
|
+
new_room.ventilation.display_name = _phx_space.ventilation.display_name
|
|
560
|
+
new_room.ventilation.schedule = _schedule_collection.get_schedule_by_key(
|
|
561
|
+
str(_phx_space.ventilation.schedule.identifier)
|
|
562
|
+
)
|
|
563
|
+
new_room.ventilation.load = create_ventilation_load(_phx_space.ventilation.load)
|
|
564
|
+
|
|
565
|
+
# TODO: Build Occupancy Program
|
|
566
|
+
|
|
567
|
+
# TODO: Build Lighting Program
|
|
568
|
+
|
|
569
|
+
return new_room
|
|
570
|
+
|
|
571
|
+
|
|
572
|
+
def create_ph_energy_rooms(
|
|
573
|
+
_ph_energy_phpp: OpPhPHPP, _phx_variant: PhxVariant
|
|
574
|
+
) -> OpPhRooms:
|
|
575
|
+
"""Create a OpPhRooms object from a PhxVariant's 'Zones'."""
|
|
576
|
+
ph_energy_rooms = OpPhRooms(_ph_energy_phpp)
|
|
577
|
+
for phx_zone in _phx_variant.building.zones:
|
|
578
|
+
for phx_space in phx_zone.spaces:
|
|
579
|
+
ph_energy_rooms.add_room(
|
|
580
|
+
create_ph_energy_room(
|
|
581
|
+
phx_space,
|
|
582
|
+
_ph_energy_phpp.schedules,
|
|
583
|
+
_ph_energy_phpp.hvac,
|
|
584
|
+
)
|
|
585
|
+
)
|
|
586
|
+
|
|
587
|
+
return ph_energy_rooms
|
|
588
|
+
|
|
589
|
+
|
|
590
|
+
def create_schedules(_phx_variant: PhxVariant) -> list[OpPhScheduleVentilation]:
|
|
591
|
+
"""Return a list of each unique Schedule object found on the PHX model's Spaces."""
|
|
592
|
+
schedules = {}
|
|
593
|
+
for phx_zone in _phx_variant.building.zones:
|
|
594
|
+
for phx_space in phx_zone.spaces:
|
|
595
|
+
# TODO: Create new OpPh Objects from the PHX Schedules...
|
|
596
|
+
schedules[str(phx_space.ventilation.schedule.identifier)] = (
|
|
597
|
+
create_ventilation_schedule(phx_space.ventilation.schedule)
|
|
598
|
+
)
|
|
599
|
+
# TODO: handle Occupancy and Lighting Schedules
|
|
600
|
+
# schedules[str(phx_space.occupancy.schedule.identifier)] = \
|
|
601
|
+
# create_new_schedule(phx_space.occupancy.schedule)
|
|
602
|
+
# schedules[str(phx_space.lighting.schedule.identifier)] = \
|
|
603
|
+
# create_new_schedule(phx_space.lighting.schedule)
|
|
604
|
+
return list(schedules.values())
|
|
605
|
+
|
|
606
|
+
|
|
607
|
+
def create_ph_energy_infiltration(
|
|
608
|
+
_ph_energy_phpp: OpPhPHPP, _phx_variant: PhxVariant
|
|
609
|
+
) -> OpPhInfiltration:
|
|
610
|
+
"""Return a new OpPhInfiltration object with props based on a PHX Variant"""
|
|
611
|
+
|
|
612
|
+
return OpPhInfiltration(
|
|
613
|
+
phpp=_ph_energy_phpp,
|
|
614
|
+
wind_coefficient_e=_phx_variant.phius_cert.ph_building_data.wind_coefficient_e,
|
|
615
|
+
wind_coefficient_f=_phx_variant.phius_cert.ph_building_data.wind_coefficient_f,
|
|
616
|
+
airtightness_n50=_phx_variant.phius_cert.ph_building_data.airtightness_n50,
|
|
617
|
+
)
|
|
618
|
+
|
|
619
|
+
|
|
620
|
+
# ----------------------------------------------------------------------------------------------------------------------
|
|
621
|
+
# -- Entry Point
|
|
622
|
+
|
|
623
|
+
|
|
624
|
+
def from_phx_variant(_phx_variant: PhxVariant) -> OpPhPHPP:
|
|
625
|
+
"""Create a new OpPhergyPHPP object from a PhxVariant."""
|
|
626
|
+
|
|
627
|
+
ph_energy_phpp = OpPhPHPP()
|
|
628
|
+
|
|
629
|
+
# -- Setup the PHPP Data
|
|
630
|
+
for program in create_schedules(_phx_variant):
|
|
631
|
+
ph_energy_phpp.schedules.add_new_schedule(program)
|
|
632
|
+
ph_energy_phpp.opaque_constructions = create_opaque_construction_collection(
|
|
633
|
+
_phx_variant
|
|
634
|
+
)
|
|
635
|
+
ph_energy_phpp.aperture_constructions = create_aperture_construction_collection(
|
|
636
|
+
_phx_variant
|
|
637
|
+
)
|
|
638
|
+
ph_energy_phpp.hvac.ventilation_system = create_ph_energy_ventilation_system(
|
|
639
|
+
ph_energy_phpp, _phx_variant
|
|
640
|
+
)
|
|
641
|
+
ph_energy_phpp.climate = create_ph_energy_climate(ph_energy_phpp, _phx_variant)
|
|
642
|
+
ph_energy_phpp.infiltration = create_ph_energy_infiltration(
|
|
643
|
+
ph_energy_phpp, _phx_variant
|
|
644
|
+
)
|
|
645
|
+
ph_energy_phpp.areas = create_ph_energy_areas(ph_energy_phpp, _phx_variant)
|
|
646
|
+
ph_energy_phpp.rooms = create_ph_energy_rooms(ph_energy_phpp, _phx_variant)
|
|
647
|
+
|
|
648
|
+
return ph_energy_phpp
|
openph/model/__init__.py
ADDED
|
File without changes
|