Posts by retug (29)

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.

Website Database Scare

On Decemeber 10th, I logged into my website to be greeted by a scary error message

I had not done anything to the site, so I figured something bad more than likely happened.

The work week was busy, so I was not able to start troubleshooting until December 17th. Running the command heroku logs --tail  led to message that stated:

File "/app/my_website/settings.py", line 108, in <module> raise Exception("DATABASE_URL environment variable not defined") Exception: DATABASE_URL environment variable not defined

Reading this, I knew the database had somehow gotten disconnected. Logging into heroku where I host the website and database, I was greeted with a scary red warning label:

At this point, real panic sets in, what happened to my database? I knew that I was paying heroku to host my website, so I thought was just a warning they were putting on all dashboards. At this point, I open up the 10 emails I received from heroku stating that they are removing free hosting and database services. After some research, I realized that I was paying for the server, but not for the database; I was still using the free tier postgres database. 

Tail tucked between my legs, I follow heroku's "hey you dummy, you ignored all of our emails and you want to get your website back" tutorial and within 4 hours of opening a ticket and 9 bucks later, my database was restored.

The take away

DONT IGNORE IMPORTANT EMAILS!

One Way Load Distribution In ETABs

I have used ETABs one way load distribution button for a long time and it works great on flat floors and "slightly sloped roof" to distribute load based on tributary area.

Today though, a coworker of mine noticed on a slightly more pitched roof, angle of about 30 degrees, the one way loading was not working as intended (could not see applied loading due to tributary area).

I built a test model to explore the odd behavior.

Slab section properties uses one way distribution:

(3) sample floors are made, each with the same load and beam restraint layout, but you can see, once the floor becomes "too sloped", the load distribution switches from a line load on the frame elements to merely point loads applied at the edge of the membrane

 

The magic slope where this seems to occur is about 20 degress from horizontal.

Hopefully this post saves someone in the future the pains of troubleshooting this odd behavior

First Previous 1 2 3 4 5 Next Last

Sidebar

Site Info

  • Latest Comments