revit-ifc icon indicating copy to clipboard operation
revit-ifc copied to clipboard

Can I link IFC files programmatically using the API?

Open Moult opened this issue 3 years ago • 6 comments

Is it possible to link an IFC programmatically? Such that I can run it via a macro? I'd like to link a bunch of IFCs in bulk.

I saw some hints in the API docs that suggest it might be possible, and made a start, but I haven't begun to code dive and decided to ask first in case there was a simple example I was missing. This code snippet runs without errors and creates an IFC link but does not seem to actually import any IFC data.

Document doc = this.Document;
Transaction t = new Transaction(doc, "Link");
string filepath = "C:/Users/dion/Documents/bulk/test.ifc";
string filepath_rvt = "C:/Users/dion/Documents/bulk/test.ifc.RVT";
t.Start();
Document doc_ifc = doc.Application.NewProjectDocument(UnitSystem.Metric);
SaveAsOptions save_options = new SaveAsOptions();
save_options.OverwriteExistingFile = true;
doc_ifc.SaveAs(filepath_rvt, save_options);
doc_ifc.Close();
RevitLinkOptions link_options = new RevitLinkOptions(false);
ExternalResourceReference ifc_resource = ExternalResourceReference.CreateLocalResource(doc, ExternalResourceTypes.BuiltInExternalResourceTypes.IFCLink, ModelPathUtils.ConvertUserVisiblePathToModelPath(filepath), PathType.Absolute);

LinkLoadResult result = RevitLinkType.CreateFromIFC(doc, ifc_resource, filepath_rvt, false, link_options);
if (result.ElementId != ElementId.InvalidElementId) {
    RevitLinkInstance.Create(doc, result.ElementId);
}
doc.Regenerate();
t.Commit();

I find it also interesting that a similar attempt in Python has a different signature (instead of external resource it expects a string filepath) but the call to RevitLinkType.CreateFromIFC seems to keep on triggering an error "A serious error has occurred" dialog and leaves Revit in an unfinished state of sorts.

        doc = self.Document
        app = self.Application.Application
        t = Transaction(doc, "link")
        filepath = 'C:/Users/moud308/Documents/bulk/test.ifc'
        filepath_rvt = filepath + ".RVT"
        t.Start()
        doc_ifc = app.NewProjectDocument(UnitSystem.Metric)
        save_options = SaveAsOptions()
        save_options.OverwriteExistingFile = True
        doc_ifc.SaveAs(filepath_rvt, save_options)
        doc_ifc.Close()
        
        link_options = RevitLinkOptions(False)
        ifc_resource = ExternalResourceReference.CreateLocalResource(doc, ExternalResourceTypes.BuiltInExternalResourceTypes.IFCLink, ModelPathUtils.ConvertUserVisiblePathToModelPath(filepath), PathType.Absolute)
        result = RevitLinkType.CreateFromIFC(doc, filepath, ifc_resource, True, link_options)
        instance = RevitLinkInstance.Create(doc, result.ElementId)
        doc.Regenerate()
        t.Commit()

What steps am I missing?

Moult avatar Nov 25 '21 03:11 Moult

Could we get a quick "official" comment on that please? The issue was logged a year ago at time of my writtng. It was tagged as "problem" by one of the maintainer. Is it simply not doable to link an IFC from API or is there another way to proceed? This repo is advertised in its readme as "This contains the source code for Link IFC, IFC export and the IFC export UI"

leefsmp avatar Nov 07 '22 13:11 leefsmp

