honeybee-core 1.64.12__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/__init__.py +23 -0
- honeybee/__main__.py +4 -0
- honeybee/_base.py +331 -0
- honeybee/_basewithshade.py +310 -0
- honeybee/_lockable.py +99 -0
- honeybee/altnumber.py +47 -0
- honeybee/aperture.py +997 -0
- honeybee/boundarycondition.py +358 -0
- honeybee/checkdup.py +173 -0
- honeybee/cli/__init__.py +118 -0
- honeybee/cli/compare.py +132 -0
- honeybee/cli/create.py +265 -0
- honeybee/cli/edit.py +559 -0
- honeybee/cli/lib.py +103 -0
- honeybee/cli/setconfig.py +43 -0
- honeybee/cli/validate.py +224 -0
- honeybee/colorobj.py +363 -0
- honeybee/config.json +5 -0
- honeybee/config.py +347 -0
- honeybee/dictutil.py +54 -0
- honeybee/door.py +746 -0
- honeybee/extensionutil.py +208 -0
- honeybee/face.py +2360 -0
- honeybee/facetype.py +153 -0
- honeybee/logutil.py +79 -0
- honeybee/model.py +4272 -0
- honeybee/orientation.py +132 -0
- honeybee/properties.py +845 -0
- honeybee/room.py +3485 -0
- honeybee/search.py +107 -0
- honeybee/shade.py +514 -0
- honeybee/shademesh.py +362 -0
- honeybee/typing.py +498 -0
- honeybee/units.py +88 -0
- honeybee/writer/__init__.py +7 -0
- honeybee/writer/aperture.py +6 -0
- honeybee/writer/door.py +6 -0
- honeybee/writer/face.py +6 -0
- honeybee/writer/model.py +6 -0
- honeybee/writer/room.py +6 -0
- honeybee/writer/shade.py +6 -0
- honeybee/writer/shademesh.py +6 -0
- honeybee_core-1.64.12.dist-info/METADATA +94 -0
- honeybee_core-1.64.12.dist-info/RECORD +48 -0
- honeybee_core-1.64.12.dist-info/WHEEL +5 -0
- honeybee_core-1.64.12.dist-info/entry_points.txt +2 -0
- honeybee_core-1.64.12.dist-info/licenses/LICENSE +661 -0
- honeybee_core-1.64.12.dist-info/top_level.txt +1 -0
honeybee/cli/compare.py
ADDED
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
"""honeybee comparison commands."""
|
|
2
|
+
import sys
|
|
3
|
+
import logging
|
|
4
|
+
import json
|
|
5
|
+
import click
|
|
6
|
+
|
|
7
|
+
from honeybee.model import Model
|
|
8
|
+
from honeybee.typing import fixed_string_length
|
|
9
|
+
_logger = logging.getLogger(__name__)
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
@click.group(help='Commands for comparing Honeybee objects.')
|
|
13
|
+
def compare():
|
|
14
|
+
pass
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
@compare.command('models')
|
|
18
|
+
@click.argument('base-model-file', type=click.Path(
|
|
19
|
+
exists=True, file_okay=True, dir_okay=False, resolve_path=True))
|
|
20
|
+
@click.argument('other-model-file', type=click.Path(
|
|
21
|
+
exists=True, file_okay=True, dir_okay=False, resolve_path=True))
|
|
22
|
+
@click.option(
|
|
23
|
+
'--include-deleted/--ignore-deleted', ' /-d', help='Flag to note whether '
|
|
24
|
+
'objects that appear in the base model but not in the other model should '
|
|
25
|
+
'be reported. It is useful to ignore-deleted when the other model represents '
|
|
26
|
+
'only a subset of the current model.', default=True, show_default=True)
|
|
27
|
+
@click.option(
|
|
28
|
+
'--include-added/--ignore-added', ' /-a', help='Flag to note whether '
|
|
29
|
+
'whether objects that appear in the other model but not in the current '
|
|
30
|
+
'model should be reported.', default=True, show_default=True)
|
|
31
|
+
@click.option(
|
|
32
|
+
'--plain-text/--json', ' /-j', help='Flag to note whether the output validation '
|
|
33
|
+
'report should be formatted as a JSON object instead of plain text. If set to JSON, '
|
|
34
|
+
'the output object will contain several attributes. The "honeybee_core" and '
|
|
35
|
+
'"honeybee_schema" attributes will note the versions of these libraries used in '
|
|
36
|
+
'the validation process. An attribute called "fatal_error" is a text string '
|
|
37
|
+
'containing an exception if the Model failed to serialize and will be an empty '
|
|
38
|
+
'string if serialization was successful. An attribute called "errors" will '
|
|
39
|
+
'contain a list of JSON objects for each invalid issue found in the model. A '
|
|
40
|
+
'boolean attribute called "valid" will note whether the Model is valid or not.',
|
|
41
|
+
default=True, show_default=True)
|
|
42
|
+
@click.option(
|
|
43
|
+
'--output-file', '-f', help='Optional file to output the full report '
|
|
44
|
+
'of differences between models. By default it will be printed out to stdout',
|
|
45
|
+
type=click.File('w'), default='-')
|
|
46
|
+
def compare_models(
|
|
47
|
+
base_model_file, other_model_file, include_deleted, include_added,
|
|
48
|
+
plain_text, output_file):
|
|
49
|
+
"""Get a report outlining the differences between this model and another.
|
|
50
|
+
|
|
51
|
+
The resulting report will only note top-level objects that are different
|
|
52
|
+
between this model and the other. If an object has not changed at all,
|
|
53
|
+
then it will not show up in the report.
|
|
54
|
+
|
|
55
|
+
Changes to geometry are reported separately from changes in metadata
|
|
56
|
+
(aka. extension properties) for each of the top level objects.
|
|
57
|
+
|
|
58
|
+
If the Model units or tolerance are different between the two models,
|
|
59
|
+
then the units and tolerance of this model will take precedence and
|
|
60
|
+
the other_model will be converted to these units and tolerance for
|
|
61
|
+
geometry comparison.
|
|
62
|
+
|
|
63
|
+
\b
|
|
64
|
+
Args:
|
|
65
|
+
base_model: Full path to a Honeybee Model file to be used as the base
|
|
66
|
+
of comparison.
|
|
67
|
+
other_model: Full path to a Honeybee Model file to which the base model
|
|
68
|
+
will be compared.
|
|
69
|
+
"""
|
|
70
|
+
try:
|
|
71
|
+
# re-serialize the Models
|
|
72
|
+
base_model = Model.from_file(base_model_file)
|
|
73
|
+
other_model = Model.from_file(other_model_file)
|
|
74
|
+
ignore_deleted = not include_deleted
|
|
75
|
+
ignore_added = not include_added
|
|
76
|
+
# generate the comparison report
|
|
77
|
+
report_dict = base_model.comparison_report(
|
|
78
|
+
other_model, ignore_deleted, ignore_added)
|
|
79
|
+
# write the report into a file or stdout
|
|
80
|
+
if plain_text: # convert the report into plain text
|
|
81
|
+
report_text = ['COMPARISON REPORT']
|
|
82
|
+
# process the changed objects into readable text
|
|
83
|
+
if 'changed_objects' in report_dict and \
|
|
84
|
+
len(report_dict['changed_objects']) != 0:
|
|
85
|
+
report_text.append('------------------------')
|
|
86
|
+
report_text.append('CHANGED OBJECTS')
|
|
87
|
+
h_txt = ' NAME TYPE '
|
|
88
|
+
change_keys = []
|
|
89
|
+
for prop in report_dict['changed_objects'][0]:
|
|
90
|
+
if '_changed' in prop:
|
|
91
|
+
change_keys.append(prop)
|
|
92
|
+
h_txt += fixed_string_length(
|
|
93
|
+
prop.replace('_changed', '').upper(), 10)
|
|
94
|
+
report_text.append(h_txt)
|
|
95
|
+
for item in report_dict['changed_objects']:
|
|
96
|
+
item_txt = ' ' + fixed_string_length(item['element_name'], 30) + \
|
|
97
|
+
' ' + fixed_string_length(item['element_type'], 10)
|
|
98
|
+
for key in change_keys:
|
|
99
|
+
if item[key]:
|
|
100
|
+
item_txt += ' X '
|
|
101
|
+
else:
|
|
102
|
+
item_txt += ' '
|
|
103
|
+
report_text.append(item_txt)
|
|
104
|
+
# process the added objects into readable text
|
|
105
|
+
if 'added_objects' in report_dict and \
|
|
106
|
+
len(report_dict['added_objects']) != 0:
|
|
107
|
+
report_text.append('------------------------')
|
|
108
|
+
report_text.append('ADDED OBJECTS')
|
|
109
|
+
h_txt = ' NAME TYPE '
|
|
110
|
+
for item in report_dict['added_objects']:
|
|
111
|
+
item_txt = ' ' + fixed_string_length(item['element_name'], 30) + \
|
|
112
|
+
' ' + fixed_string_length(item['element_type'], 10)
|
|
113
|
+
report_text.append(item_txt)
|
|
114
|
+
# process the deleted objects into readable text
|
|
115
|
+
if 'deleted_objects' in report_dict and \
|
|
116
|
+
len(report_dict['deleted_objects']) != 0:
|
|
117
|
+
report_text.append('------------------------')
|
|
118
|
+
report_text.append('DELETED OBJECTS')
|
|
119
|
+
h_txt = ' NAME TYPE '
|
|
120
|
+
for item in report_dict['deleted_objects']:
|
|
121
|
+
item_txt = ' ' + fixed_string_length(item['element_name'], 30) + \
|
|
122
|
+
' ' + fixed_string_length(item['element_type'], 10)
|
|
123
|
+
report_text.append(item_txt)
|
|
124
|
+
# write the output file
|
|
125
|
+
output_file.write('\n'.join(report_text))
|
|
126
|
+
else: # just dump the dictionary to a JSON
|
|
127
|
+
output_file.write(json.dumps(report_dict))
|
|
128
|
+
except Exception as e:
|
|
129
|
+
_logger.exception('Model comparison failed.\n{}'.format(e))
|
|
130
|
+
sys.exit(1)
|
|
131
|
+
else:
|
|
132
|
+
sys.exit(0)
|
honeybee/cli/create.py
ADDED
|
@@ -0,0 +1,265 @@
|
|
|
1
|
+
"""honeybee model creation commands."""
|
|
2
|
+
import click
|
|
3
|
+
import sys
|
|
4
|
+
import logging
|
|
5
|
+
import json
|
|
6
|
+
|
|
7
|
+
from honeybee.model import Model
|
|
8
|
+
from honeybee.boundarycondition import boundary_conditions as bcs
|
|
9
|
+
try:
|
|
10
|
+
ad_bc = bcs.adiabatic
|
|
11
|
+
except AttributeError: # honeybee_energy is not loaded and adiabatic does not exist
|
|
12
|
+
ad_bc = None
|
|
13
|
+
|
|
14
|
+
_logger = logging.getLogger(__name__)
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
@click.group(help='Commands for creating Honeybee models.')
|
|
18
|
+
def create():
|
|
19
|
+
pass
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
@create.command('shoe-box')
|
|
23
|
+
@click.argument('width', type=float)
|
|
24
|
+
@click.argument('depth', type=float)
|
|
25
|
+
@click.argument('height', type=float)
|
|
26
|
+
@click.option('--orientation-angle', '-a', help='A number between 0 and 360 for the '
|
|
27
|
+
'clockwise orientation of the box in degrees. (0=North, 90=East, 180='
|
|
28
|
+
'South, 270=West).', type=float, default=0, show_default=True)
|
|
29
|
+
@click.option('--window-ratio', '-wr', help='A number between 0 and 1 (but not equal '
|
|
30
|
+
'to 1) for the ratio between aperture area and area of the face pointing '
|
|
31
|
+
'towards the orientation-angle. Using 0 will generate no windows',
|
|
32
|
+
type=float, default=0, show_default=True)
|
|
33
|
+
@click.option('--adiabatic/--outdoors', ' /-o', help='Flag to note whether the faces '
|
|
34
|
+
'that are not in the direction of the orientation-angle are adiabatic or '
|
|
35
|
+
'outdoors.', default=True, show_default=True)
|
|
36
|
+
@click.option('--units', '-u', help=' Text for the units system in which the model '
|
|
37
|
+
'geometry exists. Must be (Meters, Millimeters, Feet, Inches, '
|
|
38
|
+
'Centimeters).', type=str, default='Meters', show_default=True)
|
|
39
|
+
@click.option('--tolerance', '-t', help='The maximum difference between x, y, and z '
|
|
40
|
+
'values at which vertices are considered equivalent.',
|
|
41
|
+
type=float, default=None)
|
|
42
|
+
@click.option('--output-file', '-f', help='Optional file to output the Model JSON '
|
|
43
|
+
'string. By default it will be printed out to stdout',
|
|
44
|
+
type=click.File('w'), default='-')
|
|
45
|
+
def shoe_box(width, depth, height, orientation_angle, window_ratio, adiabatic,
|
|
46
|
+
units, tolerance, output_file):
|
|
47
|
+
"""Create a model with a single shoe box Room.
|
|
48
|
+
|
|
49
|
+
\b
|
|
50
|
+
Args:
|
|
51
|
+
width: Number for the width of the box (in the X direction).
|
|
52
|
+
depth: Number for the depth of the box (in the Y direction).
|
|
53
|
+
height: Number for the height of the box (in the Z direction).
|
|
54
|
+
"""
|
|
55
|
+
try:
|
|
56
|
+
# create the model object
|
|
57
|
+
model = Model.from_shoe_box(
|
|
58
|
+
width, depth, height, orientation_angle, window_ratio,
|
|
59
|
+
adiabatic, units, tolerance)
|
|
60
|
+
# write the model out to the file or stdout
|
|
61
|
+
output_file.write(json.dumps(model.to_dict()))
|
|
62
|
+
except Exception as e:
|
|
63
|
+
_logger.exception('Shoe box creation failed.\n{}'.format(e))
|
|
64
|
+
sys.exit(1)
|
|
65
|
+
else:
|
|
66
|
+
sys.exit(0)
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
@create.command('rectangle-plan')
|
|
70
|
+
@click.argument('width', type=float)
|
|
71
|
+
@click.argument('length', type=float)
|
|
72
|
+
@click.argument('floor-to-floor-height', type=float)
|
|
73
|
+
@click.option('--perimeter-offset', '-p', help='An optional positive number that will '
|
|
74
|
+
'be used to offset the perimeter to create core/perimeter Rooms. '
|
|
75
|
+
'If this value is 0, no offset will occur and each floor will have one '
|
|
76
|
+
'Room', type=float, default=0, show_default=True)
|
|
77
|
+
@click.option('--story-count', '-s', help='An integer for the number of stories to '
|
|
78
|
+
'generate.', type=int, default=1, show_default=True)
|
|
79
|
+
@click.option('--orientation-angle', '-a', help='A number between 0 and 360 for the '
|
|
80
|
+
'counterclockwise orientation that the width of the box faces.',
|
|
81
|
+
type=float, default=0, show_default=True)
|
|
82
|
+
@click.option('--outdoor-roof/--adiabatic-roof', ' /-ar', help='Flag to note whether '
|
|
83
|
+
'the roof faces of the top floor should be outdoor or adiabatic.',
|
|
84
|
+
default=True, show_default=True)
|
|
85
|
+
@click.option('--ground-floor/--adiabatic-floor', ' /-af', help='Flag to note whether '
|
|
86
|
+
'the floor faces of the bottom floor should be ground or adiabatic.',
|
|
87
|
+
default=True, show_default=True)
|
|
88
|
+
@click.option('--units', '-u', help=' Text for the units system in which the model '
|
|
89
|
+
'geometry exists. Must be (Meters, Millimeters, Feet, Inches, '
|
|
90
|
+
'Centimeters).', type=str, default='Meters', show_default=True)
|
|
91
|
+
@click.option('--tolerance', '-t', help='The maximum difference between x, y, and z '
|
|
92
|
+
'values at which vertices are considered equivalent.',
|
|
93
|
+
type=float, default=None)
|
|
94
|
+
@click.option('--output-file', '-f', help='Optional file to output the Model JSON '
|
|
95
|
+
'string. By default it will be printed out to stdout',
|
|
96
|
+
type=click.File('w'), default='-')
|
|
97
|
+
def rectangle_plan(width, length, floor_to_floor_height, perimeter_offset, story_count,
|
|
98
|
+
orientation_angle, outdoor_roof, ground_floor, units, tolerance,
|
|
99
|
+
output_file):
|
|
100
|
+
"""Create a model with a rectangular floor plan.
|
|
101
|
+
|
|
102
|
+
Note that the resulting Rooms in the model won't have any windows or solved
|
|
103
|
+
adjacencies. The edit commands should be used for this purpose.
|
|
104
|
+
|
|
105
|
+
\b
|
|
106
|
+
Args:
|
|
107
|
+
width: Number for the width of the plan (in the X direction).
|
|
108
|
+
length: Number for the length of the plan (in the Y direction).
|
|
109
|
+
floor_to_floor_height: Number for the height of each floor of the model
|
|
110
|
+
(in the Z direction).
|
|
111
|
+
"""
|
|
112
|
+
try:
|
|
113
|
+
# create the model object
|
|
114
|
+
model = Model.from_rectangle_plan(
|
|
115
|
+
width, length, floor_to_floor_height, perimeter_offset, story_count,
|
|
116
|
+
orientation_angle, outdoor_roof, ground_floor, units, tolerance)
|
|
117
|
+
# write the model out to the file or stdout
|
|
118
|
+
output_file.write(json.dumps(model.to_dict()))
|
|
119
|
+
except Exception as e:
|
|
120
|
+
_logger.exception('Rectangle plan model creation failed.\n{}'.format(e))
|
|
121
|
+
sys.exit(1)
|
|
122
|
+
else:
|
|
123
|
+
sys.exit(0)
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
@create.command('l-shaped-plan')
|
|
127
|
+
@click.argument('width-1', type=float)
|
|
128
|
+
@click.argument('length-1', type=float)
|
|
129
|
+
@click.argument('width-2', type=float)
|
|
130
|
+
@click.argument('length-2', type=float)
|
|
131
|
+
@click.argument('floor-to-floor-height', type=float)
|
|
132
|
+
@click.option('--perimeter-offset', '-p', help='An optional positive number that will be'
|
|
133
|
+
' used to offset the perimeter to create core/perimeter Rooms. '
|
|
134
|
+
'If this value is 0, no offset will occur and each floor will have one '
|
|
135
|
+
'Room', type=float, default=0, show_default=True)
|
|
136
|
+
@click.option('--story-count', '-s', help='An integer for the number of stories to '
|
|
137
|
+
'generate.', type=int, default=1, show_default=True)
|
|
138
|
+
@click.option('--orientation-angle', '-a', help='A number between 0 and 360 for the '
|
|
139
|
+
'counterclockwise orientation that the L faces.',
|
|
140
|
+
type=float, default=0, show_default=True)
|
|
141
|
+
@click.option('--outdoor-roof/--adiabatic-roof', ' /-ar', help='Flag to note whether '
|
|
142
|
+
'the roof faces of the top floor should be outdoor or adiabatic.',
|
|
143
|
+
default=True, show_default=True)
|
|
144
|
+
@click.option('--ground-floor/--adiabatic-floor', ' /-af', help='Flag to note whether '
|
|
145
|
+
'the floor faces of the bottom floor should be ground or adiabatic.',
|
|
146
|
+
default=True, show_default=True)
|
|
147
|
+
@click.option('--units', '-u', help=' Text for the units system in which the model '
|
|
148
|
+
'geometry exists. Must be (Meters, Millimeters, Feet, Inches, '
|
|
149
|
+
'Centimeters).', type=str, default='Meters', show_default=True)
|
|
150
|
+
@click.option('--tolerance', '-t', help='The maximum difference between x, y, and z '
|
|
151
|
+
'values at which vertices are considered equivalent.',
|
|
152
|
+
type=float, default=None)
|
|
153
|
+
@click.option('--output-file', '-f', help='Optional file to output the Model JSON '
|
|
154
|
+
'string. By default it will be printed out to stdout',
|
|
155
|
+
type=click.File('w'), default='-')
|
|
156
|
+
def l_shaped_plan(width_1, length_1, width_2, length_2, floor_to_floor_height,
|
|
157
|
+
perimeter_offset, story_count, orientation_angle, outdoor_roof,
|
|
158
|
+
ground_floor, units, tolerance, output_file):
|
|
159
|
+
"""Create a model with an L-shaped floor plan.
|
|
160
|
+
|
|
161
|
+
Note that the resulting Rooms in the model won't have any windows or solved
|
|
162
|
+
adjacencies. The edit commands should be used for this purpose.
|
|
163
|
+
|
|
164
|
+
\b
|
|
165
|
+
Args:
|
|
166
|
+
width_1: Number for the width of the lower part of the L segment.
|
|
167
|
+
length_1: Number for the length of the lower part of the L segment, not
|
|
168
|
+
counting the overlap between the upper and lower segments.
|
|
169
|
+
width_2: Number for the width of the upper (left) part of the L segment.
|
|
170
|
+
length_2: Number for the length of the upper (left) part of the L segment, not
|
|
171
|
+
counting the overlap between the upper and lower segments.
|
|
172
|
+
floor_to_floor_height: Number for the height of each floor of the model
|
|
173
|
+
(in the Z direction).
|
|
174
|
+
"""
|
|
175
|
+
try:
|
|
176
|
+
# create the model object
|
|
177
|
+
model = Model.from_l_shaped_plan(
|
|
178
|
+
width_1, length_1, width_2, length_2, floor_to_floor_height,
|
|
179
|
+
perimeter_offset, story_count, orientation_angle, outdoor_roof, ground_floor,
|
|
180
|
+
units, tolerance)
|
|
181
|
+
# write the model out to the file or stdout
|
|
182
|
+
output_file.write(json.dumps(model.to_dict()))
|
|
183
|
+
except Exception as e:
|
|
184
|
+
_logger.exception('L Shaped plan model creation failed.\n{}'.format(e))
|
|
185
|
+
sys.exit(1)
|
|
186
|
+
else:
|
|
187
|
+
sys.exit(0)
|
|
188
|
+
|
|
189
|
+
|
|
190
|
+
@create.command('from-sync')
|
|
191
|
+
@click.argument('base-model-file', type=click.Path(
|
|
192
|
+
exists=True, file_okay=True, dir_okay=False, resolve_path=True))
|
|
193
|
+
@click.argument('other-model-file', type=click.Path(
|
|
194
|
+
exists=True, file_okay=True, dir_okay=False, resolve_path=True))
|
|
195
|
+
@click.argument('sync-instructions-file', type=click.Path(
|
|
196
|
+
exists=True, file_okay=True, dir_okay=False, resolve_path=True))
|
|
197
|
+
@click.option(
|
|
198
|
+
'--output-file', '-f', help='Optional file to output the Model JSON '
|
|
199
|
+
'string. By default it will be printed out to stdout',
|
|
200
|
+
type=click.File('w'), default='-')
|
|
201
|
+
def create_from_sync(
|
|
202
|
+
base_model_file, other_model_file, sync_instructions_file, output_file):
|
|
203
|
+
"""Create a Model from two similar model files and instructions for syncing them.
|
|
204
|
+
|
|
205
|
+
\b
|
|
206
|
+
Args:
|
|
207
|
+
base_model_file: An base Honeybee Model (as HBJSON or HBPkl)
|
|
208
|
+
that forms the base of the new model to be created.
|
|
209
|
+
other_model_file: An other Honeybee Model (as HBJSON or HBPkl)
|
|
210
|
+
that contains changes to the base model to be merged into
|
|
211
|
+
the base_model.
|
|
212
|
+
sync_instructions: A JSON file of SyncInstructions that states which
|
|
213
|
+
changes from the other model should be accepted or rejected
|
|
214
|
+
when building a new Model from the base model. The SyncInstructions
|
|
215
|
+
schema is essentially a variant of the ComparisonReport schema
|
|
216
|
+
that can be obtained by calling `honeybee compare models base_model_file
|
|
217
|
+
other_model_file --json`. The main difference is that the XXX_changed
|
|
218
|
+
properties should be replaced with update_XXX properties for
|
|
219
|
+
whether the change from the other_model should be accepted into
|
|
220
|
+
the new model or rejected from it.
|
|
221
|
+
"""
|
|
222
|
+
try:
|
|
223
|
+
new_model = Model.from_sync_files(
|
|
224
|
+
base_model_file, other_model_file, sync_instructions_file)
|
|
225
|
+
output_file.write(json.dumps(new_model.to_dict()))
|
|
226
|
+
except Exception as e:
|
|
227
|
+
_logger.exception('Model from sync failed.\n{}'.format(e))
|
|
228
|
+
sys.exit(1)
|
|
229
|
+
else:
|
|
230
|
+
sys.exit(0)
|
|
231
|
+
|
|
232
|
+
|
|
233
|
+
@create.command('merge-models')
|
|
234
|
+
@click.argument('base-model', type=click.Path(
|
|
235
|
+
exists=True, file_okay=True, dir_okay=False, resolve_path=True))
|
|
236
|
+
@click.option(
|
|
237
|
+
'--other-model', '-m', help='The other Model to be merged into the base model.',
|
|
238
|
+
type=click.File('w'), multiple=True)
|
|
239
|
+
@click.option(
|
|
240
|
+
'--output-file', '-f', help='Optional file to output the Model JSON string'
|
|
241
|
+
' with solved adjacency. By default it will be printed out to stdout',
|
|
242
|
+
type=click.File('w'), default='-')
|
|
243
|
+
def merge_models(base_model, other_model, output_file):
|
|
244
|
+
"""Create a Honeybee Model by merging multiple models together.
|
|
245
|
+
|
|
246
|
+
\b
|
|
247
|
+
Args:
|
|
248
|
+
base_model: Full path to a Honeybee Model JSON or Pkl file that serves
|
|
249
|
+
as the base into which the other model(s) will be merged. This model
|
|
250
|
+
determines the units and tolerance of the output model.
|
|
251
|
+
"""
|
|
252
|
+
try:
|
|
253
|
+
# serialize the Model and convert the units
|
|
254
|
+
parsed_model = Model.from_file(base_model)
|
|
255
|
+
other_models = [Model.from_file(m) for m in other_model]
|
|
256
|
+
for o_model in other_models:
|
|
257
|
+
parsed_model.add_model(o_model)
|
|
258
|
+
|
|
259
|
+
# write the new model out to the file or stdout
|
|
260
|
+
output_file.write(json.dumps(parsed_model.to_dict()))
|
|
261
|
+
except Exception as e:
|
|
262
|
+
_logger.exception('Model merging failed.\n{}'.format(e))
|
|
263
|
+
sys.exit(1)
|
|
264
|
+
else:
|
|
265
|
+
sys.exit(0)
|