Modeling Antennas

Under Development

Too see plots run in google colab: https://colab.research.google.com/github/casangi/sirius/blob/main/docs/antenna_beams.ipynb

Load Packages

[1]:
import os
try:
    import sirius
    print('SiRIUS version',sirius.__version__,'already installed.')
except ImportError as e:
    print(e)
    print('Installing SiRIUS')
    os.system("pip install sirius")
    import sirius
    print('SiRIUS version',sirius.__version__,' installed.')

import pkg_resources
import xarray as xr
import numpy as np
from ipywidgets import interactive, fixed
from sirius.display_tools import display_J, display_M
from sirius.calc_beam import make_mueler_mat

from astropy.coordinates import SkyCoord
xr.set_options(display_style="html")
import os
try:
    from google.colab import output
    output.enable_custom_widget_manager()
    IN_COLAB = True
except:
    IN_COLAB = False
%matplotlib widget
#%matplotlib inline
SiRIUS version 0.0.22 already installed.

Voltage Pattern and Primary Beam

The Voltage Pattern (VP) is the image plane response of a single antenna and the Primary Beam (PB) is the image plane response of pair of antennas (baselines) and is given by:

\[PB = VP \odot VP^*\]

where \(^*\) is the complex conjugate and \(\odot\) is the Hadamard product (element wise product).

\(^T\) transpose. \(^H\) Hermitian or complex conjugate transpose.

Aperture illumination function (AIP)

[2]:
from sirius.calc_beam import calc_beam, calc_zpc_beam
import numpy as np
from sirius_data.beam_1d_func_models.airy_disk import aca, alma, vla
print(vla)

#freq_chan = np.array([60*10**9,90*10**9,100*10**9,120*10**9])
freq_chan = np.array([90*10**9])
#freq_chan = np.array([3.052*10**9])
beam_parms = {}
J_xds = calc_beam(alma,freq_chan,beam_parms,check_parms=True)
#J_xds = calc_beam(vla,freq_chan,beam_parms,check_parms=True)
J_xds
{'func': 'casa_airy', 'dish_diam': 24.5, 'blockage_diam': 0.0, 'max_rad_1GHz': 0.014946999714079439}
Setting default fov_scaling  to  4.0
Setting default mueller_selection  to  [ 0  5 10 15]
Setting default zernike_freq_interp  to  nearest
Setting default pa_radius  to  0.2
Setting default image_size  to  [1000 1000]
[1000 1000] [-1.38385217e-06  1.38385217e-06] 4.0 0.03113667385557884
[2]:
<xarray.Dataset>
Dimensions:  (chan: 1, pa: 1, pol: 1, l: 1000, m: 1000)
Coordinates:
  * chan     (chan) int64 90000000000
  * pa       (pa) int64 0
  * pol      (pol) int64 0
  * l        (l) float64 0.0006919 0.0006905 0.0006892 ... -0.0006892 -0.0006905
  * m        (m) float64 -0.0006919 -0.0006905 ... 0.0006892 0.0006905
Data variables:
    J        (pa, chan, pol, l, m) float64 0.01527 0.01577 ... 0.0168 0.0163
[3]:
from sirius.display_tools import display_J

interactive_plot_1 = interactive(display_J, J_xds=fixed(J_xds), pa=fixed(0), chan=(0,J_xds.dims['chan']-1), val_type=['abs','phase','real','imag'],units=['rad','arcsec','arcmin','deg'])
output_1 = interactive_plot_1.children[-1]
output_1.layout.auto_scroll_threshold = 9999;
interactive_plot_1

EVLA Polynomial Model

[4]:
%load_ext autoreload
%autoreload 2
[5]:
pc_dir = pkg_resources.resource_filename('sirius_data', 'beam_polynomial_coefficient_models/data/EVLA_.bpc.zarr')
pc_xds = xr.open_zarr(pc_dir,consolidated=False)

pc_xds = pc_xds.where(pc_xds.band=='S',drop=True)
#pc_xds = pc_xds.isel(chan=[36,37])
print(pc_xds.band.values)
print(pc_xds.chan.values)
pc_xds

['S' 'S' 'S' 'S' 'S' 'S' 'S' 'S' 'S' 'S' 'S' 'S' 'S' 'S' 'S']
[2.052e+09 2.180e+09 2.436e+09 2.564e+09 2.692e+09 2.820e+09 2.948e+09
 3.052e+09 3.180e+09 3.308e+09 3.436e+09 3.564e+09 3.692e+09 3.820e+09
 3.948e+09]
