api.project.append_asset deduplicate material sets

When appending a wall type and a slab type in turn, if their material
layer sets have the same name then the slab type would have a wall
construction. Now the material sets are compared before reusing an
existing material set.
This commit is contained in:
Bruno Postle
2026-01-04 11:36:05 +00:00
parent db623f4e73
commit a17b0604e1
@@ -243,6 +243,59 @@ class Usecase:
except RuntimeError:
return None
def material_sets_are_equal(self, set1: ifcopenshell.entity_instance, set2: ifcopenshell.entity_instance) -> bool:
"""Check if two material sets are structurally equivalent."""
if set1.is_a() != set2.is_a():
return False
ifc_class = set1.is_a()
if ifc_class == "IfcMaterialLayerSet":
layers1 = set1.MaterialLayers or []
layers2 = set2.MaterialLayers or []
if len(layers1) != len(layers2):
return False
for l1, l2 in zip(layers1, layers2):
if (l1.Material is None) != (l2.Material is None):
return False
if l1.Material and l1.Material.Name != l2.Material.Name:
return False
if l1.LayerThickness != l2.LayerThickness:
return False
elif ifc_class == "IfcMaterialConstituentSet":
constituents1 = set1.MaterialConstituents or []
constituents2 = set2.MaterialConstituents or []
if len(constituents1) != len(constituents2):
return False
for c1, c2 in zip(constituents1, constituents2):
if (c1.Material is None) != (c2.Material is None):
return False
if c1.Material and c1.Material.Name != c2.Material.Name:
return False
if c1.Name != c2.Name:
return False
elif ifc_class == "IfcMaterialProfileSet":
profiles1 = set1.MaterialProfiles or []
profiles2 = set2.MaterialProfiles or []
if len(profiles1) != len(profiles2):
return False
for p1, p2 in zip(profiles1, profiles2):
if (p1.Material is None) != (p2.Material is None):
return False
if p1.Material and p1.Material.Name != p2.Material.Name:
return False
if (p1.Profile is None) != (p2.Profile is None):
return False
if p1.Profile:
profile_name1 = getattr(p1.Profile, "ProfileName", None)
profile_name2 = getattr(p2.Profile, "ProfileName", None)
if profile_name1 != profile_name2:
return False
return True
def get_existing_element(self, element: ifcopenshell.entity_instance) -> Union[ifcopenshell.entity_instance, None]:
"""Get existing element for a library element.
@@ -270,7 +323,11 @@ class Usecase:
material_set_name = getattr(element, name_attr)
if material_set_name is None:
return
return next((e for e in self.file.by_type(ifc_class) if getattr(e, name_attr) == material_set_name), None)
for candidate in self.file.by_type(ifc_class):
if getattr(candidate, name_attr) == material_set_name:
if self.material_sets_are_equal(element, candidate):
return candidate
return None
elif element.is_a("IfcProfileDef"):
profile_name = element.ProfileName
@@ -665,12 +722,11 @@ class Usecase:
name_attr = "LayerSetName" if ifc_class == "IfcMaterialLayerSet" else "Name"
material_set_name = getattr(element, name_attr)
if material_set_name is not None:
existing_material_set = next(
(e for e in ifc_file.by_type(ifc_class) if getattr(e, name_attr) == material_set_name), None
)
if existing_material_set is not None:
reuse_identities[element_identity] = existing_material_set
return existing_material_set
for candidate in ifc_file.by_type(ifc_class):
if getattr(candidate, name_attr) == material_set_name:
if self.material_sets_are_equal(element, candidate):
reuse_identities[element_identity] = candidate
return candidate
elif element.is_a("IfcPresentationStyle"):
style_name = element.Name