🏭 Plant 3D APIintermediatePiping & Equipment

Extract Plant 3D Piping Data Programmatically

Access pipe specifications, lengths, fittings, and equipment connections in Plant 3D for material takeoffs.

3 views12/14/2025

Extract Plant 3D Piping Data Programmatically

Application: Plant 3D API
Category: Piping & Equipment
Difficulty: Intermediate
Language: C#

Problem

You need to extract piping data from Plant 3D models - such as pipe specifications, lengths, fittings, and equipment connections - for material takeoffs, clash detection reports, or integration with other systems.

Solution

Use the Plant 3D .NET API to access piping objects, read their properties, and traverse pipe runs. Plant 3D extends AutoCAD with specialized classes for process plant design.

Code Example: Extract Pipe Run Information

using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.EditorInput;
using Autodesk.AutoCAD.Runtime;
using Autodesk.ProcessPower.PnP3dObjects;
using Autodesk.ProcessPower.DataLinks;
using System.Collections.Generic;
using System.Text;

[CommandMethod("EXTRACTPIPEDATA")]
public void ExtractPipeData()
{
    Document doc = Application.DocumentManager.MdiActiveDocument;
    Database db = doc.Database;
    Editor ed = doc.Editor;
    
    using (Transaction tr = db.TransactionManager.StartTransaction())
    {
        // Get all pipes in the drawing
        TypedValue[] filterList = new TypedValue[]
        {
            new TypedValue((int)DxfCode.Start, "PIPE")
        };
        
        SelectionFilter filter = new SelectionFilter(filterList);
        PromptSelectionResult psr = ed.SelectAll(filter);
        
        if (psr.Status != PromptStatus.OK)
        {
            ed.WriteMessage("\nNo pipes found in drawing.");
            return;
        }
        
        StringBuilder report = new StringBuilder();
        report.AppendLine("PIPE DATA EXTRACTION REPORT");
        report.AppendLine("=" + new string('=', 70));
        report.AppendLine();
        
        double totalLength = 0;
        Dictionary<string, double> specLengths = new Dictionary<string, double>();
        Dictionary<string, int> specCounts = new Dictionary<string, int>();
        
        foreach (SelectedObject selObj in psr.Value)
        {
            if (selObj == null) continue;
            
            Pipe pipe = tr.GetObject(selObj.ObjectId, OpenMode.ForRead) as Pipe;
            
            if (pipe != null)
            {
                // Get pipe properties
                string pipeTag = pipe.PipeTag;
                string spec = pipe.Spec;
                double nominalDiameter = pipe.NominalDiameter;
                double length = pipe.Length;
                string material = pipe.Material;
                string schedule = pipe.Schedule;
                
                // Get start and end points
                Point3d startPoint = pipe.StartPoint;
                Point3d endPoint = pipe.EndPoint;
                
                // Get connected equipment
                string startConnection = GetConnectedEquipment(pipe.StartPort, tr);
                string endConnection = GetConnectedEquipment(pipe.EndPort, tr);
                
                // Add to report
                report.AppendLine($"Pipe Tag: {pipeTag}");
                report.AppendLine($"  Spec: {spec}");
                report.AppendLine($"  Size: {nominalDiameter}\"");
                report.AppendLine($"  Length: {length:F2} mm");
                report.AppendLine($"  Material: {material}");
                report.AppendLine($"  Schedule: {schedule}");
                report.AppendLine($"  Start: {startPoint.X:F2}, {startPoint.Y:F2}, {startPoint.Z:F2}");
                report.AppendLine($"  End: {endPoint.X:F2}, {endPoint.Y:F2}, {endPoint.Z:F2}");
                
                if (!string.IsNullOrEmpty(startConnection))
                    report.AppendLine($"  Start Connection: {startConnection}");
                if (!string.IsNullOrEmpty(endConnection))
                    report.AppendLine($"  End Connection: {endConnection}");
                
                report.AppendLine();
                
                // Accumulate totals
                totalLength += length;
                
                if (!specLengths.ContainsKey(spec))
                {
                    specLengths[spec] = 0;
                    specCounts[spec] = 0;
                }
                
                specLengths[spec] += length;
                specCounts[spec]++;
            }
        }
        
        // Add summary
        report.AppendLine("\nSUMMARY BY SPECIFICATION");
        report.AppendLine("=" + new string('=', 70));
        
        foreach (var spec in specLengths.Keys)
        {
            report.AppendLine($"{spec}:");
            report.AppendLine($"  Count: {specCounts[spec]} pipes");
            report.AppendLine($"  Total Length: {specLengths[spec]:F2} mm ({specLengths[spec] / 1000:F2} m)");
            report.AppendLine();
        }
        
        report.AppendLine($"GRAND TOTAL LENGTH: {totalLength:F2} mm ({totalLength / 1000:F2} m)");
        
        tr.Commit();
        
        // Save report
        string reportPath = @"C:\Temp\PipeDataReport.txt";
        System.IO.File.WriteAllText(reportPath, report.ToString());
        
        ed.WriteMessage($"\nReport saved to: {reportPath}");
        
        // Open report
        System.Diagnostics.Process.Start("notepad.exe", reportPath);
    }
}

