Posts by retug (28)

SAP2000 API Example

This blog is finally starting to pay off, people are emailing with programming questions and problems. Making programming a little more accessible has always been the goal with the site. Programming can be intimidating to get started!

Foad Kondori emailed me awhile back trying to make a SAP2000 plugin to access frame reactions in SAP2000. I had never written a plugin for SAP2000, but I had written some python code awhile back to access ETABs frame force reactions in Python. I was able to send him my code and he was able to do the rest of the magic to translate the python code to C#. 

The code he wrote is really cool and I appreciate that he allowed me to share this code on github and the blog. Sample shown below retrieving selected frame object type and name

There was one small bug he had in his program and I was able to trouble shoot the culprit, moving the data source out of the for loop did the trick.

His example also provided me some insight on better ways to manage if the SAP/ETABs model has run. 

 bool isModelRunning = _SapModel.GetModelIsLocked();
            if (isModelRunning)

                //get Frame Force

                DialogResult result_2 = MessageBox.Show("the model is not run, Do you want Run the model ?"
, "Run Analysis", MessageBoxButtons.YesNo, MessageBoxIcon.Information);

                if (result_2 == DialogResult.Yes)

These are things I need to implement in the section cutter tool to prevent time consuming re-runs.

Also, I need to dig in and understand why the section cutter ETABs plugin does not work in SAP2000. The APIs between the two program look very similar, but the section cutter plugin does not work in SAP 2000. Foad's example will be a good place to start when trying to troubleshoot.

To CSI, can we improve the SAP OAPi documentation? It only appears to be written for VBA, maybe it could be updated to match that of ETABs, with samples provided for multiple languages?

Feel free to reach out anytime if you are running into programming trouble, I'll try to help if I have the time.

RAM to Revit (R2R) Part 1

The Tool

For a long time I’ve wanted a quick way to spot check if structural beams in our analysis models like RAM or ETABs matched our Revit model. The gif above shows the beginning of bringing this idea to life. The tool will be a plugin for Revit that will allow users to import their RAM structural model into an in-between file that will interact with RAM and Revit.

I'm a little sad that I am not developing this tool for ETABs, as this is my daily structural analysis driver, but our office is much more of RAM office than ETABs office. Gotta do what the people want and will (hopefully) use.

How It Works

The initial screen is split between a RAM Structural side (left) and a Revit side (right). After pointing the program to the location of the RAM SS model, the user will collect the levels and select the level of interest in the combo box. From here, the program will gather the structural grids in the RAM SS model and plot them onto the RAM canvas. A similar process occurs on the Revit side to plot all of the grids in the Revit model.

From here, the user will be promoted to select two interesting gridlines that will share the same coordinates in both the RAM model and Revit model. This intersection will be use in future to generate the global coordinate system for the project as a whole, allowing for the RAM and Revit model to have differing coordinate systems and still map into each other.

Next Steps

The mapping algorithm. The way the project is currently constructed, the program will determine the x and y offset by calculating the intersection point of the selected RAM and Revit grids.

From here, I will work on a function to determine the rotation parameter. This might be a bit tricky to determine, given that a rotation parameter could either be positive or negative, I might prompt the user to select the rotation parameter.

With the x, y and rotation parameters set, I can set up the local coordinate system for the RAM beams and map them in the global coordinate system of the revit project.

Fun Parts of the Coding Experience

