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.
Files changed (162) hide show
  1. honeybee_energy/__init__.py +24 -0
  2. honeybee_energy/__main__.py +4 -0
  3. honeybee_energy/_extend_honeybee.py +145 -0
  4. honeybee_energy/altnumber.py +21 -0
  5. honeybee_energy/baseline/__init__.py +2 -0
  6. honeybee_energy/baseline/create.py +608 -0
  7. honeybee_energy/baseline/data/__init__.py +1 -0
  8. honeybee_energy/baseline/data/constructions.csv +64 -0
  9. honeybee_energy/baseline/data/fen_ratios.csv +15 -0
  10. honeybee_energy/baseline/data/lpd_building.csv +21 -0
  11. honeybee_energy/baseline/data/pci_2016.csv +22 -0
  12. honeybee_energy/baseline/data/pci_2019.csv +22 -0
  13. honeybee_energy/baseline/data/pci_2022.csv +22 -0
  14. honeybee_energy/baseline/data/shw.csv +21 -0
  15. honeybee_energy/baseline/pci.py +512 -0
  16. honeybee_energy/baseline/result.py +371 -0
  17. honeybee_energy/boundarycondition.py +128 -0
  18. honeybee_energy/cli/__init__.py +69 -0
  19. honeybee_energy/cli/baseline.py +475 -0
  20. honeybee_energy/cli/edit.py +327 -0
  21. honeybee_energy/cli/lib.py +1154 -0
  22. honeybee_energy/cli/result.py +810 -0
  23. honeybee_energy/cli/setconfig.py +124 -0
  24. honeybee_energy/cli/settings.py +569 -0
  25. honeybee_energy/cli/simulate.py +380 -0
  26. honeybee_energy/cli/translate.py +1714 -0
  27. honeybee_energy/cli/validate.py +224 -0
  28. honeybee_energy/config.json +11 -0
  29. honeybee_energy/config.py +842 -0
  30. honeybee_energy/construction/__init__.py +1 -0
  31. honeybee_energy/construction/_base.py +374 -0
  32. honeybee_energy/construction/air.py +325 -0
  33. honeybee_energy/construction/dictutil.py +89 -0
  34. honeybee_energy/construction/dynamic.py +607 -0
  35. honeybee_energy/construction/opaque.py +460 -0
  36. honeybee_energy/construction/shade.py +319 -0
  37. honeybee_energy/construction/window.py +1096 -0
  38. honeybee_energy/construction/windowshade.py +847 -0
  39. honeybee_energy/constructionset.py +1655 -0
  40. honeybee_energy/dictutil.py +56 -0
  41. honeybee_energy/generator/__init__.py +5 -0
  42. honeybee_energy/generator/loadcenter.py +204 -0
  43. honeybee_energy/generator/pv.py +535 -0
  44. honeybee_energy/hvac/__init__.py +21 -0
  45. honeybee_energy/hvac/_base.py +124 -0
  46. honeybee_energy/hvac/_template.py +270 -0
  47. honeybee_energy/hvac/allair/__init__.py +22 -0
  48. honeybee_energy/hvac/allair/_base.py +349 -0
  49. honeybee_energy/hvac/allair/furnace.py +168 -0
  50. honeybee_energy/hvac/allair/psz.py +131 -0
  51. honeybee_energy/hvac/allair/ptac.py +163 -0
  52. honeybee_energy/hvac/allair/pvav.py +109 -0
  53. honeybee_energy/hvac/allair/vav.py +128 -0
  54. honeybee_energy/hvac/detailed.py +337 -0
  55. honeybee_energy/hvac/doas/__init__.py +28 -0
  56. honeybee_energy/hvac/doas/_base.py +345 -0
  57. honeybee_energy/hvac/doas/fcu.py +127 -0
  58. honeybee_energy/hvac/doas/radiant.py +329 -0
  59. honeybee_energy/hvac/doas/vrf.py +81 -0
  60. honeybee_energy/hvac/doas/wshp.py +91 -0
  61. honeybee_energy/hvac/heatcool/__init__.py +23 -0
  62. honeybee_energy/hvac/heatcool/_base.py +177 -0
  63. honeybee_energy/hvac/heatcool/baseboard.py +61 -0
  64. honeybee_energy/hvac/heatcool/evapcool.py +72 -0
  65. honeybee_energy/hvac/heatcool/fcu.py +92 -0
  66. honeybee_energy/hvac/heatcool/gasunit.py +53 -0
  67. honeybee_energy/hvac/heatcool/radiant.py +269 -0
  68. honeybee_energy/hvac/heatcool/residential.py +77 -0
  69. honeybee_energy/hvac/heatcool/vrf.py +54 -0
  70. honeybee_energy/hvac/heatcool/windowac.py +70 -0
  71. honeybee_energy/hvac/heatcool/wshp.py +62 -0
  72. honeybee_energy/hvac/idealair.py +699 -0
  73. honeybee_energy/internalmass.py +310 -0
  74. honeybee_energy/lib/__init__.py +1 -0
  75. honeybee_energy/lib/_loadconstructions.py +194 -0
  76. honeybee_energy/lib/_loadconstructionsets.py +117 -0
  77. honeybee_energy/lib/_loadmaterials.py +83 -0
  78. honeybee_energy/lib/_loadprogramtypes.py +125 -0
  79. honeybee_energy/lib/_loadschedules.py +87 -0
  80. honeybee_energy/lib/_loadtypelimits.py +64 -0
  81. honeybee_energy/lib/constructions.py +207 -0
  82. honeybee_energy/lib/constructionsets.py +95 -0
  83. honeybee_energy/lib/materials.py +67 -0
  84. honeybee_energy/lib/programtypes.py +125 -0
  85. honeybee_energy/lib/schedules.py +61 -0
  86. honeybee_energy/lib/scheduletypelimits.py +31 -0
  87. honeybee_energy/load/__init__.py +1 -0
  88. honeybee_energy/load/_base.py +190 -0
  89. honeybee_energy/load/daylight.py +397 -0
  90. honeybee_energy/load/dictutil.py +47 -0
  91. honeybee_energy/load/equipment.py +771 -0
  92. honeybee_energy/load/hotwater.py +543 -0
  93. honeybee_energy/load/infiltration.py +460 -0
  94. honeybee_energy/load/lighting.py +480 -0
  95. honeybee_energy/load/people.py +497 -0
  96. honeybee_energy/load/process.py +472 -0
  97. honeybee_energy/load/setpoint.py +816 -0
  98. honeybee_energy/load/ventilation.py +550 -0
  99. honeybee_energy/material/__init__.py +1 -0
  100. honeybee_energy/material/_base.py +166 -0
  101. honeybee_energy/material/dictutil.py +59 -0
  102. honeybee_energy/material/frame.py +367 -0
  103. honeybee_energy/material/gas.py +1087 -0
  104. honeybee_energy/material/glazing.py +854 -0
  105. honeybee_energy/material/opaque.py +1351 -0
  106. honeybee_energy/material/shade.py +1360 -0
  107. honeybee_energy/measure.py +472 -0
  108. honeybee_energy/programtype.py +723 -0
  109. honeybee_energy/properties/__init__.py +1 -0
  110. honeybee_energy/properties/aperture.py +333 -0
  111. honeybee_energy/properties/door.py +342 -0
  112. honeybee_energy/properties/extension.py +244 -0
  113. honeybee_energy/properties/face.py +274 -0
  114. honeybee_energy/properties/model.py +2640 -0
  115. honeybee_energy/properties/room.py +1747 -0
  116. honeybee_energy/properties/shade.py +314 -0
  117. honeybee_energy/properties/shademesh.py +262 -0
  118. honeybee_energy/reader.py +48 -0
  119. honeybee_energy/result/__init__.py +1 -0
  120. honeybee_energy/result/colorobj.py +648 -0
  121. honeybee_energy/result/emissions.py +290 -0
  122. honeybee_energy/result/err.py +101 -0
  123. honeybee_energy/result/eui.py +100 -0
  124. honeybee_energy/result/generation.py +160 -0
  125. honeybee_energy/result/loadbalance.py +890 -0
  126. honeybee_energy/result/match.py +202 -0
  127. honeybee_energy/result/osw.py +90 -0
  128. honeybee_energy/result/rdd.py +59 -0
  129. honeybee_energy/result/zsz.py +190 -0
  130. honeybee_energy/run.py +1577 -0
  131. honeybee_energy/schedule/__init__.py +1 -0
  132. honeybee_energy/schedule/day.py +626 -0
  133. honeybee_energy/schedule/dictutil.py +59 -0
  134. honeybee_energy/schedule/fixedinterval.py +1012 -0
  135. honeybee_energy/schedule/rule.py +619 -0
  136. honeybee_energy/schedule/ruleset.py +1867 -0
  137. honeybee_energy/schedule/typelimit.py +310 -0
  138. honeybee_energy/shw.py +315 -0
  139. honeybee_energy/simulation/__init__.py +1 -0
  140. honeybee_energy/simulation/control.py +214 -0
  141. honeybee_energy/simulation/daylightsaving.py +185 -0
  142. honeybee_energy/simulation/dictutil.py +51 -0
  143. honeybee_energy/simulation/output.py +646 -0
  144. honeybee_energy/simulation/parameter.py +606 -0
  145. honeybee_energy/simulation/runperiod.py +443 -0
  146. honeybee_energy/simulation/shadowcalculation.py +295 -0
  147. honeybee_energy/simulation/sizing.py +546 -0
  148. honeybee_energy/ventcool/__init__.py +5 -0
  149. honeybee_energy/ventcool/_crack_data.py +91 -0
  150. honeybee_energy/ventcool/afn.py +289 -0
  151. honeybee_energy/ventcool/control.py +269 -0
  152. honeybee_energy/ventcool/crack.py +126 -0
  153. honeybee_energy/ventcool/fan.py +493 -0
  154. honeybee_energy/ventcool/opening.py +365 -0
  155. honeybee_energy/ventcool/simulation.py +314 -0
  156. honeybee_energy/writer.py +1078 -0
  157. honeybee_energy-1.116.106.dist-info/METADATA +113 -0
  158. honeybee_energy-1.116.106.dist-info/RECORD +162 -0
  159. honeybee_energy-1.116.106.dist-info/WHEEL +5 -0
  160. honeybee_energy-1.116.106.dist-info/entry_points.txt +2 -0
  161. honeybee_energy-1.116.106.dist-info/licenses/LICENSE +661 -0
  162. honeybee_energy-1.116.106.dist-info/top_level.txt +1 -0
