Source code for pyaerocom.aeroval.config.emep.reporting_base

"""
Global config for emep reporting pyaeroval runs
"""

import copy
import functools
import logging
import os

import yaml

from pyaerocom.data import resources

logger = logging.getLogger(__name__)


# Constraints
DEFAULT_RESAMPLE_CONSTRAINTS = dict(
    yearly=dict(monthly=9),
    monthly=dict(
        daily=21,
        weekly=3,
    ),
    daily=dict(hourly=18),
)

DEFAULT_RESAMPLE_CONSTRAINTS_DAILY = dict(
    daily=dict(hourly=18),
)

OC_EC_RESAMPLE_CONSTRAINTS = dict(
    yearly=dict(monthly=4),
    monthly=dict(daily=4, weekly=1),
    daily=dict(hourly=18),
    hourly=dict(minutely=45),
)


OC_EC_RESAMPLE_CONSTRAINTS_DAILY = dict(
    # monthly=dict(daily=4, weekly=1),
    daily=dict(hourly=18),
    hourly=dict(minutely=45),
)


@functools.cache
def _get_ignore_stations_from_file():
    if os.path.exists("./omit_stations.yaml"):
        filename = os.path.abspath("./omit_stations.yaml")
        logger.info(f"reading omit_stations.yaml from {filename}")
        with open(filename) as fh:
            stations = yaml.safe_load(fh)
    else:
        with resources.path(__package__, "omit_stations.yaml") as filename:
            logger.info(f"reading omit_stations.yaml from {filename}")
            with filename.open() as fh:
                stations = yaml.safe_load(fh)

    rows = []
    for year, comps in stations.items():
        if year == "variables":
            continue
        year = int(year)
        for comp, stats in comps.items():
            for stat in stats:
                for var in stations["variables"][comp]:
                    rows.append((year, year, var.strip(), stat.strip()))
    return rows


def _get_ignore_stations(specy, year):
    """
    Read the ignore stations from either omit_stations.tsv in the local eller in the lib-folder

    specy: specy for this measurement network (ALL are translated to all specy)
    year: only select the stations for the specified year

    return: list of stations
    """
    retvals = []
    year = int(year)
    stations = _get_ignore_stations_from_file()
    for yearstart, yearend, comp, station in stations:
        if comp == "ALL" or comp == specy:
            if yearstart <= year <= yearend:
                retvals.append(station)
    return retvals


