staad-results — independently scanned and version-tracked by SaferSkills.
SaferSkills independently audited staad-results (Agent Skill) and scored it 100/100 (green). The audit ran 55 deterministic rules across Security, Supply Chain, Maintenance, Transparency, and Community; it found 0 high-severity and 0 lower-severity findings. The full rule-by-rule trace and per-finding evidence are below. Free, methodology-open.
Findings & checks · 0 flagged
Every scanned point with the score it earned and what moved between them.
First recorded scan — no prior version to compare against.
The primary manifest — the file an agent reads to learn what this artifact does.
out = staad.Output, load = staad.Loadout.AreResultsAvailable() → True if results exist; False if analysis has not runFalse, run analysis before querying any results (see staad-analysis skill)AnalyzeEx returned status 4 (errors) or -1 (terminated), do NOT call any Output getter yet — querying results from a failed run has been observed to raise a misleading COMError: Memory is locked. instead of a clear failure message. Check the status code and AreResultsAvailable() first, then read staad.GetAnalysisErrorMessages() for the real cause — see staad-analysis → check-analysis-results.pyload.GetPrimaryLoadCaseNumbers() → returns a tuple — always wrap in list() before indexingEvery result getter (`GetMemberEndForces`, `GetNodeDisplacements`, `GetSupportReactions`, `GetAllPlateCenterStressesAndMoments`, etc.) returns numbers in the model's base unit system — the same units used by every input function (see staad-core → Units & Axis: English=inches/KIP, Metric=meters/kN). staad.GetBaseUnit() is the only source of truth for interpreting a returned value.
GetOutputUnitFor* report the unit the STAAD.Pro UI is currently configured to display each result category in. Use them as the target of a conversion so the values you report match what the user sees on screen — never as a description of what the getters returned.
out.GetOutputUnitForForce() # e.g. "kN"
out.GetOutputUnitForMoment() # e.g. "kN-m"
out.GetOutputUnitForDisplacement() # e.g. "mm"
out.GetOutputUnitForStress() # e.g. "N/mm2"
out.GetOutputUnitForDimension() # e.g. "m"
out.GetOutputUnitForRotation() # e.g. "rad"
out.GetOutputUnitForSectDimension() # e.g. "cm"
out.GetOutputUnitForSectArea() # e.g. "cm2"
out.GetOutputUnitForSectInertia() # e.g. "cm4"
out.GetOutputUnitForSectModulus() # e.g. "cm3"
out.GetOutputUnitForDensity() # e.g. "kg/m3"
out.GetOutputUnitForDistForce() # e.g. "kN/m"
out.GetOutputUnitForDistMoment() # e.g. "kN-m/m"Base unit → UI unit is rarely 1:1. On a Metric model the base length is m but the UI displays displacements in mm — a factor of 1000. Reporting a raw GetNodeDisplacements value next to the mm label without converting overstates or understates the result by three orders of magnitude, which silently breaks any serviceability check (e.g. "is the drift under 10 mm?").
Recommended reporting workflow:
user's limits/targets into base units, not the other way round).
GetOutputUnitFor* unit and label it with that unit string.
output_data_path),the label belongs in the header cell as Name [unit] — see below.
out = staad.Output
base = staad.GetBaseUnit() # 'English' or 'Metric'
disp_unit = out.GetOutputUnitForDisplacement() # UI display unit, e.g. 'mm'
# Length conversion factors FROM the base length unit (Metric: m, English: in)
per_base_length = {
'Metric': {'m': 1.0, 'cm': 100.0, 'mm': 1000.0},
'English': {'in': 1.0, 'ft': 1.0 / 12.0},
}
factor = per_base_length[base][disp_unit]
ux, uy, uz, rx, ry, rz = out.GetNodeDisplacements(node_id, lc)
print(f'Node {node_id} UY = {uy * factor:.3f} {disp_unit}') # matches the STAAD.Pro UIBuild the factor table only for the unit strings you actually receive — read the GetOutputUnitFor* value at runtime rather than assuming a fixed UI configuration, and raise/report clearly if the returned string is one you have no factor for.
The GetOutputUnitFor* methods raise on error (e.g. if the model has no output units established yet) — execute_code reports any such error.
#### Result tables — unit in the header cell
Build the header from the live GetOutputUnitFor* string, one header row only:
force_unit = out.GetOutputUnitForForce() # e.g. 'kN'
moment_unit = out.GetOutputUnitForMoment() # e.g. 'kN-m'
result = {
"Reactions": {
"columns": ["Node ID", "Load Case",
f"FX [{force_unit}]", f"FY [{force_unit}]", f"MZ [{moment_unit}]"],
"rows": rows, # numbers already multiplied by the base→UI factor
},
}Never write the units as a second row underneath the header: row 0 is the header and everything below it is data, so a units row comes back as a record on re-import, skews the row count in the execute_code summary, and forces the whole Excel column to text (no sorting, no SUM, no charts). ID and load-case columns stay unbracketed.
When the same table is shown in chat rather than written to a file, the unit may sit on its own line inside the header cell (| FX<br>[kN] |) — still one header row, still inside the cell. See staad-core → Table Output Format for the full output shape.
For the solver's error/warning text after a run (GetAnalysisErrorMessages / GetAnalysisWarningMessages), see the staad-analysis skill.
| Function | Returns |
|---|---|
GetNodeDisplacements(nid, lc) | [UX, UY, UZ, rX, rY, rZ] |
GetSupportReactions(nid, lc) | [FX, FY, FZ, MX, MY, MZ] (global) |
| Function | Returns | Notes |
|---|---|---|
GetMemberEndForces(bid, end, lc, local) | [FX,FY,FZ,MX,MY,MZ] | end: 0=StartA, 1=EndB; local: 0=Local, 1=Global |
GetMinMaxAxialForce(bid, lc) | (min, minPos, max, maxPos) | position along member |
GetMinMaxShearForce(bid, dir, lc) | (min, minPos, max, maxPos) | dir: 'FY' or 'FZ' (string) |
GetMinMaxBendingMoment(bid, dir, lc) | (min, minPos, max, maxPos) | dir: 'MY' or 'MZ' (string) |
GetMemberEndDisplacements(bid, end, lc) | [X,Y,Z,rX,rY,rZ] | |
GetMaxSectionDisplacement(bid, dir, lc) | (maxDisp, position) | dir: "X", "Y", "Z" |
GetMaxBeamStresses(bid, lc) | stress tuple |
out.GetIntermediateMemberForcesAtDistance(bid, distance, lc)
out.GetIntermediateMemberTransDisplacements(bid, distance, lc)
out.GetIntermediateDeflectionAtDistance(bid, distance, lc)
out.GetIntermediateMemberAbsTransDisplacements(bid, distance, lc) # relative displacement in LOCAL X/Y/Z (vs global for the Trans variant above)See [PLATE_RESULT_INDICES.md](./assets/PLATE_RESULT_INDICES.md) for the index→symbol meaning of each return value below.
# Center stresses [SQX, SQY, MX, MY, MXY, SX, SY, SXY]
out.GetAllPlateCenterStressesAndMoments(plateNo, lc)
out.GetAllPlateCenterForces(plateNo, lc)
out.GetAllPlateCenterMoments(plateNo, lc)
out.GetPlateCenterNormalPrincipalStresses(plateNo, lc)
out.GetAllPlateCenterPrincipalStressesAndAngles(plateNo, lc)
out.GetAllPlateCenterPrincipalStressesAndAnglesEx(plateNo, lc) # adds top/bottom max-shear stress to the 4 principal values, plus per-face angles
out.GetPlateCenterVonMisesStresses(plateNo, lc)
# Corner forces
out.GetPlateCornerForces(plateNo, cornerCode, lc) # cornerCode = node number
# Stress at arbitrary point
out.GetPlateStressAtPoint(plateNo, lc, stressPoint, facingPoint) # [x,y,z] lists
# Resultant force/moment along a cut line through a set of plates or a parametric surface
fx_fy_fz_mx_my_mz = out.GetResultantForceAlongLineForPlateList(plateIds, len(plateIds), lc, startXYZ, endXYZ, transformToGlobal, node1, node2, node3)
fx_fy_fz_mx_my_mz = out.GetResultantForceAlongLineForParametricSurface(surfaceName, plateCount, lc, startXYZ, endXYZ, facingXYZ, transformToGlobal, node1, node2, node3)out.GetAllSolidNormalStresses(solidNo, corner, lc) # corner 1-8
out.GetAllSolidShearStresses(solidNo, corner, lc)
out.GetAllSolidPrincipalStresses(solidNo, corner, lc)
out.GetAllSolidVonMisesStresses(solidNo, corner, lc)n_modes = out.GetNoOfModesExtracted()
freq = out.GetModeFrequency(modeNo) # Hz
disp = out.GetModalDisplacementAtNode(modeNo, nid) # [X,Y,Z,rX,rY,rZ]
factors = out.GetModalMassParticipationFactors(modeNo)if out.IsBucklingAnalysisResultsAvailable():
n = out.GetNoOfBucklingFactors()
factor = out.GetBucklingFactor(modeNo)
disp = out.GetBucklingModeDisplacementAtNode(modeNo, nid) # [X,Y,Z,rX,rY,rZ]delta, n_steps = out.GetTimeHistoryIntegrationStepInfo()
# DOF: 1-3=trans, 4-6=rot; response_type: 0=disp, 1=vel, 2=accel
responses = out.GetTimeHistoryResponse(lc, nid, dof, response_type)
resp_at_t = out.GetTimeHistoryResponseAtTime(lc, nid, dof, response_type, time)
resp_min, resp_max = out.GetTimeHistoryResponseMinMax(lc, nid, dof, response_type)n_steps = out.GetNLLoadStep(lc)
load_level, displacements = out.GetNLNodeDisplacements(nid, lc, loadStep)# Single-block (all codes)
(code, status, ratio, allow, lc, loc, clause, section, forces, klr) = out.GetMemberSteelDesignResults(bid)
# status: 'PASS' or 'FAIL'; ratio > 1.0 = failure
ratio = out.GetMemberSteelDesignRatio(bid) # -999=not designed, -1=no analysis
max_r = out.GetMemberSteelDesignMaxFailureRatio() # model-wide max
min_r = out.GetMemberSteelDesignMinFailureRatio() # model-wide min
# Multi-block (AISC 360 etc.)
if out.IsMultipleMemberSteelDesignResultsAvailable():
n_blocks = out.GetSteelDesignParameterBlockCount()
blk_name = out.GetSteelDesignParameterBlockNameByIndex(i)
ratio = out.GetMultipleMemberSteelDesignRatio(blk_name, bid)
max_ratio = out.GetMultipleMemberSteelDesignMaxRatio(bid)
# Full result tuple for one block (code, status, ratio, allowable, critical lc, clause, section)
(code, status, ratio, allow, crit_lc, clause, section) = out.GetMultipleMemberSteelDesignResults(blk_name, bid)
# Design section name assigned by a completed design run (raises if not found — pre-analysis use GetSectionPropertyName instead)
section_name = out.GetMemberDesignSectionName(bid)out.GetPMemberEndForces(memberNo, end, lc, localOrGlobal)
out.GetPMemberIntermediateForcesAtDistance(memberNo, distance, lc)out.GetStaticCheckResult(lc)
out.GetMatInfluenceAreas(nodeList)
out.GetBasePressures(lc, nodeList)See fetch-forces.py for a complete working example.
GetMinMaxShearForce/GetMinMaxBendingMoment dir argument is a string, not an integer — 'FY'/'FZ' for shear, 'MY'/'MZ' for momentGetMemberEndForces at end 0 (StartA) and end 1 (EndB) have opposite sign conventionsGetPrimaryLoadCaseNumbers() returns a tuple — convert with list() before indexingGetMemberSteelDesignResults raises an error for members not in the design briefAreResultsAvailable() before any output queryGetBaseUnit()). Compare against limits in base units, then convert to the GetOutputUnitFor* unit only when displaying to the userGetOutputUnitFor* string without converting it firstOutput getter right after a failed/errored AnalyzeEx (status 4 or -1) can raise COMError: Memory is locked. instead of a clear error — check AreResultsAvailable() and the analysis status first, see staad-analysis skill~30 seconds. Free. No account. Every finding cites a rule and a line of evidence.