Skip to content

Commit 44c0a6b

Browse files
add EAMxx support for cmorization (#340)
* add EAMxx support for cmorization * resolve ValueError from CI/CD build * add workflow for processing eamxx vars * Add seprate handler for precip_total_surf_mass_flux Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * address review comments * fix test_utils * fix pr handler based on Shixuan's review * resume vertical interpolation options matching zppy --------- Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
1 parent 01807c3 commit 44c0a6b

File tree

7 files changed

+664
-15
lines changed

7 files changed

+664
-15
lines changed

e3sm_to_cmip/cmor_handlers/_formulas.py

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -466,20 +466,51 @@ def pr(ds: xr.Dataset) -> xr.DataArray:
466466
467467
High frequency version:
468468
pr = PRECT * 1000.0
469+
470+
EAMxx version:
471+
pr = (precip_liq_surf_mass_flux + precip_ice_surf_mass_flux) * 1000.0
472+
or:
473+
pr = precip_total_surf_mass_flux * 1000.0
469474
"""
470475
if all(key in ds.data_vars for key in ["PRECC", "PRECL"]):
471476
result = (ds["PRECC"] + ds["PRECL"]) * 1000.0
472477
elif "PRECT" in ds:
473478
result = ds["PRECT"] * 1000.0
479+
elif all(
480+
key in ds.data_vars
481+
for key in ["precip_liq_surf_mass_flux", "precip_ice_surf_mass_flux"]
482+
):
483+
result = (
484+
ds["precip_liq_surf_mass_flux"] + ds["precip_ice_surf_mass_flux"]
485+
) * 1000.0
486+
elif "precip_total_surf_mass_flux" in ds:
487+
result = ds["precip_total_surf_mass_flux"] * 1000.0
474488
else:
475489
raise KeyError(
476490
"No formula could be applied for 'pr'. Check the handler entry for 'pr' "
477-
"and input file(s) contain either 'PRECC' and 'PRECL', or 'PRECT."
491+
"and input file(s) contain either 'PRECC' and 'PRECL', 'PRECT', "
492+
"'precip_liq_surf_mass_flux' and 'precip_ice_surf_mass_flux', "
493+
"or 'precip_total_surf_mass_flux'."
478494
)
479495

480496
return result
481497

482498

499+
def clwvi(ds: xr.Dataset) -> xr.DataArray:
500+
"""
501+
CMIP6 clwvi = condensed water path (liquid + ice).
502+
503+
EAM version:
504+
clwvi = TGCLDCWP (total condensed = liquid + ice, single variable)
505+
506+
EAMxx version:
507+
clwvi = LiqWaterPath + IceWaterPath
508+
In EAMxx, LiqWaterPath is liquid-only and IceWaterPath is ice-only;
509+
they must be summed to match the CMIP6 definition.
510+
"""
511+
return ds["LiqWaterPath"] + ds["IceWaterPath"]
512+
513+
483514
def prsn(ds: xr.Dataset) -> xr.DataArray:
484515
"""
485516
prsn = (PRECSC + PRECSL) * 1000.0

e3sm_to_cmip/cmor_handlers/handlers.yaml

Lines changed: 285 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1040,3 +1040,288 @@
10401040
formula: null
10411041
positive: null
10421042
levels: { name: plev19, units: Pa, e3sm_axis_name: plev }
1043+
# ============================================================
1044+
# EAMxx variable handlers (issue #339)
1045+
# Maps EAMxx output variable names to CMIP6 variables.
1046+
# These entries coexist with EAM entries above; the handler
1047+
# system selects whichever raw_variables are present in input.
1048+
# ============================================================
1049+
# --- Precipitation and column water ---
1050+
- name: pr
1051+
units: kg m-2 s-1
1052+
raw_variables: [precip_liq_surf_mass_flux, precip_ice_surf_mass_flux]
1053+
table: CMIP6_Amon.json
1054+
unit_conversion: null
1055+
formula: (precip_liq_surf_mass_flux + precip_ice_surf_mass_flux) * 1000.0
1056+
positive: null
1057+
levels: null
1058+
- name: pr
1059+
units: kg m-2 s-1
1060+
raw_variables: [precip_total_surf_mass_flux]
1061+
table: CMIP6_Amon.json
1062+
unit_conversion: null
1063+
formula: precip_total_surf_mass_flux * 1000.0
1064+
positive: null
1065+
levels: null
1066+
- name: prw
1067+
units: kg m-2
1068+
raw_variables: [VapWaterPath]
1069+
table: CMIP6_Amon.json
1070+
unit_conversion: null
1071+
formula: null
1072+
positive: null
1073+
levels: null
1074+
# --- Surface temperature and humidity ---
1075+
- name: ts
1076+
units: K
1077+
raw_variables: [surf_radiative_T]
1078+
table: CMIP6_Amon.json
1079+
unit_conversion: null
1080+
formula: null
1081+
positive: null
1082+
levels: null
1083+
- name: tas
1084+
units: K
1085+
raw_variables: [T_2m]
1086+
table: CMIP6_Amon.json
1087+
unit_conversion: null
1088+
formula: null
1089+
positive: null
1090+
levels: null
1091+
- name: huss
1092+
units: "1"
1093+
raw_variables: [qv_2m]
1094+
table: CMIP6_Amon.json
1095+
unit_conversion: null
1096+
formula: null
1097+
positive: null
1098+
levels: null
1099+
# --- Surface energy and momentum fluxes ---
1100+
- name: hfls
1101+
units: W m-2
1102+
raw_variables: [surface_upward_latent_heat_flux]
1103+
table: CMIP6_Amon.json
1104+
unit_conversion: null
1105+
formula: null
1106+
positive: up
1107+
levels: null
1108+
- name: hfss
1109+
units: W m-2
1110+
raw_variables: [surf_sens_flux]
1111+
table: CMIP6_Amon.json
1112+
unit_conversion: null
1113+
formula: null
1114+
positive: up
1115+
levels: null
1116+
- name: evspsbl
1117+
units: kg m-2 s-1
1118+
raw_variables: [surf_evap]
1119+
table: CMIP6_Amon.json
1120+
unit_conversion: null
1121+
formula: null
1122+
positive: null
1123+
levels: null
1124+
- name: tauu
1125+
units: Pa
1126+
raw_variables: [surf_mom_flux_U]
1127+
table: CMIP6_Amon.json
1128+
unit_conversion: "-1"
1129+
formula: null
1130+
positive: down
1131+
levels: null
1132+
- name: tauv
1133+
units: Pa
1134+
raw_variables: [surf_mom_flux_V]
1135+
table: CMIP6_Amon.json
1136+
unit_conversion: "-1"
1137+
formula: null
1138+
positive: down
1139+
levels: null
1140+
# --- 10m wind speed ---
1141+
- name: sfcWind
1142+
units: m s-1
1143+
raw_variables: [wind_speed_10m]
1144+
table: CMIP6_Amon.json
1145+
unit_conversion: null
1146+
formula: null
1147+
positive: null
1148+
levels: null
1149+
# --- Sea-level pressure ---
1150+
- name: psl
1151+
units: Pa
1152+
raw_variables: [SeaLevelPressure]
1153+
table: CMIP6_Amon.json
1154+
unit_conversion: null
1155+
formula: null
1156+
positive: null
1157+
levels: null
1158+
# --- TOA radiation ---
1159+
- name: rsdt
1160+
units: W m-2
1161+
raw_variables: [SW_flux_dn_at_model_top]
1162+
table: CMIP6_Amon.json
1163+
unit_conversion: null
1164+
formula: null
1165+
positive: down
1166+
levels: null
1167+
- name: rsut
1168+
units: W m-2
1169+
raw_variables: [SW_flux_up_at_model_top]
1170+
table: CMIP6_Amon.json
1171+
unit_conversion: null
1172+
formula: null
1173+
positive: up
1174+
levels: null
1175+
- name: rsutcs
1176+
units: W m-2
1177+
raw_variables: [SW_clrsky_flux_up_at_model_top]
1178+
table: CMIP6_Amon.json
1179+
unit_conversion: null
1180+
formula: null
1181+
positive: up
1182+
levels: null
1183+
- name: rlut
1184+
units: W m-2
1185+
raw_variables: [LW_flux_up_at_model_top]
1186+
table: CMIP6_Amon.json
1187+
unit_conversion: null
1188+
formula: null
1189+
positive: up
1190+
levels: null
1191+
- name: rlutcs
1192+
units: W m-2
1193+
raw_variables: [LW_clrsky_flux_up_at_model_top]
1194+
table: CMIP6_Amon.json
1195+
unit_conversion: null
1196+
formula: null
1197+
positive: up
1198+
levels: null
1199+
# --- Surface radiation ---
1200+
- name: rsds
1201+
units: W m-2
1202+
raw_variables: [SW_flux_dn_at_model_bot]
1203+
table: CMIP6_Amon.json
1204+
unit_conversion: null
1205+
formula: null
1206+
positive: down
1207+
levels: null
1208+
- name: rsus
1209+
units: W m-2
1210+
raw_variables: [SW_flux_up_at_model_bot]
1211+
table: CMIP6_Amon.json
1212+
unit_conversion: null
1213+
formula: null
1214+
positive: up
1215+
levels: null
1216+
- name: rsdscs
1217+
units: W m-2
1218+
raw_variables: [SW_clrsky_flux_dn_at_model_bot]
1219+
table: CMIP6_Amon.json
1220+
unit_conversion: null
1221+
formula: null
1222+
positive: down
1223+
levels: null
1224+
- name: rsuscs
1225+
units: W m-2
1226+
raw_variables: [SW_clrsky_flux_up_at_model_bot]
1227+
table: CMIP6_Amon.json
1228+
unit_conversion: null
1229+
formula: null
1230+
positive: up
1231+
levels: null
1232+
- name: rlds
1233+
units: W m-2
1234+
raw_variables: [LW_flux_dn_at_model_bot]
1235+
table: CMIP6_Amon.json
1236+
unit_conversion: null
1237+
formula: null
1238+
positive: down
1239+
levels: null
1240+
- name: rlus
1241+
units: W m-2
1242+
raw_variables: [LW_flux_up_at_model_bot]
1243+
table: CMIP6_Amon.json
1244+
unit_conversion: null
1245+
formula: null
1246+
positive: up
1247+
levels: null
1248+
- name: rldscs
1249+
units: W m-2
1250+
raw_variables: [LW_clrsky_flux_dn_at_model_bot]
1251+
table: CMIP6_Amon.json
1252+
unit_conversion: null
1253+
formula: null
1254+
positive: down
1255+
levels: null
1256+
# --- Cloud water paths ---
1257+
# CMIP6 clwvi = "condensed water path" = liquid + ice (per CMIP6_Amon.json comment).
1258+
# In EAMxx, LiqWaterPath is liquid-only (equiv. EAM TGCLDLWP), and
1259+
# IceWaterPath is ice-only (equiv. EAM TGCLDIWP). Their sum gives the
1260+
# total condensed water path required by CMIP6 clwvi.
1261+
# By contrast, EAM uses TGCLDCWP (total = liquid + ice) directly for clwvi.
1262+
- name: clwvi
1263+
units: kg m-2
1264+
raw_variables: [LiqWaterPath, IceWaterPath]
1265+
table: CMIP6_Amon.json
1266+
unit_conversion: null
1267+
formula: LiqWaterPath + IceWaterPath
1268+
positive: null
1269+
levels: null
1270+
- name: clivi
1271+
units: kg m-2
1272+
raw_variables: [IceWaterPath]
1273+
table: CMIP6_Amon.json
1274+
unit_conversion: null
1275+
formula: null
1276+
positive: null
1277+
levels: null
1278+
# --- Aerosol optical depth ---
1279+
- name: od550aer
1280+
units: "1"
1281+
raw_variables: [AerosolOpticalDepth550nm]
1282+
table: CMIP6_AERmon.json
1283+
unit_conversion: null
1284+
formula: null
1285+
positive: null
1286+
levels: null
1287+
# --- 3D pressure-level variables (requires data interpolated to plev) ---
1288+
- name: ta
1289+
units: K
1290+
raw_variables: [T_mid]
1291+
table: CMIP6_Amon.json
1292+
unit_conversion: null
1293+
formula: null
1294+
positive: null
1295+
levels: { name: plev19, units: Pa, e3sm_axis_name: plev }
1296+
- name: hus
1297+
units: "1"
1298+
raw_variables: [qv]
1299+
table: CMIP6_Amon.json
1300+
unit_conversion: null
1301+
formula: null
1302+
positive: null
1303+
levels: { name: plev19, units: Pa, e3sm_axis_name: plev }
1304+
- name: hur
1305+
units: "%"
1306+
raw_variables: [RelativeHumidity]
1307+
table: CMIP6_Amon.json
1308+
unit_conversion: null
1309+
formula: null
1310+
positive: null
1311+
levels: { name: plev19, units: Pa, e3sm_axis_name: plev }
1312+
- name: wap
1313+
units: Pa s-1
1314+
raw_variables: [omega]
1315+
table: CMIP6_Amon.json
1316+
unit_conversion: null
1317+
formula: null
1318+
positive: null
1319+
levels: { name: plev19, units: Pa, e3sm_axis_name: plev }
1320+
- name: zg
1321+
units: m
1322+
raw_variables: [z_mid]
1323+
table: CMIP6_Amon.json
1324+
unit_conversion: null
1325+
formula: null
1326+
positive: null
1327+
levels: { name: plev19, units: Pa, e3sm_axis_name: plev }

e3sm_to_cmip/cmor_handlers/utils.py

Lines changed: 11 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44
from importlib.machinery import SourceFileLoader
55
from typing import Any, Literal, get_args
66

7-
import pandas as pd
87
import yaml
98

109
from e3sm_to_cmip import (
@@ -256,23 +255,21 @@ def _get_handlers_from_yaml() -> dict[str, list[dict[str, Any]]]:
256255
with open(HANDLER_DEFINITIONS_PATH, "r") as infile:
257256
handlers_file = yaml.load(infile, yaml.SafeLoader)
258257

259-
df_in = pd.DataFrame.from_dict(handlers_file)
260-
261258
handlers = defaultdict(list)
262-
for row in df_in.itertuples():
259+
for entry in handlers_file:
263260
var_handler = VarHandler(
264-
name=row.name, # type: ignore
265-
units=row.units, # type: ignore
266-
raw_variables=row.raw_variables, # type: ignore
267-
table=row.table, # type: ignore
268-
formula=row.formula, # type: ignore
269-
unit_conversion=row.unit_conversion, # type: ignore
270-
positive=row.positive, # type: ignore
271-
levels=row.levels, # type: ignore
261+
name=entry["name"],
262+
units=entry["units"],
263+
raw_variables=entry["raw_variables"],
264+
table=entry["table"],
265+
formula=entry.get("formula"),
266+
unit_conversion=entry.get("unit_conversion"),
267+
positive=entry.get("positive"),
268+
levels=entry.get("levels"),
272269
).to_dict()
273-
handlers[row.name].append(var_handler)
270+
handlers[entry["name"]].append(var_handler)
274271

275-
return dict(handlers) # type: ignore
272+
return dict(handlers)
276273

277274

278275
def _get_handlers_from_modules(path: str) -> dict[str, list[dict[str, Any]]]:

0 commit comments

Comments
 (0)