Programmatically creating Revit family and NewExtrusion

Hello!

I am having a hard time creating a revit family programmatically. Specifically with the function NewExtrusion which gets the error:

Exception: A managed exception was thrown by Revit or by one of its external applications.

Script Executor Traceback:
Autodesk.Revit.Exceptions.InternalException: A managed exception was thrown by Revit or by one of its external applications.
 at Autodesk.Revit.ApplicationServices.Application.NewFamilyDocument(String templateFileName)
 at Microsoft.Scripting.Interpreter.FuncCallInstruction`3.Run(InterpretedFrame frame)
 at Microsoft.Scripting.Interpreter.Interpreter.Run(InterpretedFrame frame)
 at Microsoft.Scripting.Interpreter.LightLambda.Run4[T0,T1,T2,T3,TRet](T0 arg0, T1 arg1, T2 arg2, T3 arg3)
 at System.Dynamic.UpdateDelegates.UpdateAndExecute3[T0,T1,T2,TRet](CallSite site, T0 arg0, T1 arg1, T2 arg2)
 at Microsoft.Scripting.Interpreter.FuncCallInstruction`6.Run(InterpretedFrame frame)
 at Microsoft.Scripting.Interpreter.Interpreter.Run(InterpretedFrame frame)
 at Microsoft.Scripting.Interpreter.LightLambda.Run4[T0,T1,T2,T3,TRet](T0 arg0, T1 arg1, T2 arg2, T3 arg3)
 at IronPython.Compiler.Ast.CallExpression.Invoke1Instruction.Run(InterpretedFrame frame)
 at Microsoft.Scripting.Interpreter.Interpreter.Run(InterpretedFrame frame)
 at Microsoft.Scripting.Interpreter.LightLambda.Run4[T0,T1,T2,T3,TRet](T0 arg0, T1 arg1, T2 arg2, T3 arg3)
 at IronPython.Compiler.PythonCallTargets.OriginalCallTarget3(PythonFunction function, Object arg0, Object arg1, Object arg2)
 at System.Dynamic.UpdateDelegates.UpdateAndExecute5[T0,T1,T2,T3,T4,TRet](CallSite site, T0 arg0, T1 arg1, T2 arg2, T3 arg3, T4 arg4)
 at Microsoft.Scripting.Interpreter.FuncCallInstruction`8.Run(InterpretedFrame frame)
 at Microsoft.Scripting.Interpreter.Interpreter.Run(InterpretedFrame frame)
 at Microsoft.Scripting.Interpreter.LightLambda.Run6[T0,T1,T2,T3,T4,T5,TRet](T0 arg0, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5)
 at IronPython.Compiler.Ast.CallExpression.Invoke3Instruction.Run(InterpretedFrame frame)
 at Microsoft.Scripting.Interpreter.Interpreter.Run(InterpretedFrame frame)
 at Microsoft.Scripting.Interpreter.LightLambda.Run3[T0,T1,T2,TRet](T0 arg0, T1 arg1, T2 arg2)
 at System.Dynamic.UpdateDelegates.UpdateAndExecute4[T0,T1,T2,T3,TRet](CallSite site, T0 arg0, T1 arg1, T2 arg2, T3 arg3)
 at IronPython.Compiler.Ast.CallExpression.Invoke2Instruction.Run(InterpretedFrame frame)
 at Microsoft.Scripting.Interpreter.Interpreter.Run(InterpretedFrame frame)
 at Microsoft.Scripting.Interpreter.LightLambda.Run4[T0,T1,T2,T3,TRet](T0 arg0, T1 arg1, T2 arg2, T3 arg3)
 at IronPython.Compiler.PythonCallTargets.OriginalCallTarget3(PythonFunction function, Object arg0, Object arg1, Object arg2)
 at System.Dynamic.UpdateDelegates.UpdateAndExecute5[T0,T1,T2,T3,T4,TRet](CallSite site, T0 arg0, T1 arg1, T2 arg2, T3 arg3, T4 arg4)
 at Microsoft.Scripting.Interpreter.DynamicInstruction`6.Run(InterpretedFrame frame)
 at Microsoft.Scripting.Interpreter.Interpreter.Run(InterpretedFrame frame)
 at Microsoft.Scripting.Interpreter.LightLambda.Run2[T0,T1,TRet](T0 arg0, T1 arg1)
 at IronPython.Compiler.PythonScriptCode.RunWorker(CodeContext ctx)
 at PyRevitLabs.PyRevit.Runtime.IronPythonEngine.Execute(ScriptRuntime& runtime)

