FamilyInstance.ByGeometry not working in Revit 2022

You’d have to at a minimum show what the code around line 140-150 is. Even better would be to show the whole graph. I am wondering if you failed to provide a name for the new family instance so it is unable to save/close a file that it creates in the background.

First, thank you for replying so fast…
I’m sorry, here it is the simple graph and the python code:

image

#Copyright(c) 2016, Dimitar Venkov
# @5devene, dimitar.ven@gmail.com

import clr
import System
from System.Collections.Generic import *

from itertools import repeat

pf_path = System.Environment.GetFolderPath(System.Environment.SpecialFolder.ProgramFilesX86)
import sys
sys.path.append('%s\IronPython 2.7\Lib' %pf_path)
import traceback

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

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

clr.AddReference('RevitAPI')
from Autodesk.Revit.DB import *
from Autodesk.Revit.DB.Structure import StructuralType

clr.AddReference('RevitNodes')
import Revit
clr.ImportExtensions(Revit.Elements)
clr.ImportExtensions(Revit.GeometryConversion)

def tolist(obj1):
	if hasattr(obj1,'__iter__'): return obj1
	else: return [obj1]

def PadLists(lists):
	len1 = len(lists[0])
	for i in xrange(1,len(lists)):
		len2 = len(lists[i])
		if len2 == len1 : continue
		elif len2 > len1: lists[i] = lists[i][:len1]
		else : lists[i].extend(repeat(lists[i][-1],len1 - len2))
	return lists

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

geom = tolist(IN[0])
fam_path = IN[1]
names = tolist(IN[2])
category = tolist(IN[3])
material = tolist(IN[4])
isVoid = tolist(IN[5])
subcat = tolist(IN[6])

units = doc.GetUnits().GetFormatOptions(SpecTypeId.Length).GetUnitTypeId()
factor = UnitUtils.ConvertToInternalUnits(1,units)
acceptable_views = ('ThreeD', 'FloorPlan', 'EngineeringPlan', 'CeilingPlan', 'Elevation', 'Section')
origin = XYZ.Zero
str_typ = StructuralType.NonStructural
t1 = TransactionManager.Instance
temp_path = System.IO.Path.GetTempPath()
invalid_chars = System.IO.Path.GetInvalidFileNameChars()
satOpt = SATImportOptions()
satOpt.Placement = ImportPlacement.Origin
satOpt.Unit = ImportUnit.Custom
satOpt.CustomScale = 1/3.280839895
opt1 = Options()
opt1.ComputeReferences = True
SaveAsOpt = SaveAsOptions()
SaveAsOpt.OverwriteExistingFile = True

