{ "cells": [ { "cell_type": "markdown", "metadata": { "tags": [] }, "source": [ "# Field projection for a zone plate\n", "\n", "This tutorial will show you how to solve for electromagnetic fields far away from your structure using field information stored on a nearby surface.\n", "\n", "This field projection technique is very useful for reducing the simulation size needed for structures involving lots of empty space.\n", "\n", "As an example, we will simulate a simple zone plate lens with a very thin domain size, and measure the transmitted fields just above the structure. Then, we'll show how to use field projections to compute the fields at the focal plane above the lens." ] }, { "cell_type": "code", "execution_count": 1, "metadata": { "execution": { "iopub.execute_input": "2023-02-03T03:34:35.384193Z", "iopub.status.busy": "2023-02-03T03:34:35.383549Z", "iopub.status.idle": "2023-02-03T03:34:36.593584Z", "shell.execute_reply": "2023-02-03T03:34:36.593198Z" }, "tags": [] }, "outputs": [ { "data": { "text/html": [ "
[21:34:36] WARNING This version of Tidy3D was pip installed from the 'tidy3d-beta' repository on __init__.py:103\n", " PyPI. Future releases will be uploaded to the 'tidy3d' repository. From now on, \n", " please use 'pip install tidy3d' instead. \n", "\n" ], "text/plain": [ "\u001b[2;36m[21:34:36]\u001b[0m\u001b[2;36m \u001b[0m\u001b[31mWARNING \u001b[0m This version of Tidy3D was pip installed from the \u001b[32m'tidy3d-beta'\u001b[0m repository on \u001b]8;id=778311;file:///Users/twhughes/Documents/Flexcompute/tidy3d-docs/tidy3d/tidy3d/__init__.py\u001b\\\u001b[2m__init__.py\u001b[0m\u001b]8;;\u001b\\\u001b[2m:\u001b[0m\u001b]8;id=656429;file:///Users/twhughes/Documents/Flexcompute/tidy3d-docs/tidy3d/tidy3d/__init__.py#103\u001b\\\u001b[2m103\u001b[0m\u001b]8;;\u001b\\\n", "\u001b[2;36m \u001b[0m PyPI. Future releases will be uploaded to the \u001b[32m'tidy3d'\u001b[0m repository. From now on, \u001b[2m \u001b[0m\n", "\u001b[2;36m \u001b[0m please use \u001b[32m'pip install tidy3d'\u001b[0m instead. \u001b[2m \u001b[0m\n" ] }, "metadata": {}, "output_type": "display_data" }, { "data": { "text/html": [ "
INFO Using client version: 1.9.0rc1 __init__.py:121\n", "\n" ], "text/plain": [ "\u001b[2;36m \u001b[0m\u001b[2;36m \u001b[0m\u001b[34mINFO \u001b[0m Using client version: \u001b[1;36m1.9\u001b[0m.0rc1 \u001b]8;id=808537;file:///Users/twhughes/Documents/Flexcompute/tidy3d-docs/tidy3d/tidy3d/__init__.py\u001b\\\u001b[2m__init__.py\u001b[0m\u001b]8;;\u001b\\\u001b[2m:\u001b[0m\u001b]8;id=358793;file:///Users/twhughes/Documents/Flexcompute/tidy3d-docs/tidy3d/tidy3d/__init__.py#121\u001b\\\u001b[2m121\u001b[0m\u001b]8;;\u001b\\\n" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "# standard python imports\n", "import numpy as np\n", "import matplotlib.pyplot as plt\n", "\n", "# tidy3d imports\n", "import tidy3d as td\n", "import tidy3d.web as web\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Problem Setup\n", "Below is a rough sketch of the setup of the field projection.\n", "\n", "The transmitted near fields are measured just above the metalens on the blue line, and are projected to the focal plane denoted by the red line.\n", "\n", "" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Define Simulation Parameters\n", "\n", "As always, we first need to define our simulation parameters. As a reminder, all length units in `tidy3D` are specified in microns." ] }, { "cell_type": "code", "execution_count": 2, "metadata": { "execution": { "iopub.execute_input": "2023-02-03T03:34:36.596068Z", "iopub.status.busy": "2023-02-03T03:34:36.595801Z", "iopub.status.idle": "2023-02-03T03:34:36.599191Z", "shell.execute_reply": "2023-02-03T03:34:36.598849Z" }, "tags": [] }, "outputs": [], "source": [ "# 1 nanometer in units of microns (for conversion)\n", "nm = 1e-3\n", "\n", "# free space central wavelength\n", "wavelength = 1.0\n", "\n", "# numerical aperture\n", "NA = 0.8\n", "\n", "# height of lens features\n", "height_lens = 200 * nm\n", "\n", "# space between bottom PML and substrate (-z)\n", "# and the space between lens structure and top pml (+z)\n", "space_below_sub = 1.5 * wavelength\n", "\n", "# height of substrate (um)\n", "height_sub = wavelength / 2\n", "\n", "# side length (xy plane) of entire metalens (um)\n", "length_xy = 20 * wavelength\n", "\n", "# Lens and substrate refractive index\n", "n_TiO2 = 2.40\n", "n_SiO2 = 1.46\n", "\n", "# define material properties\n", "air = td.Medium(permittivity=1.0)\n", "SiO2 = td.Medium(permittivity=n_SiO2**2)\n", "TiO2 = td.Medium(permittivity=n_TiO2**2)\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Process Geometry\n", "\n", "Next we perform some conversions based on these parameters to define the simulation." ] }, { "cell_type": "code", "execution_count": 3, "metadata": { "execution": { "iopub.execute_input": "2023-02-03T03:34:36.601415Z", "iopub.status.busy": "2023-02-03T03:34:36.601244Z", "iopub.status.idle": "2023-02-03T03:34:36.604116Z", "shell.execute_reply": "2023-02-03T03:34:36.603787Z" }, "tags": [] }, "outputs": [], "source": [ "# because the wavelength is in microns, use builtin td.C_0 (um/s) to get frequency in Hz\n", "f0 = td.C_0 / wavelength\n", "\n", "# Define PML layers, for this application we surround the whole structure in PML to isolate the fields\n", "boundary_spec = td.BoundarySpec.all_sides(boundary=td.PML())\n", "\n", "# domain size in z, note, we're just simulating a thin slice: (space -> substrate -> lens height -> space)\n", "length_z = space_below_sub + height_sub + height_lens + space_below_sub\n", "\n", "# construct simulation size array\n", "sim_size = (length_xy, length_xy, length_z)\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Create Geometry\n", "\n", "Now we create the ring metalens programatically" ] }, { "cell_type": "code", "execution_count": 4, "metadata": { "execution": { "iopub.execute_input": "2023-02-03T03:34:36.606297Z", "iopub.status.busy": "2023-02-03T03:34:36.605998Z", "iopub.status.idle": "2023-02-03T03:34:36.614227Z", "shell.execute_reply": "2023-02-03T03:34:36.613688Z" }, "tags": [] }, "outputs": [], "source": [ "# define substrate\n", "substrate = td.Structure(\n", " geometry=td.Box(\n", " center=[0, 0, -length_z / 2 + space_below_sub + height_sub / 2.0],\n", " size=[td.inf, td.inf, height_sub],\n", " ),\n", " medium=SiO2,\n", ")\n", "\n", "# focal length\n", "focal_length = length_xy / 2 / NA * np.sqrt(1 - NA**2)\n", "\n", "# location from center for edge of the n-th inner ring, see https://en.wikipedia.org/wiki/Zone_plate\n", "def edge(n):\n", " return np.sqrt(n * wavelength * focal_length + n**2 * wavelength**2 / 4)\n", "\n", "\n", "# loop through the ring indeces until it's too big and add each to geometry list\n", "n = 1\n", "r = edge(n)\n", "rings = []\n", "while r < 2 * length_xy:\n", " # progressively wider cylinders, material alternating between air and TiO2\n", "\n", " cylinder = td.Structure(\n", " geometry=td.Cylinder(\n", " center=[\n", " 0,\n", " 0,\n", " -length_z / 2 + space_below_sub + height_sub + height_lens / 2,\n", " ],\n", " axis=2,\n", " radius=r,\n", " length=height_lens,\n", " ),\n", " medium=TiO2 if n % 2 == 0 else air,\n", " )\n", " rings.append(cylinder)\n", "\n", " n += 1\n", " r = edge(n)\n", "\n", "# reverse geometry list so that inner, smaller rings are added last and therefore override larger rings.\n", "rings.reverse()\n", "geometry = [substrate] + rings\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Create Source\n", "\n", "Create a plane wave incident from below the structure" ] }, { "cell_type": "code", "execution_count": 5, "metadata": { "execution": { "iopub.execute_input": "2023-02-03T03:34:36.616721Z", "iopub.status.busy": "2023-02-03T03:34:36.616547Z", "iopub.status.idle": "2023-02-03T03:34:36.619551Z", "shell.execute_reply": "2023-02-03T03:34:36.619167Z" }, "tags": [] }, "outputs": [], "source": [ "# Bandwidth in Hz\n", "fwidth = f0 / 10.0\n", "\n", "# Gaussian source offset; the source peak is at time t = offset/fwidth\n", "offset = 4.0\n", "\n", "# time dependence of source\n", "gaussian = td.GaussianPulse(freq0=f0, fwidth=fwidth)\n", "\n", "source = td.PlaneWave(\n", " center=(0, 0, -length_z / 2 + space_below_sub / 2),\n", " size=(td.inf, td.inf, 0),\n", " source_time=gaussian,\n", " direction=\"+\",\n", " pol_angle=0.0,\n", ")\n", "\n", "# Simulation run time\n", "run_time = 40 / fwidth\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Create Monitors\n", "\n", "We'll create a [FieldProjectionCartesianMonitor](https://docs.flexcompute.com/projects/tidy3d/en/v1.9.0rc1/_autosummary/tidy3d.FieldProjectionCartesianMonitor.html) monitor which measures the fields just above the structure, and projects them to a Cartesian plane in the far field a given distance away. We'll also make a dedicated near-field monitor just to see what the near fields look like." ] }, { "cell_type": "code", "execution_count": 6, "metadata": { "execution": { "iopub.execute_input": "2023-02-03T03:34:36.621596Z", "iopub.status.busy": "2023-02-03T03:34:36.621435Z", "iopub.status.idle": "2023-02-03T03:34:36.625989Z", "shell.execute_reply": "2023-02-03T03:34:36.625600Z" }, "tags": [] }, "outputs": [], "source": [ "# place the monitors halfway between top of lens and PML\n", "pos_monitor_z = (\n", " -length_z / 2 + space_below_sub + height_sub + height_lens + space_below_sub / 2\n", ")\n", "\n", "# set the points on the observation grid at which fields should be projected\n", "num_far = 40\n", "xs_far = 4 * wavelength * np.linspace(-0.5, 0.5, num_far)\n", "ys_far = 4 * wavelength * np.linspace(-0.5, 0.5, num_far)\n", "\n", "monitor_far = td.FieldProjectionCartesianMonitor(\n", " center=[\n", " 0.0,\n", " 0.0,\n", " pos_monitor_z,\n", " ], # center of the near field surface on which fields are recorded\n", " size=[\n", " td.inf,\n", " td.inf,\n", " 0,\n", " ], # size of the near field surface on which fields are recorded\n", " normal_dir=\"+\", # normal vector direction of the near field surface on which fields are recorded\n", " freqs=[f0],\n", " name=\"farfield\",\n", " x=xs_far,\n", " y=ys_far,\n", " proj_axis=2, # direction along which fields are projected\n", " proj_distance=focal_length, # signed distance along the projection axis at which the observations grid resides\n", " far_field_approx=False, # turn off the geometric far field approximations, which in this case would lead to\n", " # inaccurate fields because the distance to the focal plane is of the same order as\n", " # the size of the structure and the near field monitor\n", ")\n", "\n", "monitor_near = td.FieldMonitor(\n", " center=[0.0, 0.0, pos_monitor_z],\n", " size=[td.inf, td.inf, 0],\n", " freqs=[f0],\n", " name=\"nearfield\",\n", ")\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Create Simulation\n", "\n", "Put everything together and define a simulation object. A nonuniform simulation grid is generated automatically based on a given number of cells per wavelength in each material (10 by default), using the frequencies defined in the sources.\n" ] }, { "cell_type": "code", "execution_count": 7, "metadata": { "execution": { "iopub.execute_input": "2023-02-03T03:34:36.628073Z", "iopub.status.busy": "2023-02-03T03:34:36.627911Z", "iopub.status.idle": "2023-02-03T03:34:36.641458Z", "shell.execute_reply": "2023-02-03T03:34:36.641115Z" }, "tags": [] }, "outputs": [ { "data": { "text/html": [ "
WARNING Structure at structures[57] has bounds that extend exactly to simulation simulation.py:373\n", " edges. This can cause unexpected behavior. If intending to extend the \n", " structure to infinity along one dimension, use td.inf as a size variable \n", " instead to make this explicit. \n", "\n" ], "text/plain": [ "\u001b[2;36m \u001b[0m\u001b[2;36m \u001b[0m\u001b[31mWARNING \u001b[0m Structure at structures\u001b[1m[\u001b[0m\u001b[1;36m57\u001b[0m\u001b[1m]\u001b[0m has bounds that extend exactly to simulation \u001b]8;id=111767;file:///Users/twhughes/Documents/Flexcompute/tidy3d-docs/tidy3d/tidy3d/components/simulation.py\u001b\\\u001b[2msimulation.py\u001b[0m\u001b]8;;\u001b\\\u001b[2m:\u001b[0m\u001b]8;id=743751;file:///Users/twhughes/Documents/Flexcompute/tidy3d-docs/tidy3d/tidy3d/components/simulation.py#373\u001b\\\u001b[2m373\u001b[0m\u001b]8;;\u001b\\\n", "\u001b[2;36m \u001b[0m edges. This can cause unexpected behavior. If intending to extend the \u001b[2m \u001b[0m\n", "\u001b[2;36m \u001b[0m structure to infinity along one dimension, use td.inf as a size variable \u001b[2m \u001b[0m\n", "\u001b[2;36m \u001b[0m instead to make this explicit. \u001b[2m \u001b[0m\n" ] }, "metadata": {}, "output_type": "display_data" }, { "data": { "text/html": [ "
WARNING Structure at structures[57] has bounds that extend exactly to simulation simulation.py:373\n", " edges. This can cause unexpected behavior. If intending to extend the \n", " structure to infinity along one dimension, use td.inf as a size variable \n", " instead to make this explicit. \n", "\n" ], "text/plain": [ "\u001b[2;36m \u001b[0m\u001b[2;36m \u001b[0m\u001b[31mWARNING \u001b[0m Structure at structures\u001b[1m[\u001b[0m\u001b[1;36m57\u001b[0m\u001b[1m]\u001b[0m has bounds that extend exactly to simulation \u001b]8;id=10392;file:///Users/twhughes/Documents/Flexcompute/tidy3d-docs/tidy3d/tidy3d/components/simulation.py\u001b\\\u001b[2msimulation.py\u001b[0m\u001b]8;;\u001b\\\u001b[2m:\u001b[0m\u001b]8;id=759414;file:///Users/twhughes/Documents/Flexcompute/tidy3d-docs/tidy3d/tidy3d/components/simulation.py#373\u001b\\\u001b[2m373\u001b[0m\u001b]8;;\u001b\\\n", "\u001b[2;36m \u001b[0m edges. This can cause unexpected behavior. If intending to extend the \u001b[2m \u001b[0m\n", "\u001b[2;36m \u001b[0m structure to infinity along one dimension, use td.inf as a size variable \u001b[2m \u001b[0m\n", "\u001b[2;36m \u001b[0m instead to make this explicit. \u001b[2m \u001b[0m\n" ] }, "metadata": {}, "output_type": "display_data" }, { "data": { "text/html": [ "
WARNING Structure at structures[57] has bounds that extend exactly to simulation simulation.py:373\n", " edges. This can cause unexpected behavior. If intending to extend the \n", " structure to infinity along one dimension, use td.inf as a size variable \n", " instead to make this explicit. \n", "\n" ], "text/plain": [ "\u001b[2;36m \u001b[0m\u001b[2;36m \u001b[0m\u001b[31mWARNING \u001b[0m Structure at structures\u001b[1m[\u001b[0m\u001b[1;36m57\u001b[0m\u001b[1m]\u001b[0m has bounds that extend exactly to simulation \u001b]8;id=563051;file:///Users/twhughes/Documents/Flexcompute/tidy3d-docs/tidy3d/tidy3d/components/simulation.py\u001b\\\u001b[2msimulation.py\u001b[0m\u001b]8;;\u001b\\\u001b[2m:\u001b[0m\u001b]8;id=883943;file:///Users/twhughes/Documents/Flexcompute/tidy3d-docs/tidy3d/tidy3d/components/simulation.py#373\u001b\\\u001b[2m373\u001b[0m\u001b]8;;\u001b\\\n", "\u001b[2;36m \u001b[0m edges. This can cause unexpected behavior. If intending to extend the \u001b[2m \u001b[0m\n", "\u001b[2;36m \u001b[0m structure to infinity along one dimension, use td.inf as a size variable \u001b[2m \u001b[0m\n", "\u001b[2;36m \u001b[0m instead to make this explicit. \u001b[2m \u001b[0m\n" ] }, "metadata": {}, "output_type": "display_data" }, { "data": { "text/html": [ "
WARNING Structure at structures[57] has bounds that extend exactly to simulation simulation.py:373\n", " edges. This can cause unexpected behavior. If intending to extend the \n", " structure to infinity along one dimension, use td.inf as a size variable \n", " instead to make this explicit. \n", "\n" ], "text/plain": [ "\u001b[2;36m \u001b[0m\u001b[2;36m \u001b[0m\u001b[31mWARNING \u001b[0m Structure at structures\u001b[1m[\u001b[0m\u001b[1;36m57\u001b[0m\u001b[1m]\u001b[0m has bounds that extend exactly to simulation \u001b]8;id=21006;file:///Users/twhughes/Documents/Flexcompute/tidy3d-docs/tidy3d/tidy3d/components/simulation.py\u001b\\\u001b[2msimulation.py\u001b[0m\u001b]8;;\u001b\\\u001b[2m:\u001b[0m\u001b]8;id=319183;file:///Users/twhughes/Documents/Flexcompute/tidy3d-docs/tidy3d/tidy3d/components/simulation.py#373\u001b\\\u001b[2m373\u001b[0m\u001b]8;;\u001b\\\n", "\u001b[2;36m \u001b[0m edges. This can cause unexpected behavior. If intending to extend the \u001b[2m \u001b[0m\n", "\u001b[2;36m \u001b[0m structure to infinity along one dimension, use td.inf as a size variable \u001b[2m \u001b[0m\n", "\u001b[2;36m \u001b[0m instead to make this explicit. \u001b[2m \u001b[0m\n" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "simulation = td.Simulation(\n", " size=sim_size,\n", " grid_spec=td.GridSpec.auto(min_steps_per_wvl=20),\n", " structures=geometry,\n", " sources=[source],\n", " monitors=[monitor_far, monitor_near],\n", " run_time=run_time,\n", " boundary_spec=boundary_spec,\n", ")\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Visualize Geometry\n", "\n", "Let's take a look and make sure everything is defined properly\n" ] }, { "cell_type": "code", "execution_count": 8, "metadata": { "execution": { "iopub.execute_input": "2023-02-03T03:34:36.643634Z", "iopub.status.busy": "2023-02-03T03:34:36.643469Z", "iopub.status.idle": "2023-02-03T03:34:37.413597Z", "shell.execute_reply": "2023-02-03T03:34:37.413109Z" }, "tags": [] }, "outputs": [ { "data": { "text/html": [ "
INFO Auto meshing using wavelength 1.0000 defined from sources. grid_spec.py:510\n", "\n" ], "text/plain": [ "\u001b[2;36m \u001b[0m\u001b[2;36m \u001b[0m\u001b[34mINFO \u001b[0m Auto meshing using wavelength \u001b[1;36m1.0000\u001b[0m defined from sources. \u001b]8;id=368946;file:///Users/twhughes/Documents/Flexcompute/tidy3d-docs/tidy3d/tidy3d/components/grid/grid_spec.py\u001b\\\u001b[2mgrid_spec.py\u001b[0m\u001b]8;;\u001b\\\u001b[2m:\u001b[0m\u001b]8;id=173909;file:///Users/twhughes/Documents/Flexcompute/tidy3d-docs/tidy3d/tidy3d/components/grid/grid_spec.py#510\u001b\\\u001b[2m510\u001b[0m\u001b]8;;\u001b\\\n" ] }, "metadata": {}, "output_type": "display_data" }, { "data": { "text/plain": [ "