{
  "cells": [
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "# Inverse design overview\n",
        "\n",
        "*Effortlessly optimize complex photonic devices. Tidy3D integrates\n",
        "automatic differentiation with the efficient adjoint method, allowing\n",
        "you to compute gradients for thousands of parameters with just one extra\n",
        "simulation.*\n",
        "\n",
        "## **How Automatic Differentiation Works in Tidy3D**\n",
        "\n",
        "Tidy3D empowers users to perform gradient-based optimization and\n",
        "sensitivity analysis of photonic devices directly within their\n",
        "simulation workflow. This is achieved by making Tidy3D simulations\n",
        "**differentiable** – meaning we can efficiently compute the derivative\n",
        "(gradient) of a figure of merit (like transmission efficiency) with\n",
        "respect to any number of design parameters (like geometry dimensions or\n",
        "material properties).\n",
        "\n",
        "This capability relies on two core technologies working together:\n",
        "\n",
        "1.  **Automatic Differentiation (AD) Framework (`autograd`):** Handles\n",
        "    the differentiation of the overall Python code defining your\n",
        "    objective function.\n",
        "2.  **The Adjoint Method:** Provides an efficient way to calculate the\n",
        "    specific derivatives related to the FDTD simulation step itself.\n",
        "\n",
        "Let’s break down how these pieces fit together.\n",
        "\n",
        "### The Challenge: Differentiating Complex Simulations\n",
        "\n",
        "Optimizing photonic devices often involves finding the best set of\n",
        "design parameters (e.g., taper shape) that maximize or minimize a\n",
        "specific outcome (e.g., coupling efficiency). Gradient-based\n",
        "optimization methods are highly effective for this, but they require\n",
        "knowing how sensitive the outcome is to small changes in each parameter.\n",
        "\n",
        "Calculating this gradient for a complex FDTD simulation is challenging.\n",
        "A naive approach (like finite differences) would require running many\n",
        "simulations – one for each parameter – which quickly becomes\n",
        "computationally infeasible for designs with many variables.\n",
        "\n",
        "### Our Solution: Combining `autograd` and the Adjoint Method\n",
        "\n",
        "Tidy3D leverages the `autograd` library for automatic differentiation.\n",
        "When you write a Python function that defines your simulation setup,\n",
        "runs `tidy3d.web.run`, and calculates a final figure of merit,\n",
        "`autograd` can automatically track all the mathematical operations\n",
        "*outside* the simulation.\n",
        "\n",
        "<img src=\"img/autograd_overview_1.png\" width=\"700\" alt=\"Schematic of the inverse design\">\n",
        "\n",
        "However, `autograd` doesn’t inherently know how to differentiate\n",
        "*through* the FDTD simulation node. This is where Tidy3D’s custom\n",
        "integration comes in, using the **adjoint method**.\n",
        "\n",
        "We’ve essentially “taught” `autograd` the derivative rule for the\n",
        "`tidy3d.web.run` operation. This rule is implemented using the adjoint\n",
        "method, a powerful technique derived from electromagnetic theory.\n",
        "\n",
        "**Key Benefit of the Adjoint Method:** It allows us to compute the\n",
        "gradient of the figure of merit with respect to *all* design parameters\n",
        "using just **one additional simulation** (the adjoint simulation),\n",
        "regardless of how many parameters there are (hundreds, thousands, or\n",
        "even millions).\n",
        "\n",
        "### The Differentiation Pipeline: Forward and Backward Passes\n",
        "\n",
        "When you ask `autograd` to compute the gradient (e.g., using\n",
        "`autograd.grad(objective_function)`), here’s a simplified view of what\n",
        "happens under the hood:\n",
        "\n",
        "1.  **Forward Pass:**\n",
        "    - Your Python function executes normally.\n",
        "    - Tidy3D components (`td.Structure`, `td.Box`, `td.Medium`, etc.)\n",
        "      are created, potentially using `autograd` tracked numbers (from\n",
        "      `autograd.numpy`).\n",
        "    - `tidy3d.web.run(simulation)` is called. The standard **forward\n",
        "      FDTD simulation** runs.\n",
        "    - Crucially, during this forward run, Tidy3D automatically stores\n",
        "      necessary field information in the regions relevant to the\n",
        "      differentiable parameters (e.g., near the boundaries of a\n",
        "      shape-optimized geometry).\n",
        "    - The simulation results (`SimulationData`) are returned.\n",
        "    - Your function calculates the final scalar objective value using\n",
        "      `autograd.numpy` operations. `autograd` builds the computational\n",
        "      graph along the way.\n",
        "2.  **Backward Pass (Vector-Jacobian Product - VJP):**\n",
        "    - `autograd` starts propagating gradient information backward\n",
        "      through the computational graph using the chain rule.\n",
        "    - When it reaches the `td.web.run` node, Tidy3D’s custom VJP\n",
        "      function takes over.\n",
        "    - This function uses the gradient information flowing *into* it\n",
        "      (representing the sensitivity of the final objective to the\n",
        "      simulation’s outputs, like monitor data) to set up **adjoint\n",
        "      sources**.\n",
        "    - An **adjoint FDTD simulation** is automatically configured and\n",
        "      run.\n",
        "    - Tidy3D combines the stored fields from the **forward simulation**\n",
        "      with the results of the **adjoint simulation**.\n",
        "    - Using custom gradient rules, it efficiently calculates the partial\n",
        "      derivative of the objective function with respect to *every\n",
        "      single* tracked design parameter.\n",
        "    - These parameter gradients are packaged and returned to `autograd`.\n",
        "    - `autograd` continues the backward pass until it reaches the\n",
        "      original input parameters, yielding the final overall gradient.\n",
        "\n",
        "<img src=\"img/autograd_overview_2.png\" width=\"700\" alt=\"Schematic of the adjoint method\">\n",
        "\n",
        "### How Tidy3D Components Compute Gradients\n",
        "\n",
        "Different types of parameters require different specific calculations\n",
        "within the adjoint method framework. Tidy3D handles this internally:\n",
        "\n",
        "- **Shape Optimization:** For parameters defining geometry (e.g.,\n",
        "  `Box.center`, `Box.size`, `PolySlab.vertices`), gradients are\n",
        "  typically computed using *surface integrals* involving the forward and\n",
        "  adjoint fields on the boundaries of the shape. Moving a boundary\n",
        "  slightly changes the permittivity locally, and the adjoint method\n",
        "  quantifies the impact of this change on the objective.\n",
        "- **Material Optimization (Topology Optimization):** For parameters\n",
        "  defining material properties (e.g., the permittivity in each voxel of\n",
        "  a `CustomMedium`), gradients are computed using the forward and\n",
        "  adjoint fields within that material region.\n",
        "\n",
        "You don’t need to worry about these formulas – Tidy3D components like\n",
        "`td.Box`, `td.Cylinder`, `td.Medium`, `td.CustomMedium`, etc., have\n",
        "their differentiation logic built-in.\n",
        "\n",
        "### The User Experience: Seamless Integration\n",
        "\n",
        "The beauty of this approach is its simplicity from the user’s\n",
        "perspective:\n",
        "\n",
        "1.  Define your objective function in Python using standard Tidy3D\n",
        "    components and `autograd.numpy` for numerical operations.\n",
        "2.  Call `td.web.run()` within your function as usual.\n",
        "3.  Use `autograd.grad()` (or related functions like `value_and_grad`)\n",
        "    to get the gradient.\n",
        "\n",
        "Tidy3D and `autograd` handle the complex forward simulation, field\n",
        "storage, adjoint simulation setup, adjoint run, and final gradient\n",
        "calculation automatically behind the scenes. You get the efficiency of\n",
        "the adjoint method without needing to implement it yourself.\n",
        "\n",
        "### Conclusion\n",
        "\n",
        "Tidy3D’s integration with `autograd` via the adjoint method provides a\n",
        "powerful, flexible, and efficient platform for inverse design and\n",
        "sensitivity analysis. By defining a custom derivative rule for FDTD\n",
        "simulations, we unlock the ability to use gradient-based optimization on\n",
        "complex photonic design problems, requiring only one forward and one\n",
        "adjoint simulation per optimization step, regardless of the number of\n",
        "design parameters. This dramatically accelerates the design cycle for\n",
        "cutting-edge photonic devices.\n",
        "\n",
        "**Next Steps / Further Reading:**\n",
        "\n",
        "1.  [Topology optimization of a mode\n",
        "    converter](https://www.flexcompute.com/tidy3d/examples/notebooks/Autograd3InverseDesign/)\n",
        "2.  [Shape optimization of a waveguide\n",
        "    bend](https://www.flexcompute.com/tidy3d/examples/notebooks/Autograd8WaveguideBend/)\n",
        "3.  [Level set optimization of a\n",
        "    y-branch](https://www.flexcompute.com/tidy3d/examples/notebooks/Autograd10YBranchLevelSet/)\n",
        "4.  [Fabrication-aware inverse\n",
        "    design](https://www.flexcompute.com/tidy3d/examples/notebooks/Autograd23FabricationAwareInvdes/)"
      ],
      "id": "054a9bf0-85b1-45f0-a5b6-9c9532e28c4f"
    }
  ],
  "nbformat": 4,
  "nbformat_minor": 5,
  "metadata": {
    "kernelspec": {
      "display_name": "Python 3",
      "language": "python",
      "name": "python3"
    }
  }
}