Share my code for export multiple views to Ifc

Hello PyRevitUsers,

i will share my code, because i am happy, the code works so well and it saves me time. I automated the task to export Ifcs from one file made of severial views…

if you have any comments or to make my code more smooth let me know!

# -*- coding: utf-8 -*-
__title__ = "IfcMultiViewExport"
__doc__ = """
            Version = 1.0
            Date    = 29.10.2023
          """


# Regular + Autodesk
from Autodesk.Revit.DB import *
import System
import os
import time
#import ipywidgets as widgets
#from IPython.display import display

# pyRevit
from pyrevit import revit, forms, script
from pyrevit import DB, UI
from pyrevit import PyRevitException, PyRevitIOError
from pyrevit import HOST_APP
from pyrevit import EXEC_PARAMS
from pyrevit.compat import safe_strtype
from pyrevit import framework
from pyrevit.output import linkmaker
from pyrevit.userconfig import user_config

# .NET Imports
import sys
import clr

clr.AddReference("System")

from System.Collections.Generic import List
from collections import defaultdict

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

time_start = time.time()

# 1️⃣ Get ThreeD IfcViews

collector = FilteredElementCollector(doc).OfCategory(BuiltInCategory.OST_Views)
all_views = collector.WhereElementIsNotElementType().ToElements()
all_ThreeDViews = [i for i in all_views if i.ViewType == ViewType.ThreeD]

ifcViews = [i for i in all_ThreeDViews if i.Name.Contains("HS_FM_G")]
viewNames = [i.Name for i in ifcViews]

# 2️⃣ Get Ifc Settings

folder = "C:/Users/MaxM.WERKBANK/Downloads"
view = ifcViews
name = viewNames
fileVersion = "IFC4DTV"
projectOrigin = "3"
inputPhase = None
userDefinedPset = "P:/Projekte/Demo/CAD/Revit/SharedParameter/IFC-Benutzerdefinierter Eigenschaftssatz_Demo.txt"
revitInternalPset = False
wallandcolumnsplitting = False
exportbasequantities = True

if inputPhase != None:
    phaseString = str(inputPhase.Id)
else:
    pass

if userDefinedPset != "":
    userDefPsetBool = "true"
else:
    userDefPsetBool = "false"

if revitInternalPset == True:
    revitInternalPset = "true"
else:
    revitInternalPset = "false"

t = Transaction(doc,"IfcExport")
t.Start()

result = []

for i,v in enumerate(view):
    options = IFCExportOptions()

    # if fileversion != None:
    #	options.FileVersion = fileversion
    if fileVersion == "IFC4":
        options.FileVersion = IFCVersion.IFC4
    if fileVersion == "IFC4RV":
        options.FileVersion = IFCVersion.IFC4RV
    if fileVersion == "IFC4DTV":
        options.FileVersion = IFCVersion.IFC4DTV
    if fileVersion == "IFC2x2":
        options.FileVersion = IFCVersion.IFC2x2
    if fileVersion == "IFC2x3":
        options.FileVersion = IFCVersion.IFC2x3
    if fileVersion == "IFC2x3CV2":
        options.FileVersion = IFCVersion.IFC2x3CV2
    if fileVersion == "IFC2x3BFM":
        options.FileVersion = IFCVersion.IFC2x3BFM
    if fileVersion == "IFC2x3FM":
        options.FileVersion = IFCVersion.IFC2x3FM
    if fileVersion == "IFCBCA":
        options.FileVersion = IFCVersion.IFCBCA
    if fileVersion == "IFCCOBIE":
        options.FileVersion = IFCVersion.IFCCOBIE
    if fileVersion == "":
        options.FileVersion = IFCVersion.Default

    options.WallAndColumnSplitting = wallandcolumnsplitting
    # options.AddOption("IncludeSteelElements", "true")

    options.FilterViewId = v.Id

    if inputPhase != None:
        options.AddOption("ActivePhase", phaseString)
    else:
        pass
    options.AddOption("SitePlacement", projectOrigin)
    options.AddOption("SpaceBoundaries ", "0")

    # Additional Content #
    options.AddOption("Export2DElements", "false")
    options.AddOption("ExportRoomsInView", "false")
    options.AddOption("VisibleElementsOfCurrentView ", "true")
    # True doesn't work. It would be necessary to use OpenInBackground method.
    options.AddOption("ExportLinkedFiles", "false")

    # Property Sets #
    options.ExportBaseQuantities = exportbasequantities
    options.AddOption("ExportInternalRevitPropertySets", revitInternalPset)
    options.AddOption("ExportIFCCommonPropertySets", "true")
    options.AddOption("ExportSchedulesAsPsets", "false")
    options.AddOption("ExportSpecificSchedules", "false")
    options.AddOption("ExportUserDefinedPsets", userDefPsetBool)

    if userDefinedPset != "":
        options.AddOption("ExportUserDefinedPsetsFileName", userDefinedPset)
    else:
        pass

    options.AddOption("Use2DRoomBoundaryForVolume ", "false")
    options.AddOption("UseFamilyAndTypeNameForReference ", "false")
    options.AddOption("ExportPartsAsBuildingElements", "false")
    options.AddOption("ExportBoundingBox", "false")
    options.AddOption("ExportSolidModelRep", "true")
    options.AddOption("StoreIFCGUID", "true")
    options.AddOption("UseActiveViewGeometry", "true")
    options.AddOption("IncludeSiteElevation", "true")
    options.AddOption("ExportAnnotations ", "true")

    options.AddOption("TessellationLevelOfDetail", "0,5")

    # options.AddOption("IFCFileType", "0") # "File type"
    #  0 = IFC
    #  1 = IFC XML
    #  2 = Zipped IFC
    #  3 = Zipped IFC XML

    c = doc.Export(folder, name[i], options)
    result.append(c)

