SuperAPI AI Directory

ifcos-impl-mep

General & other

View repository

Updated 10-15 22:17

About this skill

MEP System Implementation Guide

Quick Reference

Critical Warnings

  • ALWAYS create ports on distribution elements BEFORE attempting to connect them. Elements without ports cannot participate in flow networks.
  • ALWAYS create at least two ports per segment element (one inlet, one outlet). Segments are flow-through elements.
  • NEVER connect two ports with the same flow direction. A SOURCE port MUST connect to a SINK port.
  • NEVER use IfcDistributionSystem in IFC2X3 models. Use ifc_class="IfcSystem" for IFC2X3 compatibility.
  • ALWAYS use ifcopenshell.api.run("system.add_system", ...) to create systems. NEVER use model.create_entity("IfcDistributionSystem") directly — it skips required relationship setup.
  • ALWAYS assign elements to a system AFTER creating port connections. The system is a logical grouping; ports define the physical topology.
  • NEVER assume system.remove_system deletes contained elements. It ONLY removes the system grouping; elements and their ports remain in the model.
  • ALWAYS use ifcopenshell.api.run("root.create_entity", ...) for MEP elements, not model.create_entity(). The API sets GlobalId, ownership, and validates predefined types.
  • ALWAYS pass products as a list to system.assign_system (v0.8+ convention).

Decision Tree: MEP Workflow

Creating an MEP system?
├── Step 1: What IFC schema version?
│   ├── IFC2X3 → Use ifc_class="IfcSystem" for add_system
│   └── IFC4 / IFC4X3 → Use ifc_class="IfcDistributionSystem" (default)
│
├── Step 2: Create the system
│   └── system.add_system → system.edit_system (set Name + PredefinedType)
│
├── Step 3: Create distribution elements
│   ├── Duct/Pipe/Cable? → IfcDuctSegment, IfcPipeSegment, IfcCableSegment
│   ├── Elbow/Tee/Junction? → IfcDuctFitting, IfcPipeFitting, IfcCableFitting
│   ├── Air terminal/Fixture? → IfcAirTerminal, IfcSanitaryTerminal
│   ├── Valve/Damper/Switch? → IfcValve, IfcDamper, IfcSwitchingDevice
│   ├── Pump/Fan? → IfcPump, IfcFan
│   ├── Boiler/Chiller? → IfcBoiler, IfcChiller
│   └── Tank/Vessel? → IfcTank
│
├── Step 4: Create ports on each element
│   └── system.add_port (per connection point)
│
├── Step 5: Connect ports between elements
│   └── system.connect_port (port1, port2, direction)
│
├── Step 6: Assign elements to system
│   └── system.assign_system (products=[...], system=system)
│
├── Step 7: Assign flow controls (optional)
│   └── system.assign_flow_control (damper→duct, valve→pipe)
│
└── Step 8: Add properties and placement
    ├── pset.add_pset / pset.edit_pset (MEP property sets)
    └── geometry (placement, representation)

Decision Tree: Which Distribution Element Class

