
Holography Visualization Tutorial¶
This visualization tutorial uses the same dataset that is used on the VLA tutorial. If the VLA tutorial has been previously run on the same directory the data will be re-utilized.
[1]:
import os
try:
import astrohack
print("AstroHACK version", astrohack.__version__, "already installed.")
except ImportError as e:
print(e)
print("Installing AstroHACK")
os.system("pip install astrohack")
import astrohack
print("astrohack version", astrohack.__version__, " installed.")
AstroHACK version 1.0.1 already installed.
[2]:
# Convenience functions:
import glob
def list_directory_files(path):
try:
for entry in glob.glob(path):
print(entry)
except FileNotFoundError:
print(f"{path} not found.")
def display_text_file(filename, nlines=None):
with open(filename, "r") as file:
if nlines is None:
for line in file:
print(line[:-1])
else:
head = file.readlines()[0:nlines]
print("".join(head))
Download Tutorial Data and spawning an Astrohack Client¶
If the VLA data cannot be found in the specified directory it will be downloaded. The Astrohack client is spawned so that parallel processing is possible further down.
[3]:
import toolviper
from toolviper.dask.client import local_client
ms_name = "data/ea25_cal_small_after_fixed.split.ms"
toolviper.utils.data.download(file="ea25_cal_small_after_fixed.split.ms", folder="data")
parallel = True
if parallel:
client = local_client(cores=4, memory_limit="4GB")
print(client)
print(client.dashboard_link)
else:
client = None
[2026-03-19 15:39:32,539] INFO astrohack: Module path: /export/home/arya/miniforge3/envs/casadev/lib/python3.12/site-packages/toolviper
[2026-03-19 15:39:32,543] INFO astrohack: Downloading from [cloudflare] ....
Download List ───────────────────────────────────── ea25_cal_small_after_fixed.split.ms
[2026-03-19 15:39:32,547] INFO astrohack: File exists: data/ea25_cal_small_after_fixed.split.ms
[2026-03-19 15:39:32,710] INFO astrohack: Module path: /export/home/arya/miniforge3/envs/casadev/lib/python3.12/site-packages/toolviper
[2026-03-19 15:39:32,713] WARNING astrohack: It is recommended that the local cache directory be set using the dask_local_dir parameter.
[2026-03-19 15:39:33,614] INFO astrohack: Client <MenrvaClient: 'tcp://127.0.0.1:33363' processes=4 threads=4, memory=14.90 GiB>
<MenrvaClient: 'tcp://127.0.0.1:33363' processes=4 threads=4, memory=14.90 GiB>
http://127.0.0.1:8787/status
Extract_pointing: checking pointing data¶
The point_mds object created by extract_pointing or loaded into memory by open_pointing contains the pointing information grouped by antenna under a single level data tree.
[4]:
import os
from astrohack import open_pointing, extract_pointing
point_name = "data/ea25_cal_small_after_fixed.split.point.zarr"
if os.path.exists(point_name):
point_mds = open_pointing(point_name)
else:
point_mds = extract_pointing(ms_name=ms_name, point_name=point_name, parallel=parallel, overwrite=True)
The contents and capabilities of a point_mds can be glanced at by using the summary method, and the contents can be accessed directly with the usage of keys.
[5]:
point_exports = "point_exports"
point_mds.summary()
################################################################################
### Summary for: ###
### data/ea25_cal_small_after_fixed.split.point.zarr ###
################################################################################
Data origin:
creation_time: 2026-03-19 15:38:32 MDT
creator_function: extract_pointing
origin: astrohack
version: 1.0.1
Input Parameters:
+------------+--------------------------------------------------+
| Parameter | Value |
+------------+--------------------------------------------------+
| exclude | None |
| ms_name | data/ea25_cal_small_after_fixed.split.ms |
| overwrite | True |
| parallel | True |
| point_name | data/ea25_cal_small_after_fixed.split.point.zarr |
+------------+--------------------------------------------------+
Available methods:
+------------------------------+------------------------------------------+
| Methods | Description |
+------------------------------+------------------------------------------+
| add_node | Add a node to the data tree file |
| | structure, however this node is not yet |
| | consolidated into the data tree |
| | structure, consolidate must be called to |
| | integrate all nodes writen by add_node |
| | onto the tree structure. |
| consolidate | Traverse own file structure on disk |
| | consolidating metadata to create a |
| | unified data tree entity. |
| create_from_input_parameters | Create an AstrohackBaseFile object from |
| | a filename and initializes xdtree root |
| | attributes. |
| is_close_to | Tests if self and other_mds are close to |
| | each other. |
| items | Get children items |
| keys | Get children keys |
| open | Open Base file. |
| plot_array_configuration | Plot antenna positions. |
| plot_pointing_in_time | Plot Pointing for antennas in time. |
| set_antennas_as_reference | Modify point_mds data to make specific |
| | antennas reference antennas, useful for |
| | older datasets that contain |
| | wrong pointing data for reference |
| | antennas. |
| summary | Prints summary of this Astrohack File |
| | object, with available data, attributes |
| | and methods |
| values | Get children values |
| write | Write mds to disk by saving the data |
| | tree to a file |
+------------------------------+------------------------------------------+
Data Contents:
+----------+
| Antenna |
+----------+
| ant_ea04 |
| ant_ea06 |
| ant_ea25 |
+----------+
[6]:
point_mds['ant_ea25']
[6]:
<xarray.DataTree 'ant_ea25'>
Group: /ant_ea25
Dimensions: (time: 113326, az_el: 2, lm: 2)
Coordinates:
* time (time) float64 907kB 5.17e+09 5.17e+09 ... 5.17e+09
Dimensions without coordinates: az_el, lm
Data variables:
DIRECTION (time, az_el) float64 2MB dask.array<chunksize=(113326, 2), meta=np.ndarray>
DIRECTIONAL_COSINES (time, lm) float64 2MB dask.array<chunksize=(113326, 2), meta=np.ndarray>
ENCODER (time, az_el) float64 2MB dask.array<chunksize=(113326, 2), meta=np.ndarray>
POINTING_OFFSET (time, az_el) float64 2MB dask.array<chunksize=(113326, 2), meta=np.ndarray>
TARGET (time, az_el) float64 2MB dask.array<chunksize=(113326, 2), meta=np.ndarray>
Attributes:
antenna_info: {'latitude': 0.5916721356565446, 'longitude': -1...
mapping_scans_obs_dict: [{'ddi_0': {'map_0': [8, 9, 10, 12, 13, 14, 16, ...Inspecting pointing data¶
The plot_pointing_in_time method of the point_mds object is useful to compare the different pointing data variables inside each antenna pointing dataset. This comparison can be done with multiple antennas to try to identify problems, such as when a reference antenna has the wrong pointing table. It is also possible to plot each antenna in an individual plot if that is desired.
[7]:
point_mds.plot_pointing_in_time(
point_exports,
pointing_key='DIRECTIONAL_COSINES',
plot_antennas_separately=False,
display=True,
time_scale=[0.03, 3.2],
az_scale=[-1, 1],
el_scale=[-1, 1])
[2026-03-19 15:39:33,749] INFO astrohack: Module path: /export/home/arya/work/Holography-1022/astrohack/src/astrohack
Glancing at the array configuration¶
When reducing holography data it might be interesting to have a look at the array configuration, specially to choose reference antennas. The point_mds object offers a glance of the array configuration by the usage of the plot_array_configuration method.
[8]:
point_mds.plot_array_configuration(
point_exports,
display=True,
box_size=50
)
[2026-03-19 15:39:40,655] INFO astrohack: Module path: /export/home/arya/work/Holography-1022/astrohack/src/astrohack
Extract_holog: checking data calibration¶
Astrohack requires the measurement set (MS) that is fed into extract_holog to be properly calibrated in CASA. To check that calibration is sound, a method has been implemented in the holog_mds class that plots the visibilities at the center of the beam.
Run extract_holog or open a holog file¶
Prior to plotting the diagnostic plots it is necessary to create a holog_mds object (holography Multi DataSet). This Object can be created by running extract_holog, or by opening the data products of extract_holog with the function open_holog. Here extract_holog is executed if a previously produced .holog.zarr file is not found.
[9]:
from astrohack import open_holog, extract_holog
holog_name = "data/ea25_cal_small_after_fixed.split.holog.zarr"
if os.path.exists(holog_name):
holog_mds = open_holog(holog_name)
else:
holog_mds = extract_holog(
ms_name=ms_name,
point_name=point_name,
holog_name=holog_name,
data_column="CORRECTED_DATA",
parallel=parallel,
overwrite=True,
)
Generate holography dictionary¶
The function extract_holog creates a fully formed observation dictionary by default but can also take a user specified observation dictionary via the input parameter, holog_obs_dict. To help with this the user function generate_holo_obs_dict is provided to generate and return the full dictionary to the user for subselection and modification.
[10]:
from astrohack import generate_holog_obs_dict
holog_obs_dict = generate_holog_obs_dict(
point_name=point_name,
parallel=parallel,
)
Inspect holography dictionary¶
Generally the printing a nested dictionary such as the holog_obs_dict is a messy, ugly affair and for this reason a .print method has been added to the holog_obs_dict object. This method has two modes:
static mode - Static mode is a formatted string output and allows the user to define how the antenna dictionary list is handled. By adjusting the columns input variable the user can for instance take a scan list of length of 15 and display it as 3 rows of 5 columns (this would be columns=5). The reshaping code will look at the list and reshape it even if the length is not an exact split, i.e. length =17 with columns=5 would give 4 rows as output with the last row being the remainders.
dynamic mode - Dynamic mode returns a collapsible json dictionary in the cell so that the user can look at only certain parts of large holog dictionaries.
[11]:
holog_obs_dict.print(style="static")
[15:39:41] { holog_obs_dict.py:45 'ddi_0': { 'map_0': { 'scans': [ 8, 9, 10, 12, 13, 14, 16, 17, 18, 23, 24, 25, 27, 28, 29, 31, 32, 33, 38, 39, 40, 42, 43, 44, 46, 47, 48, 53, 54, 55, 57 ], 'ant': {'ea06': ['ea04'], 'ea25': ['ea04']} } }, 'ddi_1': { 'map_0': { 'scans': [ 8, 9, 10, 12, 13, 14, 16, 17, 18, 23, 24, 25, 27, 28, 29, 31, 32, 33, 38, 39, 40, 42, 43, 44, 46, 47, 48, 53, 54, 55, 57 ], 'ant': {'ea06': ['ea04'], 'ea25': ['ea04']} } } }
[12]:
holog_obs_dict.print(style="dynamic")
[12]:
<IPython.core.display.JSON object>
Now that we have a holog_mds object we define a variable holog_exports with the name of the directory to contain our diagnostic plots. We can also inspect our holog_mds object by calling its summary method. This method lists the attributes of the holog_mds, such as the input MS and the inputs given by the user, but more important for us going forward, it also gives us a summary of the data contents in the holog_mds and its available methods, here we are going to use
the plot_diagnostics and plot_lm_sky_coverage methods.
[13]:
holog_exports = "holog_exports"
holog_mds.summary()
################################################################################
### Summary for: ###
### data/ea25_cal_small_after_fixed.split.holog.zarr ###
################################################################################
Data origin:
creation_time: 2026-03-19 15:38:33 MDT
creator_function: extract_holog
origin: astrohack
version: 1.0.1
Input Parameters:
+-------------------------------+--------------------------------------------------+
| Parameter | Value |
+-------------------------------+--------------------------------------------------+
| ant | all |
| append | False |
| baseline_average_distance | all |
| baseline_average_nearest | 1 |
| data_column | CORRECTED_DATA |
| ddi | all |
| exclude_antennas | None |
| holog_name | data/ea25_cal_small_after_fixed.split.holog.zarr |
| holog_obs_dict | None |
| map | all |
| ms_name | data/ea25_cal_small_after_fixed.split.ms |
| overwrite | True |
| parallel | True |
| point_name | data/ea25_cal_small_after_fixed.split.point.zarr |
| pointing_interpolation_method | linear |
| time_smoothing_interval | None |
+-------------------------------+--------------------------------------------------+
Available methods:
+------------------------------+-----------------------------------------------+
| Methods | Description |
+------------------------------+-----------------------------------------------+
| add_node | Add a node to the data tree file structure, |
| | however this node is not yet consolidated |
| | into the data tree structure, |
| | consolidate must be called to integrate all |
| | nodes writen by add_node onto the tree |
| | structure. |
| consolidate | Traverse own file structure on disk |
| | consolidating metadata to create a unified |
| | data tree entity. |
| create_from_input_parameters | Create an AstrohackBaseFile object from a |
| | filename and initializes xdtree root |
| | attributes. |
| export_to_aips | Export data compatible to AIPS's HOLOG task |
| is_close_to | Tests if self and other_mds are close to each |
| | other. |
| items | Get children items |
| keys | Get children keys |
| observation_summary | Create a Summary of observation information |
| open | Open Base file. |
| plot_diagnostics | Plot diagnostic calibration plots from the |
| | holography data file. |
| plot_lm_sky_coverage | Plot directional cosine coverage. |
| summary | Prints summary of this Astrohack File object, |
| | with available data, attributes and methods |
| values | Get children values |
| write | Write mds to disk by saving the data tree to |
| | a file |
+------------------------------+-----------------------------------------------+
Data Contents:
+----------+-------+-----------+
| Antenna | DDI | Mapping |
+----------+-------+-----------+
| ant_ea06 | ddi_0 | ['map_0'] |
| ant_ea06 | ddi_1 | ['map_0'] |
| ant_ea25 | ddi_0 | ['map_0'] |
| ant_ea25 | ddi_1 | ['map_0'] |
+----------+-------+-----------+
Calibration diagnostic plots¶
Below is a call to plot_diagnostics, we limit our plotting to only DDI 0 of antenna ea25 for simplicity. The plot shown here displays the Amplitudes and phases for RR and LL correlations at the beam center for all channels. With this plot we want to make sure that the Amplitudes remain close to 1, and that the phase remains close to 0. The data displayed here is well calibrated but displays the Gibbs phenomenon at the band edges which is normal.
[14]:
holog_mds.plot_diagnostics(
holog_exports, # Directory to contain the plots
ant="ea25", # Plotting antenna ea25
ddi=0, # Plotting DDI 0
map_id="all", # Plotting all mappings
complex_split="polar", # Split complex data to polar (amplitude + phase)
display=True, # Display results in Notebook cell
parallel=False, # Don't plots in parallel
)
[2026-03-19 15:39:41,175] INFO astrohack: Module path: /export/home/arya/work/Holography-1022/astrohack/src/astrohack
Below we call plot_diagnostics again but this time over all the antennas and DDIs in the holog_mds. As before the created plots are stored in holog_exports but this time they are not displayed for brevity. All the files created by plot_diagnostics can be seen just after its execution.
[15]:
holog_mds.plot_diagnostics(
holog_exports, # Directory to contain the plots
ant="all", # Plotting all antennas
ddi="all", # Plotting all DDIs
map_id="all", # Plotting all mappings
complex_split="cartesian", # Split complex data to cartesian (real + imaginary)
display=False, # don't display results in Notebook cell
parallel=parallel, # Do plots in parallel
)
# All exported files
print("\nCreated files:")
list_directory_files(holog_exports + "/holog_diagnostics*.png")
[2026-03-19 15:39:43,739] INFO astrohack: Module path: /export/home/arya/work/Holography-1022/astrohack/src/astrohack
Created files:
holog_exports/holog_diagnostics_ant_ea25_ddi_0_map_0.png
holog_exports/holog_diagnostics_ant_ea06_ddi_1_map_0.png
holog_exports/holog_diagnostics_ant_ea06_ddi_0_map_0.png
holog_exports/holog_diagnostics_ant_ea25_ddi_1_map_0.png
Directional cosines sky coverage¶
Below is a call to plot_lm_sky_coverage, we limit our plotting to only DDI 0 of antenna ea25 for simplicity. The plot shown here displays the sky coverage of the L and M axes. Also displayed are the Amplitudes and phases for the RR correlation as a function of the L, M and time axes. These plots help us in identifying problems with the pointing data in the MS, and or problems in the visibility data that vary with the L, M or time axes.
[16]:
holog_mds.plot_lm_sky_coverage(
holog_exports, # Directory to contain the plots
ant="ea25", # Plotting antenna ea25
ddi=0, # Plotting DDI 0
map_id="all", # Plotting all mappings
angle_unit="deg", # Unit for L and M axes
time_unit="hour", # Time unit
plot_correlation="RR", # Which correlation to be plotted
complex_split="polar", # Split complex data to polar (Amplitude + phase)
phase_unit="deg", # Unit for phase
display=True, # Display plots bellow
parallel=False, # Do not do plots in parallel
)
[2026-03-19 15:39:49,523] INFO astrohack: Module path: /export/home/arya/work/Holography-1022/astrohack/src/astrohack
Below we call plot_lm_sky_coverage again but this time over all the antennas and DDIs in the holog_mds. As before the created plots are stored in holog_exports but this time they are not displayed for brevity. All the files created by plot_lm_sky_coverage can be seen just after its execution.
[17]:
holog_mds.plot_lm_sky_coverage(
holog_exports, # Directory to contain the plots
ant="all", # Plotting all antennas
ddi="all", # Plotting all DDIs
map_id="all", # Plotting all mappings
angle_unit="deg", # Unit for L and M axes
time_unit="hour", # Time unit
plot_correlation="all", # Plot all correlations
complex_split="polar", # Split complex data to cartesian (real + imaginary)
phase_unit="deg", # Unit for phase
display=False, # Do not display plots bellow
parallel=parallel, # Do plots in parallel
)
# All exported files
print("\nCreated files:")
list_directory_files(holog_exports + "/holog_directional_cosines*.png")
[2026-03-19 15:39:52,488] INFO astrohack: Module path: /export/home/arya/work/Holography-1022/astrohack/src/astrohack
Created files:
holog_exports/holog_directional_cosines_ant_ea25_ddi_0_map_0.png
holog_exports/holog_directional_cosines_RR_ant_ea25_ddi_0_map_0.png
holog_exports/holog_directional_cosines_ant_ea06_ddi_1_map_0.png
holog_exports/holog_directional_cosines_ant_ea25_ddi_1_map_0.png
holog_exports/holog_directional_cosines_ant_ea06_ddi_0_map_0.png
holog_exports/holog_directional_cosines_RR_ant_ea06_ddi_1_map_0.png
holog_exports/holog_directional_cosines_RR_ant_ea06_ddi_0_map_0.png
holog_exports/holog_directional_cosines_RL_ant_ea25_ddi_0_map_0.png
holog_exports/holog_directional_cosines_RR_ant_ea25_ddi_1_map_0.png
holog_exports/holog_directional_cosines_RL_ant_ea06_ddi_1_map_0.png
holog_exports/holog_directional_cosines_RL_ant_ea06_ddi_0_map_0.png
holog_exports/holog_directional_cosines_RL_ant_ea25_ddi_1_map_0.png
holog_exports/holog_directional_cosines_LR_ant_ea25_ddi_0_map_0.png
holog_exports/holog_directional_cosines_LR_ant_ea06_ddi_0_map_0.png
holog_exports/holog_directional_cosines_LR_ant_ea06_ddi_1_map_0.png
holog_exports/holog_directional_cosines_LL_ant_ea25_ddi_0_map_0.png
holog_exports/holog_directional_cosines_LR_ant_ea25_ddi_1_map_0.png
holog_exports/holog_directional_cosines_LL_ant_ea06_ddi_0_map_0.png
holog_exports/holog_directional_cosines_LL_ant_ea06_ddi_1_map_0.png
holog_exports/holog_directional_cosines_LL_ant_ea25_ddi_1_map_0.png
Holog: Checking beam and aperture quality and exporting data to FITS files¶
After we are sure about the quality of the calibrated data in the MS we want to make sure that the beam is well sampled and behaved and that the apertures are well characterized and have enough resolution. For that we are going to use the methods of the image_mds object, i.e. the data product of the holog function.
Run holog or open image file¶
Like before with the .holog.zarr file, if the .image.zarr is available we simply open it with open_image, otherwise we run holog to obtain our image_mds object.
[18]:
import os
from astrohack import holog, open_image
image_name = "data/ea25_cal_small_after_fixed.split.image.zarr"
if os.path.exists(image_name):
image_mds = open_image(image_name)
else:
image_mds = holog(
holog_name=holog_name,
image_name=image_name,
overwrite=True,
phase_fit_engine="perturbations",
to_stokes=True,
parallel=parallel,
)
Now that we have a image_mds object we define a variable called image_exports with the name of the directory to contain our plots and exported FITS files. We can also inspect our image_mds object by calling its summary method. Like for the holog_mds object, this method lists the attributes of the image_mds, such as the input .holog.zarr file and the inputs given by the user, like before we are actually interested on the data contents and the available methods. For
this tutorial the relevant methods are plot_beams, plot_apertures and export_to_fits.
[19]:
image_exports = "image_exports"
image_mds.summary()
################################################################################
### Summary for: ###
### data/ea25_cal_small_after_fixed.split.image.zarr ###
################################################################################
Data origin:
creation_time: 2026-03-19 15:38:41 MDT
creator_function: holog
origin: astrohack
version: 1.0.1
Input Parameters:
+-------------------------+--------------------------------------------------+
| Parameter | Value |
+-------------------------+--------------------------------------------------+
| alma_osf_pad | None |
| ant | all |
| cell_size | None |
| chan_average | True |
| chan_tolerance_factor | 0.005 |
| ddi | all |
| grid_interpolation_mode | gaussian |
| grid_size | None |
| holog_name | data/ea25_cal_small_after_fixed.split.holog.zarr |
| image_name | data/ea25_cal_small_after_fixed.split.image.zarr |
| overwrite | True |
| padding_factor | 10 |
| parallel | True |
| phase_fit_control | [True, True, True, True, True] |
| phase_fit_engine | perturbations |
| scan_average | True |
| to_stokes | True |
| zernike_n_order | 4 |
+-------------------------+--------------------------------------------------+
Available methods:
+------------------------------+-----------------------------------------------+
| Methods | Description |
+------------------------------+-----------------------------------------------+
| add_node | Add a node to the data tree file structure, |
| | however this node is not yet consolidated |
| | into the data tree structure, |
| | consolidate must be called to integrate all |
| | nodes writen by add_node onto the tree |
| | structure. |
| consolidate | Traverse own file structure on disk |
| | consolidating metadata to create a unified |
| | data tree entity. |
| create_from_input_parameters | Create an AstrohackBaseFile object from a |
| | filename and initializes xdtree root |
| | attributes. |
| export_phase_fit_results | Export perturbations phase fit results from |
| | the data in an AstrohackImageFIle object to |
| | ASCII files. |
| export_to_fits | Export contents of an AstrohackImageFile |
| | object to several FITS files in the |
| | destination folder |
| export_zernike_fit_results | Export Zernike coefficients from the data in |
| | an AstrohackImageFIle object to ASCII files. |
| is_close_to | Tests if self and other_mds are close to each |
| | other. |
| items | Get children items |
| keys | Get children keys |
| observation_summary | Create a Summary of observation information |
| open | Open Base file. |
| plot_apertures | Aperture amplitude and phase plots from the |
| | data in an AstrohackImageFIle object. |
| plot_beams | Beam plots from the data in an |
| | AstrohackImageFIle object. |
| plot_zernike_model | Plot Zernike models from the data in an |
| | AstrohackImageFile object. |
| summary | Prints summary of this Astrohack File object, |
| | with available data, attributes and methods |
| values | Get children values |
| write | Write mds to disk by saving the data tree to |
| | a file |
+------------------------------+-----------------------------------------------+
Data Contents:
+----------+--------------------+
| Antenna | DDI |
+----------+--------------------+
| ant_ea06 | ['ddi_0', 'ddi_1'] |
| ant_ea25 | ['ddi_0', 'ddi_1'] |
+----------+--------------------+
Plotting beams¶
Here we make a call to plot_beams to plot the beam for a single antenna and DDI for brevity. These plots tell us about the shape of the beam, i.e. is it circular? is it smeared?
[20]:
image_mds.plot_beams(
image_exports, # Directory to contain the plots
ant="ea25", # Plotting antenna ea25
ddi=0, # Plotting DDI 0
display=True, # Display plots below
angle_unit="amin", # Unit for L and M axes
complex_split="polar", # Plot amplitude and phase
phase_unit="deg", # Unit for phase
parallel=False, # Don't do plots in parallel
)
[2026-03-19 15:40:01,371] INFO astrohack: Module path: /export/home/arya/work/Holography-1022/astrohack/src/astrohack
Here we call again plot_beams, this time over all the Antennas and the DDIs in the image_mds. For brevity the plots are not displayed, but a list of the files created is displayed just after the execution of plot_beams
[21]:
image_mds.plot_beams(
image_exports, # Directory to contain the plots
ant="all", # Plotting all antennas
ddi="all", # Plotting all DDIs
display=False, # Don't display plots below
complex_split="polar", # Plot amplitude and phase
parallel=parallel, # Do plots in parallel
)
# All exported files
print("\nCreated files:")
list_directory_files(image_exports + "/image_beam*.png")
[2026-03-19 15:40:03,719] INFO astrohack: Module path: /export/home/arya/work/Holography-1022/astrohack/src/astrohack
Created files:
image_exports/image_beam_polar_ant_ea25_ddi_0_pol_I.png
image_exports/image_beam_polar_ant_ea25_ddi_0_pol_Q.png
image_exports/image_beam_polar_ant_ea25_ddi_0_pol_U.png
image_exports/image_beam_polar_ant_ea25_ddi_0_pol_V.png
image_exports/image_beam_polar_ant_ea06_ddi_1_pol_I.png
image_exports/image_beam_polar_ant_ea06_ddi_0_pol_I.png
image_exports/image_beam_polar_ant_ea25_ddi_1_pol_I.png
image_exports/image_beam_polar_ant_ea06_ddi_1_pol_Q.png
image_exports/image_beam_polar_ant_ea25_ddi_1_pol_Q.png
image_exports/image_beam_polar_ant_ea06_ddi_0_pol_Q.png
image_exports/image_beam_polar_ant_ea06_ddi_1_pol_U.png
image_exports/image_beam_polar_ant_ea06_ddi_0_pol_U.png
image_exports/image_beam_polar_ant_ea25_ddi_1_pol_U.png
image_exports/image_beam_polar_ant_ea06_ddi_1_pol_V.png
image_exports/image_beam_polar_ant_ea25_ddi_1_pol_V.png
image_exports/image_beam_polar_ant_ea06_ddi_0_pol_V.png
Plotting apertures¶
Apertures are the final product of holog and are the data that is used by panel to compute the screw adjustments. Before proceeding to panel we may want to inspect the amplitude and phase maps of the apertures. Specifically the amplitude map may be used to estimate the value of the cutoff parameter of panel. In our first call to plot_apertures we are going to use a single antenna and DDI for display, followed by a call that goes over all antennas and DDIs and lists the
created files.
[22]:
import numpy as np
image_mds.plot_apertures(
image_exports, # Directory to contain the plots
ant="ea25", # Plotting antenna ea25
ddi=0, # Plotting DDI 0
polarization_state="I", # Plot only Stokes I aperture
plot_screws=True, # Plotting screw positions
phase_unit="rad", # Plotting phase as radians, degrees also possible (deg)
phase_limits=[-np.pi, np.pi], # Plotting from -pi to pi
deviation_unit="mm", # Plotting deviation as mm, all length units available
deviation_limits=[-2, 2], # Plotting from -2 to 2 mm
display=True, # Display plots below
parallel=False, # Don't do plots in parallel
)
[2026-03-19 15:40:07,857] INFO astrohack: Module path: /export/home/arya/work/Holography-1022/astrohack/src/astrohack
[23]:
image_mds.plot_apertures(
image_exports, # Directory to contain the plots
ant="all", # Plotting all antennas
ddi="all", # Plotting all DDIs
plot_screws=True, # Plotting screw positions
display=False, # Don't display plots below
parallel=parallel, # Do plots in parallel
)
# List All exported files
print("\nCreated files:")
list_directory_files(image_exports + "/image_amplitude*.png")
list_directory_files(image_exports + "/image_corrected*.png")
[2026-03-19 15:40:20,537] INFO astrohack: Module path: /export/home/arya/work/Holography-1022/astrohack/src/astrohack
Created files:
Exporting image data to FITS¶
Sometimes it can be interesting to work with the data produced by holog in another astronomical software package such as astropy or carta. For this purpose we have introduced the method export_to_fits to the image_mds object. The FITS created by this routine encompass all the data arrays contained in each xarray dataset of the image_mds. After the execution of export_to_fits we display the list of created files and the header of one of the created files.
[24]:
image_mds.export_to_fits(
image_exports, # Directory to contain FITS files
complex_split="cartesian", # Data will be split into real and imaginary FITS files
ant="all", # Export all antennas
ddi="all", # Export all DDIs
parallel=parallel, # Export FITS files in parallel
)
# All exported files
print("\nCreated files:")
list_directory_files(image_exports + "/image*.fits")
[2026-03-19 15:40:30,260] INFO astrohack: Module path: /export/home/arya/work/Holography-1022/astrohack/src/astrohack
[2026-03-19 15:40:30,372] INFO worker_0: Exporting image contents of ant_ea06 ddi_0 to FITS files in image_exports
[2026-03-19 15:40:30,379] INFO worker_3: Exporting image contents of ant_ea06 ddi_1 to FITS files in image_exports
[2026-03-19 15:40:30,385] INFO worker_1: Exporting image contents of ant_ea25 ddi_0 to FITS files in image_exports
[2026-03-19 15:40:30,391] INFO worker_2: Exporting image contents of ant_ea25 ddi_1 to FITS files in image_exports
Created files:
image_exports/image_beam_real_ant_ea06_ddi_1.fits
image_exports/image_beam_imag_ant_ea06_ddi_1.fits
image_exports/image_beam_real_ant_ea25_ddi_0.fits
image_exports/image_beam_real_ant_ea06_ddi_0.fits
image_exports/image_beam_imag_ant_ea25_ddi_0.fits
image_exports/image_beam_imag_ant_ea06_ddi_0.fits
image_exports/image_beam_real_ant_ea25_ddi_1.fits
image_exports/image_beam_imag_ant_ea25_ddi_1.fits
image_exports/image_aperture_real_ant_ea06_ddi_0.fits
image_exports/image_aperture_real_ant_ea25_ddi_0.fits
image_exports/image_aperture_imag_ant_ea06_ddi_0.fits
image_exports/image_aperture_real_ant_ea06_ddi_1.fits
image_exports/image_aperture_imag_ant_ea25_ddi_0.fits
image_exports/image_aperture_imag_ant_ea06_ddi_1.fits
image_exports/image_corrected_phase_ant_ea06_ddi_0.fits
image_exports/image_aperture_real_ant_ea25_ddi_1.fits
image_exports/image_corrected_phase_ant_ea25_ddi_0.fits
image_exports/image_aperture_imag_ant_ea25_ddi_1.fits
image_exports/image_corrected_phase_ant_ea06_ddi_1.fits
image_exports/image_corrected_phase_ant_ea25_ddi_1.fits
[25]:
# Antenna ea06 DDI 0 aperture real FITS header
from astropy.io import fits
hdul = fits.open(image_exports + "/image_aperture_real_ant_ea06_ddi_0.fits")
print(repr(hdul[0].header))
SIMPLE = T / conforms to FITS standard
BITPIX = -64 / array data type
NAXIS = 4 / number of array dimensions
NAXIS1 = 512
NAXIS2 = 512
NAXIS3 = 1
NAXIS4 = 4
EXTEND = T
STOKES = 'I, Q, U, V'
WAVELENG= 0.021161322651231735
FREQUENC= 14167000000.0
TELESCOP= 'ea06 '
INSTRUME= 'VLA '
TIME_CEN= 5170359447.000001
PADDING = 10
GRD_INTR= 'gaussian'
CHAN_AVE= 'yes '
CHAN_TOL= 0.005
SCAN_AVE= 'yes '
TO_STOKE= 'yes '
WCSAXES = 4
CRVAL3 = 0.0
CDELT3 = 14167000000.0
CRPIX3 = 0.0
CROTA3 = 0.0
CTYPE3 = 'Frequency'
CUNIT3 = 'Hz '
CRVAL4 = 1.0
CDELT4 = 1.0
CRPIX4 = 1.0
CROTA4 = 0.0
CTYPE4 = 'STOKES '
CUNIT4 = ''
CRVAL1 = 0.000607804533870422
CDELT1 = -0.00121560906774081
CRPIX1 = 256.0
CROTA1 = 0.0
CTYPE1 = 'X----LIN'
CUNIT1 = 'm '
CRVAL2 = 0.000607804533870422
CDELT2 = -0.00121560906774081
CRPIX2 = 256.0
CROTA2 = 0.0
CTYPE2 = 'Y----LIN'
CUNIT2 = 'm '
BMAJ = 0.05744485294117574
BMIN = -0.05744485294117574
BPA = 90.0
BUNIT = 'Normalized'
TYPE = 'Complex aperture real part'
ORIGIN = 'Astrohack v1.0.1: image'
DATE = 'Mar 19 2026, 15:40:30'
Zernike polynomials fitting results¶
Understanding the features that appear in an Aperture image may be daunting, in such cases one convenient way to analyze an aperture is to decompose it using Zernike polynomials. Some of the information that can be gleaned from the Zernike polynomials coefficients is the presence of a phase ramp or if the holography was obtained out of focus or even the astigmatism of the telescope. One other use of Zernike polynomial decomposition is to
model the beams when deconvolving sky images using A projection. astrohack.holog fits the apertures with Zernike polynomials up to an order set with the zernike_N_order parameter. To visualize the results from this fitting there are two methods of the image_mds object, export_zernike_fit_results which export the Zernike polynomials coefficients to a text file and plot_zernike_model that plots the Zernike models together with the Aperture and the fitting results.
[26]:
image_mds.export_zernike_fit_results(image_exports, ant="all", ddi="all", parallel=parallel)
print("\nCreated files:\n")
list_directory_files(image_exports + "/image_zernike*.txt")
print("\nExample File:\n")
display_text_file(image_exports + "/image_zernike_fit_ant_ea25_ddi_0.txt", nlines=22)
[2026-03-19 15:40:30,727] INFO astrohack: Module path: /export/home/arya/work/Holography-1022/astrohack/src/astrohack
Created files:
image_exports/image_zernike_fit_ant_ea25_ddi_1.txt
image_exports/image_zernike_fit_ant_ea06_ddi_0.txt
image_exports/image_zernike_fit_ant_ea06_ddi_1.txt
image_exports/image_zernike_fit_ant_ea25_ddi_0.txt
Example File:
* map 0, Frequency 14.1670 GHz, Correlation RR:
Fit RMS = 0.00097177 + 0.00093742*i
+-----------+-------------+-------------+
| Indices | Real | Imaginary |
+-----------+-------------+-------------+
| N=0, M=0 | 0.29654298 | 0.01918599 |
| N=1, M=-1 | -0.07142550 | 0.76368865 |
| N=1, M=1 | -0.24968576 | 1.50688576 |
| N=2, M=-2 | -0.43299233 | -0.02125748 |
| N=2, M=0 | -0.27774335 | 0.01559826 |
| N=2, M=2 | 0.11810537 | 0.00616394 |
| N=3, M=-3 | 0.04273706 | -0.13933120 |
| N=3, M=-1 | 0.00553100 | -0.25156413 |
| N=3, M=1 | -0.09623912 | 0.17022226 |
| N=3, M=3 | 0.14572524 | -0.54838582 |
| N=4, M=-4 | 0.01290425 | -0.00773214 |
| N=4, M=-2 | 0.09770843 | 0.04968150 |
| N=4, M=0 | 0.03058520 | -0.00538474 |
| N=4, M=2 | 0.00845229 | -0.00032249 |
| N=4, M=4 | -0.01276903 | -0.02132567 |
+-----------+-------------+-------------+
[27]:
image_mds.plot_zernike_model(
image_exports,
ant="ea25",
ddi="0",
display=True,
)
[2026-03-19 15:40:31,249] INFO astrohack: Module path: /export/home/arya/work/Holography-1022/astrohack/src/astrohack
[28]:
image_mds.plot_zernike_model(
image_exports, ant="all", ddi="all", display=False, parallel=parallel
)
print("\nCreated files:\n")
list_directory_files(image_exports + "/image_zernike*.png")
[2026-03-19 15:40:42,317] INFO astrohack: Module path: /export/home/arya/work/Holography-1022/astrohack/src/astrohack
Created files:
image_exports/image_zernike_model_ant_ea25_ddi_0_corr_RR.png
image_exports/image_zernike_model_ant_ea25_ddi_0_corr_RL.png
image_exports/image_zernike_model_ant_ea25_ddi_0_corr_LR.png
image_exports/image_zernike_model_ant_ea25_ddi_0_corr_LL.png
image_exports/image_zernike_model_ant_ea06_ddi_0_corr_RR.png
image_exports/image_zernike_model_ant_ea25_ddi_1_corr_RR.png
image_exports/image_zernike_model_ant_ea25_ddi_1_corr_RL.png
image_exports/image_zernike_model_ant_ea06_ddi_0_corr_RL.png
image_exports/image_zernike_model_ant_ea06_ddi_0_corr_LR.png
image_exports/image_zernike_model_ant_ea25_ddi_1_corr_LR.png
image_exports/image_zernike_model_ant_ea06_ddi_0_corr_LL.png
image_exports/image_zernike_model_ant_ea25_ddi_1_corr_LL.png
image_exports/image_zernike_model_ant_ea06_ddi_1_corr_RR.png
image_exports/image_zernike_model_ant_ea06_ddi_1_corr_RL.png
image_exports/image_zernike_model_ant_ea06_ddi_1_corr_LR.png
image_exports/image_zernike_model_ant_ea06_ddi_1_corr_LL.png
Exporting phase fit results from image data¶
Sometimes it is important to understand what came of the phase fitting during the imaging process. This can be done using the export_phase_fit_results method of the image_mds objects.
[29]:
image_mds.export_phase_fit_results(
image_exports, # Directory to contain phase fit results
ant="all", # Export all antennas
ddi="all", # Export all DDIs
parallel=parallel, # Export ASCII files in parallel
)
display_text_file(image_exports + "/image_phase_fit_ant_ea25_ddi_0.txt")
[2026-03-19 15:41:00,000] INFO astrohack: Module path: /export/home/arya/work/Holography-1022/astrohack/src/astrohack
* map 0, Frequency 14.1670 GHz, polarization state I:
+---------------------+----------------+------+
| Parameter | Value | Unit |
+---------------------+----------------+------+
| Phase offset | 0.24 ± 0.0 | deg |
| X point offset | 0.0 ± 0.0 | deg |
| Y point offset | 0.000267 ± 0.0 | deg |
| X focus offset | -0.62 ± 0.0 | mm |
| Y focus offset | -1.9 ± 0.0 | mm |
| Z focus offset | 0.081 ± 0.0 | mm |
| X subreflector tilt | 0.000107 ± 0.0 | deg |
| Y subreflector tilt | 0.00045 ± 0.0 | deg |
| X cassegrain offset | -31.0 ± 0.0 | mm |
| Y cassegrain offset | -28.0 ± 0.0 | mm |
+---------------------+----------------+------+
Panel: screw adjustments, plotting residuals corrections and exporting data to FITS¶
If we are sure about the quality of the data we can now proceed to getting screw adjustments and evaluating the surface provided by these adjustments. For that we are going to use the methods of the panel_mds object, i.e. the data product of the panel function.
Run panel or open panel file¶
Here we check for the existence of a .panel.zarr file, if it does not exist we run panel, if it exists we open it with open_panel.
[30]:
import os
from astrohack import panel, open_panel
panel_name = "data/ea25_cal_small_after_fixed.split.panel.zarr"
if os.path.exists(panel_name):
panel_mds = open_panel(panel_name)
else:
panel_mds = panel(
image_name=image_name,
panel_name=panel_name,
panel_model="rigid",
panel_margins=0.2,
clip_type="relative",
clip_level=0.2,
overwrite=True,
parallel=parallel,
)
With the panel_mds in hands we can now explore its contents. As before we define a variable, this time called panel_exports, with the name of the directory to contain the exported products and, we call the summary method to see the list of contents and available methods. In this tutorial we will cover the methods: plot_antennas, export_screws and export_to_fits.
[31]:
panel_exports = "panel_exports"
panel_mds.summary()
################################################################################
### Summary for: ###
### data/ea25_cal_small_after_fixed.split.panel.zarr ###
################################################################################
Data origin:
creation_time: 2026-03-19 15:38:45 MDT
creator_function: panel
origin: astrohack
version: 1.0.1
Input Parameters:
+--------------------+--------------------------------------------------+
| Parameter | Value |
+--------------------+--------------------------------------------------+
| ant | all |
| clip_level | 0.2 |
| clip_type | relative |
| ddi | all |
| image_name | data/ea25_cal_small_after_fixed.split.image.zarr |
| overwrite | True |
| panel_margins | 0.2 |
| panel_model | rigid |
| panel_name | data/ea25_cal_small_after_fixed.split.panel.zarr |
| parallel | True |
| polarization_state | I |
| use_detailed_mask | True |
+--------------------+--------------------------------------------------+
Available methods:
+------------------------------+-----------------------------------------------+
| Methods | Description |
+------------------------------+-----------------------------------------------+
| add_node | Add a node to the data tree file structure, |
| | however this node is not yet consolidated |
| | into the data tree structure, |
| | consolidate must be called to integrate all |
| | nodes writen by add_node onto the tree |
| | structure. |
| consolidate | Traverse own file structure on disk |
| | consolidating metadata to create a unified |
| | data tree entity. |
| create_from_input_parameters | Create an AstrohackBaseFile object from a |
| | filename and initializes xdtree root |
| | attributes. |
| export_gain_tables | Compute estimated antenna gains in dB and |
| | saves them to ASCII files. |
| export_screws | Export screw adjustments to text files and |
| | optionally plots. |
| export_to_fits | Export contents of an Astrohack MDS file to |
| | several FITS files in the destination folder |
| get_antenna | Retrieve an AntennaSurface object for |
| | interaction |
| is_close_to | Tests if self and other_mds are close to each |
| | other. |
| items | Get children items |
| keys | Get children keys |
| observation_summary | Create a Summary of observation information |
| open | Open Base file. |
| plot_antennas | Create diagnostic plots of antenna surfaces |
| | from panel data file. |
| summary | Prints summary of this Astrohack File object, |
| | with available data, attributes and methods |
| values | Get children values |
| write | Write mds to disk by saving the data tree to |
| | a file |
+------------------------------+-----------------------------------------------+
Data Contents:
+----------+--------------------+
| Antenna | DDI |
+----------+--------------------+
| ant_ea06 | ['ddi_0', 'ddi_1'] |
| ant_ea25 | ['ddi_0', 'ddi_1'] |
+----------+--------------------+
Plotting antenna original deviation, residuals and corrections¶
Here we make a first call to plot_antennas to plot only the deviations for a single antenna and DDI for brevity, and then a second call to produce all possible plots for all the antennas and DDIs present in the panel_mds. The deviation plots shown below serve to ilustrate the current state of the antenna surface, the proposed corrections to it, and the estimated residuals after the correction is applied to the antenna.
[32]:
panel_mds.plot_antennas(
panel_exports, # Directory to contain the plot and text file
ant="ea25", # Plotting Antenna ea25
ddi=0, # Plotting DDI 0
plot_type="deviation", # Do deviation plots only
deviation_unit="mils", #
plot_screws=False, # Not plotting screw positions
parallel=False, # Don't do plots in parallel
display=True, # Display plots below
)
[2026-03-19 15:41:00,196] INFO astrohack: Module path: /export/home/arya/work/Holography-1022/astrohack/src/astrohack
[33]:
panel_mds.plot_antennas(
panel_exports, # Directory to contain the plot and text file
ant="all", # Plotting all antennas
ddi="all", # Plotting all DDIs
plot_type="all", # Do all possible plots
plot_screws=False, # Not plotting screw positions
parallel=parallel, # Do plots in parallel
display=False, # Don't Display plots below
)
# All exported files
print("\nCreated files:")
list_directory_files(panel_exports + "/panel_mask*.png")
list_directory_files(panel_exports + "/panel_amplitude*.png")
list_directory_files(panel_exports + "/panel_deviation*.png")
list_directory_files(panel_exports + "/panel_phase*.png")
[2026-03-19 15:41:02,891] INFO astrohack: Module path: /export/home/arya/work/Holography-1022/astrohack/src/astrohack
Created files:
panel_exports/panel_mask_ant_ea06_ddi_0.png
panel_exports/panel_mask_ant_ea06_ddi_1.png
panel_exports/panel_mask_ant_ea25_ddi_0.png
panel_exports/panel_mask_ant_ea25_ddi_1.png
panel_exports/panel_amplitude_ant_ea06_ddi_0.png
panel_exports/panel_amplitude_ant_ea06_ddi_1.png
panel_exports/panel_amplitude_ant_ea25_ddi_0.png
panel_exports/panel_amplitude_ant_ea25_ddi_1.png
panel_exports/panel_deviation_original_ant_ea25_ddi_0.png
panel_exports/panel_deviation_correction_ant_ea25_ddi_0.png
panel_exports/panel_deviation_residual_ant_ea25_ddi_0.png
panel_exports/panel_deviation_original_ant_ea06_ddi_0.png
panel_exports/panel_deviation_original_ant_ea06_ddi_1.png
panel_exports/panel_deviation_correction_ant_ea06_ddi_0.png
panel_exports/panel_deviation_correction_ant_ea06_ddi_1.png
panel_exports/panel_deviation_residual_ant_ea06_ddi_0.png
panel_exports/panel_deviation_residual_ant_ea06_ddi_1.png
panel_exports/panel_deviation_original_ant_ea25_ddi_1.png
panel_exports/panel_deviation_correction_ant_ea25_ddi_1.png
panel_exports/panel_deviation_residual_ant_ea25_ddi_1.png
panel_exports/panel_phase_original_ant_ea06_ddi_0.png
panel_exports/panel_phase_original_ant_ea06_ddi_1.png
panel_exports/panel_phase_original_ant_ea25_ddi_0.png
panel_exports/panel_phase_correction_ant_ea06_ddi_0.png
panel_exports/panel_phase_correction_ant_ea06_ddi_1.png
panel_exports/panel_phase_correction_ant_ea25_ddi_0.png
panel_exports/panel_phase_residual_ant_ea06_ddi_0.png
panel_exports/panel_phase_residual_ant_ea06_ddi_1.png
panel_exports/panel_phase_residual_ant_ea25_ddi_0.png
panel_exports/panel_phase_original_ant_ea25_ddi_1.png
panel_exports/panel_phase_correction_ant_ea25_ddi_1.png
panel_exports/panel_phase_residual_ant_ea25_ddi_1.png
Screw adjustments¶
If we are satisfied with the proposed corrections we can then export them as a txt file and an acompaining helper plot. This can be done with the panel_mds method export_screws. Here we cover a single antenna and DDI for brevity.
[34]:
panel_mds.export_screws(
panel_exports, # Directory to contain the plot and text file
unit="mils", # Unit to express screw adjustments in plots and texts
threshold=25, # Threshold in mils for significant adjustments
display=True, # Display plot below
ant="ea25", # Export screw adjustments for antenna ea25
ddi=0, # Export screw adjustments for ddi 0
)
[2026-03-19 15:41:15,184] INFO astrohack: Module path: /export/home/arya/work/Holography-1022/astrohack/src/astrohack
Below we show an example of a txt file produced by export_screws
[35]:
display_text_file(panel_exports + "/panel_screws_ant_ea25_ddi_0.txt", 30)
# Screw adjustments for VLA's EA25: DDI 0, pol. state I
# Frequency = 14.1670 GHz
# Antenna surface RMS before adjustment: 19.51 mils or 0.50 mm
# Antenna surface RMS after adjustment: 8.51 mils or 0.22 mm
# Lower means away from subreflector
# Raise means toward the subreflector
# LOWER the panel if the number is POSITIVE
# RAISE the panel if the number is NEGATIVE
# Adjustments are in mils
# Panel il ir ol or Fallback Model
1-1 -10.15 5.23 6.41 35.17 no rigid
1-2 -9.73 -17.33 16.72 2.51 no rigid
1-3 17.02 20.46 1.26 7.70 no rigid
1-4 -21.05 -29.22 -12.84 -28.12 no rigid
1-5 -16.01 -11.11 -15.55 -6.39 no rigid
1-6 -20.53 -13.91 -2.26 10.12 no rigid
1-7 -46.84 -47.79 12.65 10.88 no rigid
1-8 -50.32 -39.71 6.38 26.22 no rigid
1-9 21.36 3.45 1.04 -32.45 no rigid
1-10 21.61 34.16 -26.98 -3.51 no rigid
1-11 38.75 50.63 -21.05 1.18 no rigid
1-12 30.67 4.03 29.65 -20.17 no rigid
2-1 23.35 42.07 1.05 29.34 no rigid
2-2 -12.51 -14.84 1.97 -1.55 no rigid
2-3 8.28 6.17 -8.19 -11.38 no rigid
2-4 2.96 -1.65 -8.90 -15.87 no rigid
2-5 -26.17 7.63 -26.02 25.07 no rigid
2-6 9.06 -11.70 31.87 0.49 no rigid
2-7 -33.20 -17.82 -19.43 3.82 no rigid
Exporting panel data to FITS¶
As with the image_mds data it may be convenient to export the data arrays in a panel_mds to FITS files. This can be acomplished with the method export_to_fits. As before we export all the data in the panel_mds, display a list of the produced files and an example header of one of the created files.
[36]:
panel_mds.export_to_fits(
panel_exports, # Directory to contain FITS files
ant="all", # Export all antennas
ddi="all", # Export all DDIs
parallel=parallel, # Export FITS files in parallel
)
# All exported files
print("\nCreated files:")
list_directory_files(panel_exports + "/panel*.fits")
[2026-03-19 15:41:17,444] INFO astrohack: Module path: /export/home/arya/work/Holography-1022/astrohack/src/astrohack
[2026-03-19 15:41:17,559] INFO worker_0: Exporting panel contents of ant_ea06 ddi_1 to FITS files in panel_exports
[2026-03-19 15:41:17,567] INFO worker_2: Exporting panel contents of ant_ea25 ddi_1 to FITS files in panel_exports
[2026-03-19 15:41:17,573] INFO worker_1: Exporting panel contents of ant_ea06 ddi_0 to FITS files in panel_exports
[2026-03-19 15:41:17,580] INFO worker_3: Exporting panel contents of ant_ea25 ddi_0 to FITS files in panel_exports
Created files:
panel_exports/panel_amplitude_ant_ea06_ddi_1.fits
panel_exports/panel_mask_ant_ea06_ddi_1.fits
panel_exports/panel_phase_original_ant_ea06_ddi_1.fits
panel_exports/panel_phase_correction_ant_ea06_ddi_1.fits
panel_exports/panel_phase_residual_ant_ea06_ddi_1.fits
panel_exports/panel_amplitude_ant_ea06_ddi_0.fits
panel_exports/panel_deviation_original_ant_ea06_ddi_1.fits
panel_exports/panel_mask_ant_ea06_ddi_0.fits
panel_exports/panel_deviation_correction_ant_ea06_ddi_1.fits
panel_exports/panel_phase_original_ant_ea06_ddi_0.fits
panel_exports/panel_deviation_residual_ant_ea06_ddi_1.fits
panel_exports/panel_phase_correction_ant_ea06_ddi_0.fits
panel_exports/panel_phase_residual_ant_ea06_ddi_0.fits
panel_exports/panel_deviation_original_ant_ea06_ddi_0.fits
panel_exports/panel_amplitude_ant_ea25_ddi_0.fits
panel_exports/panel_deviation_correction_ant_ea06_ddi_0.fits
panel_exports/panel_mask_ant_ea25_ddi_0.fits
panel_exports/panel_deviation_residual_ant_ea06_ddi_0.fits
panel_exports/panel_phase_original_ant_ea25_ddi_0.fits
panel_exports/panel_phase_correction_ant_ea25_ddi_0.fits
panel_exports/panel_phase_residual_ant_ea25_ddi_0.fits
panel_exports/panel_deviation_original_ant_ea25_ddi_0.fits
panel_exports/panel_deviation_correction_ant_ea25_ddi_0.fits
panel_exports/panel_amplitude_ant_ea25_ddi_1.fits
panel_exports/panel_deviation_residual_ant_ea25_ddi_0.fits
panel_exports/panel_mask_ant_ea25_ddi_1.fits
panel_exports/panel_phase_original_ant_ea25_ddi_1.fits
panel_exports/panel_phase_correction_ant_ea25_ddi_1.fits
panel_exports/panel_phase_residual_ant_ea25_ddi_1.fits
panel_exports/panel_deviation_original_ant_ea25_ddi_1.fits
panel_exports/panel_deviation_correction_ant_ea25_ddi_1.fits
panel_exports/panel_deviation_residual_ant_ea25_ddi_1.fits
[37]:
# Antenna ea06 DDI 0 deviation residual FITS header
from astropy.io import fits
hdul = fits.open(panel_exports + "/panel_deviation_residual_ant_ea06_ddi_0.fits")
print(repr(hdul[0].header))
SIMPLE = T / conforms to FITS standard
BITPIX = -64 / array data type
NAXIS = 2 / number of array dimensions
NAXIS1 = 510
NAXIS2 = 510
EXTEND = T
PMODEL = 'rigid '
PMARGIN = 0.2
CLIP = 0.637374654991791
TELESCOP= 'ea06 '
INSTRUME= 'VLA '
WAVELENG= 0.021161322651231735
FREQUENC= 14167000000.0
WCSAXES = 2
CRVAL1 = 0.02872242647058928
CDELT1 = -0.05744485294117751
CRPIX1 = 255.0
CROTA1 = 0.0
CTYPE1 = 'X----LIN'
CUNIT1 = 'm '
CRVAL2 = -0.02872242647058928
CDELT2 = 0.057444852941177516
CRPIX2 = 255.0
CROTA2 = 0.0
CTYPE2 = 'Y----LIN'
CUNIT2 = 'm '
BMAJ = 1.4366515837104072
BMIN = 1.4366515837104072
BPA = 0.0
BUNIT = 'm '
TYPE = 'Deviation residuals'
ORIGIN = 'Astrohack v1.0.1: panel'
DATE = 'Mar 19 2026, 15:41:18'
Checking gain improvements¶
One important measure of antenna performance and panel adjustment sucess is the gain estimate derived from the aperture phases. Gain tables for the antennas for which holography was done can be obtained by using the export_gain_tables method of the panel_mds objects.
[38]:
panel_mds.export_gain_tables(
panel_exports, # Directory to contain gain tables
ant="all", # Export all antennas
ddi="all", # Export all DDIs
parallel=parallel, # Export ASCII files in parallel
)
[2026-03-19 15:41:18,179] INFO astrohack: Module path: /export/home/arya/work/Holography-1022/astrohack/src/astrohack
[39]:
display_text_file(panel_exports + "/panel_gains_ant_ea25_ddi_0.txt")
# Gain estimates for VLA antenna ea25
# Based on a measurement at 14.1670 GHz, 2.12 cm
# Antenna surface RMS before adjustment: 0.50 mm
# Antenna surface RMS after adjustment: 0.22 mm
+-------------+------------+--------------+-------------+------------------+
| Frequency | Wavelength | Before panel | After panel | Theoretical Max. |
+-------------+------------+--------------+-------------+------------------+
| 1.4990 GHz | 20.00 cm | 31.96 dB | 31.96 dB | 31.96 dB |
| 2.3061 GHz | 13.00 cm | 33.83 dB | 33.83 dB | 33.83 dB |
| 4.9965 GHz | 6.00 cm | 37.17 dB | 37.19 dB | 37.19 dB |
| 9.9931 GHz | 3.00 cm | 40.13 dB | 40.19 dB | 40.20 dB |
| 14.9896 GHz | 2.00 cm | 41.81 dB | 41.93 dB | 41.96 dB |
| 23.0610 GHz | 1.30 cm | 43.52 dB | 43.76 dB | 43.83 dB |
| 29.9792 GHz | 1.00 cm | 44.52 dB | 44.86 dB | 44.97 dB |
| 42.8275 GHz | 7.00 mm | 45.78 dB | 46.31 dB | 46.52 dB |
+-------------+------------+--------------+-------------+------------------+
[40]:
if parallel:
client.close()