mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-09-21 08:45:22 +00:00
See #3334. Optimise pset getting and add simple "broadphase" filtering for all facets for faster validation.
This commit is contained in:
@@ -77,7 +77,9 @@ def get_pset(element, name, prop=None, should_inherit=True):
|
|||||||
|
|
||||||
if not prop:
|
if not prop:
|
||||||
if type_pset:
|
if type_pset:
|
||||||
type_pset.update(get_property_definition(pset))
|
occurrence_pset = get_property_definition(pset)
|
||||||
|
if occurrence_pset:
|
||||||
|
type_pset.update(occurrence_pset)
|
||||||
return type_pset
|
return type_pset
|
||||||
return get_property_definition(pset)
|
return get_property_definition(pset)
|
||||||
|
|
||||||
@@ -128,9 +130,10 @@ def get_psets(element, psets_only=False, qtos_only=False, should_inherit=True):
|
|||||||
continue
|
continue
|
||||||
psets[definition.Name] = get_property_definition(definition)
|
psets[definition.Name] = get_property_definition(definition)
|
||||||
elif hasattr(element, "IsDefinedBy"):
|
elif hasattr(element, "IsDefinedBy"):
|
||||||
element_type = ifcopenshell.util.element.get_type(element)
|
if should_inherit:
|
||||||
if element_type and should_inherit:
|
element_type = ifcopenshell.util.element.get_type(element)
|
||||||
psets = get_psets(element_type, psets_only=psets_only, qtos_only=qtos_only, should_inherit=False)
|
if element_type:
|
||||||
|
psets = get_psets(element_type, psets_only=psets_only, qtos_only=qtos_only, should_inherit=False)
|
||||||
for relationship in element.IsDefinedBy:
|
for relationship in element.IsDefinedBy:
|
||||||
if relationship.is_a("IfcRelDefinesByProperties"):
|
if relationship.is_a("IfcRelDefinesByProperties"):
|
||||||
definition = relationship.RelatingPropertyDefinition
|
definition = relationship.RelatingPropertyDefinition
|
||||||
|
|||||||
@@ -131,6 +131,9 @@ class Entity(Facet):
|
|||||||
super().__init__(name, predefinedType, instructions)
|
super().__init__(name, predefinedType, instructions)
|
||||||
|
|
||||||
def filter(self, ifc_file, elements):
|
def filter(self, ifc_file, elements):
|
||||||
|
if isinstance(elements, list):
|
||||||
|
return super().filter(ifc_file, elements)
|
||||||
|
|
||||||
if isinstance(self.name, str):
|
if isinstance(self.name, str):
|
||||||
try:
|
try:
|
||||||
results = ifc_file.by_type(self.name, include_subtypes=False)
|
results = ifc_file.by_type(self.name, include_subtypes=False)
|
||||||
@@ -180,6 +183,21 @@ class Attribute(Facet):
|
|||||||
]
|
]
|
||||||
super().__init__(name, value, minOccurs, maxOccurs, instructions)
|
super().__init__(name, value, minOccurs, maxOccurs, instructions)
|
||||||
|
|
||||||
|
def filter(self, ifc_file, elements):
|
||||||
|
if isinstance(elements, list):
|
||||||
|
return super().filter(ifc_file, elements)
|
||||||
|
|
||||||
|
results = []
|
||||||
|
schema = ifcopenshell.ifcopenshell_wrapper.schema_by_name(ifc_file.schema)
|
||||||
|
for entity in schema.entities():
|
||||||
|
for attribute in entity.attributes():
|
||||||
|
if attribute.name() == self.name:
|
||||||
|
results.extend(ifc_file.by_type(entity.name(), include_subtypes=False))
|
||||||
|
|
||||||
|
# TODO: perhaps we should consider value in the filter
|
||||||
|
|
||||||
|
return results
|
||||||
|
|
||||||
def __call__(self, inst, logger=None):
|
def __call__(self, inst, logger=None):
|
||||||
if self.minOccurs == 0 and self.maxOccurs != 0:
|
if self.minOccurs == 0 and self.maxOccurs != 0:
|
||||||
return AttributeResult(True)
|
return AttributeResult(True)
|
||||||
@@ -285,7 +303,9 @@ class Classification(Facet):
|
|||||||
super().__init__(value, system, uri, minOccurs, maxOccurs, instructions)
|
super().__init__(value, system, uri, minOccurs, maxOccurs, instructions)
|
||||||
|
|
||||||
def filter(self, ifc_file, elements):
|
def filter(self, ifc_file, elements):
|
||||||
pass
|
if isinstance(elements, list):
|
||||||
|
return super().filter(ifc_file, elements)
|
||||||
|
return ifc_file.by_type("IfcObjectDefinition")
|
||||||
|
|
||||||
def __call__(self, inst, logger=None):
|
def __call__(self, inst, logger=None):
|
||||||
if self.minOccurs == 0 and self.maxOccurs != 0:
|
if self.minOccurs == 0 and self.maxOccurs != 0:
|
||||||
@@ -341,6 +361,11 @@ class PartOf(Facet):
|
|||||||
]
|
]
|
||||||
super().__init__(entity, predefinedType, relation, minOccurs, maxOccurs, instructions)
|
super().__init__(entity, predefinedType, relation, minOccurs, maxOccurs, instructions)
|
||||||
|
|
||||||
|
def filter(self, ifc_file, elements):
|
||||||
|
if isinstance(elements, list):
|
||||||
|
return super().filter(ifc_file, elements)
|
||||||
|
return list(ifc_file) # Lazy
|
||||||
|
|
||||||
def asdict(self):
|
def asdict(self):
|
||||||
results = super().asdict()
|
results = super().asdict()
|
||||||
entity = {}
|
entity = {}
|
||||||
@@ -479,16 +504,26 @@ class Property(Facet):
|
|||||||
]
|
]
|
||||||
super().__init__(propertySet, name, value, measure, uri, minOccurs, maxOccurs, instructions)
|
super().__init__(propertySet, name, value, measure, uri, minOccurs, maxOccurs, instructions)
|
||||||
|
|
||||||
|
def filter(self, ifc_file, elements):
|
||||||
|
if isinstance(elements, list):
|
||||||
|
return super().filter(ifc_file, elements)
|
||||||
|
if ifc_file.schema == "IFC2X3":
|
||||||
|
return ifc_file.by_type("IfcObjectDefinition")
|
||||||
|
return (
|
||||||
|
ifc_file.by_type("IfcObjectDefinition")
|
||||||
|
+ ifc_file.by_type("IfcMaterialDefinition")
|
||||||
|
+ ifc_file.by_type("IfcProfileDef")
|
||||||
|
)
|
||||||
|
|
||||||
def __call__(self, inst, logger=None):
|
def __call__(self, inst, logger=None):
|
||||||
if self.minOccurs == 0 and self.maxOccurs != 0:
|
if self.minOccurs == 0 and self.maxOccurs != 0:
|
||||||
return PropertyResult(True)
|
return PropertyResult(True)
|
||||||
|
|
||||||
all_psets = ifcopenshell.util.element.get_psets(inst)
|
|
||||||
|
|
||||||
if isinstance(self.propertySet, str):
|
if isinstance(self.propertySet, str):
|
||||||
pset = all_psets.get(self.propertySet, None)
|
pset = ifcopenshell.util.element.get_pset(inst, self.propertySet)
|
||||||
psets = {self.propertySet: pset} if pset else {}
|
psets = {self.propertySet: pset} if pset else {}
|
||||||
else:
|
else:
|
||||||
|
all_psets = ifcopenshell.util.element.get_psets(inst)
|
||||||
psets = {k: v for k, v in all_psets.items() if k == self.propertySet}
|
psets = {k: v for k, v in all_psets.items() if k == self.propertySet}
|
||||||
|
|
||||||
is_pass = bool(psets)
|
is_pass = bool(psets)
|
||||||
@@ -730,6 +765,11 @@ class Material(Facet):
|
|||||||
]
|
]
|
||||||
super().__init__(value, uri, minOccurs, maxOccurs, instructions)
|
super().__init__(value, uri, minOccurs, maxOccurs, instructions)
|
||||||
|
|
||||||
|
def filter(self, ifc_file, elements):
|
||||||
|
if isinstance(elements, list):
|
||||||
|
return super().filter(ifc_file, elements)
|
||||||
|
return ifc_file.by_type("IfcObjectDefinition")
|
||||||
|
|
||||||
def __call__(self, inst, logger=None):
|
def __call__(self, inst, logger=None):
|
||||||
if self.minOccurs == 0 and self.maxOccurs != 0:
|
if self.minOccurs == 0 and self.maxOccurs != 0:
|
||||||
return MaterialResult(True)
|
return MaterialResult(True)
|
||||||
|
|||||||
@@ -200,18 +200,14 @@ class Specification:
|
|||||||
if filter_version and ifc_file.schema not in self.ifcVersion:
|
if filter_version and ifc_file.schema not in self.ifcVersion:
|
||||||
return
|
return
|
||||||
|
|
||||||
elements = []
|
elements = None
|
||||||
|
|
||||||
|
# This is a broadphase filter of applicability. We almost never want to
|
||||||
|
# test every single class in an IFC model.
|
||||||
for i, facet in enumerate(self.applicability):
|
for i, facet in enumerate(self.applicability):
|
||||||
# Usually, we rely on an entity applicability to give us our first
|
|
||||||
# shortlist of elements, as it's the most efficient way to filter
|
|
||||||
# elements. If this does not exist, then we have no choice but to
|
|
||||||
# check everything.
|
|
||||||
if i == 0 and not isinstance(facet, Entity):
|
|
||||||
elements = list(ifc_file)
|
|
||||||
elements = facet.filter(ifc_file, elements)
|
elements = facet.filter(ifc_file, elements)
|
||||||
|
|
||||||
for element in elements:
|
for element in elements or []:
|
||||||
is_applicable = True
|
is_applicable = True
|
||||||
for facet in self.applicability:
|
for facet in self.applicability:
|
||||||
if isinstance(facet, Entity):
|
if isinstance(facet, Entity):
|
||||||
|
|||||||
Reference in New Issue
Block a user