What type of MEP element?
├── Carries flow (segments)?
│   ├── Air/gas → IfcDuctSegment
│   ├── Liquid → IfcPipeSegment
│   ├── Electrical → IfcCableSegment
│   └── Cable carrier → IfcCableCarrierSegment
│
├── Changes direction/splits (fittings)?
│   ├── Air/gas → IfcDuctFitting
│   ├── Liquid → IfcPipeFitting
│   ├── Electrical → IfcCableFitting
│   └── Cable carrier → IfcCableCarrierFitting
│
├── End point (terminals)?
│   ├── Air supply/return → IfcAirTerminal
│   ├── Plumbing fixture → IfcSanitaryTerminal
│   ├── Electrical outlet → IfcOutlet
│   ├── Light fixture → IfcLightFixture
│   ├── Fire sprinkler → IfcFireSuppressionTerminal
│   └── Communication jack → IfcCommunicationsAppliance
│
├── Controls flow (controllers)?
│   ├── Airflow → IfcDamper
│   ├── Liquid flow → IfcValve
│   ├── Electrical → IfcSwitchingDevice / IfcProtectiveDevice
│   └── Flow meter → IfcFlowMeter
│
├── Moves medium (movers)?
│   ├── Air → IfcFan
│   ├── Liquid → IfcPump
│   └── Gas → IfcCompressor
│
├── Converts energy?
│   ├── Heating → IfcBoiler
│   ├── Cooling → IfcChiller
│   ├── Heat exchange → IfcHeatExchanger
│   ├── Cooling tower → IfcCoolingTower
│   ├── Air handling → IfcAirToAirHeatRecovery / IfcUnitaryEquipment
│   └── Electrical → IfcTransformer / IfcElectricGenerator
│
├── Stores medium?
│   ├── Liquid → IfcTank
│   └── Electrical → IfcElectricFlowStorageDevice
│
├── Treats medium?
│   ├── Air filter → IfcFilter
│   └── Grease/oil trap → IfcInterceptor
│
└── Senses/monitors (control elements)?
    ├── Temperature → IfcSensor (TEMPERATURESENSOR)
    ├── Pressure → IfcSensor (PRESSURESENSOR)
    ├── Flow rate → IfcSensor (FLOWSENSOR)
    ├── Smoke → IfcSensor (SMOKESENSOR)
    └── Actuator → IfcActuator

Essential Patterns

Pattern 1: Complete HVAC System (IFC4)

# IFC4 / IFC4X3
import ifcopenshell
import ifcopenshell.api

model = ifcopenshell.api.run("project.create_file", version="IFC4")

# Bootstrap (project, units, contexts, spatial hierarchy)
project = ifcopenshell.api.run("root.create_entity", model,
    ifc_class="IfcProject", name="MEP Project")
ifcopenshell.api.run("unit.assign_unit", model)
model3d = ifcopenshell.api.run("context.add_context", model, context_type="Model")
body = ifcopenshell.api.run("context.add_context", model,
    context_type="Model", context_identifier="Body",
    target_view="MODEL_VIEW", parent=model3d)
site = ifcopenshell.api.run("root.create_entity", model,
    ifc_class="IfcSite", name="Site")
building = ifcopenshell.api.run("root.create_entity", model,
    ifc_class="IfcBuilding", name="Building A")
storey = ifcopenshell.api.run("root.create_entity", model,
    ifc_class="IfcBuildingStorey", name="Level 1")
ifcopenshell.api.run("aggregate.assign_object", model,
    products=[site], relating_object=project)
ifcopenshell.api.run("aggregate.assign_object", model,
    products=[building], relating_object=site)
ifcopenshell.api.run("aggregate.assign_object", model,
    products=[storey], relating_object=building)

# Step 1: Create distribution system
hvac = ifcopenshell.api.run("system.add_system", model,
    ifc_class="IfcDistributionSystem")
ifcopenshell.api.run("system.edit_system", model,
    system=hvac,
    attributes={"Name": "Supply Air System", "PredefinedType": "VENTILATION"})

# Step 2: Create elements
ahu = ifcopenshell.api.run("root.create_entity", model,
    ifc_class="IfcUnitaryEquipment", name="AHU-01")
duct1 = ifcopenshell.api.run("root.create_entity", model,
    ifc_class="IfcDuctSegment", name="SD-01")
duct2 = ifcopenshell.api.run("root.create_entity", model,
    ifc_class="IfcDuctSegment", name="SD-02")
terminal = ifcopenshell.api.run("root.create_entity", model,
    ifc_class="IfcAirTerminal", name="AT-01")

# Step 3: Place in spatial structure
ifcopenshell.api.run("spatial.assign_container", model,
    products=[ahu, duct1, duct2, terminal], relating_structure=storey)

