From 92623b98e260f793b26be2ac4040ee03aaf2ea15 Mon Sep 17 00:00:00 2001 From: Dion Moult Date: Wed, 4 Jan 2023 16:09:26 +1100 Subject: [PATCH] Write docs for system API module --- .../ifcopenshell/api/system/add_port.py | 33 +++++++- .../ifcopenshell/api/system/add_system.py | 27 ++++++- .../ifcopenshell/api/system/assign_port.py | 38 +++++++-- .../ifcopenshell/api/system/assign_system.py | 31 ++++++-- .../ifcopenshell/api/system/connect_port.py | 78 +++++++++++++++++-- .../api/system/disconnect_port.py | 46 ++++++++++- .../ifcopenshell/api/system/edit_system.py | 27 ++++++- .../ifcopenshell/api/system/remove_system.py | 23 +++++- .../ifcopenshell/api/system/unassign_port.py | 34 ++++++-- .../api/system/unassign_system.py | 32 ++++++-- 10 files changed, 322 insertions(+), 47 deletions(-) diff --git a/src/ifcopenshell-python/ifcopenshell/api/system/add_port.py b/src/ifcopenshell-python/ifcopenshell/api/system/add_port.py index d6196231ee..ac2173edae 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/system/add_port.py +++ b/src/ifcopenshell-python/ifcopenshell/api/system/add_port.py @@ -21,13 +21,38 @@ import ifcopenshell.api class Usecase: - def __init__(self, file, **settings): + def __init__(self, file, element=None): + """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. + :type element: ifcopenshell.entity_instance.entity_instance + :return: The newly created IfcDistributionPort + :rtype: ifcopenshell.entity_instance.entity_instance + + Example:: + + # Create a duct + duct = ifcopenshell.api.run("root.create_entity", model, + ifc_class="IfcDuctSegment", predefined_type="RIGIDSEGMENT") + + # Create 2 ports, one for either end. + port1 = ifcopenshell.api.run("system.add_port", model, element=duct) + port2 = ifcopenshell.api.run("system.add_port", model, element=duct) + """ self.file = file self.settings = { - "element": None, + "element": element, } - for key, value in settings.items(): - self.settings[key] = value def execute(self): port = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcDistributionPort") diff --git a/src/ifcopenshell-python/ifcopenshell/api/system/add_system.py b/src/ifcopenshell-python/ifcopenshell/api/system/add_system.py index 41131aa8b3..f6bab8e2e8 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/system/add_system.py +++ b/src/ifcopenshell-python/ifcopenshell/api/system/add_system.py @@ -21,11 +21,30 @@ import ifcopenshell.api class Usecase: - def __init__(self, file, **settings): + def __init__(self, file, ifc_class="IfcDistributionSystem"): + """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. + :type ifc_class: str + :return: The newly created IfcSystem. + :rtype: ifcopenshell.entity_instance.entity_instance + + Example:: + + # A completely empty distribution system + system = ifcopenshell.api.run("system.add_system", model) + """ self.file = file - self.settings = {"ifc_class": "IfcSystem"} - for key, value in settings.items(): - self.settings[key] = value + self.settings = {"ifc_class": ifc_class} def execute(self): return self.file.create_entity( diff --git a/src/ifcopenshell-python/ifcopenshell/api/system/assign_port.py b/src/ifcopenshell-python/ifcopenshell/api/system/assign_port.py index 016e0a238f..123750bf58 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/system/assign_port.py +++ b/src/ifcopenshell-python/ifcopenshell/api/system/assign_port.py @@ -22,14 +22,42 @@ import ifcopenshell.util.placement class Usecase: - def __init__(self, file, **settings): + def __init__(self, file, element=None, port=None): + """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. + :type element: ifcopenshell.entity_instance.entity_instance + :param port: The IfcDistributionPort you want to assign. + :type port: ifcopenshell.entity_instance.entity_instance + :return: The IfcRelNests relationship, or the + IfcRelConnectsPortToElement for IFC2X3. + :rtype: ifcopenshell.entity_instance.entity_instance + + Example:: + + # Create a duct + duct = ifcopenshell.api.run("root.create_entity", model, + ifc_class="IfcDuctSegment", predefined_type="RIGIDSEGMENT") + + # Create 2 ports, one for either end. + port1 = ifcopenshell.api.run("system.add_port", model, element=duct) + port2 = ifcopenshell.api.run("system.add_port", model, element=duct) + + # Unassign one port for some weird reason. + ifcopenshell.api.run("system.unassign_port", model, element=duct, port=port1) + + # Reassign it back + ifcopenshell.api.run("system.assign_port", model, element=duct, port=port1) + """ self.file = file self.settings = { - "element": None, - "port": None, + "element": element, + "port": port, } - for key, value in settings.items(): - self.settings[key] = value def execute(self): if self.file.schema == "IFC2X3": diff --git a/src/ifcopenshell-python/ifcopenshell/api/system/assign_system.py b/src/ifcopenshell-python/ifcopenshell/api/system/assign_system.py index 8906a01a7d..03f947987d 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/system/assign_system.py +++ b/src/ifcopenshell-python/ifcopenshell/api/system/assign_system.py @@ -49,14 +49,35 @@ def is_assignable(product, system) -> bool: class Usecase: - def __init__(self, file, **settings): + def __init__(self, file, product=None, system=None): + """Assigns a distribution element to a system + + Note that it is not necessary to assign distribution ports to a system. + + :param product: The IfcDistributionElement to assign to the system. + :type product: ifcopenshell.entity_instance.entity_instance + :param system: The IfcSystem you want to assign the element to. + :type system: ifcopenshell.entity_instance.entity_instance + :return: The IfcRelAssignsToGroup relationship + :rtype: ifcopenshell.entity_instance.entity_instance + + Example:: + + # A completely empty distribution system + system = ifcopenshell.api.run("system.add_system", model) + + # Create a duct + duct = ifcopenshell.api.run("root.create_entity", model, + ifc_class="IfcDuctSegment", predefined_type="RIGIDSEGMENT") + + # This duct is part of the system + ifcopenshell.api.run("system.assign_system", model, product=duct, system=system) + """ self.file = file self.settings = { - "product": None, - "system": None, + "product": product, + "system": system, } - for key, value in settings.items(): - self.settings[key] = value def execute(self): system = self.settings["system"] diff --git a/src/ifcopenshell-python/ifcopenshell/api/system/connect_port.py b/src/ifcopenshell-python/ifcopenshell/api/system/connect_port.py index 79af2319e8..1fd563bc3e 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/system/connect_port.py +++ b/src/ifcopenshell-python/ifcopenshell/api/system/connect_port.py @@ -21,16 +21,80 @@ import ifcopenshell.api class Usecase: - def __init__(self, file, **settings): + def __init__(self, file, port1=None, port2=None, direction="NOTDEFINED", element=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. + :type port1: ifcopenshell.entity_instance.entity_instance + :param port2: The port of the second distribution element to connect. + :type port2: ifcopenshell.entity_instance.entity_instance + :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. + :type direction: str + :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. + :type element: ifcopenshell.entity_instance.entity_instance + + Example:: + + # A completely empty distribution system + system = ifcopenshell.api.run("system.add_system", model) + + # Create a duct and a 90 degree bend fitting + duct = ifcopenshell.api.run("root.create_entity", model, + ifc_class="IfcDuctSegment", predefined_type="RIGIDSEGMENT") + fitting = ifcopenshell.api.run("root.create_entity", model, + ifc_class="IfcDuctFitting", predefined_type="BEND") + + # The duct and fitting is part of the system + ifcopenshell.api.run("system.assign_system", model, product=duct, system=system) + ifcopenshell.api.run("system.assign_system", model, product=fitting, system=system) + + # Create 2 ports, one for either end of both the duct and fitting. + duct_port1 = ifcopenshell.api.run("system.add_port", model, element=duct) + duct_port2 = ifcopenshell.api.run("system.add_port", model, element=duct) + fitting_port1 = ifcopenshell.api.run("system.add_port", model, element=duct) + fitting_port2 = ifcopenshell.api.run("system.add_port", model, element=duct) + + # 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.run("system.connect_port", model, port1=duct_port2, port2=fitting_port1) + """ self.file = file self.settings = { - "port1": None, - "port2": None, - "direction": "NOTDEFINED", - "element": None, + "port1": port1, + "port2": port2, + "direction": direction, + "element": element, } - for key, value in settings.items(): - self.settings[key] = value def execute(self): # Note: there are a number of ambiguities with port connectivity. We diff --git a/src/ifcopenshell-python/ifcopenshell/api/system/disconnect_port.py b/src/ifcopenshell-python/ifcopenshell/api/system/disconnect_port.py index 35add85315..5087be9d41 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/system/disconnect_port.py +++ b/src/ifcopenshell-python/ifcopenshell/api/system/disconnect_port.py @@ -21,13 +21,51 @@ import ifcopenshell.api class Usecase: - def __init__(self, file, **settings): + def __init__(self, file, port=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. + :type port: ifcopenshell.entity_instance.entity_instance + :return: None + :rtype: None + + Example:: + + # A completely empty distribution system + system = ifcopenshell.api.run("system.add_system", model) + + # Create a duct and a 90 degree bend fitting + duct = ifcopenshell.api.run("root.create_entity", model, + ifc_class="IfcDuctSegment", predefined_type="RIGIDSEGMENT") + fitting = ifcopenshell.api.run("root.create_entity", model, + ifc_class="IfcDuctFitting", predefined_type="BEND") + + # The duct and fitting is part of the system + ifcopenshell.api.run("system.assign_system", model, product=duct, system=system) + ifcopenshell.api.run("system.assign_system", model, product=fitting, system=system) + + # Create 2 ports, one for either end of both the duct and fitting. + duct_port1 = ifcopenshell.api.run("system.add_port", model, element=duct) + duct_port2 = ifcopenshell.api.run("system.add_port", model, element=duct) + fitting_port1 = ifcopenshell.api.run("system.add_port", model, element=duct) + fitting_port2 = ifcopenshell.api.run("system.add_port", model, element=duct) + + # 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.run("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.run("system.disconnect_port", model, port=duct_port2) + """ self.file = file self.settings = { - "port": None, + "port": port, } - for key, value in settings.items(): - self.settings[key] = value def execute(self): rels = self.settings["port"].ConnectedTo or [] diff --git a/src/ifcopenshell-python/ifcopenshell/api/system/edit_system.py b/src/ifcopenshell-python/ifcopenshell/api/system/edit_system.py index 3e36bc959b..d5af6f1875 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/system/edit_system.py +++ b/src/ifcopenshell-python/ifcopenshell/api/system/edit_system.py @@ -18,11 +18,30 @@ class Usecase: - def __init__(self, file, **settings): + def __init__(self, file, system=None, attributes=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 + :type system: ifcopenshell.entity_instance.entity_instance + :param attributes: a dictionary of attribute names and values. + :type attributes: dict, optional + :return: None + :rtype: None + + Example:: + + # A completely empty distribution system + system = ifcopenshell.api.run("system.add_system", model) + + # Change the name of the system to "HW" for Hot Water + ifcopenshell.api.run("system.edit_system", model, system=system, attributes={"Name": "HW"}) + """ + self.file = file - self.settings = {"system": None, "attributes": {}} - for key, value in settings.items(): - self.settings[key] = value + self.settings = {"system": system, "attributes": attributes or {}} def execute(self): for name, value in self.settings["attributes"].items(): diff --git a/src/ifcopenshell-python/ifcopenshell/api/system/remove_system.py b/src/ifcopenshell-python/ifcopenshell/api/system/remove_system.py index 6ed1076d8e..1fafb1e791 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/system/remove_system.py +++ b/src/ifcopenshell-python/ifcopenshell/api/system/remove_system.py @@ -18,11 +18,26 @@ class Usecase: - def __init__(self, file, **settings): + def __init__(self, file, system=None): + """Removes a distribution system + + All the distribution elements within the system are retained. + + :param system: The IfcSystem to remove. + :type system: ifcopenshell.entity_instance.entity_instance + :return: None + :rtype: None + + Example:: + + # A completely empty distribution system + system = ifcopenshell.api.run("system.add_system", model) + + # Delete it. + ifcopenshell.api.run("system.remove_system", model, system=system) + """ self.file = file - self.settings = {"system": None} - for key, value in settings.items(): - self.settings[key] = value + self.settings = {"system": system} def execute(self): for rel in self.settings["system"].IsGroupedBy or []: diff --git a/src/ifcopenshell-python/ifcopenshell/api/system/unassign_port.py b/src/ifcopenshell-python/ifcopenshell/api/system/unassign_port.py index 96b3896729..d46a08d9e8 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/system/unassign_port.py +++ b/src/ifcopenshell-python/ifcopenshell/api/system/unassign_port.py @@ -21,14 +21,38 @@ import ifcopenshell.api class Usecase: - def __init__(self, file, **settings): + def __init__(self, file, element=None, port=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. + :type element: ifcopenshell.entity_instance.entity_instance + :param port: The IfcDistributionPort you want to unassign. + :type port: ifcopenshell.entity_instance.entity_instance + :return: None + :rtype: None + + Example:: + + # Create a duct + duct = ifcopenshell.api.run("root.create_entity", model, + ifc_class="IfcDuctSegment", predefined_type="RIGIDSEGMENT") + + # Create 2 ports, one for either end. + port1 = ifcopenshell.api.run("system.add_port", model, element=duct) + port2 = ifcopenshell.api.run("system.add_port", model, element=duct) + + # Unassign one port for some weird reason. + ifcopenshell.api.run("system.unassign_port", model, element=duct, port=port1) + """ self.file = file self.settings = { - "element": None, - "port": None, + "element": element, + "port": port, } - for key, value in settings.items(): - self.settings[key] = value def execute(self): if self.file.schema == "IFC2X3": diff --git a/src/ifcopenshell-python/ifcopenshell/api/system/unassign_system.py b/src/ifcopenshell-python/ifcopenshell/api/system/unassign_system.py index 33a5f312fb..531cbc7947 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/system/unassign_system.py +++ b/src/ifcopenshell-python/ifcopenshell/api/system/unassign_system.py @@ -21,14 +21,36 @@ import ifcopenshell.api class Usecase: - def __init__(self, file, **settings): + def __init__(self, file, product=None, system=None): + """Unassigns a product from a system + + :param product: The IfcDistributionElement to unassign from the system. + :type product: ifcopenshell.entity_instance.entity_instance + :param system: The IfcSystem you want to unassign the element from. + :type system: ifcopenshell.entity_instance.entity_instance + :return: None + :rtype: None + + Example:: + + # A completely empty distribution system + system = ifcopenshell.api.run("system.add_system", model) + + # Create a duct + duct = ifcopenshell.api.run("root.create_entity", model, + ifc_class="IfcDuctSegment", predefined_type="RIGIDSEGMENT") + + # This duct is part of the system + ifcopenshell.api.run("system.assign_system", model, product=duct, system=system) + + # Not anymore! + ifcopenshell.api.run("system.unassign_system", model, product=duct, system=system) + """ self.file = file self.settings = { - "product": None, - "system": None, + "product": product, + "system": system, } - for key, value in settings.items(): - self.settings[key] = value def execute(self): if not self.settings["system"].IsGroupedBy: