You’ve exported your model to IFC. Pipes look great. Fittings? Grey. Again. What’s going on?
This one trips up a lot of teams, because pipe fittings in Revit don’t actually carry system data, even though they look like they do. So when you rely on system-assigned materials for your IFC exports, fittings get left behind.
Revit’s built-in exporter just shrugs and spits out default grey, meanwhile you’re left scratching your head.
A lot of teams set up their Revit templates so pipe colours are based on system type. They assign an appearance material to each system, and as they model, everything looks right, pipes and fittings included. Even when using filters instead of system materials, fittings appear to match because they inherit the look visually.
The catch? Fittings don’t actually contain system data. You never notice, because in Revit views and on prints, everything looks fine. But filters don’t export, and system materials are the lowest priority in IFC. So when you finally check your IFC file, bam! Grey fittings. And it’s only then you realise something’s gone wrong.


Updating to the latest Revit IFC Exporter didn’t resolve the issue. However, Autodesk’s support article confirmed that applying materials at the system level is the lowest priority in IFC exports. To fix this, we need to explicitly assign the material using the IfcSingleMaterialOverride parameter.
The Fix
You need to bypass Revit’s assumptions and assign the material directly using the IfcSingleMaterialOverride parameter. To do this, you need to:
- Create a new project parameter:
- Name: IfcSingleMaterialOverride
- Instance: Yes
- Type: Text
- Group under: Ifc Parameters
- Apply to: Pipes and Pipe Fittings
Next comes the automation so you don’t go filling out the values manually. The basics of the process is to:
- Collect all pipes and pipe fittings.
- Grab the material from the connected pipe’s system.
- Apply that material name as a string to the IfcSingleMaterialOverride parameter on each pipe and fitting.
Here’s a macro that does exactly that, no more grey ghosts in your IFC.
👇 Full C# macro below (just copy, paste, and run):
public void Override()
{
UIDocument uidoc = this.ActiveUIDocument;
Document doc = uidoc.Document;
int pipeCount = 0;
int fittingCount = 0;
// Collect all pipe elements
FilteredElementCollector pipeCollector = new FilteredElementCollector(doc);
ICollection<Element> pipes = pipeCollector
.OfCategory(BuiltInCategory.OST_PipeCurves)
.WhereElementIsNotElementType()
.ToElements();
// Collect all pipe fitting elements
FilteredElementCollector fittingCollector = new FilteredElementCollector(doc);
ICollection<Element> pipeFittings = fittingCollector
.OfCategory(BuiltInCategory.OST_PipeFitting)
.WhereElementIsNotElementType()
.ToElements();
using (Transaction trans = new Transaction(doc, "Ifc Material Override"))
{
trans.Start();
// Process pipe fittings
foreach (Element fitting in pipeFittings)
{
FamilyInstance pipeFitting = fitting as FamilyInstance;
if (pipeFitting != null && pipeFitting.MEPModel != null)
{
ConnectorSet connectors = pipeFitting.MEPModel.ConnectorManager.Connectors;
foreach (Connector connector in connectors)
{
MEPSystem system = connector.MEPSystem;
if (system != null)
{
ElementId systemTypeId = system.GetTypeId();
Element systemType = doc.GetElement(systemTypeId);
Parameter materialParam = systemType.LookupParameter("Material");
if (materialParam != null && materialParam.HasValue)
{
ElementId matId = materialParam.AsElementId();
Material mat = doc.GetElement(matId) as Material;
string materialName = mat != null ? mat.Name : "";
Parameter ifcOverrideParam = pipeFitting.LookupParameter("IfcSingleMaterialOverride");
if (ifcOverrideParam != null && !ifcOverrideParam.IsReadOnly)
{
ifcOverrideParam.Set(materialName);
}
}
break; // Found a system, no need to check other connectors
}
}
}
fittingCount++;
}
// Process pipes
foreach (Element pipe in pipes)
{
Pipe pipeElement = pipe as Pipe;
if (pipeElement != null && pipeElement.MEPSystem != null)
{
MEPSystem system = pipeElement.MEPSystem;
ElementId systemTypeId = system.GetTypeId();
Element systemType = doc.GetElement(systemTypeId);
Parameter materialParam = systemType.LookupParameter("Material");
if (materialParam != null && materialParam.HasValue)
{
ElementId matId = materialParam.AsElementId();
Material mat = doc.GetElement(matId) as Material;
string materialName = mat != null ? mat.Name : "";
Parameter ifcOverrideParam = pipeElement.LookupParameter("IfcSingleMaterialOverride");
if (ifcOverrideParam != null && !ifcOverrideParam.IsReadOnly)
{
ifcOverrideParam.Set(materialName);
}
}
}
pipeCount++;
}
trans.Commit();
}
TaskDialog.Show("Ifc Material Override", "Overridden " + pipeCount + " pipes" + Environment.NewLine + "Overridden " + fittingCount + " pipe fittings");
}
Code language: PHP (php)
⚙️ How It Works
✅ Pipes get their material from their MEP system type
✅ Fittings borrow the system data from a connected pipe
✅ All elements get the correct material applied to the IFC override
Set-and-forget. One macro, problem solved.
Relying on system-assigned materials works great until it doesn’t. This method gives you complete control and ensures fittings are never overlooked again. No more grey IFC exports. No more QA headaches.
If you’re using filters or view templates to colour systems, that’s actually my preferred method, and that’s fine for documentation, but it means nothing for your IFC.
To control IFC colour, you need to control materials.
Let me know if this approach solves your issue, or if you’ve hacked together another method that works better. Always keen to hear how others are handling dodgy exports.
No Comments