Example with CMIP6 models (100 - 500 km)#

Table of Contents#

1. Introduction #

Cloud feedbacks are a major contributor to the spread of climate sensitivity in global climate models (GCMs) Zelinka et al. (2020). Among the most poorly understood cloud feedbacks is the one associated with the cloud phase, which is expected to be modified with climate change Bjordal et al. (2020). Cloud phase bias, in addition, has significant implications for the simulation of radiative properties and glacier and ice sheet mass balances in climate models.

In this context, this work aims to expand our knowledge on how the representation of the cloud phase affects snow formation in GCMs. Better understanding this aspect is necessary to develop climate models further and improve future climate predictions.

  • Retrieve CMIP6 data through ESGF

  • Hybrid sigma-pressure coordinates to isobaric pressure levels of the European Centre for Medium-Range Weather Forecast Re-Analysis 5 (ERA5) with GeoCAT-comb

  • Regridd the CMIP6 variables to the exact horizontal resolution with xesmf

  • Calculate an ensemble mean of all used models

  • Calculate and plot the seasonal mean of the ensemble mean

Questions

  • How is the cloud phase and snowfall varying between 2007 and 2010?

NOTE: We answer questions related to the comparison of CMIP models to ERA5 in another Jupyter Notebook.

2. Data Wrangling #

This study will compare surface snowfall, ice, and liquid water content from the Coupled Model Intercomparison Project Phase 6 (CMIP6) climate models to the European Centre for Medium-Range Weather Forecast Re-Analysis 5 (ERA5) data from 2006 to 2009. We conduct statistical analysis at the annual and seasonal timescales to determine the biases in cloud phase and precipitation (liquid and solid) in the CMIP6 models and their potential connection between them.

  • Time period: 2006 to 2009

  • horizonal resolution: ~100km

  • time resolution: monthly atmospheric data (Amon, AERmon)

  • Variables:

shortname

Long name

Units

levels

prsn

Snowfall Flux

[kg m-2 s-1]

surface

clw

Mass Fraction of Cloud Liquid Water

[kg kg-1]

ml

to calculate lwp use integral clw -dp/dg

tas

Near-Surface Air Temperature

[K]

surface

clivi

Ice Water Path

[kg m-2]

lwp

Liquid Water Path

[kg m-2]

  • CMIP6 models:

Institution

Model name

Reference

MIROC

MIROC6

Tatebe et al. (2019)

NCAR

CESM2

Danabasoglu et al. (2020)

CCCma

CanESM5

Swart et al. (2019)

AWI

AWI-ESM-1-1-LR

MOHC

UKESM1-0-LL

MOHC

HadGem3-GC31-LL

Roberts et al. (2019)

CNRM-CERFACS

CNRM-CM6-1

Voldoire et al. (2019)

CNRM-CERFACS

CNRM-ESM2-1

Seferian et al. (2019)

IPSL

IPSL-CM6A-LR

Boucher et al. (2020)

IPSL

IPSL-CM5A2-INCA

Organize my data#

  • Define a prefix for my project (you may need to adjust it for your own usage on your infrastructure).

    • input folder where all the data used as input to my Jupyter Notebook is stored (and eventually shared)

    • output folder where all the results to keep are stored

    • tool folder where all the tools

The ERA5 0.25deg data is located in the folder /input/cmip6_hist/daily_means.

import os
import pathlib
import sys
import socket
hostname = socket.gethostname()

abs_path = str(pathlib.Path(hostname).parent.absolute())
WORKDIR = abs_path[:- (len(abs_path.split('/')[-2] + abs_path.split('/')[-1])+1)]


if "mimi" in hostname:
    print(hostname)
    DATA_DIR = "/scratch/franzihe/"
    # FIG_DIR = "/uio/kant/geo-metos-u1/franzihe/Documents/Figures/CMIP6/"
    FIG_DIR = "/uio/kant/geo-metos-u1/franzihe/Documents/Python/globalsnow/CloudSat_ERA5_CMIP6_analysis/Figures/CMIP6/"

elif "glefsekaldt" in hostname: 
    DATA_DIR = "/home/franzihe/Data/"
    FIG_DIR = "/home/franzihe/Documents/Figures/CMIP6/"

INPUT_DATA_DIR = os.path.join(DATA_DIR, 'input')
OUTPUT_DATA_DIR = os.path.join(DATA_DIR, 'output')
UTILS_DIR = os.path.join(WORKDIR, 'utils')

sys.path.append(UTILS_DIR)
# make figure directory
try:
    os.mkdir(FIG_DIR)
except OSError:
    pass
mimi.uio.no

Import python packages#

# supress warnings
import warnings
warnings.filterwarnings('ignore') # don't output warnings

# import packages
from imports import (xr, intake, cftime, xe, glob, np, cm, pd, fct,ccrs, cy, plt, da, gc, datetime, LogNorm)
xr.set_options(display_style="html")
<xarray.core.options.set_options at 0x7fe9825ad450>
# reload imports
%load_ext autoreload
%autoreload 2
# ### Create dask cluster to work parallel in large datasets

# from dask.distributed import Client
# client = Client(n_workers=4, 
#                 threads_per_worker=2, 
#                 memory_limit='100GB',
#                 processes=False)
# client
# chunks={'time' : 10,}
# client 
# # These are the usual ipython objects, including this one you are creating
# ipython_vars = ['In', 'Out', 'exit', 'quit', 'get_ipython', 'ipython_vars']

# # Get a sorted list of the objects and their sizes
# sorted([(x, sys.getsizeof(globals().get(x))) for x in dir() if not x.startswith('_') and x not in sys.modules and x not in ipython_vars], key=lambda x: x[1], reverse=True)

Open CMIP6 variables#

Get the data required for the analysis. Beforehand we downloaded the daily averaged data on single levels and model levels via.

cmip_in = os.path.join(INPUT_DATA_DIR, 'cmip6_hist/daily_means/single_model')
cmip_out = os.path.join(OUTPUT_DATA_DIR, 'cmip6_hist/daily_means/common_grid')

# make output data directory
try:
    os.mkdir(cmip_out)
except OSError:
    pass
variable_id = ['clw', 'cli', 'clivi', 'tas', 'prsn', 'pr', 'areacella']

At the moment we have downloaded the end of the historical simulations for CMIP6 models. We define start and end year to ensure to only extract the 4-year period between 2007 and 2010.

\(\rightarrow\) Define a start and end year

We will load all available models into one dictonary, which includes an xarray dataset with xarray.open_mfdataset(file) and select the time range by name.

# source_id
list_models = [
               'MIROC6', 
               'CESM2', 
               'CanESM5', 
               'AWI-ESM-1-1-LR', 
               'MPI-ESM1-2-LR', 
            # # #    'UKESM1-0-LL', 
            # # #    'HadGEM3-GC31-LL',
            #    # 'CNRM-CM6-1',
            #    # 'CNRM-ESM2-1',
               # 'IPSL-CM6A-LR',
               'IPSL-CM5A2-INCA'
            ]

## experiment
experiment_id = ['historical']

## time resolution
t_res = ['day',]

Search corresponding data#

Get the data required for the analysis. Define variables, models, experiment, and time resolution as defined in 2. Data Wrangling .

starty = 2006; endy = 2009
year_range = range(starty, endy+1)

dset_dict = dict()
# for model in list_models:
#     cmip_file_in = glob('{}/*{}_{}_{}*'.format(cmip_in, t_res[0], model, experiment_id[0]))
#     if len(cmip_file_in) != 0:
#         dset_dict[model] = xr.open_mfdataset(sorted(cmip_file_in), combine='nested', compat='override', use_cftime=True)
#         # select only years needed for analysis
#         dset_dict[model] = dset_dict[model].sel(time = dset_dict[model]['time'].dt.year.isin(year_range)).squeeze()
#         # shift longitude to be from -180 to 180
#         dset_dict[model] = dset_dict[model].assign_coords(lon=(((dset_dict[model]['lon'] + 180) % 360) - 180)).sortby('lon').sortby('time')
#     else:
#         continue
for model in list_models:
    cmip_file_in = glob('{}/*{}*45*'.format(cmip_in, model))
    dset_dict[model] = xr.open_mfdataset(cmip_file_in)

    dset_dict[model] = dset_dict[model].sel(time = dset_dict[model].time.dt.year.isin(year_range)).squeeze()
    
    dset_dict[model]['twp'] = dset_dict[model]['lwp'] + dset_dict[model]['clivi']

Convert Calender from dtype=object to datetime64#

def to_ERA5_date(ds, model):
    if ds.time.dtype == 'datetime64[ns]':
        print(model,ds.time[0].values)
    if ds.time.dtype == 'object':
        print(model, ds.time[0].values)
        ds['time'] = ds.indexes['time'].to_datetimeindex()
    
    # remove leap day from dataset  
    ds = ds.sel(time=~((ds.time.dt.month == 2) & (ds.time.dt.day == 29)))
    
    dates = ds.time.values
    years = dates.astype('datetime64[Y]').astype(int)+1971 # add a year to be similar to ERA5
    months = dates.astype('datetime64[M]').astype(int) % 12 + 1
    days = (dates.astype('datetime64[D]') - dates.astype('datetime64[M]')).astype(int) + 1

    
    data = np.array([list(years),list(months), list(days),[0] * len(list(years)) ])
    # Transpose the data so that columns become rows.
    data = data.T
    data = data.tolist()
    # A simple list comprehension does the trick, '*' making sure
    # the values are unpacked for 'datetime.datetime'.
    new_data = [datetime(*x) for x in data]
    # We assign the new time coordinate
    ds = ds.assign_coords({'time':new_data})
    
    return ds
for model in dset_dict.keys():

    dset_dict[model] = to_ERA5_date(dset_dict[model], model)
    #remove leap day from dataset
    dset_dict[model] = dset_dict[model].sel(time=~((dset_dict[model].time.dt.month == 2) & (dset_dict[model].time.dt.day == 29)))
MIROC6 2006-01-01T12:00:00.000000000
CESM2 2006-01-01 00:00:00
CanESM5 2006-01-01 12:00:00
AWI-ESM-1-1-LR 2006-01-01T12:00:00.000000000
MPI-ESM1-2-LR 2006-01-01T12:00:00.000000000
IPSL-CM5A2-INCA 2006-01-01 12:00:00
for model in dset_dict.keys():
    for var_id in dset_dict[model].keys():
        print(model, var_id, dset_dict[model][var_id].attrs['units'])
MIROC6 areacella m2
MIROC6 clivi kg m-2
MIROC6 lwp kg m-2
MIROC6 pr kg m-2 h-1
MIROC6 prsn kg m-2 h-1
MIROC6 tas K
MIROC6 twp kg m-2
CESM2 areacella m2
CESM2 clivi kg m-2
CESM2 lwp kg m-2
CESM2 pr kg m-2 h-1
CESM2 prsn kg m-2 h-1
CESM2 tas K
CESM2 twp kg m-2
CanESM5 areacella m2
CanESM5 clivi kg m-2
CanESM5 lwp kg m-2
CanESM5 pr kg m-2 h-1
CanESM5 prsn kg m-2 h-1
CanESM5 tas K
CanESM5 twp kg m-2
AWI-ESM-1-1-LR areacella m2
AWI-ESM-1-1-LR clivi kg m-2
AWI-ESM-1-1-LR lwp kg m-2
AWI-ESM-1-1-LR pr kg m-2 h-1
AWI-ESM-1-1-LR prsn kg m-2 h-1
AWI-ESM-1-1-LR tas K
AWI-ESM-1-1-LR twp kg m-2
MPI-ESM1-2-LR areacella m2
MPI-ESM1-2-LR clivi kg m-2
MPI-ESM1-2-LR lwp kg m-2
MPI-ESM1-2-LR pr kg m-2 h-1
MPI-ESM1-2-LR prsn kg m-2 h-1
MPI-ESM1-2-LR tas K
MPI-ESM1-2-LR twp kg m-2
IPSL-CM5A2-INCA areacella m2
IPSL-CM5A2-INCA clivi kg m-2
IPSL-CM5A2-INCA lwp kg m-2
IPSL-CM5A2-INCA pr kg m-2 h-1
IPSL-CM5A2-INCA prsn kg m-2 h-1
IPSL-CM5A2-INCA tas K
IPSL-CM5A2-INCA twp kg m-2

Count the days in one season over year range#

def is_season(month, lower_val, upper_val):
    return (month>=lower_val) & (month <= upper_val)
days_season = xr.DataArray(data = [xr.concat([dset_dict[model].sel(time=is_season(dset_dict[model]['time.month'], 1, 2)), dset_dict[model].sel(time=is_season(dset_dict[model]['time.month'],12,12))], dim='time').sizes['time'],
                                   dset_dict[model].sel(time=is_season(dset_dict[model]['time.month'], 6, 8)).sizes['time'],
                                   dset_dict[model].sel(time=is_season(dset_dict[model]['time.month'], 3, 5)).sizes['time'],
                                   dset_dict[model].sel(time=is_season(dset_dict[model]['time.month'], 9, 11)).sizes['time'],], 
                           dims={'season'}, 
                           coords={'season':['DJF', 'JJA', 'MAM', 'SON']})
_days = []
for month in np.arange(1,13):
    _days.append(dset_dict[model].sel(time=is_season(dset_dict[model]['time.month'], month, month)).sizes['time'])
    # print(month, )
days_month = xr.DataArray(data= np.array(_days),
                          dims={'month'}, 
                          coords={'month':np.arange(1,13)} )

Supercooled liquid water fraction#

\[SLF = \frac{\text{cloud liquid water content}}{\text{cloud liquid water content} + \text{cloud ice water content}}\]

Statistics#

For variables:

  • Snowfall [sf]

  • Total column cloud liquid, supercooled liqid, and rain water [tclslrw]

  • Total column cloud ice, snow water [tcisw]

  • 2m-Temperature [2t]

  1. Find where liquid water path is \(\ge\) 5 g m-2

  2. Find where snowfall is \(\ge\) 0.01mm h-1

  3. Find where 2m-temperature \(\le\) 0 \(^o\) C

dset_dict[model]
<xarray.Dataset>
Dimensions:    (lat: 48, lon: 96, time: 1460)
Coordinates:
  * lat        (lat) float32 -90.0 -88.11 -86.21 -84.32 ... 86.21 88.11 90.0
  * lon        (lon) float32 -180.0 -176.2 -172.5 -168.8 ... 168.8 172.5 176.2
    height     float64 2.0
  * time       (time) datetime64[ns] 2007-01-01 2007-01-02 ... 2010-12-31
Data variables:
    areacella  (lat, lon) float32 dask.array<chunksize=(24, 96), meta=np.ndarray>
    clivi      (time, lat, lon) float32 dask.array<chunksize=(1460, 24, 96), meta=np.ndarray>
    lwp        (time, lat, lon) float64 dask.array<chunksize=(1460, 24, 96), meta=np.ndarray>
    pr         (time, lat, lon) float32 dask.array<chunksize=(1460, 24, 96), meta=np.ndarray>
    prsn       (time, lat, lon) float32 dask.array<chunksize=(1460, 24, 96), meta=np.ndarray>
    tas        (time, lat, lon) float32 dask.array<chunksize=(1460, 24, 96), meta=np.ndarray>
    twp        (time, lat, lon) float64 dask.array<chunksize=(1460, 24, 96), meta=np.ndarray>
dset_dict_lcc = dict()
dset_dict_lcc_2t = dict()
dset_dict_lcc_2t_season = dict()
dset_dict_lcc_2t_sf = dict()
th_days_lcc_2t = dict()
th_days_sf = dict()
dset_dict_lcc_2t_sf_season = dict()
for model in dset_dict.keys():

    # 1. find where liquid water >= 0.005 kgm-2 or >= threshold
    th_lcc = 0.005
    dset_dict_lcc[model] = dset_dict[model].where(dset_dict[model]['lwp']>=th_lcc, other=np.nan)
    # find where 2m-temperature <= 0C or <= threshold
    # This should automatically assume that it is already only snow, but it could include supercooled 
    # rain in the case of total precipitation
    th_2t = 273.15
    dset_dict_lcc_2t[model] = dset_dict_lcc[model].where(dset_dict[model]['tas'] <= th_2t, other=np.nan)

    # amount of freezing rain
    dset_dict_lcc_2t[model]['mfrr'] = (dset_dict_lcc_2t[model]['pr'] - dset_dict_lcc_2t[model]['prsn'])
    dset_dict_lcc_2t[model]['mfrr'].attrs = {'units': 'kg m-2 h-1', 'long_name': 'Mean freezing rain rate'}

    # # if we want a precip or snowfall threshold apply here
    # # find where total precipitation >0 kgm-2h-1 threshold in these liquid containg clouds
    # # th_tp = 0.01
    # # dset_dict_lcc_2t = dset_dict_lcc_2t.where(dset_dict['pr']>=th_tp, other=np.nan) 
    # # 2.1 find where snowfall >= 0.24 mmday-1 or >= threshold in these liquid containing clouds, but not temperature threshold
    # # multiply by 24 to make it comparable to McIllhattan et al. As they use 0.01mmh-1 as lower threshold
    # # applying snowfall days, based on threshold (th_sf). Gives days where snowfall above th_sf and counts days in season and 
    # # devides by season days
    # th_sf = 0.01
    # dset_dict_lcc_2t_sf = dset_dict_lcc_2t.where(dset_dict['msr']>=th_sf, other=np.nan) 
    # # th_days = (dset_dict_lcc_2t_sf['twp'].groupby('time.season').count(dim='time',keep_attrs=False))/days_season


    # create dataset to use for calculating the precipitation efficency. For the precipitation efficency we want to remove th_frac 
    # days where liquid water content and temperature requirements are met. 
    # assign percent of snowfall days, required in a pixle, which should be included in the statistics
    th_frac = 0.1
    th_days_lcc_2t[model] = (dset_dict_lcc_2t[model]['twp'].groupby('time.season').count(dim='time',keep_attrs=False))/days_season

    dset_dict_lcc_2t_season[model] = dset_dict_lcc_2t[model].groupby('time.season').mean('time', skipna=True, keep_attrs=True)
    dset_dict_lcc_2t_season[model] = dset_dict_lcc_2t_season[model].where(th_days_lcc_2t[model]>=th_frac)

    # for all the other statistics we want to remove th_frac days where liquid content, temperature, and snowfall requirements are met
    # which also means we have to apply the threshold for the total precipitation
    # find where total precipitation >= 0.01 kg m-2 h-1 in LCCs with T2<0C
    th_tp = 0.01
    dset_dict_lcc_2t_sf[model] = dset_dict_lcc_2t[model].where(dset_dict_lcc_2t[model]['pr'] >=th_tp, other=np.nan)
    # find where snowfall >= 0.01 kg m-2 h-1 or >= threshold in these liquid containing clouds. 
    th_sf = 0.01
    dset_dict_lcc_2t_sf[model] = dset_dict_lcc_2t_sf[model].where(dset_dict_lcc_2t_sf[model]['prsn'] >= th_sf, other=np.nan)
    # applying snowfall days, based on threshold (th_sf). Gives days where snowfall above th_sf and counts days in season and devides 
    # by season days
    th_days_sf[model] = (dset_dict_lcc_2t_sf[model]['twp'].groupby('time.season').count(dim='time', keep_attrs=False))/days_season
    dset_dict_lcc_2t_sf_season = dset_dict_lcc_2t_sf[model].groupby('time.season').mean('time', skipna=True, keep_attrs=True)
    dset_dict_lcc_2t_sf_season = dset_dict_lcc_2t_season[model].where(th_days_sf[model]>=th_frac)
    dset_dict_lcc_2t_sf_season
dset_dict_lcc_2t_days = dict()
for model in dset_dict.keys():

    # Now create daily dataset based on seasonal supercooled liquid containing cloud days above th_sf, and th_frac
    _mam = ((dset_dict_lcc_2t[model].sel(time=is_season(dset_dict_lcc_2t[model]['time.month'], 3, 5))).where(th_days_lcc_2t[model].sel(season='MAM') >=th_frac)).drop('season')
    _jja = ((dset_dict_lcc_2t[model].sel(time=is_season(dset_dict_lcc_2t[model]['time.month'], 6, 8))).where(th_days_lcc_2t[model].sel(season='JJA') >=th_frac)).drop('season')
    _son = ((dset_dict_lcc_2t[model].sel(time=is_season(dset_dict_lcc_2t[model]['time.month'], 9, 11))).where(th_days_lcc_2t[model].sel(season='SON') >=th_frac)).drop('season')
    _djf = ((xr.concat([dset_dict_lcc_2t[model].sel(time=is_season(dset_dict_lcc_2t[model]['time.month'], 1, 2)), 
                    dset_dict_lcc_2t[model].sel(time=is_season(dset_dict_lcc_2t[model]['time.month'],12,12))], dim='time')).where(th_days_lcc_2t[model].sel(season='DJF') >=th_frac)).drop('season')

    dset_dict_lcc_2t_days[model] = xr.merge(objects=[_djf, _jja, _mam, _son])
