Analysis of the Earth’s Radiation Budget using the CERES product#
This notebook-tutorial provides an introduction to the use of the Cloud and Earth’s Radiant Energy System (CERES) Energy Balanced and Filled (EBAF) Earth’s Radiation Budget (ERB) data record for climate studies.
1. Introduction#
You will find further information about the dataset as well as the data in the Climate Data Store catalogue entry Earth’s Radiation Budget, sections “Overview”, “Download data” and “Documentation”:
2. Search, download and view data#
Before we begin, we must prepare our environment. This includes installing the Application Programming Interface (API) of the CDS, and importing the various python libraries that we will need.
How to access the notebooks#
This tutorial is in the form of a Jupyter notebook, written in Python 3.9.4. You will not need to install any software for the training as there are a number of free cloud-based services to create, edit, run and export Jupyter notebooks such as this. Here are some suggestions (simply click on one of the links below to run the notebook):
Run the tutorial via free cloud platforms: |
|
|
|
---|
The cdsapi
is used to download the data. This package is not included by default on most cloud platforms yet. You can use pip
to install it:
!pip install cdsapi
Import libraries#
To run this notebook in your own environment, we advise you to install Anaconda, which contains most of the libraries you will need. You will also need to install the CDS API (!pip install cdsapi
) for downloading data programatically from the CDS. It is also recommended to use Python 3.9.4 for the purpose of reproducibility and compatibility, with respect to the latest updates of the different libraries.
The data have been stored in files written in NetCDF format. To best handle these, we will import the library Xarray which is specifically designed for manipulating multidimensional arrays in the field of geosciences. The libraries Matplotlib and Cartopy will also be imported for plotting and visualising the analysed data. We will also import the libraries zipfile to work with zip-archives, OS to use OS-functions and pattern expansion, and urllib3 for disabling warnings for data download via CDS API.
# Libraries to work with zip-archives, OS functions and pattern expansion
import zipfile
import os
# Disable warnings for data download via API
import urllib3
urllib3.disable_warnings()
# CDS API library
import cdsapi
# Libraries for working with multidimensional arrays
import xarray as xr
import numpy as np
import pandas as pd
# Import a sublibrary method for the seasonal decomposition of the time series
from statsmodels.tsa.seasonal import seasonal_decompose
# Libraries for plotting and visualising the data
import matplotlib.pyplot as plt
import cartopy.crs as ccrs
from cartopy.mpl.ticker import (LongitudeFormatter, LatitudeFormatter)
Download Data Using CDS API#
Set up CDS API credentials#
To set up your CDS API credentials, please login/register on the CDS, then follow the instructions here to obtain your API key.
You can add this API key to your current session by uncommenting the code below and replacing #########
with your API key.
URL = 'https://cds.climate.copernicus.eu/api'
KEY = '#####################'
Here we specify a data directory in which we will download our data and all output files that we will generate:
# Data directory for downloading the data
DATADIR = './'
# Filename for the zip file downloaded from the CDS
download_zip_file = os.path.join(DATADIR, 'ceres-erb-monthly.zip')
# Filename for the netCDF file which contain the merged contents of the monthly files.
merged_netcdf_file = os.path.join(DATADIR, 'ceres-erb-monthly.nc')
Search for data#
To search for data, visit the CDS website. Here you can search for CERES EBAF data using the search bar. The data we need for this use case is the Earth’s radiation budget from 1979 to present derived from satellite observations. This catalogue entry provides data from various sources: NASA CERES EBAF, NOAA/NCEI HIRS OLR, ESA Cloud-CCI, EUMETSAT’s CM SAF CLARA-A3, C3S CCI and C3S RMIB TSI.
After selecting the correct catalogue entry, we will specify the product family, the origin, the sensor on satellite (for CCI product family only), the type of variable, the type of climate data record, the time aggregation, the temporal and geographical coverage we are interested in. These can all be selected in the “Download data” tab. In this tab a form appears in which we will select the following parameters to download:
Product family:
CERES EBAF (Clouds and the Earth's Radiant Energy System (CERES) Energy Balanced And Filled (EBAF))
Origin:
NASA (National Aeronautics and Space Administration)
Variable:
all
(useSelect all
button)Climate data record type:
Thematic Climate Data Record (TCDR)
Time aggregation:
Monthly mean
Year:
all
(useSelect all
button)Month:
all
(useSelect all
button)Geographical area:
Whole available region
If you have not already done so, you will need to accept the “terms & conditions” of the data before you can download it.
At the end of the download form, select “Show API request”. This will reveal a block of code, which you can simply copy and paste into a cell of your Jupyter Notebook (see cell below) …
Download data#
… Having copied the API request into the cell below, running this will retrieve and download the data you requested into your local directory. However, before you run the cell below, the terms and conditions of this particular dataset need to have been accepted in the CDS. The option to view and accept these conditions is given at the end of the download form, just above the “Show API request” option.
dataset = "satellite-earth-radiation-budget"
request = {
'product_family': 'ceres_ebaf',
'origin': 'nasa',
'variable': ['incoming_shortwave_radiation',
'outgoing_longwave_radiation',
'outgoing_shortwave_radiation'
],
'climate_data_record_type': 'thematic_climate_data_record',
'time_aggregation': 'monthly_mean',
'year': ['%04d' % (year) for year in range(2000, 2023)],
'month': ['%02d' % (month) for month in range(0, 13)]
}
client = cdsapi.Client(url=URL,key=KEY)
client.retrieve(dataset, request, download_zip_file)
Inspect data#
The data have been downloaded. We can now unzip the archive and merge all files into one NetCDF file to inspect them. NetCDF is a commonly used format for array-oriented scientifc data. To read and process these data, we will make use of the Xarray library. Xarray is an open source project and Python package that makes working with labelled multi-dimensional arrays simple and efficient. We will read the data from our NetCDF file into an Xarray Dataset.
# Unzip the data. The dataset is splitted in monthly files.
with zipfile.ZipFile(download_zip_file, 'r') as zip_ref:
filelist = [os.path.join(DATADIR, f) for f in zip_ref.namelist()]
zip_ref.extractall(DATADIR)
# Ensure the filelist is in the correct order
filelist = sorted(filelist)
# Merge all unpacked files into one.
ds = xr.open_mfdataset(filelist)
ds.to_netcdf(merged_netcdf_file)
# Recursively delete unpacked data
for f in filelist:
os.remove(f)
# Read data
ds_ceres = xr.open_dataset(merged_netcdf_file, decode_times=True, mask_and_scale=True)
---------------------------------------------------------------------------
FileNotFoundError Traceback (most recent call last)
Cell In[7], line 14
12 # Recursively delete unpacked data
13 for f in filelist:
---> 14 os.remove(f)
16 # Read data
17 ds_ceres = xr.open_dataset(merged_netcdf_file, decode_times=True, mask_and_scale=True)
FileNotFoundError: [Errno 2] No such file or directory: './01'
Now we can query our newly created Xarray dataset…
ds_ceres
<xarray.Dataset> Size: 3MB Dimensions: (time: 12, lat: 180, lon: 360) Coordinates: * time (time) datetime64[ns] 96B 2022-01-15 ... 2022-12-15 * lat (lat) float32 720B -89.5 -88.5 -87.5 ... 87.5 88.5 89.5 * lon (lon) float32 1kB 0.5 1.5 2.5 3.5 ... 357.5 358.5 359.5 Data variables: toa_sw_all_mon (time, lat, lon) float32 3MB ... Attributes: title: CERES EBAF (Energy Balanced and Filled) ... institution: NASA/LaRC (Langley Research Center) Hamp... Conventions: CF-1.4 comment: Climatology from 07/2005 to 06/2015 version: Edition 4.2; Release Date December 9, 2022 DOI: 10.5067/TERRA-AQUA-NOAA20/CERES/EBAF-TOA... Fill_Value: Fill Value is -999.0 DODS_EXTRA.Unlimited_Dimension: time history: 2023-02-27 15:16:30 GMT Hyrax-1.16.2 htt...
We see that the dataset has three variables solar_mon, toa_lw_all_mon and toa_sw_all_mon which stand for the incoming solar flux, the top of atmosphere (TOA) longwave (LW) and shortwave (SW) fluxes respectively, as well as three dimension coordinates of lon, lat and time.
While an Xarray dataset may contain multiple variables, an Xarray data array holds a single multi-dimensional variable and its coordinates. To make the processing of the solar_mon, toa_lw_all_mon and toa_sw_all_mon data easier, we convert them into three distinct Xarray data arrays. We can also define a new data array da_toa_erb that corresponds to the TOA net Earth’s radative budget and is defined as the difference between the incoming solar flux and the sum of the outgoing (LW, SW) fluxes at the top the atmosphere.
# Outgoing longwave radiation data array
da_toa_lw = ds_ceres['toa_lw_all_mon']
# Incoming shortwave radiation data array
da_solar = ds_ceres['solar_mon']
# Outgoing shortwave radiation data array
da_toa_sw = ds_ceres['toa_sw_all_mon']
# Creation of the net radiative flux data array
da_toa_erb = da_solar - da_toa_sw - da_toa_lw
# Name of the data array
da_toa_erb.name = 'toa_erb_all_mon'
# Add some attributes to the new data array
da_toa_erb.attrs = {
'long_name': "Top of The Atmosphere Net Earth's Radiative Flux",
'standard_name': "TOA Net Flux",'CF_name': "toa_net_radiative_flux",
'comment': "none",
'units': "W.m$^{-2}$",
}
---------------------------------------------------------------------------
KeyError Traceback (most recent call last)
~/.local/lib/python3.10/site-packages/xarray/core/dataset.py in ?(self, name)
1474 variable = self._variables[name]
1475 except KeyError:
-> 1476 _, name, variable = _get_virtual_variable(self._variables, name, self.sizes)
1477
KeyError: 'toa_lw_all_mon'
During handling of the above exception, another exception occurred:
KeyError Traceback (most recent call last)
~/.local/lib/python3.10/site-packages/xarray/core/dataset.py in ?(self, key)
1573 return self._construct_dataarray(key)
1574 except KeyError as e:
-> 1575 raise KeyError(
1576 f"No variable named {key!r}. Variables on the dataset include {shorten_list_repr(list(self.variables.keys()), max_items=10)}"
~/.local/lib/python3.10/site-packages/xarray/core/dataset.py in ?(self, name)
1474 variable = self._variables[name]
1475 except KeyError:
-> 1476 _, name, variable = _get_virtual_variable(self._variables, name, self.sizes)
1477
~/.local/lib/python3.10/site-packages/xarray/core/dataset.py in ?(variables, key, dim_sizes)
207 split_key = key.split(".", 1)
208 if len(split_key) != 2:
--> 209 raise KeyError(key)
210
KeyError: 'toa_lw_all_mon'
The above exception was the direct cause of the following exception:
KeyError Traceback (most recent call last)
/tmp/ipykernel_11596/1107301915.py in ?()
1 # Outgoing longwave radiation data array
----> 2 da_toa_lw = ds_ceres['toa_lw_all_mon']
3
4 # Incoming shortwave radiation data array
5 da_solar = ds_ceres['solar_mon']
~/.local/lib/python3.10/site-packages/xarray/core/dataset.py in ?(self, key)
1571 if utils.hashable(key):
1572 try:
1573 return self._construct_dataarray(key)
1574 except KeyError as e:
-> 1575 raise KeyError(
1576 f"No variable named {key!r}. Variables on the dataset include {shorten_list_repr(list(self.variables.keys()), max_items=10)}"
1577 ) from e
1578
KeyError: "No variable named 'toa_lw_all_mon'. Variables on the dataset include ['toa_sw_all_mon', 'time', 'lat', 'lon']"
Let us view these data, e.g. the toa_erb_all_mon data from our newly created da_toa_erb data array:
da_toa_erb
<xarray.DataArray 'toa_erb_all_mon' (time: 274, lat: 180, lon: 360)> Size: 71MB array([[[-126.02 , -126.02 , -126.02 , ..., -126.02 , -126.02 , -126.02 ], [-124.909996, -124.909996, -124.909996, ..., -124.909996, -124.909996, -124.909996], [-124.34999 , -124.34999 , -124.34999 , ..., -124.34999 , -124.34999 , -124.34999 ], ..., [-168.57 , -168.57 , -168.57 , ..., -168.57 , -168.57 , -168.57 ], [-168.98 , -168.98 , -168.98 , ..., -168.98 , -168.98 , -168.98 ], [-172.40001 , -172.40001 , -172.40001 , ..., -172.40001 , -172.40001 , -172.40001 ]], [[-124.162 , -124.162 , -124.162 , ..., -124.162 , -124.162 , -124.162 ], [-124.28 , -124.28 , -124.28 , ..., -124.28 , -124.28 , -124.28 ], [-125.229 , -125.229 , -125.229 , ..., -125.229 , -125.229 , -125.229 ], ... [-177.049 , -177.049 , -177.049 , ..., -177.049 , -177.049 , -177.049 ], [-175.149 , -175.149 , -175.149 , ..., -175.149 , -175.149 , -175.149 ], [-174.549 , -174.549 , -174.549 , ..., -174.549 , -174.549 , -174.549 ]], [[ -19.59999 , -19.59999 , -19.59999 , ..., -19.59999 , -19.59999 , -19.59999 ], [ -16.699982, -16.699982, -16.699982, ..., -16.699982, -16.699982, -16.699982], [ -12.900009, -12.900009, -12.900009, ..., -12.900009, -12.900009, -12.900009], ..., [-172.546 , -172.546 , -172.546 , ..., -172.546 , -172.546 , -172.546 ], [-171.746 , -171.746 , -171.746 , ..., -171.746 , -171.746 , -171.746 ], [-171.147 , -171.147 , -171.147 , ..., -171.147 , -171.147 , -171.147 ]]], dtype=float32) Coordinates: * time (time) datetime64[ns] 2kB 2000-03-15 2000-04-15 ... 2022-12-15 * lat (lat) float32 720B -89.5 -88.5 -87.5 -86.5 ... 86.5 87.5 88.5 89.5 * lon (lon) float32 1kB 0.5 1.5 2.5 3.5 4.5 ... 356.5 357.5 358.5 359.5 Attributes: long_name: Top of The Atmosphere Net Earth's Radiative Flux standard_name: TOA Net Flux CF_name: toa_net_radiative_flux comment: none units: W.m$^{-2}$
Plot data#
We can visualize one time step to figure out what the data look like. Xarray offers built-in matplotlib functions that allow you to plot a DataArray
. With the function plot()
, you can easily plot e.g. the first time step of the loaded array.
da_toa_erb[0, :, :].plot()
<matplotlib.collections.QuadMesh at 0x1687351c0>
![../_images/063dfe9b6fdfc76ed6ecc61b12ad975c9acd708a3673b7cf1cda95e58fd743f0.png](../_images/063dfe9b6fdfc76ed6ecc61b12ad975c9acd708a3673b7cf1cda95e58fd743f0.png)
Figure 1 shows the Top of the Atmosphere Net Earth’s Radiative Flux for March 2000.
An alternative to the built-in Xarray plotting functions is to make use of a combination of the plotting libraries matplotlib and Cartopy. One of Cartopy’s key features is its ability to transform array data into different geographic projections. In combination with matplotlib, it is a very powerful way to create high-quality visualisations and animations. In later plots, we will make use of these libraries to produce more customised visualisations.
3. Climatology of the Earth’s Radiation Budget#
3.1. Climatology of the top-of-atmosphere incoming solar radiation#
Time averaged global climatological distribution of TIS#
To calculate the mean climatology of the top of the atmosphere incoming solar radiation for the time period January 2001 to December 2021, we have to select the specific time range using the Xarray method sel()
that indexes the data and dimensions by the appropriate indexers. We can then use the method mean()
to calculate the mean along the time dimension.
# Select the TOA incoming solar radiation data for the whole time period
solar = da_solar.sel(time=slice('2001-01-01', '2021-12-31'))
# Calculate the mean along the time dimension
solar_mean = solar.mean(dim='time')
We can now visualize the time averaged global climatological distribution of the incoming solar radiation for the period January 2001 - December 2021. This time, we will make use of a combination of the plotting libraries Matplotlib and Cartopy to create a more customised figure.
# Create the figure panel and the map using the Cartopy PlateCarree projection
fig2, ax2 = plt.subplots(1, 1, figsize=(16, 8), subplot_kw={'projection': ccrs.PlateCarree()})
# Plot the data
im = plt.pcolormesh(solar_mean.lon, solar_mean.lat, solar_mean, cmap='YlOrRd')
# Set the figure title, add lat/lon grid and coastlines
ax2.set_title(
'$\\bf{Climatology\ of\ TIS\ from\ CERES\ EBAF\ (January\ 2001\ -\ December\ 2021)}$',
fontsize=20,
pad=20)
# Add coastlines
ax2.coastlines(color='black')
# Define gridlines and ticks
ax2.set_xticks(np.arange(-180, 181, 60), crs=ccrs.PlateCarree())
ax2.set_yticks(np.arange(-90, 91, 30), crs=ccrs.PlateCarree())
lon_formatter = LongitudeFormatter()
lat_formatter = LatitudeFormatter()
ax2.xaxis.set_major_formatter(lon_formatter)
ax2.yaxis.set_major_formatter(lat_formatter)
# Gridlines
gl = ax2.gridlines(linewidth=1, color='gray', alpha=0.5, linestyle='--')
# Specify the colorbar
cbar = plt.colorbar(im, fraction=0.025, pad=0.05, extend='both')
cbar.set_label('W.m$^{-2}$')
# Save the figure
fig2.savefig(f'{DATADIR}ceres_solar_climatology.png')
<>:9: SyntaxWarning: invalid escape sequence '\ '
<>:9: SyntaxWarning: invalid escape sequence '\ '
/var/folders/kx/ksg5wbrj1sq95rp2tz_72j6c0000gn/T/ipykernel_1471/715168913.py:9: SyntaxWarning: invalid escape sequence '\ '
'$\\bf{Climatology\ of\ TIS\ from\ CERES\ EBAF\ (January\ 2001\ -\ December\ 2021)}$',
![../_images/604099898a86165d80f075ee06d07976c645ea8df265b12e85b56f38793f38ff.png](../_images/604099898a86165d80f075ee06d07976c645ea8df265b12e85b56f38793f38ff.png)
Figure 2 shows the regular latitude dependent climatological distribution of the incoming solar radiation at the top of the atmosphere, with values decreasing from the tropics to the high latitudes.
Time averaged seasonal climatological distribution of TIS#
The TOA incoming solar radiation data can also be splitted according to the seasons by using the groupby()
method, with 'time.season'
as an argument, and then averaged over the years.
Seasons are defined as follows, using the Northern Hemisphere (NH) as reference:
NH spring: March, April, May
NH summer: June, July, August
NH autumn: September, October, November
NH winter: December, January, February
# Split data array solar by season
solar_seasonal_climatology = solar.groupby('time.season').mean('time')
solar_seasonal_climatology
<xarray.DataArray 'solar_mon' (season: 4, lat: 180, lon: 360)> Size: 1MB array([[[450.3857 , 450.3857 , 450.3857 , ..., 450.3857 , 450.3857 , 450.3857 ], [450.2461 , 450.2461 , 450.2461 , ..., 450.2461 , 450.2461 , 450.2461 ], [449.9793 , 449.9793 , 449.9793 , ..., 449.9793 , 449.9793 , 449.9793 ], ..., [ 0. , 0. , 0. , ..., 0. , 0. , 0. ], [ 0. , 0. , 0. , ..., 0. , 0. , 0. ], [ 0. , 0. , 0. , ..., 0. , 0. , 0. ]], [[ 0. , 0. , 0. , ..., 0. , 0. , 0. ], [ 0. , 0. , 0. , ..., 0. , 0. , 0. ], [ 0. , 0. , 0. , ..., 0. , 0. , 0. ], ... [229.18077 , 229.18077 , 229.18077 , ..., 229.18077 , 229.18077 , 229.18077 ], [228.66728 , 228.66728 , 228.66728 , ..., 228.66728 , 228.66728 , 228.66728 ], [228.41019 , 228.41019 , 228.41019 , ..., 228.41019 , 228.41019 , 228.41019 ]], [[221.11821 , 221.11821 , 221.11821 , ..., 221.11821 , 221.11821 , 221.11821 ], [221.38689 , 221.38689 , 221.38689 , ..., 221.38689 , 221.38689 , 221.38689 ], [221.92633 , 221.92633 , 221.92633 , ..., 221.92633 , 221.92633 , 221.92633 ], ..., [ 25.165077, 25.165077, 25.165077, ..., 25.165077, 25.165077, 25.165077], [ 24.507938, 24.507938, 24.507938, ..., 24.507938, 24.507938, 24.507938], [ 24.182222, 24.182222, 24.182222, ..., 24.182222, 24.182222, 24.182222]]], dtype=float32) Coordinates: * lat (lat) float32 720B -89.5 -88.5 -87.5 -86.5 ... 86.5 87.5 88.5 89.5 * lon (lon) float32 1kB 0.5 1.5 2.5 3.5 4.5 ... 356.5 357.5 358.5 359.5 * season (season) object 32B 'DJF' 'JJA' 'MAM' 'SON' Attributes: long_name: Incoming Solar Flux, Monthly Means standard_name: Incoming Solar Flux CF_name: toa_incoming_shortwave_flux comment: none units: W m-2 valid_min: 0.0 valid_max: 800.0
The xarray solar_seasonal_climatology has four entries in the time dimension (one for each season). The climatology of the incident solar flux distribution can now be plotted for each season.
# Create a list of the seasons such as defined in the dataset solar_seasonal_climatology:
seasons = ['MAM', 'JJA', 'SON', 'DJF']
# We use the "subplots" to place multiple plots according to our needs.
# In this case, we want 4 plots in a 2x2 format.
# For this "nrows" = 2 and "ncols" = 2, the projection and size are defined as well
fig3, ax3 = plt.subplots(nrows=2,
ncols=2,
subplot_kw={'projection': ccrs.PlateCarree()},
figsize=(16,8))
# Define a dictionary of subtitles, each one corresponding to a season
subtitles = {'MAM': 'NH spring', 'JJA': 'NH summer', 'SON': 'NH autumn', 'DJF': 'NH winter'}
# Configure the axes and subplot titles
for i_season, c_season in enumerate(seasons):
# convert i_season index into (row, col) index
row = i_season // 2
col = i_season % 2
# Plot data (coordinates and data) and define colormap
im = ax3[row][col].pcolormesh(solar_seasonal_climatology.lon,
solar_seasonal_climatology.lat,
solar_seasonal_climatology.sel(season=c_season),
cmap='YlOrRd')
# Set title and size
ax3[row][col].set_title('Seasonal climatology of TIS - ' + subtitles[c_season], fontsize=16)
# Add coastlines
ax3[row][col].coastlines()
# Define grid lines and ticks (e.g. from -180 to 180 in an interval of 60)
ax3[row][col].set_xticks(np.arange(-180, 181, 60), crs=ccrs.PlateCarree())
ax3[row][col].set_yticks(np.arange(-90, 91, 30), crs=ccrs.PlateCarree())
lon_formatter = LongitudeFormatter()
lat_formatter = LatitudeFormatter()
ax3[row][col].xaxis.set_major_formatter(lon_formatter)
ax3[row][col].yaxis.set_major_formatter(lat_formatter)
# Gridline
gl = ax3[row][col].gridlines(linewidth=1, color='gray', alpha=0.5, linestyle='--')
# Place the subplots
fig3.subplots_adjust(bottom=0.0, top=0.9, left=0.05, right=0.95, wspace=0.1, hspace=0.5)
# Define and place a colorbar at the bottom
cbar_ax = fig3.add_axes([0.2, -0.1, 0.6, 0.02])
cbar = fig3.colorbar(im, cax=cbar_ax, orientation='horizontal', extend='both')
cbar.set_label('W.m$^{-2}$', fontsize=16)
# Define an overall title
fig3.suptitle('$\\bf{Seasonal\ climatology\ of\ TIS\ from\ CERES\ EBAF\ (January\ 2001\ -\ December\ 2021)}$',
fontsize=20)
# Save the figure
fig3.savefig(f'{DATADIR}ceres_solar_seasonal_climatology.png')
<>:48: SyntaxWarning: invalid escape sequence '\ '
<>:48: SyntaxWarning: invalid escape sequence '\ '
/var/folders/kx/ksg5wbrj1sq95rp2tz_72j6c0000gn/T/ipykernel_1471/1698630741.py:48: SyntaxWarning: invalid escape sequence '\ '
fig3.suptitle('$\\bf{Seasonal\ climatology\ of\ TIS\ from\ CERES\ EBAF\ (January\ 2001\ -\ December\ 2021)}$',
![../_images/8f2a28ca255f71abd48b1d6e9e245f6e11cbf29a05ec6d9f8af35d77fd368bd9.png](../_images/8f2a28ca255f71abd48b1d6e9e245f6e11cbf29a05ec6d9f8af35d77fd368bd9.png)
Figure 3 shows the seasonal mean climatology of the top of the atmosphere incoming solar radiation from spring to winter (top left to bottom right panels) derived from CERES EBAF.
Zonally averaged monthly mean climatology of TIS#
We will now calculate the monthly mean climatology of the top-of-atmosphere incoming solar radiation over the time period January 2001 - December 2021 by first applying the groupby()
method to group the data array by month and then calculating the average for each monthly group. The resulting data array is the monthly climatology for the top of atmosphere incident solar radiation based on reference January 2001 - December 2021.
solar_clim_month = solar.groupby('time.month').mean("time")
Let us view the zonal monthly climatology of the TOA incoming solar radiation. To do this, we will average across the longitude bands with the mean()
method, align the time dimension coordinate with the x axis and the lat dimension coordinate along the y axis using the method transpose()
…
solar_zonal_clim_month = solar_clim_month.mean(dim="lon").transpose()
solar_zonal_clim_month
<xarray.DataArray 'solar_mon' (lat: 180, month: 12)> Size: 9kB array([[495.919 , 305.7905 , 58.86096 , ..., 212.30956 , 440.9857 , 549.4477 ], [495.75546 , 305.69235 , 59.817547, ..., 212.24393 , 440.8527 , 549.279 ], [495.45468 , 305.51282 , 61.73928 , ..., 212.10985 , 440.58533 , 548.948 ], ..., [ 0. , 0. , 22.81825 , ..., 0. , 0. , 0. ], [ 0. , 0. , 20.87273 , ..., 0. , 0. , 0. ], [ 0. , 0. , 19.901903, ..., 0. , 0. , 0. ]], dtype=float32) Coordinates: * lat (lat) float32 720B -89.5 -88.5 -87.5 -86.5 ... 86.5 87.5 88.5 89.5 * month (month) int64 96B 1 2 3 4 5 6 7 8 9 10 11 12
Now we can plot our data. Before we do this however, we define the min, max and step of contours that we will use in a contour plot.
# Define contour parameters (min, max and step)
vdiv = 5
vmin = 0
vmax = 500
clevs = np.arange(vmin, vmax, vdiv)
# Define the figure and specify size
fig4, ax4 = plt.subplots(1, 1, figsize=(16, 8))
# Configure the axes and figure title
ax4.set_xlabel('Month')
ax4.set_ylabel('Latitude [ $^o$ ]')
ax4.set_title(
'$\\bf{Zonally\ averaged\ monthly\ climatology\ of\ TIS\ (January\ 2001\ -\ December\ 2021)}$',
fontsize=20,
pad=20)
# As the months (12) are much less than the latitudes (180),
# we need to ensure the plot fits into the size of the figure.
ax4.set_aspect('auto')
# Plot the data as a contour plot
contour = ax4.contourf(solar_zonal_clim_month.month,
solar_zonal_clim_month.lat,
solar_zonal_clim_month,
levels=clevs,
cmap='YlOrRd',
extend='both')
ax4.set_xticks(np.arange(1, 13, 1))
ax4.set_yticks(np.arange(-90, 91, 30))
# Specify the colorbar
cbar = plt.colorbar(contour, fraction=0.025, pad=0.05)
cbar.set_label('W.m$^{-2}$')
# Save the figure
fig4.savefig(f'{DATADIR}ceres_solar_monthly_climatology.png')
<>:8: SyntaxWarning: invalid escape sequence '\ '
<>:8: SyntaxWarning: invalid escape sequence '\ '
/var/folders/kx/ksg5wbrj1sq95rp2tz_72j6c0000gn/T/ipykernel_1471/1717961110.py:8: SyntaxWarning: invalid escape sequence '\ '
'$\\bf{Zonally\ averaged\ monthly\ climatology\ of\ TIS\ (January\ 2001\ -\ December\ 2021)}$',
![../_images/c586522dd0ab7b2d5d8ab94e09a3a948683434a82d2828ac5f6ac81686a8aa7e.png](../_images/c586522dd0ab7b2d5d8ab94e09a3a948683434a82d2828ac5f6ac81686a8aa7e.png)
Figure 4 is a Hovmöller diagram that describes the annual variability of the incoming solar radiation at the top of the atmosphere with latitude over the time period January 2001 to December 2021. It is used in meteorology and for climate studies to show how a variable varies with latitude over time.
3.2. Climatology of the top-of-atmosphere reflected solar flux#
Time averaged global climatological distribution of TOA RSF#
To calculate the mean climatology of the top of the atmosphere reflected solar flux for the time period January 2001 to December 2021, we have to select the specific time range using the Xarray method sel()
that indexes the data and dimensions by the appropriate indexers. We can then use the method mean()
to calculate the mean along the time dimension.
# Select the TOA RSF data for the whole time period
toa_sw = da_toa_sw.sel(time=slice('2001-01-01', '2021-12-31'))
# Calculate the mean along the time dimension
toa_sw_mean = toa_sw.mean(dim='time')
We can now make use of a combination of the plotting libraries Matplotlib and Cartopy to create a customised figure and visualise the time averaged global climatological distribution of the TOA reflected solar flux for the period January 2001 - December 2021.
# Create the figure panel and the map using the Cartopy PlateCarree projection
fig5, ax5 = plt.subplots(1, 1, figsize=(16, 8), subplot_kw={'projection': ccrs.PlateCarree()})
# Plot the data
im = plt.pcolormesh(toa_sw_mean.lon, toa_sw_mean.lat, toa_sw_mean, cmap='YlOrRd')
# Set the figure title, add lat/lon grid and coastlines
ax5.set_title(
'$\\bf{Climatology\ of\ TOA\ RSF\ from\ CERES\ EBAF\ (January\ 2001\ -\ December\ 2021)}$',
fontsize=20,
pad=20)
# Add coastlines
ax5.coastlines(color='black')
# Define gridlines and ticks
ax5.set_xticks(np.arange(-180, 181, 60), crs=ccrs.PlateCarree())
ax5.set_yticks(np.arange(-90, 91, 30), crs=ccrs.PlateCarree())
lon_formatter = LongitudeFormatter()
lat_formatter = LatitudeFormatter()
ax5.xaxis.set_major_formatter(lon_formatter)
ax5.yaxis.set_major_formatter(lat_formatter)
# Gridlines
gl = ax5.gridlines(linewidth=1, color='gray', alpha=0.5, linestyle='--')
# Specify the colorbar
cbar = plt.colorbar(im, fraction=0.025, pad=0.05, extend='both')
cbar.set_label('W.m$^{-2}$')
# Save the figure
fig5.savefig(f'{DATADIR}ceres_toa_sw_climatology.png')
<>:9: SyntaxWarning: invalid escape sequence '\ '
<>:9: SyntaxWarning: invalid escape sequence '\ '
/var/folders/kx/ksg5wbrj1sq95rp2tz_72j6c0000gn/T/ipykernel_1471/166919465.py:9: SyntaxWarning: invalid escape sequence '\ '
'$\\bf{Climatology\ of\ TOA\ RSF\ from\ CERES\ EBAF\ (January\ 2001\ -\ December\ 2021)}$',
![../_images/5291e5c03e1b210b6ec9be932fba2dbac8b67cd1d05c5326836b180a98013bf5.png](../_images/5291e5c03e1b210b6ec9be932fba2dbac8b67cd1d05c5326836b180a98013bf5.png)
Figure 5 shows the global climatology of the top of the atmospohere reflected solar radiation for the time period of January 2001 - December 2021.
Time averaged seasonal climatological distribution of TOA RSF#
We now split the TOA RSF data according to the seasons by using the groupby()
method, with 'time.season'
as an argument, and then average them over the years.
# Split data array toa_sw by season
toa_sw_seasonal_climatology = toa_sw.groupby('time.season').mean('time')
# Create a list of the seasons such as defined in the dataset toa_sw_seasonal_climatology:
seasons = ['MAM', 'JJA', 'SON', 'DJF']
# We use the "subplots" to place multiple plots according to our needs.
# In this case, we want 4 plots in a 2x2 format.
# For this "nrows" = 2 and "ncols" = 2, the projection and size are defined as well
fig6, ax6 = plt.subplots(nrows=2,
ncols=2,
subplot_kw={'projection': ccrs.PlateCarree()},
figsize=(16,8))
# Define a dictionary of subtitles, each one corresponding to a season
subtitles = {'MAM': 'NH spring', 'JJA': 'NH summer', 'SON': 'NH autumn', 'DJF': 'NH winter'}
# Configure the axes and subplot titles
for i_season, c_season in enumerate(seasons):
# convert i_season index into (row, col) index
row = i_season // 2
col = i_season % 2
# Plot data (coordinates and data) and define colormap
im = ax6[row][col].pcolormesh(toa_sw_seasonal_climatology.lon,
toa_sw_seasonal_climatology.lat,
toa_sw_seasonal_climatology.sel(season=c_season),
cmap='YlOrRd')
# Set title and size
ax6[row][col].set_title('Seasonal climatology of TOA RSF - ' + subtitles[c_season], fontsize=16)
# Add coastlines
ax6[row][col].coastlines()
# Define grid lines and ticks (e.g. from -180 to 180 in an interval of 60)
ax6[row][col].set_xticks(np.arange(-180, 181, 60), crs=ccrs.PlateCarree())
ax6[row][col].set_yticks(np.arange(-90, 91, 30), crs=ccrs.PlateCarree())
lon_formatter = LongitudeFormatter()
lat_formatter = LatitudeFormatter()
ax6[row][col].xaxis.set_major_formatter(lon_formatter)
ax6[row][col].yaxis.set_major_formatter(lat_formatter)
# Gridline
gl = ax6[row][col].gridlines(linewidth=1, color='gray', alpha=0.5, linestyle='--')
# Place the subplots
fig6.subplots_adjust(bottom=0.0, top=0.9, left=0.05, right=0.95, wspace=0.1, hspace=0.5)
# Define and place a colorbar at the bottom
cbar_ax = fig6.add_axes([0.2, -0.1, 0.6, 0.02])
cbar = fig6.colorbar(im, cax=cbar_ax, orientation='horizontal', extend='both')
cbar.set_label('W.m$^{-2}$', fontsize=16)
# Define an overall title
fig6.suptitle('$\\bf{Seasonal\ climatology\ of\ TOA\ RSF\ from\ CERES\ EBAF\ (January\ 2001\ -\ December\ 2021)}$',
fontsize=20)
# Save the figure
fig6.savefig(f'{DATADIR}erb_toa_sw_seasonal_climatology.png')
<>:48: SyntaxWarning: invalid escape sequence '\ '
<>:48: SyntaxWarning: invalid escape sequence '\ '
/var/folders/kx/ksg5wbrj1sq95rp2tz_72j6c0000gn/T/ipykernel_1471/1312456052.py:48: SyntaxWarning: invalid escape sequence '\ '
fig6.suptitle('$\\bf{Seasonal\ climatology\ of\ TOA\ RSF\ from\ CERES\ EBAF\ (January\ 2001\ -\ December\ 2021)}$',
![../_images/4877e85d9f1c8d7285b64b64ee2921aacbf3dc985d576e6c564bd9c0c132410f.png](../_images/4877e85d9f1c8d7285b64b64ee2921aacbf3dc985d576e6c564bd9c0c132410f.png)
Figure 6 shows the seasonal mean climatology of the top of the atmosphere reflected solar flux from spring to winter (top left to bottom right panels) derived from CERES EBAF.
The influence of the albedo surfaces on the outgoing shorwave radiation observed in Figure 6 is confirmed by the seasonal climatology, with much higher values of TOA RSF over snow and ice in the summer Arctic and Antarctic regions.
Zonally averaged montly mean climatology of TOA RSF#
We will now calculate the monthly mean climatology of the outgoing shortwave radiation over the time period January 2001 - December 2021 by first applying the groupby()
method to group the data array by month and then calculating the average for each monthly group. The resulting data array is the monthly climatology for the TOA reflected solar radiation based on reference January 2001 - December 2021.
toa_sw_clim_month = toa_sw.groupby('time.month').mean("time")
Before viewing the zonal monthly climatology of the TOA RSF, we must average across the longitude bands with the mean()
method, align the time dimension coordinate with the x axis and the lat dimension coordinate along the y axis using the method transpose()
…
toa_sw_zonal_clim_month = toa_sw_clim_month.mean(dim="lon").transpose()
Now we can plot our data. Before we do this however, we define the min, max and step of contours that we will use in a contour plot.
# Define contour parameters (min, max and step)
vdiv = 5
vmin = 0
vmax = 355
clevs = np.arange(vmin, vmax, vdiv)
# Define the figure and specify size
fig7, ax7 = plt.subplots(1, 1, figsize=(16, 8))
# Configure the axes and figure title
ax7.set_xlabel('Month')
ax7.set_ylabel('Latitude [ $^o$ ]')
ax7.set_title(
'$\\bf{Zonally\ averaged\ monthly\ climatology\ of\ TOA\ RSF\ (January\ 2001\ -\ December\ 2021)}$',
fontsize=20,
pad=20)
# As the months (12) are much less than the latitudes (180),
# we need to ensure the plot fits into the size of the figure.
ax7.set_aspect('auto')
# Plot the data as a contour plot
contour = ax7.contourf(toa_sw_zonal_clim_month.month,
toa_sw_zonal_clim_month.lat,
toa_sw_zonal_clim_month,
levels=clevs,
cmap='YlOrRd',
extend='both')
ax7.set_xticks(np.arange(1, 13, 1))
ax7.set_yticks(np.arange(-90, 91, 30))
# Specify the colorbar
cbar = plt.colorbar(contour, fraction=0.025, pad=0.05)
cbar.set_label('W.m$^{-2}$')
# Save the figure
fig7.savefig(f'{DATADIR}ceres_toa_sw_monthly_climatology.png')
<>:8: SyntaxWarning: invalid escape sequence '\ '
<>:8: SyntaxWarning: invalid escape sequence '\ '
/var/folders/kx/ksg5wbrj1sq95rp2tz_72j6c0000gn/T/ipykernel_1471/1762449367.py:8: SyntaxWarning: invalid escape sequence '\ '
'$\\bf{Zonally\ averaged\ monthly\ climatology\ of\ TOA\ RSF\ (January\ 2001\ -\ December\ 2021)}$',
![../_images/8fede93e930dbc6836d0bd555d0b2ab8ebc9a164838225b96a46d9727eb01fab.png](../_images/8fede93e930dbc6836d0bd555d0b2ab8ebc9a164838225b96a46d9727eb01fab.png)
Figure 7 shows the seasonal variation of the top the atmosphere shortwave flux.
3.3. Climatology of the top-of-atmosphere outgoing longwave radiation#
Time averaged global climatological distribution of TOA OLR#
To calculate the mean climatology of the TOA outgoing longwave radiation for the period January 2001 to December 2021, we select the specific time range using the Xarray method sel()
that indexes the data and dimensions by the appropriate indexers. We then use the method mean()
to calculate the mean along the time dimension.
# Select the OLR data for the whole time period
toa_lw = da_toa_lw.sel(time=slice('2001-01-01', '2021-12-31'))
# Calculate the mean along the time dimension
toa_lw_mean = toa_lw.mean(dim='time')
We now make use of a combination of the plotting libraries Matplotlib and Cartopy to visualise the time averaged global climatological distribution of the TOA longwave radiation for the period January 2001 - December 2021.
# Create the figure panel and the map using the Cartopy PlateCarree projection
fig8, ax8 = plt.subplots(1, 1, figsize=(16, 8), subplot_kw={'projection': ccrs.PlateCarree()})
# Plot the data
im = plt.pcolormesh(toa_lw_mean.lon, toa_lw_mean.lat, toa_lw_mean, cmap='YlOrRd')
# Set the figure title, add lat/lon grid and coastlines
ax8.set_title(
'$\\bf{Climatology\ of\ OLR\ from\ CERES\ EBAF\ (January\ 2001\ -\ December\ 2021)}$',
fontsize=20,
pad=20)
# Add coastlines
ax8.coastlines(color='black')
# Define gridlines and ticks
ax8.set_xticks(np.arange(-180, 181, 60), crs=ccrs.PlateCarree())
ax8.set_yticks(np.arange(-90, 91, 30), crs=ccrs.PlateCarree())
lon_formatter = LongitudeFormatter()
lat_formatter = LatitudeFormatter()
ax8.xaxis.set_major_formatter(lon_formatter)
ax8.yaxis.set_major_formatter(lat_formatter)
# Gridlines
gl = ax8.gridlines(linewidth=1, color='gray', alpha=0.5, linestyle='--')
# Specify the colorbar
cbar = plt.colorbar(im, fraction=0.025, pad=0.05, extend='both')
cbar.set_label('W.m$^{-2}$')
# Save the figure
fig8.savefig(f'{DATADIR}ceres_toa_lw_climatology.png')
<>:9: SyntaxWarning: invalid escape sequence '\ '
<>:9: SyntaxWarning: invalid escape sequence '\ '
/var/folders/kx/ksg5wbrj1sq95rp2tz_72j6c0000gn/T/ipykernel_1471/3332177821.py:9: SyntaxWarning: invalid escape sequence '\ '
'$\\bf{Climatology\ of\ OLR\ from\ CERES\ EBAF\ (January\ 2001\ -\ December\ 2021)}$',
![../_images/ffaf3a5c4d4d92e97920808dfd8de7a8dc47dc04fd7fc5d222f4719394d4cc52.png](../_images/ffaf3a5c4d4d92e97920808dfd8de7a8dc47dc04fd7fc5d222f4719394d4cc52.png)
Figure 8 shows the global climatology of the top of the atmosphere outgoing longwave radiation for the time period of January 2001 - December 2021.
Time averaged seasonal climatological distribution of TOA OLR#
We now split the TOA outgoing longwave radiation data according to the seasons by using the groupby()
method, with 'time.season'
as an argument; the new data are then averaged over the years.
# Split data array toa_lw by season
toa_lw_seasonal_climatology = toa_lw.groupby('time.season').mean('time')
# Create a list of the seasons such as defined in the dataset toa_lw_seasonal_climatology:
seasons = ['MAM', 'JJA', 'SON', 'DJF']
# We use the "subplots" to place multiple plots according to our needs.
# In this case, we want 4 plots in a 2x2 format.
# For this "nrows" = 2 and "ncols" = 2, the projection and size are defined as well
fig9, ax9 = plt.subplots(nrows=2,
ncols=2,
subplot_kw={'projection': ccrs.PlateCarree()},
figsize=(16,8))
# Define a dictionary of subtitles, each one corresponding to a season
subtitles = {'MAM': 'NH spring', 'JJA': 'NH summer', 'SON': 'NH autumn', 'DJF': 'NH winter'}
# Configure the axes and subplot titles
for i_season, c_season in enumerate(seasons):
# convert i_season index into (row, col) index
row = i_season // 2
col = i_season % 2
# Plot data (coordinates and data) and define colormap
im = ax9[row][col].pcolormesh(toa_lw_seasonal_climatology.lon,
toa_lw_seasonal_climatology.lat,
toa_lw_seasonal_climatology.sel(season=c_season),
cmap='YlOrRd')
# Set title and size
ax9[row][col].set_title('Seasonal climatology of TOA LWF - ' + subtitles[c_season], fontsize=16)
# Add coastlines
ax9[row][col].coastlines()
# Define grid lines and ticks (e.g. from -180 to 180 in an interval of 60)
ax9[row][col].set_xticks(np.arange(-180, 181, 60), crs=ccrs.PlateCarree())
ax9[row][col].set_yticks(np.arange(-90, 91, 30), crs=ccrs.PlateCarree())
lon_formatter = LongitudeFormatter()
lat_formatter = LatitudeFormatter()
ax9[row][col].xaxis.set_major_formatter(lon_formatter)
ax9[row][col].yaxis.set_major_formatter(lat_formatter)
# Gridline
gl = ax9[row][col].gridlines(linewidth=1, color='gray', alpha=0.5, linestyle='--')
# Place the subplots
fig9.subplots_adjust(bottom=0.0, top=0.9, left=0.05, right=0.95, wspace=0.1, hspace=0.5)
# Define and place a colorbar at the bottom
cbar_ax = fig9.add_axes([0.2, -0.1, 0.6, 0.02])
cbar = fig9.colorbar(im, cax=cbar_ax, orientation='horizontal', extend='both')
cbar.set_label('W.m$^{-2}$', fontsize=16)
# Define an overall title
fig9.suptitle('$\\bf{Seasonal\ climatology\ of\ OLR\ from\ CERES\ EBAF\ (January\ 2001\ -\ December\ 2021)}$',
fontsize=20)
# Save the figure
fig9.savefig(f'{DATADIR}ceres_toa_lw_seasonal_climatology.png')
<>:48: SyntaxWarning: invalid escape sequence '\ '
<>:48: SyntaxWarning: invalid escape sequence '\ '
/var/folders/kx/ksg5wbrj1sq95rp2tz_72j6c0000gn/T/ipykernel_1471/1830599661.py:48: SyntaxWarning: invalid escape sequence '\ '
fig9.suptitle('$\\bf{Seasonal\ climatology\ of\ OLR\ from\ CERES\ EBAF\ (January\ 2001\ -\ December\ 2021)}$',
![../_images/7f305dec28f3194b4cac97284c055a0126ca2ff056890b15992887a29a4f73c5.png](../_images/7f305dec28f3194b4cac97284c055a0126ca2ff056890b15992887a29a4f73c5.png)
Figure 9 shows the seasonal mean climatology of the outgoing longwave flux from spring to winter (top left to bottom right panels) derived from CERES EBAF.
The general pattern is overall the same as in Figure 8 with higher values of outgoing longwave flux in the tropics and lower values in the high latitude, especially during winter. The relatively low values of OLR corresponding to high convection areas are also very well observed in the tropical summer.
Inter-annual variability of TOA OLR#
We can now analyse the inter-annual variability of the outgoing longwave radiation at the top of the atmopshere. It is defined as the root mean square of the inter-annual anomaly in the TOA OLR.
First, we apply the groupby()
method to group the data array by year and calculate the time-averaged TOA OLR for each year.
# Calculate the global time-averaged distribution of TOA OLR for each year
toa_lw_clim_year = toa_lw.groupby('time.year').mean("time")
Then, we compute the inter-annual anomaly by subtracting the global mean climatology from the the group of 21 years.
# Calculate the inter-annual anomaly of TOA OLR
toa_lw_anomaly = toa_lw_clim_year - toa_lw_mean
Finally, the inter-annual variability of TOA OLR is obtained by computing the standard deviation of the anomaly.
# Calculate the inter-annual variability of TOA OLR
toa_lw_interannual_variability = toa_lw_anomaly.std("year")
# Create the figure panel and the map using the Cartopy PlateCarree projection
fig10, ax10 = plt.subplots(1, 1, figsize=(16, 8), subplot_kw={'projection': ccrs.PlateCarree()})
# Plot the data
im = plt.pcolormesh(toa_lw_interannual_variability.lon,
toa_lw_interannual_variability.lat,
toa_lw_interannual_variability,
cmap='YlOrRd')
# Set the figure title, add lat/lon grid and coastlines
ax10.set_title(
'$\\bf{Interannual\ variability\ of\ TOA\ OLR\ from\ CERES\ EBAF\ (January\ 2001\ -\ December\ 2021)}$',
fontsize=20,
pad=20)
# Add coastlines
ax10.coastlines(color='black')
# Define gridlines and ticks
ax10.set_xticks(np.arange(-180, 181, 60), crs=ccrs.PlateCarree())
ax10.set_yticks(np.arange(-90, 91, 30), crs=ccrs.PlateCarree())
lon_formatter = LongitudeFormatter()
lat_formatter = LatitudeFormatter()
ax10.xaxis.set_major_formatter(lon_formatter)
ax10.yaxis.set_major_formatter(lat_formatter)
# Gridlines
gl = ax10.gridlines(linewidth=1, color='gray', alpha=0.5, linestyle='--')
# Specify the colorbar
cbar = plt.colorbar(im, fraction=0.025, pad=0.05, extend='both')
cbar.set_label('W.m$^{-2}$')
# Save the figure
fig10.savefig(f'{DATADIR}ceres_toa_lw_interannual_variability.png')
<>:12: SyntaxWarning: invalid escape sequence '\ '
<>:12: SyntaxWarning: invalid escape sequence '\ '
/var/folders/kx/ksg5wbrj1sq95rp2tz_72j6c0000gn/T/ipykernel_1471/1475764494.py:12: SyntaxWarning: invalid escape sequence '\ '
'$\\bf{Interannual\ variability\ of\ TOA\ OLR\ from\ CERES\ EBAF\ (January\ 2001\ -\ December\ 2021)}$',
![../_images/30cd5ec6790983ccb0aa700f1775f5d0745ac0d5a0cf9a96466ca6e670d30654.png](../_images/30cd5ec6790983ccb0aa700f1775f5d0745ac0d5a0cf9a96466ca6e670d30654.png)
Figure 10 shows that the main variability occurs in the convective regions, in particular, in the Monsoon regions and along the Pacific Basin, following “El Niño” and “La Niña” oscillations.
Zonally averaged montly mean climatology of TOA OLR#
We will now calculate the monthly mean climatology of the outgoing longwave radiation over the time period January 2001 - December 2021 by first applying the groupby()
method to group the data array by month and then calculating the average for each monthly group. The resulting data array is the monthly climatology for the total column water vapour based on reference January 2001 - December 2021.
toa_lw_clim_month = toa_lw.groupby('time.month').mean("time")
Before viewing the zonal monthly climatology of the OLR, let us average across the longitude bands with the mean()
method, align the time dimension coordinate with the x axis and the lat dimension coordinate along the y axis by using the method transpose()
…
toa_lw_zonal_clim_month = toa_lw_clim_month.mean(dim="lon").transpose()
Now we can plot our data. Before we do this however, we define the min, max and step of contours that we will use in a contour plot.
# Define contour parameters (min, max and step)
vdiv = 1
vmin = 100
vmax = 300
clevs = np.arange(vmin, vmax, vdiv)
# Define the figure and specify size
fig11, ax11 = plt.subplots(1, 1, figsize=(16, 8))
# Configure the axes and figure title
ax11.set_xlabel('Month')
ax11.set_ylabel('Latitude [ $^o$ ]')
ax11.set_title(
'$\\bf{Zonally\ averaged\ monthly\ climatology\ of\ OLR\ (January\ 2001\ -\ December\ 2021)}$',
fontsize=20,
pad=20)
# As the months (12) are much less than the latitudes (180),
# we need to ensure the plot fits into the size of the figure.
ax11.set_aspect('auto')
# Plot the data as a contour plot
contour = ax11.contourf(toa_lw_zonal_clim_month.month,
toa_lw_zonal_clim_month.lat,
toa_lw_zonal_clim_month,
levels=clevs,
cmap='YlOrRd',
extend='both')
ax11.set_xticks(np.arange(1, 13, 1))
ax11.set_yticks(np.arange(-90, 91, 30))
# Specify the colorbar
cbar = plt.colorbar(contour, fraction=0.025, pad=0.05)
cbar.set_label('W.m$^{-2}$]')
# Save the figure
fig11.savefig(f'{DATADIR}ceres_toa_lw_monthly_climatology.png')
![../_images/d19f3008a362523155324db70bb5f395362530dc2802a8e631711187d746426b.png](../_images/d19f3008a362523155324db70bb5f395362530dc2802a8e631711187d746426b.png)
Figure 11 shows the seasonal variation of the top of the atmosphere longwave flux.
3.4. Climatology of the top-of-atmosphere net Earth’s radiation budget#
Time averaged global climatological distribution of TOA net ERB#
# Calculate the mean climatology of the net radiation at the top of the atmosphere
toa_erb_mean = solar_mean - toa_sw_mean - toa_lw_mean
We now make use of a combination of the plotting libraries Matplotlib and Cartopy to visualize the time averaged global climatological distribution of the TOA net radiative flux for the period January 2001 - December 2021.
# Create the figure panel and the map using the Cartopy PlateCarree projection
fig12, ax12 = plt.subplots(1, 1, figsize=(16, 8), subplot_kw={'projection': ccrs.PlateCarree()})
# Plot the data
im = plt.pcolormesh(toa_erb_mean.lon, toa_erb_mean.lat, toa_erb_mean, cmap='RdBu_r')
# Set the figure title, add lat/lon grid and coastlines
ax12.set_title(
'$\\bf{Climatology\ of\ TOA\ net\ ERB\ from\ CERES\ EBAF\ (January\ 2001\ -\ December\ 2021)}$',
fontsize=20,
pad=20)
# Add coastlines
ax12.coastlines(color='black')
# Define gridlines and ticks
ax12.set_xticks(np.arange(-180, 181, 60), crs=ccrs.PlateCarree())
ax12.set_yticks(np.arange(-90, 91, 30), crs=ccrs.PlateCarree())
lon_formatter = LongitudeFormatter()
lat_formatter = LatitudeFormatter()
ax12.xaxis.set_major_formatter(lon_formatter)
ax12.yaxis.set_major_formatter(lat_formatter)
# Gridlines
gl = ax12.gridlines(linewidth=1, color='gray', alpha=0.5, linestyle='--')
# Specify the colorbar
cbar = plt.colorbar(im, fraction=0.025, pad=0.05, extend='both')
cbar.set_label('W.m$^{-2}$')
# Save the figure
fig12.savefig(f'{DATADIR}ceres_toa_erb_climatology.png')
![../_images/ccfaa6cacbe66e0e87a76a74dec1a10993fabf75dd62129b0c201f520de6cc96.png](../_images/ccfaa6cacbe66e0e87a76a74dec1a10993fabf75dd62129b0c201f520de6cc96.png)
Figure 12 shows the global climatology of the (net) Earth’s radiation budget at the top of the atmosphere for the time period of January 2001 - December 2021.
Time averaged seasonal climatological distribution of TOA ERB#
To compute the time averaged seasonal climatological distribution of TOA net ERB, the data are splitted according to the seasons by using the groupby()
method, with 'time.season'
as an argument, and then averaged over the years.
# Select the TOA ERB data for the whole time period
toa_erb = da_toa_erb.sel(time=slice('2001-01-01', '2021-12-31'))
# Split data array TOA ERB by season
toa_erb_seasonal_climatology = toa_erb.groupby('time.season').mean('time')
# Create a list of the seasons such as defined in the dataset toa_erb_seasonal_climatology:
seasons = ['MAM', 'JJA', 'SON', 'DJF']
# We use the "subplots" to place multiple plots according to our needs.
# In this case, we want 4 plots in a 2x2 format.
# For this "nrows" = 2 and "ncols" = 2, the projection and size are defined as well
fig13, ax13 = plt.subplots(nrows=2,
ncols=2,
subplot_kw={'projection': ccrs.PlateCarree()},
figsize=(16,8))
# Define a dictionary of subtitles, each one corresponding to a season
subtitles = {'MAM': 'NH spring', 'JJA': 'NH summer', 'SON': 'NH autumn', 'DJF': 'NH winter'}
# Configure the axes and subplot titles
for i_season, c_season in enumerate(seasons):
# convert i_season index into (row, col) index
row = i_season // 2
col = i_season % 2
# Plot data (coordinates and data) and define colormap
im = ax13[row][col].pcolormesh(toa_erb_seasonal_climatology.lon,
toa_erb_seasonal_climatology.lat,
toa_erb_seasonal_climatology.sel(season=c_season),
cmap='RdBu_r')
# Set title and size
ax13[row][col].set_title('Seasonal climatology of TOA ERB - ' + subtitles[c_season], fontsize=16)
# Add coastlines
ax13[row][col].coastlines()
# Define grid lines and ticks (e.g. from -180 to 180 in an interval of 60)
ax13[row][col].set_xticks(np.arange(-180, 181, 60), crs=ccrs.PlateCarree())
ax13[row][col].set_yticks(np.arange(-90, 91, 30), crs=ccrs.PlateCarree())
lon_formatter = LongitudeFormatter()
lat_formatter = LatitudeFormatter()
ax13[row][col].xaxis.set_major_formatter(lon_formatter)
ax13[row][col].yaxis.set_major_formatter(lat_formatter)
# Gridline
gl = ax13[row][col].gridlines(linewidth=1, color='gray', alpha=0.5, linestyle='--')
# Place the subplots
fig13.subplots_adjust(bottom=0.0, top=0.9, left=0.05, right=0.95, wspace=0.1, hspace=0.5)
# Define and place a colorbar at the bottom
cbar_ax = fig13.add_axes([0.2, -0.1, 0.6, 0.02])
cbar = fig13.colorbar(im, cax=cbar_ax, orientation='horizontal', extend='both')
cbar.set_label('W.m$^{-2}$', fontsize=16)
# Define an overall title
fig13.suptitle('$\\bf{Seasonal\ climatology\ of\ TOA\ net\ ERB\ from\ CERES\ EBAF\ (January\ 2001\ -\ December\ 2021)}$',
fontsize=20)
# Save the figure
fig13.savefig(f'{DATADIR}ceres_toa_erb_seasonal_climatology.png')
![../_images/0aefb18206e2a17799cb10f703f24e0fc18957639e91675e5ff6e8b9fe7a76ef.png](../_images/0aefb18206e2a17799cb10f703f24e0fc18957639e91675e5ff6e8b9fe7a76ef.png)
Figure 13 shows the seasonal mean climatology of the TOA net radiation from spring to winter (top left to bottom right panels) derived from CERES EBAF.
Zonally averaged montly mean climatology of TOA net ERB#
We will now calculate the monthly mean climatology of TOA net radiative flux over the period January 2001 - December 2021 by first applying the groupby()
method to group the data array by month and then calculating the average for each monthly group. The resulting data array is the monthly climatology for the TOA ERB based on reference January 2001 - December 2021.
toa_erb_clim_month = toa_erb.groupby('time.month').mean("time")
Before viewing the zonal monthly climatology of the TOA ERB, we average across the longitude bands with the mean()
method and align the time dimension coordinate with the x axis and the lat dimension coordinate along the y axis using the method transpose()
…
toa_erb_zonal_clim_month = toa_erb_clim_month.mean(dim="lon").transpose()
Now we can plot our data. Before we do this however, we define the min, max and step of contours that we will use in a contour plot.
# Define contour parameters (min, max and step)
vdiv = 1
vmin = -200
vmax = 200
clevs = np.arange(vmin, vmax, vdiv)
# Define the figure and specify size
fig14, ax14 = plt.subplots(1, 1, figsize=(16, 8))
# Configure the axes and figure title
ax14.set_xlabel('Month')
ax14.set_ylabel('Latitude [ $^o$ ]')
ax14.set_title(
'$\\bf{Zonally\ averaged\ monthly\ climatology\ of\ TOA\ net\ ERB\ (January\ 2001\ -\ December\ 2021)}$',
fontsize=20,
pad=20)
# As the months (12) are much less than the latitudes (180),
# we need to ensure the plot fits into the size of the figure.
ax14.set_aspect('auto')
# Plot the data as a contour plot
contour = ax14.contourf(toa_erb_zonal_clim_month.month,
toa_erb_zonal_clim_month.lat,
toa_erb_zonal_clim_month,
levels=clevs,
cmap='RdBu_r',
extend='both')
ax14.set_xticks(np.arange(1, 13, 1))
ax14.set_yticks(np.arange(-90, 91, 30))
# Specify the colorbar
cbar = plt.colorbar(contour, fraction=0.025, pad=0.05)
cbar.set_label('W.m$^{-2}$')
# Save the figure
fig14.savefig(f'{DATADIR}ceres_toa_erb_monthly_climatology.png')
![../_images/5cc362d8183f550be36446bfad4825404e94922dc562cc68a9ff849c62b290cd.png](../_images/5cc362d8183f550be36446bfad4825404e94922dc562cc68a9ff849c62b290cd.png)
Figure 14 shows the seasonal variation of the top of atmosphere net Earth’s radiation budget.
The general pattern of the Hovmöller diagram is overall the same as the seasonal climatological distribution (Figure 13), exhibiting the oscillation of the band of positive values of TOA net ERB around the equator that reaches a maximum in the summer tropics. This confirms the dominant effect of the solar radiation and the equator-versus-pole energy imbalance which is the main driver of atmospheric and oceanic circulations.
4. Time series and trend analysis of the Earth’s Radiation Budget#
We first create a temporal subset for the time period January 2001 to December 2021.
# Select time period for the incoming shortwave radiation data array
ceres_solar = da_solar.sel(time=slice('2001-01-01', '2021-12-31'))
# Select time period for the outgoing shortwave radiation data array
ceres_toa_sw = da_toa_sw.sel(time=slice('2001-01-01', '2021-12-31'))
# Select time period for the outgoing longwave radiation data array
ceres_toa_lw = da_toa_lw.sel(time=slice('2001-01-01', '2021-12-31'))
# Compute the net radiative flux for the selected time period
ceres_toa_erb = ceres_solar - ceres_toa_sw - ceres_toa_lw
Spatial aggregation#
# Define weights as the cosine of latitudes
weights = np.cos(np.deg2rad(ceres_solar.lat))
weights.name = "weights"
# Spatial aggregation of CERES-EBAF incident solar flux
ceres_solar_weighted = ceres_solar.weighted(weights)
# Spatial aggregation of CERES-EBAF reflected solar flux
ceres_toa_sw_weighted = ceres_toa_sw.weighted(weights)
# Spatial aggregation of CERES-EBAF Earth emitted longwave flux
ceres_toa_lw_weighted = ceres_toa_lw.weighted(weights)
# Spatial agregation of CERES-EBAF net radiative flux
ceres_toa_erb_weighted = ceres_toa_erb.weighted(weights)
The next step is to compute the mean across the latitude and longitude dimensions of the weighted data array with the mean()
method.
# Weighted mean of CERES-EBAF incident solar flux
ceres_solar_wmean = ceres_solar_weighted.mean(dim=("lat", "lon"))
# Weighted mean of CERES-EBAF reflected solar flux
ceres_toa_sw_wmean = ceres_toa_sw_weighted.mean(dim=("lat", "lon"))
# Weighted mean of CERES-EBAF Earth emitted longwave flux
ceres_toa_lw_wmean = ceres_toa_lw_weighted.mean(dim=("lat", "lon"))
# Weighted mean of CERES net radiative flux
ceres_toa_erb_wmean = ceres_toa_erb_weighted.mean(dim=("lat", "lon"))
4.1. Time series and trend analysis of the top-of-atmosphere incoming solar radiation#
Global time series of TIS#
Now we can plot the time series of globally averaged TIS data over time using the plot()
method.
# Define the figure and specify size
fig15, ax15 = plt.subplots(1, 1, figsize=(16, 8))
# Configure the axes and figure title
ax15.set_xlabel('Year')
ax15.set_ylabel('W.m$^{-2}$')
ax15.grid(linewidth=1, color='gray', alpha=0.5, linestyle='--')
ax15.set_title('$\\bf{Global\ time\ series\ of\ TIS\ from\ CERES\ EBAF}$', fontsize=20, pad=20)
# Plot the data
ax15.plot(ceres_solar_wmean.time, ceres_solar_wmean)
# Save the figure
fig15.savefig(f'{DATADIR}ceres_isf_global_timeseries.png')
![../_images/9b40826b336f4b167c7f522f2e905dd450f7d7da96998463afa4aec099e6d9ed.png](../_images/9b40826b336f4b167c7f522f2e905dd450f7d7da96998463afa4aec099e6d9ed.png)
Figure 15 shows the time series of the incoming solar flux at the top of the atmosphere.
Trend analysis and seasonal cycle of TIS#
The time series can be further analysed by extracting the trend, or the running annual mean, and the seasonal cycle.
To this end, we will convert the Xarray Data Array into a time series with the pandas library before decomposing it into the trend, the seasonal cycle and the residuals by using the seasonal_decompose()
method and visualizing the results.
# Convert the Xarray data array ceres_solar_wmean into a time series
ceres_solar_wmean_series = pd.Series(ceres_solar_wmean)
# Define the time dimension of ceres_solar_weighted_mean as the index of the time series
ceres_solar_wmean_series.index = ceres_solar_wmean.time.to_dataframe().index
# Decomposition of the time series into the trend, the seasonal cycle and the residuals
ceres_solar_seasonal_decomposition = seasonal_decompose(ceres_solar_wmean_series,
model='additive',
period=12)
# Plot the resulting seasonal decomposition
ceres_solar_seasonal_decomposition.plot()
plt.xlabel("Time")
plt.savefig(f'{DATADIR}ceres_solar_timeseries_climatology.png')
![../_images/5b5cfb7dcbe19cb0d79fc8c9cb5b8a87cb5ac5777b4f7d3108bede7ca512d68f.png](../_images/5b5cfb7dcbe19cb0d79fc8c9cb5b8a87cb5ac5777b4f7d3108bede7ca512d68f.png)
Figure 16 shows the decomposition of the TIS time series into the trend corresponding to the sunspot cycle (2nd panel), and the seasonal cycle with the confirmed amplitude of 20 W/m\(^{2}\) (3rd panel), from which are derived the anomalies or de-trended and de-seasonalised time series that have the characteristics of an uncorrelated noise pattern (4th panel).
4.2. Time series and trend analysis of the top-of-atmosphere reflected solar flux#
Global time series of TOA RSF#
We will now plot the time series of globally averaged TOA RSF data over time using the plot()
method.
# Define the figure and specify size
fig17, ax17 = plt.subplots(1, 1, figsize=(16, 8))
# Configure the axes and figure title
ax17.set_xlabel('Year')
ax17.set_ylabel('W.m$^{-2}$')
ax17.grid(linewidth=1, color='gray', alpha=0.5, linestyle='--')
ax17.set_title('$\\bf{Global\ time\ series\ of\ TOA\ RSF\ from\ CERES\ EBAF}$', fontsize=20, pad=20)
# Plot the data
ax17.plot(ceres_toa_sw_wmean.time, ceres_toa_sw_wmean)
# Save the figure
fig17.savefig(f'{DATADIR}ceres_toa_sw_global_timeseries.png')
![../_images/033563727c171f5c251c1fc9537214e8cb69d38782b54fec9014e494f4add73a.png](../_images/033563727c171f5c251c1fc9537214e8cb69d38782b54fec9014e494f4add73a.png)
Figure 17 shows the time series of the reflected solar flux at the top of the atmosphere.
From the time series of the global space and time averaged RSF, we can infer a clear seasonal cycle associated to the Earth’s orbit around the sun and a long-term seasonal cycle corresponding to the sunspot cycle together with a negative trend. They will now be further examined through the seasonal decomposition of the incoming solar flux.
Trend analysis and seasonal cycle of TOA RSF#
The time series can be further analysed by extracting the trend, or the running annual mean, and the seasonal cycle.
To this end, we will convert the Xarray Data Array into a time series with the pandas library before decomposing it into the trend, the seasonal cycle and the residuals by using the seasonal_decompose()
method and visualizing the results.
# Convert the Xarray data array ceres_toa_sw_wmean into a time series
ceres_toa_sw_wmean_series = pd.Series(ceres_toa_sw_wmean)
# Define the time dimension of ceres_toa_sw_wmean as the index of the time series
ceres_toa_sw_wmean_series.index = ceres_toa_sw_wmean.time.to_dataframe().index
# Decomposition of the time series into the trend, the seasonal cycle and the residuals
ceres_toa_sw_seasonal_decomposition = seasonal_decompose(ceres_toa_sw_wmean_series,
model='additive',
period=12)
# Plot the resulting seasonal decomposition
ceres_toa_sw_seasonal_decomposition.plot()
plt.xlabel("Time")
plt.savefig(f'{DATADIR}ceres_toa_sw_timeseries_climatology.png')
![../_images/82a73a0c36d030d2be08a0c0b03c602651526f580e38d54ba73ed22ee363a846.png](../_images/82a73a0c36d030d2be08a0c0b03c602651526f580e38d54ba73ed22ee363a846.png)
Figure 18 shows the decomposition of the TOA RSF time series into a negative trend of ~-0.1 W/m\(^{2}\)/year (2nd panel) and a seasonal cycle with an amplitude of ~15 W/m\(^{2}\) (3rd panel), from which are derived the anomalies that have the characteristics of an uncorrelated noise pattern (4th panel).
4.3. Time series and trend analysis of the top-of-atmosphere outgoing longwave radiation#
Global time series of TOA OLR#
We will now plot the time series of globally averaged outgoing longwave radiation data over time using the plot()
method.
# Define the figure and specify size
fig19, ax19 = plt.subplots(1, 1, figsize=(16, 8))
# Configure the axes and figure title
ax19.set_xlabel('Year')
ax19.set_ylabel('W.m$^{-2}$')
ax19.grid(linewidth=1, color='gray', alpha=0.5, linestyle='--')
ax19.set_title('$\\bf{Global\ time\ series\ of\ OLR\ from\ CERES\ EBAF}$', fontsize=20, pad=20)
# Plot the data
ax19.plot(ceres_toa_lw_wmean.time, ceres_toa_lw_wmean)
# Save the figure
fig19.savefig(f'{DATADIR}ceres_toa_lw_global_timeseries.png')
![../_images/467af4f709f8d5b953965f3a01889747f45ae9016e6be06e6150b9da8d9c8ad1.png](../_images/467af4f709f8d5b953965f3a01889747f45ae9016e6be06e6150b9da8d9c8ad1.png)
Figure 19 shows the time series of the outgoing longwave radiation at the top of the atmosphere. From the time series, we can infer a seasonal pattern along with a trend in the global space and time averaged TOA OLR.
We will now procede to the seasonal decomposition of the Earth-emitted longwave radiation at the top of the atmosphere and analyse more specifically the trend and seasonal cycle of CERES-EBAF OLR.
Trend analysis and seasonal cycle of TOA OLR#
The time series can be further analysed by extracting the trend, or the running annual mean, and the seasonal cycle.
To this end, we will convert the Xarray Data Array into a time series with the pandas library before decomposing it into the trend, the seasonal cycle and the residuals by using the seasonal_decompose()
method and visualizing the results.
# Convert the Xarray data array ceres_toa_lw_wmean into a time series
ceres_toa_lw_wmean_series = pd.Series(ceres_toa_lw_wmean)
# Define the time dimension of ceres_toa_lw_wmean as the index of the time series
ceres_toa_lw_wmean_series.index = ceres_toa_lw_wmean.time.to_dataframe().index
# Decomposition of the time series into the trend, the seasonal cycle and the residuals
ceres_toa_lw_seasonal_decomposition = seasonal_decompose(ceres_toa_lw_wmean_series,
model='additive',
period=12)
# Plot the resulting seasonal decomposition
ceres_toa_lw_seasonal_decomposition.plot()
plt.xlabel("Time")
plt.savefig(f'{DATADIR}ceres_toa_lw_timeseries_climatology.png')
![../_images/b91aa4d856b1f7a95b61fe9c7e29b43eff1dd134b0b42cdba14e63a80fbc5b1e.png](../_images/b91aa4d856b1f7a95b61fe9c7e29b43eff1dd134b0b42cdba14e63a80fbc5b1e.png)
Figure 20 shows the decomposition of the outgoing lonwave radiation time series into a positive trend of ~0.05 W/m\(^{2}\)/year (2nd panel), and a seasonal cycle with an amplitude of ~8 W/m\(^{2}\) (3rd panel), from which are derived the anomalies that have the characteristics of uncorrelated noise pattern (4th panel).
The observed inter-annual variations in the 2nd panel of Figure 20 suggest an influence of the “El Niño” or “La Niña” Southern Oscillation on the TOA OLR. The increase in this Earth-emitted longwave radiation is clearly related to the increase of surface temperatures.
4.4. Time series and trend analysis of the top-of-atmosphere net Earth’s radiation budget#
Global time series of TOA net ERB#
We will now plot the time series of the globally averaged TOA net Earth’s radiative flux over time using the plot()
method.
# Define the figure and specify size
fig21, ax21 = plt.subplots(1, 1, figsize=(16, 8))
# Configure the axes and figure title
ax21.set_xlabel('Year')
ax21.set_ylabel('W.m$^{-2}$')
ax21.grid(linewidth=1, color='gray', alpha=0.5, linestyle='--')
ax21.set_title('$\\bf{Global\ time\ series\ of\ TOA\ net\ ERB\ from\ CERES\ EBAF}$', fontsize=20, pad=20)
# Plot the data
ax21.plot(ceres_toa_erb_wmean.time, ceres_toa_erb_wmean)
# Save the figure
fig21.savefig(f'{DATADIR}ceres_toa_erb_global_timeseries.png')
![../_images/50f2cd29c968846396f5c22aafd6d3a1d5bc5e461c193f6cca27ef90a45e98b0.png](../_images/50f2cd29c968846396f5c22aafd6d3a1d5bc5e461c193f6cca27ef90a45e98b0.png)
Figure 21 shows the time series of the net Earth’s radiation budget at the top of the atmosphere. From the time series, we can infer a seasonal pattern together with a slightly increasing trend in the global space and time averaged TOA net ERB.
Trend analysis and seasonal cycle of TOA net ERB#
The time series can be further analysed by extracting the trend, or the running annual mean, and the seasonal cycle.
# Convert the Xarray data array ceres_toa_erb_wmean into a time series
ceres_toa_erb_wmean_series = pd.Series(ceres_toa_erb_wmean)
# Define the time dimension of ceres_toa_sw_wmean as the index of the time series
ceres_toa_erb_wmean_series.index = ceres_toa_erb_wmean.time.to_dataframe().index
# Decomposition of the time series into the trend, the seasonal cycle and the residuals
ceres_toa_erb_seasonal_decomposition = seasonal_decompose(ceres_toa_erb_wmean_series,
model='additive',
period=12)
# Plot the resulting seasonal decomposition
ceres_toa_erb_seasonal_decomposition.plot()
plt.xlabel("Time")
plt.savefig(f'{DATADIR}ceres_toa_erb_timeseries_climatology.png')
![../_images/c91d57f251d5f381de1ca3a348deaafde164f379b3dd627b98fe77a2976f00b0.png](../_images/c91d57f251d5f381de1ca3a348deaafde164f379b3dd627b98fe77a2976f00b0.png)
Figure 22 shows the decomposition of the CERES-EBAF TOA net ERB time series into a positive trend of ~0.05 W/m\(^{2}\)/year (2nd panel), and a seasonal cycle with an amplitude of ~20 W/m\(^{2}\) (3rd panel), from which are derived the anomalies that have the characteristics of uncorrelated noise pattern (4th panel).