This commit is contained in:
Andrej730
2025-03-14 10:43:47 +05:00
parent 10ecac33b8
commit 0e8810c1f5
118 changed files with 869 additions and 1203 deletions
@@ -64,13 +64,10 @@ def assign_object(
:param products: The list of parts of the aggregate, typically of IfcElement or
IfcSpatialStructureElement subclass
:type product: list[ifcopenshell.entity_instance]
:param relating_object: The whole of the aggregate, typically an
IfcElement or IfcSpatialStructureElement subclass
:type relating_object: ifcopenshell.entity_instance
:return: The IfcRelAggregate relationship instance
or `None` if `products` was empty list.
:rtype: Union[ifcopenshell.entity_instance, None]
Example:
@@ -86,16 +83,10 @@ def assign_object(
# The site has a building
ifcopenshell.api.aggregate.assign_object(model, products=[subelement], relating_object=element)
"""
settings = {
"products": products,
"relating_object": relating_object,
}
if not settings["products"]:
if not products:
return
products = set(settings["products"])
relating_object = settings["relating_object"]
products_set = set(products)
is_decomposed_by = next((i for i in relating_object.IsDecomposedBy if i.is_a("IfcRelAggregates")), None)
previous_aggregates_rels: set[ifcopenshell.entity_instance] = set()
@@ -103,7 +94,7 @@ def assign_object(
products_with_aggregates: list[ifcopenshell.entity_instance] = []
# check if there is anything to change
for product in products:
for product in products_set:
product_rel = next(iter(product.Decomposes), None)
if product_rel is None:
@@ -129,7 +120,7 @@ def assign_object(
# unassign elements from previous aggregates
for decomposes in previous_aggregates_rels:
related_objects = set(decomposes.RelatedObjects) - products
related_objects = set(decomposes.RelatedObjects) - products_set
if related_objects:
decomposes.RelatedObjects = list(related_objects)
ifcopenshell.api.owner.update_owner_history(file, **{"element": decomposes})
@@ -141,7 +132,7 @@ def assign_object(
# assign elements to a new aggregate
if is_decomposed_by:
is_decomposed_by.RelatedObjects = list(set(is_decomposed_by.RelatedObjects) | products)
is_decomposed_by.RelatedObjects = list(set(is_decomposed_by.RelatedObjects) | products_set)
ifcopenshell.api.owner.update_owner_history(file, **{"element": is_decomposed_by})
else:
is_decomposed_by = file.create_entity(
@@ -149,7 +140,7 @@ def assign_object(
**{
"GlobalId": ifcopenshell.guid.new(),
"OwnerHistory": ifcopenshell.api.owner.create_owner_history(file),
"RelatedObjects": list(products),
"RelatedObjects": list(products_set),
"RelatingObject": relating_object,
}
)
@@ -23,9 +23,7 @@ def copy_boundary(file: ifcopenshell.file, boundary: ifcopenshell.entity_instanc
"""Copies a space boundary
:param boundary: The IfcRelSpaceBoundary you want to copy.
:type boundary: ifcopenshell.entity_instance
:return: Duplicate of the IfcRelSpaceBoundary
:rtype: ifcopenshell.entity_instance
Example:
@@ -36,9 +34,7 @@ def copy_boundary(file: ifcopenshell.file, boundary: ifcopenshell.entity_instanc
# And now we have two
boundary_copy = ifcopenshell.api.boundary.copy_boundary(model, boundary=boundary)
"""
settings = {"boundary": boundary}
result = ifcopenshell.util.element.copy(file, settings["boundary"])
result = ifcopenshell.util.element.copy(file, boundary)
if result.ConnectionGeometry:
result.ConnectionGeometry = ifcopenshell.util.element.copy_deep(file, result.ConnectionGeometry)
return result
@@ -27,9 +27,7 @@ def remove_boundary(file: ifcopenshell.file, boundary: ifcopenshell.entity_insta
boundary and its connection geometry is removed.
:param boundary: The IfcRelSpaceBoundary you want to remove.
:type boundary: ifcopenshell.entity_instance
:return: None
:rtype: None
Example:
@@ -40,13 +38,11 @@ def remove_boundary(file: ifcopenshell.file, boundary: ifcopenshell.entity_insta
# Let's remove it!
ifcopenshell.api.boundary.remove_boundary(model, boundary=boundary)
"""
settings = {"boundary": boundary}
geometry = settings["boundary"].ConnectionGeometry
geometry = boundary.ConnectionGeometry
if geometry:
settings["boundary"].ConnectionGeometry = None
boundary.ConnectionGeometry = None
ifcopenshell.util.element.remove_deep2(file, geometry)
history = settings["boundary"].OwnerHistory
file.remove(settings["boundary"])
history = boundary.OwnerHistory
file.remove(boundary)
if history:
ifcopenshell.util.element.remove_deep2(file, history)
@@ -28,9 +28,7 @@ def add_metric(file: ifcopenshell.file, objective: ifcopenshell.entity_instance)
to meet the objective of the constraint.
:param objective: The IfcObjective that this metric is a benchmark of.
:type objective: ifcopenshell.entity_instance
:return: The newly created IfcMetric entity
:rtype: ifcopenshell.entity_instance
Example:
@@ -40,10 +38,6 @@ def add_metric(file: ifcopenshell.file, objective: ifcopenshell.entity_instance)
metric = ifcopenshell.api.constraint.add_metric(model,
objective=objective)
"""
settings = {
"objective": objective,
}
metric = file.create_entity(
"IfcMetric",
**{
@@ -52,8 +46,9 @@ def add_metric(file: ifcopenshell.file, objective: ifcopenshell.entity_instance)
"Benchmark": "EQUALTO",
},
)
if settings["objective"]:
benchmark_values = list(settings["objective"].BenchmarkValues or [])
if objective:
benchmark_values: list[ifcopenshell.entity_instance]
benchmark_values = list(objective.BenchmarkValues or [])
benchmark_values.append(metric)
settings["objective"].BenchmarkValues = benchmark_values
objective.BenchmarkValues = benchmark_values
return metric
@@ -29,7 +29,6 @@ def add_objective(file: ifcopenshell.file) -> ifcopenshell.entity_instance:
quantities. See ifcopenshell.api.constraint.add_metric for more information.
:return: The newly created IfcObjective entity
:rtype: ifcopenshell.entity_instance
Example:
@@ -42,8 +41,6 @@ def add_objective(file: ifcopenshell.file) -> ifcopenshell.entity_instance:
# Note: the objective right now is purely qualitative and for
# information purposes. You may wish to add quantiative metrics.
"""
settings = {}
return file.create_entity(
"IfcObjective", **{"Name": "Unnamed", "ConstraintGrade": "NOTDEFINED", "ObjectiveQualifier": "NOTDEFINED"}
)
@@ -41,9 +41,7 @@ def remove_constraint(file: ifcopenshell.file, constraint: ifcopenshell.entity_i
ifcopenshell.api.constraint.remove_constraint(model,
constraint=objective)
"""
settings = {"constraint": constraint}
file.remove(settings["constraint"])
file.remove(constraint)
for rel in file.by_type("IfcRelAssociatesConstraint"):
if not rel.RelatingConstraint:
history = rel.OwnerHistory
@@ -29,9 +29,7 @@ def remove_context(file: ifcopenshell.file, context: ifcopenshell.entity_instanc
removed. If a context is removed, then any subcontexts are also removed.
:param context: The IfcGeometricRepresentationContext entity to remove
:type context: ifcopenshell.entity_instance
:return: None
:rtype: None
Example:
@@ -46,22 +44,20 @@ def remove_context(file: ifcopenshell.file, context: ifcopenshell.entity_instanc
# Let's just get rid of it completely
ifcopenshell.api.context.remove_context(model, context=body)
"""
settings = {"context": context}
for subcontext in settings["context"].HasSubContexts:
for subcontext in context.HasSubContexts:
ifcopenshell.api.context.remove_context(file, context=subcontext)
if getattr(settings["context"], "ParentContext", None):
new = settings["context"].ParentContext
for inverse in file.get_inverse(settings["context"]):
if getattr(context, "ParentContext", None):
new = context.ParentContext
for inverse in file.get_inverse(context):
if inverse.is_a("IfcCoordinateOperation"):
inverse.SourceCRS = inverse.TargetCRS
ifcopenshell.util.element.remove_deep(file, inverse)
else:
ifcopenshell.util.element.replace_attribute(inverse, settings["context"], new)
file.remove(settings["context"])
ifcopenshell.util.element.replace_attribute(inverse, context, new)
file.remove(context)
else:
representations_in_context = settings["context"].RepresentationsInContext
file.remove(settings["context"])
representations_in_context = context.RepresentationsInContext
file.remove(context)
for element in representations_in_context:
ifcopenshell.api.geometry.remove_representation(file, representation=element)
@@ -42,12 +42,9 @@ def assign_control(
:param relating_control: The IfcControl entity that is creating the
control or constraint
:type relating_control: ifcopenshell.entity_instance
:param related_object: The IfcObjectDefinition that is being controlled
:type related_object: ifcopenshell.entity_instance
:return: The newly created IfcRelAssignsToControl. If relationship already
existed before and wasn't changed then returns None.
:rtype: ifcopenshell.entity_instance, None
Example:
@@ -72,25 +69,20 @@ def assign_control(
ifcopenshell.api.control.assign_control(model,
relating_control=cost_item, related_object=wall)
"""
settings = {
"relating_control": relating_control,
"related_object": related_object,
}
if settings["related_object"].HasAssignments:
for assignment in settings["related_object"].HasAssignments:
if assignment.is_a("IfcRelAssignsToControl") and assignment.RelatingControl == settings["relating_control"]:
if related_object.HasAssignments:
for assignment in related_object.HasAssignments:
if assignment.is_a("IfcRelAssignsToControl") and assignment.RelatingControl == relating_control:
return
controls = None
if settings["relating_control"].Controls:
controls = settings["relating_control"].Controls[0]
if relating_control.Controls:
controls = relating_control.Controls[0]
if controls:
if settings["related_object"] in controls.RelatedObjects:
if related_object in controls.RelatedObjects:
return
related_objects = set(controls.RelatedObjects)
related_objects.add(settings["related_object"])
related_objects.add(related_object)
controls.RelatedObjects = list(related_objects)
ifcopenshell.api.owner.update_owner_history(file, **{"element": controls})
else:
@@ -99,8 +91,8 @@ def assign_control(
**{
"GlobalId": ifcopenshell.guid.new(),
"OwnerHistory": ifcopenshell.api.owner.create_owner_history(file),
"RelatedObjects": [settings["related_object"]],
"RelatingControl": settings["relating_control"],
"RelatedObjects": [related_object],
"RelatingControl": relating_control,
},
)
return controls
@@ -31,12 +31,9 @@ def unassign_control(
:param relating_control: The IfcControl entity that is creating the
control or constraint
:type relating_control: ifcopenshell.entity_instance
:param related_object: The IfcObjectDefinition that is being controlled
:type related_object: ifcopenshell.entity_instance
:return: If the control still is related to other objects, the
IfcRelAssignsToControl is returned, otherwise None.
:rtype: ifcopenshell.entity_instance, None
Example:
@@ -54,14 +51,8 @@ def unassign_control(
ifcopenshell.api.control.unassign_control(model,
relating_control=cost_item, related_object=wall)
"""
settings = {
"relating_control": relating_control,
"related_object": related_object,
}
for rel in settings["related_object"].HasAssignments or []:
if not rel.is_a("IfcRelAssignsToControl") or rel.RelatingControl != settings["relating_control"]:
for rel in related_object.HasAssignments or []:
if not rel.is_a("IfcRelAssignsToControl") or rel.RelatingControl != relating_control:
continue
if len(rel.RelatedObjects) == 1:
history = rel.OwnerHistory
@@ -70,7 +61,7 @@ def unassign_control(
ifcopenshell.util.element.remove_deep2(file, history)
return
related_objects = list(rel.RelatedObjects)
related_objects.remove(settings["related_object"])
related_objects.remove(related_object)
rel.RelatedObjects = related_objects
ifcopenshell.api.owner.update_owner_history(file, **{"element": rel})
return rel
@@ -54,12 +54,9 @@ def add_cost_item_quantity(
using another API call.
:param cost_item: The IfcCostItem to add the quantity to
:type cost_item: ifcopenshell.entity_instance
:param ifc_class: The type of quantity to add
:type ifc_class: str, optional
:return: The newly created quantity entity, chosen from the ifc_class
parameter
:rtype: ifcopenshell.entity_instance
Example:
@@ -76,20 +73,18 @@ def add_cost_item_quantity(
ifcopenshell.api.cost.add_cost_item_quantity(model,
cost_item=item, ifc_class="IfcQuantityCount")
"""
settings = {"cost_item": cost_item, "ifc_class": ifc_class}
quantity = file.create_entity(settings["ifc_class"], Name="Unnamed")
quantity = file.create_entity(ifc_class, Name="Unnamed")
# 3 IfcPhysicalSimpleQuantity Value
# This is a bold assumption
# https://forums.buildingsmart.org/t/how-does-a-cost-item-know-that-it-is-counting-a-controlled-product/3564
if settings["ifc_class"] == "IfcQuantityCount":
if ifc_class == "IfcQuantityCount":
count = 0
for rel in settings["cost_item"].Controls:
for rel in cost_item.Controls:
count += len(rel.RelatedObjects)
quantity[3] = count
else:
quantity[3] = 0.0
quantities = list(settings["cost_item"].CostQuantities or [])
quantities = list(cost_item.CostQuantities or [])
quantities.append(quantity)
settings["cost_item"].CostQuantities = quantities
cost_item.CostQuantities = quantities
return quantity
@@ -39,13 +39,10 @@ def add_cost_schedule(
managing any cost items.
:param name: The name of the cost schedule.
:type name: str, optional
:param predefined_type: The predefined type of the cost schedule, chosen
from a valid type in the IFC documentation for
IfcCostScheduleTypeEnum
:type predefined_type: str, optional
:return: The newly created IfcCostSchedule entity
:rtype: ifcopenshell.entity_instance
Example:
@@ -55,13 +52,11 @@ def add_cost_schedule(
# Now that we have a cost schedule, we may add cost items to it
item = ifcopenshell.api.cost.add_cost_item(model, cost_schedule=schedule)
"""
settings = {"name": name, "predefined_type": predefined_type}
cost_schedule = ifcopenshell.api.root.create_entity(
file,
ifc_class="IfcCostSchedule",
predefined_type=settings["predefined_type"],
name=settings["name"],
predefined_type=predefined_type,
name=name,
)
if file.schema == "IFC2X3":
cost_schedule.UpdateDate = createIfcDateAndTime(file, datetime.now())
@@ -46,9 +46,7 @@ def add_cost_value(file: ifcopenshell.file, parent: ifcopenshell.entity_instance
:param parent: A parent IfcCostItem, if specifying a price directly to a
cost item, or a top-level price component. Alternatively, this can
be set to a IfcCostValue, if specifying price subcomponents.
:type parent: ifcopenshell.entity_instance
:return: The newly created IfcCostValue
:rtype: ifcopenshell.entity_instance
Example:
@@ -91,19 +89,17 @@ def add_cost_value(file: ifcopenshell.file, parent: ifcopenshell.entity_instance
ifcopenshell.api.cost.edit_cost_value(model,
cost_value=subvalue2, attributes={"AppliedValue": 3.0})
"""
settings = {"parent": parent}
value = file.create_entity("IfcCostValue")
if settings["parent"].is_a("IfcCostItem"):
values = list(settings["parent"].CostValues or [])
if parent.is_a("IfcCostItem"):
values = list(parent.CostValues or [])
values.append(value)
settings["parent"].CostValues = values
elif settings["parent"].is_a("IfcConstructionResource"):
values = list(settings["parent"].BaseCosts or [])
parent.CostValues = values
elif parent.is_a("IfcConstructionResource"):
values = list(parent.BaseCosts or [])
values.append(value)
settings["parent"].BaseCosts = values
elif settings["parent"].is_a("IfcCostValue"):
values = list(settings["parent"].Components or [])
parent.BaseCosts = values
elif parent.is_a("IfcCostValue"):
values = list(parent.Components or [])
values.append(value)
settings["parent"].Components = values
parent.Components = values
return value
@@ -36,11 +36,8 @@ def assign_cost_value(
rates as a "template" to quickly populate your rates from.
:param cost_item: The IfcCostItem that you want to copy the values to
:type cost_item: ifcopenshell.entity_instance
:param cost_rate: The IfcCostItem that you want to copy the values from
:type cost_rate: ifcopenshell.entity_instance
:return: None
:rtype: None
Example:
@@ -61,16 +58,14 @@ def assign_cost_value(
# Now the cost item has the same rate as the one from the schedule of rate's item
ifcopenshell.api.cost.assign_cost_value(model, cost_item=item, cost_rate=rate)
"""
settings = {"cost_item": cost_item, "cost_rate": cost_rate}
if settings["cost_item"].CostValues:
if cost_item.CostValues:
[
ifcopenshell.api.cost.remove_cost_value(
file,
parent=settings["cost_item"],
parent=cost_item,
cost_value=cost_value,
)
for cost_value in settings["cost_item"].CostValues
for cost_value in cost_item.CostValues
]
# This is an assumption, and not part of the official IFC documentation
settings["cost_item"].CostValues = settings["cost_rate"].CostValues
cost_item.CostValues = cost_rate.CostValues
@@ -83,13 +83,11 @@ def calculate_cost_item_resource_value(file: ifcopenshell.file, cost_item: ifcop
# (42 * 200) + 50000 = 58400 is our calculated cost
ifcopenshell.api.cost.calculate_cost_item_resource_value(model, cost_item=item)
"""
settings = {"cost_item": cost_item}
for cost_value in settings["cost_item"].CostValues or []:
ifcopenshell.api.cost.remove_cost_value(file, parent=settings["cost_item"], cost_value=cost_value)
for cost_value in cost_item.CostValues or []:
ifcopenshell.api.cost.remove_cost_value(file, parent=cost_item, cost_value=cost_value)
resources = []
for rel in settings["cost_item"].Controls or []:
for rel in cost_item.Controls or []:
for related_object in rel.RelatedObjects:
if related_object.is_a("IfcConstructionResource"):
resources.append(related_object)
@@ -112,6 +110,6 @@ def calculate_cost_item_resource_value(file: ifcopenshell.file, cost_item: ifcop
if unit and "day" in unit:
quantity = quantity / 8 # Assume 8 hour working day - TODO implement resource calendar
formula = "{}*{}".format(cost, quantity)
cost_value = ifcopenshell.api.cost.add_cost_value(file, parent=settings["cost_item"])
cost_value = ifcopenshell.api.cost.add_cost_value(file, parent=cost_item)
cost_value.Name = resource.Name
ifcopenshell.api.cost.edit_cost_value_formula(file, cost_value=cost_value, formula=formula)
@@ -29,9 +29,7 @@ def remove_cost_item(file: ifcopenshell.file, cost_item: ifcopenshell.entity_ins
retained.
:param cost_item: The IfcCostItem entity you want to remove
:type cost_item: ifcopenshell.entity_instance
:return: None
:rtype: None
Example:
@@ -41,15 +39,13 @@ def remove_cost_item(file: ifcopenshell.file, cost_item: ifcopenshell.entity_ins
item = ifcopenshell.api.cost.add_cost_item(model, cost_schedule=schedule)
ifcopenshell.api.cost.remove_cost_item(model, cost_item=item)
"""
settings = {"cost_item": cost_item}
# TODO: do a deep purge
for inverse in file.get_inverse(settings["cost_item"]):
for inverse in file.get_inverse(cost_item):
if inverse.is_a("IfcRelNests"):
if inverse.RelatingObject == settings["cost_item"]:
if inverse.RelatingObject == cost_item:
for related_object in inverse.RelatedObjects:
ifcopenshell.api.cost.remove_cost_item(file, cost_item=related_object)
elif inverse.RelatedObjects == (settings["cost_item"],):
elif inverse.RelatedObjects == (cost_item,):
history = inverse.OwnerHistory
file.remove(inverse)
if history:
@@ -59,7 +55,7 @@ def remove_cost_item(file: ifcopenshell.file, cost_item: ifcopenshell.entity_ins
file.remove(inverse)
if history:
ifcopenshell.util.element.remove_deep2(file, history)
history = settings["cost_item"].OwnerHistory
file.remove(settings["cost_item"])
history = cost_item.OwnerHistory
file.remove(cost_item)
if history:
ifcopenshell.util.element.remove_deep2(file, history)
@@ -40,17 +40,15 @@ def remove_cost_schedule(file: ifcopenshell.file, cost_schedule: ifcopenshell.en
item = ifcopenshell.api.cost.add_cost_item(model, cost_schedule=schedule)
ifcopenshell.api.cost.remove_cost_schedule(model, cost_schedule=schedule)
"""
settings = {"cost_schedule": cost_schedule}
# TODO: do a deep purge
for inverse in file.get_inverse(settings["cost_schedule"]):
for inverse in file.get_inverse(cost_schedule):
if inverse.is_a("IfcRelAssignsToControl"):
[
ifcopenshell.api.cost.remove_cost_item(file, cost_item=related_object)
for related_object in inverse.RelatedObjects
if related_object.is_a("IfcCostItem")
]
history = settings["cost_schedule"].OwnerHistory
file.remove(settings["cost_schedule"])
history = cost_schedule.OwnerHistory
file.remove(cost_schedule)
if history:
ifcopenshell.util.element.remove_deep2(file, history)
@@ -38,9 +38,7 @@ def add_reference(file: ifcopenshell.file, information: ifcopenshell.entity_inst
:param information: The IfcDocumentInformation that the reference will
be created for
:type information: ifcopenshell.entity_instance
:return: The newly created IfcDocumentReference entity
:rtype: ifcopenshell.entity_instance
Example:
@@ -63,13 +61,11 @@ def add_reference(file: ifcopenshell.file, information: ifcopenshell.entity_inst
ifcopenshell.api.document.edit_reference(model,
reference=reference2, attributes={"Identification": "2.1.15"})
"""
settings = {"information": information}
if file.schema == "IFC2X3":
reference = file.create_entity("IfcDocumentReference", ItemReference="X")
if settings["information"]:
references = list(settings["information"].DocumentReferences or [])
if information:
references = list(information.DocumentReferences or [])
references.append(reference)
settings["information"].DocumentReferences = references
information.DocumentReferences = references
return reference
return file.create_entity("IfcDocumentReference", ReferencedDocument=settings["information"], Identification="X")
return file.create_entity("IfcDocumentReference", ReferencedDocument=information, Identification="X")
@@ -30,12 +30,9 @@ def unassign_document(
:param product: The list of objects that the document reference or information is
related to.
:type product: list[ifcopenshell.entity_instance]
:param document: The IfcDocumentReference (typically) or in rare cases
the IfcDocumentInformation that is associated with the product
:type document: ifcopenshell.entity_instance
:return: None
:rtype: None
Example:
@@ -54,27 +51,21 @@ def unassign_document(
# Now let's change our mind and remove the association
ifcopenshell.api.document.unassign_document(model, products=[storey], document=reference)
"""
settings = {
"products": products,
"document": document,
}
# 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(settings["products"])
for product in products:
products_set = set(products)
for product in products_set:
reference_rels.update(product.HasAssociations)
reference_rels = {
rel
for rel in reference_rels
if rel.is_a("IfcRelAssociatesDocument") and rel.RelatingDocument == settings["document"]
rel for rel in reference_rels if rel.is_a("IfcRelAssociatesDocument") and rel.RelatingDocument == document
}
for rel in reference_rels:
related_objects = set(rel.RelatedObjects) - products
related_objects = set(rel.RelatedObjects) - products_set
if related_objects:
rel.RelatedObjects = list(related_objects)
ifcopenshell.api.owner.update_owner_history(file, **{"element": rel})
@@ -46,12 +46,9 @@ def assign_product(
in 3D.
:param relating_product: The IfcProduct the object is related to
:type relating_product: ifcopenshell.entity_instance
:param related_object: The object (typically IfcAnnotation) that the
product is related to
:type related_object: ifcopenshell.entity_instance
:return: The created IfcRelAssignsToProduct relationship
:rtype: ifcopenshell.entity_instance
Example:
@@ -62,42 +59,36 @@ def assign_product(
ifcopenshell.api.drawing.assign_product(model,
relating_product=furniture, related_object=annotation)
"""
settings = {
"relating_product": relating_product,
"related_object": related_object,
}
is_grid_axis = settings["relating_product"].is_a("IfcGridAxis")
is_grid_axis = relating_product.is_a("IfcGridAxis")
if is_grid_axis:
if settings["related_object"].HasAssignments:
for rel in settings["related_object"].HasAssignments:
if rel.is_a("IfcRelAssignsToProduct") and rel.Name == settings["relating_product"].AxisTag:
if related_object.HasAssignments:
for rel in related_object.HasAssignments:
if rel.is_a("IfcRelAssignsToProduct") and rel.Name == relating_product.AxisTag:
return
elif settings["related_object"].HasAssignments:
for rel in settings["related_object"].HasAssignments:
if rel.is_a("IfcRelAssignsToProduct") and rel.RelatingProduct == settings["relating_product"]:
elif related_object.HasAssignments:
for rel in related_object.HasAssignments:
if rel.is_a("IfcRelAssignsToProduct") and rel.RelatingProduct == relating_product:
return
referenced_by = None
if is_grid_axis:
axis = settings["relating_product"]
axis = relating_product
grid = None
for attribute in ("PartOfW", "PartOfV", "PartOfU"):
if getattr(axis, attribute, None):
grid = getattr(axis, attribute)[0]
settings["relating_product"] = grid
for rel in grid.ReferencedBy:
if rel.Name == axis.AxisTag:
referenced_by = rel
break
elif settings["relating_product"].ReferencedBy:
referenced_by = settings["relating_product"].ReferencedBy[0]
elif relating_product.ReferencedBy:
referenced_by = relating_product.ReferencedBy[0]
if referenced_by:
related_objects = list(referenced_by.RelatedObjects)
related_objects.append(settings["related_object"])
related_objects.append(related_object)
referenced_by.RelatedObjects = related_objects
ifcopenshell.api.owner.update_owner_history(file, **{"element": referenced_by})
else:
@@ -106,8 +97,8 @@ def assign_product(
**{
"GlobalId": ifcopenshell.guid.new(),
"OwnerHistory": ifcopenshell.api.owner.create_owner_history(file),
"RelatedObjects": [settings["related_object"]],
"RelatingProduct": settings["relating_product"],
"RelatedObjects": [related_object],
"RelatingProduct": relating_product,
},
)
@@ -34,12 +34,9 @@ def unassign_product(
object later or leave the annotation as a "dumb" annotation.
:param relating_product: The IfcProduct the object is related to
:type relating_product: ifcopenshell.entity_instance
:param related_object: The object (typically IfcAnnotation) that the
product is related to
:type related_object: ifcopenshell.entity_instance
:return: None
:rtype: None
Example:
@@ -54,13 +51,8 @@ def unassign_product(
ifcopenshell.api.drawing.unassign_product(model,
relating_product=furniture, related_object=annotation)
"""
settings = {
"relating_product": relating_product,
"related_object": related_object,
}
for rel in settings["related_object"].HasAssignments or []:
if not rel.is_a("IfcRelAssignsToProduct") or rel.RelatingProduct != settings["relating_product"]:
for rel in related_object.HasAssignments or []:
if not rel.is_a("IfcRelAssignsToProduct") or rel.RelatingProduct != relating_product:
continue
if len(rel.RelatedObjects) == 1:
history = rel.OwnerHistory
@@ -69,6 +61,6 @@ def unassign_product(
ifcopenshell.util.element.remove_deep2(file, history)
return
related_objects = list(rel.RelatedObjects)
related_objects.remove(settings["related_object"])
related_objects.remove(related_object)
rel.RelatedObjects = related_objects
ifcopenshell.api.owner.update_owner_history(file, element=rel)
@@ -33,11 +33,8 @@ def add_filling(
filled.
:param opening: The IfcOpeningElement to fill with the element.
:type opening: ifcopenshell.entity_instance
:param element: The IfcElement to be inserted into the opening.
:type element: ifcopenshell.entity_instance
:return: The new IfcRelFillsElement relationship
:rtype: ifcopenshell.entity_instance
Example:
@@ -102,12 +99,10 @@ def add_filling(
# The door will now fill the opening.
ifcopenshell.api.feature.add_filling(model, opening=opening, element=door)
"""
settings = {"opening": opening, "element": element}
fills_voids = settings["element"].FillsVoids
fills_voids = element.FillsVoids
if fills_voids:
if fills_voids[0].RelatingOpeningElement == settings["opening"]:
if fills_voids[0].RelatingOpeningElement == opening:
return fills_voids[0]
history = fills_voids[0].OwnerHistory
file.remove(fills_voids[0])
@@ -117,6 +112,6 @@ def add_filling(
return file.create_entity(
"IfcRelFillsElement",
GlobalId=ifcopenshell.guid.new(),
RelatingOpeningElement=settings["opening"],
RelatedBuildingElement=settings["element"],
RelatingOpeningElement=opening,
RelatedBuildingElement=element,
)
@@ -28,9 +28,7 @@ def remove_filling(file: ifcopenshell.file, element: ifcopenshell.entity_instanc
fills the opening.
:param element: The element filling an opening.
:type element: ifcopenshell.entity_instance
:return: None
:rtype: None
Example:
@@ -52,10 +50,8 @@ def remove_filling(file: ifcopenshell.file, element: ifcopenshell.entity_instanc
# Not anymore!
ifcopenshell.api.feature.remove_filling(model, element=door)
"""
settings = {"element": element}
for rel in file.by_type("IfcRelFillsElement"):
if rel.RelatedBuildingElement == settings["element"]:
if rel.RelatedBuildingElement == element:
history = rel.OwnerHistory
file.remove(rel)
if history:
@@ -56,13 +56,10 @@ def add_axis_representation(
:param context: The IfcGeometricRepresentationContext that the
representation is part of. This must be either a
Model/Axis/GRAPH_VIEW (3D) or Plan/Axis/GRAPH_VIEW (2D).
:type context: ifcopenshell.entity_instance
:param axis: The axis, as a list of two coordinates, the coordinates
being either a list of 2 or 3 float coordinates depending on whether
the axis is 2D or 3D.
:type axis: list[list[float]]
:return: The newly created IfcShapeRepresentation entity
:rtype: ifcopenshell.entity_instance
Example:
@@ -29,20 +29,14 @@ def connect_element(
related_element: ifcopenshell.entity_instance,
description: Optional[str] = None,
) -> ifcopenshell.entity_instance:
settings = {
"relating_element": relating_element,
"related_element": related_element,
"description": description,
}
incompatible_connections = []
for rel in settings["relating_element"].ConnectedFrom:
if rel.is_a() == "IfcRelConnectsElements" and rel.RelatingElement == settings["related_element"]:
for rel in relating_element.ConnectedFrom:
if rel.is_a() == "IfcRelConnectsElements" and rel.RelatingElement == related_element:
incompatible_connections.append(rel)
for rel in settings["related_element"].ConnectedTo:
if rel.is_a() == "IfcRelConnectsElements" and rel.RelatedElement == settings["relating_element"]:
for rel in related_element.ConnectedTo:
if rel.is_a() == "IfcRelConnectsElements" and rel.RelatedElement == relating_element:
incompatible_connections.append(rel)
if incompatible_connections:
@@ -52,15 +46,15 @@ def connect_element(
if history:
ifcopenshell.util.element.remove_deep2(file, history)
for rel in settings["relating_element"].ConnectedTo:
if rel.is_a() == "IfcRelConnectsElements" and rel.RelatedElement == settings["related_element"]:
rel.Description = settings["description"]
for rel in relating_element.ConnectedTo:
if rel.is_a() == "IfcRelConnectsElements" and rel.RelatedElement == related_element:
rel.Description = description
return rel
return file.createIfcRelConnectsElements(
ifcopenshell.guid.new(),
OwnerHistory=ifcopenshell.api.owner.create_owner_history(file),
Description=settings["description"],
RelatingElement=settings["relating_element"],
RelatedElement=settings["related_element"],
Description=description,
RelatingElement=relating_element,
RelatedElement=related_element,
)
@@ -17,7 +17,6 @@
# along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
import ifcopenshell
from typing import Any
def map_representation(
@@ -25,15 +24,14 @@ def map_representation(
) -> ifcopenshell.entity_instance:
usecase = Usecase()
usecase.file = file
usecase.settings = {"representation": representation}
return usecase.execute()
return usecase.execute(representation)
class Usecase:
file: ifcopenshell.file
settings: dict[str, Any]
def execute(self) -> ifcopenshell.entity_instance:
def execute(self, representation: ifcopenshell.entity_instance) -> ifcopenshell.entity_instance:
self.representation = representation
mapping_source = self.get_mapping_source()
zero = self.file.createIfcCartesianPoint((0.0, 0.0, 0.0))
@@ -46,15 +44,15 @@ class Usecase:
return self.file.create_entity(
"IfcShapeRepresentation",
**{
"ContextOfItems": self.settings["representation"].ContextOfItems,
"RepresentationIdentifier": self.settings["representation"].RepresentationIdentifier,
"ContextOfItems": representation.ContextOfItems,
"RepresentationIdentifier": representation.RepresentationIdentifier,
"RepresentationType": "MappedRepresentation",
"Items": [mapped_item],
}
)
def get_mapping_source(self) -> ifcopenshell.entity_instance:
for inverse in self.file.get_inverse(self.settings["representation"]):
for inverse in self.file.get_inverse(self.representation):
if inverse.is_a("IfcRepresentationMap"):
return inverse
zero = self.file.createIfcCartesianPoint((0.0, 0.0, 0.0))
@@ -62,5 +60,5 @@ class Usecase:
z_axis = self.file.createIfcDirection((0.0, 0.0, 1.0))
mapping_origin = self.file.createIfcAxis2Placement3D(zero, z_axis, x_axis)
return self.file.createIfcRepresentationMap(
MappingOrigin=mapping_origin, MappedRepresentation=self.settings["representation"]
MappingOrigin=mapping_origin, MappedRepresentation=self.representation
)
@@ -35,12 +35,9 @@ def add_group(
or structural load groups, which group together loads for structural
analysis, or inventories, which are groups of assets.
:param Name: The name of the group. Defaults to "Unnamed"
:type Name: str, optional
:param name: The name of the group. Defaults to "Unnamed"
:param description: The description of the purpose of the group.
:type description: str, optional
:return: The newly created IfcGroup
:rtype: ifcopenshell.entity_instance
Example:
@@ -48,17 +45,11 @@ def add_group(
ifcopenshell.api.group.add_group(model, name="Unit 1A")
"""
settings = {
"name": name or "Unnamed",
"description": description,
}
return file.create_entity(
"IfcGroup",
**{
"GlobalId": ifcopenshell.guid.new(),
"OwnerHistory": ifcopenshell.api.owner.create_owner_history(file),
"Name": settings["name"],
"Description": settings["description"],
}
GlobalId=ifcopenshell.guid.new(),
OwnerHistory=ifcopenshell.api.owner.create_owner_history(file),
Name=name,
Description=description,
)
@@ -39,9 +39,7 @@ def remove_group(file: ifcopenshell.file, group: ifcopenshell.entity_instance) -
group = ifcopenshell.api.group.add_group(model, name="Unit 1A")
ifcopenshell.api.group.remove_group(model, group=group)
"""
settings = {"group": group}
for inverse_id in [i.id() for i in file.get_inverse(settings["group"])]:
for inverse_id in [i.id() for i in file.get_inverse(group)]:
try:
inverse = file.by_id(inverse_id)
except:
@@ -49,11 +47,11 @@ def remove_group(file: ifcopenshell.file, group: ifcopenshell.entity_instance) -
if inverse.is_a("IfcRelDefinesByProperties"):
ifcopenshell.api.pset.remove_pset(
file,
product=settings["group"],
product=group,
pset=inverse.RelatingPropertyDefinition,
)
elif inverse.is_a("IfcRelAssignsToGroup"):
if inverse.RelatingGroup == settings["group"]:
if inverse.RelatingGroup == group:
history = inverse.OwnerHistory
file.remove(inverse)
if history:
@@ -63,7 +61,7 @@ def remove_group(file: ifcopenshell.file, group: ifcopenshell.entity_instance) -
file.remove(inverse)
if history:
ifcopenshell.util.element.remove_deep2(file, history)
history = settings["group"].OwnerHistory
file.remove(settings["group"])
history = group.OwnerHistory
file.remove(group)
if history:
ifcopenshell.util.element.remove_deep2(file, history)
@@ -50,14 +50,10 @@ def add_reference(file: ifcopenshell.file, library: ifcopenshell.entity_instance
ifcopenshell.api.library.edit_reference(model,
reference=reference, attributes={"Identification": "http://example.org/digitaltwin#AHU01"})
"""
settings = {
"library": library,
}
if file.schema == "IFC2X3":
reference = file.createIfcLibraryReference()
references = list(settings["library"].LibraryReference or [])
references = list(library.LibraryReference or [])
references.append(reference)
settings["library"].LibraryReference = references
library.LibraryReference = references
return reference
return file.createIfcLibraryReference(ReferencedLibrary=settings["library"])
return file.createIfcLibraryReference(ReferencedLibrary=library)
@@ -33,14 +33,11 @@ def assign_reference(
detail about how references work.
:param products: The list of IfcProducts you want to associate with the reference
:type products: list[ifcopenshell.entity_instance]
:param reference: The IfcLibraryReference you want the product to be
associated with.
:type reference: ifcopenshell.entity_instance
:return: The IfcRelAssociatesLibrary relationship entity
or `None` if `products` was an empty list or all products were
already assigned to the `reference`.
:rtype: Union[ifcopenshell.entity_instance, None]
Example:
@@ -60,38 +57,33 @@ def assign_reference(
# And now assign the IFC model's AHU with its Brickschema counterpart
ifcopenshell.api.library.assign_reference(model, reference=reference, products=[ahu])
"""
settings = {
"products": products,
"reference": reference,
}
# TODO: do we need to support non-ifcroot elements like we do in classification.add_reference?
referenced_elements = ifcopenshell.util.element.get_referenced_elements(settings["reference"])
products: set[ifcopenshell.entity_instance] = set(settings["products"])
products = products - referenced_elements
referenced_elements = ifcopenshell.util.element.get_referenced_elements(reference)
products_set: set[ifcopenshell.entity_instance] = set(products)
products_set = products_set - referenced_elements
if not products:
if not products_set:
return
if file.schema == "IFC2X3":
rel = next(
(r for r in file.by_type("IfcRelAssociatesLibrary") if r.RelatingLibrary == settings["reference"]),
(r for r in file.by_type("IfcRelAssociatesLibrary") if r.RelatingLibrary == reference),
None,
)
else:
rel = next(iter(settings["reference"].LibraryRefForObjects), None)
rel = next(iter(reference.LibraryRefForObjects), None)
if not rel:
return file.create_entity(
"IfcRelAssociatesLibrary",
GlobalId=ifcopenshell.guid.new(),
OwnerHistory=ifcopenshell.api.owner.create_owner_history(file),
RelatedObjects=list(products),
RelatingLibrary=settings["reference"],
RelatedObjects=list(products_set),
RelatingLibrary=reference,
)
related_objects = set(rel.RelatedObjects) | products
related_objects = set(rel.RelatedObjects) | products_set
rel.RelatedObjects = list(related_objects)
ifcopenshell.api.owner.update_owner_history(file, element=rel)
return rel
@@ -31,11 +31,8 @@ def unassign_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
:param products: A list of IfcProduct elements to unassign from the reference
:type products: list[ifcopenshell.entity_instance]
:return: None
:rtype: None
Example:
@@ -58,24 +55,19 @@ def unassign_reference(
# Let's change our mind and unassign it.
ifcopenshell.api.library.unassign_reference(model, reference=reference, products=[ahu])
"""
settings = {"reference": reference, "products": products}
# 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(settings["products"])
for product in products:
products_set = set(products)
for product in products_set:
reference_rels.update(product.HasAssociations)
reference_rels = {
rel
for rel in reference_rels
if rel.is_a("IfcRelAssociatesLibrary") and rel.RelatingLibrary == settings["reference"]
rel for rel in reference_rels if rel.is_a("IfcRelAssociatesLibrary") and rel.RelatingLibrary == reference
}
for rel in reference_rels:
related_objects = set(rel.RelatedObjects) - products
related_objects = set(rel.RelatedObjects) - products_set
if related_objects:
rel.RelatedObjects = list(related_objects)
ifcopenshell.api.owner.update_owner_history(file, **{"element": rel})
@@ -40,11 +40,8 @@ def add_list_item(
:param material_list: The IfcMaterialList the material should be added
to.
:type material_list: ifcopenshell.entity_instance
:param material: The IfcMaterial to add to the list
:type material: ifcopenshell.entity_instance
:return: None
:rtype: None
Example:
@@ -80,8 +77,6 @@ def add_list_item(
# aluminium and glass.
ifcopenshell.api.material.assign_material(model, products=[window_type], material=material_set)
"""
settings = {"material_list": material_list, "material": material}
materials = list(settings["material_list"].Materials or [])
materials.append(settings["material"])
settings["material_list"].Materials = materials
materials = list(material_list.Materials or [])
materials.append(material)
material_list.Materials = materials
@@ -58,13 +58,9 @@ def add_material(
:param name: The name of the material, typically tagged in a finishes
drawing or schedule.
:type name: str, optional
:param category: The category of the material.
:type category: str, optional
:param description: A description of the material.
:type description: str, optional
:return: The newly created IfcMaterial
:rtype: ifcopenshell.entity_instance
Example:
@@ -81,11 +77,9 @@ def add_material(
# "Style" has been specified.
ifcopenshell.api.material.assign_material(model, products=[concrete_bench], material=concrete)
"""
settings = {"name": name or "Unnamed", "category": category, "description": description}
material = file.create_entity("IfcMaterial", **{"Name": settings["name"] or "Unnamed"})
if settings["category"]:
material.Category = settings["category"]
if settings["description"]:
material.Description = settings["description"]
material = file.create_entity("IfcMaterial", **{"Name": name or "Unnamed"})
if category:
material.Category = category
if description:
material.Description = description
return material
@@ -68,14 +68,11 @@ def add_material_set(
:param name: The name of the material set, which may be purely
descriptive or annotated in drawings. Defaults to "Unnamed".
:type name: str, optional
:param set_type: What type of set you want to create, chosen from
IfcMaterialLayerSet, IfcMaterialProfileSet,
IfcMaterialConstituentSet, or IfcMaterialList. Defaults to
IfcMaterialConstituentSet.
:type set_type: str, optional
:return: The newly created material set element
:rtype: ifcopenshell.entity_instance
Example:
@@ -113,10 +110,8 @@ def add_material_set(
# Great! Let's assign our material set to our wall type.
ifcopenshell.api.material.assign_material(model, products=[wall_type], material=material_set)
"""
settings = {"name": name or "Unnamed", "set_type": set_type}
if settings["set_type"] == "IfcMaterialLayerSet":
return file.create_entity("IfcMaterialLayerSet", LayerSetName=settings["name"] or "Unnamed")
elif settings["set_type"] == "IfcMaterialList":
if set_type == "IfcMaterialLayerSet":
return file.create_entity("IfcMaterialLayerSet", LayerSetName=name or "Unnamed")
elif set_type == "IfcMaterialList":
return file.create_entity("IfcMaterialList")
return file.create_entity(settings["set_type"], Name=settings["name"] or "Unnamed")
return file.create_entity(set_type, Name=name or "Unnamed")
@@ -29,9 +29,7 @@ def remove_material(file: ifcopenshell.file, material: ifcopenshell.entity_insta
take care of this situation themselves.
:param material: The IfcMaterial entity you want to remove
:type material: ifcopenshell.entity_instance
:return: None
:rtype: None
Example:
@@ -43,10 +41,8 @@ def remove_material(file: ifcopenshell.file, material: ifcopenshell.entity_insta
# ... and remove it
ifcopenshell.api.material.remove_material(model, material=aluminium)
"""
settings = {"material": material}
inverse_elements = file.get_inverse(settings["material"])
file.remove(settings["material"])
inverse_elements = file.get_inverse(material)
file.remove(material)
# TODO: Right now, we we choose only to delete set items (e.g. a layer) but not the material set
# This can lead to invalid material sets, but we assume the user will deal with it
for inverse in inverse_elements:
@@ -29,9 +29,7 @@ def remove_material_set(file: ifcopenshell.file, material: ifcopenshell.entity_i
:param material: The IfcMaterialLayerSet, IfcMaterialConstituentSet,
IfcMaterialProfileSet entity you want to remove.
:type material: ifcopenshell.entity_instance
:return: None
:rtype: None
Example:
@@ -55,20 +53,18 @@ def remove_material_set(file: ifcopenshell.file, material: ifcopenshell.entity_i
ifcopenshell.api.material.remove_material_set(model, material=material_set)
"""
settings = {"material": material}
inverse_elements = file.get_inverse(settings["material"])
if settings["material"].is_a("IfcMaterialLayerSet"):
set_items = settings["material"].MaterialLayers or []
elif settings["material"].is_a("IfcMaterialProfileSet"):
set_items = settings["material"].MaterialProfiles or []
elif settings["material"].is_a("IfcMaterialConstituentSet"):
set_items = settings["material"].MaterialConstituents or []
elif settings["material"].is_a("IfcMaterialList"):
inverse_elements = file.get_inverse(material)
if material.is_a("IfcMaterialLayerSet"):
set_items = material.MaterialLayers or []
elif material.is_a("IfcMaterialProfileSet"):
set_items = material.MaterialProfiles or []
elif material.is_a("IfcMaterialConstituentSet"):
set_items = material.MaterialConstituents or []
elif material.is_a("IfcMaterialList"):
set_items = []
for set_item in set_items:
file.remove(set_item)
file.remove(settings["material"])
file.remove(material)
for inverse in inverse_elements:
if inverse.is_a("IfcRelAssociatesMaterial"):
history = inverse.OwnerHistory
@@ -49,11 +49,8 @@ def add_actor(
IfcPerson if it is a sole individual, or an IfcPersonAndOrganization
if a specific person is liable within an organisation and must be
legally nominated.
:type actor: ifcopenshell.entity_instance
:param ifc_class: Either "IfcActor" or "IfcOccupant".
:type ifc_class: str, optional
:return: The newly created IfcActor or IfcOccupant
:rtype: ifcopenshell.entity_instance
Example:
@@ -67,8 +64,7 @@ def add_actor(
# Assign that organisation to a newly created actor
actor = ifcopenshell.api.owner.add_actor(model, actor=organisation)
"""
settings = {"actor": actor, "ifc_class": ifc_class or "IfcActor"}
actor = ifcopenshell.api.root.create_entity(file, ifc_class=settings["ifc_class"])
actor.TheActor = settings["actor"]
return actor
ifc_class = ifc_class or "IfcActor"
actor_ = ifcopenshell.api.root.create_entity(file, ifc_class=ifc_class)
actor_.TheActor = actor
return actor_
@@ -39,12 +39,9 @@ def add_address(
:param assigned_object: The IfcOrganization or IfcPerson the contact
address belongs to.
:type assigned_object: ifcopenshell.entity_instance
:param ifc_class: Either IfcPostalAddress or IfcTelecomAddress. Defaults
to IfcPostalAddress.
:type ifc_class: str, optional
:return: The new IfcPostalAddress or IfcTelecomAddress
:rtype: ifcopenshell.entity_instance
Example:
@@ -67,10 +64,8 @@ def add_address(
"ElectronicMailAddresses": ["bobthebuilder@example.com"],
"WWWHomePageURL": "https://thinkmoult.com"})
"""
settings = {"assigned_object": assigned_object, "ifc_class": ifc_class}
address = file.create_entity(settings["ifc_class"], "OFFICE")
addresses = list(settings["assigned_object"].Addresses) if settings["assigned_object"].Addresses else []
address = file.create_entity(ifc_class, "OFFICE")
addresses = list(assigned_object.Addresses) if assigned_object.Addresses else []
addresses.append(address)
settings["assigned_object"].Addresses = addresses
assigned_object.Addresses = addresses
return address
@@ -31,11 +31,8 @@ def add_organisation(
Sometimes used in drawing naming schemes. Otherise used as a
canonicalised way of computers to identify the organisation. Like
their stock name.
:type identification: str, optional
:param name: The legal name of the organisation
:type name: str, optional
:return: The newly created IfcOrganization
:rtype: ifcopenshell.entity_instance
Example:
@@ -44,11 +41,9 @@ def add_organisation(
organisation = ifcopenshell.api.owner.add_organisation(model,
identification="AWB", name="Architects Without Ballpens")
"""
settings = {"identification": identification, "name": name}
data = {"Name": settings["name"]}
data = {"Name": name}
if file.schema == "IFC2X3":
data["Id"] = settings["identification"]
data["Id"] = identification
else:
data["Identification"] = settings["identification"]
data["Identification"] = identification
return file.create_entity("IfcOrganization", **data)
@@ -31,13 +31,9 @@ def add_person(
:param identification: The computer readable unique identification of
the person. For example, their username in a CDE or alias.
:type identification: str, optional
:param family_name: The family name
:type family_name: str, optional
:param given_name: The given name
:type given_name: str, optional
:return: The newly created IfcPerson
:rtype: ifcopenshell.entity_instance
Example:
@@ -46,15 +42,9 @@ def add_person(
ifcopenshell.api.owner.add_person(model,
identification="bobthebuilder", family_name="Thebuilder", given_name="Bob")
"""
settings = {
"identification": identification,
"family_name": family_name,
"given_name": given_name,
}
data = {"FamilyName": settings["family_name"], "GivenName": settings["given_name"]}
data = {"FamilyName": family_name, "GivenName": given_name}
if file.schema == "IFC2X3":
data["Id"] = settings["identification"]
data["Id"] = identification
else:
data["Identification"] = settings["identification"]
data["Identification"] = identification
return file.create_entity("IfcPerson", **data)
@@ -30,11 +30,8 @@ def add_person_and_organisation(
:param person: The IfcPerson being the representative of the
organisation.
:type person: ifcopenshell.entity_instance
:param organisation: The IfcOrganization it
:type organisation: ifcopenshell.entity_instance
:return: The newly created IfcPersonAndOrganization
:rtype: ifcopenshell.entity_instance
Example:
@@ -48,6 +45,4 @@ def add_person_and_organisation(
ifcopenshell.api.owner.add_person_and_organisation(model,
person=person, organisation=organisation)
"""
settings = {"person": person, "organisation": organisation}
return file.createIfcPersonAndOrganization(settings["person"], settings["organisation"])
return file.create_entity("IfcPersonAndOrganization", person, organisation)
@@ -43,11 +43,8 @@ def assign_actor(
ifcopenshell.api.resource.assign_resource.
:param relating_actor: The IfcActor who is responsible for the object.
:type relating_actor: ifcopenshell.entity_instance
:param related_object: The object the actor is responsible for.
:type related_object: ifcopenshell.entity_instance
:return: The newly created IfcRelAssignsToActor relationship.
:rtype: ifcopenshell.entity_instance
Example:
@@ -74,24 +71,19 @@ def assign_actor(
ifcopenshell.api.owner.assign_actor(model,
relating_actor=manufacturer, related_object=pump_type)
"""
settings = {
"relating_actor": relating_actor,
"related_object": related_object,
}
if settings["related_object"].HasAssignments:
for rel in settings["related_object"].HasAssignments:
if rel.is_a("IfcRelAssignsToActor") and rel.RelatingActor == settings["relating_actor"]:
if related_object.HasAssignments:
for rel in related_object.HasAssignments:
if rel.is_a("IfcRelAssignsToActor") and rel.RelatingActor == relating_actor:
return rel
rel = None
if settings["relating_actor"].IsActingUpon:
rel = settings["relating_actor"].IsActingUpon[0]
if relating_actor.IsActingUpon:
rel = relating_actor.IsActingUpon[0]
if rel:
related_objects = list(rel.RelatedObjects)
related_objects.append(settings["related_object"])
related_objects.append(related_object)
rel.RelatedObjects = related_objects
ifcopenshell.api.owner.update_owner_history(file, **{"element": rel})
else:
@@ -100,8 +92,8 @@ def assign_actor(
**{
"GlobalId": ifcopenshell.guid.new(),
"OwnerHistory": ifcopenshell.api.owner.create_owner_history(file),
"RelatedObjects": [settings["related_object"]],
"RelatingActor": settings["relating_actor"],
"RelatedObjects": [related_object],
"RelatingActor": relating_actor,
}
)
return rel
@@ -24,9 +24,7 @@ def remove_actor(file: ifcopenshell.file, actor: ifcopenshell.entity_instance) -
"""Removes an actor
:param actor: The IfcActor to remove.
:type actor: ifcopenshell.entity_instance
:return: None
:rtype: None
Example:
@@ -44,9 +42,7 @@ def remove_actor(file: ifcopenshell.file, actor: ifcopenshell.entity_instance) -
# Actually we need ballpens on this project
ifcopenshell.api.owner.remove_actor(model, actor=actor)
"""
settings = {"actor": actor}
history = settings["actor"].OwnerHistory
file.remove(settings["actor"])
history = actor.OwnerHistory
file.remove(actor)
if history:
ifcopenshell.util.element.remove_deep2(file, history)
@@ -25,9 +25,7 @@ def remove_address(file: ifcopenshell.file, address: ifcopenshell.entity_instanc
relationship removed.
:param address: The IfcAddress to remove.
:type address: ifcopenshell.entity_instance
:return: None
:rtype: None
Example:
@@ -40,10 +38,8 @@ def remove_address(file: ifcopenshell.file, address: ifcopenshell.entity_instanc
# Change our mind and delete it
ifcopenshell.api.owner.remove_address(model, address=address)
"""
settings = {"address": address}
for inverse in file.get_inverse(settings["address"]):
for inverse in file.get_inverse(address):
if inverse.is_a() in ("IfcOrganization", "IfcPerson"):
if inverse.Addresses == (settings["address"],):
if inverse.Addresses == (address,):
inverse.Addresses = None
file.remove(settings["address"])
file.remove(address)
@@ -25,9 +25,7 @@ def remove_application(file: ifcopenshell.file, application: ifcopenshell.entity
Check whether or not the application is used anywhere prior to removal.
:param address: The IfcApplication to remove.
:type address: ifcopenshell.entity_instance
:return: None
:rtype: None
Example:
@@ -36,6 +34,4 @@ def remove_application(file: ifcopenshell.file, application: ifcopenshell.entity
application = ifcopenshell.api.owner.add_application(model)
ifcopenshell.api.owner.remove_address(model, application=application)
"""
settings = {"application": application}
file.remove(settings["application"])
file.remove(application)
@@ -28,9 +28,7 @@ def remove_person_and_organisation(
the "person and organisation" group.
:param person_and_organisation: The IfcPersonAndOrganization to remove.
:type person_and_organisation: ifcopenshell.entity_instance
:return: None
:rtype: None
Example:
@@ -46,17 +44,15 @@ def remove_person_and_organisation(
ifcopenshell.api.owner.remove_person_and_organisation(model, person_and_organisation=user)
"""
settings = {"person_and_organisation": person_and_organisation}
for inverse in file.get_inverse(settings["person_and_organisation"]):
for inverse in file.get_inverse(person_and_organisation):
if inverse.is_a("IfcDocumentInformation"):
if inverse.Editors == (settings["person_and_organisation"],):
if inverse.Editors == (person_and_organisation,):
inverse.Editors = None
elif inverse.is_a("IfcActor"):
ifcopenshell.api.root.remove_product(file, product=inverse)
elif inverse.is_a("IfcResourceLevelRelationship"):
if inverse.RelatedResourceObjects == (settings["person_and_organisation"],):
if inverse.RelatedResourceObjects == (person_and_organisation,):
file.remove(inverse)
elif inverse.is_a("IfcOwnerHistory"):
file.remove(inverse)
file.remove(settings["person_and_organisation"])
file.remove(person_and_organisation)
@@ -25,9 +25,7 @@ def remove_role(file: ifcopenshell.file, role: ifcopenshell.entity_instance) ->
leave some of them without roles.
:param role: The IfcActorRole to remove.
:type role: ifcopenshell.entity_instance
:return: None
:rtype: None
Example:
@@ -40,13 +38,11 @@ def remove_role(file: ifcopenshell.file, role: ifcopenshell.entity_instance) ->
# After running this, the organisation will have no role again
ifcopenshell.api.owner.remove_role(model, role=role)
"""
settings = {"role": role}
for inverse in file.get_inverse(settings["role"]):
for inverse in file.get_inverse(role):
if inverse.is_a() in ("IfcOrganization", "IfcPerson", "IfcPersonAndOrganization"):
if inverse.Roles == (settings["role"],):
if inverse.Roles == (role,):
inverse.Roles = None
elif inverse.is_a("IfcResourceLevelRelationship") and not inverse.is_a("IfcOrganizationRelationship"):
if inverse.RelatedResourceObjects == (settings["organisation"],):
if inverse.RelatedResourceObjects == (organisation,):
file.remove(inverse)
file.remove(settings["role"])
file.remove(role)
@@ -29,11 +29,8 @@ def unassign_actor(
This means that the actor is no longer responsible for the object.
:param relating_actor: The IfcActor who is responsible for the object.
:type relating_actor: ifcopenshell.entity_instance
:param related_object: The object the actor is responsible for.
:type related_object: ifcopenshell.entity_instance
:return: None
:rtype: None
Example:
@@ -55,13 +52,8 @@ def unassign_actor(
ifcopenshell.api.owner.unassign_actor(model,
relating_actor=manufacturer, related_object=pump_type)
"""
settings = {
"relating_actor": relating_actor,
"related_object": related_object,
}
for rel in settings["related_object"].HasAssignments or []:
if not rel.is_a("IfcRelAssignsToActor") or rel.RelatingActor != settings["relating_actor"]:
for rel in related_object.HasAssignments or []:
if not rel.is_a("IfcRelAssignsToActor") or rel.RelatingActor != relating_actor:
continue
if len(rel.RelatedObjects) == 1:
history = rel.OwnerHistory
@@ -70,6 +62,6 @@ def unassign_actor(
ifcopenshell.util.element.remove_deep2(file, history)
return
related_objects = list(rel.RelatedObjects)
related_objects.remove(settings["related_object"])
related_objects.remove(related_object)
rel.RelatedObjects = related_objects
ifcopenshell.api.owner.update_owner_history(file, **{"element": rel})
@@ -59,9 +59,6 @@ def update_owner_history(
# API calls either.
ifcopenshell.api.attribute.edit_attributes(model, product=space, attributes={"Name": "Lobby"})
"""
settings = {"element": element}
element = settings["element"]
if not element.is_a("IfcRoot"):
return
user = ifcopenshell.api.owner.settings.get_user(file)
@@ -30,9 +30,7 @@ def add_parameterized_profile(file: ifcopenshell.file, ifc_class: str) -> ifcope
:param ifc_class: The subclass of IfcParameterizedProfileDef that you'd
like to create.
:type ifc_class: str
:return: The newly created element depending on the specified ifc_class.
:rtype: ifcopenshell.entity_instance
Example:
@@ -42,6 +40,4 @@ def add_parameterized_profile(file: ifcopenshell.file, ifc_class: str) -> ifcope
ifc_class="IfcCircleProfileDef")
circle.Radius = 1.
"""
settings = {"ifc_class": ifc_class}
return file.create_entity(settings["ifc_class"])
return file.create_entity(ifc_class)
@@ -35,11 +35,10 @@ def remove_profile(file: ifcopenshell.file, profile: ifcopenshell.entity_instanc
circle = 1.
ifcopenshell.api.profile.remove_profile(model, profile=circle)
"""
settings = {"profile": profile}
is_ifc2x3 = file.schema == "IFC2X3"
subelements = set()
for attribute in settings["profile"]:
for attribute in profile:
if isinstance(attribute, ifcopenshell.entity_instance):
subelements.add(attribute)
@@ -56,6 +55,6 @@ def remove_profile(file: ifcopenshell.file, profile: ifcopenshell.entity_instanc
for pset in profile_psets:
ifcopenshell.api.pset.remove_pset(file, product=profile, pset=pset)
file.remove(settings["profile"])
file.remove(profile)
for subelement in subelements:
ifcopenshell.util.element.remove_deep2(file, subelement)
@@ -50,9 +50,7 @@ def create_file(version: ifcopenshell.util.schema.IFC_SCHEMA = "IFC4") -> ifcope
# ... and off we go!
"""
settings = {"version": version}
file = ifcopenshell.file(schema=settings["version"])
file = ifcopenshell.file(schema=version)
file.wrapped_data.header.file_name.name = "/dev/null" # Hehehe
file.wrapped_data.header.file_name.time_stamp = (
datetime.datetime.utcnow().replace(tzinfo=datetime.timezone.utc).astimezone().replace(microsecond=0).isoformat()
@@ -28,11 +28,8 @@ def remove_pset(
All properties that are part of this property set are also removed.
:param product: The IfcObject to remove the property set from.
:type product: ifcopenshell.entity_instance
:param pset: The IfcPropertySet or IfcElementQuantity to remove.
:type pset: ifcopenshell.entity_instance
:return: None
:rtype: None
Example:
@@ -45,27 +42,25 @@ def remove_pset(
# Remove it!
ifcopenshell.api.pset.remove_pset(model, product=wall_type, pset=pset)
"""
settings = {"product": product, "pset": pset}
to_purge = []
should_remove_pset = True
for inverse in file.get_inverse(settings["pset"]):
for inverse in file.get_inverse(pset):
if inverse.is_a("IfcRelDefinesByProperties"):
if not inverse.RelatedObjects or len(inverse.RelatedObjects) == 1:
to_purge.append(inverse)
else:
related_objects = list(inverse.RelatedObjects)
related_objects.remove(settings["product"])
related_objects.remove(product)
inverse.RelatedObjects = related_objects
should_remove_pset = False
if should_remove_pset:
properties = [] # Predefined psets have no properties
if settings["pset"].is_a("IfcPropertySet"):
properties = settings["pset"].HasProperties or []
elif settings["pset"].is_a("IfcQuantitySet"):
properties = settings["pset"].Quantities or []
elif settings["pset"].is_a() in ("IfcMaterialProperties", "IfcProfileProperties"):
properties = settings["pset"].Properties or []
if pset.is_a("IfcPropertySet"):
properties = pset.HasProperties or []
elif pset.is_a("IfcQuantitySet"):
properties = pset.Quantities or []
elif pset.is_a() in ("IfcMaterialProperties", "IfcProfileProperties"):
properties = pset.Properties or []
for prop in properties:
if file.get_total_inverses(prop) != 1:
continue
@@ -75,8 +70,8 @@ def remove_pset(
file.remove(enumeration)
file.remove(prop)
# IfcMaterialProperties and IfcProfileProperties don't have OwnerHistory
history = getattr(settings["pset"], "OwnerHistory", None)
file.remove(settings["pset"])
history = getattr(pset, "OwnerHistory", None)
file.remove(pset)
if history:
ifcopenshell.util.element.remove_deep2(file, history)
for element in to_purge:
@@ -71,19 +71,15 @@ def add_pset_template(
overridden by occurrences, and is applicable to everything.
:param name: The name of the property set
:type name: str,optional
:param template_type: Choose from one of PSET_TYPEDRIVENONLY,
PSET_TYPEDRIVENOVERRIDE, PSET_OCCURRENCEDRIVEN,
PSET_PERFORMANCEDRIVEN, QTO_TYPEDRIVENONLY, QTO_TYPEDRIVENOVERRIDE,
QTO_OCCURRENCEDRIVEN, NOTDEFINED
:type template_type: str,optional
:param applicable_entity: The entity that this template is allowed to be
applied to. For example, IfcWall means that the property set may be
assigned to walls only. IfcTypeObject, the default, means that the
property set may be assigned to any type.
:type applicable_entity: str,optional
:return: The newly created IfcPropertySetTemplate
:rtype: ifcopenshell.entity_instance
Example:
@@ -99,12 +95,10 @@ def add_pset_template(
name="HighVoltage", description="Whether there is a risk of high voltage.",
primary_measure_type="IfcBoolean")
"""
settings = {"name": name, "template_type": template_type, "applicable_entity": applicable_entity}
return file.create_entity(
"IfcPropertySetTemplate",
GlobalId=ifcopenshell.guid.new(),
Name=settings["name"],
TemplateType=settings["template_type"],
ApplicableEntity=settings["applicable_entity"],
Name=name,
TemplateType=template_type,
ApplicableEntity=applicable_entity,
)
@@ -27,9 +27,7 @@ def remove_prop_template(file: ifcopenshell.file, prop_template: ifcopenshell.en
templates.
:param prop_template: The IfcSimplePropertyTemplate to remove.
:type prop_template: ifcopenshell.entity_instance
:return: None
:rtype: None
Example:
@@ -44,13 +42,11 @@ def remove_prop_template(file: ifcopenshell.file, prop_template: ifcopenshell.en
# Let's remove the second one.
ifcopenshell.api.pset_template.remove_prop_template(model, prop_template=prop2)
"""
settings = {"prop_template": prop_template}
for inverse in file.get_inverse(settings["prop_template"]):
for inverse in file.get_inverse(prop_template):
if len(inverse.HasPropertyTemplates) == 1:
inverse.HasPropertyTemplates = []
else:
has_property_templates = list(inverse.HasPropertyTemplates)
has_property_templates.remove(settings["prop_template"])
has_property_templates.remove(prop_template)
inverse.HasPropertyTemplates = has_property_templates
ifcopenshell.util.element.remove_deep(file, settings["prop_template"])
ifcopenshell.util.element.remove_deep(file, prop_template)
@@ -26,9 +26,7 @@ def remove_pset_template(file: ifcopenshell.file, pset_template: ifcopenshell.en
along with it.
:param pset_template: The IfcPropertySetTemplate to remove.
:type pset_template: ifcopenshell.entity_instance
:return: None
:rtype: None
Example:
@@ -40,6 +38,4 @@ def remove_pset_template(file: ifcopenshell.file, pset_template: ifcopenshell.en
# Let's remove the template.
ifcopenshell.api.pset_template.remove_pset_template(model, pset_template=template)
"""
settings = {"pset_template": pset_template}
ifcopenshell.util.element.remove_deep(file, settings["pset_template"])
ifcopenshell.util.element.remove_deep(file, pset_template)
@@ -51,20 +51,15 @@ def add_resource(
:param parent_resource: If this is a child resource (typically to a crew
resource), then nominate the parent IfcConstructionResource here.
:type parent_resource: ifcopenshell.entity_instance, optional
:param ifc_class: The class of resource chosen from
IfcConstructionEquipmentResource, IfcConstructionMaterialResource,
IfcConstructionProductResource, IfcCrewResource, IfcLaborResource,
or IfcSubContractResource.
:type ifc_class: str,optional
:param name: The name of the resource
:type name: str,optional
:param predefined_type: Consult the IFC documentation for the valid
predefined types for each type of resource class.
:type predefined_type: str,optional
:return: The newly created resource depending on the nominated IFC
class.
:rtype: ifcopenshell.entity_instance
Example:
@@ -76,25 +71,17 @@ def add_resource(
# Add some labour to our crew.
ifcopenshell.api.resource.add_resource(model, parent_resource=crew, ifc_class="IfcLaborResource")
"""
settings = {
"parent_resource": parent_resource,
"ifc_class": ifc_class,
"name": name,
"predefined_type": predefined_type,
}
resource = ifcopenshell.api.root.create_entity(
file,
ifc_class=settings["ifc_class"],
predefined_type=settings["predefined_type"],
name=settings["name"] or "Unnamed",
ifc_class=ifc_class,
predefined_type=predefined_type,
name=name or "Unnamed",
)
# TODO: this is an ambiguity by buildingSMART: Can we nest an IfcCrewResource under an IfcCrewResource ?
# https://forums.buildingsmart.org/t/what-are-allowed-to-be-root-level-construction-resources/3550
if settings["parent_resource"]:
ifcopenshell.api.nest.assign_object(
file, related_objects=[resource], relating_object=settings["parent_resource"]
)
if parent_resource:
ifcopenshell.api.nest.assign_object(file, related_objects=[resource], relating_object=parent_resource)
elif file.schema != "IFC2X3":
context = file.by_type("IfcContext")[0]
ifcopenshell.api.project.assign_declaration(file, definitions=[resource], relating_context=context)
@@ -42,12 +42,9 @@ def assign_resource(
(e.g. if the resource is a labour resource).
:param relating_resource: The IfcResource to assign the object to.
:type relating_resource: ifcopenshell.entity_instance
:param related_object: The IfcProduct or IfcActor to assign to the
object.
:type related_object: ifcopenshell.entity_instance
:return: The newly created IfcRelAssignsToResource
:rtype: ifcopenshell.entity_instance
Example:
@@ -82,26 +79,18 @@ def assign_resource(
# This means that UCO is now our crane operator.
ifcopenshell.api.resource.assign_resource(model, relating_resource=crane, related_object=actor)
"""
settings = {
"relating_resource": relating_resource,
"related_object": related_object,
}
if settings["related_object"].HasAssignments:
for assignment in settings["related_object"].HasAssignments:
if (
assignment.is_a("IfclRelAssignsToResource")
and assignment.RelatingResource == settings["relating_resource"]
):
if related_object.HasAssignments:
for assignment in related_object.HasAssignments:
if assignment.is_a("IfclRelAssignsToResource") and assignment.RelatingResource == relating_resource:
return assignment
resource_of = None
if settings["relating_resource"].ResourceOf:
resource_of = settings["relating_resource"].ResourceOf[0]
if relating_resource.ResourceOf:
resource_of = relating_resource.ResourceOf[0]
if resource_of:
related_objects = list(resource_of.RelatedObjects)
related_objects.append(settings["related_object"])
related_objects.append(related_object)
resource_of.RelatedObjects = related_objects
ifcopenshell.api.owner.update_owner_history(file, **{"element": resource_of})
else:
@@ -110,8 +99,8 @@ def assign_resource(
**{
"GlobalId": ifcopenshell.guid.new(),
"OwnerHistory": ifcopenshell.api.owner.create_owner_history(file),
"RelatedObjects": [settings["related_object"]],
"RelatingResource": settings["relating_resource"],
"RelatedObjects": [related_object],
"RelatingResource": relating_resource,
}
)
return resource_of
@@ -25,15 +25,18 @@ import ifcopenshell.util.resource
def calculate_resource_usage(file: ifcopenshell.file, resource: ifcopenshell.entity_instance) -> None:
"""Calculates the number of resources required to perform scheduled work on a task."""
settings = {"resource": resource}
"""Calculates the number of resources required to perform scheduled work on a task.
if ifcopenshell.util.constraint.is_attribute_locked(settings["resource"], "Usage.ScheduleUsage"):
:param resource: The IfcConstructionResource to calculate the usage for.
:return: None
"""
if ifcopenshell.util.constraint.is_attribute_locked(resource, "Usage.ScheduleUsage"):
return
if not settings["resource"].Usage or not settings["resource"].Usage.ScheduleWork:
if not resource.Usage or not resource.Usage.ScheduleWork:
return
task = ifcopenshell.util.resource.get_task_assignments(settings["resource"])
task = ifcopenshell.util.resource.get_task_assignments(resource)
if not task or not task.TaskTime:
return
@@ -46,7 +49,7 @@ def calculate_resource_usage(file: ifcopenshell.file, resource: ifcopenshell.ent
seconds = task_duration.days * hours_per_day * 60 * 60
seconds += task_duration.seconds
person_hours = ifcopenshell.util.date.ifc2datetime(settings["resource"].Usage.ScheduleWork)
person_hours = ifcopenshell.util.date.ifc2datetime(resource.Usage.ScheduleWork)
required_resources = person_hours.total_seconds() / seconds
settings["resource"].Usage.ScheduleUsage = float(required_resources)
resource.Usage.ScheduleUsage = float(required_resources)
@@ -22,6 +22,9 @@ import ifcopenshell.util.element
def remove_resource_quantity(file: ifcopenshell.file, resource: ifcopenshell.entity_instance) -> None:
"""Removes the base quantity of a resource
:param resource: The IfcConstructionResource to remove the quantity from.
:return: None
Example:
.. code:: python
@@ -41,9 +44,7 @@ def remove_resource_quantity(file: ifcopenshell.file, resource: ifcopenshell.ent
# let's clean up our mess and remove the quantity.
ifcopenshell.api.resource.remove_resource_quantity(model, resource=labour)
"""
settings = {"resource": resource}
old_quantity = settings["resource"].BaseQuantity
settings["resource"].BaseQuantity = None
old_quantity = resource.BaseQuantity
resource.BaseQuantity = None
if old_quantity:
ifcopenshell.util.element.remove_deep(file, old_quantity)
@@ -29,12 +29,9 @@ def unassign_resource(
"""Removes the relationship between a resource and object
:param relating_resource: The IfcResource to assign the object to.
:type relating_resource: ifcopenshell.entity_instance
:param related_object: The IfcProduct or IfcActor to assign to the
object.
:type related_object: ifcopenshell.entity_instance
:return: None
:rtype: None
Example:
@@ -60,13 +57,8 @@ def unassign_resource(
ifcopenshell.api.resource.unassign_resource(model,
relating_resource=crane, related_object=product)
"""
settings = {
"relating_resource": relating_resource,
"related_object": related_object,
}
for rel in settings["related_object"].HasAssignments or []:
if not rel.is_a("IfcRelAssignsToResource") or rel.RelatingResource != settings["relating_resource"]:
for rel in related_object.HasAssignments or []:
if not rel.is_a("IfcRelAssignsToResource") or rel.RelatingResource != relating_resource:
continue
if len(rel.RelatedObjects) == 1:
history = rel.OwnerHistory
@@ -75,6 +67,6 @@ def unassign_resource(
ifcopenshell.util.element.remove_deep2(file, history)
return
related_objects = list(rel.RelatedObjects)
related_objects.remove(settings["related_object"])
related_objects.remove(related_object)
rel.RelatedObjects = related_objects
ifcopenshell.api.owner.update_owner_history(file, **{"element": rel})
@@ -71,48 +71,44 @@ def create_entity(
"""
usecase = Usecase()
usecase.file = file
usecase.settings = {
"ifc_class": ifc_class,
"predefined_type": predefined_type,
"name": name,
}
return usecase.execute()
return usecase.execute(ifc_class, predefined_type, name)
class Usecase:
file: ifcopenshell.file
settings: dict[str, Any]
def execute(self):
def execute(
self, ifc_class: str, predefined_type: Optional[str] = None, name: Optional[str] = None
) -> ifcopenshell.entity_instance:
element = self.file.create_entity(
self.settings["ifc_class"],
ifc_class,
**{
"GlobalId": ifcopenshell.guid.new(),
"OwnerHistory": ifcopenshell.api.owner.create_owner_history(self.file),
}
)
element.Name = self.settings["name"] or None
if self.settings["predefined_type"]:
element.Name = name or None
if predefined_type:
if hasattr(element, "PredefinedType"):
try:
element.PredefinedType = self.settings["predefined_type"]
element.PredefinedType = predefined_type
except:
element.PredefinedType = "USERDEFINED"
if hasattr(element, "ObjectType"):
element.ObjectType = self.settings["predefined_type"]
element.ObjectType = predefined_type
elif hasattr(element, "ElementType"):
element.ElementType = self.settings["predefined_type"]
element.ElementType = predefined_type
elif hasattr(element, "ProcessType"):
element.ProcessType = self.settings["predefined_type"]
element.ProcessType = predefined_type
elif hasattr(element, "ObjectType"):
element.ObjectType = self.settings["predefined_type"]
element.ObjectType = predefined_type
if self.file.schema == "IFC2X3":
self.handle_2x3_defaults(element)
else:
self.handle_4_defaults(element)
return element
def handle_2x3_defaults(self, element):
def handle_2x3_defaults(self, element: ifcopenshell.entity_instance) -> None:
if element.is_a("IfcElementType"):
if hasattr(element, "PredefinedType") and not element.PredefinedType:
element.PredefinedType = "NOTDEFINED"
@@ -129,7 +125,7 @@ class Usecase:
element.ParameterTakesPrecedence = False
element.Sizeable = False
def handle_4_defaults(self, element):
def handle_4_defaults(self, element: ifcopenshell.entity_instance) -> None:
if element.is_a("IfcElementType"):
if hasattr(element, "PredefinedType") and not element.PredefinedType:
element.PredefinedType = "NOTDEFINED"
@@ -69,23 +69,16 @@ def add_task(
:param work_schedule: The work schedule to group the task in, if the
task is to be a top-level or root task. This is mutually exclusive
with the parent_task parameter.
:type work_schedule: ifcopenshell.entity_instance, optional
:param parent_task: The parent task, if the task is to be a subtask or
child task. This is mutually exclusive with the work_schedule
parameter.
:type parent_task: ifcopenshell.entity_instance, optioanl
:param name: The name of the task.
:type name: str,optional
:param description: The description of the task.
:type description: str,optional
:param identification: The identification code of the task.
:type identification: str,optional
:param predefined_type: The predefined type of the task. Common ones
include CONSTRUCTION, DEMOLITION, or MAINTENANCE. Consultant the
IFC documentation for IfcTaskTypeEnum for more information.
:type predefined_type: str
:return: The newly created IfcTask
:rtype: ifcopenshell.entity_instance
Example:
@@ -139,39 +132,28 @@ def add_task(
ifcopenshell.api.sequence.add_task(model, parent_task=cleaning, identification="3",
description="Setup the water pressure by tapping to a water supply and connecting to a ...")
"""
settings = {
"work_schedule": work_schedule,
"parent_task": parent_task,
"name": name,
"description": description,
"identification": identification,
"predefined_type": predefined_type,
}
task = ifcopenshell.api.root.create_entity(
file, ifc_class="IfcTask", name=settings["name"], predefined_type=settings["predefined_type"]
)
if settings["description"]:
task.Description = settings["description"]
if settings["identification"]:
task.Identification = settings["identification"]
task = ifcopenshell.api.root.create_entity(file, ifc_class="IfcTask", name=name, predefined_type=predefined_type)
if description:
task.Description = description
if identification:
task.Identification = identification
task.IsMilestone = False
if settings["work_schedule"]:
if work_schedule:
file.create_entity(
"IfcRelAssignsToControl",
**{
"GlobalId": ifcopenshell.guid.new(),
"OwnerHistory": ifcopenshell.api.owner.create_owner_history(file),
"RelatedObjects": [task],
"RelatingControl": settings["work_schedule"],
"RelatingControl": work_schedule,
}
)
elif settings["parent_task"]:
elif parent_task:
rel = ifcopenshell.api.nest.assign_object(
file,
related_objects=[task],
relating_object=settings["parent_task"],
relating_object=parent_task,
)
if file.schema != "IFC2X3" and settings["parent_task"].Identification:
task.Identification = settings["parent_task"].Identification + "." + str(len(rel.RelatedObjects))
if file.schema != "IFC2X3" and parent_task.Identification:
task.Identification = parent_task.Identification + "." + str(len(rel.RelatedObjects))
return task
@@ -44,17 +44,13 @@ def add_time_period(
:param recurrence_pattern: The IfcRecurrencePattern to add the time
period to. See ifcopenshell.api.sequence.assign_recurrence_pattern.
:type recurrence_pattern: ifcopenshell.entity_instance
:param start_time: The start time of the time period, in a format
compatible with IfcTime, such as an ISO format time string or a
datetime.time object.
:type start_time: str,datetime.time
:param end_time: The end time of the time period, in a format
compatible with IfcTime, such as an ISO format time string or a
datetime.time object.
:type end_time: str,datetime.time
:return: The newly created IfcTimePeriod
:rtype: ifcopenshell.entity_instance
Example:
@@ -81,18 +77,12 @@ def add_time_period(
ifcopenshell.api.sequence.add_time_period(model,
recurrence_pattern=pattern, start_time="13:00", end_time="17:00")
"""
settings = {
"recurrence_pattern": recurrence_pattern,
"start_time": start_time,
"end_time": end_time,
}
time_period = file.create_entity("IfcTimePeriod")
time_period.StartTime = ifcopenshell.util.date.datetime2ifc(settings["start_time"], "IfcTime")
time_period.EndTime = ifcopenshell.util.date.datetime2ifc(settings["end_time"], "IfcTime")
time_periods = list(settings["recurrence_pattern"].TimePeriods or [])
time_period.StartTime = ifcopenshell.util.date.datetime2ifc(start_time, "IfcTime")
time_period.EndTime = ifcopenshell.util.date.datetime2ifc(end_time, "IfcTime")
time_periods = list(recurrence_pattern.TimePeriods or [])
time_periods.append(time_period)
settings["recurrence_pattern"].TimePeriods = time_periods
recurrence_pattern.TimePeriods = time_periods
ifcopenshell.util.sequence.is_working_day.cache_clear()
ifcopenshell.util.sequence.is_calendar_applicable.cache_clear()
@@ -38,12 +38,10 @@ def add_work_calendar(
:param name: The name of the calendar. Typically something like
"5 Day Working Week" or "24/7".
:type name: str, optional
:param predefined_type: The type of calendar, typically used to more
specifically define shifts, such as FIRSTSHIFT, SECONDSHIFT, or
THIRDSHIFT. Leave as NOTDEFINED for basic calendar usage.
:return: The newly created IfcWorkCalendar
:rtype: ifcopenshell.entity_instance
Example:
@@ -79,13 +77,11 @@ def add_work_calendar(
# this calendar by default (though you can override them).
ifcopenshell.api.control.assign_control(model, relating_control=calendar, related_object=task)
"""
settings = {"name": name, "predefined_type": predefined_type}
work_calendar = ifcopenshell.api.root.create_entity(
file,
ifc_class="IfcWorkCalendar",
predefined_type=settings["predefined_type"],
name=settings["name"],
predefined_type=predefined_type,
name=name,
)
context = file.by_type("IfcContext")[0]
ifcopenshell.api.project.assign_declaration(
@@ -41,15 +41,11 @@ def add_work_plan(
:param name: The name of the work plan. Recommended to be "Maintenance"
or "Construction" for the two main purposes.
:type name: str, optional
:param predefined_type: The type of work plan, used for baselining.
Leave as "NOTDEFINED" if unsure.
:type predefined_type: str
:param start_time: The earliest start time when the schedules grouped
within the work plan are relevant.
:type start_time: str,datetime.time
:return: The newly created IfcWorkPlan
:rtype: ifcopenshell.entity_instance
Example:
@@ -62,23 +58,18 @@ def add_work_plan(
schedule = ifcopenshell.api.sequence.add_work_schedule(model,
name="Construction Schedule A", work_plan=work_plan)
"""
settings = {
"name": name,
"predefined_type": predefined_type,
"start_time": start_time or datetime.now(),
}
start_time = start_time or datetime.now()
work_plan = ifcopenshell.api.root.create_entity(
file,
ifc_class="IfcWorkPlan",
predefined_type=settings["predefined_type"],
name=settings["name"],
predefined_type=predefined_type,
name=name,
)
work_plan.CreationDate = ifcopenshell.util.date.datetime2ifc(datetime.now(), "IfcDateTime")
user = ifcopenshell.api.owner.settings.get_user(file)
if user:
work_plan.Creators = [user.ThePerson]
work_plan.StartTime = ifcopenshell.util.date.datetime2ifc(settings["start_time"], "IfcDateTime")
work_plan.StartTime = ifcopenshell.util.date.datetime2ifc(start_time, "IfcDateTime")
context = file.by_type("IfcContext")[0]
ifcopenshell.api.project.assign_declaration(
@@ -77,19 +77,12 @@ def add_work_schedule(
construction = ifcopenshell.api.sequence.add_task(model,
work_schedule=schedule, name="Construction", identification="C")
"""
settings = {
"name": name,
"predefined_type": predefined_type,
"object_type": object_type,
"start_time": start_time or datetime.now(),
"work_plan": work_plan,
}
start_time = start_time or datetime.now()
work_schedule = ifcopenshell.api.root.create_entity(
file,
ifc_class="IfcWorkSchedule",
predefined_type=settings["predefined_type"],
name=settings["name"],
predefined_type=predefined_type,
name=name,
)
if file.schema == "IFC2X3":
work_schedule.CreationDate = createIfcDateAndTime(file, datetime.now())
@@ -99,17 +92,17 @@ def add_work_schedule(
if user:
work_schedule.Creators = [user.ThePerson]
if file.schema == "IFC2X3":
work_schedule.StartTime = createIfcDateAndTime(file, settings["start_time"])
work_schedule.StartTime = createIfcDateAndTime(file, start_time)
else:
work_schedule.StartTime = ifcopenshell.util.date.datetime2ifc(settings["start_time"], "IfcDateTime")
if settings["object_type"]:
work_schedule.ObjectType = settings["object_type"]
if settings["work_plan"]:
work_schedule.StartTime = ifcopenshell.util.date.datetime2ifc(start_time, "IfcDateTime")
if object_type:
work_schedule.ObjectType = object_type
if work_plan:
ifcopenshell.api.aggregate.assign_object(
file,
**{
"products": [work_schedule],
"relating_object": settings["work_plan"],
"relating_object": work_plan,
}
)
elif file.schema != "IFC2X3":
@@ -36,12 +36,9 @@ def add_work_time(
:param work_calendar: The IfcWorkCalendar to add the work or holiday
time definition to.
:type work_calendar: ifcopenshell.entity_instance
:param time_type: Either WorkingTimes or ExceptionTimes, depending on
what you want to define.
:type time_type: str
:return: The newly created IfcWorkTime
:rtype: ifcopenshell.entity_instance
Example:
@@ -74,15 +71,13 @@ def add_work_time(
ifcopenshell.api.sequence.edit_recurrence_pattern(model,
recurrence_pattern=pattern, attributes={"DayComponent": [1], "MonthComponent": [1]})
"""
settings = {"work_calendar": work_calendar, "time_type": time_type}
work_time = file.create_entity("IfcWorkTime")
if settings["time_type"] == "WorkingTimes":
working_times = list(settings["work_calendar"].WorkingTimes or [])
if time_type == "WorkingTimes":
working_times = list(work_calendar.WorkingTimes or [])
working_times.append(work_time)
settings["work_calendar"].WorkingTimes = working_times
elif settings["time_type"] == "ExceptionTimes":
exception_times = list(settings["work_calendar"].ExceptionTimes or [])
work_calendar.WorkingTimes = working_times
elif time_type == "ExceptionTimes":
exception_times = list(work_calendar.ExceptionTimes or [])
exception_times.append(work_time)
settings["work_calendar"].ExceptionTimes = exception_times
work_calendar.ExceptionTimes = exception_times
return work_time
@@ -65,12 +65,9 @@ def assign_process(
:param relating_process: The IfcProcess (typically IfcTask) that the
input, control, or resource is related to.
:type relating_process: ifcopenshell.entity_instance
:param related_object: The IfcProduct (for input), IfcCostItem (for
control) or IfcConstructionResource (for resource).
:type related_object: ifcopenshell.entity_instance
:return: The newly created IfcRelAssignsToProcess relationship
:rtype: ifcopenshell.entity_instance
Example:
@@ -91,23 +88,18 @@ def assign_process(
# Let's demolish that wall!
ifcopenshell.api.sequence.assign_process(model, relating_process=task, related_object=wall)
"""
settings = {
"relating_process": relating_process,
"related_object": related_object,
}
if settings["related_object"].HasAssignments:
for assignment in settings["related_object"].HasAssignments:
if assignment.is_a("IfcRelAssignsToProcess") and assignment.RelatingProcess == settings["relating_process"]:
if related_object.HasAssignments:
for assignment in related_object.HasAssignments:
if assignment.is_a("IfcRelAssignsToProcess") and assignment.RelatingProcess == relating_process:
return
operates_on = None
if settings["relating_process"].OperatesOn:
operates_on = settings["relating_process"].OperatesOn[0]
if relating_process.OperatesOn:
operates_on = relating_process.OperatesOn[0]
if operates_on:
related_objects = list(operates_on.RelatedObjects)
related_objects.append(settings["related_object"])
related_objects.append(related_object)
operates_on.RelatedObjects = related_objects
ifcopenshell.api.owner.update_owner_history(file, **{"element": operates_on})
else:
@@ -116,8 +108,8 @@ def assign_process(
**{
"GlobalId": ifcopenshell.guid.new(),
"OwnerHistory": ifcopenshell.api.owner.create_owner_history(file),
"RelatedObjects": [settings["related_object"]],
"RelatingProcess": settings["relating_process"],
"RelatedObjects": [related_object],
"RelatingProcess": relating_process,
}
)
return operates_on
@@ -41,12 +41,9 @@ def assign_product(
:param relating_product: The IfcProduct that was constructed as a result
of the task.
:type relating_product: ifcopenshell.entity_instance
:param related_object: The IfcProcess (typically IfcTask) of the
construction task.
:type related_object: ifcopenshell.entity_instance
:return: The newly created IfcRelAssignsToProduct relationship
:rtype: ifcopenshell.entity_instance
Example:
@@ -67,23 +64,18 @@ def assign_product(
# Let's construct that wall!
ifcopenshell.api.sequence.assign_product(model, relating_product=wall, related_object=task)
"""
settings = {
"relating_product": relating_product,
"related_object": related_object,
}
if settings["related_object"].HasAssignments:
for assignment in settings["related_object"].HasAssignments:
if assignment.is_a("IfcRelAssignsToProduct") and assignment.RelatingProduct == settings["relating_product"]:
if related_object.HasAssignments:
for assignment in related_object.HasAssignments:
if assignment.is_a("IfcRelAssignsToProduct") and assignment.RelatingProduct == relating_product:
return assignment
referenced_by = None
if settings["relating_product"].ReferencedBy:
referenced_by = settings["relating_product"].ReferencedBy[0]
if relating_product.ReferencedBy:
referenced_by = relating_product.ReferencedBy[0]
if referenced_by:
related_objects = list(referenced_by.RelatedObjects)
related_objects.append(settings["related_object"])
related_objects.append(related_object)
referenced_by.RelatedObjects = related_objects
ifcopenshell.api.owner.update_owner_history(file, **{"element": referenced_by})
else:
@@ -92,8 +84,8 @@ def assign_product(
**{
"GlobalId": ifcopenshell.guid.new(),
"OwnerHistory": ifcopenshell.api.owner.create_owner_history(file),
"RelatedObjects": [settings["related_object"]],
"RelatingProduct": settings["relating_product"],
"RelatedObjects": [related_object],
"RelatingProduct": relating_product,
}
)
return referenced_by
@@ -19,11 +19,12 @@
import ifcopenshell
import ifcopenshell.api.project
import ifcopenshell.api.aggregate
from typing import Union
def assign_work_plan(
file: ifcopenshell.file, work_schedule: ifcopenshell.entity_instance, work_plan: ifcopenshell.entity_instance
) -> ifcopenshell.entity_instance:
) -> Union[ifcopenshell.entity_instance, None]:
"""Assigns a work schedule to a work plan
Typically, work schedules would be assigned to a work plan at creation.
@@ -31,11 +32,8 @@ def assign_work_plan(
:param work_schedule: The IfcWorkSchedule that will be assigned to the
work plan.
:type work_schedule: ifcopenshell.entity_instance
:param work_plan: The IfcWorkPlan for the schedule to be assigned to.
:type work_plan: ifcopenshell.entity_instance
:return: The IfcRelAggregates relationship
:rtype: ifcopenshell.entity_instance
Example:
@@ -50,20 +48,16 @@ def assign_work_plan(
# ... you can assign the work plan afterwards.
ifcopenshell.api.sequence.assign_work_plan(work_schedule=schedule, work_plan=work_plan)
"""
settings = {"work_schedule": work_schedule, "work_plan": work_plan}
# TODO: this is an ambiguity by buildingSMART
# See https://forums.buildingsmart.org/t/is-the-ifcworkschedule-project-declaration-mutually-exclusive-to-aggregation-within-a-relating-ifcworkplan/3510
ifcopenshell.api.project.unassign_declaration(
file,
definitions=[settings["work_schedule"]],
definitions=[work_schedule],
relating_context=file.by_type("IfcContext")[0],
)
rel_aggregates = ifcopenshell.api.aggregate.assign_object(
file,
**{
"products": [settings["work_schedule"]],
"relating_object": settings["work_plan"],
}
products=[work_schedule],
relating_object=work_plan,
)
return rel_aggregates
@@ -21,6 +21,7 @@ import ifcopenshell.guid
import ifcopenshell.api.nest
import ifcopenshell.api.owner
import ifcopenshell.api.sequence
import ifcopenshell.util.date
import ifcopenshell.util.element
import ifcopenshell.util.sequence
from typing import Union, Any
@@ -155,7 +156,9 @@ class Usecase:
duration_type=inverse.TimeLag.DurationType,
)
def create_object_reference(self, relating_object, related_object):
def create_object_reference(
self, relating_object: ifcopenshell.entity_instance, related_object: ifcopenshell.entity_instance
) -> ifcopenshell.entity_instance:
referenced_by = None
if relating_object.Declares:
referenced_by = relating_object.Declares[0]
@@ -31,9 +31,7 @@ def remove_task(file: ifcopenshell.file, task: ifcopenshell.entity_instance) ->
sequences or controls are also removed.
:param task: The IfcTask to remove.
:type task: ifcopenshell.entity_instance
:return: None
:rtype: None
Example:
@@ -56,76 +54,74 @@ def remove_task(file: ifcopenshell.file, task: ifcopenshell.entity_instance) ->
# just fix it on site.
ifcopenshell.api.sequence.remove_task(model, task=design)
"""
settings = {"task": task}
# TODO: do a deep purge
ifcopenshell.api.project.unassign_declaration(
file,
definitions=[settings["task"]],
definitions=[task],
relating_context=file.by_type("IfcContext")[0],
)
if task_time := settings["task"].TaskTime:
if task_time := task.TaskTime:
if task_time.is_a("IfcTaskTimeRecurring"):
ifcopenshell.api.sequence.unassign_recurrence_pattern(file, task_time.Recurrence)
file.remove(task_time)
# Handle IfcRelNests.
if rels := settings["task"].IsNestedBy:
if rels := task.IsNestedBy:
subtasks = rels[0].RelatedObjects
# Use batching for optimization.
ifcopenshell.api.nest.unassign_object(file, subtasks)
for task_ in subtasks:
ifcopenshell.api.sequence.remove_task(file, task=task_)
if settings["task"].Nests:
ifcopenshell.api.nest.unassign_object(file, [settings["task"]])
if task.Nests:
ifcopenshell.api.nest.unassign_object(file, [task])
for inverse in file.get_inverse(settings["task"]):
for inverse in file.get_inverse(task):
if inverse.is_a("IfcRelSequence"):
history = inverse.OwnerHistory
file.remove(inverse)
if history:
ifcopenshell.util.element.remove_deep2(file, history)
elif inverse.is_a("IfcRelAssignsToControl"):
if inverse.RelatingControl == settings["task"] or len(inverse.RelatedObjects) == 1:
if inverse.RelatingControl == task or len(inverse.RelatedObjects) == 1:
history = inverse.OwnerHistory
file.remove(inverse)
if history:
ifcopenshell.util.element.remove_deep2(file, history)
else:
related_objects = list(inverse.RelatedObjects)
related_objects.remove(settings["task"])
related_objects.remove(task)
inverse.RelatedObjects = related_objects
elif inverse.is_a("IfcRelDefinesByProperties"):
ifcopenshell.api.pset.remove_pset(
file,
product=settings["task"],
product=task,
pset=inverse.RelatingPropertyDefinition,
)
elif inverse.is_a("IfcRelAssignsToProcess"):
if inverse.RelatingProcess == settings["task"] or len(inverse.RelatedObjects) == 1:
if inverse.RelatingProcess == task or len(inverse.RelatedObjects) == 1:
history = inverse.OwnerHistory
file.remove(inverse)
if history:
ifcopenshell.util.element.remove_deep2(file, history)
elif inverse.is_a("IfcRelAssignsToProduct"):
if inverse.RelatingProduct == settings["task"] or len(inverse.RelatedObjects) == 1:
if inverse.RelatingProduct == task or len(inverse.RelatedObjects) == 1:
history = inverse.OwnerHistory
file.remove(inverse)
if history:
ifcopenshell.util.element.remove_deep2(file, history)
else:
related_objects = list(inverse.RelatedObjects)
related_objects.remove(settings["task"])
related_objects.remove(task)
inverse.RelatedObjects = related_objects
elif inverse.is_a("IfcRelAssignsToObject"):
if inverse.RelatingObject == settings["task"] or len(inverse.RelatedObjects) == 1:
if inverse.RelatingObject == task or len(inverse.RelatedObjects) == 1:
history = inverse.OwnerHistory
file.remove(inverse)
if history:
ifcopenshell.util.element.remove_deep2(file, history)
else:
related_objects = list(inverse.RelatedObjects)
related_objects.remove(settings["task"])
related_objects.remove(task)
inverse.RelatedObjects = related_objects
elif inverse.is_a("IfcRelAssignsToProcess"):
history = inverse.OwnerHistory
@@ -133,7 +129,7 @@ def remove_task(file: ifcopenshell.file, task: ifcopenshell.entity_instance) ->
if history:
ifcopenshell.util.element.remove_deep2(file, history)
history = settings["task"].OwnerHistory
file.remove(settings["task"])
history = task.OwnerHistory
file.remove(task)
if history:
ifcopenshell.util.element.remove_deep2(file, history)
@@ -23,9 +23,7 @@ def remove_time_period(file: ifcopenshell.file, time_period: ifcopenshell.entity
"""Removes a time period
:param time_period: The IfcTimePeriod to remove.
:type time_period: ifcopenshell.entity_instance
:return: None
:rtype: None
Example:
@@ -55,6 +53,4 @@ def remove_time_period(file: ifcopenshell.file, time_period: ifcopenshell.entity
# Let's take the afternoon off!
ifcopenshell.api.sequence.remove_time_period(model, time_period=afternoon)
"""
settings = {"time_period": time_period}
file.remove(settings["time_period"])
file.remove(time_period)
@@ -30,9 +30,7 @@ def remove_work_calendar(file: ifcopenshell.file, work_calendar: ifcopenshell.en
calendar.
:param work_calendar: The IfcWorkCalendar to remove
:type work_calendar: ifcopenshell.entity_instance
:return: None
:rtype: None
Example:
@@ -44,32 +42,30 @@ def remove_work_calendar(file: ifcopenshell.file, work_calendar: ifcopenshell.en
# And remove it immediately
ifcopenshell.api.sequence.remove_work_calendar(model, work_calendar=calendar)
"""
settings = {"work_calendar": work_calendar}
# TODO: do a deep purge
ifcopenshell.api.project.unassign_declaration(
file,
definitions=[settings["work_calendar"]],
definitions=[work_calendar],
relating_context=file.by_type("IfcContext")[0],
)
if settings["work_calendar"].Controls:
for rel in settings["work_calendar"].Controls:
if work_calendar.Controls:
for rel in work_calendar.Controls:
for related_object in rel.RelatedObjects:
ifcopenshell.api.control.unassign_control(
file,
relating_control=settings["work_calendar"],
relating_control=work_calendar,
related_object=related_object,
)
# Currently in API work times are created already attached
# to the work calendar, so they are never reused.
for working_time in settings["work_calendar"].WorkingTimes or []:
for working_time in work_calendar.WorkingTimes or []:
ifcopenshell.api.sequence.remove_work_time(file, work_time=working_time)
for exception_time in settings["work_calendar"].ExceptionTimes or []:
for exception_time in work_calendar.ExceptionTimes or []:
ifcopenshell.api.sequence.remove_work_time(file, work_time=exception_time)
history = settings["work_calendar"].OwnerHistory
file.remove(settings["work_calendar"])
history = work_calendar.OwnerHistory
file.remove(work_calendar)
if history:
ifcopenshell.util.element.remove_deep2(file, history)
@@ -29,9 +29,7 @@ def remove_work_plan(file: ifcopenshell.file, work_plan: ifcopenshell.entity_ins
removed.
:param work_plan: The IfcWorkPlan to remove.
:type work_plan: ifcopenshell.entity_instance
:return: None
:rtype: None
Example:
@@ -43,11 +41,9 @@ def remove_work_plan(file: ifcopenshell.file, work_plan: ifcopenshell.entity_ins
# And remove it immediately
ifcopenshell.api.sequence.remove_work_plan(model, work_plan=work_plan)
"""
settings = {"work_plan": work_plan}
ifcopenshell.api.project.unassign_declaration(
file,
definitions=[settings["work_plan"]],
definitions=[work_plan],
relating_context=file.by_type("IfcContext")[0],
)
@@ -55,7 +51,7 @@ def remove_work_plan(file: ifcopenshell.file, work_plan: ifcopenshell.entity_ins
if related_objects:
ifcopenshell.api.aggregate.unassign_object(file, related_objects)
history = settings["work_plan"].OwnerHistory
file.remove(settings["work_plan"])
history = work_plan.OwnerHistory
file.remove(work_plan)
if history:
ifcopenshell.util.element.remove_deep2(file, history)
@@ -47,32 +47,30 @@ def remove_work_schedule(file: ifcopenshell.file, work_schedule: ifcopenshell.en
# And remove it immediately
ifcopenshell.api.sequence.remove_work_schedule(model, work_schedule=schedule)
"""
settings = {"work_schedule": work_schedule}
# TODO: do a deep purge
ifcopenshell.api.project.unassign_declaration(
file, definitions=[settings["work_schedule"]], relating_context=file.by_type("IfcContext")[0]
file, definitions=[work_schedule], relating_context=file.by_type("IfcContext")[0]
)
if settings["work_schedule"].Declares:
for rel in settings["work_schedule"].Declares:
for work_schedule in rel.RelatedObjects:
ifcopenshell.api.sequence.remove_work_schedule(file, work_schedule=work_schedule)
if work_schedule.Declares:
for rel in work_schedule.Declares:
for work_schedule_ in rel.RelatedObjects:
ifcopenshell.api.sequence.remove_work_schedule(file, work_schedule=work_schedule_)
# Unassign from work plans.
if settings["work_schedule"].Decomposes:
ifcopenshell.api.aggregate.unassign_object(file, [settings["work_schedule"]])
if work_schedule.Decomposes:
ifcopenshell.api.aggregate.unassign_object(file, [work_schedule])
for inverse in file.get_inverse(settings["work_schedule"]):
for inverse in file.get_inverse(work_schedule):
if inverse.is_a("IfcRelDefinesByObject"):
if inverse.RelatingObject == settings["work_schedule"] or len(inverse.RelatedObjects) == 1:
if inverse.RelatingObject == work_schedule or len(inverse.RelatedObjects) == 1:
history = inverse.OwnerHistory
file.remove(inverse)
if history:
ifcopenshell.util.element.remove_deep2(file, history)
else:
related_objects = list(inverse.RelatedObjects)
related_objects.remove(settings["work_schedule"])
related_objects.remove(work_schedule)
inverse.RelatedObjects = related_objects
elif inverse.is_a("IfcRelAssignsToControl"):
[
@@ -81,7 +79,7 @@ def remove_work_schedule(file: ifcopenshell.file, work_schedule: ifcopenshell.en
if related_object.is_a("IfcTask")
]
history = settings["work_schedule"].OwnerHistory
file.remove(settings["work_schedule"])
history = work_schedule.OwnerHistory
file.remove(work_schedule)
if history:
ifcopenshell.util.element.remove_deep2(file, history)
@@ -31,11 +31,8 @@ def unassign_process(
See ifcopenshell.api.sequence.assign_process for details.
:param relating_process: The IfcTask in the relationship.
:type relating_process: ifcopenshell.entity_instance
:param related_object: The related object.
:type related_object: ifcopenshell.entity_instance
:return: None
:rtype: None
Example:
@@ -59,13 +56,8 @@ def unassign_process(
# Change our mind.
ifcopenshell.api.sequence.unassign_process(model, relating_process=task, related_object=wall)
"""
settings = {
"relating_process": relating_process,
"related_object": related_object,
}
for rel in settings["related_object"].HasAssignments or []:
if not rel.is_a("IfcRelAssignsToProcess") or rel.RelatingProcess != settings["relating_process"]:
for rel in related_object.HasAssignments or []:
if not rel.is_a("IfcRelAssignsToProcess") or rel.RelatingProcess != relating_process:
continue
if len(rel.RelatedObjects) == 1:
history = rel.OwnerHistory
@@ -74,7 +66,7 @@ def unassign_process(
ifcopenshell.util.element.remove_deep2(file, history)
return
related_objects = list(rel.RelatedObjects)
related_objects.remove(settings["related_object"])
related_objects.remove(related_object)
rel.RelatedObjects = related_objects
ifcopenshell.api.owner.update_owner_history(file, element=rel)
return rel
@@ -31,11 +31,8 @@ def unassign_product(
See ifcopenshell.api.sequence.assign_product for details.
:param relating_product: The IfcProduct in the relationship.
:type relating_product: ifcopenshell.entity_instance
:param related_object: The IfcTask in the relationship.
:type related_object: ifcopenshell.entity_instance
:return: None
:rtype: None
Example:
@@ -59,13 +56,8 @@ def unassign_product(
# Change our mind.
ifcopenshell.api.sequence.unassign_product(relating_product=wall, related_object=task)
"""
settings = {
"relating_product": relating_product,
"related_object": related_object,
}
for rel in settings["related_object"].HasAssignments or []:
if not rel.is_a("IfcRelAssignsToProduct") or rel.RelatingProduct != settings["relating_product"]:
for rel in related_object.HasAssignments or []:
if not rel.is_a("IfcRelAssignsToProduct") or rel.RelatingProduct != relating_product:
continue
if len(rel.RelatedObjects) == 1:
history = rel.OwnerHistory
@@ -74,7 +66,7 @@ def unassign_product(
ifcopenshell.util.element.remove_deep2(file, history)
return
related_objects = list(rel.RelatedObjects)
related_objects.remove(settings["related_object"])
related_objects.remove(related_object)
rel.RelatedObjects = related_objects
ifcopenshell.api.owner.update_owner_history(file, element=rel)
return rel
@@ -28,9 +28,7 @@ def unassign_recurrence_pattern(file: ifcopenshell.file, recurrence_pattern: ifc
or replace IfcTaskTimeRecurring with IfcTaskTime).
:param recurrence_pattern: The IfcRecurrencePattern to remove.
:type recurrence_pattern: ifcopenshell.entity_instance
:return: None
:rtype: None
Example:
@@ -50,8 +48,6 @@ def unassign_recurrence_pattern(file: ifcopenshell.file, recurrence_pattern: ifc
# Change our mind, let's just maintain it whenever we feel like it.
ifcopenshell.api.sequence.unassign_recurrence_pattern(recurrence_pattern=pattern)
"""
settings = {"recurrence_pattern": recurrence_pattern}
for time_period in settings["recurrence_pattern"].TimePeriods or []:
for time_period in recurrence_pattern.TimePeriods or []:
file.remove(time_period)
file.remove(settings["recurrence_pattern"])
file.remove(recurrence_pattern)
@@ -29,11 +29,8 @@ def unassign_sequence(
"""Removes a sequence relationship between tasks
:param relating_process: The previous / predecessor task.
:type relating_process: ifcopenshell.entity_instance
:param related_process: The next / successor task.
:type related_process: ifcopenshell.entity_instance
:return: None
:rtype: None
Example:
@@ -60,15 +57,10 @@ def unassign_sequence(
ifcopenshell.api.sequence.unassign_sequence(model,
relating_process=zone1, related_process=zone2)
"""
settings = {
"relating_process": relating_process,
"related_process": related_process,
}
for rel in settings["related_process"].IsSuccessorFrom or []:
if rel.RelatingProcess == settings["relating_process"]:
for rel in related_process.IsSuccessorFrom or []:
if rel.RelatingProcess == relating_process:
history = rel.OwnerHistory
file.remove(rel)
if history:
ifcopenshell.util.element.remove_deep2(file, history)
ifcopenshell.api.sequence.cascade_schedule(file, task=settings["related_process"])
ifcopenshell.api.sequence.cascade_schedule(file, task=related_process)
@@ -69,13 +69,11 @@ def assign_container(
previous aggregation, containment, or nesting relationships it may have.
:param products: A list of physical IfcElements existing in the space.
:type products: list[ifcopenshell.entity_instance]
:param relating_structure: The IfcSpatialStructureElement element, such
as IfcBuilding, IfcBuildingStorey, or IfcSpace that the element
exists in.
:return: The IfcRelContainedInSpatialStructure relationship instance
or `None` if `products` was empty list.
:rtype: Union[ifcopenshell.entity_instance, None]
Example:
@@ -103,16 +101,10 @@ def assign_container(
ifcopenshell.api.spatial.assign_container(model, products=[wall], relating_structure=storey)
ifcopenshell.api.spatial.assign_container(model, products=[furniture], relating_structure=space)
"""
settings = {
"products": products,
"relating_structure": relating_structure,
}
if not settings["products"]:
if not products:
return
products = set(settings["products"])
relating_structure = settings["relating_structure"]
products_set = set(products)
structure_rel = next(iter(relating_structure.ContainsElements), None)
previous_containers_rels: set[ifcopenshell.entity_instance] = set()
@@ -120,7 +112,7 @@ def assign_container(
products_with_containers: list[ifcopenshell.entity_instance] = []
# check if there is anything to change
for product in products:
for product in products_set:
product_rel = next(iter(product.ContainedInStructure), None)
if product_rel is None:
@@ -144,7 +136,7 @@ def assign_container(
# unassign elements from previous containers
for rel in previous_containers_rels:
related_elements = set(rel.RelatedElements) - products
related_elements = set(rel.RelatedElements) - products_set
if related_elements:
rel.RelatedElements = list(related_elements)
ifcopenshell.api.owner.update_owner_history(file, **{"element": rel})
@@ -156,7 +148,7 @@ def assign_container(
# assign elements to a new container
if structure_rel:
structure_rel.RelatedElements = list(set(structure_rel.RelatedElements) | products)
structure_rel.RelatedElements = list(set(structure_rel.RelatedElements) | products_set)
ifcopenshell.api.owner.update_owner_history(file, **{"element": structure_rel})
else:
structure_rel = file.create_entity(
@@ -164,8 +156,8 @@ def assign_container(
**{
"GlobalId": ifcopenshell.guid.new(),
"OwnerHistory": ifcopenshell.api.owner.create_owner_history(file),
"RelatedElements": list(products),
"RelatingStructure": settings["relating_structure"],
"RelatedElements": list(products_set),
"RelatingStructure": relating_structure,
}
)
@@ -25,9 +25,7 @@ def unassign_container(file: ifcopenshell.file, products: list[ifcopenshell.enti
"""Unassigns a container from products.
:param product: A list of IfcProducts to remove the containment from.
:type product: list[ifcopenshell.entity_instance]
:return: None
:rtype: None
Example:
@@ -54,15 +52,11 @@ def unassign_container(file: ifcopenshell.file, products: list[ifcopenshell.enti
# Not anymore!
ifcopenshell.api.spatial.unassign_container(model, products=[wall])
"""
settings = {
"products": products,
}
products = set(settings["products"])
rels = set(rel for product in products if (rel := next(iter(product.ContainedInStructure), None)))
products_set = set(products)
rels = set(rel for product in products_set if (rel := next(iter(product.ContainedInStructure), None)))
for rel in rels:
related_elements = set(rel.RelatedElements) - products
related_elements = set(rel.RelatedElements) - products_set
if related_elements:
rel.RelatedElements = list(related_elements)
ifcopenshell.api.owner.update_owner_history(file, element=rel)
@@ -27,7 +27,7 @@ def add_structural_activity(
ifc_class: str = "IfcStructuralPlanarAction",
predefined_type: str = "CONST",
global_or_local: Literal["GLOBAL_COORDS", "LOCAL_COORDS"] = "GLOBAL_COORDS",
) -> None:
) -> ifcopenshell.entity_instance:
"""Adds a new structural activity
A structural activity is either a structural action or a reaction. It
@@ -38,42 +38,29 @@ def add_structural_activity(
a structural member.
:param ifc_class: Choose from any subtype of IfcStructuralActivity.
:type ifc_class: str
:param predefined_type: View the IFC documentation for what valid
predefined types may be chosen.
:type predefined_type: str
:param global_or_local: The location coordinates of the load is always
defined locally relative to the structural member the activity is
assigned to. However, the directions of the applied load may either
be specified globally or locally depending on how this argument is
set. Choose from GLOBAL_COORDS or LOCAL_COORDS.
:type global_or_local: str
:param applied_load: The IfcStructuralLoad that is applied in this
activity.
:type applied_load: ifcopenshell.entity_instance
:param structural_member: The IfcStructuralMember that the load is
applied to.
:type structural_member: ifcopenshell.entity_instance
:return: The newly created entity based on the ifc_class
:rtype: ifcopenshell.entity_instance
"""
settings = {
"ifc_class": ifc_class,
"predefined_type": predefined_type,
"global_or_local": global_or_local,
"applied_load": applied_load,
"structural_member": structural_member,
}
activity = ifcopenshell.api.root.create_entity(
file,
ifc_class=settings["ifc_class"],
predefined_type=settings["predefined_type"],
ifc_class=ifc_class,
predefined_type=predefined_type,
)
activity.AppliedLoad = settings["applied_load"]
activity.GlobalOrLocal = settings["global_or_local"]
activity.AppliedLoad = applied_load
activity.GlobalOrLocal = global_or_local
rel = ifcopenshell.api.root.create_entity(file, ifc_class="IfcRelConnectsStructuralActivity")
rel.RelatingElement = settings["structural_member"]
rel.RelatingElement = structural_member
rel.RelatedStructuralActivity = activity
return activity
@@ -32,18 +32,14 @@ def add_structural_boundary_condition(
edge condition, and surface connections will have a face condition.
:param name: The name of the boundary condition.
:type name: str,optional
:param connection: The IfcStructuralConnection to apply the boundary
condition to. This will determine the type of condition that is
created. If no connection is supplied, an orphan boundary condition
will be created using the ifc_class that you specify.
:type connection: ifcopenshell.entity_instance,optional
:param ifc_class: The class of IfcBoundaryCondition to create, only
relevant if you do not specify a connection and want to create an
orphaned boundary condition.
:type ifc_class: str,optional
:return: The newly created IfcBoundaryCondition
:rtype: ifcopenshell.entity_instance
Example:
@@ -51,14 +47,12 @@ def add_structural_boundary_condition(
ifcopenshell.api.structural.add_structural_boundary_condition(model, connection=connection)
"""
settings = {"name": name, "connection": connection, "ifc_class": ifc_class}
if settings["connection"]:
if connection:
# assign boundary condition to a connection
if settings["connection"].is_a("IfcRelConnectsStructuralMember"):
related_connection = settings["connection"].RelatedStructuralConnection
if connection.is_a("IfcRelConnectsStructuralMember"):
related_connection = connection.RelatedStructuralConnection
else:
related_connection = settings["connection"]
related_connection = connection
if related_connection.is_a("IfcStructuralPointConnection"):
boundary_class = "IfcBoundaryNodeCondition"
@@ -67,9 +61,9 @@ def add_structural_boundary_condition(
elif related_connection.is_a("IfcStructuralSurfaceConnection"):
boundary_class = "IfcBoundaryFaceCondition"
condition = file.create_entity(boundary_class, Name=settings["name"])
settings["connection"].AppliedCondition = condition
condition = file.create_entity(boundary_class, Name=name)
connection.AppliedCondition = condition
return condition
else:
# add an orphan boundary condition
return file.create_entity(settings["ifc_class"], Name=settings["name"])
return file.create_entity(ifc_class, Name=name)
@@ -29,22 +29,15 @@ def add_structural_member_connection(
:param relating_structural_member: The IfcStructuralMember to have a
connection added to it.
:type relating_structural_member: ifcopenshell.entity_instance
:param related_structural_connection: The IfcStructuralConnection to add
to the IfcStructuralMember.
:type related_structural_connection: ifcopenshell.entity_instance
:return: The IfcRelConnectsStructuralMember relationship
:rtype: ifcopenshell.entity_instance
"""
settings = {
"relating_structural_member": relating_structural_member,
"related_structural_connection": related_structural_connection,
}
for connection in settings["related_structural_connection"].ConnectsStructuralMembers or []:
if connection.RelatingStructuralMember == settings["relating_structural_member"]:
for connection in related_structural_connection.ConnectsStructuralMembers or []:
if connection.RelatingStructuralMember == relating_structural_member:
return connection
rel = ifcopenshell.api.root.create_entity(file, ifc_class="IfcRelConnectsStructuralMember")
rel.RelatingStructuralMember = settings["relating_structural_member"]
rel.RelatedStructuralConnection = settings["related_structural_connection"]
rel.RelatingStructuralMember = relating_structural_member
rel.RelatedStructuralConnection = related_structural_connection
return rel
@@ -27,18 +27,14 @@ def remove_structural_connection_condition(file: ifcopenshell.file, relation: if
The condition and the member itself is preserved.
:param relation: The IfcRelConnectsStructuralMember to remove.
:type relation: ifcopenshell.entity_instance
:return: None
:rtype: None
"""
settings = {"relation": relation}
if settings["relation"].AppliedCondition:
if relation.AppliedCondition:
ifcopenshell.api.structural.remove_structural_boundary_condition(
file,
connection=settings["relation"].RelatedStructuralConnection,
connection=relation.RelatedStructuralConnection,
)
history = settings["relation"].OwnerHistory
file.remove(settings["relation"])
history = relation.OwnerHistory
file.remove(relation)
if history:
ifcopenshell.util.element.remove_deep2(file, history)
@@ -25,9 +25,7 @@ def remove_styled_representation(file: ifcopenshell.file, representation: ifcope
removes the representation but not the underlying styles.
:param representation: The IfcStyledRepresentation to remove.
:type representation: ifcopenshell.entity_instance
:return: None
:rtype: None
Example:
@@ -36,17 +34,15 @@ def remove_styled_representation(file: ifcopenshell.file, representation: ifcope
# Remove a styled representation
ifcopenshell.api.style.remove_styled_representation(model, representation=representation)
"""
settings = {"representation": representation}
for inverse in file.get_inverse(settings["representation"]):
for inverse in file.get_inverse(representation):
if inverse.is_a("IfcMaterialDefinitionRepresentation") and len(inverse.Representations) == 1:
file.remove(inverse)
for item in settings["representation"].Items:
for item in representation.Items:
if item.is_a("IfcStyledItem") and file.get_total_inverses(item) == 1:
for style in item.Styles:
if style.is_a("IfcPresentationStyleAssignment"):
file.remove(style)
file.remove(item)
file.remove(settings["representation"])
file.remove(representation)
@@ -22,7 +22,9 @@ import ifcopenshell.api.system
from typing import Optional
def add_port(file: ifcopenshell.file, element: Optional[ifcopenshell.entity_instance] = None) -> None:
def add_port(
file: ifcopenshell.file, element: Optional[ifcopenshell.entity_instance] = None
) -> ifcopenshell.entity_instance:
"""Adds a new distribution port to an element
A distribution port represents a connection point on an element, where
@@ -36,9 +38,7 @@ def add_port(file: ifcopenshell.file, element: Optional[ifcopenshell.entity_inst
:param element: The IfcDistributionElement you want to add a
distribution port to.
:type element: ifcopenshell.entity_instance, optional
:return: The newly created IfcDistributionPort
:rtype: ifcopenshell.entity_instance
Example:
@@ -52,11 +52,7 @@ def add_port(file: ifcopenshell.file, element: Optional[ifcopenshell.entity_inst
port1 = ifcopenshell.api.system.add_port(model, element=duct)
port2 = ifcopenshell.api.system.add_port(model, element=duct)
"""
settings = {
"element": element,
}
port = ifcopenshell.api.root.create_entity(file, ifc_class="IfcDistributionPort")
if settings["element"]:
ifcopenshell.api.system.assign_port(file, element=settings["element"], port=port)
if element:
ifcopenshell.api.system.assign_port(file, element=element, port=port)
return port
@@ -34,9 +34,7 @@ def add_system(file: ifcopenshell.file, ifc_class: str = "IfcDistributionSystem"
security systems. Alternatively you may choose IfcBuildingSystem for
specialised building facade systems or similar. For IFC2X3, choose
IfcSystem.
:type ifc_class: str
:return: The newly created IfcSystem.
:rtype: ifcopenshell.entity_instance
Example:
@@ -45,9 +43,7 @@ def add_system(file: ifcopenshell.file, ifc_class: str = "IfcDistributionSystem"
# A completely empty distribution system
system = ifcopenshell.api.system.add_system(model)
"""
settings = {"ifc_class": ifc_class}
ifc_class = settings["ifc_class"]
ifc_class = ifc_class
# workaround for failing default argument in ifc2x3
if file.schema == "IFC2X3" and ifc_class == "IfcDistributionSystem":
ifc_class = "IfcSystem"
@@ -34,12 +34,9 @@ def assign_flow_control(
:param related_flow_control: IfcDistributionControlElement
which may be used to impart control on the flow element
:type related_flow_control: ifcopenshell.entity_instance
:param relating_flow_element: The IfcDistributionFlowElement that is being controlled / sensed
:type relating_flow_element: ifcopenshell.entity_instance
:return: Matching or newly created IfcRelFlowControlElements. If control
is already assigned to some other element method will return None.
:rtype: ifcopenshell.entity_instance, None
Example:
@@ -51,26 +48,21 @@ def assign_flow_control(
model, related_flow_control=flow_control, relating_flow_element=flow_element
)
"""
settings = {
"relating_flow_element": relating_flow_element,
"related_flow_control": related_flow_control,
}
if settings["related_flow_control"].AssignedToFlowElement:
if related_flow_control.AssignedToFlowElement:
# only 1 control per 1 flow element is possible
assignment = settings["related_flow_control"].AssignedToFlowElement[0]
if assignment.RelatingFlowElement == settings["relating_flow_element"]:
assignment = related_flow_control.AssignedToFlowElement[0]
if assignment.RelatingFlowElement == relating_flow_element:
return assignment
# return None if this control is already assigned to another flow element
return
if settings["relating_flow_element"].HasControlElements:
assignment = settings["relating_flow_element"].HasControlElements[0]
if settings["related_flow_control"] in assignment.RelatedControlElements:
if relating_flow_element.HasControlElements:
assignment = relating_flow_element.HasControlElements[0]
if related_flow_control in assignment.RelatedControlElements:
return assignment
related_flow_controls = set(assignment.RelatedControlElements)
related_flow_controls.add(settings["related_flow_control"])
related_flow_controls.add(related_flow_control)
assignment.RelatedControlElements = list(related_flow_controls)
ifcopenshell.api.owner.update_owner_history(file, **{"element": assignment})
return assignment
@@ -80,8 +72,8 @@ def assign_flow_control(
**{
"GlobalId": ifcopenshell.guid.new(),
"OwnerHistory": ifcopenshell.api.owner.create_owner_history(file),
"RelatedControlElements": [settings["related_flow_control"]],
"RelatingFlowElement": settings["relating_flow_element"],
"RelatedControlElements": [related_flow_control],
"RelatingFlowElement": relating_flow_element,
},
)
return assignment
@@ -34,12 +34,9 @@ def assign_port(
it may be useful when patching up models.
:param element: The IfcDistributionElement to assign the port to.
:type element: ifcopenshell.entity_instance
:param port: The IfcDistributionPort you want to assign.
:type port: ifcopenshell.entity_instance
:return: The IfcRelNests relationship, or the
IfcRelConnectsPortToElement for IFC2X3.
:rtype: ifcopenshell.entity_instance
Example:
@@ -61,31 +58,30 @@ def assign_port(
"""
usecase = Usecase()
usecase.file = file
usecase.settings = {
"element": element,
"port": port,
}
return usecase.execute()
return usecase.execute(element, port)
class Usecase:
file: ifcopenshell.file
settings: dict[str, Any]
def execute(self):
def execute(
self, element: ifcopenshell.entity_instance, port: ifcopenshell.entity_instance
) -> ifcopenshell.entity_instance:
self.element = element
self.port = port
if self.file.schema == "IFC2X3":
return self.execute_ifc2x3()
rels = self.settings["element"].IsNestedBy or []
rels = self.element.IsNestedBy or []
for rel in rels:
if self.settings["port"] in rel.RelatedObjects:
if self.port in rel.RelatedObjects:
return rel
if rels:
rel = rels[0]
related_objects = set(rel.RelatedObjects) or set()
related_objects.add(self.settings["port"])
related_objects.add(self.port)
rel.RelatedObjects = list(related_objects)
ifcopenshell.api.owner.update_owner_history(self.file, **{"element": rel})
else:
@@ -93,34 +89,34 @@ class Usecase:
"IfcRelNests",
GlobalId=ifcopenshell.guid.new(),
OwnerHistory=ifcopenshell.api.owner.create_owner_history(self.file),
RelatedObjects=[self.settings["port"]],
RelatingObject=self.settings["element"],
RelatedObjects=[self.port],
RelatingObject=self.element,
)
self.update_port_placement()
return rel
def execute_ifc2x3(self):
for rel in self.settings["element"].HasPorts or []:
if rel.RelatingPort == self.settings["port"]:
def execute_ifc2x3(self) -> ifcopenshell.entity_instance:
for rel in self.element.HasPorts or []:
if rel.RelatingPort == self.port:
return rel
rel = self.file.create_entity(
"IfcRelConnectsPortToElement",
GlobalId=ifcopenshell.guid.new(),
OwnerHistory=ifcopenshell.api.owner.create_owner_history(self.file),
RelatingPort=self.settings["port"],
RelatedElement=self.settings["element"],
RelatingPort=self.port,
RelatedElement=self.element,
)
self.update_port_placement()
return rel
def update_port_placement(self):
placement = getattr(self.settings["port"], "ObjectPlacement", None)
def update_port_placement(self) -> None:
placement = getattr(self.port, "ObjectPlacement", None)
if placement and placement.is_a("IfcLocalPlacement"):
ifcopenshell.api.geometry.edit_object_placement(
self.file,
product=self.settings["port"],
matrix=ifcopenshell.util.placement.get_local_placement(self.settings["port"].ObjectPlacement),
product=self.port,
matrix=ifcopenshell.util.placement.get_local_placement(self.port.ObjectPlacement),
is_si=False,
)
@@ -64,12 +64,8 @@ def disconnect_port(file: ifcopenshell.file, port: ifcopenshell.entity_instance)
# fitting_port1 instead of duct_port2
ifcopenshell.api.system.disconnect_port(model, port=duct_port2)
"""
settings = {
"port": port,
}
rels = settings["port"].ConnectedTo or ()
rels += settings["port"].ConnectedFrom or ()
rels = port.ConnectedTo or ()
rels += port.ConnectedFrom or ()
for rel in rels:
rel.RelatingPort.FlowDirection = None
@@ -27,9 +27,7 @@ def remove_system(file: ifcopenshell.file, system: ifcopenshell.entity_instance)
All the distribution elements within the system are retained.
:param system: The IfcSystem to remove.
:type system: ifcopenshell.entity_instance
:return: None
:rtype: None
Example:
@@ -41,9 +39,7 @@ def remove_system(file: ifcopenshell.file, system: ifcopenshell.entity_instance)
# Delete it.
ifcopenshell.api.system.remove_system(model, system=system)
"""
settings = {"system": system}
for inverse_id in [i.id() for i in file.get_inverse(settings["system"])]:
for inverse_id in [i.id() for i in file.get_inverse(system)]:
try:
inverse = file.by_id(inverse_id)
except:
@@ -51,11 +47,11 @@ def remove_system(file: ifcopenshell.file, system: ifcopenshell.entity_instance)
if inverse.is_a("IfcRelDefinesByProperties"):
ifcopenshell.api.pset.remove_pset(
file,
product=settings["system"],
product=system,
pset=inverse.RelatingPropertyDefinition,
)
elif inverse.is_a("IfcRelAssignsToGroup"):
if inverse.RelatingGroup == settings["system"]:
if inverse.RelatingGroup == system:
history = inverse.OwnerHistory
file.remove(inverse)
if history:
@@ -65,7 +61,7 @@ def remove_system(file: ifcopenshell.file, system: ifcopenshell.entity_instance)
file.remove(inverse)
if history:
ifcopenshell.util.element.remove_deep2(file, history)
history = settings["system"].OwnerHistory
file.remove(settings["system"])
history = system.OwnerHistory
file.remove(system)
if history:
ifcopenshell.util.element.remove_deep2(file, history)
@@ -30,11 +30,8 @@ def unassign_flow_control(
:param related_flow_control: IfcDistributionControlElement controling the
flow element
:type related_flow_control: ifcopenshell.entity_instance
:param relating_flow_element: The IfcDistributionFlowElement that is being controlled
:type relating_flow_element: ifcopenshell.entity_instance
:return: None
:rtype: None
Example:
@@ -53,15 +50,10 @@ def unassign_flow_control(
)
"""
settings = {
"relating_flow_element": relating_flow_element,
"related_flow_control": related_flow_control,
}
if not settings["related_flow_control"].AssignedToFlowElement:
if not related_flow_control.AssignedToFlowElement:
return
assignment = settings["related_flow_control"].AssignedToFlowElement[0]
if assignment.RelatingFlowElement != settings["relating_flow_element"]:
assignment = related_flow_control.AssignedToFlowElement[0]
if assignment.RelatingFlowElement != relating_flow_element:
return
if len(assignment.RelatedControlElements) == 1:
history = assignment.OwnerHistory
@@ -70,6 +62,6 @@ def unassign_flow_control(
ifcopenshell.util.element.remove_deep2(file, history)
return
related_flow_controls = list(assignment.RelatedControlElements)
related_flow_controls.remove(settings["related_flow_control"])
related_flow_controls.remove(related_flow_control)
assignment.RelatedControlElements = related_flow_controls
ifcopenshell.api.owner.update_owner_history(file, **{"element": assignment})
@@ -19,7 +19,6 @@
import ifcopenshell
import ifcopenshell.api.owner
import ifcopenshell.util.element
from typing import Any
def unassign_port(
@@ -32,11 +31,8 @@ def unassign_port(
port for cleaning or patchin purposes.
:param element: The IfcDistributionElement to unassign the port from.
:type element: ifcopenshell.entity_instance
:param port: The IfcDistributionPort you want to unassign.
:type port: ifcopenshell.entity_instance
:return: None
:rtype: None
Example:
@@ -55,23 +51,20 @@ def unassign_port(
"""
usecase = Usecase()
usecase.file = file
usecase.settings = {
"element": element,
"port": port,
}
return usecase.execute()
return usecase.execute(element, port)
class Usecase:
file: ifcopenshell.file
settings: dict[str, Any]
def execute(self):
def execute(self, element: ifcopenshell.entity_instance, port: ifcopenshell.entity_instance) -> None:
if self.file.schema == "IFC2X3":
self.element = element
self.port = port
return self.execute_ifc2x3()
for rel in self.settings["element"].IsNestedBy or []:
if self.settings["port"] in rel.RelatedObjects:
for rel in element.IsNestedBy or []:
if port in rel.RelatedObjects:
if len(rel.RelatedObjects) == 1:
history = rel.OwnerHistory
self.file.remove(rel)
@@ -79,13 +72,13 @@ class Usecase:
ifcopenshell.util.element.remove_deep2(self.file, history)
return
related_objects = set(rel.RelatedObjects) or set()
related_objects.remove(self.settings["port"])
related_objects.remove(port)
rel.RelatedObjects = list(related_objects)
ifcopenshell.api.owner.update_owner_history(self.file, **{"element": rel})
def execute_ifc2x3(self):
for rel in self.settings["element"].HasPorts or []:
if rel.RelatingPort == self.settings["port"]:
def execute_ifc2x3(self) -> None:
for rel in self.element.HasPorts or []:
if rel.RelatingPort == self.port:
history = rel.OwnerHistory
self.file.remove(rel)
if history:
@@ -33,11 +33,8 @@ def map_type_representations(
be used to ensure consistency of the occurrence's representations.
:param related_object: The IfcElement occurrence.
:type related_object: ifcopenshell.entity_instance
:param relating_type: The IfcElementType type.
:type relating_type: ifcopenshell.entity_instance
:return: None
:rtype: None
Example:
@@ -84,28 +81,23 @@ def map_type_representations(
# ifcopenshell.api.type.map_type_representations(model,
# related_object=furniture, relating_type=furniture_type)
"""
settings = {
"related_object": related_object,
"relating_type": relating_type,
}
if not settings["relating_type"].RepresentationMaps:
if not relating_type.RepresentationMaps:
return
representations = []
if settings["related_object"].Representation:
representations = settings["related_object"].Representation.Representations
if related_object.Representation:
representations = related_object.Representation.Representations
for representation in representations:
ifcopenshell.api.geometry.unassign_representation(
file,
product=settings["related_object"],
product=related_object,
representation=representation,
)
ifcopenshell.api.geometry.remove_representation(file, **{"representation": representation})
for representation_map in settings["relating_type"].RepresentationMaps:
ifcopenshell.api.geometry.remove_representation(file, representation=representation)
for representation_map in relating_type.RepresentationMaps:
representation = representation_map.MappedRepresentation
mapped_representation = ifcopenshell.api.geometry.map_representation(file, representation=representation)
ifcopenshell.api.geometry.assign_representation(
file,
product=settings["related_object"],
product=related_object,
representation=mapped_representation,
)
@@ -35,9 +35,7 @@ def add_context_dependent_unit(
sensible normal unit for. In that case, firstly stop whatever you're
doing and have a hard think about your life, and then if life really
is going that badly for you, check out the IFC docs for IfcUnitEnum.
:type unit_type: str
:param name: Give your unit a name. X what? X bananas?
:type name: str
:param dimensions: Units typically measure one of 7 fundamental physical
dimensions: length, mass, time, electric current, temperature,
substance amount, or luminous intensity. These are represented as a
@@ -46,9 +44,7 @@ def add_context_dependent_unit(
where as an area unit is (2, 0, 0, 0, 0, 0, 0). A unit of meters per
second is (1, 0, -1, 0, 0, 0, 0). For context dependent units, it is
recommended to leave this as the default of (0, 0, 0, 0, 0, 0, 0).
:type dimensions: list[int]
:return: The new IfcContextDependentUnit
:rtype: ifcopenshell.entity_instance
Example:
@@ -57,11 +53,9 @@ def add_context_dependent_unit(
# Boxes of things
ifcopenshell.api.unit.add_context_dependent_unit(model, name="BOXES")
"""
settings = {"unit_type": unit_type, "name": name, "dimensions": dimensions}
return file.create_entity(
"IfcContextDependentUnit",
Dimensions=file.createIfcDimensionalExponents(*settings["dimensions"]),
UnitType=settings["unit_type"],
Name=settings["name"],
Dimensions=file.createIfcDimensionalExponents(*dimensions),
UnitType=unit_type,
Name=name,
)
@@ -36,17 +36,14 @@ def add_conversion_based_unit(
kip, psi, ksi, minute, hour, day, btu, and fahrenheit.
:param name: A converted name chosen from the list above.
:type name: str
:param conversion_offset: If you want to offset the conversion further
by a set number, you may specify it here. For example, fahrenheit is
1.8 * kelvin - 459.67. The -459.67 is the conversion offset. Note
that this is just an example and you don't actually need to specify
that for fahrenheit as it's built into this API function. For
advanced users only.
:type conversion_offset: float, optional
:return: The new IfcConversionBasedUnit or
IfcConversionBasedUnitWithOffset
:rtype: ifcopenshell.entity_instance
Example:
@@ -59,28 +56,26 @@ def add_conversion_based_unit(
# Make it our default units, if we are doing an imperial building
ifcopenshell.api.unit.assign_unit(model, units=[length, area])
"""
settings = {"name": name, "conversion_offset": conversion_offset}
unit_type = ifcopenshell.util.unit.imperial_types.get(settings["name"], "USERDEFINED")
unit_type = ifcopenshell.util.unit.imperial_types.get(name, "USERDEFINED")
dimensions = ifcopenshell.util.unit.named_dimensions[unit_type]
exponents = file.createIfcDimensionalExponents(*dimensions)
si_name = ifcopenshell.util.unit.si_type_names[unit_type]
si_unit = file.createIfcSIUnit(UnitType=unit_type, Name=si_name)
conversion_real = ifcopenshell.util.unit.si_conversions.get(settings["name"], 1)
conversion_real = ifcopenshell.util.unit.si_conversions.get(name, 1)
value_component = file.create_entity("IfcReal", **{"wrappedValue": conversion_real})
conversion_factor = file.createIfcMeasureWithUnit(value_component, si_unit)
conversion_offset = settings["conversion_offset"]
if not conversion_offset:
conversion_offset = ifcopenshell.util.unit.si_offsets.get(settings["name"], 0)
conversion_offset = ifcopenshell.util.unit.si_offsets.get(name, 0)
if conversion_offset:
return file.createIfcConversionBasedUnitWithOffset(
exponents,
unit_type,
settings["name"],
name,
conversion_factor,
conversion_offset,
)
return file.createIfcConversionBasedUnit(exponents, unit_type, settings["name"], conversion_factor)
return file.createIfcConversionBasedUnit(exponents, unit_type, name, conversion_factor)
@@ -26,9 +26,7 @@ def add_monetary_unit(file: ifcopenshell.file, currency: str = "DOLLARYDOO") ->
USD, GBP, AUD, MYR, etc.
:param currency: The currency code
:type currency: str
:return: The newly created IfcMonetaryUnit
:rtype: ifcopenshell.entity_instance
Example:
@@ -41,6 +39,4 @@ def add_monetary_unit(file: ifcopenshell.file, currency: str = "DOLLARYDOO") ->
# Make it our default currency
ifcopenshell.api.unit.assign_unit(model, units=[zwl])
"""
settings = {"currency": currency}
return file.create_entity("IfcMonetaryUnit", settings["currency"])
return file.create_entity("IfcMonetaryUnit", currency)
@@ -40,12 +40,9 @@ def add_si_unit(
:param unit_type: A type of unit chosen from the list above. For
example, choosing LENGTHUNIT will give you a metre.
:type unit_type: str
:param prefix: A prefix chosen from the list above, or None for no
prefix.
:type prefix: str,optional
:return: The newly created IfcSIUnit
:rtype: ifcopenshell.entity_instance
Example:
@@ -58,7 +55,5 @@ def add_si_unit(
# Make it our default units, if we are doing a metric building
ifcopenshell.api.unit.assign_unit(model, units=[length, area])
"""
settings = {"unit_type": unit_type, "prefix": prefix}
name = ifcopenshell.util.unit.si_type_names.get(settings["unit_type"], None)
return file.create_entity("IfcSIUnit", UnitType=settings["unit_type"], Name=name, Prefix=settings["prefix"])
name = ifcopenshell.util.unit.si_type_names.get(unit_type, None)
return file.create_entity("IfcSIUnit", UnitType=unit_type, Name=name, Prefix=prefix)

Some files were not shown because too many files have changed in this diff Show More