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.