Change length inside a list when connector is unconnected in python

Hi all,

I try to build a python script that check the connectors of the pipes.
The input of my python script are pipes and pipefittings. I have group this elements in a script before this script.

I try to get the length of the pipes and the fittings and sum them, but when I connector isn’t connect he need to change the value in 1500mm:

I have the connectors and I can check which connector is unconnect but I dont know how I can change this value.
Can someone help me?

BTW the “Alupex” is the Pipe

import clr #.NET Laden
import sys #sys is de fundamentele Python bibliotheek
#de standaard IronPython-bibliotheken
#sys.path.append('C:\Program Files (x86)\IronPython 2.7\Lib') #Imports the
#standaard IronPython-bibliotheken, die alles dekken, van servers en
#encryptie tot reguliere expressies.
import System #The System namespace in de hoofdmap .NET
from System import Array #.NET class voor het verwerken van array-informatie
import System.Collections.Generic as MGen #Module kan nu benaderd worden met MGen.xxxx
#from System.Collections.Generic import * #Hiermee kunt u generieke afhandelen. Revit's API
#soms wil hard-getypte 'generieke' lijsten, genaamd ILists. Als je niet nodig hebt
#deze kunt u deze regel verwijderen.
clr.AddReference('ProtoGeometry')  #Een Dynamo-bibliotheek voor zijn proxygeometrie
#classes. Je hebt dit alleen nodig als je interactie hebt met geometrie.
import Autodesk.DesignScript.Geometry as AGeo #Module kan worden opgeroepen a=met AGeo.xxxx
#from Autodesk.DesignScript.Geometry import * #Laadt alles in Dynamo's
#geometriebibliotheek
clr.AddReference("RevitNodes") #Dynamo's nodes voor Revit
import Revit #Laad in de Revit-namespaces in RevitNodes
clr.ImportExtensions(Revit.Elements) #Meer laden van Dynamo's Revit-bibliotheken
clr.ImportExtensions(Revit.GeometryConversion) #Meer laden van Dynamo's
#Revit-bibliotheken. Je hebt dit alleen nodig als je interactie hebt met geometrie.
clr.AddReference("RevitServices") #Dynamo's classes voor het omgaan met Revit-documenten
import RevitServices 
from RevitServices.Persistence import DocumentManager #Een interne Dynamo class
#dat het document bijhoudt waaraan Dynamo momenteel is gekoppeld
from RevitServices.Transactions import TransactionManager #Een Dynamo class voor
#transacties openen en sluiten om de database van het Revit-document te wijzigen

clr.AddReference("RevitAPI") #Verwijzing naar Revit's API DLL's toevoegen
clr.AddReference("RevitAPIUI") #Verwijzing naar Revit's APIUI DLL's toevoegen

import Autodesk #Loads the Autodesk namespace
import Autodesk.Revit.DB as RDB #Loading Revit's API UI classes module kan nu worden aangeroepen met RDB.xxxx
#from Autodesk.Revit.DB import * #Loading Revit's API UI classes
import Autodesk.Revit.UI as RUI # Loading Revit's API UI classes als RUI.xxxx
#from Autodesk.Revit.UI import * #Loading Revit's API UI classes

import math

doc = DocumentManager.Instance.CurrentDBDocument #Dit is het actieve Revit document
uiapp = DocumentManager.Instance.CurrentUIApplication #Een handle instellen voor het actieve Revit UI-document
app = uiapp.Application  #Een handle instellen op de momenteel geopende instantie van de Revit-toepassing
uidoc = uiapp.ActiveUIDocument #Een handle instellen op de momenteel geopende instantie van de Revit UI-toepassing
	
def GetName(ele):
	elename = None
 	try:
 		elename = ele.Name
 	except:
 		elename = RDB.Element.Name.__get__(ele)
 	return elename 
 	
# einde code omrekenen revit feet naar huidig ingestelde document units
# Hieronder kan je dan gaan programmeren
# Gebruik boiler template

elementen = UnwrapElement(IN[0])

#lengtes ophalen

lengtes = []
for e in elementen:
	for i in e:
		lengtes.append([])
		for a in i:
			namen = GetName(a)
			if namen.Contains("Alupex"):
				lengte = a.get_Parameter(RDB.BuiltInParameter.CURVE_ELEM_LENGTH).AsValueString()
				lengtes[-1].append(int(lengte))				
			else:
				lengte = a.LookupParameter("Length").AsValueString()
				lengtes[-1].append(int(lengte))	

