ifctrano 0.1.12__py3-none-any.whl → 0.3.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/bounding_box.py CHANGED
@@ -1,105 +1,47 @@
1
+ from itertools import combinations
1
2
  from logging import getLogger
2
- from typing import List, Optional, Any, Tuple, cast
3
+ from typing import List, Optional, Any, Tuple
3
4
 
4
5
  import ifcopenshell
5
- import numpy as np
6
- from ifcopenshell import entity_instance
7
6
  import ifcopenshell.geom
7
+ import ifcopenshell.util.placement
8
8
  import ifcopenshell.util.shape
9
+ import numpy as np
10
+ import open3d # type: ignore
11
+ from ifcopenshell import entity_instance
9
12
  from pydantic import (
10
13
  BaseModel,
11
14
  Field,
15
+ ConfigDict,
12
16
  )
13
- from shapely import Polygon # type: ignore
17
+ from scipy.spatial import ConvexHull # type: ignore
18
+ from vedo import Line # type: ignore
14
19
 
15
20
  from ifctrano.base import (
16
21
  Point,
17
22
  Vector,
18
- P,
19
- Sign,
20
- CoordinateSystem,
21
23
  Vertices,
22
24
  BaseModelConfig,
23
25
  settings,
24
26
  CommonSurface,
25
27
  AREA_TOLERANCE,
26
- ROUNDING_FACTOR,
28
+ FaceVertices,
29
+ BaseShow,
27
30
  )
28
- from ifctrano.exceptions import BoundingBoxFaceError, VectorWithNansError
31
+ from ifctrano.exceptions import VectorWithNansError
29
32
 
30
33
  logger = getLogger(__name__)
31
34
 
32
35
 
33
- def get_normal(
34
- centroid: Point,
35
- difference: Point,
36
- face_signs: List[Sign],
37
- coordinate_system: CoordinateSystem,
38
- ) -> Vector:
39
- point_0 = centroid + difference.s(face_signs[0])
40
- point_1 = centroid + difference.s(face_signs[1])
41
- point_2 = centroid + difference.s(face_signs[2])
42
- vector_1 = coordinate_system.project((point_1 - point_0).to_array())
43
- vector_2 = coordinate_system.project((point_2 - point_0).to_array())
44
- array = (
45
- (Vector.from_array(vector_1) * Vector.from_array(vector_2)).norm().to_array()
46
- )
47
- return Vector.from_array(array)
48
-
49
-
50
- class Polygon2D(BaseModelConfig):
51
- polygon: Polygon
52
- normal: Vector
53
- length: float
54
-
55
-
56
36
  class BoundingBoxFace(BaseModelConfig):
57
- vertices: Vertices
37
+ vertices: FaceVertices
58
38
  normal: Vector
59
- coordinate_system: CoordinateSystem
60
39
 
61
40
  @classmethod
62
- def build(
63
- cls,
64
- centroid: Point,
65
- difference: Point,
66
- face_signs: List[Sign],
67
- coordinate_system: CoordinateSystem,
68
- ) -> "BoundingBoxFace":
69
- if len(face_signs) != len(set(face_signs)):
70
- raise BoundingBoxFaceError("Face signs must be unique")
71
- normal = get_normal(centroid, difference, face_signs, coordinate_system)
72
- vertices_ = [(centroid + difference.s(s)).to_list() for s in face_signs]
73
- vertices_ = [*vertices_, vertices_[0]]
74
- vertices__ = [coordinate_system.project(v) for v in vertices_]
75
- vertices = Vertices.from_arrays(vertices__)
76
-
77
- return cls(
78
- vertices=vertices, normal=normal, coordinate_system=coordinate_system
79
- )
80
-
81
- def get_face_area(self) -> float:
82
- polygon_2d = self.get_2d_polygon(self.coordinate_system)
83
- return cast(float, round(polygon_2d.polygon.area, ROUNDING_FACTOR))
41
+ def build(cls, vertices: Vertices) -> "BoundingBoxFace":
42
+ face_vertices = vertices.to_face_vertices()
84
43
 
