Automating Robot Structural Analysis with Python and AI

A field report: from the formwork plan to an analyzed Robot model and DXF reinforcement drawings, using a Python script written by AI. Method, API code, pitfalls, and checks.

Published
Reading
11 min
Language
EN
Cover: article title, a script window, and a wireframe frame on a drawing grid
Contents

Building a model in Robot Structural Analysis node by node and bar by bar, then drawing the reinforcement for every column and every beam: on a single house, that adds up to days of repetitive work. Yet Robot has an application programming interface (API) that lets you drive the whole program from Python.

I’m not a programmer, so I asked Claude to write the script for me, then tested it on two real reinforced concrete building projects. This article covers the method, the code that actually works, and above all what doesn’t.

Key point before you start: this script produces a model and drawings that still need checking. Choosing the structural system, setting the assumptions, and validating the results remain the engineer’s job. No result should be signed off without the checks described below.

The principle: AI writes, the engineer runs and validates

The AI doesn’t drive Robot directly. The roles are split simply:

  1. Claude reads the plans, writes the Python script, and analyzes the exported results.
  2. I run the script on my computer, where Robot is installed.
  3. The script builds the model, runs the analysis, and exports the internal forces to CSV files.
  4. Claude reads those CSV files, and the script produces the reinforcement drawings as DXF.
  5. I check every step before moving on to the next.

The setup:

ItemVersion
Analysis softwareAutodesk Robot Structural Analysis Professional 2014, French version
LanguagePython 3.13, IDLE editor
Connection to Robotpywin32 library (pip install pywin32)
Reading DXF plansAutoCAD

The API commands quoted in this article come from a script used on Robot 2014 for two real projects. The code snippets themselves are simplified for readability and have not been run as they appear here. The COM API still exists in recent versions of Robot, but some behaviors may differ.

The 7-step workflow

7-step workflow Seven successive steps: inputs, plan review and validation, data entry in the script, modeling and analysis in Robot, equilibrium check, reinforcement, review before sign-off. Loop A: if the equilibrium error is above the threshold, go back to step 3. Loop B: if a column has no reasonable solution, its section is enlarged and the full analysis is rerun from step 3. 1 Inputs: formwork and architectural plans 2 Plan review and validation 3 Data entry in the script 4 Modeling and analysis in Robot 5 Equilibrium check 6 Reinforcement, floor slabs, foundations 7 Review before sign-off equilibrium OK A B A Equilibrium error above the threshold: fix the inputs, back to step 3. B Column with no reasonable solution: enlarge the section, rerun the full analysis.
The 7-step workflow and its two correction loops.
  1. Inputs: formwork plan and architectural plan (DXF or PDF): grid lines, columns, beams, levels.
  2. Plan review and validation by the engineer: grid lines, sections, floor slabs, loads, soil and site data.
  3. Project data entry in the script: grid line coordinates, list of columns and beams, floor slab type.
  4. Modeling and analysis in Robot: nodes, bars, supports, loads, seismic action, combinations.
  5. Equilibrium check: if the difference between the computed loads and the reactions is above the threshold, go back to step 3 and fix the inputs.
  6. Reinforcement, floor slabs, and foundations, from the exported internal forces. If a column has no reasonable solution, its section is enlarged and the full analysis is rerun.
  7. Review of the results before sign-off: CSV files, DXF drawings opened in AutoCAD, calculation log.

The key rule: no reinforcement is calculated until the model is in equilibrium. That rule keeps you from drawing plans from a wrong model.

One script, clearly separated sections

On the first project, the work was spread over several scripts as I learned. On the second, everything fits in a single file of about 880 lines that runs in roughly fifty seconds.

The file is organized so that a new project only requires changes to the first three sections:

SectionContentsEdit for each project?
1. ProjectProject name, starting empty template model, steps to runYes
2. GeometryGrid lines, columns, sections, levels, list of beamsYes, from the formwork plan
3. Loads, seismic, soilFinishes, floor slabs, parapets, seismic parameters, allowable soil pressureYes
4 to 10Floor slab design, Robot control, reinforcement, foundations, DXF drawingNo