#get pipe connectors
connectors = []
connlist = []
for pipe in elementen:
	for p in pipe:
		for i in p:
			try:
				conns = i.ConnectorManager.Connectors
			except:
				conns = i.MEPModel.ConnectorManager.Connectors
			for conn in conns:
				connlist.append(conn)

unconnectedlist = []
for c in connlist:
	if c.IsConnected:
		pass
	else:
		unconnectedlist.append(c)


section_lengte = []	

for l in lengtes:
	section_lengte.append([sum(l)])

OUT = lengtes, unconnectedlist

You probably want to check your connectors and gather your lengths in the same loop. Check for connectors first. If connected, get length. If unconnected, return 1500. It’s much harder to do one whole condition and then check again and modify.

1 Like

Yes that is really hard, I lost the list structure too.

I’m a beginner in python and check inside a loop is to hard for me.
I guess I will collect the unconnected pipes and set a value inside a parameter.
After that I will check that value (1500), if the value ==1500 than pass, else collect the length and sum the values.

Okay, I change my mind :slight_smile: Will try this tomorrow! Thanks!

@Nick_Boyts , I have almost the solution, I have changed the values inside the loop but when I try to set them inside the parameter I got an error:
image

elements and values structure list:
image
What do I miss?

elementen = UnwrapElement(IN[0])

TransactionManager.Instance.EnsureInTransaction(doc)
#connectors controleren en unconnected pipes over houden
lengtes = []

for pipe in elementen:
	for p in pipe:
		lengtes.append([])
		for i in p:
			namen = GetName(i)
			try:
				conns = i.ConnectorManager.Connectors
			except:
				conns = i.MEPModel.ConnectorManager.Connectors
			for conn in conns:
				if conn.IsConnected and namen.Contains("Alupex"):
					lengte = i.get_Parameter(RDB.BuiltInParameter.CURVE_ELEM_LENGTH).AsValueString()
					lengtes[-1].append(int(lengte))
				elif namen.Contains("Alupex"):
					lengte = 1500
					lengtes[-1].append(int(lengte))						
				else:
					lengte = i.LookupParameter("Length").AsValueString()
					lengtes[-1].append(int(lengte))
			section_lengte = []	
			for l in lengtes:
				s = 0
				section_lengte.append([[sum(l)]])
				i.LookupParameter("Section lengte").Set(section_lengte[s])
				s +=1

#einde transactie
TransactionManager.Instance.TransactionTaskDone()

OUT =  section_lengte

@jw.vanasselt any reason for nesting?
section_lengte.append([[ sum(l) ]])
do it like this:
section_lengte.append(sum(l)) and it should work:


@Elie.Trad thanks for you’re response

The problem is that I grouped my list in 2 steps.
In you’re situation it works but in this one it doesn’t:

I need to sum every section until a tee or another diameter.
So what I did is group all the elements by System name, after that I group them by Section.
The parameter Section does not exist in pipe fitting, I set a family parameter “Section” (same as the connected pipe) and than I group them by parameter “Section”

That is the reason I nested the list.

@jw.vanasselt I am trying to understand what you are after, getting rid of the sublist did work for the first branch but did not work for the second, what I understood is you want the summed length at every pipe from the start within the parameter (Section lengte), but I am not getting how are you calculating the length of the fitting, is it a pre-added value since the fitting is a “standard” item? if you can provide a sample Revit file with a Dynamo file to save me the trouble of guessing, maybe I can make it work

over here the sample files.
forum.dyn (25.1 KB)
Project1.rvt (1.8 MB)

I see a little problem in the first node, the section is not set the right way in the fittings.
When you run the script 2 times it will be fixed.

This still may not be the exact structure you’re looking for but this should be a much simpler process and get you close.

dataEnteringNode = IN
groups = UnwrapElement(IN[0])
lengths = []

for group in groups:
	groupOut = []
	for section in group:
		sectionOut = []
		sumLength = 0
		for element in section:
			try:
				conns = element.ConnectorManager.Connectors
			except:
				conns = element.MEPModel.ConnectorManager.Connectors
			for conn in conns:
                #use your requirements here
				if conn.IsConnected:
					try:
						length = element.LookupParameter("Length").AsDouble()
					except:
						length = 3
				else:
					length = 1500
                #sum your total lengths as you iterate through them
				sumLength = sumLength + length
			
			sectionOut.append(sumLength)
		groupOut.append(sectionOut)
	lengths.append(groupOut)

