Skip to content

Commit 9fa0baf

Browse files
Merge pull request #106 from gmacgilchrist/gmac_swmt
Surface watermass transformation diagnostic
2 parents 16e62b8 + fba289f commit 9fa0baf

File tree

4 files changed

+201
-0
lines changed

4 files changed

+201
-0
lines changed

ci/environment.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,8 @@ dependencies:
2727
- xarray
2828
- xesmf=0.6.2
2929
- xgcm
30+
- xhistogram
31+
- gsw
3032
- pip:
3133
- nc-time-axis>=1.3.1
3234
- git+https://github.com/jkrasting/cmip_basins.git
@@ -35,3 +37,4 @@ dependencies:
3537
- git+https://github.com/jkrasting/xcompare.git
3638
- git+https://github.com/jkrasting/xwavelet.git
3739
- git+https://github.com/jkrasting/xcompare.git
40+
- git+https://github.com/jetesdal/xwmt.git

om4labs/diags/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,3 +17,4 @@
1717
from . import section_transports
1818
from . import so_yz_annual_bias_1x1deg
1919
from . import acc_drake
20+
from . import surface_wmt

om4labs/diags/surface_wmt/__init__.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
from .surface_wmt import parse, read, calculate, plot, run, parse_and_run
2+
3+
__description__ = "Plots global watermass transformation (across density surfaces) from fluxes of heat, salt, and freshwater at the ocean surface"
4+
__ppstreams__ = [
5+
"ocean_monthly/av",
6+
]
7+
__ppvars__ = ["hfds","wfo","sfdsi","tos","sos"]
Lines changed: 190 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,190 @@
1+
#!/usr/bin/env python3
2+
3+
import argparse
4+
import pkg_resources as pkgr
5+
import io
6+
import matplotlib as mpl
7+
import matplotlib.pyplot as plt
8+
import numpy as np
9+
from om4labs import m6plot
10+
import palettable
11+
import xarray as xr
12+
import warnings
13+
14+
from xwmt.preprocessing import preprocessing
15+
from xwmt.swmt import swmt
16+
17+
from om4labs.om4common import horizontal_grid
18+
from om4labs.om4common import image_handler
19+
from om4labs.om4common import date_range
20+
from om4labs.om4common import open_intake_catalog
21+
from om4labs.om4parser import default_diag_parser
22+
23+
warnings.filterwarnings("ignore", message=".*csr_matrix.*")
24+
warnings.filterwarnings("ignore", message=".*dates out of range.*")
25+
26+
27+
def calculate(ds, bins, group_tend):
28+
"""Calculates watermass transformation from surface fluxes"""
29+
30+
G = swmt(ds).G("sigma0", bins=bins, group_tend=group_tend)
31+
32+
# If tendencies were grouped then G is a DataArray
33+
# For consistency in plotting function, convert it to a dataset
34+
if group_tend:
35+
G = G.to_dataset()
36+
37+
return G
38+
39+
40+
def parse(cliargs=None, template=False):
41+
"""
42+
Function to capture the user-specified command line options
43+
"""
44+
description = """ """
45+
46+
parser = default_diag_parser(
47+
description=description,
48+
template=template,
49+
exclude=["obsfile", "topog", "config", "platform", "basin"],
50+
)
51+
52+
parser.add_argument(
53+
"--bins",
54+
type=str,
55+
default="20,30,0.1",
56+
help="Density bins at which to evaluate transformation, provided as start, stop, increment.",
57+
)
58+
59+
parser.add_argument(
60+
"--group_tend",
61+
dest="group_tend",
62+
action="store_true",
63+
help="Group heat and salt tendencies together, i.e. only return the total transformation. Not passing this could lead to a performance cost.",
64+
)
65+
66+
if template is True:
67+
return parser.parse_args(None).__dict__
68+
else:
69+
return parser.parse_args(cliargs)
70+
71+
72+
def read(
73+
dictArgs,
74+
heatflux_varname="hfds",
75+
saltflux_varname="sfdsi",
76+
fwflux_varname="wfo",
77+
sst_varname="tos",
78+
sss_varname="sos",
79+
):
80+
"""Read in surface flux data"""
81+
82+
infile = dictArgs["infile"]
83+
ds = xr.open_mfdataset(infile, combine="by_coords", use_cftime=True)
84+
85+
### NEED TO IMPOSE CHECK TO MAKE SURE THIS IS NOT ANNUAL DATA
86+
87+
# Check that all required variables are here
88+
check_vars = [
89+
heatflux_varname,
90+
saltflux_varname,
91+
fwflux_varname,
92+
sst_varname,
93+
sss_varname,
94+
]
95+
check = all(item in ds.data_vars for item in check_vars)
96+
if not check:
97+
missing = set(check_vars) - set(ds.data_vars)
98+
raise RuntimeError(
99+
"Necessary variable {} not present in dataset".format(missing)
100+
)
101+
102+
ds["areacello"] = xr.open_mfdataset(dictArgs["static"])["areacello"]
103+
ds["deptho"] = xr.open_mfdataset(dictArgs["static"])["deptho"]
104+
ds["geolat"] = xr.open_mfdataset(dictArgs["static"])["geolat"]
105+
ds["geolon"] = xr.open_mfdataset(dictArgs["static"])["geolon"]
106+
107+
### WMT preprocessing step
108+
# Perhaps we should pull out some of what happens in here ?
109+
ds = preprocessing(ds, grid=ds, decode_times=False, verbose=False)
110+
111+
if "bins" in dictArgs:
112+
bins_args = dictArgs["bins"]
113+
bins_args = tuple([float(x) for x in bins_args.split(",")])
114+
bins = np.arange(*bins_args)
115+
else:
116+
# Default bins
117+
bins = np.arange(20, 30, 0.1)
118+
119+
# Retrieve group_tend boolean
120+
group_tend = dictArgs["group_tend"]
121+
122+
return (ds, bins, group_tend)
123+
124+
125+
def plot(G):
126+
127+
# Don't plot first or last bin (expanded to capture full range)
128+
G = G.isel(sigma0=slice(1, -1))
129+
levs = G["sigma0"].values
130+
131+
# Take annual mean and load
132+
G = G.mean("time").load()
133+
# Get terms in dataset
134+
terms = list(G.data_vars)
135+
136+
fig, ax = plt.subplots()
137+
# Plot each term
138+
for term in terms:
139+
if term == "heat":
140+
color = "tab:red"
141+
elif term == "salt":
142+
color = "tab:blue"
143+
else:
144+
color = "k"
145+
ax.plot(levs, G[term], label=term, color=color)
146+
147+
# If terms were not grouped then sum them up to get total
148+
if len(terms) > 1:
149+
total = xr.zeros_like(G[terms[0]])
150+
for term in terms:
151+
total += G[term]
152+
ax.plot(levs, total, label="total", color="k")
153+
154+
ax.legend()
155+
ax.set_xlabel("SIGMA0")
156+
ax.set_ylabel("TRANSFORMATION ($m^3s^{-1}$)")
157+
ax.autoscale(enable=True, axis="x", tight=True)
158+
159+
return fig
160+
161+
162+
def run(dictArgs):
163+
"""Function to call read, calc, and plot in sequence"""
164+
165+
# set visual backend
166+
if dictArgs["interactive"] is False:
167+
plt.switch_backend("Agg")
168+
169+
# --- the main show ---
170+
(ds, bins, group_tend) = read(dictArgs)
171+
172+
G = calculate(ds, bins, group_tend)
173+
174+
fig = plot(G)
175+
176+
filename = f"{dictArgs['outdir']}/surface_wmt"
177+
imgbufs = image_handler([fig], dictArgs, filename=filename)
178+
179+
return imgbufs
180+
181+
182+
def parse_and_run(cliargs=None):
183+
args = parse(cliargs)
184+
args = args.__dict__
185+
imgbuf = run(args)
186+
return imgbuf
187+
188+
189+
if __name__ == "__main__":
190+
parse_and_run()

0 commit comments

Comments
 (0)