Library for US AISC15 Steel Profiles #3203

This commit is contained in:
Andrej730
2023-07-07 12:43:12 +05:00
parent f2dd6a6fc1
commit 01ebb649d9
4 changed files with 47215 additions and 16587 deletions
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -24,6 +24,9 @@ import ifcopenshell.api
import boltspy as bolts import boltspy as bolts
from math import cos, pi from math import cos, pi
from pathlib import Path from pathlib import Path
from mathutils import Vector
V = lambda *x: Vector([float(i) for i in x])
def create_simple_2dcurve(coords, fillets, fillet_radius, closed=True, ifc_file=None): def create_simple_2dcurve(coords, fillets, fillet_radius, closed=True, ifc_file=None):
""" """
@@ -135,9 +138,9 @@ def create_z_profile_lips_curve(ifc_file, FirstFlangeWidth, SecondFlangeWidth, D
return ifc_curve return ifc_curve
class LibraryGenerator: class LibraryGenerator:
def generate(self, parse_profiles_type="EU", output_filename="IFC4 EU Steel.ifc"): def generate(self, parse_profiles_type="EU", output_filename="IFC4 EU Steel.ifc"):
print(f'Generating {parse_profiles_type} steel library "{output_filename}"')
ifcopenshell.api.pre_listeners = {} ifcopenshell.api.pre_listeners = {}
ifcopenshell.api.post_listeners = {} ifcopenshell.api.post_listeners = {}
@@ -153,41 +156,64 @@ class LibraryGenerator:
ifcopenshell.api.run( ifcopenshell.api.run(
"project.assign_declaration", self.file, definition=self.library, relating_context=self.project "project.assign_declaration", self.file, definition=self.library, relating_context=self.project
) )
unit = ifcopenshell.api.run("unit.add_si_unit", self.file, unit_type="LENGTHUNIT", prefix="MILLI") dim_exponents = self.file.createIfcDimensionalExponents(0, 0, 0, 0, 0, 0, 0)
ifcopenshell.api.run("unit.assign_unit", self.file, units=[unit]) length_unit = ifcopenshell.api.run("unit.add_si_unit", self.file, unit_type="LENGTHUNIT", prefix="MILLI")
# define angle unit to use degrees for IfcPlaneAngleMeasure:
# https://ifc43-docs.standards.buildingsmart.org/IFC/RELEASE/IFC4x3/HTML/lexical/IfcPlaneAngleMeasure.htm
angle_unit = ifcopenshell.api.run("unit.add_si_unit", self.file, unit_type="PLANEANGLEUNIT")
value_component = self.file.createIfcReal(pi/180)
angle_unit = self.file.createIfcMeasureWithUnit(ValueComponent=value_component, UnitComponent=angle_unit)
angle_unit = self.file.createIfcConversionBasedUnit(Name="degree", Dimensions=dim_exponents, UnitType="PLANEANGLEUNIT", ConversionFactor=angle_unit)
ifcopenshell.api.run("unit.assign_unit", self.file, units=[length_unit, angle_unit])
self.material = ifcopenshell.api.run("material.add_material", self.file, name="Unknown") self.material = ifcopenshell.api.run("material.add_material", self.file, name="Unknown")
# parameters could be optional (example: welded i-beams don't have FilletRadius) # NOTE: parameters could be optional (example: welded i-beams don't have FilletRadius)
profiles_translation = { profiles_translation = {
"profile_i": ("IfcIShapeProfileDef", {"tw": "WebThickness", "tf": "FlangeThickness", "h": "OverallDepth", "b": "OverallWidth", "r": "FilletRadius"}), "profile_i": ("IfcIShapeProfileDef", {"tw": "WebThickness", "tf": "FlangeThickness", "h": "OverallDepth", "b": "OverallWidth", "r": "FilletRadius", "sf": "FlangeSlope", "r1": "FilletRadius", "r2": "FlangeEdgeRadius"}),
"profile_t": ("IfcTShapeProfileDef", {"tw": "WebThickness", "tf": "FlangeThickness", "h": "Depth", "b": "FlangeWidth", "r": "FilletRadius", "r1": "FilletRadius", "r2": "FlangeEdgeRadius"}),
"profile_z": ("IfcZShapeProfileDef", {"tw": "WebThickness", "tf": "FlangeThickness", "h": "Depth", "c1": "FlangeWidth"}), "profile_z": ("IfcZShapeProfileDef", {"tw": "WebThickness", "tf": "FlangeThickness", "h": "Depth", "c1": "FlangeWidth"}),
"profile_c": ("IfcUShapeProfileDef", {"tw": "WebThickness", "tf": "FlangeThickness", "h": "Depth", "b": "FlangeWidth", "r": "FilletRadius"}), "profile_z_lips": ("IfcArbitraryClosedProfileDef", {"t": "WallThickness", "c1": "FirstFlangeWidth", "c2": "SecondFlangeWidth", "h": "Depth", "r": "FilletRadius", "ll": "Girth"}),
"profile_c": ("IfcUShapeProfileDef", {"tw": "WebThickness", "tf": "FlangeThickness", "h": "Depth", "b": "FlangeWidth", "r": "FilletRadius", "sf": "FlangeSlope", "r1": "FilletRadius", "r2": "EdgeRadius"}),
"profile_c_lips": ("IfcCShapeProfileDef", {"t": "WallThickness", "b": "Width", "h": "Depth", "ll": "Girth", "r": "InternalFilletRadius"}),
"profile_l*_equal": ("IfcLShapeProfileDef", {"a": "Depth", "t": "Thickness", "r1": "FilletRadius", "r2": "EdgeRadius"}), "profile_l*_equal": ("IfcLShapeProfileDef", {"a": "Depth", "t": "Thickness", "r1": "FilletRadius", "r2": "EdgeRadius"}),
"profile_l*_unequal": ("IfcLShapeProfileDef", {"a": "Depth", "b": "Width", "t": "Thickness", "r1": "FilletRadius", "r2": "EdgeRadius"}), "profile_l*_unequal": ("IfcLShapeProfileDef", {"a": "Depth", "b": "Width", "t": "Thickness", "r1": "FilletRadius", "r2": "EdgeRadius"}),
"profile_l*lbeam_l_imp": ("IfcLShapeProfileDef", {"a": "Depth", "b": "Width", "t": "Thickness", "r1": "FilletRadius", "r2": "EdgeRadius"}),
"profile_l*lbeam_2l": ("IfcLShapeProfileDef", {"a": "Depth", "b": "Width", "t": "Thickness", "g": "ProfilesGap"}),
"profile_hollow*_circle": ("IfcCircleHollowProfileDef", {"t": "WallThickness", "D": "Radius"}), "profile_hollow*_circle": ("IfcCircleHollowProfileDef", {"t": "WallThickness", "D": "Radius"}),
"profile_hollow*pipe_imp": ("IfcCircleHollowProfileDef", {"t": "WallThickness", "D": "Radius"}),
"profile_hollow*_square": ("IfcRectangleHollowProfileDef", {"t": "WallThickness", "b": "XDim", "ri": "InnerFilletRadius", "ro": "OuterFilletRadius"}), "profile_hollow*_square": ("IfcRectangleHollowProfileDef", {"t": "WallThickness", "b": "XDim", "ri": "InnerFilletRadius", "ro": "OuterFilletRadius"}),
"profile_hollow*_rectangular": ("IfcRectangleHollowProfileDef", {"t": "WallThickness", "b": "XDim", "h": "YDim", "ri": "InnerFilletRadius", "ro": "OuterFilletRadius"}), "profile_hollow*_rectangular": ("IfcRectangleHollowProfileDef", {"t": "WallThickness", "b": "XDim", "h": "YDim", "ri": "InnerFilletRadius", "ro": "OuterFilletRadius"}),
"profile_c_lips": ("IfcCShapeProfileDef", {"t": "WallThickness", "b": "Width", "h": "Depth", "ll": "Girth", "r": "InternalFilletRadius"}),
"profile_z_lips": ("IfcArbitraryClosedProfileDef", {"t": "WallThickness", "c1": "FirstFlangeWidth", "c2": "SecondFlangeWidth", "h": "Depth", "r": "FilletRadius", "ll": "Girth"}),
} }
US_profiles = ("ibeam_w_imp", ) processed_profiles = set()
if parse_profiles_type == "AU": def bolt_class_filter(x):
bolt_class_filter = lambda x: "bluescope" in x.id identified_type = ""
elif parse_profiles_type == "EU":
bolt_class_filter = lambda x: "bluescope" not in x.id and x.id not in US_profiles if "bluescope" in x.id:
elif parse_profiles_type == "US": identified_type = "AU"
bolt_class_filter = lambda x: x.id in US_profiles elif x.id.endswith("_imp"):
else: identified_type = "US"
bolt_class_filter = lambda x: True else:
identified_type = "EU"
return parse_profiles_type == identified_type
for prof_type in profiles_translation: for prof_type in profiles_translation:
ifc_profile_name, ifc_params_translation = profiles_translation[prof_type] ifc_profile_name, ifc_params_translation = profiles_translation[prof_type]
# prof_keyword == "" if there is no "*"
col_name, _, prof_keyword = prof_type.partition("*") col_name, _, prof_keyword = prof_type.partition("*")
bolt_col = bolts.repo.collections[col_name] bolt_col = bolts.repo.collections[col_name]
for bolt_class in bolts.repo.collection_classes.get_dsts(bolt_col): for bolt_class in bolts.repo.collection_classes.get_dsts(bolt_col):
if bolt_class.id in processed_profiles:
continue
if not bolt_class_filter(bolt_class): if not bolt_class_filter(bolt_class):
continue continue
@@ -199,6 +225,8 @@ class LibraryGenerator:
# like hollow_generic_square # like hollow_generic_square
continue continue
print(f'Processing {bolt_class.id}')
bolts_cols_original = bolt_class.parameters.tables[0].columns bolts_cols_original = bolt_class.parameters.tables[0].columns
bolts_cols = [ifc_params_translation.get(c, "unused") for c in bolts_cols_original] bolts_cols = [ifc_params_translation.get(c, "unused") for c in bolts_cols_original]
@@ -207,7 +235,8 @@ class LibraryGenerator:
data = data.copy() data = data.copy()
for i in range(len(units)): for i in range(len(units)):
unit = units[i] unit = units[i]
assert unit in ("Length (in)", "Length (mm)") assert unit in ("Length (in)", "Length (mm)", "Angle (deg)"), f"Unit {unit} is not supported"
if unit != "Length (in)": if unit != "Length (in)":
continue continue
for profile in data: for profile in data:
@@ -225,23 +254,34 @@ class LibraryGenerator:
if prof_type == "profile_hollow*_square": if prof_type == "profile_hollow*_square":
ifc_params["YDim"] = ifc_params["XDim"] ifc_params["YDim"] = ifc_params["XDim"]
elif prof_type == "profile_hollow*_circle": elif ifc_profile_name == "IfcCircleHollowProfileDef":
# by default bolts provides diameter, so we need to convert it to radius # by default bolts provides diameter, so we need to convert it to radius
ifc_params["Radius"] /= 2 ifc_params["Radius"] /= 2
elif prof_type == "profile_z_lips": elif prof_type == "profile_z_lips":
ifc_curve = create_z_profile_lips_curve(self.file, **ifc_params) ifc_curve = create_z_profile_lips_curve(self.file, **ifc_params)
ifc_params = {"OuterCurve": ifc_curve} ifc_params = {"OuterCurve": ifc_curve}
elif prof_type == "profile_l*lbeam_2l":
profiles_gap = ifc_params["ProfilesGap"]
del ifc_params["ProfilesGap"]
# profile is setup by type of profile and by supplying it's parameters # profile is setup by type of profile and by supplying it's parameters
# ProfileType stays AREA # ProfileType stays AREA
profile = self.file.create_entity(ifc_profile_name, ProfileName=prof_name, ProfileType="AREA", **ifc_params) profile = self.file.create_entity(ifc_profile_name, ProfileName=prof_name, ProfileType="AREA", **ifc_params)
if prof_type == "profile_l*lbeam_2l":
profile.ProfileName = None # to avoid name confusion
mode = "SLBB" if prof_name.endswith("_SLBB") else "LLBB"
profile = self.create_double_l_profile(profile, prof_name, profiles_gap, mode)
# building profiles for each of 3 types # building profiles for each of 3 types
self.create_profile_type("IfcBeamType", prof_name, profile) self.create_profile_type("IfcBeamType", prof_name, profile)
self.create_profile_type("IfcMemberType", prof_name, profile) self.create_profile_type("IfcMemberType", prof_name, profile)
self.create_profile_type("IfcColumnType", prof_name, profile) self.create_profile_type("IfcColumnType", prof_name, profile)
processed_profiles.add(bolt_class.id)
self.file.write(output_filename) self.file.write(output_filename)
print('-----------------------')
def create_profile_type(self, ifc_class, name, profile): def create_profile_type(self, ifc_class, name, profile):
element = ifcopenshell.api.run("root.create_entity", self.file, ifc_class=ifc_class, name=name) element = ifcopenshell.api.run("root.create_entity", self.file, ifc_class=ifc_class, name=name)
@@ -254,6 +294,49 @@ class LibraryGenerator:
ifcopenshell.api.run("material.assign_profile", self.file, material_profile=material_profile, profile=profile) ifcopenshell.api.run("material.assign_profile", self.file, material_profile=material_profile, profile=profile)
ifcopenshell.api.run("project.assign_declaration", self.file, definition=element, relating_context=self.library) ifcopenshell.api.run("project.assign_declaration", self.file, definition=element, relating_context=self.library)
def create_double_l_profile(self, profile, resulting_profile_name=None, profiles_gap=0, mode = "LLBB"):
def create_derived_profile(profile, mirrored=False):
"""
LLBB mode = long legs back-to-back
SLBB mode = short legs back-to-back
"""
derived_profile = self.file.createIfcDerivedProfileDef(
ParentProfile=profile,
Operator=self.file.createIfcCartesianTransformationOperator2D(),
ProfileType=profile.ProfileType
)
transform = derived_profile.Operator
transform.LocalOrigin = self.file.createIfcCartesianPoint()
if mode == "LLBB":
offset = profile.Depth/2 + profiles_gap
if mirrored:
transform.Axis1 = self.file.createIfcDirection(V(0, 1))
transform.Axis2 = self.file.createIfcDirection(V(-1, 0))
transform.LocalOrigin.Coordinates = V(-offset, 0)
else:
transform.LocalOrigin.Coordinates = V(offset, 0)
transform.Axis1 = self.file.createIfcDirection(V(0, 1))
transform.Axis2 = self.file.createIfcDirection(V(1, 0))
elif mode == "SLBB":
offset = profile.Width/2 + profiles_gap
if mirrored:
transform.Axis1 = self.file.createIfcDirection(V(-1, 0))
transform.LocalOrigin.Coordinates = V(-offset, 0)
else:
transform.LocalOrigin.Coordinates = V(offset, 0)
return derived_profile
composite_profile = self.file.createIfcCompositeProfileDef(Profiles=[
create_derived_profile(profile),
create_derived_profile(profile, mirrored=True)
],
ProfileType = profile.ProfileType
)
composite_profile.ProfileName = resulting_profile_name
return composite_profile
if __name__ == "__main__": if __name__ == "__main__":
path = Path(__file__).parents[1] / "blenderbim/bim/data/libraries" path = Path(__file__).parents[1] / "blenderbim/bim/data/libraries"