diff --git a/src/access_moppy/derivations/__init__.py b/src/access_moppy/derivations/__init__.py index 5210f727..8fcd9184 100644 --- a/src/access_moppy/derivations/__init__.py +++ b/src/access_moppy/derivations/__init__.py @@ -7,6 +7,8 @@ cl_level_to_height, cli_level_to_height, clw_level_to_height, + level_to_height, + load_zfull_resource, ) from access_moppy.derivations.calc_land import ( calc_carbon_pool_kg_m2, @@ -65,7 +67,9 @@ "cli_level_to_height": cli_level_to_height, "clw_level_to_height": clw_level_to_height, "cl_level_to_height": cl_level_to_height, + "level_to_height": level_to_height, "calculate_areacella": calculate_areacella, + "load_zfull_resource": load_zfull_resource, "isel": lambda x, **kwargs: x.isel(**kwargs), "calc_topsoil": calc_topsoil, "calc_landcover": calc_landcover, diff --git a/src/access_moppy/derivations/calc_atmos.py b/src/access_moppy/derivations/calc_atmos.py index 99b4503d..6b0e4403 100644 --- a/src/access_moppy/derivations/calc_atmos.py +++ b/src/access_moppy/derivations/calc_atmos.py @@ -34,6 +34,8 @@ # import click # import dask +from importlib.resources import as_file, files + import numpy as np import xarray as xr @@ -178,6 +180,35 @@ # ---------------------------------------------------------------------- +def level_to_height(ds): + """ + Transform model level indices to height coordinates. + + Converts from level dimension to height dimension by using stored height values + and updating dimension coordinates accordingly. + + Parameters + ---------- + ds : xarray.Dataset + Dataset with model level coordinates + + Returns + ------- + xarray.Dataset + Dataset with height coordinate dimension + """ + # Handle level coordinate transformation + if "theta_level_height" in ds: + ds = ( + ds.assign_coords({"lev": ds["theta_level_height"]}) + .swap_dims({"model_theta_level_number": "lev"}) + .drop_vars( + ["theta_level_height", "model_theta_level_number"], errors="ignore" + ) + ) + return ds + + def cli_level_to_height(ds): # Handle level coordinate transformation if "theta_level_height" in ds: @@ -306,3 +337,31 @@ def calculate_areacella(nlat=145, nlon=192, earth_radius=6371000.0): # Return as Dataset for use in internal calculations return xr.Dataset({"areacella": areacella_2d}) + + +def load_zfull_resource(): + """ + Load zfull height coordinate from package resource file. + + This function loads pre-computed height values for model levels that are + shipped with the package and applies the level_to_height transformation. + No model input is required. + + Returns + ------- + xarray.Dataset + Dataset containing zfull coordinate with dimensions (alevel, lat, lon) + + Notes + ----- + The resource file is typically named fx.zfull_ACCESS-ESM.nc and contains + height values above reference ellipsoid for each atmospheric model level. + The operation applies level-to-height coordinate transformation. + """ + resource_file = files("access_moppy.ressources") / "fx.zfull_ACCESS-ESM.nc" + with as_file(resource_file) as path: + ds = xr.open_dataset(path) + + # Apply level_to_height transformation + ds = level_to_height(ds) + return ds diff --git a/src/access_moppy/mappings/ACCESS-ESM1.6_mappings.json b/src/access_moppy/mappings/ACCESS-ESM1.6_mappings.json index 88d70e2c..e490cb9e 100644 --- a/src/access_moppy/mappings/ACCESS-ESM1.6_mappings.json +++ b/src/access_moppy/mappings/ACCESS-ESM1.6_mappings.json @@ -1532,6 +1532,21 @@ "type": "direct", "formula": "fld_s05i206" } + }, + "zfull": { + "dimensions": { + "alevel": "model_theta_level_number", + "lat": "lat", + "lon": "lon" + }, + "units": "m", + "positive": null, + "model_variables": "fld_s15i101", + "calculation": { + "type": "internal", + "function": "load_zfull_resource" + }, + "CF standard Name": "height_above_reference_ellipsoid" } }, "land": { diff --git a/src/access_moppy/ressources/fx.zfull_ACCESS-ESM.nc b/src/access_moppy/ressources/fx.zfull_ACCESS-ESM.nc new file mode 100644 index 00000000..d8d341a8 Binary files /dev/null and b/src/access_moppy/ressources/fx.zfull_ACCESS-ESM.nc differ