Skip to content

Commit 8ef4d04

Browse files
committed
moved piping to distribution_element module
1 parent c81882f commit 8ef4d04

File tree

2 files changed

+370
-393
lines changed

2 files changed

+370
-393
lines changed

src/bim2fem/ifcplus/api/distribution_element.py

Lines changed: 351 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -13,16 +13,359 @@
1313
import ifcopenshell.api.type
1414
import numpy as np
1515
import ifcopenshell.util.representation
16+
from typing import Literal
17+
import ifcopenshell.api.system
18+
import ifcopenshell.api.root
19+
import ifcopenshell.api.spatial
20+
import bim2fem.ifcplus.api.placement
21+
import bim2fem.ifcplus.api.geometry
22+
import bim2fem.ifcplus.api.profile
23+
import ifcopenshell.api.geometry
24+
import bim2fem.ifcplus.util.geometry
25+
import ifcopenshell.api.material
26+
import numpy as np
27+
import bim2fem.ifcplus.api.system
28+
import ifcopenshell.util.representation
29+
30+
31+
ELBOW_RADIUS_TYPE = Literal["LONG", "SHORT"]
32+
33+
34+
def create_elbow(
35+
horizontal_curve: bim2fem.ifcplus.util.geometry.HorizontalCurve,
36+
nominal_diameter: float,
37+
thickness: float,
38+
material: ifcopenshell.entity_instance,
39+
elbow: ifcopenshell.entity_instance | None = None,
40+
name: str | None = None,
41+
spatial_element: ifcopenshell.entity_instance | None = None,
42+
distribution_system: ifcopenshell.entity_instance | None = None,
43+
place_object_relative_to_parent: bool = False,
44+
add_shape_representation_to_ports: bool = False,
45+
) -> ifcopenshell.entity_instance:
46+
47+
ifc4_file = material.file
48+
49+
if elbow is None:
50+
elbow = ifcopenshell.api.root.create_entity(
51+
file=ifc4_file,
52+
ifc_class="IfcPipeFitting",
53+
name=name,
54+
predefined_type="JUNCTION",
55+
)
56+
57+
if isinstance(spatial_element, ifcopenshell.entity_instance):
58+
ifcopenshell.api.spatial.assign_container(
59+
file=ifc4_file,
60+
products=[elbow],
61+
relating_structure=spatial_element,
62+
)
63+
bim2fem.ifcplus.api.placement.edit_object_placement(
64+
product=elbow,
65+
place_object_relative_to_parent=True,
66+
)
67+
68+
if isinstance(distribution_system, ifcopenshell.entity_instance):
69+
ifcopenshell.api.system.assign_system(
70+
file=ifc4_file,
71+
products=[elbow],
72+
system=distribution_system,
73+
)
74+
75+
outer_radius = nominal_diameter / 2 + thickness / 2
76+
77+
revolved_area_solid = bim2fem.ifcplus.api.geometry.add_revolved_area_solid(
78+
ifc4_file=ifc4_file,
79+
profile=bim2fem.ifcplus.api.profile.add_parameterized_profile(
80+
ifc4_file=ifc4_file,
81+
profile_class="IfcCircleHollowProfileDef",
82+
dimensions=[outer_radius, thickness],
83+
check_for_duplicate=True,
84+
calculate_mechanical_properties=True,
85+
),
86+
central_angle_of_curvature=horizontal_curve.central_angle,
87+
center_of_curvature_in_object_xy_plane=(
88+
horizontal_curve.radius_of_curvature,
89+
0.0,
90+
),
91+
)
92+
93+
representation_type = ifcopenshell.util.representation.guess_type(
94+
items=[revolved_area_solid]
95+
)
96+
assert isinstance(representation_type, str)
97+
98+
shape_model = bim2fem.ifcplus.api.geometry.add_shape_model(
99+
ifc4_file=ifc4_file,
100+
shape_model_class="IfcShapeRepresentation",
101+
representation_identifier="Body",
102+
representation_type=representation_type,
103+
items=[revolved_area_solid],
104+
)
105+
ifcopenshell.api.geometry.assign_representation(
106+
file=ifc4_file,
107+
product=elbow,
108+
representation=shape_model,
109+
)
110+
111+
object_z_axis_in_global_coordinates = bim2fem.ifcplus.util.geometry.calculate_unit_direction_vector_between_two_points(
112+
p1=horizontal_curve.point_of_curvature,
113+
p2=horizontal_curve.point_of_intersection,
114+
)
115+
116+
object_x_axis_in_global_coordinates = bim2fem.ifcplus.util.geometry.calculate_unit_direction_vector_between_two_points(
117+
p1=horizontal_curve.point_of_curvature,
118+
p2=horizontal_curve.center_of_curvature,
119+
)
120+
121+
object_origin_in_global_coordinates = horizontal_curve.point_of_curvature
122+
123+
bim2fem.ifcplus.api.placement.edit_object_placement(
124+
product=elbow,
125+
repositioned_origin=object_origin_in_global_coordinates,
126+
repositioned_z_axis=object_z_axis_in_global_coordinates,
127+
repositioned_x_axis=object_x_axis_in_global_coordinates,
128+
place_object_relative_to_parent=place_object_relative_to_parent,
129+
)
130+
131+
ifcopenshell.api.material.assign_material(
132+
file=ifc4_file,
133+
products=[elbow],
134+
material=material,
135+
)
136+
137+
port1_origin_in_object_coordinates = (0.0, 0.0, 0.0)
138+
port1_z_axis_in_object_coordinates = (0.0, 0.0, 1.0)
139+
port1_x_axis_in_object_coordinates = (1.0, 0.0, 0.0)
140+
port1 = ifcopenshell.api.system.add_port(file=ifc4_file, element=elbow)
141+
port1.FlowDirection = "SINK"
142+
port1.PredefinedType = "PIPE"
143+
if isinstance(distribution_system, ifcopenshell.entity_instance):
144+
port1.SystemType = distribution_system.PredefinedType
145+
bim2fem.ifcplus.api.placement.edit_object_placement(
146+
product=port1,
147+
place_object_relative_to_parent=False,
148+
)
149+
port1.ObjectPlacement.PlacementRelTo = elbow.ObjectPlacement
150+
bim2fem.ifcplus.api.placement.edit_object_placement(
151+
product=port1,
152+
repositioned_origin=port1_origin_in_object_coordinates,
153+
repositioned_z_axis=port1_z_axis_in_object_coordinates,
154+
repositioned_x_axis=port1_x_axis_in_object_coordinates,
155+
place_object_relative_to_parent=True,
156+
)
157+
158+
radius_of_curvature = horizontal_curve.radius_of_curvature
159+
central_angle = horizontal_curve.central_angle
160+
port2_origin_in_object_coordinates = (
161+
float(radius_of_curvature - radius_of_curvature * np.cos(central_angle)),
162+
0.0,
163+
float(radius_of_curvature * np.sin(central_angle)),
164+
)
165+
port2_z_axis_in_object_coordinates = (
166+
float(np.sin(horizontal_curve.central_angle)),
167+
0.0,
168+
float(np.cos(horizontal_curve.central_angle)),
169+
)
170+
port2_x_axis_in_object_coordinates = (
171+
float(np.cos(horizontal_curve.central_angle)),
172+
0.0,
173+
float(-1 * np.sin(horizontal_curve.central_angle)),
174+
)
175+
port2 = ifcopenshell.api.system.add_port(file=ifc4_file, element=elbow)
176+
port2.FlowDirection = "SOURCE"
177+
port2.PredefinedType = "PIPE"
178+
if isinstance(distribution_system, ifcopenshell.entity_instance):
179+
port2.SystemType = distribution_system.PredefinedType
180+
bim2fem.ifcplus.api.placement.edit_object_placement(
181+
product=port2,
182+
place_object_relative_to_parent=False,
183+
)
184+
port2.ObjectPlacement.PlacementRelTo = elbow.ObjectPlacement
185+
bim2fem.ifcplus.api.placement.edit_object_placement(
186+
product=port2,
187+
repositioned_origin=port2_origin_in_object_coordinates,
188+
repositioned_z_axis=port2_z_axis_in_object_coordinates,
189+
repositioned_x_axis=port2_x_axis_in_object_coordinates,
190+
place_object_relative_to_parent=True,
191+
)
192+
193+
if add_shape_representation_to_ports:
194+
bim2fem.ifcplus.api.system.add_shape_representation_to_distribution_ports(
195+
ports=[port1, port2],
196+
arrow_size=nominal_diameter * 0.10,
197+
)
198+
199+
return elbow
200+
201+
202+
def create_pipe_segment(
203+
p1: tuple[float, float, float],
204+
p2: tuple[float, float, float],
205+
nominal_diameter: float,
206+
thickness: float,
207+
material: ifcopenshell.entity_instance,
208+
pipe_segment: ifcopenshell.entity_instance | None = None,
209+
name: str | None = None,
210+
spatial_element: ifcopenshell.entity_instance | None = None,
211+
distribution_system: ifcopenshell.entity_instance | None = None,
212+
place_object_relative_to_parent: bool = False,
213+
add_shape_representation_to_ports: bool = False,
214+
) -> ifcopenshell.entity_instance:
215+
216+
ifc4_file = material.file
217+
218+
if pipe_segment is None:
219+
pipe_segment = ifcopenshell.api.root.create_entity(
220+
file=ifc4_file,
221+
ifc_class="IfcPipeSegment",
222+
name=name,
223+
predefined_type="NOTDEFINED",
224+
)
16225

