Print unused elements stats / purge unused elements

Added to Purge Data section in Quality Control some tools - https://i.imgur.com/Nz3YoiH.png

1) Print unused elements - useful to investigate how much unused data you have in IFC file.
https://i.imgur.com/9Fkn0JD.png

2) purge unused elements by ifc class, it's doing it in pretty naive way and can be dangerous, so maybe it should be moved to debug section.
This commit is contained in:
Andrej730
2023-09-27 15:41:51 +05:00
parent fa90e420eb
commit 10672f3e1a
6 changed files with 99 additions and 0 deletions
@@ -38,6 +38,8 @@ classes = (
operator.SelectHighPolygonMeshes,
operator.SelectHighestPolygonMeshes,
operator.ValidateIfcFile,
operator.PrintUnusedElementStats,
operator.PurgeUnusedElementsByClass,
prop.BIMDebugProperties,
ui.BIM_PT_debug,
)
@@ -454,3 +454,44 @@ class OverrideDisplayType(bpy.types.Operator):
for obj in context.selected_objects:
obj.display_type = self.display
return {"FINISHED"}
class PrintUnusedElementStats(bpy.types.Operator, tool.Ifc.Operator):
bl_idname = "bim.print_unused_elements_stats"
bl_label = "Purge Unused Elements Stats"
bl_options = {"REGISTER", "UNDO"}
bl_description = (
"Print all unused elements in current IFC project in system console, not limited to the selected class"
)
ignore_contexts: bpy.props.BoolProperty(name="Ignore Contexts", default=True)
ignore_relationships: bpy.props.BoolProperty(name="Ignore Relationships", default=True)
ignore_types: bpy.props.BoolProperty(name="Ignore Types", default=True)
def _execute(self, context):
props = context.scene.BIMDebugProperties
# ignore some classes that could have zero 0 inverse references by their nature
ignore_classes = []
if self.ignore_contexts:
ignore_classes += ["IfcRepresentationContext"]
if self.ignore_relationships:
ignore_classes += ["IfcRelationship"]
if self.ignore_types:
ignore_classes += ["IfcTypeProduct"]
unused_elements = tool.Debug.print_unused_elements_stats(props.ifc_class_purge, ignore_classes)
self.report({"INFO"}, f"{unused_elements} unused elements found, check the system console for the details.")
class PurgeUnusedElementsByClass(bpy.types.Operator, tool.Ifc.Operator):
bl_idname = "bim.purge_unused_elements_by_class"
bl_label = "Purge Unused Elements By Class"
bl_description = (
"Will find all elements of class that have no inverse refernces and will remove them, use very carefully."
)
bl_options = {"REGISTER", "UNDO"}
def _execute(self, context):
props = context.scene.BIMDebugProperties
purged_elements = core.purge_unused_elements(tool.Ifc, tool.Debug, props.ifc_class_purge)
self.report({"INFO"}, f"{purged_elements} unused elements found and removed.")
@@ -50,3 +50,4 @@ class BIMDebugProperties(PropertyGroup):
name="Display Type",
default="BOUNDS",
)
ifc_class_purge: StringProperty(name="Unused Elements IFC Class", default="")
@@ -398,3 +398,10 @@ class BIM_PT_purge(Panel):
layout.operator("bim.purge_unused_profiles")
layout.operator("bim.purge_unused_types")
layout.operator("bim.purge_unused_representations")
layout.separator()
layout.label(text="Purge unused elements by class:")
row = layout.row(align=True)
row.prop(context.scene.BIMDebugProperties, "ifc_class_purge", text="")
row.operator("bim.purge_unused_elements_by_class", text="", icon="TRASH")
row.operator("bim.print_unused_elements_stats", text="", icon="INFO")
+8
View File
@@ -23,3 +23,11 @@ def parse_express(debug, filename):
def purge_hdf5_cache(debug):
debug.purge_hdf5_cache()
def purge_unused_elements(ifc, debug, ifc_class):
ifc_file = ifc.get()
unused_elements = [i for i in ifc_file.by_type(ifc_class) if ifc_file.get_total_inverses(i) == 0]
unused_elements_amount = len(unused_elements)
debug.remove_unused_elements(unused_elements)
return unused_elements_amount
+40
View File
@@ -20,6 +20,7 @@ import os
import bpy
import ifcopenshell.express
import blenderbim.core.tool
import blenderbim.tool as tool
from blenderbim.bim.ifc import IfcStore
@@ -48,3 +49,42 @@ class Debug(blenderbim.core.tool.Debug):
obj = bpy.data.objects.new(name, mesh)
bpy.context.scene.collection.objects.link(obj)
return obj
@classmethod
def remove_unused_elements(cls, elements):
ifc_file = tool.Ifc.get()
for element in elements:
ifcopenshell.util.element.remove_deep2(ifc_file, element)
@classmethod
def print_unused_elements_stats(cls, requested_ifc_class="", ignore_classes=tuple()):
ifc_file = tool.Ifc.get()
# get list of ifc classes used in model
classes = set()
requested_ifc_classes = set()
for el in ifc_file:
if any(el.is_a(i) for i in ignore_classes):
continue
classes.add(el.is_a())
if requested_ifc_class and el.is_a(requested_ifc_class):
requested_ifc_classes.add(el.is_a())
# count unused elements for each class
unused = dict()
for c in classes:
uses = [i for i in ifc_file.by_type(c) if ifc_file.get_total_inverses(i) == 0]
if not uses:
continue
unused[c] = len(uses)
# print classes and their unsued elements in ascending order
if unused:
print("Unused elements by classes:")
for ifc_class in sorted(unused.keys(), key=lambda x: unused[x]):
class_string = ifc_class
if ifc_class in requested_ifc_classes:
class_string = "---> " + class_string
print(f"{class_string: <50} {unused[ifc_class]: >5}")
return sum(unused.values())