# Worked examples

> Four scripts you can paste and run. Each one states the expected output for its exact inputs and the closed-form solution it was checked against, so you can confirm your install reproduces them before trusting a model of your own.

Source: https://ferscloud.com/docs/python-examples  
Last updated: 2026-09-04

## 1. Cantilever with an end load

A 5 m IPE 180 cantilever, fully fixed at one end, carrying a 1 kN downward point load at the tip. This is the simplest model that exercises the whole pipeline: geometry, material, section, support, load case and solve.

```python
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)
# IPE 180 from the FERS section library: i_z = strong axis (in play here), i_y = weak axis
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]))

lc = model.create_load_case(name="End Load")
NodalLoad(node=node2, load_case=lc, 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")
# Expected output: Tip deflection: -15.065 mm
# Hand check: δ = PL³/3EI_z = 1000·5³ / (3 · 210e9 · 13.17e-6) = 15.07 mm ✓
```

> Hand check: δ = PL³ / 3EI_z = 1000·5³ / (3 · 210×10⁹ · 13.17×10⁻⁶) = 15.07 mm. The solver returns 15.065 mm.

## 2. Simply supported beam with a centre load

A 10 m IPE 300 in two 5 m members so there is a node at mid-span to load and to read. A pin at one end, a roller at the other.

Note the explicit support conditions. The pin holds the three translations and the torsional rotation but leaves the bending rotations free — hold the torsion or the beam has a rigid-body twist mode and the stiffness matrix is singular. The roller additionally releases longitudinal translation so the beam can extend freely.

```python
from fers_core import FERS, Node, Member, Section, Material, MemberSet, NodalSupport, NodalLoad

model = FERS()

node1 = Node(0, 0, 0)
node2 = Node(5, 0, 0)   # mid-span
node3 = Node(10, 0, 0)  # right support

steel = Material(name="Steel S235", e_mod=210e9, g_mod=80.769e9, density=7850, yield_stress=235e6)
section = Section(name="IPE 300", material=steel, i_y=6.038e-6, i_z=83.58e-6, j=0.199e-6, area=0.00538)

beam1 = Member(start_node=node1, end_node=node2, section=section)
beam2 = Member(start_node=node2, end_node=node3, section=section)

# Pin at node1 — bending rotations free, torsion (RX) held so the beam
# has no rigid-body twist mode. Roller at node3 — X translation free too.
pin = NodalSupport(
    displacement_conditions={"X": "Fixed", "Y": "Fixed", "Z": "Fixed"},
    rotation_conditions={"X": "Fixed", "Y": "Free", "Z": "Free"},
)
roller = NodalSupport(
    displacement_conditions={"X": "Free", "Y": "Fixed", "Z": "Fixed"},
    rotation_conditions={"X": "Free", "Y": "Free", "Z": "Free"},
)
node1.nodal_support = pin
node3.nodal_support = roller

model.add_member_set(MemberSet(members=[beam1, beam2]))

lc = model.create_load_case(name="Centre Load")
NodalLoad(node=node2, load_case=lc, magnitude=-10000, direction=(0, 1, 0))

model.run_analysis()

dy_mid = model.resultsbundle.loadcases["Centre Load"].displacement_nodes["2"].dy
print(f"Mid-span deflection: {dy_mid*1000:.3f} mm")
# Expected output: Mid-span deflection: -11.870 mm
# Hand check: δ = PL³/48EI_z = 10000·10³ / (48 · 210e9 · 83.58e-6) = 11.87 mm ✓
```

> Hand check: δ = PL³ / 48EI_z = 10000·10³ / (48 · 210×10⁹ · 83.58×10⁻⁶) = 11.87 mm. The solver returns 11.870 mm.

## 3. Portal frame under horizontal load

Two 4 m HEA 200 columns with fixed bases and a 6 m IPE 300 rafter, loaded by a 5 kN horizontal force at the top of the left column. This is the smallest model where frame action — rather than a single member — determines the answer.

