Structural analysis in Python

Build a beam or a frame in a few lines, solve it with a Rust finite-element engine, and get reactions, internal forces, deflections and a Eurocode member check back as ordinary data you can loop over, assert on and put in a report. Run the solver locally with pip install FERS, or call the hosted REST API from any language — the same engine that runs in your browser on the free calculators.

Two ways to run it

Locally

pip install FERS gives you the model-building layer and pulls in the compiled solver as a binary wheel. Nothing leaves your machine, there is no request round-trip, and it works offline and inside a locked-down network. The right choice for batch work, notebooks and CI.

Hosted

POST /api/sdk/solve takes a model as JSON and returns the solved results. No install and no wheel to vendor, so it works from Node, Go, a shell script or a spreadsheet as well as from Python — and it is the same endpoint the MCP server calls underneath.

Quick start

1. Install

pip install FERS

The package needs Python 3.11 or newer. It installs the model-building layer plus the compiled solver.

2. Build a beam, solve it, and check it by hand

from fers_core import (
    FERS, Node, Member, Section, Material, MemberSet, NodalSupport, NodalLoad,
)

model = FERS()
node1 = Node(0, 0, 0)   # fixed end
node2 = Node(5, 0, 0)   # free end, 5 m span

steel = Material(name="Steel S235", e_mod=210e9, g_mod=80.769e9,
                 density=7850, yield_stress=235e6)

# i_z is the strong axis, i_y the weak one — the convention that catches
# everyone exactly once.
section = Section(name="IPE 180", material=steel,
                  i_y=1.009e-6, i_z=13.17e-6, j=0.0477e-6, area=0.00240)

beam = Member(start_node=node1, end_node=node2, section=section)
node1.nodal_support = NodalSupport()          # fully fixed
model.add_member_set(MemberSet(members=[beam]))

load_case = model.create_load_case(name="End Load")
NodalLoad(node=node2, load_case=load_case, magnitude=-1000, direction=(0, 1, 0))

model.run_analysis()

dy = model.resultsbundle.loadcases["End Load"].displacement_nodes["2"].dy
print(f"Tip deflection: {dy * 1000:.3f} mm")
# Tip deflection: -15.065 mm
# Hand check:  PL^3 / 3EI_z = 1000 * 5**3 / (3 * 210e9 * 13.17e-6) = 15.0655 mm

That is the whole point of the exercise: a number you can verify with a formula you already know, before you trust the solver with a model you cannot check by hand. The same closed-form comparisons are published on the accuracy page, and the standard NAFEMS benchmarks are re-solved live in the browser.

Eurocode 3 checks from a script

A plain model gives you forces. check_beam goes one step further: it adds a ULS combination and an EN 1993-1-1 member check over the span, so one call returns bending, shear, combined N+M and lateral-torsional buckling utilizations with the per-clause trace behind each.

from fers_core import check_beam

# 7.5 m simply supported IPE400 in S275, 12 kN/m characteristic, ULS factor 1.35
beam = check_beam(7.5, "IPE400", material="S275", udl=12_000, uls_factor=1.35)
beam.run_analysis()

for row in beam.unity_check_results():
    print(row["governing"]["demand"])
# 0.8002749049232664

Buckling lengths default to the full span for a single-member model, so if the compression flange is restrained along its length — by decking, by a slab, by purlins — that is a modelling decision you make explicitly rather than one the default quietly makes for you. The worked EC3 example shows the same check with every clause written out.

The thing a web form cannot do

Five sections, five solves, one loop:

import os
import requests

KEY = os.environ["FERS_API_KEY"]

for section in ["IPE300", "IPE330", "IPE360", "IPE400", "IPE450"]:
    response = requests.post(
        "https://ferscloud.com/api/sdk/check-beam",
        headers={"X-API-Key": KEY},
        json={
            "span_m": 7.5,
            "section": section,
            "material": "steel_S275",
            "support": "simply_supported",
            "udl": 12.0,                       # kN/m, characteristic
            "idempotency_key": f"sweep-{section}-7m5",
        },
        timeout=60,
    )
    response.raise_for_status()
    check = response.json()["check"]
    print(f"{section:<8} UC {check['governing_utilization']:.3f}"
          f"  governed by {check['governing_check']}")