dset_dict_lcc_2t_sf_days = dict()
for model in dset_dict.keys():
    # Now create daily dataset based on seasonal supercooled liquid containing cloud days above th_sf, and th_frac
    _mam = ((dset_dict_lcc_2t_sf[model].sel(time=is_season(dset_dict_lcc_2t_sf[model]['time.month'], 3, 5))).where(th_days_sf[model].sel(season='MAM') >=th_frac)).drop('season')
    _jja = ((dset_dict_lcc_2t_sf[model].sel(time=is_season(dset_dict_lcc_2t_sf[model]['time.month'], 6, 8))).where(th_days_sf[model].sel(season='JJA') >=th_frac)).drop('season')
    _son = ((dset_dict_lcc_2t_sf[model].sel(time=is_season(dset_dict_lcc_2t_sf[model]['time.month'], 9, 11))).where(th_days_sf[model].sel(season='SON') >=th_frac)).drop('season')
    _djf = ((xr.concat([dset_dict_lcc_2t_sf[model].sel(time=is_season(dset_dict_lcc_2t_sf[model]['time.month'], 1, 2)), 
                    dset_dict_lcc_2t_sf[model].sel(time=is_season(dset_dict_lcc_2t_sf[model]['time.month'],12,12))], dim='time')).where(th_days_sf[model].sel(season='DJF') >=th_frac)).drop('season')

    dset_dict_lcc_2t_sf_days[model] = xr.merge(objects=[_djf, _jja, _mam, _son])
# def find_precip_cloud(dset):
#     # 1. find where LWP >=5 gm-2
#     sf = dset['prsn'].where(dset['lwp']>=0.005, other=np.nan)
#     lwp = dset['lwp'].where(dset['lwp']>=0.005, other=np.nan)
#     iwp = dset['clivi'].where(dset['lwp']>=0.005, other=np.nan)
#     twp = dset['twp'].where(dset['lwp']>=0.005, other=np.nan)
#     t2 = dset['tas'].where(dset['lwp']>=0.005, other=np.nan)
    
#     # print(1,'sf', sf.min(skipna=True).values, sf.max(skipna=True).values, 'lwp', lwp.min(skipna=True).values, lwp.max(skipna=True).values, 't2', t2.min(skipna=True).values, t2.max(skipna=True).values)
#     # 2. find where snowfall >= 0.01mms-1
#     unit_sf = dset['prsn']
#     sf = sf.where(unit_sf>=0.01*24, other=np.nan)
#     lwp = lwp.where(unit_sf>=0.01*24, other=np.nan)
#     iwp = iwp.where(unit_sf>=0.01*24, other=np.nan)
#     twp = twp.where(unit_sf>0.01*24, other=np.nan)
#     t2 = t2.where(unit_sf>=0.01*24, other=np.nan)
#     # print(2,'sf', sf.min(skipna=True).values, sf.max(skipna=True).values, 'lwp', lwp.min(skipna=True).values, lwp.max(skipna=True).values, 't2', t2.min(skipna=True).values, t2.max(skipna=True).values)
    
#     # 3. find where 2m-temperature <= 0C
#     sf = sf.where(dset['tas']<=273.15, other=np.nan)
#     lwp = lwp.where(dset['tas']<=273.15, other=np.nan)
#     iwp = iwp.where(dset['tas']<=273.15, other=np.nan)
#     twp = twp.where(dset['tas']<=273.15, other=np.nan)
#     t2 = t2.where(dset['tas']<=273.15, other=np.nan)
#     # print(3,'sf', sf.min(skipna=True).values, sf.max(skipna=True).values, 'lwp', lwp.min(skipna=True).values, lwp.max(skipna=True).values, 't2', t2.min(skipna=True).values, t2.max(skipna=True).values)
    
#     sf_count = sf.groupby('time.season').count(dim='time',keep_attrs=True)
#     # lwp_count = lwp.groupby('time.season').count(dim='time',keep_attrs=True)
#     # iwp_count = iwp.groupby('time.season').count(dim='time', keep_attrs=True)
#     # t2_count = t2.groupby('time.season').count(dim='time', keep_attrs=True)
    
#     return(sf_count, sf, iwp, lwp, twp)

How often do we have SLC with thresholds for surface snowfall, liquid water path, and temperature#

cumm = dict()
for model in dset_dict.keys():

    # create dataset with all cumm
    cumm[model] = xr.Dataset()
    # count the days per season when there was a LCC, and 2t<=0C, and where 10% per season a lcc cloud existed when also the surface temp was below freezing
    cumm[model]['lcc_days'] = (dset_dict_lcc_2t_days[model]['twp'].groupby('time.season').count(dim='time',keep_attrs=False))/days_season
    print(model, 'min:', cumm[model]['lcc_days'].min().round(3).values,
        'max:', cumm[model]['lcc_days'].max().round(3).values,  
        'std:', cumm[model]['lcc_days'].std(skipna=True).round(3).values, 
        'mean:', cumm[model]['lcc_days'].mean(skipna=True).round(3).values)
    
        
    figname = '{}_cum_lcc_days_season_{}_{}.png'.format(model,starty, endy)
    fct.plt_seasonal_NH_SH((cumm[model]['lcc_days'].where(cumm[model]['lcc_days'] > 0.))*100, levels=np.arange(0,110,10), cbar_label='Frequency of liquid containing cloud days (%)', plt_title='{} ({} - {})'.format(model, starty,endy), extend=None)
    plt.savefig(FIG_DIR + figname, format = 'png', bbox_inches = 'tight', transparent = False)

    cumm[model]['sf_days'] = (dset_dict_lcc_2t_sf_days[model]['twp'].groupby('time.season').count(dim='time',keep_attrs=False))/days_season
    print(model,'min:', cumm[model]['sf_days'].min().round(3).values,
        'max:', cumm[model]['sf_days'].max().round(3).values,  
        'std:', cumm[model]['sf_days'].std(skipna=True).round(3).values, 
        'mean:', cumm[model]['sf_days'].mean(skipna=True).round(3).values)
    figname = '{}_cum_sf_days_season_{}_{}.png'.format(model,starty, endy)
    fct.plt_seasonal_NH_SH((cumm[model]['sf_days'].where(cumm[model]['sf_days'] >0.))*100, levels=np.arange(0,110,10), cbar_label='Frequency of snowfall days from LCCs (%)', plt_title='{} ({} - {})'.format(model,starty,endy), extend=None)
    plt.savefig(FIG_DIR + figname, format = 'png', bbox_inches = 'tight', transparent = False)
MIROC6 min: 0.0 max: 0.25 std: 0.064 mean: 0.031
MIROC6 min: 0.0 max: 0.236 std: 0.043 mean: 0.014
CESM2 min: 0.0 max: 0.84 std: 0.047 mean: 0.004
CESM2 min: 0.0 max: 0.0 std: 0.0 mean: 0.0
CanESM5 min: 0.0 max: 0.928 std: 0.246 mean: 0.223
CanESM5 min: 0.0 max: 0.719 std: 0.181 mean: 0.15
AWI-ESM-1-1-LR min: 0.0 max: 0.25 std: 0.081 mean: 0.054
AWI-ESM-1-1-LR min: 0.0 max: 0.201 std: 0.027 mean: 0.006
MPI-ESM1-2-LR min: 0.0 max: 0.973 std: 0.304 mean: 0.246
MPI-ESM1-2-LR min: 0.0 max: 0.679 std: 0.138 mean: 0.098
IPSL-CM5A2-INCA min: 0.0 max: 0.931 std: 0.243 mean: 0.191
IPSL-CM5A2-INCA min: 0.0 max: 0.919 std: 0.232 mean: 0.179
../_images/dd9175e4800cc57d9ffb051ff11a67fcb205449fcafd3c647bd96f42e5ce969a.png ../_images/d4d96eed47beba9903a7c8520f5a7065f55f9802e904332331c791b7a98ecfbb.png ../_images/d2efe8d10fc677d910f2ae177b81557327d33bcfec6b676d23e08b1a193d09ad.png ../_images/3aac050b9a5df9d69aacf57fa33405f0e6b464432c7d5fba68ed2168c875b8fd.png ../_images/c7577328796c8a24a98fdcc0b328294bdc075c63e366dc85ef4a040a6fd03586.png ../_images/58be859327a4e61fc94c5d437f455748e34bc5e8b815c7c1f01496a6ad0550f5.png ../_images/b1488c3aed3c431a383f18e8d63a65352e92288e485c8932b2683d070bf9e068.png ../_images/fb3e3c9b65509e1ba46e96052a5cba6eb5a7f08cddd86fcbb5fdf1f8c18f6271.png ../_images/889cc7f40448db189c2115ec64d34b464a44be008cc67efda9317a606011dd90.png ../_images/492a3d2cf9a8bf68c6646bb3b468b2d862b22b6898d6e7c04017d82fa67374e0.png ../_images/f2d87978cdab159d8cb91a84955727d9cd2cd0da16e973342cabdc5dd49a5a4f.png ../_images/fc11b37e0812c9ee814cea453038403a9d95ab485d3ddd376a5d332c69242fa1.png

Liquid containing cloud frequency#

Relative frequency

\[RF = \frac{f}{n} = \frac{\text{number of times the data occurred in an observation}}{\text{total frequency}}\]
ratios = dict()
for model in dset_dict.keys():
    ratios[model] = xr.Dataset()
    # relative frequency of liquid containing clouds per season
    f = dset_dict_lcc_2t_days[model]['lwp'].groupby('time.season').count(dim='time', keep_attrs=False) # number of times a super cooled liquid water cloud occured when the liquid water path is larger than 5gm-2, and temperature below 0degC
    n = dset_dict_lcc[model]['lwp'].groupby('time.season').count(dim='time', keep_attrs=False)         # frequency of liquid clouds (only LWP threshold applied) when lwp is larger than 5gm-2

    ratios[model]['lcc_wo_snow_season'] = f/n

    print('min:', ratios[model]['lcc_wo_snow_season'].min().round(3).values,
        'max:', ratios[model]['lcc_wo_snow_season'].max().round(3).values,  
        'std:', ratios[model]['lcc_wo_snow_season'].std(skipna=True).round(3).values, 
        'mean:', ratios[model]['lcc_wo_snow_season'].mean(skipna=True).round(3).values)
    
    fct.plt_seasonal_NH_SH((ratios[model]['lcc_wo_snow_season'].where(ratios[model]['lcc_wo_snow_season']>0.))*100, np.arange(0,110,10), 'Liquid containing cloud frequency (%)', plt_title='{} ({} - {})'.format(model,starty,endy), extend=None)

    figname = '{}_rf_lcc_wo_snow_season_{}_{}.png'.format(model,starty, endy)
    plt.savefig(FIG_DIR + figname, format = 'png', bbox_inches = 'tight', transparent = False)

    # relative frequency of liquid containing clouds per month
    f = dset_dict_lcc_2t_days[model]['lwp'].groupby('time.month').count(dim='time', keep_attrs=False) # number of times a super cooled liquid water cloud occured when the liquid water path is larger than 5gm-2, and temperature below 0degC
    n = dset_dict_lcc[model]['lwp'].groupby('time.month').count(dim='time', keep_attrs=False)         # frequency of liquid clouds (only LWP threshold applied) when lwp is larger than 5gm-2

    ratios[model]['lcc_wo_snow_month'] = f/n

    print('min:', ratios[model]['lcc_wo_snow_month'].min().round(3).values,
        'max:', ratios[model]['lcc_wo_snow_month'].max().round(3).values,  
        'std:', ratios[model]['lcc_wo_snow_month'].std(skipna=True).round(3).values, 
        'mean:', ratios[model]['lcc_wo_snow_month'].mean(skipna=True).round(3).values)
min: 0.0 max: 1.0 std: 0.367 mean: 0.186
min: 0.0 max: 1.0 std: 0.377 mean: 0.193
min: 0.0 max: 1.0 std: 0.112 mean: 0.013
min: 0.0 max: 1.0 std: 0.113 mean: 0.013
min: 0.0 max: 1.0 std: 0.443 mean: 0.473
min: 0.0 max: 1.0 std: 0.458 mean: 0.493
min: 0.0 max: 1.0 std: 0.454 mean: 0.364
min: 0.0 max: 1.0 std: 0.466 mean: 0.379
min: 0.0 max: 1.0 std: 0.449 mean: 0.472
min: 0.0 max: 1.0 std: 0.463 mean: 0.486
min: 0.0 max: 1.0 std: 0.425 mean: 0.43
min: 0.0 max: 1.0 std: 0.447 mean: 0.449
../_images/6fce2440fe1759da3f8a42581f797d769a4c20af670432cba7a90e64be3f6cc1.png ../_images/6c7470a559ca4fb1fd4f4fbcde35db923ccbfa3dad6276645b411ef319953b45.png ../_images/803e471c115f7517e77c7d0f1d4a201a74b6ac9331818c3970d751c84a96d99a.png ../_images/fe5ef6f8b90425487326af5992cab46ee961e9ea68144ce946c8c2fbb45e45af.png ../_images/1c7a3f748e299a4d09f5680cea89ea76fc042f80e196b042d48cc4402275fc4d.png ../_images/217c4d91fef496d3893ce075e1b16615e24c1f795719161fb2b10a8b542b88cb.png

Masked and weighted average#

Make use of the xarray function weighted, but use the weights from CMIP6.

NH_mean = dict()
SH_mean = dict()
NH_std = dict()
SH_std = dict()
weights = dict()

for model in dset_dict.keys():
    NH_mean[model] = xr.Dataset()
    SH_mean[model] = xr.Dataset()

    NH_std[model] = xr.Dataset()
    SH_std[model] = xr.Dataset()

    # Grid cells have different area, so when we do the global average, they have to be weigted by the area of each grid cell.
    # weights[model] = fct.area_grid(ratios['lat'].values, ratios['lon'].values)
    weights[model] = dset_dict[model].areacella

    NH_mean[model]['lcc_wo_snow_month'] = ratios[model]['lcc_wo_snow_month'].sel(lat=slice(45,90)).weighted(weights[model].fillna(0)).mean(('lat', 'lon'))
    SH_mean[model]['lcc_wo_snow_month'] = ratios[model]['lcc_wo_snow_month'].sel(lat=slice(-90,-45)).weighted(weights[model].fillna(0)).mean(('lat', 'lon'))

    NH_std[model]['lcc_wo_snow_month'] = ratios[model]['lcc_wo_snow_month'].sel(lat=slice(45,90)).weighted(weights[model].fillna(0)).std(('lat', 'lon'))
    SH_std[model]['lcc_wo_snow_month'] = ratios[model]['lcc_wo_snow_month'].sel(lat=slice(-90,-45)).weighted(weights[model].fillna(0)).std(('lat', 'lon'))

Annual cycle of frequency of liquid containing clouds#

for model in dset_dict.keys():

    fct.plt_annual_cycle(NH_mean[model]['lcc_wo_snow_month'], 
                         SH_mean[model]['lcc_wo_snow_month'], 
                         NH_std[model]['lcc_wo_snow_month'], 
                         SH_std[model]['lcc_wo_snow_month'], 
                         'Liquid containing cloud frequency','{} ({} - {})'.format(model,starty,endy))
    figname = '{}_rf_lcc_wo_snow_annual_{}_{}.png'.format(model,starty, endy)
    plt.savefig(FIG_DIR + figname, format = 'png', bbox_inches = 'tight', transparent = False)
../_images/f0493904ce54372d8f2c550bbe7d1db704dcdd5d7b562e174c02d7c56a59044b.png ../_images/6bfa17ce1ea4adf3badca754fa8ced7e63268d3bac270d86077ae5cccf035020.png ../_images/a48ffbc618975e13bd63689a4cfe0216883a1fa66bdb3a1876899416b99c6b9f.png ../_images/6536cd4a60eae274138024655266f9805bdb73b4351ae49c5e6222a276f3ebba.png ../_images/be3060e77d4a47e7249b5e9fa0fc0a267fc2679f1327936ba0afbec38ceb0103.png ../_images/ef2d3005876179e8c3f8be97ca600192ceddb13de6912fad8fadff8185f0e512.png

Frequency of snowfall from LCCs#

for model in dset_dict.keys():

      # relative frequency of snowing liquid containing clouds per season
      f = dset_dict_lcc_2t_sf_days[model]['prsn'].groupby('time.season').count(dim='time', keep_attrs=False)  # number of times a super cooled liquid water cloud occured when the liquid water path is larger than 5gm-2, and temperature below 0degC
      n = dset_dict_lcc_2t_days[model]['lwp'].groupby('time.season').count(dim='time', keep_attrs=False)

      ratios[model]['lcc_w_snow_season'] = f/n
      print('min:', ratios[model]['lcc_w_snow_season'].min().round(3).values,
            'max:', ratios[model]['lcc_w_snow_season'].max().round(3).values,  
            'std:', ratios[model]['lcc_w_snow_season'].std(skipna=True).round(3).values, 
            'mean:', ratios[model]['lcc_w_snow_season'].mean(skipna=True).round(3).values)

      # 
      fct.plt_seasonal_NH_SH((ratios[model]['lcc_w_snow_season'].where(ratios[model]['lcc_w_snow_season']>0.))*100, np.arange(0,110,10), 'Frequency of snowfall from LCCs (%)', plt_title='{} ({} - {})'.format(model,starty,endy), extend=None)

      figname = '{}_rf_lcc_w_snow_season_{}_{}.png'.format(model,starty, endy)
      plt.savefig(FIG_DIR + figname, format = 'png', bbox_inches = 'tight', transparent = False)

      # relative frequency of liquid containing clouds per month
      f = dset_dict_lcc_2t_sf_days[model]['prsn'].groupby('time.month').count(dim='time', keep_attrs=False) # number of times a super cooled liquid water cloud occured when the liquid water path is larger than 5gm-2, and temperature below 0degC
      n = dset_dict_lcc_2t_days[model]['lwp'].groupby('time.month').count(dim='time', keep_attrs=False)         # frequency of liquid clouds (only LWP threshold applied) when lwp is larger than 5gm-2

      ratios[model]['lcc_w_snow_month'] = f/n

      print('min:', ratios[model]['lcc_w_snow_month'].min().round(3).values,
            'max:', ratios[model]['lcc_w_snow_month'].max().round(3).values,  
            'std:', ratios[model]['lcc_w_snow_month'].std(skipna=True).round(3).values, 
            'mean:', ratios[model]['lcc_w_snow_month'].mean(skipna=True).round(3).values)

      # Grid cells have different area, so when we do the global average, they have to be weigted by the area of each grid cell.
      weights[model] = dset_dict[model].areacella

      NH_mean[model]['lcc_w_snow_month'] = ratios[model]['lcc_w_snow_month'].sel(lat=slice(45,90)).weighted(weights[model].fillna(0)).mean(('lat', 'lon'))
      SH_mean[model]['lcc_w_snow_month'] = ratios[model]['lcc_w_snow_month'].sel(lat=slice(-90,-45)).weighted(weights[model].fillna(0)).mean(('lat', 'lon'))

      NH_std[model]['lcc_w_snow_month'] = ratios[model]['lcc_w_snow_month'].sel(lat=slice(45,90)).weighted(weights[model].fillna(0)).std(('lat', 'lon'))
      SH_std[model]['lcc_w_snow_month'] = ratios[model]['lcc_w_snow_month'].sel(lat=slice(-90,-45)).weighted(weights[model].fillna(0)).std(('lat', 'lon'))
min: 0.0 max: 1.0 std: 0.394 mean: 0.4
min: 0.0 max: 1.0 std: 0.398 mean: 0.394
min: 0.0 max: 0.0 std: 0.0 mean: 0.0
min: 0.0 max: 0.0 std: 0.0 mean: 0.0
min: 0.0 max: 1.0 std: 0.257 mean: 0.637
min: 0.0 max: 1.0 std: 0.272 mean: 0.64
min: 0.0 max: 1.0 std: 0.242 mean: 0.104
min: 0.0 max: 1.0 std: 0.248 mean: 0.104
min: 0.0 max: 0.981 std: 0.241 mean: 0.381
min: 0.0 max: 1.0 std: 0.252 mean: 0.381
min: 0.0 max: 1.0 std: 0.153 mean: 0.917
min: 0.0 max: 1.0 std: 0.157 mean: 0.922
../_images/12d2de82193031b80ebdc078db176e6ea3999e1d52e06736828fdc091a7dc97a.png ../_images/0f6dcbb721b1d7676641f0fa95c655e669274916cf17ced30dba3db49da940c2.png ../_images/3b9d34753901db807172e667ac86390a767490fdba09c0c3779f80bfe13d49de.png ../_images/17fe1756fd028cd9088687d821cfec6d0f6e731a34f5257632158ca47cabd6d5.png ../_images/f1784b1fe647a8703257d54d3c14156bc75e0fef5fa27407260ac90e66c9534d.png ../_images/b1cf962224ec3a6792f28146fc49a0551d9c9ade01cfdfdd7d94b99464ce532d.png

