ifctrano 0.7.0__py3-none-any.whl → 0.9.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.
ifctrano/building.py CHANGED
@@ -1,12 +1,14 @@
1
1
  import logging
2
2
  import re
3
3
  from pathlib import Path
4
- from typing import List, Tuple, Any, Optional, Set
4
+ from typing import List, Tuple, Any, Optional, Set, Dict
5
5
 
6
6
  import ifcopenshell
7
+ import yaml
7
8
  from ifcopenshell import file, entity_instance
8
9
  from pydantic import validate_call, Field, model_validator, field_validator
9
10
  from trano.elements import InternalElement # type: ignore
11
+ from trano.elements.envelope import SpaceTilt # type: ignore
10
12
  from trano.elements.library.library import Library # type: ignore
11
13
  from trano.elements.types import Tilt # type: ignore
12
14
  from trano.topology import Network # type: ignore
@@ -23,7 +25,11 @@ from ifctrano.space_boundary import (
23
25
  initialize_tree,
24
26
  Space,
25
27
  )
26
- from ifctrano.construction import Constructions, default_construction
28
+ from ifctrano.construction import (
29
+ Constructions,
30
+ default_construction,
31
+ default_internal_construction,
32
+ )
27
33
 
28
34
  logger = logging.getLogger(__name__)
29
35
 
@@ -215,6 +221,83 @@ class Building(BaseShow):
215
221
  def get_adjacency(self) -> InternalElements:
216
222
  return get_internal_elements(self.space_boundaries)
217
223
 
224
+ @validate_call
225
+ def to_config(
226
+ self,
227
+ north_axis: Optional[Vector] = None,
228
+ ) -> Dict[str, Any]:
229
+ north_axis = north_axis or Vector(x=0, y=1, z=0)
230
+ spaces = [
231
+ space_boundary.to_config(
232
+ self.internal_elements.internal_element_ids(),
233
+ north_axis,
234
+ self.constructions,
235
+ )
236
+ for space_boundary in self.space_boundaries
237
+ ]
238
+ spaces = [sp for sp in spaces if sp is not None]
239
+ internal_walls = []
240
+
241
+ for internal_element in self.internal_elements.elements:
242
+ space_1 = internal_element.spaces[0]
243
+ space_2 = internal_element.spaces[1]
244
+ if any(
245
+ name not in [sp["id"] for sp in spaces] # type: ignore
246
+ for name in [
247
+ space_1.space_unique_name(),
248
+ space_2.space_unique_name(),
249
+ ]
250
+ ):
251
+ continue
252
+
253
+ construction = self.constructions.get_construction(
254
+ internal_element.element, default_internal_construction
255
+ )
256
+ if internal_element.element.is_a() in ["IfcSlab"]:
257
+ space_1_tilt = (
258
+ Tilt.floor.value
259
+ if space_1.bounding_box.centroid.z > space_2.bounding_box.centroid.z
260
+ else Tilt.ceiling.value
261
+ )
262
+ space_2_tilt = (
263
+ Tilt.floor.value
264
+ if space_2.bounding_box.centroid.z > space_1.bounding_box.centroid.z
265
+ else Tilt.ceiling.value
266
+ )
267
+ if space_1_tilt == space_2_tilt:
268
+ logger.error("Space tilts are not compatible.")
269
+ continue
270
+ internal_walls.append(
271
+ {
272
+ "space_1": space_1.space_unique_name(),
273
+ "space_2": space_2.space_unique_name(),
274
+ "construction": construction.name,
275
+ "surface": internal_element.area,
276
+ "space_1_tilt": space_1_tilt,
277
+ "space_2_tilt": space_2_tilt,
278
+ }
279
+ )
280
+ else:
281
+ internal_walls.append(
282
+ {
283
+ "space_1": space_1.space_unique_name(),
284
+ "space_2": space_2.space_unique_name(),
285
+ "construction": construction.name,
286
+ "surface": internal_element.area,
287
+ }
288
+ )
289
+ construction_config = self.constructions.to_config()
290
+ return construction_config | {
291
+ "spaces": spaces,
292
+ "internal_walls": internal_walls,
293
+ }
294
+
295
+ @validate_call
296
+ def to_yaml(self, yaml_path: Path, north_axis: Optional[Vector] = None) -> None:
297
+ config = self.to_config(north_axis=north_axis)
298
+ yaml_data = yaml.dump(config)
299
+ yaml_path.write_text(yaml_data)
300
+
218
301
  @validate_call