OUT = lengths
1 Like

Input a starting pipe, and then traverse through the system using AllRefs, appending each item to a list. Once you hit an element with more than 1 connector, iterate through the list to get the lengths, and get the sum. Make a note of the split (I appended it to a list named return_to), pick one of connectors, and do the same thing. Keep doing this until you hit something with an unused connector, and then go to your return_to list and start iterating through the other connectors. Repeat this until you’ve covered everything.
This code proooobably won’t work as is, but it should give you an idea.

segments = []
connected = [starting_pipe.Id]
next_elem = starting_pipe
return_to = []
i = 0
while True and i < 1000:
    i += 1 # Just a safety measure here. Once you're confident in the code feel free to remove it.
    cons = next_elem.ConnectorManager.Connectors
    ar = [x.AllRefs for x in cons]
    owners = [x.Owner for x in ar]
    n = next(x for x in owners if x.Id not in connected)
    connected.append(n.Id)
    num_cons = len(n.ConnectorManager.Connectors)
    if num_cons > 1:
        return_to.append(n)
        segments.append(connected)
        # If you wanted you could get your sums here
        connected = []
        next_elem = n
    elif n.ConnectorManager.UnusedConnectors:
        if len(return_to) == 0:
            break
        else:
            segments.append(connected)
            connected = []
            next_elem = return_to.pop()
    else:
        next_elem = n
# If you didn't get your sums earlier you could also do it here
sums = []
param = RDB.BuiltInParameter.CURVE_ELEM_LENGTH
for sublist in segments:
    lengths = [x.get_Parameter(param).AsDouble() for x in sublist]
    sum = sum(lengths)
    sums.append(sum)
combined = zip(segments,sums)

Hopefully that makes some sense. I have actual code written out that more or less already does this, but I won’t be able to reference it for about another week. So if you’re still working on it by then I could probably be of more help.

You could also try checking out this post from The Building Coder for some direction.

2 Likes

Thanks a lot @Nick_Boyts and @stewart.skyler
Now I can continue with the script and hopefully find the solution.

Yes than I’m still working with it, I have this script already in dynamo but I want to rebuild it in python.
This is what I do in the dynamo script:

  1. Group by building number
  2. Group by system name
  3. Get section from connected pipe and set parameter in elbows
  4. Group by sections
  5. Check connectors, when Is unconnected the length will be 1500.
  6. Sum all the length of the elbows and pipes of a section (included extra length)
  7. Set a parameter with this length.

You have your work cut out for you, I can tell you that much. :wink:

I won’t write it for you, but here a couple functions I’ve already made that you might be able to use/alter.

def groupby(list, func):
    keys = []
    group = []
    for item in list:
        key = func(item)
        if not key in keys:
            keys.append(key)
            group.append([item])
        else:
            idx = keys.index(key)
            group[idx].append(item)
    return group

This is looking for a function to use to group with. I’ve just been using a lambda function that looks up a parameter value. For example
groupby(elem_list, lambda x: x.LookupParameter("Building Name").AsString())
but you could define a new function and put it in there instead.

And here’s are my two functions for getting everything connected to a select element.

def get_cm(item):
	try:
		return item.ConnectorManager
	except:
		return item.MEPModel.ConnectorManager
		
def all_connected(start):
    connected, return_to = [start.Id], []
    i = 0
    cm = get_cm(start)
    
    while True and i<100:
        i+=1
        a_con = list(cm.Connectors)
        a_r = [x.AllRefs for x in a_con]
        a_r = [x for sublist in a_r for x in sublist]
        owners = [x.Owner.Id for x in a_r]
        elems = [x for x in owners if not x in connected]
        if not elems:
            if not return_to:
                return connected
            else:
                next_e = return_to.pop()
        else:
            if len(elems) > 1:
                next_e = elems.pop()
                return_to += elems
            else:
                next_e = elems.pop()
        connected.append(next_e)
        cm = get_cm(doc.GetElement(next_e))
1 Like

Haha yes I know, I changed the way. I started with check the connectors. When it is unconnected a fill a parameter with the value 1500.
Later I check this value in an other script like this (really simple but it works)