paleoCAMP logo

5. More Info: Climate and Model Data Access#

Tutorials at the 2025 paleoCAMP | June 16–June 30, 2025

Jiang Zhu
jiangzhu@ucar.edu
Climate & Global Dynamics Laboratory
NSF National Center for Atmospheric Research


More information and examples to demonstrate data access and analysis

  • Guidance on using climate data

  • Access Earth System Model output

  • NCAR JupyterHub is your one-stop shop for data access and analysis!

    • Example 1: Access ERA5 Reanalysis on NSF NCAR’s Research Data Archive

    • Example 2: Access CESM output on NCAR’s Campaign Storage and perform model-data comparison

Time to go through: 15 minutes


Guidance on using climate data#


Access ESM output#


NCAR JupyterHub is your one-stop shop for data access and analysis!#

  • Multiple PB of CESM Paleoclimate simulation data

  • Other CESM simulation data

  • Preinstalled Python environment

Note: variable names may be different depending on the portals (IPCC Standard; CESM Standard)


Load Python packages

import os
import glob
from datetime import timedelta

import xarray as xr
import numpy as np
import pandas as pd

import matplotlib as mpl
import matplotlib.pyplot as plt
import cartopy.crs as ccrs
from cartopy.util import add_cyclic_point

# xesmf is used for regridding ocean output
import xesmf

import warnings
warnings.filterwarnings("ignore")

Example 1: Access ERA5 Reanalysis data on NSF NCAR’s Research Data Archive#

Browse the RDA website and figure out the file structure

  • Search era5

  • Click the Monthly Mean product (d633001)

  • Click the tab DATA ACCESS

  • Total precipitation is in ERA5 monthly mean atmospheric surface forecast (accumulated) [netCDF4]

  • Click GLADE File Listing to see all the files stored on the NCAR Campaign Storage

data_dir = '/glade/campaign/collections/rda/data/d633001/e5.moda.fc.sfc.accumu/'

