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:
#Copyright(c) 2016, Dimitar Venkov # @5devene, email@example.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) 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) fam_path = IN names = tolist(IN) category = tolist(IN) material = tolist(IN) isVoid = tolist(IN) subcat = tolist(IN) 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, padded, padded, padded 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 for i in return1], [i for i in return1] satOpt.Dispose() opt1.Dispose() SaveAsOpt.Dispose()
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…
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.
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.
I do this…
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.
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 =
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.
Have you tried the FamilyType.ByGeometry node?
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.
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.