Annual cycle of frequency of snowfall from LCCs#

for model in dset_dict.keys():

    fct.plt_annual_cycle(NH_mean[model]['lcc_w_snow_month'], SH_mean[model]['lcc_w_snow_month'], NH_std[model]['lcc_w_snow_month'], SH_std[model]['lcc_w_snow_month'], 'Frequency of snowfall from LCCs','{} ({} - {})'.format(model,starty,endy))
    figname = '{}_rf_lcc_wo_snow_annual_{}_{}.png'.format(model,starty, endy)
    plt.savefig(FIG_DIR + figname, format = 'png', bbox_inches = 'tight', transparent = False)
../_images/9cc914dd8e86039ea9a1c717063f1dbc13d73c68f87ff26eb0c8c5fb04b2b30f.png ../_images/1943f61258355dc0fa1b1da88b580840b8d2bf2a730355964906b9e64195e539.png ../_images/7d0885fa52943aec70a450ec35b9cba6d789faa2dc9d1b44b5f859a684a91b1f.png ../_images/b3b74e47c8c9df3bf19bf01aa6ded8604b0d4649d64a7eda1e900136536315a1.png ../_images/d64befd318e83cf7f0b29cfef54bbaffd019d300cb5b7ba04851fde2b896520c.png ../_images/fdd3cc6ab337276c8504ccd4a0497c4bdd04d90e1639ab28559ef1fc59cf33d6.png

Relative snowfall efficency#

The precipitation efficiency of convection is a measure of how much of the water that condenses in a rising column of air reaches the surface as precipitation [3].

In our case, we calculate the snowfall efficency by deviding the mean surface snowfall rate (sf, with units kg m-2 h-1) with the column integrated total water path (TWP, with units kg m-2). This leaves us with units of h-1, which means we get a daily mean relative snowfall efficency per hour.

\[\eta = \frac{sf}{TWP}\]
for model in dset_dict.keys():

      # snowfall efficency per season
      # first averavge over time and then fraction remove weighting only for spatial average

      ratios[model]['sf_eff_season'] = dset_dict_lcc_2t_season[model]['prsn']/dset_dict_lcc_2t_season[model]['twp']
      # plt_seasonal_NH_SH(dset_dict_lcc_2t_sf_season['prsn']/dset_dict_lcc_2t_sf_season['twp'], np.arange(0,1, 0.1),'','')
      print('min:', ratios[model]['sf_eff_season'].min().round(3).values,
            'max:', ratios[model]['sf_eff_season'].max().round(3).values,  
            'std:', ratios[model]['sf_eff_season'].std(skipna=True).round(3).values, 
            'mean:', ratios[model]['sf_eff_season'].mean(skipna=True).round(3).values)

      fct.plt_seasonal_NH_SH(ratios[model]['sf_eff_season'].where(ratios[model]['sf_eff_season']>0.), np.arange(0,1.1,0.1), 'relative snowfall eff. (h$^{-1}$)', '{} ({} - {})'.format(model, starty,endy), extend='max')
      # save precip efficency from mixed-phase clouds figure
      figname = '{}_sf_twp_season_{}_{}.png'.format(model, starty, endy)
      plt.savefig(FIG_DIR + figname, format = 'png', bbox_inches = 'tight', transparent = False)
min: 0.162 max: 6.483 std: 0.446 mean: 0.777
min: 0.557 max: 0.557 std: 0.0 mean: 0.557
min: 0.01 max: 1.745 std: 0.236 mean: 0.569
min: 0.0 max: 2.899 std: 0.262 mean: 0.425
min: 0.0 max: 2.607 std: 0.273 mean: 0.476
min: 0.14 max: 1.231 std: 0.115 mean: 0.422
../_images/b1f11fd8418299c7065a338fbbcf44455416d8a5f171afb234c2e7e9361e62cd.png ../_images/206f8665ead18cb5221040c9d73a6bc74df71feec9f406cd37b43f594a4bac97.png ../_images/e0f22a8f5007a2e87ce7a3ee9d7511d8145abb042a7feb9201692ca3ecf2b88d.png ../_images/6346eb79604fb08a5c2a774d5fca4cc231cfce0aaa0a1f8ec58a1efd7329a995.png ../_images/37ae30a6ac86e0ee065571579c2930073ce22ce12ba64dfbd967e50710ae7343.png ../_images/3c5a31c7c1306f7489a9ac152d3993437f11fb8c84e9e10e34ab3f343265fa30.png

Annual cycle of relative snowfall efficency#

for model in dset_dict.keys():

      f = dset_dict_lcc_2t[model]['prsn'].groupby('time.month').sum(dim='time', skipna=True, keep_attrs=False)
      n = dset_dict_lcc_2t[model]['twp'].groupby('time.month').sum(dim='time', skipna=True, keep_attrs=False)
      ratios[model]['sf_eff_month'] = f/n
      NH_mean['sf_eff_month'] = ratios[model]['sf_eff_month'].sel(lat=slice(45,90)).weighted(weights[model].fillna(0)).mean(('lat', 'lon'))
      SH_mean['sf_eff_month'] = ratios[model]['sf_eff_month'].sel(lat=slice(-90,-45)).weighted(weights[model].fillna(0)).mean(('lat', 'lon'))

      NH_std['sf_eff_month'] = ratios[model]['sf_eff_month'].sel(lat=slice(45,90)).weighted(weights[model].fillna(0)).std(('lat', 'lon'))
      SH_std['sf_eff_month'] = ratios[model]['sf_eff_month'].sel(lat=slice(-90,-45)).weighted(weights[model].fillna(0)).std(('lat', 'lon'))

      print('min:', ratios[model]['sf_eff_month'].min().round(3).values,
            'max:', ratios[model]['sf_eff_month'].max().round(3).values,  
            'std:', ratios[model]['sf_eff_month'].std(skipna=True).round(3).values, 
            'mean:', ratios[model]['sf_eff_month'].mean(skipna=True).round(3).values)

      # for year in np.unique(dset_dict['time.year']):
      #     # print(year)
      #     f = dset_dict_lcc_2t['prsn'].sel(time=slice(str(year))).groupby('time.month').sum(dim='time', skipna=True, keep_attrs=False)
      #     n = dset_dict_lcc_2t['twp'].sel(time=slice(str(year))).groupby('time.month').sum(dim='time', skipna=True, keep_attrs=False)

      #     _ratio = f/n
      
      #     NH_mean['sf_eff_month_'+str(year)] = _ratio.sel(lat=slice(45,90)).weighted(weights).mean(('lat', 'lon'))
      #     SH_mean['sf_eff_month_'+str(year)] = _ratio.sel(lat=slice(-90,-45)).weighted(weights).mean(('lat', 'lon'))
      
      #     NH_std['sf_eff_month_'+str(year)] = _ratio.sel(lat=slice(45,90)).weighted(weights).std(('lat', 'lon'))
      #     SH_std['sf_eff_month_'+str(year)] = _ratio.sel(lat=slice(-90,-45)).weighted(weights).std(('lat', 'lon'))

      fct.plt_annual_cycle(NH_mean['sf_eff_month'], SH_mean['sf_eff_month'], NH_std['sf_eff_month'], SH_std['sf_eff_month'], 'relative snowfall eff. (h$^{-1}$)', '{} ({} - {})'.format(model,starty,endy))
      figname = '{}_sf_twp_annual_{}_{}.png'.format(model,starty, endy)
      plt.savefig(FIG_DIR + figname, format = 'png', bbox_inches = 'tight', transparent = False)
min: 0.0 max: 84.79 std: 1.97 mean: 1.357
min: 0.0 max: 1.504 std: 0.438 mean: 0.316
min: 0.0 max: 5.067 std: 0.284 mean: 0.557
min: 0.0 max: 4.663 std: 0.35 mean: 0.453
min: 0.0 max: 3.122 std: 0.301 mean: 0.464
min: 0.0 max: 1.769 std: 0.172 mean: 0.437
../_images/b56b77d7f9771508638e4f8353517da6277e7324de4786504e9c9c9f2c260555.png ../_images/618f3dfdad592766363c813727e276caab4e519762e93c87c0cba66d448350de.png ../_images/f36ccdda221bc984a5c33c25f07a2d1100a431e5dbcd48330dcd29fa11d0fba7.png ../_images/127fab0fffa1361f79f3487bf129ddb17cb78b7fcd1337cfd8c1b1ed85379ba9.png ../_images/ca0e845bdb0351c3d98d8d66ee14e173315bd36ac2a579a1c04b5085d48cb857.png ../_images/c5d0ef6b3a5a1e2c4653add1784fb2392ec9300d02da2110b72059469b578fd8.png

Relative precipitation efficency#

Use of total precipitation, but where LWP \(\ge\) 0.005 gm-2, 2-meter temperature \(\le\) 273.15K.

for model in dset_dict.keys():

      # supercooled precipitation efficency per season
      # first averavge over time and then fraction remove weighting only for spatial average

      ratios[model]['tp_eff_season'] = dset_dict_lcc_2t_season[model]['pr']/dset_dict_lcc_2t_season[model]['twp']
      # plt_seasonal_NH_SH(dset_dict_lcc_2t_tp_season['msr']/dset_dict_lcc_2t_tp_season['twp'], np.arange(0,1, 0.1),'','')
      print('min:', ratios[model]['tp_eff_season'].min().round(3).values,
            'max:', ratios[model]['tp_eff_season'].max().round(3).values,  
            'std:', ratios[model]['tp_eff_season'].std(skipna=True).round(3).values, 
            'mean:', ratios[model]['tp_eff_season'].mean(skipna=True).round(3).values)

      # Plot precipitation efficency

      fct.plt_seasonal_NH_SH(ratios[model]['tp_eff_season'].where(ratios[model]['tp_eff_season'] >0.), np.arange(0,1.1,0.1), 'relative supercooled precipitation eff. (h$^{-1}$)', '{} ({} - {})'.format(model, starty,endy), extend='max')
      # save precip efficency from mixed-phase clouds figure
      figname = '{}_tp_twp_season_{}_{}.png'.format(model, starty, endy)
      plt.savefig(FIG_DIR + figname, format = 'png', bbox_inches = 'tight', transparent = False)
min: 0.178 max: 6.485 std: 0.445 mean: 0.799
min: nan max: nan std: nan mean: nan
min: 0.115 max: 1.88 std: 0.248 mean: 0.633
min: 0.09 max: 3.051 std: 0.265 mean: 0.531
min: 0.117 max: 2.692 std: 0.273 mean: 0.581
min: 0.212 max: 1.231 std: 0.111 mean: 0.454
../_images/a5431901b13212b6c3106e51c6b7f360d000bea8a57a42a788480e843e25da98.png ../_images/e2200ff7c7948e99df60ef64d8b4b8d5586d54a195018d0a7bae2cbf21019451.png ../_images/4d2008b2822d454a61f6539f1d9c4577c6fb444da0b4e62300df02e6236c5e3d.png ../_images/e3ff80deb9a7fac43d171c250e1a339083a5db6c07165de4ffba4f4fbf9324c7.png ../_images/24ea0367b1f0fbe94a4ef00a860bc3b18e992f4c598c447b4327030f33ee6f11.png ../_images/80adf04565772775a19f1b12fa8d6f190dd534e978b0ef34ab1aebbce109eadc.png

Annual cycle of relative supercooled precipitation efficency#

for model in dset_dict.keys():

      f = dset_dict_lcc_2t[model]['pr'].groupby('time.month').sum(dim='time', skipna=True, keep_attrs=False)
      n = dset_dict_lcc_2t[model]['twp'].groupby('time.month').sum(dim='time', skipna=True, keep_attrs=False)
      ratios[model]['tp_eff_month'] = f/n
      NH_mean[model]['tp_eff_month'] = ratios[model]['tp_eff_month'].sel(lat=slice(45,90)).weighted(weights[model].fillna(0)).mean(('lat', 'lon'))
      SH_mean[model]['tp_eff_month'] = ratios[model]['tp_eff_month'].sel(lat=slice(-90,-45)).weighted(weights[model].fillna(0)).mean(('lat', 'lon'))

      NH_std[model]['tp_eff_month'] = ratios[model]['tp_eff_month'].sel(lat=slice(45,90)).weighted(weights[model].fillna(0)).std(('lat', 'lon'))
      SH_std[model]['tp_eff_month'] = ratios[model]['tp_eff_month'].sel(lat=slice(-90,-45)).weighted(weights[model].fillna(0)).std(('lat', 'lon'))

      print('min:', ratios[model]['tp_eff_month'].min().round(3).values,
            'max:', ratios[model]['tp_eff_month'].max().round(3).values,  
            'std:', ratios[model]['tp_eff_month'].std(skipna=True).round(3).values, 
            'mean:', ratios[model]['tp_eff_month'].mean(skipna=True).round(3).values)
      # for year in np.unique(dset_dict['time.year']):
      #     # print(year)
      #     f = dset_dict_lcc_2t[model]['pr'].sel(time=slice(str(year))).groupby('time.month').sum(dim='time', skipna=True, keep_attrs=False)
      #     n = dset_dict_lcc_2t[model]['twp'].sel(time=slice(str(year))).groupby('time.month').sum(dim='time', skipna=True, keep_attrs=False)

      #     _ratio = f/n
      
      #     NH_mean[model]['tp_eff_month_'+str(year)] = _ratio.sel(lat=slice(45,90)).weighted(weights[model]).mean(('lat', 'lon'))
      #     SH_mean[model]['tp_eff_month_'+str(year)] = _ratio.sel(lat=slice(-90,-45)).weighted(weights[model]).mean(('lat', 'lon'))
      
      #     NH_std[model]['tp_eff_month_'+str(year)] = _ratio.sel(lat=slice(45,90)).weighted(weights[model]).std(('lat', 'lon'))
      #     SH_std[model]['tp_eff_month_'+str(year)] = _ratio.sel(lat=slice(-90,-45)).weighted(weights[model]).std(('lat', 'lon'))

      fct.plt_annual_cycle(NH_mean[model]['tp_eff_month'], SH_mean[model]['tp_eff_month'], NH_std[model]['tp_eff_month'], SH_std[model]['tp_eff_month'], 'relative supercooled precipitation eff. (h$^{-1}$)', '{} ({} - {})'.format(model,starty,endy))
      figname = '{}_tp_twp_annual_{}_{}.png'.format(model,starty, endy)
      plt.savefig(FIG_DIR + figname, format = 'png', bbox_inches = 'tight', transparent = False)
min: 0.014 max: 84.796 std: 1.971 mean: 1.381
min: 0.0 max: 0.0 std: 0.0 mean: 0.0
min: 0.0 max: 9.465 std: 0.398 mean: 0.658
min: 0.0 max: 12.083 std: 0.367 mean: 0.556
min: 0.0 max: 6.72 std: 0.307 mean: 0.568
min: 0.0 max: 1.833 std: 0.161 mean: 0.467
../_images/c829a05e743999740c56285c90872a39b4cdd8eef701d2528358298c4ea63cf6.png ../_images/f4c9f243b990beb96874673f05d3ad3febac10e05cb30c4bb83dfa8ee2b880ee.png ../_images/b19ecd1837ebda683d20ee4cb33b42f57755ca5fe71b4728e8d0dceca2dd536c.png ../_images/4a47bcce127b0d4f2c869cdbe77916d60d3bb8ed9a57550ec2a6e1611f3d8513.png ../_images/9481f0fdf20ff53f75898577adc8c88df539102a820bf99245fed31f8b9dfb7c.png ../_images/81ede660402cf049ac87325ecd7774a47153785f7fc9ce4eee8a19b03e547886.png

Frequency of freezing rain#

Use of the thresholds.

\[RF = \frac{\text{total precipitation}(T\le273.15; LWP\ge0.005gm^{-2}) - \text{snowfall}(T\le273.15; LWP\ge0.005gm^{-2})}{\text{total precipitation}(T\le273.15; LWP\ge0.005gm^{-2})}\]
for model in dset_dict.keys():

      # first count the days where we have freezing rain per season and relate to total precipitation days
      fr_d = dset_dict_lcc_2t_days[model]['mfrr'].where(dset_dict_lcc_2t_days[model]['mfrr']>0.).groupby('time.season').count('time',keep_attrs=False)
      tp_d = dset_dict_lcc_2t_days[model]['pr'].where(dset_dict_lcc_2t_days[model]['pr']>0.).groupby('time.season').count('time',keep_attrs=False)

      ratios[model]['mfrr_pr_days'] = fr_d/tp_d

      print('min:', ratios[model]['mfrr_pr_days'].min().round(3).values,
            'max:', ratios[model]['mfrr_pr_days'].max().round(3).values,  
            'std:', ratios[model]['mfrr_pr_days'].std(skipna=True).round(3).values, 
            'mean:', ratios[model]['mfrr_pr_days'].mean(skipna=True).round(3).values)

      fct.plt_seasonal_NH_SH(ratios[model]['mfrr_pr_days'].where(ratios[model]['mfrr_pr_days']>0)*100, np.arange(0,110,10), 'Freezing rain days (%)', '{} ({} - {})'.format(model,starty,endy), extend=None)
      figname = '{}_mfrr_mtpr_days_{}_{}.png'.format(model,starty, endy)
      plt.savefig(FIG_DIR + figname, format = 'png', bbox_inches = 'tight', transparent = False)

      # then bring the amount of freezing rain into relation to the total precipitation (How many percent is freezing rain in comparison to total precipitation)
      fr = dset_dict_lcc_2t_days[model]['mfrr'].where(dset_dict_lcc_2t_days[model]['mfrr']>0.)
      tp = dset_dict_lcc_2t_days[model]['pr'].where(dset_dict_lcc_2t_days[model]['pr']>0.)

      ratios[model]['mfrr_pr_frac'] = (fr/tp).groupby('time.season').mean('time',skipna=True,keep_attrs=False)
      print('min:', ratios[model]['mfrr_pr_frac'].min().round(3).values,
            'max:', ratios[model]['mfrr_pr_frac'].max().round(3).values,  
            'std:', ratios[model]['mfrr_pr_frac'].std(skipna=True).round(3).values, 
            'mean:', ratios[model]['mfrr_pr_frac'].mean(skipna=True).round(3).values)

      fct.plt_seasonal_NH_SH(ratios[model]['mfrr_pr_frac'].where(ratios[model]['mfrr_pr_frac']>0)*100, np.arange(0,110,10), 'Freezing rain fraction (%)', '{} ({} - {})'.format(model,starty,endy), extend=None)
      figname = '{}_mfrr_mtpr_amount_season_{}_{}.png'.format(model,starty, endy)
      plt.savefig(FIG_DIR + figname, format = 'png', bbox_inches = 'tight', transparent = False)
min: 0.268 max: 0.972 std: 0.101 mean: 0.573
min: 0.0 max: 0.381 std: 0.054 mean: 0.05
min: nan max: nan std: nan mean: nan
min: nan max: nan std: nan mean: nan
min: 0.568 max: 1.0 std: 0.072 mean: 0.932
min: 0.0 max: 0.918 std: 0.182 mean: 0.224
min: 0.595 max: 1.0 std: 0.046 mean: 0.958
min: 0.03 max: 1.0 std: 0.188 mean: 0.463
min: 0.509 max: 1.0 std: 0.052 mean: 0.947
min: 0.015 max: 1.0 std: 0.174 mean: 0.447
min: 0.0 max: 0.977 std: 0.19 mean: 0.209
min: 0.0 max: 1.0 std: 0.17 mean: 0.233
../_images/7185615929a5ffb3537940fa1bbff38297c968f3f0cd8803ebe8d564b69f4244.png ../_images/d90ecf9c0e339b276994e5985109642d6974c51070893a3d7a11a071a56ecb64.png ../_images/126d8efacb6172fabc42e9c73b4cb6ef1fa30747e62b436067267ac1ae7e0b08.png ../_images/4fac27c17fd004d217adde823409458ccaf39a77781596e26552440e12871673.png ../_images/18bffeced3856ca2e9908126a42152f94a870253a659a193c62b7b02d14385de.png ../_images/2f02edc23e0e4efdf313f6a816e5e41f925b1d70a044c70b250c2dc8f1e16d69.png ../_images/c3b5c3389b45589660b586a0a3cff5f6aadf742c898c993a6cb5b298d253c573.png ../_images/a9b3d73425739aa16b967faeedaaba9638b4b65535dbc7ef2d4e8648d846a5b4.png ../_images/62af6a6301ec5851fa41420ee98129973b5ccc3188e114d9309dcdf25bfdb2c1.png ../_images/cd4e7d0db960bde339208a85a146f806f3214889bea90d81c11c1dbce3845be2.png ../_images/2f755fa51655d6c4a579b8f193f30a7a2fc4d0bbad8e9b2e587cb92554348852.png ../_images/429907ff146bbfd56b1c5c3a69fc3c58990b882325680824e008813564cbe649.png
for model in dset_dict.keys():
    dset_dict[model]['lcc_count'],dset_dict[model]['sf_lcc'],dset_dict[model]['iwp_lcc'],dset_dict[model]['lwp_lcc'],dset_dict[model]['twp_lcc'] = find_precip_cloud(dset_dict[model])