85
- def get_2d_polygon(self, coordinate_system: CoordinateSystem) -> Polygon2D:
86
-
87
- projected_vertices = coordinate_system.inverse(self.vertices.to_array())
88
- projected_normal_index = Vector.from_array(
89
- coordinate_system.inverse(self.normal.to_array())
90
- ).get_normal_index()
91
- polygon = Polygon(
92
- [
93
- [v_ for i, v_ in enumerate(v) if i != projected_normal_index]
94
- for v in projected_vertices.tolist()
95
- ]
96
- )
97
-
98
- return Polygon2D(
99
- polygon=polygon,
100
- normal=self.normal,
101
- length=projected_vertices.tolist()[0][projected_normal_index],
102
- )
44
+ return cls(vertices=face_vertices, normal=face_vertices.get_normal())
103
45
 
104
46
 
105
47
  class BoundingBoxFaces(BaseModel):
@@ -110,56 +52,51 @@ class BoundingBoxFaces(BaseModel):
110
52
 
111
53
  @classmethod
112
54
  def build(
113
- cls, centroid: Point, difference: Point, coordinate_system: CoordinateSystem
55
+ cls, box_points: np.ndarray[tuple[int, ...], np.dtype[np.float64]]
114
56
  ) -> "BoundingBoxFaces":
115
- face_signs = [
116
- [Sign(x=-1, y=-1, z=-1), Sign(y=-1, z=-1), Sign(z=-1), Sign(x=-1, z=-1)],
117
- [Sign(x=-1, y=-1), Sign(y=-1), Sign(), Sign(x=-1)],
118
- [
119
- Sign(x=-1, y=-1, z=-1),
120
- Sign(x=-1, y=1, z=-1),
121
- Sign(x=-1, y=1, z=1),
122
- Sign(x=-1, y=-1, z=1),
123
- ],
124
- [
125
- Sign(x=1, y=-1, z=-1),
126
- Sign(x=1, y=1, z=-1),
127
- Sign(x=1, y=1, z=1),
128
- Sign(x=1, y=-1, z=1),
129
- ],
130
- [
131
- Sign(x=-1, y=-1, z=-1),
132
- Sign(x=1, y=-1, z=-1),
133
- Sign(x=1, y=-1, z=1),
134
- Sign(x=-1, y=-1, z=1),
135
- ],
136
- [
137
- Sign(x=-1, y=1, z=-1),
138
- Sign(x=1, y=1, z=-1),
139
- Sign(x=1, y=1, z=1),
140
- Sign(x=-1, y=1, z=1),
141
- ],
142
- ]
143
57
  faces = [
144
- BoundingBoxFace.build(centroid, difference, face_sign, coordinate_system)
145
- for face_sign in face_signs
58
+ [0, 1, 6, 3],
59
+ [2, 5, 4, 7],
60
+ [0, 3, 5, 2],
61
+ [1, 7, 4, 6],
62
+ [0, 2, 7, 1],
63
+ [3, 6, 4, 5],
64
+ ]
65
+ faces_ = [
66
+ BoundingBoxFace.build(Vertices.from_arrays(np.array(box_points)[face]))
67
+ for face in faces
146
68
  ]
147
- return cls(faces=faces)
69
+ return cls(faces=faces_)
148
70
 
149
71
 
150
72
  class ExtendCommonSurface(CommonSurface):
151
73
  distance: float
152
74
 
153
75
  def to_common_surface(self) -> CommonSurface:
154
- return CommonSurface(area=self.area, orientation=self.orientation)
76
+ return CommonSurface(
77
+ area=self.area,
78
+ orientation=self.orientation,
79
+ main_vertices=self.main_vertices,
80
+ common_vertices=self.common_vertices,
81
+ )
155
82
 
156
83
 
157
- class OrientedBoundingBox(BaseModel):
84
+ class OrientedBoundingBox(BaseShow):
85
+ model_config = ConfigDict(arbitrary_types_allowed=True)
158
86
  faces: BoundingBoxFaces
159
87
  centroid: Point
160
88
  area_tolerance: float = Field(default=AREA_TOLERANCE)
161
89
  volume: float
162
90
  height: float
91
+ entity: Optional[entity_instance] = None
92
+
93
+ def lines(self) -> List[Line]:
94
+ lines = []
95
+ for f in self.faces.faces:
96
+ face = f.vertices.to_list()
97
+ for a, b in combinations(face, 2):
98
+ lines.append(Line(a, b))
99
+ return lines
163
100
 
164
101
  def intersect_faces(self, other: "OrientedBoundingBox") -> Optional[CommonSurface]:
165
102
  extend_surfaces = []
@@ -167,27 +104,42 @@ class OrientedBoundingBox(BaseModel):
167
104
 
168
105
  for other_face in other.faces.faces:
