{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Mach-Zehnder Interferometer (MZI)\n",
"\n",
"**We use SiEPIC EBeam library in this tutorial.**\n",
"\n",
" This notebook walks through the process of setting up and simulating a mach-zehnder interferometer device using the OPICS package. \n",
"\n",
" A mach-zehnder interferometer is a basic waveguide interference device. It consists of two couplers (or Y branches) connected by two waveguides of different length (see below). The difference between the two waveguide lengths causes differential delay, which contributes to the frequency dependent interference pattern.\n",
"\n",
""
]
},
{
"cell_type": "code",
"execution_count": 1,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"\n",
" ____ ____ _______________\n",
" / __ \\/ __ \\/ _/ ____/ ___/\n",
" / / / / /_/ // // / \\__ \\\n",
"/ /_/ / ____// // /___ ___/ /\n",
"\\____/_/ /___/\\____//____/\n",
"\n",
"OPICS version 0.2.1\n"
]
}
],
"source": [
"import time\n",
"import numpy as np\n",
"import matplotlib.pyplot as plt\n",
"import opics"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Import component library\n",
"Import `ebeam` library from `libs` module."
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {},
"outputs": [],
"source": [
"ebeam = opics.libraries.ebeam"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Define network \n",
"Create an instance of `Network` class, which is used to add, connect, and simulate circuit components. "
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {},
"outputs": [],
"source": [
"#defining custom frequency data points for a component\n",
"f = np.linspace(opics.C*1e6/1.5, opics.C*1e6/1.6, 2000)\n",
"circuit_name = \"mzi\"\n",
"circuit = opics.Network(network_id=circuit_name, f=f)"
]
},
{
"cell_type": "code",
"execution_count": 4,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"\u001b[1;31mInit signature:\u001b[0m\n",
"\u001b[0mebeam\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mWaveguide\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;33m\n",
"\u001b[0m \u001b[0mf\u001b[0m\u001b[1;33m:\u001b[0m \u001b[0mnumpy\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mndarray\u001b[0m \u001b[1;33m=\u001b[0m \u001b[0marray\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;33m[\u001b[0m\u001b[1;36m1.99861639e+14\u001b[0m\u001b[1;33m,\u001b[0m \u001b[1;36m1.99855390e+14\u001b[0m\u001b[1;33m,\u001b[0m \u001b[1;36m1.99849141e+14\u001b[0m\u001b[1;33m,\u001b[0m \u001b[1;33m...\u001b[0m\u001b[1;33m,\u001b[0m\u001b[1;33m\n",
"\u001b[0m \u001b[1;36m1.87382784e+14\u001b[0m\u001b[1;33m,\u001b[0m \u001b[1;36m1.87376535e+14\u001b[0m\u001b[1;33m,\u001b[0m \u001b[1;36m1.87370286e+14\u001b[0m\u001b[1;33m]\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m,\u001b[0m\u001b[1;33m\n",
"\u001b[0m \u001b[0mlength\u001b[0m\u001b[1;33m:\u001b[0m \u001b[0mfloat\u001b[0m \u001b[1;33m=\u001b[0m \u001b[1;36m5e-06\u001b[0m\u001b[1;33m,\u001b[0m\u001b[1;33m\n",
"\u001b[0m \u001b[0mheight\u001b[0m\u001b[1;33m:\u001b[0m \u001b[0mfloat\u001b[0m \u001b[1;33m=\u001b[0m \u001b[1;36m2.2e-07\u001b[0m\u001b[1;33m,\u001b[0m\u001b[1;33m\n",
"\u001b[0m \u001b[0mwidth\u001b[0m\u001b[1;33m:\u001b[0m \u001b[0mfloat\u001b[0m \u001b[1;33m=\u001b[0m \u001b[1;36m5e-07\u001b[0m\u001b[1;33m,\u001b[0m\u001b[1;33m\n",
"\u001b[0m \u001b[0mloss\u001b[0m\u001b[1;33m:\u001b[0m \u001b[0mint\u001b[0m \u001b[1;33m=\u001b[0m \u001b[1;36m700\u001b[0m\u001b[1;33m,\u001b[0m\u001b[1;33m\n",
"\u001b[0m \u001b[0mOID\u001b[0m\u001b[1;33m:\u001b[0m \u001b[0mint\u001b[0m \u001b[1;33m=\u001b[0m \u001b[1;36m1\u001b[0m\u001b[1;33m,\u001b[0m\u001b[1;33m\n",
"\u001b[0m\u001b[1;33m)\u001b[0m \u001b[1;33m->\u001b[0m \u001b[1;32mNone\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n",
"\u001b[1;31mDocstring:\u001b[0m \n",
"Waveguides are components that guide waves. Although these are individual components that can\n",
"be adjusted for use, it is recommended to draw paths in KLayout and convert them to waveguides\n",
"using the built-in SiEPIC features.\n",
"\n",
"Model schematic:\n",
"~~~~~~~~~~~~~~~~\n",
"\n",
"0 ┌─────────┐ 1\n",
" └─────────┘\n",
"\u001b[1;31mType:\u001b[0m type\n",
"\u001b[1;31mSubclasses:\u001b[0m \n"
]
}
],
"source": [
"ebeam.Waveguide?"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Add circuit components\n",
"\n",
"Add grating couplers, 3dB power splitters (e.g. Y-splitter or Y-branch), and waveguides to circuit. You can define custom frequency data points for a component as well (see the example for output_GC)."
]
},
{
"cell_type": "code",
"execution_count": 5,
"metadata": {},
"outputs": [],
"source": [
"\n",
"\n",
"#define component instances\n",
"input_gc = circuit.add_component(ebeam.GC)\n",
"y1 = circuit.add_component(ebeam.Y)\n",
"wg1 = circuit.add_component(ebeam.Waveguide, params=dict(length=50e-6))\n",
"wg2 = circuit.add_component(ebeam.Waveguide, params=dict(length=150e-6))\n",
"y2 = circuit.add_component(ebeam.Y)\n",
"output_gc = circuit.add_component(ebeam.GC)\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Define circuit connectivity\n",
"\n",
"In this section, we define the component connections. The connections are defined using `Network.connect`, e.g.\n",
"\n",
"`Network.connect(component1, component1_port, component2, component2_port)`"
]
},
{
"cell_type": "code",
"execution_count": 6,
"metadata": {},
"outputs": [],
"source": [
"#define circuit connectivity\n",
"circuit.connect(input_gc, 1, y1, 0)\n",
"circuit.connect(y1, 1, wg1, 0)\n",
"circuit.connect(y1, 2, wg2, 0)\n",
"circuit.connect(y2, 0, output_gc, 1)\n",
"circuit.connect(wg1, 1, y2, 1)\n",
"circuit.connect(wg2, 1, y2, 2)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Simuate the circuit"
]
},
{
"cell_type": "code",
"execution_count": 7,
"metadata": {},
"outputs": [
{
"ename": "TypingError",
"evalue": "Failed in nopython mode pipeline (step: nopython frontend)\n\u001b[1m\u001b[1m\u001b[1mNo implementation of function Function() found for signature:\n \n >>> reshape(array(complex128, 2d, A), UniTuple(int64 x 3))\n \nThere are 2 candidate implementations:\n\u001b[1m - Of which 2 did not match due to:\n Overload in function 'np_reshape': File: numba\\np\\arrayobj.py: Line 1702.\n With argument(s): '(array(complex128, 2d, A), UniTuple(int64 x 3))':\u001b[0m\n\u001b[1m Rejected as the implementation raised a specific error:\n TypingError: Failed in nopython mode pipeline (step: nopython frontend)\n \u001b[1m\u001b[1m\u001b[1m\u001b[1m- Resolution failure for literal arguments:\n \u001b[1mreshape() supports contiguous array only\u001b[0m\n \u001b[0m\u001b[1m- Resolution failure for non-literal arguments:\n \u001b[1mNone\u001b[0m\n \u001b[0m\u001b[0m\n \u001b[0m\u001b[1mDuring: resolving callee type: BoundFunction(array.reshape for array(complex128, 2d, A))\u001b[0m\n \u001b[0m\u001b[1mDuring: typing of call at C:\\Users\\jeida\\anaconda3\\lib\\site-packages\\numba\\np\\arrayobj.py (1705)\n \u001b[0m\n \u001b[1m\n File \"..\\..\\..\\..\\..\\..\\..\\anaconda3\\lib\\site-packages\\numba\\np\\arrayobj.py\", line 1705:\u001b[0m\n \u001b[1m def np_reshape_impl(a, shape):\n \u001b[1m return a.reshape(shape)\n \u001b[0m \u001b[1m^\u001b[0m\u001b[0m\n\u001b[0m\n raised from C:\\Users\\jeida\\anaconda3\\lib\\site-packages\\numba\\core\\typeinfer.py:1071\n\u001b[0m\n\u001b[0m\u001b[1mDuring: resolving callee type: Function()\u001b[0m\n\u001b[0m\u001b[1mDuring: typing of call at c:\\users\\jeida\\documents\\github\\dev-jaspreetj\\opics\\opics\\sparam_ops.py (166)\n\u001b[0m\n\u001b[1m\nFile \"..\\..\\..\\opics\\sparam_ops.py\", line 166:\u001b[0m\n\u001b[1mdef v_broadcast_sim(A: np.ndarray, k: int, l: int) -> np.ndarray:\n