This separation is what makes the time savings real: the “engine” doesn’t get rewritten for every project.

Connecting to Robot from Python

Here are the basic API commands. One detail to remember from the start: the API always works in newtons and meters, whatever units are displayed in Robot.

The API commands are taken verbatim from a script used on Robot 2014 for two real projects. The code around them is simplified for readability and has not been run as it appears here: the snippets follow on from one another, and the example data (four columns, four beams) should be replaced with your project’s. Before using them on a project, try them on a small model (a single column) and compare the support reactions with a hand calculation.

Connecting, opening, and saving

import os
import win32com.client
from win32com.client import constants as C
DOSSIER = os.path.dirname(os.path.abspath(__file__))
PROJET = "mon_projet"
MODELE_VIDE = "MODEL.VIDE.rtd" # empty template model prepared by hand: sections, materials, display units
robot = win32com.client.gencache.EnsureDispatch("Robot.Application")
robot.Visible = 1
projet = robot.Project
# Open the empty template model, then save it under the project name
chemin = os.path.join(DOSSIER, MODELE_VIDE)
projet.Open(chemin)
projet.SaveAs(os.path.join(DOSSIER, PROJET + ".rtd"))
st = projet.Structure

Nodes, bars, and sections

# Example data, same structure as section 2 of the script (m)
AXES_X = {"A": 0.00, "B": 5.65}
AXES_Y = {1: 0.00, 2: 3.90}
POTEAUX = {"P1": ("A", 1), "P2": ("B", 1), "P3": ("A", 2), "P4": ("B", 2)}
SECTION_POTEAU = {"defaut": "C30x30"}
Z_ENCAST = -0.45
H = 3.80
POUTRES = [("A1", "P1", "P2", "B25x40", H), ("A2", "P3", "P4", "B25x40", H),
("A3", "P1", "P3", "B25x40", H), ("A4", "P2", "P4", "B25x40", H)]
# Derived geometry (copied from the script)
YMAX = max(AXES_Y.values())
XY = {p: (AXES_X[a], YMAX - AXES_Y[r]) for p, (a, r) in POTEAUX.items()}
POUTRE = {n: dict(nom=n, a=a, b=b, sec=s, z=z) for n, a, b, s, z in POUTRES}
def niveaux_poteau(p):
zs = sorted({q["z"] for q in POUTRE.values() if p in (q["a"], q["b"])})
return zs or [H]
def sec_poteau(p): return SECTION_POTEAU.get(p, SECTION_POTEAU["defaut"])
# Nodes.Create(number, x, y, z) in m; Bars.Create(number, start node, end node)
nid, n = {}, 0
for p in POTEAUX:
for z in [Z_ENCAST] + niveaux_poteau(p):
n += 1; st.Nodes.Create(n, XY[p][0], XY[p][1], z); nid[(p, z)] = n
barres_pot, barres_pou, b = {}, {}, 0
for p in POTEAUX:
zs = [Z_ENCAST] + niveaux_poteau(p)
for z0, z1 in zip(zs[:-1], zs[1:]):
b += 1; st.Bars.Create(b, nid[(p, z0)], nid[(p, z1)]); barres_pot[b] = (p, z0, z1)
for q in POUTRE.values():
b += 1; st.Bars.Create(b, nid[(q["a"], q["z"])], nid[(q["b"], q["z"])]); barres_pou[b] = q["nom"]
# The sections must already exist in the empty template model (see the pitfalls)
for bb, (p, z0, z1) in barres_pot.items(): st.Bars.Get(bb).SetLabel(C.I_LT_BAR_SECTION, sec_poteau(p))
for bb, nom in barres_pou.items(): st.Bars.Get(bb).SetLabel(C.I_LT_BAR_SECTION, POUTRE[nom]["sec"])

Fixed supports