def find_precip_cloud2(dset):
    # 1. find where LWP >=5 gm-2
    sf = dset['prsn'].where(dset['lwp']>=0.005, other=np.nan)
    lwp = dset['lwp'].where(dset['lwp']>=0.005, other=np.nan)
    iwp = dset['clivi'].where(dset['lwp']>=0.005, other=np.nan)
    twp = dset['twp'].where(dset['lwp']>=0.005, other=np.nan)
    t2 = dset['tas'].where(dset['lwp']>=0.005, other=np.nan)
    
    # print(1,'sf', sf.min(skipna=True).values, sf.max(skipna=True).values, 'lwp', lwp.min(skipna=True).values, lwp.max(skipna=True).values, 't2', t2.min(skipna=True).values, t2.max(skipna=True).values)
    # 2. find where snowfall >= 0.01mms-1
    # unit_sf = dset['prsn']
    # sf = sf.where(unit_sf>=0.01*24, other=np.nan)
    # lwp = lwp.where(unit_sf>=0.01*24, other=np.nan)
    # iwp = iwp.where(unit_sf>=0.01*24, other=np.nan)
    # twp = twp.where(unit_sf>0.01*24, other=np.nan)
    # t2 = t2.where(unit_sf>=0.01*24, other=np.nan)
    # print(2,'sf', sf.min(skipna=True).values, sf.max(skipna=True).values, 'lwp', lwp.min(skipna=True).values, lwp.max(skipna=True).values, 't2', t2.min(skipna=True).values, t2.max(skipna=True).values)
    
    # 3. find where 2m-temperature <= 0C
    sf = sf.where(dset['tas']<=273.15, other=np.nan)
    lwp = lwp.where(dset['tas']<=273.15, other=np.nan)
    iwp = iwp.where(dset['tas']<=273.15, other=np.nan)
    twp = twp.where(dset['tas']<=273.15, other=np.nan)
    t2 = t2.where(dset['tas']<=273.15, other=np.nan)
    # print(3,'sf', sf.min(skipna=True).values, sf.max(skipna=True).values, 'lwp', lwp.min(skipna=True).values, lwp.max(skipna=True).values, 't2', t2.min(skipna=True).values, t2.max(skipna=True).values)
    
    sf_count = sf.groupby('time.season').count(dim='time',keep_attrs=True)
    # lwp_count = lwp.groupby('time.season').count(dim='time',keep_attrs=True)
    # iwp_count = iwp.groupby('time.season').count(dim='time', keep_attrs=True)
    # t2_count = t2.groupby('time.season').count(dim='time', keep_attrs=True)
    
    return(sf_count, sf, iwp, lwp, twp)
for model in dset_dict.keys():
    dset_dict[model]['lcc_count2'], dset_dict[model]['sf_lcc2'], dset_dict[model]['iwp_lcc2'], dset_dict[model]['lwp_lcc2'], dset_dict[model]['twp_lcc2'] = find_precip_cloud2(dset_dict[model])
def plt_seasonal_NH_SH(variable,levels,cbar_label,plt_title):

    f, axsm = plt.subplots(nrows=2,ncols=4,figsize =[10,7], subplot_kw={'projection': ccrs.NorthPolarStereo(central_longitude=0.0,globe=None)})

    coast = cy.feature.NaturalEarthFeature(category='physical', scale='110m',
                            facecolor='none', name='coastline')
    
    for ax, season in zip(axsm.flatten()[:4], variable.season):
        # ax.add_feature(cy.feature.COASTLINE, alpha=0.5)
        ax.add_feature(coast,alpha=0.5)
        ax.set_extent([-180, 180, 90, 45], ccrs.PlateCarree())
        gl = ax.gridlines(draw_labels=True)
        gl.top_labels   = False
        gl.right_labels = False
        variable.sel(season=season, lat=slice(45,90)).plot(ax=ax, transform=ccrs.PlateCarree(), extend='max', add_colorbar=False,
                            cmap=cm.hawaii_r, levels=levels)
        ax.set(title ='season = {}'.format(season.values))

    for ax, i, season in zip(axsm.flatten()[4:], np.arange(5,9), variable.season):
        ax.remove()
        ax = f.add_subplot(2,4,i, projection=ccrs.SouthPolarStereo(central_longitude=0.0, globe=None))
        # ax.add_feature(cy.feature.COASTLINE, alpha=0.5)
        ax.add_feature(coast,alpha=0.5)
        ax.set_extent([-180, 180, -90, -45], ccrs.PlateCarree())
        gl = ax.gridlines(draw_labels=True)
        gl.top_labels   = False
        gl.right_labels = False
        cf = variable.sel(season=season, lat=slice(-90,-45)).plot(ax=ax, transform=ccrs.PlateCarree(), extend='max', add_colorbar=False,
                            cmap=cm.hawaii_r, levels=levels)
        ax.set(title ='season = {}'.format(season.values))

    cbaxes = f.add_axes([1.0125, 0.025, 0.025, 0.9])
    cbar = plt.colorbar(cf, cax=cbaxes, shrink=0.5,extend='max', orientation='vertical', label=cbar_label)
    f.suptitle(plt_title, fontweight="bold");
    plt.tight_layout(pad=0., w_pad=0., h_pad=0.)
    
    
for model in dset_dict.keys():
    # Cummulative snowfall days
    figname = '{}_cum_sf_days_season_mean_{}_{}.png'.format(model,starty, endy)
    plt_seasonal_NH_SH(dset_dict[model]['lcc_count'].where(dset_dict[model]['lcc_count']>0.), levels=np.arange(0,310,10), cbar_label='Cummulative snowfall days', plt_title='{} ({} - {})'.format(model, starty,endy))
    plt.savefig(FIG_DIR + figname, format = 'png', bbox_inches = 'tight', transparent = False)
../_images/1350f817b39070997b9db575933c2b7cf663df4afb82a070355f3dfd181ccb77.png ../_images/25f9c5cdd43e59e275d57a49f52f150dfa6b448d68b9942ec59b6f55daf9681a.png ../_images/2e11a33e07590ed93f4831cd32e75bbb0ba8aa1159d37f679be7490d8b0cebe0.png ../_images/2706fe13156c0f0f2e09aaba3d3c0fa519101b46b38b30be24750fbb2ddc8100.png ../_images/cb2676ddc46e7e6f08c698953bd26af877c382dd513035110b169134ceb9ae9a.png ../_images/aaba6291a2477a207a8a854e5dd95dba8a9a138f7d7be8232f79c6b7b57b9fce.png ../_images/09ebaf43e984d0df2320d8910ed54594063f404fc0005c89568e21dfe8f8f7eb.png

Calculating bin and bin sizes#

https://www.statisticshowto.com/choose-bin-sizes-statistics/

Useful to plot TWP vs. Snowfall (precipitation efficency)

def plt_seasonal_2dhist_wp_sf(x_value, y_value, plt_title, xlabel, ylabel):
    f, axsm = plt.subplots(nrows=2,ncols=4,figsize =[10,5], sharex=True, sharey=True)
    # cmap = cm.batlow
    cmap = cm.hawaii_r
    # levels = np.arange(0.1,65000,5000)
    # norm = BoundaryNorm(levels, ncolors=cmap.N, )
    norm = LogNorm(vmin=1, vmax=50000)



    for ax, season in zip(axsm.flatten()[:4], x_value.season):
        ax.plot([0, 1], [0, 1],ls="--", c=".6", transform=ax.transAxes)
        Z, xedges, yedges = np.histogram2d((x_value.where(x_value['lat'] >=45).sel(season=season).values.flatten()), 
                                        (y_value.where(y_value['lat'] >=45).sel(season=season).values.flatten()), 
                                        bins=[90, 160], 
                                        range=[[0,9],[0, 160]])   

        im = ax.pcolormesh(xedges, yedges, Z.transpose(),cmap=cmap,norm=norm,)
        # cbar = f.colorbar(im, ax=ax,)
        ax.set(title =r'lat$\geq 45^\circ$N; season = {}'.format(season.values))
        ax.grid()
        
        
        _corr = xr.corr(x_value.where(x_value['lat'] >=45).sel(season=season), y_value.where(y_value['lat'] >=45).sel(season=season))
        props = dict(boxstyle='round', facecolor='wheat', alpha=0.5)
        ax.text(0.55, 0.95, 'Corr: {}'.format(np.round(_corr,3).values), transform=ax.transAxes, fontsize=11,
        verticalalignment='top', bbox=props)
        
        

    for ax, season in zip(axsm.flatten()[4:], x_value.season):
        ax.plot([0, 1], [0, 1],ls="--", c=".6", transform=ax.transAxes)
        Z, xedges, yedges = np.histogram2d((x_value.where(x_value['lat'] <=-45).sel(season=season).values.flatten()), 
                                        (y_value.where(y_value['lat'] <=-45).sel(season=season).values.flatten()), 
                                        bins=[90, 160], 
                                        range=[[0,9],[0, 160]])   

        im = ax.pcolormesh(xedges, yedges, Z.transpose(), cmap=cmap,norm=norm,)
        # cbar = f.colorbar(im, ax=ax, )
        ax.set(title =r'lat$\leq-45^\circ$S; season = {}'.format(season.values))
        
        ax.set_xlabel('{} ({})'.format(xlabel,x_value.attrs['units']))
        ax.grid()
        
        _corr = xr.corr(x_value.where(x_value['lat'] <=-45).sel(season=season), y_value.where(y_value['lat'] <=-45).sel(season=season))
        props = dict(boxstyle='round', facecolor='wheat', alpha=0.5)
        ax.text(0.55, 0.95, 'Corr: {}'.format(np.round(_corr,3).values), transform=ax.transAxes, fontsize=11,
        verticalalignment='top', bbox=props)
        
    axsm.flatten()[0].set_ylabel('{} ({})'.format(ylabel, y_value.attrs['units']))
    axsm.flatten()[4].set_ylabel('{} ({})'.format(ylabel, y_value.attrs['units']))


    cbaxes = f.add_axes([1.0125, 0.025, 0.025, 0.9])
    cbar = plt.colorbar(im, cax=cbaxes, shrink=0.5, orientation='vertical', label='Mean Frequency')
    f.suptitle(plt_title, fontweight="bold");
    
    
    
    
    plt.tight_layout(pad=0.5, w_pad=0.5, h_pad=0.5)
for model in dset_dict.keys():
    lwp = dset_dict[model]['lwp_lcc'].groupby('time.season').mean(('time'), keep_attrs=True, skipna=True)
    iwp = dset_dict[model]['iwp_lcc'].groupby('time.season').mean(('time'), keep_attrs=True, skipna=True)
    sf  = dset_dict[model]['sf_lcc'].groupby('time.season').mean(('time'), keep_attrs=True, skipna=True)
    
    # Total water path
    twp = dset_dict[model]['twp_lcc'].groupby('time.season').mean(('time'), keep_attrs=True, skipna=True)
    
    # precip efficency from mixed-phase clouds
    figname = '{}_2dhist_twp_sf_season_mean_{}_{}.png'.format(model, starty, endy)
    plt_seasonal_2dhist_wp_sf(twp, sf, '{} Mean seasonal total precip. efficency'.format(model, ), 'Total Water Path', 'Snowfall')
    plt.savefig(FIG_DIR + figname, format = 'png', bbox_inches = 'tight', transparent = False)
../_images/e677c98c28e2b0690ef50b8d99ec79f0e34a9a65ba82828eadd974bcb0818078.png ../_images/ed87b5d396c1e8df33f71c51c0a377c68d083eb33ab17cb364c6b95f4a041ff4.png ../_images/27c42e98dfa6d76ca7d89fcaf5bd4dba7be5c5b3dbdd223317fdb9a22d51a407.png ../_images/d407462f96e21fb9e2d58b6924a233ac4d6e5bcdca1d257b484f45449d8c0b86.png ../_images/33e7e625c4ceba314fa952ca462a1f1c10a6f86151479ce37f9802f70b8bd0fb.png ../_images/ff2a26779443935761f6e9f7f7b7240fa233477ce7497879e9acf0df14ab5ea8.png ../_images/91bebb9fc48a880af10649f1777431cc79419006d73b05f78e6be087a32bb0f9.png

Precipitation efficency - spatial#

for model in dset_dict.keys():

    # calculate precipitation efficency
    dset_dict[model]['precip_eff'] = dset_dict[model]['sf_lcc']/dset_dict[model]['twp_lcc']
    
    # calculate days in each season for each year
    sum_day_DJF = dict()
    sum_day_MAM = dict()
    sum_day_JJA = dict()
    sum_day_SON = dict()

    for year in np.unique(dset_dict[model].time.dt.year):
        year=str(year)
        sum_day_DJF[year] = len(dset_dict[model]['precip_eff'].sel(time=year+'-01')) + len(dset_dict[model]['precip_eff'].sel(time=year+'-02')) + len(dset_dict[model]['precip_eff'].sel(time=year+'-12'))
        sum_day_MAM[year] = len(dset_dict[model]['precip_eff'].sel(time=year+'-03')) + len(dset_dict[model]['precip_eff'].sel(time=year+'-04')) + len(dset_dict[model]['precip_eff'].sel(time=year+'-05'))
        sum_day_JJA[year] = len(dset_dict[model]['precip_eff'].sel(time=year+'-06')) + len(dset_dict[model]['precip_eff'].sel(time=year+'-07')) + len(dset_dict[model]['precip_eff'].sel(time=year+'-08'))
        sum_day_SON[year] = len(dset_dict[model]['precip_eff'].sel(time=year+'-09')) + len(dset_dict[model]['precip_eff'].sel(time=year+'-10')) + len(dset_dict[model]['precip_eff'].sel(time=year+'-11'))

    days_DJF = sum(sum_day_DJF.values())
    days_MAM = sum(sum_day_MAM.values())
    days_JJA = sum(sum_day_JJA.values())
    days_SON = sum(sum_day_SON.values())

    # calculate sum of monthly precip eff
    precip_eff_mon = dset_dict[model]['precip_eff'].groupby('time.month').sum('time',skipna=True)

    # calculate sum of each season precipitation eff
    precip_eff = dict()
    precip_eff['DJF'] = (precip_eff_mon.sel(month=1) + precip_eff_mon.sel(month=2) + precip_eff_mon.sel(month=12))/days_DJF
    precip_eff['MAM'] = (precip_eff_mon.sel(month=3) + precip_eff_mon.sel(month=4) + precip_eff_mon.sel(month=5))/days_MAM
    precip_eff['JJA'] = (precip_eff_mon.sel(month=6) + precip_eff_mon.sel(month=7) + precip_eff_mon.sel(month=8))/days_JJA
    precip_eff['SON'] = (precip_eff_mon.sel(month=9) + precip_eff_mon.sel(month=10) + precip_eff_mon.sel(month=11))/days_SON

    _da = list(precip_eff.values())
    _coord = list(precip_eff.keys())
    # _da = list(pe.values())
    # _coord = list(pe.keys())    
    dset_dict[model]['precip_eff_seas'] = xr.concat(objs=_da, dim=_coord, coords='all').rename({'concat_dim':'season'})
    
    # Plot precipitation efficency
    plt_seasonal_NH_SH(dset_dict[model]['precip_eff_seas'], np.arange(0., 15.5,.5), 'relative snowfall eff.', '{} ({} - {})'.format(model,starty,endy))

    # save precip efficency from mixed-phase clouds figure
    figname = '{}_sf_twp_season_mean_{}_{}.png'.format(model, starty, endy)
    plt.savefig(FIG_DIR + figname, format = 'png', bbox_inches = 'tight', transparent = False)
        
        
../_images/74c1b216be8bf68d7ada15812183a250f239dda5a4308bd1edf7e982b2cb8a31.png ../_images/abb8204d6aecf0bae0e495ef7ca576d7437dff3586f88bf65d3b9a2a23fc6230.png ../_images/cc68feaf3d6e0bfb6ace76951dd671b5d30f8ed502a3e5d441043140a0990ff0.png ../_images/b7ddb35987baed7dc2d0f9e7d0c9ba38c99892f5c06f970d6d2d812b9dcebed8.png ../_images/f549df6eb797211c24386d6a4401f37ba26e491ec7f0d0629c659bd57a731217.png ../_images/c5d4937c0e3ca96442d446edaa2a1216f42f4a023432e031499ae230e1b53832.png ../_images/f94ba67cc3f08b5489dcb8bdbb5bb9999f5ad105534d628f2a06a0041b7ed44a.png

Relative frequency#

\[RF = \frac{f}{n} = \frac{\text{number of times the data occurred in an observation}}{\text{total frequency}}\]
for model in dset_dict.keys():
    # frequency without snow requirement
    n = dset_dict[model]['lwp'].groupby('time.season').sum('time',skipna=True)
    f= dset_dict[model]['lwp_lcc2'].groupby('time.season').sum('time',skipna=True)
    dset_dict[model]['rf_lcc_wo_snow'] = f/n
    plt_seasonal_NH_SH(dset_dict[model]['rf_lcc_wo_snow'], np.arange(0,1.05,0.05), 'Relative frequency from precip. LCCs', '{} ({} - {})'.format(model,starty,endy))
    # save frequency from LCCs
    figname = '{}_rf_lcc_wo_snow_season_{}_{}.png'.format(model, starty, endy)
    plt.savefig(FIG_DIR + figname, format = 'png', bbox_inches = 'tight', transparent = False)
    
    
    # frequency with snow from lccs
    n = dset_dict[model]['lwp'].groupby('time.season').sum('time',skipna=True)
    f= dset_dict[model]['lwp_lcc'].groupby('time.season').sum('time',skipna=True)
    dset_dict[model]['rf_lcc_w_snow'] = f/n
    plt_seasonal_NH_SH(dset_dict[model]['rf_lcc_w_snow'], np.arange(0,1.05,0.05), 'Relative frequency from precip. LCCs', '{} ({} - {})'.format(model,starty,endy))
    # save frequency from LCCs
    figname = '{}_rf_lcc_w_snow_season_{}_{}.png'.format(model,starty, endy)
    plt.savefig(FIG_DIR + figname, format = 'png', bbox_inches = 'tight', transparent = False)
    
    
    n = dset_dict[model]['prsn'].groupby('time.season').sum('time',skipna=True)
    f= dset_dict[model]['sf_lcc'].groupby('time.season').sum('time',skipna=True)
    dset_dict[model]['rf_sf_lcc'] = f/n
    plt_seasonal_NH_SH(dset_dict[model]['rf_sf_lcc'], np.arange(0,1.05,0.05), 'Relative precip. frequency in LCCs', '{} ({} - {})'.format(model,starty,endy))
    # save frequency from LCCs
    figname = '{}_rf_sf_lcc_season_{}_{}.png'.format(model,starty, endy)
    plt.savefig(FIG_DIR + figname, format = 'png', bbox_inches = 'tight', transparent = False)
    