It’s funny how many pieces of my previous coding experience came into play making this.

  • Canvas and graphics
    • A lot of the lessons I learned in javascript and threejs from the concrete design tool were able to be applied to the plotting performed in this script.
    • Do not forget that the default canvas is coordinates have the y coordinate being negative. This threw me for a loop for a bit trying to trouble shoot why the grid plots were always upside down
  • Intricate point class
    • The r2rPoint class is on the upper end of my ability to understand classes in C#. There are some very complicated properties and methods used in the class and it is a big improvement from the point class utilized in the section cutter tool. It allows the point to be defined in either the global (GCS) or local coordinate system (GCS)
    • This came in handy when pulling grid data out RAM structural due to the grid system definition. Essentially each grid system in RAM structural has its own local coordinate system that the r2rPoint class can handle quite elegantly. The way a point is defined as global or local is through the isGlobal Boolean.
    //Initialize the point class in either the global or local coordinate system thru isGlobal Flag

        public r2rPoint(double X, double Y, double Z)
            this.X = X;
            this.Y = Y;
            this.Z = Z;
            isGlobal = true;

        public r2rPoint(double U, double V, double W, bool isGlobal = false)
            this.U = U;
            this.V = V;
            this.W = W;
            this.isGlobal = isGlobal;

        public void Convert_To_Local(GlobalCoordinateSystem gcs)
            double[] part1 = new double[] { X - gcs.RefPnt[0], Y - gcs.RefPnt[1], Z - gcs.RefPnt[2] };

            //the class will now have new attribute of local coordinates point.LocalCoords[0] = the X local coordinate system
            LocalCoords = new List<double>() { gcs.R_Inv[0, 0] * part1[0] + gcs.R_Inv[0, 1] * part1[1] + gcs.R_Inv[0, 2] * part1[2] ,
            gcs.R_Inv[1, 0] * part1[0] + gcs.R_Inv[1, 1] * part1[1] + gcs.R_Inv[1, 2] * part1[2],
            gcs.R_Inv[2, 0] * part1[0] + gcs.R_Inv[2, 1] * part1[1] + gcs.R_Inv[2, 2] * part1[2]};

            this.U = LocalCoords[0];
            this.V = LocalCoords[1];
            this.W = LocalCoords[2];

        public void Convert_To_Global(GlobalCoordinateSystem gcs)
            //this is the ref point
            double[] part1 = new double[] { U, V, W };

            //the class will now have new attribute of local coordinates point.LocalCoords[0] = the X local coordinate system
            GlobalCoords = new List<double>() { (gcs.R[0, 0] * U + gcs.R[0, 1] * V + gcs.R[0, 2] * W) + gcs.RefPnt[0],
            (gcs.R[1, 0] * U + gcs.R[1, 1] * V + gcs.R[1, 2] * W) + gcs.RefPnt[1],
            (gcs.R[2, 0] * U + gcs.R[2, 1] * V + gcs.R[2, 2] * W) + 0}; //unsure why this the way to do this. review in the future. should be the line below, without the 0
            //(globalCoords.R[2, 0] * X + globalCoords.R[2, 1] * Y + globalCoords.R[2, 2] * Z) + globalCoords.RefPnt[2]};

            this.X = GlobalCoords[0];
            this.Y = GlobalCoords[1];
            this.Z = GlobalCoords[2];

        public event PropertyChangedEventHandler PropertyChanged;

        private void NotifyPropertyChanged(string propertyName)
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));

Each grid system in RAM structural has it's own local corodinate system:

  • Xaml and Html Similarities
    • This is the first time I have used XAML and WPF (Note, someday I dream of experiencing less "first"s in programming. The programming world seems so large and ever expanding compared to that of structural engineering where it is the same old stuff day in and day). Xaml looks very similar to Html, Maybe Xaml stole some ideas from Html? </> formats are familiar from brief experience in web development and getting this website up and running. It did not take too long to make the Xaml work, which I attribute to having some background in Html. 
      • I learned from some previous coworkers that Xaml and WPF is a more "modern" approach to UI development compared to winforms which was used to make the sectioncutter ui. Personally, I find winforms super easy to get up and running, but for more complicated projects like this one, maybe xalm and WPF will pay off.
  • ChatGPT Skills
    • I am continually amazed by ChatGPT and it’s ability to get thing up and running quickly
    • 90% of my canvas events were written by ChatGPT and it’s really impressive how quickly ChatGPT was able to take me from an idea to a functional example, allowing me to fill in the blanks
    • I originally went down the route of using LiveCharts2 to do the interactive plotting, which I wasted a lot of time exploring. ChatGPT could not help me due to the fact that LiveCharts2 was not released until 2022. ChatGPT was really good at making up stuff that used to work in the original LiveCharts and that would not work in LiveCharts2. I eventually gave up and went to the old fanshioned canvas to handle events and plotting needs.
    • I wrote the concrete design tool before ChatGPT and I would estimate that it took me about 3 solid weeks of coding and internet forum conversations to get a workable zoom, pan and selection function to work with the canvas. ChatGPT was able to write these functions in the better part of a 20 min interaction with it. Super impressive.
    • I find myself having to thank the robot overlord all the time for the work that it does for me : ), I am not sure if others do this as well.
