diff --git a/diagnostics/example_multicase/esm_catalog_CMIP_synthetic_r1i1p1f1_gr1.json b/diagnostics/example_multicase/esm_catalog_CMIP_synthetic_r1i1p1f1_gr1.json index b9029f0f4..cd02f8f98 100644 --- a/diagnostics/example_multicase/esm_catalog_CMIP_synthetic_r1i1p1f1_gr1.json +++ b/diagnostics/example_multicase/esm_catalog_CMIP_synthetic_r1i1p1f1_gr1.json @@ -187,5 +187,5 @@ "description": null, "title": null, "last_updated": "2023-06-01", - "catalog_file": "file:/Users/jess/mdtf/MDTF-diagnostics/diagnostics/example_multicase/esm_catalog_CMIP_synthetic_r1i1p1f1_gr1.csv" + "catalog_file": "file:/home/clare/GitHub/mdtf/MDTF-diagnostics/diagnostics/example_multicase/esm_catalog_CMIP_synthetic_r1i1p1f1_gr1.csv" } \ No newline at end of file diff --git a/diagnostics/finite_amplitude_wave_diag/coordinate_utils.py b/diagnostics/finite_amplitude_wave_diag/coordinate_utils.py new file mode 100644 index 000000000..e69de29bb diff --git a/diagnostics/finite_amplitude_wave_diag/doc/finite_amplitude_wave_diag_zonal_mean.rst b/diagnostics/finite_amplitude_wave_diag/doc/finite_amplitude_wave_diag_zonal_mean.rst new file mode 100644 index 000000000..15eaaba47 --- /dev/null +++ b/diagnostics/finite_amplitude_wave_diag/doc/finite_amplitude_wave_diag_zonal_mean.rst @@ -0,0 +1,224 @@ +.. This is a comment in RestructuredText format (two periods and a space). + +.. Note that all "statements" and "paragraphs" need to be separated by a blank + line. This means the source code can be hard-wrapped to 80 columns for ease + of reading. Multi-line comments or commands like this need to be indented by + exactly three spaces. + +.. Underline with '='s to set top-level heading: + https://docutils.sourceforge.io/docs/user/rst/quickref.html#section-structure + +Finite Amplitude Rossby Wave Diagnostics Documentation +====================================================== + +.. rst-class:: center + +Clare S. Y. Huang\ |^1|, Christopher Polster |^2| and Noboru Nakamura\ |^1| + +.. rst-class:: center + +|^1|\ The University of Chicago, Chicago, Illinois + +|^2|\ Johannes Gutenberg-Universität Mainz, Germany + +.. rst-class:: center + +Last update: 03/12/2024 + +Description +----------- +For a comprehensive review of the finite-amplitude Rossby wave activity (FAWA) theory, please refer to the review article Nakamura (2024). + +This POD computes the seasonal climatologies of various finite-amplitude wave diagnostics. Each of the diagnostics captures different aspects of eddy-mean interactions. + + + + + +Physical assumptions made in FAWA framework +-------------------------------------------- + + + + + +Preprocessing of Climate Model Output +------------------------------------- + + + + + +inline :math:`\frac{ \sum_{t=0}^{N}f(t,k) }{N}` + +.. Underline with '-'s to make a second-level heading. + +Version & Contact info +---------------------- + +Here you should describe who contributed to the diagnostic, and who should be +contacted for further information: + +- Version/revision information: version 1 (03/12/2024) +- PI (name, affiliation, email): Clare S. Y. Huang (The University of Chicago, csyhuang@uchicago.edu) +- Developer/point of contact: Clare S. Y. Huang (The University of Chicago, csyhuang@uchicago.edu) +- Other contributors: Christopher Polster, Noboru Nakamura + +.. Underline with '^'s to make a third-level heading. + +Open source copyright agreement +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +The MDTF framework is distributed under the LGPLv3 license (see LICENSE.txt). +Unless you've distributed your script elsewhere, you don't need to change this. + +Functionality +------------- + +(to be filled in) + +Required programming language and libraries +------------------------------------------- + +(to be filled in) + + +Required model output variables +------------------------------- + +(to be filled in) + +References +---------- + +.. _ref-Nakamura-annual-review: + +10241. Nakamura, N. (2024). Large-Scale Eddy-Mean Flow Interaction in the Earth's Extratropical Atmosphere. *Annual Review of Fluid Mechanics*, **56**, 349-377, +`doi:10.1146/annurev-fluid-121021-035602 `__. + +.. _ref-Neal-et-al-GRL: + +10242. Neal, E., Huang, C. S., & Nakamura, N. (2022). The 2021 Pacific Northwest heat wave and associated blocking: meteorology and the role of an upstream cyclone as a diabatic source of wave activity. *Geophysical Research Letters*, **49(8)**, e2021GL097699. `doi:10.1029/2021GL097699 `__. + +.. _ref-Nakamura-Science: + +10243. Nakamura, N., & Huang, C. S. (2018). Atmospheric blocking as a traffic jam in the jet stream. *Science*, **361(6397)**, 42-47, `doi:10.1126/science.aat0721 `__. + +.. _ref-Nakamura-Solomon-JAS-2010: + +10244. Nakamura, N., & Solomon, A. (2010). Finite-amplitude wave activity and mean flow adjustments in the atmospheric general circulation. Part I: Quasigeostrophic theory and analysis. *Journal of the atmospheric sciences*, **67(12)**, 3967-3983, `doi:10.1175/2010JAS3503.1 `__. + +.. _ref-Nakamura-Solomon-JAS-2011: + +10245. Nakamura, N., & Solomon, A. (2011). Finite-amplitude wave activity and mean flow adjustments in the atmospheric general circulation. Part II: Analysis in the isentropic coordinate. Journal of the atmospheric sciences, 68(11), 2783-2799, `doi:10.1175/2011JAS3685.1 `__. + +.. _ref-Huang-Nakamura-JAS-2016: + +10246. Huang, C. S., & Nakamura, N. (2016). Local finite-amplitude wave activity as a diagnostic of anomalous weather events. Journal of the Atmospheric Sciences, 73(1), 211-229, `doi:10.1175/JAS-D-15-0194.1 `__. + +.. _ref-Huang-Nakamura-GRL-2017: + +10247. Huang, C. S., & Nakamura, N. (2017). Local wave activity budgets of the wintertime Northern Hemisphere: Implication for the Pacific and Atlantic storm tracks. Geophysical Research Letters, 44(11), 5673-5682, `doi:10.1002/2017GL073760 `__. + +More about this diagnostic +-------------------------- + +(to be filled in) + +Links to external sites +^^^^^^^^^^^^^^^^^^^^^^^ + +(to be filled in) + +More references and citations +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +(to be filled in) + +Figures +^^^^^^^ + +Images **must** be provided in either .png or .jpeg formats in order to be +displayed properly in both the html and pdf output. + +Here's the syntax for including a figure in the document: + +.. code-block:: restructuredtext + + .. _my-figure-tag: [only needed for linking to figures] + + .. figure:: [path to image file, relative to the source.rst file] + :align: left + :width: 75 % [these both need to be indented by three spaces] + + Paragraphs or other text following the figure that are indented by three + spaces are treated as a caption/legend, eg: + + - red line: a Gaussian + - blue line: another Gaussian + +which produces + +.. _my-figure-tag: + +.. figure:: gaussians.jpg + :align: left + :width: 75 % + + Paragraphs or other text following the figure that are indented by three + spaces are treated as a caption/legend, eg: + + - blue line: a Gaussian + - orange line: another Gaussian + +The tag lets you refer to figures in the text, e.g. +``:ref:`Figure 1 ``` → :ref:`Figure 1 `. + +Equations +^^^^^^^^^ + +Accented and Greek letters can be written directly using Unicode: é, Ω. +(Make sure your text editor is saving the file in UTF-8 encoding). + +Use the following syntax for superscripts and subscripts in in-line text: + +.. code-block:: restructuredtext + + W m\ :sup:`-2`\ ; CO\ :sub:`2`\ . + +which produces: W m\ :sup:`-2`\ ; CO\ :sub:`2`\ . +Note one space is needed after both forward slashes in the input; these spaces +are not included in the output. + +Equations can be written using standard +`latex `__ +(PDF link) syntax. Short equations in-line with the text can be written as +``:math:`f = 2 \Omega \sin \phi``` → :math:`f = 2 \Omega \sin \phi`. + +Longer display equations can be written as follows. Note that a blank line is +needed after the ``.. math::`` heading and after each equation, with the +exception of aligned equations. + +.. code-block:: restructuredtext + + .. math:: + + \frac{D \mathbf{u}_g}{Dt} + f_0 \hat{\mathbf{k}} \times \mathbf{u}_a &= 0; \\ + \frac{Dh}{Dt} + f \nabla_z \cdot \mathbf{u}_a &= 0, + + \text{where } \mathbf{u}_g = \frac{g}{f_0} \hat{\mathbf{k}} \times \nabla_z h. + +which produces: + +.. math:: + + \frac{D \mathbf{u}_g}{Dt} + f_0 \hat{\mathbf{k}} \times \mathbf{u}_a &= 0; \\ + \frac{Dh}{Dt} + f \nabla_z \cdot \mathbf{u}_a &= 0, + + \text{where } \mathbf{u}_g = \frac{g}{f_0} \hat{\mathbf{k}} \times \nabla_z h. + +The editor at `https://livesphinx.herokuapp.com/ +`__ can have issues formatting complicated +equations, so you may want to check its output with a latex-specific editor, +such as `overleaf `__ or other `equation editors +`__. diff --git a/diagnostics/finite_amplitude_wave_diag/doc/removed_paragraphs.rst b/diagnostics/finite_amplitude_wave_diag/doc/removed_paragraphs.rst new file mode 100644 index 000000000..947f64f95 --- /dev/null +++ b/diagnostics/finite_amplitude_wave_diag/doc/removed_paragraphs.rst @@ -0,0 +1,3 @@ +Traditional Rossby wave eddy-mean flow theory decomposes physical fields into zonal mean :math:`\overline{(...)}` and eddy :math:`(...)^\prime`. At mid-latitude, with the presumption that quasi-geostrophic (QG) approximation is valid, denote QG potential vorticity (QGPV) by :math:`q`. To the extent which the wave amplitude :math:`\alpha` is small, under conservative dynamics, the (linear) wave activity, which is a measure of wave amplitude itself, :math:`A_L \equiv \frac{1}{2} \frac{{q^\prime}^2}{\partial \bar{q}/\partial y}` obeys the Eliassen-Palm (E-P) flux relation :math:`\frac{\partial}{\partial t} A_L + \frac{1}{\rho_0} \nabla \cdot \boldsymbol{F} = \mathcal{O}(\alpha^3)`. + +When the eddy amplitude :math:`\alpha` is large, the observed zonal-mean state is modified by the eddy effects. To establish a conservation relation for finite-amplitude eddies, Nakamura and collaborators define an eddy-free "reference state" for QGPV :math:`Q_{\text{ref}}` and zonal wind :math:`u_{\text{ref}}` respectively. They represent a hypothetical flow completely devoid of eddies, which, when stirred adiabatically, would evolve into the observed state. The finite-amplitude wave activity (FAWA) defined with respect to :math:`Q_{\text{ref}}` satistfies the \ No newline at end of file diff --git a/diagnostics/finite_amplitude_wave_diag/env_otc.sh b/diagnostics/finite_amplitude_wave_diag/env_otc.sh new file mode 100644 index 000000000..3048d1ae0 --- /dev/null +++ b/diagnostics/finite_amplitude_wave_diag/env_otc.sh @@ -0,0 +1,3 @@ +export WK_DIR=/home/clare/GitHub/mdtf/wkdir +export DATADIR=/home/clare/GitHub/mdtf/inputdata/model +export CASENAME="GFDL-CM3_historical_r1i1p1" diff --git a/diagnostics/finite_amplitude_wave_diag/finite_amplitude_wave_diag.html b/diagnostics/finite_amplitude_wave_diag/finite_amplitude_wave_diag.html new file mode 100644 index 000000000..866d6fdb8 --- /dev/null +++ b/diagnostics/finite_amplitude_wave_diag/finite_amplitude_wave_diag.html @@ -0,0 +1,120 @@ + + + Finite-amplitude Rossby wave diagnostics + +

