Metadata-Version: 2.4
Name: steam-roxie-parser
Version: 2026.7.2
Summary: Parser for ROXIE files (.data, .cadata, .iron, .map2d) that builds magnet coil and iron-yoke geometry used by the STEAM tools (FiQuS, PySIGMA, LEDET)
Author-email: "STEAM Team, CERN" <steam-team@cern.ch>
License: GPL-3.0-only
Project-URL: Homepage, https://gitlab.cern.ch/steam/steam-roxie-parser
Keywords: ROXIE,superconducting magnets,CERN,STEAM,geometry
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Science/Research
Classifier: License :: OSI Approved :: GNU General Public License v3 (GPLv3)
Classifier: Programming Language :: Python :: 3
Classifier: Topic :: Scientific/Engineering :: Physics
Requires-Python: >=3.9
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: numpy
Requires-Dist: scipy
Requires-Dist: pandas
Requires-Dist: matplotlib
Requires-Dist: pydantic>=2
Requires-Dist: ruamel.yaml
Requires-Dist: PyYAML
Provides-Extra: test
Requires-Dist: unittest-parallel; extra == "test"
Requires-Dist: pysoleno; extra == "test"
Provides-Extra: solenoid
Requires-Dist: pysoleno; extra == "solenoid"
Dynamic: license-file

# steam-roxie-parser

