First Commit
This commit is contained in:
@@ -0,0 +1,56 @@
|
||||
# IfcOpenShell - IFC toolkit and geometry engine
|
||||
# Copyright (C) 2022 Dion Moult <dion@thinkmoult.com>
|
||||
#
|
||||
# This file is part of IfcOpenShell.
|
||||
#
|
||||
# IfcOpenShell is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Lesser General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# IfcOpenShell is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Lesser General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Lesser General Public License
|
||||
# along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
"""Manage distribution systems and port connectivity
|
||||
|
||||
Service distribution systems (mechanical, electrical, hydraulic, fire,
|
||||
logistical, etc) consist of connected distribution segments, fittings,
|
||||
terminals, control equipment, and more. This module handles port connectivity
|
||||
and relationships describing distribution flow.
|
||||
"""
|
||||
|
||||
from .. import wrap_usecases
|
||||
from .add_port import add_port
|
||||
from .add_system import add_system
|
||||
from .assign_flow_control import assign_flow_control
|
||||
from .assign_port import assign_port
|
||||
from .assign_system import assign_system
|
||||
from .connect_port import connect_port
|
||||
from .disconnect_port import disconnect_port
|
||||
from .edit_system import edit_system
|
||||
from .remove_system import remove_system
|
||||
from .unassign_flow_control import unassign_flow_control
|
||||
from .unassign_port import unassign_port
|
||||
from .unassign_system import unassign_system
|
||||
|
||||
wrap_usecases(__path__, __name__)
|
||||
|
||||
__all__ = [
|
||||
"add_port",
|
||||
"add_system",
|
||||
"assign_flow_control",
|
||||
"assign_port",
|
||||
"assign_system",
|
||||
"connect_port",
|
||||
"disconnect_port",
|
||||
"edit_system",
|
||||
"remove_system",
|
||||
"unassign_flow_control",
|
||||
"unassign_port",
|
||||
"unassign_system",
|
||||
]
|
||||
Binary file not shown.
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
@@ -0,0 +1,59 @@
|
||||
# IfcOpenShell - IFC toolkit and geometry engine
|
||||
# Copyright (C) 2022 Dion Moult <dion@thinkmoult.com>
|
||||
#
|
||||
# This file is part of IfcOpenShell.
|
||||
#
|
||||
# IfcOpenShell is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Lesser General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# IfcOpenShell is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Lesser General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Lesser General Public License
|
||||
# along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
from typing import Optional
|
||||
|
||||
import ifcopenshell
|
||||
import ifcopenshell.api.root
|
||||
import ifcopenshell.api.system
|
||||
|
||||
|
||||
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
|
||||
a distribution element may be connected to another distribution element.
|
||||
For example, a duct segment will typically have two ports, one at either
|
||||
end, because you can attach another segment or fitting to either end of
|
||||
the duct segment.
|
||||
|
||||
This will both add a distribution port and automatically assign it to a
|
||||
distribution element.
|
||||
|
||||
:param element: The IfcDistributionElement you want to add a
|
||||
distribution port to.
|
||||
:return: The newly created IfcDistributionPort
|
||||
|
||||
Example:
|
||||
|
||||
.. code:: python
|
||||
|
||||
# Create a duct
|
||||
duct = ifcopenshell.api.root.create_entity(model,
|
||||
ifc_class="IfcDuctSegment", predefined_type="RIGIDSEGMENT")
|
||||
|
||||
# Create 2 ports, one for either end.
|
||||
port1 = ifcopenshell.api.system.add_port(model, element=duct)
|
||||
port2 = ifcopenshell.api.system.add_port(model, element=duct)
|
||||
"""
|
||||
port = ifcopenshell.api.root.create_entity(file, ifc_class="IfcDistributionPort")
|
||||
if element:
|
||||
ifcopenshell.api.system.assign_port(file, element=element, port=port)
|
||||
return port
|
||||
@@ -0,0 +1,58 @@
|
||||
# IfcOpenShell - IFC toolkit and geometry engine
|
||||
# Copyright (C) 2021 Dion Moult <dion@thinkmoult.com>
|
||||
#
|
||||
# This file is part of IfcOpenShell.
|
||||
#
|
||||
# IfcOpenShell is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Lesser General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# IfcOpenShell is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Lesser General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Lesser General Public License
|
||||
# along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import ifcopenshell
|
||||
import ifcopenshell.api.owner
|
||||
import ifcopenshell.guid
|
||||
|
||||
|
||||
def add_system(file: ifcopenshell.file, ifc_class: str = "IfcDistributionSystem") -> ifcopenshell.entity_instance:
|
||||
"""Add a new distribution system
|
||||
|
||||
A distribution system is a group of distribution elements, like ducts,
|
||||
pipes, pumps, filters, fans, and so on that distribute a medium (air,
|
||||
liquid, or electricity) throughout a facility. Systems may be
|
||||
hierarchical, with larger systems composed of smaller subsystems.
|
||||
|
||||
:param ifc_class: The type of system, chosen from IfcDistributionSystem
|
||||
for mechanical, electrical, communications, plumbing, fire, or
|
||||
security systems. Alternatively you may choose IfcBuildingSystem for
|
||||
specialised building facade systems or similar. For IFC2X3, choose
|
||||
IfcSystem.
|
||||
:return: The newly created IfcSystem.
|
||||
|
||||
Example:
|
||||
|
||||
.. code:: python
|
||||
|
||||
# A completely empty distribution system
|
||||
system = ifcopenshell.api.system.add_system(model)
|
||||
"""
|
||||
ifc_class = ifc_class
|
||||
# workaround for failing default argument in ifc2x3
|
||||
if file.schema == "IFC2X3" and ifc_class == "IfcDistributionSystem":
|
||||
ifc_class = "IfcSystem"
|
||||
|
||||
return file.create_entity(
|
||||
ifc_class,
|
||||
**{
|
||||
"GlobalId": ifcopenshell.guid.new(),
|
||||
"OwnerHistory": ifcopenshell.api.owner.create_owner_history(file),
|
||||
"Name": "Unnamed",
|
||||
}
|
||||
)
|
||||
@@ -0,0 +1,80 @@
|
||||
# IfcOpenShell - IFC toolkit and geometry engine
|
||||
# Copyright (C) 2021 Dion Moult <dion@thinkmoult.com>
|
||||
#
|
||||
# This file is part of IfcOpenShell.
|
||||
#
|
||||
# IfcOpenShell is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Lesser General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# IfcOpenShell is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Lesser General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Lesser General Public License
|
||||
# along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
from typing import Union
|
||||
|
||||
import ifcopenshell
|
||||
import ifcopenshell.api.owner
|
||||
import ifcopenshell.guid
|
||||
|
||||
|
||||
def assign_flow_control(
|
||||
file: ifcopenshell.file,
|
||||
relating_flow_element: ifcopenshell.entity_instance,
|
||||
related_flow_control: ifcopenshell.entity_instance,
|
||||
) -> Union[ifcopenshell.entity_instance, None]:
|
||||
"""Assigns to the flow element control element that either sense or control
|
||||
some aspect of the flow element.
|
||||
|
||||
Note that control can be assigned only to the one flow element.
|
||||
|
||||
:param related_flow_control: IfcDistributionControlElement
|
||||
which may be used to impart control on the flow element
|
||||
:param relating_flow_element: The IfcDistributionFlowElement that is being controlled / sensed
|
||||
:return: Matching or newly created IfcRelFlowControlElements. If control
|
||||
is already assigned to some other element method will return None.
|
||||
|
||||
Example:
|
||||
|
||||
.. code:: python
|
||||
|
||||
flow_element = model.createIfcFlowSegment()
|
||||
flow_control = model.createIfcController()
|
||||
relation = ifcopenshell.api.system.assign_flow_control(
|
||||
model, related_flow_control=flow_control, relating_flow_element=flow_element
|
||||
)
|
||||
"""
|
||||
if related_flow_control.AssignedToFlowElement:
|
||||
# only 1 control per 1 flow element is possible
|
||||
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 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(related_flow_control)
|
||||
assignment.RelatedControlElements = list(related_flow_controls)
|
||||
ifcopenshell.api.owner.update_owner_history(file, element=assignment)
|
||||
return assignment
|
||||
|
||||
assignment = file.create_entity(
|
||||
"IfcRelFlowControlElements",
|
||||
**{
|
||||
"GlobalId": ifcopenshell.guid.new(),
|
||||
"OwnerHistory": ifcopenshell.api.owner.create_owner_history(file),
|
||||
"RelatedControlElements": [related_flow_control],
|
||||
"RelatingFlowElement": relating_flow_element,
|
||||
},
|
||||
)
|
||||
return assignment
|
||||
@@ -0,0 +1,122 @@
|
||||
# IfcOpenShell - IFC toolkit and geometry engine
|
||||
# Copyright (C) 2022 Dion Moult <dion@thinkmoult.com>
|
||||
#
|
||||
# This file is part of IfcOpenShell.
|
||||
#
|
||||
# IfcOpenShell is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Lesser General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# IfcOpenShell is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Lesser General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Lesser General Public License
|
||||
# along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
|
||||
import ifcopenshell
|
||||
import ifcopenshell.api.geometry
|
||||
import ifcopenshell.api.owner
|
||||
import ifcopenshell.guid
|
||||
import ifcopenshell.util.placement
|
||||
|
||||
|
||||
def assign_port(
|
||||
file: ifcopenshell.file, element: ifcopenshell.entity_instance, port: ifcopenshell.entity_instance
|
||||
) -> ifcopenshell.entity_instance:
|
||||
"""Assigns a port to an element
|
||||
|
||||
If you have an orphaned port, you may assign it to a distribution
|
||||
element using this function. Ports should typically not be orphaned, but
|
||||
it may be useful when patching up models.
|
||||
|
||||
:param element: The IfcDistributionElement to assign the port to.
|
||||
:param port: The IfcDistributionPort you want to assign.
|
||||
:return: The IfcRelNests relationship, or the
|
||||
IfcRelConnectsPortToElement for IFC2X3.
|
||||
|
||||
Example:
|
||||
|
||||
.. code:: python
|
||||
|
||||
# Create a duct
|
||||
duct = ifcopenshell.api.root.create_entity(model,
|
||||
ifc_class="IfcDuctSegment", predefined_type="RIGIDSEGMENT")
|
||||
|
||||
# Create 2 ports, one for either end.
|
||||
port1 = ifcopenshell.api.system.add_port(model, element=duct)
|
||||
port2 = ifcopenshell.api.system.add_port(model, element=duct)
|
||||
|
||||
# Unassign one port for some weird reason.
|
||||
ifcopenshell.api.system.unassign_port(model, element=duct, port=port1)
|
||||
|
||||
# Reassign it back
|
||||
ifcopenshell.api.system.assign_port(model, element=duct, port=port1)
|
||||
"""
|
||||
usecase = Usecase()
|
||||
usecase.file = file
|
||||
return usecase.execute(element, port)
|
||||
|
||||
|
||||
class Usecase:
|
||||
file: ifcopenshell.file
|
||||
|
||||
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.element.IsNestedBy or []
|
||||
|
||||
for rel in rels:
|
||||
if self.port in rel.RelatedObjects:
|
||||
return rel
|
||||
|
||||
if rels:
|
||||
rel = rels[0]
|
||||
related_objects = set(rel.RelatedObjects) or set()
|
||||
related_objects.add(self.port)
|
||||
rel.RelatedObjects = list(related_objects)
|
||||
ifcopenshell.api.owner.update_owner_history(self.file, element=rel)
|
||||
else:
|
||||
rel = self.file.create_entity(
|
||||
"IfcRelNests",
|
||||
GlobalId=ifcopenshell.guid.new(),
|
||||
OwnerHistory=ifcopenshell.api.owner.create_owner_history(self.file),
|
||||
RelatedObjects=[self.port],
|
||||
RelatingObject=self.element,
|
||||
)
|
||||
|
||||
self.update_port_placement()
|
||||
|
||||
return rel
|
||||
|
||||
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.port,
|
||||
RelatedElement=self.element,
|
||||
)
|
||||
self.update_port_placement()
|
||||
return rel
|
||||
|
||||
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.port,
|
||||
matrix=ifcopenshell.util.placement.get_local_placement(self.port.ObjectPlacement),
|
||||
is_si=False,
|
||||
)
|
||||
@@ -0,0 +1,57 @@
|
||||
# IfcOpenShell - IFC toolkit and geometry engine
|
||||
# Copyright (C) 2021 Dion Moult <dion@thinkmoult.com>
|
||||
#
|
||||
# This file is part of IfcOpenShell.
|
||||
#
|
||||
# IfcOpenShell is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Lesser General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# IfcOpenShell is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Lesser General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Lesser General Public License
|
||||
# along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
from typing import Union
|
||||
|
||||
import ifcopenshell
|
||||
import ifcopenshell.api.group
|
||||
import ifcopenshell.util.system
|
||||
|
||||
|
||||
def assign_system(
|
||||
file: ifcopenshell.file,
|
||||
products: list[ifcopenshell.entity_instance],
|
||||
system: ifcopenshell.entity_instance,
|
||||
) -> Union[ifcopenshell.entity_instance, None]:
|
||||
"""Assigns distribution elements to a system
|
||||
|
||||
Note that it is not necessary to assign distribution ports to a system.
|
||||
|
||||
:param products: The list of IfcDistributionElements to assign to the system.
|
||||
:param system: The IfcSystem you want to assign the element to.
|
||||
:return: The IfcRelAssignsToGroup relationship
|
||||
or `None` if `products` was empty list.
|
||||
|
||||
Example:
|
||||
|
||||
.. code:: python
|
||||
|
||||
# A completely empty distribution system
|
||||
system = ifcopenshell.api.system.add_system(model)
|
||||
|
||||
# Create a duct
|
||||
duct = ifcopenshell.api.root.create_entity(model,
|
||||
ifc_class="IfcDuctSegment", predefined_type="RIGIDSEGMENT")
|
||||
|
||||
# This duct is part of the system
|
||||
ifcopenshell.api.system.assign_system(model, products=[duct], system=system)
|
||||
"""
|
||||
if not all(ifcopenshell.util.system.is_assignable(failed_product := product, system) for product in products):
|
||||
raise TypeError(f"You cannot assign an {failed_product.is_a()} to an {system.is_a()}")
|
||||
|
||||
return ifcopenshell.api.group.assign_group(file, products=products, group=system)
|
||||
@@ -0,0 +1,218 @@
|
||||
# IfcOpenShell - IFC toolkit and geometry engine
|
||||
# Copyright (C) 2022 Dion Moult <dion@thinkmoult.com>
|
||||
#
|
||||
# This file is part of IfcOpenShell.
|
||||
#
|
||||
# IfcOpenShell is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Lesser General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# IfcOpenShell is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Lesser General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Lesser General Public License
|
||||
# along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
from typing import Any, Optional
|
||||
|
||||
import ifcopenshell
|
||||
import ifcopenshell.api.owner
|
||||
import ifcopenshell.guid
|
||||
import ifcopenshell.util.element
|
||||
|
||||
|
||||
def connect_port(
|
||||
file: ifcopenshell.file,
|
||||
port1: ifcopenshell.entity_instance,
|
||||
port2: ifcopenshell.entity_instance,
|
||||
direction: str = "NOTDEFINED",
|
||||
element: Optional[ifcopenshell.entity_instance] = None,
|
||||
) -> None:
|
||||
"""Connects two ports together
|
||||
|
||||
A distribution element (e.g. a duct) may be connected to another
|
||||
distribution element (e.g. a fitting) by connecting a port at one of the
|
||||
duct to a port at the same end of the fitting.
|
||||
|
||||
Ports may only have one connection, so you cannot have multiple things
|
||||
connected to the same port. Nor can you have incompatible port
|
||||
connections, such as an electrical port connected to an airflow port.
|
||||
|
||||
Port connectivity may be explicit or implicit. Explicit connections are
|
||||
where the port connectivity is described for every single distribution
|
||||
element in detail. For example, a duct segment would have port
|
||||
connections to a duct fitting, which would have port connections to
|
||||
another duct segment, all the way from a fan to an air terminal exactly
|
||||
as constructed on site. Implicit connections only consider the key
|
||||
distribution control elements (e.g. the fan and the terminal) and ignore
|
||||
all of the details of the duct segments and fittings in between.
|
||||
Generally, explicit connectivity is preferred for later detailed design,
|
||||
and implicit connectivity is preferred for early phase design.
|
||||
|
||||
:param port1: The port of the first distribution element to connect.
|
||||
:param port2: The port of the second distribution element to connect.
|
||||
:param direction: The directionality of distribution flow through the
|
||||
port connection. NOTDEFINED means that the direction has not yet
|
||||
been determined. This is useful during preliminary system design.
|
||||
SOURCE means that the flow is from the first element to the second
|
||||
element. SINK means that the flow is from the second element to the
|
||||
first element. SOURCEANDSINK means that flow is bi-directional
|
||||
between the first and second element. SOURCEANDSINK is a relatively
|
||||
rare scenario.
|
||||
:param element: Optionally set an element through which the port
|
||||
connectivity is made, such as a segment or fitting. This is only to
|
||||
be used for implicit port connectivity where the segments and
|
||||
fittings are less important.
|
||||
|
||||
Example:
|
||||
|
||||
.. code:: python
|
||||
|
||||
# A completely empty distribution system
|
||||
system = ifcopenshell.api.system.add_system(model)
|
||||
|
||||
# Create a duct and a 90 degree bend fitting
|
||||
duct = ifcopenshell.api.root.create_entity(model,
|
||||
ifc_class="IfcDuctSegment", predefined_type="RIGIDSEGMENT")
|
||||
fitting = ifcopenshell.api.root.create_entity(model,
|
||||
ifc_class="IfcDuctFitting", predefined_type="BEND")
|
||||
|
||||
# The duct and fitting is part of the system
|
||||
ifcopenshell.api.system.assign_system(model, products=[duct], system=system)
|
||||
ifcopenshell.api.system.assign_system(model, products=[fitting], system=system)
|
||||
|
||||
# Create 2 ports, one for either end of both the duct and fitting.
|
||||
duct_port1 = ifcopenshell.api.system.add_port(model, element=duct)
|
||||
duct_port2 = ifcopenshell.api.system.add_port(model, element=duct)
|
||||
fitting_port1 = ifcopenshell.api.system.add_port(model, element=fitting)
|
||||
fitting_port2 = ifcopenshell.api.system.add_port(model, element=fitting)
|
||||
|
||||
# Connect the duct and fitting together. At this point, we have not
|
||||
# yet determined the direction of the flow, so we leave direction as
|
||||
# NOTDEFINED.
|
||||
ifcopenshell.api.system.connect_port(model, port1=duct_port2, port2=fitting_port1)
|
||||
"""
|
||||
usecase = Usecase()
|
||||
usecase.file = file
|
||||
usecase.settings = {
|
||||
"port1": port1,
|
||||
"port2": port2,
|
||||
"direction": direction,
|
||||
"element": element,
|
||||
}
|
||||
return usecase.execute()
|
||||
|
||||
|
||||
class Usecase:
|
||||
file: ifcopenshell.file
|
||||
settings: dict[str, Any]
|
||||
|
||||
def execute(self):
|
||||
# Note: there are a number of ambiguities with port connectivity. We
|
||||
# assume system topology is represented by a directed graph. In other
|
||||
# words, SOURCEANDSINK and NOTDEFINED implies a two way connection, with
|
||||
# two IfcRelConnectsPorts. SOURCE or SINK by itself implies a one way
|
||||
# connection. NOTDEFINED semantically implies that although you may
|
||||
# traverse the graph either direction, the direction has not been
|
||||
# determined yet by the engineer. None is not allowed as a direction as
|
||||
# we assume None means that no connection is made.
|
||||
|
||||
if self.settings["port1"] == self.settings["port2"]:
|
||||
return
|
||||
|
||||
self.purge_existing_connections_to_other_ports()
|
||||
|
||||
if self.settings["direction"] == "SOURCE":
|
||||
self.settings["port1"].FlowDirection = "SOURCE"
|
||||
self.settings["port2"].FlowDirection = "SINK"
|
||||
elif self.settings["direction"] == "SINK":
|
||||
self.settings["port1"].FlowDirection = "SINK"
|
||||
self.settings["port2"].FlowDirection = "SOURCE"
|
||||
else:
|
||||
self.settings["port1"].FlowDirection = self.settings["direction"]
|
||||
self.settings["port2"].FlowDirection = self.settings["direction"]
|
||||
|
||||
if self.settings["direction"] in ["SOURCE", "SOURCEANDSINK", "NOTDEFINED"]:
|
||||
self.set_connected_to()
|
||||
else:
|
||||
self.purge_connected_to()
|
||||
|
||||
if self.settings["direction"] in ["SINK", "SOURCEANDSINK", "NOTDEFINED"]:
|
||||
self.set_connected_from()
|
||||
else:
|
||||
self.purge_connected_from()
|
||||
|
||||
self.set_realising_element()
|
||||
|
||||
def purge_existing_connections_to_other_ports(self):
|
||||
for rel in self.settings["port1"].ConnectedTo or []:
|
||||
if rel.RelatedPort != self.settings["port2"]:
|
||||
history = rel.OwnerHistory
|
||||
self.file.remove(rel)
|
||||
if history:
|
||||
ifcopenshell.util.element.remove_deep2(self.file, history)
|
||||
for rel in self.settings["port1"].ConnectedFrom or []:
|
||||
if rel.RelatingPort != self.settings["port2"]:
|
||||
history = rel.OwnerHistory
|
||||
self.file.remove(rel)
|
||||
if history:
|
||||
ifcopenshell.util.element.remove_deep2(self.file, history)
|
||||
for rel in self.settings["port2"].ConnectedTo or []:
|
||||
if rel.RelatedPort != self.settings["port1"]:
|
||||
history = rel.OwnerHistory
|
||||
self.file.remove(rel)
|
||||
if history:
|
||||
ifcopenshell.util.element.remove_deep2(self.file, history)
|
||||
for rel in self.settings["port2"].ConnectedFrom or []:
|
||||
if rel.RelatingPort != self.settings["port1"]:
|
||||
history = rel.OwnerHistory
|
||||
self.file.remove(rel)
|
||||
if history:
|
||||
ifcopenshell.util.element.remove_deep2(self.file, history)
|
||||
|
||||
def set_connected_to(self):
|
||||
if self.settings["port1"].ConnectedTo:
|
||||
return
|
||||
|
||||
self.file.create_entity(
|
||||
"IfcRelConnectsPorts",
|
||||
GlobalId=ifcopenshell.guid.new(),
|
||||
OwnerHistory=ifcopenshell.api.owner.create_owner_history(self.file),
|
||||
RelatingPort=self.settings["port1"],
|
||||
RelatedPort=self.settings["port2"],
|
||||
)
|
||||
|
||||
def set_connected_from(self):
|
||||
if self.settings["port1"].ConnectedFrom:
|
||||
return
|
||||
|
||||
self.file.create_entity(
|
||||
"IfcRelConnectsPorts",
|
||||
GlobalId=ifcopenshell.guid.new(),
|
||||
OwnerHistory=ifcopenshell.api.owner.create_owner_history(self.file),
|
||||
RelatingPort=self.settings["port2"],
|
||||
RelatedPort=self.settings["port1"],
|
||||
)
|
||||
|
||||
def purge_connected_to(self):
|
||||
for rel in self.settings["port1"].ConnectedTo or []:
|
||||
history = rel.OwnerHistory
|
||||
self.file.remove(rel)
|
||||
if history:
|
||||
ifcopenshell.util.element.remove_deep2(self.file, history)
|
||||
|
||||
def purge_connected_from(self):
|
||||
for rel in self.settings["port1"].ConnectedFrom or []:
|
||||
history = rel.OwnerHistory
|
||||
self.file.remove(rel)
|
||||
if history:
|
||||
ifcopenshell.util.element.remove_deep2(self.file, history)
|
||||
|
||||
def set_realising_element(self):
|
||||
for rel in self.settings["port1"].ConnectedTo or []:
|
||||
rel.RealizingElement = self.settings["element"]
|
||||
for rel in self.settings["port1"].ConnectedFrom or []:
|
||||
rel.RealizingElement = self.settings["element"]
|
||||
@@ -0,0 +1,73 @@
|
||||
# IfcOpenShell - IFC toolkit and geometry engine
|
||||
# Copyright (C) 2022 Dion Moult <dion@thinkmoult.com>
|
||||
#
|
||||
# This file is part of IfcOpenShell.
|
||||
#
|
||||
# IfcOpenShell is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Lesser General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# IfcOpenShell is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Lesser General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Lesser General Public License
|
||||
# along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import ifcopenshell
|
||||
import ifcopenshell.util.element
|
||||
|
||||
|
||||
def disconnect_port(file: ifcopenshell.file, port: ifcopenshell.entity_instance) -> None:
|
||||
"""Disconnects a port from any other port
|
||||
|
||||
A port may only be connected to one other port, so the other port is not
|
||||
needed to be specified.
|
||||
|
||||
:param port: The IfcDistributionPort to disconnect.
|
||||
:return: None
|
||||
|
||||
Example:
|
||||
|
||||
.. code:: python
|
||||
|
||||
# A completely empty distribution system
|
||||
system = ifcopenshell.api.system.add_system(model)
|
||||
|
||||
# Create a duct and a 90 degree bend fitting
|
||||
duct = ifcopenshell.api.root.create_entity(model,
|
||||
ifc_class="IfcDuctSegment", predefined_type="RIGIDSEGMENT")
|
||||
fitting = ifcopenshell.api.root.create_entity(model,
|
||||
ifc_class="IfcDuctFitting", predefined_type="BEND")
|
||||
|
||||
# The duct and fitting is part of the system
|
||||
ifcopenshell.api.system.assign_system(model, products=[duct], system=system)
|
||||
ifcopenshell.api.system.assign_system(model, products=[fitting], system=system)
|
||||
|
||||
# Create 2 ports, one for either end of both the duct and fitting.
|
||||
duct_port1 = ifcopenshell.api.system.add_port(model, element=duct)
|
||||
duct_port2 = ifcopenshell.api.system.add_port(model, element=duct)
|
||||
fitting_port1 = ifcopenshell.api.system.add_port(model, element=fitting)
|
||||
fitting_port2 = ifcopenshell.api.system.add_port(model, element=fitting)
|
||||
|
||||
# Connect the duct and fitting together. At this point, we have not
|
||||
# yet determined the direction of the flow, so we leave direction as
|
||||
# NOTDEFINED.
|
||||
ifcopenshell.api.system.connect_port(model, port1=duct_port2, port2=fitting_port1)
|
||||
|
||||
# Disconnect the port. note we could've equally disconnected
|
||||
# fitting_port1 instead of duct_port2
|
||||
ifcopenshell.api.system.disconnect_port(model, port=duct_port2)
|
||||
"""
|
||||
rels = port.ConnectedTo or ()
|
||||
rels += port.ConnectedFrom or ()
|
||||
|
||||
for rel in rels:
|
||||
rel.RelatingPort.FlowDirection = None
|
||||
rel.RelatedPort.FlowDirection = None
|
||||
history = rel.OwnerHistory
|
||||
file.remove(rel)
|
||||
if history:
|
||||
ifcopenshell.util.element.remove_deep2(file, history)
|
||||
@@ -0,0 +1,44 @@
|
||||
# IfcOpenShell - IFC toolkit and geometry engine
|
||||
# Copyright (C) 2021 Dion Moult <dion@thinkmoult.com>
|
||||
#
|
||||
# This file is part of IfcOpenShell.
|
||||
#
|
||||
# IfcOpenShell is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Lesser General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# IfcOpenShell is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Lesser General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Lesser General Public License
|
||||
# along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
|
||||
from typing import Any
|
||||
|
||||
import ifcopenshell
|
||||
|
||||
|
||||
def edit_system(file: ifcopenshell.file, system: ifcopenshell.entity_instance, attributes: dict[str, Any]) -> None:
|
||||
"""Edits the attributes of an IfcSystem
|
||||
|
||||
For more information about the attributes and data types of an
|
||||
IfcSystem, consult the IFC documentation.
|
||||
|
||||
:param system: The IfcSystem entity you want to edit
|
||||
:param attributes: a dictionary of attribute names and values.
|
||||
:return: None
|
||||
|
||||
Example:
|
||||
|
||||
.. code:: python
|
||||
|
||||
# A completely empty distribution system
|
||||
system = ifcopenshell.api.system.add_system(model)
|
||||
|
||||
# Change the name of the system to "HW" for Hot Water
|
||||
ifcopenshell.api.system.edit_system(model, system=system, attributes={"Name": "HW"})
|
||||
"""
|
||||
for name, value in attributes.items():
|
||||
setattr(system, name, value)
|
||||
@@ -0,0 +1,67 @@
|
||||
# IfcOpenShell - IFC toolkit and geometry engine
|
||||
# Copyright (C) 2021 Dion Moult <dion@thinkmoult.com>
|
||||
#
|
||||
# This file is part of IfcOpenShell.
|
||||
#
|
||||
# IfcOpenShell is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Lesser General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# IfcOpenShell is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Lesser General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Lesser General Public License
|
||||
# along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import ifcopenshell
|
||||
import ifcopenshell.api.pset
|
||||
import ifcopenshell.util.element
|
||||
|
||||
|
||||
def remove_system(file: ifcopenshell.file, system: ifcopenshell.entity_instance) -> None:
|
||||
"""Removes a distribution system
|
||||
|
||||
All the distribution elements within the system are retained.
|
||||
|
||||
:param system: The IfcSystem to remove.
|
||||
:return: None
|
||||
|
||||
Example:
|
||||
|
||||
.. code:: python
|
||||
|
||||
# A completely empty distribution system
|
||||
system = ifcopenshell.api.system.add_system(model)
|
||||
|
||||
# Delete it.
|
||||
ifcopenshell.api.system.remove_system(model, system=system)
|
||||
"""
|
||||
for inverse_id in [i.id() for i in file.get_inverse(system)]:
|
||||
try:
|
||||
inverse = file.by_id(inverse_id)
|
||||
except:
|
||||
continue
|
||||
if inverse.is_a("IfcRelDefinesByProperties"):
|
||||
ifcopenshell.api.pset.remove_pset(
|
||||
file,
|
||||
product=system,
|
||||
pset=inverse.RelatingPropertyDefinition,
|
||||
)
|
||||
elif inverse.is_a("IfcRelAssignsToGroup"):
|
||||
if inverse.RelatingGroup == system:
|
||||
history = inverse.OwnerHistory
|
||||
file.remove(inverse)
|
||||
if history:
|
||||
ifcopenshell.util.element.remove_deep2(file, history)
|
||||
elif len(inverse.RelatedObjects) == 1:
|
||||
history = inverse.OwnerHistory
|
||||
file.remove(inverse)
|
||||
if history:
|
||||
ifcopenshell.util.element.remove_deep2(file, history)
|
||||
history = system.OwnerHistory
|
||||
file.remove(system)
|
||||
if history:
|
||||
ifcopenshell.util.element.remove_deep2(file, history)
|
||||
@@ -0,0 +1,67 @@
|
||||
# IfcOpenShell - IFC toolkit and geometry engine
|
||||
# Copyright (C) 2021 Dion Moult <dion@thinkmoult.com>
|
||||
#
|
||||
# This file is part of IfcOpenShell.
|
||||
#
|
||||
# IfcOpenShell is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Lesser General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# IfcOpenShell is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Lesser General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Lesser General Public License
|
||||
# along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import ifcopenshell
|
||||
import ifcopenshell.api.owner
|
||||
import ifcopenshell.util.element
|
||||
|
||||
|
||||
def unassign_flow_control(
|
||||
file: ifcopenshell.file,
|
||||
relating_flow_element: ifcopenshell.entity_instance,
|
||||
related_flow_control: ifcopenshell.entity_instance,
|
||||
) -> None:
|
||||
"""Unassigns flow control element from the flow element.
|
||||
|
||||
:param related_flow_control: IfcDistributionControlElement controling the
|
||||
flow element
|
||||
:param relating_flow_element: The IfcDistributionFlowElement that is being controlled
|
||||
:return: None
|
||||
|
||||
Example:
|
||||
|
||||
.. code:: python
|
||||
|
||||
# assign control to the flow element
|
||||
flow_element = file.createIfcFlowSegment()
|
||||
flow_control = file.createIfcController()
|
||||
relation = ifcopenshell.api.system.assign_flow_control(
|
||||
file, relating_control=flow_control, related_object=flow_element
|
||||
)
|
||||
|
||||
# und unassign it
|
||||
ifcopenshell.api.system.unassign_flow_control(file,
|
||||
relating_control=flow_control, related_object=flow_element
|
||||
)
|
||||
"""
|
||||
|
||||
if not related_flow_control.AssignedToFlowElement:
|
||||
return
|
||||
assignment = related_flow_control.AssignedToFlowElement[0]
|
||||
if assignment.RelatingFlowElement != relating_flow_element:
|
||||
return
|
||||
if len(assignment.RelatedControlElements) == 1:
|
||||
history = assignment.OwnerHistory
|
||||
file.remove(assignment)
|
||||
if history:
|
||||
ifcopenshell.util.element.remove_deep2(file, history)
|
||||
return
|
||||
related_flow_controls = list(assignment.RelatedControlElements)
|
||||
related_flow_controls.remove(related_flow_control)
|
||||
assignment.RelatedControlElements = related_flow_controls
|
||||
ifcopenshell.api.owner.update_owner_history(file, element=assignment)
|
||||
@@ -0,0 +1,86 @@
|
||||
# IfcOpenShell - IFC toolkit and geometry engine
|
||||
# Copyright (C) 2022 Dion Moult <dion@thinkmoult.com>
|
||||
#
|
||||
# This file is part of IfcOpenShell.
|
||||
#
|
||||
# IfcOpenShell is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Lesser General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# IfcOpenShell is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Lesser General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Lesser General Public License
|
||||
# along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import ifcopenshell
|
||||
import ifcopenshell.api.owner
|
||||
import ifcopenshell.util.element
|
||||
|
||||
|
||||
def unassign_port(
|
||||
file: ifcopenshell.file, element: ifcopenshell.entity_instance, port: ifcopenshell.entity_instance
|
||||
) -> None:
|
||||
"""Unassigns a port to an element
|
||||
|
||||
Ports are typically always assigned to a distribution element, but in
|
||||
some edge cases you may want to unassign the port to create an orphaned
|
||||
port for cleaning or patchin purposes.
|
||||
|
||||
:param element: The IfcDistributionElement to unassign the port from.
|
||||
:param port: The IfcDistributionPort you want to unassign.
|
||||
:return: None
|
||||
|
||||
Example:
|
||||
|
||||
.. code:: python
|
||||
|
||||
# Create a duct
|
||||
duct = ifcopenshell.api.root.create_entity(model,
|
||||
ifc_class="IfcDuctSegment", predefined_type="RIGIDSEGMENT")
|
||||
|
||||
# Create 2 ports, one for either end.
|
||||
port1 = ifcopenshell.api.system.add_port(model, element=duct)
|
||||
port2 = ifcopenshell.api.system.add_port(model, element=duct)
|
||||
|
||||
# Unassign one port for some weird reason.
|
||||
ifcopenshell.api.system.unassign_port(model, element=duct, port=port1)
|
||||
"""
|
||||
usecase = Usecase()
|
||||
usecase.file = file
|
||||
return usecase.execute(element, port)
|
||||
|
||||
|
||||
class Usecase:
|
||||
file: ifcopenshell.file
|
||||
|
||||
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 element.IsNestedBy or []:
|
||||
if port in rel.RelatedObjects:
|
||||
if len(rel.RelatedObjects) == 1:
|
||||
history = rel.OwnerHistory
|
||||
self.file.remove(rel)
|
||||
if history:
|
||||
ifcopenshell.util.element.remove_deep2(self.file, history)
|
||||
return
|
||||
related_objects = set(rel.RelatedObjects) or set()
|
||||
related_objects.remove(port)
|
||||
rel.RelatedObjects = list(related_objects)
|
||||
ifcopenshell.api.owner.update_owner_history(self.file, element=rel)
|
||||
|
||||
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:
|
||||
ifcopenshell.util.element.remove_deep2(self.file, history)
|
||||
return
|
||||
@@ -0,0 +1,51 @@
|
||||
# IfcOpenShell - IFC toolkit and geometry engine
|
||||
# Copyright (C) 2021 Dion Moult <dion@thinkmoult.com>
|
||||
#
|
||||
# This file is part of IfcOpenShell.
|
||||
#
|
||||
# IfcOpenShell is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Lesser General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# IfcOpenShell is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Lesser General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Lesser General Public License
|
||||
# along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import ifcopenshell
|
||||
import ifcopenshell.api.group
|
||||
|
||||
|
||||
def unassign_system(
|
||||
file: ifcopenshell.file,
|
||||
products: list[ifcopenshell.entity_instance],
|
||||
system: ifcopenshell.entity_instance,
|
||||
) -> None:
|
||||
"""Unassigns list of products from a system
|
||||
|
||||
:param products: The list of IfcDistributionElements to unassign from the system.
|
||||
:param system: The IfcSystem you want to unassign the element from.
|
||||
:return: None
|
||||
|
||||
Example:
|
||||
|
||||
.. code:: python
|
||||
|
||||
# A completely empty distribution system
|
||||
system = ifcopenshell.api.system.add_system(model)
|
||||
|
||||
# Create a duct
|
||||
duct = ifcopenshell.api.root.create_entity(model,
|
||||
ifc_class="IfcDuctSegment", predefined_type="RIGIDSEGMENT")
|
||||
|
||||
# This duct is part of the system
|
||||
ifcopenshell.api.system.assign_system(model, products=[duct], system=system)
|
||||
|
||||
# Not anymore!
|
||||
ifcopenshell.api.system.unassign_system(model, products=[duct], system=system)
|
||||
"""
|
||||
ifcopenshell.api.group.unassign_group(file, products=products, group=system)
|
||||
Reference in New Issue
Block a user