def ring_factory(radius): """ Creates a full ring (with terminator) from a half ring. Ports of a half ring are ordered like so: 2 4 | | \ / \ / ---=====--- 1 3 Resulting pins are ('in', 'out', 'pass'). Parameters ---------- radius : float The radius of the ring resonator, in nanometers. """ # Have rings for selecting out frequencies from the data line. # See SiPANN's model API for argument order and units. half_ring = SimphonyWrapper(scee.HalfRing(500, 220, radius, 100)) circuit = Subcircuit() circuit.add([(half_ring, 'input'), (half_ring, 'output'), (term, 'terminator')]) circuit.elements['input'].pins = ('pass', 'midb', 'in', 'midt') circuit.elements['output'].pins = ('out', 'midt', 'term', 'midb') circuit.connect_many([('input', 'midb', 'output', 'midb'), ('input', 'midt', 'output', 'midt'), ('terminator', 'n1', 'output', 'term')]) return circuit
def add_gc(circuit, gc=gc1550te, cpi="input", cpo="output", gpi="port 1", gpo="port 2"): """ add input and output gratings Args: circuit: needs to have `input` and `output` pins gc: grating coupler cpi: circuit pin input name cpo: circuit pin output name gpi: grating pin input name gpo: grating pin output name .. code:: _______ | | gpi-> gpo--|cpi cpo|--gpo <-gpi |_______| """ gc = pp.call_if_func(gc) c = Subcircuit(f"{circuit.name}_{gc.name}") c.add([(gc, "gci"), (gc, "gco"), (circuit, "circuit")]) c.connect_many([("gci", gpo, "circuit", cpi), ("gco", gpo, "circuit", cpo)]) c.elements["gci"].pins[gpi] = "input" c.elements["gco"].pins[gpi] = "output" return c
def add_gc(circuit, gc=siepic.ebeam_gc_te1550): """ add input and output gratings Args: circuit: needs to have `input` and `output` pins gc: grating coupler """ c = Subcircuit(f"{circuit}_gc") gc = pp.call_if_func(gc) c.add([ (gc, "gci"), (gc, "gco"), (circuit, "circuit"), ]) c.connect_many([ ("gci", "n1", "circuit", "input"), ("gco", "n1", "circuit", "output"), ]) # c.elements["circuit"].pins["input"] = "input_circuit" # c.elements["circuit"].pins["output"] = "output_circuit" c.elements["gci"].pins["n2"] = "input" c.elements["gco"].pins["n2"] = "output" return c
def ring_double_siepic( wg_width=0.5, gap=0.2, length_x=4, bend_radius=5, length_y=2, coupler=siepic.ebeam_dc_halfring_straight, waveguide=siepic.ebeam_wg_integral_1550, ): """ double bus ring made of two couplers (ct: top, cb: bottom) connected with two vertical waveguides (wyl: left, wyr: right) .. code:: --==ct==-- | | wl wr length_y | | --==cb==-- gap length_x .. plot:: :include-source: import pp c = pp.c.ring(wg_width=0.5, gap=0.2, length_x=4, bend_radius=5, length_y=2) pp.plotgds(c) .. plot:: :include-source: import simphony.library.gdsfactory as cl c = cl.ring() cl.sweep_simulation(c) """ waveguide = pp.call_if_func(waveguide) coupler = pp.call_if_func(coupler) # Create the circuit, add all individual instances circuit = Subcircuit("mzi") circuit.add([(coupler, "ct"), (coupler, "cb"), (waveguide, "wl"), (waveguide, "wr")]) # Circuits can be connected using the elements' string names: circuit.connect_many([ ("cb", "2", "wl", "n1"), ("wl", "n2", "ct", "n4"), ("ct", "2", "wr", "n2"), ("wr", "n1", "cb", "n4"), ]) circuit.elements["cb"].pins["n4"] = "input" circuit.elements["cb"].pins["n3"] = "output" circuit.elements["ct"].pins["n3"] = "drop" circuit.elements["ct"].pins["n4"] = "cdrop" return circuit
def ring_double_siepic( wg_width=0.5, gap=0.2, length_x=4, bend_radius=5, length_y=2, coupler=siepic.ebeam_dc_halfring_straight, waveguide=siepic.ebeam_wg_integral_1550, terminator=siepic.ebeam_terminator_te1550, ): """ double bus ring made of two couplers (ct: top, cb: bottom) connected with two vertical waveguides (wyl: left, wyr: right) .. code:: --==ct==-- | | wl wr length_y | | --==cb==-- gap length_x drop n1 _ _ n3 cdrop \______/ ______ in n2 _/ \_n4 | | n1 | | n3 \______/ ______ in n2 _/ \_n4 output """ waveguide = pp.call_if_func(waveguide) coupler = pp.call_if_func(coupler) # Create the circuit, add all individual instances circuit = Subcircuit("mzi") circuit.add([(coupler, "ct"), (coupler, "cb"), (waveguide, "wl"), (waveguide, "wr")]) # Circuits can be connected using the elements' string names: circuit.connect_many([ ("cb", "n1", "wl", "n1"), ("wl", "n2", "ct", "n2"), ("ct", "n4", "wr", "n1"), ("wr", "n2", "cb", "n3"), ]) circuit.elements["cb"].pins["n2"] = "input" circuit.elements["cb"].pins["n4"] = "output" circuit.elements["ct"].pins["n1"] = "drop" circuit.elements["ct"].pins["n3"] = "cdrop" return circuit
def ring_double_sipann( wg_width=0.5, gap=0.2, length_x=4, bend_radius=5, length_y=2, coupler=siepic.ebeam_dc_halfring_straight, waveguide=siepic.ebeam_wg_integral_1550, terminator=ebeam.ebeam_terminator_te1550, ): """ double bus ring made of two couplers (ct: top, cb: bottom) connected with two vertical waveguides (wyl: left, wyr: right) .. code:: --==ct==-- | | wl wr length_y | | --==cb==-- gap length_x .. plot:: :include-source: import pp c = pp.c.ring(wg_width=0.5, gap=0.2, length_x=4, bend_radius=5, length_y=2) pp.plotgds(c) .. plot:: :include-source: import simphony.library.gdsfactory as cl c = cl.ring() cl.sweep_simulation(c) """ waveguide = pp.call_if_func(waveguide) half_ring = pp.call_if_func(coupler) term = pp.call_if_func(coupler) circuit = Subcircuit() circuit.add([(half_ring, "input"), (half_ring, "output"), (term, "terminator")]) circuit.elements["input"].pins = ("pass", "midb", "in", "midt") circuit.elements["output"].pins = ("out", "midt", "term", "midb") circuit.connect_many([ ("input", "midb", "output", "midb"), ("input", "midt", "output", "midt"), ("terminator", "n1", "output", "term"), ]) return circuit
def mzi_gc(L0=1, L1=100, L2=10, y_model_factory=mmi1x2): """ MZI with grating couplers Deprecated! use add_gc instead """ y = pp.call_if_func(y_model_factory) gc = siepic.ebeam_gc_te1550() wg_long = siepic.ebeam_wg_integral_1550(length=(2 * L0 + 2 * L1 + L2) * 1e-6) wg_short = siepic.ebeam_wg_integral_1550(length=(2 * L0 + L2) * 1e-6) # Create the circuit, add all individual instances circuit = Subcircuit("MZI") circuit.add([ (gc, "input"), (gc, "output"), (y, "splitter"), (y, "recombiner"), (wg_long, "wg_long"), (wg_short, "wg_short"), ]) # You can set pin names individually: circuit.elements["input"].pins["n2"] = "input" circuit.elements["output"].pins["n2"] = "output" # Or you can rename all the pins simultaneously: # circuit.elements["splitter"].pins = ("in1", "out1", "out2") # circuit.elements["recombiner"].pins = ("out1", "in2", "in1") # Circuits can be connected using the elements' string names: circuit.connect_many([ ("input", "n1", "splitter", "W0"), ("splitter", "E0", "wg_long", "n1"), ("splitter", "E1", "wg_short", "n1"), ("recombiner", "E0", "wg_long", "n2"), ("recombiner", "E1", "wg_short", "n2"), ("output", "n1", "recombiner", "W0"), ]) return circuit
def ring_factory(radius): """ Creates a full ring (with terminator) from a half ring. Ports of a half ring are ordered like so: 2 4 | | \ / \ / ---=====--- 1 3 Resulting pins are ('in', 'out', 'pass'). Parameters ---------- radius : float The radius of the ring resonator, in microns. """ # Have rings for selecting out frequencies from the data line. half_ring = sipann.sipann_dc_halfring(radius=radius) circuit = Subcircuit() circuit.add([ (half_ring, 'input'), (half_ring, 'output'), (term, 'terminator') ]) circuit.elements['input'].pins = ('pass', 'midb', 'in', 'midt') circuit.elements['output'].pins = ('out', 'midt', 'term', 'midb') circuit.connect_many([ ('input', 'midb', 'output', 'midb'), ('input', 'midt', 'output', 'midt'), ('terminator', 'n1', 'output', 'term') ]) return circuit
def result_monte(): # Declare the models used in the circuit gc = siepic.ebeam_gc_te1550() y = siepic.ebeam_y_1550() wg150 = siepic.ebeam_wg_integral_1550(length=150e-6) wg50 = siepic.ebeam_wg_integral_1550(length=50e-6) # Create the circuit, add all individual instances circuit = Subcircuit("MZI") e = circuit.add([ (gc, "input"), (gc, "output"), (y, "splitter"), (y, "recombiner"), (wg150, "wg_long"), (wg50, "wg_short"), ]) # You can set pin names individually: circuit.elements["input"].pins["n2"] = "input" circuit.elements["output"].pins["n2"] = "output" # Or you can rename all the pins simultaneously: circuit.elements["splitter"].pins = ("in1", "out1", "out2") circuit.elements["recombiner"].pins = ("out1", "in2", "in1") # Circuits can be connected using the elements' string names: circuit.connect_many([ ("input", "n1", "splitter", "in1"), ("splitter", "out1", "wg_long", "n1"), ("splitter", "out2", "wg_short", "n1"), ("recombiner", "in1", "wg_long", "n2"), ("recombiner", "in2", "wg_short", "n2"), ("output", "n1", "recombiner", "out1"), ]) sim = MonteCarloSweepSimulation(circuit) return sim.simulate()
plt.plot(f1, s) plt.title("10-micron Ring Resonator") plt.tight_layout() plt.show() # Now, we'll create the circuit (using several ring resonator subcircuits) # and add all individual instances. circuit = Subcircuit("Add-Drop Filter") e = circuit.add( [ (wg_data, "input"), (ring_factory(10000), "ring10"), (wg_data, "out1"), (wg_data, "connect1"), (ring_factory(11000), "ring11"), (wg_data, "out2"), (wg_data, "connect2"), (ring_factory(12000), "ring12"), (wg_data, "out3"), (term, "terminator"), ] ) # You can set pin names individually (here I'm naming all the outputs that # I'll want to access after the simulation has been run): circuit.elements["input"].pins["n1"] = "input" circuit.elements["out1"].pins["n2"] = "out1" circuit.elements["out2"].pins["n2"] = "out2" circuit.elements["out3"].pins["n2"] = "out3" circuit.connect_many(
def mzi( L0=1, L1=100, L2=10, y_model_factory=siepic.ebeam_y_1550, wg=siepic.ebeam_wg_integral_1550, ): """ Mzi circuit model Args: L0 (um): vertical length for both and top arms L1 (um): bottom arm extra length, delta_length = 2*L1 L2 (um): L_top horizontal length .. code:: __L2__ | | L0 L0r | | splitter==| |==recombiner | | L0 L0r | | L1 L1 | | |__L2__| .. plot:: :include-source: import pp c = pp.c.mzi(L0=0.1, L1=0, L2=10) pp.plotgds(c) .. plot:: :include-source: import ubc c = ubc.cm.mzi() gl.sweep_simulation(c) """ y = pp.call_if_func(y_model_factory) wg_long = wg(length=(2 * L0 + 2 * L1 + L2) * 1e-6) wg_short = wg(length=(2 * L0 + L2) * 1e-6) # Create the circuit, add all individual instances circuit = Subcircuit("mzi") circuit.add([ (y, "splitter"), (y, "recombiner"), (wg_long, "wg_long"), (wg_short, "wg_short"), ]) circuit.elements["splitter"].pins = ("in1", "out1", "out2") circuit.elements["recombiner"].pins = ("out1", "in2", "in1") # Circuits can be connected using the elements' string names: circuit.connect_many([ ("splitter", "out1", "wg_long", "n1"), ("splitter", "out2", "wg_short", "n1"), ("recombiner", "in1", "wg_long", "n2"), ("recombiner", "in2", "wg_short", "n2"), ]) circuit.elements["splitter"].pins["in1"] = "input" circuit.elements["recombiner"].pins["out1"] = "output" return circuit
def build_circuit(data, libraries): """ Parameters ---------- data : dict The dictionary defining all the circuits and analyzers, as exported by SiEPIC and parsed by the parser. libraries : list or str A string or list of strings of python module names containing the model libraries for the components in the circuit. Returns ------- built : dict A dictionary of constructed Python objects, with the following keys: - `circuits`: dictionary of circuit names to their corresponding instantiated Subcircuit objects. - `subcircuits`: instantiated Subcircuit objects for all subcircuits found in the spice data. - `analyses`: instantiated Simulation objects for all network analyzers found in the spice data. """ # Make sure `libraries` is a list libraries = libraries if type(libraries) is list else [libraries] # Create a dictionary from component names to their models available_comps = get_components(libraries) # Create all the defined subcircuits # [`name`, `ports`, `components`, `params`] subcircuits = {} for sub in data['subcircuits']: circuit = Subcircuit(sub['name']) connections = {} # Create all the components in the subcircuit and compile all the connections # [`name`, `model`, `ports`, `params`] for component in sub['components']: # Add each component kwargs = rearg(component['model'], component['params']) circuit.add([(available_comps[component['model']](**kwargs), component['name'])]) circuit.elements[component['name']].pins = tuple( pin for pin in component['ports']) # Track all it's nets for port in component['ports']: if port in connections: connections[port].append(component['name']) else: connections[port] = [component['name']] # Create all the connections between components for port, comps in connections.items(): circuit.connect(comps[0], port, comps[1], port) if len(comps) == 2 else None subcircuits[sub['name']] = circuit # Create circuits, since they're composed of subcircuits # [`name`, `ports`, `subcircuits`, `params`] circuits = {} # TODO: What if one day a circuit contains more than one subcircuit? for circ in data['circuits']: subs = subcircuits[circ['subcircuits']] if len(circ['ports']) != len(subs.pins): raise ValueError( 'Ports on circuit do not match ports on subcircuit.') circuits[circ['name']] = subs # Create all the simulation objects # [`definition`, `params`]. analyses = [] for analysis in data['analyses']: # [`input_unit`, `input_parameter`] if analysis['definition']['input_parameter'] == 'start_and_stop': # [`minimum_loss`, `analysis_type`, `multithreading`, # `number_of_threads`, `orthogonal_identifier`, `start`, `stop`, # `number_of_points`, `input`, `output`] circ, inp = analysis['params']['output'].split(',') start = analysis['params']['start'] stop = analysis['params']['stop'] points = int(analysis['params']['number_of_points']) mode = 'wl' if analysis['definition'][ 'input_unit'] == 'wavelength' else 'freq' sim = SweepSimulation(circuits[circ], start, stop, points, mode) analyses.append(sim) return { 'circuits': circuits, 'subcircuits': subcircuits, 'analyses': analyses }
from simphony.library import siepic from simphony.netlist import Subcircuit from simphony.simulation import SweepSimulation, MonteCarloSweepSimulation # Declare the models used in the circuit gc = siepic.ebeam_gc_te1550() y = siepic.ebeam_y_1550() wg150 = siepic.ebeam_wg_integral_1550(length=150e-6) wg50 = siepic.ebeam_wg_integral_1550(length=50e-6) # Create the circuit, add all individual instances circuit = Subcircuit('MZI') e = circuit.add([ (gc, 'input'), (gc, 'output'), (y, 'splitter'), (y, 'recombiner'), (wg150, 'wg_long'), (wg50, 'wg_short'), ]) # You can set pin names individually: circuit.elements['input'].pins['n2'] = 'input' circuit.elements['output'].pins['n2'] = 'output' # Or you can rename all the pins simultaneously: circuit.elements['splitter'].pins = ('in1', 'out1', 'out2') circuit.elements['recombiner'].pins = ('out1', 'in2', 'in1') # Circuits can be connected using the elements' string names: circuit.connect_many([ ('input', 'n1', 'splitter', 'in1'),
from simphony.netlist import Subcircuit from simphony.simulation import SweepSimulation, MonteCarloSweepSimulation # Declare the models used in the circuit gc = siepic.ebeam_gc_te1550() y = siepic.ebeam_y_1550() wg150 = siepic.ebeam_wg_integral_1550(length=150e-6) wg50 = siepic.ebeam_wg_integral_1550(length=50e-6) # Create the circuit, add all individual instances circuit = Subcircuit("MZI") e = circuit.add( [ (gc, "input"), (gc, "output"), (y, "splitter"), (y, "recombiner"), (wg150, "wg_long"), (wg50, "wg_short"), ] ) # You can set pin names individually: circuit.elements["input"].pins["n2"] = "input" circuit.elements["output"].pins["n2"] = "output" # Or you can rename all the pins simultaneously: circuit.elements["splitter"].pins = ("in1", "out1", "out2") circuit.elements["recombiner"].pins = ("out1", "in2", "in1") # Circuits can be connected using the elements' string names: circuit.connect_many(
e = circuit.add( [ # Define the four input grating couplers (gc, "in1"), (gc, "in2"), (gc, "in3"), (gc, "in4"), # The grating couplers each feed into their own waveguide (wg100, "wg1"), (wg100, "wg2"), (wg100, "wg3"), (wg100, "wg4"), # Each pair of waveguides feeds into a 50/50 directional coupler (dc, "dc1"), (dc, "dc2"), # After mixing, the center pair of waveguides cross paths at a 100/0 # crossing. The edge pair of waveguides pass uninterrupted. (wg300, "wg_pass1"), (wg100, "wg_in1"), (wgin2, "wg_out1"), (crossover, "crossing"), (wg100, "wg_in2"), (wgin2, "wg_out2"), (wg300, "wg_pass2"), # After crossing, the waveguides are mixed again. (dc, "dc3"), (dc, "dc4"), # The outputs are fed through waveguides. (wg100, "wg5"), (wg100, "wg6"), (wg100, "wg7"), (wg100, "wg8"), # We finally output the values through grating couplers. (gc, "out1"), (gc, "out2"), (gc, "out3"), (gc, "out4"), ] )
def mzi_circuit(L0=1, L1=100, L2=10): """ Mzi Args: L0: vertical length for both and top arms L1: bottom arm extra length L2: L_top horizontal length .. code:: __L2__ | | L0 L0r | | splitter==| |==recombiner | | L0 L0r | | L1 L1 | | |__L2__| .. plot:: :include-source: import pp c = pp.c.mzi(L0=0.1, L1=0, L2=10) pp.plotgds(c) """ gc = siepic.ebeam_gc_te1550() y = siepic.ebeam_y_1550() wg_long = siepic.ebeam_wg_integral_1550(length=(2 * L0 + +L1 + L2) * 1e-6) wg_short = siepic.ebeam_wg_integral_1550(length=(2 * L0 + L2) * 1e-6) # Create the circuit, add all individual instances circuit = Subcircuit("MZI") circuit.add([ (gc, "input"), (gc, "output"), (y, "splitter"), (y, "recombiner"), (wg_long, "wg_long"), (wg_short, "wg_short"), ]) # You can set pin names individually: circuit.elements["input"].pins["n2"] = "input" circuit.elements["output"].pins["n2"] = "output" # Or you can rename all the pins simultaneously: circuit.elements["splitter"].pins = ("in1", "out1", "out2") circuit.elements["recombiner"].pins = ("out1", "in2", "in1") # Circuits can be connected using the elements' string names: circuit.connect_many([ ("input", "n1", "splitter", "in1"), ("splitter", "out1", "wg_long", "n1"), ("splitter", "out2", "wg_short", "n1"), ("recombiner", "in1", "wg_long", "n2"), ("recombiner", "in2", "wg_short", "n2"), ("output", "n1", "recombiner", "out1"), ]) return circuit
cir1 = ring_factory(10000) sim1 = SweepSimulation(cir1, 1500e-9, 1600e-9) res1 = sim1.simulate() f1, s = res1.data(res1.pinlist['in'], res1.pinlist['pass']) plt.plot(f1, s) plt.title("10-micron Ring Resonator") plt.tight_layout() plt.show() # Now, we'll create the circuit (using several ring resonator subcircuits) # and add all individual instances. circuit = Subcircuit('Add-Drop Filter') e = circuit.add([(wg_data, 'input'), (ring_factory(10000), 'ring10'), (wg_data, 'out1'), (wg_data, 'connect1'), (ring_factory(11000), 'ring11'), (wg_data, 'out2'), (wg_data, 'connect2'), (ring_factory(12000), 'ring12'), (wg_data, 'out3'), (term, 'terminator')]) # You can set pin names individually (here I'm naming all the outputs that # I'll want to access after the simulation has been run): circuit.elements['input'].pins['n1'] = 'input' circuit.elements['out1'].pins['n2'] = 'out1' circuit.elements['out2'].pins['n2'] = 'out2' circuit.elements['out3'].pins['n2'] = 'out3' circuit.connect_many([ ('input', 'n2', 'ring10', 'in'), ('out1', 'n1', 'ring10', 'out'), ('connect1', 'n1', 'ring10', 'pass'), ('connect1', 'n2', 'ring11', 'in'),
e = circuit.add([ # Define the four input grating couplers (gc, 'in1'), (gc, 'in2'), (gc, 'in3'), (gc, 'in4'), # The grating couplers each feed into their own waveguide (wg100, 'wg1'), (wg100, 'wg2'), (wg100, 'wg3'), (wg100, 'wg4'), # Each pair of waveguides feeds into a 50/50 directional coupler (dc, 'dc1'), (dc, 'dc2'), # After mixing, the center pair of waveguides cross paths at a 100/0 # crossing. The edge pair of waveguides pass uninterrupted. (wg300, 'wg_pass1'), (wg100, 'wg_in1'), (wgin2, 'wg_out1'), (crossover, 'crossing'), (wg100, 'wg_in2'), (wgin2, 'wg_out2'), (wg300, 'wg_pass2'), # After crossing, the waveguides are mixed again. (dc, 'dc3'), (dc, 'dc4'), # The outputs are fed through waveguides. (wg100, 'wg5'), (wg100, 'wg6'), (wg100, 'wg7'), (wg100, 'wg8'), # We finally output the values through grating couplers. (gc, 'out1'), (gc, 'out2'), (gc, 'out3'), (gc, 'out4'), ])
def mzi(L0=1, L1=100, L2=10, y_model_factory=mmi1x2): """ Mzi Args: L0: vertical length for both and top arms L1: bottom arm extra length, delta_length = 2*L1 L2: L_top horizontal length .. code:: __L2__ | | L0 L0r | | splitter==| |==recombiner | | L0 L0r | | L1 L1 | | |__L2__| .. plot:: :include-source: import pp c = pp.c.mzi(L0=0.1, L1=0, L2=10) pp.plotgds(c) .. plot:: :include-source: import simphony.library.gdsfactory as cl c = cl.mzi() simulation = SweepSimulation(circuit, 1500e-9, 1600e-9) result = simulation.simulate() f, s = result.data("input", "output") plt.plot(f, s) plt.title("MZI") plt.tight_layout() plt.show() """ y = pp.call_if_func(y_model_factory) wg_long = siepic.ebeam_wg_integral_1550(length=(2 * L0 + 2 * L1 + L2) * 1e-6) wg_short = siepic.ebeam_wg_integral_1550(length=(2 * L0 + L2) * 1e-6) # Create the circuit, add all individual instances circuit = Subcircuit("mzi") circuit.add([ (y, "splitter"), (y, "recombiner"), (wg_long, "wg_long"), (wg_short, "wg_short"), ]) # Circuits can be connected using the elements' string names: circuit.connect_many([ ("splitter", "E0", "wg_long", "n1"), ("splitter", "E1", "wg_short", "n1"), ("recombiner", "E0", "wg_long", "n2"), ("recombiner", "E1", "wg_short", "n2"), ]) circuit.elements["splitter"].pins["W0"] = "input" circuit.elements["recombiner"].pins["W0"] = "output" return circuit
def mzi( length_y: float = 1.0, delta_length: float = 100.0, length_x: float = 10.0, y_model_factory: ModelFactory = siepic.ebeam_y_1550, waveguide: ModelFactory = siepic.ebeam_wg_integral_1550, ) -> Subcircuit: """Mzi circuit model Args: length_y: vertical length for both and top arms (um) delta_length: bottom arm extra length length_x: horizontal length for both and top arms (um) waveguide: waveguide_model .. code:: __Lx__ | | Ly Lyr (not a parameter) | | splitter==| |==combiner | | Ly Lyr (not a parameter) | | | delta_length/2 | | |__Lx__| .. plot:: :include-source: import pp c = pp.c.mzi(length_y=0.1, delta_length=0, length_x=10) pp.plotgds(c) .. plot:: :include-source: import ubc import gdslib as gl c = ubc.circuits.mzi() gl.plot_circuit(c) """ y = pp.call_if_func(y_model_factory) wg_long = waveguide(length=(2 * length_y + delta_length + length_x) * 1e-6) wg_short = waveguide(length=(2 * length_y + length_x) * 1e-6) # Create the circuit, add all individual instances circuit = Subcircuit("mzi") circuit.add([ (y, "splitter"), (y, "recombiner"), (wg_long, "wg_long"), (wg_short, "wg_short"), ]) circuit.elements["splitter"].pins = ("in1", "out1", "out2") circuit.elements["recombiner"].pins = ("out1", "in2", "in1") # Circuits can be connected using the elements' string names: circuit.connect_many([ ("splitter", "out1", "wg_long", "n1"), ("splitter", "out2", "wg_short", "n1"), ("recombiner", "in1", "wg_long", "n2"), ("recombiner", "in2", "wg_short", "n2"), ]) circuit.elements["splitter"].pins["in1"] = "input" circuit.elements["recombiner"].pins["out1"] = "output" return circuit
def ring_double( wg_width=0.5, gap=0.2, length_x=4, bend_radius=5, length_y=2, coupler=coupler_ring, waveguide=siepic.ebeam_wg_integral_1550, ): """ double bus ring made of two couplers (ct: top, cb: bottom) connected with two vertical waveguides (wyl: left, wyr: right) .. code:: --==ct==-- | | wl wr length_y | | --==cb==-- gap length_x ---=========--- E0 length_x W0 / \ / \ | | N1 N0 N0 N1 | | \ / \ / ---=========--- W0 length_x E0 .. plot:: :include-source: import pp c = pp.c.ring_double(wg_width=0.5, gap=0.2, length_x=4, bend_radius=5, length_y=2) pp.plotgds(c) .. plot:: :include-source: import gdslib as gl c = gl.ring_double() gl.sweep_simulation(c) """ waveguide = pp.call_if_func(waveguide) coupler = pp.call_if_func(coupler, length_x=length_x, bend_radius=bend_radius, gap=gap, wg_width=wg_width) # Create the circuit, add all individual instances circuit = Subcircuit("ring_double") circuit.add([(coupler, "ct"), (coupler, "cb"), (waveguide, "wl"), (waveguide, "wr")]) # Circuits can be connected using the elements' string names: circuit.connect_many([ ("cb", "N0", "wl", "n1"), ("wl", "n2", "ct", "N1"), ("ct", "N0", "wr", "n1"), ("wr", "n2", "cb", "N1"), ]) circuit.elements["cb"].pins["W0"] = "input" circuit.elements["cb"].pins["E0"] = "output" circuit.elements["ct"].pins["E0"] = "drop" circuit.elements["ct"].pins["W0"] = "cdrop" return circuit