Author: Kai Yang
Manuscript: Global trends in ocean fronts: impacts on air-sea CO2 flux and chlorophyll concentrations
Creation date: 2024-May-22
Latest update: 2024-Jun-03
This Jupyter notebook produces Fig. 5 of the manuscript.
# Import libraries
import xarray as xr
import numpy as np
import pandas as pd
import cartopy.crs as ccrs
import cmocean as cm
import geopandas as gpd
import matplotlib.pyplot as plt
from matplotlib.ticker import (MultipleLocator, AutoMinorLocator)
from matplotlib import ticker
from matplotlib.lines import Line2D
import matplotlib.font_manager as font_manager
# Inline plotting
%matplotlib inline
/Users/kyang7/anaconda3/lib/python3.11/site-packages/pandas/core/arrays/masked.py:60: UserWarning: Pandas requires version '1.3.6' or newer of 'bottleneck' (version '1.3.5' currently installed). from pandas.core import (
# Import plot utilities
from plot_utils_v2 import (ccrs_land,
add_patches,
area,
significance_mk,
plot_barhs,
compute_trends)
# read frontal areas shapefiles
keyF = gpd.read_file(r'../datasets/shapefiles/cumulativeMask.shp')
decF = gpd.read_file(r'../datasets/shapefiles/overlapMask_decline.shp')
intF = gpd.read_file(r'../datasets/shapefiles/overlapMask_intensify.shp')
# read co2 flux map
data = xr.open_dataset(r"../datasets/maps/GCB_fgco2_OceanModelA_month_annual_ensemble_2003-2022_v3.nc")
fco2_mean = data.OM_fgco2_A_ensemble*(-1)
# fco2_trend = data.fco2_trend
# fco2_mean[1:30,:] = np.nan
# fco2_mean[150:180,:] = np.nan
dataset_co2 = xr.open_dataset('../datasets/timeseries/CO2_masked_ts.nc')
# read timeseries by area
sum1 = dataset_co2.CO2sum_key*(-1)
sum2 = dataset_co2.CO2sum_int*(-1)
sum3 = dataset_co2.CO2sum_dec*(-1)
sum4 = dataset_co2.CO2sum_rem*(-1)
sum5 = dataset_co2.CO2sum_global*(-1)
# sum6 = dataset_co2.Key_Frontal_Polar_fco2
# sum7 = dataset_co2.Declining_Frontal_Polar_fco2
# mean calculation
sum_mean1 = sum1.mean()
sum_mean2 = sum2.mean()
sum_mean3 = sum3.mean()
sum_mean4 = sum4.mean()
sum_mean5 = sum5.mean()
# sum_mean6 = sum6.mean()
# sum_mean7 = sum7.mean()
dataset_co2 = xr.open_dataset('../datasets/timeseries/CO2_masked_ts.nc')
# read timeseries by area
net1 = dataset_co2.CO2_key*(-1)
net2 = dataset_co2.CO2_int*(-1)
net3 = dataset_co2.CO2_dec*(-1)
net4 = dataset_co2.CO2_rem*(-1)
net5 = dataset_co2.CO2_global*(-1)
# net6 = dataset_co2.Key_Frontal_Polar_fco2
# net7 = dataset_co2.Declining_Frontal_Polar_fco2
# mean calculation
net_mean1 = net1.mean()
net_mean2 = net2.mean()
net_mean3 = net3.mean()
net_mean4 = net4.mean()
net_mean5 = net5.mean()
# net_mean6 = net6.mean()
# net_mean7 = net7.mean()
# trend and significance calculation
slope_net1, intercept_net1 = compute_trends(net1)
slope_net2, intercept_net2 = compute_trends(net2)
slope_net3, intercept_net3 = compute_trends(net3)
slope_net4, intercept_net4 = compute_trends(net4)
slope_net5, intercept_net5 = compute_trends(net5)
# slope_net6, intercept_net6 = compute_trends(net6)
# slope_net7, intercept_net7 = compute_trends(net7)
co2_slopes_net=np.array([slope_net4, slope_net3, slope_net2, slope_net1, slope_net5])*10*12
trend_times = pd.date_range(start='1/06/2003', end='1/06/2023',freq='1M')
net_da1 = xr.DataArray(data=net1, coords=dict(time=trend_times)) # co2 flux per unit area
net_da2 = xr.DataArray(data=net2, coords=dict(time=trend_times)) # co2 flux per unit area
net_da3 = xr.DataArray(data=net3, coords=dict(time=trend_times)) # co2 flux per unit area
net_da4 = xr.DataArray(data=net4, coords=dict(time=trend_times)) # co2 flux per unit area
net_da5 = xr.DataArray(data=net5, coords=dict(time=trend_times)) # co2 flux per unit area
integrated_timeseries_net = [net_da4, net_da3, net_da2, net_da1, net_da5]
integrated_timeseries_dataarray_net = xr.concat(integrated_timeseries_net, dim='x')
hypothesis_net,n_effective_net = significance_mk(integrated_timeseries_dataarray_net)
std_net=np.std(integrated_timeseries_dataarray_net,axis=1)
err_net=1.96*(std_net/np.sqrt(n_effective_net))
/var/folders/fr/rwsbf6zj7qv0bm19q2wnmxmm0000gn/T/ipykernel_72652/2389196437.py:12: FutureWarning: 'M' is deprecated and will be removed in a future version, please use 'ME' instead. trend_times = pd.date_range(start='1/06/2003', end='1/06/2023',freq='1M')
hypothesis_net
array([1., 1., 1., 1., 1.])
fig = plt.figure(figsize=(60, 30),dpi=600)
plt.rcParams['figure.figsize'] = [40, 20]
fig, axs = plt.subplots(nrows=4,ncols=1,
subplot_kw={'projection': ccrs.Robinson(central_longitude=180)})
#################################################################################################################################################
#################################################################################################################################################
#################################################################################################################################################
# Panel a
axs[0] = fig.add_subplot(4, 1, 1, projection = ccrs.Robinson(central_longitude=180))
# map
im1 = fco2_mean.plot(ax=axs[0], transform = ccrs.PlateCarree(), add_colorbar=False,
cmap = 'BrBG_r', rasterized=True,vmin = -60, vmax = 60)
# axs[0].set_extent([-179.99,179.99,-85, 85], crs=ccrs.PlateCarree())
# frontal areas
keyF.plot(transform=ccrs.PlateCarree(),edgecolor='k', facecolor="None", linewidth=1.5, linestyle = "-", alpha=1, ax = axs[0])
decF.plot(transform=ccrs.PlateCarree(),edgecolor='#0787c3', facecolor="None", linewidth=1.5, linestyle = "-", alpha=1, ax = axs[0])
intF.plot(transform=ccrs.PlateCarree(),edgecolor='#ec3232', facecolor="None", linewidth=1.5, linestyle = "-", alpha=1, ax = axs[0])
plt.plot([0, 360], [60.1, 60.1],
color='black', linewidth=1, linestyle='--', markersize=3,
# Be explicit about which transform you want:
transform = ccrs.PlateCarree())
plt.plot([0, 360], [-60.1, -60.1],
color='black', linewidth=1, linestyle='--', markersize=3,
# Be explicit about which transform you want:
transform = ccrs.PlateCarree())
# land mask
axs[0].add_feature(ccrs_land)
add_patches(axs[0])
axs[0].set_title("a Air-sea CO$_2$ flux",fontname = 'Arial', fontsize=14, fontweight = "bold", loc = 'left')
# map settings
gl = axs[0].gridlines(crs=ccrs.PlateCarree(), draw_labels=True,
linewidth=0.5, color='gray', alpha=0.0, linestyle='--')
gl.xlabels_top = False
# gl.xlabels_bottom = False
gl.ylabels_right = False
gl.xlabel_style = {'size': 12, 'color': 'k','name': 'Arial'}
gl.ylabel_style = {'size': 12, 'color': 'k','name': 'Arial'}
#legend
colors1 = ['k', '#ec3232', '#0787c3']
lines = [Line2D([0], [0], color=c, linewidth=1.5, linestyle='-') for c in colors1]
labels = ['Key', 'Intensifying', 'Declining']
font = font_manager.FontProperties(family='Arial',size = 11)
axs[0].legend(lines, labels,loc='lower center', ncols=3, frameon=True,prop=font)
axs[0].text(-0.07, 0.55, 'Latitude', va='bottom', ha='center',
rotation='vertical', rotation_mode='anchor',fontweight = 'bold',
transform=axs[0].transAxes, fontname = 'Arial', fontsize = 12)
axs[0].text(0.5, -0.12, 'Longitude', va='bottom', ha='center',
rotation='horizontal', rotation_mode='anchor', fontweight = 'bold',
transform=axs[0].transAxes, fontname = 'Arial', fontsize = 12)
# colorbar
cbar_ax = fig.add_axes([0.45, 0.847, 0.035,0.005])
cbar=fig.colorbar(im1, cax=cbar_ax,orientation='horizontal',extend='both', ticks = [-60,-30, 0, 30, 60])
cbar.set_label('CO$_2$ flux (gC m$^{-2}$ yr$^{-1}$)', fontname = 'Arial', fontsize=9)
cbar.ax.tick_params(labelsize=10)
for l in cbar.ax.xaxis.get_ticklabels():
l.set_family("Arial")
cbar.outline.set_color('black')
#################################################################################################################################################
#################################################################################################################################################
#################################################################################################################################################
# Panel b
ax_b1 = plt.axes([0.423, 0.525, 0.055,0.15])
areas = ("Remaining","Declining", "Intensifying", "Key", "Global Ocean")
means = {
'Net': (sum_mean4/1000000000000000,sum_mean3/1000000000000000,sum_mean2/1000000000000000,sum_mean1/1000000000000000,sum_mean5/1000000000000000),
} # unit gC/year ---> GtC/year
x = np.arange(len(areas)) # the label locations
width = 0.6 # the width of the bars
multiplier = 0
colors=['darkgrey','#0787c3', '#ec3232','k' ,'#a6519e' ]
for attribute, measurement in means.items():
offset = width * multiplier
rects = ax_b1.barh(x, measurement, width, label=attribute, color = colors)
ax_b1.bar_label(rects, fontname = 'Arial', fontsize = 11,padding = 2,fmt='%.2f')
ax_b1.xaxis.set_major_locator(MultipleLocator(2))
ax_b1.xaxis.set_minor_locator(MultipleLocator(1))
# # Add some text for labels, title and custom x-axis tick labels, etc.
ax_b1.set_xlabel('GtC yr$^{-1}$',fontname = 'Arial', fontsize = 12)
ax_b1.set_title('b Total CO$_2$ flux',fontname = 'Arial', fontsize = 14, fontweight = 'bold',loc = 'left')
ax_b1.set_yticks(x, areas, fontname = 'Arial', fontsize = 12)
ax_b1.set_xticks([-3,-2,-1,0])
ax_b1.set_xticklabels([-3,-2,-1,0],fontname = 'Arial',fontsize = 12)
ax_b1.set_xlim(-3, 0)
# #################################################################################################################################################
# #################################################################################################################################################
# #################################################################################################################################################
# Panel c
ax_b2 = plt.axes([0.483, 0.525, 0.055,0.15])
areas = ("Remaining","Declining", "Intensifying", "Key", "Global Ocean")
means = {
'Net': (net_mean4,net_mean3,net_mean2,net_mean1,net_mean5),
} # Per unit area: unit gC/year ---> gC/m2/year
x = np.arange(len(areas)) # the label locations
width = 0.6 # the width of the bars
multiplier = 0
for attribute, measurement in means.items():
offset = width * multiplier
rects = ax_b2.barh(x + offset, measurement, width, label=attribute, color = colors)
ax_b2.bar_label(rects, fontname = 'Arial', fontsize = 11,padding = 2,fmt='%.2f')
multiplier += 1
ax_b2.xaxis.set_major_locator(MultipleLocator(10))
ax_b2.xaxis.set_minor_locator(MultipleLocator(5))
# # Add some text for labels, title and custom x-axis tick labels, etc.
ax_b2.set_xlabel('gC m$^{-2}$ yr$^{-1}$',fontname = 'Arial', fontsize = 12)
ax_b2.set_title('c CO$_2$ flux per unit area',fontname = 'Arial', fontsize = 14, fontweight = 'bold',loc = 'left')
ax_b2.set_yticks(x, areas)
ax_b2.set_xticks([-35,-30,-20,-15,-10,-5,0])
ax_b2.set_xticklabels([-35,-30,-20,-15,-10,-5,0],fontname = 'Arial',fontsize = 12)
ax_b2.set_xlim(-30, 0)
ax_b2.set_yticklabels([])
# #################################################################################################################################################
# #################################################################################################################################################
# #################################################################################################################################################
# Panel d
ax2 = plt.axes([0.543, 0.525, 0.055,0.15])
# bar plot
loc3 = [1,2,3,4,5]
rects = ax2.barh(loc3,co2_slopes_net,color = colors,alpha = 1,height = 0.6)
# errorbar
for ii in range(1,6):
plot_barhs(ax2,ii,co2_slopes_net[ii-1],err_net[ii-1],width = 0.00,linestyle='-',color='grey',linewidth=1.5)
# Make some labels.
labels = [f'label{i}' for i in range(len(rects))]
which = [0,1,2,3,4]
for index, rect in enumerate(rects):
if index in which:
if co2_slopes_net[index]<0:
ax2.text(rect.get_x() + rect.get_width()-0.65,loc3[index]-0.12,'{:,.2f}'.format(co2_slopes_net[index]),
ha='center', va='bottom',fontname = 'Arial', fontsize = 11)
else:
ax2.text(rect.get_x() + rect.get_width()+0.75,loc3[index]-0.12,'+{:,.2f}'.format(co2_slopes_net[index]),
ha='center', va='bottom',fontname = 'Arial', fontsize = 11)
# plot settings
ax2.xaxis.set_major_locator(MultipleLocator(1))
ax2.xaxis.set_minor_locator(MultipleLocator(1))
ax2.set_yticklabels([])
ax2.set_xticks([-3,-2,-1,0,1,2,3])
ax2.set_xticklabels([-3,-2,-1,0,1,2,3],fontname = 'Arial',fontsize = 12)
ax2.set_xlim(-2.5,2.5)
formatter = ticker.ScalarFormatter(useMathText=True)
formatter.set_scientific(True)
formatter.set_powerlimits((-1,1))
ax2.xaxis.set_major_formatter(formatter)
ax2.axvline(x=0, color='k', linestyle='-',linewidth = 0.5)
ax2.set_xlabel('gC m$^{-2}$ yr$^{-1}$ decade$^{-1}$ ',fontname = 'Arial', fontsize = 12)
ax2.set_title('d CO$_2$ flux trends',fontname = 'Arial', fontsize = 14, fontweight = 'bold',loc = 'left')
line1 = Line2D([0], [0], label='manual line', color='k')
####################################################################################################
fig.delaxes(axs[1])
fig.delaxes(axs[2])
fig.delaxes(axs[3])
plt.savefig('../JPEG/Fig5_FCO2_by_area.jpeg',dpi=600,bbox_inches='tight')
/Users/kyang7/anaconda3/lib/python3.11/site-packages/cartopy/mpl/gridliner.py:451: UserWarning: The .xlabels_top attribute is deprecated. Please use .top_labels to toggle visibility instead.
warnings.warn('The .xlabels_top attribute is deprecated. Please '
/Users/kyang7/anaconda3/lib/python3.11/site-packages/cartopy/mpl/gridliner.py:487: UserWarning: The .ylabels_right attribute is deprecated. Please use .right_labels to toggle visibility instead.
warnings.warn('The .ylabels_right attribute is deprecated. Please '
<Figure size 36000x18000 with 0 Axes>
Figure 5 Air-sea CO2 fluxes by area. Panel a: Global averaged air-sea CO2 fluxes (2003–2023; negative fluxes are into the ocean) with frontal areas indicated. Panel b: Total CO2 flux, which is the sum of the CO2 exchange between the ocean and atmosphere over the entire region in a year (unit: GtC yr-1). Panel c: CO2 flux per unit area, which is calculated by dividing the total CO2 flux by the surface area of the region of interest (unit: gC m-2 yr-1). Panel d: Decadal linear trends of CO2 flux by frontal area (unit: gC m-2 yr-1 decade-1), where negative trends indicate increased uptake by the ocean and positive trends indicate reduced uptake by the ocean. Standard errors are shown with grey error bars, and all presented trend values are significant (P < 0.05).
:)