From 6b2d2be182717f503229efc9fe7a759829a50a97 Mon Sep 17 00:00:00 2001 From: anawas Date: Tue, 29 Oct 2024 09:41:57 +0100 Subject: [PATCH 01/51] Warning properly formatted (no newline) --- karabo/simulation/sky_model.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/karabo/simulation/sky_model.py b/karabo/simulation/sky_model.py index ceffcccd..eab39de7 100644 --- a/karabo/simulation/sky_model.py +++ b/karabo/simulation/sky_model.py @@ -825,8 +825,8 @@ def read_from_file(cls: Type[_TSkyModel], path: str) -> _TSkyModel: if dataframe.shape[1] > cls.SOURCES_COLS: print( - f"""CSV has {dataframe.shape[1] - cls.SOURCES_COLS + 1} - too many rows. The extra rows will be cut off.""" + f"CSV has {dataframe.shape[1] - cls.SOURCES_COLS + 1} " + f"rows too many. The extra rows will be cut off." ) return cls(dataframe) From b66dde50500e9d3062252cc0454c83e913a40841 Mon Sep 17 00:00:00 2001 From: anawas Date: Tue, 29 Oct 2024 14:18:55 +0100 Subject: [PATCH 02/51] Testing baseline functionality for OSKAR telescope :dvd: --- karabo/test/test_telescope_baselines.py | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/karabo/test/test_telescope_baselines.py b/karabo/test/test_telescope_baselines.py index 171866da..46e1af31 100644 --- a/karabo/test/test_telescope_baselines.py +++ b/karabo/test/test_telescope_baselines.py @@ -1,3 +1,4 @@ +import math import os import tempfile from datetime import datetime, timedelta @@ -10,6 +11,7 @@ from karabo.simulation.observation import Observation from karabo.simulation.sky_model import SkyModel from karabo.simulation.telescope import Telescope +from karabo.simulator_backend import SimulatorBackend def test_baselines_based_cutoff(sky_data: NDArray[np.float64]): @@ -69,3 +71,16 @@ def test_baselines_based_cutoff(sky_data: NDArray[np.float64]): dirty = dirty_imager.create_dirty_image(visibility) dirty.write_to_file(os.path.join(tmpdir, "baseline_cut.fits"), overwrite=True) dirty.plot(title="Flux Density (Jy)") + + +def test_oskar_telescope_baseline(): + site_tel = Telescope.constructor("LOFAR", backend=SimulatorBackend.OSKAR) + baseline_wgs = site_tel.get_baselines_wgs84() + assert len(baseline_wgs) == 134 + + max_baseline_length = site_tel.max_baseline() + assert math.isclose(max_baseline_length, 998420.050) + + freq_Hz = 100e6 + angular_res = site_tel.ang_res(freq_Hz, max_baseline_length) + assert math.isclose(angular_res, 0.01081, rel_tol=1e-4) From ae119f5b868eee4f76f5641eb3207dea2fb93391 Mon Sep 17 00:00:00 2001 From: anawas Date: Tue, 29 Oct 2024 17:27:36 +0100 Subject: [PATCH 03/51] Added comment about which value for Earth radius astropy uses :beetle: --- karabo/simulation/coordinate_helper.py | 1 + 1 file changed, 1 insertion(+) diff --git a/karabo/simulation/coordinate_helper.py b/karabo/simulation/coordinate_helper.py index cc654747..99babbf0 100644 --- a/karabo/simulation/coordinate_helper.py +++ b/karabo/simulation/coordinate_helper.py @@ -20,6 +20,7 @@ def east_north_to_long_lat( # https://stackoverflow.com/questions/7477003/calculating-new-longitude-latitude-from-old-n-meters r_earth = 6371000 + # astropy uses 6378100.0 (astropy.constants.R_earth.value) new_latitude = lat + (east_relative / r_earth) * (180 / np.pi) new_longitude = long + (north_relative / r_earth) * (180 / np.pi) / np.cos( long * np.pi / 180 From ce8f34e9805fd8edf17929d237c0752f354a04ce Mon Sep 17 00:00:00 2001 From: anawas Date: Tue, 29 Oct 2024 17:29:38 +0100 Subject: [PATCH 04/51] Added test for coordinate_helper functions :speech_balloon: --- karabo/test/test_coordinate_helper.py | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100644 karabo/test/test_coordinate_helper.py diff --git a/karabo/test/test_coordinate_helper.py b/karabo/test/test_coordinate_helper.py new file mode 100644 index 00000000..44c3f983 --- /dev/null +++ b/karabo/test/test_coordinate_helper.py @@ -0,0 +1,23 @@ +import math + +import numpy as np +from numpy.typing import NDArray + +from karabo.simulation.coordinate_helper import ( # east_north_to_long_lat, + wgs84_to_cartesian, +) + + +# wgs for LOFAR +# Longitude 6.86763008, Latitude 52.91139459, height=50.11317741 +# Earth location (metres) +# 3826923.9, 460915.1, 5064643.2 +def test_wgs84_to_cartesian(): + cart_coord: NDArray[np.float64] = wgs84_to_cartesian( + lon=6.86763008, lat=52.91139459, alt=50.11317741 + ) + + print(cart_coord[0]) + assert math.isclose(cart_coord[0], 3826923.9, rel_tol=0.01) + assert math.isclose(cart_coord[1], 460915.1, rel_tol=0.1) + assert math.isclose(cart_coord[2], 5064643.2, rel_tol=0.1) From e9109832bf78a7728082b4be485042e6bb921a59 Mon Sep 17 00:00:00 2001 From: anawas Date: Wed, 30 Oct 2024 07:48:20 +0100 Subject: [PATCH 05/51] Set Earth radius to more precise value :star: --- karabo/simulation/coordinate_helper.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/karabo/simulation/coordinate_helper.py b/karabo/simulation/coordinate_helper.py index 99babbf0..07c009e2 100644 --- a/karabo/simulation/coordinate_helper.py +++ b/karabo/simulation/coordinate_helper.py @@ -19,8 +19,8 @@ def east_north_to_long_lat( """ # https://stackoverflow.com/questions/7477003/calculating-new-longitude-latitude-from-old-n-meters - r_earth = 6371000 - # astropy uses 6378100.0 (astropy.constants.R_earth.value) + r_earth = 6378100 + # from astropy (astropy.constants.R_earth.value) new_latitude = lat + (east_relative / r_earth) * (180 / np.pi) new_longitude = long + (north_relative / r_earth) * (180 / np.pi) / np.cos( long * np.pi / 180 @@ -32,7 +32,7 @@ def wgs84_to_cartesian( lon: Union[float, NDArray[np.float64]], lat: Union[float, NDArray[np.float64]], alt: Union[float, NDArray[np.float64]], - radius: int = 6371000, + radius: int = 6378100, ) -> NDArray[np.float64]: """Transforms WGS84 to cartesian in meters. From aff02b9f9781edf9272d6ed4355b8beeb454b5d1 Mon Sep 17 00:00:00 2001 From: anawas Date: Wed, 30 Oct 2024 12:08:28 +0100 Subject: [PATCH 06/51] using build in conversions for rad <-> deg :tram: --- karabo/simulation/coordinate_helper.py | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/karabo/simulation/coordinate_helper.py b/karabo/simulation/coordinate_helper.py index 07c009e2..a6cd65a7 100644 --- a/karabo/simulation/coordinate_helper.py +++ b/karabo/simulation/coordinate_helper.py @@ -19,11 +19,10 @@ def east_north_to_long_lat( """ # https://stackoverflow.com/questions/7477003/calculating-new-longitude-latitude-from-old-n-meters - r_earth = 6378100 - # from astropy (astropy.constants.R_earth.value) - new_latitude = lat + (east_relative / r_earth) * (180 / np.pi) - new_longitude = long + (north_relative / r_earth) * (180 / np.pi) / np.cos( - long * np.pi / 180 + r_earth = 6378100 # from astropy (astropy.constants.R_earth.value) + new_latitude = lat + np.rad2deg(east_relative / r_earth) + new_longitude = long + np.rad2deg(north_relative / r_earth) / np.cos( + np.deg2rad(long) ) return new_longitude, new_latitude From 6e1434064b60c4c4eac59f7990909daa18951216 Mon Sep 17 00:00:00 2001 From: anawas Date: Wed, 30 Oct 2024 12:32:42 +0100 Subject: [PATCH 07/51] Add test for east_north function :wedding: --- karabo/test/test_coordinate_helper.py | 34 ++++++++++++++++++++++----- 1 file changed, 28 insertions(+), 6 deletions(-) diff --git a/karabo/test/test_coordinate_helper.py b/karabo/test/test_coordinate_helper.py index 44c3f983..5755a8d5 100644 --- a/karabo/test/test_coordinate_helper.py +++ b/karabo/test/test_coordinate_helper.py @@ -3,21 +3,43 @@ import numpy as np from numpy.typing import NDArray -from karabo.simulation.coordinate_helper import ( # east_north_to_long_lat, +from karabo.simulation.coordinate_helper import ( + east_north_to_long_lat, wgs84_to_cartesian, ) # wgs for LOFAR -# Longitude 6.86763008, Latitude 52.91139459, height=50.11317741 -# Earth location (metres) +# longitude 6.86763008, latitude 52.91139459, height 50.11317741 +# expected geocentric (a.k.a. EarthLocation in astropy) (metres) # 3826923.9, 460915.1, 5064643.2 def test_wgs84_to_cartesian(): cart_coord: NDArray[np.float64] = wgs84_to_cartesian( lon=6.86763008, lat=52.91139459, alt=50.11317741 ) - print(cart_coord[0]) assert math.isclose(cart_coord[0], 3826923.9, rel_tol=0.01) - assert math.isclose(cart_coord[1], 460915.1, rel_tol=0.1) - assert math.isclose(cart_coord[2], 5064643.2, rel_tol=0.1) + assert math.isclose(cart_coord[1], 460915.1, rel_tol=0.01) + assert math.isclose(cart_coord[2], 5064643.2, rel_tol=0.01) + + +# +# east,north = 1000, 1000 --> lat,lon = 52.920378,6.876678 +# east,north = -1000, -1000 --> lat,lon = 52.902411,6.858582 + + +def test_east_north_to_long_lat(): + # coords of LOFAR in NL + lon = 6.86763008 + lat = 52.91139459 + # go to new position 1000 m east and north. + east = 1000 + north = 1000 + + new_lon, new_lat = east_north_to_long_lat( + east_relative=east, north_relative=north, long=lon, lat=lat + ) + + print(f"{new_lon=} {new_lat=}") + assert math.isclose(new_lon - 6.876678, 0.0, abs_tol=1e-4) + assert math.isclose(new_lat - 52.920378, 0.0, abs_tol=1e-4) From 86681646bad39575db783fbf249331bd1d9fccd2 Mon Sep 17 00:00:00 2001 From: anawas Date: Wed, 30 Oct 2024 12:45:06 +0100 Subject: [PATCH 08/51] parametrized test :heavy_division_sign: --- karabo/test/test_coordinate_helper.py | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/karabo/test/test_coordinate_helper.py b/karabo/test/test_coordinate_helper.py index 5755a8d5..645eca9a 100644 --- a/karabo/test/test_coordinate_helper.py +++ b/karabo/test/test_coordinate_helper.py @@ -1,6 +1,7 @@ import math import numpy as np +import pytest from numpy.typing import NDArray from karabo.simulation.coordinate_helper import ( @@ -27,19 +28,22 @@ def test_wgs84_to_cartesian(): # east,north = 1000, 1000 --> lat,lon = 52.920378,6.876678 # east,north = -1000, -1000 --> lat,lon = 52.902411,6.858582 +testdata = [ + (1000, 1000, 52.920378, 6.876678), # go east and north 1000m + (-1000, -1000, 52.902411, 6.858582), # go west and south 1000m +] -def test_east_north_to_long_lat(): + +@pytest.mark.parametrize("east, north, test_lat, test_lon", testdata) +def test_east_north_to_long_lat(east, north, test_lat, test_lon): # coords of LOFAR in NL lon = 6.86763008 lat = 52.91139459 - # go to new position 1000 m east and north. - east = 1000 - north = 1000 new_lon, new_lat = east_north_to_long_lat( east_relative=east, north_relative=north, long=lon, lat=lat ) print(f"{new_lon=} {new_lat=}") - assert math.isclose(new_lon - 6.876678, 0.0, abs_tol=1e-4) - assert math.isclose(new_lat - 52.920378, 0.0, abs_tol=1e-4) + assert math.isclose(new_lon - test_lon, 0.0, abs_tol=1e-4) + assert math.isclose(new_lat - test_lat, 0.0, abs_tol=1e-4) From 694a8d544d501c11467e32cf7639e480c901ec9f Mon Sep 17 00:00:00 2001 From: anawas Date: Wed, 30 Oct 2024 13:09:03 +0100 Subject: [PATCH 09/51] removed print statement :grimacing: --- karabo/test/test_coordinate_helper.py | 1 - 1 file changed, 1 deletion(-) diff --git a/karabo/test/test_coordinate_helper.py b/karabo/test/test_coordinate_helper.py index 645eca9a..19090aba 100644 --- a/karabo/test/test_coordinate_helper.py +++ b/karabo/test/test_coordinate_helper.py @@ -44,6 +44,5 @@ def test_east_north_to_long_lat(east, north, test_lat, test_lon): east_relative=east, north_relative=north, long=lon, lat=lat ) - print(f"{new_lon=} {new_lat=}") assert math.isclose(new_lon - test_lon, 0.0, abs_tol=1e-4) assert math.isclose(new_lat - test_lat, 0.0, abs_tol=1e-4) From 15f2886cb8e721c1f9f08e179a1faab1908b6a4d Mon Sep 17 00:00:00 2001 From: anawas Date: Fri, 1 Nov 2024 07:34:22 +0100 Subject: [PATCH 10/51] Added documentation :two_women_holding_hands: --- karabo/simulation/telescope.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/karabo/simulation/telescope.py b/karabo/simulation/telescope.py index b2f93c5e..ac2a77f8 100644 --- a/karabo/simulation/telescope.py +++ b/karabo/simulation/telescope.py @@ -523,6 +523,14 @@ def get_cartesian_position(self) -> NDArray[np.float_]: @classmethod def read_OSKAR_tm_file(cls, path: DirPathType) -> Telescope: + """Reads an OSKAR telescope model from disk and + returns an object of karabo.simulation.telescope.Telescope + + :param path: Path to a valid telescope model (extemsion *.tm) + :return: A karabo.simulation.telescope.Telescope object + :raises: A karabo.error.KaraboError if the path does not exit, + or the data in the file cannot be read. + """ path_ = str(path) abs_station_dir_paths = [] center_position_file = None From f881dee7089d4d136756ca9e1705e52b0059347d Mon Sep 17 00:00:00 2001 From: anawas Date: Fri, 1 Nov 2024 07:36:17 +0100 Subject: [PATCH 11/51] Make clear that backend is OSKAR :oncoming_automobile: --- karabo/simulation/telescope.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/karabo/simulation/telescope.py b/karabo/simulation/telescope.py index ac2a77f8..40dea14b 100644 --- a/karabo/simulation/telescope.py +++ b/karabo/simulation/telescope.py @@ -527,7 +527,8 @@ def read_OSKAR_tm_file(cls, path: DirPathType) -> Telescope: returns an object of karabo.simulation.telescope.Telescope :param path: Path to a valid telescope model (extemsion *.tm) - :return: A karabo.simulation.telescope.Telescope object + :return: A karabo.simulation.telescope.Telescope object. Importantn: + The object has the backend set to SimulatorBackend.OSKAR. :raises: A karabo.error.KaraboError if the path does not exit, or the data in the file cannot be read. """ From eb8cbdaf7bcfe2a9d04a1f83d612a3b3526d220b Mon Sep 17 00:00:00 2001 From: anawas Date: Fri, 1 Nov 2024 08:30:26 +0100 Subject: [PATCH 12/51] Must be able to set name of RASCIL telescope :fork_and_knife: --- karabo/simulation/telescope.py | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/karabo/simulation/telescope.py b/karabo/simulation/telescope.py index 40dea14b..23d317bf 100644 --- a/karabo/simulation/telescope.py +++ b/karabo/simulation/telescope.py @@ -166,6 +166,7 @@ def __init__( Altitude (in meters) at the center of the telescope, default is 0. """ self.path: Optional[DirPathType] = None + self._name = Optional[str] = None self.centre_longitude = longitude self.centre_latitude = latitude self.centre_altitude = altitude @@ -307,10 +308,19 @@ def name(self) -> Optional[str]: Returns: Telescope name or `None`. """ - if self.path is None: + if self._name: + return self._name + elif self._name is None and self.path is None: return None return os.path.split(self.path)[-1].split(".")[0] + @name.setter + def name(self, value: str): + """Sets the name of the telescope. Usually, this is the name + of the telescope model file w/o ending + """ + self._name = value + def get_backend_specific_information(self) -> Union[DirPathType, Configuration]: if self.backend is SimulatorBackend.OSKAR: return self.path @@ -524,13 +534,13 @@ def get_cartesian_position(self) -> NDArray[np.float_]: @classmethod def read_OSKAR_tm_file(cls, path: DirPathType) -> Telescope: """Reads an OSKAR telescope model from disk and - returns an object of karabo.simulation.telescope.Telescope + returns an object of karabo.simulation.telescope.Telescope :param path: Path to a valid telescope model (extemsion *.tm) :return: A karabo.simulation.telescope.Telescope object. Importantn: - The object has the backend set to SimulatorBackend.OSKAR. + The object has the backend set to SimulatorBackend.OSKAR. :raises: A karabo.error.KaraboError if the path does not exit, - or the data in the file cannot be read. + or the data in the file cannot be read. """ path_ = str(path) abs_station_dir_paths = [] From ea96a8c4062a706d3f59fb55049fb50b29e4f657 Mon Sep 17 00:00:00 2001 From: anawas Date: Fri, 1 Nov 2024 08:53:01 +0100 Subject: [PATCH 13/51] Add test to assert name can be set. :fax: --- karabo/simulation/telescope.py | 2 +- karabo/test/test_rascil_telescope_setup.py | 13 +++++++++++++ 2 files changed, 14 insertions(+), 1 deletion(-) create mode 100644 karabo/test/test_rascil_telescope_setup.py diff --git a/karabo/simulation/telescope.py b/karabo/simulation/telescope.py index 23d317bf..54b76850 100644 --- a/karabo/simulation/telescope.py +++ b/karabo/simulation/telescope.py @@ -166,7 +166,7 @@ def __init__( Altitude (in meters) at the center of the telescope, default is 0. """ self.path: Optional[DirPathType] = None - self._name = Optional[str] = None + self._name: Optional[str] = None self.centre_longitude = longitude self.centre_latitude = latitude self.centre_altitude = altitude diff --git a/karabo/test/test_rascil_telescope_setup.py b/karabo/test/test_rascil_telescope_setup.py new file mode 100644 index 00000000..ad7bd912 --- /dev/null +++ b/karabo/test/test_rascil_telescope_setup.py @@ -0,0 +1,13 @@ +from typing import get_args + +import pytest + +from karabo.simulation.telescope import RASCILTelescopes, Telescope + + +@pytest.mark.parametrize("site_name", get_args(RASCILTelescopes)) +def test_set_telescope_name(site_name): + # create dummy telescope, name will be overriden later + site: Telescope = Telescope.constructor("EXAMPLE") + site.name = site_name + assert site.name == site_name From 21198dc4e751f2ab1d6579436c9d660362bbb631 Mon Sep 17 00:00:00 2001 From: anawas Date: Fri, 1 Nov 2024 09:48:04 +0100 Subject: [PATCH 14/51] Fixed typo :neutral_face: --- karabo/test/test_rascil_telescope_setup.py | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/karabo/test/test_rascil_telescope_setup.py b/karabo/test/test_rascil_telescope_setup.py index ad7bd912..de9b8f72 100644 --- a/karabo/test/test_rascil_telescope_setup.py +++ b/karabo/test/test_rascil_telescope_setup.py @@ -6,8 +6,21 @@ @pytest.mark.parametrize("site_name", get_args(RASCILTelescopes)) -def test_set_telescope_name(site_name): +def test_set_telescope_name_from_oskar_telescope(site_name): # create dummy telescope, name will be overriden later site: Telescope = Telescope.constructor("EXAMPLE") + + site.name = site_name + assert site.name == site_name + + +def test_set_telescope_name(): + site_name = "ASKAP" + + # create dummy telescope, name will be overriden later + site: Telescope = Telescope(0.0, 0.0, 0.0) + # __init__() sets name to None + assert site.name is None + site.name = site_name assert site.name == site_name From 43fe31d6f912a9412d8c75c5ad8be97da7c7ba43 Mon Sep 17 00:00:00 2001 From: anawas Date: Fri, 1 Nov 2024 10:32:12 +0100 Subject: [PATCH 15/51] Function to convert RASCIl to OSKAR :running_shirt_with_sash: --- karabo/simulation/telescope.py | 35 ++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/karabo/simulation/telescope.py b/karabo/simulation/telescope.py index 54b76850..d4978f65 100644 --- a/karabo/simulation/telescope.py +++ b/karabo/simulation/telescope.py @@ -24,6 +24,7 @@ import pandas as pd from astropy import constants as const from astropy import units as u +from astropy.coordinates import EarthLocation from numpy.typing import NDArray from oskar.telescope import Telescope as OskarTelescope from rascil.processing_components.simulation.simulation_helpers import ( @@ -299,6 +300,40 @@ def constructor( else: assert_never(backend) + def __convert_to_karabo_telescope(self, instr_name: str) -> Telescope: + """Converts a site saved in RASCIl data format into a Karabo Telescope. + + :param instr_name: The name of the instrument to convert. + + :returns: An instance of Karabo Telescope. + :rtype: karabo.simulation.telescope.Telecope + :raises: ValueError if instr_name is not a valid RASCIL telescope + """ + config: Configuration = create_named_configuration(instr_name) + + site_location_gc: EarthLocation = config.location + # this conversion returns complex type with unit + # lon,lat,alt = site_location_gc.geodetic + longitude = site_location_gc.lon.to("deg").value + latitude = site_location_gc.lat.to("deg").value + altitude = site_location_gc.height.to("m").value + + telescope = Telescope(longitude, latitude, altitude) + + station_coords = config.xyz.data + for i, coord in enumerate(station_coords): + telescope.add_station( + horizontal_x=coord[0], + horizontal_y=coord[1], + horizontal_z=coord[2], + ) + + # there are only stations in the rascil files no antennas + # we add a dummy antenna + telescope.add_antenna_to_station(i, 0.1, 0.1) + telescope.backend = SimulatorBackend.RASCIL + return telescope + @property def name(self) -> Optional[str]: """Gets the telescope name (if available). From 5b4ad5a7907435b338a359bf96cef6c412e43d57 Mon Sep 17 00:00:00 2001 From: anawas Date: Fri, 1 Nov 2024 11:26:06 +0100 Subject: [PATCH 16/51] Function creates KARABO telescope from RASCIL dataset :neutral_face: --- karabo/simulation/telescope.py | 16 +++++----------- 1 file changed, 5 insertions(+), 11 deletions(-) diff --git a/karabo/simulation/telescope.py b/karabo/simulation/telescope.py index d4978f65..bccfca2b 100644 --- a/karabo/simulation/telescope.py +++ b/karabo/simulation/telescope.py @@ -279,7 +279,7 @@ def constructor( ) assert name in get_args(RASCILTelescopes) try: - configuration = create_named_configuration(name) + telescope: Telescope = cls.__convert_to_karabo_telescope(name) except ValueError as e: raise ValueError( f"""Requested telescope {name} is not supported by this backend. @@ -287,20 +287,12 @@ def constructor( https://gitlab.com/ska-telescope/sdp/ska-sdp-datamodels/-/blob/d6dcce6288a7bf6d9ce63ab16e799977723e7ae5/src/ska_sdp_datamodels/configuration/config_create.py""" # noqa ) from e - config_earth_location = configuration.location - telescope = Telescope( - longitude=config_earth_location.lon.to("deg").value, - latitude=config_earth_location.lat.to("deg").value, - altitude=config_earth_location.height.to("m").value, - ) - telescope.backend = SimulatorBackend.RASCIL - telescope.RASCIL_configuration = configuration - return telescope else: assert_never(backend) - def __convert_to_karabo_telescope(self, instr_name: str) -> Telescope: + @classmethod + def __convert_to_karabo_telescope(cls, instr_name: str) -> Telescope: """Converts a site saved in RASCIl data format into a Karabo Telescope. :param instr_name: The name of the instrument to convert. @@ -319,6 +311,8 @@ def __convert_to_karabo_telescope(self, instr_name: str) -> Telescope: altitude = site_location_gc.height.to("m").value telescope = Telescope(longitude, latitude, altitude) + # This is used in some inteferometer simulations + telescope.RASCIL_configuration = config station_coords = config.xyz.data for i, coord in enumerate(station_coords): From 5d267ec3029a10da19174f0aed15107ed1fd9e79 Mon Sep 17 00:00:00 2001 From: anawas Date: Fri, 1 Nov 2024 11:47:21 +0100 Subject: [PATCH 17/51] First test for new RASCIL functionality :small_red_triangle: --- karabo/test/test_rascil_telescope_setup.py | 46 ++++++++++++++++------ 1 file changed, 35 insertions(+), 11 deletions(-) diff --git a/karabo/test/test_rascil_telescope_setup.py b/karabo/test/test_rascil_telescope_setup.py index de9b8f72..8120ad2e 100644 --- a/karabo/test/test_rascil_telescope_setup.py +++ b/karabo/test/test_rascil_telescope_setup.py @@ -1,17 +1,17 @@ -from typing import get_args - import pytest -from karabo.simulation.telescope import RASCILTelescopes, Telescope - - -@pytest.mark.parametrize("site_name", get_args(RASCILTelescopes)) -def test_set_telescope_name_from_oskar_telescope(site_name): - # create dummy telescope, name will be overriden later - site: Telescope = Telescope.constructor("EXAMPLE") +from karabo.simulation.telescope import SimulatorBackend, Telescope - site.name = site_name - assert site.name == site_name +rascil_telesecopes_to_test = [ + # (site_name, num_stations) + # data files are read from + # /envs/karabo/lib/python3.9/site-packages/ska_sdp_datamodels/configuration/ \ + # example_antenna_files/ + ("LOWBD2", 512), + ("MID", 197), + ("ASKAP", 36), + ("LOFAR", 134), +] def test_set_telescope_name(): @@ -24,3 +24,27 @@ def test_set_telescope_name(): site.name = site_name assert site.name == site_name + + +@pytest.mark.parametrize("site_name, _", rascil_telesecopes_to_test) +def test_set_telescope_from_oskar_telescope(site_name, _): + # we must set the backend to RASCIL. Otherwise OSKAR is used by default + site: Telescope = Telescope.constructor(site_name, backend=SimulatorBackend.RASCIL) + + site.name = site_name + assert site.name == site_name + assert site.backend == SimulatorBackend.RASCIL + + +@pytest.mark.parametrize("site_name, num_stations", rascil_telesecopes_to_test) +def test_num_of_stations(site_name, num_stations): + site: Telescope = Telescope.constructor(site_name, backend=SimulatorBackend.RASCIL) + assert len(site.stations) == num_stations + + +# @pytest.mark.parametrize("site_name, num_stations", rascil_telesecopes_to_test) +# def test_num_of_baselines(site_name, num_stations): +# site: Telescope = +# Telescope.constructor(site_name, backend=SimulatorBackend.RASCIL) +# num_baselines = num_stations * (num_stations-1) // 2 +# assert len(site.get_baselines_wgs84()) == num_baselines From d595dd1c5eb0dc4fb928536f344144980dbcb58c Mon Sep 17 00:00:00 2001 From: anawas Date: Mon, 4 Nov 2024 10:42:24 +0100 Subject: [PATCH 18/51] Refactored to own method :boar: --- karabo/simulation/telescope.py | 43 +++++++++++++++++++++------------- 1 file changed, 27 insertions(+), 16 deletions(-) diff --git a/karabo/simulation/telescope.py b/karabo/simulation/telescope.py index bccfca2b..d70d5195 100644 --- a/karabo/simulation/telescope.py +++ b/karabo/simulation/telescope.py @@ -733,6 +733,26 @@ def _get_station_infos(cls, tel_path: DirPathType) -> pd.DataFrame: df_tel["y"] = stations[:, 1] return df_tel + @classmethod + def __create_baselines(cls, n_stations) -> List[Tuple[int, int]]: + """Calculates the baselines of the interferometer. A baseline connects + two stations. + :param n_stations: Number of stations of the telescope + :return: A list of index pairs denoting endpoints of a baseline. + """ + baselines: List[Tuple[int, int]] = sorted( + [ # each unique combination-idx a station with another station + tuple(station_idx) # type: ignore[misc] + for station_idx in set( + map( + frozenset, product(np.arange(n_stations), np.arange(n_stations)) + ) + ) + if len(station_idx) > 1 + ] + ) + return baselines + @classmethod def create_baseline_cut_telescope( cls, @@ -762,20 +782,11 @@ def create_baseline_cut_telescope( raise KaraboError(f"{tm_path=} must end with '.tm'.") df_tel = Telescope._get_station_infos(tel_path=tel.path) n_stations = df_tel.shape[0] + baselines: List[Tuple[int, int]] = cls.__create_baselines(n_stations) + n_baselines = len(baselines) + station_x = df_tel["x"].to_numpy() station_y = df_tel["y"].to_numpy() - baselines: List[Tuple[int, int]] = sorted( - [ # each unique combination-idx a station with another station - tuple(station_idx) # type: ignore[misc] - for station_idx in set( - map( - frozenset, product(np.arange(n_stations), np.arange(n_stations)) - ) - ) - if len(station_idx) > 1 - ] - ) - n_baselines = len(baselines) baseline_dist = np.zeros(n_baselines) for i, (x, y) in enumerate(baselines): baseline_dist[i] = np.linalg.norm(station_x[x] - station_y[y]) @@ -817,13 +828,13 @@ def create_baseline_cut_telescope( np.savetxt(os.path.join(tm_path, "layout.txt"), cut_stations) return tm_path, conversions - def get_baselines_wgs84(self) -> NDArray[np.float64]: - """Gets the interferometer baselines in WGS84. + def get_stations_wgs84(self) -> NDArray[np.float64]: + """Gets the coordinates of the interferometer stations in WGS84. This function assumes that `self.stations` provides WGS84 coordinates. Returns: - Baselines lon[deg]/lat[deg]/alt[m] (nx3). + Stations lon[deg]/lat[deg]/alt[m] (nx3). """ return np.array( [ @@ -864,7 +875,7 @@ def max_baseline(self) -> np.float64: Returns: Length of longest baseline. """ - dists = self.get_baselines_dists(baselines_wgs84=self.get_baselines_wgs84()) + dists = self.get_baselines_dists(baselines_wgs84=self.get_stations_wgs84()) max_distance = np.max(dists) return max_distance From df047fd12de7ac93e6373fe5d259110107ce316c Mon Sep 17 00:00:00 2001 From: anawas Date: Mon, 4 Nov 2024 11:48:12 +0100 Subject: [PATCH 19/51] Calculating combinations is easier with itertools :heavy_minus_sign: --- karabo/simulation/telescope.py | 31 ++++++------------------------- 1 file changed, 6 insertions(+), 25 deletions(-) diff --git a/karabo/simulation/telescope.py b/karabo/simulation/telescope.py index d70d5195..2ee0ddb8 100644 --- a/karabo/simulation/telescope.py +++ b/karabo/simulation/telescope.py @@ -6,7 +6,7 @@ import os import re import shutil -from itertools import product +from itertools import combinations from typing import ( Dict, List, @@ -733,26 +733,6 @@ def _get_station_infos(cls, tel_path: DirPathType) -> pd.DataFrame: df_tel["y"] = stations[:, 1] return df_tel - @classmethod - def __create_baselines(cls, n_stations) -> List[Tuple[int, int]]: - """Calculates the baselines of the interferometer. A baseline connects - two stations. - :param n_stations: Number of stations of the telescope - :return: A list of index pairs denoting endpoints of a baseline. - """ - baselines: List[Tuple[int, int]] = sorted( - [ # each unique combination-idx a station with another station - tuple(station_idx) # type: ignore[misc] - for station_idx in set( - map( - frozenset, product(np.arange(n_stations), np.arange(n_stations)) - ) - ) - if len(station_idx) > 1 - ] - ) - return baselines - @classmethod def create_baseline_cut_telescope( cls, @@ -782,16 +762,17 @@ def create_baseline_cut_telescope( raise KaraboError(f"{tm_path=} must end with '.tm'.") df_tel = Telescope._get_station_infos(tel_path=tel.path) n_stations = df_tel.shape[0] - baselines: List[Tuple[int, int]] = cls.__create_baselines(n_stations) - n_baselines = len(baselines) + baseline_idx: List[Tuple[int, int]] = list(combinations(range(n_stations), 2)) + + n_baselines = len(baseline_idx) station_x = df_tel["x"].to_numpy() station_y = df_tel["y"].to_numpy() baseline_dist = np.zeros(n_baselines) - for i, (x, y) in enumerate(baselines): + for i, (x, y) in enumerate(baseline_idx): baseline_dist[i] = np.linalg.norm(station_x[x] - station_y[y]) cut_idx = np.where((baseline_dist > lcut) & (baseline_dist < hcut)) - cut_station_list = np.unique(np.array(baselines)[cut_idx]) + cut_station_list = np.unique(np.array(baseline_idx)[cut_idx]) df_tel = df_tel[df_tel["station-nr"].isin(cut_station_list)].reset_index( drop=True ) From 12ce08e8ccc3c521649b98e2bf7c153df0a7043f Mon Sep 17 00:00:00 2001 From: anawas Date: Mon, 4 Nov 2024 12:49:55 +0100 Subject: [PATCH 20/51] Better wording (a baseline has a length) :video_camera: --- karabo/simulation/telescope.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/karabo/simulation/telescope.py b/karabo/simulation/telescope.py index 2ee0ddb8..7cc9a2d9 100644 --- a/karabo/simulation/telescope.py +++ b/karabo/simulation/telescope.py @@ -825,7 +825,7 @@ def get_stations_wgs84(self) -> NDArray[np.float64]: ) @classmethod # cls-fun to detach instance constraint - def get_baselines_dists( + def get_baseline_lengths( cls, baselines_wgs84: NDArray[np.float64], ) -> NDArray[np.float64]: @@ -856,7 +856,7 @@ def max_baseline(self) -> np.float64: Returns: Length of longest baseline. """ - dists = self.get_baselines_dists(baselines_wgs84=self.get_stations_wgs84()) + dists = self.get_baseline_lengths(baselines_wgs84=self.get_stations_wgs84()) max_distance = np.max(dists) return max_distance From fce940c9000e54c046ab37b21a0c1d3c754bd251 Mon Sep 17 00:00:00 2001 From: anawas Date: Mon, 4 Nov 2024 14:59:08 +0100 Subject: [PATCH 21/51] Renaming baselines -> stations because the values are coordinates of stations (antennas) :clock130: --- karabo/simulation/telescope.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/karabo/simulation/telescope.py b/karabo/simulation/telescope.py index 7cc9a2d9..998d0754 100644 --- a/karabo/simulation/telescope.py +++ b/karabo/simulation/telescope.py @@ -827,7 +827,7 @@ def get_stations_wgs84(self) -> NDArray[np.float64]: @classmethod # cls-fun to detach instance constraint def get_baseline_lengths( cls, - baselines_wgs84: NDArray[np.float64], + stations_wgs84: NDArray[np.float64], ) -> NDArray[np.float64]: """Gets the interferometer baselines distances in meters. @@ -840,9 +840,9 @@ def get_baseline_lengths( Interferometer baselines dists in meters. """ lon, lat, alt = ( - baselines_wgs84[:, 0], - baselines_wgs84[:, 1], - baselines_wgs84[:, 2], + stations_wgs84[:, 0], + stations_wgs84[:, 1], + stations_wgs84[:, 2], ) cart_coords = wgs84_to_cartesian(lon, lat, alt) dists: NDArray[np.float64] = np.linalg.norm( @@ -856,7 +856,7 @@ def max_baseline(self) -> np.float64: Returns: Length of longest baseline. """ - dists = self.get_baseline_lengths(baselines_wgs84=self.get_stations_wgs84()) + dists = self.get_baseline_lengths(stations_wgs84=self.get_stations_wgs84()) max_distance = np.max(dists) return max_distance From bb29d21f80f49a544147cfea72c98e580c21fe5b Mon Sep 17 00:00:00 2001 From: anawas Date: Mon, 4 Nov 2024 16:51:15 +0100 Subject: [PATCH 22/51] Refactored baseline code :cn: --- karabo/simulation/telescope.py | 14 +++++-- karabo/test/test_telescope_baselines.py | 53 ++++++++++++++++++++----- 2 files changed, 52 insertions(+), 15 deletions(-) diff --git a/karabo/simulation/telescope.py b/karabo/simulation/telescope.py index 998d0754..f31edb02 100644 --- a/karabo/simulation/telescope.py +++ b/karabo/simulation/telescope.py @@ -831,7 +831,7 @@ def get_baseline_lengths( ) -> NDArray[np.float64]: """Gets the interferometer baselines distances in meters. - It's euclidean distance, not geodesic. + It's euclidean distance (aka geocentred), not geodesic. Args: baselines_wgs84: nx3 wgs84 baselines. @@ -844,10 +844,16 @@ def get_baseline_lengths( stations_wgs84[:, 1], stations_wgs84[:, 2], ) - cart_coords = wgs84_to_cartesian(lon, lat, alt) - dists: NDArray[np.float64] = np.linalg.norm( - cart_coords[:, np.newaxis, :] - cart_coords[np.newaxis, :, :], axis=2 + cart_coords: NDArray[np.float64] = wgs84_to_cartesian(lon, lat, alt) + baseline_idx: List[Tuple[int, int]] = list( + combinations(range(len(stations_wgs84)), 2) ) + + dists: NDArray[np.float64] = np.empty((len(baseline_idx))) + for i in range(len(baseline_idx)): + ii, jj = baseline_idx[i] + dists[i] = np.linalg.norm(cart_coords[ii] - cart_coords[jj]) + return dists def max_baseline(self) -> np.float64: diff --git a/karabo/test/test_telescope_baselines.py b/karabo/test/test_telescope_baselines.py index 46e1af31..b6316de1 100644 --- a/karabo/test/test_telescope_baselines.py +++ b/karabo/test/test_telescope_baselines.py @@ -4,6 +4,7 @@ from datetime import datetime, timedelta import numpy as np +import pytest from numpy.typing import NDArray from karabo.imaging.imager_rascil import RascilDirtyImager, RascilDirtyImagerConfig @@ -14,16 +15,27 @@ from karabo.simulator_backend import SimulatorBackend -def test_baselines_based_cutoff(sky_data: NDArray[np.float64]): +@pytest.fixture +def oskar_telescope() -> Telescope: + return Telescope.constructor("MeerKAT", backend=SimulatorBackend.OSKAR) + + +@pytest.fixture +def rascil_telescope() -> Telescope: + return Telescope.constructor("LOFAR", backend=SimulatorBackend.RASCIL) + + +def test_baselines_based_cutoff( + oskar_telescope: Telescope, sky_data: NDArray[np.float64] +): lcut = 5000 hcut = 10000 # Lower cut off and higher cut-off in meters - tel = Telescope.constructor("MeerKAT") with tempfile.TemporaryDirectory() as tmpdir: tm_path = os.path.join(tmpdir, "tel-cut.tm") telescope_path, _ = Telescope.create_baseline_cut_telescope( lcut, hcut, - tel, + oskar_telescope, tm_path=tm_path, ) telescope = Telescope.read_OSKAR_tm_file(telescope_path) @@ -73,14 +85,33 @@ def test_baselines_based_cutoff(sky_data: NDArray[np.float64]): dirty.plot(title="Flux Density (Jy)") -def test_oskar_telescope_baseline(): - site_tel = Telescope.constructor("LOFAR", backend=SimulatorBackend.OSKAR) - baseline_wgs = site_tel.get_baselines_wgs84() - assert len(baseline_wgs) == 134 +def test_telescope_max_baseline_length( + oskar_telescope: Telescope, rascil_telescope: Telescope +): + max_length_oskar = oskar_telescope.max_baseline() + # Should be the same +/- 1 m + assert math.isclose(max_length_oskar - 7500.0, 0, abs_tol=1) - max_baseline_length = site_tel.max_baseline() - assert math.isclose(max_baseline_length, 998420.050) + max_length_rascil = rascil_telescope.max_baseline() + # Should be the same +/- 1 m + assert math.isclose(max_length_rascil - 995242.0, 0, abs_tol=1) freq_Hz = 100e6 - angular_res = site_tel.ang_res(freq_Hz, max_baseline_length) - assert math.isclose(angular_res, 0.01081, rel_tol=1e-4) + angular_res = Telescope.ang_res(freq_Hz, max_length_oskar) + assert math.isclose(angular_res, 1.44, rel_tol=1e-2) + + +def test_telescope_stations(oskar_telescope: Telescope, rascil_telescope: Telescope): + # station has 30 stations according to *.tm file + baseline_wgs = oskar_telescope.get_stations_wgs84() + assert len(baseline_wgs) == 64 + + baseline_wgs = rascil_telescope.get_stations_wgs84() + assert len(baseline_wgs) == 134 + + +def test_telescope_baseline_length(rascil_telescope): + stations_wgs = rascil_telescope.get_stations_wgs84() + num_stations = len(stations_wgs) + baseline_length = Telescope.get_baseline_lengths(stations_wgs) + assert len(baseline_length) == num_stations * (num_stations - 1) / 2 From f2b7fefc23698b3c1f96c2e48547922ee9094565 Mon Sep 17 00:00:00 2001 From: anawas Date: Tue, 5 Nov 2024 08:27:02 +0100 Subject: [PATCH 23/51] Edit comment :left_right_arrow: --- karabo/simulation/telescope.py | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/karabo/simulation/telescope.py b/karabo/simulation/telescope.py index f31edb02..07c36006 100644 --- a/karabo/simulation/telescope.py +++ b/karabo/simulation/telescope.py @@ -741,17 +741,18 @@ def create_baseline_cut_telescope( tel: Telescope, tm_path: Optional[DirPathType] = None, ) -> Tuple[DirPathType, Dict[str, str]]: - """Cut telescope `tel` for baseline-lengths. - - Args: - lcut: Lower cut - hcut: Higher cut - tel: Telescope to cut off - tm_path: .tm file-path to save the cut-telescope. - `tm_path` will get overwritten if it already exists. - - Returns: - .tm file-path & station-name conversion (e.g. station055 -> station009) + """Returns a telescope model for telescope `tel` with baseline lengths + only between `lcut` and `hcut` metres. + + Args: + lcut: Lower cut + hcut: Higher cut + tel: Telescope to cut off + tm_path: .tm file-path to save the cut-telescope. + `tm_path` will get overwritten if it already exists. + + Returns: + .tm file-path & station-name conversion (e.g. station055 -> station009) """ if tel.path is None: raise KaraboError( From 4621fc59fa1751f019a73ac93862c2f3edeb31ac Mon Sep 17 00:00:00 2001 From: anawas Date: Tue, 5 Nov 2024 10:37:25 +0100 Subject: [PATCH 24/51] Must set delimiter or we get an error :sunflower: --- karabo/simulation/telescope.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/karabo/simulation/telescope.py b/karabo/simulation/telescope.py index 07c36006..780aae20 100644 --- a/karabo/simulation/telescope.py +++ b/karabo/simulation/telescope.py @@ -724,7 +724,7 @@ def _get_station_infos(cls, tel_path: DirPathType) -> pd.DataFrame: raise KaraboError( f"Stations found in {tel_path} are not ascending from station<0 - n>. " ) - stations = np.loadtxt(os.path.join(tel_path, "layout.txt")) + stations = np.loadtxt(os.path.join(tel_path, "layout.txt"), delimiter=",") if (n_stations_layout := stations.shape[0]) != (n_stations := df_tel.shape[0]): raise KaraboError( f"Number of stations mismatch of {n_stations_layout=} & {n_stations=}" From 76984df062f8afd21581acb3bd3f44a1496779bb Mon Sep 17 00:00:00 2001 From: anawas Date: Tue, 5 Nov 2024 10:38:53 +0100 Subject: [PATCH 25/51] Must set delimiter or we cannot read in wit _get_station_infos() :laughing: --- karabo/simulation/telescope.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/karabo/simulation/telescope.py b/karabo/simulation/telescope.py index 780aae20..819ccf10 100644 --- a/karabo/simulation/telescope.py +++ b/karabo/simulation/telescope.py @@ -807,7 +807,7 @@ def create_baseline_cut_telescope( dst=os.path.join(tm_path, "position.txt"), ) cut_stations = df_tel[["x", "y"]].to_numpy() - np.savetxt(os.path.join(tm_path, "layout.txt"), cut_stations) + np.savetxt(os.path.join(tm_path, "layout.txt"), cut_stations, delimiter=",") return tm_path, conversions def get_stations_wgs84(self) -> NDArray[np.float64]: From 0ff4ad28755aed762267ae0afc707bc1d33c0c3b Mon Sep 17 00:00:00 2001 From: anawas Date: Wed, 6 Nov 2024 14:08:12 +0100 Subject: [PATCH 26/51] Function __read_layout_txt is more robust than np.loadtxt :family: --- karabo/simulation/telescope.py | 25 ++++++++++++++++++------- 1 file changed, 18 insertions(+), 7 deletions(-) diff --git a/karabo/simulation/telescope.py b/karabo/simulation/telescope.py index 819ccf10..35269047 100644 --- a/karabo/simulation/telescope.py +++ b/karabo/simulation/telescope.py @@ -6,6 +6,7 @@ import os import re import shutil +import tempfile from itertools import combinations from typing import ( Dict, @@ -287,6 +288,13 @@ def constructor( https://gitlab.com/ska-telescope/sdp/ska-sdp-datamodels/-/blob/d6dcce6288a7bf6d9ce63ab16e799977723e7ae5/src/ska_sdp_datamodels/configuration/config_create.py""" # noqa ) from e + # Function like _get_station_infos() and + # create_baseline_cut_telescope() need access to an OSKAR telescope + # model (.tm). This is not available for RASCIL datasets. + # Thus, we create a temporary one. + tmp_tm_path = tempfile.mkdtemp(suffix=".tm", prefix=f"{name}_") + telescope.write_to_disk(tmp_tm_path) + telescope.path = tmp_tm_path return telescope else: assert_never(backend) @@ -524,7 +532,7 @@ def write_to_disk(self, dir_name: DirPathType, *, overwrite: bool = False) -> No ): # for OSKAR & `overwrite` security purpose err_msg = f"{dir_name=} has to end with a `.tm`, but doesn't." raise RuntimeError(err_msg) - with write_dir(dir=dir_name, overwrite=overwrite) as wd: + with write_dir(dir=dir_name, overwrite=True) as wd: self.__write_position_txt(os.path.join(wd, "position.txt")) self.__write_layout_txt( os.path.join(wd, "layout.txt"), @@ -542,7 +550,7 @@ def __write_position_txt(self, position_file_path: str) -> None: position_file = open(position_file_path, "a") position_file.write( - f"{self.centre_longitude} {self.centre_latitude} {self.centre_altitude} \n" + f"{self.centre_longitude} {self.centre_latitude} {self.centre_altitude}\n" ) position_file.close() @@ -552,8 +560,8 @@ def __write_layout_txt( layout_file = open(layout_path, "a") for element in elements: layout_file.write( - f"{element.x}, {element.y}, {element.z}, {element.x_error}, " - + f"{element.y_error}, {element.z_error} \n" + f"{element.x} {element.y} {element.z} {element.x_error} " + + f"{element.y_error} {element.z_error}\n" ) layout_file.close() @@ -586,7 +594,9 @@ def read_OSKAR_tm_file(cls, path: DirPathType) -> Telescope: ) if center_position_file is None: - raise karabo.error.KaraboError("Missing crucial position.txt file_or_dir") + raise karabo.error.KaraboError( + f"Missing crucial position.txt in {file_or_dir}" + ) if station_layout_file is None: raise karabo.error.KaraboError( @@ -724,7 +734,8 @@ def _get_station_infos(cls, tel_path: DirPathType) -> pd.DataFrame: raise KaraboError( f"Stations found in {tel_path} are not ascending from station<0 - n>. " ) - stations = np.loadtxt(os.path.join(tel_path, "layout.txt"), delimiter=",") + stations = np.array(cls.__read_layout_txt(os.path.join(tel_path, "layout.txt"))) + # stations = np.loadtxt(os.path.join(tel_path, "layout.txt"), delimiter=" ") if (n_stations_layout := stations.shape[0]) != (n_stations := df_tel.shape[0]): raise KaraboError( f"Number of stations mismatch of {n_stations_layout=} & {n_stations=}" @@ -807,7 +818,7 @@ def create_baseline_cut_telescope( dst=os.path.join(tm_path, "position.txt"), ) cut_stations = df_tel[["x", "y"]].to_numpy() - np.savetxt(os.path.join(tm_path, "layout.txt"), cut_stations, delimiter=",") + np.savetxt(os.path.join(tm_path, "layout.txt"), cut_stations, delimiter=" ") return tm_path, conversions def get_stations_wgs84(self) -> NDArray[np.float64]: From a63d5142b197cb41c95de5f47062c017a9416b38 Mon Sep 17 00:00:00 2001 From: anawas Date: Wed, 6 Nov 2024 18:02:27 +0100 Subject: [PATCH 27/51] Using karabo functions to create tmp folder. This is consistent with rest of code. :birthday: --- karabo/simulation/telescope.py | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/karabo/simulation/telescope.py b/karabo/simulation/telescope.py index 35269047..598bf981 100644 --- a/karabo/simulation/telescope.py +++ b/karabo/simulation/telescope.py @@ -6,7 +6,6 @@ import os import re import shutil -import tempfile from itertools import combinations from typing import ( Dict, @@ -292,9 +291,14 @@ def constructor( # create_baseline_cut_telescope() need access to an OSKAR telescope # model (.tm). This is not available for RASCIL datasets. # Thus, we create a temporary one. - tmp_tm_path = tempfile.mkdtemp(suffix=".tm", prefix=f"{name}_") - telescope.write_to_disk(tmp_tm_path) - telescope.path = tmp_tm_path + disk_cache = FileHandler().get_tmp_dir( + prefix="telescope-constructor-rascil-", + mkdir=False, + ) + tm_path = os.path.join(disk_cache, f"{name}.tm") + + telescope.write_to_disk(tm_path) + telescope.path = tm_path return telescope else: assert_never(backend) @@ -735,7 +739,6 @@ def _get_station_infos(cls, tel_path: DirPathType) -> pd.DataFrame: f"Stations found in {tel_path} are not ascending from station<0 - n>. " ) stations = np.array(cls.__read_layout_txt(os.path.join(tel_path, "layout.txt"))) - # stations = np.loadtxt(os.path.join(tel_path, "layout.txt"), delimiter=" ") if (n_stations_layout := stations.shape[0]) != (n_stations := df_tel.shape[0]): raise KaraboError( f"Number of stations mismatch of {n_stations_layout=} & {n_stations=}" From 6dbbf8ddf93f45d90299881568a951079212344b Mon Sep 17 00:00:00 2001 From: anawas Date: Thu, 7 Nov 2024 08:18:16 +0100 Subject: [PATCH 28/51] Edit test for create_baseline_cut_telescope :seedling: --- karabo/test/test_telescope_baselines.py | 59 ++++++++++++++++++------- 1 file changed, 43 insertions(+), 16 deletions(-) diff --git a/karabo/test/test_telescope_baselines.py b/karabo/test/test_telescope_baselines.py index b6316de1..13f7d75a 100644 --- a/karabo/test/test_telescope_baselines.py +++ b/karabo/test/test_telescope_baselines.py @@ -5,7 +5,6 @@ import numpy as np import pytest -from numpy.typing import NDArray from karabo.imaging.imager_rascil import RascilDirtyImager, RascilDirtyImagerConfig from karabo.simulation.interferometer import InterferometerSimulation @@ -13,6 +12,7 @@ from karabo.simulation.sky_model import SkyModel from karabo.simulation.telescope import Telescope from karabo.simulator_backend import SimulatorBackend +from karabo.util.data_util import get_module_absolute_path @pytest.fixture @@ -25,9 +25,30 @@ def rascil_telescope() -> Telescope: return Telescope.constructor("LOFAR", backend=SimulatorBackend.RASCIL) -def test_baselines_based_cutoff( - oskar_telescope: Telescope, sky_data: NDArray[np.float64] -): +@pytest.fixture +def sky_model() -> SkyModel: + sky = SkyModel() + sky_data = np.array( + [ + [20.0, -30.0, 1, 0, 0, 0, 100.0e6, -0.7, 0.0, 0, 0, 0], + [20.0, -30.5, 3, 2, 2, 0, 100.0e6, -0.7, 0.0, 600, 50, 45], + [20.5, -30.5, 3, 0, 0, 2, 100.0e6, -0.7, 0.0, 700, 10, -10], + ] + ) + sky.add_point_sources(sky_data) + return sky + + +# This test only tests that the function runs without error. This includes +# - counting number of stations in telescope after cut +# - assert that output measurement set was created +# - assert that FITS image calculated with cut baseline was written +# However, it doesn't test if the result is correct. You would expect a +# different image quality. This is because a cut baseline reduces the +# resolution if the instrument. +def test_baselines_based_cutoff(oskar_telescope: Telescope, sky_data: SkyModel): + # Max. baselength of MeerKAT is 7500 m. Thus, we cut somewhere + # inbetween, lcut = 5000 hcut = 10000 # Lower cut off and higher cut-off in meters with tempfile.TemporaryDirectory() as tmpdir: @@ -39,28 +60,24 @@ def test_baselines_based_cutoff( tm_path=tm_path, ) telescope = Telescope.read_OSKAR_tm_file(telescope_path) + # There are 64 stations fpr MeerKAT. After baseline cut there are + # 11 left. + assert len(telescope.get_stations_wgs84()) == 11 + sky = SkyModel() sky.add_point_sources(sky_data) simulation = InterferometerSimulation( channel_bandwidth_hz=1e6, time_average_sec=1, - noise_enable=False, - noise_seed="time", - noise_freq="Range", - noise_rms="Range", - noise_start_freq=1.0e9, - noise_inc_freq=1.0e8, - noise_number_freq=24, - noise_rms_start=5000, - noise_rms_end=10000, ) + observation = Observation( phase_centre_ra_deg=20.0, + phase_centre_dec_deg=-30.5, start_date_and_time=datetime(2022, 1, 1, 11, 00, 00, 521489), length=timedelta(hours=0, minutes=0, seconds=1, milliseconds=0), - phase_centre_dec_deg=-30.5, number_of_time_steps=1, - start_frequency_hz=1.0e9, + start_frequency_hz=100e6, frequency_increment_hz=1e6, number_of_channels=1, ) @@ -81,8 +98,18 @@ def test_baselines_based_cutoff( ) ) dirty = dirty_imager.create_dirty_image(visibility) + assert os.path.isdir(visibility.path) + dirty.write_to_file(os.path.join(tmpdir, "baseline_cut.fits"), overwrite=True) - dirty.plot(title="Flux Density (Jy)") + + assert os.path.isfile(os.path.join(tmpdir, "baseline_cut.fits")) + + dirty.plot( + title="Flux Density (Jy)", + filename=os.path.join( + get_module_absolute_path(), "test/data/image_cut.png" + ), + ) def test_telescope_max_baseline_length( From b4c77f720309655b7a7c5fc398b30fc3a8db2538 Mon Sep 17 00:00:00 2001 From: anawas Date: Thu, 7 Nov 2024 10:06:52 +0100 Subject: [PATCH 29/51] Narrowing of type now done properly :baby: --- karabo/simulation/telescope.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/karabo/simulation/telescope.py b/karabo/simulation/telescope.py index 598bf981..87ebd7a4 100644 --- a/karabo/simulation/telescope.py +++ b/karabo/simulation/telescope.py @@ -349,14 +349,14 @@ def name(self) -> Optional[str]: Returns: Telescope name or `None`. """ - if self._name: + if self._name is not None: return self._name - elif self._name is None and self.path is None: + if self.path is None: return None return os.path.split(self.path)[-1].split(".")[0] @name.setter - def name(self, value: str): + def name(self, value: str) -> None: """Sets the name of the telescope. Usually, this is the name of the telescope model file w/o ending """ From ba10f8e9a2d2ad2390d917f889a7a3b3cd4f6291 Mon Sep 17 00:00:00 2001 From: anawas Date: Tue, 19 Nov 2024 09:03:23 +0100 Subject: [PATCH 30/51] Removed unnecessary f-string :phone: --- karabo/simulation/sky_model.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/karabo/simulation/sky_model.py b/karabo/simulation/sky_model.py index eab39de7..80f2552a 100644 --- a/karabo/simulation/sky_model.py +++ b/karabo/simulation/sky_model.py @@ -819,14 +819,14 @@ def read_from_file(cls: Type[_TSkyModel], path: str) -> _TSkyModel: if dataframe.shape[1] < 3: raise KaraboSkyModelError( - f"CSV does not have the necessary 3 basic columns (RA, DEC and " + "CSV does not have the necessary 3 basic columns (RA, DEC and " f"STOKES I), but only {dataframe.shape[1]} columns." ) if dataframe.shape[1] > cls.SOURCES_COLS: print( f"CSV has {dataframe.shape[1] - cls.SOURCES_COLS + 1} " - f"rows too many. The extra rows will be cut off." + "rows too many. The extra rows will be cut off." ) return cls(dataframe) From 622535835fe90942dd92401ce2394801da0346e8 Mon Sep 17 00:00:00 2001 From: anawas Date: Tue, 19 Nov 2024 09:30:12 +0100 Subject: [PATCH 31/51] Edit comment to clarify dummy coordinates :revolving_hearts: --- karabo/simulation/telescope.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/karabo/simulation/telescope.py b/karabo/simulation/telescope.py index 87ebd7a4..e9fbd422 100644 --- a/karabo/simulation/telescope.py +++ b/karabo/simulation/telescope.py @@ -335,7 +335,9 @@ def __convert_to_karabo_telescope(cls, instr_name: str) -> Telescope: ) # there are only stations in the rascil files no antennas - # we add a dummy antenna + # we add a dummy antenna in order to avoid the creation + # of an empty file. This matches other files. See + # karabo/data/aca.all.tm/station000/layout.txt for example. telescope.add_antenna_to_station(i, 0.1, 0.1) telescope.backend = SimulatorBackend.RASCIL return telescope From 4309a5a9f0c16686d7199af03b75421c27621f41 Mon Sep 17 00:00:00 2001 From: anawas Date: Tue, 19 Nov 2024 10:10:47 +0100 Subject: [PATCH 32/51] Not ignoring fun-arg :arrows_clockwise: --- karabo/simulation/telescope.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/karabo/simulation/telescope.py b/karabo/simulation/telescope.py index e9fbd422..7ab84ae5 100644 --- a/karabo/simulation/telescope.py +++ b/karabo/simulation/telescope.py @@ -538,7 +538,7 @@ def write_to_disk(self, dir_name: DirPathType, *, overwrite: bool = False) -> No ): # for OSKAR & `overwrite` security purpose err_msg = f"{dir_name=} has to end with a `.tm`, but doesn't." raise RuntimeError(err_msg) - with write_dir(dir=dir_name, overwrite=True) as wd: + with write_dir(dir=dir_name, overwrite=overwrite) as wd: self.__write_position_txt(os.path.join(wd, "position.txt")) self.__write_layout_txt( os.path.join(wd, "layout.txt"), From 571a4fb265a6affa4b1c116e804f23269cfc26c5 Mon Sep 17 00:00:00 2001 From: anawas Date: Tue, 19 Nov 2024 10:33:06 +0100 Subject: [PATCH 33/51] Uncommented test function. It tests if we get the proper number of baselines. :muscle: --- karabo/test/test_rascil_telescope_setup.py | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/karabo/test/test_rascil_telescope_setup.py b/karabo/test/test_rascil_telescope_setup.py index 8120ad2e..9419ed66 100644 --- a/karabo/test/test_rascil_telescope_setup.py +++ b/karabo/test/test_rascil_telescope_setup.py @@ -42,9 +42,11 @@ def test_num_of_stations(site_name, num_stations): assert len(site.stations) == num_stations -# @pytest.mark.parametrize("site_name, num_stations", rascil_telesecopes_to_test) -# def test_num_of_baselines(site_name, num_stations): -# site: Telescope = -# Telescope.constructor(site_name, backend=SimulatorBackend.RASCIL) -# num_baselines = num_stations * (num_stations-1) // 2 -# assert len(site.get_baselines_wgs84()) == num_baselines +@pytest.mark.parametrize("site_name, num_stations", rascil_telesecopes_to_test) +def test_num_of_baselines(site_name, num_stations): + site: Telescope = Telescope.constructor(site_name, backend=SimulatorBackend.RASCIL) + + # This is the predicted number of baselines + num_baselines = num_stations * (num_stations - 1) // 2 + stations = site.get_stations_wgs84() + assert len(site.get_baseline_lengths(stations)) == num_baselines From ef8fc250e828a77fd741c9baee996c2704d0b9db Mon Sep 17 00:00:00 2001 From: anawas Date: Tue, 19 Nov 2024 10:36:44 +0100 Subject: [PATCH 34/51] Better wording :postal_horn: --- karabo/test/test_rascil_telescope_setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/karabo/test/test_rascil_telescope_setup.py b/karabo/test/test_rascil_telescope_setup.py index 9419ed66..73cc2736 100644 --- a/karabo/test/test_rascil_telescope_setup.py +++ b/karabo/test/test_rascil_telescope_setup.py @@ -46,7 +46,7 @@ def test_num_of_stations(site_name, num_stations): def test_num_of_baselines(site_name, num_stations): site: Telescope = Telescope.constructor(site_name, backend=SimulatorBackend.RASCIL) - # This is the predicted number of baselines + # This is the expected number of baselines num_baselines = num_stations * (num_stations - 1) // 2 stations = site.get_stations_wgs84() assert len(site.get_baseline_lengths(stations)) == num_baselines From ad9f69cffff9ec74f01d9e55e78b32b81a65ff43 Mon Sep 17 00:00:00 2001 From: anawas Date: Tue, 19 Nov 2024 17:23:46 +0100 Subject: [PATCH 35/51] Add type hints :v: --- karabo/test/test_telescope_baselines.py | 23 +++++++++++++++-------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/karabo/test/test_telescope_baselines.py b/karabo/test/test_telescope_baselines.py index 13f7d75a..01364e39 100644 --- a/karabo/test/test_telescope_baselines.py +++ b/karabo/test/test_telescope_baselines.py @@ -2,9 +2,11 @@ import os import tempfile from datetime import datetime, timedelta +from typing import Dict, Tuple import numpy as np import pytest +from numpy.typing import NDArray from karabo.imaging.imager_rascil import RascilDirtyImager, RascilDirtyImagerConfig from karabo.simulation.interferometer import InterferometerSimulation @@ -13,6 +15,7 @@ from karabo.simulation.telescope import Telescope from karabo.simulator_backend import SimulatorBackend from karabo.util.data_util import get_module_absolute_path +from karabo.util.file_handler import DirPathType @pytest.fixture @@ -45,7 +48,7 @@ def sky_model() -> SkyModel: # - assert that FITS image calculated with cut baseline was written # However, it doesn't test if the result is correct. You would expect a # different image quality. This is because a cut baseline reduces the -# resolution if the instrument. +# resolution of the instrument. def test_baselines_based_cutoff(oskar_telescope: Telescope, sky_data: SkyModel): # Max. baselength of MeerKAT is 7500 m. Thus, we cut somewhere # inbetween, @@ -53,12 +56,16 @@ def test_baselines_based_cutoff(oskar_telescope: Telescope, sky_data: SkyModel): hcut = 10000 # Lower cut off and higher cut-off in meters with tempfile.TemporaryDirectory() as tmpdir: tm_path = os.path.join(tmpdir, "tel-cut.tm") - telescope_path, _ = Telescope.create_baseline_cut_telescope( + baseline_cut: Tuple[ + DirPathType, Dict[str, str] + ] = Telescope.create_baseline_cut_telescope( lcut, hcut, oskar_telescope, tm_path=tm_path, ) + + telescope_path, _ = baseline_cut telescope = Telescope.read_OSKAR_tm_file(telescope_path) # There are 64 stations fpr MeerKAT. After baseline cut there are # 11 left. @@ -115,22 +122,22 @@ def test_baselines_based_cutoff(oskar_telescope: Telescope, sky_data: SkyModel): def test_telescope_max_baseline_length( oskar_telescope: Telescope, rascil_telescope: Telescope ): - max_length_oskar = oskar_telescope.max_baseline() + max_length_oskar: np.float64 = oskar_telescope.max_baseline() # Should be the same +/- 1 m assert math.isclose(max_length_oskar - 7500.0, 0, abs_tol=1) - max_length_rascil = rascil_telescope.max_baseline() + max_length_rascil: np.float64 = rascil_telescope.max_baseline() # Should be the same +/- 1 m assert math.isclose(max_length_rascil - 995242.0, 0, abs_tol=1) freq_Hz = 100e6 - angular_res = Telescope.ang_res(freq_Hz, max_length_oskar) + angular_res: float = Telescope.ang_res(freq_Hz, max_length_oskar) assert math.isclose(angular_res, 1.44, rel_tol=1e-2) def test_telescope_stations(oskar_telescope: Telescope, rascil_telescope: Telescope): # station has 30 stations according to *.tm file - baseline_wgs = oskar_telescope.get_stations_wgs84() + baseline_wgs: NDArray[np.float64] = oskar_telescope.get_stations_wgs84() assert len(baseline_wgs) == 64 baseline_wgs = rascil_telescope.get_stations_wgs84() @@ -138,7 +145,7 @@ def test_telescope_stations(oskar_telescope: Telescope, rascil_telescope: Telesc def test_telescope_baseline_length(rascil_telescope): - stations_wgs = rascil_telescope.get_stations_wgs84() + stations_wgs: NDArray[np.float64] = rascil_telescope.get_stations_wgs84() num_stations = len(stations_wgs) - baseline_length = Telescope.get_baseline_lengths(stations_wgs) + baseline_length: NDArray[np.float64] = Telescope.get_baseline_lengths(stations_wgs) assert len(baseline_length) == num_stations * (num_stations - 1) / 2 From c7f621fd842c5192f3916c1ace5b32d2c43c7559 Mon Sep 17 00:00:00 2001 From: anawas Date: Tue, 19 Nov 2024 17:36:42 +0100 Subject: [PATCH 36/51] Raising error to fail fast (avoid silent error). Requested in PR #631. --- karabo/simulation/telescope.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/karabo/simulation/telescope.py b/karabo/simulation/telescope.py index 7ab84ae5..2d51c974 100644 --- a/karabo/simulation/telescope.py +++ b/karabo/simulation/telescope.py @@ -446,6 +446,11 @@ def add_antenna_to_station( horizontal_z_coordinate_error, ) ) + else: + raise IndexError( + "You tried to add an antenna to a station that doesn't exist.\n" + f"station_index must be between 0 and {len(self.stations)-1}" + ) def plot_telescope(self, file: Optional[str] = None) -> None: """ From b61765fa578acb05cebde9fdef6bda8373570966 Mon Sep 17 00:00:00 2001 From: anawas Date: Tue, 19 Nov 2024 17:56:08 +0100 Subject: [PATCH 37/51] Set reference to discussion in PR #631 :loop: --- karabo/simulation/telescope.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/karabo/simulation/telescope.py b/karabo/simulation/telescope.py index 2d51c974..85284ad9 100644 --- a/karabo/simulation/telescope.py +++ b/karabo/simulation/telescope.py @@ -334,10 +334,12 @@ def __convert_to_karabo_telescope(cls, instr_name: str) -> Telescope: horizontal_z=coord[2], ) - # there are only stations in the rascil files no antennas + # there are only stations in the rascil files no antennas. # we add a dummy antenna in order to avoid the creation # of an empty file. This matches other files. See # karabo/data/aca.all.tm/station000/layout.txt for example. + # Reason: Value not set to 0 probably to compensate + # for dish diameter. (see comment for PR #631) telescope.add_antenna_to_station(i, 0.1, 0.1) telescope.backend = SimulatorBackend.RASCIL return telescope From 4824d65160fc9b8286458b4002c9f72a1af42e93 Mon Sep 17 00:00:00 2001 From: anawas Date: Wed, 20 Nov 2024 09:57:10 +0100 Subject: [PATCH 38/51] Type hint removed. The return type can be deduced from the function definition. :ram: --- karabo/simulation/telescope.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/karabo/simulation/telescope.py b/karabo/simulation/telescope.py index 85284ad9..04cfbb04 100644 --- a/karabo/simulation/telescope.py +++ b/karabo/simulation/telescope.py @@ -313,7 +313,7 @@ def __convert_to_karabo_telescope(cls, instr_name: str) -> Telescope: :rtype: karabo.simulation.telescope.Telecope :raises: ValueError if instr_name is not a valid RASCIL telescope """ - config: Configuration = create_named_configuration(instr_name) + config = create_named_configuration(instr_name) site_location_gc: EarthLocation = config.location # this conversion returns complex type with unit From 6ffb29fb75e4599340ade1abee6e79a67a798377 Mon Sep 17 00:00:00 2001 From: anawas Date: Thu, 21 Nov 2024 12:48:28 +0100 Subject: [PATCH 39/51] * Antennas are not plotted anymore. They clutter the plot * Title of plot set to "Site Overview" because there are no antennas in the plot --- karabo/simulation/telescope.py | 22 +++++----------------- 1 file changed, 5 insertions(+), 17 deletions(-) diff --git a/karabo/simulation/telescope.py b/karabo/simulation/telescope.py index 04cfbb04..83156fe4 100644 --- a/karabo/simulation/telescope.py +++ b/karabo/simulation/telescope.py @@ -36,10 +36,7 @@ import karabo.error from karabo.error import KaraboError -from karabo.simulation.coordinate_helper import ( - east_north_to_long_lat, - wgs84_to_cartesian, -) +from karabo.simulation.coordinate_helper import wgs84_to_cartesian from karabo.simulation.east_north_coordinate import EastNorthCoordinate from karabo.simulation.station import Station from karabo.simulation.telescope_versions import ( @@ -479,32 +476,23 @@ def plot_telescope_OSKAR( import matplotlib.pyplot as plt fig, ax = plt.subplots() - antenna_x = [] - antenna_y = [] station_x = [] station_y = [] for station in self.stations: station_x.append(station.longitude) station_y.append(station.latitude) - for antenna in station.antennas: - long, lat = east_north_to_long_lat( - antenna.x, antenna.y, station.longitude, station.latitude - ) - antenna_x.append(long) - antenna_y.append(lat) - - ax.scatter(antenna_x, antenna_y, label="Antennas") - ax.scatter(station_x, station_y, label="Stations") + # we set the colour manually in order to keep the colour scheme. + ax.scatter(station_x, station_y, label="Stations", c="tab:orange") x = np.array([self.centre_longitude]) y = np.array([self.centre_latitude]) - ax.scatter(x, y, label="Centre") + ax.scatter(x, y, label="Centre", c="tab:green") ax.ticklabel_format(useOffset=False) ax.set_xlabel("Longitude [deg]") ax.set_ylabel("Latitude [deg]") - ax.set_title("Antenna Locations") + ax.set_title("Site Overview") ax.legend(loc="upper left", shadow=False, fontsize="medium") if file is not None: From 6f1c0f68c28eb569cb70f0c80cc3458583546bf2 Mon Sep 17 00:00:00 2001 From: anawas Date: Thu, 21 Nov 2024 13:37:16 +0100 Subject: [PATCH 40/51] Updated size. Imge is smaller now because antennas are not plotted anymore :credit_card: --- karabo/test/test_telescope.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/karabo/test/test_telescope.py b/karabo/test/test_telescope.py index 077b1b2b..66ce01b5 100644 --- a/karabo/test/test_telescope.py +++ b/karabo/test/test_telescope.py @@ -66,7 +66,7 @@ def test_OSKAR_telescope_plot_file_created(): tel = Telescope.constructor("MeerKAT") tel.plot_telescope(temp_plot_file_name) assert os.path.exists(temp_plot_file_name) - assert os.path.getsize(temp_plot_file_name) == 28171 + assert os.path.getsize(temp_plot_file_name) == 25229 def test_create_alma_telescope(): From 47c2bcd10dfe0ea46990eac109a02db52d645641 Mon Sep 17 00:00:00 2001 From: anawas Date: Thu, 21 Nov 2024 13:58:48 +0100 Subject: [PATCH 41/51] Fixed typo :curry: --- karabo/simulation/telescope.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/karabo/simulation/telescope.py b/karabo/simulation/telescope.py index 83156fe4..94b72741 100644 --- a/karabo/simulation/telescope.py +++ b/karabo/simulation/telescope.py @@ -602,7 +602,7 @@ def read_OSKAR_tm_file(cls, path: DirPathType) -> Telescope: if station_layout_file is None: raise karabo.error.KaraboError( "Missing layout.txt file in station directory. " - "Only Layout.txt is support. " + "Only layout.txt is supported. " "The layout_ecef.txt and layout_wgs84.txt as " "defined in the OSKAR Telescope .tm specification are not " "supported currently." From 1525d668fb86e4cad79c71c4b608579ae6e4d618 Mon Sep 17 00:00:00 2001 From: anawas Date: Mon, 25 Nov 2024 14:13:45 +0100 Subject: [PATCH 42/51] * Add checking for negative index * Removed obsolete function to plot RASCIL configuration --- karabo/simulation/telescope.py | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/karabo/simulation/telescope.py b/karabo/simulation/telescope.py index 5282a2b7..196bfa31 100644 --- a/karabo/simulation/telescope.py +++ b/karabo/simulation/telescope.py @@ -27,9 +27,6 @@ from astropy.coordinates import EarthLocation from numpy.typing import NDArray from oskar.telescope import Telescope as OskarTelescope -from rascil.processing_components.simulation.simulation_helpers import ( - plot_configuration, -) from ska_sdp_datamodels.configuration.config_create import create_named_configuration from ska_sdp_datamodels.configuration.config_model import Configuration from typing_extensions import assert_never @@ -378,7 +375,7 @@ def __convert_to_karabo_telescope(cls, instr_name: str) -> Telescope: # Reason: Value not set to 0 probably to compensate # for dish diameter. (see comment for PR #631) telescope.add_antenna_to_station(i, 0.1, 0.1) - telescope.backend = SimulatorBackend.RASCIL + telescope.backend = SimulatorBackend.RASCIL return telescope @property @@ -473,7 +470,7 @@ def add_antenna_to_station( :param horizontal_z_coordinate_error: altitude of antenna error :return: """ - if station_index < len(self.stations): + if station_index >= 0 and station_index < len(self.stations): station = self.stations[station_index] station.add_station_antenna( EastNorthCoordinate( @@ -499,7 +496,9 @@ def plot_telescope(self, file: Optional[str] = None) -> None: if self.backend is SimulatorBackend.OSKAR: self.plot_telescope_OSKAR(file) elif self.backend is SimulatorBackend.RASCIL: - plot_configuration(self.get_backend_specific_information(), plot_file=file) + # we can use plot_telescope_OSKAR here because we converted + # the RASCIl setup into an OSKAR setup when constructing it. + self.plot_telescope_OSKAR(file) else: logging.warning( f"""Backend {self.backend} is not valid. @@ -532,7 +531,7 @@ def plot_telescope_OSKAR( ax.ticklabel_format(useOffset=False) ax.set_xlabel("Longitude [deg]") ax.set_ylabel("Latitude [deg]") - ax.set_title("Site Overview") + ax.set_title(f"{self.name} Overview") ax.legend(loc="upper left", shadow=False, fontsize="medium") if file is not None: From eed318d54ec66d924b0690f61be74c916aa24e0d Mon Sep 17 00:00:00 2001 From: anawas Date: Tue, 26 Nov 2024 08:41:19 +0100 Subject: [PATCH 43/51] Add files from last pull :cn: --- .../meerkat.tm/element_pattern_fit_x_0_100.bin | Bin 0 -> 4509 bytes .../meerkat.tm/element_pattern_fit_y_0_100.bin | Bin 0 -> 4509 bytes 2 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 karabo/data/meerkat.tm/element_pattern_fit_x_0_100.bin create mode 100644 karabo/data/meerkat.tm/element_pattern_fit_y_0_100.bin diff --git a/karabo/data/meerkat.tm/element_pattern_fit_x_0_100.bin b/karabo/data/meerkat.tm/element_pattern_fit_x_0_100.bin new file mode 100644 index 0000000000000000000000000000000000000000..4c7a50902aa353c8aeb091a6a3f91f47ba52aee0 GIT binary patch literal 4509 zcmd5;Uu;uV7^huVwjrWC5SNf}z{v(%Z~w5)uB_~K8?d=$=vMxOx!_%U+Lg7v_1@Es z8D5k{U=a+7B8G&$m?e@J6CY3~e})<&g7V-4I6$3}Xw={XJQ#e?^PQf%wi~yxcYEL@ z=YF^MyWj8pzTf%Xd%LZ>-QU&J*15>M$j}HhZ8eD|iwTO4EKWn9{b>H9%(vg?L)(C! z%_Vq*4M-)DLZXQXk_d7+T(vf*)8?$NK~9I);r2S5sH%B$V4YC5Fw0YvYSf%k6r3O^ z6je}EMfQF>-OlN`kJ>h|Ca5uVq7RiDZhP=I#Jv=rts?JtPsyuA~xfA()mO zAsgDF;FnW45gbN6t*`Vx%$8niAU*`g453IuRxkocuv=1&VKukdi-?lK+Wr1PtVj`z z!ip3mQ8^*S5LOgf$<%m}LvU-i2QbA8%1LSj1$+%9U@{yevNBwF|E%>m#>#+11QJg# zlUdu?8K8i3r4Tq5Hixp#-p;~#!UOTJeKJNcQ=h)u@tq$VoufeM{ zS0a{_76Duf9RA06%GMJ2XLYHl7q8qo`Z~w809;|XNT62y!m$ix6^L$zC8n0tI>DOJRb(vZI z+Qu(=9n+zOue7Nva53Kue|*QEZ>|(gcCN1#ARptn-;LRxnfm@s|EZPUpV+$QQWF?0 z+e>dBZTf-d%(HXkcX<=XErOiJm&5A@J%4G}?@d|%;JEBxYhk!^{guWo7gtrb?Buw` zz;}c*{NPa0lea!uKWQ{h9lLk_lB+>98s#NJ6-;C5GvHOBqg=Mf;QUp=_aU!iI_cMC zJO+MSJHGclUdMFM^qs!6{bIgZcYNBPZ|-Vavy|&=g+4T&#m=2u+bWLuw-0>r5?h!0 z3|?BWh8|6pwJ7+aV63_gI_ zK0EQtnMR&7&&~}WJozKXErOiJm!9Swdj8U`9~fWZ;JB8;arJ#B_XOVHxW#}w!ddP< zWD-BW-*Lfc?3mnfyYsr#Xf&=qW8TL!ral8+`hLmv*}vg2nb$F$^y_kcw(IU!AzsIH r(DrTlcw{2q9GZB1W4`(Hn7fbbYlS{EpKV$5+mSAh$N%Z&zbgI%EqYOE literal 0 HcmV?d00001 diff --git a/karabo/data/meerkat.tm/element_pattern_fit_y_0_100.bin b/karabo/data/meerkat.tm/element_pattern_fit_y_0_100.bin new file mode 100644 index 0000000000000000000000000000000000000000..4c7a50902aa353c8aeb091a6a3f91f47ba52aee0 GIT binary patch literal 4509 zcmd5;Uu;uV7^huVwjrWC5SNf}z{v(%Z~w5)uB_~K8?d=$=vMxOx!_%U+Lg7v_1@Es z8D5k{U=a+7B8G&$m?e@J6CY3~e})<&g7V-4I6$3}Xw={XJQ#e?^PQf%wi~yxcYEL@ z=YF^MyWj8pzTf%Xd%LZ>-QU&J*15>M$j}HhZ8eD|iwTO4EKWn9{b>H9%(vg?L)(C! z%_Vq*4M-)DLZXQXk_d7+T(vf*)8?$NK~9I);r2S5sH%B$V4YC5Fw0YvYSf%k6r3O^ z6je}EMfQF>-OlN`kJ>h|Ca5uVq7RiDZhP=I#Jv=rts?JtPsyuA~xfA()mO zAsgDF;FnW45gbN6t*`Vx%$8niAU*`g453IuRxkocuv=1&VKukdi-?lK+Wr1PtVj`z z!ip3mQ8^*S5LOgf$<%m}LvU-i2QbA8%1LSj1$+%9U@{yevNBwF|E%>m#>#+11QJg# zlUdu?8K8i3r4Tq5Hixp#-p;~#!UOTJeKJNcQ=h)u@tq$VoufeM{ zS0a{_76Duf9RA06%GMJ2XLYHl7q8qo`Z~w809;|XNT62y!m$ix6^L$zC8n0tI>DOJRb(vZI z+Qu(=9n+zOue7Nva53Kue|*QEZ>|(gcCN1#ARptn-;LRxnfm@s|EZPUpV+$QQWF?0 z+e>dBZTf-d%(HXkcX<=XErOiJm&5A@J%4G}?@d|%;JEBxYhk!^{guWo7gtrb?Buw` zz;}c*{NPa0lea!uKWQ{h9lLk_lB+>98s#NJ6-;C5GvHOBqg=Mf;QUp=_aU!iI_cMC zJO+MSJHGclUdMFM^qs!6{bIgZcYNBPZ|-Vavy|&=g+4T&#m=2u+bWLuw-0>r5?h!0 z3|?BWh8|6pwJ7+aV63_gI_ zK0EQtnMR&7&&~}WJozKXErOiJm!9Swdj8U`9~fWZ;JB8;arJ#B_XOVHxW#}w!ddP< zWD-BW-*Lfc?3mnfyYsr#Xf&=qW8TL!ral8+`hLmv*}vg2nb$F$^y_kcw(IU!AzsIH r(DrTlcw{2q9GZB1W4`(Hn7fbbYlS{EpKV$5+mSAh$N%Z&zbgI%EqYOE literal 0 HcmV?d00001 From 8857e0c4460587e285c52fccb15101fd6394a153 Mon Sep 17 00:00:00 2001 From: anawas Date: Tue, 26 Nov 2024 09:48:20 +0100 Subject: [PATCH 44/51] Excluding binary files that are written by OSKAR. --- .gitignore | 3 +++ .../meerkat.tm/element_pattern_fit_x_0_100.bin | Bin 4509 -> 0 bytes .../meerkat.tm/element_pattern_fit_y_0_100.bin | Bin 4509 -> 0 bytes 3 files changed, 3 insertions(+) delete mode 100644 karabo/data/meerkat.tm/element_pattern_fit_x_0_100.bin delete mode 100644 karabo/data/meerkat.tm/element_pattern_fit_y_0_100.bin diff --git a/.gitignore b/.gitignore index 82711ac6..7a67bdd4 100644 --- a/.gitignore +++ b/.gitignore @@ -114,3 +114,6 @@ karabo/examples/karabo/test/data/results/* # Other (playground, experiment, ..) .experiment/* data_download/* + +# files that are generated by OSKAR +karabo/data/meerkat.tm/element_pattern* diff --git a/karabo/data/meerkat.tm/element_pattern_fit_x_0_100.bin b/karabo/data/meerkat.tm/element_pattern_fit_x_0_100.bin deleted file mode 100644 index 4c7a50902aa353c8aeb091a6a3f91f47ba52aee0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4509 zcmd5;Uu;uV7^huVwjrWC5SNf}z{v(%Z~w5)uB_~K8?d=$=vMxOx!_%U+Lg7v_1@Es z8D5k{U=a+7B8G&$m?e@J6CY3~e})<&g7V-4I6$3}Xw={XJQ#e?^PQf%wi~yxcYEL@ z=YF^MyWj8pzTf%Xd%LZ>-QU&J*15>M$j}HhZ8eD|iwTO4EKWn9{b>H9%(vg?L)(C! z%_Vq*4M-)DLZXQXk_d7+T(vf*)8?$NK~9I);r2S5sH%B$V4YC5Fw0YvYSf%k6r3O^ z6je}EMfQF>-OlN`kJ>h|Ca5uVq7RiDZhP=I#Jv=rts?JtPsyuA~xfA()mO zAsgDF;FnW45gbN6t*`Vx%$8niAU*`g453IuRxkocuv=1&VKukdi-?lK+Wr1PtVj`z z!ip3mQ8^*S5LOgf$<%m}LvU-i2QbA8%1LSj1$+%9U@{yevNBwF|E%>m#>#+11QJg# zlUdu?8K8i3r4Tq5Hixp#-p;~#!UOTJeKJNcQ=h)u@tq$VoufeM{ zS0a{_76Duf9RA06%GMJ2XLYHl7q8qo`Z~w809;|XNT62y!m$ix6^L$zC8n0tI>DOJRb(vZI z+Qu(=9n+zOue7Nva53Kue|*QEZ>|(gcCN1#ARptn-;LRxnfm@s|EZPUpV+$QQWF?0 z+e>dBZTf-d%(HXkcX<=XErOiJm&5A@J%4G}?@d|%;JEBxYhk!^{guWo7gtrb?Buw` zz;}c*{NPa0lea!uKWQ{h9lLk_lB+>98s#NJ6-;C5GvHOBqg=Mf;QUp=_aU!iI_cMC zJO+MSJHGclUdMFM^qs!6{bIgZcYNBPZ|-Vavy|&=g+4T&#m=2u+bWLuw-0>r5?h!0 z3|?BWh8|6pwJ7+aV63_gI_ zK0EQtnMR&7&&~}WJozKXErOiJm!9Swdj8U`9~fWZ;JB8;arJ#B_XOVHxW#}w!ddP< zWD-BW-*Lfc?3mnfyYsr#Xf&=qW8TL!ral8+`hLmv*}vg2nb$F$^y_kcw(IU!AzsIH r(DrTlcw{2q9GZB1W4`(Hn7fbbYlS{EpKV$5+mSAh$N%Z&zbgI%EqYOE diff --git a/karabo/data/meerkat.tm/element_pattern_fit_y_0_100.bin b/karabo/data/meerkat.tm/element_pattern_fit_y_0_100.bin deleted file mode 100644 index 4c7a50902aa353c8aeb091a6a3f91f47ba52aee0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4509 zcmd5;Uu;uV7^huVwjrWC5SNf}z{v(%Z~w5)uB_~K8?d=$=vMxOx!_%U+Lg7v_1@Es z8D5k{U=a+7B8G&$m?e@J6CY3~e})<&g7V-4I6$3}Xw={XJQ#e?^PQf%wi~yxcYEL@ z=YF^MyWj8pzTf%Xd%LZ>-QU&J*15>M$j}HhZ8eD|iwTO4EKWn9{b>H9%(vg?L)(C! z%_Vq*4M-)DLZXQXk_d7+T(vf*)8?$NK~9I);r2S5sH%B$V4YC5Fw0YvYSf%k6r3O^ z6je}EMfQF>-OlN`kJ>h|Ca5uVq7RiDZhP=I#Jv=rts?JtPsyuA~xfA()mO zAsgDF;FnW45gbN6t*`Vx%$8niAU*`g453IuRxkocuv=1&VKukdi-?lK+Wr1PtVj`z z!ip3mQ8^*S5LOgf$<%m}LvU-i2QbA8%1LSj1$+%9U@{yevNBwF|E%>m#>#+11QJg# zlUdu?8K8i3r4Tq5Hixp#-p;~#!UOTJeKJNcQ=h)u@tq$VoufeM{ zS0a{_76Duf9RA06%GMJ2XLYHl7q8qo`Z~w809;|XNT62y!m$ix6^L$zC8n0tI>DOJRb(vZI z+Qu(=9n+zOue7Nva53Kue|*QEZ>|(gcCN1#ARptn-;LRxnfm@s|EZPUpV+$QQWF?0 z+e>dBZTf-d%(HXkcX<=XErOiJm&5A@J%4G}?@d|%;JEBxYhk!^{guWo7gtrb?Buw` zz;}c*{NPa0lea!uKWQ{h9lLk_lB+>98s#NJ6-;C5GvHOBqg=Mf;QUp=_aU!iI_cMC zJO+MSJHGclUdMFM^qs!6{bIgZcYNBPZ|-Vavy|&=g+4T&#m=2u+bWLuw-0>r5?h!0 z3|?BWh8|6pwJ7+aV63_gI_ zK0EQtnMR&7&&~}WJozKXErOiJm!9Swdj8U`9~fWZ;JB8;arJ#B_XOVHxW#}w!ddP< zWD-BW-*Lfc?3mnfyYsr#Xf&=qW8TL!ral8+`hLmv*}vg2nb$F$^y_kcw(IU!AzsIH r(DrTlcw{2q9GZB1W4`(Hn7fbbYlS{EpKV$5+mSAh$N%Z&zbgI%EqYOE From 3e59b3fcaea0f263a89e389b66fd82f14881e454 Mon Sep 17 00:00:00 2001 From: anawas Date: Tue, 26 Nov 2024 09:49:29 +0100 Subject: [PATCH 45/51] Not testing for specific file size anymore. But if file size > 0. --- karabo/test/test_telescope.py | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/karabo/test/test_telescope.py b/karabo/test/test_telescope.py index 66ce01b5..e73a382c 100644 --- a/karabo/test/test_telescope.py +++ b/karabo/test/test_telescope.py @@ -66,7 +66,11 @@ def test_OSKAR_telescope_plot_file_created(): tel = Telescope.constructor("MeerKAT") tel.plot_telescope(temp_plot_file_name) assert os.path.exists(temp_plot_file_name) - assert os.path.getsize(temp_plot_file_name) == 25229 + # It is tedious to check a specific file size. Even + # small changes to the code creating the image will make + # this test fail. Thus, I check only if the file size + # is > 0. + assert os.path.getsize(temp_plot_file_name) > 0 def test_create_alma_telescope(): @@ -202,7 +206,11 @@ def test_RASCIL_telescope_plot_file_created(): tel = Telescope.constructor("MID", backend=SimulatorBackend.RASCIL) tel.plot_telescope(temp_plot_file_name) assert os.path.exists(temp_plot_file_name) - assert os.path.getsize(temp_plot_file_name) == 20583 + # It is tedious to check a specific file size. Even + # small changes to the code creating the image will make + # this test fail. Thus, I check only if the file size + # is > 0. + assert os.path.getsize(temp_plot_file_name) > 0 # There is an if statement in Telescope::plot_telescope for the From 01175d3cefda2b6032d9b2ca69eec80628c16902 Mon Sep 17 00:00:00 2001 From: anawas Date: Tue, 26 Nov 2024 09:54:06 +0100 Subject: [PATCH 46/51] Add type hint :sleepy: --- karabo/test/test_telescope_baselines.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/karabo/test/test_telescope_baselines.py b/karabo/test/test_telescope_baselines.py index 01364e39..55f45f2e 100644 --- a/karabo/test/test_telescope_baselines.py +++ b/karabo/test/test_telescope_baselines.py @@ -140,7 +140,7 @@ def test_telescope_stations(oskar_telescope: Telescope, rascil_telescope: Telesc baseline_wgs: NDArray[np.float64] = oskar_telescope.get_stations_wgs84() assert len(baseline_wgs) == 64 - baseline_wgs = rascil_telescope.get_stations_wgs84() + baseline_wgs: NDArray[np.float64] = rascil_telescope.get_stations_wgs84() assert len(baseline_wgs) == 134 From 09c52d422802fe48089b6f1bc1134624bad0f0a4 Mon Sep 17 00:00:00 2001 From: anawas Date: Thu, 28 Nov 2024 09:18:58 +0100 Subject: [PATCH 47/51] np.isclose() is better code style in this case :green_heart: --- karabo/test/test_coordinate_helper.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/karabo/test/test_coordinate_helper.py b/karabo/test/test_coordinate_helper.py index 19090aba..0c92e42a 100644 --- a/karabo/test/test_coordinate_helper.py +++ b/karabo/test/test_coordinate_helper.py @@ -19,9 +19,8 @@ def test_wgs84_to_cartesian(): lon=6.86763008, lat=52.91139459, alt=50.11317741 ) - assert math.isclose(cart_coord[0], 3826923.9, rel_tol=0.01) - assert math.isclose(cart_coord[1], 460915.1, rel_tol=0.01) - assert math.isclose(cart_coord[2], 5064643.2, rel_tol=0.01) + geocentric_coords_expected = [3826923.9, 460915.1, 5064643.2] + assert np.isclose(cart_coord, geocentric_coords_expected, rtol=0.01).all() # From 3c14024071da4b5c5a396a74d5b30057a7cb66c5 Mon Sep 17 00:00:00 2001 From: anawas Date: Thu, 28 Nov 2024 11:20:12 +0100 Subject: [PATCH 48/51] Added some comment to clarify algorithm --- karabo/simulation/telescope.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/karabo/simulation/telescope.py b/karabo/simulation/telescope.py index 196bfa31..642b894a 100644 --- a/karabo/simulation/telescope.py +++ b/karabo/simulation/telescope.py @@ -895,12 +895,18 @@ def get_baseline_lengths( stations_wgs84[:, 1], stations_wgs84[:, 2], ) + cart_coords: NDArray[np.float64] = wgs84_to_cartesian(lon, lat, alt) + + # Create an index list of all pairs of stations, i.e. + # (1,2), (1,3), (1,4), ...., (n,n-2), (n,n-1) baseline_idx: List[Tuple[int, int]] = list( combinations(range(len(stations_wgs84)), 2) ) dists: NDArray[np.float64] = np.empty((len(baseline_idx))) + + # straighforward, no scipy magic (scipy's pdist()) needed for i in range(len(baseline_idx)): ii, jj = baseline_idx[i] dists[i] = np.linalg.norm(cart_coords[ii] - cart_coords[jj]) From 85936194e26ec859d7e60fa276fbbc4b119f7917 Mon Sep 17 00:00:00 2001 From: anawas Date: Thu, 28 Nov 2024 11:22:33 +0100 Subject: [PATCH 49/51] Using numpy.isclose() is smarter solution. --- karabo/test/test_coordinate_helper.py | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/karabo/test/test_coordinate_helper.py b/karabo/test/test_coordinate_helper.py index 0c92e42a..6fff68ce 100644 --- a/karabo/test/test_coordinate_helper.py +++ b/karabo/test/test_coordinate_helper.py @@ -1,5 +1,3 @@ -import math - import numpy as np import pytest from numpy.typing import NDArray @@ -39,9 +37,10 @@ def test_east_north_to_long_lat(east, north, test_lat, test_lon): lon = 6.86763008 lat = 52.91139459 - new_lon, new_lat = east_north_to_long_lat( - east_relative=east, north_relative=north, long=lon, lat=lat + new_coords = np.array( + east_north_to_long_lat( + east_relative=east, north_relative=north, long=lon, lat=lat + ) ) - assert math.isclose(new_lon - test_lon, 0.0, abs_tol=1e-4) - assert math.isclose(new_lat - test_lat, 0.0, abs_tol=1e-4) + assert np.isclose(new_coords, np.array([test_lon, test_lat]), atol=1e-4).all() From 073168cdb1da3a01bf96378c82ca1582041106cc Mon Sep 17 00:00:00 2001 From: anawas Date: Thu, 28 Nov 2024 12:01:46 +0100 Subject: [PATCH 50/51] Add comment to make purpose of function __convert_to_karabo_telescope clearer. --- karabo/simulation/telescope.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/karabo/simulation/telescope.py b/karabo/simulation/telescope.py index 642b894a..4969d2df 100644 --- a/karabo/simulation/telescope.py +++ b/karabo/simulation/telescope.py @@ -340,6 +340,14 @@ def constructor( @classmethod def __convert_to_karabo_telescope(cls, instr_name: str) -> Telescope: """Converts a site saved in RASCIl data format into a Karabo Telescope. + This function acts as an adapter to make the functionality in Telescope + class work for a RASCIL telescope. Namely the functions max_baseline() + and get_baseline_lengths(). + It derives the necessary data structures from the RASCIL_configuration + and fits them into those of the Telescope class. The resuting class is + a SimulatorBackend.RASCIL but has the stations: List[Station] + list filled as well. Nevertheless, it should only be used as a RASCIL + telescope class. :param instr_name: The name of the instrument to convert. From 54b6779d6eaece37b5fc8153cb4f7bd3dcc9d1f3 Mon Sep 17 00:00:00 2001 From: "lukas.gehrig" Date: Thu, 28 Nov 2024 16:45:49 +0100 Subject: [PATCH 51/51] addressed MR feedback regarding pdist :saxophone: --- karabo/simulation/telescope.py | 20 ++++---------------- 1 file changed, 4 insertions(+), 16 deletions(-) diff --git a/karabo/simulation/telescope.py b/karabo/simulation/telescope.py index 4969d2df..8307005c 100644 --- a/karabo/simulation/telescope.py +++ b/karabo/simulation/telescope.py @@ -27,6 +27,7 @@ from astropy.coordinates import EarthLocation from numpy.typing import NDArray from oskar.telescope import Telescope as OskarTelescope +from scipy.spatial.distance import pdist from ska_sdp_datamodels.configuration.config_create import create_named_configuration from ska_sdp_datamodels.configuration.config_model import Configuration from typing_extensions import assert_never @@ -883,14 +884,14 @@ def get_stations_wgs84(self) -> NDArray[np.float64]: ] ) - @classmethod # cls-fun to detach instance constraint + @classmethod def get_baseline_lengths( cls, stations_wgs84: NDArray[np.float64], ) -> NDArray[np.float64]: """Gets the interferometer baselines distances in meters. - It's euclidean distance (aka geocentred), not geodesic. + It's euclidean distance (aka geocentric), not geodesic. Args: baselines_wgs84: nx3 wgs84 baselines. @@ -906,20 +907,7 @@ def get_baseline_lengths( cart_coords: NDArray[np.float64] = wgs84_to_cartesian(lon, lat, alt) - # Create an index list of all pairs of stations, i.e. - # (1,2), (1,3), (1,4), ...., (n,n-2), (n,n-1) - baseline_idx: List[Tuple[int, int]] = list( - combinations(range(len(stations_wgs84)), 2) - ) - - dists: NDArray[np.float64] = np.empty((len(baseline_idx))) - - # straighforward, no scipy magic (scipy's pdist()) needed - for i in range(len(baseline_idx)): - ii, jj = baseline_idx[i] - dists[i] = np.linalg.norm(cart_coords[ii] - cart_coords[jj]) - - return dists + return cast(NDArray[np.float64], pdist(cart_coords)) def max_baseline(self) -> np.float64: """Gets the longest baseline in meters.