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,358 @@
1
+ """Boundary Condition for Face, Aperture, Door."""
2
+ import re
3
+
4
+ from .typing import float_in_range, tuple_with_length
5
+ from .altnumber import autocalculate
6
+
7
+
8
+ class _BoundaryCondition(object):
9
+ """Base boundary condition class."""
10
+
11
+ __slots__ = ()
12
+
13
+ def __init__(self):
14
+ """Initialize Boundary condition."""
15
+
16
+ @property
17
+ def name(self):
18
+ """Get the name of the boundary condition (ie. 'Outdoors', 'Ground')."""
19
+ return self.__class__.__name__
20
+
21
+ @property
22
+ def view_factor(self):
23
+ """Get the view factor to the ground."""
24
+ return 'autocalculate'
25
+
26
+ @property
27
+ def sun_exposure_idf(self):
28
+ """Get a text string for sun exposure, which is write-able into an IDF."""
29
+ return 'NoSun'
30
+
31
+ @property
32
+ def wind_exposure_idf(self):
33
+ """ Get a text string for wind exposure, which is write-able into an IDF."""
34
+ return 'NoWind'
35
+
36
+ def to_dict(self):
37
+ """Get the boundary condition as a dictionary."""
38
+ return {'type': self.name}
39
+
40
+ def ToString(self):
41
+ """Overwrite .NET ToString."""
42
+ return self.__repr__()
43
+
44
+ def __repr__(self):
45
+ return self.name
46
+
47
+
48
+ class Outdoors(_BoundaryCondition):
49
+ """Outdoor boundary condition.
50
+
51
+ Args:
52
+ sun_exposure: A boolean noting whether the boundary is exposed to sun.
53
+ Default: True.
54
+ wind_exposure: A boolean noting whether the boundary is exposed to wind.
55
+ Default: True.
56
+ view_factor: A number between 0 and 1 for the view factor to the ground.
57
+ This input can also be an Autocalculate object to signify that the view
58
+ factor automatically calculated. Default: autocalculate.
59
+ """
60
+
61
+ __slots__ = ('_sun_exposure', '_wind_exposure', '_view_factor')
62
+
63
+ def __init__(self, sun_exposure=True, wind_exposure=True,
64
+ view_factor=autocalculate):
65
+ """Initialize Outdoors boundary condition."""
66
+ assert isinstance(sun_exposure, bool), \
67
+ 'Input sun_exposure must be a Boolean. Got {}.'.format(type(sun_exposure))
68
+ self._sun_exposure = sun_exposure
69
+ assert isinstance(wind_exposure, bool), \
70
+ 'Input wind_exposure must be a Boolean. Got {}.'.format(type(wind_exposure))
71
+ self._wind_exposure = wind_exposure
72
+ if view_factor == autocalculate:
73
+ self._view_factor = autocalculate
74
+ else:
75
+ self._view_factor = float_in_range(
76
+ view_factor, 0.0, 1.0, 'view factor to ground')
77
+
78
+ @classmethod
79
+ def from_dict(cls, data):
80
+ """Initialize Outdoors BoundaryCondition from a dictionary.
81
+
82
+ Args:
83
+ data: A dictionary representation of the boundary condition.
84
+ """
85
+ assert data['type'] == 'Outdoors', 'Expected dictionary for Outdoors boundary ' \
86
+ 'condition. Got {}.'.format(data['type'])
87
+ sun_exposure = True if 'sun_exposure' not in data else data['sun_exposure']
88
+ wind_exposure = True if 'wind_exposure' not in data else data['wind_exposure']
89
+ view_factor = autocalculate if 'view_factor' not in data or \
90
+ data['view_factor'] == autocalculate.to_dict() else data['view_factor']
91
+ return cls(sun_exposure, wind_exposure, view_factor)
92
+
93
+ @property
94
+ def sun_exposure(self):
95
+ """Get a boolean noting whether the boundary is exposed to sun."""
96
+ return self._sun_exposure
97
+
98
+ @property
99
+ def wind_exposure(self):
100
+ """Get a boolean noting whether the boundary is exposed to wind."""
101
+ return self._wind_exposure
102
+
103
+ @property
104
+ def view_factor(self):
105
+ """Get the view factor to the ground as a number or 'autocalculate'."""
106
+ return self._view_factor
107
+
108
+ @property
109
+ def sun_exposure_idf(self):
110
+ """Get a text string for sun exposure, which is write-able into an IDF."""
111
+ return 'NoSun' if not self.sun_exposure else 'SunExposed'
112
+
113
+ @property
114
+ def wind_exposure_idf(self):
115
+ """Get a text string for wind exposure, which is write-able into an IDF."""
116
+ return 'NoWind' if not self.wind_exposure else 'WindExposed'
117
+
118
+ def to_dict(self, full=False):
119
+ """Get the boundary condition as a dictionary.
120
+
121
+ Args:
122
+ full: Set to True to get the full dictionary which includes energy
123
+ simulation specific keys such as sun_exposure, wind_exposure and
124
+ view_factor. (Default: False).
125
+ """
126
+ bc_dict = {'type': self.name}
127
+ if full:
128
+ bc_dict['sun_exposure'] = self.sun_exposure
129
+ bc_dict['wind_exposure'] = self.wind_exposure
130
+ bc_dict['view_factor'] = autocalculate.to_dict() if \
131
+ self.view_factor == autocalculate else self.view_factor
132
+ return bc_dict
133
+
134
+ def __key(self):
135
+ """A tuple based on the object properties, useful for hashing."""
136
+ return (self.sun_exposure, self.wind_exposure, self.view_factor)
137
+
138
+ def __hash__(self):
139
+ return hash(self.__key())
140
+
141
+ def __eq__(self, other):
142
+ return isinstance(other, Outdoors) and self.__key() == other.__key()
143
+
144
+
145
+ class Surface(_BoundaryCondition):
146
+ """Boundary condition when an object is adjacent to another object."""
147
+
148
+ __slots__ = ('_boundary_condition_objects',)
149
+
150
+ def __init__(self, boundary_condition_objects, sub_face=False):
151
+ """Initialize Surface boundary condition.
152
+
153
+ Args:
154
+ boundary_condition_objects: A list of up to 3 object identifiers that are
155
+ adjacent to this one. The first object is always immediately
156
+ adjacent and is of the same object type (Face, Aperture, Door). When
157
+ this boundary condition is applied to a Face, the second object in the
158
+ tuple will be the parent Room of the adjacent object. When the boundary
159
+ condition is applied to a sub-face (Door or Aperture), the second object
160
+ will be the parent Face of the adjacent sub-face and the third object
161
+ will be the parent Room of the adjacent sub-face.
162
+ sub_face: Boolean to note whether this boundary condition is applied to a
163
+ sub-face (an Aperture or a Door) instead of a Face. (Default: False).
164
+ """
165
+ if sub_face:
166
+ self._boundary_condition_objects = tuple_with_length(
167
+ boundary_condition_objects, 3, str,
168
+ 'boundary_condition_objects for Apertures or Doors')
169
+ else:
170
+ self._boundary_condition_objects = tuple_with_length(
171
+ boundary_condition_objects, 2, str,
172
+ 'boundary_condition_objects for Faces')
173
+
174
+ @classmethod
175
+ def from_dict(cls, data, sub_face=False):
176
+ """Initialize Surface BoundaryCondition from a dictionary.
177
+
178
+ Args:
179
+ data: A dictionary representation of the boundary condition.
180
+ sub_face: Boolean to note whether this boundary condition is applied to a
181
+ sub-face (an Aperture or a Door) instead of a Face. Default: False.
182
+ """
183
+ assert data['type'] == 'Surface', 'Expected dictionary for Surface boundary ' \
184
+ 'condition. Got {}.'.format(data['type'])
185
+ return cls(data['boundary_condition_objects'], sub_face)
186
+
187
+ @classmethod
188
+ def from_other_object(cls, other_object, sub_face=False):
189
+ """Initialize Surface boundary condition from an adjacent other object.
190
+
191
+ Args:
192
+ other_object: Another object (Face, Aperture, Door) of the same type
193
+ that this boundary condition is assigned. This other_object will be
194
+ set as the adjacent object in this boundary condition.
195
+ sub_face: Boolean to note whether this boundary condition is applied to a
196
+ sub-face (an Aperture or a Door) instead of a Face. Default: False.
197
+ """
198
+ error_msg = 'Surface boundary conditions can only be assigned to objects' \
199
+ ' with parent Rooms.'
200
+ bc_objects = [other_object.identifier]
201
+ if other_object.has_parent:
202
+ bc_objects.append(other_object.parent.identifier)
203
+ if sub_face:
204
+ if other_object.parent.has_parent:
205
+ bc_objects.append(other_object.parent.parent.identifier)
206
+ else:
207
+ raise AttributeError(error_msg)
208
+ else:
209
+ raise AttributeError(error_msg)
210
+ return cls(bc_objects, sub_face)
211
+
212
+ @property
213
+ def boundary_condition_objects(self):
214
+ """Get a tuple of up to 3 object identifiers that are adjacent to this one.
215
+
216
+ The first object is always the one that is immediately adjacent and is of
217
+ the same object type (Face, Aperture, Door).
218
+ When this boundary condition is applied to a Face, the second object in the
219
+ tuple will be the parent Room of the adjacent object.
220
+ When the boundary condition is applied to a sub-face (Door or Aperture),
221
+ the second object will be the parent Face of the sub-face and the third
222
+ object will be the parent Room of the adjacent sub-face.
223
+ """
224
+ return self._boundary_condition_objects
225
+
226
+ @property
227
+ def boundary_condition_object(self):
228
+ """Get the identifier of the object adjacent to this one."""
229
+ return self._boundary_condition_objects[0]
230
+
231
+ def to_dict(self):
232
+ """Get the boundary condition as a dictionary.
233
+
234
+ Args:
235
+ full: Set to True to get the full dictionary which includes energy
236
+ simulation specific keys such as sun_exposure, wind_exposure and
237
+ view_factor. Default: False.
238
+ """
239
+ return {'type': self.name,
240
+ 'boundary_condition_objects': self.boundary_condition_objects}
241
+
242
+ def __key(self):
243
+ """A tuple based on the object properties, useful for hashing."""
244
+ return self.boundary_condition_objects
245
+
246
+ def __hash__(self):
247
+ return hash(self.__key())
248
+
249
+ def __eq__(self, other):
250
+ return isinstance(other, Surface) and self.__key() == other.__key()
251
+
252
+
253
+ class Ground(_BoundaryCondition):
254
+ """Ground boundary condition.
255
+
256
+ Args:
257
+ data: A dictionary representation of the boundary condition.
258
+ """
259
+ __slots__ = ()
260
+
261
+ @classmethod
262
+ def from_dict(cls, data):
263
+ """Initialize Ground BoundaryCondition from a dictionary."""
264
+ assert data['type'] == 'Ground', 'Expected dictionary for Ground boundary ' \
265
+ 'condition. Got {}.'.format(data['type'])
266
+ return cls()
267
+
268
+ def __eq__(self, other):
269
+ return isinstance(other, Ground)
270
+
271
+
272
+ class _BoundaryConditions(object):
273
+ """Boundary conditions."""
274
+
275
+ def __init__(self):
276
+ self._outdoors = Outdoors()
277
+ self._ground = Ground()
278
+ self._bc_name_dict = None
279
+
280
+ @property
281
+ def outdoors(self):
282
+ """Default outdoor boundary condition."""
283
+ return self._outdoors
284
+
285
+ @property
286
+ def ground(self):
287
+ """Default ground boundary condition."""
288
+ return self._ground
289
+
290
+ def surface(self, other_object, sub_face=False):
291
+ """Get a Surface boundary condition.
292
+
293
+ Args:
294
+ other_object: The other object that is adjacent to the one that will
295
+ bear this Surface boundary condition.
296
+ sub_face: Boolean to note whether the boundary condition is for a
297
+ sub-face (Aperture or Door) instead of a Face. (Default: False).
298
+ """
299
+ return Surface.from_other_object(other_object, sub_face)
300
+
301
+ def by_name(self, bc_name):
302
+ """Get a boundary condition object instance by its name.
303
+
304
+ This method will correct for capitalization as well as the presence of
305
+ spaces and underscores. Note that this method only works for boundary
306
+ conditions with all of their inputs defaulted.
307
+
308
+ Args:
309
+ bc_name: A boundary condition name.
310
+ """
311
+ if self._bc_name_dict is None:
312
+ self._build_bc_name_dict()
313
+ try:
314
+ return self._bc_name_dict[re.sub(r'[\s_]', '', bc_name.lower())]
315
+ except KeyError:
316
+ raise ValueError(
317
+ '"{}" is not a valid boundary condition name.\nChoose from the '
318
+ 'following: {}'.format(bc_name, list(self._bc_name_dict.keys())))
319
+
320
+ def _build_bc_name_dict(self):
321
+ """Build a dictionary that can be used to lookup boundary conditions by name."""
322
+ attr = [atr for atr in dir(self) if not atr.startswith('_')]
323
+ clean_attr = [re.sub(r'[\s_]', '', atr.lower()) for atr in attr]
324
+ self._bc_name_dict = {}
325
+ for atr_name, atr in zip(clean_attr, attr):
326
+ try:
327
+ full_attr = getattr(self, '_' + atr)
328
+ self._bc_name_dict[atr_name] = full_attr
329
+ except AttributeError:
330
+ pass # callable method that has no static default object
331
+
332
+ def __contains__(self, value):
333
+ return isinstance(value, _BoundaryCondition)
334
+
335
+
336
+ boundary_conditions = _BoundaryConditions()
337
+
338
+
339
+ def get_bc_from_position(positions, ground_depth=0):
340
+ """Return a boundary condition based on the relationship to a ground plane.
341
+
342
+ Positions that are entirely at or below the ground_depth will get a Ground
343
+ boundary condition. If there are any positions above the ground_depth, an
344
+ Outdoors boundary condition will be returned.
345
+
346
+ args:
347
+ positions: A list of ladybug_geometry Point3D objects representing the
348
+ vertices of an object.
349
+ ground_depth: The Z value above which positions are considered Outdoors
350
+ instead of Ground.
351
+
352
+ Returns:
353
+ Face type instance.
354
+ """
355
+ for position in positions:
356
+ if position.z > ground_depth:
357
+ return boundary_conditions.outdoors
358
+ return boundary_conditions.ground
honeybee/checkdup.py ADDED
@@ -0,0 +1,173 @@
1
+ # coding=utf-8
2
+ """Utilities to check whether there are any duplicate values in a list of ids."""
3
+
4
+ import collections
5
+
6
+
7
+ def check_duplicate_identifiers(
8
+ objects_to_check, raise_exception=True, obj_name='', detailed=False,
9
+ code='000000', extension='Core', error_type='Duplicate Object Identifier'):
10
+ """Check whether there are duplicated identifiers across a list of objects.
11
+
12
+ Args:
13
+ objects_to_check: A list of honeybee objects across which duplicate
14
+ identifiers will be checked.
15
+ raise_exception: Boolean to note whether an exception should be raised if
16
+ duplicated identifiers are found. (Default: True).
17
+ obj_name: An optional name for the object to be included in the error
18
+ message. Fro example, 'Room', 'Face', 'Aperture'.
19
+ detailed: Boolean for whether the returned object is a detailed list of
20
+ dicts with error info or a string with a message. (Default: False).
21
+ code: Text for the error code. (Default: 0000).
22
+ extension: Text for the name of the Honeybee extension for which duplicate
23
+ identifiers are being evaluated. (Default: Core).
24
+ error_type: Text for the type of error. This should be directly linked
25
+ to the error code and should simply be a human-readable version of
26
+ the error code. (Default: Unknown Error).
27
+
28
+ Returns:
29
+ A message string indicating the duplicated identifiers (if detailed is False)
30
+ or a list of dictionaries with information about the duplicated identifiers
31
+ (if detailed is True). This string (or list) will be empty if no duplicates
32
+ were found.
33
+ """
34
+ detailed = False if raise_exception else detailed
35
+ obj_id_iter = (obj.identifier for obj in objects_to_check)
36
+ dup = [t for t, c in collections.Counter(obj_id_iter).items() if c > 1]
37
+ if len(dup) != 0:
38
+ if detailed:
39
+ # find the object display names
40
+ dis_names = []
41
+ for obj_id in dup:
42
+ dis_name = None
43
+ for obj in objects_to_check:
44
+ if obj.identifier == obj_id:
45
+ dis_name = obj.display_name
46
+ dis_names.append(dis_name)
47
+ err_list = []
48
+ for dup_id, dis_name in zip(dup, dis_names):
49
+ msg = 'There is a duplicated {} identifier: {}'.format(obj_name, dup_id)
50
+ dup_dict = {
51
+ 'type': 'ValidationError',
52
+ 'code': code,
53
+ 'error_type': error_type,
54
+ 'extension_type': extension,
55
+ 'element_type': obj_name,
56
+ 'element_id': [dup_id],
57
+ 'message': msg
58
+ }
59
+ if dis_name is not None:
60
+ dup_dict['element_name'] = [dis_name]
61
+ err_list.append(dup_dict)
62
+ return err_list
63
+ msg = 'The following duplicated {} identifiers were found:\n{}'.format(
64
+ obj_name, '\n'.join(dup))
65
+ if raise_exception:
66
+ raise ValueError(msg)
67
+ return msg
68
+ return [] if detailed else ''
69
+
70
+
71
+ def check_duplicate_identifiers_parent(
72
+ objects_to_check, raise_exception=True, obj_name='', detailed=False,
73
+ code='000000', extension='Core', error_type='Duplicate Object Identifier'):
74
+ """Check whether there are duplicated identifiers across a list of objects.
75
+
76
+ The error message will include the identifiers of top-level parents in order
77
+ to make it easier to find the duplicated objects in the model.
78
+
79
+ Args:
80
+ objects_to_check: A list of honeybee objects across which duplicate
81
+ identifiers will be checked. These objects must have the ability to
82
+ have parents for this method to run correctly.
83
+ raise_exception: Boolean to note whether an exception should be raised if
84
+ duplicated identifiers are found. (Default: True).
85
+ obj_name: An optional name for the object to be included in the error
86
+ message. For example, 'Room', 'Face', 'Aperture'.
87
+ detailed: Boolean for whether the returned object is a detailed list of
88
+ dicts with error info or a string with a message. (Default: False).
89
+ code: Text for the error code. (Default: 0000).
90
+ extension: Text for the name of the Honeybee extension for which duplicate
91
+ identifiers are being evaluated. (Default: Core).
92
+ error_type: Text for the type of error. This should be directly linked
93
+ to the error code and should simply be a human-readable version of
94
+ the error code. (Default: Unknown Error).
95
+
96
+ Returns:
97
+ A message string indicating the duplicated identifiers (if detailed is False)
98
+ or a list of dictionaries with information about the duplicated identifiers
99
+ (if detailed is True). This string (or list) will be empty if no duplicates
100
+ were found.
101
+ """
102
+ detailed = False if raise_exception else detailed
103
+ obj_id_iter = (obj.identifier for obj in objects_to_check)
104
+ dup = [t for t, c in collections.Counter(obj_id_iter).items() if c > 1]
105
+ if len(dup) != 0:
106
+ # find the relevant top-level parents
107
+ top_par, dis_names = [], []
108
+ for obj_id in dup:
109
+ rel_parents, dis_name = [], None
110
+ for obj in objects_to_check:
111
+ if obj.identifier == obj_id:
112
+ dis_name = obj.display_name
113
+ if obj.has_parent:
114
+ try:
115
+ par_obj = obj.top_level_parent
116
+ except AttributeError:
117
+ par_obj = obj.parent
118
+ rel_parents.append(par_obj)
119
+ top_par.append(rel_parents)
120
+ dis_names.append(dis_name)
121
+ # if a detailed dictionary is requested, then create it
122
+ if detailed:
123
+ err_list = []
124
+ for dup_id, dis_name, rel_par in zip(dup, dis_names, top_par):
125
+ dup_dict = {
126
+ 'type': 'ValidationError',
127
+ 'code': code,
128
+ 'error_type': error_type,
129
+ 'extension_type': extension,
130
+ 'element_type': obj_name,
131
+ 'element_id': [dup_id]
132
+ }
133
+ if dis_name is not None:
134
+ dup_dict['element_name'] = [dis_name]
135
+ msg = 'There is a duplicated {} identifier: {}'.format(obj_name, dup_id)
136
+ if len(rel_par) != 0:
137
+ dup_dict['top_parents'] = []
138
+ msg += '\n Relevant Top-Level Parents:\n'
139
+ for par_o in rel_par:
140
+ par_dict = {
141
+ 'parent_type': par_o.__class__.__name__,
142
+ 'id': par_o.identifier,
143
+ 'name': par_o.display_name
144
+ }
145
+ dup_dict['top_parents'].append(par_dict)
146
+ msg += ' {} "{}"\n'.format(
147
+ par_o.__class__.__name__, par_o.full_id)
148
+ dup_dict['message'] = msg
149
+ err_list.append(dup_dict)
150
+ return err_list
151
+ # if just an error message is requested, then build it from the information
152
+ msg = 'The following duplicated {} identifiers were found:\n'.format(obj_name)
153
+ for obj_id, rel_par in zip(dup, top_par):
154
+ obj_msg = obj_id + '\n'
155
+ if len(rel_par) != 0:
156
+ obj_msg += ' Relevant Top-Level Parents:\n'
157
+ for par_o in rel_par:
158
+ obj_msg += ' {} "{}"\n'.format(
159
+ par_o.__class__.__name__, par_o.full_id)
160
+ msg += obj_msg
161
+ msg = msg.strip()
162
+ if raise_exception:
163
+ raise ValueError(msg)
164
+ return msg
165
+ return [] if detailed else ''
166
+
167
+
168
+ def is_equivalent(object_1, object_2):
169
+ """Check if two objects are equal with an initial check for the same instance.
170
+ """
171
+ if object_1 is object_2: # first see if they're the same instance
172
+ return True
173
+ return object_1 == object_2 # two objects that should have == operators
@@ -0,0 +1,118 @@
1
+ """
2
+ Command Line Interface (CLI) entry point for honeybee and honeybee extensions.
3
+
4
+ Use this file only to add command related to honeybee-core. For adding extra commands
5
+ from each extention see below.
6
+
7
+ Note:
8
+
9
+ Do not import this module in your code directly unless you are extending the command
10
+ line interface. For running the commands execute them from the command line or as a
11
+ subprocess (e.g. ``subprocess.call(['honeybee', 'viz'])``)
12
+
13
+ Honeybee is using click (https://click.palletsprojects.com/en/7.x/) for creating the CLI.
14
+ You can extend the command line interface from inside each extention by following these
15
+ steps:
16
+
17
+ 1. Create a ``cli.py`` file in your extension.
18
+ 2. Import the ``main`` function from this ``honeybee.cli``.
19
+ 3. Add your commands and command groups to main using add_command method.
20
+ 4. Add ``import [your-extention].cli`` to ``__init__.py`` file to the commands are added
21
+ to the cli when the module is loaded.
22
+
23
+ The good practice is to group all your extention commands in a command group named after
24
+ the extension. This will make the commands organized under extension namespace. For
25
+ instance commands for `honeybee-radiance` will be called like
26
+ ``honeybee radiance [radiance-command]``.
27
+
28
+
29
+ .. code-block:: python
30
+
31
+ import click
32
+ from honeybee.cli import main
33
+
34
+ @click.group()
35
+ def radiance():
36
+ pass
37
+
38
+ # add commands to radiance group
39
+ @radiance.command('daylight-factor')
40
+ # ...
41
+ def daylight_factor():
42
+ pass
43
+
44
+
45
+ # finally add the newly created commands to honeybee cli
46
+ main.add_command(radiance)
47
+
48
+ # do not forget to import this module in __init__.py otherwise it will not be added
49
+ # to honeybee commands.
50
+
51
+ Note:
52
+
53
+ For extension with several commands you can use a folder structure instead
54
+ of a single file. Refer to ``honeybee-radiance`` for an example.
55
+
56
+ """
57
+ import click
58
+ import sys
59
+ import logging
60
+ import json
61
+
62
+ from ..config import folders
63
+ from honeybee.cli.setconfig import set_config
64
+ from honeybee.cli.validate import validate
65
+ from honeybee.cli.compare import compare
66
+ from honeybee.cli.create import create
67
+ from honeybee.cli.edit import edit
68
+ from honeybee.cli.lib import lib
69
+
70
+ _logger = logging.getLogger(__name__)
71
+
72
+
73
+ @click.group()
74
+ @click.version_option()
75
+ def main():
76
+ pass
77
+
78
+
79
+ @main.command('config')
80
+ @click.option('--output-file', help='Optional file to output the JSON string of '
81
+ 'the config object. By default, it will be printed out to stdout',
82
+ type=click.File('w'), default='-', show_default=True)
83
+ def config(output_file):
84
+ """Get a JSON object with all configuration information"""
85
+ try:
86
+ config_dict = {
87
+ 'default_simulation_folder': folders.default_simulation_folder,
88
+ 'honeybee_schema_version': folders.honeybee_schema_version_str,
89
+ 'python_package_path': folders.python_package_path,
90
+ 'python_scripts_path': folders.python_scripts_path,
91
+ 'python_exe_path': folders.python_exe_path,
92
+ 'python_version': folders.python_version_str,
93
+ 'default_standards_folder': folders.default_standards_folder
94
+ }
95
+ output_file.write(json.dumps(config_dict, indent=4))
96
+ except Exception as e:
97
+ _logger.exception('Failed to retrieve configurations.\n{}'.format(e))
98
+ sys.exit(1)
99
+ else:
100
+ sys.exit(0)
101
+
102
+
103
+ @main.command('viz')
104
+ def viz():
105
+ """Check if honeybee is flying!"""
106
+ click.echo('viiiiiiiiiiiiizzzzzzzzz!')
107
+
108
+
109
+ main.add_command(set_config, name='set-config')
110
+ main.add_command(validate)
111
+ main.add_command(compare)
112
+ main.add_command(create)
113
+ main.add_command(edit)
114
+ main.add_command(lib)
115
+
116
+
117
+ if __name__ == "__main__":
118
+ main()