@@ -92,24 +92,38 @@ def quantify(ifc_file: ifcopenshell.file, elements: set[ifcopenshell.entity_inst
9292
9393 for calculator , queries in rules ["calculators" ].items ():
9494 calculator = calculators [calculator ]
95- if not set (m .lower () for m in queries .keys ()) - lower_case_entity_names (ifc_file .schema_identifier ):
96- # all defined queries are actually simple entity names: instead of looping over all
97- # calculators we loop over the entity types so that we don't have to repeatedly query
98- # the model, especially when the set of elements is small.
95+ # Cache schema entity names once per file
96+ schema_entity_names = lower_case_entity_names (ifc_file .schema_identifier )
97+
98+ # Fast path: all queries are simple entity names
99+ if not set (m .lower () for m in queries .keys ()) - schema_entity_names :
99100 casenorm = {k .lower (): k for k in queries .keys ()}
100101 pred = lambda inst : inst .is_a ().lower ()
101- for ty , elements in itertools .groupby (sorted (elements , key = pred ), key = pred ):
102+
103+ for ty , group in itertools .groupby (sorted (elements , key = pred ), key = pred ):
104+ group_elements = list (group )
105+ # check entity type and its supertypes for matching QTO rules
102106 for sty in entity_supertypes (ifc_file .schema_identifier , ty ):
103107 if qtos := queries .get (casenorm .get (sty )):
104- calculator .calculate (ifc_file , list (elements ), qtos , results )
108+ calculator .calculate (ifc_file , group_elements , qtos , results )
109+
110+ # Fallback: per-query evaluation
105111 for query , qtos in queries .items ():
106- if query .lower () in lower_case_entity_names (ifc_file .schema_identifier ):
107- # by_type is faster than selector parsing
108- filtered_elements = ifc_file .by_type (query )
112+ # Simple entity name: use by_type but restrict to incoming elements
113+ if query .lower () in schema_entity_names :
114+ by_type_all = ifc_file .by_type (query )
115+ # ensure we don't expand beyond provided elements subset
116+ if isinstance (elements , set ):
117+ filtered_elements = [e for e in by_type_all if e in elements ]
118+ else :
119+ elements_set = set (elements )
120+ filtered_elements = [e for e in by_type_all if e in elements_set ]
109121 else :
110122 filtered_elements = ifcopenshell .util .selector .filter_elements (ifc_file , query , elements )
123+
111124 if filtered_elements :
112125 calculator .calculate (ifc_file , filtered_elements , qtos , results )
126+
113127 return results
114128
115129
@@ -159,8 +173,7 @@ class is mimicking the iterator interface but works for IfcTypeProducts."""
159173 element : Union [ifcopenshell .entity_instance , None ] = None
160174 shape : Union [ifcopenshell .geom .ShapeType , None ] = None
161175
162- def __init__ (
163- self ,
176+ def __init__ (self ,
164177 ifc_file : ifcopenshell .file ,
165178 settings : ifcopenshell .geom .settings ,
166179 elements : Iterable [ifcopenshell .entity_instance ],
@@ -560,4 +573,4 @@ def calculate(cls, ifc_file, elements, qtos, results):
560573calculators : dict [str , type [QtoCalculator ]] = {
561574 "Blender" : Blender ,
562575 "IfcOpenShell" : IfcOpenShell ,
563- }
576+ }
0 commit comments