diff --git a/src/access_moppy/derivations/__init__.py b/src/access_moppy/derivations/__init__.py index 6e630b7..7bf0c6d 100644 --- a/src/access_moppy/derivations/__init__.py +++ b/src/access_moppy/derivations/__init__.py @@ -8,6 +8,14 @@ calc_topsoil, extract_tilefrac, ) +from access_moppy.derivations.calc_ocean import ( + calc_areacello, + calc_global_ave_ocean, +) +from access_moppy.derivations.calc_seaice import ( + calc_seaice_extent, + calc_hemi_seaice, +) custom_functions = { "add": lambda *args: reduce(operator.add, args), @@ -24,6 +32,10 @@ "calc_landcover": calc_landcover, "extract_tilefrac": extract_tilefrac, "average_tile": average_tile, + "calc_areacello": calc_areacello, + "calc_global_ave_ocean": calc_global_ave_ocean, + "calc_seaice_extent": calc_seaice_extent, + "calc_hemi_seaice": calc_hemi_seaice, } diff --git a/src/access_moppy/derivations/calc_ocean.py b/src/access_moppy/derivations/calc_ocean.py new file mode 100644 index 0000000..436e2a5 --- /dev/null +++ b/src/access_moppy/derivations/calc_ocean.py @@ -0,0 +1,144 @@ +#!/usr/bin/env python +# This file contains a collection of functions to calculate ocean +# derived variables from ACCESS model output. +# +# To propose new calculations and/or update to existing ones see documentation: +# +# and open a new issue on github. + +import numpy as np +import xarray as xr + + +def calc_areacello(area, mask_v): + """ + Calculate ocean grid cell area with proper masking. + + This function applies ocean mask to the grid cell area array, ensuring that + only valid ocean cells are included in area calculations. Land and invalid + grid cells are set to zero. + + For ACCESS-ESM1.6, this function is typically used with the ocean-2d-ht.nc + mask file, where grid cells with zero depth indicate land areas that should + be excluded from ocean calculations. + + Parameters + ---------- + area : array-like + Grid cell area values (m²). + Can be numpy masked array or regular array. + mask_v : array-like + Ocean mask array where: + - Valid ocean cells: unmasked/True values (depth > 0) + - Land or invalid cells: masked/False values (depth = 0) + Must have compatible dimensions with area. + For ACCESS-ESM1.6: typically derived from ocean-2d-ht.nc depth field. + + Returns + ------- + array-like + Ocean grid cell area with land cells set to zero. + - Units: m² + - Shape: Same as input area + - Type: Filled array (no masked values) + + Examples + -------- + Apply ocean mask to calculate valid ocean cell areas: + + >>> ocean_areas = calc_areacello(grid_area, ocean_mask) + + ACCESS-ESM1.6 typical usage with depth-based masking: + + >>> # Load ocean depth from ACCESS-ESM1.6 auxiliary file + >>> depth = load_data('ocean-2d-ht.nc')['ht'] + >>> ocean_mask = depth > 0 # Create mask where depth > 0 (ocean) + >>> ocean_areas = calc_areacello(grid_area, ocean_mask) + + Notes + ----- + - Land and invalid grid cells are filled with zeros + - The function preserves the original area values for valid ocean cells + - Output is a regular (non-masked) array for consistent downstream processing + - Commonly used for ocean budget calculations and spatial integration + + ACCESS-ESM1.6 Implementation Details: + - Uses ocean-2d-ht.nc file containing ocean depth (ht variable) + - Grid cells with depth = 0 are identified as land areas + - Grid cells with depth > 0 are valid ocean areas + - This masking ensures accurate ocean-only area calculations + """ + area.mask = mask_v.mask + return area.filled(0) + + +def calc_global_ave_ocean(var, rho_dzt, area_t): + """ + Calculate global volume-weighted average of ocean variables. + + This function computes the global ocean average of a variable by weighting + each grid cell by its mass (density × thickness × area). This provides + a proper volume-weighted mean that accounts for the varying cell sizes + and water column thickness in the ocean model. + + Parameters + ---------- + var : array-like + Ocean variable to average (e.g., temperature, salinity). + Can be 3D (time, depth, lat, lon) or 2D (time, lat, lon). + Should have compatible dimensions with rho_dzt and area_t. + rho_dzt : array-like + Ocean cell mass per unit area (kg/m²). + Typically calculated as: density × cell_thickness × area + Must have dimensions compatible with var for broadcasting. + area_t : array-like + Ocean grid cell area (m²). + Must have compatible horizontal dimensions with var. + + Returns + ------- + array-like + Global volume-weighted average of the input variable. + - Shape: (time,) - preserves time dimension only + - Units: Same as input variable + - Type: 1D array with time series of global averages + + Examples + -------- + Calculate global mean ocean temperature: + + >>> global_temp = calc_global_ave_ocean(temperature, rho_dzt, area_t) + + Calculate global mean sea surface temperature: + + >>> global_sst = calc_global_ave_ocean(sst_2d, rho_dzt_surface, area_t) + + Notes + ----- + - Uses mass-weighted averaging for proper volume representation + - Automatically handles both 3D and 2D input variables + - For 3D variables: averages over depth, latitude, and longitude (axes 1,2,3) + - For 2D variables: averages over latitude and longitude (axes 1,2) + - Mass weighting accounts for varying cell volumes in ocean models + + ACCESS-ESM1.6 Implementation Details: + - rho_dzt typically from ocean model output (density × thickness) + - area_t from ocean grid specification + - Commonly used for global ocean heat content, salt content calculations + - Handles irregular ocean grid geometries and partial cells + """ + # Calculate mass weighting field + mass = rho_dzt * area_t + + # Debug: print shape information + print("Variable shape:", np.shape(var)) + + try: + # Try 3D averaging (time, depth, lat, lon) -> (time,) + vnew = np.average(var, axis=(1, 2, 3), weights=mass) + except: + # Fall back to 2D averaging (time, lat, lon) -> (time,) + # Use only surface layer of mass weights + vnew = np.average(var, axis=(1, 2), weights=mass[:, 0, :, :]) + + return vnew \ No newline at end of file diff --git a/src/access_moppy/derivations/calc_seaice.py b/src/access_moppy/derivations/calc_seaice.py new file mode 100644 index 0000000..b7286ad --- /dev/null +++ b/src/access_moppy/derivations/calc_seaice.py @@ -0,0 +1,175 @@ +#!/usr/bin/env python +# This file contains a collection of functions to calculate sea ice +# derived variables from ACCESS model output. +# +# To propose new calculations and/or update to existing ones see documentation: +# +# and open a new issue on github. + +import logging +import numpy as np +import xarray as xr +from typing import Union, List + + +def calc_hemi_seaice(invar, tarea, hemi, extent=False): + """ + Calculate hemispheric sea ice properties (area, volume, or extent). + + This function computes sea ice properties over either the northern or southern + hemisphere. It can calculate: + - Sea ice area/volume: by multiplying ice concentration/thickness with grid cell area + - Sea ice extent: by summing grid cell areas where ice concentration ≥ 15% + + Parameters + ---------- + invar : xarray.DataArray + Input sea ice variable to process: + - For area calculation: sea ice concentration (aice, fractional 0-1) + - For volume calculation: sea ice thickness (hi, meters) + - For extent calculation: sea ice concentration (aice, fractional 0-1) + tarea : xarray.DataArray + Grid cell area (m²). Must have compatible dimensions with invar. + hemi : str + Hemisphere to calculate over: + - 'north': Northern hemisphere (latitude ≥ 0°) + - 'south': Southern hemisphere (latitude < 0°) + extent : bool, default False + Calculation mode: + - False: Calculate area/volume (invar × tarea) + - True: Calculate extent (sum tarea where invar ≥ 0.15) + + Returns + ------- + xarray.DataArray + Hemispheric sum of the requested property: + - Units: m² (for area/extent) or m³ (for volume) + - Dimensions: Time dimension preserved, spatial dimensions summed + + Examples + -------- + Calculate northern hemisphere sea ice extent: + + >>> extent = calc_hemi_seaice(aice, areacello, 'north', extent=True) + + Calculate southern hemisphere sea ice area: + + >>> area = calc_hemi_seaice(aice, areacello, 'south', extent=False) + + Calculate northern hemisphere sea ice volume: + + >>> volume = calc_hemi_seaice(ice_thickness, areacello, 'north', extent=False) + + Notes + ----- + - Sea ice extent uses the standard 15% concentration threshold + - Function automatically detects latitude coordinates in input data + - Handles coordinate dimension alignment between invar and tarea + - Time dimension is preserved; spatial dimensions are summed + """ + # Get latitude coordinates from input data + lat = None + + # Try to find latitude coordinate in invar first + for coord_name in invar.coords: + if 'lat' in coord_name.lower(): + lat = invar.coords[coord_name] + break + + # If not found in invar, try tarea + if lat is None: + for coord_name in tarea.coords: + if 'lat' in coord_name.lower(): + lat = tarea.coords[coord_name] + break + + # If still not found, raise error + if lat is None: + raise ValueError("Cannot find latitude coordinate in input data") + + # Ensure latitude coordinates align with data dimensions + invar_dims = invar.dims[1:] if invar.dims[0] == 'time' else invar.dims + + # Align latitude coordinate with data dimensions if needed + if any(d not in invar_dims for d in lat.dims): + # Create mapping from lat dims to invar dims + lat_mapping = {} + for i, d in enumerate(lat.dims): + if i < len(invar_dims): + lat_mapping[d] = invar_dims[i] + lat = lat.rename(lat_mapping) + + # if calculating extent sum carea and aice is used as filter + # with volume and area invar is multiplied by carea first + if extent: + var = tarea.where(invar >= 0.15).where(invar <= 1.) + else: + var = invar * tarea + + if hemi == 'north': + var = var.where(lat >= 0.) + elif hemi == 'south': + var = var.where(lat < 0.) + else: + logging.error(f"invalid hemisphere: {hemi}") + raise ValueError(f"invalid hemisphere: {hemi}") + + # sum over latitude and longitude (skip time dimension) + # This implements CMIP7 cell_methods: "area: sum time: mean" + # (spatial sum, temporal mean) + spatial_dims = [d for d in var.dims if d != 'time'] + vout = var.sum(dim=spatial_dims, skipna=True) + + return vout + + +def calc_seaice_extent(aice, areacello, region="north"): + """ + Calculate hemispheric sea ice extent with CMIP-standard units. + + This function calculates sea ice extent (total area of grid cells with ≥15% + ice concentration) for a specified hemisphere and converts the result to + CMIP-standard units (10⁶ km²). + + Parameters + ---------- + aice : xarray.DataArray + Sea ice concentration (fractional, range 0-1). + Must contain latitude coordinates for hemispheric selection. + areacello : xarray.DataArray + Ocean grid cell area in m². + Must have compatible dimensions with aice. + region : str, default 'north' + Hemisphere to calculate extent for: + - 'north': Northern hemisphere (latitude ≥ 0°) + - 'south': Southern hemisphere (latitude < 0°) + + Returns + ------- + xarray.DataArray + Sea ice extent in 10⁶ km² (CMIP standard units). + Time dimension preserved if present in input. + + Examples + -------- + Calculate Arctic sea ice extent: + + >>> arctic_extent = calc_seaice_extent(aice, areacello, region="north") + + Calculate Antarctic sea ice extent: + + >>> antarctic_extent = calc_seaice_extent(aice, areacello, region="south") + + Notes + ----- + - Uses the standard 15% concentration threshold for extent calculation + - Output units are 10⁶ km² as required by CMIP protocols + - This is a convenience wrapper around calc_hemi_seaice + """ + # Calculate extent using the general function + extent_m2 = calc_hemi_seaice(aice, areacello, region, extent=True) + + # Convert from m^2 to 1e6 km^2 for CMIP standard units + extent_1e6km2 = extent_m2 / 1e12 # m^2 to 1e6 km^2 + + return extent_1e6km2 diff --git a/src/access_moppy/mappings/ACCESS-ESM1.6_mappings.json b/src/access_moppy/mappings/ACCESS-ESM1.6_mappings.json index 1ce394c..681fea4 100644 --- a/src/access_moppy/mappings/ACCESS-ESM1.6_mappings.json +++ b/src/access_moppy/mappings/ACCESS-ESM1.6_mappings.json @@ -4,7 +4,8 @@ "components": [ "atmosphere", "land", - "ocean" + "ocean", + "sea_ice" ], "description": "Variable mappings for ACCESS-ESM1.6 Earth System Model" }, @@ -92,13 +93,20 @@ "units": "1", "positive": null, "model_variables": [ - "fld_s02i309" + "fld_s02i309", + "fld_s02i311" ], "calculation": { "type": "formula", "operation": "level_to_height", "operands": [ - "fld_s02i309" + { + "operation": "add", + "operands": [ + "fld_s02i309", + "fld_s02i311" + ] + } ] } }, @@ -151,13 +159,42 @@ "units": "1", "positive": null, "model_variables": [ - "fld_s02i308" + "fld_s02i308", + "fld_s02i310" ], "calculation": { "type": "formula", "operation": "level_to_height", "operands": [ - "fld_s02i308" + { + "operation": "add", + "operands": [ + "fld_s02i308", + "fld_s02i310" + ] + } + ] + } + }, + "clwvi": { + "CF standard Name": "atmosphere_mass_content_of_cloud_condensed_water", + "dimensions": { + "time": "time", + "lat": "lat", + "lon": "lon" + }, + "units": "kg m-2", + "positive": null, + "model_variables": [ + "fld_s30i405", + "fld_s30i406" + ], + "calculation": { + "type": "formula", + "operation": "add", + "operands": [ + "fld_s30i405", + "fld_s30i406" ] } }, @@ -971,11 +1008,11 @@ "units": "m", "positive": null, "model_variables": [ - "fld_s30i207" + "fld_s16i201" ], "calculation": { "type": "direct", - "formula": "fld_s30i207" + "formula": "fld_s16i201" } }, "co23D": { @@ -1829,6 +1866,7 @@ "CF standard Name": "mass_content_of_water_in_soil_layer" }, "ra": { + "CF standard Name": "surface_upward_mass_flux_of_carbon_dioxide_expressed_as_carbon_due_to_plant_respiration", "dimensions": { "time": "time", "lat": "lat", @@ -1837,13 +1875,17 @@ "units": "kg m-2 s-1", "positive": "up", "model_variables": [ - "fld_s03i263" + "fld_s03i261", + "fld_s03i262" ], "calculation": { - "type": "direct", - "formula": "fld_s03i263" - }, - "CF standard Name": "surface_upward_mass_flux_of_carbon_dioxide_expressed_as_carbon" + "type": "formula", + "operation": "subtract", + "operands": [ + "fld_s03i261", + "fld_s03i262" + ] + } }, "residualFrac": { "dimensions": { @@ -1969,6 +2011,27 @@ } }, "ocean": { + "areacello": { + "dimensions": { + "yt_ocean": "latitude", + "xt_ocean": "longitude" + }, + "units": "m2", + "positive": null, + "model_variables": [ + "area_t", + "ht" + ], + "calculation": { + "type": "formula", + "formula": "calculate_areacello", + "args": [ + "area_t", + "ht" + ] + }, + "CF standard Name": "areacello" + }, "agessc": { "dimensions": { "time": "time", @@ -2003,7 +2066,32 @@ "type": "direct", "formula": "temp" }, - "CF standard Name": "" + "CF standard Name": "sea_water_conservative_temperature" + }, + "bigthetaoga": { + "dimensions": { + "time": "time", + "st_ocean": "lev", + "yt_ocean": "latitude", + "xt_ocean": "longitude" + }, + "units": "deg_C", + "positive": null, + "model_variables": [ + "temp", + "rho_dzt", + "areacello" + ], + "calculation": { + "type": "formula", + "formula": "calculate_global_ave_ocean", + "args": [ + "temp", + "rho_dzt", + "areacello" + ] + }, + "CF standard Name": "sea_water_conservative_temperature" }, "evs": { "dimensions": { @@ -2714,5 +2802,853 @@ }, "CF standard Name": "" } + }, + "sea_ice": { + "siage": { + "CF standard Name": "sea_ice_age", + "dimensions": { + "time": "time", + "TLAT": "lat", + "TLON": "lon" + }, + "units": "s", + "positive": null, + "model_variables": [ + "siage" + ], + "calculation": { + "type": "direct", + "formula": "siage" + } + }, + "siconc": { + "CF standard Name": "sea_ice_area_fraction", + "dimensions": { + "time": "time", + "TLAT": "lat", + "TLON": "lon" + }, + "units": "%", + "positive": null, + "model_variables": [ + "siconc" + ], + "calculation": { + "type": "direct", + "formula": "siconc" + } + }, + "sidconcdyn": { + "CF standard Name": "sea_ice_dynamic_concentration_change", + "dimensions": { + "time": "time", + "TLAT": "lat", + "TLON": "lon" + }, + "units": "s-1", + "positive": null, + "model_variables": [ + "sidconcdyn" + ], + "calculation": { + "type": "formula", + "operation": "multiply", + "operands": [ + "sidconcdyn", + 100 + ] + } + }, + "sidconcth": { + "CF standard Name": "sea_ice_thermodynamic_concentration_change", + "dimensions": { + "time": "time", + "TLAT": "lat", + "TLON": "lon" + }, + "units": "s-1", + "positive": null, + "model_variables": [ + "sidconcth" + ], + "calculation": { + "type": "formula", + "operation": "multiply", + "operands": [ + "sidconcth", + 100 + ] + } + }, + "siareaacrossline": { + "CF standard Name": "sea_ice_area_transport_across_line", + "dimensions": { + "time": "time", + "TLAT": "lat", + "TLON": "lon" + }, + "units": "m2 s-1", + "positive": null, + "model_variables": [ + "aice" + ], + "calculation": { + "type": "formula", + "operation": "calc_line_transport", + "args": ["aice"] + } + }, + "siarean": { + "CF standard Name": "sea_ice_area", + "dimensions": { + "time": "time", + "TLAT": "lat", + "TLON": "lon" + }, + "units": "1e6 km2", + "positive": null, + "model_variables": [ + "aice", + "areacello" + ], + "calculation": { + "type": "formula", + "operation": "calc_seaice_area", + "args": ["aice", "areacello"], + "kwargs": { + "region": "north" + } + } + }, + "siareas": { + "CF standard Name": "sea_ice_area", + "dimensions": { + "time": "time", + "TLAT": "lat", + "TLON": "lon" + }, + "units": "1e6 km2", + "positive": null, + "model_variables": [ + "aice", + "areacello" + ], + "calculation": { + "type": "formula", + "operation": "calc_seaice_area", + "args": ["aice", "areacello"], + "kwargs": { + "region": "south" + } + } + }, + "siconca": { + "CF standard Name": "sea_ice_area_fraction", + "dimensions": { + "time": "time", + "lat": "lat", + "lon": "lon" + }, + "units": "%", + "positive": null, + "model_variables": [ + "fld_s00i031" + ], + "calculation": { + "type": "formula", + "operation": "multiply", + "args": [ + "fld_s00i031", + 100 + ] + } + }, + "sidivvel": { + "CF standard Name": "sea_ice_divergence_velocity", + "dimensions": { + "time": "time", + "ULAT": "lat", + "ULON": "lon" + }, + "units": "s-1", + "positive": null, + "model_variables": [ + "sidivvel" + ], + "calculation": { + "type": "direct", + "formula": "sidivvel" + } + }, + "sidmassdyn": { + "CF standard Name": "sea_ice_dynamic_mass_change", + "dimensions": { + "time": "time", + "TLAT": "lat", + "TLON": "lon" + }, + "units": "kg m-2 s-1", + "positive": null, + "model_variables": [ + "sidmassdyn" + ], + "calculation": { + "type": "direct", + "formula": "sidmassdyn" + } + }, + "sidmassth": { + "CF standard Name": "sea_ice_thermodynamic_mass_change", + "dimensions": { + "time": "time", + "TLAT": "lat", + "TLON": "lon" + }, + "units": "kg m-2 s-1", + "positive": null, + "model_variables": [ + "sidmassth" + ], + "calculation": { + "type": "direct", + "formula": "sidmassth" + } + }, + "sidmassmeltlat": { + "CF standard Name": "tendency_of_sea_ice_amount_due_to_lateral_melting", + "dimensions": { + "time": "time", + "TLAT": "lat", + "TLON": "lon" + }, + "units": "kg m-2 s-1", + "positive": null, + "model_variables": [ + "sidmassmeltlat" + ], + "calculation": { + "type": "formula", + "operation": "multiply", + "operands": [ + "sidmassmeltlat", + -1800 + ] + } + }, + "sidmassevapsubl": { + "CF standard Name": "water_evapotranspiration_flux", + "dimensions": { + "time": "time", + "TLAT": "lat", + "TLON": "lon" + }, + "units": "kg m-2 s-1", + "positive": "up", + "model_variables": [ + "sidmassevapsubl" + ], + "calculation": { + "type": "formula", + "operation": "multiply", + "operands": [ + "sidmassevapsubl", + -1 + ] + } + }, + "sidmassgrowthsi": { + "CF standard Name": "tendency_of_sea_ice_amount_due_to_conversion_of_snow_to_sea_ice", + "dimensions": { + "time": "time", + "TLAT": "lat", + "TLON": "lon" + }, + "units": "kg m-2 s-1", + "positive": null, + "model_variables": [ + "sidmassgrowthsi" + ], + "calculation": { + "type": "direct", + "formula": "sidmassgrowthsi" + } + }, + "sidmassgrowthbot": { + "CF standard Name": "sea_ice_bottom_growth_mass_flux", + "dimensions": { + "time": "time", + "TLAT": "lat", + "TLON": "lon" + }, + "units": "kg m-2 s-1", + "positive": null, + "model_variables": [ + "sidmassgrowthbot" + ], + "calculation": { + "type": "formula", + "operation": "divide", + "operands": [ + "sidmassgrowthbot", + 1800 + ] + } + }, + "sidmassgrowthwat": { + "CF standard Name": "sea_ice_top_growth_mass_flux", + "dimensions": { + "time": "time", + "TLAT": "lat", + "TLON": "lon" + }, + "units": "kg m-2 s-1", + "positive": null, + "model_variables": [ + "sidmassgrowthwat" + ], + "calculation": { + "type": "formula", + "operation": "divide", + "operands": [ + "sidmassgrowthwat", + 1800 + ] + } + }, + "sidmassmeltbot": { + "CF standard Name": "sea_ice_bottom_melt_mass_flux", + "dimensions": { + "time": "time", + "TLAT": "lat", + "TLON": "lon" + }, + "units": "kg m-2 s-1", + "positive": null, + "model_variables": [ + "sidmassmeltbot" + ], + "calculation": { + "type": "formula", + "operation": "divide", + "operands": [ + "sidmassmeltbot", + -1800 + ] + } + }, + "sidmassmelttop": { + "CF standard Name": "sea_ice_top_melt_mass_flux", + "dimensions": { + "time": "time", + "TLAT": "lat", + "TLON": "lon" + }, + "units": "kg m-2 s-1", + "positive": null, + "model_variables": [ + "sidmassmelttop" + ], + "calculation": { + "type": "formula", + "operation": "divide", + "operands": [ + "sidmassmelttop", + -1800 + ] + } + }, + "sisndmasssi": { + "CF standard Name": "tendency_of_surface_snow_amount_due_to_conversion_of_snow_to_sea_ice", + "dimensions": { + "time": "time", + "TLAT": "lat", + "TLON": "lon" + }, + "units": "kg m-2 s-1", + "positive": null, + "model_variables": [ + "sidmasssi" + ], + "calculation": { + "type": "formula", + "formula": null + } + }, + "sifb": { + "CF standard Name": "sea_ice_freeboard_thickness", + "dimensions": { + "time": "time", + "TLAT": "lat", + "TLON": "lon" + }, + "units": "m", + "positive": null, + "model_variables": [ + "sifb" + ], + "calculation": { + "type": "direct", + "formula": "sifb" + } + }, + "sidmasstranx": { + "CF standard Name": "sea_ice_x_transport", + "dimensions": { + "time": "time", + "ULAT": "lat", + "ULON": "lon" + }, + "units": "kg s-1", + "positive": null, + "model_variables": [ + "sidmasstranx" + ], + "calculation": { + "type": "direct", + "formula": "sidmasstranx" + } + }, + "sidmasstrany": { + "CF standard Name": "sea_ice_y_transport", + "dimensions": { + "time": "time", + "ULAT": "lat", + "ULON": "lon" + }, + "units": "kg s-1", + "positive": null, + "model_variables": [ + "sidmasstrany" + ], + "calculation": { + "type": "direct", + "formula": "sidmasstrany" + } + }, + "siextentn": { + "CF standard Name": "sea_ice_extent", + "dimensions": { + "time": "time", + "TLON": "lon", + "TLAT": "lat" + }, + "units": "1e6 km2", + "positive": null, + "model_variables": [ + "aice", + "areacello" + ], + "calculation": { + "type": "formula", + "operation": "calc_seaice_extent", + "args": ["aice", "areacello"], + "kwargs": { + "region": "north" + } + } + }, + "siextents": { + "CF standard Name": "sea_ice_extent", + "dimensions": { + "time": "time", + "TLON": "lon", + "TLAT": "lat" + }, + "units": "1e6 km2", + "positive": null, + "model_variables": [ + "aice", + "areacello" + ], + "calculation": { + "type": "formula", + "operation": "calc_seaice_extent", + "args": ["aice", "areacello"], + "kwargs": { + "region": "south" + } + } + }, + "siflfwbot": { + "CF standard Name": "water_flux_into_sea_water_due_to_sea_ice_thermodynamics", + "dimensions": { + "time": "time", + "TLAT": "lat", + "TLON": "lon" + }, + "units": "kg m-2 s-1", + "positive": "down", + "model_variables": [ + "siflfwbot" + ], + "calculation": { + "type": "direct", + "formula": "siflfwbot" + } + }, + "simass": { + "CF standard Name": "sea_ice_amount", + "dimensions": { + "time": "time", + "TLAT": "lat", + "TLON": "lon" + }, + "units": "kg m-2", + "positive": null, + "model_variables": [ + "hi", + "hs" + ], + "calculation": { + "type": "formula", + "operation": "add", + "operands": [{ + "operation": "multiply", + "operands":["hi", 917] + }, + { + "operation": "multiply", + "operands":["hs", 330] + }] + } + }, + "simassacrossline": { + "CF standard Name": "sea_ice_transport_across_line", + "dimensions": { + "time": "time", + "TLAT": "lat", + "TLON": "lon" + }, + "units": "kg s-1", + "positive": null, + "model_variables": [ + "simass" + ], + "calculation": { + "type": "formula", + "operation": "calc_line_transport", + "args": ["simass"] + } + }, + "sisnconc": { + "CF standard Name": "surface_snow_area_fraction", + "dimensions": { + "time": "time", + "TLAT": "lat", + "TLON": "lon" + }, + "units": "1", + "positive": null, + "model_variables": [ + "sisnthick" + ], + "calculation": { + "type": "formula", + "formula": "sisnconc", + "args": ["sisnthick"] + } + }, + "sisnhc": { + "CF standard Name": "surface_snow_height", + "dimensions": { + "time": "time", + "TLAT": "lat", + "TLON": "lon" + }, + "units": "m", + "positive": null, + "model_variables": [ + "sisnhc", + "siconc" + ], + "calculation": { + "type": "formula", + "formula": "mask_sea_ice_only", + "args": ["sisnhc", "siconc"] + } + }, + "sisnthick": { + "CF standard Name": "surface_snow_thickness", + "dimensions": { + "time": "time", + "TLAT": "lat", + "TLON": "lon" + }, + "units": "m", + "positive": null, + "model_variables": [ + "sisnthick" + ], + "calculation": { + "type": "direct", + "formula": "sisnthick" + } + }, + "sispeed": { + "CF standard Name": "sea_ice_speed", + "dimensions": { + "time": "time", + "ULAT": "lat", + "ULON": "lon" + }, + "units": "m s-1", + "positive": null, + "model_variables": [ + "sispeed" + ], + "calculation": { + "type": "direct", + "formula": "sispeed" + } + }, + "sistrxdtop": { + "CF standard Name": "surface_downward_x_stress", + "dimensions": { + "time": "time", + "ULAT": "lat", + "ULON": "lon" + }, + "units": "N m-2", + "positive": "down", + "model_variables": [ + "sistrxdtop" + ], + "calculation": { + "type": "direct", + "formula": "sistrxdtop" + } + }, + "sistrxubot": { + "CF standard Name": "upward_x_stress_at_sea_ice_base", + "dimensions": { + "time": "time", + "ULAT": "lat", + "ULON": "lon" + }, + "units": "N m-2", + "positive": "up", + "model_variables": [ + "sistrxubot" + ], + "calculation": { + "type": "direct", + "formula": "sistrxubot" + } + }, + "sistrydtop": { + "CF standard Name": "surface_downward_y_stress", + "dimensions": { + "time": "time", + "ULAT": "lat", + "ULON": "lon" + }, + "units": "N m-2", + "positive": "down", + "model_variables": [ + "sistrydtop" + ], + "calculation": { + "type": "direct", + "formula": "sistrydtop" + } + }, + "sistryubot": { + "CF standard Name": "sea_ice_y_stress_at_bottom", + "dimensions": { + "time": "time", + "ULAT": "lat", + "ULON": "lon" + }, + "units": "N m-2", + "positive": "up", + "model_variables": [ + "sistryubot" + ], + "calculation": { + "type": "direct", + "formula": "sistryubot" + } + }, + "sitempbot": { + "CF standard Name": "sea_ice_bottom_temperature", + "dimensions": { + "time": "time", + "TLAT": "lat", + "TLON": "lon" + }, + "units": "K", + "positive": null, + "model_variables": [ + "sitempbot" + ], + "calculation": { + "type": "direct", + "formula": "sitempbot" + } + }, + "sitemptop": { + "CF standard Name": "surface_temperature", + "dimensions": { + "time": "time", + "TLAT": "lat", + "TLON": "lon" + }, + "units": "K", + "positive": null, + "model_variables": [ + "siconca", + "siconc" + ], + "calculation":{ + "type":"formula", + "operation": "unknown" + } + }, + "sisndmassmelt": { + "CF standard Name": "surface_snow_melt_flux", + "dimensions": { + "time": "time", + "TLAT": "lat", + "TLON": "lon" + }, + "units": "kg m-2 s-1", + "positive": null, + "model_variables": [ + "sisndmassmelt" + ], + "calculation": { + "type": "direct", + "formula": "sisndmassmelt" + } + }, + "sisndmasssnf": { + "CF standard Name": "snowfall_flux", + "dimensions": { + "time": "time", + "lat": "lat", + "lon": "lon" + }, + "units": "kg m-2 s-1", + "positive": null, + "model_variables": [ + "sisndmasssnf" + ], + "calculation": { + "type": "direct", + "formula": "sisndmasssnf" + } + }, + "sithick": { + "CF standard Name": "sea_ice_thickness", + "dimensions": { + "time": "time", + "TLAT": "lat", + "TLON": "lon" + }, + "units": "m", + "positive": null, + "model_variables": [ + "sithick" + ], + "calculation": { + "type": "direct", + "formula": "sithick" + } + }, + "siv": { + "CF standard Name": "sea_ice_y_velocity", + "dimensions": { + "time": "time", + "ULAT": "lat", + "ULON": "lon" + }, + "units": "m s-1", + "positive": null, + "model_variables": [ + "siv" + ], + "calculation": { + "type": "direct", + "formula": "siv" + } + }, + "siu": { + "CF standard Name": "sea_ice_x_velocity", + "dimensions": { + "time": "time", + "ULAT": "lat", + "ULON": "lon" + }, + "units": "m s-1", + "positive": null, + "model_variables": [ + "siu" + ], + "calculation": { + "type": "direct", + "formula": "siu" + } + }, + "sivol": { + "CF standard Name": "sea_ice_thickness", + "dimensions": { + "time": "time", + "TLAT": "lat", + "TLON": "lon" + }, + "units": "m", + "positive": null, + "model_variables": [ + "sivol" + ], + "calculation": { + "type": "direct", + "formula": "sivol" + } + }, + "sivoln": { + "CF standard Name": "sea_ice_volume", + "dimensions": { + "time": "time", + "TLAT": "lat", + "TLON": "lon" + }, + "units": "1e3 km3", + "positive": null, + "model_variables": [ + "sivol", + "areacello" + ], + "calculation": { + "type": "formula", + "operation": "calc_seaice_volume", + "args": ["sivol", "areacello"], + "kwargs": { + "region": "north" + } + } + }, + "sivols": { + "CF standard Name": "sea_ice_volume", + "dimensions": { + "time": "time", + "TLAT": "lat", + "TLON": "lon" + }, + "units": "1e3 km3", + "positive": null, + "model_variables": [ + "sivol", + "areacello" + ], + "calculation": { + "type": "formula", + "operation": "calc_seaice_volume", + "args": ["sivol", "areacello"], + "kwargs": { + "region": "south" + } + } + } } } diff --git a/src/access_moppy/utilities.py b/src/access_moppy/utilities.py index 9400b95..4c5b9d1 100644 --- a/src/access_moppy/utilities.py +++ b/src/access_moppy/utilities.py @@ -46,7 +46,7 @@ def load_model_mappings(compound_name: str, model_id: str = None) -> Dict: all_mappings = json.load(f) # Search in component-organized structure - for component in ["atmosphere", "land", "ocean", "time_invariant"]: + for component in ["atmosphere", "land", "ocean", "time_invariant", "sea_ice"]: if ( component in all_mappings and cmor_name in all_mappings[component]