And here is my code: (error happens at the line with NewExtrusion)


counter_top_level = 0
boundary = [
    XYZ(0, 0, counter_top_level),           # Start point at counter height (3 feet)
    XYZ(6, 0, counter_top_level),           # Right leg
    XYZ(6, 2, counter_top_level),           # Right leg width
    XYZ(2, 2, counter_top_level),           # Connection point
    XYZ(2, 4, counter_top_level),           # Left leg
    XYZ(0, 4, counter_top_level),           # Left leg end
    XYZ(0, 0, counter_top_level)            # Back to start
  ]
thickness = 3/12
create_custom_extrusion_family(state, boundary, thickness)


def create_custom_extrusion_family(state, boundaries, thickness):
  doc = state['doc']
  app = doc.Application
  template_path = app.FamilyTemplatePath
  template_name = "Metric Casework.rft"
  template_file = System.IO.Path.Combine(template_path, template_name)
  family_doc = app.NewFamilyDocument(template_file)
  family_editor_create_extrusion(state, family_doc, boundaries, thickness)
  # we dont get here anyway
  doc.LoadFamily(family_doc.Id)


def family_editor_create_extrusion(state, family_doc, boundaries, thickness):
  assert isinstance(boundaries, list)
  doc = state['doc']
  t = Transaction(family_doc, "Create Extrusion")
  t.Start()
  create_app = doc.Application.Create
  sketch_planes = FilteredElementCollector(family_doc).OfClass(SketchPlane)
  sketch_plane = None
  for i in sketch_planes:
    if i.Name == "Ref. Level":
      sketch_plane = i
      break
  curve_arr_array = CurveArrArray()
  curve_array = CurveArray()
  for i in range(len(boundaries) - 1):
    line = Line.CreateBound(boundaries[i], boundaries[i + 1])
    curve_array.Append(line)
  curve_arr_array.Append(curve_array)
  family_creator = family_doc.FamilyCreate
  extrusion = family_creator.NewExtrusion(True, curve_arr_array, sketch_plane, thickness)
  t.Commit()
  return extrusion

The sketch plane returns the SketchPlane element which I am quite sure has its normal correct (as ground). I also tried False instead of True.
Anyone experienced similar issue?
I did follow the Great Jeremy Tammik up unto this error https://thebuildingcoder.typepad.com/blog/2011/06/creating-and-inserting-an-extrusion-family.html

Well - to start. Your path probably isn’t right.

at Autodesk.Revit.ApplicationServices.Application.NewFamilyDocument(String templateFileName)

The FamilyTemplatePath will take you to the directory where all templates are installed, French, German Russian, English etc. You need to get down one more level. So add in your preferred flavor between FamilyTemplatePath and your desired template name.

I think there are some other issues, but this should get you headed in the right direction.

Once in your family template, the extrusion creates just fine.

I would ask about the end goal as I might have some other workflow suggestions based on what I think you are tryiong to accomplish.

Or you could be a little more robust and do a search for the file.
Takes a little more time than being explicit, and may return more than one file if there are several of the same name in different subdirectories. But would help with non-standard installations of Revit.

import os

def find_all(name, path):
    result = []
    for root, dirs, files in os.walk(path):
        if name in files:
            result.append(os.path.join(root, name))
    return result
            
file_name = find("Metric Casework.rft", r"C:\ProgramData\Autodesk\RVT 2024")
print(file_name)

The path could be anything above the file. But the fristher up, the longer it takes.

Thank you! I tried but still same error. Printed out the path just to be sure and it is correct.