files_tp = glob.glob(data_dir + '*/*_tp.*.nc')
print(*sorted(files_tp), sep='\n')
/glade/campaign/collections/rda/data/d633001/e5.moda.fc.sfc.accumu/1979/e5.moda.fc.sfc.accumu.128_228_tp.ll025sc.1979010100_1979120100.nc
/glade/campaign/collections/rda/data/d633001/e5.moda.fc.sfc.accumu/1980/e5.moda.fc.sfc.accumu.128_228_tp.ll025sc.1980010100_1980120100.nc
/glade/campaign/collections/rda/data/d633001/e5.moda.fc.sfc.accumu/1981/e5.moda.fc.sfc.accumu.128_228_tp.ll025sc.1981010100_1981120100.nc
/glade/campaign/collections/rda/data/d633001/e5.moda.fc.sfc.accumu/1982/e5.moda.fc.sfc.accumu.128_228_tp.ll025sc.1982010100_1982120100.nc
/glade/campaign/collections/rda/data/d633001/e5.moda.fc.sfc.accumu/1983/e5.moda.fc.sfc.accumu.128_228_tp.ll025sc.1983010100_1983120100.nc
/glade/campaign/collections/rda/data/d633001/e5.moda.fc.sfc.accumu/1984/e5.moda.fc.sfc.accumu.128_228_tp.ll025sc.1984010100_1984120100.nc
/glade/campaign/collections/rda/data/d633001/e5.moda.fc.sfc.accumu/1985/e5.moda.fc.sfc.accumu.128_228_tp.ll025sc.1985010100_1985120100.nc
/glade/campaign/collections/rda/data/d633001/e5.moda.fc.sfc.accumu/1986/e5.moda.fc.sfc.accumu.128_228_tp.ll025sc.1986010100_1986120100.nc
/glade/campaign/collections/rda/data/d633001/e5.moda.fc.sfc.accumu/1987/e5.moda.fc.sfc.accumu.128_228_tp.ll025sc.1987010100_1987120100.nc
/glade/campaign/collections/rda/data/d633001/e5.moda.fc.sfc.accumu/1988/e5.moda.fc.sfc.accumu.128_228_tp.ll025sc.1988010100_1988120100.nc
/glade/campaign/collections/rda/data/d633001/e5.moda.fc.sfc.accumu/1989/e5.moda.fc.sfc.accumu.128_228_tp.ll025sc.1989010100_1989120100.nc
/glade/campaign/collections/rda/data/d633001/e5.moda.fc.sfc.accumu/1990/e5.moda.fc.sfc.accumu.128_228_tp.ll025sc.1990010100_1990120100.nc
/glade/campaign/collections/rda/data/d633001/e5.moda.fc.sfc.accumu/1991/e5.moda.fc.sfc.accumu.128_228_tp.ll025sc.1991010100_1991120100.nc
/glade/campaign/collections/rda/data/d633001/e5.moda.fc.sfc.accumu/1992/e5.moda.fc.sfc.accumu.128_228_tp.ll025sc.1992010100_1992120100.nc
/glade/campaign/collections/rda/data/d633001/e5.moda.fc.sfc.accumu/1993/e5.moda.fc.sfc.accumu.128_228_tp.ll025sc.1993010100_1993120100.nc
/glade/campaign/collections/rda/data/d633001/e5.moda.fc.sfc.accumu/1994/e5.moda.fc.sfc.accumu.128_228_tp.ll025sc.1994010100_1994120100.nc
/glade/campaign/collections/rda/data/d633001/e5.moda.fc.sfc.accumu/1995/e5.moda.fc.sfc.accumu.128_228_tp.ll025sc.1995010100_1995120100.nc
/glade/campaign/collections/rda/data/d633001/e5.moda.fc.sfc.accumu/1996/e5.moda.fc.sfc.accumu.128_228_tp.ll025sc.1996010100_1996120100.nc
/glade/campaign/collections/rda/data/d633001/e5.moda.fc.sfc.accumu/1997/e5.moda.fc.sfc.accumu.128_228_tp.ll025sc.1997010100_1997120100.nc
/glade/campaign/collections/rda/data/d633001/e5.moda.fc.sfc.accumu/1998/e5.moda.fc.sfc.accumu.128_228_tp.ll025sc.1998010100_1998120100.nc
/glade/campaign/collections/rda/data/d633001/e5.moda.fc.sfc.accumu/1999/e5.moda.fc.sfc.accumu.128_228_tp.ll025sc.1999010100_1999120100.nc
/glade/campaign/collections/rda/data/d633001/e5.moda.fc.sfc.accumu/2000/e5.moda.fc.sfc.accumu.128_228_tp.ll025sc.2000010100_2000120100.nc
/glade/campaign/collections/rda/data/d633001/e5.moda.fc.sfc.accumu/2001/e5.moda.fc.sfc.accumu.128_228_tp.ll025sc.2001010100_2001120100.nc
/glade/campaign/collections/rda/data/d633001/e5.moda.fc.sfc.accumu/2002/e5.moda.fc.sfc.accumu.128_228_tp.ll025sc.2002010100_2002120100.nc
/glade/campaign/collections/rda/data/d633001/e5.moda.fc.sfc.accumu/2003/e5.moda.fc.sfc.accumu.128_228_tp.ll025sc.2003010100_2003120100.nc
/glade/campaign/collections/rda/data/d633001/e5.moda.fc.sfc.accumu/2004/e5.moda.fc.sfc.accumu.128_228_tp.ll025sc.2004010100_2004120100.nc
/glade/campaign/collections/rda/data/d633001/e5.moda.fc.sfc.accumu/2005/e5.moda.fc.sfc.accumu.128_228_tp.ll025sc.2005010100_2005120100.nc
/glade/campaign/collections/rda/data/d633001/e5.moda.fc.sfc.accumu/2006/e5.moda.fc.sfc.accumu.128_228_tp.ll025sc.2006010100_2006120100.nc
/glade/campaign/collections/rda/data/d633001/e5.moda.fc.sfc.accumu/2007/e5.moda.fc.sfc.accumu.128_228_tp.ll025sc.2007010100_2007120100.nc
/glade/campaign/collections/rda/data/d633001/e5.moda.fc.sfc.accumu/2008/e5.moda.fc.sfc.accumu.128_228_tp.ll025sc.2008010100_2008120100.nc
/glade/campaign/collections/rda/data/d633001/e5.moda.fc.sfc.accumu/2009/e5.moda.fc.sfc.accumu.128_228_tp.ll025sc.2009010100_2009120100.nc
/glade/campaign/collections/rda/data/d633001/e5.moda.fc.sfc.accumu/2010/e5.moda.fc.sfc.accumu.128_228_tp.ll025sc.2010010100_2010120100.nc
/glade/campaign/collections/rda/data/d633001/e5.moda.fc.sfc.accumu/2011/e5.moda.fc.sfc.accumu.128_228_tp.ll025sc.2011010100_2011120100.nc
/glade/campaign/collections/rda/data/d633001/e5.moda.fc.sfc.accumu/2012/e5.moda.fc.sfc.accumu.128_228_tp.ll025sc.2012010100_2012120100.nc
/glade/campaign/collections/rda/data/d633001/e5.moda.fc.sfc.accumu/2013/e5.moda.fc.sfc.accumu.128_228_tp.ll025sc.2013010100_2013120100.nc
/glade/campaign/collections/rda/data/d633001/e5.moda.fc.sfc.accumu/2014/e5.moda.fc.sfc.accumu.128_228_tp.ll025sc.2014010100_2014120100.nc
/glade/campaign/collections/rda/data/d633001/e5.moda.fc.sfc.accumu/2015/e5.moda.fc.sfc.accumu.128_228_tp.ll025sc.2015010100_2015120100.nc
/glade/campaign/collections/rda/data/d633001/e5.moda.fc.sfc.accumu/2016/e5.moda.fc.sfc.accumu.128_228_tp.ll025sc.2016010100_2016120100.nc
/glade/campaign/collections/rda/data/d633001/e5.moda.fc.sfc.accumu/2017/e5.moda.fc.sfc.accumu.128_228_tp.ll025sc.2017010100_2017120100.nc
/glade/campaign/collections/rda/data/d633001/e5.moda.fc.sfc.accumu/2018/e5.moda.fc.sfc.accumu.128_228_tp.ll025sc.2018010100_2018120100.nc
/glade/campaign/collections/rda/data/d633001/e5.moda.fc.sfc.accumu/2019/e5.moda.fc.sfc.accumu.128_228_tp.ll025sc.2019010100_2019120100.nc
/glade/campaign/collections/rda/data/d633001/e5.moda.fc.sfc.accumu/2020/e5.moda.fc.sfc.accumu.128_228_tp.ll025sc.2020010100_2020120100.nc
/glade/campaign/collections/rda/data/d633001/e5.moda.fc.sfc.accumu/2021/e5.moda.fc.sfc.accumu.128_228_tp.ll025sc.2021010100_2021120100.nc
/glade/campaign/collections/rda/data/d633001/e5.moda.fc.sfc.accumu/2022/e5.moda.fc.sfc.accumu.128_228_tp.ll025sc.2022010100_2022120100.nc
ds = xr.open_mfdataset(files_tp)
ds = ds.reindex(latitude=sorted(ds.latitude.values))
ds
<xarray.Dataset> Size: 2GB
Dimensions:    (latitude: 721, longitude: 1440, time: 528)
Coordinates:
  * latitude   (latitude) float64 6kB -90.0 -89.75 -89.5 ... 89.5 89.75 90.0
  * longitude  (longitude) float64 12kB 0.0 0.25 0.5 0.75 ... 359.2 359.5 359.8
  * time       (time) datetime64[ns] 4kB 1979-01-01 1979-02-01 ... 2022-12-01
