Merge remote-tracking branch 'origin/v0.7.0' into v08attempt1

This commit is contained in:
Thomas Krijnen
2024-04-18 21:39:33 +02:00
86 changed files with 1817 additions and 631 deletions
+1 -1
View File
@@ -153,7 +153,7 @@ interface to access the IfcOpenShell utilities.
1. Install the BlenderBIM Add-on by following the `BlenderBIM Add-on
installation documentation
<https://blenderbim.org/docs/users/installation.html>`_.
<https://docs.blenderbim.org/users/installation.html>`_.
2. Launch Blender. Change to the **Scene Properties** tab in the **Properties
Panel**. Scroll down to the **IFC Collaboration > IFC CSV Import / Export**
+1 -1
View File
@@ -88,7 +88,7 @@ interface to access the IfcOpenShell utilities.
1. Install the BlenderBIM Add-on by following the `BlenderBIM Add-on
installation documentation
<https://blenderbim.org/docs/users/installation.html>`_.
<https://docs.blenderbim.org/users/installation.html>`_.
2. Launch Blender. Change to the **Scene Properties** tab in the **Properties
Panel**. Scroll down to the **IFC Quality Control > IFC Diff** panel.
@@ -227,7 +227,7 @@ The BlenderBIM Add-on is available either as a stable build or a daily build.
1. Install the BlenderBIM Add-on by following the `BlenderBIM Add-on
installation documentation
<https://blenderbim.org/docs/users/installation.html>`_.
<https://docs.blenderbim.org/users/installation.html>`_.
2. Launch Blender. On the top left of the Viewport panel, click the **Editor
Type** icon to change the viewport into a **Python Console**.
@@ -261,7 +261,7 @@ and run your script using the **Text > Run Script** menu or by clicking on the
Blender. This can help when learning how to write scripts as you can double
check the results of your scripts with what you see in the graphical
interface. `Read more
<https://blenderbim.org/docs/users/exploring_an_ifc_model.html>`_.
<https://docs.blenderbim.org/users/exploring_an_ifc_model.html>`_.
From source with precompiled binaries
-------------------------------------
@@ -12,7 +12,7 @@ Packaged installation
IfcSverchok is packaged like a regular Blender add-on, so installation is the
same as any other Blender add-on. `Download IfcSverchok here
<https://blenderbim.org/builds/ifcsverchok-230823.zip>`__.
<https://github.com/IfcOpenShell/IfcOpenShell/releases/download/ifcsverchok-240417/ifcsverchok-240417.zip>`__.
Like all Blender add-ons, they can be installed using ``Edit > Preferences >
Addons > Install > Choose Downloaded ZIP > Enable Add-on Checkbox``. You can
@@ -87,6 +87,36 @@ ARGUMENTS_DEPRECATION = {
"material.unassign_material": partial(
batching_argument_deprecation, prev_argument="product", new_argument="products"
),
"classification.add_reference": partial(
batching_argument_deprecation, prev_argument="product", new_argument="products"
),
"classification.remove_reference": partial(
batching_argument_deprecation, prev_argument="product", new_argument="products"
),
"library.assign_reference": partial(
batching_argument_deprecation, prev_argument="product", new_argument="products"
),
"library.unassign_reference": partial(
batching_argument_deprecation, prev_argument="product", new_argument="products"
),
"document.assign_document": partial(
batching_argument_deprecation, prev_argument="product", new_argument="products"
),
"document.unassign_document": partial(
batching_argument_deprecation, prev_argument="product", new_argument="products"
),
"spatial.reference_structure": partial(
batching_argument_deprecation, prev_argument="product", new_argument="products"
),
"spatial.dereference_structure": partial(
batching_argument_deprecation, prev_argument="product", new_argument="products"
),
"constraint.assign_constraint": partial(
batching_argument_deprecation, prev_argument="product", new_argument="products"
),
"constraint.unassign_constraint": partial(
batching_argument_deprecation, prev_argument="product", new_argument="products"
),
}
@@ -64,7 +64,8 @@ class Usecase:
self.settings["product"].PredefinedType = "USERDEFINED"
elif hasattr(self.settings["product"], "ObjectType"):
relating_type = ifcopenshell.util.element.get_type(self.settings["product"])
if relating_type and relating_type.PredefinedType != "NOTDEFINED":
# Allow for None due to https://github.com/buildingSMART/IFC4.3.x-development/issues/818
if relating_type and relating_type.PredefinedType not in ("NOTDEFINED", None):
self.settings["product"].ObjectType = None
self.settings["product"].PredefinedType = None
elif (
@@ -19,10 +19,11 @@
import ifcopenshell
import ifcopenshell.util.schema
import ifcopenshell.util.date
from typing import Union
class Usecase:
def __init__(self, file, classification=None):
def __init__(self, file: ifcopenshell.file, classification: Union[str, ifcopenshell.entity_instance]):
"""Adds a new classification system to the project
External classification systems such as Uniclass or Omniclass are
@@ -81,7 +82,7 @@ class Usecase:
"classification": classification,
}
def execute(self):
def execute(self) -> ifcopenshell.entity_instance:
if isinstance(self.settings["classification"], str):
classification = self.file.createIfcClassification(Name=self.settings["classification"])
self.relate_to_project(classification)
@@ -17,12 +17,24 @@
# along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
import ifcopenshell
import ifcopenshell.api
import ifcopenshell.util.element
import ifcopenshell.util.schema
from typing import Optional, Union
class Usecase:
def __init__(self, file, product=None, reference=None, identification=None, name=None, classification=None, is_lightweight=True):
"""Adds a new classification reference and assigns it to a product
def __init__(
self,
file: ifcopenshell.file,
products: list[ifcopenshell.entity_instance],
reference: Optional[ifcopenshell.entity_instance] = None,
identification: Optional[str] = None,
name: Optional[str] = None,
classification: Optional[ifcopenshell.entity_instance] = None,
is_lightweight=True,
):
"""Adds a new classification reference and assigns it to the list of products
A classification reference is a single entry such as "Pr_12_23_34" that
is part of an external classification system (such as Uniclass or
@@ -33,7 +45,7 @@ class Usecase:
resources such as profiles, documents, libraries, and so on.
Classification references can be added in two ways. Option 1) specify a
custom arbitrary reference, where you have the manually specify the
custom arbitrary reference, where you have to manually specify the
identification (e.g. "Pr_12_23_45") and name (e.g. "Door Products").
Option 2) add a reference from an IFC classification library. The latter
is preferred if you are using a common classification system such as
@@ -52,13 +64,13 @@ class Usecase:
assigned to both the type and an occurrence, then the assignment at the
occurrence will override the type classification.
:param product: The IFC object, property, or resource you want to
:param product: The list of IFC objects, properties, or resources you want to
associate the classification reference to.
:type product: ifcopenshell.entity_instance.entity_instance
:type product: list[ifcopenshell.entity_instance.entity_instance]
:param reference: The classification reference entity taken from an
IFC classification library. If you supply this parameter, you will
use option 2.
:type product: ifcopenshell.entity_instance.entity_instance, optional
:type reference: ifcopenshell.entity_instance.entity_instance, optional
:param identification: If you choose option 1 and do not specify a
reference, you may manually specify an identification code. The code
is typically a short identifier and may have punctuation to separate
@@ -70,7 +82,7 @@ class Usecase:
:param classification: The IfcClassification entity in your IFC model
(not the library, if you are doing option 2) that the reference is
part of.
:type product: ifcopenshell.entity_instance.entity_instance
:type classification: ifcopenshell.entity_instance.entity_instance
:param is_lightweight: If you are doing option 2, choose whether or not
to only add that particular reference (lighweight) or also add all
of its parent references in the classification hierarchy (not
@@ -81,8 +93,12 @@ class Usecase:
is generally unnecessary. Using lightweight classifications are
recommended and is the default.
:type is_lightweight: bool, optional
:raises TypeError: If file is IFC2X3 and `products` has non-IfcRoot elements.
:return: The newly added IfcClassificationReference
:rtype: ifcopenshell.entity_instance.entity_instance
or `None` if `products` was empty list.
:rtype: Union[ifcopenshell.entity_instance.entity_instance, None]
Example:
@@ -93,7 +109,7 @@ class Usecase:
classification = ifcopenshell.api.run("classification.add_classification",
model, classification="MyCustomClassification")
ifcopenshell.api.run("classification.add_reference", model,
product=wall_type, classification=classification,
products=[wall_type], classification=classification,
identification="W_01", name="Interior Walls")
# Option 2: adding a popular classification from a library
@@ -104,12 +120,12 @@ class Usecase:
reference = [r for r in library.by_type("IfcClassificationReference")
if r.Identification == "XYZ"][0]
ifcopenshell.api.run("classification.add_reference", model,
product=wall_type, classification=classification,
products=[wall_type], classification=classification,
reference=reference)
"""
self.file = file
self.settings = {
"product": product,
"products": products,
"reference": reference,
"identification": identification,
"name": name,
@@ -117,8 +133,27 @@ class Usecase:
"is_lightweight": is_lightweight,
}
def execute(self):
self.is_rooted = self.settings["product"].is_a("IfcRoot")
def execute(self) -> Union[ifcopenshell.entity_instance, None]:
if not self.settings["products"]:
return
if self.settings["reference"]:
referenced = ifcopenshell.util.element.get_referenced_elements(self.settings["reference"])
if set(self.settings["products"]).issubset(referenced):
# nothing to do, all elements already have this reference assigned
return self.settings["reference"]
self.rooted_products: set[ifcopenshell.entity_instance] = set()
self.non_rooted_products: set[ifcopenshell.entity_instance] = set()
for product in self.settings["products"]:
if product.is_a("IfcRoot"):
self.rooted_products.add(product)
else:
self.non_rooted_products.add(product)
if self.non_rooted_products and self.file.schema == "IFC2X3":
raise TypeError(f"Cannot add reference to non-IfcRoot element in IFC2X3: {self.non_rooted_products}.")
if self.settings["reference"]:
return self.add_from_library()
return self.add_from_identification()
@@ -134,14 +169,10 @@ class Usecase:
else:
reference.Identification = self.settings["identification"]
relationship = self.get_existing_relationship(reference)
if relationship:
self.add_to_existing_relationship(relationship)
else:
self.add_new_relationship(reference)
self.update_relationships(reference)
return reference
def add_from_library(self):
def add_from_library(self) -> ifcopenshell.entity_instance:
if hasattr(self.settings["reference"], "ItemReference"):
identification = self.settings["reference"].ItemReference # IFC2X3
else:
@@ -155,8 +186,9 @@ class Usecase:
old_referenced_source = self.settings["reference"].ReferencedSource
self.settings["reference"].ReferencedSource = None
else:
classification_name = self.settings["classification"].Name
existing_classification = [
c for c in self.file.by_type("IfcClassification") if c.Name == self.settings["classification"].Name
c for c in self.file.by_type("IfcClassification") if c.Name == classification_name
]
reference = migrator.migrate(self.settings["reference"], self.file)
@@ -174,15 +206,10 @@ class Usecase:
for element in to_delete:
self.file.remove(element)
relationship = self.get_existing_relationship(reference)
if relationship:
self.add_to_existing_relationship(relationship)
else:
self.add_new_relationship(reference)
self.update_relationships(reference)
return reference
def get_existing_reference(self, identification):
def get_existing_reference(self, identification: Optional[str] = None) -> Union[ifcopenshell.entity_instance, None]:
for reference in self.file.by_type("IfcClassificationReference"):
if self.file.schema == "IFC2X3":
if reference.ItemReference == identification:
@@ -191,39 +218,39 @@ class Usecase:
if reference.Identification == identification:
return reference
def add_new_relationship(self, reference):
if self.is_rooted:
self.file.create_entity(
"IfcRelAssociatesClassification",
GlobalId=ifcopenshell.guid.new(),
RelatedObjects=[self.settings["product"]],
RelatingClassification=reference,
)
else:
self.file.create_entity(
"IfcExternalReferenceRelationship",
RelatingReference=reference,
RelatedResourceObjects=[self.settings["product"]],
)
def add_to_existing_relationship(self, rel):
if self.is_rooted:
related_objects = set(rel.RelatedObjects)
related_objects.add(self.settings["product"])
rel.RelatedObjects = list(related_objects)
else:
related_objects = set(rel.RelatedResourceObjects)
related_objects.add(self.settings["product"])
rel.RelatedResourceObjects = list(related_objects)
def get_existing_relationship(self, reference):
if self.is_rooted:
def update_relationships(self, reference: ifcopenshell.entity_instance) -> None:
root_rel, non_root_rel = None, None
if self.rooted_products:
if self.file.schema == "IFC2X3":
for rel in self.file.by_type("IfcRelAssociatesClassification"):
if rel.RelatingClassification == reference:
return rel
elif reference.ClassificationRefForObjects:
return reference.ClassificationRefForObjects[0]
elif self.file.schema != "IFC2X3":
if reference.ExternalReferenceForResources:
return reference.ExternalReferenceForResources[0]
root_rel = rel
break
else:
root_rel = next(iter(reference.ClassificationRefForObjects), None)
if root_rel:
related_objects = set(root_rel.RelatedObjects) | self.rooted_products
root_rel.RelatedObjects = list(related_objects)
ifcopenshell.api.run("owner.update_owner_history", self.file, **{"element": root_rel})
else:
self.file.create_entity(
"IfcRelAssociatesClassification",
OwnerHistory=ifcopenshell.api.run("owner.create_owner_history", self.file),
GlobalId=ifcopenshell.guid.new(),
RelatedObjects=list(self.rooted_products),
RelatingClassification=reference,
)
if self.non_rooted_products:
# NOTE: Only Ifc4+. Ifc2x3 is already handled by raising TypeError
non_root_rel = next(iter(reference.ExternalReferenceForResources), None)
if non_root_rel:
related_objects = set(non_root_rel.RelatedResourceObjects) | self.non_rooted_products
non_root_rel.RelatedResourceObjects = list(related_objects)
else:
self.file.create_entity(
"IfcExternalReferenceRelationship",
RelatingReference=reference,
RelatedResourceObjects=list(self.non_rooted_products),
)
@@ -17,12 +17,18 @@
# along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
import ifcopenshell
import ifcopenshell.api
import ifcopenshell.util.element
class Usecase:
def __init__(self, file, reference=None, product=None):
"""Removes a classification reference from a product
def __init__(
self,
file: ifcopenshell.file,
reference: ifcopenshell.entity_instance,
products: list[ifcopenshell.entity_instance],
):
"""Removes a classification reference from the list of products
If the classification reference is no longer associated to any products,
the classification reference itself is also removed.
@@ -30,9 +36,12 @@ class Usecase:
:param reference: The IfcClassificationReference entity of the
relationship you want to remove.
:type reference: ifcopenshell.entity_instance.entity_instance
:param product: The object entity of the relationship you want to
:param product: The list fo object entities of the relationship you want to
remove.
:type reference: ifcopenshell.entity_instance.entity_instance
:type product: list[ifcopenshell.entity_instance.entity_instance]
:raises TypeError: If file is IFC2X3 and `products` has non-IfcRoot elements.
:return: None
:rtype: None
@@ -44,42 +53,75 @@ class Usecase:
classification = ifcopenshell.api.run("classification.add_classification",
model, classification="MyCustomClassification")
reference = ifcopenshell.api.run("classification.add_reference", model,
product=wall_type, classification=classification,
products=[wall_type], classification=classification,
identification="W_01", name="Interior Walls")
ifcopenshell.api.run("classification.remove_reference", model,
reference=reference, product=wall_type)
reference=reference, products=[wall_type])
"""
self.file = file
self.settings = {"reference": reference, "product": product}
self.settings = {"reference": reference, "products": products}
def execute(self):
if self.settings["product"].is_a("IfcRoot"):
for rel in self.file.by_type("IfcRelAssociatesClassification"):
if rel.RelatingClassification == self.settings["reference"] and rel.RelatedObjects:
if self.settings["product"] in rel.RelatedObjects:
related_objects = list(rel.RelatedObjects)
related_objects.remove(self.settings["product"])
if len(related_objects):
rel.RelatedObjects = related_objects
else:
history = rel.OwnerHistory
self.file.remove(rel)
if history:
ifcopenshell.util.element.remove_deep2(self.file, history)
else:
for rel in self.file.by_type("IfcExternalReferenceRelationship"):
if rel.RelatingReference == self.settings["reference"] and rel.RelatedResourceObjects:
if self.settings["product"] in rel.RelatedResourceObjects:
related_objects = list(rel.RelatedResourceObjects)
related_objects.remove(self.settings["product"])
if len(related_objects):
rel.RelatedResourceObjects = related_objects
else:
self.file.remove(rel)
def execute(self) -> None:
is_ifc2x3 = self.file.schema == "IFC2X3"
products = set(self.settings["products"])
referenced = ifcopenshell.util.element.get_referenced_elements(self.settings["reference"])
products -= products.difference(referenced)
# all products are already unassigned from a reference
if not products:
return
rooted_products: set[ifcopenshell.entity_instance] = set()
non_rooted_products: set[ifcopenshell.entity_instance] = set()
for product in self.settings["products"]:
if product.is_a("IfcRoot"):
rooted_products.add(product)
else:
non_rooted_products.add(product)
if non_rooted_products and is_ifc2x3:
raise TypeError(f"Cannot add reference to non-IfcRoot element in IFC2X3: {non_rooted_products}.")
if rooted_products:
reference_rels: set[ifcopenshell.entity_instance] = set()
for product in rooted_products:
reference_rels.update(product.HasAssociations)
reference_rels = {
rel
for rel in reference_rels
if rel.is_a("IfcRelAssociatesClassification")
and rel.RelatingClassification == self.settings["reference"]
}
for rel in reference_rels:
related_objects = set(rel.RelatedObjects) - rooted_products
if related_objects:
rel.RelatedObjects = list(related_objects)
ifcopenshell.api.run("owner.update_owner_history", self.file, **{"element": rel})
else:
history = rel.OwnerHistory
self.file.remove(rel)
if history:
ifcopenshell.util.element.remove_deep2(self.file, history)
if non_rooted_products:
reference_rels: set[ifcopenshell.entity_instance] = set()
for product in non_rooted_products:
rels = getattr(product, "HasExternalReferences", None)
if rels is None:
rels = getattr(product, "HasExternalReference", [])
reference_rels.update(rels)
reference_rels = {rel for rel in reference_rels if rel.RelatingReference == self.settings["reference"]}
for rel in reference_rels:
related_objects = set(rel.RelatedResourceObjects) - non_rooted_products
if related_objects:
rel.RelatedResourceObjects = list(related_objects)
else:
self.file.remove(rel)
# TODO: we only handle lightweight classifications here
if (
not self.settings["reference"].ClassificationRefForObjects
and not self.settings["reference"].ExternalReferenceForResources
):
referenced_elements = ifcopenshell.util.element.get_referenced_elements(self.settings["reference"])
if not referenced_elements:
self.file.remove(self.settings["reference"])
@@ -17,11 +17,18 @@
# along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
import ifcopenshell
import ifcopenshell.api
from typing import Union
class Usecase:
def __init__(self, file, product=None, constraint=None):
"""Assigns a constraint to a product
def __init__(
self,
file: ifcopenshell.file,
products: list[ifcopenshell.entity_instance],
constraint: ifcopenshell.entity_instance,
):
"""Assigns a constraint to a list of products
This assigns a relationship between a product and a constraint, so that
when a product's properties and quantities do not match the requirements
@@ -31,36 +38,58 @@ class Usecase:
constraints are inherited from the type. This way, it is not necessary
to create lots of constraint assignments.
:param product: The product the constraint applies to. This is anything
:param products: The list of products the constraint applies to. This is anything
which can have properties or quantities.
:type product: ifcopenshell.entity_instance.entity_instance
:type products: list[ifcopenshell.entity_instance.entity_instance]
:param constraint: The IfcObjective constraint
:type constraint: ifcopenshell.entity_instance.entity_instance
:return: The new or updated IfcRelAssociatesConstraint relationship
or `None` if `products` was an empty list.
:rtype: ifcopenshell.entity_instance.entity_instance
"""
self.file = file
self.settings = {
"product": product,
"products": products,
"constraint": constraint,
}
def execute(self):
rel = self.get_constraint_rel()
related_objects = set(rel.RelatedObjects) if rel.RelatedObjects else set()
related_objects.add(self.settings["product"])
rel.RelatedObjects = list(related_objects)
return rel
def execute(self) -> Union[ifcopenshell.entity_instance, None]:
products = set(self.settings["products"])
if not products:
return
self.constraint = self.settings["constraint"]
rels = self.get_constraint_rels()
related_objects = set()
for rel in rels:
related_objects.update(rel.RelatedObjects)
products_to_assign = products - related_objects
if not products_to_assign:
return rels[0]
rel = next(iter(rels), None)
if rel:
related_objects = set(rel.RelatedObjects) | products_to_assign
rel.RelatedObjects = list(related_objects)
ifcopenshell.api.run("owner.update_owner_history", self.file, **{"element": rel})
return rel
def get_constraint_rel(self):
for rel in self.file.by_type("IfcRelAssociatesConstraint"):
if rel.RelatingConstraint == self.settings["constraint"]:
return rel
return self.file.create_entity(
"IfcRelAssociatesConstraint",
**{
"GlobalId": ifcopenshell.guid.new(),
# TODO: owner history
"RelatingConstraint": self.settings["constraint"],
"OwnerHistory": ifcopenshell.api.run("owner.create_owner_history", self.file),
"RelatingConstraint": self.constraint,
"RelatedObjects": list(products_to_assign),
}
)
def get_constraint_rels(self) -> list[ifcopenshell.entity_instance]:
rels = []
for rel in self.file.get_inverse(self.constraint):
if rel.is_a("IfcRelAssociatesConstraint"):
rels.append(rel)
return rels
@@ -17,18 +17,24 @@
# along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
import ifcopenshell
import ifcopenshell.api
import ifcopenshell.util.element
class Usecase:
def __init__(self, file, product=None, constraint=None):
"""Unassigns a constraint to a product
def __init__(
self,
file: ifcopenshell.file,
products: list[ifcopenshell.entity_instance],
constraint: ifcopenshell.entity_instance,
):
"""Unassigns a constraint from a list of products
The constraint will not be deleted and is available to be assigned to
other products.
:param product: The product the constraint applies to.
:type product: ifcopenshell.entity_instance.entity_instance
:param products: The list of products the constraint applies to.
:type products: list[ifcopenshell.entity_instance.entity_instance]
:param constraint: The IfcObjective constraint
:type constraint: ifcopenshell.entity_instance.entity_instance
:return: None
@@ -36,14 +42,42 @@ class Usecase:
"""
self.file = file
self.settings = {
"product": product,
"products": products,
"constraint": constraint,
}
def execute(self):
for rel in self.settings["product"].HasAssociations:
if rel.is_a("IfcRelAssociatesConstraint") and rel.RelatingConstraint == self.settings["constraint"]:
history = rel.OwnerHistory
self.file.remove(rel)
if history:
ifcopenshell.util.element.remove_deep2(self.file, history)
products = set(self.settings["products"])
if not products:
return
self.constraint = self.settings["constraint"]
rels = self.get_constraint_rels()
related_objects = set()
for rel in rels:
related_objects.update(rel.RelatedObjects)
if not related_objects.intersection(products):
return
for rel in rels:
related_objects = set(rel.RelatedObjects)
if not related_objects.intersection(products):
continue
related_objects -= products
if related_objects:
rel.RelatedObjects = list(related_objects)
ifcopenshell.api.run("owner.update_owner_history", self.file, **{"element": rel})
continue
history = rel.OwnerHistory
self.file.remove(rel)
if history:
ifcopenshell.util.element.remove_deep2(self.file, history)
def get_constraint_rels(self) -> list[ifcopenshell.entity_instance]:
rels = []
for rel in self.file.get_inverse(self.constraint):
if rel.is_a("IfcRelAssociatesConstraint"):
rels.append(rel)
return rels
@@ -69,10 +69,12 @@ class Usecase:
def execute(self) -> ifcopenshell.entity_instance:
if self.file.schema == "IFC2X3":
reference = self.file.create_entity("IfcDocumentReference")
reference = self.file.create_entity("IfcDocumentReference", ItemReference="X")
if self.settings["information"]:
references = list(self.settings["information"].DocumentReferences or [])
references.append(reference)
self.settings["information"].DocumentReferences = references
return reference
return self.file.create_entity("IfcDocumentReference", ReferencedDocument=self.settings["information"])
return self.file.create_entity(
"IfcDocumentReference", ReferencedDocument=self.settings["information"], Identification="X"
)
@@ -17,11 +17,19 @@
# along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
import ifcopenshell
import ifcopenshell.api
import ifcopenshell.util.element
from typing import Union
class Usecase:
def __init__(self, file, product=None, document=None):
"""Assigns a document to a product
def __init__(
self,
file: ifcopenshell.file,
products: list[ifcopenshell.entity_instance],
document: ifcopenshell.entity_instance,
):
"""Assigns a document to a list of products
An object may be assigned to zero, one, or multiple documents. Almost
any object or property may be assigned to a document, though typically
@@ -32,14 +40,16 @@ class Usecase:
consistent with other external relationships (such as classification
systems or libraries).
:param product: The object to associate the document to. This could be
:param product: The list of objects to associate the document to. This could be
almost any sensible object in IFC.
:type product: ifcopenshell.entity_instance.entity_instance
:type product: list[ifcopenshell.entity_instance.entity_instance]
:param document: The IfcDocumentReference to associate to, or
alternatively an IfcDocumentInformation, though this is not
recommended.
:type document: ifcopenshell.entity_instance.entity_instance
:return: The IfcRelAssociatesDocument relationship
or `None` if `products` was an empty list or all products were
already assigned to the `document`.
:rtype: ifcopenshell.entity_instance.entity_instance
Example:
@@ -54,42 +64,51 @@ class Usecase:
reference = ifcopenshell.api.run("document.add_reference", model, information=document)
# Let's imagine storey represents an IfcBuildingStorey for the ground floor
ifcopenshell.api.run("document.assign_document", model, product=storey, document=reference)
ifcopenshell.api.run("document.assign_document", model, products=[storey], document=reference)
"""
self.file = file
self.settings = {
"product": product,
"products": products,
"document": document,
}
def execute(self):
rel = self.get_document_rel()
related_objects = set(rel.RelatedObjects) if rel.RelatedObjects else set()
related_objects.add(self.settings["product"])
rel.RelatedObjects = list(related_objects)
def execute(self) -> Union[ifcopenshell.entity_instance, None]:
# TODO: do we need to support non-ifcroot elements like we do in classification.add_reference?
# NOTE: reuses code from `library.assign_reference`
referenced_elements = ifcopenshell.util.element.get_referenced_elements(self.settings["document"])
products: set[ifcopenshell.entity_instance] = set(self.settings["products"])
products = products - referenced_elements
if not products:
return
def get_document_rel(self):
if self.file.schema == "IFC2X3":
for rel in self.file.by_type("IfcRelAssociatesDocument"):
if rel.RelatingDocument == self.settings["document"]:
return rel
rel = next(
(
r
for r in self.file.by_type("IfcRelAssociatesDocument")
if r.RelatingDocument == self.settings["document"]
),
None,
)
else:
if (
hasattr(self.settings["document"], "DocumentRefForObjects")
and self.settings["document"].DocumentRefForObjects
):
return self.settings["document"].DocumentRefForObjects[0]
elif (
hasattr(self.settings["document"], "DocumentInfoForObjects")
and self.settings["document"].DocumentInfoForObjects
):
return self.settings["document"].DocumentInfoForObjects[0]
ifc_class = self.settings["document"].is_a()
if ifc_class == "IfcDocumentReference":
rel = next(iter(self.settings["document"].DocumentRefForObjects), None)
elif ifc_class == "IfcDocumentInformation":
rel = next(iter(self.settings["document"].DocumentInfoForObjects), None)
return self.file.create_entity(
"IfcRelAssociatesDocument",
**{
"GlobalId": ifcopenshell.guid.new(),
"OwnerHistory": ifcopenshell.api.run("owner.create_owner_history", self.file),
"RelatingDocument": self.settings["document"],
}
)
if not rel:
return self.file.create_entity(
"IfcRelAssociatesDocument",
GlobalId=ifcopenshell.guid.new(),
OwnerHistory=ifcopenshell.api.run("owner.create_owner_history", self.file),
RelatedObjects=list(products),
RelatingDocument=self.settings["document"],
)
related_objects = set(rel.RelatedObjects) | products
rel.RelatedObjects = list(related_objects)
ifcopenshell.api.run("owner.update_owner_history", self.file, element=rel)
return rel
@@ -17,16 +17,22 @@
# along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
import ifcopenshell
import ifcopenshell.api
import ifcopenshell.util.element
class Usecase:
def __init__(self, file, product=None, document=None):
"""Unassigns a document and a product association
def __init__(
self,
file: ifcopenshell.file,
products: list[ifcopenshell.entity_instance],
document: ifcopenshell.entity_instance,
):
"""Unassigns a document and an association to the list of products
:param product: The object that the document reference or information is
:param product: The list of objects that the document reference or information is
related to.
:type product: ifcopenshell.entity_instance.entity_instance
:type product: list[ifcopenshell.entity_instance.entity_instance]
:param document: The IfcDocumentReference (typically) or in rare cases
the IfcDocumentInformation that is associated with the product
:type document: ifcopenshell.entity_instance.entity_instance
@@ -45,24 +51,39 @@ class Usecase:
reference = ifcopenshell.api.run("document.add_reference", model, information=document)
# Let's imagine storey represents an IfcBuildingStorey for the ground floor
ifcopenshell.api.run("document.assign_document", model, product=storey, document=reference)
ifcopenshell.api.run("document.assign_document", model, products=[storey], document=reference)
# Now let's change our mind and remove the association
ifcopenshell.api.run("document.unassign_document", model, product=storey, document=reference)
ifcopenshell.api.run("document.unassign_document", model, products=[storey], document=reference)
"""
self.file = file
self.settings = {
"product": product,
"products": products,
"document": document,
}
def execute(self):
for rel in self.settings["product"].HasAssociations:
if rel.is_a("IfcRelAssociatesDocument") and rel.RelatingDocument == self.settings["document"]:
if len(rel.RelatedObjects) == 1:
history = rel.OwnerHistory
self.file.remove(rel)
if history:
ifcopenshell.util.element.remove_deep2(self.file, history)
else:
rel.RelatedObjects = [o for o in rel.RelatedObjects if o != self.settings["product"]]
# TODO: do we need to support non-ifcroot elements like we do in classification.add_reference?
# NOTE: reuses code from `library.un assign_reference`
reference_rels: set[ifcopenshell.entity_instance] = set()
products = set(self.settings["products"])
for product in products:
reference_rels.update(product.HasAssociations)
reference_rels = {
rel
for rel in reference_rels
if rel.is_a("IfcRelAssociatesDocument") and rel.RelatingDocument == self.settings["document"]
}
for rel in reference_rels:
related_objects = set(rel.RelatedObjects) - products
if related_objects:
rel.RelatedObjects = list(related_objects)
ifcopenshell.api.run("owner.update_owner_history", self.file, **{"element": rel})
else:
history = rel.OwnerHistory
self.file.remove(rel)
if history:
ifcopenshell.util.element.remove_deep2(self.file, history)
@@ -26,7 +26,7 @@ class Usecase:
surveyor, and a third-party digital engineer with expertise in IFC to
moderate. For more information, read the BlenderBIM Add-on documentation
for Georeferencing:
https://blenderbim.org/docs/users/georeferencing.html
https://docs.blenderbim.org/users/georeferencing.html
For more information about the attributes and data types of an
IfcMapConversion, consult the IFC documentation.
@@ -17,23 +17,30 @@
# along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
import ifcopenshell
import ifcopenshell.api
import ifcopenshell.util.element
from typing import Union
class Usecase:
def __init__(self, file, product=None, reference=None):
"""Associates a product with a library reference
def __init__(
self, file: ifcopenshell.file, products: ifcopenshell.entity_instance, reference: ifcopenshell.entity_instance
):
"""Associates a list products with a library reference
A product may be associated with zero, one, or many references across
multiple libraries. See ifcopenshell.api.library.add_reference for more
detail about how references work.
:param product: The IfcProduct you want to associate with the reference
:type product: ifcopenshell.entity_instance.entity_instance
:param products: The list of IfcProducts you want to associate with the reference
:type products: list[ifcopenshell.entity_instance.entity_instance]
:param reference: The IfcLibraryReference you want the product to be
associated with.
:type reference: ifcopenshell.entity_instance.entity_instance
:return: The IfcRelAssociatesLibrary relationship entity
:rtype: ifcopenshell.entity_instance.entity_instance
or `None` if `products` was an empty list or all products were
already assigned to the `reference`.
:rtype: Union[ifcopenshell.entity_instance.entity_instance, None]
Example:
@@ -51,40 +58,46 @@ class Usecase:
ifc_class="IfcUnitaryEquipment", predefined_type="AIRHANDLER")
# And now assign the IFC model's AHU with its Brickschema counterpart
ifcopenshell.api.run("library.assign_reference", model, reference=reference, product=ahu)
ifcopenshell.api.run("library.assign_reference", model, reference=reference, products=[ahu])
"""
self.file = file
self.settings = {
"product": product,
"products": products,
"reference": reference,
}
def execute(self):
def execute(self) -> Union[ifcopenshell.entity_instance, None]:
# TODO: do we need to support non-ifcroot elements like we do in classification.add_reference?
referenced_elements = ifcopenshell.util.element.get_referenced_elements(self.settings["reference"])
products: set[ifcopenshell.entity_instance] = set(self.settings["products"])
products = products - referenced_elements
if not products:
return
if self.file.schema == "IFC2X3":
rels = self.get_ifc2x3_rels()
rel = next(
(
r
for r in self.file.by_type("IfcRelAssociatesLibrary")
if r.RelatingLibrary == self.settings["reference"]
),
None,
)
else:
rels = self.settings["reference"].LibraryRefForObjects
if not rels:
rel = next(iter(self.settings["reference"].LibraryRefForObjects), None)
if not rel:
return self.file.create_entity(
"IfcRelAssociatesLibrary",
GlobalId=ifcopenshell.guid.new(),
OwnerHistory=ifcopenshell.api.run("owner.create_owner_history", self.file),
RelatedObjects=[self.settings["product"]],
RelatedObjects=list(products),
RelatingLibrary=self.settings["reference"],
)
for rel in rels:
if self.settings["product"] in rel.RelatedObjects:
return rel
rel = rels[0]
related_objects = list(rel.RelatedObjects)
related_objects.append(self.settings["product"])
rel.RelatedObjects = related_objects
related_objects = set(rel.RelatedObjects) | products
rel.RelatedObjects = list(related_objects)
ifcopenshell.api.run("owner.update_owner_history", self.file, element=rel)
return rel
def get_ifc2x3_rels(self):
return [
r for r in self.file.by_type("IfcRelAssociatesLibrary") if r.RelatingLibrary == self.settings["reference"]
]
@@ -18,18 +18,24 @@
import ifcopenshell
import ifcopenshell.util.element
import ifcopenshell.api
class Usecase:
def __init__(self, file, reference=None, product=None):
"""Unassigns a product from a reference
def __init__(
self,
file: ifcopenshell.file,
reference: ifcopenshell.entity_instance,
products: list[ifcopenshell.entity_instance],
):
"""Unassigns a product of products from a reference
If the product isn't assigned to the reference, nothing will happen.
:param reference: The IfcLibraryReference to unassign from
:type reference: ifcopenshell.entity_instance.entity_instance
:param product: A IfcProduct element to unassign from the reference
:type product: ifcopenshell.entity_instance.entity_instance
:param products: A list of IfcProduct elements to unassign from the reference
:type products: list[ifcopenshell.entity_instance.entity_instance]
:return: None
:rtype: None
@@ -49,27 +55,36 @@ class Usecase:
ifc_class="IfcUnitaryEquipment", predefined_type="AIRHANDLER")
# And now assign the IFC model's AHU with its Brickschema counterpart
ifcopenshell.api.run("library.assign_reference", model, reference=reference, product=ahu)
ifcopenshell.api.run("library.assign_reference", model, reference=reference, products=[ahu])
# Let's change our mind and unassign it.
ifcopenshell.api.run("library.unassign_reference", model, reference=reference, product=ahu)
ifcopenshell.api.run("library.unassign_reference", model, reference=reference, products=[ahu])
"""
self.file = file
self.settings = {"reference": reference, "product": product}
self.settings = {"reference": reference, "products": products}
def execute(self):
rels = self.settings["reference"].LibraryRefForObjects
if not rels:
return
for rel in rels:
if self.settings["product"] in rel.RelatedObjects:
if len(rel.RelatedObjects) == 1:
history = rel.OwnerHistory
self.file.remove(rel)
if history:
ifcopenshell.util.element.remove_deep2(self.file, history)
continue
related_objects = list(rel.RelatedObjects)
related_objects.remove(self.settings["product"])
rel.RelatedObjects = related_objects
# TODO: do we need to support non-ifcroot elements like we do in classification.add_reference?
reference_rels: set[ifcopenshell.entity_instance] = set()
products = set(self.settings["products"])
for product in products:
reference_rels.update(product.HasAssociations)
reference_rels = {
rel
for rel in reference_rels
if rel.is_a("IfcRelAssociatesLibrary") and rel.RelatingLibrary == self.settings["reference"]
}
for rel in reference_rels:
related_objects = set(rel.RelatedObjects) - products
if related_objects:
rel.RelatedObjects = list(related_objects)
ifcopenshell.api.run("owner.update_owner_history", self.file, **{"element": rel})
else:
history = rel.OwnerHistory
self.file.remove(rel)
if history:
ifcopenshell.util.element.remove_deep2(self.file, history)
@@ -38,7 +38,7 @@ def get_application(ifc: ifcopenshell.file) -> Union[ifcopenshell.entity_instanc
if not app and ifc.schema == "IFC2X3":
raise Exception(
"Please create an application to continue. See the owner.create_owner_history docs for more info."
"https://blenderbim.org/docs-python/autoapi/ifcopenshell/api/owner/create_owner_history/index.html"
"https://docs.ifcopenshell.org/autoapi/ifcopenshell/api/owner/create_owner_history/index.html"
)
return (app or [None])[0]
@@ -58,7 +58,7 @@ def get_user(ifc: ifcopenshell.file) -> Union[ifcopenshell.entity_instance, None
if not pao and ifc.schema == "IFC2X3":
raise Exception(
"Please create a user to continue. See the owner.create_owner_history docs for more info."
"https://blenderbim.org/docs-python/autoapi/ifcopenshell/api/owner/create_owner_history/index.html"
"https://docs.ifcopenshell.org/autoapi/ifcopenshell/api/owner/create_owner_history/index.html"
)
return (pao or [None])[0]
@@ -22,11 +22,16 @@ import ifcopenshell.util.element
class Usecase:
def __init__(self, file, product=None, relating_structure=None):
"""Dereferences the a product and space
def __init__(
self,
file: ifcopenshell.file,
products: list[ifcopenshell.entity_instance],
relating_structure: ifcopenshell.entity_instance,
):
"""Dereferences a list of products and space
:param product: The physical IfcElement that exists in the space.
:type product: ifcopenshell.entity_instance.entity_instance
:param products: The list of physical IfcElements that exists in the space.
:type products: list[ifcopenshell.entity_instance.entity_instance]
:param relating_structure: The IfcSpatialStructureElement element, such
as IfcBuilding, IfcBuildingStorey, or IfcSpace that the element
exists in.
@@ -59,23 +64,24 @@ class Usecase:
ifcopenshell.api.run("spatial.assign_container", model, products=[column], relating_structure=storey1)
# And referenced in the others
ifcopenshell.api.run("spatial.reference_structure", model, product=column, relating_structure=storey2)
ifcopenshell.api.run("spatial.reference_structure", model, product=column, relating_structure=storey3)
ifcopenshell.api.run("spatial.reference_structure", model, products=[column], relating_structure=storey2)
ifcopenshell.api.run("spatial.reference_structure", model, products=[column], relating_structure=storey3)
# Actually, it only goes up to storey 2.
ifcopenshell.api.run("spatial.dereference_structure", model, product=column, relating_structure=storey3)
ifcopenshell.api.run("spatial.dereference_structure", model, products=[column], relating_structure=storey3)
"""
self.file = file
self.settings = {"product": product, "relating_structure": relating_structure}
self.settings = {"products": products, "relating_structure": relating_structure}
def execute(self):
for rel in self.settings["product"].ReferencedInStructures:
if rel.RelatingStructure != self.settings["relating_structure"]:
def execute(self) -> None:
products = set(self.settings["products"])
for rel in self.settings["relating_structure"].ReferencesElements:
related_elements = set(rel.RelatedElements)
if not related_elements.intersection(products):
continue
related_elements = list(rel.RelatedElements)
related_elements.remove(self.settings["product"])
related_elements = related_elements - products
if related_elements:
rel.RelatedElements = related_elements
rel.RelatedElements = list(related_elements)
ifcopenshell.api.run("owner.update_owner_history", self.file, **{"element": rel})
else:
history = rel.OwnerHistory
@@ -18,11 +18,18 @@
import ifcopenshell
import ifcopenshell.api
import ifcopenshell.util.element
from typing import Union
class Usecase:
def __init__(self, file, product=None, relating_structure=None):
"""Denote that a product is related to a spatial structure
def __init__(
self,
file: ifcopenshell.file,
products: list[ifcopenshell.entity_instance],
relating_structure: ifcopenshell.entity_instance,
):
"""Denote that a list products is related to a list of spatial structures
This is similar to ifcopenshell.api.spatial.assign_container, except
that containment can only occur between a product and a single spatial
@@ -39,13 +46,15 @@ class Usecase:
Referencing is non-hierarchical, so a door may be referenced in multiple
spaces simultaneously.
:param product: The physical IfcElement that exists in the space.
:type product: ifcopenshell.entity_instance.entity_instance
:param products: The list of physical IfcElements that exists in the space.
:type products: list[ifcopenshell.entity_instance.entity_instance]
:param relating_structure: The IfcSpatialStructureElement element, such
as IfcBuilding, IfcBuildingStorey, or IfcSpace that the element
exists in.
:type relating_structure: ifcopenshell.entity_instance.entity_instance
:return: The IfcRelReferencedInSpatialStructure relationship instance
:rtype: ifcopenshell.entity_instance.entity_instance
or `None` if `products` was an empty list.
:rtype: Union[ifcopenshell.entity_instance, None]
Example:
@@ -73,37 +82,43 @@ class Usecase:
ifcopenshell.api.run("spatial.assign_container", model, products=[column], relating_structure=storey1)
# And referenced in the others
ifcopenshell.api.run("spatial.reference_structure", model, product=column, relating_structure=storey2)
ifcopenshell.api.run("spatial.reference_structure", model, product=column, relating_structure=storey3)
ifcopenshell.api.run(
"spatial.reference_structure", model, products=[column], relating_structure=[storey2, storey3]
)
"""
self.file = file
self.settings = {
"product": product,
"products": products,
"relating_structure": relating_structure,
}
def execute(self):
referenced_in_structures = self.settings["product"].ReferencedInStructures
references_elements = self.settings["relating_structure"].ReferencesElements
def execute(self) -> Union[ifcopenshell.entity_instance, None]:
structure = self.settings["relating_structure"]
products = set(self.settings["products"])
for rel in referenced_in_structures:
if rel.RelatingStructure == self.settings["relating_structure"]:
return
if not products:
return
if references_elements:
related_elements = list(references_elements[0].RelatedElements)
related_elements.append(self.settings["product"])
references_elements[0].RelatedElements = related_elements
ifcopenshell.api.run("owner.update_owner_history", self.file, **{"element": references_elements[0]})
else:
references_elements = self.file.create_entity(
referenced = ifcopenshell.util.element.get_structure_referenced_elements(structure)
products_to_assign = products - referenced
rel = next(iter(structure.ReferencesElements), None)
if not products_to_assign:
return rel
if rel is None:
rel = self.file.create_entity(
"IfcRelReferencedInSpatialStructure",
**{
"GlobalId": ifcopenshell.guid.new(),
"OwnerHistory": ifcopenshell.api.run("owner.create_owner_history", self.file),
"RelatedElements": [self.settings["product"]],
"RelatingStructure": self.settings["relating_structure"],
"RelatedElements": list(products_to_assign),
"RelatingStructure": structure,
}
)
else:
related_elements = set(rel.RelatedElements) | products_to_assign
rel.RelatedElements = list(related_elements)
ifcopenshell.api.run("owner.update_owner_history", self.file, **{"element": rel})
return references_elements
return rel
@@ -30,7 +30,7 @@ import functools
import subprocess
import sys
import time
from typing import Union, Any, Callable, TypeVar
from typing import Union, Any, Callable, TypeVar, overload
from . import ifcopenshell_wrapper
from . import settings
@@ -317,15 +317,25 @@ class entity_instance(object):
return self.wrapped_data.to_string(valid_spf)
def is_a(self, *args) -> Union[str, bool]:
@overload
def is_a(self) -> str: ...
@overload
def is_a(self, ifc_class: str) -> bool: ...
@overload
def is_a(self, with_schema: bool) -> str: ...
def is_a(self, *args: Union[str, bool]) -> Union[str, bool]:
"""Return the IFC class name of an instance, or checks if an instance belongs to a class.
The check will also return true if a parent class name is provided.
:param args: If specified, is a case insensitive IFC class name to check
:type args: string
or if specified as a boolean then will define whether
returned IFC class name should include schema name
(e.g. "IFC4.IfcWall" if `True` and "IfcWall" if `False`).
If omitted will act as `False`.
:type args: Union[str, bool]
:returns: Either the name of the class, or a boolean if it passes the check
:rtype: string|bool
:rtype: Union[str, bool]
Example:
@@ -23,10 +23,10 @@ from typing import Optional
def get_references(element: ifcopenshell.entity_instance, should_inherit=True) -> set[ifcopenshell.entity_instance]:
results = set()
if not element.is_a("IfcRoot"):
if hasattr(element, "HasExternalReferences"):
return {r.RelatingReference for r in element.HasExternalReferences or []}
elif hasattr(element, "HasExternalReference"): # Seriously, IFC?
return {r.RelatingReference for r in element.HasExternalReference or []}
if (references := getattr(element, "HasExternalReferences", None)) is not None or (
references := getattr(element, "HasExternalReference", None)
) is not None:
return {r.RelatingReference for r in references}
if should_inherit:
element_type = ifcopenshell.util.element.get_type(element)
if element_type and element_type != element:
@@ -18,20 +18,59 @@
#
#
def get_constraints(product):
import ifcopenshell
from typing import Union
def get_constraints(product: ifcopenshell.entity_instance) -> list[ifcopenshell.entity_instance]:
"""
Retrieves the constraints assigned to the `product`.
:param product: The IFC element.
:type product: ifcopenshell.entity_instance.entity_instance
:return: List of assigned constraints.
:rtype: list[ifcopenshell.entity_instance.entity_instance]
"""
constraints = []
for rel in product.HasAssociations or []:
if rel.is_a("IfcRelAssociatesConstraint"):
constraints.append(rel.RelatingConstraint)
return constraints
def get_metrics(constraint):
def get_constrained_elements(constraint: ifcopenshell.entity_instance) -> set[ifcopenshell.entity_instance]:
"""
Retrieves the elements constrained by a `constraint`.
:param product: The IFC element.
:type product: ifcopenshell.entity_instance.entity_instance
:return: Set of elements constrained by a `constrant`.
:rtype: set[ifcopenshell.entity_instance.entity_instance]
"""
elements = set()
for rel in constraint.file.get_inverse(constraint):
if rel.is_a("IfcRelAssociatesConstraint"):
elements.update(rel.RelatedObjects)
return elements
def get_metrics(constraint: ifcopenshell.entity_instance) -> list[ifcopenshell.entity_instance]:
"""
Retrieves the list of nested constraints for a IfcObjective `constraint`.
:param product: IfcObjective constraint.
:type product: ifcopenshell.entity_instance.entity_instance
:return: List of nested constraints.
:rtype: list[ifcopenshell.entity_instance.entity_instance]
"""
metrics = []
for metric in constraint.BenchmarkValues or []:
metrics.append(metric)
return metrics
def get_metric_reference(metric, is_deep=True):
def get_metric_reference(metric: ifcopenshell.entity_instance, is_deep=True):
def get_reference_Attribute(ref, path):
if ref:
if is_deep:
@@ -47,7 +86,10 @@ def get_metric_reference(metric, is_deep=True):
reference = metric.ReferencePath
return get_reference_Attribute(reference, "")
def get_metric_constraints(resource, attribute):
def get_metric_constraints(
resource: ifcopenshell.entity_instance, attribute
) -> Union[list[ifcopenshell.entity_instance], None]:
metrics = []
for constraint in get_constraints(resource) or []:
for metric in get_metrics(constraint) or []:
@@ -60,15 +102,14 @@ def get_metric_constraints(resource, attribute):
return metrics
return None
def is_hard_constraint(metric):
if metric.ConstraintGrade == "HARD" and metric.Benchmark == "EQUALTO":
return True
def is_attribute_locked(product, attribute):
def is_hard_constraint(metric: ifcopenshell.entity_instance) -> bool:
return metric.ConstraintGrade == "HARD" and metric.Benchmark == "EQUALTO"
def is_attribute_locked(product: ifcopenshell.entity_instance, attribute) -> bool:
is_locked = False
metrics = get_metric_constraints(
product, attribute
)
metrics = get_metric_constraints(product, attribute)
for metric in metrics or []:
if is_hard_constraint(metric):
is_locked = True
@@ -20,16 +20,17 @@ from __future__ import annotations
import ifcopenshell
import ifcopenshell.util.element
from typing import Any, Callable, Optional, Union, Literal, overload
from collections import namedtuple
def get_pset(
element: ifcopenshell.entity_instance,
name: str,
prop: Optional[str] = None,
psets_only=False,
qtos_only=False,
should_inherit=True,
verbose=False,
psets_only: bool = False,
qtos_only: bool = False,
should_inherit: bool = True,
verbose: bool = False,
) -> Union[Any, dict[str, Any]]:
"""Retrieve a single property set or single property
@@ -429,8 +430,7 @@ def get_predefined_type(element: ifcopenshell.entity_instance) -> str:
element = ifcopenshell.by_type("IfcWall")[0]
predefined_type = ifcopenshell.util.element.get_predefined_type(element)
"""
element_type = get_type(element)
if element_type:
if element_type := get_type(element):
predefined_type = getattr(element_type, "PredefinedType", None)
if predefined_type == "USERDEFINED" or not predefined_type:
predefined_type = getattr(element_type, "ElementType", ...)
@@ -563,7 +563,9 @@ def get_material(
return get_material(relating_type, should_skip_usage)
def get_materials(element: ifcopenshell.entity_instance, should_inherit=True) -> list[ifcopenshell.entity_instance]:
def get_materials(
element: ifcopenshell.entity_instance, should_inherit: bool = True
) -> list[ifcopenshell.entity_instance]:
"""Gets individual materials of an element
If the element has a material set, the individual materials of that set are
@@ -826,7 +828,7 @@ def get_layers(
def get_container(
element: ifcopenshell.entity_instance, should_get_direct=False, ifc_class: Optional[str] = None
element: ifcopenshell.entity_instance, should_get_direct: bool = False, ifc_class: Optional[str] = None
) -> ifcopenshell.entity_instance:
"""
Retrieves the spatial structure container of an element.
@@ -902,6 +904,27 @@ def get_referenced_structures(element: ifcopenshell.entity_instance) -> list[ifc
return [r.RelatingStructure for r in getattr(element, "ReferencedInStructures", [])]
def get_structure_referenced_elements(structure: ifcopenshell.entity_instance) -> set[ifcopenshell.entity_instance]:
"""Retreives a set of elements referenced by a structure
:param structure: IfcSpatialElement
:type element: ifcopenshell.entity_instance.entity_instance
:return: A set of referenced elements, IfcSpatialReferenceSelect
:rtype: set[ifcopenshell.entity_instance.entity_instance]
Example:
.. code:: python
element = file.by_type("IfcBuildingStorey")[0]
print(ifcopenshell.util.element.get_structure_referenced_elements(element))
"""
referenced = set()
for rel in structure.ReferencesElements:
referenced.update(rel.RelatedElements)
return referenced
def get_decomposition(element: ifcopenshell.entity_instance, is_recursive=True) -> list[ifcopenshell.entity_instance]:
"""
Retrieves all subelements of an element based on the spatial decomposition
@@ -1092,6 +1115,66 @@ def get_components(element: ifcopenshell.entity_instance, include_ports=False) -
return is_decomposed_by[0].RelatedObjects
ReferenceData = namedtuple("ReferenceData", "inverse_attribute, rel_class, relating_element_attribute")
# References below are omitted because they do not introduce
# any additional referenced objects besides the objects
# from their supertype IfcExternalReference
# - IfcExternallyDefinedHatchStyle
# - IfcExternallyDefinedSurfaceStyle
# - IfcExternallyDefinedTextFont
REFERENCE_TYPES: dict[str, ReferenceData] = {
"IfcClassificationReference": ReferenceData(
"ClassificationRefForObjects",
"IfcRelAssociatesClassification",
"RelatingClassification",
),
"IfcDocumentReference": ReferenceData("DocumentRefForObjects", "IfcRelAssociatesDocument", "RelatingDocument"),
"IfcLibraryReference": ReferenceData("LibraryRefForObjects", "IfcRelAssociatesLibrary", "RelatingLibrary"),
}
def get_referenced_elements(reference: ifcopenshell.entity_instance) -> set[ifcopenshell.entity_instance]:
"""Get all elements with assigned `reference`
:param reference: IfcExternalReference subtype reference
:type reference: ifcopenshell.entity_instance.entity_instance
:return: The elements with assigned `reference`
:rtype: set[ifcopenshell.entity_instance.entity_instance]
Example:
.. code:: python
reference = file.by_type("IfcClassificationReference")[0]
elements = ifcopenshell.util.element.get_referenced_elements(reference)
"""
related_objects: set[ifcopenshell.entity_instance] = set()
ifc_file = reference.file
ifc_class = reference.is_a()
if ifc_file.schema == "IFC2X3":
reference_data = REFERENCE_TYPES.get(ifc_class)
if reference_data:
for rel in ifc_file.by_type(reference_data.rel_class):
if getattr(rel, reference_data.relating_element_attribute) == reference:
related_objects.update(rel.RelatedObjects)
else:
# IfcExternalReference
for external_rel in reference.ExternalReferenceForResources:
related_objects.update(external_rel.RelatedResourceObjects)
reference_data = REFERENCE_TYPES.get(ifc_class)
if reference_data:
for rel in getattr(reference, reference_data.inverse_attribute):
related_objects.update(rel.RelatedObjects)
return related_objects
def replace_attribute(element: ifcopenshell.entity_instance, old: Any, new: Any) -> None:
for i, attribute_value in enumerate(element):
if has_element_reference(attribute_value, old):
@@ -17,6 +17,7 @@
# along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
import shapely
import shapely.ops
import numpy as np
import ifcopenshell.util.element
import ifcopenshell.util.placement
+1 -1
View File
@@ -15,7 +15,7 @@ classifiers = [
"Programming Language :: Python :: 3",
"License :: OSI Approved :: GNU Lesser General Public License v3 or later (LGPLv3+)",
]
dependencies = ["mathutils", "shapely", "numpy", "isodate", "dateutil", "lark"]
dependencies = ["mathutils", "shapely", "numpy", "isodate", "python-dateutil", "lark"]
[project.urls]
"Homepage" = "http://ifcopenshell.org"
@@ -16,6 +16,7 @@
# You should have received a copy of the GNU Lesser General Public License
# along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
import pytest
import test.bootstrap
import ifcopenshell.api
import ifcopenshell.util.classification
@@ -23,85 +24,127 @@ import ifcopenshell.util.classification
class TestAddReference(test.bootstrap.IFC4):
def test_adding_a_reference(self):
is_ifc2x3 = self.file.schema == "IFC2X3"
ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcProject")
element = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcWall")
element2 = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcWall")
result = ifcopenshell.api.run("classification.add_classification", self.file, classification="Name")
ifcopenshell.api.run(
"classification.add_reference",
self.file,
product=element,
products=[element, element2],
identification="X",
name="Foobar",
classification=result,
)
references = list(ifcopenshell.util.classification.get_references(element))
assert len(references) == 1
assert references[0].Identification == "X"
assert getattr(references[0], "ItemReference" if is_ifc2x3 else "Identification") == "X"
assert references[0].Name == "Foobar"
assert references[0].ReferencedSource == self.file.by_type("IfcClassification")[0]
element2 = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcWall")
ifcopenshell.api.run(
"classification.add_reference",
self.file,
product=element2,
identification="X",
name="Foobar",
classification=result,
references2 = list(ifcopenshell.util.classification.get_references(element))
assert len(references2) == 1
assert getattr(references2[0], "ItemReference" if is_ifc2x3 else "Identification") == "X"
assert references2[0].Name == "Foobar"
assert references2[0] == references[0]
rel = next(
rel
for rel in self.file.by_type("IfcRelAssociatesClassification")
if rel.RelatingClassification == references[0]
)
assert list(ifcopenshell.util.classification.get_references(element2))[0].Identification == "X"
assert list(ifcopenshell.util.classification.get_references(element2))[0].Name == "Foobar"
assert list(ifcopenshell.util.classification.get_references(element2))[0] == references[0]
assert len(rel.RelatedObjects) == 2
def test_adding_a_library_based_reference(self):
is_ifc2x3 = self.file.schema == "IFC2X3"
library = ifcopenshell.file()
classification = library.createIfcClassification(Name="Name")
reference = library.createIfcClassificationReference(Identification="1", ReferencedSource=classification)
ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcProject")
element = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcWall")
element2 = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcWall")
result = ifcopenshell.api.run("classification.add_classification", self.file, classification=classification)
ifcopenshell.api.run(
"classification.add_reference",
self.file,
product=element,
products=[element, element2],
reference=reference,
classification=result,
)
references = list(ifcopenshell.util.classification.get_references(element))
assert len(references) == 1
assert references[0].Identification == "1"
assert getattr(references[0], "ItemReference" if is_ifc2x3 else "Identification") == "1"
assert references[0].ReferencedSource == self.file.by_type("IfcClassification")[0]
def test_adding_a_reference_to_a_resource(self):
references2 = list(ifcopenshell.util.classification.get_references(element2))
assert len(references2) == 1
assert getattr(references2[0], "ItemReference" if is_ifc2x3 else "Identification") == "1"
assert references2[0].ReferencedSource == self.file.by_type("IfcClassification")[0]
assert references[0] == references2[0]
rel = next(
rel
for rel in self.file.by_type("IfcRelAssociatesClassification")
if rel.RelatingClassification == references[0]
)
assert len(rel.RelatedObjects) == 2
def test_adding_a_reference_to_a_resource_and_to_a_root(self):
ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcProject")
element = self.file.createIfcMaterial()
element2 = self.file.createIfcCostValue()
element3 = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcWall")
result = ifcopenshell.api.run("classification.add_classification", self.file, classification="Name")
ifcopenshell.api.run(
"classification.add_reference",
self.file,
product=element,
identification="X",
name="Foobar",
classification=result,
)
if self.file.schema == "IFC2X3":
with pytest.raises(TypeError):
ifcopenshell.api.run(
"classification.add_reference",
self.file,
products=[element, element2, element3],
identification="X",
name="Foobar",
classification=result,
)
return
else:
ifcopenshell.api.run(
"classification.add_reference",
self.file,
products=[element, element2, element3],
identification="X",
name="Foobar",
classification=result,
)
references = list(ifcopenshell.util.classification.get_references(element))
assert len(references) == 1
assert references[0].Identification == "X"
assert references[0].Name == "Foobar"
assert references[0].ReferencedSource == self.file.by_type("IfcClassification")[0]
element2 = self.file.createIfcCostValue()
ifcopenshell.api.run(
"classification.add_reference",
self.file,
product=element2,
identification="X",
name="Foobar",
classification=result,
)
assert list(ifcopenshell.util.classification.get_references(element2))[0].Identification == "X"
assert list(ifcopenshell.util.classification.get_references(element2))[0].Name == "Foobar"
assert list(ifcopenshell.util.classification.get_references(element2))[0] == references[0]
references2 = list(ifcopenshell.util.classification.get_references(element2))
assert references2[0].Identification == "X"
assert references2[0].Name == "Foobar"
assert references2[0] == references[0]
references3 = list(ifcopenshell.util.classification.get_references(element3))
assert references3[0].Identification == "X"
assert references3[0].Name == "Foobar"
assert references3[0] == references[0]
assert len(self.file.by_type("IfcExternalReferenceRelationship")[0].RelatedResourceObjects) == 2
rel = next(
rel
for rel in self.file.by_type("IfcRelAssociatesClassification")
if rel.RelatingClassification == references[0]
)
assert len(rel.RelatedObjects) == 1
class TestAddReferenceIFC2X3(test.bootstrap.IFC2X3, TestAddReference):
pass
@@ -34,7 +34,7 @@ class TestRemoveClassification(test.bootstrap.IFC4):
ifcopenshell.api.run(
"classification.add_reference",
self.file,
product=element,
products=[element],
identification="X",
name="Foobar",
classification=result,
@@ -51,7 +51,7 @@ class TestRemoveClassification(test.bootstrap.IFC4):
ifcopenshell.api.run(
"classification.add_reference",
self.file,
product=element,
products=[element],
identification="X",
name="Foobar",
classification=result,
@@ -16,6 +16,7 @@
# You should have received a copy of the GNU Lesser General Public License
# along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
import pytest
import test.bootstrap
import ifcopenshell.api
import ifcopenshell.util.classification
@@ -25,58 +26,80 @@ class TestRemoveReference(test.bootstrap.IFC4):
def test_removing_a_reference(self):
ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcProject")
element = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcWall")
element2 = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcWall")
result = ifcopenshell.api.run("classification.add_classification", self.file, classification="Name")
reference = ifcopenshell.api.run(
"classification.add_reference",
self.file,
product=element,
products=[element, element2],
identification="X",
name="Foobar",
classification=result,
)
ifcopenshell.api.run("classification.remove_reference", self.file, product=element, reference=reference)
ifcopenshell.api.run(
"classification.remove_reference", self.file, products=[element, element2], reference=reference
)
assert len(ifcopenshell.util.classification.get_references(element)) == 0
assert len(ifcopenshell.util.classification.get_references(element2)) == 0
assert len(self.file.by_type("IfcClassificationReference")) == 0
def test_removing_a_reference_from_a_resource(self):
def test_removing_a_reference_from_a_resource_and_from_a_root(self):
ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcProject")
element = self.file.createIfcMaterial()
element2 = self.file.createIfcCostValue()
element3 = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcWall")
result = ifcopenshell.api.run("classification.add_classification", self.file, classification="Name")
reference = ifcopenshell.api.run(
"classification.add_reference",
self.file,
product=element,
identification="X",
name="Foobar",
classification=result,
if self.file.schema == "IFC2X3":
with pytest.raises(TypeError):
reference = ifcopenshell.api.run(
"classification.add_reference",
self.file,
products=[element, element2, element3],
identification="X",
name="Foobar",
classification=result,
)
return
else:
reference = ifcopenshell.api.run(
"classification.add_reference",
self.file,
products=[element, element2, element3],
identification="X",
name="Foobar",
classification=result,
)
ifcopenshell.api.run(
"classification.remove_reference", self.file, products=[element, element2, element3], reference=reference
)
ifcopenshell.api.run("classification.remove_reference", self.file, product=element, reference=reference)
assert len(ifcopenshell.util.classification.get_references(element)) == 0
assert len(ifcopenshell.util.classification.get_references(element2)) == 0
assert len(ifcopenshell.util.classification.get_references(element3)) == 0
assert len(self.file.by_type("IfcClassificationReference")) == 0
def test_retaining_the_reference_if_still_in_use(self):
ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcProject")
element = self.file.createIfcMaterial()
element2 = self.file.createIfcMaterial()
element = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcWall")
element2 = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcWall")
element3 = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcWall")
result = ifcopenshell.api.run("classification.add_classification", self.file, classification="Name")
reference = ifcopenshell.api.run(
"classification.add_reference",
self.file,
product=element,
identification="X",
name="Foobar",
classification=result,
)
reference2 = ifcopenshell.api.run(
"classification.add_reference",
self.file,
product=element2,
products=[element, element2, element3],
identification="X",
name="Foobar",
classification=result,
)
assert len(self.file.by_type("IfcClassificationReference")) == 1
ifcopenshell.api.run("classification.remove_reference", self.file, product=element, reference=reference)
ifcopenshell.api.run(
"classification.remove_reference", self.file, products=[element, element2], reference=reference
)
assert len(self.file.by_type("IfcClassificationReference")) == 1
ifcopenshell.api.run("classification.remove_reference", self.file, product=element2, reference=reference2)
ifcopenshell.api.run("classification.remove_reference", self.file, products=[element3], reference=reference)
assert len(self.file.by_type("IfcClassificationReference")) == 0
class TestRemoveReferenceIFC2X3(test.bootstrap.IFC2X3, TestRemoveReference):
pass
@@ -0,0 +1,64 @@
# IfcOpenShell - IFC toolkit and geometry engine
# Copyright (C) 2022 Dion Moult <dion@thinkmoult.com>
#
# This file is part of IfcOpenShell.
#
# IfcOpenShell 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.
#
# IfcOpenShell 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 IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
import pytest
import test.bootstrap
import ifcopenshell.api
import ifcopenshell.util.constraint
class TestAssignConstraint(test.bootstrap.IFC4):
def test_assign_a_constraint(self):
element = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcWall")
element2 = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcWall")
constraint = ifcopenshell.api.run("constraint.add_objective", self.file)
ifcopenshell.api.run(
"constraint.assign_constraint", self.file, products=[element, element2], constraint=constraint
)
assert ifcopenshell.util.constraint.get_constrained_elements(constraint) == {element, element2}
assert len(self.file.by_type("IfcRelAssociatesConstraint")) == 1
def test_doing_nothing_if_the_constraint_is_already_assigned(self):
constraint = ifcopenshell.api.run("constraint.add_objective", self.file)
element = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcWall")
element2 = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcWall")
ifcopenshell.api.run(
"constraint.assign_constraint", self.file, products=[element, element2], constraint=constraint
)
total_elements = len([e for e in self.file])
ifcopenshell.api.run(
"constraint.assign_constraint", self.file, products=[element, element2], constraint=constraint
)
assert len([e for e in self.file]) == total_elements
def test_that_old_relationships_are_updated_if_they_still_contain_elements(self):
constraint = ifcopenshell.api.run("constraint.add_objective", self.file)
element1 = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcWall")
ifcopenshell.api.run("constraint.assign_constraint", self.file, products=[element1], constraint=constraint)
rel = self.file.by_type("IfcRelAssociatesConstraint")[0]
element2 = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcWall")
element3 = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcWall")
ifcopenshell.api.run(
"constraint.assign_constraint", self.file, products=[element2, element3], constraint=constraint
)
assert len(rel.RelatedObjects) == 3
class TestAssignConstraintIFC2X3(test.bootstrap.IFC2X3, TestAssignConstraint):
pass
@@ -0,0 +1,67 @@
# IfcOpenShell - IFC toolkit and geometry engine
# Copyright (C) 2022 Dion Moult <dion@thinkmoult.com>
#
# This file is part of IfcOpenShell.
#
# IfcOpenShell 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.
#
# IfcOpenShell 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 IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
import pytest
import test.bootstrap
import ifcopenshell.api
import ifcopenshell.util.constraint
class TestUnassignConstraint(test.bootstrap.IFC4):
def test_unassigning_a_constraint(self):
constraint = ifcopenshell.api.run("constraint.add_objective", self.file)
element = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcWall")
element2 = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcWall")
ifcopenshell.api.run(
"constraint.assign_constraint", self.file, products=[element, element2], constraint=constraint
)
ifcopenshell.api.run(
"constraint.unassign_constraint", self.file, products=[element, element2], constraint=constraint
)
assert ifcopenshell.util.constraint.get_constrained_elements(element) == set()
assert len(self.file.by_type("IfcRelAssociatesConstraint")) == 0
def test_doing_nothing_if_no_constraint(self):
constraint = ifcopenshell.api.run("constraint.add_objective", self.file)
element = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcWall")
element2 = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcWall")
ifcopenshell.api.run(
"constraint.unassign_constraint", self.file, products=[element, element2], constraint=constraint
)
assert ifcopenshell.util.constraint.get_constrained_elements(element) == set()
assert ifcopenshell.util.constraint.get_constrained_elements(element2) == set()
def test_updating_the_rel_when_a_reference_is_removed_with_multipled_elements(self):
constraint = ifcopenshell.api.run("constraint.add_objective", self.file)
element1 = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcWall")
element2 = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcWall")
element3 = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcWall")
ifcopenshell.api.run("constraint.assign_constraint", self.file, products=[element1], constraint=constraint)
rel = self.file.by_type("IfcRelAssociatesConstraint")[0]
ifcopenshell.api.run(
"constraint.assign_constraint", self.file, products=[element2, element3], constraint=constraint
)
ifcopenshell.api.run(
"constraint.unassign_constraint", self.file, products=[element1, element2], constraint=constraint
)
assert rel.RelatedObjects == (element3,)
class TestUnassignConstraintIFC2X3(test.bootstrap.IFC2X3, TestUnassignConstraint):
pass
@@ -18,22 +18,25 @@
import test.bootstrap
import ifcopenshell.api
import ifcopenshell.util.element
class TestAssignDocument(test.bootstrap.IFC4):
def test_assigning_a_document(self):
element = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcWall")
reference = ifcopenshell.api.run("document.add_reference", self.file, information=None)
ifcopenshell.api.run("document.assign_document", self.file, product=element, document=reference)
ifcopenshell.api.run("document.assign_document", self.file, products=[element], document=reference)
assert element.HasAssociations[0].RelatingDocument == reference
assert ifcopenshell.util.element.get_referenced_elements(reference) == {element}
def test_assigning_multiple_documents(self):
element = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcWall")
element2 = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcWall")
reference = ifcopenshell.api.run("document.add_reference", self.file, information=None)
ifcopenshell.api.run("document.assign_document", self.file, product=element, document=reference)
ifcopenshell.api.run("document.assign_document", self.file, product=element2, document=reference)
ifcopenshell.api.run("document.assign_document", self.file, products=[element, element2], document=reference)
assert len(self.file.by_type("IfcRelAssociatesDocument")) == 1
assert element.HasAssociations[0].RelatingDocument == reference
assert element2.HasAssociations[0].RelatingDocument == reference
assert element.HasAssociations[0] == element.HasAssociations[0]
assert ifcopenshell.util.element.get_referenced_elements(reference) == {element, element2}
class TestAssignDocumentIFC2X3(test.bootstrap.IFC2X3, TestAssignDocument):
pass
@@ -35,7 +35,7 @@ class TestRemoveReference(test.bootstrap.IFC4):
wall = self.file.createIfcWall()
information = ifcopenshell.api.run("document.add_information", self.file, parent=None)
reference = ifcopenshell.api.run("document.add_reference", self.file, information=information)
ifcopenshell.api.run("document.assign_document", self.file, product=wall, document=reference)
ifcopenshell.api.run("document.assign_document", self.file, products=[wall], document=reference)
assert len(self.file.by_type("IfcRelAssociatesDocument")) == 2
ifcopenshell.api.run("document.remove_reference", self.file, reference=reference)
assert len(self.file.by_type("IfcDocumentReference")) == 0
@@ -18,23 +18,29 @@
import test.bootstrap
import ifcopenshell.api
import ifcopenshell.util.element
class TestUnassignDocument(test.bootstrap.IFC4):
def test_unassigning_a_document(self):
element = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcWall")
reference = ifcopenshell.api.run("document.add_reference", self.file, information=None)
ifcopenshell.api.run("document.assign_document", self.file, product=element, document=reference)
ifcopenshell.api.run("document.unassign_document", self.file, product=element, document=reference)
ifcopenshell.api.run("document.assign_document", self.file, products=[element], document=reference)
ifcopenshell.api.run("document.unassign_document", self.file, products=[element], document=reference)
assert not element.HasAssociations
assert not len(self.file.by_type("IfcRelAssociatesDocument"))
def test_unassigning_a_document_used_by_multiple_entities(self):
element = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcWall")
element2 = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcWall")
element3 = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcWall")
reference = ifcopenshell.api.run("document.add_reference", self.file, information=None)
ifcopenshell.api.run("document.assign_document", self.file, product=element, document=reference)
ifcopenshell.api.run("document.assign_document", self.file, product=element2, document=reference)
ifcopenshell.api.run("document.unassign_document", self.file, product=element, document=reference)
assert not element.HasAssociations
assert element2.HasAssociations[0].RelatingDocument == reference
ifcopenshell.api.run(
"document.assign_document", self.file, products=[element, element2, element3], document=reference
)
ifcopenshell.api.run("document.unassign_document", self.file, products=[element, element2], document=reference)
assert ifcopenshell.util.element.get_referenced_elements(reference) == {element3}
class TestUnassignDocumentIFC2X3(test.bootstrap.IFC2X3, TestUnassignDocument):
pass
@@ -20,39 +20,48 @@ import test.bootstrap
import ifcopenshell.api
def validate_ifc_file(ifc_file: ifcopenshell.file, use_json=True):
import ifcopenshell
import ifcopenshell.validate
if use_json:
logger = ifcopenshell.validate.json_logger()
else:
import logging
logger = logging.getLogger("validate")
logger.setLevel(logging.DEBUG)
ifcopenshell.validate.validate(ifc_file, logger, express_rules=True)
if use_json:
if logger.statements:
from pprint import pprint
pprint(logger.statements)
else:
print("IFC is completely valid.")
class TestAssignReference(test.bootstrap.IFC4):
def test_assigning_a_reference(self):
reference = self.file.createIfcLibraryReference()
product = self.file.createIfcWall()
product2 = self.file.createIfcWall()
ifcopenshell.api.run("library.assign_reference", self.file, product=product, reference=reference)
assert reference.LibraryRefForObjects[0].RelatedObjects == (product,)
ifcopenshell.api.run("library.assign_reference", self.file, product=product2, reference=reference)
assert reference.LibraryRefForObjects[0].RelatedObjects == (product, product2)
product3 = self.file.createIfcWall()
ifcopenshell.api.run("library.assign_reference", self.file, products=[product], reference=reference)
rel = self.file.by_type("IfcRelAssociatesLibrary")[0]
assert rel.RelatedObjects == (product,)
ifcopenshell.api.run("library.assign_reference", self.file, products=[product2, product3], reference=reference)
assert set(rel.RelatedObjects) == set((product, product2, product3))
def test_not_assigning_twice(self):
reference = self.file.createIfcLibraryReference()
product = self.file.createIfcWall()
ifcopenshell.api.run("library.assign_reference", self.file, product=product, reference=reference)
ifcopenshell.api.run("library.assign_reference", self.file, product=product, reference=reference)
assert reference.LibraryRefForObjects[0].RelatedObjects == (product,)
class TestAssignReferenceIFC2X3(test.bootstrap.IFC2X3):
def test_assigning_a_reference(self):
reference = self.file.createIfcLibraryReference()
product = self.file.createIfcWall()
product2 = self.file.createIfcWall()
ifcopenshell.api.run("library.assign_reference", self.file, product=product, reference=reference)
ifcopenshell.api.run("library.assign_reference", self.file, products=[product], reference=reference)
ifcopenshell.api.run("library.assign_reference", self.file, products=[product], reference=reference)
rel = self.file.by_type("IfcRelAssociatesLibrary")[0]
assert rel.RelatedObjects == (product,)
ifcopenshell.api.run("library.assign_reference", self.file, product=product2, reference=reference)
assert rel.RelatedObjects == (product, product2)
def test_not_assigning_twice(self):
reference = self.file.createIfcLibraryReference()
product = self.file.createIfcWall()
ifcopenshell.api.run("library.assign_reference", self.file, product=product, reference=reference)
ifcopenshell.api.run("library.assign_reference", self.file, product=product, reference=reference)
rel = self.file.by_type("IfcRelAssociatesLibrary")[0]
assert rel.RelatedObjects == (product,)
class TestAssignReferenceIFC2X3(test.bootstrap.IFC2X3, TestAssignReference):
pass
@@ -24,7 +24,7 @@ class TestRemoveReference(test.bootstrap.IFC4):
def test_removing_a_reference(self):
reference = self.file.createIfcLibraryReference()
product = self.file.createIfcWall()
ifcopenshell.api.run("library.assign_reference", self.file, product=product, reference=reference)
ifcopenshell.api.run("library.assign_reference", self.file, products=[product], reference=reference)
ifcopenshell.api.run("library.remove_reference", self.file, reference=reference)
assert len(self.file.by_type("IfcLibraryReference")) == 0
assert len(self.file.by_type("IfcRelAssociatesLibrary")) == 0
@@ -18,12 +18,20 @@
import test.bootstrap
import ifcopenshell.api
import ifcopenshell.util.element
class TestUnassignReference(test.bootstrap.IFC4):
def test_unassigning_a_reference(self):
reference = self.file.createIfcLibraryReference()
product = self.file.createIfcWall()
ifcopenshell.api.run("library.assign_reference", self.file, product=product, reference=reference)
ifcopenshell.api.run("library.unassign_reference", self.file, product=product, reference=reference)
products = [self.file.createIfcWall() for i in range(3)]
ifcopenshell.api.run("library.assign_reference", self.file, products=products, reference=reference)
ifcopenshell.api.run("library.unassign_reference", self.file, products=products[:1], reference=reference)
assert ifcopenshell.util.element.get_referenced_elements(reference) == set(products[1:])
ifcopenshell.api.run("library.unassign_reference", self.file, products=products[1:], reference=reference)
assert ifcopenshell.util.element.get_referenced_elements(reference) == set()
assert len(self.file.by_type("IfcRelAssociatesLibrary")) == 0
class TestUnassignReferenceIFC2X3(test.bootstrap.IFC2X3, TestUnassignReference):
pass
@@ -26,28 +26,42 @@ class TestDereferenceStructure(test.bootstrap.IFC4):
def test_removing_a_container(self):
element = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcBuilding")
subelement = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcWall")
ifcopenshell.api.run("spatial.reference_structure", self.file, product=subelement, relating_structure=element)
ifcopenshell.api.run("spatial.dereference_structure", self.file, product=subelement, relating_structure=element)
subelement2 = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcWall")
ifcopenshell.api.run(
"spatial.reference_structure", self.file, products=[subelement, subelement2], relating_structure=element
)
ifcopenshell.api.run(
"spatial.dereference_structure", self.file, products=[subelement, subelement2], relating_structure=element
)
assert ifcopenshell.util.element.get_referenced_structures(subelement) == []
assert len(self.file.by_type("IfcRelReferencedInSpatialStructure")) == 0
def test_doing_nothing_if_no_container(self):
element = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcBuilding")
subelement = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcWall")
ifcopenshell.api.run("spatial.dereference_structure", self.file, product=subelement, relating_structure=element)
subelement2 = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcWall")
ifcopenshell.api.run(
"spatial.dereference_structure", self.file, products=[subelement, subelement2], relating_structure=element
)
assert ifcopenshell.util.element.get_referenced_structures(subelement) == []
assert ifcopenshell.util.element.get_referenced_structures(subelement2) == []
def test_updating_the_rel_when_a_reference_is_removed_with_multipled_elements(self):
element = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcBuilding")
subelement1 = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcWall")
subelement2 = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcWall")
ifcopenshell.api.run("spatial.reference_structure", self.file, product=subelement1, relating_structure=element)
ifcopenshell.api.run("spatial.reference_structure", self.file, product=subelement2, relating_structure=element)
ifcopenshell.api.run("spatial.dereference_structure", self.file, product=subelement1, relating_structure=element)
assert self.file.by_type("IfcRelReferencedInSpatialStructure")[0].RelatedElements == (subelement2,)
subelement3 = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcWall")
ifcopenshell.api.run(
"spatial.reference_structure", self.file, products=[subelement1], relating_structure=element
)
ifcopenshell.api.run(
"spatial.reference_structure", self.file, products=[subelement2, subelement3], relating_structure=element
)
ifcopenshell.api.run(
"spatial.dereference_structure", self.file, products=[subelement1, subelement2], relating_structure=element
)
assert element.ReferencesElements[0].RelatedElements == (subelement3,)
def test_deleting_the_rel_when_a_container_is_removed_with_no_elements(self):
element = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcBuilding")
subelement = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcWall")
ifcopenshell.api.run("spatial.reference_structure", self.file, product=subelement, relating_structure=element)
ifcopenshell.api.run("spatial.dereference_structure", self.file, product=subelement, relating_structure=element)
assert len(self.file.by_type("IfcRelReferencedInSpatialStructure")) == 0
class TestDereferenceStructureIFC2X3(test.bootstrap.IFC2X3, TestDereferenceStructure):
pass
@@ -26,25 +26,39 @@ class TestReferenceStructure(test.bootstrap.IFC4):
def test_referencing_a_structure(self):
element = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcBuilding")
subelement = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcWall")
rel = ifcopenshell.api.run(
"spatial.reference_structure", self.file, product=subelement, relating_structure=element
subelement2 = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcWall")
ifcopenshell.api.run(
"spatial.reference_structure", self.file, products=[subelement, subelement2], relating_structure=element
)
assert ifcopenshell.util.element.get_referenced_structures(subelement) == [element]
assert rel.is_a("IfcRelReferencedInSpatialStructure")
assert ifcopenshell.util.element.get_structure_referenced_elements(element) == {subelement, subelement2}
def test_doing_nothing_if_the_structure_is_already_referenced(self):
element = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcBuilding")
subelement = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcWall")
ifcopenshell.api.run("spatial.reference_structure", self.file, product=subelement, relating_structure=element)
subelement2 = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcWall")
ifcopenshell.api.run(
"spatial.reference_structure", self.file, products=[subelement, subelement2], relating_structure=element
)
total_elements = len([e for e in self.file])
ifcopenshell.api.run("spatial.reference_structure", self.file, product=subelement, relating_structure=element)
ifcopenshell.api.run(
"spatial.reference_structure", self.file, products=[subelement, subelement2], relating_structure=element
)
assert len([e for e in self.file]) == total_elements
def test_that_old_relationships_are_updated_if_they_still_contain_elements(self):
element1 = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcBuilding")
element = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcBuilding")
subelement1 = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcWall")
ifcopenshell.api.run(
"spatial.reference_structure", self.file, products=[subelement1], relating_structure=element
)
subelement2 = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcWall")
ifcopenshell.api.run("spatial.reference_structure", self.file, product=subelement1, relating_structure=element1)
ifcopenshell.api.run("spatial.reference_structure", self.file, product=subelement2, relating_structure=element1)
subelement3 = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcWall")
ifcopenshell.api.run(
"spatial.reference_structure", self.file, products=[subelement2, subelement3], relating_structure=element
)
rel = subelement1.ReferencedInStructures[0]
assert len(rel.RelatedElements) == 2
assert len(rel.RelatedElements) == 3
class TestReferenceStructureIFC2X3(test.bootstrap.IFC2X3, TestReferenceStructure):
pass
@@ -18,6 +18,8 @@
import test.bootstrap
import ifcopenshell.api
import ifcopenshell.util.classification
import ifcopenshell.util.constraint
import ifcopenshell.util.element
import ifcopenshell.util.system
from datetime import datetime
@@ -186,3 +188,126 @@ class TestTemporarySupportForDeprecatedAPIArguments(test.bootstrap.IFC4):
assert len(self.file.by_type("IfcRelAssociatesMaterial")) == 0
assert len(self.file.by_type("IfcWall")) == 1
assert len(self.file.by_type("IfcMaterial")) == 1
@deprecation_check
def test_adding_a_reference(self):
ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcProject")
element = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcWall")
result = ifcopenshell.api.run("classification.add_classification", self.file, classification="Name")
ifcopenshell.api.run(
"classification.add_reference",
self.file,
product=element,
identification="X",
name="Foobar",
classification=result,
)
references = list(ifcopenshell.util.classification.get_references(element))
assert len(references) == 1
assert references[0].Identification == "X"
assert references[0].Name == "Foobar"
assert references[0].ReferencedSource == self.file.by_type("IfcClassification")[0]
element2 = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcWall")
ifcopenshell.api.run(
"classification.add_reference",
self.file,
product=element2,
identification="X",
name="Foobar",
classification=result,
)
assert list(ifcopenshell.util.classification.get_references(element2))[0].Identification == "X"
assert list(ifcopenshell.util.classification.get_references(element2))[0].Name == "Foobar"
assert list(ifcopenshell.util.classification.get_references(element2))[0] == references[0]
@deprecation_check
def test_removing_a_reference(self):
ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcProject")
element = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcWall")
result = ifcopenshell.api.run("classification.add_classification", self.file, classification="Name")
reference = ifcopenshell.api.run(
"classification.add_reference",
self.file,
products=[element],
identification="X",
name="Foobar",
classification=result,
)
ifcopenshell.api.run("classification.remove_reference", self.file, product=element, reference=reference)
assert len(ifcopenshell.util.classification.get_references(element)) == 0
assert len(self.file.by_type("IfcClassificationReference")) == 0
@deprecation_check
def test_assigning_a_reference(self):
reference = self.file.createIfcLibraryReference()
product = self.file.createIfcWall()
product2 = self.file.createIfcWall()
ifcopenshell.api.run("library.assign_reference", self.file, product=product, reference=reference)
assert reference.LibraryRefForObjects[0].RelatedObjects == (product,)
ifcopenshell.api.run("library.assign_reference", self.file, product=product2, reference=reference)
assert set(reference.LibraryRefForObjects[0].RelatedObjects) == set((product, product2))
@deprecation_check
def test_unassigning_a_reference(self):
reference = self.file.createIfcLibraryReference()
product = self.file.createIfcWall()
ifcopenshell.api.run("library.assign_reference", self.file, products=[product], reference=reference)
ifcopenshell.api.run("library.unassign_reference", self.file, product=product, reference=reference)
assert len(self.file.by_type("IfcRelAssociatesLibrary")) == 0
@deprecation_check
def test_assigning_a_document(self):
element = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcWall")
reference = ifcopenshell.api.run("document.add_reference", self.file, information=None)
ifcopenshell.api.run("document.assign_document", self.file, product=element, document=reference)
assert element.HasAssociations[0].RelatingDocument == reference
@deprecation_check
def test_unassigning_a_document(self):
element = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcWall")
reference = ifcopenshell.api.run("document.add_reference", self.file, information=None)
ifcopenshell.api.run("document.assign_document", self.file, products=[element], document=reference)
ifcopenshell.api.run("document.unassign_document", self.file, product=element, document=reference)
assert not element.HasAssociations
assert not len(self.file.by_type("IfcRelAssociatesDocument"))
@deprecation_check
def test_referencing_a_structure(self):
element = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcBuilding")
subelement = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcWall")
rel = ifcopenshell.api.run(
"spatial.reference_structure", self.file, product=subelement, relating_structure=element
)
assert ifcopenshell.util.element.get_referenced_structures(subelement) == [element]
assert rel.is_a("IfcRelReferencedInSpatialStructure")
@deprecation_check
def test_removing_a_container(self):
element = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcBuilding")
subelement = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcWall")
ifcopenshell.api.run(
"spatial.reference_structure", self.file, products=[subelement], relating_structure=element
)
ifcopenshell.api.run("spatial.dereference_structure", self.file, product=subelement, relating_structure=element)
assert ifcopenshell.util.element.get_referenced_structures(subelement) == []
@deprecation_check
def test_assign_a_constraint(self):
element = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcWall")
constraint = ifcopenshell.api.run("constraint.add_objective", self.file)
ifcopenshell.api.run("constraint.assign_constraint", self.file, product=element, constraint=constraint)
assert ifcopenshell.util.constraint.get_constrained_elements(constraint) == {element}
@deprecation_check
def test_unassigning_a_constraint(self):
constraint = ifcopenshell.api.run("constraint.add_objective", self.file)
element = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcWall")
ifcopenshell.api.run(
"constraint.assign_constraint", self.file, product=element, constraint=constraint
)
ifcopenshell.api.run(
"constraint.unassign_constraint", self.file, product=element, constraint=constraint
)
assert ifcopenshell.util.constraint.get_constrained_elements(element) == set()
assert len(self.file.by_type("IfcRelAssociatesConstraint")) == 0
@@ -34,14 +34,14 @@ class TestGetReferences(test.bootstrap.IFC4):
ifcopenshell.api.run(
"classification.add_reference",
self.file,
product=element,
products=[element],
reference=reference1,
classification=classification,
)
ifcopenshell.api.run(
"classification.add_reference",
self.file,
product=element,
products=[element],
reference=reference2,
classification=classification,
)
@@ -54,7 +54,7 @@ class TestGetReferences(test.bootstrap.IFC4):
ifcopenshell.api.run(
"classification.add_reference",
self.file,
product=element,
products=[element],
identification="X",
name="Foobar",
classification=result,
@@ -74,14 +74,14 @@ class TestGetReferences(test.bootstrap.IFC4):
ifcopenshell.api.run(
"classification.add_reference",
self.file,
product=element,
products=[element],
reference=reference1,
classification=classification,
)
ifcopenshell.api.run(
"classification.add_reference",
self.file,
product=element_type,
products=[element_type],
reference=reference2,
classification=classification,
)
@@ -103,14 +103,14 @@ class TestGetReferences(test.bootstrap.IFC4):
ifcopenshell.api.run(
"classification.add_reference",
self.file,
product=element,
products=[element],
reference=reference1,
classification=classification,
)
ifcopenshell.api.run(
"classification.add_reference",
self.file,
product=element_type,
products=[element_type],
reference=reference2,
classification=classification,
)
@@ -291,6 +291,16 @@ class TestGetPredefinedTypeIFC4(test.bootstrap.IFC4):
element_type.ProcessType = "FOOBAR"
assert subject.get_predefined_type(element) == "FOOBAR"
def test_getting_an_element_type_predefined_type(self):
element_type = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcWallType")
element_type.PredefinedType = "PARTITIONING"
assert subject.get_predefined_type(element_type) == "PARTITIONING"
def test_getting_an_element_type_null_predefined_type(self):
element_type = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcWallType")
element_type.PredefinedType = "NOTDEFINED"
assert subject.get_predefined_type(element_type) == "NOTDEFINED"
class TestGetTypeIFC4(test.bootstrap.IFC4):
def test_getting_the_type_of_a_product(self):
@@ -352,12 +362,16 @@ class TestGetMaterial(test.bootstrap.IFC4):
def test_getting_a_material_layer_set_of_a_product(self):
element = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcWall")
rel = ifcopenshell.api.run("material.assign_material", self.file, products=[element], type="IfcMaterialLayerSet")
rel = ifcopenshell.api.run(
"material.assign_material", self.file, products=[element], type="IfcMaterialLayerSet"
)
assert subject.get_material(element) == rel.RelatingMaterial
def test_getting_a_material_profile_set_of_a_product(self):
element = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcWall")
rel = ifcopenshell.api.run("material.assign_material", self.file, products=[element], type="IfcMaterialProfileSet")
rel = ifcopenshell.api.run(
"material.assign_material", self.file, products=[element], type="IfcMaterialProfileSet"
)
assert subject.get_material(element) == rel.RelatingMaterial
def test_getting_a_material_layer_set_usage_of_a_product(self):
@@ -512,7 +526,9 @@ class TestGetElementsByMaterial(test.bootstrap.IFC4):
material_set = ifcopenshell.api.run("material.add_material_set", self.file, set_type="IfcMaterialProfileSet")
ifcopenshell.api.run("material.add_profile", self.file, profile_set=material_set, material=material)
ifcopenshell.api.run("material.assign_material", self.file, products=[element_type], material=material_set)
ifcopenshell.api.run("material.assign_material", self.file, products=[element], type="IfcMaterialProfileSetUsage")
ifcopenshell.api.run(
"material.assign_material", self.file, products=[element], type="IfcMaterialProfileSetUsage"
)
usage = self.file.by_type("IfcMaterialProfileSetUsage")[0]
assert subject.get_elements_by_material(self.file, material) == {element, element_type}
assert subject.get_elements_by_material(self.file, material_set) == {element, element_type}
@@ -703,13 +719,33 @@ class TestGetReferencedStructures(test.bootstrap.IFC4):
element = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcWall")
assert subject.get_referenced_structures(element) == []
building = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcBuilding")
ifcopenshell.api.run("spatial.reference_structure", self.file, product=element, relating_structure=building)
ifcopenshell.api.run("spatial.reference_structure", self.file, products=[element], relating_structure=building)
assert subject.get_referenced_structures(element) == [building]
building2 = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcBuilding")
ifcopenshell.api.run("spatial.reference_structure", self.file, product=element, relating_structure=building2)
ifcopenshell.api.run("spatial.reference_structure", self.file, products=[element], relating_structure=building2)
assert subject.get_referenced_structures(element) == [building, building2]
class TestGetReferencedStructuresIFC2X3(test.bootstrap.IFC2X3, TestGetReferencedStructures):
pass
class TestGetStructureReferencedElements(test.bootstrap.IFC4):
def test_getting_references_of_an_element(self):
building = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcBuilding")
assert subject.get_structure_referenced_elements(building) == set()
element = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcWall")
ifcopenshell.api.run("spatial.reference_structure", self.file, products=[element], relating_structure=building)
assert subject.get_structure_referenced_elements(building) == {element}
element2 = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcWall")
ifcopenshell.api.run("spatial.reference_structure", self.file, products=[element2], relating_structure=building)
assert subject.get_structure_referenced_elements(building) == {element, element2}
class TestGetStructureReferencedElementsIFC2X3(test.bootstrap.IFC2X3, TestGetStructureReferencedElements):
pass
class TestGetDecompositionIFC4(test.bootstrap.IFC4):
def test_getting_decomposed_subelements_of_an_element(self):
element = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcElementAssembly")
@@ -769,6 +805,50 @@ class TestGetNestIFC2X3(test.bootstrap.IFC2X3, TestGetNestIFC4):
pass
class TestGetReferencedElements(test.bootstrap.IFC4):
# TODO: test other references:
# IfcExternallyDefinedHatchStyle
# IfcExternallyDefinedSurfaceStyle
# IfcExternallyDefinedTextFont
def test_get_elements_referenced_by_classification_reference(self):
ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcProject")
elements = [ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcWall")]
if self.file.schema != "IFC2X3":
elements.append(self.file.create_entity("IfcCostValue"))
result = ifcopenshell.api.run("classification.add_classification", self.file, classification="Name")
reference = ifcopenshell.api.run(
"classification.add_reference",
self.file,
products=elements,
identification="X",
name="Foobar",
classification=result,
)
assert subject.get_referenced_elements(reference) == set(elements)
def test_get_elements_referenced_by_library_reference(self):
reference = self.file.createIfcLibraryReference()
elements = [
ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcWall"),
ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcWall"),
]
ifcopenshell.api.run("library.assign_reference", self.file, reference=reference, products=elements)
assert subject.get_referenced_elements(reference) == set(elements)
def test_get_elements_referenced_by_document_reference(self):
reference = ifcopenshell.api.run("document.add_reference", self.file, information=None)
elements = [
ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcWall"),
ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcWall"),
]
ifcopenshell.api.run("document.assign_document", self.file, document=reference, products=elements)
assert subject.get_referenced_elements(reference) == set(elements)
class TestGetReferencedElementsIFC2X3(test.bootstrap.IFC2X3, TestGetReferencedElements):
pass
class TestReplaceAttributeIFC4(test.bootstrap.IFC4):
def test_replacing_an_elements_attribute(self):
element = self.file.createIfcWall("foo")
@@ -225,7 +225,7 @@ class TestFilterElements(test.bootstrap.IFC4):
ifcopenshell.api.run(
"classification.add_reference",
self.file,
product=element,
products=[element],
identification="X",
name="Foobar",
classification=result,