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
  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 = True,
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    Due to the (*still unresolved?*) issue(s) in SIMBAD/CDS, some identifiers
136    are problematic for querying with `main_id` - they return no results
137    with explicit `=` in `WHERE` clause, but they do return results
138    with `LIKE` instead of `=`, so a workaround/fallback had to be implemented
139    for those. This workaround/fallback is enabled by default, and if you don't
140    want these potentially incorrect results to "poison" you data, then you can
141    disable it by setting `fallbackToLikeInsteadOfEqual` parameter to `False`.
142    Also, the `problematicIdentifiersPrefixes` parameter limits the list
143    of such problematic identifiers, and so far `SZ  *` pattern (*note
144    the two spaces in the original value, which isn't visible here*) seems
145    to be one of those (*for example, `SZ  66`*), so `SZ` prefix is added
146    to the list by default. Obviously, if you set
147    `fallbackToLikeInsteadOfEqual` to `False`, then you don't need to care
148    about this parameter at all.
149
150    Example:
151
152    ``` py
153    from phab.utils.databases import simbad
154    from typing import Optional
155
156    oid: Optional[int] = None
157    try:
158        # no issues with this object identifier
159        oid = simbad.getObjectID(
160            "A2 146",
161            fallbackToLikeInsteadOfEqual=False
162        )
163        # this object has a problematic identifier,
164        # so a fallback has to be used, if we still want to get its OID
165        #oid = simbad.getObjectID(
166        #    "2MASS J15392828-3446180",  # its `main_id` is `SZ  66`
167        #    fallbackToLikeInsteadOfEqual=True
168        #)
169    except KeyError as ex:
170        print(f"Something wrong with querying SIMBAD. {repr(ex)}")
171    if oid is not None:
172        print(f"Object ID: {oid}")
173    else:
174        print("No results")
175    ```
176    """
177    oid: Optional[int] = None
178
179    # check if this name is already the main ID
180    logger.debug(f"Checking whether [{starName}] is already the main ID")
181    rez = tap.queryService(
182        tap.getServiceEndpoint("simbad"),
183        " ".join((
184            "SELECT oid",
185            "FROM basic",
186            f"WHERE main_id = '{starName}'"
187        ))
188    )
189    if rez:
190        oid = rez[0]["oid"]
191        logger.debug(
192            " ".join((
193                f"- yes, that is already the main ID,",
194                "no need to iterate all the identificators.",
195                f"SIMBAD object ID is: {oid}"
196            ))
197        )
198    else:
199        logger.debug(
200            " ".join((
201                "- no, that is not the main ID, will have to iterate",
202                "all the other identificators"
203            ))
204        )
205        ids = Simbad.query_objectids(starName)
206        if ids is None:
207            logger.warning(
208                " ".join((
209                    "SIMBAD database doesn't have information",
210                    f"about [{starName}]"
211                ))
212            )
213        else:
214            logger.debug(f"Checking SIMBAD IDs for [{starName}]:")
215
216            # before astroquery version 0.4.8 this table had
217            # an upper-cased `ID` column key, but starting
218            # with version 0.4.8 it is now lower-cased `id`
219            #
220            # https://github.com/astropy/astropy/issues/17695
221            idColumnKey: str = "ID"
222            # or compare `astroquery.__version__` with `0.4.7`
223            if idColumnKey not in ids.colnames:
224                logger.debug(
225                    " ".join((
226                        "There is no upper-cased [ID] key",
227                        "in the resulting table, will try",
228                        "with lower-cased [id] key"
229                    ))
230                )
231                idColumnKey = idColumnKey.lower()  # "id"
232                if idColumnKey not in ids.colnames:
233                    errorMsg = " ".join((
234                        "SIMBAD results table has neither [ID]",
235                        "nor [id] column"
236                    ))
237                    logger.error(errorMsg)
238                    if len(ids.colnames) > 0:
239                        logger.debug(
240                            " ".join((
241                                "Here are all the columns/keys",
242                                "in this table:",
243                                ", ".join(ids.colnames)
244                            ))
245                        )
246                    else:
247                        logger.debug(
248                            " ".join((
249                                "There are no columns/keys",
250                                "in this table at all"
251                            ))
252                        )
253                    raise KeyError(errorMsg)
254
255            for id in ids:
256                idValue: str = id[idColumnKey]
257                logger.debug(f"- {idValue}")
258
259                if idValue == starName:
260                    logger.debug(
261                        f"...the [{idValue}] has already been tested, skipping"
262                    )
263                    continue
264                rez = tap.queryService(
265                    tap.getServiceEndpoint("simbad"),
266                    " ".join((
267                        "SELECT oid",
268                        "FROM basic",
269                        f"WHERE main_id = '{idValue}'"
270                    ))
271                )
272                if rez:
273                    oid = rez[0]["oid"]
274                    logger.debug(
275                        " ".join((
276                            f"The [{idValue}] is the main ID for",
277                            f"[{starName}], SIMBAD object ID is: {oid}"
278                        ))
279                    )
280                    break
281                else:  # fallback for known problematic identifiers
282                    # database returns identifiers like `Sz  66`,
283                    # but the actual `main_id` field will contain
284                    # all capital `SZ  *`
285                    idValueUppercased = idValue.upper()
286                    if (
287                        fallbackToLikeInsteadOfEqual
288                        and
289                        idValueUppercased.startswith(
290                            tuple(problematicIdentifiersPrefixes)
291                        )
292                        # idValueUppercased.startswith(
293                        #     # a bit of an abomination to uppercase the list
294                        #     tuple(
295                        #         list(
296                        #             map(
297                        #                 str.upper,
298                        #                 problematicIdentifiersPrefixes
299                        #             )
300                        #         )
301                        #     )
302                        # )
303                    ):
304                        logger.debug(
305                            " ".join((
306                                "Did not find SIMBAD object ID for",
307                                f"[{idValue}], but it is a known problematic",
308                                "identifier, so will try a fallback with LIKE"
309                            ))
310                        )
311                        rez = tap.queryService(
312                            tap.getServiceEndpoint("simbad"),
313                            # not sure if `ORDER BY update_date` is correct
314                            # here, but there is already nothing correct about
315                            # using `LIKE` instead of strict `=`, so
316                            " ".join((
317                                "SELECT TOP 1 oid",
318                                "FROM basic",
319                                f"WHERE main_id LIKE '{idValueUppercased}'",
320                                "ORDER BY update_date DESC"
321                            ))
322                        )
323                        if rez:
324                            oid = rez[0]["oid"]
325                            logger.debug(
326                                " ".join((
327                                    f"The [{idValueUppercased}] is the main",
328                                    f"ID for [{starName}], SIMBAD object ID",
329                                    f"is: {oid}"
330                                ))
331                            )
332                            logger.warning(
333                                " ".join((
334                                    "Managed to find the SIMBAD object ID,",
335                                    "but be aware that it was found with",
336                                    "a fallback for problematic identifiers,",
337                                    "which means using LIKE in the WHERE",
338                                    "clause, so the result is not guaranteed",
339                                    "to be correct; and if you would like",
340                                    "to disable this fallback, then set",
341                                    "fallbackToLikeInsteadOfEqual to False"
342                                ))
343                            )
344                            break
345    return oid
346
347
348def getStellarParameter(
349    starName: str,
350    table: str,
351    param: str
352) -> Optional[tuple[Any, str]]:
353    """
354    A convenience function for querying SIMBAD for a stellar parameter:
355
356    1. Finds SIMBAD's object ID by the star name
357    (*with `utils.databases.simbad.getObjectID`*);
358    2. Queries for a stellar parameter by that object ID
359    (*with `utils.databases.tap.getStellarParameterFromSimbadByObjectID`*).
360
361    Example:
362
363    ``` py
364    from phab.utils.databases import simbad
365
366    starName = "PPM 725297"
367    param = "period"
368    rez = simbad.getStellarParameter(
369        starName,
370        "mesVar",
371        param
372    )
373    if rez:
374        val = rez[0]
375        ref = rez[1]
376        print(f"Value: {val}, reference: {ref}")
377    else:
378        print(f"SIMBAD doesn't have data about [{param}] parameter of [{starName}] object")
379    ```
380    """
381    rez: Optional[tuple[Any, str]] = None
382
383    oid = getObjectID(starName)
384    if oid:
385        # logger.debug(
386        #     f"Found the following object ID for [{starName}]: {oid}"
387        # )
388        rez = tap.getStellarParameterFromSimbadByObjectID(
389            oid,
390            table,
391            param
392        )
393
394    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 = True, problematicIdentifiersPrefixes: List[str] = ['SZ']) -> Optional[int]:
121def getObjectID(
122    starName: str,
123    fallbackToLikeInsteadOfEqual: bool = True,
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    Due to the (*still unresolved?*) issue(s) in SIMBAD/CDS, some identifiers
137    are problematic for querying with `main_id` - they return no results
138    with explicit `=` in `WHERE` clause, but they do return results
139    with `LIKE` instead of `=`, so a workaround/fallback had to be implemented
140    for those. This workaround/fallback is enabled by default, and if you don't
141    want these potentially incorrect results to "poison" you data, then you can
142    disable it by setting `fallbackToLikeInsteadOfEqual` parameter to `False`.
143    Also, the `problematicIdentifiersPrefixes` parameter limits the list
144    of such problematic identifiers, and so far `SZ  *` pattern (*note
145    the two spaces in the original value, which isn't visible here*) seems
146    to be one of those (*for example, `SZ  66`*), so `SZ` prefix is added
147    to the list by default. Obviously, if you set
148    `fallbackToLikeInsteadOfEqual` to `False`, then you don't need to care
149    about this parameter at all.
150
151    Example:
152
153    ``` py
154    from phab.utils.databases import simbad
155    from typing import Optional
156
157    oid: Optional[int] = None
158    try:
159        # no issues with this object identifier
160        oid = simbad.getObjectID(
161            "A2 146",
162            fallbackToLikeInsteadOfEqual=False
163        )
164        # this object has a problematic identifier,
165        # so a fallback has to be used, if we still want to get its OID
166        #oid = simbad.getObjectID(
167        #    "2MASS J15392828-3446180",  # its `main_id` is `SZ  66`
168        #    fallbackToLikeInsteadOfEqual=True
169        #)
170    except KeyError as ex:
171        print(f"Something wrong with querying SIMBAD. {repr(ex)}")
172    if oid is not None:
173        print(f"Object ID: {oid}")
174    else:
175        print("No results")
176    ```
177    """
178    oid: Optional[int] = None
179
180    # check if this name is already the main ID
181    logger.debug(f"Checking whether [{starName}] is already the main ID")
182    rez = tap.queryService(
183        tap.getServiceEndpoint("simbad"),
184        " ".join((
185            "SELECT oid",
186            "FROM basic",
187            f"WHERE main_id = '{starName}'"
188        ))
189    )
190    if rez:
191        oid = rez[0]["oid"]
192        logger.debug(
193            " ".join((
194                f"- yes, that is already the main ID,",
195                "no need to iterate all the identificators.",
196                f"SIMBAD object ID is: {oid}"
197            ))
198        )
199    else:
200        logger.debug(
201            " ".join((
202                "- no, that is not the main ID, will have to iterate",
203                "all the other identificators"
204            ))
205        )
206        ids = Simbad.query_objectids(starName)
207        if ids is None:
208            logger.warning(
209                " ".join((
210                    "SIMBAD database doesn't have information",
211                    f"about [{starName}]"
212                ))
213            )
214        else:
215            logger.debug(f"Checking SIMBAD IDs for [{starName}]:")
216
217            # before astroquery version 0.4.8 this table had
218            # an upper-cased `ID` column key, but starting
219            # with version 0.4.8 it is now lower-cased `id`
220            #
221            # https://github.com/astropy/astropy/issues/17695
222            idColumnKey: str = "ID"
223            # or compare `astroquery.__version__` with `0.4.7`
224            if idColumnKey not in ids.colnames:
225                logger.debug(
226                    " ".join((
227                        "There is no upper-cased [ID] key",
228                        "in the resulting table, will try",
229                        "with lower-cased [id] key"
230                    ))
231                )
232                idColumnKey = idColumnKey.lower()  # "id"
233                if idColumnKey not in ids.colnames:
234                    errorMsg = " ".join((
235                        "SIMBAD results table has neither [ID]",
236                        "nor [id] column"
237                    ))
238                    logger.error(errorMsg)
239                    if len(ids.colnames) > 0:
240                        logger.debug(
241                            " ".join((
242                                "Here are all the columns/keys",
243                                "in this table:",
244                                ", ".join(ids.colnames)
245                            ))
246                        )
247                    else:
248                        logger.debug(
249                            " ".join((
250                                "There are no columns/keys",
251                                "in this table at all"
252                            ))
253                        )
254                    raise KeyError(errorMsg)
255
256            for id in ids:
257                idValue: str = id[idColumnKey]
258                logger.debug(f"- {idValue}")
259
260                if idValue == starName:
261                    logger.debug(
262                        f"...the [{idValue}] has already been tested, skipping"
263                    )
264                    continue
265                rez = tap.queryService(
266                    tap.getServiceEndpoint("simbad"),
267                    " ".join((
268                        "SELECT oid",
269                        "FROM basic",
270                        f"WHERE main_id = '{idValue}'"
271                    ))
272                )
273                if rez:
274                    oid = rez[0]["oid"]
275                    logger.debug(
276                        " ".join((
277                            f"The [{idValue}] is the main ID for",
278                            f"[{starName}], SIMBAD object ID is: {oid}"
279                        ))
280                    )
281                    break
282                else:  # fallback for known problematic identifiers
283                    # database returns identifiers like `Sz  66`,
284                    # but the actual `main_id` field will contain
285                    # all capital `SZ  *`
286                    idValueUppercased = idValue.upper()
287                    if (
288                        fallbackToLikeInsteadOfEqual
289                        and
290                        idValueUppercased.startswith(
291                            tuple(problematicIdentifiersPrefixes)
292                        )
293                        # idValueUppercased.startswith(
294                        #     # a bit of an abomination to uppercase the list
295                        #     tuple(
296                        #         list(
297                        #             map(
298                        #                 str.upper,
299                        #                 problematicIdentifiersPrefixes
300                        #             )
301                        #         )
302                        #     )
303                        # )
304                    ):
305                        logger.debug(
306                            " ".join((
307                                "Did not find SIMBAD object ID for",
308                                f"[{idValue}], but it is a known problematic",
309                                "identifier, so will try a fallback with LIKE"
310                            ))
311                        )
312                        rez = tap.queryService(
313                            tap.getServiceEndpoint("simbad"),
314                            # not sure if `ORDER BY update_date` is correct
315                            # here, but there is already nothing correct about
316                            # using `LIKE` instead of strict `=`, so
317                            " ".join((
318                                "SELECT TOP 1 oid",
319                                "FROM basic",
320                                f"WHERE main_id LIKE '{idValueUppercased}'",
321                                "ORDER BY update_date DESC"
322                            ))
323                        )
324                        if rez:
325                            oid = rez[0]["oid"]
326                            logger.debug(
327                                " ".join((
328                                    f"The [{idValueUppercased}] is the main",
329                                    f"ID for [{starName}], SIMBAD object ID",
330                                    f"is: {oid}"
331                                ))
332                            )
333                            logger.warning(
334                                " ".join((
335                                    "Managed to find the SIMBAD object ID,",
336                                    "but be aware that it was found with",
337                                    "a fallback for problematic identifiers,",
338                                    "which means using LIKE in the WHERE",
339                                    "clause, so the result is not guaranteed",
340                                    "to be correct; and if you would like",
341                                    "to disable this fallback, then set",
342                                    "fallbackToLikeInsteadOfEqual to False"
343                                ))
344                            )
345                            break
346    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.

Due to the (still unresolved?) issue(s) in SIMBAD/CDS, some identifiers are problematic for querying with main_id - they return no results with explicit = in WHERE clause, but they do return results with LIKE instead of =, so a workaround/fallback had to be implemented for those. This workaround/fallback is enabled by default, and if you don't want these potentially incorrect results to "poison" you data, then you can disable it by setting fallbackToLikeInsteadOfEqual parameter to False. 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 has 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]]:
349def getStellarParameter(
350    starName: str,
351    table: str,
352    param: str
353) -> Optional[tuple[Any, str]]:
354    """
355    A convenience function for querying SIMBAD for a stellar parameter:
356
357    1. Finds SIMBAD's object ID by the star name
358    (*with `utils.databases.simbad.getObjectID`*);
359    2. Queries for a stellar parameter by that object ID
360    (*with `utils.databases.tap.getStellarParameterFromSimbadByObjectID`*).
361
362    Example:
363
364    ``` py
365    from phab.utils.databases import simbad
366
367    starName = "PPM 725297"
368    param = "period"
369    rez = simbad.getStellarParameter(
370        starName,
371        "mesVar",
372        param
373    )
374    if rez:
375        val = rez[0]
376        ref = rez[1]
377        print(f"Value: {val}, reference: {ref}")
378    else:
379        print(f"SIMBAD doesn't have data about [{param}] parameter of [{starName}] object")
380    ```
381    """
382    rez: Optional[tuple[Any, str]] = None
383
384    oid = getObjectID(starName)
385    if oid:
386        # logger.debug(
387        #     f"Found the following object ID for [{starName}]: {oid}"
388        # )
389        rez = tap.getStellarParameterFromSimbadByObjectID(
390            oid,
391            table,
392            param
393        )
394
395    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")