Skip to content
Snippets Groups Projects
Code owners
Assign users and groups as approvers for specific file changes. Learn more.
run_oskar.py 5.36 KiB
#!/usr/bin/env python3
"""
Generates all-sky zenith-centred beam patterns for SKALA-4 and EDA-2 antennas.
"""

import copy
import glob
import os.path
import re
import subprocess

from astropy.io import fits
from astropy.time import Time, TimeDelta
import matplotlib
matplotlib.use('Agg')
# pylint: disable=wrong-import-position
import matplotlib.pyplot as plt
import numpy
import oskar


def get_start_time(ra0_deg, length_sec):
    """Returns optimal start time for field RA and observation length."""
    t = Time('2000-01-01 00:00:00', scale='utc', location=('116.764d', '0d'))
    dt_hours = (24.0 - t.sidereal_time('apparent').hour) / 1.0027379
    dt_hours += (ra0_deg / 15.0)
    start = t + TimeDelta(dt_hours * 3600.0 - length_sec / 2.0, format='sec')
    return start.value


def plot_panel(ax, image, title, cmap):
    """Plots a single panel."""
    im = ax.imshow(numpy.squeeze(image), cmap=cmap)
    plt.colorbar(im, format='%.2e')
    plt.tick_params(labelcolor='none', top='off', bottom='off', left='off', right='off')
    ax.set_xticks([])
    ax.set_yticks([])
    ax.invert_yaxis()
    ax.set_title(title)
    ax.axis('equal')


def make_plots(title, glob_pattern, out_basename):
    """Generates a plot with four panels."""
    # Load FITS images matching the glob pattern.
    files = glob.glob(glob_pattern)
    files.sort()  # Must be sorted!
    images = []
    for file in files:
        images.append(fits.getdata(file))

    # Set titles and colour maps to use.
    cmap = ''
    titles = []
    if '_AUTO_POWER' in glob_pattern:
        # cmap = 'plasma'
        cmap = 'Blues_r'
        titles = ['Stokes I', 'Stokes Q', 'Stokes U', 'Stokes V']
    elif '_AMP' in glob_pattern:
        # cmap = 'jet'
        cmap = 'CMRmap'
        titles = ['XX', 'XY', 'YX', 'YY']

    # Sub-plots, one for each Stokes parameter.
    fig = plt.figure(figsize=(8, 6))
    ax = fig.add_subplot(221, frameon=False)
    plot_panel(ax, images[0], titles[0], cmap)
    ax = fig.add_subplot(222, frameon=False)
    plot_panel(ax, images[1], titles[1], cmap)
    ax = fig.add_subplot(223, frameon=False)
    plot_panel(ax, images[2], titles[2], cmap)
    ax = fig.add_subplot(224, frameon=False)
    plot_panel(ax, images[3], titles[3], cmap)

    # Add main title.
    title = title.replace('_', ' ')  # Replace underscores with spaces.
    fig.suptitle(title)
    fig.tight_layout()
    fig.subplots_adjust(top=0.88)

    # Save and close.
    plt.savefig('%s.png' % (out_basename))
    plt.close('all')


def main(npixels):
    """Main function."""
    # Name of the application to run, and a settings file for it.
    app = 'oskar_sim_beam_pattern'
    settings_path = '_temp_settings.ini'

    # Define some basic observation parameters.
    ra0_deg = 0.0
    dec0_deg = -27.0
    length_sec = 1.0

    # Define base settings dictionary.
    common_settings = {
        'observation': {
            'phase_centre_ra_deg': ra0_deg,
            'phase_centre_dec_deg': dec0_deg,
            #'pointing_file': 'station_pointing.txt',
            'start_time_utc': get_start_time(ra0_deg, length_sec),
            'length': length_sec
        },
        'telescope': {
            'normalise_beams_at_phase_centre': False,
            'aperture_array/element_pattern/normalise': False
        },
        'beam_pattern': {
            'coordinate_frame': 'Horizon',
            'beam_image/size': npixels,
            'station_outputs/fits_image/amp': True,
            'station_outputs/fits_image/phase': True,
            'station_outputs/fits_image/auto_power_real': False
        }
    }

    # Define frequencies of interest (in MHz).
    freqs = [50]
    #freqs = [70]

    # Define telescope models to use, and associated overrides for them.
    telescopes = {
        'basefunctions': {
            'telescope/input_directory': 'telescope.tm',
            'telescope/aperture_array/element_pattern/enable_numerical': True,
            'telescope/aperture_array/element_pattern/swap_xy': True,
            'telescope/aperture_array/array_pattern/enable': False
        },
    }

    # Loop over telescope models.
    for tel, tel_params in telescopes.items():

        # Copy the base settings dictionary.
        current_settings = copy.deepcopy(common_settings)

        # Update current settings with telescope model parameters.
        current_settings.update(tel_params)

        # Loop over frequencies.
        for freq in freqs:

            # Create the settings file.
            open(settings_path, 'w').close()
            settings = oskar.SettingsTree(app, settings_path)
            settings.from_dict(current_settings)

            # Update output root path and frequency.
            tel_root = re.sub(r'[^\w]', '', tel)  # Strip symbols from tel.
            root_path = tel_root + ('_%03d_MHz' % freq)
            settings['beam_pattern/root_path'] = root_path
            settings['observation/start_frequency_hz'] = 1e6 * freq

            # Run the app with the settings file.
            subprocess.call([app, settings_path])

            # Make plots.
            #title = tel + ' @ ' + str(freq) + ' MHz'
            #make_plots(title=title,
                       #glob_pattern=root_path+'*_AMP*',
                       #out_basename=root_path+'_amp')
            #make_plots(title=title,
                       #glob_pattern=root_path+'*_AUTO_POWER*',
                       #out_basename=root_path+'_stokes')


if __name__ == '__main__':
    main()