169
106
  vector = face.normal * other_face.normal
170
- if vector.is_a_zero():
171
- polygon_1 = other_face.get_2d_polygon(face.coordinate_system)
172
- polygon_2 = face.get_2d_polygon(face.coordinate_system)
173
- intersection = polygon_2.polygon.intersection(polygon_1.polygon)
107
+ if vector.is_null():
108
+ projected_face_1 = face.vertices.project(face.vertices)
109
+ projected_face_2 = face.vertices.project(other_face.vertices)
110
+ polygon_1 = projected_face_1.to_polygon()
111
+ polygon_2 = projected_face_2.to_polygon()
112
+ intersection = polygon_2.intersection(polygon_1)
174
113
  if intersection.area > self.area_tolerance:
175
- distance = abs(polygon_1.length - polygon_2.length)
114
+ distance = projected_face_1.get_distance(projected_face_2)
176
115
  area = intersection.area
177
- direction_vector = (other.centroid - self.centroid).norm()
178
116
  try:
179
- orientation = direction_vector.project(face.normal).norm()
117
+ direction_vector = (
118
+ other.centroid - self.centroid
119
+ ).normalize()
120
+ orientation = direction_vector.project(
121
+ face.normal
122
+ ).normalize()
180
123
  except VectorWithNansError as e:
