Automatiseringsbibliotek

10 scripts til BIM/IFC, data quality og digital aflevering

Find elementer uden CCS/CCI-kode

Dynamo / Revit

Finder modelobjekter uden klassifikationskode i instance- eller typeparametre som CCSCode, CCI_Code og ClassificationCode.

import clr
clr.AddReference("RevitAPI")
clr.AddReference("RevitServices")

from Autodesk.Revit.DB import *
from RevitServices.Persistence import DocumentManager

doc = DocumentManager.Instance.CurrentDBDocument
PARAMETER_NAMES = ["CCSCode", "CCS_Code", "CCI", "CCI_Code", "ClassificationCode", "Classification"]

def text_value(parameter):
    if not parameter or not parameter.HasValue:
        return None
    value = parameter.AsString() or parameter.AsValueString()
    return str(value).strip() if value and str(value).strip() else None

def get_value(element, names):
    for name in names:
        value = text_value(element.LookupParameter(name))
        if value:
            return value

    type_id = element.GetTypeId()
    if type_id and type_id != ElementId.InvalidElementId:
        element_type = doc.GetElement(type_id)
        if element_type:
            for name in names:
                value = text_value(element_type.LookupParameter(name))
                if value:
                    return value
    return None

def is_model_element(element):
    return (
        element.Category
        and element.Category.CategoryType == CategoryType.Model
        and not element.ViewSpecific
    )

elements = FilteredElementCollector(doc).WhereElementIsNotElementType().ToElements()
missing = []

for element in elements:
    if not is_model_element(element):
        continue
    if get_value(element, PARAMETER_NAMES):
        continue

    missing.append([
        element.Id.IntegerValue,
        element.Category.Name,
        getattr(element, "Name", ""),
        "CCS/CCI mangler",
    ])

OUT = [["ElementId", "Kategori", "Navn", "Fejl"]] + missing if missing else ["OK - alle modelobjekter har CCS/CCI-kode"]
Read-only Dynamo Python. Kør på en kopi eller koordineringsmodel før aflevering.
ccscciklassifikationrevit

Find døre uden FireRating

Dynamo / Revit

Kontrollerer alle Revit-døre for brandklassifikation på instance eller type, fx FireRating, Fire Rating eller Brandklasse.

import clr
clr.AddReference("RevitAPI")
clr.AddReference("RevitServices")

from Autodesk.Revit.DB import *
from RevitServices.Persistence import DocumentManager

doc = DocumentManager.Instance.CurrentDBDocument
PARAMETER_NAMES = ["FireRating", "Fire Rating", "Brandklasse", "Brandmodstand", "Fire Resistance"]

def text_value(parameter):
    if not parameter or not parameter.HasValue:
        return None
    value = parameter.AsString() or parameter.AsValueString()
    return str(value).strip() if value and str(value).strip() else None

def get_value(element, names):
    for name in names:
        value = text_value(element.LookupParameter(name))
        if value:
            return value

    type_id = element.GetTypeId()
    if type_id and type_id != ElementId.InvalidElementId:
        element_type = doc.GetElement(type_id)
        if element_type:
            for name in names:
                value = text_value(element_type.LookupParameter(name))
                if value:
                    return value
    return None

doors = (
    FilteredElementCollector(doc)
    .OfCategory(BuiltInCategory.OST_Doors)
    .WhereElementIsNotElementType()
    .ToElements()
)

missing = []
for door in doors:
    if get_value(door, PARAMETER_NAMES):
        continue
    level_name = doc.GetElement(door.LevelId).Name if door.LevelId and door.LevelId != ElementId.InvalidElementId else ""
    missing.append([
        door.Id.IntegerValue,
        getattr(door, "Name", ""),
        level_name,
        "FireRating mangler",
    ])

OUT = [["ElementId", "Dørtype", "Level", "Fejl"]] + missing if missing else ["OK - alle døre har FireRating"]
Forudsætter at projektet bruger et af parameternavnene i scriptets PARAMETER_NAMES-liste.
branddørefireratingrevit

