Skip to content

Commit 4364561

Browse files
Update tropical_subseasonal set for eamxx output (#982)
* update tropical_subseasonal_for_eamxx * use sub_monthly flag * support U_at_850hPa with missing value * add example pre-processing and run script * fix pre-commit issues * Remove commented out code in example run script --------- Co-authored-by: tomvothecoder <[email protected]>
1 parent 9cb5727 commit 4364561

File tree

4 files changed

+161
-65
lines changed

4 files changed

+161
-65
lines changed

e3sm_diags/derivations/derivations.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -881,6 +881,9 @@
881881
("wind_speed_10m",): rename, # EAMxx
882882
("si10",): rename,
883883
},
884+
"U850": {
885+
("U_at_850hPa",): rename, # EAMxx
886+
},
884887
"QREFHT": {
885888
("QREFHT",): lambda q: convert_units(q, target_units="g/kg"),
886889
("qv_2m",): lambda q: convert_units(q, target_units="g/kg"), # EAMxx

e3sm_diags/driver/tropical_subseasonal_driver.py

Lines changed: 59 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -9,16 +9,14 @@
99

1010
from __future__ import annotations
1111

12-
import glob
13-
from typing import TYPE_CHECKING # , Optional
12+
from typing import TYPE_CHECKING, Tuple
1413

1514
import numpy as np
1615
import xarray as xr
1716

1817
from e3sm_diags.driver.utils import zwf_functions as wf
1918
from e3sm_diags.driver.utils.climo_xr import ClimoFreq
2019
from e3sm_diags.driver.utils.dataset_xr import Dataset
21-
from e3sm_diags.driver.utils.general import pad_year
2220
from e3sm_diags.logger import _setup_child_logger
2321
from e3sm_diags.plot.tropical_subseasonal_plot import plot
2422

@@ -47,35 +45,28 @@ def run_diag(parameter: TropicalSubseasonalParameter) -> TropicalSubseasonalPara
4745
ref_data = Dataset(parameter, data_type="ref")
4846

4947
for variable in parameter.variables:
50-
test, test_start, test_end = calculate_spectrum(
51-
parameter.test_data_path,
52-
variable,
53-
parameter.test_start_yr,
54-
parameter.test_end_yr,
55-
)
48+
# Get test dataset
49+
test_ds = test_data.get_time_series_dataset(variable, single_point=True)
50+
test_spectrum, test_start, test_end = calculate_spectrum(test_ds, variable)
51+
52+
# Update parameters with actual time range
5653
parameter.test_start_yr = test_start
5754
parameter.test_end_yr = test_end
5855
parameter.test_name_yrs = test_data.get_name_yrs_attr(season)
56+
57+
# Get reference dataset
5958
if run_type == "model_vs_model":
60-
ref, ref_start, ref_end = calculate_spectrum(
61-
parameter.reference_data_path,
62-
variable,
63-
parameter.ref_start_yr,
64-
parameter.ref_end_yr,
65-
)
59+
ref_ds = ref_data.get_time_series_dataset(variable, single_point=True)
60+
ref_spectrum, ref_start, ref_end = calculate_spectrum(ref_ds, variable)
6661
elif run_type == "model_vs_obs":
6762
# TODO use pre-calculated spectral power
6863
# if parameter.ref_start_yr == "":
6964
# parameter.ref_name_yrs = parameter.reference_name
7065
# # read precalculated data.
7166
# else:
72-
ref_data_path = f"{parameter.reference_data_path}/{parameter.ref_name}"
73-
ref, ref_start, ref_end = calculate_spectrum(
74-
ref_data_path,
75-
variable,
76-
parameter.ref_start_yr,
77-
parameter.ref_end_yr,
78-
)
67+
ref_ds = ref_data.get_time_series_dataset(variable, single_point=True)
68+
ref_spectrum, ref_start, ref_end = calculate_spectrum(ref_ds, variable)
69+
7970
parameter.ref_start_yr = ref_start
8071
parameter.ref_end_yr = ref_end
8172
parameter.ref_name_yrs = ref_data.get_name_yrs_attr(season)
@@ -84,16 +75,24 @@ def run_diag(parameter: TropicalSubseasonalParameter) -> TropicalSubseasonalPara
8475
for diff_name in ["raw_sym", "raw_asy", "norm_sym", "norm_asy", "background"]:
8576
diff = (
8677
100
87-
* (test[f"spec_{diff_name}"] - ref[f"spec_{diff_name}"])
88-
/ ref[f"spec_{diff_name}"]
78+
* (
79+
test_spectrum[f"spec_{diff_name}"]
80+
- ref_spectrum[f"spec_{diff_name}"]
81+
)
82+
/ ref_spectrum[f"spec_{diff_name}"]
8983
)
9084
diff.name = f"spec_{diff_name}"
91-
diff.attrs.update(test[f"spec_{diff_name}"].attrs)
85+
diff.attrs.update(test_spectrum[f"spec_{diff_name}"].attrs)
9286

9387
parameter.spec_type = diff_name
9488
parameter.output_file = f"{parameter.var_id}_{parameter.spec_type}_15N-15S"
9589
parameter.diff_title = "percent difference"
96-
plot(parameter, test[f"spec_{diff_name}"], ref[f"spec_{diff_name}"], diff)
90+
plot(
91+
parameter,
92+
test_spectrum[f"spec_{diff_name}"],
93+
ref_spectrum[f"spec_{diff_name}"],
94+
diff,
95+
)
9796

9897
if "norm" in diff_name:
9998
parameter.spec_type = f"{diff_name}_zoom"
@@ -102,16 +101,33 @@ def run_diag(parameter: TropicalSubseasonalParameter) -> TropicalSubseasonalPara
102101
)
103102
plot(
104103
parameter,
105-
test[f"spec_{diff_name}"],
106-
ref[f"spec_{diff_name}"],
104+
test_spectrum[f"spec_{diff_name}"],
105+
ref_spectrum[f"spec_{diff_name}"],
107106
diff,
108107
do_zoom=True,
109108
)
110109

