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,380 @@
|
|
|
1
|
+
"""honeybee energy simulation running commands."""
|
|
2
|
+
import click
|
|
3
|
+
import sys
|
|
4
|
+
import os
|
|
5
|
+
import shutil
|
|
6
|
+
import logging
|
|
7
|
+
import json
|
|
8
|
+
|
|
9
|
+
from ladybug.futil import preparedir
|
|
10
|
+
from ladybug.epw import EPW
|
|
11
|
+
from ladybug.stat import STAT
|
|
12
|
+
from honeybee.model import Model
|
|
13
|
+
from honeybee.config import folders
|
|
14
|
+
|
|
15
|
+
from honeybee_energy.simulation.parameter import SimulationParameter
|
|
16
|
+
from honeybee_energy.run import to_openstudio_sim_folder, \
|
|
17
|
+
run_osw, run_idf, output_energyplus_files, _parse_os_cli_failure, HB_OS_MSG
|
|
18
|
+
from honeybee_energy.result.err import Err
|
|
19
|
+
|
|
20
|
+
_logger = logging.getLogger(__name__)
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
@click.group(help='Commands for simulating Honeybee JSON files in EnergyPlus.')
|
|
24
|
+
def simulate():
|
|
25
|
+
pass
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
@simulate.command('model')
|
|
29
|
+
@click.argument('model-file', type=click.Path(
|
|
30
|
+
exists=True, file_okay=True, dir_okay=False, resolve_path=True))
|
|
31
|
+
@click.argument('epw-file', type=click.Path(
|
|
32
|
+
exists=True, file_okay=True, dir_okay=False, resolve_path=True))
|
|
33
|
+
@click.option('--sim-par-json', '-sp', help='Full path to a honeybee energy '
|
|
34
|
+
'SimulationParameter JSON that describes all of the settings for '
|
|
35
|
+
'the simulation. This will be ignored if the input model-file is '
|
|
36
|
+
'an OSM or IDF.', default=None, show_default=True,
|
|
37
|
+
type=click.Path(exists=False, file_okay=True, dir_okay=False,
|
|
38
|
+
resolve_path=True))
|
|
39
|
+
@click.option('--measures', '-m', help='Full path to a folder containing an OSW JSON '
|
|
40
|
+
'be used as the base for the execution of the OpenStudio CLI. While this '
|
|
41
|
+
'OSW can contain paths to measures that exist anywhere on the machine, '
|
|
42
|
+
'the best practice is to copy the measures into this measures '
|
|
43
|
+
'folder and use relative paths within the OSW. '
|
|
44
|
+
'This makes it easier to move the inputs for this command from one '
|
|
45
|
+
'machine to another.', default=None, show_default=True,
|
|
46
|
+
type=click.Path(file_okay=False, dir_okay=True, resolve_path=True))
|
|
47
|
+
@click.option('--additional-string', '-as', help='An additional IDF text string to get '
|
|
48
|
+
'appended to the IDF before simulation. The input should include '
|
|
49
|
+
'complete EnergyPlus objects as a single string following the IDF '
|
|
50
|
+
'format. This input can be used to include small EnergyPlus objects that '
|
|
51
|
+
'are not currently supported by honeybee.', default=None, type=str)
|
|
52
|
+
@click.option('--additional-idf', '-ai', help='An IDF file with text to be '
|
|
53
|
+
'appended before simulation. This input can be used to include '
|
|
54
|
+
'large EnergyPlus objects that are not currently supported by honeybee.',
|
|
55
|
+
default=None, show_default=True,
|
|
56
|
+
type=click.Path(exists=False, file_okay=True, dir_okay=False,
|
|
57
|
+
resolve_path=True))
|
|
58
|
+
@click.option('--report-units', '-r', help='A text value to set the units of the '
|
|
59
|
+
'OpenStudio Results report that this command can output. Choose from the '
|
|
60
|
+
'following:\nnone - no results report will be produced\nsi - all units '
|
|
61
|
+
'will be in SI\nip - all units will be in IP.',
|
|
62
|
+
type=str, default='none', show_default=True)
|
|
63
|
+
@click.option('--viz-variable', '-v', help='Text for an EnergyPlus output variable to '
|
|
64
|
+
'be visualized on the geometry in an output view_data HTML report. '
|
|
65
|
+
'If unspecified, no view_data report is produced. Multiple variables '
|
|
66
|
+
'can be requested by using multiple -v options. For example\n'
|
|
67
|
+
' -v "Zone Air System Sensible Heating Rate" -v "Zone Air System '
|
|
68
|
+
'Sensible Cooling Rate"',
|
|
69
|
+
type=str, default=None, show_default=True, multiple=True)
|
|
70
|
+
@click.option('--folder', '-f', help='Folder on this computer, into which the IDF '
|
|
71
|
+
'and result files will be written. If None, the files will be output '
|
|
72
|
+
'to the honeybee default simulation folder and placed in a project '
|
|
73
|
+
'folder with the same name as the model-file.',
|
|
74
|
+
default=None, show_default=True,
|
|
75
|
+
type=click.Path(file_okay=False, dir_okay=True, resolve_path=True))
|
|
76
|
+
@click.option('--enforce-rooms/--skip-no-rooms', ' /-sr', help='Flag to note whether '
|
|
77
|
+
'the simulation should be skipped if the Model has no Rooms and is '
|
|
78
|
+
'therefore not simulate-able in EnergyPlus. Otherwise, this command '
|
|
79
|
+
'will fail with an explicit error about the lack of rooms. Note that '
|
|
80
|
+
'the input model must be a HBJSON in order for this to work correctly',
|
|
81
|
+
default=True, show_default=True)
|
|
82
|
+
@click.option('--log-file', '-log', help='Optional log file to output the paths of the '
|
|
83
|
+
'generated files (osw, osm, idf, sql, zsz, rdd, html, err) if successfully'
|
|
84
|
+
' created. By default the list will be printed out to stdout',
|
|
85
|
+
type=click.File('w'), default='-', show_default=True)
|
|
86
|
+
def simulate_model(
|
|
87
|
+
model_file, epw_file, sim_par_json, measures, additional_string, additional_idf,
|
|
88
|
+
report_units, viz_variable, folder, enforce_rooms, log_file
|
|
89
|
+
):
|
|
90
|
+
"""Simulate a Model in EnergyPlus.
|
|
91
|
+
|
|
92
|
+
\b
|
|
93
|
+
Args:
|
|
94
|
+
model_file: Full path to a Model file as either a HBJSON, OSM, or IDF.
|
|
95
|
+
epw_file: Full path to an .epw file.
|
|
96
|
+
"""
|
|
97
|
+
try:
|
|
98
|
+
# get a ddy variable that might get used later
|
|
99
|
+
epw_folder, epw_file_name = os.path.split(epw_file)
|
|
100
|
+
ddy_file = os.path.join(epw_folder, epw_file_name.replace('.epw', '.ddy'))
|
|
101
|
+
stat_file = os.path.join(epw_folder, epw_file_name.replace('.epw', '.stat'))
|
|
102
|
+
|
|
103
|
+
# sense what type of file has been input
|
|
104
|
+
file_type = _sense_input_file_type(model_file)
|
|
105
|
+
proj_name = os.path.basename(model_file).lower()
|
|
106
|
+
|
|
107
|
+
# set the default folder to the default if it's not specified
|
|
108
|
+
if folder is None:
|
|
109
|
+
for ext in ('.hbjson', '.json', '.osm', '.idf'):
|
|
110
|
+
proj_name = proj_name.replace(ext, '')
|
|
111
|
+
folder = os.path.join(folders.default_simulation_folder, proj_name)
|
|
112
|
+
folder = os.path.join(folder, 'energyplus', 'run') if file_type == 'idf' \
|
|
113
|
+
else os.path.join(folder, 'openstudio')
|
|
114
|
+
elif file_type == 'idf': # ensure that all of the files end up in the same dir
|
|
115
|
+
folder = os.path.join(folder, 'run')
|
|
116
|
+
preparedir(folder, remove_content=False)
|
|
117
|
+
|
|
118
|
+
# process the simulation parameters and write new ones if necessary
|
|
119
|
+
def ddy_from_epw(epw_file, sim_par):
|
|
120
|
+
"""Produce a DDY from an EPW file."""
|
|
121
|
+
epw_obj = EPW(epw_file)
|
|
122
|
+
des_days = [epw_obj.approximate_design_day('WinterDesignDay'),
|
|
123
|
+
epw_obj.approximate_design_day('SummerDesignDay')]
|
|
124
|
+
sim_par.sizing_parameter.design_days = des_days
|
|
125
|
+
|
|
126
|
+
sim_par = None
|
|
127
|
+
if file_type == 'hbjson':
|
|
128
|
+
if sim_par_json is None or not os.path.isfile(sim_par_json):
|
|
129
|
+
sim_par = SimulationParameter()
|
|
130
|
+
sim_par.output.add_zone_energy_use()
|
|
131
|
+
sim_par.output.add_hvac_energy_use()
|
|
132
|
+
sim_par.output.add_electricity_generation()
|
|
133
|
+
sim_par.output.reporting_frequency = 'Monthly'
|
|
134
|
+
else:
|
|
135
|
+
with open(sim_par_json) as json_file:
|
|
136
|
+
data = json.load(json_file)
|
|
137
|
+
sim_par = SimulationParameter.from_dict(data)
|
|
138
|
+
if len(sim_par.sizing_parameter.design_days) == 0 and \
|
|
139
|
+
os.path.isfile(ddy_file):
|
|
140
|
+
try:
|
|
141
|
+
sim_par.sizing_parameter.add_from_ddy_996_004(ddy_file)
|
|
142
|
+
except AssertionError: # no design days within the DDY file
|
|
143
|
+
ddy_from_epw(epw_file, sim_par)
|
|
144
|
+
elif len(sim_par.sizing_parameter.design_days) == 0:
|
|
145
|
+
ddy_from_epw(epw_file, sim_par)
|
|
146
|
+
if sim_par.sizing_parameter.climate_zone is None and \
|
|
147
|
+
os.path.isfile(stat_file):
|
|
148
|
+
stat_obj = STAT(stat_file)
|
|
149
|
+
sim_par.sizing_parameter.climate_zone = stat_obj.ashrae_climate_zone
|
|
150
|
+
|
|
151
|
+
# process the measures input if it is specified
|
|
152
|
+
base_osw = None
|
|
153
|
+
if measures is not None and measures != '' and os.path.isdir(measures):
|
|
154
|
+
for f_name in os.listdir(measures):
|
|
155
|
+
if f_name.lower().endswith('.osw'):
|
|
156
|
+
base_osw = os.path.join(measures, f_name)
|
|
157
|
+
# write the path of the measures folder into the OSW
|
|
158
|
+
with open(base_osw) as json_file:
|
|
159
|
+
osw_dict = json.load(json_file)
|
|
160
|
+
osw_dict['measure_paths'] = [os.path.abspath(measures)]
|
|
161
|
+
with open(base_osw, 'w') as fp:
|
|
162
|
+
json.dump(osw_dict, fp)
|
|
163
|
+
break
|
|
164
|
+
|
|
165
|
+
# Write the osw file to translate the model to osm
|
|
166
|
+
strings_to_inject = additional_string if additional_string is not None else ''
|
|
167
|
+
if additional_idf is not None and os.path.isfile(additional_idf):
|
|
168
|
+
with open(additional_idf, "r") as add_idf_file:
|
|
169
|
+
strings_to_inject = strings_to_inject + '\n' + add_idf_file.read()
|
|
170
|
+
|
|
171
|
+
# run the Model re-serialization and convert to OSM, OSW, and IDF
|
|
172
|
+
osm, osw, idf = None, None, None
|
|
173
|
+
if file_type in ('hbjson', 'osm'):
|
|
174
|
+
if file_type == 'hbjson':
|
|
175
|
+
model = Model.from_hbjson(model_file)
|
|
176
|
+
if not enforce_rooms and len(model.rooms) == 0:
|
|
177
|
+
sys.exit(0)
|
|
178
|
+
return None
|
|
179
|
+
else:
|
|
180
|
+
model = model_file
|
|
181
|
+
osm, osw, idf = to_openstudio_sim_folder(
|
|
182
|
+
model, folder, epw_file=epw_file, sim_par=sim_par, enforce_rooms=True,
|
|
183
|
+
base_osw=base_osw, strings_to_inject=strings_to_inject,
|
|
184
|
+
report_units=report_units, viz_variables=viz_variable,
|
|
185
|
+
print_progress=True)
|
|
186
|
+
else:
|
|
187
|
+
idf = os.path.join(folder, 'in.idf')
|
|
188
|
+
if os.path.normcase(model_file) == os.path.normcase(idf):
|
|
189
|
+
shutil.copy(model_file, idf)
|
|
190
|
+
|
|
191
|
+
# run the simulation
|
|
192
|
+
sql = None
|
|
193
|
+
if idf is not None: # run the IDF directly through E+
|
|
194
|
+
gen_files = [idf] if osm is None else [osm, idf]
|
|
195
|
+
sql, zsz, rdd, html, err = run_idf(idf, epw_file)
|
|
196
|
+
if err is not None and os.path.isfile(err):
|
|
197
|
+
gen_files.extend([sql, zsz, rdd, html, err])
|
|
198
|
+
else:
|
|
199
|
+
raise Exception('Running EnergyPlus failed.')
|
|
200
|
+
else: # run the whole simulation with the OpenStudio CLI
|
|
201
|
+
gen_files = [osw]
|
|
202
|
+
osm, idf = run_osw(osw, measures_only=False)
|
|
203
|
+
if idf is not None and os.path.isfile(idf):
|
|
204
|
+
gen_files.extend([osm, idf])
|
|
205
|
+
else:
|
|
206
|
+
_parse_os_cli_failure(folder)
|
|
207
|
+
sql, zsz, rdd, html, err = output_energyplus_files(os.path.dirname(idf))
|
|
208
|
+
if os.path.isfile(err):
|
|
209
|
+
gen_files.extend([sql, zsz, rdd, html, err])
|
|
210
|
+
else:
|
|
211
|
+
raise Exception('Running EnergyPlus failed.')
|
|
212
|
+
|
|
213
|
+
# parse the error log and report any warnings
|
|
214
|
+
err_obj = Err(err)
|
|
215
|
+
for error in err_obj.fatal_errors:
|
|
216
|
+
log_file.write(err_obj.file_contents) # log before raising the error
|
|
217
|
+
raise Exception(error)
|
|
218
|
+
if sql is not None and os.path.isfile('{}-journal'.format(sql)):
|
|
219
|
+
try: # try to finish E+'s cleanup
|
|
220
|
+
os.remove('{}-journal'.format(sql))
|
|
221
|
+
except Exception: # maybe the file is inaccessible
|
|
222
|
+
pass
|
|
223
|
+
log_file.write(json.dumps(gen_files, indent=4))
|
|
224
|
+
except Exception as e:
|
|
225
|
+
_logger.exception('Model simulation failed.\n{}'.format(e))
|
|
226
|
+
sys.exit(1)
|
|
227
|
+
else:
|
|
228
|
+
sys.exit(0)
|
|
229
|
+
|
|
230
|
+
|
|
231
|
+
@simulate.command('osm')
|
|
232
|
+
@click.argument('osm-file', type=click.Path(
|
|
233
|
+
exists=True, file_okay=True, dir_okay=False, resolve_path=True))
|
|
234
|
+
@click.argument('epw-file', type=click.Path(
|
|
235
|
+
exists=True, file_okay=True, dir_okay=False, resolve_path=True))
|
|
236
|
+
@click.option('--folder', '-f', help='Folder on this computer, into which the '
|
|
237
|
+
'result files will be written. If None, the files will be output '
|
|
238
|
+
'to the honeybee default simulation folder and placed in a project '
|
|
239
|
+
'folder with the same name as the idf_file.',
|
|
240
|
+
default=None, show_default=True,
|
|
241
|
+
type=click.Path(file_okay=False, dir_okay=True, resolve_path=True))
|
|
242
|
+
@click.option('--log-file', '-log', help='Optional log file to output the paths of the '
|
|
243
|
+
'generated files (osw, osm, idf, sql, zsz, rdd, html, err) if successfully'
|
|
244
|
+
' created. By default the list will be printed out to stdout',
|
|
245
|
+
type=click.File('w'), default='-', show_default=True)
|
|
246
|
+
def simulate_osm(osm_file, epw_file, folder, log_file):
|
|
247
|
+
"""Simulate an OSM file in EnergyPlus.
|
|
248
|
+
|
|
249
|
+
\b
|
|
250
|
+
Args:
|
|
251
|
+
osm_file: Full path to a simulate-able .osm file.
|
|
252
|
+
epw_file: Full path to an .epw file.
|
|
253
|
+
"""
|
|
254
|
+
try:
|
|
255
|
+
# check that honeybee-openstudio is installed
|
|
256
|
+
try:
|
|
257
|
+
from honeybee_openstudio.openstudio import openstudio, OSModel
|
|
258
|
+
from honeybee_openstudio.simulation import assign_epw_to_model
|
|
259
|
+
except ImportError as e: # honeybee-openstudio is not installed
|
|
260
|
+
raise ImportError('{}\n{}'.format(HB_OS_MSG, e))
|
|
261
|
+
|
|
262
|
+
# set the default folder to the default if it's not specified and copy the OSM
|
|
263
|
+
if folder is None:
|
|
264
|
+
proj_name = os.path.basename(osm_file).replace('.osm', '')
|
|
265
|
+
folder = os.path.join(folders.default_simulation_folder, proj_name)
|
|
266
|
+
preparedir(folder, remove_content=False)
|
|
267
|
+
idf = os.path.abspath(os.path.join(folder, 'in.idf'))
|
|
268
|
+
|
|
269
|
+
# load the OSM and translate it to IDF
|
|
270
|
+
exist_os_model = OSModel.load(osm_file)
|
|
271
|
+
if exist_os_model.is_initialized():
|
|
272
|
+
os_model = exist_os_model.get()
|
|
273
|
+
assign_epw_to_model(epw_file, os_model)
|
|
274
|
+
idf_translator = openstudio.energyplus.ForwardTranslator()
|
|
275
|
+
workspace = idf_translator.translateModel(os_model)
|
|
276
|
+
workspace.save(idf, overwrite=True)
|
|
277
|
+
|
|
278
|
+
# run the file through EnergyPlus
|
|
279
|
+
gen_files = [idf]
|
|
280
|
+
sql, eio, rdd, html, err = run_idf(idf, epw_file)
|
|
281
|
+
if err is not None and os.path.isfile(err):
|
|
282
|
+
gen_files.extend([sql, eio, rdd, html, err])
|
|
283
|
+
err_obj = Err(err)
|
|
284
|
+
for error in err_obj.fatal_errors:
|
|
285
|
+
log_file.write(err_obj.file_contents) # log before raising the error
|
|
286
|
+
raise Exception(error)
|
|
287
|
+
else:
|
|
288
|
+
raise Exception('Running EnergyPlus failed.')
|
|
289
|
+
|
|
290
|
+
# parse the error log and report any warnings
|
|
291
|
+
if sql is not None and os.path.isfile('{}-journal'.format(sql)):
|
|
292
|
+
try: # try to finish E+'s cleanup
|
|
293
|
+
os.remove('{}-journal'.format(sql))
|
|
294
|
+
except Exception: # maybe the file is inaccessible
|
|
295
|
+
pass
|
|
296
|
+
log_file.write(json.dumps(gen_files, indent=4))
|
|
297
|
+
except Exception as e:
|
|
298
|
+
_logger.exception('OSM simulation failed.\n{}'.format(e))
|
|
299
|
+
sys.exit(1)
|
|
300
|
+
else:
|
|
301
|
+
sys.exit(0)
|
|
302
|
+
|
|
303
|
+
|
|
304
|
+
@simulate.command('idf')
|
|
305
|
+
@click.argument('idf-file', type=click.Path(
|
|
306
|
+
exists=True, file_okay=True, dir_okay=False, resolve_path=True))
|
|
307
|
+
@click.argument('epw-file', type=click.Path(
|
|
308
|
+
exists=True, file_okay=True, dir_okay=False, resolve_path=True))
|
|
309
|
+
@click.option('--folder', '-f', help='Folder on this computer, into which the '
|
|
310
|
+
'result files will be written. If None, the files will be output '
|
|
311
|
+
'to the honeybee default simulation folder and placed in a project '
|
|
312
|
+
'folder with the same name as the idf_file.',
|
|
313
|
+
default=None, show_default=True,
|
|
314
|
+
type=click.Path(file_okay=False, dir_okay=True, resolve_path=True))
|
|
315
|
+
@click.option('--log-file', '-log', help='Optional log file to output the paths of the '
|
|
316
|
+
'generated files (idf, sql, zsz, rdd, html, err) if successfully'
|
|
317
|
+
' created. By default the list will be printed out to stdout',
|
|
318
|
+
type=click.File('w'), default='-', show_default=True)
|
|
319
|
+
def simulate_idf(idf_file, epw_file, folder, log_file):
|
|
320
|
+
"""Simulate an IDF file in EnergyPlus.
|
|
321
|
+
|
|
322
|
+
\b
|
|
323
|
+
Args:
|
|
324
|
+
idf_file: Full path to a simulate-able .idf file.
|
|
325
|
+
epw_file: Full path to an .epw file.
|
|
326
|
+
"""
|
|
327
|
+
try:
|
|
328
|
+
# set the default folder to the default if it's not specified and copy the IDF
|
|
329
|
+
if folder is None:
|
|
330
|
+
proj_name = os.path.basename(idf_file).replace('.idf', '')
|
|
331
|
+
folder = os.path.join(folders.default_simulation_folder, proj_name)
|
|
332
|
+
preparedir(folder, remove_content=False)
|
|
333
|
+
idf = os.path.join(folder, 'in.idf')
|
|
334
|
+
shutil.copy(idf_file, idf)
|
|
335
|
+
|
|
336
|
+
# run the file through EnergyPlus
|
|
337
|
+
gen_files = [idf]
|
|
338
|
+
sql, eio, rdd, html, err = run_idf(idf, epw_file)
|
|
339
|
+
if err is not None and os.path.isfile(err):
|
|
340
|
+
gen_files.extend([sql, eio, rdd, html, err])
|
|
341
|
+
err_obj = Err(err)
|
|
342
|
+
for error in err_obj.fatal_errors:
|
|
343
|
+
log_file.write(err_obj.file_contents) # log before raising the error
|
|
344
|
+
raise Exception(error)
|
|
345
|
+
else:
|
|
346
|
+
raise Exception('Running EnergyPlus failed.')
|
|
347
|
+
|
|
348
|
+
# parse the error log and report any warnings
|
|
349
|
+
if sql is not None and os.path.isfile('{}-journal'.format(sql)):
|
|
350
|
+
try: # try to finish E+'s cleanup
|
|
351
|
+
os.remove('{}-journal'.format(sql))
|
|
352
|
+
except Exception: # maybe the file is inaccessible
|
|
353
|
+
pass
|
|
354
|
+
log_file.write(json.dumps(gen_files, indent=4))
|
|
355
|
+
except Exception as e:
|
|
356
|
+
_logger.exception('IDF simulation failed.\n{}'.format(e))
|
|
357
|
+
sys.exit(1)
|
|
358
|
+
else:
|
|
359
|
+
sys.exit(0)
|
|
360
|
+
|
|
361
|
+
|
|
362
|
+
def _sense_input_file_type(model_file):
|
|
363
|
+
"""Sense whether an input model_file is a HBJSON, OSM, or IDF.
|
|
364
|
+
|
|
365
|
+
Args:
|
|
366
|
+
model_file: A file, which will have its contents evaluated to determine
|
|
367
|
+
the file type.
|
|
368
|
+
"""
|
|
369
|
+
# sense the file type from the first character to avoid maxing memory with JSON
|
|
370
|
+
# this is needed since queenbee overwrites all file extensions
|
|
371
|
+
with open(model_file) as inf:
|
|
372
|
+
first_char = inf.read(1)
|
|
373
|
+
if first_char == '{':
|
|
374
|
+
return 'hbjson'
|
|
375
|
+
with open(model_file) as inf:
|
|
376
|
+
inf.readline()
|
|
377
|
+
second_line = inf.readline()
|
|
378
|
+
if 'OS:Version,' in second_line:
|
|
379
|
+
return 'osm'
|
|
380
|
+
return 'idf'
|