diff --git a/.github/workflows/build-and-test.yml b/.github/workflows/build-and-test.yml index 1b5e3fb3..c9dc1db2 100644 --- a/.github/workflows/build-and-test.yml +++ b/.github/workflows/build-and-test.yml @@ -5,9 +5,9 @@ jobs: build: runs-on: ${{ matrix.os }} steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v2 - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v4 + uses: actions/setup-python@v1 with: python-version: ${{ matrix.python-version }} - name: Install dependencies @@ -23,7 +23,7 @@ jobs: strategy: matrix: os: [ubuntu-latest, windows-latest, macos-latest] - python-version: ['3.7', '3.8', '3.9', '3.10', '3.11.0'] + python-version: ['3.7', '3.8', '3.9', '3.10'] name: build and test on: pull_request: diff --git a/.sourcery.yaml b/.sourcery.yaml new file mode 100644 index 00000000..f300aba4 --- /dev/null +++ b/.sourcery.yaml @@ -0,0 +1,2 @@ +refactor: + python_version: '3.7' diff --git a/README.md b/README.md index 264be800..d5220120 100644 --- a/README.md +++ b/README.md @@ -44,23 +44,3 @@ The documentation is hosted [online](https://simphonyphotonics.readthedocs.io/en Changelogs can be found in docs/changelog/. There is a changelog file for each released version of the software. - -## Bibtex citation - -``` -@article{DBLP:journals/corr/abs-2009-05146, - author = {Sequoia Ploeg and - Hyrum Gunther and - Ryan M. Camacho}, - title = {Simphony: An open-source photonic integrated circuit simulation framework}, - journal = {CoRR}, - volume = {abs/2009.05146}, - year = {2020}, - url = {https://arxiv.org/abs/2009.05146}, - eprinttype = {arXiv}, - eprint = {2009.05146}, - timestamp = {Thu, 17 Sep 2020 12:49:52 +0200}, - biburl = {https://dblp.org/rec/journals/corr/abs-2009-05146.bib}, - bibsource = {dblp computer science bibliography, https://dblp.org} -} -``` diff --git a/docs/source/index.rst b/docs/source/index.rst index cc62ca03..35ecad3e 100644 --- a/docs/source/index.rst +++ b/docs/source/index.rst @@ -38,7 +38,6 @@ accessible through the sidebar navigation. tutorials/intro tutorials/mzi tutorials/filters - tutorials/layout_aware .. toctree:: :hidden: diff --git a/docs/source/reference/api.rst b/docs/source/reference/api.rst index 29092041..6ccd6fb3 100644 --- a/docs/source/reference/api.rst +++ b/docs/source/reference/api.rst @@ -8,11 +8,6 @@ API :inherited-members: :show-inheritance: -.. automodule:: simphony.die - :members: - :inherited-members: - :show-inheritance: - .. automodule:: simphony.formatters :members: :inherited-members: diff --git a/docs/source/tutorials/images/layout_aware.png b/docs/source/tutorials/images/layout_aware.png deleted file mode 100644 index 496e95bf..00000000 Binary files a/docs/source/tutorials/images/layout_aware.png and /dev/null differ diff --git a/docs/source/tutorials/images/mzi_layout_aware.png b/docs/source/tutorials/images/mzi_layout_aware.png deleted file mode 100644 index 17c28fbe..00000000 Binary files a/docs/source/tutorials/images/mzi_layout_aware.png and /dev/null differ diff --git a/docs/source/tutorials/intro.rst b/docs/source/tutorials/intro.rst index 11f51b81..5eaa9041 100644 --- a/docs/source/tutorials/intro.rst +++ b/docs/source/tutorials/intro.rst @@ -133,8 +133,8 @@ order to preserve the original circuit. Let's run a simple sweep simulation on the circuit we have created: :: - from simphony.simulators import SweepSimulator - simulation = SweepSimulator(1500e-9, 1600e-9) + from simphony.simulators import SweepSimulation + simulation = SweepSimulation(1500e-9, 1600e-9) simulation.multiconnect(component1['input'], component2['pin2']) result = simulation.simulate() diff --git a/docs/source/tutorials/layout_aware.rst b/docs/source/tutorials/layout_aware.rst deleted file mode 100644 index 416cbcba..00000000 --- a/docs/source/tutorials/layout_aware.rst +++ /dev/null @@ -1,172 +0,0 @@ -.. _example-layout_aware: - -Layout-Aware Monte Carlo Simulations for Yield Estimation -========================================================= - -Manufacturing variability can cause fabrication errors in waveguide width and thickness, -which can affect the device performance. Hence, incorporating manufacturing variability -into the photonic device design process is crucial. Simphony provides the ability to run -layout-aware Monte Carlo simulations for yield estimation to aid in robust photonic device design. -This tutorial will walk you through the codes found in ``examples/layout_aware.py`` of the Simphony repository. -We expect you to have read the previous tutorial, :doc:`mzi`. - - -The Workflow ------------- -The workflow for running layout-aware Monte Carlo simulations is as follows: - -1. instantiate the components -2. generate a layout using the components' ``component`` attributes -3. connect the components to form a circuit -4. run the simulation -5. extract and plot the results - -Example - Mach-Zehnder Interferometer (MZI) -------------------------------------------- -We will run a layout-aware Monte Carlo simulation for the -Mach-Zehnder Interferometer (MZI) described in the previous -example. - -First we need to import the necessary Simphony modules. We will need the ``siepic`` model library, -the ``Simulation``, ``Laser``, and ``Detector``. We will also need to import ``gdsfactory`` to generate the layout. -We will also import ``matplotlib.pyplot``, from outside of Simphony, to view the results -of our simulation. - -:: - import gdsfactory as gf - import matplotlib.pyplot as plt - import numpy as np - - from simphony.libraries import siepic - from simphony.simulation import Detector, Laser, Simulation - -We then create all the components and give them names. These -include the grating couplers, the Y-branches, and the -waveguides (which can be defined at any arbitrary length, -on the condition that the two lengths are different). - -:: - gc_input = siepic.GratingCoupler(name="gcinput") - y_splitter = siepic.YBranch(name="ysplit") - wg_long = siepic.Waveguide(length=150e-6, name="wglong") - wg_short = siepic.Waveguide(length=50e-6, name="wgshort") - y_recombiner = siepic.YBranch(name="y_recombiner") - gc_output = siepic.GratingCoupler(name="gcoutput") - -We then use the components' ``component`` attributes to create the layout. -The ``component`` attributes are ``gdsfactory.Component`` objects, and so can be used to create a -layout. We will next define a Parametric Cell (PCell) for the MZI. We will connect -the components to form a circuit, route the Waveguides using ``gdsfactory's`` routing functions. - -:: - @gf.cell - def mzi(): - c = gf.Component("mzi") - - ysplit = c << y_splitter.component - - gcin = c << gc_input.component - - gcout = c << gc_output.component - - yrecomb = c << y_recombiner.component - - yrecomb.move(destination=(0, -55.5)) - gcout.move(destination=(-20.4, -55.5)) - gcin.move(destination=(-20.4, 0)) - - gc_input["pin1"].connect(y_splitter, gcin, ysplit) - gc_output["pin1"].connect(y_recombiner["pin1"], gcout, yrecomb) - y_splitter["pin2"].connect(wg_long) - y_recombiner["pin3"].connect(wg_long) - y_splitter["pin3"].connect(wg_short) - y_recombiner["pin2"].connect(wg_short) - - wg_long_ref = gf.routing.get_route_from_steps( - ysplit.ports["pin2"], - yrecomb.ports["pin3"], - steps=[{"dx": 91.75 / 2}, {"dy": -61}], - ) - wg_short_ref = gf.routing.get_route_from_steps( - ysplit.ports["pin3"], - yrecomb.ports["pin2"], - steps=[{"dx": 47.25 / 2}, {"dy": -50}], - ) - - wg_long.path = wg_long_ref - wg_short.path = wg_short_ref - - c.add(wg_long_ref.references) - c.add(wg_short_ref.references) - - c.add_port("o1", port=gcin.ports["pin2"]) - c.add_port("o2", port=gcout.ports["pin2"]) - - return c - -We can then call the function, and visualize the layout in KLayout. - -:: - c = mzi() - c.show() - -. image:: images/mzi_layout_aware.png - :alt: layout - :align: center - -We can also view a 3D representation of the layout. - -:: - c.to_3d().show("gl") - - -Layout-Aware Monte Carlo Simulation ------------------------------------ -We use the ``Simulation`` class to run a simulation. We attach a ``Laser`` to one of the GratingCouplers, -and a ``Detector`` to the other GratingCoupler. - -:: - with Simulation() as sim: - l = Laser(power=1) - l.freqsweep(187370000000000.0, 199862000000000.0) - l.connect(gc_input['pin2']) - d = Detector() - d.connect(gc_output['pin2']) - - results = sim.layout_aware_simulation(c) - -Here, we can pass in the standard deviations of the widths and thicknesses, as well as the correlation length -as arguments to the ``layout_aware_simulation`` method. For this example, we use the default values. - -After the simulation is run, we can extract the results, and plot them. We will see several, slightly different curves -due to random variations incorporated into the components' widths and thicknesses. - -:: - f = l.freqs - for run in results: - p = [] - for sample in run: - for data_list in sample: - for data in data_list: - p.append(data) - plt.plot(f, p) - - run_0 = results[0] - p = [] - for sample in run_0: - for data_list in sample: - for data in data_list: - p.append(data) - plt.plot(f, p, 'k') - plt.title('MZI Layout Aware Monte Carlo') - plt.show() - -You should see something similar to this graph when you run -your MZI now: - -.. image:: images/layout_aware.png - :alt: layout-aware simulation - :align: center - -From our data, we can then compute various performance markers which are sensitive -to width and thickness variations. diff --git a/examples/filters.py b/examples/filters.py index da7ca572..ca124683 100644 --- a/examples/filters.py +++ b/examples/filters.py @@ -5,8 +5,8 @@ # # File: filters.py -import matplotlib.gridspec as gridspec import matplotlib.pyplot as plt +import matplotlib.gridspec as gridspec from simphony.libraries import siepic, sipann from simphony.simulators import SweepSimulator diff --git a/examples/layout_aware.py b/examples/layout_aware.py deleted file mode 100644 index fe11a776..00000000 --- a/examples/layout_aware.py +++ /dev/null @@ -1,115 +0,0 @@ -# Copyright © Simphony Project Contributors -# Licensed under the terms of the MIT License -# (see simphony/__init__.py for details) - -""" -layout_aware.py ---------------- - -Author: Skandan Chandrasekar -Modified: August 10, 2022 - -A script that walks the user through the process of running layout-aware Monte Carlo simulations. -""" - - -# import necessary modules -import gdsfactory as gf -import matplotlib.pyplot as plt - -from simphony.libraries import siepic -from simphony.simulation import Detector, Laser, Simulation - -# instantiate components -ysplitter = siepic.YBranch(name="ysplitter") -gcinput = siepic.GratingCoupler(name="gcinput") -gcoutput = siepic.GratingCoupler(name="gcoutput") -yrecombiner = siepic.YBranch(name="yrecombiner") -wg_long = siepic.Waveguide(name="wg_long", length=150e-6) -wg_short = siepic.Waveguide(name="wg_short", length=50e-6) - - -# define a PCell using simphony components -@gf.cell -def mzi(): - c = gf.Component("mzi") - - ysplit = c << ysplitter.component - - gcin = c << gcinput.component - - gcout = c << gcoutput.component - - yrecomb = c << yrecombiner.component - - yrecomb.move(destination=(0, -55.5)) - gcout.move(destination=(-20.4, -55.5)) - gcin.move(destination=(-20.4, 0)) - - gcinput["pin1"].connect(ysplitter, gcin, ysplit) - gcoutput["pin1"].connect(yrecombiner["pin1"], gcout, yrecomb) - ysplitter["pin2"].connect(wg_long) - yrecombiner["pin3"].connect(wg_long) - ysplitter["pin3"].connect(wg_short) - yrecombiner["pin2"].connect(wg_short) - - wg_long_ref = gf.routing.get_route_from_steps( - ysplit.ports["pin2"], - yrecomb.ports["pin3"], - steps=[{"dx": 91.75 / 2}, {"dy": -61}], - ) - wg_short_ref = gf.routing.get_route_from_steps( - ysplit.ports["pin3"], - yrecomb.ports["pin2"], - steps=[{"dx": 47.25 / 2}, {"dy": -50}], - ) - - wg_long.path = wg_long_ref - wg_short.path = wg_short_ref - - c.add(wg_long_ref.references) - c.add(wg_short_ref.references) - - c.add_port("o1", port=gcin.ports["pin2"]) - c.add_port("o2", port=gcout.ports["pin2"]) - - return c - - -c = mzi() -c.show() # open in KLayout -c.to_3d().show("gl") # 3D visualization - - -# run the layout aware simulation -with Simulation() as sim: - l = Laser(name="laser", power=1) - l.freqsweep(187370000000000.0, 199862000000000.0) - l.connect(gcinput.pins["pin2"]) - - d = Detector(name="detector") - d.connect(gcoutput.pins["pin2"]) - - results = sim.layout_aware_simulation(c) - -# Plot the results -f = l.freqs -for run in results: - p = [] - for sample in run: - for data_list in sample: - for data in data_list: - p.append(data) - plt.plot(f, p) - -run = results[0] -p = [] -for sample in run: - for data_list in sample: - for data in data_list: - p.append(data) -plt.plot(f, p, "k") -plt.xlabel("Frequency (Hz)") -plt.ylabel("Power Ratios") -plt.title("MZI Location - Aware Monte Carlo") -plt.show() diff --git a/examples/mzi.py b/examples/mzi.py index 9358d12e..3f4457b2 100644 --- a/examples/mzi.py +++ b/examples/mzi.py @@ -3,6 +3,7 @@ # (see simphony/__init__.py for details) import matplotlib.pyplot as plt +import numpy as np from simphony.libraries import siepic from simphony.simulation import Detector, Laser, Simulation diff --git a/examples/subnetwork_growth.py b/examples/subnetwork_growth.py index f67ca121..cb6b39b9 100644 --- a/examples/subnetwork_growth.py +++ b/examples/subnetwork_growth.py @@ -98,7 +98,6 @@ import matplotlib.pyplot as plt import numpy as np - from simphony.connect import connect_s, innerconnect_s from simphony.libraries import siepic, sipann from simphony.tools import wl2freq diff --git a/setup.py b/setup.py index 68676754..1786fb0a 100644 --- a/setup.py +++ b/setup.py @@ -78,7 +78,6 @@ extras_require = { "test": ["pytest"], - "gdsfactory": ["gdsfactory"], } if "setuptools" in sys.modules: diff --git a/simphony/__init__.py b/simphony/__init__.py index 3918a871..ece776ba 100644 --- a/simphony/__init__.py +++ b/simphony/__init__.py @@ -30,6 +30,7 @@ A Simulator for Photonic circuits """ + import platform import sys @@ -46,5 +47,5 @@ __license__ = "MIT" __project_url__ = "https://github.com/BYUCamachoLab/simphony" __forum_url__ = "https://github.com/BYUCamachoLab/simphony/issues" -__trouble_url__ = __project_url__ + "/wiki/Troubleshooting-Guide" +__trouble_url__ = f"{__project_url__}/wiki/Troubleshooting-Guide" __website_url__ = "https://camacholab.byu.edu/" diff --git a/simphony/connect.py b/simphony/connect.py index 5da7c51f..d91cd6bc 100644 --- a/simphony/connect.py +++ b/simphony/connect.py @@ -46,7 +46,6 @@ from simphony.tools import add_polar, mul_polar - # Functions operating on s-parameter matrices def connect_s(A, k, B, l): """ diff --git a/simphony/formatters.py b/simphony/formatters.py index e27fcf9b..d448feca 100644 --- a/simphony/formatters.py +++ b/simphony/formatters.py @@ -226,9 +226,7 @@ def format(self, circuit: "Circuit", freqs: np.array) -> str: data = {"components": [], "connections": []} for i, component in enumerate(circuit): # skip simulators - if isinstance(component, Simulator) or isinstance( - component, SimulationModel - ): + if isinstance(component, (Simulator, SimulationModel)): continue # get a representation for each component @@ -259,9 +257,7 @@ def parse(self, string: str) -> "Circuit": data = json.loads(string) # load all of the components - components = [] - for string in data["components"]: - components.append(Model.from_string(string, formatter=ModelJSONFormatter())) + components = [Model.from_string(string, formatter=ModelJSONFormatter()) for string in data["components"]] # connect the components to each other for i, j, k, l in data["connections"]: @@ -275,57 +271,19 @@ class CircuitSiEPICFormatter(CircuitFormatter): mappings = { "simphony.libraries.siepic": { - "ebeam_bdc_te1550": { - "name": "BidirectionalCoupler", - "parameters": { - "lay_x": "component.x", - "lay_y": "component.y", - }, - }, - "ebeam_dc_halfring_straight": { - "name": "HalfRing", - "parameters": { - "lay_x": "component.x", - "lay_y": "component.y", - }, - }, - "ebeam_dc_te1550": { - "name": "DirectionalCoupler", - "parameters": { - "lay_x": "component.x", - "lay_y": "component.y", - }, - }, - "ebeam_gc_te1550": { - "name": "GratingCoupler", - "parameters": { - "lay_x": "component.x", - "lay_y": "component.y", - }, - }, - "ebeam_terminator_te1550": { - "name": "Terminator", - "parameters": { - "lay_x": "component.x", - "lay_y": "component.y", - }, - }, + "ebeam_bdc_te1550": {"name": "BidirectionalCoupler", "parameters": {}}, + "ebeam_dc_halfring_straight": {"name": "HalfRing", "parameters": {}}, + "ebeam_dc_te1550": {"name": "DirectionalCoupler", "parameters": {}}, + "ebeam_gc_te1550": {"name": "GratingCoupler", "parameters": {}}, + "ebeam_terminator_te1550": {"name": "Terminator", "parameters": {}}, "ebeam_wg_integral_1550": { "name": "Waveguide", "parameters": { "wg_length": "length", "wg_width": "width", - "lay_x": "component.x", - "lay_y": "component.y", - }, - }, - "ebeam_y_1550": { - "name": "YBranch", - "parameters": { - "lay_x": "component.x", - "lay_y": "component.y", }, }, + "ebeam_y_1550": {"name": "YBranch", "parameters": {}}, }, } @@ -347,10 +305,7 @@ def _instantiate_component(self, component: Dict[str, Any]) -> "Model": mapping = self.__class__.mappings[self.pdk.__name__][component["model"]] # remap the parameter values so they match the components' API - parameters = {} - for k, v in component["params"].items(): - if k in mapping["parameters"]: - parameters[mapping["parameters"][k]] = v + parameters = {mapping["parameters"][k]: v for k, v in component["params"].items() if k in mapping["parameters"]} # instantiate the model and pass in the corrected parameters return getattr(self.pdk, mapping["name"])(**parameters) @@ -398,9 +353,9 @@ def parse(self, string: str) -> "Circuit": else "freq" ) - simulator = SweepSimulator(start, stop, points) - simulator.mode = mode - simulator.multiconnect(subcircuit[output], subcircuit[input]) + simulator = SweepSimulator(start, stop, points) + simulator.mode = mode + simulator.multiconnect(subcircuit[output], subcircuit[input]) # no reason to include the subcircuit component for now return subcircuit._wrapped_circuit diff --git a/simphony/layout.py b/simphony/layout.py index 8fedafab..8bd95fdb 100644 --- a/simphony/layout.py +++ b/simphony/layout.py @@ -14,11 +14,11 @@ import os from typing import TYPE_CHECKING, List, Optional -import numpy as np - from simphony.formatters import CircuitFormatter, CircuitJSONFormatter if TYPE_CHECKING: + import numpy as np + from simphony import Model from simphony.models import Subcircuit from simphony.pins import Pin diff --git a/simphony/libraries/siepic/__init__.py b/simphony/libraries/siepic/__init__.py index 8f7f1503..b32d20aa 100644 --- a/simphony/libraries/siepic/__init__.py +++ b/simphony/libraries/siepic/__init__.py @@ -80,14 +80,7 @@ def __init__(self, thickness=220e-9, width=500e-9, polarization='TE'): from bisect import bisect_left from collections import namedtuple -try: - import gdsfactory as gf - from gdsfactory.types import Route - _has_gf = True -except ImportError: - _has_gf = False import numpy as np -import scipy.interpolate as interp from scipy.constants import c as SPEED_OF_LIGHT from simphony import Model @@ -120,10 +113,7 @@ def closest(sorted_list, value): return sorted_list[-1] before = sorted_list[pos - 1] after = sorted_list[pos] - if after - value < value - before: - return after - else: - return before + return after if after - value < value - before else before def get_files_from_dir(path): @@ -435,13 +425,8 @@ def _get_matched_args(cls, norm_args, req_args): return norm_args.index(req_args) except ValueError: adjusted_args = cls._find_closest(norm_args, req_args) - msg = ( - "Exact parameters not available for '{}', ".format(cls) - + "using closest approximation (results may not be as accurate).\n" - + "{:<11}{}\n".format("Requested:", req_args) - + "{:<11}{}\n".format("Selected:", adjusted_args) - + "NOTE: Model attributes may have been automatically modified." - ) + msg = (f"Exact parameters not available for '{cls}', " + "using closest approximation (results may not be as accurate).\n") + "{:<11}{}\n".format("Requested:", req_args) + "{:<11}{}\n".format("Selected:", adjusted_args) + "NOTE: Model attributes may have been automatically modified." + warnings.warn(msg, UserWarning) return norm_args.index(adjusted_args) @@ -478,7 +463,7 @@ def _find_closest(normalized, args): errors = [] for count, keys, argset in candidates: - sum_error = sum([abs(percent_diff(argset[key], args[key])) for key in keys]) + sum_error = sum(abs(percent_diff(argset[key], args[key])) for key in keys) errors.append(sum_error / count) idx = np.argmin(errors) return candidates[idx].argset @@ -519,101 +504,31 @@ class BidirectionalCoupler(SiEPIC_PDK_Base): r"(?:\.sparam)" ) - def __init__( - self, - thickness=220e-9, - width=500e-9, - **kwargs, - ): - super().__init__( - **kwargs, - thickness=thickness, - width=width, - ) - - if _has_gf: - gf.clear_cache() - self.component = gf.components.coupler(gap=0.2, length=50.55, dy=4.7, width=width * 1e6) - self.component.name = self.name - pin_names = [pin.name for pin in self.pins] - for i, port in enumerate(self.component.ports.values()): - port.name = pin_names[i] - self.component.ports = dict(zip(pin_names, self.component.ports.values())) + def __init__(self, thickness=220e-9, width=500e-9, **kwargs): + super().__init__(**kwargs, thickness=thickness, width=width) def on_args_changed(self): - try: - if self.layout_aware: - self.suspend_autoupdate() - - available = self._source_argsets() - widths = [] - heights = [] - s_params = [] - for idx in range(0, len(available), 2): - d = available[idx] - widths.append(d["width"]) - heights.append(d["thickness"]) - valid_args = available[idx] - sparams = parser.read_params(self._get_file(valid_args)) - self._f, s = parser.build_matrix(sparams) - s_params.append(s) - - s_params = np.asarray(s_params) - - widths = np.asarray(widths, dtype=np.double) - heights = np.asarray(heights, dtype=np.double) - - dim = len(s_params) - s_list = [s_params[dimidx][:][:][:] for dimidx in range(dim)] - s_list = np.asarray(s_list, dtype=complex) - self._s = interp.griddata( - (widths, heights), - s_list, - (self.width * 1e9, self.thickness * 1e9), - method="cubic", - ) - - self.freq_range = (self._f[0], self._f[-1]) - - self.enable_autoupdate() - except AttributeError: - self.suspend_autoupdate() + self.suspend_autoupdate() - available = self._source_argsets() - normalized = [ - {k: round(str2float(v) * 1e-9, 21) for k, v in d.items()} - for d in available - ] - idx = self._get_matched_args(normalized, self.args) + available = self._source_argsets() + normalized = [ + {k: round(str2float(v) * 1e-9, 21) for k, v in d.items()} for d in available + ] + idx = self._get_matched_args(normalized, self.args) - valid_args = available[idx] - sparams = parser.read_params(self._get_file(valid_args)) - self._f, self._s = parser.build_matrix(sparams) + valid_args = available[idx] + sparams = parser.read_params(self._get_file(valid_args)) + self._f, self._s = parser.build_matrix(sparams) - self.freq_range = (self._f[0], self._f[-1]) - for key, value in normalized[idx].items(): - setattr(self, key, value) + self.freq_range = (self._f[0], self._f[-1]) + for key, value in normalized[idx].items(): + setattr(self, key, value) - self.enable_autoupdate() + self.enable_autoupdate() def s_parameters(self, freqs): return interpolate(freqs, self._f, self._s) - def update_variations(self, **kwargs): - self.nominal_width = self.width - self.nominal_thickness = self.thickness - - w = self.width + kwargs.get("corr_w") * 1e-9 - t = self.thickness + kwargs.get("corr_t") * 1e-9 - - self.layout_aware = True - self.width = w - self.thickness = t - - def regenerate_layout_aware_monte_carlo_parameters(self): - self.width = self.nominal_width - self.thickness = self.nominal_thickness - class HalfRing(SiEPIC_PDK_Base): """A half-ring resonator optimized for TE polarized light at 1550 @@ -668,9 +583,8 @@ def __init__( width=500e-9, thickness=220e-9, couple_length=0.0, - **kwargs, + **kwargs ): - super().__init__( **kwargs, gap=gap, @@ -680,90 +594,24 @@ def __init__( couple_length=couple_length, ) - if _has_gf: - gf.clear_cache() - self.component = gf.components.coupler_ring(gap=gap * 1e6, length_x=couple_length * 1e6, radius=radius * 1e6, width=width * 1e6,) - self.component.name = self.name - pin_names = [pin.name for pin in self.pins] - for i, port in enumerate(self.component.ports.values()): - port.name = pin_names[i] - self.component.ports = dict(zip(pin_names, self.component.ports.values())) - def on_args_changed(self): - try: - if self.layout_aware: - self.suspend_autoupdate() - - available = self._source_argsets() - widths = [] - heights = [] - s_params = [] - for idx in range(0, len(available), 2): - d = available[idx] - widths.append(d["width"]) - heights.append(d["thickness"]) - valid_args = available[idx] - sparams = parser.read_params(self._get_file(valid_args)) - sparams = list( - filter( - lambda sparams: sparams["mode"] == self.polarization, - sparams, - ) - ) - self._f, s = parser.build_matrix(sparams) - - s_params.append(s) - - s_params = np.asarray(s_params) - - widths = np.asarray(widths, dtype=complex) - heights = np.asarray(heights, dtype=complex) - - dim, _, _, _ = s_params.shape - s_list = [] - - for dimidx in range(dim): - s_list.append(s_params[dimidx][:][:][:]) - s_list = np.asarray(s_list, dtype=complex) - self._s = interp.griddata( - (widths, heights), - s_list, - (self.width * 1e9, self.thickness * 1e9), - method="cubic", - ) - - self.freq_range = (self._f[0], self._f[-1]) - - self.enable_autoupdate() - except AttributeError: - self.suspend_autoupdate() - - available = self._source_argsets() - normalized = [ - {k: round(str2float(v), 15) for k, v in d.items()} for d in available - ] - idx = self._get_matched_args(normalized, self.args) - - valid_args = available[idx] - sparams = parser.read_params(self._get_file(valid_args)) - self._f, self._s = parser.build_matrix(sparams) - - self.freq_range = (self._f[0], self._f[-1]) - for key, value in normalized[idx].items(): - setattr(self, key, value) + self.suspend_autoupdate() - self.enable_autoupdate() + available = self._source_argsets() + normalized = [ + {k: round(str2float(v), 15) for k, v in d.items()} for d in available + ] + idx = self._get_matched_args(normalized, self.args) - def update_variations(self, **kwargs): - self.nominal_width = self.width - self.nominal_thickness = self.thickness + valid_args = available[idx] + sparams = parser.read_params(self._get_file(valid_args)) + self._f, self._s = parser.build_matrix(sparams) - w = self.width + kwargs.get("corr_w") * 1e-9 - t = self.thickness + kwargs.get("corr_t") * 1e-9 + self.freq_range = (self._f[0], self._f[-1]) + for key, value in normalized[idx].items(): + setattr(self, key, value) - self.layout_aware = True - self.width = w - self.thickness = t + self.enable_autoupdate() def s_parameters(self, freqs): return interpolate(freqs, self._f, self._s) @@ -805,95 +653,30 @@ class DirectionalCoupler(SiEPIC_PDK_Base): ) def __init__(self, gap=200e-9, Lc=10e-6, **kwargs): - - super().__init__( - **kwargs, - gap=gap, - Lc=Lc, - ) - - if _has_gf: - gf.clear_cache() - self.component = gf.components.coupler(gap=gap * 1e6, length=Lc * 1e6, dy=10) - self.component.name = self.name - pin_names = [pin.name for pin in self.pins] - for i, port in enumerate(self.component.ports.values()): - port.name = pin_names[i] - self.component.ports = dict(zip(pin_names, self.component.ports.values())) + super().__init__(**kwargs, gap=gap, Lc=Lc) def on_args_changed(self): - try: - if self.layout_aware: - self.suspend_autoupdate() - - available = self._source_argsets() - Lcs = [] - s_params = [] - for idx in range(len(available)): - d = available[idx] - Lcs.append(d["Lc"]) - valid_args = available[idx] - sparams = parser.read_params(self._get_file(valid_args)) - self._f, s = parser.build_matrix(sparams) - - s_params.append(s) - - s_params = np.asarray(s_params) - - Lcs = [Lcs[i].replace("u", "") for i in range(len(Lcs))] - Lcs = np.asarray(Lcs, dtype=float) - - dim, freq, inp, out = s_params.shape - s_new = np.ndarray((freq, inp, out), dtype=complex) - for freqidx in range(freq): - for inpidx in range(inp): - for outidx in range(out): - s_list = [] - for dimidx in range(dim): - s_list.append(s_params[dimidx][freqidx][inpidx][outidx]) - - s_interp = interp.interp1d( - Lcs, np.asarray(s_list), kind="cubic" - ) - s_new[freqidx][inpidx][outidx] = s_interp(self.Lc * 1e6) - - self._s = s_new - self.freq_range = (self._f[0], self._f[-1]) - - self.enable_autoupdate() - except AttributeError: - self.suspend_autoupdate() + self.suspend_autoupdate() - available = self._source_argsets() - normalized = [ - {k: round(str2float(v), 15) for k, v in d.items()} for d in available - ] - idx = self._get_matched_args(normalized, self.args) + available = self._source_argsets() + normalized = [ + {k: round(str2float(v), 15) for k, v in d.items()} for d in available + ] + idx = self._get_matched_args(normalized, self.args) - valid_args = available[idx] - sparams = parser.read_params(self._get_file(valid_args)) - self._f, self._s = parser.build_matrix(sparams) + valid_args = available[idx] + sparams = parser.read_params(self._get_file(valid_args)) + self._f, self._s = parser.build_matrix(sparams) - self.freq_range = (self._f[0], self._f[-1]) - for key, value in normalized[idx].items(): - setattr(self, key, value) + self.freq_range = (self._f[0], self._f[-1]) + for key, value in normalized[idx].items(): + setattr(self, key, value) - self.enable_autoupdate() + self.enable_autoupdate() def s_parameters(self, freqs): return interpolate(freqs, self._f, self._s) - def update_variations(self, **kwargs): - self.nominal_Lc = self.Lc - - w = self.nominal_Lc + kwargs.get("corr_w") * 1e-6 - - self.layout_aware = True - self.Lc = w - - def regenerate_layout_aware_monte_carlo_parameters(self): - self.Lc = self.nominal_Lc - class Terminator(SiEPIC_PDK_Base): """A terminator component that dissipates light into free space optimized @@ -932,29 +715,8 @@ class Terminator(SiEPIC_PDK_Base): r"(?:_TE.sparam)" ) - def __init__( - self, - w1=500e-9, - w2=60e-9, - L=10e-6, - **kwargs, - ): - - super().__init__( - **kwargs, - w1=w1, - w2=w2, - L=L, - ) - - if _has_gf: - gf.clear_cache() - self.component = gf.components.taper(width1=w1 * 1e6, width2=w2 * 1e6, with_two_ports=False) - self.component.name = self.name - pin_names = [pin.name for pin in self.pins] - for i, port in enumerate(self.component.ports.values()): - port.name = pin_names[i] - self.component.ports = dict(zip(pin_names, self.component.ports.values())) + def __init__(self, w1=500e-9, w2=60e-9, L=10e-6, **kwargs): + super().__init__(**kwargs, w1=w1, w2=w2, L=L) def on_args_changed(self): self.suspend_autoupdate() @@ -985,9 +747,6 @@ def on_args_changed(self): def s_parameters(self, freqs): return interpolate(freqs, self._f, self._s) - def layout_aware_monte_carlo_parameters(self): - pass - class GratingCoupler(SiEPIC_PDK_Base): """A grating coupler optimized for TE polarized light at 1550 nanometers. @@ -1026,51 +785,14 @@ class GratingCoupler(SiEPIC_PDK_Base): r"(?:\.txt)" ) - def __init__( - self, - thickness=220e-9, - deltaw=0, - polarization="TE", - **kwargs, - ): - + def __init__(self, thickness=220e-9, deltaw=0, polarization="TE", **kwargs): super().__init__( - **kwargs, - thickness=thickness, - deltaw=deltaw, - polarization=polarization, + **kwargs, thickness=thickness, deltaw=deltaw, polarization=polarization ) - if _has_gf: - gf.clear_cache() - self.component = gf.components.grating_coupler_te() - self.component.name = self.name - pin_names = [pin.name for pin in self.pins] - for i, port in enumerate(self.component.ports.values()): - port.name = pin_names[i] - self.component.ports = dict(zip(pin_names, self.component.ports.values())) - port1 = self.component.ports["pin1"] - self.component.ports["pin1"] = self.component.ports["pin2"] - self.component.ports["pin2"] = port1 - def on_args_changed(self): - try: - if self.layout_aware: - self.suspend_autoupdate() - - self._update_lay_aware_true() - - self.freq_range = (self._f[0], self._f[-1]) - - self.enable_autoupdate() - except AttributeError: - self.suspend_autoupdate() - - self._update_no_lay_aware() - - self.enable_autoupdate() + self.suspend_autoupdate() - def _update_no_lay_aware(self): available = self._source_argsets() normalized = [] for d in available: @@ -1103,103 +825,11 @@ def _update_no_lay_aware(self): self._s = self._s[::-1] self.freq_range = (self._f[0], self._f[-1]) - def _update_lay_aware_true(self): - pass - # available = self._source_argsets() - # thicknesses = [] - # deltaws = [] - # s_params = [] - # for d in available: - # _, thickness, deltaw = [(key, d.get(key)) for key in self._args_keys] - # thicknesses.append(round(str2float(thickness[1]) * 1e-9, 15)) - # deltaws.append(round(str2float(deltaw[1]) * 1e-9, 15)) - - # if self.polarization == "TE": - # for idx in range(round(len(thicknesses) / 2)): - # valid_args = available[idx] - # params = np.genfromtxt(self._get_file(valid_args), delimiter="\t") - # self._f = params[:, 0] - # s = np.zeros((len(self._f), 2, 2), dtype="complex128") - # s[:, 0, 0] = params[:, 1] * np.exp(1j * params[:, 2]) - # s[:, 0, 1] = params[:, 3] * np.exp(1j * params[:, 4]) - # s[:, 1, 0] = params[:, 5] * np.exp(1j * params[:, 6]) - # s[:, 1, 1] = params[:, 7] * np.exp(1j * params[:, 8]) - - # self._f = self._f[::-1] - # s = s[::-1] - # s_params.append(s) - - # s_params = np.asarray(s_params, dtype=object) - # thicknesses = np.asarray(thicknesses, dtype=float) - # deltaws = np.asarray(deltaws, dtype=float) - - # dim = len(s_params) - # for dimidx in range(dim): - # print(f'[s_params[dimidx][:][:][:]) - # s_list.append(s_params[dimidx][:][:][:]) - # s_list = np.asarray(s_list, dtype=complex) - # self._s = interp.griddata( - # ( - # thicknesses[0 : round(len(thicknesses) / 2)], - # deltaws[0 : round(len(deltaws) / 2)], - # ), - # s_list, - # (self.thickness, self.deltaw), - # method="cubic", - # ) - - # elif self.polarization == "TM": - # for idx in range(round(len(thicknesses) / 2) + 1, len(thicknesses)): - # valid_args = available[idx] - # params = np.genfromtxt(self._get_file(valid_args), delimiter="\t") - # self._f = params[:, 0] - # s = np.zeros((len(self._f), 2, 2), dtype="complex128") - # s[:, 0, 0] = params[:, 1] * np.exp(1j * params[:, 2]) - # s[:, 0, 1] = params[:, 3] * np.exp(1j * params[:, 4]) - # s[:, 1, 0] = params[:, 5] * np.exp(1j * params[:, 6]) - # s[:, 1, 1] = params[:, 7] * np.exp(1j * params[:, 8]) - - # self._f = self._f[::-1] - # s = s[::-1] - # s_params.append(s) - - # s_params = np.asarray(s_params, dtype=object) - # thicknesses = np.asarray(thicknesses, dtype=float) - # deltaws = np.asarray(deltaws, dtype=float) - - # dim = len(s_params) - # s_list = [] - # for dimidx in range(dim): - # s_list.append(s_params[dimidx][:][:][:]) - # s_list = np.asarray(s_list, dtype=complex) - # self._s = interp.griddata( - # ( - # thicknesses[round(len(thicknesses) / 2) + 1, len(thicknesses)], - # deltaws[round(len(thicknesses) / 2) + 1, len(thicknesses)], - # ), - # s_list, - # (self.thickness, self.deltaw), - # method="cubic", - # ) + self.enable_autoupdate() def s_parameters(self, freqs): return interpolate(freqs, self._f, self._s) - def update_variations(self, **kwargs): - self.nominal_deltaw = self.deltaw - self.nominal_thickness = self.thickness - - w = self.deltaw + kwargs.get("corr_w") * 1e-9 - t = self.thickness + kwargs.get("corr_t") * 1e-9 - - self.layout_aware = True - self.deltaw = w - self.thickness = t - - def regenerate_layout_aware_monte_carlo_parameters(self): - self.thickness = self.nominal_thickness - self.deltaw = self.nominal_deltaw - class Waveguide(SiEPIC_PDK_Base): """Model for an waveguide optimized for TE polarized light at 1550 @@ -1236,7 +866,6 @@ class Waveguide(SiEPIC_PDK_Base): """ pin_count = 2 - freq_range = ( 187370000000000.0, 199862000000000.0, @@ -1264,14 +893,11 @@ def __init__( sigma_ne=0.05, sigma_ng=0.05, sigma_nd=0.0001, - **kwargs, + **kwargs ): if polarization not in ["TE", "TM"]: - raise ValueError( - "Unknown polarization value '{}', must be one of 'TE' or 'TM'".format( - polarization - ) - ) + raise ValueError(f"Unknown polarization value '{polarization}', must be one of 'TE' or 'TM'") + # TODO: TM calculations if polarization == "TM": @@ -1288,105 +914,35 @@ def __init__( sigma_nd=sigma_nd, ) - if _has_gf: - self.path: Route = None - self.regenerate_monte_carlo_parameters() def on_args_changed(self): - try: - if self.layout_aware: - self.suspend_autoupdate() - - available = self._source_argsets() - - widths = [] - heights = [] - for d in available: - widths.append(d["width"]) - heights.append(d["height"]) - - lam0_all = [] - ne_all = [] - ng_all = [] - nd_all = [] - for idx in range(len(available)): - valid_args = available[idx] - with open(self._get_file(valid_args), "r") as f: - params = f.read().rstrip("\n") - if self.polarization == "TE": - lam0, ne, _, ng, _, nd, _ = params.split(" ") - elif self.polarization == "TM": - lam0, _, ne, _, ng, _, nd = params.split(" ") - raise NotImplementedError - - lam0_all.append(lam0) - ne_all.append(ne) - ng_all.append(ng) - nd_all.append(nd) - - widths = np.asarray(widths).astype(float) - heights = np.asarray(heights).astype(float) - lam0_all = np.asarray(lam0_all).astype(float) - ne_all = np.asarray(ne_all).astype(float) - ng_all = np.asarray(ng_all).astype(float) - nd_all = np.asarray(nd_all).astype(float) - - self.lam0 = interp.griddata( - (widths, heights), - lam0_all, - (self.width * 1e9, self.height * 1e9), - method="cubic", - ) - self.ne = interp.griddata( - (widths, heights), - ne_all, - (self.width * 1e9, self.height * 1e9), - method="cubic", - ) - self.ng = interp.griddata( - (widths, heights), - ng_all, - (self.width * 1e9, self.height * 1e9), - method="cubic", - ) - self.nd = interp.griddata( - (widths, heights), - nd_all, - (self.width * 1e9, self.height * 1e9), - method="cubic", - ) - - self.enable_autoupdate() + self.suspend_autoupdate() - except AttributeError: - self.suspend_autoupdate() + available = self._source_argsets() + normalized = [ + {k: round(str2float(v) * 1e-9, 21) for k, v in d.items()} for d in available + ] + idx = self._get_matched_args(normalized, self.args) - available = self._source_argsets() - normalized = [ - {k: round(str2float(v) * 1e-9, 21) for k, v in d.items()} - for d in available - ] - idx = self._get_matched_args(normalized, self.args) - - valid_args = available[idx] - with open(self._get_file(valid_args), "r") as f: - params = f.read().rstrip("\n") - if self.polarization == "TE": - lam0, ne, _, ng, _, nd, _ = params.split(" ") - elif self.polarization == "TM": - lam0, _, ne, _, ng, _, nd = params.split(" ") - raise NotImplementedError - self.lam0 = float(lam0) - self.ne = float(ne) - self.ng = float(ng) - self.nd = float(nd) - - # Updates parameters width and thickness to closest match. - for key, value in normalized[idx].items(): - setattr(self, key, value) + valid_args = available[idx] + with open(self._get_file(valid_args), "r") as f: + params = f.read().rstrip("\n") + if self.polarization == "TE": + lam0, ne, _, ng, _, nd, _ = params.split(" ") + elif self.polarization == "TM": + lam0, _, ne, _, ng, _, nd = params.split(" ") + raise NotImplementedError + self.lam0 = float(lam0) + self.ne = float(ne) + self.ng = float(ng) + self.nd = float(nd) + + # Updates parameters width and thickness to closest match. + for key, value in normalized[idx].items(): + setattr(self, key, value) - self.enable_autoupdate() + self.enable_autoupdate() def s_parameters(self, freqs): """Get the s-parameters of a waveguide. @@ -1402,7 +958,7 @@ def s_parameters(self, freqs): Returns a tuple containing the frequency array, `freqs`, corresponding to the calculated s-parameter matrix, `s`. """ - return self.calc_s_params( + return self.cacl_s_params( freqs, self.length, self.lam0, self.ne, self.ng, self.nd ) @@ -1417,44 +973,17 @@ def monte_carlo_s_parameters(self, freqs): monte carlo parameters but they are consistent within a single circuit. """ - return self.calc_s_params( + return self.cacl_s_params( freqs, self.length, self.lam0, self.rand_ne, self.rand_ng, self.rand_nd ) - def layout_aware_monte_carlo_s_parameters(self, freqs): - """Returns a monte carlo (randomized) set of s-parameters. - - In this implementation of the monte carlo routine, values generated - for lam0, ne, ng, and nd using the Reduced Spatial Correlation Matrix method - are used to return a set of s-parameters. This is repeated for each - run of the Monte Carlo analysis for every wavehuide component in the circuit. - """ - return self.calc_s_params( - freqs, self.length, self.lam0, self.ne, self.ng, self.nd - ) - def regenerate_monte_carlo_parameters(self): self.rand_ne = np.random.normal(self.ne, self.sigma_ne) self.rand_ng = np.random.normal(self.ng, self.sigma_ng) self.rand_nd = np.random.normal(self.nd, self.sigma_nd) - def update_variations(self, **kwargs): - self.nominal_width = self.width - self.nominal_height = self.height - - w = self.width + kwargs.get("corr_w") * 1e-9 - h = self.height + kwargs.get("corr_t") * 1e-9 - - self.layout_aware = True - self.width = w - self.height = h - - def regenerate_layout_aware_monte_carlo_parameters(self): - self.width = self.nominal_width - self.height = self.nominal_height - @staticmethod - def calc_s_params(freqs, length, lam0, ne, ng, nd): + def cacl_s_params(freqs, length, lam0, ne, ng, nd): # Initialize array to hold s-params s = np.zeros((len(freqs), 2, 2), dtype=complex) @@ -1472,7 +1001,7 @@ def calc_s_params(freqs, length, lam0, ne, ng, nd): - (nd * lam0 ** 2 / (4 * np.pi * SPEED_OF_LIGHT)) * ((w - w0) ** 2) ) - for x in range(0, len(freqs)): # build s-matrix from K and waveguide length + for x in range(len(freqs)): # build s-matrix from K and waveguide length s[x, 0, 1] = s[x, 1, 0] = np.exp(-alpha * length + (K[x] * length * 1j)) return s @@ -1515,102 +1044,35 @@ class YBranch(SiEPIC_PDK_Base): r"(?:\.sparam)" ) - def __init__( - self, - thickness=220e-9, - width=500e-9, - polarization="TE", - **kwargs, - ): + def __init__(self, thickness=220e-9, width=500e-9, polarization="TE", **kwargs): if polarization not in ["TE", "TM"]: - raise ValueError( - "Unknown polarization value '{}', must be one of 'TE' or 'TM'".format( - polarization - ) - ) + raise ValueError(f"Unknown polarization value '{polarization}', must be one of 'TE' or 'TM'") + super().__init__( - **kwargs, - thickness=thickness, - width=width, - polarization=polarization, + **kwargs, thickness=thickness, width=width, polarization=polarization ) - if _has_gf: - gf.clear_cache() - self.component = gf.read.import_gds(os.path.join(os.path.dirname(__file__), 'source_data/ebeam_y_1550.gds')) - self.component.name = self.name - self.component.ports[self.pins[0].name] = gf.Port(self.pins[0].name, 180, center=(-7.4, 0), width=width * 1e6, layer="PORT", parent=self.component) - self.component.ports[self.pins[1].name] = gf.Port(self.pins[1].name, 0, center=(7.4, 2.75), width=width * 1e6, layer="PORT", parent=self.component) - self.component.ports[self.pins[2].name] = gf.Port(self.pins[2].name, 0, center=(7.4, -2.75), width=width * 1e6, layer="PORT", parent=self.component) - def on_args_changed(self): - try: - if self.layout_aware: - self.suspend_autoupdate() - - available = self._source_argsets() - widths = [] - heights = [] - s_params = [] - for idx in range(0, len(available)): - d = available[idx] - widths.append(d["width"]) - heights.append(d["thickness"]) - valid_args = available[idx] - sparams = parser.read_params(self._get_file(valid_args)) - sparams = list( - filter( - lambda sparams: sparams["mode"] == self.polarization, - sparams, - ) - ) - self._f, s = parser.build_matrix(sparams) - - s_params.append(s) - - s_params = np.asarray(s_params) - - widths = np.asarray(widths, dtype=float) - heights = np.asarray(heights, dtype=float) - - dim, _, _, _ = s_params.shape - s_list = [] - for dimidx in range(dim): - s_list.append(s_params[dimidx][:][:][:]) - s_list = np.asarray(s_list, dtype=complex) - self._s = interp.griddata( - (widths, heights), - s_list, - (self.width * 1e9, self.thickness * 1e9), - method="cubic", - ) - - self.freq_range = (self._f[0], self._f[-1]) - - self.enable_autoupdate() - - except AttributeError: - self.suspend_autoupdate() + self.suspend_autoupdate() - available = self._source_argsets() - normalized = [ - {k: round(str2float(v) * 1e-9, 21) for k, v in d.items()} - for d in available - ] - idx = self._get_matched_args(normalized, self.args) + available = self._source_argsets() + normalized = [ + {k: round(str2float(v) * 1e-9, 21) for k, v in d.items()} for d in available + ] + idx = self._get_matched_args(normalized, self.args) - valid_args = available[idx] - sparams = parser.read_params(self._get_file(valid_args)) - sparams = list( - filter(lambda sparams: sparams["mode"] == self.polarization, sparams) - ) + valid_args = available[idx] + sparams = parser.read_params(self._get_file(valid_args)) + sparams = list( + filter(lambda sparams: sparams["mode"] == self.polarization, sparams) + ) - for key, value in normalized[idx].items(): - setattr(self, key, value) - self._f, self._s = parser.build_matrix(sparams) - self.freq_range = (self._f[0], self._f[-1]) + for key, value in normalized[idx].items(): + setattr(self, key, value) + self._f, self._s = parser.build_matrix(sparams) + self.freq_range = (self._f[0], self._f[-1]) - self.enable_autoupdate() + self.enable_autoupdate() def s_parameters(self, freqs): """Returns scattering parameters for the y-branch based on its @@ -1627,18 +1089,3 @@ def s_parameters(self, freqs): The scattering parameters corresponding to the frequency range. """ return interpolate(freqs, self._f, self._s) - - def update_variations(self, **kwargs): - self.nominal_width = self.width - self.nominal_thickness = self.thickness - - w = self.width + kwargs.get("corr_w") * 1e-9 - t = self.thickness + kwargs.get("corr_t") * 1e-9 - - self.layout_aware = True - self.width = w - self.thickness = t - - def regenerate_layout_aware_monte_carlo_parameters(self): - self.width = self.nominal_width - self.thickness = self.nominal_thickness diff --git a/simphony/libraries/siepic/loader.py b/simphony/libraries/siepic/loader.py index d5b81dac..260f7d4f 100644 --- a/simphony/libraries/siepic/loader.py +++ b/simphony/libraries/siepic/loader.py @@ -39,10 +39,7 @@ def take_closest(sorted_list, value): return associations[-1] before = associations[pos - 1] after = associations[pos] - if after - Lc < Lc - before: - return after - else: - return before + return after if after - Lc < Lc - before else before associations[take_closest(list(associations.keys()), 1.35e-5)] diff --git a/simphony/libraries/siepic/parser.py b/simphony/libraries/siepic/parser.py index 7d3f5deb..e4f52123 100644 --- a/simphony/libraries/siepic/parser.py +++ b/simphony/libraries/siepic/parser.py @@ -54,10 +54,7 @@ def visit_file(self, node, visited_children): Returns: list : List of all paramsets (dict) in the file. """ - paramsets = [] - for paramset in visited_children[1]: - paramsets.append(paramset) - return paramsets + return list(visited_children[1]) def visit_paramset(self, node, visited_children): """ @@ -185,8 +182,7 @@ def visit_number(self, node, visited_children): Returns: float : the number cast to a float. """ - value = float(node.text) - return value + return float(node.text) def generic_visit(self, node, visited_children): """The generic visit method.""" diff --git a/simphony/libraries/siepic/source_data/ebeam_y_1550.gds b/simphony/libraries/siepic/source_data/ebeam_y_1550.gds deleted file mode 100644 index f05fbc54..00000000 Binary files a/simphony/libraries/siepic/source_data/ebeam_y_1550.gds and /dev/null differ diff --git a/simphony/libraries/sipann.py b/simphony/libraries/sipann.py index 49d8bdcd..8389f535 100644 --- a/simphony/libraries/sipann.py +++ b/simphony/libraries/sipann.py @@ -71,7 +71,7 @@ def __init__(self, model: TypeVar("M"), sigmas: Dict[str, float], **kwargs) -> N ) self.params = self.model.__dict__.copy() - self.rand_params = dict() + self.rand_params = {} self.regenerate_monte_carlo_parameters() def s_parameters(self, freqs: np.array) -> np.ndarray: @@ -132,7 +132,7 @@ def converted_func(input: float) -> float: class GapFuncSymmetric(SipannWrapper): - """Symmetric directional coupler, meaning both waveguides are the same + r"""Symmetric directional coupler, meaning both waveguides are the same shape. A gap function must describe the shape of the two @@ -209,24 +209,9 @@ def __init__( **kwargs ) - def update_variations(self, **kwargs): - self.nominal_width = self.params["width"] - self.nominal_thickness = self.params["thickness"] - - w = self.params["width"] + kwargs.get("corr_w") - h = self.params["thickness"] + kwargs.get("corr_t") - - self.layout_aware = True - self.params["width"] = w - self.params["thickness"] = h - - def regenerate_layout_aware_monte_carlo_parameters(self): - self.params["width"] = self.nominal_width - self.params["thickness"] = self.nominal_thickness - class GapFuncAntiSymmetric(SipannWrapper): - """Antisymmetric directional coupler, meaning both waveguides are + r"""Antisymmetric directional coupler, meaning both waveguides are differently shaped. A gap function describing the vertical distance between @@ -316,24 +301,9 @@ def __init__( **kwargs ) - def update_variations(self, **kwargs): - self.nominal_width = self.params["width"] - self.nominal_thickness = self.params["thickness"] - - w = self.params["width"] + kwargs.get("corr_w") - h = self.params["thickness"] + kwargs.get("corr_t") - - self.layout_aware = True - self.params["width"] = w - self.params["thickness"] = h - - def regenerate_layout_aware_monte_carlo_parameters(self): - self.params["width"] = self.nominal_width - self.params["thickness"] = self.nominal_thickness - class HalfRing(SipannWrapper): - """Half of a ring resonator. + r"""Half of a ring resonator. Uses a radius and a gap to describe the shape. @@ -391,24 +361,9 @@ def __init__( **kwargs ) - def update_variations(self, **kwargs): - self.nominal_width = self.params["width"] - self.nominal_thickness = self.params["thickness"] - - w = self.params["width"] + kwargs.get("corr_w") - h = self.params["thickness"] + kwargs.get("corr_t") - - self.layout_aware = True - self.params["width"] = w - self.params["thickness"] = h - - def regenerate_layout_aware_monte_carlo_parameters(self): - self.params["width"] = self.nominal_width - self.params["thickness"] = self.nominal_thickness - class HalfRacetrack(SipannWrapper): - """Half of a ring resonator, similar to the HalfRing class. + r"""Half of a ring resonator, similar to the HalfRing class. Uses a radius, gap and length to describe the shape of the device. @@ -476,21 +431,6 @@ def __init__( **kwargs ) - def update_variations(self, **kwargs): - self.nominal_width = self.params["width"] - self.nominal_thickness = self.params["thickness"] - - w = self.params["width"] + kwargs.get("corr_w") - h = self.params["thickness"] + kwargs.get("corr_t") - - self.layout_aware = True - self.params["width"] = w - self.params["thickness"] = h - - def regenerate_layout_aware_monte_carlo_parameters(self): - self.params["width"] = self.nominal_width - self.params["thickness"] = self.nominal_thickness - class StraightCoupler(SipannWrapper): """Straight directional coupler, both waveguides run parallel. @@ -548,24 +488,9 @@ def __init__( **kwargs ) - def update_variations(self, **kwargs): - self.nominal_width = self.params["width"] - self.nominal_thickness = self.params["thickness"] - - w = self.params["width"] + kwargs.get("corr_w") - h = self.params["thickness"] + kwargs.get("corr_t") - - self.layout_aware = True - self.params["width"] = w - self.params["thickness"] = h - - def regenerate_layout_aware_monte_carlo_parameters(self): - self.params["width"] = self.nominal_width - self.params["thickness"] = self.nominal_thickness - class Standard(SipannWrapper): - """Standard-shaped directional coupler. + r"""Standard-shaped directional coupler. Described by a gap, length, horizontal and vertical distance. @@ -640,24 +565,9 @@ def __init__( **kwargs ) - def update_variations(self, **kwargs): - self.nominal_width = self.params["width"] - self.nominal_thickness = self.params["thickness"] - - w = self.params["width"] + kwargs.get("corr_w") - h = self.params["thickness"] + kwargs.get("corr_t") - - self.layout_aware = True - self.params["width"] = w - self.params["thickness"] = h - - def regenerate_layout_aware_monte_carlo_parameters(self): - self.params["width"] = self.nominal_width - self.params["thickness"] = self.nominal_thickness - class DoubleHalfRing(SipannWrapper): - """Two equally sized half-rings coupling along their edges. + r"""Two equally sized half-rings coupling along their edges. Described by a radius and a gap between the two rings. @@ -717,24 +627,9 @@ def __init__( **kwargs ) - def update_variations(self, **kwargs): - self.nominal_width = self.params["width"] - self.nominal_thickness = self.params["thickness"] - - w = self.params["width"] + kwargs.get("corr_w") - h = self.params["thickness"] + kwargs.get("corr_t") - - self.layout_aware = True - self.params["width"] = w - self.params["thickness"] = h - - def regenerate_layout_aware_monte_carlo_parameters(self): - self.params["width"] = self.nominal_width - self.params["thickness"] = self.nominal_thickness - class AngledHalfRing(SipannWrapper): - """A halfring resonator, except what was the straight waveguide is now + r"""A halfring resonator, except what was the straight waveguide is now curved. Described by a radius, gap, and angle (theta) that the @@ -801,21 +696,6 @@ def __init__( **kwargs ) - def update_variations(self, **kwargs): - self.nominal_width = self.params["width"] - self.nominal_thickness = self.params["thickness"] - - w = self.params["width"] + kwargs.get("corr_w") - h = self.params["thickness"] + kwargs.get("corr_t") - - self.layout_aware = True - self.params["width"] = w - self.params["thickness"] = h - - def regenerate_layout_aware_monte_carlo_parameters(self): - self.params["width"] = self.nominal_width - self.params["thickness"] = self.nominal_thickness - class Waveguide(SipannWrapper): """Lossless model for a straight waveguide. Main use case is for playing @@ -864,24 +744,9 @@ def __init__( **kwargs ) - def update_variations(self, **kwargs): - self.nominal_width = self.params["width"] - self.nominal_thickness = self.params["thickness"] - - w = self.params["width"] + kwargs.get("corr_w") - h = self.params["thickness"] + kwargs.get("corr_t") - - self.layout_aware = True - self.params["width"] = w - self.params["thickness"] = h - - def regenerate_layout_aware_monte_carlo_parameters(self): - self.params["width"] = self.nominal_width - self.params["thickness"] = self.nominal_thickness - class Racetrack(SipannWrapper): - """Racetrack waveguide arc, used to connect to a racetrack directional + r"""Racetrack waveguide arc, used to connect to a racetrack directional coupler. Ports labeled as: @@ -948,24 +813,9 @@ def __init__( **kwargs ) - def update_variations(self, **kwargs): - self.nominal_width = self.params["width"] - self.nominal_thickness = self.params["thickness"] - - w = self.params["width"] + kwargs.get("corr_w") - h = self.params["thickness"] + kwargs.get("corr_t") - - self.layout_aware = True - self.params["width"] = w - self.params["thickness"] = h - - def regenerate_layout_aware_monte_carlo_parameters(self): - self.params["width"] = self.nominal_width - self.params["thickness"] = self.nominal_thickness - class PremadeCoupler(SipannWrapper): - """Loads premade couplers. + r"""Loads premade couplers. Various splitting ratio couplers have been made and saved. This function reloads them. Note that each of their lengths are different and are also returned for the users info. These have all been designed with waveguide diff --git a/simphony/models.py b/simphony/models.py index 1d7c7ad1..823e0105 100644 --- a/simphony/models.py +++ b/simphony/models.py @@ -23,12 +23,6 @@ class is the base class for all models. The ``Subcircuit`` class is where import numpy as np -try: - from gdsfactory import Component, ComponentReference - _has_gf = True -except ImportError: - _has_gf = False - from simphony.connect import create_block_diagonal, innerconnect_s from simphony.formatters import ModelFormatter, ModelJSONFormatter from simphony.layout import Circuit @@ -52,8 +46,6 @@ class Model: pins : A tuple of all the default pin names of the device. Must be set if pin_count is not. - component : - A gdsfactory Component object which is a representation of this component (optional). """ freq_range: ClassVar[Tuple[Optional[float], Optional[float]]] @@ -85,8 +77,6 @@ def __init__( The pins for the model. If not specified, the pins will be be initialized from cls.pins. If that is not specified, cls.pin_count number of pins will be initialized. - component : - A gdsfactory Component object for this component (optional). Raises ------ @@ -95,6 +85,8 @@ def __init__( NotImplementedError when pins is None and cls.pin_count and cls.pins are undefined. """ + self.circuit = Circuit(self) + # set the frequency range for the instance. resolution order: # 1. freq_range # 2. cls.freq_range @@ -108,6 +100,7 @@ def __init__( self.freq_range = (0, float("inf")) self.name = name + # initiate the Pin objects for the instance. resolution order: # 1. pins (list of Pin objects) # 2. cls.pins (tuple of pin names) @@ -127,13 +120,6 @@ def __init__( f"{name}.pin_count or {name}.pins needs to be defined." ) - if _has_gf: - self.component = Component() - self.component.name = name - - # Set circuit - self.circuit = Circuit(self) - def __str__(self) -> str: name = self.name or f"{self.__class__.__name__} component" return f"{name} with pins: {', '.join([pin.name for pin in self.pins])}" @@ -155,11 +141,7 @@ def _get_next_unconnected_pin(self) -> Pin: def _isconnected(self) -> bool: """Returns whether this component is connected to other components.""" - for pin in self.pins: - if pin._isconnected(): - return True - - return False + return any(pin._isconnected() for pin in self.pins) def _on_connect(self, component: "Model") -> None: """This method is called whenever one of this component's pins is @@ -243,12 +225,7 @@ def _on_disconnect_recursive(self, circuit: Circuit) -> None: if circuit._add(component): component._on_disconnect_recursive(circuit) - def connect( - self, - component_or_pin: Union["Model", Pin], - component1_ref: "ComponentReference" = None, - component2_ref: "ComponentReference" = None, - ) -> "Model": + def connect(self, component_or_pin: Union["Model", Pin]) -> "Model": """Connects the next available (unconnected) pin from this component to the component/pin passed in as the argument. @@ -256,15 +233,7 @@ def connect( component is connected to the first available pin from the other component. """ - if None in (component1_ref, component2_ref): - self._get_next_unconnected_pin().connect(component_or_pin) - elif _has_gf: - self._get_next_unconnected_pin().connect( - component_or_pin, component1_ref, component2_ref - ) - else: - raise ImportError("gdsfactory must be installed to connect gdsfactory components. Try `pip install gdsfactory`.") - + self._get_next_unconnected_pin().connect(component_or_pin) return self def disconnect(self) -> None: @@ -272,29 +241,17 @@ def disconnect(self) -> None: for pin in self.pins: pin.disconnect() - def interface( - self, - component: "Model", - component1_ref: "ComponentReference" = None, - component2_ref: "ComponentReference" = None, - ) -> "Model": + def interface(self, component: "Model") -> "Model": """Interfaces this component to the component passed in by connecting pins with the same names. Only pins that have been renamed will be connected. """ - if None in (component1_ref, component2_ref): - for selfpin in self.pins: - for componentpin in component.pins: - if selfpin.name[0:3] != "pin" and selfpin.name == componentpin.name: - selfpin.connect(componentpin) - elif _has_gf: - for selfpin in self.pins: - for componentpin in component.pins: - if selfpin.name[0:3] != "pin" and selfpin.name == componentpin.name: - selfpin.connect(componentpin, component1_ref, component2_ref) - else: - raise ImportError("gdsfactory must be installed to connect gdsfactory components. Try `pip install gdsfactory`.") + for selfpin in self.pins: + for componentpin in component.pins: + if selfpin.name[:3] != "pin" and selfpin.name == componentpin.name: + selfpin.connect(componentpin) + return self def monte_carlo_s_parameters(self, freqs: "np.array") -> "np.ndarray": @@ -319,28 +276,6 @@ def monte_carlo_s_parameters(self, freqs: "np.array") -> "np.ndarray": """ return self.s_parameters(freqs) - def layout_aware_monte_carlo_s_parameters(self, freqs: "np.array") -> "np.ndarray": - """Implements the monte carlo routine for the given Model. - - If no monte carlo routine is defined, the default behavior returns the - result of a call to ``s_parameters()``. - - Parameters - ---------- - freqs : np.array - The frequency range to generate monte carlo s-parameters over. - - Returns - ------- - s : np.ndarray - The scattering parameters corresponding to the frequency range. - Its shape should be (the number of frequency points x ports x ports). - If the scattering parameters are requested for only a single - frequency, for example, and the device has 4 ports, the shape - returned by ``monte_carlo_s_parameters`` would be (1, 4, 4). - """ - return self.s_parameters(freqs) - def multiconnect(self, *connections: Union["Model", Pin, None]) -> "Model": """Connects this component to the specified connections by looping through each connection and connecting it with the corresponding pin. @@ -370,7 +305,7 @@ def regenerate_monte_carlo_parameters(self) -> None: circuit; they will be more or less consistent within a single small circuit. - The ``MonteCarloSweepSimulator`` calls this function once per run over + The ``MonteCarloSweepSimulation`` calls this function once per run over the circuit. Notes @@ -380,27 +315,6 @@ def regenerate_monte_carlo_parameters(self) -> None: """ pass - def regenerate_layout_aware_monte_carlo_parameters(self): - """Reassigns dimension parameters to the nominal values for the component. - - If a monte carlo method is not implemented for a given model, this - method does nothing. However, it can optionally be implemented so that - parameters are reassigned for every run. - - The ``LayoutAwareMonteCarloSweepSimulator`` calls this function once per run per component. - - Notes - ----- - This function should not accept any parameters, but may act on instance - or class attributes. - """ - pass - - def update_variations(self, **kwargs): - """Update width and thickness variations for the component using correlated - samples. This is used for layout-aware Monte Carlo runs.""" - pass - def rename_pins(self, *names: str) -> None: """Renames the pins for this component. @@ -590,83 +504,22 @@ def __init__( raise ValueError( f"Multiple pins named '{pin.name}' cannot exist in a subcircuit." ) - else: - # keep track of the pin to re-expose - pins.append(pin) + # keep track of the pin to re-expose + pins.append(pin) - # make the pin's owner this component if permanent - if permanent: - pin_names[pin.name] = True - pin._component = self + # make the pin's owner this component if permanent + if permanent: + pin_names[pin.name] = True + pin._component = self - if len(pins) == 0: + if not pins: raise ValueError( "A subcircuit needs to contain at least one unconnected pin." ) self._wrapped_circuit = circuit - super().__init__( - **kwargs, - freq_range=freq_range, - name=name, - pins=pins, - ) - - def __get_s_params_from_cache(self, component, freqs: "np.array", s_parameters_method: str = "s_parameters"): - """Get the s_params from the cache if possible.""" - if s_parameters_method == "s_parameters": - # each frequency has a different s-matrix, so we need to cache - # the s-matrices by frequency as well as component - s_params = [] - for freq in freqs: - try: - # use the cached s-matrix if available - s_matrix = self.__class__.scache[component][freq] - except KeyError: - # make sure the frequency dict is created - if component not in self.__class__.scache: - self.__class__.scache[component] = {} - - # store the s-matrix for the frequency and component - s_matrix = getattr(component, s_parameters_method)( - np.array([freq]) - )[0] - self.__class__.scache[component][freq] = s_matrix - - # add the s-matrix to our list of s-matrices - s_params.append(s_matrix) - - # convert to numpy array for the rest of the function - return np.array(s_params) - elif ( - s_parameters_method == "monte_carlo_s_parameters" - or "layout_aware_monte_carlo_s_parameters" - ): - # don't cache Monte Carlo scattering parameters - return getattr(component, s_parameters_method)(freqs) - - def __compute_s_block(self, s_block, available_pins, all_pins): - """Use the subnetwork growth algorithm for each connection.""" - for pin in all_pins: - # make sure pins only get connected once - # and pins connected to simulators get skipped - if ( - pin._isconnected(include_simulators=False) - and pin in available_pins - and pin._connection in available_pins - ): - # the pin indices in available_pins lines up with the row/column - # indices in the matrix. as the matrix shrinks, we remove pins - # from available_pins so the indices always line up - k = available_pins.index(pin) - l = available_pins.index(pin._connection) - - s_block = innerconnect_s(s_block, k, l) - - available_pins.remove(pin) - available_pins.remove(pin._connection) - return s_block + super().__init__(**kwargs, freq_range=freq_range, name=name, pins=pins) def _s_parameters( self, @@ -700,8 +553,34 @@ def _s_parameters( continue # get the s_params from the cache if possible - s_params = self.__get_s_params_from_cache(component, freqs, s_parameters_method) - + if s_parameters_method == "monte_carlo_s_parameters": + # don't cache Monte Carlo scattering parameters + s_params = getattr(component, s_parameters_method)(freqs) + + elif s_parameters_method == "s_parameters": + # each frequency has a different s-matrix, so we need to cache + # the s-matrices by frequency as well as component + s_params = [] + for freq in freqs: + try: + # use the cached s-matrix if available + s_matrix = self.__class__.scache[component][freq] + except KeyError: + # make sure the frequency dict is created + if component not in self.__class__.scache: + self.__class__.scache[component] = {} + + # store the s-matrix for the frequency and component + s_matrix = getattr(component, s_parameters_method)( + np.array([freq]) + )[0] + self.__class__.scache[component][freq] = s_matrix + + # add the s-matrix to our list of s-matrices + s_params.append(s_matrix) + + # convert to numpy array for the rest of the function + s_params = np.array(s_params) # merge the s_params into the block diagonal matrix if s_block is None: if s_params.ndim == 3: @@ -716,42 +595,36 @@ def _s_parameters( available_pins += component.pins # use the subnetwork growth algorithm for each connection - return self.__compute_s_block(s_block, available_pins, all_pins) + for pin in all_pins: + # make sure pins only get connected once + # and pins connected to simulators get skipped + if ( + pin._isconnected(include_simulators=False) + and pin in available_pins + and pin._connection in available_pins + ): + # the pin indices in available_pins lines up with the row/column + # indices in the matrix. as the matrix shrinks, we remove pins + # from available_pins so the indices always line up + k = available_pins.index(pin) + l = available_pins.index(pin._connection) + + s_block = innerconnect_s(s_block, k, l) + + available_pins.remove(pin) + available_pins.remove(pin._connection) + + return s_block def monte_carlo_s_parameters(self, freqs: "np.array") -> "np.ndarray": """Returns the Monte Carlo scattering parameters for the subcircuit.""" return self._s_parameters(freqs, "monte_carlo_s_parameters") - def layout_aware_monte_carlo_s_parameters(self, freqs: "np.array") -> "np.ndarray": - """Returns the Monte Carlo scattering parameters for the subcircuit.""" - return self._s_parameters(freqs, "layout_aware_monte_carlo_s_parameters") - def regenerate_monte_carlo_parameters(self) -> None: """Regenerates parameters used to generate Monte Carlo s-matrices.""" for component in self._wrapped_circuit: component.regenerate_monte_carlo_parameters() - def regenerate_layout_aware_monte_carlo_parameters(self): - """Reassigns dimension parameters to the nominal values for the component. - - If a monte carlo method is not implemented for a given model, this - method does nothing. However, it can optionally be implemented so that - parameters are reassigned for every run. - - The ``LayoutAwareMonteCarloSweepSimulator`` calls this function once per run per component. - - Notes - ----- - This function should not accept any parameters, but may act on instance - or class attributes. - """ - for component in self._wrapped_circuit: - component.regenerate_layout_aware_monte_carlo_parameters() - - def update_variations(self, **kwargs): - for component in self._wrapped_circuit: - component.update_variations(**kwargs) - def s_parameters(self, freqs: "np.array") -> "np.ndarray": """Returns the scattering parameters for the subcircuit.""" return self._s_parameters(freqs) diff --git a/simphony/pins.py b/simphony/pins.py index 4ee98934..93ada597 100644 --- a/simphony/pins.py +++ b/simphony/pins.py @@ -14,12 +14,7 @@ from typing import TYPE_CHECKING, Union if TYPE_CHECKING: - from simphony.models import Model -try: - from gdsfactory.types import ComponentReference - _has_gf = True -except ImportError: - _has_gf = False + from simphony import Model class Pin: @@ -43,6 +38,9 @@ def __init__(self, component: "Model", name: str) -> None: self._connection = None self.name = name + def __repr__(self) -> str: + return self.name + def _isconnected(self, *, include_simulators: bool = True) -> bool: """Returns whether or not this pin is connected to another pin. @@ -65,12 +63,7 @@ def _isconnected(self, *, include_simulators: bool = True) -> bool: self._connection._component, Simulator ) and not isinstance(self._connection._component, SimulationModel) - def connect( - self, - pin_or_component: Union["Pin", "Model"], - component1_ref: "ComponentReference" = None, - component2_ref: "ComponentReference" = None, - ) -> None: + def connect(self, pin_or_component: Union["Pin", "Model"]) -> None: """Connects this pin to the pin/component that is passed in. If a component instance is passed in, this pin will connect to @@ -92,20 +85,6 @@ def connect( # let the components know that a new connection was established self._component._on_connect(pin._component) - if None not in (component1_ref, component2_ref): - if _has_gf: - if ( - component1_ref.parent.name is self._component.name - and component2_ref.parent.name is pin._component.name - ): - component1_ref.connect(self.name, component2_ref.ports[pin.name]) - else: - raise ValueError( - f"Invalid component reference(s) passed. {component1_ref}, {component2_ref}, {self._component.name}, {pin._component.name}" - ) - else: - raise ImportError("gdsfactory is not installed. Try `pip install gdsfactory`.") - def disconnect(self) -> None: """Disconnects this pin to whatever it is connected to.""" if self._isconnected(): @@ -119,11 +98,6 @@ def disconnect(self) -> None: def rename(self, name: str) -> None: """Renames the pin.""" - try: - if self._component.component.ports: - self._component.component.ports[name] = self._component.component.ports.pop(self.name) - except AttributeError: - pass self.name = name diff --git a/simphony/plugins/siepic/__init__.py b/simphony/plugins/siepic/__init__.py index 5f654ad2..bff372e8 100644 --- a/simphony/plugins/siepic/__init__.py +++ b/simphony/plugins/siepic/__init__.py @@ -245,11 +245,7 @@ def : dict return {"name": name, "model": model, "ports": ports, "params": params} def visit_ports(self, node, visited_children): - # ports = ((external / internal) ws?)+ - ports = [] - for port in visited_children: - ports.append(port[0][0]) - return ports + return [port[0][0] for port in visited_children] def visit_external(self, node, visited_children): # external = ~r"([-\w]+(detector|laser)[\d]?)" diff --git a/simphony/simulation.py b/simphony/simulation.py index 93c4c9ab..70a6fd89 100644 --- a/simphony/simulation.py +++ b/simphony/simulation.py @@ -11,25 +11,18 @@ """ from cmath import rect -from typing import Callable, ClassVar, List, Optional, Tuple, Union +from typing import TYPE_CHECKING, Callable, ClassVar, List, Optional, Tuple, Union import numpy as np - -try: - from gdsfactory import Component - - _has_gf = True -except ImportError: - _has_gf = False from scipy.constants import h -from scipy.linalg import cholesky, lu from scipy.signal import butter, sosfiltfilt from simphony import Model -from simphony.layout import Circuit -from simphony.libraries import siepic from simphony.tools import add_polar, wl2freq +if TYPE_CHECKING: + from simphony.layout import Circuit + # this variable keeps track of the current simulation context (if any) context = None @@ -145,9 +138,7 @@ def _expand_array(self, arr: np.array, size: int) -> np.array: # calculate how many times each value needs to be repeated expanded = np.zeros(size) - repeat = int(size / arr_len) - remainder = size % arr_len - + repeat, remainder = divmod(size, arr_len) # expand each value in the given array for i, value in enumerate(arr): # calculate ranges, accounting for remainders @@ -160,103 +151,6 @@ def _expand_array(self, arr: np.array, size: int) -> np.array: return expanded - def __compute_contributions_no_mod(self, t, scattering): - contributions = scattering[:, np.newaxis] + np.zeros((self.shape[1], 2)) - return contributions[:, :, np.newaxis] + np.zeros((self.num_samples, 2)) - - def __compute_contributions_with_mod(self, t, contributions, scattering, source): - for i, freq in enumerate(self.freqs): - for j, power in enumerate(source._coupled_powers): - angle = scattering[i, 1] - offset = angle / (2 * np.pi * freq) - t = np.linspace( - offset, - offset + (self.num_samples - 1) / self.fs, - self.num_samples, - ) - - mod = source.modfn(freq, power, t) - if np.shape(mod)[0] == 2: - contributions[i, j] = np.stack( - ( - contributions[i, j, 0, 0] * np.sqrt(mod[0]), - contributions[i, j, 0, 1] + source.phase + mod[1], - ), - axis=-1, - ) - else: - contributions[i, j] = np.stack( - ( - contributions[i, j, 0, 0] * np.sqrt(mod), - contributions[i, j, 0, 1] - + np.repeat(source.phase, self.num_samples), - ), - axis=-1, - ) - return contributions - - def __compute_detector_power(self, t, detector): - powers = [] - for pin in detector.pins: - output_index = self.circuit.get_pin_index(pin._connection) - - # figure out how the sources interfere - transmissions = np.zeros( - (self.shape[0], self.shape[1], self.num_samples, 2) - ) - for i, pin in enumerate(self.circuit.pins): - # calculate transmissions for every source connected to - # the circuit - - # find the source that corresponds to this pin - source = None - for s in self.sources: - if s.index == i: - source = s - break - else: - continue - - # lookup how much this source contributes to the output field - scattering = self.s_params[:, output_index, source.index] - - # convert to polar coordinates if necessary - # NOTE: complex values should be stored as a tuple. this conversion - # is for backwards compatibility with siepic library. once that gets - # updated, we can remove this, although it will be a breaking change - if np.shape(scattering[0]) != (2,): - scattering = np.column_stack( - (np.abs(scattering), np.angle(scattering)) - ) - - if not source.modfn: - # no modulation - contributions = self.__compute_contributions_no_mod(t, scattering) - else: - contributions = scattering[:, np.newaxis] + np.zeros( - (self.shape[1], 2) - ) - contributions = contributions[:, :, np.newaxis] + np.zeros( - (self.num_samples, 2) - ) - contributions = self.__compute_contributions_with_mod( - t, contributions, scattering, source - ) - - # add all of the different source contributions together - for i in range(self.shape[0]): - for j in range(self.shape[1]): - for k in range(self.num_samples): - transmissions[i, j, k] = add_polar( - transmissions[i, j, k], contributions[i, j, k] - ) - - # convert the output fields to powers - self.transmissions.append(transmissions) - powers.append((transmissions[:, :, :, 0] ** 2)) - - return powers - def _get_signals(self) -> np.ndarray: """Get the signals in the order set by the detectors. Each signal is a multi-dimensional array. The first index corresponds to frequency. The @@ -307,7 +201,103 @@ def _get_signals(self) -> np.ndarray: signals = [] for detector in self.detectors: # calculate the power detected at each detector pin - powers = self.__compute_detector_power(t, detector) + powers = [] + for pin in detector.pins: + output_index = self.circuit.get_pin_index(pin._connection) + + # figure out how the sources interfere + transmissions = np.zeros( + (self.shape[0], self.shape[1], self.num_samples, 2) + ) + for i, pin in enumerate(self.circuit.pins): + # calculate transmissions for every source connected to + # the circuit + + # find the source that corresponds to this pin + source = None + for s in self.sources: + if s.index == i: + source = s + break + else: + continue + + # lookup how much this source contributes to the output field + scattering = self.s_params[:, output_index, source.index] + + # convert to polar coordinates if necessary + # NOTE: complex values should be stored as a tuple. this conversion + # is for backwards compatibility with siepic library. once that gets + # updated, we can remove this, although it will be a breaking change + if np.shape(scattering[0]) != (2,): + scattering = np.column_stack( + (np.abs(scattering), np.angle(scattering)) + ) + + if not source.modfn: + # no modulation + contributions = np.stack( + ( + scattering[:, 0, np.newaxis] + * np.sqrt(source._coupled_powers), + scattering[:, 1, np.newaxis] + + np.repeat(source.phase, self.shape[1]), + ), + axis=-1, + ) + contributions = contributions[:, :, np.newaxis] + np.zeros( + (self.num_samples, 2) + ) + else: + contributions = scattering[:, np.newaxis] + np.zeros( + (self.shape[1], 2) + ) + contributions = contributions[:, :, np.newaxis] + np.zeros( + (self.num_samples, 2) + ) + for i, freq in enumerate(self.freqs): + for j, power in enumerate(source._coupled_powers): + angle = scattering[i, 1] + offset = angle / (2 * np.pi * freq) + t = np.linspace( + offset, + offset + (self.num_samples - 1) / self.fs, + self.num_samples, + ) + + mod = source.modfn(freq, power, t) + if np.shape(mod)[0] == 2: + contributions[i, j] = np.stack( + ( + contributions[i, j, 0, 0] * np.sqrt(mod[0]), + contributions[i, j, 0, 1] + + source.phase + + mod[1], + ), + axis=-1, + ) + else: + contributions[i, j] = np.stack( + ( + contributions[i, j, 0, 0] * np.sqrt(mod), + contributions[i, j, 0, 1] + + np.repeat(source.phase, self.num_samples), + ), + axis=-1, + ) + + # add all of the different source contributions together + for i in range(self.shape[0]): + for j in range(self.shape[1]): + for k in range(self.num_samples): + transmissions[i, j, k] = add_polar( + transmissions[i, j, k], contributions[i, j, k] + ) + + # convert the output fields to powers + self.transmissions.append(transmissions) + powers.append((transmissions[:, :, :, 0] ** 2)) + # send the powers through the detectors to convert to signals signals.extend(detector._detect(powers)) @@ -331,164 +321,6 @@ def monte_carlo(self, flag: bool) -> None: "monte_carlo_s_parameters" if flag else "s_parameters" ) - def _compute_correlated_samples(self, coords, sigmaw, sigmat, l, runs): - x = [0] * len(coords) - y = [0] * len(coords) - - # get x and y co-ordinates - components = [ - component - for component in self.circuit._get_components() - if not isinstance(component, (Laser, Detector)) - ] - if len(coords) != len(components): - raise ValueError( - f'Incorrect number of component coordinates passed to argument "coords". Expected {len(components)}, got {len(coords)}' - ) - - n = len(self.circuit._get_components()) - 2 - corr_matrix_w = np.zeros((n, n)) - corr_matrix_t = np.zeros((n, n)) - - # generate correlation values - for i in range(n): - for k in range(n): - - corr_val = np.exp( - -((x[k] - x[i]) ** 2 + (y[k] - y[i]) ** 2) / (0.5 * (l**2)) - ) - - corr_matrix_w[i][k] = corr_matrix_w[k][i] = corr_val - corr_matrix_t[i][k] = corr_matrix_t[k][i] = corr_val - - cov_matrix_w = np.zeros((n, n)) - cov_matrix_t = np.zeros((n, n)) - # generate covariance matrix - for i in range(n): - for k in range(n): - cov_matrix_w[i][k] = sigmaw * corr_matrix_w[i][k] * sigmaw - cov_matrix_t[i][k] = sigmat * corr_matrix_t[i][k] * sigmat - - try: - # perform Cholesky decomposition on the covariance matrices - l_w = cholesky(cov_matrix_w, lower=True) - l_t = cholesky(cov_matrix_t, lower=True) - except np.linalg.LinAlgError: - # if matrix is not positive-definite, do LU decomposition - _, l_w, _ = lu(cov_matrix_w) - _, l_t, _ = lu(cov_matrix_t) - - # generate random distribution with mean 0 and standard deviation of 1, size: no. of elements x no. of runs - X = np.random.multivariate_normal(np.zeros(n), np.eye(n, n), runs).T - - # generate correlation samples - corr_sample_matrix_w = np.dot(l_w, X) - corr_sample_matrix_t = np.dot(l_t, X) - - return (corr_sample_matrix_w, corr_sample_matrix_t) - - def layout_aware_simulation( - self, - component_or_circuit: Union["Component", Circuit], - sigmaw: float = 5, - sigmat: float = 2, - l: float = 4.5e-3, - runs: int = 10, - num_samples: int = 1, - ) -> Tuple[np.array, np.array]: - """Runs the layout-aware Monte Carlo sweep simulation for the circuit. - - Parameters - ---------- - component_or_circuit : - The component or circuit to run the simulation on. Can either - be a gdsfactory Component or Simphony Circuit object. - sigmaw : - Standard deviation of width variations (default 5) - sigmat : - Standard deviation of thickness variations (default 2) - l : - Correlation length (m) (default 4.5e-3) - runs : - The number of Monte Carlo iterations to run (default 10). - num_samples: - The number of samples to take. If only one sample is taken, it will - be the theoretical value of the circuit. If more than one sample is - taken, they will vary based on simulated noise. - """ - results = [] # store results - - if _has_gf and isinstance(component_or_circuit, Component): - components = [ - component - for component in self.circuit._get_components() - if not isinstance(component, (Laser, Detector)) - ] # get all components except the Laser and Detector - gfcomponents = [ - c.path if isinstance(c, siepic.Waveguide) else c.component - for c in components - ] # get all gdsfactory component attributes - - # Extract co-ordinates - coords = { - component: { - "x": (gfcomponents[i].ports[0].x + gfcomponents[i].ports[-1].x) / 2, - "y": (gfcomponents[i].ports[0].y + gfcomponents[i].ports[-1].y) / 2, - } - if isinstance(component, siepic.Waveguide) - else {"x": gfcomponents[i].x, "y": gfcomponents[i].y} - for i, component in enumerate(components) - } - elif isinstance(component_or_circuit, Circuit): - components = [ - component - for component in self.circuit._get_components() - if not isinstance(component, (Laser, Detector)) - ] # get all components except the Laser and Detector - gfcomponents = [ - c.component for c in components - ] # get all gdsfactory component attributes - - coords = { - component: {"x": gfcomponents[i].x, "y": gfcomponents[i].y} - for i, component in enumerate(components) - } - else: - raise ValueError( - "component_or_circuit must be qa gdsfactory component or a Simphony Circuit object." - ) - - # compute correlated samples - corr_sample_matrix_w, corr_sample_matrix_t = self._compute_correlated_samples( - coords, sigmaw, sigmat, l, runs - ) - - n = len(components) - for i in range(runs): - # use s_parameters for the first run, then monte_carlo_* for the rest - - # update component parameters - [ - components[idx].update_variations( - corr_w=corr_sample_matrix_w[idx][i], - corr_t=corr_sample_matrix_t[idx][i], - ) - for idx in range(n) - ] - - self.s_parameters_method = ( - "layout_aware_monte_carlo_s_parameters" if i else "s_parameters" - ) - - results.append(self.sample(num_samples=num_samples)) - - [ - components[idx].regenerate_layout_aware_monte_carlo_parameters() - for idx in range(n) - ] - - return results - def s_parameters(self, freqs: np.array) -> np.ndarray: """Gets the scattering parameters for the specified frequencies. @@ -651,7 +483,7 @@ def __init__( # initialize properties self._coupled_powers = np.array([]) - self._freqs = np.array([freq if freq else wl2freq(wl)]) + self._freqs = np.array([freq or wl2freq(wl)]) self._powers = np.array([power]) self.coupling_ratio = from_db(-np.abs(coupling_loss)) self.freqs = np.array([]) @@ -917,10 +749,7 @@ def __init__( rf_noise=0, **kwargs, ): - super().__init__( - *args, - **kwargs, - ) + super().__init__(*args, **kwargs) # initialize parameters self.monitor_conversion_gain = monitor_conversion_gain diff --git a/simphony/simulators.py b/simphony/simulators.py index 6624528a..13ae700a 100644 --- a/simphony/simulators.py +++ b/simphony/simulators.py @@ -83,10 +83,7 @@ def simulate( # if the scattering parameters for the circuit are cached, use those try: - if ( - s_parameters_method == "monte_carlo_s_parameters" - or "layout_aware_monte_carlo_s_parameters" - ): + if s_parameters_method == "monte_carlo_s_parameters": raise RuntimeError("No caching for Monte Carlo simulations.") s_params = self.__class__.scache[self.circuit] @@ -166,7 +163,7 @@ def simulate( """ freqs, power_ratios = super().simulate(**kwargs, freqs=self.freqs) - mode = mode if mode else self.mode + mode = mode or self.mode if mode == "wl": return (freq2wl(freqs), power_ratios) diff --git a/simphony/tests/mzi_monte_carlo_sparameters.npy b/simphony/tests/mzi_monte_carlo_sparameters.npy deleted file mode 100644 index 844024af..00000000 Binary files a/simphony/tests/mzi_monte_carlo_sparameters.npy and /dev/null differ diff --git a/simphony/tests/mzi_sparameters.npy b/simphony/tests/mzi_sparameters.npy deleted file mode 100644 index 05e309b2..00000000 Binary files a/simphony/tests/mzi_sparameters.npy and /dev/null differ diff --git a/simphony/tests/test_formatters.py b/simphony/tests/test_formatters.py index 090662f4..b9375e40 100644 --- a/simphony/tests/test_formatters.py +++ b/simphony/tests/test_formatters.py @@ -147,10 +147,4 @@ def test_parse(self, freqs, mzi4): mzi42 = Circuit.from_file(spi, formatter=CircuitSiEPICFormatter()) - _, p1 = mzi4._get_components()[-1].simulate() - - mzi42._get_components()[-1].freqs = freqs - - _, p2 = mzi42._get_components()[-1].simulate() - - assert np.allclose(p1, p2) + assert np.allclose(mzi4.s_parameters(freqs), mzi42.s_parameters(freqs)) diff --git a/simphony/tests/test_layout.py b/simphony/tests/test_layout.py index 46813f18..99a85d2f 100644 --- a/simphony/tests/test_layout.py +++ b/simphony/tests/test_layout.py @@ -4,14 +4,419 @@ import numpy as np import pytest -import os from simphony.libraries import siepic from simphony.tools import wl2freq -mzi_s_parameters = np.load(os.path.join(os.path.dirname(__file__), "mzi_sparameters.npy")) +mzi_s_parameters = np.array( + [ + [ + [-0.04463059 - 3.85348555e-02j, 0.03791468 - 1.65670645e-02j], + [0.03791614 - 1.65674861e-02j, -0.04462303 - 3.85407576e-02j], + ], + [ + [-0.03791501 - 6.10034051e-02j, 0.04133553 - 2.37359859e-02j], + [0.04133401 - 2.37360347e-02j, -0.03791505 - 6.09916588e-02j], + ], + [ + [-0.03565113 - 6.37895649e-02j, -0.08341049 + 6.49107954e-02j], + [-0.08341067 + 6.49106957e-02j, -0.0356537 - 6.37917582e-02j], + ], + [ + [-0.02270682 - 8.51462210e-02j, 0.02949049 - 2.82800891e-02j], + [0.02949159 - 2.82789021e-02j, -0.02271708 - 8.51442093e-02j], + ], + [ + [-0.02097628 - 7.40575215e-02j, 0.06436091 - 7.75505274e-02j], + [0.06436067 - 7.75520119e-02j, -0.02096778 - 7.40724643e-02j], + ], + [ + [-0.01181197 - 6.11944254e-02j, -0.08389937 + 1.29860682e-01j], + [-0.08389907 + 1.29860193e-01j, -0.01180817 - 6.11847245e-02j], + ], + [ + [-0.03925872 - 6.56142995e-02j, 0.00959868 - 2.08414675e-02j], + [0.0095955 - 2.08389496e-02j, -0.03925486 - 6.56135099e-02j], + ], + [ + [-0.01444806 - 5.36701951e-02j, 0.06839281 - 1.66983606e-01j], + [0.06839509 - 1.66983660e-01j, -0.01445821 - 5.36601374e-02j], + ], + [ + [-0.01271241 - 5.33899607e-02j, -0.06259837 + 1.95986599e-01j], + [-0.06259596 + 1.95987937e-01j, -0.01271647 - 5.34037132e-02j], + ], + [ + [-0.02494601 - 2.20945950e-02j, -0.00720848 + 2.55561372e-02j], + [-0.00721234 + 2.55489823e-02j, -0.02494267 - 2.20941588e-02j], + ], + [ + [0.01211876 - 6.91067871e-02j, 0.05818844 - 2.79851000e-01j], + [0.05818949 - 2.79848827e-01j, 0.01212512 - 6.91186436e-02j], + ], + [ + [0.03324559 - 4.24163165e-02j, -0.04038225 + 2.36810768e-01j], + [-0.04038177 + 2.36813612e-01j, 0.03325981 - 4.23940076e-02j], + ], + [ + [0.07503646 - 1.36955856e-02j, -0.01932495 + 1.16005203e-01j], + [-0.01932216 + 1.15998970e-01j, 0.07501582 - 1.36971411e-02j], + ], + [ + [0.03609354 - 5.33142590e-02j, 0.03211148 - 4.01178834e-01j], + [0.03211074 - 4.01178229e-01j, 0.0360865 - 5.33057465e-02j], + ], + [ + [0.09558368 - 3.02272013e-02j, -0.0112313 + 2.46219393e-01j], + [-0.01123663 + 2.46225183e-01j, 0.09557903 - 3.02752914e-02j], + ], + [ + [0.09473917 - 3.93712650e-02j, -0.00286189 + 2.45028693e-01j], + [-0.00285623 + 2.45030423e-01j, 0.0947806 - 3.93379922e-02j], + ], + [ + [0.01998947 - 1.60733895e-02j, -0.00353223 - 4.89373288e-01j], + [-0.00353202 - 4.89372669e-01j, 0.01997794 - 1.60727199e-02j], + ], + [ + [0.06463397 - 8.93727820e-02j, 0.01337211 + 1.80084991e-01j], + [0.01337209 + 1.80076723e-01j, 0.06460575 - 8.93348824e-02j], + ], + [ + [0.03157801 - 8.00734225e-02j, 0.02387594 + 3.57864496e-01j], + [0.02387243 + 3.57868423e-01j, 0.03157026 - 8.01466763e-02j], + ], + [ + [0.02956797 - 4.56126009e-02j, -0.04541933 - 5.14252173e-01j], + [-0.04542131 - 5.14250978e-01j, 0.02959902 - 4.55857284e-02j], + ], + [ + [0.00624431 - 1.39027390e-01j, 0.00942046 + 7.65663409e-02j], + [0.00942925 + 7.65654359e-02j, 0.00626149 - 1.39028819e-01j], + ], + [ + [0.05149494 - 7.16564745e-02j, 0.06234742 + 4.54777323e-01j], + [0.06234368 + 4.54776670e-01j, 0.05146629 - 7.16236043e-02j], + ], + [ + [0.07708967 - 5.99820313e-02j, -0.0861453 - 4.75746466e-01j], + [-0.08614798 - 4.75748824e-01j, 0.07708453 - 6.00066736e-02j], + ], + [ + [0.01404725 - 4.34224742e-02j, -0.00644745 - 4.12403079e-02j], + [-0.00644588 - 4.12325287e-02j, 0.01405055 - 4.34227150e-02j], + ], + [ + [0.11100129 - 3.57490163e-02j, 0.13262267 + 4.94240432e-01j], + [0.13262267 + 4.94235931e-01j, 0.11100108 - 3.57700853e-02j], + ], + [ + [0.08991462 - 2.47841924e-03j, -0.12667894 - 3.81790452e-01j], + [-0.12667764 - 3.81796807e-01j, 0.08995224 - 2.45588655e-03j], + ], + [ + [0.08546799 + 5.32314390e-02j, -0.05360831 - 1.41760900e-01j], + [-0.05361283 - 1.41756833e-01j, 0.08543929 + 5.32387284e-02j], + ], + [ + [0.09868772 - 6.85563056e-03j, 0.19967064 + 4.75298986e-01j], + [0.1996717 + 4.75298983e-01j, 0.09867837 - 6.83813358e-03j], + ], + [ + [0.10702602 + 5.18395059e-02j, -0.12654207 - 2.60064541e-01j], + [-0.12653649 - 2.60059194e-01j, 0.10700798 + 5.17823878e-02j], + ], + [ + [0.11014618 + 5.50112829e-02j, -0.11042125 - 2.10621760e-01j], + [-0.1104253 - 2.10627215e-01j, 0.11019197 + 5.50340629e-02j], + ], + [ + [0.05787243 + 3.23258382e-02j, 0.2520551 + 4.08239693e-01j], + [0.25205517 + 4.08239571e-01j, 0.05786652 + 3.23278780e-02j], + ], + [ + [0.09887536 + 4.11514971e-02j, -0.10224707 - 1.48718128e-01j], + [-0.10224997 - 1.48716555e-01j, 0.09886464 + 4.11874787e-02j], + ], + [ + [0.06755546 + 3.98103435e-02j, -0.17994784 - 2.36872088e-01j], + [-0.17994591 - 2.36874171e-01j, 0.06752747 + 3.97713798e-02j], + ], + [ + [0.0369604 + 5.73943522e-02j, 0.26832401 + 3.13394869e-01j], + [0.26832524 + 3.13395050e-01j, 0.03698308 + 5.73956444e-02j], + ], + [ + [0.04970337 + 2.02350938e-02j, -0.0526436 - 5.23225470e-02j], + [-0.05264663 - 5.23214694e-02j, 0.04971245 + 2.02237515e-02j], + ], + [ + [0.03623432 + 5.49353307e-02j, -0.22524184 - 2.16919236e-01j], + [-0.22524166 - 2.16919409e-01j, 0.03624169 + 5.49679502e-02j], + ], + [ + [0.03401243 + 6.72185478e-02j, 0.24387036 + 2.13098534e-01j], + [0.2438699 + 2.13098217e-01j, 0.03398906 + 6.72091133e-02j], + ], + [ + [0.00716189 + 5.74121804e-02j, 0.00808872 + 8.28646313e-03j], + [0.00808773 + 8.27984042e-03j, 0.00716363 + 5.74113566e-02j], + ], + [ + [0.0260074 + 7.78300575e-02j, -0.23541402 - 1.73762424e-01j], + [-0.23542479 - 1.73770149e-01j, 0.0260093 + 7.78110671e-02j], + ], + [ + [0.01197183 + 8.42286073e-02j, 0.18746779 + 1.26023316e-01j], + [0.18747699 + 1.26034659e-01j, 0.0119872 + 8.42427598e-02j], + ], + [ + [-0.00624197 + 9.56242750e-02j, 0.05597481 + 3.58601729e-02j], + [0.05597177 + 3.58591269e-02j, -0.00625072 + 9.56272885e-02j], + ], + [ + [-0.00629019 + 8.07437929e-02j, -0.207695 - 1.15495857e-01j], + [-0.20769134 - 1.15493114e-01j, -0.00629115 + 8.07503029e-02j], + ], + [ + [-0.01866223 + 8.07671147e-02j, 0.11861619 + 5.77120091e-02j], + [0.1186174 + 5.77111893e-02j, -0.01867434 + 8.07572294e-02j], + ], + [ + [-0.02247825 + 6.91239900e-02j, 0.0773675 + 3.39106726e-02j], + [0.07735209 + 3.39030769e-02j, -0.02246628 + 6.91193666e-02j], + ], + [ + [-0.02842607 + 4.72993472e-02j, -0.1540875 - 5.59675806e-02j], + [-0.1540874 - 5.59674645e-02j, -0.02842628 + 4.72996248e-02j], + ], + [ + [-0.01851712 + 3.77863880e-02j, 0.05764579 + 1.60702496e-02j], + [0.05764423 + 1.60696991e-02j, -0.01850981 + 3.77961805e-02j], + ], + [ + [-0.01205671 + 2.81280421e-02j, 0.07236243 + 1.50855388e-02j], + [0.07235358 + 1.50841149e-02j, -0.01207137 + 2.81271771e-02j], + ], + [ + [-0.00764806 + 3.01557374e-02j, -0.09612991 - 9.91224908e-03j], + [-0.09613214 - 9.91255099e-03j, -0.007644 + 3.01516657e-02j], + ], + [ + [-0.00353516 + 3.21564321e-02j, 0.01874719 - 4.03168882e-04j], + [0.01874735 - 4.02852811e-04j, -0.0035357 + 3.21514891e-02j], + ], + [ + [-0.01055669 + 3.76191893e-02j, 0.0524211 - 6.84844322e-03j], + [0.05242107 - 6.84869119e-03j, -0.01054941 + 3.76244000e-02j], + ], + ] +) -mzi_monte_carlo_s_parameters = np.load(os.path.join(os.path.dirname(__file__), "mzi_monte_carlo_sparameters.npy")) +mzi_monte_carlo_s_parameters = np.array( + [ + [ + [-6.07589269e-02 - 0.0467203j, -1.38574076e-02 + 0.04453427j], + [-1.38569604e-02 + 0.04453289j, -6.07509431e-02 - 0.04671424j], + ], + [ + [-5.05699095e-02 - 0.05313263j, 1.12835972e-02 - 0.09019379j], + [1.12837592e-02 - 0.09019399j, -5.05735696e-02 - 0.05313164j], + ], + [ + [-5.40481102e-02 - 0.06541386j, 5.39459589e-04 + 0.03532914j], + [5.37899126e-04 + 0.03532982j, -5.40517169e-02 - 0.06540547j], + ], + [ + [-3.48294783e-02 - 0.05941238j, 1.28605636e-02 + 0.08508553j], + [1.28618179e-02 + 0.08508543j, -3.48369357e-02 - 0.05942694j], + ], + [ + [-1.57249297e-02 - 0.05898637j, -3.72897443e-02 - 0.13128231j], + [-3.72892257e-02 - 0.13128203j, -1.57161893e-02 - 0.05898416j], + ], + [ + [-1.80944830e-02 - 0.03088278j, 1.40483750e-02 + 0.02991526j], + [1.40476601e-02 + 0.02991371j, -1.80893316e-02 - 0.03088749j], + ], + [ + [-2.48966475e-03 - 0.0428306j, 6.81551913e-02 + 0.12551407j], + [6.81552831e-02 + 0.12551665j, -2.48760751e-03 - 0.04281408j], + ], + [ + [-2.49501371e-04 - 0.05194382j, -1.07979989e-01 - 0.16243899j], + [-1.07980914e-01 - 0.16243751j, -2.60427348e-04 - 0.05194827j], + ], + [ + [3.58021382e-02 - 0.03599589j, 1.20982022e-02 + 0.01459319j], + [1.21027178e-02 + 0.01459054j, 3.58003126e-02 - 0.0359941j], + ], + [ + [1.34221802e-02 - 0.06615207j, 1.53893738e-01 + 0.17194722j], + [1.53889358e-01 + 0.17194707j, 1.34166721e-02 - 0.06616632j], + ], + [ + [2.21208829e-02 - 0.07476621j, -1.91770840e-01 - 0.18454077j], + [-1.91774110e-01 - 0.18454107j, 2.21381859e-02 - 0.0747656j], + ], + [ + [7.26455880e-02 - 0.11301202j, -1.04341995e-02 - 0.01088175j], + [-1.04303957e-02 - 0.01088017j, 7.26442593e-02 - 0.11301021j], + ], + [ + [4.02452952e-02 - 0.06682351j, 2.70445234e-01 + 0.20520648j], + [2.70443516e-01 + 0.20520402j, 4.02562164e-02 - 0.06679987j], + ], + [ + [4.60563959e-02 - 0.07236583j, -2.70839543e-01 - 0.18028615j], + [-2.70839917e-01 - 0.18029064j, 4.60158477e-02 - 0.07237595j], + ], + [ + [1.62830905e-02 - 0.10873216j, -6.03174179e-02 - 0.03693761j], + [-6.03145093e-02 - 0.0369288j, 1.62970025e-02 - 0.108742j], + ], + [ + [4.24066549e-02 - 0.02888373j, 3.82581048e-01 + 0.21968936j], + [3.82582648e-01 + 0.21968784j, 4.24107639e-02 - 0.02892028j], + ], + [ + [-2.76427928e-03 - 0.04131959j, -3.06619192e-01 - 0.16118533j], + [-3.06613886e-01 - 0.16118479j, -2.72011462e-03 - 0.04126886j], + ], + [ + [-5.85162004e-02 - 0.0367057j, -1.25021271e-01 - 0.06662437j], + [-1.25028866e-01 - 0.06662837j, -5.85547287e-02 - 0.03670815j], + ], + [ + [3.01815211e-02 - 0.05139664j, 4.75869462e-01 + 0.20853137j], + [4.75870059e-01 + 0.20853266j, 3.01616173e-02 - 0.05137663j], + ], + [ + [-8.65661973e-03 - 0.02859754j, -3.20260750e-01 - 0.1299913j], + [-3.20260105e-01 - 0.12998558j, -8.65371686e-03 - 0.02866558j], + ], + [ + [7.09514777e-03 - 0.00272924j, -2.11577328e-01 - 0.07673375j], + [-2.11574631e-01 - 0.07674152j, 7.12500797e-03 - 0.00269595j], + ], + [ + [5.50606166e-02 - 0.09061273j, 5.30227763e-01 + 0.15811184j], + [5.30227484e-01 + 0.15811225j, 5.50675623e-02 - 0.09061279j], + ], + [ + [8.89741528e-02 - 0.01138027j, -2.91662689e-01 - 0.06336738j], + [-2.91668271e-01 - 0.06336495j, 8.89583201e-02 - 0.01135925j], + ], + [ + [1.22421991e-01 - 0.00866062j, -2.72520725e-01 - 0.04880243j], + [-2.72514069e-01 - 0.04880103j, 1.22407107e-01 - 0.00867307j], + ], + [ + [6.81993820e-02 - 0.03748277j, 5.45350806e-01 + 0.07741174j], + [5.45351291e-01 + 0.07741201j, 6.82015610e-02 - 0.0374845j], + ], + [ + [1.47323036e-01 - 0.01573933j, -2.25532354e-01 - 0.01884177j], + [-2.25539568e-01 - 0.01884658j, 1.47314525e-01 - 0.01577197j], + ], + [ + [1.23449065e-01 - 0.01357181j, -3.18785265e-01 - 0.01470777j], + [-3.18783538e-01 - 0.014703j, 1.23494356e-01 - 0.01354622j], + ], + [ + [7.99026000e-02 + 0.03416059j, 5.14748717e-01 - 0.01684406j], + [5.14748317e-01 - 0.01684309j, 7.98838463e-02 + 0.03416665j], + ], + [ + [9.64222920e-02 - 0.0135964j, -1.62397588e-01 + 0.01498158j], + [-1.62389454e-01 + 0.0149777j, 9.64069722e-02 - 0.01356115j], + ], + [ + [7.26620494e-02 + 0.02677752j, -3.41924285e-01 + 0.05163894j], + [-3.41929642e-01 + 0.05164056j, 7.26374157e-02 + 0.02672232j], + ], + [ + [7.55047348e-02 + 0.06288612j, 4.49597933e-01 - 0.09957952j], + [4.49597460e-01 - 0.09958005j, 7.55336911e-02 + 0.06289496j], + ], + [ + [2.78175408e-02 + 0.03630503j, -8.91021024e-02 + 0.02894628j], + [-8.91014732e-02 + 0.02894974j, 2.78340933e-02 + 0.03629231j], + ], + [ + [4.94189692e-02 + 0.06301021j, -3.23408516e-01 + 0.11574237j], + [-3.23409700e-01 + 0.11574025j, 4.94189832e-02 + 0.06305781j], + ], + [ + [5.92461919e-02 + 0.06281122j, 3.61991160e-01 - 0.15496582j], + [3.61991936e-01 - 0.15496749j, 5.92207531e-02 + 0.06279186j], + ], + [ + [2.45885210e-02 + 0.08569097j, -3.04126118e-02 + 0.01676868j], + [-3.04124465e-02 + 0.01677189j, 2.45819812e-02 + 0.08569325j], + ], + [ + [5.13207382e-02 + 0.06578898j, -2.84136816e-01 + 0.16181095j], + [-2.84136930e-01 + 0.16181076j, 5.13309547e-02 + 0.06575893j], + ], + [ + [4.76018727e-02 + 0.06619713j, 2.67314175e-01 - 0.17596445j], + [2.67313731e-01 - 0.17596406j, 4.76176967e-02 + 0.06621768j], + ], + [ + [5.32442857e-02 + 0.09267201j, 9.38184585e-03 - 0.00517395j], + [9.37516966e-03 - 0.00517471j, 5.32425427e-02 + 0.09267186j], + ], + [ + [3.32660940e-02 + 0.07057563j, -2.21862775e-01 + 0.18414457j], + [-2.21873750e-01 + 0.18415365j, 3.32547079e-02 + 0.07059229j], + ], + [ + [2.74427275e-02 + 0.07627594j, 1.74354662e-01 - 0.16280845j], + [1.74366821e-01 - 0.16281416j, 2.74357282e-02 + 0.07625692j], + ], + [ + [2.63654569e-02 + 0.07366382j, 2.79163904e-02 - 0.02746451j], + [2.79146627e-02 - 0.02746171j, 2.63710311e-02 + 0.0736645j], + ], + [ + [-6.48561105e-03 + 0.07527187j, -1.50393944e-01 + 0.172274j], + [-1.50388190e-01 + 0.17226903j, -6.47990210e-03 + 0.07526305j], + ], + [ + [-1.66495125e-02 + 0.06287576j, 9.63051753e-02 - 0.12534871j], + [9.63046928e-02 - 0.12534987j, -1.66418759e-02 + 0.06289003j], + ], + [ + [-2.70092155e-02 + 0.04655459j, 2.80920965e-02 - 0.0399397j], + [2.80810101e-02 - 0.03992486j, -2.70172318e-02 + 0.04655452j], + ], + [ + [-3.05495618e-02 + 0.04498937j, -8.00309527e-02 + 0.13835036j], + [-8.00328074e-02 + 0.13835293j, -3.05534366e-02 + 0.04499525j], + ], + [ + [-3.00866625e-02 + 0.03067501j, 3.90246471e-02 - 0.08272028j], + [3.90239784e-02 - 0.08271908j, -3.00914134e-02 + 0.03066021j], + ], + [ + [-2.37066917e-02 + 0.02603948j, 1.53715131e-02 - 0.0407445j], + [1.53682283e-02 - 0.04073438j, -2.36971514e-02 + 0.02604317j], + ], + [ + [-9.13836418e-03 + 0.02988869j, -2.37714432e-02 + 0.09558664j], + [-2.37711919e-02 + 0.09558555j, -9.13589622e-03 + 0.02988716j], + ], + [ + [-9.74932278e-03 + 0.03794185j, 5.18341927e-03 - 0.04592224j], + [5.18370342e-03 - 0.04592233j, -9.75101006e-03 + 0.03795213j], + ], + [ + [-1.27103158e-02 + 0.04217811j, -3.69696036e-04 - 0.0318777j], + [-3.70046188e-04 - 0.0318777j, -1.27152475e-02 + 0.04217233j], + ], + ] +) @pytest.fixture(scope="module") diff --git a/simphony/tests/test_models.py b/simphony/tests/test_models.py index a4403f26..01cdd5b8 100644 --- a/simphony/tests/test_models.py +++ b/simphony/tests/test_models.py @@ -122,10 +122,3 @@ def __init__(self, **kwargs): assert [pin.name for pin in brancher.pins] == ["pin3"] assert brancher["pin3"]._connection == wg3["pin1"] - - -class TestModelComponent: - def testcomponent(self, y1): - y2 = siepic.YBranch(name="y2") - - assert y1.component != y2.component diff --git a/simphony/tests/test_simulation.py b/simphony/tests/test_simulation.py index 5b4e907b..564ccc9f 100644 --- a/simphony/tests/test_simulation.py +++ b/simphony/tests/test_simulation.py @@ -2,7 +2,6 @@ # Licensed under the terms of the MIT License # (see simphony/__init__.py for details) -import gdsfactory as gf import numpy as np import pytest @@ -26,73 +25,6 @@ def mzi(): return (gc_input, gc_output) -@pytest.fixture -def mzi_gf(): - - gc_input = siepic.GratingCoupler(name="gc_input") - y_splitter = siepic.YBranch(name="y_splitter") - wg_long = siepic.Waveguide(name="wg_long", length=150e-6) - wg_short = siepic.Waveguide(name="wg_short", length=50e-6) - y_recombiner = siepic.YBranch(name="y_recombiner") - gc_output = siepic.GratingCoupler(name="gc_output") - - c = gf.Component("mzi") - - ysplit = c << y_splitter.component - - gcin = c << gc_input.component - - gcout = c << gc_output.component - - yrecomb = c << y_recombiner.component - - yrecomb.move(destination=(0, -55.5)) - gcout.move(destination=(-20.4, -55.5)) - gcin.move(destination=(-20.4, 0)) - - gc_input["pin2"].connect(y_splitter, gcin, ysplit) - gc_output["pin2"].connect(y_recombiner["pin1"], gcout, yrecomb) - y_splitter["pin2"].connect(wg_long) - y_recombiner["pin3"].connect(wg_long) - y_splitter["pin3"].connect(wg_short) - y_recombiner["pin2"].connect(wg_short) - - wg_long_ref = gf.routing.get_route_from_steps( - ysplit.ports["pin2"], - yrecomb.ports["pin3"], - steps=[{"dx": 91.75 / 2}, {"dy": -61}], - ) - wg_short_ref = gf.routing.get_route_from_steps( - ysplit.ports["pin3"], - yrecomb.ports["pin2"], - steps=[{"dx": 47.25 / 2}, {"dy": -50}], - ) - - wg_long.path = wg_long_ref - print(wg_long.path) - wg_short.path = wg_short_ref - - c.add(wg_long_ref.references) - c.add(wg_short_ref.references) - - c.add_port("o1", port=gcin.ports["pin2"]) - c.add_port("o2", port=gcout.ports["pin2"]) - - return (c, gc_input, gc_output) - - -@pytest.fixture -def mzi_unconnected(): - gc_input = siepic.GratingCoupler() - y_splitter = siepic.YBranch() - wg_long = siepic.Waveguide(length=150e-6) - wg_short = siepic.Waveguide(length=50e-6) - y_recombiner = siepic.YBranch() - gc_output = siepic.GratingCoupler() - - return (gc_input, y_splitter, wg_long, wg_short, y_recombiner, gc_output) - - @pytest.fixture def oh(): x1 = siepic.GratingCoupler(name="x1") @@ -137,107 +69,107 @@ def oh(): class TestSimulation: seed117 = [ - 0.1754380152914969, - 0.17544695442565544, - 0.17544287567595707, - 0.17544503090527674, - 0.17543844500459105, - 0.17544186707373638, - 0.17544228589377417, - 0.1754404004359048, - 0.17543849716481452, - 0.17543692274591904, - 0.17543927226317219, - 0.17543999507279218, - 0.1754353380751127, - 0.17544193128079277, - 0.17543568794589912, - 0.17543832658642994, - 0.17543009244831398, - 0.17544583253229848, - 0.17544363014090936, - 0.17544256643083592, - 0.17544512241030039, - 0.1754456227379844, - 0.17543984487221026, - 0.17544758726885815, - 0.17543322936671696, - 0.17544361540240627, - 0.17544239803184677, - 0.1754448437948928, - 0.17544114541753658, - 0.17544239136728484, - 0.17543947616222758, - 0.17544552661963644, - 0.17544637220513903, - 0.17544162600891977, - 0.1754367026993376, - 0.1754471000129131, - 0.17543902094573183, - 0.17543615213308428, - 0.17544967047420865, - 0.17543146412086139, - 0.17544296807772886, - 0.17543449876934214, - 0.17544351274836711, - 0.17544819614655285, - 0.17543593118939826, - 0.1754422038724275, - 0.17544157410536837, - 0.1754397534953444, - 0.1754476727504654, - 0.17544280557328554, - 0.1754403282834214, - 0.17544255758794788, - 0.17543850331638874, - 0.175449876038959, - 0.17544753049531048, - 0.1754442724674327, - 0.17543741410327007, - 0.1754416430539068, - 0.17544524544178575, - 0.17543818035838332, - 0.17544267011085032, - 0.17543843013793015, - 0.17544483226104743, - 0.17545063486127643, - 0.17544132150635, - 0.17544385967116777, - 0.17544266639427417, - 0.1754417823614324, - 0.17544548022687032, - 0.17543783484496195, - 0.17544516534316243, - 0.17543704705898236, - 0.17543960111607987, - 0.1754430161365464, - 0.1754409539497875, - 0.1754371891859795, - 0.17543988729244112, - 0.17544361142951453, - 0.17544093933979865, - 0.1754443515407936, - 0.17544592826652938, - 0.17544733736115362, - 0.17544438165823237, - 0.1754446377171548, - 0.17543386349149767, - 0.17542962249331512, - 0.1754448082955394, - 0.17543616751201996, - 0.17543694978757066, - 0.1754513865067572, - 0.17544069673708843, - 0.17544706810162158, - 0.17543943322936553, - 0.17545438232342542, - 0.17544124909719455, - 0.1754407823461406, - 0.175441107867302, - 0.1754490941482381, - 0.17544355427149344, - 0.17544306970650564, - 0.17544362552722872, + 0.00017481, + 0.01219353, + -0.01773873, + 0.02061959, + -0.00290609, + -0.0066712, + 0.00846216, + 0.00488167, + -0.01002604, + 0.00672506, + -0.01299871, + 0.0126199, + 0.0007396, + 0.00115915, + -0.00602, + 0.00979, + -0.00520642, + -0.01741927, + -0.0240019, + 0.03115938, + -0.00537727, + -0.00066326, + -0.00495342, + 0.0002517, + -0.01819794, + -0.00936641, + 0.00736962, + -0.01756158, + 0.01517604, + 0.00298318, + 0.00553522, + -0.00281899, + 0.01784163, + 0.00610215, + -0.00944377, + -0.00967335, + 0.03266932, + -0.00754913, + -0.00785714, + 0.03044863, + -0.00879942, + 0.02543895, + -0.00322589, + -0.00785712, + 0.00815186, + -0.01540587, + 0.00631346, + 0.01470638, + -0.0051735, + 0.00150219, + 0.01991704, + -0.00193712, + 0.01432663, + 0.00699449, + 0.00281496, + -0.0075551, + 0.00341335, + 0.01141054, + -0.00696104, + 0.00628623, + -0.00156238, + 0.00271096, + -0.00631849, + 0.00724422, + 0.00808875, + 0.00742942, + -0.02009245, + 0.0071186, + -0.00098557, + -0.01329963, + -0.00692713, + 0.01484593, + 0.01073398, + 0.01623651, + -0.00623136, + -0.01092318, + -0.00766223, + -0.00344117, + 0.01897063, + 0.01066724, + -0.00842774, + -0.01002413, + 0.01600654, + -0.00786538, + 0.01610357, + 0.01215284, + 0.0039726, + 0.0194278, + -0.00150813, + -0.00359058, + -0.00125099, + 0.01863215, + -0.01533298, + -0.00367189, + 0.005698, + -0.00949113, + 0.00948224, + -0.00325547, + 0.01019897, + 0.00419238, + -0.00354101, ] def test_context(self, mzi): @@ -252,10 +184,10 @@ def test_context(self, mzi): assert l1.circuit == gc_input.circuit == sim1.circuit assert d1.circuit == gc_output.circuit == sim1.circuit - # with Simulation() as _: - assert sim1.circuit is None - assert l1.circuit != gc_input.circuit - assert d1.circuit != gc_output.circuit + with Simulation() as _: + assert sim1.circuit is None + assert l1.circuit != gc_input.circuit + assert d1.circuit != gc_output.circuit def test_sampling(self, mzi): gc_input, gc_output = mzi @@ -321,47 +253,33 @@ def test_sampling_frequency(self, mzi): assert np.allclose(data1[0][0], data2[0][0], rtol=0, atol=1e-11) - def test_layout_aware(self, mzi_gf): - c, gc_input, gc_output = mzi_gf - - with Simulation(fs=10e9, seed=117) as sim: - Laser(power=1e-3, wl=1550e-9).connect(gc_input) - Detector().connect(gc_output) - - data = sim.layout_aware_simulation( - component_or_circuit=c, runs=2, num_samples=101 - ) - - assert len(data) == 2 - assert len(data[0][0][0]) == 101 - class TestSingleDetector: - result = 0.17544204 + result = 0.00017544 results = [ - 0.1754446446318916, - 0.1740570477966146, - 0.17472088224910245, - 0.17600042687369985, - 0.17409707193348012, - 0.17656878808228374, - 0.17564879343100195, - 0.17584860763997665, - 0.17521402003433048, - 0.1767711660212849, - 0.17588534947132822, - 0.17472234709003542, - 0.17469337624014103, - 0.177897858739015, - 0.17485959291174613, - 0.17483231815278333, - 0.17772861062695466, - 0.1747695127456315, - 0.1773630911610061, - 0.1751848508997118, - 0.17483786830788275, - 0.17605227005695107, - 0.17426008721781772, + 1.80576404e-04, + 1.08063217e-02, + -1.84591717e-02, + 2.11631266e-02, + -4.24527434e-03, + -5.53885990e-03, + 8.67396297e-03, + 5.28644276e-03, + -1.02520694e-02, + 8.05882087e-03, + -1.25512983e-02, + 1.18939574e-02, + -3.92095769e-06, + 3.61245566e-03, + -6.60295137e-03, + 9.18355753e-03, + -2.92043587e-03, + -1.80968121e-02, + -2.20941667e-02, + 3.09025569e-02, + -5.98374595e-03, + -6.09039074e-05, + -6.12987780e-03, ] def test_single_sample(self, mzi): @@ -372,6 +290,7 @@ def test_single_sample(self, mzi): Detector().connect(gc_output) data = sim.sample() + assert np.allclose(data[0][0], [self.result], rtol=0, atol=1e-8) def test_conversion_gain(self, mzi): @@ -399,212 +318,212 @@ def test_noise(self, mzi): class TestDifferentialDetector: cmrr_x = [ - -0.0006649530079477117, - 0.001862986930585692, - -0.004127633806731973, - -0.002114743819325535, - 0.0034426013129120617, - -0.0031508670468650933, - 0.0017569831950194803, - -0.00045637284640092724, - -0.0017272818749597511, - -0.004850761198680061, - -0.0015435978785956952, - 0.0008714199831552534, - -0.004669033696690775, - -0.0006717397085453862, - -0.0030448708403816773, - -0.001751682348939884, - -0.004483208665495104, - -0.0003865227882897124, - -0.0001270245108348886, - -0.0003380476733973268, - -0.008645386160323062, - -0.00042084948912773923, - -0.0028703445512053907, + 0.00185109, + 0.00438826, + -0.00162186, + 0.0004025, + 0.00595868, + -0.00064218, + 0.00427251, + 0.00205762, + 0.00079248, + -0.00233544, + 0.00097263, + 0.00339304, + -0.00215764, + 0.00184303, + -0.00052858, + 0.00076543, + -0.001965, + 0.0021287, + 0.00238189, + 0.00219444, + -0.00612456, + 0.00209419, + -0.00035425, ] cmrr_p = [ - -0.0069360569388826985, - 0.007162650436662034, - 0.004092972479890926, - 0.0015257977103456232, - -0.003595451576560762, - -0.008574945982064151, - -0.005975918317579784, - -0.0007794603488169448, - -0.003243277451558061, - 0.0046364044604873655, - -0.007765984954843569, - 0.0012219494023251371, - -0.0058133270530027845, - -0.006623138756001309, - -0.0061581015123797645, - -0.007722807298276565, - 0.0035515242719080064, - -0.004062776678878426, - -0.006549156884781828, - -0.0016477338915286788, - -0.0004981163877181849, - -0.001881633727400377, - -0.0019333666109683496, + -0.00470416, + 0.00939843, + 0.00632678, + 0.00376477, + -0.00135284, + -0.00634382, + -0.00374078, + 0.00145949, + -0.0010054, + 0.00687253, + -0.00553449, + 0.00346154, + -0.00358327, + -0.00438276, + -0.0039282, + -0.00549966, + 0.00577782, + -0.00183013, + -0.00431677, + 0.00059047, + 0.00173069, + 0.00035287, + 0.00030604, ] result = [ - 0.06820933398426215, - -0.00251779027237109, - 0.07072712425663324, - 0.07528059784829455, - -0.002235358831987083, - 0.07751595668028163, + 6.820933398426216e-05, + -2.51779027237116e-06, + 7.072712425663332e-05, + 7.528059784829445e-05, + -2.2353588319872576e-06, + 7.751595668028171e-05, ] x1results = [ - 0.06821699257144385, - 0.0670952382875827, - 0.0676372896391082, - 0.06864269173955594, - 0.06714010175082658, - 0.06911780934124957, - 0.0683777068291349, - 0.06853477945343182, - 0.06802755771604438, - 0.06927209160924779, - 0.06856504613437195, - 0.06762827665802736, - 0.06761848293287445, - 0.07017466869076468, - 0.06774859678775505, - 0.06772307342540554, - 0.07003315633076466, - 0.06766455420703217, - 0.06973587278660405, - 0.06799847701751302, - 0.06771901819968199, - 0.06869352418909015, - 0.06726606144321046, + 7.25832807e-05, + 6.45213384e-03, + -1.16782238e-02, + 1.32502362e-02, + -2.92429196e-03, + -3.29487901e-03, + 5.40473883e-03, + 3.32631865e-03, + -6.47341674e-03, + 5.21927531e-03, + -7.78813016e-03, + 7.24665505e-03, + -1.74786835e-04, + 2.64408006e-03, + -4.26117438e-03, + 5.57803566e-03, + -1.45885813e-03, + -1.14445296e-02, + -1.34812942e-02, + 1.91818955e-02, + -3.87934796e-03, + 2.70018878e-05, + -4.07081299e-03, ] xresults = [ - -0.0006649530075707654, - 0.001862978025611869, - -0.004127620532783833, - -0.002114758967834034, - 0.0034426035961630276, - -0.0031508619736904902, - 0.0017569770547843028, - -0.00045637633363159943, - -0.0017272743160151303, - -0.004850766051893397, - -0.0015435881169640177, - 0.000871410762092511, - -0.0046690341148760235, - -0.0006717404374501456, - -0.00304486624987099, - -0.001751689473161437, - -0.004483204677955724, - -0.00038650975098166373, - -0.0001270065960240251, - -0.0003380706316670535, - -0.008645382045841199, - -0.00042084886758710733, - -0.002870340750907864, + 0.00185101, + 0.00642166, + -0.0046529, + 0.00386158, + 0.00543731, + -0.00180061, + 0.0056746, + 0.00285391, + -0.00093357, + -0.00122723, + -0.00125639, + 0.00549862, + -0.00206215, + 0.00200947, + -0.0015768, + 0.00239221, + -0.00287553, + -0.0008483, + -0.00170886, + 0.00743684, + -0.00706408, + 0.00195226, + -0.00122203, ] x2results = [ - 0.07073557241476708, - 0.06962048630932399, - 0.07014952579962609, - 0.07115810916114472, - 0.06965928854947752, - 0.07163069372397597, - 0.07089428677003226, - 0.07105036822199548, - 0.07055188889683411, - 0.07178828771761253, - 0.07108649658419547, - 0.07014976030079044, - 0.07013233734189801, - 0.07269184520627828, - 0.07026865262442902, - 0.07024049933599255, - 0.07255522821774693, - 0.07018568554401064, - 0.07225263048236619, - 0.07052716312848073, - 0.07024368425679589, - 0.07121114361795099, - 0.06978575600305406, + 7.51125467e-05, + 6.59171046e-03, + -1.18798537e-02, + 1.34858866e-02, + -2.95698094e-03, + -3.37021160e-03, + 5.50204769e-03, + 3.38258002e-03, + -6.58734386e-03, + 5.29622841e-03, + -7.93593533e-03, + 7.39107533e-03, + -1.65775053e-04, + 2.65791212e-03, + -4.32937195e-03, + 5.69003709e-03, + -1.51750747e-03, + -1.16429994e-02, + -1.37543005e-02, + 1.95380050e-02, + -3.94001069e-03, + 1.98397377e-05, + -4.12678903e-03, ] p1results = [ - 0.07507507044765914, - 0.07510679224036242, - 0.07605473709785131, - 0.0758074532418194, - 0.07534979484947107, - 0.07401383966670384, - 0.07477621118497266, - 0.07537404353752364, - 0.0750368698817334, - 0.0754033528579343, - 0.0749498710706332, - 0.07434496003925792, - 0.07728657396969658, - 0.07399235689447041, - 0.07569985687109715, - 0.07588374315571056, - 0.07438786494439528, - 0.07452169107365568, - 0.07563111880182889, - 0.0754454167542434, - 0.07586818593121604, - 0.07558788502434018, - 0.07537426412710296, + -0.00012833, + 0.00777554, + -0.01088095, + 0.01399814, + -0.00186632, + -0.00567741, + 0.00499696, + 0.00325294, + -0.00684533, + 0.00448832, + -0.0088845, + 0.00729312, + 0.00244817, + -0.00056501, + -0.00356172, + 0.00697164, + -0.00434339, + -0.01221006, + -0.0154076, + 0.02053649, + -0.00297821, + -0.00016577, + -0.00318841, ] presults = [ - -0.006936056938517612, - 0.007162641811828369, - 0.004092985336256041, - 0.0015257830383916472, - -0.0035954493651382275, - -0.00857494106848574, - -0.005975924264650281, - -0.0007794637263433804, - -0.0032432701304090806, - 0.004636399759950497, - -0.0077659755003015125, - 0.0012219404713463438, - -0.005813327458032411, - -0.006623139461975567, - -0.006158097066281225, - -0.007722814198378362, - 0.003551528134004016, - -0.004062764051708979, - -0.006549139533550153, - -0.0016477561275578447, - -0.0004981124026731531, - -0.0018816331254126921, - -0.0019333629302237811, + -0.00470424, + 0.01131443, + 0.00347076, + 0.00702412, + -0.00184411, + -0.00743536, + -0.00241965, + 0.0022098, + -0.00263178, + 0.00791674, + -0.0076348, + 0.00544554, + -0.00349329, + -0.00422592, + -0.0049159, + -0.00396682, + 0.00491986, + -0.00463523, + -0.00817131, + 0.00553016, + 0.00084541, + 0.00021914, + -0.00051163, ] p2results = [ - 0.07730934862206633, - 0.07734260484804065, - 0.07829441379130923, - 0.07804450462276571, - 0.07759550072649996, - 0.07624850679999039, - 0.07701193228801893, - 0.07761452762506892, - 0.0772790473182888, - 0.07764034092892877, - 0.0771860494407, - 0.07658431582555629, - 0.07951881968959741, - 0.07623457534148566, - 0.0779332797203656, - 0.07810707383472705, - 0.07661760312883541, - 0.07676017718114204, - 0.0778705881359709, - 0.07767956087555943, - 0.07810036385564213, - 0.0778247090533542, - 0.0776171483539101, + -0.00012596, + 0.00789399, + -0.01105161, + 0.01419768, + -0.00189359, + -0.00574133, + 0.0050792, + 0.00330085, + -0.00694156, + 0.00455373, + -0.00900964, + 0.00741552, + 0.00245592, + -0.00055348, + -0.00361925, + 0.00706657, + -0.00439297, + -0.0123776, + -0.01563877, + 0.02083776, + -0.00302955, + -0.00017171, + -0.00323547, ] def test_single_sample(self, oh): @@ -1303,29 +1222,29 @@ class TestLaser: ] rin_results = [ - 0.175438900210304, - 0.1754444250920227, - 0.17544089750284503, - 0.17545771248659853, - 0.17543611656310257, - 0.17543613344239561, - 0.175436990691907, - 0.17544404948283715, - 0.17543988702881463, - 0.17543755870755723, - 0.1754376205860491, - 0.1754488221489465, - 0.1754369607099373, - 0.175444662035645, - 0.17544263609826882, - 0.17543897867673636, - 0.1754429191962544, - 0.17544672691250823, - 0.17545523227288715, - 0.17544229604828684, - 0.17544420564473528, - 0.17545003776460372, - 0.17543633153874252, + 0.00017534, + 0.00019062, + 0.00015289, + 0.00020163, + 0.00017138, + 0.00016665, + 0.0001857, + 0.00018142, + 0.00016255, + 0.00018353, + 0.00015875, + 0.0001913, + 0.00017599, + 0.00017676, + 0.00016767, + 0.00018743, + 0.00016871, + 0.00015348, + 0.00014547, + 0.00021439, + 0.00016853, + 0.00017464, + 0.00016882, ] def test_wlsweep(self, mzi): diff --git a/simphony/tools.py b/simphony/tools.py index 05cff23e..c2217598 100644 --- a/simphony/tools.py +++ b/simphony/tools.py @@ -131,7 +131,7 @@ def str2float(num): r"([-+]?[0-9]+(?:[.][0-9]+)?)((?:[eE][-+]?[0-9]+)|(?:[a-zA-Z]))?", num ) if len(matches) > 1: - raise ValueError("'{}' is malformed".format(num)) + raise ValueError(f"'{num}' is malformed") num, suffix = matches[0] try: if suffix.startswith("e") or suffix.startswith("E"): @@ -139,7 +139,7 @@ def str2float(num): else: return float(num + (MATH_SUFFIXES[suffix] if suffix != "" else "")) except KeyError as e: - raise ValueError("Suffix {} in '{}' not recognized.".format(str(e), matches[0])) + raise ValueError(f"Suffix {str(e)} in '{matches[0]}' not recognized.") def freq2wl(freq): diff --git a/tox.ini b/tox.ini index 4b9a48e6..be8f9450 100644 --- a/tox.ini +++ b/tox.ini @@ -3,7 +3,6 @@ envlist = py37, py38, py39, py310 [testenv] deps = - gdsfactory numpy pytest scipy