# Migration Guide: openstaad 0.0.x to 0.1

## What changes and why

The **legacy API** (standalone classes `Root()`, `Geometry()`, `Load()`, …) is **deprecated** in the
`0.0.x` releases (it emits a `FutureWarning` on instantiation) and **will be removed in `0.1.0`**.

The **new API** is a single import with one shared session:

```python
from openstaad import ops
s = ops.connect()          # active STAAD.Pro instance   (equivalent to Root())
# s = ops.connect(path)    # a .STD already open, by path (equivalent to Root(path))
```

**97% of the migration is mechanical:** method names **do not change**; you just collapse the multiple
objects into a single session `s` and repoint the calls to `s.`.

## Mechanical migration (general case)

**Before:**
```python
from openstaad import Geometry, Root
geometry = Geometry()
root = Root()
beams = geometry.GetBeamList()
name  = root.GetSTAADFile()
```

**After:**
```python
from openstaad import ops
s = ops.connect()
beams = s.GetBeamList()
name  = s.GetSTAADFile()
```

## Deprecated API

The following 9 classes are deprecated; **all of their methods** are re-exposed with the **same name**
on the `s` session:

`Root`, `Geometry`, `Load`, `Output`, `Properties`, `View`, `Design`, `Command`, `Support`

Of the 203 public methods in the legacy API, **197 have an identical name in `ops`** (direct migration).
The remaining 6 are detailed below.

---

## ⚠️ Cases that are NOT a simple move

### 1. Renamed methods (3)

| Legacy | New (`ops`) | Note |
|---|---|---|
| `GetElementGlobalOffset` | `GetElementGlobalOffSet` | capitalization change `Offset`→`OffSet` |
| `GetElementOffsetSpec` | `GetElementOffSetSpec` | capitalization change `Offset`→`OffSet` |
| `AddResponseSpectrumLoadEx` | `AddResponseSpectrumLoad` | **the signature also changes** (see below) |

`AddResponseSpectrumLoad` **changes argument order/optionality**:
```python
# Legacy:
AddResponseSpectrumLoadEx(code_number, modal_combination, set_names_1, set_values_1,
                          spectrum_data_pairs, set_names_2=None, set_values_2=None)
# New:
s.AddResponseSpectrumLoad(rsaCode, rsaCombination, varSet1Names, varSet1Vals,
                          varSet2Names, varSet2Vals, varDataPairs)
```
The `data_pairs` move from 5th position (legacy) to **last** (new), and `set_names_2/values_2` are no
longer optional. Requires manual review, not just a rename.

### 2. Removed methods with no direct equivalent (1)

| Legacy | Replacement |
|---|---|
| `IsRelease(memb)` | **does not exist** in `ops`. Use `s.GetMemberReleaseSpecEx(beam, position)` and evaluate the release values yourself. |

### 3. Ignore (not real API)

`make_safe_array_long` and `make_variant_vt_ref` showed up as "methods" only because the legacy API did
`from openstaad.tools import *`. They are **not part of the public API** and need no migration.

---

## ⚠️ Behavior changes (same name, different result)

These methods **keep their name** but their output changed (the new API was aligned to the official
OpenSTAAD behavior). Review any code that relies on them:

| Method(s) | Legacy | New |
|---|---|---|
| `GetNodeCoordinates`, `GetNodeIncidence`, `GetNodeDistance`, `GetBeamLength` | **rounded** to 3 decimals | **full precision** (no rounding) |
| `GetApplicationVersion` | `"Version 1.2.3.4"` | `"1.2.3.4"` (no prefix) |
| `GetAnalysisStatus` | dict key `"CPUTime(sec)"` | key `"CPUTime"` |
| `NewSTAADFile` | created a `Staad_folder` subfolder; args had defaults | does **not** create folders; `(fileName, lengthUnit, forceUnit)` are required |
| `SaveModel` | `SaveModel(silent: int)` | `SaveModel(saveSilent: bool = False)` |
| several list getters | returned a `tuple` | return a `list` (low impact) |

