mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-09-16 13:46:54 +00:00
Fix quantify() regression where by_type queries and grouped iteration could apply QTOs to all entities in the model instead of the intended element subset.
This commit is contained in:
+25
-12
@@ -92,24 +92,38 @@ def quantify(ifc_file: ifcopenshell.file, elements: set[ifcopenshell.entity_inst
|
|||||||
|
|
||||||
for calculator, queries in rules["calculators"].items():
|
for calculator, queries in rules["calculators"].items():
|
||||||
calculator = calculators[calculator]
|
calculator = calculators[calculator]
|
||||||
if not set(m.lower() for m in queries.keys()) - lower_case_entity_names(ifc_file.schema_identifier):
|
# Cache schema entity names once per file
|
||||||
# all defined queries are actually simple entity names: instead of looping over all
|
schema_entity_names = lower_case_entity_names(ifc_file.schema_identifier)
|
||||||
# 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.
|
# 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()}
|
casenorm = {k.lower(): k for k in queries.keys()}
|
||||||
pred = lambda inst: inst.is_a().lower()
|
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):
|
for sty in entity_supertypes(ifc_file.schema_identifier, ty):
|
||||||
if qtos := queries.get(casenorm.get(sty)):
|
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():
|
for query, qtos in queries.items():
|
||||||
if query.lower() in lower_case_entity_names(ifc_file.schema_identifier):
|
# Simple entity name: use by_type but restrict to incoming elements
|
||||||
# by_type is faster than selector parsing
|
if query.lower() in schema_entity_names:
|
||||||
filtered_elements = ifc_file.by_type(query)
|
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:
|
else:
|
||||||
filtered_elements = ifcopenshell.util.selector.filter_elements(ifc_file, query, elements)
|
filtered_elements = ifcopenshell.util.selector.filter_elements(ifc_file, query, elements)
|
||||||
|
|
||||||
if filtered_elements:
|
if filtered_elements:
|
||||||
calculator.calculate(ifc_file, filtered_elements, qtos, results)
|
calculator.calculate(ifc_file, filtered_elements, qtos, results)
|
||||||
|
|
||||||
return results
|
return results
|
||||||
|
|
||||||
|
|
||||||
@@ -159,8 +173,7 @@ class IteratorForTypes:
|
|||||||
element: Union[ifcopenshell.entity_instance, None] = None
|
element: Union[ifcopenshell.entity_instance, None] = None
|
||||||
shape: Union[ifcopenshell.geom.ShapeType, None] = None
|
shape: Union[ifcopenshell.geom.ShapeType, None] = None
|
||||||
|
|
||||||
def __init__(
|
def __init__(self,
|
||||||
self,
|
|
||||||
ifc_file: ifcopenshell.file,
|
ifc_file: ifcopenshell.file,
|
||||||
settings: ifcopenshell.geom.settings,
|
settings: ifcopenshell.geom.settings,
|
||||||
elements: Iterable[ifcopenshell.entity_instance],
|
elements: Iterable[ifcopenshell.entity_instance],
|
||||||
@@ -560,4 +573,4 @@ class Blender(QtoCalculator):
|
|||||||
calculators: dict[str, type[QtoCalculator]] = {
|
calculators: dict[str, type[QtoCalculator]] = {
|
||||||
"Blender": Blender,
|
"Blender": Blender,
|
||||||
"IfcOpenShell": IfcOpenShell,
|
"IfcOpenShell": IfcOpenShell,
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user