111110
return parameter
112111

113112

114-
def calculate_spectrum(path, variable, start_year, end_year):
113+
def calculate_spectrum(ds: xr.Dataset, variable: str) -> Tuple[xr.Dataset, str, str]:
114+
"""Calculate wavenumber-frequency power spectra for a variable.
115+
116+
Parameters
117+
----------
118+
ds : xr.Dataset
119+
Dataset containing the variable of interest
120+
variable : str
121+
Name of the variable to analyze
122+
123+
Returns
124+
-------
125+
Tuple[xr.Dataset, str, str]
126+
Tuple containing:
127+
- Dataset with spectral power components
128+
- Start year (as string)
129+
- End year (as string)
130+
"""
115131
# latitude bounds for analysis
116132
latBound = (-15, 15)
117133
# SAMPLES PER DAY
@@ -131,42 +147,13 @@ def calculate_spectrum(path, variable, start_year, end_year):
131147
"dosymmetries": True,
132148
"rmvLowFrq": True,
133149
}
134-
# TODO the time subsetting and variable derivation should be replaced during
135-
# cdat revamp.
136-
137-
start_year_str = pad_year(start_year)
138-
end_year_str = pad_year(end_year)
139-
try:
140-
var = xr.open_mfdataset(glob.glob(f"{path}/{variable}_*.nc")).sel(
141-
lat=slice(-15, 15),
142-
time=slice(f"{start_year_str}-01-01", f"{end_year_str}-12-31"),
143-
)[variable]
144-
actual_start = var.time.dt.year.values[0]
145-
actual_end = var.time.dt.year.values[-1]
146-
except OSError:
147-
logger.info(
148-
f"No files to open for {variable} within {start_year} and {end_year} from {path}."
149-
)
150-
raise
151-
# Unit conversion
152-
if var.name == "PRECT":
153-
if var.attrs["units"] == "m/s" or var.attrs["units"] == "m s{-1}":
154-
logger.info(
155-
"\nBEFORE unit conversion: Max/min of data: "
156-
+ str(var.values.max())
157-
+ " "
158-
+ str(var.values.min())
159-
)
160-
var.values = (
161-
var.values * 1000.0 * 86400.0
162-
) # convert m/s to mm/d, do not alter metadata (yet)
163-
var.attrs["units"] = "mm/d" # adjust metadata to reflect change in units
164-
logger.info(
165-
"\nAFTER unit conversion: Max/min of data: "
166-
+ str(var.values.max())
167-
+ " "
168-
+ str(var.values.min())
169-
)
150+
151+
# Extract variable data from dataset and subset to tropical latitudes
152+
var = ds[variable].sel(lat=slice(-15, 15))
153+
154+
# Get actual time range from the data
155+
actual_start = var.time.dt.year.values[0]
156+
actual_end = var.time.dt.year.values[-1]
170157