Me: I get an error that Ellipse does not contain a definition for child

ChatGPT: I apologize for the confusion. My previous response contained incorrect information. Ellipse does not have a direct Child property to add text inside it. Instead, you can achieve this by placing both the Ellipse and TextBlock inside a container like a Grid. Here's how you can do it:

Copy code
private void AddCircleWithText(double x, double y, string text)
    Grid container = new Grid();

    Ellipse circle = new Ellipse
        Width = 10, // Diameter = 2 * Radius
        Height = 10, // Diameter = 2 * Radius
        Fill = Brushes.Red,
        Stroke = Brushes.Black,
        StrokeThickness = 1,

    TextBlock textBlock = new TextBlock
        Text = text,
        Foreground = Brushes.White,
        HorizontalAlignment = HorizontalAlignment.Center,
        VerticalAlignment = VerticalAlignment.Center


    // Position the container at (x, y)
    Canvas.SetLeft(container, x - 5); // Adjust for the radius
    Canvas.SetTop(container, y - 5); // Adjust for the radius


Now, each circle and text combination is enclosed in a Grid, which allows you to display text inside the circle on the canvas. You can use the AddCircleWithText function with the (x, y) coordinates for the center of the circle and the desired text to place inside it.

Me: This looks great thank you! How can I make it so that the elements in the canvas are larger on my screen via scrolling in and out?

Another oddity about the code, when accessing the RAM structural API, I needed to add a reference to the Microfsoft.CSharp .dll. I had never had to do this before in my previous experience following Marcello’s examples, but the binding functions and casting functions would not work without import the CSharp reference. After some confusion and googling, I was able to resolve this error.

Please note that this code is not DRY (do not repeat yourself), I would like to come back in the future and make it more DRY, many of the canvas functions are duplicated, one with ramZoom or revitZoom. I bet there is a way to make this work without repetitive canvas events.

Future Work

  • Keep going on point mapping algorithm
  • Add a save and read functionality to store the mapping parameters
    • I have not done save and read before in my programming experience so this should be a good learning experience.
  • Address radial grids
    • Both RAM and Revit
  • Address that RAM grids only work with a max and min parameter set.
  • Add zoom to fit function
    • Right now the grids are not centered in the canvas. Maybe add a double middle mouse button click to zoom extents.

Section Cutter - ETABs Plugin

A major upgrade to the section cutter tool, section cuts are now centered on the first and last location where they pass through constituent area geometry. An astute engineer in our office noticed that if the section cuts were not centered on the constituent area geometry, moment results could be quite skewed as ETABs reports moments about the center of the section cut, not the center of the cut geometry. Let's dig into the algorithm and code.

A new github repo has been created for the section cutter tool. This project has become big enough that it deserves it's own repo. The readme page also has install instructions.

The "Smart Sections Algorithm"

The algorithm to make "smart" sections was easy to do on paper, a bit harder to do by painstakingly describing every step to a computer. Let's get some visual aids to assist in outlining the algorithm.

 Step 1 - Gather User Areas and Input Node. Example will be a simple square.

Note, I think the input node could be omitted, but it has remained in the section cutter tool for now.

Step 2 - Setup Local Coordinate System (LCS) based on input vector and starting point.

Here the LCS is rotated 45 degrees as shown by the blue rotated grid lines. Convert all constituent area geometry to the LCS. LCS coordinates in Red.


Step 3 - Generate an array lines in the LCS.

The previous section cutter tool would stop at this point. These results would be incorrect for buildings with steps, like an L shaped diaphragm.

Step 4 - Determine where lines generated in step 3 cross constituent geometry.

Use these points to create the section cuts in ETABs.

The Code

The main struggle on the coding side was controlling all the lists of data and maintaining their structure. The algorithm works for many areas and openings, so the amount of data that gets generated to perform these checks inflates quite quickly. To my surprise, the tool does not seem to struggle with generating all of the section cut data, the slowest part is still the analysis of the semi-rigid diaphragm.

Once again, chatGPT really helped out with the coding. The intersection function was exactly copied and pasted and it worked, how surprising!

Here is my chat with the AI overlord bot, THEY TOOK OUR JOBS! :