Find rum uden rumnummer/navn

Dynamo / Revit

Finder placerede og uplacerede rum, hvor rumnummer eller rumnavn mangler, så rumdata kan ryddes op før skemaer og IFC.

import clr
clr.AddReference("RevitAPI")
clr.AddReference("RevitServices")

from Autodesk.Revit.DB import *
from RevitServices.Persistence import DocumentManager

doc = DocumentManager.Instance.CurrentDBDocument

def parameter_text(element, builtin):
    parameter = element.get_Parameter(builtin)
    if not parameter or not parameter.HasValue:
        return ""
    value = parameter.AsString() or parameter.AsValueString()
    return str(value).strip() if value else ""

rooms = (
    FilteredElementCollector(doc)
    .OfCategory(BuiltInCategory.OST_Rooms)
    .WhereElementIsNotElementType()
    .ToElements()
)

missing = []
for room in rooms:
    number = parameter_text(room, BuiltInParameter.ROOM_NUMBER)
    name = parameter_text(room, BuiltInParameter.ROOM_NAME)
    area = room.Area if hasattr(room, "Area") else 0

    issues = []
    if not number:
        issues.append("Rumnummer mangler")
    if not name:
        issues.append("Rumnavn mangler")
    if area <= 0:
        issues.append("Rum er ikke placeret eller har 0 m2")

    if issues:
        level_name = room.Level.Name if room.Level else ""
        missing.append([room.Id.IntegerValue, number, name, level_name, "; ".join(issues)])

OUT = [["ElementId", "Nummer", "Navn", "Level", "Fejl"]] + missing if missing else ["OK - alle rum har nummer og navn"]
Rum med 0 m2 markeres også, da de ofte fejler i IFC- og arealaflevering.
rumrumnummerroom datarevit

Find MEP-komponenter uden SystemCode

Dynamo / Revit

Scanner VVS, ventilation, el og plumbing-komponenter for manglende systemkode på instance eller type.

import clr
clr.AddReference("RevitAPI")
clr.AddReference("RevitServices")

from Autodesk.Revit.DB import *
from RevitServices.Persistence import DocumentManager

doc = DocumentManager.Instance.CurrentDBDocument
PARAMETER_NAMES = ["SystemCode", "System Code", "Systemkode", "System_Kode", "System Classification"]
MEP_CATEGORIES = [
    BuiltInCategory.OST_DuctTerminal,
    BuiltInCategory.OST_DuctAccessory,
    BuiltInCategory.OST_DuctFitting,
    BuiltInCategory.OST_MechanicalEquipment,
    BuiltInCategory.OST_PipeAccessory,
    BuiltInCategory.OST_PipeFitting,
    BuiltInCategory.OST_PlumbingFixtures,
    BuiltInCategory.OST_ElectricalEquipment,
    BuiltInCategory.OST_ElectricalFixtures,
    BuiltInCategory.OST_LightingFixtures,
    BuiltInCategory.OST_CableTray,
    BuiltInCategory.OST_Conduit,
]

def text_value(parameter):
    if not parameter or not parameter.HasValue:
        return None
    value = parameter.AsString() or parameter.AsValueString()
    return str(value).strip() if value and str(value).strip() else None

def get_value(element, names):
    for name in names:
        value = text_value(element.LookupParameter(name))
        if value:
            return value

    type_id = element.GetTypeId()
    if type_id and type_id != ElementId.InvalidElementId:
        element_type = doc.GetElement(type_id)
        if element_type:
            for name in names:
                value = text_value(element_type.LookupParameter(name))
                if value:
                    return value
    return None

elements = []
for category in MEP_CATEGORIES:
    try:
        elements.extend(
            FilteredElementCollector(doc)
            .OfCategory(category)
            .WhereElementIsNotElementType()
            .ToElements()
        )
    except:
        pass

