Posts by retug (37)

Minimum Joist Bearing Lengths - Revit and Dynamo

A co-worker of mine asked if I could find a way to check min. joist bearing lengths after being burned on a recent project where they had a few beams that worked for strength and deflection, but did not match SJI's minimum flange dimensions for bearing.

Tedious, easy problems like this is where dynamo really shines, lets dig into the code to make this happen.

Step 1 - Collect All Bar Joists

The dynamo graph located on my github works by pulling all the K-Series Joists and LH-Series Joists in the current 3d view.

If you have never seen a dynamo graph before, above is an image of the code used to make joist bearing length checker. I believe the term is visual programming, where you connect code bits with wires and it creates a nice visual programming look.

Please note when using the tool, it will only work based on the elements in your current view (I usually run this dynamo graph in a 3d view). 

This bit of code gathers all of the K  and LH Series Joists in the project.

import clr
clr.AddReference('RevitAPI')
from Autodesk.Revit.DB import *
clr.AddReference('RevitServices')
from RevitServices.Persistence import DocumentManager

doc = DocumentManager.Instance.CurrentDBDocument

category = UnwrapElement(IN[0])
joists = IN[1]
elements = FilteredElementCollector(doc, doc.ActiveView.Id)\
        .OfCategoryId(category.Id)\
        .WhereElementIsNotElementType()

filtered = []
for e in elements:
    type_id = e.GetTypeId()
    element_type = doc.GetElement(type_id)
    family = element_type.Family
    name = family.Name
    if joists[1] in name or joists[0] in name:
        filtered.append(e)

OUT = filtered
Step 2 - Check for Locations where Joist Bearing Should be checked.

Above identifies the locations where the program might want to check joist bearing, but we want to reduce the number of points the program will check if we have (2) joists bearing on the same girder.

The image below will be used in the post, note that there are  (4) total joists in the sample, the top two joists being K Series Joists, with an offset in plane, and the bottom two joists being LH Series joist, parallel to each other.

The first part of the code will identify (2) unique points for each end of each bar joist, giving a total of (8) total points. In this example though, I want to filter out the location where both LH-series joists are parallel, as this will be a location where the program should spit out only (1) unique point due the joist occurring at the same locatoin, requiring double the minimum bearing length:

This bit of code filters out points if they are close to each other:

pnts = IN[0]
tol = IN[1]
# Place your code below this line
def distance(point1,point2) :
    x1 = point1.X
    y1 = point1.Y
    z1 = point1.Z
    x2 = point2.X
    y2 = point2.Y
    z2 = point2.Z
    dist = ((x2-x1)**2+(y2-y1)**2+(z2-z1)**2)**0.5
    return dist
uni_pnts = []
temp_pnts = pnts
while len(temp_pnts) >= 1:
    if len(temp_pnts) != 1:
        pnt_init = temp_pnts[0]
        rem_pnts = temp_pnts[1:]
        j = 0
        on_off = 1
        while on_off >= 0.5:
            delta = distance(pnt_init,rem_pnts[j])
            if delta <= tol :
                uni_pnts.append(pnt_init)
                temp_pnts.pop(j+1)
                temp_pnts.pop(0)
                on_off = 0
            elif j == len(rem_pnts)-1:
                uni_pnts.append(pnt_init)
                temp_pnts.pop(0)
                on_off = 0
            else:
                j+=1
    else:
        uni_pnts.append(temp_pnts[0])
        temp_pnts.pop(0)
# Assign your output to the OUT variable.
OUT = uni_pnts

Given that the program will run for an unknown amount times, we need to utilize a while loop and remove items from our list as they go, python's .pop() function is quite useful to do this.

In our sample above, it takes the number of points from (8) to (7) as it recongnizes the (2) LH series joists being co-planar. You can see in my poor sketch below, the program effectively realizes the red points of 6 and 7 are close enough and conisders them to be a double bar joist location now.

Step 3 - Make A bounding box at each of the purple nodes

A bounding box in dynamo is quite useful for checking if geometries intersect with other geometries, in our case, we need to check if the bounding box at the purple nodes crosses any beams. Bounding boxes in dynamo show up in this orange color by default. See below

In order to do this, we use dynamo list lacing and change it to longest to check all purple bounding boxes and see if they cross any of the wide flange framing. If they do, we need to check our SJI minimum bearing lengths verse the wide flange width. In the snip below, each of the (7) purples nodes identified above are checked to see if they cross any wide flange beam. In our test model, there are only (3) beams, so 7*3 = 21 true-false results are returned. Where a true occurs, a joist is supported by the corresponding wide flange beam.

This is one area that I would like to revisit, in larger models, I am sure this task that gets computationally expensive, and quickly. If anyone has any ideas on a cool algorithm to tackle this part of the program, please let me know. I plan to explore this further in the future.

