i try to use that node to insert valve in the middle of pipe but unsuccessful, related Python node is extracted and displayed error message so i tried list as input but same error message, i may miss something simple, any insight?
The error is saying the code is trying to iterate over something that is not iterable. I believe this is due to your points. The node seems to be asking for a list of points and you’re providing a singular structure. Just wrap your point in a list and it should work.
Hi,
MEPover has not been updated to CPython3 (or IronPython3 or PythonNet3).
You must:
- either update the Python code
- or use another package such as OpenMEP
- or use IronPython2 engine (as a last resort)
thanks c.poupin, both IronPython2 and CPython3 won’t work.
Yeah as @c.poupin say, mepover isnt upgraded for cpython 3, but guess your warning say "iteration over non sequence, try input it as list and could probably work or try this one here, should work with cpython 3

import clr
import math
import traceback
clr.AddReference("RevitAPI")
clr.AddReference("RevitServices")
from RevitServices.Persistence import DocumentManager
from RevitServices.Transactions import TransactionManager
from Autodesk.Revit.DB import *
doc = DocumentManager.Instance.CurrentDBDocument
import Revit
clr.ImportExtensions(Revit.Elements)
clr.ImportExtensions(Revit.GeometryConversion)
from System.Collections.Generic import List
ducts = UnwrapElement(IN[0]) if isinstance(IN[0], list) else [UnwrapElement(IN[0])]
points = IN[1] if isinstance(IN[1], list) else [IN[1]]
famtype = UnwrapElement(IN[2]) if isinstance(IN[2], list) else [UnwrapElement(IN[2])]
ftl = len(famtype)
def IsParallel(dir1, dir2):
if dir1.Normalize().IsAlmostEqualTo(dir2.Normalize()):
return True
if dir1.Normalize().Negate().IsAlmostEqualTo(dir2.Normalize()):
return True
return False
def measure(startpoint, point):
return startpoint.DistanceTo(point)
def copyElement(element, oldloc, loc):
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])
return newelement
def GetDirection(faminstance):
for c in faminstance.MEPModel.ConnectorManager.Connectors:
conn = c
break
return conn.CoordinateSystem.BasisZ
def GetClosestDirection(faminstance, lineDirection):
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 None
tempfamtype = None
xAxis = XYZ(1, 0, 0)
def placeFitting(duct, point, familytype, lineDirection):
global tempfamtype
toggle = False
isVertical = False
if tempfamtype is None:
tempfamtype = familytype
toggle = True
elif tempfamtype != familytype:
toggle = True
tempfamtype = familytype
level = duct.ReferenceLevel
width = 4
height = 4
round = False
connectors = duct.ConnectorManager.Connectors
for c in connectors:
if c.ConnectorType != ConnectorType.End:
continue
shape = c.Shape
if shape == ConnectorProfileType.Round:
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
point = XYZ(point.X, point.Y, point.Z - level.Elevation)
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
for conn in famconns:
if IsParallel(lineDirection, conn.CoordinateSystem.BasisZ):
connpoints.append(conn.Origin)
result[newfam] = connpoints
return result
def ConnectElements(duct, fitting):
ductconns = duct.ConnectorManager.Connectors
fittingconns = fitting.MEPModel.ConnectorManager.Connectors
for conn in fittingconns:
for ductconn in ductconns:
if ductconn.Origin.IsAlmostEqualTo(conn.Origin):
ductconn.ConnectTo(conn)
break
def SortedPoints(fittingspoints, ductStartPoint):
return sorted(fittingspoints, key=lambda x: measure(ductStartPoint, x))
ductsout = []
fittingsout = []
combilist = []
TransactionManager.Instance.EnsureInTransaction(doc)
for typ in famtype:
if not typ.IsActive:
typ.Activate()
doc.Regenerate()
for i, duct in enumerate(ducts):
ListOfPoints = [x.ToXyz() for x in points[i]]
familytype = famtype[i % ftl]
ductline = duct.Location.Curve
ductStartPoint = ductline.GetEndPoint(0)
ductEndPoint = ductline.GetEndPoint(1)
pointlist = SortedPoints(ListOfPoints, ductStartPoint)
ductlist = []
newFittings = []
ductlist.append(duct)
tempStartPoint = None
tempEndPoint = None
lineDirection = ductline.Direction
for i, p in enumerate(pointlist):
output = placeFitting(duct, p, familytype, lineDirection)
newfitting = list(output.keys())[0]
newFittings.append(newfitting)
fittingpoints = list(output.values())[0]
tempPoints = SortedPoints(fittingpoints, ductStartPoint)
if i == 0:
tempEndPoint = tempPoints[0]
tempStartPoint = tempPoints[1]
newduct = copyElement(duct, ductStartPoint, tempStartPoint)
duct.Location.Curve = Line.CreateBound(ductStartPoint, tempEndPoint)
ductlist.append(newduct)
combilist.append([duct, newfitting])
combilist.append([newduct, newfitting])
else:
combilist.append([newduct, newfitting])
tempEndPoint = tempPoints[0]
newduct = copyElement(duct, tempStartPoint, tempEndPoint)
ductlist[-1].Location.Curve = Line.CreateBound(tempStartPoint, tempEndPoint)
tempStartPoint = tempPoints[1]
ductlist.append(newduct)
combilist.append([newduct, newfitting])
ductlist[-1].Location.Curve = Line.CreateBound(tempStartPoint, ductEndPoint)
ductsout.append(ductlist)
fittingsout.append(newFittings)
TransactionManager.Instance.TransactionTaskDone()
TransactionManager.Instance.EnsureInTransaction(doc)
for combi in combilist:
ConnectElements(combi[0], combi[1])
TransactionManager.Instance.TransactionTaskDone()
OUT = (ductsout, fittingsout)
In addition to @sovitek 's answer, It seems to me that the list of points should be a list of lists (one sublist per duct/pipe)
@c.poupin yes ![]()
and you could try this one here before feed in your points, then it should work for both single and multiple points, and dont need list.create and 2 x list create for single point..
data = IN[0]
if not isinstance(data, list):
data = [[data]]
elif not isinstance(data[0], list):
data = [data]
OUT = data
thanks sovitek and c.poupin, list of list!
Hi,
Here is a revised version with IronPython3 or PythonNet3 (probably works with IronPython2, but untested).
Python Code
import clr
import sys
import System
from System.Collections.Generic import List
#
clr.AddReference('ProtoGeometry')
from Autodesk.DesignScript.Geometry import *
import Autodesk.DesignScript.Geometry as DS
#import Revit API
clr.AddReference('RevitAPI')
import Autodesk
from Autodesk.Revit.DB import *
import Autodesk.Revit.DB as DB
#import specify namespace
from Autodesk.Revit.DB.Plumbing import *
from Autodesk.Revit.DB.Mechanical import *
from Autodesk.Revit.DB.Structure import *
clr.AddReference('RevitNodes')
import Revit
clr.ImportExtensions(Revit.GeometryConversion)
clr.AddReference('RevitServices')
import RevitServices
from RevitServices.Persistence import DocumentManager
from RevitServices.Transactions import TransactionManager
doc = DocumentManager.Instance.CurrentDBDocument
import math
def get_shape(lst, shape=()):
"""
returns the shape of nested lists
"""
if not hasattr(lst, "__iter__") or isinstance(lst, (str, System.String)):
# base case
return {"shape":shape, "ndim":len(shape)}
if hasattr(lst[0], "__iter__") and not isinstance(lst[0], (str, System.String)):
l = len(lst[0])
shape += (len(lst), )
# recurse
shape = get_shape(lst[0], shape)
return shape #{"shape":shape, "ndim":len(shape)}
def get_order_connectors(elemA, elemB, elemC):
test = elemA
lst = [elemA, elemB, elemC]
# create group by direction
groupA = [i for i in lst if abs(test.Location.Curve.Direction.CrossProduct(i.Location.Curve.Direction).Z) < 0.001]
groupB = [i for i in lst if abs(test.Location.Curve.Direction.CrossProduct(i.Location.Curve.Direction).Z) > 0.001]
lstGroup = [groupA, groupB]
# sort list of group by len
lstGroup.sort(key = len, reverse = True)
# flat the group : the last element is the branch
lst_sort_elem = sum(lstGroup, [])
lst_curve_elems = [i.Location.Curve for i in lst_sort_elem]
orderConnectors = []
# get closest connectors
for idxA, elem in enumerate(lst_sort_elem):
other_curve = [c for idxB, c in enumerate(lst_curve_elems) if idxA != idxB][0]
print(other_curve)
closest_connectors = sorted(elem.ConnectorManager.Connectors, key = lambda c : other_curve.Project(c.Origin).Distance)
orderConnectors.append(closest_connectors[0])
#
return orderConnectors
def get_FamilyInstance_direction(elem):
connectors = [c for c in elem.MEPModel.ConnectorManager.Connectors if c.Domain in [Domain.DomainPiping, Domain.DomainHvac]]
i = 0
groups_arr = [ [connectors.pop(0)] ]
while connectors and i < 10:
i += 1
con = connectors.pop(0)
is_placed = False
for idx, group in enumerate(groups_arr):
# if connector CoordinateSystem is parrallel add to current group
if con.CoordinateSystem.BasisZ.CrossProduct(group[-1].CoordinateSystem.BasisZ).GetLength() < 0.02:
group.append(con)
is_placed = True
if not is_placed :
groups_arr.append([con])
#
print(f"{groups_arr=}")
main_connectors = next((g for g in groups_arr if len(g) == 2), None)
if main_connectors is not None:
pta, ptb = sorted([c.Origin for c in main_connectors], key = lambda p : p.X)
pta, ptb = [c.Origin for c in main_connectors]
return DB.Line.CreateBound(pta, ptb).Direction.Normalize(), main_connectors
return None, []
def toList(x):
if isinstance(x, (list, dict)) or \
(hasattr(x, "GetType") and x.GetType().GetInterface("ICollection") is not None):
return x
else : return [x]
#Preparing input from dynamo to revit
lst_elemCurves = [x for x in toList(UnwrapElement(IN[0])) if isinstance(x.Location, LocationCurve)]
lst_elemCurveIds = [x.Id for x in lst_elemCurves]
arr_arr_DSPoints = toList(IN[1])
arr_arr_DSPoints[0] = toList(arr_arr_DSPoints[0])
#
# check if data input structure is correct
shape_Elem = get_shape(lst_elemCurves)
shape_DSPoints = get_shape(arr_arr_DSPoints)
#
if shape_Elem["ndim"] == 1\
and shape_DSPoints["ndim"] == 2\
and shape_Elem["shape"][0] == shape_DSPoints["shape"][0]:
pass
else:
raise Exception("wrong list input structure")
fitting_type = UnwrapElement(IN[2])
out_fitting = []
debug = []
#
is_tee = fitting_type.Family.get_Parameter(BuiltInParameter.FAMILY_CONTENT_PART_TYPE).AsInteger() == 6
#Do some action in a Transaction
TransactionManager.Instance.EnsureInTransaction(doc)
for array_DSPts, elem in zip(arr_arr_DSPoints, lst_elemCurves):
# get datas from Curve
c_system = list(elem.ConnectorManager.Connectors)[0].CoordinateSystem
curve_elem = elem.Location.Curve.Clone()
curve_direction = curve_elem.Direction.Normalize()
level = doc.GetElement(elem.LevelId)
#
array_pts = [p.ToXyz() for p in array_DSPts]
array_pts.sort(key = lambda p : p.DistanceTo(curve_elem.GetEndPoint(0)) )
elemId = elem.Id
#
for pt in array_pts:
newfam = None
try:
newId = MechanicalUtils.BreakCurve(doc, elemId, pt)
except Exception as ex:
print(ex)
newId = PlumbingUtils.BreakCurve(doc, elemId, pt)
#
doc.Regenerate()
elemA = doc.GetElement(elemId)
curveA = elemA.Location.Curve
elemB = doc.GetElement(newId)
curveB = elemB.Location.Curve
try :
v_axis = curve_direction.CrossProduct(XYZ.BasisX)
axis = Line.CreateUnbound(pt, v_axis)
except:
v_axis = curve_direction.CrossProduct(XYZ.BasisY)
axis = Line.CreateUnbound(pt, v_axis)
# tee creation
if is_tee :
# create temapary the third element (branch)
elemCId = list(DB.ElementTransformUtils.CopyElement(doc, elemA.Id, XYZ.Zero))[0]
elemC = doc.GetElement(elemCId)
# rotate if verticale
if abs(c_system.BasisZ.Z) > 0.99:
axis = Line.CreateUnbound(pt, c_system.BasisY)
DB.ElementTransformUtils.RotateElement(doc, elemCId, axis, math.pi / 2)
else:
DB.ElementTransformUtils.RotateElement(doc, elemCId, axis, math.pi / 2)
#
doc.Regenerate()
conA, conB, conC = get_order_connectors(elemA, elemB, elemC)
newfam = doc.Create.NewTeeFitting(conA, conB, conC)
newfam.ChangeTypeId(fitting_type.Id)
out_fitting.append(newfam)
# delete the temp branch
doc.Delete(elemCId)
#
# accessory creation
else:
if not fitting_type.IsActive:
fitting_type.Activate()
newfam = doc.Create.NewFamilyInstance(pt, fitting_type, level, StructuralType.NonStructural)
out_fitting.append(newfam)
doc.Regenerate()
# get direction of familyInstance by connectors position
v1, connectors = get_FamilyInstance_direction(newfam)
print(f"v1={v1.ToString()}")
if v1 is not None:
# check if vectors are Clockwise
factor = 1 if v1.CrossProduct(curve_direction).Z > 0.01 else -1
angle = v1.AngleTo(curve_direction)
# align direction of familyinstance
## rotate if verticale
if abs(c_system.BasisZ.Z) > 0.99 :
DB.ElementTransformUtils.RotateElement(doc, newfam.Id, axis, factor * angle )
factorX = 1 if c_system.BasisX.CrossProduct(XYZ.BasisX).Z < 0 else -1
DB.ElementTransformUtils.RotateElement(doc, newfam.Id, curve_elem, factorX * c_system.BasisX.AngleTo(XYZ.BasisX))
## if horizontal redifine the axis
else:
axis = Line.CreateBound(pt, pt + XYZ(0,0,1))
DB.ElementTransformUtils.RotateElement(doc, newfam.Id, axis, factor * angle )
#
doc.Regenerate()
# connect connectors to pipes/ducts
for con in connectors:
for curv_, elem_ in [[curveA, elemA], [curveB, elemB]]:
interR = curv_.Project(con.Origin)
#print(curv_.ComputeNormalizedParameter(interR.Parameter))
#
if interR is not None and 0.005 < curv_.ComputeNormalizedParameter(interR.Parameter) < 0.995:
closest_connectors = sorted(elem_.ConnectorManager.Connectors, key = lambda c : c.Origin.DistanceTo(con.Origin))
closest_connector = closest_connectors[0]
closest_connector.ConnectTo(con)
closest_connector.Origin = con.Origin
TransactionManager.Instance.TransactionTaskDone()
OUT = lst_elemCurves, out_fitting