[5]:
<xarray.Dataset>
Dimensions:    (chan: 15, pol: 1, coef_indx: 5)
Coordinates:
    band       (chan) <U1 dask.array<chunksize=(15,), meta=np.ndarray>
  * chan       (chan) float64 2.052e+09 2.18e+09 ... 3.82e+09 3.948e+09
  * coef_indx  (coef_indx) int64 0 1 2 3 4
  * pol        (pol) int64 5
Data variables:
    BPC        (chan, pol, coef_indx) float64 dask.array<chunksize=(15, 1, 5), meta=np.ndarray>
    ETA        (chan, pol, coef_indx) float64 dask.array<chunksize=(15, 1, 5), meta=np.ndarray>
Attributes:
    conversion_date:  2022-01-27
    dish_diam:        25
    max_rad_1GHz:     0.014946999714079439
    pc_file_name:     EVLA_.txt
    telescope_name:   EVLA
[6]:
from sirius.calc_beam import calc_bpc_beam

J_xds = calc_bpc_beam(pc_xds,pc_xds.chan.values,beam_parms={})
J_xds
Setting default fov_scaling  to  4.0
Setting default mueller_selection  to  [ 0  5 10 15]
Setting default zernike_freq_interp  to  nearest
Setting default pa_radius  to  0.2
Setting default image_size  to  [1000 1000]
cell_size  [-2.91364517e-05  2.91364517e-05]
[6]:
<xarray.Dataset>
Dimensions:  (chan: 15, pa: 1, pol: 1, l: 1000, m: 1000)
Coordinates:
  * chan     (chan) float64 2.052e+09 2.18e+09 2.436e+09 ... 3.82e+09 3.948e+09
  * pa       (pa) int64 0
  * pol      (pol) int64 0
  * l        (l) float64 0.01457 0.01454 0.01451 ... -0.01448 -0.01451 -0.01454
  * m        (m) float64 -0.01457 -0.01454 -0.01451 ... 0.01448 0.01451 0.01454
Data variables:
    J        (pa, chan, pol, l, m) float64 0.0 0.0 0.0 0.0 ... 0.0 0.0 0.0 0.0
[7]:
from sirius.display_tools import display_J

interactive_plot_1 = interactive(display_J, J_xds=fixed(J_xds), pa=fixed(0), chan=(0,J_xds.dims['chan']-1), val_type=['abs','phase','real','imag'],units=['rad','arcsec','arcmin','deg'])
output_1 = interactive_plot_1.children[-1]
output_1.layout.auto_scroll_threshold = 9999;
interactive_plot_1

Jones Matrix

Sky Jones matrix as a function of direction is given by

\[\begin{split}\textbf{J}_i^{sky} (\textbf{s}) = \begin{bmatrix} J^p_i & -J^{p \rightarrow q}_i \\ J_i^{q \rightarrow p} & J^{q}_i \end{bmatrix}\end{split}\]

Simplified case of the Airy disk (real valued and symmetric)

\[\begin{split}\textbf{J}_i^{sky} (\textbf{s}) = \begin{bmatrix} J^p_i & 0 \\ 0 & J^{p}_i \end{bmatrix}\end{split}\]

No leakage and p=q.

SiRIUS stores Jones matrices row wise

\[\begin{split}\begin{bmatrix} 0 & 1 \\ 2 & 3 \end{bmatrix}\end{split}\]
[8]:
import pkg_resources

zpc_dir = pkg_resources.resource_filename('sirius_data', 'aperture_polynomial_coefficient_models/data/EVLA_avg_zcoeffs_SBand_lookup.apc.zarr')
zpc_xds = xr.open_zarr(zpc_dir,consolidated=False)
freq_chan = zpc_xds.chan.values#np.array([2.1*10**9,2.4*10**9,2.8*10**9,3.4*10**9])

zpc_xds
[8]:
<xarray.Dataset>
Dimensions:    (chan: 16, pol: 4, coef_indx: 66)
Coordinates:
  * chan       (chan) float64 2.052e+09 2.18e+09 ... 3.844e+09 3.972e+09
  * coef_indx  (coef_indx) int64 0 1 2 3 4 5 6 7 8 ... 58 59 60 61 62 63 64 65
  * pol        (pol) int64 5 6 7 8
Data variables:
    ETA        (chan, pol, coef_indx) float64 dask.array<chunksize=(16, 4, 66), meta=np.ndarray>
    ZPC        (chan, pol, coef_indx) complex128 dask.array<chunksize=(16, 4, 66), meta=np.ndarray>