lab = st.Labels.Create(C.I_LT_SUPPORT, "ENCAST")
d = lab.Data; d.UX = 1; d.UY = 1; d.UZ = 1; d.RX = 1; d.RY = 1; d.RZ = 1
st.Labels.Store(lab)
for p in POTEAUX: st.Nodes.Get(nid[(p, Z_ENCAST)]).SetLabel(C.I_LT_SUPPORT, "ENCAST")

Load cases and combinations

COMBI_ELU, COMBI_ELS = 3, 4
# Dead load case with self-weight; live load case: the constant really is spelled "EXPLOATATION"
cases = st.Cases
G = cases.CreateSimple(1, "G", C.I_CN_PERMANENT, C.I_CAT_STATIC_LINEAR)
Q = cases.CreateSimple(2, "Q", C.I_CN_EXPLOATATION, C.I_CAT_STATIC_LINEAR)
r = G.Records.Get(G.Records.New(C.I_LRT_DEAD))
r.SetValue(C.I_DRV_Z, -1); r.SetValue(C.I_DRV_ENTIRE_STRUCTURE, True)
# Uniform load on a beam: value in kN/m, converted to N/m
def uni(cas, bb, kN):
if abs(kN) < 1e-6: return
rr = cas.Records.Get(cas.Records.New(C.I_LRT_BAR_UNIFORM))
rr.SetValue(C.I_BURV_PZ, -1000.0 * kN); rr.Objects.FromText(str(bb))
# Example: 12 kN/m dead load on every beam (the script computes these loads from the floor slabs)
for bb, nom in barres_pou.items():
uni(G, bb, 12.0) # 12 kN/m: uni() multiplies by 1000, so the API receives -12000 N/m
# ULS and SLS combinations (ELU and ELS in French)
cat = getattr(C, "I_CAT_COMB", None)
if cat is None: cat = C.I_CAT_COMB_LINEAR
elu = cases.CreateCombination(COMBI_ELU, "ELU", C.I_CBT_ULS, C.I_CN_PERMANENT, cat)
elu.CaseFactors.New(1, 1.35); elu.CaseFactors.New(2, 1.5)
els = cases.CreateCombination(COMBI_ELS, "ELS", C.I_CBT_SLS, C.I_CN_PERMANENT, cat)
els.CaseFactors.New(1, 1.0); els.CaseFactors.New(2, 1.0)

Running the analysis and reading the results

projet.CalcEngine.Calculate()
res = st.Results
combis = [COMBI_ELU, COMBI_ELS]
# Internal forces: bar, combination, position along the bar (0 to 1); the API returns N and N·m
for bb, nom in barres_pou.items():
for c in combis:
for pos in (0.0, 0.25, 0.5, 0.75, 1.0):
F = res.Bars.Forces.Value(bb, c, pos)
print(nom, c, pos, round(F.FZ / 1000, 2), round(F.MY / 1000, 2)) # kN, kN·m
# Reactions: node, case; the API returns N
for p in POTEAUX:
for c in combis:
R = res.Nodes.Reactions.Value(nid[(p, Z_ENCAST)], c)
print(p, c, round(R.FZ / 1000, 2)) # kN

What doesn’t work (in Robot 2014)

This is the part you’ll hardly find anywhere else. Several API functions seem to work but leave you with an unusable model:

  • Creating a concrete section in code: the section name is created, but the analysis returns « Section non définie » (Section not defined). The fix: create all sections by hand in the empty template model, once and for all.
  • Labels.Exist: reports the label as missing even when the section exists. Don’t use it.
  • Cladding in code: the outline is created, but without the cladding property, and the area load never reaches the beams. Cladding created by hand works. So in the script, floor slab loads are computed in Python and applied directly to the beams.
  • The actual reinforcement design module: it can’t be automated. That’s why the script calculates the reinforcement itself, from the internal forces exported from Robot.

The pitfalls that waste the most time

Looking back, most of the lost time didn’t come from the analysis, but from these details:

