Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 13 additions & 5 deletions .claude/CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,20 @@ models of RFIC passives. A run is a pipeline: generate GDS layouts with
gdsfactory → convert them for Palace (`gds2palace`) → EM-simulate with Palace →
train a PyTorch model on the S-parameters → export it to ONNX → test it. The
ONNX models are consumed by COBRA (`../COBRA`), which reads the
`input_parameter_ranges` and `physics_guarantees` metadata ORCA writes.
`input_parameter_ranges`, `input_constraints` and `physics_guarantees` metadata
ORCA writes.

## Repository Structure

- `src/orca/orca.py`: the `ORCA` runner; sorts stages by `index` and runs them
over one `PipelineContext`. Output goes to `output/<geometry name>/`.
- `src/orca/pipeline/`: `PipelineStage` base, `PipelineContext`, and the stages
in fixed order: `GDSGenerator` (0), `GDSConverter` (1), `PalaceSimulator` (2),
`ModelTrainer` (4), `OnnxExporter` (5), `ModelTester` (6).
in fixed order: `GDSGenerator` (0), `DRCChecker` (1), `GDSConverter` (2),
`PalaceSimulator` (3), `ModelTrainer` (4), `OnnxExporter` (5), `ModelTester` (6).
- `src/orca/geometry/`: `BaseGeometry` contract, `InputParameterIterator`,
layer stackups, reusable cells, and presets (`inductor_octa`,
layer stackups, the SG13G2 design rules (`drc.py`: grid snapping and KLayout
checks used by `DRCChecker`; geometry code does not snap itself), reusable
cells, and presets (`inductor_octa`,
`tf_octa_c_ports`) with their `.simcfg`/`.xml` package data.
- `src/orca/simulation/`: GDS→Palace conversion, Palace launchers (local,
Apptainer, Slurm), and Touchstone result merging.
Expand All @@ -38,7 +41,12 @@ pipeline orchestration, and GUI stay in their own packages.
say which stage was skipped, not fail on a `KeyError`.
- **Geometries:** a `BaseGeometry` subclass provides `name`, `stackup_xml`,
`simconfig_filename`, `input_parameter_iterator`, `create_gds_file`, and
`create_dataset`. Presets are examples of the contract; a change to the
`create_dataset`; `is_feasible(params)` is optional and rejects draws before
they are drawn, and `feasibility_constraints()` states the same rules as
expressions (`geometry/constraints.py` grammar) for the ONNX metadata — a
test must keep the two in agreement. Never clamp or repair parameters inside `create_gds_file` —
the parameter table records the requested values, so the model would learn a
geometry that was not built. Presets are examples of the contract; a change to the
contract updates the presets and `docs/custom_class.md`.
- **Public API:** everything importable as `orca.X` is listed in
`src/orca/__init__.py`. Anything that needs PyTorch goes in
Expand Down
34 changes: 23 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,7 @@ geometry = TransformerOcta()
orca_instance = ORCA(
[
orca.GDSGenerator(num_samples=1000),
orca.DRCChecker(),
orca.GDSConverter(),
orca.PalaceSimulator(palace_executable="palace"),
orca.ModelTrainer(),
Expand All @@ -200,6 +201,7 @@ For large-scale simulation runs, we provide an OpenStack VM image and a CLI cont
| Stage | Class | Description |
|-------|-------|-------------|
| GDS generation | `GDSGenerator` | Creates parameterized GDS layout files from a geometry class |
| Design-rule check | `DRCChecker` | Snaps layouts to the manufacturing grid and drops those violating the IHP SG13G2 rules |
| GDS conversion | `GDSConverter` | Converts GDS files to Palace-compatible simulation meshes using gds2palace |
| EM simulation | `PalaceSimulator` | Runs full-wave EM simulations in Palace and stores results as Touchstone files |
| Model training | `ModelTrainer` | Trains a PyTorch MLP to map geometry parameters + frequency to S-parameters |
Expand All @@ -217,10 +219,16 @@ ORCA runs a linear pipeline. Each stage receives a context dictionary and adds i
│ ORCA pipeline │
│ │
│ ┌──────────────┐ GDS files ┌──────────────────┐ │
│ │ GDSGenerator │───────────────▶│ GDSConverter │ │
│ │ │ │ (gds2palace mesh) │ │
│ │ GDSGenerator │───────────────▶│ DRCChecker │ │
│ │ │ │ (grid + SG13G2) │ │
│ └──────────────┘ └────────┬─────────┘ │
│ │ mesh files │
│ │ clean layouts │
│ ▼ │
│ ┌──────────────────┐ │
│ │ GDSConverter │ │
│ │ (gds2palace mesh)│ │
│ └────────┬─────────┘ │
│ │ mesh files │
│ ▼ │
│ ┌──────────────────┐ │
│ │ PalaceSimulator │ │
Expand All @@ -247,13 +255,17 @@ ORCA runs a linear pipeline. Each stage receives a context dictionary and adds i

### Stage 1 — GDS generation (`GDSGenerator`)

The geometry class's `input_parameter_iterator` samples parameter combinations (randomly or on a grid). For each combination, `create_gds_file()` is called to produce a GDS layout file. The number of samples is set by `num_samples`; `seed` makes the `"random"` picking strategy reproducible.
The geometry class's `input_parameter_iterator` samples parameter combinations (randomly or on a grid). For each combination, `create_gds_file()` is called to produce a GDS layout file. The number of samples is set by `num_samples`; `seed` makes the `"random"` picking strategy reproducible. Every draw is first passed to the geometry's `is_feasible()`; combinations it rejects (a winding that does not fit its diameter, a feed gap wider than the octagon's side) are counted and, with the `"random"` strategy, redrawn, so `num_samples` buildable layouts come out. A geometry that still raises `ValueError` in `create_gds_file()` costs a sample, and the stage warns when it delivered fewer layouts than requested.

### Stage 2 — Design-rule check (`DRCChecker`)

Every generated layout is snapped to the manufacturing grid (5 nm for SG13G2) and checked against the IHP SG13G2 back-end design rules with KLayout: off-grid vertices, edge angles, acute corners, minimum metal width and spacing, and via size, spacing and enclosure. The rule names follow the PDK's KLayout deck (`TM2.a`, `TV2.d`, ...). Off-grid vertices are repaired in place; layouts with remaining violations are reported in `<name>_drc_report.csv` and left out of the later stages, so parameter combinations that draw unbuildable geometry never reach the simulator or the model.

### Stage 2 — GDS conversion (`GDSConverter`)
### Stage 3 — GDS conversion (`GDSConverter`)

Each GDS file is converted to a Palace-ready simulation setup using [gds2palace](https://github.com/VolkerMuehlhaus/gds2palace_ihp_sg13g2). The geometry's `stackup_xml` defines the physical layer stackup and material properties; the `simconfig_filename` defines the simulation parameters (port positions, frequency sweep, mesh settings).
Each GDS file that passed DRC is converted to a Palace-ready simulation setup using [gds2palace](https://github.com/VolkerMuehlhaus/gds2palace_ihp_sg13g2). The geometry's `stackup_xml` defines the physical layer stackup and material properties; the `simconfig_filename` defines the simulation parameters (port positions, frequency sweep, mesh settings).

### Stage 3 — EM simulation (`PalaceSimulator`)
### Stage 4 — EM simulation (`PalaceSimulator`)

Palace runs a full-wave finite-element EM simulation for each layout variant and writes the S-parameters to a Touchstone file (`.sNp`). Simulations are distributed across available CPU cores. The `palace_executable` argument can point to a local binary or a container invocation (e.g. `apptainer exec palace.sif palace`).

Expand All @@ -264,15 +276,15 @@ Several simulations can run at once, each already parallelized internally with M

Palace is memory-bandwidth bound, so several smaller simulations confined to their own NUMA domain usually give a higher throughput than one simulation spread over a whole node — as long as one simulation fits into a domain's memory (use `bind="socket"` otherwise). The layout is derived from the machine or allocation at runtime, so `num_parallel_sims` and `num_processes` are capped to what is actually available. Pass `save_log=True` to keep each simulation's full Palace output in `palace.log` in its simulation folder (off by default, Palace prints a lot); failures are reported either way.

### Stage 4 — Model training (`ModelTrainer`)
### Stage 5 — Model training (`ModelTrainer`)

A PyTorch MLP is trained on the simulation data. Inputs are geometry parameters and frequency; outputs are the real and imaginary parts of each S-parameter entry. Normalization is defined in the geometry's dataset and applied automatically. An optional basis expansion of the inputs — for example a Chebyshev expansion of frequency — is chosen on the stage itself with `ModelTrainer(basis="chebyshev")`; it lives inside the model, so it is tuned with it and exported into the ONNX graph. Hyperparameters such as learning rate, batch size, and network depth can be passed to `ModelTrainer`.

### Stage 5 — ONNX export (`OnnxExporter`)
### Stage 6 — ONNX export (`OnnxExporter`)

The trained PyTorch model is exported to ONNX format with a fixed frequency sweep as part of the model signature. The resulting `.onnx` file is self-contained and can be run with `onnxruntime` — no PyTorch installation required at inference time.
The trained PyTorch model is exported to ONNX format with a fixed frequency sweep as part of the model signature. The resulting `.onnx` file is self-contained and can be run with `onnxruntime` — no PyTorch installation required at inference time. The metadata carries `input_parameter_ranges`, `input_constraints` (which part of those ranges is buildable — the part the model was trained on) and `physics_guarantees` for the consumer.

### Stage 6 — Model testing (`ModelTester`)
### Stage 7 — Model testing (`ModelTester`)

The ONNX model is loaded and evaluated against held-out simulation data. Prediction errors are logged to help assess whether the surrogate is accurate enough for use in COBRA.

Expand Down
24 changes: 24 additions & 0 deletions docs/custom_class.md
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,30 @@ The Python class should be a `@dataclass` extending `orca.BaseGeometry` and must
- `create_gds_file(name, output_path, params) -> str` — Generates a GDS layout file from geometry parameters. Returns the path to the created file.
- `create_dataset() -> BaseDataset` — Builds the dataset (e.g. `GeoToSParamDatasetSingleFrequency`) with its output codec and normalizers, used for training. It is called once per geometry instance, the first time `geometry.dataset` is read, so each instance gets its own dataset and normalizer statistics.

**Optional methods:**

- `feasibility_constraints() -> list[str]` — The same rules as `is_feasible()`, written as boolean expressions over the input parameter names, e.g. `"bottom_linewidth <= bottom_winding_diameter / 3"`. `OnnxExporter` stores them in the model's `input_constraints` metadata, so COBRA can refuse a query for a geometry that cannot be built instead of returning a prediction the model was never trained for. The grammar is a small subset of Python (arithmetic, comparisons, `and`/`or`/`not`, `a if c else b`, and `abs min max sqrt sin cos tan radians ceil floor round`, plus `pi` and `sqrt2`), documented in `orca.geometry.constraints`; anything else is rejected at export. Derive the strings from the same numbers as `is_feasible()` and add a test that they agree on random draws (see `tests/test_constraints.py` for the presets' version).
- `is_feasible(params) -> bool` — Whether a parameter combination describes a layout that can be drawn (default: always `True`). `GDSGenerator` calls it for every draw of the iterator; rejected draws are counted and, with the `"random"` strategy, redrawn, so the requested number of samples is met with buildable layouts only. Put cheap, closed-form constraints between parameters here — a winding that must fit its diameter, a feed gap that must fit the octagon's side. The presets derive it from the same check their cell code runs, so the two cannot disagree.

!!! warning "Reject, never clamp"

Do not repair a bad parameter inside `create_gds_file()` (for example clamp a
too-small diameter to the buildable minimum). The parameter table records the
*requested* values, so a repaired layout trains the model on a geometry it does
not have, and the surrogate then returns confident results for inputs that
were never built. Reject the draw in `is_feasible()` and raise `ValueError`
in `create_gds_file()` as the safety net.

!!! note "Grid snapping and design rules are not the geometry's job"

Draw the layout and return; do not snap vertices to the manufacturing grid or
re-implement design-rule checks in `create_gds_file()`. The `DRCChecker` stage
snaps every generated GDS file to the SG13G2 grid and drops layouts that break
the PDK's metal and via rules (see [Pipeline Stages](pipeline.md)). It is the
safety net behind `is_feasible()`, not a substitute for it: a rule that can be
written down belongs in `is_feasible()`, where it costs nothing and keeps the
sample count honest.

!!! tip "Keep the training imports inside `create_dataset()`"

The dataset classes and normalizers need PyTorch, which is an optional
Expand Down
4 changes: 3 additions & 1 deletion docs/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,8 @@ ORCA automates the full loop from geometry to a trained, exported surrogate mode
```mermaid
flowchart LR
A[Geometry Class] --> B[GDSGenerator]
B --> C[GDSConverter]
B --> X[DRCChecker]
X --> C[GDSConverter]
C --> D[PalaceSimulator]
D --> E[S-Parameter Dataset]
E --> F[ModelTrainer]
Expand All @@ -81,6 +82,7 @@ flowchart LR
| Stage | Purpose |
|---|---|
| `GDSGenerator` | Samples geometry parameters and writes GDS layout files |
| `DRCChecker` | Snaps layouts to the manufacturing grid and drops those violating the SG13G2 design rules |
| `GDSConverter` | Converts GDS files to Palace-compatible mesh inputs |
| `PalaceSimulator` | Runs full-wave EM simulations and stores Touchstone results |
| `ModelTrainer` | Trains a PyTorch neural network on the simulation dataset |
Expand Down
Loading
Loading