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

Reformulate Simulation.get_sources() and add 2D GaussianBeam #2333

Merged
merged 10 commits into from
Dec 15, 2022
Merged

Reformulate Simulation.get_sources() and add 2D GaussianBeam #2333

merged 10 commits into from
Dec 15, 2022

Conversation

hammy4815
Copy link
Contributor

@hammy4815 hammy4815 commented Dec 8, 2022

First, I reformulated get_sources. Before, simulation defined logic for each source type separately with if isinstance(source, GaussianBeam), elif... and then provided the specific logic to add the source parameters to the backend.

Now, it simply loops through the list of sources belonging to the simulation and calls add_source on each, passing in self to give the source access to the simulation. Source.py now contains the base logic for adding sources, but EigenModeSource, IndexedSource, GaussianBeamSource, and (new) GaussianBeam2DSource overload this logic.

In addition, a new class GaussianBeamSource2D is created. This allows a rigorous solution to Maxwell's equations in 2-dimensions via the complex point source method (similar to 3D). All the logic for finding the solution is provided in the python alone, finding the field at a slice. This is done very similar to green2d in near2far.cpp by using Hankel functions of both kinds to generate forward and backward propagating beams when the point source is complex in location. Using this slice, equivalent sources are calculated (also in python).

Once the equivalent sources are made (python meep objects), GaussianBeam2dSource.add_source() simply calls add_source() on these equivalent source objects. The fields are normalized to have $|E|=1$ at the location of the point source.

This is all done in python. Because I wrote a green2d python function and one for equivalent source (for single frequency), we can run our code once in python to generate the sources at the beginning of the run and then pass this information in to the backend before the actual meep runs start. Thus, we do not need to write our code in c++ the way 3D was done. Here is a demonstration of it working, under different conditions.

Screen Shot 2022-12-07 at 9 32 00 PM

We can change our plane and provide rotations of k, which is done by rotating the imaginary component of the location of the complex point source between x and y.

Screen Shot 2022-12-07 at 9 32 42 PM

We can also change our polarization. In order for this to work, an E source and H source are placed in the same location oriented 90 degrees from each other, similar to what's done for 3D.

Screen Shot 2022-12-07 at 9 33 19 PM

@codecov-commenter
Copy link

codecov-commenter commented Dec 8, 2022

Codecov Report

Merging #2333 (830aeea) into master (9473d31) will increase coverage by 0.55%.
The diff coverage is 92.89%.

@@            Coverage Diff             @@
##           master    #2333      +/-   ##
==========================================
+ Coverage   71.99%   72.55%   +0.55%     
==========================================
  Files          17       17              
  Lines        5032     5152     +120     
==========================================
+ Hits         3623     3738     +115     
- Misses       1409     1414       +5     
Impacted Files Coverage Δ
python/simulation.py 76.62% <66.66%> (-0.19%) ⬇️
python/source.py 94.19% <93.37%> (-1.12%) ⬇️

@oskooi oskooi self-requested a review December 8, 2022 03:18
Copy link
Collaborator

@oskooi oskooi left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for putting this together.

It would be useful to add a unit test for this new feature, perhaps similar to the existing test for GaussianBeamSource in https://github.com/NanoComp/meep/blob/master/python/tests/test_gaussianbeam.py.


class GaussianBeamSource(Source):
"""
This is a subclass of `Source` and has **all of the properties** of `Source` above. However, the `component` parameter of the `Source` object is ignored. The [Gaussian beam](https://en.wikipedia.org/wiki/Gaussian_beam) is a transverse electromagnetic mode for which the source region must be a *line* (in 2d) or *plane* (in 3d). For a beam polarized in the $x$ direction with propagation along $+z$, the electric field is defined by $\\mathbf{E}(r,z)=E_0\\hat{x}\\frac{w_0}{w(z)}\\exp\\left(\\frac{-r^2}{w(z)^2}\\right)\\exp\\left(-i\\left(kz + k\\frac{r^2}{2R(z)}\\right)\\right)$ where $r$ is the radial distance from the center axis of the beam, $z$ is the axial distance from the beam's focus (or "waist"), $k=2\\pi n/\\lambda$ is the wavenumber (for a free-space wavelength $\\lambda$ and refractive index $n$ of the homogeneous, lossless medium in which the beam propagates), $E_0$ is the electric field amplitude at the origin, $w(z)$ is the radius at which the field amplitude decays by $1/e$ of its axial values, $w_0$ is the beam waist radius, and $R(z)$ is the radius of curvature of the beam's wavefront at $z$. The only independent parameters that need to be specified are $w_0$, $E_0$, $k$, and the location of the beam focus (i.e., the origin: $r=z=0$).

(In 3d, we use a ["complex point-source" method](https://doi.org/10.1364/JOSAA.16.001381) to define a source that generates an exact Gaussian-beam solution. In 2d, we currently use the simple approximation of taking a cross-section of the 3d beam. In both cases, the beam is most accurate near the source's center frequency.)
(In 3d, we use a ["complex point-source" method](https://doi.org/10.1364/JOSAA.16.001381) to define a source that generates an exact Gaussian-beam solution. In 2d, we currently use the simple approximation of taking a cross-section of the 3d beam. In both cases, the beam is most accurate near the source's center frequency.) To use the true solution for a 2d Gaussian Beam, use the `GaussianBeam2DSource` class instead.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Given the new GuassianBeam2DSource class, it might be appropriate to rename GaussianBeamSource to GaussianBeam3DSource to clearly distinguish the two?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This makes sense to me. My only worry is will people struggle to realize the name change when they next update meep and their old scripts fail to recognize GaussianBeamSource?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

they next update meep and their old scripts fail to recognize GaussianBeamSource

I agree. We should probably avoid any breaking changes for something like this... especially since this is preliminary. A future PR will add support for multiple frequencies etc.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In other words, we shouldn't need to break the API at all. In the future, we can refactor and call GaussianBeamSource in either 2D or 3D and it just "does what it's supposed to do".

Right now that's a challenge due to the complex Hankel functions etc which aren't (readily) compatible with the current 3D codebase in C++.

python/source.py Outdated Show resolved Hide resolved
python/source.py Show resolved Hide resolved
@stevengj
Copy link
Collaborator

stevengj commented Dec 8, 2022

The most backward-compatible option would be something like:

  1. Rename the existing GaussianBeamSource to GaussianBeam3DSource.
  2. Define a compatibility wrapper called GaussianBeamSource which calls GaussianBeam3DSource. But if the simulation is in 2d, then this wrapper will print a deprecation warning noting that in the future it will be changed to call GaussianBeam2DSource in the 2d case — current users should explicitly call GaussianBeam3DSource if they want to keep the old behavior, or call GaussianBeam2DSource if they want the new (better) behavior.
  3. In a future Meep version, change GaussianBeamSource to call GaussianBeam2DSource in 2d, with no warning.

@hammy4815
Copy link
Contributor Author

Performed the following changes:

  1. Refactored GaussianBeamSource -> GaussianBeam3DSource and created a wrapper GaussianBeamSource that warns the user when running a 2D sim
  2. Pulled get_equiv_sources out as a standalone function to be used
  3. Added 2D to the gaussianbeam test case. We can still add more complex test cases for both 3D and 2D if we wish such as ensuring the beam waist is correct.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants