FERS — structural FEM
Server Details
Structural FEM for AI agents: frames, plates, buckling, seismic and Eurocode checks, solved locally.
- Status
- Healthy
- Last Tested
- Transport
- Streamable HTTP · MCP 2025-11-25
- URL
- Repository
- Jeroen124/FERS_core
- GitHub Stars
- 2
TDQS
Score is being calculated.
Available Tools
35 toolsadd_imperfection_caseAdd a geometric imperfectionRead-onlyInspect
Add a geometric imperfection — a global sway (out-of-plumb lean, EN 1993-1-1 §5.3.2) and/or a member bow — bound to specific load combinations. The solver perturbs the geometry before solving those combinations, so it feeds both first-order and P-Delta results. Give the sway as a ratio denominator (200 means a 1/200 lean); it is stored in radians. IMPORTANT: the engine ignores an unknown combination id SILENTLY, so a typo would apply no imperfection at all — this tool rejects unknown ids instead. Free.
| Name | Required | Description | Default |
|---|---|---|---|
| model | No | Stringified FERS model JSON | |
| model_ref | No | Handle from stash_model, used INSTEAD of `model`. Avoids sending the whole document on every step; the tool returns the same handle back. | |
| sway_axis_x | No | Sway rotation axis X (default 0). | |
| sway_axis_y | No | Sway rotation axis Y (default 0). | |
| sway_axis_z | No | Sway rotation axis Z (default 1). The lean direction is axis x height_direction. | |
| height_direction_x | No | Direction height is measured along, X (default 0). | |
| height_direction_y | No | Y (default 1 — these models are Y-up). | |
| height_direction_z | No | Z (default 0). | |
| sway_magnitude_rad | No | Sway angle directly in radians, if you would rather not use a ratio (0.005 = 1/200). | |
| translation_axis_x | No | Bow direction X (default 1). | |
| translation_axis_y | No | Bow direction Y (default 0). | |
| translation_axis_z | No | Bow direction Z (default 0). | |
| load_combination_ids | Yes | JSON array of combination ids this imperfection applies to, e.g. [1,2]. Required — an imperfection only affects the combinations it names. | |
| translation_magnitude | No | Bow amplitude in model length units. Required when translation_memberset_ids is given. | |
| sway_ratio_denominator | No | Sway as 1/N: pass 200 for a 1/200 lean. Converted to radians for you. | |
| translation_memberset_ids | No | JSON array of member-set ids to give a bow imperfection, e.g. [1]. |
add_loadAdd a loadRead-onlyInspect
Add ONE load of any kind to an existing load case. Choose kind: "nodal_force" (force at a node), "nodal_moment" (moment at a node — direction is the moment AXIS, right-hand rule), "distributed" (line load along a member; covers uniform, TRAPEZOIDAL/TRIANGULAR via end_magnitude, and PARTIAL via start_frac/end_frac), "member_point_force" / "member_point_moment" (at a fractional position along a member, in global or local axes), "surface" (pressure over a convex planar polygon, distributed onto the coplanar members inside it), "plate_pressure" (pressure on a meshed plate surface). SIGN: direction_x/y/z is a GLOBAL vector — gravity is direction_y = -1. The direction is normalised for you and the whole load stays in magnitude (the raw engine multiplies component-wise, so an unnormalised [0,-2,0] would silently double the load). Distributed intensity is per unit of MEMBER length, not of horizontal projection. Units are the model's own (default N, m) EXCEPT plate_pressure, which must always be in SI pascals. Free.
| Name | Required | Description | Default |
|---|---|---|---|
| axes | No | member_point_* only: "global" (default) or "local" (member frame, local x along the member). | |
| kind | Yes | nodal_force | nodal_moment | distributed | member_point_force | member_point_moment | surface | plate_pressure | |
| node | No | Target node id — required for nodal_force / nodal_moment. | |
| model | No | Stringified FERS model JSON | |
| member | No | Target member id — required for distributed / member_point_force / member_point_moment. | |
| polygon | No | surface only. >= 3 vertices, planar and CONVEX, e.g. [[0,0,0],[6,0,0],[6,0,4],[0,0,4]]. Only members lying exactly in that plane receive load. | |
| end_frac | No | distributed only. Where it ends, 0..1 (default 1). | |
| position | No | Fraction along the member, 0 = start node, 1 = end node. Required for member_point_*. Clamped to [0,1] (the engine does not range-check it). | |
| magnitude | No | Load size, always positive — put the sense in direction_x/y/z. Force / force*length / force per length / force per area depending on kind. | |
| model_ref | No | Handle from stash_model, used INSTEAD of `model`. Avoids sending the whole document on every step; the tool returns the same handle back. | |
| projected | No | plate_pressure only. false (default) = pressure on the true area; true = on the projected area (snow-style). | |
| start_frac | No | distributed only. Where the line load starts, 0..1 (default 0). Use 0.25/0.75 for a partial UDL over the middle half. | |
| surface_id | No | plate_pressure only: id of a model.plate_surfaces[] entry. Prefer this. | |
| direction_x | No | X component of the direction (or moment axis). Default 0. | |
| direction_y | No | Y component. Gravity is -1 in these Y-up models. Default 0. | |
| direction_z | No | Z component. Default 0. At least one component must be non-zero. | |
| load_case_id | No | id of the load case to append to (from add_load_case) | |
| end_magnitude | No | distributed only. Intensity at end_frac. Omit for uniform; set it different from magnitude for a TRAPEZOID; magnitude 0 + end_magnitude w gives a TRIANGLE. There is no separate shape field — the shape IS these numbers. | |
| distribution_dx | No | surface only, optional: X of the span direction the load is carried in. | |
| distribution_dy | No | surface only, optional: Y of the span direction. | |
| distribution_dz | No | surface only, optional: Z of the span direction. | |
| plate_element_id | No | plate_pressure only: one generated element. Element ids change whenever the mesh changes — prefer surface_id. |
add_load_caseAdd a load caseRead-onlyInspect
Add an empty load case — a named bucket of UNFACTORED loads of one origin (dead, snow, wind, imposed). Put loads in it with add_load, then combine cases with partial factors using add_load_combination. A load case on its own never reaches a code check: checks envelope over COMBINATIONS only. Returns the model plus the new load_case_id. Free.
| Name | Required | Description | Default |
|---|---|---|---|
| name | Yes | Name, e.g. "G - Permanent", "S - Snow", "W - Wind (+X)". results.loadcases is keyed by this, so it must be unique. | |
| model | No | Stringified FERS model JSON | |
| model_ref | No | Handle from stash_model, used INSTEAD of `model`. Avoids sending the whole document on every step; the tool returns the same handle back. |
add_load_combinationAdd a load combinationRead-onlyInspect
Add ONE factored load combination — a weighted sum of load cases at a given limit state. This is what the solver designs against: unity checks envelope over COMBINATIONS, never over raw load cases, so a load case no combination references is invisible to every check. ALWAYS set limit_state: it is the only field of a combination the solver reads, and leaving it null makes every SLS deflection check return an empty result that reads as a pass. Free.
| Name | Required | Description | Default |
|---|---|---|---|
| name | Yes | e.g. "ULS 6.10 - leading Snow". results.loadcombinations is keyed by this. | |
| check | No | Report metadata only. Defaults to "ALL". | |
| model | No | Stringified FERS model JSON | |
| factors | Yes | Load case ids to partial factors: {"1": 1.35, "2": 1.5} or [{"load_case_id":1,"factor":1.35}]. Negative factors are legal (uplift / favourable). | |
| model_ref | No | Handle from stash_model, used INSTEAD of `model`. Avoids sending the whole document on every step; the tool returns the same handle back. | |
| situation | No | Report metadata only; the solver never reads it. | |
| limit_state | No | ULS | SLS | FLS | ALS (case-sensitive). Strongly recommended — see the tool description. |
add_plate_grid_elementsAdd an explicit plate meshRead-onlyInspect
Author plate elements EXPLICITLY (model.plate_elements) as a regular quad grid over four corner points, instead of letting the solver mesh a PlateSurface. Creates the nodes, optionally supports the perimeter, and writes one 4-node PlateElement per cell. Verified to give bit-identical results to the equivalent structured surface mesh (16x16: max|dz| 4.31048e-3 m either way).
USE THIS WHEN YOU NEED PLATE UNITY CHECKS. In engine 0.2.52 a unity check targeting plates (AllPlates / PlateSurfaces / PlateElements with a PlateStress variable) returns an EMPTY per_entity for anything meshed from a PlateSurface — status "Green", max_utilization 0, no warning — because the mesh is generated on an internal working copy while the checks run against the original document. Explicit plate elements survive in that document and are checked normally (verified 256/256 entities).
ALSO USE IT when you need stable, known plate element ids: mesh-generated ids start at max(explicit id)+1 and are never echoed back, so an element-targeted pressure on a generated id is a silent no-op.
COST: verbosity. An N x N grid emits (N+1)^2 nodes and N^2 elements inline, and the engine allocates a dense (7 x nodes)^2 block, so keep total model nodes under ~2000. Load the grid with ONE add_load kind:"surface" whose polygon covers it, rather than N^2 individual pressures. Same theory guidance as add_plate_surface: PlateTheory "Auto" resolves to Mindlin, which shear-locks (-55% at 8x8 on a span/thickness 80 slab), so prefer "kirchhoff" at span/thickness >= 20.
| Name | Required | Description | Default |
|---|---|---|---|
| model | No | Stringified FERS model JSON | |
| theory | No | auto | kirchhoff | mindlin | engine_auto. | |
| behavior | No | shell | membrane. | |
| model_ref | No | Handle from stash_model, used INSTEAD of `model`. Avoids sending the whole document on every step; the tool returns the same handle back. | |
| thickness | No | Plate thickness in metres. | |
| divisions_u | No | Cells along the first edge. | |
| divisions_v | No | Cells along the other edge. | |
| material_id | No | Existing model.materials id. | |
| plane_state | No | plane_stress | plane_strain. | |
| edge_support | No | none | pinned | fixed on the perimeter. | |
| corner_points | Yes | JSON array of exactly 4 [x,y,z] corners, in order around the panel. | |
| first_element_id | No | Starting id for the generated elements. | |
| local_x_direction | No | [x,y,z] local axis direction. |
add_plate_openingCut an opening in a plateRead-onlyInspect
Cut a hole (stairwell, duct, riser) in an existing PlateSurface: creates the hole's boundary nodes and appends a PlateOpening to that surface's openings. The hole ring must be coplanar with, and lie strictly inside, the outer boundary; elements whose centroid falls in the hole are dropped, so the applied load drops with the removed area (verified: a 1x1 m hole in a 4x4 m slab under 10 kPa took the total reaction from 160000 N to exactly 150000 N).
HARD CONSTRAINT: openings only work with the Delaunay mesher. The structured mesher rejects them outright ("PlateSurface N Structured meshing does not support openings."), so this tool switches the surface to mesh method "Unstructured" with a target_size and tells you it did. That makes the surface's edge-node positions unpredictable, which is why the tool REFUSES to convert a surface whose structured edge grid is carrying a support: those pre-created edge nodes would be stranded off the new mesh and the plate would silently fall back to hanging on its corners (measured 6x too flexible, with the load still balancing exactly). If you need both an opening and a supported edge, author the surface as unstructured from the start and support it by other means.
TARGET SIZE IS A WHOLE-SURFACE SETTING, so a SMALL hole is the expensive case, not the cheap one: left to default (half the shortest hole edge) a 200 mm duct in a 6 m x 6 m slab asks for ~6100 mesh nodes and the engine aborts on that with a raw wasm trap. The default is therefore clamped to what the node budget allows — which means the hole is resolved coarsely and its stress concentration is NOT converged; model the region around the opening as its own smaller surface if you need it sharp. An explicit target_size that busts the budget is refused rather than clamped.
| Name | Required | Description | Default |
|---|---|---|---|
| model | No | Stringified FERS model JSON | |
| model_ref | No | Handle from stash_model, used INSTEAD of `model`. Avoids sending the whole document on every step; the tool returns the same handle back. | |
| opening_id | No | Overwrite this opening id. | |
| surface_id | No | id of the plate surface to cut the hole in. | |
| target_size | No | Mesh size around the hole, metres. Openings force the Delaunay mesher. | |
| corner_points | No | JSON array of [x,y,z] points bounding the hole, inside the surface and in its plane. | |
| boundary_node_ids | No | Existing node ids bounding the hole instead. |
add_plate_surfaceAdd a plate or slabRead-onlyInspect
Add a meshed plate/shell surface (slab, wall, deck, panel) to a FERS model. You give the corner points; the tool creates the boundary nodes, optionally the full edge-node grid with a support, and appends the PlateSurface to model.plate_surfaces. The SOLVER meshes it at solve time — never author plate_elements for a surface yourself. Load it with add_load kind:"plate_pressure" (surface_id), not with distributed_loads.
MESHING DECIDES WHETHER YOUR MODEL IS CORRECT. The mesher only ever creates NODES and PLATE ELEMENTS: it never splits members and never adds supports, so a plate connects to the rest of the structure ONLY through nodes it shares by exact coordinate. (1) Leave the mesh unset and the surface becomes TWO TRIANGLES spanning the corners — it still solves and reactions still equal q x A, so nothing looks wrong; this tool rejects that instead. (2) A supported or member-connected EDGE only exists where a node sits at an exact mesh-grid coordinate: structured meshing has predictable positions so edge_support can pre-create them, unstructured/Delaunay does not, so edge_support with unstructured meshing is rejected. (3) If a beam runs along a plate edge, split that beam into segments ending on the grid, or the plate hangs off its two end nodes only (measured 4.2x too flexible, beam moment 2.5x too low) with no warning.
THEORY — THE ENGINE DEFAULT IS WRONG FOR THIN PLATES. PlateTheory "Auto" is resolved unconditionally to Mindlin (the OpenAPI text claiming otherwise is stale). Mindlin (DSG3) shear-locks: on a simply-supported square slab at span/thickness 80 the centre-deflection error was -86.1% / -55.2% / -8.1% / -0.4% at 4x4 / 8x8 / 16x16 / 32x32, where Kirchhoff (DKT, rewritten and patch-test-exact in 0.2.44) gave -5.5% / -1.4% / -0.3% / -0.03%. So: span/thickness >= 20 -> "kirchhoff"; span/thickness < 10 -> "mindlin" plus a fine mesh (it correctly adds the ~10% shear deflection Kirchhoff misses). theory:"auto" (the default) makes that choice from the SMALLER IN-PLANE DIMENSION of your boundary (not its shortest edge, which collapses the moment an edge carries intermediate nodes) and tells you which it picked and why.
BOUNDARY POINTS: give the ring OPEN. A closing point that repeats the first is dropped for you (and said so in the notes); any other repeated point is refused, because coincident points collapse onto one node and leave boundary_node_ids with a duplicate id — measured, that solves with no error, balances the load to exactly q x A, and is 513% wrong on deflection.
ALSO TRUE OF EVERY PLATE: plates carry NO self-weight (enable_self_weight loops over members only) — add density x thickness x g as a pressure. Plates contribute NO geometric stiffness, so alpha_cr from a model where plate stability governs is unconservative. behavior "membrane" does not remove bending stiffness, it scales it by 1e-6, so an out-of-plane load on a membrane plate returns absurd deflections rather than an error. Boundary point ORDER sets the element normal and therefore the sign of mx/my/mxy. Keep total model nodes under ~2000: the engine allocates a dense (7 x nodes)^2 block and aborts above that with a raw wasm trap, so both meshers are rejected here once the projected node count crosses it (Delaunay produces ~1.7 nodes per target_size^2 of area, measured, so target_size 0.1 on a 4 m x 4 m slab is already fatal).
| Name | Required | Description | Default |
|---|---|---|---|
| name | No | Label for the surface. | |
| model | No | Stringified FERS model JSON | |
| offset | No | Offset of the plate mid-surface, metres. | |
| theory | No | auto (default: picks from span/thickness) | kirchhoff (best for thin plates) | mindlin (shear-flexible but coarse-mesh stiff) | engine_auto. | |
| behavior | No | shell (default) | membrane. | |
| model_ref | No | Handle from stash_model, used INSTEAD of `model`. Avoids sending the whole document on every step; the tool returns the same handle back. | |
| thickness | No | Plate thickness in metres. Required, > 0. | |
| surface_id | No | Overwrite this surface id instead of allocating a new one. | |
| divisions_u | No | Structured mesh divisions along the first edge. Use >= 8 for Kirchhoff, >= 16-32 for Mindlin. | |
| divisions_v | No | Divisions along the other edge. Omit one and it is inferred from the edge-length ratio. | |
| material_id | No | Existing model.materials id. Defaults to the first material. | |
| mesh_method | No | auto | structured | unstructured (Delaunay — required for openings). | |
| plane_state | No | plane_stress (default) | plane_strain. | |
| target_size | No | Element edge length for the unstructured mesher, in metres. | |
| edge_support | No | none (default) | pinned | fixed — supports the generated perimeter nodes. | |
| corner_points | No | JSON array of [x,y,z] corner points forming an OPEN planar ring (do not repeat the first point). At least 3; exactly 4 for the structured mesher. Give this OR boundary_node_ids. | |
| element_shape | No | auto | triangle | quad. | |
| classification | No | Free-text group name. | |
| edge_support_id | No | Reuse an existing nodal_supports id for the edge. | |
| boundary_node_ids | No | JSON array of EXISTING node ids to hang the plate off, e.g. [1,3,8,6]. | |
| local_x_direction | No | [x,y,z] setting the plate's local axes, which fix the sign and meaning of mx/my/mxy. Defaults to the first edge. | |
| stiffness_modifier_shear | No | Multiplier on shear stiffness. | |
| stiffness_modifier_bending | No | Multiplier on bending stiffness (0.5 doubles deflection). | |
| stiffness_modifier_membrane | No | Multiplier on membrane stiffness. |
add_unity_checkAdd a code checkRead-onlyInspect
Append a unity (code) check to a model's analysis.unity_checks. check is a UnityCheckDefinition JSON (see get_check_schema). Returns the updated model JSON to pass to run_unity_checks. Free.
| Name | Required | Description | Default |
|---|---|---|---|
| check | Yes | Stringified UnityCheckDefinition JSON (get its schema + an example from get_check_schema) | |
| model | No | Stringified FERS model JSON | |
| model_ref | No | Handle from stash_model, used INSTEAD of `model`. Avoids sending the whole document on every step; the tool returns the same handle back. |
build_modelBuild a model from a node/member graphRead-onlyIdempotentInspect
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.
| Name | Required | Description | Default |
|---|---|---|---|
| loads | No | JSON 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. | |
| nodes | Yes | JSON 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. | |
| order | No | "LINEAR" (default) or "NONLINEAR" for second-order. | |
| plane | No | "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. | |
| members | Yes | JSON 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. | |
| material | No | steel_S355 (default), steel_S235, aluminium_6061 or concrete_C30. | |
| sections | No | JSON array of section definitions (same shape as create_section), referenced by name from the member rows. | |
| supports | No | JSON 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_weight | No | Add self-weight as its own load case and factor it into the combinations. | |
| project_name | No | Label for the model. | |
| default_section | No | Section for members that name none. Default IPE300. | |
| load_combinations | No | JSON 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. |
check_beamCheck a steel beam (EN 1993-1-1)Read-onlyInspect
Check a steel beam to EN 1993-1-1 (Eurocode 3). Builds the beam + a ULS load combination + an EC3 member check over one member, solves it, and returns the governing utilization (<= 1.0 = OK) with the per-rule breakdown: bending, shear, combined N+M, and lateral-torsional buckling, plus a short advice string. Requires a named library section: European (IPE/HEA/HEB/HEM/UPE/RHS/SHS/CHS), UK (UB/UC/PFC) or US (W/HP/HSS/Pipe). Accepts common parameter aliases (span/length for span_m, load_kN_per_m/uniform_load for udl). Set restrained=true if the compression flange is continuously laterally restrained (skips the LTB check). On this hosted server the solve does NOT run here: the tool validates the model, parks it and returns an open_url that solves it in the user's browser, where the results are shown. Tell the user to open the link; for results inside the chat they can install the local FERS MCP server. Units: m, kN, kN/m.
| Name | Required | Description | Default |
|---|---|---|---|
| udl | No | Uniformly distributed load in kN/m, downward | |
| span | No | Alias for span_m (beam span in metres) | |
| length | No | Alias for span_m (beam span in metres) | |
| span_m | No | Beam span in metres (required — or use alias `span`/`length`) | |
| section | Yes | Library section name, e.g. 'IPE300', 'HEA200', 'UB 457x191x67', 'W10X22' (required). Carries the EC3 moduli. | |
| support | No | 'simply_supported' (default), 'cantilever', or 'fixed' (fixed–fixed) | |
| material | No | steel_S235 (default), steel_S275, steel_S355 or steel_S460 (shorthand S235/S275/S355/S460 ok) | |
| point_load | No | Point load in kN, downward | |
| restrained | No | true if the compression flange is continuously laterally restrained (e.g. by decking or a slab) — the LTB (§6.3.2) check is then skipped. Default false. | |
| uls_factor | No | ULS load factor applied to the load (default 1.35) | |
| uniform_load | No | Alias for udl (uniformly distributed load in kN/m) | |
| load_kN_per_m | No | Alias for udl (uniformly distributed load in kN/m) | |
| point_position | No | Point-load position along the span, 0..1 (default mid-span; tip for a cantilever) | |
| laterally_restrained | No | Alias for restrained |
create_beamBuild a beam modelRead-onlyIdempotentInspect
Generate a solver-ready FERS model JSON for one beam. Use a named library section (IPE300, HEA200, 'RHS 100x50x5', 'UB 457x191x67', 'W10X22', …) or a solid rectangle. Returns model JSON to pass to solve_model. Accepts common parameter aliases (span/length for span_m, load/udl/uniform_load for load_kn; point_load implies load_type 'point'). Units: metres, kN, kN/m. The model carries two load combinations: 1 'SLS characteristic (auto)' at factor 1.0 and 2 'ULS (auto)' at 1.35, so ULS unity checks evaluate factored loads. Free.
| Name | Required | Description | Default |
|---|---|---|---|
| udl | No | Alias for load_kn as a UDL in kN/m | |
| load | No | Alias for load_kn (kN/m as a UDL, kN as a point load) | |
| span | No | Alias for span_m (beam span in metres) | |
| length | No | Alias for span_m (beam span in metres) | |
| span_m | No | Beam span in metres (required — or use alias `span`/`length`) | |
| load_kn | No | UDL in kN/m or point load in kN, downward (default 10) | |
| section | No | Library section name, e.g. 'IPE300', 'HEA200', 'RHS 100x50x5', 'UB 457x191x67', 'W10X22'. Overrides height_mm/width_mm. | |
| support | No | 'simply_supported' (default) or 'cantilever' | |
| material | No | steel_S235 (default), steel_S275, steel_S355, steel_S460, aluminium_6061 or concrete_C30 (shorthand S235/S275/S355/S460 ok) | |
| width_mm | No | Section width b in mm (default 100) | |
| height_mm | No | Rectangular section depth h in mm (default 200; ignored if `section` is set) | |
| load_type | No | 'udl' (default) or 'point' | |
| point_load | No | Point load in kN, downward (implies load_type 'point') | |
| uniform_load | No | Alias for load_kn as a UDL in kN/m | |
| load_position | No | Alias for load_position_frac (0..1 along the span) | |
| point_position | No | Alias for load_position_frac (0..1 along the span) | |
| load_position_frac | No | Point-load position along the span, 0..1 (default 0.5; 1.0 for cantilever) |
create_frameBuild a portal frameRead-onlyIdempotentInspect
Generate a solver-ready FERS model JSON for a 2-D rectangular portal frame (columns + beams). Returns model JSON to pass to solve_model. Units: metres, kN, kN/m. Columns are turned 90° so their depth (column_height_mm) resists sway in the frame plane. Load combinations as create_beam: 1 'SLS characteristic (auto)' at 1.0, 2 'ULS (auto)' at 1.35. Free.
| Name | Required | Description | Default |
|---|---|---|---|
| base | No | 'fixed' (default) or 'pinned' column bases | |
| bays | No | Number of bays (default 1) | |
| storeys | No | Number of storeys (default 1) | |
| material | No | steel_S355 (default), steel_S235, steel_S275, steel_S460, aluminium_6061 or concrete_C30 | |
| lateral_kn | No | Horizontal point load at the top-left corner, kN (default 0) | |
| bay_width_m | No | Bay width in metres (default 5) | |
| beam_width_mm | No | Beam section width in mm (default 120) | |
| beam_height_mm | No | Beam section depth in mm (default 240) | |
| column_width_mm | No | Column section width in mm (default 200) | |
| storey_height_m | No | Storey height in metres (default 3) | |
| column_height_mm | No | Column section depth in mm (default 200) | |
| roof_udl_kn_per_m | No | Gravity UDL on the top-storey beams, kN/m (default 10) |
create_sectionAdd a cross-sectionRead-onlyInspect
Add or replace a cross-section on an existing FERS model and optionally assign it to members. Takes the model JSON, mutates model.sections (and model.materials when a new preset is introduced), returns the model.
THREE WAYS TO DEFINE IT:
LIBRARY — shape:"library" with library_name (or just
name) set to a library profile — European IPE/HEA/HEB/HEM/UPE/RHS/SHS/CHS/L or UK UB/UC/PFC, e.g. "IPE300", "HEB240", "CHS 114.3x5", "RHS 100x50x5", "UB 457x191x67". This is the ONLY way to get exact rolled-profile properties (root fillets included) and it carries every EC3 modulus. Prefer it whenever a standard profile fits.PARAMETRIC — shape:"rect" (h_mm, b_mm), "circle" (d_mm), "chs" (d_mm, t_mm), "rhs"/"shs"/"box" (h_mm, b_mm, t_mm) or "i" (h_mm, b_mm, tf_mm, tw_mm), dimensions in MILLIMETRES. rect/circle/chs are exact and rhs area+inertias are exact (its Bredt torsion J is ~2% low), but the "i" generator assumes SHARP fillets: against a real rolled IPE300 it is 3.6% low on area, 4.3% low on i_z and 22% low on J. Use "i" for WELDED plate girders, not for rolled profiles.
CUSTOM — shape:"custom" with your own SI numbers. area, i_y, i_z and j are all MANDATORY and strictly positive; a missing one is a hard parse error and a MISTYPED one is silently ignored (Section has no deny_unknown_fields), giving a wrong answer that still solves.
AXIS CONVENTION: local z is the STRONG axis. For depth h and width b, i_z = b·h³/12 (strong, resists gravity on a horizontal member) and i_y = h·b³/12 (weak). Getting these the wrong way round is the most common authoring error and the solver cannot detect it.
EC3 CHECKS NEED MORE THAN STIFFNESS. An Ec3Steel check on a section lacking wel_y/wpl_y/i_w does not fail — it returns utilization 0.0, status Green and an explanatory message, which reads as a clean pass. So also give wel_y, wel_z, wpl_y, wpl_z and i_w (warping constant, m⁶); the parametric shapes compute these for you. Optionally add ec3 {"section_class":1,"buckling_curve_y":"B","buckling_curve_z":"C","buckling_curve_lt":"B"} — curves A0/A/B/C/D (alpha 0.13/0.21/0.34/0.49/0.76); omitting the block silently uses curve b. Unlike the rest of the section, the ec3 block DOES reject unknown keys, so a typo there is a hard solve-time error.
ASSIGNMENT: apply_to "all" reassigns every member, a JSON array of member ids reassigns those, a classification string reassigns every member in that group. A section whose name already exists is overwritten IN PLACE, keeping its id and all existing member references.
| Name | Required | Description | Default |
|---|---|---|---|
| j | No | Torsion constant, m⁴. | |
| ec3 | No | EN 1993-1-1 design parameters: {"section_class":1,"buckling_curve_y":"a","buckling_curve_z":"b","buckling_curve_lt":"c"}. Unlike the rest of Section, a typo here IS a hard error. | |
| i_w | No | Warping constant, m⁶. | |
| i_y | No | Second moment about local y, m⁴. | |
| i_z | No | Second moment about local z (STRONG axis), m⁴. | |
| a_sy | No | Shear area along local y, m². | |
| a_sz | No | Shear area along local z, m². | |
| area | No | Area in m² (required for shape 'custom'). | |
| b_mm | No | Width in MILLIMETRES. | |
| d_mm | No | Diameter in MILLIMETRES (circle/chs). | |
| h_mm | No | Depth in MILLIMETRES. | |
| name | Yes | Name for the section in this model. | |
| t_mm | No | Wall thickness in MILLIMETRES. | |
| model | No | Stringified FERS model JSON | |
| shape | No | library (default when library_name is given) | rect | circle | chs | rhs | shs | box | i | custom. Prefer "library" for a standard profile: the parametric "i" generator has sharp fillets and is ~4% low on area and ~22% low on J versus a real rolled IPE300. | |
| tf_mm | No | Flange thickness in MILLIMETRES. | |
| tw_mm | No | Web thickness in MILLIMETRES. | |
| wel_y | No | Elastic modulus about y, m³. | |
| wel_z | No | Elastic modulus about z, m³. | |
| wpl_y | No | Plastic modulus about y, m³. | |
| wpl_z | No | Plastic modulus about z, m³. | |
| apply_to | No | "all", a JSON array of member ids, or a classification string — assigns the section to those members. | |
| material | No | Material preset for this section (see list_materials). | |
| model_ref | No | Handle from stash_model, used INSTEAD of `model`. Avoids sending the whole document on every step; the tool returns the same handle back. | |
| library_name | No | Library section name, e.g. IPE300 (see list_sections). |
generate_load_combinations_ec0Generate EN 1990 combinationsRead-onlyInspect
Generate a complete EN 1990 (Eurocode 0) combination set from the model's load cases in one call, instead of adding them one at a time. Classify each load case as an action type — G permanent, Q imposed, S snow, W wind — and this emits ULS expression 6.10 (each variable action taken as leading in turn, with psi_0 on the accompanying ones) and the SLS characteristic combinations, including the favourable-permanent (gamma_G = 1.0) variants that govern wind uplift. Every generated combination carries a limit_state, so unity checks actually evaluate against them. Any load case you do not classify is guessed from its name. Free.
| Name | Required | Description | Default |
|---|---|---|---|
| model | No | Stringified FERS model JSON | |
| actions | Yes | Action type per load case: {"1":"G","2":"S","3":"W"} or [{"load_case_id":1,"action":"G"}]. G = permanent, Q = imposed, S = snow, W = wind. Unclassified cases are guessed from their name. | |
| model_ref | No | Handle from stash_model, used INSTEAD of `model`. Avoids sending the whole document on every step; the tool returns the same handle back. | |
| include_sls | No | Emit SLS characteristic combinations (default true). | |
| include_uls | No | Emit ULS 6.10 combinations (default true). | |
| replace_existing | No | Replace analysis.load_combinations entirely instead of appending (default false). | |
| include_favorable | No | Emit the gamma_G = 1.0 favourable-permanent ULS variants, which govern uplift (default true). |
get_check_schemaCode-check schemaRead-onlyIdempotentInspect
Get the canonical schema for authoring unity (code) checks: the UnityCheckDefinition schema and everything it references, read straight from the solver's OpenAPI, plus a worked EN 1993 example. Call this before add_unity_check. Free.
| Name | Required | Description | Default |
|---|---|---|---|
No parameters | |||
get_schemaLook up the model schemaRead-onlyIdempotentInspect
Look up the FERS model input contract — the authoritative JSON Schema the solver itself is generated from. Call with NO arguments to get an index of the useful roots (geometry, loads, analysis, checks) with a one-line description of each; then call again with a root to get that schema plus everything it references. Use this instead of guessing field names: the engine silently ignores unknown keys under analysis.*, so a typo becomes a wrong answer rather than an error. Free.
| Name | Required | Description | Default |
|---|---|---|---|
| root | No | Schema to expand, e.g. 'BucklingAnalysisSettings', 'MemberHinge', 'LoadCase', 'NodalSupport', 'Model'. Omit for the index. |
get_sectionSection propertiesRead-onlyIdempotentInspect
Get a named section's full properties in SI units (m, m², m⁴): area, second moments i_y/i_z, torsion constant j, warping constant i_w, shear areas, and the elastic/plastic moduli wel_y/wel_z/wpl_y/wpl_z that an EN 1993-1-1 check needs. Free.
| Name | Required | Description | Default |
|---|---|---|---|
| name | Yes | Section name, e.g. 'IPE300', 'HEA 200', 'RHS 100x50x5' |
get_templateGet a starter modelRead-onlyIdempotentInspect
Retrieve the full JSON of a template structural model by name. Use list_templates first to see available names.
| Name | Required | Description | Default |
|---|---|---|---|
| name | Yes | Template name (e.g. 'simple_shed', 'solar_rail') |
get_usageCheck usageRead-onlyIdempotentInspect
Check your FERS tier and how solves run on this server: member limits, licence state and where results come from. With sandbox_token: true, a Pro account also gets a solve token for 15 minutes, for a code sandbox that cannot reach ferscloud.com: set it as FERS_SOLVE_TOKEN there (or run_analysis(solve_token=...), FERS 0.2.0+) to solve beyond 100 members. It sits in the transcript, so ask only when a model needs it.
| Name | Required | Description | Default |
|---|---|---|---|
| sandbox_token | No | true: also return a 15-minute Pro solve token for a code sandbox |
list_materialsList materialsRead-onlyIdempotentInspect
List the named material presets the model builders accept, with their E, G, density and yield stress in SI units. The solver itself accepts ANY isotropic material — see get_schema('Material') to author one inline. Free.
| Name | Required | Description | Default |
|---|---|---|---|
No parameters | |||
list_modelsList saved modelsRead-onlyIdempotentInspect
List your saved models (id, name, description, last-updated time).
| Name | Required | Description | Default |
|---|---|---|---|
No parameters | |||
list_sectionsList steel sectionsRead-onlyIdempotentInspect
List the named steel sections FERS knows: European (IPE, HEA, HEB, HEM, UPE, RHS, SHS, CHS, L), UK (UB, UC, PFC) and US (W, HP, HSS, Pipe). check_beam and create_beam require one of these names, so call this rather than guessing. Returns names only — use get_section for a section's properties. Free.
| Name | Required | Description | Default |
|---|---|---|---|
| family | No | Filter by family/prefix, e.g. 'IPE', 'HEA', 'RHS'. Case-insensitive. Omit for all families. |
list_templatesList starter modelsRead-onlyIdempotentInspect
List all available template structural models. Returns name and description for each template.
| Name | Required | Description | Default |
|---|---|---|---|
No parameters | |||
load_modelLoad a saved modelRead-onlyIdempotentInspect
Load one of your saved models' full JSON by id (owner only).
| Name | Required | Description | Default |
|---|---|---|---|
| id | Yes | Saved model id |
run_unity_checksRun code checksRead-onlyInspect
Solve a model and return its code-check (unity-check) results: per-entity utilizations, status, and the calculation trace behind each rule. The model's analysis.unity_checks must already be defined (see add_unity_check). On this hosted server the solve does NOT run here: the tool validates the model, parks it and returns an open_url that solves it in the user's browser, where the results are shown. Tell the user to open the link; for results inside the chat they can install the local FERS MCP server. Set include_report=true to also get the rendered HTML hand-calc report.
| Name | Required | Description | Default |
|---|---|---|---|
| model | No | Stringified FERS model JSON that includes analysis.unity_checks | |
| detail | No | How much result data to return. 'summary' (peak forces, displacements and reactions with where they occur — smallest, usually enough), 'standard' (default: envelopes + node displacements + reactions), or 'full' (adds section forces and the complete internal-force series; can be hundreds of thousands of tokens on a real model). | |
| model_ref | No | Handle from stash_model, used INSTEAD of `model`. Avoids sending the whole document on every step; the tool returns the same handle back. | |
| result_filter | No | Advanced: emit only these result blocks, overriding `detail`. Any of: local_envelopes, global_envelopes, local_end_forces, global_end_forces, section_forces, internal_force_series, local_displacements, node_displacements, reactions. An empty list is a maximal strip. Unity checks, summaries and diagnostics are never filtered. | |
| include_report | No | Embed the rendered HTML hand-calc report in the response. Default false — it is large. Use the free render_report tool on a saved result instead. | |
| idempotency_key | No | Optional. Reuse when retrying so the retry is recognised as the same request. |
save_modelSave a model to your accountInspect
Save a model to your FersCloud account (requires a Pro subscription; free accounts use local JSON export/import instead). Solver results are never stored. Returns the model id.
| Name | Required | Description | Default |
|---|---|---|---|
| name | Yes | A name for the saved model | |
| model | No | Stringified FERS model JSON | |
| model_ref | No | Handle from stash_model, used INSTEAD of `model`. Avoids sending the whole document on every step; the tool returns the same handle back. | |
| description | No | Optional description |
set_analysisConfigure the analysisRead-onlyIdempotentInspect
Configure HOW a model is solved, and switch on the analyses the engine supports beyond plain statics. Writes analysis.options (first- vs second-order, P-Delta, self-weight, gravity, tolerances) and the three optional blocks: analysis.modal (natural frequencies), analysis.buckling (linear eigenvalue alpha_cr) and analysis.seismic (EN 1998-1 response spectrum / lateral force). Pass only what you want to change — each sub-object is merged into the existing block, so load cases, combinations and unity checks are preserved. Returns the updated model for solve_model. KEY FACTS: models from create_beam, create_frame and build_model are LINEAR (the engine itself has no default and requires order; the Python SDK defaults to NONLINEAR), so set order:"NONLINEAR" for second-order/P-Delta. Buckling needs exactly one reference form (reference / references / all_combinations) and reports alpha_cr as results.buckling.modes[].critical_load_factor. Seismic directions are GLOBAL AXES and these models are Y-up, so the two horizontal directions are usually X and Z. Unknown field names are REJECTED here on purpose: the solver would accept them, ignore them, and silently give you a default. Call get_schema("BucklingAnalysisSettings") etc. for the exact fields.
| Name | Required | Description | Default |
|---|---|---|---|
| clear | No | Optional JSON array of blocks to delete first, e.g. ["modal","buckling"]. | |
| modal | No | ModalAnalysisSettings, e.g. {"num_modes":6,"mass_formulation":"CONSISTENT"}. Pass "null" to remove the block. | |
| model | No | Stringified FERS model JSON to update | |
| options | No | AnalysisOptions fields to merge, as a JSON object. e.g. {"order":"NONLINEAR","nonlinear_method":"P_DELTA","enable_self_weight":true}. See get_schema("AnalysisOptions"). | |
| seismic | No | SeismicAnalysisSettings, e.g. {"method":"MODAL_RESPONSE_SPECTRUM","num_modes":30,"directions":["X","Z"],"spectrum_x":{"EurocodeParametric":{"ag":2.45,"ground_type":"C","spectrum_type":"TYPE1","q":3.0,"beta":0.2}},"mass_sources":[{"load_case_id":1,"psi":1.0}]}. Pass "null" to remove. | |
| buckling | No | BucklingAnalysisSettings, e.g. {"num_modes":3,"reference":{"LoadCombination":1},"member_effective_lengths":true}. Needs exactly one of reference / references / all_combinations. Pass "null" to remove. | |
| model_ref | No | Handle from stash_model, used INSTEAD of `model`. Avoids sending the whole document on every step; the tool returns the same handle back. |
set_memberSet member behaviourRead-onlyInspect
Set structural behaviour on one or more existing members: element type, end releases (hinges), rigid end offsets, cross-section rotation/mirroring and cable parameters. Mutates model.members (and model.member_hinges when a release is authored) and returns the model.
member_type: Normal (full 6-DOF beam), Truss (axial only — carries exactly zero moment), Tension (tie that drops out in compression), Compression (strut that drops out in tension), Rigid (near-rigid link; produces NO entry in results.member_results at all) or Cable (analytic elastic catenary; forces the whole solve onto the nonlinear path and only sags when analysis.options.enable_self_weight is true). Tension/Compression drop-out is governed by analysis.options.axial_slack in FORCE units — a member switches off when its axial force passes 0.5*axial_slack on the wrong side — so this tool lowers axial_slack to 1e-3 when it writes one (set keep_axial_slack to skip). With a coarse axial_slack the member never drops out and quietly behaves like a plain Truss. To build a truss use member_type Truss: two double-hinged Normal members meeting at a free node give a singular stiffness matrix.
END RELEASES ARE A TRISTATE and that is the whole point: for each release DOF, null (or simply not naming it) = RIGID, 0.0 = FULLY RELEASED, and a positive number = a spring of that stiffness (force/length for vx/vy/vz, force*length/rad for mx/my/mz/warp). Release DOFs are in the MEMBER LOCAL frame. Verified on an encastre beam under UDL: mz null gives support moment wL^2/12 and midspan wL^2/24; mz 0.0 gives 0 and wL^2/8, i.e. exactly simply supported. Each release object fully defines that end's hinge — DOFs you do not name are written as rigid — and passing "rigid" clears the release. A rotational release is rejected when it would leave a node with NO rotational stiffness at all, because that is a hard "Stiffness matrix is singular or near-singular": either a dead-end node (no other member frames in and the support does not restrain it) or a node where every member meeting there releases the same DOF. Note the asymmetry the check has to respect — an absent RX/RY/RZ on a support is read as FIXED, but an absent warping_condition really is free, so a plain pinned support does NOT hold a warp release. Releasing the same vx, vy, vz or mx at BOTH ends of one member is refused outright: that one is fatal at the element level whatever the model looks like ("apply_end_releases_to_local_beam_k: interface block is singular"), while my/mz/warp at both ends is just an ordinary pin-pin beam.
A release object may also carry rotation_axes: "Global" or {"User":{"x":[...],"y":[...]}} takes mx/my/mz, their curves and their moment-rotation diagrams about those axes instead of the member's own — a beam at an angle in plan, pinned about the axis of the girder it hangs from, needs {"mx":0.0,"rotation_axes":"Global"}. A user frame's first axis runs along x and its second along y made square to it. Translations stay member-local, member results stay in member axes, and the axes stay fixed in space on the corotational path. A model whose hinges release in other axes is written with schema_version 3 (engine 0.2.68), so an older engine refuses it instead of solving the release about the member's axes.
MemberHinge also carries seven capacity fields (max_tension_*, max_moment_*, max_bimoment_warp). They are accepted by the schema and carried through the model but are NEVER ENFORCED by the solver: nothing limits a hinge force by them and they trigger no nonlinearity. Do not author "yielding" hinges with them — this tool does not write them.
Offsets (start_offset/end_offset) are rigid arms between the node and the member work point in the GLOBAL frame, in model length units. rotation_angle_deg rolls the cross-section about the member axis in DEGREES, not radians: on a vertical column FERS builds the local frame from global Z, so sway along global X is resisted by the WEAK axis i_y unless you rotate 90 degrees (verified 13.8x difference on a cantilever column, with no warning from the solver). pretension is valid ONLY on Cable/Tension/Compression (a hard validation error elsewhere) and pins a tie permanently active; unstretched_length only does anything on a Cable and is silently ignored everywhere else. Sign convention for reading results back: local_start_forces.fx is COMPRESSION-POSITIVE.
| Name | Required | Description | Default |
|---|---|---|---|
| model | No | Stringified FERS model JSON | |
| mirror | No | Mirror the cross-section. | |
| model_ref | No | Handle from stash_model, used INSTEAD of `model`. Avoids sending the whole document on every step; the tool returns the same handle back. | |
| end_offset | No | Same, at the end. | |
| member_ids | Yes | JSON array of member ids, e.g. [1,2,3], or the string "all". | |
| pretension | No | Cable / Tension / Compression members only. | |
| end_release | No | Same, at the END node. | |
| member_type | No | Normal | Truss (pin-jointed, axial only) | Tension (drops out in compression) | Compression | Rigid | Cable. | |
| start_offset | No | Rigid arm at the start, GLOBAL frame, model length units: {"X":0,"Y":0.2,"Z":0}. null removes it. | |
| start_release | No | End release at the START node, in the MEMBER LOCAL frame: {"mz":0.0} releases the major-axis moment. CAREFUL: an unnamed DOF or null means RIGID, 0.0 means FULLY RELEASED, and a positive number is a rotational spring (N·m/rad). Add "rotation_axes":"Global" (or {"User":{"x":[...],"y":[...]}}) to take mx/my/mz about other axes. Use "rigid" to clear. | |
| keep_axial_slack | No | Leave analysis.options.axial_slack untouched. | |
| rotation_angle_deg | No | Roll the cross-section about the member axis, in DEGREES. Use 90 on the columns of an XY-plane frame. | |
| unstretched_length | No | Cable members only. |
set_member_designSet buckling / design dataRead-onlyInspect
Set the design (code-check) data that lives on a MemberSet — buckling restraints, buckling lengths, LTB length, effective-length factors — plus the per-member chi override.
THESE FIELDS CHANGE NOTHING IN THE STATICS. Displacements, reactions and member forces come back bit-identical; the values only feed the EN 1993-1-1 unity-check evaluator. If the model has no Ec3Steel unity check and a matching load combination you will see no effect at all anywhere in the results, so add one with add_unity_check first.
A MemberSet is one physical beam: the ordered chain of collinear FE members that make it up. Design lengths resolve per axis with this precedence: explicit buckling_length_* > effective_length_factor_* > buckling_restraints spacing > member length. WATCH THE TWO BASELINES — an explicit length and a restraint spacing are measured along the WHOLE chain, but effective_length_factor_y/z multiplies the length of the INDIVIDUAL FE member. Verified on a 6 m column split into 4 x 1.5 m members: buckling_length = 6.0 reproduces the default exactly, buckling_length = 12.0 quarters N_cr, but effective_length_factor = 2.0 gives L_cr = 3.0 m (2 x 1.5 m), not 12 m.
buckling_restraints are named by the member LOCAL axis they hold (restrains_local_y / restrains_local_z / restrains_torsion), not strong/weak — which one is strong depends on the section orientation and rotation_angle. A restraint's node_id must actually lie on one of the set's own members or the whole model fails validation.
chi is a per-MEMBER override of the EN 1993-1-1 flexural-buckling reduction factor. It replaces the section-curve chi for BOTH flexural axes (the slenderness stays physical) and is written onto every member of the targeted sets; pass "clear" to remove it.
| Name | Required | Description | Default |
|---|---|---|---|
| chi | No | EN 1993-1-1 reduction factor override in (0,1], written onto every member of the set. | |
| model | No | Stringified FERS model JSON | |
| model_ref | No | Handle from stash_model, used INSTEAD of `model`. Avoids sending the whole document on every step; the tool returns the same handle back. | |
| ltb_length | No | Lateral-torsional buckling length. | |
| classification | No | Alternative selector: exact match on the set's classification. | |
| member_set_ids | Yes | JSON array of member-set ids, or "all". Give this or `classification`. | |
| buckling_length_t | No | Torsional buckling length. | |
| buckling_length_y | No | L_cr about local y, in model length units, measured along the whole member chain. | |
| buckling_length_z | No | L_cr about local z. | |
| set_classification | No | Rename the set's classification. | |
| buckling_restraints | No | [{"node_id":3,"restrains_local_y":true,"restrains_local_z":true,"restrains_torsion":true}]. Replaces the set's list; nodes must lie on the set's own members. [] clears them. | |
| effective_length_factor_y | No | K about y — multiplies the INDIVIDUAL element's length, not the chain's. | |
| effective_length_factor_z | No | K about z, same caveat. |
set_scissor_hingeLet a continuous beam turn on what it crossesRead-onlyInspect
Let a continuous beam turn or slide on the members it crosses (a scissor hinge, engine 0.2.68), with no link member or extra node: a rail resting on beams, a bracing pair crossing at a bolt. Name the beam by member_ids (or an existing member_set_id) and give the releases. Returns the model.
releases is an object over tx, ty, tz (translations) and rx, ry, rz (rotations): 0.0 FREES that axis, a positive number is a spring between the beam and the node (force/length for t*, forcelength/rad for r), and an axis you do not name is HELD — the same tristate as a member hinge. {"rz":0.0} lets a beam in the XY plane turn on what it crosses; {"tx":0.0} lets it slide along X.
The hinge goes on a MEMBER SET and applies wherever one of the set's members meets a member OUTSIDE the set (or a plate). Inside the set the members stay joined to each other, so the beam stays continuous. A set holding every member meets nothing and the solver refuses it, so when no set holds exactly these members they leave their sets for a new one; info lists the sets they were taken from. at_nodes limits the hinge to those nodes of the beam (a rail that slides over its first post only); omit it for every crossing.
axes: "Global" (the default) or {"User":{"x":[...],"y":[...]}} for a rack turned in plan — the first axis runs along x, the second along y made square to it. "Local" is refused: the set's members need not share one.
Supports and nodal loads at a released node act on the members outside the set, and a node's reported displacement is theirs; the beam's own motion there is in its members' local end displacements. The solver refuses a member point load or moment exactly at a released end of the beam. In second order, a model with scissor hinges takes the corotational correction only when analysis.options.nonlinear_method is COROTATIONAL. The model is written with schema_version 3, so an engine older than 0.2.68 refuses it instead of solving every crossing rigid. scissor_hinge_id reuses (and with releases or axes, rewrites) one definition for several beams; clear:true takes the hinge off the beam's set.
| Name | Required | Description | Default |
|---|---|---|---|
| axes | No | "Global" (default) or {"User":{"x":[1,0,0],"y":[0,1,0]}} for a frame turned in plan. "Local" is refused. | |
| clear | No | Take the scissor hinge off the beam's member set. | |
| model | No | Stringified FERS model JSON | |
| at_nodes | No | JSON array of the beam's node ids where the hinge applies; omit for every node where the beam meets another member. | |
| releases | No | Axes to release: {"rz":0.0} frees rotation about global Z, {"tx":0.0} lets the beam slide along X, a positive number is a spring (force/length for tx/ty/tz, force*length/rad for rx/ry/rz). An axis not named is HELD. | |
| model_ref | No | Handle from stash_model, used INSTEAD of `model`. Avoids sending the whole document on every step; the tool returns the same handle back. | |
| member_ids | No | JSON array of the continuous beam's member ids, e.g. [4,5,6]. They get a member set of their own unless one already holds exactly them. | |
| member_set_id | No | Instead of member_ids: put the hinge on this existing member set. | |
| scissor_hinge_id | No | Reuse this existing scissor hinge definition (and rewrite it when releases or axes are given). |
set_supportSet a support conditionRead-onlyInspect
Write a nodal support (boundary condition) and point one or more nodes at it: creates or overwrites an entry in model.nodal_supports and sets node.nodal_support on every node you name. Returns the updated model — feed it to solve_model.
All 7 DOFs are set independently, in the GLOBAL frame: X/Y/Z (translations), RX/RY/RZ (rotations about the global axes) and warping (the 7th DOF, only live when analysis.options.include_warping is true and the section has i_w). Condition types are case-sensitive: Fixed (rigid), Free (no restraint), Spring (needs the matching _stiffness > 0 — N/m for translations, N*m/rad for rotations), PositiveOnly (one-way: may only push in the +global direction, i.e. lift-off) and NegativeOnly (the mirror image). A one-way condition may also carry a _stiffness, which from engine 0.2.65 is the bedding stiffness it acts through while in contact — a base plate on a slab is not infinitely stiff. Released, it is free regardless. On 0.2.64 and earlier that number was accepted, range-checked and then silently discarded, so the model solved as rigid contact.
THERE IS NO "FREE BY OMISSION". Once a node references a support, any DOF you leave out is treated by the solver as FULLY FIXED — a support of literally {"id":1} is an encastre, and the OpenAPI text claiming an absent field means free is wrong. This tool therefore always writes all six translation/rotation conditions explicitly, and anything you do not name is written as Free. Use preset to fill them in one go: fixed (encastre), pinned (X/Y/Z fixed, rotations free), free, roller_x/roller_y/roller_z (ONLY that translation fixed, everything else free), ground_contact_y (X/Z fixed, Y PositiveOnly, rotations free — a base plate that can lift off). Per-DOF fields are applied on top of the preset. Warping is the one exception: leaving it out really does mean free.
ONE-WAY SUPPORTS have a prerequisite this tool checks for you: analysis.options.rigid_strategy must be RigidMember (LinearMpc is a hard validation error). Tension/Compression members are fine alongside them from engine 0.2.65 -- before that the combination was refused at validation, so if you are reading advice written against an older engine, that restriction is gone. There is also a SILENT prerequisite — the release tolerance is 0.5*axial_slack in force units, so a coarse axial_slack leaves the DOF engaged while still reporting its reaction as 0, and the printed reactions stop summing to the applied load. This tool lowers axial_slack to 1e-3 when it writes a one-way condition (set keep_axial_slack to skip that). Beware that a support which lifts off can leave the structure unrestrained, and that a one-way support can break an eigen solve if the model also has a modal or buckling block.
Reactions use ONE SIGN CONVENTION for every support type from engine 0.2.60: each row is the force the support applies to the structure, so reactions can be summed against the applied load without checking which kind of support produced each one. (Up to 0.2.59 a Spring DOF reported the spring force k*u, the negative of what a Fixed DOF reported for the same physical situation. If you are reading results solved on an older engine, that flip is still in them.) A stiffness curve (stiffness_curves) makes the spring load-dependent, forces the DOF to Spring and requires a NONLINEAR analysis order — and note its driver is the EXTERNAL nodal load at that node, not the reaction, so a base-plate curve with no direct nodal load degenerates to the first point's stiffness.
| Name | Required | Description | Default |
|---|---|---|---|
| X | No | Fixed | Free | Spring | PositiveOnly | NegativeOnly (global X translation). | |
| Y | No | Same, global Y (vertical in these models). | |
| Z | No | Same, global Z. | |
| RX | No | Same, rotation about global X. | |
| RY | No | Same, rotation about global Y. | |
| RZ | No | Same, rotation about global Z. | |
| model | No | Stringified FERS model JSON | |
| preset | No | fixed | pinned | free | roller_x | roller_y | roller_z | ground_contact_y. Applied first; the per-DOF fields below then override it. | |
| warping | No | Warping DOF condition. | |
| node_ids | Yes | JSON array of node ids to support, e.g. [1,2], or the string "all". They all share one support definition. | |
| model_ref | No | Handle from stash_model, used INSTEAD of `model`. Avoids sending the whole document on every step; the tool returns the same handle back. | |
| support_id | No | Overwrite this existing nodal_supports entry; omit to allocate a new one. | |
| X_stiffness | No | N/m — required when X is Spring. | |
| Y_stiffness | No | N/m — required when Y is Spring. | |
| Z_stiffness | No | N/m — required when Z is Spring. | |
| RX_stiffness | No | N·m/rad — required when RX is Spring. | |
| RY_stiffness | No | N·m/rad — required when RY is Spring. | |
| RZ_stiffness | No | N·m/rad — required when RZ is Spring. | |
| classification | No | Free-text label. | |
| keep_axial_slack | No | Leave analysis.options.axial_slack untouched when writing a one-way support. | |
| stiffness_curves | No | Nonlinear support springs: {"Y":{"depends_on":"Fy","points":[[0,1e5],[10000,1e6]]}}. Forces that DOF to Spring and the analysis to NONLINEAR. | |
| warping_stiffness | No | Required when warping is Spring. |
solve_modelSolve a structural modelRead-onlyInspect
Run the FERS FEM solver on a structural model. Input is the full model JSON (as a string). Returns displacements, internal forces and reactions. On this hosted server the solve does NOT run here: the tool validates the model, parks it and returns an open_url that solves it in the user's browser, where the results are shown. Tell the user to open the link; for results inside the chat they can install the local FERS MCP server. Results are returned at detail: standard by default — ask for 'summary' when you only need peak values, and 'full' only when you genuinely need every sample point.
| Name | Required | Description | Default |
|---|---|---|---|
| model | No | Stringified JSON of the FERS structural model | |
| detail | No | How much result data to return. 'summary' (peak forces, displacements and reactions with where they occur — smallest, usually enough), 'standard' (default: envelopes + node displacements + reactions), or 'full' (adds section forces and the complete internal-force series; can be hundreds of thousands of tokens on a real model). | |
| model_ref | No | Handle from stash_model, used INSTEAD of `model`. Avoids sending the whole document on every step; the tool returns the same handle back. | |
| result_filter | No | Advanced: emit only these result blocks, overriding `detail`. Any of: local_envelopes, global_envelopes, local_end_forces, global_end_forces, section_forces, internal_force_series, local_displacements, node_displacements, reactions. An empty list is a maximal strip. Unity checks, summaries and diagnostics are never filtered. | |
| include_report | No | Embed the rendered HTML hand-calc report in the response. Default false — it is large. Use the free render_report tool on a saved result instead. | |
| idempotency_key | No | Optional. Reuse the same value when retrying a solve so the retry is recognised as the same request. |
stash_modelPark a model, get a handleInspect
Park a model and get back a short handle (model_ref). Every authoring tool, and solve_model, accepts model_ref instead of model — so a multi-step build stops shipping the whole document through your context on every call, which on a real model is thousands of tokens per step. Tools called with a handle return the same handle, so the chain stays cheap. Handles are private to you and expire after 24 hours; they are NOT saved models and never appear in list_models (use save_model for that). Free.
| Name | Required | Description | Default |
|---|---|---|---|
| name | No | Optional label, purely to help you keep several drafts apart. | |
| model | Yes | Stringified FERS model JSON to park |
summarize_plate_resultsSummarize plate resultsRead-onlyIdempotentInspect
Reduce a solved FERS document to the plate numbers an engineer actually reads, and derive extreme-fibre stresses. READ-ONLY: it returns a summary, not a model.
You need this because plate results cannot be read back through the model. The mesh is generated on an internal working copy, so the returned document has model.plate_elements = [] and plate_surfaces[].generated_plate_element_ids = [], and the mesh node ids in displacement_nodes have no coordinates anywhere in model.nodes. The only georeferenced plate output is results.<case|combination>.plate_results, keyed by element id, carrying centroid {X,Y,Z}, centroid_displacement_global/local and the stress resultants.
UNITS AND CONVENTION, from the engine's own denormalisation: nx, ny, nxy, qx, qy are membrane forces and transverse shears per unit width, in force/length (N/m); mx, my, mxy are moments per unit width, in force (N.m/m, numerically N). mx is the moment PRODUCING sigma_x (conjugate to curvature about local y), not "the moment about x" — on a rectangular slab spanning the short way with local_x along the long side, the large resultant is my. The sign of mx/my/mxy follows the element normal, which follows the boundary/connectivity ordering, so reversing it flips all three; von Mises and |principal| are immune. qx and qy are identically ZERO for Kirchhoff/DKT plates by construction, so non-zero transverse shear is itself proof the Mindlin element was used. results..summary.total_plate_forces is a COUNT of plate results, not a force.
Give thickness and the tool derives the extreme-fibre stresses exactly as the engine's own plate unity check does: sigma_x = nx/t +/- 6mx/t^2, sigma_y = ny/t +/- 6my/t^2, tau_xy = nxy/t +/- 6mxy/t^2, evaluated at both fibres with the governing (larger von Mises) one reported, then vonMises = sqrt(sx^2 - sx.sy + sy^2 + 3txy^2) and the in-plane principals. Add yield_stress for a utilisation.
| Name | Required | Description | Default |
|---|---|---|---|
| top_n | No | How many governing elements to list per quantity (default 5). | |
| results | Yes | A solved FERS document (or just its `results` block) from solve_model. | |
| thickness | No | Plate thickness in metres — needed to turn moments and forces into extreme-fibre stresses. | |
| quantities | No | JSON array from: deflection, nx, ny, nxy, mx, my, mxy, qx, qy, von_mises, sigma_x, sigma_y, tau_xy, principal_max, principal_min. | |
| yield_stress | No | Yield stress in Pa — adds a von Mises utilization if given. | |
| case_or_combination | No | Name of one load case or combination. Omit for all of them. |
validate_modelValidate a modelRead-onlyIdempotentInspect
Check a FERS model against the solver's own schema WITHOUT solving it — free. Reports field-level errors (missing or wrong-typed fields, invalid enum values), broken id references (a member pointing at a node that does not exist, a load on a deleted member, duplicate ids), and warns about unknown fields under analysis.* — which matter because the engine silently ignores those and gives you a default instead of an error. Always run this before solve_model on a hand-authored model.
| Name | Required | Description | Default |
|---|---|---|---|
| model | No | Stringified JSON of the FERS structural model to validate | |
| model_ref | No | Handle from stash_model, used INSTEAD of `model`. Avoids sending the whole document on every step; the tool returns the same handle back. |
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections.
35 tool updates
- First observed
add_imperfection_case - First observed
add_load - First observed
add_load_case - First observed
add_load_combination - First observed
add_plate_grid_elements - First observed
add_plate_opening - First observed
add_plate_surface - First observed
add_unity_check - First observed
build_model - First observed
check_beam - First observed
create_beam - First observed
create_frame - First observed
create_section - First observed
generate_load_combinations_ec0 - First observed
get_check_schema - First observed
get_schema - First observed
get_section - First observed
get_template - First observed
get_usage - First observed
list_materials - First observed
list_models - First observed
list_sections - First observed
list_templates - First observed
load_model - First observed
run_unity_checks - First observed
save_model - First observed
set_analysis - First observed
set_member - First observed
set_member_design - First observed
set_scissor_hinge - First observed
set_support - First observed
solve_model - First observed
stash_model - First observed
summarize_plate_results - First observed
validate_model
Publisher details
- Operator
- FERS Cloud · Publisher source
- Operator website
- https://ferscloud.com
- Vendor relationship
- First-party
- Documentation
- https://ferscloud.com/mcp
- Trust center
- Not available
- Restrictions
- Free tier: non-commercial use only, models up to 100 members. Commercial use needs a paid FERS Cloud plan (Pro, up to 10,000 members). The hosted server returns solves as a browser link; for results inside the chat use the local server (npx -y @ferscloud/fers-mcp). · Publisher source
Related MCP Connectors
- OwlCADOAuthcom.owlcad
Parametric 3D CAD for AI agents: build print-ready parts, check them, export STL, 3MF or STEP.
Real FEA on CAD parts: validated setups, stress, safety factor, natural frequencies
AI-callable calculators and engineering models with real formulas. No hallucinated math.
Deterministic reasoning stack for AI agents: simulate, decide & compute, plus cross-domain tools.
Related MCP Servers
- AlicenseBqualityAmaintenanceAn agentic MCP server for CSI ETABS v22+ that enables AI agents to model, load, analyze, design, and inspect building structures through 60 curated engineering tools and live .NET reflection of the full 1,293-method OAPI. Includes offscreen 3D rendering of deformed shapes and D/C ratios, interactive database table editing, and background analysis execution.7120Apache 2.0
- AlicenseAqualityDmaintenanceEnables AI agents to interact with Bentley STAAD.Pro models for tasks like load case definition, data extraction, and property setting, running locally with multi-instance support and no cloud dependency.5MIT
- AlicenseAqualityAmaintenanceThe official MCP interface for AgentFEM, giving AI agents seven typed tools to create, validate, run, inspect, and verify finite-element simulations while preserving scientific evidence.739 PyPIApache 2.0
- FlicenseNot gradedqualityDmaintenanceEnables AI assistants to perform structural engineering analysis using the Pynite finite element library, including model creation, element definition, analysis execution, and result visualization through natural language.-
Glama MCP Gateway
Add one secure layer between your agents and this server.