have you tried that one from rhythm ? or try as here with ironpython 3 should work 24-25-26 not sure with 22-23, but guess no matter what with custom or ootb the material parameter has to be in that template you open…i think ![]()
import clr
import os
import sys
import traceback
import System
pf_path = System.Environment.GetFolderPath(System.Environment.SpecialFolder.ProgramFilesX86)
sys.path.append(os.path.join(pf_path, 'IronPython 3.4', 'Lib'))
clr.AddReference('RevitAPI')
clr.AddReference('RevitServices')
clr.AddReference('ProtoGeometry')
clr.AddReference('RevitNodes')
import Revit
clr.ImportExtensions(Revit.Elements)
clr.ImportExtensions(Revit.GeometryConversion)
from Autodesk.DesignScript.Geometry import *
from Autodesk.DesignScript.Geometry import Point as DynPoint
from Autodesk.Revit.DB import *
from Autodesk.Revit.DB.Structure import StructuralType
from RevitServices.Persistence import DocumentManager
from RevitServices.Transactions import TransactionManager
doc = DocumentManager.Instance.CurrentDBDocument
tManager = TransactionManager.Instance
def tolist(obj):
return obj if hasattr(obj, '__iter__') and not isinstance(obj, str) else [obj]
def pad_lists(lists):
length = len(lists[0])
for lst in lists[1:]:
while len(lst) < length:
lst.append(lst[-1])
if len(lst) > length:
lst[:] = lst[:length]
return lists
class FamLoadOpt(IFamilyLoadOptions):
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])
categories = tolist(IN[3])
materials = tolist(IN[4])
subcats = tolist(IN[5])
if int(doc.Application.VersionNumber) > 2021:
units = doc.GetUnits().GetFormatOptions(ForgeTypeId("autodesk.spec.aec:length")).GetUnitTypeId()
else:
units = doc.GetUnits().GetFormatOptions(UnitType.UT_Length).DisplayUnits
factor = UnitUtils.ConvertToInternalUnits(1, units)
origin = XYZ.Zero
str_type = StructuralType.NonStructural
temp_path = System.IO.Path.GetTempPath()
invalid_chars = System.IO.Path.GetInvalidFileNameChars()
acceptable_views = ('ThreeD', 'FloorPlan', 'EngineeringPlan', 'CeilingPlan', 'Elevation', 'Section')
satOpt = SATImportOptions()
satOpt.Placement = ImportPlacement.Origin
satOpt.Unit = ImportUnit.Foot
opt = Options()
opt.ComputeReferences = True
saveOpt = SaveAsOptions()
saveOpt.OverwriteExistingFile = True
def create_solid_family(geom_obj, name, category, material, subcat):
try:
if any(c in name for c in invalid_chars):
raise Exception("Family name contains invalid characters")
tManager.ForceCloseTransaction()
fam_doc = doc.Application.NewFamilyDocument(fam_path)
if factor != 1:
geom_obj = geom_obj.Scale(factor)
offset_vec = Vector.ByTwoPoints(BoundingBox.ByGeometry(geom_obj).MinPoint, DynPoint.Origin())
geom_obj = geom_obj.Translate(offset_vec)
sat_file = os.path.join(temp_path, f"{name}.sat")
Geometry.ExportToSAT(geom_obj, sat_file)
view = next((v for v in FilteredElementCollector(fam_doc).OfClass(View)
if str(v.ViewType) in acceptable_views and not v.IsTemplate), None)
tManager.EnsureInTransaction(fam_doc)
import_id = fam_doc.Import(sat_file, satOpt, view)
imported_el = fam_doc.GetElement(import_id)
geom_iter = imported_el.get_Geometry(opt)
solid = None
for geo in geom_iter:
for item in geo.GetInstanceGeometry():
if isinstance(item, Solid) and item.Volume > 0:
solid = item
break
if solid: break
import_ids = [imp.Id for imp in FilteredElementCollector(fam_doc).OfClass(ImportInstance)]
for imp_id in import_ids:
el = fam_doc.GetElement(imp_id)
if el.Pinned: el.Pinned = False
fam_doc.Delete(imp_id)
if os.path.exists(sat_file):
System.IO.File.Delete(sat_file)
try:
fam_cat = fam_doc.Settings.Categories.get_Item(category.Name)
fam_doc.OwnerFamily.FamilyCategory = fam_cat
except: pass
elem = FreeFormElement.Create(fam_doc, solid)
if material:
try:
mat_fec = FilteredElementCollector(fam_doc).OfClass(Material)
fam_mat = next((m for m in mat_fec if m.Name == material), None)
if fam_mat:
elem.get_Parameter(BuiltInParameter.MATERIAL_ID_PARAM).Set(fam_mat.Id)
except: pass
if subcat:
try:
cat_subs = fam_doc.OwnerFamily.FamilyCategory.SubCategories
if cat_subs.Contains(subcat):
elem.Subcategory = cat_subs[subcat]
else:
new_sub = fam_doc.Settings.Categories.NewSubcategory(fam_doc.OwnerFamily.FamilyCategory, subcat)
elem.Subcategory = new_sub
except: pass
tManager.ForceCloseTransaction()
rfa_path = os.path.join(temp_path, f"{name}.rfa")
fam_doc.SaveAs(rfa_path, saveOpt)
family = fam_doc.LoadFamily(doc, FamLoadOpt())
fam_doc.Close(False)
if os.path.exists(rfa_path):
System.IO.File.Delete(rfa_path)
sym_id = family.GetFamilySymbolIds().GetEnumerator()
sym_id.MoveNext()
symbol = doc.GetElement(sym_id.Current)
tManager.EnsureInTransaction(doc)
if not symbol.IsActive: symbol.Activate()
instance = doc.Create.NewFamilyInstance(origin, symbol, str_type)
ElementTransformUtils.MoveElement(doc, instance.Id, offset_vec.Reverse().ToXyz())
tManager.ForceCloseTransaction()
return instance.ToDSType(False), family.ToDSType(False)
except:
return traceback.format_exc(), ""
if len(geom) == len(names) == len(categories) == len(materials) == len(subcats):
results = list(map(create_solid_family, geom, names, categories, materials, subcats))
elif len(geom) == len(names):
padded = pad_lists([geom, categories, materials, subcats])
results = list(map(create_solid_family, geom, names, padded[1], padded[2], padded[3]))
else:
results = [("Ensure each geometry has a unique family name.", "")]
OUT = [r[0] for r in results], [r[1] for r in results]
satOpt.Dispose()
opt.Dispose()
saveOpt.Dispose()
and something here should do it in a project so long the same material exist in project and family document









