From ca382f47df0c7c468c7b60ad1c0af71d5e390779 Mon Sep 17 00:00:00 2001 From: Ryan Schultz Date: Thu, 30 Oct 2025 17:04:29 -0500 Subject: [PATCH] this one fixes https://github.com/IfcOpenShell/IfcOpenShell/issues/7304 --- src/ifc5d/ifc5d/qto.py | 34 +++++++++++++++++++++++++--------- 1 file changed, 25 insertions(+), 9 deletions(-) diff --git a/src/ifc5d/ifc5d/qto.py b/src/ifc5d/ifc5d/qto.py index 8f21fa4235..6d86f4c6f7 100644 --- a/src/ifc5d/ifc5d/qto.py +++ b/src/ifc5d/ifc5d/qto.py @@ -92,24 +92,40 @@ def quantify(ifc_file: ifcopenshell.file, elements: set[ifcopenshell.entity_inst for calculator, queries in rules["calculators"].items(): calculator = calculators[calculator] - if not set(m.lower() for m in queries.keys()) - lower_case_entity_names(ifc_file.schema_identifier): - # all defined queries are actually simple entity names: instead of looping over all - # calculators we loop over the entity types so that we don't have to repeatedly query - # the model, especially when the set of elements is small. + # Cache schema entity names once per file + schema_entity_names = lower_case_entity_names(ifc_file.schema_identifier) + + # Fast path: all queries are simple entity names + if not set(m.lower() for m in queries.keys()) - schema_entity_names: casenorm = {k.lower(): k for k in queries.keys()} pred = lambda inst: inst.is_a().lower() - for ty, elements in itertools.groupby(sorted(elements, key=pred), key=pred): + + for ty, group in itertools.groupby(sorted(elements, key=pred), key=pred): + group_elements = list(group) + # check entity type and its supertypes for matching QTO rules for sty in entity_supertypes(ifc_file.schema_identifier, ty): if qtos := queries.get(casenorm.get(sty)): - calculator.calculate(ifc_file, list(elements), qtos, results) + calculator.calculate(ifc_file, group_elements, qtos, results) + + # Fallback: per-query evaluation for query, qtos in queries.items(): - if query.lower() in lower_case_entity_names(ifc_file.schema_identifier): - # by_type is faster than selector parsing - filtered_elements = ifc_file.by_type(query) + # Simple entity name: use by_type but restrict to incoming elements + if query.lower() in schema_entity_names: + by_type_all = ifc_file.by_type(query) + # ensure we don't expand beyond provided elements subset + if isinstance(elements, set): + filtered_elements = [e for e in by_type_all if e in elements] + else: + elements_set = set(elements) + filtered_elements = [e for e in by_type_all if e in elements_set] else: filtered_elements = ifcopenshell.util.selector.filter_elements(ifc_file, query, elements) + if filtered_elements: calculator.calculate(ifc_file, filtered_elements, qtos, results) + + + return results