../_images/2ba3fb11b9a1c27e33859d46e1561ac73bb4cfaa67c8e0366c1b717f8ce8b53e.png ../_images/6d051827160a355e5fff880bde99f9e8039311b9bd95855a8a8a16b8a1ff3678.png ../_images/35862f98b80fa3cee5d8da9aa5487501eba75a9bcb00317acaa978045905beb8.png ../_images/6955da53ff85c4adb8db8de1d326538c29e42a4fa8e966b1b6d966eb0e451cff.png ../_images/a988b079f7742a55e7285c413f09c72fabd609d88f153ee51f2edd60bcd00743.png ../_images/596c80aaac28c95ba5b16c430b35a79e4736582feac5afbfc989f67565c1e917.png ../_images/cb80bb5ecc63fc2719d26520e1581c3d0c17b8f8f0f6d3b6350f1f3128fd87cb.png ../_images/fb81a8aa97296d8c16a892a0938fe0db639b4440027177c17d6c83d8c9b5e43d.png ../_images/6916c59445088b1e8fa1188953a711cf84c665fdc8e29c9ef737a73a47b4c745.png ../_images/c102b0756e18a680ec820a582217b56679333123f54e40bf2714c5fcb5406955.png ../_images/3290d97499ba824bfe103343d8e351f6f8215b3e9caf788131b811880dfdfc9a.png ../_images/1b7775f33b05007517aa5079c8b2f8591087fc1dfd786d6fe4428bdd93c96809.png ../_images/818eee2994ae0f4e4f914209d09a2fcf34dc5795098932b1e3e6ada32b1fa4c6.png ../_images/57ca05725caa904e550754a09c9b206c2fd46e5f6a60bdda15c4545af7bb16db.png ../_images/9f048ef90190ff8565f5c4538f06fa496d1280ca8d0b1f056d84bc8c9ea65446.png ../_images/9deb94f5402e948b3d162a785bb063c2feb04db3ac591aad43ee2db66effef80.png ../_images/863860f57ccefc37356318beb100c7f4bd691212eb023463aba058e6fe2e5525.png ../_images/f295757531c0c02d073412ebaad127c00ab60b135231bd71405ff2b137ef6f0c.png ../_images/e2e81a0e9b4982247d52e5678c1e4a8be772bf8712e48d3e349fb40ae654b0d3.png ../_images/0635a8f252e9f304b07e9977c024ad174c873814695b20d05895ffea83e74771.png ../_images/097260400d20ab66ab3300fd95b4a2df688c1a307664bf7795b57ed772ef2ed0.png

Plot annual cycle of LCC frequency, precipitaiton frequency in LCCs, relative snowfall efficeny#

lcc_wo_snow_NH = {}
lcc_wo_snow_SH = {}

sf_lcc_NH = {}
sf_lcc_SH = {}
for model in dset_dict.keys():
    n = dset_dict[model]['lwp'].groupby('time.month').sum('time',skipna=True)
    f = dset_dict[model]['lwp_lcc2'].groupby('time.month').sum('time',skipna=True)
    rf_lcc_wo_snow = f/n

    lcc_wo_snow_NH[model] = ((rf_lcc_wo_snow.sel(lat=slice(45,90))).mean(('lon','lat'),skipna=True))
    lcc_wo_snow_SH[model] = ((rf_lcc_wo_snow.sel(lat=slice(-90,-45))).mean(('lon','lat'),skipna=True))
    
    
    n = dset_dict[model]['prsn'].groupby('time.month').sum('time',skipna=True)
    f= dset_dict[model]['sf_lcc'].groupby('time.month').sum('time',skipna=True)
    rf_sf_lcc = f/n
    
    sf_lcc_NH[model] = ((rf_sf_lcc.sel(lat=slice(45,90))).mean(('lon','lat'),skipna=True))
    sf_lcc_SH[model] = ((rf_sf_lcc.sel(lat=slice(-90,-45))).mean(('lon','lat'),skipna=True))
    
    try:
        lcc_wo_snow_NH[model] = lcc_wo_snow_NH[model].drop('height')
        lcc_wo_snow_SH[model] = lcc_wo_snow_SH[model].drop('height')
        
        sf_lcc_NH[model] = sf_lcc_NH[model].drop('height')
        sf_lcc_SH[model] = sf_lcc_SH[model].drop('height')
    except ValueError:
        continue
ratios = xr.Dataset()
_da = list(lcc_wo_snow_NH.values())
_coord = list(lcc_wo_snow_NH.keys())

ratios['lcc_wo_snow_NH'] = xr.concat(objs=_da, dim=_coord, coords='all').rename({'concat_dim':'model'})
    
_da = list(lcc_wo_snow_SH.values())
_coord = list(lcc_wo_snow_SH.keys())

ratios['lcc_wo_snow_SH'] = xr.concat(objs=_da, dim=_coord, coords='all').rename({'concat_dim':'model'})
_da = list(sf_lcc_NH.values())
_coord = list(sf_lcc_NH.keys())

ratios['sf_lcc_NH'] = xr.concat(objs=_da, dim=_coord, coords='all').rename({'concat_dim':'model'})
_da = list(sf_lcc_SH.values())
_coord = list(sf_lcc_SH.keys())

ratios['sf_lcc_SH'] = xr.concat(objs=_da, dim=_coord, coords='all').rename({'concat_dim':'model'})
ds_era0 = xr.open_dataset('/scratch/franzihe/output/ERA5/daily_means/model_grid/ERA5_daily_mean_ERA5_200701_201012.nc')
---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
Cell In [71], line 1
----> 1 ds_era0 = xr.opendataset('/scratch/franzihe/output/ERA5/daily_means/model_grid/ERA5_daily_mean_ERA5_200701_201012.nc')

AttributeError: module 'xarray' has no attribute 'opendataset'
f, axsm = plt.subplots(nrows=2,ncols=2,figsize =[10,7], sharex=True)

ax = axsm.flatten()
ratios['lcc_wo_snow_NH'].plot.line(ax=ax[0], x='month', hue='model', add_legend=False, ylim=[0,1])
ratios['lcc_wo_snow_SH'].plot.line(ax=ax[1], x='month', hue='model', add_legend=False, ylim=[0,1])
ratios['sf_lcc_NH'].plot.line(ax=ax[2], x='month', hue='model', add_legend=False, ylim=[0,1])
ratios['sf_lcc_SH'].plot.line(ax=ax[3], x='month', hue='model', add_legend=False, ylim=[0,1])

ax[0].set_ylabel('relative freq. from LCCs')
ax[0].set(title ='Arctic')
ax[1].set_ylabel('relative freq. from LCCs')
ax[1].set(title ='Antarctic')

ax[2].set_ylabel('relative precip. freq. in LCCs')
ax[3].set_ylabel('relative precip. freq. in LCCs')


ax[1].legend(list(ratios['model'].values), bbox_to_anchor=(1.01, .4, -1., .102), loc='lower left', ncol = 1)
plt.tight_layout(pad=0.5, w_pad=0.5, h_pad=0.5)
../_images/f8239e54fa05354d823ce4d222d1005879f94978543dbadb940dc2366b72d1bc.png

Load ERA5 data in model resolutions#

ds_era = dict()
for model in dset_dict.keys():
    ds_era[model] = xr.open_mfdataset(glob('{}/ERA5/daily_means/model_grid/*{}*.nc'.format(OUTPUT_DATA_DIR,model)))
    
    # remove leap day from dataset
    ds_era[model] = ds_era[model].sel(time=~((ds_era[model].time.dt.month == 2) & (ds_era[model].time.dt.day == 29)))

Differences Model - ERA5#

def plt_seasonal_diff(variable, levels, cbar_label, plt_title):
    f, axsm = plt.subplots(nrows=2,ncols=4,figsize =[10,7], subplot_kw={'projection': ccrs.NorthPolarStereo(central_longitude=0.0,globe=None)})

    coast = cy.feature.NaturalEarthFeature(category='physical', scale='110m',
                            facecolor='none', name='coastline')
    
    for ax, season in zip(axsm.flatten()[:4], variable.season):
        # ax.add_feature(cy.feature.COASTLINE, alpha=0.5)
        ax.add_feature(coast,alpha=0.5)
        ax.set_extent([-180, 180, 90, 45], ccrs.PlateCarree())
        gl = ax.gridlines(draw_labels=True)
        gl.top_labels   = False
        gl.right_labels = False
        variable.sel(season=season, lat=slice(45,90)).plot(ax=ax, transform=ccrs.PlateCarree(), extend='both', add_colorbar=False,
                            cmap=cm.vik, levels=levels)
        ax.set(title ='season = {}'.format(season.values))

    for ax, i, season in zip(axsm.flatten()[4:], np.arange(5,9), variable.season):
        ax.remove()
        ax = f.add_subplot(2,4,i, projection=ccrs.SouthPolarStereo(central_longitude=0.0, globe=None))
        # ax.add_feature(cy.feature.COASTLINE, alpha=0.5)
        ax.add_feature(coast,alpha=0.5)
        ax.set_extent([-180, 180, -90, -45], ccrs.PlateCarree())
        gl = ax.gridlines(draw_labels=True)
        gl.top_labels   = False
        gl.right_labels = False
        cf = variable.sel(season=season, lat=slice(-90,-45)).plot(ax=ax, transform=ccrs.PlateCarree(), extend='both', add_colorbar=False,
                            cmap=cm.vik,levels=levels)
        ax.set(title ='season = {}'.format(season.values))

    cbaxes = f.add_axes([1.0125, 0.025, 0.025, 0.9])
    cbar = plt.colorbar(cf, cax=cbaxes, shrink=0.5,extend='both', orientation='vertical', label=cbar_label)
    f.suptitle(plt_title, fontweight="bold");
    plt.tight_layout(pad=0., w_pad=0., h_pad=0.)

Difference Cummulative snowfall days#

for model in dset_dict.keys():
    variable = dset_dict[model]['lcc_count'] - ds_era[model]['lcc_count']
    plt_title = '{} - ERA5'.format(model)
    levels = np.arange(-200, 220, 20)
    cbar_label = 'Difference - cummulative snowfall days'
    
    plt_seasonal_diff(variable, levels, cbar_label, plt_title)
    
    figname = '{}_diff_cum_sf_days_season_mean_{}_{}.png'.format(model,starty, endy)
    plt.savefig(FIG_DIR + figname, format = 'png', bbox_inches = 'tight', transparent = False)

    
../_images/67f08fac44a4cb93c5d71186cdd085c8ab6e4ddfe790b485be7bf7ca604c8b0a.png ../_images/210f2ae49b9edf5ac921712af0237dc7034ba159c92782eaf23cd0c4cf44a457.png ../_images/07f58a5d71178bb290ec71be1962b96e3f7f7248f308f6a7758898d874a0c637.png ../_images/8453421d073dfab8e7d69b5bcfe8ba37003cb58dd734b6a207d5e0b8e9b244f9.png ../_images/9714640fa3bdec8733f7b81fc000d3eede6002bfbb03ecdc362aff65e372697f.png ../_images/e963b0f7bc4a867e327815ce650b102e82f905608783e2c95aa6f2b5455bf3e2.png ../_images/aba89ff3b74b51a11b315591dc953a420847d8f07049ed57d44669fd08638fe5.png

Difference Frequencys#

for model in dset_dict.keys():
    # Difference relative frequency lccs without snow requirement
    rf_lcc_wo_snow = dset_dict[model]['rf_lcc_wo_snow'] - ds_era[model]['rf_lcc_wo_snow']
    plt_title = '{} - ERA5'.format(model)
    levels = np.arange(-1.0, 1.05, 0.05)
    cbar_label = 'Difference - LCCs'
    plt_seasonal_diff(rf_lcc_wo_snow, levels,cbar_label, plt_title)
    figname = '{}_diff_rf_lcc_wo_snow_season_{}_{}.png'.format(model,starty, endy)
    plt.savefig(FIG_DIR + figname, format = 'png', bbox_inches = 'tight', transparent = False)
    
    # Difference relative frequency precipitating LCCs
    rf_lcc = dset_dict[model]['rf_lcc'] - ds_era[model]['rf_lcc']
    plt_title = '{} - ERA5'.format(model)
    levels = np.arange(-1.0, 1.05, 0.05)
    cbar_label = 'Difference - precip. LCCs'
    
    plt_seasonal_diff(rf_lcc, levels, cbar_label, plt_title)
    
    figname = '{}_diff_rf_lcc_w_snow_season_{}_{}.png'.format(model,starty, endy)
    plt.savefig(FIG_DIR + figname, format = 'png', bbox_inches = 'tight', transparent = False)

    # Difference relative frequency precipitation from LCCss
    rf_sf_lcc = dset_dict[model]['rf_sf_lcc'] - ds_era[model]['rf_sf_lcc']
    plt_title = '{} - ERA5'.format(model)
    levels = np.arange(-1.0, 1.05, 0.05)
    cbar_label = 'Difference - LCC precip.'
    
    plt_seasonal_diff(rf_sf_lcc, levels, cbar_label, plt_title)
    
    figname = '{}_diff_rf_sf_lcc_season_{}_{}.png'.format(model,starty, endy)
    plt.savefig(FIG_DIR + figname, format = 'png', bbox_inches = 'tight', transparent = False)
../_images/ded7c1d03b0faf87ee78d38148b9511794cbfd6d5697c02387eb8ee9a4e2a26c.png ../_images/7c0bdb91abf665ebfb355652c37046f941368983380954111c230af49f8cd508.png ../_images/a1caf059afeb8a74a78a6bad3b2a70e98d79f8fb140870c5940a563dad093778.png ../_images/01c19869d3e9b517fb664b553053421a9610a3c664b3ae1755ee5c209ac6df2c.png ../_images/77e7e0ab0f46ae0ade695a5c5fdb2ab4547522dbf9ec4e3b892595644809583c.png ../_images/0748fd7c6b7da15aa5b05628a773ce0d3f584f64989bb5478a628a4c6595ea86.png ../_images/76d9b8bbf7c800009b60010a554276744eacb10d21ff188dad6041bb64605cee.png ../_images/027b63fbb254305c417f1d8ab1c0c92c67896fdc05148a54dca341a34bf11cd5.png ../_images/a615617848f9c0b39115e5b15e42ceaa97036e5115ebd6c6cd72b0638165fea8.png ../_images/68e45c448deb2fb41ba22a868b181abf4dbe0d3a1e2f38456bcff4f16b191f67.png ../_images/5938398775d81397d1dfe9a46b642d1d8f0ae11914a3f3a45003d8d3cbea980d.png ../_images/a42d7a5fac2ec809a2ee6179c93d3444130901fad0694cee354465d0bfec38be.png ../_images/a5006480c36ec7d995ff3983bb102e9cb8e667891c7780b2f936fb81c82af5a4.png ../_images/b7dcbf31add58b837384e3a73496a429a3dc6f1c7be28bfa60c7de2ee80e1e09.png ../_images/17c72a38955518d397b76780b2d7438f8fbc3547e725a39c66bb928b998ea360.png ../_images/7634d1a329908311de2d8b5e494b6a7dca759bd015f06f49678e9af4e97fda0c.png ../_images/1d9056151a316a36bbdbe7e6ac4a738e7db0263622bec9277603ca9f2b48f6f6.png ../_images/6d8e7354c0c1d65577cb5b6b34ef7300179ca24f9d912aff5764c51348dfea9d.png ../_images/b436fdcf6fe0b2d52259cf6d69549018204d6e2f83f3025556746c518080dced.png ../_images/e5c429d19d8eeb2f2def169ef1a87f3bae4b2ff6753ee806b26bd8be3b65f380.png ../_images/e64bc90b88396ecc21034c22a8a94b0bba3136e3b974d515ddf0970b869a7314.png

Difference total water path#

for model in dset_dict.keys():

    levels = np.arange(-1,1.1,0.1)
    
    # total water path
    ds_era[model]['twp_lcc'] = ds_era[model]['iwp_lcc'] + ds_era[model]['lwp_lcc']
    variable = (dset_dict[model]['twp_lcc'] - ds_era[model]['twp_lcc']).groupby('time.season').mean('time', skipna=True, keep_attrs=True)
    
    plt_seasonal_diff(variable, levels, 'Diff. total water path [kg m-2]', '{} - ERA5'.format(model))
    figname = '{}_diff_twp_sf_days_season_mean_{}_{}.png'.format(model,starty, endy)
    plt.savefig(FIG_DIR + figname, format = 'png', bbox_inches = 'tight', transparent = False)
    
    
    # snowfall difference for snowfall days
    variable = (dset_dict[model]['sf_lcc'] - ds_era[model]['sf_lcc']).groupby('time.season').mean('time', skipna=True, keep_attrs=True)    
    plt_seasonal_diff(variable, levels, 'Diff. Snowfall [mm]', '{} - ERA5'.format(model))
    figname = '{}_diff_sf_sf_days_season_mean_{}_{}.png'.format(model,starty, endy)
    plt.savefig(FIG_DIR + figname, format = 'png', bbox_inches = 'tight', transparent = False)
../_images/bc19a895a69300ddda10acfd33b527a587b54a423f3a06a066719a73f62b38da.png ../_images/852aa817c03863cb89632c680e3c3ad64dec8e097ddbc16f0c08a2c632e12831.png ../_images/11e1402ecc9b22709436633bc27fc3fc64204b3818841246aa34d55efd704b52.png ../_images/e1d349092101634f39cdcbe72b487abc443c00d0c0c524caef882201cc77897f.png ../_images/428dafe8b651c57bfac154777254fd9690cce636683d42ca16ade058dee3552a.png ../_images/70922c8ecf0e83e4b0fdc53949b57869611d9a6be3589aa9e2236862fb1eb5cc.png ../_images/109d8f64dce66bc3f49835e6f2b2bcf53b0289e839934e8ab07f3475baf2161a.png ../_images/81128b9baf61e7f51e6637a2e180e3abe83158622de73110da5d4cd2132e3469.png ../_images/439d4cf80bede73c15a108c5f3fd52038c16d76fcae79dc5cbdc358897e21017.png ../_images/bc1f00d3f53902d12f1d05a704b6fcaaf0ea498719aebfd7b6870a92bc2217be.png ../_images/c8e91cc11bc0ab5c7ecacf618ebc615cfd6dde67f16309f246de9f85077feb5c.png ../_images/4cd3cb2357017b84ada8f1068d8197ae83f4b07601111deea311a3d1e395dfba.png ../_images/69586319beaba0341bf67fa2e493546d1b1fc69559d07c07892feecf59521d72.png ../_images/2c9ee2f057ec8efa4964edb49bc9d442b8ff52b344d80038f6cf3637be90b733.png

Calendar#

Not all models in CMIP6 use the same calendar. Hence we double check the time axis. Later, when we regrid to the same horizontal resolution (Regrid CMIP6 data) we will assign the same calendars for each model.

# # metadata of the historical run:
# _d2 = pd.Series(["calendar",
#                  "branch_time_in_parent", #"parent_activity_id", "parent_experiment_id",	"parent_mip_era",
#                  "parent_source_id",#"parent_sub_experiment_id", 
#                  "parent_time_units",# "parent_variant_label"
#                   ])
# _d2 = pd.DataFrame(_d2).rename(columns={0:'index'})
# for model in dset_dict.keys():
#     _data = []
#     _names =[]
#     _data.append(dset_dict[model].time.to_index().calendar)
#     for k, v in dset_dict[model].attrs.items():
        
#         if 'parent_time_units' in k or 'branch_time_in_parent' in k or 'parent_source_id' in k:
#             _data.append(v)
#             _names.append(k)
#     _d2 = pd.concat([_d2,   pd.Series(_data)], axis=1)

# _d2.dropna(how='all', axis=1, inplace=True)
# _d2 = _d2.set_index('index')
# _d2.columns = _d2.loc['parent_source_id']
# _d2.drop('parent_source_id').T

Show attributes and individual identifier#

… is going to be the reference model for the horizontal grid. The xarray datasets inside dset_dict can be extracted as any value in a Python dictionary.

The dictonary key is the source_id from list_models.

# for model in dset_dict.keys():
#     print('Institution: {}, \
#           Model: {},   \
#           Nominal res: {},  \
#           lon x lat, level, top,: {}, \
#           tracking_id: {}'.format(dset_dict[model].attrs['institution_id'],
#                                   dset_dict[model].attrs['source_id'], 
#                                   dset_dict[model].attrs['nominal_resolution'], 
#                                   dset_dict[model].attrs['source'],
#                                   dset_dict[model].attrs['tracking_id']))

Assign attributes to the variables#

We will assign the attributes to the variables as in ERA5 to make CMIP6 and ERA5 variables comperable.

  • pr and prsn in kg m-2 s-1 \(\rightarrow\) Multiply by 3600 to get mm h-1

# now = datetime.utcnow()
# for model in dset_dict.keys():
# # 
#     for var_id in dset_dict[model].keys():
         
#         if var_id == 'prsn':
#             dset_dict[model][var_id] = dset_dict[model][var_id]*3600
#             dset_dict[model][var_id] = dset_dict[model][var_id].assign_attrs({'standard_name': 'snowfall_flux',
#     'long_name': 'Snowfall Flux',
#     'comment': 'At surface; includes precipitation of all forms of water in the solid phase',
#     'units': 'mm h-1',
#     'original_units': 'kg m-2 s-1',
#     'history': "{}Z altered by F. Hellmuth: Converted units from 'kg m-2 s-1' to 'mm h-1'.".format(now.strftime("%d/%m/%Y %H:%M:%S")),
#     'cell_methods': 'area: time: mean',
#     'cell_measures': 'area: areacella'})

Interpolate from CMIP6 hybrid sigma-pressure levels to ERA5 isobaric pressure levels#