It is possible to link an IFC from API since this is the code that Revit uses to link IFC (we don't have a different internal way to do it). I think any code that does so probably has to call this code as a start. But, i.e., Navisworks and BIM360 process IFC files by using the API here to create it. If you wanted to make your own link, you could also do that, but obviously would be more work to start from scratch.

AngelVelezSosa avatar Nov 07 '22 15:11 AngelVelezSosa

Thanks, but is there a proper snipper somewhere that illustrates that in details? What I'm looking for is to create a new document dynamically from API and link an ifc that is on disk, I dont want to save any file automatically. I'm not able to find such sample so far. I'm looking at the CreateFromIFC and I can't figure out what is the revitLinkedFilePath parameter supposed to do: "The path of the existing Revit file that contains elements created via an import by reference operation. This must be a full path." ...

public static RevitLinkLoadResult CreateFromIFC(
	Document document,
	string ifcFilePath,
	string revitLinkedFilePath,
	bool recreateLink,
	RevitLinkOptions options
)

leefsmp avatar Nov 07 '22 16:11 leefsmp

Hi Everyone!

Has anybody figured out how to link IFC files directly from BIM360?

I can create the ExternalResource, but since the ifc.RVT file is or would be stored on BIM360, I'm not sure how to handle that path.

victorbastidas avatar Aug 11 '23 16:08 victorbastidas

Any updates on this issue? I am facing the exact some challenge. I need to automate the IFC-Import or IFC-Linking into Revit to process and iterate over a couple of IFC-Files in one batch. Like @leefsmp I cant really figure out what's the revitLinkedFilePath parameter supposed to do. Using the code from @Moult I get to a point where I have a Revit Link to the IFC-File I want to Import, but it seems like no geometry is imported. Rightclick "Reload" on the Link changes nothing, but when I select "Reload From..." and select the IFC File, the IFC Model is loaded into Revit. So it seems like I am very close to a solution... :D

Edit: Here is working pieces of code that links a IFC into an Revit Doc via API (using the Hints given in: https://help.autodesk.com/view/RVT/2024/ENU/?guid=GUID-DE8B322A-A507-4E03-93EC-AA21F354E43B and https://stackoverflow.com/questions/74344678/how-to-link-ifc-with-revit-design-automation

        //Method for Linking IFC into active Revit-Doc
        public static bool LinkIfcFile(Document doc, string ifcFilepath, bool saveIntermediateRvtFile = false) {
            Transaction t = new Transaction(doc, "LinkRvtIfcFile");
            ModelPath rvtIfcPath;
            string rvtFilepath = ifcFilepath + ".RVT";
            Document docIfc = doc.Application.NewProjectDocument(UnitSystem.Metric);
            try {
                IDictionary<string, string> options = new Dictionary<string, string>();
                options["Action"] = "Link";   // default is Open.
                options["Intent"] = "Reference"; // This is the default.
                Importer importer = Importer.CreateImporter(docIfc, ifcFilepath, options);

                importer.ReferenceIFC(doc, ifcFilepath);
                rvtIfcPath = ModelPathUtils.ConvertUserVisiblePathToModelPath(rvtFilepath);

                t.Start();
                RevitLinkOptions linkOptions = new RevitLinkOptions(false);
                LinkLoadResult rvtifcLink = RevitLinkType.Create(doc, rvtIfcPath, linkOptions);
                if (rvtifcLink.ElementId != ElementId.InvalidElementId) {
                    RevitLinkInstance.Create(doc, rvtifcLink.ElementId);
                }
                doc.Regenerate();
                t.Commit();
            }
            catch (Exception ex) {
                Console.WriteLine(ex.ToString());
                t.RollBack();
                return false;
            }

            if (saveIntermediateRvtFile) {
                SaveAsOptions saveOptions = new SaveAsOptions();
                saveOptions.OverwriteExistingFile = true;
                docIfc.SaveAs(rvtIfcPath, saveOptions);
            }
            return true;
        }

simonhoeng avatar Nov 14 '23 16:11 simonhoeng

I have a slightly lighter version of the above code working...still takes ages to execute though. The logger is just in there for debug purposes. Also you don't have to pass in a Dictionary here, this is just my use case. The TransactionGroup just allows you to undo this operation in one.

Edit: the App.ActionEventHandler.Raise method is also just because I'm calling this from another thread so this just queues the action to be undertaken (delegate) for Revit to act on.

    public void ImportIfcFiles(Dictionary<Guid, string> ifcFiles)
    {
        App.ActionEventHandler.Raise(_ =>
        {
            Dictionary<string, string> options = new();
            options.Add("Action", "Link");
            options.Add("Intent", "Reference");
            options.Add("ForceImport", "true");
            options.Add("UseStreamlinedOptions", "true");
            options.Add("CreateLinkInstanceOnly", "true");

            using TransactionGroup transactionGroup = new(_revitService.RevitDocument, "Import IFC files");
            transactionGroup.Start();

            Dictionary<string, string> logger = new();

            foreach (var ifcFile in ifcFiles)
            {
                try
                {
                    var importer = Importer.CreateImporter(_revitService.RevitDocument, ifcFile.Value, options);
                    importer.ReferenceIFC(_revitService.RevitDocument, ifcFile.Value, options);

                    logger.Add(ifcFile.Value, "Success");
                }
                catch
                {
                    logger.Add(ifcFile.Value, "Failure");
                }
            }

            var status = transactionGroup.Assimilate();
        });
    }

RichardWhitfield avatar Dec 14 '23 22:12 RichardWhitfield