Attributes:
    apc_file_name:    EVLA_avg_zcoeffs_SBand_lookup.csv
    conversion_date:  2022-01-27
    dish_diam:        25
    max_rad_1GHz:     0.014946999714079439
    telescope_name:   EVLA
[9]:
beam_parms={}
beam_parms['mueller_selection'] = np.arange(16)
J_xds_zpc = calc_zpc_beam(zpc_xds,np.array([0,np.pi/4,np.pi*2/4,np.pi*3/4,np.pi]),freq_chan,beam_parms)
J_xds_zpc
Setting default fov_scaling  to  4.0
Setting default zernike_freq_interp  to  nearest
Setting default pa_radius  to  0.2
Setting default image_size  to  [1000 1000]
[9]:
<xarray.Dataset>
Dimensions:  (chan: 16, pa: 5, pol: 4, l: 1000, m: 1000)
Coordinates:
  * chan     (chan) float64 2.052e+09 2.18e+09 2.308e+09 ... 3.844e+09 3.972e+09
  * pa       (pa) float64 0.0 0.7854 1.571 2.356 3.142
  * pol      (pol) int64 5 6 7 8
  * l        (l) float64 0.01457 0.01454 0.01451 ... -0.01448 -0.01451 -0.01454
  * m        (m) float64 -0.01457 -0.01454 -0.01451 ... 0.01448 0.01451 0.01454
Data variables:
    J        (pa, chan, pol, l, m) complex128 (-0.12695041266544269-0.0096647...
[10]:
interactive_plot_1 = interactive(display_J, J_xds=fixed(J_xds_zpc), pa=(0,J_xds_zpc.dims['pa']-1), chan=(0,J_xds_zpc.dims['chan']-1), val_type=['abs','phase','real','imag'],units=['rad','arcsec','arcmin','deg'])
output_1 = interactive_plot_1.children[-1]
output_1.layout.auto_scroll_threshold = 9999;
interactive_plot_1

Mueller Matrix

\[\textbf{M}^{sky}_{ij}(\textbf{s}) = \textbf{J}_i^{sky}(\textbf{s}) \otimes \textbf{J}_j^{sky}(\textbf{s})\]

SiRIUS stores Jones matrices row wise

\[\begin{split}\begin{bmatrix} 0 & 1 & 2 & 3 \\ 4 & 5 & 6 & 7 \\ 8 & 9 & 10 & 11 \\ 12 & 13 & 14 & 15 \end{bmatrix}\end{split}\]
[11]:
#Het array support
#make_mueler_mat(J_xds1, J_xds2, mueller_selection)

M_xds = make_mueler_mat(J_xds_zpc,J_xds_zpc,beam_parms['mueller_selection']) #np.array([ 0, 5, 10, 15])
M_xds
(5, 16, 4, 1000, 1000) (5, 16, 4, 1000, 1000)
[11]:
<xarray.Dataset>
Dimensions:  (chan: 16, pa: 5, m_sel: 16, l: 1000, m: 1000)
Coordinates:
  * chan     (chan) float64 2.052e+09 2.18e+09 2.308e+09 ... 3.844e+09 3.972e+09
  * pa       (pa) float64 0.0 0.7854 1.571 2.356 3.142
  * m_sel    (m_sel) int64 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
  * l        (l) float64 0.01457 0.01454 0.01451 ... -0.01448 -0.01451 -0.01454
  * m        (m) float64 -0.01457 -0.01454 -0.01451 ... 0.01448 0.01451 0.01454
    pol1     (m_sel) float64 5.0 5.0 6.0 6.0 5.0 5.0 ... 8.0 8.0 7.0 7.0 8.0 8.0
    pol2     (m_sel) float64 5.0 6.0 5.0 6.0 7.0 8.0 ... 5.0 6.0 7.0 8.0 7.0 8.0
Data variables:
    M        (pa, chan, m_sel, l, m) complex128 (0.01620981518531084+0j) ... ...
[12]:
interactive_plot_1 = interactive(display_M, M_xds=fixed(M_xds), pa=(0,J_xds_zpc.dims['pa']-1), chan=(0,J_xds_zpc.dims['chan']-1), val_type=['abs','phase','real','imag'],units=['rad','arcsec','arcmin','deg'])
output_1 = interactive_plot_1.children[-1]
output_1.layout.auto_scroll_threshold = 9999;
interactive_plot_1
[13]:
import matplotlib.pyplot as plt
plt.figure()
plt.imshow(np.real(M_xds.M[0,0,0,:,:]))
plt.show()
_images/antenna_beams_20_0.png
[14]:
%load_ext autoreload
%autoreload 2
The autoreload extension is already loaded. To reload it, use:
  %reload_ext autoreload