The vertical variables in the CMIP6 models are in hybrid sigma-pressure levels. Hence the vertical variable in the xarray datasets in dset_dict will be calculated by using the formula: $\( P(i,j,k) = hyam(k) p0 + hybm(k) ps(i,j)\)$ to calculate the pressure

# # Rename datasets with different naming convention for constant hyam
# for model in dset_dict.keys():
#     if ('a' in list(dset_dict[model].keys())) == True:
#         dset_dict[model] = dset_dict[model].rename({'a':'ap', 'a_bnds': 'ap_bnds'})
#     if model == 'IPSL-CM6A-LR':
#         dset_dict[model] = dset_dict[model].rename({'presnivs':'plev'})
#     if model == 'IPSL-CM5A2-INCA':
#         dset_dict[model] = dset_dict[model].rename({'lev':'plev'})
    
# for model in dset_dict.keys():
#     for var_id in dset_dict[model].keys():#['clw', 'cli']:
#         if var_id == 'clw' or var_id == 'cli':
#             # Convert the model level to isobaric levels
#             #### ap, b, ps, p0
#             if ('ap' in list(dset_dict[model].keys())) == True and \
#                 ('ps' in list(dset_dict[model].keys())) == True and \
#                 ('p0' in list(dset_dict[model].keys())) == True:
#                 if ('lev' in list(dset_dict[model][var_id].coords)) == True and \
#                     ('lev' in list(dset_dict[model]['ap'].coords)) == True and \
#                     ('lev' in list(dset_dict[model]['b'].coords)) == True:
#                         print(model, var_id, 'lev, ap, ps, p0')
#                         # dset_dict[model][var_id] = gc.interpolation.interp_hybrid_to_pressure(data = dset_dict[model][var_id],
#                         #                                                                                 ps   = dset_dict[model]['ps'], 
#                         #                                                                                 hyam = dset_dict[model]['ap'], 
#                         #                                                                                 hybm = dset_dict[model]['b'], 
#                         #                                                                                 p0   = dset_dict[model]['p0'], 
#                         #                                                                                 new_levels=new_levels,
#                         #                                                                                 lev_dim='lev')
#                         dset_dict[model]['plev'] = dset_dict[model]['ap']*dset_dict[model]['p0'] + dset_dict[model]['b']*dset_dict[model]['ps']
#                         dset_dict[model]['plev'] = dset_dict[model]['plev'].transpose('time', 'lev','lat','lon')
                
#                 if ('plev' in list(dset_dict[model][var_id].coords)) == True:
#                     print(model, var_id, 'variable on pressure levels', )
#                 # if ('lev' in list(dset_dict[model][var_id].coords)) == True and \
#                 #     ('lev' in list(dset_dict[model]['ap'].coords)) == False and \
#                 #     ('lev' in list(dset_dict[model]['b'].coords)) == False:
#                 #         print(model, 'variable on pressure levels', 'lev, ap, ps,')
#             # Convert the model level to isobaric levels
#             #### ap, b, p0
#             if ('ap' in list(dset_dict[model].keys())) == True and \
#                 ('ps' in list(dset_dict[model].keys())) == True and \
#                 ('p0' in list(dset_dict[model].keys())) == False:
#                 if ('lev' in list(dset_dict[model][var_id].coords)) == True and \
#                     ('lev' in list(dset_dict[model]['ap'].coords)) == True and \
#                     ('lev' in list(dset_dict[model]['b'].coords)) == True:
#                         print(model,var_id, 'lev, ap, ps,')
#                         # dset_dict[model][var_id] = gc.interpolation.interp_hybrid_to_pressure(data = dset_dict[model][var_id],
#                         #                                                                                 ps   = dset_dict[model]['ps'], 
#                         #                                                                                 hyam = dset_dict[model]['ap'], 
#                         #                                                                                 hybm = dset_dict[model]['b'], 
#                         #                                                                                 new_levels=new_levels,
#                         #                                                                                 lev_dim='lev')
#                         dset_dict[model]['plev'] = dset_dict[model]['ap'] + dset_dict[model]['b']*dset_dict[model]['ps']
#                         dset_dict[model]['plev'] = dset_dict[model]['plev'].transpose('time', 'lev','lat','lon')
                
#                 if ('plev' in list(dset_dict[model][var_id].coords)) == True:
#                     print(model, var_id, 'variable on pressure levels', )
                
#             if ('b' in list(dset_dict[model].keys())) == True and \
#                 ('orog' in list(dset_dict[model].keys())) == True:
#                 if ('lev' in list(dset_dict[model][var_id].coords)) == True and \
#                     ('lev' in list(dset_dict[model]['pfull'].coords)) == True:
#                         print(model, 'hybrid height coordinate')
                

Calculate liquid water path from content#

# for model in dset_dict.keys():
    
#     if ('plev' in list(dset_dict[model].keys())) == True:
#         print(model, 'plev')
#         _lwp = xr.DataArray(data=da.full(shape=dset_dict[model]['clw'].shape,fill_value=np.nan),
#                                 dims=dset_dict[model]['clw'].dims,
#                                 coords=dset_dict[model]['clw'].coords)
#         # lev2 is the atmospheric pressure, lower in the atmosphere than lev. Sigma-pressure coordinates are from 1 to 0, with 1 at the surface
#         for i in range(len(dset_dict[model]['lev'])-1):
#             # calculate pressure difference between two levels
#             dp = (dset_dict[model]['plev'].isel(lev=i) - dset_dict[model]['plev'].isel(lev=i+1))
#             # calculate mean liquid water content between two layers
#             dlwc = (dset_dict[model]['clw'].isel(lev=i) + dset_dict[model]['clw'].isel(lev=i+1))/2
#             # calculate liquid water path between two layers
#             _lwp[:,i,:,:] = dp[:,:,:]/9.81 * dlwc[:,:,:]
        
#             # sum over all layers to ge the liquid water path in the atmospheric column
#             dset_dict[model]['lwp'] = _lwp.sum(dim='lev',skipna=True)
            
#             # assign attributes to data array
#             dset_dict[model]['lwp'] = dset_dict[model]['lwp'].assign_attrs(dset_dict[model]['clw'].attrs)
#             dset_dict[model]['lwp'] = dset_dict[model]['lwp'].assign_attrs({'long_name':'Liquid Water Path', 
#                                                                             'units' : 'kg m-2',
#                                                                                 'mipTable':'', 'out_name': 'lwp',
#                                                                                 'standard_name': 'atmosphere_mass_content_of_cloud_liquid_water',
#                                                                                 'title': 'Liquid Water Path',
#                                                                                 'variable_id': 'lwp', 'original_units': 'kg/kg',
#                                                                                 'history': "{}Z altered by F. Hellmuth: Interpolate data from hybrid-sigma levels to isobaric levels with P=a*p0 + b*psfc. Calculate lwp with hydrostatic equation.".format(now.strftime("%d/%m/%Y %H:%M:%S"))})
#         # when ice water path does not exist
#         if ('clivi' in list(dset_dict[model].keys())) == False:
#             _iwp = xr.DataArray(data=da.full(shape=dset_dict[model]['cli'].shape,fill_value=np.nan),
#                                     dims=dset_dict[model]['cli'].dims,
#                                     coords=dset_dict[model]['cli'].coords)
#             # lev2 is the atmospheric pressure, lower in the atmosphere than lev. Sigma-pressure coordinates are from 1 to 0, with 1 at the surface
#             for i in range(len(dset_dict[model]['lev'])-1):
#                 # calculate pressure difference between two levels
#                 dp = (dset_dict[model]['plev'].isel(lev=i) - dset_dict[model]['plev'].isel(lev=i+1))
#                 # calculate mean liquid water content between two layers
#                 diwc = (dset_dict[model]['cli'].isel(lev=i) + dset_dict[model]['cli'].isel(lev=i+1))/2
#                 # calculate liquid water path between two layers
#                 _iwp[:,i,:,:] = dp[:,:,:]/9.81 * diwc[:,:,:]
            
                
#                 # sum over all layers to ge the Ice water path in the atmospheric column
#                 dset_dict[model]['clivi'] = _iwp.sum(dim='lev',skipna=True)
                
#                 # assign attributes to data array
#                 dset_dict[model]['clivi'] = dset_dict[model]['clivi'].assign_attrs(dset_dict[model]['cli'].attrs)
#                 dset_dict[model]['clivi'] = dset_dict[model]['clivi'].assign_attrs({'long_name':'Ice Water Path', 
#                                                                                 'units' : 'kg m-2',
#                                                                                     'mipTable':'', 'out_name': 'clivi',
#                                                                                     'standard_name': 'atmosphere_mass_content_of_cloud_ice_water',
#                                                                                     'title': 'Ice Water Path',
#                                                                                     'variable_id': 'clivi', 'original_units': 'kg/kg',
#                                                                                     'history': "{}Z altered by F. Hellmuth: Interpolate data from hybrid-sigma levels to isobaric levels with P=a*p0 + b*psfc. Calculate clivi with hydrostatic equation.".format(now.strftime("%d/%m/%Y %H:%M:%S"))})
            
#     if ('plev' in list(dset_dict[model].coords)) == True:
#         print(model, 'plev coord')
#         _lwp = xr.DataArray(data=da.full(shape=dset_dict[model]['clw'].shape,fill_value=np.nan),
#                                 dims=dset_dict[model]['clw'].dims,
#                                 coords=dset_dict[model]['clw'].coords)
#         # lev2 is the atmospheric pressure, lower in the atmosphere than lev. Sigma-pressure coordinates are from 1 to 0, with 1 at the surface
#         for i in range(len(dset_dict[model]['plev'])-1):
#             # calculate pressure difference between two levels
#             dp = (dset_dict[model]['plev'].isel(plev=i) - dset_dict[model]['plev'].isel(plev=i+1))
#             # calculate mean liquid water content between two layers
#             dlwc = (dset_dict[model]['clw'].isel(plev=i) + dset_dict[model]['clw'].isel(plev=i+1))/2
#             # calculate liquid water path between two layers
#             _lwp[:,i,:,:] = dp/9.81 * dlwc[:,:,:]
        
#             # sum over all layers to ge the liquid water path in the atmospheric column
#             dset_dict[model]['lwp'] = _lwp.sum(dim='plev',skipna=True)
            
#             # assign attributes to data array
#             dset_dict[model]['lwp'] = dset_dict[model]['lwp'].assign_attrs(dset_dict[model]['clw'].attrs)
#             dset_dict[model]['lwp'] = dset_dict[model]['lwp'].assign_attrs({'long_name':'Liquid Water Path', 
#                                                                             'units' : 'kg m-2',
#                                                                                 'mipTable':'', 'out_name': 'lwp',
#                                                                                 'standard_name': 'atmosphere_mass_content_of_cloud_liquid_water',
#                                                                                 'title': 'Liquid Water Path',
#                                                                                 'variable_id': 'lwp', 'original_units': 'kg/kg',
#                                                                                 'history': "{}Z altered by F. Hellmuth: Interpolate data from hybrid-sigma levels to isobaric levels with P=a*p0 + b*psfc. Calculate lwp with hydrostatic equation.".format(now.strftime("%d/%m/%Y %H:%M:%S"))})
#         # when ice water path does not exist
#         if ('clivi' in list(dset_dict[model].keys())) == False:
#             _iwp = xr.DataArray(data=da.full(shape=dset_dict[model]['cli'].shape,fill_value=np.nan),
#                                     dims=dset_dict[model]['cli'].dims,
#                                     coords=dset_dict[model]['cli'].coords)
#             # lev2 is the atmospheric pressure, lower in the atmosphere than lev. Sigma-pressure coordinates are from 1 to 0, with 1 at the surface
#             for i in range(len(dset_dict[model]['plev'])-1):
#                 # calculate pressure difference between two levels
#                 dp = (dset_dict[model]['plev'].isel(plev=i) - dset_dict[model]['plev'].isel(plev=i+1))
#                 # calculate mean liquid water content between two layers
#                 diwc = (dset_dict[model]['cli'].isel(plev=i) + dset_dict[model]['cli'].isel(plev=i+1))/2
#                 # calculate liquid water path between two layers
#                 _iwp[:,i,:,:] = dp/9.81 * diwc[:,:,:]
            
                
#                 # sum over all layers to ge the Ice water path in the atmospheric column
#                 dset_dict[model]['clivi'] = _iwp.sum(dim='plev',skipna=True)
                
#                 # assign attributes to data array
#                 dset_dict[model]['clivi'] = dset_dict[model]['clivi'].assign_attrs(dset_dict[model]['clw'].attrs)
#                 dset_dict[model]['clivi'] = dset_dict[model]['clivi'].assign_attrs({'long_name':'Ice Water Path', 
#                                                                                 'units' : 'kg m-2',
#                                                                                     'mipTable':'', 'out_name': 'clivi',
#                                                                                     'standard_name': 'atmosphere_mass_content_of_cloud_ice_water',
#                                                                                     'title': 'Ice Water Path',
#                                                                                     'variable_id': 'clivi', 'original_units': 'kg/kg',
#                                                                                     'history': "{}Z altered by F. Hellmuth: Interpolate data from hybrid-sigma levels to isobaric levels with P=a*p0 + b*psfc. Calculate clivi with hydrostatic equation.".format(now.strftime("%d/%m/%Y %H:%M:%S"))})
            
# lwp_count_month = dset_dict[model]['lwp_lcc'].groupby('time.month').count(dim='time')
# lwp_count_season = dset_dict[model]['lwp'].groupby('time.season').count(dim='time')
# lcc_freq_djf = (lwp_count_month.sel(month=12)+lwp_count_month.sel(month=1)+lwp_count_month.sel(month=2)) / lwp_count_season.sel(season='DJF')
# lcc_freq_mam = (lwp_count_month.sel(month=3) + lwp_count_month.sel(month=4) + lwp_count_month.sel(month=5)) / lwp_count_season.sel(season='MAM')
# lcc_freq_jja = (lwp_count_month.sel(month=6) + lwp_count_month.sel(month=7) + lwp_count_month.sel(month=8)) / lwp_count_season.sel(season='JJA')
# lcc_freq_son = (lwp_count_month.sel(month=9) + lwp_count_month.sel(month=10) + lwp_count_month.sel(month=11)) / lwp_count_season.sel(season='SON')

# lcc_freq = xr.concat([lcc_freq_djf, lcc_freq_mam, lcc_freq_jja, lcc_freq_son], dim='season')
# plt_seasonal_NH_SH(lcc_freq, np.arange(0,1.1,0.1), 'LCC frequency', model)

Open CMIP6 variables#

… by using intake from pangeo.io, specifically intake-esm.

An example on Loading an ESM collection and searching for datasets can also be found on the Pangeo / ESGF Cloud Data Working Group documentation.

# cat_url = "https://storage.googleapis.com/cmip6/pangeo-cmip6.json"
# col = intake.open_esm_datastore(cat_url)
# col

Search corresponding data#

Get the data required for the analysis. Define variables, models, experiment, and time resolution as defined in 2. Data Wrangling .

  • use member_id = ‘r1i1p1f1’.

  • using intake-esm’s search() function:

    • col.search(variable_id, source_id, experiment_id, table_id, member_id, institution_id, grid_label)

# ## Variables
# variable_id=[
#             'cli',
#             'clivi',
#             'clw',
#             # 'lwp',
#             # 'pr',
#             'prsn',
#             # 'ta', 
#             'tas'
#              ]
# ## Models
# # list_models = [
# #     'NorESM2-MM',
# #     'TaiESM1',
# #     'EC-Earth3-AerChem',
# #     'GFDL-ESM4',
# #     'SAM0-UNICON',
# #     'CAMS-CSM1-0',
# #     'CMCC-CM2-HR4',
# #     'MPI-ESM1-2-HR',
# #     'BCC-CSM2-MR',
# #     'E3SM-1-1',
# #     'CMCC-CM2-SR5',
# #     'CMCC-ESM2',
# #     'FGOALS-f3-L',
# #     'E3SM-1-1-ECA',
# #     'CIESM',
# #     'GFDL-CM4',
# #     'MRI-ESM2-0']  

# list_models = ['MIROC6', 'CESM2', 'CanESM5', 'AWI-ESM-1-1-LR', 'MPI-ESM1-2-LR',
#        'UKESM1-0-LL', 'HadGEM3-GC31-LL', 'CNRM-CM6-1', 'CNRM-ESM2-1',
#        'IPSL-CM6A-LR', 'IPSL-CM5A2-INCA']
# ## experiment
# experiment_id = ['historical']

# ## time resolution
# t_res = ['day', 'CFday']
# ## search for variables, models, ...
# cat = col.search(variable_id=variable_id, source_id=list_models, experiment_id=experiment_id, table_id = t_res, member_id=['r1i1p1f1'])
# # cat.df
# ## show the CMIP6 models found in pandas Dataframe 
# cat.df['source_id'].unique()

Create dictionary from the list of datasets we found#

Load the found datasets into xarray dataset containers using intake-esm’s to_dataset_dict() function, which yields a Python dictionary.

NOTE: This step may take several minutes so be patient!

# dset_dict = cat.to_dataset_dict(zarr_kwargs={'use_cftime':True,})
# dset_dict.keys()
# # list all merged datasets and show coordinates
# for keys, ds in dset_dict.items():
#     print('{}: {}'.format(keys, list(ds.dims)))

Calendar#

Not all models in CMIP6 use the same calendar. Hence we double check the time axis. Later, when we regrid to the same horizontal resolution (Regrid CMIP6 data) we will assign the same calendars for each model.

# # metadata of the historical run:
# _d2 = pd.Series(["calendar",
#                  "branch_time_in_parent", #"parent_activity_id", "parent_experiment_id",	"parent_mip_era",
#                  "parent_source_id",#"parent_sub_experiment_id", 
#                  "parent_time_units",# "parent_variant_label"
#                   ])
# _d2 = pd.DataFrame(_d2).rename(columns={0:'index'})
# for i in dset_dict.keys():
#     _data = []
#     _names =[]
#     _data.append(dset_dict[i].time.to_index().calendar)
#     for k, v in dset_dict[i].attrs.items():
        
#         if 'parent_time_units' in k or 'branch_time_in_parent' in k or 'parent_source_id' in k:
#             _data.append(v)
#             _names.append(k)
#     _d2 = pd.concat([_d2,   pd.Series(_data)], axis=1)

# _d2.dropna(how='all', axis=1, inplace=True)
# _d2 = _d2.set_index('index')
# _d2.columns = _d2.loc['parent_source_id']
# _d2.drop('parent_source_id').T

Show attributes and individual identifier#

NorESM2-MM is going to be the reference model for the horizontal grid. The xarray datasets inside dset_dict can be extracted as any value in a Python dictionary.

The dictonary key for NorESM2-MM is: CMIP.NCC.NorESM2-MM.historical.Amon.gn

# if variable_id[0] == 'lwp':
#     ds = dset_dict['CMIP.NCC.NorESM2-MM.historical.AERmon.gn']
# else:
#     ds = dset_dict['CMIP.NCC.NorESM2-MM.historical.Amon.gn']
    

## attributes of the xarray dataset 
# ds[variable_id[0]].attrs, ds.attrs['tracking_id']
# dset_dict.keys()
# for model in dset_dict.keys():
#     for keys in dset_dict[model].keys():
#         print(model, keys)

Assign attributes to the variables#

We will assign the attributes to the variables as in ERA5 to make CMIP6 and ERA5 variables comperable.

  • cli and clw in kg kg-1 \(\rightarrow\) Multiply by 1000 to get g kg-1

  • clivi and lwp in kg m-2 \(\rightarrow\) Multiply by 1000 to get g m-2

  • pr and prsn in kg m-2 s-1 \(\rightarrow\) Multiply by 86400 to get mm day-1