Me: Given that two line segments, each with an x and y value, that are known to intersect, write c# code that determines the intersection point, x and y, of the two line segments


To find the intersection point of two line segments, you can use the concept of parametric equations. Given two line segments defined by their endpoints (x1, y1) to (x2, y2) and (x3, y3) to (x4, y4), you can calculate the intersection point (xi, yi) using C# code as follows:

using System;

class Program
    static void Main()
        // Example endpoints of two line segments
        int x1 = 1, y1 = 2;
        int x2 = 4, y2 = 6;
        int x3 = 3, y3 = 1;
        int x4 = 5, y4 = 5;

        // Calculate the intersection point
        if (Intersect(x1, y1, x2, y2, x3, y3, x4, y4, out int xi, out int yi))
            Console.WriteLine($"Intersection Point: ({xi}, {yi})");
            Console.WriteLine("The line segments do not intersect.");

    // Function to check if two line segments intersect and calculate the intersection point
    static bool Intersect(int x1, int y1, int x2, int y2, int x3, int y3, int x4, int y4, out int xi, out int yi)
        // Calculate the denominator for parametric equations
        int denominator = (y4 - y3) * (x2 - x1) - (x4 - x3) * (y2 - y1);

        // Check if the line segments are parallel or coincident
        if (denominator == 0)
            xi = 0;
            yi = 0;
            return false;

        // Calculate the parameters for each line segment
        int ua = ((x4 - x3) * (y1 - y3) - (y4 - y3) * (x1 - x3)) / denominator;
        int ub = ((x2 - x1) * (y1 - y3) - (y2 - y1) * (x1 - x3)) / denominator;

        // Check if the intersection point lies within both line segments
        if (ua >= 0 && ua <= 1 && ub >= 0 && ub <= 1)
            // Calculate the intersection point
            xi = x1 + ua * (x2 - x1);
            yi = y1 + ua * (y2 - y1);
            return true;

        xi = 0;
        yi = 0;
        return false;

Replace the example endpoints (x1, y1, x2, y2, x3, y3, x4, y4) with the actual endpoints of your line segments. The code will then calculate the intersection point (xi, yi) if the two line segments intersect, or indicate that they do not intersect.

ChatGPT used an expression "out" that I had never seen before and it was super helpful. I still need to read up on "out" and "ref" in C#, but these seem to be ways to add additional function returns in C#.

Throwing this function into the main portion of the code, I was able to perform step 4 of the algorithm outlined above. Working in the LCS system was nice, allowing for easy manipulations of items that were perpendicular/parallel to the LCS. With the LCS point geometry known, it was easy to convert back into the GCS.

In addition to the main "smart sections" addition, a few features were added

  • For Trenno, Imperial (kip, ft), freedom units, and Metric (kN, m), superior units, results.
  • Section Cut plot at the bottom of the graphical user interface has been updated to look better.
  • A few more data validations items on user input have been added

I have more updates I want to get around to listed on the github repo readme page. If you have suggestions/bugs, please let me know. Looking to make this better.

Project Management with Deltek

Deltek is a powerful tool to track all things AEC related. At my level, I use it mostly for project management and tracking project budgets. 

You can get a lot of powerful reports out of it quickly, but at face value, it's just a sea of data. We need to visualize this data to get a better feel for what's happening. The plot above shows hours on a fictitious project over time for differing employees.

Fun notes from fake project, brown engineer leaves company, olive main engineer goes back to school mid project, blue project manager scrambles to pick up the project mid way through.

If you use deltek, you can quickly get this data in an excel format from the project review tab and clicking "export to excel". 

The sample excel file will have the following format:

Person Date Hrs Checked
Help Proj Eng 1 10/14/2021 4 Checked
QAQC Eng   15  
QAQC Eng 9/14/2022 4 Checked
QAQC Eng 12/6/2022 2 Checked
QAQC Eng 5/8/2023 1 Unchecked
QA/QC Eng 5/9/2023 6 Unchecked
QA/QC Eng 5/10/2023 2 Unchecked
Help Eng 2   10  
Help Eng 2 11/21/2022 0.5 Checked
Help Eng 2 11/22/2022 5.5 Checked
Help Eng 2 11/23/2022 4 Checked
Drafter QAQC   4.5  
Drafter QAQC 4/27/2023 4.5 Checked
Drafter QAQC 5/15/2023 0 Unchecked
Proj Eng 3   130  
Proj Eng 3 9/15/2022 0.5 Checked

