xbudget 0.5.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
xbudget/__init__.py ADDED
@@ -0,0 +1,4 @@
1
+ """ xbudget: xarray and xgcm-based functions for evaluating finite-volume budgets"""
2
+ from .presets import *
3
+ from .collect import *
4
+ from .version import __version__
xbudget/collect.py ADDED
@@ -0,0 +1,381 @@
1
+ from operator import mul
2
+ from functools import reduce
3
+ import copy
4
+ import numpy as np
5
+ import numbers
6
+ import xarray as xr
7
+ import xgcm
8
+
9
+ import warnings
10
+
11
+ def aggregate(xbudget_dict, decompose=[]):
12
+ """Aggregate xbudget dictionary into simpler root-level budgets.
13
+
14
+ Parameters
15
+ ----------
16
+ xbudget_dict : dictionary in xbudget-compatible format
17
+ decompose : str or list (default: [])
18
+ Name of variable type(s) to decompose into the summed parts
19
+
20
+ Examples
21
+ --------
22
+ >>> xbudget_dict = {
23
+ "heat": {
24
+ "rhs": {
25
+ "sum": {
26
+ "advection": {
27
+ "var":"advective_tendency"
28
+ },
29
+ "var": "heat_rhs_sum"
30
+ },
31
+ "var": "heat_rhs",
32
+ }
33
+ }
34
+ }
35
+ >>> xbudget.aggregate(xbudget_dict)
36
+ {'heat': {'rhs': {'advection': 'advective_tendency'}}}
37
+
38
+ >>>xbudget_dict = {
39
+ "heat": {
40
+ "rhs": {
41
+ "sum": {
42
+ "advection": {
43
+ "var":"advective_tendency",
44
+ "sum": {
45
+ "horizontal": {
46
+ "var":"advective_tendency_h",
47
+ },
48
+ "vertical": {
49
+ "var":"advective_tendency_v"
50
+ },
51
+ "var":"heat_rhs_sum_advection_sum"
52
+ }
53
+ },
54
+ "var": "heat_rhs_sum"
55
+ },
56
+ "var": "heat_rhs",
57
+ }
58
+ }
59
+ }
60
+ >>> xbudget.aggregate(xbudget_dict)
61
+ {'heat': {'rhs': {'advection': 'advective_tendency'}}}
62
+
63
+ >>> xbudget.aggregate(xbudget_dict, decompose="advection")
64
+ {'heat': {'rhs': {'advection_horizontal': 'advective_tendency_h',
65
+ 'advection_vertical': 'advective_tendency_v'}}}
66
+
67
+ See also
68
+ --------
69
+ disaggregate, deep_search, _deep_search
70
+ """
71
+ new_budgets = copy.deepcopy(xbudget_dict)
72
+ for tr, tr_xbudget_dict in xbudget_dict.items():
73
+ for side,terms in tr_xbudget_dict.items():
74
+ if side in ["lhs", "rhs"]:
75
+ new_budgets[tr][side] = deep_search(
76
+ disaggregate(tr_xbudget_dict[side], decompose=decompose)
77
+ )
78
+ return new_budgets
79
+
80
+ def disaggregate(b, decompose=[]):
81
+ """Disaggregate variable's provenance dictionary into summed parts
82
+
83
+ Parameters
84
+ ----------
85
+ b : xbudget sub-dictionary for a variable
86
+ decompose : str or list (default: [])
87
+ Name of variable type(s) to decompose into the summed parts
88
+
89
+ Examples
90
+ --------
91
+ >>> b = {
92
+ "sum": {
93
+ "advection": {
94
+ "var":"advective_tendency",
95
+ "sum": {
96
+ "horizontal": {
97
+ "var":"advective_tendency_h",
98
+ },
99
+ "vertical": {
100
+ "var":"advective_tendency_v"
101
+ },
102
+ "var":"heat_rhs_sum_advection_sum"
103
+ }
104
+ },
105
+ "var": "heat_rhs_sum"
106
+ },
107
+ "var": "heat_rhs",
108
+ }
109
+ >>> {'advection': 'advective_tendency'}
110
+ {'advection': 'advective_tendency'}
111
+
112
+ >>> xbudget.disaggregate(b, decompose="advection")
113
+ {'advection': {'horizontal': 'advective_tendency_h',
114
+ 'vertical': 'advective_tendency_v'}}
115
+
116
+ See also
117
+ --------
118
+ aggregate
119
+ """
120
+ if "sum" in b:
121
+ bsum_novar = {k:v for (k,v) in b["sum"].items() if (k!="var") and (v is not None)}
122
+ sum_dict = dict((k,v["var"]) if ("var" in v) else (k,v) for k,v in bsum_novar.items())
123
+ b_recurse = {}
124
+ for (k,v) in sum_dict.items():
125
+ if k not in decompose:
126
+ b_recurse[k] = v
127
+ else:
128
+ v_dict = disaggregate(b["sum"][k], decompose=decompose)
129
+ if "product" in v_dict.keys():
130
+ b_recurse[k] = v_dict["var"]
131
+ else:
132
+ b_recurse[k] = v_dict
133
+ return b_recurse
134
+ return b
135
+
136
+ def deep_search(b):
137
+ """Utility function for searching for variables in xbudget dictionary.
138
+
139
+ See also
140
+ --------
141
+ aggregate, _deep_search
142
+ """
143
+ return _deep_search(b, new_b={}, k_last=None)
144
+
145
+ def _deep_search(b, new_b={}, k_last=None):
146
+ """Recursive function for searching for variables in xbudget dictionary.
147
+
148
+ See also
149
+ --------
150
+ aggregate, deep_search
151
+ """
152
+ if type(b) is str:
153
+ new_b[k_last] = b
154
+ elif type(b) is dict:
155
+ for (k, v) in b.items():
156
+ if k_last is not None:
157
+ k = f"{k_last}_{k}"
158
+ _deep_search(v, new_b=new_b, k_last=k)
159
+ return new_b
160
+
161
+ def collect_budgets(ds, xbudget_dict):
162
+ """Fills xbudget dictionary with all tracer content tendencies
163
+
164
+ Parameters
165
+ ----------
166
+ ds : xr.Dataset containing budget diagnostics
167
+ xbudget_dict : dictionary in xbudget-compatible format
168
+ Example format:
169
+ >>> xbudget_dict = {
170
+ "heat": {
171
+ "rhs": {
172
+ "sum": {
173
+ "advection": {
174
+ "var":"advective_tendency"
175
+ },
176
+ "var": "heat_rhs_sum"
177
+ },
178
+ "var": "heat_rhs",
179
+ }
180
+ }
181
+ }
182
+ """
183
+ for eq, v in xbudget_dict.items():
184
+ for side in ["lhs", "rhs"]:
185
+ if side in v:
186
+ budget_fill_dict(ds, v[side], f"{eq}_{side}")
187
+
188
+ def budget_fill_dict(data, xbudget_dict, namepath):
189
+ """Recursively fill xbudget dictionary
190
+
191
+ Parameters
192
+ ----------
193
+ data : xgcm.grid or xr.Dataset
194
+ xbudget_dict : dictionary in xbudget-compatible format containing variable in namepath
195
+ namepath : name of variable in dataset (data._ds or data)
196
+ """
197
+ if type(data)==xgcm.grid.Grid:
198
+ grid = data
199
+ ds = grid._ds
200
+ else:
201
+ ds = data
202
+ grid = None
203
+
204
+ var_pref = None
205
+
206
+ if ((xbudget_dict["var"] is not None) and
207
+ (xbudget_dict["var"] in ds) and
208
+ (namepath not in ds)):
209
+ var_rename = ds[xbudget_dict["var"]].rename(namepath)
210
+ var_rename.attrs['provenance'] = xbudget_dict["var"]
211
+ ds[namepath] = ds[xbudget_dict["var"]]
212
+ var_pref = ds[namepath]
213
+
214
+ for k,v in xbudget_dict.items():
215
+ if k in ['sum', 'product']:
216
+ op_list = []
217
+ for k_term, v_term in v.items():
218
+ if isinstance(v_term, dict): # recursive call to get this variable
219
+ v_term_recursive = budget_fill_dict(data, v_term, f"{namepath}_{k}_{k_term}")
220
+ if v_term_recursive is not None:
221
+ op_list.append(v_term_recursive)
222
+ elif isinstance(v_term, numbers.Number):
223
+ op_list.append(v_term)
224
+ elif isinstance(v_term, str):
225
+ if v_term in ds:
226
+ op_list.append(ds[v_term])
227
+ else:
228
+ warnings.warn(f"Variable {v_term} is missing from the dataset `ds`, so it is being skipped. To suppress this warning, remove {v_term} from the `xbudget_dict`.")
229
+ if k=="product":
230
+ op_list.append(0.)
231
+
232
+ # Compute variable from sum or product operation
233
+ if (
234
+ (len(op_list) == 0) |
235
+ all([e is None for e in op_list]) |
236
+ any([e is None for e in op_list])
237
+ ):
238
+ return None
239
+ else:
240
+ var = sum(op_list) if k=="sum" else reduce(mul, op_list, 1)
241
+ if not isinstance(var, xr.DataArray):
242
+ continue
243
+
244
+ # Variable metadata
245
+ var_name = f"{namepath}_{k}"
246
+ var = var.rename(var_name)
247
+ var_provenance = [o.name if isinstance(o, xr.DataArray) else o for o in op_list]
248
+ var.attrs["provenance"] = var_provenance
249
+ ds[var_name] = var
250
+ if (xbudget_dict[k]["var"] is None):
251
+ xbudget_dict[k]["var"] = var_name
252
+
253
+ if (xbudget_dict["var"] is None):
254
+ var_copy = var.copy()
255
+ var_copy.attrs["provenance"] = var_name
256
+ xbudget_dict["var"] = namepath
257
+ if namepath not in ds:
258
+ ds[namepath] = var_copy
259
+
260
+ # keep record of the first-listed variable
261
+ if var_pref is None:
262
+ var_pref = var.copy()
263
+
264
+ if k == "difference":
265
+ if grid is not None:
266
+ staggered_axes = {
267
+ axn:c for axn,ax in grid.axes.items()
268
+ for pos,c in ax.coords.items()
269
+ if pos!="center"
270
+ }
271
+ v_term = [v_term for k_term,v_term in v.items() if k_term!="var"][0]
272
+ if v_term not in ds:
273
+ warnings.warn(f"Variable {v_term} is missing from the dataset `ds`, so it is being skipped. To suppress this warning, remove {v_term} from the `xbudget_dict`.")
274
+ continue
275
+ candidate_axes = [axn for (axn,c) in staggered_axes.items() if c in ds[v_term].dims]
276
+ if len(candidate_axes) == 1:
277
+ axis = candidate_axes[0]
278
+ else:
279
+ raise ValueError("Flux difference inconsistent with finite volume discretization.")
280
+ var = grid.diff(ds[v_term].fillna(0.), axis)
281
+ var_name = f"{namepath}_difference"
282
+ var = var.rename(var_name)
283
+ var_provenance = v_term
284
+ var.attrs["provenance"] = var_provenance
285
+ ds[var_name] = var
286
+ if var_pref is None:
287
+ var_pref = var.copy()
288
+ else:
289
+ raise ValueError("Input `ds` must be `xgcm.Grid` instance if using `difference` operations.")
290
+
291
+ return var_pref
292
+
293
+ def get_vars(xbudget_dict, terms):
294
+ """Get xbudget sub-dictionaries for specified terms.
295
+
296
+ Parameters
297
+ ----------
298
+ xbudget_dict : dictionary in xbudget-compatible format
299
+ terms : str or list of str
300
+
301
+ Examples
302
+ -------
303
+ >>> xbudget_dict = {
304
+ "heat": {
305
+ "rhs": {
306
+ "sum": {
307
+ "advection": {
308
+ "var":"advective_tendency"
309
+ },
310
+ "var": "heat_rhs_sum"
311
+ },
312
+ "var": "heat_rhs",
313
+ }
314
+ }
315
+ }
316
+ >>> xbudget.get_vars(xbudget_dict, "heat_rhs_sum")
317
+ {'var': 'heat_rhs_sum', 'sum': ['advective_tendency']}
318
+ """
319
+ return _get_vars(xbudget_dict, terms)
320
+
321
+ def _get_vars(b, terms, k_long=""):
322
+ """Recursive version of _get_vars for determining variable provenance tree.
323
+
324
+ Parameters
325
+ ----------
326
+ b : dictionary
327
+ terms : str or list of str
328
+ k_long : variable name suffix
329
+
330
+ See also
331
+ --------
332
+ get_vars
333
+ """
334
+ if isinstance(terms, (list, np.ndarray)):
335
+ return [_get_vars(b, term) for term in terms]
336
+ elif type(terms) is str:
337
+ for k,v in b.items():
338
+ if type(v) is str:
339
+ k_short = k_long.replace("_sum", "").replace("_product", "")
340
+ if v==terms:
341
+ decomps = {"var": v}
342
+ if len(terms) > len("_sum"):
343
+ if (terms[-len("_sum"):] == "_sum") and ("sum" in b):
344
+ ts = {kk:vv for (kk,vv) in b["sum"].items() if kk!="var"}
345
+ decomps["sum"] = [vv["var"] if type(vv) is dict else vv for (kk,vv) in ts.items()]
346
+ elif (terms[-len("_sum"):] == "_sum"):
347
+ ts = {kk:vv for (kk,vv) in b.items() if kk!="var"}
348
+ decomps["sum"] = [vv["var"] if type(vv) is dict else vv for (kk,vv) in ts.items()]
349
+ if len(terms) > len("_product"):
350
+ if (terms[-len("_product"):] == "_product") and ("product" in b):
351
+ ts = {kk:vv for (kk,vv) in b["product"].items() if kk!="var"}
352
+ decomps["product"] = [vv["var"] if type(vv) is dict else vv for (kk,vv) in ts.items()]
353
+ elif (terms[-len("_product"):] == "_product"):
354
+ ts = {kk:vv for (kk,vv) in b.items() if kk!="var"}
355
+ decomps["product"] = [vv["var"] if type(vv) is dict else vv for (kk,vv) in ts.items()]
356
+ return decomps
357
+
358
+ if k!="var":
359
+ k_short+="_"+k
360
+ if k_short==terms:
361
+ return v
362
+ elif type(v) is dict:
363
+ if k_long=="":
364
+ new_k = k
365
+ elif len(k_long)>0:
366
+ new_k = f"{k_long}_{k}"
367
+ var = _get_vars(v, terms, k_long=new_k)
368
+ if var is not None:
369
+ return var
370
+
371
+ def flatten(container):
372
+ for i in container:
373
+ if isinstance(i, (list,tuple)):
374
+ for j in flatten(i):
375
+ yield j
376
+ else:
377
+ yield i
378
+
379
+ def flatten_lol(lol):
380
+ """Flatten a list of lists into a single list."""
381
+ return list(flatten(lol))
@@ -0,0 +1,349 @@
1
+ ---
2
+ mass: # finite-volume mass budget in units of kg/s
3
+ lambda: "density"
4
+ thickness: "thkcello"
5
+ lhs:
6
+ var: null
7
+ sum:
8
+ var: null
9
+ Eulerian_tendency:
10
+ var: null
11
+ product:
12
+ var: null
13
+ thickness_tendency: "dhdt"
14
+ density: 1035.
15
+ area: "areacello"
16
+ rhs:
17
+ var: null
18
+ sum:
19
+ var: null
20
+ advection:
21
+ var: null
22
+ sum:
23
+ var: null
24
+ lateral:
25
+ var: null
26
+ product:
27
+ var: null
28
+ thickness_tendency: "dynamics_h_tendency"
29
+ density: 1035.
30
+ area: "areacello"
31
+ sum:
32
+ var: null
33
+ zonal_convergence:
34
+ var: null
35
+ product:
36
+ var: null
37
+ zonal_divergence:
38
+ var: null
39
+ difference:
40
+ var: null
41
+ zonal_mass_transport: "umo"
42
+ sign: -1.
43
+ meridional_convergence:
44
+ var: null
45
+ product:
46
+ var: null
47
+ meridional_divergence:
48
+ var: null
49
+ difference:
50
+ var: null
51
+ meridional_mass_transport: "vmo"
52
+ sign: -1.
53
+ interfacial:
54
+ var: null
55
+ product:
56
+ var: null
57
+ thickness_tendency: "vert_remap_h_tendency"
58
+ density: 1035.
59
+ area: "areacello"
60
+ surface_exchange_flux:
61
+ var: null
62
+ product:
63
+ var: null
64
+ thickness_tendency: "boundary_forcing_h_tendency"
65
+ density: 1035.
66
+ area: "areacello"
67
+ sum:
68
+ var: null
69
+ rain_and_ice:
70
+ var: null
71
+ product:
72
+ var: null
73
+ mass_tendency_per_unit_area: "prlq"
74
+ area: "areacello"
75
+ snow:
76
+ var: null
77
+ product:
78
+ var: null
79
+ mass_tendency_per_unit_area: "prsn"
80
+ area: "areacello"
81
+ evaporation:
82
+ var: null
83
+ product:
84
+ var: null
85
+ mass_tendency_per_unit_area: "evs"
86
+ area: "areacello"
87
+ rivers:
88
+ var: null
89
+ product:
90
+ var: null
91
+ mass_tendency_per_unit_area: "friver"
92
+ area: "areacello"
93
+ icebergs:
94
+ var: null
95
+ product:
96
+ var: null
97
+ mass_tendency_per_unit_area: "ficeberg"
98
+ area: "areacello"
99
+ sea_ice_melt:
100
+ var: null
101
+ product:
102
+ var: null
103
+ mass_tendency_per_unit_area: "fsitherm"
104
+ area: "areacello"
105
+ virtual_precip_restoring:
106
+ var: null
107
+ product:
108
+ var: null
109
+ mass_tendency_per_unit_area: "vprec"
110
+ area: "areacello"
111
+
112
+ heat: # finite-volume heat budget in units of J/s
113
+ lambda: "thetao"
114
+ surface_lambda: "tos"
115
+ lhs:
116
+ var: null
117
+ sum:
118
+ var: null
119
+ Eulerian_tendency:
120
+ var: null
121
+ product:
122
+ var: null
123
+ tracer_content_tendency_per_unit_area: "opottemptend"
124
+ area: "areacello"
125
+ advection:
126
+ var: null
127
+ sum:
128
+ var: null
129
+ lateral:
130
+ var: null
131
+ product:
132
+ var: null
133
+ sign: -1.
134
+ tracer_content_tendency_per_unit_area: "T_advection_xy"
135
+ area: "areacello"
136
+ interfacial:
137
+ var: null
138
+ product:
139
+ var: null
140
+ sign: -1.
141
+ tracer_content_tendency_per_unit_area: "Th_tendency_vert_remap"
142
+ area: "areacello"
143
+ surface_ocean_flux_advective_negative_lhs:
144
+ var: null
145
+ product:
146
+ var: null
147
+ sign: -1.
148
+ specific_heat_capacity: 3992.
149
+ lambda_mass: "tos"
150
+ thickness_tendency: "boundary_forcing_h_tendency"
151
+ density: 1035.
152
+ area: "areacello"
153
+ rhs:
154
+ var: null
155
+ sum:
156
+ var: null
157
+ diffusion:
158
+ var: null
159
+ sum:
160
+ var: null
161
+ lateral:
162
+ var: null
163
+ product:
164
+ var: null
165
+ tracer_content_tendency_per_unit_area: "opottemppmdiff"
166
+ area: "areacello"
167
+ interfacial:
168
+ var: null
169
+ product:
170
+ var: null
171
+ tracer_content_tendency_per_unit_area: "opottempdiff"
172
+ area: "areacello"
173
+
174
+ surface_exchange_flux:
175
+ var:
176
+ product:
177
+ var: null
178
+ tracer_content_tendency_per_unit_area: "boundary_forcing_heat_tendency"
179
+ area: "areacello"
180
+ sum:
181
+ var: null
182
+ nonadvective:
183
+ var: null
184
+ sum:
185
+ var: null # This sum is currently broken because the shortwave terms are 3D while the other terms are 2D!
186
+ latent:
187
+ var: null
188
+ product:
189
+ var: null
190
+ tracer_content_tendency_per_unit_area: "hflso"
191
+ area: "areacello"
192
+ sensible:
193
+ var: null
194
+ product:
195
+ var: null
196
+ tracer_content_tendency_per_unit_area: "hfsso"
197
+ area: "areacello"
198
+ longwave:
199
+ var: null
200
+ product:
201
+ var: null
202
+ tracer_content_tendency_per_unit_area: "rlntds"
203
+ area: "areacello"
204
+ shortwave:
205
+ var: null
206
+ product:
207
+ var: null
208
+ tracer_content_tendency_per_unit_area: "rsdoabsorb"
209
+ area: "areacello"
210
+ # Not yet supported!
211
+ #var: "rsdoabsorb"
212
+ #finite_difference:
213
+ # Z: "rsdo"
214
+ advective:
215
+ var: null
216
+ product:
217
+ var: null
218
+ tracer_content_tendency_per_unit_area: "heat_content_surfwater"
219
+ area: "areacello"
220
+ #Alternative method:
221
+ #product:
222
+ # var: null
223
+ # specific_heat_capacity: 3992.
224
+ # lambda_mass: "tos"
225
+ # thickness_tendency: "boundary_forcing_h_tendency"
226
+ # density: 1035.
227
+ # area: "areacello"
228
+ surface_ocean_flux_advective_negative_rhs:
229
+ var: null
230
+ product:
231
+ var: null
232
+ sign: -1.
233
+ specific_heat_capacity: 3992.
234
+ lambda_mass: "tos"
235
+ thickness_tendency: "boundary_forcing_h_tendency"
236
+ density: 1035.
237
+ area: "areacello"
238
+ bottom_flux:
239
+ var: null
240
+ product:
241
+ var: null
242
+ tracer_content_tendency_per_unit_area: "internal_heat_heat_tendency"
243
+ area: "areacello"
244
+ frazil_ice:
245
+ var: null
246
+ product:
247
+ var: null
248
+ tracer_content_tendency_per_unit_area: "frazil_heat_tendency"
249
+ area: "areacello"
250
+
251
+ salt: # finite-volume salt budget in units of kg/s
252
+ lambda: "so"
253
+ surface_lambda: "sos"
254
+ lhs:
255
+ var: null
256
+ sum:
257
+ var: null
258
+ Eulerian_tendency:
259
+ var: null
260
+ product:
261
+ var: null
262
+ tracer_content_tendency_per_unit_area: "osalttend"
263
+ area: "areacello"
264
+ advection:
265
+ var: null
266
+ sum:
267
+ var: null
268
+ lateral:
269
+ var: null
270
+ product:
271
+ var: null
272
+ sign: -1.
273
+ tracer_content_tendency_per_unit_area: "S_advection_xy"
274
+ area: "areacello"
275
+ interfacial:
276
+ var: null
277
+ product:
278
+ var: null
279
+ sign: -1.
280
+ tracer_content_tendency_per_unit_area: "Sh_tendency_vert_remap"
281
+ area: "areacello"
282
+ surface_ocean_flux_advective_negative_lhs:
283
+ var: null
284
+ product:
285
+ var: null
286
+ sign: -1.
287
+ unit_conversion: 0.001
288
+ lambda_mass: "sos"
289
+ thickness_tendency: "boundary_forcing_h_tendency"
290
+ density: 1035.
291
+ area: "areacello"
292
+
293
+ rhs:
294
+ var: null
295
+ sum:
296
+ var: null
297
+ diffusion:
298
+ var: null
299
+ sum:
300
+ var: null
301
+ lateral:
302
+ var: null
303
+ product:
304
+ var: null
305
+ tracer_content_tendency_per_unit_area: "osaltpmdiff"
306
+ area: "areacello"
307
+ interfacial:
308
+ var: null
309
+ product:
310
+ var: null
311
+ tracer_content_tendency_per_unit_area: "osaltdiff"
312
+ area: "areacello"
313
+ surface_exchange_flux:
314
+ var:
315
+ product:
316
+ var: null
317
+ tracer_content_tendency_per_unit_area: "boundary_forcing_salt_tendency"
318
+ area: "areacello"
319
+ sum:
320
+ var: null
321
+ nonadvective:
322
+ var: null
323
+ sum:
324
+ var: null
325
+ basal:
326
+ var: null
327
+ product:
328
+ var: null
329
+ tracer_content_tendency_per_unit_area: "sfdsi"
330
+ area: "areacello"
331
+ advective:
332
+ var: null
333
+ product:
334
+ var: null
335
+ unit_conversion: 0.001
336
+ lambda_mass: 0.
337
+ thickness_tendency: "boundary_forcing_h_tendency"
338
+ density: 1035.
339
+ area: "areacello"
340
+ surface_ocean_flux_advective_negative_rhs:
341
+ var: null
342
+ product:
343
+ var: null
344
+ sign: -1.
345
+ unit_conversion: 0.001
346
+ lambda_mass: "sos"
347
+ thickness_tendency: "boundary_forcing_h_tendency"
348
+ density: 1035.
349
+ area: "areacello"
@@ -0,0 +1,230 @@
1
+ ---
2
+ mass: # finite-volume mass budget in units of kg/s
3
+ lambda: "density"
4
+ thickness: "thkcello"
5
+ lhs:
6
+ var: null
7
+ sum:
8
+ var: null
9
+ Eulerian_tendency:
10
+ var: null
11
+ product:
12
+ var: null
13
+ thickness_tendency: null
14
+ density: 1035.
15
+ area: "areacello"
16
+ rhs:
17
+ var: null
18
+ sum:
19
+ var: null
20
+ advection:
21
+ var: null
22
+ sum:
23
+ var: null
24
+ lateral:
25
+ var: null
26
+ sum:
27
+ var: null
28
+ zonal_convergence:
29
+ var: null
30
+ product:
31
+ var: null
32
+ zonal_divergence:
33
+ var: null
34
+ difference:
35
+ var: null
36
+ zonal_mass_transport: "umo"
37
+ sign: -1.
38
+ meridional_convergence:
39
+ var: null
40
+ product:
41
+ var: null
42
+ meridional_divergence:
43
+ var: null
44
+ difference:
45
+ var: null
46
+ meridional_mass_transport: "vmo"
47
+ interfacial:
48
+ var: null
49
+ product:
50
+ var: null
51
+ thickness_tendency: "vert_remap_h_tendency"
52
+ density: 1035.
53
+ area: "areacello"
54
+ surface_exchange_flux:
55
+ var: null
56
+ product:
57
+ var: null
58
+ thickness_tendency: "boundary_forcing_h_tendency"
59
+ density: 1035.
60
+ area: "areacello"
61
+
62
+ heat: # finite-volume heat budget in units of J/s
63
+ lambda: "thetao"
64
+ surface_lambda: "tos"
65
+ lhs:
66
+ var: null
67
+ sum:
68
+ var: null
69
+ Eulerian_tendency:
70
+ var: null
71
+ product:
72
+ var: null
73
+ tracer_content_tendency_per_unit_area: "opottemptend"
74
+ area: "areacello"
75
+ advection:
76
+ var: null
77
+ sum:
78
+ var: null
79
+ lateral:
80
+ var: null
81
+ product:
82
+ var: null
83
+ sign: -1.
84
+ tracer_content_tendency_per_unit_area: "T_advection_xy"
85
+ area: "areacello"
86
+ interfacial:
87
+ var: null
88
+ product:
89
+ var: null
90
+ sign: -1.
91
+ tracer_content_tendency_per_unit_area: "Th_tendency_vert_remap"
92
+ area: "areacello"
93
+ surface_ocean_flux_advective_negative_lhs:
94
+ var: null
95
+ product:
96
+ var: null
97
+ sign: -1.
98
+ specific_heat_capacity: 3992.
99
+ lambda_mass: "tos"
100
+ thickness_tendency: "boundary_forcing_h_tendency"
101
+ density: 1035.
102
+ area: "areacello"
103
+ rhs:
104
+ var: null
105
+ sum:
106
+ var: null
107
+ diffusion:
108
+ var: null
109
+ sum:
110
+ var: null
111
+ lateral:
112
+ var: null
113
+ product:
114
+ var: null
115
+ tracer_content_tendency_per_unit_area: "opottemppmdiff"
116
+ area: "areacello"
117
+ interfacial:
118
+ var: null
119
+ product:
120
+ var: null
121
+ tracer_content_tendency_per_unit_area: "opottempdiff"
122
+ area: "areacello"
123
+
124
+ surface_exchange_flux:
125
+ var: null
126
+ product:
127
+ var: null
128
+ tracer_content_tendency_per_unit_area: "boundary_forcing_heat_tendency"
129
+ area: "areacello"
130
+ surface_ocean_flux_advective_negative_rhs:
131
+ var: null
132
+ product:
133
+ var: null
134
+ sign: -1.
135
+ specific_heat_capacity: 3992.
136
+ lambda_mass: "tos"
137
+ thickness_tendency: "boundary_forcing_h_tendency"
138
+ density: 1035.
139
+ area: "areacello"
140
+ bottom_flux:
141
+ var: null
142
+ product:
143
+ var: null
144
+ tracer_content_tendency_per_unit_area: "internal_heat_heat_tendency"
145
+ area: "areacello"
146
+ frazil_ice:
147
+ var: null
148
+ product:
149
+ var: null
150
+ tracer_content_tendency_per_unit_area: "frazil_heat_tendency"
151
+ area: "areacello"
152
+
153
+ salt: # finite-volume salt budget in units of kg/s
154
+ lambda: "so"
155
+ surface_lambda: "sos"
156
+ lhs:
157
+ var: null
158
+ sum:
159
+ var: null
160
+ Eulerian_tendency:
161
+ var: null
162
+ product:
163
+ var: null
164
+ tracer_content_tendency_per_unit_area: "osalttend"
165
+ area: "areacello"
166
+ advection:
167
+ var: null
168
+ sum:
169
+ var: null
170
+ lateral:
171
+ var: null
172
+ product:
173
+ var: null
174
+ sign: -1.
175
+ tracer_content_tendency_per_unit_area: "S_advection_xy"
176
+ area: "areacello"
177
+ interfacial:
178
+ var: null
179
+ product:
180
+ var: null
181
+ sign: -1.
182
+ tracer_content_tendency_per_unit_area: "Sh_tendency_vert_remap"
183
+ area: "areacello"
184
+ surface_ocean_flux_advective_negative_lhs:
185
+ var: null
186
+ product:
187
+ var: null
188
+ sign: -1.
189
+ unit_conversion: 0.001
190
+ lambda_mass: "sos"
191
+ thickness_tendency: "boundary_forcing_h_tendency"
192
+ density: 1035.
193
+ area: "areacello"
194
+
195
+ rhs:
196
+ var: null
197
+ sum:
198
+ var: null
199
+ diffusion:
200
+ var: null
201
+ sum:
202
+ var: null
203
+ lateral:
204
+ var: null
205
+ product:
206
+ var: null
207
+ tracer_content_tendency_per_unit_area: "osaltpmdiff"
208
+ area: "areacello"
209
+ interfacial:
210
+ var: null
211
+ product:
212
+ var: null
213
+ tracer_content_tendency_per_unit_area: "osaltdiff"
214
+ area: "areacello"
215
+ surface_exchange_flux:
216
+ var:
217
+ product:
218
+ var: null
219
+ tracer_content_tendency_per_unit_area: "boundary_forcing_salt_tendency"
220
+ area: "areacello"
221
+ surface_ocean_flux_advective_negative_rhs:
222
+ var: null
223
+ product:
224
+ var: null
225
+ sign: -1.
226
+ unit_conversion: 0.001
227
+ lambda_mass: "sos"
228
+ thickness_tendency: "boundary_forcing_h_tendency"
229
+ density: 1035.
230
+ area: "areacello"
@@ -0,0 +1,70 @@
1
+ ---
2
+ mass: # finite-volume mass budget in units of kg/s
3
+ lambda: "density"
4
+ thickness: "thkcello"
5
+ lhs:
6
+ var: null
7
+ sum:
8
+ var: null
9
+ Eulerian_tendency:
10
+ var: null
11
+ rhs:
12
+ var: null
13
+ sum:
14
+ var: null
15
+ advection:
16
+ var: null
17
+ surface_exchange_flux:
18
+ var: null
19
+
20
+ heat: # finite-volume heat budget in units of J/s
21
+ lambda: "thetao"
22
+ surface_lambda: "tos"
23
+ lhs:
24
+ var: null
25
+ sum:
26
+ var: null
27
+ Eulerian_tendency:
28
+ var: null
29
+ surface_ocean_flux_advective_negative_lhs:
30
+ var: null
31
+ rhs:
32
+ var: null
33
+ sum:
34
+ var: null
35
+ diffusion:
36
+ var: null
37
+ surface_exchange_flux:
38
+ var: null
39
+ surface_ocean_flux_advective_negative_rhs:
40
+ var: null
41
+ bottom_flux:
42
+ var: null
43
+ frazil_ice:
44
+ var: null
45
+
46
+ salt: # finite-volume salt budget in units of kg/s
47
+ lambda: "so"
48
+ surface_lambda: "sos"
49
+ lhs:
50
+ var: null
51
+ sum:
52
+ var: null
53
+ Eulerian_tendency:
54
+ var: null
55
+ advection:
56
+ var: null
57
+ sum:
58
+ var: null
59
+ surface_ocean_flux_advective_negative_lhs:
60
+ var: null
61
+ rhs:
62
+ var: null
63
+ sum:
64
+ var: null
65
+ diffusion:
66
+ var: null
67
+ surface_exchange_flux:
68
+ var: null
69
+ surface_ocean_flux_advective_negative_rhs:
70
+ var: null
@@ -0,0 +1,214 @@
1
+ ---
2
+ mass: # finite-volume mass budget in units of kg/s
3
+ lambda: "density"
4
+ thickness: "thkcello"
5
+ lhs:
6
+ var: null
7
+ rhs:
8
+ var: null
9
+ sum:
10
+ var: null
11
+ surface_exchange_flux:
12
+ var: null
13
+ sum:
14
+ var: null
15
+ rain_and_ice:
16
+ var: null
17
+ product:
18
+ var: null
19
+ mass_tendency_per_unit_area: "prlq"
20
+ area: "areacello"
21
+ snow:
22
+ var: null
23
+ product:
24
+ var: null
25
+ mass_tendency_per_unit_area: "prsn"
26
+ area: "areacello"
27
+ evaporation:
28
+ var: null
29
+ product:
30
+ var: null
31
+ mass_tendency_per_unit_area: "evs"
32
+ area: "areacello"
33
+ rivers:
34
+ var: null
35
+ product:
36
+ var: null
37
+ mass_tendency_per_unit_area: "friver"
38
+ area: "areacello"
39
+ icebergs:
40
+ var: null
41
+ product:
42
+ var: null
43
+ mass_tendency_per_unit_area: "ficeberg"
44
+ area: "areacello"
45
+ sea_ice_melt:
46
+ var: null
47
+ product:
48
+ var: null
49
+ mass_tendency_per_unit_area: "fsitherm"
50
+ area: "areacello"
51
+ virtual_precip_restoring:
52
+ var: null
53
+ product:
54
+ var: null
55
+ mass_tendency_per_unit_area: "vprec"
56
+ area: "areacello"
57
+
58
+ heat: # finite-volume heat budget in units of J/s
59
+ lambda: "thetao"
60
+ surface_lambda: "tos"
61
+ lhs:
62
+ var: null
63
+ rhs:
64
+ var: null
65
+ sum:
66
+ var: null
67
+ surface_exchange_flux:
68
+ var:
69
+ sum:
70
+ var: null
71
+ nonadvective:
72
+ var: null
73
+ sum:
74
+ var: null
75
+ latent:
76
+ var: null
77
+ product:
78
+ var: null
79
+ tracer_content_tendency_per_unit_area: "hflso"
80
+ area: "areacello"
81
+ sensible:
82
+ var: null
83
+ product:
84
+ var: null
85
+ tracer_content_tendency_per_unit_area: "hfsso"
86
+ area: "areacello"
87
+ longwave:
88
+ var: null
89
+ product:
90
+ var: null
91
+ tracer_content_tendency_per_unit_area: "rlntds"
92
+ area: "areacello"
93
+ shortwave:
94
+ var: null
95
+ product:
96
+ var: null
97
+ tracer_content_tendency_per_unit_area: "rsntds"
98
+ area: "areacello"
99
+ advective:
100
+ var: null
101
+ product:
102
+ var: null
103
+ specific_heat_capacity: 3992.
104
+ lambda_mass: "tos"
105
+ mass_tendency_per_unit_area: "wfo"
106
+ area: "areacello"
107
+ surface_ocean_flux_advective_negative_rhs:
108
+ var: null
109
+ product:
110
+ var: null
111
+ sign: -1.
112
+ specific_heat_capacity: 3992.
113
+ lambda_mass: "tos"
114
+ mass_tendency_per_unit_area: "wfo"
115
+ area: "areacello"
116
+
117
+ salt: # finite-volume salt budget in units of kg/s
118
+ lambda: "so"
119
+ surface_lambda: "sos"
120
+ lhs:
121
+ var: null
122
+ rhs:
123
+ var: null
124
+ sum:
125
+ var: null
126
+ surface_exchange_flux:
127
+ var:
128
+ sum:
129
+ var: null
130
+ nonadvective:
131
+ var: null
132
+ sum:
133
+ var: null
134
+ basal:
135
+ var: null
136
+ product:
137
+ var: null
138
+ tracer_content_tendency_per_unit_area: "sfdsi"
139
+ area: "areacello"
140
+ advective:
141
+ var: null
142
+ product:
143
+ var: null
144
+ unit_conversion: 0.001
145
+ lambda_mass: 0.
146
+ mass_tendency_per_unit_area: "wfo"
147
+ area: "areacello"
148
+ surface_ocean_flux_advective_negative_rhs:
149
+ var: null
150
+ sum:
151
+ var: null
152
+ rain_and_ice:
153
+ var: null
154
+ product:
155
+ var: null
156
+ sign: -1.
157
+ unit_conversion: 0.001
158
+ lambda_mass: "sos"
159
+ mass_tendency_per_unit_area: "prlq"
160
+ area: "areacello"
161
+ snow:
162
+ var: null
163
+ product:
164
+ var: null
165
+ sign: -1.
166
+ unit_conversion: 0.001
167
+ lambda_mass: "sos"
168
+ mass_tendency_per_unit_area: "prsn"
169
+ area: "areacello"
170
+ evaporation:
171
+ var: null
172
+ product:
173
+ var: null
174
+ sign: -1.
175
+ unit_conversion: 0.001
176
+ lambda_mass: "sos"
177
+ mass_tendency_per_unit_area: "evs"
178
+ area: "areacello"
179
+ rivers:
180
+ var: null
181
+ product:
182
+ var: null
183
+ sign: -1.
184
+ unit_conversion: 0.001
185
+ lambda_mass: "sos"
186
+ mass_tendency_per_unit_area: "friver"
187
+ area: "areacello"
188
+ icebergs:
189
+ var: null
190
+ product:
191
+ var: null
192
+ sign: -1.
193
+ unit_conversion: 0.001
194
+ lambda_mass: "sos"
195
+ mass_tendency_per_unit_area: "ficeberg"
196
+ area: "areacello"
197
+ sea_ice_melt:
198
+ var: null
199
+ product:
200
+ var: null
201
+ sign: -1.
202
+ unit_conversion: 0.001
203
+ lambda_mass: "sos"
204
+ mass_tendency_per_unit_area: "fsitherm"
205
+ area: "areacello"
206
+ virtual_precip_restoring:
207
+ var: null
208
+ product:
209
+ var: null
210
+ sign: -1.
211
+ unit_conversion: 0.001
212
+ lambda_mass: "sos"
213
+ mass_tendency_per_unit_area: "vprec"
214
+ area: "areacello"
xbudget/presets.py ADDED
@@ -0,0 +1,42 @@
1
+ import yaml
2
+ from os import path
3
+
4
+ def load_preset_budget(model="MOM6"):
5
+ """Loads preset xbudget dictionary from yaml file for supported models.
6
+
7
+ Note: Does not yet check whether or not the format of the yaml file (or
8
+ the corresponding Python dictionary) is in an xbudget-compatible format!
9
+
10
+ Parameters
11
+ ----------
12
+ model : str (default "MOM6")
13
+ Currently supported models: ["MOM6"]
14
+ Please open an Issue if you would like to contribute an xbudget yaml
15
+ file for a new model–see /conventions/ for examples.
16
+
17
+ Returns
18
+ -------
19
+ Python dictionary
20
+ """
21
+ return load_yaml(f"{path.dirname(__file__)}/conventions/{model}.yaml")
22
+
23
+ def load_yaml(filepath):
24
+ """Loads a yaml file as a Python dictionary.
25
+
26
+ Note: Does not yet check whether or not the format of the yaml file (or
27
+ the corresponding Python dictionary) is in an xbudget-compatible format!
28
+
29
+ Parameters
30
+ ----------
31
+ filepath : path to yaml file, as str
32
+
33
+ Returns
34
+ -------
35
+ Python dictionary
36
+ """
37
+ with open(filepath, "r") as stream:
38
+ try:
39
+ xbudget_dict = yaml.safe_load(stream)
40
+ except yaml.YAMLError as exc:
41
+ print(exc)
42
+ return xbudget_dict
xbudget/version.py ADDED
@@ -0,0 +1,3 @@
1
+ """xbudget: version information"""
2
+
3
+ __version__ = "0.5.0"
@@ -0,0 +1,38 @@
1
+ Metadata-Version: 2.4
2
+ Name: xbudget
3
+ Version: 0.5.0
4
+ Summary: Helper functions and meta-data conventions for wrangling finite-volume ocean model budgets
5
+ Project-URL: Homepage, https://github.com/hdrake/xbudget
6
+ Project-URL: Bugs/Issues/Features, https://github.com/hdrake/xbudget/issues
7
+ Author-email: "Henri F. Drake" <hfdrake@uci.edu>
8
+ License-File: LICENSE
9
+ Classifier: License :: OSI Approved :: GNU General Public License v3 (GPLv3)
10
+ Classifier: Operating System :: OS Independent
11
+ Classifier: Programming Language :: Python :: 3
12
+ Requires-Python: >=3.8
13
+ Requires-Dist: numpy
14
+ Requires-Dist: xarray
15
+ Requires-Dist: xgcm>=0.9.0
16
+ Description-Content-Type: text/markdown
17
+
18
+ # xbudget
19
+ Helper functions and meta-data conventions for wrangling finite-volume ocean model budgets.
20
+
21
+ Quick Start Guide
22
+ -----------------
23
+
24
+ **For users: minimal installation within an existing environment**
25
+ ```bash
26
+ pip install git+https://github.com/hdrake/xbudget.git@main
27
+ ```
28
+
29
+ **For developers: installing from scratch using `conda`**
30
+ ```bash
31
+ git clone git@github.com:hdrake/xbudget.git
32
+ cd xbudget
33
+ conda env create -f docs/environment.yml
34
+ conda activate docs_env_xbudget
35
+ pip install -e .
36
+ python -m ipykernel install --user --name docs_env_xbudget --display-name "docs_env_xbudget"
37
+ jupyter-lab
38
+ ```
@@ -0,0 +1,12 @@
1
+ xbudget/__init__.py,sha256=JyiQGuRTHCkpfa70e7xum_Z5VSTr1airEqR-fZJroj4,164
2
+ xbudget/collect.py,sha256=3ihE7S87OfE_k3ylpx8HzPiGSkkaIsu_Pl3-vp7DcJw,13316
3
+ xbudget/presets.py,sha256=VQKWF1CDm-ei3AwEIgulwo6UBLFaag0HFW-eYVRCuhk,1221
4
+ xbudget/version.py,sha256=XsM9KNFRxEVBi_aTXVM-KXGt0OhCm_HdvZq9o0W0DGI,58
5
+ xbudget/conventions/MOM6.yaml,sha256=mV5rRyv9wYRrEm13E7G-F1ADXjlm2Gmp4CTP9smkAQI,9807
6
+ xbudget/conventions/MOM6_3Donly.yaml,sha256=AllZ2KahJ3mXtIoOYyRvFA-wYOcfiOVs3Phw2-3CZxE,6113
7
+ xbudget/conventions/MOM6_drift.yaml,sha256=JJGh2anx0Ok54yNqFUz4ODCiWSJKqbVZL7QEEA7rZnI,1345
8
+ xbudget/conventions/MOM6_surface.yaml,sha256=qyVw9vKX2wvWck8S1VjzfKl4VhmJ1L7fsFVtNURI3cM,5840
9
+ xbudget-0.5.0.dist-info/METADATA,sha256=7Pav0qRzbTxf_oEWyX0s71eDZoYUSbBm5p9T5__1e00,1260
10
+ xbudget-0.5.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
11
+ xbudget-0.5.0.dist-info/licenses/LICENSE,sha256=e8O6mejrFQyNqfJmLJNKyjBZrH-dprkeLdqqKxnY7dE,1071
12
+ xbudget-0.5.0.dist-info/RECORD,,
@@ -0,0 +1,4 @@
1
+ Wheel-Version: 1.0
2
+ Generator: hatchling 1.27.0
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2023 Henri F. Drake
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.