171158
# Wavenumber Frequency Analysis
172159
spec_all = wf_analysis(var, **opt)
@@ -220,6 +207,13 @@ def wf_analysis(x, **kwargs):
220207
# OPTIONAL kwargs:
221208
# segsize, noverlap, spd, latitude_bounds (tuple: (south, north)), dosymmetries, rmvLowFrq
222209

210+
# Interpolate missing values along longitude before spectral analysis
211+
if np.any(np.isnan(x)):
212+
logger.info(
213+
"Interpolating missing values along longitude before spectral analysis"
214+
)
215+
x = x.interpolate_na(dim="lon", method="linear", fill_value="extrapolate")
216+
223217
z2 = wf.spacetime_power(x, **kwargs)
224218
z2avg = z2.mean(dim="component")
225219
z2.loc[{"frequency": 0}] = np.nan # get rid of spurious power at \nu = 0 (mean)
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
import os
2+
from e3sm_diags.parameter.core_parameter import CoreParameter
3+
from e3sm_diags.parameter.diurnal_cycle_parameter import DiurnalCycleParameter
4+
from e3sm_diags.parameter.tropical_subseasonal_parameter import (
5+
TropicalSubseasonalParameter,
6+
)
7+
from e3sm_diags.run import runner
8+
9+
param = CoreParameter()
10+
11+
param.reference_data_path = (
12+
"/global/cfs/cdirs/e3sm/diagnostics/observations/Atm/climatology"
13+
)
14+
15+
test_base_path = "/pscratch/sd/c/chengzhu/ne256pg2_ne256pg2.F20TR-SCREAMv1.rainfrac1.spanc1000.auto2700.acc150.n0128"
16+
ref_climo = "/global/cfs/cdirs/e3sm/diagnostics/observations/Atm/climatology/"
17+
ref_ts = "/global/cfs/cdirs/e3sm/diagnostics/observations/Atm/time-series"
18+
param.test_data_path = f"{test_base_path}/rgr/climo"
19+
param.test_name = "1ma_ne30pg2.AVERAGE.nmonths_x1"
20+
param.seasons = ["ANN", "DJF", "MAM", "JJA", "SON"]
21+
# param.save_netcdf = True
22+
23+
prefix = "/global/cfs/cdirs/e3sm/www/zhang40/tests/eamxx"
24+
param.results_dir = os.path.join(prefix, "eamxx_ne256_0520_trop")
25+
params = [param]
26+
27+
trop_param = TropicalSubseasonalParameter()
28+
trop_param.test_data_path = f"{test_base_path}/rgr/ts_daily"
29+
trop_param.short_test_name = "Daily_3hi_ne30pg2"
30+
trop_param.test_start_yr = 1996
31+
trop_param.test_end_yr = 2001
32+
33+
# Obs
34+
trop_param.reference_data_path = ref_ts
35+
trop_param.ref_start_yr = 2001
36+
trop_param.ref_end_yr = 2010
37+
38+
params.append(trop_param)
39+
40+
dc_param = DiurnalCycleParameter()
41+
dc_param.test_data_path = f"{test_base_path}/rgr/climo_diurnal_3hrly"
42+
dc_param.test_name = "3hi_ne30pg2.INSTANT.nhours_x3"
43+
dc_param.short_test_name = "3hi_ne30pg2"
44+
# Plotting diurnal cycle amplitude on different scales. Default is True
45+
dc_param.normalize_test_amp = False
46+
47+
# Obs
48+
dc_param.reference_data_path = ref_climo
49+
50+
params.append(dc_param)
51+
52+
runner.sets_to_run = ["tropical_subseasonal"]
53+
54+
runner.run_diags(params)
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
#!/bin/bash
2+
3+
source /global/common/software/e3sm/anaconda_envs/load_latest_e3sm_unified_pm-cpu.sh
4+
#
5+
drc_in=/global/cfs/cdirs/e3sm/beydoun/ne256pg2_ne256pg2.F20TR-SCREAMv1.rainfrac1.spanc1000.auto2700.acc150.n0128/run
6+
drc_out=/pscratch/sd/c/chengzhu/ne256pg2_ne256pg2.F20TR-SCREAMv1.rainfrac1.spanc1000.auto2700.acc150.n0128
7+
map_file=/global/cfs/projectdirs/e3sm/zender/maps/map_ne30pg2_to_cmip6_180x360_traave.20231201.nc
8+
#Monthly averaged: 1ma_ne30pg2.AVERAGE.nmonths_x1.
9+
#3hourly instantaneous: 3hi_ne30pg2.INSTANT.nhours_x3.
10+
#1hourly instantaneous: 1hi_ne30pg2.INSTANT.nhours_x1.
11+
start=1996
12+
end=2001
13+
14+
echo "Generating Climatology files"
15+
mkdir -p $drc_out
16+
drc_rgr=${drc_out}/rgr/climo
17+
drc=${drc_out}/native/climo
18+
echo $drc_out
19+
cd ${drc_in};eval ls 1ma_ne30pg2.AVERAGE.nmonths_x1.*{${start}..${end}}*.nc | ncclimo -P eamxx -p serial --fml_nm=1ma_ne30pg2.AVERAGE.nmonths_x1 --yr_srt=${start} --yr_end=${end} --drc_out=$drc -O $drc_rgr --map=${map_file}
20+
21+
echo "Generating Diurnal Cycle Climo files"
22+
# Following drc_in included 3hi_ne30pg2.INSTANT.nhours_x3.*nc but with time_bnds
23+
drc_in=/global/cfs/cdirs/e3sm/chengzhu/ne256pg2_ne256pg2.F20TR-SCREAMv1.rainfrac1.spanc1000.auto2700.acc150.n0128/run
24+
25+
drc_rgr=${drc_out}/rgr/climo_diurnal_3hrly
26+
drc=${drc_out}/native/climo_diurnal_3hrly
27+
echo ${drc_in}
28+
29+
cd ${drc_in};eval ls 3hi_ne30pg2.INSTANT.nhours_x3.*{${start}..${end}}*-10800.nc | ncclimo -P eamxx --clm_md=hfc --caseid=3hi_ne30pg2.INSTANT.nhours_x3 -v precip_liq_surf_mass_flux,precip_ice_surf_mass_flux --yr_srt=${start} --yr_end=${end} --drc_out=${drc} -O $drc_rgr --map=${map_file} #--tpd=8
30+
##
31+
echo "Generating per-variable time-series from 3hourly instantaneous output."
32+
drc_rgr=${drc_out}/rgr/ts_3hrly
33+
drc=${drc_out}/native/ts_3hrly
34+
35+
cd ${drc_in};eval ls 3hi_ne30pg2.INSTANT.nhours_x3.*{${start}..${end}}*-10800.nc | ncclimo -P eamxx --clm_md=hfs --caseid=3hi_ne30pg2.INSTANT.nhours_x3 --var=precip_liq_surf_mass_flux,precip_ice_surf_mass_flux,U_at_850hPa,LW_flux_up_at_model_top --yr_srt=$start --yr_end=$end --drc_out=${drc} -O $drc_rgr --map=${map_file} --split # --tpd=8
36+
37+
echo "Average 3hrly to daily"
38+
drc_rgr=${drc_out}/rgr/ts_daily
39+
mkdir -p $drc_rgr
40+
cd ${drc_out}/rgr/ts_3hrly
41+
for i in *.nc; do ncra --mro -O -d time,0,,8,8 "$i" "${drc_rgr}/$i";done
42+
43+
exit
44+
45+

0 commit comments

Comments
 (0)