ifctrano 0.4.0__tar.gz → 0.7.0__tar.gz
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-0.4.0 → ifctrano-0.7.0}/PKG-INFO +4 -3
- {ifctrano-0.4.0 → ifctrano-0.7.0}/ifctrano/base.py +1 -0
- {ifctrano-0.4.0 → ifctrano-0.7.0}/ifctrano/bounding_box.py +2 -0
- {ifctrano-0.4.0 → ifctrano-0.7.0}/ifctrano/building.py +20 -4
- {ifctrano-0.4.0 → ifctrano-0.7.0}/ifctrano/exceptions.py +4 -0
- {ifctrano-0.4.0 → ifctrano-0.7.0}/ifctrano/space_boundary.py +108 -2
- ifctrano-0.7.0/ifctrano/types.py +11 -0
- {ifctrano-0.4.0 → ifctrano-0.7.0}/pyproject.toml +2 -2
- ifctrano-0.4.0/ifctrano/types.py +0 -5
- {ifctrano-0.4.0 → ifctrano-0.7.0}/LICENSE +0 -0
- {ifctrano-0.4.0 → ifctrano-0.7.0}/README.md +0 -0
- {ifctrano-0.4.0 → ifctrano-0.7.0}/ifctrano/__init__.py +0 -0
- {ifctrano-0.4.0 → ifctrano-0.7.0}/ifctrano/construction.py +0 -0
- {ifctrano-0.4.0 → ifctrano-0.7.0}/ifctrano/example/verification.ifc +0 -0
- {ifctrano-0.4.0 → ifctrano-0.7.0}/ifctrano/main.py +0 -0
- {ifctrano-0.4.0 → ifctrano-0.7.0}/ifctrano/utils.py +0 -0
@@ -1,8 +1,9 @@
|
|
1
|
-
Metadata-Version: 2.
|
1
|
+
Metadata-Version: 2.4
|
2
2
|
Name: ifctrano
|
3
|
-
Version: 0.
|
3
|
+
Version: 0.7.0
|
4
4
|
Summary: Package for generating building energy simulation model from IFC
|
5
5
|
License: GPL V3
|
6
|
+
License-File: LICENSE
|
6
7
|
Keywords: BIM,IFC,energy simulation,modelica,building energy simulation,buildings,ideas
|
7
8
|
Author: Ando Andriamamonjy
|
8
9
|
Author-email: andoludovic.andriamamonjy@gmail.com
|
@@ -15,7 +16,7 @@ Classifier: Programming Language :: Python :: 3.12
|
|
15
16
|
Requires-Dist: ifcopenshell (>=0.8.1.post1,<0.9.0)
|
16
17
|
Requires-Dist: open3d (>=0.19.0,<0.20.0)
|
17
18
|
Requires-Dist: shapely (>=2.0.7,<3.0.0)
|
18
|
-
Requires-Dist: trano (>=0.
|
19
|
+
Requires-Dist: trano (>=0.10.0,<0.11.0)
|
19
20
|
Requires-Dist: typer (>=0.12.5,<0.13.0)
|
20
21
|
Requires-Dist: vedo (>=2025.5.3,<2026.0.0)
|
21
22
|
Project-URL: Repository, https://github.com/andoludo/ifctrano
|
@@ -78,6 +78,7 @@ class ExtendCommonSurface(CommonSurface):
|
|
78
78
|
orientation=self.orientation,
|
79
79
|
main_vertices=self.main_vertices,
|
80
80
|
common_vertices=self.common_vertices,
|
81
|
+
polygon=self.polygon,
|
81
82
|
)
|
82
83
|
|
83
84
|
|
@@ -137,6 +138,7 @@ class OrientedBoundingBox(BaseShow):
|
|
137
138
|
common_vertices=projected_face_1.common_vertices(
|
138
139
|
intersection
|
139
140
|
),
|
141
|
+
polygon=intersection.wkt,
|
140
142
|
)
|
141
143
|
)
|
142
144
|
|
@@ -1,3 +1,4 @@
|
|
1
|
+
import logging
|
1
2
|
import re
|
2
3
|
from pathlib import Path
|
3
4
|
from typing import List, Tuple, Any, Optional, Set
|
@@ -12,7 +13,11 @@ from trano.topology import Network # type: ignore
|
|
12
13
|
from vedo import Line # type: ignore
|
13
14
|
|
14
15
|
from ifctrano.base import BaseModelConfig, Libraries, Vector, BaseShow, CommonSurface
|
15
|
-
from ifctrano.exceptions import
|
16
|
+
from ifctrano.exceptions import (
|
17
|
+
IfcFileNotFoundError,
|
18
|
+
NoIfcSpaceFoundError,
|
19
|
+
NoSpaceBoundariesError,
|
20
|
+
)
|
16
21
|
from ifctrano.space_boundary import (
|
17
22
|
SpaceBoundaries,
|
18
23
|
initialize_tree,
|
@@ -20,6 +25,8 @@ from ifctrano.space_boundary import (
|
|
20
25
|
)
|
21
26
|
from ifctrano.construction import Constructions, default_construction
|
22
27
|
|
28
|
+
logger = logging.getLogger(__name__)
|
29
|
+
|
23
30
|
|
24
31
|
def get_spaces(ifcopenshell_file: file) -> List[entity_instance]:
|
25
32
|
return ifcopenshell_file.by_type("IfcSpace")
|
@@ -180,9 +187,18 @@ class Building(BaseShow):
|
|
180
187
|
]
|
181
188
|
if not spaces:
|
182
189
|
raise NoIfcSpaceFoundError("No IfcSpace found in the file.")
|
183
|
-
space_boundaries = [
|
184
|
-
|
185
|
-
|
190
|
+
space_boundaries = []
|
191
|
+
for space in spaces:
|
192
|
+
try:
|
193
|
+
space_boundaries.append(
|
194
|
+
SpaceBoundaries.from_space_entity(ifc_file, tree, space)
|
195
|
+
)
|
196
|
+
except Exception as e: # noqa: PERF203
|
197
|
+
logger.error(f"Cannot process space {space.id()}. Reason {e}")
|
198
|
+
continue
|
199
|
+
if not space_boundaries:
|
200
|
+
raise NoSpaceBoundariesError("No valid space boundaries found.")
|
201
|
+
|
186
202
|
return cls(
|
187
203
|
space_boundaries=space_boundaries,
|
188
204
|
ifc_file=ifc_file,
|
@@ -5,7 +5,8 @@ import ifcopenshell
|
|
5
5
|
import ifcopenshell.geom
|
6
6
|
import ifcopenshell.util.shape
|
7
7
|
from ifcopenshell import entity_instance, file
|
8
|
-
from pydantic import Field, BeforeValidator, BaseModel
|
8
|
+
from pydantic import Field, BeforeValidator, BaseModel, ConfigDict
|
9
|
+
from shapely import wkt # type: ignore
|
9
10
|
from trano.data_models.conversion import SpaceParameter # type: ignore
|
10
11
|
from trano.elements import Space as TranoSpace, ExternalWall, Window, BaseWall, ExternalDoor # type: ignore
|
11
12
|
from trano.elements.system import Occupancy # type: ignore
|
@@ -340,4 +341,109 @@ class SpaceBoundaries(BaseShow):
|
|
340
341
|
)
|
341
342
|
if space_boundary:
|
342
343
|
space_boundaries.append(space_boundary)
|
343
|
-
|
344
|
+
merged_boundaries = MergedSpaceBoundaries.from_boundaries(space_boundaries)
|
345
|
+
space_boundaries_ = merged_boundaries.merge_boundaries_from_part()
|
346
|
+
space_boundaries__ = remove_duplicate_boundaries(space_boundaries_)
|
347
|
+
return cls(space=space_, boundaries=space_boundaries__)
|
348
|
+
|
349
|
+
|
350
|
+
def remove_duplicate_boundaries(
|
351
|
+
boundaries: List[SpaceBoundary],
|
352
|
+
) -> List[SpaceBoundary]:
|
353
|
+
types = ["IfcRoof", "IfcSlab"]
|
354
|
+
boundaries = sorted(boundaries, key=lambda b: b.entity.GlobalId)
|
355
|
+
boundaries_without_types = [
|
356
|
+
sp for sp in boundaries if sp.entity.is_a() not in types
|
357
|
+
]
|
358
|
+
new_boundaries = []
|
359
|
+
for type_ in types:
|
360
|
+
references = [sp for sp in boundaries if sp.entity.is_a() == type_]
|
361
|
+
while True:
|
362
|
+
reference = next(iter(references), None)
|
363
|
+
if not reference:
|
364
|
+
break
|
365
|
+
others = [p_ for p_ in references if p_ != reference]
|
366
|
+
intersecting = [
|
367
|
+
o
|
368
|
+
for o in others
|
369
|
+
if (
|
370
|
+
wkt.loads(o.common_surface.polygon).intersects(
|
371
|
+
wkt.loads(reference.common_surface.polygon)
|
372
|
+
)
|
373
|
+
and o.common_surface.orientation
|
374
|
+
== reference.common_surface.orientation
|
375
|
+
)
|
376
|
+
and (
|
377
|
+
wkt.loads(o.common_surface.polygon).intersection(
|
378
|
+
wkt.loads(reference.common_surface.polygon)
|
379
|
+
)
|
380
|
+
).area
|
381
|
+
> 0
|
382
|
+
]
|
383
|
+
current_group = sorted(
|
384
|
+
[*intersecting, reference], key=lambda p: p.entity.GlobalId
|
385
|
+
)
|
386
|
+
new_boundaries.append(next(iter(current_group)))
|
387
|
+
references = [p_ for p_ in references if p_ not in current_group]
|
388
|
+
return [*boundaries_without_types, *new_boundaries]
|
389
|
+
|
390
|
+
|
391
|
+
class MergedSpaceBoundary(BaseModel):
|
392
|
+
model_config = ConfigDict(arbitrary_types_allowed=True)
|
393
|
+
parent: entity_instance
|
394
|
+
related_boundaries: List[SpaceBoundary]
|
395
|
+
|
396
|
+
def get_new_boundary(self) -> Optional[SpaceBoundary]:
|
397
|
+
related_boundaries = sorted(
|
398
|
+
self.related_boundaries, key=lambda b: b.entity.GlobalId
|
399
|
+
)
|
400
|
+
boundary = next(iter(related_boundaries), None)
|
401
|
+
if boundary:
|
402
|
+
return SpaceBoundary.model_validate(
|
403
|
+
boundary.model_dump() | {"entity": self.parent}
|
404
|
+
)
|
405
|
+
return None
|
406
|
+
|
407
|
+
|
408
|
+
class MergedSpaceBoundaries(BaseModel):
|
409
|
+
part_boundaries: List[MergedSpaceBoundary]
|
410
|
+
original_boundaries: List[SpaceBoundary]
|
411
|
+
|
412
|
+
@classmethod
|
413
|
+
def from_boundaries(
|
414
|
+
cls, space_boundaries: List[SpaceBoundary]
|
415
|
+
) -> "MergedSpaceBoundaries":
|
416
|
+
building_element_part_boundaries = [
|
417
|
+
boundary
|
418
|
+
for boundary in space_boundaries
|
419
|
+
if boundary.entity.is_a() in ["IfcBuildingElementPart"]
|
420
|
+
]
|
421
|
+
existing_parent_entities = {
|
422
|
+
decompose.RelatingObject
|
423
|
+
for b in building_element_part_boundaries
|
424
|
+
for decompose in b.entity.Decomposes
|
425
|
+
}
|
426
|
+
part_boundaries = [
|
427
|
+
MergedSpaceBoundary(
|
428
|
+
parent=parent,
|
429
|
+
related_boundaries=[
|
430
|
+
b
|
431
|
+
for b in building_element_part_boundaries
|
432
|
+
for decompose in b.entity.Decomposes
|
433
|
+
if decompose.RelatingObject == parent
|
434
|
+
],
|
435
|
+
)
|
436
|
+
for parent in existing_parent_entities
|
437
|
+
]
|
438
|
+
return cls(
|
439
|
+
part_boundaries=part_boundaries, original_boundaries=space_boundaries
|
440
|
+
)
|
441
|
+
|
442
|
+
def merge_boundaries_from_part(self) -> List[SpaceBoundary]:
|
443
|
+
new_boundaries = [b.get_new_boundary() for b in self.part_boundaries]
|
444
|
+
new_boundaries_ = [nb for nb in new_boundaries if nb is not None]
|
445
|
+
return [
|
446
|
+
b
|
447
|
+
for b in self.original_boundaries
|
448
|
+
if b.entity.is_a() not in ["IfcBuildingElementPart"]
|
449
|
+
] + new_boundaries_
|
@@ -1,6 +1,6 @@
|
|
1
1
|
[tool.poetry]
|
2
2
|
name = "ifctrano"
|
3
|
-
version = "0.
|
3
|
+
version = "0.7.0"
|
4
4
|
description = "Package for generating building energy simulation model from IFC"
|
5
5
|
authors = ["Ando Andriamamonjy <andoludovic.andriamamonjy@gmail.com>"]
|
6
6
|
license = "GPL V3"
|
@@ -11,7 +11,7 @@ keywords = ["BIM","IFC","energy simulation", "modelica", "building energy simula
|
|
11
11
|
[tool.poetry.dependencies]
|
12
12
|
python = ">=3.10,<3.13"
|
13
13
|
ifcopenshell = "^0.8.1.post1"
|
14
|
-
trano = "^0.
|
14
|
+
trano = "^0.10.0"
|
15
15
|
shapely = "^2.0.7"
|
16
16
|
typer = "^0.12.5"
|
17
17
|
vedo = "^2025.5.3"
|
ifctrano-0.4.0/ifctrano/types.py
DELETED
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|