Metadata-Version: 2.4
Name: fers_calculations
Version: 0.2.74
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Rust
Classifier: Intended Audience :: Science/Research
Classifier: Topic :: Scientific/Engineering
Classifier: License :: Other/Proprietary License
Classifier: Operating System :: OS Independent
License-File: LICENSE
Summary: The FERS structural analysis (FEM) engine: frames, trusses, plates and Eurocode 3 checks, JSON in, results out. For the Python SDK, pip install FERS.
Keywords: structural engineering,finite element,fem,eurocode,steel,rust
Home-Page: https://ferscloud.com
Author-email: Jeroen Hermsen <info@ferscloud.com>
License: PolyForm Strict 1.0.0; commercial use needs a FERS Cloud plan
Description-Content-Type: text/markdown; charset=UTF-8; variant=GFM
Project-URL: Documentation, https://ferscloud.com/getting-started
Project-URL: Homepage, https://ferscloud.com
Project-URL: Issues, https://github.com/Jeroen124/FERS_core/issues
Project-URL: Python SDK, https://pypi.org/project/FERS/
Project-URL: Python guide, https://ferscloud.com/structural-analysis-python

# fers_calculations

The engine of FERS: structural finite-element analysis (3D frames, trusses,
plates, Eurocode 3 checks) for engineers and AI agents. It takes a model as JSON
and returns the solved document: displacements, reactions, member forces,
buckling and modal results, and unity checks. It is compiled Rust and runs on
your machine.

**Most people want [`pip install FERS`](https://pypi.org/project/FERS/)**, the
Python SDK and `fers` command, which installs this engine and writes the model
JSON for you. Install this package alone to drive the engine with JSON you
produce some other way.

```bash
pip install fers_calculations      # wheels for Windows, macOS and Linux (x86_64, aarch64)
```

## Usage

```python
import json

import fers_calculations
from fers_core import create_beam  # pip install FERS: the SDK that writes the model

model = create_beam(5.0, "IPE180", udl=5000.0)  # 5 m span, 5 kN/m; SI units: m, N
document = json.loads(fers_calculations.calculate_from_json(json.dumps(model.to_dict(include_results=False))))

case = document["results"]["loadcases"]["Load"]
assert case["solver_diagnostics"]["converged"] and not case["errors_and_warnings"]["errors"]
print(f"mid-span deflection {case['displacement_nodes']['5']['dy'] * 1000:.2f} mm")
print(f"reaction {case['reaction_nodes']['1']['nodal_forces']['fy'] / 1000:.2f} kN")
```

```text
mid-span deflection -14.92 mm
reaction 12.50 kN
```

The document is the input plus `results`: `loadcases` and `loadcombinations`
by name, each with `displacement_nodes`, `reaction_nodes` and `member_results`
keyed by id, plus `unity_check_results`, `solve_failures` and
`load_case_failures`. Before using a number, check that its result set has no
`errors_and_warnings.errors` and that `solver_diagnostics.converged` is true:
a mechanism, for instance, is reported as a `singular_model` error. The engine
raises `RuntimeError` when it refuses a model outright.

`calculate_to_file(json_data, path)` writes the document to a file instead of
returning it, for results too large to hold as one string.

## Premium licensing and timeouts

Without a key or token the engine runs at Free-tier limits (100 members) and
makes no network call. Two ways unlock Premium limits:

- `solve_token=...`: a short-lived token signed by FERS Cloud and checked
  offline, for a machine that cannot reach ferscloud.com. An expired or invalid
  token raises `RuntimeError`.
- `api_key=...`: checked online with ferscloud.com once per solve.

Because a licence check is a network call, it is bounded. If ferscloud.com does
not answer within `license_timeout` seconds (default 30) the call raises
`fers_calculations.FersTimeoutError` **before the solve starts**, so retrying is
always safe:

```python
import fers_calculations

try:
    fers_calculations.calculate_to_file(
        json_data, "results.json", api_key=KEY, license_timeout=60
    )
except fers_calculations.FersTimeoutError:
    ...  # transient — retry or skip this model
```

`FersTimeoutError` subclasses the built-in `TimeoutError`, so `except TimeoutError`
catches it too. A definitive failure — an invalid key, a non-premium account, an
unreachable host — raises `RuntimeError` instead, since retrying those is pointless.

`license_timeout` bounds the **licence handshake only**, not the solve. To put a
hard ceiling on a whole solve, run it in a killable subprocess.

Two environment variables are read (real process environment only — `.env` files
are loaded by the CLI binary, never by this extension module):

| Variable | Purpose | Default |
|---|---|---|
| `FERS_LICENSE_TIMEOUT` | Default `license_timeout`, in seconds | `30` |
| `FERS_CLOUD_URL` | Base URL of the licence API | `https://ferscloud.com` |

## Licence

From 0.2.70 the engine is licensed under the PolyForm Strict License 1.0.0
(see `LICENSE`). It is free for any noncommercial purpose: study, teaching,
research, hobby projects, and use by schools, charities and public bodies.
Models of more than 100 members need Premium limits.

Commercial use, at any model size, needs a paid
[FERS Cloud plan](https://ferscloud.com/pricing), which includes the commercial
licence. The licence does not allow modifying or redistributing the package.

