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,290 @@
1
+ """Module for converting EnergyPlus results into operational carbon emissions."""
2
+ import os
3
+ from collections import OrderedDict
4
+
5
+ from ladybug.sql import SQLiteResult
6
+
7
+
8
+ def emissions_region(location):
9
+ """Get the region of carbon emissions associated with a location.
10
+
11
+ Args:
12
+ location: A ladybug Location object, which will be used to determine
13
+ the subregion.
14
+
15
+ Returns:
16
+ A Tuple of text for the eGrid subregion for the location. The first value
17
+ is the future emissions region, the second is the historical region,
18
+ and the last is the historic hourly region. This will be None if
19
+ the location cannot be mapped to a region.
20
+ """
21
+ # create the map from states to electric regions
22
+ region_map = {
23
+ 'FL': ('FRCCc', 'FRCC', 'Florida'),
24
+ 'MS': ('SRMVc', 'SRMV', 'Midwest'),
25
+ 'NE': ('MROWc', 'MROW', 'Midwest'),
26
+ 'OR': ('NWPPc', 'NWPP', 'Northwest'),
27
+ 'CA': ('CAMXc', 'CAMX', 'California'),
28
+ 'VA': ('SRVCc', 'SRVC', 'Carolinas'),
29
+ 'AR': ('SRMVc', 'SRMV', 'Midwest'),
30
+ 'TX': ('ERCTc', 'ERCT', 'Texas'),
31
+ 'OH': ('RFCWc', 'RFCW', 'Midwest'),
32
+ 'UT': ('NWPPc', 'NWPP', 'Northwest'),
33
+ 'MT': ('NWPPc', 'NWPP', 'Northwest'),
34
+ 'TN': ('SRTVc', 'SRTV', 'Tennessee'),
35
+ 'ID': ('NWPPc', 'NWPP', 'Northwest'),
36
+ 'WI': ('MROEc', 'MROE', 'Midwest'),
37
+ 'WV': ('RFCWc', 'RFCW', 'Midwest'),
38
+ 'NC': ('SRVCc', 'SRVC', 'Carolinas'),
39
+ 'LA': ('SRMVc', 'SRMV', 'Midwest'),
40
+ 'IL': ('SRMWc', 'SRMW', 'Midwest'),
41
+ 'OK': ('SPSOc', 'SPSO', 'Central'),
42
+ 'IA': ('MROWc', 'MROW', 'Midwest'),
43
+ 'WA': ('NWPPc', 'NWPP', 'Northwest'),
44
+ 'SD': ('MROWc', 'MROW', 'Midwest'),
45
+ 'MN': ('MROWc', 'MROW', 'Midwest'),
46
+ 'KY': ('SRTVc', 'SRTV', 'Tennessee'),
47
+ 'MI': ('RFCMc', 'RFCM', 'Midwest'),
48
+ 'KS': ('SPNOc', 'SPNO', 'Central'),
49
+ 'NJ': ('RFCEc', 'RFCE', 'Mid-Atlantic'),
50
+ 'NY': ('NYSTc', 'NYCW', 'New York'),
51
+ 'IN': ('RFCWc', 'RFCW', 'Midwest'),
52
+ 'VT': ('NEWEc', 'NEWE', 'New England'),
53
+ 'NM': ('AZNMc', 'AZNM', 'Southwest'),
54
+ 'WY': ('RMPAc', 'RMPA', 'Rocky Mountains'),
55
+ 'GA': ('SRSOc', 'SRSO', 'SRSO'),
56
+ 'MO': ('SRMWc', 'SRMW', 'Midwest'),
57
+ 'DC': ('RFCEc', 'RFCE', 'Mid-Atlantic'),
58
+ 'SC': ('SRVCc', 'SRVC', 'Carolinas'),
59
+ 'PA': ('RFCEc', 'RFCE', 'Mid-Atlantic'),
60
+ 'CO': ('RMPAc', 'RMPA', 'Rocky Mountains'),
61
+ 'AZ': ('AZNMc', 'AZNM', 'Southwest'),
62
+ 'ME': ('NEWEc', 'NEWE', 'New England'),
63
+ 'AL': ('SRSOc', 'SRSO', 'Southeast'),
64
+ 'MD': ('RFCEc', 'RFCE', 'Mid-Atlantic'),
65
+ 'NH': ('NEWEc', 'NEWE', 'New England'),
66
+ 'MA': ('NEWEc', 'NEWE', 'New England'),
67
+ 'ND': ('MROWc', 'MROW', 'Midwest'),
68
+ 'NV': ('NWPPc', 'NWPP', 'Northwest'),
69
+ 'CT': ('NEWEc', 'NEWE', 'New England'),
70
+ 'DE': ('RFCEc', 'RFCE', 'Mid-Atlantic'),
71
+ 'RI': ('NEWEc', 'NEWE', 'New England')
72
+ }
73
+ # return the region
74
+ try:
75
+ return region_map[location.state]
76
+ except KeyError: # location could not be mapped to a region
77
+ return None
78
+
79
+
80
+ def future_electricity_emissions(location, year=2030):
81
+ """Get the future carbon emissions of the electric grid associated with a location.
82
+
83
+ Args:
84
+ location: A ladybug Location object, which will be used to determine
85
+ the subregion.
86
+ year: An integer for the future year for which carbon emissions will
87
+ be estimated. Values must be an even number and be between 2020
88
+ and 2050. (Default: 2030).
89
+
90
+ Returns:
91
+ A number for the electric grid carbon emissions in kg CO2 per MWh.
92
+ """
93
+ # create the map between regions, years, and carbon
94
+ years = (2020, 2022, 2024, 2026, 2028, 2030, 2032, 2034,
95
+ 2036, 2038, 2040, 2042, 2044, 2046, 2048, 2050)
96
+ region_map = {
97
+ 'AZNMc': (352, 412, 404, 389, 335, 301, 288, 279,
98
+ 243, 208, 179, 182, 144, 136, 132, 126),
99
+ 'CAMXc': (212, 216, 198, 180, 159, 150, 147, 140,
100
+ 130, 112, 101, 94, 82, 77, 75, 69),
101
+ 'ERCTc': (344, 342, 302, 302, 271, 208, 169, 158,
102
+ 156, 141, 147, 128, 120, 105, 103, 88),
103
+ 'FRCCc': (368, 377, 381, 399, 355, 288, 273, 277,
104
+ 268, 259, 251, 228, 192, 193, 190, 179),
105
+ 'MROEc': (411, 419, 411, 427, 389, 335, 285, 156,
106
+ 149, 156, 146, 145, 121, 107, 122, 99),
107
+ 'MROWc': (375, 386, 354, 325, 298, 185, 149, 133,
108
+ 127, 126, 129, 110, 88, 89, 79, 70),
109
+ 'NEWEc': (136, 148, 132, 108, 96, 81, 82, 70,
110
+ 73, 67, 67, 58, 60, 59, 58, 59),
111
+ 'NWPPc': (177, 225, 185, 170, 148, 137, 138, 136,
112
+ 135, 124, 111, 108, 103, 91, 87, 66),
113
+ 'NYSTc': (189, 206, 153, 134, 111, 92, 92, 79,
114
+ 86, 83, 77, 78, 79, 78, 83, 83),
115
+ 'RFCEc': (276, 285, 254, 250, 238, 209, 206, 199,
116
+ 203, 206, 198, 192, 187, 191, 170, 162),
117
+ 'RFCMc': (613, 634, 523, 527, 480, 393, 374, 357,
118
+ 338, 319, 291, 287, 290, 227, 203, 156),
119
+ 'RFCWc': (493, 508, 467, 477, 463, 415, 390, 363,
120
+ 335, 316, 287, 259, 242, 215, 217, 197),
121
+ 'RMPAc': (528, 580, 577, 584, 545, 444, 421, 354,
122
+ 302, 301, 302, 259, 253, 194, 174, 168),
123
+ 'SPNOc': (461, 447, 249, 235, 228, 182, 162, 162,
124
+ 156, 157, 155, 152, 117, 122, 130, 132),
125
+ 'SPSOc': (287, 277, 161, 143, 134, 119, 87, 82,
126
+ 72, 64, 60, 56, 47, 49, 58, 56),
127
+ 'SRMVc': (421, 411, 358, 349, 329, 281, 269, 264,
128
+ 253, 248, 241, 223, 206, 234, 233, 216),
129
+ 'SRMWc': (610, 637, 551, 542, 476, 366, 382, 366,
130
+ 348, 335, 284, 246, 211, 187, 186, 175),
131
+ 'SRSOc': (334, 322, 327, 347, 263, 222, 206, 208,
132
+ 207, 188, 185, 161, 145, 123, 126, 112),
133
+ 'SRTVc': (517, 554, 487, 513, 503, 408, 379, 355,
134
+ 327, 325, 309, 281, 261, 230, 209, 176),
135
+ 'SRVCc': (293, 303, 301, 282, 259, 212, 203, 191,
136
+ 188, 185, 179, 156, 149, 128, 121, 98),
137
+ }
138
+ # return the carbon intensity of electricity
139
+ yr_i = years.index(year)
140
+ region_str = emissions_region(location)
141
+ if region_str is not None:
142
+ return region_map[region_str[0]][yr_i]
143
+
144
+
145
+ def emissions_from_sql(sql_results, electricity_emissions):
146
+ """Get a dictionary of Carbon Emissions Intensity results from EnergyPlus SQLs.
147
+
148
+ This input emissions of electricity will be used to compute carbon intensity
149
+ for both electricity and district heating/cooling. Fixed numbers will be used
150
+ to convert the following on-site fuel sources:
151
+
152
+ * Natural Gas -- 277.358 kg/MWh
153
+ * Propane -- 323.897 kg/MWh
154
+ * Fuel Oil -- 294.962 kg/MWh
155
+ * District Heating -- 369.811 kg/MWh
156
+ * District Cooling -- [electricity_emissions] / 3.5 kg/MWh
157
+
158
+ Args:
159
+ sql_results: The file path of the SQL result file that has been generated
160
+ from an energy simulation. This can also be a list of SQL result files
161
+ in which case EUI will be computed across all files. Lastly, it can
162
+ be a directory or list of directories containing results, in which
163
+ case, EUI will be calculated form all files ending in .sql.
164
+ electricity_emissions: A number for the electric grid carbon emissions
165
+ in kg CO2 per MWh. For locations in the USA, this can be
166
+ obtained from the future_electricity_emissions method. For locations
167
+ outside of the USA where specific data is unavailable, the following
168
+ rules of thumb may be used as a guide:
169
+
170
+ * 800 kg/MWh - for an inefficient coal or oil-dominated grid
171
+ * 400 kg/MWh - for the US (energy mixed) grid around 2020
172
+ * 100-200 kg/MWh - for grids with majority renewable/nuclear composition
173
+ * 0-100 kg/MWh - for grids with renewables and storage
174
+
175
+ Returns:
176
+ A dictionary with several keys.
177
+
178
+ - carbon_intensity -- A number for the total annual carbon intensity.
179
+ This is the sum of all operational carbon emissions divided by the
180
+ gross floor area (including both conditioned and unconditioned spaces).
181
+ Units are kg CO2/m2.
182
+
183
+ - total_floor_area -- A number for the gross floor area of the building
184
+ in m2. This excludes Rooms with True exclude_floor_area property.
185
+
186
+ - conditioned_floor_area -- A number for the conditioned floor area of the
187
+ building in m2. This excludes Rooms with True exclude_floor_area property.
188
+
189
+ - total_carbon -- A number for the total annual operational carbon
190
+ in kg of Co2.
191
+
192
+ - end_uses -- A dictionary with the carbon intensity for each of the end
193
+ uses of the building (eg. heating, cooling, lighting, etc.).
194
+
195
+ - sources -- A dictionary with the carbon intensity for each of the energy
196
+ sources of the building (eg. electricity, natural_gas, district_heat, etc.).
197
+ """
198
+ # create a list of sql file path that were either passed directly or are
199
+ # contained within passed folders
200
+ if not isinstance(sql_results, (list, tuple)):
201
+ sql_results = [sql_results]
202
+ sql_paths = []
203
+ for file_or_folder_path in sql_results:
204
+ if os.path.isdir(file_or_folder_path):
205
+ for file_path in os.listdir(file_or_folder_path):
206
+ if file_path.endswith('.sql'):
207
+ sql_paths.append(os.path.join(file_or_folder_path, file_path))
208
+ else:
209
+ sql_paths.append(file_or_folder_path)
210
+
211
+ # set initial values that will be computed based on results
212
+ total_floor_area, conditioned_floor_area = 0, 0
213
+ tot_elec, tot_gas, tot_pro, tot_oil, tot_heat, tot_cool = 0, 0, 0, 0, 0, 0
214
+ end_uses = OrderedDict()
215
+
216
+ # loop through the sql files and add the energy use
217
+ for sql_path in sql_paths:
218
+ # parse the SQL file
219
+ sql_obj = SQLiteResult(sql_path)
220
+ # get the total floor area of the model
221
+ area_dict = sql_obj.tabular_data_by_name('Building Area')
222
+ areas = tuple(area_dict.values())
223
+ total_floor_area += areas[0][0]
224
+ conditioned_floor_area += areas[1][0]
225
+ # get the energy use
226
+ eui_dict = sql_obj.tabular_data_by_name('End Uses By Subcategory')
227
+ for category, vals in eui_dict.items():
228
+ total_use = sum([val for val in vals[:12]])
229
+ if total_use != 0:
230
+ elec = (vals[0] * electricity_emissions) / 1000
231
+ gas = (vals[1] * 277.358) / 1000
232
+ pro = (vals[7] * 323.897) / 1000
233
+ oil = (vals[6] * 294.962) / 1000
234
+ heat = (vals[11] * 369.811) / 1000
235
+ cool = (vals[10] * (electricity_emissions / 3.5)) / 1000
236
+
237
+ tot_elec += elec
238
+ tot_gas += gas
239
+ tot_pro += pro
240
+ tot_oil += oil
241
+ tot_heat += heat
242
+ tot_cool += cool
243
+ carb = sum((elec, gas, pro, oil, heat, cool))
244
+
245
+ cat, sub_cat = category.split(':')
246
+ eu_cat = cat if sub_cat == 'General' or sub_cat == 'Other' \
247
+ else sub_cat
248
+ try:
249
+ end_uses[eu_cat] += carb
250
+ except KeyError:
251
+ end_uses[eu_cat] = carb
252
+
253
+ # compute the total carbon emissions
254
+ sources = OrderedDict([
255
+ ('electricity', tot_elec),
256
+ ('natural_gas', tot_gas),
257
+ ('propane', tot_pro),
258
+ ('oil', tot_oil),
259
+ ('district_heat', tot_heat),
260
+ ('district_cool', tot_cool)
261
+ ])
262
+ total_carbon = sum(sources.values())
263
+
264
+ # assemble all of the results into a final dictionary
265
+ if total_floor_area != 0:
266
+ result_dict = {
267
+ 'carbon_intensity': round(total_carbon / total_floor_area, 3),
268
+ 'total_floor_area': total_floor_area,
269
+ 'conditioned_floor_area': conditioned_floor_area,
270
+ 'total_carbon': round(total_carbon, 3)
271
+ }
272
+ result_dict['end_uses'] = OrderedDict(
273
+ [(key, round(val / total_floor_area, 3)) for key, val in end_uses.items()]
274
+ )
275
+ result_dict['sources'] = OrderedDict(
276
+ [(key, round(val / total_floor_area, 3)) for key, val in sources.items()
277
+ if val != 0]
278
+ )
279
+ else:
280
+ result_dict = {
281
+ 'carbon_intensity': 0.0,
282
+ 'total_floor_area': total_floor_area,
283
+ 'conditioned_floor_area': conditioned_floor_area,
284
+ 'total_carbon': round(total_carbon, 3)
285
+ }
286
+ result_dict['end_uses'] = OrderedDict([(key, 0.0) for key in end_uses.keys()])
287
+ result_dict['sources'] = OrderedDict(
288
+ [(key, 0.0) for key, val in sources.items() if val != 0]
289
+ )
290
+ return result_dict
@@ -0,0 +1,101 @@
1
+ # coding=utf-8
2
+ """Module for parsing EnergyPlus Error (.err) files."""
3
+ from __future__ import division
4
+
5
+ import os
6
+
7
+
8
+ class Err(object):
9
+ """Object for parsing EnergyPlus Error (.err) files.
10
+
11
+ Args:
12
+ file_path: Full path to an Err file that was generated by EnergyPlus.
13
+
14
+ Properties:
15
+ * file_path
16
+ * file_contents
17
+ * warnings
18
+ * severe_errors
19
+ * fatal_errors
20
+ """
21
+
22
+ def __init__(self, file_path):
23
+ """Initialize Err"""
24
+ assert os.path.isfile(file_path), 'No file was found at {}'.format(file_path)
25
+ assert file_path.endswith('.err'), \
26
+ '{} is not an error file ending in .err.'.format(file_path)
27
+ self._file_path = file_path
28
+ self._file_contents = None
29
+ self._warnings = None
30
+ self._severe_errors = None
31
+ self._fatal_errors = None
32
+
33
+ @property
34
+ def file_path(self):
35
+ """Get the path to the .err file."""
36
+ return self._file_path
37
+
38
+ @property
39
+ def file_contents(self):
40
+ """Get a string of all contents in the file."""
41
+ if not self._file_contents:
42
+ self._parse_file_contents()
43
+ return self._file_contents
44
+
45
+ @property
46
+ def warnings(self):
47
+ """Get a list of strings for all of the warnings found in the .err file.
48
+
49
+ Warnings are usually not important enough to bring to the front-end users'
50
+ attention but they can be helpful for developers and advanced users.
51
+ """
52
+ if not self._warnings:
53
+ self._sort_warnings_errors()
54
+ return self._warnings
55
+
56
+ @property
57
+ def severe_errors(self):
58
+ """Get a list of strings for all of the severe errors found in the .err file.
59
+
60
+ Severe errors are important enough that front-end users should be made aware of
61
+ them even though they do not necessarily mean that the simulation has failed.
62
+ """
63
+ if not self._severe_errors:
64
+ self._sort_warnings_errors()
65
+ return self._severe_errors
66
+
67
+ @property
68
+ def fatal_errors(self):
69
+ """Get a list of strings for all of the fatal errors found in the .err file.
70
+
71
+ Fatal errors indicate the reason why the simulation has failed.
72
+ """
73
+ if not self._fatal_errors:
74
+ self._sort_warnings_errors()
75
+ return self._fatal_errors
76
+
77
+ def _parse_file_contents(self):
78
+ """Parse all of the contents of a file path."""
79
+ with open(self._file_path) as err_file:
80
+ self._file_contents = err_file.read()
81
+
82
+ def _sort_warnings_errors(self):
83
+ """Sort the contents of the error file into warnings and errors."""
84
+ self._warnings = []
85
+ self._severe_errors = []
86
+ self._fatal_errors = []
87
+ for line in self.file_contents.split('\n'):
88
+ if '** Warning **' in line:
89
+ self._warnings.append(line.split('** Warning **')[-1])
90
+ elif '** Fatal **' in line:
91
+ self._fatal_errors.append(line)
92
+ elif '** Severe **' in line:
93
+ if 'Degenerate surfaces' not in line:
94
+ self._severe_errors.append(line)
95
+
96
+ def ToString(self):
97
+ """Overwrite .NET ToString."""
98
+ return self.__repr__()
99
+
100
+ def __repr__(self):
101
+ return 'Energy Error Result: {}'.format(self.file_path)
@@ -0,0 +1,100 @@
1
+ """Module to parse End Use Intensity (EUI) from EnergyPlus results."""
2
+ import os
3
+ from collections import OrderedDict
4
+
5
+ from ladybug.sql import SQLiteResult
6
+
7
+
8
+ def eui_from_sql(sql_results):
9
+ """Get a dictionary of End Use Intensity (EUI) results from EnergyPlus SQLs.
10
+
11
+ Args:
12
+ sql_results: The file path of the SQL result file that has been generated
13
+ from an energy simulation. This can also be a list of SQL result files
14
+ in which case EUI will be computed across all files. Lastly, it can
15
+ be a directory or list of directories containing results, in which
16
+ case, EUI will be calculated form all files ending in .sql.
17
+
18
+ Returns:
19
+ A dictionary with several keys.
20
+
21
+ - eui -- A number for the total end use intensity. Specifically,
22
+ this is the sum of all electricity, fuel, district heating/cooling,
23
+ etc. divided by the gross floor area (including both conditioned
24
+ and unconditioned spaces). Units are kWh/m2.
25
+
26
+ - total_floor_area -- A number for the gross floor area of the building
27
+ in m2. This excludes Rooms with True exclude_floor_area property.
28
+
29
+ - conditioned_floor_area -- A number for the conditioned floor area of the
30
+ building in m2. This excludes Rooms with True exclude_floor_area property.
31
+
32
+ - total_energy -- A number for the total energy use of the building in kWh.
33
+
34
+ - end_uses -- A dictionary with the end use intensity for each of the end
35
+ uses of the building (eg. heating, cooling, lighting, etc.).
36
+ """
37
+ # set initial values that will be computed based on results
38
+ total_floor_area, conditioned_floor_area, total_energy = 0, 0, 0
39
+ end_uses = OrderedDict()
40
+
41
+ # create a list of sql file path that were either passed directly or are
42
+ # contained within passed folders
43
+ if not isinstance(sql_results, (list, tuple)):
44
+ sql_results = [sql_results]
45
+ sql_paths = []
46
+ for file_or_folder_path in sql_results:
47
+ if os.path.isdir(file_or_folder_path):
48
+ for file_path in os.listdir(file_or_folder_path):
49
+ if file_path.endswith('.sql'):
50
+ sql_paths.append(os.path.join(file_or_folder_path, file_path))
51
+ else:
52
+ sql_paths.append(file_or_folder_path)
53
+
54
+ # loop through the sql files and add the energy use
55
+ for sql_path in sql_paths:
56
+ # parse the SQL file
57
+ sql_obj = SQLiteResult(sql_path)
58
+ # get the total floor area of the model
59
+ area_dict = sql_obj.tabular_data_by_name('Building Area')
60
+ areas = tuple(area_dict.values())
61
+ try:
62
+ total_floor_area += areas[0][0]
63
+ conditioned_floor_area += areas[1][0]
64
+ except IndexError:
65
+ msg = 'Failed to find the "Building Area" table in the .sql file.'
66
+ raise ValueError(msg)
67
+ # get the energy use
68
+ eui_dict = sql_obj.tabular_data_by_name('End Uses By Subcategory')
69
+ for category, vals in eui_dict.items():
70
+ total_use = sum([val for val in vals[:12]])
71
+ if total_use != 0:
72
+ total_energy += total_use
73
+ cat, sub_cat = category.split(':')
74
+ eu_cat = cat if sub_cat == 'General' or sub_cat == 'Other' \
75
+ else sub_cat
76
+ try:
77
+ end_uses[eu_cat] += total_use
78
+ except KeyError:
79
+ end_uses[eu_cat] = total_use
80
+
81
+ # assemble all of the results into a final dictionary
82
+ if total_floor_area != 0:
83
+ result_dict = {
84
+ 'eui': round(total_energy / total_floor_area, 3),
85
+ 'total_floor_area': total_floor_area,
86
+ 'conditioned_floor_area': conditioned_floor_area,
87
+ 'total_energy': round(total_energy, 3)
88
+ }
89
+ result_dict['end_uses'] = OrderedDict(
90
+ [(key, round(val / total_floor_area, 3)) for key, val in end_uses.items()]
91
+ )
92
+ else:
93
+ result_dict = {
94
+ 'eui': 0.0,
95
+ 'total_floor_area': total_floor_area,
96
+ 'conditioned_floor_area': conditioned_floor_area,
97
+ 'total_energy': round(total_energy, 3)
98
+ }
99
+ result_dict['end_uses'] = OrderedDict([(key, 0.0) for key in end_uses.keys()])
100
+ return result_dict
@@ -0,0 +1,160 @@
1
+ """Module to parse on-site electricity generation from EnergyPlus results."""
2
+ import os
3
+ from ladybug.sql import SQLiteResult
4
+
5
+
6
+ def generation_summary_from_sql(sql_results):
7
+ """Get a dictionary of electricity generation results from EnergyPlus SQLs.
8
+
9
+ Args:
10
+ sql_results: The file path of the SQL result file that has been generated
11
+ from an energy simulation. This can also be a list of SQL result files
12
+ in which case electricity generation will be computed across all files.
13
+ Lastly, it can be a directory or list of directories containing results,
14
+ in which case, electricity generation will be calculated form all files
15
+ ending in .sql.
16
+
17
+ Returns:
18
+ A dictionary with several keys.
19
+
20
+ - total_production -- A positive number for the total electricity
21
+ produced on-site by all generators. Units are kWh.
22
+
23
+ - total_consumption -- A negative number for the total electricity
24
+ consumed on-site by all end uses. Units are kWh.
25
+
26
+ - production_used_on_site -- A positive number for the total electricity
27
+ produced on-site that was also consumed on-site. Units are kWh.
28
+
29
+ - production_surplus_sold -- A positive number for the total electricity
30
+ produced on-site that was sold to the electric utility as it could
31
+ not be used on-site. Units are kWh.
32
+
33
+ - consumption_purchased -- A negative number for the total electricity
34
+ that was purchased from the electric utility as there was no on-site
35
+ power at the time to meet the energy consumption demand. Units are kWh.
36
+ """
37
+ # set initial values that will be computed based on results
38
+ total_production, total_consumption = 0, 0
39
+ production_surplus_sold, consumption_purchased = 0, 0
40
+
41
+ # create a list of sql file path that were either passed directly or are
42
+ # contained within passed folders
43
+ if not isinstance(sql_results, (list, tuple)):
44
+ sql_results = [sql_results]
45
+ sql_paths = []
46
+ for file_or_folder_path in sql_results:
47
+ if os.path.isdir(file_or_folder_path):
48
+ for file_path in os.listdir(file_or_folder_path):
49
+ if file_path.endswith('.sql'):
50
+ sql_paths.append(os.path.join(file_or_folder_path, file_path))
51
+ else:
52
+ sql_paths.append(file_or_folder_path)
53
+
54
+ # loop through the sql files and add the energy production / consumption
55
+ for sql_path in sql_paths:
56
+ # parse the SQL file
57
+ sql_obj = SQLiteResult(sql_path)
58
+ # get the energy use
59
+ gen_dict = sql_obj.tabular_data_by_name('Electric Loads Satisfied')
60
+ for category, vals in gen_dict.items():
61
+ if category == 'Total On-Site Electric Sources':
62
+ total_production += vals[0]
63
+ elif category == 'Total Electricity End Uses':
64
+ total_consumption += vals[0]
65
+ elif category == 'Electricity Coming From Utility':
66
+ consumption_purchased += vals[0]
67
+ elif category == 'Surplus Electricity Going To Utility':
68
+ production_surplus_sold += vals[0]
69
+
70
+ # assemble all of the results into a final dictionary
71
+ result_dict = {
72
+ 'total_production': round(total_production, 3),
73
+ 'total_consumption': round(-total_consumption, 3),
74
+ 'production_used_on_site': round(total_production - production_surplus_sold),
75
+ 'production_surplus_sold': round(production_surplus_sold, 3),
76
+ 'consumption_purchased': round(-consumption_purchased, 3)
77
+ }
78
+ return result_dict
79
+
80
+
81
+ def generation_data_from_sql(sql_results):
82
+ """Get a data collections of electricity production and consumption.
83
+
84
+ Args:
85
+ sql_results: The file path of the SQL result file that has been generated
86
+ from an energy simulation. This can also be a list of SQL result files
87
+ in which case electricity generation will be computed across all files.
88
+ Lastly, it can be a directory or list of directories containing results,
89
+ in which case, electricity generation will be calculated form all files
90
+ ending in .sql.
91
+
92
+ Returns:
93
+ A tuple with two values.
94
+
95
+ - production -- A data collection of the total electricity produced on-site
96
+ by all generators. Values are in kWh. Will be None if no outputs
97
+ related to electricity production were requested from the simulation.
98
+
99
+ - consumption -- A data collection of the total electricity consumed
100
+ by all end uses. Values are in kWh. Will be None if no outputs related
101
+ to electricity production were requested from the simulation.
102
+ """
103
+ prod_data, net_data = [], []
104
+
105
+ # create a list of sql file path that were either passed directly or are
106
+ # contained within passed folders
107
+ if not isinstance(sql_results, (list, tuple)):
108
+ sql_results = [sql_results]
109
+ sql_paths = []
110
+ for file_or_folder_path in sql_results:
111
+ if os.path.isdir(file_or_folder_path):
112
+ for file_path in os.listdir(file_or_folder_path):
113
+ if file_path.endswith('.sql'):
114
+ sql_paths.append(os.path.join(file_or_folder_path, file_path))
115
+ else:
116
+ sql_paths.append(file_or_folder_path)
117
+
118
+ # loop through the sql files and add the energy production / consumption
119
+ for sql_path in sql_paths:
120
+ # parse the SQL file
121
+ sql_obj = SQLiteResult(sql_path)
122
+ sql_prod = sql_obj.data_collections_by_output_name(
123
+ 'Facility Total Produced Electricity Energy')
124
+ prod_data.extend(sql_prod)
125
+ sql_net = sql_obj.data_collections_by_output_name(
126
+ 'Facility Net Purchased Electricity Energy')
127
+ net_data.extend(sql_net)
128
+
129
+ # sum the production data together
130
+ if len(net_data) == 0:
131
+ return None, None
132
+ elif isinstance(net_data[0], (float, int)): # annual total of data
133
+ production = sum(prod_data)
134
+ else:
135
+ if len(prod_data) == 0:
136
+ production = net_data[0].duplicate()
137
+ production.values = [0] * len(production)
138
+ elif len(prod_data) == 1:
139
+ production = prod_data[0]
140
+ else:
141
+ production = prod_data[0]
142
+ for data_i in prod_data[1:]:
143
+ production = production + data_i
144
+ production.header.metadata['System'] = 'Whole Building'
145
+ production.header.metadata['type'] = 'Electricity Production'
146
+
147
+ # compute the electricity consumption from the net data
148
+ if len(net_data) == 1:
149
+ consumption = net_data[0] + production
150
+ else:
151
+ consumption = net_data[0]
152
+ for data_i in net_data[1:]:
153
+ consumption = consumption + data_i
154
+ consumption = consumption + production
155
+ try:
156
+ consumption.header.metadata['type'] = 'Electricity Consumption'
157
+ except AttributeError: # annual total of data
158
+ pass
159
+
160
+ return production, consumption