Posts by retug (36)

ETABs Plugin - Part 2

Having nailed down the initial steps of creating a plugin, I set out to do some real functional work with the plugin. Being naive, I went off the deep end to begin programming the diaphragm slicer, but was quickly met by a sea of syntax and foreign looking code that was difficult to navigate. I had to go back to what all good programmers do, watching youtube videos.

The Code

The example plugin above will gather all of the load combinations in your ETABs project, populate them in a "datagrid view" and combobox and will report the joint reaction forces at a specific node under the selected load combination. I followed this youtube video, thank you Structure Detailing, the video was very informative and helpful!

The code to make this plugin has been upload on github.

New Syntax

From the video, the syntax of {get; set;} in a C# class appeared multiple times.

public class JointReaction
    {
        public string Name { get; set; }
        public string LoadCase { get; set; }
        public double F1 { get; set; }
        public double F2 { get; set; }
        public double F3 { get; set; }
        public double M1 { get; set; }
        public double M2 { get; set; }
        public double M3 { get; set; }
    }

This notation appears related to "encapsulation" in C# and allows this class to be both private (hidden from other classes and other parts of the program) and public. The JointReaction class is then instantiated in the main Form1.cs code.

           React.Name = Name;
           JReact.LoadCase = LoadCase[0];
           JReact.F1 = F1[0];
           JReact.F2 = F2[0];
           JReact.F3 = F3[0];
           JReact.M1 = M1[0];
           JReact.M2 = M2[0];
           JReact.M3 = M3[0];

Calling the .F1 attribute on the class JReact somehow triggers the {get; set;} notation. I have to dig into more C# to fully understand this {get; set;} notation.

ETABs API - Unclear Returns

API calls to ETABs are foreign to me, how can there be no explicit return?

Take for example the following code:

string Name = "";
            eItemTypeElm ItemTypeElm;
            int NumberResults = 1;
            string[] Obj = null;
            string[] Elm = null;
            string[] LoadCase = null;
            string[] StepType = null;
            double[] StepNum = null;
            double[] F1 = null;
            double[] F2 = null;
            double[] F3 = null;
            double[] M1 = null;
            double[] M2 = null;
            double[] M3 = null;
            int x = -1;
            _SapModel.Results.Setup.DeselectAllCasesAndCombosForOutput();
            //Method below acts on a string
            _SapModel.Results.Setup.SetComboSelectedForOutput(LoadCombinationComBox.SelectedItem.ToString());

            //unsure why eItemTypeElm.Element does not need to be initiated
            //anything that is ref is returned, not input
            x = _SapModel.Results.JointReact("4", eItemTypeElm.Element, ref NumberResults, ref Obj, ref Elm, ref LoadCase, ref StepType, ref StepNum, ref F1, ref F2, ref F3, ref M1, ref M2, ref M3);

The confusing part of this code is that the output of _SapModel.Results.Joint(...), x, is merely an integer, 1. I have no idea how this method writes/returns the values of the results to F1, F2, F3... Anytime I have written methods, I have direct return statement and it would typically be written directly to the output variable, x. If you know what the syntax might look like in C# to make this complicated output, please let me know, I would be interested in learning how to return multiple data types, lists, and values.

FYI, the code above will output the joint reaction forces for the unique node "4" in the ETABs model. These are the values that are populated in the initial video.

DataGridViews - A Ray of Hope for Programming

Data grid views have to be the best thing I have discovered in C#. Coming from python and the miserable experience that was PyQt5 to make a GUI, this is a miracle. The code to make tables is very clean and concise. If I never have to write PyQt5 code again, I will be happy. Side note, I have been meaning to make a post on PyQt5 GUIs with ETABs, another post for a later time. A sample of the mess that is PyQt5:

class TableModel(QtCore.QAbstractTableModel):
    def __init__(self, data):
        super(TableModel, self).__init__()
        self._data = data
        self._headers = ['Frame Size', 'Cnx Type', 'Demand (kip)', 'Capacity (kip)', 'ETABs Frame #']

   def data(self, index, role):
        if role == QtCore.Qt.DisplayRole:
            # See below for the nested-list data structure.
            # .row() indexes into the outer list,
            # .column() indexes into the sub-list
            return self._data[index.row()][index.column()]
        if self._data[index.row()][index.column()] == 'SS/DS cnx NG' and role == QtCore.Qt.BackgroundRole :
            return QBrush(Qt.red)
        if self._data[index.row()][index.column()] == 'No Values' and role == QtCore.Qt.BackgroundRole :
            return QtGui.QColor('#FFA500')
        if self._data[index.row()][index.column()] == 'DS cnx Ok' and role == QtCore.Qt.BackgroundRole :
            return QBrush(Qt.yellow)
        
    def rowCount(self, index):
        # The length of the outer list.
        return len(self._data)