Data variables:
    TP         (time, latitude, longitude) float32 2GB dask.array<chunksize=(3, 360, 776), meta=np.ndarray>
    utc_date   (time) int32 2kB dask.array<chunksize=(12,), meta=np.ndarray>
Attributes:
    DATA_SOURCE:          ECMWF: https://cds.climate.copernicus.eu, Copernicu...
    NETCDF_CONVERSION:    CISL RDA: Conversion from ECMWF GRIB 1 data to netC...
    NETCDF_VERSION:       4.6.1
    CONVERSION_PLATFORM:  Linux casper02 3.10.0-693.21.1.el7.x86_64 #1 SMP We...
    CONVERSION_DATE:      Mon Nov 11 08:45:33 MST 2019
    Conventions:          CF-1.6
    NETCDF_COMPRESSION:   NCO: Precision-preserving compression to netCDF4/HD...
    history:              Mon Nov 11 08:45:34 2019: ncks -4 --ppc default=7 e...
    NCO:                  netCDF Operators version 4.7.9 (Homepage = http://n...

Change the units from m per day to mm per day and make a plot#

See here for clarification of units.

tp = ds.TP.mean('time') * 1000.0
tp = tp.compute()
tp
<xarray.DataArray 'TP' (latitude: 721, longitude: 1440)> Size: 4MB
array([[0.18871191, 0.18871191, 0.18871191, ..., 0.18871191, 0.18871191,
        0.18871191],
       [0.1763593 , 0.17630874, 0.17631234, ..., 0.17640808, 0.17640086,
        0.17640446],
       [0.17613353, 0.17612088, 0.17608838, ..., 0.17611367, 0.17612632,
        0.17613533],
       ...,
       [0.6987416 , 0.69886446, 0.6989421 , ..., 0.6986495 , 0.69864047,
        0.69868386],
       [0.7080526 , 0.7081086 , 0.70814294, ..., 0.70803094, 0.7080472 ,
        0.70808154],
       [0.7012306 , 0.7012306 , 0.7012306 , ..., 0.7012306 , 0.7012306 ,
        0.7012306 ]], dtype=float32)
Coordinates:
  * latitude   (latitude) float64 6kB -90.0 -89.75 -89.5 ... 89.5 89.75 90.0
  * longitude  (longitude) float64 12kB 0.0 0.25 0.5 0.75 ... 359.2 359.5 359.8
fig, ax = plt.subplots(
    nrows=1, ncols=1,
    figsize=(3, 1.5),
    subplot_kw={'projection': ccrs.Robinson(central_longitude=210)},
    constrained_layout=True)

# Plot model results using contourf
p0 = ax.contourf(tp.longitude, tp.latitude, tp,
                 levels=np.linspace(0, 16, 17),
                 cmap='YlGnBu', extend='both',
                 transform=ccrs.PlateCarree())
plt.colorbar(p0, ax=ax)
ax.coastlines(linewidth=0.5)
ax.set_title('ERA5 Annual Mean Precipitation (mm/day)', fontsize=8)

plt.savefig('./precip_xy.era5.png', format='png', dpi=600, bbox_inches="tight")
_images/ca53fd03e4a862f6a5332de0af7eb5c675e0ed14428841f3cf299a5e047796f3.png

Example 2: Access CESM paleoclimate output and perform model-data comparison on NCAR’s Campaign Storage#

  • We try to reproduce Figure 2a of Jiang’s paper to use the LGM ΔSST to assess CESM2’s climate sensitivity.

  • Whole set of model output is shared at: /glade/campaign/cesm/community/palwg

  • You can find additional simulation data here: /glade/campaign/cgd/ppc/jiangzhu

  • We are in the process of organizing available data on the Paleoclimate Working Group webiste

  • As for now, prior knowledge of the experiments and file structure are needed. We hope to develop a Simulation Catalog for this!

!ls /glade/campaign/cesm/community/palwg/
ASD_paleoweather  Holocene		   LastGlacialMaximum	      TraCE
cam6_paleo_ppe	  iCESM1.2-DeglacialSlice  set_permission.csh
CESM1-LME	  iTRACE		   storage_use_policy.readme
!ls -l /glade/campaign/cesm/community/palwg/LastGlacialMaximum/CESM2
!ls /glade/campaign/cesm/community/palwg/LastGlacialMaximum/CESM2/b.e21.B1850CLM50SP.f09_g17.PI.01/ocn/proc/tseries/month_1/*.TEMP.*
!ls /glade/campaign/cesm/community/palwg/LastGlacialMaximum/CESM2/b.e21.B1850CLM50SP.f09_g17.21ka.01/ocn/proc/tseries/month_1/*.TEMP.*
total 3
drwxr-sr-x+ 7 jiangzhu cesm 4096 Aug  2  2024 b.e21.B1850CLM50SP.f09_g17.21ka.01
drwxr-sr-x+ 8 jiangzhu cesm 4096 Aug  2  2024 b.e21.B1850CLM50SP.f09_g17.PI.01
-rw-r--r--+ 1 jiangzhu cesm  413 Aug  2  2024 please_cite_zhu_etal_2021
/glade/campaign/cesm/community/palwg/LastGlacialMaximum/CESM2/b.e21.B1850CLM50SP.f09_g17.PI.01/ocn/proc/tseries/month_1/b.e21.B1850CLM50SP.f09_g17.PI.01.pop.h.TEMP.000101-010012.nc
/glade/campaign/cesm/community/palwg/LastGlacialMaximum/CESM2/b.e21.B1850CLM50SP.f09_g17.PI.01/ocn/proc/tseries/month_1/b.e21.B1850CLM50SP.f09_g17.PI.01.pop.h.TEMP.010101-020012.nc
/glade/campaign/cesm/community/palwg/LastGlacialMaximum/CESM2/b.e21.B1850CLM50SP.f09_g17.PI.01/ocn/proc/tseries/month_1/b.e21.B1850CLM50SP.f09_g17.PI.01.pop.h.TEMP.020101-030012.nc
/glade/campaign/cesm/community/palwg/LastGlacialMaximum/CESM2/b.e21.B1850CLM50SP.f09_g17.21ka.01/ocn/proc/tseries/month_1/b.e21.B1850CLM50SP.f09_g17.21ka.01.pop.h.TEMP.000101-010012.nc
/glade/campaign/cesm/community/palwg/LastGlacialMaximum/CESM2/b.e21.B1850CLM50SP.f09_g17.21ka.01/ocn/proc/tseries/month_1/b.e21.B1850CLM50SP.f09_g17.21ka.01.pop.h.TEMP.010101-020012.nc
/glade/campaign/cesm/community/palwg/LastGlacialMaximum/CESM2/b.e21.B1850CLM50SP.f09_g17.21ka.01/ocn/proc/tseries/month_1/b.e21.B1850CLM50SP.f09_g17.21ka.01.pop.h.TEMP.020101-030012.nc
/glade/campaign/cesm/community/palwg/LastGlacialMaximum/CESM2/b.e21.B1850CLM50SP.f09_g17.21ka.01/ocn/proc/tseries/month_1/b.e21.B1850CLM50SP.f09_g17.21ka.01.pop.h.TEMP.030101-040012.nc
/glade/campaign/cesm/community/palwg/LastGlacialMaximum/CESM2/b.e21.B1850CLM50SP.f09_g17.21ka.01/ocn/proc/tseries/month_1/b.e21.B1850CLM50SP.f09_g17.21ka.01.pop.h.TEMP.040101-050012.nc
campaign_dir = '/glade/campaign/cesm/community/palwg/LastGlacialMaximum/CESM2'
comp = 'ocn/proc/tseries/month_1'

Read preindustrial SST#

case = 'b.e21.B1850CLM50SP.f09_g17.PI.01'
fname = 'b.e21.B1850CLM50SP.f09_g17.PI.01.pop.h.TEMP.020101-030012.nc'

file = os.path.join(campaign_dir, case, comp, fname)

# Open the file and select the last 10 years of data
ds_pre = xr.open_dataset(file).isel(time=slice(-120, None))
sst_pre = ds_pre.TEMP.isel(z_t=0).mean('time')
sst_pre
<xarray.DataArray 'TEMP' (nlat: 384, nlon: 320)> Size: 492kB
array([[       nan,        nan,        nan, ...,        nan,        nan,
               nan],
       [       nan,        nan,        nan, ...,        nan,        nan,
               nan],
       [-1.9042568, -1.9034731, -1.9024575, ...,        nan,        nan,
               nan],
       ...,
       [       nan,        nan,        nan, ...,        nan,        nan,
               nan],
       [       nan,        nan,        nan, ...,        nan,        nan,
               nan],
       [       nan,        nan,        nan, ...,        nan,        nan,
               nan]], dtype=float32)
Coordinates:
    z_t      float32 4B 500.0
    ULONG    (nlat, nlon) float64 983kB ...
    ULAT     (nlat, nlon) float64 983kB ...
    TLONG    (nlat, nlon) float64 983kB ...
    TLAT     (nlat, nlon) float64 983kB ...
Dimensions without coordinates: nlat, nlon

Read LGM SST#

case = 'b.e21.B1850CLM50SP.f09_g17.21ka.01'
fname = 'b.e21.B1850CLM50SP.f09_g17.21ka.01.pop.h.TEMP.040101-050012.nc'

file = os.path.join(campaign_dir, case, comp, fname)

# Open the file and select the last 10 years of data
ds_lgm = xr.open_dataset(file).isel(time=slice(-120, None))
sst_lgm = ds_lgm.TEMP.isel(z_t=0).mean('time')
sst_lgm
<xarray.DataArray 'TEMP' (nlat: 384, nlon: 320)> Size: 492kB
array([[nan, nan, nan, ..., nan, nan, nan],
       [nan, nan, nan, ..., nan, nan, nan],
       [nan, nan, nan, ..., nan, nan, nan],
       ...,
       [nan, nan, nan, ..., nan, nan, nan],
       [nan, nan, nan, ..., nan, nan, nan],
       [nan, nan, nan, ..., nan, nan, nan]], dtype=float32)
Coordinates:
    z_t      float32 4B 500.0
    ULONG    (nlat, nlon) float64 983kB ...
    ULAT     (nlat, nlon) float64 983kB ...
    TLONG    (nlat, nlon) float64 983kB ...
    TLAT     (nlat, nlon) float64 983kB ...
Dimensions without coordinates: nlat, nlon

Regrid into the 1° × 1° grid using xesmf#

%%time

ds_pre['lat'] = ds_pre.TLAT
ds_pre['lon'] = ds_pre.TLONG

regridder = xesmf.Regridder(
    ds_in=ds_pre,
    ds_out=xesmf.util.grid_global(1, 1, cf=True, lon1=360),
    method='bilinear',
    periodic=True)

dsst_1x1 = regridder(sst_lgm - sst_pre)
dsst_1x1
CPU times: user 4.15 s, sys: 143 ms, total: 4.29 s
Wall time: 5.38 s
<xarray.DataArray (lat: 180, lon: 360)> Size: 259kB
array([[        nan,         nan,         nan, ...,         nan,
                nan,         nan],
       [        nan,         nan,         nan, ...,         nan,
                nan,         nan],
       [        nan,         nan,         nan, ...,         nan,
                nan,         nan],
       ...,
       [-0.17081444, -0.17075008, -0.17069343, ..., -0.17104828,
        -0.17096627, -0.1708865 ],
       [-0.178788  , -0.17878205, -0.17877994, ..., -0.17882909,
        -0.17881154, -0.17879783],
       [-0.18547155, -0.18547687, -0.18548317, ..., -0.18546143,
        -0.18546383, -0.1854672 ]], dtype=float32)
Coordinates:
    z_t                 float32 4B 500.0
  * lon                 (lon) float64 3kB 0.5 1.5 2.5 3.5 ... 357.5 358.5 359.5
    latitude_longitude  float64 8B nan
  * lat                 (lat) float64 1kB -89.5 -88.5 -87.5 ... 87.5 88.5 89.5
Attributes:
    regrid_method:  bilinear

Read proxy ΔSST in csv from Jess’s github#

url = 'https://raw.githubusercontent.com/jesstierney/lgmDA/master/proxyData/Tierney2020_ProxyDataPaired.csv'
proxy_dsst = pd.read_csv(url)
proxy_dsst.head()
Latitude Longitude Lower2s Median Upper2s ProxyType Species
0 -55.0 73.3 -2.927296 -1.379339 0.302709 delo pachy
1 -53.0 -58.0 1.426191 2.773898 4.253553 uk NaN
2 -51.1 67.7 -4.810437 -3.192364 -0.570439 delo pachy
3 -48.1 146.9 -4.784523 -3.370838 -1.886123 uk NaN
4 -46.1 90.1 -4.949764 -3.470513 -1.919271 delo bulloides

Plot the LGM ΔSST in the model and proxy records#

cmap = plt.get_cmap('YlGnBu_r')
norm = mpl.colors.Normalize(-12, 0)

fig, ax = plt.subplots(nrows=1, ncols=1,
                       figsize=(3, 1.5),
                       subplot_kw={'projection': ccrs.Robinson(central_longitude=210)},
                       constrained_layout=True)

# Plot model results using contourf
dsst_1x1_new, lon_new = add_cyclic_point(dsst_1x1, dsst_1x1.lon)
p0 = ax.contourf(lon_new, dsst_1x1.lat, dsst_1x1_new,
                 levels=np.linspace(-12, 0, 13),
                 cmap=cmap, norm=norm, extend='both',
                 transform=ccrs.PlateCarree())
plt.colorbar(p0, ax=ax)

# Create a land-sea mask and plot the LGM coastal line
lmask = xr.where(dsst_1x1.isnull(), 0, 1)
ax.contour(lmask.lon, lmask.lat, lmask,
           levels=[0.5],
           linewidths=0.5,
           colors='black',
           transform=ccrs.PlateCarree())

# Plot proxy SST using markers
ax.scatter(proxy_dsst['Longitude'],
           proxy_dsst['Latitude'],
           c=proxy_dsst['Median'],
           marker='o',
           s=15,
           cmap=cmap,
           edgecolors='black',
           lw=0.35,
           norm=norm,
           zorder=3,
           transform=ccrs.PlateCarree())

ax.set_title("LGM ΔSST: CESM2 vs proxy")
Text(0.5, 1.0, 'LGM ΔSST: CESM2 vs proxy')
_images/b99159841e36d44238c20b469f2d154e7ae300b71cd00c5288c9f0c0c7513072.png