Metadata-Version: 2.4
Name: gds2openEMS
Version: 0.7.0
Summary: Create and run openEMS FDTD model from RFIC GDSII layout file
Author-email: Volker Muehlhaus <volker@muehlhaus.com>
License-Expression: GPL-3.0-or-later
Project-URL: Homepage, https://github.com/VolkerMuehlhaus/gds2openEMS
Project-URL: Repository, https://github.com/VolkerMuehlhaus/gds2openEMS
Project-URL: Changelog, https://github.com/VolkerMuehlhaus/gds2openEMS/blob/main/doc/CHANGES.md
Requires-Python: >=3.9
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: gdspy>1.6.0
Requires-Dist: CSXCAD
Requires-Dist: openEMS
Requires-Dist: numpy
Requires-Dist: shapely
Requires-Dist: PySide6
Dynamic: license-file

# gds2openEMS: FDTD simulation workflow for RFIC

The files provided here enable openEMS EM simulation with layouts
created for the IHP SG13G2 RFIC technology.

![Intro](https://raw.githubusercontent.com/VolkerMuehlhaus/gds2openEMS/main/doc/png/gds2openEMS_banner.png)

## Install

    pip install gds2openEMS

**Full documentation, installation guide, and usage walkthrough:**
https://github.com/VolkerMuehlhaus/gds2openEMS

## Recent changes

## 03-Nov-2026 (0.7.0)

**Bugfix: ports that excite nothing.** A lumped port acts only on the mesh edges inside its box. A via port drawn as a zero-width line needs a mesh line exactly in its plane, and the xy mesh could lose it: lines closer than 0.8 × `refined_cellsize` are merged, and two port edges of equal weight were replaced by one line in the middle, e.g. the planes of two ports 0.1 µm apart. Such a port injected no energy, its probes stayed 0, and openEMS never reached `energy_limit`. Found with a 15-port export from the KLayout plug-in klayout_em_export, where 3 ports were dead at `refined_cellsize` 0.5. After the xy mesh, every port without a mesh line in x or y now gets one, at its center or in the plane of a zero-width port ("Port meshing: added N mesh line(s)"). Other ports and the rest of the mesh are unchanged.

**Ports with different reference impedances:** S-parameters are now ratios of power waves, `S_ij = uf_ref_i / uf_inc_j · sqrt(Z0_j / Z0_i)` (`utilities.power_wave_ratio()`). Before, the plain ratio of voltage waves gave wrong values when the ports had different `port_Z0`, e.g. S21 ≠ S12 for a plain connection between a 50 Ω and a 25 Ω port. With equal impedances nothing changes. `simulation_ports.get_reference_impedance()` returns one value as before when all ports have the same `port_Z0`, else the list of port impedances, and `utilities.write_snp()` then writes a Touchstone 2.0 file with a `[Reference]` line (Touchstone 1.0 has only one reference impedance). Before, `get_reference_impedance()` stopped the script with "All ports must use the same reference impedance". The Y and Z parameter functions still require equal impedances.

**GDSII layers read once:** in the SG13G2 stackups, GDS layer 36 makes two stackup layers (the MIM top plate and the MIM dielectric below it), and every polygon is given to both. `read_gds()` read such a layer once per stackup layer, so every MIM polygon was in the model twice, and openEMS warned "Unused primitive ... MIM_equiv" and "... TopVia1" for the second copies. Now each GDS layer number is read once. The S-parameters do not change (MIM capacitor example: identical). `read_gds()` also no longer adds `gds_boundary_layers` to the caller's layer list. `more_examples/local_modules_copy` is synced with `workflow/modules` again.

Tests: `tests/test_port_meshing.py`, `tests/test_gds_reader_layers.py`.

**Stackup files moved:** the XML stackup files from `workflow/` are now in [`XML_stackup/legacy/`](../XML_stackup/legacy/), with the same file names and content. The `workflow/` examples read them from there. Models that used a stackup from `workflow/` by relative path need the new folder.

**New stackup files:** [`XML_stackup/latest/IHP/`](../XML_stackup/latest/IHP/) has the latest openEMS stackups `SG13G2_FDTD_300um.xml`, `SG13G2_FDTD_300um_passicut.xml` and `SG13CMOS5L_FDTD_300um.xml`. Chip height and air height are `<Variables>` (`total_thickness`, `air_thickness`), so one file replaces the legacy files for different substrate heights. SUBGND is a PEC sheet on layer 250 (legacy: a LOWLOSS volume on layer 210), BACKSIDEGND a PEC sheet on layer 251, and the RSIL/RPPD/RHIGH resistors and the REF_FOR_TRANSISTOR sheet are included. The metal, via and MIM positions are the same as in the legacy files; the default total height is 300 µm, the same as the legacy `SG13G2.xml` and `SG13CMOS5L.xml` used by the `workflow/` examples (for SG13G2 the substrate is 0.12 µm thicker, so the total height is exactly 300 µm). See [the README](../XML_stackup/latest/IHP/README.md) for which file replaces each legacy file. The files are installed with the pip package: `utilities.get_stackup_path('SG13G2_FDTD_300um.xml')` returns the full path of one, `utilities.get_stackup_path()` without an argument prints the folder and the available files. Legacy stackups are not included in the package.

## 31-Oct-2026 (0.6.7)

**Ports with several polygons:** several separate polygons on one port layer are now the elements of one port, in parallel, e.g. for a terminal on several transistor fingers. Each element gets N·Z0, so the port is terminated with Z0; the port voltage is the mean of the element voltages, the port current their sum. Touching polygons are one element. Before, every polygon became a port of its own with the same number: openEMS stopped with "a probe named port_ut_1 already exists", or, in older openEMS versions, ran with the port terminated by Z0/N and wrong S-parameters. Resonance estimation and `deembed_openEMS.py` (element inductances in parallel) know the elements. See "Ports with several polygons" in the user's guide.

## 31-Oct-2026 (0.6.6)

**Bugfix:** `direction='-z'` did not reverse a via port: the sign only swapped the port's x and y extent, and the port
kept the polarity of `'z'`. A via port with `'-z'` now runs from its upper to its lower metal, the opposite polarity of
`'z'`. In-plane ports (`'-x'`, `'-y'`) are unchanged. Models that use `'-z'` get the opposite sign for that port's
S-parameters than before.

The `core_transistor_3port_bce` example now explains how to use the 5-port result in a circuit simulator: the
transistor model at ports 3, 4 and 5, inductors for the DC bias, and the 0 Hz point. See
[its README](../more_examples/core_transistor_3port_bce/README.md#using-the-result-in-a-circuit-simulator).

Full history: [CHANGES.md](https://github.com/VolkerMuehlhaus/gds2openEMS/blob/main/doc/CHANGES.md)

---

**Note:** the `gds2openEMS` PyPI package itself only requires:

- gdspy>1.6.0
- CSXCAD
- openEMS
- numpy
- shapely
- PySide6

(Other Python modules mentioned above are only needed to run the example/model scripts in this repository, not to use the installed package.)
