ifctrano 0.1.7__py3-none-any.whl → 0.1.9__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/exceptions.py CHANGED
@@ -8,3 +8,19 @@ class BoundingBoxFaceError(Exception):
8
8
 
9
9
  class IfcFileNotFoundError(FileNotFoundError):
10
10
  pass
11
+
12
+
13
+ class SpaceSurfaceAreaNullError(Exception):
14
+ pass
15
+
16
+
17
+ class NoIfcSpaceFoundError(Exception):
18
+ pass
19
+
20
+
21
+ class InvalidLibraryError(Exception):
22
+ pass
23
+
24
+
25
+ class VectorWithNansError(Exception):
26
+ pass
ifctrano/main.py CHANGED
@@ -1,12 +1,14 @@
1
+ import shutil
1
2
  from pathlib import Path
2
- from typing import Annotated
3
+ from tempfile import TemporaryDirectory
4
+ from typing import Annotated, get_args
3
5
 
4
6
  import typer
5
- from pydantic import validate_call
6
7
  from rich.progress import Progress, SpinnerColumn, TextColumn
7
8
 
8
9
  from ifctrano.base import Libraries
9
10
  from ifctrano.building import Building
11
+ from ifctrano.exceptions import InvalidLibraryError
10
12
 
11
13
  app = typer.Typer()
12
14
  CHECKMARK = "[green]✔[/green]"
@@ -14,14 +16,13 @@ CROSS_MARK = "[red]✘[/red]"
14
16
 
15
17
 
16
18
  @app.command()
17
- @validate_call
18
19
  def create(
19
20
  model: Annotated[
20
21
  str,
21
22
  typer.Argument(help="Local path to the ifc file."),
22
23
  ],
23
24
  library: Annotated[
24
- Libraries,
25
+ str,
25
26
  typer.Argument(help="Modelica library to be used for simulation."),
26
27
  ] = "Buildings",
27
28
  ) -> None:
@@ -30,6 +31,10 @@ def create(
30
31
  TextColumn("[progress.description]{task.description}"),
31
32
  transient=True,
32
33
  ) as progress:
34
+ if library not in get_args(Libraries):
35
+ raise InvalidLibraryError(
36
+ f"Invalid library {library}. Valid libraries are {get_args(Libraries)}"
37
+ )
33
38
  modelica_model_path = Path(model).resolve().with_suffix(".mo")
