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.
Files changed (48) hide show
  1. honeybee/__init__.py +23 -0
  2. honeybee/__main__.py +4 -0
  3. honeybee/_base.py +331 -0
  4. honeybee/_basewithshade.py +310 -0
  5. honeybee/_lockable.py +99 -0
  6. honeybee/altnumber.py +47 -0
  7. honeybee/aperture.py +997 -0
  8. honeybee/boundarycondition.py +358 -0
  9. honeybee/checkdup.py +173 -0
  10. honeybee/cli/__init__.py +118 -0
  11. honeybee/cli/compare.py +132 -0
  12. honeybee/cli/create.py +265 -0
  13. honeybee/cli/edit.py +559 -0
  14. honeybee/cli/lib.py +103 -0
  15. honeybee/cli/setconfig.py +43 -0
  16. honeybee/cli/validate.py +224 -0
  17. honeybee/colorobj.py +363 -0
  18. honeybee/config.json +5 -0
  19. honeybee/config.py +347 -0
  20. honeybee/dictutil.py +54 -0
  21. honeybee/door.py +746 -0
  22. honeybee/extensionutil.py +208 -0
  23. honeybee/face.py +2360 -0
  24. honeybee/facetype.py +153 -0
  25. honeybee/logutil.py +79 -0
  26. honeybee/model.py +4272 -0
  27. honeybee/orientation.py +132 -0
  28. honeybee/properties.py +845 -0
  29. honeybee/room.py +3485 -0
  30. honeybee/search.py +107 -0
  31. honeybee/shade.py +514 -0
  32. honeybee/shademesh.py +362 -0
  33. honeybee/typing.py +498 -0
  34. honeybee/units.py +88 -0
  35. honeybee/writer/__init__.py +7 -0
  36. honeybee/writer/aperture.py +6 -0
  37. honeybee/writer/door.py +6 -0
  38. honeybee/writer/face.py +6 -0
  39. honeybee/writer/model.py +6 -0
  40. honeybee/writer/room.py +6 -0
  41. honeybee/writer/shade.py +6 -0
  42. honeybee/writer/shademesh.py +6 -0
  43. honeybee_core-1.64.12.dist-info/METADATA +94 -0
  44. honeybee_core-1.64.12.dist-info/RECORD +48 -0
  45. honeybee_core-1.64.12.dist-info/WHEEL +5 -0
  46. honeybee_core-1.64.12.dist-info/entry_points.txt +2 -0
  47. honeybee_core-1.64.12.dist-info/licenses/LICENSE +661 -0
  48. honeybee_core-1.64.12.dist-info/top_level.txt +1 -0
@@ -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)