# Base Units: the units OpenStaad returns to you

Every number OpenSTAAD **returns** to your script — a node coordinate, a member end force, a
displacement, a section area — comes back in the model's **base units**, a unit your script did not
choose and cannot change.

Getting this wrong is silent. A beam reported as `236.22` is not a 236 m span; it is 236.22 in, or
6 m, in a model whose base unit happens to be English. Nothing raises, nothing warns — the design
check just comes out wrong.

This page is only about base units, the **get** side. For the units of values you **send**, see
[Input Units](/guides/input_units); for how the two fit together, see
[Unit Systems](/guides/unit_systems).

---

## The short answer

| Base unit | Length-derived values | Force-derived values |
|---|---|---|
| **Metric** | meters (`m`) | kilonewtons (`kN`) |
| **English** | inches (`in`) | kilopounds (`kip`) |

That is the whole rule for reads. Everything else in this page is a consequence of it.

Note the two easy mistakes: the English force unit is the **kip**, not the pound — and the metric
length unit is the **meter**, not the millimeter. A displacement of `0.012` in a metric model is
12 mm, not 0.012 mm.

---

## Base unit is not stored in the file

![STAAD.Pro Application Configuration dialog showing the Base Unit setting under General, with Metric selected](/images/base-units-app-config.png)

Base unit is not a `.std` file property. It's set in STAAD.Pro's own **Application
Configuration → General → Base Unit** dialog (pictured above), and it applies to whatever file is
opened next — it is read once, when the file is opened, not stored inside it.

A model built and tested entirely with STAAD.Pro configured for **English** can have a `.std` file
that contains no mention of Metric anywhere — only `UNIT` lines from `SetInputUnits()` calls, which
are a separate, per-command setting (see [Input Units](/guides/input_units)):

```
UNIT INCHES KIP
JOINT COORDINATES
1 0 0 0; 2 120 0 0; 3 240 0 0;
...
UNIT METER KN
...
UNIT INCHES KIP
```

Close that file, switch **Application Configuration → General → Base Unit** to **Metric**, and
reopen the same, unmodified `.std` file: `GetBaseUnit()` immediately starts returning `"Metric"`,
and every previously-in-inches value — node coordinates, load magnitudes — comes back converted to
meters and kN, with no edit to the file at all.

Two people can open the identical file with different STAAD installs configured differently and get
different values back from every `Get*` call — same file, same geometry, different numbers. This
makes `GetBaseUnit()` **not safely cacheable across sessions or machines**, and not something a
script can infer from the file itself without calling the API. Any pipeline that assumes "this model
is an English-unit model" because that's how it was built is wrong; it depends on whoever's STAAD
instance opens it next.

**Always call `GetBaseUnit()` at the start of a script; never assume it from how the model was
authored.**

---

## Reading the base unit

`GetBaseUnit()` returns the string `"Metric"` or `"English"` for the currently open `.STD`:

```python
from openstaad import ops

s = ops.connect()

print(s.GetBaseUnit())   # -> "Metric"  or  "English"
```

Underneath, STAAD returns the integer `1` for English and `2` for Metric; the library maps it to the
string so you never have to remember which is which.

Any script that will run on somebody else's model — or on your own model from a machine configured
differently — should read this on the first line and branch on it. A script that assumes metric is a
script that works until the day it doesn't.

---

## Can I get returned values in a different unit than the base unit?

No. OpenSTAAD has no setting that changes the units of returned values: `SetInputUnits()` only
affects what you send, and the base unit is set by the STAAD.Pro application, not by your script.
If you need a value in another unit, convert it yourself as soon as it arrives — see
*A conversion pattern that holds up* below.

---

## Derived quantities

Only length and force are primitive. Everything else is built from them, which means every derived
unit changes with the base unit too:

| Quantity | Metric | English |
|---|---|---|
| Length, displacement, section dimension | `m` | `in` |
| Force, reaction, axial force | `kN` | `kip` |
| Moment | `kN·m` | `kip·in` |
| Stress | `kN/m²` | `kip/in²` (ksi) |
| Section area | `m²` | `in²` |
| Section modulus | `m³` | `in³` |
| Moment of inertia | `m⁴` | `in⁴` |
| Distributed force | `kN/m` | `kip/in` |
| Density | `kN/m³` | `kip/in³` |