34
39
  task = progress.add_task(
35
40
  description=f"Generating model {modelica_model_path.name} with library {library} from {model}",
@@ -42,3 +47,29 @@ def create(
42
47
  modelica_model_path.write_text(modelica_model)
43
48
  progress.remove_task(task)
44
49
  print(f"{CHECKMARK} Model generated at {modelica_model_path}")
50
+
51
+
52
+ @app.command()
53
+ def verify() -> None:
54
+ verification_ifc = Path(__file__).parent / "example" / "verification.ifc"
55
+ with Progress(
56
+ SpinnerColumn(),
57
+ TextColumn("[progress.description]{task.description}"),
58
+ transient=True,
59
+ ) as progress, TemporaryDirectory() as temp_dir:
60
+ temp_ifc_file = Path(temp_dir) / verification_ifc.name
61
+ shutil.copy(verification_ifc, temp_ifc_file)
62
+ task = progress.add_task(
63
+ description="Trying to create a model from a test file...",
64
+ total=None,
65
+ )
66
+ building = Building.from_ifc(temp_ifc_file)
67
+ building.save_model()
68
+ if temp_ifc_file.parent.joinpath(f"{building.name}.mo").exists():
69
+ progress.remove_task(task)
70
+ print(f"{CHECKMARK} Model successfully created... your system is ready.")
71
+ else:
72
+ progress.remove_task(task)
73
+ print(
74
+ f"{CROSS_MARK} Model could not be created... please check your system."
75
+ )
@@ -7,17 +7,34 @@ import ifcopenshell.geom
7
7
  import ifcopenshell.util.shape
8
8
  from ifcopenshell import entity_instance, file
9
9
  from pydantic import BaseModel, Field
10
- from trano.elements import Space as TranoSpace, ExternalWall, Window, BaseWall # type: ignore
11
- from trano.elements.construction import Construction, Layer, Material # type: ignore
10
+ from trano.data_models.conversion import SpaceParameter # type: ignore
11
+ from trano.elements import Space as TranoSpace, ExternalWall, Window, BaseWall, ExternalDoor # type: ignore
12
+ from trano.elements.construction import ( # type: ignore
13
+ Construction,
14
+ Layer,
15
+ Material,
16
+ Glass,
17
+ GlassLayer,
18
+ GasLayer,
19
+ GlassMaterial,
20
+ Gas,
21
+ )
12
22
  from trano.elements.system import Occupancy # type: ignore
13
- from trano.elements.types import Azimuth, Tilt # type: ignore
23
+ from trano.elements.types import Tilt # type: ignore
14
24
 
15
- from ifctrano.base import GlobalId, settings, BaseModelConfig, CommonSurface
25
+ from ifctrano.base import (
26
+ GlobalId,
27
+ settings,
28
+ BaseModelConfig,
29
+ CommonSurface,
30
+ ROUNDING_FACTOR,
31
+ CLASH_CLEARANCE,
32
+ Vector,
33
+ )
16
34
  from ifctrano.bounding_box import OrientedBoundingBox
17
35
 
18
36
 
19
37
  def initialize_tree(ifc_file: file) -> ifcopenshell.geom.tree:
20
-
21
38
  tree = ifcopenshell.geom.tree()
22
39
 
23
40
  iterator = ifcopenshell.geom.iterator(
@@ -39,14 +56,41 @@ class Space(GlobalId):
39
56
  name: Optional[str] = None
40
57
  bounding_box: OrientedBoundingBox
41
58
  entity: entity_instance
59
+ average_room_height: float
60
+ floor_area: float
61
+ bounding_box_height: float
62
+ bounding_box_volume: float
42
63
 
43
- def space_name(self) -> str:
44
- main_name = (
45
- remove_non_alphanumeric(self.name)
46
- if self.name
47
- else remove_non_alphanumeric(self.entity.GlobalId)
64
+ @classmethod
65
+ def from_entity(cls, entity: entity_instance) -> "Space":
66
+ bounding_box = OrientedBoundingBox.from_entity(entity)
67
+ entity_shape = ifcopenshell.geom.create_shape(settings, entity)
68
+ area = ifcopenshell.util.shape.get_footprint_area(entity_shape.geometry) # type: ignore
69
+ volume = ifcopenshell.util.shape.get_volume(entity_shape.geometry) # type: ignore
70
+ if area:
71
+ average_room_height = volume / area
72
+ else:
73
+ area = bounding_box.volume / bounding_box.height
74
+ average_room_height = bounding_box.height
75
+ return cls(
76
+ global_id=entity.GlobalId,
77
+ name=entity.Name,
78
+ bounding_box=bounding_box,
79
+ entity=entity,
80
+ average_room_height=average_room_height,
81
+ floor_area=area,
82
+ bounding_box_height=bounding_box.height,
83
+ bounding_box_volume=bounding_box.volume,
48
84
  )
49
- return f"space_{main_name}_{self.entity.GlobalId}"
85
+
86
+ def check_volume(self) -> bool:
87
+ return round(self.bounding_box_volume, ROUNDING_FACTOR) == round(
88
+ self.floor_area * self.average_room_height, ROUNDING_FACTOR
89
+ )
90
+
91
+ def space_name(self) -> str:
92
+ main_name = f"{remove_non_alphanumeric(self.name)}_" if self.name else ""
93
+ return f"space_{main_name}{remove_non_alphanumeric(self.entity.GlobalId)}"
50
94
 
51
95
 
52
96
  material_1 = Material(
@@ -61,27 +105,81 @@ construction = Construction(
61
105
  Layer(material=material_1, thickness=0.18),
62
106
  ],
63
107
  )
108
+ id_100 = GlassMaterial(
109
+ name="id_100",
110
+ thermal_conductivity=1,
111
+ density=2500,
112
+ specific_heat_capacity=840,
113
+ solar_transmittance=[0.646],
114
+ solar_reflectance_outside_facing=[0.062],
115
+ solar_reflectance_room_facing=[0.063],
116
+ infrared_transmissivity=0,
117
+ infrared_absorptivity_outside_facing=0.84,
118
+ infrared_absorptivity_room_facing=0.84,
119
+ )
120
+
121
+ air = Gas(
122
+ name="Air",
123
+ thermal_conductivity=0.025,
124
+ density=1.2,
125
+ specific_heat_capacity=1005,
126
+ )
127
+ glass = Glass(
128
+ name="double_glazing",
129
+ u_value_frame=1.4,
130
+ layers=[
131
+ GlassLayer(thickness=0.003, material=id_100),
132
+ GasLayer(thickness=0.0127, material=air),
133
+ GlassLayer(thickness=0.003, material=id_100),
134
+ ],
135
+ )
64
136
 
65
137
 
66
138
  class SpaceBoundary(BaseModelConfig):
67
139
  bounding_box: OrientedBoundingBox
68
140
  entity: entity_instance
69
141
  common_surface: CommonSurface
70
- adjacent_space: Optional[Space] = None
142
+ adjacent_spaces: List[Space] = Field(default_factory=list)
143
+
144
+ def boundary_name(self) -> str:
145
+ return f"{self.entity.is_a()}_{remove_non_alphanumeric(self.entity.GlobalId)}"
71
146
 
72
- def model_element(self) -> Optional[BaseWall]:
147
+ def model_element(
148
+ self, exclude_entities: List[str], north_axis: Vector
149
+ ) -> Optional[BaseWall]:
150
+ if self.entity.GlobalId in exclude_entities:
151
+ return None
152
+ azimuth = self.common_surface.orientation.angle(north_axis)
73
153
  if "wall" in self.entity.is_a().lower():
74
154
  return ExternalWall(
75
- surface=6.44,
76
- azimuth=Azimuth.south,
155
+ name=self.boundary_name(),
156
+ surface=self.common_surface.area,
157
+ azimuth=azimuth,
158
+ tilt=Tilt.wall,
159
+ construction=construction,
160
+ )
161
+ if "door" in self.entity.is_a().lower():
162
+ return ExternalDoor(
163
+ name=self.boundary_name(),
164
+ surface=self.common_surface.area,
165
+ azimuth=azimuth,
77
166
  tilt=Tilt.wall,
78
167
  construction=construction,
79
168
  )
80
169
  if "window" in self.entity.is_a().lower():
81
170
  return Window(
82
- surface=6.44,
83
- azimuth=Azimuth.south,
171
+ name=self.boundary_name(),
172
+ surface=self.common_surface.area,
173
+ azimuth=azimuth,
84
174
  tilt=Tilt.wall,
175
+ construction=glass,
176
+ )
177
+ if "roof" in self.entity.is_a().lower():
178
+ return ExternalWall(
179
+ name=self.boundary_name(),
180
+ surface=self.common_surface.area,
181
+ azimuth=azimuth,
182
+ tilt=Tilt.ceiling,
85
183
  construction=construction,
86
184
  )
87
185
 
@@ -112,15 +210,24 @@ class SpaceBoundaries(BaseModel):
112
210
  space: Space
113
211
  boundaries: List[SpaceBoundary] = Field(default_factory=list)
114
212
 
115
- def model(self) -> TranoSpace:
213
+ def model(
214
+ self, exclude_entities: List[str], north_axis: Vector
215
+ ) -> Optional[TranoSpace]:
216
+ external_boundaries = [
217
+ boundary.model_element(exclude_entities, north_axis)
218
+ for boundary in self.boundaries
219
+ if boundary.model_element(exclude_entities, north_axis)
220
+ ]
221
+ if not external_boundaries:
222
+ return None
116
223
  return TranoSpace(
117
224
  name=self.space.space_name(),
118
225
  occupancy=Occupancy(),
119
- external_boundaries=[
120
- boundary.model_element()
121
- for boundary in self.boundaries
122
- if boundary.model_element()
123
- ],
226
+ parameters=SpaceParameter(
227
+ floor_area=self.space.floor_area,
228
+ average_room_height=self.space.average_room_height,
229
+ ),
230
+ external_boundaries=external_boundaries,
124
231
  )
125
232
 
126
233
  @classmethod
@@ -130,13 +237,7 @@ class SpaceBoundaries(BaseModel):
130
237
  tree: ifcopenshell.geom.tree,
131
238
  space: entity_instance,
132
239
  ) -> "SpaceBoundaries":
133
- bounding_box = OrientedBoundingBox.from_entity(space)
134
- space_ = Space(
135
- global_id=space.GlobalId,
136
- name=space.Name,
137
- bounding_box=bounding_box,
138
- entity=space,
139
- )
240
+ space_ = Space.from_entity(space)
140
241
  clashes = tree.clash_clearance_many(
141
242
  [space],
142
243
  ifcopenshell_file.by_type("IfcWall")
@@ -144,7 +245,7 @@ class SpaceBoundaries(BaseModel):
144
245
  + ifcopenshell_file.by_type("IfcRoof")
145
246
  + ifcopenshell_file.by_type("IfcDoor")
146
247
  + ifcopenshell_file.by_type("IfcWindow"),
147
- clearance=0.1,
248
+ clearance=CLASH_CLEARANCE,
148
249
  )
149
250
  space_boundaries = []
150
251
 
@@ -157,7 +258,7 @@ class SpaceBoundaries(BaseModel):
157
258
  if element.GlobalId == space.GlobalId:
158
259
  continue
159
260
  space_boundary = SpaceBoundary.from_space_and_element(
160
- bounding_box, element
261
+ space_.bounding_box, element
161
262
  )
162
263
  if space_boundary:
163
264
  space_boundaries.append(space_boundary)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: ifctrano
3
- Version: 0.1.7
3
+ Version: 0.1.9
4
4
  Summary: Package for generating building energy simulation model from IFC
5
5
  Home-page: https://github.com/andoludo/ifctrano
6
6
  License: GPL V3
@@ -22,7 +22,7 @@ Description-Content-Type: text/markdown
22
22
 
23
23
  # ifctrano - IFC to Energy Simulation Tool
24
24
 
25
- 📖 **Full Documentation:** 👉 [Trano Docs](https://andoludo.github.io/ifctrano/)
25
+ 📖 **Full Documentation:** 👉 [ifctrano Docs](https://andoludo.github.io/ifctrano/)
26
26
 
27
27
  ```bash
28
28
  pip install ifctrano
@@ -0,0 +1,13 @@
1
+ ifctrano/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
2
+ ifctrano/base.py,sha256=xpfZ27dZ5SZ5z2fz3N4GonkGWvXRYhMfOjoy94R4Yt8,5748
3
+ ifctrano/bounding_box.py,sha256=OnkOR3NOyVRFSdo0d9BeFm93ZU40TV_gQSxYHOEPCYE,9382
4
+ ifctrano/building.py,sha256=NoF0V5H_jDoyl0zkWPrSQa4xX0HIA23Kqu_TbH4Q56M,7134
5
+ ifctrano/example/verification.ifc,sha256=L6YRD_rny7IEB4myA7uCLO6eI-xXOn2jp_67lw3lUrE,128600
6
+ ifctrano/exceptions.py,sha256=1FQeuuq_lVZ4CawwewQvkE8OlDQwhBTbKfmc2FiZodo,431
7
+ ifctrano/main.py,sha256=5S2K0dvFd5k15nsomeDmLiIxFlqYdFr2hdepDFqf0Fs,2672
8
+ ifctrano/space_boundary.py,sha256=SxVXkBi8TNZfmPDFT-6epuewk8E9V1astVgcD3FT8ew,8617
9
+ ifctrano-0.1.9.dist-info/LICENSE,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
10
+ ifctrano-0.1.9.dist-info/METADATA,sha256=EJUPwFbIeYTFV7Jo0nNRF4Vppzu0ihJg7eB3_KFYuF0,3003
11
+ ifctrano-0.1.9.dist-info/WHEEL,sha256=7Z8_27uaHI_UZAc4Uox4PpBhQ9Y5_modZXWMxtUi4NU,88
12
+ ifctrano-0.1.9.dist-info/entry_points.txt,sha256=_2daDejazkphufyEu0m3lOeTio53WYmjol3KmSN0JM4,46
13
+ ifctrano-0.1.9.dist-info/RECORD,,
@@ -0,0 +1,3 @@
1
+ [console_scripts]
2
+ ifctrano=ifctrano.main:app
3
+
@@ -1,12 +0,0 @@
1
- ifctrano/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
2
- ifctrano/base.py,sha256=vwWmzOTnMpE5rmnIti7QeG92Z_rpk-9nIrl7I9Or1cA,4971
3
- ifctrano/bounding_box.py,sha256=_cvS6G6RaNKCFIE3gaQO-bQTKOxIvn0xDZ4rKSCL5T4,8390
4
- ifctrano/building.py,sha256=0RCxlupyPl7c7d1F-JVL0mjDGpB7MPYVpoDuXFI0AeE,1940
5
- ifctrano/exceptions.py,sha256=_kntVIsqEvz3zxtJd8RougQRXEwjF5avarmGB-mig1w,228
6
- ifctrano/main.py,sha256=97Uh8JgjoeeeZ3VMP1_STpDH23mOlW2M9oo9gwECLPE,1411
7
- ifctrano/space_boundary.py,sha256=PkBiLPT26-Us7emyjqNUjKDL4GWMxfQQnHYOgRhelEI,5175
8
- ifctrano-0.1.7.dist-info/LICENSE,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
9
- ifctrano-0.1.7.dist-info/METADATA,sha256=1kgy06CAiqdoqALp5VPMAO9qSULiB1ecI4Su6NFd9Dc,3000
10
- ifctrano-0.1.7.dist-info/WHEEL,sha256=7Z8_27uaHI_UZAc4Uox4PpBhQ9Y5_modZXWMxtUi4NU,88
11
- ifctrano-0.1.7.dist-info/entry_points.txt,sha256=lHxD6pcOuPxITBoWjE35s4f6Dy7tzPa48ffuI-ehb2M,43
12
- ifctrano-0.1.7.dist-info/RECORD,,
@@ -1,3 +0,0 @@
1
- [console_scripts]
2
- trano=ifctrano.main:app
3
-