---

## Migration prompt for an LLM

Paste this prompt together with your script into any LLM:

```
CONTEXT — about the library:
`openstaad` is an unofficial, community-built Python wrapper around the OpenSTAAD COM API used to
automate STAAD.Pro. It is installed from PyPI with `pip install openstaad`, and its documentation
lives at https://www.openstaad.com/docs. Only 0.0.x releases exist today; version 0.1.x is scheduled
for late summer 2026 and removes the legacy class-based API (Root, Geometry, Load, …) in favor of the
single `ops` session shown below.

Migrate this `openstaad` script from the legacy API (pre-0.1) to the new `ops` API (0.1).

STEP 0 — PRE-FLIGHT CHECK (do this FIRST; decide whether migration is even needed):
Run these commands (or ask the user to run them) and interpret the output:

  a) Is a virtual environment active?
       python -c "import sys; print('venv active' if sys.prefix != sys.base_prefix else 'NO venv')"
     If "NO venv": warn the user to create/activate one before changing installed packages
     (Windows: py -m venv venv && venv\Scripts\activate ; macOS/Linux: python -m venv venv && source venv/bin/activate).

  b) Which openstaad is installed, and its version?
       pip show openstaad          (read the "Version:" line)
       (fallback: python -c "import importlib.metadata as m; print(m.version('openstaad'))")
     If not installed: there is nothing to migrate against yet; install the target version first.

  c) Which version is available on PyPI?
       pip index versions openstaad
       (fallback: pip install "openstaad==" 2>&1)

DECISION — do you need to apply this migration guide?
  - Installed version >= 0.1.0                          -> legacy API is REMOVED -> migration REQUIRED. Continue.
  - Installed 0.0.x AND upgrading to the >= 0.1.0 on PyPI -> migration REQUIRED before upgrading. Continue.
  - Installed 0.0.x AND staying on 0.0.x               -> migration OPTIONAL: the legacy API still works but
                                                          emits FutureWarnings. Migrating now future-proofs the
                                                          code and silences the warnings. Ask the user; continue only if they opt in.
  - openstaad NOT installed                            -> STOP: nothing to migrate; install the target version first.
Only proceed to the steps below if the decision is that migration is needed (or the user chose to future-proof).

WHAT TO REVIEW AND DO:
1. Imports: replace `from openstaad import Root, Geometry, Load, ...` with `from openstaad import ops`.
2. Connection: replace ALL class instances (Root(), Geometry(), Load(), Output(), Properties(),
   View(), Design(), Command(), Support()) with a SINGLE session:
       s = ops.connect()
   If any class received a path/filePath, use: s = ops.connect(path)
3. Calls: repoint every method to `s.` (e.g. geometry.GetBeamList() -> s.GetBeamList()).
   Method NAMES do NOT change, EXCEPT these renames:
       GetElementGlobalOffset  -> GetElementGlobalOffSet
       GetElementOffsetSpec    -> GetElementOffSetSpec
       AddResponseSpectrumLoadEx -> AddResponseSpectrumLoad  (REVIEW the signature: data_pairs goes
           last and set_names_2/values_2 are no longer optional)
4. Removed method: if the code uses IsRelease(memb), it has NO direct equivalent; mark it with a
   TODO comment and suggest using s.GetMemberReleaseSpecEx(...) evaluating the release values.
5. Behavior changes to FLAG (name unchanged, but result differs): coordinates and lengths are NO longer
   rounded to 3 decimals; GetApplicationVersion no longer has the "Version " prefix; GetAnalysisStatus
   uses the key "CPUTime" (was "CPUTime(sec)"); NewSTAADFile no longer creates a folder and its 3
   arguments are required; SaveModel now takes a bool.
6. Do not change business logic or the order of operations. Return the migrated code and, separately,
   a list of the points that need human review (IsRelease, AddResponseSpectrumLoad, and any dependency
   on the behavior changes).

--- SCRIPT TO MIGRATE ---
<paste your code here>
```

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