# for var_id in variable_id:
#     for keys in dset_dict.keys():
#         if var_id == 'cli' or var_id == 'clw' or var_id == 'lwp' or var_id  == 'clivi':
#             dset_dict[keys][var_id] = dset_dict[keys][var_id]*1000
#             if var_id == 'cli':
#                 dset_dict[keys][var_id].attrs = {'units': 'g kg-1', 'long_name': 'Mass Fraction of Cloud Ice', 'standard_name': 'mass_fraction_of_cloud_ice_in_air', 'comment': 'Includes both large-scale and convective cloud. This is calculated as the mass of cloud ice in the grid cell divided by the mass of air (including the water in all phases) in the grid cell. It includes precipitating hydrometeors ONLY if the precipitating hydrometeors affect the calculation of radiative transfer in model.', 'cell_methods': 'area: time: mean (interval: 5 minutes)', 'cell_measures': 'area: areacella',}
#             if var_id == 'clw':
#                 dset_dict[keys][var_id].attrs = {'units': 'g kg-1', 'long_name': 'Mass Fraction of Cloud Liquid Water', 'standard_name': 'mass_fraction_of_cloud_liquid_water_in_air', 'comment': 'Includes both large-scale and convective cloud. Calculate as the mass of cloud liquid water in the grid cell divided by the mass of air (including the water in all phases) in the grid cells. Precipitating hydrometeors are included ONLY if the precipitating hydrometeors affect the calculation of radiative transfer in model.', 'cell_methods': 'area: time: mean', 'cell_measures': 'area: areacella'}
#             if var_id == 'clivi':
#                 dset_dict[keys][var_id].attrs = {'units': 'g m-2', 'long_name': 'Ice Water Path', 'comment': 'mass of ice water in the column divided by the area of the column (not just the area of the cloudy portion of the column). Includes precipitating frozen hydrometeors ONLY if the precipitating hydrometeor affects the calculation of radiative transfer in model.', 'cell_methods': 'area: time: mean', 'cell_measures': 'area: areacella'} 
#             if var_id == 'lwp':
#                 dset_dict[keys][var_id].attrs = {'units': 'g m-2', 'long_name': 'Liquid Water Path', 'comment': 'The total mass of liquid water in cloud per unit area.', 'cell_methods': 'area: time: mean', 'cell_measures': 'area: areacella'}

#         if var_id == 'pr' or var_id == 'prsn':
#             try: 
#                 dset_dict[keys][var_id] = dset_dict[keys][var_id]*86400
#                 if var_id == 'pr':
#                     dset_dict[keys][var_id].attrs = {'units': 'mm day-1', 'long_name': 'Precipitation', 'comment': 'includes both liquid and solid phases','cell_methods': 'area: time: mean', 'cell_measures': 'area: areacella'}
#                 if var_id == 'prsn':
#                     dset_dict[keys][var_id].attrs = {'units': 'mm day-1', 'long_name': 'Snowfall', 'comment': 'At surface; includes precipitation of all forms of water in the solid phase', 'cell_methods': 'area: time: mean', 'cell_measures': 'area: areacella'}
#             except KeyError:
#                 continue
        

Interpolate from CMIP6 hybrid sigma-pressure levels to ERA5 isobaric pressure levels#

The vertical variables in the CMIP6 models are in hybrid sigma-pressure levels. Hence the vertical variable in the xarray datasets in dset_dict will be calculated by using the GeoCAT-comb function to interpolate data from hybrid-sigma levels to isobaric levels.

The GeoCAT-comb function takes the following input:

  • data: Multidimensional data array, which holds hybrid-sigma levels and has a lev_dim coordinate.

  • ps: A multi-dimensional array of surface pressures (Pa), same time/space shape as data. Not all variables include the surface pressure, hence we will search the Pangeo.io catalog to find the surface pressure associated with the model.

  • hyam: One-dimensional arrays containing the hybrid A coefficients. Must have the same dimension size as the lev_dim dimension of data.

  • hybm: One-dimensional arrays containing the hybrid B coefficients. Must have the same dimension size as the lev_dim dimension of data.

  • p0: Scalar numeric value equal to surface reference pressure (Pa). Defaults to 100000 Pa.

  • new_levels: A one-dimensional array of output pressure levels (Pa). We will use the pressure of air_temperature (19 levels).

\[ P(i,j,k) = hyam(k) p0 + hybm(k) ps(i,j)\]
import geocat

geocat.comp.interpolation.interp_hybrid_to_pressure(data      =ds['variable'], 
                                                    ps        =ds['ps'], 
                                                    hyam      =ds['a'], 
                                                    hybm      =ds['b'], 
                                                    p0        =ds['p0'], 
                                                    new_levels=ds['plev'])
# # load the levels from the ERA5 file and transfer to Pa
# era_level = (xr.open_dataset('/scratch/franzihe/input/ERA5/monthly_means/0.25deg/clwc_Amon_ERA5_198501_198912.nc')['level'])*100

# if (('clw' or 'cli' or 'ta') in variable_id) == True:
#     # if var_id == 'clw' or var_id == 'cli':
#     for keys in dset_dict.keys():
#         # rename cmip pressure level for atmospheric temperature
#         dset_dict[keys] = dset_dict[keys].rename({'plev':'clev'})
#         # interpolate from hybrid to pressure levels for clw and cli
#         for var_id in [variable_id[variable_id.index('clw')], variable_id[variable_id.index('cli')]]:

#             if ('ps' in list(dset_dict[keys].keys())) == False:     # valid for models which don't provide ps in the clw or cli dataset
#                 model = keys.split('.')[2]
#                 ds_ps = col.search(source_id=model, table_id = ['Amon', ], experiment_id=['historical'], variable_id=['ps','p0', ], member_id=['r1i1p1f1']).to_dataset_dict(zarr_kwargs={'use_cftime':True,}, )
#                 dset_dict[keys].update(ds_ps[keys], )
#                 dset_dict[keys]['ps'] = dset_dict[keys]['ps'].isel(member_id = 0)
                
                
#             # Rename datasets with different naming convention for constant A
#             if ('a' in list(dset_dict[keys].keys())) == False:
#                 dset_dict[keys] = dset_dict[keys].rename({'ap':'a', 'ap_bnds': 'a_bnds'})
                    
                    
#             # Convert the model level to isobaric levels
#             #### a, b, ps, p0
#             if ('a' in list(dset_dict[keys].keys())) == True and ('b' in list(dset_dict[keys].keys())) == True and ('p0' in list(dset_dict[keys].keys())) == True and ('ps' in list(dset_dict[keys].keys())) == True:
#                 dset_dict[keys]['{}_interp'.format(var_id)] = gc.interpolation.interp_hybrid_to_pressure(data=dset_dict[keys][var_id].isel(member_id = 0), 
#                                                                                                                 ps=dset_dict[keys]['ps'],hyam=dset_dict[keys]['a'],
#                                                                                                                 hybm=dset_dict[keys]['b'],
#                                                                                                                 p0=dset_dict[keys]['p0'].values, 
#                                                                                                                 new_levels=(era_level).values, )
#                 # # remove the variables needed for the calculation of the isobaric levels. If this step is not performed, 
#                 # # the horizontal regridding will not be possible.
#                 # dset_dict[keys] = dset_dict[keys].drop(('a', 'p0', 'b', 'ps', 'a_bnds', 'b_bnds', var_id))
#                 dset_dict[keys] = dset_dict[keys].drop((var_id))
#                 dset_dict[keys] = dset_dict[keys].rename({'{}_interp'.format(var_id):var_id})

#             #### a, b, ps
#             elif ('a' in list(dset_dict[keys].keys())) == True and ('b' in list(dset_dict[keys].keys())) == True and ('ps' in list(dset_dict[keys].keys())) == True and ('p0' in list(dset_dict[keys].keys())) == False:
#                 dset_dict[keys]['{}_interp'.format(var_id)] = gc.interpolation.interp_hybrid_to_pressure(data=dset_dict[keys][var_id].isel(member_id=0), 
#                                                                                                                 ps=dset_dict[keys]['ps'],
#                                                                                                                 hyam=dset_dict[keys]['a'], 
#                                                                                                                 hybm=dset_dict[keys]['b'],
#                                                                                                                 p0=100000.0,
#                                                                                                                 new_levels=(era_level).values, )
#                 # dset_dict[keys] = dset_dict[keys].drop(('a', 'b', 'ps', 'a_bnds', 'b_bnds', var_id))
#                 dset_dict[keys] = dset_dict[keys].drop((var_id))
#                 dset_dict[keys] = dset_dict[keys].rename({'{}_interp'.format(var_id):var_id})
        
#         # remove the variables needed for the calculation of the isobaric levels. If this step is not performed, 
#         # the horizontal regridding will not be possible.
#         if ('p0' in list(dset_dict[keys].keys())) == True:
#             dset_dict[keys] = dset_dict[keys].drop(('a', 'p0', 'b', 'ps', 'a_bnds', 'b_bnds',))
#         elif ('p0' in list(dset_dict[keys].keys())) == False:
#             dset_dict[keys] = dset_dict[keys].drop(('a', 'b', 'ps', 'a_bnds', 'b_bnds',))
 

Regrid CMIP6 data to NorESM2-MM grid #

We want to conduct statistical analysis at the annual and seasonal timescales to determine the biases in cloud phase and precipitation (liquid and solid) in the CMIP6 models. At the moment we have all historical data from the CMIP6 models. For this, we will have to extract the 30-year period between 1985 and 2014.

\(\rightarrow\) Define a start and end year.

The CMIP6 high resolution models have approximately a nominal resolution of 100km. But not all have identical grid spacing. Hence we will make use of the python package xesmf and the documentation on decreasing resolution, Limitations and warnings.

NorESM2-MM will be the reference grid since we want to compare the models to the ERA5 data. The ERA5 data has a nominal resolution of 0.25deg and has been regridded to the same horizontal resolution as the NorESM2-MM in the ERA5 Jupyter Notebook.

\(\rightarrow\) Define NorESM2-MM as the reference grid ds_out.

Create a new Python dictionary (ds_gridded_dict) with the regridded CMIP6 xarray datasets between 1985 an 2014. Save each regridded model to a netcdf, locally.

NOTE: This step may take several minutes!

# starty = 1985; endy = 2014
# year_range = range(starty, endy+1)
# ds_out = dset_dict['CMIP.NCC.NorESM2-MM.historical.Amon.gn']
# ds_in  = dset_dict[model]

# import xesmf

# # Regridder data
# regridder = xesmf.Regridder(ds_in, ds_out, "conservative")

# # Apply regridder to data
#  # the entire dataset can be processed at once
# ds_in_regrid = regridder(ds_in)
# # create dictionary for reggridded data
# ds_gridded_dict = dict()

# # Read in the output grid from NorESM
# if variable_id[0] == 'lwp':
#     ds_out = dset_dict['CMIP.NCC.NorESM2-MM.historical.AERmon.gn'].isel(member_id = 0)
# else:
#     ds_out = dset_dict['CMIP.NCC.NorESM2-MM.historical.Amon.gn'].isel(member_id = 0)
# ds_out = ds_out.sel(time = ds_out.time.dt.year.isin(year_range)).squeeze()


# counter = 0

# for keys in dset_dict.keys():
#     # select only models which have atmospheric monthly values
#     amon = keys.split('.')[-2]
#     if amon == 'Amon' or amon == 'AERmon': 
#         # select model name 
#         model = keys.split('.')[2]
        
#         # select where data should be saved
#         if len(variable_id) > 1:
#             filename = '{}_Amon_1deg_{}01_{}12.nc'.format(variable_id, starty, endy)
#         elif len([variable_id]) == 1:
#             filename = '{}_Amon_1deg_{}01_{}12.nc'.format(variable_id[0], starty, endy)
#         # filename = '{}_Amon_1deg_{}01_{}12.nc'.format(variable_id[0], starty, endy)
#         savepath = '/scratch/franzihe/output/CMIP6_hist/1deg/{}/'.format(model)
#         nc_out = savepath + filename
#         files = glob(nc_out)
        
#         # Input data from CMIP6 model to be regridded
#         ds_in = dset_dict[keys].isel(member_id = 0)
#         ds_in = ds_in.sel(time = ds_in.time.dt.year.isin(year_range)).squeeze()
            
#         # common time grid
#         ds_in['time'] = ds_out['time']
            

#         # Regrid data
#         ds_in_regrid = fct.regrid_data(ds_in, ds_out)


#         # Shift the longitude from 0-->360 to -180-->180 and sort by longitude and time
#         ds_in_regrid = ds_in_regrid.assign_coords(lon=(((ds_in_regrid.lon + 180) % 360) - 180)).sortby('lon').sortby('time')
#         ds_in_regrid = ds_in_regrid.reset_coords(names=['time_bnds', ], drop=True)
            
            
#         # create dataset with all models
#         ds_gridded_dict[model] = ds_in_regrid

#         # if nc_out in files:
#         #     print('{} is downloaded'.format(nc_out))
#         #     counter += 1
#         #     print('Have regridded in total: {:} files'.format(str(counter)))
#         # else:    
#             # Save to netcdf file
#         ds_in_regrid.to_netcdf(nc_out)
#         print('file written: {}'.format(nc_out))

Connect all models into one Dataset with new coordinate ‘model’#

We will create a xarray.Dataset with all CMIP6 models, after the interpolation to the same horizonal (and vertical) resolution. This step will make the next steps easier, as we will not need the the full dictonary key and can just use the model name.

# # not needed if regridding works
# ds_gridded_dict = dict()
# for model in list_models:
#     ds_gridded_dict[model] = xr.open_dataset('/scratch/franzihe/output/CMIP6_hist/1deg/{}/{}_Amon_1deg_198501_201412.nc'.format(model,variable_id))
# # expand the model with nan array where missing variables
# for model in ds_gridded_dict.keys():
#     for var in variable_id:
#         # print(var, model)
#         if (var in ds_gridded_dict[model].variables) == False:
#             print(model, var)
#             if var == 'pr' or var == 'prsn':
#                 ds_gridded_dict[model][var] = xr.DataArray(data=da.full(shape = (ds_gridded_dict[model]['time'].shape[0], 
#                                                                                  ds_gridded_dict[model]['lat'].shape[0], 
#                                                                                  ds_gridded_dict[model]['lon'].shape[0]), 
#                                                             fill_value=np.nan, 
#                                                             chunks=(120, 721, 1440/2)), dims = [ds_gridded_dict[model].time.dims[0], ds_gridded_dict[model].lat.dims[0], ds_gridded_dict[model].lon.dims[0]], coords=[ds_gridded_dict[model].time.values, ds_gridded_dict[model].lat.values, ds_gridded_dict[model].lon.values])
# _ds = list(ds_gridded_dict.values())
# _coord = list(ds_gridded_dict.keys())
# ds_cmip = xr.concat(objs=_ds, dim=_coord, coords="all").rename({'concat_dim':'model'})
# ds_cmip = ds_cmip.drop('bnds')
# ds_cmip = xr.open_mfdataset('/scratch/franzihe/output/CMIP6_hist/1deg/*.nc')
# ds_cmip

Supercooled liquid water fraction#

\[SLF = \frac{\text{cloud liquid water content}}{\text{cloud liquid water content} + \text{cloud ice water content}}\]
# ds_cmip['SLF'] = ds_cmip['clw']/(ds_cmip['cli'] + ds_cmip['clw'])
# ds_cmip['SLF'].attrs = {'units': '', 'long_name':'Super cooled liquid water fraction'}
# # ds_cmip['SLF'] = fct.set4D_latitude_values_nan(ds_cmip['SLF'], 45, -45)

# # find only SLF where snowfall
# ds_cmip['SLF_sf'] = ds_cmip['SLF'].where((ds_cmip['prsn'] * 3600/86400) >= 0.01)
# ds_cmip['SLF_sf'] = ds_cmip['SLF_sf'].sortby('latitude', ascending=True)
# ds_cmip['SLF_sf'] = ds_cmip['SLF_sf'].groupby('time.season').mean(('time', 'longitude'), skipna=True)

# fig, axsm = plt.subplots(2,2, sharex=True, sharey=True, figsize=[15, 7.5])
# for ax, sea in zip(axsm.flatten(), ds_cmip['SLF_sf'].season):
#     cf = ds_cmip['SLF_sf'].sel(season=sea).plot(ax=ax,x='latitude', y='level', cmap=cm.tokyo_r, levels=np.arange(0,1.1,0.1), ylim= [1000, 1], yincrease=False, add_colorbar=False)
#     ax.grid()
#     ax.set(title='season = {}'.format(sea.values), ylabel='Pressure [hPa]', xlabel='Latitude')
    

# fig.suptitle('ERA5 ({} - {})'.format(starty, endy), fontweight="bold")
# cbaxes = fig.add_axes([0.1, 0.0, 0.8, 0.025])
# cbar = plt.colorbar(cf, cax=cbaxes, shrink=0.5, orientation='horizontal', label='Supercooled liquid water fraction')

Statistics#

For variables:

  • Snowfall [sf]

  • Total column cloud liquid, supercooled liqid, and rain water [tclslrw]

  • Total column cloud ice, snow water [tcisw]

  • 2m-Temperature [2t]

  1. Find where liquid water path is \(\ge\) 5 g m-2

  2. Find where snowfall is \(\ge\) 0.01mm h-1

  3. Find where 2m-temperature \(\le\) 0 \(^o\) C

# def set4D_latitude_values_nan(array, upper_lat, lower_lat):
#     array[:,:,int(array['lat'].where((array['lat'] > lower_lat) & (array['lat'] < upper_lat)).argmin('lat')) : int(array['lat'].where((array['lat'] > lower_lat) & (array['lat'] < upper_lat)).argmax('lat')), :] = xr.DataArray(data=da.full(shape=(array[:,:,int(array['lat'].where((array['lat']>lower_lat) & (array['lat']<upper_lat)).argmin('lat')) :\
#                                            int(array['lat'].where((array['lat']>lower_lat) & (array['lat']<upper_lat)).argmax('lat')), :]).shape,
#                           fill_value=np.nan),
#              dims=(array[:,:,int(array['lat'].where((array['lat']>lower_lat) & (array['lat']<upper_lat)).argmin('lat')) :\
#                              int(array['lat'].where((array['lat']>lower_lat) & (array['lat']<upper_lat)).argmax('lat')), :]).dims,
#              coords=(array[:,:,int(array['lat'].where((array['lat']>lower_lat) & (array['lat']<upper_lat)).argmin('lat')) :\
#                                int(array['lat'].where((array['lat']>lower_lat) & (array['lat']<upper_lat)).argmax('lat')), :]).coords)

#     return array
# ds_cmip['prsn']  = set4D_latitude_values_nan(ds_cmip['prsn'], 45, -45)
# ds_cmip['lwp']   = set4D_latitude_values_nan(ds_cmip['lwp'], 45, -45)
# ds_cmip['clivi'] = set4D_latitude_values_nan(ds_cmip['clivi'], 45, -45)
# ds_cmip['tas']   = set4D_latitude_values_nan(ds_cmip['tas'], 45, -45)
# # 1. find where LWP >=5 gm-2
# sf = ds_cmip['prsn'].where(ds_cmip['lwp']>=5)
# lwp = ds_cmip['lwp'].where(ds_cmip['lwp']>=5)
# iwp = ds_cmip['clivi'].where(ds_cmip['lwp']>=5)
# t2 = ds_cmip['tas'].where(ds_cmip['tas']>=5)

# # 2. find where snowfall >= 0.01mmh-1
# unit_sf = ds_cmip['prsn']*(3600/86400)
# sf = sf.where(unit_sf>=0.01)
# lwp = lwp.where(unit_sf>=0.01)
# iwp = iwp.where(unit_sf>=0.01)
# t2 = t2.where(unit_sf>=0.01)

# # 3. find where 2m-temperature <= 0C
# sf = sf.where(ds_cmip['tas']<=273.15)
# lwp = lwp.where(ds_cmip['tas']<=273.15)
# iwp = iwp.where(ds_cmip['tas']<=273.15)
# t2 = t2.where(ds_cmip['tas']<=273.15)

# # count occurences per season
# sf_count = sf.groupby('time.season').count(dim='time',keep_attrs=True)
# lwp_count = lwp.groupby('time.season').count(dim='time',keep_attrs=True)
# iwp_count = iwp.groupby('time.season').count(dim='time',keep_attrs=True)
# t2_count = t2.groupby('time.season').count(dim='time',keep_attrs=True)
# def plt_seasonal_NH_SH(variable,levels,cbar_label,plt_title):

#     f, axsm = plt.subplots(nrows=2,ncols=4,figsize =[10,7], subplot_kw={'projection': ccrs.NorthPolarStereo(central_longitude=0.0,globe=None)})

#     for ax, season in zip(axsm.flatten()[:4], variable.season):
#         ax.add_feature(cy.feature.COASTLINE, alpha=0.5)
#         ax.set_extent([-180, 180, 90, 45], ccrs.PlateCarree())
#         ax.set(title ='season = {}'.format(season.values))
#         gl = ax.gridlines(draw_labels=True)
#         gl.top_labels   = False
#         gl.right_labels = False
#         variable.sel(season=season).plot(ax=ax, transform=ccrs.PlateCarree(), add_colorbar=False,
#                             cmap=cm.hawaii_r, levels=levels)