missing = []
seen = set()
for element in elements:
    if element.Id.IntegerValue in seen:
        continue
    seen.add(element.Id.IntegerValue)

    if get_value(element, PARAMETER_NAMES):
        continue

    missing.append([
        element.Id.IntegerValue,
        element.Category.Name if element.Category else "",
        getattr(element, "Name", ""),
        "SystemCode mangler",
    ])

OUT = [["ElementId", "Kategori", "Navn", "Fejl"]] + missing if missing else ["OK - alle MEP-komponenter har SystemCode"]
Tilpas PARAMETER_NAMES hvis jeres projekt bruger en anden systemkode-standard.
mepsystemcodevvsel

Find objekter uden FM_ID

Dynamo / Revit

Finder modelobjekter uden drifts-ID eller asset-ID, så FM-data er klar til digital aflevering.

import clr
clr.AddReference("RevitAPI")
clr.AddReference("RevitServices")

from Autodesk.Revit.DB import *
from RevitServices.Persistence import DocumentManager

doc = DocumentManager.Instance.CurrentDBDocument
PARAMETER_NAMES = ["FM_ID", "FMID", "AssetID", "Asset Identifier", "DriftsID", "FacilityManagementId"]
EXCLUDED_CATEGORIES = set(["Cameras", "Views", "Sheets"])

def text_value(parameter):
    if not parameter or not parameter.HasValue:
        return None
    value = parameter.AsString() or parameter.AsValueString()
    return str(value).strip() if value and str(value).strip() else None

def get_value(element, names):
    for name in names:
        value = text_value(element.LookupParameter(name))
        if value:
            return value

    type_id = element.GetTypeId()
    if type_id and type_id != ElementId.InvalidElementId:
        element_type = doc.GetElement(type_id)
        if element_type:
            for name in names:
                value = text_value(element_type.LookupParameter(name))
                if value:
                    return value
    return None

def include_element(element):
    if not element.Category:
        return False
    if element.Category.Name in EXCLUDED_CATEGORIES:
        return False
    return element.Category.CategoryType == CategoryType.Model and not element.ViewSpecific

elements = FilteredElementCollector(doc).WhereElementIsNotElementType().ToElements()
missing = []

for element in elements:
    if not include_element(element):
        continue
    if get_value(element, PARAMETER_NAMES):
        continue

    missing.append([
        element.Id.IntegerValue,
        element.Category.Name,
        getattr(element, "Name", ""),
        "FM_ID mangler",
    ])

OUT = [["ElementId", "Kategori", "Navn", "Fejl"]] + missing if missing else ["OK - alle relevante objekter har FM_ID"]
Brug filteret i include_element til at afgrænse hvilke kategorier der skal afleveres til drift.
fmassetafleveringrevit

Find views/sheets med forkert navngivning

Dynamo / Revit

Validerer navngivning af sheets og views mod projektets regex-mønstre, så output passer til CDE og IKT-krav.

import clr
clr.AddReference("RevitAPI")
clr.AddReference("RevitServices")

from Autodesk.Revit.DB import *
from RevitServices.Persistence import DocumentManager
import re

doc = DocumentManager.Instance.CurrentDBDocument

SHEET_NUMBER_PATTERN = r"^[A-Z]{2,3}-\d{3,4}(-[A-Z0-9]+)?$"
VIEW_NAME_PATTERN = r"^(ARK|KON|EL|VVS|VENT|BRAND|BIM)-[A-Z0-9]+-.+"

sheets = FilteredElementCollector(doc).OfClass(ViewSheet).ToElements()
views = FilteredElementCollector(doc).OfClass(View).ToElements()

issues = []

for sheet in sheets:
    if sheet.IsPlaceholder:
        continue
    if not re.match(SHEET_NUMBER_PATTERN, sheet.SheetNumber or ""):
        issues.append(["Sheet", sheet.Id.IntegerValue, sheet.SheetNumber, sheet.Name, "SheetNumber matcher ikke standard"])

for view in views:
    if view.IsTemplate:
        continue
    if view.ViewType in [ViewType.ProjectBrowser, ViewType.SystemBrowser, ViewType.Internal]:
        continue
    if not re.match(VIEW_NAME_PATTERN, view.Name or ""):
        issues.append(["View", view.Id.IntegerValue, "", view.Name, "ViewName matcher ikke standard"])

