Files
IfcOpenShell/src/ifctester/test/test_facet.py
T

1772 lines
92 KiB
Python

# IfcTester - IDS based model auditing
# Copyright (C) 2021-2022 Thomas Krijnen <thomas@aecgeeks.com>, Dion Moult <dion@thinkmoult.com>
#
# This file is part of IfcTester.
#
# IfcTester is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# IfcTester is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with IfcTester. If not, see <http://www.gnu.org/licenses/>.
import uuid
import ifcopenshell
import ifcopenshell.api
import ifcopenshell.api.aggregate
import ifcopenshell.api.classification
import ifcopenshell.api.group
import ifcopenshell.api.material
import ifcopenshell.api.nest
import ifcopenshell.api.pset
import ifcopenshell.api.root
import ifcopenshell.api.spatial
import ifcopenshell.api.type
import ifcopenshell.api.unit
import ifcopenshell.guid
import ifcopenshell.util.pset
import ifctester.ids
import ifctester.facet
from ifctester.facet import Entity, Attribute, Classification, Property, PartOf, Material, Restriction
def set_facet(facet):
pass
def run(name, *, facet, inst, expected):
ifctester.facet.get_pset.cache_clear()
ifctester.facet.get_psets.cache_clear()
assert bool(facet(inst)) is expected
class TestEntity:
def test_creating_an_entity_facet(self):
facet = Entity(name="IfcName")
assert facet.asdict("applicability") == {"name": {"simpleValue": "IfcName"}}
facet = Entity(name="IfcName", predefinedType="predefinedType", instructions="instructions")
assert facet.asdict("requirement") == {
"name": {"simpleValue": "IfcName"},
"predefinedType": {"simpleValue": "predefinedType"},
"@instructions": "instructions",
}
def test_filtering_using_an_entity_facet(self):
set_facet("entity")
ifc = ifcopenshell.file()
facet = Entity(name="IFCRABBIT")
run("Invalid entities always fail", facet=facet, inst=ifc.createIfcWall(), expected=False)
ifc = ifcopenshell.file()
facet = Entity(name="IFCWALL")
run("A matching entity should pass", facet=facet, inst=ifc.createIfcWall(), expected=True)
ifc = ifcopenshell.file()
run(
"An matching entity should pass regardless of predefined type",
facet=facet,
inst=ifc.createIfcWall(PredefinedType="SOLIDWALL"),
expected=True,
)
ifc = ifcopenshell.file()
run(
"An entity not matching the specified class should fail",
facet=facet,
inst=ifc.createIfcSlab(),
expected=False,
)
# TODO: Some votes to prefer inheritance (For: Moult, Evandro, Artur)
# Possible argument: CAD tools don't understand IFC
ifc = ifcopenshell.file()
run(
"Subclasses are not considered as matching",
facet=facet,
inst=ifc.createIfcWallStandardCase(),
expected=False,
)
facet = Entity(name="IfcWall")
ifc = ifcopenshell.file()
run(
"Entities must be specified as uppercase strings",
facet=facet,
inst=ifc.createIfcWall(),
expected=False,
)
facet = Entity(name="IFCWALL", predefinedType="SOLIDWALL")
ifc = ifcopenshell.file()
run(
"A matching predefined type should pass",
facet=facet,
inst=ifc.createIfcWall(PredefinedType="SOLIDWALL"),
expected=True,
)
ifc = ifcopenshell.file()
run(
"A null predefined type should always fail a specified predefined types",
facet=facet,
inst=ifc.createIfcWall(),
expected=False,
)
ifc = ifcopenshell.file()
run(
"An entity not matching a specified predefined type will fail",
facet=facet,
inst=ifc.createIfcWall(PredefinedType="PARTITIONING"),
expected=False,
)
facet = Entity(name="IFCWALL", predefinedType="solidwall")
ifc = ifcopenshell.file()
run(
"A predefined type from an enumeration must be uppercase",
facet=facet,
inst=ifc.createIfcWall(PredefinedType="SOLIDWALL"),
expected=False,
)
facet = Entity(name="IFCWALL", predefinedType="WALDO")
ifc = ifcopenshell.file()
run(
"A predefined type may specify a user-defined object type",
facet=facet,
inst=ifc.createIfcWall(PredefinedType="USERDEFINED", ObjectType="WALDO"),
expected=True,
)
facet = Entity(name="IFCWALL", predefinedType="WALDO")
ifc = ifcopenshell.file()
run(
"User-defined types are checked case sensitively",
facet=facet,
inst=ifc.createIfcWall(PredefinedType="USERDEFINED", ObjectType="waldo"),
expected=False,
)
facet = Entity(name="IFCWALLTYPE", predefinedType="WALDO")
ifc = ifcopenshell.file()
run(
"A predefined type may specify a user-defined element type",
facet=facet,
inst=ifc.createIfcWallType(PredefinedType="USERDEFINED", ElementType="WALDO"),
expected=True,
)
facet = Entity(name="IFCTASKTYPE", predefinedType="TASKY")
ifc = ifcopenshell.file()
run(
"A predefined type may specify a user-defined process type",
facet=facet,
inst=ifc.createIfcTaskType(PredefinedType="USERDEFINED", ProcessType="TASKY"),
expected=True,
)
facet = Entity(name="IFCWALL", predefinedType="USERDEFINED")
ifc = ifcopenshell.file()
run(
"A predefined type may specify USERDEFINED itself",
facet=facet,
inst=ifc.createIfcWall(PredefinedType="USERDEFINED", ObjectType="WALDO"),
expected=True,
)
ifc = ifcopenshell.file()
wall = ifcopenshell.api.root.create_entity(ifc, ifc_class="IfcWall")
wall_type = ifcopenshell.api.root.create_entity(ifc, ifc_class="IfcWallType", predefined_type="X")
ifcopenshell.api.type.assign_type(ifc, related_objects=[wall], relating_type=wall_type)
facet = Entity(name="IFCWALL", predefinedType="X")
run("Inherited predefined types should pass", facet=facet, inst=wall, expected=True)
ifc = ifcopenshell.file()
wall = ifcopenshell.api.root.create_entity(ifc, ifc_class="IfcWall", predefined_type="X")
wall_type = ifcopenshell.api.root.create_entity(ifc, ifc_class="IfcWallType", predefined_type="NOTDEFINED")
ifcopenshell.api.type.assign_type(ifc, related_objects=[wall], relating_type=wall_type)
facet = Entity(name="IFCWALL", predefinedType="X")
run("Overridden predefined types should pass", facet=facet, inst=wall, expected=True)
restriction = Restriction(options={"enumeration": ["IFCWALL", "IFCSLAB"]})
facet = Entity(name=restriction)
ifc = ifcopenshell.file()
run("Entities can be specified as an enumeration 1/3", facet=facet, inst=ifc.createIfcWall(), expected=True)
ifc = ifcopenshell.file()
run("Entities can be specified as an enumeration 2/3", facet=facet, inst=ifc.createIfcSlab(), expected=True)
ifc = ifcopenshell.file()
run("Entities can be specified as an enumeration 3/3", facet=facet, inst=ifc.createIfcBeam(), expected=False)
restriction = Restriction(options={"pattern": "IFC.*TYPE"})
facet = Entity(name=restriction)
ifc = ifcopenshell.file()
run(
"Entities can be specified as a XSD regex pattern 1/2",
facet=facet,
inst=ifc.createIfcWall(),
expected=False,
)
ifc = ifcopenshell.file()
run(
"Entities can be specified as a XSD regex pattern 2/2",
facet=facet,
inst=ifc.createIfcWallType(PredefinedType="USERDEFINED"),
expected=True,
)
restriction = Restriction(options={"pattern": "FOO.*"})
facet = Entity(name="IFCWALL", predefinedType=restriction)
ifc = ifcopenshell.file()
wall = ifcopenshell.api.root.create_entity(ifc, ifc_class="IfcWall", predefined_type="FOOBAR")
run("Restrictions an be specified for the predefined type 1/3", facet=facet, inst=wall, expected=True)
ifc = ifcopenshell.file()
wall2 = ifcopenshell.api.root.create_entity(ifc, ifc_class="IfcWall", predefined_type="FOOBAZ")
run("Restrictions an be specified for the predefined type 2/3", facet=facet, inst=wall2, expected=True)
ifc = ifcopenshell.file()
wall3 = ifcopenshell.api.root.create_entity(ifc, ifc_class="IfcWall", predefined_type="BAZFOO")
run("Restrictions an be specified for the predefined type 3/3", facet=facet, inst=wall3, expected=False)
def test_ifc2x3_occurrence_type_mapping(self):
set_facet("entity")
ifc = ifcopenshell.file(schema="IFC2X3")
application = ifcopenshell.api.owner.add_application(ifc)
person = ifcopenshell.api.owner.add_person(
ifc, identification="LPARTEE", family_name="Partee", given_name="Leeable"
)
organisation = ifcopenshell.api.owner.add_organisation(
ifc, identification="AWB", name="Architects Without Ballpens"
)
user = ifcopenshell.api.owner.add_person_and_organisation(ifc, person=person, organisation=organisation)
ifcopenshell.api.owner.settings.get_user = lambda x: user
ifcopenshell.api.owner.settings.get_application = lambda x: application
element = ifcopenshell.api.root.create_entity(ifc, "IfcFlowTerminal")
element_type = ifcopenshell.api.root.create_entity(ifc, "IfcAirTerminalType")
ifcopenshell.api.type.assign_type(ifc, related_objects=[element], relating_type=element_type)
facet = Entity(name="IFCAIRTERMINAL")
assert facet.filter(ifc) == [element]
run("In IFC2X3 the type class is checked instead 1/2", facet=facet, inst=element, expected=True)
facet = Entity(name="IFCELECTRICAPPLIANCE")
assert facet.filter(ifc) == []
run("In IFC2X3 the type class is checked instead 2/2", facet=facet, inst=element, expected=False)
def test_to_string_required_applicability(self):
spec = ifctester.ids.Specification(name="Foo", minOccurs=1, maxOccurs="unbounded")
facet = Entity(name="IFCWALL")
assert facet.to_string("applicability", spec) == "All IFCWALL data"
def test_to_string_optional_applicability(self):
spec = ifctester.ids.Specification(name="Foo", minOccurs=0, maxOccurs="unbounded")
facet = Entity(name="IFCWALL")
assert facet.to_string("applicability", spec) == "All IFCWALL data"
def test_to_string_prohibited_applicability(self):
spec = ifctester.ids.Specification(name="Foo", minOccurs=0, maxOccurs=0)
facet = Entity(name="IFCWALL")
assert facet.to_string("applicability", spec) == "Shall not be IFCWALL data"
def test_to_string_ignored_requirement(self):
spec = ifctester.ids.Specification(name="Foo", minOccurs=0, maxOccurs=0)
facet = Entity(name="IFCWALL")
assert facet.to_string("requirement", spec) == "The requirement is not applicable"
def test_to_string_required_requirement(self):
spec = ifctester.ids.Specification(name="Foo")
facet = Entity(name="IFCWALL")
assert facet.to_string("requirement", spec) == "Shall be IFCWALL data"
class TestAttribute:
def test_creating_an_attribute_facet(self):
attribute = Attribute(name="name")
assert attribute.asdict("applicability") == {"name": {"simpleValue": "name"}}
attribute = Attribute(name="name", value="value")
assert attribute.asdict("applicability") == {"name": {"simpleValue": "name"}, "value": {"simpleValue": "value"}}
attribute = Attribute(name="name", value="value", cardinality="required", instructions="instructions")
assert attribute.asdict("requirement") == {
"name": {"simpleValue": "name"},
"value": {"simpleValue": "value"},
"@cardinality": "required",
"@instructions": "instructions",
}
def test_filtering_using_an_attribute_facet(self):
set_facet("attribute")
ifc = ifcopenshell.file()
facet = Attribute(name="Rabbit")
run("Invalid attribute names always fail", facet=facet, inst=ifc.createIfcWall(), expected=False)
ifc = ifcopenshell.file()
facet = Attribute(name="Name")
run(
"Attributes with a string value should pass",
facet=facet,
inst=ifc.createIfcWall(Name="Foobar"),
expected=True,
)
ifc = ifcopenshell.file()
facet = Attribute(name="Name")
element = ifc.createIfcWall(Name="Foobar")
run("A required facet checks all parameters as normal", facet=facet, inst=element, expected=True)
facet = Attribute(name="Name", cardinality="prohibited")
run("A prohibited facet returns the opposite of a required facet", facet=facet, inst=element, expected=False)
facet = Attribute(name="Name", cardinality="optional")
run(
"An optional facet only checks requirements if there is a value to check 1/2",
facet=facet,
inst=element,
expected=True,
)
facet = Attribute(name="Rabbit", cardinality="optional")
run(
"An optional facet only checks requirements if there is a value to check 2/2",
facet=facet,
inst=element,
expected=True,
)
ifc = ifcopenshell.file()
facet = Attribute(name="Name")
run("Attributes with null values always fail", facet=facet, inst=ifc.createIfcWall(), expected=False)
# The logic is that unfortunately most BIM users cannot differentiate between the two.
ifc = ifcopenshell.file()
run("Attributes with empty strings always fail", facet=facet, inst=ifc.createIfcWall(Name=""), expected=False)
facet = Attribute(name="CountValue")
ifc = ifcopenshell.file()
run(
"Attributes with a zero number have meaning and should pass",
facet=facet,
inst=ifc.createIfcQuantityCount(Name="Foobar", CountValue=0),
expected=True,
)
facet = Attribute(name="IsCritical")
ifc = ifcopenshell.file()
element = ifc.createIfcTaskTime(IsCritical=True)
run("Attributes with a boolean true should pass", facet=facet, inst=element, expected=True)
element.IsCritical = False
run("Attributes with a boolean false should pass", facet=facet, inst=element, expected=True)
facet = Attribute(name="RelatingPriorities")
ifc = ifcopenshell.file()
run(
"Attributes with an empty list always fail",
facet=facet,
inst=ifc.createIfcRelConnectsPathElements(
RelatingElement=ifc.createIfcWall(),
RelatedElement=ifc.createIfcWall(),
RelatingPriorities=[],
RelatedPriorities=[],
RelatedConnectionType="ATSTART",
RelatingConnectionType="ATEND",
),
expected=False,
)
facet = Attribute(name="LayerStyles")
ifc = ifcopenshell.file()
item = ifc.createIfcCartesianPoint([0.0, 0.0, 0.0])
layer = ifc.createIfcPresentationLayerWithStyle("Foo", None, [item], None, True, False, False, [])
run("Attributes with an empty set always fail", facet=facet, inst=layer, expected=False)
layer.LayerOn = "UNKNOWN"
facet = Attribute(name="LayerOn")
run("Attributes with a logical unknown always fail", facet=facet, inst=layer, expected=False)
facet = Attribute(name="ScheduleDuration")
ifc = ifcopenshell.file()
element = ifc.createIfcTaskTime(ScheduleDuration="P0D")
run("Attributes with a zero duration should pass", facet=facet, inst=element, expected=True)
facet = Attribute(name="TaskTime")
ifc = ifcopenshell.file()
element = ifc.createIfcTask(IsMilestone=True, TaskTime=ifc.createIfcTaskTime())
run("Attributes referencing an object should pass", facet=facet, inst=element, expected=True)
facet = Attribute(name="DiffuseColour")
ifc = ifcopenshell.file()
rgb = ifc.createIfcColourRgb(None, 1, 1, 1)
run(
"Attributes with a select referencing an object should pass",
facet=facet,
inst=ifc.createIfcSurfaceStyleRendering(
SurfaceColour=rgb, ReflectanceMethod="FLAT", DiffuseColour=ifc.createIfcColourRgb(None, 1, 1, 1)
),
expected=True,
)
ifc = ifcopenshell.file()
rgb = ifc.createIfcColourRgb(None, 1, 1, 1)
run(
"Attributes with a select referencing a primitive should pass",
facet=facet,
inst=ifc.createIfcSurfaceStyleRendering(
SurfaceColour=rgb, ReflectanceMethod="FLAT", DiffuseColour=ifc.createIfcNormalisedRatioMeasure(0.5)
),
expected=True,
)
ifc = ifcopenshell.file()
facet = Attribute(name="EngagedIn")
person = ifc.createIfcPerson()
organisation = ifc.createIfcOrganization(Name="Foo")
ifc.createIfcPersonAndOrganization(ThePerson=person, TheOrganization=organisation)
run("Inverse attributes cannot be checked and always fail", facet=facet, inst=person, expected=False)
ifc = ifcopenshell.file()
facet = Attribute(name="Dim")
run(
"Derived attributes cannot be checked and always fail",
facet=facet,
inst=ifc.createIfcCartesianPoint([0.0, 0.0, 0.0]),
expected=False,
)
facet = Attribute(name="Name", value="Foobar")
ifc = ifcopenshell.file()
run(
"Attributes should check strings case sensitively 1/2",
facet=facet,
inst=ifc.createIfcWall(Name="Foobar"),
expected=True,
)
ifc = ifcopenshell.file()
run(
"Attributes should check strings case sensitively 2/2",
facet=facet,
inst=ifc.createIfcWall(Name="foobar"),
expected=False,
)
ifc = ifcopenshell.file()
facet = Attribute(name="Name", value="♫Don'tÄrgerhôtelЊет")
run(
"Non-ascii characters are treated without encoding",
facet=facet,
inst=ifc.createIfcWall(Name="♫Don'tÄrgerhôtelЊет"),
expected=True,
)
facet = Attribute(name="TaskTime", value="Foobar")
ifc = ifcopenshell.file()
run(
"Value checks always fail for objects",
facet=facet,
inst=ifc.createIfcTask(IsMilestone=False, TaskTime=ifc.createIfcTaskTime()),
expected=False,
)
facet = Attribute(name="DiffuseColour", value="Foobar")
ifc = ifcopenshell.file()
rgb = ifc.createIfcColourRgb(None, 1, 1, 1)
run(
"Value checks always fail for selects",
facet=facet,
inst=ifc.createIfcSurfaceStyleRendering(
SurfaceColour=rgb, ReflectanceMethod="FLAT", DiffuseColour=ifc.createIfcNormalisedRatioMeasure(0.5)
),
expected=False,
)
facet = Attribute(name="Coordinates", value="Foobar")
ifc = ifcopenshell.file()
run(
"Value checks always fail for lists",
facet=facet,
inst=ifc.createIfcCartesianPoint([0.0, 0.0, 0.0]),
expected=False,
)
ifc = ifcopenshell.file()
ns = uuid.UUID("b59aa156-82a4-4b4c-a6e5-3d04a0236af9")
element = ifc.createIfcWall()
element.GlobalId = ifcopenshell.guid.compress(uuid.uuid5(ns, str(element.id())).hex)
facet = Attribute(name="GlobalId", value=element.GlobalId)
run(
"GlobalIds are treated as strings and not expanded",
facet=facet,
inst=element,
expected=True,
)
# 255 characters
identifier = "123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345"
facet = Attribute(name="Identification", value=identifier + "_extra_characters")
ifc = ifcopenshell.file()
run(
"IDS does not handle string truncation such as for identifiers",
facet=facet,
inst=ifc.createIfcPerson(Identification=identifier),
expected=False,
)
facet = Attribute(name="RefractionIndex", value="42")
ifc = ifcopenshell.file()
run(
"Numeric values are checked using type casting 1/4",
facet=facet,
inst=ifc.createIfcSurfaceStyleRefraction(RefractionIndex=42),
expected=True,
)
facet = Attribute(name="RefractionIndex", value="42.")
ifc = ifcopenshell.file()
run(
"Numeric values are checked using type casting 2/4",
facet=facet,
inst=ifc.createIfcSurfaceStyleRefraction(RefractionIndex=42.0),
expected=True,
)
facet = Attribute(name="RefractionIndex", value="42.0")
ifc = ifcopenshell.file()
run(
"Numeric values are checked using type casting 3/4",
facet=facet,
inst=ifc.createIfcSurfaceStyleRefraction(RefractionIndex=42.0),
expected=True,
)
facet = Attribute(name="RefractionIndex", value="42")
ifc = ifcopenshell.file()
run(
"Numeric values are checked using type casting 4/4",
facet=facet,
inst=ifc.createIfcSurfaceStyleRefraction(RefractionIndex=42.3),
expected=False,
)
facet = Attribute(name="RefractionIndex", value="42,3")
ifc = ifcopenshell.file()
run(
"Only specifically formatted numbers are allowed 1/4",
facet=facet,
inst=ifc.createIfcSurfaceStyleRefraction(RefractionIndex=42.3),
expected=False,
)
facet = Attribute(name="RefractionIndex", value="123,4.5")
ifc = ifcopenshell.file()
run(
"Only specifically formatted numbers are allowed 2/4",
facet=facet,
inst=ifc.createIfcSurfaceStyleRefraction(RefractionIndex=1234.5),
expected=False,
)
facet = Attribute(name="RefractionIndex", value="1.2345e3")
ifc = ifcopenshell.file()
run(
"Only specifically formatted numbers are allowed 3/4",
facet=facet,
inst=ifc.createIfcSurfaceStyleRefraction(RefractionIndex=1234.5),
expected=True,
)
facet = Attribute(name="RefractionIndex", value="1.2345E3")
ifc = ifcopenshell.file()
run(
"Only specifically formatted numbers are allowed 4/4",
facet=facet,
inst=ifc.createIfcSurfaceStyleRefraction(RefractionIndex=1234.5),
expected=True,
)
facet = Attribute(name="RefractionIndex", value="42.")
ifc = ifcopenshell.file()
run(
"Floating point numbers are compared with a 1e-6 tolerance 1/4",
facet=facet,
inst=ifc.createIfcSurfaceStyleRefraction(RefractionIndex=42.0 * (1.0 + 1e-6)),
expected=True,
)
run(
"Floating point numbers are compared with a 1e-6 tolerance 2/4",
facet=facet,
inst=ifc.createIfcSurfaceStyleRefraction(RefractionIndex=42.0 * (1.0 - 1e-6)),
expected=True,
)
run(
"Floating point numbers are compared with a 1e-6 tolerance 3/4",
facet=facet,
inst=ifc.createIfcSurfaceStyleRefraction(RefractionIndex=42.0 * (1.0 + 2e-6)),
expected=False,
)
run(
"Floating point numbers are compared with a 1e-6 tolerance 4/4",
facet=facet,
inst=ifc.createIfcSurfaceStyleRefraction(RefractionIndex=42.0 * (1.0 - 2e-6)),
expected=False,
)
facet = Attribute(name="NumberOfRisers", value="42")
ifc = ifcopenshell.file()
run(
"Integers follow the same rules as numbers",
facet=facet,
inst=ifc.createIfcStairFlight(NumberOfRisers=42),
expected=True,
)
facet = Attribute(name="NumberOfRisers", value="42.0")
ifc = ifcopenshell.file()
run(
"Integers follow the same rules as numbers 2/2",
facet=facet,
inst=ifc.createIfcStairFlight(NumberOfRisers=42),
expected=True,
)
facet = Attribute(name="NumberOfRisers", value="42.3")
ifc = ifcopenshell.file()
run(
"Specifying a float when the value is an integer will fail",
facet=facet,
inst=ifc.createIfcStairFlight(NumberOfRisers=42),
expected=False,
)
facet = Attribute(name="IsMilestone", value="true")
ifc = ifcopenshell.file()
element = ifc.createIfcTask(IsMilestone=False)
run("Booleans must be specified as lowercase strings 1/3", facet=facet, inst=element, expected=False)
facet = Attribute(name="IsMilestone", value="false")
run("Booleans must be specified as lowercase strings 2/3", facet=facet, inst=element, expected=True)
facet = Attribute(name="IsMilestone", value="False")
run("Booleans must be specified as lowercase strings 2/3", facet=facet, inst=element, expected=False)
facet = Attribute(name="IsMilestone", value="0")
run("Booleans can be specified as a 0 or 1 1/2", facet=facet, inst=element, expected=True)
facet = Attribute(name="IsMilestone", value="1")
run("Booleans can be specified as a 0 or 1 2/2", facet=facet, inst=element, expected=False)
facet = Attribute(name="EditionDate", value="2022-01-01")
ifc = ifcopenshell.file()
run(
"Dates are treated as strings 1/2",
facet=facet,
inst=ifc.createIfcClassification(Name="Name", EditionDate="2022-01-01"),
expected=True,
)
ifc = ifcopenshell.file()
run(
"Dates are treated as strings 1/2",
facet=facet,
inst=ifc.createIfcClassification(Name="Name", EditionDate="2022-01-01+00:00"),
expected=False,
)
facet = Attribute(name="ScheduleDuration", value="PT16H")
ifc = ifcopenshell.file()
run(
"Durations are treated as strings 1/2",
facet=facet,
inst=ifc.createIfcTaskTime(Name="Name", ScheduleDuration="PT16H"),
expected=True,
)
ifc = ifcopenshell.file()
run(
"Durations are treated as strings 2/2",
facet=facet,
inst=ifc.createIfcTaskTime(Name="Name", ScheduleDuration="P2D"),
expected=False,
)
restriction = Restriction(options={"pattern": ".*Name.*"})
facet = Attribute(name=restriction)
ifc = ifcopenshell.file()
run(
"Name restrictions will match any result 1/3",
facet=facet,
inst=ifc.createIfcMaterialLayerSet(
MaterialLayers=[ifc.createIfcMaterialLayer(LayerThickness=1)], LayerSetName="Foo"
),
expected=True,
)
restriction = Restriction(options={"enumeration": ["Name", "Description"]})
facet = Attribute(name=restriction)
ifc = ifcopenshell.file()
run(
"Name restrictions will match any result 2/3",
facet=facet,
inst=ifc.createIfcWall(Name="Foo"),
expected=True,
)
ifc = ifcopenshell.file()
run(
"Name restrictions will match any result 3/3",
facet=facet,
inst=ifc.createIfcWall(Description="Bar"),
expected=True,
)
restriction = Restriction(options={"enumeration": ["Foo", "Bar"]})
facet = Attribute(name="Name", value=restriction)
ifc = ifcopenshell.file()
run("Value restrictions may be used 1/3", facet=facet, inst=ifc.createIfcWall(Name="Foo"), expected=True)
ifc = ifcopenshell.file()
run("Value restrictions may be used 2/3", facet=facet, inst=ifc.createIfcWall(Name="Bar"), expected=True)
ifc = ifcopenshell.file()
run("Value restrictions may be used 3/3", facet=facet, inst=ifc.createIfcWall(Name="Foobar"), expected=False)
ifc = ifcopenshell.file()
wall = ifcopenshell.api.root.create_entity(ifc, ifc_class="IfcWall")
wall_type = ifcopenshell.api.root.create_entity(ifc, ifc_class="IfcWallType")
ifcopenshell.api.type.assign_type(ifc, related_objects=[wall], relating_type=wall_type)
wall_type.Description = "Foobar"
facet = Attribute(name="Description", value="Foobar")
run("Attributes are not inherited by the occurrence", facet=facet, inst=wall, expected=False)
restriction = Restriction(options={"enumeration": ["42", "43"]})
facet = Attribute(name="RefractionIndex", value=restriction)
ifc = ifcopenshell.file()
run(
"Typecast checking may also occur within enumeration restrictions",
facet=facet,
inst=ifc.createIfcSurfaceStyleRefraction(RefractionIndex=42),
expected=True,
)
restriction = Restriction(options={"minInclusive": 42, "maxInclusive": 42}, base="decimal")
facet = Attribute(name="RefractionIndex", value=restriction)
ifc = ifcopenshell.file()
run(
"Strict numeric checking may be done with a bounds restriction",
facet=facet,
inst=ifc.createIfcSurfaceStyleRefraction(RefractionIndex=42),
expected=True,
)
class TestClassification:
def test_creating_a_classification_facet(self):
facet = Classification(system="system")
assert facet.asdict("requirement") == {"system": {"simpleValue": "system"}, "@cardinality": "required"}
facet = Classification(value="value", system="system")
assert facet.asdict("requirement") == {
"value": {"simpleValue": "value"},
"system": {"simpleValue": "system"},
"@cardinality": "required",
}
facet = Classification(
value="value",
system="system",
uri="https://test.com",
cardinality="required",
instructions="instructions",
)
assert facet.asdict("requirement") == {
"value": {"simpleValue": "value"},
"system": {"simpleValue": "system"},
"@uri": "https://test.com",
"@cardinality": "required",
"@instructions": "instructions",
}
def test_filtering_using_a_classification_facet(self):
set_facet("classification")
library = ifcopenshell.file()
system_a = library.createIfcClassification(Name="Foobar")
ref1 = library.createIfcClassificationReference(Identification="1", ReferencedSource=system_a)
ref11 = library.createIfcClassificationReference(Identification="11", ReferencedSource=ref1)
ref2 = library.createIfcClassificationReference(Identification="2", ReferencedSource=system_a)
ref22 = library.createIfcClassificationReference(Identification="22", ReferencedSource=ref2)
system_b = library.createIfcClassification(Name="Foobaz")
refx = library.createIfcClassificationReference(Identification="X", ReferencedSource=system_b)
ifc = ifcopenshell.file()
project = ifc.createIfcProject()
system_a = ifcopenshell.api.classification.add_classification(ifc, classification=system_a)
element0 = ifcopenshell.api.root.create_entity(ifc, ifc_class="IfcWall")
element1 = ifcopenshell.api.root.create_entity(ifc, ifc_class="IfcSlab")
ifcopenshell.api.classification.add_reference(ifc, products=[element1], reference=ref1, classification=system_a)
element11 = ifcopenshell.api.root.create_entity(ifc, ifc_class="IfcColumn")
ifcopenshell.api.classification.add_reference(
ifc, products=[element11], reference=ref11, classification=system_a
)
element22 = ifcopenshell.api.root.create_entity(ifc, ifc_class="IfcBeam")
ifcopenshell.api.classification.add_reference(
ifc,
products=[element22],
reference=ref22,
classification=system_a,
is_lightweight=False,
)
material = ifc.createIfcMaterial(Name="Material")
ifcopenshell.api.classification.add_reference(ifc, products=[material], reference=ref1, classification=system_a)
facet = Classification(system="Foobar")
run(
"A classification facet with no value matches any classification 1/2",
facet=facet,
inst=element0,
expected=False,
)
run(
"A classification facet with no value matches any classification 2/2",
facet=facet,
inst=element1,
expected=True,
)
run("A required facet checks all parameters as normal", facet=facet, inst=element1, expected=True)
facet = Classification(system="Foobar", cardinality="prohibited")
run("A prohibited facet returns the opposite of a required facet", facet=facet, inst=element1, expected=False)
facet = Classification(system="Foobar", cardinality="optional")
run(
"An optional facet only checks requirements if there is a value to check 1/2",
facet=facet,
inst=element0,
expected=True,
)
facet = Classification(system="Foobar", cardinality="optional")
run(
"An optional facet only checks requirements if there is a value to check 2/2",
facet=facet,
inst=element1,
expected=True,
)
facet = Classification(system="Foobar", value="1")
run(
"Values should match exactly if lightweight classifications are used",
facet=facet,
inst=element1,
expected=True,
)
facet = Classification(system="Foobar", value="2")
run(
"Values match subreferences if full classifications are used (e.g. EF_25_10 should match EF_25_10_25, EF_25_10_30, etc)",
facet=facet,
inst=element22,
expected=True,
)
facet = Classification(system="Foobar", value="1")
run(
"Non-rooted resources that have external classification references should also pass",
facet=facet,
inst=material,
expected=True,
)
facet = Classification(system="Foobar")
run("Systems should match exactly 1/5", facet=facet, inst=project, expected=True)
run("Systems should match exactly 2/5", facet=facet, inst=element0, expected=False)
run("Systems should match exactly 3/5", facet=facet, inst=element1, expected=True)
run("Systems should match exactly 4/5", facet=facet, inst=element11, expected=True)
run("Systems should match exactly 5/5", facet=facet, inst=element22, expected=True)
restriction = Restriction(options={"pattern": "1.*"})
facet = Classification(system="Foobar", value=restriction)
run("Restrictions can be used for values 1/3", facet=facet, inst=element1, expected=True)
run("Restrictions can be used for values 2/3", facet=facet, inst=element11, expected=True)
run("Restrictions can be used for values 3/3", facet=facet, inst=element22, expected=False)
restriction = Restriction(options={"pattern": "Foo.*"})
facet = Classification(system=restriction)
run("Restrictions can be used for systems 1/2", facet=facet, inst=element0, expected=False)
run("Restrictions can be used for systems 2/2", facet=facet, inst=element1, expected=True)
facet = Classification(system="Foobar", value="1")
run(
"Both system and value must match (all, not any) if specified 1/2",
facet=facet,
inst=element1,
expected=True,
)
run(
"Both system and value must match (all, not any) if specified 2/2",
facet=facet,
inst=element11,
expected=False,
)
# IFC doesn't yet formally specify how inheritance and overrides work here. We follow these rules:
# https://github.com/buildingSMART/IFC4.3.x-development/issues/475
wall = ifcopenshell.api.root.create_entity(ifc, ifc_class="IfcWall")
wall_type = ifcopenshell.api.root.create_entity(ifc, ifc_class="IfcWallType")
ifcopenshell.api.type.assign_type(ifc, related_objects=[wall], relating_type=wall_type)
ifcopenshell.api.classification.add_reference(ifc, products=[wall], reference=ref11, classification=system_a)
ifcopenshell.api.classification.add_reference(
ifc, products=[wall_type], reference=ref22, classification=system_a
)
system_b = ifcopenshell.api.classification.add_classification(ifc, classification=system_b)
ifcopenshell.api.classification.add_reference(
ifc, products=[wall_type], reference=refx, classification=system_b
)
facet = Classification(system="Foobar", value="11")
run("Occurrences override the type classification per system 1/3", facet=facet, inst=wall, expected=True)
facet = Classification(system="Foobar", value="22")
run("Occurrences override the type classification per system 2/3", facet=facet, inst=wall, expected=False)
facet = Classification(system="Foobaz", value="X")
run("Occurrences override the type classification per system 3/3", facet=facet, inst=wall, expected=True)
class TestProperty:
def test_creating_a_property_facet(self):
facet = Property()
assert facet.asdict("requirement") == {
"propertySet": {"simpleValue": "Property_Set"},
"baseName": {"simpleValue": "PropertyName"},
"@cardinality": "required",
}
facet = Property(
propertySet="propertySet",
baseName="baseName",
value="value",
dataType="dataType",
uri="https://test.com",
cardinality="required",
instructions="instructions",
)
assert facet.asdict("requirement") == {
"propertySet": {"simpleValue": "propertySet"},
"baseName": {"simpleValue": "baseName"},
"value": {"simpleValue": "value"},
"@dataType": "DATATYPE",
"@uri": "https://test.com",
"@cardinality": "required",
"@instructions": "instructions",
}
def test_filtering_using_a_property_facet(self):
set_facet("property")
ifc = self.setup_ifc()
facet = Property(propertySet="Foo_Bar", baseName="Foo", dataType="IFCLABEL")
element = ifcopenshell.api.root.create_entity(ifc, ifc_class="IfcWall")
run("Elements with no properties always fail", facet=facet, inst=element, expected=False)
pset = ifcopenshell.api.pset.add_pset(ifc, product=element, name="Foo_Bar")
ifcopenshell.api.pset.edit_pset(ifc, pset=pset, properties={"AnotherProperty": "AnotherValue"})
run("Elements with a matching pset but no property also fail", facet=facet, inst=element, expected=False)
ifcopenshell.api.pset.edit_pset(ifc, pset=pset, properties={"Foo": None}, should_purge=False)
run("Properties with a null value fail", facet=facet, inst=element, expected=False)
restriction = Restriction(options={"pattern": "Fo.*"})
facet = Property(propertySet="Foo_Bar", baseName=restriction, dataType="IFCLABEL")
run("Pattern matched properties with a null value fail", facet=facet, inst=element, expected=False)
ifcopenshell.api.pset.edit_pset(ifc, pset=pset, properties={"Foo": "Bar"})
run("A name check will match any property with any string value", facet=facet, inst=element, expected=True)
ifc = self.setup_ifc()
facet = Property(propertySet="Foo_Bar", baseName="Foo", dataType="IFCLABEL")
element = ifcopenshell.api.root.create_entity(ifc, ifc_class="IfcWall")
pset = ifcopenshell.api.pset.add_pset(ifc, product=element, name="Foo_Bar")
ifcopenshell.api.pset.edit_pset(ifc, pset=pset, properties={"Foo": "Bar"})
run("A required facet checks all parameters as normal", facet=facet, inst=element, expected=True)
facet = Property(propertySet="Foo_Bar", baseName="Foo", dataType="IFCLABEL", cardinality="prohibited")
run("A prohibited facet returns the opposite of a required facet", facet=facet, inst=element, expected=False)
facet = Property(propertySet="Foo_Bar", baseName="Foo", dataType="IFCLABEL", cardinality="optional")
run(
"An optional facet only checks requirements if there is a value to check 1/2",
facet=facet,
inst=element,
expected=True,
)
facet = Property(propertySet="Foo_Bar", baseName="Bar", dataType="IFCLABEL", cardinality="optional")
run(
"An optional facet only checks requirements if there is a value to check 2/2",
facet=facet,
inst=element,
expected=True,
)
ifcopenshell.api.pset.edit_pset(ifc, pset=pset, properties={"Foo": ""})
facet = Property(propertySet="Foo_Bar", baseName="Foo", dataType="IFCLABEL")
run("An empty string is considered falsey and will not pass", facet=facet, inst=element, expected=False)
ifcopenshell.api.pset.edit_pset(ifc, pset=pset, properties={"Foo": ifc.createIfcLogical("UNKNOWN")})
facet = Property(propertySet="Foo_Bar", baseName="Foo", dataType="IFCLOGICAL")
run("A logical unknown is considered falsey and will not pass", facet=facet, inst=element, expected=False)
ifcopenshell.api.pset.edit_pset(ifc, pset=pset, properties={"Foo": ifc.createIfcDuration("P0D")})
facet = Property(propertySet="Foo_Bar", baseName="Foo", dataType="IFCDURATION")
run("A zero duration will pass", facet=facet, inst=element, expected=True)
facet = Property(propertySet="Foo_Bar", baseName="Foo", dataType="IFCBOOLEAN")
ifcopenshell.api.pset.edit_pset(ifc, pset=pset, properties={"Foo": ifc.createIfcBoolean(True)})
run("A property set to true will pass a name check", facet=facet, inst=element, expected=True)
ifcopenshell.api.pset.edit_pset(ifc, pset=pset, properties={"Foo": False})
run(
"A property set to false is still considered a value and will pass a name check",
facet=facet,
inst=element,
expected=True,
)
ifc = self.setup_ifc()
facet = Property(propertySet="Foo_Bar", baseName="Foo", value="Bar", dataType="IFCLABEL")
element = ifcopenshell.api.root.create_entity(ifc, ifc_class="IfcWall")
pset = ifcopenshell.api.pset.add_pset(ifc, product=element, name="Foo_Bar")
ifcopenshell.api.pset.edit_pset(ifc, pset=pset, properties={"Foo": "Bar"})
run("Specifying a value performs a case-sensitive match 1/2", facet=facet, inst=element, expected=True)
ifcopenshell.api.pset.edit_pset(ifc, pset=pset, properties={"Foo": "bar"})
run("Specifying a value performs a case-sensitive match 2/2", facet=facet, inst=element, expected=False)
ifcopenshell.api.pset.edit_pset(ifc, pset=pset, properties={"Foo": "Baz"})
run("Specifying a value fails against different values", facet=facet, inst=element, expected=False)
facet = Property(propertySet="Foo_Bar", baseName="Foo", value="♫Don'tÄrgerhôtelЊет", dataType="IFCLABEL")
ifcopenshell.api.pset.edit_pset(ifc, pset=pset, properties={"Foo": "♫Don'tÄrgerhôtelЊет"})
run("Non-ascii characters are treated without encoding", facet=facet, inst=element, expected=True)
identifier = "123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345"
facet = Property(
propertySet="Foo_Bar", baseName="Foo", value=identifier + "_extra_characters", dataType="IFCIDENTIFIER"
)
ifcopenshell.api.pset.edit_pset(ifc, pset=pset, properties={"Foo": ifc.createIfcIdentifier(identifier)})
run("IDS does not handle string truncation such as for identifiers", facet=facet, inst=element, expected=False)
ifc = self.setup_ifc()
facet = Property(propertySet="Foo_Bar", baseName="Foo", value="1", dataType="IFCLABEL")
element = ifcopenshell.api.root.create_entity(ifc, ifc_class="IfcWall")
pset = ifcopenshell.api.pset.add_pset(ifc, product=element, name="Foo_Bar")
ifcopenshell.api.pset.edit_pset(ifc, pset=pset, properties={"Foo": "1"})
run("A number specified as a string is treated as a string", facet=facet, inst=element, expected=True)
facet = Property(propertySet="Foo_Bar", baseName="Foo", value="42", dataType="IFCINTEGER")
ifcopenshell.api.pset.edit_pset(ifc, pset=pset, properties={"Foo": ifc.createIfcInteger(42)})
run("Integer values are checked using type casting 1/4", facet=facet, inst=element, expected=True)
facet = Property(propertySet="Foo_Bar", baseName="Foo", value="42.", dataType="IFCINTEGER")
run("Integer values are checked using type casting 2/4", facet=facet, inst=element, expected=True)
facet = Property(propertySet="Foo_Bar", baseName="Foo", value="42.0", dataType="IFCINTEGER")
run("Integer values are checked using type casting 3/4", facet=facet, inst=element, expected=True)
facet = Property(propertySet="Foo_Bar", baseName="Foo", value="42.3", dataType="IFCINTEGER")
run("Integer values are checked using type casting 4/4", facet=facet, inst=element, expected=False)
facet = Property(propertySet="Foo_Bar", baseName="Foo", value="42", dataType="IFCREAL")
ifcopenshell.api.pset.edit_pset(ifc, pset=pset, properties={"Foo": ifc.createIfcReal(42.0)})
run("Real values are checked using type casting 1/3", facet=facet, inst=element, expected=True)
facet = Property(propertySet="Foo_Bar", baseName="Foo", value="42.0", dataType="IFCREAL")
run("Real values are checked using type casting 2/3", facet=facet, inst=element, expected=True)
facet = Property(propertySet="Foo_Bar", baseName="Foo", value="42.3", dataType="IFCREAL")
ifcopenshell.api.pset.edit_pset(ifc, pset=pset, properties={"Foo": ifc.createIfcReal(42.3)})
run("Real values are checked using type casting 3/3", facet=facet, inst=element, expected=True)
facet = Property(propertySet="Foo_Bar", baseName="Foo", value="42,3", dataType="IFCREAL")
ifcopenshell.api.pset.edit_pset(ifc, pset=pset, properties={"Foo": ifc.createIfcReal(42.3)})
run("Only specifically formatted numbers are allowed 1/4", facet=facet, inst=element, expected=False)
facet = Property(propertySet="Foo_Bar", baseName="Foo", value="123,4.5", dataType="IFCREAL")
ifcopenshell.api.pset.edit_pset(ifc, pset=pset, properties={"Foo": ifc.createIfcReal(1234.5)})
run("Only specifically formatted numbers are allowed 2/4", facet=facet, inst=element, expected=False)
facet = Property(propertySet="Foo_Bar", baseName="Foo", value="1.2345e3", dataType="IFCREAL")
run("Only specifically formatted numbers are allowed 3/4", facet=facet, inst=element, expected=True)
facet = Property(propertySet="Foo_Bar", baseName="Foo", value="1.2345E3", dataType="IFCREAL")
run("Only specifically formatted numbers are allowed 4/4", facet=facet, inst=element, expected=True)
facet = Property(propertySet="Foo_Bar", baseName="Foo", value="42.", dataType="IFCREAL")
ifcopenshell.api.pset.edit_pset(ifc, pset=pset, properties={"Foo": ifc.createIfcReal(42.0 * (1.0 + 1e-6))})
run("Floating point numbers are compared with a 1e-6 tolerance 1/4", facet=facet, inst=element, expected=True)
ifcopenshell.api.pset.edit_pset(ifc, pset=pset, properties={"Foo": ifc.createIfcReal(42.0 * (1.0 - 1e-6))})
run("Floating point numbers are compared with a 1e-6 tolerance 2/4", facet=facet, inst=element, expected=True)
ifcopenshell.api.pset.edit_pset(ifc, pset=pset, properties={"Foo": ifc.createIfcReal(42.0 * (1.0 + 2e-6))})
run("Floating point numbers are compared with a 1e-6 tolerance 3/4", facet=facet, inst=element, expected=False)
ifcopenshell.api.pset.edit_pset(ifc, pset=pset, properties={"Foo": ifc.createIfcReal(42.0 * (1.0 - 2e-6))})
run("Floating point numbers are compared with a 1e-6 tolerance 4/4", facet=facet, inst=element, expected=False)
facet = Property(propertySet="Foo_Bar", baseName="Foo", value="true", dataType="IFCBOOLEAN")
ifcopenshell.api.pset.edit_pset(ifc, pset=pset, properties={"Foo": ifc.createIfcBoolean(False)})
run("Booleans must be specified as uppercase strings 1/3", facet=facet, inst=element, expected=False)
facet = Property(propertySet="Foo_Bar", baseName="Foo", value="false", dataType="IFCBOOLEAN")
run("Booleans must be specified as uppercase strings 2/3", facet=facet, inst=element, expected=True)
facet = Property(propertySet="Foo_Bar", baseName="Foo", value="False", dataType="IFCBOOLEAN")
run("Booleans must be specified as uppercase strings 3/3", facet=facet, inst=element, expected=False)
facet = Property(propertySet="Foo_Bar", baseName="Foo", value="0", dataType="IFCBOOLEAN")
run("Booleans can be specified as as a 0 or 1 1/2", facet=facet, inst=element, expected=True)
facet = Property(propertySet="Foo_Bar", baseName="Foo", value="1", dataType="IFCBOOLEAN")
run("Booleans can be specified as as a 0 or 1 2/2", facet=facet, inst=element, expected=False)
facet = Property(propertySet="Foo_Bar", baseName="Foo", value="2022-01-01", dataType="IFCDATE")
ifcopenshell.api.pset.edit_pset(ifc, pset=pset, properties={"Foo": ifc.createIfcDate("2022-01-01")})
run("Dates are treated as strings 1/2", facet=facet, inst=element, expected=True)
ifcopenshell.api.pset.edit_pset(ifc, pset=pset, properties={"Foo": ifc.createIfcDate("2022-01-01+00:00")})
run("Dates are treated as strings 2/2", facet=facet, inst=element, expected=False)
facet = Property(propertySet="Foo_Bar", baseName="Foo", value="PT16H", dataType="IFCDURATION")
ifcopenshell.api.pset.edit_pset(ifc, pset=pset, properties={"Foo": ifc.createIfcDuration("PT16H")})
run("Durations are treated as strings 1/2", facet=facet, inst=element, expected=True)
ifcopenshell.api.pset.edit_pset(ifc, pset=pset, properties={"Foo": ifc.createIfcDuration("P2D")})
run("Durations are treated as strings 1/2", facet=facet, inst=element, expected=False)
ifc = self.setup_ifc()
element = ifcopenshell.api.root.create_entity(ifc, ifc_class="IfcWall")
pset = ifcopenshell.api.pset.add_pset(ifc, product=element, name="Pset_WallCommon")
pset_template = ifcopenshell.util.pset.get_template("IFC4").get_by_name("Pset_WallCommon")
ifcopenshell.api.pset.edit_pset(
ifc,
pset=pset,
properties={"Status": ["EXISTING", "DEMOLISH"]},
pset_template=pset_template,
)
facet = Property(propertySet="Pset_WallCommon", baseName="Status", value="EXISTING", dataType="IFCLABEL")
run("Any matching value in an enumerated property will pass 1/3", facet=facet, inst=element, expected=True)
facet = Property(propertySet="Pset_WallCommon", baseName="Status", value="DEMOLISH", dataType="IFCLABEL")
run("Any matching value in an enumerated property will pass 2/3", facet=facet, inst=element, expected=True)
facet = Property(propertySet="Pset_WallCommon", baseName="Status", value="NEW", dataType="IFCLABEL")
run("Any matching value in an enumerated property will pass 3/3", facet=facet, inst=element, expected=False)
ifc = self.setup_ifc()
element = ifcopenshell.api.root.create_entity(ifc, ifc_class="IfcWall")
pset = ifcopenshell.api.pset.add_pset(ifc, product=element, name="Foo_Bar")
list_property = ifc.createIfcPropertyListValue(
Name="Foo", ListValues=[ifc.createIfcLabel("X"), ifc.createIfcLabel("Y")]
)
pset.HasProperties = [list_property]
facet = Property(propertySet="Foo_Bar", baseName="Foo", value="X", dataType="IFCLABEL")
run("Any matching value in a list property will pass 1/3", facet=facet, inst=element, expected=True)
facet = Property(propertySet="Foo_Bar", baseName="Foo", value="Y", dataType="IFCLABEL")
run("Any matching value in a list property will pass 2/3", facet=facet, inst=element, expected=True)
facet = Property(propertySet="Foo_Bar", baseName="Foo", value="Z", dataType="IFCLABEL")
run("Any matching value in a list property will pass 3/3", facet=facet, inst=element, expected=False)
ifc = self.setup_ifc()
element = ifcopenshell.api.root.create_entity(ifc, ifc_class="IfcWall")
pset = ifcopenshell.api.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", baseName="Foo", value="1", dataType="IFCLENGTHMEASURE")
run("Any matching value in a bounded property will pass 1/4", facet=facet, inst=element, expected=True)
facet = Property(propertySet="Foo_Bar", baseName="Foo", value="5", dataType="IFCLENGTHMEASURE")
run("Any matching value in a bounded property will pass 2/4", facet=facet, inst=element, expected=True)
facet = Property(propertySet="Foo_Bar", baseName="Foo", value="3", dataType="IFCLENGTHMEASURE")
run("Any matching value in a bounded property will pass 3/4", facet=facet, inst=element, expected=True)
facet = Property(propertySet="Foo_Bar", baseName="Foo", value="2", dataType="IFCLENGTHMEASURE")
run("Any matching value in a bounded property will pass 4/4", facet=facet, inst=element, expected=False)
ifc = self.setup_ifc()
element = ifcopenshell.api.root.create_entity(ifc, ifc_class="IfcWall")
pset = ifcopenshell.api.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", baseName="Foo", value="X", dataType="IFCLABEL")
run("Any matching value in a table property will pass 1/3", facet=facet, inst=element, expected=True)
facet = Property(propertySet="Foo_Bar", baseName="Foo", value="1", dataType="IFCLENGTHMEASURE")
run("Any matching value in a table property will pass 2/3", facet=facet, inst=element, expected=True)
facet = Property(propertySet="Foo_Bar", baseName="Foo", value="Y", dataType="IFCLABEL")
run("Any matching value in a table property will pass 3/3", facet=facet, inst=element, expected=False)
ifc = self.setup_ifc()
element = ifcopenshell.api.root.create_entity(ifc, ifc_class="IfcWall")
pset = ifcopenshell.api.pset.add_pset(ifc, product=element, name="Foo_Bar")
pset.HasProperties = [ifc.createIfcPropertyReferenceValue(Name="Foo")]
facet = Property(propertySet="Foo_Bar", baseName="Foo", dataType="IFCLABEL")
run("Reference properties are treated as objects and not supported", facet=facet, inst=element, expected=False)
ifc = self.setup_ifc()
element = ifcopenshell.api.root.create_entity(ifc, ifc_class="IfcDoor")
pset = ifc.create_entity(
"IfcDoorPanelProperties",
GlobalId=ifcopenshell.guid.new(),
Name="Foo_Bar",
PanelOperation="SWINGING",
PanelPosition="LEFT",
)
ifc.create_entity(
"IfcRelDefinesByProperties",
GlobalId=ifcopenshell.guid.new(),
RelatedObjects=[element],
RelatingPropertyDefinition=pset,
)
facet = Property(
propertySet="Foo_Bar", baseName="PanelOperation", value="SWINGING", dataType="IFCDOORPANELOPERATIONENUM"
)
run("Predefined properties are supported but discouraged 1/2", facet=facet, inst=element, expected=True)
facet = Property(
propertySet="Foo_Bar", baseName="PanelOperation", value="SWONGING", dataType="IFCDOORPANELOPERATIONENUM"
)
run("Predefined properties are supported but discouraged 2/2", facet=facet, inst=element, expected=False)
ifc = self.setup_ifc()
facet = Property(propertySet="Foo_Bar", baseName="Foo", dataType="IFCLENGTHMEASURE")
element = ifcopenshell.api.root.create_entity(ifc, ifc_class="IfcWall")
qto = ifcopenshell.api.pset.add_qto(ifc, product=element, name="Foo_Bar")
ifcopenshell.api.pset.edit_qto(ifc, qto=qto, properties={"Foo": ifc.createIfcLengthMeasure(42)})
run("A name check will match any quantity with any value", facet=facet, inst=element, expected=True)
facet = Property(propertySet="Foo_Bar", baseName="Foo", dataType="IFCAREAMEASURE")
run("Quantities must also match the appropriate measure", facet=facet, inst=element, expected=False)
ifc = self.setup_ifc()
element = ifcopenshell.api.root.create_entity(ifc, ifc_class="IfcWall")
pset = ifcopenshell.api.pset.add_pset(ifc, product=element, name="Foo_Bar")
complex_property = ifc.createIfcComplexProperty(Name="Foo", UsageName="RabbitAgilityTraining")
ifcopenshell.api.pset.edit_pset(ifc, pset=complex_property, properties={"Rabbits": "Awesome"})
pset.HasProperties = [complex_property]
facet = Property(propertySet="Foo_Bar", baseName="Foo", dataType="IFCLABEL")
run("Complex properties are not supported 1/2", facet=facet, inst=element, expected=False)
facet = Property(propertySet="Foo", baseName="Rabbits", dataType="IFCLABEL")
run("Complex properties are not supported 2/2", facet=facet, inst=element, expected=False)
ifc = self.setup_ifc()
element = ifcopenshell.api.root.create_entity(ifc, ifc_class="IfcWall")
qto = ifcopenshell.api.pset.add_qto(ifc, product=element, name="Foo_Bar")
complex_quantity = ifc.createIfcPhysicalComplexQuantity(Name="Foo", Discrimination="FurThickness")
ifcopenshell.api.pset.edit_qto(
ifc, qto=complex_quantity, properties={"MyLength": ifc.createIfcLengthMeasure(42)}
)
qto.Quantities = [complex_quantity]
facet = Property(propertySet="Foo_Bar", baseName="Foo", dataType="IFCLENGTHMEASURE")
run("Complex properties are not supported 1/2", facet=facet, inst=element, expected=False)
facet = Property(propertySet="Foo", baseName="MyLength", dataType="IFCLENGTHMEASURE")
run("Complex properties are not supported 2/2", facet=facet, inst=element, expected=False)
ifc = self.setup_ifc()
restriction = Restriction(options={"pattern": "Foo_.*"})
facet = Property(propertySet=restriction, baseName="Foo", dataType="IFCLABEL")
element = ifcopenshell.api.root.create_entity(ifc, ifc_class="IfcWall")
pset = ifcopenshell.api.pset.add_pset(ifc, product=element, name="Foo_Bar")
ifcopenshell.api.pset.edit_pset(ifc, pset=pset, properties={"Foo": "Bar"})
run("All matching property sets must satisfy requirements 1/3", facet=facet, inst=element, expected=True)
pset = ifcopenshell.api.pset.add_pset(ifc, product=element, name="Foo_Baz")
ifcopenshell.api.pset.edit_pset(ifc, pset=pset, properties={"AnotherProperty": "AnotherValue"})
run("All matching property sets must satisfy requirements 2/3", facet=facet, inst=element, expected=False)
ifcopenshell.api.pset.edit_pset(ifc, pset=pset, properties={"Foo": "Bar"})
run("All matching property sets must satisfy requirements 3/3", facet=facet, inst=element, expected=True)
ifc = self.setup_ifc()
restriction = Restriction(options={"pattern": "Foo.*"})
facet = Property(propertySet="Foo_Bar", baseName=restriction, value="x", dataType="IFCLABEL")
element = ifcopenshell.api.root.create_entity(ifc, ifc_class="IfcWall")
pset = ifcopenshell.api.pset.add_pset(ifc, product=element, name="Foo_Bar")
ifcopenshell.api.pset.edit_pset(ifc, pset=pset, properties={"Foobar": "x"})
run("All matching properties must satisfy requirements 1/3", facet=facet, inst=element, expected=True)
ifcopenshell.api.pset.edit_pset(ifc, pset=pset, properties={"Foobar": "x", "Foobaz": "x"})
run("All matching properties must satisfy requirements 2/3", facet=facet, inst=element, expected=True)
ifcopenshell.api.pset.edit_pset(ifc, pset=pset, properties={"Foobar": "x", "Foobaz": "y"})
run("All matching properties must satisfy requirements 3/3", facet=facet, inst=element, expected=False)
ifc = self.setup_ifc()
restriction1 = Restriction(options={"pattern": "Foo.*"})
restriction2 = Restriction(options={"enumeration": ["x", "y"]})
facet = Property(propertySet="Foo_Bar", baseName=restriction1, value=restriction2, dataType="IFCLABEL")
element = ifcopenshell.api.root.create_entity(ifc, ifc_class="IfcWall")
pset = ifcopenshell.api.pset.add_pset(ifc, product=element, name="Foo_Bar")
ifcopenshell.api.pset.edit_pset(ifc, pset=pset, properties={"Foobar": "x", "Foobaz": "y"})
run(
"If multiple properties are matched, all values must satisfy requirements 1/2",
facet=facet,
inst=element,
expected=True,
)
ifcopenshell.api.pset.edit_pset(ifc, pset=pset, properties={"Foobar": "x", "Foobaz": "z"})
run(
"If multiple properties are matched, all values must satisfy requirements 2/2",
facet=facet,
inst=element,
expected=False,
)
ifc = self.setup_ifc()
facet = Property(propertySet="Foo_Bar", baseName="Foo", value="2", dataType="IFCTIMEMEASURE")
element = ifcopenshell.api.root.create_entity(ifc, ifc_class="IfcWall")
pset = ifcopenshell.api.pset.add_pset(ifc, product=element, name="Foo_Bar")
ifcopenshell.api.pset.edit_pset(ifc, pset=pset, properties={"Foo": ifc.createIfcMassMeasure(2)})
run("Measures are used to specify an IFC data type 1/2", facet=facet, inst=element, expected=False)
ifcopenshell.api.pset.edit_pset(ifc, pset=pset, properties={"Foo": ifc.createIfcTimeMeasure(2)})
run("Measures are used to specify an IFC data type 2/2", facet=facet, inst=element, expected=True)
ifc = self.setup_ifc()
facet = Property(propertySet="Foo_Bar", baseName="Foo", value="2", dataType="IFCLENGTHMEASURE")
element = ifcopenshell.api.root.create_entity(ifc, ifc_class="IfcWall")
pset = ifcopenshell.api.pset.add_pset(ifc, product=element, name="Foo_Bar")
ifcopenshell.api.pset.edit_pset(ifc, pset=pset, properties={"Foo": ifc.createIfcLengthMeasure(2)})
run(
"Unit conversions shall take place to IDS-nominated standard units 1/2",
facet=facet,
inst=element,
expected=False,
)
ifcopenshell.api.pset.edit_pset(ifc, pset=pset, properties={"Foo": ifc.createIfcLengthMeasure(2000)})
run(
"Unit conversions shall take place to IDS-nominated standard units 2/2",
facet=facet,
inst=element,
expected=True,
)
ifc = self.setup_ifc()
wall = ifcopenshell.api.root.create_entity(ifc, ifc_class="IfcWall")
wall_type = ifcopenshell.api.root.create_entity(ifc, ifc_class="IfcWallType")
ifcopenshell.api.type.assign_type(ifc, related_objects=[wall], relating_type=wall_type)
pset = ifcopenshell.api.pset.add_pset(ifc, product=wall_type, name="Foo_Bar")
ifcopenshell.api.pset.edit_pset(ifc, pset=pset, properties={"Foo": "Bar"})
facet = Property(propertySet="Foo_Bar", baseName="Foo", dataType="IFCLABEL")
run("Properties can be inherited from the type 1/2", facet=facet, inst=wall, expected=True)
run("Properties can be inherited from the type 2/2", facet=facet, inst=wall_type, expected=True)
ifc = self.setup_ifc()
wall = ifcopenshell.api.root.create_entity(ifc, ifc_class="IfcWall")
wall_type = ifcopenshell.api.root.create_entity(ifc, ifc_class="IfcWallType")
ifcopenshell.api.type.assign_type(ifc, related_objects=[wall], relating_type=wall_type)
pset = ifcopenshell.api.pset.add_pset(ifc, product=wall_type, name="Foo_Bar")
ifcopenshell.api.pset.edit_pset(ifc, pset=pset, properties={"Foo": "Baz"})
pset = ifcopenshell.api.pset.add_pset(ifc, product=wall, name="Foo_Bar")
ifcopenshell.api.pset.edit_pset(ifc, pset=pset, properties={"Foo": "Bar"})
facet = Property(propertySet="Foo_Bar", baseName="Foo", value="Bar", dataType="IFCLABEL")
run("Properties can be overriden by an occurrence 1/2", facet=facet, inst=wall, expected=True)
run("Properties can be overriden by an occurrence 2/2", facet=facet, inst=wall_type, expected=False)
ifc = self.setup_ifc()
element = ifcopenshell.api.root.create_entity(ifc, ifc_class="IfcWall")
pset = ifcopenshell.api.pset.add_pset(ifc, product=element, name="Foo_Bar")
prop = ifc.create_entity(
"IfcPropertySingleValue", Name="Foo", NominalValue=ifc.create_entity("IfcMassMeasure", 6.0)
)
ifcopenshell.api.pset.edit_pset(ifc, pset=pset, properties={"Foo": prop})
restriction = Restriction(options={"minExclusive": 5.0}, base="double")
facet = Property(propertySet="Foo_Bar", baseName="Foo", value=restriction, dataType="IfcMassMeasure")
run("MASSUNIT uses Kg instead of g", facet=facet, inst=element, expected=True)
prop.NominalValue = ifc.create_entity("IfcMassMeasure", 4.0)
run("MASSUNIT uses Kg instead of g", facet=facet, inst=element, expected=False)
# add Gram as Unit to force conversion
gram_unit = ifcopenshell.api.unit.add_si_unit(ifc, unit_type="MASSUNIT")
prop.Unit = gram_unit
prop.NominalValue = ifc.create_entity("IfcMassMeasure", 6000.0)
run("MASSUNIT uses Kg instead of g", facet=facet, inst=element, expected=True)
prop.NominalValue = ifc.create_entity("IfcMassMeasure", 6.0)
run("MASSUNIT uses Kg instead of g", facet=facet, inst=element, expected=False)
def setup_ifc(self):
ifc = ifcopenshell.file()
ifc.createIfcProject()
# Milli prefix used to check measurement conversions
lengthunit = ifcopenshell.api.unit.add_si_unit(ifc, unit_type="LENGTHUNIT", prefix="MILLI")
areaunit = ifcopenshell.api.unit.add_si_unit(ifc, unit_type="AREAUNIT", prefix="MILLI")
volumeunit = ifcopenshell.api.unit.add_si_unit(ifc, unit_type="VOLUMEUNIT", prefix="MILLI")
timeunit = ifcopenshell.api.unit.add_si_unit(ifc, unit_type="TIMEUNIT")
mass_unit = ifcopenshell.api.unit.add_si_unit(ifc, unit_type="MASSUNIT", prefix="KILO")
ifcopenshell.api.unit.assign_unit(ifc, units=[lengthunit, areaunit, volumeunit, timeunit, mass_unit])
return ifc
class TestMaterial:
def test_creating_a_material_facet(self):
facet = Material()
assert facet.asdict("requirement") == {"@cardinality": "required"}
facet = Material(value="value", uri="https://test.com", cardinality="required", instructions="instructions")
assert facet.asdict("requirement") == {
"value": {"simpleValue": "value"},
"@uri": "https://test.com",
"@cardinality": "required",
"@instructions": "instructions",
}
def test_filtering_using_a_material_facet(self):
set_facet("material")
facet = Material()
ifc = ifcopenshell.file()
element = ifcopenshell.api.root.create_entity(ifc, ifc_class="IfcWall")
run("Elements without a material always fail", facet=facet, inst=element, expected=False)
material = ifcopenshell.api.material.add_material(ifc)
ifcopenshell.api.material.assign_material(ifc, products=[element], material=material)
run("Elements with any material will pass an empty material facet", facet=facet, inst=element, expected=True)
run("A required facet checks all parameters as normal", facet=facet, inst=element, expected=True)
facet = Material(cardinality="prohibited")
run("A prohibited facet returns the opposite of a required facet", facet=facet, inst=element, expected=False)
facet = Material(cardinality="optional")
run(
"An optional facet only checks requirements if there is a value to check 1/2",
facet=facet,
inst=element,
expected=True,
)
facet = Material(value="Foo", cardinality="optional")
run(
"An optional facet only checks requirements if there is a value to check 1/2",
facet=facet,
inst=element,
expected=False,
)
ifc = ifcopenshell.file()
facet = Material(value="Foo")
element = ifcopenshell.api.root.create_entity(ifc, ifc_class="IfcWall")
material = ifcopenshell.api.material.add_material(ifc)
ifcopenshell.api.material.assign_material(ifc, products=[element], material=material)
material.Name = "Foo"
run("A material name may pass the value check", facet=facet, inst=element, expected=True)
material.Name = "Bar"
material.Category = "Foo"
run("A material category may pass the value check", facet=facet, inst=element, expected=True)
ifc = ifcopenshell.file()
facet = Material(value="Foo")
element = ifcopenshell.api.root.create_entity(ifc, ifc_class="IfcWall")
material_set = ifcopenshell.api.material.add_material_set(ifc, set_type="IfcMaterialList")
ifcopenshell.api.material.assign_material(ifc, products=[element], material=material_set)
material = ifcopenshell.api.material.add_material(ifc)
ifcopenshell.api.material.add_list_item(ifc, material_list=material_set, material=material)
material.Name = "Foo"
run("Any material Name in a list will pass a value check", facet=facet, inst=element, expected=True)
material.Name = "Bar"
material.Category = "Foo"
run("Any material Category in a list will pass a value check", facet=facet, inst=element, expected=True)
ifc = ifcopenshell.file()
facet = Material(value="Foo")
element = ifcopenshell.api.root.create_entity(ifc, ifc_class="IfcWall")
material_set = ifcopenshell.api.material.add_material_set(ifc, set_type="IfcMaterialLayerSet")
ifcopenshell.api.material.assign_material(ifc, products=[element], material=material_set)
material = ifcopenshell.api.material.add_material(ifc)
layer = ifcopenshell.api.material.add_layer(ifc, layer_set=material_set, material=material)
layer.Name = "Foo"
run("Any layer Name in a layer set will pass a value check", facet=facet, inst=element, expected=True)
layer.Name = "Bar"
layer.Category = "Foo"
run("Any layer Category in a layer set will pass a value check", facet=facet, inst=element, expected=True)
layer.Category = "Bar"
material.Name = "Foo"
run("Any material Name in a layer set will pass a value check", facet=facet, inst=element, expected=True)
material.Name = "Bar"
material.Category = "Foo"
run("Any material Category in a layer set will pass a value check", facet=facet, inst=element, expected=True)
ifc = ifcopenshell.file()
facet = Material(value="Foo")
element = ifcopenshell.api.root.create_entity(ifc, ifc_class="IfcWall")
material_set = ifcopenshell.api.material.add_material_set(ifc, set_type="IfcMaterialProfileSet")
ifcopenshell.api.material.assign_material(ifc, products=[element], material=material_set)
material = ifcopenshell.api.material.add_material(ifc)
profile = ifcopenshell.api.material.add_profile(ifc, profile_set=material_set, material=material)
profile.Name = "Foo"
profile.Profile = ifc.createIfcCircleProfileDef("AREA", None, None, 1)
run("Any profile Name in a profile set will pass a value check", facet=facet, inst=element, expected=True)
profile.Name = "Bar"
profile.Category = "Foo"
run("Any profile Category in a profile set will pass a value check", facet=facet, inst=element, expected=True)
profile.Category = "Bar"
material.Name = "Foo"
run("Any material Name in a profile set will pass a value check", facet=facet, inst=element, expected=True)
material.Name = "Bar"
material.Category = "Foo"
run("Any material category in a profile set will pass a value check", facet=facet, inst=element, expected=True)
ifc = ifcopenshell.file()
facet = Material(value="Foo")
element = ifcopenshell.api.root.create_entity(ifc, ifc_class="IfcWall")
material_set = ifcopenshell.api.material.add_material_set(ifc, set_type="IfcMaterialConstituentSet")
ifcopenshell.api.material.assign_material(ifc, products=[element], material=material_set)
run("A constituent set with no data will fail a value check", facet=facet, inst=element, expected=False)
material = ifcopenshell.api.material.add_material(ifc)
constituent = ifcopenshell.api.material.add_constituent(ifc, constituent_set=material_set, material=material)
constituent.Name = "Foo"
run(
"Any constituent Name in a constituent set will pass a value check",
facet=facet,
inst=element,
expected=True,
)
constituent.Name = "Bar"
constituent.Category = "Foo"
run(
"Any constituent Category in a constituent set will pass a value check",
facet=facet,
inst=element,
expected=True,
)
constituent.Category = "Bar"
material.Name = "Foo"
run("Any material Name in a constituent set will pass a value check", facet=facet, inst=element, expected=True)
material.Name = "Bar"
material.Category = "Foo"
run(
"Any material Category in a constituent set will pass a value check",
facet=facet,
inst=element,
expected=True,
)
ifc = ifcopenshell.file()
element = ifcopenshell.api.root.create_entity(ifc, ifc_class="IfcWall")
element_type = ifcopenshell.api.root.create_entity(ifc, ifc_class="IfcWallType")
ifcopenshell.api.type.assign_type(ifc, related_objects=[element], relating_type=element_type)
material = ifcopenshell.api.material.add_material(ifc)
ifcopenshell.api.material.assign_material(ifc, products=[element_type], material=material)
material.Name = "Foo"
facet = Material(value="Foo")
run("Occurrences can inherit materials from their types", facet=facet, inst=element, expected=True)
ifc = ifcopenshell.file()
element = ifcopenshell.api.root.create_entity(ifc, ifc_class="IfcWall")
element_type = ifcopenshell.api.root.create_entity(ifc, ifc_class="IfcWallType")
ifcopenshell.api.type.assign_type(ifc, related_objects=[element], relating_type=element_type)
material = ifcopenshell.api.material.add_material(ifc)
ifcopenshell.api.material.assign_material(ifc, products=[element_type], material=material)
material.Name = "Bar"
material = ifcopenshell.api.material.add_material(ifc)
ifcopenshell.api.material.assign_material(ifc, products=[element], material=material)
material.Name = "Foo"
facet = Material(value="Foo")
run("Occurrences can override materials from their types", facet=facet, inst=element, expected=True)
class TestPartOf:
def test_creating_a_partof_facet(self):
facet = PartOf()
assert facet.asdict("requirement") == {
"entity": {"name": {"simpleValue": "IFCWALL"}},
"@cardinality": "required",
}
facet = PartOf(
name="IFCGROUP",
predefinedType="predefinedType",
relation="IFCRELASSIGNSTOGROUP",
cardinality="required",
instructions="instructions",
)
assert facet.asdict("requirement") == {
"entity": {
"name": {"simpleValue": "IFCGROUP"},
"predefinedType": {"simpleValue": "predefinedType"},
},
"@relation": "IFCRELASSIGNSTOGROUP",
"@cardinality": "required",
"@instructions": "instructions",
}
def test_filtering_using_a_partof_facet(self):
set_facet("partof")
ifc = ifcopenshell.file()
element = ifcopenshell.api.root.create_entity(ifc, ifc_class="IfcElementAssembly")
subelement = ifcopenshell.api.root.create_entity(ifc, ifc_class="IfcWall")
facet = PartOf(name="IFCELEMENTASSEMBLY", relation="IFCRELAGGREGATES")
run("A non aggregated element fails an aggregate relationship", facet=facet, inst=subelement, expected=False)
ifcopenshell.api.aggregate.assign_object(ifc, products=[subelement], relating_object=element)
run("The aggregated whole fails an aggregate relationship", facet=facet, inst=element, expected=False)
run("The aggregated part passes an aggregate relationship", facet=facet, inst=subelement, expected=True)
run("A required facet checks all parameters as normal", facet=facet, inst=subelement, expected=True)
facet = PartOf(name="IFCELEMENTASSEMBLY", relation="IFCRELAGGREGATES", cardinality="prohibited")
run("A prohibited facet returns the opposite of a required facet", facet=facet, inst=subelement, expected=False)
ifc = ifcopenshell.file()
element = ifcopenshell.api.root.create_entity(ifc, ifc_class="IfcSlab")
subelement = ifcopenshell.api.root.create_entity(ifc, ifc_class="IfcBeam")
ifcopenshell.api.aggregate.assign_object(ifc, products=[subelement], relating_object=element)
facet = PartOf(name="IFCSLAB", relation="IFCRELAGGREGATES")
run("An aggregate may specify the entity of the whole 1/2", facet=facet, inst=subelement, expected=True)
facet = PartOf(name="IFCWALL", relation="IFCRELAGGREGATES")
run("An aggregate may specify the entity of the whole 2/2", facet=facet, inst=subelement, expected=False)
element.PredefinedType = "BASESLAB"
facet = PartOf(name="IFCSLAB", predefinedType="BASESLAB", relation="IFCRELAGGREGATES")
run(
"An aggregate may specify the predefined type of the whole 1/2", facet=facet, inst=subelement, expected=True
)
facet = PartOf(name="IFCSLAB", predefinedType="SLABRADOR", relation="IFCRELAGGREGATES")
run(
"An aggregate may specify the predefined type of the whole 2/2",
facet=facet,
inst=subelement,
expected=False,
)
ifc = ifcopenshell.file()
element = ifcopenshell.api.root.create_entity(ifc, ifc_class="IfcElementAssembly")
subelement = ifcopenshell.api.root.create_entity(ifc, ifc_class="IfcSlab")
subsubelement = ifcopenshell.api.root.create_entity(ifc, ifc_class="IfcBeam")
ifcopenshell.api.aggregate.assign_object(ifc, products=[subelement], relating_object=element)
ifcopenshell.api.aggregate.assign_object(ifc, products=[subsubelement], relating_object=subelement)
facet = PartOf(name="IFCELEMENTASSEMBLY", relation="IFCRELAGGREGATES")
run("An aggregate entity may pass any ancestral whole passes", facet=facet, inst=subsubelement, expected=True)
ifc = ifcopenshell.file()
element = ifcopenshell.api.root.create_entity(ifc, ifc_class="IfcElementAssembly")
group = ifcopenshell.api.group.add_group(ifc)
facet = PartOf(name="IFCGROUP", relation="IFCRELASSIGNSTOGROUP")
run("A non grouped element fails a group relationship", facet=facet, inst=element, expected=False)
ifcopenshell.api.group.assign_group(ifc, products=[element], group=group)
run("A grouped element passes a group relationship", facet=facet, inst=element, expected=True)
ifc = ifcopenshell.file()
element = ifcopenshell.api.root.create_entity(ifc, ifc_class="IfcElementAssembly")
group = ifc.createIfcInventory()
facet = PartOf(name="IFCGROUP", relation="IFCRELASSIGNSTOGROUP")
ifcopenshell.api.group.assign_group(ifc, products=[element], group=group)
run("A group entity must match exactly 1/2", facet=facet, inst=element, expected=False)
facet = PartOf(name="IFCINVENTORY", relation="IFCRELASSIGNSTOGROUP")
run("A group entity must match exactly 2/2", facet=facet, inst=element, expected=True)
group.ObjectType = "BUNNY"
facet = PartOf(name="IFCINVENTORY", predefinedType="BUNNARY", relation="IFCRELASSIGNSTOGROUP")
run("A group predefined type must match exactly 2/2", facet=facet, inst=element, expected=False)
facet = PartOf(name="IFCINVENTORY", predefinedType="BUNNY", relation="IFCRELASSIGNSTOGROUP")
run("A group predefined type must match exactly 2/2", facet=facet, inst=element, expected=True)
ifc = ifcopenshell.file()
element = ifcopenshell.api.root.create_entity(ifc, ifc_class="IfcElementAssembly")
container = ifcopenshell.api.root.create_entity(ifc, ifc_class="IfcSpace")
facet = PartOf(name="IFCSPACE", relation="IFCRELCONTAINEDINSPATIALSTRUCTURE")
run("Any contained element passes a containment relationship 1/2", facet=facet, inst=element, expected=False)
ifcopenshell.api.spatial.assign_container(ifc, products=[element], relating_structure=container)
run("Any contained element passes a containment relationship 2/2", facet=facet, inst=element, expected=True)
run("The container itself always fails", facet=facet, inst=container, expected=False)
ifc = ifcopenshell.file()
element = ifcopenshell.api.root.create_entity(ifc, ifc_class="IfcElementAssembly")
container = ifcopenshell.api.root.create_entity(ifc, ifc_class="IfcSpace")
ifcopenshell.api.spatial.assign_container(ifc, products=[element], relating_structure=container)
facet = PartOf(relation="IFCRELCONTAINEDINSPATIALSTRUCTURE", name="IFCSITE")
run("The container entity must match exactly 1/2", facet=facet, inst=element, expected=False)
facet = PartOf(relation="IFCRELCONTAINEDINSPATIALSTRUCTURE", name="IFCSPACE")
run("The container entity must match exactly 2/2", facet=facet, inst=element, expected=True)
container.ObjectType = "BURROW"
facet = PartOf(relation="IFCRELCONTAINEDINSPATIALSTRUCTURE", name="IFCSPACE", predefinedType="WARREN")
run("The container predefined type must match exactly 1/2", facet=facet, inst=element, expected=False)
facet = PartOf(relation="IFCRELCONTAINEDINSPATIALSTRUCTURE", name="IFCSPACE", predefinedType="BURROW")
run("The container predefined type must match exactly 2/2", facet=facet, inst=element, expected=True)
ifc = ifcopenshell.file()
element = ifcopenshell.api.root.create_entity(ifc, ifc_class="IfcSlab")
subelement = ifcopenshell.api.root.create_entity(ifc, ifc_class="IfcBeam")
ifcopenshell.api.aggregate.assign_object(ifc, products=[subelement], relating_object=element)
container = ifcopenshell.api.root.create_entity(ifc, ifc_class="IfcSpace")
ifcopenshell.api.spatial.assign_container(ifc, products=[element], relating_structure=container)
facet = PartOf(relation="IFCRELCONTAINEDINSPATIALSTRUCTURE", name="IFCSPACE")
run("The container may be indirect", facet=facet, inst=subelement, expected=True)
ifc = ifcopenshell.file()
element = ifcopenshell.api.root.create_entity(ifc, ifc_class="IfcFurniture")
subelement = ifcopenshell.api.root.create_entity(ifc, ifc_class="IfcDiscreteAccessory")
ifcopenshell.api.nest.assign_object(ifc, related_objects=[subelement], relating_object=element)
facet = PartOf(name="IFCFURNITURE", relation="IFCRELNESTS")
run("Any nested part passes a nest relationship", facet=facet, inst=subelement, expected=True)
run("Any nested whole fails a nest relationship", facet=facet, inst=element, expected=False)
ifc = ifcopenshell.file()
element = ifcopenshell.api.root.create_entity(ifc, ifc_class="IfcFurniture")
subelement = ifcopenshell.api.root.create_entity(ifc, ifc_class="IfcDiscreteAccessory")
ifcopenshell.api.nest.assign_object(ifc, related_objects=[subelement], relating_object=element)
facet = PartOf(relation="IFCRELNESTS", name="IFCBEAM")
run("The nest entity must match exactly 1/2", facet=facet, inst=subelement, expected=False)
facet = PartOf(relation="IFCRELNESTS", name="IFCFURNITURE")
run("The nest entity must match exactly 2/2", facet=facet, inst=subelement, expected=True)
element.PredefinedType = "USERDEFINED"
element.ObjectType = "WATERBOTTLE"
facet = PartOf(relation="IFCRELNESTS", name="IFCFURNITURE", predefinedType="LITTERBOX")
run("The nest predefined type must match exactly 1/2", facet=facet, inst=subelement, expected=False)
facet = PartOf(relation="IFCRELNESTS", name="IFCFURNITURE", predefinedType="WATERBOTTLE")
run("The nest predefined type must match exactly 2/2", facet=facet, inst=subelement, expected=True)
ifc = ifcopenshell.file()
element = ifcopenshell.api.root.create_entity(ifc, ifc_class="IfcFurniture")
subelement = ifcopenshell.api.root.create_entity(ifc, ifc_class="IfcDiscreteAccessory")
subsubelement = ifcopenshell.api.root.create_entity(ifc, ifc_class="IfcMechanicalFastener")
ifcopenshell.api.nest.assign_object(ifc, related_objects=[subelement], relating_object=element)
ifcopenshell.api.nest.assign_object(ifc, related_objects=[subsubelement], relating_object=subelement)
facet = PartOf(relation="IFCRELNESTS", name="IFCFURNITURE")
run("Nesting may be indirect", facet=facet, inst=subsubelement, expected=True)
class TestRestriction:
def test_creating_a_restriction(self):
restriction = Restriction(options={"enumeration": ["foo", "bar"]})
assert restriction.asdict() == {"@base": "xs:string", "xs:enumeration": [{"@value": "foo"}, {"@value": "bar"}]}
def test_enumeration(self):
restriction = Restriction(options={"enumeration": ["foo", "bar"]})
assert restriction == "foo"
assert restriction == "bar"
assert restriction != "Foo"
assert restriction != "baz"
def test_bounds(self):
restriction = Restriction(options={"minInclusive": 0, "maxExclusive": 10}, base="integer")
assert restriction == 0
assert restriction != 10
assert restriction == 5
assert restriction != -1
def test_pattern(self):
restriction = Restriction(options={"pattern": "[A-Z]{2}[0-9]{2}"})
assert restriction == "AB01"
assert restriction != "AB"
assert restriction != "01"
def test_filtering_using_restrictions(self):
set_facet("restriction")
ifc = ifcopenshell.file()
restriction = Restriction(options={"enumeration": ["Foo", "Bar"]})
facet = Attribute(name="Name", value=restriction)
element = ifc.createIfcWall(Name="Foo")
run("An enumeration matches case sensitively 1/3", facet=facet, inst=element, expected=True)
element.Name = "Bar"
run("An enumeration matches case sensitively 2/3", facet=facet, inst=element, expected=True)
element.Name = "Baz"
run("An enumeration matches case sensitively 3/3", facet=facet, inst=element, expected=False)
ifc = ifcopenshell.file()
restriction = Restriction(options={"minInclusive": 0, "maxInclusive": 10}, base="integer")
element = ifc.createIfcSurfaceStyleRefraction(RefractionIndex=0)
facet = Attribute(name="RefractionIndex", value=restriction)
run("A bound can be inclusive 1/4", facet=facet, inst=element, expected=True)
element.RefractionIndex = 5
run("A bound can be inclusive 2/4", facet=facet, inst=element, expected=True)
element.RefractionIndex = 10
run("A bound can be inclusive 3/4", facet=facet, inst=element, expected=True)
element.RefractionIndex = 100
run("A bound can be inclusive 4/4", facet=facet, inst=element, expected=False)
restriction = Restriction(options={"minExclusive": 0, "maxExclusive": 10}, base="integer")
facet = Attribute(name="RefractionIndex", value=restriction)
element.RefractionIndex = 0
run("A bound can be inclusive 1/3", facet=facet, inst=element, expected=False)
element.RefractionIndex = 5
run("A bound can be inclusive 2/3", facet=facet, inst=element, expected=True)
element.RefractionIndex = 10
run("A bound can be inclusive 3/3", facet=facet, inst=element, expected=False)
ifc = ifcopenshell.file()
restriction = Restriction(options={"pattern": "[A-Z]{2}[0-9]{2}"})
facet = Attribute(name="Name", value=restriction)
element = ifc.createIfcWall(Name="WT01")
run("Regex patterns can be used 1/3", facet=facet, inst=element, expected=True)
element.Name = "XY99"
run("Regex patterns can be used 2/3", facet=facet, inst=element, expected=True)
element.Name = "A5"
run("Regex patterns can be used 3/3", facet=facet, inst=element, expected=False)
ifc = ifcopenshell.file()
restriction = Restriction(options={"pattern": ".*"})
element = ifc.createIfcSurfaceStyleRefraction(RefractionIndex=42)
facet = Attribute(name="RefractionIndex", value=restriction)
run("Patterns only work on strings and nothing else", facet=facet, inst=element, expected=False)
ifc = ifcopenshell.file()
restriction = Restriction(options={"length": 2})
facet = Attribute(name="Name", value=restriction)
element = ifc.createIfcWall(Name="AB")
run("Length checks can be used 1/2", facet=facet, inst=element, expected=True)
element.Name = "ABC"
run("Length checks can be used 1/2", facet=facet, inst=element, expected=False)
ifc = ifcopenshell.file()
restriction = Restriction(options={"minLength": 2, "maxLength": 3})
facet = Attribute(name="Name", value=restriction)
element = ifc.createIfcWall(Name="A")
run("Max and min length checks can be used 1/3", facet=facet, inst=element, expected=False)
element.Name = "AB"
run("Max and min length checks can be used 2/3", facet=facet, inst=element, expected=True)
element.Name = "ABC"
run("Max and min length checks can be used 3/3", facet=facet, inst=element, expected=True)
element.Name = "ABCD"
run("Max and min length checks can be used 4/3", facet=facet, inst=element, expected=False)