private string GetConnectedEquipment(Port port, Transaction tr)
{
    if (port == null || port.ConnectedPorts.Count == 0)
        return string.Empty;
    
    foreach (ObjectId connectedPortId in port.ConnectedPorts)
    {
        Port connectedPort = tr.GetObject(connectedPortId, OpenMode.ForRead) as Port;
        
        if (connectedPort != null)
        {
            ObjectId ownerId = connectedPort.OwnerId;
            DBObject owner = tr.GetObject(ownerId, OpenMode.ForRead);
            
            // Check if connected to equipment
            if (owner is Equipment equipment)
            {
                return equipment.Tag;
            }
        }
    }
    
    return string.Empty;
}

Extract Fittings and Valves

[CommandMethod("EXTRACTFITTINGS")]
public void ExtractFittingsData()
{
    Document doc = Application.DocumentManager.MdiActiveDocument;
    Database db = doc.Database;
    Editor ed = doc.Editor;
    
    using (Transaction tr = db.TransactionManager.StartTransaction())
    {
        // Get all fittings
        TypedValue[] filterList = new TypedValue[]
        {
            new TypedValue((int)DxfCode.Start, "PIPEFITTING,VALVE")
        };
        
        SelectionFilter filter = new SelectionFilter(filterList);
        PromptSelectionResult psr = ed.SelectAll(filter);
        
        if (psr.Status != PromptStatus.OK)
        {
            ed.WriteMessage("\nNo fittings found.");
            return;
        }
        
        Dictionary<string, int> fittingCounts = new Dictionary<string, int>();
        
        foreach (SelectedObject selObj in psr.Value)
        {
            if (selObj == null) continue;
            
            PipeFitting fitting = tr.GetObject(selObj.ObjectId, OpenMode.ForRead) as PipeFitting;
            
            if (fitting != null)
            {
                string fittingType = fitting.PartSizeProperties.PartFamily;
                string size = fitting.PartSizeProperties.NominalDiameter.ToString();
                string key = $"{fittingType} - {size}\"";
                
                if (!fittingCounts.ContainsKey(key))
                    fittingCounts[key] = 0;
                
                fittingCounts[key]++;
            }
        }
        
        tr.Commit();
        
        // Display summary
        StringBuilder summary = new StringBuilder();
        summary.AppendLine("FITTINGS SUMMARY");
        summary.AppendLine("================\n");
        
        foreach (var kvp in fittingCounts.OrderBy(x => x.Key))
        {
            summary.AppendLine($"{kvp.Key}: {kvp.Value}");
        }
        
        ed.WriteMessage("\n" + summary.ToString());
    }
}

Export to Excel with Material Takeoff

using System.IO;
using OfficeOpenXml; // EPPlus library

[CommandMethod("EXPORTMTO")]
public void ExportMaterialTakeoff()
{
    Document doc = Application.DocumentManager.MdiActiveDocument;
    Database db = doc.Database;
    Editor ed = doc.Editor;
    
    using (Transaction tr = db.TransactionManager.StartTransaction())
    {
        // Collect all piping components
        List<PipeData> pipes = new List<PipeData>();
        List<FittingData> fittings = new List<FittingData>();
        
        // Get pipes
        TypedValue[] pipeFilter = new TypedValue[] { new TypedValue((int)DxfCode.Start, "PIPE") };
        PromptSelectionResult pipeSel = ed.SelectAll(new SelectionFilter(pipeFilter));
        
        if (pipeSel.Status == PromptStatus.OK)
        {
            foreach (SelectedObject selObj in pipeSel.Value)
            {
                Pipe pipe = tr.GetObject(selObj.ObjectId, OpenMode.ForRead) as Pipe;
                if (pipe != null)
                {
                    pipes.Add(new PipeData
                    {
                        Tag = pipe.PipeTag,
                        Spec = pipe.Spec,
                        Size = pipe.NominalDiameter,
                        Length = pipe.Length,
                        Material = pipe.Material,
                        Schedule = pipe.Schedule
                    });
                }
            }
        }
        
        // Get fittings
        TypedValue[] fittingFilter = new TypedValue[] { new TypedValue((int)DxfCode.Start, "PIPEFITTING") };
        PromptSelectionResult fittingSel = ed.SelectAll(new SelectionFilter(fittingFilter));
        
        if (fittingSel.Status == PromptStatus.OK)
        {
            foreach (SelectedObject selObj in fittingSel.Value)
            {
                PipeFitting fitting = tr.GetObject(selObj.ObjectId, OpenMode.ForRead) as PipeFitting;
                if (fitting != null)
                {
                    fittings.Add(new FittingData
                    {
                        Type = fitting.PartSizeProperties.PartFamily,
                        Size = fitting.PartSizeProperties.NominalDiameter,
                        Spec = fitting.Spec
                    });
                }
            }
        }
        
        tr.Commit();
        
        // Export to Excel
        string excelPath = @"C:\Temp\MaterialTakeoff.xlsx";
        ExportToExcel(pipes, fittings, excelPath);
        
        ed.WriteMessage($"\nMaterial takeoff exported to: {excelPath}");
        System.Diagnostics.Process.Start(excelPath);
    }
}