Finite-amplitude Rossby wave diagnostics

+ + +

The goal to incorporate Finite-amplitude wave activity (FAWA) formalism into MDTF repo is to provide diagnostic tools for model comparison. Each diagnostic shall associate with an interpretation related to the physical process.

+

Below are some proposed candidates and associated physical interpretations:

+ + + + + + + + + + + + + + + + + + + + + + + + + +
DiagnosticInterpretation
Seasonal climatology of zonal mean FAWAIt quantifies the strength of vertically propagating planetary waves, and also relative strength of synoptic eddies in troposphere
Seasonal climatology of UrefTo quantify strength of mean-flow interaction, i.e., adiabatic adjustment of flow from an eddy-free reference state is given by Uref-Ubar(zonal mean wind)
Seasonal climatology of <LWA> (<...> = vertically averaged)To quantify geographical distribution (and amplitude) of eddies
Seasonal climatology of temporal Covariance of <LWA> and <U> (as in NH18 Fig. 2A)A measure of strength of nonlinear wave-mean flow interaction (via nonlinear zonal wave activity flux)
+ + +Full Documentation and Contact Information + +

Zonal-mean finite-amplitude wave diagnostics for {{CASENAME}} (Climatologies)

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Name of DiagnosticDJFMAMJJASON
zonal mean zonal windplotplotplotplot
zonal mean wave activity (FAWA)plotplotplotplot
zonal mean reference states (Uref)plotplotplotplot
zonal mean wind adjustment (\Delta U)plotplotplotplot
+ + +