[docs] def get_CFG(reportyear, year, model_dir) -> dict: """Get a configuration usable for emep reporting :param reportyear: year of reporting :param year: year of data :param model_dir: directory containing Base_hour.nc,Base_day.nc,Base_month.nc and Base_fullrun.nc or for trends directory containing years like 2005,2010,2015 again containing above files The current working directory of the experiment should have the following files/directories by default: - `data` output directory - `coldata` output directory - `user_var_scale_colmap.ini` optional user-defined colormaps for pyaerocom variables - `omit_stations.yaml` optional user-defined yaml file of stations to omit The default values can be changed in your program. If you want to permanently change the defaults, please agree upon these changes with the emep-modellers and contact the pyaerocom-developers. Example runs with this config look like:: import os import pyaerocom as pya from pyaerocom import const from pyaerocom.aeroval import EvalSetup, ExperimentProcessor from pyaerocom.aeroval.config.emep.reporting_base import get_CFG # Setup for models used in analysis CFG = get_CFG(reportyear=2024, year=2021, model_dir="/lustre/storeB/project/fou/kl/emep/ModelRuns/2024_REPORTING/EMEP01_rv5.3_metyear2021_emis2022") CFG.update(dict( # proj_id="status-2024", exp_id="test-2021met_2022emis", exp_name="Test runs for 2024 EMEP reporting", exp_descr=( "Test run from Agnes for 2024_REPORTING/EMEP01_rv5.3_metyear2021_emis2022, i.e. 2021met and 2022emis" ), exp_pi="S. Tsyro, A. Nyiri, H. Klein", )) # remove EEA # for obs in list(CFG["obs_cfg"].keys()): # if obs.startswith("EEA"): # del CFG["obs_cfg"][obs] # print(f"removed {obs}") # remove "concCocpm10", not in model-output for obs in CFG["obs_cfg"]: if "concCocpm10" in CFG["obs_cfg"][obs]["obs_vars"]: CFG["obs_cfg"][obs]["obs_vars"].remove("concCocpm10") # remove "no, pm10, pm25" from EBAS-hourly CFG["obs_cfg"]["EBAS-h-diurnal"]["obs_vars"].remove("concNno") CFG["obs_cfg"]["EBAS-h-diurnal"]["obs_vars"].remove("concpm10") CFG["obs_cfg"]["EBAS-h-diurnal"]["obs_vars"].remove("concpm25") # CFG["raise_exceptions"] = False # CFG["add_model_maps"] = False # CFG["only_model_maps"] = True stp = EvalSetup(**CFG) cdir = "./cache/" os.makedirs(cdir, exist_ok=True) const.CACHEDIR = cdir ana = ExperimentProcessor(stp) ana.update_interface() res = ana.run() Another example for multiple model-evaluation:: import os import pyaerocom as pya from pyaerocom import const from pyaerocom.aeroval import EvalSetup, ExperimentProcessor from pyaerocom.aeroval.config.emep.reporting_base import get_CFG # Setup for models used in analysis CFG = get_CFG( reportyear=2024, year=2022, model_dir=f"/lustre/storeB/project/fou/kl/emep/ModelRuns/2024_REPORTING/EMEP01_rv5.3_year2022_Status_Rep2024", ) dir_versions = { "FFmod": "/lustre/storeB/project/fou/kl/emep/ModelRuns/2024_REPORTING/EMEP01_rv5.3_year2022_Status_Rep2024_FFmod/", "MARS5.3": "/lustre/storeB/project/fou/kl/emep/ModelRuns/2024_REPORTING/EMEP01_rv5.3_year2022_Status_Rep2024_MARS/", "MARS5.0": "/lustre/storeB/project/fou/kl/emep/ModelRuns/2024_REPORTING/EMEP01_rv5.0_year2022_Status_Rep2023_emis2022/", "NoCations": "/lustre/storeB/project/fou/kl/emep/ModelRuns/2024_REPORTING/EMEP01_rv5.3_year2022_Status_Rep2024_noCation/", } # Comparison of several models MODEL = CFG["model_cfg"]["EMEP"] for mid, fpath in dir_versions.items(): CFG["model_cfg"][mid] = MODEL.copy() CFG["model_cfg"][mid]["model_data_dir"] = fpath CFG["model_cfg"][mid]["model_id"] = mid del CFG["model_cfg"]["EMEP"] # change some config settings, usually not needed CFG.update( dict( proj_id="emepX", exp_id=f"2024-XXX_2022_ebas2", # exp_name="Evaluation of EMEP runs for 2023 EMEP reporting", exp_descr=( f"Evaluation of EMEP runs for 2024 EMEP reporting, MARS vs ISOROPIA. /lustre/storeB/project/fou/kl/emep/ModelRuns/2024_REPORTING/EMEP01_rv5.?_year2022_Status_Rep2024_*/, is compared against observations from EBAS." ), # periods=["2021"], # exp_pi="S. Tsyro, H. Klein", # add_model_maps=False, ) ) # remove "concCocpm10", not in model-output for obs in CFG["obs_cfg"]: if "concCocpm10" in CFG["obs_cfg"][obs]["obs_vars"]: CFG["obs_cfg"][obs]["obs_vars"].remove("concCocpm10") # remove "no, pm10, pm25" from EBAS-hourly CFG["obs_cfg"]["EBAS-h-diurnal"]["obs_vars"].remove("concNno") CFG["obs_cfg"]["EBAS-h-diurnal"]["obs_vars"].remove("concpm10") CFG["obs_cfg"]["EBAS-h-diurnal"]["obs_vars"].remove("concpm25") # remove EEA for obs in list(CFG["obs_cfg"].keys()): if obs.startswith("EEA"): del CFG["obs_cfg"][obs] print(f"removed {obs}") # try to run anything, but don't fail on error # CFG["raise_exceptions"] = False stp = EvalSetup(**CFG) cdir = "./cache" os.makedirs(cdir, exist_ok=True) const.CACHEDIR = cdir ana = ExperimentProcessor(stp) ana.update_interface() # run everything res = ana.run() and the example for trends:: import os import pyaerocom as pya from pyaerocom import const from pyaerocom.aeroval import EvalSetup, ExperimentProcessor from pyaerocom.aeroval.config.emep.reporting_base import get_CFG # Setup for models used in analysis CFG = get_CFG(reportyear=2023, year=2021, model_dir=f"/lustre/storeB/project/fou/kl/emep/ModelRuns/2023_REPORTING/TRENDS/pyaerocom_trends/") CFG.update(dict( proj_id="emep", exp_id=f"2023-trends", # exp_name="Evaluation of EMEP runs for 2023 EMEP reporting", exp_descr=( f"Evaluation of EMEP runs for 2023 EMEP reporting trend runs. 7 year obs-data availability per period. /lustre/storeB/project/fou/kl/emep/ModelRuns/2023_REPORTING/TRENDS/pyaerocom_trends is compared against observations fro m EBAS." ), periods=["1990-2021", "1990-1999", "2000-2009", "2010-2019", "2012-2021"], #range(1990,2022)], # exp_pi="S. Tsyro, H. Klein", add_model_maps=False, #only_model_maps=True, # trend parameters freqs=["yearly", "monthly"], # "weekly"],"daily"], # can't be hourly for trends, daily is too slow weekly hardly ever needed main_freq="monthly", add_trends=True, avg_over_trends=True, obs_min_yrs=7, # kun stasjoner med minst 14yr stats_min_yrs=7, # kun stasjoner med minst 14yr sequential_yrs=False, )) # remove "no, pm10, pm25" from EBAS-hourly CFG["obs_cfg"]["EBAS-h-diurnal"]["obs_vars"].remove("concNno") CFG["obs_cfg"]["EBAS-h-diurnal"]["obs_vars"].remove("concpm10") CFG["obs_cfg"]["EBAS-h-diurnal"]["obs_vars"].remove("concpm25") # remove EEA for obs in list(CFG["obs_cfg"].keys()): if obs.startswith("EEA"): del CFG["obs_cfg"][obs] # remove all hourly obs, f.e. for trends for obs in list(CFG["obs_cfg"].keys()): if "ts_type" in CFG["obs_cfg"][obs] and CFG["obs_cfg"][obs]["ts_type"] == "hourly": del CFG["obs_cfg"][obs] print(f"removed hourly {obs}") # remove all daily obs, f.e. for trends for obs in list(CFG["obs_cfg"].keys()): if "ts_type" in CFG["obs_cfg"][obs] and CFG["obs_cfg"][obs]["ts_type"] == "daily": del CFG["obs_cfg"][obs] print(f"removed daily {obs}") # remove "concCocpm10", not in model-output for obs in CFG["obs_cfg"]: if "concCocpm10" in CFG["obs_cfg"][obs]["obs_vars"]: CFG["obs_cfg"][obs]["obs_vars"].remove("concCocpm10") # try to run anything, but don't fail on error # CFG["raise_exceptions"] = False stp = EvalSetup(**CFG) cdir = "./cache" os.makedirs(cdir, exist_ok=True) const.CACHEDIR = cdir ana = ExperimentProcessor(stp) ana.update_interface() # run everything res = ana.run() :returns: a dict of a model configuration usable for EvalSetup """ CFG = dict( json_basedir=os.path.abspath("./data"), coldata_basedir=os.path.abspath("./coldata"), # io_aux_file=os.path.abspath("./gridded_io_aux.py"), not needed for ReadMscwCtm var_scale_colmap_file=os.path.abspath("./user_var_scale_colmap.ini"), # if True, existing colocated data files will be deleted and contours will be overwritten reanalyse_existing=True, only_json=False, add_model_maps=True, only_model_maps=False, modelmaps_opts=dict(maps_freq="yearly", maps_res_deg=5), clear_existing_json=False, # if True, the analysis will stop whenever an error occurs (else, errors that # occurred will be written into the logfiles) raise_exceptions=True, # Regional filter for analysis filter_name="ALL-wMOUNTAINS", # colocation frequency (no statistics in higher resolution can be computed) ts_type="daily", map_zoom="Europe", freqs=["yearly", "monthly", "weekly", "daily", "hourly"], periods=[f"{year}"], main_freq="daily", zeros_to_nan=False, use_diurnal=True, min_num_obs=DEFAULT_RESAMPLE_CONSTRAINTS, colocate_time=True, resample_how={"vmro3max": {"daily": {"hourly": "max"}}}, obs_remove_outliers=False, model_remove_outliers=False, harmonise_units=True, regions_how="country", annual_stats_constrained=True, proj_id="emep", exp_id=f"{reportyear}-reporting", exp_name=f"Evaluation of EMEP runs for {reportyear} EMEP reporting", exp_descr=( f"Evaluation of EMEP runs for {reportyear} EMEP reporting. The EMEP model, simulated for {year}, is compared against observations from EEA and EBAS." ), exp_pi="emep.mscw@met.no", public=True, # directory where colocated data files are supposed to be stored weighted_stats=True, var_order_menu=[ # Gases "concNno", "concNno2", "concNtno3", "concNhno3", "concNtnh", "concNnh3", "concnh4", "concSso2", "concso4t", "concso4c", "vmro3", "vmro3max", "vmro3mda8", "vmrox", "vmrco", # PMs "concpm10", "concpm25", "concno3pm10", "concno3pm25", "concno3pm1", "concnh4pm25", "concnh4pm1", "concso4pm25", "concso4pm1", "concCecpm10", "concCecpm25", "concCocpm10", # SURF_ugC_PM_OMCOARSE missing in model-output "concCocpm25", "concom1", "concsspm10", "concsspm25", # Depositions "wetrdn", "wetoxs", "wetoxn", "prmm", ], ) CFG["model_cfg"] = { "EMEP": dict( model_id="EMEP", model_data_dir=model_dir, gridded_reader_id={"model": "ReadMscwCtm"}, model_read_aux={}, # model_ts_type_read="daily", ), } """ Filters """ # OBS SPECIFIC FILTERS (combination of the above and more) EEA_RURAL_FILTER = { "station_classification": ["background"], "area_classification": [ "rural", "rural-nearcity", "rural-regional", "rural-remote", ], } BASE_FILTER = { "latitude": [30, 82], "longitude": [-30, 90], } EBAS_FILTER = { **BASE_FILTER, "data_level": [None, 2], "set_flags_nan": True, } EEA_FILTER = { **BASE_FILTER, **EEA_RURAL_FILTER, } EEA_FILTER_ALL = { **BASE_FILTER, } AERONET_FILTER = { **BASE_FILTER, # Forandring fra Daniel "altitude": [-20, 1000], } # Station filters eea_species = [ "concpm10", "concpm25", "concSso2", "concNno2", "concNno", "vmro3max", "vmro3", "concNno2", "vmrox", "concno2", ] ebas_species = [ "concNhno3", "concNtno3", "concNtnh", "concNnh3", "concnh4", "prmm", "concpm10", "concpm25", "concSso2", "concNno2", "vmrco", "vmro3max", "vmro3", "concNno", "concCecpm25", "concCocpm25", "concom1", "concCecpm10", "concCocpm10", # "concnh4pm10", # no output in the model "concnh4pm25", "concnh4pm1", # "concso4pm10", # no output in the model "concso4pm25", "concso4pm1", "concno3pm10", "concno3pm25", "concno3pm1", "concsspm10", "concsspm25", "concso4t", "concso4c", "wetoxs", "wetoxn", "wetrdn", "vmrox", ] # no new sites with 2021 observations (comment Svetlana T.) height_ignore_ebas = [ "AT0034G", "AT0038R", "AT0049R", "BG0001R", "CH0001G", "CH0018R", "CH0024R", "CH0031R", "CH0033R", "DE0054R", "DE0057G", "DE0075R", "ES0005R", "ES0022R", "FR0019R", "FR0030R", "FR0031R", "FR0038U", "FR0039U", "FR0100G", "GR0003R", "GR0101R", "HR0002R", "HR0004R", "IT0002R", "IT0009R", "IT0019R", "IT0020U", "IT0021U", "IT0024R", "KG0001R", "KG0002U", "NO0036R", "NO0039R", "NO0211R", "NO0214R", "NO0225R", "NO0226R", "NO0227R", "NO0229R", "NO0796R", "NO0802R", "NO0907R", "NO2073R", "NO2079R", "NO2085R", "NO2096R", "NO2156R", "NO2210R", "NO2216R", "NO2219R", "NO2233R", "NO2239R", "NO2257R", "NO2263R", "NO2274R", "NO2280R", "NO2288R", "NO2362R", "NO2380R", "NO2397R", "NO2411R", "PL0003R", "PT0005R", "PT0007R", "PT0012R", "RO0002R", "RO0003R", "SE0093R", "SE0094R", "SI0032R", "SK0002R", ] height_ignore_eea = [ "FR33220", "TR0047A", "AT72619", "ES1982A", "IT0983A", "IS0040A", "IT2099A", "BG0080A", "IT2159A", "IT0906A", "AT72821", "IT1190A", "IT1976A", "AT56072", "IT2178A", "IS0044A", "IT1335A", "AT0SON1", "IT0703A", "AT72227", "DEUB044", "AT55032", "HR0013A", "FR33120", "AT60182", "IT0908A", "ES1673A", "AT55019", "SK0042A", "SI0032R", "ES0005R", "FR33720", "DEBY196", "AT60177", "IT2128A", "AT2SP18", "FR15045", "R160421", "IT2234A", "TR0118A", "DEST039", "E165168", "AT72110", "FR15013", "ES1348A", "E165169", "AL0206A", "AT72822", "DEBY123", "FR15031", "AT72538", "IS0042A", "FR33114", "AT52300", "IT1859A", "FR33232", "IT2239A", "IS0043A", "PL0003R", "FR31027", "FR33113", "FR15048", "AT54057", "TR0046A", "FR33111", "IT2284A", "AT72550", "IT1037A", "FR33121", "E165167", "IT1847A", "AT72912", "RS0047A", "R610613", "TR0110A", "R160512", "IT1191A", "IT1963A", "FR15053", "RO0009R", "IT0508A", "IT2233A", "MK0041A", "AT72519", "BG0079A", "IT1696A", "IT1619A", "IT2267A", "TR0107A", "AT56071", "FR29440", "AT4S235", "AD0945A", "IS0038A", "E165166", "PT01047", "AT55018", "SK0002R", "IT0499A", "HR0014A", "IT0591A", "IT0507A", "AT72315", "E165170", "ES1432A", "IT1166A", "AT4S254", "IT1967A", "AT2VL52", "IT1930A", "AT72115", "AT82708", "IT0988A", "FR15038", "AT82801", "IT2285A", "NO0039R", "TR0020A", "IT2096A", "AD0942A", "TR0071A", "E165165", "ES0354A", "AT72910", "ES1882A", "IT1725A", "AT60150", "CH0024A", "IT1114A", "AT72113", "IT1852A", "IS0048A", "FR15017", "FR15039", "IT0980A", "IT0502A", "IT1678A", "IT1334A", "IT0978A", "FR15043", "IT2279A", "IT0775A", "IT1539A", "AT72123", "IT2014A", "XK0005A", "AT2WO15", "FR33122", "XK0007A", "AT60196", "CH0033A", "IT1385A", "GR0405A", "AT52000", "IT2266A", "FR15046", "AT72223", "FR24024", "IT0979A", "AT2SP10", "IT2179A", "IT0977A", "AT72530", "ES1248A", "AT72106", "IT0753A", ] EBAS_FILTER = { key: dict( **EBAS_FILTER, station_id=_get_ignore_stations(key, year) + height_ignore_ebas, negate="station_id", ) for key in ebas_species } EEA_FILTER = { key: dict( **EEA_FILTER, station_name=height_ignore_eea, negate="station_name", ) for key in eea_species } EEA_FILTER_ALL = { key: dict( **EEA_FILTER_ALL, station_name=height_ignore_eea, negate="station_name", ) for key in eea_species } OBS_GROUNDBASED = { ################## # EBAS ################## "EBAS-m-tc": dict( obs_id="EBASMC", web_interface_name="EBAS-m", obs_vars=[ "concNhno3", "concNtno3", "concNtnh", "concNnh3", "concnh4", # "prmm", "concpm10", "concpm25", "concSso2", "concNno2", "vmrco", "vmro3max", "vmro3", "concNno", "concso4t", "concso4c", ], obs_vert_type="Surface", colocate_time=True, ts_type="monthly", obs_filters=EBAS_FILTER, ), "EBAS-d-tc": dict( obs_id="EBASMC", web_interface_name="EBAS-d", obs_vars=[ "concNhno3", "concNtno3", "concNtnh", "concNnh3", "concnh4", "concpm10", "concpm25", "concSso2", "concNno2", "vmrco", "vmro3max", "vmro3", "concNno", "concso4t", "concso4c", ], obs_vert_type="Surface", colocate_time=True, min_num_obs=DEFAULT_RESAMPLE_CONSTRAINTS, ts_type="daily", obs_filters=EBAS_FILTER, ), "EBAS-m-tc-ecoc": dict( obs_id="EBASMC", web_interface_name="EBAS-m", obs_vars=[ "concCecpm25", "concCocpm25", "concom1", "concCecpm10", "concCocpm10", # "concnh4pm10", "concnh4pm25", "concnh4pm1", # "concso4pm10", "concso4pm25", "concso4pm1", "concno3pm10", "concno3pm25", "concno3pm1", "concsspm10", "concsspm25", ], obs_vert_type="Surface", colocate_time=True, ts_type="monthly", min_num_obs=OC_EC_RESAMPLE_CONSTRAINTS, obs_filters=EBAS_FILTER, ), "EBAS-d-tc-ecoc": dict( obs_id="EBASMC", web_interface_name="EBAS-d", obs_vars=[ "concCecpm25", "concCocpm25", "concom1", "concCecpm10", "concCocpm10", # "concnh4pm10", "concnh4pm25", "concnh4pm1", # "concso4pm10", "concso4pm25", "concso4pm1", "concno3pm10", "concno3pm25", "concno3pm1", "concsspm10", "concsspm25", ], obs_vert_type="Surface", colocate_time=True, ts_type="daily", min_num_obs=OC_EC_RESAMPLE_CONSTRAINTS, obs_filters=EBAS_FILTER, ), # Diurnal "EBAS-h-diurnal": dict( obs_id="EBASMC", web_interface_name="EBAS-h", obs_vars=[ "concNno2", "concNno", "vmro3", "concpm10", "concpm25", ], obs_vert_type="Surface", ts_type="hourly", # diurnal_only=True, resample_how="mean", obs_filters={**EBAS_FILTER, "ts_type": "hourly"}, ), # OX "EBAS-d-ox": dict( obs_id="EBAS-ox", obs_vars=["vmrox"], obs_type="ungridded", obs_vert_type="Surface", web_interface_name="EBAS", ts_type="daily", obs_merge_how={ "vmrox": "eval", }, obs_aux_requires={ "vmrox": { "EBASMC": [ "vmro3", "vmrno2", ], } }, obs_aux_funs={ "vmrox": # variables used in computation method need to be based on AeroCom # units, since the colocated StationData objects (from which the # new UngriddedData is computed, will perform AeroCom unit check # and conversion) "(EBASMC;vmro3+EBASMC;vmrno2)" }, obs_aux_units={"vmrox": "nmol mol-1"}, min_num_obs=DEFAULT_RESAMPLE_CONSTRAINTS, obs_filters=EBAS_FILTER, ), "EBAS-h-ox-diurnal": dict( obs_id="EBAS-ox-diurnal", obs_vars=["vmrox"], obs_type="ungridded", obs_vert_type="Surface", web_interface_name="EBAS-h", ts_type="hourly", # diurnal_only=True, obs_merge_how={ "vmrox": "eval", }, obs_aux_requires={ "vmrox": { "EBASMC": ["vmro3", "vmrno2"], } }, obs_aux_funs={ "vmrox": # variables used in computation method need to be based on AeroCom # units, since the colocated StationData objects (from which the # new UngriddedData is computed, will perform AeroCom unit check # and conversion) "(EBASMC;vmro3+EBASMC;vmrno2)" }, obs_aux_units={"vmrox": "nmol mol-1"}, obs_filters={**EBAS_FILTER, "ts_type": "hourly"}, ), # Wet Dep "EBAS-d-wet": dict( obs_id="EBASMC", web_interface_name="EBAS-d", ts_type="daily", obs_remove_outliers=True, obs_vars=[ "wetoxs", "wetoxn", "wetrdn", "prmm", ], obs_vert_type="Surface", min_num_obs=DEFAULT_RESAMPLE_CONSTRAINTS, colocate_time=True, obs_filters=EBAS_FILTER, ), "EBAS-m-wet": dict( obs_id="EBASMC", web_interface_name="EBAS-m", ts_type="monthly", obs_remove_outliers=True, colocate_time=True, obs_vars=[ "wetoxs", "wetoxn", "wetrdn", "prmm", ], obs_vert_type="Surface", obs_filters=EBAS_FILTER, ), ################ # EEA-rural ################ "EEA-d-rural": dict( obs_id="EEAAQeRep.v2", obs_vars=[ "concpm10", "concpm25", "concSso2", "concNno2", "vmro3max", # "concno2", ], web_interface_name="EEA-rural", obs_vert_type="Surface", obs_filters=EEA_FILTER, ), "EEA-d-rural-no": dict( obs_id="EEAAQeRep.v2", obs_vars=[ "concNno", ], web_interface_name="EEA-rural", obs_vert_type="Surface", obs_filters=EEA_FILTER, ), "EEA-h-diurnal-rural": dict( obs_id="EEAAQeRep.v2", obs_vars=["vmro3", "concNno2"], obs_vert_type="Surface", web_interface_name="EEA-h-rural", ts_type="hourly", # diurnal_only=True, harmonise_units=False, resample_how="mean", obs_filters={**EEA_FILTER, "ts_type": "hourly"}, ), "EEA-d-ox-rural": dict( obs_id="EEA-ox-rural", obs_vars=["vmrox"], obs_type="ungridded", obs_vert_type="Surface", web_interface_name="EEA-rural", ts_type="daily", # min_num_obs=None, obs_merge_how={ "vmrox": "eval", }, obs_aux_requires={ "vmrox": { "EEAAQeRep.v2": ["vmro3", "vmrno2"], } }, obs_aux_funs={ "vmrox": # variables used in computation method need to be based on AeroCom # units, since the colocated StationData objects (from which the # new UngriddedData is computed, will perform AeroCom unit check # and conversion) "(EEAAQeRep.v2;vmro3+EEAAQeRep.v2;vmrno2)" }, obs_aux_units={"vmrox": "nmol mol-1"}, obs_filters={**EEA_FILTER}, ), "EEA-h-ox-rural-diu": dict( obs_id="EEA-ox-rural-diu", obs_vars=["vmrox"], obs_type="ungridded", obs_vert_type="Surface", web_interface_name="EEA-h-rural", ts_type="hourly", # diurnal_only=True, obs_merge_how={ "vmrox": "eval", }, obs_aux_requires={ "vmrox": { "EEAAQeRep.v2": ["vmro3", "vmrno2"], } }, obs_aux_funs={ "vmrox": # variables used in computation method need to be based on AeroCom # units, since the colocated StationData objects (from which the # new UngriddedData is computed, will perform AeroCom unit check # and conversion) "(EEAAQeRep.v2;vmro3+EEAAQeRep.v2;vmrno2)" }, obs_aux_units={"vmrox": "nmol mol-1"}, obs_filters={**EEA_FILTER, "ts_type": "hourly"}, ), ################ # EEA-all ################ "EEA-d-all": dict( obs_id="EEAAQeRep.v2", obs_vars=[ "concpm10", "concpm25", "concSso2", "concNno2", "vmro3max", # "concno2", ], web_interface_name="EEA-all", obs_vert_type="Surface", obs_filters=EEA_FILTER_ALL, ), "EEA-d-all-no": dict( obs_id="EEAAQeRep.v2", obs_vars=[ "concNno", ], web_interface_name="EEA-all", obs_vert_type="Surface", obs_filters=EEA_FILTER_ALL, ), "EEA-h-diurnal-all": dict( obs_id="EEAAQeRep.v2", obs_vars=["vmro3", "concNno2"], obs_vert_type="Surface", web_interface_name="EEA-h-all", ts_type="hourly", # diurnal_only=True, harmonise_units=False, resample_how="mean", obs_filters={**EEA_FILTER_ALL, "ts_type": "hourly"}, ), "EEA-d-ox-all": dict( obs_id="EEA-ox-all", obs_vars=["vmrox"], obs_type="ungridded", obs_vert_type="Surface", web_interface_name="EEA-all", ts_type="daily", # min_num_obs=None, obs_merge_how={ "vmrox": "eval", }, obs_aux_requires={ "vmrox": { "EEAAQeRep.v2": ["vmro3", "vmrno2"], } }, obs_aux_funs={ "vmrox": # variables used in computation method need to be based on AeroCom # units, since the colocated StationData objects (from which the # new UngriddedData is computed, will perform AeroCom unit check # and conversion) "(EEAAQeRep.v2;vmro3+EEAAQeRep.v2;vmrno2)" }, obs_aux_units={"vmrox": "nmol mol-1"}, obs_filters={**EEA_FILTER_ALL}, ), "EEA-h-ox-all-diu": dict( obs_id="EEA-ox-all-diu", obs_vars=["vmrox"], obs_type="ungridded", obs_vert_type="Surface", web_interface_name="EEA-h-all", ts_type="hourly", # diurnal_only=True, obs_merge_how={ "vmrox": "eval", }, obs_aux_requires={ "vmrox": { "EEAAQeRep.v2": ["vmro3", "vmrno2"], } }, obs_aux_funs={ "vmrox": # variables used in computation method need to be based on AeroCom # units, since the colocated StationData objects (from which the # new UngriddedData is computed, will perform AeroCom unit check # and conversion) "(EEAAQeRep.v2;vmro3+EEAAQeRep.v2;vmrno2)" }, obs_aux_units={"vmrox": "nmol mol-1"}, obs_filters={**EEA_FILTER_ALL, "ts_type": "hourly"}, ), ################## # AERONET ################## "AERONET": dict( obs_id="AeronetSunV3Lev1.5.daily", obs_vars=["od550aer"], web_interface_name="AERONET", obs_vert_type="Column", ignore_station_names="DRAGON*", ts_type="daily", colocate_time=True, min_num_obs=dict( yearly=dict( daily=90, ), monthly=dict( weekly=1, ), ), obs_filters=AERONET_FILTER, ), } # Setup for supported satellite evaluations OBS_SAT = {} OBS_CFG = {**OBS_GROUNDBASED, **OBS_SAT} CFG["obs_cfg"] = OBS_CFG return copy.deepcopy(CFG)