When running my Dynamo script in Revit, it creates sections based on the Type Mark parameter. The problem is that if I accidentally delete one of these sections and then rerun the script, Dynamo does not recreate the deleted section. Even though it has been removed from the model, Dynamo still seems to think it exists and therefore skips generating it again.
This behavior happens because Dynamo typically checks for existing elements (by ID or parameter) and assumes they are still valid, even if they were deleted in Revit. As a result, the script does not regenerate the missing section, leaving gaps in the output.
What I actually want is for Dynamo to recreate sections that are missing when I rerun the script — so if a section with a given Type Mark is no longer in the model, Dynamo should generate it again.
-- coding: utf-8 --
SCRIPT 1 — CREATE
Front elevations (Section Views as elevation) for CURTAIN WALLS with ‘H’ in Mark
Looks for:
- INSTANCE Mark (Identity Data)
- (fallback) Type Mark / Type Name
- (optional) Revit Links (coordinates transformed)
ALWAYS creates new sections per run (unique name with timestamp),
sets Discipline/Phase correctly, and opens the first created view immediately.
Author: Copilot for Noah Smit
import clr
import System
from System import DateTime
Dynamo/Revit
clr.AddReference(‘RevitServices’)
from RevitServices.Persistence import DocumentManager
from RevitServices.Transactions import TransactionManager
clr.AddReference(‘RevitAPI’)
from Autodesk.Revit.DB import (
FilteredElementCollector, BuiltInParameter,
ViewFamily, ViewFamilyType, ViewSection, View,
BoundingBoxXYZ, Transform, XYZ, Wall, WallType,
ElementId, ViewDiscipline, RevitLinkInstance
)
doc = DocumentManager.Instance.CurrentDBDocument
uiapp = DocumentManager.Instance.CurrentUIApplication
uidoc = uiapp.ActiveUIDocument
---------- CONSTANTS ----------
FILTER_TEXT = “H” # Looks for ‘H’ (case-insensitive)
MARGIN_MM = 100.0
DEPTH_MM = 500.0
OFFSET_MM = 100.0
NAME_PREFIX = “CW-AZ” # Use the same prefix in Script 2 (delete)
INCLUDE_LINKS = True # Also search in Revit Links
USE_TYPE_FALLBACK = True # If instance Mark is empty: use Type Mark/Name
DEBUG = False # True => OUT contains debug rows
---------- Conversion ----------
MM_TO_FT = 1.0 / 304.8
margin = MARGIN_MM * MM_TO_FT
depth = DEPTH_MM * MM_TO_FT
offset = OFFSET_MM * MM_TO_FT
---------- Helpers ----------
def normalize(v):
try: return v.Normalize()
except: return v
def is_curtain_wall(w):
try:
wt = w.WallType
if not isinstance(wt, WallType): return False
try:
from Autodesk.Revit.DB import WallKind
return wt.Kind == WallKind.Curtain
except:
return hasattr(w, “CurtainGrid”) and w.CurtainGrid is not None
except:
return False
def str_or_none(p):
if not p or not p.HasValue: return None
return p.AsString() or p.AsValueString()
def get_instance_mark(elem):
Identity Data (instance)
v = str_or_none(elem.get_Parameter(BuiltInParameter.ALL_MODEL_MARK))
if v: return (str(v), “Instance Mark”)
for nm in (“Mark”, “Merk”):
try:
p = elem.LookupParameter(nm)
v = str_or_none(p)
if v: return (str(v), “Instance {}”.format(nm))
except: pass
return (None, None)
def get_type_mark_or_name(d, elem):
try:
t = d.GetElement(elem.GetTypeId())
if t:
v = str_or_none(t.get_Parameter(BuiltInParameter.ALL_MODEL_TYPE_MARK))
if v: return (str(v), “Type Mark”)
for nm in (“Type Mark”, “Type Merk”):
try:
p = t.LookupParameter(nm)
v = str_or_none(p)
if v: return (str(v), “Type {}”.format(nm))
except: pass
return (str(t.Name), “Type Name”)
except: pass
return (None, None)
def contains_filter(val, needle):
return (val is not None) and (needle.lower() in str(val).lower())
def wall_midpoint_and_tangent(w):
loc = w.Location
if not loc: return (None, None)
crv = getattr(loc, “Curve”, None)
if not crv: return (None, None)
try:
deriv = crv.ComputeDerivatives(0.5, True)
return (deriv.Origin, normalize(deriv.BasisX))
except:
p0 = crv.GetEndPoint(0); p1 = crv.GetEndPoint(1)
mid = XYZ((p0.X+p1.X)*0.5, (p0.Y+p1.Y)*0.5, (p0.Z+p1.Z)*0.5)
tan = normalize(p1 - p0)
return (mid, tan)
def wall_length_ft(w):
try:
crv = w.Location.Curve
if crv: return float(crv.Length)
except: pass
return 3000.0 * MM_TO_FT
def wall_height_ft(w):
try:
p = w.get_Parameter(BuiltInParameter.WALL_USER_HEIGHT_PARAM)
if p and p.HasValue and p.AsDouble() and p.AsDouble() > 0:
return float(p.AsDouble())
except: pass
try:
bb = w.get_BoundingBox(None)
if bb: return float(bb.Max.Z - bb.Min.Z)
except: pass
return 3000.0 * MM_TO_FT
def get_section_vft(d):
for vft in FilteredElementCollector(d).OfClass(ViewFamilyType):
try:
if vft.ViewFamily == ViewFamily.Section:
return vft
except: continue
return None
def unique_timestamp():
n = DateTime.Now
return “{:04d}{:02d}{:02d}-{:02d}{:02d}{:02d}-{:03d}”.format(
n.Year, n.Month, n.Day, n.Hour, n.Minute, n.Second, n.Millisecond
)
---------- Collect targets (host + optional links) ----------
sect_vft = get_section_vft(doc)
if not sect_vft:
OUT = “ No Section ViewFamilyType found. Add one (Manage > Transfer Project Standards > View Types).”
else:
targets = # (wallElem, markVal, linkTransformOrNone)
debug_rows =
# Host
for w in FilteredElementCollector(doc).OfClass(Wall).ToElements():
if not is_curtain_wall(w): continue
mval, msrc = get_instance_mark(w)
if (mval is None) and USE_TYPE_FALLBACK:
mval, msrc = get_type_mark_or_name(doc, w)
if DEBUG: debug_rows.append(["HOST", w.Id.IntegerValue, mval, msrc])
if contains_filter(mval, FILTER_TEXT):
targets.append((w, mval, None))
# Links
if INCLUDE_LINKS:
for linst in FilteredElementCollector(doc).OfClass(RevitLinkInstance).ToElements():
try:
ldoc = linst.GetLinkDocument()
except:
ldoc = None
if ldoc is None: continue
tr = linst.GetTotalTransform() # link->host
for w in FilteredElementCollector(ldoc).OfClass(Wall).ToElements():
if not is_curtain_wall(w): continue
# Instance
mv = str_or_none(w.get_Parameter(BuiltInParameter.ALL_MODEL_MARK))
src = "Instance Mark (Link)" if mv else None
if mv is None:
for nm in ("Mark","Merk"):
try:
p2 = w.LookupParameter(nm)
mv = str_or_none(p2)
if mv:
src = "Instance {} (Link)".format(nm); break
except: pass
# Type fallback
if (mv is None) and USE_TYPE_FALLBACK:
try:
t = ldoc.GetElement(w.GetTypeId())
if t:
tv = str_or_none(t.get_Parameter(BuiltInParameter.ALL_MODEL_TYPE_MARK))
if tv:
mv = tv; src = "Type Mark (Link)"
if mv is None:
for nm in ("Type Mark","Type Merk"):
p3 = t.LookupParameter(nm)
tv = str_or_none(p3)
if tv:
mv = tv; src = "Type {} (Link)".format(nm); break
if mv is None:
mv = t.Name; src = "Type Name (Link)"
except: pass
if DEBUG: debug_rows.append(["LINK:"+linst.Name, w.Id.IntegerValue, mv, src])
if contains_filter(mv, FILTER_TEXT):
targets.append((w, mv, tr))
if not targets:
OUT = debug_rows if DEBUG else "ℹ️ No Curtain Walls found containing '{}' (Instance{}{}).".format(
FILTER_TEXT,
" + Type" if USE_TYPE_FALLBACK else "",
" + Links" if INCLUDE_LINKS else ""
)
else:
created = []
ts = unique_timestamp()
TransactionManager.Instance.EnsureInTransaction(doc)
for (w, mark_val, tr_link2host) in targets:
mid, tan = wall_midpoint_and_tangent(w)
if not mid or not tan: continue
# Front: towards the wall (exterior is +Orientation) -> view_dir = -Orientation
try:
view_dir = -normalize(w.Orientation)
except:
view_dir = normalize(XYZ.BasisZ.CrossProduct(tan))
up = XYZ.BasisZ
# From a link? transform to host
if tr_link2host:
mid = tr_link2host.OfPoint(mid)
tan = normalize(tr_link2host.OfVector(tan))
view_dir = normalize(tr_link2host.OfVector(view_dir))
up = normalize(tr_link2host.OfVector(up))
right = normalize(up.CrossProduct(view_dir))
half_w = 0.5 * wall_length_ft(w) + margin
half_h = 0.5 * wall_height_ft(w) + margin
origin = XYZ(mid.X - view_dir.X * offset,
mid.Y - view_dir.Y * offset,
mid.Z - view_dir.Z * offset)
t = Transform.Identity
t.Origin = origin
t.BasisX = right
t.BasisY = up
t.BasisZ = view_dir
box = BoundingBoxXYZ()
box.Transform = t
# 0..depth => elevation (not section cut)
box.Min = XYZ(-half_w, -half_h, 0.0)
box.Max = XYZ( half_w, half_h, depth)
v = None
try:
v = ViewSection.CreateSection(doc, sect_vft.Id, box)
except:
v = None
if v:
# Unlink template, set discipline & phase, and give unique name
try:
if v.ViewTemplateId and v.ViewTemplateId != ElementId.InvalidElementId:
v.ViewTemplateId = ElementId.InvalidElementId
except: pass
try:
v.Discipline = ViewDiscipline.Coordination
except: pass
try:
ph = w.get_Parameter(BuiltInParameter.PHASE_CREATED)
if ph and ph.HasValue:
phId = ph.AsElementId()
vp = v.get_Parameter(BuiltInParameter.VIEW_PHASE)
if phId and vp: vp.Set(phId)
except: pass
safe_mark = (mark_val or "").strip()
v.Name = "{}-{}-CW-{}-{}".format(NAME_PREFIX, safe_mark, w.Id.IntegerValue, ts)
created.append([v.Id.IntegerValue, v.Name])
TransactionManager.Instance.TransactionTaskDone()
# Open the first new view (proof it exists)
try:
if created:
first_view_id = ElementId(created[0][0])
v_open = doc.GetElement(first_view_id)
if v_open:
uidoc.ActiveView = v_open
except: pass
OUT = created if created else "⚠️ Matches found, but creating/naming views failed."