Skip to content

lstautorta.observation_data

Functions:

Name Description
get_current_run_info

Queries the LST database for the current observation and observation parameters

get_current_run_info

get_current_run_info(db_hostname, obs_target_query_timeout_s)

Queries the LST database for the current observation and observation parameters

Parameters:

Name Type Description Default
db_hostname str

Hostname of the DB server. Example lst101

required
obs_target_query_timeout_s Annotated[int, Gt(0), Lt(60)]

Maximal amount of time passed to query DB for observation parameters

required

Raises:

Type Description
ExecutionTimeout

When the database query exceeds the timeout.

PyMongoError

When the database query returned inconsistent results between data.camera and data.structure fields

Returns:

Type Description
ObsInfo

Current observation information

Source code in src/lstautorta/observation_data.py
def get_current_run_info(
    db_hostname: str,
    obs_target_query_timeout_s: Annotated[int, Gt(0), Lt(60)],
) -> ObsInfo:
    """Queries the LST database for the current observation and observation parameters

    Parameters
    ----------
    db_hostname : str
        Hostname of the DB server. Example lst101
    obs_target_query_timeout_s : Annotated[int, Gt(0), Lt(60)]
        Maximal amount of time passed to query DB for observation parameters

    Raises
    ------
    pymongo.errors.ExecutionTimeout
        When the database query exceeds the timeout.
    pymongo.errors.PyMongoError
        When the database query returned inconsistent results between data.camera and data.structure fields

    Returns
    -------
    ObsInfo
        Current observation information
    """
    # The "lst1_obs_summary", DB is filled by LST sub-systems with the information they have/use, when they "want"
    # The observation information can be retrieved from the "telescope" collection which is updated with
    # camera and structure data, after a run a started (so we get a delay until the information is available)
    # For the record, the "camera" collection is updated at the end of a run.

    # There can be an issue with the values retrieved from the DB concerning the run_number: the run number
    # is decided by the EVB, and communicating it to the TCU that writes the DB can take longer than the TCU
    # is willing to wait. When this happens, the TCU writes the previous run_number, so there can be duplicated
    # run_number in the DB, and the run_number can be wrong.
    # In addition, the pointing information is not written in the same array than the run_number, therefore we
    # must query for 2 different pieces of the documents.
    # Also, the DB documents contain information for muliple runs (for a complete set of wobbles).
    # To circumvent these issues, we do the following:
    # - query the DB and sort (descending) on the tstart field of both data.structure and data.camera
    #   - limit the query to 1 element, because otherwise the sort takes to much memory.
    #     Doing so allows the sort to only keep in memory the max value
    #     https://www.mongodb.com/docs/manual/reference/operator/aggregation/sort/#-sort-operator-and-memory
    # - sort the retrieved documents by tstart (sorting the info of each wobble run)
    # - assert the two documents are coherent (simply check length). We can't compare tstart because the
    #   tstart of camera is the time at which the camera start, while tstart of structure is the time at which
    #   the drive started tracking, they can be different.
    # - assign the observation information with the most recent values.
    # - iterate on the previous runs information and check that the run_number is different
    #   (in inner for loop: iterate on wobbles, in outer while loop: iterate over previous wobble sets)
    #   If the run_number is different, or obs_id is not duplicated, we can quit. Otherwise, we increment the
    #   value by which we need to shift the obs_id.
    #   - If a document is inserted in the DB while we are iterating, we will get the same "_id" again, so we can
    #     simply ignore it.

    obs_id = None
    tstart_camera = None
    ra = None
    dec = None
    source_ra = None
    source_dec = None

    with pymongo.MongoClient(db_hostname) as client:
        collection = client.get_database("lst1_obs_summary").get_collection("telescope")
        camera_data_index = [("data.camera.tstart", pymongo.DESCENDING)]

        keep_querying = True
        query_idx = 0
        visited_ids = set()
        nb_duplicated_run_numbers = 0
        obs_info = ObsInfo(None, None, None, None, None, None)
        while keep_querying:
            with collection.find(
                {
                    "data.camera.run_number": {"$exists": True},
                    "data.camera.tstart": {"$exists": True},
                    "data.structure.tstart": {"$exists": True},
                    "data.structure.target.ra": {"$exists": True},
                    "data.structure.target.dec": {"$exists": True},
                    "data.structure.target.source_ra": {"$exists": True},
                    "data.structure.target.source_dec": {"$exists": True},
                },
                {
                    "_id": True,
                    "data.camera.run_number": True,
                    "data.camera.tstart": True,
                    "data.structure.tstart": True,
                    "data.structure.target.ra": True,
                    "data.structure.target.dec": True,
                    "data.structure.target.source_ra": True,
                    "data.structure.target.source_dec": True,
                },
                sort=camera_data_index,
                max_time_ms=obs_target_query_timeout_s * 1000,
                skip=query_idx,
                limit=1,
            ) as cursor:
                query_data = cursor.next()
                if query_data["_id"] not in visited_ids:
                    visited_ids.add(query_data["_id"])

                    structure_list = sorted(query_data["data"]["structure"], key=lambda x: x["tstart"], reverse=True)
                    camera_list = sorted(query_data["data"]["camera"], key=lambda x: x["tstart"], reverse=True)

                    # if the DB is incoherent for the latest run: raise error.
                    # Otherwise do not, and pray the incoherence is not caused by camera field...
                    if (len(structure_list) != len(camera_list)) and (query_idx == 0):
                        # Raise pymongo error because
                        raise pymongo.errors.PyMongoError(
                            f"TCU DB query returned inconsistent data.structure (length {len(structure_list)}) "
                            f"and data.camera (length {len(camera_list)})"
                        )

                    if query_idx == 0:
                        obs_id = camera_list[0]["run_number"]
                        tstart_camera = camera_list[0]["tstart"]
                        ra = structure_list[0]["target"]["ra"]
                        dec = structure_list[0]["target"]["dec"]
                        source_ra = structure_list[0]["target"]["source_ra"]
                        source_dec = structure_list[0]["target"]["source_dec"]

                    comparison_start_idx = 1 if query_idx == 0 else 0
                    for field in camera_list[comparison_start_idx:]:
                        if field["run_number"] != obs_info.obs_id:
                            keep_querying = False
                            break
                        nb_duplicated_run_numbers += 1

            query_idx += 1

    return ObsInfo(obs_id + nb_duplicated_run_numbers, tstart_camera, ra, dec, source_ra, source_dec)