Edit on GitHub

utils.databases.simbad

Getting data from SIMBAD astronomical database.

  1"""
  2Getting data from [SIMBAD](https://simbad.u-strasbg.fr/simbad/)
  3astronomical database.
  4"""
  5
  6from astroquery.simbad import Simbad
  7from astroquery import __version__ as astroqueryVersion  # noqa: F401
  8import re
  9
 10from typing import Optional, Any, List
 11
 12from ..logs.log import logger
 13from ..databases import tap
 14
 15
 16def findIdentificatorFromAnotherCatalogue(
 17    starName: str,
 18    otherIDname: str,
 19    otherIDversion: Optional[str] = None,
 20    withoutIDprefix: bool = True
 21) -> Optional[str]:
 22    """
 23    Finds object identificator from a particular catalogue. SIMBAD returns
 24    a list of identificators prepended with the catalogue name, and this
 25    function finds the identificator for the specified catalogue and returns
 26    just the identificator. In addition to that, some catalogues have versions,
 27    so it is possible to specify which one if of interest.
 28
 29    For instance, if you need to find the identificator for `TWA 20` star
 30    in `Gaia` catalog version `DR3`, then SIMBAD will return
 31    `Gaia DR3 6132146982868270976` value for it, out of which this function
 32    will extract `6132146982868270976` as the result.
 33
 34    Example:
 35
 36    ``` py
 37    from phab.utils.databases import simbad
 38
 39    otherID = simbad.findIdentificatorFromAnotherCatalogue(
 40        "TWA 20",
 41        "gaia",
 42        "dr3"
 43    )
 44    print(otherID)
 45    ```
 46    """
 47    otherID = None
 48
 49    otherIDs = Simbad.query_objectids(starName)
 50    if otherIDs is None:
 51        logger.warning(
 52            " ".join((
 53                "SIMBAD database doesn't have information",
 54                f"about [{starName}]"
 55            ))
 56        )
 57    else:
 58        logger.debug(f"Checking SIMBAD IDs for [{starName}]:")
 59
 60        # before astroquery version 0.4.8 this table had
 61        # an upper-cased `ID` column key, but starting
 62        # with version 0.4.8 it is now lower-cased `id`
 63        #
 64        # https://github.com/astropy/astropy/issues/17695
 65        idColumnKey: str = "ID"
 66        # or compare `astroquery.__version__` with `0.4.7`
 67        if idColumnKey not in otherIDs.colnames:
 68            logger.debug(
 69                " ".join((
 70                    "There is no upper-cased [ID] key",
 71                    "in the resulting table, will try",
 72                    "with lower-cased [id] key"
 73                ))
 74            )
 75            idColumnKey = idColumnKey.lower()  # "id"
 76            if idColumnKey not in otherIDs.colnames:
 77                errorMsg = " ".join((
 78                    "SIMBAD results table has neither [ID]",
 79                    "nor [id] column"
 80                ))
 81                logger.error(errorMsg)
 82                if len(otherIDs.colnames) > 0:
 83                    logger.debug(
 84                        " ".join((
 85                            "Here are all the columns/keys",
 86                            "in this table:",
 87                            ", ".join(otherIDs.colnames)
 88                        ))
 89                    )
 90                else:
 91                    logger.debug(
 92                        " ".join((
 93                            "There are no columns/keys",
 94                            "in this table at all"
 95                        ))
 96                    )
 97                raise KeyError(errorMsg)
 98
 99        for oid in otherIDs:
