| name | ifcos-impl-mep |
| description | 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.
|
| license | MIT |
| compatibility | Designed for Claude Code. Requires IfcOpenShell Python library. |
| metadata | {"author":"OpenAEC-Foundation","version":"1.0"} |
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)
import ifcopenshell
import ifcopenshell.api
model = ifcopenshell.api.run("project.create_file", version="IFC4")
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)
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"})
ahu = ifcopenshell.api.run(, model,
ifc_class=, name=)
duct1 = ifcopenshell.api.run(, model,
ifc_class=, name=)
duct2 = ifcopenshell.api.run(, model,
ifc_class=, name=)
terminal = ifcopenshell.api.run(, model,
ifc_class=, name=)
ifcopenshell.api.run(, model,
products=[ahu, duct1, duct2, terminal], relating_structure=storey)
ahu_out = ifcopenshell.api.run(, model, element=ahu)
d1_in = ifcopenshell.api.run(, model, element=duct1)
d1_out = ifcopenshell.api.run(, model, element=duct1)
d2_in = ifcopenshell.api.run(, model, element=duct2)
d2_out = ifcopenshell.api.run(, model, element=duct2)
term_in = ifcopenshell.api.run(, model, element=terminal)
ifcopenshell.api.run(, model,
port1=ahu_out, port2=d1_in, direction=)
ifcopenshell.api.run(, model,
port1=d1_out, port2=d2_in, direction=)
ifcopenshell.api.run(, model,
port1=d2_out, port2=term_in, direction=)
ifcopenshell.api.run(, model,
products=[ahu, duct1, duct2, terminal], system=hvac)
Pattern 2: Piping Network with Fittings
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"})
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")
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)
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")
ifcopenshell.api.run(, model,
relating_flow_element=pipe1, related_flow_control=valve)
ifcopenshell.api.run(, model,
products=[pipe1, elbow, pipe2, valve], system=plumbing)
Pattern 3: Tee Fitting (3 Ports)
tee = ifcopenshell.api.run("root.create_entity", model,
ifc_class="IfcPipeFitting", name="HWS-T01")
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)
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)
model = ifcopenshell.api.run("project.create_file", version="IFC2X3")
system = ifcopenshell.api.run("system.add_system", model,
ifc_class="IfcSystem")
ifcopenshell.api.run("system.edit_system", model,
system=system,
attributes={"Name": "Heating System"})
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 |
|
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)
import ifcopenshell.util.system
elements = ifcopenshell.util.system.get_system_elements(hvac)
systems = ifcopenshell.util.system.get_element_systems(duct1)
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")
downstream = ifcopenshell.util.system.get_connected_to(duct1)
upstream = ifcopenshell.util.system.get_connected_from(duct1)
owner = ifcopenshell.util.system.get_port_element(d1_out)
partner = ifcopenshell.util.system.get_connected_port(d1_out)
MEP Property Sets
Standard Property Sets for Distribution Elements
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,
"InnerDiameter": 0.313,
"OuterDiameter": 0.315,
"Length": 3.0,
})
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,
"FlowRateRange": "0.1 - 0.5",
})
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
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