Now that I have learned the basics of the plugin and the ETABs API, I think I should be able to struggle through my first bit of custom coding.

The Beginnings of an ETABs Plugin

My first "Hello World" addin for ETABs is shown above. No matter how many times you see it, it is always satisfying to get Hello World to print out.

To make the addin, I followed Jeremy's examples on his website. Big thanks to him and his github for walking me through the initial setup process for the ETABs API with C#.

The examples that he presented were from the 2017 community edition of visual studio, so there were a few buttons that had been moved around in the 2019 version that I was using.

First, the events tab that is shown in his examples has moved into its drop down menu, indicated by a lightning bolt. This threw me off for a bit until I found the events tab in its new home.

Also, upon re-compling the application, while having ETABs open, I would get this error:

I was able to solve this by navigating into the big/debug directory and removing the ETABS_Plugin.pdb and ctrl+alt+deleting to kill ETABs. Some weird bug that I will need to dig into more, just be sure to close ETABs before recompiling the code.

The Future

I am hoping to make the diaphragm slicer into a full fledged application. This will make the application much more approachable for others in the office and out in the interwebs.

Things I will need to research in C#, let me know if you have suggestions on these topics:

  • A matrix math package for converting between local and global coordinates
  • A graphing package in C#
  • Learning windows forms more in depth