private void ExportToExcel(List<PipeData> pipes, List<FittingData> fittings, string filePath)
{
    using (ExcelPackage package = new ExcelPackage())
    {
        // Pipe sheet
        ExcelWorksheet pipeSheet = package.Workbook.Worksheets.Add("Pipes");
        pipeSheet.Cells[1, 1].Value = "Tag";
        pipeSheet.Cells[1, 2].Value = "Spec";
        pipeSheet.Cells[1, 3].Value = "Size";
        pipeSheet.Cells[1, 4].Value = "Length (mm)";
        pipeSheet.Cells[1, 5].Value = "Material";
        pipeSheet.Cells[1, 6].Value = "Schedule";
        
        int row = 2;
        foreach (var pipe in pipes)
        {
            pipeSheet.Cells[row, 1].Value = pipe.Tag;
            pipeSheet.Cells[row, 2].Value = pipe.Spec;
            pipeSheet.Cells[row, 3].Value = pipe.Size;
            pipeSheet.Cells[row, 4].Value = pipe.Length;
            pipeSheet.Cells[row, 5].Value = pipe.Material;
            pipeSheet.Cells[row, 6].Value = pipe.Schedule;
            row++;
        }
        
        // Fittings sheet
        ExcelWorksheet fittingSheet = package.Workbook.Worksheets.Add("Fittings");
        fittingSheet.Cells[1, 1].Value = "Type";
        fittingSheet.Cells[1, 2].Value = "Size";
        fittingSheet.Cells[1, 3].Value = "Spec";
        fittingSheet.Cells[1, 4].Value = "Quantity";
        
        var fittingGroups = fittings.GroupBy(f => new { f.Type, f.Size, f.Spec })
                                    .Select(g => new { g.Key, Count = g.Count() });
        
        row = 2;
        foreach (var group in fittingGroups)
        {
            fittingSheet.Cells[row, 1].Value = group.Key.Type;
            fittingSheet.Cells[row, 2].Value = group.Key.Size;
            fittingSheet.Cells[row, 3].Value = group.Key.Spec;
            fittingSheet.Cells[row, 4].Value = group.Count;
            row++;
        }
        
        // Save file
        FileInfo file = new FileInfo(filePath);
        package.SaveAs(file);
    }
}

// Helper classes
public class PipeData
{
    public string Tag { get; set; }
    public string Spec { get; set; }
    public double Size { get; set; }
    public double Length { get; set; }
    public string Material { get; set; }
    public string Schedule { get; set; }
}

public class FittingData
{
    public string Type { get; set; }
    public double Size { get; set; }
    public string Spec { get; set; }
}

Key Points

  • Plant 3D extends AutoCAD with specialized piping objects
  • Pipes have ports for connectivity information
  • Use Spec property to group components by specification
  • NominalDiameter is in inches, Length is in mm
  • Equipment connections accessed through Port objects
  • Material takeoffs require grouping and counting

Common Pitfalls

Mistake: Assuming length units

// WRONG - mixing units
double lengthInMeters = pipe.Length; // Actually in mm!

// CORRECT - convert to desired units
double lengthInMeters = pipe.Length / 1000; // mm to m
double lengthInFeet = pipe.Length / 304.8; // mm to ft

Mistake: Not checking port connections

// WRONG - may throw null reference
ObjectId connectedId = pipe.StartPort.ConnectedPorts[0];

// CORRECT - check first
if (pipe.StartPort != null && pipe.StartPort.ConnectedPorts.Count > 0)
{
    ObjectId connectedId = pipe.StartPort.ConnectedPorts[0];
}

Related Topics

  • P&ID integration
  • Isometric drawing generation
  • Pipe stress analysis integration
  • Equipment nozzle management
  • Spec-driven design

Code Example