100            idCandidate: str = oid[idColumnKey]
101            logger.debug(f"- {idCandidate}")
102            if otherIDname.lower() in idCandidate.lower():
103                idToLookFor = (
104                    f"{otherIDname} {otherIDversion}"
105                    if otherIDversion else otherIDname
106                )
107                if idToLookFor.lower() in idCandidate.lower():
108                    if withoutIDprefix:
109                        prefixRE = re.compile(
110                            rf"{idToLookFor}\s?",
111                            re.IGNORECASE
112                        )
113                        otherID = prefixRE.sub("", idCandidate)
114                    else:
115                        otherID = idCandidate
116                    break
117    return otherID
118
119
120def getObjectID(
121    starName: str,
122    fallbackToLikeInsteadOfEqual: bool = False,
123    problematicIdentifiersPrefixes: List[str] = ["SZ"]
124) -> Optional[int]:
125    """
126    Finds object identificator for
127    [SIMBAD tables](http://simbad.cds.unistra.fr/simbad/tap/tapsearch.html).
128    It is stored in the `oid` field of the `basic` table.
129
130    The discovery process is to compare all the known object identificators
131    with the `main_id` field value (*also from the `basic` table*). It is
132    not clear, how exactly SIMBAD maintainers choose the main ID for an object,
133    so one has to iterate through all the identificators known to SIMBAD.
134
135    ## Some problems with strings
136
137    Due to the (*still unresolved?*) issue(s) in SIMBAD/CDS, some identifiers
138    were (*still are?*) problematic for querying with `main_id` - they might
139    return no results with explicit `=` in `WHERE` clause, but they will
140    return results with `LIKE` instead of `=`, so a workaround/fallback
141    had to be implemented for those. More details
142    [here](https://decovar.dev/blog/2022/02/26/astronomy-databases-tap-adql/#some-problems-with-strings).
143
144    This workaround/fallback is disabled by default, so if you'd like to get
145    those potentially incorrect results, then you can enable it by setting
146    `fallbackToLikeInsteadOfEqual` parameter to `True`.
147
148    Also, the `problematicIdentifiersPrefixes` parameter limits the list
149    of such problematic identifiers, and so far `SZ  *` pattern (*note
150    the two spaces in the original value, which isn't visible here*) seems
151    to be one of those (*for example, `SZ  66`*), so `SZ` prefix is added
152    to the list by default. Obviously, if you set
153    `fallbackToLikeInsteadOfEqual` to `False`, then you don't need to care
154    about this parameter at all.
155
156    Example:
157
158    ``` py
159    from phab.utils.databases import simbad
160    from typing import Optional
161
162    oid: Optional[int] = None
163    try:
164        # no issues with this object identifier
165        oid = simbad.getObjectID(
166            "A2 146",
167            fallbackToLikeInsteadOfEqual=False
168        )
169        # this object has a problematic identifier,
170        # so a fallback might need to be used, if we still want to get its OID
171        #oid = simbad.getObjectID(
172        #    "2MASS J15392828-3446180",  # its `main_id` is `SZ  66`
173        #    fallbackToLikeInsteadOfEqual=True
174        #)
175    except KeyError as ex:
176        print(f"Something wrong with querying SIMBAD. {repr(ex)}")
177    if oid is not None:
178        print(f"Object ID: {oid}")
179    else:
180        print("No results")
181    ```
182    """
183    oid: Optional[int] = None
184
185    # check if this name is already the main ID
186    logger.debug(f"Checking whether [{starName}] is already the main ID")
187    rez = tap.queryService(
188        tap.getServiceEndpoint("simbad"),
189        " ".join((
190            "SELECT oid",
191            "FROM basic",
192            f"WHERE main_id = '{starName}'"
193        ))
194    )
195    if rez:
196        oid = rez[0]["oid"]
197        logger.debug(
198            " ".join((
199                "- yes, that is already the main ID,",
200                "no need to iterate all the identificators.",
201                f"SIMBAD object ID is: {oid}"
202            ))
203        )
204    else:
205        logger.debug(
206            " ".join((
207                "- no, that is not the main ID, will have to iterate",
208                "all the other identificators"
209            ))
210        )
211        ids = Simbad.query_objectids(starName)
212        if ids is None:
213            logger.warning(
214                " ".join((
215                    "SIMBAD database doesn't have information",
216                    f"about [{starName}]"
217                ))
218            )
219        else:
220            logger.debug(f"Checking SIMBAD IDs for [{starName}]:")
221
222            # before astroquery version 0.4.8 this table had
223            # an upper-cased `ID` column key, but starting
224            # with version 0.4.8 it is now lower-cased `id`
225            #
226            # https://github.com/astropy/astropy/issues/17695
227            idColumnKey: str = "ID"
228            # or compare `astroquery.__version__` with `0.4.7`
229            if idColumnKey not in ids.colnames:
230                logger.debug(
231                    " ".join((
232                        "There is no upper-cased [ID] key",
233                        "in the resulting table, will try",
234                        "with lower-cased [id] key"
235                    ))
236                )
237                idColumnKey = idColumnKey.lower()  # "id"
238                if idColumnKey not in ids.colnames:
239                    errorMsg = " ".join((
240                        "SIMBAD results table has neither [ID]",
241                        "nor [id] column"
242                    ))
243                    logger.error(errorMsg)
244                    if len(ids.colnames) > 0:
245                        logger.debug(
246                            " ".join((
247                                "Here are all the columns/keys",
248                                "in this table:",
249                                ", ".join(ids.colnames)
250                            ))
251                        )
252                    else:
253                        logger.debug(
254                            " ".join((
255                                "There are no columns/keys",
256                                "in this table at all"
257                            ))
258                        )
259                    raise KeyError(errorMsg)
260
261            for id in ids:
262                idValue: str = id[idColumnKey]
263                logger.debug(f"- {idValue}")
264
265                if idValue == starName:
266                    logger.debug(
267                        f"...the [{idValue}] has already been tested, skipping"
268                    )
269                    continue
270                rez = tap.queryService(
271                    tap.getServiceEndpoint("simbad"),
272                    " ".join((
273                        "SELECT oid",
274                        "FROM basic",
275                        f"WHERE main_id = '{idValue}'"
276                    ))
277                )
278                if rez:
279                    oid = rez[0]["oid"]
280                    logger.debug(
281                        " ".join((
282                            f"The [{idValue}] is the main ID for",
283                            f"[{starName}], SIMBAD object ID is: {oid}"
284                        ))
285                    )
286                    break
287                else:  # fallback for known problematic identifiers
288                    # database returns identifiers like `Sz  66`,
289                    # but the actual `main_id` field will contain
290                    # all capital `SZ  *`
291                    idValueUppercased = idValue.upper()
292                    if (
293                        fallbackToLikeInsteadOfEqual
294                        and
295                        idValueUppercased.startswith(
296                            tuple(problematicIdentifiersPrefixes)
297                        )
298                        # idValueUppercased.startswith(
299                        #     # a bit of an abomination to uppercase the list
300                        #     tuple(
301                        #         list(
302                        #             map(
303                        #                 str.upper,
304                        #                 problematicIdentifiersPrefixes
305                        #             )
306                        #         )
307                        #     )
308                        # )
309                    ):
310                        logger.debug(
311                            " ".join((
312                                "Did not find SIMBAD object ID for",
313                                f"[{idValue}], but it is a known problematic",
314                                "identifier, so will try a fallback with LIKE"
315                            ))
316                        )
317                        rez = tap.queryService(
318                            tap.getServiceEndpoint("simbad"),
319                            # not sure if `ORDER BY update_date` is correct
320                            # here, but there is already nothing correct about
321                            # using `LIKE` instead of strict `=`, so
322                            " ".join((
323                                "SELECT TOP 1 oid",
324                                "FROM basic",
325                                f"WHERE main_id LIKE '{idValueUppercased}'",
326                                "ORDER BY update_date DESC"
327                            ))
328                        )
329                        if rez:
330                            oid = rez[0]["oid"]
331                            logger.debug(
332                                " ".join((
333                                    f"The [{idValueUppercased}] is the main",
334                                    f"ID for [{starName}], SIMBAD object ID",
335                                    f"is: {oid}"
336                                ))
337                            )
338                            logger.warning(
339                                " ".join((
340                                    "Managed to find the SIMBAD object ID,",
341                                    "but be aware that it was found with",
342                                    "a fallback for problematic identifiers,",
343                                    "which means using LIKE in the WHERE",
344                                    "clause, so the result is not guaranteed",
345                                    "to be correct; and if you would like",
346                                    "to disable this fallback, then set",
347                                    "fallbackToLikeInsteadOfEqual to False"
348                                ))
349                            )
350                            break
351    return oid
352
353
354def getStellarParameter(
355    starName: str,
356    table: str,
357    param: str
358) -> Optional[tuple[Any, str]]:
359    """
360    A convenience function for querying SIMBAD for a stellar parameter:
361
362    1. Finds SIMBAD's object ID by the star name
363    (*with `utils.databases.simbad.getObjectID`*);
364    2. Queries for a stellar parameter by that object ID
365    (*with `utils.databases.tap.getStellarParameterFromSimbadByObjectID`*).
366
367    Example:
368
369    ``` py
370    from phab.utils.databases import simbad
371
372    starName = "PPM 725297"
373    param = "period"
374    rez = simbad.getStellarParameter(
375        starName,
376        "mesVar",
377        param
378    )
379    if rez:
380        val = rez[0]
381        ref = rez[1]
382        print(f"Value: {val}, reference: {ref}")
383    else:
384        print(f"SIMBAD doesn't have data about [{param}] parameter of [{starName}] object")
385    ```
386    """
387    rez: Optional[tuple[Any, str]] = None
388
389    oid = getObjectID(starName)
390    if oid:
391        # logger.debug(
392        #     f"Found the following object ID for [{starName}]: {oid}"
393        # )
394        rez = tap.getStellarParameterFromSimbadByObjectID(
395            oid,
396            table,
397            param
398        )
399
400    return rez
def findIdentificatorFromAnotherCatalogue( starName: str, otherIDname: str, otherIDversion: Optional[str] = None, withoutIDprefix: bool = True) -> Optional[str]:
 17def findIdentificatorFromAnotherCatalogue(
 18    starName: str,
 19    otherIDname: str,
 20    otherIDversion: Optional[str] = None,
 21    withoutIDprefix: bool = True
 22) -> Optional[str]:
 23    """
 24    Finds object identificator from a particular catalogue. SIMBAD returns
 25    a list of identificators prepended with the catalogue name, and this
 26    function finds the identificator for the specified catalogue and returns
 27    just the identificator. In addition to that, some catalogues have versions,
 28    so it is possible to specify which one if of interest.
 29
 30    For instance, if you need to find the identificator for `TWA 20` star
 31    in `Gaia` catalog version `DR3`, then SIMBAD will return
 32    `Gaia DR3 6132146982868270976` value for it, out of which this function
 33    will extract `6132146982868270976` as the result.
 34
 35    Example:
 36
 37    ``` py
 38    from phab.utils.databases import simbad
 39
 40    otherID = simbad.findIdentificatorFromAnotherCatalogue(
 41        "TWA 20",
 42        "gaia",
 43        "dr3"
 44    )
 45    print(otherID)
 46    ```
 47    """
 48    otherID = None
 49
 50    otherIDs = Simbad.query_objectids(starName)
 51    if otherIDs is None:
 52        logger.warning(
 53            " ".join((
 54                "SIMBAD database doesn't have information",
 55                f"about [{starName}]"
 56            ))
 57        )
 58    else:
 59        logger.debug(f"Checking SIMBAD IDs for [{starName}]:")
 60
 61        # before astroquery version 0.4.8 this table had
 62        # an upper-cased `ID` column key, but starting
 63        # with version 0.4.8 it is now lower-cased `id`
 64        #
 65        # https://github.com/astropy/astropy/issues/17695
 66        idColumnKey: str = "ID"
 67        # or compare `astroquery.__version__` with `0.4.7`
 68        if idColumnKey not in otherIDs.colnames:
 69            logger.debug(
 70                " ".join((
 71                    "There is no upper-cased [ID] key",
 72                    "in the resulting table, will try",
 73                    "with lower-cased [id] key"
 74                ))
 75            )
 76            idColumnKey = idColumnKey.lower()  # "id"
 77            if idColumnKey not in otherIDs.colnames:
 78                errorMsg = " ".join((
 79                    "SIMBAD results table has neither [ID]",
 80                    "nor [id] column"
 81                ))
 82                logger.error(errorMsg)
 83                if len(otherIDs.colnames) > 0:
 84                    logger.debug(
 85                        " ".join((
 86                            "Here are all the columns/keys",
 87                            "in this table:",
 88                            ", ".join(otherIDs.colnames)
 89                        ))
 90                    )
 91                else:
 92                    logger.debug(
 93                        " ".join((
 94                            "There are no columns/keys",
 95                            "in this table at all"
 96                        ))
 97                    )
 98                raise KeyError(errorMsg)
 99
