Script for automatic placing of flanges - problem with system type

Hi,

Due to frustation with manual placing of flanges, I have written a python script for putting flanges to pipe networks. (And for those who are wondering why I don’t use Revits built-in function for flanges: This puts flanges next to every pipe fitting as well, where they are not supposed to be).

The script is heavily based on nodes in the MEPover packages, so huge thanks to the author of it.

Most of the script works fine, except roughly 50% of the flanges and pipe accessories get wrong system type, as seen on the image below, where different system types are given different colors:
image

I have spent a lot of time trying different ways to manipulate this, but I’m stuck. So, I paste my code here, both in case someone find it useful for their own projects, but also I hope someone can help with assigning the right system type to the parts.

Short explanation of script:

Loop all pipe accessories and mechanical equipement.
(I sort out pipe accessorires with type=“standard”, as our flanges are also modelled as pipe accessories, and our valves etc. using flanges do NOT have type = “standard”)
Find all instances where these are connected to a pipe.
Put a flange there.
Flip it if necessary, and move to right place.
Disconnect pipe and pipe accessory/mech equip
Connect flange with pipe and with PA/ME.
Adjust pipe length.

So, the code:

import clr
import sys
import math

clr.AddReference('ProtoGeometry')
from Autodesk.DesignScript.Geometry import *
 
clr.AddReference("RevitServices")
import RevitServices
from RevitServices.Persistence import DocumentManager
from RevitServices.Transactions import TransactionManager
doc =  DocumentManager.Instance.CurrentDBDocument
 
clr.AddReference("RevitNodes")
import Revit
clr.ImportExtensions(Revit.Elements)
clr.ImportExtensions(Revit.GeometryConversion)
 
clr.AddReference("RevitAPI")
import Autodesk
from Autodesk.Revit.DB import *

from System.Collections.Generic import List

from Autodesk.Revit.DB.Plumbing import *

pipingSystem = FilteredElementCollector(doc).OfClass(PipingSystemType).ToElements()

list_piping_system = []
list_piping_system_id = []

for i in pipingSystem:
	list_piping_system.append(i)
	list_piping_system_id.append(i.Id)
		

def measure(startpoint, point):
	return startpoint.DistanceTo(point)
	
def copyElement(element, oldloc, loc):
	TransactionManager.Instance.EnsureInTransaction(doc)
	elementlist = List[ElementId]()
	elementlist.Add(element.Id)
	OffsetZ = (oldloc.Z - loc.Z)*-1
	OffsetX = (oldloc.X - loc.X)*-1
	OffsetY = (oldloc.Y - loc.Y)*-1
	direction = XYZ(OffsetX,OffsetY,OffsetZ)
	newelementId = ElementTransformUtils.CopyElements(doc,elementlist,direction)
	newelement = doc.GetElement(newelementId[0])
	TransactionManager.Instance.TransactionTaskDone()
	return newelement

def GetDirection(faminstance):
	for c in faminstance.MEPModel.ConnectorManager.Connectors:
		conn = c
		break
	return conn.CoordinateSystem.BasisZ

def GetClosestDirection(faminstance, lineDirection):
	conndir = None
	flat_linedir = XYZ(lineDirection.X,lineDirection.Y,0).Normalize()
	for conn in faminstance.MEPModel.ConnectorManager.Connectors:
		conndir = conn.CoordinateSystem.BasisZ
		if flat_linedir.IsAlmostEqualTo(conndir):
			return conndir
	return conndir

#
debug = []

