The CAMS European air quality forecast can be retrieved from the Atmosphere Data Store (ADS) in either GRIB or NetCDF format. When requesting multiple models and/or variables in GRIB format however, it may not be clear which fields are which. The model that a field belongs to is identified by its "centre" and "subCentre" keys, and the variable by its "parameterNumber" and "constituentType" keys.
The tables below show the mapping. Below that is some example Python code showing how this information can be used with ecCodes to identify the model and variable of each field in a GRIB file.
Model | centre | subCentre |
---|---|---|
CHIMERE | 85 | 200 |
DEHM | 85 | 203 |
EMEP | 88 | 0 |
Ensemble median | 85 | 2 |
EURAD-IM | 85 | 201 |
GEM-AQ | 85 | 204 |
LOTOS-EUROS | 99 | 0 |
MATCH | 82 | 98 |
MOCAGE | 85 | 1 |
SILAM | 86 | 0 |
MINNI | 85 | 205 |
MONARCH | 85 | 206 |
Variable | parameterNumber | constituentType |
---|---|---|
Alder pollen | 59 | 62100 |
Ammonia | 0 | 9 |
Birch pollen | 59 | 62101 |
Carbon monoxide | 0 | 4 |
Dust | 0 | 62001 |
Formaldehyde | 0 | 7 |
Glyoxal | 0 | 10038 |
Grass pollen | 59 | 62300 |
Mugwort pollen | 59 | 62201 |
Nitrogen dioxide | 0 | 5 |
Nitrogen monoxide | 0 | 11 |
Non-methane VOCs | 0 | 60013 |
Olive pollen | 59 | 64002 |
Ozone | 0 | 0 |
Particulate matter < 10um (PM10) | 0 | 40008 |
Particulate matter < 2.5um (PM2.5) | 0 | 40009 |
Peroxyacyl nitrates (PANs) | 0 | 60018 |
PM2.5, total organic matter only | 0 | 62010 |
PM10, sea salt (dry) only | 0 | 62008 |
PM10, wildfires only | 0 | 62096 |
Ragweed pollen | 59 | 62200 |
Residential elementary carbon | 0 | 62094 |
Secondary inorganic aerosol (SIA) | 0 | 62099 |
Sulphur dioxide | 0 | 8 |
Total elementary carbon | 0 | 62095 |
from eccodes import ( codes_grib_new_from_file, codes_get, codes_release, codes_write) # Dictionary matching ADS API key/value pairs to grib key/value pairs definitions = { 'variable': { 'ammonia': { 'parameterNumber': 0, # Mass density (kg/m3) 'constituentType': 9}, 'carbon_monoxide': { 'parameterNumber': 0, 'constituentType': 4}, 'dust': { 'parameterNumber': 0, 'constituentType': 62001}, 'formaldehyde': { 'parameterNumber': 0, 'constituentType': 7}, 'glyoxal': { 'parameterNumber': 0, 'constituentType': 10038}, 'non_methane_vocs': { 'parameterNumber': 0, 'constituentType': 60013}, 'nitrogen_monoxide': { 'parameterNumber': 0, 'constituentType': 11}, 'nitrogen_dioxide': { 'parameterNumber': 0, 'constituentType': 5}, 'ozone': { 'parameterNumber': 0, 'constituentType': 0}, 'peroxyacyl_nitrates': { 'parameterNumber': 0, 'constituentType': 60018}, 'particulate_matter_10um': { 'parameterNumber': 0, 'constituentType': 40008}, 'particulate_matter_2.5um': { 'parameterNumber': 0, 'constituentType': 40009}, 'pm2.5_anthropogenic_wood_burning_carbon': { 'parameterNumber': 0, 'constituentType': 62098}, 'pm2.5_anthropogenic_fossil_fuel_carbon': { 'parameterNumber': 0, 'constituentType': 62097}, 'pm2.5_total_organic_matter': { 'parameterNumber': 0, 'constituentType': 62010}, 'pm10_sea_salt_dry': { 'parameterNumber': 0, 'constituentType': 62008}, 'pm10_wildfires': { 'parameterNumber': 0, 'constituentType': 62096}, 'residential_elementary_carbon': { 'parameterNumber': 0, 'constituentType': 62094}, 'secondary_inorganic_aerosol': { 'parameterNumber': 0, 'constituentType': 62099}, 'sulphur_dioxide': { 'parameterNumber': 0, 'constituentType': 8}, 'total_elementary_carbon': { 'parameterNumber': 0, 'constituentType': 62095}, 'alder_pollen': { 'parameterNumber': 59, # Number Concentration (1/m3) 'constituentType': 62100}, 'birch_pollen': { 'parameterNumber': 59, 'constituentType': 62101}, 'grass_pollen': { 'parameterNumber': 59, 'constituentType': 62300}, 'mugwort_pollen': { 'parameterNumber': 59, 'constituentType': 62201}, 'olive_pollen': { 'parameterNumber': 59, 'constituentType': 64002}, 'ragweed_pollen': { 'parameterNumber': 59, 'constituentType': 62200} }, 'model': { 'chimere': { 'centre': 85, # France 'subCentre': 200}, 'dehm': { 'centre': 85, 'subCentre': 203}, 'emep': { 'centre': 88, # Oslo 'subCentre': 0}, 'ensemble': { 'centre': 85, # France 'subCentre': 2}, 'euradim': { 'centre': 85, # France 'subCentre': 201}, 'gemaq': { 'centre': 85, 'subCentre': 204}, 'lotos': { 'centre': 99, 'subCentre': 0}, 'match': { 'centre': 82, # Norrkoping 'subCentre': 98}, 'mocage': { 'centre': 85, # Meteo France 'subCentre': 1}, 'silam': { 'centre': 86, # Helsinki 'subCentre': 0}, 'minni': { 'centre': 85, 'subCentre': 205}, 'monarch': { 'centre': 85, 'subCentre': 206} } } def identify_field(msg): """Return a dict identifying the input grib message""" field = {} for api_key, value_to_grib in definitions.items(): for api_value, grib_defn in value_to_grib.items(): # Does this message match the key/value pairs in grib_defn? match = True for grib_key, grib_value in grib_defn.items(): msg_value = codes_get(msg, grib_key, ktype=type(grib_value)) if msg_value != grib_value: match = False break if match: field[api_key] = api_value if api_key not in field: raise Exception('Unrecognised field') return field def main(grib_file, output_filename_template=None): """Read all fields in the GRIB file, identify the model and variable of each, and optionally write them to output files, split by model or variable or both. Any instance of "<model>" or "<variable>" in output_filename_template will be replaced by the associated model or variable string for the field, e.g. output_filename_template='<model>.<variable>.grib' """ fields_written = {} # Loop over messages in a grib file, identifying all fields count = 0 with open(grib_file) as fin: while True: msg = codes_grib_new_from_file(fin) if msg is None: break count += 1 try: field = identify_field(msg) print('Field ' + str(count) + ' is: ' + repr(field)) # Write the field to an output file? if output_filename_template: name = output_filename_template for k, v in field.items(): name = name.replace(f'<{k}>', v) if name not in fields_written: fields_written[name] = 0 mode = 'w' else: mode = 'a' with open(name, mode + 'b') as fout: codes_write(msg, fout) fields_written[name] += 1 finally: codes_release(msg) for name, n in fields_written.items(): print(f'Wrote {n} fields to {name}') if __name__ == '__main__': main('mydata.grib', output_filename_template='<model>.<variable>.grib')