From d25d5152268088def70c1fcf095452577a01ea6f Mon Sep 17 00:00:00 2001 From: Andeloth Date: Tue, 13 Feb 2024 09:59:59 -0700 Subject: [PATCH] moved 2x2mmi gds to files, updates to notebook --- book/{images => files}/mmi2x2.gds | Bin book/pages/scattering_parameters.ipynb | 92 +++++++++++-------------- 2 files changed, 39 insertions(+), 53 deletions(-) rename book/{images => files}/mmi2x2.gds (100%) diff --git a/book/images/mmi2x2.gds b/book/files/mmi2x2.gds similarity index 100% rename from book/images/mmi2x2.gds rename to book/files/mmi2x2.gds diff --git a/book/pages/scattering_parameters.ipynb b/book/pages/scattering_parameters.ipynb index a51a69d..b79156f 100644 --- a/book/pages/scattering_parameters.ipynb +++ b/book/pages/scattering_parameters.ipynb @@ -51,7 +51,7 @@ "\n", "In this section we'll demonstrate how to find the s-params for any device using a meep simulation. We'll show both 2D and 3D simulations, but keep in mind that 2D s-params are not very accurate, but the simulations run much faster. It is often recommended to set up the simulation in 2D to ensure everything works, then extend the simulation into 3D to run the final simulations. This can save us a lot of debugging time since the 2D simulations run faster.\n", "\n", - "We'll run through the steps using gdsfactory's default 2x2 mmi, and explain on the way how the principles can be applied to any component. There are other ways to find s-parameters, and another method is shown on the [Gds To Meep](/pages/gds_to_meep) page" + "We'll run through the steps using gdsfactory's default 2x2 mmi, and explain on the way how the principles can be applied to any component. There are other ways to find s-parameters, one such method is shown on the [Gds To Meep](/pages/gds_to_meep) page." ] }, { @@ -61,28 +61,30 @@ "source": [ "### Step 1 - Import Geometry\n", "\n", - "If we have a gds of the device t can make life easy if we add all of our sources and detectors into our gds before we import it into meep. For this tutorial I have already done this. For your simulations, you can modify your gds file directly in Klayout if you like a GUI, or you can use gdsfactory itself. If you like, you can also use meep to do the same thing, but I won't be showing how to do that here. This tutorial is adapted from the [GDSII Import](https://meep.readthedocs.io/en/latest/Python_Tutorials/GDSII_Import/) tutorial on the meep documentation\n", + "This tutorial is adapted from the [GDSII Import](https://meep.readthedocs.io/en/latest/Python_Tutorials/GDSII_Import/) tutorial.\n", + "\n", + "If we have a gds of the device whose parameters we want to simulate, we can add all of our sources and detectors into our gds before we import it into meep. This will create the sources and monitors in meep that we need for the simulation.\n", "\n", "Here is a picture of the gds we will be using. \n", "\n", "![Image of splitter file](https://raw.githubusercontent.com/BYUCamachoLab/Photonics-Bootcamp/main/book/images/Notebook_Images/mmi2x2_gds_with_ports.png)\n", "\n", - "I've added a box around the entire component (in purple) which will become the simulation region, and 8 lines, 2 on each end that will become the sources and detectors. I also added straight waveguides on the ports of the gdsfactory mmi so that my ports could be exactly where the light would enter and exit the component in an actual circuit. My sources are then slightly outside the mmi, in the waveguides.\n", + "We add a box around the entire component (purple) which will become the simulation region, and 8 lines, 2 on each end that will become the sources and detectors. We also add straight waveguides on the ports of the gdsfactory mmi so that the ports will be exactly where the light would enter and exit the component in an actual circuit.\n", "\n", "So, now that we have a gds, lets import it into meep!" ] }, { "cell_type": "code", - "execution_count": null, + "execution_count": 1, "metadata": {}, "outputs": [], "source": [ "# Imports\n", "import meep as mp \n", - "import numpy\n", + "import numpy as np\n", "import matplotlib.pyplot as plt \n", - "#import math" + "import os" ] }, { @@ -95,27 +97,15 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 2, "metadata": {}, - "outputs": [ - { - "ename": "", - "evalue": "", - "output_type": "error", - "traceback": [ - "\u001b[1;31mFailed to start the Kernel. \n", - "\u001b[1;31mKernel Python 3.9.6 is not usable. Check the Jupyter output tab for more information. \n", - "\u001b[1;31mView Jupyter log for further details." - ] - } - ], + "outputs": [], "source": [ "res = 40 # the resolution of the simulation in pixels/um\n", - "three_d = False # Turn this to false for a 2D simulation\n", - "if three_d: # If three_d, reduce the resolution\n", - " res = res/2\n", + "sim_is_3D = False # Turn this to false for a 2D simulation\n", "\n", - "gds_file = \"/home/jkart/Work/Photonics-Bootcamp/Photonics-Bootcamp/book/images/mmi2x2.gds\" # The name of our gds file\n", + "pwd = os.path.abspath('')\n", + "gds_file = f\"{pwd}/../images/mmi2x2.gds\" # The name of our gds file\n", "\n", "# Here we define the layer numbers for each part of our gds\n", "geometry_L = 1\n", @@ -147,15 +137,14 @@ "silicon = mp.Medium(epsilon=12)\n", "\n", "# Sets the min and max values for the cell and the silicon. Our simulation will be centered at y=0\n", - "cell_zmax = 0.5*cell_thickness if three_d else 0\n", - "cell_zmin = -0.5 * cell_thickness if three_d else 0\n", - "si_zmax = 0.5 * t_Si if three_d else 10 # In a 2D simulation, the geometry still has to have a thickness or it won't show up\n", - "si_zmin = -0.5 * t_Si if three_d else -10\n", + "cell_zmax = 0.5*cell_thickness if sim_is_3D else 0\n", + "cell_zmin = -0.5 * cell_thickness if sim_is_3D else 0\n", + "si_zmax = 0.5 * t_Si if sim_is_3D else 10 # In a 2D simulation, the geometry still has to have a thickness or it won't show up\n", + "si_zmin = -0.5 * t_Si if sim_is_3D else -10\n", "\n", "# Create a 2D array to hold the S-Parameters for the device\n", - "numPorts = 4 # The number of ports, also the size of our array\n", - "# s_params = [[0 for i in range(numPorts)] for j in range(numPorts)]\n", - "# input_params = [0 for i in range(numPorts)]\n" + "n_ports = 4 # The number of ports, also the size of our array\n", + "s_params = np.zeros((n_ports, n_ports))\n" ] }, { @@ -176,9 +165,7 @@ "evalue": "", "output_type": "error", "traceback": [ - "\u001b[1;31mFailed to start the Kernel. \n", - "\u001b[1;31mKernel Python 3.9.6 is not usable. Check the Jupyter output tab for more information. \n", - "\u001b[1;31mView Jupyter log for further details." + "\u001b[1;31mThe Kernel crashed while executing code in the the current cell or a previous cell. Please review the code in the cell(s) to identify a possible cause of the failure. Click here for more info. View Jupyter log for further details." ] } ], @@ -196,8 +183,7 @@ "source3 = mp.GDSII_vol(fname = gds_file, layer = source3_L, zmax=cell_zmax, zmin=cell_zmin)\n", "source4 = mp.GDSII_vol(fname = gds_file, layer = source4_L, zmax=cell_zmax, zmin=cell_zmin)\n", "\n", - "\n", - "if three_d: # Sets up the oxide cladding for a three d simulation (Not used in a 2D simulation)\n", + "if sim_is_3D: # Sets up the oxide cladding for a three d simulation (Not used in a 2D simulation)\n", " oxide_center = mp.Vector3(z=-0.5 * t_oxide)\n", " oxide_size = mp.Vector3(cell.size.x, cell.size.y, t_oxide)\n", " oxide_layer = [mp.Block(material=oxide, center=oxide_center, size=oxide_size)]\n", @@ -243,7 +229,7 @@ " size=source1.size, # Here we input the geometry for our first source\n", " center=source1.center,\n", " eig_band=1,\n", - " eig_parity = mp.NO_PARITY if three_d else mp.EVEN_Y + mp.ODD_Z,\n", + " eig_parity = mp.NO_PARITY if 3D else mp.EVEN_Y + mp.ODD_Z,\n", " eig_match_freq = True,\n", "\n", " )\n", @@ -362,11 +348,11 @@ "#############################################################\n", "alphas = [[1,1,0,0,0], [1,1,0,0,0],[1,1,0,0,1],[1,1,0,0,1]] # This array holds the values for forwards and backward propagation for each soure. The 5th number is for the total input\n", "# Finds the S parameters\n", - "input_coeff = sim.get_eigenmode_coefficients(modeMonitors[0], [1], eig_parity=mp.NO_PARITY if three_d else mp.EVEN_Y + mp.ODD_Z).alpha[0, 0, alphas[0][4]] # This is the amount of light from the source that enters the device\n", - "port1_coeff = sim.get_eigenmode_coefficients(modeMonitors[0], [1], eig_parity=mp.NO_PARITY if three_d else mp.EVEN_Y + mp.ODD_Z).alpha[0, 0, alphas[0][0]]\n", - "port2_coeff = sim.get_eigenmode_coefficients(modeMonitors[1], [1], eig_parity=mp.NO_PARITY if three_d else mp.EVEN_Y + mp.ODD_Z).alpha[0, 0, alphas[0][1]]\n", - "port3_coeff = sim.get_eigenmode_coefficients(modeMonitors[2], [1], eig_parity=mp.NO_PARITY if three_d else mp.EVEN_Y + mp.ODD_Z).alpha[0, 0, alphas[0][2]]\n", - "port4_coeff = sim.get_eigenmode_coefficients(modeMonitors[3], [1], eig_parity=mp.NO_PARITY if three_d else mp.EVEN_Y + mp.ODD_Z).alpha[0, 0, alphas[0][3]]\n", + "input_coeff = sim.get_eigenmode_coefficients(modeMonitors[0], [1], eig_parity=mp.NO_PARITY if 3D else mp.EVEN_Y + mp.ODD_Z).alpha[0, 0, alphas[0][4]] # This is the amount of light from the source that enters the device\n", + "port1_coeff = sim.get_eigenmode_coefficients(modeMonitors[0], [1], eig_parity=mp.NO_PARITY if 3D else mp.EVEN_Y + mp.ODD_Z).alpha[0, 0, alphas[0][0]]\n", + "port2_coeff = sim.get_eigenmode_coefficients(modeMonitors[1], [1], eig_parity=mp.NO_PARITY if 3D else mp.EVEN_Y + mp.ODD_Z).alpha[0, 0, alphas[0][1]]\n", + "port3_coeff = sim.get_eigenmode_coefficients(modeMonitors[2], [1], eig_parity=mp.NO_PARITY if 3D else mp.EVEN_Y + mp.ODD_Z).alpha[0, 0, alphas[0][2]]\n", + "port4_coeff = sim.get_eigenmode_coefficients(modeMonitors[3], [1], eig_parity=mp.NO_PARITY if 3D else mp.EVEN_Y + mp.ODD_Z).alpha[0, 0, alphas[0][3]]\n", "# Store the S parameters in s_params\n", "s_params[0] = [port1_coeff, port2_coeff, port3_coeff, port4_coeff]\n", "input_params[0] = input_coeff\n", @@ -534,7 +520,7 @@ " size=source2.size, # Here we input the geometry for our first source\n", " center=source2.center,\n", " eig_band=1,\n", - " eig_parity = mp.NO_PARITY if three_d else mp.EVEN_Y + mp.ODD_Z,\n", + " eig_parity = mp.NO_PARITY if 3D else mp.EVEN_Y + mp.ODD_Z,\n", " eig_match_freq = True,\n", "\n", " ),\n", @@ -543,7 +529,7 @@ " size=source3.size, # Here we input the geometry for our first source\n", " center=source3.center,\n", " eig_band=1,\n", - " eig_parity = mp.NO_PARITY if three_d else mp.EVEN_Y + mp.ODD_Z,\n", + " eig_parity = mp.NO_PARITY if 3D else mp.EVEN_Y + mp.ODD_Z,\n", " eig_match_freq = True,\n", " eig_kpoint = mp.Vector3(-1,0,0),\n", " direction = mp.NO_DIRECTION\n", @@ -554,7 +540,7 @@ " size=source4.size, # Here we input the geometry for our first source\n", " center=source4.center,\n", " eig_band=1,\n", - " eig_parity = mp.NO_PARITY if three_d else mp.EVEN_Y + mp.ODD_Z,\n", + " eig_parity = mp.NO_PARITY if 3D else mp.EVEN_Y + mp.ODD_Z,\n", " eig_match_freq = True,\n", " eig_kpoint = mp.Vector3(-1,0,0),\n", " direction = mp.NO_DIRECTION\n", @@ -582,7 +568,7 @@ "source": [ "\n", "\n", - "for i in range(numPorts-1) :\n", + "for i in range(n_ports-1) :\n", "\n", " source = sources[i:i+1]\n", "\n", @@ -609,11 +595,11 @@ "\n", " #############################################################\n", " # Finds the S parameters\n", - " port1_coeff = sim.get_eigenmode_coefficients(modeMonitors[0], [1], eig_parity=mp.NO_PARITY if three_d else mp.EVEN_Y + mp.ODD_Z).alpha[0, 0, alphas[i+1][0]]\n", - " port2_coeff = sim.get_eigenmode_coefficients(modeMonitors[1], [1], eig_parity=mp.NO_PARITY if three_d else mp.EVEN_Y + mp.ODD_Z).alpha[0, 0, alphas[i+1][1]]\n", - " port3_coeff = sim.get_eigenmode_coefficients(modeMonitors[2], [1], eig_parity=mp.NO_PARITY if three_d else mp.EVEN_Y + mp.ODD_Z).alpha[0, 0, alphas[i+1][2]]\n", - " port4_coeff = sim.get_eigenmode_coefficients(modeMonitors[3], [1], eig_parity=mp.NO_PARITY if three_d else mp.EVEN_Y + mp.ODD_Z).alpha[0, 0, alphas[i+1][3]]\n", - " input_coeff = sim.get_eigenmode_coefficients(modeMonitors[i+1], [1], eig_parity=mp.NO_PARITY if three_d else mp.EVEN_Y + mp.ODD_Z).alpha[0, 0, alphas[i+1][4]] # This is the total amount of light that entered the devie\n", + " port1_coeff = sim.get_eigenmode_coefficients(modeMonitors[0], [1], eig_parity=mp.NO_PARITY if 3D else mp.EVEN_Y + mp.ODD_Z).alpha[0, 0, alphas[i+1][0]]\n", + " port2_coeff = sim.get_eigenmode_coefficients(modeMonitors[1], [1], eig_parity=mp.NO_PARITY if 3D else mp.EVEN_Y + mp.ODD_Z).alpha[0, 0, alphas[i+1][1]]\n", + " port3_coeff = sim.get_eigenmode_coefficients(modeMonitors[2], [1], eig_parity=mp.NO_PARITY if 3D else mp.EVEN_Y + mp.ODD_Z).alpha[0, 0, alphas[i+1][2]]\n", + " port4_coeff = sim.get_eigenmode_coefficients(modeMonitors[3], [1], eig_parity=mp.NO_PARITY if 3D else mp.EVEN_Y + mp.ODD_Z).alpha[0, 0, alphas[i+1][3]]\n", + " input_coeff = sim.get_eigenmode_coefficients(modeMonitors[i+1], [1], eig_parity=mp.NO_PARITY if 3D else mp.EVEN_Y + mp.ODD_Z).alpha[0, 0, alphas[i+1][4]] # This is the total amount of light that entered the devie\n", "\n", " # Store the S parameters in s_params\n", " index = 1+i \n", @@ -639,7 +625,7 @@ ], "source": [ "print(\"S-Params:\")\n", - "for i in range(numPorts):\n", + "for i in range(n_ports):\n", " print(f\"S{i+1}1: {s_params[i][0]}\\tS{i+1}2: {s_params[i][1]}\\tS{i+1}3: {s_params[i][2]}\\tS{i+1}4: {s_params[i][3]}\")\n", "\n", "print(\"Input S-Params\")\n", @@ -648,7 +634,7 @@ "\n", "\n", "print(\"Transmission:\")\n", - "for i in range(numPorts):\n", + "for i in range(n_ports):\n", " input_power = abs(input_params[i])**2\n", " trans1 = abs(s_params[i][0])**2 / input_power * 100\n", " trans2 = abs(s_params[i][1])**2 / input_power * 100\n", @@ -720,7 +706,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.9.6" + "version": "3.11.7" }, "orig_nbformat": 4, "vscode": {