-
Notifications
You must be signed in to change notification settings - Fork 632
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 radiation pattern of a dipole using 1D calculation and Brillouin-zone integration #2917
base: master
Are you sure you want to change the base?
Conversation
Codecov ReportAll modified and coverable lines are covered by tests ✅
❗ Your organization needs to install the Codecov GitHub app to enable full functionality. Additional details and impacted files@@ Coverage Diff @@
## master #2917 +/- ##
==========================================
- Coverage 73.81% 73.71% -0.10%
==========================================
Files 18 18
Lines 5423 5449 +26
==========================================
+ Hits 4003 4017 +14
- Misses 1420 1432 +12
|
You need to take into account |
(Note that you might run into an issue with the 1d PML, because when you are right on the light line the fields won't decay.) |
Here are the equations for the S- and P-polarized farfields given a current sheet oscillating in |
from typing import Tuple
import matplotlib.pyplot as plt
import numpy as np
def spherical_to_cartesian(
polar_rad,
azimuthal_rad
) -> Tuple[float, float, float]:
"""Converts a point in spherical to Cartesian coordinates."""
rx = np.sin(polar_rad) * np.cos(azimuthal_rad)
ry = np.sin(polar_rad) * np.sin(azimuthal_rad)
rz = np.cos(polar_rad)
return rx, ry, rz
if __name__ == "__main__":
data = np.load("dipole_farfields.npz")
farfield_radius_um = data['FARFIELD_RADIUS_UM']
polar_rad = data['polar_rad']
azimuthal_rad = data['azimuthal_rad']
num_polar = data['NUM_POLAR']
num_azimuthal = data['NUM_AZIMUTHAL']
flux_x = np.zeros((num_polar, num_azimuthal))
flux_y = np.zeros((num_polar, num_azimuthal))
flux_z = np.zeros((num_polar, num_azimuthal))
xs = np.zeros((num_polar, num_azimuthal))
ys = np.zeros((num_polar, num_azimuthal))
zs = np.zeros((num_polar, num_azimuthal))
for i in range(num_polar):
for j in range(num_azimuthal):
flux_x[i, j] = np.real(
np.conj(data['Ey'][i, j]) * data['Hz'][i, j] -
np.conj(data['Ez'][i, j]) * data['Hy'][i, j]
)
flux_y[i, j] = np.real(
np.conj(data['Ez'][i, j]) * data['Hx'][i, j] -
np.conj(data['Ex'][i, j]) * data['Hz'][i, j]
)
flux_z[i, j] = np.real(
np.conj(data['Ex'][i, j]) * data['Hy'][i, j] -
np.conj(data['Ey'][i, j]) * data['Hx'][i, j]
)
flux_r = np.sqrt(flux_x[i, j]**2 + flux_y[i, j]**2 + flux_z[i, j]**2)
rx, ry, rz = spherical_to_cartesian(polar_rad[i], azimuthal_rad[j])
xs[i, j] = rx * flux_x[i, j]
ys[i, j] = ry * flux_y[i, j]
zs[i, j] = rz * flux_z[i, j]
fig, ax = plt.subplots(subplot_kw={"projection": "3d"}, figsize=(6, 6))
ax.plot_surface(xs, ys, zs, cmap="inferno")
ax.set_title("radiation pattern in 3d")
ax.set_box_aspect((np.amax(xs), np.amax(ys), np.amax(zs)))
ax.set_zlabel("z flux (a.u.)", labelpad=30)
ax.tick_params(axis="z", which="major", pad=15)
ax.set(xticklabels=[], yticklabels=[])
fig.savefig(
"radiation_pattern_3d.png",
dpi=150,
bbox_inches="tight",
) |
I think you want use xs[i, j] = rx
ys[i, j] = ry
zs[i, j] = rz
flux_r[i,j] = rx * flux_x[i, j] + ry * flux_y[i, j] + rz * flux_z[i, j] with the surface given by c = matplotlib.cm.magma(flux_r)
plot_surface(xs,ys,zs, facecolors=c) or just
|
…nd add missing rotation of farfield points
I fixed a couple of bugs in the calculation of the far fields. However, even with these fixes the radiation pattern for At this point, it might be worth investigating whether setting up the 3D simulation using a 1D grid itself is bug free (e.g., see #2916). import math
from typing import Tuple
import matplotlib
import matplotlib.pyplot as plt
import numpy as np
if __name__ == "__main__":
data = np.load("dipole_farfields.npz")
polar_rad = data['polar_rad']
num_polar = data['NUM_POLAR']
flux_x = np.zeros(num_polar)
flux_z = np.zeros(num_polar)
for i in range(num_polar):
flux_x[i] = np.real(
np.conj(data['Ey'][i, 0]) * data['Hz'][i, 0] -
np.conj(data['Ez'][i, 0]) * data['Hy'][i, 0]
)
flux_z[i] = np.real(
np.conj(data['Ex'][i, 0]) * data['Hy'][i, 0] -
np.conj(data['Ey'][i, 0]) * data['Hx'][i, 0]
)
flux_r = np.sqrt(np.square(flux_x) + np.square(flux_z))
np.savez(
"dipole_radiation_pattern.npz",
flux_r=flux_r,
polar_rad=polar_rad,
)
fig, ax = plt.subplots(subplot_kw={"projection": "polar"}, figsize=(6, 6))
ax.plot(polar_rad, flux_r / np.max(flux_r), 'b-', label="Meep")
ax.plot(polar_rad, np.square(np.cos(polar_rad)), 'r--', label="cos$^2$(θ)")
ax.set_theta_direction(-1)
ax.set_theta_offset(0.5 * math.pi)
ax.set_thetalim(0, 0.5 * math.pi)
ax.set_rmax(1)
ax.set_rticks([0,0.5,1])
ax.grid(True)
ax.set_rlabel_position(22)
ax.legend()
fig.savefig(
"radiation_pattern_jx.png",
dpi=150,
bbox_inches="tight",
) |
""" | ||
if current_component.name == "Jx": | ||
# Jx --> (Ex, Hy, Ez) [P pol.] | ||
ex0 = frequency * current_amplitude / (2 * kz) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ex0 = frequency * current_amplitude / (2 * kz) | |
ex0 = kz * current_amplitude / (2 * frequency) |
for i in range(NUM_POLAR): | ||
for j in range(NUM_AZIMUTHAL): | ||
# Map the rotated point to the closest azimuthal angle. | ||
azimuthal_index = (j + azimuthal_index_shift) % NUM_AZIMUTHAL |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
shifting the index seems wrong. the point index hasn't changed in this process, you only rotated the fields and r.
The results for the radiation pattern of a dipole in vacuum are improved after making these two modifications to the script:
The radiation pattern of the dipole computed using the Brillouin-zone integration is now similar to the analytic result. However, the results are not yet identical. The discrepancy should go away when using a finer and finer grid for the Brillouin-zone integration. This is something I am currently investigating. |
|
Increasing This explains why |
There are numerical methods (Levin, 1994) to integrate fast-oscillating integrals like this accurately and efficiently, though I can't find any implementation in Python. However, in this case we really only care about the I suspect that the result in the limit will be proportional to just integrating the powers from each |
…h grid point in the far field via Brillouin-zone integration
After reorganizing the calculation to compute the radial Poynting flux The key feature of computing For reference, here is the plotting script used to generate the figure above using the output of the simulation data ( import math
from typing import Tuple
import matplotlib.pyplot as plt
import numpy as np
AZIMUTHAL_INDEX = 0
if __name__ == "__main__":
data = np.load("dipole_farfields.npz")
polar_rad = data["polar_rad"]
poynting_flux = data["poynting_flux"][:, AZIMUTHAL_INDEX]
fig, ax = plt.subplots(subplot_kw={"projection": "polar"}, figsize=(6, 6))
ax.plot(polar_rad, poynting_flux / np.max(poynting_flux), 'b-', label="Meep")
ax.plot(polar_rad, np.cos(polar_rad), 'r--', label="cos(θ)")
ax.plot(polar_rad, np.square(np.cos(polar_rad)), 'g--', label="cos$^2$(θ)")
ax.set_theta_direction(-1) # Theta increases in the clockwise direction
ax.set_theta_offset(0.5 * math.pi)
ax.set_thetalim(0, 0.5 * math.pi)
ax.set_rmax(1)
ax.set_rticks([0, 0.5, 1])
ax.grid(True)
ax.set_rlabel_position(22)
ax.legend()
fig.savefig(
"poynting_flux_jx.png",
dpi=150,
bbox_inches="tight",
) |
Note that for the small-R calculation you could just take the far-field point (orange), subtract the source point (red) in Cartesian coordinates, then convert to spherical to get |
Denote the angle of the orange dot To get As a sanity check, when |
Here is a plot of the analytic expression for To generate a range of [0, π/2] for |
For |
There seems to be an inconsistency when calculating the radiation profile of an Why is the radiation profile of a dipole different when computing the result in cylindrical vs. Cartesian coordinates: Simulation layout of a a dipole at |
Closes #2866.
This is still a work in progress. This implementation is based on the procedure described in #2866 (comment) for computing the radiation pattern of a dipole in vacuum using a 1D calculation and Brillouin-zone integration.
The radiation pattern of a dipole should be similar to a "donut" with rotational symmetry around the axis defined by the dipole orientation. However, as shown in the image below, the radiation pattern generated by this script is rotationally symmetric around the$z$ axis. This seems to be incorrect because the dipole is oriented in the $x$ direction.
For reference, the script used to generate the radiation pattern from the farfield data produced as the output from the script is provided below.