Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Tutorial example for computing the adjoint gradient of a level set #2792

Merged
merged 13 commits into from
Jun 28, 2024

Conversation

oskooi
Copy link
Collaborator

@oskooi oskooi commented Feb 27, 2024

Closes #2235.

In order to obtain early feedback, this is the first commit which currently just includes two explanatory figures and an image showing simulation output along with the simulation script in what will eventually become a new tutorial for the adjoint solver. Text and additional results are forthcoming.

@stevengj
Copy link
Collaborator

stevengj commented Feb 29, 2024

If you are willing to do finite difference, in principle you should be able to skip the subpixel smoothing entirely. You can instead do:

  1. Construct your level set function φ at a very high resolution for a given width h. Call this φ(h)
  2. Tell numpy to downsample (with bilinear interpolation) to a lower resolution, comparable to the Meep resolution. Call this ρ(φ(h)). This will "anti-alias" the boundaries. This is the material grid you input to Meep.
  3. Meep gives you the derivative with respect to ρ of your objective function. To get the derivative with respect to h, you just need a custom vJp that computes dρ/dh, e.g. by finite differences.

(You need high resolution for φ for finite differences to be accurate, since you can only change h by steps of one pixel in φ, unless you are willing to do your own subpixel averaging.)

@oskooi
Copy link
Collaborator Author

oskooi commented Mar 14, 2024

  1. Construct your level set function φ at a very high resolution for a given width h. Call this φ(h)

Unfortunately, even using this new approach (downsampling from a grid for the level set $\phi(h)$ that that has 10X the resolution of the Yee grid) produces similar results to what I had previously: good agreement in the directional derivative for the adjoint gradient and finite difference for the $\mathcal{S}$ but not the $\mathcal{P}$ polarization.

The test uses a resolution of 400 pixels/um which is fairly high. It is not obvious why the results for the $\mathcal{P}$ polarization are inaccurate.

1. $\mathcal{S}$ polarization

directional-deriv:, -0.00096108 (finite difference), -0.00090948 (adjoint), 0.053690 (error)

2. $\mathcal{P}$ polarization

directional-deriv:, -0.00015576 (finite difference), -0.00112504 (adjoint), 6.222962 (error)

Script used to generate these results is in this gist.

@stevengj
Copy link
Collaborator

When you carry out the chain rule, you multiply the Meep d/dρ with the level-set dρ/dh derivative. The latter is only nonzero on the boundary pixels. So, if the Meep derivative is inaccurate right on the boundary, that would be exacerbated by this d/dh derivative.

My hypothesis at the moment is that the p-polarization Meep derivatives are less accurate at the boundary (due to the the differentiate–then–discretize errors), since the p polarization has a discontinuity at the boundary, compared to the s polarization (which is continuous).

To check this, try checking the Meep d/dρ derivatives against finite differences, but instead of using a random perturbation to ρ, do a perturbation right at the the boundary pixels.

@oskooi
Copy link
Collaborator Author

oskooi commented Jun 16, 2024

My hypothesis at the moment is that the p-polarization Meep derivatives are less accurate at the boundary (due to the the differentiate–then–discretize errors), since the p polarization has a discontinuity at the boundary, compared to the s polarization (which is continuous).

To check this, try checking the Meep d/dρ derivatives against finite differences, but instead of using a random perturbation to ρ, do a perturbation right at the the boundary pixels.

The P polarization Meep derivatives are indeed less accurate at the boundaries compared to the S polarization. To verify this, I used the example of the usual 1D binary grating and added a 2-pixel perturbation for the height (see gist). Results are similar for a 1-pixel height perturbation.

As shown in the results below, the error in the directional derivative is small and decreases with resolution for the S polarization but not the P polarization.

We can explain this feature in this tutorial. For now, the tutorial is restricted to just the S polarization.

$\mathcal{S}$ polarization

RESOLUTION_UM = 50
dir-deriv:, -2.331625e-07 (finite difference), -2.371818e-07 (adjoint), 0.01723843268351378 (error)

RESOLUTION_UM = 100
dir-deriv:, -1.212851e-07 (finite difference), -1.212852e-07 (adjoint), 1.295385302514402e-06 (error)

$\mathcal{P}$ polarization

RESOLUTION_UM = 50
dir-deriv:, -1.200119e-07 (finite difference), -1.417497e-07 (adjoint), 0.18113050352310484 (error)

RESOLUTION_UM = 100
dir-deriv:, 1.785888e-08 (finite difference), 1.255981e-08 (adjoint), 0.2967190280473706 (error)

@oskooi
Copy link
Collaborator Author

oskooi commented Jun 19, 2024

The test failures are due to Autograd's dependence on deprecated Numpy features as reported (and fixed but not yet merged) in HIPS/autograd#618.

@stevengj
Copy link
Collaborator

Would be good to check the in-plane polarization for the $\beta = \infty$ subpixel smoothing as well.

Maybe there is a bug in what @smartalecH calls the "recombination" step (the overlap between adjoint and forward fields) that is lowering the accuracy.

@oskooi oskooi requested a review from stevengj June 21, 2024 13:49
@oskooi
Copy link
Collaborator Author

oskooi commented Jun 22, 2024

@stevengj, this is ready to be merged pending final review.

@stevengj stevengj merged commit 833e380 into NanoComp:master Jun 28, 2024
1 of 5 checks passed
@oskooi oskooi deleted the levelset_gradient_tutorial branch June 28, 2024 19:24
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Computing the gradient of a level-set function
2 participants