TBC Samples 2023 and the New Structural API

Migrating The Building Coder samples, I encountered the same problems as others trying to update obsolete structural API code, so let's take a look at some resources assisting us in that area:

Migrating The Building Coder Samples

I migrated The Building Coder Samples last week, creating release 2023.1.152.0.

Except for the deprecation of the derived analytical model that we discuss below, it was straightforward with hardly any changes at all except for updating the Revit API .NET assembly DLL references.

After that step, 8 errors and 5 warnings remain.

We can easily deal with the warning about the ParameterFilterRuleFactory CreateEqualsRule in the module CmdCollectorPerformance.cs:

The Boolean argument was not working as expected in previous versions and can simply be removed.

Not Migrating the Derived Analytical Model

After removing the Boolean argument to CreateEqualsRule, the remaining 8 errors and 4 warnings are all caused by the analytical model changes, above all the removal of the GetAnalyticalModel method.

The existing code cannot easily be migrated to the new structural API, as I explained in the answer to Ozan Aksu's comment:

Question: Can't find AnalyticalToPhysicalRelationManager. Where is it? I was using GetAnalyticalModel. Now I am stuck.

Answer: Yes, indeed, some hard and sudden changes have been made to the Structural API.

Please refer to the online help page on The Contextual Analytical Model in the Revit API. It includes some sample code as well.

The sample describing how to access the analytical element for a given physical element is in the section named Gets the associated analytical element for a physical one.

The bottom line is that the relation between physical element and analytical element can now be edited – in the old approach, this was not possible – and the class which handles these relations is called AnalyticalToPhysicalAssociationManager.

I asked the development team myself how to migrate a sample using the GetAnalyticalModel method. They reply:

The following code makes no sense at all any longer in Revit 2023 and needs to be completely rewritten:

  var am = wall.GetAnalyticalModel();

  foreach (var ct in CurveTypes)
  {
    var curves = am.GetCurves(ct);
    var n = curves.Count;

    Debug.Print("{0} {1} curve{2}.",
      n, ct, Util.PluralSuffix(n));

    foreach (var curve in curves)
      creator.CreateModelCurve(
        curve.CreateTransformed(T));
  }

There is no meaningful way to migrate it, since the basic concepts changed. Yes, most of the code must be rewritten.

My simplistic solution was to remove the two offending external commands CmdAnalyticalModelGeom and CmdNewLineLoad.

As always with the Revit API, it will help to learn and understand the new concepts from the end user point of view first, before trying to address the programming side of things.

Revit 2023 Structural API

So, let's dive deeper into the Revit 2023 Structural API.

The explanation of the contextual analytical model in the Revit API contains all the up-to-date information and provides a good starting point.

The Revit Developer's Guide description of the Analytical Model is still work-in-progress and will be updated soon.

Finally, the 47-minute Revit 2023 Structural Analytical Model API webinar recording created during the pre-release phase has now been published and explains all the new concepts in full depth and detail.

Revit 2023 Structural API objectives

Addendum – GetAnalyticalModel Workaround

Erik Falck Jørgensen of DTU, the Technical University of Denmark, provided a GetAnalyticalModel workaround on LinkedIn, saying:

I have attached a snippet from a cs file...

The essence is that I can GetAnalyticalModel() in slightly the same way :-)

  private static ElementId GetAnalyticalElementId(dynElement element)
  {
    _document = iDocument.Current.WrappedType;

    // Gets the AnalyticalToPhysicalAssociationManager for this document

    var analyticalToPhysicalManager 
      = AnalyticalToPhysicalAssociationManager
        .GetAnalyticalToPhysicalAssociationManager(_document);

    if (analyticalToPhysicalManager == null)
    {
      throw new System.Exception(OrchidBase.InvalidType);
    }

    ElementId _elementId = GetDynamic(element).Id;
    return analyticalToPhysicalManager.GetAssociatedElementId(_elementId);
  }

  public static dynElement GetAnalytical(dynElement element)
  {
    return GetAnalyticalElementId(element).ToDynamoType();
  }

Many thanks to Erik for this suggestion!

Removing the Dynamo wrappers, I guess the gist of it is something like this:

  /// <summary>
  /// Return the associated analytical element id 
  /// for the given element
  /// </summary>
  ElementId GetAnalyticalElementId(Element e)
  {
    Document doc = e.Document;

    AnalyticalToPhysicalAssociationManager m 
      = AnalyticalToPhysicalAssociationManager
        .GetAnalyticalToPhysicalAssociationManager(
          doc);

    if (null == m)
    {
      throw new System.ArgumentException(
        "No AnalyticalToPhysicalAssociationManager found");
    }

    return m.GetAssociatedElementId(e.Id);
  }

Erik adds:

the "translation" from dynamo implementation to pure C# is correct :-)

... by the way the GetDynamic(element) has a dynamic cast built-in from a Dynamo element to a Revit element, and a check that it can hold an analytical element :-)