# Step 4: Create ports (inlet + outlet per segment)
ahu_out = ifcopenshell.api.run("system.add_port", model, element=ahu)
d1_in = ifcopenshell.api.run("system.add_port", model, element=duct1)
d1_out = ifcopenshell.api.run("system.add_port", model, element=duct1)
d2_in = ifcopenshell.api.run("system.add_port", model, element=duct2)
d2_out = ifcopenshell.api.run("system.add_port", model, element=duct2)
term_in = ifcopenshell.api.run("system.add_port", model, element=terminal)

# Step 5: Connect ports (AHU → Duct1 → Duct2 → Terminal)
ifcopenshell.api.run("system.connect_port", model,
    port1=ahu_out, port2=d1_in, direction="SOURCE")
ifcopenshell.api.run("system.connect_port", model,
    port1=d1_out, port2=d2_in, direction="SOURCE")
ifcopenshell.api.run("system.connect_port", model,
    port1=d2_out, port2=term_in, direction="SOURCE")

# Step 6: Assign all elements to the system
ifcopenshell.api.run("system.assign_system", model,
    products=[ahu, duct1, duct2, terminal], system=hvac)

Pattern 2: Piping Network with Fittings

# IFC4 / IFC4X3: requires completed bootstrap
# Create plumbing system
plumbing = ifcopenshell.api.run("system.add_system", model,
    ifc_class="IfcDistributionSystem")
ifcopenshell.api.run("system.edit_system", model,
    system=plumbing,
    attributes={"Name": "Hot Water Supply", "PredefinedType": "DOMESTICHOTWATER"})

# Create pipe elements
pipe1 = ifcopenshell.api.run("root.create_entity", model,
    ifc_class="IfcPipeSegment", name="HWS-P01")
elbow = ifcopenshell.api.run("root.create_entity", model,
    ifc_class="IfcPipeFitting", name="HWS-E01")
pipe2 = ifcopenshell.api.run("root.create_entity", model,
    ifc_class="IfcPipeSegment", name="HWS-P02")
valve = ifcopenshell.api.run("root.create_entity", model,
    ifc_class="IfcValve", name="HWS-V01")

# Ports: pipes need 2, fittings need 2+, terminals need 1
p1_in = ifcopenshell.api.run("system.add_port", model, element=pipe1)
p1_out = ifcopenshell.api.run("system.add_port", model, element=pipe1)
elb_in = ifcopenshell.api.run("system.add_port", model, element=elbow)
elb_out = ifcopenshell.api.run("system.add_port", model, element=elbow)
p2_in = ifcopenshell.api.run("system.add_port", model, element=pipe2)
p2_out = ifcopenshell.api.run("system.add_port", model, element=pipe2)

# Connect: Pipe1 → Elbow → Pipe2
ifcopenshell.api.run("system.connect_port", model,
    port1=p1_out, port2=elb_in, direction="SOURCE")
ifcopenshell.api.run("system.connect_port", model,
    port1=elb_out, port2=p2_in, direction="SOURCE")

# Assign flow control (valve controls pipe)
ifcopenshell.api.run("system.assign_flow_control", model,
    relating_flow_element=pipe1, related_flow_control=valve)

# Assign to system
ifcopenshell.api.run("system.assign_system", model,
    products=[pipe1, elbow, pipe2, valve], system=plumbing)

Pattern 3: Tee Fitting (3 Ports)

# IFC4 / IFC4X3
tee = ifcopenshell.api.run("root.create_entity", model,
    ifc_class="IfcPipeFitting", name="HWS-T01")

# Tee fittings have 3 ports: inlet, outlet-main, outlet-branch
tee_in = ifcopenshell.api.run("system.add_port", model, element=tee)
tee_out1 = ifcopenshell.api.run("system.add_port", model, element=tee)
tee_out2 = ifcopenshell.api.run("system.add_port", model, element=tee)