My goal with this is to create (for now) a basic kitchen countertop fully parametrically (with a hole for a sink which I add as an additional boundary, which I deleted until code will work with one boundary). I was thinking of doing it with DirectShape and Rhino.Inside. Tried Rhino.Inside but saw that its actually possible to create families programmatically in Revit and had to try, because I’m no good at Rhino and dont know any good ways to learn the docs. They seem very confusing.

Here is my printout and code with the explicit family path and prints. What are your other ideas for this, that you mentioned?


counter_top_level = 0
boundary = [
    XYZ(0, 0, counter_top_level),           # Start point at counter height (3 feet)
    XYZ(6, 0, counter_top_level),           # Right leg
    XYZ(6, 2, counter_top_level),           # Right leg width
    XYZ(2, 2, counter_top_level),           # Connection point
    XYZ(2, 4, counter_top_level),           # Left leg
    XYZ(0, 4, counter_top_level),           # Left leg end
    XYZ(0, 0, counter_top_level)            # Back to start
  ]

thickness = 3/12  # 3 inch height in feet
create_custom_extrusion_family(state, boundary, thickness)


def create_custom_extrusion_family(state, boundaries, thickness):
  doc = state['doc']
  app = doc.Application
  template_path = "C:\ProgramData\Autodesk\RVT 2024\Family Templates\English-Imperial"
  template_name = "Casework.rft"
  template_file_path = os.path.join(template_path, template_name)
  print(template_file_path)
  family_doc = app.NewFamilyDocument(template_file_path)
  family_editor_create_extrusion(state, family_doc, boundaries, thickness)
  doc.LoadFamily(family_doc.Id)


def family_editor_create_extrusion(state, family_doc, boundaries, thickness):
  assert isinstance(boundaries, list)
  doc = state['doc']
  t = Transaction(family_doc, "Create Extrusion")
  t.Start()
  create_app = doc.Application.Create
  sketch_planes = FilteredElementCollector(family_doc).OfClass(SketchPlane)
  sketch_plane = None
  for i in sketch_planes:
    if i.Name == "Ref. Level":
      sketch_plane = i
      break
  curve_arr_array = CurveArrArray()
  curve_array = CurveArray()
  for i in range(len(boundaries) - 1):
    print("Line: ", i)
    print(boundaries[i])
    print(boundaries[i + 1])
    line = Line.CreateBound(boundaries[i], boundaries[i + 1])
    curve_array.Append(line)
  curve_arr_array.Append(curve_array)
  family_creator = family_doc.FamilyCreate
  print(curve_arr_array)
  print(sketch_plane)
  print(thickness)
  extrusion = family_creator.NewExtrusion(True, curve_arr_array, sketch_plane, thickness)
  t.Commit()
  return extrusion
C:\ProgramData\Autodesk\RVT 2024\Family Templates\English-Imperial\Casework.rft

('Line: ', 0)

(0.000000000, 0.000000000, 0.000000000)

(6.000000000, 0.000000000, 0.000000000)

('Line: ', 1)

(6.000000000, 0.000000000, 0.000000000)

(6.000000000, 2.000000000, 0.000000000)

('Line: ', 2)

(6.000000000, 2.000000000, 0.000000000)

(2.000000000, 2.000000000, 0.000000000)

('Line: ', 3)

(2.000000000, 2.000000000, 0.000000000)

(2.000000000, 4.000000000, 0.000000000)

('Line: ', 4)

(2.000000000, 4.000000000, 0.000000000)

(0.000000000, 4.000000000, 0.000000000)

('Line: ', 5)

(0.000000000, 4.000000000, 0.000000000)

(0.000000000, 0.000000000, 0.000000000)

<Autodesk.Revit.DB.CurveArrArray object at 0x000000000000057D [Autodesk.Revit.DB.CurveArrArray]>

<Autodesk.Revit.DB.SketchPlane object at 0x000000000000057E [Autodesk.Revit.DB.SketchPlane]>

0

Your load family isn’t quite right for an unsaved family.
I recommend you either save the family to give it the desired name - or rename it once it is in the project document.

