From d9ab972b050c2c0d9e27bd31a9f1f4623e16fcce Mon Sep 17 00:00:00 2001 From: lesliech1004 Date: Fri, 11 Jul 2025 14:53:52 -0400 Subject: [PATCH 01/10] Draft code for SIMBAD resolvable name check --- astrodb_utils/sources.py | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/astrodb_utils/sources.py b/astrodb_utils/sources.py index beb70f9..ef50df6 100644 --- a/astrodb_utils/sources.py +++ b/astrodb_utils/sources.py @@ -211,6 +211,33 @@ def coords_from_simbad(source): return simbad_skycoord +def simbad_name_resolvable(source, ra, dec): + #check name of match is SIMBAD resolvable + simbad_resolvable = False + #search SIMBAD for the source using its name + simbad_result_table = Simbad.query_object(source) + if simbad_result_table is None: + #name is not resolvable or not in SIMBAD database + logger.debug(f"SIMBAD returned no results for {source}") + elif len(simbad_result_table) == 1: + logger.debug( + f"simbad colnames: {simbad_result_table.colnames} \n simbad results \n {simbad_result_table}" + ) + simbad_name = f"{simbad_result_table['main_id'][0]}" + other_name = f"{simbad_result_table['matched_id'][0]}" + logger.debug(f"SIMBAD name string: {simbad_name}") + if (simbad_name == source) or (source == other_name): + simbad_resolvable= True + else: + msg = f"Name not resolvable in SIMBAD for {source}" + logger.warning(msg) + + #search SIMBAD using coordinates and return list of possible names that are resolvable in SIMBAD + simbad_coord_result = Simbad.query_region(SkyCoord(ra = ra, dec = dec, unit = "deg")) + print(simbad_coord_result) + print(f"Alternate names for {source} that are resolvable in SIMBAD: {simbad_coord_result['main_id']}") + + return simbad_resolvable # NAMES def ingest_name( From 1b427f92a964329e6c213602891bab3e847cec44 Mon Sep 17 00:00:00 2001 From: lesliech1004 Date: Wed, 16 Jul 2025 14:48:52 -0400 Subject: [PATCH 02/10] Wrote tests for simbad_resolvable_names --- tests/test_sources.py | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/tests/test_sources.py b/tests/test_sources.py index 2a50541..83b367c 100644 --- a/tests/test_sources.py +++ b/tests/test_sources.py @@ -12,6 +12,7 @@ ingest_name, ingest_source, strip_unicode_dashes, + simbad_name_resolvable ) @@ -262,3 +263,18 @@ def test_ingest_name(db): def test_strip_unicode_dashes(input, expected): result = strip_unicode_dashes(input) assert result == expected + +@pytest.mark.parametrize('input,ra, dec,expected', [ + #2 cases whose names are NOT in the database + ("Apple", 144.395292,29.528028,"2MASS J09373487+2931409"), + ("Banana", 115.27833,17.645833,"LHS 1937"), + #2 cases whose names are in the database + ("2MASS J02394245-1735471",39.9268755,-17.596417,"2MASS J02394245-1735471"), + ("HIP 63506C", 195.2108, 42.2465, "HIP 63506C") +]) + +def test_simbad_resolvable_names(input,ra,dec,expected): + + result = simbad_name_resolvable(input, ra, dec) + assert result == expected + From d6a6a48b898b02b759a84005679b3891d83bdf36 Mon Sep 17 00:00:00 2001 From: lesliech1004 Date: Wed, 16 Jul 2025 16:05:39 -0400 Subject: [PATCH 03/10] Updated and corrected test cases for test_simbad_resolvable names --- astrodb_utils/sources.py | 3 +-- tests/test_sources.py | 20 ++++++++++++-------- 2 files changed, 13 insertions(+), 10 deletions(-) diff --git a/astrodb_utils/sources.py b/astrodb_utils/sources.py index ef50df6..0a84051 100644 --- a/astrodb_utils/sources.py +++ b/astrodb_utils/sources.py @@ -234,10 +234,9 @@ def simbad_name_resolvable(source, ra, dec): #search SIMBAD using coordinates and return list of possible names that are resolvable in SIMBAD simbad_coord_result = Simbad.query_region(SkyCoord(ra = ra, dec = dec, unit = "deg")) - print(simbad_coord_result) print(f"Alternate names for {source} that are resolvable in SIMBAD: {simbad_coord_result['main_id']}") - return simbad_resolvable + return simbad_resolvable, simbad_coord_result['main_id'] # NAMES def ingest_name( diff --git a/tests/test_sources.py b/tests/test_sources.py index 83b367c..522dd08 100644 --- a/tests/test_sources.py +++ b/tests/test_sources.py @@ -264,17 +264,21 @@ def test_strip_unicode_dashes(input, expected): result = strip_unicode_dashes(input) assert result == expected -@pytest.mark.parametrize('input,ra, dec,expected', [ +@pytest.mark.parametrize('input,ra, dec', [ #2 cases whose names are NOT in the database - ("Apple", 144.395292,29.528028,"2MASS J09373487+2931409"), - ("Banana", 115.27833,17.645833,"LHS 1937"), + ("Apple", 144.395292,29.528028), + ("Banana", 3.9888,4.2511), #2 cases whose names are in the database - ("2MASS J02394245-1735471",39.9268755,-17.596417,"2MASS J02394245-1735471"), - ("HIP 63506C", 195.2108, 42.2465, "HIP 63506C") + ("2MASS J07222760-0540384",110.6149995,-5.677333), + ("ULAS J000734.90+011247.1", 1.8957, 1.2132) ]) -def test_simbad_resolvable_names(input,ra,dec,expected): - +def test_simbad_resolvable_names(input,ra,dec): result = simbad_name_resolvable(input, ra, dec) - assert result == expected + isResolvable = False + print(result) + if input in result[1]: + isResolvable = True + print(isResolvable) + assert isResolvable == result[0] From 9ec8d5570158a0f47be118aebee46dcb721326a1 Mon Sep 17 00:00:00 2001 From: lesliech1004 <160793895+lesliech1004@users.noreply.github.com> Date: Wed, 23 Jul 2025 19:20:22 -0400 Subject: [PATCH 04/10] Update tests/test_sources.py Co-authored-by: Kelle Cruz --- tests/test_sources.py | 19 +++++++------------ 1 file changed, 7 insertions(+), 12 deletions(-) diff --git a/tests/test_sources.py b/tests/test_sources.py index 522dd08..18b2cc4 100644 --- a/tests/test_sources.py +++ b/tests/test_sources.py @@ -264,21 +264,16 @@ def test_strip_unicode_dashes(input, expected): result = strip_unicode_dashes(input) assert result == expected -@pytest.mark.parametrize('input,ra, dec', [ +@pytest.mark.parametrize('input,ra, dec, expected', [ #2 cases whose names are NOT in the database - ("Apple", 144.395292,29.528028), - ("Banana", 3.9888,4.2511), + ("Apple", 144.395292,29.528028, false), + ("Banana", 3.9888,4.2511, false), #2 cases whose names are in the database - ("2MASS J07222760-0540384",110.6149995,-5.677333), - ("ULAS J000734.90+011247.1", 1.8957, 1.2132) + ("2MASS J07222760-0540384",110.6149995,-5.677333, true), + ("ULAS J000734.90+011247.1", 1.8957, 1.2132, true) ]) -def test_simbad_resolvable_names(input,ra,dec): +def test_simbad_resolvable_names(input,ra,dec,expected): result = simbad_name_resolvable(input, ra, dec) - isResolvable = False - print(result) - if input in result[1]: - isResolvable = True - print(isResolvable) - assert isResolvable == result[0] + assert result == expected From 5c36a443b34c39fee5ec3b7b73c952553816b5ad Mon Sep 17 00:00:00 2001 From: lesliech1004 Date: Wed, 23 Jul 2025 19:52:40 -0400 Subject: [PATCH 05/10] Integrate into ingest_sources and ingest_names. Edit test cases --- astrodb_utils/sources.py | 28 +++++++++++++++++++++++++++- tests/test_sources.py | 10 +++++----- 2 files changed, 32 insertions(+), 6 deletions(-) diff --git a/astrodb_utils/sources.py b/astrodb_utils/sources.py index 0a84051..ace0d7b 100644 --- a/astrodb_utils/sources.py +++ b/astrodb_utils/sources.py @@ -240,7 +240,7 @@ def simbad_name_resolvable(source, ra, dec): # NAMES def ingest_name( - db, source: str = None, other_name: str = None, raise_error: bool = None + db, source: str = None, other_name: str = None, raise_error: bool = None, ra: float = None, dec: float = None ): """ This function ingests an other name into the Names table @@ -255,6 +255,10 @@ def ingest_name( Name of the source different than that found in source table raise_error: bool Raise an error if name was not ingested + ra: float + Right ascensions of sources. Decimal degrees. + dec: float + Declinations of sources. Decimal degrees. Returns ------- @@ -266,6 +270,17 @@ def ingest_name( """ source = strip_unicode_dashes(source) other_name = strip_unicode_dashes(other_name) + #check if name is resolvable in SIMBAD + logger.debug(f"{source}: Checking if name is resolvable in SIMBAD") + resolvable = simbad_name_resolvable(source = source, ra = ra, dec = dec) + if not resolvable: + msg1= f"{source} not resolvable in SIMBAD." + msg2 = f"Some alternative names for {source}: {resolvable[1]}" + exit_function(msg1+ msg2, raise_error) + else: + logger.info(f"{source} is resolvable in SIMBAD.") + + name_data = [{"source": source, "other_name": other_name}] try: with db.engine.connect() as conn: @@ -357,6 +372,17 @@ def ingest_source( ) exit_function(msg, raise_error) return + + #Check if source name is resolvable in SIMBAD + logger.debug(f"{source}: Checking if name is resolvable in SIMBAD") + resolvable = simbad_name_resolvable(source = source, ra = ra, dec = dec) + if not resolvable: + msg1= f"{source} not resolvable in SIMBAD." + msg2 = f"Some alternative names for {source}: {resolvable[1]}" + exit_function(msg1+ msg2, raise_error) + else: + logger.info(f"{source} is resolvable in SIMBAD.") + # Find out if source is already in database or not if search_db: diff --git a/tests/test_sources.py b/tests/test_sources.py index 18b2cc4..357c2c6 100644 --- a/tests/test_sources.py +++ b/tests/test_sources.py @@ -266,14 +266,14 @@ def test_strip_unicode_dashes(input, expected): @pytest.mark.parametrize('input,ra, dec, expected', [ #2 cases whose names are NOT in the database - ("Apple", 144.395292,29.528028, false), - ("Banana", 3.9888,4.2511, false), + ("Apple", 144.395292,29.528028, False), + ("Banana", 3.9888,4.2511, False), #2 cases whose names are in the database - ("2MASS J07222760-0540384",110.6149995,-5.677333, true), - ("ULAS J000734.90+011247.1", 1.8957, 1.2132, true) + ("2MASS J07222760-0540384",110.6149995,-5.677333, True), + ("ULAS J000734.90+011247.1", 1.8957, 1.2132, True) ]) def test_simbad_resolvable_names(input,ra,dec,expected): - result = simbad_name_resolvable(input, ra, dec) + result = simbad_name_resolvable(input, ra, dec)[0] assert result == expected From 17b62a9c15a01251a582162985e152b483b258d4 Mon Sep 17 00:00:00 2001 From: lesliech1004 Date: Fri, 25 Jul 2025 16:03:23 -0400 Subject: [PATCH 06/10] documentation --- astrodb_utils/sources.py | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/astrodb_utils/sources.py b/astrodb_utils/sources.py index ace0d7b..6f70dbf 100644 --- a/astrodb_utils/sources.py +++ b/astrodb_utils/sources.py @@ -212,6 +212,34 @@ def coords_from_simbad(source): return simbad_skycoord def simbad_name_resolvable(source, ra, dec): + """ + Checks whether a given astronomical source name is resolvable in the SIMBAD database, + and retrieves alternate resolvable names at the specified coordinates. + + Parameters: + ---------- + source : str + The name of the astronomical source to check in SIMBAD. + ra : float + Right ascension of the source (in degrees). + dec : float + Declination of the source (in degrees). + + Returns: + ------- + simbad_resolvable : bool + True if the provided source name matches an entry in SIMBAD; False otherwise. + alternate_names : list of str + List of alternate SIMBAD-resolvable names found at the given coordinates. + + Notes: + ----- + - Uses `Simbad.query_object()` to determine if the source name is directly resolvable. + - If successful, compares returned `main_id` and `matched_id` to the original source name. + - Uses `Simbad.query_region()` to identify alternate names at the specified coordinates. + - Logs debug information for traceability and warnings when resolution fails. + """ + #check name of match is SIMBAD resolvable simbad_resolvable = False #search SIMBAD for the source using its name @@ -219,6 +247,7 @@ def simbad_name_resolvable(source, ra, dec): if simbad_result_table is None: #name is not resolvable or not in SIMBAD database logger.debug(f"SIMBAD returned no results for {source}") + elif len(simbad_result_table) == 1: logger.debug( f"simbad colnames: {simbad_result_table.colnames} \n simbad results \n {simbad_result_table}" From e7f503b07dead6d3f51751ade5a6028d6a8d0c29 Mon Sep 17 00:00:00 2001 From: lesliech1004 Date: Fri, 25 Jul 2025 16:05:45 -0400 Subject: [PATCH 07/10] add use_simbad flag into ingest_names --- astrodb_utils/sources.py | 24 ++++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/astrodb_utils/sources.py b/astrodb_utils/sources.py index 6f70dbf..c4aea1a 100644 --- a/astrodb_utils/sources.py +++ b/astrodb_utils/sources.py @@ -269,7 +269,7 @@ def simbad_name_resolvable(source, ra, dec): # NAMES def ingest_name( - db, source: str = None, other_name: str = None, raise_error: bool = None, ra: float = None, dec: float = None + db, source: str = None, other_name: str = None, raise_error: bool = None, ra: float = None, dec: float = None, use_simbad: bool = True ): """ This function ingests an other name into the Names table @@ -288,6 +288,9 @@ def ingest_name( Right ascensions of sources. Decimal degrees. dec: float Declinations of sources. Decimal degrees. + use_simbad: bool + True (default): Use Simbad to resolve the source name if it is not found in the database + False: Do not use Simbad to resolve the source name. Or when internet is unavailable Returns ------- @@ -299,15 +302,16 @@ def ingest_name( """ source = strip_unicode_dashes(source) other_name = strip_unicode_dashes(other_name) - #check if name is resolvable in SIMBAD - logger.debug(f"{source}: Checking if name is resolvable in SIMBAD") - resolvable = simbad_name_resolvable(source = source, ra = ra, dec = dec) - if not resolvable: - msg1= f"{source} not resolvable in SIMBAD." - msg2 = f"Some alternative names for {source}: {resolvable[1]}" - exit_function(msg1+ msg2, raise_error) - else: - logger.info(f"{source} is resolvable in SIMBAD.") + if use_simbad: + #check if name is resolvable in SIMBAD + logger.debug(f"{source}: Checking if name is resolvable in SIMBAD") + resolvable = simbad_name_resolvable(source = source, ra = ra, dec = dec) + if not resolvable: + msg1= f"{source} not resolvable in SIMBAD." + msg2 = f"Some alternative names for {source}: {resolvable[1]}" + exit_function(msg1+ msg2, raise_error) + else: + logger.info(f"{source} is resolvable in SIMBAD.") name_data = [{"source": source, "other_name": other_name}] From 0af1dfda40daf7e9b4d027381b015e5e48c92ba6 Mon Sep 17 00:00:00 2001 From: lesliech1004 <160793895+lesliech1004@users.noreply.github.com> Date: Sat, 26 Jul 2025 20:44:59 -0400 Subject: [PATCH 08/10] Update astrodb_utils/sources.py Co-authored-by: Kelle Cruz --- astrodb_utils/sources.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/astrodb_utils/sources.py b/astrodb_utils/sources.py index c4aea1a..501d865 100644 --- a/astrodb_utils/sources.py +++ b/astrodb_utils/sources.py @@ -269,7 +269,7 @@ def simbad_name_resolvable(source, ra, dec): # NAMES def ingest_name( - db, source: str = None, other_name: str = None, raise_error: bool = None, ra: float = None, dec: float = None, use_simbad: bool = True + db, source: str = None, other_name: str = None, raise_error: bool = None, use_simbad: bool = True ): """ This function ingests an other name into the Names table From cec366ab41bb82f238c1554e7efecec3f4ab4cef Mon Sep 17 00:00:00 2001 From: lesliech1004 <160793895+lesliech1004@users.noreply.github.com> Date: Sat, 26 Jul 2025 20:45:06 -0400 Subject: [PATCH 09/10] Update astrodb_utils/sources.py Co-authored-by: Kelle Cruz --- astrodb_utils/sources.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/astrodb_utils/sources.py b/astrodb_utils/sources.py index 501d865..95cfa6d 100644 --- a/astrodb_utils/sources.py +++ b/astrodb_utils/sources.py @@ -305,10 +305,10 @@ def ingest_name( if use_simbad: #check if name is resolvable in SIMBAD logger.debug(f"{source}: Checking if name is resolvable in SIMBAD") - resolvable = simbad_name_resolvable(source = source, ra = ra, dec = dec) + resolvable = simbad_name_resolvable(source = source) if not resolvable: msg1= f"{source} not resolvable in SIMBAD." - msg2 = f"Some alternative names for {source}: {resolvable[1]}" + msg2 = f"Use the simbad_name_resolvable function to find possible names" exit_function(msg1+ msg2, raise_error) else: logger.info(f"{source} is resolvable in SIMBAD.") From 89d8d98e2aa43aec6a56002aac99e85d2cd4aa32 Mon Sep 17 00:00:00 2001 From: lesliech1004 Date: Sat, 26 Jul 2025 21:42:06 -0400 Subject: [PATCH 10/10] fixed white space error and added expected names to test --- tests/test_sources.py | 47 +++++++++++++++++++++++++++++++++++-------- 1 file changed, 39 insertions(+), 8 deletions(-) diff --git a/tests/test_sources.py b/tests/test_sources.py index 357c2c6..2f326fb 100644 --- a/tests/test_sources.py +++ b/tests/test_sources.py @@ -1,4 +1,5 @@ import math +import re import astropy.units as u import pytest @@ -264,16 +265,46 @@ def test_strip_unicode_dashes(input, expected): result = strip_unicode_dashes(input) assert result == expected -@pytest.mark.parametrize('input,ra, dec, expected', [ +@pytest.mark.parametrize('input,ra, dec, expected, expected_names', [ #2 cases whose names are NOT in the database - ("Apple", 144.395292,29.528028, False), - ("Banana", 3.9888,4.2511, False), + ("Apple", 144.395292,29.528028, False, + ["2MASSI J0937347+293142", "WISE J093742.35+293220.7"]), + + ("Banana", 3.9888,4.2511, False, + ["HD 1160", "HD 1160B", "HD 1160C", "TYC 5-669-1"]), + #2 cases whose names are in the database - ("2MASS J07222760-0540384",110.6149995,-5.677333, True), - ("ULAS J000734.90+011247.1", 1.8957, 1.2132, True) + ("2MASS J07222760-0540384",110.6149995,-5.677333, True, + ["2MASS J07222760-0540384", + "[BGM2011b] J072227.56-054034.4", + "[BGM2011b] J072227.79-054034.7", + "[BGM2011b] J072227.64-054033.0", + "WISE J072227.27-054029.9", + "[BGM2011b] J072227.02-054039.2", + "[BGM2011b] J072227.28-054030.0", + "[BGM2011b] J072227.34-054026.8", + "[BGM2011b] J072226.97-054029.4", + "UCAC3 169-73338", + "TYC 4829-156-1"]), + + ("ULAS J000734.90+011247.1", 1.8957, 1.2132, True, + ["ULAS J000734.90+011247.1", + "2MASS J00073939+0113049", + "SDSS J000741.43+011209.4", + "SDSS J000729.23+011346.4", + "2MASS J00073597+0110548"]) ]) -def test_simbad_resolvable_names(input,ra,dec,expected): - result = simbad_name_resolvable(input, ra, dec)[0] - assert result == expected +def test_simbad_resolvable_names(input,ra,dec,expected, expected_names): + result = simbad_name_resolvable(input, ra, dec) + + #SIMBAD returns actual names with weird white spaces... so remove all white spaces from both lists of names + # Remove all spaces from actual names + actual_names_list = set([re.sub(r'\s+', '', str(name)) for name in result[1]]) + + # Remove all spaces from expected names + expected_names_list = set([re.sub(r'\s+', '', name) for name in expected_names]) + + assert result[0] == expected + assert actual_names_list == expected_names_list