# Connect: upstream pipe → tee inlet
# tee outlet1 → downstream main
# tee outlet2 → downstream branch
ifcopenshell.api.run("system.connect_port", model,
    port1=upstream_pipe_out, port2=tee_in, direction="SOURCE")
ifcopenshell.api.run("system.connect_port", model,
    port1=tee_out1, port2=main_pipe_in, direction="SOURCE")
ifcopenshell.api.run("system.connect_port", model,
    port1=tee_out2, port2=branch_pipe_in, direction="SOURCE")

Pattern 4: IFC2X3 System (Backward Compatible)

# IFC2X3: use IfcSystem instead of IfcDistributionSystem
model = ifcopenshell.api.run("project.create_file", version="IFC2X3")
# ... bootstrap ...

system = ifcopenshell.api.run("system.add_system", model,
    ifc_class="IfcSystem")  # REQUIRED for IFC2X3
ifcopenshell.api.run("system.edit_system", model,
    system=system,
    attributes={"Name": "Heating System"})
# NOTE: PredefinedType is NOT available on IfcSystem in IFC2X3.
# Use ObjectType attribute for classification instead.
ifcopenshell.api.run("attribute.edit_attributes", model,
    product=system,
    attributes={"ObjectType": "HEATING"})

Distribution System Types (IFC4 / IFC4X3)

Set via PredefinedType attribute on IfcDistributionSystem:

PredefinedType Domain Description
VENTILATION HVAC Supply/return/exhaust air systems
HEATING HVAC Hot water heating, radiator circuits
COOLING HVAC Chilled water, refrigerant loops
AIRCONDITIONING HVAC Combined heating/cooling air systems
EXHAUST HVAC Kitchen/fume exhaust systems
DOMESTICHOTWATER Plumbing Hot water supply
DOMESTICCOLDWATER Plumbing Cold water supply
DRAINAGE Plumbing Gravity drainage, waste water
WASTEWATER Plumbing Waste water systems
STORMWATER Plumbing Rainwater drainage
SEWAGE Plumbing Sewer connections
RAINWATER Plumbing Rainwater collection
ELECTRICAL Electrical Power distribution
LIGHTING Electrical Lighting circuits
POWERGENERATION Electrical On-site power generation
LIGHTNINGPROTECTION Electrical Lightning protection systems
EARTHING Electrical Grounding/earthing
FIREPROTECTION Fire Sprinklers, fire suppression
SECURITY Security Access control, CCTV
COMMUNICATION Comms Data/telephone networks
DATA Comms Data networks
TV Comms Television distribution
SIGNAL Controls Signal/control systems
CONTROL Controls Building automation
GAS Fuel Natural gas distribution
FUEL Fuel Fuel supply systems
COMPRESSEDAIR Pneumatic Compressed air distribution
VACUUM Pneumatic Vacuum systems
REFRIGERATION HVAC Refrigeration systems
CHILLEDWATER HVAC Chilled water distribution
CONDENSERWATER HVAC Condenser water loops
CHEMICAL Process Chemical distribution
DISPOSAL Waste Waste disposal systems
HAZARDOUS Safety Hazardous material systems
CONVEYING Transport Material conveying
USERDEFINED Custom Set ObjectType attribute
NOTDEFINED Default Unspecified

Port Flow Directions

Direction Meaning When to Use
SOURCE Flow exits through this port Outlet ports, downstream direction
SINK Flow enters through this port Inlet ports, upstream direction
SOURCEANDSINK Bidirectional flow Reversible systems, heat exchangers
NOTDEFINED Direction not specified When flow direction is unknown

Rule: When connecting two ports, direction on connect_port describes the relationship from port1's perspective. If port1 is a SOURCE, flow goes from port1 to port2.


System Traversal (ifcopenshell.util.system)

# IFC4 / IFC4X3
import ifcopenshell.util.system

# Get all elements in a system
elements = ifcopenshell.util.system.get_system_elements(hvac)

# Get all systems an element belongs to
systems = ifcopenshell.util.system.get_element_systems(duct1)