def NewForm_background(s1, name1, cat1, isVoid1, mat1, subcat1):
	try:
		enable_mat = False if mat1 is None else True
		enable_subcat = False if subcat1 is None else True
		if any( (c in name1 for c in invalid_chars) ):
			raise Exception('Family name contains invalid characters')
		TransactionManager.ForceCloseTransaction(t1)
		famdoc = doc.Application.NewFamilyDocument(fam_path)
		sat_path = '%s%s.sat' % (temp_path, name1)
		if factor != 1:
			s1 = s1.Scale(factor)
		vec1 = Vector.ByTwoPoints(BoundingBox.ByGeometry(s1).MinPoint, DynPoint.Origin())
		s1 = s1.Translate(vec1)
		sat1 = Geometry.ExportToSAT(s1, sat_path)
		view_fec = FilteredElementCollector(famdoc).OfClass(View)
		view1 = None
		for v in view_fec:
			if str(v.ViewType) in acceptable_views and not v.IsTemplate:
				view1 = v
				break
		t1.EnsureInTransaction(famdoc)
		satId = famdoc.Import(sat1, satOpt, view1)
		el1 = famdoc.GetElement(satId)
		geom1 = el1.get_Geometry(opt1)
		enum = geom1.GetEnumerator()
		enum.MoveNext()
		geom2 = enum.Current.GetInstanceGeometry()
		enum2 = geom2.GetEnumerator()
		enum2.MoveNext()
		s1 = enum2.Current
		famdoc.Delete(satId)
		System.IO.File.Delete(sat_path)

		save_path = '%s%s.rfa' % (temp_path, name1)
		try: #set the category
			fam_cat = famdoc.Settings.Categories.get_Item(cat1.Name)
			famdoc.OwnerFamily.FamilyCategory = fam_cat
		except: pass
		s2 = FreeFormElement.Create(famdoc,s1)
		if isVoid1:
			void_par = s2.get_Parameter(BuiltInParameter.ELEMENT_IS_CUTTING).Set(1)
			void_par2 = famdoc.OwnerFamily.get_Parameter(BuiltInParameter.FAMILY_ALLOW_CUT_WITH_VOIDS).Set(1)
		else: #voids do not have a material values or a sub-cateogry
			if enable_mat:
				try:
					mat_fec = FilteredElementCollector(famdoc).OfClass(Material)
					for m in mat_fec:
						if m.Name == mat1:
							fam_mat = m
							break
					mat_par = s2.get_Parameter(BuiltInParameter.MATERIAL_ID_PARAM).Set(fam_mat.Id)
				except: pass
			if enable_subcat: #create and assign the sub-category:
				try:
					current_fam_cat = famdoc.OwnerFamily.FamilyCategory
					fam_cat_subs = current_fam_cat.SubCategories
					if fam_cat_subs.Contains(subcat1):
						new_subcat = fam_cat_subs[subcat1]
					else:
						new_subcat = famdoc.Settings.Categories.NewSubcategory(current_fam_cat, subcat1)
					s2.Subcategory = new_subcat
				except: pass
		TransactionManager.ForceCloseTransaction(t1)
		famdoc.SaveAs(save_path, SaveAsOpt)
		family1 = famdoc.LoadFamily(doc, FamOpt1() )
		famdoc.Close(False)
		System.IO.File.Delete(save_path)
		symbols = family1.GetFamilySymbolIds().GetEnumerator()
		symbols.MoveNext()
		symbol1 = doc.GetElement(symbols.Current)
		t1.EnsureInTransaction(doc)
		if not symbol1.IsActive: symbol1.Activate()
		inst1 = doc.Create.NewFamilyInstance(origin, symbol1, str_typ)
		ElementTransformUtils.MoveElement(doc,inst1.Id, vec1.Reverse().ToXyz() )
		TransactionManager.ForceCloseTransaction(t1)
		return inst1.ToDSType(False), family1.ToDSType(False)
		
	except:
		return traceback.format_exc(),''

if len(geom) == len(names) == len(category) == len(isVoid) == len(material) == len(subcat):
	return1 = map(NewForm_background, geom, names, category, isVoid, material, subcat)
elif len(geom) == len(names):
	padded = PadLists([geom, category, isVoid, material,subcat])
	p_category, p_isVoid, p_material, p_subcat = padded[1], padded[2], padded[3], padded[4]
	return1 = map(NewForm_background, geom, names, p_category, p_isVoid, p_material, p_subcat)
else : return1 = [('Make sure that each geometry\nobject has a unique family name.', '')]
OUT = [i[0] for i in return1], [i[1] for i in return1]
satOpt.Dispose()
opt1.Dispose()
SaveAsOpt.Dispose()
  1. You may need other satOpt.Unit and satOpt.CustomScale settings - I’m in US so I had to do a metric to imperial conversion (1/3.2808) - not your issue but I saw you using metric family so thought I’d point that out…

  2. Are you running this from a family or from a project? Hopefully from a project because the script would be trying to make a family called MassWall.rfa and if you have a family open already with that name (or worse you are running it from the family with that name) I could see that not working out nicely.

One thing you might find helpful (it helps me when I’m troubleshooting) is to copy/paste the contents of the custom node out and put them in the main dyn. then freeze the custom node and work on the python script in the main dyn (you’ll often get more info on what the issue is when the python fails). Then when you get it succesfully running - just copy the python code and paste it back into the custom node.

1 Like

Hi Ben / Forum