You’ll also need to close the family as it is open in memory but not visible in the UI.
I’m not sure what state refers to, as there is no reference. So I just tweaked it to doc so I could run it in RPS. I presume this is part of a larger app. (Dynamo?)

…and you can’t call create_custom_extrusion until it is defined.

This ran fine:

import clr
import System
import os

clr.AddReference('RevitAPIUI')
from Autodesk.Revit.UI import UIApplication

uiapp = __revit__.Application
app = uiapp

counter_top_level = 0
boundary = [
    XYZ(0, 0, counter_top_level),           # Start point at counter height (3 feet)
    XYZ(6, 0, counter_top_level),           # Right leg
    XYZ(6, 2, counter_top_level),           # Right leg width
    XYZ(2, 2, counter_top_level),           # Connection point
    XYZ(2, 4, counter_top_level),           # Left leg
    XYZ(0, 4, counter_top_level),           # Left leg end
    XYZ(0, 0, counter_top_level)            # Back to start
  ]

thickness = 3/12  # 3 inch height in feet

def create_custom_extrusion_family(state, boundaries, thickness):
  #doc = state['doc']
  app = doc.Application
  template_path = "C:\ProgramData\Autodesk\RVT 2024\Family Templates\English-Imperial"
  template_name = "Casework.rft"
  template_file_path = os.path.join(template_path, template_name)
  print(template_file_path)
  family_doc = app.NewFamilyDocument(template_file_path)
  family_editor_create_extrusion(state, family_doc, boundaries, thickness)
  family_doc.LoadFamily(doc)
  #doc.LoadFamily(family_doc.Id)


def family_editor_create_extrusion(state, family_doc, boundaries, thickness):
  assert isinstance(boundaries, list)
  #doc = state['doc']
  t = Transaction(family_doc, "Create Extrusion")
  t.Start()
  create_app = doc.Application.Create
  sketch_planes = FilteredElementCollector(family_doc).OfClass(SketchPlane)
  sketch_plane = None
  for i in sketch_planes:
    if i.Name == "Ref. Level":
      sketch_plane = i
      break
  curve_arr_array = CurveArrArray()
  curve_array = CurveArray()
  for i in range(len(boundaries) - 1):
    print("Line: ", i)
    print(boundaries[i])
    print(boundaries[i + 1])
    line = Line.CreateBound(boundaries[i], boundaries[i + 1])
    curve_array.Append(line)
  curve_arr_array.Append(curve_array)
  family_creator = family_doc.FamilyCreate
  print(curve_arr_array)
  print(sketch_plane)
  print(thickness)
  extrusion = family_creator.NewExtrusion(True, curve_arr_array, sketch_plane, thickness)
  t.Commit()
  return extrusion

state = doc
create_custom_extrusion_family(state, boundary, thickness)

Which gets to a bigger issue and where I thought you were headed.
Code for this is the long way around.

If you have one - yes, one - line based countertop, you can create just about any custom countertop you would ever want or need. No programing involved.

  • A line based counter top.
  • Instance width (my preference, but could be typed.)
  • Active “Cuts with Voids When Loaded”

You are done. Layout your countertop and overlap the counters as need. Use join geometry to glue them together. They remain dynamically editable and you can stretch and move them as needed. (You could script the join. Even cooler if it was event driven. But not at all needed.)

Make your sinks and other hosted element face based with a void in the face. When you drop the sink on the counter, it cuts the appropriate hole. Done. Same with deck mounted plumbing fixtures. Move the sink - the hole moves. Move the counter - the sink moves. If you have non-face based sinks - just nest them in a face based family for the hole action. Shared nesting might be preferable.

Zero code and highly flexible,

You can make a few other counter parts, like a bullnose or mitered end. But you’ll find you don’t need much else.

Here’s a few of the one created with your script joined…

Thank you for trying it out. I also did but I still get same exact error at NewExtrusion.
Had to add import of Revit API elements. If you say it ran for you then I feel like I am missing something important. My Revit version is 2024.3.

import clr
import System
import os