@@ -0,0 +1,535 @@
1
+ # coding=utf-8
2
+ """Photovoltaic properties that can be applied to Shades."""
3
+ from __future__ import division
4
+
5
+ from honeybee._lockable import lockable
6
+ from honeybee.typing import valid_ep_string, valid_string, float_in_range
7
+
8
+ from ..properties.extension import PVPropertiesProperties
9
+ from ..reader import parse_idf_string
10
+ from ..writer import generate_idf_string
11
+
12
+
13
+ @lockable
14
+ class PVProperties(object):
15
+ """Simple Photovoltaic properties that run using PVWatts to estimate electricity.
16
+
17
+ Args:
18
+ identifier: Text string for PV properties identifier. Must be < 100 characters
19
+ and not contain any EnergyPlus special characters. This will be used to
20
+ identify the object across a model and in the exported IDF.
21
+ rated_efficiency: A number between 0 and 1 for the rated nameplate efficiency
22
+ of the photovoltaic solar cells under standard test conditions (STC).
23
+ Standard test conditions are 1,000 Watts per square meter solar
24
+ irradiance, 25 degrees C cell temperature, and ASTM G173-03 standard
25
+ spectrum. Nameplate efficiencies reported by manufacturers are typically
26
+ under STC. Standard poly- or mono-crystalline silicon modules tend to have
27
+ rated efficiencies in the range of 14-17%. Premium high efficiency
28
+ mono-crystalline silicon modules with anti-reflective coatings can have
29
+ efficiencies in the range of 18-20%. Thin film photovoltaic modules
30
+ typically have efficiencies of 11% or less. (Default: 0.15 for standard
31
+ silicon solar cells).
32
+ active_area_fraction: The fraction of the parent Shade geometry that is
33
+ covered in active solar cells. This fraction includes the difference
34
+ between the PV panel (aka. PV module) area and the active cells within
35
+ the panel as well as any losses for how the (typically rectangular) panels
36
+ can be arranged on the Shade geometry. When the parent Shade geometry
37
+ represents just the solar panels, this fraction is typically around 0.9
38
+ given that the metal framing elements of the panel reduce the overall
39
+ active area. (Default: 0.9, assuming parent Shade geometry represents
40
+ only the PV panel geometry).
41
+ module_type: Text to indicate the type of solar module. This is used to
42
+ determine the temperature coefficients used in the simulation of the
43
+ photovoltaic modules. Choose from the three options below. If None,
44
+ the module_type will be inferred from the rated_efficiency of these
45
+ PVProperties using the rated efficiencies listed below. (Default: None).
46
+
47
+ * Standard - 12% <= rated_efficiency < 18%
48
+ * Premium - rated_efficiency >= 18%
49
+ * ThinFilm - rated_efficiency < 12%
50
+
51
+ mounting_type: Text to indicate the type of mounting and/or tracking used
52
+ for the photovoltaic array. Note that the OneAxis options have an axis
53
+ of rotation that is determined by the azimuth of the parent Shade
54
+ geometry. Also note that, in the case of one or two axis tracking,
55
+ shadows on the (static) parent Shade geometry still reduce the
56
+ electrical output, enabling the simulation to account for large
57
+ context geometry casting shadows on the array. However, the effects
58
+ of smaller detailed shading may be improperly accounted for and self
59
+ shading of the dynamic panel geometry is only accounted for via the
60
+ tracking_ground_coverage_ratio property on this object. Choose from
61
+ the following. (Default: FixedOpenRack).
62
+
63
+ * FixedOpenRack - ground or roof mounting where the air flows freely
64
+ * FixedRoofMounted - mounting flush with the roof with limited air flow
65
+ * OneAxis - a fixed tilt and azimuth, which define an axis of rotation
66
+ * OneAxisBacktracking - same as OneAxis but with controls to reduce self-shade
67
+ * TwoAxis - a dynamic tilt and azimuth that track the sun
68
+
69
+ system_loss_fraction: A number between 0 and 1 for the fraction of the
70
+ electricity output lost due to factors other than EPW climate conditions,
71
+ panel efficiency/type, active area, mounting, and inverter conversion from
72
+ DC to AC. Factors that should be accounted for in this input include
73
+ soiling, snow, wiring losses, electrical connection losses, manufacturer
74
+ defects/tolerances/mismatch in cell characteristics, losses from power
75
+ grid availability, and losses due to age or light-induced degradation.
76
+ Losses from these factors tend to be between 10-20% but can vary widely
77
+ depending on the installation, maintenance and the grid to which the
78
+ panels are connected. The loss_fraction_from_components staticmethod
79
+ on this class can be used to estimate this value from the various
80
+ factors that it is intended to account for. (Default: 0.14).
81
+ tracking_ground_coverage_ratio: A number between 0 and 1 that only applies to
82
+ arrays with one-axis tracking mounting_type. The ground coverage ratio (GCR)
83
+ is the ratio of module surface area to the area of the ground beneath
84
+ the array, which is used to account for self shading of single-axis panels
85
+ as they move to track the sun. A GCR of 0.5 means that, when the modules
86
+ are horizontal, half of the surface below the array is occupied by
87
+ the array. An array with wider spacing between rows of modules has a
88
+ lower GCR than one with narrower spacing. A GCR of 1 would be for an
89
+ array with no space between modules, and a GCR of 0 for infinite spacing
90
+ between rows. Typical values range from 0.3 to 0.6. (Default: 0.4).
91
+ """
92
+ __slots__ = (
93
+ '_identifier', '_display_name', '_rated_efficiency', '_active_area_fraction',
94
+ '_module_type', '_mounting_type', '_system_loss_fraction',
95
+ '_tracking_ground_coverage_ratio', '_locked', '_properties')
96
+ MODULE_TYPES = ('Standard', 'Premium', 'ThinFilm')
97
+ MOUNTING_TYPES = ('FixedOpenRack', 'FixedRoofMounted', 'OneAxis',
98
+ 'OneAxisBacktracking', 'TwoAxis')
99
+
100
+ def __init__(self, identifier, rated_efficiency=0.15, active_area_fraction=0.9,
101
+ module_type=None, mounting_type='FixedOpenRack',
102
+ system_loss_fraction=0.14, tracking_ground_coverage_ratio=0.4):
103
+ """Initialize PVProperties."""
104
+ # set the identifier and display name
105
+ self.identifier = identifier
106
+ self._display_name = None
107
+
108
+ # set the main features of the PV
109
+ self.rated_efficiency = rated_efficiency
110
+ self.active_area_fraction = active_area_fraction
111
+ self.module_type = module_type
112
+ self.mounting_type = mounting_type
113
+ self.system_loss_fraction = system_loss_fraction
114
+ self.tracking_ground_coverage_ratio = tracking_ground_coverage_ratio
115
+ self._properties = PVPropertiesProperties(self)
116
+
117
+ @property
118
+ def identifier(self):
119
+ """Get or set the text string for PV properties system identifier."""
120
+ return self._identifier
121
+
122
+ @identifier.setter
123
+ def identifier(self, identifier):
124
+ self._identifier = valid_ep_string(
125
+ identifier, 'photovoltaic properties identifier')
126
+
127
+ @property
128
+ def display_name(self):
129
+ """Get or set a string for the object name without any character restrictions.
130
+
131
+ If not set, this will be equal to the identifier.
132
+ """
133
+ if self._display_name is None:
134
+ return self._identifier
135
+ return self._display_name
136
+
137
+ @display_name.setter
138
+ def display_name(self, value):
139
+ if value is not None:
140
+ try:
141
+ value = str(value)
142
+ except UnicodeEncodeError: # Python 2 machine lacking the character set
143
+ pass # keep it as unicode
144
+ self._display_name = value
145
+
146
+ @property
147
+ def rated_efficiency(self):
148
+ """Get or set a number for the rated nameplate efficiency of the solar cells.
149
+
150
+ Standard poly- or mono-crystalline silicon modules tend to have rated
151
+ efficiencies in the range of 14-17%.
152
+
153
+ Premium high efficiency mono-crystalline silicon modules with anti-reflective
154
+ coatings can have efficiencies in the range of 18-20%.
155
+
156
+ Thin film photovoltaic modules typically have efficiencies of 11% or less.
157
+ """
158
+ return self._rated_efficiency
159
+
160
+ @rated_efficiency.setter
161
+ def rated_efficiency(self, value):
162
+ self._rated_efficiency = float_in_range(
163
+ value, 0.0, 1.0, 'photovoltaic rated efficiency')
164
+
165
+ @property
166
+ def active_area_fraction(self):
167
+ """Get or set a number for fraction of the parent covered in active solar cells.
168
+
169
+ This fraction includes the difference between the PV panel (aka. PV module) area
170
+ and the active cells within the panel as well as any losses for how
171
+ the (typically rectangular) panels can be arranged on the Shade geometry.
172
+ """
173
+ return self._active_area_fraction
174
+
175
+ @active_area_fraction.setter
176
+ def active_area_fraction(self, value):
177
+ self._active_area_fraction = float_in_range(
178
+ value, 0.0, 1.0, 'photovoltaic active area fraction')
179
+
180
+ @property
181
+ def module_type(self):
182
+ """Get or set text to indicate the type of photovoltaic module.
183
+
184
+ Choose from the following options:
185
+
186
+ * Standard
187
+ * Premium
188
+ * ThinFilm
189
+ """
190
+ if self._module_type is None:
191
+ if self.rated_efficiency < 0.12:
192
+ return 'ThinFilm'
193
+ elif self.rated_efficiency < 0.18:
194
+ return 'Standard'
195
+ return 'Premium'
196
+ return self._module_type
197
+
198
+ @module_type.setter
199
+ def module_type(self, value):
200
+ if value is not None:
201
+ clean_input = valid_string(value).lower()
202
+ for key in self.MODULE_TYPES:
203
+ if key.lower() == clean_input:
204
+ value = key
205
+ break
206
+ else:
207
+ raise ValueError(
208
+ 'PVProperties.module_type {} is not recognized.\nChoose from the '
209
+ 'following:\n{}'.format(value, self.MODULE_TYPES))
210
+ self._module_type = value
211
+
212
+ @property
213
+ def mounting_type(self):
214
+ """Get or set text to indicate the way the photovoltaic arrays are mounted.
215
+
216
+ Choose from the following options:
217
+
218
+ * FixedOpenRack
219
+ * FixedRoofMounted
220
+ * OneAxis
221
+ * OneAxisBacktracking
222
+ * TwoAxis
223
+ """
224
+ return self._mounting_type
225
+
226
+ @mounting_type.setter
227
+ def mounting_type(self, value):
228
+ clean_input = valid_string(value).lower()
229
+ for key in self.MOUNTING_TYPES:
230
+ if key.lower() == clean_input:
231
+ value = key
232
+ break
233
+ else:
234
+ raise ValueError(
235
+ 'PVProperties.mounting_type {} is not recognized.\nChoose from the '
236
+ 'following:\n{}'.format(value, self.MOUNTING_TYPES))
237
+ self._mounting_type = value
238
+
239
+ @property
240
+ def system_loss_fraction(self):
241
+ """Get or set a number for the fraction of the output lost due to other factors.
242
+
243
+ Factors that should be accounted for in this input include soiling, snow,
244
+ wiring losses, electrical connection losses, manufacturer defects/tolerances/
245
+ mismatch in cell characteristics, losses from power grid availability,
246
+ and losses due to age or light-induced degradation.
247
+ """
248
+ return self._system_loss_fraction
249
+
250
+ @system_loss_fraction.setter
251
+ def system_loss_fraction(self, value):
252
+ self._system_loss_fraction = float_in_range(
253
+ value, 0.0, 1.0, 'photovoltaic system loss fraction')
254
+
255
+ @property
256
+ def tracking_ground_coverage_ratio(self):
257
+ """Get or set a number between 0 and 1 for the ground coverage ratio.
258
+
259
+ This value only applies to systems using single-axis tracking and is
260
+ used to account for self shading of single-axis panels as they move
261
+ to track the sun.
262
+ """
263
+ return self._tracking_ground_coverage_ratio
264
+
265
+ @tracking_ground_coverage_ratio.setter
266
+ def tracking_ground_coverage_ratio(self, value):
267
+ self._tracking_ground_coverage_ratio = float_in_range(
268
+ value, 0.0, 1.0, 'photovoltaic tracking ground coverage ratio')
269
+
270
+ @property
271
+ def properties(self):
272
+ """Get the properties of the PVProperties."""
273
+ return self._properties
274
+
275
+ @classmethod
276
+ def from_idf(cls, idf_string, shade, active_area_fraction=0.9):
277
+ """Create a PVProperties object from a Generator:PVWatts IDF text string.
278
+
279
+ Note that the Generator:PVWatts idf_string must use the 'surface' array
280
+ geometry type in order to be successfully imported.
281
+
282
+ Args:
283
+ idf_string: A text string fully describing an EnergyPlus Generator:PVWatts
284
+ definition.
285
+ shade: The Honeybee Shade object to which the PV properties are assigned.
286
+ Note that the geometry of this Shade must be in meters for the
287
+ object to be loaded correctly.
288
+ active_area_fraction: The original active area fraction used to construct
289
+ the PVProperties object. This is needed to correctly derive
290
+ the PV efficiency from the system capacity.
291
+
292
+ Returns:
293
+ A PVProperties object from the IDF string.
294
+ """
295
+ # check the inputs
296
+ ep_strs = parse_idf_string(idf_string, 'Generator:PVWatts,')
297
+ # check the inputs
298
+ assert len(ep_strs) >= 9, 'PVProperties must use Surface array geometry ' \
299
+ 'type to be loaded from IDF to honeybee.'
300
+ assert ep_strs[9].upper() == shade.identifier.upper(), 'Generator:PVWatts ' \
301
+ 'surface "{}" is different from the assigned Shade "{}".'.format(
302
+ ep_strs[9].upper(), shade.identifier.upper())
303
+ # extract the properties from the string
304
+ watts_per_area = float(ep_strs[2]) / shade.area
305
+ eff = round((watts_per_area / active_area_fraction) / 1000, 3)
306
+ loss = 0.14
307
+ gcr = 0.4
308
+ try:
309
+ loss = ep_strs[5] if ep_strs[5] != '' else 0.14
310
+ gcr = ep_strs[10] if ep_strs[10] != '' else 0.4
311
+ except IndexError:
312
+ pass # shorter definition lacking ground coverage ratio
313
+
314
+ # return the PV properties object
315
+ obj_id = ep_strs[0].split('..')[0]
316
+ return cls(obj_id, eff, active_area_fraction, ep_strs[3], ep_strs[4], loss, gcr)
317
+
318
+ @classmethod
319
+ def from_dict(cls, data):
320
+ """Create a PVProperties object from a dictionary.
321
+
322
+ Args:
323
+ data: A PVProperties dictionary in following the format below.
324
+
325
+ .. code-block:: python
326
+
327
+ {
328
+ "type": "PVProperties",
329
+ "identifier": "Ablytek 270 W Monocrystalline", # identifier for the PV
330
+ "display_name": "Ablytek Module", # name for the PV
331
+ "rated_efficiency": 0.18, # Nameplate rated efficiency
332
+ "active_area_fraction": 0.92, # Active area fraction
333
+ "module_type": "Standard", # Type of solar module
334
+ "mounting_type": "FixedOpenRack", # Type of mounting and tracking
335
+ "system_loss_fraction": 0.16 # Fraction lost to outside factors
336
+ }
337
+ """
338
+ assert data['type'] == 'PVProperties', \
339
+ 'Expected PVProperties dictionary. Got {}.'.format(data['type'])
340
+
341
+ # extract the key features and properties while setting defaults
342
+ eff = data['rated_efficiency'] if 'rated_efficiency' in data and \
343
+ data['rated_efficiency'] is not None else 0.15
344
+ act = data['active_area_fraction'] if 'active_area_fraction' in data and \
345
+ data['active_area_fraction'] is not None else 0.9
346
+ mod_type = data['module_type'] if 'module_type' in data and \
347
+ data['module_type'] is not None else 'Standard'
348
+ mount = data['mounting_type'] if 'mounting_type' in data and \
349
+ data['mounting_type'] is not None else 'FixedOpenRack'
350
+ loss = data['system_loss_fraction'] if 'system_loss_fraction' in data and \
351
+ data['system_loss_fraction'] is not None else 0.14
352
+ gcr = data['tracking_ground_coverage_ratio'] \
353
+ if 'tracking_ground_coverage_ratio' in data and \
354
+ data['tracking_ground_coverage_ratio'] is not None else 0.4
355
+
356
+ new_obj = cls(data['identifier'], eff, act, mod_type, mount, loss, gcr)
357
+ if 'display_name' in data and data['display_name'] is not None:
358
+ new_obj.display_name = data['display_name']
359
+ if 'properties' in data and data['properties'] is not None:
360
+ new_obj.properties._load_extension_attr_from_dict(data['properties'])
361
+ return new_obj
362
+
363
+ def to_idf(self, shade):
364
+ """IDF string representation of PVProperties object.
365
+
366
+ Args:
367
+ shade: A Honeybee Shade for which the specific IDF string will
368
+ be generated. Note that the geometry of this shade must be in
369
+ meters in order for the returned IDF string to have correct values.
370
+
371
+ .. code-block:: shell
372
+
373
+ Generator:PVWatts,
374
+ Photovoltaic Array 1..Shade_d5d1b861, !- PV generator name
375
+ , !- PVWatts version
376
+ 4050, !- DC system capacity {W}
377
+ Standard, !- module type
378
+ FixedOpenRack, !- array type
379
+ 0.14, !- system losses
380
+ Surface, !- array geometry type
381
+ , !- tilt angle {deg}
382
+ , !- azimuth angle {deg}
383
+ Shade_d5d1b861, !- surface name
384
+ 0.4; !- ground coverage ratio
385
+ """
386
+ # compte the system capacity and other factors from the shade
387
+ idf_id = '{}..{}'.format(self.identifier, shade.identifier)
388
+ rated_watts = self.rated_efficiency * 1000 # 1000W/m2 of solar irradiance
389
+ sys_cap = int(shade.area * self.active_area_fraction * rated_watts)
390
+ # write the IDF string
391
+ values = (idf_id, '', sys_cap, self.module_type, self.mounting_type,
392
+ self.system_loss_fraction, 'Surface', '', '', shade.identifier,
393
+ self.tracking_ground_coverage_ratio)
394
+ comments = (
395
+ 'PV generator name', 'PVWatts version', 'DC system capacity {W}',
396
+ 'module type', 'array type', 'system losses', 'array geometry type',
397
+ 'tilt angle {deg}', 'azimuth angle {deg}', 'surface name',
398
+ 'ground coverage ratio')
399
+ return generate_idf_string('Generator:PVWatts', values, comments)
400
+
401
+ def to_dict(self):
402
+ """PVProperties dictionary representation.
403
+
404
+ Args:
405
+ abridged: Boolean to note whether the full dictionary describing the
406
+ object should be returned (False) or just an abridged version (True),
407
+ which only specifies the identifiers of schedules. (Default: False).
408
+ """
409
+ base = {'type': 'PVProperties'}
410
+ base['identifier'] = self.identifier
411
+ base['rated_efficiency'] = self.rated_efficiency
412
+ base['active_area_fraction'] = self.active_area_fraction
413
+ base['module_type'] = self.module_type
414
+ base['mounting_type'] = self.mounting_type
415
+ base['system_loss_fraction'] = self.system_loss_fraction
416
+ if self.mounting_type in ('OneAxis', 'OneAxisBacktracking') or \
417
+ self.tracking_ground_coverage_ratio != 0.4:
418
+ base['tracking_ground_coverage_ratio'] = self.tracking_ground_coverage_ratio
419
+ if self._display_name is not None:
420
+ base['display_name'] = self.display_name
421
+ prop_dict = self.properties.to_dict()
422
+ if prop_dict is not None:
423
+ base['properties'] = prop_dict
424
+ return base
425
+
426
+ def duplicate(self):
427
+ """Get a copy of this object."""
428
+ return self.__copy__()
429
+
430
+ def ToString(self):
431
+ """Overwrite .NET ToString."""
432
+ return self.__repr__()
433
+
434
+ @staticmethod
435
+ def loss_fraction_from_components(
436
+ age=0.045, light_induced_degradation=0.015, soiling=0.02, snow=0.0,
437
+ manufacturer_nameplate_tolerance=0.01, cell_characteristic_mismatch=0.02,
438
+ wiring=0.02, electrical_connection=0.005, grid_availability=0.015):
439
+ """Compute an estimate for system_loss_fraction from individual factors.
440
+
441
+ This method is intended to help account for all of the factors outside of
442
+ those modeled by EnergyPlus (and PVWatts), which can influence the annual
443
+ energy harvested by photovoltaic arrays. It also gives a rough understanding
444
+ of where the default value of 0.14 could originate from given the default
445
+ values used for the various factors on this method.
446
+
447
+ Note that this loss term does not include the effects of the climate's
448
+ temperature/radiation, site shading, panel efficiency/type, active area,
449
+ mounting, or inverter conversion from DC to AC since all of these effects
450
+ are modeled explicitly by EnergyPlus.
451
+
452
+ Args:
453
+ age: A number between 0 and 1 for the fraction of output lost due to the
454
+ aging/weathering of the photovoltaic panels over time. This term is
455
+ intended to account for gradual degradation through exposure to
456
+ the elements including corrosion, thermal expansion/contraction,
457
+ erosion. Typical conservative estimates assume a degradation
458
+ of 0.01 (or 1%) per year such that, on the 20th year, the panels will
459
+ be performing at 19% less than their original output. (Default: 0.045
460
+ for the average aging expected in the first 10 years of operation).
461
+ light_induced_degradation: A number between 0 and 1 for the fraction
462
+ of output lost due to light-induced degradation of the photovoltaic
463
+ cells, which is common during the first few months of operation and
464
+ results in the cells having a different efficiency than the
465
+ nameplate rating. (Default: 0.02).
466
+ soiling: A number between 0 and 1 for the fraction of output lost due to
467
+ dust, dirt, leaves, wildlife droppings, and other foreign matter
468
+ on the surface of the PV module that prevent solar radiation from
469
+ reaching the cells. Soiling is highly dependent on climate,
470
+ installation conditions, and the frequency with which the panels
471
+ are cleaned. The greatest soiling losses typically occur in
472
+ high-traffic, high-pollution areas with infrequent rain and
473
+ infrequent cleaning. (Default: 0.02).
474
+ snow: A number between 0 and 1 for the fraction of output lost due to
475
+ snow covering the panels. This is common in cases where panels have
476
+ a low slope and are not immediately cleared of snow. (Default: 0.0
477
+ assuming installation in a climate without snow).
478
+ manufacturer_nameplate_tolerance: A number between 0 and 1 for the
479
+ fraction of of output lost due to tolerance that the manufacturer
480
+ follows in the performance of its product from the nameplate
481
+ rating. (Default: 0.01).
482
+ cell_characteristic_mismatch: A number between 0 and 1 for the fraction of
483
+ output lost due to manufacturing imperfections between modules in
484
+ the array, which cause the modules to have slightly different
485
+ current-voltage characteristics and result in a reduction of
486
+ performance from the nameplate rating. (Default: 0.02).
487
+ wiring: A number between 0 and 1 for the fraction of output lost due to
488
+ resistive losses in the wires connecting the various parts of the
489
+ photovoltaic system. Setups that require longer wires and bigger
490
+ distances between equipment will have higher losses for this
491
+ term. (Default: 0.02).
492
+ electrical_connection: A number between 0 and 1 for the fraction of output
493
+ lost due to resistive losses in the electrical connectors across the
494
+ photovoltaic system. (Default: 0.005).
495
+ grid_availability: A number between 0 and 1 for the fraction of output
496
+ lost due to maintenance shutdowns, grid outages, inability for the
497
+ grid to accept input, and other operational factors. (Default: 0.015).
498
+ """
499
+ all_factors = (
500
+ age, light_induced_degradation, soiling, snow,
501
+ manufacturer_nameplate_tolerance, cell_characteristic_mismatch,
502
+ wiring, electrical_connection, grid_availability
503
+ )
504
+ downrate_factor = 1
505
+ for factor in all_factors:
506
+ downrate_factor = downrate_factor * (1 - factor)
507
+ return 1 - downrate_factor
508
+
509
+ def __key(self):
510
+ """A tuple based on the object properties, useful for hashing."""
511
+ return (self.identifier, self.rated_efficiency, self.active_area_fraction,
512
+ self.module_type, self.mounting_type, self.system_loss_fraction,
513
+ self.tracking_ground_coverage_ratio)
514
+
515
+ def __hash__(self):
516
+ return hash(self.__key())
517
+
518
+ def __eq__(self, other):
519
+ return isinstance(other, PVProperties) and self.__key() == other.__key()
520
+
521
+ def __ne__(self, other):
522
+ return not self.__eq__(other)
523
+
524
+ def __copy__(self):
525
+ new_obj = PVProperties(
526
+ self.identifier, self._rated_efficiency, self._active_area_fraction,
527
+ self._module_type, self._mounting_type, self._system_loss_fraction,
528
+ self._tracking_ground_coverage_ratio)
529
+ new_obj._display_name = self._display_name
530
+ new_obj._properties._duplicate_extension_attr(self._properties)
531
+ return new_obj
532
+
533
+ def __repr__(self):
534
+ return 'PVProperties: {} [efficiency {}%]'.format(
535
+ self.display_name, round(self.rated_efficiency, 1))
@@ -0,0 +1,21 @@
1
+ """honeybee-energy HVAC definitions.
2
+
3
+ To extend this sub-package with a new HVAC system template, add a module that contains
4
+ a single class inheriting from _HVACSystem in hvac._base. Then, add the class to the
5
+ HVAC_TYPES_DICT using the name of the class as the key.
6
+
7
+ Properties:
8
+ * HVAC_TYPES_DICT: A dictionary containing pointers to the classes of each
9
+ HVAC system. The keys of this dictionary are the names of the HVAC classes.
10
+ """
11
+ from .idealair import IdealAirSystem
12
+ from .allair import HVAC_TYPES_DICT as allair_types
13
+ from .doas import HVAC_TYPES_DICT as doas_types
14
+ from .heatcool import HVAC_TYPES_DICT as heatcool_types
15
+ from .detailed import DetailedHVAC
16
+
17
+ HVAC_TYPES_DICT = {'IdealAirSystem': IdealAirSystem}
18
+ HVAC_TYPES_DICT.update(allair_types)
19
+ HVAC_TYPES_DICT.update(doas_types)
20
+ HVAC_TYPES_DICT.update(heatcool_types)
21
+ HVAC_TYPES_DICT['DetailedHVAC'] = DetailedHVAC
@@ -0,0 +1,124 @@
1
+ # coding=utf-8
2
+ """Base class to be used for all HVAC systems."""
3
+ from __future__ import division
4
+
5
+ from ..schedule.ruleset import ScheduleRuleset
6
+ from ..schedule.fixedinterval import ScheduleFixedInterval
7
+
8
+ from honeybee._lockable import lockable
9
+ from honeybee.typing import valid_ep_string
10
+
11
+
12
+ @lockable
13
+ class _HVACSystem(object):
14
+ """Base class to be used for all HVAC systems
15
+
16
+ Args:
17
+ identifier: Text string for system identifier. Must be < 100 characters
18
+ and not contain any EnergyPlus special characters. This will be used to
19
+ identify the object across a model and in the exported IDF.
20
+
21
+ Properties:
22
+ * identifier
23
+ * display_name
24
+ * schedules
25
+ * user_data
26
+ """
27
+ __slots__ = ('_identifier', '_display_name', '_locked', '_user_data')
28
+
29
+ def __init__(self, identifier):
30
+ """Initialize HVACSystem."""
31
+ self.identifier = identifier
32
+ self._display_name = None
33
+ self._user_data = None
34
+
35
+ @property
36
+ def identifier(self):
37
+ """Get or set the text string for HVAC system identifier."""
38
+ return self._identifier
39
+
40
+ @identifier.setter
41
+ def identifier(self, identifier):
42
+ self._identifier = valid_ep_string(identifier, 'HVAC system identifier')
43
+
44
+ @property
45
+ def display_name(self):
46
+ """Get or set a string for the object name without any character restrictions.
47
+
48
+ If not set, this will be equal to the identifier.
49
+ """
50
+ if self._display_name is None:
51
+ return self._identifier
52
+ return self._display_name
53
+
54
+ @display_name.setter
55
+ def display_name(self, value):
56
+ if value is not None:
57
+ try:
58
+ value = str(value)
59
+ except UnicodeEncodeError: # Python 2 machine lacking the character set
60
+ pass # keep it as unicode
61
+ self._display_name = value
62
+
63
+ @property
64
+ def schedules(self):
65
+ """Get an array of all the schedules associated with the HVAC system.
66
+
67
+ This property should be overwritten in each of the classes inheriting from
68
+ the HVACSystem base class since each HVAC system is likely to have it's
69
+ own unique places where schedules are assigned. At a minimum, this property
70
+ should return heating/cooling availability schedules.
71
+ """
72
+ return ()
73
+
74
+ @property
75
+ def user_data(self):
76
+ """Get or set an optional dictionary for additional meta data for this object.
77
+
78
+ This will be None until it has been set. All keys and values of this
79
+ dictionary should be of a standard Python type to ensure correct
80
+ serialization of the object to/from JSON (eg. str, float, int, list, dict)
81
+ """
82
+ return self._user_data
83
+
84
+ @user_data.setter
85
+ def user_data(self, value):
86
+ if value is not None:
87
+ assert isinstance(value, dict), 'Expected dictionary for honeybee_energy' \
88
+ 'object user_data. Got {}.'.format(type(value))
89
+ self._user_data = value
90
+
91
+ def duplicate(self):
92
+ """Get a copy of this object."""
93
+ return self.__copy__()
94
+
95
+ def ToString(self):
96
+ """Overwrite .NET ToString."""
97
+ return self.__repr__()
98
+
99
+ @staticmethod
100
+ def _check_schedule(schedule, obj_name=''):
101
+ """Check that an input schedule is a correct object type."""
102
+ assert isinstance(schedule, (ScheduleRuleset, ScheduleFixedInterval)), \
103
+ 'Expected ScheduleRuleset or ScheduleFixedInterval for {} ' \
104
+ 'schedule. Got {}.'.format(obj_name, type(schedule))
105
+
106
+ @staticmethod
107
+ def _get_schedule_from_dict(sch_dict):
108
+ """Get a schedule object from a schedule dictionary."""
109
+ if sch_dict['type'] == 'ScheduleRuleset':
110
+ return ScheduleRuleset.from_dict(sch_dict)
111
+ elif sch_dict['type'] == 'ScheduleFixedInterval':
112
+ return ScheduleFixedInterval.from_dict(sch_dict)
113
+ else:
114
+ raise NotImplementedError(
115
+ 'Schedule {} is not supported.'.format(sch_dict['type']))
116
+
117
+ def __copy__(self):
118
+ new_obj = _HVACSystem(self.identifier)
119
+ new_obj._display_name = self._display_name
120
+ new_obj.user_data = None if self._user_data is None else self._user_data.copy()
121
+ return new_obj
122
+
123
+ def __repr__(self):
124
+ return 'HVACSystem: {}'.format(self.display_name)