```python
from fers_core import FERS, Node, Member, Section, Material, MemberSet, NodalSupport, NodalLoad

model = FERS()

# Portal frame: two columns (h=4 m) + one beam (L=6 m)
n1 = Node(0, 0, 0)   # left base
n2 = Node(0, 4, 0)   # left top
n3 = Node(6, 4, 0)   # right top
n4 = Node(6, 0, 0)   # right base

steel = Material(name="Steel S235", e_mod=210e9, g_mod=80.769e9, density=7850, yield_stress=235e6)
col_sec  = Section(name="HEA 200", material=steel, i_y=13.36e-6, i_z=36.93e-6, j=0.206e-6, area=0.00538)
beam_sec = Section(name="IPE 300", material=steel, i_y=6.038e-6, i_z=83.58e-6, j=0.199e-6, area=0.00538)

col_left  = Member(start_node=n1, end_node=n2, section=col_sec)
col_right = Member(start_node=n4, end_node=n3, section=col_sec)
rafter    = Member(start_node=n2, end_node=n3, section=beam_sec)

n1.nodal_support = NodalSupport()  # fixed base
n4.nodal_support = NodalSupport()  # fixed base

model.add_member_set(MemberSet(members=[col_left, col_right, rafter]))

lc = model.create_load_case(name="Wind")
# Horizontal wind load on left column top
NodalLoad(node=n2, load_case=lc, magnitude=5000, direction=(1, 0, 0))

model.run_analysis()

dx = model.resultsbundle.loadcases["Wind"].displacement_nodes["2"].dx
print(f"Sway at left top: {dx*1000:.3f} mm")
# Expected output: Sway at left top: 5.310 mm
# Hand check (slope-deflection, axial deformation neglected): 5.30 mm ✓
```

> Hand check by slope-deflection, neglecting axial deformation: 5.30 mm. The solver returns 5.310 mm. A fuller treatment with diagrams is on the [portal frame worked example](https://ferscloud.com/portal-frame-analysis-example).

## 4. Eurocode 3 steel member check

`check_beam` builds a single-span beam, solves it and runs the EN 1993-1-1 member checks in one call. Here: a 7.5 m simply supported IPE 400 in S355 under a 13 kN/m characteristic UDL, factored by 1.35, with the compression flange unrestrained over the full span.

The result carries a per-clause trace rather than a single number, so each utilization can be read as a hand calculation — the intermediate values that produced it are all there.

```python
from fers_core import check_beam

# 7.5 m simply supported IPE 400 in S355, 13 kN/m characteristic UDL,
# ULS factor 1.35, compression flange unrestrained over the full span
model = check_beam(
    span=7.5,
    section="IPE400",
    material="S355",
    udl=13_000,       # N/m characteristic (13 kN/m)
    uls_factor=1.35,
)

model.run_analysis()

check = model.unity_check_results()[0]
print(f"Governing UC: {check['max_utilization']:.2f}")
for step in check["governing"]["trace"]:
    print(f"  {step['label']:22s} {step['value']:.2f}")

# Expected output (solver-computed, matches the published worked example):
# Governing UC: 0.82
#   Bending y (6.2.5)      0.00
#   Bending z (6.2.5)      0.27
#   Shear z (6.2.6)        0.00
#   Shear y (6.2.6)        0.07
#   Combined N+M (6.2.1)   0.27
#   LTB (6.3.2)            0.82
#   Governing              0.82
```

> This reproduces the published [Eurocode 3 worked example](https://ferscloud.com/eurocode-3-steel-beam-check-example), where the same case is set out clause by clause. Lateral-torsional buckling governs at UC 0.82.

## Verify before you trust

Every example above prints a number next to the closed-form solution it should reproduce. That is deliberate, and it is the habit worth copying: solve a case whose answer you already know before scaling up to one you do not.

The [accuracy benchmarks](https://ferscloud.com/benchmarks) do this across a wider set of closed-form cases, and the [NAFEMS page](https://ferscloud.com/nafems) runs the standard finite-element benchmarks live in your browser with the target-versus-FERS error for each.

## Frequently asked questions

**Why does the simply supported example need two members?**

Only so there is a node at mid-span to apply the load to and read the displacement from. The element formulation is exact for a prismatic member, so splitting the span does not change the answer — it just gives you somewhere to attach the load.

**Why does the pin hold the torsional rotation?**

Releasing all three rotations at both ends of a straight beam leaves it free to spin about its own axis. That rigid-body mode makes the stiffness matrix singular and the solve fails. Holding RX at one support removes the mode without restraining any bending.

**My numbers differ in the last digit — is something wrong?**

No. The printed values are rounded to three decimals of a millimetre; the closed-form comparison is quoted to the precision the formula justifies. A difference beyond that usually means swapped `i_y` / `i_z` or a units mistake.

**Where do the section properties come from?**

The FERS steel section library, which ships with the package. You can also pass properties directly, as these examples do, or compute them for an arbitrary shape with the [section properties calculator](https://ferscloud.com/section-properties-calculator).

**Can I run these without an account?**

Yes. All four solve locally with no credentials. Only cloud storage and the metered API/agent channels need a key.

**How do I do the same thing from JavaScript?**

The same engine is published as a WebAssembly npm package with an identical model format — see [using FERS from JavaScript](https://ferscloud.com/docs/javascript).

## Related

- https://ferscloud.com/docs/python-api
- https://ferscloud.com/docs/rest-api

