ifctrano 0.8.0__py3-none-any.whl → 0.10.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 +39 -1
- ifctrano/construction.py +8 -4
- ifctrano/space_boundary.py +104 -25
- {ifctrano-0.8.0.dist-info → ifctrano-0.10.0.dist-info}/METADATA +2 -2
- ifctrano-0.10.0.dist-info/RECORD +16 -0
- ifctrano-0.8.0.dist-info/RECORD +0 -16
- {ifctrano-0.8.0.dist-info → ifctrano-0.10.0.dist-info}/WHEEL +0 -0
- {ifctrano-0.8.0.dist-info → ifctrano-0.10.0.dist-info}/entry_points.txt +0 -0
- {ifctrano-0.8.0.dist-info → ifctrano-0.10.0.dist-info}/licenses/LICENSE +0 -0
ifctrano/building.py
CHANGED
@@ -8,6 +8,7 @@ import yaml
|
|
8
8
|
from ifcopenshell import file, entity_instance
|
9
9
|
from pydantic import validate_call, Field, model_validator, field_validator
|
10
10
|
from trano.elements import InternalElement # type: ignore
|
11
|
+
from trano.elements.envelope import SpaceTilt # type: ignore
|
11
12
|
from trano.elements.library.library import Library # type: ignore
|
12
13
|
from trano.elements.types import Tilt # type: ignore
|
13
14
|
from trano.topology import Network # type: ignore
|
@@ -234,10 +235,21 @@ class Building(BaseShow):
|
|
234
235
|
)
|
235
236
|
for space_boundary in self.space_boundaries
|
236
237
|
]
|
238
|
+
spaces = [sp for sp in spaces if sp is not None]
|
237
239
|
internal_walls = []
|
240
|
+
|
238
241
|
for internal_element in self.internal_elements.elements:
|
239
242
|
space_1 = internal_element.spaces[0]
|
240
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
|
+
|
241
253
|
construction = self.constructions.get_construction(
|
242
254
|
internal_element.element, default_internal_construction
|
243
255
|
)
|
@@ -253,7 +265,8 @@ class Building(BaseShow):
|
|
253
265
|
else Tilt.ceiling.value
|
254
266
|
)
|
255
267
|
if space_1_tilt == space_2_tilt:
|
256
|
-
|
268
|
+
logger.error("Space tilts are not compatible.")
|
269
|
+
continue
|
257
270
|
internal_walls.append(
|
258
271
|
{
|
259
272
|
"space_1": space_1.space_unique_name(),
|
@@ -306,6 +319,30 @@ class Building(BaseShow):
|
|
306
319
|
for internal_element in self.internal_elements.elements:
|
307
320
|
space_1 = internal_element.spaces[0]
|
308
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
|
+
]
|
309
346
|
network.connect_spaces(
|
310
347
|
spaces[space_1.global_id],
|
311
348
|
spaces[space_2.global_id],
|
@@ -314,6 +351,7 @@ class Building(BaseShow):
|
|
314
351
|
construction=default_construction,
|
315
352
|
surface=internal_element.area,
|
316
353
|
tilt=Tilt.wall,
|
354
|
+
space_tilts=space_tilts,
|
317
355
|
),
|
318
356
|
)
|
319
357
|
return network
|
ifctrano/construction.py
CHANGED
@@ -235,7 +235,12 @@ class Constructions(BaseModel):
|
|
235
235
|
return None
|
236
236
|
|
237
237
|
def to_config(self) -> Dict[str, Any]:
|
238
|
-
constructions_all = [
|
238
|
+
constructions_all = [
|
239
|
+
*self.constructions,
|
240
|
+
default_construction,
|
241
|
+
glass,
|
242
|
+
default_internal_construction,
|
243
|
+
]
|
239
244
|
constructions = [
|
240
245
|
{
|
241
246
|
"id": construction.name,
|
@@ -304,8 +309,7 @@ class Constructions(BaseModel):
|
|
304
309
|
|
305
310
|
def _convert_glass(glass_: Material) -> Dict[str, Any]:
|
306
311
|
return {
|
307
|
-
key: (
|
308
|
-
value if not isinstance(value, list) else f"{{{','.join(map(str, value))}}}"
|
309
|
-
)
|
312
|
+
key: (value if not isinstance(value, list) else value)
|
310
313
|
for key, value in glass_.model_dump().items()
|
314
|
+
if key not in ["name"]
|
311
315
|
}
|
ifctrano/space_boundary.py
CHANGED
@@ -25,8 +25,7 @@ from ifctrano.base import (
|
|
25
25
|
BaseShow,
|
26
26
|
)
|
27
27
|
from ifctrano.bounding_box import OrientedBoundingBox
|
28
|
-
from ifctrano.construction import glass, Constructions
|
29
|
-
from ifctrano.exceptions import HasWindowsWithoutWallsError
|
28
|
+
from ifctrano.construction import glass, Constructions, default_construction
|
30
29
|
from ifctrano.utils import (
|
31
30
|
remove_non_alphanumeric,
|
32
31
|
_round,
|
@@ -121,11 +120,57 @@ class ExternalSpaceBoundaryGroup(BaseModelConfig):
|
|
121
120
|
for construction in self.constructions
|
122
121
|
)
|
123
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
|
+
|
124
168
|
|
125
169
|
class ExternalSpaceBoundaryGroups(BaseModelConfig):
|
126
170
|
space_boundary_groups: List[ExternalSpaceBoundaryGroup] = Field(
|
127
171
|
default_factory=list
|
128
172
|
)
|
173
|
+
remaining_constructions: List[BaseWall]
|
129
174
|
|
130
175
|
@classmethod
|
131
176
|
def from_external_boundaries(
|
@@ -136,6 +181,11 @@ class ExternalSpaceBoundaryGroups(BaseModelConfig):
|
|
136
181
|
for ex in external_boundaries
|
137
182
|
if isinstance(ex, (ExternalWall, Window)) and ex.tilt == Tilt.wall
|
138
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
|
+
]
|
139
189
|
space_boundary_groups = list(
|
140
190
|
{
|
141
191
|
ExternalSpaceBoundaryGroup(
|
@@ -150,7 +200,12 @@ class ExternalSpaceBoundaryGroups(BaseModelConfig):
|
|
150
200
|
for ex in boundary_walls
|
151
201
|
}
|
152
202
|
)
|
153
|
-
|
203
|
+
groups = cls(
|
204
|
+
space_boundary_groups=space_boundary_groups,
|
205
|
+
remaining_constructions=remaining_constructions,
|
206
|
+
)
|
207
|
+
groups.merge()
|
208
|
+
return groups
|
154
209
|
|
155
210
|
def has_windows_without_wall(self) -> bool:
|
156
211
|
return all(
|
@@ -158,9 +213,23 @@ class ExternalSpaceBoundaryGroups(BaseModelConfig):
|
|
158
213
|
for group in self.space_boundary_groups
|
159
214
|
)
|
160
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
|
+
|
161
230
|
|
162
231
|
def deg_to_rad(deg: float) -> float:
|
163
|
-
return deg * math.pi / 180.0
|
232
|
+
return round(deg * math.pi / 180.0, 2)
|
164
233
|
|
165
234
|
|
166
235
|
class Azimuths(BaseModel):
|
@@ -299,7 +368,7 @@ class SpaceBoundaries(BaseShow):
|
|
299
368
|
exclude_entities: List[str],
|
300
369
|
north_axis: Vector,
|
301
370
|
constructions: Constructions,
|
302
|
-
) -> Dict[str, Any]:
|
371
|
+
) -> Optional[Dict[str, Any]]:
|
303
372
|
external_boundaries: Dict[str, Any] = {
|
304
373
|
"external_walls": [],
|
305
374
|
"floor_on_grounds": [],
|
@@ -312,38 +381,45 @@ class SpaceBoundaries(BaseShow):
|
|
312
381
|
)
|
313
382
|
if boundary_model:
|
314
383
|
external_boundaries_check.append(boundary_model)
|
315
|
-
|
316
|
-
|
317
|
-
"azimuth": deg_to_rad(boundary_model.azimuth),
|
318
|
-
"tilt": boundary_model.tilt.value,
|
319
|
-
"construction": boundary_model.construction.name,
|
320
|
-
}
|
321
|
-
if isinstance(
|
322
|
-
boundary_model, (ExternalWall, ExternalDoor)
|
323
|
-
) and boundary_model.tilt in [Tilt.wall, Tilt.ceiling]:
|
324
|
-
external_boundaries["external_walls"].append(element)
|
325
|
-
elif isinstance(boundary_model, (Window)):
|
326
|
-
external_boundaries["windows"].append(element)
|
327
|
-
elif isinstance(
|
328
|
-
boundary_model, (ExternalWall)
|
329
|
-
) and boundary_model.tilt in [Tilt.floor]:
|
330
|
-
external_boundaries["floor_on_grounds"].append(element)
|
331
|
-
else:
|
332
|
-
raise ValueError("Unknown boundary type")
|
384
|
+
if not external_boundaries_check:
|
385
|
+
return None
|
333
386
|
external_space_boundaries_group = (
|
334
387
|
ExternalSpaceBoundaryGroups.from_external_boundaries(
|
335
388
|
external_boundaries_check
|
336
389
|
)
|
337
390
|
)
|
338
391
|
if not external_space_boundaries_group.has_windows_without_wall():
|
392
|
+
external_space_boundaries_group.add_external_walls()
|
339
393
|
logger.error(
|
340
394
|
f"Space {self.space.global_id} has a boundary that has a windows but without walls."
|
341
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
|
+
|
342
418
|
occupancy_parameters = Occupancy().parameters.model_dump(mode="json")
|
343
419
|
space_parameters = SpaceParameter(
|
344
420
|
floor_area=self.space.floor_area,
|
345
421
|
average_room_height=self.space.average_room_height,
|
346
|
-
).model_dump(mode="json")
|
422
|
+
).model_dump(mode="json", exclude={"linearize_emissive_power", "volume"})
|
347
423
|
return {
|
348
424
|
"id": self.space.space_unique_name(),
|
349
425
|
"occupancy": {"parameters": occupancy_parameters},
|
@@ -364,12 +440,15 @@ class SpaceBoundaries(BaseShow):
|
|
364
440
|
)
|
365
441
|
if boundary_model:
|
366
442
|
external_boundaries.append(boundary_model)
|
443
|
+
if not external_boundaries:
|
444
|
+
return None
|
367
445
|
|
368
446
|
external_space_boundaries_group = (
|
369
447
|
ExternalSpaceBoundaryGroups.from_external_boundaries(external_boundaries)
|
370
448
|
)
|
371
449
|
if not external_space_boundaries_group.has_windows_without_wall():
|
372
|
-
|
450
|
+
external_space_boundaries_group.add_external_walls()
|
451
|
+
logger.error(
|
373
452
|
f"Space {self.space.global_id} has a boundary that has a windows but without walls."
|
374
453
|
)
|
375
454
|
return TranoSpace(
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: ifctrano
|
3
|
-
Version: 0.
|
3
|
+
Version: 0.10.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.
|
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.10.0.dist-info/METADATA,sha256=zu_ivC9PRHxOSX7po9Iio6tCudHxtV83kU8iFajdtFU,4885
|
13
|
+
ifctrano-0.10.0.dist-info/WHEEL,sha256=zp0Cn7JsFoX2ATtOhtaFYIiE2rmFAD4OcMhtUki8W3U,88
|
14
|
+
ifctrano-0.10.0.dist-info/entry_points.txt,sha256=_2daDejazkphufyEu0m3lOeTio53WYmjol3KmSN0JM4,46
|
15
|
+
ifctrano-0.10.0.dist-info/licenses/LICENSE,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
|
16
|
+
ifctrano-0.10.0.dist-info/RECORD,,
|
ifctrano-0.8.0.dist-info/RECORD
DELETED
@@ -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=oPRk8Mwvr51sKaWW-sFPPud23a7KAq10-RgpbfFVziw,12074
|
5
|
-
ifctrano/construction.py,sha256=5W9Vo5VyliQ26vl_NTOnR9i5wbZjz6FKL7O3Sm57uNw,9997
|
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=xJc4mBxiJvZMm75APBHMnpNC7xKBZl36BoRjRhTL-xk,19046
|
10
|
-
ifctrano/types.py,sha256=FBSWrD66EL7uHvv0GveCXmYyLtCr8gGRlT7-5j7aZD8,181
|
11
|
-
ifctrano/utils.py,sha256=zLQUGVo15RSQQ-vfOuHsJBkHy4QhpxKSAH7Au72s0s0,873
|
12
|
-
ifctrano-0.8.0.dist-info/METADATA,sha256=cFw_XDKTheAlXSmvnBd4lyhcchKvs17L2fncjFzq8Rs,4884
|
13
|
-
ifctrano-0.8.0.dist-info/WHEEL,sha256=zp0Cn7JsFoX2ATtOhtaFYIiE2rmFAD4OcMhtUki8W3U,88
|
14
|
-
ifctrano-0.8.0.dist-info/entry_points.txt,sha256=_2daDejazkphufyEu0m3lOeTio53WYmjol3KmSN0JM4,46
|
15
|
-
ifctrano-0.8.0.dist-info/licenses/LICENSE,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
|
16
|
-
ifctrano-0.8.0.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|