Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

JJB tropical subseasonal diags - Wheeler and Kiladis #732

Merged
merged 49 commits into from
Apr 12, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
49 commits
Select commit Hold shift + click to select a range
943030b
Initial commit of tropical subseasonal diagnostics
jjbenedict Jun 12, 2023
88e5884
add stand alone driver to reproduce Jim's results
chengzhuzhang Sep 22, 2023
58d0b5a
add plotting; clean up driver
chengzhuzhang Nov 21, 2023
f40d46a
add viwer
chengzhuzhang Nov 21, 2023
4ac2e66
zoom in MJO;use diff ratio
chengzhuzhang Nov 22, 2023
1166fd2
[Refactor]: Update `e3sm_diags_driver.run_diags` as `CoreParameter` m…
tomvothecoder Oct 13, 2023
091d9ee
[Doc]: Add GitHub issues and pull request templates (#741)
tomvothecoder Oct 23, 2023
336be9b
fix typo (#753)
chengzhuzhang Oct 27, 2023
9cc56f3
Update DevOps configs (#755)
tomvothecoder Nov 29, 2023
28ea590
Replace `parser.readfp()` with `parser.read_file()` for Python 3.12 s…
tomvothecoder Dec 12, 2023
14a6b97
Add more aerosol metrics (#763)
mahf708 Dec 13, 2023
059c71c
Bump to 2.10.0 (#766)
tomvothecoder Dec 13, 2023
ecc5f2a
[Refactor]: Refactor integration tests and add `use_cfg` flag to `run…
tomvothecoder Dec 13, 2023
04ffd97
Fix type conditional check and `UnboundLocalError` for `params_result…
tomvothecoder Dec 22, 2023
d1ed07f
Bump to 2.10.1rc1 (#771)
tomvothecoder Dec 22, 2023
59a16e1
Port over `cdp_viewer.OutputViewer` and remove `cdp` dependency (#773)
tomvothecoder Jan 3, 2024
f575065
Fix default diagnostics attrs not being copied to parent CoreParamete…
tomvothecoder Jan 8, 2024
a12ac88
Bump to 2.10.1 (#780)
tomvothecoder Jan 19, 2024
2cd644a
Fix `cosp_histogram_driver.py` not writing mean variables to netCDF (…
tomvothecoder Jan 25, 2024
64ffabc
update zppy cfg for perlmutter (#783)
chengzhuzhang Jan 30, 2024
d951535
Update README.md
chengzhuzhang Jan 30, 2024
29acef2
Support monthly average and variable name convention for EAMxx (#712)
chengzhuzhang Feb 8, 2024
22fb716
Update intercomparison with cmip models workflow (#786)
chengzhuzhang Feb 15, 2024
765210c
put everything together in source
chengzhuzhang Feb 24, 2024
9768b79
fix formatting
chengzhuzhang Feb 24, 2024
13c5be8
fix viewer
chengzhuzhang Feb 27, 2024
29e9caa
support model vs model
chengzhuzhang Feb 28, 2024
2f6cec2
fix format
chengzhuzhang Feb 28, 2024
8b851e2
Fix for OLR
chengzhuzhang Mar 7, 2024
2bdbd08
fix diff ratio
chengzhuzhang Mar 8, 2024
c693e8a
fix viewer
chengzhuzhang Mar 8, 2024
06d3325
Merge branch 'main' into jjb_tropical_subseasonal_diags
chengzhuzhang Mar 12, 2024
1664941
fix pre-commit problems; clean up
chengzhuzhang Mar 12, 2024
ca0ded4
Apply Tom's suggestions from code review
chengzhuzhang Mar 12, 2024
0422f98
correct print out start end year for subset data
chengzhuzhang Mar 14, 2024
f0f7253
fix dispersion curves for antisymmetric
chengzhuzhang Mar 20, 2024
31dc8cd
fix formatting
chengzhuzhang Mar 20, 2024
0cc310c
fix importing zwf_functions
chengzhuzhang Mar 20, 2024
913293f
address code review; clean up
chengzhuzhang Mar 21, 2024
1921965
address code review; clean up
chengzhuzhang Mar 21, 2024
d1f4b94
Revert "address code review; clean up"
chengzhuzhang Mar 21, 2024
a8bcf9e
update README.md
chengzhuzhang Mar 21, 2024
78d4fb8
update mp_partition with similar time subsetting
chengzhuzhang Mar 26, 2024
e334b15
updated original zwf_plot.py to allow dispersion curvers to cross zer…
jjbenedict Mar 29, 2024
0db4fa0
update dispersion lines labels
chengzhuzhang Apr 9, 2024
d87c554
update contour map colorbars
chengzhuzhang Apr 9, 2024
9fff78d
fix colarbar try2
chengzhuzhang Apr 10, 2024
a6f58d2
Merge branch 'main' into jjb_tropical_subseasonal_diags
chengzhuzhang Apr 10, 2024
82f97fd
Merge branch 'main' into jjb_tropical_subseasonal_diags
chengzhuzhang Apr 12, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .pre-commit-config.yaml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
exclude: "docs|node_modules|migrations|.git|.tox|examples|analysis_data_preprocess|auxiliary_tools|conda/meta.yaml"
exclude: "docs|node_modules|migrations|.git|.tox|examples|analysis_data_preprocess|auxiliary_tools|conda/meta.yaml|e3sm_diags/driver/utils/zwf_functions.py"
default_stages: [commit]
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I suggest addressing pre-commit issues in zwf_functions for long-term maintainability and readability rather than excluding them.

It'll make debugging and updating (if needed) much easier in the future.

fail_fast: true

Expand Down
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,8 @@ E3SM Diags is modeled after the National Center for Atmospheric Research (NCAR)
### New Feature added during v2 development
| Feature name <br />(set name) | Brief Introduction | Developers Contributors* | Released version |
|--- | ---------------- | --- | --- |
| Wavenumber frequency analysis (tropical_subseasonal) | The wavenumber frequency analysis (Wheeler-Kiladis Diagram) for Tropical subseasonal analysis | Jim Benedict, Brian Medeiros, Jill Zhang, Tom Vo | 2.11.0

| T5050 diagnostic /Mixed phase partition (mp_partition) | Temperature at which cloud top is 50% ice, 50% liquid, following McCoy et al. (2015). | Yuying Zhang, Jill Zhang, Jiwen Fan, Yunpeng Shan | 2.9.0 |
| ARM diagnostics v3 (arm_diags) | Enhanced ARM diagnostics with new aerosol-cloud-interaction and aerosol activation metrics | Xiaojian Zheng, Jill Zhang, Cheng Tao, Shaocheng Xie | 2.9.0 |
| Aerosol budget diagnostics (aerosol_budget) | Global mean burdens, source/sink budgets and lifetimes for aerosol species. | Jill Zhang, Susannah Burrows, Naser Mahfouz, Tom Vo, Kai Zhang, Taufiq Hassan Mozumder, Xue Zheng, Ziming Ke, Yan Feng, Manish Shrivastava, Hailong Wang, Mingxuan Wu | 2.8.0 |
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import os
from e3sm_diags.parameter.tropical_subseasonal_parameter import TropicalSubseasonalParameter
from e3sm_diags.run import runner

param = TropicalSubseasonalParameter()

param.reference_data_path = '/global/cfs/cdirs/e3sm/e3sm_diags/obs_for_e3sm_diags/time-series'
#param.reference_data_path = '/global/cfs/cdirs/e3sm/chengzhu/e3sm_diags_zppy_test_complete_run_output/v2.LR.historical_0101_20240130/post/atm/180x360_aave/ts/daily/15yr'
param.test_data_path = '/global/cfs/cdirs/e3sm/chengzhu/e3sm_diags_zppy_test_complete_run_output/v2.LR.historical_0101_20240130/post/atm/180x360_aave/ts/daily/15yr'
param.test_name = 'E3SMv2'
prefix = '/Users/zhang40/Documents/repos/e3sm_diags/auxiliary_tools/tropical_subseasonal_diags/data1'
prefix = '/global/cfs/cdirs/e3sm/www/chengzhu/tests/tropical_diags_subsetting'

param.results_dir = os.path.join(prefix, 'tropical_variability_model_obs_refine')
#param.run_type = "model_vs_model"
#param.ref_name = 'E3SMv2'
param.test_start_yr = '2000'
param.test_end_yr = '2000'
param.ref_start_yr = '2001'
param.ref_end_yr = '2001'
param.save_netcdf = True

runner.sets_to_run = ['tropical_subseasonal']
runner.run_diags([param])


28 changes: 28 additions & 0 deletions auxiliary_tools/tropical_subseasonal_diags/tropical_diags.cfg
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
[#]
sets = ["tropical_subseasonal"]
case_id = "wavenumber-frequency"
variables = ["PRECT"]
ref_name = "IMERG_Daily"
reference_name = "IMERG Daily"
# (2001-2021)"
regions = ["15S15N"]
#

[#]
sets = ["tropical_subseasonal"]
case_id = "wavenumber-frequency"
variables = ["FLUT"]
ref_name = "NOAA-OLR_Daily"
reference_name = "NOAA OLR Daily"
# (1979-2021)"
regions = ["15S15N"]
#
#
#[#]
#sets = ["tropical_subseasonal"]
#case_id = "wavenumber-frequency"
#variables = ["U850"]
#ref_name = "ERA5_Daily"
#reference_name = "ERA5 Daily"
## (1980-2022)"
#regions = ["15S15N"]
Original file line number Diff line number Diff line change
@@ -0,0 +1,196 @@
from __future__ import annotations

import glob
import json
import os
from typing import TYPE_CHECKING # , Optional

import numpy as np
import xarray as xr

import e3sm_diags
from e3sm_diags.driver import utils
from e3sm_diags.logger import custom_logger
from e3sm_diags.plot.cartopy.mp_partition_plot import plot
import matplotlib.pyplot as plt
from matplotlib.colors import ListedColormap, BoundaryNorm

from tropical_subseasonal_plot import plot
from tropical_subseasonal_viewer import create_viewer
from zwf import zwf_functions as wf

logger = custom_logger(__name__)

# Script to compute and plot spectral powers of a subseasonal tropical field in
# zonal wavenumber-frequency space. Both the plot files and files containing the
# associated numerical data shown in the plots are created.

# Authors: Jim Benedict and Brian Medeiros
# Modified by Jill Zhang to integrate into E3SM Diags.

def find_nearest(array, value):
array = np.asarray(array)
idx = (np.abs(array - value)).argmin()
return idx,array[idx]
"""Return index of [array] closest in value to [value]
Example:
array = [ 0.21069679 0.61290182 0.63425412 0.84635244 0.91599191 0.00213826
0.17104965 0.56874386 0.57319379 0.28719469]
print(find_nearest(array, value=0.5))
# 0.568743859261

"""

def wf_analysis(x, **kwargs):
"""Return zonal wavenumber-frequency power spectra of x. The returned spectra are:
spec_sym: Raw (non-normalized) power spectrum of the component of x that is symmetric about the equator.
spec_asym: Raw (non-normalized) power spectrum of the component of x that is antisymmetric about the equator.
nspec_sym: Normalized (by a smoothed red-noise background spectrum) power spectrum of the component of x that is symmetric about the equator.
nspec_asym: Normalized (by a smoothed red-noise background spectrum) power spectrum of the component of x that is antisymmetric about the equator.

The NCL version of 'wkSpaceTime' smooths the symmetric and antisymmetric components
along the frequency dimension using a 1-2-1 filter once.

"""
# Get the "raw" spectral power
# OPTIONAL kwargs:
# segsize, noverlap, spd, latitude_bounds (tuple: (south, north)), dosymmetries, rmvLowFrq

z2 = wf.spacetime_power(x, **kwargs)
z2avg = z2.mean(dim='component')
z2.loc[{'frequency':0}] = np.nan # get rid of spurious power at \nu = 0 (mean)

# Following NCL's wkSpaceTime, apply one pass of a 1-2-1 filter along the frequency
# domain to the raw (non-normalized) spectra/um.
# Do not use 0 frequency when smoothing here.
# Use weights that sum to 1 to ensure that smoothing is conservative.
z2s = wf.smoothFrq121(z2,1)

# The background is supposed to be derived from both symmetric & antisymmetric
# Inputs to the background spectrum calculation should be z2avg
background = wf.smoothBackground_wavefreq(z2avg)
# separate components
spec_sym = z2s[0,...]
spec_asy = z2s[1,...]
# normalize: Following NCL's wkSpaceTime, use lightly smoothed version of spectra/um
# as numerator
nspec_sym = spec_sym / background
nspec_asy = spec_asy / background

spec = xr.merge([spec_sym.rename('spec_raw_sym'), spec_asy.rename('spec_raw_asy'), nspec_sym.rename('spec_norm_sym'), nspec_asy.rename('spec_norm_asy'), background.rename('spec_background')], compat='override')
spec_all = spec.drop('component')
spec_all['spec_raw_sym'].attrs = {"component": "symmetric", "type": "raw"}
spec_all['spec_raw_asy'].attrs = {"component": "antisymmetric", "type": "raw"}
spec_all['spec_norm_sym'].attrs = {"component": "symmetric", "type": "normalized"}
spec_all['spec_norm_asy'].attrs = {"component": "antisymmetric", "type": "normalized"}
spec_all['spec_background'].attrs = {"component": "", "type": "background"}

return spec_all


def calculate_spectrum(path, variable):

var = xr.open_mfdataset(glob.glob(f"{test_data_path}/{variable}_*.nc")).sel(
lat=slice(-15, 15))[variable]

# TODO: subset time

# Unit conversion
if var.name == "PRECT":
if var.attrs["units"] == "m/s" or var.attrs["units"] == "m s{-1}":
print("\nBEFORE unit conversion: Max/min of data: " + str(var.values.max()) + " " + str(var.values.min()))
var.values = var.values * 1000. * 86400. # convert m/s to mm/d, do not alter metadata (yet)
var.attrs["units"] = "mm/d" # adjust metadata to reflect change in units
print("\nAFTER unit conversion: Max/min of data: " + str(var.values.max()) + " " + str(var.values.min()))
if var.name == "precipAvg":
if var.attrs["units"] == "mm/hr":
print("\nBEFORE unit conversion: Max/min of data: " + str(var.values.max()) + " " + str(var.values.min()))
var.values = data.values * 24. # convert mm/hr to mm/d, do not alter metadata (yet)
var.attrs["units"] = "mm/d" # adjust metadata to reflect change in units
print("\nAFTER unit conversion: Max/min of data: " + str(var.values.max()) + " " + str(var.values.min()))

# Wavenumber Frequency Analysis
spec_all = wf_analysis(var, **opt)
#spec_all.to_netcdf(outDataDir + "/full_spec.nc")
return spec_all

#
# Options ... right now these only go into wk.spacetime_power()
#
do_zooming = False # Set to True to also make plots to zoom into MJO spectral region,
# in addition to the default (larger) spectral region
latBound = (-15,15) # latitude bounds for analysis
spd = 1 # SAMPLES PER DAY
nDayWin = 96 # Wheeler-Kiladis [WK] temporal window length (days)
nDaySkip = -60 # time (days) between temporal windows [segments]
# negative means there will be overlapping temporal segments
twoMonthOverlap = -1*nDaySkip

vari = "PRECT"
srcID = "model"
outDataDir = "/global/cfs/cdirs/e3sm/www/chengzhu/tests/tropical_diags"
outDataDir = "/Users/zhang40/Documents/repos/e3sm_diags/auxiliary_tools/tropical_subseasonal_diags/data"

opt = {'segsize': nDayWin,
'noverlap': twoMonthOverlap,
'spd': spd,
'latitude_bounds': latBound,
'dosymmetries': True,
'rmvLowFrq':True}


#datapath = '/global/cfs/cdirs/e3sm/forsyth/E3SMv2/v2.LR.historical_0201/post/atm/180x360_aave/ts/daily/5yr'
datapath = '/Users/zhang40/Documents/e3sm_diags_data/e3sm_diags_test_data/E3SM_v2_daily'

from e3sm_diags.parameter.core_parameter import CoreParameter
parameter = CoreParameter()

test_data_path = '/Users/zhang40/Documents/e3sm_diags_data/e3sm_diags_test_data/E3SM_v2_daily'
parameter.test_data_path = test_data_path
parameter.test_timeseries_input = True
parameter.test_start_yr = '2000'
parameter.test_end_yr = '2014'
parameter.ref_data_path = test_data_path
parameter.ref_timeseries_input = True
parameter.ref_start_yr = '2000'
parameter.ref_end_yr = '2014'
parameter.variables = ['PRECT']
season = "ANN"

test_data = utils.dataset.Dataset(parameter, test=True)
parameter.test_name_yrs = utils.general.get_name_and_yrs(
parameter, test_data, season
)

ref_data = utils.dataset.Dataset(parameter, ref=True)
parameter.ref_name_yrs = utils.general.get_name_and_yrs(
parameter, ref_data, season
)

for variable in parameter.variables:
#test = calculate_spectrum(parameter.test_data_path, variable)
#test.to_netcdf("data/full_spec_test.nc")
#ref = calculate_spectrum(parameter.ref_data_path, variable)
#ref.to_netcdf("data/full_spec_ref.nc")
# Below uses intermediate saved files for development
test = xr.open_dataset("/Users/zhang40/Documents/repos/e3sm_diags/auxiliary_tools/tropical_subseasonal_diags/data/full_spec_ref.nc").load()
ref = xr.open_dataset("/Users/zhang40/Documents/repos/e3sm_diags/auxiliary_tools/tropical_subseasonal_diags/data/full_spec_ref.nc").load()
parameter.var_id = variable

for diff_name in ["raw_sym", "raw_asy", "norm_sym", "norm_asy", "background"]:

# Compute percentage difference
diff = 100 * (test[f"spec_{diff_name}"]-ref[f"spec_{diff_name}"])/ref[f"spec_{diff_name}"]
diff.name = f"spec_{diff_name}"
diff.attrs.update(test[f"spec_{diff_name}"].attrs)
parameter.spec_type = diff_name
plot(parameter, test[f"spec_{diff_name}"], ref[f"spec_{diff_name}"], diff)
if "norm" in diff_name:
parameter.spec_type = f"{diff_name}_zoom"
plot(parameter, test[f"spec_{diff_name}"], ref[f"spec_{diff_name}"], diff, do_zoom = True)



display_name, url = create_viewer('.', parameter)
print("Viewer Created: ", url)

Loading
Loading