honeybee-core 1.62.11__py3-none-any.whl → 1.63.0__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/aperture.py CHANGED
@@ -94,13 +94,6 @@ class Aperture(_BaseWithShade):
94
94
  assert data['type'] == 'Aperture', 'Expected Aperture dictionary. ' \
95
95
  'Got {}.'.format(data['type'])
96
96
 
97
- # remove any invalid holes from the geometry
98
- geo_dict = data['geometry']
99
- if 'holes' in geo_dict and geo_dict['holes'] is not None:
100
- for i, hole_list in enumerate(geo_dict['holes']):
101
- if len(hole_list) < 3:
102
- geo_dict['holes'].pop(i)
103
-
104
97
  # serialize the aperture
105
98
  is_operable = data['is_operable'] if 'is_operable' in data else False
106
99
  if data['boundary_condition']['type'] == 'Outdoors':
honeybee/door.py CHANGED
@@ -96,13 +96,6 @@ class Door(_BaseWithShade):
96
96
  assert data['type'] == 'Door', 'Expected Door dictionary. ' \
97
97
  'Got {}.'.format(data['type'])
98
98
 
99
- # remove any invalid holes from the geometry
100
- geo_dict = data['geometry']
101
- if 'holes' in geo_dict and geo_dict['holes'] is not None:
102
- for i, hole_list in enumerate(geo_dict['holes']):
103
- if len(hole_list) < 3:
104
- geo_dict['holes'].pop(i)
105
-
106
99
  # serialize the door
107
100
  is_glass = data['is_glass'] if 'is_glass' in data else False
108
101
  if data['boundary_condition']['type'] == 'Outdoors':
honeybee/face.py CHANGED
@@ -133,13 +133,6 @@ class Face(_BaseWithShade):
133
133
  assert data['type'] == 'Face', 'Expected Face dictionary. ' \
134
134
  'Got {}.'.format(data['type'])
135
135
 
136
- # remove any invalid holes from the geometry
137
- geo_dict = data['geometry']
138
- if 'holes' in geo_dict and geo_dict['holes'] is not None:
139
- for i, hole_list in enumerate(geo_dict['holes']):
140
- if len(hole_list) < 3:
141
- geo_dict['holes'].pop(i)
142
-
143
136
  # first serialize it with an outdoor boundary condition
144
137
  face_type = face_types.by_name(data['face_type'])