With this excel file, we can do a little python on the file, using pandas and matplotlib and create fun plots.

I am still quite a beginner with pandas and I am sure there is a much more elegant way to do the cumulative time vs date plot. I struggled through this, but the code works:


### Multiple Plots

df_all = pd.read_excel('H:\\MyGenericProject.xlsx', sheet_name = 'HoursForProject')
names = df_all.iloc[:,0]

my_column_changes = names.shift() != names        
index_change = [i for i, x in enumerate(my_column_changes) if x]

PersonHrs = {}
PersonDate = {}

for i in range(len(index_change)):
    if i == len(index_change)-1:
        PersonHrs[names[index_change[i]+1]] = df_all.iloc[index_change[i]+1:,2]
        PersonDate[names[index_change[i]+1]] = df_all.iloc[index_change[i]+1:,1]
    else: ###grabs the last person in the list
        PersonHrs[names[index_change[i]+1]] = df_all.iloc[index_change[i]+1:index_change[i+1],2]
        PersonDate[names[index_change[i]+1]] = df_all.iloc[index_change[i]+1:index_change[i+1],1]
sum_time = []
for i in PersonHrs.values():
    indiv_sum = []
    indiv_time = 0
    for time in i:
        indiv_time += time
for indiv_person_hrs, indiv_person_date in zip(sum_time, PersonDate.values()):
    DF = pd.DataFrame()
    DF['value'] = indiv_person_hrs
    person_date = pd.to_datetime(indiv_person_date) 
    DF = DF.set_index(person_date)


The code to do this and sample excel file is found on github.

In addition, I made a cool stacked bar plot that tracks hours on Engineering, Drafting and QA/QC type work.

This fake project worked out to be about 70% engineering, 23% drafting and 7% QA/QC.

Moving forward, I would like to access Deltek's API directly. It would be awesome to make these plots directly without having to export excel files and pull other data manually. If you have accessed Deltek's API before, let me know, it would be fun to explore how to do this.


Transfer Diaphragm Design

I have been working through some complicated diaphragm designs with a newer engineer recently and have been learning a lot about transfer diaphragms. I realized I was falling into some pitfalls and it was good working through this with the newer engineer. One of the great things about working with newer engineers is the fresh perspective and inquisitive nature.

The Sample Building

The building shown at the start of this blog post is a 20 story tall building, with the lower (2) floors being the typical transfer diaphragm type building, where the building core shifts a lot of its lateral load out to the perimeter concrete walls. See image below for large jump in shear at the transfer diaphragm in the building we inspect in this post.

I want to note that this building is completely made up, please take all numbers with a grain of salt and let’s focus on the relative values, not the values themselves. Please also let me know if you do diaphragm design differently, this is one person's perspective on diaphragm design.

A few notes on the building.

  • Total height 248ft
  • Typical story height is 12ft
  • 24” thick concrete core with moment frame outrigger system
  • 12” thick concrete slab for typical floor
  • 24” transfer diaphragms at levels 2 and 1
    • We will study a few differing transfer diaphragm setups
    • Level 2 Shown below

  • Building is subjected to an ELF type load
    • Base ETABs seismic seismic parameters
      • R = 8
      • Sds = 1.374
    • Total base shear is 1600 kip under ELF loading

My Previous Design process for Diaphragm Design

Let me outline the way I used to design diaphragms

  • Calculate the ELF applied story forces that a program like ETABs/RAM elements produces
    • For our sample building, which I have uploaded to github, we have the following applied diaphragm forces for an ELF type load case
    • Note at story 2, we have an applied force of 34.2kip








































































  • From here, I would then calculate the diaphragm design force according to ASCE 7.
    • For fun, let’s do a quick calc based on the seismic parameters input into the ETABs model. Controlled by Fpx_min and we are checking level (2)
    • Fpx = 0.2*Sds *Wp = 0.2*1.374*(5064kip) = 1392 kip
    • This works out to about 96.67psf over the 120’x120’ level 2 transfer diaphragm
  • Next, I would take the ratio of applied diaphragm force in the ELF case and compare that to the Fpx value, this would be the ratio I would use to amplify my section cut results by to do the design of my chords and collectors, you can maybe start to see the problem.
    • Amplification factor Fpx/Fx = (1392kip)/34.249kip =~ 40.5
  • A small aside, I built this tall 20 story building to demonstrate my point, but when working with smaller buildings that often do not have as explicit of transfer diaphragms, 6-8 stories, built into a hillside for example, the addition of lateral force resisting elements, these ratios are much smaller and seem like they might be reasonable.
  • With the amplification factor, in attempt to capture the minimum diaphragm design force, I amplify my shear force and moment diagram and design my design to resist these amplified forces. You can see that a factor of 40.5 is unreasonable and does not capture what the code is truly trying to capture with minimum diaphragm design forces and higher level mode effects. More on this later.