clr.AddReference('RevitAPIUI')
from Autodesk.Revit.UI import UIApplication
from Autodesk.Revit.DB import XYZ, Transaction, FilteredElementCollector, SketchPlane, Line, CurveArrArray, CurveArray

uiapp = __revit__.Application
app = uiapp
doc = __revit__.ActiveUIDocument.Document

counter_top_level = 0
boundary = [
    XYZ(0, 0, counter_top_level),           # Start point at counter height (3 feet)
    XYZ(6, 0, counter_top_level),           # Right leg
    XYZ(6, 2, counter_top_level),           # Right leg width
    XYZ(2, 2, counter_top_level),           # Connection point
    XYZ(2, 4, counter_top_level),           # Left leg
    XYZ(0, 4, counter_top_level),           # Left leg end
    XYZ(0, 0, counter_top_level)            # Back to start
  ]

thickness = 3/12  # 3 inch height in feet

def create_custom_extrusion_family(state, boundaries, thickness):
  #doc = state['doc']
  app = doc.Application
  template_path = "C:\ProgramData\Autodesk\RVT 2024\Family Templates\English-Imperial"
  template_name = "Casework.rft"
  template_file_path = os.path.join(template_path, template_name)
  print(template_file_path)
  family_doc = app.NewFamilyDocument(template_file_path)
  family_editor_create_extrusion(state, family_doc, boundaries, thickness)
  family_doc.LoadFamily(doc)
  #doc.LoadFamily(family_doc.Id)


def family_editor_create_extrusion(state, family_doc, boundaries, thickness):
  assert isinstance(boundaries, list)
  #doc = state['doc']
  t = Transaction(family_doc, "Create Extrusion")
  t.Start()
  create_app = doc.Application.Create
  sketch_planes = FilteredElementCollector(family_doc).OfClass(SketchPlane)
  sketch_plane = None
  for i in sketch_planes:
    if i.Name == "Ref. Level":
      sketch_plane = i
      break
  curve_arr_array = CurveArrArray()
  curve_array = CurveArray()
  for i in range(len(boundaries) - 1):
    print("Line: ", i)
    print(boundaries[i])
    print(boundaries[i + 1])
    line = Line.CreateBound(boundaries[i], boundaries[i + 1])
    curve_array.Append(line)
  curve_arr_array.Append(curve_array)
  family_creator = family_doc.FamilyCreate
  print(curve_arr_array)
  print(sketch_plane)
  print(thickness)
  extrusion = family_creator.NewExtrusion(True, curve_arr_array, sketch_plane, thickness)
  t.Commit()
  return extrusion

state = doc
create_custom_extrusion_family(state, boundary, thickness)

The error:



C:\ProgramData\Autodesk\RVT 2024\Family Templates\English-Imperial\Casework.rft



('Line: ', 0)



(0.000000000, 0.000000000, 0.000000000)



(6.000000000, 0.000000000, 0.000000000)



('Line: ', 1)



(6.000000000, 0.000000000, 0.000000000)



(6.000000000, 2.000000000, 0.000000000)



('Line: ', 2)



(6.000000000, 2.000000000, 0.000000000)



(2.000000000, 2.000000000, 0.000000000)



('Line: ', 3)



(2.000000000, 2.000000000, 0.000000000)



(2.000000000, 4.000000000, 0.000000000)



('Line: ', 4)



(2.000000000, 4.000000000, 0.000000000)



(0.000000000, 4.000000000, 0.000000000)



('Line: ', 5)



(0.000000000, 4.000000000, 0.000000000)



(0.000000000, 0.000000000, 0.000000000)



<Autodesk.Revit.DB.CurveArrArray object at 0x0000000000000536 [Autodesk.Revit.DB.CurveArrArray]>



<Autodesk.Revit.DB.SketchPlane object at 0x0000000000000537 [Autodesk.Revit.DB.SketchPlane]>



0