C#
using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.EditorInput;
using Autodesk.AutoCAD.Runtime;
using Autodesk.ProcessPower.PnP3dObjects;
using Autodesk.ProcessPower.DataLinks;
using System.Collections.Generic;
using System.Text;

[CommandMethod("EXTRACTPIPEDATA")]
public void ExtractPipeData()
{
    Document doc = Application.DocumentManager.MdiActiveDocument;
    Database db = doc.Database;
    Editor ed = doc.Editor;
    
    using (Transaction tr = db.TransactionManager.StartTransaction())
    {
        // Get all pipes in the drawing
        TypedValue[] filterList = new TypedValue[]
        {
            new TypedValue((int)DxfCode.Start, "PIPE")
        };
        
        SelectionFilter filter = new SelectionFilter(filterList);
        PromptSelectionResult psr = ed.SelectAll(filter);
        
        if (psr.Status != PromptStatus.OK)
        {
            ed.WriteMessage("\nNo pipes found in drawing.");
            return;
        }
        
        StringBuilder report = new StringBuilder();
        report.AppendLine("PIPE DATA EXTRACTION REPORT");
        report.AppendLine("=" + new string('=', 70));
        report.AppendLine();
        
        double totalLength = 0;
        Dictionary<string, double> specLengths = new Dictionary<string, double>();
        Dictionary<string, int> specCounts = new Dictionary<string, int>();
        
        foreach (SelectedObject selObj in psr.Value)
        {
            if (selObj == null) continue;
            
            Pipe pipe = tr.GetObject(selObj.ObjectId, OpenMode.ForRead) as Pipe;
            
            if (pipe != null)
            {
                // Get pipe properties
                string pipeTag = pipe.PipeTag;
                string spec = pipe.Spec;
                double nominalDiameter = pipe.NominalDiameter;
                double length = pipe.Length;
                string material = pipe.Material;
                string schedule = pipe.Schedule;
                
                // Get start and end points
                Point3d startPoint = pipe.StartPoint;
                Point3d endPoint = pipe.EndPoint;
                
                // Get connected equipment
                string startConnection = GetConnectedEquipment(pipe.StartPort, tr);
                string endConnection = GetConnectedEquipment(pipe.EndPort, tr);
                
                // Add to report
                report.AppendLine($"Pipe Tag: {pipeTag}");
                report.AppendLine($"  Spec: {spec}");
                report.AppendLine($"  Size: {nominalDiameter}\"");
                report.AppendLine($"  Length: {length:F2} mm");
                report.AppendLine($"  Material: {material}");
                report.AppendLine($"  Schedule: {schedule}");
                report.AppendLine($"  Start: {startPoint.X:F2}, {startPoint.Y:F2}, {startPoint.Z:F2}");
                report.AppendLine($"  End: {endPoint.X:F2}, {endPoint.Y:F2}, {endPoint.Z:F2}");
                
                if (!string.IsNullOrEmpty(startConnection))
                    report.AppendLine($"  Start Connection: {startConnection}");
                if (!string.IsNullOrEmpty(endConnection))
                    report.AppendLine($"  End Connection: {endConnection}");
                
                report.AppendLine();
                
                // Accumulate totals
                totalLength += length;
                
                if (!specLengths.ContainsKey(spec))
                {
                    specLengths[spec] = 0;
                    specCounts[spec] = 0;
                }
                
                specLengths[spec] += length;
                specCounts[spec]++;
            }
        }
        
        // Add summary
        report.AppendLine("\nSUMMARY BY SPECIFICATION");
        report.AppendLine("=" + new string('=', 70));
        
        foreach (var spec in specLengths.Keys)
        {
            report.AppendLine($"{spec}:");
            report.AppendLine($"  Count: {specCounts[spec]} pipes");
            report.AppendLine($"  Total Length: {specLengths[spec]:F2} mm ({specLengths[spec] / 1000:F2} m)");
            report.AppendLine();
        }
        
        report.AppendLine($"GRAND TOTAL LENGTH: {totalLength:F2} mm ({totalLength / 1000:F2} m)");
        
        tr.Commit();
        
        // Save report
        string reportPath = @"C:\Temp\PipeDataReport.txt";
        System.IO.File.WriteAllText(reportPath, report.ToString());
        
        ed.WriteMessage($"\nReport saved to: {reportPath}");
        
        // Open report
        System.Diagnostics.Process.Start("notepad.exe", reportPath);
    }
}

private string GetConnectedEquipment(Port port, Transaction tr)
{
    if (port == null || port.ConnectedPorts.Count == 0)
        return string.Empty;
    
    foreach (ObjectId connectedPortId in port.ConnectedPorts)
    {
        Port connectedPort = tr.GetObject(connecte