The practical consequence: a conversion factor of `0.0254` is not enough. An area needs `0.0254²`,
an inertia needs `0.0254⁴`, and a stress needs `force / length²`. Converting each quantity with the
same length factor is a common and expensive bug.

These factors were checked by reading the same unmodified model with English and then Metric base
units (STAAD.Pro 2025 25.0.1.424, openstaad 0.0.15). The ratio `English / Metric` matched the table:

| Quantity read | Ratio `English / Metric` |
|---|---|
| Coordinates, displacements | 39.370 |
| Reactions, member end forces | 0.22481 |
| Moments | 8.8507 |
| Section area (`Ax`, `Ay`, `Az`) | 1550.003 |
| `Iy`, `Iz` | 2 402 510 |
| Modulus of elasticity | 1.4504e-4 |
| Material density | 3.684e-6 |
| UDL magnitude | 5.710e-3 |

Not everything follows the base unit. Rotations, Poisson's ratio and the thermal expansion
coefficient came back identical under both. Section modulus was not verified, because no function
returns it directly. The torsional constant returned by `GetBeamProperty` was the one exception
we could not explain: it did not follow the `39.37⁴` factor (`0.3` in⁴ against `1.181e-7` m⁴, a
ratio of about 2 539 564 instead of 2 402 510), so do not convert it with the inertia factor
without checking it against your own model.

---

## A conversion pattern that holds up

Read the base unit once, derive every factor from the two primitives, and never hand-write a
conversion at the call site:

```python
from openstaad import ops

IN_TO_M = 0.0254             # exact
KIP_TO_KN = 4.4482216152605  # exact


class BaseUnits:
    """Factors converting OpenStaad's base-unit values into m / kN."""

    def __init__(self, base_unit: str):
        if base_unit == "Metric":
            self.length, self.force = 1.0, 1.0
        elif base_unit == "English":
            self.length, self.force = IN_TO_M, KIP_TO_KN
        else:
            raise ValueError(f"unknown base unit: {base_unit!r}")

    # Everything below is derived, so it can never drift out of sync.
    @property
    def area(self):
        return self.length ** 2

    @property
    def inertia(self):
        return self.length ** 4

    @property
    def moment(self):
        return self.force * self.length

    @property
    def stress(self):
        return self.force / self.length ** 2

    @property
    def dist_force(self):
        return self.force / self.length


s = ops.connect()
u = BaseUnits(s.GetBaseUnit())

x, y, z = s.GetNodeCoordinates(1)
print(f"node 1: ({x * u.length:.3f}, {y * u.length:.3f}, {z * u.length:.3f}) m")

# end = 0 (start) or 1 (end); last argument is 0 global / 1 local
fx, fy, fz, mx, my, mz = s.GetMemberEndForces(1, 0, 1, 1)
print(f"axial  = {fx * u.force:.2f} kN")
print(f"moment = {mz * u.moment:.2f} kN·m")
```

Two habits make this robust:

- **Convert at the boundary.** Turn base-unit values into your working units the moment they arrive,
  and keep everything downstream in one system. Mixed-unit values flowing through a calculation are
  nearly impossible to debug after the fact.
- **Label your output.** Print the unit next to the number. A report that says `236.22` invites the
  reader to guess; one that says `236.22 in` does not.

---

## Checklist

- Read `GetBaseUnit()` once at startup, every run — never assume it from how the model was authored
  or from a previous run. It's an application setting, not a file property.
- Convert returned values at the boundary and label every printed number with its unit.
- Derive area, inertia, moment and stress factors from the length and force factors; never reuse the
  length factor alone.
- If a script runs on shared models or teammates' machines, don't assume `GetBaseUnit()` will be the
  same on every machine — check STAAD's Application Configuration if results look suspiciously off
  by a unit factor.

---

## Further reading

- [Unit Systems](/guides/unit_systems) — how base units and input units work together.
- [Input Units](/guides/input_units) — the units of every value you send.
- [Get Base Unit](/docs/session-file-root#GetBaseUnit) — the function reference.
- [Units in STAAD.Pro](https://docs.bentley.com/LiveContent/web/STAAD.Pro%20Help-v19/en/GUID-C03F5D61-17FE-4FE3-8CB8-042D71C2F6AB.html) — Bentley's own description of the base unit setting.

Source: https://www.openstaad.com/guides/base_units