#global variables for rotating new families
tempfamtype = None
xAxis = XYZ(1,0,0)
def placeFitting(duct,point,familytype,lineDirection):
	
	toggle = False
	isVertical = False
	
	
	global tempfamtype
	if tempfamtype == None:
		tempfamtype = familytype
		toggle = True
	elif tempfamtype != familytype:
		toggle = True
		tempfamtype = familytype
	level = duct.ReferenceLevel
	
	width = 4
	height = 4
	radius = 2
	round = False
	connectors = duct.ConnectorManager.Connectors
	for c in connectors:
		if c.ConnectorType != ConnectorType.End:
			continue
		shape = c.Shape
		if shape == ConnectorProfileType.Round:
			radius = c.Radius
			round = True	
			break
		elif shape == ConnectorProfileType.Rectangular or shape == ConnectorProfileType.Oval:
			if abs(lineDirection.Z) == 1:
				isVertical = True
				yDir = c.CoordinateSystem.BasisY
			width = c.Width
			height = c.Height
			break
		
	TransactionManager.Instance.EnsureInTransaction(doc)	
	point = XYZ(point.X,point.Y,point.Z-level.Elevation)
	
	
	## THIS LINE IS DEPENDENT ON UNITS AND PROJECT SETTINGS. LINE BELOW IS FOR PROEJCT USING MM AS UNIT, AND THERE IS NOT ADDED FLANGES FOR DN<55 MM
	if radius < 55/304.8/2:
		return 0
		
	newfam = doc.Create.NewFamilyInstance(point,familytype,level,Structure.StructuralType.NonStructural)
	doc.Regenerate()
	transform = newfam.GetTransform()
	axis = Line.CreateUnbound(transform.Origin, transform.BasisZ)
	global xAxis
	if toggle:
		xAxis = GetDirection(newfam)
	zAxis = XYZ(0,0,1)
	
	if isVertical:
		angle = xAxis.AngleOnPlaneTo(yDir,zAxis)
	else:
		angle = xAxis.AngleOnPlaneTo(lineDirection,zAxis)
	
	
	ElementTransformUtils.RotateElement(doc,newfam.Id,axis,angle)
	doc.Regenerate()
	
	if lineDirection.Z != 0:
		newAxis = GetClosestDirection(newfam,lineDirection)
		yAxis = newAxis.CrossProduct(zAxis)
		angle2 = newAxis.AngleOnPlaneTo(lineDirection,yAxis)
		axis2 = Line.CreateUnbound(transform.Origin, yAxis)
		ElementTransformUtils.RotateElement(doc,newfam.Id,axis2,angle2)
	
	result = {}
	connpoints = []
	#famconns = newfam.MEPModel.ConnectorManager.Connectors
	
	newfam.LookupParameter('Nominal Radius').Set(radius)
	## 
	#famconns = UnwrapElement(famconns)
	
	#if round:
		#for conn in famconns:
		## JF
	#	for j,conn in enumerate(famconns):
	#		if IsParallel(lineDirection,conn.CoordinateSystem.BasisZ) == False:
	#			continue
	#		if conn.Shape != shape:
	#			continue
			#conn.Radius = radius
			## JF
	#		if j == 0:
				#conn.Radius = 0.32
	#			debug.append('radius ' + str(radius))
	#			debug.append('conn.radius ' + str(conn.Radius))
	#		debug.append('anyway')
	#		connpoints.append(conn.Origin)
	#else:
	#	for conn in famconns:
	#		if IsParallel(lineDirection,conn.CoordinateSystem.BasisZ) == False:
	#			continue
	#		if conn.Shape != shape:
	#			continue
	#		conn.Width = width
	#		conn.Height = height
	#		connpoints.append(conn.Origin)
	TransactionManager.Instance.TransactionTaskDone()
	#result[newfam] = connpoints
	return newfam

def ConnectElements(duct, fitting):
	ductconns = duct.ConnectorManager.Connectors
	fittingconns = fitting.MEPModel.ConnectorManager.Connectors
	
	TransactionManager.Instance.EnsureInTransaction(doc)
	for conn in fittingconns:
		for ductconn in ductconns:
			result = ductconn.Origin.IsAlmostEqualTo(conn.Origin)
			if result:
				ductconn.ConnectTo(conn)
				break
	TransactionManager.Instance.TransactionTaskDone()
	return result

def SortedPoints(fittingspoints,ductStartPoint):
	sortedpoints = sorted(fittingspoints, key=lambda x: measure(ductStartPoint, x))
	return sortedpoints
	
###########################################################
## start algorithm for finding missing flanges
###########################################################
	
#COMPANY SPESIFIC CODE FOR FLANGE TYPES TO BE SELECTED
PA1 = FilteredElementCollector(doc).OfCategory(BuiltInCategory.OST_PipeAccessory).WhereElementIsElementType()

flange_family_type = [0,0,0,0]

for i in PA1:
	if 'Krage-Løsflens_med pakning' in i.Family.Name:
		flange_family_type[0] = i
		break		

for j in PA1:
	if 'Krage-Løsflens_uten pakning' in j.Family.Name:
		flange_family_type[1] = j
		break		
		
for k in PA1:
	if 'Sveiseflens_uten pakning' in k.Family.Name:
		flange_family_type[2] = k
		break		
		
for l in PA1:
	if 'Sveiseflens_uten pakning' in l.Family.Name:
		flange_family_type[3] = l
		break		


#collect all mechanical equipment and pipe accessories in project


cat_list = [BuiltInCategory.OST_PipeAccessory, BuiltInCategory.OST_MechanicalEquipment]
typed_list = List[BuiltInCategory](cat_list)
filter = ElementMulticategoryFilter(typed_list)
EQ = FilteredElementCollector(doc).WherePasses(filter).WhereElementIsNotElementType().ToElements()
    
    

pipe = []
pipe_connector = []
pipe_endpoints = []
pipe_endpoint_id = []
valve_connector = []
opposite_valve_connector = []
gasket = []
valve =[]