219
302
  def create_network(
220
303
  self,
@@ -236,6 +319,30 @@ class Building(BaseShow):
236
319
  for internal_element in self.internal_elements.elements:
237
320
  space_1 = internal_element.spaces[0]
238
321
  space_2 = internal_element.spaces[1]
322
+ if any(
323
+ global_id not in spaces
324
+ for global_id in [space_1.entity.GlobalId, space_2.entity.GlobalId]
325
+ ):
326
+ continue
327
+ space_tilts = []
328
+ if internal_element.element.is_a() in ["IfcSlab"]:
329
+ space_1_tilt = (
330
+ Tilt.floor
331
+ if space_1.bounding_box.centroid.z > space_2.bounding_box.centroid.z
332
+ else Tilt.ceiling
333
+ )
334
+ space_2_tilt = (
335
+ Tilt.floor
336
+ if space_2.bounding_box.centroid.z > space_1.bounding_box.centroid.z
337
+ else Tilt.ceiling
338
+ )
339
+ if space_1_tilt == space_2_tilt:
340
+ logger.error("Space tilts are not compatible.")
341
+ continue
342
+ space_tilts = [
343
+ SpaceTilt(space_name=space_1.name, tilt=space_1_tilt),
344
+ SpaceTilt(space_name=space_2.name, tilt=space_2_tilt),
345
+ ]
239
346
  network.connect_spaces(
240
347
  spaces[space_1.global_id],
241
348
  spaces[space_2.global_id],
@@ -244,6 +351,7 @@ class Building(BaseShow):
244
351
  construction=default_construction,
245
352
  surface=internal_element.area,
246
353
  tilt=Tilt.wall,
354
+ space_tilts=space_tilts,
247
355
  ),
248
356
  )
249
357
  return network
ifctrano/construction.py CHANGED
@@ -1,5 +1,5 @@
1
1
  import logging
2
- from typing import List, Optional
2
+ from typing import List, Optional, Dict, Any
3
3
 
4
4
  from ifcopenshell import file, entity_instance
5
5
 
@@ -38,6 +38,12 @@ default_construction = Construction(
38
38
  Layer(material=material_1, thickness=0.18),
39
39
  ],
40
40
  )
41
+ default_internal_construction = Construction(
42
+ name="default_internal_construction",
43
+ layers=[
44
+ Layer(material=material_1, thickness=0.18),
45
+ ],
46
+ )
41
47
  id_100 = GlassMaterial(
42
48
  name="id_100",
43
49
  thermal_conductivity=1,
@@ -186,14 +192,16 @@ class Constructions(BaseModel):
186
192
  )
187
193
  return cls(constructions=constructions)
188
194
 
189
- def get_construction(self, entity: entity_instance) -> Construction:
195
+ def get_construction(
196
+ self, entity: entity_instance, default: Optional[Construction] = None
197
+ ) -> Construction:
190
198
  construction_id = self._get_construction_id(entity)
191
199
  if construction_id is None:
192
200
  logger.warning(
193
201
  f"Construction ID not found for {entity.GlobalId} ({entity.is_a()}). "
194
202
  f"Using default construction."
195
203
  )