I uploaded the code on github (basically a copy of Jeremy's code) if you want to take a look.

RAM Structural API - A Practical Example

After unlocking the first part of the RAM API, I wanted to create a practical example of how this could be used to automate a few tedious tasks that we perform in the office.

Chord and Collector Design, A Tedious Task

I chose chord and collector design as this is a very repetitive task in our office. The task typically involves pulling the following information for a chord of collector element from RAM Structural System Model:

  • Member size, e.g. W16x31
  • Member length to determine unbraced lengths
  • Member demands (mainly flexural demands)
  • Member capacity (again mainly flexural)

We then take this information and plug it directly in a nice spreadsheet that will perform a combined axial and flexural demand calculation on our steel chord and collector members. 

Looking at the API functions in the RAM documentation it looked like I could pull most of these values pretty easily.

User Input

The user input for this dynamo program involves the following items

  • RAM Structural System file path
  • Story of Interest
    • This is an integer at the moment, with 0 being the first level.
  • RAM Beam #
    • Each beam on each story in a RAM model has a unquie Beam ID, input this # to pull information
  • Sds for your project
  • Make sure you have a green light (RAM structural home page) on gravity beam design model before running this script!

With this information, the program opens the RAM model and pulls out the pertinent information and writes it to excel, let's dig into some of the fun items for making this happen.

Member Size

To get the member size, I was able to copy an example from Marcello and pull all beam sizes on the user input level. I would like to modify this in the future to only pull the member sizes based on the user input beam number, but reviewing the API documentation, I did not see a function immediately retrieved the beam ID, given a story and beam # that corresponds to the numbers on the screen in RAM structural. The code to pull this is shown below:

public static List<string> GET_RAM_BM_SIZE(string FileName, int In_Story_Count)
        {
            RamDataAccess1 RAMDataAccess = new RAMDATAACCESSLib.RamDataAccess1();
            RAMDATAACCESSLib.IDBIO1 IDBI = (RAMDATAACCESSLib.IDBIO1)
                RAMDataAccess.GetInterfacePointerByEnum(EINTERFACES.IDBIO1_INT);
            RAMDATAACCESSLib.IModel IModel = (RAMDATAACCESSLib.IModel)
                RAMDataAccess.GetInterfacePointerByEnum(EINTERFACES.IModel_INT);
            //OPEN
            IDBI.LoadDataBase2(FileName, "1");
            IStories My_stories = IModel.GetStories();
            int My_story_count = My_stories.GetCount();
            IStory My_Story = My_stories.GetAt(In_Story_Count);
            IBeams My_Beams = My_Story.GetBeams();
            int Beam_Count = My_Beams.GetCount();
            List<string> ListLine = new List<string>();
            //create loop herenthru all count
            //start..end..step
            for (int i = 0; i < Beam_Count; i = i + 1)
            {
                string My_Beam_Size = My_Story.GetBeams().GetAt(i).strSectionLabel;
                ListLine.Add(My_Beam_Size);
            }
            //CLOSE           
            IDBI.CloseDatabase();
            return ListLine;
        }

 

Giving A Description

Previously, we would describe our chord/collector locations based on maybe grid lines and stories. Given that we typically provide a beam map to our plan reviewers with all beam #s labelled, I thought it might be easier to just provide a description based on beam # and story number. This makes it easy for internal review and plan review. 

Making text for this with a little python was pretty easy and actually the first time I used the new fStrings in Python. fStrings were added in python 3.6 and I think they infinetly times easier to read and write, a great change.

Determining the moment

One of the pains of RAM structural system beam designer is that it will spit out diagrams for 1.4 Dead Load (DL) and 1.2DL + 1.6 Live Load (LL).

Sometimes you just want to know what the DL moment and the LL moment so you can modify these for differing load combinations like that in a seismic load combination, 1.2DL + 0.2Sds + 0.5LL typically.

I was able to use Marcello's examples from his packets and tweak some C# code to retreive the unfactored DL and LL moments from a RAM structural API method. GetGravBeamForcesLeftAt(...) did the trick.

[MultiReturn(new[] { "pdDeadMoment", "pdDeadShear", "pdCDMoment", "pdCDShear", "pdCLMoment", "pdCLShear",
                "pdPosLiveMoment","pdPosLiveShear","pdNegLiveMoment", "pdNegLiveShear"})]
        public static Dictionary<string, object> GET_GRV_BEAM_FORCES(string FileName, int BeamID, double BeamLocation)
        {
            RamDataAccess1 RAMDataAccess = new RAMDATAACCESSLib.RamDataAccess1();
            RAMDATAACCESSLib.IDBIO1 IDBI = (RAMDATAACCESSLib.IDBIO1)RAMDataAccess.GetInterfacePointerByEnum(EINTERFACES.IDBIO1_INT); //casting this to an object
            RAMDATAACCESSLib.IModel IModel = (RAMDATAACCESSLib.IModel)
                RAMDataAccess.GetInterfacePointerByEnum(EINTERFACES.IModel_INT);
            RAMDATAACCESSLib.IForces1 IForces1 = (RAMDATAACCESSLib.IForces1)
                RAMDataAccess.GetInterfacePointerByEnum(EINTERFACES.IForces_INT);
            Dictionary<string, object> OutPutPorts = new Dictionary<string, object>();
            //OPEN
            IDBI.LoadDataBase2(FileName, "1");
            double pdDeadMoment = 0;
            double pdDeadShear= 0;
            double pdCDMoment = 0;
            double pdCDShear = 0;
            double pdCLMoment = 0;
            double pdCLShear = 0;
            double pdPosLiveMoment = 0;
            double pdPosLiveShear = 0;
            double pdNegLiveMoment = 0;
            double pdNegLiveShear = 0;
            IForces1.GetGravBeamForcesLeftAt(BeamID, BeamLocation, ref pdDeadMoment, ref pdDeadShear,
                ref pdCDMoment, ref pdCDShear, ref pdCLMoment, ref pdCLShear,
                ref pdPosLiveMoment, ref pdPosLiveShear, ref pdNegLiveMoment, ref pdNegLiveShear);
            //CLOSE           
            IDBI.CloseDatabase();
            OutPutPorts.Add("pdDeadMoment", pdDeadMoment); OutPutPorts.Add("pdDeadShear", pdDeadShear);
            OutPutPorts.Add("pdCDMoment", pdCDMoment); OutPutPorts.Add("pdCDShear", pdCDShear);
            OutPutPorts.Add("pdCLMoment", pdCLMoment); OutPutPorts.Add("pdCLShear", pdCLShear);
            OutPutPorts.Add("pdPosLiveMoment", pdPosLiveMoment); OutPutPorts.Add("pdPosLiveShear", pdPosLiveShear);
            OutPutPorts.Add("pdNegLiveMoment", pdNegLiveMoment); OutPutPorts.Add("pdNegLiveShear", pdNegLiveShear);
            return OutPutPorts;
        }

 

A WORD OF CAUTION

The way I have this setup, this function only pulls the beam results at mid span, should you have an unsymmetrical loaded beam, you MAY BE MISSING THE MAX MOMENTS. I have not dug deep enough into the API to know if there is a way to pull out the max moments along the beam.

Determining Phi*Mn

From my review of the documentation, there does not seem to be an explicit function that returns Phi*Mn for a steel beam. Marcello has an example the returns design results, with one of the results being a strength DCR. I wrote some code that determines Mu and backs into Phi*Mn by taking Mu and dividing by the DCR. I thought this was pretty slick until I realized that sometimes the pre-composite strength of a beam might control the design.

Take the following example below.

The way I currently have the code written, my program will correctly determine that Mu = 120.1 kip*ft as noted on the RAM output. But, you can see that the strength DCR is 0.88. The program will determine that phi*Mn = 120.1/0.88 = 136.36 kip*ft, but this is incorrect. As you can see, the pre-composite strength is actually governing this beam with a DCR of 0.88 coming from the prec-omposite check, 43kip*ft/48kip*ft.

The true moment capacity of the composite beam is 341 kip*ft, another item to be aware when using the program.

I will not touch on length, this one is pretty easy to retrieve from the RAM API and is written in the excel file at the end of the program.

Writing To Excel

At the end, we write all the info we just pulled out of our RAM model and write into excel.

Review

Hopefully this will speed up steel chord/collector design in our office. I am worried about some of the pitfalls noted above, but as long as I stay alert and pay attention non-symmetrical loadings and reviewing phi*Mn calculations, I think the benefits outweigh the cons.

Testing on Large Models

I wanted to run a speed test on a larger model to see how long this would take to run. I picked a large (6) story building and tried to retrieve information about 20 beams.

There were a total of 352 beams on the level I selected and the excel file was populated in 33 seconds for 20 beams. Not bad, way faster than having to do that by hand. The populated excel file:

If you have any requests on what I should explore next in RAM Structural API, let me know. I think we may be just scratching the surface.

RAM API - Cracked

I finally cracked into the RAM Structural System API after maybe 3 years of wanting to get into it.

The key to getting in? More powerful googling. 

Googling "RAM Structural API" yields some very uninspiring result. Usually the results pointed to the RAM Structural API documentation that looks very cryptic and appears to require C++ as coding language. A few snips from the API documentation. 

All of this looked pretty intimidating and I had about 0 desire to learn a new programming language. I put learning the RAM API on the backburner even though this is our offices daily driver for most buildings.

Marcello Sgambelluri to the Rescue

One day I happened to stumble on a post on Autodesk's forum regarding RAM API access by Marcello. I knew Macello's name and I knew this would be the ticket to getting into the API. He always does a great job of documenting his madness.

A link to the material that cracked the API.

Following this link is kinda crazy. Never in my wildest dreams would I imagine that cracking the RAM API would involve some tech with Revit, Dynamo and C#... the whole RAM API documentation is in C++!! How, why? Seriously if you have an answer to how Marcello was able to access the API with C# and not C++, let me know, there is not a single mention of C# in the documentation. Maybe all .net languages can be intertwined??

Anyways, Marcello's examples utilize C# and something called zero touch nodes in dyanmo to unlock the RAM API. The examples are quite helpful and I can't wait to dig in deeper to see what the API might unlock.

I have posted the code on github. The hardest part was not the code, but setting up all the references in the visual studio IDE and getting the code to successfully compile. After some tweaking on the settings, I was able to get a successful build. 

Upon loading the .dll (a .dll file is the compiled code as I understand it) into dynamo, you feed the file name into the "node" and the code returns the total number of stories in your RAM structural model. This is a simple example, but opens the doors to so much more.

The picture above correctly returns the number of stories in the sample RAM structural model I fed to it. HOW COOL IS THAT!

 

Moving forward, I hope to keep digging into the RAM API, exploring more of Marcello's examples and building some code of my own to automate tasks in RAM Structural. 

ETABs API - More Examples (Database Tables)

Sparked by a reddit inquiry, I have added an example on my github to access the database table results using the ETABs API. The python code can be found on github.

The Code

The example code works to access the joint reaction database table results from ETABs v20.

 

In the image above, you can see that the python code pulls the correct vertical reaction when looking at the dead load reaction (3.177 kips). The python code returns results in pounds, but you can easily format your result units with SapModel.SetPresentUnits(x). The ETABs API document gives a good run down on all the units available.

First Previous 2 3 4 5 6 Next Last

Sidebar

Site Info

  • Latest Comments