In this tutorial, we are going to construct new coord objects and their axes from parameters that vary across a range of experiments. Numerical model runs often involve configuration (control) files that specify the value of parameters used in the runs. Here, we will look at two types of model runs, one where the Drake Passage is open, and one type where it is closed, equilibrated under a range of atmospheric CO2 values. The CO2 concentrations are specified in configuration files that we have saved alongside the Netcdf data in the experiment directories. First, import the modules:
In [1]:
import spacegrids as sg
import numpy as np
import matplotlib.pyplot as plt
import os
figsize(8,4)
Obtain a dictionary of available projects for easy reference:
In [2]:
D = sg.info()
Initialize a project:
In [3]:
P = sg.project(D['DP'])
Bring axes names X, Y, Z, T into namespace:
In [4]:
for c in P['GM_C_flat'].axes:
exec c.name + ' = c'
As can be seen from the names of created experiment objects, we have two types of experiments, the DP type and the GM type. The Drake Passage is closed in the DP type, whereas this ocean gateway is open in the GM type experiment. The experiment names also contain a CO2 concentration value (e.g. 750ppm), indicating the atmospheric CO2 concentration used in that run. In both experiment types, DP and GM, CO2 varies from 280ppm to 2000ppm. The Netcdf output data contains fields in equilibrium with each CO2 value. The CO2 values are not stored in the Netcdf files. Instead, they are specified in the original configuration files, named control.in
, used in the experiments:
In [5]:
ls /home/wim/PROJECTS/DP4/GM_C_flat/
The control.in file looks like this:
The input parameter co2ccn is what we are looking for. Let's examine temperature:
In [6]:
P.load('temp')
The project insert
method takes a function that takes an exper
object as input and outputs a list of (name,value) tuple pairs. These pairs are subsequently inserted into the params
dictionary attribute of each experiment object by the project insert
method. In our case here, we use a function defined in sg
that uses the path
attribute to find the configuration file for that experiment, parse it, and insert the name/ value pairs into the params
dictionary of that experiment. sg.read_control_func
is actually a function that yields a function that reads the control file specified in the argument to sg.read_control_func
. sg.read_control_func('control.in')
is then a function that is passed an exper
object, and outputs a list of (name, value) pairs. Such functions can be passed to P.insert to be processed, where for each experiment in the project, the function is executed, and the resulting (name, value) pairs are inserted into the vars
or params
dictionaries of that experiment. We will encounter another such function, using regular expressions.
In [7]:
P.insert(sg.read_control_func('control.in')) # read, parse and insert parameters from control.in
The value that we saw in the control.in file now appears in the params
attribute of P['GM_C_flat']
:
In [8]:
P['GM_C_flat'].params['co2ccn'] # obtain the value of the 'co2ccn' param
Out[8]:
The param2gr
project method creates a new field defined on a grid containing a new coord
object constructed from a specified parameter:
In [9]:
bigfield_DP = P.param2gr('co2ccn' , lambda x:x['temp'], 'DP*') # do this for the DP type
Here, the parameter is co2ccn
and the second argument is a function acting on an exper
object (experiment), simply yielding the temp field contained by that exper
: in general, this function must take an experiment argument and yield a field
object. A simple wildcard filter is applied in the last argument to obtain only results for the DP type runs. Similar for the GM type runs:
In [10]:
bigfield_GM = P.param2gr('co2ccn', lambda x:x['temp'], 'GM*') # do this for the GM type
This new bigger field bigfield_DP
, constructed from temp fields across a range of experiments has a 4 dimensional grid that includes the newly constructed co2ccn_crd
coordinate:
In [11]:
bigfield_DP.gr
Out[11]:
This new coord
contains the CO2 values that we saw in the experiments:
In [12]:
bigfield_DP.gr[0][:]
Out[12]:
Both bigfield fields contain temperature values over a range of CO2 concentrations. We can examine how the average bottom ocean temperatures (grid cells 17 to 19) depend on CO2 for the DP open and closed type experiments:
In [13]:
h1, = sg.plot(bigfield_DP[Z,17:]/(X*Y*Z) + sg.zero_kelvin)
h2, = sg.plot(bigfield_GM[Z,17:]/(X*Y*Z) + sg.zero_kelvin)
lbl = plt.ylabel('C')
lbl = plt.xlabel('co2 ccn')
tle = plt.title('Bottom ocean temperature for DP open and closed')
lgnd = plt.legend([h1,h2],["DP closed","DP open"],loc=2)
plt.grid()
Deep ocean temperature in the DP closed type is more sensitive!
The file names (and therefore experiment names) used contain sufficient information to reconstruct the co2 values used in those runs. In some situations, we may not have access the configuration files used, and file names is all we can go by. Spacegrids contains several regular expression based methods (of the project class) that allow us to do this. First, the following regular expression extracts the co2 value from the experiment (file) names:
In [14]:
regexp = '[\w_]+_(\d+)ppm[\w_]+'
The function sg.parse_fname_func
takes this regular expression, and the intended parameter name, to use it to yield another function that extracts the parameter from the experiment (file) name using the regexp. This is similar to the file reading function discussed above. The resulting function is passed an exper
object, and outputs a list of (name, value) pairs. Such functions can be passed to P.insert to be processed, where for each experiment in the project, the function is executed, and the resulting (name, value) pairs are inserted into the vars
or params
dictionaries of that experiment:
In [15]:
P.insert(sg.parse_fname_func(regexp,parname='co2'))
This yields the following (unordered) list of co2 values, this time extracted from the experiment names (their filenames), without reading the configuration files:
In [16]:
[E.params['co2'] for E in P.expers.values() if 'DP' in E.name ]
Out[16]:
The 280ppm value is absent (in fact, it corresponds to the None entry). This is because this value is not encoded in the 280 ppm DP experiment name DPclsd_C_flat
. This can be fixed by specifying the nomatch_fill argument:
In [17]:
P.insert(sg.parse_fname_func(regexp,parname='co2', nomatch_fill = '280ppm'))
In [18]:
[E.params['co2'] for E in P.expers.values() if 'DP' in E.name ]
Out[18]:
These co2 parameters can again be used to construct big_field_DP
:
In [19]:
big_field_DP = P.param2gr('co2' , lambda x:x['temp'], name_filter = 'DP*')
We have created the same big field again as before, but now we have constructed it by using the information contained in the file naming:
In [20]:
h1, = sg.plot(bigfield_DP[Z,17:]/(X*Y*Z) + sg.zero_kelvin)
lbl = plt.ylabel('C')
lbl = plt.xlabel('co2 ccn')
A shorthand for this regexp-based procedure is contained in the pattern2gr
method:
In [21]:
bigfield_DP=P.pattern2gr(fld_name = 'temp',pattern = regexp,parname='co2', name_filter = 'DP*', nomatch_fill = 280.)
In [22]:
bigfield_DP.gr[0][:]
Out[22]:
In [ ]: