Placing Arrows for sloped pipes (sanitary)

Placing Arrows for each and every sloped pipe to indicate the direction for all levels and especially in basement areas where sanitary pipes are huge in number is a very difficult task for plumbing modelers. Here is a very good solution.

The script does the following:

  1. Filters pipe length as per requirement and collects all sloped pipes.
  2. Assigns the right level to pipes even if they have wrong reference levels.
  3. Places the Arrow family as per level.

Inputs:

  1. Select Family.
  2. Assign Pipe length.
  3. Select level
  4. Select all the pipes visible in the view

Outputs:

  1. Arrows placed



Here is the entire code written in Revit Api, Python :

import clr

import sys
sys.path.append('C:\Program Files (x86)\IronPython 2.7\Lib')

import math 

import System
from System.Collections.Generic import *

clr.AddReference('ProtoGeometry')
from Autodesk.DesignScript.Geometry import *

clr.AddReference("RevitNodes")
import Revit
clr.ImportExtensions(Revit.Elements)
clr.ImportExtensions(Revit.GeometryConversion)
clr.AddReference("RevitServices")
import RevitServices
from RevitServices.Persistence import DocumentManager 
from RevitServices.Transactions import TransactionManager 

clr.AddReference("RevitAPI")
clr.AddReference("RevitAPIUI")

import Autodesk 
from Autodesk.Revit.DB import *
from Autodesk.Revit.UI import *
from Autodesk.Revit.DB.Structure import *
from Autodesk.Revit.DB.Plumbing import *
doc = DocumentManager.Instance.CurrentDBDocument
uiapp = DocumentManager.Instance.CurrentUIApplication 
app = uiapp.Application 
uidoc = uiapp.ActiveUIDocument
def tolist(obj1):
	if hasattr(obj1,"__iter__"): return obj1
	else: return [obj1]
	
# Place your code below this line
def ToInternalUnits(number, unittype = None):
    if unittype == None:
        unittype = UnitType.UT_Length 
	currentType = doc.GetUnits().GetFormatOptions(unittype).DisplayUnits
	return UnitUtils.ConvertToInternalUnits(number, currentType)

def getview(v):
	v1 = str(v.ViewType)
	if v1=="FloorPlan":
		return(v)
	else: return("Input is Not a Plan view")	
		
View = UnwrapElement(IN[0])

Length = IN[1]
ft = UnwrapElement(IN[2])
Level = UnwrapElement(IN[3])
#System classification rule filter
Sys_rule = ParameterFilterRuleFactory.CreateEqualsRule(ElementId(-1140325),"Sanitary",True)
#Length rule filter
Length_rule = ParameterFilterRuleFactory.CreateGreaterOrEqualRule(ElementId(-1004005),ToInternalUnits(Length),0)
#C list for filters
Filters = List[FilterRule]()
Filters.Add(Sys_rule)
Filters.Add(Length_rule)
# Parameter Filer
param_filter =ElementParameterFilter(Filters)
# filter collector for pipes
Fec_Pipes = FilteredElementCollector(doc,View.Id).OfClass(Pipe).WherePasses(param_filter).ToElements()
#pipe curves
curves = [pipe.Location.Curve.Direction for pipe in Fec_Pipes ]
#for pipe in Fec_Pipes:	
#	curves.append(pipe.Location.Curve.Direction)
#pipe directions
vectors = [ curve.IsAlmostEqualTo(XYZ(0,0,1)) for curve in curves ]
#for i in curves:
#	vectors.append(i.IsAlmostEqualTo(XYZ(0,0,1)))
#filtering elements parallel to z
filtered_elements = [m for indx,m in enumerate(Fec_Pipes) if vectors[indx] == False]
elementIds = List[ElementId]()
for x in filtered_elements:
	elementIds.Add(x.Id)
#Slope rule filter
Slope_rule = ParameterFilterRuleFactory.CreateHasValueParameterRule(ElementId(-1140256))
Element_Filter = ElementParameterFilter(Slope_rule)
# Filtering on the existing element list
Fec_Elements = FilteredElementCollector(doc,elementIds).WherePasses(Element_Filter).ToElements()

