honeybee-energy 1.116.106__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- honeybee_energy/__init__.py +24 -0
- honeybee_energy/__main__.py +4 -0
- honeybee_energy/_extend_honeybee.py +145 -0
- honeybee_energy/altnumber.py +21 -0
- honeybee_energy/baseline/__init__.py +2 -0
- honeybee_energy/baseline/create.py +608 -0
- honeybee_energy/baseline/data/__init__.py +1 -0
- honeybee_energy/baseline/data/constructions.csv +64 -0
- honeybee_energy/baseline/data/fen_ratios.csv +15 -0
- honeybee_energy/baseline/data/lpd_building.csv +21 -0
- honeybee_energy/baseline/data/pci_2016.csv +22 -0
- honeybee_energy/baseline/data/pci_2019.csv +22 -0
- honeybee_energy/baseline/data/pci_2022.csv +22 -0
- honeybee_energy/baseline/data/shw.csv +21 -0
- honeybee_energy/baseline/pci.py +512 -0
- honeybee_energy/baseline/result.py +371 -0
- honeybee_energy/boundarycondition.py +128 -0
- honeybee_energy/cli/__init__.py +69 -0
- honeybee_energy/cli/baseline.py +475 -0
- honeybee_energy/cli/edit.py +327 -0
- honeybee_energy/cli/lib.py +1154 -0
- honeybee_energy/cli/result.py +810 -0
- honeybee_energy/cli/setconfig.py +124 -0
- honeybee_energy/cli/settings.py +569 -0
- honeybee_energy/cli/simulate.py +380 -0
- honeybee_energy/cli/translate.py +1714 -0
- honeybee_energy/cli/validate.py +224 -0
- honeybee_energy/config.json +11 -0
- honeybee_energy/config.py +842 -0
- honeybee_energy/construction/__init__.py +1 -0
- honeybee_energy/construction/_base.py +374 -0
- honeybee_energy/construction/air.py +325 -0
- honeybee_energy/construction/dictutil.py +89 -0
- honeybee_energy/construction/dynamic.py +607 -0
- honeybee_energy/construction/opaque.py +460 -0
- honeybee_energy/construction/shade.py +319 -0
- honeybee_energy/construction/window.py +1096 -0
- honeybee_energy/construction/windowshade.py +847 -0
- honeybee_energy/constructionset.py +1655 -0
- honeybee_energy/dictutil.py +56 -0
- honeybee_energy/generator/__init__.py +5 -0
- honeybee_energy/generator/loadcenter.py +204 -0
- honeybee_energy/generator/pv.py +535 -0
- honeybee_energy/hvac/__init__.py +21 -0
- honeybee_energy/hvac/_base.py +124 -0
- honeybee_energy/hvac/_template.py +270 -0
- honeybee_energy/hvac/allair/__init__.py +22 -0
- honeybee_energy/hvac/allair/_base.py +349 -0
- honeybee_energy/hvac/allair/furnace.py +168 -0
- honeybee_energy/hvac/allair/psz.py +131 -0
- honeybee_energy/hvac/allair/ptac.py +163 -0
- honeybee_energy/hvac/allair/pvav.py +109 -0
- honeybee_energy/hvac/allair/vav.py +128 -0
- honeybee_energy/hvac/detailed.py +337 -0
- honeybee_energy/hvac/doas/__init__.py +28 -0
- honeybee_energy/hvac/doas/_base.py +345 -0
- honeybee_energy/hvac/doas/fcu.py +127 -0
- honeybee_energy/hvac/doas/radiant.py +329 -0
- honeybee_energy/hvac/doas/vrf.py +81 -0
- honeybee_energy/hvac/doas/wshp.py +91 -0
- honeybee_energy/hvac/heatcool/__init__.py +23 -0
- honeybee_energy/hvac/heatcool/_base.py +177 -0
- honeybee_energy/hvac/heatcool/baseboard.py +61 -0
- honeybee_energy/hvac/heatcool/evapcool.py +72 -0
- honeybee_energy/hvac/heatcool/fcu.py +92 -0
- honeybee_energy/hvac/heatcool/gasunit.py +53 -0
- honeybee_energy/hvac/heatcool/radiant.py +269 -0
- honeybee_energy/hvac/heatcool/residential.py +77 -0
- honeybee_energy/hvac/heatcool/vrf.py +54 -0
- honeybee_energy/hvac/heatcool/windowac.py +70 -0
- honeybee_energy/hvac/heatcool/wshp.py +62 -0
- honeybee_energy/hvac/idealair.py +699 -0
- honeybee_energy/internalmass.py +310 -0
- honeybee_energy/lib/__init__.py +1 -0
- honeybee_energy/lib/_loadconstructions.py +194 -0
- honeybee_energy/lib/_loadconstructionsets.py +117 -0
- honeybee_energy/lib/_loadmaterials.py +83 -0
- honeybee_energy/lib/_loadprogramtypes.py +125 -0
- honeybee_energy/lib/_loadschedules.py +87 -0
- honeybee_energy/lib/_loadtypelimits.py +64 -0
- honeybee_energy/lib/constructions.py +207 -0
- honeybee_energy/lib/constructionsets.py +95 -0
- honeybee_energy/lib/materials.py +67 -0
- honeybee_energy/lib/programtypes.py +125 -0
- honeybee_energy/lib/schedules.py +61 -0
- honeybee_energy/lib/scheduletypelimits.py +31 -0
- honeybee_energy/load/__init__.py +1 -0
- honeybee_energy/load/_base.py +190 -0
- honeybee_energy/load/daylight.py +397 -0
- honeybee_energy/load/dictutil.py +47 -0
- honeybee_energy/load/equipment.py +771 -0
- honeybee_energy/load/hotwater.py +543 -0
- honeybee_energy/load/infiltration.py +460 -0
- honeybee_energy/load/lighting.py +480 -0
- honeybee_energy/load/people.py +497 -0
- honeybee_energy/load/process.py +472 -0
- honeybee_energy/load/setpoint.py +816 -0
- honeybee_energy/load/ventilation.py +550 -0
- honeybee_energy/material/__init__.py +1 -0
- honeybee_energy/material/_base.py +166 -0
- honeybee_energy/material/dictutil.py +59 -0
- honeybee_energy/material/frame.py +367 -0
- honeybee_energy/material/gas.py +1087 -0
- honeybee_energy/material/glazing.py +854 -0
- honeybee_energy/material/opaque.py +1351 -0
- honeybee_energy/material/shade.py +1360 -0
- honeybee_energy/measure.py +472 -0
- honeybee_energy/programtype.py +723 -0
- honeybee_energy/properties/__init__.py +1 -0
- honeybee_energy/properties/aperture.py +333 -0
- honeybee_energy/properties/door.py +342 -0
- honeybee_energy/properties/extension.py +244 -0
- honeybee_energy/properties/face.py +274 -0
- honeybee_energy/properties/model.py +2640 -0
- honeybee_energy/properties/room.py +1747 -0
- honeybee_energy/properties/shade.py +314 -0
- honeybee_energy/properties/shademesh.py +262 -0
- honeybee_energy/reader.py +48 -0
- honeybee_energy/result/__init__.py +1 -0
- honeybee_energy/result/colorobj.py +648 -0
- honeybee_energy/result/emissions.py +290 -0
- honeybee_energy/result/err.py +101 -0
- honeybee_energy/result/eui.py +100 -0
- honeybee_energy/result/generation.py +160 -0
- honeybee_energy/result/loadbalance.py +890 -0
- honeybee_energy/result/match.py +202 -0
- honeybee_energy/result/osw.py +90 -0
- honeybee_energy/result/rdd.py +59 -0
- honeybee_energy/result/zsz.py +190 -0
- honeybee_energy/run.py +1577 -0
- honeybee_energy/schedule/__init__.py +1 -0
- honeybee_energy/schedule/day.py +626 -0
- honeybee_energy/schedule/dictutil.py +59 -0
- honeybee_energy/schedule/fixedinterval.py +1012 -0
- honeybee_energy/schedule/rule.py +619 -0
- honeybee_energy/schedule/ruleset.py +1867 -0
- honeybee_energy/schedule/typelimit.py +310 -0
- honeybee_energy/shw.py +315 -0
- honeybee_energy/simulation/__init__.py +1 -0
- honeybee_energy/simulation/control.py +214 -0
- honeybee_energy/simulation/daylightsaving.py +185 -0
- honeybee_energy/simulation/dictutil.py +51 -0
- honeybee_energy/simulation/output.py +646 -0
- honeybee_energy/simulation/parameter.py +606 -0
- honeybee_energy/simulation/runperiod.py +443 -0
- honeybee_energy/simulation/shadowcalculation.py +295 -0
- honeybee_energy/simulation/sizing.py +546 -0
- honeybee_energy/ventcool/__init__.py +5 -0
- honeybee_energy/ventcool/_crack_data.py +91 -0
- honeybee_energy/ventcool/afn.py +289 -0
- honeybee_energy/ventcool/control.py +269 -0
- honeybee_energy/ventcool/crack.py +126 -0
- honeybee_energy/ventcool/fan.py +493 -0
- honeybee_energy/ventcool/opening.py +365 -0
- honeybee_energy/ventcool/simulation.py +314 -0
- honeybee_energy/writer.py +1078 -0
- honeybee_energy-1.116.106.dist-info/METADATA +113 -0
- honeybee_energy-1.116.106.dist-info/RECORD +162 -0
- honeybee_energy-1.116.106.dist-info/WHEEL +5 -0
- honeybee_energy-1.116.106.dist-info/entry_points.txt +2 -0
- honeybee_energy-1.116.106.dist-info/licenses/LICENSE +661 -0
- honeybee_energy-1.116.106.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,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
|