Python - output aligning to multiple view input

Hi,

This follows on from my other thread i made. (thanks again for the help! :+1:)

https://forum.dynamobim.com/t/python-get-element-family-name-then-apply-a-filter/38261/3

So i tried to get it working with multiple view inputs, which works to some extent.

i can get the fittings per view. 14 on the first view, 2 on the second. that’s correct.:heavy_check_mark:

Then i put in the code from the MEPOver package (@T_Pover), to find the connected elements and it comes out as the same number but in one list.

get%20ducts5

my question really is how do i get the duct output to match the fittings output?

i was thinking it should be be “for s in sfittings” not “for s in sfitting” but this gives an error about not having MEP connectors.

i tried changing the indents which gave some different results and adding a zip, but i could’nt get it to work either.

import clr

#Import Revit Nodes
clr.AddReference("RevitNodes")
import Revit
clr.ImportExtensions(Revit.Elements)

# Import RevitAPI
clr.AddReference("RevitAPI")
import Autodesk
from Autodesk.Revit.DB import *

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

# Import DocumentManager and TransactionManager
clr.AddReference("RevitServices")
from RevitServices.Persistence import DocumentManager
from RevitServices.Transactions import TransactionManager

clr.AddReference ("DSCoreNodes")
import DSCore
from DSCore import *

doc = DocumentManager.Instance.CurrentDBDocument

def tolist(obj):
	if isinstance(obj, list):
		return UnwrapElement(obj)
	else:
		return [UnwrapElement(obj)]

views = tolist(IN[0])
ducts = []
sfittings = []
sducts = []

#collection filter FamilyInstance in view list
for view in views:
	sfitting = [x for x in FilteredElementCollector(doc, view.Id).OfCategory(BuiltInCategory.OST_DuctFitting).ToElements() if "Shoe" in x.Symbol.FamilyName]
	sfittings.append(sfitting)
	for s in sfitting:
		connset = s.MEPModel.ConnectorManager.Connectors
		conn_pipes = []
		for c in connset:
			if c.IsConnected:
				for sc in c.AllRefs:
					conn_pipes.append(sc.Owner)
		sducts.append(conn_pipes)
	

OUT = sfittings, sducts
#OUT = List.Flatten(shoe_ducts,2), List.Flatten(taper_ducts,2)#,  

could anyone offer any advice?
Thanks,

@c.poupin @cgartland

Hello you can try this (not test)

ducts = []
sfittings = []
sducts = []    
#collection filter FamilyInstance in view list
for view in views:
    sfitting = [x for x in FilteredElementCollector(doc, view.Id).OfCategory(BuiltInCategory.OST_DuctFitting).ToElements() if "Shoe" in x.Symbol.FamilyName]
    #joinlist
    sfittings += sfitting
for s in sfittings:
    connset = s.MEPModel.ConnectorManager.Connectors
    conn_pipes = []
    for c in connset:
        if c.IsConnected:
            for sc in c.AllRefs:
                conn_pipes.append(sc.Owner)
    sducts.append(conn_pipes)

or make a flat list

for view in views:
	sfitting = [x for x in FilteredElementCollector(doc, view.Id).OfCategory(BuiltInCategory.OST_DuctFitting).ToElements() if "Shoe" in x.Symbol.FamilyName]
	sfittings.append(sfitting)
flat_sfittings = [item for sublist in sfittings for item in sublist]
for s in flat_sfittings:
.....
1 Like

why collect the elements by view? (“all views” in the previous python node) Is not it easier to collect all the elements of the project in this case?

sfitting = [x for x in FilteredElementCollector(doc).OfCategory(BuiltInCategory.OST_DuctFitting).ToElements() if "Shoe" in x.Symbol.FamilyName]

1 Like

If you want to create a list per view (with sublists of your fittings) then you have to create a new list on every iteration of a view to which you can append your ‘conn_pipes’. Right now you are appending everything to the ‘sducts’ list, which will accumulate everything from every iteration.

3 Likes

Thank for your replies guys,

I tried the first solution and i.m getting matching list now, except they are both in one list, I want to keep them in separate lists per view so as to tag them in multiple sheets later on.

get%20ducts6

@T_Pover, so would i need to do something like “for sfittings in view” in between “view in views” and “s in sfittings”?

You could do something like this:

views = tolist(IN[0])
ducts = []
sfittings = []
sducts = []


#collection filter FamilyInstance in view list
for view in views:
	sfitting = [x for x in FilteredElementCollector(doc, view.Id).OfCategory(BuiltInCategory.OST_DuctFitting).ToElements()]
	sfittings.append(sfitting)
	connected_fittings = []
	for s in sfitting:
		connset = s.MEPModel.ConnectorManager.Connectors
		conn_pipes = []
		for c in connset:
			if c.IsConnected:
				for sc in c.AllRefs:
					conn_pipes.append(sc.Owner)
		connected_fittings.append(conn_pipes)
	sducts.append(connected_fittings)
	

OUT = sfittings, sducts

Where I have added a variable ‘connected_fittings’ that will be filled with the fittings per view. Then append that variable to your sducts. That way you will get what you want (I think :))

1 Like

Ok this is great, thank you so much @T_Pover. its working and i think im understanding why now too!
I left in the family name filter from @c.poupin
for view in views:

for view in views:
	sfitting = [x for x in FilteredElementCollector(doc, view.Id).OfCategory(BuiltInCategory.OST_DuctFitting).ToElements() if "Taper" in x.Symbol.FamilyName]
	sfittings.append(sfitting)
	connected_fittings = []
	for s in sfitting:

it was the 2nd and 3rd line, i didn’t quite get why sfitting wasn’t outputting per view. It was outputting the fittings on one view (the last view in the list). then sfittings is doing it for each view because its on the view indent.

connected_fittings is outputting the last view to, and because sducts is aligned to the view indent it does it for each view.

does that sound about right?:thinking:

So i changed to filter to “Taper”, to get a situation like this, where i get a bend immediately after the “Taper”. in this instance i want to get the duct after the bend.

get%20ducts2

I tried adding something to filter out the ducts and fittings, but its giving me an empty list and a full list.

views = tolist(IN[0])
ducts = []
sfittings = []
sducts = []
ductlist = []
fittinglist = []

#collection filter FamilyInstance in view list
for view in views:
	sfitting = [x for x in FilteredElementCollector(doc, view.Id).OfCategory(BuiltInCategory.OST_DuctFitting).ToElements() if "Taper" in x.Symbol.FamilyName]
	sfittings.append(sfitting)
	connected_fittings = []
	for s in sfitting:
		connset = s.MEPModel.ConnectorManager.Connectors
		conn_pipes = []
		for c in connset:
			if c.IsConnected:
				for sc in c.AllRefs:
					conn_pipes.append(sc.Owner)
		connected_fittings.append(conn_pipes)
	sducts.append(connected_fittings)
	
	for duct in sducts:
		if duct == "Duct":
			ductlist.append(duct)
		else:
			fittinglist.append(duct)

OUT = ductlist, fittinglist
#OUT = sfittings, sducts

The idea is to apply the whole connected elements process again to the fittings, to get the ducts.

i know its probably notthe best way of going about, im thinking it should be added just after

conn_pipes.append(sc.Owner)

and say
if Owner == “Duct” :
connected_fitting.append(conn_pipes)
else:
#repeat connected element process:
connected_fitting.append(conn_pipes)

That way it might be able to keep the list structure…

I did try this but obviously i broke it straight away! :grimacing:

any advice!? :pray: