From 66c7aff5134360b8471e2cb8dcdbb50a159eee1d Mon Sep 17 00:00:00 2001 From: blaylockbk Date: Tue, 29 Oct 2024 17:57:36 +0000 Subject: [PATCH] implement wgrib2-style shortcuts for accessing NAVGEM files on GODAE --- docs/gallery/usnavy_models/navgem.ipynb | 72 ++++++++++++++++++++++--- herbie/models/usnavy.py | 71 ++++++++++++++++++++++-- 2 files changed, 130 insertions(+), 13 deletions(-) diff --git a/docs/gallery/usnavy_models/navgem.ipynb b/docs/gallery/usnavy_models/navgem.ipynb index 03b21e8f..dc45aef4 100644 --- a/docs/gallery/usnavy_models/navgem.ipynb +++ b/docs/gallery/usnavy_models/navgem.ipynb @@ -1414,7 +1414,7 @@ }, { "cell_type": "code", - "execution_count": 16, + "execution_count": 1, "id": "062e3c33", "metadata": {}, "outputs": [], @@ -2562,6 +2562,67 @@ ")" ] }, + { + "cell_type": "markdown", + "id": "e692d43d", + "metadata": {}, + "source": [ + "### Shortcuts\n", + "\n", + "You may use wgrib-style strings in place of the `variable` (and omit `level`) for a few common variables. Please open a pull request to add additional variables to the shortcuts. Some examples:\n", + "\n", + "- `TMP:2 m`\n", + "- `RH:2 m`\n", + "- `UGRD:10 m`\n", + "- `VGRD:10 m`\n", + "- `TMP:### mb` - Pressure levels\n", + "- `PRES:surface`" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "1f017ce0", + "metadata": {}, + "outputs": [ + { + "ename": "AttributeError", + "evalue": "'Herbie' object has no attribute 'level'", + "output_type": "error", + "traceback": [ + "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[0;31mAttributeError\u001b[0m Traceback (most recent call last)", + "Cell \u001b[0;32mIn[2], line 1\u001b[0m\n\u001b[0;32m----> 1\u001b[0m H \u001b[38;5;241m=\u001b[39m \u001b[43mHerbie\u001b[49m\u001b[43m(\u001b[49m\n\u001b[1;32m 2\u001b[0m \u001b[43m \u001b[49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[38;5;124;43m2024-01-04 00:00\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[43m,\u001b[49m\n\u001b[1;32m 3\u001b[0m \u001b[43m \u001b[49m\u001b[43mmodel\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[38;5;124;43mnavgem_godae\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[43m,\u001b[49m\n\u001b[1;32m 4\u001b[0m \u001b[43m \u001b[49m\u001b[43mvariable\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[38;5;124;43mTMP:2 m\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[43m,\u001b[49m\n\u001b[1;32m 5\u001b[0m \u001b[43m \u001b[49m\u001b[43mfxx\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[38;5;241;43m6\u001b[39;49m\u001b[43m,\u001b[49m\n\u001b[1;32m 6\u001b[0m \u001b[43m)\u001b[49m\n", + "File \u001b[0;32m~/GitHub.com/blaylockbk/Herbie/herbie/core.py:235\u001b[0m, in \u001b[0;36mHerbie.__init__\u001b[0;34m(self, date, valid_date, model, fxx, product, priority, save_dir, overwrite, verbose, **kwargs)\u001b[0m\n\u001b[1;32m 227\u001b[0m \u001b[38;5;28msetattr\u001b[39m(\u001b[38;5;28mself\u001b[39m, key, value)\n\u001b[1;32m 229\u001b[0m \u001b[38;5;66;03m# Get details from the template of the specified model.\u001b[39;00m\n\u001b[1;32m 230\u001b[0m \u001b[38;5;66;03m# This attaches the details from the `models..template`\u001b[39;00m\n\u001b[1;32m 231\u001b[0m \u001b[38;5;66;03m# class to this Herbie object.\u001b[39;00m\n\u001b[1;32m 232\u001b[0m \u001b[38;5;66;03m# This line is equivalent to `model_templates.gfs.template(self)`.\u001b[39;00m\n\u001b[1;32m 233\u001b[0m \u001b[38;5;66;03m# I do it this way because the model name is a variable.\u001b[39;00m\n\u001b[1;32m 234\u001b[0m \u001b[38;5;66;03m# (see https://stackoverflow.com/a/7936588/2383070 for what I'm doing here)\u001b[39;00m\n\u001b[0;32m--> 235\u001b[0m \u001b[38;5;28;43mgetattr\u001b[39;49m\u001b[43m(\u001b[49m\u001b[43mmodel_templates\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mmodel\u001b[49m\u001b[43m)\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mtemplate\u001b[49m\u001b[43m(\u001b[49m\u001b[38;5;28;43mself\u001b[39;49m\u001b[43m)\u001b[49m\n\u001b[1;32m 237\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m product \u001b[38;5;129;01mis\u001b[39;00m \u001b[38;5;28;01mNone\u001b[39;00m:\n\u001b[1;32m 238\u001b[0m \u001b[38;5;66;03m# The user didn't specify a product, so let's use the first\u001b[39;00m\n\u001b[1;32m 239\u001b[0m \u001b[38;5;66;03m# product in the model template.\u001b[39;00m\n\u001b[1;32m 240\u001b[0m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mproduct \u001b[38;5;241m=\u001b[39m \u001b[38;5;28mlist\u001b[39m(\u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mPRODUCTS)[\u001b[38;5;241m0\u001b[39m]\n", + "File \u001b[0;32m~/GitHub.com/blaylockbk/Herbie/herbie/models/usnavy.py:35\u001b[0m, in \u001b[0;36mnavgem_godae.template\u001b[0;34m(self)\u001b[0m\n\u001b[1;32m 27\u001b[0m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mPRODUCTS \u001b[38;5;241m=\u001b[39m {\n\u001b[1;32m 28\u001b[0m \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mGMET\u001b[39m\u001b[38;5;124m\"\u001b[39m: \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124m\"\u001b[39m,\n\u001b[1;32m 29\u001b[0m \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mGLND\u001b[39m\u001b[38;5;124m\"\u001b[39m: \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124m\"\u001b[39m,\n\u001b[1;32m 30\u001b[0m \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mGCOM\u001b[39m\u001b[38;5;124m\"\u001b[39m: \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124m\"\u001b[39m,\n\u001b[1;32m 31\u001b[0m }\n\u001b[1;32m 33\u001b[0m \u001b[38;5;66;03m# Please review https://usgodae.org/docs/layout/mdllayout.pns.html\u001b[39;00m\n\u001b[1;32m 34\u001b[0m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mSOURCES \u001b[38;5;241m=\u001b[39m {\n\u001b[0;32m---> 35\u001b[0m \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mnavgem\u001b[39m\u001b[38;5;124m\"\u001b[39m: \u001b[38;5;124mf\u001b[39m\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mhttps://usgodae.org/ftp/outgoing/fnmoc/models/navgem_0.5/\u001b[39m\u001b[38;5;132;01m{\u001b[39;00m\u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mdate\u001b[38;5;132;01m:\u001b[39;00m\u001b[38;5;124m%Y/%Y%m%d%H\u001b[39m\u001b[38;5;132;01m}\u001b[39;00m\u001b[38;5;124m/US058\u001b[39m\u001b[38;5;132;01m{\u001b[39;00m\u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mproduct\u001b[38;5;132;01m}\u001b[39;00m\u001b[38;5;124m-GR1mdl.0018_0056_\u001b[39m\u001b[38;5;132;01m{\u001b[39;00m\u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mfxx\u001b[38;5;132;01m:\u001b[39;00m\u001b[38;5;124m03d\u001b[39m\u001b[38;5;132;01m}\u001b[39;00m\u001b[38;5;124m00F0RL\u001b[39m\u001b[38;5;132;01m{\u001b[39;00m\u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mdate\u001b[38;5;132;01m:\u001b[39;00m\u001b[38;5;124m%Y%m%d%H\u001b[39m\u001b[38;5;132;01m}\u001b[39;00m\u001b[38;5;124m_\u001b[39m\u001b[38;5;132;01m{\u001b[39;00m\u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mlevel\u001b[49m\u001b[38;5;132;01m}\u001b[39;00m\u001b[38;5;132;01m{\u001b[39;00m\u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mvariable\u001b[38;5;132;01m}\u001b[39;00m\u001b[38;5;124m\"\u001b[39m,\n\u001b[1;32m 36\u001b[0m \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mnogaps\u001b[39m\u001b[38;5;124m\"\u001b[39m: \u001b[38;5;124mf\u001b[39m\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mhttps://usgodae.org/ftp/outgoing/fnmoc/models/nogaps/\u001b[39m\u001b[38;5;132;01m{\u001b[39;00m\u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mdate\u001b[38;5;132;01m:\u001b[39;00m\u001b[38;5;124m%Y/%Y%m%d%H\u001b[39m\u001b[38;5;132;01m}\u001b[39;00m\u001b[38;5;124m/US058\u001b[39m\u001b[38;5;132;01m{\u001b[39;00m\u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mproduct\u001b[38;5;132;01m}\u001b[39;00m\u001b[38;5;124m-GR1mdl.0058_0240_\u001b[39m\u001b[38;5;132;01m{\u001b[39;00m\u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mfxx\u001b[38;5;132;01m:\u001b[39;00m\u001b[38;5;124m03d\u001b[39m\u001b[38;5;132;01m}\u001b[39;00m\u001b[38;5;124m00F0RL\u001b[39m\u001b[38;5;132;01m{\u001b[39;00m\u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mdate\u001b[38;5;132;01m:\u001b[39;00m\u001b[38;5;124m%Y%m%d%H\u001b[39m\u001b[38;5;132;01m}\u001b[39;00m\u001b[38;5;124m_\u001b[39m\u001b[38;5;132;01m{\u001b[39;00m\u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mlevel\u001b[38;5;132;01m}\u001b[39;00m\u001b[38;5;132;01m{\u001b[39;00m\u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mvariable\u001b[38;5;132;01m}\u001b[39;00m\u001b[38;5;124m\"\u001b[39m,\n\u001b[1;32m 37\u001b[0m }\n\u001b[1;32m 38\u001b[0m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mLOCALFILE \u001b[38;5;241m=\u001b[39m \u001b[38;5;124mf\u001b[39m\u001b[38;5;124m\"\u001b[39m\u001b[38;5;132;01m{\u001b[39;00m\u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mget_remoteFileName\u001b[38;5;132;01m}\u001b[39;00m\u001b[38;5;124m\"\u001b[39m\n", + "\u001b[0;31mAttributeError\u001b[0m: 'Herbie' object has no attribute 'level'" + ] + } + ], + "source": [ + "H = Herbie(\n", + " \"2024-01-04 00:00\",\n", + " model=\"navgem_godae\",\n", + " variable=\"TMP:2 m\",\n", + " fxx=6,\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "68ffa21f", + "metadata": {}, + "outputs": [], + "source": [ + "H = Herbie(\n", + " \"2024-01-04 00:00\",\n", + " model=\"navgem_godae\",\n", + " variable=\"RH:500 mb\",\n", + " fxx=6,\n", + ")" + ] + }, { "cell_type": "markdown", "id": "978ca0ab", @@ -3115,7 +3176,7 @@ ], "metadata": { "kernelspec": { - "display_name": "herbie-dev", + "display_name": "Python 3", "language": "python", "name": "python3" }, @@ -3129,12 +3190,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.12.2" - }, - "vscode": { - "interpreter": { - "hash": "ac511961bc6976a3d289cde8fc247e6b0e9298cf513cb762aa0a7df646d192ed" - } + "version": "3.12.7" } }, "nbformat": 4, diff --git a/herbie/models/usnavy.py b/herbie/models/usnavy.py index 2d8e752a..867c3628 100644 --- a/herbie/models/usnavy.py +++ b/herbie/models/usnavy.py @@ -24,16 +24,77 @@ def template(self): "godae": "https://usgodae.org/", "filename_description": "https://usgodae.org/docs/layout/mdllayout.pns.html", } - self.PRODUCTS = { - "GMET": "", - "GLND": "", - "GCOM": "", + + if self.variable == "HGT:surface": + # Special case for terrain height + self.PRODUCTS = { + "GLND": "Land fields (terrain)", + } + else: + self.PRODUCTS = { + "GMET": "Meteorological fields", + "GLND": "Land fields (terrain)", + "GOCN": "Ocean fields", + "GCOM": "?", + } + + # Facilitate familiar shortcuts using wgrib2-style terms to allow + # - `variable='TMP:2 m'` + # - `variable='TMP:500 mb'` + # - `variable='UGRD:10 m'` + # - `variable='SNOD:surface'` + # See https://usgodae.org/docs/layout/pn_level_type_tbl.pns.html + + # Map of wgrib2 variable names to GODAE variable names + variable_map = { + "TMP": "air_temp", # Air temperature + "DEPR": "dwpt_dprs", # Dew point depression + "ABSV": "abs_vort", # Absolute vorticity + "RH": "rltv_hum", # Relative Humidity + "PRES": "pres", # Pressure + "UGRD": "wnd_ucmp", # Wind u-component + "VGRD": "wnd_vcmp", # Wind v-component + "HGT": "geop_ht", # Geopotential height + "VAPP": "vpr_pres", # Vapor pressure + "VVEL": "wnd_vert_vel", # Vertical velocity + "CAPE": "cape", # CAPE + "VIS": "visib", # Visibility + "PWAT": "prcp_h20", # Precipitable water (use PWAT:surface) + "PRATE": "rain_rate", # Precipitation rate + "SHTFL": "snsb_heat_flux", # Sensible heat flux + "SNOD": "snw_dpth", # Snow depth + "NSWRS": "sol_rad", # Net short-wave radiation flux + "UFLX": "wnd_strs_ucmp", # Momentum flux, u-component + "VFLX": "wnd_strs_vcmp", # Momentum flux, v-component + "PRMSL": "pres_msl", # Mean sea level pressure } - # Please review https://usgodae.org/docs/layout/mdllayout.pns.html + if ":" in self.variable: + var, lev = self.variable.split(":", maxsplit=1) + self.variable = variable_map.get(var) + if var == "HGT" and lev == "surface": + self.level = "0001_000000-000000" + self.variable = "terr_ht" + elif var in {"MSLMA", "PRMSL"}: + self.level = "0102_000000-000000" + elif lev.endswith("mb"): + lev = int(lev.strip("mb").strip()) + self.level = f"0100_{lev:06d}-000000" + elif lev == "2 m": + self.level = "0105_000020-000000" + elif lev == "10 m": + self.level = "0105_000100-000000" + elif lev == "surface": + self.level = "0001_000000-000000" + elif lev in {"0C", "0C isotherm"}: + self.level = "0006_000000-000000" + elif lev in {"tropopause"}: + self.level = "0007_000000-000000" + self.SOURCES = { "navgem": f"https://usgodae.org/ftp/outgoing/fnmoc/models/navgem_0.5/{self.date:%Y/%Y%m%d%H}/US058{self.product}-GR1mdl.0018_0056_{self.fxx:03d}00F0RL{self.date:%Y%m%d%H}_{self.level}{self.variable}", "nogaps": f"https://usgodae.org/ftp/outgoing/fnmoc/models/nogaps/{self.date:%Y/%Y%m%d%H}/US058{self.product}-GR1mdl.0058_0240_{self.fxx:03d}00F0RL{self.date:%Y%m%d%H}_{self.level}{self.variable}", + "navgem grib2": f"https://usgodae.org/ftp/outgoing/fnmoc/models/navgem_0.5/{self.date:%Y/%Y%m%d%H}/US058{self.product}-GR2mdl.0018_0056_{self.fxx:03d}00F0RL{self.date:%Y%m%d%H}_{self.level}{self.variable}.gr2", } self.LOCALFILE = f"{self.get_remoteFileName}"