100        for oid in otherIDs:
101            idCandidate: str = oid[idColumnKey]
102            logger.debug(f"- {idCandidate}")
103            if otherIDname.lower() in idCandidate.lower():
104                idToLookFor = (
105                    f"{otherIDname} {otherIDversion}"
106                    if otherIDversion else otherIDname
107                )
108                if idToLookFor.lower() in idCandidate.lower():
109                    if withoutIDprefix:
110                        prefixRE = re.compile(
111                            rf"{idToLookFor}\s?",
112                            re.IGNORECASE
113                        )
114                        otherID = prefixRE.sub("", idCandidate)
115                    else:
116                        otherID = idCandidate
117                    break
118    return otherID

Finds object identificator from a particular catalogue. SIMBAD returns a list of identificators prepended with the catalogue name, and this function finds the identificator for the specified catalogue and returns just the identificator. In addition to that, some catalogues have versions, so it is possible to specify which one if of interest.

For instance, if you need to find the identificator for TWA 20 star in Gaia catalog version DR3, then SIMBAD will return Gaia DR3 6132146982868270976 value for it, out of which this function will extract 6132146982868270976 as the result.

Example:

from phab.utils.databases import simbad

otherID = simbad.findIdentificatorFromAnotherCatalogue(
    "TWA 20",
    "gaia",
    "dr3"
)
print(otherID)
def getObjectID( starName: str, fallbackToLikeInsteadOfEqual: bool = False, problematicIdentifiersPrefixes: List[str] = ['SZ']) -> Optional[int]:
121def getObjectID(
122    starName: str,
123    fallbackToLikeInsteadOfEqual: bool = False,
124    problematicIdentifiersPrefixes: List[str] = ["SZ"]
125) -> Optional[int]:
126    """
127    Finds object identificator for
128    [SIMBAD tables](http://simbad.cds.unistra.fr/simbad/tap/tapsearch.html).
129    It is stored in the `oid` field of the `basic` table.
130
131    The discovery process is to compare all the known object identificators
132    with the `main_id` field value (*also from the `basic` table*). It is
133    not clear, how exactly SIMBAD maintainers choose the main ID for an object,
134    so one has to iterate through all the identificators known to SIMBAD.
135
136    ## Some problems with strings
137
138    Due to the (*still unresolved?*) issue(s) in SIMBAD/CDS, some identifiers
139    were (*still are?*) problematic for querying with `main_id` - they might
140    return no results with explicit `=` in `WHERE` clause, but they will
141    return results with `LIKE` instead of `=`, so a workaround/fallback
142    had to be implemented for those. More details
143    [here](https://decovar.dev/blog/2022/02/26/astronomy-databases-tap-adql/#some-problems-with-strings).
144
145    This workaround/fallback is disabled by default, so if you'd like to get
146    those potentially incorrect results, then you can enable it by setting
147    `fallbackToLikeInsteadOfEqual` parameter to `True`.
148
149    Also, the `problematicIdentifiersPrefixes` parameter limits the list
150    of such problematic identifiers, and so far `SZ  *` pattern (*note
151    the two spaces in the original value, which isn't visible here*) seems
152    to be one of those (*for example, `SZ  66`*), so `SZ` prefix is added
153    to the list by default. Obviously, if you set
154    `fallbackToLikeInsteadOfEqual` to `False`, then you don't need to care
155    about this parameter at all.
156
157    Example:
158
159    ``` py
160    from phab.utils.databases import simbad
161    from typing import Optional
162
163    oid: Optional[int] = None
164    try:
165        # no issues with this object identifier
166        oid = simbad.getObjectID(
167            "A2 146",
168            fallbackToLikeInsteadOfEqual=False
169        )
170        # this object has a problematic identifier,
171        # so a fallback might need to be used, if we still want to get its OID
172        #oid = simbad.getObjectID(
173        #    "2MASS J15392828-3446180",  # its `main_id` is `SZ  66`
174        #    fallbackToLikeInsteadOfEqual=True
175        #)
176    except KeyError as ex:
177        print(f"Something wrong with querying SIMBAD. {repr(ex)}")
178    if oid is not None:
179        print(f"Object ID: {oid}")
180    else:
181        print("No results")
182    ```
183    """
184    oid: Optional[int] = None
185
186    # check if this name is already the main ID
187    logger.debug(f"Checking whether [{starName}] is already the main ID")
188    rez = tap.queryService(
189        tap.getServiceEndpoint("simbad"),
190        " ".join((
191            "SELECT oid",
192            "FROM basic",
193            f"WHERE main_id = '{starName}'"
194        ))
195    )
196    if rez:
197        oid = rez[0]["oid"]
198        logger.debug(
199            " ".join((
200                "- yes, that is already the main ID,",
201                "no need to iterate all the identificators.",
202                f"SIMBAD object ID is: {oid}"
203            ))
204        )
205    else:
206        logger.debug(
207            " ".join((
208                "- no, that is not the main ID, will have to iterate",
209                "all the other identificators"
210            ))
211        )
212        ids = Simbad.query_objectids(starName)
213        if ids is None:
214            logger.warning(
215                " ".join((
216                    "SIMBAD database doesn't have information",
217                    f"about [{starName}]"
218                ))
219            )
220        else:
221            logger.debug(f"Checking SIMBAD IDs for [{starName}]:")
222
223            # before astroquery version 0.4.8 this table had
224            # an upper-cased `ID` column key, but starting
225            # with version 0.4.8 it is now lower-cased `id`
226            #
227            # https://github.com/astropy/astropy/issues/17695
228            idColumnKey: str = "ID"
229            # or compare `astroquery.__version__` with `0.4.7`
230            if idColumnKey not in ids.colnames:
231                logger.debug(
232                    " ".join((
233                        "There is no upper-cased [ID] key",
234                        "in the resulting table, will try",
235                        "with lower-cased [id] key"
236                    ))
237                )
238                idColumnKey = idColumnKey.lower()  # "id"
239                if idColumnKey not in ids.colnames:
240                    errorMsg = " ".join((
241                        "SIMBAD results table has neither [ID]",
242                        "nor [id] column"
243                    ))
244                    logger.error(errorMsg)
245                    if len(ids.colnames) > 0:
246                        logger.debug(
247                            " ".join((
248                                "Here are all the columns/keys",
249                                "in this table:",
250                                ", ".join(ids.colnames)
251                            ))
252                        )
253                    else:
254                        logger.debug(
255                            " ".join((
256                                "There are no columns/keys",
257                                "in this table at all"
258                            ))
259                        )
260                    raise KeyError(errorMsg)
261
262            for id in ids:
263                idValue: str = id[idColumnKey]
264                logger.debug(f"- {idValue}")
265
266                if idValue == starName:
267                    logger.debug(
268                        f"...the [{idValue}] has already been tested, skipping"
269                    )
270                    continue
271                rez = tap.queryService(
272                    tap.getServiceEndpoint("simbad"),
273                    " ".join((
274                        "SELECT oid",
275                        "FROM basic",
276                        f"WHERE main_id = '{idValue}'"
277                    ))
278                )
279                if rez:
280                    oid = rez[0]["oid"]
281                    logger.debug(
282                        " ".join((
283                            f"The [{idValue}] is the main ID for",
284                            f"[{starName}], SIMBAD object ID is: {oid}"
285                        ))
286                    )
287                    break
288                else:  # fallback for known problematic identifiers
289                    # database returns identifiers like `Sz  66`,
290                    # but the actual `main_id` field will contain
291                    # all capital `SZ  *`
292                    idValueUppercased = idValue.upper()
293                    if (
294                        fallbackToLikeInsteadOfEqual
295                        and
296                        idValueUppercased.startswith(
297                            tuple(problematicIdentifiersPrefixes)
298                        )
299                        # idValueUppercased.startswith(
300                        #     # a bit of an abomination to uppercase the list
301                        #     tuple(
302                        #         list(
303                        #             map(
304                        #                 str.upper,
305                        #                 problematicIdentifiersPrefixes
306                        #             )
307                        #         )
308                        #     )
309                        # )
310                    ):
311                        logger.debug(
312                            " ".join((
313                                "Did not find SIMBAD object ID for",
314                                f"[{idValue}], but it is a known problematic",
315                                "identifier, so will try a fallback with LIKE"
316                            ))
317                        )
318                        rez = tap.queryService(
319                            tap.getServiceEndpoint("simbad"),
320                            # not sure if `ORDER BY update_date` is correct
321                            # here, but there is already nothing correct about
322                            # using `LIKE` instead of strict `=`, so
323                            " ".join((
324                                "SELECT TOP 1 oid",
325                                "FROM basic",
326                                f"WHERE main_id LIKE '{idValueUppercased}'",
327                                "ORDER BY update_date DESC"
328                            ))
329                        )
330                        if rez:
331                            oid = rez[0]["oid"]
332                            logger.debug(
333                                " ".join((
334                                    f"The [{idValueUppercased}] is the main",
335                                    f"ID for [{starName}], SIMBAD object ID",
336                                    f"is: {oid}"
337                                ))
338                            )
339                            logger.warning(
340                                " ".join((
341                                    "Managed to find the SIMBAD object ID,",
342                                    "but be aware that it was found with",
343                                    "a fallback for problematic identifiers,",
344                                    "which means using LIKE in the WHERE",
345                                    "clause, so the result is not guaranteed",
346                                    "to be correct; and if you would like",
347                                    "to disable this fallback, then set",
348                                    "fallbackToLikeInsteadOfEqual to False"
349                                ))
350                            )
351                            break
352    return oid

