diff --git a/src/ifcopenshell-python/ifcopenshell/util/element.py b/src/ifcopenshell-python/ifcopenshell/util/element.py index 09927210d9..1047c3602d 100644 --- a/src/ifcopenshell-python/ifcopenshell/util/element.py +++ b/src/ifcopenshell-python/ifcopenshell/util/element.py @@ -86,6 +86,11 @@ def get_quantities(quantities): for quantity in quantities or []: if quantity.is_a("IfcPhysicalSimpleQuantity"): results[quantity.Name] = quantity[3] + elif quantity.is_a("IfcPhysicalComplexQuantity"): + data = {k: v for k, v in quantity.get_info().items() if v is not None and k != "Name"} + data["properties"] = get_quantities(quantity.HasQuantities) + del data["HasQuantities"] + results[quantity.Name] = data return results @@ -98,6 +103,12 @@ def get_properties(properties): results[prop.Name] = [v.wrappedValue for v in prop.EnumerationValues] or None elif prop.is_a("IfcPropertyListValue"): results[prop.Name] = [v.wrappedValue for v in prop.ListValues] or None + elif prop.is_a("IfcPropertyBoundedValue"): + data = prop.get_info() + del data["Unit"] + results[prop.Name] = data + elif prop.is_a("IfcPropertyTableValue"): + results[prop.Name] = prop.get_info() elif prop.is_a("IfcComplexProperty"): data = {k: v for k, v in prop.get_info().items() if v is not None and k != "Name"} data["properties"] = get_properties(prop.HasProperties) diff --git a/src/ifcopenshell-python/ifcopenshell/util/unit.py b/src/ifcopenshell-python/ifcopenshell/util/unit.py index 37d67442d3..4ca430056d 100644 --- a/src/ifcopenshell-python/ifcopenshell/util/unit.py +++ b/src/ifcopenshell-python/ifcopenshell/util/unit.py @@ -341,6 +341,29 @@ def get_property_unit(prop, ifc_file): measure_class = prop.EnumerationValues[0].is_a() elif prop.is_a("IfcPropertyListValue") and prop.ListValues: measure_class = prop.ListValues[0].is_a() + elif prop.is_a("IfcPropertyBoundedValue"): + if prop.UpperBoundValue: + measure_class = prop.UpperBoundValue.is_a() + elif prop.LowerBoundValue: + measure_class = prop.LowerBoundValue.is_a() + elif prop.SetPointValue: + measure_class = prop.SetPointValue.is_a() + elif prop.is_a("IfcPropertyTableValue"): + table_units = {} + for attribute in ["Defining", "Defined"]: + if getattr(prop, f"{attribute}Unit"): + table_units[f"{attribute}Unit"] = getattr(prop, f"{attribute}Unit") + elif getattr(prop, f"{attribute}Values"): + measure_class = getattr(prop, f"{attribute}Values")[0].is_a() + unit_type = get_measure_unit_type(measure_class) + units = [u for u in unit_assignment.Units if getattr(u, "UnitType", None) == unit_type] + if units: + table_units[f"{attribute}Unit"] = units[0] + else: + table_units[f"{attribute}Unit"] = None + else: + table_units[f"{attribute}Unit"] = None + return table_units unit_type = get_measure_unit_type(measure_class) units = [u for u in unit_assignment.Units if getattr(u, "UnitType", None) == unit_type] if units: diff --git a/src/ifctester/ifctester/facet.py b/src/ifctester/ifctester/facet.py index e0667cd100..11153e1513 100644 --- a/src/ifctester/ifctester/facet.py +++ b/src/ifctester/ifctester/facet.py @@ -517,7 +517,68 @@ class Property(Facet): break unit = ifcopenshell.util.unit.get_property_unit(prop_entity, inst.wrapped_data.file) if unit: - props[pset_name][prop_entity.Name] = [ifcopenshell.util.unit.convert(v, getattr(unit, "Prefix", None), unit.Name, None, ifcopenshell.util.unit.si_type_names[unit.UnitType]) for v in props[pset_name][prop_entity.Name]] + props[pset_name][prop_entity.Name] = [ + ifcopenshell.util.unit.convert( + v, + getattr(unit, "Prefix", None), + unit.Name, + None, + ifcopenshell.util.unit.si_type_names[unit.UnitType], + ) + for v in props[pset_name][prop_entity.Name] + ] + elif prop_entity.is_a("IfcPropertyBoundedValue"): + values = [] + for attribute in ["UpperBoundValue", "LowerBoundValue", "SetPointValue"]: + value = getattr(prop_entity, attribute) + if value is not None: + data_type = value.is_a() + values.append(value.wrappedValue) + if data_type != self.measure: + is_pass = False + reason = {"type": "MEASURE", "actual": data_type} + break + unit = ifcopenshell.util.unit.get_property_unit(prop_entity, inst.wrapped_data.file) + if unit: + values = [ + ifcopenshell.util.unit.convert( + v, + getattr(unit, "Prefix", None), + unit.Name, + None, + ifcopenshell.util.unit.si_type_names[unit.UnitType], + ) + for v in values + ] + props[pset_name][prop_entity.Name] = values + elif prop_entity.is_a("IfcPropertyTableValue"): + values = [] + units = ifcopenshell.util.unit.get_property_unit(prop_entity, inst.wrapped_data.file) + for attribute in ["Defining", "Defined"]: + column_values = props[pset_name][prop_entity.Name][f"{attribute}Values"] + if not column_values: + continue + data_type = column_values[0].is_a() + if data_type == self.measure: + column_values = [v.wrappedValue for v in column_values] + unit = units[f"{attribute}Unit"] + if unit: + column_values = [ + ifcopenshell.util.unit.convert( + v, + getattr(unit, "Prefix", None), + unit.Name, + None, + ifcopenshell.util.unit.si_type_names[unit.UnitType], + ) + for v in column_values + ] + values.extend(column_values) + if not values: + is_pass = False + reason = {"type": "MEASURE", "actual": data_type} + break + props[pset_name][prop_entity.Name] = values else: is_property_supported_class = False @@ -538,7 +599,8 @@ class Property(Facet): break elif isinstance(self.value, str) and isinstance(value, list): # "i_require_foo" = ["a", "b"] such as in enumerated properties - if self.value not in value: + cast_value = cast_to_value(self.value, value[0]) + if cast_value not in value: is_pass = False reason = {"type": "VALUE", "actual": value} break diff --git a/src/ifctester/test/test_facet.py b/src/ifctester/test/test_facet.py index 39ddb3ec9c..862dd934dc 100644 --- a/src/ifctester/test/test_facet.py +++ b/src/ifctester/test/test_facet.py @@ -1039,6 +1039,43 @@ class TestProperty: facet = Property(propertySet="Foo_Bar", name="Foo", value="Z", measure="IfcLabel") run("Any matching value in a list property will pass 3/3", facet=facet, inst=element, expected=False) + element = ifcopenshell.api.run("root.create_entity", ifc, ifc_class="IfcWall") + pset = ifcopenshell.api.run("pset.add_pset", ifc, product=element, name="Foo_Bar") + bounded_property = ifc.createIfcPropertyBoundedValue( + Name="Foo", + UpperBoundValue=ifc.createIfcLengthMeasure(5000), + LowerBoundValue=ifc.createIfcLengthMeasure(1000), + SetPointValue=ifc.createIfcLengthMeasure(3000), + ) + pset.HasProperties = [bounded_property] + facet = Property(propertySet="Foo_Bar", name="Foo", value="1", measure="IfcLengthMeasure") + run("Any matching value in a bounded property will pass 1/4", facet=facet, inst=element, expected=True) + facet = Property(propertySet="Foo_Bar", name="Foo", value="5", measure="IfcLengthMeasure") + run("Any matching value in a bounded property will pass 2/4", facet=facet, inst=element, expected=True) + facet = Property(propertySet="Foo_Bar", name="Foo", value="3", measure="IfcLengthMeasure") + run("Any matching value in a bounded property will pass 3/4", facet=facet, inst=element, expected=True) + facet = Property(propertySet="Foo_Bar", name="Foo", value="2", measure="IfcLengthMeasure") + run("Any matching value in a bounded property will pass 4/4", facet=facet, inst=element, expected=False) + + element = ifcopenshell.api.run("root.create_entity", ifc, ifc_class="IfcWall") + pset = ifcopenshell.api.run("pset.add_pset", ifc, product=element, name="Foo_Bar") + table_property = ifc.createIfcPropertyTableValue( + Name="Foo", DefiningValues=[ifc.createIfcLabel("X")], DefinedValues=[ifc.createIfcLengthMeasure(1000)] + ) + pset.HasProperties = [table_property] + facet = Property(propertySet="Foo_Bar", name="Foo", value="X", measure="IfcLabel") + run("Any matching value in a table property will pass 1/3", facet=facet, inst=element, expected=True) + facet = Property(propertySet="Foo_Bar", name="Foo", value="1", measure="IfcLengthMeasure") + run("Any matching value in a table property will pass 2/3", facet=facet, inst=element, expected=True) + facet = Property(propertySet="Foo_Bar", name="Foo", value="Y", measure="IfcLabel") + run("Any matching value in a table property will pass 3/3", facet=facet, inst=element, expected=False) + + element = ifcopenshell.api.run("root.create_entity", ifc, ifc_class="IfcWall") + pset = ifcopenshell.api.run("pset.add_pset", ifc, product=element, name="Foo_Bar") + pset.HasProperties = [ifc.createIfcPropertyReferenceValue(Name="Foo")] + facet = Property(propertySet="Foo_Bar", name="Foo", measure="IfcLabel") + run("Reference properties are treated as objects and not supported", facet=facet, inst=element, expected=False) + facet = Property(propertySet="Foo_Bar", name="Foo", measure="IfcLengthMeasure") element = ifcopenshell.api.run("root.create_entity", ifc, ifc_class="IfcWall") qto = ifcopenshell.api.run("pset.add_qto", ifc, product=element, name="Foo_Bar")