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,310 @@
1
+ # coding=utf-8
2
+ """Schedule type definition."""
3
+ from __future__ import division
4
+
5
+ from ..reader import parse_idf_string, clean_idf_file_contents
6
+ from ..writer import generate_idf_string
7
+
8
+ from honeybee.typing import valid_ep_string, valid_string, float_in_range
9
+ from honeybee.altnumber import no_limit
10
+ from ladybug.datatype import fraction, temperature, temperaturedelta, power, \
11
+ angle, speed, distance, uvalue
12
+
13
+ import re
14
+
15
+
16
+ class ScheduleTypeLimit(object):
17
+ """Energy schedule type definition.
18
+
19
+ Schedule types exist for the sole purpose of validating schedule values against
20
+ upper/lower limits and assigning a data type and units to the schedule values.
21
+
22
+ Args:
23
+ identifier: Text string for a unique Schedule Type ID. Must be < 100 characters
24
+ and not contain any EnergyPlus special characters. This will be used to
25
+ identify the object across a model and in the exported IDF.
26
+ lower_limit: An optional number for the lower limit for values in the
27
+ schedule. If None or a NoLimit object, there will be no lower limit.
28
+ upper_limit: An optional number for the upper limit for values in the
29
+ schedule. If None or a NoLimit object, there will be no upper limit.
30
+ numeric_type: Either one of two strings: 'Continuous' or 'Discrete'. The
31
+ latter means that only integers are accepted as schedule values. (Default:
32
+ Continuous).
33
+ unit_type: Text for an EnergyPlus unit type, which will be used
34
+ to assign units to the values in the schedule. Note that this field
35
+ is not used in the actual calculations of EnergyPlus. (Default:
36
+ Dimensionless). Choose from the following options:
37
+
38
+ * Dimensionless
39
+ * Temperature
40
+ * DeltaTemperature
41
+ * PrecipitationRate
42
+ * Angle
43
+ * ConvectionCoefficient
44
+ * ActivityLevel
45
+ * Velocity
46
+ * Capacity
47
+ * Power
48
+ * Availability
49
+ * Percent
50
+ * Control
51
+ * Mode
52
+
53
+ Properties:
54
+ * identifier
55
+ * display_name
56
+ * lower_limit
57
+ * upper_limit
58
+ * numeric_type
59
+ * unit_type
60
+ * data_type
61
+ * unit
62
+ """
63
+ _default_lb_unit_type = {
64
+ 'Dimensionless': (fraction.Fraction(), 'fraction'),
65
+ 'Temperature': (temperature.Temperature(), 'C'),
66
+ 'DeltaTemperature': (temperaturedelta.TemperatureDelta(), 'dC'),
67
+ 'PrecipitationRate': [distance.Distance(), 'm'],
68
+ 'Angle': [angle.Angle(), 'degrees'],
69
+ 'ConvectionCoefficient': [uvalue.ConvectionCoefficient(), 'W/m2-K'],
70
+ 'ActivityLevel': [power.ActivityLevel(), 'W'],
71
+ 'Velocity': [speed.Speed(), 'm/s'],
72
+ 'Capacity': [power.Power(), 'W'],
73
+ 'Power': [power.Power(), 'W'],
74
+ 'Availability': [fraction.Fraction(), 'fraction'],
75
+ 'Percent': [fraction.Fraction(), '%'],
76
+ 'Control': [fraction.Fraction(), 'fraction'],
77
+ 'Mode': [fraction.Fraction(), 'fraction']}
78
+
79
+ UNIT_TYPES = tuple(_default_lb_unit_type.keys())
80
+ NUMERIC_TYPES = ('Continuous', 'Discrete')
81
+
82
+ def __init__(self, identifier, lower_limit=no_limit, upper_limit=no_limit,
83
+ numeric_type='Continuous', unit_type='Dimensionless'):
84
+ """Initialize ScheduleTypeLimit."""
85
+ # process the identifier and limits
86
+ self._identifier = valid_ep_string(identifier, 'schedule type identifier')
87
+ self._display_name = None
88
+ self._lower_limit = float_in_range(lower_limit) if lower_limit is not \
89
+ None and lower_limit != no_limit else no_limit
90
+ self._upper_limit = float_in_range(upper_limit) if upper_limit is not \
91
+ None and upper_limit != no_limit else no_limit
92
+ if self._lower_limit != no_limit and self._upper_limit != no_limit:
93
+ assert self._lower_limit <= self._upper_limit, 'ScheduleTypeLimit ' \
94
+ 'lower_limit must be less than upper_limit. {} > {}.'.format(
95
+ self._lower_limit, self._upper_limit)
96
+
97
+ # process the numeric type
98
+ self._numeric_type = numeric_type.capitalize() or 'Continuous'
99
+ assert self._numeric_type in self.NUMERIC_TYPES, '"{}" is not an acceptable ' \
100
+ 'numeric type. Choose from the following:\n{}'.format(
101
+ numeric_type, self.NUMERIC_TYPES)
102
+
103
+ # process the unit type and assign the ladybug data type and unit
104
+ if unit_type is None:
105
+ self._data_type, self._unit = self._default_lb_unit_type['Dimensionless']
106
+ self._unit_type = 'Dimensionless'
107
+ else:
108
+ clean_input = valid_string(unit_type).lower()
109
+ for key in self.UNIT_TYPES:
110
+ if key.lower() == clean_input:
111
+ unit_type = key
112
+ break
113
+ else:
114
+ raise ValueError(
115
+ 'unit_type {} is not recognized.\nChoose from the '
116
+ 'following:\n{}'.format(unit_type, self.UNIT_TYPES))
117
+ self._data_type, self._unit = self._default_lb_unit_type[unit_type]
118
+ self._unit_type = unit_type
119
+
120
+ @property
121
+ def identifier(self):
122
+ """Get the text string for unique schedule type identifier."""
123
+ return self._identifier
124
+
125
+ @property
126
+ def display_name(self):
127
+ """Get or set a string for the object name without any character restrictions.
128
+
129
+ If not set, this will be equal to the identifier.
130
+ """
131
+ if self._display_name is None:
132
+ return self._identifier
133
+ return self._display_name
134
+
135
+ @display_name.setter
136
+ def display_name(self, value):
137
+ if value is not None:
138
+ try:
139
+ value = str(value)
140
+ except UnicodeEncodeError: # Python 2 machine lacking the character set
141
+ pass # keep it as unicode
142
+ self._display_name = value
143
+
144
+ @property
145
+ def lower_limit(self):
146
+ """Get the lower limit of the schedule type."""
147
+ return self._lower_limit
148
+
149
+ @property
150
+ def upper_limit(self):
151
+ """Get the upper limit of the schedule type."""
152
+ return self._upper_limit
153
+
154
+ @property
155
+ def numeric_type(self):
156
+ """Text noting whether schedule values are 'Continuous' or 'Discrete'."""
157
+ return self._numeric_type
158
+
159
+ @property
160
+ def unit_type(self):
161
+ """Get the text string describing the energyplus unit type."""
162
+ return self._unit_type
163
+
164
+ @property
165
+ def data_type(self):
166
+ """Get the Ladybug DataType object corresponding to the energyplus unit type.
167
+
168
+ This object can be used for creating Ladybug DataCollections, performing unit
169
+ conversions of schedule values, etc.
170
+ """
171
+ return self._data_type
172
+
173
+ @property
174
+ def unit(self):
175
+ """Get the string describing the units of the schedule values (ie. 'C', 'W')."""
176
+ return self._unit
177
+
178
+ @classmethod
179
+ def from_idf(cls, idf_string):
180
+ """Create a ScheduleTypeLimit from an IDF string of ScheduleTypeLimits.
181
+
182
+ Args:
183
+ idf_string: A text string describing EnergyPlus ScheduleTypeLimits.
184
+ """
185
+ ep_strs = parse_idf_string(idf_string, 'ScheduleTypeLimits,')
186
+ ep_fields = [prop if prop != '' else None for prop in ep_strs]
187
+ return cls(*ep_fields)
188
+
189
+ @classmethod
190
+ def from_dict(cls, data):
191
+ """Create a ScheduleTypeLimit from a dictionary.
192
+
193
+ Args:
194
+ data: ScheduleTypeLimit dictionary following the format below.
195
+
196
+ .. code-block:: python
197
+
198
+ {
199
+ "type": 'ScheduleTypeLimit',
200
+ "identifier": 'Fractional',
201
+ "display_name": 'Fractional',
202
+ "lower_limit": 0,
203
+ "upper_limit": 1,
204
+ "numeric_type": Continuous,
205
+ "unit_type": "Dimensionless"
206
+ }
207
+ """
208
+ assert data['type'] == 'ScheduleTypeLimit', \
209
+ 'Expected ScheduleTypeLimit dictionary. Got {}.'.format(data['type'])
210
+ lower_limit = no_limit if 'lower_limit' not in data or \
211
+ data['lower_limit'] == no_limit.to_dict() else data['lower_limit']
212
+ upper_limit = no_limit if 'upper_limit' not in data or \
213
+ data['upper_limit'] == no_limit.to_dict() else data['upper_limit']
214
+ numeric_type = data['numeric_type'] if 'numeric_type' in data else 'Continuous'
215
+ unit_type = data['unit_type'] if 'unit_type' in data else 'Dimensionless'
216
+ new_obj = cls(data['identifier'], lower_limit, upper_limit,
217
+ numeric_type, unit_type)
218
+ if 'display_name' in data and data['display_name'] is not None:
219
+ new_obj.display_name = data['display_name']
220
+ return new_obj
221
+
222
+ def to_idf(self):
223
+ """IDF string for the ScheduleTypeLimits of this object.
224
+
225
+ .. code-block:: shell
226
+
227
+ ScheduleTypeLimits,
228
+ my type limit, !- name
229
+ , !- lower limit value
230
+ , !- upper limit value
231
+ Continuous, !- numeric type
232
+ Percent; !- unit type
233
+ """
234
+ values = [self.identifier, self.lower_limit, self.upper_limit,
235
+ self.numeric_type, self.unit_type]
236
+ if values[1] == no_limit:
237
+ values[1] = ''
238
+ if values[2] == no_limit:
239
+ values[2] = ''
240
+ comments = ('name', 'lower limit value', 'upper limit value',
241
+ 'numeric type', 'unit type')
242
+ return generate_idf_string('ScheduleTypeLimits', values, comments)
243
+
244
+ def to_dict(self):
245
+ """Shade construction dictionary representation."""
246
+ base = {'type': 'ScheduleTypeLimit'}
247
+ base['identifier'] = self.identifier
248
+ base['lower_limit'] = self.lower_limit if \
249
+ isinstance(self.lower_limit, float) else self.lower_limit.to_dict()
250
+ base['upper_limit'] = self.upper_limit if \
251
+ isinstance(self.upper_limit, float) else self.upper_limit.to_dict()
252
+ base['numeric_type'] = self.numeric_type
253
+ base['unit_type'] = self.unit_type
254
+ if self._display_name is not None:
255
+ base['display_name'] = self.display_name
256
+ return base
257
+
258
+ @staticmethod
259
+ def extract_all_from_idf_file(idf_file):
260
+ """Extract all ScheduleTypeLimit objects from an EnergyPlus IDF file.
261
+
262
+ Args:
263
+ idf_file: A path to an IDF file containing objects for ScheduleTypeLimits.
264
+
265
+ Returns:
266
+ schedule_type_limits -- A list of all ScheduleTypeLimits objects in the
267
+ IDF file as honeybee_energy ScheduleTypeLimit objects.
268
+ """
269
+ # read the file and remove lines of comments
270
+ file_contents = clean_idf_file_contents(idf_file)
271
+ # extract all of the ScheduleTypeLimit objects
272
+ type_pattern = re.compile(r"(?i)(ScheduleTypeLimits,[\s\S]*?;)")
273
+ type_idf_strings = type_pattern.findall(file_contents)
274
+ schedule_type_limits = []
275
+ for type_str in type_idf_strings:
276
+ type_str = type_str.strip()
277
+ schedule_type_limits.append(ScheduleTypeLimit.from_idf(type_str))
278
+ return schedule_type_limits
279
+
280
+ def duplicate(self):
281
+ """Get a copy of this object."""
282
+ return self.__copy__()
283
+
284
+ def __copy__(self):
285
+ new_obj = ScheduleTypeLimit(
286
+ self.identifier, self._lower_limit, self._upper_limit, self._numeric_type,
287
+ self._unit_type)
288
+ new_obj._display_name = self._display_name
289
+ return new_obj
290
+
291
+ def __key(self):
292
+ """A tuple based on the object properties, useful for hashing."""
293
+ return (self.identifier, str(self._lower_limit), str(self._upper_limit),
294
+ self._numeric_type, self._unit_type)
295
+
296
+ def __hash__(self):
297
+ return hash(self.__key())
298
+
299
+ def __eq__(self, other):
300
+ return isinstance(other, ScheduleTypeLimit) and self.__key() == other.__key()
301
+
302
+ def __ne__(self, other):
303
+ return not self.__eq__(other)
304
+
305
+ def ToString(self):
306
+ """Overwrite .NET ToString."""
307
+ return self.__repr__()
308
+
309
+ def __repr__(self):
310
+ return self.to_idf()
honeybee_energy/shw.py ADDED
@@ -0,0 +1,315 @@
1
+ # coding=utf-8
2
+ """Detailed Service Hot Water (SHW) system template used to meet hot water demand."""
3
+ from __future__ import division
4
+
5
+ from honeybee._lockable import lockable
6
+ from honeybee.typing import valid_ep_string, valid_string, float_positive
7
+ from honeybee.altnumber import autocalculate
8
+ from .properties.extension import SHWSystemProperties
9
+
10
+
11
+ @lockable
12
+ class SHWSystem(object):
13
+ """Detailed Service Hot Water (SHW) system template used to meet hot water demand.
14
+
15
+ Args:
16
+ identifier: Text string for the system identifier. Must be < 100 characters
17
+ and not contain any EnergyPlus special characters. This will be used to
18
+ identify the object across a model and in the exported IDF.
19
+ equipment_type: Text for the specific type of the system and equipment. (Default:
20
+ Gas_WaterHeater) Choose from.
21
+
22
+ * Gas_WaterHeater
23
+ * Electric_WaterHeater
24
+ * HeatPump_WaterHeater
25
+ * Gas_TanklessHeater
26
+ * Electric_TanklessHeater
27
+
28
+ heater_efficiency: A number for the efficiency of the heater within
29
+ the system. For Gas systems, this is the efficiency of the burner.
30
+ For HeatPump systems, this is the rated COP of the system. For electric
31
+ systems, this should usually be set to 1. If set to None or Autocalculate,
32
+ this value will automatically be set based on the equipment_type. See below
33
+ for the default value for each equipment type. (Default: None).
34
+
35
+ * Gas_WaterHeater - 0.8
36
+ * Electric_WaterHeater - 1.0
37
+ * HeatPump_WaterHeater - 3.5
38
+ * Gas_TanklessHeater - 0.8
39
+ * Electric_TanklessHeater - 1.0
40
+
41
+ ambient_condition: A number for the ambient temperature in which the hot
42
+ water tank is located [C]. This can also be the identifier of a Room
43
+ in which the tank is located. (Default: 22).
44
+ ambient_loss_coefficient: A number for the loss of heat from the water heater
45
+ tank to the surrounding ambient conditions [W/K]. (Default: 6 W/K).
46
+
47
+ Properties:
48
+ * identifier
49
+ * display_name
50
+ * equipment_type
51
+ * heater_efficiency
52
+ * ambient_condition
53
+ * ambient_loss_coefficient
54
+ * user_data
55
+ """
56
+ __slots__ = ('_identifier', '_display_name', '_equipment_type', '_heater_efficiency',
57
+ '_ambient_condition', '_ambient_loss_coefficient',
58
+ '_locked', '_user_data', '_properties')
59
+
60
+ EQUIPMENT_TYPES = (
61
+ 'Gas_WaterHeater',
62
+ 'Electric_WaterHeater',
63
+ 'HeatPump_WaterHeater',
64
+ 'Gas_TanklessHeater',
65
+ 'Electric_TanklessHeater'
66
+ )
67
+ DEFAULT_EFFICIENCIES = {
68
+ 'Gas_WaterHeater': 0.8,
69
+ 'Electric_WaterHeater': 1.0,
70
+ 'HeatPump_WaterHeater': 3.5,
71
+ 'Gas_TanklessHeater': 0.8,
72
+ 'Electric_TanklessHeater': 1.0
73
+ }
74
+
75
+ def __init__(self, identifier, equipment_type='Gas_WaterHeater',
76
+ heater_efficiency=autocalculate, ambient_condition=22,
77
+ ambient_loss_coefficient=6):
78
+ """Initialize SHWSystem."""
79
+ # assign the identifier and general properties
80
+ self.identifier = identifier
81
+ self._display_name = None
82
+ self._user_data = None
83
+ self._properties = SHWSystemProperties(self)
84
+
85
+ # set some dummy values that will get overwritten but let the checks pass
86
+ self._heater_efficiency = None
87
+ self._ambient_condition = ''
88
+
89
+ # set the main features of the HVAC system
90
+ self.equipment_type = equipment_type
91
+ self.heater_efficiency = heater_efficiency
92
+ self.ambient_condition = ambient_condition
93
+ self.ambient_loss_coefficient = ambient_loss_coefficient
94
+
95
+ @property
96
+ def identifier(self):
97
+ """Get or set the text string for HVAC system identifier."""
98
+ return self._identifier
99
+
100
+ @identifier.setter
101
+ def identifier(self, identifier):
102
+ self._identifier = valid_ep_string(identifier, 'HVAC system identifier')
103
+
104
+ @property
105
+ def display_name(self):
106
+ """Get or set a string for the object name without any character restrictions.
107
+
108
+ If not set, this will be equal to the identifier.
109
+ """
110
+ if self._display_name is None:
111
+ return self._identifier
112
+ return self._display_name
113
+
114
+ @display_name.setter
115
+ def display_name(self, value):
116
+ if value is not None:
117
+ try:
118
+ value = str(value)
119
+ except UnicodeEncodeError: # Python 2 machine lacking the character set
120
+ pass # keep it as unicode
121
+ self._display_name = value
122
+
123
+ @property
124
+ def equipment_type(self):
125
+ """Get or set text to indicate the type of the equipment."""
126
+ return self._equipment_type
127
+
128
+ @equipment_type.setter
129
+ def equipment_type(self, value):
130
+ clean_input = valid_string(value).lower()
131
+ for key in self.EQUIPMENT_TYPES:
132
+ if key.lower() == clean_input:
133
+ value = key
134
+ break
135
+ else:
136
+ raise ValueError(
137
+ 'equipment_type {} is not recognized.\nChoose from the '
138
+ 'following:\n{}'.format(value, self.EQUIPMENT_TYPES))
139
+ self._equipment_type = value
140
+ self._check_efficiency_equipment_type()
141
+ self._check_condition_equipment_type()
142
+
143
+ @property
144
+ def heater_efficiency(self):
145
+ """Get or set a number for the efficiency of the heater within the system."""
146
+ return self._heater_efficiency if self._heater_efficiency is not None \
147
+ else self.DEFAULT_EFFICIENCIES[self._equipment_type]
148
+
149
+ @heater_efficiency.setter
150
+ def heater_efficiency(self, value):
151
+ if value == autocalculate:
152
+ value = None
153
+ elif value is not None:
154
+ value = float_positive(value, 'shw heater efficiency')
155
+ self._heater_efficiency = value
156
+ self._check_efficiency_equipment_type()
157
+
158
+ @property
159
+ def ambient_condition(self):
160
+ """Get or set a number for the ambient temperature where the tank is located [C].
161
+
162
+ This can also be the identifier of a Room in which the tank is located.
163
+ """
164
+ return self._ambient_condition
165
+
166
+ @ambient_condition.setter
167
+ def ambient_condition(self, value):
168
+ try:
169
+ value = float_positive(value, 'shw ambient condition')
170
+ except Exception:
171
+ assert isinstance(value, str), 'SHW ambient_condition must be either a ' \
172
+ 'temperature in Celsius or the identifier of a Room to locate the ' \
173
+ 'tank. Got {}.'.format(type(value))
174
+ value = valid_ep_string(value)
175
+ self._ambient_condition = value
176
+ self._check_condition_equipment_type()
177
+
178
+ @property
179
+ def ambient_loss_coefficient(self):
180
+ """Get or set a number the loss to the surrounding ambient conditions [W/K]."""
181
+ return self._ambient_loss_coefficient
182
+
183
+ @ambient_loss_coefficient.setter
184
+ def ambient_loss_coefficient(self, value):
185
+ self._ambient_loss_coefficient = \
186
+ float_positive(value, 'shw ambient loss coefficient')
187
+
188
+ @property
189
+ def user_data(self):
190
+ """Get or set an optional dictionary for additional meta data for this object.
191
+
192
+ This will be None until it has been set. All keys and values of this
193
+ dictionary should be of a standard Python type to ensure correct
194
+ serialization of the object to/from JSON (eg. str, float, int, list, dict)
195
+ """
196
+ return self._user_data
197
+
198
+ @user_data.setter
199
+ def user_data(self, value):
200
+ if value is not None:
201
+ assert isinstance(value, dict), 'Expected dictionary for honeybee_energy' \
202
+ 'object user_data. Got {}.'.format(type(value))
203
+ self._user_data = value
204
+
205
+ @property
206
+ def properties(self):
207
+ """Get properties for extensions."""
208
+ return self._properties
209
+
210
+ @classmethod
211
+ def from_dict(cls, data):
212
+ """Create a SHWSystem object from a dictionary.
213
+
214
+ Args:
215
+ data: A SHWSystem dictionary in following the format below.
216
+
217
+ .. code-block:: python
218
+
219
+ {
220
+ "type": "SHWSystem",
221
+ "identifier": "HP SHW System 3.8", # identifier for the SHWSystem
222
+ "display_name": "Bathroom Service Hot Water", # name for the SHWSystem
223
+ "equipment_type": 'HeatPump_WaterHeater', # Equipment type
224
+ "heater_efficiency": 3.8, # Heater efficiency/COP
225
+ "ambient_condition": "Basement Room", # Identifier for room with the tank
226
+ "ambient_loss_coefficient": 5 # Ambient loss from the tank [W/K]
227
+ }
228
+ """
229
+ assert data['type'] == 'SHWSystem', \
230
+ 'Expected SHWSystem dictionary. Got {}.'.format(data['type'])
231
+
232
+ # extract the key features and properties of the SHW
233
+ equip = data['equipment_type'] if 'equipment_type' in data and \
234
+ data['equipment_type'] is not None else 'Gas_WaterHeater'
235
+ eff = data['heater_efficiency'] if 'heater_efficiency' in data and \
236
+ data['heater_efficiency'] != autocalculate.to_dict() else None
237
+ cond = data['ambient_condition'] if 'ambient_condition' in data and \
238
+ data['ambient_condition'] is not None else 22
239
+ coeff = data['ambient_loss_coefficient'] if 'ambient_loss_coefficient' \
240
+ in data and data['ambient_loss_coefficient'] is not None else 6
241
+
242
+ new_obj = cls(data['identifier'], equip, eff, cond, coeff)
243
+ if 'display_name' in data and data['display_name'] is not None:
244
+ new_obj.display_name = data['display_name']
245
+ if 'user_data' in data and data['user_data'] is not None:
246
+ new_obj.user_data = data['user_data']
247
+ if 'properties' in data and data['properties'] is not None:
248
+ new_obj.properties._load_extension_attr_from_dict(data['properties'])
249
+ return new_obj
250
+
251
+ def to_dict(self):
252
+ """SHWSystem dictionary representation."""
253
+ base = {'type': 'SHWSystem'}
254
+ base['identifier'] = self.identifier
255
+ base['equipment_type'] = self.equipment_type
256
+ base['heater_efficiency'] = self.heater_efficiency
257
+ base['ambient_condition'] = self.ambient_condition
258
+ base['ambient_loss_coefficient'] = self.ambient_loss_coefficient
259
+ if self._display_name is not None:
260
+ base['display_name'] = self.display_name
261
+ if self._user_data is not None:
262
+ base['user_data'] = self.user_data
263
+ prop_dict = self.properties.to_dict()
264
+ if prop_dict is not None:
265
+ base['properties'] = prop_dict
266
+ return base
267
+
268
+ def _check_efficiency_equipment_type(self):
269
+ """Check that the efficiency is suitable for the equipment type"""
270
+ if self._heater_efficiency is not None:
271
+ if self._equipment_type != 'HeatPump_WaterHeater':
272
+ assert self.heater_efficiency <= 1, 'heater_efficiency must be less ' \
273
+ 'then 1 when using {} equipment_tpe. Got {}.'.format(
274
+ self._equipment_type, self._heater_efficiency)
275
+
276
+ def _check_condition_equipment_type(self):
277
+ """Check that the ambient condition is suitable for th equipment type"""
278
+ if self._equipment_type == 'HeatPump_WaterHeater':
279
+ assert isinstance(self.ambient_condition, str), 'ambient_condition ' \
280
+ 'must be a Room when using HeatPump_WaterHeater.'
281
+
282
+ def duplicate(self):
283
+ """Get a copy of this object."""
284
+ return self.__copy__()
285
+
286
+ def ToString(self):
287
+ """Overwrite .NET ToString."""
288
+ return self.__repr__()
289
+
290
+ def __copy__(self):
291
+ new_obj = SHWSystem(
292
+ self._identifier, self._equipment_type, self._heater_efficiency,
293
+ self._ambient_condition, self._ambient_loss_coefficient)
294
+ new_obj._display_name = self._display_name
295
+ new_obj._user_data = None if self._user_data is None else self._user_data.copy()
296
+ new_obj._properties._duplicate_extension_attr(self._properties)
297
+ return new_obj
298
+
299
+ def __key(self):
300
+ """A tuple based on the object properties, useful for hashing."""
301
+ return (
302
+ self._identifier, self._equipment_type, self._heater_efficiency,
303
+ self._ambient_condition, self._ambient_loss_coefficient)
304
+
305
+ def __hash__(self):
306
+ return hash(self.__key())
307
+
308
+ def __eq__(self, other):
309
+ return isinstance(other, SHWSystem) and self.__key() == other.__key()
310
+
311
+ def __ne__(self, other):
312
+ return not self.__eq__(other)
313
+
314
+ def __repr__(self):
315
+ return 'SHWSystem: {}'.format(self.display_name)
@@ -0,0 +1 @@
1
+ """honeybee-energy simulation settings."""