Quantify elements from a rules using preset quantification rules Rules placed as a JSON configuration file in the ``ifc5d`` folder will be autodetected and loaded with the module for convenience. :param rules: Set of rules from `ifc5d.qto.rules`.
(ifc_file: ifcopenshell.file, elements: set[ifcopenshell.entity_instance], rules: dict)
| 77 | |
| 78 | |
| 79 | def quantify(ifc_file: ifcopenshell.file, elements: set[ifcopenshell.entity_instance], rules: dict) -> ResultsDict: |
| 80 | """Quantify elements from a rules using preset quantification rules |
| 81 | |
| 82 | Rules placed as a JSON configuration file in the ``ifc5d`` folder will be |
| 83 | autodetected and loaded with the module for convenience. |
| 84 | |
| 85 | :param rules: Set of rules from `ifc5d.qto.rules`. |
| 86 | """ |
| 87 | results: ResultsDict = {} |
| 88 | elements_by_classes: defaultdict[str, set[ifcopenshell.entity_instance]] = defaultdict(set) |
| 89 | for element in elements: |
| 90 | elements_by_classes[element.is_a()].add(element) |
| 91 | |
| 92 | for calculator, queries in rules["calculators"].items(): |
| 93 | calculator = calculators[calculator] |
| 94 | # Cache schema entity names once per file |
| 95 | schema_entity_names = lower_case_entity_names(ifc_file.schema_identifier) |
| 96 | |
| 97 | # Fast path: all queries are simple entity names |
| 98 | if not set(m.lower() for m in queries.keys()) - schema_entity_names: |
| 99 | casenorm = {k.lower(): k for k in queries.keys()} |
| 100 | pred = lambda inst: inst.is_a().lower() |
| 101 | |
| 102 | for ty, group in itertools.groupby(sorted(elements, key=pred), key=pred): |
| 103 | group_elements = list(group) |
| 104 | # check entity type and its supertypes for matching QTO rules |
| 105 | for sty in entity_supertypes(ifc_file.schema_identifier, ty): |
| 106 | if qtos := queries.get(casenorm.get(sty)): |
| 107 | calculator.calculate(ifc_file, group_elements, qtos, results) |
| 108 | continue # Skip general path to avoid duplicate calculations |
| 109 | |
| 110 | # Fallback: per-query evaluation |
| 111 | for query, qtos in queries.items(): |
| 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] |
| 121 | else: |
| 122 | filtered_elements = ifcopenshell.util.selector.filter_elements(ifc_file, query, elements) |
| 123 | |
| 124 | if filtered_elements: |
| 125 | calculator.calculate(ifc_file, filtered_elements, qtos, results) |
| 126 | |
| 127 | return results |
| 128 | |
| 129 | |
| 130 | def edit_qtos(ifc_file: ifcopenshell.file, results: ResultsDict) -> None: |
no test coverage detected