FamilyInstance By geometry

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 :wink:

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

I have tried the Rhythm package, but currently, the operations are failing.

I have tried the nodes in the Rhythm package, but the material doesn’t seem to be applied.

@jacob.small @emrullah.yildiz
Do you know anything about the following?

Does the material exist in the family template?

Yes, it does.

I am setting it up this way, but does the material need to be retrieved from the family template? Currently, a material with the same name exists in both the project data and the template.

If it’s not too much trouble, could you share these Dynamo data?

dont have it anymore…try you can follow the image i use that python as shared as is actuelly spring node but modified for work in ironpython 3 and for new versions…the rest is from genius loci, guess crumple have some nodes for that as well…good luck :wink:

ps but seems to work so long the same material exist in family and project…i guess :wink: :wink: i dont do so much in materials :wink: :wink:

Revit_4fK98Mwd5S

Is that Python code the same as the one you shared previously?

yeah think so…

Thank you. I’ll try it
By the way, what goes into Input 5?

subcategori i guess, as is an option, but keep in mind i can only get it to work some material that way…for other materials try the workflow with genius loci as showed, but have to admit i havent goin deep into it…i will try when get some time, but normally i just open the family and create an associate parameter as @emrullah.yildiz wrote as well…with what i show in post # 14 it will give better control in a project

By the way, I tried it with a Sphere, but it resulted in an error…

you will need ironpython 3 engine and can be installed from P manager if you dont have

@sovitek @jacob.small @emrullah.yildiz
I have built the program referencing these (discussions/code), but it seems the material is not being applied with either Python or OOTB. The data is also on Drive, so checking it would be very helpful.

i have just tried with that way, seems work here…could you share an image aon your graph with all preview bubles ??

I wonder why. Could it be due to the language environment?


cheers not sure then seems good :wink: but try get the document from the created family for input in doc in element class for material…but not sure

I tried that too, but the material that should be in the family template disappears after the family is exported :innocent:

Separately, a concerning point is that if I run the Dynamo script multiple times, this kind of error occurs.

The referenced object is not valid, possibly because it has been deleted from the database, or its creation was undone.

   at Autodesk.Revit.Proxy.DB.DocumentProxy.getValidObject()
   at Autodesk.Revit.DB.Document.GetHashCode()
   at ProtoFFI.ReferenceEqualityComparer.GetHashCode(Object obj)
   at System.Collections.Generic.Dictionary`2.Remove(TKey key)
   at ProtoFFI.CLRObjectMarshaler.OnDispose(StackValue dsObject, Context context, Interpreter dsi)
   at ProtoFFI.DisposeFunctionPointer.Execute(Context c, Interpreter dsi, List`1 s)
   at ProtoCore.Lang.FFIFunctionEndPoint.Execute(Context c, List`1 formalParameters, StackFrame stackFrame, RuntimeCore runtimeCore)
   at ProtoCore.CallSite.DispatchDispose(StackValue stackValue, RuntimeCore runtimeCore)
   at ProtoCore.DSASM.Executive.CallDispose(ProcedureNode fNode, StackValue svThisPtr, Int32 classIndex)
   at ProtoCore.DSASM.Heap.GCDisposeObject(StackValue svPtr, Executive exe)
   at ProtoCore.DSASM.Heap.Sweep()
   at ProtoCore.DSASM.Heap.SingleStep(Boolean forceGC)
   at ProtoCore.DSASM.Heap.FullGC(IEnumerable`1 gcroots, Executive exe)
   at ProtoScript.Runners.LiveRunner.ForceGC()
   at ProtoScript.Runners.LiveRunner.ApplyUpdate()
   at ProtoScript.Runners.LiveRunner.PostExecution()
   at ProtoScript.Runners.LiveRunner.CompileAndExecuteForDeltaExecution(List`1 astList)
   at ProtoScript.Runners.LiveRunner.SynchronizeInternal(GraphSyncData syncData)
   at ProtoScript.Runners.LiveRunner.UpdateGraph(GraphSyncData syncData)
   at Dynamo.Engine.LiveRunnerServices.UpdateGraph(GraphSyncData graphData, Boolean verboseLogging)
   at Dynamo.Engine.EngineController.UpdateGraphImmediate(GraphSyncData graphSyncData)
   at Dynamo.Scheduler.UpdateGraphAsyncTask.HandleTaskExecutionCore()
   at Dynamo.Scheduler.AsyncTask.Execute()