<jupyter_start><jupyter_text>Does the CMIP5 emergent constraint on SH jet shifts work in CMIP6?Kidston and Gerber (2010) (https://doi.org/10.1029/2010GL042873) demonstrated in the annual mean that there was a relationship between the SH climatological jet latitude in the present day and how much the SH jet latitude shifted further poleward under anthropogenic climate change.  Suggesting that there is an emergent constraint on the SH jet latitude change i.e., we know where the real worl jet latitude is and we can use the inter-model relationship between jet latitude and jet shift, to predict where the real world jet will shift in the future.  This relationship was further demonstrated by Simpson and Polvani (https://doi.org/10.1002/2016GL067989) to have a strong seasonality, with the emergent constraint only being strong in the SH winter (JJA).<jupyter_code># Loading libraries
%matplotlib inline
import matplotlib.pyplot as plt
import numpy as np
import xarray as xr
import xesmf as xe
import dask
import season_util as su
import jetlatcalcs as jlat
import intake
from ncar_jobqueue import NCARCluster
from distributed import Client
cluster = NCARCluster(project='UCGD0006')
cluster.adapt(minimum_jobs=1, maximum_jobs=10)
client = Client(cluster)
cluster
models=['AWI-CM-1-1-MR','BCC-CSM2-MR','BCC-ESM1',
       'CAMS-CSM1-0','CanESM5','CNRM-CM6-1','CNRM-ESM2-1','E3SM-1-0',
       'MIROC6','IPSL-CM6A-LR',
       'HadGEM3-GC31-LL','UKESM1-0-LL','MRI-ESM2-0',
       'GISS-E2-1-G','GISS-E2-1-H','CESM2','CESM2-WACCM',
       'NorESM2-LM','GFDL-AM4','GFDL-ESM4','SAM0-UNICON']

#model = ['IPSL-CM6A-LR','CanESM5']

var = ["ua"]
col = intake.open_esm_datastore("/glade/collections/cmip/catalog/\
intake-esm-datastore/catalogs/glade-cmip6.json")
cat = col.search(activity_id="CMIP",experiment_id="piControl",
                variable_id=var, table_id="Amon", source_id=models)
cat4x = col.search(activity_id="CMIP",experiment_id="abrupt-4xCO2",
                variable_id=var, table_id="Amon", source_id=models)
dset_dict_pi = cat.to_dataset_dict(cdf_kwargs={'chunks': {"time": 36}, 
                                    'decode_times': True})
dset_dict_4x = cat4x.to_dataset_dict(cdf_kwargs={'chunks': {"time": 36}, 
                                    'decode_times': True})
cat4x.df.head()<jupyter_output>--> The keys in the returned dictionary of datasets are constructed as follows:
	'activity_id.institution_id.source_id.experiment_id.table_id.grid_label'

--> There will be 20 group(s)
--> The keys in the returned dictionary of datasets are constructed as follows:
	'activity_id.institution_id.source_id.experiment_id.table_id.grid_label'

--> There will be 20 group(s)<jupyter_text>Note, currently all models piControls start in January.  This is going to fail for a model where that's not the case e.g., often the met office.  We need to do something fancier here to make sure it works more generally<jupyter_code>#calculate the mean over longitude, ensemble members and desired time period and pick out 700hPa
pi_mean = {}
abrupt4x_mean = {}
for key, ds in dset_dict_pi.items():
    pi_mean[key] = ds.isel(time = slice(0,100*12))
    pi_mean[key] = pi_mean[key].sel(plev=70000.).mean(dim=["member_id","lon"])
for key, ds in dset_dict_4x.items():
    abrupt4x_mean[key] = ds.isel(time = slice(50*12,150*12))
    abrupt4x_mean[key] = abrupt4x_mean[key].sel(plev=70000.).mean(dim=["member_id","lon"])
print(abrupt4x_mean)
pi_mean = dask.compute(pi_mean)
abrupt4x_mean = dask.compute(abrupt4x_mean)
# calculate JJA season average
#xr.Dataset.drop(dset_dict_pi["CMIP.IPSL.IPSL-CM6A-LR.piControl.Amon.gr"])
#del dset_dict_pi["CMIP.MRI.MRI-ESM2-0.piControl.Amon.gn"]
#del dset_dict_4x["CMIP.MRI.MRI-ESM2-0.abrupt-4xCO2.Amon.gn"]
import pandas as pd
pi_jja = {}
for key, x in dset_dict_pi.items():
    print(key)
    try:
        pi_jja[key] = su.season_mean(pi_mean[0][key],"ua",season="JJA")
    except:
        pi_mean[0][key]["time"]=pd.date_range(pi_mean[0][key].time[0].values, pd.date_range(pi_mean[0][key].time[-1].values, periods=1,freq='M').values[-1], freq='M')
        pi_jja[key] = su.season_mean(pi_mean[0][key],"ua",season="JJA")

abrupt4x_jja = {}
for key, x in dset_dict_4x.items():
    
    try:
        abrupt4x_jja[key] = su.season_mean(abrupt4x_mean[0][key],"ua",season="JJA")
    except:
        abrupt4x_mean[0][key]["time"]=pd.date_range(abrupt4x_mean[0][key].time[0].values, pd.date_range(abrupt4x_mean[0][key].time[-1].values, periods=1,freq='M').values[-1], freq='M')
        abrupt4x_jja[key] = su.season_mean(abrupt4x_mean[0][key],"ua",season="JJA")
        
#calculate jet latitude
jlathist = {}
jspeedhist = {}
for key, x in dset_dict_pi.items():
    jlatv, jspeedv = jlat.calcjetlat( pi_jja[key], -80, -20)
    jlathist[key] = jlatv
    jspeedhist[key] = jspeedv
    
jlatssp370 = {}
jspeedssp370 = {}
for key, x in dset_dict_4x.items():
    jlatv, jspeedv = jlat.calcjetlat( abrupt4x_jja[key], -80, -20)
    jlatssp370[key] = jlatv
    jspeedssp370[key] = jspeedv
#read in cmip5 data
cmip5path = "cmip5_jetlatitudes_pi4xco2.nc"
cmip5 = xr.open_dataset(cmip5path)
#calculate linear regression line
coefs = np.polyfit(cmip5.jlatpi, cmip5.jlat4x - cmip5.jlatpi, 1)
acmip5=coefs[1]
bcmip5=coefs[0]
xvalues = [i for i in range(-55,-35)]
yvalues = [acmip5 + bcmip5*i for i in xvalues]
#predict cmip6 values from cmip5
jlathistvalues = list(jlathist.values())
ycmip6 = [acmip5 + bcmip5*i for i in jlathistvalues]
ycmip6
# plotting
jlathistvalues = list(jlathist.values())
jlatssp370values = list(jlatssp370.values())
jlatdif = [a - b for a, b in zip(jlatssp370values, jlathistvalues)]
jlatdifpredict = [acmip5 + bcmip5*i for i in jlathistvalues]
fig = plt.figure(figsize=(12, 6),facecolor='w')
ax = fig.add_subplot(1, 2, 1)
#ax.plot(jlathistvalues,jlatdif, color='red', label='CMIP6', marker='o', linestyle='none')
ax.plot(cmip5.jlatpi, cmip5.jlat4x-cmip5.jlatpi, color='red', label='CMIP5', marker='o', linestyle='none')
ax.plot(xvalues,yvalues,color='green', label='CMIP5 Regression', linestyle='solid')
ax.plot(jlathistvalues,jlatdif, color='blue', label='CMIP6', marker='o', linestyle='none')
ax.set_xlabel('Past jet latitude ($\phi_{o}$)', fontdict={'size':12});
ax.set_ylabel('Future - Past jet latitude ($\Delta\phi$)', fontdict={'size':12});  
ax.set_title('$\Delta\phi$ vs $\phi_{o}$', fontdict={'size':14})
ax.legend(loc='lower left', fontsize=14)


r = np.corrcoef(jlatdif,jlatdifpredict)
r=r[0,1]
rsq = r**2

ax2=fig.add_subplot(1,2,2)
ax2.plot(jlatdif, jlatdifpredict, color='red', label='r$^{2}$='+str(np.around(rsq,3)), marker='o', linestyle='none')
ax2.set_xlabel('$\Delta\phi$', fontdict={'size':12})
ax2.set_ylabel('Predicted $\Delta\phi$', fontdict={'size':12})
ax2.plot([-10,0],[-10,0], color='black', label='1:1', linestyle='solid')
ax2.set_title('$\Delta\phi$ prediction vs $\Delta\phi$', fontdict={'size':14})
ax2.legend(loc='upper left', fontsize=14)<jupyter_output><empty_output>
{'fraction_non_alphanumeric': 0.15499855949294153, 'fraction_numerical': 0.040334197637568424, 'mean_word_length': 5.193577163247101, 'pattern_counts': {'":': 2, '<': 8, '<?xml version=': 0, '>': 12, 'https://': 2, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 2, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '2', 'dir': 'jupyter-structured-clean-dedup', 'id': '331593', 'license': 'MIT', 'n_tokens_mistral': 2830, 'n_tokens_neox': 2672, 'n_words': 549, 'path': 'notebooks/shjlat_ec_4xco2_v002.ipynb', 'repo_name': 'afahadabdullah/cmip6hack-emergentconstraints', 'size': '6942'}