diff --git a/config/data_set.toml b/config/data_set.toml index d1f77c5..246fe03 100644 --- a/config/data_set.toml +++ b/config/data_set.toml @@ -1,24 +1,26 @@ [sampling_options] mode = "basic" -layout = "vlba" -img_size = 64 -fov_center_ra = [00:00:00.0, 23:59:59.59] -fov_center_dec = [00:00:00.0, 11:59:59.59] -fov_size = 0.0064 # max res 0.1 -corr_int_time = 10.0 -scan_start = ["01-01-2020 00:00:01", "31-12-2021 23:59:59"] -scan_duration = [50, 300] -scans = [30, 72] -interval_length = 1200 +layout = "vla" +img_size = 128 +fov_center_ra = [100, 110] +fov_center_dec = [30, 40] +fov_size = 100 # max res 0.1 +corr_int_time = 30.0 +scan_start = ["16-01-2020 00:04:01", "16-01-2020 08:59:59"] +scan_duration = [60, 90] +scans = [1, 2] +interval_length = 360 base_freq = 15.21e9 frequsel = [0e8, 0.8e8, 1.44e8, 2.08e8] bandwidths = [6.4e7, 6.4e7, 6.4e7, 6.4e7] +corrupted = true [bundle_options] -num_bundles = 5 -size_bundles = 10 -in_path = "../../../test_radiosim/build/test_data" -out_path = "./build" - -[cluster_options] -num_jobs = 4 \ No newline at end of file +in_path = "build/skies/" +out_path_fits = "build/uvfits" +out_path_gridded = "build/gridded" +num_test_images = 500 +bundle_size = 100 +train_valid_split = 0.2 +grid_size = 128 +amp_phase = true diff --git a/examples/01_layouts.ipynb b/examples/01_layouts.ipynb new file mode 100644 index 0000000..75efda4 --- /dev/null +++ b/examples/01_layouts.ipynb @@ -0,0 +1,322 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 1, + "id": "5b65f63b", + "metadata": {}, + "outputs": [], + "source": [ + "from pyvisgen.layouts.layouts import get_array_layout, Stations" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "dae31ddc", + "metadata": {}, + "outputs": [], + "source": [ + "vlba_layout = get_array_layout(\"vlba\")" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "48586eb1", + "metadata": {}, + "outputs": [], + "source": [ + "vla_layout = get_array_layout(\"vla\")" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "1813010a", + "metadata": {}, + "outputs": [], + "source": [ + "eht_layout = get_array_layout(\"eht\")" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "1398748a", + "metadata": {}, + "outputs": [], + "source": [ + "from pyvisgen.simulation.utils import calc_ref_elev\n", + "from datetime import datetime\n", + "import pandas as pd\n", + "import numpy as np\n", + "import astropy.units as un\n", + "from astropy.time import Time\n", + "from astropy.coordinates import SkyCoord\n", + "import matplotlib.pyplot as plt" + ] + }, + { + "cell_type": "code", + "execution_count": 17, + "id": "ae966407", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "datetime.datetime(2021, 6, 22, 18, 0, 1)" + ] + }, + "execution_count": 17, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "start_time_l = datetime.strptime(\"01-01-2020 00:00:01\", \"%d-%m-%Y %H:%M:%S\")\n", + "start_time_h = datetime.strptime(\"31-12-2021 23:59:59\", \"%d-%m-%Y %H:%M:%S\")\n", + "start_times = pd.date_range(\n", + " start_time_l,\n", + " start_time_h,\n", + " freq=\"1h\",\n", + ").strftime(\"%d-%m-%Y %H:%M:%S\")\n", + "scan_start = np.random.choice(\n", + " [datetime.strptime(time, \"%d-%m-%Y %H:%M:%S\") for time in start_times]\n", + ")\n", + "scan_start" + ] + }, + { + "cell_type": "code", + "execution_count": 18, + "id": "15c4d443", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "(39,)" + ] + }, + "execution_count": 18, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "start_time = Time(scan_start.isoformat(), format=\"isot\")\n", + "interval = 3600\n", + "num_scans = 3\n", + "scan_duration = 360\n", + "int_time = 30\n", + "\n", + "time_lst = [\n", + " start_time + interval * i * un.second + j * int_time * un.second\n", + " for i in range(num_scans)\n", + " for j in range(int(scan_duration / int_time) + 1)\n", + "]\n", + "# +1 because t_1 is the stop time of t_0\n", + "# in order to save computing power we take one time more to complete interval\n", + "time = Time(time_lst)\n", + "time.shape" + ] + }, + { + "cell_type": "code", + "execution_count": 19, + "id": "ae6e0fd1", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "[]" + ] + }, + "execution_count": 19, + "metadata": {}, + "output_type": "execute_result" + }, + { + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": { + "needs_background": "light" + }, + "output_type": "display_data" + } + ], + "source": [ + "plt.figure(figsize=(12,8))\n", + "plt.plot(time.datetime, np.ones(time.shape), marker=\".\", linestyle=\"none\")" + ] + }, + { + "cell_type": "code", + "execution_count": 20, + "id": "b164b0bc", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "[[1. 1. 1. 1. 1. 1. 1. 1. 1. 1.]\n", + " [1. 1. 1. 1. 1. 1. 1. 1. 1. 1.]\n", + " [1. 1. 1. 1. 1. 1. 1. 1. 1. 1.]\n", + " [1. 1. 1. 1. 1. 1. 1. 1. 1. 1.]\n", + " [1. 1. 1. 1. 1. 1. 1. 1. 1. 1.]\n", + " [1. 1. 1. 1. 1. 1. 1. 1. 1. 1.]\n", + " [1. 1. 1. 1. 1. 1. 1. 1. 1. 1.]\n", + " [1. 1. 1. 1. 1. 1. 1. 1. 1. 1.]\n", + " [1. 1. 1. 1. 1. 1. 1. 1. 1. 1.]\n", + " [1. 1. 1. 1. 1. 1. 1. 1. 1. 1.]\n", + " [1. 1. 1. 1. 1. 1. 1. 1. 1. 1.]\n", + " [1. 1. 1. 1. 1. 1. 1. 1. 1. 1.]\n", + " [1. 1. 1. 1. 1. 1. 1. 1. 1. 1.]\n", + " [1. 1. 1. 1. 0. 1. 1. 1. 1. 1.]\n", + " [1. 1. 1. 1. 0. 1. 1. 1. 1. 1.]\n", + " [1. 1. 1. 1. 0. 1. 1. 1. 1. 1.]\n", + " [1. 1. 1. 1. 0. 1. 1. 1. 1. 1.]\n", + " [1. 1. 1. 1. 0. 1. 1. 1. 1. 1.]\n", + " [1. 1. 1. 1. 0. 1. 1. 1. 1. 1.]\n", + " [1. 1. 1. 1. 0. 1. 1. 1. 1. 1.]\n", + " [1. 1. 1. 1. 0. 1. 1. 1. 1. 1.]\n", + " [1. 1. 1. 1. 0. 1. 1. 1. 1. 1.]\n", + " [1. 1. 1. 1. 0. 1. 1. 1. 1. 1.]\n", + " [1. 1. 1. 1. 0. 1. 1. 1. 1. 1.]\n", + " [1. 1. 1. 1. 0. 1. 1. 1. 1. 1.]\n", + " [1. 1. 1. 1. 0. 1. 1. 1. 1. 1.]\n", + " [1. 1. 1. 0. 0. 1. 1. 1. 1. 1.]\n", + " [1. 1. 1. 0. 0. 1. 1. 1. 1. 1.]\n", + " [1. 1. 1. 0. 0. 1. 1. 1. 1. 1.]\n", + " [1. 1. 1. 0. 0. 1. 1. 1. 1. 1.]\n", + " [1. 1. 1. 0. 0. 1. 1. 1. 1. 1.]\n", + " [1. 1. 1. 0. 0. 1. 1. 1. 1. 1.]\n", + " [1. 1. 1. 0. 0. 1. 1. 1. 1. 1.]\n", + " [1. 1. 1. 0. 0. 1. 1. 1. 1. 1.]\n", + " [1. 1. 1. 0. 0. 1. 1. 1. 1. 1.]\n", + " [1. 1. 1. 0. 0. 1. 1. 1. 1. 1.]\n", + " [1. 1. 1. 0. 0. 1. 1. 1. 1. 1.]\n", + " [1. 1. 1. 0. 0. 1. 1. 1. 1. 1.]\n", + " [1. 1. 1. 0. 0. 1. 1. 1. 1. 1.]]\n" + ] + } + ], + "source": [ + "src_crd = SkyCoord(\n", + " ra=240,\n", + " dec=50,\n", + " unit=(un.deg, un.deg),\n", + ")\n", + "\n", + "array_layout = get_array_layout(\"vlba\")\n", + "\n", + "_, el_st_all = calc_ref_elev(src_crd, time, array_layout)\n", + "\n", + "el_min = 15\n", + "el_max = 85\n", + "\n", + "valid = np.where((el_st_all >= el_min) & (el_st_all <= el_max), np.zeros(el_st_all.shape), 1)\n", + "print(valid)\n", + "telescopes = valid * (np.arange(10) + 1)" + ] + }, + { + "cell_type": "code", + "execution_count": 21, + "id": "bf876195", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "Text(0.5, 0, 'Time')" + ] + }, + "execution_count": 21, + "metadata": {}, + "output_type": "execute_result" + }, + { + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAAAs0AAAHgCAYAAABelVD0AAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjQuMiwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy8rg+JYAAAACXBIWXMAAAsTAAALEwEAmpwYAAAikUlEQVR4nO3de5SkaV0f8O+vu3c2y0XAYeQwMLre0BCDKK1xvGXiGCV4AYWNGM3B20Gjx4DEBEw8B3PhRKLBSzQxGy+QeAFXMHgQuZwNHTVnhPQALjdZCYINs8qwqCCu9ND75I+3hunt6a6nZrarq3rq8zmnT1U971Pv+9TT7/zmW1VPV1VrLQAAwN6WZj0AAACYd0IzAAB0CM0AANAhNAMAQIfQDAAAHUIzAAB0rMx6AJN48IMf3G688cZZDwMAgGvc2bNn399aO7az/VCE5htvvDHr6+uzHgYAANe4qnr3bu2WZwAAQIfQDAAAHUIzAAB0CM0AANAhNAMAQIfQDAAAHUIzAAB0CM0AANAhNAMAQIfQDAAAHUIzAAB0CM0AANAhNAMAQIfQDAAAHVMLzVX181X1vqp687a2j6+qV1fVH44uHzSt4wMAwH6Z5ivNz0/y2B1tz0pya2vt05PcOroNAABzbWVaO26t/XZV3bij+fFJTo2uvyDJWpJnTmsM98q5M8nGWnLi1HB7Yy254Why1527t43bdjX7OH5ymo8OOAymXYd6/dUhYFwd2u+as1vbHNWhqYXmPTyktXZHkrTW7qiqTzjg40/m3JnkltPJ1maytJykkq0LSe5OspQsr+xoqyRtj22Ttm3bx8r1yU23ztWJAhywqdehTn91CBhbh/a55hyCOjS3fwhYVU+tqvWqWj9//vzBHnxjbThB2tbwy9vazPALzHB5WVsbs23Stu372BzGACyujbUp16Fef3UIFt7G2pg6tN81Z/7r0EGH5j+tqocmyejyfXt1bK3d3Fpbba2tHjt27MAGmGR4O2D5SFLLyfJ1w/WLU1VLl7el9t42ads99nHk0tsUwGKadh3q9leHYOGNq0P7XnPmvw4d9PKM30jylCQ/PLp86QEffzLHTw5vB2ysWdMMzMZB1KFDtJYQmIFeHVqwNc3VWuv3upodV/1Khj/6e3CSP03y7CT/M8mvJvnEJH+c5KbW2gd6+1pdXW3r6+tTGScAAFxUVWdba6s726f56RnfuMem09M6JgAATMPc/iEgAADMC6EZAAA6hGYAAOgQmgEAoENoBgCADqEZAAA6hGYAAOgQmgEAoENoBgCADqEZAAA6hGYAAOgQmgEAoENoBgCADqEZAAA6hGYAAOgQmgEAoENoBgCADqEZAAA6hGYAAOgQmgEAoENoBgCADqEZAAA6hGYAAOgQmgEAoENoBgCADqEZAAA6hGYAAOgQmgEAoENoBgCADqEZAAA6hGYAAOgQmgEAoGNl1gOYW+fOJBtryYlTw+2NteSGo8ldd+7eNm7b1ezj+MlpPjrgMJh2Her1V4eAcXVov2vObm1zVIeE5t2cO5PccjrZ2kyWlpNUsnUhyd1JlpLllR1tlaTtsW3Stm37WLk+uenWuTpRgAM29TrU6a8OAWPr0D7XnENQhyzP2M3G2nCCtK3hl7e1meEXmOHysrY2Ztukbdv3sTmMAVhcG2tTrkO9/uoQLLyNtTF1aL9rzvzXIa807+bEqWT5yO7PrGopWdrj2dNu2yZt276P5SOX3qYAFtO061CvvzoEjKtD+11zDkEdqtZav9eMra6utvX19YM9qDXNwKxZ0wzM2gKuaa6qs6211cvahWYAABjsFZqtaQYAgA6hGQAAOoRmAADoEJoBAKBDaAYAgA6hGQAAOoRmAADoEJoBAKBDaAYAgA6hGQAAOoRmAADoEJoBAKBDaAYAgA6hGQAAOoRmAADoEJoBAKBDaAYAgA6hGQAAOoRmAADoEJoBAKBDaAYAgA6hGQAAOoRmAADoEJoBAKBDaAYAgA6hGQAAOoRmAADoEJoBAKBDaAYAgA6hGQAAOoRmAADoEJoBAKBjJqG5qr6vqt5SVW+uql+pqr8xi3EAAMAkDjw0V9XDkvzTJKuttc9KspzkyQc9DgAAmNTKDI97Q1VdSHKfJOdmNI69nTuTbKwlJ04NtzfWkhuOJnfduXvbuG1Xs4/jJ6f56IDDYNp1qNdfHQLG1aH9rjm7tc1RHTrw0Nxae29V/WiSP05yV5JXtdZeddDjGOvcmeSW08nWZrK0nKSSrQtJ7k6ylCyv7GirJG2PbZO2bdvHyvXJTbfO1YkCHLCp16FOf3UIGFuH9rnmHII6NIvlGQ9K8vgkn5zkeJL7VtU379LvqVW1XlXr58+fP9hBbqwNJ0jbGn55W5sZfoEZLi9ra2O2Tdq2fR+bwxiAxbWxNuU61OuvDsHC21gbU4f2u+bMfx2axR8CfnmSP2qtnW+tXUjykiRfuLNTa+3m1tpqa2312LFjBzvCE6eS5SNJLSfL1w3XL05VLV3eltp726Rt99jHkUtvUwCLadp1qNtfHYKFN64O7XvNmf86NIs1zX+c5Auq6j4ZlmecTrI+g3Hs7fjJ4e2AjTVrmoHZOIg6dIjWEgIz0KtDC7amuVpr/V77fdCqf53kG5J8NMkbknxHa+0je/VfXV1t6+vzlasBALj2VNXZ1trqzvaZfHpGa+3ZSZ49i2MDAMCV8o2AAADQITQDAECH0AwAAB1CMwAAdAjNAADQITQDAECH0AwAAB1CMwAAdAjNAADQITQDAECH0AwAAB1CMwAAdAjNAADQITQDAECH0AwAAB1CMwAAdAjNAADQITQDAECH0AwAAB1CMwAAdAjNAADQITQDAECH0AwAAB1CMwAAdAjNAADQITQDAECH0AwAAB1CMwAAdAjNAADQITQDAECH0AwAAB1CMwAAdKzMegBz69yZZGMtOXFquL2xltxwNLnrzt3bxm27mn0cPznNRwccBtOuQ73+6hAwrg7td83ZrW2O6pDQvJtzZ5JbTidbm8nScpJKti4kuTvJUrK8sqOtkrQ9tk3atm0fK9cnN906VycKcMCmXoc6/dUhYGwd2ueacwjqkOUZu9lYG06QtjX88rY2M/wCM1xe1tbGbJu0bfs+NocxAItrY23KdajXXx2ChbexNqYO7XfNmf865JXm3Zw4lSwf2f2ZVS0lS3s8e9pt26Rt2/exfOTS2xTAYpp2Her1V4eAcXVov2vOIahD1Vrr95qx1dXVtr6+frAHtaYZmDVrmoFZW8A1zVV1trW2elm70AwAAIO9QrM1zQAA0CE0AwBAh9AMAAAdQjMAAHQIzQAA0CE0AwBAh9AMAAAdQjMAAHQIzQAA0CE0AwBAh9AMAAAdQjMAAHQIzQAA0CE0AwBAh9AMAAAdQjMAAHQIzQAA0CE0AwBAh9AMAAAdQjMAAHQIzQAA0CE0AwBAh9AMAAAdQjMAAHQIzQAA0CE0AwBAh9AMAAAdQjMAAHQIzQAA0CE0AwBAh9AMAAAdQjMAAHTMJDRX1QOr6teq6g+q6m1VdXIW4wAAgEmszOi4P5HkFa21J1XVkST3mdE4AACg68BDc1V9XJIvTfItSdJa20yyedDj6Dp3JtlYS06cGm5vrCU3HE3uunP3tnHbrmYfx734Dgtv2nWo118dAsbVof2uObu1zVEdmsUrzZ+S5HySX6iqz05yNsnTWmsfnsFYdnfuTHLL6WRrM1laTlLJ1oUkdydZSpZXdrRVkrbHtknbtu1j5frkplvn6kQBDtjU61CnvzoEjK1D+1xzDkEdmsWa5pUkn5vkv7TWPifJh5M8a2enqnpqVa1X1fr58+cPdoQba8MJ0raGX97WZoZfYIbLy9ramG2Ttm3fx+YwBmBxbaxNuQ71+qtDsPA21sbUof2uOfNfh2YRmt+T5D2ttdeObv9ahhB9D621m1trq6211WPHjh3oAHPiVLJ8JKnlZPm64frFqaqly9tSe2+btO0e+zhy6W0KYDFNuw51+6tDsPDG1aF9rznzX4cOfHlGa+1Pqmqjqj6jtfb2JKeTvPWgxzHW8ZPD2wEba9Y0A7NxEHXoEK0lBGagV4cWbE1ztdb6vfb7oFWPTvKzSY4keWeSb22t/dle/VdXV9v6+voBjQ4AgEVVVWdba6s722fykXOttTcmuWwwAAAwj3wjIAAAdAjNAADQITQDAECH0AwAAB1CMwAAdAjNAADQITQDAECH0AwAAB1CMwAAdAjNAADQITQDAECH0AwAAB3d0FxVj6iqW6vqzaPbj6qqH5z+0AAAYD5M8krzf0vyA0kuJElr7bYkT57moAAAYJ5MEprv01p73Y62j05jMAAAMI8mCc3vr6pPTdKSpKqelOSOqY4KAADmyMoEfb4nyc1JPrOq3pvkj5J801RHBQAAc6Qbmltr70zy5VV13yRLrbUPTX9YAAAwPyb59IyjVfWTSX4nyVpV/URVHZ3+0AAAYD5Msqb5hUnOJ3likieNrr9omoMCAIB5Msma5o9vrf3bbbf/XVU9YUrjAQCAuTPJK82vqaonV9XS6OcfJvnNaQ8MAADmxSSh+TuT/HKSzdHPC5M8o6o+VFUfnObgAABgHkzy6Rn3P4iBAADAvJpkTXOq6muTfOno5lpr7WXTGxIAAMyXST5y7oeTPC3JW0c/Txu1AQDAQpjklebHJXl0a+3uJKmqFyR5Q5JnTXNgAAAwLyb5Q8AkeeC26w+YwjgAAGBuTfJK879P8oaqek2SyrC2+QemOioAAJgjk3x6xq9U1VqSz8sQmp/ZWvuTaQ8MAADmxSR/CPh1Sf6qtfYbrbWXJvlr3wgIAMAimWRN87Nba39x8UZr7c+TPHtqIwIAgDkzyZrm3YL1RJ/vfKidO5NsrCUnTg23N9aSG44md925e9u4bVezj+Mnp/noAAC4ApOE3/Wqel6Sn07SknxvkrNTHdWsnTuT3HI62dpMlpaTVLJ1IcndSZaS5ZUdbZVhanbbNmnbtn2sXJ/cdKvgDAAwJyZZnvG9STaTvCjJLUn+Osn3THNQM7exNgTmtjWE2q3NDME2w+VlbW3Mtknbtu9jcxgDAABzYZJPz/hwRl9kUlXLSe47art2nTiVLB/Z/ZXmWkqW9niVeLdtk7Zt38fykUvLNwAAmLluaK6qX07yXUm2MizLeEBVPa+19iPTHtzMHD85LI/YWLOmGQCAVGttfIeqN7bWHl1V35TkMUmemeRsa+1RBzHAJFldXW3r6+sHdTgAABZUVZ1tra3ubJ9kTfN1VXVdkickeWlr7UIuLcAFAIBr3iSh+b8meVeS+yb57ar6pCQfnOagAABgnkzyh4A/meQntzW9u6r+3vSGBAAA82WSr9F+SFX9XFX91uj2I5M8ZeojAwCAOTHJ8oznJ3llkuOj27cnefqUxgMAAHNnktD84Nbar2b0LRyttY9m+Pg5AABYCJOE5g9X1dGMPjGjqr4gyV9MdVQAADBHun8ImOQZSX4jyadW1f9JcizJk6Y6KgAAmCOTfHrG66vq7yb5jAzf9fz20Wc1AwDAQtgzNFfV1++x6RFVldbaS6Y0JgAAmCvjXmn+mjHbWhKhGQCAhbBnaG6tfetBDgQAAObVVX25SVV9+/SHBgAA88GXmwAAQMeeobmqLi7d8OUmAAAstHGvNL9udOnLTQAAWGjjPj2jRpe+3AQAgIU2LjQfq6pnjK7/epKXZwjSH0ny5Ulum/LYAABgLowLzctJ7pdLrzhfdJ/pDQcAAObPuNB8R2vt3xzYSAAAYE6N+0PAna8wAwDAQhoXmk8f2CgAAGCO7RmaW2sfOMiBAADAvJrkGwEBAGChCc0AANAhNAMAQIfQDAAAHUIzAAB0CM0AANAhNAMAQIfQDAAAHUIzAAB0zCw0V9VyVb2hql42qzEAAMAkZvlK89OSvG2GxwcAgImszOKgVfXwJF+V5DlJnjGLMXSdO5NsrCUnTg23N9aSG44md925e9u4bVezj+Mnp/nogMNg2nWo118dAviYmYTmJD+e5F8kuf+Mjj/euTPJLaeTrc1kaTlJJVsXktydZClZXtnRVknaHtsmbdu2j5Xrk5tu9R8WLLKp16FOf3UI4B4OfHlGVX11kve11s52+j21qtarav38+fMHNLqRjbXhP6q2NfwnsrWZ4T+SDJeXtbUx2yZt276PzWEMwOLaWJtyHer1V4cAtpvFmuYvSvK1VfWuJC9M8mVV9Ys7O7XWbm6trbbWVo8dO3awIzxxKlk+ktRysnzdcP3iVNXS5W2pvbdN2naPfRy59HYpsJimXYe6/dUhgO2qtdbvNa2DV51K8v2tta8e1291dbWtr68fyJg+xppmYNasaQY4cFV1trW2elm70AwAAIO9QvOs/hAwSdJaW0uyNssxAABAj28EBACADqEZAAA6hGYAAOgQmgEAoENoBgCADqEZAAA6hGYAAOgQmgEAoENoBgCADqEZAAA6hGYAAOgQmgEAoENoBgCADqEZAAA6hGYAAOgQmgEAoENoBgCADqEZAAA6hGYAAOgQmgEAoENoBgCADqEZAAA6hGYAAOgQmgEAoENoBgCADqEZAAA6hGYAAOgQmgEAoENoBgCADqEZAAA6hGYAAOgQmgEAoGNl1gOYW+fOJBtryYlTw+2NteSGo8ldd+7eNm7b1ezj+MlpPjrgMJh2Her1V4eAcXVov2vObm1zVIeE5t2cO5PccjrZ2kyWlpNUsnUhyd1JlpLllR1tlaTtsW3Stm37WLk+uenWuTpRgAM29TrU6a8OAWPr0D7XnENQhyzP2M3G2nCCtK3hl7e1meEXmOHysrY2Ztukbdv3sTmMAVhcG2tTrkO9/uoQLLyNtTF1aL9rzvzXIa807+bEqWT5yO7PrGopWdrj2dNu2yZt276P5SOX3qYAFtO061CvvzoEjKtD+11zDkEdqtZav9eMra6utvX19YM9qDXNwKxZ0wzM2gKuaa6qs6211cvahWYAABjsFZqtaQYAgA6hGQAAOoRmAADoEJoBAKBDaAYAgA6hGQAAOoRmAADoEJoBAKBDaAYAgA6hGQAAOoRmAADoEJoBAKBDaAYAgA6hGQAAOoRmAADoEJoBAKBDaAYAgA6hGQAAOoRmAADoEJoBAKBDaAYAgA6hGQAAOoRmAADoEJoBAKBDaAYAgA6hGQAAOoRmAADoEJoBAKBDaAYAgA6hGQAAOoRmAADoEJoBAKDjwENzVZ2oqtdU1duq6i1V9bSDHgMAAFyJlRkc86NJ/llr7fVVdf8kZ6vq1a21t85gLAAA0HXgobm1dkeSO0bXP1RVb0vysCTzFZrPnUk21pITp4bbG2vJDUeTu+7cvW3ctqvZx/GT03x0wGEw7TrU668OAePq0H7XnN3a5qgOzeKV5o+pqhuTfE6S185yHJc5dya55XSytZksLSepZOtCkruTLCXLKzvaKknbY9ukbdv2sXJ9ctOtc3WiAAds6nWo018dAsbWoX2uOYegDs3sDwGr6n5JXpzk6a21D+6y/alVtV5V6+fPnz/YwW2sDSdI2xp+eVubGX6BGS4va2tjtk3atn0fm8MYgMW1sTblOtTrrw7BwttYG1OH9rvmzH8dmklorqrrMgTmX2qtvWS3Pq21m1trq6211WPHjh3sAE+cSpaPJLWcLF83XL84VbV0eVtq722Ttt1jH0cuvU0BLKZp16Fuf3UIFt64OrTvNWf+61C11vq99vOAVZXkBUk+0Fp7+iT3WV1dbevr61Md12WsaQZmzZpmYNYWcE1zVZ1tra1e1j6D0PzFSX4nyZty6TX4f9lae/le95lJaAYAYOHsFZpn8ekZv5uPvUYPAADzzzcCAgBAh9AMAAAdQjMAAHQIzQAA0CE0AwBAh9AMAAAdQjMAAHQIzQAA0CE0AwBAh9AMAAAdQjMAAHQIzQAA0CE0AwBAh9AMAAAdQjMAAHQIzQAA0CE0AwBAh9AMAAAdQjMAAHQIzQAA0CE0AwBAh9AMAAAdQjMAAHQIzQAA0CE0AwBAh9AMAAAdQjMAAHQIzQAA0CE0AwBAh9AMAAAdQjMAAHQIzQAA0LEy6wHMrXNnko215MSp4fbGWnLD0eSuO3dvG7ftavZx/OQ0Hx1wGEy7DvX6q0PAuDq03zVnt7Y5qkNC827OnUluOZ1sbSZLy0kq2bqQ5O4kS8nyyo62StL22DZp27Z9rFyf3HTrXJ0owAGbeh3q9FeHgLF1aJ9rziGoQ5Zn7GZjbThB2tbwy9vazPALzHB5WVsbs23Stu372BzGACyujbUp16Fef3UIFt7G2pg6tN81Z/7rkFead3PiVLJ8ZPdnVrWULO3x7Gm3bZO2bd/H8pFLb1MAi2nadajXXx0CxtWh/a45h6AOVWut32vGVldX2/r6+sEe1JpmYNasaQZmbQHXNFfV2dba6mXtQjMAAAz2Cs3WNAMAQIfQDAAAHUIzAAB0CM0AANAhNAMAQIfQDAAAHUIzAAB0CM0AANAhNAMAQIfQDAAAHUIzAAB0CM0AANAhNAMAQIfQDAAAHUIzAAB0CM0AANAhNAMAQIfQDAAAHUIzAAB0CM0AANAhNAMAQIfQDAAAHUIzAAB0CM0AANAhNAMAQIfQDAAAHUIzAAB0CM0AANAhNAMAQIfQDAAAHUIzAAB0CM0AANAxk9BcVY+tqrdX1Tuq6lmzGAMAAEzqwENzVS0n+ekk/yDJI5N8Y1U98qDHAQAAk1qZwTE/P8k7WmvvTJKqemGSxyd56wzGAjC/zp1JNtaSE6eG2xtryQ1Hk7vu3L1t3Lar6X/85DQfHXAY3HZzcvuLk0c8cbh9+4uT+xxL/ur8pcvdtu1H/0c8MXnUUw/28Y4xi9D8sCQb226/J8nfmcE4AObXuTPJLaeTrc1kaTlJJVsXktydZClZXtnRVknaHtuuov/K9clNtwrOsMhuuzl59XcO19/9qr377bZtP/pfbJuT4DyLNc21S1u7rFPVU6tqvarWz58/fwDDApgjG2tDYG5bQ5jd2swQaDNcXtbWxmy7mv6bwxiAxXX7i2c9gvkYw8gsQvN7kpzYdvvhSc7t7NRau7m1ttpaWz127NiBDQ5gLpw4lSwfSWo5Wb5uuH6xZNfS5W0XX4/YbdtV9T9yadkGsJguLplY9DGMzGJ5xv9N8ulV9clJ3pvkyUn+0QzGATC/jp8clkdsrFnTDMzGxWUR1jQnSaq1y1ZGTP+gVY9L8uNJlpP8fGvtOeP6r66utvX19YMYGgAAC6yqzrbWVne2z+KV5rTWXp7k5bM4NgAAXCnfCAgAAB1CMwAAdAjNAADQITQDAECH0AwAAB1CMwAAdAjNAADQITQDAECH0AwAAB1CMwAAdAjNAADQITQDAECH0AwAAB1CMwAAdAjNAADQUa21WY+hq6rOJ3n3DA794CTvn8FxDxNzNJ756TNH45mfPnM0nvnpM0fjLdr8fFJr7djOxkMRmmelqtZba6uzHsc8M0fjmZ8+czSe+ekzR+OZnz5zNJ75GVieAQAAHUIzAAB0CM3j3TzrARwC5mg889NnjsYzP33maDzz02eOxjM/saYZAAC6vNIMAAAd10RorqoTVfWaqnpbVb2lqp62bduPVNUfVNVtVfXrVfXAPfbxiqr686p62Y7201X1+qp6Y1X9blV92h73f0pV/eHo5ynb2j+5ql47an9RVR3Zp4c9sTmZn73u//yq+qPR/d9YVY++94/4ys16jqrqk6rq7KjPW6rqu7Ztcw5d6vtxVfXeqvqpbW2LcA592WiO3lxVL6iqlT3uv6h1aNL5WeQ61J2jBa9DE51Do77XbB2qqkdX1ZnRfW+rqm/Ytm2ic2Ce69C91lo79D9JHprkc0fX75/k9iSPHN3+iiQro+vPTfLcPfZxOsnXJHnZjvbbk/zN0fXvTvL8Xe778UneObp80Oj6g0bbfjXJk0fXfybJP1m0+enc//lJnuQcypEk14+u3y/Ju5Icdw5dto+fSPLLSX5qUc6hDC9ubCR5xOj2v0ny7bvcdyHr0KTz0zkHnUNtcevQlZxDo+3XbB1K8ogknz66fjzJHUkeOOk5kDmvQ/f255p4pbm1dkdr7fWj6x9K8rYkDxvdflVr7aOjrr+X5OF77OPWJB/abVOSjxtdf0CSc7v0+cokr26tfaC19mdJXp3ksVVVSb4sya+N+r0gyROu7NHde3MwP+PuPxdmPUettc3W2kdGN6/P6F0g59AlVfWYJA9J8qqrfBhTNcU5OprkI62120e3X53kibvcfVHr0KTzs8h1aKI5WuA6NPE5dK3Xodba7a21PxxdP5fkfUmOXcE5MNd16N66JkLzdlV1Y5LPSfLaXTZ/W5LfusJdfkeSl1fVe5L84yQ/PDrOalX97KjPwzI8S73oPaO2o0n+fNtJerF9ZmY0Pz3PGb0N9GNVdf0VHn/fzWqORm+r3ZbhXHruqGA5h4brS0n+Y5J/vsc+ruVz6P1Jrquqi18s8KQkJ0bHUYcmn58e51AWtg5NND+LVoeq6vMzvPvw/zLmHDisdehqXFOhuarul+TFSZ7eWvvgjm3/KslHk/zSFe72+5I8rrX28CS/kOR5SdJaW2+tfcfF3e9yvzamfSZmOD/j/ECSz0zyeRneznnmFR5/X81yjlprG621RyX5tCRPqaqHxDl0cX6+O8nLW2sbu9z/mj6HWmstyZOT/FhVvS7Dq2QfHW1b+Dp0BfMzjnPoUt+Fq0NXMD8LU4eq6qFJ/keSb22t3Z0x58BhrENXa8+F7odNVV2X4QT5pdbaS3Zse0qSr05yevSPY9J9Hkvy2a21i8/SXpTkFbt0fU+SU9tuPzzJWoZnrw+sqpXRs6uHZ4+3nqdtxvOzp9baHaOrH6mqX0jy/Vdy//00L3PUWjtXVW9J8iWj8TiHkpNJvqSqvjvDWssjVfWXrbVnXevnUJK01s5kOB9SVV+RYd3hTgtZh5KJ52fc/Z1Dl/dfmDqUTDw/C1GHqurjkvxmkh9srf3eqHnSOjL3dejeuCZeaR6tlfm5JG9rrT1vx7bHZnjG97Wttb+6wl3/WZIHVNXFfzx/P8P6oJ1emeQrqupBVfWgDIvtXzk6IV+T4a2eJHlKkpde4RjutTmYn3Fje+i2MT4hyZuvcAz7YtZzVFUPr6obRtcflOSLkrzdOTRorX1Ta+0TW2s3ZvjP6L+31p41Ov61fg6lqj5hdHn9aD8/s0u3Ra1Dk87PuPs7h7LQdWii+VmEOlTDJ1r8eobHdsvF9is4B+a6Dt1rbQ7+GvHe/iT54gwv89+W5I2jn8eNtr0jw/qai+0/s8c+fifJ+SR3ZXim9JWj9q9L8qYkv5/h2dKnjNpXk/zstvt/2+hY78jwdsbF9k9J8rpR+y0Z/WXyAs7PXvf/X6P7vznJLya53yKeQxnC4m2jPrcleapz6J7n0Lb9fEvu+Vfri3AO/UiGJxNvz/B268X+6tCVzc8i16HuHGWx69BE59C29m/JNViHknxzkgvb+rwxyaPHnQM75yhzXIfu7Y9vBAQAgI5rYnkGAABMk9AMAAAdQjMAAHQIzQAA0CE0AwBAh9AMcAhU1dGqeuPo50+q6r2j639ZVf951uMDuNb5yDmAQ6aqfijJX7bWfnTWYwFYFF5pBjjEqupUVb1sdP2HquoFVfWqqnpXVX19Vf2HqnpTVb1i9PW6qarHVNX/rqqzVfXKi99mBsDehGaAa8unJvmqJI/P8M1kr2mt/e0M34D2VaPg/J+SPKm19pgkP5/kObMaLMBhsTLrAQCwr36rtXahqt6UZDnJK0btb0pyY5LPSPJZSV5dVRn1uWMG4wQ4VIRmgGvLR5KktXZ3VV1ol/5w5e4MNb+SvKW1dnJWAwQ4jCzPAFgsb09yrKpOJklVXVdVf2vGYwKYe0IzwAJprW0meVKS51bV7yd5Y5IvnOmgAA4BHzkHAAAdXmkGAIAOoRkAADqEZgAA6BCaAQCgQ2gGAIAOoRkAADqEZgAA6BCaAQCg4/8DZBerTUIK3G0AAAAASUVORK5CYII=\n", + "text/plain": [ + "
" + ] + }, + "metadata": { + "needs_background": "light" + }, + "output_type": "display_data" + } + ], + "source": [ + "plt.figure(figsize=(12,8))\n", + "plt.plot(time.datetime, telescopes, color=\"darkorange\", marker=\".\", linestyle=\"none\")\n", + "\n", + "plt.ylabel(\"Telescope\")\n", + "plt.xlabel(\"Time\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "f13d9598", + "metadata": {}, + "outputs": [], + "source": [] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "c955440f", + "metadata": {}, + "outputs": [], + "source": [] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "76bee522", + "metadata": {}, + "outputs": [], + "source": [] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d837c212", + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.9.7" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/examples/02_times.ipynb b/examples/02_times.ipynb new file mode 100644 index 0000000..d828788 --- /dev/null +++ b/examples/02_times.ipynb @@ -0,0 +1,427 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 1, + "id": "384f9bbd", + "metadata": {}, + "outputs": [], + "source": [ + "from pyvisgen.simulation.utils import calc_ref_elev\n", + "from datetime import datetime\n", + "import pandas as pd\n", + "import numpy as np\n", + "import astropy.units as un\n", + "from astropy.time import Time\n", + "from astropy.coordinates import SkyCoord\n", + "import matplotlib.pyplot as plt\n", + "from pyvisgen.layouts.layouts import get_array_layout" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "f5d84015", + "metadata": {}, + "outputs": [], + "source": [ + "# try different observation dates\n", + "scan_start = datetime.strptime(\"18-1-2021 6:0:1\", \"%d-%m-%Y %H:%M:%S\")" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "16b44ffe", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "(25,)" + ] + }, + "execution_count": 3, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "start_time = Time(scan_start.isoformat(), format=\"isot\")\n", + "\n", + "# try different intervals and integration times / units are seconds\n", + "interval = 360\n", + "num_scans = 1\n", + "scan_duration = 360\n", + "int_time = 15\n", + "\n", + "time_lst = [\n", + " start_time + interval * i * un.second + j * int_time * un.second\n", + " for i in range(num_scans)\n", + " for j in range(int(scan_duration / int_time) + 1)\n", + "]\n", + "time = Time(time_lst)\n", + "time.shape" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "c748d88e", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "[]" + ] + }, + "execution_count": 4, + "metadata": {}, + "output_type": "execute_result" + }, + { + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": { + "needs_background": "light" + }, + "output_type": "display_data" + } + ], + "source": [ + "# plot time steps\n", + "plt.figure(figsize=(12,8))\n", + "plt.plot(time.datetime, np.ones(time.shape), marker=\".\", linestyle=\"none\")" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "6588f902", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "[59.45441696 59.42492996 59.43560109 59.44401955 59.4355367 59.41870372\n", + " 59.43547195 59.43044743 59.43735472 59.45068632 59.43911547 59.45855358\n", + " 59.42215729 59.43549136 59.41492211 59.43553383 59.43512177 59.43560022\n", + " 59.43613891 59.44134061 59.43403561 59.43552479 59.43557475 59.43555851\n", + " 59.43554835 59.428052 59.43245418 59.44713497]\n" + ] + } + ], + "source": [ + "# try different source coordinates\n", + "src_crd = SkyCoord(\n", + " ra=137,\n", + " dec=34,\n", + " unit=(un.deg, un.deg),\n", + ")\n", + "\n", + "array_layout = get_array_layout(\"vla\")\n", + "\n", + "_, el_st_all = calc_ref_elev(src_crd, time, array_layout)\n", + "print(el_st_all[0])\n", + "\n", + "# this is fixed for the telescopes\n", + "el_min = 15\n", + "el_max = 75\n", + "\n", + "valid = np.where((el_st_all >= el_min) & (el_st_all <= el_max), np.ones(el_st_all.shape), 0)\n", + "telescopes = valid * (np.arange(28) + 1)" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "id": "cc3eef79", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "array([70.8706793 , 70.93335042, 70.99602154, 71.05869265, 71.12136377,\n", + " 71.18403488, 71.246706 , 71.30937712, 71.37204823, 71.43471935,\n", + " 71.49739046, 71.56006158, 71.6227327 , 71.68540381, 71.74807493,\n", + " 71.81074604, 71.87341716, 71.93608828, 71.99875939, 72.06143051,\n", + " 72.12410162, 72.18677274, 72.24944386, 72.31211497, 72.37478609])" + ] + }, + "execution_count": 6, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "from astropy.coordinates import EarthLocation, AltAz, Angle\n", + "ha_all = Angle(\n", + " [t.sidereal_time(\"apparent\", \"greenwich\") - src_crd.ra for t in time]\n", + ")\n", + "ha_all.deg" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "id": "7dd1602e", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "array([[1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1.,\n", + " 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1.],\n", + " [1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1.,\n", + " 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1.],\n", + " [1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1.,\n", + " 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1.],\n", + " [1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1.,\n", + " 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1.],\n", + " [1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1.,\n", + " 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1.],\n", + " [1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1.,\n", + " 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1.],\n", + " [1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1.,\n", + " 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1.],\n", + " [1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1.,\n", + " 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1.],\n", + " [1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1.,\n", + " 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1.],\n", + " [1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1.,\n", + " 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1.],\n", + " [1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1.,\n", + " 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1.],\n", + " [1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1.,\n", + " 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1.],\n", + " [1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1.,\n", + " 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1.],\n", + " [1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1.,\n", + " 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1.],\n", + " [1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1.,\n", + " 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1.],\n", + " [1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1.,\n", + " 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1.],\n", + " [1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1.,\n", + " 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1.],\n", + " [1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1.,\n", + " 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1.],\n", + " [1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1.,\n", + " 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1.],\n", + " [1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1.,\n", + " 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1.],\n", + " [1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1.,\n", + " 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1.],\n", + " [1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1.,\n", + " 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1.],\n", + " [1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1.,\n", + " 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1.],\n", + " [1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1.,\n", + " 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1.],\n", + " [1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1.,\n", + " 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1.]])" + ] + }, + "execution_count": 7, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "valid" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "id": "fc657749", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "(0.0, 29.0)" + ] + }, + "execution_count": 8, + "metadata": {}, + "output_type": "execute_result" + }, + { + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": { + "needs_background": "light" + }, + "output_type": "display_data" + } + ], + "source": [ + "plt.figure(figsize=(12,8))\n", + "plt.plot(time.datetime, telescopes, color=\"darkorange\", marker=\".\", linestyle=\"none\")\n", + "\n", + "plt.ylabel(\"Telescope\")\n", + "plt.xlabel(\"Time\")\n", + "plt.ylim(0, 29)" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "id": "a9907a67", + "metadata": {}, + "outputs": [], + "source": [ + "import geopandas\n", + "import geoplot\n", + "import geoplot.crs as gcrs" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "id": "cdc8f9b4", + "metadata": {}, + "outputs": [], + "source": [ + "world = geopandas.read_file(\n", + " geopandas.datasets.get_path('naturalearth_lowres')\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "id": "df5c6d32", + "metadata": {}, + "outputs": [], + "source": [ + "world[\"gdp_pp\"] = world[\"gdp_md_est\"] / world[\"pop_est\"]" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "id": "9f70660c", + "metadata": {}, + "outputs": [], + "source": [ + "from shapely.geometry import Point\n", + "\n", + "d = {'col1': ['ant1', 'ant2', 'ant3', 'ant4', 'ant5', 'ant6', 'ant7', 'ant8', 'ant9', 'ant10'],\n", + " 'geometry': [\n", + " Point(array_layout.x[0], array_layout.y[0], array_layout.z[0]),\n", + " Point(array_layout.x[1], array_layout.y[1], array_layout.z[1]),\n", + " Point(array_layout.x[2], array_layout.y[2], array_layout.z[2]),\n", + " Point(array_layout.x[3], array_layout.y[3], array_layout.z[3]),\n", + " Point(array_layout.x[4], array_layout.y[4], array_layout.z[4]),\n", + " Point(array_layout.x[5], array_layout.y[5], array_layout.z[5]),\n", + " Point(array_layout.x[6], array_layout.y[6], array_layout.z[6]),\n", + " Point(array_layout.x[7], array_layout.y[7], array_layout.z[7]),\n", + " Point(array_layout.x[8], array_layout.y[8], array_layout.z[8]),\n", + " Point(array_layout.x[9], array_layout.y[9], array_layout.z[9]),\n", + "]}\n", + "gdf = geopandas.GeoDataFrame(d, crs=4328)" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "id": "51ef9600", + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "/tmp/ipykernel_3069209/1818496961.py:5: DeprecationWarning: The outline_patch property is deprecated. Use GeoAxes.spines['geo'] or the default Axes properties instead.\n", + " ax.outline_patch.set_visible(True)\n" + ] + }, + { + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAAAOcAAADnCAYAAADl9EEgAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjQuMiwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy8rg+JYAAAACXBIWXMAAAsTAAALEwEAmpwYAAB15UlEQVR4nO2ddVhU6dvH7zMBQwwxMEWKgCCKKAiKgQoGYmF3rt2ru/Yqrq5u2O7aP9dae+3OtRNbLFRMuqRj5vv+wc55maVzRuVzXc91wZlznuc+M+c+T93BAKAqqqhC++BoWoAqqqgif6qUs4oqtJQq5ayiCi2lSjmrqEJLqVLOLwiGYfgMw/A0LUcV5QNTtVqrfTAMwyEiWyKqKRAIahkYGFjzeDwxEYkAiBQKhfDfIiAiHuW8ZDkcDochIlIqlQCgZBhGwTBMNpfLTeVyuckcDucTwzDxAGKzs7OjUlJS3mRkZDwioidEFI6qh0GrqFJODcIwjICIahCRs0gkasTj8bzT09PtAegZGhoyUqmUa2dnpyOVSkkkEpGZmRmJxWKSSCQklUpJJpORkZER6enpEcMwanUrlUpKTU2lxMREioiIoMjISIqMjKSYmBiKjY2luLg4Cg8Px+vXrzOjo6OVqamp4HK5Sbq6us8zMjIuJSYm3qIcpX0JIFsDX89XT5VyVhL/9ob1jI2NO/P5/C7p6emWXC6Xb2JiQhYWFnwXFxdeo0aNyNfXl2xtbfMoW0WjVCrp6dOndPbsWbpx4wY9e/YsKyIiIvvTp08EIF0gELxOTU3dkZKScpiInlf1shVPlXJWIAzDVNPV1W1nZGQ0MC0tzcXMzIzr6emp27t3b8bX15dMTEw0LWKxiIyMpOPHj9Pu3buV9+7dy0hKSsrW19e/FRcX92d2dvYpAFGalvFLpEo5yxGGYUwZhmlhZmY2PD093ZvL5QpSU1N1fv/9d+rbty8ZGBhoWsRyITo6mv73v//RwYMHs58/f56VnZ2doqurezI6OnorEV0CkKppGb8EqpSzjDAMY25sbDwSwFgAJgqFQic1NZUhIlq3bh0NHTq00oeolc3jx49p3bp1dPr06czQ0FCOjo5OrFKp/CEtLW07gBRNy/e5UqWcpYBhGC7DMK1MTU0Xfvr0yTU7O5vbunVr8vb2pitXrlBoaCgdP36cnJ2dS1U/ADp48CClpaVRnTp1yMXFhVXwxMREioqKIoVCQc7OzpSdnU337t2jevXqEZfLLc/bLBXp6emkp6dHRES6urpZBgYGV+Li4qYT0Y2qeWoJAVBVilmIyF4sFq80MDBIEgqFClNTUwQGBmL58uXw9PREjRo1sGHDBqSnp6M0vH//HgcPHkS3bt1ARGjXrh2ICP369cOmTZvQuXNnEBHs7e1haGgIPT098Pl8EBFb3r9/X6q2AeDfLZh8iYiIwOnTp3HmzBm8e/cO2dnZhdZ1//59BAYGgojA4XCU+vr6CcbGxnOJSAot+C0/h6JxAbS9EJG+vr7+IAsLiwdeXl5R27ZtU759+xYAcPv2bdja2sLX1xeHDh2CQqFAaYiJiWGVq1mzZli9ejWOHTsGpVKJkJAQTJ48GX369MG0adOwdu1aAEBmZiaSkpKQmZkJd3d38Pl8zJ07F4mJiaySKZVKZGdnIywsDEqlkj2uUCgQGxuL0aNHY/Xq1Xjz5g3i4+Ohq6uLoKAghIeH49GjRwgLC8OLFy/g4eGh9gJQlejo6CLvLSsrC40bN2avEQqFaebm5ve4XG5HIuJp+vfV5qJxAbS1EJG1TCbbZmVlFTVjxozksLAwAEBycjKmTp0KJycnWFpaYuvWrSgLiYmJ+N///sc+vEePHsXLly/x/PlzZGVlFauOP/74A9bW1tDT02PrWbp0KcRiMQQCAfT19WFiYgKGYSASicDj8UBEkMlkGDBgAMzNzcHn89GmTRu2t8uv/Pzzz7h+/ToaN26Mrl27IjMzs9j3eeHCBVSvXh1EBIFAALlcnmVoaJhkZmb2OxGZQAt+c20rGhdA2woRyWUy2RZHR8foffv2KXL3hhcuXGAf1Dt37pR4+JqRkYHg4GDs2rULq1atQtu2bSEUCtGkSRO23tatW0MkEoGI0Llz52LXrVQqkZycjCtXroCIYGhoCCLC48ePoVAoEBUVhcTERERFRSEjIwOpqal5rlWxc+dOGBgYsDINGDAAu3fvxqlTp1gFMzIyQlRUVInuHwCeP38OHx8fMAwDiUQCFxcXhVAoTBGJREuIyAha8AxoS9G4ANpSiEgilUo32NvbR69fv16xefNmTJgwAQMGDMDz588BAFu2bAGfz4dYLEZJyMrKwvTp06Gnp4datWqhS5cuGDRoEHbu3ImYmBgsWrQIZmZmbI9GRLCzs8Off/5ZonYAIC4uDgKBQDXXg0gkKlU9WVlZCAoKgpeXFzp37oyuXbuiRYsWmDZtGi5fvowhQ4Zg5MiRyMjIKNbw9r+8efMG7dq1A8MwqF69Oho0aKAQCoXJIpHoZyIyhBY8E5ouGhdA04WIzCUSySpra+vYnj17Kt3d3WFsbIwuXbpgzpw5ICKsX78eBw4cwOTJk2FpaYmlS5eiKB4/foyHDx/iyJEjaNCgAVq1aoWIiIg854WGhrIKOWPGDFy9ehXR0dGFLs4URUZGBiZNmoQ+ffpg+fLlyFn3K5qYmBjMnj0bGzduRGhoaKEyxMXFwd3dHbq6uiAi6OjoqPW+xSU0NBR169YFwzCoV68erKysFEKhMMnU1HQuEelDC54RTRWNC6CxGycylUgky6ytraNVK56jRo3C+fPnkZmZCaVSCYVCgSZNmmD27Nng8Xj4/vvvce3aNRSGUqnE0KFDYWFhAScnJzRp0gSbNm0qdLEoIyOj0DpLQ1xcHKytraGvr49evXoVS9mPHDmiNsfU0dHBoEGDsGTJEpw8eRJ3795FWFgYMjIyoFQqsXbtWnTu3Bl37txBw4YNIZfLsW3bNqSlpZVY3qNHj8LR0REMw7Dt6+vrJ5uYmMwkIgG04Jmp7KJxASr9hol0JRLJQmtr66h169ZlPn78mH0YpkyZgv3798PV1RVEhICAAGzfvh0mJiYgInz77bcojA8fPqBjx47w9PREYmJioedWBqqXTEn49OkTrly5gmHDhhW4MJRLeUBEqFu3LgCgX79+7Hy0tD1/UlIS+vbty7bB4/GUhoaGn4yNjScQEQda8AxVVtG4AJVZuFxuI6lUGvbzzz+nqXorpVKJNm3asA9DixYt2KFgy5YtkZGRgfPnz2P37t1ISUlBfsTFxWHLli1wdnbGlClTStVzaCPZ2dm4c+cOlixZgmHDhmHUqFHsMJaI2L/9/PwA5LwMFixYAKFQiIYNG+LkyZNlav/ChQvQ19eHTCaDo6NjpomJSSgROUILnqXKKBoXoFJukshQKpX+6eXlFfvy5UsUxTfffIO+ffsWW8ly9yZlmSt+bmRlZeHp06d5tlSys7Oxa9cuSCQSdsRR2pHEu3fvYGtrCz6fDz8/PxgbG6eZmpou+Br2SDUuQEUXXV1df5lM9n7t2rUZRSlOfHw8XF1dIRaL8fTp00LPzc3MmTNBRFi3bl2xr9E2Ro8eDSIq875tbrp3786+tDw9PUu19aJi+vTpYBgGe/bsgb29fbaenl4swzBu0IJnrKKKxgWosBsjMpVKpfv9/PziPnz4kN/vnYdnz56VWMmuXr0KuVyOU6dOFfsabST3PK+82Lt3LxwcHEBEkEqlICLs3Lmz1PW1a9cOQqEQHz9+hJ2dHXg8nsLU1HQjEelCC5658i4aF6AiiqGhYQ+5XB6xY8eO7JIOM9euXYvWrVuz/4eGhuY5R6FQ4OHDh5g2bRokEgkWL14MIkL9+vVL1Ja28fr1ayQkJJR7vWvWrIGpqSmr/IsWLUJiYmKJV6kzMjJQo0YN6Onp4dixY1i3bh0YhoGenl4iEXlDC5698iwaF6BcbybHkOBs586d42NiYgr5mQvm6NGjcHd3x19//cVa2bRs2RLe3t6oXr26muWMl5cX3r17hxUrVoCI4OPjU6o2v2QuXrwImUyGkJAQhISEsD0pEaFatWoIDw8vUX0KhQLdu3cHwzDo1q0bPn78mNtu9/SXtDeqcQHKq3C53Poymezj4cOHS2d9jpyFDNUP3aZNG/z222+5l/TRvXt3WFhYQF9fH0ePHmWvy8jIwMWLF7+YxaAPHz7gxx9/xIULF8pc18yZMzFhwgT2f6VSCWNjYzx//hx8Pp+11y0pJ06cgIGBAXR1ddG8efPce7NJRGQLLXgmy1o0LkB5FBMTkyE1atSIKc5KbGEolUrs3r0bjx49glKpxK5du2BqaooJEybAxsYGQqEQkyZNKpUlzOcEEcHS0hIikQhnzpzB5cuXMWnSJNy9e7fYdTx8+JC1Ge7WrRt7PDk5GUSE2rVrq61yl4asrCxYWVmBYRi17TAej5fF4XBaQAuezbIUjQtQJuGJeFKpdJ2/v398UlJSMX7O4pGeno7hw4fDzc0Nx44dY3/0EydOlFsb2kytWrXURgy554vFHR04OzurrdbWq1cPx44dw7hx47B48WKcOnUK586dw6+//lqm/VCFQoFp06aBw+GoKTuXy1UaGxsH0b8BBT7HonEBSi04kUgikdyYPXt2SnkPJ4cPH84ajh84cAD29vaYN29eubahzah6NyJCYmIiZs2aBalUCisrK8yYMaNY3jizZ89Gly5d2HoYhgGHw4GpqSkYhoGxsTGMjIzQsWNHnDlzBjdv3gSAUu+HZmRkoHnz5jAxMYGlpSXbrrGx8ZnPdTVX4wKUpvB4vNpSqfTtgQMHCnfHLyFXr16Frq4utm3bhhUrVsDe3h5isRj79+8HAJw5cwaDBw/GvXv3yrNZrUS1yCUWi2Fubq7mK/r48eMir3/79i27iKaaEzZt2hRAzve4dOlSLFiwALa2tuByuWAYho3qUKdOHURGRpZa9qysLAwZMgROTk4wNDRUmpmZhRCRHFrw7JakaFyAkhYjI6PudnZ20SEhIcX/tYpBbGws+/BlZmbi8OHDcHZ2xpEjRwDk7NmZm5vDxsYG7u7uaNu2LX7++edSRz/4HFAoFAgPD0dERATS0tLw7t27EjlYKxQK1KxZE8uWLWO/WzMzM7i7u2PhwoVISEjA2bNn8fTpU8THx2PFihW4e/cu7O3twefzsWfPnlLLrlQqsWHDBgiFQnh5ecHExCSOYRgvaMEzXNyicQGKLSgRRywWL2ratGlcfHx8SX6nYnHu3DkQEUJCQjBhwgRYWlqyq7WNGzdG9erVcfPmTaSkpGDv3r3Yv38/nJ2d0bBhQ5R226ayiYyMxO7du3HkyBGoXm5Pnz7FyZMnS6R0JeHs2bNqfqqdO3eGr68vu00lFoshFouxa9cutetGjhwJhmEwePBg3L9/v0Rtdu7cGf379wcApKSkYNGiRSpb4CyhUDgcWvA8F6doXIBiCUnEkUgku0aPHp1UVGCpsqBQKHDx4kU4ODggISEBPXr0ABHBzc0tX6P38PBwEBECAwMrTKbyYtiwYTA2NkaHDh3QrFkzEBFWrlwJIyMj1KpVC9OmTauwto8dO8Yu2Li5ubHH3717B4lEgokTJ8LKygqLFi1SW3Dat28f+Hw+9PT00KtXr2I7dateBCKRCAcOHAAAvHr1ip37mpqazoUWPNdFFY0LUKSA/yrmpEmTkspr4Sc8PBxHjhzB06dPMWDAAAQEBMDHxwcnT57EL7/8wi7tr127FqNGjSrQkkX1g58+fbpc5KoofvrpJxAR4uPj2RdK7vLkyRMQUYVYB6nI7ZrXuHFjqFbXX79+DT8/PxgZGcHCwgLffPONWgiVjRs3slsyXC4XEydOxLt37wDk7HUGBgYiLCwMV69eRUhICBYvXgwul8tu4XA4HPzxxx8AwEaI+HehaBG04PkurGhcgEKFqwDFzMrKYn8gc3NzLFiwAIcOHWKPqTwpirNqeOLEiQp/qAsjOTkZCxYsQKNGjSCVSiEWi9GgQQOMGDECb968AQA8ePAARMTO3+Li4jBy5Eg8ffoUz58/x8GDB1krm2bNmpU6rGdx+PDhA3R0dEBE4PP5rNIAOXbN1apVg5ubGxwdHbFq1So8efIEmZmZcHNzg1gsRvv27SEWi0FEsLKyyvOSyV0cHR0BAJ6eniAiDBw4ECkpKWrnGBsb/woteM4LKhoXoEDBKkAxgZwld3t7ezg7O+PZs2fs8fj4eHTq1Ak//fRTsV3Fdu/eXen2tEqlEvHx8WjRogU7bxs7dixCQkIQGhqKS5cuwdPTEw0bNsyz0X/u3LkC601PT0f37t3Rtm1bxMXFVZj8CQkJahH+vLy82CnDhw8fUL16dYwZMwaDBg2CXC5HQEAAzM3Noa+vjxYtWmDQoEEgItaNzNzcHPHx8YiPj4dMJgMRwdTUVK3NXr16gYjQp08f+Pv7q30npqam86EFz3t+ReMC5CtUBSkmkPMAcLnccnkAt27diu7du5eDVMUjNTUV7du3Vy1uID4+Pk9w5/Hjx4MoJxrB1atXcfz4cWRnZ7NR9wojMzMT48aNg5WVFbZt21ZhllBKpRJLlixhQ5IIhUJs2LABCoUCoaGhsLCwwJQpUxAbG4u1a9di6dKlOHPmDOvWRkTo27cvJk6cCCJCjRo1WAVftWoViAjDhg1Ta3P06NGQSqVqtr3/GlkoRSKRViqoxgXII1AFKuaOHTvg6uqK9u3bl0t9J06cgFQqLfVK5/v377Fx40YMHToUbdu2xcGDB3H//n3o6OjAxsYG4eHhGDhwIGscPnDgQHTr1g0DBw5kh+CLFi1i60tPT4eFhQUEAgEWLlxY6vs6ffo0iHIM/iuSy5cvs6u1HA4HHh4eAHLWBPr16wcbGxssXLgQf/zxB7ulBYBVyly9H2xtbdmh/M6dO0FEaj65aWlpqFOnDjudefbsGSwtLcHhcMDlcmFiYqJ1Q1yNC6AmTAUqZkJCAogI06ZNK3aw5qLIyMgAESE2NrbE18bHx7MPl5OTEzvnUx2bPn06+zAFBATgyJEj4PP5GDNmjNq82d3dHUBOb/Tbb7/B2toacrm8zFsjqkWkDh064PXr12WqqzCePXsGOzs71ja2d+/e7O/zzz//YPLkyRg+fDiqV6+OcePGISMjA/fv3wcRwcLCAjVq1GCHw2ZmZuy+MxHhhx9+UGvr3bt37Pd27do1vHr1CmZmZuy81NjYeDG0QA9UReMCsIKUk2KuWrUK7dq1gypCu4p//vkHRIQtW7aUuu7cnDlzhnUgLi43btzAkiVLkJycDIVCgXnz5qFhw4YwNTWFv78/3r9/jwEDBmD16tVQKpWsgpiZmYGIYGBggMWLF7P7dqreURWJoXr16li6dCn78JUFpVKJ6OhoTJ8+HUSE27dvl6m+wvj48SNsbGwwceJE1tNk7NixyO0kHx8fjzZt2mDSpEkAclZ/Ve57Q4YMYQNxDx8+HFlZWeDxeOw2Sm5UQcmICJGRkVixYgUaN24Ma2trlYL+Bi3QB2iTckokkt/Hjx9f5h5T9TAREebNmweVwYLqgX7w4EGZ6n/16hXc3d3BMAwaNGgAgUBQpDF4eno6/vrrL9jY2MDX1xc1a9ZEZmYm2rVrBy6XixMnTkBHR4cNmJW7Z1+yZAlkMhk4HA7atWvHvhC8vb3x6dMnADlR7wQCAXr06AFzc3MsX7683CyXMjMzMXv2bBARfv/993KpMz9u3boFsViM169fY+7cuTA2NmZ7x1u3bgHI6fmsra0xcOBAREVFQaFQYMmSJWort3w+n/0eDx48qNbG/Pnz1YbD48ePh0KhgEgkwosXL9jjQqFwDLRAJzQuAAAyMTHp37Jly/jyeKA+ffqEUaNGgYjg4eEBQ0NDWFlZwdLSEl27dsWLFy8QHx+PtLQ0vHz5stjDv5SUFHTt2hUMw8DZ2RkPHz7EwYMH2eHo4cOH81yTkZGBn3/+GRYWFmjZsiX2798PpVKJwMBAdO/eHbt37853G+DevXsYP348unXrhmHDhsHc3BzGxsa4c+cOvvvuOxDluFwBOfM2iUSCbt26QSgU4vHjx+XuV5qUlAQPDw9wOJwKdZf77rvv2J4RyOktVakbfv31V1aWSZMmQSwWY8eOHQCAsLAwVjEVCgWbOInH47GWR9evX1f7jkeNGsXutYpEIrx69QpHjx5VXacgIo2b+mlcMXk8Xj0HB4eY8ozzqlAoIJPJIBAI4OjoCHt7e6xbtw6XL19mY9LmLkWxaNEi6OjoQCQSYe/evezxzMxMnD17Flu3bmU3uAcOHIjjx48DyImqUKNGDdy9e5fNGPbXX39h7NixcHNzw/79+1kZuFwu7O3tIRAIWG+O3E7ERMRuxm/YsAHXrl1jN/aXL1+OuLg41KxZkz0390Z+eaAKxXLmzJlyrTc3b968gUgkyhMITGWbm3vxKzg4GHZ2dggKCgIAGBsbo06dOuznFy9eRK1atcDhcDB06NB8jS++//57AMDYsWPx3XffAfj/WEoCgSCViGT4WpWTiMQymez9ixcviv7lSkhmZiZiYmKwefNmeHl5wcbGBl5eXqhRowb7w5w/f75QB+KwsDA4OTmBy+VixowZhQ4VlUol5s6dq6bwy5Ytg76+PjZs2ACZTAYdHR3WztTBwQEAEBUVhXfv3uHatWtYsWIFoqKi0KlTJxDlZAHz8/ND06ZNYWtry+bldHBwQJ06ddC/f38QERuVYfDgwahTpw4ePHiA58+f4+rVq3j16lW5fJ+5F7C2bNlSYVEfpk6dCh8fH/w3KJuq7a5du+LQoUPIyMjA1atXIZVKkZGRAYlEohadQsXOnTvBMAw2bdqE7t27g8/nq730jh07hvfv38PMzIzNiWNhYQEigqGh4XvSoLuZJhWTL5FI7h4/frxC3TpyO/xeu3YNt27dglwuR7Vq1WBtbY169erl+6CpHHhr1arFmosVhVKpZIfU/fv3x9atW1G7dm3UqlULurq62LdvH7Kzs9l545gxY7BhwwZMnDgR48ePZ61z7ty5g3bt2rGR5o2NjXHo0CGIRCK4u7vDxsYGtra28PHxwY0bN9j2k5KS0LRpU/D5fFVyIIjFYnTv3p2dn5aFmTNnwt3dHdbW1mjTpk0eBSoPsrOzMXXqVIhEIsyZM4cdeu7ZswfVq1fH4sWLIZPJIBQKERAQAA6Hg65du+Lnn3/G4MGD861zxowZICJ2uPv8+XPs2rULHTp0gJmZGf7++28sW7YMDRs2RFZWFi5dusQ+MyYmJidJQw7bGlNOqVS6ZcGCBeU79sqHBw8eYObMmWpG1UqlEsuWLcM333zDbkWoePjwIaysrMDn87FixYoSt7du3Tr2h7179y6ys7PZ3vqbb75hz/v+++/Z81Sb8WfPnoW7uzvat2+PQ4cOYfTo0XBycoKrqyvatWuH8+fPY9u2bTh79iyuXr0KhmHybAsplUq1Y8nJyWjZsiUGDhxYLjlZVDILBAL4+PhUWA/6+vVr9OnTB3K5HGvWrEFGRga6d++OefPmsTLkTuobGRkJY2PjAgOG5R7O5l55DgoKAhHh/PnzaN26NTu39fHxYX8bExOTafhalNPMzGx0YGBggqYDYi1cuJCddygUCgwdOhQMw6Bhw4al2rtU8ebNG3zzzTdYuHAha39LRKzTNgB2dbBnz57Q19fH5MmToaOjg549e7LbJz4+PujZsyckEgmmTJkCFxcXSCQSiEQi6Ovro2PHjsVSjri4OLRu3RqTJ08u9T2pePv2rdqDXhan6OJw69YttG7dGjY2NnBzc8OoUaMwdepUmJubq8nRvHlz8Hg8cLlcBAUFsS8ihUKh5k9KlGP6p3qBbd++HUQ5hgxLly5FgwYNAORYY+V+eWoiJlGlK6aOjk5jFxeX2PJesCgNQUFB6N27Ny5dugQzMzPo6elh+/bt5VL3iRMn4OLiAgcHB/j5+bE/tGpr5927d1i6dCmGDRuG6dOnY+zYsbCyssLVq1dhbGwMc3NzbNu2DY0aNULDhg3ZvU6JRAI/Pz907NgRgwcPxsWLF4slT0xMDKytrfNdVS4py5cvx9GjRyEUCgs1+j979iyOHTtW5vYA4ObNmzh48CCSkpLw9u1b2Nrawt/fH3p6eqhXrx74fD769++PadOmQVdXF3w+H3PmzGH3L1VzeC6XC0NDQ3h5ebF1m5mZqcVN+uWXXwAAs2bNYtcIGIZRUCVH9avcNwGRpVwuj1CZWWmaiIgI9gdp06ZNgYmKSoNSqUTz5s0hEAgwZMgQJCYm4s6dO2qLSjExMRAKhYiJicHFixdRs2ZNnDp1it0or1mzJubMmQMzMzPWpM7LywuTJ08Gj8dDvXr12OFzcbhy5QrEYjEePnxY5vuLi4uDoaFhHtteFQ8fPoRIJIJUKsWECRNw6dKlMreZm5iYGAwYMEBN8ZYtWwbg/4N+qT6ztrbGyJEjWVey7du3g8fj4bvvvkN0dDRatGgBe3t7dvqho6ODT58+QaFQwNbWlq1HX18/hioxLm5lKiYjlUpvnj9/vvx+oTISHx/PbliXd2aw7OxsbNiwgR1GvX//Ps85WVlZkMvluH//Pj59+gRnZ2dYWFjA0NAQ+vr6uHXrFnr27Illy5axeT9jYmLw7Nkz6OjoQCgUgsfjlSh9xODBg7Fy5coy39+BAwfQqFGjfD+LiYlBmzZt0LhxY7x48QLz58+HkZFRhbjWBQcHo2nTppBIJGqLY0CODS6PxwPw/+abRDl+noMHD86ztZJ7CvLzzz8DAH788Ue1c8zMzHbjS1NOY2Pj4UOGDNF80spc3L59W/VGLPe6o6KiYGhoCHd3d/Tr1w+mpqZwcnKCp6cnBgwYgI8fP+L169cYPnw4+vfvj2fPnuHmzZsQiUQIDQ1Fnz598Oeff4KIMHv2bLW6Hzx4gJYtW2LZsmVYs2YNpFIpHj9+XKwFn8GDB0MqlaJTp07s1kFJuXfvHmQyWZ4ha3p6OkaOHIlu3brB3t5ezSXP398fu3fvLlV7xcHb2zvPnnVsbCw4HA62bduGPn36sApmYWHBGhy4ubnB3d0dRAQ7Ozt2r5hhGERGRuL8+fNqaQ//XbyrlNQPldVryqysrKLKYzm/PFF5N8hkMnYzuzw5d+4cu3zv5eUFfX19iMViWFlZoVatWqxBRLNmzWBgYABbW1sMGjQIADB06FDW/Sm31Ux+qLZvfH19i1wgSkpKwtSpU9mHraRz0ISEBBgZGWHbtm15Ptu2bRtq166Nzp07Y/78+Wqfubu748qVKyVq68OHD5g0aRLS09OLDFHSt29fMAyDzZs3qx1v3749GIZhpwqrV68GUY6bmuo7MDAwwL59+9R6yICAAEgkEly/fh1t2rSBt7c3uwilo6OTXBn7n5WinFKp9NzRo0e1Lkydi4sLzM3NYWRkBF1d3QprR7W90rNnT3z69Ak6OjqYMmUK2rRpg6ioKNja2qJ9+/YwMDCAvb09Ll68CD6fj+PHj2PBggUwMTHBqFGj1PYVcw/D4+Pj8e7dOzRu3Bh16tRhzdoKYuPGjWjdujWuXbsGe3v7fJM1FcSLFy9ga2ub72cfP35kQ2g6ODhAFYE/Pj4e+vr6JbZaateunZrCPHr0qMBzp06dChsbGzAMw849Vezdu5eNRbx06VL8+uuvICLY29vnGdrmLtOmTWMVslevXlAqlbn3P/+Hz105DQwMenbp0iW+RL9KJdGiRQts2LABpqamGD9+fIW1k56eDpUVlMrbxNzcHCdPnsT169dRvXp11gLI2NgYiYmJaNiwIfT09PDy5UvcvHkTDMOAYRjY2Niw6d3HjBmjZuuqUChw5swZ2NjY5Gsto+Ljx48wMTFhXaWWLl1a7HvJysqCQCAocPEsPT0ddevWBVGOw3ffvn1hYmKili+luPTq1Ytd6V60aBFsbW0LtO09ePAgateuzTqb5yefyilC5cWjKm/evIGRkVEe5QwMDMShQ4dgZ2eH4cOHA8hZCMs1xHXD56qcRCSSy+UR2ho68t69e6xNbEmzXZWV1atXo2nTphAIBOwDRURYu3YtALD/T5gwAcuWLcOgQYNw8+ZNds9z//798Pb2xvTp0/PUvW/fPri7uxc6xH358iW2bt2KgIAA/PTTT8WWW7XZX5QBfGZmJvr06YPff/+91N9tZGQkLCws2IS+KmuvBg0aYNOmTThz5gzruKBUKjFgwAC1nCn/9UoBkHveyBY+n68WJT73cLdjx455VppV0wiBQBBPFZhhu0KVUyaT7d+5c2f5eDZXAOvXr2cXCDSFKkgXEeHPP/9kFerw4cMYMWIEBg8eDJlMhmvXriEzMxNeXl6s8b0qbMfly5fZ+lJTU+Hl5QUfH58i3cbOnz8PZ2fnYnuaKBQK9O/fv0AzuYrg7t27EIvFmDp1KoKDg7Fv3z4cOHAArVu3hoODAxwcHLB582b88MMPCA4ORoMGDUBE7P7kxIkT0b59e8yYMQMxMTGs4v03moKqqNYIiHLseEeOHAkiQkREBGJjY/Hp0ye1dBWmpqYL8Lkpp66ubls/P784TVsBFUR6ejr7Bed+uDVBREREvlstjRo1gkQiYT36jx8/DmdnZ3ZvcejQoZBIJDA3N8f69esB5GzhjBs3DmKxGLNmzSp0T/PQoUPFDtmiUCgwYsQING3atEIDgOXHs2fPMGzYMPb32r59O/744w+Eh4fj3Llz8PPzQ+vWrSGTyVjvmW3btqklN/o3Xq3aCMXOzg5+fn64ceMG9u7dy86Jr127xp63Y8cOeHp6YsKECeyiEvD/I5t/3ctq4HNRTiISSqXSDxVhGF1enDx5EkQET09PTYtSIGvXrsXs2bPZ3vTmzZuwsbEBkGNGZ2xsDG9vbyxcuBAikUjNIODBgweYMmUKxGJxgUYKwcHBMDc3L5YD+pkzZ+Ds7FzqREPlgcoLp2bNmujVqxfEYrFaxIsRI0awEfhUQ9e6devi7du3bNQ+Dw8P6OnpoXHjxggODoZcLsfQoUMxYcIEPHjwAP369YObmxu7zeLt7c32wipb3rS0NHbVl4hgaGj4log4+ByUUyqVblizZk3ZrawriE+fPrHmWuWRILaiUCqV6NGjB6ZPn45p06ZBJBJh9erVAHJWQO3s7NCyZUtcunQJIpEo30WQ3bt3w9DQEE5OTvkaAQwdOjTP6mZ+HD16FN7e3mW/qVKiipukr68Pe3t7Nh5v7sS7SqUS/fr1g1AozDdECYfDQbNmzSCRSCCRSADk2EH/9ttvmDFjBmsN1Lt3bxARe7+bN28GEbExnZKTk5GZmQkbGxvo6uqCYRgYGRlNgLYrJxFZOzo6xmhzgp9Fixahc+fOYBimVFmVK4ugoCCIxWL06dMHkyZNyhMXCch5ID09PbFhwwb22LNnz+Dv74+pU6dCqVTi48ePMDQ0zOPEDOTMO83NzYs0EMjMzIS9vb1Gv6/cc0MvLy/IZDL89NNPasPsxo0b49tvv803nrDKo8XQ0BA6OjoAcuboqvSDuQkKCoK+vj67LRUfH4/U1FS1aUK3bt2wY8cODB06FMbGxvFEJIA2K6dcLt+5f/9+rdPMCxcugCgnXGLXrl3ZjejyNtsrLw4fPswu75ubm+cJpxIaGooXL15gx44d4HK5aiuzy5cvh5+fH+rVq4egoCBs2rQJTZo0KXD19s6dOzA1NcXbt28LlWnw4MFsz60J+vbti5YtW2LTpk3o27cvunbtCg8PD8ycORMTJ05kh/pPnjxhI77nRqFQqK3UPnr0iJ2HDh8+HKdPn8aRI0dYJwNVKWjbqH379ti3bx+ysrJQs2bNbGNj4+nQVuUkomo1a9aM0cZFINVcpGvXrhAKhfl622uapKQkbN26FYsXL4aZmRlEIhH+/vtvNGrUCO3atWOHpX///TdMTExgZ2cHR0dH+Pv7q9UTFhYGS0tLfPvtt3B1dQWPxyty0Wvs2LH49ttvCz2nVq1aGl08++OPP+Dm5oZHjx4hNjYWZmZmrD8mEcHPzw+NGjVCnz59YGRkVGA9UVFRsLGxYZ3Zz507h2bNmqF58+Zo3LgxZs2ahYiICDaMTKdOnfKtp1atWmzO0WfPnsHQ0DCJytEwvrx7zb+PHDmidZqpCu7Ur18//PzzzxgzZoymRcpDWloaHBwc0LZtW3Tv3h2BgYGwsbGBUqnEypUr4e7uDjc3N2RmZqJVq1bo06cPGwZz6NCheZyuQ0ND0aJFC7Ru3RpXr14tsv3IyEhYWVlh48aN+X6empoKHR0dtUDNlY1CocCaNWtgbm6Oa9euoWHDhrhy5QqSkpJgbW0NCwsL/Prrr+zcsLDwN/Hx8ahfvz6IiLW79ff3z7P9pFqE+u/3mztan8oE0sfHJ8vU1DQI2qacROTg6uoarY29pmqVTaFQoE2bNuxGvzaQnZ2NefPmwcXFBW3atAEAtf04Pz8/tS2AGTNm4NmzZxg+fDgmTJiAMWPGoHHjxmjcuDEbTuXatWuIiopSDbcKzZGSm5CQEDg4OODbb7/NN/D26tWrC/REqUzWrFmD5s2bQyqVssb1KvO8yMhI3L17F0KhsMAXTW7Wrl2LTp06sflUZs2apfb5X3/9BSLC4sWL1Y4fOXIEQqEQPXv2hIGBAVJSUvDy5UsIhcIkIjKENimnXC4/fOrUqWJ+vZVLUFAQli5dij///BP169ev0ExaJSEhIQHNmzeHr68vLl++rLZNkZGRgebNm2P27NkYNmwYevXqhe7du+e7LaJQKGBvb48ePXoAAORyOYgI69atw6JFi0pkNBAbGws/Pz8MGTIkz2dpaWnQ09Mr98h+JSU9PR0uLi6YNWsWO49+9OgR5s+fj8zMTHTt2hVLliwpVl3jxo1D7969kZ6ejqFDh4IoJwK/KsLDunXrWI+X3EP658+fs5H1pVIpqlevDoVCAT8/v2yRSFQuhgnl1Ws6161bVyt7zdysXLmStZHUNAkJCWjQoAHGjBlToMMy8P8Ryq2srPD8+XNcuXIFwcHBWLNmDRtYOSEhAbdv32YDLoeEhLDR0Pl8PiZOnFgi2ZKSkuDi4oKff/4Z2dnZePDgAWuCV7du3Tx+k9pG165d0aFDBzRq1Ai1a9fGmDFjcOXKlTwLYmFhYTAzM2OjUwCAoaEh+Hw+vL29kZaWhjlz5sDOzg6WlpZ48uSJ2vWenp44dOgQYmNj2Sj1b9++hb6+fioRGUEblFMul5/QJifq/EhISEDt2rXZEBSaRKlUwt/fH6NHjy7SxWvp0qWYNWuWWm5JiUSCwMBAGBkZwcbGBnp6evjzzz+RkJCAOXPmQCQSsen/VDFbS8rLly/RqFEj6OnpoXr16hCJRGwco//973+lvfVKISUlBTNmzMC+ffsQHByMefPmwdbWNo8N8bVr1/IEeAsODmZDYy5evBhJSUmYMWNGvi5re/fuhYuLC+Lj47F582YwDIO7d++Cw+HAxMRkFTStnERUu379+tpp2f4vN2/exJQpU9CzZ88KixZXEjZt2gRPT88SJxsaMGAA5s6dCwMDAyQlJSE5ORl37txBSEgIqlWrhlWrVgHIsdc9ffo0xo0bV6aYQQqFgh3evX37FmPHjsUvv/yiUSuh0vLhwwdYW1uzEeCBnCG8WCzO88J++PAhiAgjR44EkKOwEREReepUKpUYPXo0O51o0qQJpFIpFixYAB0dnUwiMoEmlVMul58t7/gw5YkqPIVQKMTJkyc1LQ6USiXq1KmDsszP9fT08hirP336FEKhEPXq1SvWQsjXyN27d2Fubs4aUuzcuRMCgQB8Pl8tagOQo7iJiYlsqgeVTe1/+fTpE0xMTBAREYGkpCQIBAJ88803ICKIRKLVKINucagMMAwjNzU1rdOkSZOyVFOhhISEkEQioaysLNq0aZOmxaHXr1/Tw4cPKTY2ttR1GBkZUXx8vNoxJycnevHiBQUEBNCNGzfUPvv999/p/v37pW7vS6Fu3bq0YsUKGjduHD1+/Jh69epFcrmcunXrRqdOnVI7VyQSkZGREd2+fZs9tnr1arVzAFB0dDRxOByKiIggQ0ND2rRpE23cuJHs7e0pNTV1IMMwvFILXBbNNjMzm7N69WqtdQkDclY9dXR00LlzZ4hEIk2LAyDH6L60cYuys7Ph4OBQYIq/bdu2oWfPnuz/d+/ehZ6eHjw8PApdePpaCAsLUzNkz8jIwLx589hcKf9l06ZN4PP5uHLlCiwsLHDo0CEAOfucKh/Q/85lXV1dIRKJwOVyweVyO6Cyh7VExMhksrfaPv9QKpUwMjKCl5cXxGKxpsUBkBPN3NraulTXzpgxA76+vgUqWnBwMJuBDMhxDP7xxx/RvHlzNqLc14pSqUTr1q3xww8/ICQkhD3et2/fAqNBqOI8NWzYEMePH4ebmxsAIDAwkI03/N/YWM+fP2fNBM3MzO5BA8rZuHPnzqUPi15JqBKpqiKCa4NBfkhICGxsbHDx4sUS7RlmZmbC0NAQha2Mp6amwtraGs2aNcPJkydZm9k3b97kGz7yayE7OxsDBgxAvXr18hhYHD58OF/jirS0NHC5XDx+/Bi6urro27cv6yOqr6+fryOCisDAQNV5GUQkRWUqp4WFxf5//vmndN9UJaJQKNhgwbq6uoXG1qksoqOj4ePjg3r16sHFxQX37t0r9rUbN25ErVq1Cn3JZGVloX///hCJRGr3u3fvXtjb25dLUqPPiaysLIwfPx7NmzfP14g9LS0NpqambEAyFarFoMzMTOzduxcMw2Dfvn1o2bIl6/RdEMnJyeBwOKpcK0GoLOUkIkMbG5sobdiWKIpz586BKCfYlEAgAMMwJd6UryiUSiW2bNkCc3PzYkdEV632FvVizMzMzDdiweDBg9USKn0NjB07Fi1atCg0r8vs2bPRvn17NZ/X77//Xi3wm7W1NQYMGAAAaN68eb5uablROXgLhcIYKkWmslIpp6Gh4bCgoCDNJzspBp06dWLD9g8dOhRbt24Fn8+Hvb19ueWuLCtHjx4tNLLcf5k+fTrmzJlTqrY+ffoEoVCYb1iUL5WAgAB2v/fp06f59p5JSUno0qUL++JSKBSwsrJS89+cNWsW+Hw+Nm/ejNWrV0MgEBTarkKhgL6+PgwMDLKJqAEqQznlcnlIcXNWapJbt25BJpOhR48eICLWMDw8PByurq7gcDiYMWOGhqXMYfTo0bC1tS2WS9awYcOwcOHCUrWTlZUFHR0dbN26VSsMMiqDdu3aoUePHvD19QVRTlS9/KLj37t3DzY2NsjIyMCNGzfg5OSU55zWrVuzw1UiQnBwcKFtq3KDisXiY6ho5SQiJ29v78LDb2sB2dnZqF+/PjZt2oSTJ0/maymzbNky8Pl8yOVyrQhXcuTIEYjFYkyYMAF//fVXvlYpFy5cgJmZWZlS7128eBEeHh7w9/ev8BR+2sC7d+9YQwJra2swDMNGMPwvPj4+2L17NxYvXoxx48ble05KSgpGjBjBRlUoDIVCoXohpBGRASpSOaVS6ardu3dr/St3yZIlhXr/q4iPj4efnx8YhoG3t7fGHbAfPnyIX375BYGBgeDxeKxRdlRUFH799VeIxWKcPn26zO1kZWXh22+/hYGBAczMzDBhwoRySa6rrdy4cQPGxsZYtWoV+vfvDz09vXwX1dq0aYODBw9i2rRpeVJK/JdWrVqBiHD//v1CzzMxMYGOjo5SIBB8g4pSTsrJFBau7T/iP//8A6lUmmf1rTBu3bqF6tWrg8PhoFOnTmVKnlse3Lt3D/b29lAqlbh8+TIEAgH69etXaEqC0qBUKvH27Vs0bdoUf/zxR7nWrW28evUKFhYWuHLlCng8Xr5Tmu+//x5BQUGYP38+m1i5MJo0aQKBQFBgLpfo6Gh2+0UkEoWiApWzduvWrfNGidIi3r59C5lMVmrb1T179kAmk4HD4aBLly4aG/YdPnwYAQEBAHKW5W1sbHDr1q0Ka+/WrVsQCoUICAj4orda/vjjD/j7+8PDwwMcDkfNGAHIMdqYPHlysbOiqRaOfH1983yWlpYGW1tb2NjYgMvlqoa2xQ5jUiLlNDIymrJmzZpsIGeFMTAwsOTfTgXy7t07ODs747fffitzXZs3b4aFhQUYhkGzZs3KvccqipMnT6Jly5bs/7169cJff/1VoW3Gx8ejc+fOWLRoUYW2o0kyMjIgFovx+vVrODo6wsXFhR3Onz59GhYWFoiNjYWHh0exX4ZnzpzJN5KjytUuOjoavr6+0NHRURJRa1SEclpaWt5UbT9cvXpVq35EhUKBmjVrlru/5rFjx+Ds7AyGYVCtWjWsWbOmUqyMXr58CQsLC3bOPH78+BIlHCote/fuRdu2bSu8HU3Su3dvBAUF4fz58+Dz+Rg1ahQAYODAgVi1ahVCQ0Ohq6uLWrVqFXse3rhxYzg4OKgd43A4rDVXcHCwKpLiVpS3chKRjrW1NTukPXbsmFaZgp0/fx61a9eusO2BV69eoWPHjuDz+dDV1YW/v3+FDjOVSiXkcjnryjRx4kT8+OOPFdaeigULFqBu3bqfpc9mcQkLC4NYLAYRoUmTJuBwOPD09ISbmxsmTJgAmUyGqVOnwsjIqNgxk169egUOh6MWn0pfX1/N4MPY2BgGBgbxqADl9Onfvz+7SkJE6N69e+m+nXJGoVCgYcOGaoGVK7KtlStXwsnJCQzDQCQSoW/fvoXmJCkt8+bNg4+PDxISEiAWiys08l1ycjLmzZvHhkXJLznul0RWVhbmzZsHHo8HhmGwevVqWFlZQSqVspnE79+/Dz6fj5YtW+Yb8Oy/tGzZEtbW1khKSkJaWhq7faMaac2ZMwc6OjrZRCRBeSqnWCxemntvyNjYON+Q95pg7ty5aNSoUaUbtcfGxuLbb7+FtbU1ayLYsWNHHDt2DPPmzWMjE5QWhUIBf39/mJmZYeDAgeUjdD5cu3YNMpkMzZo1g42NDQwMDIoMMP0loFKgTZs2AQB++eUXjBgxQu2c69evg8fjoX///kXW9+jRIzYWbu6iCoWSK89qH5Sncsrl8ue5bTWJSp6yvCK4d+8e5HJ5pefX/C/x8fH44Ycf4OTkBC6XCyKCsbFxudR79uzZChuux8bGwtbWFn///TdGjBjB5sA8c+ZMhbSnbeR2vXv69CkbKzg3lpaWaj6yhREaGgoejwdDQ0N06NABZ86cUQut+a8b2QWUl3ISkYmTkxM738zMzFQzh9Mkf//9N9q1a6dpMdRQKBRs/kdvb2+N2vCmpqZi8eLFGDp0KAIDA9G6dWv06NEDI0aMQLt27WBkZISpU6cCAGrXrs2GgdTV1cX48eMxcODAYgWl/hJQKpWwsLBAaGgoe0yVQOnNmzclqqtRo0bsanvr1q1Rq1YtAICDgwP09PRSimMIX6wwJRwOp3n79u31VP8vXLiQiIisrKyKc3mFsmnTJgoICNC0GGpwOBxaunQpnT9/nuLj48nR0ZEmTZpEhw4dqnRZpk+fTocPHyYPDw969eoVnTp1iq5du0bx8fHUu3dvevnyJf3888+UkJBAYWFhdPToUfr48SN9++23lJSURBwOh/z8/Ojdu3eVLntlwzAMOTo6UlhYGHtMdd/m5uYlqkskErGhZH744QcKCQmh5ORk6tevHykUCgERORZZSVHai5wtlO25HXypkIBHlYlSqQSXyy0w0Yy2oErfx+FwYGdnB2dnZ3h6elao3B8+fMCAAQMgFAoxZ84cjBw5EpMnT0azZs3Y3y+3BcycOXPQtWvXPPXs3r0bRIRhw4ZVmKzahL29vVp82ocPH4JhmCIN3P/L4MGD1bZWhEIhZs2ahcjISBAR9PT0JqM8ek6FQtHY29ubVWYionHjxpXoTVIRpKWlEZ/PJ319fU2LUigLFy6k2NhYevPmDTk7O5OTkxOFhYWRjY0Nffz4sdzbS0lJoebNm9O5c+dIqVTS69evycXFha5cuULVqlWj6OhoOnr0KE2cOJGIiJRKJc2dO5c6duyYpy6BQECHDh2iAwcO0OPHj8tdVm3i06dPFBERQY6O/9+p1a5dm8zNzWnz5s0lqksqlVJycjL7f/v27WnTpk0kkUiIiIhhmIlFVlKU9hIR387OTs1kLyUlRStSGqSlpYFhmM8ycFVaWhqcnZ2hp6dX4rdyUQwZMgREBFdXV3YvOjw8HFKpNN+FHtW8qlevXpgyZQoCAgJgZ2eHevXqgYhQv359dOnSRS3m65fIx48f2aS6ueFwOMV2hlexYsUKtUxnoaGhICJ2j5XP56ejHHrO6jVq1FA7oK+vT7q6usV7hVQgwcHB5OTkRBxOmSJ8agSBQECPHz+mpk2bkpeXF50+fbpc6t2zZw9t3LiRateuTbdv3yYvLy8iIoqNjSVdXV3y9fXNcw2Xy6WgoCASiURkYmJCQ4YMoREjRtDdu3epTZs2FBoaSsePHydLS8tykVFbMTc3p8TEREpKSlI7DoAEAkGJ6pLL5ZSRkcH+b29vTzKZjBYvXky7du0ihUKhU2TYzKK0l4gCp02bVjwX/Upm/fr1GDRokKbFKDP29vYgIjx//rxM9Vy9elVtf43H47E9pVKphJWVFSZNmqRm7L1+/XqYmZnlqevDhw/Yt28fMjIycOXKFZw8efKrcM7u0KEDtmzZwv6flJQEIspjIF8UZ86cAY/HUzvm5eWF1q1bQ6FQgMvlKomoBsqylWJqajpXlXpb25g8eXKpIwJoE6r8oaUNl6lCKBSyirlixQpUq1ZNzYpL5SBM/4Z6bNq0KRwdHUFEuHPnTllv44tg7dq16Nu3L/v/0KFDYWJiUuJ6bt26BQ6Ho3asW7du7JbKv25knVCWYa2hoaFnzZo1S9SlVxZRUVEkk8k0LUaZadCgAW3dupXevXtHc+fOLXU9iYmJZG1tTd988w2NGzeOzMzMKCUlhf184sSJZGBgQERE169fp7CwMOJyudS5c2caNmxYme/jS6B9+/Z08uRJdvFrx44d9N1335W4HlNTU3bxVIWrqyt9+PCBiHIW4YRCYeNCKylMc5FjGRSq6XyMBTFixIgym8hpEytXrgTDMKW+/sCBA2ymKyBn89vS0hKJiYm4ceMGDh48iG3btmHJkiUYPHgwFi9ejF69eqF69ep4/fp1+dzEF8CGDRtQp04dfPz4EUSkliKwuMTGxubZbnz16hWICCkpKbCxsYGpqeltlHZYS0SMlZWV1gaZGTZsGFasWKFpMcqNxMTEYu0fHz58GDVq1ICtrS0cHR3h7OwMc3NzEJGaDWh4eDj09fXB5/NZ+9+2bdsiKioKEyZMgKmpKcaNG5dvrKKvGaVSCWNjYzajW2lQxQ76r8G8rq4uatasib59+0JfXz8eZRjWyrXBCqggrl+/Ti4uLpoWo9wwNDQkopwhT0HMnj2bOnbsSHZ2duTv70/e3t7k7u5OLVq0oD179tCWLVuIKOele+fOHTIzM6MZM2aQQqGg2bNn05gxY6hOnTqkVCrp8ePHtGLFCpJKpZVyf58LDMMQl8ulunXrUmpqKr18+bLEdaj2OP/7W9auXZuePHlCLi4ulJ2drc8wDFNgJYVpLhH5jRw5Mr5Ur45KYNmyZahevfoXE1YjPj6eHfao2Lt3LwYNGoQpU6agW7du4HA4WL9+fYF1ZGRkYOfOnfD29oaTkxNOnjyJp0+fws7ODvv374dEIinxnt3XRmJiIhs+08LCAhKJBHv37sXdu3eRlJRUrDr27dsHHR2dPMdVuVfMzc1V7mNylGZYq6+vP2Ht2rVavX7eqlWrPFmePldatWoFmUymdoz+zS0qk8lgampaqM+qUqlEp06d0LhxY+zevZs1zkhOToa9vT0sLS1x8+bNCr2HL4GHDx+iZs2aAHICUhMR+Hw+G6iLiMDhcMDn86GnpwehUAgjIyMIhUIIhUJUq1ZN5RqWp+527dqp1UFEvihA/wrdBBWJRJ41a9YsuNvVAiZPnkyjRo2iKVOmEI9X+lSImmbZsmV09uxZunDhAt27d49u375Na9euJSKi9evXU8+ePYus4/Dhw/TixQu6c+eOmpGIgYEBhYaGVpjsXxpPnjxhzexUhu9t27algwcP0vDhw2nz5s105MgRysrKovj4eIqPjycAxDAMZWdn06pVq9iV2qtXr1KjRo3Yuq9evUomJiaUkJBASqWSdHR0XInoXL6CFKS1AMja2vpMbiNgbUQVO0ibQqaUBplMBktLS5iamoJhGOjp6cHT07NEfpUzZ87ElClTPktzRm1BoVDA0tISp06dQps2bcAwDAICAtjUCy1atICjo2OR9ah639xB2rKyssAwDPr27cv2niYmJktQmmGtlZXVTU0HWS4O3bt3/+zDamzevBlyuRxDhw4t9rzmv/z9998qj4dylu7r4cOHD5BIJKy9cVBQEDgcDqZMmQIgx0aWYZhC0zCqWL9+Pfh8Pvu/yr521apVrHKKxeK/UBrltLCwePI5LLaYm5vDwsIC+/btK9b5iw/ewfzdtz97czSlUolDhw6hS5cuCAgIgFQqBRGhQ4cOmhbtsyUiIoJNsqxSoG+//VbtHB8fn2L1nqrtFNXL9ujRo+BwOHj48GFu5TyFAvSPwX+sGHIjk8nefPz40UbbDcslEgm5uLjQxYsXSVdXl1JSUvI1ht91+TltPP9C7VgrVwv6LrBeZYlabiQnJ5NQKFQ79urVK5JIJKwVUBUlJysri/T09CgzM5O4XC7Vr1+fbt26pXbOmzdvyM7Ojk6fPk1+fn6F1sfhcOjmzZtUv359MjQ0pObNm9OhQ4eIy+USEZGxsXFIQkJCrXyvLaJirrYrplKppJiYGDp37hzdvHmT0tPTadmyZfme+1/FJCI6/bD8/Skrg7179xIR0ZAhQ2jz5s0UHh5OdnZ2VYpZRp49e0aWlpak2n588uRJnnNsbW2pefPmNHz48CLrMzY2piNHjtDw4cMpJSWF1qxZQxwOh1VOpVIpLOjaQjWPq6pBi8nKyiIej0eZmZnk6elJjo6O9P3339ONGzeKXcfjtzEVKGH5kpCQQC9fviQOh0Nt27al//3vfzRgwIAvwsZYGwgNDSU3NzdiGIYsLS0pJSWFXrzI+1LftGkTvX79mo4fP15gXQkJCay98/r162nYsGFsaB+VCxqHwylQxwrdeyjsQm0hOzubeDwePXjwgAwNDSk5OZnkcjk1b96cXr58SRYWFkXWITLUvG9qcZgzZw5FR0fT6tWriYjo4sWLGpboy0Mmk9Hr16+JiNjec/z48XmU0MbGhnx9fWn8+PH5Ki8R0aRJk8jMzIxOnz5N9vb2tG7dOvYzVb+XmZlZ8p6TYRi+jo6OVu9xEhEpFAricrl0+fJl6tOnDy1btozs7e2pWrVqVLduXUpPTy+yDrmowO9Ha1AqlfTjjz9SWloaEeU8ME2bNtWwVF8enp6eFB4eTuHh4RQZGUn+/v504cKFfM/9448/6OXLl/kOfYmIjhw5Qv3796eUlBTWNFOF6n+FQqFTkCyFDWuFQqGwYCNPLYHH41FWVhbdvXuX3Nzc6MOHD+Tq6krBwcGkUCjIysqK/vzzTyIi2vdd3igAm8Z+Hg94165diShnOGViYlIqN6YqiobD4ZBQKKSnT59SVlYW/fPPP9SvX798z3VyciIHBwc2FlNuEhISKCYmhpo1a0Y1a9akyMhItc+NjIyIiEipVBaog4Uqp6oCbUZfX5/atm1Lly9fpkePHlFKSgoZGRmRvr4+vXz5knx9fWnYsGFkZmZGNRwc6Nn/xlB/u3g6+UM7OvlDO5Kbav89EhHt2rWL/Xv06NFkbW2tQWm+XD59+kTR0dHUoEEDIsoZmdWtW7fA8+fOnUtnz57NM0JzcXEhANS4cWOysbHJE/okMjKS+Hw+KRSKgqeWBe2xEFHtjh07anUuThX79++HVCqFg4MDfv/99zwh9ZOSkjBw4ECIRCK0bt0aXC5XzXLjc0EVST4zM1PTonyxvHjxAnZ2dgAAIyMjWFlZFRmhQigU4rvvvmP/V6W5/9e4nd3TTE7OifajMnBo2rSp6jMuSugyxuFyuVo/53z16hVlZWWRrq4uvXv3jrV3zI2hoSFt2rSJYmNj6eTJk7Rv3z46d+5chYSlrEg2bNhA3t7exOfzNS3KF4tAIGB7QZlMRm5ubvTu3Tu6fPlygdcMHjyY1q9fz/6/YcMGMjExoTdv3lBmZiZ7fNGiRUREFBcXR0RE8+fPJ4ZhQAWMYAtTzqTExEStn3POnTuXevToQXPnziWZTEYhISFqoTnyo2PHjlStWjXq06dPJUlZPjRp0oSioqI0LcYXjUwmI6VSSS9evKDExEQ6evQoiUQicnZ2LvCahQsXUlJSEs2fP5+IiB4+fEhWVlakr69PycnJxDAM2dvbswtLT58+JS6XS9HR0cTn8xUAsvKrtyjlLP1dVhIBAQHUpEkTGjBgAFlYWFCbNm3o0qVLlJqaWuh1K1eupEuXLlWSlOVDUlISu1pbRcXA4/HI2NiYFixYQJGRkaSvr09v3rwpNB2Dvr4+eXh40IEDB4gox3BBFZj6/fv3RETk7u5O4eHhRJSzN88wDB0+fJg4HE52QfUWqpyfPn3SbvMgylnFTEhIoAkTJrABlJo2bUrff/99odf5+/uTUqn8LIa2qampdPz4cerbt2+ZAoBVUTSZmZn0/Plz2rRpE5mamlJSUlKebZD/kpyczFoWERF9/PiRGjZsSEREa9asIaKc7AR6ejnphvh8PgGgbdu2kUKhKFjH8puIqoqtre1nsSB05MgREBEWLlyIzp074/Xr13mclvODy+Wy3iwlzSJVWYSEhMDa2ho+Pj74448/NC3OVwERQUdHp1iOEQqFAo6OjpDL5cjKysK+fftARLh//z5SUlLYxSCZTIYmTZoAyMnTqTquo6OTilK6jGk26WUJGDFiBEaNGgU+n4+oqCgYGxsXmSiob9++0NHRQcOGDUFEEAgEuHDhQiVJXDhhYWFo2LAhBAIBNm/erGlxvirmzp2LatWqYenSpUWe26lTJwgEAjY/bNeuXVmPlQEDBqgF+VbFrD137hx7TE9PLw6lUU65XK79zpz/cvToUXh7e0NfXx8A0L59e/zyyy9FXte6dWsYGxsjODgY/v7+MDQ0rGhRiyQsLAwNGjTAzJkzqxynNcTZs2fh6elZ6Dnz588Hh8PB5cuX2WPjxo2DpaUlHj16BIZhsHLlSlYRjYyMcO/ePSxatAhisRhEBENDw/cojXJKpdK3n4vP49y5czF8+HAIBAJkZ2dj7969bLrv4qJQKMDhcHD27NkKkrJwlEolfv31V4hEIsyZM6dqP1ODZGZmws7OrsDEwYmJieDz+Rg9erTa8V69esHZ2Rnu7u6oW7cuAEAgEORJRd+gQQOVwj5FAfpXlMtYalGrntrC48ePyc7OjszMzOj9+/d08+ZNatGiRYnq4HA4ZG1tzYaXLIjMzEz6+PEjRUREsLFiygoAGjNmDO3Zs4fu3LlDQUFBVfuZGoTP59OoUaPU9i9VPHjwgGrVqkVCoZCWLl2q9tmLFy/I1taWdHV12YUkT09P9vMBAwYQ0f/b1nK53LgChShIa5Ez57ytGktrO0+ePIGZmRmMjIzw5MkTPHnyBObm5njw4EGJ6unatSuqVauW72dZWVn4448/YGZmBplMBjMzM7i7u2PPnj1slHUgZ4Fq6NChmDp1Ks6cOYPk5GQoFIpC292xYwdq1679xYT5/BIIDQ2FtbU1FAoFJk6ciDZt2uCHH35As2bNIBaL8ejRozzX+Pj4oHHjxnBzcwOXywWQ80zRvz1mamoqFAoFOnbsCCKCSCQ6hNIMa62trc8/e/asEr6G8mHYsGEgIgwfPhwAsHDhQvbv4qKKEXP//n322D///IOWLVtCKBSiRYsWrMIrlUr873//Q4cOHSAWi3HkyBEAOQlrGjRogKCgIHh4eEBPTw8cDgcDBgxgTbj+S2BgILZu3Vqa266iglAoFNDX14efnx8EAgHq1q0LHo/HJoLKDz8/PzWTvVu3bmHz5s0gIgQGBrLnLVq0CEQEY2Pj9Silcu6+du1aBd5++RISEsLGec3KysKjR49gaWlZ4kS/BgYGbHzYgwcPQiaTYdu2bYiJiSnwmqtXr8LY2JgNGvzf7GdJSUno0qULunbtiokTJ6JFixa4d+8e+3mzZs1w+vTpEslZRcWjUrJTp04ByMmBYm5ujqZNmwLI2YJzd3fHrVu31LZOPDw8YGlpiZEjR0KhUIDH4+HkyZNsvXPmzAERQVdXdzpKM+dMTEy88/z588JO0Spq1qxJxsbGlJSURE+ePKFatWpRnTp18p03FIaBgQE9e/aMiIiWLFlCa9asob59+5KZmVmB13h7e9P79+9p7dq1dOjQoTxuRoaGhvS///2POBwOGRkZUYcOHahnz55sglVHR0d6+vRpCe+4iorGwMCA+vbtS61atSIiIpFIRIsXL6bLly/T8uXLafz48XTnzh3y8vKiOXPmEFFOAuPbt28Tj8cjDodDd+/epWrVqrF1EBHFxMQQwzCUkZFxr8DGC9LanJcGtZ04cWJiZb2lyoNp06bh+++/ZxPIBAcHw9LSskRbEqNGjQKXy8WuXbtgaGiI2NjYCpG1U6dObA/7559/okePHhXSThWlJyAgAAcPHgSQM/pR7Tn7+vqyveS4cePYv8eOHcteK5fLMWXKFEybNg3Tpk1Tq3fQoEHg8XhKIqqG0gxriciuWbNmWptlrLi4u7uXeMg4btw4/OsxgISEhAqR68qVK3B1dQWQE5LRxMSk0KFzFZVPzZo18eDBA5w9exYikQhEhH379rHPxqxZs6BQKGBmZga5XK52rZWVFSZOnIg6dergypUrap+NGDECfD4/i4g4KKVycmxsbD4LE77CWLx4MQYPHlzi66ZNmwYiKvGctbhs2bIFbdu2Zf/v0KEDtDWL+NfIo0ePWFM8lTKqyn/9gX/99VfweDxkZGSwx1xdXVGzZk3WtC83O3fuhL6+fgIK0b9C55wAlJmZmalZWfl6tHw2tG/fns6cOVOia7Kzs2nnzp107tw5tbwj5UlsbCzlTrEokUgoOjq6QtqqouRYWVnRpEmTyM3NTW0/WygU0unTp4no/30zv/32W1IoFGrP2YQJE+jJkyc0ZsyYPHl8ateuTaampsGFClCY5iJnxfZsSEhIRbyYKo2kpCTWcqi4HD16FA0aNKhAqYDXr19DJBKx2ys7duxA+/btK7TNKkoO/b8dLGrVqgVbW1sAQM+ePUFEWL58OcLDw9XSN6akpKBBgwbo0qVLvgb0e/fuhbm5+S8obc9JRJSenh78ua8iGhoakpWVFT169KjY15w7d47at29fgVIRVatWjZo2bUpbt24lopwV31u3btGrV6/Izc2NzM3NiWEYOnr0aIXKUUXhODk5ERHRihUr6PHjx7Rt2za6ffs27dq1i+rXr08TJkwguVxORP+fLHfmzJlUvXp12rt3L+WXH/fhw4dpsbGxtwttuDDNBUAMw/SYO3duWoW+miqYuXPnQk9PD4mJxV949vT0rBQPlfPnz8PJyQmpqakIDg5GnTp18PTp0zy2mJ+LjfOXyJUrV8Dn89nf4u3bt+jfvz/s7Oxw7949td/p/v37ePjwIcRiMaKiCl6uad++fRQRuaKwNZ/CPkTOOLvO5xLoqyBq166NYcOGFfv8d+/ewczMrMIWgnKjVCrRq1cvDB06FEuWLEH//v3VPu/Xrx+MjIwqXI4qCicpKQnx8fEYP348q4hdu3bF2rVroaenx/pxEhHatWuH3377rdD6HBwcoohIF2VUToGDg8NnrZwzZsxQi45WFM+fP2cjsFUGb968gVgshlAoZDNPx8fHY9CgQbh//z7i4+MrTZYqiiYyMhJmZmbYv38/OnfuzPppqlL8URHbbwqFAjKZ7AOK0r2iTgBAMpnsw+fsV/j06VOIxWKEhoYW6/ysrCyIxWK8fv26YgXLRVpamtoe59q1a0FE+OabbypNhiqKT1BQEJo2bQpLS0u4urri/Pnz7KJQt27dCr329evXsLKyuoEi9K5YMYJ4PN7dO3fuFOdUrcTJyYmCgoIoICAgT9jM/ODxeFS7dm16+fJlJUiXg0AgUDMPdHNzIyKibdu2VZoMVRSf6dOnU9euXaldu3bk4OBAM2bMIEdHR2revHmR5qL//PMPUlJSilzlKzQ/pwodHZ1+s2bNWj979mxB8cXXPgYNGkQWFha0YMGCYp1bq1atIgOFlRSFQkEXLlyg69evU3JyMiUkJFC9evXI0dGRPDw81HJuBgYG0uHDh0mhUJSrDFVolo4dO8YcPny4NYC7hZ5YVNf6r/LK6tat+1nPO4Gc4YS1tTVmzJhRZJSBBw8eQCKRFOji9V8yMjLw5MkT3Lp1C48ePcrXfzMqKgr16tWDhYWF2gpfs2bN0KhRI5ibm2PevHnsfmxqamqVf+cXhlKphFwuj6JCzPZQkmEtgIiIiIjUooI1azvVqlWj27dv071798jFxYViYgrOy+nq6ko1atSga9euFVnvX3/9RaamptShQwcaOXIkBQYGkqmpKdWoUYPOnj1LREQpKSkkkUjo7t27FB4eTl27dmWHP6ampjR27Fg6evQonTlzhpo0aUL+/v60f//+PNmrq/i8efToEXG53IcAig7YXpT2qopcLv/f8ePHK/c1U4FMmDABAQEBhfagP/74I/r161fkHmPDhg1Zfz8VMTExGDZsGKZMmcIeu3//Pvbu3au2kvfs2TMsWbIEHTp0gI2NDX7++WcsXrwY27dvh52dHWbPnl21x/kF8euvv2YIBIIRKM6ItTgnASAOh9N29OjRn5X7WGFkZmYiICAA3bp1Q2pqar7nxMXFwd3dHY0aNcLDhw/zPefjx48QCoVISkpSO75161ZYWFjg8ePHxZbpwoUL6NGjB2QyGXr27ImIiAh4eXmhd+/e+PjxY/FvrgqtpWHDhlFEZIti6FyxFoSIiBiGMbC3t38dGhoqLmPPrjWkp6fTkCFD6NWrV7Rt2zZycHDIc45CoaBVq1bRsmXLqFu3bpSdnU0SiYSUSiUlJibSnj17qGfPnmqLTEqlkoyMjOjChQvk4eFRYrni4+PJ0dGRZs2aRb6+vrR48WI6dOgQicViunLlConFX8xP8FWRkZFBtra2HyIiIqyKPrvwdAxqAEhJS0uL+m8S0M8ZgUBAf/31F3Xv3p0aNmyYJ5IaUU568HHjxtG0adPI1NSUZDIZxcbGUmJiIhkaGtLWrVvpp59+UrsmLS2NUlJSyMbGplRymZqa0tGjR+nmzZvk5eVFY8aMoffv31N4eDjZ29tTz549KSIiolR1V6E5rl27Rjwer+B0Zf+h4MSd+ZCenn7gzJkzLn379tX61IDFhWEYmjx5MvXo0YPatGlDYWFhtGDBAjIwMFA7b9iwYXmujYiIoFGjRlGjRo2od+/elJGRQS1atKB3795RQEBAmXq4Bg0aUEhICGVkZFCPHj1IJpNRp06d6NOnT7R7925q1qwZicVievXqFdWoUYM+ffpESUlJVKtWLWrevHm+xtZVaJajR48mh4eH7yr6zH8pzthXVYioQdeuXb9YV/2YmBj06tULtra2OH/+PBISEnDp0iXcunUrj7uZQqGAQCAAwzAQCoVqWyOmpqa4ceNGmeWJiIjAvXv38ODBA1y6dAmbN2/G9u3bce3aNbVgUq1btwYRYdSoUXBxcQGXy8WKFSsQGfnZB7H4onB2do4iIhMUV9+KeyJylJMrl8ujiorB+rlz5MgR2NjYQE9PD15eXnBycoKXl1eeRSGGYcDhcJCVlYWTJ0+Cw+FgyJAhmDlzZplluHnzJjZs2IBr167lMcC/fv067t69i5iYGOjp6bGhF+Pj46FUKrFr1y4QETgcDg4cOAAASE5OxqtXr/Du3bsiY+hWUf5ER0dDLpc/R0n0rSQnAyALC4tdX1sIR4VCgXXr1kEikeDu3bvIysqCl5cXGwZz4sSJWLhwIUxMTODn54ddu3aVuU1VfFRnZ2cQEfbv3w8gZxM7dy+du6xYsQIdOnRA27ZtUbduXYjFYlSvXh1ZWVlqvXuzZs3KLF8VJWPJkiUZJiYm36MilZOIvNq1a/fFDm0LY8eOHbC3t4ePjw8MDQ3h5+cHkUiEFStWoHPnziAiSKXSEvmNFsSdO3dQv3591o+wS5cuanI0a9YMw4YNQ4sWLdhhrampKYYMGYKlS5fiwoUL+P333/H48WMolUrUqVMHDg4OqFevHlauXFlm+aooPkqlUuUiZoYKVk5GJpOFxcXFVeLtaQ/Dhw9n53e52bhxI9szGRgYwN/fH8uWLcOkSZOwYcMGnDhxQi34U3EpbuS/RYsWsRHnq9Au/g3PehYl1LUSZ64GgIyMjLVbt279vKN+lZJly5aRubk56enpqV5WRES0du1a4nK51K9fP7p9+zb17NmTXrx4QXw+n06cOEEzZ84kW1tbOnfuXInaMzY2LtZ5kydPpnbt2pWo7ioqh5UrVyaGh4f/WtLrim2EoHYRw4hr1KgR8uzZM/MSX/wFEBERQW3atCGhUEgdO3akdevW0cuXL8nFxYUeP36c53ylUkknT54kHR0dat26NfXu3bvKFewrIS0tjezs7CIiIyOtAJTMvaikXa2qWFhYXMidWetrIz09HQcOHECdOnXYOKYcDgcCgQBBQUEAckJbXL9+He7u7nkWb/4bx7SKL5O//vpLIRaLl6IUOlZq5eRwOAHDhg0r3oToC0apVGLt2rVwdXVFtWrVYGBgAD6fr5b2TU9PD1u3bsWFCxdw+PBhWFtba1rsKioJT0/PKCKqjspUTiLiyWSyiMoIgvU5kJGRgXXr1qFNmzasUjZv3hzBwcFqvWTv3r2xbNkyDUpaRWXx+vVryOXyByhtB1jasTSAbAB7Dhw4UD6pnT9zdHR0aNiwYXT06FGyt7cnW1tbOnnyJLm7u6tF+37x4gU1aNBAg5JWUVmsW7cuNTExcXFpry+1chIRRUZGrli2bFnBHstfIVwulx49ekTOzs5sSjgV2dnZ9OHDB8rOztaQdFVUFkqlkrZs2ZKSmpq6p7R1lEk5Abx48+bN+5CQkLJU88UhEAho48aNtGPHDpo5cybrQXLnzh3icDjUqFGjcm0vOzubHj58SJ97TpsviYMHDyqVSuVJAKmlrqS042FV4XK5Pm3btq2YBJafOUeOHAERgWEYSCQS1K1bF82bNy/XNqKjo9GpUyeYmpoiICAAUVFRGDBgAN6+fVuu7VRRfBQKBRwcHKKJyApl0K0SuYzlR3Z29kW5XB728OFDkaura1mr+6JQhbdcv349HTt2jPbt20dEOU7eAkHpAhmmpKTQ1atXKS0tjZ48eUK//fYbxcbGElFO1rKVK1fSli1b6MWLF+Tj40Pjxo0jS0vLYtUNgOLi4ujYsWOUnZ1NgwcPLpWMXzt79+5VJicnHwXwviz1lMoI4b/weDzvFi1aHDl9+rSozJV9QaSmplLt2rXp9evXZGZmRlFRUdS7d28yMDCgDRs2EIdT/FmFQqGgadOm0e+//04AqEmTJiSRSCgsLIwNQsbj8cjBwYFGjRpFfD6fnj9/Tjt27KDdu3eTra0tvX37lqKjo6ldu3akq6tLT548oV9++YXevHlDV65cYYfFJiYm9OnTp6qQnKVAoVBQjRo1Yl69elUHQHiZKitLt5u7yOXy68HBwRU5WvgsSUlJwYABA2BqaooWLVqwSYqaNm2aJ3BXbGws0tLyzxl16tQpuLi4qCXHmTVrFvh8Pvbu3Qsiwvfff5/nuh9++AE8Hg/W1tZo1KgRmjRpgho1amD48OGwsrJCx44dcfr0aWzbtk3NSKJdu3bl+0V8JWzdujVbKpWuQznoVLkpJ5fL9WzWrNlX6a1SHLKzszFs2DAEBASwCuDj44ObN29i586dGDJkCIyMjEBEaNCgAXbv3s0qb3p6Ojp37owZM2ao1enq6gp/f3906tQJzZo1g4mJSZHG9UqlEufOncOqVavyRAy8c+cOtm/fjmnTpsHExKRS01F8CWRlZcHW1jaaiCTQJuVETu95uTwiAHypXLx4kVXMWrVqwdfXF3Z2dmjZsiVGjBgBXV1dEBHs7e3h4uKCPn364Pz586hXrx68vLxw9epVtq7IyEjW0OG3336Dk5MTnJycyi2MZr9+/WBtbY0aNWrgxIkTVQ7axWDjxo1ZUqn0d5STPpWrchJR3UaNGlX1noUQGhoKNzc39O3bFyYmJli4cCF++uknEBGWLl3KJsOZNGkSiAgCgQDLly/Po3RZWVmYMmUKiAj79u3D77//jjt37pSbnDdv3gTDMNDT04OLiwv8/f3xtboJFofMzEzY2NiU2GezsFKuyomc3vP8lStXKvJ7+GI4fvw421MSER49egQgx8TP1tYWRIShQ4cWWkfDhg1Rr169cpdt7Nix4PF4SElJQWZmJvr06YP69esjIiKi3Nv6Eli7dm2mRCJZgvLs7MqzMuT0nrW9vLyqes9iEhYWhtGjR2PDhg1sELGQkBAQEcRicZG5OevXrw8DAwPcvHkThw8fxsGDB5GSklImma5evQqJRMJmagZy5qNt27aFRCLBggUL8M8//2Dt2rVo1aoVxGIxZDIZzMzM4Ovri1GjRuHZs2dlkuFzIiMjA1ZWVlFEZAptVk7k9J7Hjxw5UjVJKQM3btyAiYlJkdm1w8LCIJfLweFwoKOjAz6fDw6Hg4MHD5a67S5dukAoFCIkJCTPZ9euXWNXm319fbF161Z8+PABYrEYTZs2xalTpzB37lyYm5tjxIgReP78eanl+Fz4+eef0yQSyUKUd0dX3hUip/eUWVlZRVVlyCob9vb2KM321IgRI8DhcGBoaAgjIyNwuVwYGxsXu0d1cHAAh8PBggULkNtnNysrC927dwePx1M7X6FQgIjw9OlT9tiFCxfYkC3lEVNJWwkNDYVUKn1NRaSQL02pEOUEQMbGxsMHDx785f4qFUxiYiIb7rI0/PHHH1i2bBlWrFiBnj17QkdHp9grrikpKdDX1wcRgcfjsdctWrQIRITNmzfnucbc3ByNGjVi/7exsUGjRo3Qo0cPLF++vFT3oO0olUp4enrGcrlcb1REJ1cRlSKn92SkUunNixcvVtBX8+Xj6OiI8og2IRaL8wQky4/ExETMnTsXVlZWYBgGPXv2BBEhMDAQANCzZ0+Ym5vne+2kSZNgYmICADhx4gQYhkF0dDQuXrxYrls82sTq1aszpFLpRlSUDlVUxchR0Gp2dnbRBWXxqqJg0tLSoKOjg0uXLpW5LiMjI8ydO1ftWHx8PLvnam1tDScnJxARDA0N0atXL4SFhQEALC0t4efnBwB48+YNdHR0wDAM0tLSkJGRgUePHuGHH36AWCyGSCRCq1atYGxsDF9fXwA5vYu3tzcMDQ3x6tWrMt+LtvD+/XtIpdL3RGSIz1E5AZBIJPr+22+/Vc+PV0WRxMTEQCKR4M8//yxzXT179oRAIFAbIisUClhZWYHD4WDEiBFo3rw5bt26pXZdVlZWnrlkUlISDAwM0Lx5c1hbW7PxclXR7/X19eHj46MW/SEpKQnW1ta4fft2me9FG1AqlWjRokWcQCBojQrUnTL5cxaHuLi4JX/99VfYnTt3KrqpLwKlUklz586l5s2b08CBA2nQoEFlrlMgEFB6ejolJiayxzgcDg0aNIj09fXJ3t6eduzYQfXr189zrY6ODu3cuZP9f9myZZSVlUX//PMPvXv3jiZNmkRxcXGkVCpJoVBQSkoKXbhwgY3+AIA6d+5MTk5OFB8fT7///jtdu3aNjh8/Ttu3b6ctW7bQzp076dChQ3Tt2jXViIslMzOTrl27RiNHjiRzc3OysbGhFi1a0Jw5c+jjx49ERPTu3Tvavn07JSUllfm7Kg47d+7MDgkJOZ+WlnaqQhuqSM1XFSKq6eTkFFNYFukqcli5ciVsbGzw999/l5vJXPfu3UFE2LRpk9rxd+/esUPbxo0b53ttnz59wOfz4efnB4FAAB6PhylTpuDEiROIjo4usu3MzExUr14dxsbG8PHxQaNGjSAUCuHs7IzevXujX79+6NGjB9q3b4/q1aujf//+GDFiBGrUqAETExPweDzUrFkT8+fPx6tXr/Dq1SucOnUKo0ePhomJCSwtLSESiVCnTh1MnTq1XL6vwvg350k4lfOeZn6lUpQTAJmbm/8UFBRUtt3xL5iUlBScOHGCDa9Zlu2HOXPmoFq1arCxsYGVlRX09PRARNDX12fPefPmDUaOHAmGYTBgwIAC21MoFJg1axZq1aqF0aNHlypqfW5OnDgBX1/ffCPZr169GkSEqVOn4vHjx4iNjS30BfXp0ye8efMGGRkZOHfuHBo2bFgm2YpDYGBgvKGhYXdURqdWGY0gp/fkSySSFyVJw/41QUSoV68epk6dColEAi6XixEjRpSqLjc3NxAR6tSpg/Hjx2PWrFnw9vZmDeX9/f1BRJBIJJg2bVo530np+fTpk5pLXEmIjo6Gnp5eOUukzqFDhxRSqfQ0KktnKqshAMTj8VyrV68eU9z8H18LR44cgbW1NXKvav/6669gGAY6OjrQ1dVFixYtEBoaWqyhLpfLZQNdq8jIyMD+/ftRvXp1uLi4YP369RVyL5piz549aNWqVYXVHxoaCplM9oHKyR2sOKVSlRMAGRsb92nRokV8lQtSDg8fPoSTkxMOHz6c57OUlBTs2rULy5Ytg6WlJWtxU1SKQSLCH3/88VW5eXXp0gUbNmyokLqTkpLg6OgYy+VyPVCJulLpygmAJBLJsu+++65qewXAvHnz4OjomCdz9n9JS0tjIxRwOJw8+5a5GT9+PBiG+aLN5nJz8uRJWFpaltqaqjAUCgXatGkTb2JiMhiVrCcaUU4i4kgkkks7d+786hOGBAUFwcbGBn/99VeBD1dkZCQcHR1hamoKhUKBOXPmgMfjYdiwYQXWy+Px8kQ6+BJ5+vQppFIpzp07VyH1z5o1K1kikayBJvREE40iR0GNJRLJ63v37pXT1/h5cvfuXdjZ2YGI8MMPP7DH09LScPDgQYwcORJ8Ph/Vq1fHmzdv2M/37NkDhmHg6+sLT09PWFtbo06dOujYsSPu3LkDIkJS0pc9OPn48SOqVatWLoYa+bF///5siURynYi4+JqUEzkK6mRjYxNdnP2yL5WPHz+iXbt2EAgErHnbrVu3wOfzwefzYWVllW/gLgBYvHgxvL290bhxY/Tv3x+dOnWCiYkJa4b3JZOUlIR69eph/vz5FVL/48ePIZVK31bGfmZBRaPKCYAMDAw6NGjQIO5rTIkXHx8PuVzOGgLs2bMHCoUCAoEAbdu2LXF9KqMCPp+PunXrVoDE2sPYsWPRt2/fCjGoj4uLg52dXTSPx6sFTXZemmxcVczNzX8cMWLEV+f8eeLECTg4OGDGjBmoUaMGGIaBVCqFQCAo0UprZGQkZs2ahbp167KKPnjw4AqUXLOcPXsWlpaWFRLTKDs7G02aNIkzNDTsCg3rhcYVE8hxL5NIJCdWr15dNvOTzwyFQoFatWph48aNAIDDhw+jY8eOaobmhfHq1SvUr18fDMPA1NQUnp6emDJlCoYMGVIs07rPkSdPnkAikeDMmTPlXrdSqcTo0aOTJBLJb9AGvdC0AKwgRHoSieTmhg0bvhoFVUUQ8PDwUFvsKS4eHh4wNzfHhQsXKkA67ePx48cQiUQVsgCkVCoxadKkJIlEsouIONAGndC0AGrCEOl/bQqanJyMuXPnQiwW4++//y72dcHBwew89WuhW7duWLRoUbnXq42KCW1TTnylCgrkrNCKRKJiRVl/9eoViAg2NjYIDw+veOG0BAsLC9YJvLzQVsWENionvmIFXbFiBapVq4YPHz4Uet6cOXO+in3M3MTFxcHQ0LBcTRK1WTGhrcqJr1hBZ86cifbt2xe6RaBQKCCVSmFhYfHV9JyrVq1iYxmVB9qumNBm5cRXqqAZGRlwdXXFli1bCj1PFZ1PR0cH58+fB5BjbdS2bdsCzQAfPXqEBQsWoHv37mrWSNqOQqGAk5NTuS18fQ6KCW1XTnylChocHAyxWFzk/HPevHnw9vYGwzDQ1dUFwzAgIly/fp09R6FQYMOGDahduza75aKvrw9ra+sKvovyY8uWLahXr165GBx8LoqJz0E5kUtBv6Z90KVLl8LDw6PAfJ25uXnzJvbu3cumcahTpw4MDQ1hY2PDhvpo1qwZG6B6xIgRkMlkFX0L5UJERARMTU3LJUmTQqHA+PHjPwvFxOeinAC7D3pi+PDhn76GWERKpRLdu3fH0KFDS9RjTJw4ES1btoSpqSkEAgEmTJiAyMhItXMWLFgAhmHg4eGBjRs3Ys+ePaXaZ60Mpk+fjuHDh5e5nsTERPj6+sZLJJLfPwfFxOeknABrSfRjgwYN4r5UC5jcfPr0CXXr1sWsWbNKfK0qf+f48ePz/fzWrVtwdXWFnp4eOySWyWTw9fVFp06dMHr0aI0vNq1evRo2NjZlfnE8f/4c9vb2MUZGRn2hBc9xcYvGBShNMTAw6GBjYxOlyoD1JRMVFQUej4dt27aV+FoiKrZP54ULFzBlyhTUrVsXderUga6uboWG/SiKDRs2wNbWFi9fvixTPceOHVNIpdL3PB6vLrTg2S1J0bgApRacqIZEInn9NThsT5gwAc2aNUNycnKxr1H1nKXdF7S0tISOjk6pri0rHz58gJmZGZ48eVLqOpRKJX766acUiUQSTERiaMEzW9KicQHKJHyOw/alyZMnJ33J8XLS09PRu3dvWFpaYvny5fmGlfwvNjY2ZYpG5+npCR6Ph4YNG8LOzg48Hq9UGc9Kyu7duyEWi/HLL7+Uuo6UlBR07NgxQSqVbiIiPrTgWS1N0bgAZb6BnJAny1q0aBH3pUf1u3HjBnr27AkTExOMGDECwcHBBS4W6ejooHbt2qVu6/nz5/Dw8EDLli3Rt29fEBH8/f0rJGiYUqnE5cuXMX78eEgkkjKtzL558wY1a9aMNTU1HQUteD7LUjQuQHkVY2Pj3tWrV4/5GuLihoeH48cff4SdnR3s7e0xZcoU3Lx5U01RfXx8YGBgUG5tLly4EBwOJ0/U+LKQnp6OP//8E25ubnBycsKPP/6I0NDQUtd37tw5yOXycB0dnUbQgmeyrEXjApRn4fF4rhKJ5EVQUFDK17LdcufOHcyYMQMSiQTDhw/H8+fPMXDgQBARVq1aVab6w8LCcODAATx//hzz5s0Dh8PJsy1TGhQKBQ4cOAAHBwe0atUKx48fL1OPnJiYiEGDBiVKJJKbRGQJLXgWy6NoXIByvyEivlgsnu/k5BRTGXMkbeHVq1cYOXIkBAIB+Hw+LC0tIZfLS5Q+4fr16/D19YVMJgOHw2HNA1XRFfr06VMmGbOysrBq1SpUr14dHh4eOHr0aJnqA4AjR44orKysooyNjYcREQMteAbLq2hcgAq7MSJniUTycOLEiZ++pvygv/zyC8aOHYvQ0FDo6urC3t6+WNc9fPgQPB4PtWrVwqRJk3Dp0iW2N1Pl8ly+fDmysrKKZbX0Xy5fvow6deqgRYsWuHbtWplN8aKjo9G5c+d4qVR6hohk0IJnrryLxgWo0Jsj4ohEou/t7Oyiv5YM20ePHoW/vz8AQCKRICAgoFjXNW/eHNWrV8/3M1XEBqFQyPaoDg4OxapXoVDgp59+gkwmw65du8qslEqlEtu3b8+Wy+URhoaGPaAFz1lFFY0LUCk3SVRNKpXeHDx4cMKXHgX9woUL8Pb2BpBjn8swDB4+fAiFQoExY8bAzc0NjRo1wtChQzF27FiIxWIYGBiAiArdvli0aBHmz5+PgwcPYvv27SAi8Hg8tq2C+PHHH+Hl5YX379+X+d4+fPgAX1/fOJlMtk+TISsrq2hcgEq7USLG2Nh4mJWVVdSRI0e+2E3RJUuWqGUnc3V1hUgkAp/Ph66uLjp16gQ/Pz8YGRmBYRh8++23mD9/PpydncEwDCQSCQYNGoRRo0Zh165d+Q5hMzIy0KtXL7Y3LSisaVZWFmxsbMrs6qVQKLBmzZoMmUz2XldX1x9a8DxVRtG4AJV+w0QymUx2zNPTM+by5cv40mjXrh12797N/v/PP//A1NQURIRHjx6xx7OysvKE/IiOjsbAgQNhZ2cHa2trtcUgDw8PtXMHDx7MfrZgwYI8ciQkJGDQoEFo1apVqYeySqUS+/btUzg6OkbLZLL1RGQILXiGKqtoXACN3ThRbblcft7b2zvmxo0b+BJITU2FUChk47kuXrwYRAQTExPMmzevVHVu2rQJRARPT0+Eh4dj6dKlaoGwzczM8hjIHzp0CFKpFEOGDMGnTyUPR6xUKnH48GGls7NzjFwu305EVtCCZ6ayi8YF0HQhorpyufySj49P7Oe+9XLs2DH4+Piw//ft27dMTtUKhYLN9fnfIpVK8xgkZGVlYcqUKbC2tsaVK1dK3J5SqcTJkydRu3btaLlcvpeIqkELnhFNFY0LoC2Fy+XWl8vl11q2bBn7uXq7BAUFsZmq79+/DyICh8MpU53h4eEICQnBgAEDwOVyIZPJcODAAbVzlEoljhw5gtq1a8Pf379UAa3PnTsHNze3GLlcfoiI7KEFz4Smi8YF0LbC5XK9ZTLZ7bZt28Z9TqaAoaGhsLKywpkzZzB37lwwDAM3Nze1kCWlQaXkRAQulwtLS0u4urqia9euOHr0KG7evIl69eqhVq1aOHDgQInnl5cuXYKHh0eMXC4/TkQ1oAXPgLYUjQugrYXL5frI5fI7DRo0iN6xY4cyPT0d2syUKVMwduxYPH/+HAzDlNl0D8gxPjAwMECNGjVw+PBhTJo0CWPHjkVgYCCrsHp6etixY0eJzO8SEhKwatWqLBcXlygLC4szRKTRhEHaWjQugLYXInKQSqUrZDJZ+NChQxPu3r0LbaRjx45YsmQJmjRpAj6fXy51Nm/eHCKRiFW8xMREBAcHY+rUqSAiuLq64u3bt8WqS6FQ4Pz58wgMDIyRSqVvRSLR7C/Vsqe8isYF+FwKEXE5HE5bS0vLf2rUqBG9fPnyzIrIclVaAgMDsW7dOvB4PAwaNKjM9V2/fp0dylpYWMDCwoLtRadOnYqQkJBi1fPu3TvMmTMn1cbGJsrS0vJvIvL+0mxgK6poXIDPsRCR2MTEZKpUKn0dEBAQc+rUqQrxcywujx8/hlgsxp07dzBr1iwQEZuIt7TExsZi+vTpICI0bdoUYWFhxb7H9PR07Nq1S9mwYcNouVz+yMDAYOjXtkdZHkXjAnzOhYgYIvK0sLDYKZfLo7p06RKzdetWZWUGxrp58yYkEgm2bt3KZsS2sLAo0GqnKJKTk3Hp0iX89NNPqFevHmxtbYuVbu/ly5dYvXp1dqtWraKkUmm4VCr9vWqBp2yFQc5DVkUZYRiGS0QeZmZmHXR1dQMFAoG0TZs2Oh06dDD28fEhAwODcm9z5syZtGDBAjp06BB16NCBGIYhsVhMt2/fJhsbm2LVMXLkSIqPj6fIyEh68eIFxcXFUe3atcnHx4datWpFbdq0IYZh8lwXFxdH586do/3798ddvHhRCeBVUlLS3k+fPh0johBUPVhlpko5KwiGYQyIqKlcLu8GwE8mkxl06tTJMCAgQM/Dw4O4XG6Z2wgMDKSDBw8SAGrcuDFdvXqVpkyZQr/88ku+5yclJdHu3bspLS2NMjIy6OzZs3T8+HFavnw5ubi4kKOjI1lbWxOHw8lzbUZGBl25coWOHj2afOTIkbSkpKR4hUJxNCoqaj8R3QCQWeYbqkKNKuWsJBiGkfH5/JZSqbRndna2u6GhId/Z2Zk8PDwM69Spo+fs7EwODg6ko6NTrPrCw8PJwsKC9PX1qVu3brRlyxY6cOAAderUqcBrxo0bR7///jv7/9KlS8nPz49cXV3ZY6mpqfTs2TN6+vQp3bt3L/nu3btpL168oPT09DQul3s5PDx8t1Kp/AdAYum/jSqKQ5VyagiGYQRE5MgwjLOZmVl9fX19j8zMTHs+n29gZ2eHevXqCerVq2dUo0YNEolEZGRkREKhkAwMDGju3Lk0d+5cti5/f386ceIEERHl/j1jY2PJ3Nyc/d/Gxob69u1Lrq6u5OjoSKmpqfTkyRPcvXs38d69e5kfPnyg7OzsJD6f/ywlJeVWXFzcXSJ6QkSvAWRV0ldTxb9UKaeWwTAMh4hsiMhZKBS6GRsbezAMYw5AqFQqhQqFQi8jI0OYnZ2tQ0ScrKwsXnZ2NhcAw+VyycjIKEtHR0eRmZlJGRkZDOUYCygYhlHo6ekl8ni8FA6Hk8QwzCcAkbGxsTfT0tIeUY4SRlTNFbWHKuX8zGEYRoeIhP8WIyJSEFGSqgDI1qB4VZSBKuWsogotJe+yXBVVVKEVVClnFVVoKVXKWUUVWkqVclZRhZZSpZxVVKGl/B8qMI9T/wFr4AAAAABJRU5ErkJggg==\n", + "text/plain": [ + "
" + ] + }, + "metadata": { + "needs_background": "light" + }, + "output_type": "display_data" + } + ], + "source": [ + "ax = geoplot.polyplot(\n", + " world, projection=geoplot.crs.Orthographic(central_longitude=360-ha_all.deg[0], central_latitude=37), figsize=(8, 4)\n", + ")\n", + "geoplot.pointplot(gdf.to_crs(\"epsg:4326\"), projection=geoplot.crs.Orthographic(central_longitude=360-137, central_latitude=37), ax=ax)\n", + "ax.outline_patch.set_visible(True)\n", + "ax.set_global()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "64d7ea96", + "metadata": {}, + "outputs": [], + "source": [] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "dbe6c876", + "metadata": {}, + "outputs": [], + "source": [] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a6faad99", + "metadata": {}, + "outputs": [], + "source": [] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "98fa3f95", + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.9.7" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/examples/0X_simulation_chain.ipynb b/examples/0X_simulation_chain.ipynb new file mode 100644 index 0000000..f594388 --- /dev/null +++ b/examples/0X_simulation_chain.ipynb @@ -0,0 +1,161 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 1, + "id": "42e51422", + "metadata": {}, + "outputs": [], + "source": [ + "from pyvisgen.layouts.layouts import get_array_layout" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "719cee21", + "metadata": {}, + "outputs": [], + "source": [] + }, + { + "cell_type": "markdown", + "id": "7135e0e8", + "metadata": {}, + "source": [ + "### Define radio interferometer array layout\n", + "\n", + "Available layouts:\n", + "* vla\n", + "* vlba\n", + "* eht" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "fb73812f", + "metadata": {}, + "outputs": [], + "source": [ + "array_layout = get_array_layout(\"vlba\") # in rc" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "a4f1f2bd", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "Stations(st_num=array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9]), name=array(['MKO', 'OVRO', 'BR', 'NL', 'HC', 'KPN', 'PT', 'FD', 'LA', 'SC'],\n", + " dtype=object), x=array([-5464075.238656, -2409150.471188, -2112065.261576, -130872.556729,\n", + " 1446374.806874, -1995678.891541, -1640953.992891, -1324009.374466,\n", + " -1449752.637707, 2607848.664542]), y=array([-2495247.871441, -4478573.093114, -3705356.502894, -4762317.087045,\n", + " -4447939.68308 , -5037317.693848, -5014816.024485, -5332181.952906,\n", + " -4975298.573645, -5488069.500452]), z=array([2148297.485741, 3838617.326057, 4726813.649034, 4226850.993404,\n", + " 4322306.19968 , 3357328.002045, 3575411.772132, 3231962.377936,\n", + " 3709123.828301, 1932739.778597]), diam=array([25, 25, 25, 25, 25, 25, 25, 25, 25, 25]), el_low=array([15, 15, 15, 15, 15, 15, 15, 15, 15, 15]), el_high=array([85, 85, 85, 85, 85, 85, 85, 85, 85, 85]), sefd=array([ 110, 11900, 560, 4900, 2900, 1600, 7300, 4744, 4744,\n", + " 4744]), altitude=array([5030, 3185, 4640, 4205, 2850, 2550, 2800, 3210, 3210, 3210]))" + ] + }, + "execution_count": 3, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "array_layout" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "6cace9fb", + "metadata": {}, + "outputs": [], + "source": [ + "import torch\n", + "from pyvisgen.utils.data import data_handler\n", + "from pyvisgen.simulation.data_set import create_sampling_rc\n", + "from pyvisgen.simulation.visibility import vis_loop\n", + "from pyvisgen.utils.config import read_data_set_conf\n", + "from pathlib import Path" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "e2d892fa", + "metadata": {}, + "outputs": [], + "source": [ + "config = \"test_conf.toml\"\n", + "conf = read_data_set_conf(config)" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "id": "fb870908", + "metadata": {}, + "outputs": [], + "source": [ + "data = data_handler(conf[\"in_path\"])\n", + "samp_ops = create_sampling_rc(conf)" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "id": "ff3e2b78", + "metadata": { + "scrolled": false + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "(16245,)\n" + ] + } + ], + "source": [ + "SI = torch.tensor(data[0][0][0], dtype=torch.cdouble)\n", + "vis_data = vis_loop(samp_ops, SI)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "bb6d8ab3", + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.9.7" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/pyvisgen/fits/data.py b/pyvisgen/fits/data.py index 83acb8c..2a315e2 100644 --- a/pyvisgen/fits/data.py +++ b/pyvisgen/fits/data.py @@ -30,7 +30,11 @@ def __getitem__(self, i): return self.open_file(i) def get_files(self, path): - return np.sort(np.array([x for x in path.iterdir()])) + import natsort + + fits_files = [str(x) for x in path.iterdir()] + fits_files_sorted = natsort.natsorted(fits_files) + return fits_files_sorted def get_uv_data(self, i): with fits.open(self.files[i]) as hdul: diff --git a/pyvisgen/fits/writer.py b/pyvisgen/fits/writer.py index 9f04990..083122e 100644 --- a/pyvisgen/fits/writer.py +++ b/pyvisgen/fits/writer.py @@ -15,9 +15,7 @@ def create_vis_hdu(data, conf, layout="vlba", source_name="sim-source-0"): w = data.w - DATE = data.date - int( - data.date.min() - ) + DATE = data.date - int(data.date.min()) _DATE = data._date # central time in the integration period diff --git a/pyvisgen/gridding/alt_gridder.py b/pyvisgen/gridding/alt_gridder.py index 7f95954..8038e3a 100644 --- a/pyvisgen/gridding/alt_gridder.py +++ b/pyvisgen/gridding/alt_gridder.py @@ -98,11 +98,11 @@ def ms2dirty_python_fast( # xkernel = kernel(pos - ratposx + xle) # ykernel = kernel(pos - ratposy + yle) for xx in range(supp): - foo = vis #* xkernel[xx] + foo = vis # * xkernel[xx] myxpos = (xle + xx) % ng[0] for yy in range(supp): myypos = (yle + yy) % ng[1] - grid[myxpos, myypos] += foo #* ykernel[yy] + grid[myxpos, myypos] += foo # * ykernel[yy] # loopim = np.fft.fftshift(np.fft.ifft2(grid)*np.prod(ng)) # loopim = loopim[slc0, slc1] # if do_wgridding: @@ -116,9 +116,9 @@ def ms2dirty_python_fast( def get_npixdirty(uvw, freq, fov_deg, mask): - speedOfLight = 299792458. - bl = np.sqrt(uvw[:,0]**2+uvw[:,1]**2+uvw[:,2]**2) - bluvw = bl.reshape((-1,1))*freq.reshape((1,-1))/speedOfLight - maxbluvw = np.max(bluvw*mask) - minsize = int((2*fov_deg*np.pi/180*maxbluvw)) + 1 - return minsize+(minsize%2) # make even + speedOfLight = 299792458.0 + bl = np.sqrt(uvw[:, 0] ** 2 + uvw[:, 1] ** 2 + uvw[:, 2] ** 2) + bluvw = bl.reshape((-1, 1)) * freq.reshape((1, -1)) / speedOfLight + maxbluvw = np.max(bluvw * mask) + minsize = int((2 * fov_deg * np.pi / 180 * maxbluvw)) + 1 + return minsize + (minsize % 2) # make even diff --git a/pyvisgen/gridding/gridder.py b/pyvisgen/gridding/gridder.py index b6033f5..9820489 100644 --- a/pyvisgen/gridding/gridder.py +++ b/pyvisgen/gridding/gridder.py @@ -1,24 +1,27 @@ -import cv2 import numpy as np from tqdm import tqdm from pathlib import Path -from radiosim.data import radiosim_data from pyvisgen.fits.data import fits_data from pyvisgen.utils.config import read_data_set_conf +from pyvisgen.utils.data import load_bundles, open_bundles from radionets.dl_framework.data import save_fft_pair import astropy.constants as const -from pyvisgen.gridding.alt_gridder import ms2dirty_python_fast, get_npixdirty +from pyvisgen.gridding.alt_gridder import ms2dirty_python_fast +import os + +os.environ["HDF5_USE_FILE_LOCKING"] = "FALSE" def create_gridded_data_set(config): conf = read_data_set_conf(config) - out_path_fits = Path(conf["out_path"]) - out_path = out_path_fits.parent / "gridded/" + out_path_fits = Path(conf["out_path_fits"]) + out_path = Path(conf["out_path_gridded"]) out_path.mkdir(parents=True, exist_ok=True) - sky_dist = radiosim_data(conf["in_path"]) + sky_dist = load_bundles(conf["in_path"]) fits_files = fits_data(out_path_fits) size = len(fits_files) + print(size) ################### # test @@ -41,14 +44,14 @@ def create_gridded_data_set(config): truth_amp_phase_test = convert_amp_phase(truth_fft_test, sky_sim=True) assert gridded_data_test.shape[1] == 2 - out = out_path / Path("samp_test" + str(i) + ".h5") save_fft_pair(out, gridded_data_test, truth_amp_phase_test) # ################### - size_valid = conf["train_valid_split"] * size - size_train = size - size_valid + size_train = int(size // (1 + conf["train_valid_split"])) + size_valid = size - size_train + print(f"Training size: {size_train}, Validation size: {size_valid}") bundle_train = int(size_train // conf["bundle_size"]) bundle_valid = int(size_valid // conf["bundle_size"]) @@ -105,38 +108,33 @@ def create_gridded_data_set(config): def open_data(fits_files, sky_dist, conf, i): - uv_data = np.array( - [ - fits_files.get_uv_data(n).copy() - for n in np.arange( - i * conf["bundle_size"], - (i * conf["bundle_size"]) + conf["bundle_size"], - ) - ], - dtype="object", - ) + uv_data = [ + fits_files.get_uv_data(n).copy() + for n in np.arange( + i * conf["bundle_size"], (i * conf["bundle_size"]) + conf["bundle_size"] + ) + ] freq_data = np.array( [ fits_files.get_freq_data(n) for n in np.arange( - i * conf["bundle_size"], - (i * conf["bundle_size"]) + conf["bundle_size"], + i * conf["bundle_size"], (i * conf["bundle_size"]) + conf["bundle_size"] ) ], dtype="object", ) gridded_data = np.array( - [ - ducc0_gridding(data, freq).copy() - for data, freq in tqdm(zip(uv_data, freq_data)) - ] + [grid_data(data, freq, conf).copy() for data, freq in zip(uv_data, freq_data)] ) + bundle = np.floor_divide(i * conf["bundle_size"], len(open_bundles(sky_dist[0]))) gridded_truth = np.array( [ - sky_dist[int(n)][0][0] + open_bundles(sky_dist[bundle])[n] for n in np.arange( - i * conf["bundle_size"], - (i * conf["bundle_size"]) + conf["bundle_size"], + i * conf["bundle_size"] - bundle * len(open_bundles(sky_dist[0])), + (i * conf["bundle_size"]) + + conf["bundle_size"] + - bundle * len(open_bundles(sky_dist[0])), ) ] ) @@ -147,8 +145,7 @@ def calc_truth_fft(sky_dist): # norm = np.sum(np.sum(sky_dist_test, keepdims=True, axis=1), axis=2) # sky_dist_test = np.expand_dims(sky_dist_test, -1) / norm[:, None, None] truth_fft = np.fft.fftshift( - np.fft.fft2(np.fft.fftshift(sky_dist, axes=(1, 2)), axes=(1, 2)), - axes=(1, 2), + np.fft.fft2(np.fft.fftshift(sky_dist, axes=(1, 2)), axes=(1, 2)), axes=(1, 2) ) return truth_fft @@ -182,22 +179,22 @@ def ducc0_gridding(uv_data, freq_data): mask[wgt == 0] = False DEG2RAD = np.pi / 180 - nthreads = 4 + # nthreads = 4 epsilon = 1e-4 - do_wgridding = False - verbosity = 1 + # do_wgridding = False + # verbosity = 1 - do_sycl = False # True - do_cng = False # True + # do_sycl = False # True + # do_cng = False # True - ntries = 1 + # ntries = 1 - fov_deg = 0.02 # 1e-5 # 3.3477833333331884e-5 + fov_deg = 0.02 # 1e-5 # 3.3477833333331884e-5 npixdirty = 64 # get_npixdirty(uvw, freq, fov_deg, mask) pixsize = fov_deg / npixdirty * DEG2RAD - mintime = 1e300 + # mintime = 1e300 grid = ms2dirty_python_fast( uvw, freq, vis, npixdirty, npixdirty, pixsize, pixsize, epsilon, False @@ -209,18 +206,21 @@ def ducc0_gridding(uv_data, freq_data): def grid_data(uv_data, freq_data, conf): cmplx = uv_data["DATA"] - real = np.squeeze(cmplx[..., 0, 0]).ravel() - imag = np.squeeze(cmplx[..., 0, 1]).ravel() + real = np.squeeze(cmplx[..., 0, 0, 0]) # .ravel() + imag = np.squeeze(cmplx[..., 0, 0, 1]) # .ravel() # weight = np.squeeze(cmplx[..., 0, 2]) freq = freq_data[1] IF_bands = (freq_data[0]["IF FREQ"] + freq).reshape(-1, 1) - u = np.repeat([uv_data["UU--"]], np.squeeze(cmplx[..., 0, 0]).shape[1], axis=0) - v = np.repeat([uv_data["VV--"]], np.squeeze(cmplx[..., 0, 0]).shape[1], axis=0) + u = np.repeat([uv_data["UU--"]], real.shape[1], axis=0) + v = np.repeat([uv_data["VV--"]], real.shape[1], axis=0) u = (u * IF_bands).T.ravel() v = (v * IF_bands).T.ravel() + real = real.ravel() + imag = imag.ravel() + samps = np.array( [ np.append(u, -u), @@ -229,7 +229,6 @@ def grid_data(uv_data, freq_data, conf): np.append(imag, -imag), ] ) - # Generate Mask N = conf["grid_size"] # image size fov = ( @@ -242,9 +241,6 @@ def grid_data(uv_data, freq_data, conf): mask, *_ = np.histogram2d(samps[0], samps[1], bins=[bins, bins], normed=False) mask[mask == 0] = 1 - # flip or rot90? Stefan used flip -> write test!!! - # mask = np.flip(mask, [1]) - mask = np.rot90(mask, 1) mask_real, x_edges, y_edges = np.histogram2d( samps[0], samps[1], bins=[bins, bins], weights=samps[2], normed=False @@ -253,12 +249,12 @@ def grid_data(uv_data, freq_data, conf): samps[0], samps[1], bins=[bins, bins], weights=samps[3], normed=False ) - mask_real = np.rot90(mask_real, 1) - mask_imag = np.rot90(mask_imag, 1) - mask_real /= mask mask_imag /= mask + mask_real = np.rot90(mask_real, 1) + mask_imag = np.rot90(mask_imag, 1) + gridded_vis = np.zeros((2, N, N)) gridded_vis[0] = mask_real gridded_vis[1] = mask_imag @@ -271,17 +267,18 @@ def convert_amp_phase(data, sky_sim=False, rescale=False): phase = np.angle(data) # get rescale clear # amp = (np.log10(amp + 1e-10) / 10) + 1 - if rescale: - amp = np.array([cv2.resize(a, (128, 128)) for a in amp]) - phase = np.array([cv2.resize(p, (128, 128)) for p in phase]) + # if rescale: + # amp = np.array([cv2.resize(a, (128, 128)) for a in amp]) + # phase = np.array([cv2.resize(p, (128, 128)) for p in phase]) data = np.stack((amp, phase), axis=1) else: - amp = np.abs(data) - phase = np.angle(data) + test = data[:, 0] + 1j * data[:, 1] + amp = np.abs(test) + phase = np.angle(test) # amp = (np.log10(amp + 1e-10) / 10) + 1 - if rescale: - amp = np.array([cv2.resize(a, (128, 128)) for a in amp]) - phase = np.array([cv2.resize(p, (128, 128)) for p in phase]) + # if rescale: + # amp = np.array([cv2.resize(a, (128, 128)) for a in amp]) + # phase = np.array([cv2.resize(p, (128, 128)) for p in phase]) data = np.stack((amp, phase), axis=1) return data diff --git a/pyvisgen/layouts/layouts.py b/pyvisgen/layouts/layouts.py index 915a723..58c2264 100644 --- a/pyvisgen/layouts/layouts.py +++ b/pyvisgen/layouts/layouts.py @@ -2,6 +2,7 @@ import pandas as pd from dataclasses import dataclass import numpy as np +from astropy.coordinates import EarthLocation file_dir = Path(__file__).parent.resolve() @@ -73,6 +74,12 @@ def get_array_layout(array_name, writer=False): """ f = array_name + ".txt" array = pd.read_csv(file_dir / f, sep=" ") + if array_name == "vla": + loc = EarthLocation.of_site("VLA") + array["X"] += loc.value[0] + array["Y"] += loc.value[1] + array["Z"] += loc.value[2] + stations = Stations( np.arange(len(array)), array["station_name"].values, diff --git a/pyvisgen/layouts/vla.txt b/pyvisgen/layouts/vla.txt index fae9dd9..580255b 100644 --- a/pyvisgen/layouts/vla.txt +++ b/pyvisgen/layouts/vla.txt @@ -1,9 +1,8 @@ station_name X Y Z dish_dia el_low el_high SEFD altitude W32 1640.02760601 -4329.92665085 -2416.70580433 25 15 85 110 5030 N24 -1660.49128127 -259.39960505 2454.41532549 25 15 85 110 5030 -OUT 0. 0. 0. 25 15 85 110 5030 W20 733.34798474 -1932.98013829 -1078.10923116 25 15 85 110 5030 -E24 765.21097819 2889.44852033 -1108.8777769 25 15 85 110 5030 +E24 765.21097819 2889.44852033 -1108.8777769 25 15 85 110 5030 N32 -2629.09218269 -410.65158466 3885.60273242 25 15 85 110 5030 E36 1534.56120139 5793.91275687 -2223.48335235 25 15 85 110 5030 N16 -801.39819671 -124.9731867 1182.11742906 25 15 85 110 5030 diff --git a/pyvisgen/simulation/data_set.py b/pyvisgen/simulation/data_set.py index bf5adde..7ee6290 100644 --- a/pyvisgen/simulation/data_set.py +++ b/pyvisgen/simulation/data_set.py @@ -5,22 +5,28 @@ from pathlib import Path from datetime import datetime from pyvisgen.utils.config import read_data_set_conf +from pyvisgen.utils.data import load_bundles, open_bundles from pyvisgen.simulation.visibility import vis_loop import pyvisgen.fits.writer as writer -from radiosim.data import radiosim_data +import pyvisgen.layouts.layouts as layouts +from astropy import units as un +from astropy.coordinates import SkyCoord +from pyvisgen.simulation.utils import calc_ref_elev, calc_time_steps def simulate_data_set(config, slurm=False, job_id=None, n=None): - np.random.seed(1) conf = read_data_set_conf(config) - out_path = Path(conf["out_path"]) + out_path = Path(conf["out_path_fits"]) out_path.mkdir(parents=True, exist_ok=True) if slurm: - job_id = int(job_id + n * 1000) - data = radiosim_data(conf["in_path"]) + job_id = int(job_id + n * 500) + data = load_bundles(conf["in_path"]) out = out_path / Path("vis_" + str(job_id) + ".fits") - SI = torch.tensor(data[job_id][0][0], dtype=torch.cdouble) + imgs_bundle = len(open_bundles(data[0])) + bundle = torch.div(job_id, imgs_bundle, rounding_mode="floor") + image = job_id - bundle * imgs_bundle + SI = torch.tensor(open_bundles(data[bundle])[image], dtype=torch.cdouble) samp_ops = create_sampling_rc(conf) vis_data = vis_loop(samp_ops, SI) @@ -31,70 +37,49 @@ def simulate_data_set(config, slurm=False, job_id=None, n=None): hdu_list.writeto(out, overwrite=True) else: - data = radiosim_data(conf["in_path"]) - for i in tqdm(range(len(data))): - out = out_path / Path("vis_" + str(i) + ".fits") - SI = torch.tensor(data[i][0][0], dtype=torch.cdouble) - - samp_ops = create_sampling_rc(conf) - vis_data = vis_loop(samp_ops, SI) - while vis_data == 0: + data = load_bundles(conf["in_path"]) + for i in range(len(data)): + SIs = open_bundles(data[i]) + for j, SI in enumerate(tqdm(SIs)): + out = out_path / Path("vis_" + str(j) + ".fits") samp_ops = create_sampling_rc(conf) vis_data = vis_loop(samp_ops, SI) - hdu_list = writer.create_hdu_list(vis_data, samp_ops) - hdu_list.writeto(out, overwrite=True) + while vis_data == 0: + samp_ops = create_sampling_rc(conf) + vis_data = vis_loop(samp_ops, SI) + hdu_list = writer.create_hdu_list(vis_data, samp_ops) + hdu_list.writeto(out, overwrite=True) def create_sampling_rc(conf): - sampling_opts = draw_sampling_opts(conf) - samp_ops = { - "mode": sampling_opts[0], - "layout": sampling_opts[1], - "img_size": sampling_opts[2], - "fov_center_ra": sampling_opts[3], - "fov_center_dec": sampling_opts[4], - "fov_size": sampling_opts[5], - "corr_int_time": sampling_opts[6], - "scan_start": sampling_opts[7], - "scan_duration": sampling_opts[8], - "scans": sampling_opts[9], - "interval_length": sampling_opts[10], - "base_freq": sampling_opts[11], - "frequsel": sampling_opts[12], - "bandwidths": sampling_opts[13], - } + samp_ops = draw_sampling_opts(conf) + + while test_opts(samp_ops) <= 5: + samp_ops = draw_sampling_opts(conf) + return samp_ops def draw_sampling_opts(conf): - date_str_ra = pd.date_range( - conf["fov_center_ra"][0][0].strftime("%H:%M:%S"), - conf["fov_center_ra"][0][1].strftime("%H:%M:%S"), - freq="1min", - ).strftime("%H:%M:%S") - times_ra = [ - datetime.time(datetime.strptime(date, "%H:%M:%S")) for date in date_str_ra - ] - fov_center_ra = np.random.choice(times_ra) + angles_ra = np.arange( + conf["fov_center_ra"][0][0], conf["fov_center_ra"][0][1], step=0.1 + ) + fov_center_ra = np.random.choice(angles_ra) + angles_dec = np.arange( - conf["fov_center_dec"][0][0], - conf["fov_center_dec"][0][1], - step=0.1, + conf["fov_center_dec"][0][0], conf["fov_center_dec"][0][1], step=0.1 ) fov_center_dec = np.random.choice(angles_dec) start_time_l = datetime.strptime(conf["scan_start"][0], "%d-%m-%Y %H:%M:%S") start_time_h = datetime.strptime(conf["scan_start"][1], "%d-%m-%Y %H:%M:%S") - start_times = pd.date_range( - start_time_l, - start_time_h, - freq="1h", - ).strftime("%d-%m-%Y %H:%M:%S") + start_times = pd.date_range(start_time_l, start_time_h, freq="1h").strftime( + "%d-%m-%Y %H:%M:%S" + ) scan_start = np.random.choice( [datetime.strptime(time, "%d-%m-%Y %H:%M:%S") for time in start_times] ) - scan_duration = ( - np.random.randint(conf["scan_duration"][0], conf["scan_duration"][1]) - * conf["corr_int_time"] + scan_duration = np.random.randint( + conf["scan_duration"][0], conf["scan_duration"][1] ) scans = np.random.randint(conf["scans"][0], conf["scans"][1]) opts = np.array( @@ -113,11 +98,44 @@ def draw_sampling_opts(conf): conf["base_freq"], conf["frequsel"], conf["bandwidths"], + conf["corrupted"], ], dtype="object", ) - return opts + samp_ops = { + "mode": opts[0], + "layout": opts[1], + "img_size": opts[2], + "fov_center_ra": opts[3], + "fov_center_dec": opts[4], + "fov_size": opts[5], + "corr_int_time": opts[6], + "scan_start": opts[7], + "scan_duration": opts[8], + "scans": opts[9], + "interval_length": opts[10], + "base_freq": opts[11], + "frequsel": opts[12], + "bandwidths": opts[13], + "corrupted": opts[14], + } + return samp_ops + + +def test_opts(rc): + array_layout = layouts.get_array_layout(rc["layout"]) + src_crd = SkyCoord( + ra=rc["fov_center_ra"], dec=rc["fov_center_dec"], unit=(un.deg, un.deg) + ) + time = calc_time_steps(rc) + _, el_st_0 = calc_ref_elev(src_crd, time[0], array_layout) + _, el_st_1 = calc_ref_elev(src_crd, time[1], array_layout) + el_min = 15 + el_max = 85 + active_telescopes_0 = np.sum((el_st_0 >= el_min) & (el_st_0 <= el_max)) + active_telescopes_1 = np.sum((el_st_1 >= el_min) & (el_st_1 <= el_max)) + return min(active_telescopes_0, active_telescopes_1) if __name__ == "__main__": - simulate_data_set("/net/big-tank/POOL/projects/radio/test_rime/create_dataset.toml") + simulate_data_set() diff --git a/pyvisgen/simulation/scan.py b/pyvisgen/simulation/scan.py index 643b746..9b319e8 100644 --- a/pyvisgen/simulation/scan.py +++ b/pyvisgen/simulation/scan.py @@ -1,10 +1,10 @@ from dataclasses import dataclass from astropy import units as un -from astropy.coordinates import EarthLocation, AltAz, Angle +from astropy.coordinates import EarthLocation import numpy as np from scipy.special import j1 from astroplan import Observer -from pyvisgen.simulation.utils import single_occurance, get_pairs +from pyvisgen.simulation.utils import calc_ref_elev, Array, calc_direction_cosines import torch import itertools import numexpr as ne @@ -72,108 +72,42 @@ def get_baselines(src_crd, time, array_layout): Returns ------- dataclass object - baselines between telescopes with visinility flags + baselines between telescopes with visibility flags """ # Calculate for all times # calculate GHA, Greenwich as reference for EHT - ha_all = Angle( - [t.sidereal_time("apparent", "greenwich") - src_crd.ra for t in time] - ) + ha_all, el_st_all = calc_ref_elev(src_crd, time, array_layout) - # calculate elevations - el_st_all = src_crd.transform_to( - AltAz( - obstime=time.reshape(len(time), -1), - location=EarthLocation.from_geocentric( - np.repeat([array_layout.x], len(time), axis=0), - np.repeat([array_layout.y], len(time), axis=0), - np.repeat([array_layout.z], len(time), axis=0), - unit=un.m, - ), - ) - ).alt.degree - - # fails for 1 timestep - assert len(ha_all.value) == len(el_st_all) - - # always the same - delta_x, delta_y, delta_z = get_pairs(array_layout) - indices = single_occurance(delta_x) - delta_x = delta_x[indices] - delta_y = delta_y[indices] - delta_z = delta_z[indices] - mask = [i * len(array_layout.x) + i for i in range(len(array_layout.x))] - pairs = np.delete( - np.array(np.meshgrid(array_layout.name, array_layout.name)).T.reshape(-1, 2), - mask, - axis=0, - )[indices] - - st_nums = np.delete( - np.array(np.meshgrid(array_layout.st_num, array_layout.st_num)).T.reshape( - -1, 2 - ), - mask, - axis=0, - )[indices] - - els_low = np.delete( - np.array(np.meshgrid(array_layout.el_low, array_layout.el_low)).T.reshape( - -1, 2 - ), - mask, - axis=0, - )[indices] - - els_high = np.delete( - np.array(np.meshgrid(array_layout.el_high, array_layout.el_high)).T.reshape( - -1, 2 - ), - mask, - axis=0, - )[indices] + ar = Array(array_layout) + delta_x, delta_y, delta_z, indices = ar.calc_relative_pos + mask = ar.get_baseline_mask + antenna_pairs, st_num_pairs, els_low_pairs, els_high_pairs = ar.calc_ant_pair_vals + names = antenna_pairs[:, 0] + "-" + antenna_pairs[:, 1] # Loop over ha and el_st baselines = Baselines([], [], [], [], [], [], []) for ha, el_st in zip(ha_all, el_st_all): - u = np.sin(ha) * delta_x + np.cos(ha) * delta_y - v = ( - -np.sin(src_crd.ra) * np.cos(ha) * delta_x - + np.sin(src_crd.ra) * np.sin(ha) * delta_y - + np.cos(src_crd.ra) * delta_z - ) - w = ( - np.cos(src_crd.ra) * np.cos(ha) * delta_x - - np.cos(src_crd.ra) * np.sin(ha) * delta_y - + np.sin(src_crd.ra) * delta_z - ) - assert u.shape == v.shape == w.shape + u, v, w = calc_direction_cosines(ha, el_st, delta_x, delta_y, delta_z, src_crd) + # calc current elevations els_st = np.delete( np.array(np.meshgrid(el_st, el_st)).T.reshape(-1, 2), mask, axis=0, )[indices] + # calc valid baselines valid = np.ones(u.shape).astype(bool) - - m1 = (els_st < els_low).any(axis=1) - m2 = (els_st > els_high).any(axis=1) + m1 = (els_st < els_low_pairs).any(axis=1) + m2 = (els_st > els_high_pairs).any(axis=1) valid_mask = np.ma.mask_or(m1, m2) valid[valid_mask] = False - names = pairs[:, 0] + "-" + pairs[:, 1] - - u = u.reshape(-1) - v = v.reshape(-1) - w = w.reshape(-1) - valid = valid.reshape(-1) - # collect baselines base = Baselines( names, - array_layout[st_nums[:, 0]], - array_layout[st_nums[:, 1]], + array_layout[st_num_pairs[:, 0]], + array_layout[st_num_pairs[:, 1]], u, v, w, @@ -183,7 +117,7 @@ def get_baselines(src_crd, time, array_layout): return baselines -def rd_grid(fov, samples, src_crd): +def create_rd_grid(fov, samples, src_crd): """Calculates RA and Dec values for a given fov around a source position Parameters @@ -200,6 +134,10 @@ def rd_grid(fov, samples, src_crd): 3d array Returns a 3d array with every pixel containing a RA and Dec value """ + # transform to rad + fov *= np.pi / (3600 * 180) + + # define resolution res = fov / samples rd_grid = np.zeros((samples, samples, 2)) @@ -210,11 +148,10 @@ def rd_grid(fov, samples, src_crd): rd_grid[:, i, 1] = np.array( [-(i - samples / 2) * res + src_crd.dec.rad for i in range(samples)] ) - return rd_grid -def lm_grid(rd_grid, src_crd): +def create_lm_grid(rd_grid, src_crd): """Calculates sine projection for fov Parameters @@ -335,17 +272,17 @@ def corrupted(lm, baselines, wave, time, src_crd, array_layout, SI, rd): B = np.zeros((lm.shape[0], lm.shape[1], 1), dtype=complex) B[:, :, 0] = SI + SI - # B[:, :, 0, 0] = I[:, :, 0] + I[:, :, 1] - # B[:, :, 0, 1] = I[:, :, 2] + 1j * I[:, :, 3] - # B[:, :, 1, 0] = I[:, :, 2] - 1j * I[:, :, 3] - # B[:, :, 1, 1] = I[:, :, 0] - I[:, :, 1] + # # only calculate without polarization for the moment + # B[:, :, 0, 0] = SI[:, :, 0] + SI[:, :, 1] + # B[:, :, 0, 1] = SI[:, :, 2] + 1j * SI[:, :, 3] + # B[:, :, 1, 0] = SI[:, :, 2] - 1j * SI[:, :, 3] + # B[:, :, 1, 1] = SI[:, :, 0] - SI[:, :, 1] - # coherency X = torch.einsum("lmi,lmb->lmbi", torch.tensor(B), K) # X = np.einsum('lmij,lmb->lmbij', B, K, optimize=True) # X = torch.tensor(B)[:,:,None,:,:] * K[:,:,:,None,None] - del K + del K, B # telescope response E_st = getE(rd, array_layout, wave, src_crd) @@ -356,7 +293,7 @@ def corrupted(lm, baselines, wave, time, src_crd, array_layout, SI, rd): EX = torch.einsum("lmb,lmbi->lmbi", E1, X) - del E1, X + del E1, X, E_st # EXE = torch.einsum('lmbij,lmbjk->lmbik',EX,torch.transpose(torch.conj(E2),3,4)) EXE = torch.einsum("lmbi,lmb->lmbi", EX, E2) del EX, E2 @@ -380,12 +317,10 @@ def corrupted(lm, baselines, wave, time, src_crd, array_layout, SI, rd): P1 = torch.tensor(getP(b1), dtype=torch.cdouble) P2 = torch.tensor(getP(b2), dtype=torch.cdouble) - print("P", P1.shape) - - PEXE = torch.einsum("bi,lmbj->lmbi", P1, EXE) - del EXE - PEXEP = torch.einsum("lmbi,bk->lmbik", PEXE, torch.transpose(torch.conj(P2), 1, 2)) - del PEXE + PEXE = torch.einsum("bi,lmbi->lmbi", P1, EXE) + del EXE, P1, beta, tsob + PEXEP = torch.einsum("lmbi,bi->lmbi", PEXE, torch.conj(P2)) + del PEXE, P2 return PEXEP @@ -511,7 +446,6 @@ def getE(rd, array_layout, wave, src_crd): # calculate matrix E for every point in grid # E = np.zeros((rd.shape[0], rd.shape[1], array_layout.st_num.shape[0], 2, 2)) E = np.zeros((rd.shape[0], rd.shape[1], array_layout.st_num.shape[0])) - # print("E", E.shape) # get diameters of all stations and do vectorizing stuff diameters = array_layout.diam @@ -546,7 +480,7 @@ def angularDistance(rd, src_crd): r = rd[:, :, 0] - src_crd.ra.rad d = rd[:, :, 1] - src_crd.dec.rad - theta = np.arcsin(np.sqrt(r ** 2 + d ** 2)) + theta = np.arcsin(np.sqrt(r**2 + d**2)) return theta @@ -615,7 +549,7 @@ def getK(baselines, lm, wave, base_num): l = torch.tensor(lm[:, :, 0]) m = torch.tensor(lm[:, :, 1]) - n = torch.sqrt(1 - l ** 2 - m ** 2) + n = torch.sqrt(1 - l**2 - m**2) ul = torch.einsum("b,ij->ijb", torch.tensor(u_cmplt), l) vm = torch.einsum("b,ij->ijb", torch.tensor(v_cmplt), m) @@ -640,10 +574,6 @@ def jinc(x): array value of jinc function at x """ - # if x == 0: - # return 1 - # jinc = 2 * j1(x) / x - # jinc[x == 0] = 1 jinc = np.ones(x.shape) jinc[x != 0] = 2 * j1(x[x != 0]) / x[x != 0] return jinc diff --git a/pyvisgen/simulation/scripts/create_dataset.py b/pyvisgen/simulation/scripts/create_dataset.py index 622b7fa..606b028 100644 --- a/pyvisgen/simulation/scripts/create_dataset.py +++ b/pyvisgen/simulation/scripts/create_dataset.py @@ -5,8 +5,8 @@ @click.command() @click.argument("configuration_path", type=click.Path(exists=True, dir_okay=False)) -@click.option('--job_id', required=False, type=int, default=None) -@click.option('--n', required=False, type=int, default=None) +@click.option("--job_id", required=False, type=int, default=None) +@click.option("--n", required=False, type=int, default=None) @click.option( "--mode", type=click.Choice( diff --git a/pyvisgen/simulation/utils.py b/pyvisgen/simulation/utils.py index cf591e9..dcb5264 100644 --- a/pyvisgen/simulation/utils.py +++ b/pyvisgen/simulation/utils.py @@ -4,6 +4,9 @@ import numpy as np from astropy.time import Time from datetime import datetime +import astropy.constants as const +from astropy.coordinates import EarthLocation, AltAz, Angle +from astropy.utils.decorators import lazyproperty def read_config(conf): @@ -40,6 +43,7 @@ def read_config(conf): def single_occurance(array): + # only calc one half of visibility because of Fourier symmetry vals, index = np.unique(np.abs(array), return_index=True) return index @@ -72,13 +76,12 @@ def get_pairs(array_layout): def calc_time_steps(conf): start_time = Time(conf["scan_start"].isoformat(), format="isot") interval = conf["interval_length"] - integration_time = conf["corr_int_time"] num_scans = conf["scans"] scan_duration = conf["scan_duration"] int_time = conf["corr_int_time"] time_lst = [ - start_time + interval * i * un.second + j * integration_time * un.second + start_time + interval * i * un.second + j * int_time * un.second for i in range(num_scans) for j in range(int(scan_duration / int_time) + 1) ] @@ -87,3 +90,133 @@ def calc_time_steps(conf): time = Time(time_lst) return time + + +def get_IFs(rc): + IFs = np.array( + [ + const.c / ((rc["base_freq"] + float(freq)) / un.second) / un.meter + for freq in rc["frequsel"] + ] + ) + return IFs + + +def calc_ref_elev(src_crd, time, array_layout): + if time.shape == (): + time = time[None] + # Calculate for all times + # calculate GHA, Greenwich as reference for EHT + ha_all = Angle( + [t.sidereal_time("apparent", "greenwich") - src_crd.ra for t in time] + ) + + # calculate elevations + el_st_all = src_crd.transform_to( + AltAz( + obstime=time.reshape(len(time), -1), + location=EarthLocation.from_geocentric( + np.repeat([array_layout.x], len(time), axis=0), + np.repeat([array_layout.y], len(time), axis=0), + np.repeat([array_layout.z], len(time), axis=0), + unit=un.m, + ), + ) + ).alt.degree + assert len(ha_all.value) == len(el_st_all) + return ha_all, el_st_all + + +class Array: + def __init__(self, array_layout): + self.array_layout = array_layout + + @lazyproperty + def calc_relative_pos(self): + # from geocentric coordinates to relative coordinates inside array + delta_x, delta_y, delta_z = get_pairs(self.array_layout) + self.indices = single_occurance(delta_x) + delta_x = delta_x[self.indices] + delta_y = delta_y[self.indices] + delta_z = delta_z[self.indices] + return delta_x, delta_y, delta_z, self.indices + + @lazyproperty + def get_baseline_mask(self): + # mask baselines between the same telescope + self.mask = [ + i * len(self.array_layout.x) + i for i in range(len(self.array_layout.x)) + ] + return self.mask + + @lazyproperty + def calc_ant_pair_vals(self): + antenna_pairs = np.delete( + np.array( + np.meshgrid(self.array_layout.name, self.array_layout.name) + ).T.reshape(-1, 2), + self.mask, + axis=0, + )[self.indices] + + st_num_pairs = np.delete( + np.array( + np.meshgrid(self.array_layout.st_num, self.array_layout.st_num) + ).T.reshape(-1, 2), + self.mask, + axis=0, + )[self.indices] + + els_low_pairs = np.delete( + np.array( + np.meshgrid(self.array_layout.el_low, self.array_layout.el_low) + ).T.reshape(-1, 2), + self.mask, + axis=0, + )[self.indices] + + els_high_pairs = np.delete( + np.array( + np.meshgrid(self.array_layout.el_high, self.array_layout.el_high) + ).T.reshape(-1, 2), + self.mask, + axis=0, + )[self.indices] + return antenna_pairs, st_num_pairs, els_low_pairs, els_high_pairs + + +def calc_direction_cosines(ha, el_st, delta_x, delta_y, delta_z, src_crd): + u = (np.sin(ha) * delta_x + np.cos(ha) * delta_y).reshape(-1) + v = ( + -np.sin(src_crd.ra) * np.cos(ha) * delta_x + + np.sin(src_crd.ra) * np.sin(ha) * delta_y + + np.cos(src_crd.ra) * delta_z + ).reshape(-1) + w = ( + np.cos(src_crd.ra) * np.cos(ha) * delta_x + - np.cos(src_crd.ra) * np.sin(ha) * delta_y + + np.sin(src_crd.ra) * delta_z + ).reshape(-1) + assert u.shape == v.shape == w.shape + return u, v, w + + +def calc_valid_baselines(baselines, base_num, t, rc): + valid = baselines.valid.reshape(-1, base_num) + mask = np.array(valid[:-1]).astype(bool) & np.array(valid[1:]).astype(bool) + u = baselines.u.reshape(-1, base_num) + v = baselines.v.reshape(-1, base_num) + w = baselines.w.reshape(-1, base_num) + base_valid = np.arange(len(baselines.u)).reshape(-1, base_num)[:-1][mask] + u_valid = (u[:-1][mask] + u[1:][mask]) / 2 + v_valid = (v[:-1][mask] + v[1:][mask]) / 2 + w_valid = (w[:-1][mask] + w[1:][mask]) / 2 + date = np.repeat( + (t[:-1] + rc["corr_int_time"] * un.second / 2).jd.reshape(-1, 1), + base_num, + axis=1, + )[mask] + + _date = np.zeros(len(u_valid)) + assert u_valid.shape == v_valid.shape == w_valid.shape + return base_valid, u_valid, v_valid, w_valid, date, _date diff --git a/pyvisgen/simulation/visibility.py b/pyvisgen/simulation/visibility.py index e624d82..98c40a8 100644 --- a/pyvisgen/simulation/visibility.py +++ b/pyvisgen/simulation/visibility.py @@ -1,8 +1,7 @@ import numpy as np from dataclasses import dataclass -import pyvisgen.simulation.utils as ut +from pyvisgen.simulation.utils import get_IFs, calc_valid_baselines, calc_time_steps import pyvisgen.layouts.layouts as layouts -import astropy.constants as const from astropy import units as un import pyvisgen.simulation.scan as scan import torch @@ -15,12 +14,12 @@ class Visibilities: SQ: [complex] SU: [complex] SV: [complex] - num: [int] - scan: [int] - base_num: [int] - u: [float] - v: [float] - w: [float] + num: [float] + scan: [float] + base_num: [float] + u: [un] + v: [un] + w: [un] date: [float] _date: [float] @@ -65,53 +64,45 @@ class Vis: SQ: complex SU: complex SV: complex - num: int - scan: int - base_num: int - u: float - v: float - w: float + num: float + scan: float + base_num: float + u: un + v: un + w: un date: float _date: float -def vis_loop(rc, SI, num_threads=48): - # torch.set_num_threads(num_threads) +def vis_loop(rc, SI, num_threads=10): + torch.set_num_threads(num_threads) - # read config + # define array, source coords, and IFs array_layout = layouts.get_array_layout(rc["layout"]) src_crd = SkyCoord( - ra=rc["fov_center_ra"].strftime("%H:%M:%S"), - dec=rc["fov_center_dec"], - unit=(un.hourangle, un.deg), - ) - rc["fov_center_ra"] = src_crd.ra.value - rc["fov_center_dec"] = src_crd.dec.value - - waves = np.array( - [ - const.c / ((rc["base_freq"] + float(freq)) / un.second) / un.meter - for freq in rc["frequsel"] - ] + ra=rc["fov_center_ra"], dec=rc["fov_center_dec"], unit=(un.deg, un.deg) ) - # calculate rd, lm - rd = scan.rd_grid(rc["fov_size"] * np.pi / (3600 * 180), rc["img_size"], src_crd) - lm = scan.lm_grid(rd, src_crd) + # define IFs + IFs = get_IFs(rc) # calculate time steps - time = ut.calc_time_steps(rc) + time = calc_time_steps(rc) + + # calculate rd, lm + rd = scan.create_rd_grid(rc["fov_size"], rc["img_size"], src_crd) + lm = scan.create_lm_grid(rd, src_crd) - # number statiosn, number baselines + # def number stations and number baselines stat_num = array_layout.st_num.shape[0] base_num = int(stat_num * (stat_num - 1) / 2) # calculate vis visibilities = Visibilities( - np.empty(shape=[0] + [len(waves)]), - np.empty(shape=[0] + [len(waves)]), - np.empty(shape=[0] + [len(waves)]), - np.empty(shape=[0] + [len(waves)]), + np.empty(shape=[0] + [len(IFs)]), + np.empty(shape=[0] + [len(IFs)]), + np.empty(shape=[0] + [len(IFs)]), + np.empty(shape=[0] + [len(IFs)]), [], [], [], @@ -128,39 +119,28 @@ def vis_loop(rc, SI, num_threads=48): baselines = scan.get_baselines(src_crd, t, array_layout) - valid = baselines.valid.reshape(-1, base_num) - mask = np.array(valid[:-1]).astype(bool) & np.array(valid[1:]).astype(bool) - u = baselines.u.reshape(-1, base_num) - v = baselines.v.reshape(-1, base_num) - w = baselines.w.reshape(-1, base_num) - base_valid = np.arange(len(baselines.u)).reshape(-1, base_num)[:-1][mask] - u_valid = u[:-1][mask] - v_valid = v[:-1][mask] - w_valid = w[:-1][mask] - date = np.repeat( - (t[:-1] + rc["corr_int_time"] * un.second / 2).jd.reshape(-1, 1), - base_num, - axis=1, - )[mask] - - _date = np.zeros(len(u_valid)) - - int_values = np.array( - [ - calc_vis( - lm, - baselines, - wave, - t, - src_crd, - array_layout, - SI, - rd, - vis_num, - ) - for wave in waves - ] + base_valid, u_valid, v_valid, w_valid, date, _date = calc_valid_baselines( + baselines, base_num, t, rc ) + + int_values = [] + for IF in IFs: + val_i = calc_vis( + lm, + baselines, + IF, + t, + src_crd, + array_layout, + SI, + rd, + vis_num, + corrupted=rc["corrupted"], + ) + int_values.append(val_i) + del val_i + + int_values = np.array(int_values) if int_values.dtype != np.complex128: continue int_values = np.swapaxes(int_values, 0, 1) @@ -182,24 +162,29 @@ def vis_loop(rc, SI, num_threads=48): ) visibilities.add(vis) - # workaround to guarantee min number of visibilities - # when num vis is below N sampling is redone - # if visibilities.get_values().shape[1] < 3500: - # return 0 + # workaround to guarantee min number of visibilities + # when num vis is below N sampling is redone + # if visibilities.get_values().shape[1] < 3500: + # return 0 + del int_values return visibilities -def calc_vis(lm, baselines, wave, t, src_crd, array_layout, SI, rd, vis_num): - X1 = scan.uncorrupted( - lm, baselines, wave, t, src_crd, array_layout, SI#, rd - ) - if X1.shape[0] == 1: - return -1 - X2 = scan.uncorrupted( - lm, baselines, wave, t, src_crd, array_layout, SI#, rd - ) +def calc_vis( + lm, baselines, wave, t, src_crd, array_layout, SI, rd, vis_num, corrupted=True +): + if corrupted: + X1 = scan.corrupted(lm, baselines, wave, t, src_crd, array_layout, SI, rd) + if X1.shape[0] == 1: + return -1 + X2 = scan.corrupted(lm, baselines, wave, t, src_crd, array_layout, SI, rd) + else: + X1 = scan.uncorrupted(lm, baselines, wave, t, src_crd, array_layout, SI) + if X1.shape[0] == 1: + return -1 + X2 = scan.uncorrupted(lm, baselines, wave, t, src_crd, array_layout, SI) int_values = scan.integrate(X1, X2).numpy() - del X1, X2 + del X1, X2, SI int_values = int_values return int_values diff --git a/pyvisgen/utils/config.py b/pyvisgen/utils/config.py index 3eea4ad..fa2883a 100644 --- a/pyvisgen/utils/config.py +++ b/pyvisgen/utils/config.py @@ -33,18 +33,16 @@ def read_data_set_conf(conf_toml): conf["base_freq"] = config["sampling_options"]["base_freq"] conf["frequsel"] = config["sampling_options"]["frequsel"] conf["bandwidths"] = config["sampling_options"]["bandwidths"] + conf["corrupted"] = config["sampling_options"]["corrupted"] conf["num_test_images"] = config["bundle_options"]["num_test_images"] conf["bundle_size"] = config["bundle_options"]["bundle_size"] conf["train_valid_split"] = config["bundle_options"]["train_valid_split"] conf["grid_size"] = config["bundle_options"]["grid_size"] conf["amp_phase"] = config["bundle_options"]["amp_phase"] - conf["target"] = config["bundle_options"]["target"] conf["in_path"] = config["bundle_options"]["in_path"] - conf["out_path"] = config["bundle_options"]["out_path"] - - conf["num_jobs"] = config["cluster_options"]["num_jobs"] - + conf["out_path_fits"] = config["bundle_options"]["out_path_fits"] + conf["out_path_gridded"] = config["bundle_options"]["out_path_gridded"] return conf diff --git a/pyvisgen/utils/data.py b/pyvisgen/utils/data.py new file mode 100644 index 0000000..3219161 --- /dev/null +++ b/pyvisgen/utils/data.py @@ -0,0 +1,24 @@ +import h5py +import numpy as np +import re +from pathlib import Path +from natsort import natsorted + + +def load_bundles(data_path): + bundle_paths = get_bundles(data_path) + bundles = natsorted([path for path in bundle_paths if re.findall("fft_", path.name)]) + print(bundles) + return bundles + + +def get_bundles(path): + data_path = Path(path) + bundles = np.array([x for x in data_path.iterdir()]) + return bundles + + +def open_bundles(path): + f = h5py.File(path, "r") + bundle_y = np.array(f["y"]) + return bundle_y diff --git a/setup.py b/setup.py index 19791a9..aa180f2 100644 --- a/setup.py +++ b/setup.py @@ -2,8 +2,8 @@ setup( name="pyvisgen", - version="0.0.4", - description="Simulate radio interferometer observations and visibility generation.", + version="0.1.0", + description="Simulate radio interferometer observations and visibility generation with the RIME formalism.", url="https://github.com/radionets-project/pyvisgen", author="Kevin Schmidt, Felix Geyer, Stefan Fröse", author_email="kevin3.schmidt@tu-dortmund.de", @@ -23,6 +23,11 @@ "astroplan", "torch", "tqdm", + "numexpr", + "opencv-python", + "click", + "h5py", + "natsort", ], setup_requires=["pytest-runner"], tests_require=["pytest"], @@ -33,7 +38,7 @@ ], }, classifiers=[ - "Development Status :: 3 - Alpha", + "Development Status :: 4 - Beta", "Intended Audience :: Science/Research", "License :: OSI Approved :: MIT License", "Natural Language :: English", @@ -45,7 +50,6 @@ "Programming Language :: Python :: 3 :: Only", "Topic :: Scientific/Engineering :: Astronomy", "Topic :: Scientific/Engineering :: Physics", - "Topic :: Scientific/Engineering :: Artificial Intelligence", "Topic :: Scientific/Engineering :: Information Analysis", ], ) diff --git a/tests/conftest.py b/tests/conftest.py new file mode 100644 index 0000000..15130eb --- /dev/null +++ b/tests/conftest.py @@ -0,0 +1,12 @@ +import pytest +import shutil + + +@pytest.fixture(autouse=True, scope='session') +def test_suite_cleanup_thing(): + yield + + build = "./tests/build/" + print("Cleaning up tests.") + + shutil.rmtree(build) diff --git a/tests/data/fft_train0.h5 b/tests/data/fft_train0.h5 new file mode 100644 index 0000000..ba35a8c Binary files /dev/null and b/tests/data/fft_train0.h5 differ diff --git a/tests/test_conf.toml b/tests/test_conf.toml new file mode 100644 index 0000000..5b7e6c9 --- /dev/null +++ b/tests/test_conf.toml @@ -0,0 +1,32 @@ +[sampling_options] +mode = "basic" +layout = "vlba" +img_size = 128 +fov_center_ra = [90, 140] +fov_center_dec = [-20, 50] +fov_size = 0.0064 # max res 0.1 +corr_int_time = 10.0 +scan_start = ["01-01-2020 00:00:01", "31-12-2021 23:59:59"] +scan_duration = [0, 50] +scans = [1, 10] +interval_length = 1200 +base_freq = 15.21e9 +frequsel = [0e8, 0.8e8, 1.44e8, 2.08e8] +bandwidths = [6.4e7, 6.4e7, 6.4e7, 6.4e7] +corrupted = true + +[bundle_options] +num_bundles = 5 +size_bundles = 10 +in_path = "./tests/data" +out_path_fits = "./tests/build/fits" +out_path_gridded = "./tests/build/gridded" +num_test_images = 1000 +bundle_size = 10 +train_valid_split = 0.2 +grid_size = 5 +amp_phase = true +target = 5 + +[cluster_options] +num_jobs = 4 diff --git a/tests/test_layouts.py b/tests/test_layouts.py index 41b69e0..5509aa3 100644 --- a/tests/test_layouts.py +++ b/tests/test_layouts.py @@ -6,7 +6,7 @@ def test_get_array_layout(): layout = get_array_layout("eht") - assert len(layout.name) == 8 + assert len(layout.st_num) == 8 assert type(layout[0].name) == str assert type(layout[0].x) == np.float64 assert type(layout[0].y) == np.float64 @@ -16,3 +16,12 @@ def test_get_array_layout(): assert type(layout[0].el_high) == np.int64 assert type(layout[0].sefd) == np.int64 assert type(layout[0].altitude) == np.int64 + + layout = get_array_layout("vlba") + + assert len(layout.st_num) == 10 + assert layout.get_station("MKO").st_num == 0 + + layout = get_array_layout("vla") + + assert len(layout.st_num) == 27 diff --git a/tests/test_simulation.py b/tests/test_simulation.py new file mode 100644 index 0000000..53d59c8 --- /dev/null +++ b/tests/test_simulation.py @@ -0,0 +1,61 @@ +import numpy as np +from pyvisgen.utils.config import read_data_set_conf +from pathlib import Path + +np.random.seed(1) +config = "tests/test_conf.toml" +conf = read_data_set_conf(config) +out_path = Path(conf["out_path_fits"]) +out_path.mkdir(parents=True, exist_ok=True) + + +def test_get_data(): + from pyvisgen.utils.data import load_bundles + + data = load_bundles(conf["in_path"]) + assert len(data) > 0 + + +def test_create_sampling_rc(): + from pyvisgen.simulation.data_set import test_opts, create_sampling_rc + + samp_ops = create_sampling_rc(conf) + assert len(samp_ops) == 15 + + test_opts(samp_ops) + + +def test_vis_loop(): + import torch + from pyvisgen.utils.data import load_bundles, open_bundles + from pyvisgen.simulation.data_set import create_sampling_rc, test_opts + from pyvisgen.simulation.visibility import vis_loop + from astropy import units as un + + bundles = load_bundles(conf["in_path"]) + samp_ops = create_sampling_rc(conf) + num_active_telescopes = test_opts(samp_ops) + data = open_bundles(bundles[0]) + SI = torch.tensor(data[0], dtype=torch.cdouble) + vis_data = vis_loop(samp_ops, SI) + + assert type(vis_data[0].SI[0]) == np.complex128 + assert type(vis_data[0].SQ[0]) == np.complex128 + assert type(vis_data[0].SU[0]) == np.complex128 + assert type(vis_data[0].SV[0]) == np.complex128 + assert type(vis_data[0].num) == np.float64 + assert type(vis_data[0].scan) == np.float64 + assert type(vis_data[0].base_num) == np.float64 + assert type(vis_data[0].u) == un.Quantity + assert type(vis_data[0].v) == un.Quantity + assert type(vis_data[0].w) == un.Quantity + assert type(vis_data[0].date) == np.float64 + assert type(vis_data[0]._date) == np.float64 + + # test num vis for time step 0 + num_vis_theory = num_active_telescopes * (num_active_telescopes - 1) / 2 + num_vis_calsc = vis_data.base_num[ + vis_data.date == np.unique(vis_data.date)[0] + ].shape[0] + + assert num_vis_theory == num_vis_calsc diff --git a/tests/test_utils.py b/tests/test_utils.py index 4e9cd4a..f0dbcdf 100644 --- a/tests/test_utils.py +++ b/tests/test_utils.py @@ -48,3 +48,27 @@ def test_calc_time_steps(): time = calc_time_steps(conf) assert time.shape == (2232,) + + +def test_Array(): + from pyvisgen.simulation.utils import Array + from pyvisgen.layouts.layouts import get_array_layout + + array_layout = get_array_layout("vlba") + ar = Array(array_layout) + delta_x, delta_y, delta_z, indices = ar.calc_relative_pos + + mask = ar.get_baseline_mask + + antenna_pairs, st_num_pairs, els_low_pairs, els_high_pairs = ar.calc_ant_pair_vals + + assert delta_x.shape == delta_y.shape == delta_z.shape == (45, 1) + assert indices.shape == (45,) + assert len(mask) == 10 + assert ( + antenna_pairs.shape + == st_num_pairs.shape + == els_low_pairs.shape + == els_high_pairs.shape + == (45, 2) + )