SymptomCauseFix
The result is identical to the previous version of the scriptThe script window was still open in IDLE and wrote the old code back over the new oneClose the window without saving, reopen it, and check a line that only exists in the new version
PermissionErrorA CSV or DXF file is open in Excel or AutoCADClose the files before running the script
« Section non définie pour la barre n° … » (Section not defined for bar no. …)The section is missing from the empty template model, or its name differs (an extra space)Add the section by hand in the empty template model
« Le projet ouvert n’est pas vide » (The open project is not empty)The empty template model was saved with a model inside itRecreate the empty template model, and answer « Non » (No) when Robot offers to save it
All results are zeroThe analysis failed without Robot reporting it to PythonOpen the calculation messages window in Robot
A soil pressure of 20 MPaUnit mix-up: 2 bar = 0.20 MPaAny soil pressure in the tens of MPa is an error

I made that last mistake myself, twice. The AI caught it because I had asked it to flag any inconsistent value.

Mandatory checks

The script prints most of these checks automatically. No result is used until all of them pass:

  1. Vertical equilibrium: under G, the script compares the sum of the reactions in Robot with the load computed in Python. If the difference exceeds 5%, it stops before the reinforcement step. Under Q, both values are only printed, and the comparison is done by eye.
  2. Seismic base shear: the sum of the horizontal reactions must equal the computed seismic force, in all four directions.
  3. Displacements: story drifts must stay within the limits of the seismic code.
  4. Hand check of at least one element: for example, the midspan moment of a beam recalculated by hand.
  5. Consistency between the model and the drawings: if a column was enlarged for the reinforcement, it must be enlarged in the model too, and the analysis rerun, because stiffness changes how seismic forces are distributed.
  6. Reading every warning from the script: columns with no reasonable solution, excessive floor slab spans, large deflections.

A model in equilibrium is not necessarily a correct model

The first two checks have a limit you need to know about: they compare Python and Robot, which both work from the same geometry. If a column was placed on the wrong grid line, or a span was copied wrong from the plan, the model stays perfectly in equilibrium, and still wrong.

So the real risk in this workflow is data entry, not the analysis. Two independent checks cover it:

  • Geometry overlay: export the model geometry (grid lines, columns, beams with their names) to DXF, then overlay it on the original formwork plan in AutoCAD. Any mismatch shows up in seconds.
  • A hand load takedown on one column, compared with the axial force from Robot.

The seismic method has conditions of use

The script applies the equivalent static method of the seismic code. That method is only valid if the building meets the regularity conditions, in plan and in elevation. A building with split levels, for example, needs an explicit check before using it. Otherwise, a response spectrum (modal) analysis is required. That’s an engineering decision, to be made and recorded for each project.

Finally, the DXF drawings produced are engineering drafts: I rework them in AutoCAD before issuing anything.

A job-site rule in the code

On the sites where I work, beams and columns rarely use bars larger than HA14 (14 mm high-bond bars). So I gave the script two rules: no over-reinforcement, and no bar diameter above HA14. If an element doesn’t pass within that limit, the script never drops the steel below the required minimum: it flags the element and suggests a solution, such as a larger section or two layers of bars.

It’s a good example of what the engineer brings: the AI applies the code, but it doesn’t know site practice unless you tell it.

The results

  • Time saved: reinforcement detailing used to take me a full day; it now takes about two hours, checks included.
  • What stays manual: creating the sections in the empty template model, cladding, the actual reinforcement design module, and the final layout of the drawings.
  • What doesn’t change: responsibility. The script speeds up production, but every assumption and every result is still validated by the engineer.

Going further

Several improvements are still on the list: automatic geometry export for the overlay with the formwork plan, a rigid diaphragm at each level, response spectrum analysis directly in Robot, elastic supports, stairs and shear walls, and multistory buildings. I’ll come back to these in future articles.

If you want to try this, start small: a single-story frame, one support, one load, and compare the reactions with a hand calculation. It’s the best way to understand the API before trusting it with a real project.

This article was written with the help of AI tools, then reviewed and checked.

TagsRobot Structural AnalysisPythonAPIClaudeautomationreinforced concrete

Articles written by a state-certified civil engineer