Part 4 - Testing Beam Widths vs. SJI Minimums

Now that we have idenified where double and single joist connections occur, in addition to the beam that supports the specific joist, we can now check SJI Bearing lengths, 4" minimum for a K Series joist and 6" minimum for an LH Series joist.

This is done with a long if statement shown below:

# The inputs to this node will be stored as a list in the IN variables.
joist_types = IN[0]
beam_widths = IN[1]
pnts = IN[2]

pnts_failing = []
for ind, (joist,beam_width) in enumerate(zip(joist_types,beam_widths)):
   if len(joist)==1:
       if "K" in joist[0]: #SJI Min Bearing for K Series is 4"
           min_seat = 0.3333 
           if beam_width[0] <= min_seat:
               pnts_failing.append(pnts[ind])
           else:
               pass
       else:
           min_seat = 0.5 #SJI Min Bearing for LH Series is 6"
           if beam_width[0] <= min_seat:
               pnts_failing.append(pnts[ind])
           else:
               pass
   else:
       if "LH" in joist[0] or "LH" in joist[1]: #SJI Min Bearing for (2) LH Series is 12"
           min_seat = 1
           if beam_width[0] <= min_seat:
               pnts_failing.append(pnts[ind])
           else:
               pass
       else:
           min_seat = 0.67 #SJI Min Bearing for (2) K Series is 8"
           if beam_width[0] <= min_seat:
               pnts_failing.append(pnts[ind])
           else:
               pass

Part 5 - Visualize the Results

The program places red orbs at locations where minimum joist bearing is not met. In our example the central girder is a W18x35, bf = 6". A singular red orb is placed at the LH-Series location as the LH location would need 12" of flange width to meet SJI requirements.

A nice pop up window stating how many joists did not have adequate joist bearing is also displayed.

Hopefully this example demonstrates a useful way to utilize dynamo. Let me know how you use dynamo, it is a great way to automate boring, time consuming tasks that need be completed.

About Page Eye Candy - Javascript and Web Dev Misery

With a desire to update my "about" page, I set out to kill two birds with one stone, a fresh new about page and learning 3d graphics in the browser. https://www.re-tug.com/about/ is built with three.js, a package built within Javascript to make 3d graphics. 

About threejs

I followed a tutorial on youtube to create the threejs scene in the background. My code to make the pulsating mesh can be found here. The about page turned out really cool, click and drag on the background and you should be able to rotate the pulsating mesh.

Learning both C# and javascript recently has lead to a lot of mistakes and lost time, but it has been a enjoyable process. Learning how to debug with console.log has become natural at this point.

Trying to tweak the code in the youtube link to my liking lead to some interesting errors, below was my attempt to manually recreate the wireframe pulsating:

Deployment Hell

Through building this website, I have learned that merely getting your code up and running on a development server is usually about 1/4 of the battle. After getting through the tutorial I set out to push this code up to the internet for everyone to see. (I am mostly writing this part so I have something to reference in the future when I am going through deployment pains)

Attempt #1 - Using Script Tags to Load ThreeJS

After some research, it seemed like the easiest and quickest way to get threejs, orbit controls and gsap (the packages used to make the graphics) was through script tags in htlm.

I had something that looked like the following:

<script type="importmap">
{
"imports": {
     "three" : "https://unpkg.com/three@0.126.1/build/three.module.js",
      "orbitcontrols" :      "https://unpkg.com/three@0.126.1/examples/jsm/controls/OrbitControls.js",
"datgui" : "https://cdnjs.cloudflare.com/ajax/libs/dat-gui/0.7.9/dat.gui.min.js"
"gsap" : "https://cdnjs.cloudflare.com/ajax/libs/gsap/3.10.4/gsap.min.js"
}
}
</script>

<script type="module" src="{% static 'blog/threeBackground.js' %}"></script>

This method relies on cdns (content delivery networks) to deliver the code that is used for threejs, orbit controls etc. I was successfully able to deploy my code with this method, but the code did not work on firefox nor any of the iOS web browsers. 

With my whole family and friends using apple products, I had to find a better to make this happen. Apparently script tags are going to be outdated in the future and I think this is why it did not work on firefox and iOS web browsers.

I would get console errors like the following:

At this point I had maybe sunk 20 hours of work into getting this eye candy into the website.

Attempt #2 - Using node.js and NPM

I'll be honest, I still have no idea what node.js and NPM (node package manager) are, but they seemed like the solution to my iOS problems.

My best understanding of these, node.js is a way to write javascript outside of the web browser and NPM is similar to pythons pip command, a way to install packages. 

Using node.js and npm, I could see threejs and other modules being downloaded into a folder called node_modules in my file directory. Naively, I thought this was the ticket, I could just reference these files on my production machiene on heroku. This did not work and I spent way too long going down this route.