# Get ports on an element (optionally filter by direction)
all_ports = ifcopenshell.util.system.get_ports(duct1)
outlets = ifcopenshell.util.system.get_ports(duct1, flow_direction="SOURCE")
inlets = ifcopenshell.util.system.get_ports(duct1, flow_direction="SINK")

# Traverse connections: downstream / upstream
downstream = ifcopenshell.util.system.get_connected_to(duct1)
upstream = ifcopenshell.util.system.get_connected_from(duct1)

# Get element that owns a port
owner = ifcopenshell.util.system.get_port_element(d1_out)

# Get port connected to a given port
partner = ifcopenshell.util.system.get_connected_port(d1_out)

MEP Property Sets

Standard Property Sets for Distribution Elements

# IFC4 / IFC4X3: Add MEP-specific properties
# Duct segment properties
pset = ifcopenshell.api.run("pset.add_pset", model,
    product=duct1, name="Pset_DuctSegmentTypeCommon")
ifcopenshell.api.run("pset.edit_pset", model, pset=pset, properties={
    "NominalDiameter": 0.315,      # meters
    "InnerDiameter": 0.313,
    "OuterDiameter": 0.315,
    "Length": 3.0,
})

# Pipe segment properties
pset_pipe = ifcopenshell.api.run("pset.add_pset", model,
    product=pipe1, name="Pset_PipeSegmentTypeCommon")
ifcopenshell.api.run("pset.edit_pset", model, pset=pset_pipe, properties={
    "NominalDiameter": 0.025,
    "InnerDiameter": 0.021,
    "OuterDiameter": 0.025,
    "WorkingPressure": 600000.0,    # Pascals
    "FlowRateRange": "0.1 - 0.5",  # m3/s
})

# Quantity set for a duct
qto = ifcopenshell.api.run("pset.add_qto", model,
    product=duct1, name="Qto_DuctSegmentBaseQuantities")
ifcopenshell.api.run("pset.edit_qto", model, qto=qto, properties={
    "Length": 3.0,
    "GrossCrossSectionArea": 0.078,
    "OuterSurfaceArea": 2.97,
})

Common MEP Pset Names

Element Type Property Set Key Properties
Duct segments Pset_DuctSegmentTypeCommon NominalDiameter, Length
Pipe segments Pset_PipeSegmentTypeCommon NominalDiameter, WorkingPressure
Air terminals Pset_AirTerminalTypeCommon AirFlowRateRange, NominalAirFlowRate
Valves Pset_ValveTypeCommon ValvePattern, WorkingPressure
Pumps Pset_PumpTypeCommon FlowRateRange, NominalRotationSpeed
Fans Pset_FanTypeCommon NominalAirFlowRate, NominalTotalPressure
Boilers Pset_BoilerTypeCommon NominalEnergyConsumption, WaterTemperatureRange

Version Differences

Feature IFC2X3 IFC4 IFC4X3
System class IfcSystem IfcDistributionSystem IfcDistributionSystem
PredefinedType on system Not available Full enum (37 values) Full enum + additions
Building system Not available IfcBuildingSystem IfcBuildingSystem
Port nesting IfcRelNests IfcRelNests IfcRelNests
Port connection IfcRelConnectsPorts IfcRelConnectsPorts IfcRelConnectsPorts
Flow direction enum Same Same Same

Version-Safe Pattern

# Detect schema and choose correct system class
schema = model.schema
if schema == "IFC2X3":
    system = ifcopenshell.api.run("system.add_system", model,
        ifc_class="IfcSystem")
else:
    system = ifcopenshell.api.run("system.add_system", model,
        ifc_class="IfcDistributionSystem")

Dependency

This skill depends on:

  • ifcos-syntax-api for api.run() invocation patterns, module table, and parameter conventions
  • ifcos-syntax-fileio for file creation, writing, and transaction management

Reference Links

Official Sources