|
13 | 13 | import ifcopenshell.api.type |
14 | 14 | import numpy as np |
15 | 15 | 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 | + ) |
16 | 225 |
|
| 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 | + ) |
17 | 367 |
|
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 |
26 | 369 |
|
27 | 370 |
|
28 | 371 | def create_make_up_air_unit( |
|
0 commit comments