Skip to content

Commit a035fdc

Browse files
committed
feat(cli): Add a CLI to translate Dragonfly Models to CSV
1 parent 3bb208e commit a035fdc

11 files changed

Lines changed: 657 additions & 15 deletions

File tree

dragonfly_trace/airflows.py

Lines changed: 139 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,9 @@
22
"""Methods to write room airflows to matrices for Trane TRACE tables."""
33
from __future__ import division
44

5+
from ladybug.datatype.volumeflowrate import VolumeFlowRate
6+
from ladybug.datatype.volumeflowrateintensity import VolumeFlowRateIntensity
7+
58

69
def airflows_trace700_matrix(rooms, si_units=False):
710
"""Get a matrix for the "Airflows" table of the TRACE 700 Component Tree.
@@ -16,3 +19,139 @@ def airflows_trace700_matrix(rooms, si_units=False):
1619
A list of list where each sublist represents a row of the Airflows
1720
table of the TRACE 700 Component Tree.
1821
"""
22+
# set up things for unit conversion
23+
flow_unit = 'L/s' if si_units else 'cfm'
24+
flow_intensity_unit = 'L/s/sq m' if si_units else 'cfm/sq ft of wall'
25+
flow, fi = VolumeFlowRate(), VolumeFlowRateIntensity()
26+
27+
# set up the names of the rows
28+
row_names = [
29+
'Room Description',
30+
'Adjacent Air Transfer from Room',
31+
'Airflow Template',
32+
'Ventilation Method',
33+
'Ventilation Type',
34+
'Ventilation Cooling',
35+
'Ventilation Cooling Units',
36+
'Ventilation Heating',
37+
'Ventilation Heating Units',
38+
'People-based Rate (Rp)',
39+
'People-based Unit',
40+
'Area-based Rate (Ra)',
41+
'Area-based Unit',
42+
'Ventilation Schedule',
43+
'Std62.1-2004-2010 Clg Ez',
44+
'Std62.1-2004-2010 Clg Ez Pct',
45+
'Std62.1-2004-2010 Htg Ez',
46+
'Std62.1-2004-2010 Htg Ez Pct',
47+
'Std62.1-2004-2010 Er',
48+
'Std62.1-2004-2010 Er Pct',
49+
'DCV Min OA Intake',
50+
'DCV Min OA Intake Unit',
51+
'Infiltration Type',
52+
'Infiltration Cooling',
53+
'Infiltration Cooling Units',
54+
'Infiltration Heating',
55+
'Infiltration Heating Units',
56+
'Infiltration Schedule',
57+
'Main Supply Cooling',
58+
'Main Supply Cooling Units',
59+
'Main Supply Heating',
60+
'Main Supply Heating Units',
61+
'Aux Supply Cooling',
62+
'Aux Supply Cooling Units',
63+
'Aux Supply Heating',
64+
'Aux Supply Heating Units',
65+
'Cooling VAV Min Airflow',
66+
'Cooling VAV Min Airflow Units',
67+
'Heating VAV Max Airflow',
68+
'Heating VAV Max Airflow Units',
69+
'VAV Airflow Schedule',
70+
'VAV Type',
71+
'Room Exhaust',
72+
'Room Exhaust Units',
73+
'Room Exhaust Schedule'
74+
]
75+
76+
# loop through the rooms and add each of the attributes
77+
airflow_mtx = []
78+
for room in rooms:
79+
# calculate the total outdoor air ventilation and infiltration
80+
vent_obj = room.properties.energy.ventilation
81+
vent_flow = vent_obj.room_absolute_flow(room) if vent_obj is not None else 0
82+
inf_obj = room.properties.energy.infiltration
83+
inf_flow = inf_obj.flow_per_exterior_area if inf_obj is not None else 0
84+
85+
# put all attributes into a list
86+
airflow_attr = [
87+
room.display_name,
88+
'<<No adjacent room>>',
89+
'Default',
90+
'Sum of Outdoor Air',
91+
'None',
92+
vent_flow,
93+
flow_unit,
94+
vent_flow,
95+
flow_unit,
96+
'',
97+
'',
98+
'',
99+
'',
100+
'Available (100%)',
101+
'',
102+
'',
103+
'',
104+
'',
105+
'',
106+
'',
107+
'',
108+
'',
109+
'None',
110+
inf_flow,
111+
flow_intensity_unit,
112+
inf_flow,
113+
flow_intensity_unit,
114+
'Available (100%)',
115+
'',
116+
'To be calculated',
117+
'',
118+
'To be calculated',
119+
'',
120+
'To be calculated',
121+
'',
122+
'To be calculated',
123+
'',
124+
'% Clg Airflow',
125+
'',
126+
'% Clg Airflow',
127+
'Available (100%)',
128+
'Default',
129+
'0',
130+
'air changes/hr',
131+
'Available (100%)'
132+
]
133+
airflow_mtx.append(airflow_attr)
134+
135+
# transpose the matrix and convert SI units to IP
136+
airflow_matrix = [list(row) for row in zip(*airflow_mtx)]
137+
if not si_units:
138+
airflow_matrix[5] = list(flow.to_unit(airflow_matrix[5], 'cfm', 'm3/s'))
139+
airflow_matrix[7] = list(flow.to_unit(airflow_matrix[7], 'cfm', 'm3/s'))
140+
airflow_matrix[23] = list(fi.to_unit(airflow_matrix[23], 'cfm/ft2', 'm3/s-m2'))
141+
airflow_matrix[25] = list(fi.to_unit(airflow_matrix[25], 'cfm/ft2', 'm3/s-m2'))
142+
else:
143+
airflow_matrix[5] = list(flow.to_unit(airflow_matrix[5], 'L/s', 'm3/s'))
144+
airflow_matrix[7] = list(flow.to_unit(airflow_matrix[7], 'L/s', 'm3/s'))
145+
airflow_matrix[23] = list(fi.to_unit(airflow_matrix[23], 'L/s-m2', 'm3/s-m2'))
146+
airflow_matrix[25] = list(fi.to_unit(airflow_matrix[25], 'L/s-m2', 'm3/s-m2'))
147+
148+
# round the numbers so that they display nicely
149+
for row_i in (5, 7):
150+
airflow_matrix[row_i] = [round(val, 1) for val in airflow_matrix[row_i]]
151+
for row_i in (23, 25):
152+
airflow_matrix[row_i] = [round(val, 3) for val in airflow_matrix[row_i]]
153+
154+
# insert the column for the row names
155+
for row_name, row in zip(row_names, airflow_matrix):
156+
row.insert(0, row_name)
157+
return airflow_matrix