# End Transaction
t.Commit()


if fileVersion == "":
    print("Default settings used")
else:
    print("Success")

time_end = time.time()
duration = time_end - time_start
print('\n The code took {} seconds to run.'.format(duration))

2024-01-22_11h39_02

KR

Andreas

5 Likes

Hello Andreas,

Your code is in perfect timing because I have a similar project and you will save me a lot of time !
I will try to work on my project this coming weeks and give a feedback on this post.

Kind Regards,
Yoann

1 Like

Hi,
I like to help making the code better (be it more readable or efficient), so let me throw some hints:

  • since you use all_ThreeDViews only once, you could use a generator instead of a list (simply use round parenthesis instead of square brackets), it will save some memory. Alternatively, you could just do the viewtype and name checks in a single pass: view = [i for i in all_views if i.ViewType == ViewType.ThreeD and i.Name.Contains("HS_FM_G")]. I named it view because you can skip having 2 variables with the same values… it is a tiny amount of RAM, but a great amount of cognitive load saved!
  • you save the viewNames / name, then use them in the loop only once by calling viewNames[i]; since view contains the view items, you can just use v.Name in the loop and drop viewNames entirely. And now the view list can be turned into a generator to shave off more memory! Maybe it is overkill for the View object, but take it as a general advice to avoid consuming RAM when it is not needed.
  • else: pass is useless, drop it
  • if you define al bool2str(condition) function that return str(condition).lower() then you can use userDefPsetBool = bool2str(userDefinedPset !="") and revitInternalPset = bool2str(revitInternalPset)
  • for the fileVersion checks you should use elif to skip the checks that follows the right one; you could also move them outside of the loop since the fileVersion doesn’t change. Even better, I would create a function get_file_version(ifc_version) that returns the IFCVersion enum (instead of directly set the options.FileVersion) so that you can assign them at every loop
  • looking at the revi api, the IfcExportOptions could be initialized to be a copy of another one; this means that you can create a base_export_options object with the options that don’t change outside of the loop, then the loop would simply be
    result = []
    for v in view:
        options = IFCExportOptions(base_export_options)
        options.FilterViewId = v.Id
        result.append(doc.Export(folder, v.Name, options)
    
  • do you really need a transaction? Nothing is written on the revit doc, so I suppose you can skip it.

All of this, along with some imports cleanup, would translate into the followinf, untested code:

# -*- coding: utf-8 -*-
__title__ = "IfcMultiViewExport"
__doc__ = """
            Version = 1.0
            Date    = 29.10.2023
          """
import time
from pyrevit.DB import IFCVersion, IFCExportOptions, FilteredElementCollector, BuiltInCategory, ViewType

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


def bool2str(condition):
    return str(condition).lower()

def get_ifc_version(version):
    # this could also be made into a dictionary to speedup the lookup
    if version == "IFC4":
        return IFCVersion.IFC4
    if version == "IFC4RV":
        return IFCVersion.IFC4RV
    if version == "IFC4DTV":
        return IFCVersion.IFC4DTV
    if version == "IFC2x2":
        return IFCVersion.IFC2x2
    if version == "IFC2x3":
        return IFCVersion.IFC2x3
    if version == "IFC2x3CV2":
        return IFCVersion.IFC2x3CV2
    if version == "IFC2x3BFM":
        return IFCVersion.IFC2x3BFM
    if version == "IFC2x3FM":
        return IFCVersion.IFC2x3FM
    if version == "IFCBCA":
        return IFCVersion.IFCBCA
    if version == "IFCCOBIE":
        return IFCVersion.IFCCOBIE
    return IFCVersion.Default

time_start = time.time()

# 1️⃣ Get ThreeD IfcViews

collector = FilteredElementCollector(doc).OfCategory(BuiltInCategory.OST_Views)
all_views = collector.WhereElementIsNotElementType().ToElements()
views = (i for i in all_views if i.ViewType == ViewType.ThreeD and i.Name.Contains("HS_FM_G"))

# 2️⃣ Get Ifc Settings

folder = "C:/Users/MaxM.WERKBANK/Downloads"
fileVersion = "IFC4DTV"
projectOrigin = "3"
inputPhase = None
userDefinedPset = "P:/Projekte/Demo/CAD/Revit/SharedParameter/IFC-Benutzerdefinierter Eigenschaftssatz_Demo.txt"
revitInternalPset = False
wallandcolumnsplitting = False
exportbasequantities = True


base_options = IFCExportOptions()
base_options.FileVersion = get_ifc_version(fileVersion)
base_options.WallAndColumnSplitting = wallandcolumnsplitting
if inputPhase is not None:
    base_options.AddOption("ActivePhase", str(inputPhase.Id))
base_options.AddOption("SitePlacement", projectOrigin)
base_options.AddOption("SpaceBoundaries ", "0")
# Additional Content #
base_options.AddOption("Export2DElements", "false")
base_options.AddOption("ExportRoomsInView", "false")
base_options.AddOption("VisibleElementsOfCurrentView ", "true")
# True doesn't work. It would be necessary to use OpenInBackground method.
base_options.AddOption("ExportLinkedFiles", "false")

# Property Sets #
base_options.ExportBaseQuantities = exportbasequantities
base_options.AddOption("ExportInternalRevitPropertySets", bool2str(revitInternalPset))
base_options.AddOption("ExportIFCCommonPropertySets", "true")
base_options.AddOption("ExportSchedulesAsPsets", "false")
base_options.AddOption("ExportSpecificSchedules", "false")
base_options.AddOption("ExportUserDefinedPsets", bool2str(userDefinedPset != ""))

if userDefinedPset != "":
    base_options.AddOption("ExportUserDefinedPsetsFileName", userDefinedPset)

base_options.AddOption("Use2DRoomBoundaryForVolume ", "false")
base_options.AddOption("UseFamilyAndTypeNameForReference ", "false")
base_options.AddOption("ExportPartsAsBuildingElements", "false")
base_options.AddOption("ExportBoundingBox", "false")
base_options.AddOption("ExportSolidModelRep", "true")
base_options.AddOption("StoreIFCGUID", "true")
base_options.AddOption("UseActiveViewGeometry", "true")
base_options.AddOption("IncludeSiteElevation", "true")
base_options.AddOption("ExportAnnotations ", "true")

base_options.AddOption("TessellationLevelOfDetail", "0,5")

for v in views:
    options = IFCExportOptions(base_options)
    options.FilterViewId = v.Id
    doc.Export(folder, v.Name, options)


if fileVersion == "":
    print("Default settings used")
else:
    print("Success")

time_end = time.time()
duration = time_end - time_start
print('\n The code took {} seconds to run.'.format(duration))
1 Like


@sanzoghenzo ,

maybe yes… i will remain the old code… i know when i write a ifc sometimes it happens that i own things or groups get opened,… maybe because of backwriting IfcGUID.

KR

Andreas

You’re absolutely right, I didn’t think about the ifc guid!
Transaction is definitely needed, sorry…