for i in EQ:
#for i in []:
	#Filter out flanges and other parts where type-name i "Standard"

	if i.Name != 'Standard':

		#find connectorer to valve
		try:
			connectors = i.MEPModel.ConnectorManager.Connectors
		except:
			try:
				connectors = i.ConnectorManager.Connectors
			except:			
				connectors = []
		#modify connectorset to be subscriptable
		cons = []
		for kk in connectors:
			cons.append(kk)
		
		#check what type of parts the connectors are connected to
		# from node "connector.connectedElements from MEPover	
		for n,j in enumerate(cons):
			refs = None
			try:
				refs = [doc.GetElement(x.Owner.Id) for x in j.AllRefs if x.Owner.Id != j.Owner.Id and x.ConnectorType != ConnectorType.Logical]
			except:
				refs = None
			if type(refs)==list:
				if refs == []:
					reft = None
				else:
					refs = refs[0]

			for y in j.AllRefs:
				if y.Owner.Id != j.Owner.Id and y.ConnectorType != ConnectorType.Logical:
					pipecon = y
						


			# check if connected to straight pipe
			if refs is not None:
				cat_name = None
				try: 
					cat_name = refs.Category.Name
				except:
					continue
				
				if cat_name == 'Pipes':
					#checking if pipe is longer than 10mm. don't want to add flanges on very short pipes which should not be there, stuck between valves. 
					#LINE BELOW IS ONLY FOR PROJECTS USING MM AS UNIT
					if refs.Location.Curve.GetEndPoint(0).DistanceTo(refs.Location.Curve.GetEndPoint(1)) > 10/304.8:
	
						#Preparing lists with corresponding indexes:		
						pipe.append(refs)
						pipe_endpoints.append([refs.Location.Curve.GetEndPoint(0), refs.Location.Curve.GetEndPoint(1)])
						pipe_connector.append(pipecon) 
						valve_connector.append(j)				
						opposite_valve_connector.append(cons[1-n])
						valve.append(i)
						if refs.Location.Curve.GetEndPoint(0).DistanceTo(j.Origin) < refs.Location.Curve.GetEndPoint(1).DistanceTo(j.Origin):
							pipe_endpoint_id.append(0)
						else:
							pipe_endpoint_id.append(1)
						#Company spesific code determining which type of flange to be used
						if 'innspent' in i.Symbol.FamilyName:
							gasket.append(False)
						else:
							gasket.append(True)

				
############################################################################
#####  Start script for adding flanges. Based on MEPover node
###########################################################################


TransactionManager.Instance.EnsureInTransaction(doc)
for typ in flange_family_type:
	if typ != 0:
		if typ.IsActive == False:
			typ.Activate()
			doc.Regenerate()
TransactionManager.Instance.TransactionTaskDone()

debug2 = []

for i,duct in enumerate(pipe):

	pointlist = valve_connector[i].Origin
	
	#Krage-løsflens
	if gasket[i]:
		familytype = flange_family_type[0]
	else:
		familytype = flange_family_type[1]
	
	#Sveiseflens
	#if gasket[i]:
	#	familytype = flange_family_type[0]
	#else:
	#	familytype = flange_family_type[1]
	
	#create duct location line
	ductline = duct.Location.Curve
	#ductStartPoint = ductline.GetEndPoint(0)
	#ductEndPoint = ductline.GetEndPoint(1)
	#get end connector to reconnect later
	#endIsConnected = False
	#endrefconn = None
	#for ductconn in duct.ConnectorManager.Connectors:
	#		if ductconn.Origin.DistanceTo(ductEndPoint) < 5/304.8:
	#		if ductconn.IsConnected:
	#			endIsConnected = True
	#			for refconn in ductconn.AllRefs:
	#				if refconn.ConnectorType != ConnectorType.Logical and refconn.Owner.Id.IntegerValue != duct.Id.IntegerValue:
	#					endrefconn = refconn
			
	#sort the points from start of duct to end of duct
	#pointlist = SortedPoints(ListOfPoints,ductStartPoint)
	
	
	#pointlist = ListOfPoints
	#debug.append(pointlist)
	
	
	#ductlist = []
	#newFittings = []
	#ductlist.append(duct)
	
	#tempStartPoint = None
	#tempEndPoint = None
	
	lineDirection = ductline.Direction
	
	
	
	new_flange = placeFitting(duct,pointlist,familytype,lineDirection)

	
	#check if flange was created. If not, probably due to too small DN
	if new_flange == 0:
		continue
	debug2.append('            ')
	#check if flange need to be flipped
	try:
		flange_connectors = new_flange.MEPModel.ConnectorManager.Connectors
	except:
		try:
			flange_connectors = new_flange.ConnectorManager.Connectors
		except:			
			flange_connectors = []
			cons = []
	#make subscriptable
	f_cons = []
	for a in flange_connectors:
			f_cons.append(a)
	# valve_cons.append([x for x in connectors])
	flange_a_con_position = f_cons[0].Origin
	flange_b_con_position = f_cons[1].Origin
	

	if flange_a_con_position.DistanceTo(opposite_valve_connector[i].Origin) < flange_b_con_position.DistanceTo(opposite_valve_connector[i].Origin):
		#flange side a is facing the valve
		need_to_flip = f_cons[0].GetMEPConnectorInfo().IsPrimary
	else:
		need_to_flip = f_cons[1].GetMEPConnectorInfo().IsPrimary


	
	TransactionManager.Instance.EnsureInTransaction(doc)
	if need_to_flip:
		
		vector = valve_connector[i].CoordinateSystem.BasisY

		line = Autodesk.Revit.DB.Line.CreateBound(valve_connector[i].Origin , valve_connector[i].Origin + vector)
		line = UnwrapElement(line)

		flipped = new_flange.Location.Rotate(line, math.pi)
		

	
	##Move to right place
	#find primary and secondardy connector
	
	doc.Regenerate()
	
	if f_cons[0].GetMEPConnectorInfo().IsPrimary:
		#debug2.append('primary')
		primary_con_id = 0
		secondary_con_id = 1
	else: 
		primary_con_id = 1
		secondary_con_id = 0
		#debug2.append('secondary')
	
	doc.Regenerate()
	
	new_flange.Location.Move((valve_connector[i].Origin - f_cons[secondary_con_id].Origin))
	
	doc.Regenerate()
	
		
	#modify pipe endpoints
	if pipe_endpoint_id[i] == 0:
		new_pipeline = Autodesk.Revit.DB.Line.CreateBound(f_cons[primary_con_id].Origin, pipe[i].Location.Curve.GetEndPoint(1))
		pipe[i].Location.Curve = new_pipeline
		#debug2.append('a')
	else :
		new_pipeline = Autodesk.Revit.DB.Line.CreateBound(pipe[i].Location.Curve.GetEndPoint(0), f_cons[primary_con_id].Origin)
		pipe[i].Location.Curve = new_pipeline
		#debug2.append('b')
		
	doc.Regenerate()

	#duct_piping_system_type = pipe_connector[i].MEPSystem	
	duct_piping_system_type = pipe[i].get_Parameter(BuiltInParameter.RBS_PIPING_SYSTEM_TYPE_PARAM).AsElementId()
		
	for k, sys in enumerate(list_piping_system_id):
		if sys == duct_piping_system_type:
			rorsystem = list_piping_system[k]


	#disconnect
	valve_connector[i].DisconnectFrom(pipe_connector[i])
		
	doc.Regenerate()
	
	
	#make new connections
	pipe_connector[i].ConnectTo(f_cons[primary_con_id])
	
	#connset = ConnectorSet()
	#connset.Insert(f_cons[0])
	#connset.Insert(f_cons[1])
	#connset.Insert(valve_connector[i])
	#duct_piping_system_type.Add(connset)

			
	
	doc.Regenerate()

	f_cons[secondary_con_id].ConnectTo(valve_connector[i])
	
	doc.Regenerate()

	#pipe_connector[i].MEPSystem.Add(connset)
	
	doc.Regenerate()
	TransactionManager.Instance.TransactionTaskDone()	
	
	debug2.append(new_flange)