Attempt #3 - Using Webpack, A Bundler

Webpack is another javascript package that converts the javascript file I linked on my github into a singular javascript file that embeds all of the threejs code and other javascript libraries referenced and places it into one singular file. A sample of the file created by using webpack is shown below:

if(t.applyMatrix3(this.matrix),t.x<0||t.x>1)switch(this.wrapS){case r:t.x=t.x-Math.floor(t.x);break;case a:t.x=t.x<0?0:1;break;case
 s:1===Math.abs(Math.floor(t.x)%2)?t.x=Math.ceil(t.x)-t.x:t.x=t.x-Math.floor(t.x)}if(t.y<0||t.y>1)switch(this.wrapT){case r:t.y=t.y-Math.floor(t.y);break;case a:t.y=t.y<0?0:1;break;case 
s:1===Math.abs(Math.floor(t.y)%2)?t.y=Math.ceil(t.y)-t.y:t.y=t.y-Math.floor(t.y)}return this.flip

Lots of gibberish to my eyes...

The command to make this all encompassing file on my development server was: npm run build

This file was the ticket to making the "about" page work on all devices, it no longer requires the html code to have script tags and nor the use of CDNs for the packages like three.js

I had to upload this static file to heroku and serve it up and the rest is history. All total, I think I spent about 10 hours on the actual three.js tutorial part and 40 hours on the deployment aspect of the "about" page. I would like to thank and apologize to my wife for her having to listen me scream, laugh and cry during deployment of this page.

DEPLOYMENT IS TERRIBLE!

Notes on heroku deployment:

  • Disable collectstatic by default, have to run:
    • heroku run python manage.py collectstatic
  • Can ignore double static file reference on deployment
    • heroku defaults to ....\my_website\blog\static
  • added in gitignore file node_modules
  • Within procfile added:
    • web: gunicorn my_website.wsgi npm start

 

Revit Rebar Bender - New and Improved

Above is my first functional C# revit add-in, making bending rebar at any angle an easy task. For those non-structural engineers, the smaller the piece of rebar, the smaller the allowable bend. Being able to quickly sketch rebar to scale with minimum bend radii is often helpful to see if a detail sketch is constructible. This add-in will bend rebar to the ACI minimum allowable bend diameter and will allow for as many points as the user desires.

How to use the add-in

To start the add-in navigate to the add-in tab, click the drop down and select your desired rebar size. Once clicked, you can now start clicking the points where you want to draw the rebar. Once you are done selecting your rebar points (3) minimum, you hit escape to have Revit create a filled region in the location where you clicked. Currently, the filled region defaults to a filled region type of "Solid Black", make sure you have this filled region in your revit project for the add-in to work. The code was written for Revit 2022, I have yet to try the code in any other version of revit.

You can find the code on my github.

Lessons Learned in C#

Given that this was my first real dive in C# and the Revit API, there were a ton of things to learn a few examples.

  • 2/12 does not equal what you think it might, you have to cast your integer 2 and integer 12 to a double as shown below

 

        double x = 2/12; //This actually = 0
        double x = (double)2/(double)12; // 0.16667

 

  • I still do not fully understand FilteredElementCollector (this class gathers revit items from my understanding). Looking at these lines of code is intimidating... The lines below gather all of the filled region types in the revit project and finds the unique ID for "Solid Black"

 // find solid fill type in our revit list
            FilteredElementCollector fillRegionTypes = new FilteredElementCollector(doc).OfClass(typeof(FilledRegionType));
            var patterns = new FilteredElementCollector(doc).OfClass(typeof(FilledRegionType)).FirstElement();
            foreach (Element elem in fillRegionTypes)
            {
                if (elem.Name == "Solid Black")
                {
                    patterns = elem;
                }
            }

  • I made a fun function that tests if user selected points are clicked in clockwise or counterclockwise manner. I was super proud of this, then found out that the Revit API has this function prebuilt into the Curveloop class. I am going to tell myself that my function is way more optimized than the curveloop method to make myself feel better.


public static double ClockWise(List<XYZ> pnts)
        {
            double y = 0;
            for (int i = 0; i <= pnts.Count - 1; i++)
            {
                if (i == pnts.Count - 1)
                {
                    double x = (pnts[0].X - pnts[i].X) * (pnts[0].Y + pnts[i].Y);
                    y = (double)y + (double)x;
                }
                else
                {
                    double x = (pnts[i + 1].X - pnts[i].X) * (pnts[i + 1].Y + pnts[i].Y);
                    y = (double)y + (double)x;
                }
            }
            bool CW_CCW = true;
            if (y <= (double)0)
            {
                CW_CCW = false;
            }
            return y;
        }

