.. currentmodule:: uataq When there is nothing to read ============================= .. note:: The output under each example on this page is from a run on CHPC on 15 October 2026, except under "Pollutant or site not found" and "A level asked of get_obs" (19 October 2026) and under "A group that does not log the instrument" (30 October 2026). The archive keeps growing, so what a read returns will have moved on by the time you read this (:ref:`why the output is recorded `). The archive has gaps. Sites close, instruments are removed or break, and some months are missing. A read then raises an error or comes back empty. Each case below says what you see, what it means and what to do. First, turn on the log. The package reports why it skipped something as a warning, and Python shows nothing from a library unless you ask: .. code-block:: python import logging logging.basicConfig(level=logging.WARNING) The log lines are shown with each case below. The examples start from these lines: .. code-block:: pycon >>> import uataq >>> from uataq import Network >>> wbb = uataq.get_site("WBB") No data found ------------- .. code-block:: pycon >>> wbb.get_obs("CO2", time_range="2021-08") WARNING:uataq.sites:Error reading lgr_ugga@WBB data from lin groupspace: No files found within the specified time range (files run from 2014-02 to 2026-10, with gaps). Traceback (most recent call last): ... uataq.errors.ReaderError: No final data found for CO2 at WBB: lgr_ugga in lin groupspace: No files found within the specified time range (files run from 2014-02 to 2026-10, with gaps). Nothing in the archive holds data for that instrument, in that range, at the level that was read. ``get_obs`` and ``get_recent_obs`` read the final level. Three cases that gave this: - A month with no final file (WBB, August 2021, above). - Dates after a site stopped sending data but before its instruments have a removal date in the configuration. Once they have one, these dates raise the error under `Instrument inactive`_ instead. - Dates in the future. The message names what you asked for (``CO2``), the level that was read and the site. Then it says, for each **instrument** that measures it (``lgr_ugga`` measures CO2 and CH4), what was found. ``lin`` is the research group whose archive was searched (see :ref:`input-group`). The brackets say which files exist: the first and last period they cover (a month at the final level), and "with gaps" when a period in between has none, as August 2021 has none here. When the message lists several instruments, ``name in lin groupspace`` means it was looked for and not found, and ``name not read: ...`` means it was skipped, with the reason. A file can hold no rows, so the periods are not the days with data. To see those, use :meth:`Network.get_availability`, and add ``"qaqc"`` to ``lvls`` to see whether the instrument measured at all. Instrument inactive ------------------- .. code-block:: pycon >>> wbb.get_obs("CO2", time_range="2010") WARNING:uataq.sites:Not reading lgr_ugga@WBB: lgr_ugga@WBB is inactive in given time_range Traceback (most recent call last): ... uataq.errors.ReaderError: No final data found for CO2 at WBB: lgr_ugga not read: lgr_ugga@WBB is inactive in given time_range. The instrument was not installed at the site in that range. Its window is its ``active_range`` (see :ref:`sites-in-code`; :doc:`/reference/sites_and_instruments` lists every instrument's dates). A range that only touches the window, such as one that ends the day the instrument was installed, counts as outside it. A range that overlaps the window only in part is cut down to it, with no error. Dates after a site was taken down give this error too: HDP after 7 February 2025, and SUG's CO2 after 22 August 2025. That includes ``get_recent_obs("HDP", pollutants="CO2")``: nothing is recent at a site that stopped. Zero rows, no error ------------------- .. code-block:: pycon >>> wbb_obs = wbb.get_obs("CO2", time_range="2024-03") >>> print(len(wbb_obs)) 206114 >>> print(len(wbb.get_obs("CO2", time_range="2024-03-04"))) :1: EmptyResultWarning: No final data found for CO2 at WBB (2024-03-04 00:00:00 to 2024-03-05 00:00:00): the files for lgr_ugga exist but hold no data in that range. 0 The month has a file, but there are no rows on that day, at any level (``raw`` too): the instrument recorded nothing. The read did not fail, so you get an empty table and a warning, ``uataq.errors.EmptyResultWarning``. It is a Python warning, so it shows without the log. ``get_recent_obs`` and ``get_network_obs`` warn the same way when their table has no rows, and so does ``get_obs`` at a mobile site whose rows found no GPS fix (see :ref:`mobile-no-gps`). To silence it, for a loop over days that may be empty: .. code-block:: python import warnings import uataq warnings.filterwarnings("ignore", category=uataq.errors.EmptyResultWarning) To see where the gaps in a month are, look at the time between rows: .. code-block:: pycon >>> import pandas as pd >>> gaps = wbb_obs.index.to_series().diff() >>> gaps[gaps > pd.Timedelta("6h")] Time_UTC 2024-03-05 19:29:38 2 days 06:00:42 2024-03-20 00:42:51 0 days 22:58:15 2024-03-25 18:02:30 2 days 14:47:56 Name: Time_UTC, dtype: timedelta64[us] Each row is the time the data resumed and how long the gap before it was. One of several pollutants missing --------------------------------- .. code-block:: pycon >>> both = wbb.get_obs(["CO2", "O3"], time_range="2021-08") WARNING:uataq.sites:Error reading lgr_ugga@WBB data from lin groupspace: No files found within the specified time range (files run from 2014-02 to 2026-10, with gaps). :1: DroppedPollutantWarning: No final data found for CO2 at WBB (2021-08-01 00:00:00 to 2021-09-01 00:00:00): lgr_ugga gave none, so the table leaves out CO2. >>> both.columns.tolist() ['O3_ppb', 'instrument'] When you ask for several pollutants, the ones with data come back and the ones without are left out. A warning, ``uataq.errors.DroppedPollutantWarning``, names each of them and the instruments that gave none. It is a Python warning, so it shows without the log. To see why an instrument gave none, ask for that pollutant alone: the error is the one under `No data found`_. A read of several raises that error only when none of them has data. To silence the warning: .. code-block:: python import warnings import uataq warnings.filterwarnings("ignore", category=uataq.errors.DroppedPollutantWarning) A pollutant is left out only when none of the instruments that measure it gave data (in 2022, WBB's NO2 comes from two). ``get_recent_obs`` warns the same way. ``get_obs()`` with no pollutants named does not: "all" includes every pollutant the site ever measured, and an instrument removed from the site is expected to give none, so check the columns you got. To go through many sites or months and skip the empty ones, catch the error: .. code-block:: pycon >>> from uataq.errors import ReaderError >>> for sid in ["WBB", "SUG", "HDP"]: ... try: ... obs = uataq.get_obs(sid, "CO2", time_range="2025-06") ... print(sid, len(obs), "rows") ... except ReaderError: ... print(sid, "has no CO2 data in June 2025") ... WBB 250687 rows WARNING:uataq.sites:Not reading licor_6262@SUG: licor_6262@SUG is inactive in given time_range SUG 239760 rows WARNING:uataq.sites:Not reading lgr_ugga@HDP: lgr_ugga@HDP is inactive in given time_range HDP has no CO2 data in June 2025 The two ``WARNING`` lines are the log, which is on here. SUG's ``licor_6262`` was replaced by the ``licor_7000``, which gave the rows, so the first line is not a problem. Pollutant or site not found --------------------------- .. code-block:: pycon >>> uataq.get_obs("WBB", "NH3", time_range="2025-06") Traceback (most recent call last): ... ValueError: Invalid pollutant(s): NH3. WBB measures BC, CH4, CO, CO2, NO, NO2, NOx, O3, PM2.5. >>> uataq.get_obs("CSP", "O3", time_range="2025-06") Traceback (most recent call last): ... ValueError: Not measured at CSP: O3. CSP measures CH4, CO2. >>> uataq.get_obs("WBBB", "CO2", time_range="2025-06") Traceback (most recent call last): ... ValueError: Site 'WBBB' not found in the configuration file. The first message is for a name that no uataq instrument measures: a misspelling, or a molecule uataq does not read. The second is for a pollutant that other instruments measure but this site does not. Both end with what the site does measure, spelled as in the columns. You can also print ``sorted(site.pollutants)``. For a site, check the ID against ``uataq.laboratory.sites``. The case does not matter for either. ``read_data`` has a similar error when you name an instrument the site does not have (``InstrumentNotFoundError``, which lists the site's instruments). A level asked of get_obs ------------------------ .. code-block:: pycon >>> uataq.get_obs("WBB", "CO2", lvl="qaqc", time_range="2025-06") Traceback (most recent call last): ... TypeError: get_obs() got an unexpected keyword argument 'lvl'. get_obs always reads the final level; to read another, call read_data(..., lvl='qaqc'). ``get_obs`` takes no level: it always reads ``final``. ``level=`` raises the same error. To read another level, call ``read_data``, which gives a dictionary with one table for each instrument: ``uataq.read_data("WBB", "lgr_ugga", lvl="qaqc", time_range="2025-06")``. :ref:`data-levels` says what each level is for. A group that does not log the instrument ---------------------------------------- .. code-block:: pycon >>> uataq.get_obs("WBB", "CO2", group="horel", time_range="2025-06") WARNING:uataq.sites:Error reading lgr_ugga@WBB data from horel groupspace: horel group invalid for lgr_ugga@WBB Traceback (most recent call last): ... uataq.errors.ReaderError: No final data found for CO2 at WBB: lgr_ugga in horel groupspace: horel group invalid for lgr_ugga@WBB. >>> uataq.get_obs("WBB", "CO2", group="hore1", time_range="2025-06") Traceback (most recent call last): ... ValueError: Invalid group: hore1 ``group`` (see :ref:`input-group`) is ``"lin"`` or ``"horel"``. The first error is for a group that does not log that instrument: the lin group logs every instrument at WBB, and the horel group none. The second is for a name that is neither group. A site's ``groups`` (``sorted(wbb.groups)`` is ``['lin']``, and the Groups column of :doc:`/reference/sites_and_instruments` lists them) says which archives log it. Leave ``group`` out and each instrument picks its own. A level the instrument does not have ------------------------------------ .. code-block:: pycon >>> uataq.read_data("WBB", "teledyne_t400", lvl="calibrated", time_range="2025-06") WARNING:uataq.sites:Error reading teledyne_t400@WBB data from lin groupspace: teledyne_t400@WBB has no calibrated level (it has raw, qaqc and final). Traceback (most recent call last): ... uataq.errors.ReaderError: No calibrated data found for ['teledyne_t400'] at WBB: teledyne_t400 in lin groupspace: teledyne_t400@WBB has no calibrated level (it has raw, qaqc and final). Not every instrument has every level, and the message lists the ones this instrument has, from least to most processed. Only the lin group's CO2 and CH4 analyzers have a ``calibrated`` level, and not TRX01's ``lgr_ugga_manual_cal`` (see :ref:`when-cal-is-empty`), so every other instrument, and every instrument in the horel archive, gives this error for it. Leave ``lvl`` out to read the highest level the instrument has. When ``read_data`` names several instruments, one without the level is logged and left out, and the others are returned. The error is raised only when none has it: .. code-block:: pycon >>> data = uataq.read_data("WBB", ["lgr_ugga", "teledyne_t400"], lvl="calibrated", time_range="2025-06") WARNING:uataq.sites:Error reading teledyne_t400@WBB data from lin groupspace: teledyne_t400@WBB has no calibrated level (it has raw, qaqc and final). >>> list(data) ['lgr_ugga'] A level that is not one of ``raw``, ``qaqc``, ``calibrated`` or ``final`` raises ``ValueError: Invalid data level`` and lists the four. Network surprises ----------------- .. code-block:: pycon >>> uataq.get_network_obs(["HDP"], "CO2", time_range="2025-06") WARNING:uataq.sites:Not reading lgr_ugga@HDP: lgr_ugga@HDP is inactive in given time_range WARNING:uataq.network:Error reading data from HDP: No data found for ['lgr_ugga'] at HDP: lgr_ugga not read: lgr_ugga@HDP is inactive in given time_range. Traceback (most recent call last): ... uataq.errors.ReaderError: No data found for CO2 in any site. None of the sites had data in the range. The log says why for each site. .. code-block:: pycon >>> Network(["WBB", "WBBB"], "CO2") WARNING:uataq.network:Site 'WBBB' not found: Site 'WBBB' not found in the configuration file. Network(sites=['WBB'], pollutant='CO2') >>> Network(["CSP"], "O3") Traceback (most recent call last): ... ValueError: None of the sites given (CSP) measure O3. Sites that measure it: BUS01, BUS02, BUS03, BUS04, BUS05, BUS06, BUS07, BUS08, BUS09, BUS10, BUS11, BUS12, BUS13, BUS14, BUS15, TRX01, TRX02, TRX03, WBB. A misspelled site, or a site that does not measure the pollutant, is dropped from a ``Network`` without an error. The log warns about a misspelled site; a site that does not measure the pollutant is dropped without a warning. Print the ``Network``, or read its ``sites`` attribute, to see the sites it kept; a loop over ``net.sites`` reaches only those. If none is left you get the second error, which names the sites you gave (only their number when there are more than ten), then lists the sites in the configuration that measure the pollutant, or says that none does. A site named twice, in any case (``"WBB"`` and ``"wbb"``), is read once. If the error is about ``time_range`` itself, see :ref:`input-time-range`.