181
- logger.error(
182
- "Orientation vector was not properly computed when computing the intersection between"
183
- f"two elements. Error: {e}"
124
+ logger.warning(
125
+ "Orientation vector was not properly computed when computing the intersection between "
126
+ f"two elements "
127
+ f"({(self.entity.GlobalId, self.entity.is_a(), self.entity.Name) if self.entity else None}" # noqa: E501
128
+ f", {(other.entity.GlobalId, other.entity.is_a(), other.entity.Name)if other.entity else None}). Error: {e}" # noqa: E501
184
129
  )
185
130
  continue
186
131
  extend_surfaces.append(
187
132
  ExtendCommonSurface(
188
- distance=distance, area=area, orientation=orientation
133
+ distance=distance,
134
+ area=area,
135
+ orientation=orientation,
136
+ main_vertices=face.vertices,
137
+ common_vertices=projected_face_1.common_vertices(
138
+ intersection
139
+ ),
189
140
  )
190
141
  )
142
+
191
143
  if extend_surfaces:
192
144
  if not all(
193
145
  e.orientation == extend_surfaces[0].orientation for e in extend_surfaces
@@ -199,69 +151,47 @@ class OrientedBoundingBox(BaseModel):
199
151
  extend_surfaces, key=lambda x: x.distance, reverse=True
200
152
  )[-1]
201
153
  return extend_surface.to_common_surface()
154
+ else:
155
+ logger.warning(
156
+ "No common surfaces found between between "
157
+ f"two elements "
158
+ f"({(self.entity.GlobalId, self.entity.is_a(), self.entity.Name) if self.entity else None}, "
159
+ f"{(other.entity.GlobalId, other.entity.is_a(), other.entity.Name) if other.entity else None})."
160
+ )
202
161
  return None
203
162
 
204
163
  @classmethod
205
164
  def from_vertices(
206
- cls, vertices: np.ndarray[tuple[int, ...], np.dtype[np.float64]]
165
+ cls,
166
+ vertices: np.ndarray[tuple[int, ...], np.dtype[np.float64]],
167
+ entity: Optional[entity_instance] = None,
207
168
  ) -> "OrientedBoundingBox":
208
- vertices_np = np.array(vertices)
209
- points = np.asarray(vertices_np)
210
- cov = np.cov(points, y=None, rowvar=0, bias=0) # type: ignore
211
- v, vect = np.linalg.eig(np.round(cov, ROUNDING_FACTOR))
212
- tvect = np.transpose(vect)
213
- points_r = np.dot(points, np.linalg.inv(tvect))
214
-
215
- co_min = np.min(points_r, axis=0)
216
- co_max = np.max(points_r, axis=0)
217
-
218
- xmin, xmax = co_min[0], co_max[0]
219
- ymin, ymax = co_min[1], co_max[1]
220
- zmin, zmax = co_min[2], co_max[2]
221
-
222
- x_len = xmax - xmin
223
- y_len = ymax - ymin
224
- z_len = zmax - zmin
225
- xdif = x_len * 0.5
226
- ydif = y_len * 0.5
227
- zdif = z_len * 0.5
228
-
229
- cx = xmin + xdif
230
- cy = ymin + ydif
231
- cz = zmin + zdif
232
- corners = np.array(
233
- [
234
- [cx - xdif, cy - ydif, cz - zdif],
235
- [cx - xdif, cy + ydif, cz - zdif],
236
- [cx - xdif, cy + ydif, cz + zdif],
237
- [cx - xdif, cy - ydif, cz + zdif],
238
- [cx + xdif, cy + ydif, cz + zdif],
239
- [cx + xdif, cy + ydif, cz - zdif],
240
- [cx + xdif, cy - ydif, cz + zdif],
241
- [cx + xdif, cy - ydif, cz - zdif],
242
- ]
169
+ points_ = open3d.utility.Vector3dVector(vertices)
170
+ mobb = open3d.geometry.OrientedBoundingBox.create_from_points_minimal(
171
+ points_, robust=True
243
172
  )
244
- corners_ = np.dot(corners, tvect)
245
- dims = np.transpose(corners_)
246
- x_size = np.max(dims[0]) - np.min(dims[0])
247
- y_size = np.max(dims[1]) - np.min(dims[1])
248
- z_size = np.max(dims[2]) - np.min(dims[2])
249
- coordinate_system = CoordinateSystem.from_array(tvect)
250
- c = P(x=cx, y=cy, z=cz)
251
- d = P(x=xdif, y=ydif, z=zdif)
252
- faces = BoundingBoxFaces.build(c, d, coordinate_system)
173
+ height = (mobb.get_max_bound() - mobb.get_min_bound())[
174
+ 2
175
+ ] # assuming that height is the z axis
176
+ centroid = Point.from_array(mobb.get_center())
177
+ faces = BoundingBoxFaces.build(np.array(mobb.get_box_points()))
253
178
  return cls(
254
179
  faces=faces,
255
- centroid=Point.from_array(coordinate_system.project(c.to_array())),
256
- volume=x_size * y_size * z_size,
257
- height=z_size,
180
+ centroid=centroid,
181
+ volume=mobb.volume(),
182
+ height=height,
183
+ entity=entity,
258
184
  )
259
185
 
260
186
  @classmethod
261
187
  def from_entity(cls, entity: entity_instance) -> "OrientedBoundingBox":
262
188
  entity_shape = ifcopenshell.geom.create_shape(settings, entity)
263
-
264
189
  vertices = ifcopenshell.util.shape.get_shape_vertices(
265
190
  entity_shape, entity_shape.geometry # type: ignore
266
191
  )
267
- return cls.from_vertices(vertices)
192
+ vertices_ = Vertices.from_arrays(np.asarray(vertices))
193
+ hull = ConvexHull(vertices_.to_array())
194
+ vertices_ = Vertices.from_arrays(vertices_.to_array()[hull.vertices])
195
+ points_ = open3d.utility.Vector3dVector(vertices_.to_array())
196
+ aab = open3d.geometry.AxisAlignedBoundingBox.create_from_points(points_)
197
+ return cls.from_vertices(aab.get_box_points(), entity)
ifctrano/building.py CHANGED
@@ -1,6 +1,6 @@
1
1
  import re
2
2
  from pathlib import Path
3
- from typing import List, Tuple, Any, Optional
3
+ from typing import List, Tuple, Any, Optional, Set
4
4
 
5
5
  import ifcopenshell
6
6
  from ifcopenshell import file, entity_instance
@@ -9,15 +9,16 @@ from trano.elements import InternalElement # type: ignore
9
9
  from trano.elements.library.library import Library # type: ignore
10
10
  from trano.elements.types import Tilt # type: ignore
11
11
  from trano.topology import Network # type: ignore
12
+ from vedo import Line # type: ignore
12
13
 
13
- from ifctrano.base import BaseModelConfig, Libraries, Vector
14
+ from ifctrano.base import BaseModelConfig, Libraries, Vector, BaseShow, CommonSurface
14
15
  from ifctrano.exceptions import IfcFileNotFoundError, NoIfcSpaceFoundError
15
16
  from ifctrano.space_boundary import (
16
17
  SpaceBoundaries,
17
18
  initialize_tree,
18
19
  Space,
19
- construction,
20
20
  )
21
+ from ifctrano.construction import Constructions, default_construction
21
22
 
22
23
 
23
24
  def get_spaces(ifcopenshell_file: file) -> List[entity_instance]:
@@ -28,6 +29,7 @@ class IfcInternalElement(BaseModelConfig):
28
29
  spaces: List[Space]
29
30
  element: entity_instance
30
31
  area: float
32
+ common_surface: CommonSurface
31
33
 
32
34
  def __hash__(self) -> int:
33
35
  return hash(
@@ -49,23 +51,108 @@ class IfcInternalElement(BaseModelConfig):
49
51
  self.area,
50
52
  )
51
53
 
54
+ def lines(self) -> List[Line]:
55
+ lines = []
56
+ if self.common_surface:
57
+ lines += self.common_surface.lines()
58
+ return lines
52
59
 
53
- class InternalElements(BaseModelConfig):
60
+
61
+ class InternalElements(BaseShow):
54
62
  elements: List[IfcInternalElement] = Field(default_factory=list)
55
63
 
56
64
  def internal_element_ids(self) -> List[str]:
57
65
  return list({e.element.GlobalId for e in self.elements})
58
66
 
59
- def description(self) -> List[Tuple[Any, Any, str, float]]:
60
- return sorted([element.description() for element in self.elements])
67
+ def description(self) -> Set[Tuple[Any, Any, str, float]]:
68
+ return set( # noqa: C414
69
+ sorted([element.description() for element in self.elements])
70
+ )
71
+
72
+
73
+ def get_internal_elements(space1_boundaries: List[SpaceBoundaries]) -> InternalElements:
74
+ elements = []
75
+ seen = set()
76
+ common_boundaries = []
77
+ for space_boundaries_ in space1_boundaries:
78
+ for space_boundaries__ in space1_boundaries:
79
+ space_1 = space_boundaries_.space
80
+ space_2 = space_boundaries__.space
81
+
82
+ if (
83
+ space_1.global_id == space_2.global_id
84
+ and (space_1.global_id, space_2.global_id) in seen
85
+ ):
86
+ continue
87
+ seen.update(
88
+ {
89
+ (space_1.global_id, space_2.global_id),
90
+ (space_2.global_id, space_1.global_id),
91
+ }
92
+ )
93
+ common_surface = space_1.bounding_box.intersect_faces(space_2.bounding_box)
94
+
95
+ for boundary in space_boundaries_.boundaries:
96
+ for boundary_ in space_boundaries__.boundaries:
97
+ if (
98
+ boundary.entity.GlobalId == boundary_.entity.GlobalId
99
+ and boundary.common_surface
100
+ and boundary_.common_surface
101
+ and common_surface
102
+ and (
103
+ boundary.common_surface.orientation
104
+ * common_surface.orientation
105
+ ).is_null()
106
+ and (
107
+ boundary_.common_surface.orientation
108
+ * common_surface.orientation
109
+ ).is_null()
110
+ ) and boundary.common_surface.orientation.dot(
111
+ boundary_.common_surface.orientation
112
+ ) < 0:
113
+ common_boundaries.extend([boundary, boundary_])
114
+ common_surface = sorted(
115
+ [boundary.common_surface, boundary_.common_surface],
116
+ key=lambda s: s.area,
117
+ )[0]
118
+ common_surface.exterior = False
119
+ elements.append(
120
+ IfcInternalElement(
121
+ spaces=[space_1, space_2],
122
+ element=boundary_.entity,
123
+ area=common_surface.area,
124
+ common_surface=common_surface,
125
+ )
126
+ )
127
+ for space_boundaries_ in space1_boundaries:
128
+ space_boundaries_.remove(common_boundaries)
129
+ return InternalElements(elements=list(set(elements)))
61
130
 
62
131
 
63
- class Building(BaseModelConfig):
132
+ class Building(BaseShow):
64
133
  name: str
65
134
  space_boundaries: List[SpaceBoundaries]
66
135
  ifc_file: file
67
136
  parent_folder: Path
68
137
  internal_elements: InternalElements = Field(default_factory=InternalElements)
138
+ constructions: Constructions
139
+
140
+ def get_boundaries(self, space_id: str) -> SpaceBoundaries:
141
+ return next(
142
+ sb for sb in self.space_boundaries if sb.space.global_id == space_id
143
+ )
144
+
145
+ def description(self) -> list[list[tuple[float, tuple[float, ...], Any, str]]]:
146
+ return sorted([sorted(b.description()) for b in self.space_boundaries])
147
+
148
+ def lines(self) -> List[Line]:
149
+ lines = []
150
+ for space_boundaries_ in [
151
+ *self.space_boundaries,
152
+ *self.internal_elements.elements,
153
+ ]:
154
+ lines += space_boundaries_.lines() # type: ignore
155
+ return lines
69
156
 
70
157
  @field_validator("name")
71
158
  @classmethod
@@ -86,6 +173,7 @@ class Building(BaseModelConfig):
86
173
  ifc_file = ifcopenshell.open(str(ifc_file_path))
87
174
  tree = initialize_tree(ifc_file)
88
175
  spaces = get_spaces(ifc_file)
176
+ constructions = Constructions.from_ifc(ifc_file)
89
177
  if selected_spaces_global_id:
90
178
  spaces = [
91
179
  space for space in spaces if space.GlobalId in selected_spaces_global_id
@@ -100,6 +188,7 @@ class Building(BaseModelConfig):
100
188
  ifc_file=ifc_file,
101
189
  parent_folder=ifc_file_path.parent,
102
190
  name=ifc_file_path.stem,
191
+ constructions=constructions,
103
192
  )
104
193
 
105
194
  @model_validator(mode="after")
@@ -108,58 +197,21 @@ class Building(BaseModelConfig):
108
197
  return self
109
198
 
110
199
  def get_adjacency(self) -> InternalElements:
111
- elements = []
112
- for space_boundaries_ in self.space_boundaries:
113
- for space_boundaries__ in self.space_boundaries:
114
- space_1 = space_boundaries_.space
115
- space_2 = space_boundaries__.space
116
- if space_1.global_id == space_2.global_id:
117
- continue
118
- common_surface = space_1.bounding_box.intersect_faces(
119
- space_2.bounding_box
120
- )
121
- for boundary in space_boundaries_.boundaries:
122
- for boundary_ in space_boundaries__.boundaries:
123
- if (
124
- boundary.entity.GlobalId == boundary_.entity.GlobalId
125
- and boundary.common_surface
126
- and boundary_.common_surface
127
- and common_surface
128
- and (
129
- boundary.common_surface.orientation
130
- * common_surface.orientation
131
- ).is_a_zero()
132
- and (
133
- boundary_.common_surface.orientation
134
- * common_surface.orientation
135
- ).is_a_zero()
136
- ) and boundary.common_surface.orientation.dot(
137
- boundary_.common_surface.orientation
138
- ) < 0:
139
- elements.append( # noqa: PERF401
140
- IfcInternalElement(
141
- spaces=[space_1, space_2],
142
- element=boundary_.entity,
143
- area=min(
144
- common_surface.area,
145
- boundary.common_surface.area,
146
- boundary_.common_surface.area,
147
- ),
148
- )
149
- )
150
- return InternalElements(elements=list(set(elements)))
200
+ return get_internal_elements(self.space_boundaries)
151
201
 
152
202
  @validate_call
153
- def create_model(
203
+ def create_network(
154
204
  self,
155
205
  library: Libraries = "Buildings",
156
206
  north_axis: Optional[Vector] = None,
157
- ) -> str:
207
+ ) -> Network:
158
208
  north_axis = north_axis or Vector(x=0, y=1, z=0)
159
209
  network = Network(name=self.name, library=Library.from_configuration(library))
160
210
  spaces = {
161
211
  space_boundary.space.global_id: space_boundary.model(
162
- self.internal_elements.internal_element_ids(), north_axis
212
+ self.internal_elements.internal_element_ids(),
213
+ north_axis,
214
+ self.constructions,
163
215
  )
164
216
  for space_boundary in self.space_boundaries
165
217
  }
@@ -173,13 +225,16 @@ class Building(BaseModelConfig):
173
225
  spaces[space_2.global_id],
174
226
  InternalElement(
175
227
  azimuth=10,
176
- construction=construction,
228
+ construction=default_construction,
177
229
  surface=internal_element.area,
178
230
  tilt=Tilt.wall,
179
231
  ),
180
232
  )
181
- return network.model() # type: ignore
233
+ return network
234
+
235
+ def get_model(self) -> str:
236
+ return str(self.create_network().model())
182
237
 
183
238
  def save_model(self, library: Libraries = "Buildings") -> None:
184
- model_ = self.create_model(library)
185
- Path(self.parent_folder.joinpath(f"{self.name}.mo")).write_text(model_)
239
+ model_ = self.create_network(library)
240
+ Path(self.parent_folder.joinpath(f"{self.name}.mo")).write_text(model_.model())