Finds object identificator for SIMBAD tables. It is stored in the oid field of the basic table.

The discovery process is to compare all the known object identificators with the main_id field value (also from the basic table). It is not clear, how exactly SIMBAD maintainers choose the main ID for an object, so one has to iterate through all the identificators known to SIMBAD.

Some problems with strings

Due to the (still unresolved?) issue(s) in SIMBAD/CDS, some identifiers were (still are?) problematic for querying with main_id - they might return no results with explicit = in WHERE clause, but they will return results with LIKE instead of =, so a workaround/fallback had to be implemented for those. More details here.

This workaround/fallback is disabled by default, so if you'd like to get those potentially incorrect results, then you can enable it by setting fallbackToLikeInsteadOfEqual parameter to True.

Also, the problematicIdentifiersPrefixes parameter limits the list of such problematic identifiers, and so far SZ * pattern (note the two spaces in the original value, which isn't visible here) seems to be one of those (for example, SZ 66), so SZ prefix is added to the list by default. Obviously, if you set fallbackToLikeInsteadOfEqual to False, then you don't need to care about this parameter at all.

Example:

from phab.utils.databases import simbad
from typing import Optional

oid: Optional[int] = None
try:
    # no issues with this object identifier
    oid = simbad.getObjectID(
        "A2 146",
        fallbackToLikeInsteadOfEqual=False
    )
    # this object has a problematic identifier,
    # so a fallback might need to be used, if we still want to get its OID
    #oid = simbad.getObjectID(
    #    "2MASS J15392828-3446180",  # its `main_id` is `SZ  66`
    #    fallbackToLikeInsteadOfEqual=True
    #)
except KeyError as ex:
    print(f"Something wrong with querying SIMBAD. {repr(ex)}")
if oid is not None:
    print(f"Object ID: {oid}")
else:
    print("No results")
def getStellarParameter(starName: str, table: str, param: str) -> Optional[tuple[Any, str]]:
355def getStellarParameter(
356    starName: str,
357    table: str,
358    param: str
359) -> Optional[tuple[Any, str]]:
360    """
361    A convenience function for querying SIMBAD for a stellar parameter:
362
363    1. Finds SIMBAD's object ID by the star name
364    (*with `utils.databases.simbad.getObjectID`*);
365    2. Queries for a stellar parameter by that object ID
366    (*with `utils.databases.tap.getStellarParameterFromSimbadByObjectID`*).
367
368    Example:
369
370    ``` py
371    from phab.utils.databases import simbad
372
373    starName = "PPM 725297"
374    param = "period"
375    rez = simbad.getStellarParameter(
376        starName,
377        "mesVar",
378        param
379    )
380    if rez:
381        val = rez[0]
382        ref = rez[1]
383        print(f"Value: {val}, reference: {ref}")
384    else:
385        print(f"SIMBAD doesn't have data about [{param}] parameter of [{starName}] object")
386    ```
387    """
388    rez: Optional[tuple[Any, str]] = None
389
390    oid = getObjectID(starName)
391    if oid:
392        # logger.debug(
393        #     f"Found the following object ID for [{starName}]: {oid}"
394        # )
395        rez = tap.getStellarParameterFromSimbadByObjectID(
396            oid,
397            table,
398            param
399        )
400
401    return rez

A convenience function for querying SIMBAD for a stellar parameter:

  1. Finds SIMBAD's object ID by the star name (with utils.databases.simbad.getObjectID);
  2. Queries for a stellar parameter by that object ID (with utils.databases.tap.getStellarParameterFromSimbadByObjectID).

Example:

from phab.utils.databases import simbad

starName = "PPM 725297"
param = "period"
rez = simbad.getStellarParameter(
    starName,
    "mesVar",
    param
)
if rez:
    val = rez[0]
    ref = rez[1]
    print(f"Value: {val}, reference: {ref}")
else:
    print(f"SIMBAD doesn't have data about [{param}] parameter of [{starName}] object")