honeybee-energy 1.116.106__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.
- honeybee_energy/__init__.py +24 -0
- honeybee_energy/__main__.py +4 -0
- honeybee_energy/_extend_honeybee.py +145 -0
- honeybee_energy/altnumber.py +21 -0
- honeybee_energy/baseline/__init__.py +2 -0
- honeybee_energy/baseline/create.py +608 -0
- honeybee_energy/baseline/data/__init__.py +1 -0
- honeybee_energy/baseline/data/constructions.csv +64 -0
- honeybee_energy/baseline/data/fen_ratios.csv +15 -0
- honeybee_energy/baseline/data/lpd_building.csv +21 -0
- honeybee_energy/baseline/data/pci_2016.csv +22 -0
- honeybee_energy/baseline/data/pci_2019.csv +22 -0
- honeybee_energy/baseline/data/pci_2022.csv +22 -0
- honeybee_energy/baseline/data/shw.csv +21 -0
- honeybee_energy/baseline/pci.py +512 -0
- honeybee_energy/baseline/result.py +371 -0
- honeybee_energy/boundarycondition.py +128 -0
- honeybee_energy/cli/__init__.py +69 -0
- honeybee_energy/cli/baseline.py +475 -0
- honeybee_energy/cli/edit.py +327 -0
- honeybee_energy/cli/lib.py +1154 -0
- honeybee_energy/cli/result.py +810 -0
- honeybee_energy/cli/setconfig.py +124 -0
- honeybee_energy/cli/settings.py +569 -0
- honeybee_energy/cli/simulate.py +380 -0
- honeybee_energy/cli/translate.py +1714 -0
- honeybee_energy/cli/validate.py +224 -0
- honeybee_energy/config.json +11 -0
- honeybee_energy/config.py +842 -0
- honeybee_energy/construction/__init__.py +1 -0
- honeybee_energy/construction/_base.py +374 -0
- honeybee_energy/construction/air.py +325 -0
- honeybee_energy/construction/dictutil.py +89 -0
- honeybee_energy/construction/dynamic.py +607 -0
- honeybee_energy/construction/opaque.py +460 -0
- honeybee_energy/construction/shade.py +319 -0
- honeybee_energy/construction/window.py +1096 -0
- honeybee_energy/construction/windowshade.py +847 -0
- honeybee_energy/constructionset.py +1655 -0
- honeybee_energy/dictutil.py +56 -0
- honeybee_energy/generator/__init__.py +5 -0
- honeybee_energy/generator/loadcenter.py +204 -0
- honeybee_energy/generator/pv.py +535 -0
- honeybee_energy/hvac/__init__.py +21 -0
- honeybee_energy/hvac/_base.py +124 -0
- honeybee_energy/hvac/_template.py +270 -0
- honeybee_energy/hvac/allair/__init__.py +22 -0
- honeybee_energy/hvac/allair/_base.py +349 -0
- honeybee_energy/hvac/allair/furnace.py +168 -0
- honeybee_energy/hvac/allair/psz.py +131 -0
- honeybee_energy/hvac/allair/ptac.py +163 -0
- honeybee_energy/hvac/allair/pvav.py +109 -0
- honeybee_energy/hvac/allair/vav.py +128 -0
- honeybee_energy/hvac/detailed.py +337 -0
- honeybee_energy/hvac/doas/__init__.py +28 -0
- honeybee_energy/hvac/doas/_base.py +345 -0
- honeybee_energy/hvac/doas/fcu.py +127 -0
- honeybee_energy/hvac/doas/radiant.py +329 -0
- honeybee_energy/hvac/doas/vrf.py +81 -0
- honeybee_energy/hvac/doas/wshp.py +91 -0
- honeybee_energy/hvac/heatcool/__init__.py +23 -0
- honeybee_energy/hvac/heatcool/_base.py +177 -0
- honeybee_energy/hvac/heatcool/baseboard.py +61 -0
- honeybee_energy/hvac/heatcool/evapcool.py +72 -0
- honeybee_energy/hvac/heatcool/fcu.py +92 -0
- honeybee_energy/hvac/heatcool/gasunit.py +53 -0
- honeybee_energy/hvac/heatcool/radiant.py +269 -0
- honeybee_energy/hvac/heatcool/residential.py +77 -0
- honeybee_energy/hvac/heatcool/vrf.py +54 -0
- honeybee_energy/hvac/heatcool/windowac.py +70 -0
- honeybee_energy/hvac/heatcool/wshp.py +62 -0
- honeybee_energy/hvac/idealair.py +699 -0
- honeybee_energy/internalmass.py +310 -0
- honeybee_energy/lib/__init__.py +1 -0
- honeybee_energy/lib/_loadconstructions.py +194 -0
- honeybee_energy/lib/_loadconstructionsets.py +117 -0
- honeybee_energy/lib/_loadmaterials.py +83 -0
- honeybee_energy/lib/_loadprogramtypes.py +125 -0
- honeybee_energy/lib/_loadschedules.py +87 -0
- honeybee_energy/lib/_loadtypelimits.py +64 -0
- honeybee_energy/lib/constructions.py +207 -0
- honeybee_energy/lib/constructionsets.py +95 -0
- honeybee_energy/lib/materials.py +67 -0
- honeybee_energy/lib/programtypes.py +125 -0
- honeybee_energy/lib/schedules.py +61 -0
- honeybee_energy/lib/scheduletypelimits.py +31 -0
- honeybee_energy/load/__init__.py +1 -0
- honeybee_energy/load/_base.py +190 -0
- honeybee_energy/load/daylight.py +397 -0
- honeybee_energy/load/dictutil.py +47 -0
- honeybee_energy/load/equipment.py +771 -0
- honeybee_energy/load/hotwater.py +543 -0
- honeybee_energy/load/infiltration.py +460 -0
- honeybee_energy/load/lighting.py +480 -0
- honeybee_energy/load/people.py +497 -0
- honeybee_energy/load/process.py +472 -0
- honeybee_energy/load/setpoint.py +816 -0
- honeybee_energy/load/ventilation.py +550 -0
- honeybee_energy/material/__init__.py +1 -0
- honeybee_energy/material/_base.py +166 -0
- honeybee_energy/material/dictutil.py +59 -0
- honeybee_energy/material/frame.py +367 -0
- honeybee_energy/material/gas.py +1087 -0
- honeybee_energy/material/glazing.py +854 -0
- honeybee_energy/material/opaque.py +1351 -0
- honeybee_energy/material/shade.py +1360 -0
- honeybee_energy/measure.py +472 -0
- honeybee_energy/programtype.py +723 -0
- honeybee_energy/properties/__init__.py +1 -0
- honeybee_energy/properties/aperture.py +333 -0
- honeybee_energy/properties/door.py +342 -0
- honeybee_energy/properties/extension.py +244 -0
- honeybee_energy/properties/face.py +274 -0
- honeybee_energy/properties/model.py +2640 -0
- honeybee_energy/properties/room.py +1747 -0
- honeybee_energy/properties/shade.py +314 -0
- honeybee_energy/properties/shademesh.py +262 -0
- honeybee_energy/reader.py +48 -0
- honeybee_energy/result/__init__.py +1 -0
- honeybee_energy/result/colorobj.py +648 -0
- honeybee_energy/result/emissions.py +290 -0
- honeybee_energy/result/err.py +101 -0
- honeybee_energy/result/eui.py +100 -0
- honeybee_energy/result/generation.py +160 -0
- honeybee_energy/result/loadbalance.py +890 -0
- honeybee_energy/result/match.py +202 -0
- honeybee_energy/result/osw.py +90 -0
- honeybee_energy/result/rdd.py +59 -0
- honeybee_energy/result/zsz.py +190 -0
- honeybee_energy/run.py +1577 -0
- honeybee_energy/schedule/__init__.py +1 -0
- honeybee_energy/schedule/day.py +626 -0
- honeybee_energy/schedule/dictutil.py +59 -0
- honeybee_energy/schedule/fixedinterval.py +1012 -0
- honeybee_energy/schedule/rule.py +619 -0
- honeybee_energy/schedule/ruleset.py +1867 -0
- honeybee_energy/schedule/typelimit.py +310 -0
- honeybee_energy/shw.py +315 -0
- honeybee_energy/simulation/__init__.py +1 -0
- honeybee_energy/simulation/control.py +214 -0
- honeybee_energy/simulation/daylightsaving.py +185 -0
- honeybee_energy/simulation/dictutil.py +51 -0
- honeybee_energy/simulation/output.py +646 -0
- honeybee_energy/simulation/parameter.py +606 -0
- honeybee_energy/simulation/runperiod.py +443 -0
- honeybee_energy/simulation/shadowcalculation.py +295 -0
- honeybee_energy/simulation/sizing.py +546 -0
- honeybee_energy/ventcool/__init__.py +5 -0
- honeybee_energy/ventcool/_crack_data.py +91 -0
- honeybee_energy/ventcool/afn.py +289 -0
- honeybee_energy/ventcool/control.py +269 -0
- honeybee_energy/ventcool/crack.py +126 -0
- honeybee_energy/ventcool/fan.py +493 -0
- honeybee_energy/ventcool/opening.py +365 -0
- honeybee_energy/ventcool/simulation.py +314 -0
- honeybee_energy/writer.py +1078 -0
- honeybee_energy-1.116.106.dist-info/METADATA +113 -0
- honeybee_energy-1.116.106.dist-info/RECORD +162 -0
- honeybee_energy-1.116.106.dist-info/WHEEL +5 -0
- honeybee_energy-1.116.106.dist-info/entry_points.txt +2 -0
- honeybee_energy-1.116.106.dist-info/licenses/LICENSE +661 -0
- honeybee_energy-1.116.106.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,365 @@
|
|
|
1
|
+
# coding=utf-8
|
|
2
|
+
"""Definition of window opening for ventilative cooling."""
|
|
3
|
+
from __future__ import division
|
|
4
|
+
|
|
5
|
+
from .control import VentilationControl
|
|
6
|
+
from ..writer import generate_idf_string
|
|
7
|
+
|
|
8
|
+
from honeybee.typing import float_in_range, float_positive
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class VentilationOpening(object):
|
|
12
|
+
"""Definition of window opening for ventilative cooling.
|
|
13
|
+
|
|
14
|
+
Args:
|
|
15
|
+
fraction_area_operable: A number between 0.0 and 1.0 for the fraction of the
|
|
16
|
+
window area that is operable. (Default: 0.5, typical of sliding windows).
|
|
17
|
+
fraction_height_operable: A number between 0.0 and 1.0 for the fraction
|
|
18
|
+
of the distance from the bottom of the window to the top that is
|
|
19
|
+
operable. (Default: 1.0, typical of windows that slide horizontally).
|
|
20
|
+
discharge_coefficient: A number between 0.0 and 1.0 that will be multiplied
|
|
21
|
+
by the area of the window in the stack (buoyancy-driven) part of the
|
|
22
|
+
equation to account for additional friction from window geometry,
|
|
23
|
+
insect screens, etc. (Default: 0.45, for unobstructed windows with
|
|
24
|
+
insect screens). This value should be lowered if windows are of an
|
|
25
|
+
awning or casement type and not allowed to fully open. Some common
|
|
26
|
+
values for this coefficient include the following.
|
|
27
|
+
|
|
28
|
+
* 0.0 - Completely discount stack ventilation from the calculation.
|
|
29
|
+
* 0.45 - For unobstructed windows with an insect screen.
|
|
30
|
+
* 0.65 - For unobstructed windows with NO insect screen.
|
|
31
|
+
|
|
32
|
+
wind_cross_vent: Boolean to indicate if there is an opening of roughly
|
|
33
|
+
equal area on the opposite side of the Room such that wind-driven
|
|
34
|
+
cross ventilation will be induced. If False, the assumption is that
|
|
35
|
+
the operable area is primarily on one side of the Room and there is
|
|
36
|
+
no wind-driven ventilation. (Default: False)
|
|
37
|
+
flow_coefficient_closed: A number in kg/s-m, at 1 Pa per meter of crack
|
|
38
|
+
length, used to calculate the mass flow rate when the opening is closed;
|
|
39
|
+
only used in an AirflowNetwork simulation. Some common values for this
|
|
40
|
+
coefficient (from the DesignBuilder Cracks template) include the following:
|
|
41
|
+
|
|
42
|
+
* 0.00001 - Tight, low-leakage closed external window
|
|
43
|
+
* 0.003 - Very poor, high-leakage closed external window
|
|
44
|
+
|
|
45
|
+
(Default: 0, indicates the VentilationOpening object will not participate
|
|
46
|
+
in the AirflowNetwork simulation).
|
|
47
|
+
flow_exponent_closed: An optional dimensionless number between 0.5 and
|
|
48
|
+
1 used to calculate the mass flow rate when the opening is closed; required
|
|
49
|
+
to run an AirflowNetwork simulation. This value represents the leak geometry
|
|
50
|
+
impact on airflow, with 0.5 generally corresponding to turbulent orifice flow
|
|
51
|
+
and 1 generally corresponding to laminar flow. (Default: 0.65,
|
|
52
|
+
representative of many cases of wall and window leakage, used when the
|
|
53
|
+
exponent cannot be measured).
|
|
54
|
+
two_way_threshold: A number in kg/m3 indicating the minimum density difference
|
|
55
|
+
above which two-way flow may occur due to stack effect, required to run an
|
|
56
|
+
AirflowNetwork simulation. This value is required because the air density
|
|
57
|
+
difference between two zones (which drives two-way air flow) will tend
|
|
58
|
+
towards division by zero errors as the air density difference approaches
|
|
59
|
+
zero. (Default: 0.0001, typical default value used for AirflowNetwork
|
|
60
|
+
openings).
|
|
61
|
+
|
|
62
|
+
Properties:
|
|
63
|
+
* fraction_area_operable
|
|
64
|
+
* fraction_height_operable
|
|
65
|
+
* discharge_coefficient
|
|
66
|
+
* wind_cross_vent
|
|
67
|
+
* flow_coefficient_closed
|
|
68
|
+
* flow_exponent_closed
|
|
69
|
+
* two_way_threshold
|
|
70
|
+
* parent
|
|
71
|
+
* has_parent
|
|
72
|
+
"""
|
|
73
|
+
__slots__ = ('_fraction_area_operable', '_fraction_height_operable',
|
|
74
|
+
'_discharge_coefficient', '_wind_cross_vent',
|
|
75
|
+
'_flow_coefficient_closed', '_flow_exponent_closed',
|
|
76
|
+
'_two_way_threshold', '_parent')
|
|
77
|
+
|
|
78
|
+
def __init__(self, fraction_area_operable=0.5, fraction_height_operable=1.0,
|
|
79
|
+
discharge_coefficient=0.45, wind_cross_vent=False,
|
|
80
|
+
flow_coefficient_closed=0, flow_exponent_closed=0.65,
|
|
81
|
+
two_way_threshold=0.0001):
|
|
82
|
+
"""Initialize VentilationOpening."""
|
|
83
|
+
self.fraction_area_operable = fraction_area_operable
|
|
84
|
+
self.fraction_height_operable = fraction_height_operable
|
|
85
|
+
self.discharge_coefficient = discharge_coefficient
|
|
86
|
+
self.wind_cross_vent = wind_cross_vent
|
|
87
|
+
self.flow_coefficient_closed = flow_coefficient_closed
|
|
88
|
+
self.flow_exponent_closed = flow_exponent_closed
|
|
89
|
+
self.two_way_threshold = two_way_threshold
|
|
90
|
+
self._parent = None # this will be set when assigned to an aperture
|
|
91
|
+
|
|
92
|
+
@property
|
|
93
|
+
def fraction_area_operable(self):
|
|
94
|
+
"""Get or set a number for the fraction of the window area that is operable."""
|
|
95
|
+
return self._fraction_area_operable
|
|
96
|
+
|
|
97
|
+
@fraction_area_operable.setter
|
|
98
|
+
def fraction_area_operable(self, value):
|
|
99
|
+
self._fraction_area_operable = \
|
|
100
|
+
float_in_range(value, 0.0, 1.0, 'fraction area operable')
|
|
101
|
+
|
|
102
|
+
@property
|
|
103
|
+
def fraction_height_operable(self):
|
|
104
|
+
"""Get or set a number for the fraction of the window height that is operable."""
|
|
105
|
+
return self._fraction_height_operable
|
|
106
|
+
|
|
107
|
+
@fraction_height_operable.setter
|
|
108
|
+
def fraction_height_operable(self, value):
|
|
109
|
+
self._fraction_height_operable = \
|
|
110
|
+
float_in_range(value, 0.0, 1.0, 'fraction height operable')
|
|
111
|
+
|
|
112
|
+
@property
|
|
113
|
+
def discharge_coefficient(self):
|
|
114
|
+
"""Get or set a number for the discharge coefficient."""
|
|
115
|
+
return self._discharge_coefficient
|
|
116
|
+
|
|
117
|
+
@discharge_coefficient.setter
|
|
118
|
+
def discharge_coefficient(self, value):
|
|
119
|
+
self._discharge_coefficient = \
|
|
120
|
+
float_in_range(value, 0.0, 1.0, 'discharge coefficient')
|
|
121
|
+
|
|
122
|
+
@property
|
|
123
|
+
def wind_cross_vent(self):
|
|
124
|
+
"""Get or set a boolean for whether there is cross ventilation from the window.
|
|
125
|
+
|
|
126
|
+
Note that this property only has significance for simulations using SingleZone
|
|
127
|
+
ventilation_simulation_control and has no bearing on multizone simulations
|
|
128
|
+
with the Airflow Network.
|
|
129
|
+
|
|
130
|
+
This should be True if there is an opening of roughly equal area on the
|
|
131
|
+
opposite side of the Room such that wind-driven cross ventilation will
|
|
132
|
+
be induced. If False, the assumption is that the operable area is primarily
|
|
133
|
+
on one side of the Room and there is no wind-driven ventilation.
|
|
134
|
+
"""
|
|
135
|
+
return self._wind_cross_vent
|
|
136
|
+
|
|
137
|
+
@wind_cross_vent.setter
|
|
138
|
+
def wind_cross_vent(self, value):
|
|
139
|
+
self._wind_cross_vent = bool(value)
|
|
140
|
+
|
|
141
|
+
@property
|
|
142
|
+
def flow_coefficient_closed(self):
|
|
143
|
+
"""Get or set a number for the mass flow coefficient when opening is closed [kg/s-m].
|
|
144
|
+
|
|
145
|
+
Note that anything assigned here has no bearing on the simulation unless
|
|
146
|
+
the Model that this object is a part of has its ventilation_simulation_control
|
|
147
|
+
set for MultiZone air flow, thereby triggering the use of the AirflowNetwork.
|
|
148
|
+
"""
|
|
149
|
+
return self._flow_coefficient_closed
|
|
150
|
+
|
|
151
|
+
@flow_coefficient_closed.setter
|
|
152
|
+
def flow_coefficient_closed(self, value):
|
|
153
|
+
self._flow_coefficient_closed = float_positive(value, 'flow_coefficient_closed')
|
|
154
|
+
|
|
155
|
+
@property
|
|
156
|
+
def flow_exponent_closed(self):
|
|
157
|
+
"""Get or set the exponent for deriving the mass flow rate when opening is closed.
|
|
158
|
+
|
|
159
|
+
Note that anything assigned here has no bearing on the simulation unless
|
|
160
|
+
the Model that this object is a part of has its ventilation_simulation_control
|
|
161
|
+
set for MultiZone air flow, thereby triggering the use of the AirflowNetwork.
|
|
162
|
+
"""
|
|
163
|
+
return self._flow_exponent_closed
|
|
164
|
+
|
|
165
|
+
@flow_exponent_closed.setter
|
|
166
|
+
def flow_exponent_closed(self, value):
|
|
167
|
+
self._flow_exponent_closed = \
|
|
168
|
+
float_in_range(value, 0.5, 1.0, 'flow_exponent_closed')
|
|
169
|
+
|
|
170
|
+
@property
|
|
171
|
+
def two_way_threshold(self):
|
|
172
|
+
"""Get or set minimum density difference above which two-way flow occurs [kg/m3].
|
|
173
|
+
|
|
174
|
+
Note that anything assigned here has no bearing on the simulation unless
|
|
175
|
+
the Model that this object is a part of has its ventilation_simulation_control
|
|
176
|
+
set for MultiZone air flow, thereby triggering the use of the AirflowNetwork.
|
|
177
|
+
"""
|
|
178
|
+
return self._two_way_threshold
|
|
179
|
+
|
|
180
|
+
@two_way_threshold.setter
|
|
181
|
+
def two_way_threshold(self, value):
|
|
182
|
+
self._two_way_threshold = float_positive(value, 'two_way_threshold')
|
|
183
|
+
|
|
184
|
+
@property
|
|
185
|
+
def parent(self):
|
|
186
|
+
"""Get the parent of this object if it exists."""
|
|
187
|
+
return self._parent
|
|
188
|
+
|
|
189
|
+
@property
|
|
190
|
+
def has_parent(self):
|
|
191
|
+
"""Get a boolean noting whether this VentilationOpening has a parent object."""
|
|
192
|
+
return self._parent is not None
|
|
193
|
+
|
|
194
|
+
@classmethod
|
|
195
|
+
def from_dict(cls, data):
|
|
196
|
+
"""Create a VentilationOpening object from a dictionary.
|
|
197
|
+
|
|
198
|
+
Args:
|
|
199
|
+
data: A VentilationOpening dictionary in following the format below.
|
|
200
|
+
|
|
201
|
+
.. code-block:: python
|
|
202
|
+
|
|
203
|
+
{
|
|
204
|
+
"type": "VentilationOpening",
|
|
205
|
+
"fraction_area_operable": 0.5, # Fractional number for area operable
|
|
206
|
+
"fraction_height_operable": 0.5, # Fractional number for height operable
|
|
207
|
+
"discharge_coefficient": 0.45, # Fractional number for discharge coefficient
|
|
208
|
+
"wind_cross_vent": True # Wind-driven cross ventilation
|
|
209
|
+
"flow_coefficient_closed": 0.001 # Coefficient for mass flow rate
|
|
210
|
+
"flow_exponent_closed": 0.667 # Exponent for mass flow rate
|
|
211
|
+
"two_way_threshold": 1e-3 # Minimum density for two-way flow
|
|
212
|
+
}
|
|
213
|
+
"""
|
|
214
|
+
assert data['type'] == 'VentilationOpening', \
|
|
215
|
+
'Expected VentilationOpening dictionary. Got {}.'.format(data['type'])
|
|
216
|
+
|
|
217
|
+
area_op = data['fraction_area_operable'] if 'fraction_area_operable' in data \
|
|
218
|
+
and data['fraction_area_operable'] is not None else 0.5
|
|
219
|
+
height_op = data['fraction_height_operable'] if 'fraction_height_operable' in \
|
|
220
|
+
data and data['fraction_height_operable'] is not None else 1.0
|
|
221
|
+
discharge = data['discharge_coefficient'] if 'discharge_coefficient' in data \
|
|
222
|
+
and data['discharge_coefficient'] is not None else 0.45
|
|
223
|
+
cross_vent = data['wind_cross_vent'] if 'wind_cross_vent' in data \
|
|
224
|
+
and data['wind_cross_vent'] is not None else False
|
|
225
|
+
|
|
226
|
+
# Add AFN parameters
|
|
227
|
+
air_flow_coeff = data['flow_coefficient_closed'] if \
|
|
228
|
+
'flow_coefficient_closed' in data and \
|
|
229
|
+
data['flow_coefficient_closed'] is not None else 0
|
|
230
|
+
air_flow_exp = data['flow_exponent_closed'] if \
|
|
231
|
+
'flow_exponent_closed' in data and \
|
|
232
|
+
data['flow_exponent_closed'] is not None else 0.65
|
|
233
|
+
min_diff = data['two_way_threshold'] if \
|
|
234
|
+
'two_way_threshold' in data and \
|
|
235
|
+
data['two_way_threshold'] is not None else 0.0001
|
|
236
|
+
|
|
237
|
+
return cls(area_op, height_op, discharge, cross_vent, air_flow_coeff,
|
|
238
|
+
air_flow_exp, min_diff)
|
|
239
|
+
|
|
240
|
+
def to_idf(self):
|
|
241
|
+
"""IDF string representation of VentilationOpening object.
|
|
242
|
+
|
|
243
|
+
Note that this ventilation opening must be assigned to a honeybee Aperture
|
|
244
|
+
or Door for this method to run. This parent Aperture or Door must also have
|
|
245
|
+
a parent Room. It is also recommended that this Room have a VentilationControl
|
|
246
|
+
object under its energy properties. Otherwise, the default control sequence
|
|
247
|
+
will be used, which will likely result in the window never opening.
|
|
248
|
+
|
|
249
|
+
Also note that the parent's geometry should be in meters whenever calling
|
|
250
|
+
this method and that this method does not return full definitions of
|
|
251
|
+
ventilation control schedules. So these schedules must also be translated
|
|
252
|
+
into the final IDF file.
|
|
253
|
+
|
|
254
|
+
.. code-block shell
|
|
255
|
+
|
|
256
|
+
ZoneVentilation:WindandStackOpenArea,
|
|
257
|
+
ZONE 3 Ventl 1, !- Name
|
|
258
|
+
ZONE 3, !- Zone Name
|
|
259
|
+
0.5, !- Opening Area {m2}
|
|
260
|
+
Constant, !- Opening Area Fraction Schedule Name
|
|
261
|
+
AutoCalculate, !- Opening Effectiveness
|
|
262
|
+
0.0, !- Effective Angle {deg}
|
|
263
|
+
1.0, !- Height Difference {m}
|
|
264
|
+
AutoCalculate, !- Discharge Coefficient for Opening
|
|
265
|
+
18.0, !- Minimum Indoor Temperature {C}
|
|
266
|
+
, !- Minimum Indoor Temperature Schedule Name
|
|
267
|
+
, !- Maximum Indoor Temperature {C}
|
|
268
|
+
, !- Maximum Indoor Temperature Schedule Name
|
|
269
|
+
1.0; !- Delta Temperature {deltaC}
|
|
270
|
+
"""
|
|
271
|
+
# check that a parent is assigned
|
|
272
|
+
assert self.parent is not None, \
|
|
273
|
+
'VentilationOpening must be assigned to an Aperture or Door to use to_idf().'
|
|
274
|
+
|
|
275
|
+
# get the VentilationControl object from the room
|
|
276
|
+
cntrl = None
|
|
277
|
+
room = None
|
|
278
|
+
if self.parent.has_parent:
|
|
279
|
+
if self.parent.parent.has_parent:
|
|
280
|
+
room = self.parent.parent.parent
|
|
281
|
+
if room.properties.energy.window_vent_control is not None:
|
|
282
|
+
cntrl = room.properties.energy.window_vent_control
|
|
283
|
+
if cntrl is None: # use default ventilation control
|
|
284
|
+
cntrl = VentilationControl()
|
|
285
|
+
assert room is not None, \
|
|
286
|
+
'VentilationOpening must have a parent Room to use to_idf().'
|
|
287
|
+
|
|
288
|
+
# process the properties on this object into IDF format
|
|
289
|
+
sch = '' if cntrl.schedule is None else cntrl.schedule.identifier
|
|
290
|
+
wind = 'autocalculate' if self.wind_cross_vent else 0
|
|
291
|
+
angle = self.parent.horizontal_orientation() if self.parent.normal.z != 1 else 0
|
|
292
|
+
angle = angle % 360
|
|
293
|
+
height = (self.parent.geometry.max.z - self.parent.geometry.min.z) * \
|
|
294
|
+
self.fraction_height_operable
|
|
295
|
+
|
|
296
|
+
# create the final IDF string
|
|
297
|
+
values = (
|
|
298
|
+
'{}_Opening'.format(self.parent.identifier), room.identifier,
|
|
299
|
+
self.parent.area * self.fraction_area_operable, sch, wind, angle,
|
|
300
|
+
height, self.discharge_coefficient, cntrl.min_indoor_temperature, '',
|
|
301
|
+
cntrl.max_indoor_temperature, '', cntrl.delta_temperature, '',
|
|
302
|
+
cntrl.min_outdoor_temperature, '', cntrl.max_outdoor_temperature, '', 40)
|
|
303
|
+
comments = (
|
|
304
|
+
'name', 'zone name', 'opening area', 'opening schedule',
|
|
305
|
+
'opening effectiveness {m2}', 'horizontal orientation angle',
|
|
306
|
+
'height difference {m}', 'discharge coefficient',
|
|
307
|
+
'min indoor temperature {C}', 'min in temp schedule',
|
|
308
|
+
'max indoor temperature {C}', 'max in temp schedule',
|
|
309
|
+
'delta temperature {C}', 'delta temp schedule',
|
|
310
|
+
'min outdoor temperature {C}', 'min out temp schedule',
|
|
311
|
+
'max outdoor temperature {C}', 'max out temp schedule', 'max wind speed')
|
|
312
|
+
return generate_idf_string(
|
|
313
|
+
'ZoneVentilation:WindandStackOpenArea', values, comments)
|
|
314
|
+
|
|
315
|
+
def to_dict(self):
|
|
316
|
+
"""Ventilation Opening dictionary representation."""
|
|
317
|
+
base = {'type': 'VentilationOpening'}
|
|
318
|
+
base['fraction_area_operable'] = self.fraction_area_operable
|
|
319
|
+
base['fraction_height_operable'] = self.fraction_height_operable
|
|
320
|
+
base['discharge_coefficient'] = self.discharge_coefficient
|
|
321
|
+
base['wind_cross_vent'] = self.wind_cross_vent
|
|
322
|
+
|
|
323
|
+
base['flow_coefficient_closed'] = self.flow_coefficient_closed
|
|
324
|
+
base['flow_exponent_closed'] = self.flow_exponent_closed
|
|
325
|
+
base['two_way_threshold'] = self.two_way_threshold
|
|
326
|
+
|
|
327
|
+
return base
|
|
328
|
+
|
|
329
|
+
def duplicate(self):
|
|
330
|
+
"""Get a copy of this object."""
|
|
331
|
+
return self.__copy__()
|
|
332
|
+
|
|
333
|
+
def __copy__(self):
|
|
334
|
+
return VentilationOpening(
|
|
335
|
+
self.fraction_area_operable, self.fraction_height_operable,
|
|
336
|
+
self.discharge_coefficient, self.wind_cross_vent,
|
|
337
|
+
self.flow_coefficient_closed, self.flow_exponent_closed,
|
|
338
|
+
self.two_way_threshold)
|
|
339
|
+
|
|
340
|
+
def __key(self):
|
|
341
|
+
"""A tuple based on the object properties, useful for hashing."""
|
|
342
|
+
return (self.fraction_area_operable, self.fraction_height_operable,
|
|
343
|
+
self.discharge_coefficient, self.wind_cross_vent,
|
|
344
|
+
self.flow_coefficient_closed,
|
|
345
|
+
self.flow_exponent_closed,
|
|
346
|
+
self.two_way_threshold)
|
|
347
|
+
|
|
348
|
+
def __hash__(self):
|
|
349
|
+
return hash(self.__key())
|
|
350
|
+
|
|
351
|
+
def __eq__(self, other):
|
|
352
|
+
return isinstance(other, VentilationOpening) and self.__key() == other.__key()
|
|
353
|
+
|
|
354
|
+
def __ne__(self, other):
|
|
355
|
+
return not self.__eq__(other)
|
|
356
|
+
|
|
357
|
+
def ToString(self):
|
|
358
|
+
"""Overwrite .NET ToString."""
|
|
359
|
+
return self.__repr__()
|
|
360
|
+
|
|
361
|
+
def __repr__(self):
|
|
362
|
+
return 'VentilationOpening: [fraction area: {}] ' \
|
|
363
|
+
'[fraction height: {}] [discharge: {}]'.format(
|
|
364
|
+
self.fraction_area_operable, self.fraction_height_operable,
|
|
365
|
+
self.discharge_coefficient)
|
|
@@ -0,0 +1,314 @@
|
|
|
1
|
+
# coding=utf-8
|
|
2
|
+
"""Definitions for global parameters used in the ventilation simulation."""
|
|
3
|
+
from __future__ import division
|
|
4
|
+
import math
|
|
5
|
+
|
|
6
|
+
from ladybug_geometry.bounding import bounding_box_extents
|
|
7
|
+
|
|
8
|
+
from honeybee._lockable import lockable
|
|
9
|
+
from honeybee.typing import float_in_range, float_positive, float_in_range_excl_incl
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
@lockable
|
|
13
|
+
class VentilationSimulationControl(object):
|
|
14
|
+
"""Global parameters used to specify the simulation of air flow.
|
|
15
|
+
|
|
16
|
+
Args:
|
|
17
|
+
vent_control_type: Text indicating type of ventilation control.
|
|
18
|
+
Choose from the following:
|
|
19
|
+
|
|
20
|
+
* SingleZone
|
|
21
|
+
* MultiZoneWithDistribution
|
|
22
|
+
* MultiZoneWithoutDistribution
|
|
23
|
+
|
|
24
|
+
The MultiZone options will model air flow with the AirflowNetwork model,
|
|
25
|
+
which is generally more accurate then the SingleZone option, but will take
|
|
26
|
+
considerably longer to simulate, and requires defining more ventilation
|
|
27
|
+
parameters to explicitly account for weather and building-induced pressure
|
|
28
|
+
differences, and the leakage geometry corresponding to specific windows,
|
|
29
|
+
doors, and surface cracks (Default: 'SingleZone').
|
|
30
|
+
reference_temperature: Reference temperature measurement in Celsius under which
|
|
31
|
+
the surface crack data were obtained. This is only used for AFN simulations,
|
|
32
|
+
when vent_control_type is NOT SingleZone. (Default: 20).
|
|
33
|
+
reference_pressure: Reference barometric pressure measurement in Pascals
|
|
34
|
+
under which the surface crack data were obtained. This is only used for AFN
|
|
35
|
+
simulations, when vent_control_type is NOT SingleZone.(Default: 101325).
|
|
36
|
+
reference_humidity_ratio: Reference humidity ratio measurement in
|
|
37
|
+
kgWater/kgDryAir under which the surface crack data were obtained.
|
|
38
|
+
This is only used for AFN simulations, when vent_control_type is
|
|
39
|
+
NOT SingleZone. (Default: 0).
|
|
40
|
+
building_type: Text indicating relationship between building footprint and
|
|
41
|
+
height. Choose from the following:
|
|
42
|
+
|
|
43
|
+
* LowRise
|
|
44
|
+
* HighRise
|
|
45
|
+
|
|
46
|
+
LowRise corresponds to a building where the height is less then three
|
|
47
|
+
times the width AND length of the footprint. HighRise corresponds to a
|
|
48
|
+
building where height is more than three times the width OR length of
|
|
49
|
+
the footprint. This parameter is used to estimate building-wide wind
|
|
50
|
+
pressure coefficients for the AFN by approximating the building geometry
|
|
51
|
+
as an extruded rectangle. This property can be auto-calculated from
|
|
52
|
+
Honeybee Room geometry with the geometry_properties_from_rooms
|
|
53
|
+
method. (Default: 'LowRise').
|
|
54
|
+
long_axis_angle: A number between 0 and 180 for the clockwise angle difference
|
|
55
|
+
in degrees that the long axis of the building is from true North. This
|
|
56
|
+
parameter is used to estimate building-wide wind pressure coefficients
|
|
57
|
+
for the AFN by approximating the building geometry as an extruded
|
|
58
|
+
rectangle. 0 indicates a North-South long axis while 90 indicates an
|
|
59
|
+
East-West long axis. (Default: 0).
|
|
60
|
+
aspect_ratio: A number between 0 and 1 for the aspect ratio of the building's
|
|
61
|
+
footprint, defined as the ratio of length of the short axis divided
|
|
62
|
+
by the length of the long axis. This parameter is used to estimate
|
|
63
|
+
building-wide wind pressure coefficients for the AFN by approximating
|
|
64
|
+
the building geometry as an extruded rectangle (Default: 1).
|
|
65
|
+
|
|
66
|
+
Properties:
|
|
67
|
+
* vent_control_type
|
|
68
|
+
* reference_temperature
|
|
69
|
+
* reference_pressure
|
|
70
|
+
* reference_humidity_ratio
|
|
71
|
+
* building_type
|
|
72
|
+
* long_axis_angle
|
|
73
|
+
* aspect_ratio
|
|
74
|
+
"""
|
|
75
|
+
__slots__ = ('_vent_control_type', '_reference_temperature',
|
|
76
|
+
'_reference_pressure', '_reference_humidity_ratio',
|
|
77
|
+
'_building_type', '_long_axis_angle', '_aspect_ratio', '_locked')
|
|
78
|
+
VENT_CONTROL_TYPES = ('SingleZone', 'MultiZoneWithDistribution',
|
|
79
|
+
'MultiZoneWithoutDistribution')
|
|
80
|
+
BUILDING_TYPES = ('LowRise', 'HighRise')
|
|
81
|
+
|
|
82
|
+
def __init__(self, vent_control_type='SingleZone', reference_temperature=20,
|
|
83
|
+
reference_pressure=101325, reference_humidity_ratio=0,
|
|
84
|
+
building_type='LowRise', long_axis_angle=0, aspect_ratio=1):
|
|
85
|
+
"""Initialize VentilationSimulationControl."""
|
|
86
|
+
self.vent_control_type = vent_control_type
|
|
87
|
+
self.reference_temperature = reference_temperature
|
|
88
|
+
self.reference_pressure = reference_pressure
|
|
89
|
+
self.reference_humidity_ratio = reference_humidity_ratio
|
|
90
|
+
self.building_type = building_type
|
|
91
|
+
self.long_axis_angle = long_axis_angle
|
|
92
|
+
self.aspect_ratio = aspect_ratio
|
|
93
|
+
|
|
94
|
+
@property
|
|
95
|
+
def vent_control_type(self):
|
|
96
|
+
"""Get or set text indicating type of ventilation control type."""
|
|
97
|
+
return self._vent_control_type
|
|
98
|
+
|
|
99
|
+
@vent_control_type.setter
|
|
100
|
+
def vent_control_type(self, value):
|
|
101
|
+
assert value in self.VENT_CONTROL_TYPES, 'vent_control_type {} is not '\
|
|
102
|
+
'recognized.\nChoose from the following:\n{}'.format(
|
|
103
|
+
value, self.VENT_CONTROL_TYPES)
|
|
104
|
+
self._vent_control_type = value
|
|
105
|
+
|
|
106
|
+
@property
|
|
107
|
+
def reference_temperature(self):
|
|
108
|
+
"""Get or set the temperature for the reference crack."""
|
|
109
|
+
return self._reference_temperature
|
|
110
|
+
|
|
111
|
+
@reference_temperature.setter
|
|
112
|
+
def reference_temperature(self, value):
|
|
113
|
+
self._reference_temperature = \
|
|
114
|
+
float_in_range(value, mi=-273.15, input_name='reference_temperature')
|
|
115
|
+
|
|
116
|
+
@property
|
|
117
|
+
def reference_pressure(self):
|
|
118
|
+
"""Get or set the barometric pressure for the reference crack."""
|
|
119
|
+
return self._reference_pressure
|
|
120
|
+
|
|
121
|
+
@reference_pressure.setter
|
|
122
|
+
def reference_pressure(self, value):
|
|
123
|
+
self._reference_pressure = \
|
|
124
|
+
float_in_range(value, 31000, 120000, 'reference_pressure')
|
|
125
|
+
|
|
126
|
+
@property
|
|
127
|
+
def reference_humidity_ratio(self):
|
|
128
|
+
"""Get or set the humidity ratio for the reference crack."""
|
|
129
|
+
return self._reference_humidity_ratio
|
|
130
|
+
|
|
131
|
+
@reference_humidity_ratio.setter
|
|
132
|
+
def reference_humidity_ratio(self, value):
|
|
133
|
+
self._reference_humidity_ratio = \
|
|
134
|
+
float_positive(value, 'reference_humidity_ratio')
|
|
135
|
+
|
|
136
|
+
@property
|
|
137
|
+
def building_type(self):
|
|
138
|
+
"""Get or set text indicating whether the building is high or low rise."""
|
|
139
|
+
return self._building_type
|
|
140
|
+
|
|
141
|
+
@building_type.setter
|
|
142
|
+
def building_type(self, value):
|
|
143
|
+
assert value in self.BUILDING_TYPES, 'building_type {} is not '\
|
|
144
|
+
'recognized.\nChoose from the following:\n{}'.format(
|
|
145
|
+
value, self.BUILDING_TYPES)
|
|
146
|
+
self._building_type = value
|
|
147
|
+
|
|
148
|
+
@property
|
|
149
|
+
def long_axis_angle(self):
|
|
150
|
+
"""Get or set a number between 0 and 180 for the building long axis angle.
|
|
151
|
+
|
|
152
|
+
The value represents the clockwise difference between the long axis and
|
|
153
|
+
true North. 0 indicates a North-South long axis while 90 indicates an
|
|
154
|
+
East-West long axis.
|
|
155
|
+
"""
|
|
156
|
+
return self._long_axis_angle
|
|
157
|
+
|
|
158
|
+
@long_axis_angle.setter
|
|
159
|
+
def long_axis_angle(self, value):
|
|
160
|
+
self._long_axis_angle = float_in_range(value, 0, 180, 'long_axis_angle')
|
|
161
|
+
|
|
162
|
+
@property
|
|
163
|
+
def aspect_ratio(self):
|
|
164
|
+
"""Get or set a number between 0 and 1 for the building footprint aspect ratio.
|
|
165
|
+
"""
|
|
166
|
+
return self._aspect_ratio
|
|
167
|
+
|
|
168
|
+
@aspect_ratio.setter
|
|
169
|
+
def aspect_ratio(self, value):
|
|
170
|
+
self._aspect_ratio = float_in_range_excl_incl(value, 0, 1, 'aspect_ratio')
|
|
171
|
+
|
|
172
|
+
def assign_geometry_properties_from_rooms(self, rooms):
|
|
173
|
+
"""Assign the geometry properties of this object using an array of Honeybee Rooms.
|
|
174
|
+
|
|
175
|
+
The building_type (HighRise or LowRise) will be determined by analyzing
|
|
176
|
+
the bounding box around the Rooms (assessing whether the box is taller than
|
|
177
|
+
it is wide + long).
|
|
178
|
+
|
|
179
|
+
This object's long_axis_angle will be used to orient the bounding box and
|
|
180
|
+
compute the aspect ratio of the footprint. If the length of what should
|
|
181
|
+
be the short axis ends up being longer than the other axis, this object's
|
|
182
|
+
long_axis_angle will be rotated 90 degrees in order to keep the aspect
|
|
183
|
+
ratio from being greater than 1.
|
|
184
|
+
|
|
185
|
+
Args:
|
|
186
|
+
rooms: An array of Honeybee Rooms, which will have their geometry
|
|
187
|
+
collectively analyzed in order to set the geometry properties
|
|
188
|
+
of this object. Typically, this should be all of the Rooms of
|
|
189
|
+
a Honeybee Model.
|
|
190
|
+
"""
|
|
191
|
+
l_axis = self.long_axis_angle
|
|
192
|
+
bldg_type, aspect_r = self.geometry_properties_from_rooms(rooms, l_axis)
|
|
193
|
+
self.building_type = bldg_type
|
|
194
|
+
if aspect_r > 1: # rotate the long axis 90 degrees
|
|
195
|
+
aspect_r = 1 / aspect_r
|
|
196
|
+
self.long_axis_angle = l_axis + 90 if l_axis < 90 else l_axis - 90
|
|
197
|
+
self.aspect_ratio = aspect_r
|
|
198
|
+
|
|
199
|
+
@classmethod
|
|
200
|
+
def from_dict(cls, data):
|
|
201
|
+
"""Create a VentilationSimulationControl object from a dictionary.
|
|
202
|
+
|
|
203
|
+
Args:
|
|
204
|
+
data: A VentilationSimulationControl dictionary following the format below.
|
|
205
|
+
|
|
206
|
+
.. code-block:: python
|
|
207
|
+
|
|
208
|
+
{
|
|
209
|
+
"type": "VentilationSimulationControl"
|
|
210
|
+
"vent_control_type": SingleZone # type of ventilation control
|
|
211
|
+
"reference_temperature": 20 # reference crack temperature
|
|
212
|
+
"reference_pressure": 101320 # reference crack barometric pressure
|
|
213
|
+
"reference_humidity_ratio": 0.5 # reference crack humidity ratio
|
|
214
|
+
"building_type": 'LowRise' # building type text
|
|
215
|
+
"long_axis_angle": 0 # angle of building low axis
|
|
216
|
+
"aspect_ratio": 1 # aspect ratio of building footprint
|
|
217
|
+
}
|
|
218
|
+
"""
|
|
219
|
+
assert data['type'] == 'VentilationSimulationControl', 'Expected ' \
|
|
220
|
+
'VentilationSimulationControl dictionary. Got {}.'.format(data['type'])
|
|
221
|
+
|
|
222
|
+
vent_control_type = data['vent_control_type'] if 'vent_control_type' in data \
|
|
223
|
+
and data['vent_control_type'] is not None else 'SingleZone'
|
|
224
|
+
ref_temp = data['reference_temperature'] if 'reference_temperature' in data \
|
|
225
|
+
and data['reference_temperature'] is not None else 20
|
|
226
|
+
ref_pres = data['reference_pressure'] if \
|
|
227
|
+
'reference_pressure' in data and \
|
|
228
|
+
data['reference_pressure'] is not None else 101320
|
|
229
|
+
ref_hum = data['reference_humidity_ratio'] if 'reference_humidity_ratio' in \
|
|
230
|
+
data and data['reference_humidity_ratio'] is not None else 0
|
|
231
|
+
bld_type = data['building_type'] if 'building_type' in data and \
|
|
232
|
+
data['building_type'] is not None else 'LowRise'
|
|
233
|
+
axis = data['long_axis_angle'] if 'long_axis_angle' in data and \
|
|
234
|
+
data['long_axis_angle'] is not None else 0
|
|
235
|
+
ratio = data['aspect_ratio'] if 'aspect_ratio' in data and data['aspect_ratio'] \
|
|
236
|
+
is not None else 1
|
|
237
|
+
return cls(vent_control_type, ref_temp, ref_pres, ref_hum, bld_type, axis, ratio)
|
|
238
|
+
|
|
239
|
+
def to_dict(self):
|
|
240
|
+
"""VentilationSimulationControl dictionary representation."""
|
|
241
|
+
base = {'type': 'VentilationSimulationControl'}
|
|
242
|
+
base['vent_control_type'] = self.vent_control_type
|
|
243
|
+
base['reference_temperature'] = self.reference_temperature
|
|
244
|
+
base['reference_pressure'] = self.reference_pressure
|
|
245
|
+
base['reference_humidity_ratio'] = self.reference_humidity_ratio
|
|
246
|
+
base['building_type'] = self.building_type
|
|
247
|
+
base['long_axis_angle'] = self.long_axis_angle
|
|
248
|
+
base['aspect_ratio'] = self.aspect_ratio
|
|
249
|
+
return base
|
|
250
|
+
|
|
251
|
+
def duplicate(self):
|
|
252
|
+
"""Get a copy of this object."""
|
|
253
|
+
return self.__copy__()
|
|
254
|
+
|
|
255
|
+
@staticmethod
|
|
256
|
+
def geometry_properties_from_rooms(rooms, axis_angle=0):
|
|
257
|
+
"""Get AFN building geometry properties from an array of Honeybee Rooms.
|
|
258
|
+
|
|
259
|
+
Args:
|
|
260
|
+
rooms: An array of Honeybee Rooms, which will have their geometry
|
|
261
|
+
collectively analyzed.
|
|
262
|
+
axis_angle: The clockwise rotation angle in degrees in the XY plane
|
|
263
|
+
to represent the orientation of the bounding box. (Default: 0).
|
|
264
|
+
|
|
265
|
+
Returns:
|
|
266
|
+
A tuple with 2 values for geometry properties.
|
|
267
|
+
|
|
268
|
+
1) Text indicating the building_type (either Highrise or LowRise)
|
|
269
|
+
2) A number for the aspect ratio of the axis_angle-oriented bounding box.
|
|
270
|
+
|
|
271
|
+
Note that the aspect ratio may be greater than 1 if the axis_angle
|
|
272
|
+
isn't aligned to the long axis of the geometry.
|
|
273
|
+
"""
|
|
274
|
+
# process the inputs to be suitable for ladybug_geometry
|
|
275
|
+
if axis_angle != 0: # convert to counter-clockwise radians for ladybug_geometry
|
|
276
|
+
axis_angle = -math.radians(axis_angle)
|
|
277
|
+
geo = [room.geometry for room in rooms] # get ladybug_geometry polyfaces
|
|
278
|
+
|
|
279
|
+
# get the bounding box and return the properties
|
|
280
|
+
xx, yy, zz = bounding_box_extents(geo)
|
|
281
|
+
bldg_type = 'LowRise' if zz <= 3 * max(xx, yy) else 'HighRise'
|
|
282
|
+
return bldg_type, xx / yy
|
|
283
|
+
|
|
284
|
+
def __copy__(self):
|
|
285
|
+
return VentilationSimulationControl(
|
|
286
|
+
self.vent_control_type, self.reference_temperature,
|
|
287
|
+
self.reference_pressure, self.reference_humidity_ratio,
|
|
288
|
+
self.building_type, self.long_axis_angle, self.aspect_ratio)
|
|
289
|
+
|
|
290
|
+
def __key(self):
|
|
291
|
+
"""A tuple based on the object properties, useful for hashing."""
|
|
292
|
+
return (self.vent_control_type, self.reference_temperature,
|
|
293
|
+
self.reference_pressure, self.reference_humidity_ratio,
|
|
294
|
+
self.building_type, self.long_axis_angle, self.aspect_ratio)
|
|
295
|
+
|
|
296
|
+
def __hash__(self):
|
|
297
|
+
return hash(self.__key())
|
|
298
|
+
|
|
299
|
+
def __eq__(self, other):
|
|
300
|
+
return isinstance(other, VentilationSimulationControl) and \
|
|
301
|
+
self.__key() == other.__key()
|
|
302
|
+
|
|
303
|
+
def __ne__(self, other):
|
|
304
|
+
return not self.__eq__(other)
|
|
305
|
+
|
|
306
|
+
def ToString(self):
|
|
307
|
+
"""Overwrite .NET ToString."""
|
|
308
|
+
return self.__repr__()
|
|
309
|
+
|
|
310
|
+
def __repr__(self):
|
|
311
|
+
return 'VentilationSimulationControl: [control type: {}] ' \
|
|
312
|
+
'[building_type: {}] [long axis: {}] [aspect_ratio: {}]' .format(
|
|
313
|
+
self.vent_control_type, self.building_type,
|
|
314
|
+
round(self.long_axis_angle), round(self.aspect_ratio, 2))
|