# Getting elements have slope
def getelements(y):
	elements = []
	for e in y:
		if e is None:
			elements.append(None)
		else:
			value = e.LookupParameter("Slope").AsDouble()
			if value > 0 and value <1:
				elements.append(e)
	return elements

elems = getelements(Fec_Elements)
# locations
Startpoints = []
Directions = []
Length = []
D_curve = []
for elem in elems:
	D_curve.append(elem.Location.Curve.ToProtoType())
	Startpoints.append(elem.Location.Curve.Evaluate(0.5, True))
	Length.append(elem.Location.Curve.Length)
	start_ptz = elem.Location.Curve.GetEndPoint(0).Z
	Mid_ptz = elem.Location.Curve.Evaluate(0.5, True).Z
	if start_ptz > Mid_ptz:
		Directions.append(elem.Location.Curve.Direction)
	else:
		Directions.append(elem.Location.Curve.Direction.Negate())
		
#Linebystartpointdirectionandlength
def lineDB_ByStartPointDirectionLength(pt, vector, distance):
	vector = vector.Normalize() * distance
	return Line.CreateBound(pt, pt + vector)

def PlaceArrows(a):	
	arrow = doc.Create.NewFamilyInstance(a,ft,Level,StructuralType.NonStructural)
	param = arrow.LookupParameter("W")
	param_value = param.Set(ToInternalUnits(25))
	Arrows.append(arrow)
	return Arrows



Lines = []
for i,j,k in zip(Startpoints,Directions,Length):
	l = lineDB_ByStartPointDirectionLength(i,j,k)
	Lines.append(l)
	
#Making family active			
if not ft.IsActive:
	ft.Activate()	
	

TransactionManager.Instance.EnsureInTransaction(doc)
#t = Transaction(doc,"Exception Handling")
Arrows = []
for line in Lines:
	try:
		Arrows_placed = PlaceArrows(line)
	except:
		Arrows.append("No elements placed")
TransactionManager.Instance.TransactionTaskDone()

OUT = Arrows_placed
12 Likes

Hi Chandu,
The units issue is now resolved, but now on the final line. This occurs

Warning: IronPythonEvaluator.EvaluateIronPythonScript operation failed.
Traceback (most recent call last):
File “”, line 151, in
NameError: name ‘Arrows_placed’ is not defined

Hi…
Can you send the screenshot of your inputs and also the error screenshot for better understanding.

Hi Chandu,

So i have resolved the input errors, but now when i run the script nothing happens.
Is there a workflow or selection inputs that need to be done in order?

Hi Chandu,
On the final line. This occurs

what family category of Arrow, Chandu?

@khoa.lehuuanh …The selection order is correct. The view should only be a plan view and the family type should be a detail item line-based family for the families to be placed in the project.

1 Like

@sead.redzepagic…The input order must be View, Length, Familytype, and Level. The view should only be a plan view and family-type (Arrow) should be a detail item line-based family.

But still have error in the python script.
image

Can you please send the Revit file and family so that I can review once.

ah perfect. Worked like a charm mate! Thank you

@sead.redzepagic…You are Welcome…!! :slightly_smiling_face:

I have changed family-type (Arrow) to detail item line-based family and it’s run perfectly. Thank you so much!

@khoa.lehuuanh…You are welcome…!! :slightly_smiling_face:

Chandu,
Do you have a solution for placing pipe invert levels/spot elevations at every bend, junction and fixture?
It would be pretty awesome if you could have something automated for that. Similar to the the pipe slope arrow tool.

@sead.redzepagic…Previously placed duct tags…Might be of a similar kind and will definitely try to work on this and post the solution… :slightly_smiling_face:

Sounds good mate. See how you go

Hi Chandu

can you resolve the error?

I guess there are not enough sanitary pipes after passing the filters and also check the system classification of the plumbing pipes. I t should be sanitary.