Future Work

Currently this add-in will not work in any view other than a drafting view, I would like to come back to this at some point and let the code work in live view. I should have tested this earlier, I started into this task trying to alleviate the issues of the original revit rebar bender and keep this completely detail component based. 

Another item to add in the future is to load the predefined hooked ends, e.g. 90 135, 180 degree hooks. This should be pretty easy if I am thinking about this correctly, but might the necessitate the need for popup window for the user to select hook end directions and hook types

More items off the top of my head, allow for self intersection (currently breaks if the rebar loops cross), update the sloppy code up on Github (could probably be 300 lines instead of 900 lines), learn C# better. Currently I do not know how to have a function return multiple outputs like a list and int. Overall a fun project that yields a semi functional end product, I will never complain about that.

Download the code off of github and try it out, let me know if you have any comments.

 

Revit Addin

Making a revit addin has been a goal of mine for some time. Yesterday I finally succeeded, complete with ridiculous names, images and tasks completed.

The addin is simple and essentially useless, it assigns walls of a certain type to a workset (Github).

Learning C#

I learned C# mostly through Autodesk's tutorial on the Revit API, found here. C# is different enough from python  that it initimiated me.

A few examples:

  • Declare variable types
  • Cast variables
  • Compile your code etc.

 

Those familiar with C# probably know all of these items, it was fun to learn on my end coming from python. This video guided me in making the ribbon found in the user interface of revit.

You can see in the code below, if you know what a for loop is, you can piece together what these lines are doing, there are just a lot more { } everywhere:

foreach (Workset workset in worksetList)
{
   if (workset.Name.Contains("Concrete"))
       {
           ConcreteWalls = workset.Id.IntegerValue;
       }
}
TaskDialog.Show("Revit", ConcreteWalls.ToString());
// note we have now identified the Concrete shearwall workset

Learning C# sharp was relatively quick. My mind remembered the pains of learning python, but most of the struggles with python were due to never learning about object oriented programming. Classes, objects and everything that went along with an object oriented programming were easy enough to map from python to C#.

Learning C# taught me an important skill, the use of debugger. Why I never used this while coding in Python I will never know. Debuggers allow you to not have to print variables out all the time and appear to have a lot more power that I have not yet unleashed. In the code example above, I was able to watch the debugger stepping through the foreach loop for each workset in the project, and if the workset contains "Concrete" it steps out of the for loop. I look forward to learning more about debugging in both C# and python.

As with any new skill or programming language, intimidation and coding tutorials are the obstacles to progress. Once I actually started coding, debugging, and yes, print screening still, making the addin started to click. 

A negative of C#

To debug the program you have to start and close revit all the time, this is super time consuming! The ability to quickly whip up a revit idea in dynamo and python is really unparalleled. Getting instant feed back on the items in a list of variable is definitely valuable. If you know of a way to get around revit having to fire up each time a change is made to the code, please let me know.

C# Moving forward

C# appears powerful, but will only be reserved for projects that warrant the slow development process. One that comes to my mind is the revit rebar bender. Knowing C# will help me alleviate the limitations that the rebar adaptive component faced. C# is also supported by the ETABs API and I believe you can even have your very own button in the UI of ETABs like Revit. More work in the future...

Time Histories to Response Spectrums

I have inherited an awesome project through the most unfortunate circumstances, the loss of a great engineer moving onto bigger and brighter pastures.

The project is going to be an ASCE 41 non-linear dynamic analysis and has had geotech engineers involved in picking out ground motion that represent similar ground motions that the faults near this building may produce.

Converting from time history to response spectrum can be done in ETABs, but post processing the data is kinda difficult, I can't seem to find a way to programmatically access the response spectrum curves that ETABs produces.

It's also never a bad day when you have to break out the Chopra Book, so I decided to try to code the process.

The Results

 

You can find the code to make plots like these  on my github:
The required data format is a .txt file that is separated with returns and tabs. I have heard not all geotechs provide their time history data in this manner, so if you want to use this, you may have to edit.

Starting into this, I thought this process involved using fourier transforms to transition from the time domain to the frequency domain, I clearly had forgotten my structural dynamics. Reading chapter 5 of Chopra and getting some guidance from fellow engineer Bryant, I was able to recreate the Newmark method outlined in the Chopra book. Adding a whole bunch of for loops later, some awesome plots popped out.

Comparing the BSE-2E plots (the big, rare earthquakes in ASCE 41) to the code generated response spectrum yielded a ~1.3 increase in peak accelerations at the typical plateau of the response spectrum (time histories were produced high accelerations). 

The BSE-1E time history plots were much closer to the code generated BSE-1E response spectrum.

First Previous 4 5 6 7 8 Next Last

Sidebar

Site Info

  • Latest Comments