Diffractive splitter

Diffractive splitter#

The diffractive splitter challenge entails designing a metasurface that evenly splits a normally-incident plane wave into a 7x7 array of beams. Light is incident from the ambient, with the substrate and the metasurface pattern being silicon oxide. The operating wavelength is 732.8 nm, and the unit cell pitch is 7.2 microns, corresponding to diffraction angles of ±15 degrees. The challenge is based on “Design and rigorous analysis of a non-paraxial diffractive beamsplitter” slide deck retrieved from the LightTrans web site.

Simulating an existing design#

We’ll begin by loading, visualizing, and simulating existing designs extracted from LightTrans material (slide 12).

import matplotlib.pyplot as plt
import numpy as onp
from skimage import measure


def load_design(name):
    path = f"../../../reference_designs/diffractive_splitter/{name}.csv"
    return onp.genfromtxt(path, delimiter=",")


names = ["device1", "device2", "device3"]
designs = [load_design(name) for name in names]

plt.figure(figsize=(8, 4))
for i, design in enumerate(designs):
    ax = plt.subplot(1, 3, i + 1)
    im = ax.imshow(1 - design, cmap="gray")
    im.set_clim([-2, 1])
    contours = measure.find_contours(design)
    for c in contours:
        plt.plot(c[:, 1], c[:, 0], "k", lw=1)
    ax.set_xticks([])
    ax.set_yticks([])
../../_images/b430c17df5ca5dbea138e4c079361f6bca6615416e09db75f328b11f80d5993c.png
from invrs_gym.challenges.diffract import splitter_challenge

challenge = splitter_challenge.diffractive_splitter()

While several challenges involve only the design of two-dimensional patterns (with a Density2DArray being the optimization variable), the diffractive splitter degrees of freedom include both the metasurface pattern and several film thicknesses, in the form of a BoundedArray.

import jax

params = challenge.component.init(jax.random.PRNGKey(0))
for key, value in params.items():
    print(f"Variable {key}: {type(value)}")
Variable density: <class 'totypes.types.Density2DArray'>
Variable thickness_cap: <class 'totypes.types.BoundedArray'>
Variable thickness_grating: <class 'totypes.types.BoundedArray'>
Variable thickness_spacer: <class 'totypes.types.BoundedArray'>

We’ll simulate a reference design by overwriting the density entry in the params dict, leaving thicknesses unchanged. The default values match those from the LightTrans example. Then simulate using the component.response method.

import dataclasses

params["density"] = dataclasses.replace(params["density"], array=load_design("device1"))
response, aux = challenge.component.response(params)

Now let’s plot the diffraction efficiency for each order. We use the extract_orders_for_splitting function, and get the efficiency for a 9x9 array of beams (even though this design is for a 7x7 splitter). This will let us see how the diffraction efficiency drops off for orders beyond those targeted by the design.

plt.figure(figsize=(4, 3))

splitting = splitter_challenge.extract_orders_for_splitting(
    response.transmission_efficiency,
    response.expansion,
    splitting=(9, 9),
    polarization="TM",
)

ax = plt.subplot(111)
im = plt.imshow(splitting * 100, cmap="coolwarm")
ax.set_xticks(onp.arange(9))
ax.set_yticks(onp.arange(9))
ax.set_xticklabels(range(-4, 5))
ax.set_yticklabels(range(-4, 5))
plt.colorbar(im)
im.set_clim([0, onp.amax(splitting * 100)])
ax.set_title("device1\nDiffraction efficiency (%)")
_ = ax.set_ylim(ax.get_ylim()[::-1])
../../_images/5ae424d01b0eaa419adef5d67db418b927d9ecc0f6dce477710f3df5cd52a822.png

This device is not a particularly good one, as most of the power ends up in the zeroth order. This is reported also in the LightTrans material, and seen in the metrics we can compute using the challenge metrics method.

print("Challenge metrics:")
for key, value in challenge.metrics(response, params=params, aux=aux).items():
    print(f"    {key} = {value:.4f}")
Challenge metrics:
    binarization_degree = 1.0000
    total_efficiency = 0.7062
    average_efficiency = 0.0144
    min_efficiency = 0.0082
    zeroth_order_efficiency = 0.0749
    zeroth_order_error = 4.2005
    uniformity_error = 0.8023
    uniformity_error_without_zeroth_order = 0.3975

Let’s take a look at the remaining devices, which have higher reported performance.

plt.figure(figsize=(8, 3))
for i, name in enumerate(["device2", "device3"]):
    params["density"] = dataclasses.replace(params["density"], array=load_design(name))
    response, aux = challenge.component.response(params)

    splitting = splitter_challenge.extract_orders_for_splitting(
        response.transmission_efficiency,
        response.expansion,
        splitting=(9, 9),
        polarization="TM",
    )

    ax = plt.subplot(1, 2, i + 1)
    im = plt.imshow(splitting * 100, cmap="coolwarm")
    ax.set_xticks(onp.arange(9))
    ax.set_yticks(onp.arange(9))
    ax.set_xticklabels(range(-4, 5))
    ax.set_yticklabels(range(-4, 5))
    plt.colorbar(im)
    im.set_clim([0, onp.amax(splitting * 100)])
    ax.set_title(f"{name}\nDiffraction efficiency (%)")
    ax.set_ylim(ax.get_ylim()[::-1])
../../_images/0e075aee61e947d91d00b55d989b067f504a9b933a7686dbf2a4d3d9e839d435.png