I am having the same issue with the spring nodes FamilyInstance.ByGeometry node. On simple geometry it works ok, but when I try more complex geometry I get the mesh issue you described in an earlier post on this thread.

Can I ask what you suggest at this moment in time as a workflow to get complex dynamo geometry into Revit? Do you think this node will be updated, or did you find a solution? Sorry if I missed this.
I tried replacing the python code as you suggested with no luck.

I received the same warning:

File ", line119, in NewForm_background
TypeError: expected Solid, got Mesh

Any help would be greatly appreciated.

Many thanks
Tim

I do this…

1 Like

Hi Ben

Thank you very much for your reply. Yes you are right this works really well - the spring nodes node works so well in 2020, I really hope this can be updated or a good OOTB node can be made for geometry into Revit for 2022 + going forwards.

Thanks again
Tim

Tim-
That would be awesome if someone could come up with a fix - but it would surely be complex because it would require the instance of Dynamo that is running in Revit 2022 to fire up an instance of Revit 2020 and do the family creation in that version, then save the family and have it then load into Revit 2022. Seems very difficult (getting Dynamo to operate on multiple versions of Revit simultaneously).

Bottom line - something either in the methodology that Revit is using to convert the .sat into a DirectShape in the Revit Family or something that Revit is using to export the .sat has changed from previous versions in that the same .sat will be a solid in Revit 2020 but a mesh in Revit 2022 (in some instances). My suspicion (and this could only likely be confirmed by someone on the Revit development side) is that there was a “face count” limit placed in Revit 2022 that automatically makes that import as a mesh if the number of faces exceeds some threshold limit - leaving us kind of without much option except a clunky workaround of going back to the old version that worked in a much friendlier manner.

Below is a link to a .sat file generated out of Revit 2022 if anyone (…“cough” Autodesk employed “cough”…) wants to test and confirm this behavior. Import this .sat into a Revit 2020 family and you get a “Solid”. Import it to a Revit 2022 family and you get “Mesh”. Same .sat = different results. Inconsistency = :frowning:

Hi Ben

Thank you again for this. I wasnt actually thinking/suggesting anyone should make a workaround fix, but hoping the Spring Nodes node could be updated to work with the Revit 2022 changes, or as you say Autodesk come up with a proper OOTB node for this workflow - it really limits Dynamo/Revit for modelling, compared to using other softwares.

Thanks again
Tim

Have you tried the FamilyType.ByGeometry node?

https://dictionary.dynamobim.com/#/Revit/Elements/FamilyType/Create/ByGeometry

it probably would be possible to make the Spring Nodes code work in 2022 for all cases - but the import would still be a mesh and not a Solid - so you’d end up with Mesh behavior (Volume = 0, filter and category color overrides wouldnt work, etc). My opinion is that the current 2022 behavior is wrong - it should not arbitrarily turn that geometry into a Mesh in a newer version when it always was a Solid previously.

Jacob-
I have not tried that node - but my suspicion is that if it is using the same SATimportOptions API methods as the Springs Node - then there wouldn’t be any different result. The resultant geometry would still be a mesh in 2022 and a solid in 2020. You don’t even need Dynamo to test this - just create new family and import that .sat I uploaded.

In general - Solids are always preferable to Meshes in Revit because they behave in predictable manners (edges and faces are always dimensionable, VG and filter color overrides actually change the colors, they report volumes, etc…) - and the root issue here really has nothing to do with Dynamo - it’s a change in the base Revit behavior that a lot of people were leveraging to get Dynamo geometry into Revit.

I can confirm it is also using an SAT import method, but the means in which that happens may differ between the Revit node to the springs node due to the changes in the Dynamo for Revit code base being more recent than the Springs equivalent.

Narrowing down the issue to a particular version which introduced the change in behavior (ie: works in 2020 but not 2021 or 2022) and limiting to the out of the box nodes (the FamilyType.ByGeometry) and submitting an issue to the Dynamo for Revit GitHub (Issues · DynamoDS/DynamoRevit · GitHub) is the best way to get this addressed.