OUT = [["Type", "ElementId", "SheetNumber", "Navn", "Fejl"]] + issues if issues else ["OK - views og sheets følger navngivningsstandarden"]
Tilpas SHEET_NUMBER_PATTERN og VIEW_NAME_PATTERN til jeres CDE/BIM-manual.
viewssheetsnavngivningcde

IFC export readiness check

Dynamo / Revit

Kører en samlet preflight før IFC-eksport med checks for projektinfo, links, rumdata, klassifikation og modeladvarsler.

import clr
clr.AddReference("RevitAPI")
clr.AddReference("RevitServices")

from Autodesk.Revit.DB import *
from RevitServices.Persistence import DocumentManager

doc = DocumentManager.Instance.CurrentDBDocument

def parameter_text(element, name):
    parameter = element.LookupParameter(name) if element else None
    if not parameter or not parameter.HasValue:
        return ""
    value = parameter.AsString() or parameter.AsValueString()
    return str(value).strip() if value else ""

def has_any_parameter(element, names):
    for name in names:
        if parameter_text(element, name):
            return True
    type_id = element.GetTypeId()
    if type_id and type_id != ElementId.InvalidElementId:
        element_type = doc.GetElement(type_id)
        if element_type:
            for name in names:
                if parameter_text(element_type, name):
                    return True
    return False

checks = []

project_info = doc.ProjectInformation
required_project_info = ["Project Number", "Project Name"]
for name in required_project_info:
    checks.append(["Projektinfo", name, "OK" if parameter_text(project_info, name) else "MANGLER"])

rooms = FilteredElementCollector(doc).OfCategory(BuiltInCategory.OST_Rooms).WhereElementIsNotElementType().ToElements()
bad_rooms = []
for room in rooms:
    number = room.get_Parameter(BuiltInParameter.ROOM_NUMBER)
    name = room.get_Parameter(BuiltInParameter.ROOM_NAME)
    number_value = number.AsString().strip() if number and number.AsString() else ""
    name_value = name.AsString().strip() if name and name.AsString() else ""
    if not number_value or not name_value or room.Area <= 0:
        bad_rooms.append(room.Id.IntegerValue)
checks.append(["Rumdata", "Rum uden nummer/navn/areal", "OK" if not bad_rooms else str(len(bad_rooms))])

elements = FilteredElementCollector(doc).WhereElementIsNotElementType().ToElements()
classification_missing = 0
for element in elements:
    if not element.Category or element.Category.CategoryType != CategoryType.Model or element.ViewSpecific:
        continue
    if not has_any_parameter(element, ["CCSCode", "CCI_Code", "ClassificationCode", "IfcExportAs"]):
        classification_missing += 1
checks.append(["Klassifikation", "Objekter uden CCS/CCI/IfcExportAs", "OK" if classification_missing == 0 else str(classification_missing)])

try:
    link_status = []
    for link_type in FilteredElementCollector(doc).OfClass(RevitLinkType).ToElements():
        link_status.append(str(link_type.GetLinkedFileStatus()))
    unloaded = [status for status in link_status if "Loaded" not in status]
    checks.append(["Links", "Revit links loaded", "OK" if not unloaded else ", ".join(unloaded)])
except:
    checks.append(["Links", "Revit links loaded", "Kunne ikke læses"])

warnings_count = len(doc.GetWarnings())
checks.append(["Model health", "Revit warnings", "OK" if warnings_count == 0 else str(warnings_count)])

OUT = [["Område", "Check", "Status"]] + checks
Preflighten er read-only og erstatter ikke en fuld IFC-validering i Solibri/IDS.
ifcpreflightexportrevit

LOD/LOI parameter completeness check

Dynamo / Revit

Kontrollerer om modelobjekter har de aftalte LOD/LOI- og informationsparametre før fagmodel- eller afleveringsmilepæle.

