def __init__(self, start: float = 1.5e-6, stop: float = 1.6e-6, num: int = 2000) -> None: """Initializes the SweepSimulator instance. The start and stop values can be given in either wavelength or frequency. The simulation will output results in the same mode. Parameters ---------- start : The starting frequency/wavelength. stop : The stopping frequency/wavelength. num : The number of points between start and stop. """ super().__init__() # automatically detect mode self.mode = "wl" if start < 1 else "freq" # if mode is wavelength, convert to frequencies if self.mode == "wl": temp_start = start start = wl2freq(stop) stop = wl2freq(temp_start) if start > stop: raise ValueError( "Starting frequency cannot be greater than stopping frequency." ) self.freqs = np.linspace(start, stop, num)
def __init__( self, circuit: Subcircuit, start: float = 1.5e-6, stop: float = 1.6e-6, num: int = 2000, mode="wl", ): super().__init__(circuit) if start > stop: raise ValueError("simulation 'start' value must be less than 'stop' value.") if mode == "wl": tmp_start = start tmp_stop = stop start = wl2freq(tmp_stop) stop = wl2freq(tmp_start) elif mode == "freq": pass else: err = "mode '{}' is not one of 'freq' or 'wl'".format(mode) raise ValueError(err) if start > stop: raise ValueError( "starting frequency cannot be greater than stopping frequency" ) self.freq = np.linspace(start, stop, num) self.wl = freq2wl(self.freq)
def test_ebeam_wg_integral_1550(tmpdir): """Tests whether the results of `s_parameters()` for the same frequency on an exported and reloaded model are (close) to the same.""" p = tmpdir.mkdir("persist").join("export") wg1 = ebeam_wg_integral_1550(100e-9) export_model(wg1, p, wl=np.linspace(1520e-9, 1580e-9, 51)) wg = import_model(p + ".mdl") wg2 = wg() freq = np.linspace(wl2freq(1540e-9), wl2freq(1560e-9), 50) assert np.allclose(wg1.s_parameters(freq), wg2.s_parameters(freq))
def export_model(model, filename, wl=None, freq=None): """Exports a simphony model (using pickle) for the given frequency/wavelength range to a '.mdl' file. Must include either the wavelength or frequency argument. If both are included, defaults to frequency argument. Parameters ----------- model : Model Any class inheriting from simphony.elements.Model filename : str The filename (may include path to directory) to save the model to. Note that the suffix '.mdl' will be appended to the filename. wl : ndarray, optional Wavelengths you want to save sparameters for (in meters). freq : ndarray, optional Frequencies you want to save sparameters for (in Hz). Examples -------- We can write a model for a ``ebeam_wg_integral_1550`` instantiated with a length of 100 nanometers to a file named ``wg100nm.mdl``. >>> import numpy as np >>> from simphony.library.ebeam import ebeam_wg_integral_1550 >>> wg1 = ebeam_wg_integral_1550(100e-9) >>> export_model(wg1, 'wg100nm', wl=np.linspace(1520e-9, 1580e-9, 51)) """ if not issubclass(model.__class__, Model): raise ValueError("{} does not extend {}".format(model, Model)) if wl is None and freq is None: raise ValueError("Frequency or wavelength range not defined.") # Convert wavelength to frequency if freq is None: freq = wl2freq(wl)[::-1] # Load all data into a dictionary. attributes = inspect.getmembers(model, lambda a: not (inspect.isroutine(a))) attributes = dict( [ a for a in attributes if not (a[0].startswith("__") and a[0].endswith("__")) and not a[0].startswith("_") ] ) params = dict() params["model"] = model.__class__.__name__ params["attributes"] = attributes params["f"] = freq params["s"] = model.s_parameters(freq) # Dump to pickle. pickle.dump( params, io.open(filename + ".mdl", "wb"), protocol=pickle.HIGHEST_PROTOCOL )
def export_interconnect(sparams, wavelength, filename, clear=True): """Exports scattering parameters to a file readable by interconnect. Parameters ----------- sparams : ndarray Numpy array of size (N, d, d) where N is the number of frequency points and d the number of ports wavelength : ndarray Numpy array of wavelengths (in nm, like the rest of SCEE) of size (N) filename : string Location to save file clear : bool, optional If True, empties the file first. Defaults to True. """ # set things up _, d, _ = sparams.shape if clear: open(filename, "w").close() file = open(filename, "ab") # make frequencies freq = wl2freq(wavelength * 1e-9) # iterate through sparams saving for in_ in range(d): for out in range(d): # put things together sp = sparams[:, in_, out] temp = np.vstack((freq, np.abs(sp), np.unwrap(np.angle(sp)))).T # Save header header = f'("port {out+1}", "TE", 1, "port {in_+1}", 1, "transmission")\n' header += f"{temp.shape}" # save data np.savetxt(file, temp, header=header, comments="") file.close()
# -*- coding: utf-8 -*- # # Copyright © Simphony Project Contributors # Licensed under the terms of the MIT License # (see simphony/__init__.py for details) import pytest import numpy as np from simphony.library import ebeam, siepic from simphony.tools import wl2freq f = np.linspace(wl2freq(1600e-9), wl2freq(1500e-9)) def is_equal(c1, c2): return np.array_equal(c1.s_parameters(f), c2.s_parameters(f)) class TestReimplementedComponents: def test_ebeam_y_1550(self): assert is_equal(siepic.ebeam_y_1550(), ebeam.ebeam_y_1550()) def test_ebeam_bdc_te1550(self): assert is_equal(siepic.ebeam_bdc_te1550(), ebeam.ebeam_bdc_te1550()) def test_ebeam_dc_halfring_straight(self): d1 = siepic.ebeam_dc_halfring_straight(gap=30e-9, radius=3e-6, width=520e-9, thickness=210e-9)
You can see that in the final network, our input port is port 3, our through port is port 2, and our drop port is port 0. To get the transmission from input to output in the s-matrix, the indexing is ``s[out, in]``. """ import matplotlib.pyplot as plt import numpy as np from simphony.connect import connect_s, innerconnect_s from simphony.library import ebeam, sipann from simphony.tools import freq2wl, wl2freq # First, we'll set up the frequency range we wish to perform the simulation on. freq = np.linspace(wl2freq(1600e-9), wl2freq(1500e-9), 2000) # Get the scattering parameters for each of the elements in our network. half_ring_left = sipann.sipann_dc_halfring(radius=10).s_parameters(freq) half_ring_right = sipann.sipann_dc_halfring(radius=10).s_parameters(freq) term = ebeam.ebeam_terminator_te1550().s_parameters(freq) ### CONFIGURATION 1 ### n1 = connect_s(half_ring_left, 1, half_ring_right, 3) n2 = innerconnect_s(n1, 2, 4) n3 = connect_s(n2, 1, term, 0) ### CONFIGURATION 2 ### m1 = connect_s(half_ring_right, 1, half_ring_left, 3) m2 = innerconnect_s(m1, 2, 4) m3 = connect_s(term, 0, m2, 3)
wg_in2.multiconnect(dc2, crossing) wg_out1.multiconnect(crossing, dc3) wg_out2.multiconnect(crossing, dc4) wg_pass2.multiconnect(dc2, dc4) # connect output grating couplers to directional couplers wg5.multiconnect(dc3, out1) wg6.multiconnect(dc3, out2) wg7.multiconnect(dc4, out3) wg8.multiconnect(dc4, out4) simulator = SweepSimulator(1549.9e-9, 1550.1e-9) # The Green Machine is optimized for 1550 nanometers. We'd like to investigate # its behavior at that specific frequency: set_freq = wl2freq(1550e-9) plt.figure() simulator.multiconnect(in1["pin2"], out1) plt.plot(*simulator.simulate(), label="1 to 5") simulator.multiconnect(in1["pin2"], out2) plt.plot(*simulator.simulate(), label="1 to 6") simulator.multiconnect(in1["pin2"], out3) plt.plot(*simulator.simulate(), label="1 to 7") simulator.multiconnect(in1["pin2"], out4) plt.plot(*simulator.simulate(), label="1 to 8")
def freqs(): return np.linspace(wl2freq(1600e-9), wl2freq(1500e-9))
def test_auto_mode_conversion(self, mzi, sweep_wl): _, gc_input, _, _, _, gc_output = mzi sweep_wl.multiconnect(gc_input, gc_output) wl, p = sweep_wl.simulate() assert np.allclose(wl2freq(wl), sweep_freqs)
def test_modes(self, mzi, sweep): _, gc_input, _, _, _, gc_output = mzi sweep.multiconnect(gc_input, gc_output) wl, p = sweep.simulate(mode="wl") assert np.allclose(wl2freq(wl), sweep_freqs)