Adding Custom Variables with EMEP
In this tutorial we will go through how to add custom variables to our PyAeroVal evaluation, as well as how to read these from EMEP. We will add two new variables: elementalcarbon
and organiccarbon
. These variables are just meant as illustrative examples, and variables for elemental and organic carbon do already exist in Pyaerocom.
Adding the variables will be done in three steps:
Register the new variables in Pyaerocom
Tell Pyaercom how to read
organiccarbon
from EMEPCombine two existing variables to make
elementalcarbon
Before continuing, please note: 1. You need to have access to PPI/Lustre to do this tutorial, due to it using EMEP files found there. 2. Adding custom variables should only be done for testing, and not production! See the last section.
Registering the New Variables
Here we will look at how to add an extra variable, in the same way as is done in variables.ini
. This is needed so that PyAerocom knows about the variable and it units.
PyAerocom has a static object called config
, which contains a function for adding new variables
[2]:
from pyaerocom.variable import Variable
from pyaerocom import const
variables = {
"elemental_carbon": Variable(var_name="elementalcarbon", units="ug Cm-3"),
"organic_carbon": Variable(var_name="organiccarbon", units="ug Cm-3"),
}
const.register_custom_variables(variables)
The function register_custom_variables
takes a dictionary where the values are Variable
objects. These objects need at least the variable name and unit. It can also take other arguments, such as min
and max
. In `variables.ini
<https://github.com/metno/pyaerocom/blob/main-dev/pyaerocom/data/variables.ini>`__ you can see all the other arguments that are used.
Reading From EMEP
PyAerocom now know that we have two new variables, but since they are not define anywhere inside the PyAerocom code, PyAerocom does not know from where to read the data. We will here tell PyAerocom how to read the two variables from EMEP.
Reading From an EMEP File
The most common way of reading data from EMEP is to just read them directly from an EMEP file. To do this, PyAerocom needs to know what that variable is called in EMEP. Normally PyAerocom gets this mapping from this file, but since we just introduced a new variables, we need to define this mapping our self. This is done when defining our EMEP model entry in our config by adding the option
emep_vars
[3]:
folder_EMEP = "/lustre/storeB/project/fou/kl/emep/ModelRuns/2021_REPORTING/TRENDS/2018"
EMEP = dict(
model_id="EMEP",
model_data_dir=folder_EMEP,
gridded_reader_id={"model": "ReadMscwCtm"}, # Tells pyaerocom to use the EMEP reader instead of the default aerocom reader
emep_vars={"organiccarbon": "SURF_ugC_PM_OM25"},
)
Our new mapping is thus defined as "organiccarbon": "SURF_ugC_PM_OM25"
. PyAerocom will now read our first variables
Combining Existing Variables
The other way PyAerocom gets variables from EMEP is to combine other variables. In this case we will combine the already existing variables concecCoarse
and concecFine
to make our new variable elementalcarbon
.
This takes a bit more work than what we did above. We will start by creating a new function we will use to tell PyAerocom how to combine concecCoarse
and concecFine
[4]:
def calc_elecmentalcarbon(concecCoarse, concecFine):
elementalcarbon = concecCoarse.copy(deep=True) + concecFine.copy(
deep=True
) # Adds the two variables
elementalcarbon.attrs["units"] = "ug C m-3" # Make sure the unit is correct
return elementalcarbon
This function tells PyAerocom that we want to add the two variables. It also makes sure that we have the correct unit (in this case the wrong unit, but again this is just an example). We now need to tell PyAerocom that we have this function. This is again done in the EMEP model entry
[5]:
folder_EMEP = "/lustre/storeB/project/fou/kl/emep/ModelRuns/2021_REPORTING/TRENDS/2018"
EMEP = dict(
model_id="EMEP",
model_data_dir=folder_EMEP,
gridded_reader_id={
"model": "ReadMscwCtm"
}, # Tells pyaerocom to use the EMEP reader instead of the default aerocom reader
emep_vars={"organiccarbon": "SURF_ugC_PM_OM25"},
model_read_aux={
"elementalcarbon": {
"vars_required": ["concecCoarse", "concecFine"],
"fun": calc_elecmentalcarbon,
}
},
)
We see here that we have the new option model_read_aux
. This option takes keys which are our new variables. The values are also dictionaries:
vars_required
: A list of the PyAerocom variables needed for the calculation. In our caseconcecCoarse
andconcecFine
.fun
: The function which tells PyAerocom how to combine the variables. In our case the function we made just above
Note that for other used vars_required
doesn’t need to have two variables in the list. We can make combinations with arbitrary many variables. We can also use newly defined variables, such as organiccarbon
.
The config for this evaluation can be found here.
Best Practice
Note that this way of adding custom variables should only be used for testing, and not in production. Meaning that when you have found all the variables you want to add to AeroTools (Pyaerocom and/or the EMEP reader), you should make an issue on the PyAerocom github or ask one of the developers to add it. There are couple of reasons why
It’s it not as easy to reproduce experiments where you have added custom variables, since your additions are not documented on the webpage or in the PyAerocom code.
You might get errors/memory errors which are difficult to handle, especially with the EMEP reader
Other parts having to do with the variables are hardcoded in PyAerocom/PyAeroval. Examples are axis scaling, display names, etc.