Skip to main content
Glama

FERS — structural FEM

Build a model from a node/member graph

build_model
Read-onlyIdempotent

Build a complete, solver-ready FERS model from an arbitrary node/member graph: 3-D frames, trusses, pitched roofs, bracing, towers — anything create_beam/create_frame cannot express. Returns a full FERS JSON document ready for solve_model.

GEOMETRY (1-BASED). nodes is a JSON array of [X, Y, Z] triples in METRES, Y up (gravity is −Y). The index you use to refer to a node IS its solver node id: nodes[0] is node 1, so results (displacement_nodes, reaction_nodes) come back under exactly the numbers you wrote. Index 0 is rejected. members is a JSON array of [startNode, endNode] / [startNode, endNode, "SectionName"] / [startNode, endNode, "SectionName", {options}]; member ids are 1..N in the order given. Member options: rotation_angle (DEGREES, rolls the section about the member axis), type ("Truss" for a pin-jointed axial-only bar; also Tension/Compression/Cable/Rigid), hinge ("start"|"end"|"both" — releases both bending moments there; at a joint only ONE of the members may be released, because a node whose every member end is released has no rotational stiffness left and the solve then fails outright), classification (free-text group name; one member_set per distinct value, and unity checks can target it).

COLUMN ORIENTATION — READ THIS. FERS's strong bending axis is the LOCAL z axis (i_z > i_y for an I-section), and the member local frame is built from a global-Y reference vector. A HORIZONTAL member therefore bends strong-axis under gravity automatically, but a VERTICAL member gets local y = global Z and local z = global X, so it resists sway along global X with its WEAK axis i_y. Verified: a 6 m cantilever column pushed 20 kN in +X deflects 1.1357 m at rotation_angle 0 (= PL³/3E·i_y) and 0.0820 m at rotation_angle 90 (= PL³/3E·i_z) — a 13.8× difference the solver will never warn about. In plane:"XY" this tool sets rotation_angle 90 on vertical bending members for you (reported in info.auto_rotation), because sway there is along X; in "3D" it only warns, since which axis you want depends on the structure — set rotation_angle explicitly.

SUPPORTS. supports maps a 1-based node number to a preset or an explicit DOF object: "fixed" (all 6 DOFs), "pinned" (X,Y,Z fixed, rotations free), "roller_x"/"roller_y"/"roller_z" (free to slide along that global axis), "free". For anything else pass {"X":"fixed","Y":"fixed","Z":"free","RX":"free","RY":"free","RZ":2.5e6} — a NUMBER is a spring stiffness (N/m for X/Y/Z, N·m/rad for RX/RY/RZ), and that node then settles by exactly force/stiffness. Watch the sign when you read results: a SPRING reaction comes back with the OPPOSITE sign to a rigid support carrying the same force (measured: +30 kN at the pin and −30 kN at the spring of one beam), so do not add the two up. Any DOF you leave out of an explicit object is written as Free (never omitted: in the raw JSON an absent DOF means FIXED, which is the opposite of what everyone expects).

PLANE. plane is "3D" (default) or "XY". "XY" requires every node to have Z = 0 and pins Z, RX and RY at EVERY node, which is what makes a planar model well-posed — essential for a planar TRUSS, where the bars carry exactly zero out-of-plane stiffness. Note that analysis.options.dimensionality is cosmetic: the solver never reads it, so only supports can make a model planar. An under-restrained model whose loads reach the free direction is reported, not solved: that load case's errors_and_warnings carries singular_model and its converged flag is false. Read errors_and_warnings before using any number.

SECTIONS. Give a library name ("IPE300", "HEA200", "CHS 114.3x5", "RHS 100x50x5", "UB 457x191x67", …) in the member row and it is looked up and inserted with all EC3 moduli. For a rectangle or your own numbers, declare it in sections (same object shape as create_section) and reference it by name. A member naming no section gets the first declared section, else default_section (IPE300).

LOADS are in kN, kN/m and kNm and are converted to newtons internally. loads is a list of load cases: {"name":"G","udl":[[member, w]],"nodal_loads":[[node, Fx, Fy, Fz]],"nodal_moments":[[node, Mx, My, Mz]],"point_loads":[[member, position0to1, Fx, Fy, Fz]]}. A 2-entry udl row [member, w] is w kN/m acting DOWN (−Y); a 4-entry row [member, wx, wy, wz] is a full vector. Combine them with load_combinations: [{"name":"ULS","factors":{"G":1.35,"W":1.5}}], referencing load cases BY NAME; omit it for two automatic ones over every case: combination 1 "SLS characteristic (auto)" at factor 1.0 and combination 2 "ULS (auto)" at 1.35. A combination you name gets limit_state "ULS" unless you give one, because a null limit_state makes every later unity check silently evaluate nothing and report a pass.

LIMITS: 100 members on the Free tier (hard "LimitExceeded" above that), so keep element counts modest.

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
loadsNoJSON array of load cases in kN / kN·m: [{"name":"G","udl":[[member, w]],"nodal_loads":[[node,Fx,Fy,Fz]],"nodal_moments":[[node,Mx,My,Mz]],"point_loads":[[member,pos0to1,Fx,Fy,Fz]]}]. A 2-entry udl row is kN/m acting DOWN.
nodesYesJSON array of [X, Y, Z] node coordinates in metres, Y up. The 1-based array position IS the node id: the first triple is node 1, so results come back under the numbers you wrote. Minimum 2 nodes.
orderNo"LINEAR" (default) or "NONLINEAR" for second-order.
planeNo"3D" (default) or "XY". "XY" declares a planar model (every node at Z=0) and pins Z/RX/RY at EVERY node, which is what makes a planar model well-posed — an under-restrained one comes back as singular_model (converged:false), with no usable numbers.
membersYesJSON array of member rows: [start, end] / [start, end, "IPE300"] / [start, end, "IPE300", {options}]. Node numbers are 1-based; member ids are 1..N in this order. Options: rotation_angle (DEGREES — use 90 on the columns of an XY-plane frame or they resist sway with their WEAK axis), type (Truss/Tension/Compression/Cable/Rigid), hinge (start|end|both), classification.
materialNosteel_S355 (default), steel_S235, aluminium_6061 or concrete_C30.
sectionsNoJSON array of section definitions (same shape as create_section), referenced by name from the member rows.
supportsNoJSON object mapping a 1-based node number to "fixed" / "pinned" / "roller_x" / "roller_y" / "roller_z" / "free", or to an explicit DOF object {"X":"fixed","Y":"fixed","Z":"free","RX":"free","RY":"free","RZ":1.0e7} where a NUMBER means a spring stiffness. Required — a model with no supports is rejected.
self_weightNoAdd self-weight as its own load case and factor it into the combinations.
project_nameNoLabel for the model.
default_sectionNoSection for members that name none. Default IPE300.
load_combinationsNoJSON array referencing load cases by NAME: [{"name":"ULS","factors":{"G":1.35,"W":1.5},"limit_state":"ULS"}]. Omit for two automatic ones over every case: 1 "SLS characteristic (auto)" at 1.0 and 2 "ULS (auto)" at 1.35.

Schema Changelog

Changes observed during successful MCP inspections.

  1. First observed

TDQS

Score is being calculated.

Try in Browser

Glama MCP Gateway

Add one secure layer between your agents and this server.