dragonfly_trace/cli/__init__.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22
import click
33
from dragonfly.cli import main
44

5+
from .translate import translate
6+
57

68
@click.group(help='dragonfly trace commands.')
79
@click.version_option()
@@ -10,6 +12,7 @@ def trace():
1012

1113

1214
# add sub-commands to trace
15+
trace.add_command(translate)
1316

1417
# add trace sub-commands
1518
main.add_command(trace)

dragonfly_trace/cli/translate.py

Lines changed: 149 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,149 @@
1+
"""dragonfly trace translation commands."""
2+
import click
3+
import sys
4+
import logging
5+
6+
from ladybug.commandutil import process_content_to_output
7+
from dragonfly.model import Model
8+
from dragonfly_trace.writer import model_to_trace700_csv as model_to_csv
9+
10+
11+
_logger = logging.getLogger(__name__)
12+
13+
14+
@click.group(help='Commands for translating URBANopt systems to OSM/IDF.')
15+
def translate():
16+
pass
17+
18+
19+
@translate.command('model-to-trace700-csv')
20+
@click.argument('model-file', type=click.Path(
21+
exists=True, file_okay=True, dir_okay=False, resolve_path=True))
22+
@click.option('--multiplier/--full-geometry', ' /-fg', help='Flag to note if the '
23+
'multipliers on each Building story will be passed along to the '
24+
'generated Honeybee Room objects or if full geometry objects should be '
25+
'written for each story in the building.', default=True, show_default=True)
26+
@click.option('--plenum/--separate-plenum', '-p/-sp', help='Flag to indicate whether '
27+
'ceiling/floor plenum depths assigned to Room2Ds should simply be '
28+
'reported as plenum depths in the CSV or they should be used to generate '
29+
'distinct separated plenum rooms in the translation.',
30+
default=True, show_default=True)
31+
@click.option('--merge-method', '-m', help='Text to describe how the Room2Ds should '
32+
'be merged into individual Rooms during the translation. Specifying a '
33+
'value here can be an effective way to reduce the number of Room '
34+
'volumes in the resulting Model and, ultimately, yield a faster simulation '
35+
'time with less results to manage. Choose from: None, Zones, PlenumZones, '
36+
'Stories, PlenumStories.', type=str, default='None', show_default=True)
37+
@click.option('--imperial/--metric', '-ip/-si', help='Flag to note whether imperial '
38+
'or metric units should be used for values in the output CSV.',
39+
default=True, show_default=True)
40+
@click.option('--geometry-ids/--geometry-names', ' /-gn', help='Flag to note whether a '
41+
'cleaned version of all geometry display names should be used instead '
42+
'of identifiers when translating the Model.',
43+
default=True, show_default=True)
44+
@click.option('--resource-ids/--resource-names', ' /-rn', help='Flag to note whether a '
45+
'cleaned version of all resource display names should be used instead '
46+
'of identifiers when translating the Model.',
47+
default=True, show_default=True)
48+
@click.option('--output-file', '-f', help='Optional CSV file to output the string '
49+
'of the translation. By default it printed out to stdout.',
50+
type=click.File('w'), default='-', show_default=True)
51+
def model_to_trace700_csv_cli(
52+
model_file, multiplier, plenum, merge_method, imperial,
53+
geometry_ids, resource_ids, output_file
54+
):
55+
"""Translate a Dragonfly Model to a CSV with tables for TRACE 700 attributes.
56+
57+
The resulting CSV tables can be copied into the tables that appear in the
58+
Component Tree view of TRACE 700. The order and organization of rooms in
59+
the resulting matrix should match that of the gbXML produced from the same model.
60+
61+
\b
62+
Args:
63+
model_file: Full path to a Dragonfly Model file (DFJSON or DFpkl).
64+
"""
65+
try:
66+
full_geometry = not multiplier
67+
separate_plenum = not plenum
68+
metric = not imperial
69+
geo_names = not geometry_ids
70+
res_names = not resource_ids
71+
model_to_trace700_csv(
72+
model_file, full_geometry, separate_plenum, merge_method,
73+
metric, geo_names, res_names, output_file
74+
)
75+
except Exception as e:
76+
_logger.exception('System translation failed.\n{}'.format(e))
77+
sys.exit(1)
78+
else:
79+
sys.exit(0)
80+
81+
82+
def model_to_trace700_csv(
83+
model_file, full_geometry=False, separate_plenum=False, merge_method='None',
84+
metric=False, geometry_names=False, resource_names=False, output_file=None,
85+
multiplier=True, plenum=True, imperial=True, geometry_ids=True, resource_ids=True
86+
):
87+
"""Translate a Dragonfly Model to a CSV with tables for TRACE 700 attributes.
88+
89+
The resulting CSV tables can be copied into the tables that appear in the
90+
Component Tree view of TRACE 700. The order and organization of rooms in
91+
the resulting matrix should match that of the gbXML produced from the same model.
92+
93+
Args:
94+
model: A dragonfly Model for which a TRACE 700 CSV matrix will be returned.
95+
multiplier: If True, the multipliers on this Model's Stories will be
96+
passed along to the CSV. If False, full geometry objects will be written
97+
for each and every floor in the building that are represented through
98+
multipliers and all resulting multipliers will be 1. (Default: True).
99+
separate_plenum: Boolean to indicate whether ceiling/floor plenum depths
100+
assigned to Room2Ds should simply be reported as plenum depths in the
101+
CSV or they should be used to generate distinct separated plenum
102+
rooms in the translation. (Default: False).
103+
merge_method: An optional text string to describe how the Room2Ds should
104+
be merged into individual Rooms during the translation. Specifying a
105+
value here can be an effective way to reduce the number of Room
106+
volumes in the resulting model and, ultimately, yield a faster
107+
simulation time in the destination engine with fewer results
108+
to manage. Note that Room2Ds will only be merged if they form a
109+
continuous volume. Otherwise, there will be multiple Rooms per
110+
zone or story, each with an integer added at the end of their
111+
identifiers. Choose from the following options:
112+
113+
* None - No merging of Room2Ds will occur
114+
* Zones - Room2Ds in the same zone will be merged
115+
* PlenumZones - Only plenums in the same zone will be merged
116+
* Stories - Rooms in the same story will be merged
117+
* PlenumStories - Only plenums in the same story will be merged
118+
119+
metric: Boolean to note whether the units of the values in the resulting
120+
matrix are in SI (True) instead of IP (False). (Default: False).
121+
geometry_names: Boolean to note whether a cleaned version of all geometry
122+
display names should be used instead of identifiers when translating
123+
the Model to OSM and IDF. Using this flag will affect all Rooms, Faces,
124+
Apertures, Doors, and Shades. It will generally result in more read-able
125+
names in the OSM and IDF but this means that it will not be easy to map
126+
the EnergyPlus results back to the original Honeybee Model. Cases
127+
of duplicate IDs resulting from non-unique names will be resolved
128+
by adding integers to the ends of the new IDs that are derived from
129+
the name. (Default: False).
130+
resource_names: Boolean to note whether a cleaned version of all resource
131+
display names should be used instead of identifiers when translating
132+
the Model to OSM and IDF. Using this flag will affect all Materials,
133+
Constructions, ConstructionSets, Schedules, Loads, and ProgramTypes.
134+
It will generally result in more read-able names for the resources
135+
in the OSM and IDF. Cases of duplicate IDs resulting from non-unique
136+
names will be resolved by adding integers to the ends of the new IDs
137+
that are derived from the name. (Default: False).
138+
output_file: Optional CSV file to output the CSV string of the translation.
139+
By default this string will be returned from this method.
140+
"""
141+
# load the model and translate it to a CSV
142+
model = Model.from_file(model_file)
143+
exclude_plenums = not separate_plenum
144+
csv_str = model_to_csv(
145+
model, multiplier, exclude_plenums, merge_method,
146+
metric, geometry_names, resource_names
147+
)
148+
149+
return process_content_to_output(csv_str, output_file)

0 commit comments

Comments
 (0)