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

New monsoon sperber code in xCDAT -- PR cleaned up #1022

Merged
merged 53 commits into from
Jun 22, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
53 commits
Select commit Hold shift + click to select a range
817462d
code update by @bosup -- new branch made by @lee1043 for clean up PR
lee1043 Jan 16, 2024
c1f950b
modified: driver_monsoon_sperber.py
bosup Feb 6, 2024
97aecc3
modified: lib/argparse_functions.py
bosup Feb 6, 2024
09aaeaf
modified: ../../share/DefArgsCIA.json
bosup Feb 6, 2024
6692788
modified: driver_monsoon_sperber.py
bosup Feb 6, 2024
2f66f5b
modified: driver_monsoon_sperber.py
bosup Feb 13, 2024
3d9dd77
modified: driver_monsoon_sperber.py
bosup Feb 13, 2024
8eefb97
modified: driver_monsoon_sperber.py
bosup Feb 13, 2024
e68a0a0
modified: driver_monsoon_sperber.py
bosup Feb 21, 2024
b915245
Merge branch 'main' into new_monsoon_lee1043_clean_up
bosup Mar 1, 2024
22a50f4
Merge remote-tracking branch 'origin/main' into new_monsoon_lee1043_c…
bosup Mar 4, 2024
08170a1
modified: driver_monsoon_sperber.py
bosup Mar 5, 2024
aa2f7c5
modified: lib/model_land_only.py
bosup Mar 5, 2024
23ee2e8
modified: driver_monsoon_sperber.py
bosup Mar 5, 2024
3d44dc4
modified: driver_monsoon_sperber.py
bosup Mar 5, 2024
68a5fcb
modified: driver_monsoon_sperber.py
bosup Mar 7, 2024
2f1f442
modified: driver_monsoon_sperber.py
bosup Apr 5, 2024
93d8ff2
modified: driver_monsoon_sperber.py
bosup Apr 5, 2024
1e7bd3d
Merge branch 'main' into new_monsoon_lee1043_clean_up
lee1043 Apr 6, 2024
f07ec2c
modified: driver_monsoon_sperber.py
bosup Apr 10, 2024
1296607
Merge branch 'new_monsoon_lee1043_clean_up' of github.com:PCMDI/pcmdi…
bosup Apr 10, 2024
bb0d2d4
modified: driver_monsoon_sperber.py
bosup Apr 24, 2024
639313f
modified: driver_monsoon_sperber.py
bosup May 13, 2024
67e382c
clean up the driver file
lee1043 May 15, 2024
4814871
pre-commit clean up
lee1043 May 15, 2024
21cc657
pre-commit clean up
lee1043 May 15, 2024
1d66708
Merge branch 'main' into new_monsoon_lee1043_clean_up
lee1043 May 15, 2024
d6c3b80
pre-commit clean up
lee1043 May 15, 2024
8a07210
bug fix and clean up
lee1043 May 15, 2024
956f7bd
bug fix, clean up
lee1043 May 15, 2024
f19f00f
clean up
lee1043 May 15, 2024
8d6f2fe
clean up
lee1043 May 15, 2024
ceefe71
clean up, variable name shortened for simplify the code
lee1043 May 15, 2024
a2186b9
adjust units just one time immediately after load data, instead of do…
lee1043 May 16, 2024
acf80b1
clean up
lee1043 May 16, 2024
be5ed29
debug, clean up, simplify
lee1043 May 16, 2024
8c96248
clean up
lee1043 May 16, 2024
bf30d7b
clarify
lee1043 May 16, 2024
1cc88d2
simplify logic and remove repeating lines
lee1043 May 16, 2024
3df2172
more info to netcdf for debug
lee1043 May 17, 2024
fc5f5c6
clean up
lee1043 May 17, 2024
aede34c
clean up
lee1043 May 19, 2024
263b6fb
Merge branch 'main' into new_monsoon_lee1043_clean_up
lee1043 Jun 1, 2024
4879f15
clean up
lee1043 Jun 3, 2024
3065074
clean up (inlcude move CMEC parsers to argparse lib
lee1043 Jun 3, 2024
9fbf0b0
clean up
lee1043 Jun 3, 2024
70c11b8
update
lee1043 Jun 3, 2024
b60daf4
clean up
lee1043 Jun 3, 2024
f0b61d0
update
lee1043 Jun 3, 2024
d7322fb
bug fix
lee1043 Jun 4, 2024
b5a64fe
update
lee1043 Jun 4, 2024
6c12692
reduce number of lines
lee1043 Jun 4, 2024
adeceb0
Merge branch 'main' into new_monsoon_lee1043_clean_up
lee1043 Jun 11, 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
453 changes: 358 additions & 95 deletions doc/jupyter/Demo/Demo_2b_monsoon_sperber.ipynb

Large diffs are not rendered by default.

566 changes: 333 additions & 233 deletions pcmdi_metrics/monsoon_sperber/driver_monsoon_sperber.py

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions pcmdi_metrics/monsoon_sperber/lib/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,4 @@
from .calc_metrics import sperber_metrics # noqa
from .divide_chunks import divide_chunks, divide_chunks_advanced, interp1d # noqa
from .model_land_only import model_land_only # noqa
from .lib_monsoon_sperber import pick_year_last_day, tree
22 changes: 21 additions & 1 deletion pcmdi_metrics/monsoon_sperber/lib/argparse_functions.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,10 @@ def AddParserArgument(P):
P.add_argument(
"--meyear", dest="meyear", type=int, help="End year for model data set"
)
P.add_argument("--modnames", type=list, default=None, help="List of models")
P.add_argument("--modnames", type=str, default=None, help="List of models")
P.add_argument(
"--list_monsoon_regions", type=str, default=None, help="List of regions"
)
P.add_argument(
"-r",
"--realization",
Expand Down Expand Up @@ -95,6 +98,23 @@ def AddParserArgument(P):
default=True,
help="Option for update existing JSON file: True (i.e., update) (default) / False (i.e., overwrite)",
)
# CMEC
P.add_argument(
"--cmec",
dest="cmec",
default=False,
action="store_true",
help="Use to save CMEC format metrics JSON",
)
P.add_argument(
"--no_cmec",
dest="cmec",
default=False,
action="store_false",
help="Do not save CMEC format metrics JSON",
)
P.set_defaults(cmec=False)

return P


Expand Down
17 changes: 14 additions & 3 deletions pcmdi_metrics/monsoon_sperber/lib/calc_metrics.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,34 +8,45 @@

Drafted: Jiwoo Lee, 2018-07
Revised: Jiwoo Lee, 2019-05
Revised: Bo Dong, 2023-12

Note: Code for picking onset/decay index inspired by
https://stackoverflow.com/questions/2236906/first-python-list-index-greater-than-x
"""

import MV2


def sperber_metrics(d, region, debug=False):
"""d: input, 1d array of cumulative pentad time series"""
# Convert accumulation to fractional accumulation; normalize by sum
d_sum = d[-1]

# Normalize
frac_accum = MV2.divide(d, d_sum)
frac_accum = d / d_sum

# Stat 1: Onset
onset_index = next(i for i, v in enumerate(frac_accum) if v >= 0.2)
i = onset_index
v = frac_accum[i]

if debug:
print("i = , ", i, " v = ", v)

# Stat 2: Decay
if region == "GoG":
decay_threshold = 0.6
else:
decay_threshold = 0.8

decay_index = next(i for i, v in enumerate(frac_accum) if v >= decay_threshold)

# Stat 3: Slope
slope = (frac_accum[decay_index] - frac_accum[onset_index]) / float(
decay_index - onset_index
)

# Stat 4: Duration
duration = decay_index - onset_index + 1

# Calc done, return result as dic
return {
"frac_accum": frac_accum,
Expand Down
29 changes: 18 additions & 11 deletions pcmdi_metrics/monsoon_sperber/lib/divide_chunks.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
from __future__ import print_function

import sys

import numpy as np
Expand All @@ -14,7 +12,7 @@

def divide_chunks(data, n):
# looping till length data
for i in range(0, len(data), n):
for i in range(0, data.time.shape[0], n):
yield data[i : i + n]


Expand All @@ -24,43 +22,51 @@ def divide_chunks(data, n):

def divide_chunks_advanced(data, n, debug=False):
# Double check first date should be Jan 1 (except for SH monsoon)
tim = data.getTime()
calendar = tim.calendar
month = tim.asComponentTime()[0].month
day = tim.asComponentTime()[0].day

tim = data.time.dt
month = tim.month[0]
day = tim.day[0]
month = month.values
day = day.values
calendar = "gregorian"

if debug:
print("debug: first day of year is " + str(month) + "/" + str(day))

if month not in [1, 7] or day != 1:
sys.exit(
"error: first day of year time series is " + str(month) + "/" + str(day)
)

# Check number of days in given year
nday = len(data)
nday = data.time.shape[0]

if nday in [365, 360]:
# looping till length data
for i in range(0, nday, n):
yield data[i : i + n]

elif nday == 366:
# until leap year day detected
for i in range(0, nday, n):
# Check if leap year date included
leap_detect = False
for ii in range(i, i + n):
date = data.getTime().asComponentTime()[ii]
month = date.month
day = date.day
date = data.time.dt
month = date.month[ii]
day = date.day[ii]
if month == 2 and day > 28:
if debug:
print("debug: leap year detected:", month, "/", day)
leap_detect = True

if leap_detect:
yield data[i : i + n + 1]
tmp = i + n + 1
break
else:
yield data[i : i + n]

# after leap year day passed
if leap_detect:
for i in range(tmp, nday, n):
Expand All @@ -76,6 +82,7 @@ def divide_chunks_advanced(data, n, debug=False):
# looping till length data
for i in range(0, nday, n):
yield data[i : i + n]

else:
sys.exit("error: number of days in year is " + str(nday))

Expand Down
22 changes: 22 additions & 0 deletions pcmdi_metrics/monsoon_sperber/lib/lib_monsoon_sperber.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
from collections import defaultdict

import xcdat as xc


def tree():
return defaultdict(tree)


def pick_year_last_day(ds):
eday = 31
try:
time_key = xc.axis.get_dim_keys(ds, axis="T")
if "calendar" in ds[time_key].attrs.keys():
if "360" in ds[time_key]["calendar"]:
eday = 30
else:
if "360" in ds[time_key][0].values.item().calendar:
eday = 30
except Exception:
pass
return eday
59 changes: 13 additions & 46 deletions pcmdi_metrics/monsoon_sperber/lib/model_land_only.py
Original file line number Diff line number Diff line change
@@ -1,80 +1,47 @@
import cartopy.crs as ccrs
import genutil
import matplotlib.pyplot as plt
import MV2
import numpy as np


def model_land_only(model, model_timeseries, lf, debug=False):
# -------------------------------------------------
# Mask out over ocean grid
# - - - - - - - - - - - - - - - - - - - - - - - - -

if debug:
plot_map(model_timeseries[0], "_".join(["test", model, "beforeMask.png"]))
# plot_map(model_timeseries[0], "_".join(["test", model, "beforeMask.png"]))
print("debug: plot for beforeMask done")

# Check land fraction variable to see if it meet criteria
# (0 for ocean, 100 for land, no missing value)
lat_c = lf.getAxis(0)
lon_c = lf.getAxis(1)
lf_id = lf.id

lf = MV2.array(lf.filled(0.0))

lf.setAxis(0, lat_c)
lf.setAxis(1, lon_c)
lf.id = lf_id

if float(MV2.max(lf)) == 1.0:
lf = MV2.multiply(lf, 100.0)

# Matching dimension
if debug:
print("debug: match dimension in model_land_only")
model_timeseries, lf_timeConst = genutil.grower(model_timeseries, lf)

# Conserve axes
time_c = model_timeseries.getAxis(0)
lat_c2 = model_timeseries.getAxis(1)
lon_c2 = model_timeseries.getAxis(2)
if np.max(lf) == 1.0:
lf = lf * 100.0

opt1 = False

if opt1: # Masking out partial ocean grids as well
# Mask out ocean even fractional (leave only pure ocean grid)
model_timeseries_masked = MV2.masked_where(lf_timeConst < 100, model_timeseries)
model_timeseries_masked = model_timeseries.where(lf > 0 & lf < 100)

else: # Mask out only full ocean grid & use weighting for partial ocean grid
model_timeseries_masked = MV2.masked_where(
lf_timeConst == 0, model_timeseries
) # mask out pure ocean grids
model_timeseries_masked = model_timeseries.where(lf > 0)

if model == "EC-EARTH":
# Mask out over 90% land grids for models those consider river as
# part of land-sea fraction. So far only 'EC-EARTH' does..
model_timeseries_masked = MV2.masked_where(
lf_timeConst < 90, model_timeseries
)
lf2 = MV2.divide(lf, 100.0)
model_timeseries, lf2_timeConst = genutil.grower(
model_timeseries, lf2
) # Matching dimension
model_timeseries_masked = MV2.multiply(
model_timeseries_masked, lf2_timeConst
) # consider land fraction like as weighting

# Make sure to have consistent axes
model_timeseries_masked.setAxis(0, time_c)
model_timeseries_masked.setAxis(1, lat_c2)
model_timeseries_masked.setAxis(2, lon_c2)
model_timeseries_masked = model_timeseries.where(lf > 90)

if debug:
plot_map(model_timeseries_masked[0], "_".join(["test", model, "afterMask.png"]))
# plot_map(model_timeseries_masked[0], "_".join(["test", model, "afterMask.png"]))
print("debug: plot for afterMask done")

return model_timeseries_masked


def plot_map(data, filename):
lons = data.getLongitude()
lats = data.getLatitude()
lons = data["lon"]
lats = data["lat"]
ax = plt.axes(projection=ccrs.PlateCarree(central_longitude=180))
ax.contourf(lons, lats, data, transform=ccrs.PlateCarree(), cmap="viridis")
ax.coastlines()
Expand Down
76 changes: 76 additions & 0 deletions pcmdi_metrics/monsoon_sperber/param/Bo_param.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
import datetime
import os

# =================================================
# Background Information
# -------------------------------------------------
mip = "cmip5"
exp = "historical"
frequency = "da"
realm = "atm"

# =================================================
# Miscellaneous
# -------------------------------------------------
update_json = False
debug = False
# debug = True

# list_monsoon_regions = ["AIR", "AUS", "Sahel", "GoG", "NAmo", "SAmo"]
list_monsoon_regions = ["AUS"]
# =================================================
# Observation
# -------------------------------------------------
reference_data_name = "GPCP-1-3"
reference_data_path = "/p/user_pub/PCMDIobs/obs4MIPs/NASA-GSFC/GPCP-1DD-CDR-v1-3/day/pr/1x1/latest/pr_day_GPCP-1DD-CDR-v1-3_PCMDIFROGS_1x1_19961001-20201231.nc"
reference_data_lf_path = (
"/work/lee1043/DATA/LandSeaMask_1x1_NCL/NCL_LandSeaMask_rewritten.nc" # noqa
)

varOBS = "pr"
ObsUnitsAdjust = (True, "multiply", 86400.0) # kg m-2 s-1 to mm day-1

osyear = 1998
oeyear = 1999

includeOBS = True

# =================================================
# Models
# -------------------------------------------------
modpath = "/work/lee1043/ESGF/xmls/cmip5/historical/day/pr/cmip5.%(model).%(exp).%(realization).day.pr.xml"
modpath_lf = "/work/lee1043/ESGF/xmls/cmip5/historical/fx/sftlf/cmip5.%(model).historical.r0i0p0.fx.sftlf.xml"

# /p/css03/scratch/published-older/cmip5/output1/CSIRO-BOM/ACCESS1-0/historical/day/atmos/day/r1i1p1/v4/pr/pr_day_ACCESS1-0_historical_r1i1p1_19750101-19991231.nc

# modnames = ['ACCESS1-0', 'ACCESS1-3', 'BCC-CSM1-1', 'BCC-CSM1-1-M', 'BNU-ESM', 'CanCM4', 'CanESM2', 'CCSM4', 'CESM1-BGC', 'CESM1-CAM5', 'CESM1-FASTCHEM', 'CMCC-CESM', 'CMCC-CM', 'CMCC-CMS', 'CNRM-CM5', 'CSIRO-Mk3-6-0', 'EC-EARTH', 'FGOALS-g2', 'GFDL-CM3', 'GFDL-ESM2G', 'GFDL-ESM2M', 'GISS-E2-H', 'GISS-E2-R', 'HadGEM2-AO', 'HadGEM2-CC', 'HadGEM2-ES', 'INMCM4', 'IPSL-CM5A-LR', 'IPSL-CM5A-MR', 'IPSL-CM5B-LR', 'MIROC-ESM', 'MIROC-ESM-CHEM', 'MIROC4h', 'MIROC5', 'MPI-ESM-MR', 'MPI-ESM-P', 'MRI-CGCM3', 'MRI-ESM1', 'NorESM1-M'] # noqa

modnames = ["ACCESS1-0"]

realization = "r1i1p1"
# realization = '*'

varModel = "pr"
ModUnitsAdjust = (True, "multiply", 86400.0) # kg m-2 s-1 to mm day-1
units = "mm/d"

msyear = 1998
meyear = 1999

# =================================================
# Output
# -------------------------------------------------
# pmprdir = "/p/user_pub/pmp/pmp_results/pmp_v1.1.2"
pmprdir = "/p/user_pub/climate_work/dong12/PMP_result/"
case_id = "{:v%Y%m%d}".format(datetime.datetime.now())

if debug:
pmprdir = "/p/user_pub/climate_work/dong12/PMP_result/"
case_id = "{:v%Y%m%d-%H%M}".format(datetime.datetime.now())

results_dir = os.path.join(
pmprdir, "%(output_type)", "monsoon", "monsoon_sperber", mip, exp, case_id
)

nc_out = True # Write output in NetCDF
plot = True # Create map graphics
6 changes: 3 additions & 3 deletions pcmdi_metrics/monsoon_sperber/param/myParam.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,8 @@
varOBS = "pr"
ObsUnitsAdjust = (True, "multiply", 86400.0) # kg m-2 s-1 to mm day-1

osyear = 1996
oeyear = 2016
osyear = 1998
oeyear = 1999

includeOBS = True

Expand All @@ -49,7 +49,7 @@
ModUnitsAdjust = (True, "multiply", 86400.0) # kg m-2 s-1 to mm day-1
units = "mm/d"

msyear = 1961
msyear = 1998
meyear = 1999

# =================================================
Expand Down
Loading