Reciprocity Applied to Dipole Sources - 2. Far Fields¶ Jupyter Notebook
In this post, we will continue the series on modelling point dipole sources. In the previous post, we presented the reciprocity theorem and applied it to dipole sources that were close to each other. Here we will expand the discussion to dipoles that are separated by a large distance. The post is broken down in the following steps:
- Extension of reciprocity to the far field.
- Numerical validation of far field reciprocity with JCMsuite.
- Discussion of practical use cases for reciprocity.
Reciprocity in the Far Field¶
In the previous post in this series, we showed that reciprocity could be used to eliminate a dipole source at a given position by simulating three linearly independent dipoles at another position. At first glance, we have made life more complicated for ourselves, since now we have to solve Maxwell's equations for three sources instead of just one. However, we shall see that this can sometimes be extremely beneficial, particularly when the sources are separated by large distances.
We begin by considering two dipole sources $\mathrm{\mathbf{J}}_{1}$ and $\mathrm{\mathbf{J}}_{2}$ located at positions $\mathrm{\mathbf{r}}_{1}$ and $\mathrm{\mathbf{r}}_{2}$, respectively. The emission from the dipole sources creates the electric fields $\mathrm{\mathbf{E}}_{1}$ and $\mathrm{\mathbf{E}}_{2}$. We now move the second dipole (i.e. the position $\mathbf{r}_{2}$) further and further away from the first dipole (position $\mathbf{r}_{1}$), until only the far fields from $\mathrm{\mathbf{J}}_{1}$ reach position $\mathbf{r}_{2}$. For a discussion of the near and far fields of point dipole sources, see posts 1 & 2 in this serious on dipole sources.
The associated fields $\mathrm{\mathbf{E}_{1}(\mathbf{r}_{2})}$ are a contribution to the total emission into the far field from a dipole at $\mathbf{r}_{1}$. Due to the rapid decay of the radial field component, only transversal fields remain in the far field. The fields of dipole emitters far from their origin can be described as outgoing plane waves. This means that the field $\mathrm{\mathbf{E}_{1}(\mathbf{r}_{2})}$ is a plane wave.
We now turn the problem around by swapping the roles of source and observation position (i.e. applying reciprocity). The fields emitted from $\mathrm{\mathbf{j}_{2}(\mathbf{r}_{2})}$, when they reach the position $\mathbf{r}_{1}$ will be described via incoming plane waves. Since plane waves have no longitudinal component, we only need to illuminate with two linearly independent plane waves. A typical choice are s and p polarized plane waves.
Combining these concepts, we are now in a position to model the contribution to the total far field emission for a given angle due to a dipole source by exploiting reciprocity. We obtain this via illuminating the position where the dipole source would be with PlaneWave
sources in the sources.jcm
file. We then obtain the fields at the position $\mathbf{r}_{1}$ with the ExportFields
post process. By applying reciprocity to these fields, we obtain the field at $\mathbf{r}_{2}$ due to a source at $\mathbf{r}_{1}$.
To verify that we have the correct fields, we can also place a PointDipole
source at position $\mathbf{r}_{1}$ and use the FarField
post process to obtain the field at position $\mathbf{r}_{2}$. The reciprocity principle tells us that the results must be equivalent.
Far Field Example in JCMsuite¶
Load Data¶
First we define the dipole source current density vector that we wish to calculate the far field for. Then we load the precomputed data, this includes the outputs of the ExportFields
post process for the plane wave sources and the FarField
post process for the dipole source.
import os, sys
import numpy as np
sys.path.insert(0,os.path.join(os.getenv('JCMROOT'), 'ThirdPartySupport', 'Python'))
import jcmwave as jcm
# the current density vector
j1_r1 = np.array([1, 2, 3])
source_dir = "sources"
# load the efield results at r1 from the plane wave simulations
e_field = jcm.loadtable(os.path.join(source_dir, "isolated_dipole_far_field_via_reciprocity",
"plane_wave_source", "project_results", "efield.jcm"))
e2_r1_spol = e_field['ElectricFieldStrength'][0][0, :]
e2_r1_ppol = e_field['ElectricFieldStrength'][1][0, :]
# load the efield results at r2 from the far field simulations as a comparison
ff = jcm.loadtable(os.path.join(source_dir, "isolated_dipole_far_field_via_reciprocity",
"dipole_source", "project_results", "ff.jcm"))
e1_r2 = ff['ElectricFieldStrength'][0][0, :]
Normalisation Factor¶
Here we define a normalisation factor for the incident plane waves. In order to apply reciprocity, we need to know the fields at position $\mathbf{r}_{1}$ due to a dipole at $\mathbf{r}_{2}$. In the previous post in this series, we chose three linearly independent dipole polarisations for the dipole at $\mathbf{r}_{2}$, each with a current density strength of 1.
Instead of placing a dipole source at $\mathbf{r}_{2}$, we now simulate two plane waves with linearly independent polarisations and an incident amplitude of 1. The normalisation factor rescales the incident plane waves to have amplitudes as if they had been emitted by a dipole source at $\mathbf{r}_{2}$ with unity current density strength.
from scipy import constants
# normalisation factor
# this is necessary since we do not specify j2_r2 = [1, 0, 0] ... , instead we specify the incident
# plane wave amplitude to be 1 V/m. The normalisation factor converts the plane wave amplitude
# to the the efield amplitude of a dipole source at r2 with |j| = 1.
wvl = 4.5e-7 # (m)
omega = 2*np.pi*constants.c / wvl # rad/s
distance = 1000. # distance between r1 and r2 (m)
j0 = 1. # current density (A/m^2)
p0 = j0/(omega) # dipole moment (Cm)
phase_factor = 1j*np.exp(1j*omega*(distance)/constants.c) # phase shift when going from r2 to r1
amp_norm = p0*omega**2 *constants.mu_0 /(4*np.pi*(distance))*phase_factor #total amplitude normalisation factor
Calculate E1_r2 via reciprocity¶
Now we are the position to apply reciprocity. As a reminder, we show the reciprocity equation presented in the previous post.
\begin{equation} \mathrm{\mathbf{j}}_{1} \cdot \mathrm{\mathbf{E}}_{2}(\mathbf{r}_{1}) = \mathrm{\mathbf{j}}_{2} \cdot \mathrm{\mathbf{E}}_{1}(\mathbf{r}_{2}), \end{equation}Which relates the fields $\mathrm{\mathbf{E}}_{2}$ at position $\mathrm{\mathbf{r}}_{1}$ produced by a source $\mathrm{\mathbf{j}}_{2}$ at $\mathrm{\mathbf{r}}_{2}$ to the fields $\mathrm{\mathbf{E}}_{1}$ at position $\mathrm{\mathbf{r}}_{2}$ produced by a source $\mathrm{\mathbf{j}}_{1}$ at $\mathrm{\mathbf{r}}_{1}$.
This can be expanded to a set of equations for the individual components of $\mathrm{\mathbf{E}}_{1}$. Here we use the fact that the $s$ and $p$ polarised plane waves have been normalised to represent a dipole source at $\mathbf{r_{2}}$ with unity current density. Additionally, the $z$ component of the outgoing fields will be 0 since there is no radial field emission in the far field.
\begin{equation} \mathrm{E}_{1,x}(\mathbf{r}_{2}) = N|\mathrm{j}_{1,x}\mathrm{E}^{s}_{1,x} + \mathrm{j}_{1,y}\mathrm{E}^{s}_{1,y} + \mathrm{j}_{1,z}\mathrm{E}^{s}_{1,z}|_{(\mathbf{r}_{1})}, \\ \mathrm{E}_{1,y}(\mathbf{r}_{2}) = N|\mathrm{j}_{1,x}\mathrm{E}^{p}_{1,x} + \mathrm{j}_{1,y}\mathrm{E}^{p}_{1,y} + \mathrm{j}_{1,z}\mathrm{E}^{p}_{1,z}|_{(\mathbf{r}_{1})},\\ \mathrm{E}_{1,z}(\mathbf{r}_{2}) = 0. \end{equation}Which can be evaluated numerically and compared to the direct computation using the FarField
post process applied to a dipole source places at $\mathrm{\mathbf{r}}_{1}$.
e1_r2_xpol = np.dot(e2_r1_ppol*amp_norm, j1_r1)
e1_r2_ypol = np.dot(e2_r1_spol*amp_norm, j1_r1)
e1_r2_zpol = 0. #due to incident direction along z axis
e1_r2_recip = np.array([e1_r2_xpol, e1_r2_ypol, e1_r2_zpol])
# showing e2_r1 and e2_r1_recip are equivalent:
print(f"x component: \t\t\t{e1_r2[0]:.8e} \t norm: {np.abs(e1_r2[0]):.8e}")
print(f"x component reciprocal.: \t{e1_r2_recip[0]:.8e} \t norm: {np.abs(e1_r2_recip[0]):.8e}")
print(f"x component relative error: \t{np.abs(e1_r2[0]-e1_r2_recip[0])/np.abs(e1_r2[0]):.8e}")
print("###########")
print(f"y component: \t\t\t{e1_r2[1]:.8e} \t norm: {np.abs(e1_r2[1]):.8e}")
print(f"y component reciprocal: \t{e1_r2_recip[1]:.8e} \t norm: {np.abs(e1_r2_recip[1]):.8e}")
print(f"y component relative error: \t{np.abs(e1_r2[1]-e1_r2_recip[1])/np.abs(e1_r2[1]):.8e}")
print("###########")
print(f"z component: \t\t\t{e1_r2[2]:.8e}")
print(f"z component reciprocal: \t{e1_r2_recip[2]:.8e}")
print(f"z component relative error: \t{np.abs(e1_r2[2]-e1_r2_recip[2]):.8e}")
When to Use Reciprocity¶
Up to this point, we have used the reciprocity theorem to obtain the fields of a given source by simulating different sources. In the last example, we replaced a single dipole source with two plane wave sources. The advantage to doing this becomes clear if we consider simulating for multiple dipole sources simultaneously.
Consider the case in which we want to know the fields generated by a great number of dipole sources emitting incoherently with one another. This can occur when approximating the average response of a material with dipoles sources dispersed inside. An possible example would be the light emitted from LEDs. Say we wish to simulate the emission from dipoles placed at $N$ different positions. This requires setting up a JCMsuite simulation with $N$ separate sources. If $N$ becomes large, this can become computationally costly.
Now consider calculating the electric far field for those same $N$ dipoles using reciprocity. We have seen in the previous example that this involves illuminating the same volume of space that contains the $N$ dipole sources. We then evaluate the near field, using the ExportFields
post process, for each source (in this case just two) at all $N$ required positions. If the number of plane wave sources is much lower than the number of dipole sources, this can be a much more efficient process than simulating the dipole sources directly.
Generally speaking, in cases where the number of far field emission directions of interest is smaller than the number of dipole sources needed to evaluate, reciprocity can help speed up simulations. This is especially relevant for periodic dipoles. Periodic dipoles have a discrete number of far field directions into which they can emit. Therefore, there is an upper limit on the number of sources required to simulate them using reciprocity. For isolated dipoles, the far field directions are continuous. Depending on the numerical aperture and resolution of the angular emission spectrum required as well as the number of dipole sources, it may or may not make sense to apply reciprocity.
Limitations of Reciprocity¶
Reciprocity is typically used to obtain the emission into the far field in a more computationally efficient manner. Exploiting reciprocity does not provide the near fields of the dipole sources, only their emission into the far field. If the near fields of the dipoles are required, direct simulation of the dipoles is necessary.
If materials with absorption are present, this will violate the reciprocity principle. Although the emission into the far field will still be qualitatively modelled, the amount of light lost due to absorption will not be accounted for when modelling a system with absorption using reciprocity. This is a limitation if the losses due to absorption are also of interest.
Conclusion¶
The reciprocity theorem is a powerful tool for simplifying electromagnetic simulations. In particular, when the far fields of point dipoles is of interest. Reciprocity allows for the simulation of the dipole emission to the far field by illuminating a structure from the far field instead of using a dipole source. This has benefits when the number of dipole sources is much larger than the number of modes (i.e., propagation angles) in the far field.
In this post we discussed the following topics,
- Extension of the reciprocity principle to the far field.
- Numerical example of applying reciprocity in the far field using JCMsuite.
- A discussion of the advantages and limitations of applying reciprocity.
The next post in this series will go on to present a use case in which a huge speed up in simulation time is achieved applying reciprocity. This example will focus on simulations of periodic arrays of dipole sources.
Additional Resources¶
- Download a free trial version of JCMsuite.
- Documentation for dipole sources in JCMsuite can be found here.
- Documentation for the export field post process in JCMsuite can be found here
- Documentation for the far field post process in JCMsuite can be found here
- Mathematical Background of the Reciprocity Theorem