145
138
  face = cls(data['identifier'], Face3D.from_dict(data['geometry']),
honeybee/model.py CHANGED
@@ -161,11 +161,16 @@ class Model(_Base):
161
161
  self._properties = ModelProperties(self)
162
162
 
163
163
  @classmethod
164
- def from_dict(cls, data):
164
+ def from_dict(cls, data, cleanup_irrational=False):
165
165
  """Initialize a Model from a dictionary.
166
166
 
167
167
  Args:
168
168
  data: A dictionary representation of a Model object.
169
+ cleanup_irrational: Boolean to note whether common types of irrational
170
+ objects should be cleaned or removed from the dictionary before
171
+ serializing the model to Python. Typical cases that are removed
172
+ this way include Face3Ds with fewer than 3 vertices, Rooms that
173
+ have no Face geometry, etc. (Default: False).
169
174
  """
170
175
  # check the type of dictionary
171
176
  assert data['type'] == 'Model', 'Expected Model dictionary. ' \
@@ -179,6 +184,10 @@ class Model(_Base):
179
184
  angle_tol = 1.0 if 'angle_tolerance' not in data or \
180
185
  data['angle_tolerance'] is None else data['angle_tolerance']
181
186
 
187
+ # clean the irrational objects out if requested
188
+ if cleanup_irrational:
189
+ cls.clean_irrational_geometry(data)
190
+
182
191
  # import all of the geometry
183
192
  rooms = None # import rooms
184
193
  if 'rooms' in data and data['rooms'] is not None:
@@ -244,11 +253,16 @@ class Model(_Base):
244
253
  return model
245
254
 
246
255
  @classmethod
247
- def from_file(cls, hb_file):
256
+ def from_file(cls, hb_file, cleanup_irrational=False):
248
257
  """Initialize a Model from a HBJSON or HBpkl file, auto-sensing the type.
249
258
 
250
259
  Args:
251
260
  hb_file: Path to either a HBJSON or HBpkl file.
261
+ cleanup_irrational: Boolean to note whether common types of irrational
262
+ objects should be cleaned or removed from the dictionary before
263
+ serializing the model to Python. Typical cases that are removed
264
+ this way include Face3Ds with fewer than 3 vertices, Rooms that
265
+ have no Face geometry, etc. (Default: False).
252
266
  """
253
267
  # sense the file type from the first character to avoid maxing memory with JSON
254
268
  # this is needed since queenbee overwrites all file extensions
@@ -258,15 +272,20 @@ class Model(_Base):
258
272
  is_json = True if first_char == '{' or second_char == '{' else False
259
273
  # load the file using either HBJSON pathway or HBpkl
260
274
  if is_json:
261
- return cls.from_hbjson(hb_file)
262
- return cls.from_hbpkl(hb_file)
275
+ return cls.from_hbjson(hb_file, cleanup_irrational)
276
+ return cls.from_hbpkl(hb_file, cleanup_irrational)
263
277
 
264
278
  @classmethod
265
- def from_hbjson(cls, hbjson_file):
279
+ def from_hbjson(cls, hbjson_file, cleanup_irrational=False):
266
280
  """Initialize a Model from a HBJSON file.
267
281
 
268
282
  Args:
269
283
  hbjson_file: Path to HBJSON file.
284
+ cleanup_irrational: Boolean to note whether common types of irrational
285
+ objects should be cleaned or removed from the dictionary before
286
+ serializing the model to Python. Typical cases that are removed
287
+ this way include Face3Ds with fewer than 3 vertices, Rooms that
288
+ have no Face geometry, etc. (Default: False).
270
289
  """
271
290
  assert os.path.isfile(hbjson_file), 'Failed to find %s' % hbjson_file
272
291
  with io.open(hbjson_file, encoding='utf-8') as inf:
@@ -276,19 +295,24 @@ class Model(_Base):
276
295
  if second_char == '{':
277
296
  inf.read(1)
278
297
  data = json.load(inf)
279
- return cls.from_dict(data)
298
+ return cls.from_dict(data, cleanup_irrational)
280
299
 
281
300
  @classmethod
282
- def from_hbpkl(cls, hbpkl_file):
301
+ def from_hbpkl(cls, hbpkl_file, cleanup_irrational=False):
283
302
  """Initialize a Model from a HBpkl file.
284
303
 
285
304
  Args:
286
305
  hbpkl_file: Path to HBpkl file.
306
+ cleanup_irrational: Boolean to note whether common types of irrational
307
+ objects should be cleaned or removed from the dictionary before
308
+ serializing the model to Python. Typical cases that are removed
309
+ this way include Face3Ds with fewer than 3 vertices, Rooms that
310
+ have no Face geometry, etc. (Default: False).
287
311
  """
288
312
  assert os.path.isfile(hbpkl_file), 'Failed to find %s' % hbpkl_file
289
313
  with open(hbpkl_file, 'rb') as inf:
290
314
  data = pickle.load(inf)
291
- return cls.from_dict(data)
315
+ return cls.from_dict(data, cleanup_irrational)
292
316
 
293
317
  @classmethod
294
318
  def from_stl(cls, file_path, geometry_to_faces=False, units='Meters',
@@ -3685,6 +3709,97 @@ class Model(_Base):
3685
3709
  out_dict['valid'] = False
3686
3710
  return json.dumps(out_dict, indent=4)
3687
3711
 
3712
+ @staticmethod
3713
+ def clean_irrational_geometry(model_dict):
3714
+ """Remove irrational geometry objects from a honeybee Model dictionary.
3715
+
3716
+ This can be useful to run prior to serializing the Model object from a
3717
+ dictionary if it was produced from a source other than the Python
3718
+ core libraries, in which case the dictionary is necessarily rational
3719
+ and serializable. This is because not all honeybee-schema bindings
3720
+ enforce fundamental definitions of geometry types upon initialization
3721
+ of the geometry objects, leading to exceptions when an attempt is made
3722
+ to serialize them to Python. Furthermore, it is possible that the honeybee
3723
+ Model dictionary did not originate from any schema bindings at all, in
3724
+ which case it is highly recommended that this method be run.
3725
+
3726
+ Typical irrational geometry cases that are removed by this method include.
3727
+
3728
+ * Apertures/Doors with less than 3 vertices or holes less than 3 vertices.
3729
+ * Faces with less than 3 vertices or holes with less than 3 vertices.
3730
+ * Rooms that have no Face geometry.
3731
+ * Shade Face3Ds with less than 3 vertices or holes with less than 3 vertices.
3732
+ * ShadeMesh Mesh3Ds with no faces or faces with less than 3 vertices.
3733
+ """
3734
+ # clean all of the Room geometry in the model dictionary
3735
+ if 'rooms' in model_dict and model_dict['rooms'] is not None:
3736
+ for ri in range(len(model_dict['rooms']) - 1, -1, -1):
3737
+ r_dict = model_dict['rooms'][ri]
3738
+ # clean all of the Face geometry
3739
+ Model._clean_irrational_geo_with_shade(r_dict['faces'])
3740
+ for f_dict in r_dict['faces']:
3741
+ # clean all of the Aperture/Door geometry
3742
+ if 'apertures' in f_dict and f_dict['apertures'] is not None:
3743
+ Model._clean_irrational_geo_with_shade(f_dict['apertures'])
3744
+ if 'doors' in f_dict and f_dict['doors'] is not None:
3745
+ Model._clean_irrational_geo_with_shade(f_dict['doors'])
3746
+ # clean the assigned shade geometry
3747
+ if 'outdoor_shades' in r_dict and r_dict['outdoor_shades'] is not None:
3748
+ Model._clean_irrational_face3ds(r_dict['outdoor_shades'])
3749
+ if 'indoor_shades' in r_dict and r_dict['indoor_shades'] is not None:
3750
+ Model._clean_irrational_face3ds(r_dict['indoor_shades'])
3751
+ if len(r_dict['faces']) == 0: # the entire Room is irrational
3752
+ model_dict['rooms'].pop(ri)
3753
+ # clean all of the orphaned geometry in the model dictionary
3754
+ if 'orphaned_faces' in model_dict and model_dict['orphaned_faces'] is not None:
3755
+ Model._clean_irrational_geo_with_shade(model_dict['orphaned_faces'])
3756
+ if 'orphaned_apertures' in model_dict and \
3757
+ model_dict['orphaned_apertures'] is not None:
3758
+ Model._clean_irrational_geo_with_shade(model_dict['orphaned_apertures'])
3759
+ if 'orphaned_doors' in model_dict and model_dict['orphaned_doors'] is not None:
3760
+ Model._clean_irrational_geo_with_shade(model_dict['orphaned_doors'])
3761
+ if 'orphaned_shades' in model_dict and model_dict['orphaned_shades'] is not None:
3762
+ Model._clean_irrational_face3ds(model_dict['orphaned_shades'])
3763
+ # clean all of the shade mesh geometry in the model dictionary
3764
+ if 'shade_meshes' in model_dict and model_dict['shade_meshes'] is not None:
3765
+ for smi in range(len(model_dict['shade_meshes']) - 1, -1, -1):
3766
+ sm_dict = model_dict['shade_meshes'][smi]['geometry']
3767
+ for mfi in range(len(sm_dict['faces']) - 1, -1, -1):
3768
+ mf = sm_dict['faces'][mfi]
3769
+ if len(mf) not in (3, 4):
3770
+ sm_dict['faces'].pop(mfi)
3771
+ else:
3772
+ for ind in mf:
3773
+ try:
3774
+ sm_dict['vertices'][ind]
3775
+ except IndexError:
3776
+ sm_dict['faces'].pop(mfi)
3777
+ break
3778
+ if len(sm_dict['faces']) == 0:
3779
+ model_dict['shade_meshes'].pop(smi)
3780
+
3781
+ @staticmethod
3782
+ def _clean_irrational_geo_with_shade(geo_obj_dicts):
3783
+ """Clean irrational Face3Ds out of a list of honeybee geometry objects."""
3784
+ Model._clean_irrational_face3ds(geo_obj_dicts)
3785
+ for f_dict in geo_obj_dicts:
3786
+ if 'outdoor_shades' in f_dict and f_dict['outdoor_shades'] is not None:
3787
+ Model._clean_irrational_face3ds(f_dict['outdoor_shades'])
3788
+ if 'indoor_shades' in f_dict and f_dict['indoor_shades'] is not None:
3789
+ Model._clean_irrational_face3ds(f_dict['indoor_shades'])
3790
+
3791
+ @staticmethod
3792
+ def _clean_irrational_face3ds(geo_obj_dicts):
3793
+ """Clean irrational Face3Ds out of a list of honeybee geometry objects."""
3794
+ for fi in range(len(geo_obj_dicts) - 1, -1, -1):
3795
+ f_dict = geo_obj_dicts[fi]
3796
+ face_3d = f_dict['geometry']
3797
+ if len(face_3d['boundary']) < 3:
3798
+ geo_obj_dicts.pop(fi)
3799
+ continue
3800
+ elif 'holes' in face_3d:
3801
+ face_3d['holes'] = [h for h in face_3d['holes'] if len(h) >= 3]
3802
+
3688
3803
  @staticmethod
3689
3804
  def conversion_factor_to_meters(units):
3690
3805
  """Get the conversion factor to meters based on input units.
honeybee/room.py CHANGED
@@ -2059,7 +2059,7 @@ class Room(_BaseWithShade):
2059
2059
  for adj_group in adj_groups:
2060
2060
  # first check to see if the group has any adjacencies at all
2061
2061
  if len(adj_group) == 1:
2062
- joined_rooms.append(adj_group[0])
2062
+ joined_rooms.append(adj_group[0].duplicate())
2063
2063
  continue
2064
2064
  # determine the primary room that will set the properties of the new Room
2065
2065
  volumes = [r.volume for r in adj_group]
honeybee/shade.py CHANGED
@@ -86,16 +86,9 @@ class Shade(_Base):
86
86
  assert data['type'] == 'Shade', 'Expected Shade dictionary. ' \
87
87
  'Got {}.'.format(data['type'])
88
88
 
89
- # remove any invalid holes from the geometry
90
- geo_dict = data['geometry']
91
- if 'holes' in geo_dict and geo_dict['holes'] is not None:
92
- for i, hole_list in enumerate(geo_dict['holes']):
93
- if len(hole_list) < 3:
94
- geo_dict['holes'].pop(i)
95
-
96
89
  # serialize the dictionary to an object
97
- is_detached = data['is_detached'] if 'is_detached' in data else False
98
- shade = cls(data['identifier'], Face3D.from_dict(geo_dict), is_detached)
90
+ is_det = data['is_detached'] if 'is_detached' in data else False
91
+ shade = cls(data['identifier'], Face3D.from_dict(data['geometry']), is_det)
99
92
  if 'display_name' in data and data['display_name'] is not None:
100
93
  shade.display_name = data['display_name']
101
94
  if 'user_data' in data and data['user_data'] is not None:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: honeybee-core
3
- Version: 1.62.11
3
+ Version: 1.63.0
4
4
  Summary: A library to create 3D building geometry for various types of environmental simulation.
5
5
  Home-page: https://github.com/ladybug-tools/honeybee-core
6
6
  Author: Ladybug Tools
@@ -4,24 +4,24 @@ honeybee/_base.py,sha256=11TweR_0YJFv5ma1ttN2bs6FW_RlC3hNTjhhjvqXDB0,13181
4
4
  honeybee/_basewithshade.py,sha256=SXCWZ-4do6_Z-bZpVFidUINbylVyjwpkY2TIJE28Vuw,12943
5
5
  honeybee/_lockable.py,sha256=jmTVsXEWmYyEHpL3WsQZ_ItGOVVlJht1z3NFn07DIp0,3707
6
6
  honeybee/altnumber.py,sha256=nVXIvji9YDpy_I37oG41-rkPDx5QLplW9B2RYRCNeoA,1002
7
- honeybee/aperture.py,sha256=MheeMEaIDtilVu37sCeov-Rw5N_cnd2dvH-zAvxns0g,45838
7
+ honeybee/aperture.py,sha256=Kegu9rPZtRhezwsJwqVz74jxepoTmGnzzLJ3D1xUfmc,45512
8
8
  honeybee/boundarycondition.py,sha256=ys3kgMqgeJDBMLpXwSgwNBwvKMRz-LbCtLiL9tiOXS0,13777
9
9
  honeybee/checkdup.py,sha256=8q5p4tD3S4dl69jcURQhppeajJ_pYXRBlA48zTOoAGI,8221
10
10
  honeybee/colorobj.py,sha256=jhJmTBcLayFKSniW5ASo2-XMKFsW-RBUJilc9v-33ik,13349
11
11
  honeybee/config.json,sha256=GAFduJOXTlNcPM4M0fMkSXom5Cb7XZAMYQis2WsNaq0,167
12
12
  honeybee/config.py,sha256=auH_ooOyqMKAeBZxUc2NGUehUcYg8SHya98b5hfNlvM,13836
13
13
  honeybee/dictutil.py,sha256=cOqkhgJSQ3MviIkqOPv8MRb-N-BeDv07cVcNg7w7nLY,1992
14
- honeybee/door.py,sha256=XWMb9CL5aZoIisVyzE29uF8k4usfNvl0A0FzrKxFVmc,31732
14
+ honeybee/door.py,sha256=HiMBdT07_KjaGdasT2MvUdDl6XjOM7lhr-DxEy_0oZE,31406
15
15
  honeybee/extensionutil.py,sha256=DDQYhM7tFD3avRSCOjwTzLqX9ldUxl5GzY79V3_sATE,9644
16
- honeybee/face.py,sha256=j8ud3d-8Z8RvDomeiyYTqalu5czgiCMb08Xue_tFRGo,112287
16
+ honeybee/face.py,sha256=BOL2tlFLErxY27euixVWFzL1z-xtABtzvMmLIpzMqcE,111961
17
17
  honeybee/facetype.py,sha256=vCtWZKHp21RH-Yzs8zsHJHuFhJvczNh0yFl8wDe_RWY,4489
18
18
  honeybee/logutil.py,sha256=2gn-6RcWqFLvwdFzBHPqUwFqTj_R3iwHKALrl-2eL7M,2564
19
- honeybee/model.py,sha256=WzquyG8denJ5pX-ZiAyXKp1cRXFuIFih-fT7BqiRWL4,181757
19
+ honeybee/model.py,sha256=PJERVjX5IkHG9EcrUeBChv32hdH4h8rLAa1y47ACxDc,189014
20
20
  honeybee/orientation.py,sha256=GogGblASW9OU-fobfDaQ7w5yRbEAFdJJuHwg2fadhKI,5046
21
21
  honeybee/properties.py,sha256=ok976fbUdXzLDR2XiPNf8Q5ejfOM-PArqm5CVUbFiCc,34316
22
- honeybee/room.py,sha256=SoChV0-00oIM4RjKDPn8tTlCmtZPika0Tm2ue_lhzko,163183
22
+ honeybee/room.py,sha256=ec8DIebXfAc9A4f5oVcKCrF-ZR0loFvNgB1AZ7sXkw8,163195
23
23
  honeybee/search.py,sha256=KOIeQjYdj0yhRWPmF5kiFiH8Ei0WbzuiU-capnwYVeM,5060
24
- honeybee/shade.py,sha256=HwLkOqvPpluLMXzicWXmKl4fMVJHmkqqehntT69e00Q,21026
24
+ honeybee/shade.py,sha256=2zuW1lR5wfe-UyAosuCX-3tII6CKONq7wt8Ac_jXU6c,20698
25
25
  honeybee/shademesh.py,sha256=oldugnwhu-ibX9f0hfxpO-DvgM8U7S-dYwUjBSVzo4g,13273
26
26
  honeybee/typing.py,sha256=E8-HrCB9cSoqhFR6zcFXhrAlQRAFw_sxHGdobB8Lre8,20051
27
27
  honeybee/units.py,sha256=_qG_G5b9hdqjpyVOpGdIYCB6k8VKYjcxSJn1St-7Xjc,3204
@@ -40,9 +40,9 @@ honeybee/writer/model.py,sha256=N7F_jksf-5TrdVecuxTaFWxnPVFLmQs7k8g27TsdB7Q,177
40
40
  honeybee/writer/room.py,sha256=kFghgStTU1SEJSLigXB0VjOWhZtgs4uXuAqdwd4yRQo,174
41
41
  honeybee/writer/shade.py,sha256=EpgX-vMc-s21TnMvNWvWTKyT8iAnxu1nFVXzjY1oyF8,177
42
42
  honeybee/writer/shademesh.py,sha256=Y41bLogJ7dwpvMe5cAWVRDRVqJEwo9e5hFJQjlt6UX8,189
43
- honeybee_core-1.62.11.dist-info/licenses/LICENSE,sha256=hIahDEOTzuHCU5J2nd07LWwkLW7Hko4UFO__ffsvB-8,34523
44
- honeybee_core-1.62.11.dist-info/METADATA,sha256=Ku3shh4F9BKpZRJVv7FkSN3Xensrm6_UBf_p9hqV_80,3730
45
- honeybee_core-1.62.11.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
46
- honeybee_core-1.62.11.dist-info/entry_points.txt,sha256=r3YqOm40goBroH3ccUhpwQjvTwu10JWLd0HIRHI1J8E,47
47
- honeybee_core-1.62.11.dist-info/top_level.txt,sha256=8ve7puCRLUA9XDEGc1Mcs-UX9sFjpPV8MeTaIMwQ_Tg,9
48
- honeybee_core-1.62.11.dist-info/RECORD,,
43
+ honeybee_core-1.63.0.dist-info/licenses/LICENSE,sha256=hIahDEOTzuHCU5J2nd07LWwkLW7Hko4UFO__ffsvB-8,34523
44
+ honeybee_core-1.63.0.dist-info/METADATA,sha256=maYFxvWP4Wx2M0zSCz7v4Hvh6y53-wzPITrwMxLTPXc,3729
45
+ honeybee_core-1.63.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
46
+ honeybee_core-1.63.0.dist-info/entry_points.txt,sha256=r3YqOm40goBroH3ccUhpwQjvTwu10JWLd0HIRHI1J8E,47
47
+ honeybee_core-1.63.0.dist-info/top_level.txt,sha256=8ve7puCRLUA9XDEGc1Mcs-UX9sFjpPV8MeTaIMwQ_Tg,9
48
+ honeybee_core-1.63.0.dist-info/RECORD,,