Vertically-averaged finite-amplitude wave diagnostics for {{CASENAME}} (Climatologies)

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Name of DiagnosticDJFMAMJJASON
Barotropic zonal mean zonal windplotplotplotplot
Barotropic local wave activityplotplotplotplot
Covariance between barotropic zonal wind and LWAplotplotplotplot
diff --git a/diagnostics/finite_amplitude_wave_diag/finite_amplitude_wave_diag_utils.py b/diagnostics/finite_amplitude_wave_diag/finite_amplitude_wave_diag_utils.py new file mode 100644 index 000000000..5bbc253c5 --- /dev/null +++ b/diagnostics/finite_amplitude_wave_diag/finite_amplitude_wave_diag_utils.py @@ -0,0 +1,223 @@ +from typing import Optional + +import gridfill +import matplotlib.pyplot as plt # python library we use to make plots +import numpy as np +import xarray as xr +from falwa.constant import P_GROUND, SCALE_HEIGHT +from matplotlib import gridspec +from cartopy import crs as ccrs + +# from diagnostics.finite_amplitude_wave_diag.finite_amplitude_wave_diag_zonal_mean import plev_name, lat_name, lon_name, \ +# sampled_dataset + + +def gridfill_each_level(lat_lon_field, itermax=1000, verbose=False): + """ + Fill missing values in lat-lon grids with values derived by solving Poisson's equation + using a relaxation scheme. + + Args: + lat_lon_field(np.ndarray): 2D array to apply gridfill on + itermax(int): maximum iteration for poisson solver + verbose(bool): verbose level of poisson solver + + Returns: + A 2D array of the same dimension with all nan filled. + """ + if np.isnan(lat_lon_field).sum() == 0: + return lat_lon_field + + lat_lon_filled, converged = gridfill.fill( + grids=np.ma.masked_invalid(lat_lon_field), xdim=1, ydim=0, eps=0.01, + cyclic=True, itermax=itermax, verbose=verbose) + + return lat_lon_filled + + +class LatLonMapPlotter(object): + def __init__(self, figsize, title_str, xgrid, ygrid, cmap, xland, yland, lon_range, lat_range): + self._figsize = figsize + self._title_str = title_str + self._xgrid = xgrid + self._ygrid = ygrid + self._cmap = cmap + self._xland = xland + self._yland = yland + self._lon_range = lon_range + self._lat_range = lat_range + + def plot_and_save_variable(self, variable, cmap, var_title_str, save_path, num_level=30): + fig = plt.figure(figsize=self._figsize) + spec = gridspec.GridSpec( + ncols=1, nrows=1, wspace=0.3, hspace=0.3) + ax = fig.add_subplot(spec[0], projection=ccrs.PlateCarree()) + ax.coastlines(color='black', alpha=0.7) + ax.set_aspect('auto', adjustable=None) + main_fig = ax.contourf( + self._xgrid, self._ygrid, + variable, + num_level, + cmap=cmap) + ax.scatter(self._xgrid[self._xland], self._ygrid[self._yland], s=1, c='gray') + ax.set_xticks(self._lon_range, crs=ccrs.PlateCarree()) + ax.set_yticks(self._lat_range, crs=ccrs.PlateCarree()) + fig.colorbar(main_fig, ax=ax) + ax.set_title(f"{self._title_str}\n{var_title_str}") + plt.savefig(save_path, bbox_inches='tight') + # plt.savefig(save_path.replace("/PS/", "/").replace(".eps", ".png"), bbox_inches='tight') # Do I need this? + plt.show() + + +class HeightLatPlotter(object): + def __init__(self, figsize, title_str, xgrid, ygrid, cmap, xlim): + self._figsize = figsize + self._title_str = title_str + self._xgrid = xgrid + self._ygrid = ygrid + self._cmap = cmap + self._xlim = xlim # [-80, 80] + + def plot_and_save_variable(self, variable, cmap, var_title_str, save_path, num_level=30): + fig = plt.figure(figsize=self._figsize) + spec = gridspec.GridSpec(ncols=1, nrows=1) + ax = fig.add_subplot(spec[0]) + # *** Zonal mean U *** + main_fig = ax.contourf( + self._xgrid, + self._ygrid, + variable, + num_level, + cmap=cmap if cmap else self._cmap) + fig.colorbar(main_fig, ax=ax) + ax.set_title(f"{self._title_str}\n{var_title_str}") + ax.set_xlim(self._xlim) + plt.tight_layout() + plt.savefig(save_path, bbox_inches='tight') + # plt.savefig(save_path.replace("/PS/", "/").replace(".eps", ".png"), bbox_inches='tight') # Do I need this? + plt.show() + + +def convert_pseudoheight_to_hPa(height_array): + """ + Args: + height_array(np.array): pseudoheight in [m] + + Returns: + np.array which contains pressure levels in [hPa] + """ + p_array = P_GROUND * np.exp(- height_array / SCALE_HEIGHT) + return p_array + + +def convert_hPa_to_pseudoheight(p_array): + """ + Args: + height_array(np.array): pseudoheight in [m] + + Returns: + np.array which contains pressure levels in [hPa] + """ + height_array = - SCALE_HEIGHT * np.log(p_array / P_GROUND) + return height_array + + +class DataPreprocessor: + def __init__( + self, wk_dir, xlon, ylat, u_var_name, v_var_name, t_var_name, plev_name, lat_name, lon_name, time_coord_name): + + self._wk_dir = wk_dir + self._xlon: np.array = xlon # user input + self._ylat: np.array = ylat # user input + self._u_var_name: str = u_var_name + self._v_var_name: str = v_var_name + self._t_var_name: str = t_var_name + self._plev_name: str = plev_name + self._lat_name: str = lat_name + self._lon_name: str = lon_name + self._original_plev = None + self._original_lat = None + self._original_lon = None + self._original_time_coord = None + self._time_coord_name: str = time_coord_name + self._new_time_coord_name: str = "day" + self._sampled_dataset = None # Shall be xarray. Set type later + self._gridfill_needed: Optional[bool] = None + self._yz_mask = None + self._xy_mask = None + + @property + def xy_mask(self): + return self._xy_mask + + @property + def yz_mask(self): + return self._yz_mask + + def _save_original_coordinates(self, dataset): + self._original_plev = dataset.coords[self._plev_name] + self._original_lat = dataset.coords[self._lat_name] + self._original_lon = dataset.coords[self._lon_name] + + def _check_if_gridfill_is_needed(self, sampled_dataset): + num_of_nan = sampled_dataset[self._u_var_name].isnull().sum().values + if num_of_nan > 0: + self._gridfill_needed = True + self._do_save_mask(sampled_dataset) + else: + self._gridfill_needed = False + + def _do_save_mask(self, dataset): + self._yz_mask = dataset[self._u_var_name]\ + .to_masked_array().mask.sum(axis=0).sum(axis=-1).astype(bool) + self._xy_mask = dataset[self._u_var_name]\ + .to_masked_array().mask[:, 1:, :, :].sum(axis=0).sum(axis=0).astype(bool) + + def _save_preprocessed_data(self, dataset, output_path): + dataset.to_netcdf(output_path) + dataset.close() + print(f"Finished outputing preprocessed dataset: {output_path}") + + def _interpolate_onto_regular_grid(self, dataset): + dataset = dataset.interp( + coords={self._lat_name: self._ylat, self._lon_name: self._xlon}, + method="linear", + kwargs={"fill_value": "extrapolate"}) + return dataset + + def _implement_gridfill(self, dataset: xr.Dataset): + if not self._gridfill_needed: + print("No NaN values detected. Gridfill not needed. Bypass DataPreprocessor._implement_gridfill.") + return dataset + # *** Implement gridfill procedure *** + print(f"self._gridfill_needed = True. Do gridfill with poisson solver.") + args_tuple = [self._u_var_name, self._v_var_name, self._t_var_name] + gridfill_file_path = self._wk_dir + "/model/netCDF/gridfill_{var}.nc" + for var_name in args_tuple: + field_at_all_level = xr.apply_ufunc( + gridfill_each_level, + *[dataset[var_name]], + input_core_dims=((self._lat_name, self._lon_name),), + output_core_dims=((self._lat_name, self._lon_name),), + vectorize=True, dask="allowed") + field_at_all_level.to_netcdf(gridfill_file_path.format(var=var_name)) + field_at_all_level.close() + print(f"Finished outputing {var_name} to {gridfill_file_path.format(var=var_name)}") + load_gridfill_path = gridfill_file_path.format(var="*") + return load_gridfill_path + + def output_preprocess_data(self, sampled_dataset, output_path): + """ + Main procedure executed by this class + """ + self._save_original_coordinates(sampled_dataset) + self._check_if_gridfill_is_needed(sampled_dataset) + gridfill_path = self._implement_gridfill(sampled_dataset) + gridfilled_dataset = xr.open_mfdataset(gridfill_path) + dataset = self._interpolate_onto_regular_grid(gridfilled_dataset) # Interpolate onto regular grid + gridfilled_dataset.close() + self._save_preprocessed_data(dataset, output_path) # Save preprocessed data + dataset.close() + + + diff --git a/diagnostics/finite_amplitude_wave_diag/finite_amplitude_wave_diag_zonal_mean.py b/diagnostics/finite_amplitude_wave_diag/finite_amplitude_wave_diag_zonal_mean.py new file mode 100644 index 000000000..367555dd7 --- /dev/null +++ b/diagnostics/finite_amplitude_wave_diag/finite_amplitude_wave_diag_zonal_mean.py @@ -0,0 +1,323 @@ +# Finite-amplitude Rossby wave POD +# ================================================================================ +# Calculate finite-amplitude wave diagnostics that quantifies wave-mean flow +# interactions. +# +# Last update: 03/18/2024 +# ================================================================================ +# Version & Contact info +# +# - Version/revision information: version 1 (09/07/2023) +# - PI: Clare S. Y. Huang. The University of Chicago. csyhuang@uchicago.edu. +# - Developer/point of contact (name, affiliation, email): (same as PI) +# - Other contributors: Christopher Polster (JGU Mainz), Noboru Nakamura (UChicago) +# ================================================================================ +# Open source copyright agreement +# +# The MDTF framework is distributed under the LGPLv3 license (see LICENSE.txt). +# ================================================================================ +# Functionality (not written yet) +# ================================================================================ +# Required programming language and libraries (not written yet) +# ================================================================================ +# Required model output variables (not written yet) +# ================================================================================ +# References (not written yet) +# ================================================================================ +import os +import gc +import socket +from collections import namedtuple +import matplotlib +from finite_amplitude_wave_diag_utils import convert_hPa_to_pseudoheight, DataPreprocessor, LatLonMapPlotter, \ + HeightLatPlotter + +# Commands to load third-party libraries. Any code you don't include that's +# not part of your language's standard library should be listed in the +# settings.jsonc file. +from typing import Dict +import numpy as np +import xarray as xr # python library we use to read netcdf files +from falwa.xarrayinterface import QGDataset +from falwa.oopinterface import QGFieldNH18 +from falwa.constant import P_GROUND, SCALE_HEIGHT + +if socket.gethostname() == 'otc': + matplotlib.use('Agg') # non-X windows backend + +# 1) Loading model data files: +# +# The framework copies model data to a regular directory structure of the form +# //...nc +# Here and frequency are requested in the "varlist" part of +# settings.json. +already_done_gridfill: bool = True +load_environ: bool = (socket.gethostname() == 'otc') +frequency = "day" # TODO: change later + +if socket.gethostname() == 'otc': + matplotlib.use('Agg') # non-X windows backend + +if load_environ: # otc path + print( + f""" + Start running on OTC. Print out all environment variables: + {os.environ} + """) + wk_dir = os.environ["WK_DIR"] + uvt_path = f"{os.environ['DATADIR']}/{frequency}/{os.environ['CASENAME']}.[uvt]a.{frequency}.nc" + casename = os.environ["CASENAME"] +else: # iMac path + wk_dir = "/Users/claresyhuang/Dropbox/GitHub/hn2016_falwa/github_data_storage" + uvt_path = f"{os.environ['HOME']}/Dropbox/GitHub/mdtf/MDTF-diagnostics/diagnostics/finite_amplitude_wave_diag/" + \ + "GFDL-CM3_historical_r1i1p1_20050101-20051231_10tslice.nc" + casename = "GFDL-CM3_historical_r1i1p1" + +print( + f""" + wk_dir = {wk_dir} + uvt_path = {uvt_path} + casename = {casename} + """) + +# *** Coordinates of input dataset *** +u_var_name = "ua" +v_var_name = "va" +t_var_name = "ta" +time_coord_name = "time" +plev_name = "plev" +lat_name = "lat" +lon_name = "lon" + +# *** Regular analysis grid defined by developer *** +xlon = np.arange(0, 360, 1.0) +ylat = np.arange(-90, 91, 1.0) + +# 2) Doing computations: +model_dataset = xr.open_mfdataset(uvt_path) # command to load the netcdf file +firstyr = model_dataset.coords['time'].values[0].year +lastyr = model_dataset.coords['time'].values[-1].year +if model_dataset[plev_name].units == 'Pa': # Pa shall be divided by 100 to become hPa + print("model_dataset[plev_name].units == 'Pa'. Convert it to hPa.") + model_dataset = model_dataset.assign_coords({plev_name: model_dataset[plev_name] // 100}) + model_dataset[plev_name].attrs["units"] = 'hPa' +print(f""" + Use xlon: {xlon} + Use ylat: {ylat} + firstyr, lastyr = {firstyr}, {lastyr} + """) + +# === 2.0) Save original grid === +original_grid = { + time_coord_name: model_dataset.coords[time_coord_name], + plev_name: model_dataset.coords[plev_name], + lat_name: model_dataset.coords[lat_name], + lon_name: model_dataset.coords[lon_name]} + + +def compute_from_sampled_data(gridfilled_dataset: xr.Dataset): + + # === 2.3) VERTICAL RESOLUTION: determine the maximum pseudo-height this calculation can handle === + dz = 1000 # TODO Variable to set earlier? + hmax = -SCALE_HEIGHT * np.log(gridfilled_dataset[plev_name].min() / P_GROUND) + kmax = int(hmax // dz) + 1 + original_pseudoheight = convert_hPa_to_pseudoheight(original_grid[plev_name]).rename("height") + + # === 2.4) WAVE ACTIVITY COMPUTATION: Compute Uref, FAWA, barotropic components of u and LWA === + qgds = QGDataset( + gridfilled_dataset, + var_names={"u": u_var_name, "v": v_var_name, "t": t_var_name}, + qgfield=QGFieldNH18, + qgfield_kwargs={"dz": dz, "kmax": kmax}) + gridfilled_dataset.close() + # Compute reference states and LWA + qgds.interpolate_fields(return_dataset=False) + qgds.compute_reference_states(return_dataset=False) + qgds.compute_lwa_and_barotropic_fluxes(return_dataset=False) + output_dataset = xr.Dataset(data_vars={ + 'uref': qgds.uref, + 'zonal_mean_u': qgds.interpolated_u.mean(axis=-1), + 'zonal_mean_lwa': qgds.lwa.mean(axis=-1), + 'lwa_baro': qgds.lwa_baro, + 'u_baro': qgds.u_baro}).interp(coords={ + "xlon": (lon_name, original_grid[lon_name].data), + "ylat": (lat_name, original_grid[lat_name].data)}) + return output_dataset + + +def calculate_covariance(lwa_baro, u_baro): + """ + Calculate covariance. + Args: + lwa_baro: dataset.lwa_baro + u_baro: dataset.u_baro + Returns: + cov_map in dimension of (lat, lon) + """ + baro_matrix_shape = lwa_baro.data.shape + flatten_lwa_baro = lwa_baro.data.reshape(baro_matrix_shape[0], baro_matrix_shape[1] * baro_matrix_shape[2]) + flatten_u_baro = u_baro.data.reshape(baro_matrix_shape[0], baro_matrix_shape[1] * baro_matrix_shape[2]) + covv = np.cov(m=flatten_lwa_baro, y=flatten_u_baro, rowvar=False) + row_cov = np.diagonal(covv, offset=baro_matrix_shape[1] * baro_matrix_shape[2]) + cov_map = row_cov.reshape(baro_matrix_shape[1], baro_matrix_shape[2]) + return cov_map + + +def time_average_processing(dataset: xr.Dataset): + SeasonalAverage = namedtuple( + "SeasonalAverage", [ + "zonal_mean_u", + "uref", + "zonal_mean_lwa", + "lwa_baro", + "u_baro", + "covariance_lwa_u_baro"]) + + seasonal_avg_zonal_mean_u = dataset.zonal_mean_u.mean(axis=0) + seasonal_avg_zonal_mean_lwa = dataset.zonal_mean_lwa.mean(axis=0) + seasonal_avg_uref = dataset.uref.mean(axis=0) + seasonal_avg_lwa_baro = dataset.lwa_baro.mean(axis=0) + seasonal_avg_u_baro = dataset.u_baro.mean(axis=0) + seasonal_covariance_lwa_u_baro = calculate_covariance(lwa_baro=dataset.lwa_baro, u_baro=dataset.u_baro) + seasonal_avg_data = SeasonalAverage( + seasonal_avg_zonal_mean_u, seasonal_avg_uref, seasonal_avg_zonal_mean_lwa, + seasonal_avg_lwa_baro, seasonal_avg_u_baro, seasonal_covariance_lwa_u_baro) + return seasonal_avg_data + + +def plot_and_save_figure(seasonal_average_data, analysis_height_array, plot_dir, title_str, season, + xy_mask=None, yz_mask=None): + if xy_mask is None: + xy_mask = np.zeros_like(seasonal_average_data.u_baro) + yland, xland = [], [] + else: + yland, xland = np.where(xy_mask) + if yz_mask is None: + yz_mask = np.zeros_like(seasonal_average_data.zonal_mean_u) + lon_range = np.arange(-180, 181, 60) + lat_range = np.arange(-90, 91, 30) + + cmap = "jet" + + height_lat_plotter = HeightLatPlotter(figsize=(4, 4), title_str=title_str, xgrid=original_grid['lat'], + ygrid=analysis_height_array, cmap=cmap, xlim=[-80, 80]) + height_lat_plotter.plot_and_save_variable(variable=seasonal_average_data.zonal_mean_u, cmap=cmap, + var_title_str='zonal mean U', + save_path=f"{plot_dir}{season}_zonal_mean_u.eps", num_level=30) + height_lat_plotter.plot_and_save_variable(variable=seasonal_average_data.zonal_mean_lwa, cmap=cmap, + var_title_str='zonal mean LWA', + save_path=f"{plot_dir}{season}_zonal_mean_lwa.eps", num_level=30) + height_lat_plotter.plot_and_save_variable(variable=seasonal_average_data.uref, cmap=cmap, + var_title_str='zonal mean Uref', + save_path=f"{plot_dir}{season}_zonal_mean_uref.eps", num_level=30) + height_lat_plotter.plot_and_save_variable(variable=seasonal_average_data.zonal_mean_u - seasonal_average_data.uref, + cmap=cmap, var_title_str='zonal mean $\Delta$ U', + save_path=f"{plot_dir}{season}_zonal_mean_delta_u.eps", num_level=30) + + # Use encapsulated class to plot + lat_lon_plotter = LatLonMapPlotter(figsize=(6, 3), title_str=title_str, xgrid=original_grid['lon'], + ygrid=original_grid['lat'], cmap=cmap, xland=xland, yland=yland, + lon_range=lon_range, lat_range=lat_range) + lat_lon_plotter.plot_and_save_variable(variable=seasonal_average_data.u_baro, cmap=cmap, var_title_str='U baro', + save_path=f"{plot_dir}{season}_u_baro.eps", num_level=30) + lat_lon_plotter.plot_and_save_variable(variable=seasonal_average_data.lwa_baro, cmap=cmap, var_title_str='LWA baro', + save_path=f"{plot_dir}{season}_lwa_baro.eps", num_level=30) + lat_lon_plotter.plot_and_save_variable(variable=seasonal_average_data.covariance_lwa_u_baro, cmap="Purples_r", + var_title_str='Covariance between LWA and U(baro)', + save_path=f"{plot_dir}{season}_u_lwa_covariance.eps", num_level=30) + + +# === 3) Saving output data === +# Diagnostics should write output data to disk to a) make relevant results +# available to the user for further use or b) to pass large amounts of data +# between stages of a calculation run as different sub-scripts. Data can be in +# any format (as long as it's documented) and should be written to the +# directory /model/netCDF (created by the framework). + +# *** MAIN PROCESS: Produce data by season, daily *** +model_or_obs: str = "model" # It can be "model" or "obs" +season_to_months = [ + ("DJF", [1, 2, 12]), ("MAM", [3, 4, 5]), ("JJA", [6, 7, 8]), ("SON", [9, 10, 11])] +intermediate_output_paths: Dict[str, str] = { + item[0]: f"{wk_dir}/{model_or_obs}/intermediate_{item[0]}.nc" for item in season_to_months} + +for season, selected_months in season_to_months: + print(f"season: {season}") + # Construct data preprocessor + data_preprocessor = DataPreprocessor( + wk_dir=wk_dir, xlon=xlon, ylat=ylat, u_var_name=u_var_name, v_var_name=v_var_name, t_var_name=t_var_name, + plev_name=plev_name, lat_name=lat_name, lon_name=lon_name, time_coord_name=time_coord_name) + + plot_dir = f"{wk_dir}/{model_or_obs}/PS/" + + # Do temporal sampling to reduce the data size + print("Start samping data in frequency 'day'.") + sampled_dataset = model_dataset.where( + model_dataset.time.dt.month.isin(selected_months), drop=True) \ + .groupby("time.day").first(skipna=False) + preprocessed_output_path = intermediate_output_paths[season] # TODO set it + print(f"Start preparing intermediate data in the directory: {preprocessed_output_path}") + data_preprocessor.output_preprocess_data( + sampled_dataset=sampled_dataset, output_path=preprocessed_output_path) + print(f"Finished preparing intermediate data in the directory: {preprocessed_output_path}") + intermediate_dataset = xr.open_mfdataset(preprocessed_output_path) + print(f"Start computing FAWA diagnostics from sampled data.") + fawa_diagnostics_dataset = compute_from_sampled_data(intermediate_dataset) + analysis_height_array = fawa_diagnostics_dataset.coords['height'].data + seasonal_avg_data = time_average_processing(fawa_diagnostics_dataset) + print( + f""" + Finished computing FAWA diagnostics from sampled data. + fawa_diagnostics_dataset: {fawa_diagnostics_dataset} + seasonal_avg_data: {seasonal_avg_data} + """) + + # === 4) Saving output plots === + # + # Plots should be saved in EPS or PS format at //PS + # (created by the framework). Plots can be given any filename, but should have + # the extension ".eps" or ".ps". To make the webpage output, the framework will + # convert these to bitmaps with the same name but extension ".png". + + # Define a python function to make the plot, since we'll be doing it twice and + # we don't want to repeat ourselves. + + # set an informative title using info about the analysis set in env vars + title_string = f"{casename} ({firstyr}-{lastyr}) {season}" + # Plot the model data: + plot_and_save_figure( + seasonal_average_data=seasonal_avg_data, + analysis_height_array=analysis_height_array, + plot_dir=plot_dir, + title_str=title_string, + season=season, + xy_mask=data_preprocessor.xy_mask, + yz_mask=data_preprocessor.yz_mask) + print(f"Finishing outputting figures to {plot_dir}.") + + # Close xarray datasets + sampled_dataset.close() + intermediate_dataset.close() + fawa_diagnostics_dataset.close() + gc.collect() +print("Finish the whole process") +model_dataset.close() + +# 6) Cleaning up: +# +# In addition to your language's normal housekeeping, don't forget to delete any +# temporary/scratch files you created in step 4). +# os.system(f"rm -f {wk_dir}/model/gridfill_*.nc") +# os.system(f"rm -f {wk_dir}/model/intermediate_*.nc") + +# 7) Error/Exception-Handling Example ######################################## +# nonexistent_file_path = "{DATADIR}/mon/nonexistent_file.nc".format(**os.environ) +# try: +# nonexistent_dataset = xr.open_dataset(nonexistent_file_path) +# except IOError as error: +# print(error) +# print("This message is printed by the example POD because exception-handling is working!") + +# 8) Confirm POD executed sucessfully ######################################## +print("POD Finite-amplitude wave diagnostic (zonal mean) finished successfully!") + diff --git a/diagnostics/finite_amplitude_wave_diag/settings.jsonc b/diagnostics/finite_amplitude_wave_diag/settings.jsonc new file mode 100644 index 000000000..47cef4ee9 --- /dev/null +++ b/diagnostics/finite_amplitude_wave_diag/settings.jsonc @@ -0,0 +1,43 @@ +{ + "settings":{ + "driver":"finite_amplitude_wave_diag_zonal_mean.py", + "long_name":"Finite-amplitude Rossby Wave Diagnostics (zonal-mean version)", + "convention": "GFDL", + "realm" : "atmos", + "description":"Finite-amplitude Rossby Wave Diagnostics (zonal-mean version)", + "runtime_requirements":{ + "python3":[ + "matplotlib", + "xarray", + "netCDF4", + "numpy", + "cartopy", + "falwa" + ] + } + }, + "data":{"frequency":"day"}, + "dimensions":{ + "lat": {"standard_name": "latitude"}, + "lon": {"standard_name": "longitude"}, + "plev":{"standard_name":"air_pressure", "units":"Pa", "positive":"down", "axis":"Z"}, + "time": {"standard_name": "time"} + }, + "varlist":{ + "ua":{ + "standard_name":"eastward_wind", + "units":"m s-1", + "dimensions":["time", "plev", "lat", "lon"], + "freq":"day"}, + "va":{ + "standard_name":"northward_wind", + "units":"m s-1", + "dimensions":["time", "plev", "lat", "lon"], + "freq":"day"}, + "ta":{ + "standard_name":"air_temperature", + "units":"K", + "dimensions":["time", "plev", "lat", "lon"], + "freq":"day"} + } +} \ No newline at end of file diff --git a/src/conda/env_finite_amplitude_wave_diag.yml b/src/conda/env_finite_amplitude_wave_diag.yml new file mode 100644 index 000000000..896ece9bf --- /dev/null +++ b/src/conda/env_finite_amplitude_wave_diag.yml @@ -0,0 +1,19 @@ +name: _MDTF_finite_amplitude_wave_diag +channels: +- conda-forge +dependencies: +- python=3.10 +- numpy=1.22.3 +- scipy=1.9 +- netCDF4=1.5.8 +- xarray=2023.2.0 +- cartopy=0.21.0 +- matplotlib=3.5.3 +- pytest==7.4.0 +- pip=22.0.4 +- dask +- gridfill +- bottleneck +- pip +- pip: + - falwa==1.2.1 diff --git a/src/default_finite_amplitude_wave_diag.jsonc b/src/default_finite_amplitude_wave_diag.jsonc new file mode 100644 index 000000000..3512eb229 --- /dev/null +++ b/src/default_finite_amplitude_wave_diag.jsonc @@ -0,0 +1,128 @@ +// Configuration for MDTF-diagnostics driver script self-test. +// +// Copy this file and customize the settings as needed to run the framework on +// your own model output without repeating command-line options. Pass it to the +// framework at the end of the command line (positionally) or with the +// -f/--input-file flag. Any other explicit command line options will override +// what's listed here. +// +// All text to the right of an unquoted "//" is a comment and ignored, as well +// as blank lines (JSONC quasi-standard.) +{ + "case_list" : [ + // The cases below correspond to the different sample model data sets. Note + // that the MDTF package does not currently support analyzing multiple + // models in a single invocation. Comment out or delete the first entry and + // uncomment the second to run NOAA-GFDL-AM4 only for the MJO_prop_amp POD, + // and likewise for the SM_ET_coupling POD. + { + "CASENAME" : "GFDL-CM3_historical_r1i1p1", + "model" : "CMIP", + "convention" : "CMIP", + "FIRSTYR" : 2005, + "LASTYR" : 2005, + "pod_list": [ + // Optional: PODs to run for this model only (defaults to all) + "finite_amplitude_wave_diag" + ] + } + // { + // "CASENAME" : "GFDL.CM4.c96L32.am4g10r8", + // "model" : "AM4", + // "convention" : "GFDL", + // "FIRSTYR" : 1, + // "LASTYR" : 10, + // "pod_list" : ["MJO_prop_amp"] + // } + // { + // "CASENAME" : "Lmon_GISS-E2-H_historical_r1i1p1", + // "model" : "CMIP", + // "convention" : "CMIP", + // "FIRSTYR" : 1951, + // "LASTYR" : 2005, + // "pod_list" : ["SM_ET_coupling"] + // } + // { + // "CASENAME" : "NCAR-CAM5.timeslice", + // "model" : "CESM", + // "convention" : "CMIP", + // "FIRSTYR" : 2000, + // "LASTYR" : 2004, + // "pod_list": ["example"] + // } + ], + // PATHS --------------------------------------------------------------------- + // Location of supporting data downloaded when the framework was installed. + + // If a relative path is given, it's resolved relative to the MDTF-diagnostics + // code directory. Environment variables (eg, $HOME) can be referenced with a + // "$" and will be expended to their current values when the framework runs. + + // Parent directory containing observational data used by individual PODs. + "OBS_DATA_ROOT": "../inputdata/obs_data/", + + // Parent directory containing results from different models. + "MODEL_DATA_ROOT": "../inputdata/model/", + + // Working directory. Defaults to OUTPUT_DIR if blank. + "WORKING_DIR": "../wkdir", + + // Directory to write output. The results of each run of the framework will be + // put in a subdirectory of this directory. + "OUTPUT_DIR": "../wkdir", + + // Location of the Anaconda/miniconda or micromamba installation to use for managing + // dependencies (path returned by running `[conda | micromamba] info`.) If empty, + // framework will attempt to determine location of system's conda installation. + "conda_root": "$HOME/miniconda3", + + // Location of micromamba executable if using micromamba + "micromamba_exe":"", + + // Directory containing the framework-specific conda environments. This should + // be equal to the "--env_dir" flag passed to conda_env_setup.sh. If left + // blank, the framework will look for its environments in the system default + // location. + "conda_env_root": "$HOME/miniconda3/envs", + + // SETTINGS ------------------------------------------------------------------ + // Any command-line option recognized by the mdtf script (type `mdtf --help`) + // can be set here, in the form "flag name": "desired setting". + + // Method used to fetch model data. + "data_manager": "Local_File", + + // Type of data that POD(s) will analyze + // "single_run" (default) or "multi_run" + "data_type": "single_run", + + // Method used to manage dependencies. + "environment_manager": "Conda", + + // Settings affecting what output is generated: + + // Set to true to have PODs save postscript figures in addition to bitmaps. + "save_ps": true, + + // Set to true to have PODs save netCDF files of processed data. + "save_nc": true, + + // Set to true to save HTML and bitmap plots in a .tar file. + "make_variab_tar": true, + + // Set to true to overwrite results in OUTPUT_DIR; otherwise results saved + // under a unique name. + "overwrite": false, + + // Settings used in debugging: + + // Log verbosity level. + "verbose": 2, + + // Set to true for framework test. Data is fetched but PODs are not run. + "test_mode": false, + + // Set to true for framework test. No external commands are run and no remote + // data is copied. Implies test_mode. + "dry_run": false +} diff --git a/src/default_tests.jsonc b/src/default_tests.jsonc new file mode 100644 index 000000000..28825a0b9 --- /dev/null +++ b/src/default_tests.jsonc @@ -0,0 +1,138 @@ +// Configuration for MDTF-diagnostics driver script self-test. +// +// Copy this file and customize the settings as needed to run the framework on +// your own model output without repeating command-line options. Pass it to the +// framework at the end of the command line (positionally) or with the +// -f/--input-file flag. Any other explicit command line options will override +// what's listed here. +// +// All text to the right of an unquoted "//" is a comment and ignored, as well +// as blank lines (JSONC quasi-standard.) +{ + "case_list" : [ + // The cases below correspond to the different sample model data sets. Note + // that the MDTF package does not currently support analyzing multiple + // models in a single invocation. Comment out or delete the first entry and + // uncomment the second to run NOAA-GFDL-AM4 only for the MJO_prop_amp POD, + // and likewise for the SM_ET_coupling POD. + { + "CASENAME" : "QBOi.EXP1.AMIP.001", + "model" : "CESM", + "convention" : "CESM", + "FIRSTYR" : 1977, + "LASTYR" : 1981, + "pod_list": [ + // Optional: PODs to run for this model only (defaults to all) + "Wheeler_Kiladis", + "EOF_500hPa", + "MJO_suite", + "MJO_teleconnection" + // "convective_transition_diag", + // "precip_diurnal_cycle", + // "ocn_surf_flux_diag", + // "mixed_layer_depth", + // "tropical_pacific_sea_level", + // "temp_extremes_distshape", + // "precip_buoy_diag" + ] + } + // { + // "CASENAME" : "GFDL.CM4.c96L32.am4g10r8", + // "model" : "AM4", + // "convention" : "GFDL", + // "FIRSTYR" : 1, + // "LASTYR" : 10, + // "pod_list" : ["MJO_prop_amp"] + // } + // { + // "CASENAME" : "Lmon_GISS-E2-H_historical_r1i1p1", + // "model" : "CMIP", + // "convention" : "CMIP", + // "FIRSTYR" : 1951, + // "LASTYR" : 2005, + // "pod_list" : ["SM_ET_coupling"] + // } + // { + // "CASENAME" : "NCAR-CAM5.timeslice", + // "model" : "CESM", + // "convention" : "CMIP", + // "FIRSTYR" : 2000, + // "LASTYR" : 2004, + // "pod_list": ["example"] + // } + ], + // PATHS --------------------------------------------------------------------- + // Location of supporting data downloaded when the framework was installed. + + // If a relative path is given, it's resolved relative to the MDTF-diagnostics + // code directory. Environment variables (eg, $HOME) can be referenced with a + // "$" and will be expended to their current values when the framework runs. + + // Parent directory containing observational data used by individual PODs. + "OBS_DATA_ROOT": "../inputdata/obs_data/", + + // Parent directory containing results from different models. + "MODEL_DATA_ROOT": "../inputdata/model/", + + // Working directory. Defaults to OUTPUT_DIR if blank. + "WORKING_DIR": "../wkdir", + + // Directory to write output. The results of each run of the framework will be + // put in a subdirectory of this directory. + "OUTPUT_DIR": "../wkdir", + + // Location of the Anaconda/miniconda or micromamba installation to use for managing + // dependencies (path returned by running `[conda | micromamba] info`.) If empty, + // framework will attempt to determine location of system's conda installation. + "conda_root": "$HOME/miniconda3", + + // Location of micromamba executable if using micromamba + "micromamba_exe":"", + + // Directory containing the framework-specific conda environments. This should + // be equal to the "--env_dir" flag passed to conda_env_setup.sh. If left + // blank, the framework will look for its environments in the system default + // location. + "conda_env_root": "$HOME/miniconda3/envs", + + // SETTINGS ------------------------------------------------------------------ + // Any command-line option recognized by the mdtf script (type `mdtf --help`) + // can be set here, in the form "flag name": "desired setting". + + // Method used to fetch model data. + "data_manager": "Local_File", + + // Type of data that POD(s) will analyze + // "single_run" (default) or "multi_run" + "data_type": "single_run", + + // Method used to manage dependencies. + "environment_manager": "Conda", + + // Settings affecting what output is generated: + + // Set to true to have PODs save postscript figures in addition to bitmaps. + "save_ps": false, + + // Set to true to have PODs save netCDF files of processed data. + "save_nc": false, + + // Set to true to save HTML and bitmap plots in a .tar file. + "make_variab_tar": true, + + // Set to true to overwrite results in OUTPUT_DIR; otherwise results saved + // under a unique name. + "overwrite": false, + + // Settings used in debugging: + + // Log verbosity level. + "verbose": 1, + + // Set to true for framework test. Data is fetched but PODs are not run. + "test_mode": false, + + // Set to true for framework test. No external commands are run and no remote + // data is copied. Implies test_mode. + "dry_run": false +} diff --git a/templates/runtime_config.jsonc b/templates/runtime_config.jsonc index 0eb2f095f..9131aa665 100755 --- a/templates/runtime_config.jsonc +++ b/templates/runtime_config.jsonc @@ -67,12 +67,12 @@ // Location of the Anaconda/miniconda or micromamba installation to use for managing // dependencies (path returned by running `conda info --base` or `micromamba info`.) - "conda_root": "", + "conda_root": "/home/clare/miniconda3", // Directory containing the framework-specific conda environments. This should // be equal to the "--env_dir" flag passed to conda_env_setup.sh. If left // blank, the framework will look for its environments in conda_root/envs - "conda_env_root": "", + "conda_env_root": "/home/clare/miniconda3/envs", // Location of the micromamba executable. Required if using micromamba "micromamba_exe": "", diff --git a/templates/runtime_config_finite_amplitude_wave_diag.jsonc b/templates/runtime_config_finite_amplitude_wave_diag.jsonc new file mode 100755 index 000000000..f346203df --- /dev/null +++ b/templates/runtime_config_finite_amplitude_wave_diag.jsonc @@ -0,0 +1,109 @@ +// This a template for configuring MDTF to run PODs that analyze multi-run/ensemble data +// +// Copy this file, rename it, and customize the settings as needed +// Pass your file to the framework using the -f/--input-file flag. +// Any other explicit command line options will override what's listed here. +// +// All text to the right of an unquoted "//" is a comment and ignored, as well +// as blank lines (JSONC quasi-standard.) +// +// Remove your test config file, or any changes you make to this template if you do not rename it, +// from your remote repository before you submit a PR for review. +// To generate CMIP synthetic data in the example dataset, run the following: +// > mamba env create --force -q -f ./src/conda/_env_synthetic_data.yml +// > conda activate _MDTF_synthetic_data +// > pip install mdtf-test-data +// > cd /mdtf +// > mkdir mdtf_test_data && cd mdtf_test_data +// > mdtf_synthetic.py -c CMIP --startyear 1980 --nyears 5 +// > mdtf_synthetic.py -c CMIP --startyear 1985 --nyears 5 +// Note that MODEL_DATA_ROOT assumes that mdtf_test_data is one directory above MDTF-diagnostics +// in this sample config file +{ + // Run each ensemble on the example POD. + // Add other PODs that work on ensemble datasets to the pod_list as needed + "pod_list" : [ + //"example" + "finite_amplitude_wave_diag" + ], + // Each case corresponds to a different simulation/output dataset + // startdate, enddate: either YYYY-MM-DD, YYYYMMDD:HHMMSS, or YYYY-MM-DD:HHMMSS + "case_list": + { + "GFDL-CM3_historical_r1i1p1": + { + "model": "test", + "convention": "CMIP", + "startdate": "19800101000000", + "enddate": "19801231000000" + } + }, + // PATHS --------------------------------------------------------------------- + // Location of supporting data downloaded when the framework was installed. + // If a relative path is given, it's resolved relative to the MDTF-diagnostics + // code directory. Environment variables (eg, $HOME) can be referenced with a + // "$" and will be expended to their current values when the framework runs. + // Full or relative path to model data ESM-intake catalog header file + + "DATA_CATALOG": "./diagnostics/example_multicase/esm_catalog_CMIP_synthetic_r1i1p1f1_gr1.json", + + // Parent directory containing observational data used by individual PODs. + "OBS_DATA_ROOT": "../inputdata/obs_data", + + // Working directory. + "WORK_DIR": "../wkdir", + + // Directory to write output. The results of each run of the framework will be + // put in a subdirectory of this directory. Defaults to WORKING_DIR if blank. + "OUTPUT_DIR": "../wkdir", + + // Location of the Anaconda/miniconda or micromamba installation to use for managing + // dependencies (path returned by running `conda info --base` or `micromamba info`.) + "conda_root": "/home/clare/miniconda3", + + // Directory containing the framework-specific conda environments. This should + // be equal to the "--env_dir" flag passed to conda_env_setup.sh. If left + // blank, the framework will look for its environments in conda_root/envs + "conda_env_root": "/home/clare/miniconda3/envs", + + // Location of the micromamba executable. Required if using micromamba + "micromamba_exe": "", + + // SETTINGS ------------------------------------------------------------------ + // Any command-line option recognized by the mdtf script (type `mdtf --help`) + // can be set here, in the form "flag name": "desired setting". + + // Settings affecting what output is generated: + // Set to true to run the preprocessor; default true: + "run_pp": true, + // Set to true to perform data translation; default false: + "translate_data": true, + // Set to true to have PODs save postscript figures in addition to bitmaps. + "save_ps": false, + + // Set to true for files > 4 GB + "large_file": false, + + // If true, leave pp data in OUTPUT_DIR after preprocessing; if false, delete pp data after PODs + // run to completion + "save_pp_data": true, + + // Set to true to save HTML and bitmap plots in a .tar file. + "make_variab_tar": false, + + // Generate html output for multiple figures per case + "make_multicase_figure_html": false, + + // Set to true to overwrite results in OUTPUT_DIR; otherwise results saved + // under a unique name. + "overwrite": false, + + // List with custom preprocessing script(s) to run on data + // Place these scripts in the user_scripts directory of your copy of the MDTF-diagnostics repository + "user_pp_scripts" : [], + + // Settings used in debugging: + + // Log verbosity level. + "verbose": 1 +} diff --git a/templates/runtime_config_finite_amplitude_wave_diag.yml b/templates/runtime_config_finite_amplitude_wave_diag.yml new file mode 100755 index 000000000..e5dcf1f95 --- /dev/null +++ b/templates/runtime_config_finite_amplitude_wave_diag.yml @@ -0,0 +1,63 @@ +# Runtime yaml configuration file template for the MDTF-diagnostics package +# Create a copy of this file for personal use, and pass it to the framework +# with the -f | --configfile flag + +# List of POD(s) to run +pod_list: + - "finite_amplitude_wave_diag" + +# Case list entries (must be unique IDs for each simulation) +case_list: + "GFDL-CM3_historical_r1i1p1" : + model: "test" + convention: "CMIP" + startdate: "19800101000000" + enddate: "19801231000000" + +### Data location settings ### +# Required: full or relative path to ESM-intake catalog header file +DATA_CATALOG: "./diagnostics/example_multicase/esm_catalog_CMIP_synthetic_r1i1p1f1_gr1.json" +# Optional: Parent directory containing observational data used by individual PODs. +# If defined, the framework assumes observational data is in OBS_DATA_ROOT/[POD_NAME] +OBS_DATA_ROOT: "../inputdata/obs_data" +# Required: Working directory location. Temporary files are written here. +# Final output is also written here if the OUTPUT_DIR is not defined. +WORK_DIR: "../wkdir" +# Optional: Location to write final output if you don't want it in the wkdir +OUTPUT_DIR: "../wkdir" +### Environment Settings ### +# Required: Location of the Anaconda/miniconda installation to use for managing +# dependencies (path returned by running `conda info --base`.) +conda_root: "/home/clare/miniconda3" +# Optional: Directory containing the framework-specific conda environments. This should +# be equal to the "--env_dir" flag passed to conda_env_setup.sh. If left +# blank, the framework will look for its environments in conda_root/envs +conda_env_root: "/home/clare/miniconda3/envs" +# Location of micromamba executable; REQUIRED if using micromamba +micromamba_exe: "" +### Data type settings ### +# set to true to handle data files > 4 GB +large_file: False +### Output Settings ### +# Set to true to have PODs save postscript figures in addition to bitmaps. +save_ps: False +# If true, leave pp data in OUTPUT_DIR after preprocessing; if false, delete pp data after PODs +# run to completion +save_pp_data: True +# Set to true to perform data translation; default is True: +translate_data: True +# Set to true to save HTML and bitmap plots in a .tar file. +make_variab_tar: False +# Set to true to overwrite results in OUTPUT_DIR; otherwise results saved +# under a unique name. +overwrite: False +# Generate html output for multiple figures per case +"make_multicase_figure_html": False +### Developer settings ### +# Set to True to run the preprocessor +run_pp: True +# Additional custom preprocessing scripts to run on data +# place these scripts in the MDTF-diagnostics/user_scripts directory +# The framework will run the specified scripts whether run_pp is set to True or False +user_pp_scripts: + - ""