Problem with section when deleted

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 = “:cross_mark: 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."

This is not likely the cause and sounds incorrect, though I am not entirely sure. If you close and reopen the graph does it generate the section on the new run?