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
- API Method Signatures -- Complete signatures for all 12 system API functions
- Working Code Examples -- End-to-end MEP system examples
- Anti-Patterns -- Common MEP modeling mistakes and how to avoid them
Official Sources
1---2name: ifcos-impl-mep3description: Use when modeling MEP (Mechanical, Electrical, Plumbing) systems in IFC -- HVAC, piping, electrical circuits, distribution elements, ports, and connections. Prevents the common mistake of not creating ports for flow connections between elements. Covers IfcSystem, IfcDistributionElement, flow segments, fittings, and MEP property sets. Keywords: MEP, HVAC, piping, electrical, IfcSystem, IfcDistributionElement, port, connection, flow segment, fitting, mechanical, plumbing, duct, ventilation, connect pipes.4license: MIT5---67# MEP System Implementation Guide89## Quick Reference1011### Critical Warnings1213- **ALWAYS** create ports on distribution elements BEFORE attempting to connect them. Elements without ports cannot participate in flow networks.14- **ALWAYS** create at least two ports per segment element (one inlet, one outlet). Segments are flow-through elements.15- **NEVER** connect two ports with the same flow direction. A `SOURCE` port MUST connect to a `SINK` port.16- **NEVER** use `IfcDistributionSystem` in IFC2X3 models. Use `ifc_class="IfcSystem"` for IFC2X3 compatibility.17- **ALWAYS** use `ifcopenshell.api.run("system.add_system", ...)` to create systems. NEVER use `model.create_entity("IfcDistributionSystem")` directly — it skips required relationship setup.18- **ALWAYS** assign elements to a system AFTER creating port connections. The system is a logical grouping; ports define the physical topology.19- **NEVER** assume `system.remove_system` deletes contained elements. It ONLY removes the system grouping; elements and their ports remain in the model.20- **ALWAYS** use `ifcopenshell.api.run("root.create_entity", ...)` for MEP elements, not `model.create_entity()`. The API sets GlobalId, ownership, and validates predefined types.21- **ALWAYS** pass `products` as a **list** to `system.assign_system` (v0.8+ convention).2223### Decision Tree: MEP Workflow2425```26Creating an MEP system?27├── Step 1: What IFC schema version?28│ ├── IFC2X3 → Use ifc_class="IfcSystem" for add_system29│ └── IFC4 / IFC4X3 → Use ifc_class="IfcDistributionSystem" (default)30│31├── Step 2: Create the system32│ └── system.add_system → system.edit_system (set Name + PredefinedType)33│34├── Step 3: Create distribution elements35│ ├── Duct/Pipe/Cable? → IfcDuctSegment, IfcPipeSegment, IfcCableSegment36│ ├── Elbow/Tee/Junction? → IfcDuctFitting, IfcPipeFitting, IfcCableFitting37│ ├── Air terminal/Fixture? → IfcAirTerminal, IfcSanitaryTerminal38│ ├── Valve/Damper/Switch? → IfcValve, IfcDamper, IfcSwitchingDevice39│ ├── Pump/Fan? → IfcPump, IfcFan40│ ├── Boiler/Chiller? → IfcBoiler, IfcChiller41│ └── Tank/Vessel? → IfcTank42│43├── Step 4: Create ports on each element44│ └── system.add_port (per connection point)45│46├── Step 5: Connect ports between elements47│ └── system.connect_port (port1, port2, direction)48│49├── Step 6: Assign elements to system50│ └── system.assign_system (products=[...], system=system)51│52├── Step 7: Assign flow controls (optional)53│ └── system.assign_flow_control (damper→duct, valve→pipe)54│55└── Step 8: Add properties and placement56 ├── pset.add_pset / pset.edit_pset (MEP property sets)57 └── geometry (placement, representation)58```5960### Decision Tree: Which Distribution Element Class6162```63What type of MEP element?64├── Carries flow (segments)?65│ ├── Air/gas → IfcDuctSegment66│ ├── Liquid → IfcPipeSegment67│ ├── Electrical → IfcCableSegment68│ └── Cable carrier → IfcCableCarrierSegment69│70├── Changes direction/splits (fittings)?71│ ├── Air/gas → IfcDuctFitting72│ ├── Liquid → IfcPipeFitting73│ ├── Electrical → IfcCableFitting74│ └── Cable carrier → IfcCableCarrierFitting75│76├── End point (terminals)?77│ ├── Air supply/return → IfcAirTerminal78│ ├── Plumbing fixture → IfcSanitaryTerminal79│ ├── Electrical outlet → IfcOutlet80│ ├── Light fixture → IfcLightFixture81│ ├── Fire sprinkler → IfcFireSuppressionTerminal82│ └── Communication jack → IfcCommunicationsAppliance83│84├── Controls flow (controllers)?85│ ├── Airflow → IfcDamper86│ ├── Liquid flow → IfcValve87│ ├── Electrical → IfcSwitchingDevice / IfcProtectiveDevice88│ └── Flow meter → IfcFlowMeter89│90├── Moves medium (movers)?91│ ├── Air → IfcFan92│ ├── Liquid → IfcPump93│ └── Gas → IfcCompressor94│95├── Converts energy?96│ ├── Heating → IfcBoiler97│ ├── Cooling → IfcChiller98│ ├── Heat exchange → IfcHeatExchanger99│ ├── Cooling tower → IfcCoolingTower100│ ├── Air handling → IfcAirToAirHeatRecovery / IfcUnitaryEquipment101│ └── Electrical → IfcTransformer / IfcElectricGenerator102│103├── Stores medium?104│ ├── Liquid → IfcTank105│ └── Electrical → IfcElectricFlowStorageDevice106│107├── Treats medium?108│ ├── Air filter → IfcFilter109│ └── Grease/oil trap → IfcInterceptor110│111└── Senses/monitors (control elements)?112 ├── Temperature → IfcSensor (TEMPERATURESENSOR)113 ├── Pressure → IfcSensor (PRESSURESENSOR)114 ├── Flow rate → IfcSensor (FLOWSENSOR)115 ├── Smoke → IfcSensor (SMOKESENSOR)116 └── Actuator → IfcActuator117```118119---120121## Essential Patterns122123### Pattern 1: Complete HVAC System (IFC4)124125```python126# IFC4 / IFC4X3127import ifcopenshell128import ifcopenshell.api129130model = ifcopenshell.api.run("project.create_file", version="IFC4")131132# Bootstrap (project, units, contexts, spatial hierarchy)133project = ifcopenshell.api.run("root.create_entity", model,134 ifc_class="IfcProject", name="MEP Project")135ifcopenshell.api.run("unit.assign_unit", model)136model3d = ifcopenshell.api.run("context.add_context", model, context_type="Model")137body = ifcopenshell.api.run("context.add_context", model,138 context_type="Model", context_identifier="Body",139 target_view="MODEL_VIEW", parent=model3d)140site = ifcopenshell.api.run("root.create_entity", model,141 ifc_class="IfcSite", name="Site")142building = ifcopenshell.api.run("root.create_entity", model,143 ifc_class="IfcBuilding", name="Building A")144storey = ifcopenshell.api.run("root.create_entity", model,145 ifc_class="IfcBuildingStorey", name="Level 1")146ifcopenshell.api.run("aggregate.assign_object", model,147 products=[site], relating_object=project)148ifcopenshell.api.run("aggregate.assign_object", model,149 products=[building], relating_object=site)150ifcopenshell.api.run("aggregate.assign_object", model,151 products=[storey], relating_object=building)152153# Step 1: Create distribution system154hvac = ifcopenshell.api.run("system.add_system", model,155 ifc_class="IfcDistributionSystem")156ifcopenshell.api.run("system.edit_system", model,157 system=hvac,158 attributes={"Name": "Supply Air System", "PredefinedType": "VENTILATION"})159160# Step 2: Create elements161ahu = ifcopenshell.api.run("root.create_entity", model,162 ifc_class="IfcUnitaryEquipment", name="AHU-01")163duct1 = ifcopenshell.api.run("root.create_entity", model,164 ifc_class="IfcDuctSegment", name="SD-01")165duct2 = ifcopenshell.api.run("root.create_entity", model,166 ifc_class="IfcDuctSegment", name="SD-02")167terminal = ifcopenshell.api.run("root.create_entity", model,168 ifc_class="IfcAirTerminal", name="AT-01")169170# Step 3: Place in spatial structure171ifcopenshell.api.run("spatial.assign_container", model,172 products=[ahu, duct1, duct2, terminal], relating_structure=storey)173174# Step 4: Create ports (inlet + outlet per segment)175ahu_out = ifcopenshell.api.run("system.add_port", model, element=ahu)176d1_in = ifcopenshell.api.run("system.add_port", model, element=duct1)177d1_out = ifcopenshell.api.run("system.add_port", model, element=duct1)178d2_in = ifcopenshell.api.run("system.add_port", model, element=duct2)179d2_out = ifcopenshell.api.run("system.add_port", model, element=duct2)180term_in = ifcopenshell.api.run("system.add_port", model, element=terminal)181182# Step 5: Connect ports (AHU → Duct1 → Duct2 → Terminal)183ifcopenshell.api.run("system.connect_port", model,184 port1=ahu_out, port2=d1_in, direction="SOURCE")185ifcopenshell.api.run("system.connect_port", model,186 port1=d1_out, port2=d2_in, direction="SOURCE")187ifcopenshell.api.run("system.connect_port", model,188 port1=d2_out, port2=term_in, direction="SOURCE")189190# Step 6: Assign all elements to the system191ifcopenshell.api.run("system.assign_system", model,192 products=[ahu, duct1, duct2, terminal], system=hvac)193```194195### Pattern 2: Piping Network with Fittings196197```python198# IFC4 / IFC4X3: requires completed bootstrap199# Create plumbing system200plumbing = ifcopenshell.api.run("system.add_system", model,201 ifc_class="IfcDistributionSystem")202ifcopenshell.api.run("system.edit_system", model,203 system=plumbing,204 attributes={"Name": "Hot Water Supply", "PredefinedType": "DOMESTICHOTWATER"})205206# Create pipe elements207pipe1 = ifcopenshell.api.run("root.create_entity", model,208 ifc_class="IfcPipeSegment", name="HWS-P01")209elbow = ifcopenshell.api.run("root.create_entity", model,210 ifc_class="IfcPipeFitting", name="HWS-E01")211pipe2 = ifcopenshell.api.run("root.create_entity", model,212 ifc_class="IfcPipeSegment", name="HWS-P02")213valve = ifcopenshell.api.run("root.create_entity", model,214 ifc_class="IfcValve", name="HWS-V01")215216# Ports: pipes need 2, fittings need 2+, terminals need 1217p1_in = ifcopenshell.api.run("system.add_port", model, element=pipe1)218p1_out = ifcopenshell.api.run("system.add_port", model, element=pipe1)219elb_in = ifcopenshell.api.run("system.add_port", model, element=elbow)220elb_out = ifcopenshell.api.run("system.add_port", model, element=elbow)221p2_in = ifcopenshell.api.run("system.add_port", model, element=pipe2)222p2_out = ifcopenshell.api.run("system.add_port", model, element=pipe2)223224# Connect: Pipe1 → Elbow → Pipe2225ifcopenshell.api.run("system.connect_port", model,226 port1=p1_out, port2=elb_in, direction="SOURCE")227ifcopenshell.api.run("system.connect_port", model,228 port1=elb_out, port2=p2_in, direction="SOURCE")229230# Assign flow control (valve controls pipe)231ifcopenshell.api.run("system.assign_flow_control", model,232 relating_flow_element=pipe1, related_flow_control=valve)233234# Assign to system235ifcopenshell.api.run("system.assign_system", model,236 products=[pipe1, elbow, pipe2, valve], system=plumbing)237```238239### Pattern 3: Tee Fitting (3 Ports)240241```python242# IFC4 / IFC4X3243tee = ifcopenshell.api.run("root.create_entity", model,244 ifc_class="IfcPipeFitting", name="HWS-T01")245246# Tee fittings have 3 ports: inlet, outlet-main, outlet-branch247tee_in = ifcopenshell.api.run("system.add_port", model, element=tee)248tee_out1 = ifcopenshell.api.run("system.add_port", model, element=tee)249tee_out2 = ifcopenshell.api.run("system.add_port", model, element=tee)250251# Connect: upstream pipe → tee inlet252# tee outlet1 → downstream main253# tee outlet2 → downstream branch254ifcopenshell.api.run("system.connect_port", model,255 port1=upstream_pipe_out, port2=tee_in, direction="SOURCE")256ifcopenshell.api.run("system.connect_port", model,257 port1=tee_out1, port2=main_pipe_in, direction="SOURCE")258ifcopenshell.api.run("system.connect_port", model,259 port1=tee_out2, port2=branch_pipe_in, direction="SOURCE")260```261262### Pattern 4: IFC2X3 System (Backward Compatible)263264```python265# IFC2X3: use IfcSystem instead of IfcDistributionSystem266model = ifcopenshell.api.run("project.create_file", version="IFC2X3")267# ... bootstrap ...268269system = ifcopenshell.api.run("system.add_system", model,270 ifc_class="IfcSystem") # REQUIRED for IFC2X3271ifcopenshell.api.run("system.edit_system", model,272 system=system,273 attributes={"Name": "Heating System"})274# NOTE: PredefinedType is NOT available on IfcSystem in IFC2X3.275# Use ObjectType attribute for classification instead.276ifcopenshell.api.run("attribute.edit_attributes", model,277 product=system,278 attributes={"ObjectType": "HEATING"})279```280281---282283## Distribution System Types (IFC4 / IFC4X3)284285Set via `PredefinedType` attribute on `IfcDistributionSystem`:286287| PredefinedType | Domain | Description |288|----------------|--------|-------------|289| `VENTILATION` | HVAC | Supply/return/exhaust air systems |290| `HEATING` | HVAC | Hot water heating, radiator circuits |291| `COOLING` | HVAC | Chilled water, refrigerant loops |292| `AIRCONDITIONING` | HVAC | Combined heating/cooling air systems |293| `EXHAUST` | HVAC | Kitchen/fume exhaust systems |294| `DOMESTICHOTWATER` | Plumbing | Hot water supply |295| `DOMESTICCOLDWATER` | Plumbing | Cold water supply |296| `DRAINAGE` | Plumbing | Gravity drainage, waste water |297| `WASTEWATER` | Plumbing | Waste water systems |298| `STORMWATER` | Plumbing | Rainwater drainage |299| `SEWAGE` | Plumbing | Sewer connections |300| `RAINWATER` | Plumbing | Rainwater collection |301| `ELECTRICAL` | Electrical | Power distribution |302| `LIGHTING` | Electrical | Lighting circuits |303| `POWERGENERATION` | Electrical | On-site power generation |304| `LIGHTNINGPROTECTION` | Electrical | Lightning protection systems |305| `EARTHING` | Electrical | Grounding/earthing |306| `FIREPROTECTION` | Fire | Sprinklers, fire suppression |307| `SECURITY` | Security | Access control, CCTV |308| `COMMUNICATION` | Comms | Data/telephone networks |309| `DATA` | Comms | Data networks |310| `TV` | Comms | Television distribution |311| `SIGNAL` | Controls | Signal/control systems |312| `CONTROL` | Controls | Building automation |313| `GAS` | Fuel | Natural gas distribution |314| `FUEL` | Fuel | Fuel supply systems |315| `COMPRESSEDAIR` | Pneumatic | Compressed air distribution |316| `VACUUM` | Pneumatic | Vacuum systems |317| `REFRIGERATION` | HVAC | Refrigeration systems |318| `CHILLEDWATER` | HVAC | Chilled water distribution |319| `CONDENSERWATER` | HVAC | Condenser water loops |320| `CHEMICAL` | Process | Chemical distribution |321| `DISPOSAL` | Waste | Waste disposal systems |322| `HAZARDOUS` | Safety | Hazardous material systems |323| `CONVEYING` | Transport | Material conveying |324| `USERDEFINED` | Custom | Set ObjectType attribute |325| `NOTDEFINED` | Default | Unspecified |326327---328329## Port Flow Directions330331| Direction | Meaning | When to Use |332|-----------|---------|-------------|333| `SOURCE` | Flow exits through this port | Outlet ports, downstream direction |334| `SINK` | Flow enters through this port | Inlet ports, upstream direction |335| `SOURCEANDSINK` | Bidirectional flow | Reversible systems, heat exchangers |336| `NOTDEFINED` | Direction not specified | When flow direction is unknown |337338**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.339340---341342## System Traversal (ifcopenshell.util.system)343344```python345# IFC4 / IFC4X3346import ifcopenshell.util.system347348# Get all elements in a system349elements = ifcopenshell.util.system.get_system_elements(hvac)350351# Get all systems an element belongs to352systems = ifcopenshell.util.system.get_element_systems(duct1)353354# Get ports on an element (optionally filter by direction)355all_ports = ifcopenshell.util.system.get_ports(duct1)356outlets = ifcopenshell.util.system.get_ports(duct1, flow_direction="SOURCE")357inlets = ifcopenshell.util.system.get_ports(duct1, flow_direction="SINK")358359# Traverse connections: downstream / upstream360downstream = ifcopenshell.util.system.get_connected_to(duct1)361upstream = ifcopenshell.util.system.get_connected_from(duct1)362363# Get element that owns a port364owner = ifcopenshell.util.system.get_port_element(d1_out)365366# Get port connected to a given port367partner = ifcopenshell.util.system.get_connected_port(d1_out)368```369370---371372## MEP Property Sets373374### Standard Property Sets for Distribution Elements375376```python377# IFC4 / IFC4X3: Add MEP-specific properties378# Duct segment properties379pset = ifcopenshell.api.run("pset.add_pset", model,380 product=duct1, name="Pset_DuctSegmentTypeCommon")381ifcopenshell.api.run("pset.edit_pset", model, pset=pset, properties={382 "NominalDiameter": 0.315, # meters383 "InnerDiameter": 0.313,384 "OuterDiameter": 0.315,385 "Length": 3.0,386})387388# Pipe segment properties389pset_pipe = ifcopenshell.api.run("pset.add_pset", model,390 product=pipe1, name="Pset_PipeSegmentTypeCommon")391ifcopenshell.api.run("pset.edit_pset", model, pset=pset_pipe, properties={392 "NominalDiameter": 0.025,393 "InnerDiameter": 0.021,394 "OuterDiameter": 0.025,395 "WorkingPressure": 600000.0, # Pascals396 "FlowRateRange": "0.1 - 0.5", # m3/s397})398399# Quantity set for a duct400qto = ifcopenshell.api.run("pset.add_qto", model,401 product=duct1, name="Qto_DuctSegmentBaseQuantities")402ifcopenshell.api.run("pset.edit_qto", model, qto=qto, properties={403 "Length": 3.0,404 "GrossCrossSectionArea": 0.078,405 "OuterSurfaceArea": 2.97,406})407```408409### Common MEP Pset Names410411| Element Type | Property Set | Key Properties |412|-------------|-------------|----------------|413| Duct segments | `Pset_DuctSegmentTypeCommon` | NominalDiameter, Length |414| Pipe segments | `Pset_PipeSegmentTypeCommon` | NominalDiameter, WorkingPressure |415| Air terminals | `Pset_AirTerminalTypeCommon` | AirFlowRateRange, NominalAirFlowRate |416| Valves | `Pset_ValveTypeCommon` | ValvePattern, WorkingPressure |417| Pumps | `Pset_PumpTypeCommon` | FlowRateRange, NominalRotationSpeed |418| Fans | `Pset_FanTypeCommon` | NominalAirFlowRate, NominalTotalPressure |419| Boilers | `Pset_BoilerTypeCommon` | NominalEnergyConsumption, WaterTemperatureRange |420421---422423## Version Differences424425| Feature | IFC2X3 | IFC4 | IFC4X3 |426|---------|--------|------|--------|427| System class | `IfcSystem` | `IfcDistributionSystem` | `IfcDistributionSystem` |428| PredefinedType on system | Not available | Full enum (37 values) | Full enum + additions |429| Building system | Not available | `IfcBuildingSystem` | `IfcBuildingSystem` |430| Port nesting | `IfcRelNests` | `IfcRelNests` | `IfcRelNests` |431| Port connection | `IfcRelConnectsPorts` | `IfcRelConnectsPorts` | `IfcRelConnectsPorts` |432| Flow direction enum | Same | Same | Same |433434### Version-Safe Pattern435436```python437# Detect schema and choose correct system class438schema = model.schema439if schema == "IFC2X3":440 system = ifcopenshell.api.run("system.add_system", model,441 ifc_class="IfcSystem")442else:443 system = ifcopenshell.api.run("system.add_system", model,444 ifc_class="IfcDistributionSystem")445```446447---448449## Dependency450451This skill depends on:452- **ifcos-syntax-api** for `api.run()` invocation patterns, module table, and parameter conventions453- **ifcos-syntax-fileio** for file creation, writing, and transaction management454455---456457## Reference Links458459- [API Method Signatures](references/methods.md) -- Complete signatures for all 12 system API functions460- [Working Code Examples](references/examples.md) -- End-to-end MEP system examples461- [Anti-Patterns](references/anti-patterns.md) -- Common MEP modeling mistakes and how to avoid them462463### Official Sources464465- https://docs.ifcopenshell.org/autoapi/ifcopenshell/api/system/466- https://standards.buildingsmart.org/IFC/RELEASE/IFC4/ADD2_TC1/HTML/schema/ifcsharedbldgserviceelements/467- https://standards.buildingsmart.org/IFC/RELEASE/IFC4/ADD2_TC1/HTML/schema/ifchvacdomain/