196
- return default_construction
204
+ return default or default_construction
197
205
  constructions = [
198
206
  construction.to_construction()
199
207
  for construction in self.constructions
@@ -225,3 +233,83 @@ class Constructions(BaseModel):
225
233
  else:
226
234
  logger.error("Unexpected material type found.")
227
235
  return None
236
+
237
+ def to_config(self) -> Dict[str, Any]:
238
+ constructions_all = [
239
+ *self.constructions,
240
+ default_construction,
241
+ glass,
242
+ default_internal_construction,
243
+ ]
244
+ constructions = [
245
+ {
246
+ "id": construction.name,
247
+ "layers": [
248
+ {"material": layer.material.name, "thickness": layer.thickness}
249
+ for layer in construction.layers
250
+ ],
251
+ }
252
+ for construction in constructions_all
253
+ if isinstance(construction, Construction)
254
+ ]
255
+ glazings = [
256
+ {
257
+ "id": construction.name,
258
+ "layers": [
259
+ {
260
+ (
261
+ "glass" if isinstance(layer, GlassLayer) else "gas"
262
+ ): layer.material.name,
263
+ "thickness": layer.thickness,
264
+ }
265
+ for layer in construction.layers
266
+ ],
267
+ }
268
+ for construction in constructions_all
269
+ if isinstance(construction, Glass)
270
+ ]
271
+ materials = {
272
+ layer.material
273
+ for construction in constructions_all
274
+ for layer in construction.layers
275
+ if type(layer.material) is Material
276
+ }
277
+ gas = {
278
+ layer.material
279
+ for construction in constructions_all
280
+ for layer in construction.layers
281
+ if type(layer.material) is Gas
282
+ }
283
+ glass_ = {
284
+ layer.material
285
+ for construction in constructions_all
286
+ for layer in construction.layers
287
+ if type(layer.material) is GlassMaterial
288
+ }
289
+
290
+ materials_ = [
291
+ (material.model_dump(exclude={"name"}) | {"id": material.name})
292
+ for material in materials
293
+ ]
294
+ gas_ = [
295
+ (material.model_dump(exclude={"name"}) | {"id": material.name})
296
+ for material in gas
297
+ ]
298
+ glass_material = [
299
+ (_convert_glass(material) | {"id": material.name}) for material in glass_
300
+ ]
301
+ return {
302
+ "constructions": constructions,
303
+ "material": materials_,
304
+ "glazings": glazings,
305
+ "gas": gas_,
306
+ "glass_material": glass_material,
307
+ }
308
+
309
+
310
+ def _convert_glass(glass_: Material) -> Dict[str, Any]:
311
+ return {
312
+ key: (value if not isinstance(value, list) else value)
313
+ for key, value in glass_.model_dump().items()
314
+ if key not in ["name"]
315
+ }
ifctrano/main.py CHANGED
@@ -22,6 +22,38 @@ CHECKMARK = "[green]✔[/green]"
22
22
  CROSS_MARK = "[red]✘[/red]"
23
23
 
24
24
 
25
+ @app.command()
26
+ def config(
27
+ model: Annotated[
28
+ str,
29
+ typer.Argument(help="Local path to the ifc file."),
30
+ ],
31
+ show_space_boundaries: Annotated[
32
+ bool,
33
+ typer.Option(help="Show computed space boundaries."),
34
+ ] = False,
35
+ ) -> None:
36
+ working_directory = Path.cwd()
37
+ with Progress(
38
+ SpinnerColumn(),
39
+ TextColumn("[progress.description]{task.description}"),
40
+ transient=True,
41
+ ) as progress:
42
+ modelica_model_path = Path(model).resolve().with_suffix(".mo")
43
+ config_path = working_directory.joinpath(f"{modelica_model_path.stem}.yaml")
44
+ task = progress.add_task(
45
+ description=f"Generating {config_path} configuration file.",
46
+ total=None,
47
+ )
48
+ building = Building.from_ifc(Path(model))
49
+ if show_space_boundaries:
50
+ print(f"{CHECKMARK} Showing space boundaries.")
51
+ building.show()
52
+ building.to_yaml(config_path)
53
+ progress.remove_task(task)
54
+ print(f"{CHECKMARK} configuration file generated: {config_path}.")
55
+
56
+
25
57
  @app.command()
26
58
  def create(
27
59
  model: Annotated[
@@ -1,5 +1,7 @@
1
+ import logging
2
+ import math
1
3
  import multiprocessing
2
- from typing import Optional, List, Tuple, Any, Annotated
4
+ from typing import Optional, List, Tuple, Any, Annotated, Dict
3
5
 
4
6
  import ifcopenshell
5
7
  import ifcopenshell.geom
@@ -23,8 +25,7 @@ from ifctrano.base import (
23
25
  BaseShow,
24
26
  )
25
27
  from ifctrano.bounding_box import OrientedBoundingBox
26
- from ifctrano.construction import glass, Constructions
27
- from ifctrano.exceptions import HasWindowsWithoutWallsError
28
+ from ifctrano.construction import glass, Constructions, default_construction
28
29
  from ifctrano.utils import (
29
30
  remove_non_alphanumeric,
30
31
  _round,
@@ -34,6 +35,8 @@ from ifctrano.utils import (
34
35
 
35
36
  ROOF_VECTOR = Vector(x=0, y=0, z=1)
36
37
 
38
+ logger = logging.getLogger(__name__)
39
+
37
40
 
38
41
  def initialize_tree(ifc_file: file) -> ifcopenshell.geom.tree:
39
42
  tree = ifcopenshell.geom.tree()
@@ -89,6 +92,14 @@ class Space(GlobalId):
89
92
  main_name = f"{remove_non_alphanumeric(self.name)}_" if self.name else ""
90
93
  return f"space_{main_name}{remove_non_alphanumeric(self.entity.GlobalId)}"
91
94
 
95
+ def space_unique_name(self) -> str:
96
+ base_name = remove_non_alphanumeric(self.name) if self.name else ""
97
+ main_name = f"{base_name}_" if base_name else ""
98
+ space_name = f"{main_name}{remove_non_alphanumeric(self.entity.GlobalId)[-3:]}"
99
+ if "space" not in space_name:
100
+ return f"space_{space_name}"
101
+ return space_name
102
+
92
103
 
93
104
  class ExternalSpaceBoundaryGroup(BaseModelConfig):
94
105
  constructions: List[BaseWall]
@@ -109,11 +120,57 @@ class ExternalSpaceBoundaryGroup(BaseModelConfig):
109
120
  for construction in self.constructions
110
121
  )
111
122
 
123
+ def _merge(
124
+ self,
125
+ constructions: List[Window | ExternalWall],
126
+ construction_type: type[Window | ExternalWall],
127
+ ) -> List[Window | ExternalWall]:
128
+ construction_types = [
129
+ c for c in self.constructions if isinstance(c, construction_type)
130
+ ]
131
+ reference = next(iter(construction_types), None)
132
+ if not reference:
133
+ return []
134
+ surface = sum([construction.surface for construction in construction_types])
135
+ azimuth = reference.azimuth
136
+ tilt = reference.tilt
137
+ return [
138
+ construction_type(
139
+ surface=surface,
140
+ azimuth=azimuth,
141
+ tilt=tilt,
142
+ construction=reference.construction,
143
+ )
144
+ ]
145
+
146
+ def merge(self) -> None:
147
+ self.constructions = [
148
+ *self._merge(self.constructions, Window),
149
+ *self._merge(self.constructions, ExternalWall),
150
+ ]
151
+
152
+ def add_external_wall(self) -> None:
153
+ reference = next(iter(self.constructions))
154
+ surface = sum([construction.surface for construction in self.constructions]) * (
155
+ 0.7 / 0.3
156
+ )
157
+ azimuth = reference.azimuth
158
+ tilt = reference.tilt
159
+ self.constructions.append(
160
+ ExternalWall(
161
+ surface=surface,
162
+ azimuth=azimuth,
163
+ tilt=tilt,
164
+ construction=default_construction,
165
+ )
166
+ )
167
+
112
168
 
113
169
  class ExternalSpaceBoundaryGroups(BaseModelConfig):
114
170
  space_boundary_groups: List[ExternalSpaceBoundaryGroup] = Field(
115
171
  default_factory=list
116
172
  )
173
+ remaining_constructions: List[BaseWall]
117
174
 
118
175
  @classmethod
119
176
  def from_external_boundaries(
@@ -124,6 +181,11 @@ class ExternalSpaceBoundaryGroups(BaseModelConfig):
124
181
  for ex in external_boundaries
125
182
  if isinstance(ex, (ExternalWall, Window)) and ex.tilt == Tilt.wall
126
183
  ]
184
+ remaining_constructions = [
185
+ ex
186
+ for ex in external_boundaries
187
+ if not (isinstance(ex, (ExternalWall, Window)) and ex.tilt == Tilt.wall)
188
+ ]
127
189
  space_boundary_groups = list(
128
190
  {
129
191
  ExternalSpaceBoundaryGroup(
@@ -138,7 +200,12 @@ class ExternalSpaceBoundaryGroups(BaseModelConfig):
138
200
  for ex in boundary_walls
139
201
  }
140
202
  )
141
- return cls(space_boundary_groups=space_boundary_groups)
203
+ groups = cls(
204
+ space_boundary_groups=space_boundary_groups,
205
+ remaining_constructions=remaining_constructions,
206
+ )
207
+ groups.merge()
208
+ return groups
142
209
 
143
210
  def has_windows_without_wall(self) -> bool:
144
211
  return all(
@@ -146,6 +213,24 @@ class ExternalSpaceBoundaryGroups(BaseModelConfig):
146
213
  for group in self.space_boundary_groups
147
214
  )
148
215
 
216
+ def add_external_walls(self) -> None:
217
+ for group in self.space_boundary_groups:
218
+ group.add_external_wall()
219
+
220
+ def get_constructions(self) -> List[ExternalWall | Window]:
221
+ return [
222
+ *[c for group in self.space_boundary_groups for c in group.constructions],
223
+ *self.remaining_constructions,
224
+ ]
225
+
226
+ def merge(self) -> None:
227
+ for g in self.space_boundary_groups:
228
+ g.merge()
229
+
230
+
231
+ def deg_to_rad(deg: float) -> float:
232
+ return round(deg * math.pi / 180.0, 2)
233
+
149
234
 
150
235
  class Azimuths(BaseModel):
151
236
  north: List[float] = [0.0, 360]
@@ -278,6 +363,70 @@ class SpaceBoundaries(BaseShow):
278
363
  if space_boundary in self.boundaries:
279
364
  self.boundaries.remove(space_boundary)
280
365
 
366
+ def to_config(
367
+ self,
368
+ exclude_entities: List[str],
369
+ north_axis: Vector,
370
+ constructions: Constructions,
371
+ ) -> Optional[Dict[str, Any]]:
372
+ external_boundaries: Dict[str, Any] = {
373
+ "external_walls": [],
374
+ "floor_on_grounds": [],
375
+ "windows": [],
376
+ }
377
+ external_boundaries_check = []
378
+ for boundary in self.boundaries:
379
+ boundary_model = boundary.model_element(
380
+ exclude_entities, north_axis, constructions
381
+ )
382
+ if boundary_model:
383
+ external_boundaries_check.append(boundary_model)
384
+ if not external_boundaries_check:
385
+ return None
386
+ external_space_boundaries_group = (
387
+ ExternalSpaceBoundaryGroups.from_external_boundaries(
388
+ external_boundaries_check
389
+ )
390
+ )
391
+ if not external_space_boundaries_group.has_windows_without_wall():
392
+ external_space_boundaries_group.add_external_walls()
393
+ logger.error(
394
+ f"Space {self.space.global_id} has a boundary that has a windows but without walls."
395
+ )
396
+ for boundary_model in external_space_boundaries_group.get_constructions():
397
+ element = {
398
+ "surface": boundary_model.surface,
399
+ "azimuth": deg_to_rad(boundary_model.azimuth),
400
+ "tilt": boundary_model.tilt.value,
401
+ "construction": boundary_model.construction.name,
402
+ }
403
+ if isinstance(
404
+ boundary_model, (ExternalWall, ExternalDoor)
405
+ ) and boundary_model.tilt in [Tilt.wall, Tilt.ceiling]:
406
+ external_boundaries["external_walls"].append(element)
407
+ elif isinstance(boundary_model, (Window)):
408
+ external_boundaries["windows"].append(element)
409
+ elif isinstance(boundary_model, (ExternalWall)) and boundary_model.tilt in [
410
+ Tilt.floor
411
+ ]:
412
+ element.pop("azimuth")
413
+ element.pop("tilt")
414
+ external_boundaries["floor_on_grounds"].append(element)
415
+ else:
416
+ raise ValueError("Unknown boundary type")
417
+
418
+ occupancy_parameters = Occupancy().parameters.model_dump(mode="json")
419
+ space_parameters = SpaceParameter(
420
+ floor_area=self.space.floor_area,
421
+ average_room_height=self.space.average_room_height,
422
+ ).model_dump(mode="json", exclude={"linearize_emissive_power", "volume"})
423
+ return {
424
+ "id": self.space.space_unique_name(),
425
+ "occupancy": {"parameters": occupancy_parameters},
426
+ "parameters": space_parameters,
427
+ "external_boundaries": external_boundaries,
428
+ }
429
+
281
430
  def model(
282
431
  self,
283
432
  exclude_entities: List[str],
@@ -291,12 +440,15 @@ class SpaceBoundaries(BaseShow):
291
440
  )
292
441
  if boundary_model:
293
442
  external_boundaries.append(boundary_model)
443
+ if not external_boundaries:
444
+ return None
294
445
 
295
446
  external_space_boundaries_group = (
296
447
  ExternalSpaceBoundaryGroups.from_external_boundaries(external_boundaries)
297
448
  )
298
449
  if not external_space_boundaries_group.has_windows_without_wall():
299
- raise HasWindowsWithoutWallsError(
450
+ external_space_boundaries_group.add_external_walls()
451
+ logger.error(
300
452
  f"Space {self.space.global_id} has a boundary that has a windows but without walls."
301
453
  )
302
454
  return TranoSpace(
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ifctrano
3
- Version: 0.7.0
3
+ Version: 0.9.0
4
4
  Summary: Package for generating building energy simulation model from IFC
5
5
  License: GPL V3
6
6
  License-File: LICENSE
@@ -16,7 +16,7 @@ Classifier: Programming Language :: Python :: 3.12
16
16
  Requires-Dist: ifcopenshell (>=0.8.1.post1,<0.9.0)
17
17
  Requires-Dist: open3d (>=0.19.0,<0.20.0)
18
18
  Requires-Dist: shapely (>=2.0.7,<3.0.0)
19
- Requires-Dist: trano (>=0.10.0,<0.11.0)
19
+ Requires-Dist: trano (>=0.12.0,<0.13.0)
20
20
  Requires-Dist: typer (>=0.12.5,<0.13.0)
21
21
  Requires-Dist: vedo (>=2025.5.3,<2026.0.0)
22
22
  Project-URL: Repository, https://github.com/andoludo/ifctrano
@@ -0,0 +1,16 @@
1
+ ifctrano/__init__.py,sha256=6wK0Qa_5Xe0KSk5Spn98AjTNCTIXtenZ3C0k8ZaVcn8,191
2
+ ifctrano/base.py,sha256=s8BSnQqd6v_NFvkQFRriHCine6iylUBQai2FduC4IFk,14352
3
+ ifctrano/bounding_box.py,sha256=64jlzY60ZDUX4ipR34WkOICpPhxLP-5w-FP42yBONRs,7729
4
+ ifctrano/building.py,sha256=MgYGyCXKbLXKpqtuokLlds_b1JO3cfOSnDkK7tf1wA0,13583
5
+ ifctrano/construction.py,sha256=I8oRRd1KbG-_sy40-X3PXIz4VCcmbR68WaJcWdpwDIA,10067
6
+ ifctrano/example/verification.ifc,sha256=tQ9QcubT_wrbb-sc1WRRwYpb2cbkWm3dnRfXdP5GTTg,131
7
+ ifctrano/exceptions.py,sha256=JDy_0HV7_FLfr92DkrN8F59zUHl9KdMa_lfIcfBJG6I,540
8
+ ifctrano/main.py,sha256=W6Jx8ZN2Euoz44ECrU8xYetdSmdgkWBo3lngOVYXNU4,6053
9
+ ifctrano/space_boundary.py,sha256=VEQug6zAVtaCpm0YbX1l8UJt0MKmB0oL401JiXYX0Zg,21674
10
+ ifctrano/types.py,sha256=FBSWrD66EL7uHvv0GveCXmYyLtCr8gGRlT7-5j7aZD8,181
11
+ ifctrano/utils.py,sha256=zLQUGVo15RSQQ-vfOuHsJBkHy4QhpxKSAH7Au72s0s0,873
12
+ ifctrano-0.9.0.dist-info/METADATA,sha256=EIzRyrJexhRoyBcvMxI1dlVpkobTcsTJjTvA15NJer8,4884
13
+ ifctrano-0.9.0.dist-info/WHEEL,sha256=zp0Cn7JsFoX2ATtOhtaFYIiE2rmFAD4OcMhtUki8W3U,88
14
+ ifctrano-0.9.0.dist-info/entry_points.txt,sha256=_2daDejazkphufyEu0m3lOeTio53WYmjol3KmSN0JM4,46
15
+ ifctrano-0.9.0.dist-info/licenses/LICENSE,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
16
+ ifctrano-0.9.0.dist-info/RECORD,,
@@ -1,16 +0,0 @@
1
- ifctrano/__init__.py,sha256=6wK0Qa_5Xe0KSk5Spn98AjTNCTIXtenZ3C0k8ZaVcn8,191
2
- ifctrano/base.py,sha256=s8BSnQqd6v_NFvkQFRriHCine6iylUBQai2FduC4IFk,14352
3
- ifctrano/bounding_box.py,sha256=64jlzY60ZDUX4ipR34WkOICpPhxLP-5w-FP42yBONRs,7729
4
- ifctrano/building.py,sha256=tBctMyrsvwRiDk5bDhAdfgRlbyvnbCCeA8rcdUjMbU8,9355
5
- ifctrano/construction.py,sha256=Lyh_o37znrDyxtKgw7dOqVsQk1S3btCPDh2YTJ8h6T0,7234
6
- ifctrano/example/verification.ifc,sha256=tQ9QcubT_wrbb-sc1WRRwYpb2cbkWm3dnRfXdP5GTTg,131
7
- ifctrano/exceptions.py,sha256=JDy_0HV7_FLfr92DkrN8F59zUHl9KdMa_lfIcfBJG6I,540
8
- ifctrano/main.py,sha256=YTp7RaWRMKt_UC2DRt_oXXLGTLCAc9FPg6Tsqg1zgpk,4979
9
- ifctrano/space_boundary.py,sha256=PmiKhcJo6iyxXKNab5NCphQgBQwatoNsqsi-BYHOONs,16055
10
- ifctrano/types.py,sha256=FBSWrD66EL7uHvv0GveCXmYyLtCr8gGRlT7-5j7aZD8,181
11
- ifctrano/utils.py,sha256=zLQUGVo15RSQQ-vfOuHsJBkHy4QhpxKSAH7Au72s0s0,873
12
- ifctrano-0.7.0.dist-info/METADATA,sha256=VAynLl3eaecRmVSgvU3XVSmO-_weipMqdfbw-pq3Blw,4884
13
- ifctrano-0.7.0.dist-info/WHEEL,sha256=zp0Cn7JsFoX2ATtOhtaFYIiE2rmFAD4OcMhtUki8W3U,88
14
- ifctrano-0.7.0.dist-info/entry_points.txt,sha256=_2daDejazkphufyEu0m3lOeTio53WYmjol3KmSN0JM4,46
15
- ifctrano-0.7.0.dist-info/licenses/LICENSE,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
16
- ifctrano-0.7.0.dist-info/RECORD,,