import clr
clr.AddReference("RevitAPI")
clr.AddReference("RevitServices")

from Autodesk.Revit.DB import *
from RevitServices.Persistence import DocumentManager

doc = DocumentManager.Instance.CurrentDBDocument
REQUIRED_PARAMETERS = ["LOD", "LOI", "ModelStatus", "DisciplineCode"]

def parameter_has_value(element, name):
    parameter = element.LookupParameter(name)
    if parameter and parameter.HasValue:
        value = parameter.AsString() or parameter.AsValueString()
        if value and str(value).strip():
            return True

    type_id = element.GetTypeId()
    if type_id and type_id != ElementId.InvalidElementId:
        element_type = doc.GetElement(type_id)
        if element_type:
            type_parameter = element_type.LookupParameter(name)
            if type_parameter and type_parameter.HasValue:
                value = type_parameter.AsString() or type_parameter.AsValueString()
                if value and str(value).strip():
                    return True
    return False

def include_element(element):
    return (
        element.Category
        and element.Category.CategoryType == CategoryType.Model
        and not element.ViewSpecific
    )

elements = FilteredElementCollector(doc).WhereElementIsNotElementType().ToElements()
missing = []

for element in elements:
    if not include_element(element):
        continue

    missing_parameters = [name for name in REQUIRED_PARAMETERS if not parameter_has_value(element, name)]
    if missing_parameters:
        missing.append([
            element.Id.IntegerValue,
            element.Category.Name,
            getattr(element, "Name", ""),
            ", ".join(missing_parameters),
        ])

OUT = [["ElementId", "Kategori", "Navn", "Manglende parametre"]] + missing if missing else ["OK - alle relevante elementer har LOD/LOI-data"]
Tilpas REQUIRED_PARAMETERS til projektets BEP/EIR før brug.
lodloibepdatakrav

Model health report

Dynamo / Revit

Genererer en hurtig sundhedsrapport med Revit warnings, importerede CAD-filer, in-place families, uplacerede rum og linkstatus.

import clr
clr.AddReference("RevitAPI")
clr.AddReference("RevitServices")

from Autodesk.Revit.DB import *
from RevitServices.Persistence import DocumentManager

doc = DocumentManager.Instance.CurrentDBDocument
report = []

warnings = doc.GetWarnings()
report.append(["Warnings", len(warnings), "Bør gennemgås før aflevering"])

imports = FilteredElementCollector(doc).OfClass(ImportInstance).ToElements()
report.append(["Importerede CAD/ImportInstance", len(imports), "Fjern eller link i stedet hvis muligt"])

rooms = FilteredElementCollector(doc).OfCategory(BuiltInCategory.OST_Rooms).WhereElementIsNotElementType().ToElements()
unplaced_rooms = [room for room in rooms if room.Area <= 0]
report.append(["Uplacerede rum", len(unplaced_rooms), "Skal ryddes op før IFC/arealskema"])

views = FilteredElementCollector(doc).OfClass(View).ToElements()
working_views = [view for view in views if not view.IsTemplate and not view.Name.startswith("{")]
report.append(["Views uden templates", len(working_views), "Kontroller browserstruktur og navngivning"])

in_place = []
for family_instance in FilteredElementCollector(doc).OfClass(FamilyInstance).ToElements():
    try:
        family = family_instance.Symbol.Family
        if family and family.IsInPlace:
            in_place.append(family_instance)
    except:
        pass
report.append(["In-place families", len(in_place), "Bør kun bruges hvis aftalt"])

try:
    link_rows = []
    for link_type in FilteredElementCollector(doc).OfClass(RevitLinkType).ToElements():
        link_rows.append([link_type.Name, str(link_type.GetLinkedFileStatus())])
    unloaded = [row for row in link_rows if "Loaded" not in row[1]]
    report.append(["Unloaded links", len(unloaded), "Alle nødvendige links bør være loaded"])
except:
    report.append(["Unloaded links", "Ukendt", "Linkstatus kunne ikke læses"])