Sample buildings and iterations with level 2 Transfer Diaphragm

I have uploaded (3) differing ETABs models to github.

  1. The base model with a 24” thick transfer diaphragm

  1. The model with a 12” thick diaphragm and stiffness modifiers to account for cracking
  2. Same model as #1, but with a large hole in the transfer diaphragm



The new way of designing diaphragms:

Moving forward with diaphragm designs, I will not use this amplification method I outline above anymore. I think this method works for regular buildings, where there is little transfer of forces through diaphragms and for buildings where the lateral force resisting system is “regular”. My definition of regular is that shear force in the lateral force resisting system slowly accumulates on a story, by story basis. Backstay effects and transfer diaphragms are not "regular".

I will note that the sample building I have built, I would define as regular above level 2 and above, the shear force accumulates in the shaft as you move down the building until you hit level 2 and experience the large backstay effect.

The new way of designing diaphragms is running two overall design checks

  1. Running the overall ELF model, reviewing results in the diaphragm with the slicer
  2. Appling a diaphragm design force one level at a time and reviewing the results of the diaphragm with the slice

Please note that these forces need to be amplified for element being design, for example, a lot of collector forces need to be amplified by omega.

My understanding of diaphragm design forces is that they are to capture higher level mode effects where the max diaphragm design force at a specific level would not typically occur in the same assumed manner as the first mode. The first mode and corresponding ELF design forces is the correct way to design the lateral force resisting system and the design of transfer diaphragm forces. In a lot of cases for transfer diaphragms, the typical ELF load distribution will be the worst case diaphragm force.

Fun Sketches

You can see in the 1st mode assumed shape, the lateral load builds up and a majority of the lateral load will have to transferred at the transfer diaphragm to the stiff basement walls if the transfer diaphragm is stiff enough. In the case of the higher level mode deformed shape, the applied loads will typically cancel out as you move from story to story.

This approach is backed up by NEHPR Seismic Design Technical Brief #5, Seismic Design of Composite Steel Deck and Concrete Filled Diaphragms, a few excerpts below

Shear Results

­Onto the fun plots. All plots produced with results from the diaphragm slicer and matplotlib

Notes on the shear plots

  • These are clickable for closer inspection, I realize they are quite tiny. Click to review and zoom
  • You can see big differences in shear forces between the differing models
  • Switching from a 24” thick transfer diaphragm to a 12” diaphragm with stiffness reduction for cracking led to a large decrease in diaphragm shear
    • 1335 kip (12”) / 2380 kip (24” diaphragm) = 0.56
  • Note that this could lead to a negative feedback loop, I need more diaphragm strength, whoops, I have more diaphragm demand.
    • One thing to pay attention to
  • Note that the diaphragm Fpx graph (red line) is tiny in comparison to the other transfer diaphragm forces. This illustrates the pitfalls with my old design method. You live and learn and get better.
  • I was also surprised to see how little impact the large hole had on the shear diagram (black and yellow plot)

Moment Plot

Not much to note here, moment results are in line with what you would expect after reviewing the shear diagrams.

The Takeaway

If you have a transfer diaphragm, be sure to do two diaphragm designs, one based on the overall ELF response and one based on the diaphragm design force applied as individual load case. Even on buildings where diaphragms are not immediately obvious as transfer diaphragms, doing this procedure can reduce your transfer diaphragm forces significantly. Any change in building geometry or the addition of a new lateral force resisting line, consider doing a little extra work of two diaphragm designs and the savings could be well worth it.

1 2 3 Next Last


Site Info

  • Latest Comments