Summary
The ifcpatch recipe MergeDuplicateTypes can silently destroy distinct element types (and, as a knock-on effect, break Bonsai array modifiers that reference them). There are two related problems in the recipe:
should_merge_null=True is the default, which merges every type that has an empty merge attribute into a single type — even when those types are clearly not duplicates.
- Types are grouped by the attribute value only, ignoring their IFC class, so two unrelated types that happen to share a value can be merged, which then raises a
TypeError from assign_type (or, worse, silently mis-types occurrences).
Recipe: src/ifcpatch/ifcpatch/recipes/MergeDuplicateTypes.py
Problem 1 — should_merge_null=True destroys distinct untagged types
The default groups types by Tag. With should_merge_null=True, all types with an empty Tag are treated as duplicates of one another and collapsed into a single type. But an empty attribute is an absence of evidence, not proof of duplication.
On a Bonsai-authored (or mixed) model this is destructive. For example a masonry assembly built with array modifiers had three distinct, differently-named types that all happened to lack a Tag:
IfcBuildingElementProxyType "Wire Ladder"
IfcBuildingElementProxyType "CMU Core"
IfcBuildingElementProxyType "Mortar Joint"
Running the default patch collapsed all three into one and removed the other two. Because these types were referenced as array children in BBIM_Array psets, re-opening the patched file in Bonsai produced:
setup_arrays: array parent 3QecqJiErB$9TeAgOjclzW references missing child GUID '2N9VxgdQL2eO0eoCEe8Bi_'.
setup_arrays: array parent 0QwQTUhPr6EAqfEjNat8se references missing child GUID '0d1KDqYRrCE9_7zIFzbh7M'.
setup_arrays: array parent 3iGRJZfy51iAd8K3LgiYDq references missing child GUID '3FI_7o4Dz1keW3geVnqXHX'.
("Arrays With Missing Children" warning in the Project panel.)
Genuine Revit duplicates — the recipe's intended target — always carry a populated Tag, so merging untagged types buys nothing for that workflow while risking data loss elsewhere.
Minimal repro
import ifcopenshell, ifcopenshell.api.root, ifcpatch
f = ifcopenshell.file(schema="IFC4")
for name in ("Wire Ladder", "CMU Core", "Mortar Joint"):
ifcopenshell.api.root.create_entity(f, ifc_class="IfcBuildingElementProxyType", name=name)
# Tag left as None
out = ifcpatch.execute({"file": f, "recipe": "MergeDuplicateTypes", "arguments": []})
print(len(out.by_type("IfcBuildingElementProxyType"))) # -> 1 (expected 3)
Problem 2 — cross-class merge raises TypeError
Because the grouping key ignores the IFC class, two unrelated types with the same (or both-empty) attribute value can be merged. When the "keeper" type cannot type the other type's occurrences, assign_type raises. Observed on a Revit export:
TypeError: IfcTypeProduct cannot type IfcBeam in schema IFC4 (allowed occurrence classes: IfcAnnotation)
(Bonsai uses IfcTypeProduct for annotation types; it collided with a beam type on an empty Tag.)
Minimal repro
import ifcopenshell, ifcopenshell.api.root, ifcopenshell.api.type, ifcpatch
f = ifcopenshell.file(schema="IFC4")
at = ifcopenshell.api.root.create_entity(f, ifc_class="IfcTypeProduct")
a = ifcopenshell.api.root.create_entity(f, ifc_class="IfcAnnotation")
ifcopenshell.api.type.assign_type(f, related_objects=[a], relating_type=at, should_map_representations=False)
bt = ifcopenshell.api.root.create_entity(f, ifc_class="IfcBeamType")
b = ifcopenshell.api.root.create_entity(f, ifc_class="IfcBeam")
ifcopenshell.api.type.assign_type(f, related_objects=[b], relating_type=bt, should_map_representations=False)
ifcpatch.execute({"file": f, "recipe": "MergeDuplicateTypes", "arguments": ["Tag", True]}) # -> TypeError
Proposed fix
- Include the IFC class in the grouping key so only same-class types are ever merged.
- Change the default to
should_merge_null=False, so untagged types are kept separate unless the caller explicitly opts in via ["Tag", True].
Both changes are small and localized to the recipe. Happy to open a PR with these plus regression tests covering: same-class populated-attribute duplicates still merge; distinct null-attribute types are preserved by default; cross-class types are never merged (no TypeError); and the explicit should_merge_null toggle still works.
Remaining edge case (not addressed by the above)
Even with those fixes, any type removal in this recipe can orphan a BBIM_Array "children" reference if a genuinely-duplicate type is also an array child. A more complete fix would make the recipe array-aware (repoint or clean up BBIM_Array child GUIDs when a referenced type is merged away). Noting it here for completeness.
Environment
- IfcOpenShell / Bonsai
v0.8.0 branch
- Blender 4.5, Windows 10
Summary
The
ifcpatchrecipeMergeDuplicateTypescan silently destroy distinct element types (and, as a knock-on effect, break Bonsai array modifiers that reference them). There are two related problems in the recipe:should_merge_null=Trueis the default, which merges every type that has an empty merge attribute into a single type — even when those types are clearly not duplicates.TypeErrorfromassign_type(or, worse, silently mis-types occurrences).Recipe:
src/ifcpatch/ifcpatch/recipes/MergeDuplicateTypes.pyProblem 1 —
should_merge_null=Truedestroys distinct untagged typesThe default groups types by
Tag. Withshould_merge_null=True, all types with an emptyTagare treated as duplicates of one another and collapsed into a single type. But an empty attribute is an absence of evidence, not proof of duplication.On a Bonsai-authored (or mixed) model this is destructive. For example a masonry assembly built with array modifiers had three distinct, differently-named types that all happened to lack a
Tag:IfcBuildingElementProxyType"Wire Ladder"IfcBuildingElementProxyType"CMU Core"IfcBuildingElementProxyType"Mortar Joint"Running the default patch collapsed all three into one and removed the other two. Because these types were referenced as array children in
BBIM_Arraypsets, re-opening the patched file in Bonsai produced:("Arrays With Missing Children" warning in the Project panel.)
Genuine Revit duplicates — the recipe's intended target — always carry a populated
Tag, so merging untagged types buys nothing for that workflow while risking data loss elsewhere.Minimal repro
Problem 2 — cross-class merge raises
TypeErrorBecause the grouping key ignores the IFC class, two unrelated types with the same (or both-empty) attribute value can be merged. When the "keeper" type cannot type the other type's occurrences,
assign_typeraises. Observed on a Revit export:(Bonsai uses
IfcTypeProductfor annotation types; it collided with a beam type on an emptyTag.)Minimal repro
Proposed fix
should_merge_null=False, so untagged types are kept separate unless the caller explicitly opts in via["Tag", True].Both changes are small and localized to the recipe. Happy to open a PR with these plus regression tests covering: same-class populated-attribute duplicates still merge; distinct null-attribute types are preserved by default; cross-class types are never merged (no
TypeError); and the explicitshould_merge_nulltoggle still works.Remaining edge case (not addressed by the above)
Even with those fixes, any type removal in this recipe can orphan a
BBIM_Array"children" reference if a genuinely-duplicate type is also an array child. A more complete fix would make the recipe array-aware (repoint or clean upBBIM_Arraychild GUIDs when a referenced type is merged away). Noting it here for completeness.Environment
v0.8.0branch