That is the argument for scripting a structural calculation. The marginal cost of the sixth variant is one more entry in a list, not another twenty clicks — and the same loop works over spans, load cases, steel grades, or a CSV of members exported from your model. What it reports is which limit state governs and by how much; which section you then choose is your call, not the script's.

Pass a stable idempotency_key and reuse it on retries: a repeat with the same key replays the stored result instead of charging a second solve.

Call it from any language

curl -s https://ferscloud.com/api/sdk/solve \
  -H "X-API-Key: keyId.secret" \
  -H "Content-Type: application/json" \
  -d '{ "model": { /* FERS model JSON */ }, "idempotency_key": "run-123" }'

Endpoints: solve, validate, check-beam, create-beam, sections, schema, models and me, all under /api/sdk/. The full machine-readable spec is at ferscloud.com/api/openapi.json.

Two habits save a lot of grief. Call validate before solve — it is free and it catches broken references and misspelled keys that the solver would otherwise silently resolve to a default. And ask schema for the contract rather than guessing field names, for the same reason.

Use it from an AI agent

The same solver is exposed as a remote MCP server, so Claude, ChatGPT, Cursor and VS Code can call it directly:

{
  "mcpServers": {
    "fers": {
      "url": "https://ferscloud.com/api/mcp",
      "headers": { "X-API-Key": "keyId.secret" }
    }
  }
}

The clients, the one-click installers, the OAuth flow and the full tool list are on the MCP server page.

Prefer JavaScript? The same engine is published on npm as a WebAssembly build that solves in the browser with no server round-trip — see structural analysis in JavaScript.

Units, axes and sign conventions

Metres, newtons and newtons per metre in the Python layer; elastic modulus in pascals; deflections come back in metres, so multiply by 1000 for millimetres. The REST beam helpers take kN and kN/m, which is why the sweep above passes 12.0 where the local call takes 12_000.

The axis convention catches everyone once: i_z is the strong (major) axis and i_y the weak one. The full set of axis, sign and unit conventions is written out on the modeling conventions page, and it is the first thing to read if a result comes back an order of magnitude off.

What it costs

The Python package is free to install and the solver runs locally, with a 100-member ceiling on the free tier. Hosted solves — REST and MCP — are free for the first 100 successful solves per rolling week, then charged from a prepaid balance, or unlimited on Pro at €19.95 a month. Only successful solves are charged. See pricing.

Frequently asked questions

Can I do structural analysis in Python?

Yes. `pip install FERS` gives you a model-building layer plus a compiled finite-element solver. You build nodes, members, sections, supports and loads as Python objects, call run_analysis(), and read displacements, internal forces and reactions off the results bundle. For the standard cases the results match the closed-form solution to within solver tolerance.

Is there a REST API for structural analysis?

Yes, at https://ferscloud.com/api/sdk/. It takes a model as JSON and returns solved results, authenticated with an X-API-Key header. The machine-readable OpenAPI spec is at /api/openapi.json, so most HTTP clients can generate bindings from it.

Do I need an API key to use the Python package?

No. The local package solves on your machine with no account and no network call. A key is only needed for the hosted REST API, for the MCP server, and for saving models to your account.

Can I run a Eurocode 3 steel check from Python?

Yes. check_beam adds an EN 1993-1-1 member check to a single-span model and returns bending, shear, combined N+M and lateral-torsional buckling utilizations, together with the per-clause trace behind each one. The same check is one call at /api/sdk/check-beam.

Does it work offline?

The local Python package does — the solver ships as a compiled wheel, not a service call, so it works with no network and inside a locked-down corporate environment. The REST API and the MCP server naturally do not.

What size of model can I solve?

The free tier caps a model at 100 members; Pro raises that to 10 000.

How many hosted solves are free?

The first 100 successful solves each rolling week are free. Only successful solves are counted, and replaying a request with the same idempotency_key returns the stored result rather than charging again.

How accurate is it?

For the cases with a closed-form solution it matches to within solver tolerance — the cantilever example on this page agrees with PL³/3EI to four significant figures. The accuracy page lists the comparisons, and the applicable NAFEMS benchmarks are re-solved live in the browser on the validation page rather than quoted from a table.

Start with the free tier

The package installs without an account. Create a free account when you want hosted solves, the MCP server, or to save models.

Prefer not to write code? Try the free 2D frame calculator or the Eurocode 3 steel beam check in your browser.