diff --git a/notebooks/luminescent.ipynb b/notebooks/luminescent.ipynb index c39885fe..7429f7e8 100644 --- a/notebooks/luminescent.ipynb +++ b/notebooks/luminescent.ipynb @@ -1,789 +1,36 @@ { - "cells": [ - { - "cell_type": "markdown", - "id": "0", - "metadata": {}, - "source": [ - "# FDTD Luminescent Simulation and Inverse Design\n", - "v0.2.12-beta\n", - "2024/12/06 \n", - "Paul Shen " - ] - }, - { - "cell_type": "markdown", - "id": "1", - "metadata": {}, - "source": [ - "# Synopsis\n", - "\n", - "[Luminescent AI](https://luminescentai.com/) empowers photonic and RF engineers to simulate or inverse design complex electromagnetic components in just a few lines of code! We created an automatic differentiation (AD) and GPU compatible FDTD engine and geometry generator for photonic integrated circuits (PIC) and metasurfaces, as well (in the future) RF microstrip circuits and patch antennas. Experimental release 🥼. Expect bugs 🐛🐞\n", - "\n", - "[Follow us](https://www.linkedin.com/company/luminescent-ai/about) for updates! Star us [GitHub](https://github.com/paulxshen/Luminescent.jl) if you like our work. We respond to issues within a day " - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "2", - "metadata": {}, - "outputs": [], - "source": [ - "%%HTML\n", - "" - ] - }, - { - "cell_type": "markdown", - "id": "3", - "metadata": {}, - "source": [ - "# Features and conventions" - ] - }, - { - "cell_type": "markdown", - "id": "4", - "metadata": {}, - "source": [ - "We do simulation and generative inverse design in 3D or 2.5D, with multiple wavelengths and modes, and on CPU or GPU. However, GPU acceleration is guaranteed only for simulation and not inverse design because the later requires significant VRAM. \n" - ] - }, - { - "cell_type": "markdown", - "id": "5", - "metadata": {}, - "source": [ - "## Technical features of FDTD backend\n", - "- Automatic differentiation (AD) compatible for adjoint optimization\n", - "- Optional GPU acceleration \n", - "- Length scale controlled geometry optimizer \n", - "- Nonlinear and anisotropic materials \n", - "- Tensor subpixel smoothing for accuracy \n", - "- Adaptive graded grid and Float16 support for speed \n", - "- PML, periodic, PEC, PMC boundaries \n", - "- Modal sources, plane waves, Gaussian beams\n", - "- Modal monitors, DFT fields " - ] - }, - { - "cell_type": "markdown", - "id": "6", - "metadata": {}, - "source": [ - "## Geometry layout\n", - "We layout using Python gdsfactory which is integrated with KLayout. Can also import .gds into gdsfactory . Internally, gdsfactory component and layer stack let generate a 3D mesh which is clipped vertically some depths above and below `core_layer`. By default,\n", - "we apply a SOI 220nm node (Si, SiO, SiN and Ge layers) implemented by [`gdsfactory.generic_tech`](https://gdsfactory.github.io/gdsfactory/notebooks/03_layer_stack.html#layerstack) layer stack. `luminescent.MATERIALS` maps the material tag of a layer in the layer stack to its property eg `MATERIALS[\"si\"][\"epsilon\"]`. Can create your own LAYER_STACK and MATERIALS for your process node." - ] - }, - { - "cell_type": "markdown", - "id": "7", - "metadata": {}, - "source": [ - "## Margins and ports\n", - "We automatically extend waveguides and adds margins during simulation but port locations remain fixed thus not affecting sparams. Port numbers map to gdsfactory component ports. Specify port pairs as eg `2,1` which is equivalent to `o2@0,o1@0` meaning optical_port_number@mode_number." - ] - }, - { - "cell_type": "markdown", - "id": "8", - "metadata": {}, - "source": [ - "## Reciprocity and symmetry\n", - "Usually only a subset of sparams is needed because of symmetry or reciprocity, specified as `keys` in sparams study or inferred automatically from inverse design study. We do a run for each port excitation. If `keys` is omitted in sparams study, all sparams will be calculated requiring more runs." - ] - }, - { - "cell_type": "markdown", - "id": "9", - "metadata": {}, - "source": [ - "## File workflow\n", - "Each simulation or design optimization run is saved to a folder named after `name` arg or (if unnamed) timestamp string. These run folders are inside working directory `wd=\"~/luminescent_runs\"` which can be modified by passing to `write_sparams` or `gcell_problem`. Can access saved runs via `load_solution()`, `load_solution()`, `finetune()` which act on latest modified run folder inside `wd` if `name` not specified. " - ] - }, - { - "cell_type": "markdown", - "id": "10", - "metadata": {}, - "source": [ - "# Installation\n", - "Backend CPU binaries at [releases](https://github.com/paulxshen/Luminescent.jl/releases) . GPU binaries can be requested\n", - "\n", - "## Windows\n", - "1. **Frontend**. Install Anaconda distribution of Python . Inside Anaconda Prompt do `pip install -U luminescent`\n", - "2. **Backend**. Unzip `LuminescentAI.zip` binaries. Add **where_you_unzipped**`\\LuminescentAI\\bin` to Path (using \"edit environmental variables\")\n", - " \n", - "## Linux\n", - "1. **Frontend**. `pip install -U luminescent`\n", - "2. **Backend**. TBD" - ] - }, - { - "cell_type": "markdown", - "id": "11", - "metadata": {}, - "source": [ - "# Tutorials: PIC S-parameters simulation" - ] - }, - { - "cell_type": "markdown", - "id": "12", - "metadata": {}, - "source": [ - "## waveguide bend" - ] - }, - { - "cell_type": "markdown", - "id": "13", - "metadata": {}, - "source": [ - "### 5um inner radius" - ] - }, - { - "cell_type": "markdown", - "id": "14", - "metadata": {}, - "source": [ - "#### Single wavelength 3D\n", - "We characterize insertion loss (IL) of waveguide bends wrt radius. We set R=5.0um, the often cited minimum radius in silicon photonics. First, we use `gdsfactory` to make the bend (note `gdsfactory` uses inner radius). Next, `write_sparams` simulates and saves results, which are retrieved and visualized by `load_solution`. \n", - "\n", - "`nres` is the maximum resolution - the number of points per wavelength in free space. The grid is adaptive so border regions actually use a lower resolution for speed. Remember FDTD scales as O(n^4)! `nres` has a huge effect on simulation time! Here, `nres=40` is accurate but can take half hour on CPU. `nres=20` is 16x faster and offers a reasonable estimate. Finally, Because of reciprocity we only need S or T param keys of \"2,1\". \n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "15", - "metadata": {}, - "outputs": [], - "source": [ - "import gdsfactory as gf\n", - "import luminescent as lumi\n", - "import numpy as np\n", - "\n", - "radius = 5\n", - "c = gf.components.bend_circular(radius=radius)\n", - "# c.plot()\n", - "\n", - "name = f\"bend_R{radius}\"\n", - "N = 3 # 3D or 2D\n", - "wavelengths = 1.55\n", - "keys = [\"2,1\"] # same as keys=[\"o2@0,o1@0\"]\n", - "nres = 40\n", - "\n", - "lumi.write_sparams(c, name=name, wavelengths=wavelengths, nres=nres, keys=keys, N=N)\n", - "sol = lumi.load_solution(name=name)" - ] - }, - { - "cell_type": "markdown", - "id": "16", - "metadata": {}, - "source": [ - "Loss is <-0.01dB, which is lower than reported by [Tidy3D example](https://www.flexcompute.com/tidy3d/examples/notebooks/EulerWaveguideBend/)" - ] - }, - { - "cell_type": "markdown", - "id": "17", - "metadata": {}, - "source": [ - "#### Wavelength range 3D\n", - "We can repeat the simulation with multiple wavelengths in a range. Closely spaced wavelengths may require longer integration periods to resolve.\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "18", - "metadata": {}, - "outputs": [], - "source": [ - "name = f\"bend_R{radius}_multi\"\n", - "wavelengths = np.linspace(1.5, 1.6, 5) # number or list or array\n", - "\n", - "lumi.write_sparams(c, name=name, wavelengths=wavelengths, nres=nres, keys=keys, N=N)\n", - "sol = lumi.load_solution(name=name)" - ] - }, - { - "cell_type": "markdown", - "id": "19", - "metadata": {}, - "source": [ - "#### Quickie 2.5D approximation \n", - "Set `N=2` to do a 2.5D simulation using (Ex, Ey, Hz) on a 2D plane with an \"effective\" refractive index integrated from a vertical modal profile. This is significantly faster but not accurate. Nevertheless it's a good way of exploring various geometries." - ] - }, - { - "cell_type": "markdown", - "id": "20", - "metadata": {}, - "source": [ - "### 1.5um inner radius\n", - "Let's try a tighter bend" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "21", - "metadata": {}, - "outputs": [], - "source": [ - "radius = 1.5\n", - "name = f\"bend_R{radius}\"\n", - "c = gf.components.bend_circular(radius=radius, allow_min_radius_violation=True)\n", - "\n", - "lumi.write_sparams(c, name=name, wavelengths=wavelengths, nres=nres, keys=keys, N=N)\n", - "sol = lumi.load_solution(name=name)" - ] - }, - { - "cell_type": "markdown", - "id": "22", - "metadata": {}, - "source": [ - "Loss increased to 0.11dB" - ] - }, - { - "cell_type": "markdown", - "id": "23", - "metadata": {}, - "source": [ - "## customizing materials, layers, and modes" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "24", - "metadata": {}, - "outputs": [], - "source": [] - }, - { - "cell_type": "markdown", - "id": "25", - "metadata": {}, - "source": [ - "#### Defaults" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "26", - "metadata": {}, - "outputs": [], - "source": [ - "import gdsfactory as gf\n", - "import luminescent as lumi\n", - "from gdsfactory.generic_tech import LAYER, LAYER_STACK\n", - "from luminescent import MATERIALS\n", - "\n", - "core_layer = LAYER.WG\n", - "bbox_layer = LAYER.WAFER\n", - "layer_stack = LAYER_STACK\n", - "materials = MATERIALS\n", - "\n", - "N = 3\n", - "dtype = \"float32\"\n", - "gpu = None" - ] - }, - { - "cell_type": "markdown", - "id": "27", - "metadata": {}, - "source": [ - "#### TE1" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "28", - "metadata": {}, - "outputs": [], - "source": [ - "c = gf.components.straight(length=1.0, width=0.5, layer=core_layer)\n", - "wavelengths = 1.55\n", - "nres = 30\n", - "\n", - "name = \"TE1\"\n", - "keys = [\"o2@1,o1@1\"]\n", - "lumi.write_sparams(\n", - " c,\n", - " name=name,\n", - " wavelengths=wavelengths,\n", - " keys=keys,\n", - " nres=nres,\n", - " N=N,\n", - " dtype=dtype,\n", - " gpu=gpu,\n", - ")\n", - "lumi.load_solution(name=name)" - ] - }, - { - "cell_type": "markdown", - "id": "29", - "metadata": {}, - "source": [ - "### Si rib waveguide\n", - "only need to modify bbox_layer to include 90nm bottom silicon slab" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "30", - "metadata": {}, - "outputs": [], - "source": [ - "name = \"rib\"\n", - "keys = [\"2,1\"]\n", - "bbox_layer = [LAYER.WAFER, LAYER.SLAB90]\n", - "lumi.write_sparams(\n", - " c,\n", - " name=name,\n", - " wavelengths=wavelengths,\n", - " keys=keys,\n", - " nres=nres,\n", - " N=N,\n", - " dtype=dtype,\n", - " gpu=gpu,\n", - " bbox_layer=bbox_layer,\n", - ")\n", - "sol = lumi.load_solution(name=name)" - ] - }, - { - "cell_type": "markdown", - "id": "31", - "metadata": {}, - "source": [ - "### SiN strip waveguide" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "32", - "metadata": {}, - "outputs": [], - "source": [ - "name = \"SiN\"\n", - "core_layer = LAYER.WGN\n", - "c = gf.components.straight(length=2.0, width=1.0, layer=core_layer)\n", - "\n", - "lumi.write_sparams(\n", - " c,\n", - " name=name,\n", - " wavelengths=wavelengths,\n", - " keys=keys,\n", - " nres=nres,\n", - " N=N,\n", - " dtype=dtype,\n", - " gpu=gpu,\n", - " core_layer=core_layer,\n", - ")\n", - "sol = lumi.load_solution(name=name)" - ] - }, - { - "cell_type": "markdown", - "id": "33", - "metadata": {}, - "source": [ - "# Tutorials: PIC generative inverse design\n", - "We introduce GCells (generative cells), a natural evolution of PCells (parametric cells) in semiconductor design . Given a set of inverse design objectives, a GCell will generate optimal geometry using adjoint optimization while ensuring manufacturability by enforcing minimum feature lengths.\n", - "\n", - "In examples below, `gcells.mimo` (multi in multi out) is just a gdsfactory component with configurable waveguide ports, simple slab as pre-optimization geometry, and overlying rectangular design regions. Dimensions `l` along x and `w` along y. Ports are numbered incrementally: west (SW->NW) -> east (SE->NE) -> south (SW->SE) -> north (NW->NE). By default, they're spaced equally on a side. Example: `west=1, east=2` places port 1 on west, ports 2 & 3 on east. But can also individually specify their locations and widths. Example : `west=[1.0, 2.5], wwg_west=[0.5, 0.4]`.\n", - "\n", - "Optimization `targets` is a dictionary organized wrt target type & wavelength. Types include T-params (`tparams`, most common), phase difference (`phasediff`), S-params (`sparams`). Multiple types & wavelengths are possible & often necessary . Loss for each type is scaled automatically to vary from 0 to 1 usually . For example , `tparams` loss of 0.5 roughly means 50% of power going to wrong places.\n", - "\n", - "`lvoid` is minimum length scale for voids. No void features smaller than it (currently this isn't exact - contact us for a more precise algorithm). Similarly for `lsolid`. \n", - "\n", - "`N=2` optimizes in 2.5D which serves as a fast initialization for 3D optimization. Examples done at low resolution and lax convergence in 2.5D on CPU. For accuracy, the result must be finetuned in 3D at finer resolution, a feature that can be requested from Luminescent AI .\n" - ] - }, - { - "cell_type": "markdown", - "id": "34", - "metadata": {}, - "source": [ - "## Generative cells PDK for passive devices \n" - ] - }, - { - "cell_type": "markdown", - "id": "35", - "metadata": {}, - "source": [ - "### mode converter\n", - "We target \"o2@1,o1@0\", converting optical port 1 mode TE0 input to optical port 2 mode TE1 output at 1.55um. Mode converters are notoriously hard to design, but generative AI does it in a pinch! " - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "36", - "metadata": {}, - "outputs": [], - "source": [ - "# recommended RAM: >16G\n", - "import luminescent as lumi\n", - "\n", - "name = \"mode_converter\" # can be any string\n", - "c = lumi.gcells.mimo(west=1, east=1, l=5.0, w=2.4, wwg=0.5, taper=0.05)\n", - "targets = {\"tparams\": {1.55: {\"o2@1,o1@0\": 1.0}}}\n", - "\n", - "prob = lumi.gcell_problem(\n", - " c,\n", - " targets,\n", - " name=name,\n", - " N=2,\n", - " nres=15,\n", - " lvoid=0.15,\n", - " lsolid=0.15,\n", - " iters=100,\n", - " stoploss=0.05,\n", - ")\n", - "lumi.solve(prob)" - ] - }, - { - "cell_type": "markdown", - "id": "37", - "metadata": {}, - "source": [ - "Can optionally optimize more using `finetune`" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "38", - "metadata": {}, - "outputs": [], - "source": [ - "# import luminescent as lumi\n", - "# lumi.finetune(iters=40,name=\"mode_converter\")" - ] - }, - { - "cell_type": "markdown", - "id": "39", - "metadata": {}, - "source": [ - "Lets see simulation fields and optimized geometry. Note the lax convergence criteria (`stoploss=.05`) means the solution isn't perfect." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "40", - "metadata": {}, - "outputs": [], - "source": [ - "name = \"mode_converter\"\n", - "sol = lumi.load_solution(name=name)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "41", - "metadata": {}, - "outputs": [], - "source": [] - }, - { - "cell_type": "markdown", - "id": "42", - "metadata": {}, - "source": [ - "### 1x2 splitter MMI\n", - "1.55um wavelength 1x2 splitter. We set symmetry about y so only need to specify T21=.5 as optimization target. Data saved to `name` folder inside working directory. We start iteratiions of adjoint optimization." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "43", - "metadata": {}, - "outputs": [], - "source": [ - "import luminescent as lumi\n", - "\n", - "name = \"splitter\"\n", - "c = lumi.gcells.mimo(\n", - " west=1,\n", - " east=2,\n", - " l=4.0,\n", - " w=2.0,\n", - " wwg=0.5,\n", - " taper=0.05,\n", - ")\n", - "targets = {\n", - " \"tparams\": {1.55: {\"2,1\": 0.5}},\n", - "}\n", - "\n", - "prob = lumi.gcell_problem(\n", - " c,\n", - " targets,\n", - " name=name,\n", - " N=2,\n", - " nres=15,\n", - " symmetries=[1],\n", - " lvoid=0.15,\n", - " lsolid=0.15,\n", - " iters=50,\n", - " stoploss=0.05,\n", - ")\n", - "lumi.solve(prob, run=False)" - ] - }, - { - "cell_type": "markdown", - "id": "44", - "metadata": {}, - "source": [] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "45", - "metadata": {}, - "outputs": [], - "source": [ - "name = \"splitter\"\n", - "sol = lumi.load_solution(name=name)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "46", - "metadata": {}, - "outputs": [], - "source": [] - }, - { - "cell_type": "markdown", - "id": "47", - "metadata": {}, - "source": [ - "### 1x4 splitter MMI\n", - "1x4 splitter at 1.55um with same phase on outputs. Because of symmetry, only need 0.0 phase difference between first 2 outputs." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "48", - "metadata": {}, - "outputs": [], - "source": [] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "49", - "metadata": {}, - "outputs": [], - "source": [ - "# finetune(iters=10,name=name)\n", - "sol = lumi.load_solution(name)" - ] - }, - { - "cell_type": "markdown", - "id": "50", - "metadata": {}, - "source": [ - "### wavelength domain demultiplexer" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "51", - "metadata": {}, - "outputs": [], - "source": [ - "# RAM: 32G\n", - "import luminescent as lumi\n", - "\n", - "name = \"demux\"\n", - "c = lumi.gcells.mimo(west=1, east=2, l=4.0, w=4.0, wwg=0.5)\n", - "targets = {\n", - " \"tparams\": {\n", - " 1.55: {\"2,1\": 1.0},\n", - " 1.20: {\"3,1\": 1.0},\n", - " }\n", - "}\n", - "\n", - "prob = lumi.gcell_problem(\n", - " c, targets, name=name, lvoid=0.15, lsolid=0.15, nres=15, N=2, iters=50\n", - ")\n", - "lumi.solve(prob)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "52", - "metadata": {}, - "outputs": [], - "source": [ - "name = \"demux\"\n", - "sol = lumi.load_solution(name)" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "id": "53", - "metadata": {}, - "source": [] - }, - { - "cell_type": "markdown", - "id": "54", - "metadata": {}, - "source": [ - "### symmetric crossing " - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "55", - "metadata": {}, - "outputs": [], - "source": [ - "import luminescent as lumi\n", - "\n", - "name = \"crossing\"\n", - "c = lumi.gcells.mimo(west=1, east=1, south=1, north=1, l=4.0, w=4.0, wwg=0.5)\n", - "targets = {\"tparams\": {1.55: {\"2,1\": 1.0}}}\n", - "\n", - "prob = lumi.gcell_problem(\n", - " c,\n", - " targets,\n", - " name=name,\n", - " lvoid=0.15,\n", - " lsolid=0.15,\n", - " nres=15,\n", - " symmetries=[0, 1],\n", - " N=2,\n", - " iters=40,\n", - ")\n", - "lumi.solve(prob)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "56", - "metadata": {}, - "outputs": [], - "source": [ - "sol = lumi.load_solution(name)" - ] - }, - { - "cell_type": "markdown", - "id": "57", - "metadata": {}, - "source": [ - "## GCells PDK for active devices" - ] - }, - { - "cell_type": "markdown", - "id": "58", - "metadata": {}, - "source": [ - "### Thermo-optic phase shifters \n", - "Please request " - ] - }, - { - "cell_type": "markdown", - "id": "59", - "metadata": {}, - "source": [ - "## GCells PDK for nonlinear devices\n", - "Please request " - ] - }, - { - "cell_type": "markdown", - "id": "60", - "metadata": {}, - "source": [ - "# Collaboration and research opportunities \n", - "We can publish or keep it proprietary, depending on your needs.\n", - "\n", - "Algorithms \n", - "- Reduced basis geometry generators for length scale controlled topology optimization\n", - "- Automatic differentiation and GPU compatible FDTD for inverse design in photonics and RF\n", - "- Algorithms for reduced memory usage in time domain adjoint optimization\n", - "- Neural surrogates for FDTD\n", - " \n", - "Photonic applications \n", - "- Energy efficient and compact photonic phase shifters and resonators\n", - "- Energy efficient and compact photonic modulators and MZIs\n", - "- Inverse design of nonlinear photonic devices\n", - "- Inverse design of ...\n" - ] - }, - { - "cell_type": "markdown", - "id": "61", - "metadata": {}, - "source": [ - "# Links\n", - "[GitHub](https://github.com/paulxshen/Luminescent.jl): Star us :) We respond to issues within a day \n", - "[LinkedIn](https://www.linkedin.com/company/luminescent-ai/about): Follow us for new features and bug fixes \n", - "[Company](luminescentai.com): Consulting, collaboration, publication opportunities available\n", - "Email: pxshen@alumni.stanford.edu info@luminescentai.com \n", - "WhatsApp: 650-776-7724 \n", - "WeChat: pxshen1230\n", - "" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "base", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.11.7" - } - }, - "nbformat": 4, - "nbformat_minor": 5 + "nbformat": 4, + "nbformat_minor": 0, + "metadata": { + "colab": { + "provenance": [] + }, + "kernelspec": { + "name": "python3", + "display_name": "Python 3" + }, + "language_info": { + "name": "python" + } + }, + "cells": [ + { + "cell_type": "markdown", + "source": [ + "Please refer to https://github.com/paulxshen/Luminescent.jl" + ], + "metadata": { + "id": "5mhpiXIxHkzQ" + } + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "SQgtuWsFHgYT" + }, + "outputs": [], + "source": [] + } + ] }