OUT = debug2

Edited 02.09.2020 due to typos in code

1 Like

Hi @jorgen.fidjeland
could you post also the families .rfa that you want to place ? Cheers

Sure!
Krage-Løsflens_med pakning.rfa (332 KB) UV aggregat.rfa (308 KB)

The first one is the flange, and the second one is an example of PA equipment. There are four flange-types mentioned in the script, but so far the script only uses two of them, and it will select the attached flange for the attaced PA equipment.

I have solved the problem!

I did some more testing, and realised that the problem actually had never occured in the family I attached to the answer above. I figured out that the connectors on the families that caused problems had the parameter “System classification” set to “Global”. I changed this to “cold water supply”, and now it works like a charm!

I should probably add some lines of code displaying an error message if the scripts detects that the connectors of the valves etc. has wrong system classification. I will post updated code here soon in case someone else is interested.

1 Like

Hi @jorgen.fidjeland
Great, well done. I can’t open your family on Revit 2019, can you post an image or direcly the .dyn file?
Thanks for the python code.
Cheers

Here is the code. It also checks the families for all the valves etc. where flanges are to be put, whether the system classification is “Global”, and changes it to “Cold Water Supply” automatically if so.

Feel free to use the code on your own projects, but some adaptations is necessary due to the following:

  • I have sorted out pipes shorter than 10mm and smaller than or equal to DN50 (<55)

  • You need to replace “Krage-løsflens” etc. with the family name of the flange you want to use

  • I use revit with mm as unit. Some parts of the code will not work using feet as unit.

    import clr
    import sys
    import math

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

    clr.AddReference(“RevitServices”)
    import RevitServices
    from RevitServices.Persistence import DocumentManager
    from RevitServices.Transactions import TransactionManager
    doc = DocumentManager.Instance.CurrentDBDocument

    clr.AddReference(“RevitNodes”)
    import Revit
    clr.ImportExtensions(Revit.Elements)
    clr.ImportExtensions(Revit.GeometryConversion)

    clr.AddReference(“RevitAPI”)
    import Autodesk
    from Autodesk.Revit.DB import *

    from System.Collections.Generic import List

    from Autodesk.Revit.DB.Plumbing import *

    pipingSystem = FilteredElementCollector(doc).OfClass(PipingSystemType).ToElements()

    list_piping_system =
    list_piping_system_id =

    for i in pipingSystem:
    list_piping_system.append(i)
    list_piping_system_id.append(i.Id)

    def measure(startpoint, point):
    return startpoint.DistanceTo(point)

    def copyElement(element, oldloc, loc):
    TransactionManager.Instance.EnsureInTransaction(doc)
    elementlist = ListElementId
    elementlist.Add(element.Id)
    OffsetZ = (oldloc.Z - loc.Z)-1
    OffsetX = (oldloc.X - loc.X)
    -1
    OffsetY = (oldloc.Y - loc.Y)*-1
    direction = XYZ(OffsetX,OffsetY,OffsetZ)
    newelementId = ElementTransformUtils.CopyElements(doc,elementlist,direction)
    newelement = doc.GetElement(newelementId[0])
    TransactionManager.Instance.TransactionTaskDone()
    return newelement

    def GetDirection(faminstance):
    for c in faminstance.MEPModel.ConnectorManager.Connectors:
    conn = c
    break
    return conn.CoordinateSystem.BasisZ

    def GetClosestDirection(faminstance, lineDirection):
    conndir = None
    flat_linedir = XYZ(lineDirection.X,lineDirection.Y,0).Normalize()
    for conn in faminstance.MEPModel.ConnectorManager.Connectors:
    conndir = conn.CoordinateSystem.BasisZ
    if flat_linedir.IsAlmostEqualTo(conndir):
    return conndir
    return conndir

    debug =

    #global variables for rotating new families
    tempfamtype = None
    xAxis = XYZ(1,0,0)
    def placeFitting(duct,point,familytype,lineDirection):

      toggle = False
      isVertical = False
      
      
      global tempfamtype
      if tempfamtype == None:
      	tempfamtype = familytype
      	toggle = True
      elif tempfamtype != familytype:
      	toggle = True
      	tempfamtype = familytype
      level = duct.ReferenceLevel
      
      width = 4
      height = 4
      radius = 2
      round = False
      connectors = duct.ConnectorManager.Connectors
      for c in connectors:
      	if c.ConnectorType != ConnectorType.End:
      		continue
      	shape = c.Shape
      	if shape == ConnectorProfileType.Round:
      		radius = c.Radius
      		round = True	
      		break
      	elif shape == ConnectorProfileType.Rectangular or shape == ConnectorProfileType.Oval:
      		if abs(lineDirection.Z) == 1:
      			isVertical = True
      			yDir = c.CoordinateSystem.BasisY
      		width = c.Width
      		height = c.Height
      		break
      	
      TransactionManager.Instance.EnsureInTransaction(doc)	
      point = XYZ(point.X,point.Y,point.Z-level.Elevation)
      
      
      ## THIS LINE IS DEPENDENT ON UNITS AND PROJECT SETTINGS. LINE BELOW IS FOR PROEJCT USING MM AS UNIT, AND THERE IS NOT ADDED FLANGES FOR DN<55 MM
      if radius < 55/304.8/2:
      	return 0
      	
      newfam = doc.Create.NewFamilyInstance(point,familytype,level,Structure.StructuralType.NonStructural)
      doc.Regenerate()
      transform = newfam.GetTransform()
      axis = Line.CreateUnbound(transform.Origin, transform.BasisZ)
      global xAxis
      if toggle:
      	xAxis = GetDirection(newfam)
      zAxis = XYZ(0,0,1)
      
      if isVertical:
      	angle = xAxis.AngleOnPlaneTo(yDir,zAxis)
      else:
      	angle = xAxis.AngleOnPlaneTo(lineDirection,zAxis)
      
      
      ElementTransformUtils.RotateElement(doc,newfam.Id,axis,angle)
      doc.Regenerate()
      
      if lineDirection.Z != 0:
      	newAxis = GetClosestDirection(newfam,lineDirection)
      	yAxis = newAxis.CrossProduct(zAxis)
      	angle2 = newAxis.AngleOnPlaneTo(lineDirection,yAxis)
      	axis2 = Line.CreateUnbound(transform.Origin, yAxis)
      	ElementTransformUtils.RotateElement(doc,newfam.Id,axis2,angle2)
      
      result = {}
      connpoints = []
      #famconns = newfam.MEPModel.ConnectorManager.Connectors
      
      newfam.LookupParameter('Nominal Radius').Set(radius)
      ## 
      #famconns = UnwrapElement(famconns)
      
      #if round:
      	#for conn in famconns:
      	## JF
      #	for j,conn in enumerate(famconns):
      #		if IsParallel(lineDirection,conn.CoordinateSystem.BasisZ) == False:
      #			continue
      #		if conn.Shape != shape:
      #			continue
      		#conn.Radius = radius
      		## JF
      #		if j == 0:
      			#conn.Radius = 0.32
      #			debug.append('radius ' + str(radius))
      #			debug.append('conn.radius ' + str(conn.Radius))
      #		debug.append('anyway')
      #		connpoints.append(conn.Origin)
      #else:
      #	for conn in famconns:
      #		if IsParallel(lineDirection,conn.CoordinateSystem.BasisZ) == False:
      #			continue
      #		if conn.Shape != shape:
      #			continue
      #		conn.Width = width
      #		conn.Height = height
      #		connpoints.append(conn.Origin)
      TransactionManager.Instance.TransactionTaskDone()
      #result[newfam] = connpoints
      return newfam
    

    def ConnectElements(duct, fitting):
    ductconns = duct.ConnectorManager.Connectors
    fittingconns = fitting.MEPModel.ConnectorManager.Connectors

      TransactionManager.Instance.EnsureInTransaction(doc)
      for conn in fittingconns:
      	for ductconn in ductconns:
      		result = ductconn.Origin.IsAlmostEqualTo(conn.Origin)
      		if result:
      			ductconn.ConnectTo(conn)
      			break
      TransactionManager.Instance.TransactionTaskDone()
      return result
    

    def SortedPoints(fittingspoints,ductStartPoint):
    sortedpoints = sorted(fittingspoints, key=lambda x: measure(ductStartPoint, x))
    return sortedpoints

    class for overwriting loaded families in the project

    class FamOpt1(IFamilyLoadOptions):
    def init(self): pass
    def OnFamilyFound(self,familyInUse, overwriteParameterValues): return True
    def OnSharedFamilyFound(self,familyInUse, source, overwriteParameterValues): return True

    ###########################################################

    start algorithm for finding missing flanges

    ###########################################################

    #COMPANY SPESIFIC CODE FOR FLANGE TYPES TO BE SELECTED
    PA1 = FilteredElementCollector(doc).OfCategory(BuiltInCategory.OST_PipeAccessory).WhereElementIsElementType()

    flange_family_type = [0,0,0,0]

    for i in PA1:
    if ‘Krage-Løsflens_med pakning’ in i.Family.Name:
    flange_family_type[0] = i
    break

    for j in PA1:
    if ‘Krage-Løsflens_uten pakning’ in j.Family.Name:
    flange_family_type[1] = j
    break

    for k in PA1:
    if ‘Sveiseflens_uten pakning’ in k.Family.Name:
    flange_family_type[2] = k
    break

    for l in PA1:
    if ‘Sveiseflens_uten pakning’ in l.Family.Name:
    flange_family_type[3] = l
    break

    #prepare connection filter for later collector within family editor
    con_cat_list = [BuiltInCategory.OST_ConnectorElem]
    con_typed_list = ListBuiltInCategory
    con_filter = ElementMulticategoryFilter(con_typed_list)

    #collect all mechanical equipment and pipe accessories in project

    cat_list = [BuiltInCategory.OST_PipeAccessory, BuiltInCategory.OST_MechanicalEquipment]
    typed_list = ListBuiltInCategory
    filter = ElementMulticategoryFilter(typed_list)
    EQ = FilteredElementCollector(doc).WherePasses(filter).WhereElementIsNotElementType().ToElements()

    #Gir: Autodesk.Revit.DB.FamilyInstance
    #EQ_families = FilteredElementCollector(doc).WherePasses(filter).WhereElementIsNotElementType()
    #Gir: Autodesk.Revit.DB.FamilySymbol
    #EQ_families = FilteredElementCollector(doc).WherePasses(filter).WhereElementIsElementType()

    debug4 =

    pipe =
    pipe_connector =
    pipe_endpoints =
    pipe_endpoint_id =
    valve_connector =
    opposite_valve_connector =
    gasket =
    valve =

    #list containing all family names where connectors has been checked and potentially modified

    checked_valve_families =

    for i in EQ:
    #for i in :
    #Filter out flanges and other parts where type-name i “Standard”

      if i.Name != 'Standard':
    
      	#find connectorer to valve
      	try:
      		connectors = i.MEPModel.ConnectorManager.Connectors
      	except:
      		try:
      			connectors = i.ConnectorManager.Connectors
      		except:			
      			connectors = []
      	#modify connectorset to be subscriptable
      	cons = []
      	for kk in connectors:
      		cons.append(kk)
      	
      	#check what type of parts the connectors are connected to
      	# from node "connector.connectedElements from MEPover	
      	for n,j in enumerate(cons):
      		refs = None
      		try:
      			refs = [doc.GetElement(x.Owner.Id) for x in j.AllRefs if x.Owner.Id != j.Owner.Id and x.ConnectorType != ConnectorType.Logical]
      		except:
      			refs = None
      		if type(refs)==list:
      			if refs == []:
      				reft = None
      			else:
      				refs = refs[0]
    
      		for y in j.AllRefs:
      			if y.Owner.Id != j.Owner.Id and y.ConnectorType != ConnectorType.Logical:
      				pipecon = y
      					
    
    
      		# check if connected to straight pipe
      		if refs is not None:
      			cat_name = None
      			try: 
      				cat_name = refs.Category.Name
      			except:
      				continue
      			
      			if cat_name == 'Pipes':
      				#checking if pipe is longer than 10mm. don't want to add flanges on very short pipes which should not be there, stuck between valves. 
      				#LINE BELOW IS ONLY FOR PROJECTS USING MM AS UNIT
      				if refs.Location.Curve.GetEndPoint(0).DistanceTo(refs.Location.Curve.GetEndPoint(1)) > 10/304.8:
      
      					#Preparing lists with corresponding indexes:		
      					pipe.append(refs)
      					pipe_endpoints.append([refs.Location.Curve.GetEndPoint(0), refs.Location.Curve.GetEndPoint(1)])
      					pipe_connector.append(pipecon) 
      					valve_connector.append(j)				
      					opposite_valve_connector.append(cons[1-n])
      					valve.append(i)
      					if refs.Location.Curve.GetEndPoint(0).DistanceTo(j.Origin) < refs.Location.Curve.GetEndPoint(1).DistanceTo(j.Origin):
      						pipe_endpoint_id.append(0)
      					else:
      						pipe_endpoint_id.append(1)
      					#Company spesific code determining which type of flange to be used
      					if 'innspent' in i.Symbol.FamilyName:
      						gasket.append(False)
      					else:
      						gasket.append(True)
    
      					#Checking the connector-types of the family
      					valve_type_id = i.GetTypeId()
      					valve_element_type = doc.GetElement(valve_type_id)
      					valve_family = valve_element_type.Family
      					
      					if valve_family.Name not in checked_valve_families:
      					
      						trans1 = TransactionManager.Instance
      						trans1.ForceCloseTransaction() #just to make sure everything is closed down
      					
      						famdoc = doc.EditFamily(valve_family)
      					
      						#Below is moved
      						#con_cat_list = [BuiltInCategory.OST_ConnectorElem]
      						#con_typed_list = List[BuiltInCategory](con_cat_list)
      						#con_filter = ElementMulticategoryFilter(con_typed_list)
      					
      						fam_connections = FilteredElementCollector(famdoc).WherePasses(con_filter).WhereElementIsNotElementType().ToElements()
      					
      						for a in fam_connections:
      						
      							debug4.append(a.get_Parameter(BuiltInParameter.RBS_PIPE_CONNECTOR_SYSTEM_CLASSIFICATION_PARAM).AsValueString())
      							debug4.append(a.get_Parameter(BuiltInParameter.RBS_PIPE_CONNECTOR_SYSTEM_CLASSIFICATION_PARAM).AsInteger())
      							if a.get_Parameter(BuiltInParameter.RBS_PIPE_CONNECTOR_SYSTEM_CLASSIFICATION_PARAM).AsValueString() == 'Global':
      								debug4.append('treff på global')
      								try: # this might fail if the parameter exists or for some other reason
      									trans1.EnsureInTransaction(famdoc)
      									#famdoc.FamilyManager.AddParameter(par_name, par_grp, par_type, inst_or_typ)
      									###a.get_Parameter(BuiltInParameter.RBS_PIPE_CONNECTOR_SYSTEM_CLASSIFICATION_PARAM).SetValueString('Domestic Cold Water')
      								
      									a.get_Parameter(BuiltInParameter.RBS_PIPE_CONNECTOR_SYSTEM_CLASSIFICATION_PARAM).Set(20)
      									#20 is the integer-id for Domestic Cold Water
      								
      									trans1.ForceCloseTransaction()
      									famdoc.LoadFamily(doc, FamOpt1())
      									#result.append(True)
      									debug4.append('jabadadoo')
      								except: #you might want to import traceback for a more detailed error report
      									#result.append(False)
      									debug4.append('neeeeei')
      									trans1.ForceCloseTransaction()      
      					
      						famdoc.Close(False)
      						
      						checked_valve_families.append(valve_family.Name)
      							
      							#TransactionManager.Instance.EnsureInTransaction(famdoc)
      
      							#debug4.append('treff på global')
      							#if a.get_Parameter(BuiltInParameter.RBS_PIPE_CONNECTOR_SYSTEM_CLASSIFICATION_PARAM).SetValueString('Domestic Cold Water'):
      							#	debug4.append('suksess')
      							#famdoc.Regenerate()
      							#TransactionManager.Instance.TransactionTaskDone()
    

    ############################################################################

    Start script for adding flanges. Based on MEPover node

    ###########################################################################

    TransactionManager.Instance.EnsureInTransaction(doc)
    for typ in flange_family_type:
    if typ != 0:
    if typ.IsActive == False:
    typ.Activate()
    doc.Regenerate()
    TransactionManager.Instance.TransactionTaskDone()

    debug2 =

    for i,duct in enumerate(pipe):

      pointlist = valve_connector[i].Origin
      
      #Krage-løsflens
      if gasket[i]:
      	familytype = flange_family_type[0]
      else:
      	familytype = flange_family_type[1]
      
      #Sveiseflens
      #if gasket[i]:
      #	familytype = flange_family_type[2]
      #else:
      #	familytype = flange_family_type[3]
      
      #create duct location line
      ductline = duct.Location.Curve
      #ductStartPoint = ductline.GetEndPoint(0)
      #ductEndPoint = ductline.GetEndPoint(1)
      #get end connector to reconnect later
      #endIsConnected = False
      #endrefconn = None
      #for ductconn in duct.ConnectorManager.Connectors:
      #		if ductconn.Origin.DistanceTo(ductEndPoint) < 5/304.8:
      #		if ductconn.IsConnected:
      #			endIsConnected = True
      #			for refconn in ductconn.AllRefs:
      #				if refconn.ConnectorType != ConnectorType.Logical and refconn.Owner.Id.IntegerValue != duct.Id.IntegerValue:
      #					endrefconn = refconn
      		
      #sort the points from start of duct to end of duct
      #pointlist = SortedPoints(ListOfPoints,ductStartPoint)
      
      
      #pointlist = ListOfPoints
      #debug.append(pointlist)
      
      
      #ductlist = []
      #newFittings = []
      #ductlist.append(duct)
      
      #tempStartPoint = None
      #tempEndPoint = None
      
      lineDirection = ductline.Direction
      
      
      
      new_flange = placeFitting(duct,pointlist,familytype,lineDirection)
    
      
      #check if flange was created. If not, probably due to too small DN
      if new_flange == 0:
      	continue
      debug2.append('            ')
      #check if flange need to be flipped
      try:
      	flange_connectors = new_flange.MEPModel.ConnectorManager.Connectors
      except:
      	try:
      		flange_connectors = new_flange.ConnectorManager.Connectors
      	except:			
      		flange_connectors = []
      		cons = []
      #make subscriptable
      f_cons = []
      for a in flange_connectors:
      		f_cons.append(a)
      # valve_cons.append([x for x in connectors])
      flange_a_con_position = f_cons[0].Origin
      flange_b_con_position = f_cons[1].Origin
      
    
      if flange_a_con_position.DistanceTo(opposite_valve_connector[i].Origin) < flange_b_con_position.DistanceTo(opposite_valve_connector[i].Origin):
      	#flange side a is facing the valve
      	need_to_flip = f_cons[0].GetMEPConnectorInfo().IsPrimary
      else:
      	need_to_flip = f_cons[1].GetMEPConnectorInfo().IsPrimary
    
    
      
      TransactionManager.Instance.EnsureInTransaction(doc)
      if need_to_flip:
      	
      	vector = valve_connector[i].CoordinateSystem.BasisY
    
      	line = Autodesk.Revit.DB.Line.CreateBound(valve_connector[i].Origin , valve_connector[i].Origin + vector)
      	line = UnwrapElement(line)
    
      	flipped = new_flange.Location.Rotate(line, math.pi)
      	
    
      
      ##Move to right place
      #find primary and secondardy connector
      
      doc.Regenerate()
      
      if f_cons[0].GetMEPConnectorInfo().IsPrimary:
      	#debug2.append('primary')
      	primary_con_id = 0
      	secondary_con_id = 1
      else: 
      	primary_con_id = 1
      	secondary_con_id = 0
      	#debug2.append('secondary')
      
      doc.Regenerate()
      
      new_flange.Location.Move((valve_connector[i].Origin - f_cons[secondary_con_id].Origin))
      
      doc.Regenerate()
      
      	
      #modify pipe endpoints
      if pipe_endpoint_id[i] == 0:
      	new_pipeline = Autodesk.Revit.DB.Line.CreateBound(f_cons[primary_con_id].Origin, pipe[i].Location.Curve.GetEndPoint(1))
      	pipe[i].Location.Curve = new_pipeline
      	#debug2.append('a')
      else :
      	new_pipeline = Autodesk.Revit.DB.Line.CreateBound(pipe[i].Location.Curve.GetEndPoint(0), f_cons[primary_con_id].Origin)
      	pipe[i].Location.Curve = new_pipeline
      	#debug2.append('b')
      	
      doc.Regenerate()
    
      #duct_piping_system_type = pipe_connector[i].MEPSystem	
      #duct_piping_system_type = pipe[i].get_Parameter(BuiltInParameter.RBS_PIPING_SYSTEM_TYPE_PARAM).AsElementId()
      duct_piping_system_type = pipe[i].get_Parameter(BuiltInParameter.RBS_PIPING_SYSTEM_TYPE_PARAM).AsValueString()
      
      #Gir: Rentvann UV
      debug2.append(duct_piping_system_type)
      #Gir: Rentvann UV 4
      debug2.append(valve_connector[i].MEPSystem.Name)
      #Gir: 
      debug2.append(valve_connector[i].PipeSystemType.ToString())
      
      
      for k, sys in enumerate(list_piping_system_id):
      	if sys == duct_piping_system_type:
      		rorsystem = list_piping_system[k]
    
    
      #disconnect
      valve_connector[i].DisconnectFrom(pipe_connector[i])
      doc.Regenerate()
      
      
      #make new connections
      pipe_connector[i].ConnectTo(f_cons[primary_con_id])
      #doc.Regenerate()
      f_cons[secondary_con_id].ConnectTo(valve_connector[i])
    
    
      doc.Regenerate()
      TransactionManager.Instance.TransactionTaskDone()	
      
      debug2.append(new_flange)
    

    #OUT = debug2
    #OUT = EQ_families
    #OUT = debug4
    OUT = ‘done’

there is only one node in the dynamo-script, the python-node where the code is put

Hi @jorgen.fidjeland
Thank you I’ll try.
Cheers

Hi @jorgen.fidjeland
There is a little problem with the formatting of your python code…
Cheers

I don’t know how to fix it…

I attach the .dyn-file Autoflens.dyn (23.2 KB)

HI @jorgen.fidjeland
you cans use the Preformatted text button

Thank you for the .dyn I’ll try.
Cheers