#     for ax, i, season in zip(axsm.flatten()[4:], np.arange(5,9), variable.season):
#         ax.remove()
#         ax = f.add_subplot(2,4,i, projection=ccrs.SouthPolarStereo(central_longitude=0.0, globe=None))
#         ax.add_feature(cy.feature.COASTLINE, alpha=0.5)
#         ax.set_extent([-180, 180, -90, -45], ccrs.PlateCarree())
#         ax.set(title ='season = {}'.format(season.values))
#         gl = ax.gridlines(draw_labels=True)
#         gl.top_labels   = False
#         gl.right_labels = False
#         cf = variable.sel(season=season).plot(ax=ax, transform=ccrs.PlateCarree(), add_colorbar=False,
#                             cmap=cm.hawaii_r, levels=levels)

#     cbaxes = f.add_axes([1.0125, 0.025, 0.025, 0.9])
#     cbar = plt.colorbar(cf, cax=cbaxes, shrink=0.5,extend='max', orientation='vertical', label=cbar_label)
#     f.suptitle(plt_title, fontweight="bold");
#     plt.tight_layout(pad=0., w_pad=0., h_pad=0.)
# for model in sf_count['model']:

#     # snowfall event occurance
#     plt_seasonal_NH_SH((sf_count.where(sf_count>0.)).sel(model=model),np.arange(0,100,10),cbar_label ='{} frequency (month)',plt_title='{} {} ({} - {}) Count of months where sf >= 7.2mm'.format(model.values,sf_count.where(sf_count>0.).attrs['long_name'], starty,endy))

#     # snowfall divided by ice water path
#     sf_iwp = ((sf/(iwp)).groupby('time.season').mean('time', keep_attrs=True, skipna=True))
#     plt_seasonal_NH_SH(sf_iwp.sel(model=model),np.arange(0,0.0475,0.0025),cbar_label='precipitation efficency ()',plt_title='{} Snowfall/IWP ({} - {}) where sf >= 7.2mm'.format(model.values,starty,endy))

#     # snowfall divided by the sum of ice and liquid water path
#     sf_iwp_lwp = ((sf/(lwp+iwp)).groupby('time.season').mean('time', keep_attrs=True, skipna=True))
#     plt_seasonal_NH_SH(sf_iwp_lwp.sel(model=model), np.arange(0,0.0475,0.0025),cbar_label='precipiation efficency ()', plt_title='{} Snowfall/(IWP + LWP) ({} {}) where sf >= 7.2mm'.format(model.values,starty,endy))

#     # liquid water path
#     lwp_season = lwp.groupby('time.season').mean(dim='time',keep_attrs=True, skipna=True)
#     plt_seasonal_NH_SH(lwp_season.sel(model=model), np.arange(0,360, 10),cbar_label='{} ({})'.format(lwp_season.attrs['long_name'], lwp_season.attrs['units']),plt_title='{} {} ({} - {}) Count of months where sf >= 7.2mm'.format(model.values,lwp_season.attrs['long_name'], starty,endy))
# _iwp = iwp.groupby('time.season').mean(('time', ), keep_attrs=True, skipna=True)
# _lwp = lwp.groupby('time.season').mean(('time', ), keep_attrs=True, skipna=True)
# _sf = sf.groupby('time.season').mean(('time', ), keep_attrs=True, skipna=True)
# 'EC-Earth3-AerChem'
# 'NorESM2-MM'
# 'GFDL-ESM4'
# 'TaiESM1'
# _iwp.min().values, _iwp.max().values
# _sf.min().values, _sf.max().values
# from matplotlib.colors import LogNorm
# def plt_2dhist_iwp_sf(_iwp, _sf,model ):
#     f, axsm = plt.subplots(nrows=2,ncols=4,figsize =[10,5], sharex=True, sharey=True)
#     cmap = cm.batlow
#     # levels = np.arange(0.1,65000,5000)
#     # norm = BoundaryNorm(levels, ncolors=cmap.N, )
#     norm = LogNorm(vmin=1, vmax=65000)



#     for ax, season in zip(axsm.flatten()[:4], _iwp.season):
#         Z, xedges, yedges = np.histogram2d((_iwp.where(_iwp['lat'] >=45).sel(season=season).values.flatten()), 
#                                         (_sf.where(_sf['lat'] >=45).sel(season=season).values.flatten()), 
#                                         bins=[54, 25], 
#                                         range=[[5,545],[0, 25]])   

#         im = ax.pcolormesh(xedges, yedges, Z.transpose(), cmap=cmap,norm=norm,)
#         # cbar = f.colorbar(im, ax=ax,)
#         ax.set(title =r'lat$\geq 45^\circ$N; season = {}'.format(season.values))
#         ax.grid()

#     for ax, season in zip(axsm.flatten()[4:], _iwp.season):
#         Z, xedges, yedges = np.histogram2d((_iwp.where(_iwp['lat'] <=-45).sel(season=season).values.flatten()), 
#                                         (_sf.where(_sf['lat'] <=-45).sel(season=season).values.flatten()), 
#                                         bins=[54, 25], 
#                                         range=[[5,545],[0, 25]])   

#         im = ax.pcolormesh(xedges, yedges, Z.transpose(), cmap=cmap,norm=norm,)
#         # cbar = f.colorbar(im, ax=ax, )
#         ax.set(title =r'lat$\leq-45^\circ$S; season = {}'.format(season.values))
        
#         ax.set_xlabel('Ice Water Path ({})'.format(_iwp.attrs['units']))
#         ax.grid()
        
#     axsm.flatten()[0].set_ylabel('{} ({})'.format(_sf.attrs['long_name'], _sf.attrs['units']))
#     axsm.flatten()[4].set_ylabel('{} ({})'.format(_sf.attrs['long_name'], _sf.attrs['units']))


#     cbaxes = f.add_axes([1.0125, 0.025, 0.025, 0.9])
#     cbar = plt.colorbar(im, cax=cbaxes, shrink=0.5, orientation='vertical', label='frequency')
#     f.suptitle('{} ({} - {}) Months where sf >= 7.2mm'.format(model, starty,endy), fontweight="bold");
#     plt.tight_layout(pad=0.5, w_pad=0.5, h_pad=0.5)
# model = ['EC-Earth3-AerChem', 'NorESM2-MM', 'GFDL-ESM4', 'TaiESM1']
# for model in model:
#     plt_2dhist_iwp_sf(_iwp.sel(model=model), _sf.sel(model='NorESM2-MM'), model)

Find mixed-phase clouds#

Calculate the IWC/LWC statistics given by the values. Setting the value to 0.5 will find the level in the atmosphere where IWC and LWC are 50/50. Setting it to a value of 0.7 will find the level where IWC is 70% while LWC is 30%.

  1. find the fraction of IWC to LWC $\(fraction = \frac{IWC}{IWC + LWC}\)$

  2. find the nearest value to given IWC-fraction

  3. find atmospheric pressure levels where IWC/LWC fraction occurs

  4. find the index of the first atmospheric pressure level

  5. use the index to select variables

Create dictionary from the list of datasets we want to use for the IWC/LWC statistics#

Calculate the IWC/LWC statistics given by the values. Setting the value to 0.5 will find the level in the atmosphere where IWC and LWC are 50/50. Setting it to a value of 0.7 will find the level where IWC is 70% while LWC is 30%.

# _ds = list(dset_dict2.values())
# _coord = list(dset_dict2.keys())
# ds_cmip_1deg = xr.concat(objs=_ds, dim=_coord, coords="all").rename({'concat_dim':'stat'})

3. Exploratory Data Analysis #

Create seasonal mean of all regridded models#

…and plot seasonal mean of each individual model

# for var_id in list(ds_cmip.keys()):
#     ds_cmip = fct.seasonal_mean_std(ds_cmip, var_id)
# # extract the variable name similar to ERA5 for plotting
# var = fct.to_era_variable[variable_id[variable_id.index('prsn')]]

# for model in ds_cmip.model.values:
#     fct.plt_spatial_seasonal_mean(ds_cmip[variable_id[variable_id.index('prsn')]+'_season_mean'].sel(model=model), var, title='{} MEAN ({} - {})'.format(model,starty, endy))

Create model mean/spread of seasonal mean of all regridded models#

# ds_cmip[variable_id[variable_id.index('prsn')]+'_season_model_mean'] = ds_cmip[variable_id[variable_id.index('prsn')]+'_season_mean'].mean('model', keep_attrs=True, skipna = True)
# ds_cmip[variable_id[variable_id.index('prsn')]+'_season_model_std']  = ds_cmip[variable_id[variable_id.index('prsn')]+'_season_mean'].std('model', keep_attrs=True, skipna = True)
# fig, axs, im = fct.plt_spatial_seasonal_mean(ds_cmip[variable_id[variable_id.index('prsn')]+'_season_model_mean'], var, add_colorbar=False, title='CMIP6 - high resolution (1985 - 2014)')

# fig.subplots_adjust(right=0.8)
# cbar_ax = fig.add_axes([1, 0.15, 0.025, 0.7])
# cb = fig.colorbar(im, cax=cbar_ax, orientation="vertical", fraction=0.046, pad=0.04)
# cb.set_label(label='MEAN - {}'.format(fct.plt_dict[var][fct.plt_dict['header'].index('label')]), weight='bold')

# plt.tight_layout()


# axs[2].text(1,-0.12, ds_cmip.model.values.tolist()[0:5], size=12, ha="center", 
#          transform=axs[2].transAxes, bbox ={'facecolor':'green',
#                 'alpha':0.6,
#                 'pad':5})
# if len(ds_cmip.model.values.tolist()) > 4:
#     axs[2].text(1,-0.25, ds_cmip.model.values.tolist()[5:10], size=12, ha="center", 
#             transform=axs[2].transAxes, bbox ={'facecolor':'green',
#                     'alpha':0.6,
#                     'pad':5})
# if len(ds_cmip.model.values.tolist()) > 10:
#     axs[2].text(1,-0.38, ds_cmip.model.values.tolist()[10:-1], size=12, ha="center", 
#             transform=axs[2].transAxes, bbox ={'facecolor':'green',
#                     'alpha':0.6,
#                     'pad':5})
    

# # save figure to png
# figdir = '/uio/kant/geo-metos-u1/franzihe/Documents/Figures/CMIP6/'
# figname = '{}_season_mean_1deg_{}_{}.png'.format(variable_id[0], starty, endy)
# plt.savefig(figdir + figname, format = 'png', bbox_inches = 'tight', transparent = False)
# fig, axs, im = fct.plt_spatial_seasonal_mean(ds_cmip[variable_id[variable_id.index('prsn')]+'_season_model_mean'], var, add_colorbar=False, title='CMIP6 - high resolution (1985 - 2014)')

# fig.subplots_adjust(right=0.8)
# cbar_ax = fig.add_axes([1, 0.15, 0.025, 0.7])
# cb = fig.colorbar(im, cax=cbar_ax, orientation="vertical", fraction=0.046, pad=0.04)
# cb.set_label(label='MEAN - {}'.format(fct.plt_dict[var][fct.plt_dict['header'].index('label')]), weight='bold')



# for ax, i in zip(axs, ds_cmip[variable_id[variable_id.index('prsn')]+'_season_model_std'].season):
#     sm = ds_cmip[variable_id[variable_id.index('prsn')]+'_season_model_std'].sel(season=i).plot.contour(ax=ax, transform=ccrs.PlateCarree(), 
#                                                                       robust=True,
#                                                                       vmin = fct.plt_dict[var][fct.plt_dict['header'].index('vmin_std')], 
#                                                                       vmax = fct.plt_dict[var][fct.plt_dict['header'].index('vmax_std')],
#                                                                        levels = 6,
#                                                                       cmap=cm.lajolla,
#                                                                       add_colorbar=False)
    
# cbar_ax = fig.add_axes([1.10, 0.15, 0.025, 0.7])
# sb = fig.colorbar(sm, cax=cbar_ax, orientation="vertical", fraction=0.046, pad=0.04)
# sb.set_label(label='STD - {}'.format(fct.plt_dict[var][fct.plt_dict['header'].index('label')]), weight='bold')


# plt.tight_layout()


# axs[2].text(1,-0.12, ds_cmip.model.values.tolist()[0:5], size=12, ha="center", 
#          transform=axs[2].transAxes, bbox ={'facecolor':'green',
#                 'alpha':0.6,
#                 'pad':5})
# if len(ds_cmip.model.values.tolist()) > 4:
#     axs[2].text(1,-0.25, ds_cmip.model.values.tolist()[5:10], size=12, ha="center", 
#             transform=axs[2].transAxes, bbox ={'facecolor':'green',
#                     'alpha':0.6,
#                     'pad':5})
# if len(ds_cmip.model.values.tolist()) > 10:
#     axs[2].text(1,-0.38, ds_cmip.model.values.tolist()[10:-1], size=12, ha="center", 
#             transform=axs[2].transAxes, bbox ={'facecolor':'green',
#                     'alpha':0.6,
#                     'pad':5})
# # save figure to png
# figdir = '/uio/kant/geo-metos-u1/franzihe/Documents/Figures/CMIP6/'
# figname = '{}_season_mean_std_1deg_{}_{}.png'.format(variable_id[0], starty, endy)
# plt.savefig(figdir + figname, format = 'png', bbox_inches = 'tight', transparent = False)
# # save to netcdf
# filename = '{}_1deg_{}01_{}12.nc'.format(variable_id[0], starty, endy)
# savepath = '/scratch/franzihe/output/CMIP6_hist/1deg/'
# nc_out = savepath + filename
# files = glob(nc_out)

# counter = 0 
# # Save to netcdf file
# if nc_out in files:
# #     print('{} is downloaded'.format(nc_out))
# #     counter += 1
# #     print('Have saved in total: {:} files'.format(str(counter)))
# # else:
#     ds_cmip.to_netcdf(nc_out)
#     print('file written: .{}'.format(nc_out))

References #

[1] Zelinka, M. D., Myers, T. A., McCoy, D. T., Po-Chedley, S., Caldwell, P. M., Ceppi, P., et al. (2020). Causes of higher climate sensitivity in CMIP6 models. Geophysical Research Letters, 47, e2019GL085782. https://doi-org.ezproxy.uio.no/10.1029/2019GL085782

[2] Bjordal, J., Storelvmo, T., Alterskjær, K. et al. Equilibrium climate sensitivity above 5 °C plausible due to state-dependent cloud feedback. Nat. Geosci. 13, 718–721 (2020). https://doi-org.ezproxy.uio.no/10.1038/s41561-020-00649-1

[3] Wu, T., Lu, Y., Fang, Y., Xin, X., Li, L., Li, W., Jie, W., Zhang, J., Liu, Y., Zhang, L., Zhang, F., Zhang, Y., Wu, F., Li, J., Chu, M., Wang, Z., Shi, X., Liu, X., Wei, M., Huang, A., Zhang, Y., and Liu, X.: The Beijing Climate Center Climate System Model (BCC-CSM): the main progress from CMIP5 to CMIP6 , Geosci. Model Dev., 12, 1573–1600, https://doi.org/10.5194/gmd-12-1573-2019, 2019.

[4] Lee, W.-L., Wang, Y.-C., Shiu, C.-J., Tsai, I., Tu, C.-Y., Lan, Y.-Y., Chen, J.-P., Pan, H.-L., and Hsu, H.-H.: Taiwan Earth System Model Version 1: description and evaluation of mean state, Geosci. Model Dev., 13, 3887–3904, https://doi.org/10.5194/gmd-13-3887-2020, 2020.

[5] Bian HE, Yongqiang YU, Qing BAO, Pengfei LIN, Hailong LIU, Jinxiao LI, Lei WANG, Yimin LIU, Guoxiong WU, Kangjun CHEN, Yuyang GUO, Shuwen ZHAO, Xiaoqi ZHANG, Mirong SONG & Jinbo XIE (2020) CAS FGOALS-f3-L model dataset descriptions for CMIP6 DECK experiments, Atmospheric and Oceanic Science Letters, 13:6, 582-588, DOI: 10.1080/16742834.2020.1778419

[6] Cherchi, A., Fogli, P. G., Lovato, T., Peano, D., Iovino, D., Gualdi, S., et al. (2019). Global mean climate and main patterns of variability in the CMCC-CM2 coupled model. Journal of Advances in Modeling Earth Systems, 11, 185– 209. https://doi-org.ezproxy.uio.no/10.1029/2018MS001369

[7] van Noije, T., Bergman, T., Le Sager, P., O’Donnell, D., Makkonen, R., Gonçalves-Ageitos, M., Döscher, R., Fladrich, U., von Hardenberg, J., Keskinen, J.-P., Korhonen, H., Laakso, A., Myriokefalitakis, S., Ollinaho, P., Pérez García-Pando, C., Reerink, T., Schrödner, R., Wyser, K., and Yang, S.: EC-Earth3-AerChem: a global climate model with interactive aerosols and atmospheric chemistry participating in CMIP6 , Geosci. Model Dev., 14, 5637–5668, https://doi.org/10.5194/gmd-14-5637-2021, 2021.

[8] Golaz, J.-C., Caldwell, P. M., Van Roekel, L. P., Petersen, M. R., Tang, Q., Wolfe, J. D., et al. (2019). The DOE E3SM coupled model version 1: Overview and evaluation at standard resolution. Journal of Advances in Modeling Earth Systems, 11, 2089– 2129. https://doi-org.ezproxy.uio.no/10.1029/2018MS001603

[9] Burrows, S. M., Maltrud, M., Yang, X., Zhu, Q., Jeffery, N., Shi, X., et al. (2020). The DOE E3SM v1.1 biogeochemistry configuration: Description and simulated ecosystem-climate responses to historical changes in forcing. Journal of Advances in Modeling Earth Systems, 12, e2019MS001766. https://doi-org.ezproxy.uio.no/10.1029/2019MS001766

[10] Müller, W. A., Jungclaus, J. H., Mauritsen, T., Baehr, J., Bittner, M., Budich, R., et al. (2018). A higher-resolution version of the Max Planck Institute Earth System Model (MPI-ESM1.2-HR). Journal of Advances in Modeling Earth Systems, 10, 1383– 1413. https://doi-org.ezproxy.uio.no/10.1029/2017MS001217

[11] Yukimoto, S., H. Kawai, T. Koshiro, N. Oshima, K. Yoshida, S. Urakawa, H. Tsujino, M. Deushi, T. Tanaka, M. Hosaka, S. Yabu, H. Yoshimura, E. Shindo, R. Mizuta, A. Obata, Y. Adachi, and M. Ishii, 2019: The Meteorological Research Institute Earth System Model version 2.0, MRI-ESM2.0: Description and basic evaluation of the physical component. J. Meteor. Soc. Japan, 97, 931–965, doi:10.2151/jmsj.2019-051.

[12] Seland, Ø., Bentsen, M., Olivié, D., Toniazzo, T., Gjermundsen, A., Graff, L. S., Debernard, J. B., Gupta, A. K., He, Y.-C., Kirkevåg, A., Schwinger, J., Tjiputra, J., Aas, K. S., Bethke, I., Fan, Y., Griesfeller, J., Grini, A., Guo, C., Ilicak, M., Karset, I. H. H., Landgren, O., Liakka, J., Moseid, K. O., Nummelin, A., Spensberger, C., Tang, H., Zhang, Z., Heinze, C., Iversen, T., and Schulz, M.: Overview of the Norwegian Earth System Model (NorESM2) and key climate response of CMIP6 DECK, historical, and scenario simulations, Geosci. Model Dev., 13, 6165–6200, https://doi.org/10.5194/gmd-13-6165-2020, 2020.

[13] Held, I. M., Guo, H., Adcroft, A., Dunne, J. P., Horowitz, L. W., Krasting, J., et al. (2019). Structure and performance of GFDL’s CM4.0 climate model. Journal of Advances in Modeling Earth Systems, 11, 3691– 3727. https://doi-org.ezproxy.uio.no/10.1029/2019MS001829

[14] Dunne, J. P., Horowitz, L. W., Adcroft, A. J., Ginoux, P., Held, I. M., John, J. G., et al. (2020). The GFDL Earth System Model Version 4.1 (GFDL-ESM 4.1): Overall coupled model description and simulation characteristics. Journal of Advances in Modeling Earth Systems, 12, e2019MS002015. https://doi-org.ezproxy.uio.no/10.1029/2019MS002015

[15] Park, S., Shin, J., Kim, S., Oh, E., & Kim, Y. (2019). Global Climate Simulated by the Seoul National University Atmosphere Model Version 0 with a Unified Convection Scheme (SAM0-UNICON), Journal of Climate, 32(10), 2917-2949. Retrieved Jan 12, 2022, from https://journals-ametsoc-org.ezproxy.uio.no/view/journals/clim/32/10/jcli-d-18-0796.1.xml

[16] Lin, Y., Huang, X., Liang, Y., Qin, Y., Xu, S., & Huang, W., et al. (2020). Community Integrated Earth System Model (CIESM): Description and evaluation. Journal of Advances in Modeling Earth Systems, 12, e2019MS002036. https://doi-org.ezproxy.uio.no/10.1029/2019MS002036