IronPython Traceback:
Traceback (most recent call last):
 File "C:\Maciek\formX\formX_github_repository\parametric_design\PyRevitExtensions\FormX.extension\FormX.tab\FormX.panel\countertop.pushbutton\script.py", line 69, in <module>
 File "C:\Maciek\formX\formX_github_repository\parametric_design\PyRevitExtensions\FormX.extension\FormX.tab\FormX.panel\countertop.pushbutton\script.py", line 34, in create_custom_extrusion_family
 File "C:\Maciek\formX\formX_github_repository\parametric_design\PyRevitExtensions\FormX.extension\FormX.tab\FormX.panel\countertop.pushbutton\script.py", line 64, in family_editor_create_extrusion
Exception: One of the conditions for the inputs was not satisfied. Consult the documentation for requirements for each argument.

Script Executor Traceback:
Autodesk.Revit.Exceptions.ArgumentException: One of the conditions for the inputs was not satisfied. Consult the documentation for requirements for each argument.
 at Autodesk.Revit.Creation.FamilyItemFactory.NewExtrusion(Boolean isSolid, CurveArrArray profile, SketchPlane sketchPlane, Double end)
 at Microsoft.Scripting.Interpreter.FuncCallInstruction`6.Run(InterpretedFrame frame)
 at Microsoft.Scripting.Interpreter.Interpreter.Run(InterpretedFrame frame)
 at Microsoft.Scripting.Interpreter.LightLambda.Run7[T0,T1,T2,T3,T4,T5,T6,TRet](T0 arg0, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6)
 at System.Dynamic.UpdateDelegates.UpdateAndExecute6[T0,T1,T2,T3,T4,T5,TRet](CallSite site, T0 arg0, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5)
 at Microsoft.Scripting.Interpreter.DynamicInstruction`7.Run(InterpretedFrame frame)
 at Microsoft.Scripting.Interpreter.Interpreter.Run(InterpretedFrame frame)
 at Microsoft.Scripting.Interpreter.LightLambda.Run5[T0,T1,T2,T3,T4,TRet](T0 arg0, T1 arg1, T2 arg2, T3 arg3, T4 arg4)
 at IronPython.Compiler.PythonCallTargets.OriginalCallTarget4(PythonFunction function, Object arg0, Object arg1, Object arg2, Object arg3)
 at System.Dynamic.UpdateDelegates.UpdateAndExecute6[T0,T1,T2,T3,T4,T5,TRet](CallSite site, T0 arg0, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5)
 at Microsoft.Scripting.Interpreter.DynamicInstruction`7.Run(InterpretedFrame frame)
 at Microsoft.Scripting.Interpreter.Interpreter.Run(InterpretedFrame frame)
 at Microsoft.Scripting.Interpreter.LightLambda.Run4[T0,T1,T2,T3,TRet](T0 arg0, T1 arg1, T2 arg2, T3 arg3)
 at IronPython.Compiler.PythonCallTargets.OriginalCallTarget3(PythonFunction function, Object arg0, Object arg1, Object arg2)
 at System.Dynamic.UpdateDelegates.UpdateAndExecute5[T0,T1,T2,T3,T4,TRet](CallSite site, T0 arg0, T1 arg1, T2 arg2, T3 arg3, T4 arg4)
 at Microsoft.Scripting.Interpreter.DynamicInstruction`6.Run(InterpretedFrame frame)
 at Microsoft.Scripting.Interpreter.Interpreter.Run(InterpretedFrame frame)
 at Microsoft.Scripting.Interpreter.LightLambda.Run2[T0,T1,TRet](T0 arg0, T1 arg1)
 at IronPython.Compiler.PythonScriptCode.RunWorker(CodeContext ctx)
 at PyRevitLabs.PyRevit.Runtime.IronPythonEngine.Execute(ScriptRuntime& runtime)

The point is to have the whole unit with detail fully parametric. I will resort to Rhino or Direct shape if this approach fails. But seems it should work since its exact example from Jeremy Tammik and you also say it works on your end. This is really a headscratcher…

You post ran perfectly in RPS Revit 2024. No error.
Your issue is outside of this.

1 Like

Hares a clip of my dynamic version in action:

…and faster with tab. Edges could be walls or whatever. This could easily be scripted.

I had thickness set to 3/12… Switched it to an int or decimal number and it works and loads family definition into the project!

Thank you!

1 Like