Parser for [ROXIE](https://roxie.docs.cern.ch/) files that builds the 2D coil and iron-yoke geometry of
superconducting accelerator magnets. It is part of the [STEAM](https://espace.cern.ch/steam/) framework and provides
the geometry input consumed by [FiQuS](https://gitlab.cern.ch/steam/steam-fiqus-dev),
[PySIGMA](https://gitlab.cern.ch/steam/steam-pysigma) and [LEDET](https://espace.cern.ch/steam/SitePages/LEDET.aspx)
via [steam-sdk](https://gitlab.cern.ch/steam/steam_sdk).

Note: the parser supports so-called multipole magnets (cos-theta, block-coil, common-coil) defined in ROXIE.
It is not applicable to CCT magnets, solenoids, or conductor-only models.

## Concepts

The parser has two distinct stages:

1. **Input parsing** — the ROXIE input files are parsed into a raw data structure (`RoxieRawData`):
   `.cadata` → cable database, `.data` → coil winding definition, `.iron` → iron yoke definition.
   This stage is **bidirectional**: the raw data structure can be written to / read from a yaml file, and
   written back to the ROXIE input files, closing the round trip `ROXIE files ↔ RoxieRawData ↔ yaml`.
2. **Geometry building** — the raw data structure is turned into the magnet geometry (`RoxieData`): half-turn
   corner positions (bare and insulated), strand positions, wedges, and the iron-yoke outline. This is the
   `.geom` content consumed by FiQuS and PySIGMA.

`ParserRoxie.get_data(...)` orchestrates both stages; `parse_roxie_input_files(...)` and `build_geometry(...)` expose
them separately.

## Features

- Parse ROXIE input files: `.data` (coil), `.cadata` (cable database), `.iron` (iron yoke), `.map2d` (field maps)
- Raw data structure `RoxieRawData` ↔ yaml (`utils.raw_data_file.write_raw_data_yaml` / `read_raw_data_yaml`)
- Raw data structure → ROXIE files (`parsers.roxie_file_writers.write_roxie_input_files`, and the per-file
  `write_data_file` / `write_cadata_file` / `write_iron_file`)
- Build the magnet cross-section geometry into a versioned pydantic data model (`RoxieData`)
- Write and read the `.geom` geometry file (`utils.geom_file.write_geom_file` / `read_geom_file`)
- Solenoid geometry from a coil definition (`parsers.solenoids.Solenoid_magnet`)
- Plot the parsed geometry, or save it as SVG/PNG (`steam_roxie_parser.plotters.plotter_roxie`)

## Installation

```bash
pip install steam-roxie-parser
```

For development:

```bash
git clone https://gitlab.cern.ch/steam/steam-roxie-parser.git
cd steam-roxie-parser
pip install -e .[test]
```

## Running the tests

The tests are plain `unittest.TestCase` classes. Run them in parallel across all cores with
[`unittest-parallel`](https://pypi.org/project/unittest-parallel/) (installed by the `[test]` extra):

```bash
python -m unittest_parallel -t . -s tests -p "test_*.py" --level=test -j 0
```

`-j 0` uses all CPUs and `--level=test` distributes at the individual test-method level (each magnet is its own
test method, so they fan out across processes). Expect `Ran 187 tests ... OK` in ~2 minutes on a many-core machine.

Or run serially with only the standard library (no extra tools):

```bash
python -m unittest discover -s tests -t . -p "test_*.py"
```

### In PyCharm

Add a **Python** run configuration (not the "Python tests" type, since `unittest-parallel` is a custom runner):

1. **Run | Edit Configurations… | `+` | Python**
2. In the target dropdown switch from "Script path" to **Module name**, and enter `unittest_parallel`
3. **Parameters**: `-t . -s tests -p test_*.py --level=test -j 0`
4. **Working directory**: the project root
5. **Python interpreter**: the environment where `unittest-parallel` is installed (the `[test]` extra)
6. **OK**, then run it with ▶

A ready-made copy of this configuration is at `.idea/runConfigurations/Tests_unittest_parallel.xml`. Note that
`.idea/` is gitignored, so on a fresh checkout recreate it with the steps above.

## Usage

### Multipole magnets (parsed from ROXIE files)

```python
from pathlib import Path
from steam_roxie_parser.parsers.parser_roxie import ParserRoxie

parser = ParserRoxie()
roxie_data = parser.get_data(
    dir_data=Path("MQXA.data"),        # coil definition
    dir_cadata=Path("roxie.cadata"),   # cable database
    dir_iron=Path("MQXA.iron"),        # iron yoke (optional)
    path_to_yaml_model_data="modelData_MQXA.yaml",  # optional, needed for ribbon cables
)

# Write the .geom file used by FiQuS and PySIGMA
from steam_roxie_parser.utils.geom_file import write_geom_file
write_geom_file(roxie_data, "MQXA_FiQuS.geom")
```

`get_data` runs the two stages; you can also run them separately, e.g. to dump the parsed ROXIE file content to
yaml, edit it, and rebuild the geometry:

```python
from steam_roxie_parser.utils.raw_data_file import write_raw_data_yaml_files, read_raw_data_yaml_files

parser = ParserRoxie()

# Stage 1: parse the .data / .cadata / .iron files into the raw data structure
raw = parser.parse_roxie_input_files(dir_data=Path("MQXA.data"),
                                  dir_cadata=Path("roxie.cadata"),
                                  dir_iron=Path("MQXA.iron"))

# Dump the content of each ROXIE file to its own separate yaml file (and read them back)
write_raw_data_yaml_files(raw, dir_cadata_yaml="MQXA_cadata.yaml",
                          dir_data_yaml="MQXA_data.yaml", dir_iron_yaml="MQXA_iron.yaml")
raw = read_raw_data_yaml_files(dir_cadata_yaml="MQXA_cadata.yaml",
                               dir_data_yaml="MQXA_data.yaml", dir_iron_yaml="MQXA_iron.yaml")

# Stage 2: build the geometry from the (possibly yaml-roundtripped) raw data structure
roxie_data = parser.build_geometry(raw_data=raw)
```

### Solenoids (built from a coil definition, not from ROXIE files)

Solenoid geometry does not come from ROXIE input files — it is built directly from a coil definition. This
capability moved here from `steam_sdk.builders.Solenoids`. `Solenoid_magnet` turns per-section coil dimensions
(`SolenoidCoilInfo`) and conductor data (`ConductorInfo`) into the **same** `RoxieData` geometry the multipole
parser produces, so it writes an identical `.geom` file for FiQuS / PySIGMA. Field-map and self-inductance
calculations use [pysoleno](https://pypi.org/project/pysoleno/) (install the `solenoid` extra); the geometry
itself works without it.

```python
from steam_roxie_parser.data.data_model_info import CableInfo, ConductorInfo, SolenoidCoilInfo, StrandInfo
from steam_roxie_parser.parsers.solenoids import Solenoid_magnet
from steam_roxie_parser.utils.geom_file import write_geom_file

# one SolenoidCoilInfo per coil section: inner/outer radius (a1/a2), axial extent (b1/b2),
# turns-per-layer (ntpl) and number of layers (nl)
coil = SolenoidCoilInfo(name="ColSol", a1=0.09175, a2=0.11095, b1=0.0, b2=0.200134,
                        conductor_name="HEL_W1", ntpl=121, nl=16, section=1)
conductor = ConductorInfo(name="HEL_W1",
                          cable=CableInfo(th_insulation_along_width=1.075e-4, th_insulation_along_height=2e-5),
                          strand=StrandInfo(bare_width=0.00101, bare_height=0.00161))

roxie_data = Solenoid_magnet(coils=[coil], conductors=[conductor], Iref=100.0).build_geom_object()
write_geom_file(roxie_data, "MLEC_FiQuS.geom")   # same .geom format as the multipole magnets
```

### Field maps and plotting

```python
from steam_roxie_parser.parsers.parser_map2d import ParserMap2dFile
parameters = ParserMap2dFile(map2dFile=Path("MQXA.map2d")).get_parameters_from_map2d(headerLines=1)

from steam_roxie_parser.plotters import plotter_roxie
plotter_roxie.plot_all(roxie_data)
plotter_roxie.save_svg(roxie_data, "MQXA.svg")   # or save_png(...) for a quick image
```

## STEAM

This repository is part of the STEAM framework maintained by the TE-MPE-PE section at CERN.
Contact: steam-team@cern.ch

## License

This project is licensed under the GNU General Public License v3.0 — see the [LICENSE](LICENSE) file for details.