226+
if isinstance(spatial_element, ifcopenshell.entity_instance):
227+
ifcopenshell.api.spatial.assign_container(
228+
file=ifc4_file,
229+
products=[pipe_segment],
230+
relating_structure=spatial_element,
231+
)
232+
bim2fem.ifcplus.api.placement.edit_object_placement(
233+
product=pipe_segment,
234+
place_object_relative_to_parent=True,
235+
)
236+
237+
if isinstance(distribution_system, ifcopenshell.entity_instance):
238+
ifcopenshell.api.system.assign_system(
239+
file=ifc4_file,
240+
products=[pipe_segment],
241+
system=distribution_system,
242+
)
243+
244+
object_z_axis_in_global_coordinates = np.array(p2) - np.array(p1)
245+
angle_between_local_and_global_z_axes = (
246+
bim2fem.ifcplus.util.geometry.calculate_angle_between_two_vectors(
247+
vector1=tuple(object_z_axis_in_global_coordinates.tolist()),
248+
vector2=(0.0, 0.0, 1.0),
249+
)
250+
)
251+
angle_between_local_and_global_z_axes_is_zero = (
252+
angle_between_local_and_global_z_axes <= 1e-4
253+
)
254+
angle_between_local_and_global_z_axes_is_pi = (
255+
abs(angle_between_local_and_global_z_axes - np.pi) <= 1e-4
256+
)
257+
if (
258+
angle_between_local_and_global_z_axes_is_zero
259+
or angle_between_local_and_global_z_axes_is_pi
260+
):
261+
object_y_axis_in_global_coordinates = np.array([0.0, 1.0, 0.0])
262+
else:
263+
object_y_axis_in_global_coordinates = np.cross(
264+
np.array([0.0, 0.0, 1.0]), object_z_axis_in_global_coordinates
265+
)
266+
object_x_axis_in_global_coordinates = np.cross(
267+
object_y_axis_in_global_coordinates, object_z_axis_in_global_coordinates
268+
)
269+
270+
length = float(np.linalg.norm(object_z_axis_in_global_coordinates))
271+
272+
outer_radius = nominal_diameter / 2 + thickness / 2
273+
274+
extruded_area_solid = bim2fem.ifcplus.api.geometry.add_extruded_area_solid(
275+
ifc4_file=ifc4_file,
276+
profile=bim2fem.ifcplus.api.profile.add_parameterized_profile(
277+
ifc4_file=ifc4_file,
278+
profile_class="IfcCircleHollowProfileDef",
279+
dimensions=[outer_radius, thickness],
280+
check_for_duplicate=True,
281+
calculate_mechanical_properties=True,
282+
),
283+
extrusion_depth=length,
284+
)
285+
286+
representation_type = ifcopenshell.util.representation.guess_type(
287+
items=[extruded_area_solid]
288+
)
289+
assert isinstance(representation_type, str)
290+
291+
shape_model = bim2fem.ifcplus.api.geometry.add_shape_model(
292+
ifc4_file=ifc4_file,
293+
shape_model_class="IfcShapeRepresentation",
294+
representation_identifier="Body",
295+
representation_type=representation_type,
296+
items=[extruded_area_solid],
297+
)
298+
ifcopenshell.api.geometry.assign_representation(
299+
file=ifc4_file,
300+
product=pipe_segment,
301+
representation=shape_model,
302+
)
303+
304+
object_origin_in_global_coordinates = p1
305+
306+
bim2fem.ifcplus.api.placement.edit_object_placement(
307+
product=pipe_segment,
308+
repositioned_origin=object_origin_in_global_coordinates,
309+
repositioned_z_axis=object_z_axis_in_global_coordinates,
310+
repositioned_x_axis=object_x_axis_in_global_coordinates,
311+
place_object_relative_to_parent=place_object_relative_to_parent,
312+
)
313+
314+
ifcopenshell.api.material.assign_material(
315+
file=ifc4_file,
316+
products=[pipe_segment],
317+
material=material,
318+
)
319+
320+
port1_origin_in_object_coordinates = (0.0, 0.0, 0.0)
321+
port1_z_axis_in_object_coordinates = (0.0, 0.0, 1.0)
322+
port1_x_axis_in_object_coordinates = (1.0, 0.0, 0.0)
323+
port1 = ifcopenshell.api.system.add_port(file=ifc4_file, element=pipe_segment)
324+
port1.FlowDirection = "SINK"
325+
port1.PredefinedType = "PIPE"
326+
if isinstance(distribution_system, ifcopenshell.entity_instance):
327+
port1.SystemType = distribution_system.PredefinedType
328+
bim2fem.ifcplus.api.placement.edit_object_placement(
329+
product=port1,
330+
place_object_relative_to_parent=False,
331+
)
332+
port1.ObjectPlacement.PlacementRelTo = pipe_segment.ObjectPlacement
333+
bim2fem.ifcplus.api.placement.edit_object_placement(
334+
product=port1,
335+
repositioned_origin=port1_origin_in_object_coordinates,
336+
repositioned_z_axis=port1_z_axis_in_object_coordinates,
337+
repositioned_x_axis=port1_x_axis_in_object_coordinates,
338+
place_object_relative_to_parent=True,
339+
)
340+
341+
port2_origin_in_object_coordinates = (0.0, 0.0, length)
342+
port2_z_axis_in_object_coordinates = (0.0, 0.0, 1.0)
343+
port2_x_axis_in_object_coordinates = (1.0, 0.0, 0.0)
344+
port2 = ifcopenshell.api.system.add_port(file=ifc4_file, element=pipe_segment)
345+
port2.FlowDirection = "SOURCE"
346+
port2.PredefinedType = "PIPE"
347+
if isinstance(distribution_system, ifcopenshell.entity_instance):
348+
port2.SystemType = distribution_system.PredefinedType
349+
bim2fem.ifcplus.api.placement.edit_object_placement(
350+
product=port2,
351+
place_object_relative_to_parent=False,
352+
)
353+
port2.ObjectPlacement.PlacementRelTo = pipe_segment.ObjectPlacement
354+
bim2fem.ifcplus.api.placement.edit_object_placement(
355+
product=port2,
356+
repositioned_origin=port2_origin_in_object_coordinates,
357+
repositioned_z_axis=port2_z_axis_in_object_coordinates,
358+
repositioned_x_axis=port2_x_axis_in_object_coordinates,
359+
place_object_relative_to_parent=True,
360+
)
361+
362+
if add_shape_representation_to_ports:
363+
bim2fem.ifcplus.api.system.add_shape_representation_to_distribution_ports(
364+
ports=[port1, port2],
365+
arrow_size=nominal_diameter * 0.10,
366+
)
17367

18-
PREDEFINED_DISTRIBUTION_ELEMENTS = [
19-
"MAKEUP_AIR_UNIT",
20-
"MOTORIZED_VALVE",
21-
"GENERIC_AIR_FILTER",
22-
"AIR_FILTRATION_CONTAINMENT_HOUSING",
23-
"HPRS_EXHAUST_FAN",
24-
"STACK",
25-
]
368+
return pipe_segment
26369

27370

28371
def create_make_up_air_unit(

0 commit comments

Comments
 (0)