OUT = [["Check", "Antal", "Kommentar"]] + report
Rapporten ændrer ikke modellen. Brug den som fast QA før koordineringsmøder og aflevering.
model healthwarningscadrevit

Digital aflevering check

Dynamo / Revit

Samler afleveringskritiske checks for projektinfo, rumdata, klassifikation, FM_ID, views/sheets og Revit warnings.

import clr
clr.AddReference("RevitAPI")
clr.AddReference("RevitServices")

from Autodesk.Revit.DB import *
from RevitServices.Persistence import DocumentManager
import re

doc = DocumentManager.Instance.CurrentDBDocument

PROJECT_INFO_PARAMETERS = ["Project Number", "Project Name", "Client Name"]
CLASSIFICATION_PARAMETERS = ["CCSCode", "CCI_Code", "ClassificationCode"]
FM_PARAMETERS = ["FM_ID", "FMID", "AssetID", "Asset Identifier", "DriftsID"]
SHEET_PATTERN = r"^[A-Z]{2,3}-\d{3,4}(-[A-Z0-9]+)?$"

def text_value(parameter):
    if not parameter or not parameter.HasValue:
        return ""
    value = parameter.AsString() or parameter.AsValueString()
    return str(value).strip() if value else ""

def has_any_parameter(element, names):
    for name in names:
        if text_value(element.LookupParameter(name)):
            return True

    type_id = element.GetTypeId()
    if type_id and type_id != ElementId.InvalidElementId:
        element_type = doc.GetElement(type_id)
        if element_type:
            for name in names:
                if text_value(element_type.LookupParameter(name)):
                    return True
    return False

def include_model_element(element):
    return (
        element.Category
        and element.Category.CategoryType == CategoryType.Model
        and not element.ViewSpecific
    )

summary = []

project_missing = [name for name in PROJECT_INFO_PARAMETERS if not text_value(doc.ProjectInformation.LookupParameter(name))]
summary.append(["Projektinfo", "OK" if not project_missing else "MANGLER", ", ".join(project_missing)])

rooms = FilteredElementCollector(doc).OfCategory(BuiltInCategory.OST_Rooms).WhereElementIsNotElementType().ToElements()
bad_rooms = 0
for room in rooms:
    number = room.get_Parameter(BuiltInParameter.ROOM_NUMBER)
    name = room.get_Parameter(BuiltInParameter.ROOM_NAME)
    if not text_value(number) or not text_value(name) or room.Area <= 0:
        bad_rooms += 1
summary.append(["Rumdata", "OK" if bad_rooms == 0 else "FEJL", str(bad_rooms)])

elements = [element for element in FilteredElementCollector(doc).WhereElementIsNotElementType().ToElements() if include_model_element(element)]
missing_classification = [element.Id.IntegerValue for element in elements if not has_any_parameter(element, CLASSIFICATION_PARAMETERS)]
missing_fm = [element.Id.IntegerValue for element in elements if not has_any_parameter(element, FM_PARAMETERS)]
summary.append(["CCS/CCI", "OK" if not missing_classification else "FEJL", str(len(missing_classification))])
summary.append(["FM_ID", "OK" if not missing_fm else "FEJL", str(len(missing_fm))])

sheets = FilteredElementCollector(doc).OfClass(ViewSheet).ToElements()
bad_sheets = [sheet.SheetNumber for sheet in sheets if not sheet.IsPlaceholder and not re.match(SHEET_PATTERN, sheet.SheetNumber or "")]
summary.append(["Sheet navngivning", "OK" if not bad_sheets else "FEJL", str(len(bad_sheets))])

warnings_count = len(doc.GetWarnings())
summary.append(["Revit warnings", "OK" if warnings_count == 0 else "ADVARSEL", str(warnings_count)])

delivery_ready = all(row[1] == "OK" for row in summary)
OUT = [["Digital aflevering klar", delivery_ready], ["Område", "Status", "Antal/mangler"]] + summary
Afleveringschecket er en teknisk preflight. Sammenhold altid med projektets IKT-aftale og bygherrekrav.
afleveringfmiktqa