# Build and solve a model in Python

> Seven objects cover almost every model: Node, Material, Section, Member, MemberSet, NodalSupport and NodalLoad. You assemble them, call `run_analysis()`, and read a results bundle keyed by load case.

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

## The core objects

Everything is imported from `fers_core`. A model is a `FERS` instance holding member sets; loads hang off load cases rather than off the model directly, so the same geometry can carry several independent load cases.

- **Node** — an X, Y, Z point. Y is up. Nodes own their support condition.
- **Material** — E-modulus, shear modulus, density, yield stress. All SI: pascals and kg/m³.
- **Section** — area and second moments of area, referencing a material.
- **Member** — a 2-node beam element with six degrees of freedom per node, referencing a section.
- **MemberSet** — the group you add to the model; also the unit design checks are applied to.
- **NodalSupport** — the boundary condition assigned to a node.
- **NodalLoad** — a force or moment applied to a node within a load case.

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

## Define geometry, material and section

Coordinates are in metres and material properties in pascals. The section below is an IPE 180 taken from the FERS steel section library.

Note the axis convention, because it is the single most common source of a wrong answer: `i_z` is the **strong** axis and `i_y` the weak one. A beam bending in its usual plane is governed by `i_z`.

```python
# Nodes (x, y, z coordinates in metres)
node1 = Node(0, 0, 0)
node2 = Node(5, 0, 0)

# Material (Steel S235)
steel = Material(
    name="Steel S235",
    e_mod=210e9,     # Pa
    g_mod=80.769e9,  # Pa
    density=7850,    # kg/m³
    yield_stress=235e6  # Pa
)

# Cross-section (IPE 180), values from the FERS steel section library.
# Axis convention: i_z is the STRONG axis (bending in the local x-y plane),
# i_y the weak axis — see ferscloud.com/conventions
section = Section(
    name="IPE 180",
    material=steel,
    i_y=1.009e-6,  # m⁴ (weak axis)
    i_z=13.17e-6,  # m⁴ (strong axis)
    j=0.0477e-6,   # m⁴
    area=0.00240   # m²
)
```

> Full axis, sign and unit conventions are on the [conventions page](https://ferscloud.com/conventions). The JSON contract takes angles in degrees, not radians.

## Create the member, apply a support and a load

A bare `NodalSupport()` is fully fixed. For a pin or a roller, pass explicit `displacement_conditions` and `rotation_conditions` — see [the worked examples](https://ferscloud.com/docs/python-examples) for both.

Load directions are global unit vectors, so `(0, 1, 0)` with a negative magnitude is a downward force.

```python
# Create beam element
beam = Member(start_node=node1, end_node=node2, section=section)

# Fixed support at node1
node1.nodal_support = NodalSupport()

# Organise members into a set and add to model
model = FERS()
model.add_member_set(MemberSet(members=[beam]))

# Load case with a −1 kN point load at node2
lc = model.create_load_case(name="Gravity")
NodalLoad(node=node2, load_case=lc, magnitude=-1000, direction=(0, 1, 0))
```

## Run the analysis and read results

`run_analysis()` solves every load case. Results come back on `model.resultsbundle`, keyed by load-case name, with displacements and reactions keyed by node id as a string.

The comments show the actual output for these exact inputs.

```python
model.run_analysis()

results = model.resultsbundle.loadcases["Gravity"]

dy = results.displacement_nodes["2"].dy
Vy = results.reaction_nodes["1"].nodal_forces.fy
Mz = results.reaction_nodes["1"].nodal_forces.mz

print(f"Tip deflection : {dy*1000:.3f} mm")   # -15.065 mm
print(f"Reaction Vy    : {Vy:.1f} N")         # 1000.0 N
print(f"Reaction Mz    : {Mz:.1f} Nm")        # 5000.0 Nm
```

> Hand check: a 5 m cantilever bending about its strong axis gives δ = PL³ / 3EI_z = 1000·5³ / (3 · 210×10⁹ · 13.17×10⁻⁶) = 15.07 mm, with a 1 kN reaction and a 5 kN·m fixing moment from equilibrium. The solver returns 15.065 mm.

## Beyond linear statics

The same model object carries the settings for the other analysis types. Second-order (P-Delta) analysis, linear buckling with a critical load factor, modal analysis and response-spectrum seismic analysis are all driven from analysis settings rather than from a different API.

Design checks are attached to member sets and evaluated after the solve, returning per-clause utilizations with the intermediate values that produced them — so a check can be read as a hand calculation rather than a single number.

- Verify against closed-form solutions first — see [accuracy benchmarks](https://ferscloud.com/benchmarks) and the [NAFEMS set](https://ferscloud.com/nafems).
- For EN 1993-1-1 member checks, `check_beam` builds, solves and checks a single span in one call — see [the examples](https://ferscloud.com/docs/python-examples).

## Frequently asked questions

**Why are my displacements keyed by a string?**

Result dictionaries are keyed by node and member id as strings, because the results bundle round-trips through JSON. `displacement_nodes["2"]`, not `[2]`.

**What is a MemberSet for?**

It is the unit you add to the model and the unit design checks apply to. Grouping members that form one physical element — a column spliced from two members, say — lets a buckling length or a code check span the whole thing.

**How do I model a pin or a roller?**

Pass explicit conditions to `NodalSupport`. A pin fixes the three translations and leaves bending rotations free; hold the torsional rotation as well or the member has a rigid-body twist mode and the solve is singular. The [simply supported example](https://ferscloud.com/docs/python-examples) shows both.

**Which second moment of area is the strong axis?**

`i_z`. FERS treats local z as the strong bending axis throughout — the solver, the design checks and the diagrams all agree on this. Swapping `i_y` and `i_z` is the most common modelling error.

**Do I need an internet connection to solve?**

No. `run_analysis()` runs the compiled solver in your own process. Network access is only used if you call a cloud endpoint.

**Can I get the deflected shape rather than just end values?**

Yes, and it is load-exact rather than interpolated between end displacements — a fixed-fixed beam under a uniformly distributed load reports the true wL⁴/384EI sag. Request it in the analysis options.

## Related

- https://ferscloud.com/docs/installation
- https://ferscloud.com/docs/python-examples

