Getting started: increasing test coverage

In this section we will add additional tests to our library, so that we cover more views and behavioral aspects of the cells. The higher the test coverage, the higher the chance to detect errors early on.

Add a circuit model reference test

The SMatrix reference test Compare.SMatrix compares the S-matrices generated by the component’s circuit model to those saved in a reference file. Adding it is done by adding Compare.SMatrix to the comparisons mark and generating the reference file:

from ip_manager.testing import ComponentReferenceTest, Compare
import pytest
import numpy as np


@pytest.mark.comparisons([Compare.LayoutToGds, Compare.LayoutToXML, Compare.NetlistToXML, Compare.SMatrix])
class TestDiskResonator(ComponentReferenceTest):
    @pytest.fixture
    def component(self):
        # Create and return a basic disk resonator.
        from my_library.all import DiskResonator
        my_disk = DiskResonator(name='my_disk')
        my_disk.Layout(disk_radius=10.0)
        return my_disk

    @pytest.fixture
    def wavelengths(self):
        return np.arange(1.3, 1.8, 0.0001)
  • The PCell will need a CircuitModelView definition respectively, if not the test will fail.
  • This S-model is calculated over a specific wavelength range that can be configured by adding the wavelengths fixture to the TestDiskResonator class. The default wavelength range is 1.4 to 1.7 micrometres.

A small tolerance is allowed before two S-models are considered unequal, see S-model testing for more information.

In order to generate the reference file for this additional test only, we can rerun generate_reference_files(path, match_tests) using the match_tests argument to only process tests that have ‘smatrix’ in their name:

from ip_manager.testing import generate_reference_files
generate_reference_files(path="", match_tests="smatrix")

Alternatively, you can run pytest with the -k argument. (For regenerating reference files, see Generating the known-good reference files)

pytest tests --regenerate -k smatrix

This will only process the tests that have smatrix in the name. Users can pass the -k argument to pytest to filter tests in a flexible way.

Add a layout-versus-netlist test

IP Manager can use the IPKISS netlist extraction features to perform a type of layout-versus-schematic (LVS) or rather layout-versus-netlist comparison: it will extract a netlist from the LayoutView (ports, instances of other cells and their connectivity) and compare it with the NetlistView of the component. (Note this is mostly useful for components or circuits which do not use i3.NetlistFromLayout).

If the netlist extraction raises warnings or errors e.g. because of bad connections due to mismatched waveguides, you will get these for free as well!

Implementing this test is done by inheriting from ip_manager.testing.LayoutToNetlistTest:

# extra import
from ip_manager.testing import LayoutToNetlistTest

# test class inheriting ``LayoutToNetlistTest``
 class TestDiskResonatorLVS(LayoutToNetlistTest):
     @pytest.fixture
     def component(self):
         # Create and return a basic disk resonator.
         from team_library.all import DiskResonator
         my_disk = DiskResonator(name='my_disk')
         my_disk.Layout(disk_radius=11.0)
         return my_disk

Sharing settings across tests

If multiple components share the same set of tests, we can define one variable to specify the tests:

from ip_manager.testing import ComponentReferenceTest, Compare
import pytest

comparisons = [Compare.LayoutToGds, Compare.LayoutToXML]

@pytest.mark.comparisons(comparisons)
class TestDiskResonator(ComponentReferenceTest):
    @pytest.fixture
    def component(self):
        # Create and return a basic disk resonator.
        from diskres_cell import DiskResonator
        my_disk = DiskResonator(name='my_disk')
        my_disk.Layout(disk_radius=10.0)
        return my_disk

@pytest.mark.comparisons(comparisons)
class TestDiskResonator2(ComponentReferenceTest):
    @pytest.fixture
    def component(self):
        # Create and return a basic disk resonator.
        from diskres_cell import DiskResonator
        my_disk = DiskResonator(name='my_disk')
        my_disk.Layout(disk_radius=15.0)
        return my_disk

Add your own circuit checks

IP Manager also allows you to define custom circuit model tests on the S-parameters.

This is done by inheriting from CircuitModelTest and implementing a custom test method that creates an SMatrixTester object.

This can be combined in the same class as the component reference tests but could also be done in a separate test class.

See the full code below for an example:

from ip_manager.testing import ComponentReferenceTest, CircuitModelTest, SMatrixTester, Compare

import numpy as np
import pytest


@pytest.mark.comparisons([Compare.NetlistToXML, Compare.LayoutToGds, Compare.SMatrix])
class TestDiskResonatorPeaks(ComponentReferenceTest, CircuitModelTest):

    @pytest.fixture
    def component(self):
        # Create and return a basic disk resonator.
        from team_library.all import DiskResonator
        my_disk = DiskResonator(name='my_disk')
        my_disk.Layout(disk_radius=10.0)
        my_disk.CircuitModel(disk_n_eff=3.0)
        return my_disk

    # Used by circuit model tests (optional)
    @pytest.fixture
    def wavelengths(self):
        return np.arange(1.3, 1.8, 0.0001)

    # You can specify your own tests in this method.
    # `smatrix` is a fixture provided by `CircuitModelTest` so you don't need to care about calculating it yourself.
    def test_disk_smatrix(self, smatrix):

        # Create a tester object
        tester = SMatrixTester(smatrix, central_wl=1.55)

        # Perform general tests
        assert tester.is_reciprocal(), "Component is not reciprocal"
        assert tester.is_passive(), "Component is active"

        # Select a terminal link
        tester.select_ports('in', 'out')  # from 'in' to 'out'

        # Perform tests on this link
        assert tester.has_peak_at(centre=1.5, rtol_centre=0.001, test_fwhm=False), "No peak at 1.5 um"
        assert tester.has_peak_at(centre=1.579, rtol_centre=0.001, fwhm=0.004, rtol_fwhm=0.1), "No peak at 1.579 um, or wrong FWHM"
        assert tester.fsr_isclose(0.08, rtol=0.01), "Free spectral range is wrong"
        assert tester.il < 0.1, "Insertion loss is too high"

Standard python assert statements are used. These will raise an error and make the test fail when the statement after assert is not True. Pytest has several functions available for testing that you can use as well, such as pytest.approx.

Try changing the disk_n_eff value and you’ll see that the test fails when its value differs from 3.0. If HTML reporting (see HTML output) is enabled, a plot of the S-matrix will be automatically inserted for easy inspection. In this case, the peaks would visibly shift away from 1.5 and 1.579 micrometres.

A complete example file can be downloaded here: test_diskresonator_smatrix.py. Note that, in this file, we added a test that fails in order to simulate a situation in which modifications resulted in a regression.