# Model Comparison to Qucs-S

This notebook compares the S-parameter models from qpdk (using sax) against reference
results from Qucs-S simulations. Each test suite validates a different component model.

The comparisons include:

- Polar plots showing S-parameters in the complex plane
- Magnitude and phase plots versus frequency
- Visual validation of model accuracy against reference data

```ipython3
import sys

if "google.colab" in sys.modules:
    import subprocess

    print("Running in Google Colab. Installing QPDK...")
    subprocess.check_call([
        sys.executable,
        "-m",
        "pip",
        "install",
        "-q",
        "qpdk[models] @ git+https://github.com/gdsfactory/quantum-rf-pdk.git",
    ])
```

```ipython3
import importlib.util
import inspect

import numpy as np
from IPython.display import Markdown, display

from qpdk import PDK
from qpdk.config import PATH as QPDK_PATH

# Load the test module directly by file path (avoids sys.path manipulation)
_spec = importlib.util.spec_from_file_location(
    "test_compare_to_qucs",
    QPDK_PATH.tests / "models" / "test_compare_to_qucs.py",
)
if _spec is None or _spec.loader is None:
    raise ImportError("Cannot load test_compare_to_qucs.py")
_test_module = importlib.util.module_from_spec(_spec)
_spec.loader.exec_module(_test_module)
BaseCompareToQucs = _test_module.BaseCompareToQucs

PDK.activate()
```

## Discover Test Suites

Dynamically discover all test suite classes that compare qpdk models to Qucs-S results.
We find all classes that:

1. Are subclasses of `BaseCompareToQucs`
2. Are not the base class itself
3. Are concrete classes (not abstract)

```ipython3
def discover_test_suites() -> list[type[BaseCompareToQucs]]:
    """Discover all test suite classes for Qucs-S comparison.

    Returns:
        List of test suite classes that inherit from :class:`~BaseCompareToQucs`.
    """
    test_suites = []

    # Get all members of the module (loaded via importlib above)
    for _name, obj in inspect.getmembers(_test_module):
        # Check if it's a class
        if not inspect.isclass(obj):
            continue

        # Check if it's a subclass of BaseCompareToQucs but not the base class itself
        if not issubclass(obj, BaseCompareToQucs) or obj is BaseCompareToQucs:
            continue

        # Check if it's a concrete class (not abstract)
        if inspect.isabstract(obj):
            continue

        test_suites.append(obj)

    return test_suites
```

```ipython3
# Discover all available test suites
test_suites = discover_test_suites()
print(f"Found {len(test_suites)} test suite(s):")
for suite in test_suites:
    print(f"\t· {suite.__name__}")
```

```myst-ansi
Found 5 test suite(s):
	· TestCPWCompareToQucs
	· TestCapacitorCompareToQucs
	· TestCouplerStraightCompareToQucs
	· TestInductorCompareToQucs
	· TestLCResonatorCompareToQucs
```

## Model Comparison

Compare the S-parameter models against Qucs-S reference data.

```ipython3
# Find and plot all test suites

for suite in test_suites:
    test_instance = suite()
    display(Markdown(f"### {test_instance.component_name}"))
    display(Markdown(f"**Test Suite:** `{suite.__name__}`"))

    param_strs = [
        f"{p.name} = {p.value / p.unit:.2f} × 10^{int(np.log10(p.unit))}"
        for p in sorted(test_instance.parameters, key=lambda x: x.name)
    ]
    param_text = "**Parameters:**\n" + "\n".join(f"- {p}" for p in param_strs)
    display(Markdown(param_text))

    display(Markdown(f"**CSV:** `{test_instance.csv_filename}`"))
    test_instance.plot_comparison()
```

### Coplanar waveguide

**Test Suite:** `TestCPWCompareToQucs`

**Parameters:**

- length = 10000000000.00 × 10^-6

**CSV:** `cpw_w10_s_6_l10mm.csv`

```myst-ansi
<Figure size 3600x1800 with 3 Axes>
```

### Capacitor

**Test Suite:** `TestCapacitorCompareToQucs`

**Parameters:**

- capacitance = 60.00 × 10^-15

**CSV:** `capacitor_qucs.csv`

```myst-ansi
<Figure size 3600x1800 with 3 Axes>
```

### Coupler Straight

**Test Suite:** `TestCouplerStraightCompareToQucs`

**Parameters:**

- gap = 1520000.00 × 10^-6
- length = 500000000.00 × 10^-6

**CSV:** `coupler_straight_qucs.csv`

```myst-ansi
<Figure size 3600x1800 with 3 Axes>
```

### Inductor

**Test Suite:** `TestInductorCompareToQucs`

**Parameters:**

- inductance = 10.00 × 10^-9

**CSV:** `inductor_qucs.csv`

```myst-ansi
<Figure size 3600x1800 with 3 Axes>
```

### LC Resonator

**Test Suite:** `TestLCResonatorCompareToQucs`

**Parameters:**

- capacitance = 10.00 × 10^-15
- inductance = 10.00 × 10^-9

**CSV:** `lc_resonator_qucs.csv`

```myst-ansi
<Figure size 3600x1800 with 3 Axes>
```

## Summary

The plots above show comparisons between qpdk models (dashed lines) and Qucs-S
reference simulations (solid lines) for various passive components:

- **Left plot**: Polar representation showing S-parameters in the complex plane
- **Right plot**: Magnitude (in dB) and phase (in radians) versus frequency

Good agreement between the models validates the accuracy of the qpdk implementations
for use in circuit simulations and design optimization.
