The following modules are required to run the scripts:
numpy
xarray
yaml
matplotlib
cartopy
cftime
To run unit tests:
python -m unittestThis script allows to plot a map of differences: between two experiments, or between two periods of the same run, or between different seasons, or between different variables. The input data and the parameters of the plot are specified in the input YAML file, or on the command line. The YAML configuration file is the primary and most flexible way to specify plot parameters, but many options can be set on the command line, if that is more convenient.
usage: plot-diff-map.py [-h] [-v] [-s FILENAME] [--dpi DPI] [--variable VARIABLE]
[--levels LEVELS] [--colormap COLORMAP]
[--projection PROJECTION]
[--scale SCALE] [--units UNITS]
[--years YEARS] [--season SEASON]
[--measureFile MEASUREFILE] [--areaVariable AREAVARIABLE]
FILENAME.yaml
plot a map of differences between two experiments
positional arguments:
FILENAME.yaml plot configuration file, in yaml format. If the file name
is "-" then the script reads from standard input.
options:
-h, --help show this help message and exit
-v, --verbose increase verbosity
-s FILENAME, --save FILENAME
save plot to file (pdf, png, ...) instead of plotting it on
screen.
--dpi DPI resolution for saved raster figures
extras:
arguments that can be used to override options in the YAML cinfig file
--variable VARIABLE variable to plot
--levels LEVELS levels for plotting
--projection PROJECTION
geographic projection of the map
--colormap COLORMAP color map for the plot
--scale SCALE scaling factor for plotted variable
--units UNITS units of plotted variable
--years YEARS years to include in the averaging
--season SEASON season to plot
--measureFile MEASUREFILE
file that conains measures for spatial averaging, i.e. areas
for each of grid cells
--areaVariable AREAVARIABLE
name of the variable from measureFile that is used as area
for the spatial averaging. Use it if you want the statistics
to be normalized but the area different from the one
specified in "cell_measures" attrubute. Use "computed" if you
want to use full areas of the grid cells, as defined by their
lat-lon geometry.
For example:
plot-diff-map.py --save=diff.png diff.yamlwhere the contents of diff.yaml is:
variable: t_ref
levels: "-2:2:0.2, del(0)"
colormap: RdBu_r
experiments:
- files: "/archive/slm/am5/am5f12e0r1/c96L65_am5f12e0r1_amip_noLam/gfdl.ncrc5-intel25-prod-openmp/pp/atmos/ts/monthly/1yr/atmos.200*.t_ref.nc"
- files: "/archive/oar.gfdl.am5/am5/am5f12e0r1/c96L65_am5f12e0r1_amip/gfdl.ncrc5-deploy-prod-openmp/pp/atmos/ts/monthly/1yr/atmos.200*.t_ref.nc"Some of the options can be specified either in the common section, or for each
of the experiments individually. If an option is set for the specific experiment (e.g.
scale of the variable), then it is used to process this experiment data,
otherwise the option from the common section is used; if neither is set then the default
is used. For example, season could be JJA for the first and DJF for the
second experiment, in which case the average JJA-DJF difference will be calculated and
plotted. Another example: if "variable" is "t_surf" in the first experiment and
"t_ref" in the second, then the t_surf-t_ref difference will be calculated and
plotted.
Configuration options (case sensitive):
title: string, optional; if absent formed automaticallyfigureWidth: float, optiona, default is 10figureHeight: float, optiona, default is 5projection: string, optional, default is 'PlateCarree'; one of the global cartopy projectionscolormap: string, the name of a matplotlib colormap, optional, defaults to "rainbow"levels: string (e.g."-2:2:0.2, del(0)"), optional, but typically should be providedvariable: string, the name of the variable to plot; naturally the variable of that name must be present in each of the input filesscale: float, optional, default is 1.0units: string; if not specified units form the variable attributes are usedseason: string, default is "ANNUAL". Can also be a name of a month (e.g. APR or April), or abbreviated season name: a string of contiguous month initials, e.g. DJF, JJA, JJAS, etc.measureFile: file that conains measures for spatial averaging, i.e. areas for each of grid cells. This is required if the variable has "area" section in cell_measures attribute; typically one of the "static" files in the post-processingareaVariable: optional, name of the variable from measureFile that is used as area for the spatial averaging. Use it if you want the statistics to be normalized but the area different from the one specified in "cell_measures" attrubute. Use "computed" if you want to use full areas of the grid cells, as defined by their lat-lon geometry.years: optional, range of years as a string (e.g. "1979:2001", inclusive), optional, defaults to the all available in input filesexperiments: required, array of two experiment/variable/period/season descriptionsfiles: Unix file mask for the experiment data, requiredvariable: string, the name of the variable; variable of this name must be present in the input filesseason: string, can be different in each experiment, e.g. to calculate JJA-DJFscale: float, can be different in two experimentsunits: string (only units from the first experiment -- or general section -- are used)years: range of years, optional, can be different in two experiments, defaults to the all years available in the experiment's input files
The examples below use here-doc feature of Unix bash shell to keep all the
information in one place, but they also can use the configuration stored in a
yaml file, or (in many cases) command-line arguments.
plot-diff-map.py -v - <<EOF
variable: t_ref
levels: "-2:2:0.2, del(0)"
colormap: RdBu_r
experiments:
- files: "/archive/slm/am5/am5f12e0r1/c96L65_am5f12e0r1_amip_noLam/gfdl.ncrc5-intel25-prod-openmp/pp/atmos/ts/monthly/1yr/atmos.200*.t_ref.nc"
- files: "/archive/oar.gfdl.am5/am5/am5f12e0r1/c96L65_am5f12e0r1_amip/gfdl.ncrc5-deploy-prod-openmp/pp/atmos/ts/monthly/1yr/atmos.200*.t_ref.nc"
EOFplot-diff-map.py -v - <<EOF
variable: t_ref
levels: "-2:2:0.2"
colormap: RdBu_r
season: July
projection: Robinson
experiments:
- files: "/archive/slm/am5/am5f12e0r1/c96L65_am5f12e0r1_amip_noLam/gfdl.ncrc5-intel25-prod-openmp/pp/atmos/ts/monthly/1yr/atmos.200*.t_ref.nc"
- files: "/archive/oar.gfdl.am5/am5/am5f12e0r1/c96L65_am5f12e0r1_amip/gfdl.ncrc5-deploy-prod-openmp/pp/atmos/ts/monthly/1yr/atmos.200*.t_ref.nc"
EOFLand data have area variable specified in the cell_measure attribute of the
variables, and therefore the script uses it for calculating spatial statistics):
plot-diff-map.py -v - <<EOF
variable: t_ref
levels: "-2:2:0.2"
colormap: RdBu_r
measureFile: /archive/oar.gfdl.am5/am5/am5f12e0r1/c96L65_am5f12e0r1_amip/gfdl.ncrc5-deploy-prod-openmp/pp/land/land.static.nc
experiments:
- files: "/archive/slm/am5/am5f12e0r1/c96L65_am5f12e0r1_amip_noLam/gfdl.ncrc5-intel25-prod-openmp/pp/land/ts/monthly/1yr/land.200*.t_ref.nc"
- files: "/archive/oar.gfdl.am5/am5/am5f12e0r1/c96L65_am5f12e0r1_amip/gfdl.ncrc5-deploy-prod-openmp/pp/land/ts/monthly/1yr/land.200*.t_ref.nc"
EOFplot-diff-map.py -v - <<EOF
variable: precip
scale: 86400
units: "mm/day"
levels: "-2:2:0.2"
colormap: RdBu
experiments:
- files: "/archive/slm/am5/am5f12e0r1/c96L65_am5f12e0r1_amip_noLam/gfdl.ncrc5-intel25-prod-openmp/pp/atmos/ts/monthly/1yr/atmos.200*.precip.nc"
- files: "/archive/oar.gfdl.am5/am5/am5f12e0r1/c96L65_am5f12e0r1_amip/gfdl.ncrc5-deploy-prod-openmp/pp/atmos/ts/monthly/1yr/atmos.200*.precip.nc"
EOFplot-diff-map.py -v - <<EOF
levels: "-2:2:0.2"
colormap: RdBu_r
experiments:
- files: "/archive/oar.gfdl.am5/am5/am5f12e0r1/c96L65_am5f12e0r1_amip/gfdl.ncrc5-deploy-prod-openmp/pp/atmos/ts/monthly/1yr/atmos.200*.t_ref.nc"
variable: t_ref
- files: "/archive/oar.gfdl.am5/am5/am5f12e0r1/c96L65_am5f12e0r1_amip/gfdl.ncrc5-deploy-prod-openmp/pp/atmos/ts/monthly/1yr/atmos.200*.t_surf.nc"
variable: t_surf
EOFplot-diff-map.py -v - <<EOF
variable: t_ref
levels: "-2:2:0.2"
colormap: RdBu_r
season: JJAS
experiments:
- files: "/archive/slm/am5/am5f12e0r1/c96L65_am5f12e0r1_amip_noLam/gfdl.ncrc5-intel25-prod-openmp/pp/atmos/ts/monthly/1yr/atmos.200*.t_ref.nc"
- files: "/archive/oar.gfdl.am5/am5/am5f12e0r1/c96L65_am5f12e0r1_amip/gfdl.ncrc5-deploy-prod-openmp/pp/atmos/ts/monthly/1yr/atmos.200*.t_ref.nc"
EOFplot-diff-map.py -v - <<EOF
variable: t_ref
levels: "-50:50:5, -2:2:1, del(0)"
colormap: RdBu_r
files: "/archive/oar.gfdl.am5/am5/am5f12e0r1/c96L65_am5f12e0r1_amip/gfdl.ncrc5-deploy-prod-openmp/pp/atmos/ts/monthly/1yr/atmos.200*.t_ref.nc"
experiments:
- season: JJA
- season: DJF
EOFThis example also shows how to disable colorbar
plot-diff-map.py -v - <<EOF
variable: t_ref
levels: "-2:2:0.2"
colormap: RdBu_r
colorbar: No
files: "/archive/oar.gfdl.am5/am5/am5f12e0r1/c96L65_am5f12e0r1_amip/gfdl.ncrc5-deploy-prod-openmp/pp/atmos/ts/monthly/1yr/atmos.200*.t_ref.nc"
experiments:
- years: "2008:2009"
- years: "2000:2002"
EOFThis script allows to plot time series of annual mean values (average or total global) for a number of experiments, or for several variables, or for several regions, on the same plot.
usage: plot-annual-ts.py [-h] [-v] [-s FILENAME] FILENAME.yaml
plot something
positional arguments:
FILENAME.yaml plot configuration file, in yaml format
options:
-h, --help show this help message and exit
-v, --verbose increase verbosity
-s FILENAME, --save FILENAME
save plot to file (pdf, png, ...) instead of plotting it on
screen.The script expects to find the configuration of plots in the input YAML file, but it can
also come from stdin, as shown in examples below using here-doc feature of
bash; it should work in other shells too, perhaps with appropriate
modifications.
Some of the options can be specified either in the common section, or for each plot individually. If an option is set for the specific plot (e.g. name of the variable), then it is used to process this experiment data, otherwise the option from the common section is used; if neither is set then the default is used.
Many parameters may exist in either defaults or individual plots parameters, while some only make sense either in defaults or in per-plot settings
Description of parameters:
files: pattern (in terms of the Unix file name glob) describing the files where the data aremeasures: pattern of the file(s) containing the area associated with the variable; typically a "static" filevar: name of the variable to plotscale: float, optional, scaling factor applied to the variable before plottingunits: string, optional, units of the variable (after scale is applied)region: latitudinal region to plot, array. Example: [30S, 30N]label: string, optional, label of the line, if different from the defaultlineProperties: dictionary of the matplotlib properties of the line(s)op: aggregation operation in horizontal dimensions, one of ...smooth: number of years to smooth data overtitle: title of the entire plotfigureWidth,figureHeight: dimensions of the figure
In these examples, paths to input directories are assigned to shell variables that are consequently used in the plot configurations; this is solely for convenience and shorten the notation.
Plot time series of annual total vegetation carbon amount
from two experiments, using here-doc feature of bash shell:
dir1="/archive/slm/lm4p2/2022/lm4p2sc-GSWP3-potveg/gfdl.ncrc3-intel18-prod/pp"
dir2="/archive/slm/lm4p2/2025/lm4p2-c96am5-potveg/gfdl.ncrc6-intel23-prod/pp"
var=cVeg
plot-annual-ts.py -v - << EOF
scale: 1e-12
units: PgC
var: $var
plots:
- files: "$dir1/land_cmip/ts/monthly/5yr/land_cmip.00*.$var.nc"
measures: "$dir1/land_cmip/land_cmip.static.nc"
- files: "$dir2/land_cmip/ts/monthly/5yr/land_cmip.00*.$var.nc"
measures: "$dir2/land_cmip/land_cmip.static.nc"
lineProperties:
ls: "--"
color: black
linewidth: 2.5
EOFdir1="/archive/slm/lm4p2/2022/lm4p2sc-GSWP3-potveg/gfdl.ncrc3-intel18-prod/pp"
plot-annual-ts.py -v - << EOF
scale: 1e-12
units: PgC
measures: "$dir1/land_cmip/land_cmip.static.nc"
plots:
- files: "$dir1/land_cmip/ts/monthly/5yr/land_cmip.*.cSoil.nc"
var: cSoil
- files: "$dir1/land_cmip/ts/monthly/5yr/land_cmip.*.cVeg.nc"
var: cVeg
EOFExample of plotting several time series in a loop, saving plots to respective
files. This is written in bash, so users of c-shell must modify it
accordingly.
dir1="/archive/slm/lm4p2/2022/lm4p2sc-GSWP3-potveg/gfdl.ncrc3-intel18-prod/pp"
dir2="/archive/slm/lm4p2/2025/lm4p2-c96am5-potveg/gfdl.ncrc6-intel23-prod/pp"
for var in cVeg cSoil cLitter cLand; do
plot-annual-ts.py --save ts-$var.pdf - << EOF
scale: 1e-12
units: PgC
var: $var
plots:
- files: "$dir1/land_cmip/ts/monthly/5yr/land_cmip.*.$var.nc"
measures: "$dir1/land_cmip/land_cmip.static.nc"
- files: "$dir2/land_cmip/ts/monthly/5yr/land_cmip.*.$var.nc"
measures: "$dir2/land_cmip/land_cmip.static.nc"
EOF
doneThe example below do not work because land static file is broken
dir1="/archive/ens/CMIP7/ESM4/DECK/ESM4.5-landbridge-esm/gfdl.ncrc6-intel25-prod-openmp/pp"
dir2="/archive/ens/CMIP7/ESM4/DECK/ESM4.5-landbridge-newsun/gfdl.ncrc6-intel25-prod-openmp/pp"
var=btot
plot-annual-ts.py - << EOF
scale: 1e-12
units: PgC
var: $var
plots:
- files: "$dir1/land/ts/monthly/5yr/*.$var.nc"
measures: "$dir2/land/land.static.nc"
- files: "$dir2/land/ts/monthly/5yr/*.$var.nc"
measures: "$dir2/land/land.static.nc"
EOFplot-annual-ts.py - << EOF
scale: 1e-12
units: PgC
var: btot
plots:
- files: "/archive/ens/CMIP7/ESM4/DECK/ESM4.5-landbridge-newsun/gfdl.ncrc6-intel25-prod-openmp/pp/land/ts/monthly/5yr/*.btot.nc"
measures: "/archive/ens/CMIP7/ESM4/DECK/ESM4.5-landbridge-newsun/gfdl.ncrc6-intel25-prod-openmp/pp/land/land.static.nc"
EOFOld script that does not work any more:
ppDir=/archive/ens/CMIP7/ESM4/DECK/ESM4.5-landbridge-esm/gfdl.ncrc6-intel25-prod-openmp/pp
var=cLand
~/bin/plot/plot-annual-ts.py --verb --var=$var --scale=1e-12 --units=PgC \
--measures "$ppDir/land/land.static.nc" \
--exp "$ppDir/land/ts/monthly/5yr/*.Ctot.nc"
Error message:
File "/home/Sergey.Malyshev/bin/plot/plot-annual-ts.py", line 172, in <module>
src=nc.MFDataset(files,master_file=files[-1])
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "src/netCDF4/_netCDF4.pyx", line 7044, in netCDF4._netCDF4.MFDataset.__init__
ValueError: MFNetCDF4 only works with NETCDF3_* and NETCDF4_CLASSIC formatted files, not NETCDF4
The question of what is the proper way to calculate time average becomes less obvious if one considers seasons, different lengths of months for monthly data, missing data, and time-varying areas.
For example, suppose we need to calculate a DJF mean of the sensible heat flux from croplands, using monthly means as an input. First, we need to exclude the incomplete seasons, so for the typical case of the input monthly data set starting on January, the first month included in the averaging should be December of the first year, and the last --- February of the last year.
Likewise, we probably also need to exclude --- for each location --- seasons where part of the data may be missing, e.g because there were no cropland at that month (which can easily be happening for croplands in DJF). In more general form, we might want to exclude the seasons with the fraction of missing data above certain threshold.
Finally, the area of croplands is changing in time: should the time average for each point be weighted with the time-dependent area, or should all seasons contribute equally to the time averages? In the case of the area-weighted average the result will tend to be biased toward modern era, because the area of croplands generally increases in time.
After all of that is said and done, what area should we use to calculate area-weighted global statistics, e.g. global mean?
If the purpose is to calculate the geographical map of differences between two experim