pset.add_pset - support ifc2x3 material/profile psets

1) For psets there was an issue - it was instantiating abstract IfcProfileProperties
2) For materials it was only able to instantiate IfcExtendedMaterialProperties though there are other pset types too in ifc2x3.
This commit is contained in:
Andrej730
2024-09-03 11:56:24 +05:00
parent 88b861737c
commit d2bc6f9509
2 changed files with 55 additions and 15 deletions
@@ -19,9 +19,15 @@
import ifcopenshell
import ifcopenshell.api.owner
import ifcopenshell.guid
from typing import Optional
def add_pset(file: ifcopenshell.file, product: ifcopenshell.entity_instance, name: str) -> ifcopenshell.entity_instance:
def add_pset(
file: ifcopenshell.file,
product: ifcopenshell.entity_instance,
name: str,
ifc2x3_subclass: Optional[str] = None,
) -> ifcopenshell.entity_instance:
"""Adds a new property set to a product
Products, such as physical objects or types in IFC may have properties
@@ -54,18 +60,21 @@ def add_pset(file: ifcopenshell.file, product: ifcopenshell.entity_instance, nam
data, rather than arbitrary metadata.
:param product: The IfcObject that you want to assign a property set to.
:type product: ifcopenshell.entity_instance
:param name: The name of the property set. Property sets that are
standardised by buildingSMART typically have a prefix of "Pset_",
like "Pset_WallCommon". If you create your own, you must not use
that prefix. It is recommended to use your own prefix tailored to
your project, company, or local government requirement.
:type name: str
:param ifc2x3_subclass: IFC2X3 subclass for material or profile properties.
In IFC2X3 IfcProfileProperties and IfcMaterialProperties are abstract
so you need one of their subclasses to instantiate them.
By default, for profile will be created IfcGeneralProfileProperties
and for material - IfcExtendedMaterialProperties.
Will have no effect in >=IFC4.
:raises TypeError: If `product` class doesn't support adding a pset.
:return: The newly created IfcPropertySet
:rtype: ifcopenshell.entity_instance
Example:
@@ -83,6 +92,7 @@ def add_pset(file: ifcopenshell.file, product: ifcopenshell.entity_instance, nam
ifcopenshell.api.pset.edit_pset(model, pset=pset, properties={"FireRating": "2HR"})
"""
settings = {"product": product, "name": name}
is_ifc2x3 = file.schema == "IFC2X3"
if settings["product"].is_a("IfcObject") or settings["product"].is_a("IfcContext"):
for rel in settings["product"].IsDefinedBy or []:
@@ -126,24 +136,23 @@ def add_pset(file: ifcopenshell.file, product: ifcopenshell.entity_instance, nam
return pset
# in IFC2X3 IfcMaterialDefinition not yet existed
elif settings["product"].is_a("IfcMaterialDefinition") or settings["product"].is_a("IfcMaterial"):
kwargs = {"Material": settings["product"]}
if file.schema == "IFC2X3":
ifc_class = "IfcExtendedMaterialProperties"
ifc_class = ifc2x3_subclass or "IfcExtendedMaterialProperties"
definitions = (d for d in file.by_type("IfcMaterialProperties") if d.Material == settings["product"])
if ifc_class == "IfcExtendedMaterialProperties":
kwargs["Name"] = settings["name"]
else:
ifc_class = "IfcMaterialProperties"
definitions = settings["product"].HasProperties
kwargs["Name"] = settings["name"]
for definition in definitions:
# In IFC2X3 not all IfcMaterialProperties has Name
if getattr(definition, "Name") == settings["name"]:
if getattr(definition, "Name", None) == settings["name"]:
return definition
return file.create_entity(
ifc_class,
**{
"Name": settings["name"],
"Material": settings["product"],
},
)
return file.create_entity(ifc_class, **kwargs)
elif settings["product"].is_a("IfcProfileDef"):
# in IFC2X3 IfcProfileProperties doesn't have Name and we cannot identify them
if file.schema != "IFC2X3":
@@ -156,6 +165,10 @@ def add_pset(file: ifcopenshell.file, product: ifcopenshell.entity_instance, nam
if file.schema != "IFC2X3":
kwargs["Name"] = settings["name"]
return file.create_entity("IfcProfileProperties", **kwargs)
if is_ifc2x3:
ifc_class = ifc2x3_subclass or "IfcGeneralProfileProperties"
else:
ifc_class = "IfcProfileProperties"
return file.create_entity(ifc_class, **kwargs)
raise TypeError(f"Class '{settings['product'].is_a(True)}' doesn't support adding a property set.")
@@ -38,17 +38,23 @@ class TestAddPset(test.bootstrap.IFC4):
assert "Pset_WallCommon" in ifcopenshell.util.element.get_psets(element)
def test_adding_a_pset_to_a_material(self):
is_ifc2x3 = self.file.schema == "IFC2X3"
material = ifcopenshell.api.material.add_material(self.file)
pset = ifcopenshell.api.pset.add_pset(self.file, product=material, name="Pset_MaterialCommon")
assert pset.is_a("IfcMaterialProperties")
if is_ifc2x3:
assert pset.is_a() == "IfcExtendedMaterialProperties"
assert pset.Name == "Pset_MaterialCommon"
assert pset.Material == material
def test_adding_a_pset_to_a_profile(self):
is_ifc2x3 = self.file.schema == "IFC2X3"
profile = ifcopenshell.api.profile.add_parameterized_profile(self.file, ifc_class="IfcCircleProfileDef")
pset = ifcopenshell.api.pset.add_pset(self.file, product=profile, name="Pset_ProfileMechanical")
assert pset.is_a("IfcProfileProperties")
if self.file.schema != "IFC2X3":
if is_ifc2x3:
assert pset.is_a() == "IfcGeneralProfileProperties"
else:
assert pset.Name == "Pset_ProfileMechanical"
assert pset.ProfileDefinition == profile
@@ -65,3 +71,24 @@ class TestAddPsetIFC2X3(test.bootstrap.IFC2X3, TestAddPset):
pset = ifcopenshell.api.pset.add_pset(self.file, product=element, name="Custom_Pset")
assert pset.is_a("IfcPropertySet")
assert "Custom_Pset" in ifcopenshell.util.element.get_psets(element)
def test_adding_a_pset_subclass_to_a_profile(self):
profile = ifcopenshell.api.profile.add_parameterized_profile(self.file, ifc_class="IfcCircleProfileDef")
pset = ifcopenshell.api.pset.add_pset(
self.file,
product=profile,
name="Pset_ProfileMechanical",
ifc2x3_subclass="IfcStructuralSteelProfileProperties",
)
assert pset.is_a() == "IfcStructuralSteelProfileProperties"
def test_adding_a_material_subclass_to_a_profile(self):
is_ifc2x3 = self.file.schema == "IFC2X3"
material = ifcopenshell.api.material.add_material(self.file)
pset = ifcopenshell.api.pset.add_pset(
self.file,
product=material,
name="Pset_MaterialCommon",
ifc2x3_subclass="IfcFuelProperties",
)
assert pset.is_a() == "IfcFuelProperties"