diff --git a/lofar_random_gen.py b/lofar_random_gen.py new file mode 100644 index 0000000000000000000000000000000000000000..313509118bad04adef8e10ea8ae496f8cd1f7735 --- /dev/null +++ b/lofar_random_gen.py @@ -0,0 +1,92 @@ +import numpy +from argparse import ArgumentParser +import json +import logging +from lofardatagenerator.coords import * + +logger = logging.getLogger('LOFAR generator') +logger.setLevel(logging.DEBUG) + +__MINUTE_IN_SECONDS = 60 + +def parse_arguments(): + parser = ArgumentParser('Generate uvw and compute eccentricity and ' + 'filling factor of the uvw plane') + parser.add_argument('array_coordinates', + type=str, + help='coordinate list of the array elements') + + parser.add_argument('observation_duration', + type=int, + nargs='+', + help='duration of the observation in minutes') + parser.add_argument('--n_antennas', + type=int, + default=-1, + help='antenna sample size') + + parser.add_argument('--min_alt', + type=float, + default='10', + help='source_coordinates') + parser.add_argument('--n_pointings', + type=float, + default=100) + + parser.add_argument('--random_seed', + default=numpy.random.seed(), + type=int, + help='seed for the random number generator') + + parser.add_argument('--frequency', + default=150., + type=float, + help='frequency in MHz') + + parser.add_argument('--max_baseline_length', + default=-1, + type=float, + help='max baseline length in m') + parser.add_argument('--save_image_to', type=str, default='out') + return parser.parse_args() + + +import os +def main(): + start_time = datetime.now() + arguments = parse_arguments() + numpy.random.seed(arguments.random_seed) + for i in range(arguments.n_pointings): + + array_x, array_y, array_z = load_station_layout(arguments.array_coordinates, + sample_size = arguments.n_antennas) + + observation_duration_in_seconds = int(numpy.random.choice(arguments.observation_duration) * + __MINUTE_IN_SECONDS) + alt = numpy.random.uniform([arguments.min_alt, 90], 1)[0] + az = numpy.random.uniform([0, 360], 1)[0] + + ra, dec = local_coords_to_radians(alt, az) + + uvw = generate_uvw_per_observation(array_x, array_y, array_z, ra, dec, + start_time, + observation_duration_in_seconds) + uvw = select_baseline_length(uvw, arguments.max_baseline_length) + logger.debug("UVW generated") + rotated_uvw, pca, r_pca = rotate_ellipse_points(uvw) + ecc, filling_factor, sampling, mean_coverage, mean_d = compute_parameters(rotated_uvw) + out_path = os.path.join(arguments.save_image_to, f'POINTING_{i}') + os.makedirs(out_path, exist_ok=True) + make_plots(uvw, rotated_uvw, sampling, arguments.frequency, pca, r_pca, out_path) + + data = dict( + duration=observation_duration_in_seconds, + ra=ra, dec=dec, out_dir=arguments.save_image_to, e=ecc, + filling_factor=filling_factor, average_coverage=mean_coverage, + average_scale=mean_d) + with open(os.path.join(arguments.save_image_to, f'parameters_pointing_{i}.json'), 'w') as f_out: + json.dump(data, f_out) + + +if __name__== '__main__': + main() diff --git a/lofardatagenerator/__init__.py b/lofardatagenerator/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/lofardatagenerator/coords.py b/lofardatagenerator/coords.py new file mode 100644 index 0000000000000000000000000000000000000000..30f8e670de08076ea9c65374076d8481156d219e --- /dev/null +++ b/lofardatagenerator/coords.py @@ -0,0 +1,229 @@ +from datetime import datetime, timedelta +import sys +from astropy.coordinates import SkyCoord +import pyuvwsim +import matplotlib.pyplot as plt +from sklearn.decomposition import PCA +import scipy +import numpy +import logging +from astropy.coordinates import AltAz, EarthLocation +import astropy.units as u +__DEFAULT_LOFAR_ARRAY_CENTER = EarthLocation.from_geocentric(*[3183230.104, 1276816.818, 5359477.705], unit=u.m) + + +def local_coords_to_radians(alt, az, location=__DEFAULT_LOFAR_ARRAY_CENTER): + local_frame = AltAz(location=location, obstime=datetime.now()) + local_coord = SkyCoord(alt=alt * u.deg, az=az * u.deg, frame=local_frame) + sky_coord = local_coord.transform_to('icrs') + return sky_coord.ra.value, sky_coord.dec.value + + +def coords_into_radians(coords_string): + c = SkyCoord(coords_string, unit=(u.hourangle, u.deg)) + ra, dec = c.ra.radian, c.dec.radian + return ra, dec + + +def load_station_layout(file_path: str, sample_size=10, max_baseline_length=-1): + + coords = numpy.loadtxt(file_path, delimiter=',') + max_sample = coords.shape[0] + if sample_size > 0 and sample_size <= max_sample: + print(max_sample, numpy.arange(0, max_sample, 1)) + sample = numpy.arange(0, max_sample, 1) + numpy.random.shuffle(sample) + sample = sample[:sample_size] + return coords[sample, 0], coords[sample, 1], coords[sample, 2] + else: + return coords[:, 0], coords[:, 1], coords[:, 2] + + +def get_mjd_from_datetime(time : datetime): + mjd = pyuvwsim.datetime_to_mjd(time.year, + time.month, + time.day, + time.hour, + time.minute, + time.second) + return mjd + + +def generate_uvw_per_observation(array_x, array_y, array_z, ra, dec, start_time, + observation_duration): + + n_antennas = len(array_x) + n_baselines = int(n_antennas * (n_antennas - 1) // 2) + + uvw = numpy.zeros((observation_duration, n_baselines, 3)) + + for i in range(observation_duration): + mjd = get_mjd_from_datetime(start_time - timedelta(minutes = i)) + u, v, w = pyuvwsim.evaluate_baseline_uvw(array_x, + array_y, + array_z, + ra, dec, mjd) + uvw[i, :, 0] = u + uvw[i, :, 1] = v + uvw[i, :, 2] = w + + u, v, w = uvw[:, :, 0].flatten(), \ + uvw[:, :, 1].flatten(), \ + uvw[:, :, 2].flatten() + points_array = numpy.zeros((len(u), 3), dtype=numpy.float32) + points_array[:, 0] = u + points_array[:, 1] = v + points_array[:, 2] = w + + return points_array + + +def rotate_ellipse_points(uvw): + __NDIMS = len(uvw.shape) + + pca = PCA(n_components=3) + + mean_uvw = numpy.mean(uvw, axis=0) + mean_uvw = uvw - mean_uvw + principal_components = pca.fit_transform(mean_uvw.T) + + principal_component_0 = principal_components[0] + principal_component_1 = principal_components[1] + + rot_axis = [1., 0, 0] + cos_teta = numpy.dot(principal_component_0, rot_axis) / \ + numpy.linalg.norm(principal_component_0) / \ + numpy.linalg.norm(rot_axis) + + sin_teta = numpy.sqrt(1 - cos_teta**2) + + rotation_matrix = numpy.zeros((3,3)) + rotation_matrix[0, 0] = cos_teta + rotation_matrix[0, 1] = sin_teta + rotation_matrix[1, 0] = -sin_teta + rotation_matrix[1, 1] = cos_teta + rotation_matrix[2, 2] = 1 + + rotated_matrix = numpy.dot(rotation_matrix.T, mean_uvw.T) + rotated_components = numpy.dot(rotation_matrix.T, numpy.array(principal_components).T) + + return rotated_matrix, principal_components, rotated_components + + +def compute_parameters(rotated_matrix): + u = rotated_matrix[0, :] + v = rotated_matrix[1, :] + baseline = numpy.sqrt(u ** 2 + v ** 2) + grid_step = numpy.max(baseline) / 100 + + max_u, min_u = max(rotated_matrix[0, :]), min(rotated_matrix[0, :]) + max_v, min_v = max(rotated_matrix[1, :]), min(rotated_matrix[1, :]) + max_w, min_w = max(rotated_matrix[2, :]), min(rotated_matrix[2, :]) + + sampling_u = numpy.floor((max_u - min_u) / grid_step) + sampling_v = numpy.floor((max_v - min_v) / grid_step) + sampling = int(max([sampling_u, sampling_v])) + + dv = max_v - min_v + du = max_u - min_u + ecc = min([du, dv]) / max([du, dv]) + + hist, ax1, ax2 = numpy.histogram2d(rotated_matrix[0, :], + rotated_matrix[1, :], + bins=sampling) + d = numpy.sqrt(ax1[:-1, numpy.newaxis] ** 2 + ax2[numpy.newaxis, :-1] ** 2) + + mean_coverage = numpy.mean(hist) + + mean_d = numpy.average(d, weights=hist) + filling_factor = len(hist[hist > 0]) / (hist.shape[0] * hist.shape[1]) + return ecc, filling_factor, sampling, mean_coverage, mean_d + + +def plot_lm_coverage(uvw, sampling, frequency_in_mhz): + #TODO: THIS might be incorrect. Check + __MHZ_IN_HZ = 1.e6 + __DEG_IN_ARCSEC = 206264.80624709636 + hist, ax1, ax2 = numpy.histogram2d(uvw[:, 0], uvw[:, 1], bins=sampling) + ubl = ax1[1] - ax1[0] + vbl = ax2[1] - ax2[0] + + freq = frequency_in_mhz * __MHZ_IN_HZ + + xcoeff = 1./(numpy.pi * ubl / scipy.constants.c * freq) + ycoeff = 1./(numpy.pi * vbl / scipy.constants.c * freq) + + values = numpy.zeros_like(hist) + values[hist > 0] = 1 + + plt.imshow(abs(numpy.fft.fftshift(numpy.fft.fft2(values))), + extent=[-xcoeff, xcoeff, + -ycoeff, ycoeff]) + plt.xlabel('m') + plt.ylabel('l') + + +def make_uv_hist_plot(uvw, sampling, pca): + hist, ax1, ax2 = numpy.histogram2d(uvw[:, 0], uvw[:, 1], bins=sampling) + plt.minorticks_on() + plt.imshow(numpy.log10(hist), extent=[ + ax2[0] * 1.e-6, ax2[-1] * 1.e-6, + ax1[0] * 1.e-6, ax1[-1] * 1.e-6 + ], aspect='equal') + + plt.quiver(0, 0, pca[0][0], pca[0][1], scale=5) + plt.quiver(0, 0, pca[1][0], pca[1][1], scale=5) + plt.xlabel('x [Mm]') + plt.ylabel('y [Mm]') + + plt.colorbar() + +def make_baseline_hist_plot(uvw, sampling): + u = uvw[:, 0] + v = uvw[:, 1] + w = uvw[:, 2] + baselines = numpy.sqrt(u ** 2 + v ** 2 + w ** 2) / 1.e6 + plt.hist(baselines, bins=sampling) + plt.semilogy() + plt.ylabel('N samples') + plt.xlabel('$\sqrt{u^2 + v^2 + w^z}$ [Mm]') + + +def make_plots(original_uvw, rotated_uvw, sampling, frequency_in_mhz, pca, r_pca, path): + plt.figure('original_uvw') + make_uv_hist_plot(original_uvw, sampling, pca) + plt.savefig(path + '/original_uvw.png') + plt.close() + + plt.figure('lm coverage') + plot_lm_coverage(original_uvw, sampling, frequency_in_mhz) + plt.savefig(path + '/lm_coverage.png') + plt.close() + + plt.figure('baselines histogram') + make_baseline_hist_plot(original_uvw, sampling) + plt.savefig(path + '/baseline_histogram.png') + plt.close() + + plt.figure('rotated_uvw') + make_uv_hist_plot(rotated_uvw.T, sampling, r_pca) + plt.savefig(path + '/rotated_uvw.png') + plt.close() + + +def select_baseline_length(uvw, max_baseline_length): + if max_baseline_length > 0: + x = uvw[:, 0] + y = uvw[:, 1] + z = uvw[:, 2] + dist = numpy.sqrt(x**2. + y**2. + z**2.) + print(dist) + uvw_new = uvw[dist <= max_baseline_length, :] + if len(uvw_new) == 0: + logging.error('max length %s selects an empty sample of antennas', + max_baseline_length) + sys.exit(1) + + return uvw_new + else: + return uvw diff --git a/lofargen.py b/lofargen.py index 044fd6f7ab40fb17df072934a1111bfd77fe5116..033156af5d4b36862dc1d16463b54036d6d8c895 100644 --- a/lofargen.py +++ b/lofargen.py @@ -1,19 +1,13 @@ -import pyuvwsim import numpy -from datetime import datetime, timedelta -import sys -from astropy.coordinates import SkyCoord -from astropy import units as u -import matplotlib.pyplot as plt -from sklearn.decomposition import PCA -import scipy from argparse import ArgumentParser - +import json import logging +from lofardatagenerator.coords import * logger = logging.getLogger('LOFAR generator') -logging.basicConfig(level=logging.DEBUG) +logger.setLevel(logging.DEBUG) +__MINUTE_IN_SECONDS = 60 def parse_arguments(): parser = ArgumentParser('Generate uvw and compute eccentricity and ' @@ -49,195 +43,10 @@ def parse_arguments(): default=-1, type=float, help='max baseline length in m') - + parser.add_argument('--save_image_to', type=str, default='.') return parser.parse_args() -def coords_into_radians(coords_string): - c = SkyCoord(coords_string, unit=(u.hourangle, u.deg)) - ra, dec = c.ra.radian, c.dec.radian - return ra, dec - - -def load_station_layout(file_path: str, sample_size=10, max_baseline_length=-1): - - coords = numpy.loadtxt(file_path, delimiter=',') - max_sample = coords.shape[0] - if sample_size > 0 and sample_size <= max_sample: - print(max_sample, numpy.arange(0, max_sample, 1)) - sample = numpy.arange(0, max_sample, 1) - numpy.random.shuffle(sample) - sample = sample[:sample_size] - return coords[sample, 0], coords[sample, 1], coords[sample, 2] - else: - return coords[:, 0], coords[:, 1], coords[:, 2] - - -def get_mjd_from_datetime(time : datetime): - mjd = pyuvwsim.datetime_to_mjd(time.year, - time.month, - time.day, - time.hour, - time.minute, - time.second) - return mjd - - -def generate_uvw_per_observation(array_x, array_y, array_z, ra, dec, start_time, - observation_duration): - - n_antennas = len(array_x) - n_baselines = int(n_antennas * (n_antennas - 1) // 2) - - uvw = numpy.zeros((observation_duration, n_baselines, 3)) - - for i in range(observation_duration): - mjd = get_mjd_from_datetime(start_time - timedelta(minutes = i)) - u, v, w = pyuvwsim.evaluate_baseline_uvw(array_x, - array_y, - array_z, - ra, dec, mjd) - uvw[i, :, 0] = u - uvw[i, :, 1] = v - uvw[i, :, 2] = w - - u, v, w = uvw[:, :, 0].flatten(), \ - uvw[:, :, 1].flatten(), \ - uvw[:, :, 2].flatten() - points_array = numpy.zeros((len(u), 3), dtype=numpy.float32) - points_array[:, 0] = u - points_array[:, 1] = v - points_array[:, 2] = w - - return points_array - - -def rotate_ellipse_points(uvw): - __NDIMS = len(uvw.shape) - - pca = PCA(n_components=3) - principal_components = pca.fit_transform(uvw.T) - logger.debug("Found PCA") - logger.debug("PCAs %s", principal_components) - principal_component_0 = principal_components[0] - principal_component_1 = principal_components[1] - - cos_teta = numpy.dot(principal_component_0, principal_component_1) / \ - numpy.linalg.norm(principal_component_0) / \ - numpy.linalg.norm(principal_component_1) - - sin_teta = numpy.sqrt(1 - cos_teta**2) - - rotation_matrix = numpy.zeros_like(principal_components) - rotation_matrix[0, 0] = cos_teta - rotation_matrix[0, 1] = sin_teta - rotation_matrix[1, 0] = -sin_teta - rotation_matrix[1, 1] = cos_teta - rotation_matrix[2, 2] = 1 - - rotated_matrix = numpy.dot(rotation_matrix, uvw.T) - return rotated_matrix - - -def compute_parameters(rotated_matrix): - u = rotated_matrix[0, :] - v = rotated_matrix[1, :] - baseline = numpy.sqrt(u ** 2 + v ** 2) - grid_step = numpy.max(baseline) / 100 - - max_u, min_u = max(rotated_matrix[0, :]), min(rotated_matrix[0, :]) - max_v, min_v = max(rotated_matrix[1, :]), min(rotated_matrix[1, :]) - max_w, min_w = max(rotated_matrix[2, :]), min(rotated_matrix[2, :]) - - sampling_u = numpy.floor((max_u - min_u) / grid_step) - sampling_v = numpy.floor((max_v - min_v) / grid_step) - sampling = int(max([sampling_u, sampling_v])) - - dv = max_v - min_v - du = max_u - min_u - ecc = min([du, dv]) / max([du, dv]) - - hist, ax1, ax2 = numpy.histogram2d(rotated_matrix[0, :], - rotated_matrix[1, :], - bins=sampling) - - filling_factor = len(hist[hist > 0]) / (hist.shape[0] * hist.shape[1]) - return ecc, filling_factor, sampling - - -def plot_lm_coverage(uvw, sampling, frequency_in_mhz): - #TODO: THIS might be incorrect. Check - __MHZ_IN_HZ = 1.e6 - __DEG_IN_ARCSEC = 206264.80624709636 - hist, ax1, ax2 = numpy.histogram2d(uvw[:, 0], uvw[:, 1], bins=sampling) - ubl = ax1[1] - ax1[0] - vbl = ax2[1] - ax2[0] - - freq = frequency_in_mhz * __MHZ_IN_HZ - - xcoeff = 1./(numpy.pi * ubl / scipy.constants.c * freq) - ycoeff = 1./(numpy.pi * vbl / scipy.constants.c * freq) - - values = numpy.zeros_like(hist) - values[hist > 0] = 1 - - plt.imshow(abs(numpy.fft.fftshift(numpy.fft.fft2(values))), - extent=[-xcoeff, xcoeff, - -ycoeff, ycoeff]) - plt.xlabel('m') - plt.ylabel('l') - - -def make_uv_hist_plot(uvw, sampling): - hist, ax1, ax2 = numpy.histogram2d(uvw[:, 0], uvw[:, 1], bins=sampling) - plt.imshow(numpy.log10(hist), extent=[ - ax2[0], ax2[-1], - ax1[0], ax1[-1] - ]) - plt.xlabel('x [km]') - plt.ylabel('y [km]') - - plt.colorbar() - -def make_baseline_hist_plot(uvw, sampling): - u = uvw[:, 0] - v = uvw[:, 1] - w = uvw[:, 2] - baselines = numpy.sqrt(u ** 2 + v ** 2 + w ** 2) - plt.hist(baselines, bins=sampling) - plt.xlabel('baseline length [km]') - -def make_plots(original_uvw, rotated_uvw, sampling, frequency_in_mhz): - plt.figure('original_uvw') - make_uv_hist_plot(original_uvw, sampling) - plt.figure('lm coverage') - plot_lm_coverage(original_uvw, sampling, frequency_in_mhz) - plt.figure('baselines histogram') - make_baseline_hist_plot(original_uvw, sampling) - plt.figure('rotated_uvw') - make_uv_hist_plot(rotated_uvw.T, sampling) - plt.show() - -__MINUTE_IN_SECONDS = 60 - - -def select_baseline_length(uvw, max_baseline_length): - if max_baseline_length > 0: - x = uvw[:, 0] - y = uvw[:, 1] - z = uvw[:, 2] - dist = numpy.sqrt(x**2. + y**2. + z**2.) - print(dist) - uvw_new = uvw[dist <= max_baseline_length, :] - if len(uvw_new) == 0: - logger.error('max length %s selects an empty sample of antennas', - max_baseline_length) - sys.exit(1) - - return uvw_new - else: - return uvw - def main(): start_time = datetime.now() @@ -255,14 +64,18 @@ def main(): observation_duration_in_seconds) uvw = select_baseline_length(uvw, arguments.max_baseline_length) logger.debug("UVW generated") - rotated_uvw = rotate_ellipse_points(uvw) - ecc, filling_factor, sampling = compute_parameters(rotated_uvw) + rotated_uvw, pca, r_pca = rotate_ellipse_points(uvw) + ecc, filling_factor, sampling, mean_coverage, mean_d = compute_parameters(rotated_uvw) if(arguments.make_uvw_plot): - make_plots(uvw, rotated_uvw, sampling, arguments.frequency) - - print("Eccentricity: ", ecc) - print("Filling factor: ", filling_factor) + make_plots(uvw, rotated_uvw, sampling, arguments.frequency, pca, r_pca, arguments.save_image_to) + + data = dict(frequency=arguments.frequency, duration=observation_duration_in_seconds, + ra=ra, dec=dec, out_dir=arguments.save_image_to, e=ecc, + filling_factor=filling_factor, average_coverage=mean_coverage, + average_scale=mean_d) + with open(arguments.save_image_to + '/parameters.json', 'w') as f_out: + json.dump(data, f_out) if __name__== '__main__':