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,480 @@
|
|
|
1
|
+
# coding=utf-8
|
|
2
|
+
"""Complete definition of lighting in a simulation, including schedule and load."""
|
|
3
|
+
from __future__ import division
|
|
4
|
+
|
|
5
|
+
from honeybee._lockable import lockable
|
|
6
|
+
from honeybee.typing import float_in_range, float_positive, clean_and_id_ep_string
|
|
7
|
+
|
|
8
|
+
from ._base import _LoadBase
|
|
9
|
+
from ..schedule.ruleset import ScheduleRuleset
|
|
10
|
+
from ..schedule.fixedinterval import ScheduleFixedInterval
|
|
11
|
+
from ..reader import parse_idf_string
|
|
12
|
+
from ..writer import generate_idf_string
|
|
13
|
+
from ..properties.extension import LightingProperties
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
@lockable
|
|
17
|
+
class Lighting(_LoadBase):
|
|
18
|
+
"""A complete definition of lighting, including schedules and load.
|
|
19
|
+
|
|
20
|
+
Args:
|
|
21
|
+
identifier: Text string for a unique Lighting ID. Must be < 100 characters
|
|
22
|
+
and not contain any EnergyPlus special characters. This will be used to
|
|
23
|
+
identify the object across a model and in the exported IDF.
|
|
24
|
+
watts_per_area: A numerical value for the lighting power density in
|
|
25
|
+
Watts per square meter of floor area.
|
|
26
|
+
schedule: A ScheduleRuleset or ScheduleFixedInterval for the use of lights
|
|
27
|
+
over the course of the year. The type of this schedule should be
|
|
28
|
+
Fractional and the fractional values will get multiplied by the
|
|
29
|
+
watts_per_area to yield a complete lighting profile.
|
|
30
|
+
return_air_fraction: A number between 0 and 1 for the fraction of the total
|
|
31
|
+
lighting load that goes into the zone return air (into the zone outlet
|
|
32
|
+
node). (Default: 0.0). (representative of pendant lighting).
|
|
33
|
+
radiant_fraction: A number between 0 and 1 for the fraction of the total
|
|
34
|
+
lighting load given off as long wave radiant heat.
|
|
35
|
+
(Default: 0.32). (representative of pendant lighting).
|
|
36
|
+
visible_fraction: A number between 0 and 1 for the fraction of the total
|
|
37
|
+
lighting load given off as short wave visible light.
|
|
38
|
+
(Default: 0.25). (representative of pendant lighting).
|
|
39
|
+
|
|
40
|
+
Properties:
|
|
41
|
+
* identifier
|
|
42
|
+
* display_name
|
|
43
|
+
* watts_per_area
|
|
44
|
+
* schedule
|
|
45
|
+
* return_air_fraction
|
|
46
|
+
* radiant_fraction
|
|
47
|
+
* visible_fraction
|
|
48
|
+
* convected_fraction
|
|
49
|
+
* baseline_watts_per_area
|
|
50
|
+
* user_data
|
|
51
|
+
"""
|
|
52
|
+
__slots__ = ('_watts_per_area', '_schedule', '_return_air_fraction',
|
|
53
|
+
'_radiant_fraction', '_visible_fraction', '_baseline_watts_per_area')
|
|
54
|
+
|
|
55
|
+
def __init__(self, identifier, watts_per_area, schedule, return_air_fraction=0.0,
|
|
56
|
+
radiant_fraction=0.32, visible_fraction=0.25):
|
|
57
|
+
"""Initialize Lighting."""
|
|
58
|
+
_LoadBase.__init__(self, identifier)
|
|
59
|
+
self._radiant_fraction = 0 # starting value so that check runs correctly
|
|
60
|
+
self._visible_fraction = 0 # starting value so that check runs correctly
|
|
61
|
+
|
|
62
|
+
self.watts_per_area = watts_per_area
|
|
63
|
+
self.schedule = schedule
|
|
64
|
+
self.return_air_fraction = return_air_fraction
|
|
65
|
+
self.radiant_fraction = radiant_fraction
|
|
66
|
+
self.visible_fraction = visible_fraction
|
|
67
|
+
self.baseline_watts_per_area = None # can be set by the user later
|
|
68
|
+
self._properties = LightingProperties(self)
|
|
69
|
+
|
|
70
|
+
@property
|
|
71
|
+
def watts_per_area(self):
|
|
72
|
+
"""Get or set the lighting power density in Watts/square meter of floor area."""
|
|
73
|
+
return self._watts_per_area
|
|
74
|
+
|
|
75
|
+
@watts_per_area.setter
|
|
76
|
+
def watts_per_area(self, value):
|
|
77
|
+
self._watts_per_area = float_positive(value, 'lighting watts per area')
|
|
78
|
+
|
|
79
|
+
@property
|
|
80
|
+
def schedule(self):
|
|
81
|
+
"""Get or set a ScheduleRuleset or ScheduleFixedInterval for lighting usage."""
|
|
82
|
+
return self._schedule
|
|
83
|
+
|
|
84
|
+
@schedule.setter
|
|
85
|
+
def schedule(self, value):
|
|
86
|
+
assert isinstance(value, (ScheduleRuleset, ScheduleFixedInterval)), \
|
|
87
|
+
'Expected ScheduleRuleset or ScheduleFixedInterval for Lighting ' \
|
|
88
|
+
'schedule. Got {}.'.format(type(value))
|
|
89
|
+
self._check_fractional_schedule_type(value, 'Lighting')
|
|
90
|
+
value.lock() # lock editing in case schedule has multiple references
|
|
91
|
+
self._schedule = value
|
|
92
|
+
|
|
93
|
+
@property
|
|
94
|
+
def return_air_fraction(self):
|
|
95
|
+
"""Get or set the fraction of lighting heat that goes into the return air."""
|
|
96
|
+
return self._return_air_fraction
|
|
97
|
+
|
|
98
|
+
@return_air_fraction.setter
|
|
99
|
+
def return_air_fraction(self, value):
|
|
100
|
+
if value is not None:
|
|
101
|
+
self._return_air_fraction = float_in_range(
|
|
102
|
+
value, 0.0, 1.0, 'lighting return air fraction')
|
|
103
|
+
else:
|
|
104
|
+
self._return_air_fraction = 0
|
|
105
|
+
self._check_fractions()
|
|
106
|
+
|
|
107
|
+
@property
|
|
108
|
+
def radiant_fraction(self):
|
|
109
|
+
"""Get or set the fraction of lighting heat given off as long wave radiation."""
|
|
110
|
+
return self._radiant_fraction
|
|
111
|
+
|
|
112
|
+
@radiant_fraction.setter
|
|
113
|
+
def radiant_fraction(self, value):
|
|
114
|
+
if value is not None:
|
|
115
|
+
self._radiant_fraction = float_in_range(
|
|
116
|
+
value, 0.0, 1.0, 'lighting radiant fraction')
|
|
117
|
+
else:
|
|
118
|
+
self._radiant_fraction = 0.32
|
|
119
|
+
self._check_fractions()
|
|
120
|
+
|
|
121
|
+
@property
|
|
122
|
+
def visible_fraction(self):
|
|
123
|
+
"""Get or set the fraction of lighting heat given off as visible light."""
|
|
124
|
+
return self._visible_fraction
|
|
125
|
+
|
|
126
|
+
@visible_fraction.setter
|
|
127
|
+
def visible_fraction(self, value):
|
|
128
|
+
if value is not None:
|
|
129
|
+
self._visible_fraction = float_in_range(
|
|
130
|
+
value, 0.0, 1.0, 'lighting visible fraction')
|
|
131
|
+
else:
|
|
132
|
+
self._visible_fraction = 0.25
|
|
133
|
+
self._check_fractions()
|
|
134
|
+
|
|
135
|
+
@property
|
|
136
|
+
def convected_fraction(self):
|
|
137
|
+
"""Get the fraction of lighting heat that convects to the zone air."""
|
|
138
|
+
tot = (self._return_air_fraction, self._radiant_fraction, self._visible_fraction)
|
|
139
|
+
return 1 - sum(tot)
|
|
140
|
+
|
|
141
|
+
@property
|
|
142
|
+
def baseline_watts_per_area(self):
|
|
143
|
+
"""Get or set the baseline lighting power density in W/m2 of floor area.
|
|
144
|
+
|
|
145
|
+
This baseline is useful to track how much better the installed lights are
|
|
146
|
+
in comparison to a standard like ASHRAE 90.1. If set to None, it will
|
|
147
|
+
default to 11.84029 W/m2, which is that ASHRAE 90.1-2004 baseline for
|
|
148
|
+
an office.
|
|
149
|
+
"""
|
|
150
|
+
return self._baseline_watts_per_area if self._baseline_watts_per_area \
|
|
151
|
+
is not None else 11.84029
|
|
152
|
+
|
|
153
|
+
@baseline_watts_per_area.setter
|
|
154
|
+
def baseline_watts_per_area(self, value):
|
|
155
|
+
if value is not None:
|
|
156
|
+
value = float_positive(value, 'lighting baseline watts per area')
|
|
157
|
+
self._baseline_watts_per_area = value
|
|
158
|
+
|
|
159
|
+
def diversify(self, count, watts_stdev=20, schedule_offset=1, timestep=1,
|
|
160
|
+
schedule_indices=None):
|
|
161
|
+
"""Get an array of diversified Lighting derived from this "average" one.
|
|
162
|
+
|
|
163
|
+
Approximately 2/3 of the schedules in the output objects will be offset
|
|
164
|
+
from the mean by the input schedule_offset (1/3 ahead and another 1/3 behind).
|
|
165
|
+
|
|
166
|
+
Args:
|
|
167
|
+
count: An positive integer for the number of diversified objects to
|
|
168
|
+
generate from this mean object.
|
|
169
|
+
watts_stdev: A number between 0 and 100 for the percent of the
|
|
170
|
+
watts_per_area representing one standard deviation
|
|
171
|
+
of diversification from the mean. (Default 20 percent).
|
|
172
|
+
schedule_offset: A positive integer for the number of timesteps at which
|
|
173
|
+
the lighting schedule of the resulting objects will be shifted - roughly
|
|
174
|
+
1/3 of the objects ahead and another 1/3 behind. (Default: 1).
|
|
175
|
+
timestep: An integer for the number of timesteps per hour at which the
|
|
176
|
+
shifting is occurring. This must be a value between 1 and 60, which
|
|
177
|
+
is evenly divisible by 60. 1 indicates that each step is an hour
|
|
178
|
+
while 60 indicates that each step is a minute. (Default: 1).
|
|
179
|
+
schedule_indices: An optional list of integers from 0 to 2 with a length
|
|
180
|
+
equal to the input count, which will be used to set whether a given
|
|
181
|
+
schedule is behind (0), ahead (2), or the same (1). This can be
|
|
182
|
+
used to coordinate schedules across diversified programs. If None
|
|
183
|
+
a random list of integers will be generated. (Default: None).
|
|
184
|
+
"""
|
|
185
|
+
# generate shifted schedules and a gaussian distribution of watts_per_area
|
|
186
|
+
usage_schs = self._shift_schedule(self.schedule, schedule_offset, timestep)
|
|
187
|
+
stdev = self.watts_per_area * (watts_stdev / 100)
|
|
188
|
+
new_loads, sch_ints = self._gaussian_values(count, self.watts_per_area, stdev)
|
|
189
|
+
sch_ints = sch_ints if schedule_indices is None else schedule_indices
|
|
190
|
+
|
|
191
|
+
# generate the new objects and return them
|
|
192
|
+
new_objects = []
|
|
193
|
+
for load_val, sch_int in zip(new_loads, sch_ints):
|
|
194
|
+
new_obj = self.duplicate()
|
|
195
|
+
new_obj.identifier = clean_and_id_ep_string(self.identifier)
|
|
196
|
+
new_obj.watts_per_area = load_val
|
|
197
|
+
new_obj.schedule = usage_schs[sch_int]
|
|
198
|
+
new_objects.append(new_obj)
|
|
199
|
+
return new_objects
|
|
200
|
+
|
|
201
|
+
@classmethod
|
|
202
|
+
def from_idf(cls, idf_string, schedule_dict):
|
|
203
|
+
"""Create a Lighting object from an EnergyPlus IDF text string.
|
|
204
|
+
|
|
205
|
+
Note that the Lighting idf_string must use the 'watts per zone floor area'
|
|
206
|
+
method in order to be successfully imported.
|
|
207
|
+
|
|
208
|
+
Args:
|
|
209
|
+
idf_string: A text string fully describing an EnergyPlus lighting definition.
|
|
210
|
+
schedule_dict: A dictionary with schedule identifiers as keys and honeybee
|
|
211
|
+
schedule objects as values (either ScheduleRuleset or
|
|
212
|
+
ScheduleFixedInterval). These will be used to assign the schedules to
|
|
213
|
+
the Lighting object.
|
|
214
|
+
|
|
215
|
+
Returns:
|
|
216
|
+
A tuple with two elements
|
|
217
|
+
|
|
218
|
+
- lighting: A Lighting object loaded from the idf_string.
|
|
219
|
+
|
|
220
|
+
- zone_identifier: The identifier of the zone to which the Lighting
|
|
221
|
+
object should be assigned.
|
|
222
|
+
"""
|
|
223
|
+
# check the inputs
|
|
224
|
+
ep_strs = parse_idf_string(idf_string, 'Lights,')
|
|
225
|
+
assert ep_strs[3].lower() == 'watts/area', \
|
|
226
|
+
'Lights must use Watts/Area method to be loaded from IDF to honeybee.'
|
|
227
|
+
|
|
228
|
+
# extract the properties from the string
|
|
229
|
+
return_fract = 0
|
|
230
|
+
rad_fract = 0
|
|
231
|
+
vis_fract = 0
|
|
232
|
+
try:
|
|
233
|
+
return_fract = ep_strs[7] if ep_strs[7] != '' else 0
|
|
234
|
+
rad_fract = ep_strs[8] if ep_strs[8] != '' else 0
|
|
235
|
+
vis_fract = ep_strs[9] if ep_strs[9] != '' else 0
|
|
236
|
+
except IndexError:
|
|
237
|
+
pass # shorter lighting definition lacking fractions
|
|
238
|
+
|
|
239
|
+
# extract the schedules from the string
|
|
240
|
+
try:
|
|
241
|
+
sched = schedule_dict[ep_strs[2]]
|
|
242
|
+
except KeyError as e:
|
|
243
|
+
raise ValueError('Failed to find {} in the schedule_dict.'.format(e))
|
|
244
|
+
|
|
245
|
+
# return the lighting object and the zone id for the lighting object
|
|
246
|
+
obj_id = ep_strs[0].split('..')[0]
|
|
247
|
+
zone_id = ep_strs[1]
|
|
248
|
+
lighting = cls(obj_id, ep_strs[5], sched, return_fract, rad_fract, vis_fract)
|
|
249
|
+
return lighting, zone_id
|
|
250
|
+
|
|
251
|
+
@classmethod
|
|
252
|
+
def from_dict(cls, data):
|
|
253
|
+
"""Create a Lighting object from a dictionary.
|
|
254
|
+
|
|
255
|
+
Note that the dictionary must be a non-abridged version for this classmethod
|
|
256
|
+
to work.
|
|
257
|
+
|
|
258
|
+
Args:
|
|
259
|
+
data: A Lighting dictionary in following the format below.
|
|
260
|
+
|
|
261
|
+
.. code-block:: python
|
|
262
|
+
|
|
263
|
+
{
|
|
264
|
+
"type": 'Lighting',
|
|
265
|
+
"identifier": 'Open_Office_Lighting_100_0_032_025',
|
|
266
|
+
"display_name": 'Office Lighting',
|
|
267
|
+
"watts_per_area": 10, # lighting watts per square meter of floor area
|
|
268
|
+
"schedule": {}, # ScheduleRuleset/ScheduleFixedInterval dictionary
|
|
269
|
+
"return_air_fraction": 0, # fraction of heat going to return air
|
|
270
|
+
"radiant_fraction": 0.32, # fraction of heat that is long wave radiant
|
|
271
|
+
"visible_fraction": 0.25 # fraction of heat that is short wave visible
|
|
272
|
+
}
|
|
273
|
+
"""
|
|
274
|
+
assert data['type'] == 'Lighting', \
|
|
275
|
+
'Expected Lighting dictionary. Got {}.'.format(data['type'])
|
|
276
|
+
sched = cls._get_schedule_from_dict(data['schedule'])
|
|
277
|
+
ret_fract, rad_fract, vis_fract = cls._optional_dict_keys(data)
|
|
278
|
+
new_obj = cls(data['identifier'], data['watts_per_area'], sched,
|
|
279
|
+
ret_fract, rad_fract, vis_fract)
|
|
280
|
+
if 'user_data' in data and data['user_data'] is not None:
|
|
281
|
+
new_obj.user_data = data['user_data']
|
|
282
|
+
if 'properties' in data and data['properties'] is not None:
|
|
283
|
+
new_obj.properties._load_extension_attr_from_dict(data['properties'])
|
|
284
|
+
return cls._apply_optional_dict_props(new_obj, data)
|
|
285
|
+
|
|
286
|
+
@classmethod
|
|
287
|
+
def from_dict_abridged(cls, data, schedule_dict):
|
|
288
|
+
"""Create a Lighting object from an abridged dictionary.
|
|
289
|
+
|
|
290
|
+
Args:
|
|
291
|
+
data: A LightingAbridged dictionary in following the format below.
|
|
292
|
+
schedule_dict: A dictionary with schedule identifiers as keys and
|
|
293
|
+
honeybee schedule objects as values (either ScheduleRuleset or
|
|
294
|
+
ScheduleFixedInterval). These will be used to assign the schedules
|
|
295
|
+
to the Lighting object.
|
|
296
|
+
|
|
297
|
+
.. code-block:: python
|
|
298
|
+
|
|
299
|
+
{
|
|
300
|
+
"type": 'LightingAbridged',
|
|
301
|
+
"identifier": 'Open_Office_Lighting_100_0_032_025',
|
|
302
|
+
"display_name": 'Office Lighting',
|
|
303
|
+
"watts_per_area": 10, # lighting watts per square meter of floor area
|
|
304
|
+
"schedule": "Office Lighting Schedule", # Schedule identifier
|
|
305
|
+
"return_air_fraction": 0, # fraction of heat going to return air
|
|
306
|
+
"radiant_fraction": 0.32, # fraction of heat that is long wave radiant
|
|
307
|
+
"visible_fraction": 0.25 # fraction of heat that is short wave visible
|
|
308
|
+
}
|
|
309
|
+
"""
|
|
310
|
+
assert data['type'] == 'LightingAbridged', \
|
|
311
|
+
'Expected LightingAbridged dictionary. Got {}.'.format(data['type'])
|
|
312
|
+
try:
|
|
313
|
+
sched = schedule_dict[data['schedule']]
|
|
314
|
+
except KeyError as e:
|
|
315
|
+
raise ValueError('Failed to find {} in the schedule_dict.'.format(e))
|
|
316
|
+
ret_fract, rad_fract, vis_fract = cls._optional_dict_keys(data)
|
|
317
|
+
new_obj = cls(data['identifier'], data['watts_per_area'], sched,
|
|
318
|
+
ret_fract, rad_fract, vis_fract)
|
|
319
|
+
if 'user_data' in data and data['user_data'] is not None:
|
|
320
|
+
new_obj.user_data = data['user_data']
|
|
321
|
+
if 'properties' in data and data['properties'] is not None:
|
|
322
|
+
new_obj.properties._load_extension_attr_from_dict(data['properties'])
|
|
323
|
+
return cls._apply_optional_dict_props(new_obj, data)
|
|
324
|
+
|
|
325
|
+
def to_idf(self, zone_identifier):
|
|
326
|
+
"""IDF string representation of Lighting object.
|
|
327
|
+
|
|
328
|
+
Note that this method only outputs a single string for the Lights object and,
|
|
329
|
+
to write everything needed to describe the object into an IDF, this object's
|
|
330
|
+
schedule must also be written. This is done to give more control over the
|
|
331
|
+
export process since you typically want to check whether these schedules are
|
|
332
|
+
used by multiple Lighting objects and write the schedule into the IDF only once.
|
|
333
|
+
|
|
334
|
+
Args:
|
|
335
|
+
zone_identifier: Text for the zone identifier that the Lights object
|
|
336
|
+
is assigned to.
|
|
337
|
+
|
|
338
|
+
.. code-block:: shell
|
|
339
|
+
|
|
340
|
+
Lights,
|
|
341
|
+
RIGHT FORK Lights 1, !- Name
|
|
342
|
+
RIGHT FORK, !- Zone Name
|
|
343
|
+
Office Lighting, !- SCHEDULE Name
|
|
344
|
+
Watts/Area, !- Design Level calculation method
|
|
345
|
+
, !- Lighting Level {W}
|
|
346
|
+
10, !- Lighting Per Floor Area {W/m2}
|
|
347
|
+
, !- Lighting Per Person {W/ppl}
|
|
348
|
+
0.0000000E+00, !- Return Air Fraction
|
|
349
|
+
0.4000000, !- Fraction Radiant
|
|
350
|
+
0.2000000; !- Fraction Visible
|
|
351
|
+
"""
|
|
352
|
+
values = ('{}..{}'.format(self.identifier, zone_identifier), zone_identifier,
|
|
353
|
+
self.schedule.identifier, 'Watts/Area', '', self.watts_per_area, '',
|
|
354
|
+
self.return_air_fraction, self.radiant_fraction, self.visible_fraction)
|
|
355
|
+
comments = ('name', 'zone name', 'schedule name', 'lighting level method',
|
|
356
|
+
'lighting power level {W}', 'lighting per floor area {W/m2}',
|
|
357
|
+
'lighting per person {W/ppl}', 'return air fraction',
|
|
358
|
+
'radiant fraction', 'visible fraction')
|
|
359
|
+
return generate_idf_string('Lights', values, comments)
|
|
360
|
+
|
|
361
|
+
def to_dict(self, abridged=False):
|
|
362
|
+
"""Lighting dictionary representation.
|
|
363
|
+
|
|
364
|
+
Args:
|
|
365
|
+
abridged: Boolean to note whether the full dictionary describing the
|
|
366
|
+
object should be returned (False) or just an abridged version (True),
|
|
367
|
+
which only specifies the identifiers of schedules. Default: False.
|
|
368
|
+
"""
|
|
369
|
+
base = {'type': 'Lighting'} if not abridged else {'type': 'LightingAbridged'}
|
|
370
|
+
base['identifier'] = self.identifier
|
|
371
|
+
base['watts_per_area'] = self.watts_per_area
|
|
372
|
+
base['return_air_fraction'] = self.return_air_fraction
|
|
373
|
+
base['radiant_fraction'] = self.radiant_fraction
|
|
374
|
+
base['visible_fraction'] = self.visible_fraction
|
|
375
|
+
base['schedule'] = self.schedule.to_dict() if not \
|
|
376
|
+
abridged else self.schedule.identifier
|
|
377
|
+
if self._display_name is not None:
|
|
378
|
+
base['display_name'] = self.display_name
|
|
379
|
+
if self._baseline_watts_per_area is not None:
|
|
380
|
+
base['baseline_watts_per_area'] = self._baseline_watts_per_area
|
|
381
|
+
if self._user_data is not None:
|
|
382
|
+
base['user_data'] = self._user_data
|
|
383
|
+
prop_dict = self.properties.to_dict()
|
|
384
|
+
if prop_dict is not None:
|
|
385
|
+
base['properties'] = prop_dict
|
|
386
|
+
return base
|
|
387
|
+
|
|
388
|
+
@staticmethod
|
|
389
|
+
def average(identifier, lightings, weights=None, timestep_resolution=1):
|
|
390
|
+
"""Get a Lighting object that's a weighted average between other Lighting objects.
|
|
391
|
+
|
|
392
|
+
Args:
|
|
393
|
+
identifier: Text string for a unique ID for the new averaged Lighting.
|
|
394
|
+
Must be < 100 characters and not contain any EnergyPlus special
|
|
395
|
+
characters. This will be used to identify the object across a model
|
|
396
|
+
and in the exported IDF.
|
|
397
|
+
lightings: A list of Lighting objects that will be averaged together to make
|
|
398
|
+
a new Lighting.
|
|
399
|
+
weights: An optional list of fractional numbers with the same length
|
|
400
|
+
as the input lightings. These will be used to weight each of the
|
|
401
|
+
Lighting objects in the resulting average. Note that these weights
|
|
402
|
+
can sum to less than 1 in which case the average watts_per_area will
|
|
403
|
+
assume 0 for the unaccounted fraction of the weights.
|
|
404
|
+
If None, the objects will be weighted equally. Default: None.
|
|
405
|
+
timestep_resolution: An optional integer for the timestep resolution
|
|
406
|
+
at which the schedules will be averaged. Any schedule details
|
|
407
|
+
smaller than this timestep will be lost in the averaging process.
|
|
408
|
+
Default: 1.
|
|
409
|
+
"""
|
|
410
|
+
weights, u_weights = Lighting._check_avg_weights(lightings, weights, 'Lighting')
|
|
411
|
+
|
|
412
|
+
# calculate the average values
|
|
413
|
+
lpd = sum([li.watts_per_area * w for li, w in zip(lightings, weights)])
|
|
414
|
+
ret_fract = sum(
|
|
415
|
+
[li.return_air_fraction * w for li, w in zip(lightings, u_weights)])
|
|
416
|
+
rad_fract = sum(
|
|
417
|
+
[li.radiant_fraction * w for li, w in zip(lightings, u_weights)])
|
|
418
|
+
vis_fract = sum(
|
|
419
|
+
[li.visible_fraction * w for li, w in zip(lightings, u_weights)])
|
|
420
|
+
|
|
421
|
+
# calculate the average schedules
|
|
422
|
+
sched = Lighting._average_schedule(
|
|
423
|
+
'{} Schedule'.format(identifier), [li.schedule for li in lightings],
|
|
424
|
+
u_weights, timestep_resolution)
|
|
425
|
+
|
|
426
|
+
# return the averaged lighting object
|
|
427
|
+
return Lighting(identifier, lpd, sched, ret_fract, rad_fract, vis_fract)
|
|
428
|
+
|
|
429
|
+
def _check_fractions(self):
|
|
430
|
+
tot = (self._return_air_fraction, self._radiant_fraction, self._visible_fraction)
|
|
431
|
+
assert sum(tot) <= 1 + 1e-9, 'Sum of lighting return_air_fraction, ' \
|
|
432
|
+
'radiant_fraction and visible_fraction ({}) is greater than ' \
|
|
433
|
+
'1.'.format(sum(tot))
|
|
434
|
+
|
|
435
|
+
@staticmethod
|
|
436
|
+
def _optional_dict_keys(data):
|
|
437
|
+
"""Get the optional keys from a Lighting dictionary."""
|
|
438
|
+
ret_fract = data['return_air_fraction'] if 'return_air_fraction' in data else 0
|
|
439
|
+
rad_fract = data['radiant_fraction'] if 'radiant_fraction' in data else 0.32
|
|
440
|
+
vis_fract = data['visible_fraction'] if 'visible_fraction' in data else 0.25
|
|
441
|
+
return ret_fract, rad_fract, vis_fract
|
|
442
|
+
|
|
443
|
+
@staticmethod
|
|
444
|
+
def _apply_optional_dict_props(new_obj, data):
|
|
445
|
+
"""Apply optional properties like display_name to an object from a dictionary."""
|
|
446
|
+
if 'display_name' in data and data['display_name'] is not None:
|
|
447
|
+
new_obj.display_name = data['display_name']
|
|
448
|
+
if 'baseline_watts_per_area' in data and \
|
|
449
|
+
data['baseline_watts_per_area'] is not None:
|
|
450
|
+
new_obj.baseline_watts_per_area = data['baseline_watts_per_area']
|
|
451
|
+
return new_obj
|
|
452
|
+
|
|
453
|
+
def __key(self):
|
|
454
|
+
"""A tuple based on the object properties, useful for hashing."""
|
|
455
|
+
return (self.identifier, self.watts_per_area, hash(self.schedule),
|
|
456
|
+
self.return_air_fraction, self.radiant_fraction, self.visible_fraction)
|
|
457
|
+
|
|
458
|
+
def __hash__(self):
|
|
459
|
+
return hash(self.__key())
|
|
460
|
+
|
|
461
|
+
def __eq__(self, other):
|
|
462
|
+
return isinstance(other, Lighting) and self.__key() == other.__key()
|
|
463
|
+
|
|
464
|
+
def __ne__(self, other):
|
|
465
|
+
return not self.__eq__(other)
|
|
466
|
+
|
|
467
|
+
def __copy__(self):
|
|
468
|
+
new_obj = Lighting(
|
|
469
|
+
self.identifier, self.watts_per_area, self.schedule,
|
|
470
|
+
self.return_air_fraction, self.radiant_fraction, self.visible_fraction)
|
|
471
|
+
new_obj._display_name = self._display_name
|
|
472
|
+
new_obj._baseline_watts_per_area = self._baseline_watts_per_area
|
|
473
|
+
new_obj._user_data = None if self._user_data is None else self._user_data.copy()
|
|
474
|
+
new_obj._properties._duplicate_extension_attr(self._properties)
|
|
475
|
+
return new_obj
|
|
476
|
+
|
|
477
|
+
def __repr__(self):
|
|
478
|
+
return 'Lighting: {} [{} W/m2] [schedule: {}]'.format(
|
|
479
|
+
self.display_name, round(self.watts_per_area, 1),
|
|
480
|
+
self.schedule.display_name)
|