CRO
fitting with CMIP6 models#
This tutorial illustrates how to use the pyCRO
to estimate RO parameters from all CMIP6 models
Contact:
Sen Zhao (zhaos@hawaii.edu)
Load library#
[1]:
%config IPCompleter.greedy = True
%matplotlib inline
%config InlineBackend.figure_format='retina'
%load_ext autoreload
%autoreload 2
import os
import sys
import numpy as np
import xarray as xr
import matplotlib.pyplot as plt
## comment this if you install pyCRO already
sys.path.append(os.path.abspath("../../../"))
import pyCRO
Load ENSO timeseries from CMIP6 models#
[2]:
# load observations
file_name = os.path.join(os.getcwd(), "../../../data", "CROdata_timeseries_CMIP6.nc")
cmip_ds = xr.open_dataset(file_name)
cmip_ds
[2]:
<xarray.Dataset> Size: 1MB Dimensions: (model: 48, time: 1332) Coordinates: * time (time) datetime64[ns] 11kB 1900-01-01 1900-02-01 ... 2010-12-01 * model (model) <U15 3kB 'ACCESS-CM2' 'ACCESS-ESM1-5' ... 'UKESM1-0-LL' Data variables: Nino3 (model, time) float32 256kB ... Nino34 (model, time) float32 256kB ... Hm (model, time) float32 256kB ... Hw (model, time) float32 256kB ...
Fitting RO to the all CMIP6 models#
[3]:
def fit_dict_to_dataset(fit_dict):
"""
Convert a single fit dictionary into an xarray.Dataset.
Assigns dimensions based on the length of the parameter list:
len=1 -> 'ac_0'
len=3 -> 'ac_1'
len=5 -> 'ac_2'
Empty lists are replaced with np.nan (float).
Parameters
----------
fit_dict : dict
Dictionary where keys are parameter names and values are lists.
Returns
-------
xr.Dataset
"""
data_vars = {}
for k, v in fit_dict.items():
if len(v) == 0:
arr = np.array([np.nan], dtype=float)
dim = "ac_0"
elif len(v) == 1:
arr = np.array(v, dtype=float)
dim = "ac_0"
elif len(v) == 3:
arr = np.array(v, dtype=float)
dim = "ac_1"
elif len(v) == 5:
arr = np.array(v, dtype=float)
dim = "ac_2"
else:
# Other lengths: convert to float, pad with nan if needed
arr = np.array(v, dtype=float)
dim = f"ac_{len(v)}"
data_vars[k] = (dim, arr)
return xr.Dataset(data_vars)
def CRO_cmip6_fitting(par_option_T, par_option_h, par_option_noise, verbose=False):
"""
fit all cmip6 models with the same T, h, and noise options
"""
for i, model in enumerate(cmip_ds.model.values):
par_tmp = pyCRO.RO_fitting(cmip_ds['Nino34'].sel(model=model), cmip_ds['Hm'].sel(model=model),
par_option_T, par_option_h, par_option_noise, verbose=verbose)
par_ds = fit_dict_to_dataset(par_tmp).assign_coords({'model': model})
if i==0:
par_cmip = par_ds
else:
par_cmip = xr.concat([par_cmip, par_ds], dim='model')
return par_cmip
Type 1: Linear-White-Additive#
Fit linear RO with white and additive noise
[4]:
par_option_T = {"R": 1, "F1": 1, "b_T": 0, "c_T": 0, "d_T": 0}
par_option_h = {"F2": 1, "epsilon": 1, "b_h": 0}
par_option_noise = {"T": "white", "h": "white", "T_type": "additive"}
par_cmip_LWA = CRO_cmip6_fitting(par_option_T, par_option_h, par_option_noise)
par_cmip_LWA
[4]:
<xarray.Dataset> Size: 9kB Dimensions: (model: 48, ac_0: 1) Coordinates: * model (model) <U15 3kB 'ACCESS-CM2' 'ACCESS-ESM1-5' ... 'UKESM1-0-LL' Dimensions without coordinates: ac_0 Data variables: (12/16) R (model, ac_0) float64 384B -0.0277 0.04347 ... 0.07818 0.006757 F1 (model, ac_0) float64 384B 0.03008 0.0195 ... 0.03125 0.02193 F2 (model, ac_0) float64 384B 1.138 1.671 1.259 ... 1.106 1.602 1.103 epsilon (model, ac_0) float64 384B 0.08306 0.1292 0.1211 ... 0.1831 0.09856 b_T (model, ac_0) float64 384B nan nan nan nan nan ... nan nan nan nan c_T (model, ac_0) float64 384B nan nan nan nan nan ... nan nan nan nan ... ... B (model, ac_0) float64 384B nan nan nan nan nan ... nan nan nan nan m_T (model, ac_0) float64 384B nan nan nan nan nan ... nan nan nan nan m_h (model, ac_0) float64 384B nan nan nan nan nan ... nan nan nan nan n_T (model, ac_0) float64 384B 1.0 1.0 1.0 1.0 1.0 ... 1.0 1.0 1.0 1.0 n_h (model, ac_0) float64 384B 1.0 1.0 1.0 1.0 1.0 ... 1.0 1.0 1.0 1.0 n_g (model, ac_0) float64 384B 2.0 2.0 2.0 2.0 2.0 ... 2.0 2.0 2.0 2.0
Type 2: Nonlinear-White-Additive#
Fit nonlinear RO with red and additive noise
[5]:
par_option_T = {"R": 1, "F1": 1, "b_T": 1, "c_T": 1, "d_T": 1}
par_option_h = {"F2": 1, "epsilon": 1, "b_h": 1}
par_option_noise = {"T": "white", "h": "white", "T_type": "additive"}
par_cmip_NWA = CRO_cmip6_fitting(par_option_T, par_option_h, par_option_noise)
par_cmip_NWA
[5]:
<xarray.Dataset> Size: 9kB Dimensions: (model: 48, ac_0: 1) Coordinates: * model (model) <U15 3kB 'ACCESS-CM2' 'ACCESS-ESM1-5' ... 'UKESM1-0-LL' Dimensions without coordinates: ac_0 Data variables: (12/16) R (model, ac_0) float64 384B -0.02369 0.06183 ... 0.1074 0.0235 F1 (model, ac_0) float64 384B 0.03023 0.01987 ... 0.0325 0.02317 F2 (model, ac_0) float64 384B 1.129 1.654 1.259 ... 1.105 1.605 1.109 epsilon (model, ac_0) float64 384B 0.08266 0.1288 0.1332 ... 0.187 0.1002 b_T (model, ac_0) float64 384B 0.01088 -0.002446 ... 0.03614 0.03063 c_T (model, ac_0) float64 384B 0.0006461 0.00857 ... 0.005134 0.002772 ... ... B (model, ac_0) float64 384B nan nan nan nan nan ... nan nan nan nan m_T (model, ac_0) float64 384B nan nan nan nan nan ... nan nan nan nan m_h (model, ac_0) float64 384B nan nan nan nan nan ... nan nan nan nan n_T (model, ac_0) float64 384B 1.0 1.0 1.0 1.0 1.0 ... 1.0 1.0 1.0 1.0 n_h (model, ac_0) float64 384B 1.0 1.0 1.0 1.0 1.0 ... 1.0 1.0 1.0 1.0 n_g (model, ac_0) float64 384B 2.0 2.0 2.0 2.0 2.0 ... 2.0 2.0 2.0 2.0
Understanding relationship between ENSO amplitude and RO parameters#
Calculation of seasonal variance
[6]:
cmip_SD = cmip_ds['Nino34'].var('time')
[7]:
fig, axes = plt.subplots(1, 2, figsize=(8, 4), sharey=True, layout='compressed')
ax = axes[0]
ax.scatter((par_cmip_LWA['R']-par_cmip_LWA['epsilon'])*0.5, cmip_SD, label='Linear RO')
ax.scatter((par_cmip_NWA['R']-par_cmip_NWA['epsilon'])*0.5, cmip_SD, label='Nonlinear RO')
ax.set_xlabel('BJ (month$^{-1}$)')
ax.set_ylabel(r"Nino34 index variance (K$^2$)")
ax.legend(fontsize=9)
ax = axes[1]
ax.scatter(par_cmip_LWA['sigma_T'], cmip_SD, label='Linear RO')
ax.scatter(par_cmip_NWA['sigma_T'], cmip_SD, label='Nonlinear RO')
ax.set_xlabel(r"$\sigma_T$ (K month$^{-1/2}$)")
[7]:
Text(0.5, 0, '$\\sigma_T$ (K month$^{-1/2}$)')

The ENSO amplitude is highly related the growth rate (BJ index) and a less degree to noise amplitude.